diff options
Diffstat (limited to 'source/blender/modifiers/intern/MOD_skin.c')
-rw-r--r-- | source/blender/modifiers/intern/MOD_skin.c | 3080 |
1 files changed, 1507 insertions, 1573 deletions
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index dab2d72133b..ffef1197491 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -76,262 +76,249 @@ #include "bmesh.h" typedef struct { - float mat[3][3]; - /* Vert that edge is pointing away from, no relation to - * MEdge.v1 */ - int origin; + float mat[3][3]; + /* Vert that edge is pointing away from, no relation to + * MEdge.v1 */ + int origin; } EMat; typedef enum { - CAP_START = 1, - CAP_END = 2, - SEAM_FRAME = 4, - ROOT = 8, + CAP_START = 1, + CAP_END = 2, + SEAM_FRAME = 4, + ROOT = 8, } SkinNodeFlag; typedef struct Frame { - /* Index in the MVert array */ - BMVert *verts[4]; - /* Location of each corner */ - float co[4][3]; - /* Indicates which corners have been merged with another - * frame's corner (so they share an MVert index) */ - struct { - /* Merge to target frame/corner (no merge if frame is null) */ - struct Frame *frame; - int corner; - /* checked to avoid chaining. - * (merging when we're already been referenced), see T39775 */ - unsigned int is_target : 1; - } merge[4]; - - /* For hull frames, whether each vertex is detached or not */ - bool inside_hull[4]; - /* Whether any part of the frame (corner or edge) is detached */ - bool detached; + /* Index in the MVert array */ + BMVert *verts[4]; + /* Location of each corner */ + float co[4][3]; + /* Indicates which corners have been merged with another + * frame's corner (so they share an MVert index) */ + struct { + /* Merge to target frame/corner (no merge if frame is null) */ + struct Frame *frame; + int corner; + /* checked to avoid chaining. + * (merging when we're already been referenced), see T39775 */ + unsigned int is_target : 1; + } merge[4]; + + /* For hull frames, whether each vertex is detached or not */ + bool inside_hull[4]; + /* Whether any part of the frame (corner or edge) is detached */ + bool detached; } Frame; #define MAX_SKIN_NODE_FRAMES 2 typedef struct { - Frame frames[MAX_SKIN_NODE_FRAMES]; - int totframe; + Frame frames[MAX_SKIN_NODE_FRAMES]; + int totframe; - SkinNodeFlag flag; + SkinNodeFlag flag; - /* Used for hulling a loop seam */ - int seam_edges[2]; + /* Used for hulling a loop seam */ + int seam_edges[2]; } SkinNode; typedef struct { - BMesh *bm; - SkinModifierData *smd; - int mat_nr; + BMesh *bm; + SkinModifierData *smd; + int mat_nr; } SkinOutput; -static void add_poly( - SkinOutput *so, - BMVert *v1, - BMVert *v2, - BMVert *v3, - BMVert *v4); +static void add_poly(SkinOutput *so, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4); /***************************** Convex Hull ****************************/ -static bool is_quad_symmetric( - BMVert *quad[4], - const SkinModifierData *smd) +static bool is_quad_symmetric(BMVert *quad[4], const SkinModifierData *smd) { - const float threshold = 0.0001f; - const float threshold_squared = threshold * threshold; - int axis; - - for (axis = 0; axis < 3; axis++) { - if (smd->symmetry_axes & (1 << axis)) { - float a[3]; - - copy_v3_v3(a, quad[0]->co); - a[axis] = -a[axis]; - - if (len_squared_v3v3(a, quad[1]->co) < threshold_squared) { - copy_v3_v3(a, quad[2]->co); - a[axis] = -a[axis]; - if (len_squared_v3v3(a, quad[3]->co) < threshold_squared) - return 1; - } - else if (len_squared_v3v3(a, quad[3]->co) < threshold_squared) { - copy_v3_v3(a, quad[2]->co); - a[axis] = -a[axis]; - if (len_squared_v3v3(a, quad[1]->co) < threshold_squared) - return 1; - } - } - } - - return 0; + const float threshold = 0.0001f; + const float threshold_squared = threshold * threshold; + int axis; + + for (axis = 0; axis < 3; axis++) { + if (smd->symmetry_axes & (1 << axis)) { + float a[3]; + + copy_v3_v3(a, quad[0]->co); + a[axis] = -a[axis]; + + if (len_squared_v3v3(a, quad[1]->co) < threshold_squared) { + copy_v3_v3(a, quad[2]->co); + a[axis] = -a[axis]; + if (len_squared_v3v3(a, quad[3]->co) < threshold_squared) + return 1; + } + else if (len_squared_v3v3(a, quad[3]->co) < threshold_squared) { + copy_v3_v3(a, quad[2]->co); + a[axis] = -a[axis]; + if (len_squared_v3v3(a, quad[1]->co) < threshold_squared) + return 1; + } + } + } + + return 0; } /* Returns true if the quad crosses the plane of symmetry, false otherwise */ -static bool quad_crosses_symmetry_plane( - BMVert *quad[4], - const SkinModifierData *smd) +static bool quad_crosses_symmetry_plane(BMVert *quad[4], const SkinModifierData *smd) { - int axis; - - for (axis = 0; axis < 3; axis++) { - if (smd->symmetry_axes & (1 << axis)) { - bool left = false, right = false; - int i; - - for (i = 0; i < 4; i++) { - if (quad[i]->co[axis] < 0.0f) - left = true; - else if (quad[i]->co[axis] > 0.0f) - right = true; - - if (left && right) - return true; - } - } - } - - return false; + int axis; + + for (axis = 0; axis < 3; axis++) { + if (smd->symmetry_axes & (1 << axis)) { + bool left = false, right = false; + int i; + + for (i = 0; i < 4; i++) { + if (quad[i]->co[axis] < 0.0f) + left = true; + else if (quad[i]->co[axis] > 0.0f) + right = true; + + if (left && right) + return true; + } + } + } + + return false; } /* Returns true if the frame is filled by precisely two faces (and * outputs those faces to fill_faces), otherwise returns false. */ -static bool skin_frame_find_contained_faces( - const Frame *frame, - BMFace *fill_faces[2]) +static bool skin_frame_find_contained_faces(const Frame *frame, BMFace *fill_faces[2]) { - BMEdge *diag; + BMEdge *diag; - /* See if the frame is bisected by a diagonal edge */ - diag = BM_edge_exists(frame->verts[0], frame->verts[2]); - if (!diag) - diag = BM_edge_exists(frame->verts[1], frame->verts[3]); + /* See if the frame is bisected by a diagonal edge */ + diag = BM_edge_exists(frame->verts[0], frame->verts[2]); + if (!diag) + diag = BM_edge_exists(frame->verts[1], frame->verts[3]); - if (diag) - return BM_edge_face_pair(diag, &fill_faces[0], &fill_faces[1]); - else - return false; + if (diag) + return BM_edge_face_pair(diag, &fill_faces[0], &fill_faces[1]); + else + return false; } /* Returns true if hull is successfully built, false otherwise */ static bool build_hull(SkinOutput *so, Frame **frames, int totframe) { #ifdef WITH_BULLET - BMesh *bm = so->bm; - BMOperator op; - BMIter iter; - BMOIter oiter; - BMVert *v; - BMFace *f; - BMEdge *e; - int i, j; - - BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - - for (i = 0; i < totframe; i++) { - for (j = 0; j < 4; j++) { - BM_elem_flag_enable(frames[i]->verts[j], BM_ELEM_TAG); - } - } - - /* Deselect all faces so that only new hull output faces are - * selected after the operator is run */ - BM_mesh_elem_hflag_disable_all(bm, BM_ALL_NOLOOP, BM_ELEM_SELECT, false); - - BMO_op_initf(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "convex_hull input=%hv", BM_ELEM_TAG); - BMO_op_exec(bm, &op); - - if (BMO_error_occurred(bm)) { - BMO_op_finish(bm, &op); - return false; - } - - /* Apply face attributes to hull output */ - BMO_ITER (f, &oiter, op.slots_out, "geom.out", BM_FACE) { - BM_face_normal_update(f); - if (so->smd->flag & MOD_SKIN_SMOOTH_SHADING) - BM_elem_flag_enable(f, BM_ELEM_SMOOTH); - f->mat_nr = so->mat_nr; - } - - /* Mark interior frames */ - BMO_ITER (v, &oiter, op.slots_out, "geom_interior.out", BM_VERT) { - for (i = 0; i < totframe; i++) { - Frame *frame = frames[i]; - - if (!frame->detached) { - for (j = 0; j < 4; j++) { - if (frame->verts[j] == v) { - frame->inside_hull[j] = true; - frame->detached = true; - break; - } - } - } - } - } - - /* Also mark frames as interior if an edge is not in the hull */ - for (i = 0; i < totframe; i++) { - Frame *frame = frames[i]; - - if (!frame->detached && - (!BM_edge_exists(frame->verts[0], frame->verts[1]) || - !BM_edge_exists(frame->verts[1], frame->verts[2]) || - !BM_edge_exists(frame->verts[2], frame->verts[3]) || - !BM_edge_exists(frame->verts[3], frame->verts[0]))) - { - frame->detached = true; - } - } - - /* Remove triangles that would fill the original frames -- skip if - * frame is partially detached */ - BM_mesh_elem_hflag_disable_all(bm, BM_ALL_NOLOOP, BM_ELEM_TAG, false); - for (i = 0; i < totframe; i++) { - Frame *frame = frames[i]; - if (!frame->detached) { - BMFace *fill_faces[2]; - - /* Check if the frame is filled by precisely two - * triangles. If so, delete the triangles and their shared - * edge. Otherwise, give up and mark the frame as - * detached. */ - if (skin_frame_find_contained_faces(frame, fill_faces)) { - BM_elem_flag_enable(fill_faces[0], BM_ELEM_TAG); - BM_elem_flag_enable(fill_faces[1], BM_ELEM_TAG); - } - else - frame->detached = true; - } - } - - /* Check if removing triangles above will create wire triangles, - * mark them too */ - BMO_ITER (e, &oiter, op.slots_out, "geom.out", BM_EDGE) { - bool is_wire = true; - BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) { - if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { - is_wire = false; - break; - } - } - if (is_wire) - BM_elem_flag_enable(e, BM_ELEM_TAG); - } - - BMO_op_finish(bm, &op); - - BM_mesh_delete_hflag_tagged(bm, BM_ELEM_TAG, BM_EDGE | BM_FACE); - - return true; + BMesh *bm = so->bm; + BMOperator op; + BMIter iter; + BMOIter oiter; + BMVert *v; + BMFace *f; + BMEdge *e; + int i, j; + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); + + for (i = 0; i < totframe; i++) { + for (j = 0; j < 4; j++) { + BM_elem_flag_enable(frames[i]->verts[j], BM_ELEM_TAG); + } + } + + /* Deselect all faces so that only new hull output faces are + * selected after the operator is run */ + BM_mesh_elem_hflag_disable_all(bm, BM_ALL_NOLOOP, BM_ELEM_SELECT, false); + + BMO_op_initf( + bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), "convex_hull input=%hv", BM_ELEM_TAG); + BMO_op_exec(bm, &op); + + if (BMO_error_occurred(bm)) { + BMO_op_finish(bm, &op); + return false; + } + + /* Apply face attributes to hull output */ + BMO_ITER (f, &oiter, op.slots_out, "geom.out", BM_FACE) { + BM_face_normal_update(f); + if (so->smd->flag & MOD_SKIN_SMOOTH_SHADING) + BM_elem_flag_enable(f, BM_ELEM_SMOOTH); + f->mat_nr = so->mat_nr; + } + + /* Mark interior frames */ + BMO_ITER (v, &oiter, op.slots_out, "geom_interior.out", BM_VERT) { + for (i = 0; i < totframe; i++) { + Frame *frame = frames[i]; + + if (!frame->detached) { + for (j = 0; j < 4; j++) { + if (frame->verts[j] == v) { + frame->inside_hull[j] = true; + frame->detached = true; + break; + } + } + } + } + } + + /* Also mark frames as interior if an edge is not in the hull */ + for (i = 0; i < totframe; i++) { + Frame *frame = frames[i]; + + if (!frame->detached && (!BM_edge_exists(frame->verts[0], frame->verts[1]) || + !BM_edge_exists(frame->verts[1], frame->verts[2]) || + !BM_edge_exists(frame->verts[2], frame->verts[3]) || + !BM_edge_exists(frame->verts[3], frame->verts[0]))) { + frame->detached = true; + } + } + + /* Remove triangles that would fill the original frames -- skip if + * frame is partially detached */ + BM_mesh_elem_hflag_disable_all(bm, BM_ALL_NOLOOP, BM_ELEM_TAG, false); + for (i = 0; i < totframe; i++) { + Frame *frame = frames[i]; + if (!frame->detached) { + BMFace *fill_faces[2]; + + /* Check if the frame is filled by precisely two + * triangles. If so, delete the triangles and their shared + * edge. Otherwise, give up and mark the frame as + * detached. */ + if (skin_frame_find_contained_faces(frame, fill_faces)) { + BM_elem_flag_enable(fill_faces[0], BM_ELEM_TAG); + BM_elem_flag_enable(fill_faces[1], BM_ELEM_TAG); + } + else + frame->detached = true; + } + } + + /* Check if removing triangles above will create wire triangles, + * mark them too */ + BMO_ITER (e, &oiter, op.slots_out, "geom.out", BM_EDGE) { + bool is_wire = true; + BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) { + if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { + is_wire = false; + break; + } + } + if (is_wire) + BM_elem_flag_enable(e, BM_ELEM_TAG); + } + + BMO_op_finish(bm, &op); + + BM_mesh_delete_hflag_tagged(bm, BM_ELEM_TAG, BM_EDGE | BM_FACE); + + return true; #else - UNUSED_VARS(so, frames, totframe, skin_frame_find_contained_faces); - return false; + UNUSED_VARS(so, frames, totframe, skin_frame_find_contained_faces); + return false; #endif } @@ -339,428 +326,421 @@ static bool build_hull(SkinOutput *so, Frame **frames, int totframe) * just the average of two adjacent edge lengths) */ static float frame_len(const Frame *frame) { - return (len_v3v3(frame->co[0], frame->co[1]) + - len_v3v3(frame->co[1], frame->co[2])) * 0.5f; + return (len_v3v3(frame->co[0], frame->co[1]) + len_v3v3(frame->co[1], frame->co[2])) * 0.5f; } static void merge_frame_corners(Frame **frames, int totframe) { - float dist, side_a, side_b, thresh, mid[3]; - int i, j, k, l; - - for (i = 0; i < totframe; i++) { - side_a = frame_len(frames[i]); - - /* For each corner of each frame... */ - for (j = 0; j < 4; j++) { - - /* Ensure the merge target is not itself a merge target */ - if (frames[i]->merge[j].frame) - continue; - - for (k = i + 1; k < totframe; k++) { - BLI_assert(frames[i] != frames[k]); - - side_b = frame_len(frames[k]); - thresh = min_ff(side_a, side_b) / 2.0f; - - /* Compare with each corner of all other frames... */ - for (l = 0; l < 4; l++) { - if (frames[k]->merge[l].frame || frames[k]->merge[l].is_target) - continue; - - /* Some additional concerns that could be checked - * further: - * - * + Vertex coords are being used for the - * edge-length test, but are also being - * modified, might cause symmetry problems. - * - * + A frame could be merged diagonally across - * another, would generate a weird (bad) T - * junction - */ - - /* Check if corners are near each other, where - * 'near' is based in the frames' minimum side - * length */ - dist = len_v3v3(frames[i]->co[j], - frames[k]->co[l]); - if (dist < thresh) { - mid_v3_v3v3(mid, - frames[i]->co[j], - frames[k]->co[l]); - - copy_v3_v3(frames[i]->co[j], mid); - copy_v3_v3(frames[k]->co[l], mid); - - frames[k]->merge[l].frame = frames[i]; - frames[k]->merge[l].corner = j; - frames[i]->merge[j].is_target = true; - - /* Can't merge another corner into the same - * frame corner, so move on to frame k+1 */ - break; - } - } - } - } - } + float dist, side_a, side_b, thresh, mid[3]; + int i, j, k, l; + + for (i = 0; i < totframe; i++) { + side_a = frame_len(frames[i]); + + /* For each corner of each frame... */ + for (j = 0; j < 4; j++) { + + /* Ensure the merge target is not itself a merge target */ + if (frames[i]->merge[j].frame) + continue; + + for (k = i + 1; k < totframe; k++) { + BLI_assert(frames[i] != frames[k]); + + side_b = frame_len(frames[k]); + thresh = min_ff(side_a, side_b) / 2.0f; + + /* Compare with each corner of all other frames... */ + for (l = 0; l < 4; l++) { + if (frames[k]->merge[l].frame || frames[k]->merge[l].is_target) + continue; + + /* Some additional concerns that could be checked + * further: + * + * + Vertex coords are being used for the + * edge-length test, but are also being + * modified, might cause symmetry problems. + * + * + A frame could be merged diagonally across + * another, would generate a weird (bad) T + * junction + */ + + /* Check if corners are near each other, where + * 'near' is based in the frames' minimum side + * length */ + dist = len_v3v3(frames[i]->co[j], frames[k]->co[l]); + if (dist < thresh) { + mid_v3_v3v3(mid, frames[i]->co[j], frames[k]->co[l]); + + copy_v3_v3(frames[i]->co[j], mid); + copy_v3_v3(frames[k]->co[l], mid); + + frames[k]->merge[l].frame = frames[i]; + frames[k]->merge[l].corner = j; + frames[i]->merge[j].is_target = true; + + /* Can't merge another corner into the same + * frame corner, so move on to frame k+1 */ + break; + } + } + } + } + } } static Frame **collect_hull_frames( - int v, SkinNode *frames, - const MeshElemMap *emap, const MEdge *medge, - int *tothullframe) + int v, SkinNode *frames, const MeshElemMap *emap, const MEdge *medge, int *tothullframe) { - SkinNode *f; - Frame **hull_frames; - int nbr, i; - - (*tothullframe) = emap[v].count; - hull_frames = MEM_calloc_arrayN((*tothullframe), sizeof(Frame *), - "hull_from_frames.hull_frames"); - i = 0; - for (nbr = 0; nbr < emap[v].count; nbr++) { - const MEdge *e = &medge[emap[v].indices[nbr]]; - f = &frames[BKE_mesh_edge_other_vert(e, v)]; - /* Can't have adjacent branch nodes yet */ - if (f->totframe) - hull_frames[i++] = &f->frames[0]; - else - (*tothullframe)--; - } - - return hull_frames; + SkinNode *f; + Frame **hull_frames; + int nbr, i; + + (*tothullframe) = emap[v].count; + hull_frames = MEM_calloc_arrayN( + (*tothullframe), sizeof(Frame *), "hull_from_frames.hull_frames"); + i = 0; + for (nbr = 0; nbr < emap[v].count; nbr++) { + const MEdge *e = &medge[emap[v].indices[nbr]]; + f = &frames[BKE_mesh_edge_other_vert(e, v)]; + /* Can't have adjacent branch nodes yet */ + if (f->totframe) + hull_frames[i++] = &f->frames[0]; + else + (*tothullframe)--; + } + + return hull_frames; } - /**************************** Create Frames ***************************/ static void node_frames_init(SkinNode *nf, int totframe) { - int i; + int i; - nf->totframe = totframe; - memset(nf->frames, 0, sizeof(nf->frames)); + nf->totframe = totframe; + memset(nf->frames, 0, sizeof(nf->frames)); - nf->flag = 0; - for (i = 0; i < 2; i++) - nf->seam_edges[i] = -1; + nf->flag = 0; + for (i = 0; i < 2; i++) + nf->seam_edges[i] = -1; } static void create_frame( - Frame *frame, const float co[3], - const float radius[2], - float mat[3][3], float offset) + Frame *frame, const float co[3], const float radius[2], float mat[3][3], float offset) { - float rx[3], ry[3], rz[3]; - int i; + float rx[3], ry[3], rz[3]; + int i; - mul_v3_v3fl(ry, mat[1], radius[0]); - mul_v3_v3fl(rz, mat[2], radius[1]); + mul_v3_v3fl(ry, mat[1], radius[0]); + mul_v3_v3fl(rz, mat[2], radius[1]); - add_v3_v3v3(frame->co[3], co, ry); - add_v3_v3v3(frame->co[3], frame->co[3], rz); + add_v3_v3v3(frame->co[3], co, ry); + add_v3_v3v3(frame->co[3], frame->co[3], rz); - sub_v3_v3v3(frame->co[2], co, ry); - add_v3_v3v3(frame->co[2], frame->co[2], rz); + sub_v3_v3v3(frame->co[2], co, ry); + add_v3_v3v3(frame->co[2], frame->co[2], rz); - sub_v3_v3v3(frame->co[1], co, ry); - sub_v3_v3v3(frame->co[1], frame->co[1], rz); + sub_v3_v3v3(frame->co[1], co, ry); + sub_v3_v3v3(frame->co[1], frame->co[1], rz); - add_v3_v3v3(frame->co[0], co, ry); - sub_v3_v3v3(frame->co[0], frame->co[0], rz); + add_v3_v3v3(frame->co[0], co, ry); + sub_v3_v3v3(frame->co[0], frame->co[0], rz); - mul_v3_v3fl(rx, mat[0], offset); - for (i = 0; i < 4; i++) - add_v3_v3v3(frame->co[i], frame->co[i], rx); + mul_v3_v3fl(rx, mat[0], offset); + for (i = 0; i < 4; i++) + add_v3_v3v3(frame->co[i], frame->co[i], rx); } static float half_v2(const float v[2]) { - return (v[0] + v[1]) * 0.5f; + return (v[0] + v[1]) * 0.5f; } -static void end_node_frames( - int v, SkinNode *skin_nodes, const MVert *mvert, - const MVertSkin *nodes, const MeshElemMap *emap, - EMat *emat) +static void end_node_frames(int v, + SkinNode *skin_nodes, + const MVert *mvert, + const MVertSkin *nodes, + const MeshElemMap *emap, + EMat *emat) { - const float *rad = nodes[v].radius; - float mat[3][3]; - - if (emap[v].count == 0) { - float avg = half_v2(rad); - - /* For solitary nodes, just build a box (two frames) */ - node_frames_init(&skin_nodes[v], 2); - skin_nodes[v].flag |= (CAP_START | CAP_END); - - /* Hardcoded basis */ - zero_m3(mat); - mat[0][2] = mat[1][0] = mat[2][1] = 1; - - /* Caps */ - create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, avg); - create_frame(&skin_nodes[v].frames[1], mvert[v].co, rad, mat, -avg); - } - else { - /* For nodes with an incoming edge, create a single (capped) frame */ - node_frames_init(&skin_nodes[v], 1); - skin_nodes[v].flag |= CAP_START; - - /* Use incoming edge for orientation */ - copy_m3_m3(mat, emat[emap[v].indices[0]].mat); - if (emat[emap[v].indices[0]].origin != v) - negate_v3(mat[0]); - - /* End frame */ - create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, 0); - } - - if (nodes[v].flag & MVERT_SKIN_ROOT) - skin_nodes[v].flag |= ROOT; + const float *rad = nodes[v].radius; + float mat[3][3]; + + if (emap[v].count == 0) { + float avg = half_v2(rad); + + /* For solitary nodes, just build a box (two frames) */ + node_frames_init(&skin_nodes[v], 2); + skin_nodes[v].flag |= (CAP_START | CAP_END); + + /* Hardcoded basis */ + zero_m3(mat); + mat[0][2] = mat[1][0] = mat[2][1] = 1; + + /* Caps */ + create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, avg); + create_frame(&skin_nodes[v].frames[1], mvert[v].co, rad, mat, -avg); + } + else { + /* For nodes with an incoming edge, create a single (capped) frame */ + node_frames_init(&skin_nodes[v], 1); + skin_nodes[v].flag |= CAP_START; + + /* Use incoming edge for orientation */ + copy_m3_m3(mat, emat[emap[v].indices[0]].mat); + if (emat[emap[v].indices[0]].origin != v) + negate_v3(mat[0]); + + /* End frame */ + create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, 0); + } + + if (nodes[v].flag & MVERT_SKIN_ROOT) + skin_nodes[v].flag |= ROOT; } /* Returns 1 for seam, 0 otherwise */ static int connection_node_mat(float mat[3][3], int v, const MeshElemMap *emap, EMat *emat) { - float axis[3], angle, ine[3][3], oute[3][3]; - EMat *e1, *e2; - - e1 = &emat[emap[v].indices[0]]; - e2 = &emat[emap[v].indices[1]]; - - if (e1->origin != v && e2->origin == v) { - copy_m3_m3(ine, e1->mat); - copy_m3_m3(oute, e2->mat); - } - else if (e1->origin == v && e2->origin != v) { - copy_m3_m3(ine, e2->mat); - copy_m3_m3(oute, e1->mat); - } - else - return 1; - - /* Get axis and angle to rotate frame by */ - angle = angle_normalized_v3v3(ine[0], oute[0]) / 2.0f; - cross_v3_v3v3(axis, ine[0], oute[0]); - normalize_v3(axis); - - /* Build frame matrix (don't care about X axis here) */ - copy_v3_v3(mat[0], ine[0]); - rotate_normalized_v3_v3v3fl(mat[1], ine[1], axis, angle); - rotate_normalized_v3_v3v3fl(mat[2], ine[2], axis, angle); - - return 0; + float axis[3], angle, ine[3][3], oute[3][3]; + EMat *e1, *e2; + + e1 = &emat[emap[v].indices[0]]; + e2 = &emat[emap[v].indices[1]]; + + if (e1->origin != v && e2->origin == v) { + copy_m3_m3(ine, e1->mat); + copy_m3_m3(oute, e2->mat); + } + else if (e1->origin == v && e2->origin != v) { + copy_m3_m3(ine, e2->mat); + copy_m3_m3(oute, e1->mat); + } + else + return 1; + + /* Get axis and angle to rotate frame by */ + angle = angle_normalized_v3v3(ine[0], oute[0]) / 2.0f; + cross_v3_v3v3(axis, ine[0], oute[0]); + normalize_v3(axis); + + /* Build frame matrix (don't care about X axis here) */ + copy_v3_v3(mat[0], ine[0]); + rotate_normalized_v3_v3v3fl(mat[1], ine[1], axis, angle); + rotate_normalized_v3_v3v3fl(mat[2], ine[2], axis, angle); + + return 0; } -static void connection_node_frames( - int v, SkinNode *skin_nodes, const MVert *mvert, - const MVertSkin *nodes, const MeshElemMap *emap, - EMat *emat) +static void connection_node_frames(int v, + SkinNode *skin_nodes, + const MVert *mvert, + const MVertSkin *nodes, + const MeshElemMap *emap, + EMat *emat) { - const float *rad = nodes[v].radius; - float mat[3][3]; - EMat *e1, *e2; - - if (connection_node_mat(mat, v, emap, emat)) { - float avg = half_v2(rad); - - /* Get edges */ - e1 = &emat[emap[v].indices[0]]; - e2 = &emat[emap[v].indices[1]]; - - /* Handle seam separately to avoid twisting */ - /* Create two frames, will be hulled to neighbors later */ - node_frames_init(&skin_nodes[v], 2); - skin_nodes[v].flag |= SEAM_FRAME; - - copy_m3_m3(mat, e1->mat); - if (e1->origin != v) negate_v3(mat[0]); - create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, avg); - skin_nodes[v].seam_edges[0] = emap[v].indices[0]; - - copy_m3_m3(mat, e2->mat); - if (e2->origin != v) negate_v3(mat[0]); - create_frame(&skin_nodes[v].frames[1], mvert[v].co, rad, mat, avg); - skin_nodes[v].seam_edges[1] = emap[v].indices[1]; - - return; - } - - /* Build regular frame */ - node_frames_init(&skin_nodes[v], 1); - create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, 0); + const float *rad = nodes[v].radius; + float mat[3][3]; + EMat *e1, *e2; + + if (connection_node_mat(mat, v, emap, emat)) { + float avg = half_v2(rad); + + /* Get edges */ + e1 = &emat[emap[v].indices[0]]; + e2 = &emat[emap[v].indices[1]]; + + /* Handle seam separately to avoid twisting */ + /* Create two frames, will be hulled to neighbors later */ + node_frames_init(&skin_nodes[v], 2); + skin_nodes[v].flag |= SEAM_FRAME; + + copy_m3_m3(mat, e1->mat); + if (e1->origin != v) + negate_v3(mat[0]); + create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, avg); + skin_nodes[v].seam_edges[0] = emap[v].indices[0]; + + copy_m3_m3(mat, e2->mat); + if (e2->origin != v) + negate_v3(mat[0]); + create_frame(&skin_nodes[v].frames[1], mvert[v].co, rad, mat, avg); + skin_nodes[v].seam_edges[1] = emap[v].indices[1]; + + return; + } + + /* Build regular frame */ + node_frames_init(&skin_nodes[v], 1); + create_frame(&skin_nodes[v].frames[0], mvert[v].co, rad, mat, 0); } static SkinNode *build_frames( - const MVert *mvert, int totvert, - const MVertSkin *nodes, const MeshElemMap *emap, - EMat *emat) + const MVert *mvert, int totvert, const MVertSkin *nodes, const MeshElemMap *emap, EMat *emat) { - SkinNode *skin_nodes; - int v; - - skin_nodes = MEM_calloc_arrayN(totvert, sizeof(SkinNode), "build_frames.skin_nodes"); - - for (v = 0; v < totvert; v++) { - if (emap[v].count <= 1) - end_node_frames(v, skin_nodes, mvert, nodes, emap, emat); - else if (emap[v].count == 2) - connection_node_frames(v, skin_nodes, mvert, nodes, emap, emat); - else { - /* Branch node generates no frames */ - } - } - - return skin_nodes; + SkinNode *skin_nodes; + int v; + + skin_nodes = MEM_calloc_arrayN(totvert, sizeof(SkinNode), "build_frames.skin_nodes"); + + for (v = 0; v < totvert; v++) { + if (emap[v].count <= 1) + end_node_frames(v, skin_nodes, mvert, nodes, emap, emat); + else if (emap[v].count == 2) + connection_node_frames(v, skin_nodes, mvert, nodes, emap, emat); + else { + /* Branch node generates no frames */ + } + } + + return skin_nodes; } /**************************** Edge Matrices ***************************/ static void calc_edge_mat(float mat[3][3], const float a[3], const float b[3]) { - const float z_up[3] = {0, 0, 1}; - float dot; - - /* X = edge direction */ - sub_v3_v3v3(mat[0], b, a); - normalize_v3(mat[0]); - - dot = dot_v3v3(mat[0], z_up); - if (dot > -1 + FLT_EPSILON && dot < 1 - FLT_EPSILON) { - /* Y = Z cross x */ - cross_v3_v3v3(mat[1], z_up, mat[0]); - normalize_v3(mat[1]); - - /* Z = x cross y */ - cross_v3_v3v3(mat[2], mat[0], mat[1]); - normalize_v3(mat[2]); - } - else { - mat[1][0] = 1; - mat[1][1] = 0; - mat[1][2] = 0; - mat[2][0] = 0; - mat[2][1] = 1; - mat[2][2] = 0; - } + const float z_up[3] = {0, 0, 1}; + float dot; + + /* X = edge direction */ + sub_v3_v3v3(mat[0], b, a); + normalize_v3(mat[0]); + + dot = dot_v3v3(mat[0], z_up); + if (dot > -1 + FLT_EPSILON && dot < 1 - FLT_EPSILON) { + /* Y = Z cross x */ + cross_v3_v3v3(mat[1], z_up, mat[0]); + normalize_v3(mat[1]); + + /* Z = x cross y */ + cross_v3_v3v3(mat[2], mat[0], mat[1]); + normalize_v3(mat[2]); + } + else { + mat[1][0] = 1; + mat[1][1] = 0; + mat[1][2] = 0; + mat[2][0] = 0; + mat[2][1] = 1; + mat[2][2] = 0; + } } typedef struct { - float mat[3][3]; - int parent_v; - int e; + float mat[3][3]; + int parent_v; + int e; } EdgeStackElem; -static void build_emats_stack( - BLI_Stack *stack, BLI_bitmap *visited_e, EMat *emat, - const MeshElemMap *emap, const MEdge *medge, - const MVertSkin *vs, const MVert *mvert) +static void build_emats_stack(BLI_Stack *stack, + BLI_bitmap *visited_e, + EMat *emat, + const MeshElemMap *emap, + const MEdge *medge, + const MVertSkin *vs, + const MVert *mvert) { - EdgeStackElem stack_elem; - float axis[3], angle; - int i, e, v, parent_v, parent_is_branch; - - BLI_stack_pop(stack, &stack_elem); - parent_v = stack_elem.parent_v; - e = stack_elem.e; - - /* Skip if edge already visited */ - if (BLI_BITMAP_TEST(visited_e, e)) - return; - - /* Mark edge as visited */ - BLI_BITMAP_ENABLE(visited_e, e); - - /* Process edge */ - - parent_is_branch = ((emap[parent_v].count > 2) || - (vs[parent_v].flag & MVERT_SKIN_ROOT)); - - v = BKE_mesh_edge_other_vert(&medge[e], parent_v); - emat[e].origin = parent_v; - - /* If parent is a branch node, start a new edge chain */ - if (parent_is_branch) { - calc_edge_mat(emat[e].mat, mvert[parent_v].co, - mvert[v].co); - } - else { - /* Build edge matrix guided by parent matrix */ - sub_v3_v3v3(emat[e].mat[0], mvert[v].co, mvert[parent_v].co); - normalize_v3(emat[e].mat[0]); - angle = angle_normalized_v3v3(stack_elem.mat[0], emat[e].mat[0]); - cross_v3_v3v3(axis, stack_elem.mat[0], emat[e].mat[0]); - normalize_v3(axis); - rotate_normalized_v3_v3v3fl(emat[e].mat[1], stack_elem.mat[1], axis, angle); - rotate_normalized_v3_v3v3fl(emat[e].mat[2], stack_elem.mat[2], axis, angle); - } - - /* Add neighbors to stack */ - for (i = 0; i < emap[v].count; i++) { - /* Add neighbors to stack */ - copy_m3_m3(stack_elem.mat, emat[e].mat); - stack_elem.e = emap[v].indices[i]; - stack_elem.parent_v = v; - BLI_stack_push(stack, &stack_elem); - } + EdgeStackElem stack_elem; + float axis[3], angle; + int i, e, v, parent_v, parent_is_branch; + + BLI_stack_pop(stack, &stack_elem); + parent_v = stack_elem.parent_v; + e = stack_elem.e; + + /* Skip if edge already visited */ + if (BLI_BITMAP_TEST(visited_e, e)) + return; + + /* Mark edge as visited */ + BLI_BITMAP_ENABLE(visited_e, e); + + /* Process edge */ + + parent_is_branch = ((emap[parent_v].count > 2) || (vs[parent_v].flag & MVERT_SKIN_ROOT)); + + v = BKE_mesh_edge_other_vert(&medge[e], parent_v); + emat[e].origin = parent_v; + + /* If parent is a branch node, start a new edge chain */ + if (parent_is_branch) { + calc_edge_mat(emat[e].mat, mvert[parent_v].co, mvert[v].co); + } + else { + /* Build edge matrix guided by parent matrix */ + sub_v3_v3v3(emat[e].mat[0], mvert[v].co, mvert[parent_v].co); + normalize_v3(emat[e].mat[0]); + angle = angle_normalized_v3v3(stack_elem.mat[0], emat[e].mat[0]); + cross_v3_v3v3(axis, stack_elem.mat[0], emat[e].mat[0]); + normalize_v3(axis); + rotate_normalized_v3_v3v3fl(emat[e].mat[1], stack_elem.mat[1], axis, angle); + rotate_normalized_v3_v3v3fl(emat[e].mat[2], stack_elem.mat[2], axis, angle); + } + + /* Add neighbors to stack */ + for (i = 0; i < emap[v].count; i++) { + /* Add neighbors to stack */ + copy_m3_m3(stack_elem.mat, emat[e].mat); + stack_elem.e = emap[v].indices[i]; + stack_elem.parent_v = v; + BLI_stack_push(stack, &stack_elem); + } } -static EMat *build_edge_mats( - const MVertSkin *vs, - const MVert *mvert, - int totvert, - const MEdge *medge, - const MeshElemMap *emap, - int totedge, - bool *has_valid_root) +static EMat *build_edge_mats(const MVertSkin *vs, + const MVert *mvert, + int totvert, + const MEdge *medge, + const MeshElemMap *emap, + int totedge, + bool *has_valid_root) { - BLI_Stack *stack; - EMat *emat; - EdgeStackElem stack_elem; - BLI_bitmap *visited_e; - int i, v; - - stack = BLI_stack_new(sizeof(stack_elem), "build_edge_mats.stack"); - - visited_e = BLI_BITMAP_NEW(totedge, "build_edge_mats.visited_e"); - emat = MEM_calloc_arrayN(totedge, sizeof(EMat), "build_edge_mats.emat"); - - /* Edge matrices are built from the root nodes, add all roots with - * children to the stack */ - for (v = 0; v < totvert; v++) { - if (vs[v].flag & MVERT_SKIN_ROOT) { - if (emap[v].count >= 1) { - const MEdge *e = &medge[emap[v].indices[0]]; - calc_edge_mat(stack_elem.mat, mvert[v].co, - mvert[BKE_mesh_edge_other_vert(e, v)].co); - stack_elem.parent_v = v; - - /* Add adjacent edges to stack */ - for (i = 0; i < emap[v].count; i++) { - stack_elem.e = emap[v].indices[i]; - BLI_stack_push(stack, &stack_elem); - } - - *has_valid_root = true; - } - } - } - - while (!BLI_stack_is_empty(stack)) { - build_emats_stack(stack, visited_e, emat, emap, medge, vs, mvert); - } - - MEM_freeN(visited_e); - BLI_stack_free(stack); - - return emat; + BLI_Stack *stack; + EMat *emat; + EdgeStackElem stack_elem; + BLI_bitmap *visited_e; + int i, v; + + stack = BLI_stack_new(sizeof(stack_elem), "build_edge_mats.stack"); + + visited_e = BLI_BITMAP_NEW(totedge, "build_edge_mats.visited_e"); + emat = MEM_calloc_arrayN(totedge, sizeof(EMat), "build_edge_mats.emat"); + + /* Edge matrices are built from the root nodes, add all roots with + * children to the stack */ + for (v = 0; v < totvert; v++) { + if (vs[v].flag & MVERT_SKIN_ROOT) { + if (emap[v].count >= 1) { + const MEdge *e = &medge[emap[v].indices[0]]; + calc_edge_mat(stack_elem.mat, mvert[v].co, mvert[BKE_mesh_edge_other_vert(e, v)].co); + stack_elem.parent_v = v; + + /* Add adjacent edges to stack */ + for (i = 0; i < emap[v].count; i++) { + stack_elem.e = emap[v].indices[i]; + BLI_stack_push(stack, &stack_elem); + } + + *has_valid_root = true; + } + } + } + + while (!BLI_stack_is_empty(stack)) { + build_emats_stack(stack, visited_e, emat, emap, medge, vs, mvert); + } + + MEM_freeN(visited_e); + BLI_stack_free(stack); + + return emat; } - /************************** Input Subdivision *************************/ /* Returns number of edge subdivisions, taking into account the radius @@ -768,54 +748,54 @@ static EMat *build_edge_mats( * nodes, at least two intermediate frames are required. (This avoids * having any special cases for dealing with sharing a frame between * two hulls.) */ -static int calc_edge_subdivisions( - const MVert *mvert, const MVertSkin *nodes, - const MEdge *e, const int *degree) +static int calc_edge_subdivisions(const MVert *mvert, + const MVertSkin *nodes, + const MEdge *e, + const int *degree) { - /* prevent memory errors [#38003] */ + /* prevent memory errors [#38003] */ #define NUM_SUBDIVISIONS_MAX 128 - const MVertSkin *evs[2] = {&nodes[e->v1], &nodes[e->v2]}; - float avg_radius; - const bool v1_branch = degree[e->v1] > 2; - const bool v2_branch = degree[e->v2] > 2; - int num_subdivisions; - - /* If either end is a branch node marked 'loose', don't subdivide - * the edge (or subdivide just twice if both are branches) */ - if ((v1_branch && (evs[0]->flag & MVERT_SKIN_LOOSE)) || - (v2_branch && (evs[1]->flag & MVERT_SKIN_LOOSE))) - { - if (v1_branch && v2_branch) - return 2; - else - return 0; - } - - avg_radius = half_v2(evs[0]->radius) + half_v2(evs[1]->radius); - - if (avg_radius != 0.0f) { - /* possible (but unlikely) that we overflow INT_MAX */ - float num_subdivisions_fl; - const float edge_len = len_v3v3(mvert[e->v1].co, mvert[e->v2].co); - num_subdivisions_fl = (edge_len / avg_radius); - if (num_subdivisions_fl < NUM_SUBDIVISIONS_MAX) { - num_subdivisions = (int)num_subdivisions_fl; - } - else { - num_subdivisions = NUM_SUBDIVISIONS_MAX; - } - } - else { - num_subdivisions = 0; - } - - /* If both ends are branch nodes, two intermediate nodes are - * required */ - if (num_subdivisions < 2 && v1_branch && v2_branch) - num_subdivisions = 2; - - return num_subdivisions; + const MVertSkin *evs[2] = {&nodes[e->v1], &nodes[e->v2]}; + float avg_radius; + const bool v1_branch = degree[e->v1] > 2; + const bool v2_branch = degree[e->v2] > 2; + int num_subdivisions; + + /* If either end is a branch node marked 'loose', don't subdivide + * the edge (or subdivide just twice if both are branches) */ + if ((v1_branch && (evs[0]->flag & MVERT_SKIN_LOOSE)) || + (v2_branch && (evs[1]->flag & MVERT_SKIN_LOOSE))) { + if (v1_branch && v2_branch) + return 2; + else + return 0; + } + + avg_radius = half_v2(evs[0]->radius) + half_v2(evs[1]->radius); + + if (avg_radius != 0.0f) { + /* possible (but unlikely) that we overflow INT_MAX */ + float num_subdivisions_fl; + const float edge_len = len_v3v3(mvert[e->v1].co, mvert[e->v2].co); + num_subdivisions_fl = (edge_len / avg_radius); + if (num_subdivisions_fl < NUM_SUBDIVISIONS_MAX) { + num_subdivisions = (int)num_subdivisions_fl; + } + else { + num_subdivisions = NUM_SUBDIVISIONS_MAX; + } + } + else { + num_subdivisions = 0; + } + + /* If both ends are branch nodes, two intermediate nodes are + * required */ + if (num_subdivisions < 2 && v1_branch && v2_branch) + num_subdivisions = 2; + + return num_subdivisions; #undef NUM_SUBDIVISIONS_MAX } @@ -824,258 +804,235 @@ static int calc_edge_subdivisions( * reasonably close. */ static Mesh *subdivide_base(Mesh *orig) { - Mesh *result; - MVertSkin *orignode, *outnode; - MVert *origvert, *outvert; - MEdge *origedge, *outedge, *e; - MDeformVert *origdvert, *outdvert; - int totorigvert, totorigedge; - int totsubd, *degree, *edge_subd; - int i, j, k, u, v; - float radrat; - - orignode = CustomData_get_layer(&orig->vdata, CD_MVERT_SKIN); - origvert = orig->mvert; - origedge = orig->medge; - origdvert = orig->dvert; - totorigvert = orig->totvert; - totorigedge = orig->totedge; - - /* Get degree of all vertices */ - degree = MEM_calloc_arrayN(totorigvert, sizeof(int), "degree"); - for (i = 0; i < totorigedge; i++) { - degree[origedge[i].v1]++; - degree[origedge[i].v2]++; - } - - /* Per edge, store how many subdivisions are needed */ - edge_subd = MEM_calloc_arrayN(totorigedge, sizeof(int), "edge_subd"); - for (i = 0, totsubd = 0; i < totorigedge; i++) { - edge_subd[i] += calc_edge_subdivisions(origvert, orignode, - &origedge[i], degree); - BLI_assert(edge_subd[i] >= 0); - totsubd += edge_subd[i]; - } - - MEM_freeN(degree); - - /* Allocate output mesh */ - result = BKE_mesh_new_nomain_from_template( - orig, - totorigvert + totsubd, - totorigedge + totsubd, - 0, 0, 0); - - outvert = result->mvert; - outedge = result->medge; - outnode = CustomData_get_layer(&result->vdata, CD_MVERT_SKIN); - outdvert = result->dvert; - - /* Copy original vertex data */ - CustomData_copy_data(&orig->vdata, - &result->vdata, - 0, 0, totorigvert); - - /* Subdivide edges */ - for (i = 0, v = totorigvert; i < totorigedge; i++) { - struct { - /* Vertex group number */ - int def_nr; - float w1, w2; - } *vgroups = NULL, *vg; - int totvgroup = 0; - - e = &origedge[i]; - - if (origdvert) { - const MDeformVert *dv1 = &origdvert[e->v1]; - const MDeformVert *dv2 = &origdvert[e->v2]; - vgroups = MEM_calloc_arrayN(dv1->totweight, sizeof(*vgroups), "vgroup"); - - /* Only want vertex groups used by both vertices */ - for (j = 0; j < dv1->totweight; j++) { - vg = NULL; - for (k = 0; k < dv2->totweight; k++) { - if (dv1->dw[j].def_nr == dv2->dw[k].def_nr) { - vg = &vgroups[totvgroup]; - totvgroup++; - break; - } - } - - if (vg) { - vg->def_nr = dv1->dw[j].def_nr; - vg->w1 = dv1->dw[j].weight; - vg->w2 = dv2->dw[k].weight; - } - } - } - - u = e->v1; - radrat = (half_v2(outnode[e->v2].radius) / - half_v2(outnode[e->v1].radius)); - radrat = (radrat + 1) / 2; - - /* Add vertices and edge segments */ - for (j = 0; j < edge_subd[i]; j++, v++, outedge++) { - float r = (j + 1) / (float)(edge_subd[i] + 1); - float t = powf(r, radrat); - - /* Interpolate vertex coord */ - interp_v3_v3v3(outvert[v].co, outvert[e->v1].co, - outvert[e->v2].co, t); - - /* Interpolate skin radii */ - interp_v3_v3v3(outnode[v].radius, - orignode[e->v1].radius, - orignode[e->v2].radius, t); - - /* Interpolate vertex group weights */ - for (k = 0; k < totvgroup; k++) { - float weight; - - vg = &vgroups[k]; - weight = interpf(vg->w2, vg->w1, t); - - if (weight > 0) - defvert_add_index_notest(&outdvert[v], vg->def_nr, weight); - } - - outedge->v1 = u; - outedge->v2 = v; - u = v; - } - - if (vgroups) - MEM_freeN(vgroups); - - /* Link up to final vertex */ - outedge->v1 = u; - outedge->v2 = e->v2; - outedge++; - } - - MEM_freeN(edge_subd); - - return result; + Mesh *result; + MVertSkin *orignode, *outnode; + MVert *origvert, *outvert; + MEdge *origedge, *outedge, *e; + MDeformVert *origdvert, *outdvert; + int totorigvert, totorigedge; + int totsubd, *degree, *edge_subd; + int i, j, k, u, v; + float radrat; + + orignode = CustomData_get_layer(&orig->vdata, CD_MVERT_SKIN); + origvert = orig->mvert; + origedge = orig->medge; + origdvert = orig->dvert; + totorigvert = orig->totvert; + totorigedge = orig->totedge; + + /* Get degree of all vertices */ + degree = MEM_calloc_arrayN(totorigvert, sizeof(int), "degree"); + for (i = 0; i < totorigedge; i++) { + degree[origedge[i].v1]++; + degree[origedge[i].v2]++; + } + + /* Per edge, store how many subdivisions are needed */ + edge_subd = MEM_calloc_arrayN(totorigedge, sizeof(int), "edge_subd"); + for (i = 0, totsubd = 0; i < totorigedge; i++) { + edge_subd[i] += calc_edge_subdivisions(origvert, orignode, &origedge[i], degree); + BLI_assert(edge_subd[i] >= 0); + totsubd += edge_subd[i]; + } + + MEM_freeN(degree); + + /* Allocate output mesh */ + result = BKE_mesh_new_nomain_from_template( + orig, totorigvert + totsubd, totorigedge + totsubd, 0, 0, 0); + + outvert = result->mvert; + outedge = result->medge; + outnode = CustomData_get_layer(&result->vdata, CD_MVERT_SKIN); + outdvert = result->dvert; + + /* Copy original vertex data */ + CustomData_copy_data(&orig->vdata, &result->vdata, 0, 0, totorigvert); + + /* Subdivide edges */ + for (i = 0, v = totorigvert; i < totorigedge; i++) { + struct { + /* Vertex group number */ + int def_nr; + float w1, w2; + } *vgroups = NULL, *vg; + int totvgroup = 0; + + e = &origedge[i]; + + if (origdvert) { + const MDeformVert *dv1 = &origdvert[e->v1]; + const MDeformVert *dv2 = &origdvert[e->v2]; + vgroups = MEM_calloc_arrayN(dv1->totweight, sizeof(*vgroups), "vgroup"); + + /* Only want vertex groups used by both vertices */ + for (j = 0; j < dv1->totweight; j++) { + vg = NULL; + for (k = 0; k < dv2->totweight; k++) { + if (dv1->dw[j].def_nr == dv2->dw[k].def_nr) { + vg = &vgroups[totvgroup]; + totvgroup++; + break; + } + } + + if (vg) { + vg->def_nr = dv1->dw[j].def_nr; + vg->w1 = dv1->dw[j].weight; + vg->w2 = dv2->dw[k].weight; + } + } + } + + u = e->v1; + radrat = (half_v2(outnode[e->v2].radius) / half_v2(outnode[e->v1].radius)); + radrat = (radrat + 1) / 2; + + /* Add vertices and edge segments */ + for (j = 0; j < edge_subd[i]; j++, v++, outedge++) { + float r = (j + 1) / (float)(edge_subd[i] + 1); + float t = powf(r, radrat); + + /* Interpolate vertex coord */ + interp_v3_v3v3(outvert[v].co, outvert[e->v1].co, outvert[e->v2].co, t); + + /* Interpolate skin radii */ + interp_v3_v3v3(outnode[v].radius, orignode[e->v1].radius, orignode[e->v2].radius, t); + + /* Interpolate vertex group weights */ + for (k = 0; k < totvgroup; k++) { + float weight; + + vg = &vgroups[k]; + weight = interpf(vg->w2, vg->w1, t); + + if (weight > 0) + defvert_add_index_notest(&outdvert[v], vg->def_nr, weight); + } + + outedge->v1 = u; + outedge->v2 = v; + u = v; + } + + if (vgroups) + MEM_freeN(vgroups); + + /* Link up to final vertex */ + outedge->v1 = u; + outedge->v2 = e->v2; + outedge++; + } + + MEM_freeN(edge_subd); + + return result; } /******************************* Output *******************************/ /* Can be either quad or triangle */ -static void add_poly( - SkinOutput *so, - BMVert *v1, - BMVert *v2, - BMVert *v3, - BMVert *v4) +static void add_poly(SkinOutput *so, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4) { - BMVert *verts[4] = {v1, v2, v3, v4}; - BMFace *f; - - BLI_assert(v1 != v2 && v1 != v3 && v1 != v4); - BLI_assert(v2 != v3 && v2 != v4); - BLI_assert(v3 != v4); - BLI_assert(v1 && v2 && v3); - - f = BM_face_create_verts(so->bm, verts, v4 ? 4 : 3, NULL, BM_CREATE_NO_DOUBLE, true); - BM_face_normal_update(f); - if (so->smd->flag & MOD_SKIN_SMOOTH_SHADING) - BM_elem_flag_enable(f, BM_ELEM_SMOOTH); - f->mat_nr = so->mat_nr; + BMVert *verts[4] = {v1, v2, v3, v4}; + BMFace *f; + + BLI_assert(v1 != v2 && v1 != v3 && v1 != v4); + BLI_assert(v2 != v3 && v2 != v4); + BLI_assert(v3 != v4); + BLI_assert(v1 && v2 && v3); + + f = BM_face_create_verts(so->bm, verts, v4 ? 4 : 3, NULL, BM_CREATE_NO_DOUBLE, true); + BM_face_normal_update(f); + if (so->smd->flag & MOD_SKIN_SMOOTH_SHADING) + BM_elem_flag_enable(f, BM_ELEM_SMOOTH); + f->mat_nr = so->mat_nr; } -static void connect_frames( - SkinOutput *so, - BMVert *frame1[4], - BMVert *frame2[4]) +static void connect_frames(SkinOutput *so, BMVert *frame1[4], BMVert *frame2[4]) { - BMVert *q[4][4] = {{frame2[0], frame2[1], frame1[1], frame1[0]}, - {frame2[1], frame2[2], frame1[2], frame1[1]}, - {frame2[2], frame2[3], frame1[3], frame1[2]}, - {frame2[3], frame2[0], frame1[0], frame1[3]}}; - int i; - bool swap; - - /* Check if frame normals need swap */ + BMVert *q[4][4] = {{frame2[0], frame2[1], frame1[1], frame1[0]}, + {frame2[1], frame2[2], frame1[2], frame1[1]}, + {frame2[2], frame2[3], frame1[3], frame1[2]}, + {frame2[3], frame2[0], frame1[0], frame1[3]}}; + int i; + bool swap; + + /* Check if frame normals need swap */ #if 0 - { - /* simple method, works mostly */ - float p[3], no[3]; - sub_v3_v3v3(p, q[3][0]->co, q[0][0]->co); - normal_quad_v3(no, - q[0][0]->co, q[0][1]->co, - q[0][2]->co, q[0][3]->co); - swap = dot_v3v3(no, p) > 0; - } + { + /* simple method, works mostly */ + float p[3], no[3]; + sub_v3_v3v3(p, q[3][0]->co, q[0][0]->co); + normal_quad_v3(no, + q[0][0]->co, q[0][1]->co, + q[0][2]->co, q[0][3]->co); + swap = dot_v3v3(no, p) > 0; + } #else - { - /* comprehensive method, accumulate flipping of all faces */ - float cent_sides[4][3]; - float cent[3]; - float dot = 0.0f; - - for (i = 0; i < 4; i++) { - mid_v3_v3v3v3v3(cent_sides[i], UNPACK4_EX(, q[i], ->co)); - } - mid_v3_v3v3v3v3(cent, UNPACK4(cent_sides)); - - for (i = 0; i < 4; i++) { - float p[3], no[3]; - normal_quad_v3(no, UNPACK4_EX(, q[i], ->co)); - sub_v3_v3v3(p, cent, cent_sides[i]); - dot += dot_v3v3(no, p); - } - - swap = dot > 0; - } + { + /* comprehensive method, accumulate flipping of all faces */ + float cent_sides[4][3]; + float cent[3]; + float dot = 0.0f; + + for (i = 0; i < 4; i++) { + mid_v3_v3v3v3v3(cent_sides[i], UNPACK4_EX(, q[i], ->co)); + } + mid_v3_v3v3v3v3(cent, UNPACK4(cent_sides)); + + for (i = 0; i < 4; i++) { + float p[3], no[3]; + normal_quad_v3(no, UNPACK4_EX(, q[i], ->co)); + sub_v3_v3v3(p, cent, cent_sides[i]); + dot += dot_v3v3(no, p); + } + + swap = dot > 0; + } #endif - for (i = 0; i < 4; i++) { - if (swap) - add_poly(so, q[i][3], q[i][2], q[i][1], q[i][0]); - else - add_poly(so, q[i][0], q[i][1], q[i][2], q[i][3]); - } + for (i = 0; i < 4; i++) { + if (swap) + add_poly(so, q[i][3], q[i][2], q[i][1], q[i][0]); + else + add_poly(so, q[i][0], q[i][1], q[i][2], q[i][3]); + } } -static void output_frames( - BMesh *bm, - SkinNode *sn, - const MDeformVert *input_dvert) +static void output_frames(BMesh *bm, SkinNode *sn, const MDeformVert *input_dvert) { - Frame *f; - int i, j; - - /* Output all frame verts */ - for (i = 0; i < sn->totframe; i++) { - f = &sn->frames[i]; - for (j = 0; j < 4; j++) { - if (!f->merge[j].frame) { - BMVert *v = f->verts[j] = BM_vert_create(bm, f->co[j], NULL, BM_CREATE_NOP); - - if (input_dvert) { - MDeformVert *dv; - dv = CustomData_bmesh_get(&bm->vdata, - v->head.data, - CD_MDEFORMVERT); - - BLI_assert(dv->totweight == 0); - defvert_copy(dv, input_dvert); - } - } - } - } + Frame *f; + int i, j; + + /* Output all frame verts */ + for (i = 0; i < sn->totframe; i++) { + f = &sn->frames[i]; + for (j = 0; j < 4; j++) { + if (!f->merge[j].frame) { + BMVert *v = f->verts[j] = BM_vert_create(bm, f->co[j], NULL, BM_CREATE_NOP); + + if (input_dvert) { + MDeformVert *dv; + dv = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_MDEFORMVERT); + + BLI_assert(dv->totweight == 0); + defvert_copy(dv, input_dvert); + } + } + } + } } #define PRINT_HOLE_INFO 0 static void calc_frame_center(float center[3], const Frame *frame) { - add_v3_v3v3(center, frame->verts[0]->co, frame->verts[1]->co); - add_v3_v3(center, frame->verts[2]->co); - add_v3_v3(center, frame->verts[3]->co); - mul_v3_fl(center, 0.25f); + add_v3_v3v3(center, frame->verts[0]->co, frame->verts[1]->co); + add_v3_v3(center, frame->verts[2]->co); + add_v3_v3(center, frame->verts[3]->co); + mul_v3_fl(center, 0.25f); } /* Does crappy fan triangulation of poly, may not be so accurate for @@ -1085,32 +1042,30 @@ static int isect_ray_poly(const float ray_start[3], BMFace *f, float *r_lambda) { - BMVert *v, *v_first = NULL, *v_prev = NULL; - BMIter iter; - float best_dist = FLT_MAX; - bool hit = false; - - BM_ITER_ELEM (v, &iter, f, BM_VERTS_OF_FACE) { - if (!v_first) - v_first = v; - else if (v_prev != v_first) { - float dist; - bool curhit; - - curhit = isect_ray_tri_v3(ray_start, ray_dir, - v_first->co, v_prev->co, v->co, - &dist, NULL); - if (curhit && dist < best_dist) { - hit = true; - best_dist = dist; - } - } - - v_prev = v; - } - - *r_lambda = best_dist; - return hit; + BMVert *v, *v_first = NULL, *v_prev = NULL; + BMIter iter; + float best_dist = FLT_MAX; + bool hit = false; + + BM_ITER_ELEM (v, &iter, f, BM_VERTS_OF_FACE) { + if (!v_first) + v_first = v; + else if (v_prev != v_first) { + float dist; + bool curhit; + + curhit = isect_ray_tri_v3(ray_start, ray_dir, v_first->co, v_prev->co, v->co, &dist, NULL); + if (curhit && dist < best_dist) { + hit = true; + best_dist = dist; + } + } + + v_prev = v; + } + + *r_lambda = best_dist; + return hit; } /* Reduce the face down to 'n' corners by collapsing the edges; @@ -1118,237 +1073,238 @@ static int isect_ray_poly(const float ray_start[3], * * The orig_verts should contain the vertices of 'f' */ -static BMFace *collapse_face_corners(BMesh *bm, BMFace *f, int n, - BMVert **orig_verts) +static BMFace *collapse_face_corners(BMesh *bm, BMFace *f, int n, BMVert **orig_verts) { - int orig_len = f->len; - - BLI_assert(n >= 3); - BLI_assert(f->len > n); - if (f->len <= n) - return f; - - /* Collapse shortest edge for now */ - while (f->len > n) { - BMFace *vf; - BMEdge *shortest_edge; - BMVert *v_safe, *v_merge; - BMOperator op; - BMIter iter; - int i; - BMOpSlot *slot_targetmap; - - shortest_edge = BM_face_find_shortest_loop(f)->e; - BMO_op_initf(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), "weld_verts"); - - slot_targetmap = BMO_slot_get(op.slots_in, "targetmap"); - - /* Note: could probably calculate merges in one go to be - * faster */ - - v_safe = shortest_edge->v1; - v_merge = shortest_edge->v2; - mid_v3_v3v3(v_safe->co, v_safe->co, v_merge->co); - BMO_slot_map_elem_insert(&op, slot_targetmap, v_merge, v_safe); - BMO_op_exec(bm, &op); - BMO_op_finish(bm, &op); - - /* Find the new face */ - f = NULL; - BM_ITER_ELEM (vf, &iter, v_safe, BM_FACES_OF_VERT) { - bool wrong_face = false; - - for (i = 0; i < orig_len; i++) { - if (orig_verts[i] == v_merge) { - orig_verts[i] = NULL; - } - else if (orig_verts[i] && - !BM_vert_in_face(orig_verts[i], vf)) - { - wrong_face = true; - break; - } - } - - if (!wrong_face) { - f = vf; - break; - } - } - - BLI_assert(f); - } - - return f; + int orig_len = f->len; + + BLI_assert(n >= 3); + BLI_assert(f->len > n); + if (f->len <= n) + return f; + + /* Collapse shortest edge for now */ + while (f->len > n) { + BMFace *vf; + BMEdge *shortest_edge; + BMVert *v_safe, *v_merge; + BMOperator op; + BMIter iter; + int i; + BMOpSlot *slot_targetmap; + + shortest_edge = BM_face_find_shortest_loop(f)->e; + BMO_op_initf(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), "weld_verts"); + + slot_targetmap = BMO_slot_get(op.slots_in, "targetmap"); + + /* Note: could probably calculate merges in one go to be + * faster */ + + v_safe = shortest_edge->v1; + v_merge = shortest_edge->v2; + mid_v3_v3v3(v_safe->co, v_safe->co, v_merge->co); + BMO_slot_map_elem_insert(&op, slot_targetmap, v_merge, v_safe); + BMO_op_exec(bm, &op); + BMO_op_finish(bm, &op); + + /* Find the new face */ + f = NULL; + BM_ITER_ELEM (vf, &iter, v_safe, BM_FACES_OF_VERT) { + bool wrong_face = false; + + for (i = 0; i < orig_len; i++) { + if (orig_verts[i] == v_merge) { + orig_verts[i] = NULL; + } + else if (orig_verts[i] && !BM_vert_in_face(orig_verts[i], vf)) { + wrong_face = true; + break; + } + } + + if (!wrong_face) { + f = vf; + break; + } + } + + BLI_assert(f); + } + + return f; } /* Choose a good face to merge the frame with, used in case the frame * is completely inside the hull. */ static BMFace *skin_hole_target_face(BMesh *bm, Frame *frame) { - BMFace *f, *isect_target_face, *center_target_face; - BMIter iter; - float frame_center[3]; - float frame_normal[3]; - float best_isect_dist = FLT_MAX; - float best_center_dist = FLT_MAX; - - calc_frame_center(frame_center, frame); - normal_quad_v3(frame_normal, frame->verts[3]->co, - frame->verts[2]->co, frame->verts[1]->co, - frame->verts[0]->co); - - /* Use a line intersection test and nearest center test against - * all faces */ - isect_target_face = center_target_face = NULL; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - float dist, poly_center[3]; - int hit; - - /* Intersection test */ - hit = isect_ray_poly(frame_center, frame_normal, f, &dist); - if (hit && dist < best_isect_dist) { - isect_target_face = f; - best_isect_dist = dist; - } - - /* Nearest test */ - BM_face_calc_center_median(f, poly_center); - dist = len_v3v3(frame_center, poly_center); - if (dist < best_center_dist) { - center_target_face = f; - best_center_dist = dist; - } - } - - f = isect_target_face; - if (!f || best_center_dist < best_isect_dist / 2) - f = center_target_face; - - /* This case is unlikely now, but could still happen. Should look - * into splitting edges to make new faces. */ + BMFace *f, *isect_target_face, *center_target_face; + BMIter iter; + float frame_center[3]; + float frame_normal[3]; + float best_isect_dist = FLT_MAX; + float best_center_dist = FLT_MAX; + + calc_frame_center(frame_center, frame); + normal_quad_v3(frame_normal, + frame->verts[3]->co, + frame->verts[2]->co, + frame->verts[1]->co, + frame->verts[0]->co); + + /* Use a line intersection test and nearest center test against + * all faces */ + isect_target_face = center_target_face = NULL; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float dist, poly_center[3]; + int hit; + + /* Intersection test */ + hit = isect_ray_poly(frame_center, frame_normal, f, &dist); + if (hit && dist < best_isect_dist) { + isect_target_face = f; + best_isect_dist = dist; + } + + /* Nearest test */ + BM_face_calc_center_median(f, poly_center); + dist = len_v3v3(frame_center, poly_center); + if (dist < best_center_dist) { + center_target_face = f; + best_center_dist = dist; + } + } + + f = isect_target_face; + if (!f || best_center_dist < best_isect_dist / 2) + f = center_target_face; + + /* This case is unlikely now, but could still happen. Should look + * into splitting edges to make new faces. */ #if PRINT_HOLE_INFO - if (!f) { - printf("no good face found\n"); - } + if (!f) { + printf("no good face found\n"); + } #endif - return f; + return f; } /* Use edge-length heuristic to choose from eight possible polygon bridges */ -static void skin_choose_quad_bridge_order(BMVert *a[4], BMVert *b[4], - int best_order[4]) +static void skin_choose_quad_bridge_order(BMVert *a[4], BMVert *b[4], int best_order[4]) { - int orders[8][4]; - float shortest_len; - int i, j; - - /* Enumerate all valid orderings */ - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - orders[i][j] = (j + i) % 4; - orders[i + 4][j] = 3 - ((j + i) % 4); - } - } - - shortest_len = FLT_MAX; - for (i = 0; i < 8; i++) { - float len = 0; - - /* Get total edge length for this configuration */ - for (j = 0; j < 4; j++) - len += len_squared_v3v3(a[j]->co, b[orders[i][j]]->co); - - if (len < shortest_len) { - shortest_len = len; - memcpy(best_order, orders[i], sizeof(int) * 4); - } - } + int orders[8][4]; + float shortest_len; + int i, j; + + /* Enumerate all valid orderings */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + orders[i][j] = (j + i) % 4; + orders[i + 4][j] = 3 - ((j + i) % 4); + } + } + + shortest_len = FLT_MAX; + for (i = 0; i < 8; i++) { + float len = 0; + + /* Get total edge length for this configuration */ + for (j = 0; j < 4; j++) + len += len_squared_v3v3(a[j]->co, b[orders[i][j]]->co); + + if (len < shortest_len) { + shortest_len = len; + memcpy(best_order, orders[i], sizeof(int) * 4); + } + } } static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_face) { - BMFace *f; - BMVert *verts[4]; - BMVert **vert_buf = NULL; - BLI_array_declare(vert_buf); - BMOIter oiter; - BMOperator op; - int i, best_order[4]; - BMOpSlot *slot_targetmap; - - BLI_assert(split_face->len >= 3); - - /* Extrude the split face */ - BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); - BM_elem_flag_enable(split_face, BM_ELEM_TAG); - BMO_op_initf(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "extrude_discrete_faces faces=%hf", BM_ELEM_TAG); - BMO_op_exec(bm, &op); - - /* Update split face (should only be one new face created - * during extrusion) */ - split_face = NULL; - BMO_ITER (f, &oiter, op.slots_out, "faces.out", BM_FACE) { - BLI_assert(!split_face); - split_face = f; - } - - BMO_op_finish(bm, &op); - - if (split_face->len == 3) { - BMEdge *longest_edge; - - /* Need at least four ring edges, so subdivide longest edge if - * face is a triangle */ - longest_edge = BM_face_find_longest_loop(split_face)->e; - - BM_mesh_elem_hflag_disable_all(bm, BM_EDGE, BM_ELEM_TAG, false); - BM_elem_flag_enable(longest_edge, BM_ELEM_TAG); - - BMO_op_callf(bm, BMO_FLAG_DEFAULTS, - "subdivide_edges edges=%he cuts=%i quad_corner_type=%i", - BM_ELEM_TAG, 1, SUBD_CORNER_STRAIGHT_CUT); - } - else if (split_face->len > 4) { - /* Maintain a dynamic vert array containing the split_face's - * vertices, avoids frequent allocs in collapse_face_corners() */ - if (BLI_array_len(vert_buf) < split_face->len) { - BLI_array_grow_items(vert_buf, (split_face->len - - BLI_array_len(vert_buf))); - } - - /* Get split face's verts */ - BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, - (void **)vert_buf, split_face->len); - - /* Earlier edge split operations may have turned some quads - * into higher-degree faces */ - split_face = collapse_face_corners(bm, split_face, 4, vert_buf); - } - - /* Done with dynamic array, split_face must now be a quad */ - BLI_array_free(vert_buf); - BLI_assert(split_face->len == 4); - if (split_face->len != 4) - return; - - /* Get split face's verts */ - // BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, (void **)verts, 4); - BM_face_as_array_vert_quad(split_face, verts); - skin_choose_quad_bridge_order(verts, frame->verts, best_order); - - /* Delete split face and merge */ - BM_face_kill(bm, split_face); - BMO_op_init(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "weld_verts"); - slot_targetmap = BMO_slot_get(op.slots_in, "targetmap"); - for (i = 0; i < 4; i++) { - BMO_slot_map_elem_insert(&op, slot_targetmap, verts[i], frame->verts[best_order[i]]); - } - BMO_op_exec(bm, &op); - BMO_op_finish(bm, &op); + BMFace *f; + BMVert *verts[4]; + BMVert **vert_buf = NULL; + BLI_array_declare(vert_buf); + BMOIter oiter; + BMOperator op; + int i, best_order[4]; + BMOpSlot *slot_targetmap; + + BLI_assert(split_face->len >= 3); + + /* Extrude the split face */ + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + BM_elem_flag_enable(split_face, BM_ELEM_TAG); + BMO_op_initf(bm, + &op, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "extrude_discrete_faces faces=%hf", + BM_ELEM_TAG); + BMO_op_exec(bm, &op); + + /* Update split face (should only be one new face created + * during extrusion) */ + split_face = NULL; + BMO_ITER (f, &oiter, op.slots_out, "faces.out", BM_FACE) { + BLI_assert(!split_face); + split_face = f; + } + + BMO_op_finish(bm, &op); + + if (split_face->len == 3) { + BMEdge *longest_edge; + + /* Need at least four ring edges, so subdivide longest edge if + * face is a triangle */ + longest_edge = BM_face_find_longest_loop(split_face)->e; + + BM_mesh_elem_hflag_disable_all(bm, BM_EDGE, BM_ELEM_TAG, false); + BM_elem_flag_enable(longest_edge, BM_ELEM_TAG); + + BMO_op_callf(bm, + BMO_FLAG_DEFAULTS, + "subdivide_edges edges=%he cuts=%i quad_corner_type=%i", + BM_ELEM_TAG, + 1, + SUBD_CORNER_STRAIGHT_CUT); + } + else if (split_face->len > 4) { + /* Maintain a dynamic vert array containing the split_face's + * vertices, avoids frequent allocs in collapse_face_corners() */ + if (BLI_array_len(vert_buf) < split_face->len) { + BLI_array_grow_items(vert_buf, (split_face->len - BLI_array_len(vert_buf))); + } + + /* Get split face's verts */ + BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, (void **)vert_buf, split_face->len); + + /* Earlier edge split operations may have turned some quads + * into higher-degree faces */ + split_face = collapse_face_corners(bm, split_face, 4, vert_buf); + } + + /* Done with dynamic array, split_face must now be a quad */ + BLI_array_free(vert_buf); + BLI_assert(split_face->len == 4); + if (split_face->len != 4) + return; + + /* Get split face's verts */ + // BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, (void **)verts, 4); + BM_face_as_array_vert_quad(split_face, verts); + skin_choose_quad_bridge_order(verts, frame->verts, best_order); + + /* Delete split face and merge */ + BM_face_kill(bm, split_face); + BMO_op_init(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), "weld_verts"); + slot_targetmap = BMO_slot_get(op.slots_in, "targetmap"); + for (i = 0; i < 4; i++) { + BMO_slot_map_elem_insert(&op, slot_targetmap, verts[i], frame->verts[best_order[i]]); + } + BMO_op_exec(bm, &op); + BMO_op_finish(bm, &op); } /* If the frame has some vertices that are inside the hull (detached) @@ -1356,473 +1312,451 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f * whole frame off the hull. */ static void skin_hole_detach_partially_attached_frame(BMesh *bm, Frame *frame) { - int i, attached[4], totattached = 0; - - /* Get/count attached frame corners */ - for (i = 0; i < 4; i++) { - if (!frame->inside_hull[i]) - attached[totattached++] = i; - } - - /* Detach everything */ - for (i = 0; i < totattached; i++) { - BMVert **av = &frame->verts[attached[i]]; - (*av) = BM_vert_create(bm, (*av)->co, *av, BM_CREATE_NOP); - } + int i, attached[4], totattached = 0; + + /* Get/count attached frame corners */ + for (i = 0; i < 4; i++) { + if (!frame->inside_hull[i]) + attached[totattached++] = i; + } + + /* Detach everything */ + for (i = 0; i < totattached; i++) { + BMVert **av = &frame->verts[attached[i]]; + (*av) = BM_vert_create(bm, (*av)->co, *av, BM_CREATE_NOP); + } } - static void quad_from_tris(BMEdge *e, BMFace *adj[2], BMVert *ndx[4]) { - BMVert *tri[2][3]; - BMVert *opp = NULL; - int i, j; + BMVert *tri[2][3]; + BMVert *opp = NULL; + int i, j; - BLI_assert(adj[0]->len == 3 && adj[1]->len == 3); + BLI_assert(adj[0]->len == 3 && adj[1]->len == 3); #if 0 - BM_iter_as_array(bm, BM_VERTS_OF_FACE, adj[0], (void **)tri[0], 3); - BM_iter_as_array(bm, BM_VERTS_OF_FACE, adj[1], (void **)tri[1], 3); + BM_iter_as_array(bm, BM_VERTS_OF_FACE, adj[0], (void **)tri[0], 3); + BM_iter_as_array(bm, BM_VERTS_OF_FACE, adj[1], (void **)tri[1], 3); #else - BM_face_as_array_vert_tri(adj[0], tri[0]); - BM_face_as_array_vert_tri(adj[1], tri[1]); + BM_face_as_array_vert_tri(adj[0], tri[0]); + BM_face_as_array_vert_tri(adj[1], tri[1]); #endif - /* Find what the second tri has that the first doesn't */ - for (i = 0; i < 3; i++) { - if (tri[1][i] != tri[0][0] && - tri[1][i] != tri[0][1] && - tri[1][i] != tri[0][2]) - { - opp = tri[1][i]; - break; - } - } - BLI_assert(opp); - - for (i = 0, j = 0; i < 3; i++, j++) { - ndx[j] = tri[0][i]; - /* When the triangle edge cuts across our quad-to-be, - * throw in the second triangle's vertex */ - if ((tri[0][i] == e->v1 || tri[0][i] == e->v2) && - (tri[0][(i + 1) % 3] == e->v1 || tri[0][(i + 1) % 3] == e->v2)) - { - j++; - ndx[j] = opp; - } - } + /* Find what the second tri has that the first doesn't */ + for (i = 0; i < 3; i++) { + if (tri[1][i] != tri[0][0] && tri[1][i] != tri[0][1] && tri[1][i] != tri[0][2]) { + opp = tri[1][i]; + break; + } + } + BLI_assert(opp); + + for (i = 0, j = 0; i < 3; i++, j++) { + ndx[j] = tri[0][i]; + /* When the triangle edge cuts across our quad-to-be, + * throw in the second triangle's vertex */ + if ((tri[0][i] == e->v1 || tri[0][i] == e->v2) && + (tri[0][(i + 1) % 3] == e->v1 || tri[0][(i + 1) % 3] == e->v2)) { + j++; + ndx[j] = opp; + } + } } static void add_quad_from_tris(SkinOutput *so, BMEdge *e, BMFace *adj[2]) { - BMVert *quad[4]; + BMVert *quad[4]; - quad_from_tris(e, adj, quad); + quad_from_tris(e, adj, quad); - add_poly(so, quad[0], quad[1], quad[2], quad[3]); + add_poly(so, quad[0], quad[1], quad[2], quad[3]); } static void hull_merge_triangles(SkinOutput *so, const SkinModifierData *smd) { - BMIter iter; - BMEdge *e; - HeapSimple *heap; - float score; - - heap = BLI_heapsimple_new(); - - BM_mesh_elem_hflag_disable_all(so->bm, BM_FACE, BM_ELEM_TAG, false); - - /* Build heap */ - BM_ITER_MESH (e, &iter, so->bm, BM_EDGES_OF_MESH) { - BMFace *adj[2]; - - /* Only care if the edge is used by exactly two triangles */ - if (BM_edge_face_pair(e, &adj[0], &adj[1])) { - if (adj[0]->len == 3 && adj[1]->len == 3) { - BMVert *quad[4]; - - BLI_assert(BM_face_is_normal_valid(adj[0])); - BLI_assert(BM_face_is_normal_valid(adj[1])); - - /* Construct quad using the two triangles adjacent to - * the edge */ - quad_from_tris(e, adj, quad); - - /* Calculate a score for the quad, higher score for - * triangles being closer to coplanar */ - score = ((BM_face_calc_area(adj[0]) + - BM_face_calc_area(adj[1])) * - dot_v3v3(adj[0]->no, adj[1]->no)); - - /* Check if quad crosses the axis of symmetry */ - if (quad_crosses_symmetry_plane(quad, smd)) { - /* Increase score if the triangles form a - * symmetric quad, otherwise don't use it */ - if (is_quad_symmetric(quad, smd)) - score *= 10; - else - continue; - } - - /* Don't use the quad if it's concave */ - if (!is_quad_convex_v3(quad[0]->co, quad[1]->co, - quad[2]->co, quad[3]->co)) - { - continue; - } - - BLI_heapsimple_insert(heap, -score, e); - } - } - } - - while (!BLI_heapsimple_is_empty(heap)) { - BMFace *adj[2]; - - e = BLI_heapsimple_pop_min(heap); - - if (BM_edge_face_pair(e, &adj[0], &adj[1])) { - /* If both triangles still free, and if they don't already - * share a border with another face, output as a quad */ - if (!BM_elem_flag_test(adj[0], BM_ELEM_TAG) && - !BM_elem_flag_test(adj[1], BM_ELEM_TAG) && - !BM_face_share_face_check(adj[0], adj[1])) - { - add_quad_from_tris(so, e, adj); - BM_elem_flag_enable(adj[0], BM_ELEM_TAG); - BM_elem_flag_enable(adj[1], BM_ELEM_TAG); - BM_elem_flag_enable(e, BM_ELEM_TAG); - } - } - } - - BLI_heapsimple_free(heap, NULL); - - BM_mesh_delete_hflag_tagged(so->bm, BM_ELEM_TAG, BM_EDGE | BM_FACE); - + BMIter iter; + BMEdge *e; + HeapSimple *heap; + float score; + + heap = BLI_heapsimple_new(); + + BM_mesh_elem_hflag_disable_all(so->bm, BM_FACE, BM_ELEM_TAG, false); + + /* Build heap */ + BM_ITER_MESH (e, &iter, so->bm, BM_EDGES_OF_MESH) { + BMFace *adj[2]; + + /* Only care if the edge is used by exactly two triangles */ + if (BM_edge_face_pair(e, &adj[0], &adj[1])) { + if (adj[0]->len == 3 && adj[1]->len == 3) { + BMVert *quad[4]; + + BLI_assert(BM_face_is_normal_valid(adj[0])); + BLI_assert(BM_face_is_normal_valid(adj[1])); + + /* Construct quad using the two triangles adjacent to + * the edge */ + quad_from_tris(e, adj, quad); + + /* Calculate a score for the quad, higher score for + * triangles being closer to coplanar */ + score = ((BM_face_calc_area(adj[0]) + BM_face_calc_area(adj[1])) * + dot_v3v3(adj[0]->no, adj[1]->no)); + + /* Check if quad crosses the axis of symmetry */ + if (quad_crosses_symmetry_plane(quad, smd)) { + /* Increase score if the triangles form a + * symmetric quad, otherwise don't use it */ + if (is_quad_symmetric(quad, smd)) + score *= 10; + else + continue; + } + + /* Don't use the quad if it's concave */ + if (!is_quad_convex_v3(quad[0]->co, quad[1]->co, quad[2]->co, quad[3]->co)) { + continue; + } + + BLI_heapsimple_insert(heap, -score, e); + } + } + } + + while (!BLI_heapsimple_is_empty(heap)) { + BMFace *adj[2]; + + e = BLI_heapsimple_pop_min(heap); + + if (BM_edge_face_pair(e, &adj[0], &adj[1])) { + /* If both triangles still free, and if they don't already + * share a border with another face, output as a quad */ + if (!BM_elem_flag_test(adj[0], BM_ELEM_TAG) && !BM_elem_flag_test(adj[1], BM_ELEM_TAG) && + !BM_face_share_face_check(adj[0], adj[1])) { + add_quad_from_tris(so, e, adj); + BM_elem_flag_enable(adj[0], BM_ELEM_TAG); + BM_elem_flag_enable(adj[1], BM_ELEM_TAG); + BM_elem_flag_enable(e, BM_ELEM_TAG); + } + } + } + + BLI_heapsimple_free(heap, NULL); + + BM_mesh_delete_hflag_tagged(so->bm, BM_ELEM_TAG, BM_EDGE | BM_FACE); } -static void skin_merge_close_frame_verts(SkinNode *skin_nodes, int totvert, +static void skin_merge_close_frame_verts(SkinNode *skin_nodes, + int totvert, const MeshElemMap *emap, const MEdge *medge) { - Frame **hull_frames; - int v, tothullframe; - - for (v = 0; v < totvert; v++) { - /* Only check branch nodes */ - if (!skin_nodes[v].totframe) { - hull_frames = collect_hull_frames(v, skin_nodes, - emap, medge, - &tothullframe); - merge_frame_corners(hull_frames, tothullframe); - MEM_freeN(hull_frames); - } - } + Frame **hull_frames; + int v, tothullframe; + + for (v = 0; v < totvert; v++) { + /* Only check branch nodes */ + if (!skin_nodes[v].totframe) { + hull_frames = collect_hull_frames(v, skin_nodes, emap, medge, &tothullframe); + merge_frame_corners(hull_frames, tothullframe); + MEM_freeN(hull_frames); + } + } } static void skin_update_merged_vertices(SkinNode *skin_nodes, int totvert) { - int v; + int v; - for (v = 0; v < totvert; ++v) { - SkinNode *sn = &skin_nodes[v]; - int i, j; + for (v = 0; v < totvert; ++v) { + SkinNode *sn = &skin_nodes[v]; + int i, j; - for (i = 0; i < sn->totframe; i++) { - Frame *f = &sn->frames[i]; + for (i = 0; i < sn->totframe; i++) { + Frame *f = &sn->frames[i]; - for (j = 0; j < 4; j++) { - if (f->merge[j].frame) { - /* Merge chaining not allowed */ - BLI_assert(!f->merge[j].frame->merge[f->merge[j].corner].frame); + for (j = 0; j < 4; j++) { + if (f->merge[j].frame) { + /* Merge chaining not allowed */ + BLI_assert(!f->merge[j].frame->merge[f->merge[j].corner].frame); - f->verts[j] = f->merge[j].frame->verts[f->merge[j].corner]; - } - } - } - } + f->verts[j] = f->merge[j].frame->verts[f->merge[j].corner]; + } + } + } + } } -static void skin_fix_hull_topology(BMesh *bm, SkinNode *skin_nodes, - int totvert) +static void skin_fix_hull_topology(BMesh *bm, SkinNode *skin_nodes, int totvert) { - int v; + int v; - for (v = 0; v < totvert; v++) { - SkinNode *sn = &skin_nodes[v]; - int j; + for (v = 0; v < totvert; v++) { + SkinNode *sn = &skin_nodes[v]; + int j; - for (j = 0; j < sn->totframe; j++) { - Frame *f = &sn->frames[j]; + for (j = 0; j < sn->totframe; j++) { + Frame *f = &sn->frames[j]; - if (f->detached) { - BMFace *target_face; + if (f->detached) { + BMFace *target_face; - skin_hole_detach_partially_attached_frame(bm, f); + skin_hole_detach_partially_attached_frame(bm, f); - target_face = skin_hole_target_face(bm, f); - if (target_face) - skin_fix_hole_no_good_verts(bm, f, target_face); - } - } - } + target_face = skin_hole_target_face(bm, f); + if (target_face) + skin_fix_hole_no_good_verts(bm, f, target_face); + } + } + } } -static void skin_output_end_nodes(SkinOutput *so, SkinNode *skin_nodes, - int totvert) +static void skin_output_end_nodes(SkinOutput *so, SkinNode *skin_nodes, int totvert) { - int v; - - for (v = 0; v < totvert; ++v) { - SkinNode *sn = &skin_nodes[v]; - /* Assuming here just two frames */ - if (sn->flag & SEAM_FRAME) { - BMVert *v_order[4]; - int i, order[4]; - - skin_choose_quad_bridge_order(sn->frames[0].verts, - sn->frames[1].verts, - order); - for (i = 0; i < 4; i++) - v_order[i] = sn->frames[1].verts[order[i]]; - connect_frames(so, sn->frames[0].verts, v_order); - } - else if (sn->totframe == 2) { - connect_frames(so, - sn->frames[0].verts, - sn->frames[1].verts); - } - - if (sn->flag & CAP_START) { - if (sn->flag & ROOT) { - add_poly(so, - sn->frames[0].verts[0], - sn->frames[0].verts[1], - sn->frames[0].verts[2], - sn->frames[0].verts[3]); - } - else { - add_poly(so, - sn->frames[0].verts[3], - sn->frames[0].verts[2], - sn->frames[0].verts[1], - sn->frames[0].verts[0]); - } - } - if (sn->flag & CAP_END) { - add_poly(so, - sn->frames[1].verts[0], - sn->frames[1].verts[1], - sn->frames[1].verts[2], - sn->frames[1].verts[3]); - } - } + int v; + + for (v = 0; v < totvert; ++v) { + SkinNode *sn = &skin_nodes[v]; + /* Assuming here just two frames */ + if (sn->flag & SEAM_FRAME) { + BMVert *v_order[4]; + int i, order[4]; + + skin_choose_quad_bridge_order(sn->frames[0].verts, sn->frames[1].verts, order); + for (i = 0; i < 4; i++) + v_order[i] = sn->frames[1].verts[order[i]]; + connect_frames(so, sn->frames[0].verts, v_order); + } + else if (sn->totframe == 2) { + connect_frames(so, sn->frames[0].verts, sn->frames[1].verts); + } + + if (sn->flag & CAP_START) { + if (sn->flag & ROOT) { + add_poly(so, + sn->frames[0].verts[0], + sn->frames[0].verts[1], + sn->frames[0].verts[2], + sn->frames[0].verts[3]); + } + else { + add_poly(so, + sn->frames[0].verts[3], + sn->frames[0].verts[2], + sn->frames[0].verts[1], + sn->frames[0].verts[0]); + } + } + if (sn->flag & CAP_END) { + add_poly(so, + sn->frames[1].verts[0], + sn->frames[1].verts[1], + sn->frames[1].verts[2], + sn->frames[1].verts[3]); + } + } } -static void skin_output_connections(SkinOutput *so, SkinNode *skin_nodes, +static void skin_output_connections(SkinOutput *so, + SkinNode *skin_nodes, const MEdge *medge, int totedge) { - int e; - - for (e = 0; e < totedge; e++) { - SkinNode *a, *b; - a = &skin_nodes[medge[e].v1]; - b = &skin_nodes[medge[e].v2]; - - if (a->totframe && b->totframe) { - if ((a->flag & SEAM_FRAME) || (b->flag & SEAM_FRAME)) { - Frame *fr[2] = {&a->frames[0], &b->frames[0]}; - BMVert *v_order[4]; - int i, order[4]; - - if ((a->flag & SEAM_FRAME) && (e != a->seam_edges[0])) - fr[0]++; - if ((b->flag & SEAM_FRAME) && (e != b->seam_edges[0])) - fr[1]++; - - skin_choose_quad_bridge_order(fr[0]->verts, fr[1]->verts, order); - for (i = 0; i < 4; i++) - v_order[i] = fr[1]->verts[order[i]]; - connect_frames(so, fr[0]->verts, v_order); - } - else { - connect_frames(so, - a->frames[0].verts, - b->frames[0].verts); - } - } - } + int e; + + for (e = 0; e < totedge; e++) { + SkinNode *a, *b; + a = &skin_nodes[medge[e].v1]; + b = &skin_nodes[medge[e].v2]; + + if (a->totframe && b->totframe) { + if ((a->flag & SEAM_FRAME) || (b->flag & SEAM_FRAME)) { + Frame *fr[2] = {&a->frames[0], &b->frames[0]}; + BMVert *v_order[4]; + int i, order[4]; + + if ((a->flag & SEAM_FRAME) && (e != a->seam_edges[0])) + fr[0]++; + if ((b->flag & SEAM_FRAME) && (e != b->seam_edges[0])) + fr[1]++; + + skin_choose_quad_bridge_order(fr[0]->verts, fr[1]->verts, order); + for (i = 0; i < 4; i++) + v_order[i] = fr[1]->verts[order[i]]; + connect_frames(so, fr[0]->verts, v_order); + } + else { + connect_frames(so, a->frames[0].verts, b->frames[0].verts); + } + } + } } -static void skin_smooth_hulls(BMesh *bm, SkinNode *skin_nodes, - int totvert, const SkinModifierData *smd) +static void skin_smooth_hulls(BMesh *bm, + SkinNode *skin_nodes, + int totvert, + const SkinModifierData *smd) { - BMIter iter, eiter; - BMVert *v; - int i, j, k, skey; - - if (smd->branch_smoothing == 0) - return; - - /* Mark all frame vertices */ - BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - for (i = 0; i < totvert; i++) { - for (j = 0; j < skin_nodes[i].totframe; j++) { - Frame *frame = &skin_nodes[i].frames[j]; - - for (k = 0; k < 4; k++) - BM_elem_flag_enable(frame->verts[k], BM_ELEM_TAG); - } - } - - /* Add temporary shapekey layer to store original coordinates */ - BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY); - skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1; - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - copy_v3_v3(CustomData_bmesh_get_n(&bm->vdata, v->head.data, - CD_SHAPEKEY, skey), v->co); - } - - /* Smooth vertices, weight unmarked vertices more strongly (helps - * to smooth frame vertices, but don't want to alter them too - * much) */ - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - BMEdge *e; - float avg[3]; - float weight = smd->branch_smoothing; - int totv = 1; - - if (BM_elem_flag_test(v, BM_ELEM_TAG)) - weight *= 0.5f; - - copy_v3_v3(avg, v->co); - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - BMVert *other = BM_edge_other_vert(e, v); - - add_v3_v3(avg, CustomData_bmesh_get_n(&bm->vdata, - other->head.data, - CD_SHAPEKEY, skey)); - totv++; - } - - if (totv > 1) { - mul_v3_fl(avg, 1.0f / (float)totv); - interp_v3_v3v3(v->co, v->co, avg, weight); - } - } - - /* Done with original coordinates */ - BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey); + BMIter iter, eiter; + BMVert *v; + int i, j, k, skey; + + if (smd->branch_smoothing == 0) + return; + + /* Mark all frame vertices */ + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); + for (i = 0; i < totvert; i++) { + for (j = 0; j < skin_nodes[i].totframe; j++) { + Frame *frame = &skin_nodes[i].frames[j]; + + for (k = 0; k < 4; k++) + BM_elem_flag_enable(frame->verts[k], BM_ELEM_TAG); + } + } + + /* Add temporary shapekey layer to store original coordinates */ + BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY); + skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1; + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + copy_v3_v3(CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, skey), v->co); + } + + /* Smooth vertices, weight unmarked vertices more strongly (helps + * to smooth frame vertices, but don't want to alter them too + * much) */ + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BMEdge *e; + float avg[3]; + float weight = smd->branch_smoothing; + int totv = 1; + + if (BM_elem_flag_test(v, BM_ELEM_TAG)) + weight *= 0.5f; + + copy_v3_v3(avg, v->co); + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + BMVert *other = BM_edge_other_vert(e, v); + + add_v3_v3(avg, CustomData_bmesh_get_n(&bm->vdata, other->head.data, CD_SHAPEKEY, skey)); + totv++; + } + + if (totv > 1) { + mul_v3_fl(avg, 1.0f / (float)totv); + interp_v3_v3v3(v->co, v->co, avg, weight); + } + } + + /* Done with original coordinates */ + BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey); } /* Returns true if all hulls are successfully built, false otherwise */ -static bool skin_output_branch_hulls(SkinOutput *so, SkinNode *skin_nodes, - int totvert, const MeshElemMap *emap, - const MEdge *medge) +static bool skin_output_branch_hulls( + SkinOutput *so, SkinNode *skin_nodes, int totvert, const MeshElemMap *emap, const MEdge *medge) { - bool result = true; - int v; + bool result = true; + int v; - for (v = 0; v < totvert; v++) { - SkinNode *sn = &skin_nodes[v]; + for (v = 0; v < totvert; v++) { + SkinNode *sn = &skin_nodes[v]; - /* Branch node hulls */ - if (!sn->totframe) { - Frame **hull_frames; - int tothullframe; + /* Branch node hulls */ + if (!sn->totframe) { + Frame **hull_frames; + int tothullframe; - hull_frames = collect_hull_frames(v, skin_nodes, - emap, medge, - &tothullframe); - if (!build_hull(so, hull_frames, tothullframe)) - result = false; + hull_frames = collect_hull_frames(v, skin_nodes, emap, medge, &tothullframe); + if (!build_hull(so, hull_frames, tothullframe)) + result = false; - MEM_freeN(hull_frames); - } - } + MEM_freeN(hull_frames); + } + } - return result; + return result; } static BMesh *build_skin(SkinNode *skin_nodes, - int totvert, const MeshElemMap *emap, - const MEdge *medge, int totedge, + int totvert, + const MeshElemMap *emap, + const MEdge *medge, + int totedge, const MDeformVert *input_dvert, SkinModifierData *smd) { - SkinOutput so; - int v; - - so.smd = smd; - so.bm = BM_mesh_create( - &bm_mesh_allocsize_default, - &((struct BMeshCreateParams){.use_toolflags = true,})); - so.mat_nr = 0; - - /* BMESH_TODO: bumping up the stack level (see MOD_array.c) */ - BM_mesh_elem_toolflags_ensure(so.bm); - BMO_push(so.bm, NULL); - bmesh_edit_begin(so.bm, 0); - - if (input_dvert) - BM_data_layer_add(so.bm, &so.bm->vdata, CD_MDEFORMVERT); - - /* Check for mergeable frame corners around hulls before - * outputting vertices */ - skin_merge_close_frame_verts(skin_nodes, totvert, emap, medge); - - /* Write out all frame vertices to the mesh */ - for (v = 0; v < totvert; ++v) { - if (skin_nodes[v].totframe) - output_frames(so.bm, &skin_nodes[v], - input_dvert ? &input_dvert[v] : NULL); - } - - /* Update vertex pointers for merged frame corners */ - skin_update_merged_vertices(skin_nodes, totvert); - - if (!skin_output_branch_hulls(&so, skin_nodes, totvert, emap, medge)) - modifier_setError(&smd->modifier, "Hull error"); - - /* Merge triangles here in the hope of providing better target - * faces for skin_fix_hull_topology() to connect to */ - hull_merge_triangles(&so, smd); - - /* Using convex hulls may not generate a nice manifold mesh. Two - * problems can occur: an input frame's edges may be inside the - * hull, and/or an input frame's vertices may be inside the hull. - * - * General fix to produce manifold mesh: for any frame that is - * partially detached, first detach it fully, then find a suitable - * existing face to merge with. (Note that we do this after - * creating all hull faces, but before creating any other - * faces. - */ - skin_fix_hull_topology(so.bm, skin_nodes, totvert); - - skin_smooth_hulls(so.bm, skin_nodes, totvert, smd); - - skin_output_end_nodes(&so, skin_nodes, totvert); - skin_output_connections(&so, skin_nodes, medge, totedge); - hull_merge_triangles(&so, smd); - - bmesh_edit_end(so.bm, 0); - BMO_pop(so.bm); - - return so.bm; + SkinOutput so; + int v; + + so.smd = smd; + so.bm = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + so.mat_nr = 0; + + /* BMESH_TODO: bumping up the stack level (see MOD_array.c) */ + BM_mesh_elem_toolflags_ensure(so.bm); + BMO_push(so.bm, NULL); + bmesh_edit_begin(so.bm, 0); + + if (input_dvert) + BM_data_layer_add(so.bm, &so.bm->vdata, CD_MDEFORMVERT); + + /* Check for mergeable frame corners around hulls before + * outputting vertices */ + skin_merge_close_frame_verts(skin_nodes, totvert, emap, medge); + + /* Write out all frame vertices to the mesh */ + for (v = 0; v < totvert; ++v) { + if (skin_nodes[v].totframe) + output_frames(so.bm, &skin_nodes[v], input_dvert ? &input_dvert[v] : NULL); + } + + /* Update vertex pointers for merged frame corners */ + skin_update_merged_vertices(skin_nodes, totvert); + + if (!skin_output_branch_hulls(&so, skin_nodes, totvert, emap, medge)) + modifier_setError(&smd->modifier, "Hull error"); + + /* Merge triangles here in the hope of providing better target + * faces for skin_fix_hull_topology() to connect to */ + hull_merge_triangles(&so, smd); + + /* Using convex hulls may not generate a nice manifold mesh. Two + * problems can occur: an input frame's edges may be inside the + * hull, and/or an input frame's vertices may be inside the hull. + * + * General fix to produce manifold mesh: for any frame that is + * partially detached, first detach it fully, then find a suitable + * existing face to merge with. (Note that we do this after + * creating all hull faces, but before creating any other + * faces. + */ + skin_fix_hull_topology(so.bm, skin_nodes, totvert); + + skin_smooth_hulls(so.bm, skin_nodes, totvert, smd); + + skin_output_end_nodes(&so, skin_nodes, totvert); + skin_output_connections(&so, skin_nodes, medge, totedge); + hull_merge_triangles(&so, smd); + + bmesh_edit_end(so.bm, 0); + BMO_pop(so.bm); + + return so.bm; } static void skin_set_orig_indices(Mesh *mesh) { - int *orig, totpoly; + int *orig, totpoly; - totpoly = mesh->totpoly; - orig = CustomData_add_layer(&mesh->pdata, CD_ORIGINDEX, - CD_CALLOC, NULL, totpoly); - copy_vn_i(orig, totpoly, ORIGINDEX_NONE); + totpoly = mesh->totpoly; + orig = CustomData_add_layer(&mesh->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, totpoly); + copy_vn_i(orig, totpoly, ORIGINDEX_NONE); } /* @@ -1831,129 +1765,129 @@ static void skin_set_orig_indices(Mesh *mesh) * 2) Generate node frames * 3) Output vertices and polygons from frames, connections, and hulls */ -static Mesh *base_skin(Mesh *origmesh, - SkinModifierData *smd) +static Mesh *base_skin(Mesh *origmesh, SkinModifierData *smd) { - Mesh *result; - MVertSkin *nodes; - BMesh *bm; - EMat *emat; - SkinNode *skin_nodes; - MeshElemMap *emap; - int *emapmem; - MVert *mvert; - MEdge *medge; - MDeformVert *dvert; - int totvert, totedge; - bool has_valid_root = false; + Mesh *result; + MVertSkin *nodes; + BMesh *bm; + EMat *emat; + SkinNode *skin_nodes; + MeshElemMap *emap; + int *emapmem; + MVert *mvert; + MEdge *medge; + MDeformVert *dvert; + int totvert, totedge; + bool has_valid_root = false; - nodes = CustomData_get_layer(&origmesh->vdata, CD_MVERT_SKIN); + nodes = CustomData_get_layer(&origmesh->vdata, CD_MVERT_SKIN); - mvert = origmesh->mvert; - dvert = origmesh->dvert; - medge = origmesh->medge; - totvert = origmesh->totvert; - totedge = origmesh->totedge; + mvert = origmesh->mvert; + dvert = origmesh->dvert; + medge = origmesh->medge; + totvert = origmesh->totvert; + totedge = origmesh->totedge; - BKE_mesh_vert_edge_map_create(&emap, &emapmem, medge, totvert, totedge); + BKE_mesh_vert_edge_map_create(&emap, &emapmem, medge, totvert, totedge); - emat = build_edge_mats(nodes, mvert, totvert, medge, emap, totedge, &has_valid_root); - skin_nodes = build_frames(mvert, totvert, nodes, emap, emat); - MEM_freeN(emat); - emat = NULL; + emat = build_edge_mats(nodes, mvert, totvert, medge, emap, totedge, &has_valid_root); + skin_nodes = build_frames(mvert, totvert, nodes, emap, emat); + MEM_freeN(emat); + emat = NULL; - bm = build_skin(skin_nodes, totvert, emap, medge, totedge, dvert, smd); + bm = build_skin(skin_nodes, totvert, emap, medge, totedge, dvert, smd); - MEM_freeN(skin_nodes); - MEM_freeN(emap); - MEM_freeN(emapmem); + MEM_freeN(skin_nodes); + MEM_freeN(emap); + MEM_freeN(emapmem); - if (!has_valid_root) { - modifier_setError(&smd->modifier, "No valid root vertex found (you need one per mesh island you want to skin)"); - } + if (!has_valid_root) { + modifier_setError( + &smd->modifier, + "No valid root vertex found (you need one per mesh island you want to skin)"); + } - if (!bm) - return NULL; + if (!bm) + return NULL; - result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL); - BM_mesh_free(bm); + result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL); + BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - skin_set_orig_indices(result); + skin_set_orig_indices(result); - return result; + return result; } static Mesh *final_skin(SkinModifierData *smd, Mesh *mesh) { - Mesh *result; + Mesh *result; - /* Skin node layer is required */ - if (!CustomData_get_layer(&mesh->vdata, CD_MVERT_SKIN)) - return mesh; + /* Skin node layer is required */ + if (!CustomData_get_layer(&mesh->vdata, CD_MVERT_SKIN)) + return mesh; - mesh = subdivide_base(mesh); - result = base_skin(mesh, smd); + mesh = subdivide_base(mesh); + result = base_skin(mesh, smd); - BKE_id_free(NULL, mesh); - return result; + BKE_id_free(NULL, mesh); + return result; } - /**************************** Skin Modifier ***************************/ static void initData(ModifierData *md) { - SkinModifierData *smd = (SkinModifierData *) md; + SkinModifierData *smd = (SkinModifierData *)md; - /* Enable in editmode by default */ - md->mode |= eModifierMode_Editmode; + /* Enable in editmode by default */ + md->mode |= eModifierMode_Editmode; - smd->branch_smoothing = 0; - smd->flag = 0; - smd->symmetry_axes = MOD_SKIN_SYMM_X; + smd->branch_smoothing = 0; + smd->flag = 0; + smd->symmetry_axes = MOD_SKIN_SYMM_X; } -static Mesh *applyModifier(ModifierData *md, - const ModifierEvalContext *UNUSED(ctx), - Mesh *mesh) +static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh) { - Mesh *result; + Mesh *result; - if (!(result = final_skin((SkinModifierData *)md, mesh))) - return mesh; - return result; + if (!(result = final_skin((SkinModifierData *)md, mesh))) + return mesh; + return result; } -static void requiredDataMask(Object *UNUSED(ob), ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks) +static void requiredDataMask(Object *UNUSED(ob), + ModifierData *UNUSED(md), + CustomData_MeshMasks *r_cddata_masks) { - r_cddata_masks->vmask |= CD_MASK_MVERT_SKIN | CD_MASK_MDEFORMVERT; + r_cddata_masks->vmask |= CD_MASK_MVERT_SKIN | CD_MASK_MDEFORMVERT; } ModifierTypeInfo modifierType_Skin = { - /* name */ "Skin", - /* structName */ "SkinModifierData", - /* structSize */ sizeof(SkinModifierData), - /* type */ eModifierTypeType_Constructive, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode, - - /* copyData */ modifier_copyData_generic, - - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* applyModifier */ applyModifier, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ NULL, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachObjectLink */ NULL, - /* foreachIDLink */ NULL, - /* freeRuntimeData */ NULL, + /* name */ "Skin", + /* structName */ "SkinModifierData", + /* structSize */ sizeof(SkinModifierData), + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode, + + /* copyData */ modifier_copyData_generic, + + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* freeRuntimeData */ NULL, }; |