Multires interpolation now works on cases like 
simple cubes. (though it isn't perfect
when bevelling sharp corners).

Normal flipping is handled correctly, and
multires interpolation now works on 
normal-inconsistent meshes (so long as 
they are manifold, at least).
This commit is contained in:
Joseph Eagar
2011-03-30 22:46:56 +00:00
parent 992e78cd40
commit 6e6b4b5f77
10 changed files with 162 additions and 39 deletions

View File

@@ -112,6 +112,7 @@ struct EditMesh;
#define BM_ACTIVE (1<<6)
#define BM_NONORMCALC (1<<7)
#define BM_PINNED (1<<8)
#define BM_FLIPPED (1<<9) /*internal flag, used for ensuring correct normals during multires interpolation*/
#include "bmesh_class.h"
@@ -149,6 +150,7 @@ struct BMFace *BM_Make_Ngon ( struct BMesh *bm, struct BMVert *v1, struct BMVert
#define BM_TestHFlag(ele, f) (ele && (((BMHeader*)ele)->flag & (f)))
#define BM_SetHFlag(ele, f) (((BMHeader*)ele)->flag = ((BMHeader*)ele)->flag | (f))
#define BM_ClearHFlag(ele, f) (((BMHeader*)ele)->flag = ((BMHeader*)ele)->flag & ~(f))
#define BM_ToggleHFlag(ele, f) (((BMHeader*)ele)->flag = ((BMHeader*)ele)->flag ^ (f))
/*stuff for setting indices in elements.*/
#define BMINDEX_SET(ele, i) (((BMHeader*)ele)->index = i)

View File

@@ -255,6 +255,8 @@ typedef struct BMesh {
ListBase errorstack;
struct Object *ob; /*owner object*/
int opflag; /*current operator flag*/
} BMesh;
BMFace *BM_Copy_Face(BMesh *bm, BMFace *f, int copyedges, int copyverts);

View File

@@ -119,6 +119,12 @@ typedef struct BMOpDefine {
/*BMOpDefine->flag*/
#define BMOP_UNTAN_MULTIRES 1 /*switch from multires tangent space to absolute coordinates*/
/*ensures consistent normals before operator execution,
restoring the original ones windings/normals afterwards.
keep in mind, this won't work if the input mesh isn't
manifold.*/
#define BMOP_RATIONALIZE_NORMALS 2
/*------------- Operator API --------------*/
/*data types that use pointers (arrays, etc) should never
@@ -147,6 +153,7 @@ void BMO_Finish_Op(struct BMesh *bm, struct BMOperator *op);
#define BMO_TestFlag(bm, element, flag) (((BMHeader*)(element))->flags[bm->stackdepth-1].f & (flag))
#define BMO_SetFlag(bm, element, flag) (((BMHeader*)(element))->flags[bm->stackdepth-1].f |= (flag))
#define BMO_ClearFlag(bm, element, flag) (((BMHeader*)(element))->flags[bm->stackdepth-1].f &= ~(flag))
#define BMO_ToggleFlag(bm, element, flag) (((BMHeader*)(element))->flags[bm->stackdepth-1].f ^= (flag))
/*profiling showed a significant amount of time spent in BMO_TestFlag
void BMO_SetFlag(struct BMesh *bm, void *element, int flag);

View File

@@ -457,9 +457,9 @@ double quad_coord(double aa[3], double bb[3], double cc[3], double dd[3], int a1
int quad_co(double *x, double *y, double v1[3], double v2[3], double v3[3], double v4[3], double p[3], float n[3])
{
float projverts[5][3];
double xn, yn, zn, dprojverts[4][3], origin[3]={0.0f, 0.0f, 0.0f};
int i, ax, ay;
float projverts[5][3], n2[3];
double dprojverts[4][3], origin[3]={0.0f, 0.0f, 0.0f};
int i;
/*project points into 2d along normal*/
VECCOPY(projverts[0], v1);
@@ -467,7 +467,12 @@ int quad_co(double *x, double *y, double v1[3], double v2[3], double v3[3], doub
VECCOPY(projverts[2], v3);
VECCOPY(projverts[3], v4);
VECCOPY(projverts[4], p);
normal_quad_v3(n2, projverts[0], projverts[1], projverts[2], projverts[3]);
if (INPR(n, n2) < -FLT_EPSILON)
return 0;
/*rotate*/
poly_rotate_plane(n, projverts, 5);
@@ -494,8 +499,8 @@ int quad_co(double *x, double *y, double v1[3], double v2[3], double v3[3], doub
}
/*tl is loop to project onto, sl is loop whose internal displacement, co, is being
projected. x and y are location in loop's mdisps grid of co.*/
/*tl is loop to project onto, l is loop whose internal displacement, co, is being
projected. x and y are location in loop's mdisps grid of point co.*/
static int mdisp_in_mdispquad(BMesh *bm, BMLoop *l, BMLoop *tl, double p[3], double *x, double *y, int res)
{
double v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3];
@@ -534,6 +539,10 @@ static void bmesh_loop_interp_mdisps(BMesh *bm, BMLoop *target, BMFace *source)
double x, y, d, v1[3], v2[3], v3[3], v4[3] = {0.0f, 0.0f, 0.0f}, e1[3], e2[3], e3[3], e4[3];
int ix, iy, res;
/*ignore 2-edged faces*/
if (target->f->len < 3)
return;
if (!CustomData_has_layer(&bm->ldata, CD_MDISPS))
return;

View File

@@ -294,17 +294,46 @@ void BM_Compute_Normals(BMesh *bm)
MEM_freeN(projectverts);
}
/*
* BMESH BEGIN/END EDIT
*
* Functions for setting up a mesh for editing and cleaning up after
* the editing operations are done. These are called by the tools/operator
* API for each time a tool is executed.
*
* Returns -
* Nothing
*
/*
This function ensures correct normals for the mesh, but
sets the flag BM_FLIPPED in flipped faces, to allow restoration
of original normals.
if undo is 0: calculate right normals
if undo is 1: restore original normals
*/
//keep in sycn with utils.c!
#define FACE_FLIP 8
static void bmesh_rationalize_normals(BMesh *bm, int undo) {
BMOperator bmop;
BMFace *f;
BMIter iter;
if (undo) {
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BM_TestHFlag(f, BM_FLIPPED)) {
BM_flip_normal(bm, f);
}
BM_ClearHFlag(f, BM_FLIPPED);
}
return;
}
BMO_InitOpf(bm, &bmop, "righthandfaces faces=%af doflip=%d", 0);
BMO_push(bm, &bmop);
bmesh_righthandfaces_exec(bm, &bmop);
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BMO_TestFlag(bm, f, FACE_FLIP))
BM_SetHFlag(f, BM_FLIPPED);
else BM_ClearHFlag(f, BM_FLIPPED);
}
BMO_pop(bm);
BMO_Finish_Op(bm, &bmop);
}
void bmesh_set_mdisps_space(BMesh *bm, int from, int to)
{
@@ -357,18 +386,45 @@ void bmesh_set_mdisps_space(BMesh *bm, int from, int to)
}
}
/*
* BMESH BEGIN/END EDIT
*
* Functions for setting up a mesh for editing and cleaning up after
* the editing operations are done. These are called by the tools/operator
* API for each time a tool is executed.
*
* Returns -
* Nothing
*
*/
void bmesh_begin_edit(BMesh *bm, int flag) {
bm->opflag = flag;
/*switch multires data out of tangent space*/
if ((flag & BMOP_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS))
if ((flag & BMOP_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
bmesh_set_mdisps_space(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE);
/*ensure correct normals, if possible*/
bmesh_rationalize_normals(bm, 0);
BM_Compute_Normals(bm);
} else if (flag & BMOP_RATIONALIZE_NORMALS) {
bmesh_rationalize_normals(bm, 0);
}
}
void bmesh_end_edit(BMesh *bm, int flag){
/*switch multires data into tangent space*/
if ((flag & BMOP_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
/*set normals to their previous winding*/
bmesh_rationalize_normals(bm, 1);
bmesh_set_mdisps_space(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT);
} else if (flag & BMOP_RATIONALIZE_NORMALS) {
bmesh_rationalize_normals(bm, 1);
}
bm->opflag = 0;
/*compute normals, clear temp flags and flush selections*/
BM_Compute_Normals(bm);
BM_SelectMode_Flush(bm);
/*switch multires data into tangent space*/
if ((flag & BMOP_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS))
bmesh_set_mdisps_space(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT);
}

View File

@@ -665,15 +665,12 @@ int bmesh_loop_length(BMLoop *l)
int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f, BMLoopList *lst){
BMLoop *l = lst->first, *curloop, *oldprev, *oldnext;
BMEdge *staticedar[64], **edar;
int i, j, edok, len = 0;
BMEdge **edar = NULL;
MDisps *md;
BLI_array_staticdeclare(edar, 64);
int i, j, edok, len = 0, do_disps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
len = bmesh_loop_length(l);
if(len >= 64){
edar = MEM_callocN(sizeof(BMEdge *)* len, "BM Edge pointer array");
} else {
edar = staticedar;
}
for(i=0, curloop = l; i< len; i++, curloop= ((BMLoop*)(curloop->next)) ){
curloop->e->head.eflag1 = 0;
@@ -681,7 +678,7 @@ int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f, BMLoopList *lst){
bmesh_radial_remove_loop(curloop, curloop->e);
/*in case of border edges we HAVE to zero out curloop->radial Next/Prev*/
curloop->radial_next = curloop->radial_prev = NULL;
edar[i] = curloop->e;
BLI_array_append(edar, curloop->e);
}
/*actually reverse the loop.*/
@@ -691,6 +688,24 @@ int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f, BMLoopList *lst){
curloop->next = (BMLoop*)oldprev;
curloop->prev = (BMLoop*)oldnext;
curloop = oldnext;
if (do_disps) {
float (*co)[3];
int x, y, sides;
md = CustomData_bmesh_get(&bm->ldata, curloop->head.data, CD_MDISPS);
if (!md->totdisp || !md->disps)
continue;
sides=sqrt(md->totdisp);
co = md->disps;
for (x=0; x<sides; x++) {
for (y=0; y<x; y++) {
swap_v3_v3(co[y*sides+x], co[sides*x + y]);
}
}
}
}
if(len == 2){ //two edged face
@@ -722,8 +737,7 @@ int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f, BMLoopList *lst){
CHECK_ELEMENT(bm, curloop->f);
}
if (edar != staticedar)
MEM_freeN(edar);
BLI_array_free(edar);
CHECK_ELEMENT(bm, f);

View File

@@ -93,10 +93,11 @@ BMOpDefine def_vertexsmooth = {
BMOpDefine def_righthandfaces = {
"righthandfaces",
{{BMOP_OPSLOT_ELEMENT_BUF, "faces"},
{BMOP_OPSLOT_INT, "doflip"}, //internal flag, used by bmesh_rationalize_normals
{0} /*null-terminating sentinel*/,
},
bmesh_righthandfaces_exec,
BMOP_UNTAN_MULTIRES
BMOP_UNTAN_MULTIRES,
};
/*
@@ -150,7 +151,7 @@ BMOpDefine def_reversefaces = {
{0} /*null-terminating sentinel*/,
},
bmesh_reversefaces_exec,
BMOP_UNTAN_MULTIRES
BMOP_UNTAN_MULTIRES,
};
/*

View File

@@ -1286,6 +1286,20 @@ int BMO_CallOpf(BMesh *bm, const char *fmt, ...) {
return 1;
}
/*
* BMO_TOGGLEFLAG
*
* Toggles a flag for a certain element
*
*/
#ifdef BMO_ToggleFlag
#undef BMO_ToggleFlag
#endif
void BMO_ToggleFlag(BMesh *bm, void *element, int flag)
{
BMHeader *head = element;
head->flags[bm->stackdepth-1].f ^= flag;
}
/*
* BMO_SETFLAG

View File

@@ -229,6 +229,7 @@ void bmesh_regionextend_exec(BMesh *bm, BMOperator *op)
#define FACE_VIS 1
#define FACE_FLAG 2
#define FACE_MARK 4
#define FACE_FLIP 8
/* NOTE: these are the original righthandfaces comment in editmesh_mods.c,
copied here for reference.
@@ -257,7 +258,7 @@ void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op)
BLI_array_declare(fstack);
BMLoop *l, *l2;
float maxx, cent[3];
int i, maxi;
int i, maxi, flagflip = BMO_Get_Int(op, "doflip");
startf= NULL;
maxx= -1.0e10;
@@ -285,8 +286,13 @@ void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op)
BM_Compute_Face_Center(bm, startf, cent);
/*make sure the starting face has the correct winding*/
if (cent[0]*startf->no[0] + cent[1]*startf->no[1] + cent[2]*startf->no[2] < 0.0)
if (cent[0]*startf->no[0] + cent[1]*startf->no[1] + cent[2]*startf->no[2] < 0.0) {
BM_flip_normal(bm, startf);
BMO_ToggleFlag(bm, startf, FACE_FLIP);
if (flagflip)
BM_ToggleHFlag(startf, BM_FLIPPED);
}
/*now that we've found our starting face, make all connected faces
have the same winding. this is done recursively, using a manual
@@ -312,9 +318,19 @@ void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op)
BMO_SetFlag(bm, l2->f, FACE_VIS);
i++;
if (l2->v == l->v)
if (l2->v == l->v) {
BM_flip_normal(bm, l2->f);
BMO_ToggleFlag(bm, l2->f, FACE_FLIP);
if (flagflip)
BM_ToggleHFlag(l2->f, BM_FLIPPED);
} else if (BM_TestHFlag(l2->f, BM_FLIPPED) || BM_TestHFlag(l->f, BM_FLIPPED)) {
if (flagflip) {
BM_ClearHFlag(l->f, BM_FLIPPED);
BM_ClearHFlag(l2->f, BM_FLIPPED);
}
}
if (i == maxi) {
BLI_array_growone(fstack);
maxi++;

View File

@@ -1710,11 +1710,13 @@ static int normals_make_consistent_exec(bContext *C, wmOperator *op)
Object *obedit= CTX_data_edit_object(C);
BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
if (!EDBM_CallOpf(em, op, "righthandfaces faces=%hf", BM_SELECT))
/*doflip has to do with bmesh_rationalize_normals, it's an internal
thing*/
if (!EDBM_CallOpf(em, op, "righthandfaces faces=%hf doflip=%d", BM_SELECT, 1))
return OPERATOR_CANCELLED;
if (RNA_boolean_get(op->ptr, "inside"))
EDBM_CallOpf(em, op, "reversefaces faces=%hf", BM_SELECT);
EDBM_CallOpf(em, op, "reversefaces faces=%hf doflip=%d", BM_SELECT, 1);
DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);