We already have different storages for cddata of verts, edges etc., 'simply' do the same for the mask flags we use all around Blender code to request some data, or limit some operation to some layers, etc. Reason we need this is that some cddata types (like Normals) are actually shared between verts/polys/loops, and we don’t want to generate clnors everytime we request vnors! As a side note, this also does final fix to T59338, which was the trigger for this patch (need to request computed loop normals for another mesh than evaluated one). Reviewers: brecht, campbellbarton, sergey Differential Revision: https://developer.blender.org/D4407
1965 lines
51 KiB
C
1965 lines
51 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup modifiers
|
|
*/
|
|
|
|
/* Implementation based in part off the paper "B-Mesh: A Fast Modeling
|
|
* System for Base Meshes of 3D Articulated Shapes" (Zhongping Ji,
|
|
* Ligang Liu, Yigang Wang)
|
|
*
|
|
* Note that to avoid confusion with Blender's BMesh data structure,
|
|
* this tool is renamed as the Skin modifier.
|
|
*
|
|
* The B-Mesh paper is current available here:
|
|
* http://www.math.zju.edu.cn/ligangliu/CAGD/Projects/BMesh/
|
|
*
|
|
* The main missing features in this code compared to the paper are:
|
|
*
|
|
* + No mesh evolution. The paper suggests iteratively subsurfing the
|
|
* skin output and adapting the output to better conform with the
|
|
* spheres of influence surrounding each vertex.
|
|
*
|
|
* + No mesh fairing. The paper suggests re-aligning output edges to
|
|
* follow principal mesh curvatures.
|
|
*
|
|
* + No auxiliary balls. These would serve to influence mesh
|
|
* evolution, which as noted above is not implemented.
|
|
*
|
|
* The code also adds some features not present in the paper:
|
|
*
|
|
* + Loops in the input edge graph.
|
|
*
|
|
* + Concave surfaces around branch nodes. The paper does not discuss
|
|
* how to handle non-convex regions; this code adds a number of
|
|
* cleanup operations to handle many (though not all) of these
|
|
* cases.
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLI_array.h"
|
|
#include "BLI_bitmap.h"
|
|
#include "BLI_heap_simple.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_stack.h"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
|
|
#include "BKE_deform.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_mesh_mapping.h"
|
|
#include "BKE_modifier.h"
|
|
|
|
#include "MOD_modifiertypes.h"
|
|
|
|
#include "bmesh.h"
|
|
|
|
typedef struct {
|
|
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,
|
|
} 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;
|
|
} Frame;
|
|
|
|
#define MAX_SKIN_NODE_FRAMES 2
|
|
typedef struct {
|
|
Frame frames[MAX_SKIN_NODE_FRAMES];
|
|
int totframe;
|
|
|
|
SkinNodeFlag flag;
|
|
|
|
/* Used for hulling a loop seam */
|
|
int seam_edges[2];
|
|
} SkinNode;
|
|
|
|
typedef struct {
|
|
BMesh *bm;
|
|
SkinModifierData *smd;
|
|
int mat_nr;
|
|
} SkinOutput;
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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])
|
|
{
|
|
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]);
|
|
|
|
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;
|
|
#else
|
|
UNUSED_VARS(so, frames, totframe, skin_frame_find_contained_faces);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/* Returns the average frame side length (frames are rectangular, so
|
|
* 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Frame **collect_hull_frames(
|
|
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;
|
|
}
|
|
|
|
|
|
/**************************** Create Frames ***************************/
|
|
|
|
static void node_frames_init(SkinNode *nf, int totframe)
|
|
{
|
|
int i;
|
|
|
|
nf->totframe = totframe;
|
|
memset(nf->frames, 0, sizeof(nf->frames));
|
|
|
|
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)
|
|
{
|
|
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]);
|
|
|
|
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[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);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static SkinNode *build_frames(
|
|
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;
|
|
}
|
|
|
|
/**************************** 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;
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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
|
|
* of the endpoints and the edge length. If both endpoints are branch
|
|
* 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)
|
|
{
|
|
/* 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;
|
|
|
|
#undef NUM_SUBDIVISIONS_MAX
|
|
}
|
|
|
|
/* Take a Mesh and subdivide its edges to keep skin nodes
|
|
* 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;
|
|
}
|
|
|
|
/******************************* Output *******************************/
|
|
|
|
/* Can be either quad or triangle */
|
|
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;
|
|
}
|
|
|
|
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 */
|
|
#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;
|
|
}
|
|
#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;
|
|
}
|
|
#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]);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#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);
|
|
}
|
|
|
|
/* Does crappy fan triangulation of poly, may not be so accurate for
|
|
* concave faces */
|
|
static int isect_ray_poly(const float ray_start[3],
|
|
const float ray_dir[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;
|
|
}
|
|
|
|
/* Reduce the face down to 'n' corners by collapsing the edges;
|
|
* returns the new face.
|
|
*
|
|
* The orig_verts should contain the vertices of 'f'
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* 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. */
|
|
#if PRINT_HOLE_INFO
|
|
if (!f) {
|
|
printf("no good face found\n");
|
|
}
|
|
#endif
|
|
|
|
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])
|
|
{
|
|
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);
|
|
}
|
|
|
|
/* If the frame has some vertices that are inside the hull (detached)
|
|
* and some attached, duplicate the attached vertices and take the
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
|
|
static void quad_from_tris(BMEdge *e, BMFace *adj[2], BMVert *ndx[4])
|
|
{
|
|
BMVert *tri[2][3];
|
|
BMVert *opp = NULL;
|
|
int i, j;
|
|
|
|
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);
|
|
#else
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void add_quad_from_tris(SkinOutput *so, BMEdge *e, BMFace *adj[2])
|
|
{
|
|
BMVert *quad[4];
|
|
|
|
quad_from_tris(e, adj, quad);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void skin_update_merged_vertices(SkinNode *skin_nodes, int totvert)
|
|
{
|
|
int v;
|
|
|
|
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 (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];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void skin_fix_hull_topology(BMesh *bm, SkinNode *skin_nodes,
|
|
int totvert)
|
|
{
|
|
int v;
|
|
|
|
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];
|
|
|
|
if (f->detached) {
|
|
BMFace *target_face;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
bool result = true;
|
|
int v;
|
|
|
|
for (v = 0; v < totvert; v++) {
|
|
SkinNode *sn = &skin_nodes[v];
|
|
|
|
/* 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;
|
|
|
|
MEM_freeN(hull_frames);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static BMesh *build_skin(SkinNode *skin_nodes,
|
|
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;
|
|
}
|
|
|
|
static void skin_set_orig_indices(Mesh *mesh)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 0) Subdivide edges (in caller)
|
|
* 1) Generate good edge matrices (uses root nodes)
|
|
* 2) Generate node frames
|
|
* 3) Output vertices and polygons from frames, connections, and hulls
|
|
*/
|
|
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;
|
|
|
|
nodes = CustomData_get_layer(&origmesh->vdata, CD_MVERT_SKIN);
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
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 (!bm)
|
|
return NULL;
|
|
|
|
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL);
|
|
BM_mesh_free(bm);
|
|
|
|
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
|
|
|
skin_set_orig_indices(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
static Mesh *final_skin(SkinModifierData *smd, Mesh *mesh)
|
|
{
|
|
Mesh *result;
|
|
|
|
/* 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);
|
|
|
|
BKE_id_free(NULL, mesh);
|
|
return result;
|
|
}
|
|
|
|
|
|
/**************************** Skin Modifier ***************************/
|
|
|
|
static void initData(ModifierData *md)
|
|
{
|
|
SkinModifierData *smd = (SkinModifierData *) md;
|
|
|
|
/* Enable in editmode by default */
|
|
md->mode |= eModifierMode_Editmode;
|
|
|
|
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)
|
|
{
|
|
Mesh *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)
|
|
{
|
|
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_DM */ NULL,
|
|
/* deformMatrices_DM */ NULL,
|
|
/* deformVertsEM_DM */ NULL,
|
|
/* deformMatricesEM_DM*/NULL,
|
|
/* applyModifier_DM */ NULL,
|
|
|
|
/* 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,
|
|
};
|