diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 17ac21b876..6c754ec4e1 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -606,158 +606,19 @@ void CSGShape3D::update_shape() { CSGBrush *n = _get_brush(); ERR_FAIL_NULL_MSG(n, "Cannot get CSGBrush."); + AHashMap vec_map; + Vector face_count; face_count.resize(n->materials.size() + 1); - - Vector smooth_faces; - LocalVector smooth_vertex; - smooth_faces.resize(n->faces.size()); - smooth_vertex.resize(n->faces.size() * 3); - - for (int i = 0; i < face_count.size(); i++) { - face_count.write[i] = 0; - } - - for (int i = 0; i < n->faces.size(); i++) { - int mat = n->faces[i].material; - ERR_CONTINUE(mat < -1 || mat >= face_count.size()); - int idx = mat == -1 ? face_count.size() - 1 : mat; - - Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); - - smooth_faces.write[i] = p.normal; - // Not sure if resize populates the LocalVector. - smooth_vertex[i * 3 + 0] = Vector3(p.normal); - smooth_vertex[i * 3 + 1] = Vector3(p.normal); - smooth_vertex[i * 3 + 2] = Vector3(p.normal); - // We could use a AHashMap Vector3, int to store the number of connections of each vertex position and end the loop earlier. But I'm not sure if the performance gains outweigh the cost. - face_count.write[idx]++; - } - - if (autosmooth) { - // We could add a `use_groups` property later to only apply autosmooth on smooth faces or respect smoothing groups in some way. - if (smoothing_angle > 0.1) { - float smooth_angle_rad = Math::cos(Math::deg_to_rad(smoothing_angle)); - for (int i = 0; i < smooth_faces.size(); i++) { - for (int k = 0; k < 3; k++) { - int curr_vert = i * 3 + k; - Vector3 vert_a = n->faces[i].vertices[k]; - for (int j = i + 1; j < smooth_faces.size(); j++) { - // Compare the angles of faces instead of vertices. - if (smooth_faces[i].dot(smooth_faces[j]) > smooth_angle_rad) { - for (int h = 0; h < 3; h++) { - Vector3 vert_b = n->faces[j].vertices[h]; - if (vert_a == vert_b) { - int curr_j = j * 3 + h; - smooth_vertex[curr_vert] += smooth_faces[j]; - smooth_vertex[curr_j] += smooth_faces[i]; - break; - } - } - } - } - smooth_vertex[curr_vert].normalize(); - } - } - } - } else { - for (int i = 0; i < smooth_faces.size(); i++) { - bool face_is_smooth = n->faces[i].smooth; - if (face_is_smooth) { - for (int k = 0; k < 3; k++) { - Vector3 vert_a = n->faces[i].vertices[k]; - int curr_vert = i * 3 + k; - // Skip the other vertices of the face as they will never occupy the same position. - for (int j = i + 1; j < smooth_faces.size(); j++) { - // Preparing for when and if we replace Vector of bool for Vector of int smoothing groups. for now, face_is_smooth is always true. - if (face_is_smooth == n->faces[j].smooth) { - for (int h = 0; h < 3; h++) { - Vector3 vert_b = n->faces[j].vertices[h]; - if (vert_a == vert_b) { - int curr_j = j * 3 + h; - smooth_vertex[curr_vert] += smooth_faces[j]; - smooth_vertex[curr_j] += smooth_faces[i]; - // Skip the other 2 vertices as only one vertex of each face can connect with one vertex of other face. - break; - } - } - } - } - smooth_vertex[curr_vert].normalize(); - } - } - } - } + face_count.fill(0); Vector surfaces; - surfaces.resize(face_count.size()); - //create arrays - for (int i = 0; i < surfaces.size(); i++) { - surfaces.write[i].vertices.resize(face_count[i] * 3); - surfaces.write[i].normals.resize(face_count[i] * 3); - surfaces.write[i].uvs.resize(face_count[i] * 3); - if (calculate_tangents) { - surfaces.write[i].tans.resize(face_count[i] * 3 * 4); - } - surfaces.write[i].last_added = 0; - - if (i != surfaces.size() - 1) { - surfaces.write[i].material = n->materials[i]; - } - - surfaces.write[i].verticesw = surfaces.write[i].vertices.ptrw(); - surfaces.write[i].normalsw = surfaces.write[i].normals.ptrw(); - surfaces.write[i].uvsw = surfaces.write[i].uvs.ptrw(); - if (calculate_tangents) { - surfaces.write[i].tansw = surfaces.write[i].tans.ptrw(); - } - } - - //fill arrays - { - for (int i = 0; i < n->faces.size(); i++) { - int order[3] = { 0, 1, 2 }; - - if (n->faces[i].invert) { - SWAP(order[1], order[2]); - } - - int mat = n->faces[i].material; - ERR_CONTINUE(mat < -1 || mat >= face_count.size()); - int idx = mat == -1 ? face_count.size() - 1 : mat; - - int last = surfaces[idx].last_added; - - int face_pos_i = i * 3; - - for (int j = 0; j < 3; j++) { - Vector3 v = n->faces[i].vertices[j]; - - Vector3 normal = smooth_vertex[face_pos_i + j]; - - if (n->faces[i].invert) { - normal = -normal; - } - - int k = last + order[j]; - surfaces[idx].verticesw[k] = v; - surfaces[idx].uvsw[k] = n->faces[i].uvs[j]; - surfaces[idx].normalsw[k] = normal; - - if (calculate_tangents) { - // zero out our tangents for now - k *= 4; - surfaces[idx].tansw[k++] = 0.0; - surfaces[idx].tansw[k++] = 0.0; - surfaces[idx].tansw[k++] = 0.0; - surfaces[idx].tansw[k++] = 0.0; - } - } - - surfaces.write[idx].last_added += 3; - } + if (autosmooth) { + _build_surfaces_smoothed(n, surfaces, face_count); + } else { + _build_surfaces_default(n, surfaces, face_count); } root_mesh.instantiate(); @@ -811,6 +672,256 @@ void CSGShape3D::update_shape() { #endif // PHYSICS_3D_DISABLED } +void CSGShape3D::_build_surfaces_smoothed(CSGBrush *p_brush, Vector &r_surfaces, Vector &r_face_count) { + Vector smooth_faces; + LocalVector smooth_vertex; + smooth_faces.resize(p_brush->faces.size()); + smooth_vertex.resize(p_brush->faces.size() * 3); + + Vector3 *smooth_faces_ptrw = smooth_faces.ptrw(); + int *face_count_ptrw = r_face_count.ptrw(); + + for (int i = 0; i < p_brush->faces.size(); i++) { + int mat = p_brush->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= r_face_count.size()); + int idx = mat == -1 ? r_face_count.size() - 1 : mat; + + Plane p(p_brush->faces[i].vertices[0], p_brush->faces[i].vertices[1], p_brush->faces[i].vertices[2]); + + smooth_faces_ptrw[i] = p.normal; + // Not sure if resize populates the LocalVector. + smooth_vertex[i * 3 + 0] = Vector3(p.normal); + smooth_vertex[i * 3 + 1] = Vector3(p.normal); + smooth_vertex[i * 3 + 2] = Vector3(p.normal); + // We could use a AHashMap Vector3, int to store the number of connections of each vertex position and end the loop earlier. But I'm not sure if the performance gains outweigh the cost. + face_count_ptrw[idx]++; + } + + const Vector3 *smooth_faces_ptr = smooth_faces.ptr(); + const int smooth_faces_size = smooth_faces.size(); + + // We could add a `use_groups` property later to only apply autosmooth on smooth faces or respect smoothing groups in some way. + if (smoothing_angle > 0.1) { + float smooth_angle_rad = Math::cos(Math::deg_to_rad(smoothing_angle)); + for (int i = 0; i < smooth_faces_size; i++) { + for (int k = 0; k < 3; k++) { + int curr_vert = i * 3 + k; + Vector3 vert_a = p_brush->faces[i].vertices[k]; + for (int j = i + 1; j < smooth_faces_size; j++) { + // Compare the angles of faces instead of vertices. + if (smooth_faces_ptr[i].dot(smooth_faces_ptr[j]) > smooth_angle_rad) { + for (int h = 0; h < 3; h++) { + Vector3 vert_b = p_brush->faces[j].vertices[h]; + if (vert_a == vert_b) { + int curr_j = j * 3 + h; + smooth_vertex[curr_vert] += smooth_faces_ptr[j]; + smooth_vertex[curr_j] += smooth_faces_ptr[i]; + break; + } + } + } + } + smooth_vertex[curr_vert].normalize(); + } + } + } else { + for (int i = 0; i < smooth_faces_size; i++) { + bool face_is_smooth = p_brush->faces[i].smooth; + if (face_is_smooth) { + for (int k = 0; k < 3; k++) { + Vector3 vert_a = p_brush->faces[i].vertices[k]; + int curr_vert = i * 3 + k; + // Skip the other vertices of the face as they will never occupy the same position. + for (int j = i + 1; j < smooth_faces_size; j++) { + // Preparing for when and if we replace Vector of bool for Vector of int smoothing groups. for now, face_is_smooth is always true. + if (face_is_smooth == p_brush->faces[j].smooth) { + for (int h = 0; h < 3; h++) { + Vector3 vert_b = p_brush->faces[j].vertices[h]; + if (vert_a == vert_b) { + int curr_j = j * 3 + h; + smooth_vertex[curr_vert] += smooth_faces_ptr[j]; + smooth_vertex[curr_j] += smooth_faces_ptr[i]; + // Skip the other 2 vertices as only one vertex of each face can connect with one vertex of other face. + break; + } + } + } + } + smooth_vertex[curr_vert].normalize(); + } + } + } + } + + //create arrays + for (int i = 0; i < r_surfaces.size(); i++) { + r_surfaces.write[i].vertices.resize(r_face_count[i] * 3); + r_surfaces.write[i].normals.resize(r_face_count[i] * 3); + r_surfaces.write[i].uvs.resize(r_face_count[i] * 3); + if (calculate_tangents) { + r_surfaces.write[i].tans.resize(r_face_count[i] * 3 * 4); + } + r_surfaces.write[i].last_added = 0; + + if (i != r_surfaces.size() - 1) { + r_surfaces.write[i].material = p_brush->materials[i]; + } + + r_surfaces.write[i].verticesw = r_surfaces.write[i].vertices.ptrw(); + r_surfaces.write[i].normalsw = r_surfaces.write[i].normals.ptrw(); + r_surfaces.write[i].uvsw = r_surfaces.write[i].uvs.ptrw(); + if (calculate_tangents) { + r_surfaces.write[i].tansw = r_surfaces.write[i].tans.ptrw(); + } + } + + //fill arrays + { + for (int i = 0; i < p_brush->faces.size(); i++) { + int order[3] = { 0, 1, 2 }; + + if (p_brush->faces[i].invert) { + SWAP(order[1], order[2]); + } + + int mat = p_brush->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= r_face_count.size()); + int idx = mat == -1 ? r_face_count.size() - 1 : mat; + + int last = r_surfaces[idx].last_added; + + int face_pos_i = i * 3; + + for (int j = 0; j < 3; j++) { + Vector3 v = p_brush->faces[i].vertices[j]; + + Vector3 normal = smooth_vertex[face_pos_i + j]; + + if (p_brush->faces[i].invert) { + normal = -normal; + } + + int k = last + order[j]; + r_surfaces[idx].verticesw[k] = v; + r_surfaces[idx].uvsw[k] = p_brush->faces[i].uvs[j]; + r_surfaces[idx].normalsw[k] = normal; + + if (calculate_tangents) { + // zero out our tangents for now + k *= 4; + r_surfaces[idx].tansw[k++] = 0.0; + r_surfaces[idx].tansw[k++] = 0.0; + r_surfaces[idx].tansw[k++] = 0.0; + r_surfaces[idx].tansw[k++] = 0.0; + } + } + + r_surfaces.write[idx].last_added += 3; + } + } +} + +void CSGShape3D::_build_surfaces_default(CSGBrush *p_brush, Vector &r_surfaces, Vector &r_face_count) { + AHashMap vec_map; + vec_map.reserve(p_brush->faces.size() * 3); + + for (int i = 0; i < p_brush->faces.size(); i++) { + int mat = p_brush->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= r_face_count.size()); + int idx = mat == -1 ? r_face_count.size() - 1 : mat; + + if (p_brush->faces[i].smooth) { + Plane p(p_brush->faces[i].vertices[0], p_brush->faces[i].vertices[1], p_brush->faces[i].vertices[2]); + + for (int j = 0; j < 3; j++) { + Vector3 v = p_brush->faces[i].vertices[j]; + Vector3 *vec = vec_map.getptr(v); + if (vec) { + *vec += p.normal; + } else { + vec_map.insert(v, p.normal); + } + } + } + + r_face_count.write[idx]++; + } + + //create arrays + for (int i = 0; i < r_surfaces.size(); i++) { + r_surfaces.write[i].vertices.resize(r_face_count[i] * 3); + r_surfaces.write[i].normals.resize(r_face_count[i] * 3); + r_surfaces.write[i].uvs.resize(r_face_count[i] * 3); + if (calculate_tangents) { + r_surfaces.write[i].tans.resize(r_face_count[i] * 3 * 4); + } + r_surfaces.write[i].last_added = 0; + + if (i != r_surfaces.size() - 1) { + r_surfaces.write[i].material = p_brush->materials[i]; + } + + r_surfaces.write[i].verticesw = r_surfaces.write[i].vertices.ptrw(); + r_surfaces.write[i].normalsw = r_surfaces.write[i].normals.ptrw(); + r_surfaces.write[i].uvsw = r_surfaces.write[i].uvs.ptrw(); + if (calculate_tangents) { + r_surfaces.write[i].tansw = r_surfaces.write[i].tans.ptrw(); + } + } + + //fill arrays + { + for (int i = 0; i < p_brush->faces.size(); i++) { + int order[3] = { 0, 1, 2 }; + + if (p_brush->faces[i].invert) { + SWAP(order[1], order[2]); + } + + int mat = p_brush->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= r_face_count.size()); + int idx = mat == -1 ? r_face_count.size() - 1 : mat; + + int last = r_surfaces[idx].last_added; + + Plane p(p_brush->faces[i].vertices[0], p_brush->faces[i].vertices[1], p_brush->faces[i].vertices[2]); + + for (int j = 0; j < 3; j++) { + Vector3 v = p_brush->faces[i].vertices[j]; + + Vector3 normal = p.normal; + + if (p_brush->faces[i].smooth) { + Vector3 *ptr = vec_map.getptr(v); + if (ptr) { + normal = ptr->normalized(); + } + } + + if (p_brush->faces[i].invert) { + normal = -normal; + } + + int k = last + order[j]; + r_surfaces[idx].verticesw[k] = v; + r_surfaces[idx].uvsw[k] = p_brush->faces[i].uvs[j]; + r_surfaces[idx].normalsw[k] = normal; + + if (calculate_tangents) { + // zero out our tangents for now + k *= 4; + r_surfaces[idx].tansw[k++] = 0.0; + r_surfaces[idx].tansw[k++] = 0.0; + r_surfaces[idx].tansw[k++] = 0.0; + r_surfaces[idx].tansw[k++] = 0.0; + } + } + + r_surfaces.write[idx].last_added += 3; + } + } +} + Ref CSGShape3D::bake_static_mesh() { Ref baked_mesh; if (is_root_shape() && root_mesh.is_valid()) { diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index 247c58f0f9..5ac05a5e65 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -118,6 +118,9 @@ private: Vector _get_brush_collision_faces(); #endif // PHYSICS_3D_DISABLED + void _build_surfaces_smoothed(CSGBrush *p_brush, Vector &r_surfaces, Vector &r_face_count); + void _build_surfaces_default(CSGBrush *p_brush, Vector &r_surfaces, Vector &r_face_count); + protected: void _notification(int p_what); virtual CSGBrush *_build_brush() = 0;