copying bmesh dir on its own from bmesh branch

This commit is contained in:
2012-02-19 18:31:04 +00:00
parent d6deca4e9d
commit afc56a0b10
54 changed files with 35884 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
# $Id: CMakeLists.txt 31746 2010-09-04 05:31:25Z joeedh $
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2006, Blender Foundation
# All rights reserved.
#
# The Original Code is: all of this file.
#
# Contributor(s): Jacques Beaurain.
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
intern
operators
../avi
../blenfont
../blenkernel
../blenlib
../blenloader
../editors/include
../editors/mesh
../gpu
../ikplugin
../imbuf
../makesdna
../makesrna
../modifiers
../nodes
../render/extern/include
../../../extern/glew/include
../../../intern/audaspace/intern
../../../intern/bsp/extern
../../../intern/decimation/extern
../../../intern/elbeem/extern
../../../intern/guardedalloc
../../../intern/iksolver/extern
../../../intern/memutil
../../../intern/mikktspace
../../../intern/opennl/extern
../../../intern/smoke/extern
# XXX - BAD LEVEL CALL WM_api.h
../../../source/blender/windowmanager
)
set(INC_SYS
${ZLIB_INCLUDE_DIRS}
)
set(SRC
operators/bmo_bevel.c
operators/bmo_connect.c
operators/bmo_create.c
operators/bmo_dissolve.c
operators/bmo_dupe.c
operators/bmo_edgesplit.c
operators/bmo_extrude.c
operators/bmo_join_triangles.c
operators/bmo_mesh_conv.c
operators/bmo_mirror.c
operators/bmo_primitive.c
operators/bmo_removedoubles.c
operators/bmo_subdivide.c
operators/bmo_subdivide.h
operators/bmo_triangulate.c
operators/bmo_utils.c
intern/bmesh_newcore.c
intern/bmesh_interp.c
intern/bmesh_iterators.c
intern/bmesh_iterators_inline.c
intern/bmesh_marking.c
intern/bmesh_mesh.c
intern/bmesh_mods.c
intern/bmesh_structure.h
intern/bmesh_construct.c
intern/bmesh_operators_private.h
intern/bmesh_structure.c
intern/bmesh_polygon.c
intern/bmesh_queries.c
intern/bmesh_opdefines.c
intern/bmesh_eulers.c
intern/bmesh_operators.c
intern/bmesh_private.h
intern/bmesh_walkers.c
intern/bmesh_walkers_impl.c
intern/bmesh_walkers_private.h
intern/bmesh_inline.c
tools/BME_bevel.c
bmesh.h
bmesh_class.h
bmesh_error.h
bmesh_iterators.h
bmesh_marking.h
bmesh_operator_api.h
bmesh_operators.h
bmesh_queries.h
bmesh_walkers.h
)
add_definitions(-DGLEW_STATIC)
if(WITH_LZO)
add_definitions(-DWITH_LZO)
list(APPEND INC_SYS
../../../extern/lzo/minilzo
)
endif()
if(WITH_LZMA)
add_definitions(-DWITH_LZMA)
list(APPEND INC_SYS
../../../extern/lzma
)
endif()
if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
endif()
blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}")
+40
View File
@@ -0,0 +1,40 @@
#!/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('../blenloader')
incs.append('../makesdna')
incs.append('../makesrna')
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 )
+380
View File
@@ -0,0 +1,380 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Geoffrey Bantle, Levi Schooley.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_H__
#define __BMESH_H__
/** \file blender/bmesh/bmesh.h
* \ingroup bmesh
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "DNA_listBase.h"
#include "DNA_customdata_types.h"
#include "BLI_utildefines.h"
#include "bmesh_class.h"
/*
* short introduction:
*
* the bmesh structure is a boundary representation, supporting non-manifold
* locally modifiable topology. the API is designed to allow clean, maintainable
* code, that never (or almost never) directly inspects the underlying structure.
*
* The API includes iterators, including many useful topological iterators;
* walkers, which walk over a mesh, without the risk of hitting the recursion
* limit; operators, which are logical, reusable mesh modules; topological
* modification functions (like split face, join faces, etc), which are used for
* topological manipulations; and some (not yet finished) geometric utility
* functions.
*
* some definitions:
*
* tool flags: private flags for tools. each operator has it's own private
* tool flag "layer", which it can use to flag elements.
* tool flags are also used by various other parts of the api.
* header flags: stores persistent flags, such as selection state, hide state,
* etc. be careful of touching these.
*/
/*forward declarations*/
struct BMesh;
struct BMVert;
struct BMEdge;
struct BMFace;
struct BMLoop;
struct BMOperator;
struct Mesh;
struct EditMesh;
/*
* 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 "header" flags/markings (sharp, seam, select, hidden, ect)
note that this is different from the "tool" flags.
* 3: Unique ID in the bmesh.
* 4: some elements for internal record keeping.
*
*/
/* BMHeader->htype (char) */
#define BM_VERT 1
#define BM_EDGE 2
#define BM_LOOP 4
#define BM_FACE 8
#define BM_ALL (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)
/* BMHeader->hflag (char) */
#define BM_ELEM_SELECT (1 << 0)
#define BM_ELEM_HIDDEN (1 << 1)
#define BM_ELEM_SEAM (1 << 2)
#define BM_ELEM_SMOOTH (1 << 3) /* used for faces and edges, note from the user POV,
* this is a sharp edge when disabled */
#define BM_ELEM_TAG (1 << 4) /* internal flag, used for ensuring correct normals
* during multires interpolation, and any other time
* when temp tagging is handy.
* always assume dirty & clear before use. */
/* we have 3 spare flags which is awesome but since we're limited to 8
* only add new flags with care! - campbell */
/* #define BM_ELEM_SPARE (1<<5) */
/* #define BM_ELEM_SPARE (1<<6) */
/* #define BM_ELEM_NONORMCALC (1<<7) */ /* UNUSED */
/* stub */
void bmesh_error(void);
/* Mesh Level Ops */
extern int bm_mesh_allocsize_default[4];
/* ob is needed by multires */
BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4]);
BMesh *BM_mesh_copy(BMesh *bmold);
void BM_mesh_free(BMesh *bm);
/* frees mesh, but not actual BMesh struct */
void BM_mesh_data_free(BMesh *bm);
void BM_mesh_normals_update(BMesh *bm);
/* Construction */
BMVert *BM_vert_create(BMesh *bm, const float co[3], const BMVert *example);
BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble);
BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble);
BMFace *BM_face_create_quad_tri_v(BMesh *bm,
BMVert **verts, int len,
const BMFace *example, const int nodouble);
/* easier to use version of BM_face_create_quad_tri_v.
* creates edges if necassary. */
BMFace *BM_face_create_quad_tri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
const BMFace *example, const int nodouble);
/* makes an ngon from an unordered list of edges. v1 and v2 must be the verts
* defining edges[0], and define the winding of the new face. */
BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble);
/* stuff for dealing with header flags */
BM_INLINE char BM_elem_flag_test(const void *element, const char hflag);
/* stuff for dealing with header flags */
BM_INLINE void BM_elem_flag_enable(void *element, const char hflag);
/* stuff for dealing with header flags */
BM_INLINE void BM_elem_flag_disable(void *element, const char hflag);
/* stuff for dealing BM_elem_flag_toggle header flags */
BM_INLINE void BM_elem_flag_toggle(void *element, const char hflag);
BM_INLINE void BM_elem_flag_merge(void *element_a, void *element_b);
/* notes on BM_elem_index_set(...) usage,
* Set index is sometimes abused as temp storage, other times we cant be
* sure if the index values are valid because certain operations have modified
* the mesh structure.
*
* To set the elements to valid indicies 'BM_mesh_elem_index_ensure' should be used
* rather then adding inline loops, however there are cases where we still
* set the index directly
*
* In an attempt to manage this, here are 3 tags Im adding to uses of
* 'BM_elem_index_set'
*
* - 'set_inline' -- since the data is already being looped over set to a
* valid value inline.
*
* - 'set_dirty!' -- intentionally sets the index to an invalid value,
* flagging 'bm->elem_index_dirty' so we dont use it.
*
* - 'set_ok' -- this is valid use since the part of the code is low level.
*
* - 'set_ok_invalid' -- set to -1 on purpose since this should not be
* used without a full array re-index, do this on
* adding new vert/edge/faces since they may be added at
* the end of the array.
*
* - 'set_loop' -- currently loop index values are not used used much so
* assume each case they are dirty.
* - campbell */
BM_INLINE void BM_elem_index_set(void *element, const int index);
BM_INLINE int BM_elem_index_get(const void *element);
/* todo */
BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts);
/* copies loop data from adjacent faces */
void BM_face_copy_shared(BMesh *bm, BMFace *f);
/* copies attributes, e.g. customdata, header flags, etc, from one element
* to another of the same type.*/
void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target);
/* Modification */
/* join two adjacent faces together along an edge. note that
* the faces must only be joined by on edge. e is the edge you
* wish to dissolve.*/
BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e);
/* generic, flexible join faces function; note that most everything uses
* this, including BM_faces_join_pair */
BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface);
/* split a face along two vertices. returns the newly made face, and sets
* the nl member to a loop in the newly created edge.*/
BMFace *BM_face_split(BMesh *bm, BMFace *f,
BMVert *v1, BMVert *v2,
struct BMLoop **nl, BMEdge *example);
/* these 2 functions are very similar */
BMEdge* BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, const int join_faces);
BMEdge* BM_vert_collapse_edges(BMesh *bm, BMEdge *ke, BMVert *kv);
/* splits an edge. ne is set to the new edge created. */
BMVert *BM_edge_split(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent);
/* split an edge multiple times evenly */
BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts);
/* connect two verts together, through a face they share. this function may
* be removed in the future. */
BMEdge *BM_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf);
/* rotates an edge topologically, either clockwise (if ccw=0) or counterclockwise
* (if ccw is 1). */
BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw);
/* Rip a single face from a vertex fan */
BMVert *BM_vert_rip(BMesh *bm, BMFace *sf, BMVert *sv);
/*updates a face normal*/
void BM_face_normal_update(BMesh *bm, BMFace *f);
void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3]);
/*updates face and vertex normals incident on an edge*/
void BM_edge_normals_update(BMesh *bm, BMEdge *e);
/*update a vert normal (but not the faces incident on it)*/
void BM_vert_normal_update(BMesh *bm, BMVert *v);
void BM_vert_normal_update_all(BMesh *bm, BMVert *v);
void BM_face_normal_flip(BMesh *bm, BMFace *f);
/*dissolves all faces around a vert, and removes it.*/
int BM_disk_dissolve(BMesh *bm, BMVert *v);
/* dissolves vert, in more situations then BM_disk_dissolve
* (e.g. if the vert is part of a wire edge, etc).*/
int BM_vert_dissolve(BMesh *bm, BMVert *v);
/* Projects co onto face f, and returns true if it is inside
* the face bounds. Note that this uses a best-axis projection
* test, instead of projecting co directly into f's orientation
* space, so there might be accuracy issues.*/
int BM_face_point_inside_test(BMesh *bm, BMFace *f, const float co[3]);
/* Interpolation */
/* projects target onto source for customdata interpolation. note: only
* does loop customdata. multires is handled. */
void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source);
/* projects a single loop, target, onto source for customdata interpolation. multires is handled.
* if do_vertex is true, target's vert data will also get interpolated.*/
void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
int do_vertex, int do_multires);
/* smoothes boundaries between multires grids, including some borders in adjacent faces */
void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f);
/* project the multires grid in target onto source's set of multires grids */
void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source);
void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source);
void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac);
void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, struct BMEdge *e1, const float fac);
void BM_data_layer_add(BMesh *em, CustomData *data, int type);
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name);
void BM_data_layer_free(BMesh *em, CustomData *data, int type);
void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n);
float BM_elem_float_data_get(struct CustomData *cd, void *element, int type);
void BM_elem_float_data_set(struct CustomData *cd, void *element, int type, const float val);
/* get the area of the face */
float BM_face_area_calc(BMesh *bm, BMFace *f);
/* computes the centroid of a face, using the center of the bounding box */
void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float center[3]);
/* computes the centroid of a face, using the mean average */
void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float center[3]);
void BM_mesh_select_mode_flush(BMesh *bm);
/* mode independant flushing up/down */
void BM_mesh_deselect_flush(BMesh *bm);
void BM_mesh_select_flush(BMesh *bm);
/* flag conversion funcs */
char BM_face_flag_from_mflag(const char mflag);
char BM_edge_flag_from_mflag(const short mflag);
char BM_vert_flag_from_mflag(const char mflag);
/* reverse */
char BM_face_flag_to_mflag(BMFace *f);
short BM_edge_flag_to_mflag(BMEdge *e);
char BM_vert_flag_to_mflag(BMVert *v);
/* convert MLoop*** in a bmface to mtface and mcol in
* an MFace*/
void BM_loops_to_corners(BMesh *bm, struct Mesh *me, int findex,
BMFace *f, int numTex, int numCol);
void BM_loop_kill(BMesh *bm, BMLoop *l);
void BM_face_kill(BMesh *bm, BMFace *f);
void BM_edge_kill(BMesh *bm, BMEdge *e);
void BM_vert_kill(BMesh *bm, BMVert *v);
/* kills all edges associated with f, along with any other faces containing
* those edges*/
void BM_face_edges_kill(BMesh *bm, BMFace *f);
/* kills all verts associated with f, along with any other faces containing
* those vertices*/
void BM_face_verts_kill(BMesh *bm, BMFace *f);
/*clear all data in bm*/
void BM_mesh_clear(BMesh *bm);
void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag);
void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func,
const char *msg_a, const char *msg_b);
BMVert *BM_vert_at_index(BMesh *bm, const int index);
BMEdge *BM_edge_at_index(BMesh *bm, const int index);
BMFace *BM_face_at_index(BMesh *bm, const int index);
/*start/stop edit*/
void bmesh_begin_edit(BMesh *bm, int flag);
void bmesh_end_edit(BMesh *bm, int flag);
#ifdef USE_BMESH_HOLES
# define BM_FACE_FIRST_LOOP(p) (((BMLoopList *)((p)->loops.first))->first)
#else
# define BM_FACE_FIRST_LOOP(p) ((p)->l_first)
#endif
/* size to use for static arrays when dealing with NGons,
* alloc after this limit is reached.
* this value is rather arbitrary */
#define BM_NGON_STACK_SIZE 32
/* avoid inf loop, this value is arbtrary
* but should not error on valid cases */
#define BM_LOOP_RADIAL_MAX 10000
#define BM_NGON_MAX 100000
/* include the rest of the API */
#include "bmesh_marking.h"
#include "bmesh_operator_api.h"
#include "bmesh_operators.h"
#include "bmesh_error.h"
#include "bmesh_queries.h"
#include "bmesh_iterators.h"
#include "bmesh_walkers.h"
#include "intern/bmesh_inline.c"
#ifdef __cplusplus
}
#endif
#endif /* __BMESH_H__ */
+190
View File
@@ -0,0 +1,190 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Geoffrey Bantle, Levi Schooley, Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_CLASS_H__
#define __BMESH_CLASS_H__
/** \file blender/bmesh/bmesh_class.h
* \ingroup bmesh
*/
/* bmesh data structures */
/* dissable holes for now, these are ifdef'd because they use more memory and cant be saved in DNA currently */
// define USE_BMESH_HOLES
struct BMesh;
struct BMVert;
struct BMEdge;
struct BMLoop;
struct BMFace;
struct BMFlagLayer;
struct BMLayerType;
struct BMSubClassLayer;
struct BLI_mempool;
struct Object;
/*note: it is very important for BMHeader to start with two
pointers. this is a requirement of mempool's method of
iteration.
*/
typedef struct BMHeader {
void *data; /* customdata layers */
int index; /* notes:
* - Use BM_elem_index_get/SetIndex macros for index
* - Unitialized to -1 so we can easily tell its not set.
* - Used for edge/vert/face, check BMesh.elem_index_dirty for valid index values,
* this is abused by various tools which set it dirty.
* - For loops this is used for sorting during tesselation. */
char htype; /* element geometric type (verts/edges/loops/faces) */
char hflag; /* this would be a CD layer, see below */
} BMHeader;
/* note: need some way to specify custom locations for custom data layers. so we can
* make them point directly into structs. and some way to make it only happen to the
* active layer, and properly update when switching active layers.*/
typedef struct BMVert {
BMHeader head;
struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
float co[3];
float no[3];
struct BMEdge *e;
} BMVert;
/* disk link structure, only used by edges */
typedef struct BMDiskLink {
struct BMEdge *next, *prev;
} BMDiskLink;
typedef struct BMEdge {
BMHeader head;
struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
struct BMVert *v1, *v2;
struct BMLoop *l;
/* disk cycle pointers */
BMDiskLink v1_disk_link, v2_disk_link;
} BMEdge;
typedef struct BMLoop {
BMHeader head;
/* notice no flags layer */
struct BMVert *v;
struct BMEdge *e;
struct BMFace *f;
struct BMLoop *radial_next, *radial_prev;
/* these were originally commented as private but are used all over the code */
/* can't use ListBase API, due to head */
struct BMLoop *next, *prev;
} BMLoop;
/* can cast BMFace/BMEdge/BMVert, but NOT BMLoop, since these dont have a flag layer */
typedef struct BMElemF {
BMHeader head;
struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
} BMElemF;
#ifdef USE_BMESH_HOLES
/* eventually, this structure will be used for supporting holes in faces */
typedef struct BMLoopList {
struct BMLoopList *next, *prev;
struct BMLoop *first, *last;
} BMLoopList;
#endif
typedef struct BMFace {
BMHeader head;
struct BMFlagLayer *oflags; /* an array of flags, mostly used by the operator stack */
int len; /*includes all boundary loops*/
#ifdef USE_BMESH_HOLES
int totbounds; /*total boundaries, is one plus the number of holes in the face*/
ListBase loops;
#else
BMLoop *l_first;
#endif
float no[3]; /*yes, we do store this here*/
short mat_nr;
} BMFace;
typedef struct BMFlagLayer {
short f, pflag; /* flags */
} BMFlagLayer;
typedef struct BMesh {
int totvert, totedge, totloop, totface;
int totvertsel, totedgesel, totfacesel;
/* flag index arrays as being dirty so we can check if they are clean and
* avoid looping over the entire vert/edge/face array in those cases.
* valid flags are - BM_VERT | BM_EDGE | BM_FACE.
* BM_LOOP isnt handled so far. */
char elem_index_dirty;
/*element pools*/
struct BLI_mempool *vpool, *epool, *lpool, *fpool;
/*operator api stuff*/
struct BLI_mempool *toolflagpool;
int stackdepth;
struct BMOperator *currentop;
CustomData vdata, edata, ldata, pdata;
#ifdef USE_BMESH_HOLES
struct BLI_mempool *looplistpool;
#endif
/* should be copy of scene select mode */
/* stored in BMEditMesh too, this is a bit confusing,
* make sure the're in sync!
* Only use when the edit mesh cant be accessed - campbell */
short selectmode;
/*ID of the shape key this bmesh came from*/
int shapenr;
int walkers, totflags;
ListBase selected, error_stack;
BMFace *act_face;
ListBase errorstack;
struct Object *ob; /* owner object */
int opflag; /* current operator flag */
} BMesh;
#define BM_VERT 1
#define BM_EDGE 2
#define BM_LOOP 4
#define BM_FACE 8
#endif /* __BMESH_CLASS_H__ */
+72
View File
@@ -0,0 +1,72 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_ERROR_H__
#define __BMESH_ERROR_H__
/** \file blender/bmesh/bmesh_error.h
* \ingroup bmesh
*/
/*----------- bmop error system ----------*/
/* pushes an error onto the bmesh error stack.
* if msg is null, then the default message for the errorcode is used.*/
void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg);
/* gets the topmost error from the stack.
* returns error code or 0 if no error.*/
int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op);
int BMO_error_occurred(BMesh *bm);
/* same as geterror, only pops the error off the stack as well */
int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op);
void BMO_error_clear(BMesh *bm);
#if 0
//this is meant for handling errors, like self-intersection test failures.
//it's dangerous to handle errors in general though, so disabled for now.
/* catches an error raised by the op pointed to by catchop.
* errorcode is either the errorcode, or BMERR_ALL for any
* error.*/
int BMO_error_catch_op(BMesh *bm, BMOperator *catchop, int errorcode, char **msg);
#endif
#define BM_ELEM_INDEX_VALIDATE(_bm, _msg_a, _msg_b) \
BM_mesh_elem_index_validate(_bm, __FILE__ ":" STRINGIFY(__LINE__), __func__, _msg_a, _msg_b)
/*------ error code defines -------*/
/*error messages*/
#define BMERR_SELF_INTERSECTING 1
#define BMERR_DISSOLVEDISK_FAILED 2
#define BMERR_CONNECTVERT_FAILED 3
#define BMERR_WALKER_FAILED 4
#define BMERR_DISSOLVEFACES_FAILED 5
#define BMERR_DISSOLVEVERTS_FAILED 6
#define BMERR_TESSELATION 7
#define BMERR_NONMANIFOLD 8
#define BMERR_INVALID_SELECTION 9
#define BMERR_MESH_ERROR 10
#endif /* __BMESH_ERROR_H__ */
+136
View File
@@ -0,0 +1,136 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_ITERATORS_H__
#define __BMESH_ITERATORS_H__
/** \file blender/bmesh/bmesh_iterators.h
* \ingroup bmesh
*/
/*
* 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.
*
*/
#include "BLI_mempool.h"
/* Defines for passing to BM_iter_new.
*
* "OF" can be substituted for "around"
* so BM_VERTS_OF_FACE means "vertices
* around a face."
*/
/* these iterator over all elements of a specific
* type in the mesh.*/
#define BM_VERTS_OF_MESH 1
#define BM_EDGES_OF_MESH 2
#define BM_FACES_OF_MESH 3
/*these are topological iterators.*/
#define BM_EDGES_OF_VERT 4
#define BM_FACES_OF_VERT 5
#define BM_LOOPS_OF_VERT 6
#define BM_FACES_OF_EDGE 7
#define BM_VERTS_OF_FACE 8
#define BM_EDGES_OF_FACE 9
#define BM_LOOPS_OF_FACE 10
/* returns elements from all boundaries, and returns
* the first element at the end to flag that we're entering
* a different face hole boundary*/
#define BM_ALL_LOOPS_OF_FACE 11
/* iterate through loops around this loop, which are fetched
* from the other faces in the radial cycle surrounding the
* input loop's edge.*/
#define BM_LOOPS_OF_LOOP 12
#define BM_LOOPS_OF_EDGE 13
#define BM_ITER(ele, iter, bm, itype, data) \
ele = BM_iter_new(iter, bm, itype, data); \
for ( ; ele; ele=BM_iter_step(iter))
#define BM_ITER_INDEX(ele, iter, bm, itype, data, indexvar) \
ele = BM_iter_new(iter, bm, itype, data); \
for (indexvar=0; ele; indexvar++, ele=BM_iter_step(iter))
/*Iterator Structure*/
typedef struct BMIter {
BLI_mempool_iter pooliter;
struct BMVert *firstvert, *nextvert, *vdata;
struct BMEdge *firstedge, *nextedge, *edata;
struct BMLoop *firstloop, *nextloop, *ldata, *l;
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 count;
char itype;
} BMIter;
void *BM_iter_at_index(struct BMesh *bm, const char htype, void *data, int index);
int BM_iter_as_array(struct BMesh *bm, const char htype, void *data, void **array, const int len);
/* private for bmesh_iterators_inline.c */
void bmiter__vert_of_mesh_begin(struct BMIter *iter);
void *bmiter__vert_of_mesh_step(struct BMIter *iter);
void bmiter__edge_of_mesh_begin(struct BMIter *iter);
void *bmiter__edge_of_mesh_step(struct BMIter *iter);
void bmiter__face_of_mesh_begin(struct BMIter *iter);
void *bmiter__face_of_mesh_step(struct BMIter *iter);
void bmiter__edge_of_vert_begin(struct BMIter *iter);
void *bmiter__edge_of_vert_step(struct BMIter *iter);
void bmiter__face_of_vert_begin(struct BMIter *iter);
void *bmiter__face_of_vert_step(struct BMIter *iter);
void bmiter__loop_of_vert_begin(struct BMIter *iter);
void *bmiter__loop_of_vert_step(struct BMIter *iter);
void bmiter__loops_of_edge_begin(struct BMIter *iter);
void *bmiter__loops_of_edge_step(struct BMIter *iter);
void bmiter__loops_of_loop_begin(struct BMIter *iter);
void *bmiter__loops_of_loop_step(struct BMIter *iter);
void bmiter__face_of_edge_begin(struct BMIter *iter);
void *bmiter__face_of_edge_step(struct BMIter *iter);
void bmiter__vert_of_face_begin(struct BMIter *iter);
void *bmiter__vert_of_face_step(struct BMIter *iter);
void bmiter__edge_of_face_begin(struct BMIter *iter);
void *bmiter__edge_of_face_step(struct BMIter *iter);
void bmiter__loop_of_face_begin(struct BMIter *iter);
void *bmiter__loop_of_face_step(struct BMIter *iter);
#include "intern/bmesh_iterators_inline.c"
#endif /* __BMESH_ITERATORS_H__ */
+75
View File
@@ -0,0 +1,75 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_MARKING_H__
#define __BMESH_MARKING_H__
/** \file blender/bmesh/bmesh_marking.h
* \ingroup bmesh
*/
typedef struct BMEditSelection
{
struct BMEditSelection *next, *prev;
void *data;
char htype;
} BMEditSelection;
/* geometry hiding code */
void BM_elem_hide_set(BMesh *bm, void *element, int hide);
void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide);
void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide);
void BM_face_hide_set(BMesh *bm, BMFace *f, int hide);
/* Selection code */
void BM_elem_select_set(struct BMesh *bm, void *element, int select);
/* use BM_elem_flag_test(ele, BM_ELEM_SELECT) to test selection */
void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag);
void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag);
/* individual element select functions, BM_elem_select_set is a shortcut for these
* that automatically detects which one to use*/
void BM_vert_select_set(struct BMesh *bm, struct BMVert *v, int select);
void BM_edge_select_set(struct BMesh *bm, struct BMEdge *e, int select);
void BM_face_select_set(struct BMesh *bm, struct BMFace *f, int select);
void BM_select_mode_set(struct BMesh *bm, int selectmode);
/* counts number of elements with flag set */
int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide);
/* edit selection stuff */
void BM_active_face_set(BMesh *em, BMFace *f);
BMFace *BM_active_face_get(BMesh *bm, int sloppy);
void BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese);
void BM_editselection_normal(float r_normal[3], BMEditSelection *ese);
void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese);
int BM_select_history_check(BMesh *bm, void *data);
void BM_select_history_remove(BMesh *bm, void *data);
void BM_select_history_store(BMesh *bm, void *data);
void BM_select_history_validate(BMesh *bm);
void BM_select_history_clear(BMesh *em);
#endif /* __BMESH_MARKING_H__ */
+555
View File
@@ -0,0 +1,555 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_OPERATOR_API_H__
#define __BMESH_OPERATOR_API_H__
/** \file blender/bmesh/bmesh_operator_api.h
* \ingroup bmesh
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "BLI_memarena.h"
#include "BLI_ghash.h"
#include "BKE_utildefines.h"
#include <stdarg.h>
#include <string.h> /* for memcpy */
/*
* operators represent logical, executable mesh modules. all topological
* operations involving a bmesh has to go through them.
*
* operators are nested, as are tool flags, which are private to an operator
* when it's executed. tool flags are allocated in layers, one per operator
* execution, and are used for all internal flagging a tool needs to do.
*
* each operator has a series of "slots," which can be of the following types:
* - simple numerical types
* - arrays of elements (e.g. arrays of faces).
* - hash mappings.
*
* each slot is identified by a slot code, as are each operator.
* operators, and their slots, are defined in bmesh_opdefines.c (with their
* execution functions prototyped in bmesh_operators_private.h), with all their
* operator code and slot codes defined in bmesh_operators.h. see
* bmesh_opdefines.c and the BMOpDefine struct for how to define new operators.
*
* in general, operators are fed arrays of elements, created using either
* BM_HeaderFlag_To_Slot or BM_Flag_To_Slot (or through one of the format
* specifyers in BMO_op_callf or BMO_op_initf). Note that multiple element
* types (e.g. faces and edges) can be fed to the same slot array. Operators
* act on this data, and possibly spit out data into output slots.
*
* some notes:
* - operators should never read from header flags (e.g. element->head.flag). for
* example, if you want an operator to only operate on selected faces, you
* should use BM_HeaderFlag_To_Slot to put the selected elements into a slot.
* - when you read from an element slot array or mapping, you can either tool-flag
* all the elements in it, or read them using an iterator APi (which is
* semantically similar to the iterator api in bmesh_iterators.h).
*/
struct GHashIterator;
/* slot type arrays are terminated by the last member
* having a slot type of 0.*/
#define BMO_OP_SLOT_SENTINEL 0
#define BMO_OP_SLOT_INT 1
#define BMO_OP_SLOT_FLT 2
#define BMO_OP_SLOT_PNT 3
#define BMO_OP_SLOT_MAT 4
#define BMO_OP_SLOT_VEC 7
/* after BMO_OP_SLOT_VEC, everything is
* dynamically allocated arrays. we
* leave a space in the identifiers
* for future growth.
*/
//it's very important this remain a power of two
#define BMO_OP_SLOT_ELEMENT_BUF 8
#define BMO_OP_SLOT_MAPPING 9
/* #define BMO_OP_SLOT_TOTAL_TYPES 10 */ /* not used yet */
/* please ignore all these structures, don't touch them in tool code, except
* for when your defining an operator with BMOpDefine.*/
typedef struct BMOpSlot{
int slottype;
int len;
int flag;
int index; /* index within slot array */
union {
int i;
float f;
void *p;
float vec[3];
void *buf;
GHash *ghash;
} data;
} BMOpSlot;
#define BMO_OP_MAX_SLOTS 16 /* way more than probably needed */
#ifdef slots
#undef slots
#endif
typedef struct BMOperator {
int type;
int slottype;
int needflag;
int flag;
struct BMOpSlot slots[BMO_OP_MAX_SLOTS];
void (*exec)(struct BMesh *bm, struct BMOperator *op);
MemArena *arena;
} BMOperator;
#define MAX_SLOTNAME 32
typedef struct BMOSlotType {
int type;
char name[MAX_SLOTNAME];
} BMOSlotType;
typedef struct BMOpDefine {
const char *name;
BMOSlotType slottypes[BMO_OP_MAX_SLOTS];
void (*exec)(BMesh *bm, BMOperator *op);
int flag;
} BMOpDefine;
/* BMOpDefine->flag */
#define BMO_OP_FLAG_UNTAN_MULTIRES 1 /*switch from multires tangent space to absolute coordinates*/
/* ensures consistent normals before operator execution,
* restoring the original ones windings/normals afterwards.
* keep in mind, this won't work if the input mesh isn't
* manifold.*/
#define BMO_OP_FLAG_RATIONALIZE_NORMALS 2
/*------------- Operator API --------------*/
/* data types that use pointers (arrays, etc) should never
* have it set directly. and never use BMO_slot_ptr_set to
* pass in a list of edges or any arrays, really.*/
void BMO_op_init(struct BMesh *bm, struct BMOperator *op, const char *opname);
/* executes an operator, pushing and popping a new tool flag
* layer as appropriate.*/
void BMO_op_exec(struct BMesh *bm, struct BMOperator *op);
/* finishes an operator (though note the operator's tool flag is removed
* after it finishes executing in BMO_op_exec).*/
void BMO_op_finish(struct BMesh *bm, struct BMOperator *op);
/* tool flag API. never, ever ever should tool code put junk in
* header flags (element->head.flag), nor should they use
* element->head.eflag1/eflag2. instead, use this api to set
* flags.
*
* if you need to store a value per element, use a
* ghash or a mapping slot to do it. */
/* flags 15 and 16 (1<<14 and 1<<15) are reserved for bmesh api use */
#define BMO_elem_flag_test(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f & (oflag))
#define BMO_elem_flag_enable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f |= (oflag))
#define BMO_elem_flag_disable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f &= ~(oflag))
#define BMO_elem_flag_toggle(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f ^= (oflag))
/* profiling showed a significant amount of time spent in BMO_elem_flag_test */
#if 0
void BMO_elem_flag_enable(struct BMesh *bm, void *element, const short oflag);
void BMO_elem_flag_disable(struct BMesh *bm, void *element, const short oflag);
int BMO_elem_flag_test(struct BMesh *bm, void *element, const short oflag);
#endif
/* count the number of elements with a specific flag.
* type can be a bitmask of BM_FACE, BM_EDGE, or BM_FACE. */
int BMO_mesh_flag_count(struct BMesh *bm, const short oflag, const char htype);
/*---------formatted operator initialization/execution-----------*/
/*
* this system is used to execute or initialize an operator,
* using a formatted-string system.
*
* for example, BMO_op_callf(bm, "del geom=%hf context=%d", BM_ELEM_SELECT, DEL_FACES);
* . . .will execute the delete operator, feeding in selected faces, deleting them.
*
* the basic format for the format string is:
* [operatorname] [slotname]=%[code] [slotname]=%[code]
*
* as in printf, you pass in one additional argument to the function
* for every code.
*
* the formatting codes are:
* %d - put int in slot
* %f - put float in slot
* %p - put pointer in slot
* %h[f/e/v] - put elements with a header flag in slot.
* the letters after %h define which element types to use,
* so e.g. %hf will do faces, %hfe will do faces and edges,
* %hv will do verts, etc. must pass in at least one
* element type letter.
* %f[f/e/v] - same as %h, except it deals with tool flags instead of
* header flags.
* %a[f/e/v] - pass all elements (of types specified by f/e/v) to the
* slot.
* %e - pass in a single element.
* %v - pointer to a float vector of length 3.
* %m[3/4] - matrix, 3/4 refers to the matrix size, 3 or 4. the
* corrusponding argument must be a pointer to
* a float matrix.
* %s - copy a slot from another op, instead of mapping to one
* argument, it maps to two, a pointer to an operator and
* a slot name.
*/
void BMO_push(BMesh *bm, BMOperator *op);
void BMO_pop(BMesh *bm);
/*executes an operator*/
int BMO_op_callf(BMesh *bm, const char *fmt, ...);
/* initializes, but doesn't execute an operator. this is so you can
* gain access to the outputs of the operator. note that you have
* to execute/finitsh (BMO_op_exec and BMO_op_finish) yourself. */
int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...);
/* va_list version, used to implement the above two functions,
* plus EDBM_CallOpf in bmeshutils.c. */
int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *fmt, va_list vlist);
/* test whether a named slot exists */
int BMO_slot_exists(struct BMOperator *op, const char *slotname);
/* get a pointer to a slot. this may be removed layer on from the public API. */
BMOpSlot *BMO_slot_get(struct BMOperator *op, const char *slotname);
/* copies the data of a slot from one operator to another. src and dst are the
* source/destination slot codes, respectively. */
void BMO_slot_copy(struct BMOperator *source_op, struct BMOperator *dest_op,
const char *src, const char *dst);
/* remove tool flagged elements */
void BMO_remove_tagged_faces(struct BMesh *bm, const short oflag);
void BMO_remove_tagged_edges(struct BMesh *bm, const short oflag);
void BMO_remove_tagged_verts(struct BMesh *bm, const short oflag);
/* take care, uses operator flag DEL_WIREVERT */
void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type);
/* del "context" slot values, used for operator too */
enum {
DEL_VERTS = 1,
DEL_EDGES,
DEL_ONLYFACES,
DEL_EDGESFACES,
DEL_FACES,
DEL_ALL ,
DEL_ONLYTAGGED
};
void BMO_op_flag_enable(struct BMesh *bm, struct BMOperator *op, const int op_flag);
void BMO_op_flag_disable(struct BMesh *bm, struct BMOperator *op, const int op_flag);
void BMO_slot_float_set(struct BMOperator *op, const char *slotname, const float f);
float BMO_slot_float_get(BMOperator *op, const char *slotname);
void BMO_slot_int_set(struct BMOperator *op, const char *slotname, const int i);
int BMO_slot_int_get(BMOperator *op, const char *slotname);
/* don't pass in arrays that are supposed to map to elements this way.
*
* so, e.g. passing in list of floats per element in another slot is bad.
* passing in, e.g. pointer to an editmesh for the conversion operator is fine
* though. */
void BMO_slot_ptr_set(struct BMOperator *op, const char *slotname, void *p);
void *BMO_slot_ptr_get(BMOperator *op, const char *slotname);
void BMO_slot_vec_set(struct BMOperator *op, const char *slotname, const float vec[3]);
void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3]);
/* only supports square mats */
/* size must be 3 or 4; this api is meant only for transformation matrices.
* note that internally the matrix is stored in 4x4 form, and it's safe to
* call whichever BMO_Get_Mat* function you want. */
void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size);
void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4]);
void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3]);
void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *op, const char htype, const short oflag);
/* puts every element of type type (which is a bitmask) with tool flag flag,
* into a slot. */
void BMO_slot_from_flag(struct BMesh *bm, struct BMOperator *op, const char *slotname,
const short oflag, const char htype);
/* tool-flags all elements inside an element slot array with flag flag. */
void BMO_slot_buffer_flag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
const short oflag, const char htype);
/* clears tool-flag flag from all elements inside a slot array. */
void BMO_slot_buffer_flag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
const short oflag, const char htype);
/* tool-flags all elements inside an element slot array with flag flag. */
void BMO_slot_buffer_hflag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
const char hflag, const char htype);
/* clears tool-flag flag from all elements inside a slot array. */
void BMO_slot_buffer_hflag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
const char hflag, const char htype);
/* puts every element of type type (which is a bitmask) with header flag
* flag, into a slot. note: ignores hidden elements (e.g. elements with
* header flag BM_ELEM_HIDDEN set).*/
void BMO_slot_from_hflag(struct BMesh *bm, struct BMOperator *op, const char *slotname,
const char hflag, const char htype);
/* counts number of elements inside a slot array. */
int BMO_slot_buf_count(struct BMesh *bm, struct BMOperator *op, const char *slotname);
int BMO_slot_map_count(struct BMesh *bm, struct BMOperator *op, const char *slotname);
/* Counts the number of edges with tool flag toolflag around
*/
int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag);
/* inserts a key/value mapping into a mapping slot. note that it copies the
* value, it doesn't store a reference to it. */
#if 0
BM_INLINE void BMO_slot_map_insert(BMesh *bm, BMOperator *op, const char *slotname,
void *element, void *data, int len);
/* inserts a key/float mapping pair into a mapping slot. */
BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname,
void *element, float val);
/* returns 1 if the specified pointer is in the map. */
BM_INLINE int BMO_slot_map_contains(BMesh *bm, BMOperator *op, const char *slotname, void *element);
/* returns a point to the value of a specific key. */
BM_INLINE void *BMO_slot_map_data_get(BMesh *bm, BMOperator *op, const char *slotname, void *element);
/* returns the float part of a key/float pair. */
BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname, void *element);
#endif
/* flags all elements in a mapping. note that the mapping must only have
* bmesh elements in it.*/
void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op,
const char *slotname, const short oflag);
/* pointer versoins of BMO_slot_map_float_get and BMO_slot_map_float_insert.
*
* do NOT use these for non-operator-api-allocated memory! instead
* use BMO_slot_map_data_get and BMO_slot_map_insert, which copies the data. */
#if 0
BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname, void *key, void *val);
BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname, void *key);
#endif
/* this part of the API is used to iterate over element buffer or
* mapping slots.
*
* for example, iterating over the faces in a slot is:
*
* BMOIter oiter;
* BMFace *f;
*
* f = BMO_iter_new(&oiter, bm, some_operator, "slotname", BM_FACE);
* for (; f; f=BMO_iter_step(&oiter)) {
* /do something with the face
* }
*
* another example, iterating over a mapping:
* BMOIter oiter;
* void *key;
* void *val;
*
* key = BMO_iter_new(&oiter, bm, some_operator, "slotname", 0);
* for (; key; key=BMO_iter_step(&oiter)) {
* val = BMO_iter_map_value(&oiter);
* //do something with the key/val pair
* //note that val is a pointer to the val data,
* //whether it's a float, pointer, whatever.
* //
* // so to get a pointer, for example, use:
* // *((void**)BMO_iter_map_value(&oiter));
* //or something like that.
* }
*/
/* contents of this structure are private,
* don't directly access. */
typedef struct BMOIter {
BMOpSlot *slot;
int cur; //for arrays
struct GHashIterator giter;
void *val;
char restrictmask; /* bitwise '&' with BMHeader.htype */
} BMOIter;
void *BMO_slot_elem_first(BMOperator *op, const char *slotname);
/* restrictmask restricts the iteration to certain element types
* (e.g. combination of BM_VERT, BM_EDGE, BM_FACE), if iterating
* over an element buffer (not a mapping).*/
void *BMO_iter_new(BMOIter *iter, BMesh *bm, BMOperator *op,
const char *slotname, const char restrictmask);
void *BMO_iter_step(BMOIter *iter);
/* returns a pointer to the key value when iterating over mappings.
* remember for pointer maps this will be a pointer to a pointer.*/
void *BMO_iter_map_value(BMOIter *iter);
/* use this for pointer mappings */
void *BMO_iter_map_value_p(BMOIter *iter);
/* use this for float mappings */
float BMO_iter_map_value_f(BMOIter *iter);
#define BMO_ITER(ele, iter, bm, op, slotname, restrict) \
ele = BMO_iter_new(iter, bm, op, slotname, restrict); \
for ( ; ele; ele=BMO_iter_step(iter))
/******************* Inlined Functions********************/
typedef void (*opexec)(struct BMesh *bm, struct BMOperator *op);
/* mappings map elements to data, which
* follows the mapping struct in memory. */
typedef struct BMOElemMapping {
BMHeader *element;
int len;
} BMOElemMapping;
extern const int BMO_OPSLOT_TYPEINFO[];
BM_INLINE void BMO_slot_map_insert(BMesh *UNUSED(bm), BMOperator *op, const char *slotname,
void *element, void *data, int len)
{
BMOElemMapping *mapping;
BMOpSlot *slot = BMO_slot_get(op, slotname);
/*sanity check*/
if (slot->slottype != BMO_OP_SLOT_MAPPING) {
return;
}
mapping = (BMOElemMapping *) BLI_memarena_alloc(op->arena, sizeof(*mapping) + len);
mapping->element = (BMHeader*) element;
mapping->len = len;
memcpy(mapping + 1, data, len);
if (!slot->data.ghash) {
slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash,
BLI_ghashutil_ptrcmp, "bmesh op");
}
BLI_ghash_insert(slot->data.ghash, element, mapping);
}
BM_INLINE void BMO_slot_map_int_insert(BMesh *bm, BMOperator *op, const char *slotname,
void *element, int val)
{
BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(int));
}
BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname,
void *element, float val)
{
BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(float));
}
BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname,
void *element, void *val)
{
BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(void*));
}
BM_INLINE int BMO_slot_map_contains(BMesh *UNUSED(bm), BMOperator *op, const char *slotname, void *element)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
/*sanity check*/
if (slot->slottype != BMO_OP_SLOT_MAPPING) return 0;
if (!slot->data.ghash) return 0;
return BLI_ghash_haskey(slot->data.ghash, element);
}
BM_INLINE void *BMO_slot_map_data_get(BMesh *UNUSED(bm), BMOperator *op, const char *slotname,
void *element)
{
BMOElemMapping *mapping;
BMOpSlot *slot = BMO_slot_get(op, slotname);
/*sanity check*/
if (slot->slottype != BMO_OP_SLOT_MAPPING) return NULL;
if (!slot->data.ghash) return NULL;
mapping = (BMOElemMapping *)BLI_ghash_lookup(slot->data.ghash, element);
if (!mapping) return NULL;
return mapping + 1;
}
BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname,
void *element)
{
float *val = (float*) BMO_slot_map_data_get(bm, op, slotname, element);
if (val) return *val;
return 0.0f;
}
BM_INLINE int BMO_slot_map_int_get(BMesh *bm, BMOperator *op, const char *slotname,
void *element)
{
int *val = (int*) BMO_slot_map_data_get(bm, op, slotname, element);
if (val) return *val;
return 0;
}
BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname,
void *element)
{
void **val = (void**) BMO_slot_map_data_get(bm, op, slotname, element);
if (val) return *val;
return NULL;
}
#ifdef __cplusplus
}
#endif
#endif /* __BMESH_OPERATOR_API_H__ */
+105
View File
@@ -0,0 +1,105 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_OPERATORS_H__
#define __BMESH_OPERATORS_H__
/** \file blender/bmesh/bmesh_operators.h
* \ingroup bmesh
*/
/*see comments in intern/bmesh_opdefines.c for documentation of specific operators*/
/*--------defines/enumerations for specific operators-------*/
/*quad innervert values*/
enum {
SUBD_INNERVERT,
SUBD_PATH,
SUBD_FAN,
SUBD_STRAIGHT_CUT
};
/* similar face selection slot values */
enum {
SIMFACE_MATERIAL = 201,
SIMFACE_IMAGE,
SIMFACE_AREA,
SIMFACE_PERIMETER,
SIMFACE_NORMAL,
SIMFACE_COPLANAR
};
/* similar edge selection slot values */
enum {
SIMEDGE_LENGTH = 101,
SIMEDGE_DIR,
SIMEDGE_FACE,
SIMEDGE_FACE_ANGLE,
SIMEDGE_CREASE,
SIMEDGE_SEAM,
SIMEDGE_SHARP
};
/* similar vertex selection slot values */
enum {
SIMVERT_NORMAL = 0,
SIMVERT_FACE,
SIMVERT_VGROUP
};
enum {
OPUVC_AXIS_X = 1,
OPUVC_AXIS_Y
};
enum {
DIRECTION_CW = 1,
DIRECTION_CCW
};
/* vertex path selection values */
enum {
VPATH_SELECT_EDGE_LENGTH = 0,
VPATH_SELECT_TOPOLOGICAL
};
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.*/
struct Object;
struct EditMesh;
#if 0
void BMO_dupe_from_flag(struct BMesh *bm, int etypeflag, const char hflag);
#endif
void BM_mesh_esubdivideflag(struct Object *obedit, BMesh *bm, int flag, float smooth,
float fractal, int beauty, int numcuts, int seltype,
int cornertype, int singleedge, int gridfill, int seed);
#endif /* __BMESH_OPERATORS_H__ */
+123
View File
@@ -0,0 +1,123 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_QUERIES_H__
#define __BMESH_QUERIES_H__
/** \file blender/bmesh/bmesh_queries.h
* \ingroup bmesh
*/
#include <stdio.h>
/* Queries */
/* counts number of elements of type type are in the mesh. */
int BM_mesh_elem_count(struct BMesh *bm, const char htype);
/*returns true if v is in f*/
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_vert_in_edge(struct BMEdge *e, struct BMVert *v);
int BM_verts_in_edge(struct BMVert *v1, struct BMVert *v2, struct BMEdge *e);
/*get opposing vert from v in edge e.*/
struct BMVert *BM_edge_other_vert(struct BMEdge *e, struct BMVert *v);
/*finds other loop that shares v with e's loop in f.*/
struct BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v);
/*returns the edge existing between v1 and v2, or NULL if there isn't one.*/
struct BMEdge *BM_edge_exists(struct BMVert *v1, struct BMVert *v2);
/*returns number of edges aroudn a vert*/
int BM_vert_edge_count(struct BMVert *v);
/*returns number of faces around an edge*/
int BM_edge_face_count(struct BMEdge *e);
/*returns number of faces around a vert.*/
int BM_vert_face_count(struct BMVert *v);
/*returns true if v is a wire vert*/
int BM_vert_is_wire(struct BMesh *bm, struct BMVert *v);
/*returns true if e is a wire edge*/
int BM_edge_is_wire(struct BMesh *bm, struct BMEdge *e);
/* returns FALSE if v is part of a non-manifold edge in the mesh,
* I believe this includes if it's part of both a wire edge and
* a face.*/
int BM_vert_is_manifold(struct BMesh *bm, struct BMVert *v);
/* returns FALSE if e is shared by more then two faces. */
int BM_edge_is_manifold(struct BMesh *bm, struct BMEdge *e);
/* returns true if e is a boundary edge, e.g. has only 1 face bordering it. */
int BM_edge_is_boundry(struct BMEdge *e);
/* returns angle of two faces surrounding an edge. note there must be
* exactly two faces sharing the edge.*/
float BM_edge_face_angle(struct BMesh *bm, struct BMEdge *e);
/* returns angle of two faces surrounding edges. note there must be
* exactly two edges sharing the vertex.*/
float BM_vert_edge_angle(struct BMesh *bm, struct BMVert *v);
/* checks overlapping of existing faces with the verts in varr. */
int BM_face_exists_overlap(struct BMesh *bm, struct BMVert **varr, int len, struct BMFace **existface);
/* checks if a face defined by varr already exists. */
int BM_face_exists(BMesh *bm, BMVert **varr, int len, BMFace **existface);
/* returns number of edges f1 and f2 share. */
int BM_face_share_edges(struct BMFace *f1, struct BMFace *f2);
/* returns number of faces e1 and e2 share. */
int BM_edge_share_faces(struct BMEdge *e1, struct BMEdge *e2);
/* returns bool 1/0 if the edges share a vertex */
int BM_edge_share_vert(struct BMEdge *e1, struct BMEdge *e2);
/* edge verts in winding order from face */
void BM_edge_ordered_verts(struct BMEdge *edge, struct BMVert **r_v1, struct BMVert **r_v2);
/* checks if a face is valid in the data structure */
int BM_face_validate(BMesh *bm, BMFace *face, FILE *err);
/* each pair of loops defines a new edge, a split. this function goes
* through and sets pairs that are geometrically invalid to null. a
* split is invalid, if it forms a concave angle or it intersects other
* edges in the face.*/
void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len);
#endif /* __BMESH_QUERIES_H__ */
+139
View File
@@ -0,0 +1,139 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_WALKERS_H__
#define __BMESH_WALKERS_H__
/** \file blender/bmesh/bmesh_walkers.h
* \ingroup bmesh
*/
#include "BLI_ghash.h"
/*
NOTE: do NOT modify topology while walking a mesh!
*/
typedef enum {
BMW_DEPTH_FIRST,
BMW_BREADTH_FIRST
} BMWOrder;
/*Walkers*/
typedef struct BMWalker {
void (*begin) (struct BMWalker *walker, void *start);
void *(*step) (struct BMWalker *walker);
void *(*yield)(struct BMWalker *walker);
int structsize;
BMWOrder order;
int valid_mask;
/* runtime */
int layer;
BMesh *bm;
BLI_mempool *worklist;
ListBase states;
short mask_vert;
short mask_edge;
short mask_loop;
short mask_face;
GHash *visithash;
int depth;
} BMWalker;
/* define to make BMW_init more clear */
#define BMW_MASK_NOP 0
/* initialize a walker. searchmask restricts some (not all) walkers to
* elements with a specific tool flag set. flags is specific to each walker.*/
void BMW_init(struct BMWalker *walker, BMesh *bm, int type,
short mask_vert, short mask_edge, short mask_loop, short mask_face,
int layer);
void *BMW_begin(BMWalker *walker, void *start);
void *BMW_step(struct BMWalker *walker);
void BMW_end(struct BMWalker *walker);
int BMW_current_depth(BMWalker *walker);
/*these are used by custom walkers*/
void *BMW_current_state(BMWalker *walker);
void *BMW_state_add(BMWalker *walker);
void BMW_state_remove(BMWalker *walker);
void *BMW_walk(BMWalker *walker);
void BMW_reset(BMWalker *walker);
/*
example of usage, walking over an island of tool flagged faces:
BMWalker walker;
BMFace *f;
BMW_init(&walker, bm, BMW_ISLAND, SOME_OP_FLAG);
f = BMW_begin(&walker, some_start_face);
for (; f; f=BMW_step(&walker)) {
//do something with f
}
BMW_end(&walker);
*/
enum {
/* walk over connected geometry. can restrict to a search flag,
* or not, it's optional.
*
* takes a vert as an arugment, and spits out edges, restrict flag acts
* on the edges as well. */
BMW_SHELL,
/*walk over an edge loop. search flag doesn't do anything.*/
BMW_LOOP,
BMW_FACELOOP,
BMW_EDGERING,
/* #define BMW_RING 2 */
/* walk over uv islands; takes a loop as input. restrict flag
* restricts the walking to loops whose vert has restrict flag set as a
* tool flag.
*
* the flag parameter to BMW_init maps to a loop customdata layer index.
*/
BMW_LOOPDATA_ISLAND,
/* walk over an island of flagged faces. note, that this doesn't work on
* non-manifold geometry. it might be better to rewrite this to extract
* boundary info from the island walker, rather then directly walking
* over the boundary. raises an error if it encouters nonmanifold
* geometry. */
BMW_ISLANDBOUND,
/* walk over all faces in an island of tool flagged faces. */
BMW_ISLAND,
/* walk from a vertex to all connected vertices. */
BMW_CONNECTED_VERTEX,
/* end of array index enum vals */
/* do not intitialze function pointers and struct size in BMW_init */
BMW_CUSTOM,
BMW_MAXWALKERS
};
/* use with BMW_init, so as not to confuse with restrict flags */
#define BMW_NIL_LAY 0
#endif /* __BMESH_WALKERS_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,773 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_construct.c
* \ingroup bmesh
*
* BM construction functions.
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.h"
#include "BLI_math.h"
#include "BKE_customdata.h"
#include "DNA_meshdata_types.h"
#include "bmesh.h"
#include "bmesh_private.h"
#define SELECT 1
/* prototypes */
static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMLoop *source_loop, BMLoop *target_loop);
/*
* 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
*/
BMFace *BM_face_create_quad_tri(BMesh *bm,
BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
const BMFace *example, const int nodouble)
{
BMVert *vtar[4] = {v1, v2, v3, v4};
return BM_face_create_quad_tri_v(bm, vtar, v4 ? 4 : 3, example, nodouble);
}
/* remove the edge array bits from this. Its not really needed? */
BMFace *BM_face_create_quad_tri_v(BMesh *bm, BMVert **verts, int len, const BMFace *example, const int nodouble)
{
BMEdge *edar[4] = {NULL};
BMFace *f = NULL;
int overlap = 0;
edar[0] = BM_edge_exists(verts[0], verts[1]);
edar[1] = BM_edge_exists(verts[1], verts[2]);
if (len == 4) {
edar[2] = BM_edge_exists(verts[2], verts[3]);
edar[3] = BM_edge_exists(verts[3], verts[0]);
}
else {
edar[2] = BM_edge_exists(verts[2], verts[0]);
}
if (nodouble) {
/* check if face exists or overlaps */
if (len == 4) {
overlap = BM_face_exists_overlap(bm, verts, len, &f);
}
else {
overlap = BM_face_exists_overlap(bm, verts, len, &f);
}
}
/* make new face */
if ((!f) && (!overlap)) {
if (!edar[0]) edar[0] = BM_edge_create(bm, verts[0], verts[1], NULL, FALSE);
if (!edar[1]) edar[1] = BM_edge_create(bm, verts[1], verts[2], NULL, FALSE);
if (len == 4) {
if (!edar[2]) edar[2] = BM_edge_create(bm, verts[2], verts[3], NULL, FALSE);
if (!edar[3]) edar[3] = BM_edge_create(bm, verts[3], verts[0], NULL, FALSE);
}
else {
if (!edar[2]) edar[2] = BM_edge_create(bm, verts[2], verts[0], NULL, FALSE);
}
f = BM_face_create(bm, verts, edar, len, FALSE);
if (example && f) {
BM_elem_attrs_copy(bm, bm, example, f);
}
}
return f;
}
/* copies face data from shared adjacent faces */
void BM_face_copy_shared(BMesh *bm, BMFace *f)
{
BMIter iter;
BMLoop *l, *l2;
if (!f) return;
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
for ( ; l; l = BM_iter_step(&iter)) {
l2 = l->radial_next;
if (l2 && l2 != l) {
if (l2->v == l->v) {
bm_loop_attrs_copy(bm, bm, l2, l);
}
else {
l2 = l2->next;
bm_loop_attrs_copy(bm, bm, l2, l);
}
}
}
}
/*
* BMESH MAKE NGON
*
* Attempts to make a new Ngon from a list of edges.
* If nodouble equals one, a check for overlaps or existing
*
* The edges are not required to be ordered, simply to to form
* a single closed loop as a whole
*
* Note that while this function will work fine when the edges
* are already sorted, if the edges are always going to be sorted,
* BM_face_create should be considered over this function as it
* avoids some unnecessary work.
*/
BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble)
{
BMEdge **edges2 = NULL;
BLI_array_staticdeclare(edges2, BM_NGON_STACK_SIZE);
BMVert **verts = NULL, *v;
BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
BMFace *f = NULL;
BMEdge *e;
BMVert *ev1, *ev2;
int i, /* j, */ v1found, reverse;
/* this code is hideous, yeek. I'll have to think about ways of
* cleaning it up. basically, it now combines the old BM_face_create_ngon
* _and_ the old bmesh_mf functions, so its kindof smashed together
* - joeedh */
if (!len || !v1 || !v2 || !edges || !bm)
return NULL;
/* put edges in correct order */
for (i = 0; i < len; i++) {
BM_ELEM_API_FLAG_ENABLE(edges[i], _FLAG_MF);
}
ev1 = edges[0]->v1;
ev2 = edges[0]->v2;
if (v1 == ev2) {
/* Swapping here improves performance and consistency of face
* structure in the special case that the edges are already in
* the correct order and winding */
SWAP(BMVert *, ev1, ev2);
}
BLI_array_append(verts, ev1);
v = ev2;
e = edges[0];
do {
BMEdge *e2 = e;
BLI_array_append(verts, v);
BLI_array_append(edges2, e);
do {
e2 = bmesh_disk_nextedge(e2, v);
if (e2 != e && BM_ELEM_API_FLAG_TEST(e2, _FLAG_MF)) {
v = BM_edge_other_vert(e2, v);
break;
}
} while (e2 != e);
if (e2 == e)
goto err; /* the edges do not form a closed loop */
e = e2;
} while (e != edges[0]);
if (BLI_array_count(edges2) != len) {
goto err; /* we didn't use all edges in forming the boundary loop */
}
/* ok, edges are in correct order, now ensure they are going
* in the correct direction */
v1found = reverse = FALSE;
for (i = 0; i < len; i++) {
if (BM_vert_in_edge(edges2[i], v1)) {
/* see if v1 and v2 are in the same edge */
if (BM_vert_in_edge(edges2[i], v2)) {
/* if v1 is shared by the *next* edge, then the winding
* is incorrect */
if (BM_vert_in_edge(edges2[(i + 1) % len], v1)) {
reverse = TRUE;
break;
}
}
v1found = TRUE;
}
if ((v1found == FALSE) && BM_vert_in_edge(edges2[i], v2)) {
reverse = TRUE;
break;
}
}
if (reverse) {
for (i = 0; i < len / 2; i++) {
v = verts[i];
verts[i] = verts[len - i - 1];
verts[len - i - 1] = v;
}
}
for (i = 0; i < len; i++) {
edges2[i] = BM_edge_exists(verts[i], verts[(i + 1) % len]);
if (!edges2[i]) {
goto err;
}
}
f = BM_face_create(bm, verts, edges2, len, nodouble);
/* clean up flags */
for (i = 0; i < len; i++) {
BM_ELEM_API_FLAG_DISABLE(edges2[i], _FLAG_MF);
}
BLI_array_free(verts);
BLI_array_free(edges2);
return f;
err:
for (i = 0; i < len; i++) {
BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF);
}
BLI_array_free(verts);
BLI_array_free(edges2);
return NULL;
}
/* 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 BMO_remove_tagged_faces(BMesh *bm, const short oflag)
{
BMFace *f;
BMIter iter;
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, f, oflag)) {
BM_face_kill(bm, f);
}
}
}
void BMO_remove_tagged_edges(BMesh *bm, const short oflag)
{
BMEdge *e;
BMIter iter;
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, e, oflag)) {
BM_edge_kill(bm, e);
}
}
}
void BMO_remove_tagged_verts(BMesh *bm, const short oflag)
{
BMVert *v;
BMIter iter;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, v, oflag)) {
BM_vert_kill(bm, v);
}
}
}
/*************************************************************/
/* 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....
*/
static void bmo_remove_tagged_context_verts(BMesh *bm, const short oflag)
{
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter verts;
BMIter edges;
BMIter faces;
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) {
if (BMO_elem_flag_test(bm, v, oflag)) {
/* Visit edge */
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_VERT, v); e; e = BM_iter_step(&edges))
BMO_elem_flag_enable(bm, e, oflag);
/* Visit face */
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_VERT, v); f; f = BM_iter_step(&faces))
BMO_elem_flag_enable(bm, f, oflag);
}
}
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
BMO_remove_tagged_verts(bm, oflag);
}
static void bmo_remove_tagged_context_edges(BMesh *bm, const short oflag)
{
BMEdge *e;
BMFace *f;
BMIter edges;
BMIter faces;
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
if (BMO_elem_flag_test(bm, e, oflag)) {
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_EDGE, e); f; f = BM_iter_step(&faces)) {
BMO_elem_flag_enable(bm, f, oflag);
}
}
}
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
}
#define DEL_WIREVERT (1 << 10)
/* warning, oflag applies to different types in some contexts,
* not just the type being removed */
void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type)
{
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter verts;
BMIter edges;
BMIter faces;
switch (type) {
case DEL_VERTS:
{
bmo_remove_tagged_context_verts(bm, oflag);
break;
}
case DEL_EDGES:
{
/* flush down to vert */
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
if (BMO_elem_flag_test(bm, e, oflag)) {
BMO_elem_flag_enable(bm, e->v1, oflag);
BMO_elem_flag_enable(bm, e->v2, oflag);
}
}
bmo_remove_tagged_context_edges(bm, oflag);
/* remove loose vertice */
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) {
if (BMO_elem_flag_test(bm, v, oflag) && (!(v->e)))
BMO_elem_flag_enable(bm, v, DEL_WIREVERT);
}
BMO_remove_tagged_verts(bm, DEL_WIREVERT);
break;
}
case DEL_EDGESFACES:
{
bmo_remove_tagged_context_edges(bm, oflag);
break;
}
case DEL_ONLYFACES:
{
BMO_remove_tagged_faces(bm, oflag);
break;
}
case DEL_ONLYTAGGED:
{
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
BMO_remove_tagged_verts(bm, oflag);
break;
}
case DEL_FACES:
{
/* go through and mark all edges and all verts of all faces for delet */
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
if (BMO_elem_flag_test(bm, f, oflag)) {
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges))
BMO_elem_flag_enable(bm, e, oflag);
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts))
BMO_elem_flag_enable(bm, v, oflag);
}
}
/* now go through and mark all remaining faces all edges for keeping */
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
if (!BMO_elem_flag_test(bm, f, oflag)) {
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) {
BMO_elem_flag_disable(bm, e, oflag);
}
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) {
BMO_elem_flag_disable(bm, v, oflag);
}
}
}
/* also mark all the vertices of remaining edges for keeping */
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
if (!BMO_elem_flag_test(bm, e, oflag)) {
BMO_elem_flag_disable(bm, e->v1, oflag);
BMO_elem_flag_disable(bm, e->v2, oflag);
}
}
/* now delete marked face */
BMO_remove_tagged_faces(bm, oflag);
/* delete marked edge */
BMO_remove_tagged_edges(bm, oflag);
/* remove loose vertice */
BMO_remove_tagged_verts(bm, oflag);
break;
}
case DEL_ALL:
{
/* does this option even belong in here? */
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces))
BMO_elem_flag_enable(bm, f, oflag);
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges))
BMO_elem_flag_enable(bm, e, oflag);
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts))
BMO_elem_flag_enable(bm, v, oflag);
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
BMO_remove_tagged_verts(bm, oflag);
break;
}
}
}
/*************************************************************/
static void bm_vert_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMVert *source_vertex, BMVert *target_vertex)
{
if ((source_mesh == target_mesh) && (source_vertex == target_vertex)) {
return;
}
copy_v3_v3(target_vertex->no, source_vertex->no);
CustomData_bmesh_free_block(&target_mesh->vdata, &target_vertex->head.data);
CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata,
source_vertex->head.data, &target_vertex->head.data);
}
static void bm_edge_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMEdge *source_edge, BMEdge *target_edge)
{
if ((source_mesh == target_mesh) && (source_edge == target_edge)) {
return;
}
CustomData_bmesh_free_block(&target_mesh->edata, &target_edge->head.data);
CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata,
source_edge->head.data, &target_edge->head.data);
}
static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMLoop *source_loop, BMLoop *target_loop)
{
if ((source_mesh == target_mesh) && (source_loop == target_loop)) {
return;
}
CustomData_bmesh_free_block(&target_mesh->ldata, &target_loop->head.data);
CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata,
source_loop->head.data, &target_loop->head.data);
}
static void bm_face_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMFace *source_face, BMFace *target_face)
{
if ((source_mesh == target_mesh) && (source_face == target_face)) {
return;
}
copy_v3_v3(target_face->no, source_face->no);
CustomData_bmesh_free_block(&target_mesh->pdata, &target_face->head.data);
CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata,
source_face->head.data, &target_face->head.data);
target_face->mat_nr = source_face->mat_nr;
}
/* BMESH_TODO: Special handling for hide flags? */
void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target)
{
const BMHeader *sheader = source;
BMHeader *theader = target;
if (sheader->htype != theader->htype)
return;
/* First we copy select */
if (BM_elem_flag_test(source, BM_ELEM_SELECT)) BM_elem_select_set(target_mesh, target, TRUE);
/* Now we copy flags */
theader->hflag = sheader->hflag;
/* Copy specific attributes */
if (theader->htype == BM_VERT)
bm_vert_attrs_copy(source_mesh, target_mesh, (const BMVert *)source, (BMVert *)target);
else if (theader->htype == BM_EDGE)
bm_edge_attrs_copy(source_mesh, target_mesh, (const BMEdge *)source, (BMEdge *)target);
else if (theader->htype == BM_LOOP)
bm_loop_attrs_copy(source_mesh, target_mesh, (const BMLoop *)source, (BMLoop *)target);
else if (theader->htype == BM_FACE)
bm_face_attrs_copy(source_mesh, target_mesh, (const BMFace *)source, (BMFace *)target);
}
BMesh *BM_mesh_copy(BMesh *bmold)
{
BMesh *bm;
BMVert *v, *v2, **vtable = NULL;
BMEdge *e, *e2, **edges = NULL, **etable = NULL;
BLI_array_declare(edges);
BMLoop *l, /* *l2, */ **loops = NULL;
BLI_array_declare(loops);
BMFace *f, *f2, **ftable = NULL;
BMEditSelection *ese;
BMIter iter, liter;
int i, j;
/* allocate a bmesh */
bm = BM_mesh_create(bmold->ob, bm_mesh_allocsize_default);
CustomData_copy(&bmold->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&bmold->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&bmold->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&bmold->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_bmesh_init_pool(&bm->vdata, bm_mesh_allocsize_default[0]);
CustomData_bmesh_init_pool(&bm->edata, bm_mesh_allocsize_default[1]);
CustomData_bmesh_init_pool(&bm->ldata, bm_mesh_allocsize_default[2]);
CustomData_bmesh_init_pool(&bm->pdata, bm_mesh_allocsize_default[3]);
vtable = MEM_mallocN(sizeof(BMVert *) * bmold->totvert, "BM_mesh_copy vtable");
etable = MEM_mallocN(sizeof(BMEdge *) * bmold->totedge, "BM_mesh_copy etable");
ftable = MEM_mallocN(sizeof(BMFace *) * bmold->totface, "BM_mesh_copy ftable");
v = BM_iter_new(&iter, bmold, BM_VERTS_OF_MESH, NULL);
for (i = 0; v; v = BM_iter_step(&iter), i++) {
v2 = BM_vert_create(bm, v->co, NULL); /* copy between meshes so cant use 'example' argument */
BM_elem_attrs_copy(bmold, bm, v, v2);
vtable[i] = v2;
BM_elem_index_set(v, i); /* set_inline */
BM_elem_index_set(v2, i); /* set_inline */
}
bmold->elem_index_dirty &= ~BM_VERT;
bm->elem_index_dirty &= ~BM_VERT;
/* safety check */
BLI_assert(i == bmold->totvert);
e = BM_iter_new(&iter, bmold, BM_EDGES_OF_MESH, NULL);
for (i = 0; e; e = BM_iter_step(&iter), i++) {
e2 = BM_edge_create(bm,
vtable[BM_elem_index_get(e->v1)],
vtable[BM_elem_index_get(e->v2)],
e, FALSE);
BM_elem_attrs_copy(bmold, bm, e, e2);
etable[i] = e2;
BM_elem_index_set(e, i); /* set_inline */
BM_elem_index_set(e2, i); /* set_inline */
}
bmold->elem_index_dirty &= ~BM_EDGE;
bm->elem_index_dirty &= ~BM_EDGE;
/* safety check */
BLI_assert(i == bmold->totedge);
f = BM_iter_new(&iter, bmold, BM_FACES_OF_MESH, NULL);
for (i = 0; f; f = BM_iter_step(&iter), i++) {
BM_elem_index_set(f, i); /* set_inline */
BLI_array_empty(loops);
BLI_array_empty(edges);
BLI_array_growitems(loops, f->len);
BLI_array_growitems(edges, f->len);
l = BM_iter_new(&liter, bmold, BM_LOOPS_OF_FACE, f);
for (j = 0; j < f->len; j++, l = BM_iter_step(&liter)) {
loops[j] = l;
edges[j] = etable[BM_elem_index_get(l->e)];
}
v = vtable[BM_elem_index_get(loops[0]->v)];
v2 = vtable[BM_elem_index_get(loops[1]->v)];
if (!bmesh_verts_in_edge(v, v2, edges[0])) {
v = vtable[BM_elem_index_get(loops[BLI_array_count(loops) - 1]->v)];
v2 = vtable[BM_elem_index_get(loops[0]->v)];
}
f2 = BM_face_create_ngon(bm, v, v2, edges, f->len, FALSE);
if (!f2)
continue;
/* use totface incase adding some faces fails */
BM_elem_index_set(f2, (bm->totface - 1)); /* set_inline */
ftable[i] = f2;
BM_elem_attrs_copy(bmold, bm, f, f2);
copy_v3_v3(f2->no, f->no);
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f2);
for (j = 0; j < f->len; j++, l = BM_iter_step(&liter)) {
BM_elem_attrs_copy(bmold, bm, loops[j], l);
}
if (f == bmold->act_face) bm->act_face = f2;
}
bmold->elem_index_dirty &= ~BM_FACE;
bm->elem_index_dirty &= ~BM_FACE;
/* safety check */
BLI_assert(i == bmold->totface);
/* copy over edit selection history */
for (ese = bmold->selected.first; ese; ese = ese->next) {
void *ele = NULL;
if (ese->htype == BM_VERT)
ele = vtable[BM_elem_index_get(ese->data)];
else if (ese->htype == BM_EDGE)
ele = etable[BM_elem_index_get(ese->data)];
else if (ese->htype == BM_FACE) {
ele = ftable[BM_elem_index_get(ese->data)];
}
else {
BLI_assert(0);
}
if (ele)
BM_select_history_store(bm, ele);
}
MEM_freeN(etable);
MEM_freeN(vtable);
MEM_freeN(ftable);
BLI_array_free(loops);
BLI_array_free(edges);
return bm;
}
/* ME -> BM */
char BM_vert_flag_from_mflag(const char meflag)
{
return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) |
((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)
);
}
char BM_edge_flag_from_mflag(const short meflag)
{
return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) |
((meflag & ME_SEAM) ? BM_ELEM_SEAM : 0) |
((meflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */
((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)
);
}
char BM_face_flag_from_mflag(const char meflag)
{
return ( ((meflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) |
((meflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) |
((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)
);
}
/* BM -> ME */
char BM_vert_flag_to_mflag(BMVert *eve)
{
const char hflag = eve->head.hflag;
return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) |
((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)
);
}
short BM_edge_flag_to_mflag(BMEdge *eed)
{
const char hflag = eed->head.hflag;
return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) |
((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) |
((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) |
((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) |
((BM_edge_is_wire(NULL, eed)) ? ME_LOOSEEDGE : 0) | /* not typical */
(ME_EDGEDRAW | ME_EDGERENDER)
);
}
char BM_face_flag_to_mflag(BMFace *efa)
{
const char hflag = efa->head.hflag;
return ( ((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) |
((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) |
((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)
);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_inline.c
* \ingroup bmesh
*
* BM Inline functions.
*/
#ifndef __BMESH_INLINE_C__
#define __BMESH_INLINE_C__
#include "bmesh.h"
BM_INLINE char BM_elem_flag_test(const void *element, const char hflag)
{
return ((const BMHeader *)element)->hflag & hflag;
}
BM_INLINE void BM_elem_flag_enable(void *element, const char hflag)
{
((BMHeader *)element)->hflag |= hflag;
}
BM_INLINE void BM_elem_flag_disable(void *element, const char hflag)
{
((BMHeader *)element)->hflag &= ~hflag;
}
BM_INLINE void BM_elem_flag_toggle(void *element, const char hflag)
{
((BMHeader *)element)->hflag ^= hflag;
}
BM_INLINE void BM_elem_flag_merge(void *element_a, void *element_b)
{
((BMHeader *)element_a)->hflag =
((BMHeader *)element_b)->hflag = (((BMHeader *)element_a)->hflag |
((BMHeader *)element_b)->hflag);
}
BM_INLINE void BM_elem_index_set(void *element, const int index)
{
((BMHeader *)element)->index = index;
}
BM_INLINE int BM_elem_index_get(const void *element)
{
return ((BMHeader *)element)->index;
}
#endif /* __BMESH_INLINE_C__ */
+984
View File
@@ -0,0 +1,984 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_interp.c
* \ingroup bmesh
*
* Functions for interpolating data across the surface of a mesh.
*/
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_customdata.h"
#include "BKE_multires.h"
#include "BLI_array.h"
#include "BLI_math.h"
#include "bmesh.h"
#include "bmesh_private.h"
/**
* bmesh_data_interp_from_verts
*
* Interpolates per-vertex data from two sources to a target.
*/
void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac)
{
if (v1->head.data && v2->head.data) {
/* first see if we can avoid interpolation */
if (fac <= 0.0f) {
if (v1 == v) {
/* do nothing */
}
else {
CustomData_bmesh_free_block(&bm->vdata, &v->head.data);
CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v1->head.data, &v->head.data);
}
}
else if (fac >= 1.0f) {
if (v2 == v) {
/* do nothing */
}
else {
CustomData_bmesh_free_block(&bm->vdata, &v->head.data);
CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v2->head.data, &v->head.data);
}
}
else {
void *src[2];
float w[2];
src[0] = v1->head.data;
src[1] = v2->head.data;
w[0] = 1.0f-fac;
w[1] = fac;
CustomData_bmesh_interp(&bm->vdata, src, w, NULL, 2, v->head.data);
}
}
}
/*
* BM Data Vert Average
*
* Sets all the customdata (e.g. vert, loop) associated with a vert
* to the average of the face regions surrounding it.
*/
static void UNUSED_FUNCTION(BM_Data_Vert_Average)(BMesh *UNUSED(bm), BMFace *UNUSED(f))
{
// BMIter iter;
}
/**
* 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_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BMVert *v, BMEdge *e1, const float fac)
{
void *src[2];
float w[2];
BMLoop *v1loop = NULL, *vloop = NULL, *v2loop = NULL;
BMLoop *l_iter = NULL;
if (!e1->l) {
return;
}
w[1] = 1.0f - fac;
w[0] = fac;
l_iter = e1->l;
do {
if (l_iter->v == v1) {
v1loop = l_iter;
vloop = v1loop->next;
v2loop = vloop->next;
}
else if (l_iter->v == v) {
v1loop = l_iter->next;
vloop = l_iter;
v2loop = l_iter->prev;
}
if (!v1loop || !v2loop)
return;
src[0] = v1loop->head.data;
src[1] = v2loop->head.data;
CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, vloop->head.data);
} while ((l_iter = l_iter->radial_next) != e1->l);
}
void BM_loops_to_corners(BMesh *bm, Mesh *me, int findex,
BMFace *f, int numTex, int numCol)
{
BMLoop *l;
BMIter iter;
MTFace *texface;
MTexPoly *texpoly;
MCol *mcol;
MLoopCol *mloopcol;
MLoopUV *mloopuv;
int i, j;
for (i = 0; i < numTex; i++) {
texface = CustomData_get_n(&me->fdata, CD_MTFACE, findex, i);
texpoly = CustomData_bmesh_get_n(&bm->pdata, f->head.data, CD_MTEXPOLY, i);
ME_MTEXFACE_CPY(texface, texpoly);
j = 0;
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i);
copy_v2_v2(texface->uv[j], mloopuv->uv);
j++;
}
}
for (i = 0; i < numCol; i++) {
mcol = CustomData_get_n(&me->fdata, CD_MCOL, findex, i);
j = 0;
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->head.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++;
}
}
}
/**
* BM_data_interp_from_face
*
* projects target onto source, and pulls interpolated customdata from
* source.
*
* Returns -
* Nothing
*/
void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source)
{
BMLoop *l_iter;
BMLoop *l_first;
void **blocks = NULL;
float (*cos)[3] = NULL, *w = NULL;
BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__);
BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__);
BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__);
int i;
BM_elem_attrs_copy(bm, bm, source, target);
i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(source);
do {
copy_v3_v3(cos[i], l_iter->v->co);
blocks[i] = l_iter->head.data;
i++;
} while ((l_iter = l_iter->next) != l_first);
i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(target);
do {
interp_weights_poly_v3(w, cos, source->len, l_iter->v->co);
CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, l_iter->head.data);
i++;
} while ((l_iter = l_iter->next) != l_first);
BLI_array_fixedstack_free(cos);
BLI_array_fixedstack_free(w);
BLI_array_fixedstack_free(blocks);
}
/* some math stuff for dealing with doubles, put here to
* avoid merge errors - joeedh */
#define VECMUL(a, b) (((a)[0] = (a)[0] * (b)), ((a)[1] = (a)[1] * (b)), ((a)[2] = (a)[2] * (b)))
#define VECADD2(a, b) (((a)[0] = (a)[0] + (b)[0]), ((a)[1] = (a)[1] + (b)[1]), ((a)[2] = (a)[2] + (b)[2]))
#define VECSUB2(a, b) (((a)[0] = (a)[0] - (b)[0]), ((a)[1] = (a)[1] - (b)[1]), ((a)[2] = (a)[2] - (b)[2]))
/* find closest point to p on line through l1, l2 and return lambda,
* where (0 <= lambda <= 1) when cp is in the line segement l1, l2
*/
static double closest_to_line_v3_d(double cp[3], const double p[3], const double l1[3], const double l2[3])
{
double h[3], u[3], lambda;
VECSUB(u, l2, l1);
VECSUB(h, p, l1);
lambda = INPR(u, h) / INPR(u, u);
cp[0] = l1[0] + u[0] * lambda;
cp[1] = l1[1] + u[1] * lambda;
cp[2] = l1[2] + u[2] * lambda;
return lambda;
}
/* point closest to v1 on line v2-v3 in 3D */
static void UNUSED_FUNCTION(closest_to_line_segment_v3_d)(double *closest, double v1[3], double v2[3], double v3[3])
{
double lambda, cp[3];
lambda = closest_to_line_v3_d(cp, v1, v2, v3);
if (lambda <= 0.0) {
VECCOPY(closest, v2);
}
else if (lambda >= 1.0) {
VECCOPY(closest, v3);
}
else {
VECCOPY(closest, cp);
}
}
static double UNUSED_FUNCTION(len_v3_d)(const double a[3])
{
return sqrt(INPR(a, a));
}
static double UNUSED_FUNCTION(len_v3v3_d)(const double a[3], const double b[3])
{
double d[3];
VECSUB(d, b, a);
return sqrt(INPR(d, d));
}
static void cent_quad_v3_d(double *cent, double *v1, double *v2, double *v3, double *v4)
{
cent[0] = 0.25 * (v1[0] + v2[0] + v3[0] + v4[0]);
cent[1] = 0.25 * (v1[1] + v2[1] + v3[1] + v4[1]);
cent[2] = 0.25 * (v1[2] + v2[2] + v3[2] + v4[2]);
}
static void UNUSED_FUNCTION(cent_tri_v3_d)(double *cent, double *v1, double *v2, double *v3)
{
cent[0] = (1.0 / 3.0) * (v1[0] + v2[0] + v3[0]);
cent[1] = (1.0 / 3.0) * (v1[1] + v2[1] + v3[1]);
cent[2] = (1.0 / 3.0) * (v1[2] + v2[2] + v3[2]);
}
static void UNUSED_FUNCTION(cross_v3_v3v3_d)(double r[3], const double a[3], const double b[3])
{
r[0] = a[1] * b[2] - a[2] * b[1];
r[1] = a[2] * b[0] - a[0] * b[2];
r[2] = a[0] * b[1] - a[1] * b[0];
}
/* distance v1 to line-piece v2-v3 */
static double UNUSED_FUNCTION(dist_to_line_segment_v2_d)(double v1[3], double v2[3], double v3[3])
{
double labda, rc[2], pt[2], len;
rc[0] = v3[0] - v2[0];
rc[1] = v3[1] - v2[1];
len = rc[0] * rc[0] + rc[1] * rc[1];
if (len == 0.0) {
rc[0] = v1[0] - v2[0];
rc[1] = v1[1] - v2[1];
return sqrt(rc[0] * rc[0] + rc[1] * rc[1]);
}
labda = (rc[0] * (v1[0] - v2[0]) + rc[1] * (v1[1] - v2[1])) / len;
if (labda <= 0.0) {
pt[0] = v2[0];
pt[1] = v2[1];
}
else if (labda >= 1.0) {
pt[0] = v3[0];
pt[1] = v3[1];
}
else {
pt[0] = labda * rc[0] + v2[0];
pt[1] = labda * rc[1] + v2[1];
}
rc[0] = pt[0] - v1[0];
rc[1] = pt[1] - v1[1];
return sqrt(rc[0] * rc[0] + rc[1] * rc[1]);
}
MINLINE double line_point_side_v2_d(const double *l1, const double *l2, const double *pt)
{
return ((l1[0] - pt[0]) * (l2[1] - pt[1])) -
((l2[0] - pt[0]) * (l1[1] - pt[1]));
}
/* point in quad - only convex quads */
static int isect_point_quad_v2_d(double pt[2], double v1[2], double v2[2], double v3[2], double v4[2])
{
if (line_point_side_v2_d(v1, v2, pt) >= 0.0) {
if (line_point_side_v2_d(v2, v3, pt) >= 0.0) {
if (line_point_side_v2_d(v3, v4, pt) >= 0.0) {
if (line_point_side_v2_d(v4, v1, pt) >= 0.0) {
return 1;
}
}
}
}
else {
if (! (line_point_side_v2_d(v2, v3, pt) >= 0.0)) {
if (! (line_point_side_v2_d(v3, v4, pt) >= 0.0)) {
if (! (line_point_side_v2_d(v4, v1, pt) >= 0.0)) {
return -1;
}
}
}
}
return 0;
}
/***** multires interpolation*****
*
* mdisps is a grid of displacements, ordered thus:
*
* v1/center----v4/next -> x
* | |
* | |
* v2/prev------v3/cur
* |
* V
* y
*/
static int compute_mdisp_quad(BMLoop *l, double v1[3], double v2[3], double v3[3], double v4[3],
double e1[3], double e2[3])
{
double cent[3] = {0.0, 0.0, 0.0}, n[3], p[3];
BMLoop *l_first;
BMLoop *l_iter;
/* computer center */
l_iter = l_first = BM_FACE_FIRST_LOOP(l->f);
do {
cent[0] += (double)l_iter->v->co[0];
cent[1] += (double)l_iter->v->co[1];
cent[2] += (double)l_iter->v->co[2];
} while ((l_iter = l_iter->next) != l_first);
VECMUL(cent, (1.0 / (double)l->f->len));
VECADD(p, l->prev->v->co, l->v->co);
VECMUL(p, 0.5);
VECADD(n, l->next->v->co, l->v->co);
VECMUL(n, 0.5);
VECCOPY(v1, cent);
VECCOPY(v2, p);
VECCOPY(v3, l->v->co);
VECCOPY(v4, n);
VECSUB(e1, v2, v1);
VECSUB(e2, v3, v4);
return 1;
}
/* funnily enough, I think this is identical to face_to_crn_interp, heh */
static double quad_coord(double aa[3], double bb[3], double cc[3], double dd[3], int a1, int a2)
{
double x, y, z, f1;
x = aa[a1] * cc[a2] - cc[a1] * aa[a2];
y = aa[a1] * dd[a2] + bb[a1] * cc[a2] - cc[a1] * bb[a2] - dd[a1] * aa[a2];
z = bb[a1] * dd[a2] - dd[a1] * bb[a2];
if (fabs(2 * (x - y + z)) > DBL_EPSILON * 10.0) {
double f2;
f1 = (sqrt(y * y - 4.0 * x * z) - y + 2.0 * z) / (2.0 * (x - y + z));
f2 = (-sqrt(y * y - 4.0 * x * z) - y + 2.0 * z) / (2.0 * (x - y + z));
f1 = fabs(f1);
f2 = fabs(f2);
f1 = MIN2(f1, f2);
CLAMP(f1, 0.0, 1.0 + DBL_EPSILON);
}
else {
f1 = -z / (y - 2 * z);
CLAMP(f1, 0.0, 1.0 + DBL_EPSILON);
if (isnan(f1) || f1 > 1.0 || f1 < 0.0) {
int i;
for (i = 0; i < 2; i++) {
if (fabsf(aa[i]) < FLT_EPSILON * 100)
return aa[(i + 1) % 2] / fabs(bb[(i + 1) % 2] - aa[(i + 1) % 2]);
if (fabsf(cc[i]) < FLT_EPSILON * 100)
return cc[(i + 1) % 2] / fabs(dd[(i + 1) % 2] - cc[(i + 1) % 2]);
}
}
}
return f1;
}
static int quad_co(double *x, double *y, double v1[3], double v2[3], double v3[3], double v4[3],
double p[3], float n[3])
{
float projverts[5][3], n2[3];
double dprojverts[4][3], origin[3] = {0.0f, 0.0f, 0.0f};
int i;
/* project points into 2d along normal */
VECCOPY(projverts[0], v1);
VECCOPY(projverts[1], v2);
VECCOPY(projverts[2], v3);
VECCOPY(projverts[3], v4);
VECCOPY(projverts[4], p);
normal_quad_v3(n2, projverts[0], projverts[1], projverts[2], projverts[3]);
if (INPR(n, n2) < -FLT_EPSILON) {
return 0;
}
/* rotate */
poly_rotate_plane(n, projverts, 5);
/* flatten */
for (i = 0; i < 5; i++) {
projverts[i][2] = 0.0f;
}
/* subtract origin */
for (i = 0; i < 4; i++) {
VECSUB2(projverts[i], projverts[4]);
}
VECCOPY(dprojverts[0], projverts[0]);
VECCOPY(dprojverts[1], projverts[1]);
VECCOPY(dprojverts[2], projverts[2]);
VECCOPY(dprojverts[3], projverts[3]);
if (!isect_point_quad_v2_d(origin, dprojverts[0], dprojverts[1], dprojverts[2], dprojverts[3])) {
return 0;
}
*y = quad_coord(dprojverts[1], dprojverts[0], dprojverts[2], dprojverts[3], 0, 1);
*x = quad_coord(dprojverts[2], dprojverts[1], dprojverts[3], dprojverts[0], 0, 1);
return 1;
}
/* tl is loop to project onto, l is loop whose internal displacement, co, is being
* projected. x and y are location in loop's mdisps grid of point co. */
static int mdisp_in_mdispquad(BMesh *bm, BMLoop *l, BMLoop *tl, double p[3], double *x, double *y, int res)
{
double v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3];
double eps = FLT_EPSILON * 4000;
if (len_v3(l->v->no) == 0.0f)
BM_vert_normal_update_all(bm, l->v);
if (len_v3(tl->v->no) == 0.0f)
BM_vert_normal_update_all(bm, tl->v);
compute_mdisp_quad(tl, v1, v2, v3, v4, e1, e2);
/* expand quad a bit */
cent_quad_v3_d(c, v1, v2, v3, v4);
VECSUB2(v1, c); VECSUB2(v2, c);
VECSUB2(v3, c); VECSUB2(v4, c);
VECMUL(v1, 1.0 + eps); VECMUL(v2, 1.0 + eps);
VECMUL(v3, 1.0 + eps); VECMUL(v4, 1.0 + eps);
VECADD2(v1, c); VECADD2(v2, c);
VECADD2(v3, c); VECADD2(v4, c);
if (!quad_co(x, y, v1, v2, v3, v4, p, l->v->no))
return 0;
*x *= res - 1;
*y *= res - 1;
return 1;
}
static void bmesh_loop_interp_mdisps(BMesh *bm, BMLoop *target, BMFace *source)
{
MDisps *mdisps;
BMLoop *l_iter;
BMLoop *l_first;
double x, y, d, v1[3], v2[3], v3[3], v4[3] = {0.0f, 0.0f, 0.0f}, e1[3], e2[3];
int ix, iy, res;
/* ignore 2-edged faces */
if (target->f->len < 3)
return;
if (!CustomData_has_layer(&bm->ldata, CD_MDISPS))
return;
mdisps = CustomData_bmesh_get(&bm->ldata, target->head.data, CD_MDISPS);
compute_mdisp_quad(target, v1, v2, v3, v4, e1, e2);
/* if no disps data allocate a new grid, the size of the first grid in source. */
if (!mdisps->totdisp) {
MDisps *md2 = CustomData_bmesh_get(&bm->ldata, BM_FACE_FIRST_LOOP(source)->head.data, CD_MDISPS);
mdisps->totdisp = md2->totdisp;
if (mdisps->totdisp) {
mdisps->disps = MEM_callocN(sizeof(float) * 3 * mdisps->totdisp,
"mdisp->disps in bmesh_loop_intern_mdisps");
}
else {
return;
}
}
res = (int)sqrt(mdisps->totdisp);
d = 1.0 / (double)(res - 1);
for (x = 0.0f, ix = 0; ix < res; x += d, ix++) {
for (y = 0.0f, iy = 0; iy < res; y += d, iy++) {
double co1[3], co2[3], co[3];
/* double xx, yy; */ /* UNUSED */
VECCOPY(co1, e1);
/* if (!iy) yy = y + (double)FLT_EPSILON * 20; */
/* else yy = y - (double)FLT_EPSILON * 20; */
VECMUL(co1, y);
VECADD2(co1, v1);
VECCOPY(co2, e2);
VECMUL(co2, y);
VECADD2(co2, v4);
/* if (!ix) xx = x + (double)FLT_EPSILON * 20; */ /* UNUSED */
/* else xx = x - (double)FLT_EPSILON * 20; */ /* UNUSED */
VECSUB(co, co2, co1);
VECMUL(co, x);
VECADD2(co, co1);
l_iter = l_first = BM_FACE_FIRST_LOOP(source);
do {
double x2, y2;
MDisps *md1, *md2;
md1 = CustomData_bmesh_get(&bm->ldata, target->head.data, CD_MDISPS);
md2 = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS);
if (mdisp_in_mdispquad(bm, target, l_iter, co, &x2, &y2, res)) {
/* int ix2 = (int)x2; */ /* UNUSED */
/* int iy2 = (int)y2; */ /* UNUSED */
old_mdisps_bilinear(md1->disps[iy * res + ix], md2->disps, res, (float)x2, (float)y2);
}
} while ((l_iter = l_iter->next) != l_first);
}
}
}
void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f)
{
BMLoop *l;
BMIter liter;
if (!CustomData_has_layer(&bm->ldata, CD_MDISPS))
return;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
MDisps *mdp = CustomData_bmesh_get(&bm->ldata, l->prev->head.data, CD_MDISPS);
MDisps *mdl = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS);
MDisps *mdn = CustomData_bmesh_get(&bm->ldata, l->next->head.data, CD_MDISPS);
float co1[3];
int sides;
int y;
/*
* mdisps is a grid of displacements, ordered thus:
*
* v4/next
* |
* | v1/cent-----mid2 ---> x
* | | |
* | | |
* v2/prev---mid1-----v3/cur
* |
* V
* y
*/
sides = (int)sqrt(mdp->totdisp);
for (y = 0; y < sides; y++) {
add_v3_v3v3(co1, mdn->disps[y * sides], mdl->disps[y]);
mul_v3_fl(co1, 0.5);
copy_v3_v3(mdn->disps[y * sides], co1);
copy_v3_v3(mdl->disps[y], co1);
}
}
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
MDisps *mdl1 = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS);
MDisps *mdl2;
float co1[3], co2[3], co[3];
int sides;
int y;
/*
* mdisps is a grid of displacements, ordered thus:
*
* v4/next
* |
* | v1/cent-----mid2 ---> x
* | | |
* | | |
* v2/prev---mid1-----v3/cur
* |
* V
* y
*/
if (l->radial_next == l)
continue;
if (l->radial_next->v == l->v)
mdl2 = CustomData_bmesh_get(&bm->ldata, l->radial_next->head.data, CD_MDISPS);
else
mdl2 = CustomData_bmesh_get(&bm->ldata, l->radial_next->next->head.data, CD_MDISPS);
sides = (int)sqrt(mdl1->totdisp);
for (y = 0; y < sides; y++) {
int a1, a2, o1, o2;
if (l->v != l->radial_next->v) {
a1 = sides * y + sides - 2;
a2 = (sides - 2) * sides + y;
o1 = sides * y + sides - 1;
o2 = (sides - 1) * sides + y;
}
else {
a1 = sides * y + sides - 2;
a2 = sides * y + sides - 2;
o1 = sides * y + sides - 1;
o2 = sides * y + sides - 1;
}
/* magic blending numbers, hardcoded! */
add_v3_v3v3(co1, mdl1->disps[a1], mdl2->disps[a2]);
mul_v3_fl(co1, 0.18);
add_v3_v3v3(co2, mdl1->disps[o1], mdl2->disps[o2]);
mul_v3_fl(co2, 0.32);
add_v3_v3v3(co, co1, co2);
copy_v3_v3(mdl1->disps[o1], co);
copy_v3_v3(mdl2->disps[o2], co);
}
}
}
void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source)
{
bmesh_loop_interp_mdisps(bm, target, source);
}
void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
int do_vertex, int do_multires)
{
BMLoop *l_iter;
BMLoop *l_first;
void **blocks = NULL;
void **vblocks = NULL;
float (*cos)[3] = NULL, co[3], *w = NULL;
float cent[3] = {0.0f, 0.0f, 0.0f};
BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__);
BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__);
BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__);
BLI_array_fixedstack_declare(vblocks, BM_NGON_STACK_SIZE, do_vertex ? source->len : 0, __func__);
int i, ax, ay;
BM_elem_attrs_copy(bm, bm, source, target->f);
i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(source);
do {
copy_v3_v3(cos[i], l_iter->v->co);
add_v3_v3(cent, cos[i]);
w[i] = 0.0f;
blocks[i] = l_iter->head.data;
if (do_vertex) {
vblocks[i] = l_iter->v->head.data;
}
i++;
} while ((l_iter = l_iter->next) != l_first);
/* find best projection of face XY, XZ or YZ: barycentric weights of
* the 2d projected coords are the same and faster to compute */
axis_dominant_v3(&ax, &ay, source->no);
/* scale source face coordinates a bit, so points sitting directonly on an
* edge will work. */
mul_v3_fl(cent, 1.0f / (float)source->len);
for (i = 0; i < source->len; i++) {
float vec[3], tmp[3];
sub_v3_v3v3(vec, cent, cos[i]);
mul_v3_fl(vec, 0.001f);
add_v3_v3(cos[i], vec);
copy_v3_v3(tmp, cos[i]);
cos[i][0] = tmp[ax];
cos[i][1] = tmp[ay];
cos[i][2] = 0.0;
}
/* interpolate */
co[0] = target->v->co[ax];
co[1] = target->v->co[ay];
co[2] = 0.0f;
interp_weights_poly_v3(w, cos, source->len, co);
CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, target->head.data);
if (do_vertex) {
CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, source->len, target->v->head.data);
BLI_array_fixedstack_free(vblocks);
}
BLI_array_fixedstack_free(cos);
BLI_array_fixedstack_free(w);
BLI_array_fixedstack_free(blocks);
if (do_multires) {
if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
bmesh_loop_interp_mdisps(bm, target, source);
}
}
}
void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source)
{
BMLoop *l_iter;
BMLoop *l_first;
void **blocks = NULL;
float (*cos)[3] = NULL, *w = NULL;
float cent[3] = {0.0f, 0.0f, 0.0f};
BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__);
BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__);
BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__);
int i;
i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(source);
do {
copy_v3_v3(cos[i], l_iter->v->co);
add_v3_v3(cent, cos[i]);
w[i] = 0.0f;
blocks[i] = l_iter->v->head.data;
i++;
} while ((l_iter = l_iter->next) != l_first);
/* scale source face coordinates a bit, so points sitting directonly on an
* edge will work. */
mul_v3_fl(cent, 1.0f / (float)source->len);
for (i = 0; i < source->len; i++) {
float vec[3];
sub_v3_v3v3(vec, cent, cos[i]);
mul_v3_fl(vec, 0.01);
add_v3_v3(cos[i], vec);
}
/* interpolate */
interp_weights_poly_v3(w, cos, source->len, v->co);
CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, source->len, v->head.data);
BLI_array_fixedstack_free(cos);
BLI_array_fixedstack_free(w);
BLI_array_fixedstack_free(blocks);
}
static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data)
{
BMIter iter;
BLI_mempool *oldpool = olddata->pool;
void *block;
CustomData_bmesh_init_pool(data, data == &bm->ldata ? 2048 : 512);
if (data == &bm->vdata) {
BMVert *eve;
BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
block = NULL;
CustomData_bmesh_set_default(data, &block);
CustomData_bmesh_copy_data(olddata, data, eve->head.data, &block);
CustomData_bmesh_free_block(olddata, &eve->head.data);
eve->head.data = block;
}
}
else if (data == &bm->edata) {
BMEdge *eed;
BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
block = NULL;
CustomData_bmesh_set_default(data, &block);
CustomData_bmesh_copy_data(olddata, data, eed->head.data, &block);
CustomData_bmesh_free_block(olddata, &eed->head.data);
eed->head.data = block;
}
}
else if (data == &bm->pdata || data == &bm->ldata) {
BMIter liter;
BMFace *efa;
BMLoop *l;
BM_ITER(efa, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (data == &bm->pdata) {
block = NULL;
CustomData_bmesh_set_default(data, &block);
CustomData_bmesh_copy_data(olddata, data, efa->head.data, &block);
CustomData_bmesh_free_block(olddata, &efa->head.data);
efa->head.data = block;
}
if (data == &bm->ldata) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, efa) {
block = NULL;
CustomData_bmesh_set_default(data, &block);
CustomData_bmesh_copy_data(olddata, data, l->head.data, &block);
CustomData_bmesh_free_block(olddata, &l->head.data);
l->head.data = block;
}
}
}
}
if (oldpool) {
/* this should never happen but can when dissolve fails - [#28960] */
BLI_assert(data->pool != oldpool);
BLI_mempool_destroy(oldpool);
}
}
void BM_data_layer_add(BMesh *bm, CustomData *data, int type)
{
CustomData olddata;
olddata = *data;
olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL;
/* the pool is now owned by olddata and must not be shared */
data->pool = NULL;
CustomData_add_layer(data, type, CD_DEFAULT, NULL, 0);
update_data_blocks(bm, &olddata, data);
if (olddata.layers) MEM_freeN(olddata.layers);
}
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name)
{
CustomData olddata;
olddata = *data;
olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL;
/* the pool is now owned by olddata and must not be shared */
data->pool = NULL;
CustomData_add_layer_named(data, type, CD_DEFAULT, NULL, 0, name);
update_data_blocks(bm, &olddata, data);
if (olddata.layers) MEM_freeN(olddata.layers);
}
void BM_data_layer_free(BMesh *bm, CustomData *data, int type)
{
CustomData olddata;
olddata = *data;
olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL;
/* the pool is now owned by olddata and must not be shared */
data->pool = NULL;
CustomData_free_layer_active(data, type, 0);
update_data_blocks(bm, &olddata, data);
if (olddata.layers) MEM_freeN(olddata.layers);
}
void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n)
{
CustomData olddata;
olddata = *data;
olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL;
/* the pool is now owned by olddata and must not be shared */
data->pool = NULL;
CustomData_free_layer(data, type, 0, CustomData_get_layer_index_n(data, type, n));
update_data_blocks(bm, &olddata, data);
if (olddata.layers) MEM_freeN(olddata.layers);
}
float BM_elem_float_data_get(CustomData *cd, void *element, int type)
{
float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type);
return f ? *f : 0.0f;
}
void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val)
{
float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type);
if (f) *f = val;
}
@@ -0,0 +1,417 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_iterators.c
* \ingroup bmesh
*
* Functions to abstract looping over bmesh data structures.
*
* See: bmesh_iterators_inlin.c too, some functions are here for speed reasons.
*/
#include "bmesh.h"
#include "bmesh_private.h"
/*
* note, we have BM_vert_at_index/BM_edge_at_index/BM_face_at_index for arrays
*/
void *BM_iter_at_index(struct BMesh *bm, const char itype, void *data, int index)
{
BMIter iter;
void *val;
int i;
/* sanity check */
if (index < 0) {
return NULL;
}
val = BM_iter_new(&iter, bm, itype, data);
i = 0;
while (i < index) {
val = BM_iter_step(&iter);
i++;
}
return val;
}
/*
* ITERATOR AS ARRAY
*
* Sometimes its convenient to get the iterator as an array
* to avoid multiple calls to BM_iter_at_index.
*/
int BM_iter_as_array(struct BMesh *bm, const char type, void *data, void **array, const int len)
{
int i = 0;
/* sanity check */
if (len > 0) {
BMIter iter;
void *val;
BM_ITER(val, &iter, bm, type, data) {
array[i] = val;
i++;
if (i == len) {
return len;
}
}
}
return i;
}
/*
* 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
*
* The 'bmiter__' prefix is used because these are used in
* bmesh_iterators_inine.c but should otherwise be seen as
* private.
*/
/*
* VERT OF MESH CALLBACKS
*
*/
void bmiter__vert_of_mesh_begin(BMIter *iter)
{
BLI_mempool_iternew(iter->bm->vpool, &iter->pooliter);
}
void *bmiter__vert_of_mesh_step(BMIter *iter)
{
return BLI_mempool_iterstep(&iter->pooliter);
}
void bmiter__edge_of_mesh_begin(BMIter *iter)
{
BLI_mempool_iternew(iter->bm->epool, &iter->pooliter);
}
void *bmiter__edge_of_mesh_step(BMIter *iter)
{
return BLI_mempool_iterstep(&iter->pooliter);
}
void bmiter__face_of_mesh_begin(BMIter *iter)
{
BLI_mempool_iternew(iter->bm->fpool, &iter->pooliter);
}
void *bmiter__face_of_mesh_step(BMIter *iter)
{
return BLI_mempool_iterstep(&iter->pooliter);
}
/*
* EDGE OF VERT CALLBACKS
*
*/
void bmiter__edge_of_vert_begin(BMIter *iter)
{
init_iterator(iter);
if (iter->vdata->e) {
iter->firstedge = iter->vdata->e;
iter->nextedge = iter->vdata->e;
}
}
void *bmiter__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
*
*/
void bmiter__face_of_vert_begin(BMIter *iter)
{
init_iterator(iter);
iter->count = 0;
if (iter->vdata->e)
iter->count = bmesh_disk_count_facevert(iter->vdata);
if (iter->count) {
iter->firstedge = bmesh_disk_find_first_faceedge(iter->vdata->e, iter->vdata);
iter->nextedge = iter->firstedge;
iter->firstloop = bmesh_radial_find_first_facevert(iter->firstedge->l, iter->vdata);
iter->nextloop = iter->firstloop;
}
}
void *bmiter__face_of_vert_step(BMIter *iter)
{
BMLoop *current = iter->nextloop;
if (iter->count && iter->nextloop) {
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->l, iter->vdata);
iter->nextloop = iter->firstloop;
}
}
if (!iter->count) iter->nextloop = NULL;
return current ? current->f : NULL;
}
/*
* LOOP OF VERT CALLBACKS
*
*/
void bmiter__loop_of_vert_begin(BMIter *iter)
{
init_iterator(iter);
iter->count = 0;
if (iter->vdata->e)
iter->count = bmesh_disk_count_facevert(iter->vdata);
if (iter->count) {
iter->firstedge = bmesh_disk_find_first_faceedge(iter->vdata->e, iter->vdata);
iter->nextedge = iter->firstedge;
iter->firstloop = bmesh_radial_find_first_facevert(iter->firstedge->l, iter->vdata);
iter->nextloop = iter->firstloop;
}
}
void *bmiter__loop_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->l, iter->vdata);
iter->nextloop = iter->firstloop;
}
}
if (!iter->count) iter->nextloop = NULL;
if (current) {
return current;
}
return NULL;
}
void bmiter__loops_of_edge_begin(BMIter *iter)
{
BMLoop *l;
l = iter->edata->l;
/* note sure why this sets ldata ... */
init_iterator(iter);
iter->firstloop = iter->nextloop = l;
}
void *bmiter__loops_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;
}
return NULL;
}
void bmiter__loops_of_loop_begin(BMIter *iter)
{
BMLoop *l;
l = iter->ldata;
/* note sure why this sets ldata ... */
init_iterator(iter);
iter->firstloop = l;
iter->nextloop = bmesh_radial_nextloop(iter->firstloop);
if (iter->nextloop == iter->firstloop)
iter->nextloop = NULL;
}
void *bmiter__loops_of_loop_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;
}
return NULL;
}
/*
* FACE OF EDGE CALLBACKS
*
*/
void bmiter__face_of_edge_begin(BMIter *iter)
{
init_iterator(iter);
if (iter->edata->l) {
iter->firstloop = iter->edata->l;
iter->nextloop = iter->edata->l;
}
}
void *bmiter__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;
return current ? current->f : NULL;
}
/*
* VERT OF FACE CALLBACKS
*
*/
void bmiter__vert_of_face_begin(BMIter *iter)
{
init_iterator(iter);
iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata);
}
void *bmiter__vert_of_face_step(BMIter *iter)
{
BMLoop *current = iter->nextloop;
if (iter->nextloop) iter->nextloop = iter->nextloop->next;
if (iter->nextloop == iter->firstloop) iter->nextloop = NULL;
return current ? current->v : NULL;
}
/*
* EDGE OF FACE CALLBACKS
*
*/
void bmiter__edge_of_face_begin(BMIter *iter)
{
init_iterator(iter);
iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata);
}
void *bmiter__edge_of_face_step(BMIter *iter)
{
BMLoop *current = iter->nextloop;
if (iter->nextloop) iter->nextloop = iter->nextloop->next;
if (iter->nextloop == iter->firstloop) iter->nextloop = NULL;
return current ? current->e : NULL;
}
/*
* LOOP OF FACE CALLBACKS
*
*/
void bmiter__loop_of_face_begin(BMIter *iter)
{
init_iterator(iter);
iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata);
}
void *bmiter__loop_of_face_step(BMIter *iter)
{
BMLoop *current = iter->nextloop;
if (iter->nextloop) iter->nextloop = iter->nextloop->next;
if (iter->nextloop == iter->firstloop) iter->nextloop = NULL;
return current;
}
@@ -0,0 +1,160 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_iterators_inline.c
* \ingroup bmesh
*
* BMesh inline iterator functions.
*/
#ifndef __BMESH_ITERATORS_INLINE_C__
#define __BMESH_ITERATORS_INLINE_C__
#include "bmesh.h"
/* inline here optimizes out the switch statement when called with
* constant values (which is very common), nicer for loop-in-loop situations */
/*
* BMESH ITERATOR STEP
*
* Calls an iterators step fucntion to return
* the next element.
*/
BM_INLINE void *BM_iter_step(BMIter *iter)
{
return iter->step(iter);
}
/*
* 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.
*
*/
BM_INLINE void *BM_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data)
{
/* int argtype; */
iter->itype = itype;
iter->bm = bm;
/* inlining optimizes out this switch when called with the defined type */
switch (itype) {
case BM_VERTS_OF_MESH:
iter->begin = bmiter__vert_of_mesh_begin;
iter->step = bmiter__vert_of_mesh_step;
break;
case BM_EDGES_OF_MESH:
iter->begin = bmiter__edge_of_mesh_begin;
iter->step = bmiter__edge_of_mesh_step;
break;
case BM_FACES_OF_MESH:
iter->begin = bmiter__face_of_mesh_begin;
iter->step = bmiter__face_of_mesh_step;
break;
case BM_EDGES_OF_VERT:
if (!data)
return NULL;
iter->begin = bmiter__edge_of_vert_begin;
iter->step = bmiter__edge_of_vert_step;
iter->vdata = data;
break;
case BM_FACES_OF_VERT:
if (!data)
return NULL;
iter->begin = bmiter__face_of_vert_begin;
iter->step = bmiter__face_of_vert_step;
iter->vdata = data;
break;
case BM_LOOPS_OF_VERT:
if (!data)
return NULL;
iter->begin = bmiter__loop_of_vert_begin;
iter->step = bmiter__loop_of_vert_step;
iter->vdata = data;
break;
case BM_FACES_OF_EDGE:
if (!data)
return NULL;
iter->begin = bmiter__face_of_edge_begin;
iter->step = bmiter__face_of_edge_step;
iter->edata = data;
break;
case BM_VERTS_OF_FACE:
if (!data)
return NULL;
iter->begin = bmiter__vert_of_face_begin;
iter->step = bmiter__vert_of_face_step;
iter->pdata = data;
break;
case BM_EDGES_OF_FACE:
if (!data)
return NULL;
iter->begin = bmiter__edge_of_face_begin;
iter->step = bmiter__edge_of_face_step;
iter->pdata = data;
break;
case BM_LOOPS_OF_FACE:
if (!data)
return NULL;
iter->begin = bmiter__loop_of_face_begin;
iter->step = bmiter__loop_of_face_step;
iter->pdata = data;
break;
case BM_LOOPS_OF_LOOP:
if (!data)
return NULL;
iter->begin = bmiter__loops_of_loop_begin;
iter->step = bmiter__loops_of_loop_step;
iter->ldata = data;
break;
case BM_LOOPS_OF_EDGE:
if (!data)
return NULL;
iter->begin = bmiter__loops_of_edge_begin;
iter->step = bmiter__loops_of_edge_step;
iter->edata = data;
break;
default:
break;
}
iter->begin(iter);
return BM_iter_step(iter);
}
#endif /* __BMESH_ITERATORS_INLINE_C__ */
+910
View File
@@ -0,0 +1,910 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_marking.c
* \ingroup bmesh
*
* 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.
*/
#include "MEM_guardedalloc.h"
#include "DNA_scene_types.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
#include "bmesh.h"
/*
* 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.
*
*/
static void recount_totsels(BMesh *bm)
{
BMIter iter;
BMHeader *ele;
const char iter_types[3] = {BM_VERTS_OF_MESH,
BM_EDGES_OF_MESH,
BM_FACES_OF_MESH};
int *tots[3];
int i;
/* recount (tot * sel) variables */
bm->totvertsel = bm->totedgesel = bm->totfacesel = 0;
tots[0] = &bm->totvertsel;
tots[1] = &bm->totedgesel;
tots[2] = &bm->totfacesel;
for (i = 0; i < 3; i++) {
ele = BM_iter_new(&iter, bm, iter_types[i], NULL);
for ( ; ele; ele = BM_iter_step(&iter)) {
if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) *tots[i] += 1;
}
}
}
void BM_mesh_select_mode_flush(BMesh *bm)
{
BMEdge *e;
BMLoop *l_iter;
BMLoop *l_first;
BMFace *f;
BMIter edges;
BMIter faces;
int ok;
if (bm->selectmode & SCE_SELECT_VERTEX) {
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
BM_elem_flag_test(e->v2, BM_ELEM_SELECT) &&
!BM_elem_flag_test(e, BM_ELEM_HIDDEN))
{
BM_elem_flag_enable(e, BM_ELEM_SELECT);
}
else {
BM_elem_flag_disable(e, BM_ELEM_SELECT);
}
}
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
ok = TRUE;
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
ok = FALSE;
break;
}
} while ((l_iter = l_iter->next) != l_first);
}
else {
ok = FALSE;
}
if (ok) {
BM_elem_flag_enable(f, BM_ELEM_SELECT);
}
else {
BM_elem_flag_disable(f, BM_ELEM_SELECT);
}
}
}
else if (bm->selectmode & SCE_SELECT_EDGE) {
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
ok = TRUE;
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (!BM_elem_flag_test(&(l_iter->e->head), BM_ELEM_SELECT)) {
ok = FALSE;
break;
}
} while ((l_iter = l_iter->next) != l_first);
}
else {
ok = FALSE;
}
if (ok) {
BM_elem_flag_enable(f, BM_ELEM_SELECT);
}
else {
BM_elem_flag_disable(f, BM_ELEM_SELECT);
}
}
}
/* Remove any deselected elements from the BMEditSelection */
BM_select_history_validate(bm);
recount_totsels(bm);
}
/* BMESH NOTE: matches EM_deselect_flush() behavior from trunk */
void BM_mesh_deselect_flush(BMesh *bm)
{
BMEdge *e;
BMLoop *l_iter;
BMLoop *l_first;
BMFace *f;
BMIter edges;
BMIter faces;
int ok;
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
if (!(BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
BM_elem_flag_test(e->v2, BM_ELEM_SELECT) &&
!BM_elem_flag_test(e, BM_ELEM_HIDDEN)))
{
BM_elem_flag_disable(e, BM_ELEM_SELECT);
}
}
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
ok = TRUE;
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
ok = FALSE;
break;
}
} while ((l_iter = l_iter->next) != l_first);
}
else {
ok = FALSE;
}
if (ok == FALSE) {
BM_elem_flag_disable(f, BM_ELEM_SELECT);
}
}
/* Remove any deselected elements from the BMEditSelection */
BM_select_history_validate(bm);
recount_totsels(bm);
}
/* BMESH NOTE: matches EM_select_flush() behavior from trunk */
void BM_mesh_select_flush(BMesh *bm)
{
BMEdge *e;
BMLoop *l_iter;
BMLoop *l_first;
BMFace *f;
BMIter edges;
BMIter faces;
int ok;
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
BM_elem_flag_test(e->v2, BM_ELEM_SELECT) &&
!BM_elem_flag_test(e, BM_ELEM_HIDDEN))
{
BM_elem_flag_enable(e, BM_ELEM_SELECT);
}
}
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
ok = TRUE;
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
ok = FALSE;
break;
}
} while ((l_iter = l_iter->next) != l_first);
}
else {
ok = FALSE;
}
if (ok) {
BM_elem_flag_enable(f, BM_ELEM_SELECT);
}
}
recount_totsels(bm);
}
/*
* BMESH SELECT VERT
*
* Changes selection state of a single vertex
* in a mesh
*
*/
void BM_vert_select_set(BMesh *bm, BMVert *v, int select)
{
/* BMIter iter; */
/* BMEdge *e; */
if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
return;
}
if (select) {
if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) {
bm->totvertsel += 1;
BM_elem_flag_enable(v, BM_ELEM_SELECT);
}
}
else {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
bm->totvertsel -= 1;
BM_elem_flag_disable(v, BM_ELEM_SELECT);
}
}
}
/*
* BMESH SELECT EDGE
*
* Changes selection state of a single edge
* in a mesh.
*
*/
void BM_edge_select_set(BMesh *bm, BMEdge *e, int select)
{
if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
return;
}
if (select) {
if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel += 1;
BM_elem_flag_enable(&(e->head), BM_ELEM_SELECT);
BM_elem_select_set(bm, e->v1, TRUE);
BM_elem_select_set(bm, e->v2, TRUE);
}
else {
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel -= 1;
BM_elem_flag_disable(&(e->head), BM_ELEM_SELECT);
if ( bm->selectmode == SCE_SELECT_EDGE ||
bm->selectmode == SCE_SELECT_FACE ||
bm->selectmode == (SCE_SELECT_EDGE | SCE_SELECT_FACE))
{
BMIter iter;
BMVert *verts[2] = {e->v1, e->v2};
BMEdge *e2;
int i;
for (i = 0; i < 2; i++) {
int deselect = 1;
for (e2 = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, verts[i]); e2; e2 = BM_iter_step(&iter)) {
if (e2 == e) {
continue;
}
if (BM_elem_flag_test(e2, BM_ELEM_SELECT)) {
deselect = 0;
break;
}
}
if (deselect) BM_vert_select_set(bm, verts[i], FALSE);
}
}
else {
BM_elem_select_set(bm, e->v1, FALSE);
BM_elem_select_set(bm, e->v2, FALSE);
}
}
}
/*
*
* BMESH SELECT FACE
*
* Changes selection state of a single
* face in a mesh.
*
*/
void BM_face_select_set(BMesh *bm, BMFace *f, int select)
{
BMLoop *l_iter;
BMLoop *l_first;
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
return;
}
if (select) {
if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) {
bm->totfacesel++;
}
BM_elem_flag_enable(&(f->head), BM_ELEM_SELECT);
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BM_vert_select_set(bm, l_iter->v, TRUE);
BM_edge_select_set(bm, l_iter->e, TRUE);
} while ((l_iter = l_iter->next) != l_first);
}
else {
BMIter liter;
BMLoop *l;
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) bm->totfacesel -= 1;
BM_elem_flag_disable(&(f->head), BM_ELEM_SELECT);
/* flush down to edges */
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BMIter fiter;
BMFace *f2;
BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
if (BM_elem_flag_test(f2, BM_ELEM_SELECT))
break;
}
if (!f2)
{
BM_elem_select_set(bm, l->e, FALSE);
}
}
/* flush down to verts */
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BMIter eiter;
BMEdge *e;
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, l->v) {
if (BM_elem_flag_test(e, BM_ELEM_SELECT))
break;
}
if (!e) {
BM_elem_select_set(bm, l->v, FALSE);
}
}
}
}
/*
* BMESH SELECTMODE SET
*
* Sets the selection mode for the bmesh
*
*/
void BM_select_mode_set(BMesh *bm, int selectmode)
{
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter verts;
BMIter edges;
BMIter faces;
bm->selectmode = selectmode;
if (bm->selectmode & SCE_SELECT_VERTEX) {
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges))
BM_elem_flag_disable(e, 0);
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces))
BM_elem_flag_disable(f, 0);
BM_mesh_select_mode_flush(bm);
}
else if (bm->selectmode & SCE_SELECT_EDGE) {
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts))
BM_elem_flag_disable(v, 0);
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
if (BM_elem_flag_test(&(e->head), BM_ELEM_SELECT)) {
BM_edge_select_set(bm, e, TRUE);
}
}
BM_mesh_select_mode_flush(bm);
}
else if (bm->selectmode & SCE_SELECT_FACE) {
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges))
BM_elem_flag_disable(e, 0);
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
if (BM_elem_flag_test(&(f->head), BM_ELEM_SELECT)) {
BM_face_select_set(bm, f, TRUE);
}
}
BM_mesh_select_mode_flush(bm);
}
}
int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide)
{
BMHeader *head;
BMIter iter;
int tot = 0;
if (htype & BM_VERT) {
for (head = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); head; head = BM_iter_step(&iter)) {
if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue;
if (BM_elem_flag_test(head, hflag)) tot++;
}
}
if (htype & BM_EDGE) {
for (head = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) {
if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue;
if (BM_elem_flag_test(head, hflag)) tot++;
}
}
if (htype & BM_FACE) {
for (head = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) {
if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue;
if (BM_elem_flag_test(head, hflag)) tot++;
}
}
return tot;
}
/* note: by design, this will not touch the editselection history stuff */
void BM_elem_select_set(struct BMesh *bm, void *element, int select)
{
BMHeader *head = element;
if (head->htype == BM_VERT) BM_vert_select_set(bm, (BMVert *)element, select);
else if (head->htype == BM_EDGE) BM_edge_select_set(bm, (BMEdge *)element, select);
else if (head->htype == BM_FACE) BM_face_select_set(bm, (BMFace *)element, select);
}
/* this replaces the active flag used in uv/face mode */
void BM_active_face_set(BMesh *bm, BMFace *efa)
{
bm->act_face = efa;
}
BMFace *BM_active_face_get(BMesh *bm, int sloppy)
{
if (bm->act_face) {
return bm->act_face;
}
else if (sloppy) {
BMIter iter;
BMFace *f = NULL;
BMEditSelection *ese;
/* Find the latest non-hidden face from the BMEditSelection */
ese = bm->selected.last;
for ( ; ese; ese = ese->prev) {
if (ese->htype == BM_FACE) {
f = (BMFace *)ese->data;
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
f = NULL;
}
else {
break;
}
}
}
/* Last attempt: try to find any selected face */
if (f == NULL) {
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
break;
}
}
}
return f; /* can still be null */
}
return NULL;
}
/* Generic way to get data from an EditSelection type
* These functions were written to be used by the Modifier widget
* when in Rotate about active mode, but can be used anywhere.
*
* - EM_editselection_center
* - EM_editselection_normal
* - EM_editselection_plane
*/
void BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese)
{
if (ese->htype == BM_VERT) {
BMVert *eve = ese->data;
copy_v3_v3(r_center, eve->co);
}
else if (ese->htype == BM_EDGE) {
BMEdge *eed = ese->data;
add_v3_v3v3(r_center, eed->v1->co, eed->v2->co);
mul_v3_fl(r_center, 0.5);
}
else if (ese->htype == BM_FACE) {
BMFace *efa = ese->data;
BM_face_center_bounds_calc(bm, efa, r_center);
}
}
void BM_editselection_normal(float r_normal[3], BMEditSelection *ese)
{
if (ese->htype == BM_VERT) {
BMVert *eve = ese->data;
copy_v3_v3(r_normal, eve->no);
}
else if (ese->htype == BM_EDGE) {
BMEdge *eed = ese->data;
float plane[3]; /* need a plane to correct the normal */
float vec[3]; /* temp vec storage */
add_v3_v3v3(r_normal, eed->v1->no, eed->v2->no);
sub_v3_v3v3(plane, eed->v2->co, eed->v1->co);
/* the 2 vertex normals will be close but not at rightangles to the edge
* for rotate about edge we want them to be at right angles, so we need to
* do some extra colculation to correct the vert normals,
* we need the plane for this */
cross_v3_v3v3(vec, r_normal, plane);
cross_v3_v3v3(r_normal, plane, vec);
normalize_v3(r_normal);
}
else if (ese->htype == BM_FACE) {
BMFace *efa = ese->data;
copy_v3_v3(r_normal, efa->no);
}
}
/* ref - editmesh_lib.cL:EM_editselection_plane() */
/* Calculate a plane that is rightangles to the edge/vert/faces normal
* also make the plane run along an axis that is related to the geometry,
* because this is used for the manipulators Y axis. */
void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese)
{
if (ese->htype == BM_VERT) {
BMVert *eve = ese->data;
float vec[3] = {0.0f, 0.0f, 0.0f};
if (ese->prev) { /* use previously selected data to make a useful vertex plane */
BM_editselection_center(bm, vec, ese->prev);
sub_v3_v3v3(r_plane, vec, eve->co);
}
else {
/* make a fake plane thats at rightangles to the normal
* we cant make a crossvec from a vec thats the same as the vec
* unlikely but possible, so make sure if the normal is (0, 0, 1)
* that vec isnt the same or in the same direction even. */
if (eve->no[0] < 0.5f) vec[0] = 1.0f;
else if (eve->no[1] < 0.5f) vec[1] = 1.0f;
else vec[2] = 1.0f;
cross_v3_v3v3(r_plane, eve->no, vec);
}
}
else if (ese->htype == BM_EDGE) {
BMEdge *eed = ese->data;
/* the plane is simple, it runs along the edge
* however selecting different edges can swap the direction of the y axis.
* this makes it less likely for the y axis of the manipulator
* (running along the edge).. to flip less often.
* at least its more pradictable */
if (eed->v2->co[1] > eed->v1->co[1]) { /* check which to do first */
sub_v3_v3v3(r_plane, eed->v2->co, eed->v1->co);
}
else {
sub_v3_v3v3(r_plane, eed->v1->co, eed->v2->co);
}
}
else if (ese->htype == BM_FACE) {
BMFace *efa = ese->data;
float vec[3] = {0.0f, 0.0f, 0.0f};
/* for now, use face normal */
/* make a fake plane thats at rightangles to the normal
* we cant make a crossvec from a vec thats the same as the vec
* unlikely but possible, so make sure if the normal is (0, 0, 1)
* that vec isnt the same or in the same direction even. */
if (efa->len < 3) {
/* crappy fallback method */
if (efa->no[0] < 0.5f) vec[0] = 1.0f;
else if (efa->no[1] < 0.5f) vec[1] = 1.0f;
else vec[2] = 1.0f;
cross_v3_v3v3(r_plane, efa->no, vec);
}
else {
BMVert *verts[4] = {NULL};
BM_iter_as_array(bm, BM_VERTS_OF_FACE, efa, (void **)verts, 4);
if (efa->len == 4) {
float vecA[3], vecB[3];
sub_v3_v3v3(vecA, verts[3]->co, verts[2]->co);
sub_v3_v3v3(vecB, verts[0]->co, verts[1]->co);
add_v3_v3v3(r_plane, vecA, vecB);
sub_v3_v3v3(vecA, verts[0]->co, verts[3]->co);
sub_v3_v3v3(vecB, verts[1]->co, verts[2]->co);
add_v3_v3v3(vec, vecA, vecB);
/* use the biggest edge length */
if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) {
copy_v3_v3(r_plane, vec);
}
}
else {
/* BMESH_TODO (not urgent, use longest ngon edge for alignment) */
/* start with v1-2 */
sub_v3_v3v3(r_plane, verts[0]->co, verts[1]->co);
/* test the edge between v2-3, use if longer */
sub_v3_v3v3(vec, verts[1]->co, verts[2]->co);
if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec))
copy_v3_v3(r_plane, vec);
/* test the edge between v1-3, use if longer */
sub_v3_v3v3(vec, verts[2]->co, verts[0]->co);
if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) {
copy_v3_v3(r_plane, vec);
}
}
}
}
normalize_v3(r_plane);
}
int BM_select_history_check(BMesh *bm, void *data)
{
BMEditSelection *ese;
for (ese = bm->selected.first; ese; ese = ese->next) {
if (ese->data == data) {
return TRUE;
}
}
return FALSE;
}
void BM_select_history_remove(BMesh *bm, void *data)
{
BMEditSelection *ese;
for (ese = bm->selected.first; ese; ese = ese->next) {
if (ese->data == data) {
BLI_freelinkN(&(bm->selected), ese);
break;
}
}
}
void BM_select_history_clear(BMesh *bm)
{
BLI_freelistN(&bm->selected);
bm->selected.first = bm->selected.last = NULL;
}
void BM_select_history_store(BMesh *bm, void *data)
{
BMEditSelection *ese;
if (!BM_select_history_check(bm, data)) {
ese = (BMEditSelection *) MEM_callocN(sizeof(BMEditSelection), "BMEdit Selection");
ese->htype = ((BMHeader *)data)->htype;
ese->data = data;
BLI_addtail(&(bm->selected), ese);
}
}
void BM_select_history_validate(BMesh *bm)
{
BMEditSelection *ese, *nextese;
ese = bm->selected.first;
while (ese) {
nextese = ese->next;
if (!BM_elem_flag_test(ese->data, BM_ELEM_SELECT)) {
BLI_freelinkN(&(bm->selected), ese);
}
ese = nextese;
}
}
void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag)
{
const char iter_types[3] = {BM_VERTS_OF_MESH,
BM_EDGES_OF_MESH,
BM_FACES_OF_MESH};
BMIter iter;
BMHeader *ele;
int i;
if (hflag & BM_ELEM_SELECT) {
BM_select_history_clear(bm);
}
for (i = 0; i < 3; i++) {
if (htype & iter_types[i]) {
ele = BM_iter_new(&iter, bm, iter_types[i], NULL);
for ( ; ele; ele = BM_iter_step(&iter)) {
if (hflag & BM_ELEM_SELECT) {
BM_elem_select_set(bm, ele, FALSE);
}
BM_elem_flag_disable(ele, hflag);
}
}
}
}
void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag)
{
const char iter_types[3] = {BM_VERTS_OF_MESH,
BM_EDGES_OF_MESH,
BM_FACES_OF_MESH};
BMIter iter;
BMHeader *ele;
int i;
if (hflag & BM_ELEM_SELECT) {
BM_select_history_clear(bm);
}
for (i = 0; i < 3; i++) {
if (htype & iter_types[i]) {
ele = BM_iter_new(&iter, bm, iter_types[i], NULL);
for ( ; ele; ele = BM_iter_step(&iter)) {
if (hflag & BM_ELEM_SELECT) {
BM_elem_select_set(bm, ele, TRUE);
}
BM_elem_flag_enable(ele, hflag);
}
}
}
}
/***************** Mesh Hiding stuff *********** */
#define BM_ELEM_HIDE_SET(ele, hide) \
(hide) ? BM_elem_flag_enable(ele, BM_ELEM_HIDDEN) : BM_elem_flag_disable(ele, BM_ELEM_HIDDEN);
static void vert_flush_hide_set(BMesh *bm, BMVert *v)
{
BMIter iter;
BMEdge *e;
int hide = TRUE;
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
hide = hide && BM_elem_flag_test(e, BM_ELEM_HIDDEN);
}
BM_ELEM_HIDE_SET(v, hide);
}
static void edge_flush_hide(BMesh *bm, BMEdge *e)
{
BMIter iter;
BMFace *f;
int hide = TRUE;
BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) {
hide = hide && BM_elem_flag_test(f, BM_ELEM_HIDDEN);
}
BM_ELEM_HIDE_SET(e, hide);
}
void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide)
{
/* vert hiding: vert + surrounding edges and faces */
BMIter iter, fiter;
BMEdge *e;
BMFace *f;
BM_ELEM_HIDE_SET(v, hide);
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
BM_ELEM_HIDE_SET(e, hide);
BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
BM_ELEM_HIDE_SET(f, hide);
}
}
}
void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide)
{
BMIter iter;
BMFace *f;
/* BMVert *v; */
/* edge hiding: faces around the edge */
BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) {
BM_ELEM_HIDE_SET(f, hide);
}
BM_ELEM_HIDE_SET(e, hide);
/* hide vertices if necassary */
vert_flush_hide_set(bm, e->v1);
vert_flush_hide_set(bm, e->v2);
}
void BM_face_hide_set(BMesh *bm, BMFace *f, int hide)
{
BMIter iter;
BMLoop *l;
BM_ELEM_HIDE_SET(f, hide);
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
edge_flush_hide(bm, l->e);
}
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
vert_flush_hide_set(bm, l->v);
}
}
#undef BM_ELEM_HIDE_SET
void BM_elem_hide_set(BMesh *bm, void *element, int hide)
{
BMHeader *h = element;
/* Follow convention of always deselecting before
* hiding an element */
if (hide) {
BM_elem_select_set(bm, element, FALSE);
}
switch (h->htype) {
case BM_VERT:
BM_vert_hide_set(bm, element, hide);
break;
case BM_EDGE:
BM_edge_hide_set(bm, element, hide);
break;
case BM_FACE:
BM_face_hide_set(bm, element, hide);
break;
}
}
+625
View File
@@ -0,0 +1,625 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_mesh.c
* \ingroup bmesh
*
* BM mesh level functions.
*/
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "DNA_object_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_mesh_types.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_utildefines.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_tessmesh.h"
#include "BKE_customdata.h"
#include "BKE_multires.h"
#include "ED_mesh.h"
#include "bmesh_private.h"
/* used as an extern, defined in bmesh.h */
int bm_mesh_allocsize_default[4] = {512, 512, 2048, 512};
/* bmesh_error stub */
void bmesh_error(void)
{
printf("BM modelling error!\n");
/* This placeholder assert makes modelling errors easier to catch
* in the debugger, until bmesh_error is replaced with something
* better. */
BLI_assert(0);
}
static void bmesh_mempool_init(BMesh *bm, const int allocsize[4])
{
bm->vpool = BLI_mempool_create(sizeof(BMVert), allocsize[0], allocsize[0], FALSE, TRUE);
bm->epool = BLI_mempool_create(sizeof(BMEdge), allocsize[1], allocsize[1], FALSE, TRUE);
bm->lpool = BLI_mempool_create(sizeof(BMLoop), allocsize[2], allocsize[2], FALSE, FALSE);
bm->fpool = BLI_mempool_create(sizeof(BMFace), allocsize[3], allocsize[3], FALSE, TRUE);
#ifdef USE_BMESH_HOLES
bm->looplistpool = BLI_mempool_create(sizeof(BMLoopList), allocsize[3], allocsize[3], FALSE, FALSE);
#endif
/* allocate one flag pool that we dont get rid of. */
bm->toolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), 512, 512, FALSE, FALSE);
}
/*
* BMESH MAKE MESH
*
* Allocates a new BMesh structure.
* Returns -
* Pointer to a BM
*
*/
BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4])
{
/* allocate the structure */
BMesh *bm = MEM_callocN(sizeof(BMesh), __func__);
bm->ob = ob;
/* allocate the memory pools for the mesh elements */
bmesh_mempool_init(bm, allocsize);
/* allocate one flag pool that we dont get rid of. */
bm->stackdepth = 1;
bm->totflags = 1;
return bm;
}
/*
* BMESH FREE MESH
*
* Frees a BMesh structure.
*/
void BM_mesh_data_free(BMesh *bm)
{
BMVert *v;
BMEdge *e;
BMLoop *l;
BMFace *f;
BMIter verts;
BMIter edges;
BMIter faces;
BMIter loops;
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) {
CustomData_bmesh_free_block(&(bm->vdata), &(v->head.data));
}
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
CustomData_bmesh_free_block(&(bm->edata), &(e->head.data));
}
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
CustomData_bmesh_free_block(&(bm->pdata), &(f->head.data));
for (l = BM_iter_new(&loops, bm, BM_LOOPS_OF_FACE, f); l; l = BM_iter_step(&loops)) {
CustomData_bmesh_free_block(&(bm->ldata), &(l->head.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->lpool);
BLI_mempool_destroy(bm->fpool);
/* destroy flag pool */
BLI_mempool_destroy(bm->toolflagpool);
#ifdef USE_BMESH_HOLES
BLI_mempool_destroy(bm->looplistpool);
#endif
/* These tables aren't used yet, so it's not stricly necessary
* to 'end' them (with 'e' param) but if someone tries to start
* using them, having these in place will save a lot of pain */
mesh_octree_table(NULL, NULL, NULL, 'e');
mesh_mirrtopo_table(NULL, 'e');
BLI_freelistN(&bm->selected);
BMO_error_clear(bm);
}
void BM_mesh_clear(BMesh *bm)
{
Object *ob = bm->ob;
/* free old mesh */
BM_mesh_data_free(bm);
memset(bm, 0, sizeof(BMesh));
/* re-initialize mesh */
bm->ob = ob;
/* allocate the memory pools for the mesh elements */
bmesh_mempool_init(bm, bm_mesh_allocsize_default);
bm->stackdepth = 1;
bm->totflags = 1;
}
/*
* BMESH FREE MESH
*
* Frees a BMesh structure.
*/
void BM_mesh_free(BMesh *bm)
{
BM_mesh_data_free(bm);
MEM_freeN(bm);
}
/*
* BMESH COMPUTE NORMALS
*
* Updates the normals of a mesh.
* Note that this can only be called
*
*/
void BM_mesh_normals_update(BMesh *bm)
{
BMVert *v;
BMFace *f;
BMLoop *l;
BMEdge *e;
BMIter verts;
BMIter faces;
BMIter loops;
BMIter edges;
unsigned int maxlength = 0;
int index;
float (*projectverts)[3];
float (*edgevec)[3];
/* first, find out the largest face in mesh */
BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) {
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN))
continue;
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 */
BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) {
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN))
continue;
#if 0 /* UNUSED */
if (f->head.flag & BM_NONORMCALC)
continue;
#endif
bmesh_update_face_normal(bm, f, f->no, projectverts);
}
/* Zero out vertex normals */
BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) {
if (BM_elem_flag_test(v, BM_ELEM_HIDDEN))
continue;
zero_v3(v->no);
}
/* compute normalized direction vectors for each edge. directions will be
* used below for calculating the weights of the face normals on the vertex
* normals */
index = 0;
edgevec = MEM_callocN(sizeof(float) * 3 * bm->totedge, "BM normal computation array");
BM_ITER(e, &edges, bm, BM_EDGES_OF_MESH, NULL) {
BM_elem_index_set(e, index); /* set_inline */
if (e->l) {
sub_v3_v3v3(edgevec[index], e->v2->co, e->v1->co);
normalize_v3(edgevec[index]);
}
else {
/* the edge vector will not be needed when the edge has no radial */
}
index++;
}
bm->elem_index_dirty &= ~BM_EDGE;
/* add weighted face normals to vertices */
BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) {
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN))
continue;
BM_ITER(l, &loops, bm, BM_LOOPS_OF_FACE, f) {
float *e1diff, *e2diff;
float dotprod;
float fac;
/* calculate the dot product of the two edges that
* meet at the loop's vertex */
e1diff = edgevec[BM_elem_index_get(l->prev->e)];
e2diff = edgevec[BM_elem_index_get(l->e)];
dotprod = dot_v3v3(e1diff, e2diff);
/* edge vectors are calculated from e->v1 to e->v2, so
* adjust the dot product if one but not both loops
* actually runs from from e->v2 to e->v1 */
if ((l->prev->e->v1 == l->prev->v) ^ (l->e->v1 == l->v)) {
dotprod = -dotprod;
}
fac = saacos(-dotprod);
/* accumulate weighted face normal into the vertex's normal */
madd_v3_v3fl(l->v->no, f->no, fac);
}
}
/* normalize the accumulated vertex normals */
BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) {
if (BM_elem_flag_test(v, BM_ELEM_HIDDEN))
continue;
if (normalize_v3(v->no) == 0.0f) {
normalize_v3_v3(v->no, v->co);
}
}
MEM_freeN(edgevec);
MEM_freeN(projectverts);
}
/*
This function ensures correct normals for the mesh, but
sets the flag BM_ELEM_TAG in flipped faces, to allow restoration
of original normals.
if undo is 0: calculate right normals
if undo is 1: restore original normals
*/
//keep in sycn with utils.c!
#define FACE_FLIP 8
static void bmesh_rationalize_normals(BMesh *bm, int undo)
{
BMOperator bmop;
BMFace *f;
BMIter iter;
if (undo) {
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BM_elem_flag_test(f, BM_ELEM_TAG)) {
BM_face_normal_flip(bm, f);
}
BM_elem_flag_disable(f, BM_ELEM_TAG);
}
return;
}
BMO_op_initf(bm, &bmop, "righthandfaces faces=%af doflip=%d", FALSE);
BMO_push(bm, &bmop);
bmesh_righthandfaces_exec(bm, &bmop);
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, f, FACE_FLIP))
BM_elem_flag_enable(f, BM_ELEM_TAG);
else BM_elem_flag_disable(f, BM_ELEM_TAG);
}
BMO_pop(bm);
BMO_op_finish(bm, &bmop);
}
static void bmesh_set_mdisps_space(BMesh *bm, int from, int to)
{
/* switch multires data out of tangent space */
if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
Object *ob = bm->ob;
BMEditMesh *em = BMEdit_Create(bm, FALSE);
DerivedMesh *dm = CDDM_from_BMEditMesh(em, NULL, TRUE, FALSE);
MDisps *mdisps;
BMFace *f;
BMIter iter;
// int i = 0; // UNUSED
multires_set_space(dm, ob, from, to);
mdisps = CustomData_get_layer(&dm->loopData, CD_MDISPS);
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
BMLoop *l;
BMIter liter;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
MDisps *lmd = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS);
if (!lmd->disps) {
printf("%s: warning - 'lmd->disps' == NULL\n", __func__);
}
if (lmd->disps && lmd->totdisp == mdisps->totdisp) {
memcpy(lmd->disps, mdisps->disps, sizeof(float) * 3 * lmd->totdisp);
}
else if (mdisps->disps) {
if (lmd->disps)
MEM_freeN(lmd->disps);
lmd->disps = MEM_dupallocN(mdisps->disps);
lmd->totdisp = mdisps->totdisp;
}
mdisps++;
// i += 1;
}
}
dm->needsFree = 1;
dm->release(dm);
/* setting this to NULL prevents BMEdit_Free from freeing it */
em->bm = NULL;
BMEdit_Free(em);
MEM_freeN(em);
}
}
/*
* 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.
*/
void bmesh_begin_edit(BMesh *bm, int flag)
{
bm->opflag = flag;
/* Most operators seem to be using BMO_OP_FLAG_UNTAN_MULTIRES to change the MDisps to
* absolute space during mesh edits. With this enabled, changes to the topology
* (loop cuts, edge subdivides, etc) are not reflected in the higher levels of
* the mesh at all, which doesn't seem right. Turning off completely for now,
* until this is shown to be better for certain types of mesh edits. */
#if BMOP_UNTAN_MULTIRES_ENABLED
/* switch multires data out of tangent space */
if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
bmesh_set_mdisps_space(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE);
/* ensure correct normals, if possible */
bmesh_rationalize_normals(bm, 0);
BM_mesh_normals_update(bm);
}
else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
bmesh_rationalize_normals(bm, 0);
}
#else
if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
bmesh_rationalize_normals(bm, 0);
}
#endif
}
void bmesh_end_edit(BMesh *bm, int flag)
{
/* BMO_OP_FLAG_UNTAN_MULTIRES disabled for now, see comment above in bmesh_begin_edit. */
#if BMOP_UNTAN_MULTIRES_ENABLED
/* switch multires data into tangent space */
if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
/* set normals to their previous winding */
bmesh_rationalize_normals(bm, 1);
bmesh_set_mdisps_space(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT);
}
else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
bmesh_rationalize_normals(bm, 1);
}
#else
if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
bmesh_rationalize_normals(bm, 1);
}
#endif
bm->opflag = 0;
/* compute normals, clear temp flags and flush selections */
BM_mesh_normals_update(bm);
BM_mesh_select_mode_flush(bm);
}
void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag)
{
BMIter iter;
BMHeader *ele;
#ifdef DEBUG
BM_ELEM_INDEX_VALIDATE(bm, "Should Never Fail!", __func__);
#endif
if (hflag & BM_VERT) {
if (bm->elem_index_dirty & BM_VERT) {
int index = 0;
BM_ITER(ele, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BM_elem_index_set(ele, index); /* set_ok */
index++;
}
bm->elem_index_dirty &= ~BM_VERT;
BLI_assert(index == bm->totvert);
}
else {
// printf("%s: skipping vert index calc!\n", __func__);
}
}
if (hflag & BM_EDGE) {
if (bm->elem_index_dirty & BM_EDGE) {
int index = 0;
BM_ITER(ele, &iter, bm, BM_EDGES_OF_MESH, NULL) {
BM_elem_index_set(ele, index); /* set_ok */
index++;
}
bm->elem_index_dirty &= ~BM_EDGE;
BLI_assert(index == bm->totedge);
}
else {
// printf("%s: skipping edge index calc!\n", __func__);
}
}
if (hflag & BM_FACE) {
if (bm->elem_index_dirty & BM_FACE) {
int index = 0;
BM_ITER(ele, &iter, bm, BM_FACES_OF_MESH, NULL) {
BM_elem_index_set(ele, index); /* set_ok */
index++;
}
bm->elem_index_dirty &= ~BM_FACE;
BLI_assert(index == bm->totface);
}
else {
// printf("%s: skipping face index calc!\n", __func__);
}
}
}
/* array checking/setting macros */
/* currently vert/edge/loop/face index data is being abused, but we should
* eventually be able to rely on it being valid. To this end, there are macros
* that validate them (so blender doesnt crash), but also print errors so we can
* fix the offending parts of the code, this way after some months we can
* confine this code for debug mode.
*
*
*/
void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func,
const char *msg_a, const char *msg_b)
{
const char iter_types[3] = {BM_VERTS_OF_MESH,
BM_EDGES_OF_MESH,
BM_FACES_OF_MESH};
const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE};
const char *type_names[3] = {"vert", "edge", "face"};
BMIter iter;
BMHeader *ele;
int i;
int is_any_error = 0;
for (i = 0; i < 3; i++) {
const int is_dirty = (flag_types[i] & bm->elem_index_dirty);
int index = 0;
int is_error = FALSE;
int err_val = 0;
int err_idx = 0;
BM_ITER(ele, &iter, bm, iter_types[i], NULL) {
if (!is_dirty) {
if (BM_elem_index_get(ele) != index) {
err_val = BM_elem_index_get(ele);
err_idx = index;
is_error = TRUE;
}
}
BM_elem_index_set(ele, index); /* set_ok */
index++;
}
if ((is_error == TRUE) && (is_dirty == FALSE)) {
is_any_error = TRUE;
fprintf(stderr,
"Invalid Index: at %s, %s, %s[%d] invalid index %d, '%s', '%s'\n",
location, func, type_names[i], err_idx, err_val, msg_a, msg_b);
}
else if ((is_error == FALSE) && (is_dirty == TRUE)) {
#if 0 /* mostly annoying */
/* dirty may have been incorrectly set */
fprintf(stderr,
"Invalid Dirty: at %s, %s (%s), dirty flag was set but all index values are correct, '%s', '%s'\n",
location, func, type_names[i], msg_a, msg_b);
#endif
}
}
#if 0 /* mostly annoying, even in debug mode */
#ifdef DEBUG
if (is_any_error == 0) {
fprintf(stderr,
"Valid Index Success: at %s, %s, '%s', '%s'\n",
location, func, msg_a, msg_b);
}
#endif
#endif
(void) is_any_error; /* shut up the compiler */
}
BMVert *BM_vert_at_index(BMesh *bm, const int index)
{
return BLI_mempool_findelem(bm->vpool, index);
}
BMEdge *BM_edge_at_index(BMesh *bm, const int index)
{
return BLI_mempool_findelem(bm->epool, index);
}
BMFace *BM_face_at_index(BMesh *bm, const int index)
{
return BLI_mempool_findelem(bm->fpool, index);
}
+769
View File
@@ -0,0 +1,769 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_mods.c
* \ingroup bmesh
*
* This file contains functions for locally modifying
* the topology of existing mesh data. (split, join, flip etc).
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "BLI_smallhash.h"
#include "BKE_customdata.h"
#include "bmesh.h"
#include "bmesh_private.h"
/**
* bmesh_dissolve_disk
*
* Turns the face region surrounding a manifold vertex into
* A single polygon.
*
*
* Example:
*
* |=========| |=========|
* | \ / | | |
* Before: | V | After: | |
* | / \ | | |
* |=========| |=========|
*
*
*/
#if 1
int BM_vert_dissolve(BMesh *bm, BMVert *v)
{
BMIter iter;
BMEdge *e;
int len = 0;
if (!v) {
return FALSE;
}
e = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, v);
for ( ; e; e = BM_iter_step(&iter)) {
len++;
}
if (len == 1) {
if (v->e)
BM_edge_kill(bm, v->e);
BM_vert_kill(bm, v);
return TRUE;
}
if (!BM_vert_is_manifold(bm, v)) {
if (!v->e) BM_vert_kill(bm, v);
else if (!v->e->l) {
BM_edge_kill(bm, v->e);
BM_vert_kill(bm, v);
}
else {
return FALSE;
}
return TRUE;
}
return BM_disk_dissolve(bm, v);
}
int BM_disk_dissolve(BMesh *bm, BMVert *v)
{
BMFace *f, *f2;
BMEdge *e, *keepedge = NULL, *baseedge = NULL;
int len = 0;
if (!BM_vert_is_manifold(bm, v)) {
return FALSE;
}
if (v->e) {
/* v->e we keep, what else */
e = v->e;
do {
e = bmesh_disk_nextedge(e, v);
if (!(BM_edge_share_faces(e, v->e))) {
keepedge = e;
baseedge = v->e;
break;
}
len++;
} while (e != v->e);
}
/* 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. . */
BMLoop *loop = e->l;
if (loop->v == v) loop = loop->next;
if (!BM_face_split(bm, loop->f, v, loop->v, NULL, NULL))
return FALSE;
if (!BM_disk_dissolve(bm, v)) {
return FALSE;
}
return TRUE;
}
else if (keepedge == NULL && len == 2) {
/* collapse the verte */
e = BM_vert_collapse_faces(bm, v->e, v, 1.0, TRUE);
if (!e) {
return FALSE;
}
/* handle two-valenc */
f = e->l->f;
f2 = e->l->radial_next->f;
if (f != f2 && !BM_faces_join_pair(bm, f, f2, e)) {
return FALSE;
}
return TRUE;
}
if (keepedge) {
int done = 0;
while (!done) {
done = 1;
e = v->e;
do {
f = NULL;
len = bmesh_radial_length(e->l);
if (len == 2 && (e != baseedge) && (e != keepedge)) {
f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
/* return if couldn't join faces in manifold
* conditions */
//!disabled for testing why bad things happen
if (!f) {
return FALSE;
}
}
if (f) {
done = 0;
break;
}
e = bmesh_disk_nextedge(e, v);
} while (e != v->e);
}
/* collapse the verte */
e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, TRUE);
if (!e) {
return FALSE;
}
/* get remaining two face */
f = e->l->f;
f2 = e->l->radial_next->f;
if (f != f2) {
/* join two remaining face */
if (!BM_faces_join_pair(bm, f, f2, e)) {
return FALSE;
}
}
}
return TRUE;
}
#else
void BM_disk_dissolve(BMesh *bm, BMVert *v)
{
BMFace *f;
BMEdge *e;
BMIter iter;
int done, len;
if (v->e) {
done = 0;
while (!done) {
done = 1;
/* loop the edges looking for an edge to dissolv */
for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, v); e;
e = BM_iter_step(&iter)) {
f = NULL;
len = bmesh_cycle_length(&(e->l->radial));
if (len == 2) {
f = BM_faces_join_pair(bm, e->l->f, ((BMLoop *)(e->l->radial_next))->f, e);
}
if (f) {
done = 0;
break;
}
};
}
BM_vert_collapse_faces(bm, v->e, v, 1.0, TRUE);
}
}
#endif
/**
* BM_faces_join_pair
*
* Joins two adjacenct faces togather.
*
* Because this method calls to BM_faces_join to do its work, ff a pair
* of faces share multiple edges, the pair of faces will be joined at
* every edge (not just edge e). This part of the functionality might need
* to be reconsidered.
*
* If the windings do not match the winding of the new face will follow
* f1's winding (i.e. f2 will be reversed before the join).
*
* Returns:
* pointer to the combined face
*/
BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
{
BMLoop *l1, *l2;
BMEdge *jed = NULL;
BMFace *faces[2] = {f1, f2};
jed = e;
if (!jed) {
BMLoop *l_first;
/* search for an edge that has both these faces in its radial cycl */
l1 = l_first = BM_FACE_FIRST_LOOP(f1);
do {
if (l1->radial_next->f == f2) {
jed = l1->e;
break;
}
} while ((l1 = l1->next) != l_first);
}
if (!jed) {
bmesh_error();
return NULL;
}
l1 = jed->l;
if (!l1) {
bmesh_error();
return NULL;
}
l2 = l1->radial_next;
if (l1->v == l2->v) {
bmesh_loop_reverse(bm, f2);
}
f1 = BM_faces_join(bm, faces, 2);
return f1;
}
/* 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_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf)
{
BMIter iter, iter2;
BMVert *v;
BMLoop *nl;
BMFace *face;
/* be warned: this can do weird things in some ngon situation, see BM_LegalSplit */
for (face = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v1); face; face = BM_iter_step(&iter)) {
for (v = BM_iter_new(&iter2, bm, BM_VERTS_OF_FACE, face); v; v = BM_iter_step(&iter2)) {
if (v == v2) {
face = BM_face_split(bm, face, v1, v2, &nl, NULL);
if (nf) *nf = face;
return nl->e;
}
}
}
return NULL;
}
/**
* BM_face_split
*
* Splits a single face into two.
*
* f - the original face
* v1 & v2 - vertices which define the split edge, must be different
* nl - pointer which will receive the BMLoop for the split edge in the new face
*
* Notes: the
* Returns -
* Pointer to the newly created face representing one side of the split
* if the split is successful (and the original original face will be the
* other side). NULL if the split fails.
*
*/
BMFace *BM_face_split(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **nl, BMEdge *UNUSED(example))
{
const int has_mdisp = CustomData_has_layer(&bm->ldata, CD_MDISPS);
BMFace *nf, *of;
/* do we have a multires layer */
if (has_mdisp) {
of = BM_face_copy(bm, f, 0, 0);
}
#ifdef USE_BMESH_HOLES
nf = bmesh_sfme(bm, f, v1, v2, nl, NULL);
#else
nf = bmesh_sfme(bm, f, v1, v2, nl);
#endif
if (nf) {
BM_elem_attrs_copy(bm, bm, f, nf);
copy_v3_v3(nf->no, f->no);
/* handle multires update */
if (has_mdisp && (nf != f)) {
BMLoop *l_iter;
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE);
} while ((l_iter = l_iter->next) != l_first);
l_iter = l_first = BM_FACE_FIRST_LOOP(nf);
do {
BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE);
} while ((l_iter = l_iter->next) != l_first);
BM_face_kill(bm, of);
BM_face_multires_bounds_smooth(bm, f);
BM_face_multires_bounds_smooth(bm, nf);
}
}
return nf;
}
/**
* BM_vert_collapse_faces
*
* 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.
*
* Note this function is very close to 'BM_vert_collapse_edges', both collapse
* a vertex and return a new edge. Except this takes a factor and merges
* custom data.
*
* BMESH_TODO:
* Insert error checking for KV valance.
*
* @param fac The factor along the edge
* @param join_faces When true the faces around the vertex will be joined
* otherwise collapse the vertex by merging the 2 edges this vert touches into one.
* @returns The New Edge
*/
BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, const int join_faces)
{
BMEdge *ne = NULL;
BMVert *tv = bmesh_edge_getothervert(ke, kv);
BMEdge *e2;
BMVert *tv2;
BMIter iter;
BMLoop *l_iter = NULL, *kvloop = NULL, *tvloop = NULL;
void *src[2];
float w[2];
/* Only intended to be called for 2-valence vertices */
BLI_assert(bmesh_disk_count(kv) <= 2);
/* first modify the face loop data */
w[0] = 1.0f - fac;
w[1] = fac;
if (ke->l) {
l_iter = ke->l;
do {
if (l_iter->v == tv && l_iter->next->v == kv) {
tvloop = l_iter;
kvloop = l_iter->next;
src[0] = kvloop->head.data;
src[1] = tvloop->head.data;
CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, kvloop->head.data);
}
} while ((l_iter = l_iter->radial_next) != ke->l);
}
/* now interpolate the vertex data */
BM_data_interp_from_verts(bm, kv, tv, kv, fac);
e2 = bmesh_disk_nextedge(ke, kv);
tv2 = BM_edge_other_vert(e2, kv);
if (join_faces) {
BMFace **faces = NULL, *f;
BLI_array_staticdeclare(faces, 8);
BM_ITER(f, &iter, bm, BM_FACES_OF_VERT, kv) {
BLI_array_append(faces, f);
}
if (BLI_array_count(faces) >= 2) {
BMFace *f2 = BM_faces_join(bm, faces, BLI_array_count(faces));
if (f2) {
BMLoop *nl = NULL;
if (BM_face_split(bm, f2, tv, tv2, &nl, NULL)) {
ne = nl->e;
}
}
}
BLI_array_free(faces);
return ne;
}
/* single face or no faces */
/* same as BM_vert_collapse_edges() however we already
* have vars to perform this operation so dont call. */
bmesh_jekv(bm, ke, kv);
ne = BM_edge_exists(tv, tv2);
return ne;
}
/**
* BM_vert_collapse_edges
*
* Collapses a vertex onto another vertex it shares an edge with.
*
* Returns -
* The New Edge
*/
BMEdge *BM_vert_collapse_edges(BMesh *bm, BMEdge *ke, BMVert *kv)
{
/* nice example implimentation but we want loops to have their customdata
* accounted for */
#if 0
BMEdge *ne = NULL;
/* Collapse between 2 edges */
/* in this case we want to keep all faces and not join them,
* rather just get rid of the veretex - see bug [#28645] */
BMVert *tv = bmesh_edge_getothervert(ke, kv);
if (tv) {
BMEdge *e2 = bmesh_disk_nextedge(ke, kv);
if (e2) {
BMVert *tv2 = BM_edge_other_vert(e2, kv);
if (tv2) {
/* only action, other calls here only get the edge to return */
bmesh_jekv(bm, ke, kv);
ne = BM_edge_exists(tv, tv2);
}
}
}
return ne;
#else
/* with these args faces are never joined, same as above
* but account for loop customdata */
return BM_vert_collapse_faces(bm, ke, kv, 1.0f, FALSE);
#endif
}
#undef DO_V_INTERP
/**
* 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_edge_split(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent)
{
BMVert *nv, *v2;
BMFace **oldfaces = NULL;
BMEdge *dummy;
BLI_array_staticdeclare(oldfaces, 32);
SmallHash hash;
/* we need this for handling multire */
if (!ne)
ne = &dummy;
/* do we have a multires layer */
if (CustomData_has_layer(&bm->ldata, CD_MDISPS) && e->l) {
BMLoop *l;
int i;
l = e->l;
do {
BLI_array_append(oldfaces, l->f);
l = l->radial_next;
} while (l != e->l);
/* create a hash so we can differentiate oldfaces from new face */
BLI_smallhash_init(&hash);
for (i = 0; i < BLI_array_count(oldfaces); i++) {
oldfaces[i] = BM_face_copy(bm, oldfaces[i], 1, 1);
BLI_smallhash_insert(&hash, (intptr_t)oldfaces[i], NULL);
}
}
v2 = bmesh_edge_getothervert(e, v);
nv = bmesh_semv(bm, v, e, ne);
if (nv == NULL) {
return NULL;
}
sub_v3_v3v3(nv->co, v2->co, v->co);
madd_v3_v3v3fl(nv->co, v->co, nv->co, percent);
if (ne) {
(*ne)->head.hflag = e->head.hflag;
BM_elem_attrs_copy(bm, bm, e, *ne);
}
/* v->nv->v2 */
BM_data_interp_face_vert_edge(bm, v2, v, nv, e, percent);
BM_data_interp_from_verts(bm, v, v2, nv, percent);
if (CustomData_has_layer(&bm->ldata, CD_MDISPS) && e->l && nv) {
int i, j;
/* interpolate new/changed loop data from copied old face */
for (j = 0; j < 2; j++) {
for (i = 0; i < BLI_array_count(oldfaces); i++) {
BMEdge *e1 = j ? *ne : e;
BMLoop *l, *l2;
l = e1->l;
if (!l) {
bmesh_error();
break;
}
do {
if (!BLI_smallhash_haskey(&hash, (intptr_t)l->f)) {
BMLoop *l2_first;
l2 = l2_first = BM_FACE_FIRST_LOOP(l->f);
do {
BM_loop_interp_multires(bm, l2, oldfaces[i]);
} while ((l2 = l2->next) != l2_first);
}
l = l->radial_next;
} while (l != e1->l);
}
}
/* destroy the old face */
for (i = 0; i < BLI_array_count(oldfaces); i++) {
BM_face_verts_kill(bm, oldfaces[i]);
}
/* fix boundaries a bit, doesn't work too well quite ye */
#if 0
for (j = 0; j < 2; j++) {
BMEdge *e1 = j ? *ne : e;
BMLoop *l, *l2;
l = e1->l;
if (!l) {
bmesh_error();
break;
}
do {
BM_face_multires_bounds_smooth(bm, l->f);
l = l->radial_next;
} while (l != e1->l);
}
#endif
BLI_array_free(oldfaces);
BLI_smallhash_release(&hash);
}
return nv;
}
BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts)
{
int i;
float percent;
BMVert *nv = NULL;
for (i = 0; i < numcuts; i++) {
percent = 1.0f / (float)(numcuts + 1 - i);
nv = BM_edge_split(bm, e->v2, e, NULL, percent);
}
return nv;
}
int BM_face_validate(BMesh *bm, BMFace *face, FILE *err)
{
BMIter iter;
BLI_array_declare(verts);
BMVert **verts = NULL;
BMLoop *l;
int ret = 1, i, j;
if (face->len == 2) {
fprintf(err, "warning: found two-edged face. face ptr: %p\n", face);
fflush(err);
}
for (l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, face); l; l = BM_iter_step(&iter)) {
BLI_array_growone(verts);
verts[BLI_array_count(verts) - 1] = l->v;
if (l->e->v1 == l->e->v2) {
fprintf(err, "Found bmesh edge with identical verts!\n");
fprintf(err, " edge ptr: %p, vert: %p\n", l->e, l->e->v1);
fflush(err);
ret = 0;
}
}
for (i = 0; i < BLI_array_count(verts); i++) {
for (j = 0; j < BLI_array_count(verts); j++) {
if (j == i) {
continue;
}
if (verts[i] == verts[j]) {
fprintf(err, "Found duplicate verts in bmesh face!\n");
fprintf(err, " face ptr: %p, vert: %p\n", face, verts[i]);
fflush(err);
ret = 0;
}
}
}
BLI_array_free(verts);
return ret;
}
/*
* BM Rotate Edge
*
* Spins an edge topologically, either counter-clockwise or clockwise.
* If ccw is true, the edge is spun counter-clockwise, otherwise it is
* spun clockwise.
*
* Returns the spun edge. Note that this works by dissolving the edge
* then re-creating it, so the returned edge won't have the same pointer
* address as the original one.
*
* Returns NULL on error (e.g., if the edge isn't surrounded by exactly
* two faces).
*/
BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw)
{
BMVert *v1, *v2;
BMLoop *l, *l1, *l2, *nl;
BMFace *f;
BMIter liter;
v1 = e->v1;
v2 = e->v2;
if (BM_edge_face_count(e) != 2)
return NULL;
/* If either of e's vertices has valence 2, then
* dissolving the edge would leave a spur, so not allowed */
if (BM_vert_edge_count(e->v1) == 2 || BM_vert_edge_count(e->v2) == 2)
return NULL;
f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
if (f == NULL)
return NULL;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
if (l->v == v1)
l1 = l;
else if (l->v == v2)
l2 = l;
}
if (ccw) {
l1 = l1->prev;
l2 = l2->prev;
}
else {
l1 = l1->next;
l2 = l2->next;
}
if (!BM_face_split(bm, f, l1->v, l2->v, &nl, NULL))
return NULL;
return nl->e;
}
BMVert *BM_vert_rip ( BMesh *bm, BMFace *sf, BMVert *sv)
{
return bmesh_urmv(bm, sf, sv);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,106 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_OPERATORS_PRIVATE_H__
#define __BMESH_OPERATORS_PRIVATE_H__
/** \file blender/bmesh/intern/bmesh_operators_private.h
* \ingroup bmesh
*/
struct BMesh;
struct BMOperator;
void BMO_push(BMesh *bm, BMOperator *op);
void BMO_pop(BMesh *bm);
void splitop_exec(BMesh *bm, BMOperator *op);
void spinop_exec(BMesh *bm, BMOperator *op);
void dupeop_exec(BMesh *bm, BMOperator *op);
void delop_exec(BMesh *bm, 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 dissolvelimit_exec(BMesh *bmesh, BMOperator *op);
void bmesh_make_fgons_exec(BMesh *bmesh, BMOperator *op);
void extrude_edge_context_exec(BMesh *bm, BMOperator *op);
void connectverts_exec(BMesh *bm, BMOperator *op);
void makeprim_exec(BMesh *bm, BMOperator *op);
void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op);
void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op);
void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op);
void bmesh_translate_exec(BMesh *bm, BMOperator *op);
void bmesh_transform_exec(BMesh *bm, BMOperator *op);
void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op);
void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op);
void bmesh_rotate_exec(BMesh *bm, BMOperator *op);
void bmesh_makevert_exec(BMesh *bm, BMOperator *op);
void dissolveedges_exec(BMesh *bm, BMOperator *op);
void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op);
void bmesh_weldverts_exec(BMesh *bm, BMOperator *op);
void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op);
void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op);
void bmesh_mirror_exec(BMesh *bm, BMOperator *op);
void esplit_exec(BMesh *bm, BMOperator *op);
void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op);
void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op);
void bmesh_regionextend_exec(BMesh *bm, BMOperator *op);
void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op);
void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op);
void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op);
void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op);
void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op);
void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op);
void bmesh_collapse_exec(BMesh *bm, BMOperator *op);
void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op);
void bmesh_similaredges_exec(BMesh *bm, BMOperator *op);
void bmesh_similarverts_exec(BMesh *bm, BMOperator *op);
void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op);
void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op);
void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op);
void object_load_bmesh_exec(BMesh *bm, BMOperator *op);
void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op);
void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op);
void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op);
void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op);
void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op);
void bmesh_scale_exec(BMesh *bm, BMOperator *op);
void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op);
void bmesh_automerge_exec(BMesh *bm, BMOperator *op);
void bmesh_create_cone_exec(BMesh *bm, BMOperator *op);
void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op);
void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op);
void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op);
void bmesh_create_grid_exec(BMesh *bm, BMOperator *op);
void bmesh_create_cube_exec(BMesh *bm, BMOperator *op);
void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op);
void bmesh_bevel_exec(BMesh *bm, BMOperator *op);
void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op);
void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op);
void bmesh_create_circle_exec(BMesh *bm, BMOperator *op);
void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op);
void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op);
#endif /* __BMESH_OPERATORS_PRIVATE_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,98 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 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__
/** \file blender/bmesh/intern/bmesh_private.h
* \ingroup bmesh
*
* Private function prototypes for bmesh public API.
* This file is a grab-bag of functions from various
* parts of the bmesh internals.
*/
struct Link;
struct BMLoop;
/* returns positive nonzero on error */
int bmesh_check_element(BMesh *bm, void *element, const char htype);
#define BM_CHECK_ELEMENT(bm, el) \
if (bmesh_check_element(bm, el, ((BMHeader*)el)->htype)) { \
printf("check_element failure, with code %i on line %i in file\n" \
" \"%s\"\n\n", \
bmesh_check_element(bm, el, ((BMHeader*)el)->htype), \
__LINE__, __FILE__); \
}
#define BM_EDGE_DISK_LINK_GET(e, v) ( \
((v) == ((BMEdge*)(e))->v1) ? \
&((e)->v1_disk_link) : \
&((e)->v2_disk_link) \
)
int bmesh_radial_length(struct BMLoop *l);
int bmesh_disk_count(BMVert *v);
/* 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);
/* NOTE: ensure different parts of the API do not conflict
* on using these internal flags!*/
#define _FLAG_JF 1 /* join faces */
#define _FLAG_MF 2 /* make face */
#define BM_ELEM_API_FLAG_ENABLE(element, f) ((element)->oflags[0].pflag |= (f))
#define BM_ELEM_API_FLAG_DISABLE(element, f) ((element)->oflags[0].pflag &= ~(f))
#define BM_ELEM_API_FLAG_TEST(element, f) ((element)->oflags[0].pflag & (f))
/* Polygon Utilities ? FIXME... where do these each go? */
/* newedgeflag sets a flag layer flag, obviously not the header flag. */
void BM_face_triangulate(BMesh *bm, BMFace *f, float (*projectverts)[3],
const short newedge_oflag, const short newface_oflag, BMFace **newfaces);
void bmesh_update_face_normal(struct BMesh *bm, struct BMFace *f, float no[3],
float (*projectverts)[3]);
void bmesh_update_face_normal_vertex_cos(struct BMesh *bm, struct BMFace *f, float no[3],
float (*projectverts)[3], float (*vertexCos)[3]);
void compute_poly_plane(float (*verts)[3], int nverts);
void poly_rotate_plane(const float normal[3], float (*verts)[3], const int nverts);
void bmesh_flip_normal(struct BMesh *bm, struct BMFace *f);
BMEdge *bmesh_disk_next(BMEdge *e, BMVert *v);
BMEdge *bmesh_disk_prev(BMEdge *e, BMVert *v);
/* include the rest of our private declarations */
#include "bmesh_structure.h"
#include "bmesh_operators_private.h"
#endif /* __BMESH_PRIVATE_H__ */
+658
View File
@@ -0,0 +1,658 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_queries.c
* \ingroup bmesh
*
* 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.
*/
#include "BLI_math.h"
#include "bmesh.h"
#include "bmesh_private.h"
#define BM_OVERLAP (1 << 13)
/*
* BMESH COUNT ELEMENT
*
* Return the amount of element of
* type 'type' in a given bmesh.
*/
int BM_mesh_elem_count(BMesh *bm, const char htype)
{
if (htype == BM_VERT) return bm->totvert;
else if (htype == BM_EDGE) return bm->totedge;
else if (htype == 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 OTHER EDGE IN FACE SHARING A VERTEX
*
* Returns an opposing loop that shares the same face.
*
*/
BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v)
{
BMLoop *l_iter;
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (l_iter->e == e) {
break;
}
} while ((l_iter = l_iter->next) != l_first);
return l_iter->v == v ? l_iter->prev : l_iter->next;
}
/*
* 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_iter, *l_first;
#ifdef USE_BMESH_HOLES
BMLoopList *lst;
for (lst = f->loops.first; lst; lst = lst->next)
#endif
{
#ifdef USE_BMESH_HOLES
l_iter = l_first = lst->first;
#else
l_iter = l_first = f->l_first;
#endif
do {
if (l_iter->v == v) {
return TRUE;
}
} while ((l_iter = l_iter->next) != l_first);
}
return FALSE;
}
/*
* 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 *l_iter, *l_first;
#ifdef USE_BMESH_HOLES
BMLoopList *lst;
#endif
int i, count = 0;
for (i = 0; i < len; i++) BMO_elem_flag_enable(bm, varr[i], BM_OVERLAP);
#ifdef USE_BMESH_HOLES
for (lst = f->loops.first; lst; lst = lst->next)
#endif
{
#ifdef USE_BMESH_HOLES
l_iter = l_first = lst->first;
#else
l_iter = l_first = f->l_first;
#endif
do {
if (BMO_elem_flag_test(bm, l_iter->v, BM_OVERLAP)) {
count++;
}
} while ((l_iter = l_iter->next) != l_first);
}
for (i = 0; i < len; i++) BMO_elem_flag_disable(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_iter;
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (l_iter->e == e) {
return TRUE;
}
} while ((l_iter = l_iter->next) != l_first);
return FALSE;
}
/*
* 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_edge_other_vert(BMEdge *e, BMVert *v)
{
return bmesh_edge_getothervert(e, v);
}
/*
* BMESH VERT EDGECOUNT
*
* Returns the number of edges around this vertex.
*/
int BM_vert_edge_count(BMVert *v)
{
return bmesh_disk_count(v);
}
/*
* BMESH EDGE FACECOUNT
*
* Returns the number of faces around this edge
*/
int BM_edge_face_count(BMEdge *e)
{
int count = 0;
BMLoop *curloop = NULL;
if (e->l) {
curloop = e->l;
do {
count++;
curloop = bmesh_radial_nextloop(curloop);
} while (curloop != e->l);
}
return count;
}
/*
* BMESH VERT FACECOUNT
*
* Returns the number of faces around this vert
*/
int BM_vert_face_count(BMVert *v)
{
int count = 0;
BMLoop *l;
BMIter iter;
BM_ITER(l, &iter, NULL, BM_LOOPS_OF_VERT, v) {
count++;
}
return count;
#if 0 //this code isn't working
BMEdge *curedge = NULL;
if (v->e) {
curedge = v->e;
do {
if (curedge->l) count += BM_edge_face_count(curedge);
curedge = bmesh_disk_nextedge(curedge, v);
} while (curedge != v->e);
}
return count;
#endif
}
/*
* 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_vert_is_wire(BMesh *UNUSED(bm), BMVert *v)
{
BMEdge *curedge;
if (v->e == NULL) {
return FALSE;
}
curedge = v->e;
do {
if (curedge->l) {
return FALSE;
}
curedge = bmesh_disk_nextedge(curedge, v);
} while (curedge != v->e);
return TRUE;
}
/*
* 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_edge_is_wire(BMesh *UNUSED(bm), BMEdge *e)
{
return (e->l) ? FALSE : TRUE;
}
/*
* 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_vert_is_manifold(BMesh *UNUSED(bm), BMVert *v)
{
BMEdge *e, *oe;
BMLoop *l;
int len, count, flag;
if (v->e == NULL) {
/* loose vert */
return FALSE;
}
/* count edges while looking for non-manifold edges */
oe = v->e;
for (len = 0, e = v->e; e != oe || (e == oe && len == 0); len++, e = bmesh_disk_nextedge(e, v)) {
if (e->l == NULL) {
/* loose edge */
return FALSE;
}
if (bmesh_radial_length(e->l) > 2) {
/* edge shared by more than two faces */
return FALSE;
}
}
count = 1;
flag = 1;
e = NULL;
oe = v->e;
l = oe->l;
while (e != oe) {
l = (l->v == v) ? l->prev : l->next;
e = l->e;
count++; /* count the edges */
if (flag && l->radial_next == l) {
/* we've hit the edge of an open mesh, reset once */
flag = 0;
count = 1;
oe = e;
e = NULL;
l = oe->l;
}
else if (l->radial_next == l) {
/* break the loop */
e = oe;
}
else {
l = l->radial_next;
}
}
if (count < len) {
/* vert shared by multiple regions */
return FALSE;
}
return TRUE;
}
/*
* 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_edge_is_manifold(BMesh *UNUSED(bm), BMEdge *e)
{
int count = BM_edge_face_count(e);
if (count != 2 && count != 1) {
return FALSE;
}
return TRUE;
}
/*
* 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_edge_is_boundry(BMEdge *e)
{
int count = BM_edge_face_count(e);
if (count == 1) {
return TRUE;
}
return FALSE;
}
/*
* BMESH FACE SHAREDEDGES
*
* Counts the number of edges two faces share (if any)
*
* BMESH_TODO:
* Move this to structure, and wrap.
*
* Returns -
* Integer
*/
int BM_face_share_edges(BMFace *f1, BMFace *f2)
{
BMLoop *l_iter;
BMLoop *l_first;
int count = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f1);
do {
if (bmesh_radial_find_face(l_iter->e, f2)) {
count++;
}
} while ((l_iter = l_iter->next) != l_first);
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->l && e2->l) {
l = e1->l;
do {
f = l->f;
if (bmesh_radial_find_face(e2, f)) {
return TRUE;
}
l = l->radial_next;
} while (l != e1->l);
}
return FALSE;
}
/**
*
* BMESH EDGE SHARE A VERTEX
*
* Tests to see if e1 shares a vertex with e2
*
*/
int BM_edge_share_vert(struct BMEdge *e1, struct BMEdge *e2)
{
return (e1->v1 == e2->v1 ||
e1->v1 == e2->v2 ||
e1->v2 == e2->v1 ||
e1->v2 == e2->v2);
}
/**
*
* BMESH EDGE ORDERED VERTS
*
* Returns the verts of an edge as used in a face
* if used in a face at all, otherwise just assign as used in the edge.
*
* Useful to get a determanistic winding order when calling
* BM_face_create_ngon() on an arbitrary array of verts,
* though be sure to pick an edge which has a face.
*
*/
void BM_edge_ordered_verts(BMEdge *edge, BMVert **r_v1, BMVert **r_v2)
{
if ( (edge->l == NULL) ||
( ((edge->l->prev->v == edge->v1) && (edge->l->v == edge->v2)) ||
((edge->l->v == edge->v1) && (edge->l->next->v == edge->v2)) )
)
{
*r_v1 = edge->v1;
*r_v2 = edge->v2;
}
else {
*r_v1 = edge->v2;
*r_v2 = edge->v1;
}
}
/*
* BMESH FACE ANGLE
*
* Calculates the angle between two faces.
* Assumes the face normals are correct.
*
* Returns -
* Float.
*/
float BM_edge_face_angle(BMesh *UNUSED(bm), BMEdge *e)
{
if (BM_edge_face_count(e) == 2) {
BMLoop *l1 = e->l;
BMLoop *l2 = e->l->radial_next;
return angle_normalized_v3v3(l1->f->no, l2->f->no);
}
else {
return (float)M_PI / 2.0f; /* acos(0.0) */
}
}
/*
* BMESH FACE ANGLE
*
* Calculates the angle a verts 2 edges.
*
* Returns -
* Float.
*/
float BM_vert_edge_angle(BMesh *UNUSED(bm), BMVert *v)
{
BMEdge *e1, *e2;
/* saves BM_vert_edge_count(v) and and edge iterator,
* get the edges and count them both at once */
if ((e1 = v->e) &&
(e2 = bmesh_disk_nextedge(e1, v)) &&
/* make sure we come full circle and only have 2 connected edges */
(e1 == bmesh_disk_nextedge(e2, v)))
{
BMVert *v1 = BM_edge_other_vert(e1, v);
BMVert *v2 = BM_edge_other_vert(e2, v);
return M_PI - angle_v3v3v3(v1->co, v->co, v2->co);
}
else {
return (float)M_PI / 2.0f; /* acos(0.0) */
}
}
/*
* BMESH EXIST FACE OVERLAPS
*
* Given a set of vertices (varr), find out if
* all those vertices overlap an existing face.
*
* Returns:
* 0 for no overlap
* 1 for overlap
*
*
*/
int BM_face_exists_overlap(BMesh *bm, BMVert **varr, int len, BMFace **overlapface)
{
BMIter vertfaces;
BMFace *f;
int i, amount;
if (overlapface) *overlapface = NULL;
for (i = 0; i < len; i++) {
f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]);
while (f) {
amount = BM_verts_in_face(bm, f, varr, len);
if (amount >= len) {
if (overlapface) *overlapface = f;
return TRUE;
}
f = BM_iter_step(&vertfaces);
}
}
return FALSE;
}
/*
* BMESH FACE EXISTS
*
* Given a set of vertices (varr), find out if
* there is a face with exactly those vertices
* (and only those vertices).
*
* Returns:
* 0 for no face found
* 1 for face found
*/
int BM_face_exists(BMesh *bm, BMVert **varr, int len, BMFace **existface)
{
BMIter vertfaces;
BMFace *f;
int i, amount;
if (existface) *existface = NULL;
for (i = 0; i < len; i++) {
f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]);
while (f) {
amount = BM_verts_in_face(bm, f, varr, len);
if (amount == len && amount == f->len) {
if (existface) *existface = f;
return TRUE;
}
f = BM_iter_step(&vertfaces);
}
}
return FALSE;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,106 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2004 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_STRUCTURE_H__
#define __BMESH_STRUCTURE_H__
/** \file blender/bmesh/intern/bmesh_structure.h
* \ingroup bmesh
*
* The lowest level of functionality for manipulating bmesh structures.
* None of these functions should ever be exported to the rest of Blender.
*
* in the vast majority of cases thes should not be used directly.
* if absolutely necassary, see function defitions in code for
* descriptive comments. but seriously, don't use this stuff.
*/
struct ListBase;
void remove_loop_radial_link(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);
/* LOOP CYCLE MANAGEMENT */
int bmesh_loop_validate(BMFace *f);
/*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);
int bmesh_radial_validate(int radlen, struct BMLoop *l);
/*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 *newv); /*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);
int bmesh_disk_validate(int len, struct BMEdge *e, struct BMVert *v);
/*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
#ifdef USE_BMESH_HOLES
, ListBase *holes
#endif
);
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, struct BMFace *f2, struct BMEdge *e);
struct BMVert *bmesh_urmv(struct BMesh *bm, struct BMFace *sf, struct BMVert *sv);
//int *bmesh_grkv(struct BMesh *bm, struct BMFace *sf, struct BMVert *kv);
#endif /* __BMESH_STRUCTURE_H__ */
+266
View File
@@ -0,0 +1,266 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Levi Schooley.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_walkers.c
* \ingroup bmesh
*
* BMesh Walker API.
*/
#include <stdlib.h>
#include "BLI_listbase.h"
#include "bmesh.h"
#include "bmesh_walkers_private.h"
/* - joeedh -
* design notes:
*
* original desing: walkers directly emulation recursive functions.
* functions save their state onto a worklist, and also add new states
* to implement recursive or looping behaviour. generally only one
* state push per call with a specific state is desired.
*
* basic design pattern: the walker step function goes through it's
* list of possible choices for recursion, and recurses (by pushing a new state)
* using the first non-visited one. this choise is the flagged as visited using
* the ghash. each step may push multiple new states onto the worklist at once.
*
* - walkers use tool flags, not header flags
* - walkers now use ghash for storing visited elements,
* rather then stealing flags. ghash can be rewritten
* to be faster if necassary, in the far future :) .
* - tools should ALWAYS have necassary error handling
* for if walkers fail.
*/
void *BMW_begin(BMWalker *walker, void *start)
{
walker->begin(walker, start);
return BMW_current_state(walker) ? walker->step(walker) : NULL;
}
/*
* BMW_CREATE
*
* Allocates and returns a new mesh walker of
* a given type. The elements visited are filtered
* by the bitmask 'searchmask'.
*/
void BMW_init(BMWalker *walker, BMesh *bm, int type,
short mask_vert, short mask_edge, short mask_loop, short mask_face,
int layer)
{
memset(walker, 0, sizeof(BMWalker));
walker->layer = layer;
walker->bm = bm;
walker->mask_vert = mask_vert;
walker->mask_edge = mask_edge;
walker->mask_loop = mask_loop;
walker->mask_face = mask_face;
walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 1");
if (type >= BMW_MAXWALKERS || type < 0) {
bmesh_error();
fprintf(stderr,
"Invalid walker type in BMW_init; type: %d, "
"searchmask: (v:%d, e:%d, l:%d, f:%d), flag: %d\n",
type, mask_vert, mask_edge, mask_loop, mask_face, layer);
}
if (type != BMW_CUSTOM) {
walker->begin = bm_walker_types[type]->begin;
walker->yield = bm_walker_types[type]->yield;
walker->step = bm_walker_types[type]->step;
walker->structsize = bm_walker_types[type]->structsize;
walker->order = bm_walker_types[type]->order;
walker->valid_mask = bm_walker_types[type]->valid_mask;
/* safety checks */
/* if this raises an error either the caller is wrong or
* 'bm_walker_types' needs updating */
BLI_assert(mask_vert == 0 || (walker->valid_mask & BM_VERT));
BLI_assert(mask_edge == 0 || (walker->valid_mask & BM_EDGE));
BLI_assert(mask_loop == 0 || (walker->valid_mask & BM_LOOP));
BLI_assert(mask_face == 0 || (walker->valid_mask & BM_FACE));
}
walker->worklist = BLI_mempool_create(walker->structsize, 100, 100, TRUE, FALSE);
walker->states.first = walker->states.last = NULL;
}
/*
* BMW_end
*
* Frees a walker's worklist.
*/
void BMW_end(BMWalker *walker)
{
BLI_mempool_destroy(walker->worklist);
BLI_ghash_free(walker->visithash, NULL, NULL);
}
/*
* BMW_step
*/
void *BMW_step(BMWalker *walker)
{
BMHeader *head;
head = BMW_walk(walker);
return head;
}
/*
* BMW_current_depth
*
* Returns the current depth of the walker.
*/
int BMW_current_depth(BMWalker *walker)
{
return walker->depth;
}
/*
* BMW_WALK
*
* Steps a mesh walker forward by one element
*
* BMESH_TODO:
* -add searchmask filtering
*/
void *BMW_walk(BMWalker *walker)
{
void *current = NULL;
while (BMW_current_state(walker)) {
current = walker->step(walker);
if (current) {
return current;
}
}
return NULL;
}
/*
* BMW_current_state
*
* Returns the first state from the walker state
* worklist. This state is the the next in the
* worklist for processing.
*/
void *BMW_current_state(BMWalker *walker)
{
bmesh_walkerGeneric *currentstate = walker->states.first;
if (currentstate) {
/* Automatic update of depth. For most walkers that
* follow the standard "Step" pattern of:
* - read current state
* - remove current state
* - push new states
* - return walk result from just-removed current state
* this simple automatic update should keep track of depth
* just fine. Walkers that deviate from that pattern may
* need to manually update the depth if they care about
* keeping it correct. */
walker->depth = currentstate->depth + 1;
}
return currentstate;
}
/*
* BMW_state_remove
*
* Remove and free an item from the end of the walker state
* worklist.
*/
void BMW_state_remove(BMWalker *walker)
{
void *oldstate;
oldstate = BMW_current_state(walker);
BLI_remlink(&walker->states, oldstate);
BLI_mempool_free(walker->worklist, oldstate);
}
/*
* BMW_newstate
*
* Allocate a new empty state and put it on the worklist.
* A pointer to the new state is returned so that the caller
* can fill in the state data. The new state will be inserted
* at the front for depth-first walks, and at the end for
* breadth-first walks.
*/
void *BMW_state_add(BMWalker *walker)
{
bmesh_walkerGeneric *newstate;
newstate = BLI_mempool_alloc(walker->worklist);
newstate->depth = walker->depth;
switch (walker->order)
{
case BMW_DEPTH_FIRST:
BLI_addhead(&walker->states, newstate);
break;
case BMW_BREADTH_FIRST:
BLI_addtail(&walker->states, newstate);
break;
default:
BLI_assert(0);
break;
}
return newstate;
}
/*
* BMW_reset
*
* Frees all states from the worklist, resetting the walker
* for reuse in a new walk.
*/
void BMW_reset(BMWalker *walker)
{
while (BMW_current_state(walker)) {
BMW_state_remove(walker);
}
walker->depth = 0;
BLI_ghash_free(walker->visithash, NULL, NULL);
walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 1");
}
@@ -0,0 +1,911 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Levi Schooley.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_walkers_impl.c
* \ingroup bmesh
*
* BMesh Walker Code.
*/
#include "BKE_customdata.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "bmesh_walkers_private.h"
/* 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_visitEdge(BMWalker *walker, BMEdge *e)
{
shellWalker *shellWalk = NULL;
if (BLI_ghash_haskey(walker->visithash, e)) {
return;
}
if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, e, walker->mask_edge)) {
return;
}
shellWalk = BMW_state_add(walker);
shellWalk->curedge = e;
BLI_ghash_insert(walker->visithash, e, NULL);
}
static void shellWalker_begin(BMWalker *walker, void *data)
{
BMIter eiter;
BMHeader *h = data;
BMEdge *e;
BMVert *v;
if (h == NULL)
{
return;
}
switch (h->htype) {
case BM_VERT:
{
/* starting the walk at a vert, add all the edges
* to the worklist */
v = (BMVert *)h;
BM_ITER(e, &eiter, walker->bm, BM_EDGES_OF_VERT, v) {
shellWalker_visitEdge(walker, e);
}
break;
}
case BM_EDGE:
{
/* starting the walk at an edge, add the single edge
* to the worklist */
e = (BMEdge *)h;
shellWalker_visitEdge(walker, e);
break;
}
}
}
static void *shellWalker_yield(BMWalker *walker)
{
shellWalker *shellWalk = BMW_current_state(walker);
return shellWalk->curedge;
}
static void *shellWalker_step(BMWalker *walker)
{
shellWalker *swalk = BMW_current_state(walker);
BMEdge *e, *e2;
BMVert *v;
BMIter iter;
int i;
e = swalk->curedge;
BMW_state_remove(walker);
for (i = 0; i < 2; i++) {
v = i ? e->v2 : e->v1;
BM_ITER(e2, &iter, walker->bm, BM_EDGES_OF_VERT, v) {
shellWalker_visitEdge(walker, e2);
}
}
return e;
}
#if 0
static void *shellWalker_step(BMWalker *walker)
{
BMEdge *curedge, *next = NULL;
BMVert *ov = NULL;
int restrictpass = 1;
shellWalker shellWalk = *((shellWalker *)BMW_current_state(walker));
if (!BLI_ghash_haskey(walker->visithash, shellWalk.base)) {
BLI_ghash_insert(walker->visithash, shellWalk.base, NULL);
}
BMW_state_remove(walker);
/* find the next edge whose other vertex has not been visite */
curedge = shellWalk.curedge;
do {
if (!BLI_ghash_haskey(walker->visithash, curedge)) {
if (!walker->restrictflag ||
(walker->restrictflag && BMO_elem_flag_test(walker->bm, curedge, walker->restrictflag)))
{
shellWalker *newstate;
ov = BM_edge_other_vert(curedge, shellWalk.base);
/* push a new state onto the stac */
newState = BMW_state_add(walker);
BLI_ghash_insert(walker->visithash, curedge, NULL);
/* populate the new stat */
newState->base = ov;
newState->curedge = curedge;
}
}
curedge = bmesh_disk_nextedge(curedge, shellWalk.base);
} while (curedge != shellWalk.curedge);
return shellWalk.curedge;
}
#endif
/* Connected Vertex Walker:
*
* Similar to shell walker, but visits vertices instead of edges.
*
*/
static void connectedVertexWalker_visitVertex(BMWalker *walker, BMVert *v)
{
connectedVertexWalker *vwalk;
if (BLI_ghash_haskey(walker->visithash, v)) {
/* already visited */
return;
}
if (walker->mask_vert && !BMO_elem_flag_test(walker->bm, v, walker->mask_vert)) {
/* not flagged for walk */
return;
}
vwalk = BMW_state_add(walker);
vwalk->curvert = v;
BLI_ghash_insert(walker->visithash, v, NULL);
}
static void connectedVertexWalker_begin(BMWalker *walker, void *data)
{
BMVert *v = data;
connectedVertexWalker_visitVertex(walker, v);
}
static void *connectedVertexWalker_yield(BMWalker *walker)
{
connectedVertexWalker *vwalk = BMW_current_state(walker);
return vwalk->curvert;
}
static void *connectedVertexWalker_step(BMWalker *walker)
{
connectedVertexWalker *vwalk = BMW_current_state(walker);
BMVert *v, *v2;
BMEdge *e;
BMIter iter;
v = vwalk->curvert;
BMW_state_remove(walker);
BM_ITER(e, &iter, walker->bm, BM_EDGES_OF_VERT, v) {
v2 = BM_edge_other_vert(e, v);
if (!BLI_ghash_haskey(walker->visithash, v2)) {
connectedVertexWalker_visitVertex(walker, v2);
}
}
return v;
}
/* Island Boundary Walker:
*
* Starts at a edge on the mesh and walks over the boundary of an
* island it belongs to.
*
* TODO:
*
* Add restriction flag/callback for wire edges.
*
*/
static void islandboundWalker_begin(BMWalker *walker, void *data)
{
BMLoop *l = data;
islandboundWalker *iwalk = NULL;
iwalk = BMW_state_add(walker);
iwalk->base = iwalk->curloop = l;
iwalk->lastv = l->v;
BLI_ghash_insert(walker->visithash, data, NULL);
}
static void *islandboundWalker_yield(BMWalker *walker)
{
islandboundWalker *iwalk = BMW_current_state(walker);
return iwalk->curloop;
}
static void *islandboundWalker_step(BMWalker *walker)
{
islandboundWalker *iwalk = BMW_current_state(walker), owalk;
BMVert *v;
BMEdge *e = iwalk->curloop->e;
BMFace *f;
BMLoop *l = iwalk->curloop;
/* int found = 0; */
owalk = *iwalk;
if (iwalk->lastv == e->v1) v = e->v2;
else v = e->v1;
if (!BM_vert_is_manifold(walker->bm, v)) {
BMW_reset(walker);
BMO_error_raise(walker->bm, NULL, BMERR_WALKER_FAILED,
"Non-manifold vert "
"while searching region boundary");
return NULL;
}
/* pop off current stat */
BMW_state_remove(walker);
f = l->f;
while (1) {
l = BM_face_other_loop(e, f, v);
if (bmesh_radial_nextloop(l) != l) {
l = bmesh_radial_nextloop(l);
f = l->f;
e = l->e;
if (walker->mask_face && !BMO_elem_flag_test(walker->bm, f, walker->mask_face)) {
l = l->radial_next;
break;
}
}
else {
f = l->f;
e = l->e;
break;
}
}
if (l == owalk.curloop) {
return NULL;
}
else if (BLI_ghash_haskey(walker->visithash, l)) {
return owalk.curloop;
}
BLI_ghash_insert(walker->visithash, l, NULL);
iwalk = BMW_state_add(walker);
iwalk->base = owalk.base;
//if (!BMO_elem_flag_test(walker->bm, l->f, walker->restrictflag))
// iwalk->curloop = l->radial_next;
iwalk->curloop = l; //else iwalk->curloop = l;
iwalk->lastv = v;
return owalk.curloop;
}
/* Island Walker:
*
* Starts at a tool flagged-face and walks over the face region
*
* TODO:
*
* Add restriction flag/callback for wire edges.
*
*/
static void islandWalker_begin(BMWalker *walker, void *data)
{
islandWalker *iwalk = NULL;
if (walker->mask_face && !BMO_elem_flag_test(walker->bm, (BMElemF *)data, walker->mask_face)) {
return;
}
iwalk = BMW_state_add(walker);
BLI_ghash_insert(walker->visithash, data, NULL);
iwalk->cur = data;
}
static void *islandWalker_yield(BMWalker *walker)
{
islandWalker *iwalk = BMW_current_state(walker);
return iwalk->cur;
}
static void *islandWalker_step(BMWalker *walker)
{
islandWalker *iwalk = BMW_current_state(walker);
/* islandWalker *owalk = iwalk; */ /* UNUSED */
BMIter iter, liter;
BMFace *f, *curf = iwalk->cur;
BMLoop *l;
BMW_state_remove(walker);
l = BM_iter_new(&liter, walker->bm, BM_LOOPS_OF_FACE, iwalk->cur);
for ( ; l; l = BM_iter_step(&liter)) {
/* could skip loop here too, but dont add unless we need it */
if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, l->e, walker->mask_edge)) {
continue;
}
f = BM_iter_new(&iter, walker->bm, BM_FACES_OF_EDGE, l->e);
for ( ; f; f = BM_iter_step(&iter)) {
if (walker->mask_face && !BMO_elem_flag_test(walker->bm, f, walker->mask_face)) {
continue;
}
if (BLI_ghash_haskey(walker->visithash, f)) continue;
iwalk = BMW_state_add(walker);
iwalk->cur = f;
BLI_ghash_insert(walker->visithash, f, NULL);
break;
}
}
return curf;
}
/* Edge Loop Walker:
*
* Starts at a tool-flagged edge and walks over the edge loop
*
*/
static void loopWalker_begin(BMWalker *walker, void *data)
{
loopWalker *lwalk = NULL, owalk;
BMEdge *e = data;
BMVert *v;
/* int found = 1, val; */ /* UNUSED */
v = e->v1;
/* val = BM_vert_edge_count(v); */ /* UNUSED */
lwalk = BMW_state_add(walker);
BLI_ghash_insert(walker->visithash, e, NULL);
lwalk->cur = lwalk->start = e;
lwalk->lastv = lwalk->startv = v;
lwalk->stage2 = 0;
lwalk->startrad = BM_edge_face_count(e);
/* rewin */
while (BMW_current_state(walker)) {
owalk = *((loopWalker *)BMW_current_state(walker));
BMW_walk(walker);
}
lwalk = BMW_state_add(walker);
*lwalk = owalk;
if (lwalk->lastv == owalk.cur->v1) lwalk->lastv = owalk.cur->v2;
else lwalk->lastv = owalk.cur->v1;
lwalk->startv = lwalk->lastv;
BLI_ghash_free(walker->visithash, NULL, NULL);
walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 2");
BLI_ghash_insert(walker->visithash, owalk.cur, NULL);
}
static void *loopWalker_yield(BMWalker *walker)
{
loopWalker *lwalk = BMW_current_state(walker);
return lwalk->cur;
}
static void *loopWalker_step(BMWalker *walker)
{
loopWalker *lwalk = BMW_current_state(walker), owalk;
BMIter eiter;
BMEdge *e = lwalk->cur, *nexte = NULL;
BMLoop *l, *l2;
BMVert *v;
int val, rlen /* , found = 0 */, i = 0, stopi;
owalk = *lwalk;
BMW_state_remove(walker);
l = e->l;
/* handle wire edge case */
if (!l) {
/* match trunk: mark all connected wire edges */
for (i = 0; i < 2; i++) {
v = i ? e->v2 : e->v1;
BM_ITER(nexte, &eiter, walker->bm, BM_EDGES_OF_VERT, v) {
if ((nexte->l == NULL) && !BLI_ghash_haskey(walker->visithash, nexte)) {
lwalk = BMW_state_add(walker);
lwalk->cur = nexte;
lwalk->lastv = v;
lwalk->startrad = owalk.startrad;
BLI_ghash_insert(walker->visithash, nexte, NULL);
}
}
}
return owalk.cur;
}
v = (e->v1 == lwalk->lastv) ? e->v2 : e->v1;
val = BM_vert_edge_count(v);
rlen = owalk.startrad;
if (val == 4 || val == 2 || rlen == 1) {
i = 0;
stopi = val / 2;
while (1) {
if (rlen != 1 && i == stopi) break;
l = BM_face_other_loop(l->e, l->f, v);
if (!l)
break;
l2 = bmesh_radial_nextloop(l);
if (l2 == l) {
break;
}
l = l2;
i += 1;
}
}
if (!l) {
return owalk.cur;
}
if (l != e->l && !BLI_ghash_haskey(walker->visithash, l->e)) {
if (!(rlen != 1 && i != stopi)) {
lwalk = BMW_state_add(walker);
lwalk->cur = l->e;
lwalk->lastv = v;
lwalk->startrad = owalk.startrad;
BLI_ghash_insert(walker->visithash, l->e, NULL);
}
}
return owalk.cur;
}
/* Face Loop Walker:
*
* Starts at a tool-flagged face and walks over the face loop
* Conditions for starting and stepping the face loop have been
* tuned in an attempt to match the face loops built by EditMesh
*
*/
/* Check whether the face loop should includes the face specified
* by the given BMLoop */
static int faceloopWalker_include_face(BMWalker *walker, BMLoop *l)
{
/* face must have degree 4 */
if (l->f->len != 4) {
return FALSE;
}
/* the face must not have been already visite */
if (BLI_ghash_haskey(walker->visithash, l->f)) {
return FALSE;
}
return TRUE;
}
/* Check whether the face loop can start from the given edge */
static int faceloopWalker_edge_begins_loop(BMWalker *walker, BMEdge *e)
{
BMesh *bm = walker->bm;
/* There is no face loop starting from a wire edge */
if (BM_edge_is_wire(bm, e)) {
return FALSE;
}
/* Don't start a loop from a boundary edge if it cannot
* be extended to cover any faces */
if (BM_edge_face_count(e) == 1) {
if (!faceloopWalker_include_face(walker, e->l)) {
return FALSE;
}
}
/* Don't start a face loop from non-manifold edges */
if (!BM_edge_is_manifold(bm, e)) {
return FALSE;
}
return TRUE;
}
static void faceloopWalker_begin(BMWalker *walker, void *data)
{
faceloopWalker *lwalk, owalk;
BMEdge *e = data;
/* BMesh *bm = walker->bm; */ /* UNUSED */
/* int fcount = BM_edge_face_count(e); */ /* UNUSED */
if (!faceloopWalker_edge_begins_loop(walker, e))
return;
lwalk = BMW_state_add(walker);
lwalk->l = e->l;
lwalk->nocalc = 0;
BLI_ghash_insert(walker->visithash, lwalk->l->f, NULL);
/* rewin */
while (BMW_current_state(walker)) {
owalk = *((faceloopWalker *)BMW_current_state(walker));
BMW_walk(walker);
}
lwalk = BMW_state_add(walker);
*lwalk = owalk;
lwalk->nocalc = 0;
BLI_ghash_free(walker->visithash, NULL, NULL);
walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 3");
BLI_ghash_insert(walker->visithash, lwalk->l->f, NULL);
}
static void *faceloopWalker_yield(BMWalker *walker)
{
faceloopWalker *lwalk = BMW_current_state(walker);
if (!lwalk) {
return NULL;
}
return lwalk->l->f;
}
static void *faceloopWalker_step(BMWalker *walker)
{
faceloopWalker *lwalk = BMW_current_state(walker);
BMFace *f = lwalk->l->f;
BMLoop *l = lwalk->l, *origl = lwalk->l;
BMW_state_remove(walker);
l = l->radial_next;
if (lwalk->nocalc)
return f;
if (!faceloopWalker_include_face(walker, l)) {
l = lwalk->l;
l = l->next->next;
if (BM_edge_face_count(l->e) != 2) {
l = l->prev->prev;
}
l = l->radial_next;
}
if (faceloopWalker_include_face(walker, l)) {
lwalk = BMW_state_add(walker);
lwalk->l = l;
if (l->f->len != 4) {
lwalk->nocalc = 1;
lwalk->l = origl;
}
else {
lwalk->nocalc = 0;
}
BLI_ghash_insert(walker->visithash, l->f, NULL);
}
return f;
}
/* Edge Ring Walker:
*
* Starts at a tool-flagged edge and walks over the edge ring
* Conditions for starting and stepping the edge ring have been
* tuned in an attempt to match the edge rings built by EditMesh
*
*/
static void edgeringWalker_begin(BMWalker *walker, void *data)
{
edgeringWalker *lwalk, owalk;
BMEdge *e = data;
lwalk = BMW_state_add(walker);
lwalk->l = e->l;
if (!lwalk->l) {
lwalk->wireedge = e;
return;
}
else {
lwalk->wireedge = NULL;
}
BLI_ghash_insert(walker->visithash, lwalk->l->e, NULL);
/* rewin */
while (BMW_current_state(walker)) {
owalk = *((edgeringWalker *)BMW_current_state(walker));
BMW_walk(walker);
}
lwalk = BMW_state_add(walker);
*lwalk = owalk;
if (lwalk->l->f->len != 4)
lwalk->l = lwalk->l->radial_next;
BLI_ghash_free(walker->visithash, NULL, NULL);
walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 4");
BLI_ghash_insert(walker->visithash, lwalk->l->e, NULL);
}
static void *edgeringWalker_yield(BMWalker *walker)
{
edgeringWalker *lwalk = BMW_current_state(walker);
if (!lwalk) {
return NULL;
}
if (lwalk->l)
return lwalk->l->e;
else
return lwalk->wireedge;
}
static void *edgeringWalker_step(BMWalker *walker)
{
edgeringWalker *lwalk = BMW_current_state(walker);
BMEdge *e;
BMLoop *l = lwalk->l /* , *origl = lwalk->l */;
BMesh *bm = walker->bm;
BMW_state_remove(walker);
if (!l)
return lwalk->wireedge;
e = l->e;
if (!BM_edge_is_manifold(bm, e)) {
/* walker won't traverse to a non-manifold edge, but may
* be started on one, and should not traverse *away* from
* a non-manfold edge (non-manifold edges are never in an
* edge ring with manifold edges */
return e;
}
l = l->radial_next;
l = l->next->next;
if ((l->f->len != 4) || !BM_edge_is_manifold(bm, l->e)) {
l = lwalk->l->next->next;
}
/* only walk to manifold edge */
if ((l->f->len == 4) && BM_edge_is_manifold(bm, l->e) &&
!BLI_ghash_haskey(walker->visithash, l->e)) {
lwalk = BMW_state_add(walker);
lwalk->l = l;
lwalk->wireedge = NULL;
BLI_ghash_insert(walker->visithash, l->e, NULL);
}
return e;
}
static void uvedgeWalker_begin(BMWalker *walker, void *data)
{
uvedgeWalker *lwalk;
BMLoop *l = data;
if (BLI_ghash_haskey(walker->visithash, l))
return;
lwalk = BMW_state_add(walker);
lwalk->l = l;
BLI_ghash_insert(walker->visithash, l, NULL);
}
static void *uvedgeWalker_yield(BMWalker *walker)
{
uvedgeWalker *lwalk = BMW_current_state(walker);
if (!lwalk) {
return NULL;
}
return lwalk->l;
}
static void *uvedgeWalker_step(BMWalker *walker)
{
uvedgeWalker *lwalk = BMW_current_state(walker);
BMLoop *l, *l2, *l3, *nl, *cl;
BMIter liter;
void *d1, *d2;
int i, j, rlen, type;
l = lwalk->l;
nl = l->next;
type = walker->bm->ldata.layers[walker->layer].type;
BMW_state_remove(walker);
if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, l->e, walker->mask_edge))
return l;
/* go over loops around l->v and nl->v and see which ones share l and nl's
* mloopuv's coordinates. in addition, push on l->next if necassary */
for (i = 0; i < 2; i++) {
cl = i ? nl : l;
BM_ITER(l2, &liter, walker->bm, BM_LOOPS_OF_VERT, cl->v) {
d1 = CustomData_bmesh_get_layer_n(&walker->bm->ldata,
cl->head.data, walker->layer);
rlen = BM_edge_face_count(l2->e);
for (j = 0; j < rlen; j++) {
if (BLI_ghash_haskey(walker->visithash, l2))
continue;
if (walker->mask_edge && !(BMO_elem_flag_test(walker->bm, l2->e, walker->mask_edge))) {
if (l2->v != cl->v)
continue;
}
l3 = l2->v != cl->v ? l2->next : l2;
d2 = CustomData_bmesh_get_layer_n(&walker->bm->ldata,
l3->head.data, walker->layer);
if (!CustomData_data_equals(type, d1, d2))
continue;
lwalk = BMW_state_add(walker);
BLI_ghash_insert(walker->visithash, l2, NULL);
lwalk->l = l2;
l2 = l2->radial_next;
}
}
}
return l;
}
static BMWalker shell_walker_type = {
shellWalker_begin,
shellWalker_step,
shellWalker_yield,
sizeof(shellWalker),
BMW_BREADTH_FIRST,
BM_EDGE, /* valid restrict masks */
};
static BMWalker islandbound_walker_type = {
islandboundWalker_begin,
islandboundWalker_step,
islandboundWalker_yield,
sizeof(islandboundWalker),
BMW_DEPTH_FIRST,
BM_FACE, /* valid restrict masks */
};
static BMWalker island_walker_type = {
islandWalker_begin,
islandWalker_step,
islandWalker_yield,
sizeof(islandWalker),
BMW_BREADTH_FIRST,
BM_EDGE | BM_FACE, /* valid restrict masks */
};
static BMWalker loop_walker_type = {
loopWalker_begin,
loopWalker_step,
loopWalker_yield,
sizeof(loopWalker),
BMW_DEPTH_FIRST,
0, /* valid restrict masks */ /* could add flags here but so far none are used */
};
static BMWalker faceloop_walker_type = {
faceloopWalker_begin,
faceloopWalker_step,
faceloopWalker_yield,
sizeof(faceloopWalker),
BMW_DEPTH_FIRST,
0, /* valid restrict masks */ /* could add flags here but so far none are used */
};
static BMWalker edgering_walker_type = {
edgeringWalker_begin,
edgeringWalker_step,
edgeringWalker_yield,
sizeof(edgeringWalker),
BMW_DEPTH_FIRST,
0, /* valid restrict masks */ /* could add flags here but so far none are used */
};
static BMWalker loopdata_region_walker_type = {
uvedgeWalker_begin,
uvedgeWalker_step,
uvedgeWalker_yield,
sizeof(uvedgeWalker),
BMW_DEPTH_FIRST,
BM_EDGE, /* valid restrict masks */
};
static BMWalker connected_vertex_walker_type = {
connectedVertexWalker_begin,
connectedVertexWalker_step,
connectedVertexWalker_yield,
sizeof(connectedVertexWalker),
BMW_BREADTH_FIRST,
BM_VERT, /* valid restrict masks */
};
BMWalker *bm_walker_types[] = {
&shell_walker_type, /* BMW_SHELL */
&loop_walker_type, /* BMW_LOOP */
&faceloop_walker_type, /* BMW_FACELOOP */
&edgering_walker_type, /* BMW_EDGERING */
&loopdata_region_walker_type, /* BMW_LOOPDATA_ISLAND */
&islandbound_walker_type, /* BMW_ISLANDBOUND */
&island_walker_type, /* BMW_ISLAND */
&connected_vertex_walker_type, /* BMW_CONNECTED_VERTEX */
};
int bm_totwalkers = sizeof(bm_walker_types) / sizeof(*bm_walker_types);
@@ -0,0 +1,89 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar, Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMESH_WALKERS_PRIVATE_H__
#define __BMESH_WALKERS_PRIVATE_H__
/** \file blender/bmesh/intern/bmesh_walkers_private.h
* \ingroup bmesh
*
* BMesh walker API.
*/
extern BMWalker *bm_walker_types[];
extern int bm_totwalkers;
/* Pointer hiding*/
typedef struct bmesh_walkerGeneric{
Link link;
int depth;
} bmesh_walkerGeneric;
typedef struct shellWalker{
bmesh_walkerGeneric header;
BMEdge *curedge;
} shellWalker;
typedef struct islandboundWalker {
bmesh_walkerGeneric header;
BMLoop *base;
BMVert *lastv;
BMLoop *curloop;
} islandboundWalker;
typedef struct islandWalker {
bmesh_walkerGeneric header;
BMFace *cur;
} islandWalker;
typedef struct loopWalker {
bmesh_walkerGeneric header;
BMEdge *cur, *start;
BMVert *lastv, *startv;
int startrad, stage2;
} loopWalker;
typedef struct faceloopWalker {
bmesh_walkerGeneric header;
BMLoop *l;
int nocalc;
} faceloopWalker;
typedef struct edgeringWalker {
bmesh_walkerGeneric header;
BMLoop *l;
BMEdge *wireedge;
} edgeringWalker;
typedef struct uvedgeWalker {
bmesh_walkerGeneric header;
BMLoop *l;
} uvedgeWalker;
typedef struct connectedVertexWalker {
bmesh_walkerGeneric header;
BMVert *curvert;
} connectedVertexWalker;
#endif /* __BMESH_WALKERS_PRIVATE_H__ */
@@ -0,0 +1,479 @@
#if 0
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 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->getNumTessFaces(dm);
mvert = dm->getVertArray(dm);
medge = dm->getEdgeArray(dm);
mface = dm->getTessFaceArray(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)
{
copy_v3_v3(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 TRUE;
}
return FALSE;
}
/*
* 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;
}
#endif
+881
View File
@@ -0,0 +1,881 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.h"
#include "BLI_math.h"
#include "BLI_smallhash.h"
#include "BKE_customdata.h"
#include "bmesh.h"
#define BEVEL_FLAG 1
#define BEVEL_DEL 2
#define FACE_NEW 4
#define EDGE_OLD 8
#define FACE_OLD 16
#define VERT_OLD 32
#define FACE_SPAN 64
#define FACE_HOLE 128
typedef struct LoopTag {
BMVert *newv;
} LoopTag;
typedef struct EdgeTag {
BMVert *newv1, *newv2;
} EdgeTag;
static void calc_corner_co(BMesh *bm, BMLoop *l, const float fac, float r_co[3],
const short do_dist, const short do_even)
{
float no[3], l_vec_prev[3], l_vec_next[3], l_co_prev[3], l_co[3], l_co_next[3], co_ofs[3];
int is_concave;
/* first get the prev/next verts */
if (l->f->len > 2) {
copy_v3_v3(l_co_prev, l->prev->v->co);
copy_v3_v3(l_co, l->v->co);
copy_v3_v3(l_co_next, l->next->v->co);
/* calculate norma */
sub_v3_v3v3(l_vec_prev, l_co_prev, l_co);
sub_v3_v3v3(l_vec_next, l_co_next, l_co);
cross_v3_v3v3(no, l_vec_prev, l_vec_next);
is_concave = dot_v3v3(no, l->f->no) > 0.0f;
}
else {
BMIter iter;
BMLoop *l2;
float up[3] = {0.0f, 0.0f, 1.0f};
copy_v3_v3(l_co_prev, l->prev->v->co);
copy_v3_v3(l_co, l->v->co);
BM_ITER(l2, &iter, bm, BM_LOOPS_OF_VERT, l->v) {
if (l2->f != l->f) {
copy_v3_v3(l_co_next, BM_edge_other_vert(l2->e, l2->next->v)->co);
break;
}
}
sub_v3_v3v3(l_vec_prev, l_co_prev, l_co);
sub_v3_v3v3(l_vec_next, l_co_next, l_co);
cross_v3_v3v3(no, l_vec_prev, l_vec_next);
if (dot_v3v3(no, no) == 0.0f) {
no[0] = no[1] = 0.0f; no[2] = -1.0f;
}
is_concave = dot_v3v3(no, up) < 0.0f;
}
/* now calculate the new location */
if (do_dist) { /* treat 'fac' as distance */
normalize_v3(l_vec_prev);
normalize_v3(l_vec_next);
add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next);
if (UNLIKELY(normalize_v3(co_ofs) == 0.0f)) { /* edges form a straignt line */
cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no);
}
if (do_even) {
negate_v3(l_vec_next);
mul_v3_fl(co_ofs, fac * shell_angle_to_dist(0.5f * angle_normalized_v3v3(l_vec_prev, l_vec_next)));
/* negate_v3(l_vec_next); */ /* no need unless we use again */
}
else {
mul_v3_fl(co_ofs, fac);
}
}
else { /* treat as 'fac' as a factor (0 - 1) */
/* not strictly necessary, balance vectors
* so the longer edge doesn't skew the result,
* gives nicer, move even output.
*
* Use the minimum rather then the middle value so skinny faces don't flip along the short axis */
float min_fac = minf(normalize_v3(l_vec_prev), normalize_v3(l_vec_next));
float angle;
if (do_even) {
negate_v3(l_vec_next);
angle = angle_normalized_v3v3(l_vec_prev, l_vec_next);
negate_v3(l_vec_next); /* no need unless we use again */
}
else {
angle = 0.0f;
}
mul_v3_fl(l_vec_prev, min_fac);
mul_v3_fl(l_vec_next, min_fac);
add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next);
if (UNLIKELY(is_zero_v3(co_ofs))) {
cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no);
normalize_v3(co_ofs);
mul_v3_fl(co_ofs, min_fac);
}
/* done */
if (do_even) {
mul_v3_fl(co_ofs, (fac * 0.5) * shell_angle_to_dist(0.5f * angle));
}
else {
mul_v3_fl(co_ofs, fac * 0.5);
}
}
/* apply delta vec */
if (is_concave)
negate_v3(co_ofs);
add_v3_v3v3(r_co, co_ofs, l->v->co);
}
#define ETAG_SET(e, v, nv) ( \
(v) == (e)->v1 ? \
(etags[BM_elem_index_get((e))].newv1 = (nv)) : \
(etags[BM_elem_index_get((e))].newv2 = (nv)) \
)
#define ETAG_GET(e, v) ( \
(v) == (e)->v1 ? \
(etags[BM_elem_index_get((e))].newv1) : \
(etags[BM_elem_index_get((e))].newv2) \
)
void bmesh_bevel_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMIter iter;
BMEdge *e;
BMVert *v;
BMFace **faces = NULL, *f;
LoopTag *tags = NULL, *tag;
EdgeTag *etags = NULL;
BMVert **verts = NULL;
BMEdge **edges = NULL;
BLI_array_declare(faces);
BLI_array_declare(tags);
BLI_array_declare(etags);
BLI_array_declare(verts);
BLI_array_declare(edges);
SmallHash hash;
float fac = BMO_slot_float_get(op, "percent");
const short do_even = BMO_slot_int_get(op, "use_even");
const short do_dist = BMO_slot_int_get(op, "use_dist");
int i, li, has_elens, HasMDisps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
has_elens = CustomData_has_layer(&bm->edata, CD_PROP_FLT) && BMO_slot_int_get(op, "use_lengths");
if (has_elens) {
li = BMO_slot_int_get(op, "lengthlayer");
}
BLI_smallhash_init(&hash);
BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) {
BMO_elem_flag_enable(bm, e, BEVEL_FLAG|BEVEL_DEL);
BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL);
BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL);
if (BM_edge_face_count(e) < 2) {
BMO_elem_flag_disable(bm, e, BEVEL_DEL);
BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL);
BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL);
}
#if 0
if (BM_edge_face_count(e) == 0) {
BMVert *verts[2] = {e->v1, e->v2};
BMEdge *edges[2] = {e, BM_edge_create(bm, e->v1, e->v2, e, 0)};
BMO_elem_flag_enable(bm, edges[1], BEVEL_FLAG);
BM_face_create(bm, verts, edges, 2, FALSE);
}
#endif
}
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BMO_elem_flag_enable(bm, v, VERT_OLD);
}
#if 0
//a bit of cleaner code that, alas, doens't work.
/* build edge tag */
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, e->v1, BEVEL_FLAG) || BMO_elem_flag_test(bm, e->v2, BEVEL_FLAG)) {
BMIter liter;
BMLoop *l;
if (!BMO_elem_flag_test(bm, e, EDGE_OLD)) {
BM_elem_index_set(e, BLI_array_count(etags)); /* set_dirty! */
BLI_array_growone(etags);
BMO_elem_flag_enable(bm, e, EDGE_OLD);
}
BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
BMLoop *l2;
BMIter liter2;
if (BMO_elem_flag_test(bm, l->f, BEVEL_FLAG))
continue;
BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) {
BM_elem_index_set(l2, BLI_array_count(tags)); /* set_loop */
BLI_array_growone(tags);
if (!BMO_elem_flag_test(bm, l2->e, EDGE_OLD)) {
BM_elem_index_set(l2->e, BLI_array_count(etags)); /* set_dirty! */
BLI_array_growone(etags);
BMO_elem_flag_enable(bm, l2->e, EDGE_OLD);
}
}
BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG);
BLI_array_append(faces, l->f);
}
}
else {
BM_elem_index_set(e, -1); /* set_dirty! */
}
}
#endif
/* create and assign looptag structure */
BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) {
BMLoop *l;
BMIter liter;
BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL);
BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL);
if (BM_edge_face_count(e) < 2) {
BMO_elem_flag_disable(bm, e, BEVEL_DEL);
BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL);
BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL);
}
if (!BLI_smallhash_haskey(&hash, (intptr_t)e)) {
BLI_array_growone(etags);
BM_elem_index_set(e, BLI_array_count(etags) - 1); /* set_dirty! */
BLI_smallhash_insert(&hash, (intptr_t)e, NULL);
BMO_elem_flag_enable(bm, e, EDGE_OLD);
}
/* find all faces surrounding e->v1 and, e->v2 */
for (i = 0; i < 2; i++) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, i ? e->v2:e->v1) {
BMLoop *l2;
BMIter liter2;
/* see if we've already processed this loop's fac */
if (BLI_smallhash_haskey(&hash, (intptr_t)l->f))
continue;
/* create tags for all loops in l-> */
BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) {
BLI_array_growone(tags);
BM_elem_index_set(l2, BLI_array_count(tags) - 1); /* set_loop */
if (!BLI_smallhash_haskey(&hash, (intptr_t)l2->e)) {
BLI_array_growone(etags);
BM_elem_index_set(l2->e, BLI_array_count(etags) - 1); /* set_dirty! */
BLI_smallhash_insert(&hash, (intptr_t)l2->e, NULL);
BMO_elem_flag_enable(bm, l2->e, EDGE_OLD);
}
}
BLI_smallhash_insert(&hash, (intptr_t)l->f, NULL);
BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG);
BLI_array_append(faces, l->f);
}
}
}
bm->elem_index_dirty |= BM_EDGE;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BMIter eiter;
if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG))
continue;
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && !ETAG_GET(e, v)) {
BMVert *v2;
float co[3];
v2 = BM_edge_other_vert(e, v);
sub_v3_v3v3(co, v2->co, v->co);
if (has_elens) {
float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, e->head.data, CD_PROP_FLT, li);
normalize_v3(co);
mul_v3_fl(co, elen);
}
mul_v3_fl(co, fac);
add_v3_v3(co, v->co);
v2 = BM_vert_create(bm, co, v);
ETAG_SET(e, v, v2);
}
}
}
for (i = 0; i < BLI_array_count(faces); i++) {
BMLoop *l;
BMIter liter;
BMO_elem_flag_enable(bm, faces[i], FACE_OLD);
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
float co[3];
if (BMO_elem_flag_test(bm, l->e, BEVEL_FLAG)) {
if (BMO_elem_flag_test(bm, l->prev->e, BEVEL_FLAG))
{
tag = tags + BM_elem_index_get(l);
calc_corner_co(bm, l, fac, co, do_dist, do_even);
tag->newv = BM_vert_create(bm, co, l->v);
}
else {
tag = tags + BM_elem_index_get(l);
tag->newv = ETAG_GET(l->prev->e, l->v);
if (!tag->newv) {
sub_v3_v3v3(co, l->prev->v->co, l->v->co);
if (has_elens) {
float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data,
CD_PROP_FLT, li);
normalize_v3(co);
mul_v3_fl(co, elen);
}
mul_v3_fl(co, fac);
add_v3_v3(co, l->v->co);
tag->newv = BM_vert_create(bm, co, l->v);
ETAG_SET(l->prev->e, l->v, tag->newv);
}
}
}
else if (BMO_elem_flag_test(bm, l->v, BEVEL_FLAG)) {
tag = tags + BM_elem_index_get(l);
tag->newv = ETAG_GET(l->e, l->v);
if (!tag->newv) {
sub_v3_v3v3(co, l->next->v->co, l->v->co);
if (has_elens) {
float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->e->head.data, CD_PROP_FLT, li);
normalize_v3(co);
mul_v3_fl(co, elen);
}
mul_v3_fl(co, fac);
add_v3_v3(co, l->v->co);
tag = tags + BM_elem_index_get(l);
tag->newv = BM_vert_create(bm, co, l->v);
ETAG_SET(l->e, l->v, tag->newv);
}
}
else {
tag = tags + BM_elem_index_get(l);
tag->newv = l->v;
BMO_elem_flag_disable(bm, l->v, BEVEL_DEL);
}
}
}
/* create new faces inset from original face */
for (i = 0; i < BLI_array_count(faces); i++) {
BMLoop *l;
BMIter liter;
BMFace *f;
BMVert *lastv = NULL, *firstv = NULL;
BMO_elem_flag_enable(bm, faces[i], BEVEL_DEL);
BLI_array_empty(verts);
BLI_array_empty(edges);
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
BMVert *v2;
tag = tags + BM_elem_index_get(l);
BLI_array_append(verts, tag->newv);
if (!firstv)
firstv = tag->newv;
if (lastv) {
e = BM_edge_create(bm, lastv, tag->newv, l->e, TRUE);
BM_elem_attrs_copy(bm, bm, l->prev->e, e);
BLI_array_append(edges, e);
}
lastv = tag->newv;
v2 = ETAG_GET(l->e, l->next->v);
tag = &tags[BM_elem_index_get(l->next)];
if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG) && v2 && v2 != tag->newv) {
BLI_array_append(verts, v2);
e = BM_edge_create(bm, lastv, v2, l->e, TRUE);
BM_elem_attrs_copy(bm, bm, l->e, e);
BLI_array_append(edges, e);
lastv = v2;
}
}
e = BM_edge_create(bm, firstv, lastv, BM_FACE_FIRST_LOOP(faces[i])->e, TRUE);
if (BM_FACE_FIRST_LOOP(faces[i])->prev->e != e) {
BM_elem_attrs_copy(bm, bm, BM_FACE_FIRST_LOOP(faces[i])->prev->e, e);
}
BLI_array_append(edges, e);
f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), FALSE);
if (!f) {
printf("%s: could not make face!\n", __func__);
continue;
}
BMO_elem_flag_enable(bm, f, FACE_NEW);
}
for (i = 0; i < BLI_array_count(faces); i++) {
BMLoop *l;
BMIter liter;
int j;
/* create quad spans between split edge */
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
BMVert *v1 = NULL, *v2 = NULL, *v3 = NULL, *v4 = NULL;
if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG))
continue;
v1 = tags[BM_elem_index_get(l)].newv;
v2 = tags[BM_elem_index_get(l->next)].newv;
if (l->radial_next != l) {
v3 = tags[BM_elem_index_get(l->radial_next)].newv;
if (l->radial_next->next->v == l->next->v) {
v4 = v3;
v3 = tags[BM_elem_index_get(l->radial_next->next)].newv;
}
else {
v4 = tags[BM_elem_index_get(l->radial_next->next)].newv;
}
}
else {
/* the loop is on a boundar */
v3 = l->next->v;
v4 = l->v;
for (j = 0; j < 2; j++) {
BMIter eiter;
BMVert *v = j ? v4 : v3;
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
if (!BM_vert_in_edge(e, v3) || !BM_vert_in_edge(e, v4))
continue;
if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && BMO_elem_flag_test(bm, e, EDGE_OLD)) {
BMVert *vv;
vv = ETAG_GET(e, v);
if (!vv || BMO_elem_flag_test(bm, vv, BEVEL_FLAG))
continue;
if (j)
v1 = vv;
else
v2 = vv;
break;
}
}
}
BMO_elem_flag_disable(bm, v3, BEVEL_DEL);
BMO_elem_flag_disable(bm, v4, BEVEL_DEL);
}
if (v1 != v2 && v2 != v3 && v3 != v4) {
BMIter liter2;
BMLoop *l2, *l3;
BMEdge *e1, *e2;
float d1, d2, *d3;
f = BM_face_create_quad_tri(bm, v4, v3, v2, v1, l->f, TRUE);
e1 = BM_edge_exists(v4, v3);
e2 = BM_edge_exists(v2, v1);
BM_elem_attrs_copy(bm, bm, l->e, e1);
BM_elem_attrs_copy(bm, bm, l->e, e2);
/* set edge lengths of cross edges as the average of the cross edges they're based o */
if (has_elens) {
/* angle happens not to be used. why? - not sure it just isnt - campbell.
* leave this in incase we need to use it later */
#if 0
float ang;
#endif
e1 = BM_edge_exists(v1, v4);
e2 = BM_edge_exists(v2, v3);
if (l->radial_next->v == l->v) {
l2 = l->radial_next->prev;
l3 = l->radial_next->next;
}
else {
l2 = l->radial_next->next;
l3 = l->radial_next->prev;
}
d3 = CustomData_bmesh_get_n(&bm->edata, e1->head.data, CD_PROP_FLT, li);
d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, CD_PROP_FLT, li);
d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l2->e->head.data, CD_PROP_FLT, li);
#if 0
ang = angle_v3v3v3(l->prev->v->co, l->v->co, BM_edge_other_vert(l2->e, l->v)->co);
#endif
*d3 = (d1 + d2) * 0.5f;
d3 = CustomData_bmesh_get_n(&bm->edata, e2->head.data, CD_PROP_FLT, li);
d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->next->e->head.data, CD_PROP_FLT, li);
d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l3->e->head.data, CD_PROP_FLT, li);
#if 0
ang = angle_v3v3v3(BM_edge_other_vert(l->next->e, l->next->v)->co, l->next->v->co,
BM_edge_other_vert(l3->e, l->next->v)->co);
#endif
*d3 = (d1 + d2) * 0.5f;
}
if (!f) {
fprintf(stderr, "%s: face index out of range! (bmesh internal error)\n", __func__);
continue;
}
BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_SPAN);
/* un-tag edges in f for deletio */
BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, f) {
BMO_elem_flag_disable(bm, l2->e, BEVEL_DEL);
}
}
else {
f = NULL;
}
}
}
/* fill in holes at vertices */
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BMIter eiter;
BMVert *vv, *vstart = NULL, *lastv = NULL;
SmallHash tmphash;
int rad, insorig = 0, err = 0;
BLI_smallhash_init(&tmphash);
if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG))
continue;
BLI_array_empty(verts);
BLI_array_empty(edges);
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
BMIter liter;
BMVert *v1 = NULL, *v2 = NULL;
BMLoop *l;
if (BM_edge_face_count(e) < 2)
insorig = 1;
if (BM_elem_index_get(e) == -1)
continue;
rad = 0;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
if (!BMO_elem_flag_test(bm, l->f, FACE_OLD))
continue;
rad++;
tag = tags + BM_elem_index_get((l->v == v) ? l : l->next);
if (!v1)
v1 = tag->newv;
else if (!v2)
v2 = tag->newv;
}
if (rad < 2)
insorig = 1;
if (!v1)
v1 = ETAG_GET(e, v);
if (!v2 || v1 == v2)
v2 = ETAG_GET(e, v);
if (v1) {
if (!BLI_smallhash_haskey(&tmphash, (intptr_t)v1)) {
BLI_array_append(verts, v1);
BLI_smallhash_insert(&tmphash, (intptr_t)v1, NULL);
}
if (v2 && v1 != v2 && !BLI_smallhash_haskey(&tmphash, (intptr_t)v2)) {
BLI_array_append(verts, v2);
BLI_smallhash_insert(&tmphash, (intptr_t)v2, NULL);
}
}
}
if (!BLI_array_count(verts))
continue;
if (insorig) {
BLI_array_append(verts, v);
BLI_smallhash_insert(&tmphash, (intptr_t)v, NULL);
}
/* find edges that exist between vertices in verts. this is basically
* a topological walk of the edges connecting them */
vstart = vstart ? vstart : verts[0];
vv = vstart;
do {
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) {
BMVert *vv2 = BM_edge_other_vert(e, vv);
if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) {
/* if we've go over the same vert twice, break out of outer loop */
if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) {
e = NULL;
err = 1;
break;
}
/* use self pointer as ta */
BLI_smallhash_remove(&tmphash, (intptr_t)vv2);
BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2);
lastv = vv;
BLI_array_append(edges, e);
vv = vv2;
break;
}
}
if (e == NULL) {
break;
}
} while (vv != vstart);
if (err) {
continue;
}
/* there may not be a complete loop of edges, so start again and make
* final edge afterwards. in this case, the previous loop worked to
* find one of the two edges at the extremes. */
if (vv != vstart) {
/* undo previous taggin */
for (i = 0; i < BLI_array_count(verts); i++) {
BLI_smallhash_remove(&tmphash, (intptr_t)verts[i]);
BLI_smallhash_insert(&tmphash, (intptr_t)verts[i], NULL);
}
vstart = vv;
lastv = NULL;
BLI_array_empty(edges);
do {
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) {
BMVert *vv2 = BM_edge_other_vert(e, vv);
if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) {
/* if we've go over the same vert twice, break out of outer loo */
if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) {
e = NULL;
err = 1;
break;
}
/* use self pointer as ta */
BLI_smallhash_remove(&tmphash, (intptr_t)vv2);
BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2);
lastv = vv;
BLI_array_append(edges, e);
vv = vv2;
break;
}
}
if (e == NULL)
break;
} while (vv != vstart);
if (!err) {
e = BM_edge_create(bm, vv, vstart, NULL, TRUE);
BLI_array_append(edges, e);
}
}
if (err)
continue;
if (BLI_array_count(edges) >= 3) {
BMFace *f;
if (BM_face_exists(bm, verts, BLI_array_count(verts), &f))
continue;
f = BM_face_create_ngon(bm, lastv, vstart, edges, BLI_array_count(edges), FALSE);
if (!f) {
fprintf(stderr, "%s: in bevel vert fill! (bmesh internal error)\n", __func__);
}
else {
BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_HOLE);
}
}
BLI_smallhash_release(&tmphash);
}
/* copy over customdat */
for (i = 0; i < BLI_array_count(faces); i++) {
BMLoop *l;
BMIter liter;
BMFace *f = faces[i];
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BMLoop *l2;
BMIter liter2;
tag = tags + BM_elem_index_get(l);
if (!tag->newv)
continue;
BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_VERT, tag->newv) {
if (!BMO_elem_flag_test(bm, l2->f, FACE_NEW) || (l2->v != tag->newv && l2->v != l->v))
continue;
if (tag->newv != l->v || HasMDisps) {
BM_elem_attrs_copy(bm, bm, l->f, l2->f);
BM_loop_interp_from_face(bm, l2, l->f, TRUE, TRUE);
}
else {
BM_elem_attrs_copy(bm, bm, l->f, l2->f);
BM_elem_attrs_copy(bm, bm, l, l2);
}
if (HasMDisps) {
BMLoop *l3;
BMIter liter3;
BM_ITER(l3, &liter3, bm, BM_LOOPS_OF_FACE, l2->f) {
BM_loop_interp_multires(bm, l3, l->f);
}
}
}
}
}
/* handle vertices along boundary edge */
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, v, VERT_OLD) &&
BMO_elem_flag_test(bm, v, BEVEL_FLAG) &&
!BMO_elem_flag_test(bm, v, BEVEL_DEL))
{
BMLoop *l;
BMLoop *lorig = NULL;
BMIter liter;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
// BMIter liter2;
// BMLoop *l2 = l->v == v ? l : l->next, *l3;
if (BMO_elem_flag_test(bm, l->f, FACE_OLD)) {
lorig = l;
break;
}
}
if (!lorig)
continue;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
BMLoop *l2 = l->v == v ? l : l->next;
BM_elem_attrs_copy(bm, bm, lorig->f, l2->f);
BM_elem_attrs_copy(bm, bm, lorig, l2);
}
}
}
#if 0
/* clean up any remaining 2-edged face */
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (f->len == 2) {
BMFace *faces[2] = {f, BM_FACE_FIRST_LOOP(f)->radial_next->f};
if (faces[0] == faces[1])
BM_face_kill(bm, f);
else
BM_faces_join(bm, faces, 2);
}
}
#endif
BMO_op_callf(bm, "del geom=%fv context=%i", BEVEL_DEL, DEL_VERTS);
/* clean up any edges that might not get properly delete */
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, e, EDGE_OLD) && !e->l)
BMO_elem_flag_enable(bm, e, BEVEL_DEL);
}
BMO_op_callf(bm, "del geom=%fe context=%i", BEVEL_DEL, DEL_EDGES);
BMO_op_callf(bm, "del geom=%ff context=%i", BEVEL_DEL, DEL_FACES);
BLI_smallhash_release(&hash);
BLI_array_free(tags);
BLI_array_free(etags);
BLI_array_free(verts);
BLI_array_free(edges);
BLI_array_free(faces);
BMO_slot_from_flag(bm, op, "face_spans", FACE_SPAN, BM_FACE);
BMO_slot_from_flag(bm, op, "face_holes", FACE_HOLE, BM_FACE);
}
@@ -0,0 +1,414 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "bmesh.h"
#include "BLI_math.h"
#include "BLI_array.h"
#define VERT_INPUT 1
#define EDGE_OUT 1
#define FACE_NEW 2
#define EDGE_MARK 4
#define EDGE_DONE 8
void connectverts_exec(BMesh *bm, BMOperator *op)
{
BMIter iter, liter;
BMFace *f, *nf;
BMLoop **loops = NULL, *lastl = NULL;
BLI_array_declare(loops);
BMLoop *l, *nl;
BMVert **verts = NULL;
BLI_array_declare(verts);
int i;
BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_INPUT, BM_VERT);
for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
BLI_array_empty(loops);
BLI_array_empty(verts);
if (BMO_elem_flag_test(bm, f, FACE_NEW)) continue;
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
lastl = NULL;
for ( ; l; l = BM_iter_step(&liter)) {
if (BMO_elem_flag_test(bm, l->v, VERT_INPUT)) {
if (!lastl) {
lastl = l;
continue;
}
if (lastl != l->prev && lastl != l->next) {
BLI_array_growone(loops);
loops[BLI_array_count(loops) - 1] = lastl;
BLI_array_growone(loops);
loops[BLI_array_count(loops) - 1] = l;
}
lastl = l;
}
}
if (BLI_array_count(loops) == 0) continue;
if (BLI_array_count(loops) > 2) {
BLI_array_growone(loops);
loops[BLI_array_count(loops) - 1] = loops[BLI_array_count(loops) - 2];
BLI_array_growone(loops);
loops[BLI_array_count(loops) - 1] = loops[0];
}
BM_face_legal_splits(bm, f, (BMLoop *(*)[2])loops, BLI_array_count(loops) / 2);
for (i = 0; i < BLI_array_count(loops) / 2; i++) {
if (loops[i * 2] == NULL) continue;
BLI_array_growone(verts);
verts[BLI_array_count(verts) - 1] = loops[i * 2]->v;
BLI_array_growone(verts);
verts[BLI_array_count(verts) - 1] = loops[i * 2 + 1]->v;
}
for (i = 0; i < BLI_array_count(verts) / 2; i++) {
nf = BM_face_split(bm, f, verts[i * 2], verts[i * 2 + 1], &nl, NULL);
f = nf;
if (!nl || !nf) {
BMO_error_raise(bm, op, BMERR_CONNECTVERT_FAILED, NULL);
BLI_array_free(loops);
return;
}
BMO_elem_flag_enable(bm, nf, FACE_NEW);
BMO_elem_flag_enable(bm, nl->e, EDGE_OUT);
}
}
BMO_slot_from_flag(bm, op, "edgeout", EDGE_OUT, BM_EDGE);
BLI_array_free(loops);
BLI_array_free(verts);
}
static BMVert *get_outer_vert(BMesh *bm, BMEdge *e)
{
BMIter iter;
BMEdge *e2;
int i;
i = 0;
BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, e->v1) {
if (BMO_elem_flag_test(bm, e2, EDGE_MARK)) {
i++;
}
}
return (i == 2) ? e->v2 : e->v1;
}
/* Clamp x to the interval {0..len-1}, with wrap-around */
static int clamp_index(const int x, const int len)
{
return (x < 0) ? (len - (-x % len)) : (x % len);
}
/* There probably is a better way to swap BLI_arrays, or if there
* isn't there should be... */
#define ARRAY_SWAP(elemtype, arr1, arr2) \
{ \
int i; \
elemtype *arr_tmp = NULL; \
BLI_array_declare(arr_tmp); \
for (i = 0; i < BLI_array_count(arr1); i++) { \
BLI_array_append(arr_tmp, arr1[i]); \
} \
BLI_array_empty(arr1); \
for (i = 0; i < BLI_array_count(arr2); i++) { \
BLI_array_append(arr1, arr2[i]); \
} \
BLI_array_empty(arr2); \
for (i = 0; i < BLI_array_count(arr_tmp); i++) { \
BLI_array_append(arr2, arr_tmp[i]); \
} \
BLI_array_free(arr_tmp); \
}
void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op)
{
BMEdge **ee1 = NULL, **ee2 = NULL;
BMVert **vv1 = NULL, **vv2 = NULL;
BLI_array_declare(ee1);
BLI_array_declare(ee2);
BLI_array_declare(vv1);
BLI_array_declare(vv2);
BMOIter siter;
BMIter iter;
BMEdge *e, *nexte;
int c = 0, cl1 = 0, cl2 = 0;
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
if (!BMO_elem_flag_test(bm, e, EDGE_DONE)) {
BMVert *v, *ov;
/* BMEdge *e2, *e3, *oe = e; */ /* UNUSED */
BMEdge *e2, *e3;
if (c > 2) {
BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Select only two edge loops");
goto cleanup;
}
e2 = e;
v = e->v1;
do {
v = BM_edge_other_vert(e2, v);
nexte = NULL;
BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) {
if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK)) {
if (nexte == NULL) {
nexte = e3;
}
else {
/* edges do not form a loop: there is a disk
* with more than two marked edges. */
BMO_error_raise(bm, op, BMERR_INVALID_SELECTION,
"Selection must only contain edges from two edge loops");
goto cleanup;
}
}
}
if (nexte)
e2 = nexte;
} while (nexte && e2 != e);
if (!e2)
e2 = e;
e = e2;
ov = v;
do {
if (c == 0) {
BLI_array_append(ee1, e2);
BLI_array_append(vv1, v);
}
else {
BLI_array_append(ee2, e2);
BLI_array_append(vv2, v);
}
BMO_elem_flag_enable(bm, e2, EDGE_DONE);
v = BM_edge_other_vert(e2, v);
BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) {
if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK) && !BMO_elem_flag_test(bm, e3, EDGE_DONE)) {
break;
}
}
if (e3)
e2 = e3;
} while (e3 && e2 != e);
if (v && !e3) {
if (c == 0) {
if (BLI_array_count(vv1) && v == vv1[BLI_array_count(vv1) - 1]) {
printf("%s: internal state waning *TODO DESCRIPTION!*\n", __func__);
}
BLI_array_append(vv1, v);
}
else {
BLI_array_append(vv2, v);
}
}
/* test for connected loops, and set cl1 or cl2 if so */
if (v == ov) {
if (c == 0) {
cl1 = 1;
}
else {
cl2 = 1;
}
}
c++;
}
}
if (ee1 && ee2) {
int i, j;
BMVert *v1, *v2, *v3, *v4;
int starti = 0, dir1 = 1, wdir = 0, lenv1, lenv2;
/* Simplify code below by avoiding the (!cl1 && cl2) case */
if (!cl1 && cl2) {
SWAP(int, cl1, cl2);
ARRAY_SWAP(BMVert *, vv1, vv2);
ARRAY_SWAP(BMEdge *, ee1, ee2);
}
lenv1 = lenv2 = BLI_array_count(vv1);
/* Below code assumes vv1/vv2 each have at least two verts. should always be
* a safe assumption, since ee1/ee2 are non-empty and an edge has two verts. */
BLI_assert((lenv1 > 1) && (lenv2 > 1));
/* BMESH_TODO: Would be nice to handle cases where the edge loops
* have different edge counts by generating triangles & quads for
* the bridge instead of quads only. */
if (BLI_array_count(ee1) != BLI_array_count(ee2)) {
BMO_error_raise(bm, op, BMERR_INVALID_SELECTION,
"Selected loops must have equal edge counts");
goto cleanup;
}
j = 0;
if (vv1[0] == vv1[lenv1 - 1]) {
lenv1--;
}
if (vv2[0] == vv2[lenv2 - 1]) {
lenv2--;
}
/* Find starting point and winding direction for two unclosed loops */
if (!cl1 && !cl2) {
/* First point of loop 1 */
v1 = get_outer_vert(bm, ee1[0]);
/* Last point of loop 1 */
v2 = get_outer_vert(bm, ee1[clamp_index(-1, BLI_array_count(ee1))]);
/* First point of loop 2 */
v3 = get_outer_vert(bm, ee2[0]);
/* Last point of loop 2 */
v4 = get_outer_vert(bm, ee2[clamp_index(-1, BLI_array_count(ee2))]);
/* If v1 is a better match for v4 than v3, AND v2 is a better match
* for v3 than v4, the loops are in opposite directions, so reverse
* the order of reads from vv1. We can avoid sqrt for comparison */
if (len_squared_v3v3(v1->co, v3->co) > len_squared_v3v3(v1->co, v4->co) &&
len_squared_v3v3(v2->co, v4->co) > len_squared_v3v3(v2->co, v3->co))
{
dir1 = -1;
starti = clamp_index(-1, lenv1);
}
}
/* Find the shortest distance from a vert in vv1 to vv2[0]. Use that
* vertex in vv1 as a starting point in the first loop, while starting
* from vv2[0] in the second loop. This is a simplistic attempt to get
* a better edge-to-edge match between the two loops. */
if (cl1) {
int previ, nexti;
float min = 1e32;
/* BMESH_TODO: Would be nice to do a more thorough analysis of all
* the vertices in both loops to find a more accurate match for the
* starting point and winding direction of the bridge generation. */
for (i = 0; i < BLI_array_count(vv1); i++) {
if (len_v3v3(vv1[i]->co, vv2[0]->co) < min) {
min = len_v3v3(vv1[i]->co, vv2[0]->co);
starti = i;
}
}
/* Reverse iteration order for the first loop if the distance of
* the (starti - 1) vert from vv1 is a better match for vv2[1] than
* the (starti + 1) vert.
*
* This is not always going to be right, but it will work better in
* the average case.
*/
previ = clamp_index(starti - 1, lenv1);
nexti = clamp_index(starti + 1, lenv1);
/* avoid sqrt for comparison */
if (len_squared_v3v3(vv1[nexti]->co, vv2[1]->co) > len_squared_v3v3(vv1[previ]->co, vv2[1]->co)) {
/* reverse direction for reading vv1 (1 is forward, -1 is backward) */
dir1 = -1;
}
}
/* Vert rough attempt to determine proper winding for the bridge quads:
* just uses the first loop it finds for any of the edges of ee2 or ee1 */
if (wdir == 0) {
for (i = 0; i < BLI_array_count(ee2); i++) {
if (ee2[i]->l) {
wdir = (ee2[i]->l->v == vv2[i]) ? (-1) : (1);
break;
}
}
}
if (wdir == 0) {
for (i = 0; i < BLI_array_count(ee1); i++) {
j = clamp_index((i * dir1) + starti, BLI_array_count(ee1));
if (ee1[j]->l && ee2[j]->l) {
wdir = (ee2[j]->l->v == vv2[j]) ? (1) : (-1);
break;
}
}
}
/* Generate the bridge quads */
for (i = 0; i < BLI_array_count(ee1) && i < BLI_array_count(ee2); i++) {
BMFace *f;
int i1, i1next, i2, i2next;
i1 = clamp_index(i * dir1 + starti, lenv1);
i1next = clamp_index((i + 1) * dir1 + starti, lenv1);
i2 = i;
i2next = clamp_index(i + 1, lenv2);
if (vv1[i1] == vv1[i1next]) {
continue;
}
if (wdir < 0) {
SWAP(int, i1, i1next);
SWAP(int, i2, i2next);
}
f = BM_face_create_quad_tri(bm,
vv1[i1],
vv2[i2],
vv2[i2next],
vv1[i1next],
NULL, TRUE);
if (!f || f->len != 4) {
fprintf(stderr, "%s: in bridge! (bmesh internal error)\n", __func__);
}
}
}
cleanup:
BLI_array_free(ee1);
BLI_array_free(ee2);
BLI_array_free(vv1);
BLI_array_free(vv2);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,559 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.h"
#include "BLI_math.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "bmesh_operators_private.h" /* own include */
#define FACE_MARK 1
#define FACE_ORIG 2
#define FACE_NEW 4
#define EDGE_MARK 1
#define VERT_MARK 1
static int UNUSED_FUNCTION(check_hole_in_region)(BMesh *bm, BMFace *f)
{
BMWalker regwalker;
BMIter liter2;
BMLoop *l2, *l3;
BMFace *f2;
/* checks if there are any unmarked boundary edges in the face regio */
BMW_init(&regwalker, bm, BMW_ISLAND,
BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
BMW_NIL_LAY);
f2 = BMW_begin(&regwalker, f);
for ( ; f2; f2 = BMW_step(&regwalker)) {
l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
for ( ; l2; l2 = BM_iter_step(&liter2)) {
l3 = bmesh_radial_nextloop(l2);
if ( BMO_elem_flag_test(bm, l3->f, FACE_MARK) !=
BMO_elem_flag_test(bm, l2->f, FACE_MARK))
{
if (!BMO_elem_flag_test(bm, l2->e, EDGE_MARK)) {
return FALSE;
}
}
}
}
BMW_end(&regwalker);
return TRUE;
}
void dissolvefaces_exec(BMesh *bm, BMOperator *op)
{
BMOIter oiter;
BMFace *f, *f2 /* , *nf = NULL */;
BLI_array_declare(faces);
BLI_array_declare(regions);
BMFace ***regions = NULL;
BMFace **faces = NULL;
BMWalker regwalker;
int i;
int use_verts = BMO_slot_int_get(op, "use_verts");
if (use_verts) {
/* tag verts that start out with only 2 edges,
* don't remove these later */
BMIter viter;
BMVert *v;
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (BM_vert_edge_count(v) == 2) {
BMO_elem_flag_disable(bm, v, VERT_MARK);
}
else {
BMO_elem_flag_enable(bm, v, VERT_MARK);
}
}
}
BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_MARK, BM_FACE);
/* collect region */
BMO_ITER(f, &oiter, bm, op, "faces", BM_FACE) {
if (!BMO_elem_flag_test(bm, f, FACE_MARK)) continue;
BLI_array_empty(faces);
faces = NULL; /* forces different allocatio */
/* yay, walk */
BMW_init(&regwalker, bm, BMW_ISLAND,
BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
BMW_NIL_LAY);
f2 = BMW_begin(&regwalker, f);
for ( ; f2; f2 = BMW_step(&regwalker)) {
BLI_array_append(faces, f2);
}
BMW_end(&regwalker);
for (i = 0; i < BLI_array_count(faces); i++) {
f2 = faces[i];
BMO_elem_flag_disable(bm, f2, FACE_MARK);
BMO_elem_flag_enable(bm, f2, FACE_ORIG);
}
if (BMO_error_occurred(bm)) {
BMO_error_clear(bm);
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
goto cleanup;
}
BLI_array_append(faces, NULL);
BLI_array_append(regions, faces);
}
for (i = 0; i < BLI_array_count(regions); i++) {
int tot = 0;
faces = regions[i];
if (!faces[0]) {
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
"Could not find boundary of dissolve region");
goto cleanup;
}
while (faces[tot])
tot++;
f = BM_faces_join(bm, faces, tot);
if (!f) {
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
"Could not create merged face");
goto cleanup;
}
/* if making the new face failed (e.g. overlapping test)
* unmark the original faces for deletion */
BMO_elem_flag_disable(bm, f, FACE_ORIG);
BMO_elem_flag_enable(bm, f, FACE_NEW);
}
BMO_op_callf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES);
if (use_verts) {
BMIter viter;
BMVert *v;
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
if (BM_vert_edge_count(v) == 2) {
BM_vert_collapse_edges(bm, v->e, v);
}
}
}
}
if (BMO_error_occurred(bm)) goto cleanup;
BMO_slot_from_flag(bm, op, "regionout", FACE_NEW, BM_FACE);
cleanup:
/* free/cleanu */
for (i = 0; i < BLI_array_count(regions); i++) {
if (regions[i]) MEM_freeN(regions[i]);
}
BLI_array_free(regions);
}
/* almost identical to dissolve edge, except it cleans up vertice */
void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op)
{
/* BMOperator fop; */
BMOIter oiter;
BMIter iter;
BMVert *v, **verts = NULL;
BLI_array_declare(verts);
BMEdge *e;
/* BMFace *f; */
int i;
BMO_ITER(e, &oiter, bm, op, "edges", BM_EDGE) {
if (BM_edge_face_count(e) == 2) {
BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
BM_faces_join_pair(bm, e->l->f,
e->l->radial_next->f,
e);
}
}
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, v, VERT_MARK) && BM_vert_edge_count(v) == 2) {
BLI_array_append(verts, v);
}
}
/* clean up extreneous 2-valence vertice */
for (i = 0; i < BLI_array_count(verts); i++) {
if (verts[i]->e) {
BM_vert_collapse_edges(bm, verts[i]->e, verts[i]);
}
}
BLI_array_free(verts);
//BMO_op_initf(bm, &fop, "dissolvefaces faces=%ff", FACE_MARK);
//BMO_op_exec(bm, &fop);
//BMO_slot_copy(op, &fop, "regionout", "regionout");
//BMO_op_finish(bm, &fop);
}
void dissolveedges_exec(BMesh *bm, BMOperator *op)
{
/* might want to make this an option or mode - campbell */
/* BMOperator fop; */
BMOIter eiter;
BMEdge *e;
BMIter viter;
BMVert *v;
int use_verts = BMO_slot_int_get(op, "use_verts");
if (use_verts) {
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (BM_vert_edge_count(v) == 2) {
BMO_elem_flag_disable(bm, v, VERT_MARK);
}
else {
BMO_elem_flag_enable(bm, v, VERT_MARK);
}
}
}
BMO_ITER(e, &eiter, bm, op, "edges", BM_EDGE) {
const int edge_face_count = BM_edge_face_count(e);
if (edge_face_count == 2) {
/* join faces */
BM_faces_join_pair(bm, e->l->f,
e->l->radial_next->f,
e);
}
}
if (use_verts) {
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
if (BM_vert_edge_count(v) == 2) {
BM_vert_collapse_edges(bm, v->e, v);
}
}
}
}
}
static int test_extra_verts(BMesh *bm, BMVert *v)
{
BMIter iter, liter, iter2, iter3;
BMFace *f, *f2;
BMLoop *l;
BMEdge *e;
int found;
/* test faces around verts for verts that would be wronly killed
* by dissolve faces. */
f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v);
for ( ; f; f = BM_iter_step(&iter)) {
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
for ( ; l; l = BM_iter_step(&liter)) {
if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) {
/* if an edge around a vert is a boundary edge,
* then dissolve faces won't destroy it.
* also if it forms a boundary with one
* of the face region */
found = FALSE;
e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, l->v);
for ( ; e; e = BM_iter_step(&iter2)) {
if (BM_edge_face_count(e) == 1) {
found = TRUE;
}
f2 = BM_iter_new(&iter3, bm, BM_FACES_OF_EDGE, e);
for ( ; f2; f2 = BM_iter_step(&iter3)) {
if (!BMO_elem_flag_test(bm, f2, FACE_MARK)) {
found = TRUE;
break;
}
}
if (found == TRUE) {
break;
}
}
if (found == FALSE) {
return FALSE;
}
}
}
}
return TRUE;
}
void dissolveverts_exec(BMesh *bm, BMOperator *op)
{
BMOpSlot *vinput;
BMIter iter, fiter;
BMVert *v;
BMFace *f;
/* int i; */
vinput = BMO_slot_get(op, "verts");
BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_MARK, BM_VERT);
for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
/* check if it's a two-valence ver */
if (BM_vert_edge_count(v) == 2) {
/* collapse the ver */
BM_vert_collapse_faces(bm, v->e, v, 1.0f, TRUE);
continue;
}
f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
for ( ; f; f = BM_iter_step(&fiter)) {
BMO_elem_flag_enable(bm, f, FACE_ORIG);
BMO_elem_flag_enable(bm, f, FACE_MARK);
}
/* check if our additions to the input to face dissolve
* will destroy nonmarked vertices. */
if (!test_extra_verts(bm, v)) {
f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
for ( ; f; f = BM_iter_step(&fiter)) {
if (BMO_elem_flag_test(bm, f, FACE_ORIG)) {
BMO_elem_flag_disable(bm, f, FACE_MARK);
BMO_elem_flag_disable(bm, f, FACE_ORIG);
}
}
}
else {
f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
for ( ; f; f = BM_iter_step(&fiter)) {
BMO_elem_flag_disable(bm, f, FACE_ORIG);
}
}
}
}
BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_MARK);
if (BMO_error_occurred(bm)) {
const char *msg;
BMO_error_get(bm, &msg, NULL);
BMO_error_clear(bm);
BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, msg);
}
/* clean up any remainin */
for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
if (!BM_vert_dissolve(bm, v)) {
BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL);
return;
}
}
}
}
/* this code is for cleaning up two-edged faces, it shall become
* it's own function one day */
#if 0
void dummy_exec(BMesh *bm, BMOperator *op)
{
{
/* clean up two-edged face */
/* basic idea is to keep joining 2-edged faces until their
* gone. this however relies on joining two 2-edged faces
* together to work, which doesn't */
found3 = 1;
while (found3) {
found3 = 0;
for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
if (!BM_face_validate(bm, f, stderr)) {
printf("error.\n");
}
if (f->len == 2) {
//this design relies on join faces working
//with two-edged faces properly.
//commenting this line disables the
//outermost loop.
//found3 = 1;
found2 = 0;
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
fe = l->e;
for ( ; l; l = BM_iter_step(&liter)) {
f2 = BM_iter_new(&fiter, bm,
BM_FACES_OF_EDGE, l->e);
for ( ; f2; f2 = BM_iter_step(&fiter)) {
if (f2 != f) {
BM_faces_join_pair(bm, f, f2, l->e);
found2 = 1;
break;
}
}
if (found2) break;
}
if (!found2) {
bmesh_kf(bm, f);
bmesh_ke(bm, fe);
}
} /* else if (f->len == 3) {
BMEdge *ed[3];
BMVert *vt[3];
BMLoop *lp[3];
int i = 0;
//check for duplicate edges
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
for ( ; l; l = BM_iter_step(&liter)) {
ed[i] = l->e;
lp[i] = l;
vt[i++] = l->v;
}
if (vt[0] == vt[1] || vt[0] == vt[2]) {
i += 1;
}
*/
}
}
if (oldlen == len) break;
oldlen = len;
}
}
#endif
/**/
typedef struct DissolveElemWeight_t {
BMHeader *ele;
float weight;
} DissolveElemWeight_t;
static int dissolve_elem_cmp(const void *a1, const void *a2)
{
const struct DissolveElemWeight_t *d1 = a1, *d2 = a2;
if (d1->weight > d2->weight) return 1;
else if (d1->weight < d2->weight) return -1;
return 0;
}
void dissolvelimit_exec(BMesh *bm, BMOperator *op)
{
BMOpSlot *einput = BMO_slot_get(op, "edges");
BMOpSlot *vinput = BMO_slot_get(op, "verts");
const float angle_max = (float)M_PI / 2.0f;
const float angle_limit = minf(angle_max, BMO_slot_float_get(op, "angle_limit"));
DissolveElemWeight_t *weight_elems = MEM_mallocN(MAX2(einput->len, vinput->len) *
sizeof(DissolveElemWeight_t), __func__);
int i, tot_found;
/* --- first edges --- */
/* go through and split edge */
for (i = 0, tot_found = 0; i < einput->len; i++) {
BMEdge *e = ((BMEdge **)einput->data.p)[i];
const float angle = BM_edge_face_angle(bm, e);
if (angle < angle_limit) {
weight_elems[i].ele = (BMHeader *)e;
weight_elems[i].weight = angle;
tot_found++;
}
else {
weight_elems[i].ele = NULL;
weight_elems[i].weight = angle_max;
}
}
if (tot_found != 0) {
qsort(weight_elems, einput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
for (i = 0; i < tot_found; i++) {
BMEdge *e = (BMEdge *)weight_elems[i].ele;
/* check twice because cumulative effect could disolve over angle limit */
if (BM_edge_face_angle(bm, e) < angle_limit) {
BMFace *nf = BM_faces_join_pair(bm, e->l->f,
e->l->radial_next->f,
e); /* join faces */
/* there may be some errors, we dont mind, just move on */
if (nf == NULL) {
BMO_error_clear(bm);
}
}
}
}
/* --- second verts --- */
for (i = 0, tot_found = 0; i < vinput->len; i++) {
BMVert *v = ((BMVert **)vinput->data.p)[i];
const float angle = BM_vert_edge_angle(bm, v);
if (angle < angle_limit) {
weight_elems[i].ele = (BMHeader *)v;
weight_elems[i].weight = angle;
tot_found++;
}
else {
weight_elems[i].ele = NULL;
weight_elems[i].weight = angle_max;
}
}
if (tot_found != 0) {
qsort(weight_elems, vinput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
for (i = 0; i < tot_found; i++) {
BMVert *v = (BMVert *)weight_elems[i].ele;
/* check twice because cumulative effect could disolve over angle limit */
if (BM_vert_edge_angle(bm, v) < angle_limit) {
BM_vert_collapse_edges(bm, v->e, v); /* join edges */
}
}
}
MEM_freeN(weight_elems);
}
+512
View File
@@ -0,0 +1,512 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.h"
#include "BLI_math.h"
#include "bmesh.h"
/* local flag define */
#define DUPE_INPUT 1 /* input from operato */
#define DUPE_NEW 2
#define DUPE_DONE 4
#define DUPE_MAPPED 8
/*
* 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 verte */
target_vertex = BM_vert_create(target_mesh, source_vertex->co, NULL);
/* Insert new vertex into the vert has */
BLI_ghash_insert(vhash, source_vertex, target_vertex);
/* Copy attribute */
BM_elem_attrs_copy(source_mesh, target_mesh, source_vertex, target_vertex);
/* Set internal op flag */
BMO_elem_flag_enable(target_mesh, target_vertex, DUPE_NEW);
return target_vertex;
}
/*
* COPY EDGE
*
* Copy an existing edge from one bmesh to another.
*
*/
static BMEdge *copy_edge(BMOperator *op, BMesh *source_mesh,
BMEdge *source_edge, BMesh *target_mesh,
GHash *vhash, GHash *ehash)
{
BMEdge *target_edge = NULL;
BMVert *target_vert1, *target_vert2;
BMFace *face;
BMIter fiter;
int rlen;
/* see if any of the neighboring faces are
* not being duplicated. in that case,
* add it to the new/old map. */
rlen = 0;
for (face = BM_iter_new(&fiter, source_mesh, BM_FACES_OF_EDGE, source_edge);
face;
face = BM_iter_step(&fiter))
{
if (BMO_elem_flag_test(source_mesh, face, DUPE_INPUT)) {
rlen++;
}
}
/* 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 edg */
target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
/* add to new/old edge map if necassar */
if (rlen < 2) {
/* not sure what non-manifold cases of greater then three
* radial should do. */
BMO_slot_map_ptr_insert(source_mesh, op, "boundarymap",
source_edge, target_edge);
}
/* Insert new edge into the edge hash */
BLI_ghash_insert(ehash, source_edge, target_edge);
/* Copy attributes */
BM_elem_attrs_copy(source_mesh, target_mesh, source_edge, target_edge);
/* Set internal op flags */
BMO_elem_flag_enable(target_mesh, target_edge, DUPE_NEW);
return target_edge;
}
/*
* COPY FACE
*
* Copy an existing face from one bmesh to another.
*/
static BMFace *copy_face(BMOperator *op, BMesh *source_mesh,
BMFace *source_face, BMesh *target_mesh,
BMVert **vtar, BMEdge **edar, GHash *vhash, GHash *ehash)
{
/* BMVert *target_vert1, *target_vert2; */ /* UNUSED */
BMLoop *source_loop, *target_loop;
BMFace *target_face = NULL;
BMIter iter, iter2;
int i;
/* lookup the first and second vert */
#if 0 /* UNUSED */
target_vert1 = BLI_ghash_lookup(vhash, BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face));
target_vert2 = BLI_ghash_lookup(vhash, BM_iter_step(&iter));
#else
BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face);
BM_iter_step(&iter);
#endif
/* lookup edge */
for (i = 0, source_loop = BM_iter_new(&iter, source_mesh, BM_LOOPS_OF_FACE, source_face);
source_loop;
source_loop = BM_iter_step(&iter), i++)
{
vtar[i] = BLI_ghash_lookup(vhash, source_loop->v);
edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
}
/* create new fac */
target_face = BM_face_create(target_mesh, vtar, edar, source_face->len, FALSE);
BMO_slot_map_ptr_insert(source_mesh, op,
"facemap", source_face, target_face);
BMO_slot_map_ptr_insert(source_mesh, op,
"facemap", target_face, source_face);
BM_elem_attrs_copy(source_mesh, target_mesh, source_face, target_face);
/* mark the face for outpu */
BMO_elem_flag_enable(target_mesh, target_face, DUPE_NEW);
/* copy per-loop custom dat */
BM_ITER(source_loop, &iter, source_mesh, BM_LOOPS_OF_FACE, source_face) {
BM_ITER(target_loop, &iter2, target_mesh, BM_LOOPS_OF_FACE, target_face) {
if (BLI_ghash_lookup(vhash, source_loop->v) == target_loop->v) {
BM_elem_attrs_copy(source_mesh, target_mesh, source_loop, target_loop);
break;
}
}
}
return target_face;
}
/*
* COPY MESH
*
* Internal Copy function.
*/
static void copy_mesh(BMOperator *op, BMesh *source, BMesh *target)
{
BMVert *v = NULL, *v2;
BMEdge *e = NULL;
BMFace *f = NULL;
BLI_array_declare(vtar);
BLI_array_declare(edar);
BMVert **vtar = NULL;
BMEdge **edar = NULL;
BMIter verts;
BMIter edges;
BMIter faces;
GHash *vhash;
GHash *ehash;
/* initialize pointer hashe */
vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops v");
ehash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops e");
for (v = BM_iter_new(&verts, source, BM_VERTS_OF_MESH, source); v; v = BM_iter_step(&verts)) {
if ( BMO_elem_flag_test(source, v, DUPE_INPUT) &&
!BMO_elem_flag_test(source, v, DUPE_DONE))
{
BMIter iter;
int iso = 1;
v2 = copy_vertex(source, v, target, vhash);
BM_ITER(f, &iter, source, BM_FACES_OF_VERT, v) {
if (BMO_elem_flag_test(source, f, DUPE_INPUT)) {
iso = 0;
break;
}
}
if (iso) {
BM_ITER(e, &iter, source, BM_EDGES_OF_VERT, v) {
if (BMO_elem_flag_test(source, e, DUPE_INPUT)) {
iso = 0;
break;
}
}
}
if (iso) {
BMO_slot_map_ptr_insert(source, op, "isovertmap", v, v2);
}
BMO_elem_flag_enable(source, v, DUPE_DONE);
}
}
/* now we dupe all the edge */
for (e = BM_iter_new(&edges, source, BM_EDGES_OF_MESH, source); e; e = BM_iter_step(&edges)) {
if ( BMO_elem_flag_test(source, e, DUPE_INPUT) &&
!BMO_elem_flag_test(source, e, DUPE_DONE))
{
/* make sure that verts are copie */
if (!BMO_elem_flag_test(source, e->v1, DUPE_DONE)) {
copy_vertex(source, e->v1, target, vhash);
BMO_elem_flag_enable(source, e->v1, DUPE_DONE);
}
if (!BMO_elem_flag_test(source, e->v2, DUPE_DONE)) {
copy_vertex(source, e->v2, target, vhash);
BMO_elem_flag_enable(source, e->v2, DUPE_DONE);
}
/* now copy the actual edg */
copy_edge(op, source, e, target, vhash, ehash);
BMO_elem_flag_enable(source, e, DUPE_DONE);
}
}
/* first we dupe all flagged faces and their elements from sourc */
for (f = BM_iter_new(&faces, source, BM_FACES_OF_MESH, source); f; f = BM_iter_step(&faces)) {
if (BMO_elem_flag_test(source, f, DUPE_INPUT)) {
/* vertex pas */
for (v = BM_iter_new(&verts, source, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) {
if (!BMO_elem_flag_test(source, v, DUPE_DONE)) {
copy_vertex(source, v, target, vhash);
BMO_elem_flag_enable(source, v, DUPE_DONE);
}
}
/* edge pas */
for (e = BM_iter_new(&edges, source, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) {
if (!BMO_elem_flag_test(source, e, DUPE_DONE)) {
copy_edge(op, source, e, target, vhash, ehash);
BMO_elem_flag_enable(source, e, DUPE_DONE);
}
}
/* ensure arrays are the right size */
BLI_array_empty(vtar);
BLI_array_empty(edar);
BLI_array_growitems(vtar, f->len);
BLI_array_growitems(edar, f->len);
copy_face(op, source, f, target, vtar, edar, vhash, ehash);
BMO_elem_flag_enable(source, f, DUPE_DONE);
}
}
/* free pointer hashe */
BLI_ghash_free(vhash, NULL, NULL);
BLI_ghash_free(ehash, NULL, NULL);
BLI_array_free(vtar); /* free vert pointer array */
BLI_array_free(edar); /* free edge pointer array */
}
/*
* 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;
BMesh *bm2 = BMO_slot_ptr_get(op, "dest");
if (!bm2)
bm2 = bm;
/* flag inpu */
BMO_slot_buffer_flag_enable(bm, dupeop, "geom", DUPE_INPUT, BM_ALL);
/* use the internal copy functio */
copy_mesh(dupeop, bm, bm2);
/* Outpu */
/* First copy the input buffers to output buffers - original dat */
BMO_slot_copy(dupeop, dupeop, "geom", "origout");
/* Now alloc the new output buffer */
BMO_slot_from_flag(bm, dupeop, "newout", DUPE_NEW, BM_ALL);
}
#if 0 /* UNUSED */
/* 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 BMO_dupe_from_flag(BMesh *bm, int etypeflag, const char hflag)
{
BMOperator dupeop;
BMO_op_init(bm, &dupeop, "dupe");
BMO_slot_from_hflag(bm, &dupeop, "geom", hflag, etypeflag);
BMO_op_exec(bm, &dupeop);
BMO_op_finish(bm, &dupeop);
}
#endif
/*
* 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
*/
#define SPLIT_INPUT 1
void splitop_exec(BMesh *bm, BMOperator *op)
{
BMOperator *splitop = op;
BMOperator dupeop;
BMOperator delop;
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter iter, iter2;
int found;
/* initialize our sub-operator */
BMO_op_init(bm, &dupeop, "dupe");
BMO_op_init(bm, &delop, "del");
BMO_slot_copy(splitop, &dupeop, "geom", "geom");
BMO_op_exec(bm, &dupeop);
BMO_slot_buffer_flag_enable(bm, splitop, "geom", SPLIT_INPUT, BM_ALL);
/* make sure to remove edges and verts we don't need */
for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); e; e = BM_iter_step(&iter)) {
found = 0;
f = BM_iter_new(&iter2, bm, BM_FACES_OF_EDGE, e);
for ( ; f; f = BM_iter_step(&iter2)) {
if (!BMO_elem_flag_test(bm, f, SPLIT_INPUT)) {
found = 1;
break;
}
}
if (!found) BMO_elem_flag_enable(bm, e, SPLIT_INPUT);
}
for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
found = 0;
e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, v);
for ( ; e; e = BM_iter_step(&iter2)) {
if (!BMO_elem_flag_test(bm, e, SPLIT_INPUT)) {
found = 1;
break;
}
}
if (!found) BMO_elem_flag_enable(bm, v, SPLIT_INPUT);
}
/* connect outputs of dupe to delete, exluding keep geometr */
BMO_slot_int_set(&delop, "context", DEL_FACES);
BMO_slot_from_flag(bm, &delop, "geom", SPLIT_INPUT, BM_ALL);
BMO_op_exec(bm, &delop);
/* now we make our outputs by copying the dupe output */
BMO_slot_copy(&dupeop, splitop, "newout", "geomout");
BMO_slot_copy(&dupeop, splitop, "boundarymap",
"boundarymap");
BMO_slot_copy(&dupeop, splitop, "isovertmap",
"isovertmap");
/* cleanu */
BMO_op_finish(bm, &delop);
BMO_op_finish(bm, &dupeop);
}
void delop_exec(BMesh *bm, BMOperator *op)
{
#define DEL_INPUT 1
BMOperator *delop = op;
/* Mark Buffer */
BMO_slot_buffer_flag_enable(bm, delop, "geom", DEL_INPUT, BM_ALL);
BMO_remove_tagged_context(bm, DEL_INPUT, BMO_slot_int_get(op, "context"));
#undef DEL_INPUT
}
/*
* Spin Operator
*
* Extrude or duplicate geometry a number of times,
* rotating and possibly translating after each step
*/
void spinop_exec(BMesh *bm, BMOperator *op)
{
BMOperator dupop, extop;
float cent[3], dvec[3];
float axis[3] = {0.0f, 0.0f, 1.0f};
float q[4];
float rmat[3][3];
float phi, si;
int steps, dupli, a, usedvec;
BMO_slot_vec_get(op, "cent", cent);
BMO_slot_vec_get(op, "axis", axis);
normalize_v3(axis);
BMO_slot_vec_get(op, "dvec", dvec);
usedvec = !is_zero_v3(dvec);
steps = BMO_slot_int_get(op, "steps");
phi = BMO_slot_float_get(op, "ang") * (float)M_PI / (360.0f * steps);
dupli = BMO_slot_int_get(op, "dupli");
si = (float)sin(phi);
q[0] = (float)cos(phi);
q[1] = axis[0]*si;
q[2] = axis[1]*si;
q[3] = axis[2]*si;
quat_to_mat3(rmat, q);
BMO_slot_copy(op, op, "geom", "lastout");
for (a = 0; a < steps; a++) {
if (dupli) {
BMO_op_initf(bm, &dupop, "dupe geom=%s", op, "lastout");
BMO_op_exec(bm, &dupop);
BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s",
cent, rmat, &dupop, "newout");
BMO_slot_copy(&dupop, op, "newout", "lastout");
BMO_op_finish(bm, &dupop);
}
else {
BMO_op_initf(bm, &extop, "extrudefaceregion edgefacein=%s",
op, "lastout");
BMO_op_exec(bm, &extop);
BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s",
cent, rmat, &extop, "geomout");
BMO_slot_copy(&extop, op, "geomout", "lastout");
BMO_op_finish(bm, &extop);
}
if (usedvec)
BMO_op_callf(bm, "translate vec=%v verts=%s", dvec, op, "lastout");
}
}
@@ -0,0 +1,426 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.h"
#include "bmesh.h"
#include "bmesh_operators_private.h" /* own include */
typedef struct EdgeTag {
BMVert *newv1, *newv2;
BMEdge *newe1, *newe2;
int tag;
} EdgeTag;
/* (EDGE_DEL == FACE_DEL) - this must be the case */
#define EDGE_DEL 1
#define EDGE_SEAM 2
#define EDGE_MARK 4
#define EDGE_RET1 8
#define EDGE_RET2 16
#define FACE_DEL 1
#define FACE_NEW 2
static BMFace *remake_face(BMesh *bm, EdgeTag *etags, BMFace *f, BMVert **verts, BMEdge **edges_tmp)
{
BMIter liter1, liter2;
EdgeTag *et;
BMFace *f2;
BMLoop *l, *l2;
BMEdge *e;
BMVert *lastv1, *lastv2 /* , *v1, *v2 */ /* UNUSED */;
int i;
/* we do final edge last */
lastv1 = verts[f->len - 1];
lastv2 = verts[0];
/* v1 = verts[0]; */ /* UNUSED */
/* v2 = verts[1]; */ /* UNUSED */
for (i = 0; i < f->len - 1; i++) {
e = BM_edge_create(bm, verts[i], verts[i + 1], NULL, TRUE);
if (!e) {
return NULL;
}
edges_tmp[i] = e;
}
edges_tmp[i] = BM_edge_create(bm, lastv1, lastv2, NULL, TRUE);
f2 = BM_face_create(bm, verts, edges_tmp, f->len, FALSE);
if (!f2) {
return NULL;
}
BM_elem_attrs_copy(bm, bm, f, f2);
l = BM_iter_new(&liter1, bm, BM_LOOPS_OF_FACE, f);
l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
for ( ; l && l2; l = BM_iter_step(&liter1), l2 = BM_iter_step(&liter2)) {
BM_elem_attrs_copy(bm, bm, l, l2);
if (l->e != l2->e) {
/* set up data for figuring out the two sides of
* the split */
/* set edges index as dirty after running all */
BM_elem_index_set(l2->e, BM_elem_index_get(l->e)); /* set_dirty! */
et = &etags[BM_elem_index_get(l->e)];
if (!et->newe1) {
et->newe1 = l2->e;
}
else if (!et->newe2) {
et->newe2 = l2->e;
}
else {
/* Only two new edges should be created from each original edge
* for edge split operation */
//BLI_assert(et->newe1 == l2->e || et->newe2 == l2->e);
et->newe2 = l2->e;
}
if (BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
BMO_elem_flag_enable(bm, l2->e, EDGE_SEAM);
}
BM_elem_attrs_copy(bm, bm, l->e, l2->e);
}
BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
BMO_elem_flag_enable(bm, l2->e, EDGE_MARK);
}
return f2;
}
static void tag_out_edges(BMesh *bm, EdgeTag *etags, BMOperator *UNUSED(op))
{
EdgeTag *et;
BMIter iter;
BMLoop *l, *startl;
BMEdge *e;
BMVert *v;
int i, ok;
ok = 0;
while (ok++ < 100000) {
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, e, EDGE_SEAM))
continue;
et = &etags[BM_elem_index_get(e)];
if (!et->tag && e->l) {
break;
}
}
if (!e) {
break;
}
/* ok we found an edge, part of a region of splits we need
* to identify. now walk along it */
for (i = 0; i < 2; i++) {
l = e->l;
v = i ? l->next->v : l->v;
while (1) {
et = &etags[BM_elem_index_get(l->e)];
if (et->newe1 == l->e) {
if (et->newe1) {
BMO_elem_flag_enable(bm, et->newe1, EDGE_RET1);
BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM);
}
if (et->newe2) {
BMO_elem_flag_enable(bm, et->newe2, EDGE_RET2);
BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM);
}
}
else {
if (et->newe1) {
BMO_elem_flag_enable(bm, et->newe1, EDGE_RET2);
BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM);
}
if (et->newe2) {
BMO_elem_flag_enable(bm, et->newe2, EDGE_RET1);
BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM);
}
}
/* If the original edge was non-manifold edges, then it is
* possible l->e is not et->newe1 or et->newe2. So always clear
* the flag on l->e as well, to prevent infinite looping. */
BMO_elem_flag_disable(bm, l->e, EDGE_SEAM);
startl = l;
do {
l = BM_face_other_loop(l->e, l->f, v);
if (l == startl || BM_edge_face_count(l->e) != 2) {
break;
}
l = l->radial_next;
} while (l != startl && !BMO_elem_flag_test(bm, l->e, EDGE_SEAM));
if (l == startl || !BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
break;
}
v = (l->v == v) ? l->next->v : l->v;
}
}
}
}
void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op)
{
EdgeTag *etags, *et;
BMIter iter, liter;
BMOIter siter;
BMFace *f, *f2;
BMLoop *l, *nextl, *prevl, *l2, *l3;
BMEdge *e, *e2;
BMVert *v, *v2, **verts = NULL;
BLI_array_declare(verts);
BMEdge **edges_tmp = NULL;
BLI_array_declare(edges_tmp);
int i, j;
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_SEAM, BM_EDGE);
/* single marked edges unconnected to any other marked edges
* are illegal, go through and unmark them */
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
for (i = 0; i < 2; i++) {
BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) {
if (e != e2 && BMO_elem_flag_test(bm, e2, EDGE_SEAM)) {
break;
}
}
if (e2) {
break;
}
}
if (!e2) {
BMO_elem_flag_disable(bm, e, EDGE_SEAM);
}
}
etags = MEM_callocN(sizeof(EdgeTag)*bm->totedge, "EdgeTag");
BM_mesh_elem_index_ensure(bm, BM_EDGE);
#ifdef ETV
# undef ETV
#endif
#ifdef SETETV
# undef SETETV
#endif
#define ETV(et, v, l) (l->e->v1 == v ? et->newv1 : et->newv2)
#define SETETV(et, v, l, vs) l->e->v1 == v ? (et->newv1 = vs) : (et->newv2 = vs)
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, f, FACE_NEW)) {
continue;
}
BLI_array_empty(verts);
BLI_array_growitems(verts, f->len);
memset(verts, 0, sizeof(BMVert *) * f->len);
/* this is passed onto remake_face() so it doesnt need to allocate
* a new array on each call. */
BLI_array_empty(edges_tmp);
BLI_array_growitems(edges_tmp, f->len);
i = 0;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
if (!BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
if (!verts[i]) {
et = &etags[BM_elem_index_get(l->e)];
if (ETV(et, l->v, l)) {
verts[i] = ETV(et, l->v, l);
}
else
{
verts[i] = l->v;
}
}
i++;
continue;
}
nextl = l->next;
prevl = l->prev;
for (j = 0; j < 2; j++) {
/* correct as long as i & j dont change during the loop */
const int fv_index = j ? (i + 1) % f->len : i; /* face vert index */
l2 = j ? nextl : prevl;
v = j ? l2->v : l->v;
if (BMO_elem_flag_test(bm, l2->e, EDGE_SEAM)) {
if (verts[fv_index] == NULL) {
/* make unique vert here for this face only */
v2 = BM_vert_create(bm, v->co, v);
verts[fv_index] = v2;
}
else {
v2 = verts[fv_index];
}
}
else {
/* generate unique vert for non-seam edge(s)
* around the manifold vert fan if necassary */
/* first check that we have two seam edges
* somewhere within this fa */
l3 = l2;
do {
if (BM_edge_face_count(l3->e) != 2) {
/* if we hit a boundary edge, tag
* l3 as null so we know to disconnect
* it */
if (BM_edge_face_count(l3->e) == 1) {
l3 = NULL;
}
break;
}
l3 = l3->radial_next;
l3 = BM_face_other_loop(l3->e, l3->f, v);
} while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM));
if (l3 == NULL || (BMO_elem_flag_test(bm, l3->e, EDGE_SEAM) && l3->e != l->e)) {
et = &etags[BM_elem_index_get(l2->e)];
if (ETV(et, v, l2) == NULL) {
v2 = BM_vert_create(bm, v->co, v);
l3 = l2;
do {
SETETV(et, v, l3, v2);
if (BM_edge_face_count(l3->e) != 2) {
break;
}
l3 = l3->radial_next;
l3 = BM_face_other_loop(l3->e, l3->f, v);
et = &etags[BM_elem_index_get(l3->e)];
} while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM));
}
else {
v2 = ETV(et, v, l2);
}
verts[fv_index] = v2;
}
else {
verts[fv_index] = v;
}
}
}
i++;
}
/* debugging code, quick way to find the face/vert combination
* which is failing assuming quads start planer - campbell */
#if 0
if (f->len == 4) {
float no1[3];
float no2[3];
float angle_error;
printf(" ** found QUAD\n");
normal_tri_v3(no1, verts[0]->co, verts[1]->co, verts[2]->co);
normal_tri_v3(no2, verts[0]->co, verts[2]->co, verts[3]->co);
if ((angle_error = angle_v3v3(no1, no2)) > 0.05) {
printf(" ERROR %.4f\n", angle_error);
print_v3("0", verts[0]->co);
print_v3("1", verts[1]->co);
print_v3("2", verts[2]->co);
print_v3("3", verts[3]->co);
}
}
else {
printf(" ** fount %d len face\n", f->len);
}
#endif
f2 = remake_face(bm, etags, f, verts, edges_tmp);
if (!f2) {
continue;
}
BMO_elem_flag_enable(bm, f, FACE_DEL);
BMO_elem_flag_enable(bm, f2, FACE_NEW);
}
/* remake_face() sets invalid indecies,
* likely these will be corrected on operator exit anyway */
bm->elem_index_dirty &= ~BM_EDGE;
/* cant call the operator because 'tag_out_edges'
* relies on original index values, from before editing geometry */
#if 0
BMO_op_callf(bm, "del geom=%ff context=%i", FACE_DEL, DEL_ONLYFACES);
#else
BMO_remove_tagged_context(bm, FACE_DEL, DEL_ONLYFACES);
#endif
/* test EDGE_MARK'd edges if we need to delete them, EDGE_MARK
* is set in remake_face */
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
if (!e->l) {
BMO_elem_flag_enable(bm, e, EDGE_DEL);
}
}
}
#if 0
BMO_op_callf(bm, "del geom=%fe context=%i", EDGE_DEL, DEL_EDGES);
#else
BMO_remove_tagged_context(bm, EDGE_DEL, DEL_EDGES);
#endif
tag_out_edges(bm, etags, op);
BMO_slot_from_flag(bm, op, "edgeout1", EDGE_RET1, BM_EDGE);
BMO_slot_from_flag(bm, op, "edgeout2", EDGE_RET2, BM_EDGE);
BLI_array_free(verts);
BLI_array_free(edges_tmp);
if (etags) MEM_freeN(etags);
}
#undef ETV
#undef SETETV
@@ -0,0 +1,591 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "bmesh.h"
#include "bmesh_operators_private.h" /* own include */
#define EXT_INPUT 1
#define EXT_KEEP 2
#define EXT_DEL 4
#define VERT_MARK 1
#define EDGE_MARK 1
#define FACE_MARK 1
#define VERT_NONMAN 2
#define EDGE_NONMAN 2
void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMIter liter, liter2;
BMFace *f, *f2, *f3;
BMLoop *l, *l2, *l3, *l4, *l_tmp;
BMEdge **edges = NULL, *e, *laste;
BMVert *v, *lastv, *firstv;
BLI_array_declare(edges);
int i;
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
BLI_array_empty(edges);
i = 0;
firstv = lastv = NULL;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BLI_array_growone(edges);
v = BM_vert_create(bm, l->v->co, l->v);
if (lastv) {
e = BM_edge_create(bm, lastv, v, l->e, FALSE);
edges[i++] = e;
}
lastv = v;
laste = l->e;
if (!firstv) firstv = v;
}
BLI_array_growone(edges);
e = BM_edge_create(bm, v, firstv, laste, FALSE);
edges[i++] = e;
BMO_elem_flag_enable(bm, f, EXT_DEL);
f2 = BM_face_create_ngon(bm, firstv, BM_edge_other_vert(edges[0], firstv), edges, f->len, FALSE);
if (!f2) {
BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Extrude failed; could not create face");
BLI_array_free(edges);
return;
}
BMO_elem_flag_enable(bm, f2, EXT_KEEP);
BM_elem_attrs_copy(bm, bm, f, f2);
l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BM_elem_attrs_copy(bm, bm, l, l2);
l3 = l->next;
l4 = l2->next;
f3 = BM_face_create_quad_tri(bm, l3->v, l4->v, l2->v, l->v, f, FALSE);
l_tmp = BM_FACE_FIRST_LOOP(f3);
BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next;
BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next;
BM_elem_attrs_copy(bm, bm, l, l_tmp); l_tmp = l_tmp->next;
BM_elem_attrs_copy(bm, bm, l, l_tmp);
l2 = BM_iter_step(&liter2);
}
}
BLI_array_free(edges);
BMO_op_callf(bm, "del geom=%ff context=%d", EXT_DEL, DEL_ONLYFACES);
BMO_slot_from_flag(bm, op, "faceout", EXT_KEEP, BM_FACE);
}
void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMOperator dupeop;
BMVert *v1, *v2, *v3, *v4;
BMEdge *e, *e2;
BMFace *f;
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
BMO_elem_flag_enable(bm, e, EXT_INPUT);
BMO_elem_flag_enable(bm, e->v1, EXT_INPUT);
BMO_elem_flag_enable(bm, e->v2, EXT_INPUT);
}
BMO_op_initf(bm, &dupeop, "dupe geom=%fve", EXT_INPUT);
BMO_op_exec(bm, &dupeop);
e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0);
for ( ; e; e = BMO_iter_step(&siter)) {
e2 = BMO_iter_map_value(&siter);
e2 = *(BMEdge **)e2;
if (e->l && e->v1 != e->l->v) {
v1 = e->v1;
v2 = e->v2;
v3 = e2->v2;
v4 = e2->v1;
}
else {
v1 = e2->v1;
v2 = e2->v2;
v3 = e->v2;
v4 = e->v1;
}
/* not sure what to do about example face, pass NULL for now */
f = BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE);
if (BMO_elem_flag_test(bm, e, EXT_INPUT))
e = e2;
BMO_elem_flag_enable(bm, f, EXT_KEEP);
BMO_elem_flag_enable(bm, e, EXT_KEEP);
BMO_elem_flag_enable(bm, e->v1, EXT_KEEP);
BMO_elem_flag_enable(bm, e->v2, EXT_KEEP);
}
BMO_op_finish(bm, &dupeop);
BMO_slot_from_flag(bm, op, "geomout", EXT_KEEP, BM_ALL);
}
void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMVert *v, *dupev;
BMEdge *e;
v = BMO_iter_new(&siter, bm, op, "verts", BM_VERT);
for ( ; v; v = BMO_iter_step(&siter)) {
dupev = BM_vert_create(bm, v->co, v);
e = BM_edge_create(bm, v, dupev, NULL, FALSE);
BMO_elem_flag_enable(bm, e, EXT_KEEP);
BMO_elem_flag_enable(bm, dupev, EXT_KEEP);
}
BMO_slot_from_flag(bm, op, "vertout", EXT_KEEP, BM_VERT);
BMO_slot_from_flag(bm, op, "edgeout", EXT_KEEP, BM_EDGE);
}
void extrude_edge_context_exec(BMesh *bm, BMOperator *op)
{
BMOperator dupeop, delop;
BMOIter siter;
BMIter iter, fiter, viter;
BMEdge *e, *newedge;
BMLoop *l, *l2;
BMVert *verts[4], *v, *v2;
BMFace *f;
int rlen, found, fwd, delorig = 0;
/* initialize our sub-operators */
BMO_op_init(bm, &dupeop, "dupe");
BMO_slot_buffer_flag_enable(bm, op, "edgefacein", EXT_INPUT, BM_EDGE|BM_FACE);
/* if one flagged face is bordered by an unflagged face, then we delete
* original geometry unless caller explicitly asked to keep it. */
if (!BMO_slot_int_get(op, "alwayskeeporig")) {
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, e, EXT_INPUT)) continue;
found = 0;
f = BM_iter_new(&fiter, bm, BM_FACES_OF_EDGE, e);
for (rlen = 0; f; f = BM_iter_step(&fiter), rlen++) {
if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
found = 1;
delorig = 1;
break;
}
}
if (!found && (rlen > 1)) BMO_elem_flag_enable(bm, e, EXT_DEL);
}
}
/* calculate verts to delet */
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
found = 0;
BM_ITER(e, &viter, bm, BM_EDGES_OF_VERT, v) {
if (!BMO_elem_flag_test(bm, e, EXT_INPUT) || !BMO_elem_flag_test(bm, e, EXT_DEL)) {
found = 1;
break;
}
}
BM_ITER(f, &viter, bm, BM_FACES_OF_VERT, v) {
if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
found = 1;
break;
}
}
if (!found) {
BMO_elem_flag_enable(bm, v, EXT_DEL);
}
}
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, f, EXT_INPUT))
BMO_elem_flag_enable(bm, f, EXT_DEL);
}
if (delorig) {
BMO_op_initf(bm, &delop, "del geom=%fvef context=%d",
EXT_DEL, DEL_ONLYTAGGED);
}
BMO_slot_copy(op, &dupeop, "edgefacein", "geom");
BMO_op_exec(bm, &dupeop);
if (bm->act_face && BMO_elem_flag_test(bm, bm->act_face, EXT_INPUT))
bm->act_face = BMO_slot_map_ptr_get(bm, &dupeop, "facemap", bm->act_face);
if (delorig) BMO_op_exec(bm, &delop);
/* if not delorig, reverse loops of original face */
if (!delorig) {
for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
if (BMO_elem_flag_test(bm, f, EXT_INPUT)) {
BM_face_normal_flip(bm, f);
}
}
}
BMO_slot_copy(&dupeop, op, "newout", "geomout");
e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0);
for ( ; e; e = BMO_iter_step(&siter)) {
if (BMO_slot_map_contains(bm, op, "exclude", e)) continue;
newedge = BMO_iter_map_value(&siter);
newedge = *(BMEdge **)newedge;
if (!newedge) continue;
/* orient loop to give same normal as a loop of newedge
* if it exists (will be an extruded face),
* else same normal as a loop of e, if it exists */
if (!newedge->l)
fwd = !e->l || !(e->l->v == e->v1);
else
fwd = (newedge->l->v == newedge->v1);
if (fwd) {
verts[0] = e->v1;
verts[1] = e->v2;
verts[2] = newedge->v2;
verts[3] = newedge->v1;
}
else {
verts[3] = e->v1;
verts[2] = e->v2;
verts[1] = newedge->v2;
verts[0] = newedge->v1;
}
/* not sure what to do about example face, pass NULL for now */
f = BM_face_create_quad_tri_v(bm, verts, 4, NULL, FALSE);
/* copy attribute */
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
for ( ; l; l = BM_iter_step(&iter)) {
if (l->e != e && l->e != newedge) continue;
l2 = l->radial_next;
if (l2 == l) {
l2 = newedge->l;
BM_elem_attrs_copy(bm, bm, l2->f, l->f);
BM_elem_attrs_copy(bm, bm, l2, l);
l2 = l2->next;
l = l->next;
BM_elem_attrs_copy(bm, bm, l2, l);
}
else {
BM_elem_attrs_copy(bm, bm, l2->f, l->f);
/* copy dat */
if (l2->v == l->v) {
BM_elem_attrs_copy(bm, bm, l2, l);
l2 = l2->next;
l = l->next;
BM_elem_attrs_copy(bm, bm, l2, l);
}
else {
l2 = l2->next;
BM_elem_attrs_copy(bm, bm, l2, l);
l2 = l2->prev;
l = l->next;
BM_elem_attrs_copy(bm, bm, l2, l);
}
}
}
}
/* link isolated vert */
v = BMO_iter_new(&siter, bm, &dupeop, "isovertmap", 0);
for ( ; v; v = BMO_iter_step(&siter)) {
v2 = *((void **)BMO_iter_map_value(&siter));
BM_edge_create(bm, v, v2, v->e, TRUE);
}
/* cleanu */
if (delorig) BMO_op_finish(bm, &delop);
BMO_op_finish(bm, &dupeop);
}
/*
* Compute higher-quality vertex normals used by solidify.
* Only considers geometry in the marked solidify region.
* Note that this does not work so well for non-manifold
* regions.
*/
static void calc_solidify_normals(BMesh *bm)
{
BMIter viter, eiter, fiter;
BMVert *v;
BMEdge *e;
BMFace *f, *f1, *f2;
float edge_normal[3];
int i;
/* can't use BM_edge_face_count because we need to count only marked faces */
int *edge_face_count = MEM_callocN(sizeof(int) * bm->totedge, __func__);
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
BM_elem_flag_enable(v, BM_ELEM_TAG);
}
BM_mesh_elem_index_ensure(bm, BM_EDGE);
BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
continue;
}
BM_ITER(e, &eiter, bm, BM_EDGES_OF_FACE, f) {
/* And mark all edges and vertices on the
* marked faces */
BMO_elem_flag_enable(bm, e, EDGE_MARK);
BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
edge_face_count[BM_elem_index_get(e)]++;
}
}
BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
continue;
}
i = edge_face_count[BM_elem_index_get(e)]++;
if (i == 0 || i > 2) {
/* Edge & vertices are non-manifold even when considering
* only marked faces */
BMO_elem_flag_enable(bm, e, EDGE_NONMAN);
BMO_elem_flag_enable(bm, e->v1, VERT_NONMAN);
BMO_elem_flag_enable(bm, e->v2, VERT_NONMAN);
}
}
MEM_freeN(edge_face_count);
edge_face_count = NULL; /* dont re-use */
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (!BM_vert_is_manifold(bm, v)) {
BMO_elem_flag_enable(bm, v, VERT_NONMAN);
continue;
}
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
zero_v3(v->no);
}
}
BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) {
/* If the edge is not part of a the solidify region
* its normal should not be considered */
if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
continue;
}
/* If the edge joins more than two marked faces high
* quality normal computation won't work */
if (BMO_elem_flag_test(bm, e, EDGE_NONMAN)) {
continue;
}
f1 = f2 = NULL;
BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
if (f1 == NULL) {
f1 = f;
}
else {
BLI_assert(f2 == NULL);
f2 = f;
}
}
}
BLI_assert(f1 != NULL);
if (f2 != NULL) {
const float angle = angle_normalized_v3v3(f1->no, f2->no);
if (angle > 0.0f) {
/* two faces using this edge, calculate the edge normal
* using the angle between the faces as a weighting */
add_v3_v3v3(edge_normal, f1->no, f2->no);
normalize_v3(edge_normal);
mul_v3_fl(edge_normal, angle);
}
else {
/* can't do anything useful here!
* Set the face index for a vert incase it gets a zero normal */
BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
continue;
}
}
else {
/* only one face attached to that edge */
/* an edge without another attached- the weight on this is
* undefined, M_PI / 2 is 90d in radians and that seems good enough */
copy_v3_v3(edge_normal, f1->no);
mul_v3_fl(edge_normal, M_PI / 2);
}
add_v3_v3(e->v1->no, edge_normal);
add_v3_v3(e->v2->no, edge_normal);
}
/* normalize accumulated vertex normal */
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, v, VERT_MARK)) {
continue;
}
if (BMO_elem_flag_test(bm, v, VERT_NONMAN)) {
/* use standard normals for vertices connected to non-manifold edges */
BM_vert_normal_update(bm, v);
}
else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
/* exceptional case, totally flat. use the normal
* of any marked face around the vertex */
BM_ITER(f, &fiter, bm, BM_FACES_OF_VERT, v) {
if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
break;
}
}
copy_v3_v3(v->no, f->no);
}
}
}
static void solidify_add_thickness(BMesh *bm, float dist)
{
BMFace *f;
BMVert *v;
BMLoop *l;
BMIter iter, loopIter;
float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */
float *vert_accum = vert_angles + bm->totvert;
float angle;
int i, index;
float maxdist = dist * sqrtf(3.0f);
/* array for passing verts to angle_poly_v3 */
float **verts = NULL;
BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
/* array for receiving angles from angle_poly_v3 */
float *angles = NULL;
BLI_array_staticdeclare(angles, BM_NGON_STACK_SIZE);
BM_mesh_elem_index_ensure(bm, BM_VERT);
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
continue;
}
BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
BLI_array_append(verts, l->v->co);
BLI_array_growone(angles);
}
angle_poly_v3(angles, (const float **)verts, f->len);
i = 0;
BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
v = l->v;
index = BM_elem_index_get(v);
angle = angles[i];
vert_accum[index] += angle;
vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * angle;
i++;
}
BLI_array_empty(verts);
BLI_array_empty(angles);
}
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
index = BM_elem_index_get(v);
if (vert_accum[index]) { /* zero if unselected */
float vdist = MIN2(maxdist, dist * vert_angles[index] / vert_accum[index]);
madd_v3_v3fl(v->co, v->no, vdist);
}
}
MEM_freeN(vert_angles);
}
void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op)
{
BMOperator extrudeop;
BMOperator reverseop;
float thickness;
thickness = BMO_slot_float_get(op, "thickness");
/* Flip original faces (so the shell is extruded inward) */
BMO_op_init(bm, &reverseop, "reversefaces");
BMO_slot_copy(op, &reverseop, "geom", "faces");
BMO_op_exec(bm, &reverseop);
BMO_op_finish(bm, &reverseop);
/* Extrude the region */
BMO_op_initf(bm, &extrudeop, "extrudefaceregion alwayskeeporig=%i", TRUE);
BMO_slot_copy(op, &extrudeop, "geom", "edgefacein");
BMO_op_exec(bm, &extrudeop);
/* Push the verts of the extruded faces inward to create thickness */
BMO_slot_buffer_flag_enable(bm, &extrudeop, "geomout", FACE_MARK, BM_FACE);
calc_solidify_normals(bm);
solidify_add_thickness(bm, thickness);
BMO_slot_copy(&extrudeop, op, "geomout", "geomout");
BMO_op_finish(bm, &extrudeop);
}
@@ -0,0 +1,373 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "DNA_meshdata_types.h"
#include "BKE_customdata.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "bmesh.h"
#include "bmesh_operators_private.h" /* own include */
/*
* JOIN_TRIANGLES.C
*
* utility bmesh operators, e.g. transform,
* translate, rotate, scale, etc.
*
*/
/* Bitflags for edges */
#define T2QDELETE 1
#define T2QCOMPLEX 2
#define T2QJOIN 4
/* assumes edges are validated before reaching this poin */
static float measure_facepair(BMesh *UNUSED(bm), BMVert *v1, BMVert *v2,
BMVert *v3, BMVert *v4, float limit)
{
/* gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would mak */
/* Note: this is more complicated than it needs to be and should be cleaned up.. */
float n1[3], n2[3], measure = 0.0f, angle1, angle2, diff;
float edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3];
float minarea, maxarea, areaA, areaB;
/* First Test: Normal differenc */
normal_tri_v3(n1, v1->co, v2->co, v3->co);
normal_tri_v3(n2, v1->co, v3->co, v4->co);
if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle1 = 0.0f;
else angle1 = angle_v3v3(n1, n2);
normal_tri_v3(n1, v2->co, v3->co, v4->co);
normal_tri_v3(n2, v4->co, v1->co, v2->co);
if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle2 = 0.0f;
else angle2 = angle_v3v3(n1, n2);
measure += (angle1 + angle2) * 0.5f;
if (measure > limit) {
return measure;
}
/* Second test: Colinearit */
sub_v3_v3v3(edgeVec1, v1->co, v2->co);
sub_v3_v3v3(edgeVec2, v2->co, v3->co);
sub_v3_v3v3(edgeVec3, v3->co, v4->co);
sub_v3_v3v3(edgeVec4, v4->co, v1->co);
/* a completely skinny face is 'pi' after halving */
diff = 0.25f * (fabsf(angle_v3v3(edgeVec1, edgeVec2) - (float)M_PI_2) +
fabsf(angle_v3v3(edgeVec2, edgeVec3) - (float)M_PI_2) +
fabsf(angle_v3v3(edgeVec3, edgeVec4) - (float)M_PI_2) +
fabsf(angle_v3v3(edgeVec4, edgeVec1) - (float)M_PI_2));
if (!diff) {
return 0.0;
}
measure += diff;
if (measure > limit) {
return measure;
}
/* Third test: Concavit */
areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co);
areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(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.005f
#define T2QCOL_LIMIT 3
static int compareFaceAttribs(BMesh *bm, BMEdge *e, int douvs, int dovcols)
{
MTexPoly *tp1, *tp2;
MLoopCol *lcol1, *lcol2, *lcol3, *lcol4;
MLoopUV *luv1, *luv2, *luv3, *luv4;
BMLoop *l1, *l2, *l3, *l4;
int mergeok_uvs = !douvs, mergeok_vcols = !dovcols;
l1 = e->l;
l3 = e->l->radial_next;
/* match up loops on each side of an edge corrusponding to each ver */
if (l1->v == l3->v) {
l2 = l1->next;
l4 = l2->next;
}
else {
l2 = l1->next;
l4 = l3;
l3 = l4->next;
}
lcol1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
lcol2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
lcol3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
lcol4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
luv1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
luv2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
luv3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
luv4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
tp1 = CustomData_bmesh_get(&bm->pdata, l1->f->head.data, CD_MTEXPOLY);
tp2 = CustomData_bmesh_get(&bm->pdata, l2->f->head.data, CD_MTEXPOLY);
if (!lcol1)
mergeok_vcols = 1;
if (!luv1)
mergeok_uvs = 1;
/* compare faceedges for each face attribute. Additional per face attributes can be added late */
/* do VCOL */
if (lcol1 && dovcols) {
char *cols[4] = {(char *)lcol1, (char *)lcol2, (char *)lcol3, (char *)lcol4};
int i;
for (i = 0; i < 3; i++) {
if (cols[0][i] + T2QCOL_LIMIT < cols[2][i] - T2QCOL_LIMIT)
break;
if (cols[1][i] + T2QCOL_LIMIT < cols[3][i] - T2QCOL_LIMIT)
break;
}
if (i == 3)
mergeok_vcols = 1;
}
/* do UV */
if (luv1 && douvs) {
if (tp1->tpage != tp2->tpage); /* do nothin */
else {
int i;
for (i = 0; i < 2; i++) {
if (luv1->uv[0] + T2QUV_LIMIT > luv3->uv[0] && luv1->uv[0] - T2QUV_LIMIT < luv3->uv[0] &&
luv1->uv[1] + T2QUV_LIMIT > luv3->uv[1] && luv1->uv[1] - T2QUV_LIMIT < luv3->uv[1])
{
if (luv2->uv[0] + T2QUV_LIMIT > luv4->uv[0] && luv2->uv[0] - T2QUV_LIMIT < luv4->uv[0] &&
luv2->uv[1] + T2QUV_LIMIT > luv4->uv[1] && luv2->uv[1] - T2QUV_LIMIT < luv4->uv[1])
{
mergeok_uvs = 1;
}
}
}
}
}
if (douvs == mergeok_uvs && dovcols == mergeok_vcols) {
return TRUE;
}
return FALSE;
}
typedef struct JoinEdge {
float weight;
BMEdge *e;
} JoinEdge;
#define EDGE_MARK 1
#define EDGE_CHOSEN 2
#define FACE_MARK 1
#define FACE_INPUT 2
static int fplcmp(const void *v1, const void *v2)
{
const JoinEdge *e1 = (JoinEdge *)v1, *e2 = (JoinEdge *)v2;
if (e1->weight > e2->weight) return 1;
else if (e1->weight < e2->weight) return -1;
return 0;
}
void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op)
{
BMIter iter, liter;
BMOIter siter;
BMFace *f1, *f2;
BMLoop *l;
BMEdge *e;
BLI_array_declare(jedges);
JoinEdge *jedges = NULL;
int dosharp = BMO_slot_int_get(op, "compare_sharp"), douvs = BMO_slot_int_get(op, "compare_uvs");
int dovcols = BMO_slot_int_get(op, "compare_vcols"), domat = BMO_slot_int_get(op, "compare_materials");
float limit = BMO_slot_float_get(op, "limit");
int i, totedge;
/* flag all edges of all input face */
BMO_ITER(f1, &siter, bm, op, "faces", BM_FACE) {
BMO_elem_flag_enable(bm, f1, FACE_INPUT);
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f1) {
BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
}
}
/* unflag edges that are invalid; e.g. aren't surrounded by triangle */
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
continue;
if (BM_edge_face_count(e) != 2) {
BMO_elem_flag_disable(bm, e, EDGE_MARK);
continue;
}
f1 = e->l->f;
f2 = e->l->radial_next->f;
if (f1->len != 3 || f2->len != 3) {
BMO_elem_flag_disable(bm, e, EDGE_MARK);
continue;
}
if (!BMO_elem_flag_test(bm, f1, FACE_INPUT) || !BMO_elem_flag_test(bm, f2, FACE_INPUT)) {
BMO_elem_flag_disable(bm, e, EDGE_MARK);
continue;
}
}
i = 0;
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
BMVert *v1, *v2, *v3, *v4;
BMFace *f1, *f2;
float measure;
if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
continue;
f1 = e->l->f;
f2 = e->l->radial_next->f;
v1 = e->l->v;
v2 = e->l->prev->v;
v3 = e->l->next->v;
v4 = e->l->radial_next->prev->v;
if (dosharp && !BM_elem_flag_test(e, BM_ELEM_SMOOTH))
continue;
if ((douvs || dovcols) && compareFaceAttribs(bm, e, douvs, dovcols))
continue;
if (domat && f1->mat_nr != f2->mat_nr)
continue;
measure = measure_facepair(bm, v1, v2, v3, v4, limit);
if (measure < limit) {
BLI_array_growone(jedges);
jedges[i].e = e;
jedges[i].weight = measure;
i++;
}
}
if (!jedges)
return;
qsort(jedges, BLI_array_count(jedges), sizeof(JoinEdge), fplcmp);
totedge = BLI_array_count(jedges);
for (i = 0; i < totedge; i++) {
BMFace *f1, *f2;
e = jedges[i].e;
f1 = e->l->f;
f2 = e->l->radial_next->f;
if (BMO_elem_flag_test(bm, f1, FACE_MARK) || BMO_elem_flag_test(bm, f2, FACE_MARK))
continue;
BMO_elem_flag_enable(bm, f1, FACE_MARK);
BMO_elem_flag_enable(bm, f2, FACE_MARK);
BMO_elem_flag_enable(bm, e, EDGE_CHOSEN);
}
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, e, EDGE_CHOSEN))
continue;
f1 = e->l->f;
f2 = e->l->radial_next->f;
BM_faces_join_pair(bm, f1, f2, e);
}
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
/* ok, this edge wasn't merged, check if it's
* in a 2-tri-pair island, and if so merg */
f1 = e->l->f;
f2 = e->l->radial_next->f;
if (f1->len != 3 || f2->len != 3)
continue;
for (i = 0; i < 2; i++) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, i ? f2 : f1) {
if (l->e != e && BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
break;
}
}
/* if l isn't NULL, we broke out of the loo */
if (l) {
break;
}
}
/* if i isn't 2, we broke out of that loo */
if (i != 2) {
continue;
}
BM_faces_join_pair(bm, f1, f2, e);
}
}
BLI_array_free(jedges);
}
@@ -0,0 +1,906 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_key_types.h"
#include "DNA_modifier_types.h"
#include "BKE_mesh.h"
#include "BLI_listbase.h"
#include "BKE_global.h"
#include "BKE_key.h"
#include "BKE_main.h"
#include "BKE_customdata.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "bmesh_operators_private.h" /* own include */
/*
* MESH CONV.C
*
* This file contains functions
* for converting a Mesh
* into a Bmesh, and back again.
*
*/
void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op)
{
Object *ob = BMO_slot_ptr_get(op, "object");
Mesh *me = BMO_slot_ptr_get(op, "mesh");
MVert *mvert;
BLI_array_declare(verts);
MEdge *medge;
MLoop *ml;
MPoly *mpoly;
KeyBlock *actkey, *block;
BMVert *v, **vt = NULL, **verts = NULL;
BMEdge *e, **fedges = NULL, **et = NULL;
BMFace *f;
BMLoop *l;
BLI_array_declare(fedges);
float (*keyco)[3] = NULL;
int *keyi;
int set_key = BMO_slot_int_get(op, "set_shapekey");
int totuv, i, j;
if (!me || !me->totvert) {
return; /* sanity check */
}
vt = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable");
CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
/* make sure uv layer names are consisten */
totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
for (i = 0; i < totuv; i++) {
int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i);
CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name);
}
if (!CustomData_has_layer(&bm->edata, CD_CREASE))
CustomData_add_layer(&bm->edata, CD_CREASE, CD_ASSIGN, NULL, 0);
if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT))
CustomData_add_layer(&bm->edata, CD_BWEIGHT, CD_ASSIGN, NULL, 0);
if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT))
CustomData_add_layer(&bm->vdata, CD_BWEIGHT, CD_ASSIGN, NULL, 0);
if (me->key && ob->shapenr > me->key->totkey) {
ob->shapenr = me->key->totkey - 1;
}
actkey = ob_get_keyblock(ob);
if (actkey && actkey->totelem == me->totvert) {
CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
/* check if we need to generate unique ids for the shapekeys.
* this also exists in the file reading code, but is here for
* a sanity chec */
if (!me->key->uidgen) {
fprintf(stderr,
"%s had to generate shape key uid's in a situation we shouldn't need to! "
"(bmesh internal error)\n",
__func__);
me->key->uidgen = 1;
for (block = me->key->block.first; block; block = block->next) {
block->uid = me->key->uidgen++;
}
}
keyco = actkey->data;
bm->shapenr = ob->shapenr;
for (i = 0, block = me->key->block.first; block; block = block->next, i++) {
CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
CD_ASSIGN, NULL, 0, block->name);
j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
bm->vdata.layers[j].uid = block->uid;
}
}
else if (actkey) {
printf("shapekey<->mesh mismatch!\n");
}
CustomData_bmesh_init_pool(&bm->vdata, bm_mesh_allocsize_default[0]);
CustomData_bmesh_init_pool(&bm->edata, bm_mesh_allocsize_default[1]);
CustomData_bmesh_init_pool(&bm->ldata, bm_mesh_allocsize_default[2]);
CustomData_bmesh_init_pool(&bm->pdata, bm_mesh_allocsize_default[3]);
for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
v = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL);
BM_elem_index_set(v, i); /* set_ok */
vt[i] = v;
/* transfer flag */
v->head.hflag = BM_vert_flag_from_mflag(mvert->flag);
/* this is necassary for selection counts to work properl */
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) BM_vert_select_set(bm, v, TRUE);
normal_short_to_float_v3(v->no, mvert->no);
BM_elem_float_data_set(&bm->vdata, v, CD_BWEIGHT, (float)mvert->bweight / 255.0f);
/* Copy Custom Dat */
CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data);
/* set shapekey dat */
if (me->key) {
/* set shape key original inde */
keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX);
if (keyi) {
*keyi = i;
}
for (block = me->key->block.first, j = 0; block; block = block->next, j++) {
float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j);
if (co) {
copy_v3_v3(co, ((float *)block->data) + 3 * i);
}
}
}
}
bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */
if (!me->totedge) {
MEM_freeN(vt);
return;
}
et = MEM_mallocN(sizeof(void **) * me->totedge, "mesh to bmesh etable");
medge = me->medge;
for (i = 0; i < me->totedge; i++, medge++) {
e = BM_edge_create(bm, vt[medge->v1], vt[medge->v2], NULL, FALSE);
BM_elem_index_set(e, i); /* set_ok */
et[i] = e;
/* transfer flags */
e->head.hflag = BM_edge_flag_from_mflag(medge->flag);
/* this is necassary for selection counts to work properly */
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) BM_elem_select_set(bm, e, TRUE);
/* Copy Custom Dat */
CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data);
BM_elem_float_data_set(&bm->edata, e, CD_CREASE, (float)medge->crease / 255.0f);
BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (float)medge->bweight / 255.0f);
}
bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */
mpoly = me->mpoly;
for (i = 0; i < me->totpoly; i++, mpoly++) {
BMIter iter;
BLI_array_empty(fedges);
BLI_array_empty(verts);
BLI_array_growitems(fedges, mpoly->totloop);
BLI_array_growitems(verts, mpoly->totloop);
for (j = 0; j < mpoly->totloop; j++) {
ml = &me->mloop[mpoly->loopstart + j];
v = vt[ml->v];
e = et[ml->e];
fedges[j] = e;
verts[j] = v;
}
/* not sure what this block is supposed to do,
* but its unused. so commenting - campbell */
#if 0
{
BMVert *v1, *v2;
v1 = vt[me->mloop[mpoly->loopstart].v];
v2 = vt[me->mloop[mpoly->loopstart + 1].v];
if (v1 == fedges[0]->v1) {
v2 = fedges[0]->v2;
}
else {
v1 = fedges[0]->v2;
v2 = fedges[0]->v1;
}
}
#endif
f = BM_face_create(bm, verts, fedges, mpoly->totloop, FALSE);
if (!f) {
printf("%s: Warning! Bad face in mesh"
" \"%s\" at index %d!, skipping\n",
__func__, me->id.name + 2, i);
continue;
}
/* dont use 'i' since we may have skipped the face */
BM_elem_index_set(f, bm->totface - 1); /* set_ok */
/* transfer flag */
f->head.hflag = BM_face_flag_from_mflag(mpoly->flag);
/* this is necassary for selection counts to work properl */
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) BM_elem_select_set(bm, f, TRUE);
f->mat_nr = mpoly->mat_nr;
if (i == me->act_face) bm->act_face = f;
j = 0;
BM_ITER_INDEX(l, &iter, bm, BM_LOOPS_OF_FACE, f, j) {
/* Save index of correspsonding MLoop */
BM_elem_index_set(l, mpoly->loopstart + j); /* set_loop */
}
/* Copy Custom Dat */
CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data);
}
bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */
{
BMIter fiter;
BMIter liter;
/* Copy over loop CustomData. Doing this in a separate loop isn't necessary
* but is an optimization, to avoid copying a bunch of interpolated customdata
* for each BMLoop (from previous BMLoops using the same edge), always followed
* by freeing the interpolated data and overwriting it with data from the Mesh. */
BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
int li = BM_elem_index_get(l);
CustomData_to_bmesh_block(&me->ldata, &bm->ldata, li, &l->head.data);
BM_elem_index_set(l, 0); /* set_loop */
}
}
}
if (me->mselect && me->totselect != 0) {
BMIter iter;
BMVert *vertex;
BMEdge *edge;
BMFace *face;
BMVert **vertex_array = MEM_callocN(sizeof(BMVert *) * bm->totvert,
"Selection Conversion Vertex Pointer Array");
BMEdge **edge_array = MEM_callocN(sizeof(BMEdge *) * bm->totedge,
"Selection Conversion Edge Pointer Array");
BMFace **face_array = MEM_callocN(sizeof(BMFace *) * bm->totface,
"Selection Conversion Face Pointer Array");
for (i = 0, vertex = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
vertex;
i++, vertex = BM_iter_step(&iter))
{
vertex_array[i] = vertex;
}
for (i = 0, edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL);
edge;
i++, edge = BM_iter_step(&iter))
{
edge_array[i] = edge;
}
for (i = 0, face = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL);
face;
i++, face = BM_iter_step(&iter))
{
face_array[i] = face;
}
if (me->mselect) {
for (i = 0; i < me->totselect; i++) {
if (me->mselect[i].type == ME_VSEL) {
BM_select_history_store(bm, vertex_array[me->mselect[i].index]);
}
else if (me->mselect[i].type == ME_ESEL) {
BM_select_history_store(bm, edge_array[me->mselect[i].index]);
}
else if (me->mselect[i].type == ME_FSEL) {
BM_select_history_store(bm, face_array[me->mselect[i].index]);
}
}
}
else {
me->totselect = 0;
}
MEM_freeN(vertex_array);
MEM_freeN(edge_array);
MEM_freeN(face_array);
}
else {
me->totselect = 0;
if (me->mselect) {
MEM_freeN(me->mselect);
me->mselect = NULL;
}
}
BLI_array_free(fedges);
BLI_array_free(verts);
MEM_freeN(vt);
MEM_freeN(et);
}
void object_load_bmesh_exec(BMesh *bm, BMOperator *op)
{
Object *ob = BMO_slot_ptr_get(op, "object");
/* Scene *scene = BMO_slot_ptr_get(op, "scene"); */
Mesh *me = ob->data;
BMO_op_callf(bm, "bmesh_to_mesh mesh=%p object=%p notesselation=%i", me, ob, TRUE);
}
static BMVert **bmesh_to_mesh_vertex_map(BMesh *bm, int ototvert)
{
BMVert **vertMap = NULL;
BMVert *eve;
int index;
int i = 0;
BMIter iter;
/* caller needs to ensure this */
BLI_assert(ototvert > 0);
vertMap = MEM_callocN(sizeof(*vertMap)*ototvert, "vertMap");
if (CustomData_has_layer(&bm->vdata, CD_SHAPE_KEYINDEX)) {
int *keyi;
BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
if (keyi) {
if (((index = *keyi) != ORIGINDEX_NONE) && (index < ototvert)) {
vertMap[index] = eve;
}
}
else {
if (i < ototvert) {
vertMap[i] = eve;
}
}
i++;
}
}
else {
BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if (i < ototvert) {
vertMap[i] = eve;
}
else {
break;
}
i++;
}
}
return vertMap;
}
BM_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
{
/* this is a cheap way to set the edge draw, its not precise and will
* pick the first 2 faces an edge uses */
if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
(e->l && (e->l != e->l->radial_next)) &&
(dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.998f))
{
med->flag &= ~ME_EDGEDRAW;
}
}
void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op)
{
Mesh *me = BMO_slot_ptr_get(op, "mesh");
/* Object *ob = BMO_slot_ptr_get(op, "object"); */
MLoop *mloop;
MPoly *mpoly;
MVert *mvert, *oldverts;
MEdge *med, *medge;
BMVert *v, *eve;
BMEdge *e;
BMLoop *l;
BMFace *f;
BMIter iter, liter;
int i, j, *keyi, ototvert, totloop;
int dotess = !BMO_slot_int_get(op, "notesselation");
ototvert = me->totvert;
/* new Vertex block */
if (bm->totvert == 0) mvert = NULL;
else mvert = MEM_callocN(bm->totvert * sizeof(MVert), "loadeditbMesh vert");
/* new Edge block */
if (bm->totedge == 0) medge = NULL;
else medge = MEM_callocN(bm->totedge * sizeof(MEdge), "loadeditbMesh edge");
/* build ngon dat */
/* new Ngon Face block */
if (bm->totface == 0) mpoly = NULL;
else mpoly = MEM_callocN(bm->totface * sizeof(MPoly), "loadeditbMesh poly");
/* find number of loops to allocat */
totloop = 0;
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
totloop += f->len;
}
if (totloop == 0) mloop = NULL;
else mloop = MEM_callocN(totloop * sizeof(MLoop), "loadeditbMesh loop");
/* lets save the old verts just in case we are actually working on
* a key ... we now do processing of the keys at the end */
oldverts = me->mvert;
/* don't free this yet */
CustomData_set_layer(&me->vdata, CD_MVERT, NULL);
/* free custom data */
CustomData_free(&me->vdata, me->totvert);
CustomData_free(&me->edata, me->totedge);
CustomData_free(&me->fdata, me->totface);
CustomData_free(&me->ldata, me->totloop);
CustomData_free(&me->pdata, me->totpoly);
/* add new custom data */
me->totvert = bm->totvert;
me->totedge = bm->totedge;
me->totloop = totloop;
me->totpoly = bm->totface;
/* will be overwritten with a valid value if 'dotess' is set, otherwise we
* end up with 'me->totface' and me->mface == NULL which can crash [#28625]
*/
me->totface = 0;
CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert);
CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge);
CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop);
CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly);
CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop);
CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly);
/* this is called again, 'dotess' arg is used there */
mesh_update_customdata_pointers(me, 0);
i = 0;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
float *bweight = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_BWEIGHT);
mvert->bweight = bweight ? (char)((*bweight) * 255) : 0;
copy_v3_v3(mvert->co, v->co);
normal_float_to_short_v3(mvert->no, v->no);
mvert->flag = BM_vert_flag_to_mflag(v);
BM_elem_index_set(v, i); /* set_inline */
/* copy over customdat */
CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i);
i++;
mvert++;
BM_CHECK_ELEMENT(bm, v);
}
bm->elem_index_dirty &= ~BM_VERT;
med = medge;
i = 0;
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
float *crease = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE);
float *bweight = CustomData_bmesh_get(&bm->edata, e->head.data, CD_BWEIGHT);
med->v1 = BM_elem_index_get(e->v1);
med->v2 = BM_elem_index_get(e->v2);
med->crease = crease ? (char)((*crease) * 255) : 0;
med->bweight = bweight ? (char)((*bweight) * 255) : 0;
med->flag = BM_edge_flag_to_mflag(e);
BM_elem_index_set(e, i); /* set_inline */
/* copy over customdat */
CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i);
bmesh_quick_edgedraw_flag(med, e);
i++;
med++;
BM_CHECK_ELEMENT(bm, e);
}
bm->elem_index_dirty &= ~BM_EDGE;
i = 0;
j = 0;
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
mpoly->loopstart = j;
mpoly->totloop = f->len;
mpoly->mat_nr = f->mat_nr;
mpoly->flag = BM_face_flag_to_mflag(f);
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
for ( ; l; l = BM_iter_step(&liter), j++, mloop++) {
mloop->e = BM_elem_index_get(l->e);
mloop->v = BM_elem_index_get(l->v);
/* copy over customdat */
CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l->head.data, j);
BM_CHECK_ELEMENT(bm, l);
BM_CHECK_ELEMENT(bm, l->e);
BM_CHECK_ELEMENT(bm, l->v);
}
if (f == bm->act_face) me->act_face = i;
/* copy over customdat */
CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i);
i++;
mpoly++;
BM_CHECK_ELEMENT(bm, f);
}
/* patch hook indices and vertex parents */
if (ototvert > 0) {
Object *ob;
ModifierData *md;
BMVert **vertMap = NULL;
int i, j;
for (ob = G.main->object.first; ob; ob = ob->id.next) {
if (ob->parent == bm->ob && ELEM(ob->partype, PARVERT1, PARVERT3)) {
if (vertMap == NULL) {
vertMap = bmesh_to_mesh_vertex_map(bm, ototvert);
}
if (ob->par1 < ototvert) {
eve = vertMap[ob->par1];
if (eve) ob->par1 = BM_elem_index_get(eve);
}
if (ob->par2 < ototvert) {
eve = vertMap[ob->par2];
if (eve) ob->par2 = BM_elem_index_get(eve);
}
if (ob->par3 < ototvert) {
eve = vertMap[ob->par3];
if (eve) ob->par3 = BM_elem_index_get(eve);
}
}
if (ob->data == me) {
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *) md;
if (vertMap == NULL) {
vertMap = bmesh_to_mesh_vertex_map(bm, ototvert);
}
for (i = j = 0; i < hmd->totindex; i++) {
if (hmd->indexar[i] < ototvert) {
eve = vertMap[hmd->indexar[i]];
if (eve) {
hmd->indexar[j++] = BM_elem_index_get(eve);
}
}
else j++;
}
hmd->totindex = j;
}
}
}
}
if (vertMap) MEM_freeN(vertMap);
}
if (dotess) {
BKE_mesh_tessface_calc(me);
}
mesh_update_customdata_pointers(me, dotess);
{
BMEditSelection *selected;
me->totselect = BLI_countlist(&(bm->selected));
if (me->mselect) MEM_freeN(me->mselect);
me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
if (selected->htype == BM_VERT) {
me->mselect[i].type = ME_VSEL;
}
else if (selected->htype == BM_EDGE) {
me->mselect[i].type = ME_ESEL;
}
else if (selected->htype == BM_FACE) {
me->mselect[i].type = ME_FSEL;
}
me->mselect[i].index = BM_elem_index_get(selected->data);
}
}
/* see comment below, this logic is in twice */
if (me->key) {
KeyBlock *currkey;
KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
float (*ofs)[3] = NULL;
/* go through and find any shapekey customdata layers
* that might not have corrusponding KeyBlocks, and add them if
* necassary */
j = 0;
for (i = 0; i < bm->vdata.totlayer; i++) {
if (bm->vdata.layers[i].type != CD_SHAPEKEY)
continue;
for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
if (currkey->uid == bm->vdata.layers[i].uid)
break;
}
if (!currkey) {
currkey = MEM_callocN(sizeof(KeyBlock), "KeyBlock mesh_conv.c");
currkey->type = KEY_LINEAR;
currkey->slidermin = 0.0f;
currkey->slidermax = 1.0f;
BLI_addtail(&me->key->block, currkey);
me->key->totkey++;
}
j++;
}
/* editing the base key should update others */
if (me->key->type == KEY_RELATIVE && oldverts) {
int act_is_basis = 0;
/* find if this key is a basis for any others */
for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
if (bm->shapenr - 1 == currkey->relative) {
act_is_basis = 1;
break;
}
}
if (act_is_basis) { /* active key is a base */
float (*fp)[3] = actkey->data;
int *keyi;
i = 0;
ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
mvert = me->mvert;
BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
if (keyi && *keyi != ORIGINDEX_NONE) {
sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]);
}
i++;
mvert++;
}
}
}
for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
j = 0;
for (i = 0; i < bm->vdata.totlayer; i++) {
if (bm->vdata.layers[i].type != CD_SHAPEKEY)
continue;
if (currkey->uid == bm->vdata.layers[i].uid) {
int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
float *fp, *co;
float (*ofs_pt)[3] = ofs;
if (currkey->data)
MEM_freeN(currkey->data);
currkey->data = fp = MEM_mallocN(sizeof(float) * 3 * bm->totvert, "shape key data");
currkey->totelem = bm->totvert;
BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
co = (currkey == actkey) ?
eve->co :
CustomData_bmesh_get_n(&bm->vdata, eve->head.data, CD_SHAPEKEY, j);
copy_v3_v3(fp, co);
/* propagate edited basis offsets to other shapes */
if (apply_offset) {
add_v3_v3(fp, *ofs_pt++);
}
fp += 3;
}
break;
}
j++;
}
/* if we didn't find a shapekey, tag the block to be reconstructed
* via the old method below */
if (j == CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)) {
currkey->flag |= KEYBLOCK_MISSING;
}
}
if (ofs) MEM_freeN(ofs);
}
/* XXX, code below is from trunk and a duplicate functionality
* to the block above.
* We should use one or the other, having both means we have to maintain
* both and keep them working the same way which is a hassle - campbell */
/* old method of reconstructing keys via vertice's original key indices,
* currently used if the new method above fails (which is theoretically
* possible in certain cases of undo) */
if (me->key) {
float *fp, *newkey, *oldkey;
KeyBlock *currkey;
KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
float (*ofs)[3] = NULL;
/* editing the base key should update others */
if (me->key->type == KEY_RELATIVE && oldverts) {
int act_is_basis = 0;
/* find if this key is a basis for any others */
for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
if (bm->shapenr - 1 == currkey->relative) {
act_is_basis = 1;
break;
}
}
if (act_is_basis) { /* active key is a base */
float (*fp)[3] = actkey->data;
int *keyi;
i = 0;
ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
mvert = me->mvert;
BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
if (keyi && *keyi != ORIGINDEX_NONE) {
sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]);
}
i++;
mvert++;
}
}
}
/* Lets reorder the key data so that things line up roughly
* with the way things were before editmode */
currkey = me->key->block.first;
while (currkey) {
int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
if (!(currkey->flag & KEYBLOCK_MISSING)) {
currkey = currkey->next;
continue;
}
printf("warning: had to hackishly reconstruct shape key \"%s\","
" it may not be correct anymore.\n", currkey->name);
currkey->flag &= ~KEYBLOCK_MISSING;
fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
oldkey = currkey->data;
eve = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
i = 0;
mvert = me->mvert;
while (eve) {
keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
if (!keyi) {
break;
}
if (*keyi >= 0 && *keyi < currkey->totelem) { // valid old vertex
if (currkey == actkey) {
if (actkey == me->key->refkey) {
copy_v3_v3(fp, mvert->co);
}
else {
copy_v3_v3(fp, mvert->co);
if (oldverts) {
copy_v3_v3(mvert->co, oldverts[*keyi].co);
}
}
}
else {
if (oldkey) {
copy_v3_v3(fp, oldkey + 3 * *keyi);
}
}
}
else {
copy_v3_v3(fp, mvert->co);
}
/* propagate edited basis offsets to other shapes */
if (apply_offset) {
add_v3_v3(fp, ofs[i]);
}
fp+= 3;
++i;
++mvert;
eve = BM_iter_step(&iter);
}
currkey->totelem = bm->totvert;
if (currkey->data) MEM_freeN(currkey->data);
currkey->data = newkey;
currkey = currkey->next;
}
if (ofs) MEM_freeN(ofs);
}
if (oldverts) MEM_freeN(oldverts);
}
+126
View File
@@ -0,0 +1,126 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "DNA_meshdata_types.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "BKE_customdata.h"
#include "bmesh.h"
#include "bmesh_operators_private.h" /* own include */
#define ELE_NEW 1
void bmesh_mirror_exec(BMesh *bm, BMOperator *op)
{
BMOperator dupeop, weldop;
BMOIter siter;
BMIter iter;
BMVert *v, *v2, **vmap = NULL;
BLI_array_declare(vmap);
BMEdge /* *e, */ **emap = NULL;
BLI_array_declare(emap);
float mtx[4][4];
float imtx[4][4];
float scale[3] = {1.0f, 1.0f, 1.0f};
float dist = BMO_slot_float_get(op, "mergedist");
int i, ototvert, ototedge, axis = BMO_slot_int_get(op, "axis");
int mirroru = BMO_slot_int_get(op, "mirror_u");
int mirrorv = BMO_slot_int_get(op, "mirror_v");
ototvert = bm->totvert;
ototedge = bm->totedge;
BMO_slot_mat4_get(op, "mat", mtx);
invert_m4_m4(imtx, mtx);
BMO_op_initf(bm, &dupeop, "dupe geom=%s", op, "geom");
BMO_op_exec(bm, &dupeop);
BMO_slot_buffer_flag_enable(bm, &dupeop, "newout", ELE_NEW, BM_ALL);
/* create old -> new mappin */
i = 0;
v2 = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
BMO_ITER(v, &siter, bm, &dupeop, "newout", BM_VERT) {
BLI_array_growone(vmap);
vmap[i] = v;
/* BMESH_TODO, double check this is being used, calling following operators will overwrite anyway - campbell */
BM_elem_index_set(v2, i); /* set_dirty! */
v2 = BM_iter_step(&iter);
i++;
}
bm->elem_index_dirty |= BM_VERT;
/* feed old data to transform bmo */
scale[axis] = -1.0f;
BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, mtx);
BMO_op_callf(bm, "scale verts=%fv vec=%v", ELE_NEW, scale);
BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, imtx);
BMO_op_init(bm, &weldop, "weldverts");
v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
for (i = 0; i < ototvert; i++) {
if (ABS(v->co[axis]) <= dist) {
BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", vmap[i], v);
}
v = BM_iter_step(&iter);
}
if (mirroru || mirrorv) {
BMFace *f;
BMLoop *l;
MLoopUV *luv;
int totlayer;
BMIter liter;
BMO_ITER(f, &siter, bm, &dupeop, "newout", BM_FACE) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
totlayer = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV);
for (i = 0; i < totlayer; i++) {
luv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i);
if (mirroru)
luv->uv[0] = 1.0f - luv->uv[0];
if (mirrorv)
luv->uv[1] = 1.0f - luv->uv[1];
}
}
}
}
BMO_op_exec(bm, &weldop);
BMO_op_finish(bm, &weldop);
BMO_op_finish(bm, &dupeop);
BMO_slot_from_flag(bm, op, "newout", ELE_NEW, BM_ALL);
BLI_array_free(vmap);
BLI_array_free(emap);
}
@@ -0,0 +1,734 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "ED_mesh.h"
#include "bmesh.h"
#include "bmesh_private.h"
/* ************************ primitives ******************* */
static float icovert[12][3] = {
{0.0f,0.0f,-200.0f},
{144.72f, -105.144f,-89.443f},
{-55.277f, -170.128,-89.443f},
{-178.885f,0.0f,-89.443f},
{-55.277f,170.128f,-89.443f},
{144.72f,105.144f,-89.443f},
{55.277f,-170.128f,89.443f},
{-144.72f,-105.144f,89.443f},
{-144.72f,105.144f,89.443f},
{55.277f,170.128f,89.443f},
{178.885f,0.0f,89.443f},
{0.0f,0.0f,200.0f}
};
static short icoface[20][3] = {
{0,1,2},
{1,0,5},
{0,2,3},
{0,3,4},
{0,4,5},
{1,5,10},
{2,1,6},
{3,2,7},
{4,3,8},
{5,4,9},
{1,10,6},
{2,6,7},
{3,7,8},
{4,8,9},
{5,9,10},
{6,10,11},
{7,6,11},
{8,7,11},
{9,8,11},
{10,9,11}
};
// HACK: these can also be found in cmoview.tga.c, but are here so that they can be found by linker
// this hack is only used so that scons & mingw + split-sources hack works
// ------------------------------- start copied code
/* these are not the monkeys you are looking for */
static int monkeyo = 4;
static int monkeynv = 271;
static int monkeynf = 250;
static signed char monkeyv[271][3] = {
{-71,21,98},{-63,12,88},{-57,7,74},{-82,-3,79},{-82,4,92},
{-82,17,100},{-92,21,102},{-101,12,95},{-107,7,83},
{-117,31,84},{-109,31,95},{-96,31,102},{-92,42,102},
{-101,50,95},{-107,56,83},{-82,66,79},{-82,58,92},
{-82,46,100},{-71,42,98},{-63,50,88},{-57,56,74},
{-47,31,72},{-55,31,86},{-67,31,97},{-66,31,99},
{-70,43,100},{-82,48,103},{-93,43,105},{-98,31,105},
{-93,20,105},{-82,31,106},{-82,15,103},{-70,20,100},
{-127,55,95},{-127,45,105},{-127,-87,94},{-127,-41,100},
{-127,-24,102},{-127,-99,92},{-127,52,77},{-127,73,73},
{-127,115,-70},{-127,72,-109},{-127,9,-106},{-127,-49,-45},
{-101,-24,72},{-87,-56,73},{-82,-89,73},{-80,-114,68},
{-85,-121,67},{-104,-124,71},{-127,-126,74},{-71,-18,68},
{-46,-5,69},{-21,19,57},{-17,55,76},{-36,62,80},
{-64,77,88},{-86,97,94},{-107,92,97},{-119,63,96},
{-106,53,99},{-111,39,98},{-101,12,95},{-79,2,90},
{-64,8,86},{-47,24,83},{-45,38,83},{-50,48,85},
{-72,56,92},{-95,60,97},{-127,-98,94},{-113,-92,94},
{-112,-107,91},{-119,-113,89},{-127,-114,88},{-127,-25,96},
{-127,-18,95},{-114,-19,95},{-111,-29,96},{-116,-37,95},
{-76,-6,86},{-48,7,80},{-34,26,77},{-32,48,84},
{-39,53,93},{-71,70,102},{-87,82,107},{-101,79,109},
{-114,55,108},{-111,-13,104},{-100,-57,91},{-95,-90,88},
{-93,-105,85},{-97,-117,81},{-106,-119,81},{-127,-121,82},
{-127,6,93},{-127,27,98},{-85,61,95},{-106,18,96},
{-110,27,97},{-112,-88,94},{-117,-57,96},{-127,-57,96},
{-127,-42,95},{-115,-35,100},{-110,-29,102},{-113,-17,100},
{-122,-16,100},{-127,-26,106},{-121,-19,104},{-115,-20,104},
{-113,-29,106},{-117,-32,103},{-127,-37,103},{-94,-40,71},
{-106,-31,91},{-104,-40,91},{-97,-32,71},{-127,-112,88},
{-121,-111,88},{-115,-105,91},{-115,-95,93},{-127,-100,84},
{-115,-96,85},{-115,-104,82},{-121,-109,81},{-127,-110,81},
{-105,28,100},{-103,20,99},{-84,55,97},{-92,54,99},
{-73,51,99},{-55,45,89},{-52,37,88},{-53,25,87},
{-66,13,92},{-79,8,95},{-98,14,100},{-104,38,100},
{-100,48,100},{-97,46,97},{-102,38,97},{-96,16,97},
{-79,11,93},{-68,15,90},{-57,27,86},{-56,36,86},
{-59,43,87},{-74,50,96},{-91,51,98},{-84,52,96},
{-101,22,96},{-102,29,96},{-113,59,78},{-102,85,79},
{-84,88,76},{-65,71,71},{-40,58,63},{-25,52,59},
{-28,21,48},{-50,0,53},{-71,-12,60},{-127,115,37},
{-127,126,-10},{-127,-25,-86},{-127,-59,24},{-127,-125,59},
{-127,-103,44},{-127,-73,41},{-127,-62,36},{-18,30,7},
{-17,41,-6},{-28,34,-56},{-68,56,-90},{-33,-6,9},
{-51,-16,-21},{-45,-1,-55},{-84,7,-85},{-97,-45,52},
{-104,-53,33},{-90,-91,49},{-95,-64,50},{-85,-117,51},
{-109,-97,47},{-111,-69,46},{-106,-121,56},{-99,-36,55},
{-100,-29,60},{-101,-22,64},{-100,-50,21},{-89,-40,-34},
{-83,-19,-69},{-69,111,-49},{-69,119,-9},{-69,109,30},
{-68,67,55},{-34,52,43},{-46,58,36},{-45,90,7},
{-25,72,16},{-25,79,-15},{-45,96,-25},{-45,87,-57},
{-25,69,-46},{-48,42,-75},{-65,3,-70},{-22,42,-26},
{-75,-22,19},{-72,-25,-27},{-13,52,-30},{-28,-18,-16},
{6,-13,-42},{37,7,-55},{46,41,-54},{31,65,-54},
{4,61,-40},{3,53,-37},{25,56,-50},{35,37,-52},
{28,10,-52},{5,-5,-39},{-21,-9,-17},{-9,46,-28},
{-6,39,-37},{-14,-3,-27},{6,0,-47},{25,12,-57},
{31,32,-57},{23,46,-56},{4,44,-46},{-19,37,-27},
{-20,22,-35},{-30,12,-35},{-22,11,-35},{-19,2,-35},
{-23,-2,-35},{-34,0,-9},{-35,-3,-22},{-35,5,-24},
{-25,26,-27},{-13,31,-34},{-13,30,-41},{-23,-2,-41},
{-18,2,-41},{-21,10,-41},{-29,12,-41},{-19,22,-41},
{6,42,-53},{25,44,-62},{34,31,-63},{28,11,-62},
{7,0,-54},{-14,-2,-34},{-5,37,-44},{-13,14,-42},
{-7,8,-43},{1,16,-47},{-4,22,-45},{3,30,-48},
{8,24,-49},{15,27,-50},{12,35,-50},{4,56,-62},
{33,60,-70},{48,38,-64},{41,7,-68},{6,-11,-63},
{-26,-16,-42},{-17,49,-49},
};
static signed char monkeyf[250][4] = {
{27,4,5,26}, {25,4,5,24}, {3,6,5,4}, {1,6,5,2}, {5,6,7,4},
{3,6,7,2}, {5,8,7,6}, {3,8,7,4}, {7,8,9,6},
{5,8,9,4}, {7,10,9,8}, {5,10,9,6}, {9,10,11,8},
{7,10,11,6}, {9,12,11,10}, {7,12,11,8}, {11,6,13,12},
{5,4,13,12}, {3,-2,13,12}, {-3,-4,13,12}, {-5,-10,13,12},
{-11,-12,14,12}, {-13,-18,14,13}, {-19,4,5,13}, {10,12,4,4},
{10,11,9,9}, {8,7,9,9}, {7,5,6,6}, {6,3,4,4},
{5,1,2,2}, {4,-1,0,0}, {3,-3,-2,-2}, {22,67,68,23},
{20,65,66,21}, {18,63,64,19}, {16,61,62,17}, {14,59,60,15},
{12,19,48,57}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47},
{18,19,48,47}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47},
{18,19,48,47}, {18,-9,-8,47}, {18,27,45,46}, {26,55,43,44},
{24,41,42,54}, {22,39,40,23}, {20,37,38,21}, {18,35,36,19},
{16,33,34,17}, {14,31,32,15}, {12,39,30,13}, {11,48,45,38},
{8,36,-19,9}, {8,-20,44,47}, {42,45,46,43}, {18,19,40,39},
{16,17,38,37}, {14,15,36,35}, {32,44,43,33}, {12,33,32,42},
{19,44,43,42}, {40,41,42,-27}, {8,9,39,-28}, {15,43,42,16},
{13,43,42,14}, {11,43,42,12}, {9,-30,42,10}, {37,12,38,-32},
{-33,37,45,46}, {-33,40,41,39}, {38,40,41,37}, {36,40,41,35},
{34,40,41,33}, {36,39,38,37}, {35,40,39,38}, {1,2,14,21},
{1,2,40,13}, {1,2,40,39}, {1,24,12,39}, {-34,36,38,11},
{35,38,36,37}, {-37,8,35,37}, {-11,-12,-45,40}, {-11,-12,39,38},
{-11,-12,37,36}, {-11,-12,35,34}, {33,34,40,41}, {33,34,38,39},
{33,34,36,37}, {33,-52,34,35}, {33,37,36,34}, {33,35,34,34},
{8,7,37,36}, {-32,7,35,46}, {-34,-33,45,46}, {4,-33,43,34},
{-34,-33,41,42}, {-34,-33,39,40}, {-34,-33,37,38}, {-34,-33,35,36},
{-34,-33,33,34}, {-34,-33,31,32}, {-34,-4,28,30}, {-5,-34,28,27},
{-35,-44,36,27}, {26,35,36,45}, {24,25,44,45}, {25,23,44,42},
{25,24,41,40}, {25,24,39,38}, {25,24,37,36}, {25,24,35,34},
{25,24,33,32}, {25,24,31,30}, {15,24,29,38}, {25,24,27,26},
{23,12,37,26}, {11,12,35,36}, {-86,-59,36,-80}, {-60,-61,36,35},
{-62,-63,36,35}, {-64,-65,36,35}, {-66,-67,36,35}, {-68,-69,36,35},
{-70,-71,36,35}, {-72,-73,36,35}, {-74,-75,36,35}, {42,43,53,58},
{40,41,57,56}, {38,39,55,57}, {-81,-80,37,56}, {-83,-82,55,52},
{-85,-84,51,49}, {-87,-86,48,49}, {47,50,51,48}, {46,48,51,49},
{43,46,49,44}, {-92,-91,45,42}, {-23,49,50,-20}, {-94,40,48,-24},
{-96,-22,48,49}, {-97,48,21,-90}, {-100,36,50,23}, {22,49,48,-100},
{-101,47,46,22}, {21,45,35,25}, {33,34,44,41}, {13,14,28,24},
{-107,26,30,-106}, {14,46,45,15}, {14,44,43,-110}, {-111,42,23,-110},
{6,7,45,46}, {45,44,47,46}, {45,46,47,48}, {47,46,49,48},
{17,49,47,48}, {17,36,46,48}, {35,36,44,45}, {35,36,40,43},
{35,36,38,39}, {-4,-3,37,35}, {-123,34,33,1}, {-9,-8,-7,-6},
{-10,-7,32,-125}, {-127,-11,-126,-126}, {-7,-6,5,31}, {4,5,33,30},
{4,39,33,32}, {4,35,32,38}, {20,21,39,38}, {4,37,38,5},
{-11,-10,36,3}, {-11,15,14,35}, {13,16,34,34}, {-13,14,13,13},
{-3,1,30,29}, {-3,28,29,1}, {-2,31,28,-1}, {12,13,27,30},
{-2,26,12,12}, {35,29,42,36}, {34,35,36,33}, {32,35,36,31},
{30,35,36,29}, {28,35,36,27}, {26,35,36,25}, {34,39,38,35},
{32,39,38,33}, {30,39,38,31}, {28,39,38,29}, {26,39,38,27},
{25,31,32,38}, {-18,-17,45,44}, {-18,17,28,44}, {-24,-20,42,-23},
{11,35,27,14}, {25,28,39,41}, {37,41,40,38}, {34,40,36,35},
{32,40,39,33}, {30,39,31,40}, {21,29,39,22}, {-31,37,28,4},
{-32,33,35,36}, {32,33,34,34}, {18,35,36,48}, {34,25,40,35},
{24,25,38,39}, {24,25,36,37}, {24,25,34,35}, {24,25,32,33},
{24,13,41,31}, {17,11,41,35}, {15,16,34,35}, {13,14,34,35},
{11,12,34,35}, {9,10,34,35}, {7,8,34,35}, {26,25,37,36},
{35,36,37,38}, {37,36,39,38}, {37,38,39,40}, {25,31,36,39},
{18,34,35,30}, {17,22,30,33}, {19,29,21,20}, {16,26,29,17},
{24,29,28,25}, {22,31,28,23}, {20,31,30,21}, {18,31,30,19},
{16,30,17,17}, {-21,-22,35,34}, {-21,-22,33,32}, {-21,-22,31,30},
{-21,-22,29,28}, {-21,-22,27,26}, {-28,-22,25,31}, {24,28,29,30},
{23,24,26,27}, {23,24,25,25}, {-69,-35,-32,27}, {-70,26,25,-66},
{-68,-67,24,-33},
};
#define VERT_MARK 1
#define EDGE_ORIG 1
#define EDGE_MARK 2
#define FACE_MARK 1
#define FACE_NEW 2
void bmesh_create_grid_exec(BMesh *bm, BMOperator *op)
{
BMOperator bmop, prevop;
BMVert *eve, *preveve;
BMEdge *e;
float vec[3], mat[4][4], phi, phid, dia = BMO_slot_float_get(op, "size");
int a, tot = BMO_slot_int_get(op, "xsegments"), seg = BMO_slot_int_get(op, "ysegments");
if (tot < 2) tot = 2;
if (seg < 2) seg = 2;
BMO_slot_mat4_get(op, "mat", mat);
/* one segment first: the X axis */
phi = 1.0f;
phid = 2.0f / ((float)tot - 1);
for (a = 0; a < tot; a++) {
vec[0] = dia * phi;
vec[1] = -dia;
vec[2] = 0.0f;
mul_m4_v3(mat, vec);
eve = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, eve, VERT_MARK);
if (a) {
e = BM_edge_create(bm, preveve, eve, NULL, TRUE);
BMO_elem_flag_enable(bm, e, EDGE_ORIG);
}
preveve = eve;
phi -= phid;
}
/* extrude and translate */
vec[0] = vec[2] = 0.0f;
vec[1] = dia * phid;
mul_mat3_m4_v3(mat, vec);
for (a = 0; a < seg - 1; a++) {
if (a) {
BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout");
BMO_op_exec(bm, &bmop);
BMO_op_finish(bm, &prevop);
BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
}
else {
BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG);
BMO_op_exec(bm, &bmop);
BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
}
BMO_op_callf(bm, "translate vec=%v verts=%s", vec, &bmop, "geomout");
prevop = bmop;
}
if (a)
BMO_op_finish(bm, &bmop);
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op)
{
BMOperator bmop, prevop;
BMVert *eve, *preveve;
BMEdge *e;
BMIter iter;
float vec[3], mat[4][4], cmat[3][3], phi, q[4];
float phid, dia = BMO_slot_float_get(op, "diameter");
int a, seg = BMO_slot_int_get(op, "segments"), tot = BMO_slot_int_get(op, "revolutions");
BMO_slot_mat4_get(op, "mat", mat);
phid = 2.0f * (float)M_PI / tot;
phi = 0.25f * (float)M_PI;
/* one segment first */
phi = 0;
phid /= 2;
for (a = 0; a <= tot; a++) {
/* Going in this direction, then edge extruding, makes normals face outward */
vec[0] = -dia * sinf(phi);
vec[1] = 0.0;
vec[2] = dia * cosf(phi);
eve = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, eve, VERT_MARK);
if (a != 0) {
e = BM_edge_create(bm, preveve, eve, NULL, FALSE);
BMO_elem_flag_enable(bm, e, EDGE_ORIG);
}
phi+= phid;
preveve = eve;
}
/* extrude and rotate; negative phi to make normals face outward */
phi = -M_PI / seg;
q[0] = cosf(phi);
q[3] = sinf(phi);
q[1] = q[2] = 0.0f;
quat_to_mat3(cmat, q);
for (a = 0; a < seg; a++) {
if (a) {
BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout");
BMO_op_exec(bm, &bmop);
BMO_op_finish(bm, &prevop);
}
else {
BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG);
BMO_op_exec(bm, &bmop);
}
BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", vec, cmat, &bmop, "geomout");
prevop = bmop;
}
if (a)
BMO_op_finish(bm, &bmop);
{
float len, len2, vec2[3];
len= 2*dia*sinf(phid / 2.0f);
/* length of one segment in shortest parallen */
vec[0]= dia*sinf(phid);
vec[1]= 0.0;
vec[2]= dia*cosf(phid);
mul_v3_m3v3(vec2, cmat, vec);
len2= len_v3v3(vec, vec2);
/* use shortest segment length divided by 3 as merge threshold */
BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, MIN2(len, len2) / 3.0f);
}
/* and now do imat */
BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, eve, VERT_MARK)) {
mul_m4_v3(mat, eve->co);
}
}
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op)
{
BMVert *eva[12];
BMVert *v;
BMIter liter;
BMIter viter;
BMLoop *l;
float vec[3], mat[4][4] /* , phi, phid */;
float dia = BMO_slot_float_get(op, "diameter");
int a, subdiv = BMO_slot_int_get(op, "subdivisions");
BMO_slot_mat4_get(op, "mat", mat);
/* phid = 2.0f * (float)M_PI / subdiv; */ /* UNUSED */
/* phi = 0.25f * (float)M_PI; */ /* UNUSED */
dia /= 200.0f;
for (a = 0; a < 12; a++) {
vec[0] = dia * icovert[a][0];
vec[1] = dia * icovert[a][1];
vec[2] = dia * icovert[a][2];
eva[a] = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, eva[a], VERT_MARK);
}
for (a = 0; a < 20; a++) {
BMFace *eftemp;
BMVert *v1, *v2, *v3;
v1 = eva[icoface[a][0]];
v2 = eva[icoface[a][1]];
v3 = eva[icoface[a][2]];
eftemp = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, NULL, FALSE);
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, eftemp) {
BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
}
BMO_elem_flag_enable(bm, eftemp, FACE_MARK);
}
dia *= 200.0f;
for (a = 1; a < subdiv; a++) {
BMOperator bmop;
BMO_op_initf(bm, &bmop,
"esubd edges=%fe smooth=%f numcuts=%i gridfill=%i beauty=%i",
EDGE_MARK, dia, 1, 1, B_SPHERE);
BMO_op_exec(bm, &bmop);
BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", EDGE_MARK, BM_EDGE);
BMO_op_finish(bm, &bmop);
}
/* must transform after becayse of sphere subdivision */
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
mul_m4_v3(mat, v->co);
}
}
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op)
{
BMVert *eve;
BMVert **tv = MEM_mallocN(sizeof(*tv)*monkeynv * 2, "tv");
float mat[4][4];
int i;
BMO_slot_mat4_get(op, "mat", mat);
for (i = 0; i < monkeynv; i++) {
float v[3];
v[0] = (monkeyv[i][0] + 127) / 128.0, v[1] = monkeyv[i][1] / 128.0, v[2] = monkeyv[i][2] / 128.0;
tv[i] = BM_vert_create(bm, v, NULL);
BMO_elem_flag_enable(bm, tv[i], VERT_MARK);
tv[monkeynv + i] = (fabsf(v[0] = -v[0]) < 0.001f) ?
tv[i] :
(eve = BM_vert_create(bm, v, NULL), mul_m4_v3(mat, eve->co), eve);
BMO_elem_flag_enable(bm, tv[monkeynv + i], VERT_MARK);
mul_m4_v3(mat, tv[i]->co);
}
for (i = 0; i < monkeynf; i++) {
BM_face_create_quad_tri(bm,
tv[monkeyf[i][0] + i - monkeyo],
tv[monkeyf[i][1] + i - monkeyo],
tv[monkeyf[i][2] + i - monkeyo],
(monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeyf[i][3] + i - monkeyo] : NULL,
NULL, FALSE);
BM_face_create_quad_tri(bm,
tv[monkeynv + monkeyf[i][2] + i - monkeyo],
tv[monkeynv + monkeyf[i][1] + i - monkeyo],
tv[monkeynv + monkeyf[i][0] + i - monkeyo],
(monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeynv + monkeyf[i][3] + i - monkeyo]: NULL,
NULL, FALSE);
}
MEM_freeN(tv);
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
void bmesh_create_circle_exec(BMesh *bm, BMOperator *op)
{
BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL;
float vec[3], mat[4][4], phi, phid;
float dia = BMO_slot_float_get(op, "diameter");
int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments");
int cap_tris = BMO_slot_int_get(op, "cap_tris");
int a;
if (!segs)
return;
BMO_slot_mat4_get(op, "mat", mat);
phid = 2.0f * (float)M_PI / segs;
phi = .25f * (float)M_PI;
if (cap_ends) {
vec[0] = vec[1] = 0.0f;
vec[2] = 0.0;
mul_m4_v3(mat, vec);
cent1 = BM_vert_create(bm, vec, NULL);
}
for (a = 0; a < segs; a++, phi += phid) {
/* Going this way ends up with normal(s) upward */
vec[0] = -dia * sinf(phi);
vec[1] = dia * cosf(phi);
vec[2] = 0.0f;
mul_m4_v3(mat, vec);
v1 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v1, VERT_MARK);
if (lastv1)
BM_edge_create(bm, v1, lastv1, NULL, FALSE);
if (a && cap_ends) {
BMFace *f;
f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE);
BMO_elem_flag_enable(bm, f, FACE_NEW);
}
if (!firstv1)
firstv1 = v1;
lastv1 = v1;
}
if (!a)
return;
BM_edge_create(bm, lastv1, firstv1, NULL, FALSE);
if (cap_ends) {
BMFace *f;
f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE);
BMO_elem_flag_enable(bm, f, FACE_NEW);
}
if (!cap_tris) {
BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW);
}
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
void bmesh_create_cone_exec(BMesh *bm, BMOperator *op)
{
BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2;
float vec[3], mat[4][4], phi, phid;
float dia1 = BMO_slot_float_get(op, "diameter1");
float dia2 = BMO_slot_float_get(op, "diameter2");
float depth = BMO_slot_float_get(op, "depth");
int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments");
int cap_tris = BMO_slot_int_get(op, "cap_tris");
int a;
if (!segs)
return;
BMO_slot_mat4_get(op, "mat", mat);
phid = 2.0f * (float)M_PI / segs;
phi = 0.25f * (float)M_PI;
depth *= 0.5f;
if (cap_ends) {
vec[0] = vec[1] = 0.0f;
vec[2] = -depth;
mul_m4_v3(mat, vec);
cent1 = BM_vert_create(bm, vec, NULL);
vec[0] = vec[1] = 0.0f;
vec[2] = depth;
mul_m4_v3(mat, vec);
cent2 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, cent1, VERT_MARK);
BMO_elem_flag_enable(bm, cent2, VERT_MARK);
}
for (a = 0; a < segs; a++, phi += phid) {
vec[0] = dia1 * sinf(phi);
vec[1] = dia1 * cosf(phi);
vec[2] = -depth;
mul_m4_v3(mat, vec);
v1 = BM_vert_create(bm, vec, NULL);
vec[0] = dia2 * sinf(phi);
vec[1] = dia2 * cosf(phi);
vec[2] = depth;
mul_m4_v3(mat, vec);
v2 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v1, VERT_MARK);
BMO_elem_flag_enable(bm, v2, VERT_MARK);
if (a) {
if (cap_ends) {
BMFace *f;
f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE);
BMO_elem_flag_enable(bm, f, FACE_NEW);
f = BM_face_create_quad_tri(bm, cent2, v2, lastv2, NULL, NULL, FALSE);
BMO_elem_flag_enable(bm, f, FACE_NEW);
}
BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, NULL, FALSE);
}
else {
firstv1 = v1;
firstv2 = v2;
}
lastv1 = v1;
lastv2 = v2;
}
if (!a)
return;
if (cap_ends) {
BMFace *f;
f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE);
BMO_elem_flag_enable(bm, f, FACE_NEW);
f = BM_face_create_quad_tri(bm, cent2, firstv2, v2, NULL, NULL, FALSE);
BMO_elem_flag_enable(bm, f, FACE_NEW);
}
if (!cap_tris) {
BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW);
}
BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, NULL, FALSE);
BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, 0.000001);
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
void bmesh_create_cube_exec(BMesh *bm, BMOperator *op)
{
BMVert *v1, *v2, *v3, *v4, *v5, *v6, *v7, *v8;
float vec[3], mat[4][4], off = BMO_slot_float_get(op, "size") / 2.0f;
BMO_slot_mat4_get(op, "mat", mat);
if (!off) off = 0.5f;
vec[0] = -off;
vec[1] = -off;
vec[2] = -off;
mul_m4_v3(mat, vec);
v1 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v1, VERT_MARK);
vec[0] = -off;
vec[1] = off;
vec[2] = -off;
mul_m4_v3(mat, vec);
v2 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v2, VERT_MARK);
vec[0] = off;
vec[1] = off;
vec[2] = -off;
mul_m4_v3(mat, vec);
v3 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v3, VERT_MARK);
vec[0] = off;
vec[1] = -off;
vec[2] = -off;
mul_m4_v3(mat, vec);
v4 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v4, VERT_MARK);
vec[0] = -off;
vec[1] = -off;
vec[2] = off;
mul_m4_v3(mat, vec);
v5 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v5, VERT_MARK);
vec[0] = -off;
vec[1] = off;
vec[2] = off;
mul_m4_v3(mat, vec);
v6 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v6, VERT_MARK);
vec[0] = off;
vec[1] = off;
vec[2] = off;
mul_m4_v3(mat, vec);
v7 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v7, VERT_MARK);
vec[0] = off;
vec[1] = -off;
vec[2] = off;
mul_m4_v3(mat, vec);
v8 = BM_vert_create(bm, vec, NULL);
BMO_elem_flag_enable(bm, v8, VERT_MARK);
/* the four sides */
BM_face_create_quad_tri(bm, v5, v6, v2, v1, NULL, FALSE);
BM_face_create_quad_tri(bm, v6, v7, v3, v2, NULL, FALSE);
BM_face_create_quad_tri(bm, v7, v8, v4, v3, NULL, FALSE);
BM_face_create_quad_tri(bm, v8, v5, v1, v4, NULL, FALSE);
/* top/bottom */
BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE);
BM_face_create_quad_tri(bm, v8, v7, v6, v5, NULL, FALSE);
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
@@ -0,0 +1,590 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "BKE_customdata.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "bmesh_operators_private.h" /* own include */
static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op)
{
BMIter liter;
BMLoop *l;
BMVert *v2, *doub;
int split = FALSE;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", l->v);
/* ok: if v2 is NULL (e.g. not in the map) then it's
* a target vert, otherwise it's a doubl */
if ((v2 && BM_vert_in_face(f, v2)) &&
(v2 != l->prev->v) &&
(v2 != l->next->v))
{
doub = l->v;
split = TRUE;
break;
}
}
if (split && doub != v2) {
BMLoop *nl;
BMFace *f2 = BM_face_split(bm, f, doub, v2, &nl, NULL);
remdoubles_splitface(f, bm, op);
remdoubles_splitface(f2, bm, op);
}
}
#define ELE_DEL 1
#define EDGE_COL 2
#define FACE_MARK 2
#if 0
int remdoubles_face_overlaps(BMesh *bm, BMVert **varr,
int len, BMFace *exclude,
BMFace **overlapface)
{
BMIter vertfaces;
BMFace *f;
int i, amount;
if (overlapface) *overlapface = NULL;
for (i = 0; i < len; i++) {
f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]);
while (f) {
amount = BM_verts_in_face(bm, f, varr, len);
if (amount >= len) {
if (overlapface) *overlapface = f;
return TRUE;
}
f = BM_iter_step(&vertfaces);
}
}
return FALSE;
}
#endif
void bmesh_weldverts_exec(BMesh *bm, BMOperator *op)
{
BMIter iter, liter;
BMVert *v, *v2;
BMEdge *e, *e2, **edges = NULL;
BLI_array_declare(edges);
BMLoop *l, *l2, **loops = NULL;
BLI_array_declare(loops);
BMFace *f, *f2;
int a, b;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if ((v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v))) {
BMO_elem_flag_enable(bm, v, ELE_DEL);
/* merge the vertex flags, else we get randomly selected/unselected verts */
BM_elem_flag_merge(v, v2);
}
}
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
remdoubles_splitface(f, bm, op);
}
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, e->v1, ELE_DEL) || BMO_elem_flag_test(bm, e->v2, ELE_DEL)) {
v = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v1);
v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v2);
if (!v) v = e->v1;
if (!v2) v2 = e->v2;
if (v == v2)
BMO_elem_flag_enable(bm, e, EDGE_COL);
else if (!BM_edge_exists(v, v2))
BM_edge_create(bm, v, v2, e, TRUE);
BMO_elem_flag_enable(bm, e, ELE_DEL);
}
}
/* BMESH_TODO, stop abusing face index here */
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
BM_elem_index_set(f, 0); /* set_dirty! */
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
if (BMO_elem_flag_test(bm, l->v, ELE_DEL)) {
BMO_elem_flag_enable(bm, f, FACE_MARK|ELE_DEL);
}
if (BMO_elem_flag_test(bm, l->e, EDGE_COL)) {
BM_elem_index_set(f, BM_elem_index_get(f) + 1); /* set_dirty! */
}
}
}
bm->elem_index_dirty |= BM_FACE;
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, f, FACE_MARK))
continue;
if (f->len - BM_elem_index_get(f) < 3) {
BMO_elem_flag_enable(bm, f, ELE_DEL);
continue;
}
BLI_array_empty(edges);
BLI_array_empty(loops);
a = 0;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
v = l->v;
v2 = l->next->v;
if (BMO_elem_flag_test(bm, v, ELE_DEL)) {
v = BMO_slot_map_ptr_get(bm, op, "targetmap", v);
}
if (BMO_elem_flag_test(bm, v2, ELE_DEL)) {
v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2);
}
e2 = v != v2 ? BM_edge_exists(v, v2) : NULL;
if (e2) {
for (b = 0; b < a; b++) {
if (edges[b] == e2) {
break;
}
}
if (b != a) {
continue;
}
BLI_array_growone(edges);
BLI_array_growone(loops);
edges[a] = e2;
loops[a] = l;
a++;
}
}
if (BLI_array_count(loops) < 3)
continue;
v = loops[0]->v;
v2 = loops[1]->v;
if (BMO_elem_flag_test(bm, v, ELE_DEL)) {
v = BMO_slot_map_ptr_get(bm, op, "targetmap", v);
}
if (BMO_elem_flag_test(bm, v2, ELE_DEL)) {
v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2);
}
f2 = BM_face_create_ngon(bm, v, v2, edges, a, TRUE);
if (f2 && (f2 != f)) {
BM_elem_attrs_copy(bm, bm, f, f2);
a = 0;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f2) {
l2 = loops[a];
BM_elem_attrs_copy(bm, bm, l2, l);
a++;
}
}
}
BMO_op_callf(bm, "del geom=%fvef context=%i", ELE_DEL, DEL_ONLYTAGGED);
BLI_array_free(edges);
BLI_array_free(loops);
}
static int vergaverco(const void *e1, const void *e2)
{
const BMVert *v1 = *(void **)e1, *v2 = *(void **)e2;
float x1 = v1->co[0] + v1->co[1] + v1->co[2];
float x2 = v2->co[0] + v2->co[1] + v2->co[2];
if (x1 > x2) return 1;
else if (x1 < x2) return -1;
else return 0;
}
#define VERT_TESTED 1
#define VERT_DOUBLE 2
#define VERT_TARGET 4
#define VERT_KEEP 8
#define VERT_MARK 16
#define VERT_IN 32
#define EDGE_MARK 1
void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMIter iter;
BMVert *v, *snapv;
BMLoop *l, *firstl = NULL;
float fac;
int i, tot;
snapv = BMO_iter_new(&siter, bm, op, "snapv", BM_VERT);
tot = BM_vert_face_count(snapv);
if (!tot)
return;
fac = 1.0f / tot;
BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, snapv) {
if (!firstl) {
firstl = l;
}
for (i = 0; i < bm->ldata.totlayer; i++) {
if (CustomData_layer_has_math(&bm->ldata, i)) {
int type = bm->ldata.layers[i].type;
void *e1, *e2;
e1 = CustomData_bmesh_get_layer_n(&bm->ldata, firstl->head.data, i);
e2 = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
CustomData_data_multiply(type, e2, fac);
if (l != firstl)
CustomData_data_add(type, e1, e2);
}
}
}
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
if (l == firstl) {
continue;
}
CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, firstl->head.data, &l->head.data);
}
}
}
void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMIter iter;
BMVert *v;
BMLoop *l /* , *firstl = NULL */;
CDBlockBytes min, max;
void *block;
int i, type;
for (i = 0; i < bm->ldata.totlayer; i++) {
if (!CustomData_layer_has_math(&bm->ldata, i))
continue;
type = bm->ldata.layers[i].type;
CustomData_data_initminmax(type, &min, &max);
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
CustomData_data_dominmax(type, block, &min, &max);
}
}
CustomData_data_multiply(type, &min, 0.5f);
CustomData_data_multiply(type, &max, 0.5f);
CustomData_data_add(type, &min, &max);
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
CustomData_data_copy_value(type, &min, block);
}
}
}
}
void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op)
{
BMOperator weldop;
BMOIter siter;
BMVert *v, *snapv = NULL;
float vec[3];
BMO_slot_vec_get(op, "mergeco", vec);
//BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges");
BMO_op_init(bm, &weldop, "weldverts");
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
if (!snapv) {
snapv = v;
copy_v3_v3(snapv->co, vec);
}
else {
BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", v, snapv);
}
}
BMO_op_exec(bm, &weldop);
BMO_op_finish(bm, &weldop);
}
void bmesh_collapse_exec(BMesh *bm, BMOperator *op)
{
BMOperator weldop;
BMWalker walker;
BMIter iter;
BMEdge *e, **edges = NULL;
BLI_array_declare(edges);
float min[3], max[3];
int i, tot;
BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges");
BMO_op_init(bm, &weldop, "weldverts");
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
BMW_init(&walker, bm, BMW_SHELL,
BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP,
BMW_NIL_LAY);
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
continue;
e = BMW_begin(&walker, e->v1);
BLI_array_empty(edges);
INIT_MINMAX(min, max);
for (tot = 0; e; tot++, e = BMW_step(&walker)) {
BLI_array_growone(edges);
edges[tot] = e;
DO_MINMAX(e->v1->co, min, max);
DO_MINMAX(e->v2->co, min, max);
}
add_v3_v3v3(min, min, max);
mul_v3_fl(min, 0.5f);
/* snap edges to a point. for initial testing purposes anyway */
for (i = 0; i < tot; i++) {
copy_v3_v3(edges[i]->v1->co, min);
copy_v3_v3(edges[i]->v2->co, min);
if (edges[i]->v1 != edges[0]->v1)
BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v1, edges[0]->v1);
if (edges[i]->v2 != edges[0]->v1)
BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v2, edges[0]->v1);
}
}
BMO_op_exec(bm, &weldop);
BMO_op_finish(bm, &weldop);
BMW_end(&walker);
BLI_array_free(edges);
}
/* uv collapse functio */
static void bmesh_collapsecon_do_layer(BMesh *bm, BMOperator *op, int layer)
{
BMIter iter, liter;
BMFace *f;
BMLoop *l, *l2;
BMWalker walker;
void **blocks = NULL;
BLI_array_declare(blocks);
CDBlockBytes min, max;
int i, tot, type = bm->ldata.layers[layer].type;
/* clear all short flags */
BMO_mesh_flag_disable_all(bm, op, BM_ALL, (1 << 16) - 1);
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
BMW_init(&walker, bm, BMW_LOOPDATA_ISLAND,
BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP,
layer);
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
if (BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
/* wal */
BLI_array_empty(blocks);
tot = 0;
l2 = BMW_begin(&walker, l);
CustomData_data_initminmax(type, &min, &max);
for (tot = 0; l2; tot++, l2 = BMW_step(&walker)) {
BLI_array_growone(blocks);
blocks[tot] = CustomData_bmesh_get_layer_n(&bm->ldata, l2->head.data, layer);
CustomData_data_dominmax(type, blocks[tot], &min, &max);
}
if (tot) {
CustomData_data_multiply(type, &min, 0.5f);
CustomData_data_multiply(type, &max, 0.5f);
CustomData_data_add(type, &min, &max);
/* snap CD (uv, vcol) points to their centroi */
for (i = 0; i < tot; i++) {
CustomData_data_copy_value(type, &min, blocks[i]);
}
}
}
}
}
BMW_end(&walker);
BLI_array_free(blocks);
}
void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op)
{
int i;
for (i = 0; i < bm->ldata.totlayer; i++) {
if (CustomData_layer_has_math(&bm->ldata, i))
bmesh_collapsecon_do_layer(bm, op, i);
}
}
void bmesh_finddoubles_common(BMesh *bm, BMOperator *op, BMOperator *optarget, const char *targetmapname)
{
BMOIter oiter;
BMVert *v, *v2;
BMVert **verts = NULL;
BLI_array_declare(verts);
float dist, dist3;
int i, j, len, keepvert = 0;
dist = BMO_slot_float_get(op, "dist");
dist3 = dist * 3.0f;
i = 0;
BMO_ITER(v, &oiter, bm, op, "verts", BM_VERT) {
BLI_array_growone(verts);
verts[i++] = v;
}
/* Test whether keepverts arg exists and is non-empty */
if (BMO_slot_exists(op, "keepverts")) {
keepvert = BMO_iter_new(&oiter, bm, op, "keepverts", BM_VERT) != NULL;
}
/* sort by vertex coordinates added togethe */
qsort(verts, BLI_array_count(verts), sizeof(void *), vergaverco);
/* Flag keepverts */
if (keepvert) {
BMO_slot_buffer_flag_enable(bm, op, "keepverts", VERT_KEEP, BM_VERT);
}
len = BLI_array_count(verts);
for (i = 0; i < len; i++) {
v = verts[i];
if (BMO_elem_flag_test(bm, v, VERT_DOUBLE)) continue;
for (j = i + 1; j < len; j++) {
v2 = verts[j];
/* Compare sort values of the verts using 3x tolerance (allowing for the tolerance
* on each of the three axes). This avoids the more expensive length comparison
* for most vertex pairs. */
if ((v2->co[0]+v2->co[1]+v2->co[2])-(v->co[0]+v->co[1]+v->co[2]) > dist3)
break;
if (keepvert) {
if (BMO_elem_flag_test(bm, v2, VERT_KEEP) == BMO_elem_flag_test(bm, v, VERT_KEEP))
continue;
}
if (compare_len_v3v3(v->co, v2->co, dist)) {
/* If one vert is marked as keep, make sure it will be the target */
if (BMO_elem_flag_test(bm, v2, VERT_KEEP)) {
SWAP(BMVert *, v, v2);
}
BMO_elem_flag_enable(bm, v2, VERT_DOUBLE);
BMO_elem_flag_enable(bm, v, VERT_TARGET);
BMO_slot_map_ptr_insert(bm, optarget, targetmapname, v2, v);
}
}
}
BLI_array_free(verts);
}
void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op)
{
BMOperator weldop;
BMO_op_init(bm, &weldop, "weldverts");
bmesh_finddoubles_common(bm, op, &weldop, "targetmap");
BMO_op_exec(bm, &weldop);
BMO_op_finish(bm, &weldop);
}
void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op)
{
bmesh_finddoubles_common(bm, op, op, "targetmapout");
}
void bmesh_automerge_exec(BMesh *bm, BMOperator *op)
{
BMOperator findop, weldop;
BMIter viter;
BMVert *v;
/* The "verts" input sent to this op is the set of verts that
* can be merged away into any other verts. Mark all other verts
* as VERT_KEEP. */
BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_IN, BM_VERT);
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
if (!BMO_elem_flag_test(bm, v, VERT_IN)) {
BMO_elem_flag_enable(bm, v, VERT_KEEP);
}
}
/* Search for doubles among all vertices, but only merge non-VERT_KEEP
* vertices into VERT_KEEP vertices. */
BMO_op_initf(bm, &findop, "finddoubles verts=%av keepverts=%fv", VERT_KEEP);
BMO_slot_copy(op, &findop, "dist", "dist");
BMO_op_exec(bm, &findop);
/* weld the vertices */
BMO_op_init(bm, &weldop, "weldverts");
BMO_slot_copy(&findop, &weldop, "targetmapout", "targetmap");
BMO_op_exec(bm, &weldop);
BMO_op_finish(bm, &findop);
BMO_op_finish(bm, &weldop);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,66 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BMO_SUBDIVIDE_H__
#define __BMO_SUBDIVIDE_H__
/** \file blender/bmesh/operators/bmo_subdivide.h
* \ingroup bmesh
*/
typedef struct subdparams {
int numcuts;
float smooth;
float fractal;
int beauty;
int seed;
int origkey; /* shapekey holding displaced vertex coordinates for current geometry */
BMOperator *op;
float off[3];
} subdparams;
typedef void (*subd_pattern_fill_fp)(BMesh *bm, BMFace *face, BMVert **verts,
const subdparams *params);
/*
* note: this is a pattern-based edge subdivider.
* it tries to match a pattern to edge selections on faces,
* then executes functions to cut them.
*/
typedef struct SubDPattern {
int seledges[20]; /* selected edges mask, for splitting */
/* verts starts at the first new vert cut, not the first vert in the face */
subd_pattern_fill_fp connectexec;
int len; /* total number of verts, before any subdivision */
} 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?
*/
#endif /* __BMO_SUBDIVIDE_H__ */
@@ -0,0 +1,219 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Joseph Eagar.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_scanfill.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "BLI_editVert.h"
#include "BLI_smallhash.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "bmesh_operators_private.h" /* own include */
#define EDGE_NEW 1
#define FACE_NEW 1
#define ELE_NEW 1
#define FACE_MARK 2
#define EDGE_MARK 4
void triangulate_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMFace *face, **newfaces = NULL;
BLI_array_declare(newfaces);
float (*projectverts)[3] = NULL;
BLI_array_declare(projectverts);
int i, lastlen = 0 /* , count = 0 */;
face = BMO_iter_new(&siter, bm, op, "faces", BM_FACE);
for ( ; face; face = BMO_iter_step(&siter)) {
if (lastlen < face->len) {
BLI_array_empty(projectverts);
BLI_array_empty(newfaces);
for (lastlen = 0; lastlen < face->len; lastlen++) {
BLI_array_growone(projectverts);
BLI_array_growone(projectverts);
BLI_array_growone(projectverts);
BLI_array_growone(newfaces);
}
}
BM_face_triangulate(bm, face, projectverts, EDGE_NEW, FACE_NEW, newfaces);
BMO_slot_map_ptr_insert(bm, op, "facemap", face, face);
for (i = 0; newfaces[i]; i++) {
BMO_slot_map_ptr_insert(bm, op, "facemap",
newfaces[i], face);
}
}
BMO_slot_from_flag(bm, op, "edgeout", EDGE_NEW, BM_EDGE);
BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE);
BLI_array_free(projectverts);
BLI_array_free(newfaces);
}
void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMIter iter;
BMFace *f;
BMEdge *e;
int stop = 0;
BMO_slot_buffer_flag_enable(bm, op, "constrain_edges", EDGE_MARK, BM_EDGE);
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
if (f->len == 3)
BMO_elem_flag_enable(bm, f, FACE_MARK);
}
while (!stop) {
stop = 1;
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
BMVert *v1, *v2, *v3, *v4;
if (BM_edge_face_count(e) != 2 || BMO_elem_flag_test(bm, e, EDGE_MARK)) {
continue;
}
if (!BMO_elem_flag_test(bm, e->l->f, FACE_MARK) ||
!BMO_elem_flag_test(bm, e->l->radial_next->f, FACE_MARK))
{
continue;
}
v1 = e->l->prev->v;
v2 = e->l->v;
v3 = e->l->radial_next->prev->v;
v4 = e->l->next->v;
if (is_quad_convex_v3(v1->co, v2->co, v3->co, v4->co)) {
float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2;
/* testing rule:
* the area divided by the total edge lengths
*/
len1 = len_v3v3(v1->co, v2->co);
len2 = len_v3v3(v2->co, v3->co);
len3 = len_v3v3(v3->co, v4->co);
len4 = len_v3v3(v4->co, v1->co);
len5 = len_v3v3(v1->co, v3->co);
len6 = len_v3v3(v2->co, v4->co);
opp1 = area_tri_v3(v1->co, v2->co, v3->co);
opp2 = area_tri_v3(v1->co, v3->co, v4->co);
fac1 = opp1 / (len1 + len2 + len5) + opp2 / (len3 + len4 + len5);
opp1 = area_tri_v3(v2->co, v3->co, v4->co);
opp2 = area_tri_v3(v2->co, v4->co, v1->co);
fac2 = opp1 / (len2 + len3 + len6) + opp2 / (len4 + len1 + len6);
if (fac1 > fac2) {
e = BM_edge_rotate(bm, e, 0);
if (e) {
BMO_elem_flag_enable(bm, e, ELE_NEW);
BMO_elem_flag_enable(bm, e->l->f, FACE_MARK|ELE_NEW);
BMO_elem_flag_enable(bm, e->l->radial_next->f, FACE_MARK|ELE_NEW);
stop = 0;
}
}
}
}
}
BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE);
}
void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMEdge *e;
BMOperator bmop;
EditEdge *eed;
EditVert *eve, *v1, *v2;
EditFace *efa;
SmallHash hash;
BLI_smallhash_init(&hash);
BLI_begin_edgefill();
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v1)) {
eve = BLI_addfillvert(e->v1->co);
eve->tmp.p = e->v1;
BLI_smallhash_insert(&hash, (uintptr_t)e->v1, eve);
}
if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v2)) {
eve = BLI_addfillvert(e->v2->co);
eve->tmp.p = e->v2;
BLI_smallhash_insert(&hash, (uintptr_t)e->v2, eve);
}
v1 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v1);
v2 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v2);
eed = BLI_addfilledge(v1, v2);
eed->tmp.p = e;
}
BLI_edgefill(0);
for (efa = fillfacebase.first; efa; efa = efa->next) {
BMFace *f = BM_face_create_quad_tri(bm,
efa->v1->tmp.p, efa->v2->tmp.p, efa->v3->tmp.p, NULL,
NULL, TRUE);
BMLoop *l;
BMIter liter;
BMO_elem_flag_enable(bm, f, ELE_NEW);
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
if (!BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
BMO_elem_flag_enable(bm, l->e, ELE_NEW);
}
}
}
BLI_end_edgefill();
BLI_smallhash_release(&hash);
/* clean up fill */
BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff constrain_edges=%fe", ELE_NEW, EDGE_MARK);
BMO_op_exec(bm, &bmop);
BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", ELE_NEW, BM_FACE|BM_EDGE);
BMO_op_finish(bm, &bmop);
BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+322
View File
@@ -0,0 +1,322 @@
#if 0
/*
* 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_vert_create(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_vert_select_set(target_mesh, target_vertex, TRUE);
if(BM_Is_Hidden((BMHeader*)source_vertex)) BM_Mark_Hidden((BMHeader*)target_vertex, 1);
BMO_elem_flag_enable(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_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
/*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_edge_select_set(target_mesh, target_edge, TRUE);
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_elem_flag_enable(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->lbase->v);
target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
/*lookup edges*/
i = 0;
source_loop = source_face->lbase;
do{
edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
i++;
source_loop = source_loop->next;
}while(source_loop != source_face->lbase);
/*create new face*/
target_face = BM_face_create_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, TRUE);
if(BM_Is_Hidden((BMHeader*)source_face)) BM_Mark_Hidden((BMHeader*)target_face, 1);
/*mark the face for output*/
BMO_elem_flag_enable(target_mesh, (BMHeader*)target_face, DUPE_NEW);
/*copy per-loop custom data*/
source_loop = source_face->lbase;
target_loop = target_face->lbase;
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->lbase);
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 = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f = BM_iter_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 = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f= BM_iter_step(&faces)){
if(BMO_elem_flag_test(source, (BMHeader*)f, DUPE_INPUT)){
/*vertex pass*/
for(v = BM_iter_new(&verts, source, BM_VERT_OF_FACE, f, 0, NULL); v; v = BM_iter_step(&verts)){
if(!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE)){
copy_vertex(source,v, target, vhash);
BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE);
}
}
/*edge pass*/
for(e = BM_iter_new(&edges, source, BM_EDGE_OF_FACE, f, 0, NULL); e; e = BMeshIter_step(&edges)){
if(!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE)){
copy_edge(source, e, target, vhash, ehash);
BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
}
}
copy_face(source, f, target, edar, vhash, ehash);
BMO_elem_flag_enable(source, (BMHeader*)f, DUPE_DONE);
}
}
/*now we dupe all the edges*/
for(e = BM_iter_new(&edges, source, BM_EDGES, source, 0, NULL); e; e = BM_iter_step(&edges)){
if(BMO_elem_flag_test(source, (BMHeader*)e, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE))){
/*make sure that verts are copied*/
if(!BMO_elem_flag_test(source, (BMHeader*)e->v1, DUPE_DONE){
copy_vertex(source, e->v1, target, vhash);
BMO_elem_flag_enable(source, (BMHeader*)e->v1, DUPE_DONE);
}
if(!BMO_elem_flag_test(source, (BMHeader*)e->v2, DUPE_DONE){
copy_vertex(source, e->v2, target, vhash);
BMO_elem_flag_enable(source, (BMHeader*)e->v2, DUPE_DONE);
}
/*now copy the actual edge*/
copy_edge(source, e, target, vhash, ehash);
BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
}
}
/*finally dupe all loose vertices*/
for(v = BM_iter_new(&verts, source, BM_VERTS, source, 0, NULL); v; v = BM_iter_step(&verts)){
if(BMO_elem_flag_test(source, (BMHeader*)v, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE))){
copy_vertex(source, v, target, vhash);
BMO_elem_flag_enable(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_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_VINPUT, DUPE_INPUT);
BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_EINPUT, DUPE_INPUT);
BMO_slot_buffer_flag_enable(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_slot_from_flag(bm, dupeop, BMOP_DUPE_VNEW, DUPE_NEW, BMESH_VERT);
BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_ENEW, DUPE_NEW, BMESH_EDGE);
BMO_slot_from_flag(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_op_init(&dupeop, BMOP_DUPE);
BMO_op_init(&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_op_exec(&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_op_exec(&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_op_finish(&dupeop);
BMO_op_finish(&delop);
}
#endif
+310
View File
@@ -0,0 +1,310 @@
#if 0
/*
* 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->lbase->v);
target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
/*lookup edges*/
i = 0;
source_loop = source_face->lbase;
do{
edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
i++;
source_loop = source_loop->next;
}while(source_loop != source_face->lbase);
/*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->lbase;
target_loop = target_face->lbase;
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->lbase);
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);
}
#endif
+341
View File
@@ -0,0 +1,341 @@
#if
/*
* 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= fabsf(v2->co[0]-v->co[0]);
if(dist<=limit) {
dist= fabsf(v2->co[1]-v->co[1]);
if(dist<=limit) {
dist= fabsf(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*/
copy_v3_v3(min,v->co);
copy_v3_v3(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);
copy_v3_v3(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);
}
#endif