Solidify bmesh op and edit mode tool

This commit is contained in:
Andrew Wiggin
2011-09-23 12:06:47 +00:00
parent 75e7f63322
commit 74e21161e3
6 changed files with 253 additions and 29 deletions

View File

@@ -165,6 +165,7 @@ float angle_v3v3v3(const float a[3], const float b[3], const float c[3]);
float angle_normalized_v3v3(const float v1[3], const float v2[3]);
void angle_tri_v3(float angles[3], const float v1[3], const float v2[3], const float v3[3]);
void angle_quad_v3(float angles[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3]);
void angle_poly_v3(float* angles, const float* verts[3], int len);
/********************************* Geometry **********************************/

View File

@@ -241,6 +241,20 @@ void angle_quad_v3(float angles[4], const float v1[3], const float v2[3], const
angles[3]= (float)M_PI - angle_normalized_v3v3(ed4, ed1);
}
void angle_poly_v3(float *angles, const float *verts[3], int len)
{
int i;
float vec[3][3];
sub_v3_v3v3(vec[2], verts[len-1], verts[0]);
normalize_v3(vec[2]);
for (i = 0; i < len; i++) {
sub_v3_v3v3(vec[i%3], verts[i%len], verts[(i+1)%len]);
normalize_v3(vec[i%3]);
angles[i] = (float)M_PI - angle_normalized_v3v3(vec[(i+2)%3], vec[i%3]);
}
}
/********************************* Geometry **********************************/
/* Project v1 on v2 */

View File

@@ -593,6 +593,7 @@ static BMOpDefine def_extrudefaceregion = {
"extrudefaceregion",
{{BMOP_OPSLOT_ELEMENT_BUF, "edgefacein"},
{BMOP_OPSLOT_MAPPING, "exclude"},
{BMOP_OPSLOT_INT, "alwayskeeporig"},
{BMOP_OPSLOT_ELEMENT_BUF, "geomout"},
{0} /*null-terminating sentinel*/},
extrude_edge_context_exec,
@@ -1021,6 +1022,21 @@ static BMOpDefine def_triangle_fill = {
BMOP_UNTAN_MULTIRES
};
/*
Solidify
Turns a mesh into a shell with thickness
*/
static BMOpDefine def_solidify = {
"solidify",
{{BMOP_OPSLOT_ELEMENT_BUF, "geom"},
{BMOP_OPSLOT_FLT, "thickness"},
{BMOP_OPSLOT_ELEMENT_BUF, "geomout"},
{0}},
bmesh_solidify_face_region_exec,
0
};
BMOpDefine *opdefines[] = {
&def_splitop,
&def_spinop,
@@ -1086,6 +1102,7 @@ BMOpDefine *opdefines[] = {
&def_beautify_fill,
&def_triangle_fill,
&def_bridge_loops,
&def_solidify,
};
int bmesh_total_ops = (sizeof(opdefines) / sizeof(void*));

View File

@@ -74,5 +74,6 @@ void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op);
void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op);
void bmesh_create_circle_exec(BMesh *bm, BMOperator *op);
void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op);
void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op);
#endif

View File

@@ -17,9 +17,13 @@
#include <stdlib.h>
#include <string.h>
#define EXT_INPUT 1
#define EXT_KEEP 2
#define EXT_DEL 4
#define EXT_INPUT 1
#define EXT_KEEP 2
#define EXT_DEL 4
#define VERT_NONMAN 1
#define EDGE_NONMAN 1
#define FACE_MARK 1
void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op)
{
@@ -179,21 +183,23 @@ void extrude_edge_context_exec(BMesh *bm, BMOperator *op)
BMO_Flag_Buffer(bm, op, "edgefacein", EXT_INPUT, BM_EDGE|BM_FACE);
/*if one flagged face is bordered by an unflagged face, then we delete
original geometry.*/
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_TestFlag(bm, e, EXT_INPUT)) continue;
original geometry unless caller explicitly asked to keep it. */
if (!BMO_Get_Int(op, "alwayskeeporig")) {
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_TestFlag(bm, e, EXT_INPUT)) continue;
found = 0;
f = BMIter_New(&fiter, bm, BM_FACES_OF_EDGE, e);
for (rlen=0; f; f=BMIter_Step(&fiter), rlen++) {
if (!BMO_TestFlag(bm, f, EXT_INPUT)) {
found = 1;
delorig = 1;
break;
found = 0;
f = BMIter_New(&fiter, bm, BM_FACES_OF_EDGE, e);
for (rlen=0; f; f=BMIter_Step(&fiter), rlen++) {
if (!BMO_TestFlag(bm, f, EXT_INPUT)) {
found = 1;
delorig = 1;
break;
}
}
}
if (!found && (rlen > 1)) BMO_SetFlag(bm, e, EXT_DEL);
if (!found && (rlen > 1)) BMO_SetFlag(bm, e, EXT_DEL);
}
}
/*calculate verts to delete*/
@@ -223,8 +229,11 @@ void extrude_edge_context_exec(BMesh *bm, BMOperator *op)
if (BMO_TestFlag(bm, f, EXT_INPUT))
BMO_SetFlag(bm, f, EXT_DEL);
}
if (delorig) BMO_InitOpf(bm, &delop, "del geom=%fvef context=%d",
EXT_DEL, DEL_ONLYTAGGED);
if (delorig) {
BMO_InitOpf(bm, &delop, "del geom=%fvef context=%d",
EXT_DEL, DEL_ONLYTAGGED);
}
BMO_CopySlot(op, &dupeop, "edgefacein", "geom");
BMO_Exec_Op(bm, &dupeop);
@@ -321,3 +330,181 @@ void extrude_edge_context_exec(BMesh *bm, BMOperator *op)
if (delorig) BMO_Finish_Op(bm, &delop);
BMO_Finish_Op(bm, &dupeop);
}
/*
* Compute higher-quality vertex normals used by solidify.
* Note that this will not work for non-manifold regions.
*
*/
static void calc_solidify_normals(BMesh *bm)
{
BMIter viter, eiter, fiter;
BMVert *v;
BMEdge *e;
BMLoop *l;
BMFace *f, *f1, *f2;
float edge_normal[3];
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, NULL) {
/* Mark the non-manifold edges and the vertices they connect */
if (BM_Nonmanifold_Edge(bm, e)) {
BMO_SetFlag(bm, e, EDGE_NONMAN);
BMO_SetFlag(bm, e->v1, VERT_NONMAN);
BMO_SetFlag(bm, e->v2, VERT_NONMAN);
}
}
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
BM_SetIndex(v, 0);
zero_v3(v->no);
}
BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) {
float angle;
if (BMO_TestFlag(bm, e, EDGE_NONMAN)) {
continue;
}
l = e->l;
f1 = l->f;
f2 = bmesh_radial_nextloop(l)->f;
angle = angle_normalized_v3v3(f1->no, f2->no);
if (f1 != f2) {
if (angle > 0.0f) {
/* two faces using this edge, calculate the edges normal
* using the angle between the faces as a weighting */
add_v3_v3v3(edge_normal, f1->no, f2->no);
normalize_v3(edge_normal);
mul_v3_fl(edge_normal, angle);
}
else {
/* can't do anything useful here!
Set the face index for a vert incase it gets a zero normal */
BM_SetIndex(e->v1, -1);
BM_SetIndex(e->v2, -1);
continue;
}
}
else {
/* only one face attached to that edge */
/* an edge without another attached- the weight on this is
* undefined, M_PI/2 is 90d in radians and that seems good enough */
copy_v3_v3(edge_normal, f1->no);
mul_v3_fl(edge_normal, M_PI/2);
}
add_v3_v3(e->v1->no, edge_normal);
add_v3_v3(e->v2->no, edge_normal);
}
/* normalize accumulated vertex normals*/
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_TestFlag(bm, v, VERT_NONMAN)) {
/* use standard normals for vertices connected to non-manifold
edges */
BM_Vert_UpdateNormal(bm, v);
}
else if (normalize_v3(v->no) == 0.0f && BM_GetIndex(v) < 0) {
/* exceptional case, totally flat. use the normal
of any face around the vertex */
f = BMIter_New(&fiter, bm, BM_FACES_OF_VERT, v);
if (f) {
copy_v3_v3(v->no, f->no);
}
}
}
}
static void solidify_add_thickness(BMesh *bm, float dist)
{
BMFace *f;
BMVert *v;
BMLoop *l;
BMIter iter, loopIter;
float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */
float *vert_accum = vert_angles + bm->totvert;
float angle;
int i, index;
float maxdist = dist * sqrtf(3.0f);
/* array for passing verts to angle_poly_v3 */
float **verts = NULL;
BLI_array_staticdeclare(verts, 16);
/* array for receiving angles from angle_poly_v3 */
float *angles = NULL;
BLI_array_staticdeclare(angles, 16);
i = 0;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BM_SetIndex(v, i++);
}
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if(!BMO_TestFlag(bm, f, FACE_MARK)) {
continue;
}
BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
BLI_array_append(verts, l->v->co);
BLI_array_growone(angles);
}
angle_poly_v3(angles, verts, f->len);
i = 0;
BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
v = l->v;
index = BM_GetIndex(v);
angle = angles[i];
vert_accum[index] += angle;
vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * angle;
i++;
}
BLI_array_empty(verts);
BLI_array_empty(angles);
}
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
index = BM_GetIndex(v);
if(vert_accum[index]) { /* zero if unselected */
float vdist = MIN2(maxdist, dist * vert_angles[index] / vert_accum[index]);
madd_v3_v3fl(v->co, v->no, vdist);
}
}
MEM_freeN(vert_angles);
}
void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op)
{
BMOperator extrudeop;
BMOperator reverseop;
float thickness;
thickness = BMO_Get_Float(op, "thickness");
/* Flip original faces (so the shell is extruded inward) */
BMO_Init_Op(&reverseop, "reversefaces");
BMO_CopySlot(op, &reverseop, "geom", "faces");
BMO_Exec_Op(bm, &reverseop);
BMO_Finish_Op(bm, &reverseop);
calc_solidify_normals(bm);
/* Extrude the region */
BMO_InitOpf(bm, &extrudeop, "extrudefaceregion alwayskeeporig=%i", 1);
BMO_CopySlot(op, &extrudeop, "geom", "edgefacein");
BMO_Exec_Op(bm, &extrudeop);
/* Push the verts of the extruded faces inward to create thickness */
BMO_Flag_Buffer(bm, &extrudeop, "geomout", FACE_MARK, BM_FACE);
solidify_add_thickness(bm, thickness);
BMO_CopySlot(&extrudeop, op, "geomout", "geomout");
BMO_Finish_Op(bm, &extrudeop);
}

View File

@@ -3145,31 +3145,35 @@ void MESH_OT_select_axis(wmOperatorType *ot)
RNA_def_enum(ot->srna, "axis", axis_items_xyz, 0, "Axis", "Select the axis to compare each vertex on");
}
static int solidify_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
static int solidify_exec(bContext *C, wmOperator *op)
{
#if 0
Object *obedit= CTX_data_edit_object(C);
EditMesh *em= BKE_mesh_get_editmesh(((Mesh *)obedit->data));
float nor[3] = {0,0,1};
Mesh *me = obedit->data;
BMEditMesh *em = me->edit_btmesh;
BMesh *bm = em->bm;
BMOperator bmop;
float thickness= RNA_float_get(op->ptr, "thickness");
extrudeflag(obedit, em, SELECT, nor, 1);
EM_make_hq_normals(em);
EM_solidify(em, thickness);
BMO_InitOpf(bm, &bmop, "solidify geom=%hf thickness=%f", BM_SELECT, thickness);
/* deselect only the faces in the region to be solidified (leave wire
edges and loose verts selected, as there will be no corresponding
geometry selected below) */
BMO_UnHeaderFlag_Buffer(bm, &bmop, "geom", BM_SELECT, BM_FACE);
/* update vertex normals too */
recalc_editnormals(em);
/* run the solidify operator */
BMO_Exec_Op(bm, &bmop);
BKE_mesh_end_editmesh(obedit->data, em);
/* select the newly generated faces */
BMO_HeaderFlag_Buffer(bm, &bmop, "geomout", BM_SELECT, BM_FACE);
BMO_Finish_Op(bm, &bmop);
DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
return OPERATOR_FINISHED;
#endif
return OPERATOR_CANCELLED;
}