1
1

Fix #106469: unstable tessellation with quad-flipping detection

The result of detecting if a quad should flip the default 0-2 split
when tessellated only used a pre-calculated normal when available,
since the method of detecting the flip was different, the check for a
concave face could change depending on the existence of polygon-normals.

In practice this meant cycles render preview could use a different
tessellation than the GPU display.

While [0] exposed the bug, it's an inherent problem with having 2
methods of detecting concave quads.

Remove is_quad_flip_v3_first_third_fast_with_normal(..) and always
use is_quad_flip_v3_first_third_fast(..), because having to calculate
the normal inline has significant overhead.

Note that "bow-tie" quads may now render with a subdivision in a
different direction although they must be very distorted with both
triangles along the 0-2 split pointing away from each other.

Thanks to @HooglyBoogly for investigating the issue.

[0]: 16fbadde36.
This commit is contained in:
2023-04-21 14:01:21 +10:00
parent 5721b34e53
commit 19dbe049db
5 changed files with 19 additions and 45 deletions

View File

@@ -63,20 +63,10 @@ BLI_INLINE void mesh_calc_tessellation_for_face_impl(const Span<int> corner_vert
MLoopTri *mlt_a = mlt++;
create_tri(0, 2, 3);
MLoopTri *mlt_b = mlt;
if (UNLIKELY(face_normal ? is_quad_flip_v3_first_third_fast_with_normal(
/* Simpler calculation (using the normal). */
positions[corner_verts[mlt_a->tri[0]]],
positions[corner_verts[mlt_a->tri[1]]],
positions[corner_verts[mlt_a->tri[2]]],
positions[corner_verts[mlt_b->tri[2]]],
normal_precalc) :
is_quad_flip_v3_first_third_fast(
/* Expensive calculation (no normal). */
positions[corner_verts[mlt_a->tri[0]]],
positions[corner_verts[mlt_a->tri[1]]],
positions[corner_verts[mlt_a->tri[2]]],
positions[corner_verts[mlt_b->tri[2]]]))) {
if (UNLIKELY(is_quad_flip_v3_first_third_fast(positions[corner_verts[mlt_a->tri[0]]],
positions[corner_verts[mlt_a->tri[1]]],
positions[corner_verts[mlt_a->tri[2]]],
positions[corner_verts[mlt_b->tri[2]]]))) {
/* Flip out of degenerate 0-2 state. */
mlt_a->tri[2] = mlt_b->tri[2];
mlt_b->tri[0] = mlt_a->tri[1];

View File

@@ -145,11 +145,6 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
const float v2[3],
const float v3[3],
const float v4[3]);
bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3],
const float v2[3],
const float v3[3],
const float v4[3],
const float normal[3]);
/** \} */

View File

@@ -5889,6 +5889,9 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
const float v3[3],
const float v4[3])
{
/* NOTE: if the faces normal has been calculated it's possible to simplify the following checks,
* however this means the solution may be different depending on the existence of normals
* causing tessellation to be "unstable" depending on the existence of normals, see #106469. */
float d_12[3], d_13[3], d_14[3];
float cross_a[3], cross_b[3];
sub_v3_v3v3(d_12, v2, v1);
@@ -5899,19 +5902,6 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
return dot_v3v3(cross_a, cross_b) > 0.0f;
}
bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3],
const float v2[3],
const float v3[3],
const float v4[3],
const float normal[3])
{
float dir_v3v1[3], tangent[3];
sub_v3_v3v3(dir_v3v1, v3, v1);
cross_v3_v3v3(tangent, dir_v3v1, normal);
const float dot = dot_v3v3(v1, tangent);
return (dot_v3v3(v4, tangent) >= dot) || (dot_v3v3(v2, tangent) <= dot);
}
float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3])
{
BLI_ASSERT_UNIT_V3(tan_l);

View File

@@ -1962,17 +1962,19 @@ static Face *cdt_tri_as_imesh_face(
return facep;
}
/* Like BLI_math's is_quad_flip_v3_first_third_fast_with_normal, with const double3's. */
/* Like BLI_math's is_quad_flip_v3_first_third_fast, with const double3's. */
static bool is_quad_flip_first_third(const double3 &v1,
const double3 &v2,
const double3 &v3,
const double3 &v4,
const double3 &normal)
const double3 &v4)
{
double3 dir_v3v1 = v3 - v1;
double3 tangent = math::cross(dir_v3v1, normal);
double dot = math::dot(v1, tangent);
return (math::dot(v4, tangent) >= dot) || (math::dot(v2, tangent) <= dot);
const double3 d_12 = v2 - v1;
const double3 d_13 = v3 - v1;
const double3 d_14 = v4 - v1;
const double3 cross_a = math::cross(d_12, d_13);
const double3 cross_b = math::cross(d_14, d_13);
return math::dot(cross_a, cross_b) > 0.0f;
}
/**
@@ -2008,7 +2010,7 @@ static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
int eo_23 = f->edge_orig[2];
int eo_30 = f->edge_orig[3];
Face *f0, *f1;
if (UNLIKELY(is_quad_flip_first_third(v0->co, v1->co, v2->co, v3->co, f->plane->norm))) {
if (UNLIKELY(is_quad_flip_first_third(v0->co, v1->co, v2->co, v3->co))) {
f0 = arena->add_face({v0, v1, v3}, f->orig, {eo_01, -1, eo_30}, {false, false, false});
f1 = arena->add_face({v1, v2, v3}, f->orig, {eo_12, eo_23, -1}, {false, false, false});
}

View File

@@ -81,11 +81,8 @@ BLI_INLINE void bmesh_calc_tessellation_for_face_impl(BMLoop *(*looptris)[3],
efa->no, l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co);
}
if (UNLIKELY(is_quad_flip_v3_first_third_fast_with_normal(l_ptr_a[0]->v->co,
l_ptr_a[1]->v->co,
l_ptr_a[2]->v->co,
l_ptr_b[2]->v->co,
efa->no))) {
if (UNLIKELY(is_quad_flip_v3_first_third_fast(
l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) {
/* Flip out of degenerate 0-2 state. */
l_ptr_a[2] = l_ptr_b[2];
l_ptr_b[0] = l_ptr_a[1];