From bc0d55e724a27fba61a93cc95f2cc48e205e1cd8 Mon Sep 17 00:00:00 2001 From: Mattias Fredriksson Date: Thu, 5 Aug 2021 18:34:32 -0500 Subject: Fix: Avoid floating point error in some mesh primitive nodes Some mesh primitives created using geometry nodes use loops to create vertices and accumulates positions/angles in FP variables. This allows rounding errors to accumulate and can introduce significant errors. To minimize changes from original implementation, variables allowing errors to accumulate are replaced by: delta * index. Affected Mesh Primitives nodes are Line, Grid, Cylinder, Circle, Cone, and UV-Sphere. Differential Revision: https://developer.blender.org/D12136 --- .../nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc | 10 +++++----- .../nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc | 5 ++--- .../nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc | 14 ++++++-------- .../nodes/geometry/nodes/node_geo_mesh_primitive_line.cc | 4 +--- .../geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc | 14 ++++++-------- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 96c6f073ab3..131f9548b40 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -126,11 +126,11 @@ static Mesh *create_circle_mesh(const float radius, MutableSpan edges{mesh->medge, mesh->totedge}; MutableSpan polys{mesh->mpoly, mesh->totpoly}; - float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast(verts_num); - for (MVert &vert : verts) { - copy_v3_v3(vert.co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); - angle += angle_delta; + /* Assign vertex coordinates. */ + const float angle_delta = 2.0f * (M_PI / static_cast(verts_num)); + for (const int i : IndexRange(verts_num)) { + const float angle = i * angle_delta; + copy_v3_v3(verts[i].co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); } if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { copy_v3_v3(verts.last().co, float3(0)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 790a518e584..b834f5e2fa0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -318,9 +318,9 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, /* Calculate vertex positions. */ const int top_verts_start = 0; const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1); - float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast(verts_num); + const float angle_delta = 2.0f * (M_PI / static_cast(verts_num)); for (const int i : IndexRange(verts_num)) { + const float angle = i * angle_delta; const float x = std::cos(angle); const float y = std::sin(angle); if (!top_is_point) { @@ -330,7 +330,6 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, copy_v3_v3(verts[bottom_verts_start + i].co, float3(x * radius_bottom, y * radius_bottom, -height)); } - angle += angle_delta; } if (top_is_point) { copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 7a97ae8e318..410290c79ee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -79,19 +79,17 @@ static Mesh *create_grid_mesh(const int verts_x, MutableSpan polys{mesh->mpoly, mesh->totpoly}; { - const float dx = size_x / edges_x; - const float dy = size_y / edges_y; - float x = -size_x * 0.5; + const float dx = edges_x == 0 ? 0.0f : size_x / edges_x; + const float dy = edges_y == 0 ? 0.0f : size_y / edges_y; + const float x_shift = edges_x / 2.0f; + const float y_shift = edges_y / 2.0f; for (const int x_index : IndexRange(verts_x)) { - float y = -size_y * 0.5; for (const int y_index : IndexRange(verts_y)) { const int vert_index = x_index * verts_y + y_index; - verts[vert_index].co[0] = x; - verts[vert_index].co[1] = y; + verts[vert_index].co[0] = (x_index - x_shift) * dx; + verts[vert_index].co[1] = (y_index - y_shift) * dy; verts[vert_index].co[2] = 0.0f; - y += dy; } - x += dx; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index a193c05daa1..2e6d8ca34c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -118,11 +118,9 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int short normal[3]; normal_float_to_short_v3(normal, delta.normalized()); - float3 co = start; for (const int i : verts.index_range()) { - copy_v3_v3(verts[i].co, co); + copy_v3_v3(verts[i].co, start + delta * i); copy_v3_v3_short(verts[i].no, normal); - co += delta; } fill_edge_data(edges); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index fe456dc4564..affba602234 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -69,26 +69,24 @@ static void calculate_sphere_vertex_data(MutableSpan verts, const int rings) { const float delta_theta = M_PI / rings; - const float delta_phi = (2 * M_PI) / segments; + const float delta_phi = (2.0f * M_PI) / segments; copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius)); normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f)); int vert_index = 1; - float theta = delta_theta; - for (const int UNUSED(ring) : IndexRange(rings - 1)) { - float phi = 0.0f; - const float z = cosf(theta); - for (const int UNUSED(segment) : IndexRange(segments)) { + for (const int ring : IndexRange(1, rings - 1)) { + const float theta = ring * delta_theta; + const float z = std::cos(theta); + for (const int segment : IndexRange(1, segments)) { + const float phi = segment * delta_phi; const float sin_theta = std::sin(theta); const float x = sin_theta * std::cos(phi); const float y = sin_theta * std::sin(phi); copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius); normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z)); - phi += delta_phi; vert_index++; } - theta += delta_theta; } copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius)); -- cgit v1.2.3