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

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}")

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 )

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__ */

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__ */

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__ */

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__ */

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__ */

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__ */

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__ */

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__ */

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

View File

@@ -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

View File

@@ -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__ */

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;
}

View File

@@ -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;
}

View File

@@ -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__ */

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;
}
}

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);
}

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

View File

@@ -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

View File

@@ -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__ */

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

View File

@@ -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__ */

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");
}

View File

@@ -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);

View File

@@ -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__ */

View File

@@ -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

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);
}

View File

@@ -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

View File

@@ -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);
}

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");
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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

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

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

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