diff options
Diffstat (limited to 'source')
44 files changed, 17464 insertions, 31 deletions
diff --git a/source/blender/SConscript b/source/blender/SConscript index e38d593d9a8..f9bafcd804a 100644 --- a/source/blender/SConscript +++ b/source/blender/SConscript @@ -3,6 +3,7 @@ Import ('env') import sys SConscript(['avi/SConscript', + 'bmesh/SConscript', 'blenkernel/SConscript', 'blenlib/SConscript', 'blenloader/SConscript', diff --git a/source/blender/bmesh/SConscript b/source/blender/bmesh/SConscript new file mode 100644 index 00000000000..d7cfa9ca942 --- /dev/null +++ b/source/blender/bmesh/SConscript @@ -0,0 +1,38 @@ +#!/usr/bin/python +Import ('env') + +cflags='' +""" +sources = ['intern/bmesh_eulers.c'] +sources.append('intern/bmesh_mesh.c') +sources.append('intern/bmesh_polygon.c') +sources.append('intern/bmesh_structure.c') +sources.append('intern/bmesh_marking.c') + +sources.append('intern/bmesh_construct.c') +sources.append('intern/bmesh_interp.c') +sources.append('intern/bmesh_filters.c') +sources.append('intern/bmesh_iterators.c') +sources.append('intern/bmesh_mods.c') +sources.append('intern/bmesh_queries.c') +sources.append('intern/bmesh_operators.c') +""" +#sources.append('api/BME_walkers.c') + + +sources = env.Glob('intern/*.c') +sources += env.Glob('operators/*.c') + +#sources += env.Glob('tools/*.c') + +incs = ['#/intern/guardedalloc'] +incs.append('../blenlib') +incs.append('../makesdna') +incs.append('../blenkernel') +incs.append('./') +incs.append('./intern') +incs.append('../editors/mesh') +incs.append('../editors/include') + +defs = [] +env.BlenderLib ( libname = 'bf_bmesh', sources = sources, includes = Split(incs), libtype = 'core', defines=defs, priority=100, compileflags=cflags ) diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h new file mode 100644 index 00000000000..1ee07dd9a83 --- /dev/null +++ b/source/blender/bmesh/bmesh.h @@ -0,0 +1,211 @@ +/** + * bmesh.h jan 2007 + * + * BM API. + * + * $Id: BKE_bmesh.h,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** 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. 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, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BM_H +#define BM_H + +#include "DNA_listBase.h" +#include "DNA_customdata_types.h" +#include "BLI_mempool.h" +#include "BKE_customdata.h" + +/*forward declarations*/ +struct BMVert; +struct BMEdge; +struct BMFace; +struct BMLoop; + +/* + * BMHeader + * + * All mesh elements begin with a BMHeader. This structure + * hold several types of data + * + * 1: The type of the element (vert, edge, loop or face) + * 2: Persistant flags/markings (sharp, seam, select, hidden, ect) + * 3: Unique ID in the bmesh. + * 4: some elements for internal record keeping. + * +*/ + +/*BMHeader->type*/ +#define BM_VERT 1 +#define BM_EDGE 2 +#define BM_FACE 4 +#define BM_LOOP 8 +#define BM_ALL BM_VERT | BM_EDGE | BM_FACE | BM_LOOP + +/*BMHeader->flag*/ +#define BM_SELECT (1<<0) + +#define BM_SEAM (1<<1) +#define BM_FGON (1<<2) +#define BM_HIDDEN (1<<3) +#define BM_SHARP (1<<4) +#define BM_SMOOTH (1<<5) + +typedef struct BMHeader { + struct BMHeader *next, *prev; + int EID; /*Consider removing this/making it ifdeffed for debugging*/ + int flag, type; + int eflag1, eflag2; /*Flags used by eulers. Try and get rid of/minimize some of these*/ + struct BMFlagLayer *flags; /*Dynamically allocated block of flag layers for operators to use*/ +} BMHeader; + +typedef struct BMFlagLayer { + int f1; + short mask, pflag; +} BMFlagLayer; + +#define BM_OVERLAP (1<<14) /*used by bmesh_verts_in_face*/ +#define BM_EDGEVERT (1<<15) /*used by bmesh_make_ngon*/ + +/* + * BMNode + * + * Used for circular/linked list functions that form basis of + * adjacency system in BMesh. This should probably be hidden + * somewhere since tool authors never need to know about it. + * +*/ + +typedef struct BMNode { + struct BMNode *next, *prev; + void *data; +} BMNode; + +typedef struct BMesh { + ListBase verts, edges, polys; + struct BLI_mempool *vpool; + struct BLI_mempool *epool; + struct BLI_mempool *lpool; + struct BLI_mempool *ppool; + struct BMVert **vtar; + struct BMEdge **edar; + struct BMLoop **lpar; + struct BMFace **plar; + int vtarlen, edarlen, lparlen, plarlen; + int totvert, totedge, totface, totloop; + int nextv, nexte, nextp, nextl; + struct CustomData vdata, edata, pdata, ldata; + int selectmode; + struct BLI_mempool *flagpool; /*memory pool for dynamically allocated flag layers*/ + int stackdepth; /*current depth of operator stack*/ + int totflags, walkers; /*total number of tool flag layers*/ + ListBase errorstack; +} BMesh; + +typedef struct BMVert { + struct BMHeader head; + float co[3]; + float no[3]; + struct BMEdge *edge; + void *data; + void *tmp; /*what?*/ + float bweight; /*please, someone just get rid of me...*/ +} BMVert; + +typedef struct BMEdge { + struct BMHeader head; + struct BMVert *v1, *v2; + struct BMNode d1, d2; + struct BMLoop *loop; + void *data; + float crease, bweight; /*make these custom data.... no really, please....*/ +} BMEdge; + +typedef struct BMLoop { + struct BMHeader head; + struct BMNode radial; + struct BMVert *v; + struct BMEdge *e; + struct BMFace *f; + void *data; +} BMLoop; + +typedef struct BMFace { + struct BMHeader head; + struct BMLoop *loopbase; + unsigned int len; + void *data; + float no[3]; + unsigned short mat_nr; /*custom data again, and get rid of the unsigned short nonsense...*/ +} BMFace; + +/*stub */ +void bmesh_error(void); + +/*Mesh Level Ops */ +struct BMesh *BM_Make_Mesh(int allocsize[4]); +void BM_Free_Mesh(struct BMesh *bm); +void BM_Compute_Normals(struct BMesh *bm); + +/*Construction*/ +struct BMVert *BM_Make_Vert(struct BMesh *bm, float co[3], struct BMVert *example); +struct BMEdge *BM_Make_Edge(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMEdge *example, int nodouble); +struct BMFace *BM_Make_Quadtriangle(struct BMesh *bm, struct BMVert **verts, BMEdge **edges, int len, struct BMFace *example, int nodouble); +struct BMFace *BM_Make_Ngon(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMEdge **edges, int len, int nodouble); +void BM_Copy_Attributes(struct BMesh *source_mesh, struct BMesh *target_mesh, void *source, void *target); +void BM_remove_tagged_faces(struct BMesh *bm, int flag); +void BM_remove_tagged_edges(struct BMesh *bm, int flag); +void BM_remove_tagged_verts(struct BMesh *bm, int flag); + + +/*Modification*/ +struct BMFace *BM_Join_Faces(struct BMesh *bm, struct BMFace *f1, struct BMFace *f2, struct BMEdge *e, int calcnorm, int weldUVs); +struct BMFace *BM_Split_Face(struct BMesh *bm, struct BMFace *f, struct BMVert *v1, struct BMVert *v2, struct BMLoop **nl, struct BMEdge *example, int calcnorm); +void BM_Collapse_Vert(struct BMesh *bm, struct BMEdge *ke, struct BMVert *kv, float fac, int calcnorm); +struct BMVert *BM_Split_Edge(struct BMesh *bm, struct BMVert *v, struct BMEdge *e, struct BMEdge **ne, float percent, int calcnorm); +struct BMVert *BM_Split_Edge_Multi(struct BMesh *bm, struct BMEdge *e, int numcuts); +BMEdge *BM_Connect_Verts(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf); +void BM_Dissolve_Disk(BMesh *bm, BMVert *v); + +/*Interpolation*/ +void BM_Data_Interp_From_Verts(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMVert *v, float fac); +void BM_Data_Facevert_Edgeinterp(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMVert *v, struct BMEdge *e1, float fac); +//void bmesh_data_interp_from_face(struct BMesh *bm, struct BMFace *source, struct BMFace *target); + +struct EditMesh; +BMesh *editmesh_to_bmesh(struct EditMesh *em); +struct EditMesh *bmesh_to_editmesh(BMesh *bm); + +/*include the rest of the API*/ +#include "bmesh_filters.h" +#include "bmesh_iterators.h" +#include "bmesh_marking.h" +#include "bmesh_operators.h" +#include "bmesh_queries.h" +#endif diff --git a/source/blender/bmesh/bmesh_filters.h b/source/blender/bmesh/bmesh_filters.h new file mode 100644 index 00000000000..9eed4e509fa --- /dev/null +++ b/source/blender/bmesh/bmesh_filters.h @@ -0,0 +1,4 @@ +#ifndef BM_FILTER_H +#define BM_FILTER_H + +#endif diff --git a/source/blender/bmesh/bmesh_iterators.h b/source/blender/bmesh/bmesh_iterators.h new file mode 100644 index 00000000000..7e1eef84918 --- /dev/null +++ b/source/blender/bmesh/bmesh_iterators.h @@ -0,0 +1,50 @@ +/* + * BMESH ITERATORS + * + * The functions and structures in this file + * provide a unified method for iterating over + * the elements of a mesh and answering simple + * adjacency queries. Tool authors should use + * the iterators provided in this file instead + * of inspecting the structure directly. + * +*/ + +#ifndef BM_ITERATORS_H +#define BM_ITERATORS_H + +/*Defines for passing to BMIter_New*/ +#define BM_VERTS 1 +#define BM_EDGES 2 +#define BM_FACES 3 +#define BM_EDGES_OF_VERT 4 +#define BM_FACES_OF_VERT 5 +#define BM_VERTS_OF_EDGE 6 +#define BM_FACES_OF_EDGE 7 +#define BM_VERTS_OF_FACE 8 +#define BM_FACEVERTS_OF_FACE 9 +#define BM_EDGES_OF_FACE 10 +#define BM_LOOPS_OF_FACE 11 + +/*Iterator Structure*/ +typedef struct BMIter{ + struct BMVert *firstvert, *nextvert, *vdata; + struct BMEdge *firstedge, *nextedge, *edata; + struct BMLoop *firstloop, *nextloop, *ldata; + struct BMFace *firstpoly, *nextpoly, *pdata; + struct BMesh *bm; + void (*begin)(struct BMIter *iter); + void *(*step)(struct BMIter *iter); + union{ + void *p; + int i; + long l; + float f; + }filter; + int type, count; +}BMIter; + +void *BMIter_New(struct BMIter *iter, struct BMesh *bm, int type, void *data); +void *BMIter_Step(struct BMIter *iter); + +#endif diff --git a/source/blender/bmesh/bmesh_marking.h b/source/blender/bmesh/bmesh_marking.h new file mode 100644 index 00000000000..1074b057eba --- /dev/null +++ b/source/blender/bmesh/bmesh_marking.h @@ -0,0 +1,16 @@ +#ifndef BM_MARKING_H +#define BM_MARKING_H + +/*Selection code*/ +void BM_Select_Vert(struct BMesh *bm, struct BMVert *v, int select); +void BM_Select_Edge(struct BMesh *bm, struct BMEdge *e, int select); +void BM_Select_Face(struct BMesh *bm, struct BMFace *f, int select); +void BM_Selectmode_Set(struct BMesh *bm, int selectmode); + +/*counts number of elements with flag set*/ +int BM_CountFlag(struct BMesh *bm, int type, int flag); + +void BM_Select(struct BMesh *bm, void *element, int select); +int BM_Is_Selected(BMesh *bm, void *element); + +#endif diff --git a/source/blender/bmesh/bmesh_operators.h b/source/blender/bmesh/bmesh_operators.h new file mode 100644 index 00000000000..b7fd6cc5275 --- /dev/null +++ b/source/blender/bmesh/bmesh_operators.h @@ -0,0 +1,196 @@ +#ifndef BM_OPERATORS_H +#define BM_OPERATORS_H + +#include "BLI_memarena.h" + +#define BMOP_OPSLOT_INT 0 +#define BMOP_OPSLOT_FLT 1 +#define BMOP_OPSLOT_PNT 2 +#define BMOP_OPSLOT_VEC 6 + +/*after BMOP_OPSLOT_VEC, everything is + dynamically allocated arrays. we + leave a space in the identifiers + for future growth.*/ +#define BMOP_OPSLOT_INT_BUF 7 +#define BMOP_OPSLOT_FLT_BUF 8 +#define BMOP_OPSLOT_PNT_BUF 9 +#define BMOP_OPSLOT_TYPES 10 + +typedef struct BMOpSlot{ + int slottype; + int len; + int index; /*index within slot array*/ + union { + int i; + float f; + void *p; + float vec[3]; /*vector*/ + void *buf; /*buffer*/ + } data; +}BMOpSlot; + +/*operators represent logical, executable mesh modules.*/ +#define BMOP_MAX_SLOTS 16 /*way more than probably needed*/ + +typedef struct BMOperator{ + int type; + int slottype; + int needflag; + struct BMOpSlot slots[BMOP_MAX_SLOTS]; + void (*exec)(struct BMesh *bm, struct BMOperator *op); + MemArena *arena; +}BMOperator; + +/*need to refactor code to use this*/ +typedef struct BMOpDefine { + int slottypes[BMOP_MAX_SLOTS]; + void (*exec)(BMesh *bm, BMOperator *op); + int totslot; + int flag; +} BMOpDefine; + +/*BMOpDefine->flag*/ +//doesn't do anything at the moment. +#define BMO_NOFLAGS 1 + +/*API for operators*/ +void BMO_Init_Op(struct BMOperator *op, int opcode); +void BMO_Exec_Op(struct BMesh *bm, struct BMOperator *op); +void BMO_Finish_Op(struct BMesh *bm, struct BMOperator *op); +BMOpSlot *BMO_GetSlot(struct BMOperator *op, int slotcode); +void BMO_CopySlot(struct BMOperator *source_op, struct BMOperator *dest_op, int src, int dst); +void BMO_Set_Float(struct BMOperator *op, int slotcode, float f); +void BMO_Set_Int(struct BMOperator *op, int slotcode, int i); +void BMO_Set_PntBuf(struct BMOperator *op, int slotcode, void *p, int len); +void BMO_Set_Pnt(struct BMOperator *op, int slotcode, void *p); +void BMO_Set_Vec(struct BMOperator *op, int slotcode, float *vec); +void BMO_SetFlag(struct BMesh *bm, void *element, int flag); +void BMO_ClearFlag(struct BMesh *bm, void *element, int flag); +int BMO_TestFlag(struct BMesh *bm, void *element, int flag); +int BMO_CountFlag(struct BMesh *bm, int flag, int type); +void BMO_Flag_To_Slot(struct BMesh *bm, struct BMOperator *op, int slotcode, int flag, int type); +void BMO_Flag_Buffer(struct BMesh *bm, struct BMOperator *op, int slotcode, int flag); +void BMO_HeaderFlag_To_Slot(struct BMesh *bm, struct BMOperator *op, int slotcode, int flag, int type); + +/*if msg is null, then the default message for the errorcode is used*/ +void BMOP_RaiseError(BMesh *bm, int errcode, char *msg); +/*returns error code or 0 if no error*/ +int BMOP_GetError(BMesh *bm, char **msg); +/*returns 1 if there was an error*/ +int BMOP_CheckError(BMesh *bm); +int BMOP_PopError(BMesh *bm, char **msg); + +/*------ error code defines -------*/ + +/*error messages*/ +#define BMERR_SELF_INTERSECTING 1 + +static char *bmop_error_messages[] = { + 0, + "Self intersection error", +}; + +/*------------begin operator defines (see bmesh_opdefines.c too)------------*/ +/*split op*/ +#define BMOP_SPLIT 0 + +enum { + BMOP_SPLIT_MULTIN, + BMOP_SPLIT_MULTOUT, + BMOP_SPLIT_TOTSLOT, +}; + +/*dupe op*/ +#define BMOP_DUPE 1 + +enum { + BMOP_DUPE_MULTIN, + BMOP_DUPE_ORIG, + BMOP_DUPE_NEW, + BMOP_DUPE_TOTSLOT +}; + +/*delete op*/ +#define BMOP_DEL 2 + +enum { + BMOP_DEL_MULTIN, + BMOP_DEL_CONTEXT, + BMOP_DEL_TOTSLOT, +}; + +#define DEL_VERTS 1 +#define DEL_EDGES 2 +#define DEL_ONLYFACES 3 +#define DEL_EDGESFACES 4 +#define DEL_FACES 5 +#define DEL_ALL 6 + +/*BMOP_DEL_CONTEXT*/ +enum { + BMOP_DEL_VERTS = 1, + BMOP_DEL_EDGESFACES, + BMOP_DEL_ONLYFACES, + BMOP_DEL_FACES, + BMOP_DEL_ALL, +}; + +/*editmesh->bmesh op*/ +#define BMOP_FROM_EDITMESH 3 +enum { + BMOP_FROM_EDITMESH_EM, + BMOP_FROM_EDITMESH_TOTSLOT, +}; + +#define BMOP_TO_EDITMESH 4 +/*bmesh->editmesh op*/ +enum { + BMOP_TO_EDITMESH_EMOUT, + BMOP_TO_EDITMESH_TOTSLOT, +}; + +/*edge subdivide op*/ +#define BMOP_ESUBDIVIDE 5 +#define BMOP_ESUBDIVIDE_EDGES 0 +#define BMOP_ESUBDIVIDE_TOTSLOT 1 + +/*triangulate*/ +#define BMOP_TRIANGULATE 6 + +#define BMOP_TRIANG_FACEIN 0 +#define BMOP_TRIANG_NEW_EDGES 1 +#define BMOP_TRIANG_NEW_FACES 2 +#define BMOP_TRIANG_TOTSLOT 3 + +/*dissolve faces*/ +#define BMOP_DISSOLVE_FACES 7 + +#define BMOP_DISFACES_FACEIN 0 +#define BMOP_DISFACES_TOTSLOT 1 + +/*dissolve verts*/ +#define BMOP_DISSOLVE_VERTS 8 + +#define BMOP_DISVERTS_VERTIN 0 +#define BMOP_DISVERTS_TOTSLOT 1 + +#define BMOP_MAKE_FGONS 9 +#define BMOP_MAKE_FGONS_TOTSLOT 0 + +/*keep this updated!*/ +#define BMOP_TOTAL_OPS 10 +/*-------------------------------end operator defines-------------------------------*/ + +extern BMOpDefine *opdefines[]; +extern int bmesh_total_ops; + +/*------specific operator helper functions-------*/ + +/*executes the duplicate operation, feeding elements of + type flag etypeflag and header flag flag to it. note, + to get more useful information (such as the mapping from + original to new elements) you should run the dupe op manually.*/ +void BMOP_DupeFromFlag(struct BMesh *bm, int etypeflag, int flag); + +#endif diff --git a/source/blender/bmesh/bmesh_queries.h b/source/blender/bmesh/bmesh_queries.h new file mode 100644 index 00000000000..f02375313a1 --- /dev/null +++ b/source/blender/bmesh/bmesh_queries.h @@ -0,0 +1,30 @@ +#ifndef BMESH_QUERIES_H +#define BMESH_QUERIES_H + +/*Queries*/ +int BM_Count_Element(struct BMesh *bm, int type); +int BM_Vert_In_Edge(struct BMEdge *e, struct BMVert *v); +int BM_Vert_In_Face(struct BMFace *f, struct BMVert *v); +// int BM_Verts_In_Face(struct BMFace *f, struct BMVert **varr, int len); +int BM_Verts_In_Face(struct BMesh *bm, struct BMFace *f, struct BMVert **varr, int len); +int BM_Edge_In_Face(struct BMFace *f, struct BMEdge *e); +int BM_Verts_In_Edge(struct BMVert *v1, struct BMVert *v2, BMEdge *e); + +struct BMVert *BM_OtherEdgeVert(struct BMEdge *e, struct BMVert *v); +//#define BM_OtherEdgeVert(e, v) (v==e->v1?e->v2:e->v1) + +struct BMEdge *BM_Edge_Exist(struct BMVert *v1, struct BMVert *v2); +int BM_Vert_EdgeCount(struct BMVert *v); +int BM_Edge_FaceCount(struct BMEdge *e); +int BM_Vert_FaceCount(struct BMVert *v); +int BM_Wire_Vert(struct BMesh *bm, struct BMVert *v); +int BM_Wire_Edge(struct BMesh *bm, struct BMEdge *e); +int BM_Nonmanifold_Vert(struct BMesh *bm, struct BMVert *v); +int BM_Nonmanifold_Edge(struct BMesh *bm, struct BMEdge *e); +int BM_Boundary_Edge(struct BMEdge *e); +int BM_Face_Sharededges(struct BMFace *f1, struct BMFace *f2); +float BM_Face_Angle(struct BMesh *bm, struct BMEdge *e); +int BM_Exist_Face_Overlaps(struct BMesh *bm, struct BMVert **varr, int len, struct BMFace **existface); +int BM_Edge_Share_Faces(struct BMEdge *e1, struct BMEdge *e2); + +#endif diff --git a/source/blender/bmesh/bmesh_walkers.h b/source/blender/bmesh/bmesh_walkers.h new file mode 100644 index 00000000000..5f0b1e2f256 --- /dev/null +++ b/source/blender/bmesh/bmesh_walkers.h @@ -0,0 +1,26 @@ +#ifndef BM_WALKERS_H +#define BM_WALKERS_H + +/*Walkers*/ +typedef struct BMWalker{ + BLI_mempool *stack; + BMesh *bm; + void *currentstate; + void *(*begin) (struct BMWalker *walker, void *start); + void *(*yield)(struct BMWalker *walker); + void (*step) (struct BMWalker *walker); + int visitedmask; + int restrictflag; +}BMWalker; + +void BMWalker_Init(struct BMWalker *walker, BMesh *bm, int type, int searchmask); +void *BMWalker_Step(struct BMWalker *walker); +void BMWalker_End(struct BMWalker *walker); + +#define BM_SHELLWALKER 0 +#define BM_LOOPWALKER 1 +#define BM_RINGWALKER 2 +#define BM_UVISLANDS 3 +#define BM_MAXWALKERS 4 + +#endif
\ No newline at end of file diff --git a/source/blender/bmesh/editmesh_tools.c b/source/blender/bmesh/editmesh_tools.c new file mode 100644 index 00000000000..e2b83c6e111 --- /dev/null +++ b/source/blender/bmesh/editmesh_tools.c @@ -0,0 +1,7244 @@ +/** + * $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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Johnny Matthews, Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* + +editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise in mods.c + +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "BMF_Api.h" +#include "DNA_mesh_types.h" +#include "DNA_material_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" +#include "DNA_key_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" +#include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_heap.h" + +#include "BKE_depsgraph.h" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" + +#ifdef WITH_VERSE +#include "BKE_verse.h" +#endif + +#include "BIF_cursors.h" +#include "BIF_editmesh.h" +#include "BIF_gl.h" +#include "BIF_glutil.h" +#include "BIF_graphics.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_screen.h" +#include "BIF_space.h" +#include "BIF_resources.h" +#include "BIF_toolbox.h" +#include "BIF_transform.h" +#include "transform.h" + +#ifdef WITH_VERSE +#include "BIF_verse.h" +#endif + +#include "BDR_drawobject.h" +#include "BDR_editobject.h" + +#include "BSE_view.h" +#include "BSE_edit.h" + +#include "blendef.h" +#include "multires.h" +#include "mydevice.h" + +#include "editmesh.h" + +#include "MTC_vectorops.h" + +#include "PIL_time.h" + +#include "BLO_sys_types.h" // for intptr_t support + +/* local prototypes ---------------*/ +void bevel_menu(void); +static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa); + +/********* qsort routines *********/ + + +typedef struct xvertsort { + float x; + EditVert *v1; +} xvertsort; + +static int vergxco(const void *v1, const void *v2) +{ + const xvertsort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + +struct facesort { + uintptr_t x; + struct EditFace *efa; +}; + + +static int vergface(const void *v1, const void *v2) +{ + const struct facesort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + + +/* *********************************** */ + +void convert_to_triface(int direction) +{ + EditMesh *em = G.editMesh; + EditFace *efa, *efan, *next; + float fac; + + if(multires_test()) return; + + efa= em->faces.last; + while(efa) { + next= efa->prev; + if(efa->v4) { + if(efa->f & SELECT) { + /* choose shortest diagonal for split */ + fac= VecLenf(efa->v1->co, efa->v3->co) - VecLenf(efa->v2->co, efa->v4->co); + /* this makes sure exact squares get split different in both cases */ + if( (direction==0 && fac<FLT_EPSILON) || (direction && fac>0.0f) ) { + efan= EM_face_from_faces(efa, NULL, 0, 1, 2, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + efan= EM_face_from_faces(efa, NULL, 0, 2, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + } + else { + efan= EM_face_from_faces(efa, NULL, 0, 1, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + efan= EM_face_from_faces(efa, NULL, 1, 2, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + } + + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + } + efa= next; + } + + EM_fgon_flags(); // redo flags and indices for fgons + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Convert Quads to Triangles"); + +} + +int removedoublesflag(short flag, short automerge, float limit) /* return amount */ +{ + /* + flag - Test with vert->flags + automerge - Alternative operation, merge unselected into selected. + Used for "Auto Weld" mode. warning. + limit - Quick manhattan distance between verts. + */ + + EditMesh *em = G.editMesh; + /* all verts with (flag & 'flag') are being evaluated */ + EditVert *eve, *v1, *nextve; + EditEdge *eed, *e1, *nexted; + EditFace *efa, *nextvl; + xvertsort *sortblock, *sb, *sb1; + struct facesort *vlsortblock, *vsb, *vsb1; + int a, b, test, amount; + + if(multires_test()) return 0; + + + /* flag 128 is cleared, count */ + + /* Normal non weld operation */ + eve= em->verts.first; + amount= 0; + while(eve) { + eve->f &= ~128; + if(eve->h==0 && (automerge || (eve->f & flag))) amount++; + eve= eve->next; + } + if(amount==0) return 0; + + /* allocate memory and qsort */ + sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub"); + eve= em->verts.first; + while(eve) { + if(eve->h==0 && (automerge || (eve->f & flag))) { + sb->x= eve->co[0]+eve->co[1]+eve->co[2]; + sb->v1= eve; + sb++; + } + eve= eve->next; + } + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + + /* test for doubles */ + sb= sortblock; + if (automerge) { + for(a=0; a<amount; a++, sb++) { + eve= sb->v1; + if( (eve->f & 128)==0 ) { + sb1= sb+1; + for(b=a+1; b<amount && (eve->f & 128)==0; b++, sb1++) { + if(sb1->x - sb->x > limit) break; + + /* when automarge, only allow unselected->selected */ + v1= sb1->v1; + if( (v1->f & 128)==0 ) { + if ((eve->f & flag)==0 && (v1->f & flag)==1) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { /* unique bit */ + eve->f|= 128; + eve->tmp.v = v1; + } + } else if( (eve->f & flag)==1 && (v1->f & flag)==0 ) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { /* unique bit */ + v1->f|= 128; + v1->tmp.v = eve; + } + } + } + } + } + } + } else { + for(a=0; a<amount; a++, sb++) { + eve= sb->v1; + if( (eve->f & 128)==0 ) { + sb1= sb+1; + for(b=a+1; b<amount; b++, sb1++) { + /* first test: simpel dist */ + if(sb1->x - sb->x > limit) break; + v1= sb1->v1; + + /* second test: is vertex allowed */ + if( (v1->f & 128)==0 ) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { + v1->f|= 128; + v1->tmp.v = eve; + } + } + } + } + } + } + MEM_freeN(sortblock); + + if (!automerge) + for(eve = em->verts.first; eve; eve=eve->next) + if((eve->f & flag) && (eve->f & 128)) + EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f); + + /* test edges and insert again */ + eed= em->edges.first; + while(eed) { + eed->f2= 0; + eed= eed->next; + } + eed= em->edges.last; + while(eed) { + nexted= eed->prev; + + if(eed->f2==0) { + if( (eed->v1->f & 128) || (eed->v2->f & 128) ) { + remedge(eed); + + if(eed->v1->f & 128) eed->v1 = eed->v1->tmp.v; + if(eed->v2->f & 128) eed->v2 = eed->v2->tmp.v; + e1= addedgelist(eed->v1, eed->v2, eed); + + if(e1) { + e1->f2= 1; + if(eed->f & SELECT) + e1->f |= SELECT; + } + if(e1!=eed) free_editedge(eed); + } + } + eed= nexted; + } + + /* first count amount of test faces */ + efa= (struct EditFace *)em->faces.first; + amount= 0; + while(efa) { + efa->f1= 0; + if(efa->v1->f & 128) efa->f1= 1; + else if(efa->v2->f & 128) efa->f1= 1; + else if(efa->v3->f & 128) efa->f1= 1; + else if(efa->v4 && (efa->v4->f & 128)) efa->f1= 1; + + if(efa->f1==1) amount++; + efa= efa->next; + } + + /* test faces for double vertices, and if needed remove them */ + efa= (struct EditFace *)em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f1==1) { + + if(efa->v1->f & 128) efa->v1= efa->v1->tmp.v; + if(efa->v2->f & 128) efa->v2= efa->v2->tmp.v; + if(efa->v3->f & 128) efa->v3= efa->v3->tmp.v; + if(efa->v4 && (efa->v4->f & 128)) efa->v4= efa->v4->tmp.v; + + test= 0; + if(efa->v1==efa->v2) test+=1; + if(efa->v2==efa->v3) test+=2; + if(efa->v3==efa->v1) test+=4; + if(efa->v4==efa->v1) test+=8; + if(efa->v3==efa->v4) test+=16; + if(efa->v2==efa->v4) test+=32; + + if(test) { + if(efa->v4) { + if(test==1 || test==2) { + efa->v2= efa->v3; + efa->v3= efa->v4; + efa->v4= 0; + + EM_data_interp_from_faces(efa, NULL, efa, 0, 2, 3, 3); + + test= 0; + } + else if(test==8 || test==16) { + efa->v4= 0; + test= 0; + } + else { + BLI_remlink(&em->faces, efa); + free_editface(efa); + amount--; + } + } + else { + BLI_remlink(&em->faces, efa); + free_editface(efa); + amount--; + } + } + + if(test==0) { + /* set edge pointers */ + efa->e1= findedgelist(efa->v1, efa->v2); + efa->e2= findedgelist(efa->v2, efa->v3); + if(efa->v4==0) { + efa->e3= findedgelist(efa->v3, efa->v1); + efa->e4= 0; + } + else { + efa->e3= findedgelist(efa->v3, efa->v4); + efa->e4= findedgelist(efa->v4, efa->v1); + } + } + } + efa= nextvl; + } + + /* double faces: sort block */ + /* count again, now all selected faces */ + amount= 0; + efa= em->faces.first; + while(efa) { + efa->f1= 0; + if(faceselectedOR(efa, 1)) { + efa->f1= 1; + amount++; + } + efa= efa->next; + } + + if(amount) { + /* double faces: sort block */ + vsb= vlsortblock= MEM_mallocN(sizeof(struct facesort)*amount, "sortremovedoub"); + efa= em->faces.first; + while(efa) { + if(efa->f1 & 1) { + if(efa->v4) vsb->x= (uintptr_t) MIN4( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3, (uintptr_t)efa->v4); + else vsb->x= (uintptr_t) MIN3( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3); + + vsb->efa= efa; + vsb++; + } + efa= efa->next; + } + + qsort(vlsortblock, amount, sizeof(struct facesort), vergface); + + vsb= vlsortblock; + for(a=0; a<amount; a++) { + efa= vsb->efa; + if( (efa->f1 & 128)==0 ) { + vsb1= vsb+1; + + for(b=a+1; b<amount; b++) { + + /* first test: same pointer? */ + if(vsb->x != vsb1->x) break; + + /* second test: is test permitted? */ + efa= vsb1->efa; + if( (efa->f1 & 128)==0 ) { + if( compareface(efa, vsb->efa)) efa->f1 |= 128; + + } + vsb1++; + } + } + vsb++; + } + + MEM_freeN(vlsortblock); + + /* remove double faces */ + efa= (struct EditFace *)em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f1 & 128) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + } + + /* remove double vertices */ + a= 0; + eve= (struct EditVert *)em->verts.first; + while(eve) { + nextve= eve->next; + if(automerge || eve->f & flag) { + if(eve->f & 128) { + a++; + BLI_remlink(&em->verts, eve); + free_editvert(eve); + } + } + eve= nextve; + } + +#ifdef WITH_VERSE + if((a>0) && (G.editMesh->vnode)) { + sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode); + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); + } +#endif + + return a; /* amount */ +} + +/* called from buttons */ +static void xsortvert_flag__doSetX(void *userData, EditVert *eve, int x, int y, int index) +{ + xvertsort *sortblock = userData; + + sortblock[index].x = x; +} +void xsortvert_flag(int flag) +{ + EditMesh *em = G.editMesh; + /* all verts with (flag & 'flag') are sorted */ + EditVert *eve; + xvertsort *sortblock; + ListBase tbase; + int i, amount = BLI_countlist(&em->verts); + + if(multires_test()) return; + + sortblock = MEM_callocN(sizeof(xvertsort)*amount,"xsort"); + for (i=0,eve=em->verts.first; eve; i++,eve=eve->next) + if(eve->f & flag) + sortblock[i].v1 = eve; + mesh_foreachScreenVert(xsortvert_flag__doSetX, sortblock, 0); + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + /* make temporal listbase */ + tbase.first= tbase.last= 0; + for (i=0; i<amount; i++) { + eve = sortblock[i].v1; + + if (eve) { + BLI_remlink(&em->verts, eve); + BLI_addtail(&tbase, eve); + } + } + + addlisttolist(&em->verts, &tbase); + + MEM_freeN(sortblock); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Xsort"); + +} + +/* called from buttons */ +void hashvert_flag(int flag) +{ + /* switch vertex order using hash table */ + EditMesh *em = G.editMesh; + EditVert *eve; + struct xvertsort *sortblock, *sb, onth, *newsort; + ListBase tbase; + int amount, a, b; + + if(multires_test()) return; + + /* count */ + eve= em->verts.first; + amount= 0; + while(eve) { + if(eve->f & flag) amount++; + eve= eve->next; + } + if(amount==0) return; + + /* allocate memory */ + sb= sortblock= (struct xvertsort *)MEM_mallocN(sizeof(struct xvertsort)*amount,"sortremovedoub"); + eve= em->verts.first; + while(eve) { + if(eve->f & flag) { + sb->v1= eve; + sb++; + } + eve= eve->next; + } + + BLI_srand(1); + + sb= sortblock; + for(a=0; a<amount; a++, sb++) { + b= (int)(amount*BLI_drand()); + if(b>=0 && b<amount) { + newsort= sortblock+b; + onth= *sb; + *sb= *newsort; + *newsort= onth; + } + } + + /* make temporal listbase */ + tbase.first= tbase.last= 0; + sb= sortblock; + while(amount--) { + eve= sb->v1; + BLI_remlink(&em->verts, eve); + BLI_addtail(&tbase, eve); + sb++; + } + + addlisttolist(&em->verts, &tbase); + + MEM_freeN(sortblock); +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Hash"); + +} + +/* generic extern called extruder */ +void extrude_mesh(void) +{ + float nor[3]= {0.0, 0.0, 0.0}; + short nr, transmode= 0; + + TEST_EDITMESH + if(multires_test()) return; + + if(G.scene->selectmode & SCE_SELECT_VERTEX) { + if(G.totvertsel==0) nr= 0; + else if(G.totvertsel==1) nr= 4; + else if(G.totedgesel==0) nr= 4; + else if(G.totfacesel==0) + nr= pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4"); + else if(G.totfacesel==1) + nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4"); + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4"); + } + else if(G.scene->selectmode & SCE_SELECT_EDGE) { + if (G.totedgesel==0) nr = 0; + else if (G.totedgesel==1) nr = 3; + else if(G.totfacesel==0) nr = 3; + else if(G.totfacesel==1) + nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3"); + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3"); + } + else { + if (G.totfacesel == 0) nr = 0; + else if (G.totfacesel == 1) nr = 1; + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2"); + } + + if(nr<1) return; + + if(nr==1) transmode= extrudeflag(SELECT, nor); + else if(nr==4) transmode= extrudeflag_verts_indiv(SELECT, nor); + else if(nr==3) transmode= extrudeflag_edges_indiv(SELECT, nor); + else transmode= extrudeflag_face_indiv(SELECT, nor); + + if(transmode==0) { + error("No valid selection"); + } + else { + EM_fgon_flags(); + countall(); + + /* We need to force immediate calculation here because + * transform may use derived objects (which are now stale). + * + * This shouldn't be necessary, derived queries should be + * automatically building this data if invalid. Or something. + */ + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + object_handle_update(G.obedit); + + /* individual faces? */ + BIF_TransformSetUndo("Extrude"); + if(nr==2) { + initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR); + Transform(); + } + else { + initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR); + if(transmode=='n') { + Mat4MulVecfl(G.obedit->obmat, nor); + VecSubf(nor, nor, G.obedit->obmat[3]); + BIF_setSingleAxisConstraint(nor, "along normal"); + } + Transform(); + } + } + +} + +void split_mesh(void) +{ + + TEST_EDITMESH + if(multires_test()) return; + + if(okee(" Split ")==0) return; + + waitcursor(1); + + /* make duplicate first */ + adduplicateflag(SELECT); + /* old faces have flag 128 set, delete them */ + delfaceflag(128); + recalc_editnormals(); + + waitcursor(0); + + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Split"); + +} + +void extrude_repeat_mesh(int steps, float offs) +{ + float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0}; + short a; + + TEST_EDITMESH + if(multires_test()) return; + + /* dvec */ + dvec[0]= G.vd->persinv[2][0]; + dvec[1]= G.vd->persinv[2][1]; + dvec[2]= G.vd->persinv[2][2]; + Normalize(dvec); + dvec[0]*= offs; + dvec[1]*= offs; + dvec[2]*= offs; + + /* base correction */ + Mat3CpyMat4(bmat, G.obedit->obmat); + Mat3Inv(tmat, bmat); + Mat3MulVecfl(tmat, dvec); + + for(a=0; a<steps; a++) { + extrudeflag(SELECT, nor); + translateflag(SELECT, dvec); + } + + recalc_editnormals(); + + EM_fgon_flags(); + countall(); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Extrude Repeat"); +} + +void spin_mesh(int steps, float degr, float *dvec, int mode) +{ + EditMesh *em = G.editMesh; + EditVert *eve,*nextve; + float nor[3]= {0.0, 0.0, 0.0}; + float *curs, si,n[3],q[4],cmat[3][3],imat[3][3], tmat[3][3]; + float cent[3],bmat[3][3]; + float phi; + short a,ok; + + TEST_EDITMESH + if(multires_test()) return; + + /* imat and center and size */ + Mat3CpyMat4(bmat, G.obedit->obmat); + Mat3Inv(imat,bmat); + + curs= give_cursor(); + VECCOPY(cent, curs); + cent[0]-= G.obedit->obmat[3][0]; + cent[1]-= G.obedit->obmat[3][1]; + cent[2]-= G.obedit->obmat[3][2]; + Mat3MulVecfl(imat, cent); + + phi= degr*M_PI/360.0; + phi/= steps; + if(G.scene->toolsettings->editbutflag & B_CLOCKWISE) phi= -phi; + + if(dvec) { + n[0]= G.vd->viewinv[1][0]; + n[1]= G.vd->viewinv[1][1]; + n[2]= G.vd->viewinv[1][2]; + } else { + n[0]= G.vd->viewinv[2][0]; + n[1]= G.vd->viewinv[2][1]; + n[2]= G.vd->viewinv[2][2]; + } + Normalize(n); + + q[0]= (float)cos(phi); + si= (float)sin(phi); + q[1]= n[0]*si; + q[2]= n[1]*si; + q[3]= n[2]*si; + QuatToMat3(q, cmat); + + Mat3MulMat3(tmat,cmat,bmat); + Mat3MulMat3(bmat,imat,tmat); + + if(mode==0) if(G.scene->toolsettings->editbutflag & B_KEEPORIG) adduplicateflag(1); + ok= 1; + + for(a=0;a<steps;a++) { + if(mode==0) ok= extrudeflag(SELECT, nor); + else adduplicateflag(SELECT); + if(ok==0) { + error("No valid vertices are selected"); + break; + } + rotateflag(SELECT, cent, bmat); + if(dvec) { + Mat3MulVecfl(bmat,dvec); + translateflag(SELECT, dvec); + } + } + + if(ok==0) { + /* no vertices or only loose ones selected, remove duplicates */ + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f & SELECT) { + BLI_remlink(&em->verts,eve); + free_editvert(eve); + } + eve= nextve; + } + } + recalc_editnormals(); + + EM_fgon_flags(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + + if(dvec==NULL) BIF_undo_push("Spin"); +} + +void screw_mesh(int steps, int turns) +{ + EditMesh *em = G.editMesh; + EditVert *eve,*v1=0,*v2=0; + EditEdge *eed; + float dvec[3], nor[3]; + + TEST_EDITMESH + if(multires_test()) return; + + /* clear flags */ + eve= em->verts.first; + while(eve) { + eve->f1= 0; + eve= eve->next; + } + /* edges set flags in verts */ + eed= em->edges.first; + while(eed) { + if(eed->v1->f & SELECT) { + if(eed->v2->f & SELECT) { + /* watch: f1 is a byte */ + if(eed->v1->f1<2) eed->v1->f1++; + if(eed->v2->f1<2) eed->v2->f1++; + } + } + eed= eed->next; + } + /* find two vertices with eve->f1==1, more or less is wrong */ + eve= em->verts.first; + while(eve) { + if(eve->f1==1) { + if(v1==0) v1= eve; + else if(v2==0) v2= eve; + else { + v1=0; + break; + } + } + eve= eve->next; + } + if(v1==0 || v2==0) { + error("You have to select a string of connected vertices too"); + return; + } + + /* calculate dvec */ + dvec[0]= ( (v1->co[0]- v2->co[0]) )/(steps); + dvec[1]= ( (v1->co[1]- v2->co[1]) )/(steps); + dvec[2]= ( (v1->co[2]- v2->co[2]) )/(steps); + + VECCOPY(nor, G.obedit->obmat[2]); + + if(nor[0]*dvec[0]+nor[1]*dvec[1]+nor[2]*dvec[2]>0.000) { + dvec[0]= -dvec[0]; + dvec[1]= -dvec[1]; + dvec[2]= -dvec[2]; + } + + spin_mesh(turns*steps, turns*360, dvec, 0); + + BIF_undo_push("Spin"); +} + + +static void erase_edges(ListBase *l) +{ + EditEdge *ed, *nexted; + + ed = (EditEdge *) l->first; + while(ed) { + nexted= ed->next; + if( (ed->v1->f & SELECT) || (ed->v2->f & SELECT) ) { + remedge(ed); + free_editedge(ed); + } + ed= nexted; + } +} + +static void erase_faces(ListBase *l) +{ + EditFace *f, *nextf; + + f = (EditFace *) l->first; + + while(f) { + nextf= f->next; + if( faceselectedOR(f, SELECT) ) { + BLI_remlink(l, f); + free_editface(f); + } + f = nextf; + } +} + +static void erase_vertices(ListBase *l) +{ + EditVert *v, *nextv; + + v = (EditVert *) l->first; + while(v) { + nextv= v->next; + if(v->f & 1) { + BLI_remlink(l, v); + free_editvert(v); + } + v = nextv; + } +} + +void delete_mesh(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa, *nextvl; + EditVert *eve,*nextve; + EditEdge *eed,*nexted; + short event; + int count; + char *str="Erase"; + + TEST_EDITMESH + if(multires_test()) return; + + event= pupmenu("Erase %t|Vertices%x10|Edges%x1|Faces%x2|All%x3|Edges & Faces%x4|Only Faces%x5|Edge Loop%x6"); + if(event<1) return; + + if(event==10 ) { + str= "Erase Vertices"; + erase_edges(&em->edges); + erase_faces(&em->faces); + erase_vertices(&em->verts); + } + else if(event==6) { + if(!EdgeLoopDelete()) + return; + + str= "Erase Edge Loop"; + } + else if(event==4) { + str= "Erase Edges & Faces"; + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + /* delete only faces with 1 or more edges selected */ + count= 0; + if(efa->e1->f & SELECT) count++; + if(efa->e2->f & SELECT) count++; + if(efa->e3->f & SELECT) count++; + if(efa->e4 && (efa->e4->f & SELECT)) count++; + if(count) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f & SELECT) { + remedge(eed); + free_editedge(eed); + } + eed= nexted; + } + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + event=0; + if( efa->v1->f & SELECT) event++; + if( efa->v2->f & SELECT) event++; + if( efa->v3->f & SELECT) event++; + if(efa->v4 && (efa->v4->f & SELECT)) event++; + + if(event>1) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + } + else if(event==1) { + str= "Erase Edges"; + // faces first + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + event=0; + if( efa->e1->f & SELECT) event++; + if( efa->e2->f & SELECT) event++; + if( efa->e3->f & SELECT) event++; + if(efa->e4 && (efa->e4->f & SELECT)) event++; + + if(event) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f & SELECT) { + remedge(eed); + free_editedge(eed); + } + eed= nexted; + } + /* to remove loose vertices: */ + eed= em->edges.first; + while(eed) { + if( eed->v1->f & SELECT) eed->v1->f-=SELECT; + if( eed->v2->f & SELECT) eed->v2->f-=SELECT; + eed= eed->next; + } + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f & SELECT) { + BLI_remlink(&em->verts,eve); + free_editvert(eve); + } + eve= nextve; + } + + } + else if(event==2) { + str="Erase Faces"; + delfaceflag(SELECT); + } + else if(event==3) { + str= "Erase All"; + if(em->verts.first) free_vertlist(&em->verts); + if(em->edges.first) free_edgelist(&em->edges); + if(em->faces.first) free_facelist(&em->faces); + if(em->selected.first) BLI_freelistN(&(em->selected)); + } + else if(event==5) { + str= "Erase Only Faces"; + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f & SELECT) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + } + + EM_fgon_flags(); // redo flags and indices for fgons + + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push(str); +} + + +/* Got this from scanfill.c. You will need to juggle around the + * callbacks for the scanfill.c code a bit for this to work. */ +void fill_mesh(void) +{ + EditMesh *em = G.editMesh; + EditVert *eve,*v1; + EditEdge *eed,*e1,*nexted; + EditFace *efa,*nextvl, *efan; + short ok; + + if(G.obedit==0 || (G.obedit->type!=OB_MESH)) return; + if(multires_test()) return; + + waitcursor(1); + + /* copy all selected vertices */ + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + v1= BLI_addfillvert(eve->co); + eve->tmp.v= v1; + v1->tmp.v= eve; + v1->xs= 0; // used for counting edges + } + eve= eve->next; + } + /* copy all selected edges */ + eed= em->edges.first; + while(eed) { + if( (eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) { + e1= BLI_addfilledge(eed->v1->tmp.v, eed->v2->tmp.v); + e1->v1->xs++; + e1->v2->xs++; + } + eed= eed->next; + } + /* from all selected faces: remove vertices and edges to prevent doubles */ + /* all edges add values, faces subtract, + then remove edges with vertices ->xs<2 */ + efa= em->faces.first; + ok= 0; + while(efa) { + nextvl= efa->next; + if( faceselectedAND(efa, 1) ) { + efa->v1->tmp.v->xs--; + efa->v2->tmp.v->xs--; + efa->v3->tmp.v->xs--; + if(efa->v4) efa->v4->tmp.v->xs--; + ok= 1; + + } + efa= nextvl; + } + if(ok) { /* there are faces selected */ + eed= filledgebase.first; + while(eed) { + nexted= eed->next; + if(eed->v1->xs<2 || eed->v2->xs<2) { + BLI_remlink(&filledgebase,eed); + } + eed= nexted; + } + } + + if(BLI_edgefill(0, (G.obedit && G.obedit->actcol)?(G.obedit->actcol-1):0)) { + efa= fillfacebase.first; + while(efa) { + /* normals default pointing up */ + efan= addfacelist(efa->v3->tmp.v, efa->v2->tmp.v, + efa->v1->tmp.v, 0, NULL, NULL); + if(efan) EM_select_face(efan, 1); + efa= efa->next; + } + } + + BLI_end_edgefill(); + + waitcursor(0); + EM_select_flush(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Fill"); +} +/*GB*/ +/*-------------------------------------------------------------------------------*/ +/*--------------------------- Edge Based Subdivide ------------------------------*/ + +#define EDGENEW 2 +#define FACENEW 2 +#define EDGEINNER 4 +#define EDGEOLD 8 + +/*used by faceloop cut to select only edges valid for edge slide*/ +#define DOUBLEOPFILL 16 + +/* calculates offset for co, based on fractal, sphere or smooth settings */ +static void alter_co(float *co, EditEdge *edge, float rad, int beauty, float perc) +{ + float vec1[3], fac; + + if(beauty & B_SMOOTH) { + /* we calculate an offset vector vec1[], to be added to *co */ + float len, fac, nor[3], nor1[3], nor2[3]; + + VecSubf(nor, edge->v1->co, edge->v2->co); + len= 0.5f*Normalize(nor); + + VECCOPY(nor1, edge->v1->no); + VECCOPY(nor2, edge->v2->no); + + /* cosine angle */ + fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ; + + vec1[0]= fac*nor1[0]; + vec1[1]= fac*nor1[1]; + vec1[2]= fac*nor1[2]; + + /* cosine angle */ + fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ; + + vec1[0]+= fac*nor2[0]; + vec1[1]+= fac*nor2[1]; + vec1[2]+= fac*nor2[2]; + + vec1[0]*= rad*len; + vec1[1]*= rad*len; + vec1[2]*= rad*len; + + co[0] += vec1[0]; + co[1] += vec1[1]; + co[2] += vec1[2]; + } + else { + if(rad > 0.0) { /* subdivide sphere */ + Normalize(co); + co[0]*= rad; + co[1]*= rad; + co[2]*= rad; + } + else if(rad< 0.0) { /* fractal subdivide */ + fac= rad* VecLenf(edge->v1->co, edge->v2->co); + vec1[0]= fac*(float)(0.5-BLI_drand()); + vec1[1]= fac*(float)(0.5-BLI_drand()); + vec1[2]= fac*(float)(0.5-BLI_drand()); + VecAddf(co, co, vec1); + } + + } +} + +/* assumes in the edge is the correct interpolated vertices already */ +/* percent defines the interpolation, rad and beauty are for special options */ +/* results in new vertex with correct coordinate, vertex normal and weight group info */ +static EditVert *subdivide_edge_addvert(EditEdge *edge, float rad, int beauty, float percent) +{ + EditVert *ev; + float co[3]; + + co[0] = (edge->v2->co[0]-edge->v1->co[0])*percent + edge->v1->co[0]; + co[1] = (edge->v2->co[1]-edge->v1->co[1])*percent + edge->v1->co[1]; + co[2] = (edge->v2->co[2]-edge->v1->co[2])*percent + edge->v1->co[2]; + + /* offset for smooth or sphere or fractal */ + alter_co(co, edge, rad, beauty, percent); + + /* clip if needed by mirror modifier */ + if (edge->v1->f2) { + if ( edge->v1->f2 & edge->v2->f2 & 1) { + co[0]= 0.0f; + } + if ( edge->v1->f2 & edge->v2->f2 & 2) { + co[1]= 0.0f; + } + if ( edge->v1->f2 & edge->v2->f2 & 4) { + co[2]= 0.0f; + } + } + + ev = addvertlist(co, NULL); + + /* vert data (vgroups, ..) */ + EM_data_interp_from_verts(edge->v1, edge->v2, ev, percent); + + /* normal */ + ev->no[0] = (edge->v2->no[0]-edge->v1->no[0])*percent + edge->v1->no[0]; + ev->no[1] = (edge->v2->no[1]-edge->v1->no[1])*percent + edge->v1->no[1]; + ev->no[2] = (edge->v2->no[2]-edge->v1->no[2])*percent + edge->v1->no[2]; + Normalize(ev->no); + + return ev; +} + +static void flipvertarray(EditVert** arr, short size) +{ + EditVert *hold; + int i; + + for(i=0; i<size/2; i++) { + hold = arr[i]; + arr[i] = arr[size-i-1]; + arr[size-i-1] = hold; + } +} + +static void facecopy(EditFace *source, EditFace *target) +{ + EditMesh *em= G.editMesh; + float *v1 = source->v1->co, *v2 = source->v2->co, *v3 = source->v3->co; + float *v4 = source->v4? source->v4->co: NULL; + float w[4][4]; + + CustomData_em_copy_data(&em->fdata, &em->fdata, source->data, &target->data); + + target->mat_nr = source->mat_nr; + target->flag = source->flag; + target->h = source->h; + + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v1->co, w[0]); + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v2->co, w[1]); + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v3->co, w[2]); + if (target->v4) + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v4->co, w[3]); + + CustomData_em_interp(&em->fdata, &source->data, NULL, (float*)w, 1, target->data); +} + +static void fill_quad_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype) +{ + EditEdge *cedge=NULL; + EditVert *v[4], **verts; + EditFace *hold; + short start=0, end, left, right, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + | | + | | + | | + ------------- + left right + + where start,end,left, right are indexes of EditFace->v1, etc (stored in v) + and 0,1,2... are the indexes of the new verts stored in verts + + We will fill this case like this or this depending on even or odd cuts + + |---*---*---| |---*---| + | / \ | | / \ | + | / \ | | / \ | + |/ \| |/ \| + ------------- --------- + */ + + // Make center face + if(vertsize % 2 == 0) { + hold = addfacelist(verts[(vertsize-1)/2],verts[((vertsize-1)/2)+1],v[left],v[right], NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e4->f2 |= EDGEINNER; + }else{ + hold = addfacelist(verts[(vertsize-1)/2],v[left],v[right],NULL, NULL,NULL); + hold->e1->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + } + facecopy(efa,hold); + + // Make side faces + for(i=0;i<(vertsize-1)/2;i++) { + hold = addfacelist(verts[i],verts[i+1],v[right],NULL,NULL,NULL); + facecopy(efa,hold); + if(i+1 != (vertsize-1)/2) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e2->f2 |= EDGEINNER; + } + } + hold = addfacelist(verts[vertsize-2-i],verts[vertsize-1-i],v[left],NULL,NULL,NULL); + facecopy(efa,hold); + if(i+1 != (vertsize-1)/2) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e3->f2 |= EDGEINNER; + } + } + } +} + +static void fill_tri_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype) +{ + EditEdge *cedge=NULL; + EditVert *v[3], **verts; + EditFace *hold; + short start=0, end, op, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} + end = (start+1)%3; + op = (start+2)%3; + + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + \ | + \ | + \ | + \ | + \ | + \ | + |op + + where start,end,op are indexes of EditFace->v1, etc (stored in v) + and 0,1,2... are the indexes of the new verts stored in verts + + We will fill this case like this or this depending on even or odd cuts + + 3 2 1 0 + |---*---*---| + \ \ \ | + \ \ \ | + \ \ \ | + \ \ \| + \ \\| + \ | + |op + */ + + // Make side faces + for(i=0;i<(vertsize-1);i++) { + hold = addfacelist(verts[i],verts[i+1],v[op],NULL,NULL,NULL); + if(i+1 != vertsize-1) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e2->f2 |= EDGEINNER; + } + } + facecopy(efa,hold); + } +} + +static void fill_quad_double_op(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], **verts[2]; + EditFace *hold; + short start=0, end, left, right, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge[0] = efa->e1; cedge[1] = efa->e3; start = 0;} + else if(efa->e2->f & SELECT) { cedge[0] = efa->e2; cedge[1] = efa->e4; start = 1;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + if(verts[1][0] != v[left]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + | | + | | + | | + |---*---*---| + 0 1 2 3 + left right + + We will fill this case like this or this depending on even or odd cuts + + |---*---*---| + | | | | + | | | | + | | | | + |---*---*---| + */ + + // Make side faces + for(i=0;i<vertsize-1;i++) { + hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-2-i],verts[1][vertsize-1-i],NULL,NULL); + if(i < vertsize-2) { + hold->e2->f2 |= EDGEINNER; + hold->e2->f2 |= DOUBLEOPFILL; + } + facecopy(efa,hold); + } +} + +static void fill_quad_double_adj_path(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------| + + We will fill this case like this or this depending on even or odd cuts + |---*---*---| + | / / / | + * / / | + | / / | + * / | + | / | + |-----------| + */ + + // Make outside tris + hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + /* when ctrl is depressed, only want verts on the cutline selected */ + if (G.qual != LR_CTRLKEY) + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + hold = addfacelist(verts[0][0],verts[1][vertsize-1],v[(start2+2)%4],NULL,NULL,NULL); + /* when ctrl is depressed, only want verts on the cutline selected */ + if (G.qual != LR_CTRLKEY) + hold->e1->f2 |= EDGEINNER; + facecopy(efa,hold); + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + // Make side faces + + for(i=0;i<numcuts;i++) { + hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + } + //EM_fgon_flags(); + +} + +static void fill_quad_double_adj_fan(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], *op=NULL, **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} + + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------|op + + We will fill this case like this or this (warning horrible ascii art follows) + |---*---*---| + | \ \ \ | + *---\ \ \ | + | \ \ \ \| + *---- \ \ \ | + | --- \\\| + |-----------| + */ + + for(i=0;i<=numcuts;i++) { + hold = addfacelist(op,verts[1][numcuts-i],verts[1][numcuts-i+1],NULL,NULL,NULL); + hold->e1->f2 |= EDGEINNER; + facecopy(efa,hold); + + hold = addfacelist(op,verts[0][i],verts[0][i+1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + } +} + +static void fill_quad_double_adj_inner(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], *op=NULL, **verts[2],**inner; + EditFace *hold; + short start=0, start2=0, vertsize,i; + float co[3]; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} + + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------|op + + We will fill this case like this or this (warning horrible ascii art follows) + |---*-----*---| + | * / | + * \ / | + | * | + | / \ | + * \ | + | \ | + |-------------| + */ + + // Add Inner Vert(s) + inner = MEM_mallocN(sizeof(EditVert*)*numcuts,"New inner verts"); + + for(i=0;i<numcuts;i++) { + co[0] = (verts[0][numcuts-i]->co[0] + verts[1][i+1]->co[0] ) / 2 ; + co[1] = (verts[0][numcuts-i]->co[1] + verts[1][i+1]->co[1] ) / 2 ; + co[2] = (verts[0][numcuts-i]->co[2] + verts[1][i+1]->co[2] ) / 2 ; + inner[i] = addvertlist(co, NULL); + inner[i]->f2 |= EDGEINNER; + + EM_data_interp_from_verts(verts[0][numcuts-i], verts[1][i+1], inner[i], 0.5f); + } + + // Add Corner Quad + hold = addfacelist(verts[0][numcuts+1],verts[1][1],inner[0],verts[0][numcuts],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + // Add Bottom Quads + hold = addfacelist(verts[0][0],verts[0][1],inner[numcuts-1],op,NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + + hold = addfacelist(op,inner[numcuts-1],verts[1][numcuts],verts[1][numcuts+1],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + // Add Fill Quads (if # cuts > 1) + + for(i=0;i<numcuts-1;i++) { + hold = addfacelist(inner[i],verts[1][i+1],verts[1][i+2],inner[i+1],NULL,NULL); + hold->e1->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + + hold = addfacelist(inner[i],inner[i+1],verts[0][numcuts-1-i],verts[0][numcuts-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e4->f2 |= EDGEINNER; + facecopy(efa,hold); + + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + } + + //EM_fgon_flags(); + + MEM_freeN(inner); +} + +static void fill_tri_double(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[3], **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} + if(efa->e3->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e1; start = 2; start2 = 0;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | / + 1* / + | / + 2* / + | / + end2 3| + + We will fill this case like this or this depending on even or odd cuts + |---*---*---| + | / / / + * / / + | / / + * / + | / + | + */ + + // Make outside tri + hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + // Make side faces + + for(i=0;i<numcuts;i++) { + hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + } +} + +static void fill_quad_triple(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[3]={0}; + EditVert *v[4], **verts[3]; + EditFace *hold; + short start=0, start2=0, start3=0, vertsize, i, repeats; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(!(efa->e1->f & SELECT)) { + cedge[0] = efa->e2; + cedge[1] = efa->e3; + cedge[2] = efa->e4; + start = 1;start2 = 2;start3 = 3; + } + if(!(efa->e2->f & SELECT)) { + cedge[0] = efa->e3; + cedge[1] = efa->e4; + cedge[2] = efa->e1; + start = 2;start2 = 3;start3 = 0; + } + if(!(efa->e3->f & SELECT)) { + cedge[0] = efa->e4; + cedge[1] = efa->e1; + cedge[2] = efa->e2; + start = 3;start2 = 0;start3 = 1; + } + if(!(efa->e4->f & SELECT)) { + cedge[0] = efa->e1; + cedge[1] = efa->e2; + cedge[2] = efa->e3; + start = 0;start2 = 1;start3 = 2; + } + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + verts[2] = BLI_ghash_lookup(gh, cedge[2]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] != v[start3]) {flipvertarray(verts[2],numcuts+2);} + /* + We should have something like this now + + start2 + 3 2 1 0 + start3 0|---*---*---|3 + | | + 1* *2 + | | + 2* *1 + | | + 3|-----------|0 start + + We will fill this case like this or this depending on even or odd cuts + there are a couple of differences. For odd cuts, there is a tri in the + middle as well as 1 quad at the bottom (not including the extra quads + for odd cuts > 1 + + For even cuts, there is a quad in the middle and 2 quads on the bottom + + they are numbered here for clarity + + 1 outer tris and bottom quads + 2 inner tri or quad + 3 repeating quads + + |---*---*---*---| + |1/ / \ \ 1| + |/ 3 / \ 3 \| + * / 2 \ * + | / \ | + |/ \ | + *---------------* + | 3 | + | | + *---------------* + | | + | 1 | + | | + |---------------| + + |---*---*---*---*---| + | 1/ / \ \ 1| + | / / \ \ | + |/ 3 / \ 3 \| + * / \ * + | / \ | + | / 2 \ | + |/ \| + *-------------------* + | | + | 3 | + | | + *-------------------* + | | + | 1 | + | | + *-------------------* + | | + | 1 | + | | + |-------------------| + + */ + + // Make outside tris + hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + hold = addfacelist(verts[1][vertsize-2],verts[1][vertsize-1],verts[2][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + // Make bottom quad + hold = addfacelist(verts[0][0],verts[0][1],verts[2][vertsize-2],verts[2][vertsize-1],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + //If it is even cuts, add the 2nd lower quad + if(numcuts % 2 == 0) { + hold = addfacelist(verts[0][1],verts[0][2],verts[2][vertsize-3],verts[2][vertsize-2],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + // Also Make inner quad + hold = addfacelist(verts[1][numcuts/2],verts[1][(numcuts/2)+1],verts[2][numcuts/2],verts[0][(numcuts/2)+1],NULL,NULL); + hold->e3->f2 |= EDGEINNER; + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e3->h |= EM_FGON; + //} + facecopy(efa,hold); + repeats = (numcuts / 2) -1; + } else { + // Make inner tri + hold = addfacelist(verts[1][(numcuts/2)+1],verts[2][(numcuts/2)+1],verts[0][(numcuts/2)+1],NULL,NULL,NULL); + hold->e2->f2 |= EDGEINNER; + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e2->h |= EM_FGON; + //} + facecopy(efa,hold); + repeats = ((numcuts+1) / 2)-1; + } + + // cuts for 1 and 2 do not have the repeating quads + if(numcuts < 3) {repeats = 0;} + for(i=0;i<repeats;i++) { + //Make side repeating Quads + hold = addfacelist(verts[1][i+1],verts[1][i+2],verts[0][vertsize-i-3],verts[0][vertsize-i-2],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + hold = addfacelist(verts[1][vertsize-i-3],verts[1][vertsize-i-2],verts[2][i+1],verts[2][i+2],NULL,NULL); + hold->e4->f2 |= EDGEINNER; + facecopy(efa,hold); + } + // Do repeating bottom quads + for(i=0;i<repeats;i++) { + if(numcuts % 2 == 1) { + hold = addfacelist(verts[0][1+i],verts[0][2+i],verts[2][vertsize-3-i],verts[2][vertsize-2-i],NULL,NULL); + } else { + hold = addfacelist(verts[0][2+i],verts[0][3+i],verts[2][vertsize-4-i],verts[2][vertsize-3-i],NULL,NULL); + } + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + } + //EM_fgon_flags(); +} + +static void fill_quad_quadruple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty) +{ + EditVert **verts[4], ***innerverts; + EditFace *hold; + EditEdge temp; + short vertsize, i, j; + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, efa->e1); + verts[1] = BLI_ghash_lookup(gh, efa->e2); + verts[2] = BLI_ghash_lookup(gh, efa->e3); + verts[3] = BLI_ghash_lookup(gh, efa->e4); + + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] == efa->v3) {flipvertarray(verts[2],numcuts+2);} + if(verts[3][0] == efa->v4) {flipvertarray(verts[3],numcuts+2);} + /* + We should have something like this now + 1 + + 3 2 1 0 + 0|---*---*---|0 + | | + 1* *1 + 2 | | 4 + 2* *2 + | | + 3|---*---*---|3 + 3 2 1 0 + + 3 + // we will fill a 2 dim array of editvert*s to make filling easier + // the innervert order is shown + + 0 0---1---2---3 + | | | | + 1 0---1---2---3 + | | | | + 2 0---1---2---3 + | | | | + 3 0---1---2---3 + + */ + innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts outer array"); + for(i=0;i<numcuts+2;i++) { + innerverts[i] = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts inner array"); + } + + // first row is e1 last row is e3 + for(i=0;i<numcuts+2;i++) { + innerverts[0][i] = verts[0][(numcuts+1)-i]; + innerverts[numcuts+1][i] = verts[2][(numcuts+1)-i]; + } + + for(i=1;i<=numcuts;i++) { + /* we create a fake edge for the next loop */ + temp.v2 = innerverts[i][0] = verts[1][i]; + temp.v1 = innerverts[i][numcuts+1] = verts[3][i]; + + for(j=1;j<=numcuts;j++) { + float percent= (float)j/(float)(numcuts+1); + + innerverts[i][(numcuts+1)-j]= subdivide_edge_addvert(&temp, rad, beauty, percent); + } + } + // Fill with faces + for(i=0;i<numcuts+1;i++) { + for(j=0;j<numcuts+1;j++) { + hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],innerverts[i+1][j+1],NULL,NULL); + hold->e1->f2 = EDGENEW; + hold->e2->f2 = EDGENEW; + hold->e3->f2 = EDGENEW; + hold->e4->f2 = EDGENEW; + + if(i != 0) { hold->e1->f2 |= EDGEINNER; } + if(j != 0) { hold->e2->f2 |= EDGEINNER; } + if(i != numcuts) { hold->e3->f2 |= EDGEINNER; } + if(j != numcuts) { hold->e4->f2 |= EDGEINNER; } + + facecopy(efa,hold); + } + } + // Clean up our dynamic multi-dim array + for(i=0;i<numcuts+2;i++) { + MEM_freeN(innerverts[i]); + } + MEM_freeN(innerverts); +} + +static void fill_tri_triple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty) +{ + EditVert **verts[3], ***innerverts; + short vertsize, i, j; + EditFace *hold; + EditEdge temp; + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, efa->e1); + verts[1] = BLI_ghash_lookup(gh, efa->e2); + verts[2] = BLI_ghash_lookup(gh, efa->e3); + + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] != efa->v3) {flipvertarray(verts[2],numcuts+2);} + /* + We should have something like this now + 3 + + 3 2 1 0 + 0|---*---*---|3 + | / + 1 1* *2 + | / + 2* *1 2 + | / + 3|/ + 0 + + we will fill a 2 dim array of editvert*s to make filling easier + + 3 + + 0 0---1---2---3---4 + | / | / |/ | / + 1 0---1----2---3 + 1 | / | / | / + 2 0----1---2 2 + | / | / + |/ |/ + 3 0---1 + | / + |/ + 4 0 + + */ + + innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"tri-tri subdiv inner verts outer array"); + for(i=0;i<numcuts+2;i++) { + innerverts[i] = MEM_mallocN(sizeof(EditVert*)*((numcuts+2)-i),"tri-tri subdiv inner verts inner array"); + } + //top row is e3 backwards + for(i=0;i<numcuts+2;i++) { + innerverts[0][i] = verts[2][(numcuts+1)-i]; + } + + for(i=1;i<=numcuts+1;i++) { + //fake edge, first vert is from e1, last is from e2 + temp.v1= innerverts[i][0] = verts[0][i]; + temp.v2= innerverts[i][(numcuts+1)-i] = verts[1][(numcuts+1)-i]; + + for(j=1;j<(numcuts+1)-i;j++) { + float percent= (float)j/(float)((numcuts+1)-i); + + innerverts[i][((numcuts+1)-i)-j]= subdivide_edge_addvert(&temp, rad, beauty, 1-percent); + } + } + + // Now fill the verts with happy little tris :) + for(i=0;i<=numcuts+1;i++) { + for(j=0;j<(numcuts+1)-i;j++) { + //We always do the first tri + hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],NULL,NULL,NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + if(i != 0) { hold->e1->f2 |= EDGEINNER; } + if(j != 0) { hold->e2->f2 |= EDGEINNER; } + if(j+1 != (numcuts+1)-i) {hold->e3->f2 |= EDGEINNER;} + + facecopy(efa,hold); + //if there are more to come, we do the 2nd + if(j+1 <= numcuts-i) { + hold = addfacelist(innerverts[i+1][j],innerverts[i+1][j+1],innerverts[i][j+1],NULL,NULL,NULL); + facecopy(efa,hold); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + } + } + } + + // Clean up our dynamic multi-dim array + for(i=0;i<numcuts+2;i++) { + MEM_freeN(innerverts[i]); + } + MEM_freeN(innerverts); +} + +//Next two fill types are for knife exact only and are provided to allow for knifing through vertices +//This means there is no multicut! +static void fill_quad_doublevert(EditFace *efa, int v1, int v2){ + EditFace *hold; + /* + Depending on which two vertices have been knifed through (v1 and v2), we + triangulate like the patterns below. + X-------| |-------X + | \ | | / | + | \ | | / | + | \ | | / | + --------X X-------- + */ + + if(v1 == 1 && v2 == 3){ + hold= addfacelist(efa->v1, efa->v2, efa->v3, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(efa, hold); + + hold= addfacelist(efa->v1, efa->v3, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(efa, hold); + } + else{ + hold= addfacelist(efa->v1, efa->v2, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e2->f2 |= EDGEINNER; + facecopy(efa, hold); + + hold= addfacelist(efa->v2, efa->v3, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(efa, hold); + } +} + +static void fill_quad_singlevert(EditFace *efa, struct GHash *gh) +{ + EditEdge *cedge=NULL; + EditVert *v[4], **verts; + EditFace *hold; + short start=0, end, left, right, vertsize; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = 3; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,3);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + +/* + We should have something like this now + + end start + 2 1 0 + |-----*-----| + | | + | | + | | + ------------- + left right + + where start,end,left, right are indexes of EditFace->v1, etc (stored in v) + and 0,1,2 are the indexes of the new verts stored in verts. We fill like + this, depending on whether its vertex 'left' or vertex 'right' thats + been knifed through... + + |---*---| |---*---| + | / | | \ | + | / | | \ | + |/ | | \| + X-------- --------X +*/ + + if(v[left]->f1){ + //triangle is composed of cutvert, end and left + hold = addfacelist(verts[1],v[end],v[left],NULL, NULL,NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(efa, hold); + + //quad is composed of cutvert, left, right and start + hold = addfacelist(verts[1],v[left],v[right],v[start], NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e4->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(efa, hold); + } + else if(v[right]->f1){ + //triangle is composed of cutvert, right and start + hold = addfacelist(verts[1],v[right],v[start], NULL, NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(efa, hold); + //quad is composed of cutvert, end, left, right + hold = addfacelist(verts[1],v[end], v[left], v[right], NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e4->f2 |= EDGENEW; + hold->e4->f2 |= EDGEINNER; + facecopy(efa, hold); + } + +} + +// This function takes an example edge, the current point to create and +// the total # of points to create, then creates the point and return the +// editvert pointer to it. +static EditVert *subdivideedgenum(EditEdge *edge, int curpoint, int totpoint, float rad, int beauty) +{ + EditVert *ev; + float percent; + + if (beauty & (B_PERCENTSUBD) && totpoint == 1) + //percent=(float)(edge->tmp.l)/32768.0f; + percent= edge->tmp.fp; + else + percent= (float)curpoint/(float)(totpoint+1); + + ev= subdivide_edge_addvert(edge, rad, beauty, percent); + ev->f = edge->v1->f; + + return ev; +} + +#if 1 +#include "bmesh.h" + +void esubdivideflag(int flag, float rad, int beauty, int numcuts, int seltype) +{ + BMesh *bm; + BMOperator subdop, conv; + EditMesh *em = G.editMesh; + BMEdge **list, *bed; + BMIter iter; + int tot; + + /*convert from editmesh*/ + bm = editmesh_to_bmesh(G.editMesh); + + BMO_Init_Op(&subdop, BMOP_ESUBDIVIDE); + for (tot=0, bed=BMIter_New(&iter, bm, BM_EDGES, NULL); bed; bed=BMIter_Step(&iter)) { + if (BM_Is_Selected(bm, bed)) tot++; + } + + list = MEM_callocN(sizeof(void*)*tot, "vert ptr list"); + + for (tot=0, bed=BMIter_New(&iter, bm, BM_EDGES, NULL); bed; bed=BMIter_Step(&iter)) { + if (BM_Is_Selected(bm, bed)) list[tot++] = bed; + } + + BMO_Set_PntBuf(&subdop, BMOP_ESUBDIVIDE_EDGES, list, tot); + BMO_Exec_Op(bm, &subdop); + BMO_Finish_Op(bm, &subdop); + + free_editMesh(G.editMesh); + bmesh_to_editmesh(bm); + BM_Free_Mesh(bm); + + if (list) MEM_freeN(list); +} +#else +void esubdivideflag(int flag, float rad, int beauty, int numcuts, int seltype) +{ + EditMesh *em = G.editMesh; + EditFace *ef; + EditEdge *eed, *cedge, *sort[4]; + EditVert *eve, **templist; + struct GHash *gh; + float length[4], v1mat[3], v2mat[3], v3mat[3], v4mat[3]; + int i, j, edgecount, touchcount, facetype,hold; + ModifierData *md= G.obedit->modifiers.first; + + if(multires_test()) return; + + //Set faces f1 to 0 cause we need it later + for(ef=em->faces.first;ef;ef = ef->next) ef->f1 = 0; + for(eve=em->verts.first; eve; eve=eve->next) { + if(!(beauty & B_KNIFE)) /* knife sets this flag for vertex cuts */ + eve->f1 = 0; + eve->f2 = 0; + } + + for (; md; md=md->next) { + if (md->type==eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->flag & MOD_MIR_CLIPPING) { + for (eve= em->verts.first; eve; eve= eve->next) { + eve->f2= 0; + switch(mmd->axis){ + case 0: + if (fabs(eve->co[0]) < mmd->tolerance) + eve->f2 |= 1; + break; + case 1: + if (fabs(eve->co[1]) < mmd->tolerance) + eve->f2 |= 2; + break; + case 2: + if (fabs(eve->co[2]) < mmd->tolerance) + eve->f2 |= 4; + break; + } + } + } + } + } + + //Flush vertex flags upward to the edges + for(eed = em->edges.first;eed;eed = eed->next) { + //if(eed->f & flag && eed->v1->f == eed->v2->f) { + // eed->f |= eed->v1->f; + // } + eed->f2 = 0; + if(eed->f & flag) { + eed->f2 |= EDGEOLD; + } + } + + // We store an array of verts for each edge that is subdivided, + // we put this array as a value in a ghash which is keyed by the EditEdge* + + // Now for beauty subdivide deselect edges based on length + if(beauty & B_BEAUTY) { + for(ef = em->faces.first;ef;ef = ef->next) { + if(!ef->v4) { + continue; + } + if(ef->f & SELECT) { + VECCOPY(v1mat, ef->v1->co); + VECCOPY(v2mat, ef->v2->co); + VECCOPY(v3mat, ef->v3->co); + VECCOPY(v4mat, ef->v4->co); + Mat4Mul3Vecfl(G.obedit->obmat, v1mat); + Mat4Mul3Vecfl(G.obedit->obmat, v2mat); + Mat4Mul3Vecfl(G.obedit->obmat, v3mat); + Mat4Mul3Vecfl(G.obedit->obmat, v4mat); + + length[0] = VecLenf(v1mat, v2mat); + length[1] = VecLenf(v2mat, v3mat); + length[2] = VecLenf(v3mat, v4mat); + length[3] = VecLenf(v4mat, v1mat); + sort[0] = ef->e1; + sort[1] = ef->e2; + sort[2] = ef->e3; + sort[3] = ef->e4; + + + // Beauty Short Edges + if(beauty & B_BEAUTY_SHORT) { + for(j=0;j<2;j++) { + hold = -1; + for(i=0;i<4;i++) { + if(length[i] < 0) { + continue; + } else if(hold == -1) { + hold = i; + } else { + if(length[hold] < length[i]) { + hold = i; + } + } + } + sort[hold]->f &= ~SELECT; + sort[hold]->f2 |= EDGENEW; + length[hold] = -1; + } + } + + // Beauty Long Edges + else { + for(j=0;j<2;j++) { + hold = -1; + for(i=0;i<4;i++) { + if(length[i] < 0) { + continue; + } else if(hold == -1) { + hold = i; + } else { + if(length[hold] > length[i]) { + hold = i; + } + } + } + sort[hold]->f &= ~SELECT; + sort[hold]->f2 |= EDGENEW; + length[hold] = -1; + } + } + } + } + } + + gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + // If we are knifing, We only need the selected edges that were cut, so deselect if it was not cut + if(beauty & B_KNIFE) { + for(eed= em->edges.first;eed;eed=eed->next) { + if( eed->tmp.fp == 0 ) { + EM_select_edge(eed,0); + } + } + } + // So for each edge, if it is selected, we allocate an array of size cuts+2 + // so we can have a place for the v1, the new verts and v2 + for(eed=em->edges.first;eed;eed = eed->next) { + if(eed->f & flag) { + templist = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"vertlist"); + templist[0] = eed->v1; + for(i=0;i<numcuts;i++) { + // This function creates the new vert and returns it back + // to the array + templist[i+1] = subdivideedgenum(eed, i+1, numcuts, rad, beauty); + //while we are here, we can copy edge info from the original edge + cedge = addedgelist(templist[i],templist[i+1],eed); + // Also set the edge f2 to EDGENEW so that we can use this info later + cedge->f2 = EDGENEW; + } + templist[i+1] = eed->v2; + //Do the last edge too + cedge = addedgelist(templist[i],templist[i+1],eed); + cedge->f2 = EDGENEW; + // Now that the edge is subdivided, we can put its verts in the ghash + BLI_ghash_insert(gh, eed, templist); + } + } + + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + // Now for each face in the mesh we need to figure out How many edges were cut + // and which filling method to use for that face + for(ef = em->faces.first;ef;ef = ef->next) { + edgecount = 0; + facetype = 3; + if(ef->e1->f & flag) {edgecount++;} + if(ef->e2->f & flag) {edgecount++;} + if(ef->e3->f & flag) {edgecount++;} + if(ef->v4) { + facetype = 4; + if(ef->e4->f & flag) {edgecount++;} + } + if(facetype == 4) { + switch(edgecount) { + case 0: + if(beauty & B_KNIFE && numcuts == 1){ + /*Test for when knifing through two opposite verts but no edges*/ + touchcount = 0; + if(ef->v1->f1) touchcount++; + if(ef->v2->f1) touchcount++; + if(ef->v3->f1) touchcount++; + if(ef->v4->f1) touchcount++; + if(touchcount == 2){ + if(ef->v1->f1 && ef->v3->f1){ + ef->f1 = SELECT; + fill_quad_doublevert(ef, 1, 3); + } + else if(ef->v2->f1 && ef->v4->f1){ + ef->f1 = SELECT; + fill_quad_doublevert(ef, 2, 4); + } + } + } + break; + + case 1: + if(beauty & B_KNIFE && numcuts == 1){ + /*Test for when knifing through an edge and one vert*/ + touchcount = 0; + if(ef->v1->f1) touchcount++; + if(ef->v2->f1) touchcount++; + if(ef->v3->f1) touchcount++; + if(ef->v4->f1) touchcount++; + + if(touchcount == 1){ + if( (ef->e1->f & flag && ( !ef->e1->v1->f1 && !ef->e1->v2->f1 )) || + (ef->e2->f & flag && ( !ef->e2->v1->f1 && !ef->e2->v2->f1 )) || + (ef->e3->f & flag && ( !ef->e3->v1->f1 && !ef->e3->v2->f1 )) || + (ef->e4->f & flag && ( !ef->e4->v1->f1 && !ef->e4->v2->f1 )) ){ + + ef->f1 = SELECT; + fill_quad_singlevert(ef, gh); + } + else{ + ef->f1 = SELECT; + fill_quad_single(ef, gh, numcuts, seltype); + } + } + else{ + ef->f1 = SELECT; + fill_quad_single(ef, gh, numcuts, seltype); + } + } + else{ + ef->f1 = SELECT; + fill_quad_single(ef, gh, numcuts, seltype); + } + break; + case 2: ef->f1 = SELECT; + // if there are 2, we check if edge 1 and 3 are either both on or off that way + // we can tell if the selected pair is Adjacent or Opposite of each other + if((ef->e1->f & flag && ef->e3->f & flag) || + (ef->e2->f & flag && ef->e4->f & flag)) { + fill_quad_double_op(ef, gh, numcuts); + }else{ + switch(G.scene->toolsettings->cornertype) { + case 0: fill_quad_double_adj_path(ef, gh, numcuts); break; + case 1: fill_quad_double_adj_inner(ef, gh, numcuts); break; + case 2: fill_quad_double_adj_fan(ef, gh, numcuts); break; + } + + } + break; + case 3: ef->f1 = SELECT; + fill_quad_triple(ef, gh, numcuts); + break; + case 4: ef->f1 = SELECT; + fill_quad_quadruple(ef, gh, numcuts, rad, beauty); + break; + } + } else { + switch(edgecount) { + case 0: break; + case 1: ef->f1 = SELECT; + fill_tri_single(ef, gh, numcuts, seltype); + break; + case 2: ef->f1 = SELECT; + fill_tri_double(ef, gh, numcuts); + break; + case 3: ef->f1 = SELECT; + fill_tri_triple(ef, gh, numcuts, rad, beauty); + break; + } + } + } + + // Delete Old Edges and Faces + for(eed = em->edges.first;eed;eed = eed->next) { + if(BLI_ghash_haskey(gh,eed)) { + eed->f1 = SELECT; + } else { + eed->f1 = 0; + } + } + free_tagged_edges_faces(em->edges.first, em->faces.first); + + if(seltype == SUBDIV_SELECT_ORIG && G.qual != LR_CTRLKEY) { + /* bugfix: vertex could get flagged as "not-selected" + // solution: clear flags before, not at the same time as setting SELECT flag -dg + */ + for(eed = em->edges.first;eed;eed = eed->next) { + if(!(eed->f2 & EDGENEW || eed->f2 & EDGEOLD)) { + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & EDGENEW || eed->f2 & EDGEOLD) { + eed->f |= flag; + EM_select_edge(eed,1); + } + } + } else if ((seltype == SUBDIV_SELECT_INNER || seltype == SUBDIV_SELECT_INNER_SEL)|| G.qual == LR_CTRLKEY) { + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & EDGEINNER) { + eed->f |= flag; + EM_select_edge(eed,1); + if(eed->v1->f & EDGEINNER) eed->v1->f |= SELECT; + if(eed->v2->f & EDGEINNER) eed->v2->f |= SELECT; + }else{ + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + } else if(seltype == SUBDIV_SELECT_LOOPCUT){ + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & DOUBLEOPFILL){ + eed->f |= flag; + EM_select_edge(eed,1); + }else{ + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + } + if(G.scene->selectmode & SCE_SELECT_VERTEX) { + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f & SELECT) { + eed->v1->f |= SELECT; + eed->v2->f |= SELECT; + } + } + } + + //fix hide flags for edges. First pass, hide edges of hidden faces + for(ef=em->faces.first; ef; ef=ef->next){ + if(ef->h){ + ef->e1->h |= 1; + ef->e2->h |= 1; + ef->e3->h |= 1; + if(ef->e4) ef->e4->h |= 1; + } + } + //second pass: unhide edges of visible faces adjacent to hidden faces + for(ef=em->faces.first; ef; ef=ef->next){ + if(ef->h == 0){ + ef->e1->h &= ~1; + ef->e2->h &= ~1; + ef->e3->h &= ~1; + if(ef->e4) ef->e4->h &= ~1; + } + } + + // Free the ghash and call MEM_freeN on all the value entries to return + // that memory + BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN); + + EM_selectmode_flush(); + for(ef=em->faces.first;ef;ef = ef->next) { + if(ef->e4) { + if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && + (ef->e3->f & SELECT && ef->e4->f & SELECT) ) { + ef->f |= SELECT; + } + } else { + if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && ef->e3->f & SELECT) { + ef->f |= SELECT; + } + } + } + + recalc_editnormals(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} +#endif + +static int count_selected_edges(EditEdge *ed) +{ + int totedge = 0; + while(ed) { + ed->tmp.p = 0; + if( ed->f & SELECT ) totedge++; + ed= ed->next; + } + return totedge; +} + +/* hurms, as if this makes code readable! It's pointerpointer hiding... (ton) */ +typedef EditFace *EVPtr; +typedef EVPtr EVPTuple[2]; + +/** builds EVPTuple array efaa of face tuples (in fact pointers to EditFaces) + sharing one edge. + arguments: selected edge list, face list. + Edges will also be tagged accordingly (see eed->f2) */ + +static int collect_quadedges(EVPTuple *efaa, EditEdge *eed, EditFace *efa) +{ + EditEdge *e1, *e2, *e3; + EVPtr *evp; + int i = 0; + + /* run through edges, if selected, set pointer edge-> facearray */ + while(eed) { + eed->f2= 0; + eed->f1= 0; + if( eed->f & SELECT ) { + eed->tmp.p = (EditVert *) (&efaa[i]); + i++; + } + else eed->tmp.p = NULL; + + eed= eed->next; + } + + + /* find edges pointing to 2 faces by procedure: + + - run through faces and their edges, increase + face counter e->f1 for each face + */ + + while(efa) { + efa->f1= 0; + if(efa->v4==0 && (efa->f & SELECT)) { /* if selected triangle */ + e1= efa->e1; + e2= efa->e2; + e3= efa->e3; + if(e1->f2<3 && e1->tmp.p) { + if(e1->f2<2) { + evp= (EVPtr *) e1->tmp.p; + evp[(int)e1->f2] = efa; + } + e1->f2+= 1; + } + if(e2->f2<3 && e2->tmp.p) { + if(e2->f2<2) { + evp= (EVPtr *) e2->tmp.p; + evp[(int)e2->f2]= efa; + } + e2->f2+= 1; + } + if(e3->f2<3 && e3->tmp.p) { + if(e3->f2<2) { + evp= (EVPtr *) e3->tmp.p; + evp[(int)e3->f2]= efa; + } + e3->f2+= 1; + } + } + else { + /* set to 3 to make sure these are not flipped or joined */ + efa->e1->f2= 3; + efa->e2->f2= 3; + efa->e3->f2= 3; + if (efa->e4) efa->e4->f2= 3; + } + + efa= efa->next; + } + return i; +} + + +/* returns vertices of two adjacent triangles forming a quad + - can be righthand or lefthand + + 4-----3 + |\ | + | \ 2 | <- efa1 + | \ | + efa-> | 1 \ | + | \| + 1-----2 + +*/ +#define VTEST(face, num, other) \ + (face->v##num != other->v1 && face->v##num != other->v2 && face->v##num != other->v3) + +static void givequadverts(EditFace *efa, EditFace *efa1, EditVert **v1, EditVert **v2, EditVert **v3, EditVert **v4, int *vindex) +{ + if VTEST(efa, 1, efa1) { + *v1= efa->v1; + *v2= efa->v2; + vindex[0]= 0; + vindex[1]= 1; + } + else if VTEST(efa, 2, efa1) { + *v1= efa->v2; + *v2= efa->v3; + vindex[0]= 1; + vindex[1]= 2; + } + else if VTEST(efa, 3, efa1) { + *v1= efa->v3; + *v2= efa->v1; + vindex[0]= 2; + vindex[1]= 0; + } + + if VTEST(efa1, 1, efa) { + *v3= efa1->v1; + *v4= efa1->v2; + vindex[2]= 0; + vindex[3]= 1; + } + else if VTEST(efa1, 2, efa) { + *v3= efa1->v2; + *v4= efa1->v3; + vindex[2]= 1; + vindex[3]= 2; + } + else if VTEST(efa1, 3, efa) { + *v3= efa1->v3; + *v4= efa1->v1; + vindex[2]= 2; + vindex[3]= 0; + } + else + *v3= *v4= NULL; +} + +/* Helper functions for edge/quad edit features*/ +static void untag_edges(EditFace *f) +{ + f->e1->f1 = 0; + f->e2->f1 = 0; + f->e3->f1 = 0; + if (f->e4) f->e4->f1 = 0; +} + +/** remove and free list of tagged edges and faces */ +static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa) +{ + EditMesh *em= G.editMesh; + EditEdge *nexted; + EditFace *nextvl; + + while(efa) { + nextvl= efa->next; + if(efa->f1) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + else + /* avoid deleting edges that are still in use */ + untag_edges(efa); + efa= nextvl; + } + + while(eed) { + nexted= eed->next; + if(eed->f1) { + remedge(eed); + free_editedge(eed); + } + eed= nexted; + } +} + +/* note; the EM_selectmode_set() calls here illustrate how badly constructed it all is... from before the + edge/face flags, with very mixed results.... */ +void beauty_fill(void) +{ + EditMesh *em = G.editMesh; + EditVert *v1, *v2, *v3, *v4; + EditEdge *eed, *nexted; + EditEdge dia1, dia2; + EditFace *efa, *w; + // void **efaar, **efaa; + EVPTuple *efaar; + EVPtr *efaa; + float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2; + int totedge, ok, notbeauty=8, onedone, vindex[4]; + + if(multires_test()) return; + + /* - all selected edges with two faces + * - find the faces: store them in edges (using datablock) + * - per edge: - test convex + * - test edge: flip? + * - if true: remedge, addedge, all edges at the edge get new face pointers + */ + + EM_selectmode_set(); // makes sure in selectmode 'face' the edges of selected faces are selected too + + totedge = count_selected_edges(em->edges.first); + if(totedge==0) return; + + /* temp block with face pointers */ + efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "beautyfill"); + + while (notbeauty) { + notbeauty--; + + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + + /* there we go */ + onedone= 0; + + eed= em->edges.first; + while(eed) { + nexted= eed->next; + + /* f2 is set in collect_quadedges() */ + if(eed->f2==2 && eed->h==0) { + + efaa = (EVPtr *) eed->tmp.p; + + /* none of the faces should be treated before, nor be part of fgon */ + ok= 1; + efa= efaa[0]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + if(efa->fgonf) ok= 0; + efa= efaa[1]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + if(efa->fgonf) ok= 0; + + if(ok) { + /* test convex */ + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if(v1 && v2 && v3 && v4) { + if( convex(v1->co, v2->co, v3->co, v4->co) ) { + + /* test edges */ + if( (v1) > (v3) ) { + dia1.v1= v3; + dia1.v2= v1; + } + else { + dia1.v1= v1; + dia1.v2= v3; + } + + if( (v2) > (v4) ) { + dia2.v1= v4; + dia2.v2= v2; + } + else { + dia2.v1= v2; + dia2.v2= v4; + } + + /* testing rule: + * the area divided by the total edge lengths + */ + + len1= VecLenf(v1->co, v2->co); + len2= VecLenf(v2->co, v3->co); + len3= VecLenf(v3->co, v4->co); + len4= VecLenf(v4->co, v1->co); + len5= VecLenf(v1->co, v3->co); + len6= VecLenf(v2->co, v4->co); + + opp1= AreaT3Dfl(v1->co, v2->co, v3->co); + opp2= AreaT3Dfl(v1->co, v3->co, v4->co); + + fac1= opp1/(len1+len2+len5) + opp2/(len3+len4+len5); + + opp1= AreaT3Dfl(v2->co, v3->co, v4->co); + opp2= AreaT3Dfl(v2->co, v4->co, v1->co); + + fac2= opp1/(len2+len3+len6) + opp2/(len4+len1+len6); + + ok= 0; + if(fac1 > fac2) { + if(dia2.v1==eed->v1 && dia2.v2==eed->v2) { + eed->f1= 1; + efa= efaa[0]; + efa->f1= 1; + efa= efaa[1]; + efa->f1= 1; + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[0], vindex[1], 4+vindex[2], -1); + w->f |= SELECT; + + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[0], 4+vindex[2], 4+vindex[3], -1); + w->f |= SELECT; + + onedone= 1; + } + } + else if(fac1 < fac2) { + if(dia1.v1==eed->v1 && dia1.v2==eed->v2) { + eed->f1= 1; + efa= efaa[0]; + efa->f1= 1; + efa= efaa[1]; + efa->f1= 1; + + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[1], 4+vindex[2], 4+vindex[3], -1); + w->f |= SELECT; + + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[0], 4+vindex[1], 4+vindex[3], -1); + w->f |= SELECT; + + onedone= 1; + } + } + } + } + } + + } + eed= nexted; + } + + free_tagged_edges_faces(em->edges.first, em->faces.first); + + if(onedone==0) break; + + EM_selectmode_set(); // new edges/faces were added + } + + MEM_freeN(efaar); + + EM_select_flush(); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Beauty Fill"); +} + + +/* ******************** BEGIN TRIANGLE TO QUAD ************************************* */ +static float measure_facepair(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, float limit){ + + /*gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make*/ + /*Note: this is more complicated than it needs to be and should be cleaned up...*/ + float measure = 0.0, noA1[3], noA2[3], noB1[3], noB2[3], normalADiff, normalBDiff, + edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3], diff, + minarea, maxarea, areaA, areaB; + + /*First Test: Normal difference*/ + CalcNormFloat(v1->co, v2->co, v3->co, noA1); + CalcNormFloat(v1->co, v3->co, v4->co, noA2); + + if(noA1[0] == noA2[0] && noA1[1] == noA2[1] && noA1[2] == noA2[2]) normalADiff = 0.0; + else normalADiff = VecAngle2(noA1, noA2); + //if(!normalADiff) normalADiff = 179; + CalcNormFloat(v2->co, v3->co, v4->co, noB1); + CalcNormFloat(v4->co, v1->co, v2->co, noB2); + + if(noB1[0] == noB2[0] && noB1[1] == noB2[1] && noB1[2] == noB2[2]) normalBDiff = 0.0; + else normalBDiff = VecAngle2(noB1, noB2); + //if(!normalBDiff) normalBDiff = 179; + + measure += (normalADiff/360) + (normalBDiff/360); + if(measure > limit) return measure; + + /*Second test: Colinearity*/ + VecSubf(edgeVec1, v1->co, v2->co); + VecSubf(edgeVec2, v2->co, v3->co); + VecSubf(edgeVec3, v3->co, v4->co); + VecSubf(edgeVec4, v4->co, v1->co); + + diff = 0.0; + + diff = ( + fabs(VecAngle2(edgeVec1, edgeVec2) - 90) + + fabs(VecAngle2(edgeVec2, edgeVec3) - 90) + + fabs(VecAngle2(edgeVec3, edgeVec4) - 90) + + fabs(VecAngle2(edgeVec4, edgeVec1) - 90)) / 360; + if(!diff) return 0.0; + + measure += diff; + if(measure > limit) return measure; + + /*Third test: Concavity*/ + areaA = AreaT3Dfl(v1->co, v2->co, v3->co) + AreaT3Dfl(v1->co, v3->co, v4->co); + areaB = AreaT3Dfl(v2->co, v3->co, v4->co) + AreaT3Dfl(v4->co, v1->co, v2->co); + + if(areaA <= areaB) minarea = areaA; + else minarea = areaB; + + if(areaA >= areaB) maxarea = areaA; + else maxarea = areaB; + + if(!maxarea) measure += 1; + else measure += (1 - (minarea / maxarea)); + + return measure; +} + +#define T2QUV_LIMIT 0.005 +#define T2QCOL_LIMIT 3 +static int compareFaceAttribs(EditFace *f1, EditFace *f2, EditEdge *eed) +{ + /*Test to see if the per-face attributes for the joining edge match within limit*/ + MTFace *tf1, *tf2; + unsigned int *col1, *col2; + short i,attrok=0, flag = G.scene->toolsettings->editbutflag, fe1[2], fe2[2]; + + tf1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MTFACE); + tf2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MTFACE); + + col1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MCOL); + col2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MCOL); + + /*store indices for faceedges*/ + f1->v1->f1 = 0; + f1->v2->f1 = 1; + f1->v3->f1 = 2; + + fe1[0] = eed->v1->f1; + fe1[1] = eed->v2->f1; + + f2->v1->f1 = 0; + f2->v2->f1 = 1; + f2->v3->f1 = 2; + + fe2[0] = eed->v1->f1; + fe2[1] = eed->v2->f1; + + /*compare faceedges for each face attribute. Additional per face attributes can be added later*/ + /*do UVs*/ + if(flag & B_JOINTRIA_UV){ + + if(tf1 == NULL || tf2 == NULL) attrok |= B_JOINTRIA_UV; + else if(tf1->tpage != tf2->tpage); /*do nothing*/ + else{ + for(i = 0; i < 2; i++){ + if(tf1->uv[fe1[i]][0] + T2QUV_LIMIT > tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][0] - T2QUV_LIMIT < tf2->uv[fe2[i]][0] && + tf1->uv[fe1[i]][1] + T2QUV_LIMIT > tf2->uv[fe2[i]][1] && tf1->uv[fe1[i]][1] - T2QUV_LIMIT < tf2->uv[fe2[i]][1]) attrok |= B_JOINTRIA_UV; + } + } + } + + /*do VCOLs*/ + if(flag & B_JOINTRIA_VCOL){ + if(!col1 || !col2) attrok |= B_JOINTRIA_VCOL; + else{ + char *f1vcol, *f2vcol; + for(i = 0; i < 2; i++){ + f1vcol = (char *)&(col1[fe1[i]]); + f2vcol = (char *)&(col2[fe2[i]]); + + /*compare f1vcol with f2vcol*/ + if( f1vcol[1] + T2QCOL_LIMIT > f2vcol[1] && f1vcol[1] - T2QCOL_LIMIT < f2vcol[1] && + f1vcol[2] + T2QCOL_LIMIT > f2vcol[2] && f1vcol[2] - T2QCOL_LIMIT < f2vcol[2] && + f1vcol[3] + T2QCOL_LIMIT > f2vcol[3] && f1vcol[3] - T2QCOL_LIMIT < f2vcol[3]) attrok |= B_JOINTRIA_VCOL; + } + } + } + + if( ((attrok & B_JOINTRIA_UV) == (flag & B_JOINTRIA_UV)) && ((attrok & B_JOINTRIA_VCOL) == (flag & B_JOINTRIA_VCOL)) ) return 1; + return 0; +} + +static int fplcmp(const void *v1, const void *v2) +{ + const EditEdge *e1= *((EditEdge**)v1), *e2=*((EditEdge**)v2); + + if( e1->crease > e2->crease) return 1; + else if( e1->crease < e2->crease) return -1; + + return 0; +} + +/*Bitflags for edges.*/ +#define T2QDELETE 1 +#define T2QCOMPLEX 2 +#define T2QJOIN 4 +void join_triangles(void) +{ + EditMesh *em=G.editMesh; + EditVert *v1, *v2, *v3, *v4, *eve; + EditEdge *eed, **edsortblock = NULL, **edb = NULL; + EditFace *efa; + EVPTuple *efaar = NULL; + EVPtr *efaa = NULL; + float *creases = NULL; + float measure; /*Used to set tolerance*/ + float limit = G.scene->toolsettings->jointrilimit; + int i, ok, totedge=0, totseledge=0, complexedges, vindex[4]; + + /*test for multi-resolution data*/ + if(multires_test()) return; + + /*if we take a long time on very dense meshes we want waitcursor to display*/ + waitcursor(1); + + totseledge = count_selected_edges(em->edges.first); + if(totseledge==0) return; + + /*abusing crease value to store weights for edge pairs. Nasty*/ + for(eed=em->edges.first; eed; eed=eed->next) totedge++; + if(totedge) creases = MEM_callocN(sizeof(float) * totedge, "Join Triangles Crease Array"); + for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++){ + creases[i] = eed->crease; + eed->crease = 0.0; + } + + /*clear temp flags*/ + for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = eve->f2 = 0; + for(eed=em->edges.first; eed; eed=eed->next) eed->f2 = eed->f1 = 0; + for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = efa->tmp.l = 0; + + /*For every selected 2 manifold edge, create pointers to its two faces.*/ + efaar= (EVPTuple *) MEM_callocN(totseledge * sizeof(EVPTuple), "Tri2Quad"); + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + complexedges = 0; + + if(ok){ + + + /*clear tmp.l flag and store number of faces that are selected and coincident to current face here.*/ + for(eed=em->edges.first; eed; eed=eed->next){ + /* eed->f2 is 2 only if this edge is part of exactly two + triangles, and both are selected, and it has EVPTuple assigned */ + if(eed->f2 == 2){ + efaa= (EVPtr *) eed->tmp.p; + efaa[0]->tmp.l++; + efaa[1]->tmp.l++; + } + } + + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f2 == 2){ + efaa= (EVPtr *) eed->tmp.p; + v1 = v2 = v3 = v4 = NULL; + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if(v1 && v2 && v3 && v4){ + /*test if simple island first. This mimics 2.42 behaviour and the tests are less restrictive.*/ + if(efaa[0]->tmp.l == 1 && efaa[1]->tmp.l == 1){ + if( convex(v1->co, v2->co, v3->co, v4->co) ){ + eed->f1 |= T2QJOIN; + efaa[0]->f1 = 1; //mark for join + efaa[1]->f1 = 1; //mark for join + } + } + else{ + + /* The face pair is part of a 'complex' island, so the rules for dealing with it are more involved. + Depending on what options the user has chosen, this face pair can be 'thrown out' based upon the following criteria: + + 1: the two faces do not share the same material + 2: the edge joining the two faces is marked as sharp. + 3: the two faces UV's do not make a good match + 4: the two faces Vertex colors do not make a good match + + If the face pair passes all the applicable tests, it is then given a 'weight' with the measure_facepair() function. + This measures things like concavity, colinearity ect. If this weight is below the threshold set by the user + the edge joining them is marked as being 'complex' and will be compared against other possible pairs which contain one of the + same faces in the current pair later. + + This technique is based upon an algorithm that Campbell Barton developed for his Tri2Quad script that was previously part of + the python scripts bundled with Blender releases. + */ + + if(G.scene->toolsettings->editbutflag & B_JOINTRIA_SHARP && eed->sharp); /*do nothing*/ + else if(G.scene->toolsettings->editbutflag & B_JOINTRIA_MAT && efaa[0]->mat_nr != efaa[1]->mat_nr); /*do nothing*/ + else if(((G.scene->toolsettings->editbutflag & B_JOINTRIA_UV) || (G.scene->toolsettings->editbutflag & B_JOINTRIA_VCOL)) && + compareFaceAttribs(efaa[0], efaa[1], eed) == 0); /*do nothing*/ + else{ + measure = measure_facepair(v1, v2, v3, v4, limit); + if(measure < limit){ + complexedges++; + eed->f1 |= T2QCOMPLEX; + eed->crease = measure; /*we dont mark edges for join yet*/ + } + } + } + } + } + } + + /*Quicksort the complex edges according to their weighting*/ + if(complexedges){ + edsortblock = edb = MEM_callocN(sizeof(EditEdge*) * complexedges, "Face Pairs quicksort Array"); + for(eed = em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QCOMPLEX){ + *edb = eed; + edb++; + } + } + qsort(edsortblock, complexedges, sizeof(EditEdge*), fplcmp); + /*now go through and mark the edges who get the highest weighting*/ + for(edb=edsortblock, i=0; i < complexedges; edb++, i++){ + efaa = (EVPtr *)((*edb)->tmp.p); /*suspect!*/ + if( !efaa[0]->f1 && !efaa[1]->f1){ + efaa[0]->f1 = 1; //mark for join + efaa[1]->f1 = 1; //mark for join + (*edb)->f1 |= T2QJOIN; + } + } + } + + /*finally go through all edges marked for join (simple and complex) and create new faces*/ + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QJOIN){ + efaa= (EVPtr *)eed->tmp.p; + v1 = v2 = v3 = v4 = NULL; + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if((v1 && v2 && v3 && v4) && (exist_face(v1, v2, v3, v4)==0)){ /*exist_face is very slow! Needs to be adressed.*/ + /*flag for delete*/ + eed->f1 |= T2QDELETE; + /*create new quad and select*/ + efa = EM_face_from_faces(efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], 4+vindex[3]); + EM_select_face(efa,1); + } + else{ + efaa[0]->f1 = 0; + efaa[1]->f1 = 0; + } + } + } + } + + /*free data and cleanup*/ + if(creases){ + for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++) eed->crease = creases[i]; + MEM_freeN(creases); + } + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QDELETE) eed->f1 = 1; + else eed->f1 = 0; + } + free_tagged_edges_faces(em->edges.first, em->faces.first); + if(efaar) MEM_freeN(efaar); + if(edsortblock) MEM_freeN(edsortblock); + + EM_selectmode_flush(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + #ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); + #endif + waitcursor(0); + BIF_undo_push("Convert Triangles to Quads"); +} +/* ******************** END TRIANGLE TO QUAD ************************************* */ + +#define FACE_MARKCLEAR(f) (f->f1 = 1) + +/* quick hack, basically a copy of beauty_fill */ +void edge_flip(void) +{ + EditMesh *em = G.editMesh; + EditVert *v1, *v2, *v3, *v4; + EditEdge *eed, *nexted; + EditFace *efa, *w; + //void **efaar, **efaa; + EVPTuple *efaar; + EVPtr *efaa; + int totedge, ok, vindex[4]; + + /* - all selected edges with two faces + * - find the faces: store them in edges (using datablock) + * - per edge: - test convex + * - test edge: flip? + - if true: remedge, addedge, all edges at the edge get new face pointers + */ + + EM_selectmode_flush(); // makes sure in selectmode 'face' the edges of selected faces are selected too + + totedge = count_selected_edges(em->edges.first); + if(totedge==0) return; + + /* temporary array for : edge -> face[1], face[2] */ + efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "edgeflip"); + + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + + eed= em->edges.first; + while(eed) { + nexted= eed->next; + + if(eed->f2==2) { /* points to 2 faces */ + + efaa= (EVPtr *) eed->tmp.p; + + /* don't do it if flagged */ + + ok= 1; + efa= efaa[0]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + efa= efaa[1]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + + if(ok) { + /* test convex */ + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + +/* + 4-----3 4-----3 + |\ | | /| + | \ 1 | | 1 / | + | \ | -> | / | + | 0 \ | | / 0 | + | \| |/ | + 1-----2 1-----2 +*/ + /* make new faces */ + if (v1 && v2 && v3) { + if( convex(v1->co, v2->co, v3->co, v4->co) ) { + if(exist_face(v1, v2, v3, v4)==0) { + /* outch this may break seams */ + w= EM_face_from_faces(efaa[0], efaa[1], vindex[0], + vindex[1], 4+vindex[2], -1); + + EM_select_face(w, 1); + + /* outch this may break seams */ + w= EM_face_from_faces(efaa[0], efaa[1], vindex[0], + 4+vindex[2], 4+vindex[3], -1); + + EM_select_face(w, 1); + } + /* tag as to-be-removed */ + FACE_MARKCLEAR(efaa[1]); + FACE_MARKCLEAR(efaa[0]); + eed->f1 = 1; + + } /* endif test convex */ + } + } + } + eed= nexted; + } + + /* clear tagged edges and faces: */ + free_tagged_edges_faces(em->edges.first, em->faces.first); + + MEM_freeN(efaar); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Flip Triangle Edges"); + +} + +static void edge_rotate(EditEdge *eed,int dir) +{ + EditMesh *em = G.editMesh; + EditVert **verts[2]; + EditFace *face[2], *efa, *newFace[2]; + EditEdge **edges[2], **hiddenedges, *srchedge; + int facecount, p1, p2, p3, p4, fac1, fac2, i, j; + int numhidden, numshared, p[2][4]; + + /* check to make sure that the edge is only part of 2 faces */ + facecount = 0; + for(efa = em->faces.first;efa;efa = efa->next) { + if((efa->e1 == eed || efa->e2 == eed) || (efa->e3 == eed || efa->e4 == eed)) { + if(facecount >= 2) { + /* more than two faces with this edge */ + return; + } + else { + face[facecount] = efa; + facecount++; + } + } + } + + if(facecount < 2) + return; + + /* how many edges does each face have */ + if(face[0]->e4) fac1= 4; + else fac1= 3; + + if(face[1]->e4) fac2= 4; + else fac2= 3; + + /* make a handy array for verts and edges */ + verts[0]= &face[0]->v1; + edges[0]= &face[0]->e1; + verts[1]= &face[1]->v1; + edges[1]= &face[1]->e1; + + /* we don't want to rotate edges between faces that share more than one edge */ + numshared= 0; + for(i=0; i<fac1; i++) + for(j=0; j<fac2; j++) + if (edges[0][i] == edges[1][j]) + numshared++; + + if(numshared > 1) + return; + + /* coplaner faces only please */ + if(Inpf(face[0]->n,face[1]->n) <= 0.000001) + return; + + /* we want to construct an array of vertex indicis in both faces, starting at + the last vertex of the edge being rotated. + - first we find the two vertices that lie on the rotating edge + - then we make sure they are ordered according to the face vertex order + - and then we construct the array */ + p1= p2= p3= p4= 0; + + for(i=0; i<4; i++) { + if(eed->v1 == verts[0][i]) p1 = i; + if(eed->v2 == verts[0][i]) p2 = i; + if(eed->v1 == verts[1][i]) p3 = i; + if(eed->v2 == verts[1][i]) p4 = i; + } + + if((p1+1)%fac1 == p2) + SWAP(int, p1, p2); + if((p3+1)%fac2 == p4) + SWAP(int, p3, p4); + + for (i = 0; i < 4; i++) { + p[0][i]= (p1 + i)%fac1; + p[1][i]= (p3 + i)%fac2; + } + + /* create an Array of the Edges who have h set prior to rotate */ + numhidden = 0; + for(srchedge = em->edges.first;srchedge;srchedge = srchedge->next) + if(srchedge->h && ((srchedge->v1->f & SELECT) || (srchedge->v2->f & SELECT))) + numhidden++; + + hiddenedges = MEM_mallocN(sizeof(EditVert*)*numhidden+1, "RotateEdgeHiddenVerts"); + if(!hiddenedges) { + error("Malloc Was not happy!"); + return; + } + + numhidden = 0; + for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) + if(srchedge->h && (srchedge->v1->f & SELECT || srchedge->v2->f & SELECT)) + hiddenedges[numhidden++] = srchedge; + + /* create the 2 new faces */ + if(fac1 == 3 && fac2 == 3) { + /* no need of reverse setup */ + + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); + } + else if(fac1 == 4 && fac2 == 3) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], 4+p[1][1], p[0][0], p[0][1]); + newFace[1]= EM_face_from_faces(face[1], face[0], 4+p[0][2], p[1][0], p[1][1], -1); + + verts[0][p[0][2]]->f |= SELECT; + verts[1][p[1][1]]->f |= SELECT; + } + } + else if(fac1 == 3 && fac2 == 4) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][0], p[0][1], 4+p[1][2], -1); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], 4+p[0][2]); + + verts[0][p[0][1]]->f |= SELECT; + verts[1][p[1][2]]->f |= SELECT; + } + + } + else if(fac1 == 4 && fac2 == 4) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], p[0][3], 4+p[1][1], 4+p[1][2]); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][2], p[1][3], 4+p[0][1], 4+p[0][2]); + + verts[0][p[0][2]]->f |= SELECT; + verts[1][p[1][2]]->f |= SELECT; + } + } + else + return; /* This should never happen */ + + if(dir == 1 || (fac1 == 3 && fac2 == 3)) { + verts[0][p[0][1]]->f |= SELECT; + verts[1][p[1][1]]->f |= SELECT; + } + + /* copy old edge's flags to new center edge*/ + for(srchedge=em->edges.first;srchedge;srchedge=srchedge->next) { + if((srchedge->v1->f & SELECT) && (srchedge->v2->f & SELECT)) { + srchedge->f = eed->f; + srchedge->h = eed->h; + srchedge->dir = eed->dir; + srchedge->seam = eed->seam; + srchedge->crease = eed->crease; + srchedge->bweight = eed->bweight; + } + } + + /* resetting hidden flag */ + for(numhidden--; numhidden>=0; numhidden--) + hiddenedges[numhidden]->h= 1; + + /* check for orhphan edges */ + for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) + srchedge->f1= -1; + + /* cleanup */ + MEM_freeN(hiddenedges); + + /* get rid of the old edge and faces*/ + remedge(eed); + free_editedge(eed); + BLI_remlink(&em->faces, face[0]); + free_editface(face[0]); + BLI_remlink(&em->faces, face[1]); + free_editface(face[1]); +} + +/* only accepts 1 selected edge, or 2 selected faces */ +void edge_rotate_selected(int dir) +{ + EditEdge *eed; + EditFace *efa; + short edgeCount = 0; + + /*clear new flag for new edges, count selected edges */ + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + eed->f1= 0; + eed->f2 &= ~2; + if(eed->f & SELECT) edgeCount++; + } + + if(edgeCount>1) { + /* more selected edges, check faces */ + for(efa= G.editMesh->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + edgeCount= 0; + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + if(eed->f1==2) edgeCount++; + } + if(edgeCount==1) { + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + if(eed->f1==2) { + edge_rotate(eed,dir); + break; + } + } + } + else error("Select one edge or two adjacent faces"); + } + else if(edgeCount==1) { + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + if(eed->f & SELECT) { + EM_select_edge(eed, 0); + edge_rotate(eed,dir); + break; + } + } + } + else error("Select one edge or two adjacent faces"); + + + /* flush selected vertices (again) to edges/faces */ + EM_select_flush(); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Rotate Edge"); +} + +/******************* BEVEL CODE STARTS HERE ********************/ + +static void bevel_displace_vec(float *midvec, float *v1, float *v2, float *v3, float d, float no[3]) +{ + float a[3], c[3], n_a[3], n_c[3], mid[3], ac, ac2, fac; + + VecSubf(a, v1, v2); + VecSubf(c, v3, v2); + + Crossf(n_a, a, no); + Normalize(n_a); + Crossf(n_c, no, c); + Normalize(n_c); + + Normalize(a); + Normalize(c); + ac = Inpf(a, c); + + if (ac == 1 || ac == -1) { + midvec[0] = midvec[1] = midvec[2] = 0; + return; + } + ac2 = ac * ac; + fac = (float)sqrt((ac2 + 2*ac + 1)/(1 - ac2) + 1); + VecAddf(mid, n_c, n_a); + Normalize(mid); + VecMulf(mid, d * fac); + VecAddf(mid, mid, v2); + VecCopyf(midvec, mid); +} + +/* Finds the new point using the sinus law to extrapolate a triangle + Lots of sqrts which would not be good for a real time algo + Using the mid point of the extrapolation of both sides + Useless for coplanar quads, but that doesn't happen too often */ +static void fix_bevel_wrap(float *midvec, float *v1, float *v2, float *v3, float *v4, float d, float no[3]) +{ + float a[3], b[3], c[3], l_a, l_b, l_c, s_a, s_b, s_c, Pos1[3], Pos2[3], Dir[3]; + + VecSubf(a, v3, v2); + l_a = Normalize(a); + VecSubf(b, v4, v3); + Normalize(b); + VecSubf(c, v1, v2); + Normalize(c); + + s_b = Inpf(a, c); + s_b = (float)sqrt(1 - (s_b * s_b)); + s_a = Inpf(b, c); + s_a = (float)sqrt(1 - (s_a * s_a)); + VecMulf(a, -1); + s_c = Inpf(a, b); + s_c = (float)sqrt(1 - (s_c * s_c)); + + l_b = s_b * l_a / s_a; + l_c = s_c * l_a / s_a; + + VecMulf(b, l_b); + VecMulf(c, l_c); + + VecAddf(Pos1, v2, c); + VecAddf(Pos2, v3, b); + + VecAddf(Dir, Pos1, Pos2); + VecMulf(Dir, 0.5); + + bevel_displace_vec(midvec, v3, Dir, v2, d, no); + +} + + +static char detect_wrap(float *o_v1, float *o_v2, float *v1, float *v2, float *no) +{ + float o_a[3], a[3], o_c[3], c[3]; + + VecSubf(o_a, o_v1, o_v2); + VecSubf(a, v1, v2); + + Crossf(o_c, o_a, no); + Crossf(c, a, no); + + if (Inpf(c, o_c) <= 0) + return 1; + else + return 0; +} + +// Detects and fix a quad wrapping after the resize +// Arguments are the orginal verts followed by the final verts and then the bevel size and the normal +static void fix_bevel_quad_wrap(float *o_v1, float *o_v2, float *o_v3, float *o_v4, float *v1, float *v2, float *v3, float *v4, float d, float *no) +{ + float vec[3]; + char wrap[4]; + + // Quads can wrap partially. Watch out + wrap[0] = detect_wrap(o_v1, o_v2, v1, v2, no); // Edge 1-2 + wrap[1] = detect_wrap(o_v2, o_v3, v2, v3, no); // Edge 2-3 + wrap[2] = detect_wrap(o_v3, o_v4, v3, v4, no); // Edge 3-4 + wrap[3] = detect_wrap(o_v4, o_v1, v4, v1, no); // Edge 4-1 + + // Edge 1 inverted + if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 0) { + fix_bevel_wrap(vec, o_v2, o_v3, o_v4, o_v1, d, no); + VECCOPY(v1, vec); + VECCOPY(v2, vec); + } + // Edge 2 inverted + else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 0) { + fix_bevel_wrap(vec, o_v3, o_v4, o_v1, o_v2, d, no); + VECCOPY(v2, vec); + VECCOPY(v3, vec); + } + // Edge 3 inverted + else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) { + fix_bevel_wrap(vec, o_v4, o_v1, o_v2, o_v3, d, no); + VECCOPY(v3, vec); + VECCOPY(v4, vec); + } + // Edge 4 inverted + else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 1) { + fix_bevel_wrap(vec, o_v1, o_v2, o_v3, o_v4, d, no); + VECCOPY(v4, vec); + VECCOPY(v1, vec); + } + // Edge 2 and 4 inverted + else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 1) { + VecAddf(vec, v2, v3); + VecMulf(vec, 0.5); + VECCOPY(v2, vec); + VECCOPY(v3, vec); + VecAddf(vec, v1, v4); + VecMulf(vec, 0.5); + VECCOPY(v1, vec); + VECCOPY(v4, vec); + } + // Edge 1 and 3 inverted + else if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) { + VecAddf(vec, v1, v2); + VecMulf(vec, 0.5); + VECCOPY(v1, vec); + VECCOPY(v2, vec); + VecAddf(vec, v3, v4); + VecMulf(vec, 0.5); + VECCOPY(v3, vec); + VECCOPY(v4, vec); + } + // Totally inverted + else if (wrap[0] == 1 && wrap[1] == 1 && wrap[2] == 1 && wrap[3] == 1) { + VecAddf(vec, v1, v2); + VecAddf(vec, vec, v3); + VecAddf(vec, vec, v4); + VecMulf(vec, 0.25); + VECCOPY(v1, vec); + VECCOPY(v2, vec); + VECCOPY(v3, vec); + VECCOPY(v4, vec); + } + +} + +// Detects and fix a tri wrapping after the resize +// Arguments are the orginal verts followed by the final verts and the normal +// Triangles cannot wrap partially (not in this situation +static void fix_bevel_tri_wrap(float *o_v1, float *o_v2, float *o_v3, float *v1, float *v2, float *v3, float *no) +{ + if (detect_wrap(o_v1, o_v2, v1, v2, no)) { + float vec[3]; + VecAddf(vec, o_v1, o_v2); + VecAddf(vec, vec, o_v3); + VecMulf(vec, 1.0f/3.0f); + VECCOPY(v1, vec); + VECCOPY(v2, vec); + VECCOPY(v3, vec); + } +} + +static void bevel_shrink_faces(float d, int flag) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + float vec[3], no[3], v1[3], v2[3], v3[3], v4[3]; + + /* move edges of all faces with efa->f1 & flag closer towards their centers */ + efa= em->faces.first; + while (efa) { + if (efa->f1 & flag) { + VECCOPY(v1, efa->v1->co); + VECCOPY(v2, efa->v2->co); + VECCOPY(v3, efa->v3->co); + VECCOPY(no, efa->n); + if (efa->v4 == NULL) { + bevel_displace_vec(vec, v1, v2, v3, d, no); + VECCOPY(efa->v2->co, vec); + bevel_displace_vec(vec, v2, v3, v1, d, no); + VECCOPY(efa->v3->co, vec); + bevel_displace_vec(vec, v3, v1, v2, d, no); + VECCOPY(efa->v1->co, vec); + + fix_bevel_tri_wrap(v1, v2, v3, efa->v1->co, efa->v2->co, efa->v3->co, no); + } else { + VECCOPY(v4, efa->v4->co); + bevel_displace_vec(vec, v1, v2, v3, d, no); + VECCOPY(efa->v2->co, vec); + bevel_displace_vec(vec, v2, v3, v4, d, no); + VECCOPY(efa->v3->co, vec); + bevel_displace_vec(vec, v3, v4, v1, d, no); + VECCOPY(efa->v4->co, vec); + bevel_displace_vec(vec, v4, v1, v2, d, no); + VECCOPY(efa->v1->co, vec); + + fix_bevel_quad_wrap(v1, v2, v3, v4, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, d, no); + } + } + efa= efa->next; + } +} + +static void bevel_shrink_draw(float d, int flag) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + float vec[3], no[3], v1[3], v2[3], v3[3], v4[3], fv1[3], fv2[3], fv3[3], fv4[3]; + + /* move edges of all faces with efa->f1 & flag closer towards their centers */ + efa= em->faces.first; + while (efa) { + VECCOPY(v1, efa->v1->co); + VECCOPY(v2, efa->v2->co); + VECCOPY(v3, efa->v3->co); + VECCOPY(no, efa->n); + if (efa->v4 == NULL) { + bevel_displace_vec(vec, v1, v2, v3, d, no); + VECCOPY(fv2, vec); + bevel_displace_vec(vec, v2, v3, v1, d, no); + VECCOPY(fv3, vec); + bevel_displace_vec(vec, v3, v1, v2, d, no); + VECCOPY(fv1, vec); + + fix_bevel_tri_wrap(v1, v2, v3, fv1, fv2, fv3, no); + + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv2); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv2); + glVertex3fv(fv3); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv3); + glEnd(); + } else { + VECCOPY(v4, efa->v4->co); + bevel_displace_vec(vec, v4, v1, v2, d, no); + VECCOPY(fv1, vec); + bevel_displace_vec(vec, v1, v2, v3, d, no); + VECCOPY(fv2, vec); + bevel_displace_vec(vec, v2, v3, v4, d, no); + VECCOPY(fv3, vec); + bevel_displace_vec(vec, v3, v4, v1, d, no); + VECCOPY(fv4, vec); + + fix_bevel_quad_wrap(v1, v2, v3, v4, fv1, fv2, fv3, fv4, d, no); + + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv2); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv2); + glVertex3fv(fv3); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv3); + glVertex3fv(fv4); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv4); + glEnd(); + } + efa= efa->next; + } +} + +static void bevel_mesh(float bsize, int allfaces) +{ + EditMesh *em = G.editMesh; +//#define BEV_DEBUG +/* Enables debug printfs and assigns material indices: */ +/* 2 = edge quad */ +/* 3 = fill polygon (vertex clusters) */ + + EditFace *efa, *example; //, *nextvl; + EditEdge *eed, *eed2; + EditVert *neweve[1024], *eve, *eve2, *eve3, *v1, *v2, *v3, *v4; //, *eve4; + //short found4, search; + //float f1, f2, f3, f4; + float cent[3], min[3], max[3]; + int a, b, c; + float limit= 0.001f; + + if(multires_test()) return; + + waitcursor(1); + + removedoublesflag(1, 0, limit); + + /* tag all original faces */ + efa= em->faces.first; + while (efa) { + efa->f1= 0; + if (faceselectedAND(efa, 1)||allfaces) { + efa->f1= 1; + efa->v1->f |= 128; + efa->v2->f |= 128; + efa->v3->f |= 128; + if (efa->v4) efa->v4->f |= 128; + } + efa->v1->f &= ~64; + efa->v2->f &= ~64; + efa->v3->f &= ~64; + if (efa->v4) efa->v4->f &= ~64; + + efa= efa->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: split\n"); +#endif + + efa= em->faces.first; + while (efa) { + if (efa->f1 & 1) { + efa->f1-= 1; + v1= addvertlist(efa->v1->co, efa->v1); + v1->f= efa->v1->f & ~128; + efa->v1->tmp.v = v1; + + v1= addvertlist(efa->v2->co, efa->v2); + v1->f= efa->v2->f & ~128; + efa->v2->tmp.v = v1; + + v1= addvertlist(efa->v3->co, efa->v3); + v1->f= efa->v3->f & ~128; + efa->v3->tmp.v = v1; + + if (efa->v4) { + v1= addvertlist(efa->v4->co, efa->v4); + v1->f= efa->v4->f & ~128; + efa->v4->tmp.v = v1; + } + + /* Needs better adaption of creases? */ + addedgelist(efa->e1->v1->tmp.v, + efa->e1->v2->tmp.v, + efa->e1); + addedgelist(efa->e2->v1->tmp.v, + efa->e2->v2->tmp.v, + efa->e2); + addedgelist(efa->e3->v1->tmp.v, + efa->e3->v2->tmp.v, + efa->e3); + if (efa->e4) addedgelist(efa->e4->v1->tmp.v, + efa->e4->v2->tmp.v, + efa->e4); + + if(efa->v4) { + v1 = efa->v1->tmp.v; + v2 = efa->v2->tmp.v; + v3 = efa->v3->tmp.v; + v4 = efa->v4->tmp.v; + addfacelist(v1, v2, v3, v4, efa,NULL); + } else { + v1= efa->v1->tmp.v; + v2= efa->v2->tmp.v; + v3= efa->v3->tmp.v; + addfacelist(v1, v2, v3, 0, efa,NULL); + } + + efa= efa-> next; + } else { + efa= efa->next; + } + } + + for(efa= em->faces.first; efa; efa= efa->next) { + if( (efa->v1->f & 128) && (efa->v2->f & 128) && (efa->v3->f & 128) ) { + if(efa->v4==NULL || (efa->v4->f & 128)) efa->f |= 128; + } + } + + delfaceflag(128); // works with face flag now + + /* tag all faces for shrink*/ + efa= em->faces.first; + while (efa) { + if (faceselectedAND(efa, 1)||allfaces) { + efa->f1= 2; + } + efa= efa->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: make edge quads\n"); +#endif + + /* find edges that are on each other and make quads between them */ + + eed= em->edges.first; + while(eed) { + eed->f2= eed->f1= 0; + if ( ((eed->v1->f & eed->v2->f) & 1) || allfaces) + eed->f1 |= 4; /* original edges */ + eed->tmp.v = 0; + eed= eed->next; + } + + eed= em->edges.first; + while (eed) { + if ( ((eed->f1 & 2)==0) && (eed->f1 & 4) ) { + eed2= em->edges.first; + while (eed2) { + if ( (eed2 != eed) && ((eed2->f1 & 2)==0) && (eed->f1 & 4) ) { + if ( + (eed->v1 != eed2->v1) && + (eed->v1 != eed2->v2) && + (eed->v2 != eed2->v1) && + (eed->v2 != eed2->v2) && ( + ( VecCompare(eed->v1->co, eed2->v1->co, limit) && + VecCompare(eed->v2->co, eed2->v2->co, limit) ) || + ( VecCompare(eed->v1->co, eed2->v2->co, limit) && + VecCompare(eed->v2->co, eed2->v1->co, limit) ) ) ) + { + +#ifdef BEV_DEBUG + fprintf(stderr, "bevel_mesh: edge quad\n"); +#endif + + eed->f1 |= 2; /* these edges are finished */ + eed2->f1 |= 2; + + example= NULL; + efa= em->faces.first; /* search example face (for mat_nr, ME_SMOOTH, ...) */ + while (efa) { + if ( (efa->e1 == eed) || + (efa->e2 == eed) || + (efa->e3 == eed) || + (efa->e4 && (efa->e4 == eed)) ) { + example= efa; + efa= NULL; + } + if (efa) efa= efa->next; + } + + neweve[0]= eed->v1; neweve[1]= eed->v2; + neweve[2]= eed2->v1; neweve[3]= eed2->v2; + + if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) { + efa= NULL; + + if (VecCompare(eed->v1->co, eed2->v2->co, limit)) { + efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], example,NULL); + } else { + efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], example,NULL); + } + + if(efa) { + float inp; + CalcNormFloat(efa->v1->co, efa->v2->co, efa->v3->co, efa->n); + inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2]; + if(inp < 0.0) flipface(efa); +#ifdef BEV_DEBUG + efa->mat_nr= 1; +#endif + } else fprintf(stderr,"bevel_mesh: error creating face\n"); + } + eed2= NULL; + } + } + if (eed2) eed2= eed2->next; + } + } + eed= eed->next; + } + + eed= em->edges.first; + while(eed) { + eed->f2= eed->f1= 0; + eed->f1= 0; + eed->v1->f1 &= ~1; + eed->v2->f1 &= ~1; + eed->tmp.v = 0; + eed= eed->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: find clusters\n"); +#endif + + /* Look for vertex clusters */ + + eve= em->verts.first; + while (eve) { + eve->f &= ~(64|128); + eve->tmp.v = NULL; + eve= eve->next; + } + + /* eve->f: 128: first vertex in a list (->tmp.v) */ + /* 64: vertex is in a list */ + + eve= em->verts.first; + while (eve) { + eve2= em->verts.first; + eve3= NULL; + while (eve2) { + if ((eve2 != eve) && ((eve2->f & (64|128))==0)) { + if (VecCompare(eve->co, eve2->co, limit)) { + if ((eve->f & (128|64)) == 0) { + /* fprintf(stderr,"Found vertex cluster:\n *\n *\n"); */ + eve->f |= 128; + eve->tmp.v = eve2; + eve3= eve2; + } else if ((eve->f & 64) == 0) { + /* fprintf(stderr," *\n"); */ + if (eve3) eve3->tmp.v = eve2; + eve2->f |= 64; + eve3= eve2; + } + } + } + eve2= eve2->next; + if (!eve2) { + if (eve3) eve3->tmp.v = NULL; + } + } + eve= eve->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: shrink faces\n"); +#endif + + bevel_shrink_faces(bsize, 2); + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: fill clusters\n"); +#endif + + /* Make former vertex clusters faces */ + + eve= em->verts.first; + while (eve) { + eve->f &= ~64; + eve= eve->next; + } + + eve= em->verts.first; + while (eve) { + if (eve->f & 128) { + eve->f &= ~128; + a= 0; + neweve[a]= eve; + eve2 = eve->tmp.v; + while (eve2) { + a++; + neweve[a]= eve2; + eve2 = eve2->tmp.v; + } + a++; + efa= NULL; + if (a>=3) { + example= NULL; + efa= em->faces.first; /* search example face */ + while (efa) { + if ( (efa->v1 == neweve[0]) || + (efa->v2 == neweve[0]) || + (efa->v3 == neweve[0]) || + (efa->v4 && (efa->v4 == neweve[0])) ) { + example= efa; + efa= NULL; + } + if (efa) efa= efa->next; + } +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: Making %d-gon\n", a); +#endif + if (a>4) { + cent[0]= cent[1]= cent[2]= 0.0; + INIT_MINMAX(min, max); + for (b=0; b<a; b++) { + VecAddf(cent, cent, neweve[b]->co); + DO_MINMAX(neweve[b]->co, min, max); + } + cent[0]= (min[0]+max[0])/2; + cent[1]= (min[1]+max[1])/2; + cent[2]= (min[2]+max[2])/2; + eve2= addvertlist(cent, NULL); + eve2->f |= 1; + eed= em->edges.first; + while (eed) { + c= 0; + for (b=0; b<a; b++) + if ((neweve[b]==eed->v1) || (neweve[b]==eed->v2)) c++; + if (c==2) { + if(exist_face(eed->v1, eed->v2, eve2, 0)==0) { + efa= addfacelist(eed->v1, eed->v2, eve2, 0, example,NULL); +#ifdef BEV_DEBUG + efa->mat_nr= 2; +#endif + } + } + eed= eed->next; + } + } else if (a==4) { + if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) { + /* the order of vertices can be anything, three cases to check */ + if( convex(neweve[0]->co, neweve[1]->co, neweve[2]->co, neweve[3]->co) ) { + efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[2]->co, neweve[3]->co, neweve[1]->co) ) { + efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[2]->co, neweve[1]->co, neweve[3]->co) ) { + efa= addfacelist(neweve[0], neweve[2], neweve[1], neweve[3], NULL, NULL); + } + } + } + else if (a==3) { + if(exist_face(neweve[0], neweve[1], neweve[2], 0)==0) + efa= addfacelist(neweve[0], neweve[1], neweve[2], 0, example, NULL); + } + if(efa) { + float inp; + CalcNormFloat(neweve[0]->co, neweve[1]->co, neweve[2]->co, efa->n); + inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2]; + if(inp < 0.0) flipface(efa); +#ifdef BEV_DEBUG + efa->mat_nr= 2; +#endif + } + } + } + eve= eve->next; + } + + eve= em->verts.first; + while (eve) { + eve->f1= 0; + eve->f &= ~(128|64); + eve->tmp.v= NULL; + eve= eve->next; + } + + recalc_editnormals(); + waitcursor(0); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + removedoublesflag(1, 0, limit); + + /* flush selected vertices to edges/faces */ + EM_select_flush(); + +#undef BEV_DEBUG +} + +static void bevel_mesh_recurs(float bsize, short recurs, int allfaces) +{ + float d; + short nr; + + d= bsize; + for (nr=0; nr<recurs; nr++) { + bevel_mesh(d, allfaces); + if (nr==0) d /= 3; else d /= 2; + } +} + +void bevel_menu(void) { + 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_editmesh_to_bmesh(G.editMesh); + 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); + EM_selectmode_flush(); + 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; + float vec[3], d, drawd=0.0, center[3], fac = 1; + + getmouseco_areawin(mval); + oval[0] = mval[0]; oval[1] = mval[1]; + + // Silly hackish code to initialise the variable (warning if not done) + // while still drawing in the first iteration (and without using another variable) + curval[0] = mval[0] + 1; curval[1] = mval[1] + 1; + + // Init grabz for window to vec conversions + initgrabz(-G.vd->ofs[0], -G.vd->ofs[1], -G.vd->ofs[2]); + window_to_3d(center, mval[0], mval[1]); + + if(button(&recurs, 1, 4, "Recursion:")==0) return; + + for (nr=0; nr<recurs-1; nr++) { + if (nr==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * nr * 2.0f); + } + + EM_set_flag_all(SELECT); + + SetBlenderCursor(SYSCURSOR); + + while (Finished == 0) + { + getmouseco_areawin(mval); + if (mval[0] != curval[0] || mval[1] != curval[1] || (Recalc == 1)) + { + Recalc = 0; + curval[0] = mval[0]; + curval[1] = mval[1]; + + window_to_3d(vec, mval[0]-oval[0], mval[1]-oval[1]); + d = Normalize(vec) / 10; + + + drawd = d * fac; + if (G.qual & LR_CTRLKEY) + drawd = (float) floor(drawd * 10.0f)/10.0f; + if (G.qual & LR_SHIFTKEY) + drawd /= 10; + + /*------------- Preview lines--------------- */ + + /* uses callback mechanism to draw it all in current area */ + scrarea_do_windraw(curarea); + + /* set window matrix to perspective, default an area returns with buttons transform */ + persp(PERSP_VIEW); + /* make a copy, for safety */ + glPushMatrix(); + /* multiply with the object transformation */ + mymultmatrix(G.obedit->obmat); + + glColor3ub(255, 255, 0); + + // PREVIEW CODE GOES HERE + bevel_shrink_draw(drawd, 2); + + /* restore matrix transform */ + glPopMatrix(); + + sprintf(str, "Bevel Size: %.4f LMB to confirm, RMB to cancel, SPACE to input directly.", drawd); + headerprint(str); + + /* this also verifies other area/windows for clean swap */ + screen_swapbuffers(); + + persp(PERSP_WIN); + + glDrawBuffer(GL_FRONT); + + BIF_ThemeColor(TH_WIRE); + + setlinestyle(3); + glBegin(GL_LINE_STRIP); + glVertex2sv(mval); + glVertex2sv(oval); + glEnd(); + setlinestyle(0); + + persp(PERSP_VIEW); + bglFlush(); // flush display for frontbuffer + glDrawBuffer(GL_BACK); + } + while(qtest()) { + short val=0; + event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle + + /* val==0 on key-release event */ + if(val && (event==ESCKEY || event==RIGHTMOUSE || event==LEFTMOUSE || event==RETKEY || event==ESCKEY)) { + if (event==RIGHTMOUSE || event==ESCKEY) + Canceled = 1; + Finished = 1; + } + else if (val && event==SPACEKEY) { + if (fbutton(&d, 0.000, 10.000, 10, 0, "Width:")!=0) { + drawd = d * fac; + Finished = 1; + } + } + else if (val) { + /* On any other keyboard event, recalc */ + Recalc = 1; + } + + } + } + if (Canceled==0) { + SetBlenderCursor(BC_WAITCURSOR); + bevel_mesh_recurs(drawd/fac, recurs, 1); + righthandfaces(1); + SetBlenderCursor(SYSCURSOR); + BIF_undo_push("Bevel"); + } +} + +/* *********** END BEVEL *********/ +typedef struct SlideUv { + float origuv[2]; + float *uv_up, *uv_down; + //float *fuv[4]; + LinkNode *fuv_list; +} SlideUv; + +typedef struct SlideVert { + EditEdge *up,*down; + EditVert origvert; +} SlideVert; + +int EdgeLoopDelete(void) { + + /* temporal flag setting so we keep UVs when deleting edge loops, + * this is a bit of a hack but it works how you would want in almost all cases */ + short uvcalc_flag_orig = G.scene->toolsettings->uvcalc_flag; + G.scene->toolsettings->uvcalc_flag |= UVCALC_TRANSFORM_CORRECT; + + if(!EdgeSlide(1, 1)) { + return 0; + } + + /* restore uvcalc flag */ + G.scene->toolsettings->uvcalc_flag = uvcalc_flag_orig; + + EM_select_more(); + removedoublesflag(1,0, 0.001); + EM_select_flush(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + return 1; +} + +int EdgeSlide(short immediate, float imperc) +{ + NumInput num; + EditMesh *em = G.editMesh; + EditFace *efa; + EditEdge *eed,*first=NULL,*last=NULL, *temp = NULL; + EditVert *ev, *nearest; + LinkNode *edgelist = NULL, *vertlist=NULL, *look; + GHash *vertgh; + + SlideVert *tempsv; + float perc = 0, percp = 0,vertdist, projectMat[4][4], viewMat[4][4]; + float shiftlabda= 0.0f,len = 0.0f; + int i = 0,j, numsel, numadded=0, timesthrough = 0, vertsel=0, prop=1, cancel = 0,flip=0; + int wasshift = 0; + + /* UV correction vars */ + GHash **uvarray= NULL; + int uvlay_tot= CustomData_number_of_layers(&G.editMesh->fdata, CD_MTFACE); + int uvlay_idx; + SlideUv *slideuvs=NULL, *suv=NULL, *suv_last=NULL; + float uv_tmp[2]; + LinkNode *fuv_link; + + short event, draw=1; + short mval[2], mvalo[2]; + char str[128]; + float labda = 0.0f; + + initNumInput(&num); + + view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat); + + mvalo[0] = -1; mvalo[1] = -1; + numsel =0; + + // Get number of selected edges and clear some flags + for(eed=em->edges.first;eed;eed=eed->next) { + eed->f1 = 0; + eed->f2 = 0; + if(eed->f & SELECT) numsel++; + } + + for(ev=em->verts.first;ev;ev=ev->next) { + ev->f1 = 0; + } + + //Make sure each edge only has 2 faces + // make sure loop doesn't cross face + for(efa=em->faces.first;efa;efa=efa->next) { + int ct = 0; + if(efa->e1->f & SELECT) { + ct++; + efa->e1->f1++; + if(efa->e1->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + if(efa->e2->f & SELECT) { + ct++; + efa->e2->f1++; + if(efa->e2->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + if(efa->e3->f & SELECT) { + ct++; + efa->e3->f1++; + if(efa->e3->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + if(efa->e4 && efa->e4->f & SELECT) { + ct++; + efa->e4->f1++; + if(efa->e4->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + // Make sure loop is not 2 edges of same face + if(ct > 1) { + error("loop crosses itself"); + return 0; + } + } + // Get # of selected verts + for(ev=em->verts.first;ev;ev=ev->next) { + if(ev->f & SELECT) vertsel++; + } + + // Test for multiple segments + if(vertsel > numsel+1) { + error("Was not a single edge loop"); + return 0; + } + + // Get the edgeloop in order - mark f1 with SELECT once added + for(eed=em->edges.first;eed;eed=eed->next) { + if((eed->f & SELECT) && !(eed->f1 & SELECT)) { + // If this is the first edge added, just put it in + if(!edgelist) { + BLI_linklist_prepend(&edgelist,eed); + numadded++; + first = eed; + last = eed; + eed->f1 = SELECT; + } else { + if(editedge_getSharedVert(eed, last)) { + BLI_linklist_append(&edgelist,eed); + eed->f1 = SELECT; + numadded++; + last = eed; + } else if(editedge_getSharedVert(eed, first)) { + BLI_linklist_prepend(&edgelist,eed); + eed->f1 = SELECT; + numadded++; + first = eed; + } + } + } + if(eed->next == NULL && numadded != numsel) { + eed=em->edges.first; + timesthrough++; + } + + // It looks like there was an unexpected case - Hopefully should not happen + if(timesthrough >= numsel*2) { + BLI_linklist_free(edgelist,NULL); + error("could not order loop"); + return 0; + } + } + + // Put the verts in order in a linklist + look = edgelist; + while(look) { + eed = look->link; + if(!vertlist) { + if(look->next) { + temp = look->next->link; + + //This is the first entry takes care of extra vert + if(eed->v1 != temp->v1 && eed->v1 != temp->v2) { + BLI_linklist_append(&vertlist,eed->v1); + eed->v1->f1 = 1; + } else { + BLI_linklist_append(&vertlist,eed->v2); + eed->v2->f1 = 1; + } + } else { + //This is the case that we only have 1 edge + BLI_linklist_append(&vertlist,eed->v1); + eed->v1->f1 = 1; + } + } + // for all the entries + if(eed->v1->f1 != 1) { + BLI_linklist_append(&vertlist,eed->v1); + eed->v1->f1 = 1; + } else if(eed->v2->f1 != 1) { + BLI_linklist_append(&vertlist,eed->v2); + eed->v2->f1 = 1; + } + look = look->next; + } + + // populate the SlideVerts + + vertgh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + look = vertlist; + while(look) { + i=0; + j=0; + ev = look->link; + tempsv = (struct SlideVert*)MEM_mallocN(sizeof(struct SlideVert),"SlideVert"); + tempsv->up = NULL; + tempsv->down = NULL; + tempsv->origvert.co[0] = ev->co[0]; + tempsv->origvert.co[1] = ev->co[1]; + tempsv->origvert.co[2] = ev->co[2]; + tempsv->origvert.no[0] = ev->no[0]; + tempsv->origvert.no[1] = ev->no[1]; + tempsv->origvert.no[2] = ev->no[2]; + // i is total edges that vert is on + // j is total selected edges that vert is on + + for(eed=em->edges.first;eed;eed=eed->next) { + if(eed->v1 == ev || eed->v2 == ev) { + i++; + if(eed->f & SELECT) { + j++; + } + } + } + // If the vert is in the middle of an edge loop, it touches 2 selected edges and 2 unselected edges + if(i == 4 && j == 2) { + for(eed=em->edges.first;eed;eed=eed->next) { + if(editedge_containsVert(eed, ev)) { + if(!(eed->f & SELECT)) { + if(!tempsv->up) { + tempsv->up = eed; + } else if (!(tempsv->down)) { + tempsv->down = eed; + } + } + } + } + } + // If it is on the end of the loop, it touches 1 selected and as least 2 more unselected + if(i >= 3 && j == 1) { + for(eed=em->edges.first;eed;eed=eed->next) { + if(editedge_containsVert(eed, ev) && eed->f & SELECT) { + for(efa = em->faces.first;efa;efa=efa->next) { + if(editface_containsEdge(efa, eed)) { + if(editedge_containsVert(efa->e1, ev) && efa->e1 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e1; + } else if (!(tempsv->down)) { + tempsv->down = efa->e1; + } + } + if(editedge_containsVert(efa->e2, ev) && efa->e2 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e2; + } else if (!(tempsv->down)) { + tempsv->down = efa->e2; + } + } + if(editedge_containsVert(efa->e3, ev) && efa->e3 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e3; + } else if (!(tempsv->down)) { + tempsv->down = efa->e3; + } + } + if(efa->e4) { + if(editedge_containsVert(efa->e4, ev) && efa->e4 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e4; + } else if (!(tempsv->down)) { + tempsv->down = efa->e4; + } + } + } + + } + } + } + } + } + if(i > 4 && j == 2) { + BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); + BLI_linklist_free(vertlist,NULL); + BLI_linklist_free(edgelist,NULL); + return 0; + } + BLI_ghash_insert(vertgh,ev,tempsv); + + look = look->next; + } + + // make sure the UPs nad DOWNs are 'faceloops' + // Also find the nearest slidevert to the cursor + getmouseco_areawin(mval); + look = vertlist; + nearest = NULL; + vertdist = -1; + while(look) { + tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); + + if(!tempsv->up || !tempsv->down) { + error("Missing rails"); + BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); + BLI_linklist_free(vertlist,NULL); + BLI_linklist_free(edgelist,NULL); + return 0; + } + + if(G.f & G_DRAW_EDGELEN) { + if(!(tempsv->up->f & SELECT)) { + tempsv->up->f |= SELECT; + tempsv->up->f2 |= 16; + } else { + tempsv->up->f2 |= ~16; + } + if(!(tempsv->down->f & SELECT)) { + tempsv->down->f |= SELECT; + tempsv->down->f2 |= 16; + } else { + tempsv->down->f2 |= ~16; + } + } + + if(look->next != NULL) { + SlideVert *sv; + + sv = BLI_ghash_lookup(vertgh,(EditVert*)look->next->link); + + if(sv) { + float tempdist, co[2]; + + if(!sharesFace(tempsv->up,sv->up)) { + EditEdge *swap; + swap = sv->up; + sv->up = sv->down; + sv->down = swap; + } + + view3d_project_float(curarea, tempsv->origvert.co, co, projectMat); + + tempdist = sqrt(pow(co[0] - mval[0],2)+pow(co[1] - mval[1],2)); + + if(vertdist < 0) { + vertdist = tempdist; + nearest = (EditVert*)look->link; + } else if ( tempdist < vertdist ) { + vertdist = tempdist; + nearest = (EditVert*)look->link; + } + } + } + + + + look = look->next; + } + + + if (uvlay_tot && (G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + int maxnum = 0; + uvarray = MEM_callocN( uvlay_tot * sizeof(GHash *), "SlideUVs Array"); + suv_last = slideuvs = MEM_callocN( uvlay_tot * (numadded+1) * sizeof(SlideUv), "SlideUVs"); /* uvLayers * verts */ + suv = NULL; + + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + + uvarray[uvlay_idx] = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + for(ev=em->verts.first;ev;ev=ev->next) { + ev->tmp.l = 0; + } + look = vertlist; + while(look) { + float *uv_new; + tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); + + ev = look->link; + suv = NULL; + for(efa = em->faces.first;efa;efa=efa->next) { + if (ev->tmp.l != -1) { /* test for self, in this case its invalid */ + int k=-1; /* face corner */ + + /* Is this vert in the faces corner? */ + if (efa->v1==ev) k=0; + else if (efa->v2==ev) k=1; + else if (efa->v3==ev) k=2; + else if (efa->v4 && efa->v4==ev) k=3; + + if (k != -1) { + MTFace *tf = CustomData_em_get_n(&em->fdata, efa->data, CD_MTFACE, uvlay_idx); + EditVert *ev_up, *ev_down; + + uv_new = tf->uv[k]; + + if (ev->tmp.l) { + if (fabs(suv->origuv[0]-uv_new[0]) > 0.0001 || fabs(suv->origuv[1]-uv_new[1])) { + ev->tmp.l = -1; /* Tag as invalid */ + BLI_linklist_free(suv->fuv_list,NULL); + suv->fuv_list = NULL; + BLI_ghash_remove(uvarray[uvlay_idx],ev, NULL, NULL); + suv = NULL; + break; + } + } else { + ev->tmp.l = 1; + suv = suv_last; + + suv->fuv_list = NULL; + suv->uv_up = suv->uv_down = NULL; + suv->origuv[0] = uv_new[0]; + suv->origuv[1] = uv_new[1]; + + BLI_linklist_prepend(&suv->fuv_list, uv_new); + BLI_ghash_insert(uvarray[uvlay_idx],ev,suv); + + suv_last++; /* advance to next slide UV */ + maxnum++; + } + + /* Now get the uvs along the up or down edge if we can */ + if (suv) { + if (!suv->uv_up) { + ev_up = editedge_getOtherVert(tempsv->up,ev); + if (efa->v1==ev_up) suv->uv_up = tf->uv[0]; + else if (efa->v2==ev_up) suv->uv_up = tf->uv[1]; + else if (efa->v3==ev_up) suv->uv_up = tf->uv[2]; + else if (efa->v4 && efa->v4==ev_up) suv->uv_up = tf->uv[3]; + } + if (!suv->uv_down) { /* if the first face was apart of the up edge, it cant be apart of the down edge */ + ev_down = editedge_getOtherVert(tempsv->down,ev); + if (efa->v1==ev_down) suv->uv_down = tf->uv[0]; + else if (efa->v2==ev_down) suv->uv_down = tf->uv[1]; + else if (efa->v3==ev_down) suv->uv_down = tf->uv[2]; + else if (efa->v4 && efa->v4==ev_down) suv->uv_down = tf->uv[3]; + } + + /* Copy the pointers to the face UV's */ + BLI_linklist_prepend(&suv->fuv_list, uv_new); + } + } + } + } + look = look->next; + } + } /* end uv layer loop */ + } /* end uvlay_tot */ + + + + // we should have enough info now to slide + + len = 0.0f; + + percp = -1; + while(draw) { + /* For the % calculation */ + short mval[2]; + float rc[2]; + float v2[2], v3[2]; + EditVert *centerVert, *upVert, *downVert; + + getmouseco_areawin(mval); + + if (!immediate && (mval[0] == mvalo[0] && mval[1] == mvalo[1])) { + PIL_sleep_ms(10); + } else { + char *p = str;; + + mvalo[0] = mval[0]; + mvalo[1] = mval[1]; + + + tempsv = BLI_ghash_lookup(vertgh,nearest); + + centerVert = editedge_getSharedVert(tempsv->up, tempsv->down); + upVert = editedge_getOtherVert(tempsv->up, centerVert); + downVert = editedge_getOtherVert(tempsv->down, centerVert); + + view3d_project_float(curarea, upVert->co, v2, projectMat); + view3d_project_float(curarea, downVert->co, v3, projectMat); + + /* Determine the % on which the loop should be cut */ + + rc[0]= v3[0]-v2[0]; + rc[1]= v3[1]-v2[1]; + len= rc[0]*rc[0]+ rc[1]*rc[1]; + if (len==0) {len = 0.0001;} + + if ((G.qual & LR_SHIFTKEY)==0) { + wasshift = 0; + labda= ( rc[0]*((mval[0]-v2[0])) + rc[1]*((mval[1]-v2[1])) )/len; + } + else { + if (wasshift==0) { + wasshift = 1; + shiftlabda = labda; + } + labda= ( rc[0]*((mval[0]-v2[0])) + rc[1]*((mval[1]-v2[1])) )/len / 10.0 + shiftlabda; + } + + + if(labda<=0.0) labda=0.0; + else if(labda>=1.0)labda=1.0; + + perc=((1-labda)*2)-1; + + if(G.qual == 0) { + perc *= 100; + perc = floor(perc); + perc /= 100; + } else if (G.qual == LR_CTRLKEY) { + perc *= 10; + perc = floor(perc); + perc /= 10; + } + + if(prop == 0) { + len = VecLenf(upVert->co,downVert->co)*((perc+1)/2); + if(flip == 1) { + len = VecLenf(upVert->co,downVert->co) - len; + } + } + + if (hasNumInput(&num)) + { + applyNumInput(&num, &perc); + + if (prop) + { + perc = MIN2(perc, 1); + perc = MAX2(perc, -1); + } + else + { + len = MIN2(perc, VecLenf(upVert->co,downVert->co)); + len = MAX2(len, 0); + } + } + + //Adjust Edgeloop + if(immediate) { + perc = imperc; + } + percp = perc; + if(prop) { + look = vertlist; + while(look) { + EditVert *tempev; + ev = look->link; + tempsv = BLI_ghash_lookup(vertgh,ev); + + tempev = editedge_getOtherVert((perc>=0)?tempsv->up:tempsv->down, ev); + VecLerpf(ev->co, tempsv->origvert.co, tempev->co, fabs(perc)); + + if (G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + suv = BLI_ghash_lookup( uvarray[uvlay_idx], ev ); + if (suv && suv->fuv_list && suv->uv_up && suv->uv_down) { + Vec2Lerpf(uv_tmp, suv->origuv, (perc>=0)?suv->uv_up:suv->uv_down, fabs(perc)); + fuv_link = suv->fuv_list; + while (fuv_link) { + VECCOPY2D(((float *)fuv_link->link), uv_tmp); + fuv_link = fuv_link->next; + } + } + } + } + + look = look->next; + } + } + else { + //Non prop code + look = vertlist; + while(look) { + float newlen; + ev = look->link; + tempsv = BLI_ghash_lookup(vertgh,ev); + newlen = (len / VecLenf(editedge_getOtherVert(tempsv->up,ev)->co,editedge_getOtherVert(tempsv->down,ev)->co)); + if(newlen > 1.0) {newlen = 1.0;} + if(newlen < 0.0) {newlen = 0.0;} + if(flip == 0) { + VecLerpf(ev->co, editedge_getOtherVert(tempsv->down,ev)->co, editedge_getOtherVert(tempsv->up,ev)->co, fabs(newlen)); + if (G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { + /* dont do anything if no UVs */ + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + suv = BLI_ghash_lookup( uvarray[uvlay_idx], ev ); + if (suv && suv->fuv_list && suv->uv_up && suv->uv_down) { + Vec2Lerpf(uv_tmp, suv->uv_down, suv->uv_up, fabs(newlen)); + fuv_link = suv->fuv_list; + while (fuv_link) { + VECCOPY2D(((float *)fuv_link->link), uv_tmp); + fuv_link = fuv_link->next; + } + } + } + } + } else{ + VecLerpf(ev->co, editedge_getOtherVert(tempsv->up,ev)->co, editedge_getOtherVert(tempsv->down,ev)->co, fabs(newlen)); + + if (G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { + /* dont do anything if no UVs */ + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + suv = BLI_ghash_lookup( uvarray[uvlay_idx], ev ); + if (suv && suv->fuv_list && suv->uv_up && suv->uv_down) { + Vec2Lerpf(uv_tmp, suv->uv_up, suv->uv_down, fabs(newlen)); + fuv_link = suv->fuv_list; + while (fuv_link) { + VECCOPY2D(((float *)fuv_link->link), uv_tmp); + fuv_link = fuv_link->next; + } + } + } + } + } + look = look->next; + } + + } + + // Highlight the Control Edges + scrarea_do_windraw(curarea); + persp(PERSP_VIEW); + glPushMatrix(); + mymultmatrix(G.obedit->obmat); + + glColor3ub(0, 255, 0); + glBegin(GL_LINES); + glVertex3fv(upVert->co); + glVertex3fv(downVert->co); + glEnd(); + + if(prop == 0) { + // draw start edge for non-prop + glPointSize(5); + glBegin(GL_POINTS); + glColor3ub(255,0,255); + if(flip) { + glVertex3fv(upVert->co); + } else { + glVertex3fv(downVert->co); + } + glEnd(); + } + + + glPopMatrix(); + + if(prop) { + p += sprintf(str, "(P)ercentage: "); + } else { + p += sprintf(str, "Non (P)rop Length: "); + } + + if (hasNumInput(&num)) + { + char num_str[20]; + + outputNumInput(&num, num_str); + p += sprintf(p, "%s", num_str); + } + else + { + if (prop) + { + p += sprintf(p, "%f", perc); + } + else + { + p += sprintf(p, "%f", len); + } + } + + + if (prop == 0) { + p += sprintf(p, ", Press (F) to flip control side"); + } + + headerprint(str); + screen_swapbuffers(); + } + if(!immediate) { + while(qtest()) { + short val=0; + event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle + + /* val==0 on key-release event */ + if (val) { + if(ELEM(event, ESCKEY, RIGHTMOUSE)) { + prop = 1; // Go back to prop mode + imperc = 0; // This is the % that gets set for immediate + immediate = 1; //Run through eval code 1 more time + cancel = 1; // Return -1 + mvalo[0] = -1; + } else if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)) { + draw = 0; // End looping now + } else if(event==MIDDLEMOUSE) { + perc = 0; + immediate = 1; + } else if(event==PKEY) { + initNumInput(&num); /* reset num input */ + if (prop) { + prop = 0; + num.flag |= NUM_NO_NEGATIVE; + } + else { + prop = 1; + } + mvalo[0] = -1; + } else if(event==FKEY) { + (flip == 1) ? (flip = 0):(flip = 1); + mvalo[0] = -1; + } else if(ELEM(event, RIGHTARROWKEY, WHEELUPMOUSE)) { // Scroll through Control Edges + look = vertlist; + while(look) { + if(nearest == (EditVert*)look->link) { + if(look->next == NULL) { + nearest = (EditVert*)vertlist->link; + } else { + nearest = (EditVert*)look->next->link; + } + mvalo[0] = -1; + break; + } + look = look->next; + } + } else if(ELEM(event, LEFTARROWKEY, WHEELDOWNMOUSE)) { // Scroll through Control Edges + look = vertlist; + while(look) { + if(look->next) { + if(look->next->link == nearest) { + nearest = (EditVert*)look->link; + mvalo[0] = -1; + break; + } + } else { + if((EditVert*)vertlist->link == nearest) { + nearest = look->link; + mvalo[0] = -1; + break; + } + } + look = look->next; + } + } + + if (handleNumInput(&num, event)) + { + mvalo[0] = -1; /* NEED A BETTER WAY TO TRIGGER REDRAW */ + } + } + + } + } else { + draw = 0; + } + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + } + + + if(G.f & G_DRAW_EDGELEN) { + look = vertlist; + while(look) { + tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); + if(tempsv != NULL) { + tempsv->up->f &= !SELECT; + tempsv->down->f &= !SELECT; + } + look = look->next; + } + } + + force_draw(0); + + if(!immediate) + EM_automerge(0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + scrarea_queue_winredraw(curarea); + + //BLI_ghash_free(edgesgh, freeGHash, NULL); + BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); + BLI_linklist_free(vertlist,NULL); + BLI_linklist_free(edgelist,NULL); + + if (uvlay_tot && (G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + BLI_ghash_free(uvarray[uvlay_idx], NULL, NULL); + } + MEM_freeN(uvarray); + MEM_freeN(slideuvs); + + suv = suv_last-1; + while (suv >= slideuvs) { + if (suv->fuv_list) { + BLI_linklist_free(suv->fuv_list,NULL); + } + suv--; + } + + allqueue(REDRAWIMAGE, 0); + } + + if(cancel == 1) { + return -1; + } + else { +#ifdef WITH_VERSE + if(G.editMesh->vnode) { + sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode); + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); + } +#endif + } + return 1; +} + +/* -------------------- More tools ------------------ */ + +void mesh_set_face_flags(short mode) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + MTFace *tface; + short m_tex=0, m_tiles=0, m_shared=0, + m_light=0, m_invis=0, m_collision=0, + m_twoside=0, m_obcolor=0, m_halo=0, + m_billboard=0, m_shadow=0, m_text=0, + m_sort=0; + short flag = 0, change = 0; + + if (!EM_texFaceCheck()) { + error("not a mesh with uv/image layers"); + return; + } + + add_numbut(0, TOG|SHO, "Texture", 0, 0, &m_tex, NULL); + add_numbut(1, TOG|SHO, "Tiles", 0, 0, &m_tiles, NULL); + add_numbut(2, TOG|SHO, "Light", 0, 0, &m_light, NULL); + add_numbut(3, TOG|SHO, "Invisible", 0, 0, &m_invis, NULL); + add_numbut(4, TOG|SHO, "Collision", 0, 0, &m_collision, NULL); + add_numbut(5, TOG|SHO, "Shared", 0, 0, &m_shared, NULL); + add_numbut(6, TOG|SHO, "Twoside", 0, 0, &m_twoside, NULL); + add_numbut(7, TOG|SHO, "ObColor", 0, 0, &m_obcolor, NULL); + add_numbut(8, TOG|SHO, "Halo", 0, 0, &m_halo, NULL); + add_numbut(9, TOG|SHO, "Billboard", 0, 0, &m_billboard, NULL); + add_numbut(10, TOG|SHO, "Shadow", 0, 0, &m_shadow, NULL); + add_numbut(11, TOG|SHO, "Text", 0, 0, &m_text, NULL); + add_numbut(12, TOG|SHO, "Sort", 0, 0, &m_sort, NULL); + + if (!do_clever_numbuts((mode ? "Set Flags" : "Clear Flags"), 13, REDRAW)) + return; + + /* these 2 cant both be on */ + if (mode) /* are we seeting*/ + if (m_halo) + m_billboard = 0; + + if (m_tex) flag |= TF_TEX; + if (m_tiles) flag |= TF_TILES; + if (m_shared) flag |= TF_SHAREDCOL; + if (m_light) flag |= TF_LIGHT; + if (m_invis) flag |= TF_INVISIBLE; + if (m_collision) flag |= TF_DYNAMIC; + if (m_twoside) flag |= TF_TWOSIDE; + if (m_obcolor) flag |= TF_OBCOL; + if (m_halo) flag |= TF_BILLBOARD; + if (m_billboard) flag |= TF_BILLBOARD2; + if (m_shadow) flag |= TF_SHADOW; + if (m_text) flag |= TF_BMFONT; + if (m_sort) flag |= TF_ALPHASORT; + + if (flag==0) + return; + + efa= em->faces.first; + while(efa) { + if(efa->f & SELECT) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (mode) tface->mode |= flag; + else tface->mode &= ~flag; + change = 1; + } + efa= efa->next; + } + + if (change) { + BIF_undo_push((mode ? "Set Flags" : "Clear Flags")); + + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWVIEW3D, 0); + } +} + +void mesh_set_smooth_faces(short event) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + + if(G.obedit==0) return; + + if(G.obedit->type != OB_MESH) return; + + efa= em->faces.first; + while(efa) { + if(efa->f & SELECT) { + if(event==1) efa->flag |= ME_SMOOTH; + else if(event==0) efa->flag &= ~ME_SMOOTH; + } + efa= efa->next; + } + + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + + if(event==1) BIF_undo_push("Set Smooth"); + else if(event==0) BIF_undo_push("Set Solid"); +} + +/* helper to find edge for edge_rip */ +static float mesh_rip_edgedist(float mat[][4], float *co1, float *co2, short *mval) +{ + float vec1[3], vec2[3], mvalf[2]; + + view3d_project_float(curarea, co1, vec1, mat); + view3d_project_float(curarea, co2, vec2, mat); + mvalf[0]= (float)mval[0]; + mvalf[1]= (float)mval[1]; + + return PdistVL2Dfl(mvalf, vec1, vec2); +} + +/* helper for below */ +static void mesh_rip_setface(EditFace *sefa) +{ + /* put new vertices & edges in best face */ + if(sefa->v1->tmp.v) sefa->v1= sefa->v1->tmp.v; + if(sefa->v2->tmp.v) sefa->v2= sefa->v2->tmp.v; + if(sefa->v3->tmp.v) sefa->v3= sefa->v3->tmp.v; + if(sefa->v4 && sefa->v4->tmp.v) sefa->v4= sefa->v4->tmp.v; + + sefa->e1= addedgelist(sefa->v1, sefa->v2, sefa->e1); + sefa->e2= addedgelist(sefa->v2, sefa->v3, sefa->e2); + if(sefa->v4) { + sefa->e3= addedgelist(sefa->v3, sefa->v4, sefa->e3); + sefa->e4= addedgelist(sefa->v4, sefa->v1, sefa->e4); + } + else + sefa->e3= addedgelist(sefa->v3, sefa->v1, sefa->e3); + +} + +/* based on mouse cursor position, it defines how is being ripped */ +void mesh_rip(void) +{ + extern void faceloop_select(EditEdge *startedge, int select); + EditMesh *em = G.editMesh; + EditVert *eve, *nextve; + EditEdge *eed, *seed= NULL; + EditFace *efa, *sefa= NULL; + float projectMat[4][4], viewMat[4][4], vec[3], dist, mindist; + short doit= 1, mval[2],propmode,prop; + + propmode = G.scene->prop_mode; + G.scene->prop_mode = 0; + prop = G.scene->proportional; + G.scene->proportional = 0; + + /* select flush... vertices are important */ + EM_selectmode_set(); + + getmouseco_areawin(mval); + view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat); + + /* find best face, exclude triangles and break on face select or faces with 2 edges select */ + mindist= 1000000.0f; + for(efa= em->faces.first; efa; efa=efa->next) { + if( efa->f & 1) + break; + if(efa->v4 && faceselectedOR(efa, SELECT) ) { + int totsel=0; + + if(efa->e1->f & SELECT) totsel++; + if(efa->e2->f & SELECT) totsel++; + if(efa->e3->f & SELECT) totsel++; + if(efa->e4->f & SELECT) totsel++; + + if(totsel>1) + break; + view3d_project_float(curarea, efa->cent, vec, projectMat); + dist= sqrt( (vec[0]-mval[0])*(vec[0]-mval[0]) + (vec[1]-mval[1])*(vec[1]-mval[1]) ); + if(dist<mindist) { + mindist= dist; + sefa= efa; + } + } + } + + if(efa) { + error("Can't perform ripping with faces selected this way"); + return; + } + if(sefa==NULL) { + error("No proper selection or faces included"); + return; + } + + + /* duplicate vertices, new vertices get selected */ + for(eve = em->verts.last; eve; eve= eve->prev) { + eve->tmp.v = NULL; + if(eve->f & SELECT) { + eve->tmp.v = addvertlist(eve->co, eve); + eve->f &= ~SELECT; + eve->tmp.v->f |= SELECT; + } + } + + /* find the best candidate edge */ + /* or one of sefa edges is selected... */ + if(sefa->e1->f & SELECT) seed= sefa->e2; + if(sefa->e2->f & SELECT) seed= sefa->e1; + if(sefa->e3->f & SELECT) seed= sefa->e2; + if(sefa->e4 && sefa->e4->f & SELECT) seed= sefa->e3; + + /* or we do the distance trick */ + if(seed==NULL) { + mindist= 1000000.0f; + if(sefa->e1->v1->tmp.v || sefa->e1->v2->tmp.v) { + dist = mesh_rip_edgedist(projectMat, + sefa->e1->v1->co, + sefa->e1->v2->co, mval); + if(dist<mindist) { + seed= sefa->e1; + mindist= dist; + } + } + if(sefa->e2->v1->tmp.v || sefa->e2->v2->tmp.v) { + dist = mesh_rip_edgedist(projectMat, + sefa->e2->v1->co, + sefa->e2->v2->co, mval); + if(dist<mindist) { + seed= sefa->e2; + mindist= dist; + } + } + if(sefa->e3->v1->tmp.v || sefa->e3->v2->tmp.v) { + dist= mesh_rip_edgedist(projectMat, + sefa->e3->v1->co, + sefa->e3->v2->co, mval); + if(dist<mindist) { + seed= sefa->e3; + mindist= dist; + } + } + if(sefa->e4 && (sefa->e4->v1->tmp.v || sefa->e4->v2->tmp.v)) { + dist= mesh_rip_edgedist(projectMat, + sefa->e4->v1->co, + sefa->e4->v2->co, mval); + if(dist<mindist) { + seed= sefa->e4; + mindist= dist; + } + } + } + + if(seed==NULL) { // never happens? + error("No proper edge found to start"); + return; + } + + faceloop_select(seed, 2); // tmp abuse for finding all edges that need duplicated, returns OK faces with f1 + + /* duplicate edges in the loop, with at least 1 vertex selected, needed for selection flip */ + for(eed = em->edges.last; eed; eed= eed->prev) { + eed->tmp.v = NULL; + if((eed->v1->tmp.v) || (eed->v2->tmp.v)) { + EditEdge *newed; + + newed= addedgelist(eed->v1->tmp.v?eed->v1->tmp.v:eed->v1, + eed->v2->tmp.v?eed->v2->tmp.v:eed->v2, eed); + if(eed->f & SELECT) { + eed->f &= ~SELECT; + newed->f |= SELECT; + } + eed->tmp.v = (EditVert *)newed; + } + } + + /* first clear edges to help finding neighbours */ + for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0; + + /* put new vertices & edges && flag in best face */ + mesh_rip_setface(sefa); + + /* starting with neighbours of best face, we loop over the seam */ + sefa->f1= 2; + doit= 1; + while(doit) { + doit= 0; + + for(efa= em->faces.first; efa; efa=efa->next) { + /* new vert in face */ + if (efa->v1->tmp.v || efa->v2->tmp.v || + efa->v3->tmp.v || (efa->v4 && efa->v4->tmp.v)) { + /* face is tagged with loop */ + if(efa->f1==1) { + mesh_rip_setface(efa); + efa->f1= 2; + doit= 1; + } + } + } + } + + /* remove loose edges, that were part of a ripped face */ + for(eve = em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0; + for(efa= em->faces.first; efa; efa=efa->next) { + efa->e1->f1= 1; + efa->e2->f1= 1; + efa->e3->f1= 1; + if(efa->e4) efa->e4->f1= 1; + } + + for(eed = em->edges.last; eed; eed= seed) { + seed= eed->prev; + if(eed->f1==0) { + if(eed->v1->tmp.v || eed->v2->tmp.v || + (eed->v1->f & SELECT) || (eed->v2->f & SELECT)) { + remedge(eed); + free_editedge(eed); + eed= NULL; + } + } + if(eed) { + eed->v1->f1= 1; + eed->v2->f1= 1; + } + } + + /* and remove loose selected vertices, that got duplicated accidentally */ + for(eve = em->verts.first; eve; eve= nextve) { + nextve= eve->next; + if(eve->f1==0 && (eve->tmp.v || (eve->f & SELECT))) { + BLI_remlink(&em->verts,eve); + free_editvert(eve); + } + } + + countall(); // apparently always needed when adding stuff, derived mesh + +#ifdef WITH_VERSE + if(G.editMesh->vnode) { + sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode); + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); + } +#endif + + BIF_TransformSetUndo("Rip"); + initTransform(TFM_TRANSLATION, 0); + Transform(); + + G.scene->prop_mode = propmode; + G.scene->proportional = prop; +} + +void shape_propagate(){ + EditMesh *em = G.editMesh; + EditVert *ev = NULL; + Mesh* me = (Mesh*)G.obedit->data; + Key* ky = NULL; + KeyBlock* kb = NULL; + Base* base=NULL; + + + if(me->key){ + ky = me->key; + } else { + error("Object Has No Key"); + return; + } + + if(ky->block.first){ + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + for(kb=ky->block.first;kb;kb = kb->next){ + float *data; + data = kb->data; + VECCOPY(data+(ev->keyindex*3),ev->co); + } + } + } + } else { + error("Object Has No Blendshapes"); + return; + } + + //TAG Mesh Objects that share this data + for(base = G.scene->base.first; base; base = base->next){ + if(base->object && base->object->data == me){ + base->object->recalc = OB_RECALC_DATA; + } + } + + BIF_undo_push("Propagate Blendshape Verts"); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + return; +} + +void shape_copy_from_lerp(KeyBlock* thisBlock, KeyBlock* fromBlock) +{ + EditMesh *em = G.editMesh; + EditVert *ev = NULL; + short mval[2], curval[2], event = 0, finished = 0, canceled = 0, fullcopy=0 ; + float perc = 0; + char str[64]; + float *data, *odata; + + data = fromBlock->data; + odata = thisBlock->data; + + getmouseco_areawin(mval); + curval[0] = mval[0] + 1; curval[1] = mval[1] + 1; + + while (finished == 0) + { + getmouseco_areawin(mval); + if (mval[0] != curval[0] || mval[1] != curval[1]) + { + + if(mval[0] > curval[0]) + perc += 0.1; + else if(mval[0] < curval[0]) + perc -= 0.1; + + if(perc < 0) perc = 0; + if(perc > 1) perc = 1; + + curval[0] = mval[0]; + curval[1] = mval[1]; + + if(fullcopy == 1){ + perc = 1; + } + + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + VecLerpf(ev->co,odata+(ev->keyindex*3),data+(ev->keyindex*3),perc); + } + } + sprintf(str,"Blending at %d%c MMB to Copy at 100%c",(int)(perc*100),'%','%'); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + headerprint(str); + force_draw(0); + + if(fullcopy == 1){ + break; + } + + } else { + PIL_sleep_ms(10); + } + + while(qtest()) { + short val=0; + event= extern_qread(&val); + if(val){ + if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)){ + finished = 1; + } + else if (event == MIDDLEMOUSE){ + fullcopy = 1; + } + else if (ELEM3(event,ESCKEY,RIGHTMOUSE,RIGHTMOUSE)){ + canceled = 1; + finished = 1; + } + } + } + } + if(!canceled) + BIF_undo_push("Copy Blendshape Verts"); + else + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + VECCOPY(ev->co, odata+(ev->keyindex*3)); + } + } + return; +} + + + +void shape_copy_select_from() +{ + Mesh* me = (Mesh*)G.obedit->data; + EditMesh *em = G.editMesh; + EditVert *ev = NULL; + int totverts = 0,curshape = G.obedit->shapenr; + + Key* ky = NULL; + KeyBlock *kb = NULL,*thisBlock = NULL; + int maxlen=32, nr=0, a=0; + char *menu; + + if(me->key){ + ky = me->key; + } else { + error("Object Has No Key"); + return; + } + + if(ky->block.first){ + for(kb=ky->block.first;kb;kb = kb->next){ + maxlen += 40; // Size of a block name + if(a == curshape-1){ + thisBlock = kb; + } + + a++; + } + a=0; + menu = MEM_callocN(maxlen, "Copy Shape Menu Text"); + strcpy(menu, "Copy Vert Positions from Shape %t|"); + for(kb=ky->block.first;kb;kb = kb->next){ + if(a != curshape-1){ + sprintf(menu,"%s %s %cx%d|",menu,kb->name,'%',a); + } + a++; + } + nr = pupmenu_col(menu, 20); + MEM_freeN(menu); + } else { + error("Object Has No Blendshapes"); + return; + } + + a = 0; + + for(kb=ky->block.first;kb;kb = kb->next){ + if(a == nr){ + + for(ev = em->verts.first;ev;ev = ev->next){ + totverts++; + } + + if(me->totvert != totverts){ + error("Shape Has had Verts Added/Removed, please cycle editmode before copying"); + return; + } + shape_copy_from_lerp(thisBlock,kb); + + return; + } + a++; + } + return; +} + +/* Collection Routines|Currently used by the improved merge code*/ +/* buildEdge_collection() creates a list of lists*/ +/* these lists are filled with edges that are topologically connected.*/ +/* This whole tool needs to be redone, its rather poorly implemented...*/ + +typedef struct Collection{ + struct Collection *next, *prev; + int index; + ListBase collectionbase; +} Collection; + +typedef struct CollectedEdge{ + struct CollectedEdge *next, *prev; + EditEdge *eed; +} CollectedEdge; + +#define MERGELIMIT 0.000001 + +static void build_edgecollection(ListBase *allcollections) +{ + EditEdge *eed; + Collection *edgecollection, *newcollection; + CollectedEdge *newedge; + + int currtag = 1; + short ebalanced = 0; + short collectionfound = 0; + + for (eed=G.editMesh->edges.first; eed; eed = eed->next){ + eed->tmp.l = 0; + eed->v1->tmp.l = 0; + eed->v2->tmp.l = 0; + } + + /*1st pass*/ + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(eed->f&SELECT){ + eed->v1->tmp.l = currtag; + eed->v2->tmp.l = currtag; + currtag +=1; + } + } + + /*2nd pass - Brute force. Loop through selected faces until there are no 'unbalanced' edges left (those with both vertices 'tmp.l' tag matching */ + while(ebalanced == 0){ + ebalanced = 1; + for(eed=G.editMesh->edges.first; eed; eed = eed->next){ + if(eed->f&SELECT){ + if(eed->v1->tmp.l != eed->v2->tmp.l) /*unbalanced*/{ + if(eed->v1->tmp.l > eed->v2->tmp.l && eed->v2->tmp.l !=0) eed->v1->tmp.l = eed->v2->tmp.l; + else if(eed->v1 != 0) eed->v2->tmp.l = eed->v1->tmp.l; + ebalanced = 0; + } + } + } + } + + /*3rd pass, set all the edge flags (unnessecary?)*/ + for(eed=G.editMesh->edges.first; eed; eed = eed->next){ + if(eed->f&SELECT) eed->tmp.l = eed->v1->tmp.l; + } + + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(eed->f&SELECT){ + if(allcollections->first){ + for(edgecollection = allcollections->first; edgecollection; edgecollection=edgecollection->next){ + if(edgecollection->index == eed->tmp.l){ + newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); + newedge->eed = eed; + BLI_addtail(&(edgecollection->collectionbase), newedge); + collectionfound = 1; + break; + } + else collectionfound = 0; + } + } + if(allcollections->first == NULL || collectionfound == 0){ + newcollection = MEM_mallocN(sizeof(Collection), "element collection"); + newcollection->index = eed->tmp.l; + newcollection->collectionbase.first = 0; + newcollection->collectionbase.last = 0; + + newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); + newedge->eed = eed; + + BLI_addtail(&(newcollection->collectionbase), newedge); + BLI_addtail(allcollections, newcollection); + } + } + + } +} + +static void freecollections(ListBase *allcollections) +{ + struct Collection *curcollection; + + for(curcollection = allcollections->first; curcollection; curcollection = curcollection->next) + BLI_freelistN(&(curcollection->collectionbase)); + BLI_freelistN(allcollections); +} + +/*Begin UV Edge Collapse Code + Like Edge subdivide, Edge Collapse should handle UV's intelligently, but since UV's are a per-face attribute, normal edge collapse will fail + in areas such as the boundries of 'UV islands'. So for each edge collection we need to build a set of 'welded' UV vertices and edges for it. + The welded UV edges can then be sorted and collapsed. +*/ +typedef struct wUV{ + struct wUV *next, *prev; + ListBase nodes; + float u, v; /*cached copy of UV coordinates pointed to by nodes*/ + EditVert *eve; + int f; +} wUV; + +typedef struct wUVNode{ + struct wUVNode *next, *prev; + float *u; /*pointer to original tface data*/ + float *v; /*pointer to original tface data*/ +} wUVNode; + +typedef struct wUVEdge{ + struct wUVEdge *next, *prev; + float v1uv[2], v2uv[2]; /*nasty.*/ + struct wUV *v1, *v2; /*oriented same as editedge*/ + EditEdge *eed; + int f; +} wUVEdge; + +typedef struct wUVEdgeCollect{ /*used for grouping*/ + struct wUVEdgeCollect *next, *prev; + wUVEdge *uved; + int id; +} wUVEdgeCollect; + +static void append_weldedUV(EditFace *efa, EditVert *eve, int tfindex, ListBase *uvverts) +{ + wUV *curwvert, *newwvert; + wUVNode *newnode; + int found; + MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + found = 0; + + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwvert->eve == eve && curwvert->u == tf->uv[tfindex][0] && curwvert->v == tf->uv[tfindex][1]){ + newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); + newnode->u = &(tf->uv[tfindex][0]); + newnode->v = &(tf->uv[tfindex][1]); + BLI_addtail(&(curwvert->nodes), newnode); + found = 1; + break; + } + } + + if(!found){ + newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); + newnode->u = &(tf->uv[tfindex][0]); + newnode->v = &(tf->uv[tfindex][1]); + + newwvert = MEM_callocN(sizeof(wUV), "Welded UV Vert"); + newwvert->u = *(newnode->u); + newwvert->v = *(newnode->v); + newwvert->eve = eve; + + BLI_addtail(&(newwvert->nodes), newnode); + BLI_addtail(uvverts, newwvert); + + } +} + +static void build_weldedUVs(ListBase *uvverts) +{ + EditFace *efa; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->v1->f1) append_weldedUV(efa, efa->v1, 0, uvverts); + if(efa->v2->f1) append_weldedUV(efa, efa->v2, 1, uvverts); + if(efa->v3->f1) append_weldedUV(efa, efa->v3, 2, uvverts); + if(efa->v4 && efa->v4->f1) append_weldedUV(efa, efa->v4, 3, uvverts); + } +} + +static void append_weldedUVEdge(EditFace *efa, EditEdge *eed, ListBase *uvedges) +{ + wUVEdge *curwedge, *newwedge; + int v1tfindex, v2tfindex, found; + MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + found = 0; + + if(eed->v1 == efa->v1) v1tfindex = 0; + else if(eed->v1 == efa->v2) v1tfindex = 1; + else if(eed->v1 == efa->v3) v1tfindex = 2; + else /* if(eed->v1 == efa->v4) */ v1tfindex = 3; + + if(eed->v2 == efa->v1) v2tfindex = 0; + else if(eed->v2 == efa->v2) v2tfindex = 1; + else if(eed->v2 == efa->v3) v2tfindex = 2; + else /* if(eed->v2 == efa->v4) */ v2tfindex = 3; + + for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ + if(curwedge->eed == eed && curwedge->v1uv[0] == tf->uv[v1tfindex][0] && curwedge->v1uv[1] == tf->uv[v1tfindex][1] && curwedge->v2uv[0] == tf->uv[v2tfindex][0] && curwedge->v2uv[1] == tf->uv[v2tfindex][1]){ + found = 1; + break; //do nothing, we don't need another welded uv edge + } + } + + if(!found){ + newwedge = MEM_callocN(sizeof(wUVEdge), "Welded UV Edge"); + newwedge->v1uv[0] = tf->uv[v1tfindex][0]; + newwedge->v1uv[1] = tf->uv[v1tfindex][1]; + newwedge->v2uv[0] = tf->uv[v2tfindex][0]; + newwedge->v2uv[1] = tf->uv[v2tfindex][1]; + newwedge->eed = eed; + + BLI_addtail(uvedges, newwedge); + } +} + +static void build_weldedUVEdges(ListBase *uvedges, ListBase *uvverts) +{ + wUV *curwvert; + wUVEdge *curwedge; + EditFace *efa; + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->e1->f1) append_weldedUVEdge(efa, efa->e1, uvedges); + if(efa->e2->f1) append_weldedUVEdge(efa, efa->e2, uvedges); + if(efa->e3->f1) append_weldedUVEdge(efa, efa->e3, uvedges); + if(efa->e4 && efa->e4->f1) append_weldedUVEdge(efa, efa->e4, uvedges); + } + + + //link vertices: for each uvedge, search uvverts to populate v1 and v2 pointers + for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwedge->eed->v1 == curwvert->eve && curwedge->v1uv[0] == curwvert->u && curwedge->v1uv[1] == curwvert->v){ + curwedge->v1 = curwvert; + break; + } + } + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwedge->eed->v2 == curwvert->eve && curwedge->v2uv[0] == curwvert->u && curwedge->v2uv[1] == curwvert->v){ + curwedge->v2 = curwvert; + break; + } + } + } +} + +static void free_weldedUVs(ListBase *uvverts) +{ + wUV *curwvert; + for(curwvert = uvverts->first; curwvert; curwvert=curwvert->next) BLI_freelistN(&(curwvert->nodes)); + BLI_freelistN(uvverts); +} + +static void collapse_edgeuvs(void) +{ + ListBase uvedges, uvverts, allcollections; + wUVEdge *curwedge; + wUVNode *curwnode; + wUVEdgeCollect *collectedwuve, *newcollectedwuve; + Collection *wuvecollection, *newcollection; + int curtag, balanced, collectionfound= 0, vcount; + float avg[2]; + + if (!EM_texFaceCheck()) + return; + + uvverts.first = uvverts.last = uvedges.first = uvedges.last = allcollections.first = allcollections.last = NULL; + + build_weldedUVs(&uvverts); + build_weldedUVEdges(&uvedges, &uvverts); + + curtag = 0; + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + curwedge->v1->f = curtag; + curwedge->v2->f = curtag; + curtag +=1; + } + + balanced = 0; + while(!balanced){ + balanced = 1; + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + if(curwedge->v1->f != curwedge->v2->f){ + if(curwedge->v1->f > curwedge->v2->f) curwedge->v1->f = curwedge->v2->f; + else curwedge->v2->f = curwedge->v1->f; + balanced = 0; + } + } + } + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next) curwedge->f = curwedge->v1->f; + + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + if(allcollections.first){ + for(wuvecollection = allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ + if(wuvecollection->index == curwedge->f){ + newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); + newcollectedwuve->uved = curwedge; + BLI_addtail(&(wuvecollection->collectionbase), newcollectedwuve); + collectionfound = 1; + break; + } + + else collectionfound = 0; + } + } + if(allcollections.first == NULL || collectionfound == 0){ + newcollection = MEM_callocN(sizeof(Collection), "element collection"); + newcollection->index = curwedge->f; + newcollection->collectionbase.first = 0; + newcollection->collectionbase.last = 0; + + newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); + newcollectedwuve->uved = curwedge; + + BLI_addtail(&(newcollection->collectionbase), newcollectedwuve); + BLI_addtail(&allcollections, newcollection); + } + } + + for(wuvecollection=allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ + + vcount = avg[0] = avg[1] = 0; + + for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ + avg[0] += collectedwuve->uved->v1uv[0]; + avg[1] += collectedwuve->uved->v1uv[1]; + + avg[0] += collectedwuve->uved->v2uv[0]; + avg[1] += collectedwuve->uved->v2uv[1]; + + vcount +=2; + + } + + avg[0] /= vcount; avg[1] /= vcount; + + for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ + for(curwnode=collectedwuve->uved->v1->nodes.first; curwnode; curwnode=curwnode->next){ + *(curwnode->u) = avg[0]; + *(curwnode->v) = avg[1]; + } + for(curwnode=collectedwuve->uved->v2->nodes.first; curwnode; curwnode=curwnode->next){ + *(curwnode->u) = avg[0]; + *(curwnode->v) = avg[1]; + } + } + } + + free_weldedUVs(&uvverts); + BLI_freelistN(&uvedges); + freecollections(&allcollections); +} + +/*End UV Edge collapse code*/ + +static void collapseuvs(EditVert *mergevert) +{ + EditFace *efa; + MTFace *tf; + int uvcount; + float uvav[2]; + + if (!EM_texFaceCheck()) + return; + + uvcount = 0; + uvav[0] = 0; + uvav[1] = 0; + + for(efa = G.editMesh->faces.first; efa; efa=efa->next){ + tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + if(efa->v1->f1 && ELEM(mergevert, NULL, efa->v1)) { + uvav[0] += tf->uv[0][0]; + uvav[1] += tf->uv[0][1]; + uvcount += 1; + } + if(efa->v2->f1 && ELEM(mergevert, NULL, efa->v2)){ + uvav[0] += tf->uv[1][0]; + uvav[1] += tf->uv[1][1]; + uvcount += 1; + } + if(efa->v3->f1 && ELEM(mergevert, NULL, efa->v3)){ + uvav[0] += tf->uv[2][0]; + uvav[1] += tf->uv[2][1]; + uvcount += 1; + } + if(efa->v4 && efa->v4->f1 && ELEM(mergevert, NULL, efa->v4)){ + uvav[0] += tf->uv[3][0]; + uvav[1] += tf->uv[3][1]; + uvcount += 1; + } + } + + if(uvcount > 0) { + uvav[0] /= uvcount; + uvav[1] /= uvcount; + + for(efa = G.editMesh->faces.first; efa; efa=efa->next){ + tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + if(efa->v1->f1){ + tf->uv[0][0] = uvav[0]; + tf->uv[0][1] = uvav[1]; + } + if(efa->v2->f1){ + tf->uv[1][0] = uvav[0]; + tf->uv[1][1] = uvav[1]; + } + if(efa->v3->f1){ + tf->uv[2][0] = uvav[0]; + tf->uv[2][1] = uvav[1]; + } + if(efa->v4 && efa->v4->f1){ + tf->uv[3][0] = uvav[0]; + tf->uv[3][1] = uvav[1]; + } + } + } +} + +int collapseEdges(void) +{ + EditVert *eve; + EditEdge *eed; + + ListBase allcollections; + CollectedEdge *curredge; + Collection *edgecollection; + + int totedges, groupcount, mergecount,vcount; + float avgcount[3]; + + allcollections.first = 0; + allcollections.last = 0; + + mergecount = 0; + + if(multires_test()) return 0; + + build_edgecollection(&allcollections); + groupcount = BLI_countlist(&allcollections); + + + for(edgecollection = allcollections.first; edgecollection; edgecollection = edgecollection->next){ + totedges = BLI_countlist(&(edgecollection->collectionbase)); + mergecount += totedges; + avgcount[0] = 0; avgcount[1] = 0; avgcount[2] = 0; + + vcount = 0; + + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + avgcount[0] += ((EditEdge*)curredge->eed)->v1->co[0]; + avgcount[1] += ((EditEdge*)curredge->eed)->v1->co[1]; + avgcount[2] += ((EditEdge*)curredge->eed)->v1->co[2]; + + avgcount[0] += ((EditEdge*)curredge->eed)->v2->co[0]; + avgcount[1] += ((EditEdge*)curredge->eed)->v2->co[1]; + avgcount[2] += ((EditEdge*)curredge->eed)->v2->co[2]; + + vcount +=2; + } + + avgcount[0] /= vcount; avgcount[1] /=vcount; avgcount[2] /= vcount; + + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + VECCOPY(((EditEdge*)curredge->eed)->v1->co,avgcount); + VECCOPY(((EditEdge*)curredge->eed)->v2->co,avgcount); + } + + if (EM_texFaceCheck()) { + /*uv collapse*/ + for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0; + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + curredge->eed->v1->f1 = 1; + curredge->eed->v2->f1 = 1; + curredge->eed->f1 = 1; + } + collapse_edgeuvs(); + } + + } + freecollections(&allcollections); + removedoublesflag(1, 0, MERGELIMIT); + /*get rid of this!*/ + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + return mergecount; +} + +int merge_firstlast(int first, int uvmerge) +{ + EditVert *eve,*mergevert; + EditSelection *ese; + + if(multires_test()) return 0; + + /* do sanity check in mergemenu in edit.c ?*/ + if(first == 0){ + ese = G.editMesh->selected.last; + mergevert= (EditVert*)ese->data; + } + else{ + ese = G.editMesh->selected.first; + mergevert = (EditVert*)ese->data; + } + + if(mergevert->f&SELECT){ + for (eve=G.editMesh->verts.first; eve; eve=eve->next){ + if (eve->f&SELECT) + VECCOPY(eve->co,mergevert->co); + } + } + + if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){ + + for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f&SELECT) eve->f1 = 1; + } + collapseuvs(mergevert); + } + + countall(); + return removedoublesflag(1, 0, MERGELIMIT); +} + +int merge_target(int target, int uvmerge) +{ + EditVert *eve; + + if(multires_test()) return 0; + + if(target) snap_sel_to_curs(); + else snap_to_center(); + + if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){ + for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f&SELECT) eve->f1 = 1; + } + collapseuvs(NULL); + } + + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + return removedoublesflag(1, 0, MERGELIMIT); + +} +#undef MERGELIMIT + +typedef struct PathNode{ + int u; + int visited; + ListBase edges; +} PathNode; + +typedef struct PathEdge{ + struct PathEdge *next, *prev; + int v; + float w; +} PathEdge; + +void pathselect(void) +{ + EditVert *eve, *s, *t; + EditEdge *eed; + EditSelection *ese; + PathEdge *newpe, *currpe; + PathNode *currpn; + PathNode *Q; + int v, *previous, pathvert, pnindex; /*pnindex redundant?*/ + int unbalanced, totnodes; + short physical; + float *cost; + Heap *heap; /*binary heap for sorting pointers to PathNodes based upon a 'cost'*/ + + s = t = NULL; + + countall(); /*paranoid?*/ + + ese = ((EditSelection*)G.editMesh->selected.last); + if(ese && ese->type == EDITVERT && ese->prev && ese->prev->type == EDITVERT){ + physical= pupmenu("Distance Method? %t|Edge Length%x1|Topological%x0"); + + t = (EditVert*)ese->data; + s = (EditVert*)ese->prev->data; + + /*need to find out if t is actually reachable by s....*/ + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + eve->f1 = 0; + } + + s->f1 = 1; + + unbalanced = 1; + totnodes = 1; + while(unbalanced){ + unbalanced = 0; + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(!eed->h){ + if(eed->v1->f1 && !eed->v2->f1){ + eed->v2->f1 = 1; + totnodes++; + unbalanced = 1; + } + else if(eed->v2->f1 && !eed->v1->f1){ + eed->v1->f1 = 1; + totnodes++; + unbalanced = 1; + } + } + } + } + + + + if(s->f1 && t->f1){ /*t can be reached by s*/ + Q = MEM_callocN(sizeof(PathNode)*totnodes, "Path Select Nodes"); + totnodes = 0; + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f1){ + Q[totnodes].u = totnodes; + Q[totnodes].edges.first = 0; + Q[totnodes].edges.last = 0; + Q[totnodes].visited = 0; + eve->tmp.p = &(Q[totnodes]); + totnodes++; + } + else eve->tmp.p = NULL; + } + + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(!eed->h){ + if(eed->v1->f1){ + currpn = ((PathNode*)eed->v1->tmp.p); + + newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); + newpe->v = ((PathNode*)eed->v2->tmp.p)->u; + if(physical){ + newpe->w = VecLenf(eed->v1->co, eed->v2->co); + } + else newpe->w = 1; + newpe->next = 0; + newpe->prev = 0; + BLI_addtail(&(currpn->edges), newpe); + } + if(eed->v2->f1){ + currpn = ((PathNode*)eed->v2->tmp.p); + newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); + newpe->v = ((PathNode*)eed->v1->tmp.p)->u; + if(physical){ + newpe->w = VecLenf(eed->v1->co, eed->v2->co); + } + else newpe->w = 1; + newpe->next = 0; + newpe->prev = 0; + BLI_addtail(&(currpn->edges), newpe); + } + } + } + + heap = BLI_heap_new(); + cost = MEM_callocN(sizeof(float)*totnodes, "Path Select Costs"); + previous = MEM_callocN(sizeof(int)*totnodes, "PathNode indices"); + + for(v=0; v < totnodes; v++){ + cost[v] = 1000000; + previous[v] = -1; /*array of indices*/ + } + + pnindex = ((PathNode*)s->tmp.p)->u; + cost[pnindex] = 0; + BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(pnindex)); + + while( !BLI_heap_empty(heap) ){ + + pnindex = GET_INT_FROM_POINTER(BLI_heap_popmin(heap)); + currpn = &(Q[pnindex]); + + if(currpn == (PathNode*)t->tmp.p) /*target has been reached....*/ + break; + + for(currpe=currpn->edges.first; currpe; currpe=currpe->next){ + if(!Q[currpe->v].visited){ + if( cost[currpe->v] > (cost[currpn->u ] + currpe->w) ){ + cost[currpe->v] = cost[currpn->u] + currpe->w; + previous[currpe->v] = currpn->u; + Q[currpe->v].visited = 1; + BLI_heap_insert(heap, cost[currpe->v], SET_INT_IN_POINTER(currpe->v)); + } + } + } + } + + pathvert = ((PathNode*)t->tmp.p)->u; + while(pathvert != -1){ + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f1){ + if( ((PathNode*)eve->tmp.p)->u == pathvert) eve->f |= SELECT; + } + } + pathvert = previous[pathvert]; + } + + for(v=0; v < totnodes; v++) BLI_freelistN(&(Q[v].edges)); + MEM_freeN(Q); + MEM_freeN(cost); + MEM_freeN(previous); + BLI_heap_free(heap, NULL); + EM_select_flush(); + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + } + } + else{ + error("Path Selection requires that exactly two vertices be selected"); + return; + } +} + +void region_to_loop(void) +{ + EditEdge *eed; + EditFace *efa; + + if(G.totfacesel){ + for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0; + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->f&SELECT){ + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) + efa->e4->f1++; + } + } + + EM_clear_flag_all(SELECT); + + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(eed->f1 == 1) EM_select_edge(eed, 1); + } + + G.scene->selectmode = SCE_SELECT_EDGE; + EM_selectmode_set(); + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + BIF_undo_push("Face Region to Edge Loop"); + + } +} + +static int validate_loop(Collection *edgecollection) +{ + EditEdge *eed; + EditFace *efa; + CollectedEdge *curredge; + + /*1st test*/ + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + curredge->eed->v1->f1 = 0; + curredge->eed->v2->f1 = 0; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + curredge->eed->v1->f1++; + curredge->eed->v2->f1++; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + if(curredge->eed->v1->f1 > 2) return(0); else + if(curredge->eed->v2->f1 > 2) return(0); + } + + /*2nd test*/ + for(eed = G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + if(curredge->eed->f1 > 2) return(0); + } + return(1); +} + +static int loop_bisect(Collection *edgecollection){ + + EditFace *efa, *sf1, *sf2; + EditEdge *eed, *sed; + CollectedEdge *curredge; + int totsf1, totsf2, unbalanced,balancededges; + + for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = eed->f2 = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next) efa->f1 = 0; + + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next) curredge->eed->f1 = 1; + + sf1 = sf2 = NULL; + sed = ((CollectedEdge*)edgecollection->collectionbase.first)->eed; + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(sf2) break; + else if(sf1){ + if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf2 = efa; + } + else{ + if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf1 = efa; + } + } + + if(sf1==NULL || sf2==NULL) + return(-1); + + if(!(sf1->e1->f1)) sf1->e1->f2 = 1; + if(!(sf1->e2->f1)) sf1->e2->f2 = 1; + if(!(sf1->e3->f1)) sf1->e3->f2 = 1; + if(sf1->e4 && !(sf1->e4->f1)) sf1->e4->f2 = 1; + sf1->f1 = 1; + totsf1 = 1; + + if(!(sf2->e1->f1)) sf2->e1->f2 = 2; + if(!(sf2->e2->f1)) sf2->e2->f2 = 2; + if(!(sf2->e3->f1)) sf2->e3->f2 = 2; + if(sf2->e4 && !(sf2->e4->f1)) sf2->e4->f2 = 2; + sf2->f1 = 2; + totsf2 = 1; + + /*do sf1*/ + unbalanced = 1; + while(unbalanced){ + unbalanced = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + balancededges = 0; + if(efa->f1 == 0){ + if(efa->e1->f2 == 1 || efa->e2->f2 == 1 || efa->e3->f2 == 1 || ( (efa->e4) ? efa->e4->f2 == 1 : 0) ){ + balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 1; + balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 1; + balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 1; + if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 1; + if(balancededges){ + unbalanced = 1; + efa->f1 = 1; + totsf1++; + } + } + } + } + } + + /*do sf2*/ + unbalanced = 1; + while(unbalanced){ + unbalanced = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + balancededges = 0; + if(efa->f1 == 0){ + if(efa->e1->f2 == 2 || efa->e2->f2 == 2 || efa->e3->f2 == 2 || ( (efa->e4) ? efa->e4->f2 == 2 : 0) ){ + balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 2; + balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 2; + balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 2; + if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 2; + if(balancededges){ + unbalanced = 1; + efa->f1 = 2; + totsf2++; + } + } + } + } + } + + if(totsf1 < totsf2) return(1); + else return(2); +} + +void loop_to_region(void) +{ + EditFace *efa; + ListBase allcollections={NULL,NULL}; + Collection *edgecollection; + int testflag; + + build_edgecollection(&allcollections); + + for(edgecollection = (Collection *)allcollections.first; edgecollection; edgecollection=edgecollection->next){ + if(validate_loop(edgecollection)){ + testflag = loop_bisect(edgecollection); + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->f1 == testflag){ + if(efa->f&SELECT) EM_select_face(efa, 0); + else EM_select_face(efa,1); + } + } + } + } + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ /*fix this*/ + if(efa->f&SELECT) EM_select_face(efa,1); + } + + countall(); + freecollections(&allcollections); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + BIF_undo_push("Edge Loop to Face Region"); +} + + +/* texface and vertex color editmode tools for the face menu */ + +void mesh_rotate_uvs(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, ccw; + MTFace *tf; + float u1, v1; + + if (!EM_texFaceCheck()) { + error("mesh has no uv/image layers"); + return; + } + + ccw = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + u1= tf->uv[0][0]; + v1= tf->uv[0][1]; + + if (ccw) { + if(efa->v4) { + tf->uv[0][0]= tf->uv[3][0]; + tf->uv[0][1]= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[2][0]; + tf->uv[3][1]= tf->uv[2][1]; + } else { + tf->uv[0][0]= tf->uv[2][0]; + tf->uv[0][1]= tf->uv[2][1]; + } + + tf->uv[2][0]= tf->uv[1][0]; + tf->uv[2][1]= tf->uv[1][1]; + + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + } else { + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + + if(efa->v4) { + tf->uv[2][0]= tf->uv[3][0]; + tf->uv[2][1]= tf->uv[3][1]; + + tf->uv[3][0]= u1; + tf->uv[3][1]= v1; + } + else { + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Rotate UV face"); + } +} + +void mesh_mirror_uvs(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, altaxis; + MTFace *tf; + float u1, v1; + + if (!EM_texFaceCheck()) { + error("mesh has no uv/image layers"); + return; + } + + altaxis = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (altaxis) { + u1= tf->uv[1][0]; + v1= tf->uv[1][1]; + if(efa->v4) { + + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + + u1= tf->uv[3][0]; + v1= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[0][0]; + tf->uv[3][1]= tf->uv[0][1]; + + tf->uv[0][0]= u1; + tf->uv[0][1]= v1; + } + else { + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + + } else { + u1= tf->uv[0][0]; + v1= tf->uv[0][1]; + if(efa->v4) { + + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + + u1= tf->uv[3][0]; + v1= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[2][0]; + tf->uv[3][1]= tf->uv[2][1]; + + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + else { + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + } + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Mirror UV face"); + } +} + +void mesh_rotate_colors(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, ccw; + MCol tmpcol, *mcol; + if (!EM_vertColorCheck()) { + error("mesh has no color layers"); + return; + } + + ccw = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + tmpcol= mcol[0]; + + if (ccw) { + if(efa->v4) { + mcol[0]= mcol[3]; + mcol[3]= mcol[2]; + } else { + mcol[0]= mcol[2]; + } + mcol[2]= mcol[1]; + mcol[1]= tmpcol; + } else { + mcol[0]= mcol[1]; + mcol[1]= mcol[2]; + + if(efa->v4) { + mcol[2]= mcol[3]; + mcol[3]= tmpcol; + } + else + mcol[2]= tmpcol; + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Rotate Color face"); + } +} + +void mesh_mirror_colors(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, altaxis; + MCol tmpcol, *mcol; + if (!EM_vertColorCheck()) { + error("mesh has no color layers"); + return; + } + + altaxis = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + if (altaxis) { + tmpcol= mcol[1]; + mcol[1]= mcol[2]; + mcol[2]= tmpcol; + + if(efa->v4) { + tmpcol= mcol[0]; + mcol[0]= mcol[3]; + mcol[3]= tmpcol; + } + } else { + tmpcol= mcol[0]; + mcol[0]= mcol[1]; + mcol[1]= tmpcol; + + if(efa->v4) { + tmpcol= mcol[2]; + mcol[2]= mcol[3]; + mcol[3]= tmpcol; + } + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Mirror Color face"); + } +} diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c new file mode 100644 index 00000000000..29a5c329088 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -0,0 +1,318 @@ +/** + * bmesh_construct.c August 2008 + * + * BM construction functions. + * + * ***** 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. + * 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 LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * BM_CONSTRUCT.C + * + * This file contains functions for making and destroying + * individual elements like verts, edges and faces. + * +*/ + +/* + * BMESH MAKE VERT + * + * Creates a new vertex and returns a pointer + * to it. If a pointer to an example vertex is + * passed in, it's custom data and properties + * will be copied to the new vertex. + * +*/ + +BMVert *BM_Make_Vert(BMesh *bm, float co[3], BMVert *example) +{ + BMVert *v = NULL; + v = bmesh_mv(bm, co); + if(example) + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, example->data, &v->data); + return v; +} + +/* + * BMESH MAKE EDGE + * + * Creates a new edge betweeen two vertices and returns a + * pointer to it. If 'nodouble' equals 1, then a check is + * is done to make sure that an edge between those two vertices + * does not already exist. If it does, that edge is returned instead + * of creating a new one. + * + * If a new edge is created, and a pointer to an example edge is + * provided, it's custom data and properties will be copied to the + * new edge. + * +*/ + +BMEdge *BM_Make_Edge(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge *example, int nodouble) +{ + BMEdge *e = NULL; + + if(nodouble) /*test if edge already exists.*/ + e = bmesh_disk_existedge(v1, v2); + + if(!e){ + e = bmesh_me(bm, v1, v2); + + if(example) + CustomData_bmesh_copy_data(&bm->edata, &bm->edata, example->data, &e->data); + } + + return e; + +} + +/* + * BMESH MAKE QUADTRIANGLE + * + * Creates a new quad or triangle from + * a list of 3 or 4 vertices. If nodouble + * equals 1, then a check is done to see + * if a face with these vertices already + * exists and returns it instead. If a pointer + * to an example face is provided, it's custom + * data and properties will be copied to the new + * face. + * + * Note that the winding of the face is determined + * by the order of the vertices in the vertex array + * +*/ + +/*remove the edge array bits from this. Its not really needed?*/ +BMFace *BM_Make_Quadtriangle(BMesh *bm, BMVert **verts, BMEdge **edges, int len, BMFace *example, int nodouble) +{ + BMEdge *edar[4]; + BMFace *f = NULL; + int overlap = 0; + + edar[0] = edar[1] = edar[2] = edar[3] = NULL; + + if(edges){ + edar[0] = edges[0]; + edar[1] = edges[1]; + edar[2] = edges[2]; + if(len == 4) edar[3] = edges[3]; + }else{ + edar[0] = bmesh_disk_existedge(verts[0],verts[1]); + edar[1] = bmesh_disk_existedge(verts[1],verts[2]); + if(len == 4){ + edar[2] = bmesh_disk_existedge(verts[2],verts[3]); + edar[3] = bmesh_disk_existedge(verts[3],verts[0]); + + }else{ + edar[2] = bmesh_disk_existedge(verts[2],verts[0]); + } + } + + if(nodouble){ + /*check if face exists or overlaps*/ + if(len == 4){ + overlap = BM_Exist_Face_Overlaps(bm, verts, len, &f); + }else{ + overlap = BM_Exist_Face_Overlaps(bm, verts, len, &f); + } + } + + /*make new face*/ + if((!f) && (!overlap)){ + if(!edar[0]) edar[0] = bmesh_me(bm, verts[0], verts[1]); + if(!edar[1]) edar[1] = bmesh_me(bm, verts[1], verts[2]); + if(len == 4){ + if(!edar[2]) edar[2] = bmesh_me(bm, verts[2], verts[3]); + if(!edar[3]) edar[3] = bmesh_me(bm, verts[3], verts[0]); + } else { + if(!edar[2]) edar[2] = bmesh_me(bm, verts[2], verts[0]); + } + + if(len == 4) f = bmesh_mf(bm, verts[0], verts[1], edar, 4); + else f = bmesh_mf(bm, verts[0], verts[1], edar, 3); + + if(example) + CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, example->data, &f->data); + + } + + return f; +} + +/* + * BMESH MAKE NGON + * + * Attempts to make a new Ngon from a list of edges. + * If nodouble equals one, a check for overlaps or existing + * + * + * +*/ +#define VERT_BUF_SIZE 100 +BMFace *BM_Make_Ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble) +{ + BMVert *vert_buf[VERT_BUF_SIZE]; + BMVert **verts = vert_buf; + BMFace *f = NULL; + int overlap = 0, i; + + if(nodouble){ + if(len > VERT_BUF_SIZE) + verts = MEM_callocN(sizeof(BMVert *) * len, "bmesh make ngon vertex array"); + for(i = 0; i < len; i++){ + if(!BMO_TestFlag(bm, edges[i]->v1, BM_EDGEVERT)){ + BMO_SetFlag(bm, edges[i]->v1, BM_EDGEVERT); + verts[i] = edges[i]->v1; + } else if(!BMO_TestFlag(bm, edges[i]->v2, BM_EDGEVERT)) { + BMO_SetFlag(bm, edges[i]->v2, BM_EDGEVERT); + verts[i] = edges[i]->v2; + } + } + + overlap = BM_Exist_Face_Overlaps(bm, verts, len, &f); + + /*clear flags*/ + for(i = 0; i < len; i++){ + BMO_ClearFlag(bm, edges[i]->v1, BM_EDGEVERT); + BMO_ClearFlag(bm, edges[i]->v2, BM_EDGEVERT); + } + + if(len > VERT_BUF_SIZE) + MEM_freeN(verts); + } + + if((!f) && (!overlap)) + f = bmesh_mf(bm, v1, v2, edges, len); + + return f; +} + + +/*bmesh_make_face_from_face(BMesh *bm, BMFace *source, BMFace *target) */ + + +/* + * REMOVE TAGGED XXX + * + * Called by operators to remove elements that they have marked for + * removal. + * +*/ + +void BM_remove_tagged_faces(BMesh *bm, int flag) +{ + BMHeader *current, *next; + + current = bm->polys.first; + while(current){ + next = current->next; + if(BMO_TestFlag(bm, current, flag)) bmesh_kf(bm, (BMFace*)current); + current = next; + } +} +void BM_remove_tagged_edges(BMesh *bm, int flag) +{ + BMHeader *current, *next; + + current = bm->edges.first; + + while(current){ + next = current->next; + if(BMO_TestFlag(bm, current, flag)) bmesh_ke(bm, (BMEdge*)current); + current = next; + } +} + +void BM_remove_tagged_verts(BMesh *bm, int flag) +{ + BMHeader *current, *next; + + current = bm->verts.first; + + while(current){ + next = current->next; + if(BMO_TestFlag(bm, current, flag)) bmesh_kv(bm,(BMVert*)current); + current = next; + } +} + +static void bm_copy_vert_attributes(BMesh *source_mesh, BMesh *target_mesh, BMVert *source_vertex, BMVert *target_vertex) +{ + CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data); + target_vertex->bweight = source_vertex->bweight; +} + +static void bm_copy_edge_attributes(BMesh *source_mesh, BMesh *target_mesh, BMEdge *source_edge, BMEdge *target_edge) +{ + CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data); + target_edge->crease = source_edge->crease; + target_edge->bweight = source_edge->bweight; +} + +static void bm_copy_loop_attributes(BMesh *source_mesh, BMesh *target_mesh, BMLoop *source_loop, BMLoop *target_loop) +{ + CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data); +} + +static void bm_copy_face_attributes(BMesh *source_mesh, BMesh *target_mesh, BMFace *source_face, BMFace *target_face) +{ + CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data); + target_face->mat_nr = source_face->mat_nr; +} + +/*Todo: Special handling for hide flags?*/ + +void BM_Copy_Attributes(BMesh *source_mesh, BMesh *target_mesh, void *source, void *target) +{ + BMHeader *sheader = source, *theader = target; + + if(sheader->type != theader->type) + return; + + /*First we copy select*/ + if(BM_Is_Selected(source_mesh, source)) BM_Select(target_mesh, target, 1); + + /*Now we copy flags*/ + theader->flag = sheader->flag; + + /*Copy specific attributes*/ + if(theader->type == BM_VERT) + bm_copy_vert_attributes(source_mesh, target_mesh, (BMVert*)source, (BMVert*)target); + else if(theader->type == BM_EDGE) + bm_copy_edge_attributes(source_mesh, target_mesh, (BMEdge*)source, (BMEdge*)target); + else if(theader->type == BM_LOOP) + bm_copy_loop_attributes(source_mesh, target_mesh, (BMLoop*)source, (BMLoop*)target); + else if(theader->type == BM_FACE) + bm_copy_face_attributes(source_mesh, target_mesh, (BMFace*)source, (BMFace*)target); +}
\ No newline at end of file diff --git a/source/blender/bmesh/intern/bmesh_eulers.c b/source/blender/bmesh/intern/bmesh_eulers.c new file mode 100644 index 00000000000..c7f42ae390c --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_eulers.c @@ -0,0 +1,987 @@ +/** + * bmesh_eulers.c jan 2007 + * + * BM Euler construction API. + * + * $Id: bmesh_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** 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. + * 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 LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_customdata.h" +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "BLI_blenlib.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 BM 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 bmesh_loop_reverse, which is its own inverse). + + bmesh_MF/KF: Make Face and Kill Face + bmesh_ME/KE: Make Edge and Kill Edge + bmesh_MV/KV: Make Vert and Kill Vert + bmesh_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert + bmesh_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge + bmesh_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: + -Make seperate 'debug levels' of validation + -Add in the UnglueFaceRegionMakeVert and GlueFaceRegionKillVert eulers. + + NOTE: + -The functions in this file are notoriously difficult to debug and even understand sometimes. + better code comments would be nice.... + +*/ + + +/*MAKE Eulers*/ + +/** + * bmesh_MV + * + * MAKE VERT EULER: + * + * Makes a single loose vertex. + * + * Returns - + * A BMVert pointer. + */ + +BMVert *bmesh_mv(BMesh *bm, float *vec){ + BMVert *v = bmesh_addvertlist(bm, NULL); + VECCOPY(v->co,vec); + return v; +} + +/** + * bmesh_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 BMEdge pointer. + */ + +BMEdge *bmesh_me(BMesh *bm, BMVert *v1, BMVert *v2){ + BMEdge *e=NULL; + BMNode *d1=NULL, *d2=NULL; + int valance1=0, valance2=0, edok; + + /*edge must be between two distinct vertices...*/ + if(v1 == v2) return NULL; + + #ifndef bmesh_FASTEULER + /*count valance of v1*/ + if(v1->edge){ + d1 = bmesh_disk_getpointer(v1->edge,v1); + if(d1) valance1 = bmesh_cycle_length(d1); + else bmesh_error(); + } + if(v2->edge){ + d2 = bmesh_disk_getpointer(v2->edge,v2); + if(d2) valance2 = bmesh_cycle_length(d2); + else bmesh_error(); + } + #endif + + /*go ahead and add*/ + e = bmesh_addedgelist(bm, v1, v2, NULL); + bmesh_disk_append_edge(e, e->v1); + bmesh_disk_append_edge(e, e->v2); + + #ifndef bmesh_FASTEULER + /*verify disk cycle lengths*/ + d1 = bmesh_disk_getpointer(e, e->v1); + edok = bmesh_cycle_validate(valance1+1, d1); + if(!edok) bmesh_error(); + d2 = bmesh_disk_getpointer(e, e->v2); + edok = bmesh_cycle_validate(valance2+1, d2); + if(!edok) bmesh_error(); + + /*verify that edge actually made it into the cycle*/ + edok = bmesh_disk_hasedge(v1, e); + if(!edok) bmesh_error(); + edok = bmesh_disk_hasedge(v2, e); + if(!edok) bmesh_error(); + #endif + return e; +} + + + +/** + * bmesh_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 BMFace pointer + */ + +#define MF_CANDIDATE 1 +#define MF_VISITED 2 +#define MF_TAKEN 4 + +BMFace *bmesh_mf(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **elist, int len) +{ + BMFace *f = NULL; + BMEdge *curedge; + BMVert *curvert, *tv, **vlist; + int i, j, done, cont, edok; + + if(len < 2) return NULL; + + /*make sure that v1 and v2 are in elist[0]*/ + if(bmesh_verts_in_edge(v1,v2,elist[0]) == 0) return NULL; + + /*clear euler flags*/ + for(i=0;i<len;i++) elist[i]->head.eflag1=elist[i]->head.eflag2 = 0; + for(i=0;i<len;i++){ + elist[i]->head.eflag1 |= MF_CANDIDATE; + + /*if elist[i] has a loop, count its radial length*/ + if(elist[i]->loop) elist[i]->head.eflag2 = bmesh_cycle_length(&(elist[i]->loop->radial)); + else elist[i]->head.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; i<len; i++){ + edok = bmesh_disk_count_edgeflag(elist[i]->v1, MF_CANDIDATE, 0); + if(edok != 2) return NULL; + edok = bmesh_disk_count_edgeflag(elist[i]->v2, MF_CANDIDATE, 0); + if(edok != 2) return NULL; + } + + /*set start edge, start vert and target vert for our loop traversal*/ + curedge = elist[0]; + tv = v1; + curvert = v2; + + if(bm->vtarlen < len){ + MEM_freeN(bm->vtar); + bm->vtar = MEM_callocN(sizeof(BMVert *)* len, "BM Vert pointer array"); + bm->vtarlen = len; + } + /*insert tv into vlist since its the first vertex in face*/ + i=0; + vlist=bm->vtar; + 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->head.eflag1 |= MF_VISITED; + + /*find next edge and vert*/ + curedge = bmesh_disk_next_edgeflag(curedge, curvert, MF_CANDIDATE, 0); + curvert = bmesh_edge_getothervert(curedge, curvert); + if(curvert == tv){ + curedge->head.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; i<len; i++){ + if((elist[i]->head.eflag1 & MF_VISITED) == 0) cont = 0; + } + + /*if we get this far, its ok to allocate the face and add the loops*/ + if(cont){ + BMLoop *l; + BMEdge *e; + f = bmesh_addpolylist(bm, NULL); + f->len = len; + for(i=0;i<len;i++){ + curvert = vlist[i]; + l = bmesh_create_loop(bm,curvert,NULL,f,NULL); + if(!(f->loopbase)) f->loopbase = l; + bmesh_cycle_append(f->loopbase, l); + } + + /*take care of edge pointers and radial cycle*/ + for(i=0, l = f->loopbase; i<len; i++, l=((BMLoop*)(l->head.next))){ + e = NULL; + if(l == f->loopbase) e = elist[0]; /*first edge*/ + + else{/*search elist for others*/ + for(j=1; j<len; j++){ + edok = bmesh_verts_in_edge(l->v, ((BMLoop*)(l->head.next))->v, elist[j]); + if(edok){ + e = elist[j]; + break; + } + } + } + l->e = e; /*set pointer*/ + bmesh_radial_append(e, l); /*append into radial*/ + } + + f->len = len; + + /*Validation Loop cycle*/ + edok = bmesh_cycle_validate(len, f->loopbase); + if(!edok) bmesh_error(); + for(i=0, l = f->loopbase; i<len; i++, l=((BMLoop*)(l->head.next))){ + /*validate loop vert pointers*/ + edok = bmesh_verts_in_edge(l->v, ((BMLoop*)(l->head.next))->v, l->e); + if(!edok) bmesh_error(); + /*validate the radial cycle of each edge*/ + edok = bmesh_cycle_length(&(l->radial)); + if(edok != (l->e->head.eflag2 + 1)) bmesh_error(); + } + } + return f; +} + +/* KILL Eulers */ + +/** + * bmesh_KV + * + * KILL VERT EULER: + * + * Kills a single loose vertex. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_kv(BMesh *bm, BMVert *v){ + if(v->edge == NULL){ + BLI_remlink(&(bm->verts), &(v->head)); + bmesh_free_vert(bm,v); + return 1; + } + return 0; +} + +/** + * bmesh_KE + * + * KILL EDGE EULER: + * + * Kills a wire edge. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_ke(BMesh *bm, BMEdge *e){ + int edok; + + /*Make sure that no faces!*/ + if(e->loop == NULL){ + bmesh_disk_remove_edge(e, e->v1); + bmesh_disk_remove_edge(e, e->v2); + + /*verify that edge out of disk*/ + edok = bmesh_disk_hasedge(e->v1, e); + if(edok) bmesh_error(); + edok = bmesh_disk_hasedge(e->v2, e); + if(edok) bmesh_error(); + + /*remove and deallocate*/ + BLI_remlink(&(bm->edges), &(e->head)); + bmesh_free_edge(bm, e); + return 1; + } + return 0; +} + +/** + * bmesh_KF + * + * KILL FACE EULER: + * + * The logical inverse of bmesh_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 bmesh_kf(BMesh *bm, BMFace *bply){ + BMLoop *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 = bmesh_cycle_length(bply->loopbase); + for(i=0, curloop=bply->loopbase; i < len; i++, curloop = ((BMLoop*)(curloop->head.next))) + bmesh_radial_remove_loop(curloop, curloop->e); + + /*now deallocate the editloops*/ + for(i=0; i < len; i++){ + newbase = ((BMLoop*)(bply->loopbase->head.next)); + oldbase = bply->loopbase; + bmesh_cycle_remove(oldbase, oldbase); + bmesh_free_loop(bm, oldbase); + bply->loopbase = newbase; + } + + BLI_remlink(&(bm->polys), &(bply->head)); + bmesh_free_poly(bm, bply); + return 1; +} + +/*SPLIT Eulers*/ + +/** + * bmesh_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 - + * BMVert pointer. + * +*/ + +BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re){ + BMVert *nv, *ov; + BMNode *diskbase; + BMEdge *ne; + int i, edok, valance1=0, valance2=0; + + if(bmesh_vert_in_edge(e,tv) == 0) return NULL; + ov = bmesh_edge_getothervert(e,tv); + //v2 = tv; + + /*count valance of v1*/ + diskbase = bmesh_disk_getpointer(e, ov); + valance1 = bmesh_cycle_length(diskbase); + /*count valance of v2*/ + diskbase = bmesh_disk_getpointer(e, tv); + valance2 = bmesh_cycle_length(diskbase); + + nv = bmesh_addvertlist(bm, tv); + ne = bmesh_addedgelist(bm, nv, tv, e); + + //e->v2 = nv; + /*remove e from v2's disk cycle*/ + bmesh_disk_remove_edge(e, tv); + /*swap out tv for nv in e*/ + bmesh_edge_swapverts(e, tv, nv); + /*add e to nv's disk cycle*/ + bmesh_disk_append_edge(e, nv); + /*add ne to nv's disk cycle*/ + bmesh_disk_append_edge(ne, nv); + /*add ne to tv's disk cycle*/ + bmesh_disk_append_edge(ne, tv); + /*verify disk cycles*/ + diskbase = bmesh_disk_getpointer(ov->edge,ov); + edok = bmesh_cycle_validate(valance1, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(tv->edge,tv); + edok = bmesh_cycle_validate(valance2, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(nv->edge,nv); + edok = bmesh_cycle_validate(2, diskbase); + if(!edok) bmesh_error(); + + /*Split the radial cycle if present*/ + if(e->loop){ + BMLoop *nl,*l; + BMNode *radEBase=NULL, *radNEBase=NULL; + int radlen = bmesh_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++; + bmesh_radial_remove_loop(l,e); + + nl = bmesh_create_loop(bm,NULL,NULL,l->f,l); + nl->head.prev = (BMHeader*)l; + nl->head.next = (BMHeader*)(l->head.next); + nl->head.prev->next = (BMHeader*)nl; + nl->head.next->prev = (BMHeader*)nl; + nl->v = nv; + + /*assign the correct edge to the correct loop*/ + if(bmesh_verts_in_edge(nl->v, ((BMLoop*)(nl->head.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; + } + + bmesh_cycle_append(radEBase,&(nl->radial)); + bmesh_cycle_append(radNEBase,&(l->radial)); + + } + else if(bmesh_verts_in_edge(nl->v,((BMLoop*)(nl->head.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; + } + bmesh_cycle_append(radEBase,&(l->radial)); + bmesh_cycle_append(radNEBase,&(nl->radial)); + } + + } + + e->loop = radEBase->data; + ne->loop = radNEBase->data; + + /*verify length of radial cycle*/ + edok = bmesh_cycle_validate(radlen,&(e->loop->radial)); + if(!edok) bmesh_error(); + edok = bmesh_cycle_validate(radlen,&(ne->loop->radial)); + if(!edok) bmesh_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)) bmesh_error(); + if(!(l->radial.data == l)) bmesh_error(); + if( ((BMLoop*)(l->head.prev))->e != ne && ((BMLoop*)(l->head.next))->e != ne) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, ((BMLoop*)(l->head.next))->v, e); + if(!edok) bmesh_error(); + if(l->v == ((BMLoop*)(l->head.next))->v) bmesh_error(); + if(l->e == ((BMLoop*)(l->head.next))->e) bmesh_error(); + /*verify loop cycle for kloop->f*/ + edok = bmesh_cycle_validate(l->f->len, l->f->loopbase); + if(!edok) bmesh_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)) bmesh_error(); + if(!(l->radial.data == l)) bmesh_error(); + if( ((BMLoop*)(l->head.prev))->e != e && ((BMLoop*)(l->head.next))->e != e) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, ((BMLoop*)(l->head.next))->v, ne); + if(!edok) bmesh_error(); + if(l->v == ((BMLoop*)(l->head.next))->v) bmesh_error(); + if(l->e == ((BMLoop*)(l->head.next))->e) bmesh_error(); + /*verify loop cycle for kloop->f. Redundant*/ + edok = bmesh_cycle_validate(l->f->len, l->f->loopbase); + if(!edok) bmesh_error(); + } + } + + if(re) *re = ne; + return nv; +} + +/** + * bmesh_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. + * + * Note that the tesselator abuses eflag2 while using this euler! (don't ever ever do this....) + * + * Returns - + * A BMFace pointer + */ +BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **rl){ + + BMFace *f2; + BMLoop *v1loop = NULL, *v2loop = NULL, *curloop, *f1loop=NULL, *f2loop=NULL; + BMEdge *e; + int i, len, f1len, f2len; + + + /*verify that v1 and v2 are in face.*/ + len = bmesh_cycle_length(f->loopbase); + for(i = 0, curloop = f->loopbase; i < len; i++, curloop = ((BMLoop*)(curloop->head.next)) ){ + if(curloop->v == v1) v1loop = curloop; + else if(curloop->v == v2) v2loop = curloop; + } + + if(!v1loop || !v2loop) return NULL; + + /*allocate new edge between v1 and v2*/ + e = bmesh_addedgelist(bm, v1, v2,NULL); + bmesh_disk_append_edge(e, v1); + bmesh_disk_append_edge(e, v2); + + f2 = bmesh_addpolylist(bm,f); + f1loop = bmesh_create_loop(bm,v2,e,f,v2loop); + f2loop = bmesh_create_loop(bm,v1,e,f2,v1loop); + + f1loop->head.prev = v2loop->head.prev; + f2loop->head.prev = v1loop->head.prev; + v2loop->head.prev->next = (BMHeader*)f1loop; + v1loop->head.prev->next = (BMHeader*)f2loop; + + f1loop->head.next = (BMHeader*)v1loop; + f2loop->head.next = (BMHeader*)v2loop; + v1loop->head.prev = (BMHeader*)f1loop; + v2loop->head.prev = (BMHeader*)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 = bmesh_cycle_length(f2->loopbase); + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((BMLoop*)(curloop->head.next)) ) curloop->f = f2; + + /*link up the new loops into the new edges radial*/ + bmesh_radial_append(e, f1loop); + bmesh_radial_append(e, f2loop); + + + f2->len = f2len; + + f1len = bmesh_cycle_length(f->loopbase); + f->len = f1len; + + if(rl) *rl = f2loop; + return f2; +} + + +/** + * bmesh_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 bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv) +{ + BMEdge *oe; + BMVert *ov, *tv; + BMNode *diskbase; + BMLoop *killoop,*nextl; + int len,radlen=0, halt = 0, i, valance1, valance2,edok; + + if(bmesh_vert_in_edge(ke,kv) == 0) return 0; + diskbase = bmesh_disk_getpointer(kv->edge, kv); + len = bmesh_cycle_length(diskbase); + + if(len == 2){ + oe = bmesh_disk_nextedge(ke, kv); + tv = bmesh_edge_getothervert(ke, kv); + ov = bmesh_edge_getothervert(oe, kv); + halt = bmesh_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 = bmesh_disk_getpointer(ov->edge, ov); + valance1 = bmesh_cycle_length(diskbase); + diskbase = bmesh_disk_getpointer(tv->edge, tv); + valance2 = bmesh_cycle_length(diskbase); + + /*remove oe from kv's disk cycle*/ + bmesh_disk_remove_edge(oe,kv); + /*relink oe->kv to be oe->tv*/ + bmesh_edge_swapverts(oe, kv, tv); + /*append oe to tv's disk cycle*/ + bmesh_disk_append_edge(oe, tv); + /*remove ke from tv's disk cycle*/ + bmesh_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 = bmesh_cycle_length(&(ke->loop->radial)); + for(i=0,killoop = ke->loop; i<radlen; i++, killoop = bmesh_radial_nextloop(killoop)){ + /*relink loops and fix vertex pointer*/ + killoop->head.next->prev = killoop->head.prev; + killoop->head.prev->next = killoop->head.next; + if( ((BMLoop*)(killoop->head.next))->v == kv) ((BMLoop*)(killoop->head.next))->v = tv; + + /*fix len attribute of face*/ + killoop->f->len--; + if(killoop->f->loopbase == killoop) killoop->f->loopbase = ((BMLoop*)(killoop->head.next)); + } + /*second step, remove all the hanging loops attached to ke*/ + killoop = ke->loop; + radlen = bmesh_cycle_length(&(ke->loop->radial)); + /*make sure we have enough room in bm->lpar*/ + if(bm->lparlen < radlen){ + MEM_freeN(bm->lpar); + bm->lpar = MEM_callocN(sizeof(BMLoop *)* radlen, "BM Loop pointer array"); + bm->lparlen = bm->lparlen * radlen; + } + /*this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well...*/ + i=0; + while(i<radlen){ + bm->lpar[i] = killoop; + killoop = killoop->radial.next->data; + i++; + } + i=0; + while(i<radlen){ + bmesh_free_loop(bm,bm->lpar[i]); + i++; + } + /*Validate radial cycle of oe*/ + edok = bmesh_cycle_validate(radlen,&(oe->loop->radial)); + + } + + + /*Validate disk cycles*/ + diskbase = bmesh_disk_getpointer(ov->edge,ov); + edok = bmesh_cycle_validate(valance1, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(tv->edge,tv); + edok = bmesh_cycle_validate(valance2, diskbase); + if(!edok) bmesh_error(); + + /*Validate loop cycle of all faces attached to oe*/ + for(i=0,nextl = oe->loop; i<radlen; i++, nextl = bmesh_radial_nextloop(nextl)){ + edok = bmesh_cycle_validate(nextl->f->len,nextl->f->loopbase); + if(!edok) bmesh_error(); + } + /*deallocate edge*/ + BLI_remlink(&(bm->edges), &(ke->head)); + bmesh_free_edge(bm, ke); + /*deallocate vertex*/ + BLI_remlink(&(bm->verts), &(kv->head)); + bmesh_free_vert(bm, kv); + return 1; + } + } + return 0; +} + + +/** + * bmesh_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 bmesh_loop_reverse(BMesh *bm, BMFace *f){ + BMLoop *l = f->loopbase, *curloop, *oldprev, *oldnext; + int i, j, edok, len = 0; + + len = bmesh_cycle_length(l); + if(bm->edarlen < len){ + MEM_freeN(bm->edar); + bm->edar = MEM_callocN(sizeof(BMEdge *)* len, "BM Edge pointer array"); + bm->edarlen = len; + } + + for(i=0, curloop = l; i< len; i++, curloop= ((BMLoop*)(curloop->head.next)) ){ + curloop->e->head.eflag1 = 0; + curloop->e->head.eflag2 = bmesh_cycle_length(&curloop->radial); + bmesh_radial_remove_loop(curloop, curloop->e); + /*in case of border edges we HAVE to zero out curloop->radial Next/Prev*/ + curloop->radial.next = curloop->radial.prev = NULL; + bm->edar[i] = curloop->e; + } + + /*actually reverse the loop. This belongs in bmesh_cycle_reverse!*/ + for(i=0, curloop = l; i < len; i++){ + oldnext = ((BMLoop*)(curloop->head.next)); + oldprev = ((BMLoop*)(curloop->head.prev)); + curloop->head.next = (BMHeader*)oldprev; + curloop->head.prev = (BMHeader*)oldnext; + curloop = oldnext; + } + + if(len == 2){ //two edged face + //do some verification here! + l->e = bm->edar[1]; + ((BMLoop*)(l->head.next))->e = bm->edar[0]; + } + else{ + for(i=0, curloop = l; i < len; i++, curloop = ((BMLoop*)(curloop->head.next)) ){ + edok = 0; + for(j=0; j < len; j++){ + edok = bmesh_verts_in_edge(curloop->v, ((BMLoop*)(curloop->head.next))->v, bm->edar[j]); + if(edok){ + curloop->e = bm->edar[j]; + break; + } + } + } + } + /*rebuild radial*/ + for(i=0, curloop = l; i < len; i++, curloop = ((BMLoop*)(curloop->head.next)) ) bmesh_radial_append(curloop->e, curloop); + + /*validate radial*/ + for(i=0, curloop = l; i < len; i++, curloop = ((BMLoop*)(curloop->head.next)) ){ + edok = bmesh_cycle_validate(curloop->e->head.eflag2, &(curloop->radial)); + if(!edok){ + bmesh_error(); + } + } + return 1; +} + +/** + * bmesh_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 bmesh_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 BMFace pointer +*/ + +BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) +{ + + BMLoop *curloop, *f1loop=NULL, *f2loop=NULL; + int loopok = 0, newlen = 0,i, f1len=0, f2len=0, radlen=0, edok, shared; + + /*need to reinsert logic for checking to make sure that faces only share one edge!*/ + if(f1 == f2) return NULL; //can't join a face to itself + /*verify that e is in both f1 and f2*/ + f1len = bmesh_cycle_length(f1->loopbase); + f2len = bmesh_cycle_length(f2->loopbase); + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((BMLoop*)(curloop->head.next)) ){ + if(curloop->e == e){ + f1loop = curloop; + break; + } + } + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((BMLoop*)(curloop->head.next)) ){ + if(curloop->e==e){ + f2loop = curloop; + break; + } + } + if(!(f1loop && f2loop)) return NULL; + + /*validate that edge is 2-manifold edge*/ + radlen = bmesh_cycle_length(&(f1loop->radial)); + if(radlen != 2) return NULL; + + /*validate direction of f2's loop cycle is compatible.*/ + if(f1loop->v == f2loop->v) return NULL; + + /* + Finally validate that for each face, each vertex has another edge in its disk cycle that is + not e, and not shared. + */ + if(bmesh_radial_find_face( ((BMLoop*)(f1loop->head.next))->e,f2)) return NULL; + if(bmesh_radial_find_face( ((BMLoop*)(f1loop->head.prev))->e,f2)) return NULL; + if(bmesh_radial_find_face( ((BMLoop*)(f2loop->head.next))->e,f1)) return NULL; + if(bmesh_radial_find_face( ((BMLoop*)(f2loop->head.prev))->e,f1)) return NULL; + + /*validate only one shared edge*/ + shared = BM_Face_Sharededges(f1,f2); + if(shared > 1) return NULL; + + + /*join the two loops*/ + f1loop->head.prev->next = f2loop->head.next; + f2loop->head.next->prev = f1loop->head.prev; + + f1loop->head.next->prev = f2loop->head.prev; + f2loop->head.prev->next = f1loop->head.next; + + /*if f1loop was baseloop, give f1loop->next the base.*/ + if(f1->loopbase == f1loop) f1->loopbase = ((BMLoop*)(f1loop->head.next)); + + /*validate the new loop*/ + loopok = bmesh_cycle_validate((f1len+f2len)-2, f1->loopbase); + if(!loopok) bmesh_error(); + + /*make sure each loop points to the proper face*/ + newlen = bmesh_cycle_length(f1->loopbase); + for(i = 0, curloop = f1->loopbase; i < newlen; i++, curloop = ((BMLoop*)(curloop->head.next)) ) curloop->f = f1; + + f1->len = newlen; + + edok = bmesh_cycle_validate(f1->len, f1->loopbase); + if(!edok) bmesh_error(); + + /*remove edge from the disk cycle of its two vertices.*/ + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1); + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2); + + /*deallocate edge and its two loops as well as f2*/ + BLI_remlink(&(bm->edges), &(f1loop->e->head)); + BLI_remlink(&(bm->polys), &(f2->head)); + bmesh_free_edge(bm, f1loop->e); + bmesh_free_loop(bm, f1loop); + bmesh_free_loop(bm, f2loop); + bmesh_free_poly(bm, f2); + return f1; +} diff --git a/source/blender/bmesh/intern/bmesh_filters.c b/source/blender/bmesh/intern/bmesh_filters.c new file mode 100644 index 00000000000..c31139a8f93 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_filters.c @@ -0,0 +1,5 @@ +#include <stdio.h> +#include <string.h> + +#include "bmesh.h" +#include "bmesh_private.h" diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c new file mode 100644 index 00000000000..9e9d6e0c355 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -0,0 +1,111 @@ +/** + * BME_interp.c August 2008 + * + * BM interpolation functions. + * + * ***** 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. + * 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 LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * BME_INTERP.C + * + * Functions for interpolating data across the surface of a mesh. + * +*/ + +/** + * bmesh_data_interp_from_verts + * + * Interpolates per-vertex data from two sources to a target. + * + * Returns - + * Nothing + */ +void BM_Data_Interp_From_Verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, float fac) +{ + void *src[2]; + float w[2]; + if (v1->data && v2->data) { + src[0]= v1->data; + src[1]= v2->data; + w[0] = 1.0f-fac; + w[1] = fac; + CustomData_bmesh_interp(&bm->vdata, src, w, NULL, 2, v->data); + } +} + +/** + * bmesh_data_facevert_edgeinterp + * + * Walks around the faces of an edge and interpolates the per-face-edge + * data between two sources to a target. + * + * Returns - + * Nothing +*/ + +void BM_Data_Facevert_Edgeinterp(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, BMEdge *e1, float fac){ + void *src[2]; + float w[2]; + BMLoop *l=NULL, *v1loop = NULL, *vloop = NULL, *v2loop = NULL; + + w[0] = 1.0f - fac; + w[1] = fac; + + if(!e1->loop) return; + l = e1->loop; + do{ + if(l->v == v1){ + v1loop = l; + vloop = (BMLoop*)(v1loop->head.next); + v2loop = (BMLoop*)(vloop->head.next); + }else if(l->v == v){ + v1loop = (BMLoop*)(l->head.next); + vloop = l; + v2loop = (BMLoop*)(l->head.prev); + + } + + src[0] = v1loop->data; + src[1] = v2loop->data; + + CustomData_bmesh_interp(&bm->ldata, src,w, NULL, 2, vloop->data); + l = l->radial.next->data; + }while(l!=e1->loop); +} + +//static void bmesh_data_interp_from_face(BME_Mesh *bm, BMFace *source, BMFace *target) +//{ +// +//} +/*insert BM_data_interp_from_face here for mean value coordinates...*/ diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c new file mode 100644 index 00000000000..e64e4c9ebc9 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_iterators.c @@ -0,0 +1,342 @@ +#include <string.h> + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * BMESH ITERATOR STEP + * + * Calls an iterators step fucntion to return + * the next element. +*/ + +void *BMIter_Step(BMIter *iter) +{ + return iter->step(iter); +} + +/* + * INIT ITERATOR + * + * Clears the internal state of an iterator + * For begin() callbacks. + * +*/ + +static void init_iterator(BMIter *iter) +{ + iter->firstvert = iter->nextvert = NULL; + iter->firstedge = iter->nextedge = NULL; + iter->firstloop = iter->nextloop = NULL; + iter->firstpoly = iter->nextpoly = NULL; + iter->ldata = NULL; +} + +/* + * Notes on iterator implementation: + * + * Iterators keep track of the next element + * in a sequence. When a step() callback is + * invoked the current value of 'next' is stored + * to be returned later and the next variable is + * incremented. + * + * When the end of a sequence is + * reached, next should always equal NULL + * +*/ + +/* + * VERT OF MESH CALLBACKS + * +*/ + +static void vert_of_mesh_begin(BMIter *iter) +{ + init_iterator(iter); + if(iter->bm->verts.first){ + iter->firstvert = iter->bm->verts.first; + iter->nextvert = iter->bm->verts.first; + } +} + +static void *vert_of_mesh_step(BMIter *iter) +{ + BMVert *current = iter->nextvert; + + if(iter->nextvert) + iter->nextvert = (BMVert*)(iter->nextvert->head.next); + + return current; +} + +/* + * EDGE OF MESH CALLBACKS + * +*/ + +static void edge_of_mesh_begin(BMIter *iter) +{ + init_iterator(iter); + if(iter->bm->edges.first){ + iter->firstedge = iter->bm->edges.first; + iter->nextedge = iter->bm->edges.first; + } +} + +static void *edge_of_mesh_step(BMIter *iter) +{ + BMEdge *current = iter->nextedge; + + if(iter->nextedge) + iter->nextedge = (BMEdge*)(iter->nextedge->head.next); + + return current; +} + +/* + * FACE OF MESH CALLBACKS + * +*/ + +static void face_of_mesh_begin(BMIter *iter) +{ + init_iterator(iter); + if(iter->bm->polys.first){ + iter->firstpoly = iter->bm->polys.first; + iter->nextpoly = iter->bm->polys.first; + } +} + +static void *face_of_mesh_step(BMIter *iter) +{ + BMFace *current = iter->nextpoly; + + if(iter->nextpoly) + iter->nextpoly = (BMFace*)(iter->nextpoly->head.next); + return current; +} + +/* + * EDGE OF VERT CALLBACKS + * +*/ + +static void edge_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + if(iter->vdata->edge){ + iter->firstedge = iter->vdata->edge; + iter->nextedge = iter->vdata->edge; + } +} + +static void *edge_of_vert_step(BMIter *iter) +{ + BMEdge *current = iter->nextedge; + + if(iter->nextedge) + iter->nextedge = bmesh_disk_nextedge(iter->nextedge, iter->vdata); + + if(iter->nextedge == iter->firstedge) iter->nextedge = NULL; + + return current; +} + +/* + * FACE OF VERT CALLBACKS + * +*/ + +static void face_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + iter->count = 0; + if(iter->vdata->edge) + iter->count = bmesh_disk_count_facevert(iter->vdata); + if(iter->count){ + iter->firstedge = bmesh_disk_find_first_faceedge(iter->vdata->edge, iter->vdata); + iter->nextedge = iter->firstedge; + iter->firstloop = bmesh_radial_find_first_facevert(iter->firstedge->loop, iter->vdata); + iter->nextloop = iter->firstloop; + } +} +static void *face_of_vert_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if(iter->count){ + iter->count--; + iter->nextloop = bmesh_radial_find_next_facevert(iter->nextloop, iter->vdata); + if(iter->nextloop == iter->firstloop){ + iter->nextedge = bmesh_disk_find_next_faceedge(iter->nextedge, iter->vdata); + iter->firstloop = bmesh_radial_find_first_facevert(iter->nextedge->loop, iter->vdata); + iter->nextloop = iter->firstloop; + } + } + + if(!iter->count) iter->nextloop = NULL; + + + if(current) return current->f; + return NULL; +} + +/* + * FACE OF EDGE CALLBACKS + * +*/ + +static void face_of_edge_begin(BMIter *iter) +{ + init_iterator(iter); + + if(iter->edata->loop){ + iter->firstloop = iter->edata->loop; + iter->nextloop = iter->edata->loop; + } +} + +static void *face_of_edge_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if(iter->nextloop) iter->nextloop = bmesh_radial_nextloop(iter->nextloop); + + if(iter->nextloop == iter->firstloop) iter->nextloop = NULL; + if(current) return current->f; + return NULL; +} + +/* + * VERT OF FACE CALLBACKS + * +*/ + +static void vert_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = iter->pdata->loopbase; +} + +static void *vert_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if(iter->nextloop) iter->nextloop = ((BMLoop*)(iter->nextloop->head.next)); + if(iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + if(current) return current->v; + return NULL; +} + +/* + * EDGE OF FACE CALLBACKS + * +*/ + +static void edge_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = iter->pdata->loopbase; +} + +static void *edge_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if(iter->nextloop) iter->nextloop = ((BMLoop*)(iter->nextloop->head.next)); + if(iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + if(current) return current->e; + return NULL; +} + +/* + * LOOP OF FACE CALLBACKS + * +*/ + +static void loop_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = iter->pdata->loopbase; +} + +static void *loop_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if(iter->nextloop) iter->nextloop = ((BMLoop*)(iter->nextloop->head.next)); + if(iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current; +} + +/* + * BMESH ITERATOR INIT + * + * Takes a bmesh iterator structure and fills + * it with the appropriate function pointers based + * upon its type and then calls BMeshIter_step() + * to return the first element of the iterator. + * +*/ +void *BMIter_New(BMIter *iter, BMesh *bm, int type, void *data) +{ + int argtype; + iter->type = type; + iter->bm = bm; + + switch(type){ + case BM_VERTS: + iter->begin = vert_of_mesh_begin; + iter->step = vert_of_mesh_step; + iter->bm = bm; + break; + case BM_EDGES: + iter->begin = edge_of_mesh_begin; + iter->step = edge_of_mesh_step; + iter->bm = bm; + break; + case BM_FACES: + iter->begin = face_of_mesh_begin; + iter->step = face_of_mesh_step; + iter->bm = bm; + break; + case BM_EDGES_OF_VERT: + iter->begin = edge_of_vert_begin; + iter->step = edge_of_vert_step; + iter->vdata = data; + break; + case BM_FACES_OF_VERT: + iter->begin = face_of_vert_begin; + iter->step = face_of_vert_step; + iter->vdata = data; + break; + case BM_FACES_OF_EDGE: + iter->begin = face_of_edge_begin; + iter->step = face_of_edge_step; + iter->edata = data; + break; + case BM_VERTS_OF_FACE: + iter->begin = vert_of_face_begin; + iter->step = vert_of_face_step; + iter->pdata = data; + break; + case BM_EDGES_OF_FACE: + iter->begin = edge_of_face_begin; + iter->step = edge_of_face_step; + iter->pdata = data; + break; + case BM_LOOPS_OF_FACE: + iter->begin = loop_of_face_begin; + iter->step = loop_of_face_step; + iter->pdata = data; + default: + break; + } + + iter->begin(iter); + return BMIter_Step(iter); +} diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c new file mode 100644 index 00000000000..8f6bfad03d3 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -0,0 +1,241 @@ +#include <string.h> +#include "bmesh.h" +#include "bmesh_private.h" + + +/* + * BM_MARK.C + * + * Selection routines for bmesh structures. + * This is actually all old code ripped from + * editmesh_lib.c and slightly modified to work + * for bmesh's. This also means that it has some + * of the same problems.... something that + * that should be addressed eventually. + * +*/ + + +/* + * BMESH SELECTMODE FLUSH + * + * Makes sure to flush selections + * 'upwards' (ie: all verts of an edge + * selects the edge and so on). This + * should only be called by system and not + * tool authors. + * +*/ + +void bmesh_selectmode_flush(BMesh *bm) +{ + BMEdge *e; + BMLoop *l; + BMFace *f; + + BMIter edges; + BMIter faces; + + int totsel; + + if(bm->selectmode & BM_VERT){ + for(e = BMIter_New(&edges, bm, BM_EDGES, bm ); e; e= BMIter_Step(&edges)){ + if(bmesh_test_sysflag(&(e->v1->head), BM_SELECT) && bmesh_test_sysflag(&(e->v2->head), BM_SELECT)) bmesh_set_sysflag(&(e->head), BM_SELECT); + else bmesh_clear_sysflag(&(e->head), BM_SELECT); + } + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f= BMIter_Step(&faces)){ + totsel = 0; + l=f->loopbase; + do{ + if(bmesh_test_sysflag(&(l->v->head), BM_SELECT)) + totsel++; + l = ((BMLoop*)(l->head.next)); + }while(l!=f->loopbase); + + if(totsel == f->len) + bmesh_set_sysflag(&(f->head), BM_SELECT); + else + bmesh_clear_sysflag(&(f->head), BM_SELECT); + } + } + else if(bm->selectmode & BM_EDGE) { + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f= BMIter_Step(&faces)){ + totsel = 0; + l=f->loopbase; + do{ + if(bmesh_test_sysflag(&(l->e->head), BM_SELECT)) + totsel++; + l = ((BMLoop*)(l->head.next)); + }while(l!=f->loopbase); + + if(totsel == f->len) + bmesh_set_sysflag(&(f->head), BM_SELECT); + else + bmesh_clear_sysflag(&(f->head), BM_SELECT); + } + } +} + +/* + * BMESH SELECT VERT + * + * Changes selection state of a single vertex + * in a mesh + * +*/ + +void BM_Select_Vert(BMesh *bm, BMVert *v, int select) +{ + if(select) + bmesh_set_sysflag(&(v->head), BM_SELECT); + else + bmesh_clear_sysflag(&(v->head), BM_SELECT); +} + +/* + * BMESH SELECT EDGE + * + * Changes selection state of a single edge + * in a mesh. Note that this is actually not + * 100 percent reliable. Deselecting an edge + * will also deselect both its vertices + * regardless of the selection state of + * other edges incident upon it. Fixing this + * issue breaks multi-select mode though... + * +*/ + +void BM_Select_Edge(BMesh *bm, BMEdge *e, int select) +{ + if(select){ + bmesh_set_sysflag(&(e->head), BM_SELECT); + bmesh_set_sysflag(&(e->v1->head), BM_SELECT); + bmesh_set_sysflag(&(e->v2->head), BM_SELECT); + } + else{ + bmesh_clear_sysflag(&(e->head), BM_SELECT); + bmesh_clear_sysflag(&(e->v1->head), BM_SELECT); + bmesh_clear_sysflag(&(e->v2->head), BM_SELECT); + } +} + +/* + * + * BMESH SELECT FACE + * + * Changes selection state of a single + * face in a mesh. This (might) suffer + * from same problems as edge select + * code... + * +*/ + +void BM_Select_Face(BMesh *bm, BMFace *f, int select) +{ + BMLoop *l; + + if(select){ + bmesh_set_sysflag(&(f->head), BM_SELECT); + l = f->loopbase; + do{ + bmesh_set_sysflag(&(l->v->head), BM_SELECT); + bmesh_set_sysflag(&(l->e->head), BM_SELECT); + l = ((BMLoop*)(l->head.next)); + }while(l != f->loopbase); + } + else{ + bmesh_clear_sysflag(&(f->head), BM_SELECT); + l = f->loopbase; + do{ + bmesh_clear_sysflag(&(l->v->head), BM_SELECT); + bmesh_clear_sysflag(&(l->e->head), BM_SELECT); + l = ((BMLoop*)(l->head.next)); + }while(l != f->loopbase); + } +} + +/* + * BMESH SELECTMODE SET + * + * Sets the selection mode for the bmesh + * +*/ + +void BM_Selectmode_Set(BMesh *bm, int selectmode) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + bm->selectmode = selectmode; + + if(bm->selectmode & BM_VERT){ + for(e = BMIter_New(&edges, bm, BM_EDGES, bm ); e; e= BMIter_Step(&edges)) + BM_Select_Edge(bm, e, 0); + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f= BMIter_Step(&faces)) + bmesh_clear_sysflag(&(f->head), 0); + bmesh_selectmode_flush(bm); + } + else if(bm->selectmode & BM_EDGE){ + for(v= BMIter_New(&verts, bm, BM_VERTS, bm ); v; v= BMIter_Step(&verts)) + BM_Select_Vert(bm, v, 0); + for(e= BMIter_New(&edges, bm, BM_EDGES, bm ); e; e= BMIter_Step(&edges)){ + if(bmesh_test_sysflag(&(e->head), BM_SELECT)) + BM_Select_Edge(bm, e, 1); + } + bmesh_selectmode_flush(bm); + } + else if(bm->selectmode & BM_FACE){ + for(e = BMIter_New(&edges, bm, BM_EDGES, bm ); e; e= BMIter_Step(&edges)) + BM_Select_Edge(bm, e, 0); + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f= BMIter_Step(&faces)){ + if(bmesh_test_sysflag(&(f->head), BM_SELECT)) + BM_Select_Face(bm, f, 1); + } + } +} + + +int BM_CountFlag(struct BMesh *bm, int type, int flag) +{ + BMHeader *head; + BMIter iter; + int tot = 0; + + if (type & BM_VERT) { + for (head = BMIter_New(&iter, bm, BM_VERTS, NULL); head; head=BMIter_Step(&iter)) { + if (head->flag & flag) tot++; + } + } + if (type & BM_EDGE) { + for (head = BMIter_New(&iter, bm, BM_EDGES, NULL); head; head=BMIter_Step(&iter)) { + if (head->flag & flag) tot++; + } + } + if (type & BM_FACE) { + for (head = BMIter_New(&iter, bm, BM_FACES, NULL); head; head=BMIter_Step(&iter)) { + if (head->flag & flag) tot++; + } + } + + return tot; +} + +void BM_Select(struct BMesh *bm, void *element, int select) +{ + BMHeader *head = element; + + if(head->type == BM_VERT) BM_Select_Vert(bm, (BMVert*)element, select); + else if(head->type == BM_EDGE) BM_Select_Edge(bm, (BMEdge*)element, select); + else if(head->type == BM_FACE) BM_Select_Face(bm, (BMFace*)element, select); +} + +int BM_Is_Selected(BMesh *bm, void *element) +{ + BMHeader *head = element; + return bmesh_test_sysflag(head, BM_SELECT); +}
\ No newline at end of file diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c new file mode 100644 index 00000000000..e8066aa52f9 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -0,0 +1,310 @@ +/** + * BME_mesh.c jan 2007 + * + * BM mesh level functions. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** 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. + * 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 LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" +#include "DNA_listBase.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +void BME_error(void); + +/*bmesh_error stub*/ +void bmesh_error(void) +{ + printf("BM modelling error!"); +} + +/*error system*/ +typedef struct bmop_error { + struct bmop_error *next, *prev; + int errorcode; + char *msg; +} bmop_error; + +void BMOP_RaiseError(BMesh *bm, int errcode, char *msg) +{ + bmop_error *err = MEM_callocN(sizeof(bmop_error), "bmop_error"); + err->errorcode = errcode; + err->msg = msg; + BLI_addhead(&bm->errorstack, err); +} + +/*returns error code or 0 if no error*/ +int BMOP_GetError(BMesh *bm, char **msg) +{ + bmop_error *err = bm->errorstack.first; + if (!err) return 0; + + if (msg) *msg = err->msg; + + return err->errorcode; +} + +int BMOP_CheckError(BMesh *bm) +{ + return bm->errorstack.first != NULL; +} + +int BMOP_PopError(BMesh *bm, char **msg) +{ + int errorcode = BMOP_GetError(bm, msg); + if (errorcode) + BLI_remlink(&bm->errorstack, &bm->errorstack.first); + + return errorcode; +} + +/* + * BMESH SET SYSFLAG + * + * Sets a bitflag for a given element. + * +*/ + +void bmesh_set_sysflag(BMHeader *head, int flag) +{ + head->flag |= flag; +} + +/* + * BMESH CLEAR SYSFLAG + * + * Clears a bitflag for a given element. + * +*/ + +void bmesh_clear_sysflag(BMHeader *head, int flag) +{ + head->flag &= ~flag; +} + + +/* + * BMESH TEST SYSFLAG + * + * Tests whether a bitflag is set for a given element. + * +*/ + +int bmesh_test_sysflag(BMHeader *head, int flag) +{ + if(head->flag & flag) + return 1; + return 0; +} + +/* + * BMESH MAKE MESH + * + * Allocates a new BMesh structure. + * Returns - + * Pointer to a BM + * +*/ + +BMesh *BM_Make_Mesh(int allocsize[4]) +{ + /*allocate the structure*/ + BMesh *bm = MEM_callocN(sizeof(BMesh),"BM"); + /*allocate the memory pools for the mesh elements*/ + bm->vpool = BLI_mempool_create(sizeof(BMVert), allocsize[0], allocsize[0]); + bm->epool = BLI_mempool_create(sizeof(BMEdge), allocsize[1], allocsize[1]); + bm->lpool = BLI_mempool_create(sizeof(BMLoop), allocsize[2], allocsize[2]); + bm->ppool = BLI_mempool_create(sizeof(BMFace), allocsize[3], allocsize[3]); + + /*allocate one flag pool that we dont get rid of.*/ + bm->flagpool = BLI_mempool_create(sizeof(BMFlagLayer), 512, 512); + bm->totflags = 1; + + return bm; +} +/* + * BMESH FREE MESH + * + * Frees a BMesh structure. +*/ + +void BM_Free_Mesh(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMLoop *l; + BMFace *f; + + + BMIter verts; + BMIter edges; + BMIter faces; + BMIter loops; + + for(v = BMIter_New(&verts, bm, BM_VERTS, bm ); v; v = BMIter_Step(&verts)) CustomData_bmesh_free_block( &(bm->vdata), &(v->data) ); + for(e = BMIter_New(&edges, bm, BM_EDGES, bm ); e; e = BMIter_Step(&edges)) CustomData_bmesh_free_block( &(bm->edata), &(e->data) ); + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f = BMIter_Step(&faces)){ + CustomData_bmesh_free_block( &(bm->pdata), &(f->data) ); + for(l = BMIter_New(&loops, bm, BM_LOOPS_OF_FACE, f ); l; l = BMIter_Step(&loops)) CustomData_bmesh_free_block( &(bm->ldata), &(l->data) ); + } + + /*Free custom data pools, This should probably go in CustomData_free?*/ + if(bm->vdata.totlayer) BLI_mempool_destroy(bm->vdata.pool); + if(bm->edata.totlayer) BLI_mempool_destroy(bm->edata.pool); + if(bm->ldata.totlayer) BLI_mempool_destroy(bm->ldata.pool); + if(bm->pdata.totlayer) BLI_mempool_destroy(bm->pdata.pool); + + /*free custom data*/ + CustomData_free(&bm->vdata,0); + CustomData_free(&bm->edata,0); + CustomData_free(&bm->ldata,0); + CustomData_free(&bm->pdata,0); + + /*destroy element pools*/ + BLI_mempool_destroy(bm->vpool); + BLI_mempool_destroy(bm->epool); + BLI_mempool_destroy(bm->ppool); + BLI_mempool_destroy(bm->lpool); + + /*destroy flag pool*/ + BLI_mempool_destroy(bm->flagpool); + + MEM_freeN(bm); +} + +/* + * BMESH COMPUTE NORMALS + * + * Updates the normals of a mesh. + * Note that this can only be called + * +*/ + +void BM_Compute_Normals(BMesh *bm) +{ + BMVert *v; + BMFace *f; + BMLoop *l; + + BMIter verts; + BMIter faces; + BMIter loops; + + unsigned int maxlength = 0; + float (*projectverts)[3]; + + /*first, find out the largest face in mesh*/ + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f = BMIter_Step(&faces)){ + if(f->len > maxlength) maxlength = f->len; + } + + /*make sure we actually have something to do*/ + if(maxlength < 3) return; + + /*allocate projectverts array*/ + projectverts = MEM_callocN(sizeof(float) * maxlength * 3, "BM normal computation array"); + + /*calculate all face normals*/ + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f = BMIter_Step(&faces)){ + bmesh_update_face_normal(bm, f, projectverts); + } + + /*Zero out vertex normals*/ + for(v = BMIter_New(&verts, bm, BM_VERTS, bm ); v; v = BMIter_Step(&verts)) v->no[0] = v->no[1] = v->no[2] = 0.0; + + /*add face normals to vertices*/ + for(f = BMIter_New(&faces, bm, BM_FACES, bm ); f; f = BMIter_Step(&faces)){ + for(l = BMIter_New(&loops, bm, BM_LOOPS_OF_FACE, f ); l; l = BMIter_Step(&loops)) VecAddf(l->v->no, l->v->no, f->no); + } + + /*average the vertex normals*/ + for(v = BMIter_New(&verts, bm, BM_VERTS, bm ); v; v= BMIter_Step(&verts)){ + if (Normalize(v->no)==0.0) { + VECCOPY(v->no, v->co); + Normalize(v->no); + } + } + + MEM_freeN(projectverts); +} + +/* + * BMESH BEGIN/END EDIT + * + * Functions for setting up a mesh for editing and cleaning up after + * the editing operations are done. These are called by the tools/operator + * API for each time a tool is executed. + * + * Returns - + * Nothing + * +*/ + +void bmesh_begin_edit(BMesh *bm){ + + /*Initialize some scratch pointer arrays used by eulers*/ + bm->vtar = MEM_callocN(sizeof(BMVert *) * 1024, "BM scratch vert array"); + bm->edar = MEM_callocN(sizeof(BMEdge *) * 1024, "BM scratch edge array"); + bm->lpar = MEM_callocN(sizeof(BMLoop *) * 1024, "BM scratch loop array"); + bm->plar = MEM_callocN(sizeof(BMFace *) * 1024, "BM scratch poly array"); + + bm->vtarlen = bm->edarlen = bm->lparlen = bm->plarlen = 1024; +} + +void bmesh_end_edit(BMesh *bm, int flag){ + int totvert, totedge, totface; + /*verify element counts*/ + totvert = BLI_countlist(&(bm->verts)); + totedge = BLI_countlist(&(bm->edges)); + totface = BLI_countlist(&(bm->polys)); + + if(bm->totvert!=totvert || bm->totedge!=totedge || bm->totface!=totface) BME_error(); + + /*free temp storage*/ + if(bm->vtar) MEM_freeN(bm->vtar); + if(bm->edar) MEM_freeN(bm->edar); + if(bm->lpar) MEM_freeN(bm->lpar); + if(bm->plar) MEM_freeN(bm->plar); + + /*zero out pointers*/ + bm->vtar = NULL; + bm->edar = NULL; + bm->lpar = NULL; + bm->plar = NULL; + bm->vtarlen = bm->edarlen = bm->lparlen = bm->plarlen = 0; + + /*compute normals, clear temp flags and flush selections*/ + BM_Compute_Normals(bm); + bmesh_selectmode_flush(bm); +} diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c new file mode 100644 index 00000000000..525c26e5d9a --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -0,0 +1,309 @@ +#include <limits.h> +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "BKE_utildefines.h" +#include "BLI_blenlib.h" +#include "BLI_linklist.h" +#include "BLI_ghash.h" +#include "BLI_arithb.h" + +#include "bmesh.h" +#include "bmesh_private.h" + + +/* + * BME_MODS.C + * + * This file contains functions for locally modifying + * the topology of existing mesh data. (split, join, flip ect). + * +*/ + +/** + * bmesh_dissolve_disk + * + * Turns the face region surrounding a manifold vertex into + * A single polygon. + * + * + * Example: + * + * |=========| |=========| + * | \ / | | | + * Before: | V | After: | | + * | / \ | | | + * |=========| |=========| + * + * + * Returns - + * 1 for success, 0 for failure. + */ + +void BM_Dissolve_Disk(BMesh *bm, BMVert *v) { + BMFace *f, *f2; + BMEdge *e, *keepedge=NULL, *baseedge=NULL; + BMLoop *loop; + int done, len; + + if(BM_Nonmanifold_Vert(bm, v)) return; + + if(v->edge){ + /*v->edge we keep, what else?*/ + e = v->edge; + len = 0; + do{ + e = bmesh_disk_nextedge(e,v); + if(!(BM_Edge_Share_Faces(e, v->edge))){ + keepedge = e; + baseedge = v->edge; + break; + } + len++; + }while(e != v->edge); + } + + /*this code for handling 2 and 3-valence verts + may be totally bad.*/ + if (keepedge == NULL && len == 3) { + /*handle specific case for three-valence. solve it by + increasing valence to four. this may be hackish. . .*/ + loop = e->loop; + if (loop->v == v) loop = (BMLoop*) loop->head.next; + BM_Split_Face(bm, loop->f, v, loop->v, NULL, NULL, 0); + + BM_Dissolve_Disk(bm, v); + return; + } else if (keepedge == NULL && len == 2) { + /*handle two-valence*/ + f = v->edge->loop->f; + f2 = ((BMLoop*)v->edge->loop->radial.next->data)->f; + /*collapse the vertex*/ + BM_Collapse_Vert(bm, v->edge, v, 1.0, 0); + BM_Join_Faces(bm, f, f2, NULL, 0, 0); + } + + if(keepedge){ + done = 0; + while(!done){ + done = 1; + e = v->edge; + do{ + f = NULL; + len = bmesh_cycle_length(&(e->loop->radial)); + if(len == 2 && (e!=baseedge) && (e!=keepedge)) + f = BM_Join_Faces(bm, e->loop->f, ((BMLoop*)(e->loop->radial.next->data))->f, e, 0, 0); + if(f){ + done = 0; + break; + } + e = bmesh_disk_nextedge(e, v); + }while(e != v->edge); + } + + /*get remaining two faces*/ + f = v->edge->loop->f; + f2 = ((BMLoop*)v->edge->loop->radial.next->data)->f; + /*collapse the vertex*/ + BM_Collapse_Vert(bm, baseedge, v, 1.0, 0); + + /*join two remaining faces*/ + BM_Join_Faces(bm, f, f2, NULL, 0, 0); + } +} + +/** + * bmesh_join_faces + * + * joins two adjacenct faces togather. If weldUVs == 1 + * and the uv/vcols of the two faces are non-contigous, the + * per-face properties of f2 will be transformed into place + * around f1. + * + * Returns - + * BMFace pointer + */ + +BMFace *BM_Join_Faces(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e, int calcnorm, int weldUVs) { + + BMLoop *l1, *l2; + BMEdge *jed=NULL; + + jed = e; + if(!jed){ + /*search for an edge that has both these faces in its radial cycle*/ + l1 = f1->loopbase; + do{ + if( ((BMLoop*)l1->radial.next->data)->f == f2 ){ + jed = l1->e; + break; + } + l1 = ((BMLoop*)(l1->head.next)); + }while(l1!=f1->loopbase); + } + + l1 = jed->loop; + l2 = l1->radial.next->data; + if (l1->v == l2->v) { + bmesh_loop_reverse(bm, f2); + } + + return bmesh_jfke(bm, f1, f2, jed); +} + +/*connects two verts together, automatically (if very naively) finding the + face they both share (if there is one) and splittling it. use this at your + own risk, as it doesn't handle the many complex cases it should (like zero-area faces, + multiple faces, etc). + + this is really only meant for cases where you don't know before hand the face + the two verts belong to for splitting (e.g. the subdivision operator). +*/ + +BMEdge *BM_Connect_Verts(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf) { + BMIter iter, iter2; + BMVert *v; + BMLoop *nl; + BMFace *face; + + /*this isn't the best thing in the world. it doesn't handle cases where there's + multiple faces yet. that might require a convexity test to figure out which + face is "best," and who knows what for non-manifold conditions.*/ + for (face = BMIter_New(&iter, bm, BM_FACES_OF_VERT, v1); face; face=BMIter_Step(&iter)) { + for (v=BMIter_New(&iter2, bm, BM_VERTS_OF_FACE, face); v; v=BMIter_Step(&iter2)) { + if (v == v2) { + face = BM_Split_Face(bm, face, v1, v2, &nl, NULL, 1); + + if (nf) *nf = face; + return nl->e; + } + } + } + + return NULL; +} + +/** + * BM_split_face + * + * Splits a single face into two. + * + * Returns - + * BMFace pointer + */ + +BMFace *BM_Split_Face(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **nl, BMEdge *example, int calcnorm) +{ + BMFace *nf; + nf = bmesh_sfme(bm,f,v1,v2,nl); + + /* + nf->flag = f->flag; + if (example->flag & SELECT) f->flag |= BM_SELECT; + 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; +} + +/** + * bmesh_collapse_vert + * + * Collapses a vertex that has only two manifold edges + * onto a vertex it shares an edge with. Fac defines + * the amount of interpolation for Custom Data. + * + * Note that this is not a general edge collapse function. For + * that see BM_manifold_edge_collapse + * + * TODO: + * Insert error checking for KV valance. + * + * Returns - + * Nothing + */ + +void BM_Collapse_Vert(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, int calcnorm){ + void *src[2]; + float w[2]; + BMLoop *l=NULL, *kvloop=NULL, *tvloop=NULL; + BMVert *tv = bmesh_edge_getothervert(ke,kv); + + w[0] = 1.0f - fac; + w[1] = fac; + + if(ke->loop){ + l = ke->loop; + do{ + if(l->v == tv && ((BMLoop*)(l->head.next))->v == kv){ + tvloop = l; + kvloop = ((BMLoop*)(l->head.next)); + + src[0] = kvloop->data; + src[1] = tvloop->data; + CustomData_bmesh_interp(&bm->ldata, src,w, NULL, 2, kvloop->data); + } + l=l->radial.next->data; + }while(l!=ke->loop); + } + BM_Data_Interp_From_Verts(bm, kv, tv, kv, fac); + bmesh_jekv(bm,ke,kv); +} + +/** + * BM_split_edge + * + * Splits an edge. v should be one of the vertices in e and + * defines the direction of the splitting operation for interpolation + * purposes. + * + * Returns - + * the new vert + */ + +BMVert *BM_Split_Edge(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent, int calcnorm) { + BMVert *nv, *v2; + + v2 = bmesh_edge_getothervert(e,v); + nv = bmesh_semv(bm,v,e,ne); + if (nv == NULL) return NULL; + VECSUB(nv->co,v2->co,v->co); + VECADDFAC(nv->co,v->co,nv->co,percent); + if (ne) { + if(bmesh_test_sysflag(&(e->head), BM_SELECT)) bmesh_set_sysflag(&((*ne)->head), BM_SELECT); + if(bmesh_test_sysflag(&(e->head), BM_HIDDEN)) bmesh_set_sysflag(&((*ne)->head), BM_HIDDEN); + } + /*v->nv->v2*/ + BM_Data_Facevert_Edgeinterp(bm,v2, v, nv, e, percent); + BM_Data_Interp_From_Verts(bm, v2, v, nv, percent); + return nv; +} + +BMVert *BM_Split_Edge_Multi(BMesh *bm, BMEdge *e, int numcuts) +{ + int i; + float percent, step,length, vt1[3], v2[3]; + BMVert *nv = NULL; + + percent = 0.0; + step = (1.0/((float)numcuts+1)); + + length = VecLenf(e->v1->co,e->v2->co); + VECCOPY(v2,e->v2->co); + VecSubf(vt1, e->v1->co, e->v2->co); + + for(i=0; i < numcuts; i++){ + percent += step; + nv = BM_Split_Edge(bm, e->v2, e, NULL, percent, 0); + VECCOPY(nv->co,vt1); + VecMulf(nv->co,percent); + VecAddf(nv->co,v2,nv->co); + } + return nv; +} diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c new file mode 100644 index 00000000000..b25cdd45337 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -0,0 +1,91 @@ +#include "bmesh.h" +#include "bmesh_private.h" + +#include <stdio.h> + +BMOpDefine def_makefgonsop = { + {0}, + bmesh_make_fgons_exec, + BMOP_MAKE_FGONS_TOTSLOT, + 0 +}; + +BMOpDefine def_dissolvevertsop = { + {BMOP_OPSLOT_PNT_BUF}, + dissolveverts_exec, + BMOP_DISVERTS_TOTSLOT, + 0 +}; + +BMOpDefine def_dissolvefacesop = { + {BMOP_OPSLOT_PNT_BUF}, + dissolvefaces_exec, + BMOP_DISFACES_TOTSLOT, + 0 +}; + +BMOpDefine def_triangop = { + {BMOP_OPSLOT_PNT_BUF, + BMOP_OPSLOT_PNT_BUF, + BMOP_OPSLOT_PNT_BUF}, + triangulate_exec, + BMOP_TRIANG_TOTSLOT, + 0 +}; + +BMOpDefine def_subdop = { + {BMOP_OPSLOT_PNT_BUF}, + esubdivide_exec, + BMOP_ESUBDIVIDE_TOTSLOT, + 0 +}; + +BMOpDefine def_edit2bmesh = { + {BMOP_OPSLOT_PNT}, + edit2bmesh_exec, + BMOP_TO_EDITMESH_TOTSLOT, + 0 +}; + +BMOpDefine def_bmesh2edit = { + {BMOP_OPSLOT_PNT}, + bmesh2edit_exec, + BMOP_FROM_EDITMESH_TOTSLOT, + 0 +}; + +BMOpDefine def_delop = { + {BMOP_OPSLOT_PNT_BUF, BMOP_OPSLOT_INT}, + delop_exec, + BMOP_DEL_TOTSLOT, + 0 +}; + +BMOpDefine def_dupeop = { + {BMOP_OPSLOT_PNT_BUF, BMOP_OPSLOT_PNT_BUF, BMOP_OPSLOT_PNT_BUF}, + dupeop_exec, + BMOP_DUPE_TOTSLOT, + 0 +}; + +BMOpDefine def_splitop = { + {BMOP_OPSLOT_PNT_BUF, BMOP_OPSLOT_PNT_BUF}, + splitop_exec, + BMOP_SPLIT_TOTSLOT, + 0 +}; + +BMOpDefine *opdefines[] = { + &def_splitop, + &def_dupeop, + &def_delop, + &def_edit2bmesh, + &def_bmesh2edit, + &def_subdop, + &def_triangop, + &def_dissolvefacesop, + &def_dissolvevertsop, + &def_makefgonsop, +}; + +int bmesh_total_ops = (sizeof(opdefines) / sizeof(void*)); diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c new file mode 100644 index 00000000000..231c0b999b4 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -0,0 +1,521 @@ +#include "MEM_guardedalloc.h" + +#include "BLI_memarena.h" +#include "BLI_mempool.h" + +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include <string.h> + +/*forward declarations*/ +static void alloc_flag_layer(BMesh *bm); +static void free_flag_layer(BMesh *bm); + +/*function pointer table*/ +typedef void (*opexec)(struct BMesh *bm, struct BMOperator *op); + +/*operator slot type information - size of one element of the type given.*/ +const int BMOP_OPSLOT_TYPEINFO[BMOP_OPSLOT_TYPES] = { + sizeof(int), + sizeof(float), + sizeof(void*), + 0, /* unused */ + 0, /* unused */ + 0, /* unused */ + sizeof(float)*3, + sizeof(int), /* int buffer */ + sizeof(float), /* float buffer */ + sizeof(void*) /* pointer buffer */ +}; + +/* + * BMESH OPSTACK PUSH + * + * Pushes the opstack down one level + * and allocates a new flag layer if + * appropriate. + * +*/ + +void BMO_push(BMesh *bm, BMOperator *op) +{ + bm->stackdepth++; + + /*add flag layer, if appropriate*/ + if (bm->stackdepth > 1) + alloc_flag_layer(bm); +} + +/* + * BMESH OPSTACK POP + * + * Pops the opstack one level + * and frees a flag layer if appropriate + * TODO: investigate NOT freeing flag + * layers. + * +*/ +void BMO_pop(BMesh *bm) +{ + bm->stackdepth--; + if(bm->stackdepth > 1) + free_flag_layer(bm); +} + +/* + * BMESH OPSTACK INIT OP + * + * Initializes an operator structure + * to a certain type + * +*/ + +void BMO_Init_Op(BMOperator *op, int opcode) +{ + int i; + + memset(op, 0, sizeof(BMOperator)); + op->type = opcode; + + //currently not used, flags are always allocated + op->needflag = !(opdefines[opcode]->flag & BMO_NOFLAGS); + + /*initialize the operator slot types*/ + for(i = 0; i < opdefines[opcode]->totslot; i++) { + op->slots[i].slottype = opdefines[opcode]->slottypes[i]; + op->slots[i].index = i; + } + + /*callback*/ + op->exec = opdefines[opcode]->exec; + + /*memarena, used for operator's slot buffers*/ + op->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + BLI_memarena_use_calloc (op->arena); +} + +/* + * BMESH OPSTACK EXEC OP + * + * Executes a passed in operator. This handles + * the allocation and freeing of temporary flag + * layers and starting/stopping the modelling + * loop. Can be called from other operators + * exec callbacks as well. + * +*/ + +void BMO_Exec_Op(BMesh *bm, BMOperator *op) +{ + + BMO_push(bm, op); + + if(bm->stackdepth == 1) + bmesh_begin_edit(bm); + op->exec(bm, op); + + if(bm->stackdepth == 1) + bmesh_end_edit(bm,0); + + BMO_pop(bm); +} + +/* + * BMESH OPSTACK FINISH OP + * + * Does housekeeping chores related to finishing + * up an operator. + * +*/ + +void BMO_Finish_Op(BMesh *bm, BMOperator *op) +{ + BLI_memarena_free(op->arena); +} + +/* + * BMESH OPSTACK GET SLOT + * + * Returns a pointer to the slot of + * type 'slotcode' + * +*/ + +BMOpSlot *BMO_GetSlot(BMOperator *op, int slotcode) +{ + return &(op->slots[slotcode]); +} + +/* + * BMESH OPSTACK COPY SLOT + * + * Copies data from one slot to another + * +*/ + +void BMO_CopySlot(BMOperator *source_op, BMOperator *dest_op, int src, int dst) +{ + BMOpSlot *source_slot = &source_op->slots[src]; + BMOpSlot *dest_slot = &dest_op->slots[dst]; + + if(source_slot == dest_slot) + return; + + if(source_slot->slottype != dest_slot->slottype) + return; + + if(dest_slot->slottype > BMOP_OPSLOT_VEC){ + /*do buffer copy*/ + dest_slot->data.buf = NULL; + dest_slot->len = source_slot->len; + if(dest_slot->len){ + dest_slot->data.buf = BLI_memarena_alloc(dest_op->arena, BMOP_OPSLOT_TYPEINFO[dest_slot->slottype] * dest_slot->len); + memcpy(dest_slot->data.buf, source_slot->data.buf, BMOP_OPSLOT_TYPEINFO[dest_slot->slottype] * dest_slot->len); + } + } else { + dest_slot->data = source_slot->data; + } +} + +/* + * BMESH OPSTACK SET XXX + * + * Sets the value of a slot depending on it's type + * +*/ + + +void BMO_Set_Float(BMOperator *op, int slotcode, float f) +{ + if( !(op->slots[slotcode].slottype == BMOP_OPSLOT_FLT) ) + return; + op->slots[slotcode].data.f = f; +} + +void BMO_Set_Int(BMOperator *op, int slotcode, int i) +{ + if( !(op->slots[slotcode].slottype == BMOP_OPSLOT_INT) ) + return; + op->slots[slotcode].data.i = i; + +} + + +void BMO_Set_PntBuf(BMOperator *op, int slotcode, void *p, int len) +{ + if( !(op->slots[slotcode].slottype == BMOP_OPSLOT_PNT_BUF) ) + return; + + op->slots[slotcode].data.p = p; + op->slots[slotcode].len = len; +} + +void BMO_Set_Pnt(BMOperator *op, int slotcode, void *p) +{ + if( !(op->slots[slotcode].slottype == BMOP_OPSLOT_PNT) ) + return; + op->slots[slotcode].data.p = p; +} +void BMO_Set_Vec(BMOperator *op, int slotcode, float *vec) +{ + if( !(op->slots[slotcode].slottype == BMOP_OPSLOT_VEC) ) + return; + + VECCOPY(op->slots[slotcode].data.vec, vec); +} + +/* + * BMO_SETFLAG + * + * Sets a flag for a certain element + * +*/ +void BMO_SetFlag(BMesh *bm, void *element, int flag) +{ + BMHeader *head = element; + head->flags[bm->stackdepth-1].mask |= flag; +} + +/* + * BMO_CLEARFLAG + * + * Clears a specific flag from a given element + * +*/ + +void BMO_ClearFlag(BMesh *bm, void *element, int flag) +{ + BMHeader *head = element; + head->flags[bm->stackdepth].mask &= ~flag; +} + +/* + * BMO_TESTFLAG + * + * Tests whether or not a flag is set for a specific element + * + * +*/ + +int BMO_TestFlag(BMesh *bm, void *element, int flag) +{ + BMHeader *head = element; + if(head->flags[bm->stackdepth-1].mask & flag) + return 1; + return 0; +} + +/* + * BMO_COUNTFLAG + * + * Counts the number of elements of a certain type that + * have a specific flag set. + * +*/ + +int BMO_CountFlag(BMesh *bm, int flag, int type) +{ + BMIter elements; + BMHeader *e; + int count = 0; + + if(type & BM_VERT){ + for(e = BMIter_New(&elements, bm, BM_VERTS, bm); e; e = BMIter_Step(&elements)){ + if(BMO_TestFlag(bm, e, flag)) + count++; + } + } + if(type & BM_EDGE){ + for(e = BMIter_New(&elements, bm, BM_EDGES, bm); e; e = BMIter_Step(&elements)){ + if(BMO_TestFlag(bm, e, flag)) + count++; + } + } + if(type & BM_FACE){ + for(e = BMIter_New(&elements, bm, BM_FACES, bm); e; e = BMIter_Step(&elements)){ + if(BMO_TestFlag(bm, e, flag)) + count++; + } + } + + return count; +} + +static void *alloc_slot_buffer(BMOperator *op, int slotcode, int len){ + + /*check if its actually a buffer*/ + if( !(op->slots[slotcode].slottype > BMOP_OPSLOT_VEC) ) + return NULL; + + op->slots[slotcode].len = len; + if(len) + op->slots[slotcode].data.buf = BLI_memarena_alloc(op->arena, BMOP_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode]] * len); + return op->slots[slotcode].data.buf; +} + +/* + * + * BMO_HEADERFLAG_TO_SLOT + * + * Copies elements of a certain type, which have a certain header flag set + * into an output slot for an operator. + * +*/ + +void BMO_HeaderFlag_To_Slot(BMesh *bm, BMOperator *op, int slotcode, int flag, int type) +{ + BMIter elements; + BMHeader *e; + BMOpSlot *output = BMO_GetSlot(op, slotcode); + int totelement=0, i=0; + + totelement = BM_CountFlag(bm, type, BM_SELECT); + + if(totelement){ + alloc_slot_buffer(op, slotcode, totelement); + + if (type & BM_VERT) { + for (e = BMIter_New(&elements, bm, BM_VERTS, bm); e; e = BMIter_Step(&elements)) { + if(e->flag & flag) { + ((BMHeader**)output->data.p)[i] = e; + i++; + } + } + } + + if (type & BM_EDGE) { + for (e = BMIter_New(&elements, bm, BM_EDGES, bm); e; e = BMIter_Step(&elements)) { + if(e->flag & flag){ + ((BMHeader**)output->data.p)[i] = e; + i++; + } + } + } + + if (type & BM_FACE) { + for (e = BMIter_New(&elements, bm, BM_FACES, bm); e; e = BMIter_Step(&elements)) { + if(e->flag & flag){ + ((BMHeader**)output->data.p)[i] = e; + i++; + } + } + } + } +} + +/* + * + * BMO_FLAG_TO_SLOT + * + * Copies elements of a certain type, which have a certain flag set + * into an output slot for an operator. + * +*/ +void BMO_Flag_To_Slot(BMesh *bm, BMOperator *op, int slotcode, int flag, int type) +{ + BMIter elements; + BMHeader *e; + BMOpSlot *output = BMO_GetSlot(op, slotcode); + int totelement = BMO_CountFlag(bm, flag, type), i=0; + + if(totelement){ + alloc_slot_buffer(op, slotcode, totelement); + + if (type & BM_VERT) { + for (e = BMIter_New(&elements, bm, BM_VERTS, bm); e; e = BMIter_Step(&elements)) { + if(BMO_TestFlag(bm, e, flag)){ + ((BMHeader**)output->data.p)[i] = e; + i++; + } + } + } + + if (type & BM_EDGE) { + for (e = BMIter_New(&elements, bm, BM_EDGES, bm); e; e = BMIter_Step(&elements)) { + if(BMO_TestFlag(bm, e, flag)){ + ((BMHeader**)output->data.p)[i] = e; + i++; + } + } + } + + if (type & BM_FACE) { + for (e = BMIter_New(&elements, bm, BM_FACES, bm); e; e = BMIter_Step(&elements)) { + if(BMO_TestFlag(bm, e, flag)){ + ((BMHeader**)output->data.p)[i] = e; + i++; + } + } + } + } +} + +/* + * + * BMO_FLAG_BUFFER + * + * Flags elements in a slots buffer + * +*/ + +void BMO_Flag_Buffer(BMesh *bm, BMOperator *op, int slotcode, int flag) +{ + BMOpSlot *slot = BMO_GetSlot(op, slotcode); + BMHeader **data = slot->data.p; + int i; + + for(i = 0; i < slot->len; i++) + BMO_SetFlag(bm, data[i], flag); +} + + +/* + * + * ALLOC/FREE FLAG LAYER + * + * Used by operator stack to free/allocate + * private flag data. This is allocated + * using a mempool so the allocation/frees + * should be quite fast. + * + * TODO: + * Investigate not freeing flag layers until + * all operators have been executed. This would + * save a lot of realloc potentially. + * +*/ + +static void alloc_flag_layer(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + BLI_mempool *oldpool = bm->flagpool; /*old flag pool*/ + void *oldflags; + + /*allocate new flag pool*/ + bm->flagpool = BLI_mempool_create(sizeof(BMFlagLayer)*(bm->totflags+1), 512, 512 ); + + /*now go through and memcpy all the flags. Loops don't get a flag layer at this time...*/ + for(v = BMIter_New(&verts, bm, BM_VERTS, bm); v; v = BMIter_Step(&verts)){ + oldflags = v->head.flags; + v->head.flags = BLI_mempool_calloc(bm->flagpool); + memcpy(v->head.flags, oldflags, sizeof(BMFlagLayer)*bm->totflags); /*dont know if this memcpy usage is correct*/ + } + for(e = BMIter_New(&edges, bm, BM_EDGES, bm); e; e = BMIter_Step(&edges)){ + oldflags = e->head.flags; + e->head.flags = BLI_mempool_calloc(bm->flagpool); + memcpy(e->head.flags, oldflags, sizeof(BMFlagLayer)*bm->totflags); + } + for(f = BMIter_New(&faces, bm, BM_FACES, bm); f; f = BMIter_Step(&faces)){ + oldflags = f->head.flags; + f->head.flags = BLI_mempool_calloc(bm->flagpool); + memcpy(f->head.flags, oldflags, sizeof(BMFlagLayer)*bm->totflags); + } + bm->totflags++; + BLI_mempool_destroy(oldpool); +} + +static void free_flag_layer(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + BLI_mempool *oldpool = bm->flagpool; + void *oldflags; + + /*de-increment the totflags first...*/ + bm->totflags--; + /*allocate new flag pool*/ + bm->flagpool = BLI_mempool_create(sizeof(BMFlagLayer)*bm->totflags, 512, 512); + + /*now go through and memcpy all the flags*/ + for(v = BMIter_New(&verts, bm, BM_VERTS, bm); v; v = BMIter_Step(&verts)){ + oldflags = v->head.flags; + v->head.flags = BLI_mempool_alloc(bm->flagpool); + memcpy(v->head.flags, oldflags, sizeof(BMFlagLayer)*bm->totflags); /*correct?*/ + } + for(e = BMIter_New(&edges, bm, BM_EDGES, bm); e; e = BMIter_Step(&edges)){ + oldflags = e->head.flags; + e->head.flags = BLI_mempool_alloc(bm->flagpool); + memcpy(e->head.flags, oldflags, sizeof(BMFlagLayer)*bm->totflags); + } + for(f = BMIter_New(&faces, bm, BM_FACES, bm); f; f = BMIter_Step(&faces)){ + oldflags = f->head.flags; + f->head.flags = BLI_mempool_alloc(bm->flagpool); + memcpy(f->head.flags, oldflags, sizeof(BMFlagLayer)*bm->totflags); + } + + BLI_mempool_destroy(oldpool); +} diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h new file mode 100644 index 00000000000..ccdfd00e2e0 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -0,0 +1,21 @@ +#ifndef BM_OPERATORS_PRIVATE_H +#define BM_OPERATORS_PRIVATE_H + +struct BMesh; +struct BMOperator; + +void BMO_push(struct BMesh *bm, struct BMOperator *op); +void BMO_pop(struct BMesh *bm); + +void splitop_exec(struct BMesh *bm, struct BMOperator *op); +void dupeop_exec(struct BMesh *bm, struct BMOperator *op); +void delop_exec(struct BMesh *bm, struct BMOperator *op); +void esubdivide_exec(BMesh *bmesh, BMOperator *op); +void edit2bmesh_exec(BMesh *bmesh, BMOperator *op); +void bmesh2edit_exec(BMesh *bmesh, BMOperator *op); +void triangulate_exec(BMesh *bmesh, BMOperator *op); +void dissolvefaces_exec(BMesh *bmesh, BMOperator *op); +void dissolveverts_exec(BMesh *bmesh, BMOperator *op); +void bmesh_make_fgons_exec(BMesh *bmesh, BMOperator *op); + +#endif diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c new file mode 100644 index 00000000000..b5a65932743 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -0,0 +1,414 @@ +#include "bmesh.h" +#include "bmesh_private.h" + +#include "BLI_arithb.h" +#include "BKE_utildefines.h" +#include <string.h> + +/* + * + * BME POLYGON.C + * + * This file contains code for dealing + * with polygons (normal/area calculation, + * tesselation, ect) + * + * TODO: + * -Add in Tesselator frontend that creates + * BMTriangles from copied faces + * -Add in Function that checks for and flags + * degenerate faces. + * +*/ + +/* + * TEST EDGE SIDE and POINT IN TRIANGLE + * + * Point in triangle tests stolen from scanfill.c. + * Used for tesselator + * +*/ + +static short testedgeside(float *v1, float *v2, float *v3) +/* is v3 to the right of v1-v2 ? With exception: v3==v1 || v3==v2 */ +{ + float inp; + + //inp= (v2[cox]-v1[cox])*(v1[coy]-v3[coy]) +(v1[coy]-v2[coy])*(v1[cox]-v3[cox]); + inp= (v2[0]-v1[0])*(v1[1]-v3[1]) +(v1[1]-v2[1])*(v1[0]-v3[0]); + + if(inp<0.0) return 0; + else if(inp==0) { + if(v1[0]==v3[0] && v1[1]==v3[1]) return 0; + if(v2[0]==v3[0] && v2[1]==v3[1]) return 0; + } + return 1; +} + +static int point_in_triangle(float *v1, float *v2, float *v3, float *pt) +{ + if(testedgeside(v1,v2,pt) && testedgeside(v2,v3,pt) && testedgeside(v3,v1,pt)) + return 1; + return 0; +} + +/* + * CONVEX ANGLE + * + * Tests whether or not a given angle in + * a polygon is convex or not. Note that + * this assumes that the polygon has been + * projected to the x/y plane + * +*/ +static int convexangle(float *v1t, float *v2t, float *v3t) +{ + float v1[3], v3[3], n[3]; + VecSubf(v1, v1t, v2t); + VecSubf(v3, v3t, v2t); + + Normalize(v1); + Normalize(v3); + Crossf(n, v1, v3); + + if(n[2] < 0.0) + return 0; + + return 1; +} + +/* + * COMPUTE POLY NORMAL + * + * Computes the normal of a planar + * polygon See Graphics Gems for + * computing newell normal. + * +*/ +static void compute_poly_normal(float normal[3], float (*verts)[3], int nverts) +{ + + float *u, *v; + int i; + + normal[0] = 0.0; + normal[1] = 0.0; + normal[2] = 0.0; + + for(i = 0; i < nverts; i++){ + u = verts[i]; + v = verts[(i+1) % nverts]; + + normal[0] += (u[1] - v[1]) * (u[2] + v[2]); + normal[1] += (u[2] - v[2]) * (u[0] + v[0]); + normal[2] += (u[0] - v[0]) * (u[1] + v[1]); + i++; + } + + Normalize(normal); +} + +/* + * COMPUTE POLY CENTER + * + * Computes the centroid and + * area of a polygon in the X/Y + * plane. + * +*/ + +static int compute_poly_center(float center[3], float *area, float (*verts)[3], int nverts) +{ + int i, j; + float atmp = 0.0, xtmp = 0.0, ytmp = 0.0, ai; + + center[0] = center[1] = center[2] = 0.0; + + if(nverts < 3) + return 0; + + i = nverts-1; + j = 0; + + while(j < nverts){ + ai = verts[i][0] * verts[j][1] - verts[j][0] * verts[i][1]; + atmp += ai; + xtmp += (verts[j][0] + verts[i][0]) * ai; + ytmp += (verts[j][1] + verts[i][1]) * ai; + i = j; + j += 1; + } + + if(area) + *area = atmp / 2.0; + + if (atmp != 0){ + center[0] = xtmp / (3.0 * atmp); + center[1] = xtmp / (3.0 * atmp); + return 1; + } + return 0; +} + + +/* + * COMPUTE POLY PLANE + * + * Projects a set polygon's vertices to + * a plane defined by the average + * of its edges cross products + * +*/ + +void compute_poly_plane(float (*verts)[3], int nverts) +{ + + float avgc[3], norm[3], temp[3], mag, avgn[3]; + float *v1, *v2, *v3; + int i; + + if(nverts < 3) + return; + + avgn[0] = avgn[1] = avgn[2] = 0.0; + avgc[0] = avgc[1] = avgc[2] = 0.0; + + for(i = 0; i < nverts; i++){ + v1 = verts[i]; + v2 = verts[(i+1) % nverts]; + v3 = verts[(i+2) % nverts]; + CalcNormFloat(v1, v2, v3, norm); + + avgn[0] += norm[0]; + avgn[1] += norm[1]; + avgn[2] += norm[2]; + } + + /*what was this bit for?*/ + if(avgn[0] == 0.0 && avgn[1] == 0.0 && avgn[2] == 0.0){ + avgn[0] = 0.0; + avgn[1] = 0.0; + avgn[2] = 1.0; + } else { + avgn[0] /= nverts; + avgn[1] /= nverts; + avgn[2] /= nverts; + Normalize(avgn); + } + + for(i = 0; i < nverts; i++){ + v1 = verts[i]; + VECCOPY(temp, v1); + mag = 0.0; + mag += (temp[0] * avgn[0]); + mag += (temp[1] * avgn[1]); + mag += (temp[2] * avgn[2]); + + temp[0] = (avgn[0] * mag); + temp[1] = (avgn[1] * mag); + temp[2] = (avgn[2] * mag); + + VecSubf(v1, v1, temp); + } +} + +/* + * POLY ROTATE PLANE + * + * Rotates a polygon so that it's + * normal is pointing towards the mesh Z axis + * +*/ + +void poly_rotate_plane(float normal[3], float (*verts)[3], int nverts) +{ + + float up[3] = {0.0,0.0,1.0}, axis[3], angle, q[4]; + float mat[3][3]; + int i; + + Crossf(axis, up, normal); + angle = VecAngle2(normal, up); + + AxisAngleToQuat(q, axis, angle); + QuatToMat3(q, mat); + + for(i = 0; i < nverts; i++) + Mat3MulVecfl(mat, verts[i]); +} + +/* + * BMESH UPDATE FACE NORMAL + * + * Updates the stored normal for the + * given face. Requires that a buffer + * of sufficient length to store projected + * coordinates for all of the face's vertices + * is passed in as well. + * +*/ + +void bmesh_update_face_normal(BMesh *bm, BMFace *f, float (*projectverts)[3]) +{ + BMLoop *l; + int i; + + if(f->len > 4){ + i = 0; + l = f->loopbase; + do{ + VECCOPY(projectverts[i], l->v->co); + l = (BMLoop*)(l->head.next); + }while(l!=f->loopbase); + + compute_poly_plane(projectverts, f->len); + compute_poly_normal(f->no, projectverts, f->len); + } + else if(f->len == 3){ + BMVert *v1, *v2, *v3; + v1 = f->loopbase->v; + v2 = ((BMLoop*)(f->loopbase->head.next))->v; + v3 = ((BMLoop*)(f->loopbase->head.next->next))->v; + CalcNormFloat(v1->co, v2->co, v3->co, f->no); + } + else if(f->len == 4){ + BMVert *v1, *v2, *v3, *v4; + v1 = f->loopbase->v; + v2 = ((BMLoop*)(f->loopbase->head.next))->v; + v3 = ((BMLoop*)(f->loopbase->head.next->next))->v; + v4 = ((BMLoop*)(f->loopbase->head.prev))->v; + CalcNormFloat4(v1->co, v2->co, v3->co, v4->co, f->no); + } + else{ /*horrible, two sided face!*/ + f->no[0] = 0.0; + f->no[1] = 0.0; + f->no[2] = 1.0; + } + +} + + +/* + * BMESH FLIP NORMAL + * + * Reverses the winding of a faces + * Note that this does *not* update the calculated + * Normal +*/ +void BM_flip_normal(BMesh *bm, BMFace *f) +{ + bmesh_loop_reverse(bm, f); +} + + + +/* + * FIND EAR + * + * Used by tesselator to find + * the next triangle to 'clip off' + * of a polygon while tesselating. + * +*/ + +static BMLoop *find_ear(BMFace *f, float (*verts)[3]) +{ + BMVert *v1, *v2, *v3; + BMLoop *bestear = NULL, *l, *l2; + float angle, bestangle = 180.0f; + int isear; + + l = f->loopbase; + do{ + isear = 1; + + v1 = ((BMLoop*)(l->head.prev))->v; + v2 = l->v; + v3 = ((BMLoop*)(l->head.next))->v; + + if (BM_Edge_Exist(v1, v3)) isear = 0; + + if(isear && convexangle(verts[v1->head.eflag2], verts[v2->head.eflag2], verts[v3->head.eflag2])){ + for(l2 = ((BMLoop*)(l->head.next->next)); l2 != ((BMLoop*)(l->head.prev)); l2 = ((BMLoop*)(l2->head.next)) ){ + if(point_in_triangle(verts[v1->head.eflag2], verts[v2->head.eflag2],verts[v3->head.eflag2], l2->v->co)){ + isear = 0; + break; + } + } + } else isear = 0; + + if(isear){ + angle = VecAngle3(verts[v1->head.eflag2], verts[v2->head.eflag2], verts[v3->head.eflag2]); + if(!bestear || angle < bestangle){ + bestear = l; + bestangle = angle; + } + if(angle < 90.0) + break; + } + l = (BMLoop*)(l->head.next); + } + while(l != f->loopbase); + + return bestear; +} + +/* + * BMESH TRIANGULATE FACE + * + * Triangulates a face using a + * simple 'ear clipping' algorithm + * that tries to favor non-skinny + * triangles (angles less than + * 90 degrees). If the triangulator + * has bits left over (or cannot + * triangulate at all) it uses an + * arbitrary triangulation. + * + * TODO: + * -Modify this to try and find ears that will not create a non-manifold face after conversion back to editmesh + * +*/ +void BM_Triangulate_Face(BMesh *bm, BMFace *f, float (*projectverts)[3], int newedgeflag, int newfaceflag) +{ + int i, done; + BMLoop *l, *newl, *nextloop; + + /*copy vertex coordinates to vertspace array*/ + i = 0; + l = f->loopbase; + do{ + VECCOPY(projectverts[i], l->v->co); + l->v->head.eflag2 = i; /*warning, abuse! never duplicate in tools code! never you hear?*/ /*actually, get rid of this completely, use a new structure for this....*/ + i++; + l = (BMLoop*)(l->head.next); + }while(l != f->loopbase); + + bmesh_update_face_normal(bm, f, projectverts); + + compute_poly_plane(projectverts, i); + poly_rotate_plane(f->no, projectverts, i); + + done = 0; + while(!done && f->len > 3){ + done = 1; + l = find_ear(f, projectverts); + if(l) { + done = 0; + f = bmesh_sfme(bm, f, ((BMLoop*)(l->head.prev))->v, ((BMLoop*)(l->head.next))->v, &newl); + BMO_SetFlag(bm, newl->e, newedgeflag); + BMO_SetFlag(bm, f, newfaceflag); + } + } + + if (f->len > 3){ + l = f->loopbase; + while (l->f->len > 3){ + nextloop = ((BMLoop*)(l->head.next->next)); + bmesh_sfme(bm, l->f, l->v,nextloop->v, &newl); + BMO_SetFlag(bm, newl->e, newedgeflag); + BMO_SetFlag(bm, f, newfaceflag); + l = nextloop; + } + } +} diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h new file mode 100644 index 00000000000..1e5a4958e79 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -0,0 +1,82 @@ +/** + * bmesh_private.h jan 2007 + * + * Private function prototypes for bmesh public API. + * This file is a grab-bag of functions from various + * parts of the bmesh internals. + * + * + * $Id: BKE_bmesh.h,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** 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. 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 LICENSE BLOCK ***** + */ + +#ifndef BMESH_PRIVATE_H +#define BMESH_PRIVATE_H + +#include "bmesh.h" + +/*start/stop edit*/ +void bmesh_begin_edit(struct BMesh *bm); +void bmesh_end_edit(struct BMesh *bm, int flag); + +/*internal selection flushing*/ +void bmesh_selectmode_flush(struct BMesh *bm); + +/*internal filter API*/ +void *bmesh_get_filter_callback(int type); +int bmesh_get_filter_argtype(int type); + +/*system flag access*/ +void bmesh_set_sysflag(struct BMHeader *element, int flag); +void bmesh_clear_sysflag(struct BMHeader *element, int flag); +int bmesh_test_sysflag(struct BMHeader *element, int flag); + +/*NOTE: ensure different parts of the API do not conflict + on using this! sets and gets the API index member + of the current flag layer.*/ +#define bmesh_api_seti(bm, head, i) ((head)->flags[bm->stackdepth-1].pflag = i) +#define bmesh_api_geti(bm, head) ((head)->flags[bm->stackdepth-1].pflag) + +/*Polygon Utilities ? FIXME... where do these each go?*/ +/*newedgeflag sets a flag layer flag, obviously not the header flag.*/ +void BM_Triangulate_Face(BMesh *bm, BMFace *f, float (*projectverts)[3], int newedgeflag, int newfaceflag); +void bmesh_update_face_normal(struct BMesh *bm, struct BMFace *f, float (*projectverts)[3]); +void compute_poly_plane(float (*verts)[3], int nverts); +void poly_rotate_plane(float normal[3], float (*verts)[3], int nverts); +void bmesh_flip_normal(struct BMesh *bm, struct BMFace *f); + +/*Error reporting. Shouldnt be called by tools ever.*/ +void BME_error(void); + +/*include the rest of our private declarations*/ +#include "bmesh_structure.h" +#include "bmesh_operators_private.h" + +#endif diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c new file mode 100644 index 00000000000..55631cfda3b --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -0,0 +1,501 @@ +#include <string.h> + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "BLI_arithb.h" +#include "MTC_vectorops.h" + +/* + * BM_QUERIES.C + * + * This file contains functions for answering common + * Topological and geometric queries about a mesh, such + * as, "What is the angle between these two faces?" or, + * "How many faces are incident upon this vertex?" Tool + * authors should use the functions in this file instead + * of inspecting the mesh structure directly. + * +*/ + +/* + * BMESH COUNT ELEMENT + * + * Return the amount of element of + * type 'type' in a given bmesh. + * + * +*/ + +int BM_Count_Element(BMesh *bm, int type) +{ + if(type == BM_VERT) return bm->totvert; + else if(type == BM_EDGE) return bm->totedge; + else if(type == BM_FACE) return bm->totface; + + return 0; +} + +/* + * BMESH VERT IN EDGE + * + * Returns whether or not a given vertex is + * is part of a given edge. + * +*/ + +int BM_Vert_In_Edge(BMEdge *e, BMVert *v) +{ + return bmesh_vert_in_edge(e, v); +} + +/* + * BMESH VERT IN FACE + * + * Returns whether or not a given vertex is + * is part of a given face. + * +*/ + +int BM_Vert_In_Face(BMFace *f, BMVert *v) +{ + BMLoop *l; + + l = f->loopbase; + do{ + if(l->v == v) return 1; + l = ((BMLoop*)(l->head.next)); + }while(l != f->loopbase); + return 0; +} + +/* + * BMESH VERTS IN FACE + * + * Compares the number of vertices in an array + * that appear in a given face + * +*/ +int BM_Verts_In_Face(BMesh *bm, BMFace *f, BMVert **varr, int len) +{ + BMLoop *curloop = NULL; + int i, count = 0; + + for(i=0; i < len; i++) BMO_SetFlag(bm, varr[i], BM_OVERLAP); + + curloop = f->loopbase; + do{ + if(BMO_TestFlag(bm, curloop->v, BM_OVERLAP)) count++; + curloop = ((BMLoop*)(curloop->head.next)); + } while(curloop = ((BMLoop*)(curloop->head.next))); + + for(i=0; i < len; i++) BMO_ClearFlag(bm, varr[i], BM_OVERLAP); + + return count; +} + +/* + * BMESH EDGE IN FACE + * + * Returns whether or not a given edge is + * is part of a given face. + * +*/ + +int BM_Edge_In_Face(BMFace *f, BMEdge *e) +{ + BMLoop *l; + + l = f->loopbase; + do{ + + if(l->e == e) return 1; + l = ((BMLoop*)(l->head.next)); + }while(l != f->loopbase); + + return 0; +} + +/* + * BMESH VERTS IN EDGE + * + * Returns whether or not two vertices are in + * a given edge + * +*/ + +int BM_Verts_In_Edge(BMVert *v1, BMVert *v2, BMEdge *e) +{ + return bmesh_verts_in_edge(v1,v2,e); +} + +/* + * BMESH GET OTHER EDGEVERT + * + * Given a edge and one of its vertices, returns + * the other vertex. + * +*/ + +BMVert *BM_OtherEdgeVert(BMEdge *e, BMVert *v) +{ + return bmesh_edge_getothervert(e,v); +} + +/** + * BMESH EDGE EXIST + * + * Finds out if two vertices already have an edge + * connecting them. Note that multiple edges may + * exist between any two vertices, and therefore + * This function only returns the first one found. + * + * Returns - + * BMEdge pointer + */ + +BMEdge *BM_Edge_Exist(BMVert *v1, BMVert *v2) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len=0; + + if(v1->edge){ + diskbase = bmesh_disk_getpointer(v1->edge,v1); + len = bmesh_cycle_length(diskbase); + + for(i=0,curedge=v1->edge;i<len;i++,curedge = bmesh_disk_nextedge(curedge,v1)){ + if(bmesh_verts_in_edge(v1,v2,curedge)) return curedge; + } + } + + return NULL; +} + +/* + * BMESH VERT EDGECOUNT + * + * Returns the number of edges around this vertex. + */ + +int BM_Vert_EdgeCount(BMVert *v) +{ + int count = 0; + BMEdge *curedge = NULL; + + if(v->edge){ + curedge = v->edge; + do{ + count++; + curedge = bmesh_disk_nextedge(curedge,v); + }while(curedge !=v->edge); + } + return count; +} + +/** + * BMESH EDGE FACECOUNT + * + * Returns the number of faces around this edge +*/ + +int BM_Edge_FaceCount(BMEdge *e) +{ + int count = 0; + BMLoop *curloop = NULL; + + if(e->loop){ + curloop = e->loop; + do{ + count++; + curloop = bmesh_radial_nextloop(curloop); + }while(curloop != e->loop); + } + + return count; +} + +/** + * BMESH VERT FACECOUNT + * + * Returns the number of faces around this vert +*/ + +int BM_Vert_FaceCount(BMVert *v){ + int count = 0; + BMEdge *curedge = NULL; + + if(v->edge){ + curedge = v->edge; + do{ + if(curedge->loop) count += BM_Edge_FaceCount(curedge); + curedge = bmesh_disk_nextedge(curedge,v); + }while(curedge != v->edge); + } + return count; +} + +/** + * BMESH WIRE VERT + * + * Tests whether or not the vertex is part of a wire edge. + * (ie: has no faces attached to it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_Wire_Vert(BMesh *bm, BMVert *v) +{ + BMEdge *curedge; + + if(!(v->edge)) return 0; + + curedge = v->edge; + do{ + if(curedge->loop) return 0; + curedge = bmesh_disk_nextedge(curedge, v); + }while(curedge != v->edge); + + return 1; +} + +/** + * BMESH WIRE EDGE + * + * Tests whether or not the edge is part of a wire. + * (ie: has no faces attached to it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_Wire_Edge(BMesh *bm, BMEdge *e) +{ + if(e->loop) return 0; + return 1; +} + +/** + * BMESH NONMANIFOLD VERT + * + * A vertex is non-manifold if it meets the following conditions: + * 1: Loose - (has no edges/faces incident upon it) + * 2: Joins two distinct regions - (two pyramids joined at the tip) + * 3: Is part of a non-manifold edge (edge with more than 2 faces) + * 4: Is part of a wire edge + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_Nonmanifold_Vert(BMesh *bm, BMVert *v) { + BMEdge *e, *oe; + BMLoop *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=bmesh_disk_nextedge(e,v)) { + if (e->loop == NULL) { + /* loose edge */ + return 1; + } + + if (bmesh_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 = ((BMLoop*)(l->head.prev)); + else l = ((BMLoop*)(l->head.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; +} + +/** + * BMESH NONMANIFOLD EDGE + * + * Tests whether or not this edge is manifold. + * A manifold edge either has 1 or 2 faces attached + * to it. + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_Nonmanifold_Edge(BMesh *bm, BMEdge *e) +{ + int count = BM_Edge_FaceCount(e); + if(count != 2 && count != 1) return 1; + return 0; +} + +/** + * BMESH BOUNDARY EDGE + * + * Tests whether or not an edge is on the boundary + * of a shell (has one face associated with it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_Boundary_Edge(BMEdge *e) +{ + int count = BM_Edge_FaceCount(e); + if(count == 1) return 1; + return 0; +} + +/** + * BMESH FACE SHAREDEDGES + * + * Counts the number of edges two faces share (if any) + * + * TODO: + * Move this to structure, and wrap. + * + * Returns - + * Integer + */ + +int BM_Face_Sharededges(BMFace *f1, BMFace *f2){ + BMLoop *l; + int count = 0; + + l = f1->loopbase; + do{ + if(bmesh_radial_find_face(l->e,f2)) count++; + l = ((BMLoop*)(l->head.next)); + }while(l != f1->loopbase); + + return count; +} + +/** + * + * BMESH EDGE SHARE FACES + * + * Tests to see if e1 shares any faces with e2 + * +*/ + +int BM_Edge_Share_Faces(BMEdge *e1, BMEdge *e2) +{ + BMLoop *l; + BMFace *f; + + if(e1->loop && e2->loop){ + l = e1->loop; + do{ + f = l->f; + if(bmesh_radial_find_face(e2,f)){ + return 1; + } + l = (BMLoop*)(l->radial.next->data); + }while(l != e1->loop); + } + return 0; +} + + + +/** + * BMESH FACE ANGLE + * + * Calculates the angle between two faces. Assumes + * That face normals are correct. + * + * Returns - + * Float. + */ + +float BM_Face_Angle(BMesh *bm, BMEdge *e) +{ + BMLoop *l1, *l2; + int radlen; + float edge_angle_cos = 0.0; + + radlen = BM_Edge_FaceCount(e); + if(radlen == 2){ + l1 = e->loop; + l2 = e->loop->radial.next->data; + edge_angle_cos = MTC_dot3Float(l1->f->no, l2->f->no); + } + return edge_angle_cos; + +} + +/* + * BMESH EXIST FACE OVERLAPS + * + * Given a set of vertices (varr), find out if + * a face exists with those vertices already, or + * if those vertices overlap an existing face. + * + * Returns: + * 0 for no overlap + * 1 for overlap + * + * +*/ + +int BM_Exist_Face_Overlaps(BMesh *bm, BMVert **varr, int len, BMFace **existface) +{ + BMIter vertfaces; + BMFace *f; + int i, amount; + + *existface = NULL; + + for(i=0; i < len; i++){ + f = BMIter_New(&vertfaces, bm, BM_FACES_OF_VERT, varr[i] ); + while(f){ + amount = BM_Verts_In_Face(bm, f, varr, len); + if(amount >= len){ + if((len == f->len) && existface) + *existface = f; + return 1; + } + f = BMIter_Step(&vertfaces); + } + } + return 0; +} diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c new file mode 100644 index 00000000000..e987bcb70ad --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -0,0 +1,772 @@ +/** + * bmesh_structure.c jan 2007 + * + * Low level routines for manipulating the BM structure. + * + * $Id: bmesh_structure.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** 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. + * 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 LICENSE BLOCK ***** + */ + +#include <limits.h> +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "BKE_utildefines.h" +#include "bmesh.h" +#include "bmesh_private.h" +#include "BLI_blenlib.h" +#include "BLI_linklist.h" +#include "BLI_ghash.h" +/** + * MISC utility functions. + * + */ + +int bmesh_vert_in_edge(BMEdge *e, BMVert *v){ + if(e->v1 == v || e->v2 == v) return 1; + return 0; +} +int bmesh_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e){ + if(e->v1 == v1 && e->v2 == v2) return 1; + else if(e->v1 == v2 && e->v2 == v1) return 1; + return 0; +} + +BMVert *bmesh_edge_getothervert(BMEdge *e, BMVert *v){ + if(e->v1 == v) return e->v2; + else if(e->v2 == v) return e->v1; + return NULL; +} + +int bmesh_edge_swapverts(BMEdge *e, BMVert *orig, BMVert *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 + */ + +BMVert *bmesh_addvertlist(BMesh *bm, BMVert *example){ + BMVert *v=NULL; + v = BLI_mempool_calloc(bm->vpool); + v->head.next = v->head.prev = NULL; + v->head.flag = 0; + v->head.EID = bm->nextv; + v->head.type = BM_VERT; + v->co[0] = v->co[1] = v->co[2] = 0.0f; + v->no[0] = v->no[1] = v->no[2] = 0.0f; + v->edge = NULL; + v->data = NULL; + v->bweight = 0.0f; + BLI_addtail(&(bm->verts), &(v->head)); + bm->nextv++; + bm->totvert++; + + if(example){ + VECCOPY(v->co,example->co); + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, example->data, &v->data); + } + else + CustomData_bmesh_set_default(&bm->vdata, &v->data); + + /*allocate flags*/ + v->head.flags = BLI_mempool_calloc(bm->flagpool); + + return v; +} +BMEdge *bmesh_addedgelist(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge *example){ + BMEdge *e=NULL; + e = BLI_mempool_calloc(bm->epool); + e->head.next = e->head.prev = NULL; + e->head.EID = bm->nexte; + e->head.type = BM_EDGE; + e->head.flag = 0; + e->v1 = v1; + e->v2 = v2; + e->d1.next = e->d1.prev = e->d2.next = e->d2.prev = NULL; + e->d1.data = e; + e->d2.data = e; + e->loop = NULL; + e->data = NULL; + e->crease = e->bweight = 0.0f; + bm->nexte++; + bm->totedge++; + BLI_addtail(&(bm->edges), &(e->head)); + + if(example) + CustomData_bmesh_copy_data(&bm->edata, &bm->edata, example->data, &e->data); + else + CustomData_bmesh_set_default(&bm->edata, &e->data); + + /*allocate flags*/ + e->head.flags = BLI_mempool_calloc(bm->flagpool); + + return e; +} +BMLoop *bmesh_create_loop(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, BMLoop *example){ + BMLoop *l=NULL; + l = BLI_mempool_calloc(bm->lpool); + l->head.next = l->head.prev = NULL; + l->head.EID = bm->nextl; + l->head.type = BM_LOOP; + l->head.flag = 0; + l->radial.next = l->radial.prev = NULL; + l->radial.data = l; + l->v = v; + l->e = e; + l->f = f; + l->data = NULL; + bm->nextl++; + bm->totloop++; + + if(example) + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, example->data, &l->data); + else + CustomData_bmesh_set_default(&bm->ldata, &l->data); + + return l; +} + +BMFace *bmesh_addpolylist(BMesh *bm, BMFace *example){ + BMFace *f = NULL; + f = BLI_mempool_calloc(bm->ppool); + f->head.flag = 0; + f->head.next = f->head.prev = NULL; + f->head.EID = bm->nextp; + f->head.type = BM_FACE; + f->loopbase = NULL; + f->len = 0; + f->data = NULL; + f->mat_nr = 0; + BLI_addtail(&(bm->polys),&(f->head)); + bm->nextp++; + bm->totface++; + + if(example) + CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, example->data, &f->data); + else + CustomData_bmesh_set_default(&bm->pdata, &f->data); + + /*allocate flags*/ + f->head.flags = BLI_mempool_calloc(bm->flagpool); + + 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 bmesh_free_vert(BMesh *bm, BMVert *v){ + bm->totvert--; + CustomData_bmesh_free_block(&bm->vdata, &v->data); + BLI_mempool_free(bm->flagpool, v->head.flags); + BLI_mempool_free(bm->vpool, v); +} +void bmesh_free_edge(BMesh *bm, BMEdge *e){ + bm->totedge--; + CustomData_bmesh_free_block(&bm->edata, &e->data); + BLI_mempool_free(bm->flagpool, e->head.flags); + BLI_mempool_free(bm->epool, e); +} +void bmesh_free_poly(BMesh *bm, BMFace *f){ + bm->totface--; + CustomData_bmesh_free_block(&bm->pdata, &f->data); + BLI_mempool_free(bm->flagpool, f->head.flags); + BLI_mempool_free(bm->ppool, f); +} +void bmesh_free_loop(BMesh *bm, BMLoop *l){ + bm->totloop--; + CustomData_bmesh_free_block(&bm->ldata, &l->data); + BLI_mempool_free(bm->lpool, 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 + * bmesh_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 BM 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 bmesh_Edge contains + * two bmesh_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 bmesh_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: + * + * bmesh_disk_append_edge + * bmesh_disk_remove_edge + * bmesh_disk_nextedge + * bmesh_disk_getpointer + * + * 2: The Radial Cycle - A circle of face edges (bmesh_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 BM does not keep track of region/shell + * information. + * + * Functions relating to this cycle: + * + * bmesh_radial_append + * bmesh_radial_remove_loop + * bmesh_radial_nextloop + * bmesh_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: + * + * bmesh_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 bmesh_cycle_append(void *h, void *nt) +{ + BMNode *oldtail, *head, *newtail; + + head = (BMNode*)h; + newtail = (BMNode*)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; + + } +} + +/** + * bmesh_cycle_length + * + * Count the nodes in a cycle. + * + * Returns - + * Integer + */ + +int bmesh_cycle_length(void *h){ + + int len = 0; + BMNode *head, *curnode; + head = (BMNode*)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; +} + + +/** + * bmesh_cycle_remove + * + * Removes a node from a cycle. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_cycle_remove(void *h, void *remn) +{ + int i, len; + BMNode *head, *remnode, *curnode; + + head = (BMNode*)h; + remnode = (BMNode*)remn; + len = bmesh_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; +} + +/** + * bmesh_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 bmesh_cycle_validate(int len, void *h){ + int i; + BMNode *curnode, *head; + head = (BMNode*)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*/ + +/** + * bmesh_disk_nextedge + * + * Find the next edge in a disk cycle + * + * Returns - + * Pointer to the next edge in the disk cycle for the vertex v. + */ + +BMEdge *bmesh_disk_nextedge(BMEdge *e, BMVert *v) +{ + if(bmesh_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; +} + +/** + * bmesh_disk_getpointer + * + * Given an edge and one of its vertices, find the apporpriate CycleNode + * + * Returns - + * Pointer to bmesh_CycleNode. + */ +BMNode *bmesh_disk_getpointer(BMEdge *e, BMVert *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; +} + +/** + * bmesh_disk_append_edge + * + * Appends edge to the end of a vertex disk cycle. + * + * Returns - + * 1 for success, 0 for failure + */ + +int bmesh_disk_append_edge(BMEdge *e, BMVert *v) +{ + + BMNode *base, *tail; + + if(bmesh_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 = bmesh_disk_getpointer(e, v); + bmesh_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 = bmesh_disk_getpointer(v->edge, v); + tail = bmesh_disk_getpointer(e, v); + bmesh_cycle_append(base, tail); + return 1; +} + +/** + * bmesh_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 bmesh_disk_remove_edge(BMEdge *e, BMVert *v) +{ + BMNode *base, *remnode; + BMEdge *newbase; + int len; + + base = bmesh_disk_getpointer(v->edge, v); + remnode = bmesh_disk_getpointer(e, v); + + /*first deal with v->edge pointer...*/ + len = bmesh_cycle_length(base); + if(len == 1) newbase = NULL; + else if(v->edge == e) newbase = base->next-> data; + else newbase = v->edge; + + /*remove and rebase*/ + bmesh_cycle_remove(base, remnode); + v->edge = newbase; +} + +/** + * bmesh_disk_next_edgeflag + * + * Searches the disk cycle of v, starting with e, for the + * next edge that has either eflag or tflag. + * + * bmesh_Edge pointer. + */ + +BMEdge *bmesh_disk_next_edgeflag(BMEdge *e, BMVert *v, int eflag, int tflag) +{ + + BMNode *diskbase; + BMEdge *curedge; + int len, ok; + + if(eflag && tflag) return NULL; + + ok = bmesh_vert_in_edge(e,v); + if(ok){ + diskbase = bmesh_disk_getpointer(e, v); + len = bmesh_cycle_length(diskbase); + curedge = bmesh_disk_nextedge(e,v); + while(curedge != e){ + if(eflag){ + if(curedge->head.eflag1 == eflag) return curedge; + } + curedge = bmesh_disk_nextedge(curedge, v); + } + } + return NULL; +} + +/** + * bmesh_disk_count_edgeflag + * + * Counts number of edges in this verts disk cycle which have + * either eflag or tflag (but not both!) + * + * Returns - + * Integer. + */ + +int bmesh_disk_count_edgeflag(BMVert *v, int eflag, int tflag) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len=0, count=0; + + if(v->edge){ + if(eflag && tflag) return 0; /*tflag and eflag are reserved for different functions!*/ + diskbase = bmesh_disk_getpointer(v->edge, v); + len = bmesh_cycle_length(diskbase); + + for(i = 0, curedge=v->edge; i<len; i++){ + if(eflag){ + if(curedge->head.eflag1 == eflag) count++; + } + curedge = bmesh_disk_nextedge(curedge, v); + } + } + return count; +} + + +int bmesh_disk_hasedge(BMVert *v, BMEdge *e){ + BMNode *diskbase; + BMEdge *curedge; + int i, len=0; + + if(v->edge){ + diskbase = bmesh_disk_getpointer(v->edge,v); + len = bmesh_cycle_length(diskbase); + + for(i = 0, curedge=v->edge; i<len; i++){ + if(curedge == e) return 1; + else curedge=bmesh_disk_nextedge(curedge, v); + } + } + return 0; +} + +BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2){ + BMNode *diskbase; + BMEdge *curedge; + int i, len=0; + + if(v1->edge){ + diskbase = bmesh_disk_getpointer(v1->edge,v1); + len = bmesh_cycle_length(diskbase); + + for(i=0,curedge=v1->edge;i<len;i++,curedge = bmesh_disk_nextedge(curedge,v1)){ + if(bmesh_verts_in_edge(v1,v2,curedge)) return curedge; + } + } + + return NULL; +} + +/*end disk cycle routines*/ + +BMLoop *bmesh_radial_nextloop(BMLoop *l){ + return (BMLoop*)(l->radial.next->data); +} + +void bmesh_radial_append(BMEdge *e, BMLoop *l){ + if(e->loop == NULL) e->loop = l; + bmesh_cycle_append(&(e->loop->radial), &(l->radial)); +} + +void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e) +{ + BMLoop *newbase; + int len; + + /*deal with edge->loop pointer*/ + len = bmesh_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*/ + bmesh_cycle_remove(&(e->loop->radial), &(l->radial)); + e->loop = newbase; +} + +int bmesh_radial_find_face(BMEdge *e,BMFace *f) +{ + + BMLoop *curloop; + int i, len; + + len = bmesh_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; +} + + +/* + * BME RADIAL COUNT FACE VERT + * + * Returns the number of times a vertex appears + * in a radial cycle + * +*/ + +int bmesh_radial_count_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + int count = 0; + curloop = l; + do{ + if(curloop->v == v) count++; + curloop = bmesh_radial_nextloop(curloop); + }while(curloop != l); + return count; +} + +/* + * BME DISK COUNT FACE VERT + * + * Counts the number of loop users + * for this vertex. Note that this is + * equivalent to counting the number of + * faces incident upon this vertex + * +*/ + +int bmesh_disk_count_facevert(BMVert *v) +{ + BMEdge *curedge; + int count = 0; + + /*is there an edge on this vert at all?*/ + if(!v->edge) + return count; + + /*first, loop around edges*/ + curedge = v->edge; + do{ + if(curedge->loop) count += bmesh_radial_count_facevert(curedge->loop, v); + curedge = bmesh_disk_nextedge(curedge, v); + }while(curedge != v->edge); + + return count; +} + +/* + * BME RADIAL FIND FIRST FACE VERT + * + * Finds the first loop of v around radial + * cycle + * +*/ +BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = l; + do{ + if(curloop->v == v) return curloop; + curloop = bmesh_radial_nextloop(curloop); + }while(curloop != l); + return NULL; +} + +BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = bmesh_radial_nextloop(l); + do{ + if(curloop->v == v) return curloop; + curloop = bmesh_radial_nextloop(curloop); + }while(curloop !=l); + return l; +} + + +/* + * BME FIND FIRST FACE EDGE + * + * Finds the first edge in a vertices + * Disk cycle that has one of this + * vert's loops attached + * to it. + * + * +*/ + +BMEdge *bmesh_disk_find_first_faceedge(BMEdge *e, BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = e; + do{ + if(searchedge->loop && bmesh_radial_count_facevert(searchedge->loop,v)) return searchedge; + searchedge = bmesh_disk_nextedge(searchedge,v); + }while(searchedge != e); + + return NULL; +} + +BMEdge *bmesh_disk_find_next_faceedge(BMEdge *e, BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = bmesh_disk_nextedge(e,v); + do{ + if(searchedge->loop && bmesh_radial_count_facevert(searchedge->loop,v)) return searchedge; + searchedge = bmesh_disk_nextedge(searchedge,v); + }while(searchedge !=e); + return e; +} + + + + + +struct BMLoop *bmesh_loop_find_loop(struct BMFace *f, struct BMVert *v) { + BMLoop *l; + int i, len; + + len = bmesh_cycle_length(f->loopbase); + for (i = 0, l=f->loopbase; i < len; i++, l=((BMLoop*)(l->head.next)) ) { + if (l->v == v) return l; + } + return NULL; +} diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h new file mode 100644 index 00000000000..3135457938e --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -0,0 +1,97 @@ +/** + * bmesh_structure.h jan 2007 + * + * The lowest level of functionality for manipulating bmesh structures. + * None of these functions should ever be exported to the rest of Blender. + * + * ***** 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. 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 LICENSE BLOCK ***** + */ + +#ifndef BM_STRUCTURE_H +#define BM_STRUCTURE_H + +/*ALLOCATION/DEALLOCATION*/ +struct BMVert *bmesh_addvertlist(struct BMesh *bm, struct BMVert *example); +struct BMEdge *bmesh_addedgelist(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMEdge *example); +struct BMFace *bmesh_addpolylist(struct BMesh *bm, struct BMFace *example); +struct BMLoop *bmesh_create_loop(struct BMesh *bm, struct BMVert *v, struct BMEdge *e, struct BMFace *f, struct BMLoop *example); + +void bmesh_free_vert(struct BMesh *bm, struct BMVert *v); +void bmesh_free_edge(struct BMesh *bm, struct BMEdge *e); +void bmesh_free_poly(struct BMesh *bm, struct BMFace *f); +void bmesh_free_loop(struct BMesh *bm, struct BMLoop *l); + +/*DOUBLE CIRCULAR LINKED LIST FUNCTIONS*/ +void bmesh_cycle_append(void *h, void *nt); +int bmesh_cycle_remove(void *h, void *remn); +int bmesh_cycle_validate(int len, void *h); +int bmesh_cycle_length(void *h); + +/*DISK CYCLE MANAGMENT*/ +int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v); +void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v); +struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v); +struct BMNode *bmesh_disk_getpointer(struct BMEdge *e, struct BMVert *v); +int bmesh_disk_count_facevert(struct BMVert *v); +struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v); +struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v); + +/*RADIAL CYCLE MANAGMENT*/ +void bmesh_radial_append(struct BMEdge *e, struct BMLoop *l); +void bmesh_radial_remove_loop(struct BMLoop *l, struct BMEdge *e); +int bmesh_radial_find_face(struct BMEdge *e, struct BMFace *f); +struct BMLoop *bmesh_radial_nextloop(struct BMLoop *l); +int bmesh_radial_count_facevert(struct BMLoop *l, struct BMVert *v); +struct BMLoop *bmesh_radial_find_first_facevert(struct BMLoop *l, struct BMVert *v); +struct BMLoop *bmesh_radial_find_next_facevert(struct BMLoop *l, struct BMVert *v); + +/*EDGE UTILITIES*/ +int bmesh_vert_in_edge(struct BMEdge *e, struct BMVert *v); +int bmesh_verts_in_edge(struct BMVert *v1, struct BMVert *v2, struct BMEdge *e); +int bmesh_edge_swapverts(struct BMEdge *e, struct BMVert *orig, struct BMVert *new); /*relink edge*/ +struct BMVert *bmesh_edge_getothervert(struct BMEdge *e, struct BMVert *v); +int bmesh_disk_hasedge(struct BMVert *v, struct BMEdge *e); +struct BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2); +struct BMEdge *bmesh_disk_next_edgeflag(struct BMEdge *e, struct BMVert *v, int eflag, int tflag); +int bmesh_disk_count_edgeflag(struct BMVert *v, int eflag, int tflag); + +/*EULER API - For modifying structure*/ +struct BMVert *bmesh_mv(struct BMesh *bm, float *vec); +struct BMEdge *bmesh_me(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2); +struct BMFace *bmesh_mf(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMEdge **elist, int len); +int bmesh_kv(struct BMesh *bm, struct BMVert *v); +int bmesh_ke(struct BMesh *bm, struct BMEdge *e); +int bmesh_kf(struct BMesh *bm, struct BMFace *bply); +struct BMVert *bmesh_semv(struct BMesh *bm, struct BMVert *tv, struct BMEdge *e, struct BMEdge **re); +struct BMFace *bmesh_sfme(struct BMesh *bm, struct BMFace *f, struct BMVert *v1, struct BMVert *v2, struct BMLoop **rl); +int bmesh_jekv(struct BMesh *bm, struct BMEdge *ke, struct BMVert *kv); +int bmesh_loop_reverse(struct BMesh *bm, struct BMFace *f); +struct BMFace *bmesh_jfke(struct BMesh *bm, struct BMFace *f1, BMFace *f2, BMEdge *e); + +#endif diff --git a/source/blender/bmesh/intern/bmesh_to_editmesh.c b/source/blender/bmesh/intern/bmesh_to_editmesh.c new file mode 100644 index 00000000000..63a35a99fd4 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_to_editmesh.c @@ -0,0 +1,286 @@ +#include "MEM_guardedalloc.h" +#include "BKE_customdata.h" +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include <string.h> +#include "BKE_utildefines.h" +#include "BKE_mesh.h" +#include "BKE_global.h" +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" + +#include "BLI_editVert.h" +#include "mesh_intern.h" +#include "ED_mesh.h" + +#include "BLI_blenlib.h" +#include "BLI_edgehash.h" + +#include "bmesh.h" + +/* + * BMESH TO EDITMESH + * + * This file contains functions for converting + * from a bmesh to an editmesh + * +*/ + +/* + * LOOPS TO EDITMESH CORNERS + * + * Converts N-Gon loop (face-edge) + * data (UVs, Verts Colors, ect) to + * face corner data. + * +*/ + +static void loops_to_editmesh_corners(BMesh *bm, CustomData *facedata, void *face_block, BMFace *f,int numCol, int numTex){ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i < numTex; i++){ + texface = CustomData_em_get_n(facedata, face_block, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texface->tpage = texpoly->tpage; + texface->flag = texpoly->flag; + texface->transp = texpoly->transp; + texface->mode = texpoly->mode; + texface->tile = texpoly->tile; + texface->unwrap = texpoly->unwrap; + + j = 0; + l = f->loopbase; + do { + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + texface->uv[j][0] = mloopuv->uv[0]; + texface->uv[j][1] = mloopuv->uv[1]; + j++; + l = ((BMLoop*)(l->head.next)); + } while(l!=f->loopbase); + + } + + for(i=0; i < numCol; i++){ + mcol = CustomData_em_get_n(facedata, face_block, CD_MCOL, i); + j = 0; + l = f->loopbase; + do { + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mcol[j].r = mloopcol->r; + mcol[j].g = mloopcol->g; + mcol[j].b = mloopcol->b; + mcol[j].a = mloopcol->a; + j++; + l = ((BMLoop*)(l->head.next)); + } while(l!=f->loopbase); + } +} + +static EditVert *bmeshvert_to_editvert(BMesh *bm, EditMesh *em, BMVert *v, int index, EditVert **evlist) +{ + EditVert *eve = NULL; + + v->head.eflag1 = index; /*abuse!*/ + eve = addvertlist(em, v->co, NULL); + eve->keyindex = index; + evlist[index]= eve; + + /*copy flags*/ + if(v->head.flag & BM_HIDDEN) eve->h = 1; + if (v->head.flag & BM_SELECT) eve->f |= SELECT; + + eve->bweight = v->bweight; + CustomData_em_copy_data(&bm->vdata, &em->vdata, v->data, &eve->data); + /*copy normal*/ + eve->no[0] = v->no[0]; + eve->no[1] = v->no[1]; + eve->no[2] = v->no[2]; + + return eve; +} + +static void bmeshedge_to_editedge_internal(BMesh *bm, EditMesh *em, BMEdge *e, EditEdge *eed) +{ + eed->crease = e->crease; + eed->bweight = e->bweight; + + //copy relavent flags + if (e->head.flag & BM_SELECT) eed->f |= SELECT; + if (e->head.flag & BM_SEAM) eed->seam = 1; + if (e->head.flag & BM_SHARP) eed->sharp = 1; + if (e->head.flag & BM_HIDDEN) eed->h = 1; + if (e->head.flag & BM_FGON) eed->h |= EM_FGON; + + CustomData_em_copy_data(&bm->edata, &em->edata, e->data, &eed->data); +} + +static EditEdge *bmeshedge_to_editedge(BMesh *bm, EditMesh *em, BMEdge *e, EditVert **evlist) +{ + EditEdge *eed = NULL; + + if(!(findedgelist(em, evlist[e->v1->head.eflag1], evlist[e->v2->head.eflag1]))){ + eed= addedgelist(em, evlist[e->v1->head.eflag1], evlist[e->v2->head.eflag1], NULL); + bmeshedge_to_editedge_internal(bm, em, e, eed); + } + + return eed; +} + +static EditFace *bmeshface_to_editface(BMesh *bm, EditMesh *em, BMFace *f, EditVert **evlist, int numCol, int numTex) +{ + EditVert *eve1, *eve2, *eve3, *eve4; + EditFace *efa = NULL; + int len; + + len = f->len; + + eve1= evlist[f->loopbase->v->head.eflag1]; + eve2= evlist[((BMLoop*)(f->loopbase->head.next))->v->head.eflag1]; + eve3= evlist[((BMLoop*)(f->loopbase->head.next->next))->v->head.eflag1]; + if (len == 4) { + eve4= evlist[ ((BMLoop*)(f->loopbase->head.prev))->v->head.eflag1]; + } + else { + eve4= NULL; + } + + efa = addfacelist(em, eve1, eve2, eve3, eve4, NULL, NULL); + + bmeshedge_to_editedge_internal(bm, em, f->loopbase->e, efa->e1); + bmeshedge_to_editedge_internal(bm, em, ((BMLoop*)(f->loopbase->head.next))->e, efa->e2); + bmeshedge_to_editedge_internal(bm, em, ((BMLoop*)(f->loopbase->head.next->next))->e, efa->e3); + if(eve4) + bmeshedge_to_editedge_internal(bm, em, ((BMLoop*)(f->loopbase->head.prev))->e, efa->e4); + + efa->mat_nr = (unsigned char)f->mat_nr; + + + /*Copy normal*/ + efa->n[0] = f->no[0]; + efa->n[1] = f->no[1]; + efa->n[2] = f->no[2]; + + //copy relavent original flags + if (f->head.flag & BM_SELECT) efa->f |= SELECT; + if (f->head.flag & BM_HIDDEN) efa->h = 1; + if (f->head.flag & BM_SMOOTH) efa->flag |= ME_SMOOTH; + + CustomData_em_copy_data(&bm->pdata, &em->fdata, f->data, &efa->data); + loops_to_editmesh_corners(bm, &em->fdata, efa->data, f, numCol,numTex); + + return efa; +} + +EditMesh *bmesh_to_editmesh_intern(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + EditMesh *em; + EditVert *eve, **evlist; + + int totvert, i, numTex, numCol; + + em = MEM_callocN(sizeof(EditMesh), "EditMesh from bmesh"); + + if(bm->selectmode & BM_VERT) em->selectmode |= SCE_SELECT_VERTEX; + if(bm->selectmode & BM_EDGE) em->selectmode |= SCE_SELECT_EDGE; + if(bm->selectmode & BM_FACE) em->selectmode |= SCE_SELECT_FACE; + + CustomData_copy(&bm->vdata, &em->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->edata, &em->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->pdata, &em->fdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_from_bmeshpoly(&em->fdata, &bm->pdata, &bm->ldata,0); + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + totvert = BM_Count_Element(bm, BM_VERT); + evlist= MEM_mallocN(totvert*sizeof(EditVert *),"evlist"); + + /* make vertices */ + for(i=0, v = BMIter_New(&verts, bm, BM_VERTS, bm); v; v = BMIter_Step(&verts), i++) + eve = bmeshvert_to_editvert(bm, em, v, i, evlist); + + /* make edges */ + for(e = BMIter_New(&edges, bm, BM_EDGES, bm); e; e = BMIter_Step(&edges)) + bmeshedge_to_editedge(bm, em, e, evlist); + + /* make faces */ + for(f = BMIter_New(&faces, bm, BM_FACES, bm); f; f = BMIter_Step(&faces)) + bmeshface_to_editface(bm, em, f, evlist, numCol, numTex); + + MEM_freeN(evlist); + + EM_fgon_flags(em); + + return em; +} + +void bmesh2edit_exec(BMesh *bmesh, BMOperator *op) +{ + BMO_Set_Pnt(op, BMOP_TO_EDITMESH_EMOUT, bmesh_to_editmesh_intern(bmesh)); +} + +#define FACE_NGON 1 + +void bmesh_make_fgons_exec(BMesh *bmesh, BMOperator *op) +{ + BMOperator triop; + BMFace *face; + BMIter iter; + BMEdge *edge; + BMOpSlot *eout; + int i; + + BMO_Init_Op(&triop, BMOP_TRIANGULATE); + + /*HACK: I don't know if this'll conflict with other flags at all!*/ + for (face = BMIter_New(&iter, bmesh, BM_FACES, NULL); face; face=BMIter_Step(&iter)) { + if (face->len > 4) { + BMO_SetFlag(bmesh, face, FACE_NGON); + } + } + + BMO_Flag_To_Slot(bmesh, &triop, BMOP_TRIANG_FACEIN, FACE_NGON, BM_FACE); + BMO_Exec_Op(bmesh, &triop); + + eout = BMO_GetSlot(&triop, BMOP_TRIANG_NEW_EDGES); + for (i=0; i<eout->len; i++) { + edge = ((BMEdge**)eout->data.buf)[i]; + edge->head.flag |= BM_FGON; + } + + BMO_Finish_Op(bmesh, &triop); +} + +EditMesh *bmesh_to_editmesh(BMesh *bmesh) +{ + BMOperator conv, makefgon; + EditMesh *em; + + /*first fgon-afy the mesh*/ + BMO_Init_Op(&makefgon, BMOP_MAKE_FGONS); + BMO_Exec_Op(bmesh, &makefgon); + BMO_Finish_Op(bmesh, &makefgon); + + BMO_Init_Op(&conv, BMOP_TO_EDITMESH); + BMO_Exec_Op(bmesh, &conv); + em = conv.slots[BMOP_TO_EDITMESH_EMOUT].data.p; + BMO_Finish_Op(bmesh, &conv); + + return em; +}
\ No newline at end of file diff --git a/source/blender/bmesh/intern/bmesh_walkers.c b/source/blender/bmesh/intern/bmesh_walkers.c new file mode 100644 index 00000000000..6e0717ce7e8 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_walkers.c @@ -0,0 +1,286 @@ +#include <stdio.h> +#include <string.h> +#include "BLI_mempool.h" + +#include "bmesh_private.h" +#include "bmesh_walkers.h" + +#include "bmesh.h" + +/* +NOTE: This code needs to be read through a couple of times!! +*/ + +typedef struct shellWalker{ + struct shellWalker *prev; + BMVert *base; + BMEdge *curedge, *current; +} shellWalker; + +/* + * BMWalker - change this to use the filters functions. + * + * A generic structure for maintaing the state and callbacks nessecary for walking over + * the surface of a mesh. An example of usage: + * + * BMEdge *edge; + * BMWalker *walker = BMWalker_create(BM_SHELLWALKER, BM_SELECT); + * walker->begin(walker, vert); + * for(edge = BMWalker_walk(walker); edge; edge = bmeshWwalker_walk(walker)){ + * bmesh_select_edge(edge); + * } + * BMWalker_free(walker); + * + * The above example creates a walker that walks over the surface a mesh by starting at + * a vertex and traveling across its edges to other vertices, and repeating the process + * over and over again until it has visited each vertex in the shell. An additional restriction + * is passed into the BMWalker_create function stating that we are only interested + * in walking over edges that have been flagged with the bitmask 'BM_SELECT'. + * + * +*/ + +/*Forward declerations*/ +static int request_walkerMask(struct BMesh *bm); +static void *BMWalker_walk(struct BMWalker *walker); +static void BMWalker_popState(struct BMWalker *walker); +static void BMWalker_pushState(struct BMWalker *walker); +static void *shellWalker_begin(struct BMWalker *walker, void *data); +static void *shellWalker_yield(struct BMWalker *walker); +static void shellWalker_step(struct BMWalker *walker); +struct shellWalker; + +/* Pointer hiding*/ +typedef struct bmesh_walkerGeneric{ + struct bmesh_walkerGeneric *prev; +} bmesh_walkerGeneric; + + +/* + * REQUEST_WALKERMASK + * + * Each active walker for a bmesh has its own bitmask + * to use for flagging elements as visited. request_walkerMask + * queries the bmesh passed in and returns the first free + * bitmask. If none are free, it returns 0. The maximum number + * of walkers that can be used for a single bmesh between calls to + * bmesh_edit_begin() and bmesh_edit_end() is defined by the constant + * BM_MAXWALKERS. + * +*/ + +static int request_walkerMask(BMesh *bm) +{ + int i; + for(i=0; i < BM_MAXWALKERS; i++){ + if(!(bm->walkers & (1 << i))){ + bm->walkers |= (1 << i); + return (1 << i); + } + } + return 0; +} + + + + +/* + * BMWalker_CREATE + * + * Allocates and returns a new mesh walker of + * a given type. The elements visited are filtered + * by the bitmask 'searchmask'. + * +*/ + +void BMWalker_init(BMWalker *walker, BMesh *bm, int type, int searchmask) +{ + int visitedmask = request_walkerMask(bm); + int size = 0; + + if(visitedmask){ + memset(walker, 0, sizeof(BMWalker)); + walker->bm = bm; + walker->visitedmask = visitedmask; + walker->restrictflag = searchmask; + switch(type){ + case BM_SHELLWALKER: + walker->begin = shellWalker_begin; + walker->step = shellWalker_step; + walker->yield = shellWalker_yield; + size = sizeof(shellWalker); + break; + //case BM_LOOPWALKER: + // walker->begin = loopwalker_begin; + // walker->step = loopwalker_step; + // walker->yield = loopwalker_yield; + // size = sizeof(loopWalker); + // break; + //case BM_RINGWALKER: + // walker->begin = ringwalker_begin; + // walker->step = ringwalker_step; + // walker->yield = ringwalker_yield; + // size = sizeof(ringWalker); + // break; + default: + break; + } + walker->stack = BLI_mempool_create(size, 100, 100); + walker->currentstate = NULL; + } +} + +/* + * BMWalker_END + * + * Frees a walker's stack. + * +*/ + +void BMWalker_end(BMWalker *walker) +{ + BLI_mempool_destroy(walker->stack); +} + + +/* + * BMWalker_STEP + * +*/ + +void *BMWalker_step(BMWalker *walker) +{ + BMHeader *head; + + while(head = BMWalker_walk(walker)){ + //NOTE: figure this out + //if(bmesh_test_flag(head, walker->restrictflag)) return head; + return head; + } + return NULL; +} + +/* + * BMWalker_WALK + * + * Steps a mesh walker forward by one element + * + * TODO: + * -add searchmask filtering + * +*/ + +static void *BMWalker_walk(BMWalker *walker) +{ + void *current = NULL; + + while(walker->currentstate){ + walker->step(walker); + current = walker->yield(walker); + if(current) return current; + else BMWalker_popState(walker); + + } + return NULL; +} + +/* + * BMWalker_POPSTATE + * + * Pops the current walker state off the stack + * and makes the previous state current + * +*/ + +static void BMWalker_popState(BMWalker *walker) +{ + void *oldstate; + oldstate = walker->currentstate; + walker->currentstate + = ((bmesh_walkerGeneric*)walker->currentstate)->prev; + BLI_mempool_free(walker->stack, oldstate); +} + +/* + * BMWalker_PUSHSTATE + * + * Pushes the current state down the stack and allocates + * a new one. + * +*/ + +static void BMWalker_pushState(BMWalker *walker) +{ + bmesh_walkerGeneric *newstate; + newstate = BLI_mempool_alloc(walker->stack); + newstate->prev = walker->currentstate; + walker->currentstate = newstate; +} + +/* Shell Walker: + * + * Starts at a vertex on the mesh and walks over the 'shell' it belongs + * to via visiting connected edges. + * + * TODO: + * + * Add restriction flag/callback for wire edges. + * +*/ + +static void *shellWalker_begin(BMWalker *walker, void *data){ + BMVert *v = data; + shellWalker *shellWalk = NULL; + BMWalker_pushState(walker); + shellWalk = walker->currentstate; + shellWalk->base = shellWalk->curedge = NULL; + if(v->edge){ + shellWalk->base = v; + shellWalk->curedge = v->edge; + } +} +static void *shellWalker_yield(BMWalker *walker) +{ + shellWalker *shellWalk = walker->currentstate; + return shellWalk->curedge; +} + +static void shellWalker_step(BMWalker *walker) +{ + BMEdge *curedge, *next = NULL; + BMVert *ov = NULL; + int restrictpass = 1; + shellWalker *shellWalk = walker->currentstate; + + if(!(shellWalk->base->head.flag & walker->visitedmask)) + shellWalk->base->head.flag |= walker->visitedmask; + + /*find the next edge whose other vertex has not been visited*/ + curedge = shellWalk->curedge; + do{ + if(!(curedge->head.flag & walker->visitedmask)){ + curedge->head.flag |= walker->visitedmask; + if(walker->restrictflag && (!(curedge->head.flag & walker->restrictflag))) restrictpass = 0; + if(restrictpass){ + ov = BM_OtherEdgeVert(curedge, shellWalk->base); + + /*save current state*/ + shellWalk->curedge = curedge; + /*push a new state onto the stack*/ + BMWalker_pushState(walker); + + /*populate the new state*/ + ((shellWalker*)walker->currentstate)->base = ov; + ((shellWalker*)walker->currentstate)->curedge = curedge; + /*break out of loop*/ + + next = curedge; + break; + } + curedge = bmesh_disk_nextedge(curedge, shellWalk->base); + } + }while(curedge != shellWalk->curedge); + + shellWalk->current = next; + return shellWalk->current; +}
\ No newline at end of file diff --git a/source/blender/bmesh/intern/editmesh_to_bmesh.c b/source/blender/bmesh/intern/editmesh_to_bmesh.c new file mode 100644 index 00000000000..e328f779133 --- /dev/null +++ b/source/blender/bmesh/intern/editmesh_to_bmesh.c @@ -0,0 +1,413 @@ +#include "MEM_guardedalloc.h" +#include "BKE_customdata.h" +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include <string.h> +#include "BKE_utildefines.h" +#include "BKE_mesh.h" +#include "BKE_global.h" +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" + +#include "BLI_editVert.h" +#include "mesh_intern.h" +#include "ED_mesh.h" + +#include "BLI_blenlib.h" +#include "BLI_edgehash.h" + +#include "bmesh.h" + +/* + * EDITMESH TO BMESH.C + * + * This file contains functions + * for converting an editmesh + * into a Bmesh + * +*/ + +/* + * EDITMESH CORNERS TO LOOPS + * + * Converts editmesh face corner data + * (UVs, Vert colors, ect) to N-Gon + * face-edge ('loop') data. + * +*/ + +static void editmesh_corners_to_loops(BMesh *bm, CustomData *facedata, void *face_block, BMFace *f,int numCol, int numTex){ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + BMIter iter; + + for(i=0; i < numTex; i++){ + texface = CustomData_em_get_n(facedata, face_block, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texpoly->tpage = texface->tpage; + texpoly->flag = texface->flag; + texpoly->transp = texface->transp; + texpoly->mode = texface->mode; + texpoly->tile = texface->tile; + texpoly->unwrap = texface->unwrap; + + for (j=0, l=BMIter_New(&iter, bm, BM_LOOPS_OF_FACE, f); l; j++, l=BMIter_Step(&iter)) { + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + mloopuv->uv[0] = texface->uv[j][0]; + mloopuv->uv[1] = texface->uv[j][1]; + } + + } + for(i=0; i < numCol; i++){ + mcol = CustomData_em_get_n(facedata, face_block, CD_MCOL, i); + for (j=0, l=BMIter_New(&iter, bm, BM_LOOPS_OF_FACE, f); l; j++, l=BMIter_Step(&iter)) { + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mloopcol->r = mcol[j].r; + mloopcol->g = mcol[j].g; + mloopcol->b = mcol[j].b; + mloopcol->a = mcol[j].a; + } + } +} + +/* + * EDITVERT TO BMVert + * + * Converts an editvert to + * a BMVert. + * +*/ + +static BMVert *editvert_to_BMVert(BMesh *bm, EditMesh *em, EditVert *eve) +{ + BMVert *v = NULL; + + v = BM_Make_Vert(bm, eve->co, NULL); + + /*transfer flags*/ + v->head.flag = eve->h ? BM_HIDDEN : 0; + if(eve->f & SELECT) BM_Select_Vert(bm, v, 1); + v->bweight = eve->bweight; + + /*Copy Custom Data*/ + CustomData_bmesh_copy_data(&em->vdata, &bm->vdata, eve->data, &v->data); + + return v; +} + +/* + * EDITEDGE TO BMEdge + * + * Converts an editedge to + * a BMEdge + * +*/ + +static void editedge_to_BMEdge_internal(BMesh *bm, EditMesh *em, BMEdge *e, EditEdge *eed) +{ + e->crease = eed->crease; + e->bweight = eed->bweight; + + e->head.flag = eed->f & SELECT ? BM_SELECT : 0; + e->head.flag |= eed->seam ? BM_SEAM : 0; + e->head.flag |= eed->h & 1 ? BM_HIDDEN : 0; + e->head.flag |= eed->h & EM_FGON ? BM_FGON : 0; + e->head.flag |= eed->sharp ? BM_SHARP : 0; + + CustomData_bmesh_copy_data(&em->edata, &bm->edata, eed->data, &e->data); +} + +static BMEdge *editedge_to_BMEdge(BMesh *bm, EditMesh *em, EditEdge *eed) +{ + BMVert *v1 = NULL, *v2 = NULL; + BMEdge *e = NULL; + + v1 = eed->v1->tmp.p; + v2 = eed->v2->tmp.p; + + e = BM_Make_Edge(bm, v1, v2,NULL, 0); + + editedge_to_BMEdge_internal(bm, em, e, eed); + + return e; +} +/* + * EDITFACE TO BMFace + * + * Converts an editface to a BMFace. + * Note that this also convert per-face + * corner data as well. + * +*/ + +static BMFace *editface_to_BMFace(BMesh *bm, EditMesh *em, EditFace *efa, int numCol, int numTex) +{ + BMVert *v1 = NULL, *v2 = NULL; + BMFace *f = NULL; + BMEdge *edar[4]; + int len; + + edar[0] = BM_Make_Edge(bm, efa->v1->tmp.p, efa->v2->tmp.p, NULL, 1); + edar[1] = BM_Make_Edge(bm, efa->v2->tmp.p, efa->v3->tmp.p, NULL, 1); + if(efa->v4){ + edar[2] = BM_Make_Edge(bm, efa->v3->tmp.p, efa->v4->tmp.p, NULL, 1); + edar[3] = BM_Make_Edge(bm, efa->v4->tmp.p, efa->v1->tmp.p, NULL, 1); + }else{ + edar[2] = BM_Make_Edge(bm, efa->v3->tmp.p, efa->v1->tmp.p, NULL, 1); + } + + editedge_to_BMEdge_internal(bm, em, edar[0], efa->e1); + editedge_to_BMEdge_internal(bm, em, edar[1], efa->e2); + editedge_to_BMEdge_internal(bm, em, edar[2], efa->e3); + if(efa->v4) + editedge_to_BMEdge_internal(bm, em, edar[3], efa->e4); + + + if(efa->e1->fgoni) edar[0]->head.flag |= BM_FGON; + if(efa->e2->fgoni) edar[1]->head.flag |= BM_FGON; + if(efa->e3->fgoni) edar[2]->head.flag |= BM_FGON; + if(efa->v4 && efa->e4->fgoni) edar[3]->head.flag |= BM_FGON; + + if(efa->v4) len = 4; + else len = 3; + + /*find v1 and v2*/ + v1 = efa->v1->tmp.p; + v2 = efa->v2->tmp.p; + + f = BM_Make_Ngon(bm, v1, v2, edar, len, 0); + f->head.flag = 0; + f->mat_nr = efa->mat_nr; + if(efa->f & SELECT) BM_Select_Face(bm, f, 1); + if(efa->h) f->head.flag |= BM_HIDDEN; + + CustomData_bmesh_copy_data(&em->fdata, &bm->pdata, efa->data, &f->data); + editmesh_corners_to_loops(bm, &em->fdata, efa->data, f,numCol,numTex); + + return f; +} + +/* + * BMESH FGONCONVERT + * + * This function and its associated structures + * /helpers (fgonsort, sortfgon, fuse_fgon) are + * used to convert f-gons to bmesh n-gons. This + * is accomplished by sorting a list of fgon faces + * such that faces that are part of the same fgon + * are next to each other. These faces are then + * converted as is into bmesh faces and + * fused togather. + * + * Note that currently, there is no support for + * holes in faces in the bmesh structure, so + * f-gons with holes will only partially convert. + * +*/ + +typedef struct fgonsort { + unsigned long x; + struct EditFace *efa; + struct BMFace *f; + int done; +}fgonsort; + +static int sortfgon(const void *v1, const void *v2) +{ + const struct fgonsort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + +static void fuse_fgon(BMesh *bm, BMFace *f) +{ + BMFace *sf; + BMLoop *l; + int done; + + sf = f; + done = 0; + while(!done){ + done = 1; + l = sf->loopbase; + do{ + if(l->e->head.flag & BM_FGON){ + sf = BM_Join_Faces(bm,l->f, ((BMLoop*)l->radial.next->data)->f, l->e, 0,0); + if(sf){ + done = 0; + break; + } else { /*we have to get out of here...*/ + return; + } + } + l = ((BMLoop*)(l->head.next)); + }while(l != sf->loopbase); + } +} + +static BM_fgonconvert(BMesh *bm, EditMesh *em, int numCol, int numTex) +{ + EditFace *efa; + struct fgonsort *sortblock, *sb, *sb1; + int a, b, amount=0; + + /*zero out efa->tmp, we store fgon index here*/ + for(efa = em->faces.first; efa; efa = efa->next){ + efa->tmp.l = 0; + amount++; + } + /*go through and give each editface an fgon index*/ + for(efa = em->faces.first; efa; efa = efa->next){ + if(efa->e1->fgoni) efa->tmp.l = efa->e1->fgoni; + else if(efa->e2->fgoni) efa->tmp.l = efa->e2->fgoni; + else if(efa->e3->fgoni) efa->tmp.l = efa->e3->fgoni; + else if(efa->e4 && efa->e4->fgoni) efa->tmp.l = efa->e4->fgoni; + } + + sb= sortblock= MEM_mallocN(sizeof(fgonsort)* amount,"fgon sort block"); + + for(efa = em->faces.first; efa; efa=efa->next){ + sb->x = efa->tmp.l; + sb->efa = efa; + sb->done = 0; + sb++; + } + + qsort(sortblock, amount, sizeof(fgonsort), sortfgon); + + sb = sortblock; + for(a=0; a<amount; a++, sb++) { + if(sb->x && sb->done == 0){ + /*first pass: add in faces for this fgon*/ + for(b=a, sb1 = sb; b<amount && sb1->x == sb->x; b++, sb1++){ + efa = sb1->efa; + sb1->f = editface_to_BMFace(bm, em, efa, numCol, numTex); + sb1->done = 1; + } + /*fuse fgon*/ + fuse_fgon(bm, sb->f); + } + } + MEM_freeN(sortblock); +} + +/* + * TAG WIRE EDGES + * + * Flags editedges 'f1' member + * if the edge has no faces. + * +*/ + +static void tag_wire_edges(EditMesh *em){ + EditFace *efa; + EditEdge *eed; + for(eed = em->edges.first; eed; eed = eed->next) eed->f1 = 1; + for(efa = em->faces.first; efa; efa = efa->next){ + efa->e1->f1 = 0; + efa->e2->f1 = 0; + efa->e3->f1 = 0; + if(efa->e4) efa->e4->f1 = 0; + } +} + +/* + * EDITMESH TO BMESH + * + * Function to convert an editmesh to a bmesh + * Currently all custom data as well as + * f-gons should be converted correctly. + * +*/ + +BMesh *editmesh_to_bmesh_intern(EditMesh *em, BMesh *bm) { + BMVert *v; + EditVert *eve; + EditEdge *eed; + EditFace *efa; + int allocsize[4] = {512,512,2048,512}, numTex, numCol; + + /*make sure to update FGon flags*/ + EM_fgon_flags(em); + + /*copy custom data layout*/ + CustomData_copy(&em->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&em->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&em->fdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*copy face corner data*/ + CustomData_to_bmeshpoly(&em->fdata, &bm->pdata, &bm->ldata); + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&bm->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&bm->edata, allocsize[1]); + CustomData_bmesh_init_pool(&bm->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&bm->pdata, allocsize[3]); + /*needed later*/ + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + /*copy over selection mode*/ + bm->selectmode = 0; + if(em->selectmode & SCE_SELECT_VERTEX) bm->selectmode |= BM_VERT; + if(em->selectmode & SCE_SELECT_EDGE) bm->selectmode |= BM_EDGE; + if(em->selectmode & SCE_SELECT_FACE) bm->selectmode |= BM_FACE; + + + /*begin editloop*/ + //BM_Begin_Edit(bm); + + /*tag wire edges*/ + tag_wire_edges(em); + + /*add verts*/ + for(eve = em->verts.first; eve; eve = eve->next){ + v = editvert_to_BMVert(bm, em, eve); + eve->tmp.p = v; + } + /*convert f-gons*/ + BM_fgonconvert(bm, em, numCol, numTex); + + /*do quads + triangles*/ + for(efa = em->faces.first; efa; efa = efa->next){ + if(!efa->tmp.l) editface_to_BMFace(bm, em, efa, numCol, numTex); + } + + /*add wire edges*/ + for(eed = em->edges.first; eed; eed = eed->next){ + if(eed->f1) editedge_to_BMEdge(bm, em, eed); + } + //BM_end_edit(bm, BM_CALC_NORM); + return bm; +} + +void edit2bmesh_exec(BMesh *bmesh, BMOperator *op) +{ + editmesh_to_bmesh_intern(op->slots[BMOP_FROM_EDITMESH_EM].data.p, bmesh); +} + +BMesh *editmesh_to_bmesh(EditMesh *em) +{ + BMOperator conv; + BMesh *bm; + int allocsize[4] = {512,512,2048,512}, numTex, numCol; + + /*allocate a bmesh*/ + bm = BM_Make_Mesh(allocsize); + + BMO_Init_Op(&conv, BMOP_FROM_EDITMESH); + BMO_Set_Pnt(&conv, BMOP_FROM_EDITMESH_EM, em); + BMO_Exec_Op(bm, &conv); + BMO_Finish_Op(bm, &conv); + + return bm; +}
\ No newline at end of file diff --git a/source/blender/bmesh/intern/in-progress/BME_conversions.c b/source/blender/bmesh/intern/in-progress/BME_conversions.c new file mode 100644 index 00000000000..d39bf689e76 --- /dev/null +++ b/source/blender/bmesh/intern/in-progress/BME_conversions.c @@ -0,0 +1,478 @@ +/** + * BME_conversions.c August 2008 + * + * BM to Derived Mesh conversion functions. + * + * ***** 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. + * 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 LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" +#include "BKE_customdata.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 "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 "bmesh_private.h" + + + +/* + * BMESH DERIVED MESH CONVERSION FUNCTIONS + * + * The functions in this file provides + * methods for converting to and from + * a bmesh. + * +*/ + + +/* + * DMCORNERS TO LOOPS + * + * Function to convert derived mesh per-face + * corner data (uvs, vertex colors), to n-gon + * per-loop data. + * +*/ + +static void DMcorners_to_loops(BMMesh *bm, CustomData *facedata, int index, BMFace *f, int numCol, int numTex){ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i< numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texpoly->tpage = texface[index].tpage; + texpoly->flag = texface[index].flag; + texpoly->transp = texface[index].transp; + texpoly->mode = texface[index].mode; + texpoly->tile = texface[index].tile; + texpoly->unwrap = texface[index].unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + mloopuv->uv[0] = texface[index].uv[j][0]; + mloopuv->uv[1] = texface[index].uv[j][1]; + j++; + l = l->next; + }while(l!=f->loopbase); + } + + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata, CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mloopcol->r = mcol[(index*4)+j].r; + mloopcol->g = mcol[(index*4)+j].g; + mloopcol->b = mcol[(index*4)+j].b; + mloopcol->a = mcol[(index*4)+j].a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +/* + * LOOPS TO DMCORNERS + * + * Function to convert n-gon per-loop data + * (uvs, vertex colors, ect)to derived mesh + * face corner data. + * +*/ + +static void loops_to_DMcorners(BMMesh *bm, CustomData *facedata, int index, BMFace *f,int numCol, int numTex){ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i < numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texface[index].tpage = texpoly->tpage; + texface[index].flag = texpoly->flag; + texface[index].transp = texpoly->transp; + texface[index].mode = texpoly->mode; + texface[index].tile = texpoly->tile; + texface[index].unwrap = texpoly->unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + texface[index].uv[j][0] = mloopuv->uv[0]; + texface[index].uv[j][1] = mloopuv->uv[1]; + j++; + l = l->next; + }while(l!=f->loopbase); + + } + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata,CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mcol[(index*4) + j].r = mloopcol->r; + mcol[(index*4) + j].g = mloopcol->g; + mcol[(index*4) + j].b = mloopcol->b; + mcol[(index*4) + j].a = mloopcol->a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +/* + * MVERT TO BMESHVERT + * + * Converts a MVert to a BMVert + * +*/ +static BMVert *mvert_to_bmeshvert(BMMesh *bm, BMVert **vert_array, int index, MVert *mv, CustomData *data) +{ + BMVert *v = NULL; + + v = bmesh_make_vert(bm, mv->co, NULL); + vert_array[index] = v; + if(mv->flag & SELECT) bmesh_set_flag(v, BMESH_SELECT); + v->bweight = mv->bweight/255.0f; + CustomData_to_bmesh_block(data, &bm->vdata, index, &v->data); + + return v; +} + +/* + * MEDGE TO BMESHEDGE + * + * Converts a MEdge to a BMEdge + * +*/ + +static BMEdge *medge_to_bmeshedge(BMMesh *bm, BMVert **vert_array, int index, MEdge *me, CustomData *data, Edge_Hash *edge_hash) +{ + BMVert *v1, *v2; + BMEdge *e = NULL; + + v1 = vert_array[me->v1]; + v2 = vert_array[me->v2]; + e = bmesh_make_edge(bm, v1, v2, NULL, 0); + e->crease = me->crease/255.0f; + e->bweight = me->bweight/255.0f; + if(me->flag & 1) bmesh_set_flag(e, BMESH_SELECT); + if(me->flag & ME_SEAM) bmesh_set_flag(e, BMESH_SEAM); + BLI_edgehash_insert(edge_hash,me->v1,me->v2,e); + CustomData_to_bmesh_block(data, &bm->edata, index, &e->data); + + return e; +} + +/* + * MFACE TO BMESHFACE + * + * Converts a MFace to a BMFace. + * Note that this will fail on eekadoodle + * faces. + * +*/ + +static BMFace *mface_to_bmeshface(BMMesh *bm, BMVert **vert_array, int index, MFace *mf, CustomData *data, Edge_Hash *edge_hash) +{ + BMVert *v1, *v2; + BMEdge *edar[4]; + BMFace *f = NULL; + int len; + + 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 = bmesh_make_ngon(bm, v1, v2, edar, len, 0); + f->mat_nr = mf->mat_nr; + if(mf->flag & 1) bmesh_set_flag(f, BMESH_SELECT); + if(mf->flag & ME_HIDE) bmesh_set_flag(f, BMESH_HIDDEN); + CustomData_to_bmesh_block(data, &bm->pdata, index, &f->data); + + return f; +} + +/* + * DERIVEDMESH TO BMESH + * + * Converts a derived mesh to a bmesh. + * +*/ + +BMMesh *derivedmesh_to_bmesh(DerivedMesh *dm) +{ + + BMMesh *bm; + BMVert **vert_array; + BMFace *f=NULL; + + MVert *mvert, *mv; + MEdge *medge, *me; + MFace *mface, *mf; + + int totface,totedge,totvert,i,len, numTex, numCol; + int allocsize[4] = {512,512,2048,512}; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + /*allocate a new bmesh*/ + bm = bmesh_make_mesh(allocsize); + + /*copy custom data layout*/ + CustomData_copy(&dm->vertData, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->edgeData, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->faceData, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*copy face corner data*/ + CustomData_to_bmeshpoly(&dm->faceData, &bm->pdata, &bm->ldata); + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&bm->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&bm->edata, allocsize[1]); + CustomData_bmesh_init_pool(&bm->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&bm->pdata, allocsize[3]); + + /*needed later*/ + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + 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(BMVert *)* totvert,"derivedmesh to bmesh vertex pointer array"); + + bmesh_begin_edit(bm); + /*add verts*/ + for(i=0, mv = mvert; i < totvert; i++, mv++) + mvert_to_bmeshvert(bm, vert_array, i, mv, &dm->vertData); + + /*add edges*/ + for(i=0, me = medge; i < totedge; i++, me++) + medge_to_bmeshedge(bm, vert_array, i, me, &dm->edgeData, edge_hash); + + /*add faces.*/ + for(i=0, mf = mface; i < totface; i++, mf++){ + f = mface_to_bmeshface(bm, vert_array, mf, &dm->faceData, edge_hash); + if(f) DMcorners_to_loops(bm, &dm->faceData, i, f, numCol, numTex); + } + + bmesh_end__edit(bm); + BLI_edgehash_free(edge_hash, NULL); + MEM_freeN(vert_array); + return bm; +} + +static void bmeshvert_to_mvert(BMMesh *bm, BMVert *v, MVert *mv, int index, CustomData *data) +{ + VECCOPY(mv->co,v->co); + if(bmesh_test_flag(v, BMESH_SELECT)) mv->flag |= 1; + if(bmesh_test_flag(v, BMESH_HIDDEN)) mv->flag |= ME_HIDE; + mv->bweight = (char)(255.0*v1->bweight); + CustomData_from_bmesh_block(&bm->vdata, data, &v1->data, index); +} + +static int bmeshedge_to_medge(BMMesh *bm, BMEdge *e, MEdge *me, int index, CustomData *data) +{ + if(e->head.eflag2){ + if(e->v1->head.eflag1 < e->v2->head.eflag1){ + me->v1 = e->v1->head.eflag1; + me->v2 = e->v2->head.eflag1; + } + else{ + me->v1 = e->v2->head.eflag1; + me->v2 = e->v1->eflag1; + } + + me->crease = (char)(255.0*e->crease); + me->bweight = (char)(255.0*e->bweight); + if(bmesh_test_flag(e, BMESH_SELECT)) me->flag |= 1; + if(bmesh_test_flag(e, BMESH_HIDDEN)) me->flag |= ME_HIDE; + CustomData_from_bmesh_block(&bm->edata, data, &e->data, index); + return 1; + } + return 0; +} + +static int bmeshface_to_mface(BMMesh *bm, BMFace *f, MFace *mf, int index, CustomData *data) +{ + if(f->len==3 || f->len==4){ + mf->v1 = f->loopbase->v->head.eflag1; + mf->v2 = f->loopbase->next->v->head.eflag1; + mf->v3 = f->loopbase->next->next->v->head.eflag1; + if(len == 4){ + mf->v4 = f->loopbase->prev->v->head.eflag1; + } + /* test and rotate indexes if necessary so that verts 3 and 4 aren't index 0 */ + if(mf->v3 == 0 || (f->len == 4 && mf->v4 == 0)){ + test_index_face(mf, NULL, index, f->len); + } + mf->mat_nr = (unsigned char)f->mat_nr; + if(bmesh_test_flag(f, BMESH_SELECT)) mf->flag |= 1; + if(bmesh_test_flag(f, BMESH_HIDDEN)) mf->flag |= ME_HIDE; + CustomData_from_bmesh_block(&bm->pdata, data, &f->data, index); + return 1; + } + return 0; +} + +/* + * BMESH TO DERIVEDMESH + * + * Converts a bmesh to a derived mesh. + * +*/ + +DerivedMesh *bmesh_to_derivedmesh(BMMesh *bm, DerivedMesh *dm) +{ + MFace *mface = NULL, *mf = NULL; + MEdge *medge = NULL, *me = NULL; + MVert *mvert = NULL, *mv = NULL; + DerivedMesh *result = NULL; + + BMVert *v=NULL; + BMEdge *e=NULL, *oe=NULL; + BMFace *f=NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + + int totface = 0,totedge = 0,totvert = 0,i = 0, numTex, numCol; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + /*get element counts*/ + totvert = bmesh_count_element(bm, BMESH_VERT); + + /*store element indices. Note that the abuse of eflag here should NOT be duplicated!*/ + for(i=0, v = bmeshIterator_init(verts, BM_VERTS, bm, 0); v; v = bmeshIterator_step(verts), i++) + v->head.eflag1 = i; + + /*we cannot have double edges in a derived mesh!*/ + for(e = bmeshIterator_init(edges, BM_EDGES, bm, 0); e; e = bmeshIterator_step(edges)){ + oe = BLI_edgehash_lookup(edge_hash,e->v1->head.eflag1, e->v2->head.eflag1); + if(!oe){ + totedge++; + BLI_edgehash_insert(edge_hash,e->v1->head.eflag1,e->v2->head.eflag1,e); + e->head.eflag2 = 1; + } + else{ + e->head.eflag2 = 0; + } + } + + /*count quads and tris*/ + for(f = bmeshIterator_init(faces, BM_FACES, bm, 0); f; f = bmeshIterator_step(faces)){ + if(f->len == 3 || f->len == 4) totface++; + } + + /*Allocate derivedmesh and copy custom data*/ + result = CDDM_from_template(dm,totvert,totedge,totface); + CustomData_merge(&bm->vdata, &result->vertData, CD_MASK_BMESH, CD_CALLOC, totvert); + CustomData_merge(&bm->edata, &result->edgeData, CD_MASK_BMESH, CD_CALLOC, totedge); + CustomData_merge(&bm->pdata, &result->faceData, CD_MASK_BMESH, CD_CALLOC, totface); + CustomData_from_bmeshpoly(&result->faceData, &bm->pdata, &bm->ldata,totface); + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + /*Make Verts*/ + mvert = CDDM_get_verts(result); + for(i = 0, v = bmeshIterator_init(verts, BM_VERTS, bm, 0); v; v = bmeshIterator_step(verts), i++, mv++){ + bmeshvert_to_mvert(bm,v,mv,i,&result->vertData); + } + + /*Make Edges*/ + medge = CDDM_get_edges(result); + i=0; + for(e = bmeshIterator_init(edges, BM_EDGES, bm, 0); e; e = bmeshIterator_step(edges)){ + me = &medge[i]; + if(bmeshedge_to_medge(bm, e, me, i, &result->edgeData){ + me++; + i++; + } + } + /*Make Faces*/ + if(totface){ + mface = CDDM_get_faces(result); + i=0; + for(f = bmeshIterator_init(faces, BM_FACES, bm, 0); f; f = bmeshIterator_step(faces)){ + mf = &mface[i]; + if(bmeshface_to_mface(bm, f, mf, i, &result->faceData)){ + loops_to_DMcorners(bm, &result->faceData, i, f, numCol, numTex); + i++; + } + } + } + BLI_edgehash_free(edge_hash, NULL); + return result; +} diff --git a/source/blender/bmesh/operators/bmesh_dupeops.c b/source/blender/bmesh/operators/bmesh_dupeops.c new file mode 100644 index 00000000000..2746ed2acdd --- /dev/null +++ b/source/blender/bmesh/operators/bmesh_dupeops.c @@ -0,0 +1,475 @@ +#include "MEM_guardedalloc.h" + +#include "BKE_utildefines.h" + +#include "BLI_ghash.h" +#include "BLI_memarena.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "bmesh.h" +#include "bmesh_operators_private.h" + +/*local flag defines*/ +#define DUPE_INPUT 1 /*input from operator*/ +#define DUPE_NEW 2 +#define DUPE_DONE 4 + +/* + * COPY VERTEX + * + * Copy an existing vertex from one bmesh to another. + * +*/ +static BMVert *copy_vertex(BMesh *source_mesh, BMVert *source_vertex, BMesh *target_mesh, GHash *vhash) +{ + BMVert *target_vertex = NULL; + + /*Create a new vertex*/ + target_vertex = BM_Make_Vert(target_mesh, source_vertex->co, NULL); + + /*Insert new vertex into the vert hash*/ + BLI_ghash_insert(vhash, source_vertex, target_vertex); + + /*Copy attributes*/ + BM_Copy_Attributes(source_mesh, target_mesh, source_vertex, target_vertex); + + /*Set internal op flags*/ + BMO_SetFlag(target_mesh, (BMHeader*)target_vertex, DUPE_NEW); + + return target_vertex; +} + +/* + * COPY EDGE + * + * Copy an existing edge from one bmesh to another. + * +*/ +static BMEdge *copy_edge(BMesh *source_mesh, BMEdge *source_edge, BMesh *target_mesh, GHash *vhash, GHash *ehash) +{ + BMEdge *target_edge = NULL; + BMVert *target_vert1, *target_vert2; + + /*Lookup v1 and v2*/ + target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1); + target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2); + + /*Create a new edge*/ + target_edge = BM_Make_Edge(target_mesh, target_vert1, target_vert2, NULL, 0); + + /*Insert new edge into the edge hash*/ + BLI_ghash_insert(ehash, source_edge, target_edge); + + /*Copy attributes*/ + BM_Copy_Attributes(source_mesh, target_mesh, source_edge, target_edge); + + /*Set internal op flags*/ + BMO_SetFlag(target_mesh, (BMHeader*)target_edge, DUPE_NEW); + + return target_edge; +} + +/* + * COPY FACE + * + * Copy an existing face from one bmesh to another. + * +*/ +static BMFace *copy_face(BMesh *source_mesh, BMFace *source_face, BMesh *target_mesh, BMEdge **edar, GHash *vhash, GHash *ehash) +{ + BMVert *target_vert1, *target_vert2; + BMLoop *source_loop, *target_loop; + BMFace *target_face = NULL; + BMIter iter, iter2; + int i; + + /*lookup the first and second verts*/ + target_vert1 = BLI_ghash_lookup(vhash, BMIter_New(&iter, source_mesh, BM_VERTS_OF_FACE, source_face)); + target_vert2 = BLI_ghash_lookup(vhash, BMIter_Step(&iter)); + + /*lookup edges*/ + for (i=0,source_loop=BMIter_New(&iter, source_mesh, BM_LOOPS_OF_FACE, source_face); + source_loop; source_loop=BMIter_Step(&iter), i++) { + edar[i] = BLI_ghash_lookup(ehash, source_loop->e); + } + + /*create new face*/ + target_face = BM_Make_Ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0); + + BM_Copy_Attributes(source_mesh, target_mesh, source_face, target_face); + + /*mark the face for output*/ + BMO_SetFlag(target_mesh, (BMHeader*)target_face, DUPE_NEW); + + /*copy per-loop custom data*/ + for (i=0,source_loop=BMIter_New(&iter, source_mesh, BM_LOOPS_OF_FACE, source_face), + target_loop=BMIter_New(&iter2, target_mesh, BM_LOOPS_OF_FACE, target_face); + source_loop && target_loop; source_loop=BMIter_Step(&iter), target_loop=BMIter_Step(&iter2), + i++) { + BM_Copy_Attributes(source_mesh, target_mesh, source_loop, target_loop); + } + + + return target_face; +} + /* + * COPY MESH + * + * Internal Copy function. +*/ + +static void copy_mesh(BMesh *source, BMesh *target) +{ + + BMVert *v = NULL; + BMEdge *e = NULL, **edar = NULL; + BMLoop *l = NULL; + BMFace *f = NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + + GHash *vhash; + GHash *ehash; + + int maxlength = 0; + + /*initialize pointer hashes*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + ehash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + /*initialize edge pointer array*/ + for(f = BMIter_New(&faces, source, BM_FACES, source); f; f = BMIter_Step(&faces)){ + if(f->len > maxlength) maxlength = f->len; + } + edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array"); + + /*first we dupe all flagged faces and their elements from source*/ + for(f = BMIter_New(&faces, source, BM_FACES, source); f; f= BMIter_Step(&faces)){ + if(BMO_TestFlag(source, (BMHeader*)f, DUPE_INPUT)){ + /*vertex pass*/ + for(v = BMIter_New(&verts, source, BM_VERTS_OF_FACE, f); v; v = BMIter_Step(&verts)){ + if(!BMO_TestFlag(source, (BMHeader*)v, DUPE_DONE)){ + copy_vertex(source,v, target, vhash); + BMO_SetFlag(source, (BMHeader*)v, DUPE_DONE); + } + } + + /*edge pass*/ + for(e = BMIter_New(&edges, source, BM_EDGES_OF_FACE, f); e; e = BMIter_Step(&edges)){ + if(!BMO_TestFlag(source, (BMHeader*)e, DUPE_DONE)){ + copy_edge(source, e, target, vhash, ehash); + BMO_SetFlag(source, (BMHeader*)e, DUPE_DONE); + } + } + copy_face(source, f, target, edar, vhash, ehash); + BMO_SetFlag(source, (BMHeader*)f, DUPE_DONE); + } + } + + /*now we dupe all the edges*/ + for(e = BMIter_New(&edges, source, BM_EDGES, source); e; e = BMIter_Step(&edges)){ + if(BMO_TestFlag(source, (BMHeader*)e, DUPE_INPUT) && (!BMO_TestFlag(source, (BMHeader*)e, DUPE_DONE))){ + /*make sure that verts are copied*/ + if(!BMO_TestFlag(source, (BMHeader*)e->v1, DUPE_DONE)) { + copy_vertex(source, e->v1, target, vhash); + BMO_SetFlag(source, (BMHeader*)e->v1, DUPE_DONE); + } + if(!BMO_TestFlag(source, (BMHeader*)e->v2, DUPE_DONE)) { + copy_vertex(source, e->v2, target, vhash); + BMO_SetFlag(source, (BMHeader*)e->v2, DUPE_DONE); + } + /*now copy the actual edge*/ + copy_edge(source, e, target, vhash, ehash); + BMO_SetFlag(source, (BMHeader*)e, DUPE_DONE); + } + } + + /*finally dupe all loose vertices*/ + for(v = BMIter_New(&verts, source, BM_VERTS, source); v; v = BMIter_Step(&verts)){ + if(BMO_TestFlag(source, (BMHeader*)v, DUPE_INPUT) && (!BMO_TestFlag(source, (BMHeader*)v, DUPE_DONE))){ + copy_vertex(source, v, target, vhash); + BMO_SetFlag(source, (BMHeader*)v, DUPE_DONE); + } + } + + /*free pointer hashes*/ + BLI_ghash_free(vhash, NULL, NULL); + BLI_ghash_free(ehash, NULL, NULL); + + /*free edge pointer array*/ + if(edar) + MEM_freeN(edar); +} + +/* +BMesh *bmesh_make_mesh_from_mesh(BMesh *bm, int allocsize[4]) +{ + BMesh *target = NULL; + target = bmesh_make_mesh(allocsize); + + + CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + + CustomData_bmesh_init_pool(&target->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&target->edata, allocsize[1]); + CustomData_bmesh_init_pool(&target->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&target->pdata, allocsize[3]); + + bmesh_begin_edit(bm); + bmesh_begin_edit(target); + + bmesh_copy_mesh(bm, target, 0); + + bmesh_end_edit(bm); + bmesh_end_edit(target); + + return target; + +} +*/ + +/* + * Duplicate Operator + * + * Duplicates verts, edges and faces of a mesh. + * + * INPUT SLOTS: + * + * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be duplicated + * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be duplicated + * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be duplicated + * + * OUTPUT SLOTS: + * + * BMOP_DUPE_VORIGINAL: Buffer containing pointers to the original mesh vertices + * BMOP_DUPE_EORIGINAL: Buffer containing pointers to the original mesh edges + * BMOP_DUPE_FORIGINAL: Buffer containing pointers to the original mesh faces + * BMOP_DUPE_VNEW: Buffer containing pointers to the new mesh vertices + * BMOP_DUPE_ENEW: Buffer containing pointers to the new mesh edges + * BMOP_DUPE_FNEW: Buffer containing pointers to the new mesh faces + * +*/ + +void dupeop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator *dupeop = op; + + /*flag input*/ + BMO_Flag_Buffer(bm, dupeop, BMOP_DUPE_MULTIN, DUPE_INPUT); + /*use the internal copy function*/ + copy_mesh(bm, bm); + + /*Output*/ + /*First copy the input buffers to output buffers - original data*/ + BMO_CopySlot(dupeop, dupeop, BMOP_DUPE_MULTIN, BMOP_DUPE_ORIG); + + /*Now alloc the new output buffers*/ + BMO_Flag_To_Slot(bm, dupeop, BMOP_DUPE_NEW, DUPE_NEW, BM_VERT|BM_EDGE|BM_FACE); +} + +/*executes the duplicate operation, feeding elements of + type flag etypeflag and header flag flag to it. note, + to get more useful information (such as the mapping from + original to new elements) you should run the dupe op manually.*/ +void BMOP_DupeFromFlag(BMesh *bm, int etypeflag, int flag) +{ + BMOperator dupeop; + + BMO_Init_Op(&dupeop, BMOP_DUPE); + BMO_HeaderFlag_To_Slot(bm, &dupeop, BMOP_DUPE_MULTIN, flag, etypeflag); + + BMO_Exec_Op(bm, &dupeop); + BMO_Finish_Op(bm, &dupeop); +} + +/* + * Split Operator + * + * Duplicates verts, edges and faces of a mesh but also deletes the originals. + * + * INPUT SLOTS: + * + * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be split + * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be split + * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be split + * + * OUTPUT SLOTS: + * + * BMOP_DUPE_VOUTPUT: Buffer containing pointers to the split mesh vertices + * BMOP_DUPE_EOUTPUT: Buffer containing pointers to the split mesh edges + * BMOP_DUPE_FOUTPUT: Buffer containing pointers to the split mesh faces + * +*/ + +void splitop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator *splitop = op; + BMOperator dupeop; + BMOperator delop; + + /*initialize our sub-operators*/ + BMO_Init_Op(&dupeop, BMOP_DUPE); + BMO_Init_Op(&delop, BMOP_DEL); + + BMO_Set_Int(&delop, BMOP_DEL_CONTEXT, DEL_FACES); + + BMO_CopySlot(splitop, &dupeop, BMOP_SPLIT_MULTIN, BMOP_DUPE_MULTIN); + BMO_Exec_Op(bm, &dupeop); + + /*connect outputs of dupe to delete*/ + BMO_CopySlot(&dupeop, &delop, BMOP_DUPE_ORIG, BMOP_DEL_MULTIN); + BMO_Exec_Op(bm, &delop); + + /*now we make our outputs by copying the dupe outputs*/ + BMO_CopySlot(&dupeop, splitop, BMOP_DUPE_NEW, BMOP_SPLIT_MULTOUT); + + /*cleanup*/ + BMO_Finish_Op(bm, &delop); + BMO_Finish_Op(bm, &dupeop); +} + +#define DEL_INPUT 1 +#define DEL_WIREVERT 2 + +static void delete_verts(BMesh *bm); +static void delete_context(BMesh *bm, int type); + +void delop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator *delop = op; + + /*Mark Buffers*/ + BMO_Flag_Buffer(bm, delop, BMOP_DEL_MULTIN, DEL_INPUT); + + delete_context(bm, op->slots[BMOP_DEL_CONTEXT].data.i); +} + +static void delete_verts(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMLoop *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + for(v = BMIter_New(&verts, bm, BM_VERTS, bm); v; v = BMIter_Step(&verts)){ + if(BMO_TestFlag(bm, (BMHeader*)v, DEL_INPUT)) { + /*Visit edges*/ + for(e = BMIter_New(&edges, bm, BM_EDGES_OF_VERT, v); e; e = BMIter_Step(&edges)) + BMO_SetFlag(bm, (BMHeader*)e, DEL_INPUT); + /*Visit faces*/ + for(f = BMIter_New(&faces, bm, BM_FACES_OF_VERT, v); f; f = BMIter_Step(&faces)) + BMO_SetFlag(bm, (BMHeader*)f, DEL_INPUT); + } + } + + BM_remove_tagged_faces(bm, DEL_INPUT); + BM_remove_tagged_edges(bm, DEL_INPUT); + BM_remove_tagged_verts(bm, DEL_INPUT); +} + +static void delete_edges(BMesh *bm){ + BMEdge *e; + BMFace *f; + + BMIter edges; + BMIter faces; + + for(e = BMIter_New(&edges, bm, BM_EDGES, bm); e; e = BMIter_Step(&edges)){ + if(BMO_TestFlag(bm, (BMHeader*)e, DEL_INPUT)) { + for(f = BMIter_New(&faces, bm, BM_FACES_OF_EDGE, e); f; f = BMIter_Step(&faces)){ + BMO_SetFlag(bm, (BMHeader*)f, DEL_INPUT); + } + } + } + BM_remove_tagged_faces(bm, DEL_INPUT); + BM_remove_tagged_edges(bm, DEL_INPUT); +} + +/*you need to make remove tagged verts/edges/faces + api functions that take a filter callback..... + and this new filter type will be for opstack flags. + This is because the BM_remove_taggedXXX functions bypass iterator API. + -Ops dont care about 'UI' considerations like selection state, hide state, ect. If you want to work on unhidden selections for instance, + copy output from a 'select context' operator to another operator.... +*/ + +/*Break this into smaller functions*/ + +static void delete_context(BMesh *bm, int type){ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + if(type == DEL_VERTS) delete_verts(bm); + else if(type == DEL_EDGES){ + /*flush down to verts*/ + for(e = BMIter_New(&edges, bm, BM_EDGES, bm); e; e = BMIter_Step(&edges)){ + if(BMO_TestFlag(bm, (BMHeader*)e, DEL_INPUT)){ + BMO_SetFlag(bm, (BMHeader*)(e->v1), DEL_INPUT); + BMO_SetFlag(bm, (BMHeader*)(e->v2), DEL_INPUT); + } + } + delete_edges(bm); + /*remove loose vertices*/ + for(v = BMIter_New(&verts, bm, BM_VERTS, bm); v; v = BMIter_Step(&verts)){ + if(BMO_TestFlag(bm, (BMHeader*)v, DEL_INPUT) && (!(v->edge))) + BMO_SetFlag(bm, (BMHeader*)v, DEL_WIREVERT); + } + BM_remove_tagged_verts(bm, DEL_WIREVERT); + } + else if(type == DEL_EDGESFACES) delete_edges(bm); + else if(type == DEL_ONLYFACES) BM_remove_tagged_faces(bm, DEL_INPUT); + else if(type == DEL_FACES){ + /*go through and mark all edges and all verts of all faces for delete*/ + for(f = BMIter_New(&faces, bm, BM_FACES, bm); f; f = BMIter_Step(&faces)){ + if(BMO_TestFlag(bm, (BMHeader*)f, DEL_INPUT)){ + for(e = BMIter_New(&edges, bm, BM_EDGES_OF_FACE, f); e; e = BMIter_Step(&edges)) + BMO_SetFlag(bm, (BMHeader*)e, DEL_INPUT); + for(v = BMIter_New(&verts, bm, BM_VERTS_OF_FACE, f); v; v = BMIter_Step(&verts)) + BMO_SetFlag(bm, (BMHeader*)v, DEL_INPUT); + } + } + /*now go through and mark all remaining faces all edges for keeping.*/ + for(f = BMIter_New(&faces, bm, BM_FACES, bm); f; f = BMIter_Step(&faces)){ + if(!BMO_TestFlag(bm, (BMHeader*)f, DEL_INPUT)){ + for(e = BMIter_New(&edges, bm, BM_EDGES_OF_FACE, f); e; e= BMIter_Step(&edges)) + BMO_ClearFlag(bm, (BMHeader*)e, DEL_INPUT); + for(v = BMIter_New(&verts, bm, BM_VERTS_OF_FACE, f); v; v= BMIter_Step(&verts)) + BMO_ClearFlag(bm, (BMHeader*)v, DEL_INPUT); + } + } + /*now delete marked faces*/ + BM_remove_tagged_faces(bm, DEL_INPUT); + /*delete marked edges*/ + BM_remove_tagged_edges(bm, DEL_INPUT); + /*remove loose vertices*/ + BM_remove_tagged_verts(bm, DEL_INPUT); + } + /*does this option even belong in here?*/ + else if(type == DEL_ALL){ + for(f = BMIter_New(&faces, bm, BM_FACES, bm); f; f = BMIter_Step(&faces)) + BMO_SetFlag(bm, (BMHeader*)f, DEL_INPUT); + for(e = BMIter_New(&edges, bm, BM_EDGES, bm); e; e = BMIter_Step(&edges)) + BMO_SetFlag(bm, (BMHeader*)e, DEL_INPUT); + for(v = BMIter_New(&verts, bm, BM_VERTS, bm); v; v = BMIter_Step(&verts)) + BMO_SetFlag(bm, (BMHeader*)v, DEL_INPUT); + + BM_remove_tagged_faces(bm, DEL_INPUT); + BM_remove_tagged_edges(bm, DEL_INPUT); + BM_remove_tagged_verts(bm, DEL_INPUT); + } +}
\ No newline at end of file diff --git a/source/blender/bmesh/operators/dissolveops.c b/source/blender/bmesh/operators/dissolveops.c new file mode 100644 index 00000000000..805531cf9dd --- /dev/null +++ b/source/blender/bmesh/operators/dissolveops.c @@ -0,0 +1,50 @@ +#include "MEM_guardedalloc.h" + +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "bmesh_private.h" +#include "BLI_arithb.h" + +#include <stdio.h> + +#define FACE_MARK 1 +void dissolvefaces_exec(BMesh *bmesh, BMOperator *op) +{ + BMOpSlot *finput; + BMFace *face; + float projectverts[400][3]; + void *projverts; + int i, count = 0; + + BMO_Flag_Buffer(bmesh, op, BMOP_DISFACES_FACEIN, FACE_MARK); + + /*TODO: need to discuss with Briggs how best to implement this, seems this would be + a great time to use the walker api, get it up to snuff. perhaps have a walker + that goes over inner vertices of a contiguously-flagged region? then you + could just use dissolve disk on them.*/ +} + +void dissolveverts_exec(BMesh *bmesh, BMOperator *op) +{ + BMOpSlot *vinput; + BMVert *vert; + int i; + + vinput = BMO_GetSlot(op, BMOP_DISVERTS_VERTIN); + + /* + BMO_Flag_Buffer(bmesh, op, BMOP_DISVERTS_VERTIN, VERT_MARK); + + for (vert=BMIter_New(&iter, bmesh, BM_VERTS, NULL); vert; vert=BMIter_Step(&iter)) { + if (BMO_TestFlag(bmesh, vert, VERT_MARK)) { + BM_Dissolve_Disk(bmesh, vert); + } + } + */ + + for (i=0; i<vinput->len; i++) { + vert = ((BMVert**)vinput->data.p)[i]; + BM_Dissolve_Disk(bmesh, vert); + } +}
\ No newline at end of file diff --git a/source/blender/bmesh/operators/subdivideop.c b/source/blender/bmesh/operators/subdivideop.c new file mode 100644 index 00000000000..cd9d37cfae8 --- /dev/null +++ b/source/blender/bmesh/operators/subdivideop.c @@ -0,0 +1,321 @@ +#include "MEM_guardedalloc.h" + +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "BLI_arithb.h" + +#include <stdio.h> + +/* +note: this is a pattern-based edge subdivider. +it tries to match a pattern to edge selections on faces. +it was a fairly easy exercise to test the bmesh api; it +doesn't support multicuts, so it won't actually be used. + +the patterns are defined as followed: + +the patterns are defined for the state of the face after +initial splitting. each edge that was split is flagged, as is +the new resulting edge. + +subdpattern pattern = { + //boolean flags for if an edge should have been split or not + {1, 0, 0, 0}, + //connection values for verts, + {2, -1, -1, -1}, + //second stage split flags, splits newly created edges + {0, 0, 0, 0, 1}, + //second stage connection values for verts, connects stuff again. + {-1, -1, -1, -1, 3}, + 4 //len of face before second stage splits, but after initial edge splits +}; + +*/ +typedef struct subdpattern { + int seledges[20]; //selected edges mask, for splitting + int connectverts[20]; //verts to connect; + + int secondstage_splitedges[20]; + //verts to connect afterwards. size must be len + number + //of edges split in secondstage_splitedges + int secondstage_connect[20]; + + int len; /*total number of verts*/ +} subdpattern; + +/*generic subdivision rules: + + * two selected edges in a face should make a link + between them. + + * one edge should do, what? make pretty topology, or just + split the edge only? +*/ + +/*note: the patterns are rotated as necassary to + match the input geometry. they're also based on the + post-splitted state of the faces. note that + second stage splitted stuff doesn't count + for pattern->len!*/ + +/* + v2 + / \ +v1 s_e1 e2 + /e0 \ +v0---e3----v3 + +handle case of one edge selected. +*/ + +subdpattern t_1edge = { + {1, 1, 0, 0}, + {-1, 3, -1, -1}, + {0}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1}, + 4 +}; + + +/* + v2 + / \e2 +v1 e1 e3 v3 + /e0 \ +v0---e4----v4 + +handle case of two edges selected. +*/ +subdpattern t_2edge = { + {1, 1, 1, 1, 0}, + {-1, 3, -1, -1, -1}, + {0}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1}, + 5 +}; + +/* + v2 + / \e2 +v1 e1 e3 v3 + /e0 \ +v0--e5--e4-v4 + v5 + +handle case of one edge selected. +make an edge between v1 and v5, +v5 and v3, and v3 and v1 +*/ +subdpattern t_3edge = { + {1, 1, 1, 1, 1, 1}, + {-1, 5, -1, 1, -1, 3}, //creates e6 + {0}, + {-1, -1, -1, -1, -1, -1, -1}, + 6 +}; + +/* + e3 +v4---------v3 +| | +|e4 | e2 +| | +|e0 e1 | +v0---v1----v2 + +connect v1 to v4 and v3 + +*/ +subdpattern q_1edge = { + {1, 1, 0, 0, 0}, + {-1, 3, -1, -1, 1}, + {0}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1}, + 5 +}; + +/* + e4 +v5---------v4 +| | +|e5 | e3 +| |v3 +|e0 e1 | e2 +v0---v1----v2 + +connect v1 to v3 + +*/ +subdpattern q_2adjedge = { + {1, 1, 1, 1, 0, 0}, + {-1, 3, -1, -1, -1, -1}, + {0, 0, 0, 0, 0, 0, 1}, + {-1, -1, -1, -1, -1, 6, -1, -1, -1}, + 6 +}; + + +/* + e4 v4 e3 +v5---------v3 +| | +|e5 | +| | e2 +| e0 e1 | +v0---v1----v2 + +connect v1 to v4 + +*/ +subdpattern q_2opedge = { + {1, 1, 0, 1, 1, 0}, + {-1, 4, -1, -1, -1, -1}, + {0}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1}, + 6 +}; + +/* + e5 v5 e4 +v6---------v4 +| | +|e6 | e3 +| | v3 +| e0 e1 | e2 +v0---v1----v2 + +connect v1 to v5, v1 to v3, and v3 to v5 + +*/ +subdpattern q_3edge = { + {1, 1, 1, 1, 1, 1, 0}, + {-1, 3, -1, 5, -1, 1, -1}, + {0}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1}, + 7 +}; + +/* + e5 v5 e4 +v6---------v4 +| | +|e6 | e3 +|v7 | v3 +|e7 e0 e1 | e2 +v0---v1----v2 + +connect v1 to v5, split edge, than connect +v3 and v7 to v8 (new vert from split) + +*/ +subdpattern q_4edge = { + {1, 1, 1, 1, 1, 1, 1, 1}, + {-1, 5, -1, -1, -1, -1, -1, -1}, + {0, 0, 0, 0, 0, 0, 0, 0, 1}, + {-1, -1, -1, 8, -1, -1, -1, 8, -1}, + 8 +}; + +subdpattern *patterns[] = { + &t_1edge, + &t_2edge, + &t_3edge, + &q_1edge, + &q_2adjedge, + &q_2opedge, + &q_3edge, + &q_4edge, +}; + +#define PLEN (sizeof(patterns) / sizeof(void*)) + +#define SUBD_SPLIT 1 +#define FACE_NEW 1 +#define MAX_FACE 10 + +void esubdivide_exec(BMesh *bmesh, BMOperator *op) +{ + BMOpSlot *einput; + BMEdge *edge, *nedge, *edges[MAX_FACE]; + BMFace *face, *nf; + BMLoop *nl, *loop; + BMVert *v1, *verts[MAX_FACE], *lastv; + BMIter fiter, eiter; + subdpattern *pat; + int i, j, matched, a, b, newlen, newvlen; + + BMO_Flag_Buffer(bmesh, op, BMOP_ESUBDIVIDE_EDGES, SUBD_SPLIT); + + einput = BMO_GetSlot(op, BMOP_ESUBDIVIDE_EDGES); + + /*first go through and split edges*/ + for (i=0; i<einput->len; i++) { + edge = ((BMEdge**)einput->data.p)[i]; + v1 = BM_Split_Edge(bmesh, edge->v1, edge, &nedge, 0.5, 1); + BMO_SetFlag(bmesh, v1, SUBD_SPLIT); + BMO_SetFlag(bmesh, nedge, SUBD_SPLIT); + } + + /*now go through all the faces and connect the new geometry*/ + for (face = BMIter_New(&fiter, bmesh, BM_FACES, NULL); face; face=BMIter_Step(&fiter)) { + matched = 0; + if (BMO_TestFlag(bmesh, face, FACE_NEW)) continue; + + if (face->len < MAX_FACE) { + /*try all possible pattern rotations*/ + for (i=0; i<PLEN; i++) { + if (patterns[i]->len != face->len) continue; + lastv = NULL; + for (j=0; j<patterns[i]->len; j++) { + for (a=0, loop=BMIter_New(&eiter, bmesh, BM_LOOPS_OF_FACE,face); loop; a++, loop=BMIter_Step(&eiter)) { + b = (j + a) % patterns[i]->len; + + edge = loop->e; + verts[b] = loop->v; + edges[b] = edge; + if (!(patterns[i]->seledges[b] == BMO_TestFlag(bmesh, edge, SUBD_SPLIT))) break; + + lastv = verts[b]; + } + + if (a == face->len) { + matched = 1; + pat = patterns[i]; + break; + } + } + if (matched) break; + } + } + + if (matched) { + /*first stage*/ + newlen = pat->len; + for (i=0; i<pat->len; i++) { + if (pat->connectverts[i] != -1) { + edges[newlen++] = BM_Connect_Verts(bmesh, verts[i], verts[pat->connectverts[i]], &nf); + BMO_SetFlag(bmesh, nf, FACE_NEW); + } + } + + newvlen = pat->len; + /*second stage*/ + for (i; i<newlen; i++) { + if (pat->secondstage_splitedges[i]) { + v1 = BM_Split_Edge(bmesh, edges[i]->v1, edges[i], &nedge, 0.5, 1); + verts[newvlen++] = v1; + } + } + + for (i=0; i<newvlen; i++) { + if (pat->secondstage_connect[i] != -1) { + edges[newlen++] = BM_Connect_Verts(bmesh, verts[i], verts[pat->secondstage_connect[i]], &nf); + BMO_SetFlag(bmesh, nf, FACE_NEW); + } + } + } else { /*no match in the pattern*/ + /*this should do some sort of generic subdivision*/ + } + } +}
\ No newline at end of file diff --git a/source/blender/bmesh/operators/triangulateop.c b/source/blender/bmesh/operators/triangulateop.c new file mode 100644 index 00000000000..875fc79d2ad --- /dev/null +++ b/source/blender/bmesh/operators/triangulateop.c @@ -0,0 +1,38 @@ +#include "MEM_guardedalloc.h" + +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "bmesh_private.h" +#include "BLI_arithb.h" + +#include <stdio.h> + +#define EDGE_NEW 1 +#define FACE_NEW 1 +void triangulate_exec(BMesh *bmesh, BMOperator *op) +{ + BMOpSlot *finput; + BMFace *face; + float projectverts[400][3]; + void *projverts; + int i, count = 0; + + finput = BMO_GetSlot(op, BMOP_ESUBDIVIDE_EDGES); + + for (i=0; i<finput->len; i++) { + face = ((BMFace**)finput->data.p)[i]; + + /*HACK! need to discuss with Briggs why the function takes an + externally-allocated array of vert coordinates in the first place.*/ + if (face->len > 400) projverts = MEM_callocN(sizeof(float)*3*face->len, "projverts"); + else projverts = projectverts; + + BM_Triangulate_Face(bmesh, face, projectverts, EDGE_NEW, FACE_NEW); + + if (projverts != projectverts) MEM_freeN(projverts); + } + + BMO_Flag_To_Slot(bmesh, op, BMOP_TRIANG_NEW_EDGES, EDGE_NEW, BM_EDGE); + BMO_Flag_To_Slot(bmesh, op, BMOP_TRIANG_NEW_FACES, FACE_NEW, BM_FACE); +}
\ No newline at end of file diff --git a/source/blender/bmesh/tools/BME_bevel.c b/source/blender/bmesh/tools/BME_bevel.c new file mode 100644 index 00000000000..543d64b6131 --- /dev/null +++ b/source/blender/bmesh/tools/BME_bevel.c @@ -0,0 +1,918 @@ +/** + * 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 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., 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 LICENSE BLOCK ***** + */ + +#include <math.h> + +#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 "MTC_matrixops.h" +#include "MTC_vectorops.h" + +#include "blendef.h" + +/* ------- Bevel code starts here -------- */ + +BME_TransData_Head *BME_init_transdata(int bufsize) { + BME_TransData_Head *td; + + td = MEM_callocN(sizeof(BME_TransData_Head), "BM 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, BMesh *bm, BMVert *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, BMVert *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)); +} + +static int BME_bevel_is_split_vert(BMLoop *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 */ +static int BME_bevel_get_vec(float *vec, BMVert *v1, BMVert *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 */ +static 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. */ +static BMVert *BME_bevel_split_edge(BMesh *bm, BMVert *v, BMVert *v1, BMLoop *l, float *up_vec, float value, BME_TransData_Head *td) { + BME_TransData *vtd, *vtd1, *vtd2; + BMVert *sv, *v2, *v3, *ov; + BMLoop *lv1, *lv2; + BMEdge *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 = bmesh_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 = BM_OtherEdgeVert(e1, v); + v3 = BM_OtherEdgeVert(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; + } + ov = BM_OtherEdgeVert(e1,v); + sv = BM_Split_Edge(bm,v,e1,&ne,0); + //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /*this is technically wrong...*/ + //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); + //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); + 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; + ov = BM_OtherEdgeVert(l->e,v); + sv = BM_Split_Edge(bm,v,l->e,&ne,0); + //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /*this is technically wrong...*/ + //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); + //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); + 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; +} + +static float BME_bevel_set_max(BMVert *v1, BMVert *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; +} + +static BMVert *BME_bevel_wire(BMesh *bm, BMVert *v, float value, int res, int options, BME_TransData_Head *td) { + BMVert *ov1, *ov2, *v1, *v2; + + ov1 = BM_OtherEdgeVert(v->edge, v); + ov2 = BM_OtherEdgeVert(bmesh_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) { + bmesh_jekv; + + //void BM_Collapse_Vert(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, int calcnorm){ + //hrm, why is there a fac here? it just removes a vert + BM_Collapse_Vert(bm, v->edge, v, 1.0, 0); + //bmesh_jekv(bm,v->edge,v); + } + + return v1; +} + +static BMLoop *BME_bevel_edge(BMesh *bm, BMLoop *l, float value, int options, float *up_vec, BME_TransData_Head *td) { + BMVert *v1, *v2, *kv; + BMLoop *kl=NULL, *nl; + BMEdge *e; + BMFace *f; + + 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) { + BM_Split_Face(bm,kl->f,kl->prev->v,kl->next->v,&nl,kl->prev->e); + bmesh_jfke(bm,((BMLoop*)kl->prev->radial.next->data)->f,kl->f,kl->prev->e); + BM_Collapse_Vert(bm, kl->e, kv, 1.0); + //BME_JEKV(bm,kl->e,kv); + + } + else { + BM_Split_Face(bm,kl->f,kl->next->next->v,kl->v,&nl,kl->next->e); + bmesh_jfke(bm,((BMLoop*)kl->next->radial.next->data)->f,kl->f,kl->next->e); + BM_Collapse_Vert(bm, kl->e, kv, 1.0); + //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) { + BM_Split_Face(bm,kl->f,kl->prev->v,kl->next->v,&nl,kl->prev->e); + bmesh_jfke(bm,((BMLoop*)kl->prev->radial.next->data)->f,kl->f,kl->prev->e); + BM_Collapse_Vert(bm, kl->e, kv, 1.0); + //BME_JEKV(bm,kl->e,kv); + } + else { + BM_Split_Face(bm,kl->f,kl->next->next->v,kl->v,&nl,kl->next->e); + bmesh_jfke(bm,((BMLoop*)kl->next->radial.next->data)->f,kl->f,kl->next->e); + BM_Collapse_Vert(bm, kl->e, kv, 1.0); + //BME_JEKV(bm,kl->e,kv); + } + } + + if ((v1->tflag1 & BME_BEVEL_NONMAN)==0 || (v2->tflag1 & BME_BEVEL_NONMAN)==0) { + BM_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; +} + +static BMLoop *BME_bevel_vert(BMesh *bm, BMLoop *l, float value, int options, float *up_vec, BME_TransData_Head *td) { + BMVert *v1, *v2; + BMFace *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 = BM_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 BMFace pointer to the resulting inner face. +*/ +static BMFace *BME_bevel_poly(BMesh *bm, BMFace *f, float value, int options, BME_TransData_Head *td) { + BMLoop *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; i<len; i++,l=l->next) { + 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; i<len; i++,l=l->next) { + 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; +} + +static void BME_bevel_add_vweight(BME_TransData_Head *td, BMesh *bm, BMVert *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); + } +} + + +static bevel_init_verts(BMesh *bm, int options, BME_TransData_Head *td){ + BMVert *v; + float weight; + for(v=bm->verts.first; v; v=v->next){ + weight = 0.0; + if(!(v->tflag1 & BME_BEVEL_NONMAN)){ + if(options & BME_BEVEL_SELECT){ + if(v->flag & SELECT) weight = 1.0; + } + else if(options & BME_BEVEL_WEIGHT) weight = v->bweight; + else weight = 1.0; + if(weight > 0.0){ + v->tflag1 |= BME_BEVEL_BEVEL; + BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0,weight, -1, NULL); + } + } + } +} +static bevel_init_edges(BMesh *bm, int options, BME_TransData_Head *td){ + BMEdge *e; + int count; + float weight; + for( e = BM_first_element(bm, BME_EDGE); e; e = BM_next_element(bm, BME_EDGE, e)){ + weight = 0.0; + if(!(e->tflag1 & BME_BEVEL_NONMAN)){ + if(options & BME_BEVEL_SELECT){ + if(e->flag & SELECT) weight = 1.0; + } + else if(options & BME_BEVEL_WEIGHT){ + weight = e->bweight; + } + else{ + weight = 1.0; + } + if(weight > 0.0){ + e->tflag1 |= BME_BEVEL_BEVEL; + e->v1->tflag1 |= BME_BEVEL_BEVEL; + e->v2->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); + } + } + } + + /*clean up edges with 2 faces that share more than one edge*/ + for( e = BM_first_element(bm, BME_EDGE); e; e = BM_next_element(bm, BME_EDGE, e)){ + if(e->tflag1 & BME_BEVEL_BEVEL){ + count = BM_face_sharededges(e->loop->f, ((BMLoop*)e->loop->radial.next->data)->f); + if(count > 1) e->tflag1 &= ~BME_BEVEL_BEVEL; + } + } +} +static BMesh *BME_bevel_initialize(BMesh *bm, int options, int defgrp_index, float angle, BME_TransData_Head *td){ + + BMVert *v, *v2; + BMEdge *e, *curedge; + BMFace *f; + BMIter iter; + int wire, len; + + for (v = BMIter_New(&iter, bm, BM_VERTS, nm); v; v = BMIter_Step(&iter)) v->tflag1 = 0; + for (e = BMIter_New(&iter, bm, BM_EDGES, nm); e; e = BMIter_Step(&iter)) e->tflag1 = 0; + for (f = BMIter_New(&iter, bm, BM_FACES, nm); f; f = BMIter_Step(&iter)) f->tflag1 = 0; + + /*tag non-manifold geometry*/ + for (v = BMIter_New(&iter, bm, BM_VERTS, nm); v; v = BMIter_Step(&iter)) { + v->tflag1 = BME_BEVEL_ORIG; + if(v->edge){ + BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL); + if (BM_Nonmanifold_Vert(bm,v)) v->tflag1 |= BME_BEVEL_NONMAN; + /*test wire vert*/ + len = bmesh_cycle_length(bmesh_disk_getpointer(v->edge,v)); + if(len == 2 && BME_wirevert(bm, v)) v->tflag1 &= ~BME_BEVEL_NONMAN; + }else v->tflag1 |= BME_BEVEL_NONMAN; + } + + for (e = BMIter_New(&iter, bm, BM_EDGES, nm); e; e = BMIter_Step(&iter)) { + e->tflag1 = BME_BEVEL_ORIG; + if (e->loop == NULL || BME_cycle_length(&(e->loop->radial)) > 2){ + e->v1->tflag1 |= BME_BEVEL_NONMAN; + e->v2->tflag1 |= BME_BEVEL_NONMAN; + e->tflag1 |= BME_BEVEL_NONMAN; + } + if((e->v1->tflag1 & BME_BEVEL_NONMAN) || (e->v2->tflag1 & BME_BEVEL_NONMAN)) e->tflag1 |= BME_BEVEL_NONMAN; + } + + for (f = BMIter_New(&iter, bm, BM_FACES, nm); f; f = BMIter_Step(&iter)) + f->tflag1 = BME_BEVEL_ORIG; + if(options & BME_BEVEL_VERT) bevel_init_verts(bm, options, td); + else bevel_init_edges(bm, options, td); + return bm; + +} +static BMesh *BME_bevel_reinitialize(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + for (v = BMIter_New(bm, BM_VERTS, NULL); v; v = BMIter_Step(bm)){ + v->tflag1 |= BME_BEVEL_ORIG; + v->tflag2 = 0; + } + for (e = BMIter_New(bm, BM_EDGES, NULL); e; e = BMIter_Step(bm)){ + e->tflag1 |= BME_BEVEL_ORIG; + } + for (f = BMIter_New(bm, BM_FACES, NULL); f; f = BMIter_Step(bm)){ + 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 BMesh pointer to the BM passed as a parameter. +*/ + +static BMMesh *BME_bevel_mesh(BMMesh *bm, float value, int res, int options, int defgrp_index, BME_TransData_Head *td) { + BMVert *v, *nv; + BMEdge *e, *curedge, *ne; + BMLoop *l, *l2; + BMFace *f, *nf; + + BMeshIter verts; + BMeshIter edges; + BMeshIter loops; + BMeshIter faces; + + unsigned int i, len; + + /*bevel polys*/ + for(f = BMeshIter_init(faces, BM_FACES, bm, 0); f; f = BMeshIter_step(faces)){ + if(bmesh_test_flag(f, BME_BEVEL_ORIG){ + bevel_poly(bm,f,value,options,td); + } + } + /*get rid of beveled edges*/ + for(e = BMeshIter_init(edges, BM_EDGES, bm, 0); e;){ + ne = BMeshIter_step(edges); + if( bmesh_test_flag(e, BME_BEVEL_BEVEL) && bmesh_test_flag(e, BME_BEVEL_ORIG) ) bmesh_join_faces(bm, e->loop->f, ((BMLoop*)e->loop->radial.next->data)->f, e, 1); + e = ne; + } + /*link up corners and clip*/ + for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v;){ + nv = BMeshIter_step(verts) + if( bmesh_test_flag(v, BME_BEVEL_ORIG) && bmesh_test_flag(v, BME_BEVEL_BEVEL)){ + curedge = v->edge; + do{ + l = curedge->loop; + l2 = l->radial.next->data; + if(l->v != v) l = l->next; + if(l2->v != v) l2 = l2->next; + if(l->f->len > 3) BM_split_face(bm,l->f,l->next->v,l->prev->v,&l,l->e); /* clip this corner off */ + if(l2->f->len > 3) BM_split_face(bm,l2->f,l2->next->v,l2->prev->v,&l,l2->e); /* clip this corner off */ + curedge = BM_edge_of_vert(curedge, v); + }while(curedge != v->edge); + BM_dissolve_disk(bm,v); + } + v = nv; + } + + /*Debug print, remove*/ + for(f = BMeshIter_init(faces, BM_FACES, bm, 0); f;){ + if(f->len == 2){ + printf("warning"); + } + } + + return bm; +} + +BMesh *BME_bevel(BMMesh *bm, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd) { + BMVert *v; + BMEdge *e; + BMIter *verts; + + BME_TransData_Head *td; + BME_TransData *vtd; + int i; + double fac=1, d; + + + td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE); + /* recursion math courtesy of Martin Poirier (theeth) */ + for (i=0; i<res-1; i++) { + if (i==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * i * 2.0f); + } + d = 1.0f/fac; + + + for (i=0; i<res || (res==0 && i==0); i++) { + BME_bevel_initialize(bm, options, defgrp_index, angle, td); + //if (i != 0) BME_bevel_reinitialize(bm); + bmesh_begin_edit(bm); + BME_bevel_mesh(bm,(float)d,res,options,defgrp_index,td); + bmesh_end_edit(bm); + if (i==0) d /= 3; else d /= 2; + } + + BME_model_begin(bm); + BME_tesselate(bm); + BME_model_end(bm); + + + /*interactive preview?*/ + if (rtd) { + *rtd = td; + return bm; + } + + /* otherwise apply transforms */ + for( v = BMeshIter_init(verts); v; v = BMeshIter_step(verts)){ + 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/bmesh/tools/BME_dupe_ops.c b/source/blender/bmesh/tools/BME_dupe_ops.c new file mode 100644 index 00000000000..533e278ea68 --- /dev/null +++ b/source/blender/bmesh/tools/BME_dupe_ops.c @@ -0,0 +1,318 @@ +/* + * BME_DUPLICATE.C + * + * This file contains functions for duplicating, copying, and splitting + * elements from a bmesh. + * + */ + +/* + * COPY VERTEX + * + * Copy an existing vertex from one bmesh to another. + * +*/ + +static BMVert *copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash) +{ + BMVert *target_vertex = NULL; + + /*create a new vertex*/ + target_vertex = BM_Make_Vert(target, source_vertex->co, NULL); + + /*insert new vertex into the vert hash*/ + BLI_ghash_insert(vhash, source_vertex, target_vertex); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data); + + /*Copy Markings*/ + if(BM_Is_Selected((BMHeader*)source_vertex)) BM_Select_Vert(target_mesh, target_vertex, 1); + if(BM_Is_Hidden((BMHeader*)source_vertex)) BM_Mark_Hidden((BMHeader*)target_vertex, 1); + + BMO_SetFlag(target_mesh, (BMHeader*)target_vertex, DUPE_NEW); + + return target_vertex; +} + +/* + * COPY EDGE + * + * Copy an existing edge from one bmesh to another. + * +*/ + +static BMEdge *copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash) +{ + BMEdge *target_edge = NULL; + BMVert *target_vert1, *target_vert2; + + /*lookup v1 and v2*/ + target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1); + target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2); + + /*create a new edge*/ + target_edge = BM_Make_Edge(target_mesh, target_vert1, target_vert2, NULL, 0); + + /*insert new edge into the edge hash*/ + BLI_ghash_insert(ehash, source_edge, target_edge); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data); + + /*copy flags*/ + if(BM_Is_Selected((BMHeader*) source_edge)) BM_Select_Edge(target_mesh, target_edge, 1); + if(BM_Is_Hidden((BMHeader*) source_edge)) BM_Mark_Hidden(target_mesh, target_edge, 1); + if(BM_Is_Sharp((BMHeader*) source_edge)) BM_Mark_Sharp(target_edge, 1); + if(BM_Is_Seam((BMHeader*) source_edge)) BM_Mark_Seam(target_edge, 1); + if(BM_Is_Fgon((BMHeader*) source_edge)) BM_Mark_Fgon(target_edge, 1); + + BMO_SetFlag(target_mesh, (BMHeader*)target_edge, DUPE_NEW); + + return target_edge; +} + +/* + * COPY FACE + * + * Copy an existing face from one bmesh to another. + * +*/ + +static BMFace *copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash) +{ + BMEdge *target_edge; + BMVert *target_vert1, *target_vert2; + BMLoop *source_loop, *target_loop; + BMFace *target_face = NULL; + int i; + + /*lookup the first and second verts*/ + target_vert1 = BLI_ghash_lookup(vhash, source_face->loopbase->v); + target_vert2 = BLI_ghash_lookup(vhash, source_face->loopbase->next->v); + + /*lookup edges*/ + i = 0; + source_loop = source_face->loopbase; + do{ + edar[i] = BLI_ghash_lookup(ehash, source_loop->e); + i++; + source_loop = source_loop->next; + }while(source_loop != source_face->loopbase); + + /*create new face*/ + target_face = BM_Make_Ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0); + + /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/ + CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data); + + /*copy flags*/ + if(BM_Is_Selected((BMHeader*)source_face)) BM_Select_face(target, target_face, 1); + if(BM_Is_Hidden((BMHeader*)source_face)) BM_Mark_Hidden((BMHeader*)target_face, 1); + + /*mark the face for output*/ + BMO_SetFlag(target_mesh, (BMHeader*)target_face, DUPE_NEW); + + /*copy per-loop custom data*/ + source_loop = source_face->loopbase; + target_loop = target_face->loopbase; + do{ + CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data); + source_loop = source_loop->next; + target_loop = target_loop->next; + }while(source_loop != source_face->loopbase); + + return target_face; +} + +/* + * COPY MESH + * + * Internal Copy function. +*/ + +/*local flag defines*/ + +#define DUPE_INPUT 1 /*input from operator*/ +#define DUPE_NEW 2 +#define DUPE_DONE 3 + +static void copy_mesh(BMMesh *source, BMMesh *target) +{ + + BMVert *v = NULL; + BMEdge *e = NULL, **edar = NULL; + BMLoop *l = NULL; + BMFace *f = NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + BMIter loops; + + GHash *vhash; + GHash *ehash; + + int maxlength = 0, flag; + + /*initialize pointer hashes*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp); + + /*initialize edge pointer array*/ + for(f = BMIter_New(&faces, source, BM_FACES, source, 0, NULL); f; f = BMIter_Step(&faces)){ + if(f->len > maxlength) maxlength = f->len; + } + edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array"); + + + /*first we dupe all flagged faces and their elements from source*/ + for(f = BMIter_New(&faces, source, BM_FACES, source, 0, NULL); f; f= BMIter_Step(&faces)){ + if(BMO_TestFlag(source, (BMHeader*)f, DUPE_INPUT)){ + /*vertex pass*/ + for(v = BMIter_New(&verts, source, BM_VERT_OF_FACE, f, 0, NULL); v; v = BMIter_Step(&verts)){ + if(!BMO_TestFlag(source, (BMHeader*)v, DUPE_DONE)){ + copy_vertex(source,v, target, vhash); + BMO_SetFlag(source, (BMHeader*)v, DUPE_DONE); + } + } + + /*edge pass*/ + for(e = BMIter_New(&edges, source, BM_EDGE_OF_FACE, f, 0, NULL); e; e = BMeshIter_step(&edges)){ + if(!BMO_TestFlag(source, (BMHeader*)e, DUPE_DONE)){ + copy_edge(source, e, target, vhash, ehash); + BMO_SetFlag(source, (BMHeader*)e, DUPE_DONE); + } + } + copy_face(source, f, target, edar, vhash, ehash); + BMO_SetFlag(source, (BMHeader*)f, DUPE_DONE); + } + } + + /*now we dupe all the edges*/ + for(e = BMIter_New(&edges, source, BM_EDGES, source, 0, NULL); e; e = BMIter_Step(&edges)){ + if(BMO_TestFlag(source, (BMHeader*)e, DUPE_INPUT) && (!BMO_TestFlag(source, (BMHeader*)e, DUPE_DONE))){ + /*make sure that verts are copied*/ + if(!BMO_TestFlag(source, (BMHeader*)e->v1, DUPE_DONE){ + copy_vertex(source, e->v1, target, vhash); + BMO_SetFlag(source, (BMHeader*)e->v1, DUPE_DONE); + } + if(!BMO_TestFlag(source, (BMHeader*)e->v2, DUPE_DONE){ + copy_vertex(source, e->v2, target, vhash); + BMO_SetFlag(source, (BMHeader*)e->v2, DUPE_DONE); + } + /*now copy the actual edge*/ + copy_edge(source, e, target, vhash, ehash); + BMO_SetFlag(source, (BMHeader*)e, DUPE_DONE); + } + } + + /*finally dupe all loose vertices*/ + for(v = BMIter_New(&verts, source, BM_VERTS, source, 0, NULL); v; v = BMIter_Step(&verts)){ + if(BMO_TestFlag(source, (BMHeader*)v, DUPE_INPUT) && (!BMO_TestFlag(source, (BMHeader*)v, DUPE_DONE))){ + copy_vertex(source, v, target, vhash); + BMO_SetFlag(source, (BMHeader*)v, DUPE_DONE); + } + } + + /*free pointer hashes*/ + BLI_ghash_free(vhash, NULL, NULL); + BLI_ghash_free(ehash, NULL, NULL); + + /*free edge pointer array*/ + if(edar) + MEM_freeN(edar); +} +/* +BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4]) +{ + BMMesh *target = NULL; + target = bmesh_make_mesh(allocsize); + + + CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + + CustomData_bmesh_init_pool(&target->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&target->edata, allocsize[1]); + CustomData_bmesh_init_pool(&target->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&target->pdata, allocsize[3]); + + bmesh_begin_edit(bm); + bmesh_begin_edit(target); + + bmesh_copy_mesh(bm, target, 0); + + bmesh_end_edit(bm); + bmesh_end_edit(target); + + return target; + +} +*/ + +void dupeop_exec(BMMesh *bm, BMOperator *op) +{ + BMOperator *dupeop = op; + BMOpSlot *vinput, *einput, *finput, *vnew, *enew, *fnew; + int i; + + vinput = BMO_Get_Slot(dupeop, BMOP_DUPE_VINPUT); + einput = BMO_Get_Slot(dupeop, BMOP_DUPE_EINPUT); + finput = BMO_Get_Slot(dupeop, BMOP_DUPE_FINPUT); + + /*go through vinput, einput, and finput and flag elements with private flags*/ + BMO_Flag_Buffer(bm, dupeop, BMOP_DUPE_VINPUT, DUPE_INPUT); + BMO_Flag_Buffer(bm, dupeop, BMOP_DUPE_EINPUT, DUPE_INPUT); + BMO_Flag_Buffer(bm, dupeop, BMOP_DUPE_FINPUT, DUPE_INPUT); + + /*use the internal copy function*/ + copy_mesh(bm, bm); + + /*Output*/ + /*First copy the input buffers to output buffers - original data*/ + BMO_Copy_Opslot_Buffer_Alloc(dupeop, vinput, BMO_Get_Slot(dupeop, BMOP_DUPE_VORIGINAL)); + BMO_Copy_Opslot_Buffer_Alloc(dupeop, einput, BMO_Get_Slot(dupeop, BMOP_DUPE_EORIGINAL)); + BMO_Copy_Opslot_Buffer_Alloc(dupeop, finput, BMO_Get_Slot(dupeop, BMOP_DUPE_FORIGINAL)); + + /*Now alloc the new output buffers*/ + BMO_Flag_To_Slot(bm, dupeop, BMOP_DUPE_VNEW, DUPE_NEW, BMESH_VERT); + BMO_Flag_To_Slot(bm, dupeop, BMOP_DUPE_ENEW, DUPE_NEW, BMESH_EDGE); + BMO_Flag_To_Slot(bm, dupeop, BMOP_DUPE_FNEW, DUPE_NEW, BMESH_FACE); +} + +void splitop_exec(BMMesh *bm, BMOperator *op) +{ + BMOperator *splitop = op; + BMOperator dupeop; + BMOperator delop; + + /*initialize our sub-operators*/ + BMO_Init_Op(&dupeop, BMOP_DUPE); + BMO_Init_Op(&delop, BMOP_DEL); + + BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_VINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_VINPUT)); + BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_EINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_EINPUT)); + BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_FINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_FINPUT)); + + BMO_Exec_Op(&dupeop); + + /*connect outputs of dupe to delete*/ + BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_VORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_VINPUT)); + BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_EORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_EINPUT)); + BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_FORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_FINPUT)); + + BMO_Exec_Op(&delop); + + /*now we make our outputs by copying the dupe outputs*/ + BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_VNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_VOUTPUT)); + BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_ENEW), BMO_Get_Slot(splitop, BMOP_SPLIT_EOUTPUT)); + BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_FNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_FOUTPUT)); + + /*cleanup*/ + BMO_Finish_Op(&dupeop); + BMO_Finish_Op(&delop); +}
\ No newline at end of file diff --git a/source/blender/bmesh/tools/BME_duplicate.c b/source/blender/bmesh/tools/BME_duplicate.c new file mode 100644 index 00000000000..7c679ff6a89 --- /dev/null +++ b/source/blender/bmesh/tools/BME_duplicate.c @@ -0,0 +1,307 @@ +/* + * BME_DUPLICATE.C + * + * This file contains functions for duplicating, copying, and splitting + * elements from a bmesh. + * + */ + +/* + * BMESH COPY VERTEX + * + * Copy an existing vertex from one bmesh to another. + * +*/ + +static BMVert *bmesh_copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash) +{ + BMVert *target_vertex = NULL; + + /*create a new vertex*/ + target_vertex = bmesh_make_vert(target, source_vertex->co, NULL); + + /*insert new vertex into the vert hash*/ + BLI_ghash_insert(vhash, source_vertex, target_vertex); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data); + + /*copy flags*/ + if(bmesh_test_flag(source_vertex, BMESH_SELECT)) bmesh_set_flag(target_vertex, BMESH_SELECT); + if(bmesh_test_flag(source_vertex, BMESH_HIDDEN)) bmesh_set_flag(target_vertex, BMESH_HIDDEN); + + return target_vertex; +} + +/* + * BMESH COPY EDGE + * + * Copy an existing edge from one bmesh to another. + * +*/ + +static BMEdge *bmesh_copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash) +{ + BMEdge *target_edge = NULL; + BMVert *target_vert1, *target_vert2; + + /*lookup v1 and v2*/ + target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1); + target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2); + + /*create a new edge*/ + target_edge = bmesh_make_edge(target_mesh, target_vert1, target_vert2, NULL, 0); + + /*insert new edge into the edge hash*/ + BLI_ghash_insert(ehash, source_edge, target_edge); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data); + + /*copy flags*/ + if(bmesh_test_flag(source_edge, BMESH_SELECT)) bmesh_set_flag(target_edge, BMESH_SELECT); + if(bmesh_test_flag(source_edge, BMESH_HIDDEN)) bmesh_set_flag(target_edge, BMESH_SELECT); + if(bmesh_test_flag(source_edge, BMESH_SHARP)) bmesh_set_flag(target_edge, BMESH_SHARP); + if(bmesh_test_flag(source_edge, BMESH_SEAM)) bmesh_set_flag(target_edge, BMESH_SEAM); + if(bmesh_test_flag(source_edge, BMESH_FGON)) bmesh_set_flag(target_edge, BMESH_FGON); + + return target_edge; +} + +/* + * BMESH COPY FACE + * + * Copy an existing face from one bmesh to another. + * +*/ + +static BMFace *bmesh_copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash) +{ + BMEdge *target_edge; + BMVert *target_vert1, *target_vert2; + BMLoop *source_loop, *target_loop; + BMFace *target_face = NULL; + int i; + + + /*lookup the first and second verts*/ + target_vert1 = BLI_ghash_lookup(vhash, source_face->loopbase->v); + target_vert2 = BLI_ghash_lookup(vhash, source_face->loopbase->next->v); + + /*lookup edges*/ + i = 0; + source_loop = source_face->loopbase; + do{ + edar[i] = BLI_ghash_lookup(ehash, source_loop->e); + i++; + source_loop = source_loop->next; + }while(source_loop != source_face->loopbase); + + /*create new face*/ + target_face = bmesh_make_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0); + + /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/ + CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data); + + /*copy flags*/ + if(bmesh_test_flag(source_face, BMESH_SELECT)) bmesh_set_flag(target_face, BMESH_SELECT); + if(bmesh_test_flag(source_face, BMESH_HIDDEN)) bmesh_set_flag(target_face, BMESH_HIDDEN); + + /*mark the face as dirty for normal and tesselation calcs*/ + bmesh_set_flag(target_face, BMESH_DIRTY); + + /*copy per-loop custom data*/ + source_loop = source_face->loopbase; + target_loop = target_face->loopbase; + do{ + CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data); + source_loop = source_loop->next; + target_loop = target_loop->next; + }while(source_loop != source_face->loopbase); + + return target_face; +} + +/* + * BMESH COPY MESH + * + * Internal Copy function. copies flagged elements from + * source to target, which may in fact be the same mesh. + * Note that if __flag is 0, all elements will be copied. + * +*/ + +static void bmesh_copy_mesh(BMMesh *source, BMMesh *target, int __flag) +{ + + BMVert *v; + BMEdge *e, **edar; + BMLoop *l; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + BMIter loops; + + GHash *vhash; + GHash *ehash; + + int maxlength = 0, flag; + + /*initialize pointer hashes*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp); + + /*initialize edge pointer array*/ + for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)){ + if(f->len > maxlength) maxlength = f->len; + } + edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array"); + + /*begin modelling loop for target*/ + bmesh_begin_edit(target); + + /*we make special exception for __flag == 0... we copy all*/ + if(!__flag){ + flag = BMESH_DUPE; + for(v = BMeshIter_init(verts, BM_VERTS, source, 0); v; v = BMeshIter_step(verts)) bmesh_set_flag(v, BMESH_DUPE); + for(e = BMeshIter_init(verts, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)) bmesh_set_flag(e, BMESH_DUPE); + for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)) bmesh_set_flag(f, BMESH_DUPE); + } else{ + flag = __flag; + } + + /*first we dupe all flagged faces and their elements from source*/ + for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f= BMeshIter_step(faces)){ + if(bmesh_test_flag(f, flag)){ + /*vertex pass*/ + for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){ + if(!bmesh_test_flag(l->v, BMESH_DUPED)){ + bmesh_copy_vertex(source,l->v, target, vhash); + bmesh_set_flag(l->v, BMESH_DUPED); + } + } + + /*edge pass*/ + for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){ + if(!bmesh_test_flag(l->e, BMESH_DUPED)){ + bmesh_copy_edge(source, l->e, target, vhash, ehash); + bmesh_set_flag(l->e, BMESH_DUPED); + } + } + bmesh_copy_face(source, f, target, edar, vhash, ehash); + bmesh_set_flag(f, BMESH_DUPED); + } + } + + /*now we dupe all the edges*/ + for(e = BMeshIter_init(edges, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)){ + if(bmesh_test_flag(e, flag) && (!bmesh_test_flag(e, BMESH_DUPED))){ + /*make sure that verts are copied*/ + if(!bmesh_test_flag(e->v1, BMESH_DUPED)){ + bmesh_copy_vertex(source, e->v1, target, vhash); + bmesh_set_flag(e->v1, BMESH_DUPED); + } + if(!bmesh_test_flag(e->v2, BMESH_DUPED)){ + bmesh_copy_vertex(source, e->v2, target, vhash); + bmesh_set_flag(e->v2, BMESH_DUPED); + } + /*now copy the actual edge*/ + bmesh_copy_edge(source, e, target, vhash, ehash); + bmesh_set_flag(e, BMESH_DUPED); + } + } + + /*finally dupe all loose vertices*/ + for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){ + if(bmesh_test_flag(v, flag) && (!bmesh_test_flag(v, BMESH_DUPED))){ + bmesh_copy_vertex(source, v, target, vhash); + bmesh_set_flag(v, BMESH_DUPED); + } + } + + /*finish*/ + bmesh_end_edit(target, BMESH_CALC_NORM | BMESH_CALC_TESS); + + /*free pointer hashes*/ + BLI_ghash_free(vhash, NULL, NULL); + BLI_ghash_free(ehash, NULL, NULL); + + /*free edge pointer array*/ + MEM_freeN(edar); +} + +/* + * BMESH MAKE MESH FROM MESH + * + * Creates a new mesh by duplicating an existing one. + * +*/ + +BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4]) +{ + BMMesh *target = NULL; + target = bmesh_make_mesh(allocsize); + + /*copy custom data layout*/ + CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&target->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&target->edata, allocsize[1]); + CustomData_bmesh_init_pool(&target->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&target->pdata, allocsize[3]); + + bmesh_begin_edit(bm); + bmesh_begin_edit(target); + + bmesh_copy_mesh(bm, target, 0); /*copy all elements*/ + + bmesh_end_edit(bm); + bmesh_end_edit(target); + + return target; + +} + +/* + * BMESH SPLIT MESH + * + * Copies flagged elements then deletes them. + * +*/ + +void bmesh_split_mesh(BMMesh *bm, int flag){ + + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + bmesh_begin_edit(bm); + bmesh_copy_mesh(bm, bm, flag); + + /*mark verts for deletion*/ + for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){ + if(bmesh_test_flag(v, flag)) bmesh_delete_vert(bm, v); + } + /*mark edges for deletion*/ + for(e = BMeshIter_init(edges, BM_EDGES, bm, 0); e; e = BMeshIter_step(edges)){ + if(bmesh_test_flag(e, flag)) bmesh_delete_edge(bm, e); + + } + /*mark faces for deletion*/ + for(f = BMeshIter_init(faces, BM_FACES, bm, 0); f; f= BMeshIter_step(faces)){ + if(bmesh_tes t_flag(f, flag)) bmesh_delete_face(bm, f); + + } + bmesh_end_edit(bm); +} + diff --git a/source/blender/bmesh/tools/BME_extrude.c b/source/blender/bmesh/tools/BME_extrude.c new file mode 100644 index 00000000000..b7b3a94893d --- /dev/null +++ b/source/blender/bmesh/tools/BME_extrude.c @@ -0,0 +1,216 @@ +/** + * BMESH EXTRUDE TOOL + * + * A rewrite of the old editmesh extrude code with the + * redundant parts broken into multiple functions + * in an effort to reduce code. This works with multiple + * selection modes, and is intended to build the + * extrusion in steps, depending on what elements are selected. + * Also decoupled the calculation of transform normal + * and put it in UI where it probably is more appropriate + * for the moment. + * + * TODO: + * -Fit this into the new 'easy' API. +*/ + +void BME_extrude_verts(BME_Mesh *bm, GHash *vhash){ + BMVert *v, *nv = NULL; + BMEdge *ne = NULL; + float vec[3]; + + //extrude the vertices + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(BME_SELECTED(v)){ + VECCOPY(vec,v->co); + nv = BME_MV(bm,vec); + nv->tflag2 =1; //mark for select + ne = BME_ME(bm,v,nv); + ne->tflag1 = 2; //mark as part of skirt 'ring' + BLI_ghash_insert(vhash,v,nv); + BME_VISIT(v); + } + } +} + +void BME_extrude_skirt(BME_Mesh *bm, GHash *ehash){ + + BMFace *nf=NULL; + BMEdge *e, *l=NULL, *r=NULL, *edar[4], *ne; + BMVert *v, *v1, *v2, *lv, *rv, *nv; + + for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){ + if(BME_SELECTED(e)){ + /*find one face incident upon e and use it for winding of new face*/ + if(e->loop){ + v1 = e->loop->next->v; + v2 = e->loop->v; + } + else{ + v1 = e->v1; + v2 = e->v2; + } + + if(v1->edge->tflag1 == 2) l = v1->edge; + else l = BME_disk_next_edgeflag(v1->edge, v1, 0, 2); + if(v2->edge->tflag1 == 2) r = v2->edge; + else r = BME_disk_next_edgeflag(v2->edge, v2, 0, 2); + + lv = BME_edge_getothervert(l,v1); + rv = BME_edge_getothervert(r,v2); + + ne = BME_ME(bm,lv,rv); + ne->tflag2 = 1; //mark for select + BLI_ghash_insert(ehash,e,ne); + BME_VISIT(e); + + edar[0] = e; + edar[1] = l; + edar[2] = ne; + edar[3] = r; + BME_MF(bm,v1,v2,edar,4); + } + } +} + +void BME_cap_skirt(BME_Mesh *bm, GHash *vhash, GHash *ehash){ + BMVert *v, *nv, *v1, *v2; + BMEdge *e, **edar, *ne; + BME_Loop *l; + BMFace *f, *nf; + MemArena *edgearena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + float vec[3]; + int i, j, del_old =0; + + + //loop through faces, then loop through their verts. If the verts havnt been visited yet, duplicate these. + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(BME_SELECTED(f)){ + l = f->loopbase; + do{ + if(!(BME_ISVISITED(l->v))){ //interior vertex + //dupe vert + VECCOPY(vec,l->v->co); + nv = BME_MV(bm,vec); + BLI_ghash_insert(vhash,l->v,nv); + //mark for delete + l->v->tflag1 = 1; + BME_VISIT(l->v); //we dont want to dupe it again. + } + l=l->next; + }while(l!=f->loopbase); + } + } + + //find out if we delete old faces or not. This needs to be improved a lot..... + for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){ + if(BME_SELECTED(e) && e->loop){ + i= BME_cycle_length(&(e->loop->radial)); + if(i > 2){ + del_old = 1; + break; + } + } + } + + + //build a new edge net, insert the new edges into the edge hash + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(BME_SELECTED(f)){ + l=f->loopbase; + do{ + if(!(BME_ISVISITED(l->e))){ //interior edge + //dupe edge + ne = BME_ME(bm,BLI_ghash_lookup(vhash,l->e->v1),BLI_ghash_lookup(vhash,l->e->v2)); + BLI_ghash_insert(ehash,l->e,ne); + //mark for delete + l->e->tflag1 = 1; + BME_VISIT(l->e); //we dont want to dupe it again. + } + l=l->next; + }while(l!=f->loopbase); + } + } + + //build new faces. grab edges from edge hash. + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(BME_SELECTED(f)){ + edar = MEM_callocN(sizeof(BMEdge*)*f->len,"Extrude array"); + v1 = BLI_ghash_lookup(vhash,f->loopbase->v); + v2 = BLI_ghash_lookup(vhash,f->loopbase->next->v); + for(i=0,l=f->loopbase; i < f->len; i++,l=l->next){ + ne = BLI_ghash_lookup(ehash,l->e); + edar[i] = ne; + } + nf=BME_MF(bm,v1,v2,edar,f->len); + nf->tflag2 = 1; // mark for select + if(del_old) f->tflag1 = 1; //mark for delete + MEM_freeN(edar); + } + } + BLI_memarena_free(edgearena); +} + +/*unified extrude code*/ +void BME_extrude_mesh(BME_Mesh *bm, int type){ + + BMVert *v; + BMEdge *e; + BMFace *f; + BME_Loop *l; + + struct GHash *vhash, *ehash; + /*Build a hash table of old pointers and new pointers.*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + ehash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + BME_selectmode_flush(bm); //ensure consistent selection. contains hack to make sure faces get consistent select. + if(type & BME_EXTRUDE_FACES){ //Find selected edges with more than one incident face that is also selected. deselect them. + for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){ + int totsel=0; + if(e->loop){ + l= e->loop; + do{ + if(BME_SELECTED(l->f)) totsel++; + l=BME_radial_nextloop(l); + }while(l!=e->loop); + } + if(totsel > 1) BME_select_edge(bm,e,0); + } + } + + /*another hack to ensure consistent selection.....*/ + for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){ + if(BME_SELECTED(e)) BME_select_edge(bm,e,1); + } + + /*now we are ready to extrude*/ + if(type & BME_EXTRUDE_VERTS) BME_extrude_verts(bm,vhash); + if(type & BME_EXTRUDE_EDGES) BME_extrude_skirt(bm,ehash); + if(type & BME_EXTRUDE_FACES) BME_cap_skirt(bm,vhash,ehash); + + /*clear all selection flags*/ + BME_clear_flag_all(bm, SELECT|BME_VISITED); + /*go through and fix up selection flags. Anything with BME_NEW should be selected*/ + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(f->tflag2 == 1) BME_select_poly(bm,f,1); + if(f->tflag1 == 1) BME_VISIT(f); //mark for delete + } + for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){ + if(e->tflag2 == 1) BME_select_edge(bm,e,1); + if(e->tflag1 == 1) BME_VISIT(e); // mark for delete + } + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(v->tflag2 == 1) BME_select_vert(bm,v,1); + if(v->tflag1 == 1) BME_VISIT(v); //mark for delete + } + /*go through and delete all of our old faces , edges and vertices.*/ + remove_tagged_polys(bm); + remove_tagged_edges(bm); + remove_tagged_verts(bm); + /*free our hash tables*/ + BLI_ghash_free(vhash,NULL, NULL); //check usage! + BLI_ghash_free(ehash,NULL, NULL); //check usage! + BME_selectmode_flush(bm); +} + diff --git a/source/blender/bmesh/tools/BME_weld.c b/source/blender/bmesh/tools/BME_weld.c new file mode 100644 index 00000000000..a17c07addbc --- /dev/null +++ b/source/blender/bmesh/tools/BME_weld.c @@ -0,0 +1,333 @@ +/* + * BME_WELD.C + * + * This file contains functions for welding + * elements in a mesh togather (remove doubles, + * collapse, ect). + * + * TODO: + * -Rewrite this to fit into the new API + * -Seperate out find doubles code and put it in + * BME_queries.c + * +*/ + + +/********* qsort routines *********/ + + +typedef struct xvertsort { + float x; + BMVert *v1; +} xvertsort; + +static int vergxco(const void *v1, const void *v2) +{ + const xvertsort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + +struct facesort { + unsigned long x; + struct BMFace *f; +}; + + +static int vergface(const void *v1, const void *v2) +{ + const struct facesort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + + + +/*break this into two functions.... 'find doubles' and 'remove doubles'?*/ + +static void BME_remove_doubles__splitface(BME_Mesh *bm,BMFace *f,GHash *vhash){ + BMVert *doub=NULL, *target=NULL; + BME_Loop *l; + BMFace *f2=NULL; + int split=0; + + l=f->loopbase; + do{ + if(l->v->tflag1 == 2){ + target = BLI_ghash_lookup(vhash,l->v); + if((BME_vert_in_face(target,f)) && (target != l->next->v) && (target != l->prev->v)){ + doub = l->v; + split = 1; + break; + } + } + + l= l->next; + }while(l!= f->loopbase); + + if(split){ + f2 = BME_SFME(bm,f,doub,target,NULL); + BME_remove_doubles__splitface(bm,f,vhash); + BME_remove_doubles__splitface(bm,f2,vhash); + } +} + +int BME_remove_doubles(BME_Mesh *bm, float limit) +{ + + /* all verts with (flag & 'flag') are being evaluated */ + BMVert *v, *v2, *target; + BMEdge *e, **edar, *ne; + BME_Loop *l; + BMFace *f, *nf; + xvertsort *sortblock, *sb, *sb1; + struct GHash *vhash; + struct facesort *fsortblock, *vsb, *vsb1; + int a, b, test, amount=0, found; + float dist; + + /*Build a hash table of doubles to thier target vert/edge.*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + /*count amount of selected vertices*/ + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(BME_SELECTED(v))amount++; + } + + /*qsort vertices based upon average of coordinate. We test this way first.*/ + sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub"); + + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(BME_SELECTED(v)){ + sb->x = v->co[0]+v->co[1]+v->co[2]; + sb->v1 = v; + sb++; + } + } + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + /* test for doubles */ + sb= sortblock; + for(a=0; a<amount; a++) { + v= sb->v1; + if(!(v->tflag1)) { //have we tested yet? + sb1= sb+1; + for(b=a+1; b<amount; b++) { + /* first test: simple distance. Simple way to discard*/ + dist= sb1->x - sb->x; + if(dist > limit) break; + + /* second test: have we already done this vertex? + (eh this should be swapped, simple equality test should be cheaper than math above... small savings + though) */ + v2= sb1->v1; + if(!(v2->tflag1)) { + dist= (float)fabs(v2->co[0]-v->co[0]); + if(dist<=limit) { + dist= (float)fabs(v2->co[1]-v->co[1]); + if(dist<=limit) { + dist= (float)fabs(v2->co[2]-v->co[2]); + if(dist<=limit) { + /*v2 is a double of v. We want to throw out v1 and relink everything to v*/ + BLI_ghash_insert(vhash,v2, v); + v->tflag1 = 1; //mark this vertex as a target + v->tflag2++; //increase user count for this vert. + v2->tflag1 = 2; //mark this vertex as a double. + BME_VISIT(v2); //mark for delete + } + } + } + } + sb1++; + } + } + sb++; + } + MEM_freeN(sortblock); + + + /*todo... figure out what this is for... + for(eve = em->verts.first; eve; eve=eve->next) + if((eve->f & flag) && (eve->f & 128)) + EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f); + */ + + /*We cannot collapse a vertex onto another vertex if they share a face and are not connected via a collapsable edge. + so to deal with this we simply find these offending vertices and split the faces. Note that this is not optimal, but works. + */ + + + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(!(BME_NEWELEM(f))){ + BME_remove_doubles__splitface(bm,f,vhash); + } + } + + for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){ + /*If either vertices of this edge are a double, we must mark it for removal and we create a new one.*/ + if(e->v1->tflag1 == 2 || e->v2->tflag1 == 2){ + v = v2 = NULL; + /*For each vertex in the edge, test to find out what it should equal now.*/ + if(e->v1->tflag1 == 2) v= BLI_ghash_lookup(vhash,e->v1); + else v = e->v1; + if(e->v2->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,e->v2); + else v2 = e->v2; + + /*small optimization, test to see if the edge needs to be rebuilt at all*/ + if((e->v1 != v) || (e->v2 != v2)){ /*will this always be true of collapsed edges?*/ + if(v == v2) e->tflag1 = 2; /*mark as a collapsed edge*/ + else if(!BME_disk_existedge(v,v2)) ne = BME_ME(bm,v,v2); + BME_VISIT(e); /*mark for delete*/ + } + } + } + + + /*need to remove double edges as well. To do this we decide on one edge to keep, and if its inserted into hash then we need to remove all other + edges incident upon and relink.*/ + /* + * REBUILD FACES + * + * Loop through double faces and if they have vertices that have been flagged, they need to be rebuilt. + * We do this by looking up the face + *rebuild faces. loop through original face, for each loop, if the edge it is attached to is marked for delete and has no + *other edge in the hash edge, then we know to skip that loop on face recreation. Simple. + */ + + /*1st loop through, just marking elements*/ + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ //insert bit here about double edges, mark with a flag (e->tflag2) so that we can nuke it later. + l = f->loopbase; + do{ + if(l->v->tflag1 == 2) f->tflag1 = 1; //double, mark for rebuild + if(l->e->tflag1 != 2) f->tflag2++; //count number of edges in the new face. + l=l->next; + }while(l!=f->loopbase); + } + + /*now go through and create new faces*/ + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(f->tflag1 && f->tflag2 < 3) BME_VISIT(f); //mark for delete + else if (f->tflag1 == 1){ /*is the face marked for rebuild*/ + edar = MEM_callocN(sizeof(BMEdge *)*f->tflag2,"Remove doubles face creation array."); + a=0; + l = f->loopbase; + do{ + v = l->v; + v2 = l->next->v; + if(l->v->tflag1 == 2) v = BLI_ghash_lookup(vhash,l->v); + if(l->next->v->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,l->next->v); + ne = BME_disk_existedge(v,v2); //use BME_disk_next_edgeflag here or something to find the edge that is marked as 'target'. + //add in call here to edge doubles hash array... then bobs your uncle. + if(ne){ + edar[a] = ne; + a++; + } + l=l->next; + }while(l!=f->loopbase); + + if(BME_vert_in_edge(edar[1],edar[0]->v2)){ + v = edar[0]->v1; + v2 = edar[0]->v2; + } + else{ + v = edar[0]->v2; + v2 = edar[0]->v1; + } + + nf = BME_MF(bm,v,v2,edar,f->tflag2); + + /*copy per loop data here*/ + if(nf){ + BME_VISIT(f); //mark for delete + } + MEM_freeN(edar); + } + } + + /*count amount of removed vert doubles*/ + a = 0; + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(v->tflag1 == 2) a++; + } + + /*free memory and return amount removed*/ + remove_tagged_polys(bm); + remove_tagged_edges(bm); + remove_tagged_verts(bm); + BLI_ghash_free(vhash,NULL, NULL); + BME_selectmode_flush(bm); + return a; +} + +static void BME_MeshWalk__collapsefunc(void *userData, BMEdge *applyedge){ + int index; + GHash *collected = userData; + index = BLI_ghash_size(collected); + if(!BLI_ghash_lookup(collected,applyedge->v1)){ + BLI_ghash_insert(collected,index,applyedge->v1); + index++; + } + if(!BLI_ghash_lookup(collected,applyedge->v2)){ + BLI_ghash_insert(collected,index,applyedge->v2); + } +} + +void BME_collapse_edges(BME_Mesh *bm){ + + BMVert *v, *cvert; + GHash *collected; + float min[3], max[3], cent[3]; + int size, i=0, j, num=0; + + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(!(BME_ISVISITED(v)) && v->edge){ + /*initiate hash table*/ + collected = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + /*do the walking.*/ + BME_MeshWalk(bm,v,BME_MeshWalk__collapsefunc,collected,BME_RESTRICTSELECT); + /*now loop through the hash table twice, once to calculate bounding box, second time to do the actual collapse*/ + size = BLI_ghash_size(collected); + /*initial values*/ + VECCOPY(min,v->co); + VECCOPY(max,v->co); + cent[0] = cent[1] = cent[2]=0; + for(i=0; i<size; i++){ + cvert = BLI_ghash_lookup(collected,i); + cent[0] = cent[0] + cvert->co[0]; + cent[1] = cent[1] + cvert->co[1]; + cent[2] = cent[2] + cvert->co[2]; + } + + cent[0] = cent[0] / size; + cent[1] = cent[1] / size; + cent[2] = cent[2] / size; + + for(i=0; i<size; i++){ + cvert = BLI_ghash_lookup(collected,i); + VECCOPY(cvert->co,cent); + num++; + } + /*free the hash table*/ + BLI_ghash_free(collected,NULL, NULL); + } + } + /*if any collapsed, call remove doubles*/ + if(num){ + //need to change selection mode here, OR do something else? Or does tool change selection mode? + //selectgrep + //first clear flags + BMEdge *e; + BMFace *f; + BME_clear_flag_all(bm,BME_VISITED); + for(v=BME_first(bm,BME_VERT); v; v=BME_next(bm,BME_VERT,v)) v->tflag1 = v->tflag2 = 0; + for(e=BME_first(bm,BME_EDGE); e; e=BME_next(bm,BME_EDGE,e)) e->tflag1 = e->tflag2 = 0; + for(f=BME_first(bm,BME_POLY); f; f=BME_next(bm,BME_POLY,f)) f->tflag1 = f->tflag2 = 0; + /*now call remove doubles*/ + BME_remove_doubles(bm,0.0000001); + } + BME_selectmode_flush(bm); +}
\ No newline at end of file diff --git a/source/blender/editors/mesh/SConscript b/source/blender/editors/mesh/SConscript index e99351e1a70..bd6d355d84c 100644 --- a/source/blender/editors/mesh/SConscript +++ b/source/blender/editors/mesh/SConscript @@ -7,5 +7,6 @@ incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' incs += ' #/intern/guardedalloc #intern/bmfont ../../gpu' incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern' +incs += ' ../../bmesh ' env.BlenderLib ( 'bf_editors_mesh', sources, Split(incs), [], libtype=['core'], priority=[45] ) diff --git a/source/blender/editors/mesh/editmesh_mods.c b/source/blender/editors/mesh/editmesh_mods.c index a49d1217946..d79d8003ff0 100644 --- a/source/blender/editors/mesh/editmesh_mods.c +++ b/source/blender/editors/mesh/editmesh_mods.c @@ -82,6 +82,7 @@ editmesh_mods.c, UI level access, no geometry changes #include "ED_mesh.h" #include "ED_screen.h" #include "ED_view3d.h" +#include "bmesh.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -3291,6 +3292,84 @@ void MESH_OT_de_select_all(wmOperatorType *ot) ot->poll= ED_operator_editmesh; } +static int bmesh_test_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + EditMesh *em= ((Mesh *)obedit->data)->edit_mesh; + EditMesh *em2; + BMesh *bm; + + bm = editmesh_to_bmesh(em); + +#if 1 + /*dissolve vert test*/ + { + BMOperator op; + BMOpSlot *eoutput, *foutput; + int i; + + BMO_Init_Op(&op, BMOP_DISSOLVE_VERTS); + BMO_HeaderFlag_To_Slot(bm, &op, BMOP_DISVERTS_VERTIN, BM_SELECT, BM_VERT); + BMO_Exec_Op(bm, &op); + + BMO_Finish_Op(bm, &op); + } +#endif + +#if 0 + /*triangulator test code*/ + { + BMOperator op; + BMOpSlot *eoutput, *foutput; + int i; + + BMO_Init_Op(&op, BMOP_TRIANGULATE); + BMO_HeaderFlag_To_Slot(bm, &op, BMOP_TRIANG_FACEIN, BM_SELECT, BM_FACE); + BMO_Exec_Op(bm, &op); + + eoutput = BMO_GetSlot(&op, BMOP_TRIANG_NEW_EDGES); + foutput = BMO_GetSlot(&op, BMOP_TRIANG_NEW_FACES); + + /*select new faces/edges*/ + for (i=0; i<eoutput->len; i++) { + BM_Select(bm, ((void**)eoutput->data.buf)[i], 1); + } + + for (i=0; i<foutput->len; i++) { + BM_Select(bm, ((void**)foutput->data.buf)[i], 1); + } + + BMO_Finish_Op(bm, &op); + + } +#endif + em2 = bmesh_to_editmesh(bm); + + /*free em's data, then copy the contents of the em2 struct + to em, then free the em2 struct.*/ + free_editMesh(em); + *em = *em2; + MEM_freeN(em2); + + BM_Free_Mesh(bm); + + BIF_undo_push("BMesh Test"); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW|ND_TRANSFORM|ND_GEOM_SELECT, obedit); + return OPERATOR_FINISHED; +} + +void MESH_OT_bmesh_test(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "bmesh test op"; + ot->idname= "MESH_OT_bmesh_test"; + + /* api callbacks */ + ot->exec= bmesh_test_exec; + ot->poll= ED_operator_editmesh; +} + /* ******************** **************** */ void EM_select_more(EditMesh *em) diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index a6d899e2451..cf4adc4a59c 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -34,6 +34,7 @@ struct bContext; struct wmOperatorType; +struct ViewContext; #define UVCOPY(t, s) memcpy(t, s, 2 * sizeof(float)); @@ -91,7 +92,7 @@ int editedge_containsVert(struct EditEdge *eed, struct EditVert *eve); int editface_containsVert(struct EditFace *efa, struct EditVert *eve); int editface_containsEdge(struct EditFace *efa, struct EditEdge *eed); -void em_setup_viewcontext(struct bContext *C, ViewContext *vc); +void em_setup_viewcontext(struct bContext *C, struct ViewContext *vc); void MESH_OT_separate(struct wmOperatorType *ot); @@ -169,6 +170,7 @@ extern struct EditFace *EM_face_from_faces(EditMesh *em, struct EditFace *efa1, /* ******************* editmesh_mods.c */ void MESH_OT_de_select_all(struct wmOperatorType *ot); +void MESH_OT_bmesh_test(struct wmOperatorType *ot); void MESH_OT_select_more(struct wmOperatorType *ot); void MESH_OT_select_less(struct wmOperatorType *ot); void MESH_OT_selectswap_mesh(struct wmOperatorType *ot); @@ -180,9 +182,7 @@ void MESH_OT_reveal_mesh(struct wmOperatorType *ot); void MESH_OT_righthandfaces(struct wmOperatorType *ot); void MESH_OT_select_linked_flat_faces(struct wmOperatorType *ot); void MESH_OT_select_sharp_edges(struct wmOperatorType *ot); - - -extern EditEdge *findnearestedge(ViewContext *vc, int *dist); +extern EditEdge *findnearestedge(struct ViewContext *vc, int *dist); extern void EM_automerge(int update); void editmesh_select_by_material(EditMesh *em, int index); void righthandfaces(EditMesh *em, int select); /* makes faces righthand turning */ @@ -198,7 +198,7 @@ void selectconnected_mesh_all(EditMesh *em); * if 0, unselected vertice are given the bias * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased */ -extern EditVert *findnearestvert(ViewContext *vc, int *dist, short sel, short strict); +extern EditVert *findnearestvert(struct ViewContext *vc, int *dist, short sel, short strict); /* ******************* editmesh_tools.c */ diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 892db9e4dbc..53c80708e9e 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -107,6 +107,7 @@ static void MESH_OT_add_duplicate(wmOperatorType *ot) void ED_operatortypes_mesh(void) { WM_operatortype_append(MESH_OT_de_select_all); + WM_operatortype_append(MESH_OT_bmesh_test); WM_operatortype_append(MESH_OT_select_more); WM_operatortype_append(MESH_OT_select_less); WM_operatortype_append(MESH_OT_selectswap_mesh); @@ -148,6 +149,7 @@ void ED_keymap_mesh(wmWindowManager *wm) /* selecting */ WM_keymap_add_item(keymap, "MESH_OT_de_select_all", AKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "MESH_OT_bmesh_test", JKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "MESH_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "MESH_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "MESH_OT_selectswap_mesh", IKEY, KM_PRESS, KM_CTRL, 0); diff --git a/source/gameengine/GameLogic/SCA_DelaySensor.cpp b/source/gameengine/GameLogic/SCA_DelaySensor.cpp index 4c97ca98d72..f15d4c7249f 100644 --- a/source/gameengine/GameLogic/SCA_DelaySensor.cpp +++ b/source/gameengine/GameLogic/SCA_DelaySensor.cpp @@ -158,7 +158,6 @@ PyParentObject SCA_DelaySensor::Parents[] = { }; PyMethodDef SCA_DelaySensor::Methods[] = { - //Deprecated functions ------> /* setProperty */ {"setDelay", (PyCFunction) SCA_DelaySensor::sPySetDelay, METH_VARARGS, (PY_METHODCHAR)SetDelay_doc}, {"setDuration", (PyCFunction) SCA_DelaySensor::sPySetDuration, METH_VARARGS, (PY_METHODCHAR)SetDuration_doc}, @@ -167,32 +166,13 @@ PyMethodDef SCA_DelaySensor::Methods[] = { {"getDelay", (PyCFunction) SCA_DelaySensor::sPyGetDelay, METH_NOARGS, (PY_METHODCHAR)GetDelay_doc}, {"getDuration", (PyCFunction) SCA_DelaySensor::sPyGetDuration, METH_NOARGS, (PY_METHODCHAR)GetDuration_doc}, {"getRepeat", (PyCFunction) SCA_DelaySensor::sPyGetRepeat, METH_NOARGS, (PY_METHODCHAR)GetRepeat_doc}, - //<----- Deprecated {NULL,NULL} //Sentinel }; -PyAttributeDef SCA_DelaySensor::Attributes[] = { - KX_PYATTRIBUTE_INT_RW("delay",0,100000,true,SCA_DelaySensor,m_delay), - KX_PYATTRIBUTE_INT_RW("duration",0,100000,true,SCA_DelaySensor,m_duration), - KX_PYATTRIBUTE_BOOL_RW("repeat",SCA_DelaySensor,m_repeat), - { NULL } //Sentinel -}; - PyObject* SCA_DelaySensor::_getattr(const STR_String& attr) { - PyObject* object = _getattr_self(Attributes, this, attr); - if (object != NULL) - return object; _getattr_up(SCA_ISensor); } -int SCA_DelaySensor::_setattr(const STR_String& attr, PyObject *value) { - int ret = _setattr_self(Attributes, this, attr, value); - if (ret >= 0) - return ret; - return SCA_ISensor::_setattr(attr, value); -} - - const char SCA_DelaySensor::SetDelay_doc[] = "setDelay(delay)\n" "\t- delay: length of the initial OFF period as number of frame\n" @@ -200,7 +180,6 @@ const char SCA_DelaySensor::SetDelay_doc[] = "\tSet the initial delay before the positive trigger\n"; PyObject* SCA_DelaySensor::PySetDelay(PyObject* self, PyObject* args, PyObject* kwds) { - ShowDeprecationWarning("setDelay()", "the delay property"); int delay; if(!PyArg_ParseTuple(args, "i", &delay)) { @@ -222,7 +201,6 @@ const char SCA_DelaySensor::SetDuration_doc[] = "\tIf > 0, a negative trigger is fired at the end of the ON pulse.\n"; PyObject* SCA_DelaySensor::PySetDuration(PyObject* self, PyObject* args, PyObject* kwds) { - ShowDeprecationWarning("setDuration()", "the duration property"); int duration; if(!PyArg_ParseTuple(args, "i", &duration)) { @@ -243,7 +221,6 @@ const char SCA_DelaySensor::SetRepeat_doc[] = "\tSet the sensor repeat mode\n"; PyObject* SCA_DelaySensor::PySetRepeat(PyObject* self, PyObject* args, PyObject* kwds) { - ShowDeprecationWarning("setRepeat()", "the repeat property"); int repeat; if(!PyArg_ParseTuple(args, "i", &repeat)) { @@ -258,7 +235,6 @@ const char SCA_DelaySensor::GetDelay_doc[] = "\tReturn the delay parameter value\n"; PyObject* SCA_DelaySensor::PyGetDelay(PyObject* self) { - ShowDeprecationWarning("getDelay()", "the delay property"); return PyInt_FromLong(m_delay); } @@ -267,7 +243,6 @@ const char SCA_DelaySensor::GetDuration_doc[] = "\tReturn the duration parameter value\n"; PyObject* SCA_DelaySensor::PyGetDuration(PyObject* self) { - ShowDeprecationWarning("getDuration()", "the duration property"); return PyInt_FromLong(m_duration); } @@ -276,7 +251,6 @@ const char SCA_DelaySensor::GetRepeat_doc[] = "\tReturn the repeat parameter value\n"; PyObject* SCA_DelaySensor::PyGetRepeat(PyObject* self) { - ShowDeprecationWarning("getRepeat()", "the repeat property"); return BoolToPyArg(m_repeat); } |