From e03ab146ae673ec296e97f3c146c048417833521 Mon Sep 17 00:00:00 2001 From: Geoffrey Bantle Date: Sat, 23 Feb 2008 22:11:16 +0000 Subject: -> Bevel tools and Bmesh kernel The following is a commit of Levi Schooley's bevel code and the bmesh library it depends on. The current editmode bevel has been replaced with a new per edge bevel function. Vertex beveling is also availible. To set weights for the modifier to use, use the ctrl-shift-e shortcut on either edges or vertices. Recursive beveling is turned of for the time being. --- source/blender/blenkernel/BKE_bmesh.h | 250 ++++ source/blender/blenkernel/BKE_global.h | 5 + source/blender/blenkernel/intern/BME_conversions.c | 406 ++++++ source/blender/blenkernel/intern/BME_eulers.c | 1007 +++++++++++++++ source/blender/blenkernel/intern/BME_mesh.c | 334 +++++ source/blender/blenkernel/intern/BME_structure.c | 618 +++++++++ source/blender/blenkernel/intern/BME_tools.c | 1326 ++++++++++++++++++++ source/blender/blenkernel/intern/bmesh_private.h | 71 ++ source/blender/blenkernel/intern/cdderivedmesh.c | 2 + source/blender/blenkernel/intern/modifier.c | 99 ++ source/blender/blenlib/BLI_editVert.h | 2 + source/blender/include/BIF_transform.h | 3 + source/blender/include/butspace.h | 1 + source/blender/include/transform.h | 7 + source/blender/makesdna/DNA_meshdata_types.h | 4 +- source/blender/makesdna/DNA_modifier_types.h | 24 +- source/blender/src/buttons_editing.c | 75 +- source/blender/src/drawobject.c | 38 + source/blender/src/editmesh.c | 26 +- source/blender/src/editmesh_tools.c | 50 +- source/blender/src/header_view3d.c | 10 +- source/blender/src/space.c | 12 + source/blender/src/transform.c | 206 +++ 23 files changed, 4560 insertions(+), 16 deletions(-) create mode 100644 source/blender/blenkernel/BKE_bmesh.h create mode 100644 source/blender/blenkernel/intern/BME_conversions.c create mode 100644 source/blender/blenkernel/intern/BME_eulers.c create mode 100644 source/blender/blenkernel/intern/BME_mesh.c create mode 100644 source/blender/blenkernel/intern/BME_structure.c create mode 100644 source/blender/blenkernel/intern/BME_tools.c create mode 100644 source/blender/blenkernel/intern/bmesh_private.h diff --git a/source/blender/blenkernel/BKE_bmesh.h b/source/blender/blenkernel/BKE_bmesh.h new file mode 100644 index 00000000000..6a2209618d5 --- /dev/null +++ b/source/blender/blenkernel/BKE_bmesh.h @@ -0,0 +1,250 @@ +/** + * BKE_bmesh.h jan 2007 + * + * BMesh modeler structure and functions. + * + * $Id: BKE_bmesh.h,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifndef BKE_BMESH_H +#define BKE_BMESH_H + +#include "DNA_listBase.h" +#include "BLI_ghash.h" +#include "BLI_memarena.h" +#include "DNA_customdata_types.h" +#include "BLI_editVert.h" +#include "BKE_DerivedMesh.h" +#include "transform.h" + +struct BME_Vert; +struct BME_Edge; +struct BME_Poly; +struct BME_Loop; +struct RetopoPaintData; +struct DerivedMesh; + +typedef struct BME_CycleNode{ + struct BME_CycleNode *next, *prev; + void *data; +} BME_CycleNode; + +typedef struct BME_Mesh +{ + ListBase verts, edges, polys, loops; + int lock; /*if set, all calls to eulers will fail.*/ + struct BME_Mesh *backup; /*full copy of the mesh*/ + int totvert, totedge, totpoly, totloop; /*record keeping*/ + int nextv, nexte, nextp, nextl; /*Next element ID for verts/edges/faces/loops. Never reused*/ + struct CustomData vdata, edata, pdata, ldata; /*Custom Data Layer information*/ + struct DerivedMesh *derivedFinal, *derivedCage; + struct RetopoPaintData *retopo_paint_data; /*here for temporary code compatibility only*/ + //BME_ElementList selection; + int lastDataMask; +} BME_Mesh; + +//60, 52, 52, 12 704 +//60, 52, 84 + + +typedef struct BME_Vert +{ + struct BME_Vert *next, *prev; + int EID; + float co[3]; /*vertex location. Actually pointer to custom data block*/ + float no[3]; /*vertex normal. Actually pointer to custom data block*/ + struct BME_Edge *edge; /*first edge in the disk cycle for this vertex*/ + void *data; /*custom vertex data*/ + int eflag1, eflag2; /*reserved for use by eulers*/ + int tflag1, tflag2; /*reserved for use by tools*/ + unsigned short flag, h; + float bweight; +} BME_Vert; + +typedef struct BME_Edge +{ + struct BME_Edge *next, *prev; + int EID; + struct BME_Vert *v1, *v2; /*note that order of vertex pointers means nothing to eulers*/ + struct BME_CycleNode d1, d2; /*disk cycle nodes for v1 and v2 respectivley*/ + struct BME_Loop *loop; /*first BME_Loop in the radial cycle around this edge*/ + void *data; /*custom edge data*/ + int eflag1, eflag2; /*reserved for use by eulers*/ + int tflag1, tflag2; /*reserved for use by tools*/ + unsigned char flag, h; + float crease, bweight; +} BME_Edge; + +typedef struct BME_Loop +{ + struct BME_Loop *next, *prev; /*circularly linked list around face*/ + int EID; + struct BME_CycleNode radial; /*circularly linked list used to find faces around an edge*/ + struct BME_CycleNode *gref; /*pointer to loop ref. Nasty.*/ + struct BME_Vert *v; /*vertex that this loop starts at.*/ + struct BME_Edge *e; /*edge this loop belongs to*/ + struct BME_Poly *f; /*face this loop belongs to*/ + void *data; /*custom per face vertex data*/ + int eflag1, eflag2; /*reserved for use by eulers*/ + int tflag1, tflag2; /*reserved for use by tools*/ + unsigned short flag, h; +} BME_Loop; + +typedef struct BME_Poly +{ + struct BME_Poly *next, *prev; + int EID; + //~ float no[3]; + struct BME_Loop *loopbase; /*First editloop around Polygon.*/ + struct ListBase holes; /*list of inner loops in the face*/ + unsigned int len; /*total length of the face. Eulers should preserve this data*/ + void *data; /*custom face data*/ + int eflag1, eflag2; /*reserved for use by eulers*/ + int tflag1, tflag2; /*reserved for use by tools*/ + unsigned short flag, h, mat_nr; +} BME_Poly; + +//*EDGE UTILITIES*/ +int BME_verts_in_edge(struct BME_Vert *v1, struct BME_Vert *v2, struct BME_Edge *e); +int BME_vert_in_edge(struct BME_Edge *e, BME_Vert *v); +struct BME_Vert *BME_edge_getothervert(struct BME_Edge *e, struct BME_Vert *v); + +/*GENERAL CYCLE*/ +int BME_cycle_length(void *h); + +/*DISK CYCLE*/ +struct BME_Edge *BME_disk_nextedge(struct BME_Edge *e, struct BME_Vert *v); +struct BME_CycleNode *BME_disk_getpointer(struct BME_Edge *e, struct BME_Vert *v); +struct BME_Edge *BME_disk_next_edgeflag(struct BME_Edge *e, struct BME_Vert *v, int eflag, int tflag); +int BME_disk_count_edgeflag(struct BME_Vert *v, int eflag, int tflag); + +/*RADIAL CYCLE*/ +struct BME_Loop *BME_radial_nextloop(struct BME_Loop *l); +int BME_radial_find_face(struct BME_Edge *e,struct BME_Poly *f); + +/*LOOP CYCLE*/ +struct BME_Loop *BME_loop_find_loop(struct BME_Poly *f, struct BME_Vert *v); + +/*MESH CREATION/DESTRUCTION*/ +struct BME_Mesh *BME_make_mesh(void); +void BME_free_mesh(struct BME_Mesh *bm); +struct BME_Mesh *BME_copy_mesh(struct BME_Mesh *bm); +/*FULL MESH VALIDATION*/ +int BME_validate_mesh(struct BME_Mesh *bm, int halt); +/*ENTER/EXIT MODELLING LOOP*/ +int BME_model_begin(struct BME_Mesh *bm); +void BME_model_end(struct BME_Mesh *bm); + +/*MESH CONSTRUCTION API.*/ +/*MAKE*/ +struct BME_Vert *BME_MV(struct BME_Mesh *bm, float *vec); +struct BME_Edge *BME_ME(struct BME_Mesh *bm, struct BME_Vert *v1, struct BME_Vert *v2); +struct BME_Poly *BME_MF(struct BME_Mesh *bm, struct BME_Vert *v1, struct BME_Vert *v2, struct BME_Edge **elist, int len); +/*KILL*/ +int BME_KV(struct BME_Mesh *bm, struct BME_Vert *v); +int BME_KE(struct BME_Mesh *bm, struct BME_Edge *e); +int BME_KF(struct BME_Mesh *bm, struct BME_Poly *bply); +/*SPLIT*/ +struct BME_Vert *BME_SEMV(struct BME_Mesh *bm, struct BME_Vert *tv, struct BME_Edge *e, struct BME_Edge **re); +struct BME_Poly *BME_SFME(struct BME_Mesh *bm, struct BME_Poly *f, struct BME_Vert *v1, struct BME_Vert *v2, struct BME_Loop **rl); +/*JOIN*/ +int BME_JEKV(struct BME_Mesh *bm, struct BME_Edge *ke, struct BME_Vert *kv); +struct BME_Poly *BME_JFKE(struct BME_Mesh *bm, struct BME_Poly *f1, struct BME_Poly *f2,struct BME_Edge *e); /*no reason to return BME_Poly pointer?*/ +/*NORMAL FLIP(Is its own inverse)*/ +int BME_loop_reverse(struct BME_Mesh *bm, struct BME_Poly *f); + +/*TOOLS CODE*/ +struct BME_Loop *BME_inset_edge(struct BME_Mesh *bm, struct BME_Loop *l, struct BME_Poly *f); +struct BME_Poly *BME_inset_poly(struct BME_Mesh *bm, struct BME_Poly *f); + +/* bevel tool defines */ +/* element flags */ +#define BME_BEVEL_ORIG 1 +#define BME_BEVEL_BEVEL (1<<1) +#define BME_BEVEL_NONMAN (1<<2) +#define BME_BEVEL_WIRE (1<<3) + +/* tool options */ +#define BME_BEVEL_SELECT 1 +#define BME_BEVEL_VERT (1<<1) +#define BME_BEVEL_RADIUS (1<<2) +#define BME_BEVEL_ANGLE (1<<3) +#define BME_BEVEL_WEIGHT (1<<4) +//~ #define BME_BEVEL_EWEIGHT (1<<4) +//~ #define BME_BEVEL_VWEIGHT (1<<5) +#define BME_BEVEL_PERCENT (1<<6) +#define BME_BEVEL_EMIN (1<<7) +#define BME_BEVEL_EMAX (1<<8) +#define BME_BEVEL_RUNNING (1<<9) +#define BME_BEVEL_RES (1<<10) + +typedef struct BME_TransData { + BME_Mesh *bm; /* the bmesh the vert belongs to */ + BME_Vert *v; /* pointer to the vert this tdata applies to */ + float co[3]; /* the original coordinate */ + float org[3]; /* the origin */ + float vec[3]; /* a directional vector; always, always normalize! */ + void *loc; /* a pointer to the data to transform (likely the vert's cos) */ + float factor; /* primary scaling factor; also accumulates number of weighted edges for beveling tool */ + float weight; /* another scaling factor; used primarily for propogating vertex weights to transforms; */ + /* weight is also used across recursive bevels to help with the math */ + float maxfactor; /* the unscaled, original factor (used only by "edge verts" in recursive beveling) */ + float *max; /* the maximum distance this vert can be transformed; negative is infinite + * it points to the "parent" maxfactor (where maxfactor makes little sense) + * where the max limit is stored (limits are stored per-corner) */ +} BME_TransData; + +typedef struct BME_TransData_Head { + GHash *gh; /* the hash structure for element lookup */ + MemArena *ma; /* the memory "pool" we will be drawing individual elements from */ + int len; +} BME_TransData_Head; + +typedef struct BME_Glob { /* stored in Global G for Transform() purposes */ + BME_Mesh *bm; + BME_TransData_Head *td; + struct TransInfo *Trans; /* a pointer to the global Trans struct */ + int imval[2]; /* for restoring original mouse co when initTransform() is called multiple times */ + int options; + int res; +} BME_Glob; + +struct BME_TransData *BME_get_transdata(struct BME_TransData_Head *td, struct BME_Vert *v); +void BME_free_transdata(struct BME_TransData_Head *td); +float *BME_bevel_calc_polynormal(struct BME_Poly *f, struct BME_TransData_Head *td); +struct BME_Mesh *BME_bevel(struct BME_Mesh *bm, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd); + +/*CONVERSION FUNCTIONS*/ +struct BME_Mesh *BME_editmesh_to_bmesh(EditMesh *em, struct BME_Mesh *bm); +struct EditMesh *BME_bmesh_to_editmesh(struct BME_Mesh *bm, BME_TransData_Head *td); +struct BME_Mesh *BME_derivedmesh_to_bmesh(struct DerivedMesh *dm, struct BME_Mesh *bm); +struct DerivedMesh *BME_bmesh_to_derivedmesh(struct BME_Mesh *bm, struct DerivedMesh *dm); +#endif diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 8aaa9e42289..7019eb26bcb 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -62,6 +62,7 @@ struct Object; struct bSoundListener; struct BMF_Font; struct EditMesh; +struct BME_Glob; typedef struct Global { @@ -111,6 +112,9 @@ typedef struct Global { /* Editmode lists */ struct EditMesh *editMesh; + + /* Used for BMesh transformations */ + struct BME_Glob *editBMesh; float textcurs[4][2]; @@ -191,6 +195,7 @@ typedef struct Global { /* #define G_AUTOMATKEYS (1 << 30) also removed */ #define G_HIDDENHANDLES (1 << 31) /* used for curves only */ +#define G_DRAWBWEIGHTS (1 << 31) /* macro for testing face select mode * Texture paint could be removed since selected faces are not used diff --git a/source/blender/blenkernel/intern/BME_conversions.c b/source/blender/blenkernel/intern/BME_conversions.c new file mode 100644 index 00000000000..ee8c6523cfc --- /dev/null +++ b/source/blender/blenkernel/intern/BME_conversions.c @@ -0,0 +1,406 @@ +/** + * BME_mesh.c jan 2007 + * + * BMesh mesh level functions. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle, Levi Schooley. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_utildefines.h" +#include "BKE_mesh.h" +#include "BKE_bmesh.h" +#include "BKE_global.h" +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" + +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_edgehash.h" +#include "BIF_editmesh.h" +#include "editmesh.h" +#include "bmesh_private.h" + +#include "BSE_edit.h" + +BME_Mesh *BME_editmesh_to_bmesh(EditMesh *em, BME_Mesh *bm) { + BME_Vert *v1, *v2; + BME_Edge *e, *edar[4]; + BME_Poly *f; + + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + int len; + + BME_model_begin(bm); + /*custom data*/ + + /*add verts*/ + CustomData_copy(&em->vdata, &bm->vdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + eve= em->verts.first; + while(eve) { + v1 = BME_MV(bm,eve->co); + VECCOPY(v1->no,eve->no); + v1->flag = eve->f; + v1->h = eve->h; + v1->bweight = eve->bweight; + + /* link the verts for edge and face construction; + * kind of a dangerous thing - remember to cast back to BME_Vert before using! */ + eve->tmp.v = (EditVert*)v1; + + CustomData_em_copy_data(&em->vdata, &bm->vdata, eve->data, &v1->data); + + eve = eve->next; + } + + /*add edges*/ + CustomData_copy(&em->edata, &bm->edata, CD_MASK_EDITMESH, CD_CALLOC, 0); + eed= em->edges.first; + while(eed) { + v1 = (BME_Vert*)eed->v1->tmp.v; + v2 = (BME_Vert*)eed->v2->tmp.v; + e = BME_ME(bm, v1, v2); + e->crease = eed->crease; + e->bweight = eed->bweight; + e->flag = eed->f & SELECT; + if(eed->sharp) e->flag |= ME_SHARP; + if(eed->seam) e->flag |= ME_SEAM; + if(eed->h & EM_FGON) e->flag |= ME_FGON; + if(eed->h & 1) e->flag |= ME_HIDE; + CustomData_em_copy_data(&em->edata, &bm->edata, eed->data, &e->data); + + /* link the edges for face construction; + * kind of a dangerous thing - remember to cast back to BME_Edge before using! */ + eed->tmp.e = (EditEdge*)e; + eed = eed->next; + } + + /*add faces.*/ + CustomData_copy(&em->fdata, &bm->pdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + efa= em->faces.first; + while(efa) { + if(efa->v4) len = 4; + else len = 3; + + edar[0] = (BME_Edge*)efa->e1->tmp.e; + edar[1] = (BME_Edge*)efa->e2->tmp.e; + edar[2] = (BME_Edge*)efa->e3->tmp.e; + if(len == 4){ + edar[3] = (BME_Edge*)efa->e4->tmp.e; + } + + /*find v1 and v2*/ + v1 = (BME_Vert*)efa->v1->tmp.v; + v2 = (BME_Vert*)efa->v2->tmp.v; + + f = BME_MF(bm,v1,v2,edar,len); + f->mat_nr = efa->mat_nr; + f->flag = efa->flag; + if(efa->h) { + f->flag |= ME_HIDE; + f->flag &= ~ME_FACE_SEL; + } + else { + if(efa->f & 1) f->flag |= ME_FACE_SEL; + else f->flag &= ~ME_FACE_SEL; + } + CustomData_em_copy_data(&em->fdata, &bm->pdata, efa->data, &f->data); + + efa = efa->next; + } + BME_model_end(bm); + return bm; +} + +/* adds the geometry in the bmesh to G.editMesh (does not free G.editMesh) + * if td != NULL, the transdata will be mapped to the EditVert's co */ +EditMesh *BME_bmesh_to_editmesh(BME_Mesh *bm, BME_TransData_Head *td) { + BME_Vert *v1; + BME_Edge *e; + BME_Poly *f; + + BME_TransData *vtd; + + EditMesh *em; + EditVert *eve1, *eve2, *eve3, *eve4, **evlist; + EditEdge *eed; + EditFace *efa; + + int totvert, len, i; + + em = G.editMesh; + + if (em == NULL) return NULL; + + /* convert to EditMesh */ + /* make editverts */ + CustomData_copy(&bm->vdata, &em->vdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + totvert = BLI_countlist(&(bm->verts)); + evlist= (EditVert **)MEM_mallocN(totvert*sizeof(void *),"evlist"); + for (i=0,v1=bm->verts.first;v1;v1=v1->next,i++) { + v1->tflag1 = i; + eve1 = addvertlist(v1->co,NULL); + if (td && (vtd = BME_get_transdata(td,v1))) { + vtd->loc = eve1->co; + } + eve1->keyindex = i; + evlist[i]= eve1; + eve1->f = (unsigned char)v1->flag; + eve1->h = (unsigned char)v1->h; + eve1->bweight = v1->bweight; + CustomData_em_copy_data(&bm->vdata, &em->vdata, v1->data, &eve1->data); + } + + /* make edges */ + CustomData_copy(&bm->edata, &em->edata, CD_MASK_EDITMESH, CD_CALLOC, 0); + for (e=bm->edges.first;e;e=e->next) { + eed= addedgelist(evlist[e->v1->tflag1], evlist[e->v2->tflag1], NULL); + eed->crease = e->crease; + eed->bweight = e->bweight; + if(e->flag & ME_SEAM) eed->seam = 1; + if(e->flag & ME_SHARP) eed->sharp = 1; + if(e->flag & SELECT) eed->f |= SELECT; + if(e->flag & ME_FGON) eed->h= EM_FGON; // 2 different defines! + if(e->flag & ME_HIDE) eed->h |= 1; + if(G.scene->selectmode==SCE_SELECT_EDGE) + EM_select_edge(eed, eed->f & SELECT); + CustomData_em_copy_data(&bm->edata, &em->edata, e->data, &eed->data); + } + + /* make faces */ + CustomData_copy(&bm->pdata, &em->fdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + for (f=bm->polys.first;f;f=f->next) { + len = BME_cycle_length(f->loopbase); + if (len==3 || len==4) { + eve1= evlist[f->loopbase->v->tflag1]; + eve2= evlist[f->loopbase->next->v->tflag1]; + eve3= evlist[f->loopbase->next->next->v->tflag1]; + if (len == 4) { + eve4= evlist[f->loopbase->prev->v->tflag1]; + } + else { + eve4= NULL; + } + + efa = addfacelist(eve1, eve2, eve3, eve4, NULL, NULL); + CustomData_em_copy_data(&bm->pdata, &em->fdata, f->data, &efa->data); + efa->mat_nr = (unsigned char)f->mat_nr; + efa->flag= f->flag & ~ME_HIDE; + if(f->flag & ME_FACE_SEL) { + efa->f |= SELECT; + } + if(f->flag & ME_HIDE) efa->h= 1; + if((G.f & G_FACESELECT) && (efa->f & SELECT)) + EM_select_face(efa, 1); /* flush down */ + } + } + + MEM_freeN(evlist); + + countall(); + + return em; +} + +/* Adds the geometry found in dm to bm + * NOTE: it does not allocate a new BME_Mesh! + */ +BME_Mesh *BME_derivedmesh_to_bmesh(DerivedMesh *dm, BME_Mesh *bm) +{ + MVert *mvert, *mv; + MEdge *medge, *me; + MFace *mface, *mf; + int totface,totedge,totvert,i,len; + + BME_Vert *v1=NULL,*v2=NULL, **vert_array; + BME_Edge *e=NULL; + BME_Poly *f=NULL; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + totvert = dm->getNumVerts(dm); + totedge = dm->getNumEdges(dm); + totface = dm->getNumFaces(dm); + mvert = dm->getVertArray(dm); + medge = dm->getEdgeArray(dm); + mface = dm->getFaceArray(dm); + + vert_array = MEM_mallocN(sizeof(*vert_array)*totvert,"BME_derivedmesh_to_bmesh BME_Vert* array"); + + /*custom data*/ + /* NOTE: I haven't tested whether or not custom data is being copied correctly */ + CustomData_copy(&dm->vertData, &bm->vdata, CD_MASK_DERIVEDMESH, + CD_CALLOC, 0); + CustomData_copy(&dm->edgeData, &bm->edata, CD_MASK_DERIVEDMESH, + CD_CALLOC, 0); + CustomData_copy(&dm->faceData, &bm->pdata, CD_MASK_DERIVEDMESH, + CD_CALLOC, 0); + /*add verts*/ + for(i=0,mv = mvert; i < totvert;i++,mv++){ + v1 = BME_MV(bm,mv->co); + vert_array[i] = v1; + v1->flag = mv->flag; + v1->bweight = mv->bweight/255.0f; + CustomData_to_em_block(&dm->vertData, &bm->vdata, i, &v1->data); + } + /*add edges*/ + for(i=0,me = medge; i < totedge;i++,me++){ + v1 = vert_array[me->v1]; + v2 = vert_array[me->v2]; + e = BME_ME(bm, v1, v2); + e->crease = me->crease/255.0f; + e->bweight = me->bweight/255.0f; + e->flag = (unsigned char)me->flag; + BLI_edgehash_insert(edge_hash,me->v1,me->v2,e); + CustomData_to_em_block(&dm->edgeData, &bm->edata, i, &e->data); + } + /*add faces.*/ + for(i=0,mf = mface; i < totface;i++,mf++){ + BME_Edge *edar[4]; + if(mf->v4) len = 4; + else len = 3; + + edar[0] = BLI_edgehash_lookup(edge_hash,mf->v1,mf->v2); + edar[1] = BLI_edgehash_lookup(edge_hash,mf->v2,mf->v3); + if(len == 4){ + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v4); + edar[3] = BLI_edgehash_lookup(edge_hash,mf->v4,mf->v1); + } + else + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v1); + + /*find v1 and v2*/ + v1 = vert_array[mf->v1]; + v2 = vert_array[mf->v2]; + + f = BME_MF(bm,v1,v2,edar,len); + f->mat_nr = mf->mat_nr; + f->flag = mf->flag; + CustomData_to_em_block(&dm->faceData, &bm->pdata, i, &f->data); + } + + BLI_edgehash_free(edge_hash, NULL); + MEM_freeN(vert_array); + return bm; +} + +DerivedMesh *BME_bmesh_to_derivedmesh(BME_Mesh *bm, DerivedMesh *dm) +{ + MFace *mface, *mf; + MEdge *medge, *me; + MVert *mvert, *mv; + int totface,totedge,totvert,i,bmeshok,len; + + BME_Vert *v1=NULL; + BME_Edge *e=NULL; + BME_Poly *f=NULL; + + DerivedMesh *result; + + totvert = BLI_countlist(&(bm->verts)); + totedge = BLI_countlist(&(bm->edges)); + /*count quads and tris*/ + totface = 0; + bmeshok = 1; + for(f=bm->polys.first;f;f=f->next){ + len = BME_cycle_length(f->loopbase); + if(len == 3 || len == 4) totface++; + } + + /*convert back to mesh*/ + result = CDDM_from_template(dm,totvert,totedge,totface); + /*custom data*/ + /* NOTE: I haven't tested whether or not custom data is being copied correctly */ + CustomData_merge(&bm->vdata, &result->vertData, CD_MASK_DERIVEDMESH, + CD_CALLOC, totvert); + CustomData_merge(&bm->edata, &result->edgeData, CD_MASK_DERIVEDMESH, + CD_CALLOC, totedge); + CustomData_merge(&bm->pdata, &result->faceData, CD_MASK_DERIVEDMESH, + CD_CALLOC, totface); + /*Make Verts*/ + mvert = CDDM_get_verts(result); + for(i=0,v1=bm->verts.first,mv=mvert;v1;v1=v1->next,i++,mv++){ + v1->tflag1 = i; + VECCOPY(mv->co,v1->co); + mv->flag = (unsigned char)v1->flag; + mv->bweight = (char)(255.0*v1->bweight); + CustomData_from_em_block(&bm->vdata, &result->vertData, v1->data, i); + } + medge = CDDM_get_edges(result); + for(i=0,e=bm->edges.first,me=medge;e;e=e->next,me++,i++){ + if(e->v1->tflag1 < e->v2->tflag1){ + me->v1 = e->v1->tflag1; + me->v2 = e->v2->tflag1; + } + else{ + me->v1 = e->v2->tflag1; + me->v2 = e->v1->tflag1; + } + me->crease = (char)(255.0*e->crease); + me->bweight = (char)(255.0*e->bweight); + me->flag = e->flag; + CustomData_from_em_block(&bm->edata, &result->edgeData, e->data, i); + } + if(totface){ + mface = CDDM_get_faces(result); + /*make faces*/ + for(i=0,f=bm->polys.first;f;f=f->next,i++){ + mf = &mface[i]; + len = BME_cycle_length(f->loopbase); + if(len==3 || len==4){ + mf->v1 = f->loopbase->v->tflag1; + mf->v2 = f->loopbase->next->v->tflag1; + mf->v3 = f->loopbase->next->next->v->tflag1; + if(len == 4){ + mf->v4 = f->loopbase->prev->v->tflag1; + } + /* test and rotate indexes if necessary so that verts 3 and 4 aren't index 0 */ + if(mf->v3 == 0 || (len == 4 && mf->v4 == 0)){ + test_index_face(mf, NULL, i, len); + } + } + mf->mat_nr = (unsigned char)f->mat_nr; + mf->flag = (unsigned char)f->flag; + CustomData_from_em_block(&bm->pdata, &result->faceData, f->data, i); + } + } + + return result; +} \ No newline at end of file diff --git a/source/blender/blenkernel/intern/BME_eulers.c b/source/blender/blenkernel/intern/BME_eulers.c new file mode 100644 index 00000000000..34187e71beb --- /dev/null +++ b/source/blender/blenkernel/intern/BME_eulers.c @@ -0,0 +1,1007 @@ +/** + * BME_eulers.c jan 2007 + * + * BMesh Euler construction API. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" + +#include "BLI_blenlib.h" +#include "bmesh_private.h" +#include "BLI_ghash.h" + +/********************************************************* + * "Euler API" * + * * + * * + * Primitive construction operators for mesh tools. * + * * + **********************************************************/ + + +/* + The functions in this file represent the 'primitive' or 'atomic' operators that + mesh tools use to manipulate the topology of the structure.* The purpose of these + functions is to provide a trusted set of operators to manipulate the mesh topology + and which can also be combined together like building blocks to create more + sophisticated tools. It needs to be stressed that NO manipulation of an existing + mesh structure should be done outside of these functions. + + In the BMesh system, each euler is named by an ancronym which describes what it actually does. + Furthermore each Euler has a logical inverse. An important design criteria of all Eulers is that + through a Euler's logical inverse you can 'undo' an operation. (Special note should + be taken of BME_loop_reverse, which is its own inverse). + + BME_MF/KF: Make Face and Kill Face + BME_ME/KE: Make Edge and Kill Edge + BME_MV/KV: Make Vert and Kill Vert + BME_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert + BME_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge + BME_loop_reverse: Reverse a Polygon's loop cycle. (used for flip normals for one) + + Using a combination of these eleven eulers any non-manifold modelling operation can be achieved. + Each Euler operator has a detailed explanation of what is does in the comments preceding its + code. + + *The term "Euler Operator" is actually a misnomer when referring to a non-manifold + data structure. Its use is in keeping with the convention established by others. + + TODO: + -Finish inserting 'strict' validation in all Eulers +*/ + +void *BME_exit(char *s) { + if (s) printf("%s\n",s); + return NULL; +} + +#define RETCLEAR(bm) {bm->rval->v = bm->rval->e = bm->rval->f = bm->rva->l = NULL;} +/*MAKE Eulers*/ + +/** + * BME_MV + * + * MAKE VERT EULER: + * + * Makes a single loose vertex. + * + * Returns - + * A BME_Vert pointer. + */ + +BME_Vert *BME_MV(BME_Mesh *bm, float *vec){ + BME_Vert *v = BME_addvertlist(bm, NULL); + VECCOPY(v->co,vec); + return v; +} + +/** + * BME_ME + * + * MAKE EDGE EULER: + * + * Makes a single wire edge between two vertices. + * If the caller does not want there to be duplicate + * edges between the vertices, it is up to them to check + * for this condition beforehand. + * + * Returns - + * A BME_Edge pointer. + */ + +BME_Edge *BME_ME(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2){ + BME_Edge *e=NULL; + BME_CycleNode *d1=NULL, *d2=NULL; + int valance1=0, valance2=0, edok; + + /*edge must be between two distinct vertices...*/ + if(v1 == v2) return BME_exit("ME returned NULL"); + + #ifndef BME_FASTEULER + /*count valance of v1*/ + if(v1->edge){ + d1 = BME_disk_getpointer(v1->edge,v1); + if(d1) valance1 = BME_cycle_length(d1); + else BME_error(); + } + if(v2->edge){ + d2 = BME_disk_getpointer(v2->edge,v2); + if(d2) valance2 = BME_cycle_length(d2); + else BME_error(); + } + #endif + + /*go ahead and add*/ + e = BME_addedgelist(bm, v1, v2, NULL); + BME_disk_append_edge(e, e->v1); + BME_disk_append_edge(e, e->v2); + + #ifndef BME_FASTEULER + /*verify disk cycle lengths*/ + d1 = BME_disk_getpointer(e, e->v1); + edok = BME_cycle_validate(valance1+1, d1); + if(!edok) BME_error(); + d2 = BME_disk_getpointer(e, e->v2); + edok = BME_cycle_validate(valance2+1, d2); + if(!edok) BME_error(); + + /*verify that edge actually made it into the cycle*/ + edok = BME_disk_hasedge(v1, e); + if(!edok) BME_error(); + edok = BME_disk_hasedge(v2, e); + if(!edok) BME_error(); + #endif + return e; +} + + + +/** + * BME_MF + * + * MAKE FACE EULER: + * Takes a list of edge pointers which form a closed loop and makes a face + * from them. The first edge in elist is considered to be the start of the + * polygon, and v1 and v2 are its vertices and determine the winding of the face + * Other than the first edge, no other assumptions are made about the order of edges + * in the elist array. To verify that it is a single closed loop and derive the correct + * order a simple series of verifications is done and all elements are visited. + * + * Returns - + * A BME_Poly pointer + */ + +#define MF_CANDIDATE 1 +#define MF_VISITED 2 +#define MF_TAKEN 4 + +BME_Poly *BME_MF(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2, BME_Edge **elist, int len) +{ + BME_Poly *f = NULL; + BME_Edge *curedge; + BME_Vert *curvert, *tv, *nextv,**vlist; + int i, j, done, cont, edok,vlen; + + if(len < 2) return BME_exit("MF returned NULL"); + + /*make sure that v1 and v2 are in elist[0]*/ + if(BME_verts_in_edge(v1,v2,elist[0]) == 0) return BME_exit("MF returned NULL"); + + /*clear euler flags*/ + for(i=0;ieflag1=elist[i]->eflag2 = 0; + for(i=0;ieflag1 |= MF_CANDIDATE; + + /*if elist[i] has a loop, count its radial length*/ + if(elist[i]->loop) elist[i]->eflag2 = BME_cycle_length(&(elist[i]->loop->radial)); + else elist[i]->eflag2 = 0; + } + + /* For each vertex in each edge, it must have exactly two MF_CANDIDATE edges attached to it + Note that this does not gauruntee that face is a single closed loop. At best it gauruntees + that elist contains a finite number of seperate closed loops. + */ + for(i=0; iv1, MF_CANDIDATE, 0); + if(edok != 2) return BME_exit("MF returned NULL"); + edok = BME_disk_count_edgeflag(elist[i]->v2, MF_CANDIDATE, 0); + if(edok != 2) return BME_exit("MF returned NULL"); + } + + /*set start edge, start vert and target vert for our loop traversal*/ + curedge = elist[0]; + tv = v1; + curvert = v2; + + /*insert tv into vlist since its the first vertex in face*/ + i=0; + vlist=MEM_callocN(sizeof(BME_Vert*)*len,"BME_MF vlist array"); + vlist[i] = tv; + + /* Basic procedure: Starting with curv we find the edge in it's disk cycle which hasn't + been visited yet. When we do, we put curv in a linked list and find the next MF_CANDIDATE + edge, loop until we find TV. We know TV is reachable because of test we did earlier. + */ + done=0; + while(!done){ + /*add curvert to vlist*/ + /*insert some error cheking here for overflows*/ + i++; + vlist[i] = curvert; + + /*mark curedge as visited*/ + curedge->eflag1 |= MF_VISITED; + + /*find next edge and vert*/ + curedge = BME_disk_next_edgeflag(curedge, curvert, MF_CANDIDATE, 0); + curvert = BME_edge_getothervert(curedge, curvert); + if(curvert == tv){ + curedge->eflag1 |= MF_VISITED; + done=1; + } + } + + /* Verify that all edges have been visited It's possible that we did reach tv + from sv, but that several unconnected loops were passed in via elist. + */ + cont=1; + for(i=0; ieflag1 & MF_VISITED) == 0) cont = 0; + } + + /*if we get this far, its ok to allocate the face and add the loops*/ + if(cont){ + BME_Loop *l; + BME_Edge *e; + f = BME_addpolylist(bm, NULL); + f->len = len; + for(i=0;iloopbase)) f->loopbase = l; + BME_cycle_append(f->loopbase, l); + } + + /*take care of edge pointers and radial cycle*/ + for(i=0, l = f->loopbase; inext){ + e = NULL; + if(l == f->loopbase) e = elist[0]; /*first edge*/ + + else{/*search elist for others*/ + for(j=1; jv, l->next->v, elist[j]); + if(edok){ + e = elist[j]; + break; + } + } + } + l->e = e; /*set pointer*/ + BME_radial_append(e, l); /*append into radial*/ + } + + f->len = len; + + /*Validation Loop cycle*/ + edok = BME_cycle_validate(len, f->loopbase); + if(!edok) BME_error(); + for(i=0, l = f->loopbase; inext){ + /*validate loop vert pointers*/ + edok = BME_verts_in_edge(l->v, l->next->v, l->e); + if(!edok) BME_error(); + /*validate the radial cycle of each edge*/ + edok = BME_cycle_length(&(l->radial)); + if(edok != (l->e->eflag2 + 1)) BME_error(); + } + } + + MEM_freeN(vlist); + return f; +} + +/* KILL Eulers */ + +/** + * BME_KV + * + * KILL VERT EULER: + * + * Kills a single loose vertex. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_KV(BME_Mesh *bm, BME_Vert *v){ + if(v->edge == NULL){ + BLI_remlink(&(bm->verts), v); + BME_free_vert(bm,v); + return 1; + } + return 0; +} + +/** + * BME_KE + * + * KILL EDGE EULER: + * + * Kills a wire edge. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_KE(BME_Mesh *bm, BME_Edge *e){ + int edok; + + /*Make sure that no faces!*/ + if(e->loop == NULL){ + BME_disk_remove_edge(e, e->v1); + BME_disk_remove_edge(e, e->v2); + + /*verify that edge out of disk*/ + edok = BME_disk_hasedge(e->v1, e); + if(edok) BME_error(); + edok = BME_disk_hasedge(e->v2, e); + if(edok) BME_error(); + + /*remove and deallocate*/ + BLI_remlink(&(bm->edges), e); + BME_free_edge(bm, e); + return 1; + } + return 0; +} + +/** + * BME_KF + * + * KILL FACE EULER: + * + * The logical inverse of BME_MF. + * Kills a face and removes each of its loops from the radial that it belongs to. + * + * Returns - + * 1 for success, 0 for failure. +*/ + +int BME_KF(BME_Mesh *bm, BME_Poly *bply){ + BME_Loop *newbase,*oldbase, *curloop; + int i,len=0; + + /*add validation to make sure that radial cycle is cleaned up ok*/ + /*deal with radial cycle first*/ + len = BME_cycle_length(bply->loopbase); + for(i=0, curloop=bply->loopbase; i < len; i++, curloop = curloop->next) + BME_radial_remove_loop(curloop, curloop->e); + + /*now deallocate the editloops*/ + for(i=0; i < len; i++){ + newbase = bply->loopbase->next; + oldbase = bply->loopbase; + BME_cycle_remove(oldbase, oldbase); + BME_free_loop(bm, oldbase); + bply->loopbase = newbase; + } + + BLI_remlink(&(bm->polys), bply); + BME_free_poly(bm, bply); + return 1; +} + +/*SPLIT Eulers*/ + +/** + * BME_SEMV + * + * SPLIT EDGE MAKE VERT: + * Takes a given edge and splits it into two, creating a new vert. + * + * + * Before: OV---------TV + * After: OV----NV---TV + * + * Returns - + * BME_Vert pointer. + * +*/ + +BME_Vert *BME_SEMV(BME_Mesh *bm, BME_Vert *tv, BME_Edge *e, BME_Edge **re){ + BME_Vert *nv, *ov; + BME_CycleNode *diskbase; + BME_Edge *ne; + int i, radlen, edok, valance1=0, valance2=0; + + if(BME_vert_in_edge(e,tv) == 0) return BME_exit("SEMV returned NULL"); + ov = BME_edge_getothervert(e,tv); + //v2 = tv; + + /*count valance of v1*/ + diskbase = BME_disk_getpointer(e, ov); + valance1 = BME_cycle_length(diskbase); + /*count valance of v2*/ + diskbase = BME_disk_getpointer(e, tv); + valance2 = BME_cycle_length(diskbase); + + nv = BME_addvertlist(bm, tv); + ne = BME_addedgelist(bm, nv, tv, e); + + //e->v2 = nv; + /*remove e from v2's disk cycle*/ + BME_disk_remove_edge(e, tv); + /*swap out tv for nv in e*/ + BME_edge_swapverts(e, tv, nv); + /*add e to nv's disk cycle*/ + BME_disk_append_edge(e, nv); + /*add ne to nv's disk cycle*/ + BME_disk_append_edge(ne, nv); + /*add ne to tv's disk cycle*/ + BME_disk_append_edge(ne, tv); + /*verify disk cycles*/ + diskbase = BME_disk_getpointer(ov->edge,ov); + edok = BME_cycle_validate(valance1, diskbase); + if(!edok) BME_error(); + diskbase = BME_disk_getpointer(tv->edge,tv); + edok = BME_cycle_validate(valance2, diskbase); + if(!edok) BME_error(); + diskbase = BME_disk_getpointer(nv->edge,nv); + edok = BME_cycle_validate(2, diskbase); + if(!edok) BME_error(); + + /*Split the radial cycle if present*/ + if(e->loop){ + BME_Loop *nl,*l; + BME_CycleNode *radEBase=NULL, *radNEBase=NULL; + int radlen = BME_cycle_length(&(e->loop->radial)); + /*Take the next loop. Remove it from radial. Split it. Append to appropriate radials.*/ + while(e->loop){ + l=e->loop; + l->f->len++; + BME_radial_remove_loop(l,e); + + nl = BME_create_loop(bm,NULL,NULL,l->f,l); + nl->prev = l; + nl->next = l->next; + nl->prev->next = nl; + nl->next->prev = nl; + nl->v = nv; + + /*assign the correct edge to the correct loop*/ + if(BME_verts_in_edge(nl->v, nl->next->v, e)){ + nl->e = e; + l->e = ne; + + /*append l into ne's rad cycle*/ + if(!radNEBase){ + radNEBase = &(l->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + + if(!radEBase){ + radEBase = &(nl->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + + BME_cycle_append(radEBase,&(nl->radial)); + BME_cycle_append(radNEBase,&(l->radial)); + + } + else if(BME_verts_in_edge(nl->v,nl->next->v,ne)){ + nl->e = ne; + l->e = e; + + if(!radNEBase){ + radNEBase = &(nl->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + if(!radEBase){ + radEBase = &(l->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + BME_cycle_append(radEBase,&(l->radial)); + BME_cycle_append(radNEBase,&(nl->radial)); + } + + } + + e->loop = radEBase->data; + ne->loop = radNEBase->data; + + /*verify length of radial cycle*/ + edok = BME_cycle_validate(radlen,&(e->loop->radial)); + if(!edok) BME_error(); + edok = BME_cycle_validate(radlen,&(ne->loop->radial)); + if(!edok) BME_error(); + + /*verify loop->v and loop->next->v pointers for e*/ + for(i=0,l=e->loop; i < radlen; i++, l = l->radial.next->data){ + if(!(l->e == e)) BME_error(); + if(!(l->radial.data == l)) BME_error(); + if(l->prev->e != ne && l->next->e != ne) BME_error(); + edok = BME_verts_in_edge(l->v, l->next->v, e); + if(!edok) BME_error(); + if(l->v == l->next->v) BME_error(); + if(l->e == l->next->e) BME_error(); + /*verify loop cycle for kloop->f*/ + edok = BME_cycle_validate(l->f->len, l->f->loopbase); + if(!edok) BME_error(); + } + /*verify loop->v and loop->next->v pointers for ne*/ + for(i=0,l=ne->loop; i < radlen; i++, l = l->radial.next->data){ + if(!(l->e == ne)) BME_error(); + if(!(l->radial.data == l)) BME_error(); + if(l->prev->e != e && l->next->e != e) BME_error(); + edok = BME_verts_in_edge(l->v, l->next->v, ne); + if(!edok) BME_error(); + if(l->v == l->next->v) BME_error(); + if(l->e == l->next->e) BME_error(); + /*verify loop cycle for kloop->f. Redundant*/ + edok = BME_cycle_validate(l->f->len, l->f->loopbase); + if(!edok) BME_error(); + } + } + + if(re) *re = ne; + return nv; +} + +/** + * BME_SFME + * + * SPLIT FACE MAKE EDGE: + * + * Takes as input two vertices in a single face. An edge is created which divides the original face + * into two distinct regions. One of the regions is assigned to the original face and it is closed off. + * The second region has a new face assigned to it. + * + * Examples: + * + * Before: After: + * ---------- ---------- + * | | | | + * | | | f1 | + * v1 f1 v2 v1======v2 + * | | | f2 | + * | | | | + * ---------- ---------- + * + * Note that the input vertices can be part of the same edge. This will result in a two edged face. + * This is desirable for advanced construction tools and particularly essential for edge bevel. Because + * of this it is up to the caller to decide what to do with the extra edge. + * + * Returns - + * A BME_Poly pointer + */ +BME_Poly *BME_SFME(BME_Mesh *bm, BME_Poly *f, BME_Vert *v1, BME_Vert *v2, BME_Loop **rl){ + + BME_Poly *f2; + BME_Loop *v1loop = NULL, *v2loop = NULL, *curloop, *f1loop=NULL, *f2loop=NULL; + BME_Edge *e; + int i, len, f1len, f2len; + + if(f->holes.first) return BME_exit("SFME returned NULL"); //not good, fix me + + /*verify that v1 and v2 are in face.*/ + len = BME_cycle_length(f->loopbase); + for(i = 0, curloop = f->loopbase; i < len; i++, curloop = curloop->next){ + if(curloop->v == v1) v1loop = curloop; + else if(curloop->v == v2) v2loop = curloop; + } + + if(!v1loop || !v2loop) return BME_exit("SFME returned NULL"); + + /*allocate new edge between v1 and v2*/ + e = BME_addedgelist(bm, v1, v2,NULL); + BME_disk_append_edge(e, v1); + BME_disk_append_edge(e, v2); + + f2 = BME_addpolylist(bm,f); + f1loop = BME_create_loop(bm,v2,e,f,NULL); + f2loop = BME_create_loop(bm,v1,e,f2,NULL); + + f1loop->prev = v2loop->prev; + f2loop->prev = v1loop->prev; + v2loop->prev->next = f1loop; + v1loop->prev->next = f2loop; + + f1loop->next = v1loop; + f2loop->next = v2loop; + v1loop->prev = f1loop; + v2loop->prev = f2loop; + + f2->loopbase = f2loop; + f->loopbase = f1loop; + + /*validate both loops*/ + /*I dont know how many loops are supposed to be in each face at this point! FIXME!*/ + + /*go through all of f2's loops and make sure they point to it properly.*/ + f2len = BME_cycle_length(f2->loopbase); + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = curloop->next) curloop->f = f2; + + /*link up the new loops into the new edges radial*/ + BME_radial_append(e, f1loop); + BME_radial_append(e, f2loop); + + + f2->len = f2len; + + f1len = BME_cycle_length(f->loopbase); + f->len = f1len; + + if(rl) *rl = f2loop; + return f2; +} + + +/** + * BME_JEKV + * + * JOIN EDGE KILL VERT: + * Takes a an edge and pointer to one of its vertices and collapses + * the edge on that vertex. + * + * Before: OE KE + * ------- ------- + * | || | + * OV KV TV + * + * + * After: OE + * --------------- + * | | + * OV TV + * + * + * Restrictions: + * KV is a vertex that must have a valance of exactly two. Furthermore + * both edges in KV's disk cycle (OE and KE) must be unique (no double + * edges). + * + * It should also be noted that this euler has the possibility of creating + * faces with just 2 edges. It is up to the caller to decide what to do with + * these faces. + * + * Returns - + * 1 for success, 0 for failure. + */ +int BME_JEKV(BME_Mesh *bm, BME_Edge *ke, BME_Vert *kv) +{ + BME_Edge *oe; + BME_Vert *ov, *tv; + BME_CycleNode *diskbase; + BME_Loop *killoop,*nextl; + int len,radlen=0, halt = 0, i, valance1, valance2,edok; + + if(BME_vert_in_edge(ke,kv) == 0) return 0; + diskbase = BME_disk_getpointer(kv->edge, kv); + len = BME_cycle_length(diskbase); + + if(len == 2){ + oe = BME_disk_nextedge(ke, kv); + tv = BME_edge_getothervert(ke, kv); + ov = BME_edge_getothervert(oe, kv); + halt = BME_verts_in_edge(kv, tv, oe); //check for double edges + + if(halt) return 0; + else{ + + /*For verification later, count valance of ov and tv*/ + diskbase = BME_disk_getpointer(ov->edge, ov); + valance1 = BME_cycle_length(diskbase); + diskbase = BME_disk_getpointer(tv->edge, tv); + valance2 = BME_cycle_length(diskbase); + + /*remove oe from kv's disk cycle*/ + BME_disk_remove_edge(oe,kv); + /*relink oe->kv to be oe->tv*/ + BME_edge_swapverts(oe, kv, tv); + /*append oe to tv's disk cycle*/ + BME_disk_append_edge(oe, tv); + /*remove ke from tv's disk cycle*/ + BME_disk_remove_edge(ke, tv); + + /*deal with radial cycle of ke*/ + if(ke->loop){ + /*first step, fix the neighboring loops of all loops in ke's radial cycle*/ + radlen = BME_cycle_length(&(ke->loop->radial)); + for(i=0,killoop = ke->loop; inext->prev = killoop->prev; + killoop->prev->next = killoop->next; + if(killoop->next->v == kv) killoop->next->v = tv; + + /*fix len attribute of face*/ + killoop->f->len--; + if(killoop->f->loopbase == killoop) killoop->f->loopbase = killoop->next; + } + /*second step, remove all the hanging loops attached to ke*/ + killoop = ke->loop; + radlen = BME_cycle_length(&(ke->loop->radial)); + i=0; + while(iradial.next->data; + BME_free_loop(bm, killoop); + killoop = nextl; + i++; + } + /*Validate radial cycle of oe*/ + edok = BME_cycle_validate(radlen,&(oe->loop->radial)); + + } + + /*Validate disk cycles*/ + diskbase = BME_disk_getpointer(ov->edge,ov); + edok = BME_cycle_validate(valance1, diskbase); + if(!edok) BME_error(); + diskbase = BME_disk_getpointer(tv->edge,tv); + edok = BME_cycle_validate(valance2, diskbase); + if(!edok) BME_error(); + + /*Validate loop cycle of all faces attached to oe*/ + for(i=0,nextl = oe->loop; if->len,nextl->f->loopbase); + if(!edok) BME_error(); + } + /*deallocate edge*/ + BLI_remlink(&(bm->edges), ke); + BME_free_edge(bm, ke); + /*deallocate vertex*/ + BLI_remlink(&(bm->verts), kv); + BME_free_vert(bm, kv); + return 1; + } + } + return 0; +} + + +/** + * BME_loop_reverse + * + * FLIP FACE EULER + * + * Changes the winding order of a face from CW to CCW or vice versa. + * This euler is a bit peculiar in compairson to others as it is its + * own inverse. + * + * TODO: reinsert validation code. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_loop_reverse(BME_Mesh *bm, BME_Poly *f){ + BME_Loop *l = f->loopbase, *curloop, *oldprev, *oldnext; + BME_Edge **elist; + int i, j, edok, len = 0; + + len = BME_cycle_length(l); + elist = MEM_callocN(sizeof(BME_Edge *)*len, "BME Loop Reverse edge array"); + + for(i=0, curloop = l; i< len; i++, curloop=curloop->next){ + BME_radial_remove_loop(curloop, curloop->e); + curloop->e->eflag1 = 0; + elist[i] = curloop->e; + } + + /*actually reverse the loop. This belongs in BME_cycle_reverse!*/ + for(i=0, curloop = l; i < len; i++){ + oldnext = curloop->next; + oldprev = curloop->prev; + curloop->next = oldprev; + curloop->prev = oldnext; + curloop = oldnext; + } + + if(len == 2){ //two edged face + //do some verification here! + l->e = elist[1]; + l->next->e = elist[0]; + } + else{ + for(i=0, curloop = l; i < len; i++, curloop = curloop->next){ + edok = 0; + for(j=0; j < len; j++){ + edok = BME_verts_in_edge(curloop->v, curloop->next->v, elist[j]); + if(edok){ + curloop->e = elist[j]; + break; + } + } + } + } + /*rebuild radial*/ + for(i=0, curloop = l; i < len; i++, curloop = curloop->next){ + BME_radial_append(curloop->e, curloop); + //radok = BME_cycle_validate(curloop->e->tmp.l, &(curloop->radial)); + //if(!radok || curloop->e->loop == NULL) BME_error(); + } + MEM_freeN(elist); + return 1; +} + +/** + * BME_JFKE + * + * JOIN FACE KILL EDGE: + * + * Takes two faces joined by a single 2-manifold edge and fuses them togather. + * The edge shared by the faces must not be connected to any other edges which have + * Both faces in its radial cycle + * + * Examples: + * + * A B + * ---------- ---------- + * | | | | + * | f1 | | f1 | + * v1========v2 = Ok! v1==V2==v3 == Wrong! + * | f2 | | f2 | + * | | | | + * ---------- ---------- + * + * In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used. + * In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller + * in this case should call BME_JEKV on the extra edges before attempting to fuse f1 and f2. + * + * Also note that the order of arguments decides whether or not certain per-face attributes are present + * in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited + * from f1, not f2. + * + * Returns - + * A BME_Poly pointer +*/ + +BME_Poly *BME_JFKE(BME_Mesh *bm, BME_Poly *f1, BME_Poly *f2, BME_Edge *e) +{ + + BME_Loop *curloop, *f1loop=NULL, *f2loop=NULL; + int loopok = 0, newlen = 0,i, f1len=0, f2len=0, radlen=0, valance1,valance2,edok; + + if(f1->holes.first || f2->holes.first) return BME_exit("JFKE returned NULL"); //dont operate on faces with holes. Not best solution but tolerable. + if(f1 == f2) return BME_exit("JFKE returned NULL"); //can't join a face to itself + /*verify that e is in both f1 and f2*/ + f1len = BME_cycle_length(f1->loopbase); + f2len = BME_cycle_length(f2->loopbase); + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = curloop->next){ + if(curloop->e == e){ + f1loop = curloop; + break; + } + } + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = curloop->next){ + if(curloop->e==e){ + f2loop = curloop; + break; + } + } + if(!(f1loop && f2loop)) return BME_exit("JFKE returned NULL"); + + /*validate that edge is 2-manifold edge*/ + radlen = BME_cycle_length(&(f1loop->radial)); + if(radlen != 2) return BME_exit("JFKE returned NULL"); + + /*validate direction of f2's loop cycle is compatible.*/ + if(f1loop->v == f2loop->v) return BME_exit("JFKE returned NULL"); + + /* + Finally validate that for each face, each vertex has another edge in its disk cycle that is + not e, and not shared. + */ + if(BME_radial_find_face(f1loop->next->e,f2)) return BME_exit("JFKE returned NULL"); + if(BME_radial_find_face(f1loop->prev->e,f2)) return BME_exit("JFKE returned NULL"); + if(BME_radial_find_face(f2loop->next->e,f1)) return BME_exit("JFKE returned NULL"); + if(BME_radial_find_face(f2loop->prev->e,f1)) return BME_exit("JFKE returned NULL"); + + /*join the two loops*/ + f1loop->prev->next = f2loop->next; + f2loop->next->prev = f1loop->prev; + + f1loop->next->prev = f2loop->prev; + f2loop->prev->next = f1loop->next; + + /*if f1loop was baseloop, give f1loop->next the base.*/ + if(f1->loopbase == f1loop) f1->loopbase = f1loop->next; + + /*validate the new loop*/ + loopok = BME_cycle_validate((f1len+f2len)-2, f1->loopbase); + if(!loopok) BME_error(); + + /*make sure each loop points to the proper face*/ + newlen = BME_cycle_length(f1->loopbase); + for(i = 0, curloop = f1->loopbase; i < newlen; i++, curloop = curloop->next) curloop->f = f1; + + f1->len = newlen; + + edok = BME_cycle_validate(f1->len, f1->loopbase); + if(!edok) BME_error(); + + /*remove edge from the disk cycle of its two vertices.*/ + BME_disk_remove_edge(f1loop->e, f1loop->e->v1); + BME_disk_remove_edge(f1loop->e, f1loop->e->v2); + + /*deallocate edge and its two loops as well as f2*/ + BLI_remlink(&(bm->edges), f1loop->e); + BLI_remlink(&(bm->polys), f2); + BME_free_edge(bm, f1loop->e); + BME_free_loop(bm, f1loop); + BME_free_loop(bm, f2loop); + BME_free_poly(bm, f2); + return f1; +} + +/** + * BME_MEKL + * + * MAKE EDGE KILL LOOP: + * + * Bridges a perphiary loop of a face with an internal loop + * + * Examples: + * + * ---------------- ---------------- + * | f1 | | f1 | + * | ----- | | ----- | + * | | | | | | | | + * X X | | X-----X | | + * | | | | | | | | + * | ----- | | ----- | + * | | | | + * ---------------- ---------------- + * + * + * Returns - + * A BME_Poly pointer + */ + +/** + * BME_KEML + * + * KILL EDGE MAKE LOOP: + * + * Kills an edge and splits the loose loops off into an internal loop + * + * Examples: + * + * ---------------- ---------------- + * | f1 | | f1 | + * | ----- | | ----- | + * | | | | | | | | + * X ----X | | X X | | + * | | | | | | | | + * | ----- | | ----- | + * | | | | + * ---------------- ---------------- + * + * The tool author should take care to realize that although a face may have + * a hole in its topology, that hole may be filled with one or many other faces. + * Regardless, this does not imply a parent child relationship. + * + * + * Returns - + * A BME_Poly pointer + */ + diff --git a/source/blender/blenkernel/intern/BME_mesh.c b/source/blender/blenkernel/intern/BME_mesh.c new file mode 100644 index 00000000000..26aa3e8439a --- /dev/null +++ b/source/blender/blenkernel/intern/BME_mesh.c @@ -0,0 +1,334 @@ +/** + * BME_mesh.c jan 2007 + * + * BMesh mesh level functions. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + + +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" +#include "BKE_global.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" + +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BIF_editmesh.h" +#include "BIF_space.h" +#include "editmesh.h" +#include "bmesh_private.h" +#include "mydevice.h" + +#include "BSE_edit.h" + + +/* + * BME MAKE MESH + * + * Allocates a new BME_Mesh structure +*/ + +BME_Mesh *BME_make_mesh(void){ + BME_Mesh *bm = MEM_callocN(sizeof(BME_Mesh),"BMesh"); + return bm; +} + +/* + * BME FREE MESH + * + * Frees a BME_Mesh structure. +*/ + +void BME_free_mesh(BME_Mesh *bm) +{ + BME_Poly *bf, *nextf; + BME_Edge *be, *nexte; + BME_Vert *bv, *nextv; + BME_CycleNode *loopref; + int loopcount=0; + + /*destroy polygon data*/ + bf = bm->polys.first; + while(bf){ + nextf = bf->next; + BLI_remlink(&(bm->polys), bf); + if(bf->holes.first) + BLI_freelistN(&(bf->holes)); + BME_free_poly(bm, bf); + + bf = nextf; + } + /*destroy edge data*/ + be = bm->edges.first; + while(be){ + nexte = be->next; + BLI_remlink(&(bm->edges), be); + BME_free_edge(bm, be); + be = nexte; + } + /*destroy vert data*/ + bv = bm->verts.first; + while(bv){ + nextv = bv->next; + BLI_remlink(&(bm->verts), bv); + BME_free_vert(bm, bv); + bv = nextv; + } + + if (bm->derivedFinal) { + bm->derivedFinal->needsFree = 1; + bm->derivedFinal->release(bm->derivedFinal); + } + + if (bm->derivedCage && bm->derivedCage != bm->derivedFinal) { + bm->derivedCage->needsFree = 1; + bm->derivedCage->release(bm->derivedCage); + } + + for(loopref=bm->loops.first;loopref;loopref=loopref->next) BME_delete_loop(bm,loopref->data); + BLI_freelistN(&(bm->loops)); + + CustomData_free(&bm->vdata, 0); + CustomData_free(&bm->edata, 0); + CustomData_free(&bm->ldata, 0); + CustomData_free(&bm->pdata, 0); + + MEM_freeN(bm); +} + +/* + * BME COPY MESH + * + * Copies a BME_Mesh structure. + * + * This is probably more low level than any mesh manipulation routine should be + * and somewhat violates the rule about modifying/creating mesh structures outside + * of the euler API. Regardless, its much more effecient than rebuilding the mesh + * from scratch. +*/ + +BME_Mesh *BME_copy_mesh(BME_Mesh *bm){ + + BME_Vert *v, *cv; + BME_Edge *e, *ce; + BME_Poly *f, *cf; + BME_Loop *l, *cl; + BME_Mesh *meshcopy; + meshcopy = BME_make_mesh(); + + + return meshcopy; +} + +/* + * BME MODEL BEGIN AND END + * + * These two functions represent the 'point of entry' for tools. Every BMesh tool + * must begin with a call to BME_model_end() and finish with a call to BME_model_end(). + * No modification of mesh data is allowed except in between these two calls. + * + * TODO + * FOR BME_MODEL_BEGIN: + * -integrate euler undo system. + * -make full copy of structure to safely recover from errors. + * -accept a toolname string. + * -accept param to turn off full copy if just selection tool. (perhaps check for this in eulers...) + * + * BME_MODEL_END: + * -full mesh validation if debugging turned on + * -free structure copy or use it to restore. + * -do euler undo push. + * +*/ + +int BME_model_begin(BME_Mesh *bm){ + if(bm->lock) return 0; + bm->lock = 1; + bm->backup = BME_copy_mesh(bm); + return 1; +} + +void BME_model_end(BME_Mesh *bm){ + BME_Mesh *badmesh; + int meshok,backupok, totvert, totedge, totpoly, totloop; + + totvert = BLI_countlist(&(bm->verts)); + totedge = BLI_countlist(&(bm->edges)); + totpoly = BLI_countlist(&(bm->polys)); + totloop = BLI_countlist(&(bm->loops)); + + if(bm->totvert!=totvert || bm->totedge!=totedge || bm->totpoly!=totpoly || bm->totloop!=totloop) + BME_error(); + + meshok = BME_validate_mesh(bm, 1); + if(!meshok){ + printf("Warning, Mesh failed validation, restoring from backup"); + badmesh = bm; + bm= badmesh->backup; + bm->backup = badmesh; + backupok = BME_validate_mesh(bm,1); + if(!backupok) printf("Backup corrupted too, Briggs did something stupid!"); + } + BME_free_mesh(bm->backup); + bm->lock = 0; +} + + +/* + * BME VALIDATE MESH + * + * There are several levels of validation for meshes. At the + * Euler level, some basic validation is done to local topology. + * To catch more subtle problems however, BME_validate_mesh() is + * called by BME_model_end() whenever a tool is done executing. + * The purpose of this function is to insure that during the course + * of tool execution that nothing has been done to invalidate the + * structure, and if it has, provide a way of reporting that so that + * we can restore the proper structure from a backup. Since a full mesh + * validation would be too expensive, this is presented as a compromise. + * + * TODO + * + * -Add validation for hole loops (which are experimental anyway) + * -Write a full mesh validation function for debugging purposes. + */ + +#define VHALT(halt) {BME_error(); if(halt) return 0;} + +int BME_validate_mesh(struct BME_Mesh *bm, int halt) +{ + BME_Vert *v; + BME_Edge *e; + BME_Poly *f; + BME_Loop *l; + BME_CycleNode *diskbase; + int i, ok; + + /*Simple edge verification*/ + for(e=bm->edges.first; e; e=e->next){ + if(e->v1 == e->v2) VHALT(halt); + /*validate e->d1.data and e->d2.data*/ + if(e->d1.data != e || e->d2.data != e) VHALT(halt); + /*validate e->loop->e*/ + if(e->loop){ + if(e->loop->e != e) VHALT(halt); + } + } + + /*calculate disk cycle lengths*/ + for(v=bm->verts.first; v; v=v->next) v->tflag1 = v->tflag2 = 0; + for(e=bm->edges.first; e; e=e->next){ + e->v1->tflag1++; + e->v2->tflag1++; + } + /*Validate vertices and disk cycle*/ + for(v=bm->verts.first; v; v=v->next){ + /*validate v->edge pointer*/ + if(v->tflag1){ + if(v->edge){ + ok = BME_vert_in_edge(v->edge,v); + if(!ok) VHALT(halt); + /*validate length of disk cycle*/ + diskbase = BME_disk_getpointer(v->edge, v); + ok = BME_cycle_validate(v->tflag1, diskbase); + if(!ok) VHALT(halt); + /*validate that each edge in disk cycle contains V*/ + for(i=0, e=v->edge; i < v->tflag1; i++, e = BME_disk_nextedge(e,v)){ + ok = BME_vert_in_edge(e, v); + if(!ok) VHALT(halt); + } + } + else VHALT(halt); + } + } + /*validate edges*/ + for(e=bm->edges.first; e; e=e->next){ + /*seperate these into BME_disk_hasedge (takes pointer to edge)*/ + /*search v1 disk cycle for edge*/ + ok = BME_disk_hasedge(e->v1,e); + if(!ok) VHALT(halt); + /*search v2 disk cycle for edge*/ + ok = BME_disk_hasedge(e->v2,e); + if(!ok) VHALT(halt); + } + + for(e=bm->edges.first; e; e=e->next) e->tflag2 = 0; //store incident faces + /*Validate the loop cycle integrity.*/ + for(f=bm->polys.first; f; f=f->next){ + ok = BME_cycle_length(f->loopbase); + if(ok > 1){ + f->tflag1 = ok; + } + else VHALT(halt); + for(i=0, l=f->loopbase; i < f->tflag1; i++, l=l->next){ + /*verify loop->v pointers*/ + ok = BME_verts_in_edge(l->v, l->next->v, l->e); + if(!ok) VHALT(halt); + /*verify radial node data pointer*/ + if(l->radial.data != l) VHALT(halt); + /*validate l->e->loop poitner*/ + if(l->e->loop == NULL) VHALT(halt); + /*validate l->f pointer*/ + if(l->f != f) VHALT(halt); + /*see if l->e->loop is actually in radial cycle*/ + + l->e->tflag2++; + } + } + + /*validate length of radial cycle*/ + for(e=bm->edges.first; e; e=e->next){ + if(e->loop){ + ok = BME_cycle_validate(e->tflag2,&(e->loop->radial)); + if(!ok) VHALT(halt); + } + } + + /*if we get this far, pretty safe to return 1*/ + return 1; +} + +/* Currently just a convient place for a breakpoint. + Probably should take an error string +*/ +void BME_error(void){ + printf("BME modelling error!"); +} diff --git a/source/blender/blenkernel/intern/BME_structure.c b/source/blender/blenkernel/intern/BME_structure.c new file mode 100644 index 00000000000..17f91c1d078 --- /dev/null +++ b/source/blender/blenkernel/intern/BME_structure.c @@ -0,0 +1,618 @@ +/** + * BME_structure.c jan 2007 + * + * Low level routines for manipulating the BMesh structure. + * + * $Id: BME_structure.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" +#include "BLI_blenlib.h" +#include "BLI_linklist.h" +#include "BLI_ghash.h" + +#include "BKE_customdata.h" +/** + * MISC utility functions. + * + */ + +int BME_vert_in_edge(BME_Edge *e, BME_Vert *v){ + if(e->v1 == v || e->v2 == v) return 1; + return 0; +} +int BME_verts_in_edge(BME_Vert *v1, BME_Vert *v2, BME_Edge *e){ + if(e->v1 == v1 && e->v2 == v2) return 1; + else if(e->v1 == v2 && e->v2 == v1) return 1; + return 0; +} + +BME_Vert *BME_edge_getothervert(BME_Edge *e, BME_Vert *v){ + if(e->v1 == v) return e->v2; + else if(e->v2 == v) return e->v1; + return NULL; +} + +int BME_edge_swapverts(BME_Edge *e, BME_Vert *orig, BME_Vert *new){ + if(e->v1 == orig){ + e->v1 = new; + e->d1.next = NULL; + e->d1.prev = NULL; + return 1; + } + else if(e->v2 == orig){ + e->v2 = new; + e->d2.next = NULL; + e->d2.prev = NULL; + return 1; + } + return 0; +} + +/** + * ALLOCATION/DEALLOCATION FUNCTIONS + */ + +BME_Vert *BME_addvertlist(BME_Mesh *bm, BME_Vert *example){ + BME_Vert *v=NULL; + v = MEM_callocN(sizeof(BME_Vert), "BME Vertex"); + BLI_addtail(&(bm->verts), v); + v->EID = bm->nextv; + bm->nextv++; + bm->totvert++; + + if(example) + VECCOPY(v->co,example->co); + if(example) + CustomData_em_copy_data(&bm->vdata, &bm->vdata, example->data, &v->data); + else + CustomData_em_set_default(&bm->vdata, &v->data); + + return v; +} +BME_Edge *BME_addedgelist(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2, BME_Edge *example){ + BME_Edge *e=NULL; + e = MEM_callocN(sizeof(BME_Edge), "BME_Edge"); + e->v1 = v1; + e->v2 = v2; + e->d1.data = e; + e->d2.data = e; + e->EID = bm->nexte; + bm->nexte++; + bm->totedge++; + BLI_addtail(&(bm->edges), e); + + if(example) + CustomData_em_copy_data(&bm->edata, &bm->edata, example->data, &e->data); + else + CustomData_em_set_default(&bm->edata, &e->data); + + + return e; +} +BME_Loop *BME_create_loop(BME_Mesh *bm, BME_Vert *v, BME_Edge *e, BME_Poly *f, BME_Loop *example){ + /*allocate a BME_Loop and add it to the loophash*/ + BME_Loop *l=NULL; + BME_CycleNode *loopnode = MEM_callocN(sizeof(BME_CycleNode),"BME Loop Reference"); + l = MEM_callocN(sizeof(BME_Loop), "BME_Loop"); + l->radial.data = l; + l->v = v; + l->e = e; + l->f = f; + l->EID = bm->nextl; + l->gref = loopnode; + loopnode->data = l; + BLI_addtail(&(bm->loops),loopnode); + bm->nextl++; + bm->totloop++; + + +/* if(example) + BME_CustomData_copy_data(&bm->ldata, &bm->ldata, example->data, &l->data); + else + BME_CustomData_set_default(&bm->ldata, &l->data); +*/ + return l; +} + +BME_Poly *BME_addpolylist(BME_Mesh *bm, BME_Poly *example){ + BME_Poly *f = NULL; + f= MEM_callocN(sizeof(BME_Poly),"BME_Poly"); + BLI_addtail(&(bm->polys),f); + f->EID = bm->nextp; + bm->nextp++; + bm->totpoly++; + + if(example) + CustomData_em_copy_data(&bm->pdata, &bm->pdata, example->data, &f->data); + else + CustomData_em_set_default(&bm->pdata, &f->data); + + + return f; +} + +/* free functions dont do much *yet*. When per-vertex, per-edge and per-face/faceloop + data is added though these will be needed. +*/ +void BME_free_vert(BME_Mesh *bm, BME_Vert *v){ + bm->totvert--; + CustomData_em_free_block(&bm->vdata, &v->data); + MEM_freeN(v); +} +void BME_free_edge(BME_Mesh *bm, BME_Edge *e){ + bm->totedge--; + CustomData_em_free_block(&bm->edata, &e->data); + MEM_freeN(e); +} +void BME_free_poly(BME_Mesh *bm, BME_Poly *f){ + bm->totpoly--; + CustomData_em_free_block(&bm->pdata, &f->data); + MEM_freeN(f); +} +void BME_delete_loop(BME_Mesh *bm, BME_Loop *l){ + bm->totloop--; + CustomData_em_free_block(&bm->ldata, &l->data); + MEM_freeN(l); +} +void BME_free_loop(BME_Mesh *bm, BME_Loop *l){ + BME_CycleNode *loopref = l->gref; + BLI_freelinkN(&(bm->loops),loopref); + BME_delete_loop(bm,l); +} + + +/** + * BMESH CYCLES + * + * Cycles are circular doubly linked lists that form the basis of adjacency + * information in the BME modeller. Full adjacency relations can be derived + * from examining these cycles very quickly. Although each cycle is a double + * circular linked list, each one is considered to have a 'base' or 'head', + * and care must be taken by Euler code when modifying the contents of a cycle. + * + * The contents of this file are split into two parts. First there are the + * BME_cycle family of functions which are generic circular double linked list + * procedures. The second part contains higher level procedures for supporting + * modification of specific cycle types. + * + * The three cycles explicitly stored in the BMesh data structure are as follows: + * + * 1: The Disk Cycle - A circle of edges around a vertex + * Base: vertex->edge pointer. + * + * This cycle is the most complicated in terms of its structure. Each BME_Edge contains + * two BME_CycleNode structures to keep track of that edge's membership in the disk cycle + * of each of its vertices. However for any given vertex it may be the first in some edges + * in its disk cycle and the second for others. The BME_disk_XXX family of functions contain + * some nice utilities for navigating disk cycles in a way that hides this detail from the + * tool writer. + * + * Note that the disk cycle is completley independant from face data. One advantage of this + * is that wire edges are fully integrated into the topology database. Another is that the + * the disk cycle has no problems dealing with non-manifold conditions involving faces. + * + * Functions relating to this cycle: + * + * BME_disk_append_edge + * BME_disk_remove_edge + * BME_disk_nextedge + * BME_disk_getpointer + * + * 2: The Radial Cycle - A circle of face edges (BME_Loop) around an edge + * Base: edge->loop->radial structure. + * + * The radial cycle is similar to the radial cycle in the radial edge data structure.* + * Unlike the radial edge however, the radial cycle does not require a large amount of memory + * to store non-manifold conditions since BMesh does not keep track of region/shell + * information. + * + * Functions relating to this cycle: + * + * BME_radial_append + * BME_radial_remove_loop + * BME_radial_nextloop + * BME_radial_find_face + * + * + * 3: The Loop Cycle - A circle of face edges around a polygon. + * Base: polygon->loopbase. + * + * The loop cycle keeps track of a faces vertices and edges. It should be noted that the + * direction of a loop cycle is either CW or CCW depending on the face normal, and is + * not oriented to the faces editedges. + * + * Functions relating to this cycle: + * + * BME_cycle_XXX family of functions. + * + * + * Note that the order of elements in all cycles except the loop cycle is undefined. This + * leads to slightly increased seek time for deriving some adjacency relations, however the + * advantage is that no intrinsic properties of the data structures are dependant upon the + * cycle order and all non-manifold conditions are represented trivially. + * +*/ + + +void BME_cycle_append(void *h, void *nt) +{ + BME_CycleNode *oldtail, *head, *newtail; + + head = (BME_CycleNode*)h; + newtail = (BME_CycleNode*)nt; + + if(head->next == NULL){ + head->next = newtail; + head->prev = newtail; + newtail->next = head; + newtail->prev = head; + } + else{ + oldtail = head->prev; + oldtail->next = newtail; + newtail->next = head; + newtail->prev = oldtail; + head->prev = newtail; + + } +} + +/** + * BME_cycle_length + * + * Count the nodes in a cycle. + * + * Returns - + * Integer + */ + +int BME_cycle_length(void *h){ + + int len = 0; + BME_CycleNode *head, *curnode; + head = (BME_CycleNode*)h; + + if(head){ + len = 1; + for(curnode = head->next; curnode != head; curnode=curnode->next){ + if(len == INT_MAX){ //check for infinite loop/corrupted cycle + return -1; + } + len++; + } + } + return len; +} + + +/** + * BME_cycle_remove + * + * Removes a node from a cycle. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_cycle_remove(void *h, void *remn) +{ + int i, len; + BME_CycleNode *head, *remnode, *curnode; + + head = (BME_CycleNode*)h; + remnode = (BME_CycleNode*)remn; + len = BME_cycle_length(h); + + if(len == 1 && head == remnode){ + head->next = NULL; + head->prev = NULL; + return 1; + } + else{ + for(i=0, curnode = head; i < len; curnode = curnode->next){ + if(curnode == remnode){ + remnode->prev->next = remnode->next; + remnode->next->prev = remnode->prev; + /*zero out remnode pointers, important!*/ + //remnode->next = NULL; + //remnode->prev = NULL; + return 1; + + } + } + } + return 0; +} + +/** + * BME_cycle_validate + * + * Validates a cycle. Takes as an argument the expected length of the cycle and + * a pointer to the cycle head or base. + * + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_cycle_validate(int len, void *h){ + int i; + BME_CycleNode *curnode, *head; + head = (BME_CycleNode*)h; + + /*forward validation*/ + for(i = 0, curnode = head; i < len; i++, curnode = curnode->next); + if(curnode != head) return 0; + + /*reverse validation*/ + for(i = 0, curnode = head; i < len; i++, curnode = curnode->prev); + if(curnode != head) return 0; + + return 1; +} + +/*Begin Disk Cycle routines*/ + +/** + * BME_disk_nextedge + * + * Find the next edge in a disk cycle + * + * Returns - + * Pointer to the next edge in the disk cycle for the vertex v. + */ + +BME_Edge *BME_disk_nextedge(BME_Edge *e, BME_Vert *v) +{ + if(BME_vert_in_edge(e, v)){ + if(e->v1 == v) return e->d1.next->data; + else if(e->v2 == v) return e->d2.next->data; + } + return NULL; +} + +/** + * BME_disk_getpointer + * + * Given an edge and one of its vertices, find the apporpriate CycleNode + * + * Returns - + * Pointer to BME_CycleNode. + */ +BME_CycleNode *BME_disk_getpointer(BME_Edge *e, BME_Vert *v){ + /*returns pointer to the cycle node for the appropriate vertex in this disk*/ + if(e->v1 == v) return &(e->d1); + else if (e->v2 == v) return &(e->d2); + return NULL; +} + +/** + * BME_disk_append_edge + * + * Appends edge to the end of a vertex disk cycle. + * + * Returns - + * 1 for success, 0 for failure + */ + +int BME_disk_append_edge(BME_Edge *e, BME_Vert *v) +{ + + BME_CycleNode *base, *tail; + + if(BME_vert_in_edge(e, v) == 0) return 0; /*check to make sure v is in e*/ + + /*check for loose vert first*/ + if(v->edge == NULL){ + v->edge = e; + base = tail = BME_disk_getpointer(e, v); + BME_cycle_append(base, tail); /*circular reference is ok!*/ + return 1; + } + + /*insert e at the end of disk cycle and make it the new v->edge*/ + base = BME_disk_getpointer(v->edge, v); + tail = BME_disk_getpointer(e, v); + BME_cycle_append(base, tail); + return 1; +} + +/** + * BME_disk_remove_edge + * + * Removes an edge from a disk cycle. If the edge to be removed is + * at the base of the cycle, the next edge becomes the new base. + * + * + * Returns - + * Nothing + */ + +void BME_disk_remove_edge(BME_Edge *e, BME_Vert *v) +{ + BME_CycleNode *base, *remnode; + BME_Edge *newbase; + int len; + + base = BME_disk_getpointer(v->edge, v); + remnode = BME_disk_getpointer(e, v); + + /*first deal with v->edge pointer...*/ + len = BME_cycle_length(base); + if(len == 1) newbase = NULL; + else if(v->edge == e) newbase = base->next-> data; + else newbase = v->edge; + + /*remove and rebase*/ + BME_cycle_remove(base, remnode); + v->edge = newbase; +} + +/** + * BME_disk_next_edgeflag + * + * Searches the disk cycle of v, starting with e, for the + * next edge that has either eflag or tflag. + * + * BME_Edge pointer. + */ + +BME_Edge *BME_disk_next_edgeflag(BME_Edge *e, BME_Vert *v, int eflag, int tflag){ + + BME_CycleNode *diskbase; + BME_Edge *curedge; + int len, ok; + + if(eflag && tflag) return NULL; + + ok = BME_vert_in_edge(e,v); + if(ok){ + diskbase = BME_disk_getpointer(e, v); + len = BME_cycle_length(diskbase); + curedge = BME_disk_nextedge(e,v); + while(curedge != e){ + if(tflag){ + if(curedge->tflag1 == tflag) return curedge; + } + else if(eflag){ + if(curedge->eflag1 == eflag) return curedge; + } + curedge = BME_disk_nextedge(curedge, v); + } + } + return NULL; +} + +/** + * BME_disk_count_edgeflag + * + * Counts number of edges in this verts disk cycle which have + * either eflag or tflag (but not both!) + * + * Returns - + * Integer. + */ + +int BME_disk_count_edgeflag(BME_Vert *v, int eflag, int tflag){ + BME_CycleNode *diskbase; + BME_Edge *curedge; + int i, len=0, count=0; + + if(v->edge){ + if(eflag && tflag) return 0; /*tflag and eflag are reserved for different functions!*/ + diskbase = BME_disk_getpointer(v->edge, v); + len = BME_cycle_length(diskbase); + + for(i = 0, curedge=v->edge; itflag1 == tflag) count++; + } + else if(eflag){ + if(curedge->eflag1 == eflag) count++; + } + curedge = BME_disk_nextedge(curedge, v); + } + } + return count; +} + +int BME_disk_hasedge(BME_Vert *v, BME_Edge *e){ + BME_CycleNode *diskbase; + BME_Edge *curedge; + int i, len=0; + + if(v->edge){ + diskbase = BME_disk_getpointer(v->edge,v); + len = BME_cycle_length(diskbase); + + for(i = 0, curedge=v->edge; iradial.next->data); +} + +void BME_radial_append(BME_Edge *e, BME_Loop *l){ + if(e->loop == NULL) e->loop = l; + BME_cycle_append(&(e->loop->radial), &(l->radial)); +} + +void BME_radial_remove_loop(BME_Loop *l, BME_Edge *e) +{ + BME_Loop *newbase; + int len; + + /*deal with edge->loop pointer*/ + len = BME_cycle_length(&(e->loop->radial)); + if(len == 1) newbase = NULL; + else if(e->loop == l) newbase = e->loop->radial.next->data; + else newbase = e->loop; + + /*remove and rebase*/ + BME_cycle_remove(&(e->loop->radial), &(l->radial)); + e->loop = newbase; +} + +int BME_radial_find_face(BME_Edge *e,BME_Poly *f) +{ + + BME_Loop *curloop; + int i, len; + + len = BME_cycle_length(&(e->loop->radial)); + for(i = 0, curloop = e->loop; i < len; i++, curloop = curloop->radial.next->data){ + if(curloop->f == f) return 1; + } + return 0; +} + +struct BME_Loop *BME_loop_find_loop(struct BME_Poly *f, struct BME_Vert *v) { + BME_Loop *l; + int i, len; + + len = BME_cycle_length(f->loopbase); + for (i = 0, l=f->loopbase; i < len; i++, l=l->next) { + if (l->v == v) return l; + } + return NULL; +} diff --git a/source/blender/blenkernel/intern/BME_tools.c b/source/blender/blenkernel/intern/BME_tools.c new file mode 100644 index 00000000000..441a8f52692 --- /dev/null +++ b/source/blender/blenkernel/intern/BME_tools.c @@ -0,0 +1,1326 @@ +/** + * BME_tools.c jan 2007 + * + * Functions for changing the topology of a mesh. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle and Levi Schooley. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "blendef.h" + +/** + * BME_dissolve_edge + * + * Edge Dissolve Function: + * + * Dissolves a 2-manifold edge by joining it's two faces. if + * they have opposite windings it first makes them consistent + * by calling BME_loop_reverse() + * + * Returns - +*/ + +/** + * BME_inset_edge + * + * Edge Inset Function: + * + * Splits a face in two along an edge and returns the next loop + * + * Returns - + * A BME_Poly pointer. + */ + +BME_Loop *BME_inset_edge(BME_Mesh *bm, BME_Loop *l, BME_Poly *f){ + BME_Loop *nloop; + BME_SFME(bm, f, l->v, l->next->v, &nloop); + return nloop->next; +} + +/** + * BME_inset_poly + * + * Face Inset Tool: + * + * Insets a single face and returns a pointer to the face at the + * center of the newly created region + * + * Returns - + * A BME_Poly pointer. + */ + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +BME_Poly *BME_inset_poly(BME_Mesh *bm,BME_Poly *f){ + + BME_Vert *v; + BME_Loop *l,*nextloop, *killoop, *sloop; + + int len,i; + float max[3],min[3],cent[3]; //center of original face + + /*get bounding box for face*/ + VECCOPY(max,f->loopbase->v->co); + VECCOPY(min,f->loopbase->v->co); + len = f->len; + for(i=0,l=f->loopbase;inext){ + max[0] = MAX(max[0],l->v->co[0]); + max[1] = MAX(max[1],l->v->co[1]); + max[2] = MAX(max[2],l->v->co[2]); + + min[0] = MIN(min[0],l->v->co[0]); + min[1] = MIN(min[1],l->v->co[1]); + min[2] = MIN(min[2],l->v->co[2]); + } + + cent[0] = (min[0] + max[0]) / 2.0f; + cent[1] = (min[1] + max[1]) / 2.0f; + cent[2] = (min[2] + max[2]) / 2.0f; + + /*inset each edge in the polygon.*/ + len = f->len; + for(i=0,l=f->loopbase; i < len; i++){ + nextloop = l->next; + f = BME_SFME(bm,l->f,l->v,l->next->v,NULL); + l=nextloop; + } + + /*for each new edge, call SEMV on it*/ + for(i=0,l=f->loopbase; i < len; i++, l=l->next){ + l->tflag1 = 1; //going to store info that this loops edge still needs split + f = BME_SFME(bm,l->f,l->v,l->next->v,NULL); + l->tflag2 = l->v->tflag1 = l->v->tflag2 = 0; + } + + len = f->len; + for(i=0,l=f->loopbase; i < len; i++){ + if(l->tflag1){ + l->tflag1 = 0; + v= BME_SEMV(bm,l->next->v,l->e,NULL); + VECCOPY(v->co,l->v->co); + v->tflag1 = 1; + l = l->next->next; + } + } + + len = f->len; + sloop = NULL; + for(i=0,l=f->loopbase; i < len; i++,l=l->next){ + if(l->v->tflag1 && l->next->next->v->tflag1){ + sloop = l; + break; + } + } + if(sloop){ + for(i=0,l=sloop; i < len; i++){ + nextloop = l->next->next; + f = BME_SFME(bm,f,l->v,l->next->next->v,&killoop); + i+=1; //i+=2; + BME_JFKE(bm,l->f,((BME_Loop*)l->radial.next->data)->f,l->e); + l=nextloop; + } + } + + len = f->len; + for(i=0,l=f->loopbase; i < len; i++,l=l->next){ + l->v->co[0] = (l->v->co[0] + cent[0]) / 2.0f; + l->v->co[1] = (l->v->co[1] + cent[1]) / 2.0f; + l->v->co[2] = (l->v->co[2] + cent[2]) / 2.0f; + } + return NULL; +} + +/* ------- Bevel code starts here -------- */ + +BME_TransData_Head *BME_init_transdata(int bufsize) { + BME_TransData_Head *td; + + td = MEM_callocN(sizeof(BME_TransData_Head), "BMesh transdata header"); + td->gh = BLI_ghash_new(BLI_ghashutil_ptrhash,BLI_ghashutil_ptrcmp); + td->ma = BLI_memarena_new(bufsize); + BLI_memarena_use_calloc(td->ma); + + return td; +} + +void BME_free_transdata(BME_TransData_Head *td) { + BLI_ghash_free(td->gh,NULL,NULL); + BLI_memarena_free(td->ma); + MEM_freeN(td); +} + +BME_TransData *BME_assign_transdata(BME_TransData_Head *td, BME_Mesh *bm, BME_Vert *v, + float *co, float *org, float *vec, float *loc, + float factor, float weight, float maxfactor, float *max) { + BME_TransData *vtd; + int is_new = 0; + + if (v == NULL) return NULL; + + if ((vtd = BLI_ghash_lookup(td->gh, v)) == NULL && bm != NULL) { + vtd = BLI_memarena_alloc(td->ma, sizeof(*vtd)); + BLI_ghash_insert(td->gh, v, vtd); + td->len++; + is_new = 1; + } + + vtd->bm = bm; + vtd->v = v; + if (co != NULL) VECCOPY(vtd->co,co); + if (org == NULL && is_new) { VECCOPY(vtd->org,v->co); } /* default */ + else if (org != NULL) VECCOPY(vtd->org,org); + if (vec != NULL) { + VECCOPY(vtd->vec,vec); + Normalize(vtd->vec); + } + vtd->loc = loc; + + vtd->factor = factor; + vtd->weight = weight; + vtd->maxfactor = maxfactor; + vtd->max = max; + + return vtd; +} + +BME_TransData *BME_get_transdata(BME_TransData_Head *td, BME_Vert *v) { + BME_TransData *vtd; + vtd = BLI_ghash_lookup(td->gh, v); + return vtd; +} + +/* a hack (?) to use the transdata memarena to allocate floats for use with the max limits */ +float *BME_new_transdata_float(BME_TransData_Head *td) { + return BLI_memarena_alloc(td->ma, sizeof(float)); +} + +int BME_is_nonmanifold_vert(BME_Mesh *bm, BME_Vert *v) { + BME_Edge *e, *oe; + BME_Loop *l; + int len, count, flag; + + if (v->edge == NULL) { + /* loose vert */ + return 1; + } + + /* count edges while looking for non-manifold edges */ + oe = v->edge; + for (len=0,e=v->edge; e != oe || (e == oe && len == 0); len++,e=BME_disk_nextedge(e,v)) { + if (e->loop == NULL) { + /* loose edge */ + return 1; + } + + if (BME_cycle_length(&(e->loop->radial)) > 2) { + /* edge shared by more than two faces */ + return 1; + } + } + + count = 1; + flag = 1; + e = NULL; + oe = v->edge; + l = oe->loop; + while(e != oe) { + if (l->v == v) l = l->prev; + else l = l->next; + e = l->e; + count++; /* count the edges */ + + if (flag && l->radial.next->data == l) { + /* we've hit the edge of an open mesh, reset once */ + flag = 0; + count = 1; + oe = e; + e = NULL; + l = oe->loop; + } + else if (l->radial.next->data == l) { + /* break the loop */ + e = oe; + } + else { + l = l->radial.next->data; + } + } + + if (count < len) { + /* vert shared by multiple regions */ + return 1; + } + + return 0; +} + +/* a wrapper for BME_JFKE that [for now just] checks to + * make sure loop directions are compatible */ +BME_Poly *BME_JFKE_safe(BME_Mesh *bm, BME_Poly *f1, BME_Poly *f2, BME_Edge *e) { + BME_Loop *l1, *l2; + + l1 = e->loop; + l2 = l1->radial.next->data; + if (l1->v == l2->v) { + BME_loop_reverse(bm, f2); + } + + return BME_JFKE(bm, f1, f2, e); +} + +/* a wrapper for BME_SFME that transfers element flags */ +BME_Poly *BME_split_face(BME_Mesh *bm, BME_Poly *f, BME_Vert *v1, BME_Vert *v2, BME_Loop **nl, BME_Edge *example) { + BME_Poly *nf; + nf = BME_SFME(bm,f,v1,v2,nl); + nf->flag = f->flag; + /* if the edge was selected, select this face, too */ + if (example->flag & SELECT) f->flag |= ME_FACE_SEL; + nf->h = f->h; + nf->mat_nr = f->mat_nr; + if (nl && example) { + (*nl)->e->flag = example->flag; + (*nl)->e->h = example->h; + (*nl)->e->crease = example->crease; + (*nl)->e->bweight = example->bweight; + } + + return nf; +} + +/* a wrapper for BME_SEMV that transfers element flags */ +BME_Vert *BME_split_edge(BME_Mesh *bm, BME_Vert *v, BME_Edge *e, BME_Edge **ne, float percent) { + BME_Vert *nv, *v2; + float len; + + v2 = BME_edge_getothervert(e,v); + nv = BME_SEMV(bm,v,e,ne); + if (nv == NULL) return NULL; + VECSUB(nv->co,v2->co,v->co); + len = VecLength(nv->co); + VECADDFAC(nv->co,v->co,nv->co,len*percent); + nv->flag = v->flag; + nv->bweight = v->bweight; + if (ne) { + (*ne)->flag = e->flag; + (*ne)->h = e->h; + (*ne)->crease = e->crease; + (*ne)->bweight = e->bweight; + } + + return nv; +} + +int BME_bevel_is_split_vert(BME_Loop *l) { + /* look for verts that have already been added to the edge when + * beveling other polys; this can be determined by testing the + * vert and the edges around it for originality + */ + if ((l->v->tflag1 & BME_BEVEL_ORIG)==0 + && (l->e->tflag1 & BME_BEVEL_ORIG) + && (l->prev->e->tflag1 & BME_BEVEL_ORIG)) + { + return 1; + } + return 0; +} + +/* get a vector, vec, that points from v1->co to wherever makes sense to + * the bevel operation as a whole based on the relationship between v1 and v2 + * (won't necessarily be a vec from v1->co to v2->co, though it probably will be); + * the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */ +int BME_bevel_get_vec(float *vec, BME_Vert *v1, BME_Vert *v2, BME_TransData_Head *td) { + BME_TransData *vtd1, *vtd2; + + vtd1 = BME_get_transdata(td,v1); + vtd2 = BME_get_transdata(td,v2); + if (!vtd1 || !vtd2) { + printf("BME_bevel_get_vec() got called without proper BME_TransData\n"); + return -1; + } + + /* compare the transform origins to see if we can use the vert co's; + * if they belong to different origins, then we will use the origins to determine + * the vector */ + if (VecCompare(vtd1->org,vtd2->org,0.000001f)) { + VECSUB(vec,v2->co,v1->co); + if (VecLength(vec) < 0.000001f) { + VecMulf(vec,0); + } + return 0; + } + else { + VECSUB(vec,vtd2->org,vtd1->org); + if (VecLength(vec) < 0.000001f) { + VecMulf(vec,0); + } + return 1; + } +} + +/* "Projects" a vector perpendicular to vec2 against vec1, such that + * the projected vec1 + vec2 has a min distance of 1 from the "edge" defined by vec2. + * note: the direction, is_forward, is used in conjunction with up_vec to determine + * whether this is a convex or concave corner. If it is a concave corner, it will + * be projected "backwards." If vec1 is before vec2, is_forward should be 0 (we are projecting backwards). + * vec1 is the vector to project onto (expected to be normalized) + * vec2 is the direction of projection (pointing away from vec1) + * up_vec is used for orientation (expected to be normalized) + * returns the length of the projected vector that lies along vec1 */ +float BME_bevel_project_vec(float *vec1, float *vec2, float *up_vec, int is_forward, BME_TransData_Head *td) { + float factor, vec3[3], tmp[3],c1,c2; + + Crossf(tmp,vec1,vec2); + Normalize(tmp); + factor = Inpf(up_vec,tmp); + if ((factor > 0 && is_forward) || (factor < 0 && !is_forward)) { + Crossf(vec3,vec2,tmp); /* hmm, maybe up_vec should be used instead of tmp */ + } + else { + Crossf(vec3,tmp,vec2); /* hmm, maybe up_vec should be used instead of tmp */ + } + Normalize(vec3); + c1 = Inpf(vec3,vec1); + c2 = Inpf(vec1,vec1); + if (fabs(c1) < 0.000001f || fabs(c2) < 0.000001f) { + factor = 0.0f; + } + else { + factor = c2/c1; + } + + return factor; +} + +/* BME_bevel_split_edge() is the main math work-house; its responsibilities are: + * using the vert and the loop passed, get or make the split vert, set its coordinates + * and transform properties, and set the max limits. + * Finally, return the split vert. */ +BME_Vert *BME_bevel_split_edge(BME_Mesh *bm, BME_Vert *v, BME_Vert *v1, BME_Loop *l, float *up_vec, float value, BME_TransData_Head *td) { + BME_TransData *vtd, *vtd1, *vtd2; + BME_Vert *sv, *v2, *v3; + BME_Loop *lv1, *lv2; + BME_Edge *ne, *e1, *e2; + float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3]; + int is_edge, forward, is_split_vert; + + if (l == NULL) { + /* what you call operator overloading in C :) + * I wanted to use the same function for both wire edges and poly loops + * so... here we walk around edges to find the needed verts */ + forward = 1; + is_split_vert = 0; + if (v->edge == NULL) { + printf("We can't split a loose vert's edge!\n"); + return NULL; + } + e1 = v->edge; /* we just use the first two edges */ + e2 = BME_disk_nextedge(v->edge, v); + if (e1 == e2) { + printf("You need at least two edges to use BME_bevel_split_edge()\n"); + return NULL; + } + v2 = BME_edge_getothervert(e1, v); + v3 = BME_edge_getothervert(e2, v); + if (v1 != v2 && v1 != v3) { + printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n"); + return NULL; + } + if (v1 == v2) { + v2 = v3; + } + else { + e1 = e2; + } + sv = BME_split_edge(bm,v,e1,&ne,0); + BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ + sv->tflag1 |= BME_BEVEL_BEVEL; + ne->tflag1 = BME_BEVEL_ORIG; /* mark edge as original, even though it isn't */ + BME_bevel_get_vec(vec1,v1,v,td); + BME_bevel_get_vec(vec2,v2,v,td); + Crossf(t_up_vec,vec1,vec2); + Normalize(t_up_vec); + up_vec = t_up_vec; + } + else { + /* establish loop direction */ + if (l->v == v) { + forward = 1; + lv1 = l->next; + lv2 = l->prev; + v1 = l->next->v; + v2 = l->prev->v; + } + else if (l->next->v == v) { + forward = 0; + lv1 = l; + lv2 = l->next->next; + v1 = l->v; + v2 = l->next->next->v; + } + else { + printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n"); + return NULL; + } + + if (BME_bevel_is_split_vert(lv1)) { + is_split_vert = 1; + sv = v1; + if (forward) v1 = l->next->next->v; + else v1 = l->prev->v; + } + else { + is_split_vert = 0; + sv = BME_split_edge(bm,v,l->e,&ne,0); + BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ + sv->tflag1 |= BME_BEVEL_BEVEL; + ne->tflag1 = BME_BEVEL_ORIG; /* mark edge as original, even though it isn't */ + } + + if (BME_bevel_is_split_vert(lv2)) { + if (forward) v2 = lv2->prev->v; + else v2 = lv2->next->v; + } + } + + is_edge = BME_bevel_get_vec(vec1,v,v1,td); /* get the vector we will be projecting onto */ + BME_bevel_get_vec(vec2,v,v2,td); /* get the vector we will be projecting parallel to */ + len = VecLength(vec1); + Normalize(vec1); + + vtd = BME_get_transdata(td, sv); + vtd1 = BME_get_transdata(td, v); + vtd2 = BME_get_transdata(td,v1); + + if (vtd1->loc == NULL) { + /* this is a vert with data only for calculating initial weights */ + if (vtd1->weight < 0) { + vtd1->weight = 0; + } + scale = vtd1->weight/vtd1->factor; + if (!vtd1->max) { + vtd1->max = BME_new_transdata_float(td); + *vtd1->max = -1; + } + } + else { + scale = vtd1->weight; + } + vtd->max = vtd1->max; + + if (is_edge && vtd1->loc != NULL) { + maxfactor = vtd1->maxfactor; + } + else { + maxfactor = scale*BME_bevel_project_vec(vec1,vec2,up_vec,forward,td); + if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) { + maxfactor = vtd->maxfactor; + } + } + + dis = (v1->tflag1 & BME_BEVEL_ORIG)? len/3 : len/2; + if (is_edge || dis > maxfactor*value) { + dis = maxfactor*value; + } + VECADDFAC(sv->co,v->co,vec1,dis); + VECSUB(vec1,sv->co,vtd1->org); + dis = VecLength(vec1); + Normalize(vec1); + BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max); + + return sv; +} + +float BME_bevel_set_max(BME_Vert *v1, BME_Vert *v2, float value, BME_TransData_Head *td) { + BME_TransData *vtd1, *vtd2; + float max, fac1, fac2, vec1[3], vec2[3], vec3[3]; + + BME_bevel_get_vec(vec1,v1,v2,td); + vtd1 = BME_get_transdata(td,v1); + vtd2 = BME_get_transdata(td,v2); + + if (vtd1->loc == NULL) { + fac1 = 0; + } + else { + VECCOPY(vec2,vtd1->vec); + VecMulf(vec2,vtd1->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec2,vec1); + fac1 = VecLength(vec2)/value; + } + else { + fac1 = 0; + } + } + + if (vtd2->loc == NULL) { + fac2 = 0; + } + else { + VECCOPY(vec3,vtd2->vec); + VecMulf(vec3,vtd2->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec3,vec1); + fac2 = VecLength(vec2)/value; + } + else { + fac2 = 0; + } + } + + if (fac1 || fac2) { + max = VecLength(vec1)/(fac1 + fac2); + if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { + *vtd1->max = max; + } + if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { + *vtd2->max = max; + } + } + else { + max = -1; + } + + return max; +} + +BME_Vert *BME_bevel_wire(BME_Mesh *bm, BME_Vert *v, float value, int res, int options, BME_TransData_Head *td) { + BME_Vert *ov1, *ov2, *v1, *v2; + + ov1 = BME_edge_getothervert(v->edge, v); + ov2 = BME_edge_getothervert(BME_disk_nextedge(v->edge, v), v); + + /* split the edges */ + v1 = BME_bevel_split_edge(bm,v,ov1,NULL,NULL,value,td); + v1->tflag1 |= BME_BEVEL_NONMAN; + v2 = BME_bevel_split_edge(bm,v,ov2,NULL,NULL,value,td); + v2->tflag1 |= BME_BEVEL_NONMAN; + + if (value > 0.5) { + BME_bevel_set_max(v1,ov1,value,td); + BME_bevel_set_max(v2,ov2,value,td); + } + + /* remove the original vert */ + if (res) { + BME_JEKV(bm,v->edge,v); + } + + return v1; +} + +BME_Loop *BME_bevel_edge(BME_Mesh *bm, BME_Loop *l, float value, int options, float *up_vec, BME_TransData_Head *td) { + BME_Vert *v1, *v2, *kv; + BME_Loop *kl, *nl; + BME_Edge *e; + BME_Poly *f; + float factor=1; + + f = l->f; + e = l->e; + + if ((l->e->tflag1 & BME_BEVEL_BEVEL) == 0 + && ((l->v->tflag1 & BME_BEVEL_BEVEL) || (l->next->v->tflag1 & BME_BEVEL_BEVEL))) + { /* sanity check */ + return l; + } + + /* checks and operations for prev edge */ + /* first, check to see if this edge was inset previously */ + if ((l->prev->e->tflag1 & BME_BEVEL_ORIG) == 0 + && (l->v->tflag1 & BME_BEVEL_NONMAN) == 0) { + kl = l->prev->radial.next->data; + if (kl->v == l->v) kl = kl->prev; + else kl = kl->next; + kv = l->v; + } + else { + kv = NULL; + } + /* get/make the first vert to be used in SFME */ + if (l->v->tflag1 & BME_BEVEL_NONMAN){ + v1 = l->v; + } + else { /* we'll need to split the previous edge */ + v1 = BME_bevel_split_edge(bm,l->v,NULL,l->prev,up_vec,value,td); + } + /* if we need to clean up geometry... */ + if (kv) { + l = l->next; + if (kl->v == kv) { + BME_split_face(bm,kl->f,kl->prev->v,kl->next->v,&nl,kl->prev->e); + BME_JFKE(bm,((BME_Loop*)kl->prev->radial.next->data)->f,kl->f,kl->prev->e); + BME_JEKV(bm,kl->e,kv); + } + else { + BME_split_face(bm,kl->f,kl->next->next->v,kl->v,&nl,kl->next->e); + BME_JFKE(bm,((BME_Loop*)kl->next->radial.next->data)->f,kl->f,kl->next->e); + BME_JEKV(bm,kl->e,kv); + } + l = l->prev; + } + + /* checks and operations for the next edge */ + /* first, check to see if this edge was inset previously */ + if ((l->next->e->tflag1 & BME_BEVEL_ORIG) == 0 + && (l->next->v->tflag1 & BME_BEVEL_NONMAN) == 0) { + kl = l->next->radial.next->data; + if (kl->v == l->next->v) kl = kl->prev; + else kl = kl->next; + kv = l->next->v; + } + else { + kv = NULL; + } + /* get/make the second vert to be used in SFME */ + if (l->next->v->tflag1 & BME_BEVEL_NONMAN) { + v2 = l->next->v; + } + else { /* we'll need to split the next edge */ + v2 = BME_bevel_split_edge(bm,l->next->v,NULL,l->next,up_vec,value,td); + } + /* if we need to clean up geometry... */ + if (kv) { + if (kl->v == kv) { + BME_split_face(bm,kl->f,kl->prev->v,kl->next->v,&nl,kl->prev->e); + BME_JFKE(bm,((BME_Loop*)kl->prev->radial.next->data)->f,kl->f,kl->prev->e); + BME_JEKV(bm,kl->e,kv); + } + else { + BME_split_face(bm,kl->f,kl->next->next->v,kl->v,&nl,kl->next->e); + BME_JFKE(bm,((BME_Loop*)kl->next->radial.next->data)->f,kl->f,kl->next->e); + BME_JEKV(bm,kl->e,kv); + } + } + + if ((v1->tflag1 & BME_BEVEL_NONMAN)==0 || (v2->tflag1 & BME_BEVEL_NONMAN)==0) { + BME_split_face(bm,f,v2,v1,&l,e); + l->e->tflag1 = BME_BEVEL_BEVEL; + l = l->radial.next->data; + } + + if (l->f != f) printf("Whoops! You got something out of order in BME_bevel_edge()!\n"); + + return l; +} + +BME_Loop *BME_bevel_vert(BME_Mesh *bm, BME_Loop *l, float value, int options, float *up_vec, BME_TransData_Head *td) { + BME_Vert *v1, *v2; + BME_Poly *f; + + /* get/make the first vert to be used in SFME */ + /* may need to split the previous edge */ + v1 = BME_bevel_split_edge(bm,l->v,NULL,l->prev,up_vec,value,td); + + /* get/make the second vert to be used in SFME */ + /* may need to split this edge (so move l) */ + l = l->prev; + v2 = BME_bevel_split_edge(bm,l->next->v,NULL,l->next,up_vec,value,td); + l = l->next->next; + + /* "cut off" this corner */ + f = BME_split_face(bm,l->f,v2,v1,NULL,l->e); + + return l; +} + +/** + * BME_bevel_poly + * + * Polygon inset tool: + * + * Insets a polygon/face based on the tflag1's of its vertices + * and edges. Used by the bevel tool only, for now. + * The parameter "value" is the distance to inset (should be negative). + * The parameter "options" is not currently used. + * + * Returns - + * A BME_Poly pointer to the resulting inner face. +*/ +BME_Poly *BME_bevel_poly(BME_Mesh *bm, BME_Poly *f, float value, int options, BME_TransData_Head *td) { + BME_Loop *l, *ol; + BME_TransData *vtd1, *vtd2; + float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1; + int len, i; + + up_vec[0] = 0.0f; + up_vec[1] = 0.0f; + up_vec[2] = 0.0f; + /* find a good normal for this face (there's better ways, I'm sure) */ + ol = f->loopbase; + l = ol->next; + for (i=0,ol=f->loopbase,l=ol->next; l->next!=ol; l=l->next) { + BME_bevel_get_vec(vec1,l->next->v,ol->v,td); + BME_bevel_get_vec(vec2,l->v,ol->v,td); + Crossf(vec3,vec2,vec1); + VECADD(up_vec,up_vec,vec3); + i++; + } + VecMulf(up_vec,1.0f/i); + Normalize(up_vec); + + for (i=0,len=f->len; inext) { + if ((l->e->tflag1 & BME_BEVEL_BEVEL) && (l->e->tflag1 & BME_BEVEL_ORIG)) { + max = 1.0f; + l = BME_bevel_edge(bm, l, value, options, up_vec, td); + } + else if ((l->v->tflag1 & BME_BEVEL_BEVEL) && (l->v->tflag1 & BME_BEVEL_ORIG) && (l->prev->e->tflag1 & BME_BEVEL_BEVEL) == 0) { + max = 1.0f; + l = BME_bevel_vert(bm, l, value, options, up_vec, td); + } + } + + /* max pass */ + if (value > 0.5 && max > 0) { + max = -1; + for (i=0,len=f->len; inext) { + if ((l->e->tflag1 & BME_BEVEL_BEVEL) || (l->e->tflag1 & BME_BEVEL_ORIG)) { + BME_bevel_get_vec(vec1,l->v,l->next->v,td); + vtd1 = BME_get_transdata(td,l->v); + vtd2 = BME_get_transdata(td,l->next->v); + if (vtd1->loc == NULL) { + fac1 = 0; + } + else { + VECCOPY(vec2,vtd1->vec); + VecMulf(vec2,vtd1->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec2,vec1); + fac1 = VecLength(vec2)/value; + } + else { + fac1 = 0; + } + } + if (vtd2->loc == NULL) { + fac2 = 0; + } + else { + VECCOPY(vec3,vtd2->vec); + VecMulf(vec3,vtd2->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec3,vec1); + fac2 = VecLength(vec2)/value; + } + else { + fac2 = 0; + } + } + if (fac1 || fac2) { + max = VecLength(vec1)/(fac1 + fac2); + if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { + *vtd1->max = max; + } + if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { + *vtd2->max = max; + } + } + } + } + } + + return l->f; +} + +void BME_bevel_add_vweight(BME_TransData_Head *td, BME_Mesh *bm, BME_Vert *v, float weight, float factor, int options) { + BME_TransData *vtd; + + if (v->tflag1 & BME_BEVEL_NONMAN) return; + v->tflag1 |= BME_BEVEL_BEVEL; + if (vtd = BME_get_transdata(td, v)) { + if (options & BME_BEVEL_EMIN) { + vtd->factor = 1.0; + if (vtd->weight < 0 || weight < vtd->weight) { + vtd->weight = weight; + } + } + else if (options & BME_BEVEL_EMAX) { + vtd->factor = 1.0; + if (weight > vtd->weight) { + vtd->weight = weight; + } + } + else if (vtd->weight < 0) { + vtd->factor = factor; + vtd->weight = weight; + } + else { + vtd->factor += factor; /* increment number of edges with weights (will be averaged) */ + vtd->weight += weight; /* accumulate all the weights */ + } + } + else { + /* we'll use vtd->loc == NULL to mark that this vert is not moving */ + vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL); + } +} + +float BME_bevel_get_angle(BME_Mesh *bm, BME_Edge *e, BME_Vert *v) { + BME_Vert *v1, *v2; + BME_Loop *l1, *l2; + float vec1[3], vec2[3], vec3[3], vec4[3]; + + l1 = e->loop; + l2 = e->loop->radial.next->data; + if (l1->v == v) { + v1 = l1->prev->v; + v2 = l1->next->v; + } + else { + v1 = l1->next->next->v; + v2 = l1->v; + } + VECSUB(vec1,v1->co,v->co); + VECSUB(vec2,v2->co,v->co); + Crossf(vec3,vec1,vec2); + + l1 = l2; + if (l1->v == v) { + v1 = l1->prev->v; + v2 = l1->next->v; + } + else { + v1 = l1->next->next->v; + v2 = l1->v; + } + VECSUB(vec1,v1->co,v->co); + VECSUB(vec2,v2->co,v->co); + Crossf(vec4,vec2,vec1); + + Normalize(vec3); + Normalize(vec4); + + return Inpf(vec3,vec4); +} + +/** + * BME_bevel_initialize + * + * Prepare the mesh for beveling: + * + * Sets the tflag1's of the mesh elements based on the options passed. + * + * Returns - + * A BME_Mesh pointer to the BMesh passed as a parameter. +*/ +BME_Mesh *BME_bevel_initialize(BME_Mesh *bm, int options, int defgrp_index, float angle, BME_TransData_Head *td) { + BME_Vert *v; + BME_Edge *e; + BME_Poly *f; + BME_TransData *vtd; + MDeformVert *dvert; + MDeformWeight *dw; + int i, len; + float weight, threshold; + + /* vert pass */ + for (v=bm->verts.first; v; v=v->next) { + dvert = NULL; + dw = NULL; + v->tflag1 = BME_BEVEL_ORIG; + /* originally coded, a vertex gets tagged with BME_BEVEL_BEVEL in this pass if + * the vert is manifold (or is shared by only two edges - wire bevel) + * BME_BEVEL_SELECT is passed and the vert has v->flag&SELECT or + * BME_BEVEL_VWEIGHT is passed, and the vert has a defgrp and weight + * BME_BEVEL_ANGLE is not passed + * BME_BEVEL_EWEIGHT is not passed + */ + /* originally coded, a vertex gets tagged with BME_BEVEL_NONMAN in this pass if + * the vert is loose, shared by multiple regions, or is shared by wire edges + * note: verts belonging to edges of open meshes are not tagged with BME_BEVEL_NONMAN + */ + /* originally coded, a vertex gets a transform weight set in this pass if + * BME_BEVEL_VWEIGHT is passed, and the vert has a defgrp and weight + */ + + /* get disk cycle length */ + if (v->edge == NULL) { + len = 0; + } + else { + len = BME_cycle_length(BME_disk_getpointer(v->edge,v)); + /* we'll assign a default transform data to every vert (except the loose ones) */ + vtd = BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL); + } + + /* check for non-manifold vert */ + if (BME_is_nonmanifold_vert(bm,v)) { + v->tflag1 |= BME_BEVEL_NONMAN; + } + + /* BME_BEVEL_BEVEL tests */ + if ((v->tflag1 & BME_BEVEL_NONMAN) == 0 || len == 2) { /* either manifold vert, or wire vert */ + if (((options & BME_BEVEL_SELECT) && (v->flag & SELECT)) + || ((options & BME_BEVEL_WEIGHT) && (options & BME_BEVEL_VERT)) /* use weights for verts */ + || ((options & BME_BEVEL_ANGLE) == 0 + && (options & BME_BEVEL_SELECT) == 0 + && (options & BME_BEVEL_WEIGHT) == 0)) + { + if (options & BME_BEVEL_WEIGHT) { + /* do vert weight stuff */ + //~ dvert = CustomData_em_get(&bm->vdata,v->data,CD_MDEFORMVERT); + //~ if (!dvert) continue; + //~ for (i = 0; i < dvert->totweight; ++i) { + //~ if(dvert->dw[i].def_nr == defgrp_index) { + //~ dw = &dvert->dw[i]; + //~ break; + //~ } + //~ } + //~ if (!dw || dw->weight == 0.0) continue; + if (v->bweight == 0.0) continue; + vtd = BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, v->bweight, -1, NULL); + v->tflag1 |= BME_BEVEL_BEVEL; + } + else { + vtd = BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, 1.0, -1, NULL); + v->tflag1 |= BME_BEVEL_BEVEL; + } + } + } + } + + /* edge pass */ + threshold = (float)cos((angle + 0.00001) * M_PI / 180.0); + for (e=bm->edges.first; e; e=e->next) { + e->tflag1 = BME_BEVEL_ORIG; + weight = 0.0; + /* originally coded, an edge gets tagged with BME_BEVEL_BEVEL in this pass if + * BME_BEVEL_VERT is not set + * the edge is manifold (shared by exactly two faces) + * BME_BEVEL_SELECT is passed and the edge has e->flag&SELECT or + * BME_BEVEL_EWEIGHT is passed, and the edge has the crease set or + * BME_BEVEL_ANGLE is passed, and the edge is sharp enough + * BME_BEVEL_VWEIGHT is passed, and both verts are set for bevel + */ + /* originally coded, a vertex gets tagged with BME_BEVEL_BEVEL in this pass if + * the vert belongs to the edge + * the vert is not tagged with BME_BEVEL_NONMAN + * the edge is eligible for bevel (even if BME_BEVEL_VERT is set, or the edge is shared by less than 2 faces) + */ + /* originally coded, a vertex gets a transform weight set in this pass if + * the vert belongs to the edge + * the edge has a weight + */ + /* note: edge weights are cumulative at the verts, + * i.e. the vert's weight is the average of the weights of its weighted edges + */ + + if (e->loop == NULL) { + len = 0; + e->v1->tflag1 |= BME_BEVEL_NONMAN; + e->v2->tflag1 |= BME_BEVEL_NONMAN; + } + else { + len = BME_cycle_length(&(e->loop->radial)); + } + + if (len > 2) { + /* non-manifold edge of the worst kind */ + continue; + } + + if ((options & BME_BEVEL_SELECT) && (e->flag & SELECT)) { + weight = 1.0; + /* stupid editmode doesn't always flush selections, or something */ + e->v1->flag |= SELECT; + e->v2->flag |= SELECT; + } + else if ((options & BME_BEVEL_WEIGHT) && (options & BME_BEVEL_VERT) == 0) { + weight = e->bweight; + } + else if (options & BME_BEVEL_ANGLE) { + if ((e->v1->tflag1 & BME_BEVEL_NONMAN) == 0 && BME_bevel_get_angle(bm,e,e->v1) < threshold) { + e->tflag1 |= BME_BEVEL_BEVEL; + e->v1->tflag1 |= BME_BEVEL_BEVEL; + BME_bevel_add_vweight(td, bm, e->v1, 1.0, 1.0, options); + } + else { + BME_bevel_add_vweight(td, bm, e->v1, 0.0, 1.0, options); + } + if ((e->v2->tflag1 & BME_BEVEL_NONMAN) == 0 && BME_bevel_get_angle(bm,e,e->v2) < threshold) { + e->tflag1 |= BME_BEVEL_BEVEL; + e->v2->tflag1 |= BME_BEVEL_BEVEL; + BME_bevel_add_vweight(td, bm, e->v2, 1.0, 1.0, options); + } + else { + BME_bevel_add_vweight(td, bm, e->v2, 0.0, 1.0, options); + } + } + //~ else if ((options & BME_BEVEL_VWEIGHT) && (options & BME_BEVEL_VERT) == 0) { + //~ if ((e->v1->tflag1 & BME_BEVEL_BEVEL) && (e->v2->tflag1 & BME_BEVEL_BEVEL)) { + //~ e->tflag1 |= BME_BEVEL_BEVEL; + //~ } + //~ } + else if ((options & BME_BEVEL_SELECT) == 0 + && (options & BME_BEVEL_VERT) == 0) + { + weight = 1.0; + } + + if (weight > 0.0) { + e->tflag1 |= BME_BEVEL_BEVEL; + BME_bevel_add_vweight(td, bm, e->v1, weight, 1.0, options); + BME_bevel_add_vweight(td, bm, e->v2, weight, 1.0, options); + } + + if (len != 2 || options & BME_BEVEL_VERT) { + e->tflag1 &= ~BME_BEVEL_BEVEL; + } + } + + /* face pass */ + for (f=bm->polys.first; f; f=f->next) f->tflag1 = BME_BEVEL_ORIG; + + return bm; +} + +/* tags all elements as originals */ +BME_Mesh *BME_bevel_reinitialize(BME_Mesh *bm) { + BME_Vert *v; + BME_Edge *e; + BME_Poly *f; + + for (v = bm->verts.first; v; v=v->next) { + v->tflag1 |= BME_BEVEL_ORIG; + } + + for (e=bm->edges.first; e; e=e->next) { + e->tflag1 |= BME_BEVEL_ORIG; + } + + for (f=bm->polys.first; f; f=f->next) { + f->tflag1 |= BME_BEVEL_ORIG; + } + + return bm; +} + +/** + * BME_bevel_mesh + * + * Mesh beveling tool: + * + * Bevels an entire mesh. It currently uses the tflag1's of + * its vertices and edges to track topological changes. + * The parameter "value" is the distance to inset (should be negative). + * The parameter "options" is not currently used. + * + * Returns - + * A BME_Mesh pointer to the BMesh passed as a parameter. +*/ +BME_Mesh *BME_bevel_mesh(BME_Mesh *bm, float value, int res, int options, int defgrp_index, BME_TransData_Head *td) { + BME_Vert *v, *nv; + BME_Edge *e, *oe; + BME_Loop *l, *l2; + BME_Poly *f; + unsigned int i, len; + + for (f=bm->polys.first; f; f=f->next) { + if(f->tflag1 & BME_BEVEL_ORIG) { + BME_bevel_poly(bm,f,value,options,td); + } + } + + /* here we will loop through all the verts to clean up the left over geometry */ + /* crazy idea. when res == 0, don't remove the original geometry */ + for (v = bm->verts.first; v; /* we may kill v, so increment in-loop */) { + nv = v->next; + if ((v->tflag1 & BME_BEVEL_NONMAN) && (v->tflag1 & BME_BEVEL_BEVEL) && (v->tflag1 & BME_BEVEL_ORIG)) { + v = BME_bevel_wire(bm, v, value, res, options, td); + } + else if (res && ((v->tflag1 & BME_BEVEL_BEVEL) && (v->tflag1 & BME_BEVEL_ORIG))) { + /* first, make sure we're not sitting on an edge to be removed */ + oe = v->edge; + e = BME_disk_nextedge(oe,v); + while ((e->tflag1 & BME_BEVEL_BEVEL) && (e->tflag1 & BME_BEVEL_ORIG)) { + e = BME_disk_nextedge(e,v); + if (e == oe) { + printf("Something's wrong! We can't remove every edge here!\n"); + break; + } + } + /* look for original edges, and remove them */ + oe = e; + while (e = BME_disk_next_edgeflag(oe, v, 0, BME_BEVEL_ORIG | BME_BEVEL_BEVEL)) { + /* join the faces (we'll split them later) */ + f = BME_JFKE_safe(bm,e->loop->f,((BME_Loop*)e->loop->radial.next->data)->f,e); + if (!f) printf("Non-manifold geometry not getting tagged right?\n"); + } + + /* all original edges marked to be beveled have been removed; + * now we need to link up the edges for this "corner" */ + len = BME_cycle_length(BME_disk_getpointer(v->edge, v)); + for (i=0,e=v->edge; i < len; i++,e=BME_disk_nextedge(e,v)) { + l = e->loop; + l2 = l->radial.next->data; + if (l->v != v) l = l->next; + if (l2->v != v) l2 = l2->next; + /* look for faces that have had the original edges removed via JFKE */ + if (l->f->len > 3) { + BME_split_face(bm,l->f,l->next->v,l->prev->v,&l,l->e); /* clip this corner off */ + if (len > 2) { + l->e->tflag1 |= BME_BEVEL_BEVEL; + } + } + if (l2->f->len > 3) { + BME_split_face(bm,l2->f,l2->next->v,l2->prev->v,&l,l2->e); /* clip this corner off */ + if (len > 2) { + l->e->tflag1 |= BME_BEVEL_BEVEL; + } + } + } + + l = v->edge->loop; + if (len > 2) { + f = l->f; + while(f->len <= len) { + if (l->radial.next->data != l) { + e = l->e; + l = l->radial.next->data; + } + else { + e = NULL; + } + if (l->v == v) l = l->prev; + else l = l->next; + if (e) { + f = BME_JFKE_safe(bm,e->loop->f,((BME_Loop*)e->loop->radial.next->data)->f,e); + } + } + } + if (l->v == v) l = l->prev; + l = l->prev; + if (l->next->radial.next->data == l->next) { /* was part of the open end of a mesh */ + BME_JEKV(bm,l->next->e,v); + if (len == 2) { + f = BME_JFKE_safe(bm,l->f,((BME_Loop*)l->radial.next->data)->f,l->e); + } + } + else { + BME_JEKV(bm,l->next->e,v); + f = BME_JFKE_safe(bm,l->f,((BME_Loop*)l->next->radial.next->data)->f,l->next->e); + if (f->len == 2) { + f = BME_JFKE_safe(bm,f,((BME_Loop*)f->loopbase->radial.next->data)->f,f->loopbase->e); + } + } + } + v = nv; + } + return bm; +} + +BME_Mesh *BME_tesselate(BME_Mesh *bm) { + BME_Loop *l, *nextloop; + BME_Poly *f; + + for (f=bm->polys.first; f; f=f->next) { + l = f->loopbase; + while (l->f->len > 4) { + nextloop = l->next->next->next; + /* make a quad */ + BME_split_face(bm,l->f,l->v,nextloop->v,NULL,l->e); + l = nextloop; + } + } + return bm; +} + +/* options that can be passed: + * BME_BEVEL_VWEIGHT <---- v, Look at vertex weights; use defgrp_index if option is present + * BME_BEVEL_SELECT <---- v,e, check selection for verts and edges + * BME_BEVEL_ANGLE <---- v,e, don't bevel-tag verts - tag verts per edge + * BME_BEVEL_VERT <---- e, don't tag edges + * BME_BEVEL_EWEIGHT <---- e, use crease flag for now + * BME_BEVEL_PERCENT <---- Will need to think about this one; will probably need to incorporate into actual bevel routine + * BME_BEVEL_RADIUS <---- Will need to think about this one; will probably need to incorporate into actual bevel routine + * All weights/limits are stored per-vertex + */ +BME_Mesh *BME_bevel(BME_Mesh *bm, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd) { + BME_Vert *v; + BME_TransData_Head *td; + BME_TransData *vtd; + int i; + float fac=1, d; + + td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE); + + BME_bevel_initialize(bm, options, defgrp_index, angle, td); + + /* recursion math courtesy of Martin Poirier (theeth) */ + for (i=0; iverts.first; v; v=v->next) { + if (vtd = BME_get_transdata(td, v)) { + if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) { + d = *vtd->max; + } + else { + d = value; + } + VECADDFAC(v->co,vtd->org,vtd->vec,vtd->factor*d); + } + v->tflag1 = 0; + } + + BME_free_transdata(td); + return bm; +} diff --git a/source/blender/blenkernel/intern/bmesh_private.h b/source/blender/blenkernel/intern/bmesh_private.h new file mode 100644 index 00000000000..4c6460b3378 --- /dev/null +++ b/source/blender/blenkernel/intern/bmesh_private.h @@ -0,0 +1,71 @@ +/** + * BME_private.h jan 2007 + * + * low level, 'private' function prototypes for bmesh kernel. + * + * $Id: BKE_bmesh.h,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifndef BMESH_PRIVATE +#define BMESH_PRIVATE + +#include "BKE_bmesh.h" + +/*ALLOCATION/DEALLOCATION*/ +struct BME_Vert *BME_addvertlist(struct BME_Mesh *bm, struct BME_Vert *example); +struct BME_Edge *BME_addedgelist(struct BME_Mesh *bm, struct BME_Vert *v1, struct BME_Vert *v2, struct BME_Edge *example); +struct BME_Poly *BME_addpolylist(struct BME_Mesh *bm, struct BME_Poly *example); +struct BME_Loop *BME_create_loop(struct BME_Mesh *bm, struct BME_Vert *v, struct BME_Edge *e, struct BME_Poly *f, struct BME_Loop *example); + +void BME_free_vert(struct BME_Mesh *bm, struct BME_Vert *v); +void BME_free_edge(struct BME_Mesh *bm, struct BME_Edge *e); +void BME_free_poly(struct BME_Mesh *bm, struct BME_Poly *f); +void BME_free_loop(struct BME_Mesh *bm, struct BME_Loop *l); +void BME_delete_loop(struct BME_Mesh *bm, struct BME_Loop *l); + +/*DOUBLE CIRCULAR LINKED LIST FUNCTIONS*/ +void BME_cycle_append(void *h, void *nt); +int BME_cycle_remove(void *h, void *remn); +int BME_cycle_validate(int len, void *h); +/*DISK CYCLE MANAGMENT*/ +int BME_disk_append_edge(struct BME_Edge *e, struct BME_Vert *v); +void BME_disk_remove_edge(struct BME_Edge *e, struct BME_Vert *v); +/*RADIAL CYCLE MANAGMENT*/ +void BME_radial_append(struct BME_Edge *e, struct BME_Loop *l); +void BME_radial_remove_loop(struct BME_Loop *l, struct BME_Edge *e); + +/*MISC FUNCTIONS*/ +int BME_edge_swapverts(struct BME_Edge *e, struct BME_Vert *orig, struct BME_Vert *new); /*relink edge*/ +int BME_disk_hasedge(struct BME_Vert *v, struct BME_Edge *e); + +/*Error reporting. Shouldnt be called by tools ever.*/ +void BME_error(void); +#endif diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index ebfb96f001d..3dc2fef4f73 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -840,6 +840,7 @@ DerivedMesh *CDDM_from_editmesh(EditMesh *em, Mesh *me) mv->no[0] = eve->no[0] * 32767.0; mv->no[1] = eve->no[1] * 32767.0; mv->no[2] = eve->no[2] * 32767.0; + mv->bweight = (unsigned char) (eve->bweight * 255.0f); mv->mat_nr = 0; mv->flag = 0; @@ -857,6 +858,7 @@ DerivedMesh *CDDM_from_editmesh(EditMesh *em, Mesh *me) med->v1 = eed->v1->tmp.l; med->v2 = eed->v2->tmp.l; med->crease = (unsigned char) (eed->crease * 255.0f); + med->bweight = (unsigned char) (eed->bweight * 255.0f); med->flag = ME_EDGEDRAW|ME_EDGERENDER; if(eed->seam) med->flag |= ME_SEAM; diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 91892f59a78..6d57c26adc9 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -96,6 +96,7 @@ #include "BKE_pointcache.h" #include "BKE_utildefines.h" #include "depsgraph_private.h" +#include "BKE_bmesh.h" #include "LOD_DependKludge.h" #include "LOD_decimation.h" @@ -2677,6 +2678,92 @@ static DerivedMesh *edgesplitModifier_applyModifierEM( return edgesplitModifier_applyModifier(md, ob, derivedData, 0, 1); } +/* Bevel */ + +static void bevelModifier_initData(ModifierData *md) +{ + BevelModifierData *bmd = (BevelModifierData*) md; + + bmd->value = 0.1f; + bmd->res = 1; + bmd->flags = 0; + bmd->val_flags = 0; + bmd->lim_flags = 0; + bmd->e_flags = 0; + bmd->bevel_angle = 30; + bmd->defgrp_name[0] = '\0'; +} + +static void bevelModifier_copyData(ModifierData *md, ModifierData *target) +{ + BevelModifierData *bmd = (BevelModifierData*) md; + BevelModifierData *tbmd = (BevelModifierData*) target; + + tbmd->value = bmd->value; + tbmd->res = bmd->res; + tbmd->flags = bmd->flags; + tbmd->val_flags = bmd->val_flags; + tbmd->lim_flags = bmd->lim_flags; + tbmd->e_flags = bmd->e_flags; + tbmd->bevel_angle = bmd->bevel_angle; + strncpy(tbmd->defgrp_name, bmd->defgrp_name, 32); +} + +CustomDataMask bevelModifier_requiredDataMask(ModifierData *md) +{ + BevelModifierData *bmd = (BevelModifierData *)md; + CustomDataMask dataMask = 0; + + /* ask for vertexgroups if we need them */ + if(bmd->defgrp_name[0]) dataMask |= (1 << CD_MDEFORMVERT); + + return dataMask; +} + +static DerivedMesh *bevelModifier_applyModifier( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) +{ + DerivedMesh *result; + BME_Mesh *bm; + bDeformGroup *def; + int i, options, defgrp_index = -1; + BevelModifierData *bmd = (BevelModifierData*) md; + + options = bmd->flags|bmd->val_flags|bmd->lim_flags|bmd->e_flags; + + //~ if ((options & BME_BEVEL_VWEIGHT) && bmd->defgrp_name[0]) { + //~ for (i = 0, def = ob->defbase.first; def; def = def->next, i++) { + //~ if (!strcmp(def->name, bmd->defgrp_name)) { + //~ defgrp_index = i; + //~ break; + //~ } + //~ } + //~ if (defgrp_index < 0) { + //~ options &= ~BME_BEVEL_VWEIGHT; + //~ } + //~ } + + bm = BME_make_mesh(); + bm = BME_derivedmesh_to_bmesh(derivedData, bm); + BME_model_begin(bm); + BME_bevel(bm,bmd->value,bmd->res,options,defgrp_index,bmd->bevel_angle,NULL); + BME_model_end(bm); + result = BME_bmesh_to_derivedmesh(bm,derivedData); + BME_free_mesh(bm); + + CDDM_calc_normals(result); + + return result; +} + +static DerivedMesh *bevelModifier_applyModifierEM( + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) +{ + return bevelModifier_applyModifier(md, ob, derivedData, 0, 1); +} + /* Displace */ static void displaceModifier_initData(ModifierData *md) @@ -6952,6 +7039,18 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti->applyModifier = edgesplitModifier_applyModifier; mti->applyModifierEM = edgesplitModifier_applyModifierEM; + mti = INIT_TYPE(Bevel); + mti->type = eModifierTypeType_Constructive; + mti->flags = eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; + mti->initData = bevelModifier_initData; + mti->copyData = bevelModifier_copyData; + mti->requiredDataMask = bevelModifier_requiredDataMask; + mti->applyModifier = bevelModifier_applyModifier; + mti->applyModifierEM = bevelModifier_applyModifierEM; + mti = INIT_TYPE(Displace); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsMesh|eModifierTypeFlag_SupportsEditmode; diff --git a/source/blender/blenlib/BLI_editVert.h b/source/blender/blenlib/BLI_editVert.h index 795f6781894..f9b73d521c9 100644 --- a/source/blender/blenlib/BLI_editVert.h +++ b/source/blender/blenlib/BLI_editVert.h @@ -67,6 +67,7 @@ typedef struct EditVert h for hidden. if (!eve->h) {... f1 and f2 can be used for temp data, clear them first*/ unsigned char f, h, f1, f2; + float bweight; short fast; /* only 0 or 1, for editmesh_fastmalloc, do not store temp data here! */ int hash; int keyindex; /* original index #, for restoring key information */ @@ -103,6 +104,7 @@ typedef struct EditEdge short f1, f2; /* short, f1 is (ab)used in subdiv */ unsigned char f, h, dir, seam, sharp; float crease; + float bweight; short fast; /* only 0 or 1, for editmesh_fastmalloc */ short fgoni; /* index for fgon, for search */ HashEdge hash; diff --git a/source/blender/include/BIF_transform.h b/source/blender/include/BIF_transform.h index a0f991f2631..deebe7c27cb 100644 --- a/source/blender/include/BIF_transform.h +++ b/source/blender/include/BIF_transform.h @@ -60,6 +60,8 @@ #define TFM_TIME_SCALE 21 #define TFM_TIME_EXTEND 22 #define TFM_BAKE_TIME 23 +#define TFM_BEVEL 24 +#define TFM_BWEIGHT 25 /* TRANSFORM CONTEXTS */ #define CTX_NONE 0 @@ -69,6 +71,7 @@ #define CTX_TWEAK 8 #define CTX_NO_MIRROR 16 #define CTX_AUTOCONFIRM 32 +#define CTX_BMESH 64 void initTransform(int mode, int context); void Transform(void); diff --git a/source/blender/include/butspace.h b/source/blender/include/butspace.h index 5a276665240..a2339f3f1a6 100644 --- a/source/blender/include/butspace.h +++ b/source/blender/include/butspace.h @@ -438,6 +438,7 @@ void curvemap_buttons(struct uiBlock *block, struct CurveMapping *cumap, char la #define B_JOINTRIA 2081 #define B_SETTFACE_RND 2082 #define B_SETMCOL_RND 2083 +#define B_DRAWBWEIGHTS 2084 #define B_GEN_SKELETON 2090 diff --git a/source/blender/include/transform.h b/source/blender/include/transform.h index 7ac417fda7c..694cfece989 100644 --- a/source/blender/include/transform.h +++ b/source/blender/include/transform.h @@ -339,6 +339,13 @@ int Trackball(TransInfo *t, short mval[2]); void initPushPull(TransInfo *t); int PushPull(TransInfo *t, short mval[2]); +void initBevel(TransInfo *t); +int handleEventBevel(TransInfo *t, unsigned short evenl, short val); +int Bevel(TransInfo *t, short mval[2]); + +void initBevelWeight(TransInfo *t); +int BevelWeight(TransInfo *t, short mval[2]); + void initCrease(TransInfo *t); int Crease(TransInfo *t, short mval[2]); diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index afb1dfb108c..7970ccd073c 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -45,7 +45,7 @@ typedef struct MFace { typedef struct MEdge { unsigned int v1, v2; - char crease, pad; + char crease, bweight; short flag; } MEdge; @@ -63,7 +63,7 @@ typedef struct MDeformVert { typedef struct MVert { float co[3]; short no[3]; - char flag, mat_nr; + char flag, mat_nr, bweight, pad[3]; } MVert; /* at the moment alpha is abused for vertex painting diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 1c70508509b..dd1d8eb01b3 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -33,7 +33,8 @@ typedef enum ModifierType { eModifierType_ParticleInstance, eModifierType_Explode, eModifierType_Cloth, - eModifierType_Collision, + eModifierType_Collision, + eModifierType_Bevel, NUM_MODIFIER_TYPES } ModifierType; @@ -187,6 +188,27 @@ typedef struct EdgeSplitModifierData { #define MOD_EDGESPLIT_FROMANGLE 1<<1 #define MOD_EDGESPLIT_FROMFLAG 1<<2 +typedef struct BevelModifierData { + ModifierData modifier; + + float value; /* the "raw" bevel value (distance/amount to bevel) */ + int res; /* the resolution (as originally coded, it is the number of recursive bevels) */ + int pad; + short flags; /* general option flags */ + short val_flags; /* flags used to interpret the bevel value */ + short lim_flags; /* flags to tell the tool how to limit the bevel */ + short e_flags; /* flags to direct how edge weights are applied to verts */ + float bevel_angle; /* if the BME_BEVEL_ANGLE is set, this will be how "sharp" an edge must be before it gets beveled */ + char defgrp_name[32]; /* if the BME_BEVEL_VWEIGHT option is set, this will be the name of the vert group */ +} BevelModifierData; + +typedef struct BMeshModifierData { + ModifierData modifier; + + float pad; + int type; +} BMeshModifierData; + typedef struct DisplaceModifierData { ModifierData modifier; diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c index e35cc6a04a9..be6b872774e 100644 --- a/source/blender/src/buttons_editing.c +++ b/source/blender/src/buttons_editing.c @@ -96,6 +96,7 @@ #include "BKE_packedFile.h" #include "BKE_particle.h" #include "BKE_scene.h" +#include "BKE_bmesh.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" @@ -1752,6 +1753,10 @@ static void draw_modifier(uiBlock *block, Object *ob, ModifierData *md, int *xco height = 86; } else if (md->type==eModifierType_Mirror) { height = 86; + } else if (md->type==eModifierType_Bevel) { + BevelModifierData *bmd = (BevelModifierData*) md; + height = 105; /* height = 124; */ + if ((bmd->lim_flags & BME_BEVEL_ANGLE) || ((bmd->lim_flags & BME_BEVEL_WEIGHT) && !(bmd->flags & BME_BEVEL_VERT))) height += 19; } else if (md->type==eModifierType_EdgeSplit) { EdgeSplitModifierData *emd = (EdgeSplitModifierData*) md; height = 48; @@ -1895,7 +1900,62 @@ static void draw_modifier(uiBlock *block, Object *ob, ModifierData *md, int *xco "Ob: ", lx, (cy -= 19), buttonWidth, 19, &mmd->mirror_ob, "Object to use as mirror"); - + } else if (md->type==eModifierType_Bevel) { + BevelModifierData *bmd = (BevelModifierData*) md; + /*uiDefButS(block, ROW, B_MODIFIER_RECALC, "Distance", + lx, (cy -= 19), (buttonWidth/2), 19, &bmd->val_flags, + 11.0, 0, 0, 0, + "Interpret bevel value as a constant distance from each edge"); + uiDefButS(block, ROW, B_MODIFIER_RECALC, "Radius", + (lx+buttonWidth/2), cy, (buttonWidth - buttonWidth/2), 19, &bmd->val_flags, + 11.0, BME_BEVEL_RADIUS, 0, 0, + "Interpret bevel value as a radius - smaller angles will be beveled more");*/ + uiDefButF(block, NUM, B_MODIFIER_RECALC, "Dis", + lx, (cy -= 19), buttonWidth, 19, &bmd->value, + 0.0, 0.5, 5, 2, + "Bevel value/amount"); + /*uiDefButI(block, NUM, B_MODIFIER_RECALC, "Recurs", + lx, (cy -= 19), buttonWidth, 19, &bmd->res, + 1, 4, 5, 2, + "Number of times to bevel");*/ + uiDefButBitS(block, TOG, BME_BEVEL_VERT, + B_MODIFIER_RECALC, "Verts only", + lx, (cy -= 19), buttonWidth, 19, + &bmd->flags, 0, 0, 0, 0, + "Bevel only verts/corners; not edges"); + uiDefBut(block, LABEL, 1, "Limit using:", lx, (cy-=19), buttonWidth,19, NULL, 0.0, 0.0, 0, 0, ""); + uiDefButS(block, ROW, B_MODIFIER_RECALC, "None", + lx, (cy -= 19), (buttonWidth/3), 19, &bmd->lim_flags, + 12.0, 0, 0, 0, + "Bevel the entire mesh by a constant amount"); + uiDefButS(block, ROW, B_MODIFIER_RECALC, "Angle", + (lx+buttonWidth/3), cy, (buttonWidth/3), 19, &bmd->lim_flags, + 12.0, BME_BEVEL_ANGLE, 0, 0, + "Only bevel edges with sharp enough angles between faces"); + uiDefButS(block, ROW, B_MODIFIER_RECALC, "BevWeight", + lx+(2*buttonWidth/3), cy, buttonWidth-2*(buttonWidth/3), 19, &bmd->lim_flags, + 12.0, BME_BEVEL_WEIGHT, 0, 0, + "Use bevel weights to determine how much bevel is applied; apply them separately in vert/edge select mode"); + if ((bmd->lim_flags & BME_BEVEL_WEIGHT) && !(bmd->flags & BME_BEVEL_VERT)) { + uiDefButS(block, ROW, B_MODIFIER_RECALC, "Min", + lx, (cy -= 19), (buttonWidth/3), 19, &bmd->e_flags, + 13.0, BME_BEVEL_EMIN, 0, 0, + "The sharpest edge's weight is used when weighting a vert"); + uiDefButS(block, ROW, B_MODIFIER_RECALC, "Average", + (lx+buttonWidth/3), cy, (buttonWidth/3), 19, &bmd->e_flags, + 13.0, 0, 0, 0, + "The edge weights are averaged when weighting a vert"); + uiDefButS(block, ROW, B_MODIFIER_RECALC, "Max", + (lx+2*(buttonWidth/3)), cy, buttonWidth-2*(buttonWidth/3), 19, &bmd->e_flags, + 13.0, BME_BEVEL_EMAX, 0, 0, + "The largest edge's wieght is used when weighting a vert"); + } + else if (bmd->lim_flags & BME_BEVEL_ANGLE) { + uiDefButF(block, NUM, B_MODIFIER_RECALC, "Angle:", + lx, (cy -= 19), buttonWidth, 19, &bmd->bevel_angle, + 0.0, 180.0, 100, 2, + "Angle above which to bevel edges"); + } } else if (md->type==eModifierType_EdgeSplit) { EdgeSplitModifierData *emd = (EdgeSplitModifierData*) md; uiDefButBitI(block, TOG, MOD_EDGESPLIT_FROMANGLE, @@ -4788,6 +4848,10 @@ void do_meshbuts(unsigned short event) allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWVIEW3D, 0); break; + //~ case B_DRAWBWEIGHTS: + //~ allqueue(REDRAWBUTSEDIT, 0); + //~ allqueue(REDRAWVIEW3D, 0); + //~ break; case B_JOINTRIA: join_triangles(); break; @@ -4966,10 +5030,11 @@ static void editing_panel_mesh_tools1(Object *ob, Mesh *me) uiBlockBeginAlign(block); uiDefButBitI(block, TOG, G_DRAWFACES, REDRAWVIEW3D_IMAGE, "Draw Faces", 955,88,150,19, &G.f, 0, 0, 0, 0, "Displays all faces as shades in the 3d view and UV editor"); - uiDefButBitI(block, TOG, G_DRAWEDGES, REDRAWVIEW3D, "Draw Edges", 955,66,150,19, &G.f, 0, 0, 0, 0, "Displays selected edges using hilights"); - uiDefButBitI(block, TOG, G_DRAWCREASES, REDRAWVIEW3D, "Draw Creases", 955,44,150,19, &G.f, 0, 0, 0, 0, "Displays creases created for subsurf weighting"); - uiDefButBitI(block, TOG, G_DRAWSEAMS, REDRAWVIEW3D, "Draw Seams", 955,22,150,19, &G.f, 0, 0, 0, 0, "Displays UV unwrapping seams"); - uiDefButBitI(block, TOG, G_DRAWSHARP, REDRAWVIEW3D, "Draw Sharp", 955,0,150,19, &G.f, 0, 0, 0, 0, "Displays sharp edges, used with the EdgeSplit modifier"); + uiDefButBitI(block, TOG, G_DRAWEDGES, REDRAWVIEW3D, "Draw Edges", 955, 66,150,19, &G.f, 0, 0, 0, 0, "Displays selected edges using hilights"); + uiDefButBitI(block, TOG, G_DRAWCREASES, REDRAWVIEW3D, "Draw Creases", 955, 42,150,19, &G.f, 0, 0, 0, 0, "Displays creases created for subsurf weighting"); + uiDefButBitI(block, TOG, G_DRAWBWEIGHTS, REDRAWVIEW3D, "Draw Bevel Weights", 955, 20,150,19, &G.f, 0, 0, 0, 0, "Displays weights created for the Bevel modifier"); + uiDefButBitI(block, TOG, G_DRAWSEAMS, REDRAWVIEW3D, "Draw Seams", 955, -2,150,19, &G.f, 0, 0, 0, 0, "Displays UV unwrapping seams"); + uiDefButBitI(block, TOG, G_DRAWSHARP, REDRAWVIEW3D, "Draw Sharp", 955, -24,150,19, &G.f, 0, 0, 0, 0, "Displays sharp edges, used with the EdgeSplit modifier"); uiBlockEndAlign(block); /* Measurement drawing options */ diff --git a/source/blender/src/drawobject.c b/source/blender/src/drawobject.c index 6edb89a1049..6375af4d49e 100644 --- a/source/blender/src/drawobject.c +++ b/source/blender/src/drawobject.c @@ -1660,6 +1660,41 @@ static void draw_dm_creases(DerivedMesh *dm) glLineWidth(1.0); } +static int draw_dm_bweights__setDrawOptions(void *userData, int index) +{ + EditEdge *eed = EM_get_edge_for_index(index); + + if (eed->h==0 && eed->bweight!=0.0) { + BIF_ThemeColorBlend(TH_WIRE, TH_EDGE_SELECT, eed->bweight); + return 1; + } else { + return 0; + } +} +static void draw_dm_bweights__mapFunc(void *userData, int index, float *co, float *no_f, short *no_s) +{ + EditVert *eve = EM_get_vert_for_index(index); + + if (eve->h==0 && eve->bweight!=0.0) { + BIF_ThemeColorBlend(TH_VERTEX, TH_VERTEX_SELECT, eve->bweight); + bglVertex3fv(co); + } +} +static void draw_dm_bweights(DerivedMesh *dm) +{ + if (G.scene->selectmode & SCE_SELECT_VERTEX) { + glPointSize(BIF_GetThemeValuef(TH_VERTEX_SIZE) + 2); + bglBegin(GL_POINTS); + dm->foreachMappedVert(dm, draw_dm_bweights__mapFunc, NULL); + bglEnd(); + } + else { + glLineWidth(3.0); + dm->drawMappedEdges(dm, draw_dm_bweights__setDrawOptions, NULL); + glLineWidth(1.0); + } +} + /* Second section of routines: Combine first sets to form fancy * drawing routines (for example rendering twice to get overlays). * @@ -2139,6 +2174,9 @@ static void draw_em_fancy(Object *ob, EditMesh *em, DerivedMesh *cageDM, Derived if(G.f & G_DRAWCREASES) { draw_dm_creases(cageDM); } + if(G.f & G_DRAWBWEIGHTS) { + draw_dm_bweights(cageDM); + } draw_em_fancy_edges(cageDM, 0, eed_act); } diff --git a/source/blender/src/editmesh.c b/source/blender/src/editmesh.c index de4bcda157f..acb4134a040 100644 --- a/source/blender/src/editmesh.c +++ b/source/blender/src/editmesh.c @@ -163,10 +163,13 @@ EditVert *addvertlist(float *vec, EditVert *example) createVerseVert(eve); #endif - if(example) + if(example) { CustomData_em_copy_data(&em->vdata, &em->vdata, example->data, &eve->data); - else + eve->bweight = example->bweight; + } + else { CustomData_em_set_default(&em->vdata, &eve->data); + } return eve; } @@ -299,6 +302,7 @@ EditEdge *addedgelist(EditVert *v1, EditVert *v2, EditEdge *example) rule is to do this with addedgelist call, before addfacelist */ if(example) { eed->crease= example->crease; + eed->bweight= example->bweight; eed->sharp = example->sharp; eed->seam = example->seam; eed->h |= (example->h & EM_FGON); @@ -896,6 +900,8 @@ void make_editMesh() eve->no[1]= mvert->no[1]/32767.0; eve->no[2]= mvert->no[2]/32767.0; + eve->bweight= ((float)mvert->bweight)/255.0f; + /* lets overwrite the keyindex of the editvert * with the order it used to be in before * editmode @@ -915,7 +921,8 @@ void make_editMesh() eed= addedgelist(evlist[medge->v1], evlist[medge->v2], NULL); /* eed can be zero when v1 and v2 are identical, dxf import does this... */ if(eed) { - eed->crease= ((float)medge->crease)/255.0; + eed->crease= ((float)medge->crease)/255.0f; + eed->bweight= ((float)medge->bweight)/255.0f; if(medge->flag & ME_SEAM) eed->seam= 1; if(medge->flag & ME_SHARP) eed->sharp = 1; @@ -1153,7 +1160,8 @@ void load_editMesh(void) mvert->flag= 0; if(eve->f1==1) mvert->flag |= ME_SPHERETEST; mvert->flag |= (eve->f & SELECT); - if (eve->h) mvert->flag |= ME_HIDE; + if (eve->h) mvert->flag |= ME_HIDE; + mvert->bweight= (char)(255.0*eve->bweight); #ifdef WITH_VERSE if(eve->vvert) { @@ -1205,6 +1213,7 @@ void load_editMesh(void) if(eed->h & 1) medge->flag |= ME_HIDE; medge->crease= (char)(255.0*eed->crease); + medge->bweight= (char)(255.0*eed->bweight); CustomData_from_em_block(&em->edata, &me->edata, eed->data, a); eed->tmp.l = a++; @@ -1926,6 +1935,7 @@ typedef struct EditVertC float no[3]; float co[3]; unsigned char f, h; + short bweight; int keyindex; } EditVertC; @@ -1933,7 +1943,7 @@ typedef struct EditEdgeC { int v1, v2; unsigned char f, h, seam, sharp, pad; - short crease, fgoni; + short crease, bweight, fgoni; } EditEdgeC; typedef struct EditFaceC @@ -2034,6 +2044,7 @@ static void *editMesh_to_undoMesh(void) evec->h= eve->h; evec->keyindex= eve->keyindex; eve->tmp.l = a; /*store index*/ + evec->bweight= (short)(eve->bweight*255.0); CustomData_from_em_block(&em->vdata, &um->vdata, eve->data, a); } @@ -2048,6 +2059,7 @@ static void *editMesh_to_undoMesh(void) eedc->seam= eed->seam; eedc->sharp= eed->sharp; eedc->crease= (short)(eed->crease*255.0); + eedc->bweight= (short)(eed->bweight*255.0); eedc->fgoni= eed->fgoni; eed->tmp.l = a; /*store index*/ CustomData_from_em_block(&em->edata, &um->edata, eed->data, a); @@ -2161,6 +2173,7 @@ static void undoMesh_to_editMesh(void *umv) eve->f= evec->f; eve->h= evec->h; eve->keyindex= evec->keyindex; + eve->bweight= ((float)evec->bweight)/255.0f; CustomData_to_em_block(&um->vdata, &em->vdata, a, &eve->data); } @@ -2174,7 +2187,8 @@ static void undoMesh_to_editMesh(void *umv) eed->seam= eedc->seam; eed->sharp= eedc->sharp; eed->fgoni= eedc->fgoni; - eed->crease= ((float)eedc->crease)/255.0; + eed->crease= ((float)eedc->crease)/255.0f; + eed->bweight= ((float)eedc->bweight)/255.0f; CustomData_to_em_block(&um->edata, &em->edata, a, &eed->data); } diff --git a/source/blender/src/editmesh_tools.c b/source/blender/src/editmesh_tools.c index 14b8fe39d45..a20327dcfac 100644 --- a/source/blender/src/editmesh_tools.c +++ b/source/blender/src/editmesh_tools.c @@ -73,6 +73,7 @@ editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise #include "BKE_mesh.h" #include "BKE_object.h" #include "BKE_utildefines.h" +#include "BKE_bmesh.h" #ifdef WITH_VERSE #include "BKE_verse.h" @@ -90,6 +91,7 @@ editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise #include "BIF_resources.h" #include "BIF_toolbox.h" #include "BIF_transform.h" +#include "transform.h" #ifdef WITH_VERSE #include "BIF_verse.h" @@ -3707,6 +3709,7 @@ static void edge_rotate(EditEdge *eed,int dir) srchedge->dir = eed->dir; srchedge->seam = eed->seam; srchedge->crease = eed->crease; + srchedge->bweight = eed->bweight; } } @@ -4476,7 +4479,52 @@ static void bevel_mesh_recurs(float bsize, short recurs, int allfaces) } } -void bevel_menu() +void bevel_menu() { + BME_Mesh *bm; + BME_TransData_Head *td; + TransInfo *t; + int options, res, gbm_free = 0; + + t = BIF_GetTransInfo(); + if (!G.editBMesh) { + G.editBMesh = MEM_callocN(sizeof(*(G.editBMesh)),"bevel_menu() G.editBMesh"); + gbm_free = 1; + } + + G.editBMesh->options = BME_BEVEL_RUNNING | BME_BEVEL_SELECT; + G.editBMesh->res = 1; + + while(G.editBMesh->options & BME_BEVEL_RUNNING) { + options = G.editBMesh->options; + res = G.editBMesh->res; + bm = BME_make_mesh(); + bm = BME_editmesh_to_bmesh(G.editMesh, bm); + BIF_undo_push("Pre-Bevel"); + free_editMesh(G.editMesh); + BME_bevel(bm,0.1f,res,options,0,0,&td); + BME_bmesh_to_editmesh(bm, td); + G.editBMesh->bm = bm; + G.editBMesh->td = td; + initTransform(TFM_BEVEL,CTX_BMESH); + Transform(); + BME_free_transdata(td); + BME_free_mesh(bm); + if (t->state != TRANS_CONFIRM) { + BIF_undo(); + } + if (options == G.editBMesh->options) { + G.editBMesh->options &= ~BME_BEVEL_RUNNING; + } + } + + if (gbm_free) { + MEM_freeN(G.editBMesh); + G.editBMesh = NULL; + } +} + + +void bevel_menu_old() { char Finished = 0, Canceled = 0, str[100], Recalc = 0; short mval[2], oval[2], curval[2], event = 0, recurs = 1, nr; diff --git a/source/blender/src/header_view3d.c b/source/blender/src/header_view3d.c index 34169b428cd..7f9a2d02d83 100644 --- a/source/blender/src/header_view3d.c +++ b/source/blender/src/header_view3d.c @@ -2688,7 +2688,7 @@ void do_view3d_edit_mesh_edgesmenu(void *arg, int event) case 8: /* Clear Seam */ editmesh_mark_seam(1); break; - case 9: /* Cease SubSurf */ + case 9: /* Crease SubSurf */ if(!multires_level1_test()) { initTransform(TFM_CREASE, CTX_EDGE); Transform(); @@ -2720,6 +2720,12 @@ void do_view3d_edit_mesh_edgesmenu(void *arg, int event) BIF_undo_push("Clear Sharp"); DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); break; + case 17: /* Adjust Bevel Weight */ + if(!multires_level1_test()) { + initTransform(TFM_BWEIGHT, CTX_EDGE); + Transform(); + } + break; } allqueue(REDRAWVIEW3D, 0); } @@ -2756,6 +2762,8 @@ static uiBlock *view3d_edit_mesh_edgesmenu(void *arg_unused) uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Mark Sharp|Ctrl E", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 15, ""); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Clear Sharp|Ctrl E", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 16, ""); + uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Adjust Bevel Weight|Ctrl Shift E", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 17, ""); uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Crease SubSurf|Shift E", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 9, ""); diff --git a/source/blender/src/space.c b/source/blender/src/space.c index 1994a2fcb7f..6dacf38c544 100644 --- a/source/blender/src/space.c +++ b/source/blender/src/space.c @@ -1896,6 +1896,18 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) extrude_armature(1); } } + else if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) { + if (G.obedit && G.obedit->type==OB_MESH && + !multires_level1_test()) { + if (G.scene->selectmode & SCE_SELECT_VERTEX) { + initTransform(TFM_BWEIGHT, CTX_NONE); + } + else { + initTransform(TFM_BWEIGHT, CTX_EDGE); + } + Transform(); + } + } break; case FKEY: if(G.obedit) { diff --git a/source/blender/src/transform.c b/source/blender/src/transform.c index 87ca52dcfad..72d4c418b80 100644 --- a/source/blender/src/transform.c +++ b/source/blender/src/transform.c @@ -84,6 +84,7 @@ #include "BKE_utildefines.h" #include "BKE_bad_level_calls.h"/* popmenu and error */ #include "BKE_particle.h" +#include "BKE_bmesh.h" #include "BSE_drawipo.h" #include "BSE_editnla_types.h" /* for NLAWIDTH */ @@ -576,6 +577,10 @@ static char *transform_to_undostr(TransInfo *t) return "Trackball"; case TFM_PUSHPULL: return "Push/Pull"; + case TFM_BEVEL: + return "Bevel"; + case TFM_BWEIGHT: + return "Bevel Weight"; case TFM_CREASE: return "Crease"; case TFM_BONESIZE: @@ -1030,6 +1035,12 @@ void initTransform(int mode, int context) { case TFM_MIRROR: initMirror(&Trans); break; + case TFM_BEVEL: + initBevel(&Trans); + break; + case TFM_BWEIGHT: + initBevelWeight(&Trans); + break; } } @@ -3232,6 +3243,201 @@ int PushPull(TransInfo *t, short mval[2]) return 1; } +/* ************************** BEVEL **************************** */ + +void initBevel(TransInfo *t) +{ + t->mode = TFM_BEVEL; + t->flag |= T_NO_CONSTRAINT; + t->transform = Bevel; + t->handleEvent = handleEventBevel; + if (G.editBMesh->imval[0] == 0 && G.editBMesh->imval[1] == 0) { + /* save the initial mouse co */ + G.editBMesh->imval[0] = t->imval[0]; + G.editBMesh->imval[1] = t->imval[1]; + } + else { + /* restore the mouse co from a previous call to initTransform() */ + t->imval[0] = G.editBMesh->imval[0]; + t->imval[1] = G.editBMesh->imval[1]; + } +} + +int handleEventBevel(TransInfo *t, unsigned short event, short val) +{ + if (val) { + switch (event) { + case MIDDLEMOUSE: + G.editBMesh->options ^= BME_BEVEL_VERT; + t->state = TRANS_CANCEL; + return 1; + //case PADPLUSKEY: + // G.editBMesh->options ^= BME_BEVEL_RES; + // G.editBMesh->res += 1; + // if (G.editBMesh->res > 4) { + // G.editBMesh->res = 4; + // } + // t->state = TRANS_CANCEL; + // return 1; + //case PADMINUS: + // G.editBMesh->options ^= BME_BEVEL_RES; + // G.editBMesh->res -= 1; + // if (G.editBMesh->res < 0) { + // G.editBMesh->res = 0; + // } + // t->state = TRANS_CANCEL; + // return 1; + default: + return 0; + } + } + return 0; +} + +int Bevel(TransInfo *t, short mval[2]) +{ + float distance,d; + int i; + char str[128]; + char *mode; + TransData *td = t->data; + + mode = (G.editBMesh->options & BME_BEVEL_VERT) ? "verts only" : "normal"; + distance = InputHorizontalAbsolute(t, mval)/4; /* 4 just seemed a nice value to me, nothing special */ + + applyNumInput(&t->num, &distance); + + /* header print for NumInput */ + if (hasNumInput(&t->num)) { + char c[20]; + + outputNumInput(&(t->num), c); + + sprintf(str, "Bevel: %s", c); + } + else { + /* default header print */ + sprintf(str, "Bevel - Dist: %.4f, Mode: %s (MMB to toggle))", distance, mode); + } + + if (distance < 0) distance = -distance; + for(i = 0 ; i < t->total; i++, td++) { + if (td->axismtx[1][0] > 0 && distance > td->axismtx[1][0]) { + d = td->axismtx[1][0]; + } + else { + d = distance; + } + VECADDFAC(td->loc,td->center,td->axismtx[0],(*td->val)*d); + } + + recalcData(t); + + headerprint(str); + + viewRedrawForce(t); + + return 1; +} + +/* ************************** BEVEL WEIGHT *************************** */ + +void initBevelWeight(TransInfo *t) +{ + t->mode = TFM_BWEIGHT; + t->transform = BevelWeight; + + t->idx_max = 0; + t->num.idx_max = 0; + t->snap[0] = 0.0f; + t->snap[1] = 0.1f; + t->snap[2] = t->snap[1] * 0.1f; + + t->flag |= T_NO_CONSTRAINT; + + t->fac = (float)sqrt( + ( + ((float)(t->center2d[1] - t->imval[1]))*((float)(t->center2d[1] - t->imval[1])) + + + ((float)(t->center2d[0] - t->imval[0]))*((float)(t->center2d[0] - t->imval[0])) + ) ); + + if(t->fac==0.0f) t->fac= 1.0f; // prevent Inf +} + +int BevelWeight(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + float weight; + int i; + char str[50]; + + + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + float dx= (float)(t->center2d[0] - t->shiftmval[0]); + float dy= (float)(t->center2d[1] - t->shiftmval[1]); + weight = (float)sqrt( dx*dx + dy*dy)/t->fac; + + dx= (float)(t->center2d[0] - mval[0]); + dy= (float)(t->center2d[1] - mval[1]); + weight+= 0.1f*(float)(sqrt( dx*dx + dy*dy)/t->fac -weight); + + } + else { + float dx= (float)(t->center2d[0] - mval[0]); + float dy= (float)(t->center2d[1] - mval[1]); + weight = (float)sqrt( dx*dx + dy*dy)/t->fac; + } + + weight -= 1.0f; + if (weight > 1.0f) weight = 1.0f; + + snapGrid(t, &weight); + + applyNumInput(&t->num, &weight); + + /* header print for NumInput */ + if (hasNumInput(&t->num)) { + char c[20]; + + outputNumInput(&(t->num), c); + + if (weight >= 0.0f) + sprintf(str, "Bevel Weight: +%s %s", c, t->proptext); + else + sprintf(str, "Bevel Weight: %s %s", c, t->proptext); + } + else { + /* default header print */ + if (weight >= 0.0f) + sprintf(str, "Bevel Weight: +%.3f %s", weight, t->proptext); + else + sprintf(str, "Bevel Weight: %.3f %s", weight, t->proptext); + } + + for(i = 0 ; i < t->total; i++, td++) { + if (td->flag & TD_NOACTION) + break; + + if (td->val) { + *td->val = td->ival + weight * td->factor; + if (*td->val < 0.0f) *td->val = 0.0f; + if (*td->val > 1.0f) *td->val = 1.0f; + } + } + + recalcData(t); + + headerprint(str); + + viewRedrawForce(t); + + helpline (t, t->center); + + return 1; +} + /* ************************** CREASE *************************** */ void initCrease(TransInfo *t) -- cgit v1.2.3