1960 lines
50 KiB
C
1960 lines
50 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 */ 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,
|
|
};
|