Fix CSG performance regression from autosmoothing
Fixes CSG performance regression from autosmoothing.
This commit is contained in:
+257
-146
@@ -606,158 +606,19 @@ void CSGShape3D::update_shape() {
|
||||
CSGBrush *n = _get_brush();
|
||||
ERR_FAIL_NULL_MSG(n, "Cannot get CSGBrush.");
|
||||
|
||||
AHashMap<Vector3, Vector3> vec_map;
|
||||
|
||||
Vector<int> face_count;
|
||||
face_count.resize(n->materials.size() + 1);
|
||||
|
||||
Vector<Vector3> smooth_faces;
|
||||
LocalVector<Vector3> 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<ShapeUpdateSurface> 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<CSGShape3D::ShapeUpdateSurface> &r_surfaces, Vector<int> &r_face_count) {
|
||||
Vector<Vector3> smooth_faces;
|
||||
LocalVector<Vector3> 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<CSGShape3D::ShapeUpdateSurface> &r_surfaces, Vector<int> &r_face_count) {
|
||||
AHashMap<Vector3, Vector3> 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<ArrayMesh> CSGShape3D::bake_static_mesh() {
|
||||
Ref<ArrayMesh> baked_mesh;
|
||||
if (is_root_shape() && root_mesh.is_valid()) {
|
||||
|
||||
@@ -118,6 +118,9 @@ private:
|
||||
Vector<Vector3> _get_brush_collision_faces();
|
||||
#endif // PHYSICS_3D_DISABLED
|
||||
|
||||
void _build_surfaces_smoothed(CSGBrush *p_brush, Vector<ShapeUpdateSurface> &r_surfaces, Vector<int> &r_face_count);
|
||||
void _build_surfaces_default(CSGBrush *p_brush, Vector<ShapeUpdateSurface> &r_surfaces, Vector<int> &r_face_count);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
virtual CSGBrush *_build_brush() = 0;
|
||||
|
||||
Reference in New Issue
Block a user