diff options
Diffstat (limited to 'source/blender/modifiers/intern/MOD_edgesplit.c')
-rw-r--r-- | source/blender/modifiers/intern/MOD_edgesplit.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c new file mode 100644 index 00000000000..9d622669c78 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -0,0 +1,461 @@ +/* +* $Id$ +* +* ***** BEGIN GPL 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* The Original Code is Copyright (C) 2005 by the Blender Foundation. +* All rights reserved. +* +* Contributor(s): Joseph Eagar +* +* ***** END GPL LICENSE BLOCK ***** +* +*/ + +/* EdgeSplit modifier: Splits edges in the mesh according to sharpness flag + * or edge angle (can be used to achieve autosmoothing) */ + +#include "DNA_meshdata_types.h" + +#include "BLI_listbase.h" +#include "BLI_memarena.h" +#include "BLI_edgehash.h" +#include "BLI_math.h" +#include "BLI_array.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_modifier.h" +#include "BKE_particle.h" +#include "BKE_tessmesh.h" + +#include "MEM_guardedalloc.h" + +/* EdgeSplit */ +/* EdgeSplit modifier: Splits edges in the mesh according to sharpness flag + * or edge angle (can be used to achieve autosmoothing) +*/ + +/*new cddm-based edge split code*/ +typedef struct VertUser { + int ov, v, done; + ListBase users; +} VertUser; + +typedef struct EdgeNode { + struct EdgeNode *next, *prev; + struct EdgeData *edge; +} EdgeNode; + +typedef struct EdgeData { + EdgeNode v1node, v2node; + VertUser *v1user, *v2user; + float fno[3]; /*used to calculate face angles*/ + int has_fno; + int tag; + int v1, v2; + int used; +} EdgeData; + +typedef struct MemBase { + BLI_mempool *vertuserpool; +} MemBase; + +BM_INLINE EdgeData *edge_get_next(EdgeData *e, int ov) { + if (ov == e->v1) + return e->v1node.next ? e->v1node.next->edge : NULL; + else return e->v2node.next ? e->v2node.next->edge : NULL; +} + +BM_INLINE EdgeNode *edge_get_node(EdgeData *e, int ov) +{ + if (ov == e->v1) + return &e->v1node; + else return &e->v2node; + return NULL; +} + +BM_INLINE VertUser *edge_get_vuser(MemBase *b, EdgeData *edge, int ov) +{ + if (ov == edge->v1) + return edge->v1user; + else if (ov == edge->v2) + return edge->v2user; + else { + printf("yeek!!\n"); + return NULL; + } +} + +BM_INLINE void edge_set_vuser(MemBase *b, EdgeData *e, int ov, VertUser *vu) + +{ + VertUser *olduser = edge_get_vuser(b, e, ov); + + if (vu == olduser) + return; + + if (olduser) + BLI_remlink(&olduser->users, ov==e->v1 ? &e->v1node : &e->v2node); + BLI_addtail(&vu->users, ov==e->v1 ? &e->v1node : &e->v2node); + + if (ov == e->v1) + e->v1user = vu; + else e->v2user = vu; +} + +BM_INLINE VertUser *new_vuser(MemBase *base) +{ + VertUser *vusr = BLI_mempool_calloc(base->vertuserpool); + + return vusr; +} + +BM_INLINE MemBase *new_membase(void) +{ + MemBase *b = MEM_callocN(sizeof(MemBase), "MemBase for edgesplit in modifier.c"); + b->vertuserpool = BLI_mempool_create(sizeof(VertUser), 1, 2048, 1, 0); + + return b; +} + +BM_INLINE void free_membase(MemBase *b) +{ + BLI_mempool_destroy(b->vertuserpool); + MEM_freeN(b); +} + +BM_INLINE EdgeData *edge_get_first(VertUser *vu) +{ + return vu->users.first ? ((EdgeNode*)vu->users.first)->edge : NULL; +} + +DerivedMesh *doEdgeSplit(DerivedMesh *dm, EdgeSplitModifierData *emd) +{ + DerivedMesh *cddm = CDDM_copy(dm, 0); + MEdge *medge; + BLI_array_declare(medge); + MLoop *mloop, *ml, *prevl; + MPoly *mpoly, *mp; + MVert *mvert; + BLI_array_declare(mvert); + EdgeData *etags, *e, *enext; + BLI_array_declare(etags); + VertUser *vu, *vu2; + MemBase *membase; + CustomData edata, vdata; + int i, j, curv, cure; + float threshold = cos((emd->split_angle + 0.00001) * M_PI / 180.0); + float no[3], edge_angle_cos; + + if (!cddm->numVertData || !cddm->numEdgeData) + return cddm; + + membase = new_membase(); + + etags = MEM_callocN(sizeof(EdgeData)*cddm->numEdgeData, "edgedata tag thingies"); + BLI_array_set_length(etags, cddm->numEdgeData); + + mvert = cddm->getVertArray(cddm); + BLI_array_set_length(mvert, cddm->numVertData); + medge = cddm->getEdgeArray(cddm); + BLI_array_set_length(medge, cddm->numEdgeData); + mloop = CustomData_get_layer(&cddm->loopData, CD_MLOOP); + mpoly = CustomData_get_layer(&cddm->polyData, CD_MPOLY); + + for (i=0; i<cddm->numEdgeData; i++) { + etags[i].v1 = medge[i].v1; + etags[i].v2 = medge[i].v2; + + etags[i].tag = (medge[i].flag & ME_SHARP) != 0; + + etags[i].v1node.edge = etags+i; + etags[i].v2node.edge = etags+i; + } + + if (emd->flags & MOD_EDGESPLIT_FROMANGLE) { + mp = mpoly; + for (i=0; i<cddm->numPolyData; i++, mp++) { + mesh_calc_poly_normal(mp, mloop+mp->loopstart, mvert, no); + + ml = mloop + mp->loopstart; + for (j=0; j<mp->totloop; j++, ml++) { + if (!etags[ml->e].has_fno) { + VECCOPY(etags[ml->e].fno, no); + etags[ml->e].has_fno = 1; + } else if (!etags[ml->e].tag) { + edge_angle_cos = INPR(etags[ml->e].fno, no); + if (edge_angle_cos < threshold) { + etags[ml->e].tag = 1; + } + } + } + } + } + + mp = mpoly; + for (i=0; i<cddm->numPolyData; i++, mp++) { + ml = mloop + mp->loopstart; + for (j=0; j<mp->totloop; j++, ml++) { + if (etags[ml->e].tag) + continue; + + prevl = mloop + mp->loopstart + ((j-1)+mp->totloop) % mp->totloop; + + if (!edge_get_vuser(membase, etags+prevl->e, ml->v)) { + vu = new_vuser(membase); + vu->ov = vu->v = ml->v; + edge_set_vuser(membase, etags+prevl->e, ml->v, vu); + } + + if (!edge_get_vuser(membase, etags+ml->e, ml->v)) { + vu = new_vuser(membase); + vu->ov = vu->v = ml->v; + edge_set_vuser(membase, etags+ml->e, ml->v, vu); + } + + /*continue if previous edge is tagged*/ + if (etags[prevl->e].tag) + continue; + + /*merge together adjacent split vert users*/ + if (edge_get_vuser(membase, etags+prevl->e, ml->v) + != edge_get_vuser(membase, etags+ml->e, ml->v)) + { + vu = edge_get_vuser(membase, etags+prevl->e, ml->v); + vu2 = edge_get_vuser(membase, etags+ml->e, ml->v); + + /*remove from vu2's users list and add to vu's*/ + for (e=edge_get_first(vu2); e; e=enext) { + enext = edge_get_next(e, ml->v); + edge_set_vuser(membase, e, ml->v, vu); + } + } + } + } + + mp = mpoly; + for (i=0; i<cddm->numPolyData; i++, mp++) { + ml = mloop + mp->loopstart; + for (j=0; j<mp->totloop; j++, ml++) { + if (!etags[ml->e].tag) + continue; + + prevl = mloop + mp->loopstart + ((j-1)+mp->totloop) % mp->totloop; + + if (!etags[prevl->e].tag) { + vu = edge_get_vuser(membase, etags+prevl->e, ml->v); + if (!vu) { + vu = new_vuser(membase); + vu->ov = vu->v = ml->v; + edge_set_vuser(membase, etags+prevl->e, ml->v, vu); + } + + edge_set_vuser(membase, etags+ml->e, ml->v, vu); + } else { + vu = new_vuser(membase); + vu->ov = vu->v = ml->v; + edge_set_vuser(membase, etags+ml->e, ml->v, vu); + } + } + } + + curv = cddm->numVertData; + cure = cddm->numEdgeData; + mp = mpoly; + for (i=0; i<cddm->numPolyData; i++, mp++) { + ml = mloop + mp->loopstart; + for (j=0; j<mp->totloop; j++, ml++) { + e = etags + ml->e; + if (e->v1user && !e->v1user->done) { + e->v1user->done = 1; + BLI_array_growone(mvert); + + mvert[curv] = mvert[e->v1user->ov]; + e->v1user->v = curv; + + curv++; + } + + if (e->v2user && !e->v2user->done) { + e->v2user->done = 1; + BLI_array_growone(mvert); + + mvert[curv] = mvert[e->v2user->ov]; + e->v2user->v = curv; + + curv++; + } + + vu = edge_get_vuser(membase, e, ml->v); + if (!vu) + continue; + ml->v = vu->v; + +#if 0 //BMESH_TODO should really handle edges here, but for now use cddm_calc_edges + /*ok, now we have to deal with edges. . .*/ + if (etags[ml->e].tag) { + if (etags[ml->e].used) { + BLI_array_growone(medge); + BLI_array_growone(etags); + medge[cure] = medge[ml->e]; + + ml->e = cure; + etags[cure].used = 1; + cure++; + } + + vu = etags[ml->e].v1user; + vu2 = etags[ml->e].v2user; + + if (vu) + medge[ml->e].v1 = vu->v; + if (vu2) + medge[ml->e].v2 = vu2->v; + } else { + etags[ml->e].used = 1; + + if (vu->ov == etags[ml->e].v1) + medge[ml->e].v1 = vu->v; + else if (vu->ov == etags[ml->e].v2) + medge[ml->e].v2 = vu->v; + } +#endif + } + } + + + /*resize customdata arrays and add new medge/mvert arrays*/ + vdata = cddm->vertData; + edata = cddm->edgeData; + + /*make sure we don't copy over mvert/medge layers*/ + CustomData_set_layer(&vdata, CD_MVERT, NULL); + CustomData_set_layer(&edata, CD_MEDGE, NULL); + CustomData_free_layer_active(&vdata, CD_MVERT, cddm->numVertData); + CustomData_free_layer_active(&edata, CD_MEDGE, cddm->numEdgeData); + + memset(&cddm->vertData, 0, sizeof(CustomData)); + memset(&cddm->edgeData, 0, sizeof(CustomData)); + + CustomData_copy(&vdata, &cddm->vertData, CD_MASK_DERIVEDMESH, CD_CALLOC, curv); + CustomData_copy_data(&vdata, &cddm->vertData, 0, 0, cddm->numVertData); + CustomData_free(&vdata, cddm->numVertData); + cddm->numVertData = curv; + + CustomData_copy(&edata, &cddm->edgeData, CD_MASK_DERIVEDMESH, CD_CALLOC, cure); + CustomData_copy_data(&edata, &cddm->edgeData, 0, 0, cddm->numEdgeData); + CustomData_free(&edata, cddm->numEdgeData); + cddm->numEdgeData = cure; + + CDDM_set_mvert(cddm, mvert); + CDDM_set_medge(cddm, medge); + + free_membase(membase); + MEM_freeN(etags); + + /*edge calculation isn't working correctly, so just brute force it*/ + cddm->numEdgeData = 0; + CDDM_calc_edges_poly(cddm); + + cddm->numFaceData = mesh_recalcTesselation(&cddm->faceData, + &cddm->loopData, &cddm->polyData, + mvert, cddm->numFaceData, + cddm->numLoopData, cddm->numPolyData, 1, 0); + + CDDM_set_mface(cddm, DM_get_tessface_data_layer(cddm, CD_MFACE)); + CDDM_calc_normals(cddm); + + return cddm; +} + + +static void initData(ModifierData *md) +{ + EdgeSplitModifierData *emd = (EdgeSplitModifierData*) md; + + /* default to 30-degree split angle, sharpness from both angle & flag + */ + emd->split_angle = 30; + emd->flags = MOD_EDGESPLIT_FROMANGLE | MOD_EDGESPLIT_FROMFLAG; +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + EdgeSplitModifierData *emd = (EdgeSplitModifierData*) md; + EdgeSplitModifierData *temd = (EdgeSplitModifierData*) target; + + temd->split_angle = emd->split_angle; + temd->flags = emd->flags; +} + +static DerivedMesh *edgesplitModifier_do(EdgeSplitModifierData *emd, + Object *ob, DerivedMesh *dm) +{ + if(!(emd->flags & (MOD_EDGESPLIT_FROMANGLE | MOD_EDGESPLIT_FROMFLAG))) + return dm; + + return doEdgeSplit(dm, emd); +} + +static DerivedMesh *applyModifier( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) +{ + DerivedMesh *result; + EdgeSplitModifierData *emd = (EdgeSplitModifierData*) md; + + result = edgesplitModifier_do(emd, ob, derivedData); + + if(result != derivedData) + CDDM_calc_normals(result); + + return result; +} + +static DerivedMesh *applyModifierEM(ModifierData *md, Object *ob, + BMEditMesh *editData, DerivedMesh *derivedData) +{ + return applyModifier(md, ob, derivedData, 0, 1); +} + + +ModifierTypeInfo modifierType_EdgeSplit = { + /* name */ "EdgeSplit", + /* structName */ "EdgeSplitModifierData", + /* structSize */ sizeof(EdgeSplitModifierData), + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_AcceptsCVs + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode, + + /* copyData */ copyData, + /* deformVerts */ 0, + /* deformVertsEM */ 0, + /* deformMatricesEM */ 0, + /* applyModifier */ applyModifier, + /* applyModifierEM */ applyModifierEM, + /* initData */ initData, + /* requiredDataMask */ 0, + /* freeData */ 0, + /* isDisabled */ 0, + /* updateDepgraph */ 0, + /* dependsOnTime */ 0, + /* foreachObjectLink */ 0, + /* foreachIDLink */ 0, +}; |