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
This commit is contained in:
Mattias Fredriksson 2021-08-05 18:34:32 -05:00 committed by Hans Goudey
parent 263fa406cd
commit bc0d55e724
Notes: blender-bot 2024-01-06 18:56:35 +01:00
Referenced by pull request #116729, WIP: Refactoring: Geometry Nodes: Rewrite Ico Sphere mesh primitive
5 changed files with 20 additions and 27 deletions

View File

@ -126,11 +126,11 @@ static Mesh *create_circle_mesh(const float radius,
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
float angle = 0.0f;
const float angle_delta = 2.0f * M_PI / static_cast<float>(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<float>(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));

View File

@ -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<float>(verts_num);
const float angle_delta = 2.0f * (M_PI / static_cast<float>(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));

View File

@ -79,19 +79,17 @@ static Mesh *create_grid_mesh(const int verts_x,
MutableSpan<MPoly> 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;
}
}

View File

@ -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);

View File

@ -69,26 +69,24 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> 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));