diff options
-rw-r--r-- | source/blender/modifiers/intern/MOD_surfacedeform.c | 157 |
1 files changed, 123 insertions, 34 deletions
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index 0fad78683eb..008e258dc7a 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -114,7 +114,12 @@ typedef struct SDefBindPoly { float weight_dist_proj; float weight_dist; float weight; + /** Distances from the centroid to edges flanking the corner vertex, used to penalize + * small or long and narrow faces in favor of bigger and more square ones. */ float scales[2]; + /** Distance weight from the corner vertex to the chord line, used to penalize + * cases with the three consecutive vertices being nearly in line. */ + float scale_mid; /** Center of `coords` */ float centroid[3]; /** Center of `coords_v2` */ @@ -123,15 +128,18 @@ typedef struct SDefBindPoly { * The calculated normal of coords (could be shared between faces). */ float normal[3]; + /** Vectors pointing from the centroid to the midpoints of the two edges + * flanking the corner vertex. */ float cent_edgemid_vecs_v2[2][2]; - /** - * The unsigned angle of this face-corner in `[0.0 .. PI]` range, - * where a small value is a thin corner. PI is a straight line. - * Take care dividing by this value as it can approach zero. - */ + /** Angle between the cent_edgemid_vecs_v2 vectors. */ float edgemid_angle; + /** Angles between the centroid-to-point and cent_edgemid_vecs_v2 vectors. + * Positive values measured towards the corner; clamped non-negative. */ float point_edgemid_angles[2]; + /** Angles between the centroid-to-corner and cent_edgemid_vecs_v2 vectors. */ float corner_edgemid_angles[2]; + /** Weight of the bind mode based on the corner and two adjacent vertices, + * versus the one based on the centroid and the dominant edge. */ float dominant_angle_weight; /** Index of the input polygon. */ uint index; @@ -414,13 +422,14 @@ BLI_INLINE uint nearestVert(SDefBindCalcData *const data, const float point_co[3 BLI_INLINE int isPolyValid(const float coords[][2], const uint nr) { - float prev_co[2]; + float prev_co[2], prev_prev_co[2]; float curr_vec[2], prev_vec[2]; if (!is_poly_convex_v2(coords, nr)) { return MOD_SDEF_BIND_RESULT_CONCAVE_ERR; } + copy_v2_v2(prev_prev_co, coords[nr - 2]); copy_v2_v2(prev_co, coords[nr - 1]); sub_v2_v2v2(prev_vec, prev_co, coords[nr - 2]); normalize_v2(prev_vec); @@ -428,15 +437,23 @@ BLI_INLINE int isPolyValid(const float coords[][2], const uint nr) for (int i = 0; i < nr; i++) { sub_v2_v2v2(curr_vec, coords[i], prev_co); + /* Check ovelap between directly adjacent vertices. */ const float curr_len = normalize_v2(curr_vec); if (curr_len < FLT_EPSILON) { return MOD_SDEF_BIND_RESULT_OVERLAP_ERR; } + /* Check ovelap between vertices skipping one. */ + if (len_squared_v2v2(prev_prev_co, coords[i]) < FLT_EPSILON * FLT_EPSILON) { + return MOD_SDEF_BIND_RESULT_OVERLAP_ERR; + } + + /* Check for adjacent parallel edges. */ if (1.0f - dot_v2v2(prev_vec, curr_vec) < FLT_EPSILON) { return MOD_SDEF_BIND_RESULT_CONCAVE_ERR; } + copy_v2_v2(prev_prev_co, prev_co); copy_v2_v2(prev_co, coords[i]); copy_v2_v2(prev_vec, curr_vec); } @@ -460,9 +477,9 @@ static void freeBindData(SDefBindWeightData *const bwdata) MEM_freeN(bwdata); } -BLI_INLINE float computeAngularWeight(const float point_angle) +BLI_INLINE float computeAngularWeight(const float point_angle, const float edgemid_angle) { - return sinf(point_angle * M_PI_2); + return sinf(min_ff(point_angle / edgemid_angle, 1) * M_PI_2); } BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data, @@ -603,33 +620,51 @@ BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data, avg_point_dist += bpoly->weight_dist; - /* Compute centroid to mid-edge vectors */ - mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[0], - bpoly->coords_v2[bpoly->edge_vert_inds[0]], - bpoly->coords_v2[bpoly->corner_ind]); + /* Common vertex coordinates. */ + const float *const vert0_v2 = bpoly->coords_v2[bpoly->edge_vert_inds[0]]; + const float *const vert1_v2 = bpoly->coords_v2[bpoly->edge_vert_inds[1]]; + const float *const corner_v2 = bpoly->coords_v2[bpoly->corner_ind]; - mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[1], - bpoly->coords_v2[bpoly->edge_vert_inds[1]], - bpoly->coords_v2[bpoly->corner_ind]); + /* Compute centroid to mid-edge vectors */ + mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[0], vert0_v2, corner_v2); + mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[1], vert1_v2, corner_v2); sub_v2_v2(bpoly->cent_edgemid_vecs_v2[0], bpoly->centroid_v2); sub_v2_v2(bpoly->cent_edgemid_vecs_v2[1], bpoly->centroid_v2); - /* Compute poly scales with respect to mid-edges, and normalize the vectors */ - bpoly->scales[0] = normalize_v2(bpoly->cent_edgemid_vecs_v2[0]); - bpoly->scales[1] = normalize_v2(bpoly->cent_edgemid_vecs_v2[1]); + normalize_v2(bpoly->cent_edgemid_vecs_v2[0]); + normalize_v2(bpoly->cent_edgemid_vecs_v2[1]); - /* Compute the required polygon angles */ + /* Compute poly scales with respect to the two edges. */ + bpoly->scales[0] = dist_to_line_v2(bpoly->centroid_v2, vert0_v2, corner_v2); + bpoly->scales[1] = dist_to_line_v2(bpoly->centroid_v2, vert1_v2, corner_v2); + + /* Compute the angle between the edge mid vectors. */ bpoly->edgemid_angle = angle_normalized_v2v2(bpoly->cent_edgemid_vecs_v2[0], bpoly->cent_edgemid_vecs_v2[1]); - sub_v2_v2v2(tmp_vec_v2, bpoly->coords_v2[bpoly->corner_ind], bpoly->centroid_v2); + /* Compute the angles between the corner and the edge mid vectors. The angles + * are computed signed in order to correctly clamp point_edgemid_angles later. */ + float corner_angles[2]; + + sub_v2_v2v2(tmp_vec_v2, corner_v2, bpoly->centroid_v2); normalize_v2(tmp_vec_v2); - bpoly->corner_edgemid_angles[0] = angle_normalized_v2v2(tmp_vec_v2, - bpoly->cent_edgemid_vecs_v2[0]); - bpoly->corner_edgemid_angles[1] = angle_normalized_v2v2(tmp_vec_v2, - bpoly->cent_edgemid_vecs_v2[1]); + corner_angles[0] = angle_signed_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[0]); + corner_angles[1] = angle_signed_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[1]); + + bpoly->corner_edgemid_angles[0] = fabsf(corner_angles[0]); + bpoly->corner_edgemid_angles[1] = fabsf(corner_angles[1]); + + /* Verify that the computed values are valid (the polygon isn't somehow + * degenerate despite having passed isPolyValid). */ + if (bpoly->scales[0] < FLT_EPSILON || bpoly->scales[1] < FLT_EPSILON || + bpoly->edgemid_angle < FLT_EPSILON || bpoly->corner_edgemid_angles[0] < FLT_EPSILON || + bpoly->corner_edgemid_angles[1] < FLT_EPSILON) { + freeBindData(bwdata); + data->success = MOD_SDEF_BIND_RESULT_GENERIC_ERR; + return NULL; + } /* Check for infinite weights, and compute angular data otherwise. */ if (bpoly->weight_dist < FLT_EPSILON) { @@ -640,15 +675,54 @@ BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data, inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ; } else { - float cent_point_vec[2]; + /* Compute angles between the point and the edge mid vectors. */ + float cent_point_vec[2], point_angles[2]; sub_v2_v2v2(cent_point_vec, bpoly->point_v2, bpoly->centroid_v2); normalize_v2(cent_point_vec); - bpoly->point_edgemid_angles[0] = angle_normalized_v2v2(cent_point_vec, - bpoly->cent_edgemid_vecs_v2[0]); - bpoly->point_edgemid_angles[1] = angle_normalized_v2v2(cent_point_vec, - bpoly->cent_edgemid_vecs_v2[1]); + point_angles[0] = angle_signed_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[0]) * + signf(corner_angles[0]); + point_angles[1] = angle_signed_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[1]) * + signf(corner_angles[1]); + + if (point_angles[0] <= 0 && point_angles[1] <= 0) { + /* If the point is outside the corner formed by the edge mid vectors, + * choose to clamp the closest side and flip the other. */ + if (point_angles[0] < point_angles[1]) { + point_angles[0] = bpoly->edgemid_angle - point_angles[1]; + } + else { + point_angles[1] = bpoly->edgemid_angle - point_angles[0]; + } + } + + bpoly->point_edgemid_angles[0] = max_ff(0, point_angles[0]); + bpoly->point_edgemid_angles[1] = max_ff(0, point_angles[1]); + + /* Compute the distance scale for the corner. The base value is the orthogonal + * distance from the corner to the chord, scaled by sqrt(2) to preserve the old + * values in case of a square grid. This doesn't use the centroid because the + * LOOPTRI method only uses these three vertices. */ + bpoly->scale_mid = area_tri_v2(vert0_v2, corner_v2, vert1_v2) / + len_v2v2(vert0_v2, vert1_v2) * sqrtf(2); + + if (bpoly->inside) { + /* When inside, interpolate to centroid-based scale close to the center. */ + float min_dist = min_ff(bpoly->scales[0], bpoly->scales[1]); + + bpoly->scale_mid = interpf(bpoly->scale_mid, + (bpoly->scales[0] + bpoly->scales[1]) / 2, + min_ff(bpoly->weight_dist_proj / min_dist, 1)); + } + + /* Verify that the additional computed values are valid. */ + if (bpoly->scale_mid < FLT_EPSILON || + bpoly->point_edgemid_angles[0] + bpoly->point_edgemid_angles[1] < FLT_EPSILON) { + freeBindData(bwdata); + data->success = MOD_SDEF_BIND_RESULT_GENERIC_ERR; + return NULL; + } } } } @@ -688,12 +762,15 @@ BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data, /* Compute angular weight component */ if (epolys->num == 1) { - ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]]); + ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]], + bpolys[0]->edgemid_angle); bpolys[0]->weight_angular *= ang_weights[0] * ang_weights[0]; } else if (epolys->num == 2) { - ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]]); - ang_weights[1] = computeAngularWeight(bpolys[1]->point_edgemid_angles[edge_on_poly[1]]); + ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]], + bpolys[0]->edgemid_angle); + ang_weights[1] = computeAngularWeight(bpolys[1]->point_edgemid_angles[edge_on_poly[1]], + bpolys[1]->edgemid_angle); bpolys[0]->weight_angular *= ang_weights[0] * ang_weights[1]; bpolys[1]->weight_angular *= ang_weights[0] * ang_weights[1]; @@ -731,6 +808,13 @@ BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data, bpoly->dominant_angle_weight = corner_angle_weights[1]; } + /* Check for invalid weights just in case computations fail. */ + if (bpoly->dominant_angle_weight < 0 || bpoly->dominant_angle_weight > 1) { + freeBindData(bwdata); + data->success = MOD_SDEF_BIND_RESULT_GENERIC_ERR; + return NULL; + } + bpoly->dominant_angle_weight = sinf(bpoly->dominant_angle_weight * M_PI_2); /* Compute quadratic angular scale interpolation weight */ @@ -748,10 +832,15 @@ BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data, inv_sqr *= inv_sqr; scale_weight = sqr / (sqr + inv_sqr); + BLI_assert(scale_weight >= 0 && scale_weight <= 1); + /* Compute interpolated scale (no longer need the individual scales, * so simply storing the result over the scale in index zero) */ - bpoly->scales[0] = bpoly->scales[bpoly->dominant_edge] * (1.0f - scale_weight) + - bpoly->scales[!bpoly->dominant_edge] * scale_weight; + bpoly->scales[0] = interpf(bpoly->scale_mid, + interpf(bpoly->scales[!bpoly->dominant_edge], + bpoly->scales[bpoly->dominant_edge], + scale_weight), + bpoly->dominant_angle_weight); /* Scale the point distance weights, and introduce falloff */ bpoly->weight_dist_proj /= bpoly->scales[0]; |