Fix numerical instabilities of solidify with complex constraints

Sometimes on flat open vertices the thickness would suddenly start
to be jumping in powers of 2, also when bending a plane there is a
noticeable jump in the geometry.
When offset is set to -1 or 1 weird glitches happen.

See D6559 for test cases that failed.
This commit is contained in:
Henrik Dick 2020-01-22 06:02:12 +11:00 committed by Campbell Barton
parent 56c7ee99b8
commit bda0c0847b
1 changed files with 56 additions and 77 deletions

View File

@ -163,6 +163,8 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset;
const float ofs_back = ofs_front - smd->offset * smd->offset_fac;
const float ofs_front_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_front : ofs_back));
const float ofs_back_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_back : ofs_front));
const float offset_fac_vg = smd->offset_fac_vg;
const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg;
const float offset = fabsf(smd->offset) * smd->offset_clamp;
@ -1262,15 +1264,16 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
NewFaceRef *face = edge->faces[l];
if (face && (first_edge == NULL ||
(first_edge->faces[0] != face && first_edge->faces[1] != face))) {
const float ofs = face->reversed ? ofs_back_clamped : ofs_front_clamped;
if (!null_faces[face->index]) {
mul_v3_v3fl(normals_queue[queue_index],
poly_nors[face->index],
face->reversed ? -1 : 1);
normals_queue[queue_index++][3] = face->reversed ? ofs_back : ofs_front;
normals_queue[queue_index++][3] = ofs;
}
else {
mul_v3_v3fl(face_nors[0], poly_nors[face->index], face->reversed ? -1 : 1);
nor_ofs[0] = face->reversed ? ofs_back : ofs_front;
nor_ofs[0] = ofs;
}
}
}
@ -1280,7 +1283,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
}
}
uint face_nors_len = 0;
const float stop_explosion = 1 - fabsf(smd->offset_fac) * 0.05f;
const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f;
while (queue_index > 0) {
if (face_nors_len == 0) {
if (queue_index <= 2) {
@ -1371,50 +1374,23 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
}
MEM_freeN(normals_queue);
/* When up to 3 constraint normals are found. */
float d, q;
switch (face_nors_len) {
case 0:
mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]);
disable_boundary_fix = true;
break;
case 1:
mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]);
disable_boundary_fix = true;
break;
case 2:
q = dot_v3v3(face_nors[0], face_nors[1]);
d = 1.0f - q * q;
if (LIKELY(d > FLT_EPSILON) && q < stop_explosion) {
d = 1.0f / d;
mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d);
mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d);
add_v3_v3v3(nor, face_nors[0], face_nors[1]);
}
else {
mul_v3_fl(face_nors[0], nor_ofs[0] * 0.5f);
mul_v3_fl(face_nors[1], nor_ofs[1] * 0.5f);
add_v3_v3v3(nor, face_nors[0], face_nors[1]);
}
if (!disable_boundary_fix) {
cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]);
}
break;
case 3:
q = dot_v3v3(face_nors[0], face_nors[1]);
d = 1.0f - q * q;
float *free_nor = move_nor; /* No need to allocate a new array. */
cross_v3_v3v3(free_nor, face_nors[0], face_nors[1]);
if (LIKELY(d > FLT_EPSILON) && q < stop_explosion) {
d = 1.0f / d;
mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d);
mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d);
add_v3_v3v3(nor, face_nors[0], face_nors[1]);
}
else {
mul_v3_fl(face_nors[0], nor_ofs[0] * 0.5f);
mul_v3_fl(face_nors[1], nor_ofs[1] * 0.5f);
add_v3_v3v3(nor, face_nors[0], face_nors[1]);
}
if (ELEM(face_nors_len, 2, 3)) {
const float q = dot_v3v3(face_nors[0], face_nors[1]);
float d = 1.0f - q * q;
cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]);
if (d > FLT_EPSILON * 10 && q < stop_explosion) {
d = 1.0f / d;
mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d);
mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d);
}
else {
d = 1.0f / (fabsf(q) + 1.0f);
mul_v3_fl(face_nors[0], nor_ofs[0] * d);
mul_v3_fl(face_nors[1], nor_ofs[1] * d);
}
add_v3_v3v3(nor, face_nors[0], face_nors[1]);
if (face_nors_len == 3) {
float *free_nor = move_nor;
mul_v3_fl(face_nors[2], nor_ofs[2]);
d = dot_v3v3(face_nors[2], free_nor);
if (LIKELY(fabsf(d) > FLT_EPSILON)) {
@ -1423,9 +1399,12 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
sub_v3_v3(nor, free_nor);
}
disable_boundary_fix = true;
break;
default:
BLI_assert(0);
}
}
else {
BLI_assert(face_nors_len < 2);
mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]);
disable_boundary_fix = true;
}
}
/* Simple/Even Method. */
@ -1447,8 +1426,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
if (face && (first_edge == NULL ||
(first_edge->faces[0] != face && first_edge->faces[1] != face))) {
float angle = 1.0f;
float ofs = face->reversed ? -max_ff(1.0e-5f, ofs_back) :
max_ff(1.0e-5f, ofs_front);
float ofs = face->reversed ? -ofs_back_clamped : ofs_front_clamped;
if (smd->nonmanifold_offset_mode ==
MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) {
MLoop *ml_next = orig_mloop + face->face->loopstart;
@ -1495,17 +1473,16 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
/* Set normal length with selected method. */
if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) {
float d = dot_v3v3(nor, nor_back);
if (has_front) {
float length = len_squared_v3(nor);
if (LIKELY(length > FLT_EPSILON)) {
mul_v3_fl(nor, total_angle / length);
float length_sq = len_squared_v3(nor);
if (LIKELY(length_sq > FLT_EPSILON)) {
mul_v3_fl(nor, total_angle / length_sq);
}
}
if (has_back) {
float length = len_squared_v3(nor_back);
if (LIKELY(length > FLT_EPSILON)) {
mul_v3_fl(nor_back, total_angle_back / length);
float length_sq = len_squared_v3(nor_back);
if (LIKELY(length_sq > FLT_EPSILON)) {
mul_v3_fl(nor_back, total_angle_back / length_sq);
}
if (!has_front) {
copy_v3_v3(nor, nor_back);
@ -1518,7 +1495,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
if (LIKELY(fabsf(q) > FLT_EPSILON)) {
q /= nor_length * nor_back_length;
}
d = 1.0f - q * q;
float d = 1.0f - q * q;
if (LIKELY(d > FLT_EPSILON)) {
d = 1.0f / d;
if (LIKELY(nor_length > FLT_EPSILON)) {
@ -1625,24 +1602,26 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md,
/* Do clamping. */
if (do_clamp) {
if (do_angle_clamp) {
float min_length = 0;
float angle = 0.5f * M_PI;
uint k = 0;
for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) {
float length = orig_edge_lengths[(*p)->old_edge];
float e_ang = (*p)->angle;
if (e_ang > angle) {
angle = e_ang;
if (g->edges_len > 2) {
float min_length = 0;
float angle = 0.5f * M_PI;
uint k = 0;
for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) {
float length = orig_edge_lengths[(*p)->old_edge];
float e_ang = (*p)->angle;
if (e_ang > angle) {
angle = e_ang;
}
if (length < min_length || k == 0) {
min_length = length;
}
}
if (length < min_length || k == 0) {
min_length = length;
}
}
float cos_ang = cosf(angle * 0.5f);
if (cos_ang > 0) {
float max_off = min_length * 0.5f / cos_ang;
if (max_off < offset * 0.5f) {
scalar_vgroup *= max_off / offset * 2;
float cos_ang = cosf(angle * 0.5f);
if (cos_ang > 0) {
float max_off = min_length * 0.5f / cos_ang;
if (max_off < offset * 0.5f) {
scalar_vgroup *= max_off / offset * 2;
}
}
}
}