copying bmesh dir on its own from bmesh branch

This commit is contained in:
Campbell Barton
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__ */

View File

@@ -0,0 +1,6384 @@
#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) 2004 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Johnny Matthews, Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
/*
editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise in mods.c
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "MEM_guardedalloc.h"
#include "BMF_Api.h"
#include "DNA_mesh_types.h"
#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_view3d_types.h"
#include "DNA_key_types.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_editVert.h"
#include "BLI_rand.h"
#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_heap.h"
#include "BKE_depsgraph.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_utildefines.h"
#include "BKE_bmesh.h"
#ifdef WITH_VERSE
#include "BKE_verse.h"
#endif
#include "BIF_cursors.h"
#include "BIF_editmesh.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "BIF_graphics.h"
#include "BIF_interface.h"
#include "BIF_mywindow.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_resources.h"
#include "BIF_toolbox.h"
#include "BIF_transform.h"
#include "transform.h"
#ifdef WITH_VERSE
#include "BIF_verse.h"
#endif
#include "BDR_drawobject.h"
#include "BDR_editobject.h"
#include "BSE_view.h"
#include "BSE_edit.h"
#include "blendef.h"
#include "multires.h"
#include "mydevice.h"
#include "editmesh.h"
#include "MTC_vectorops.h"
#include "PIL_time.h"
#include "BLO_sys_types.h" // for intptr_t support
/* local prototypes ---------------*/
void bevel_menu(void);
static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa);
/********* qsort routines *********/
typedef struct xvertsort {
float x;
EditVert *v1;
} xvertsort;
static int vergxco(const void *v1, const void *v2)
{
const xvertsort *x1=v1, *x2=v2;
if( x1->x > x2->x ) return 1;
else if( x1->x < x2->x) return -1;
return 0;
}
struct facesort {
uintptr_t x;
struct EditFace *efa;
};
static int vergface(const void *v1, const void *v2)
{
const struct facesort *x1=v1, *x2=v2;
if( x1->x > x2->x ) return 1;
else if( x1->x < x2->x) return -1;
return 0;
}
/* *********************************** */
void convert_to_triface(int direction)
{
EditMesh *em = G.editMesh;
EditFace *efa, *efan, *next;
float fac;
if(multires_test()) return;
efa= em->faces.last;
while(efa) {
next= efa->prev;
if(efa->v4) {
if(efa->f & SELECT) {
/* choose shortest diagonal for split */
fac= len_v3v3(efa->v1->co, efa->v3->co) - len_v3v3(efa->v2->co, efa->v4->co);
/* this makes sure exact squares get split different in both cases */
if( (direction==0 && fac<FLT_EPSILON) || (direction && fac>0.0f) ) {
efan= EM_face_from_faces(efa, NULL, 0, 1, 2, -1);
if(efa->f & SELECT) EM_select_face(efan, 1);
efan= EM_face_from_faces(efa, NULL, 0, 2, 3, -1);
if(efa->f & SELECT) EM_select_face(efan, 1);
}
else {
efan= EM_face_from_faces(efa, NULL, 0, 1, 3, -1);
if(efa->f & SELECT) EM_select_face(efan, 1);
efan= EM_face_from_faces(efa, NULL, 1, 2, 3, -1);
if(efa->f & SELECT) EM_select_face(efan, 1);
}
BLI_remlink(&em->faces, efa);
free_editface(efa);
}
}
efa= next;
}
EM_fgon_flags(); // redo flags and indices for fgons
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Convert Quads to Triangles");
}
int removedoublesflag(short flag, short automerge, float limit) /* return amount */
{
/*
flag - Test with vert->flags
automerge - Alternative operation, merge unselected into selected.
Used for "Auto Weld" mode. warning.
limit - Quick manhattan distance between verts.
*/
EditMesh *em = G.editMesh;
/* all verts with (flag & 'flag') are being evaluated */
EditVert *eve, *v1, *nextve;
EditEdge *eed, *e1, *nexted;
EditFace *efa, *nextvl;
xvertsort *sortblock, *sb, *sb1;
struct facesort *vlsortblock, *vsb, *vsb1;
int a, b, test, amount;
if(multires_test()) return 0;
/* flag 128 is cleared, count */
/* Normal non weld operation */
eve= em->verts.first;
amount= 0;
while(eve) {
eve->f &= ~128;
if(eve->h==0 && (automerge || (eve->f & flag))) amount++;
eve= eve->next;
}
if(amount==0) return 0;
/* allocate memory and qsort */
sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub");
eve= em->verts.first;
while(eve) {
if(eve->h==0 && (automerge || (eve->f & flag))) {
sb->x= eve->co[0]+eve->co[1]+eve->co[2];
sb->v1= eve;
sb++;
}
eve= eve->next;
}
qsort(sortblock, amount, sizeof(xvertsort), vergxco);
/* test for doubles */
sb= sortblock;
if (automerge) {
for(a=0; a<amount; a++, sb++) {
eve= sb->v1;
if( (eve->f & 128)==0 ) {
sb1= sb+1;
for(b=a+1; b<amount && (eve->f & 128)==0; b++, sb1++) {
if(sb1->x - sb->x > limit) break;
/* when automarge, only allow unselected->selected */
v1= sb1->v1;
if( (v1->f & 128)==0 ) {
if ((eve->f & flag)==0 && (v1->f & flag)==1) {
if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
(float)fabs(v1->co[1]-eve->co[1])<=limit &&
(float)fabs(v1->co[2]-eve->co[2])<=limit)
{ /* unique bit */
eve->f|= 128;
eve->tmp.v = v1;
}
} else if( (eve->f & flag)==1 && (v1->f & flag)==0 ) {
if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
(float)fabs(v1->co[1]-eve->co[1])<=limit &&
(float)fabs(v1->co[2]-eve->co[2])<=limit)
{ /* unique bit */
v1->f|= 128;
v1->tmp.v = eve;
}
}
}
}
}
}
} else {
for(a=0; a<amount; a++, sb++) {
eve= sb->v1;
if( (eve->f & 128)==0 ) {
sb1= sb+1;
for(b=a+1; b<amount; b++, sb1++) {
/* first test: simpel dist */
if(sb1->x - sb->x > limit) break;
v1= sb1->v1;
/* second test: is vertex allowed */
if( (v1->f & 128)==0 ) {
if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
(float)fabs(v1->co[1]-eve->co[1])<=limit &&
(float)fabs(v1->co[2]-eve->co[2])<=limit)
{
v1->f|= 128;
v1->tmp.v = eve;
}
}
}
}
}
}
MEM_freeN(sortblock);
if (!automerge)
for(eve = em->verts.first; eve; eve=eve->next)
if((eve->f & flag) && (eve->f & 128))
EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f);
/* test edges and insert again */
eed= em->edges.first;
while(eed) {
eed->f2= 0;
eed= eed->next;
}
eed= em->edges.last;
while(eed) {
nexted= eed->prev;
if(eed->f2==0) {
if( (eed->v1->f & 128) || (eed->v2->f & 128) ) {
remedge(eed);
if(eed->v1->f & 128) eed->v1 = eed->v1->tmp.v;
if(eed->v2->f & 128) eed->v2 = eed->v2->tmp.v;
e1= addedgelist(eed->v1, eed->v2, eed);
if(e1) {
e1->f2= 1;
if(eed->f & SELECT)
e1->f |= SELECT;
}
if(e1!=eed) free_editedge(eed);
}
}
eed= nexted;
}
/* first count amount of test faces */
efa= (struct EditFace *)em->faces.first;
amount= 0;
while(efa) {
efa->f1= 0;
if(efa->v1->f & 128) efa->f1= 1;
else if(efa->v2->f & 128) efa->f1= 1;
else if(efa->v3->f & 128) efa->f1= 1;
else if(efa->v4 && (efa->v4->f & 128)) efa->f1= 1;
if(efa->f1==1) amount++;
efa= efa->next;
}
/* test faces for double vertices, and if needed remove them */
efa= (struct EditFace *)em->faces.first;
while(efa) {
nextvl= efa->next;
if(efa->f1==1) {
if(efa->v1->f & 128) efa->v1= efa->v1->tmp.v;
if(efa->v2->f & 128) efa->v2= efa->v2->tmp.v;
if(efa->v3->f & 128) efa->v3= efa->v3->tmp.v;
if(efa->v4 && (efa->v4->f & 128)) efa->v4= efa->v4->tmp.v;
test= 0;
if(efa->v1==efa->v2) test+=1;
if(efa->v2==efa->v3) test+=2;
if(efa->v3==efa->v1) test+=4;
if(efa->v4==efa->v1) test+=8;
if(efa->v3==efa->v4) test+=16;
if(efa->v2==efa->v4) test+=32;
if(test) {
if(efa->v4) {
if(test==1 || test==2) {
efa->v2= efa->v3;
efa->v3= efa->v4;
efa->v4= 0;
EM_data_interp_from_faces(efa, NULL, efa, 0, 2, 3, 3);
test= 0;
}
else if(test==8 || test==16) {
efa->v4= 0;
test= 0;
}
else {
BLI_remlink(&em->faces, efa);
free_editface(efa);
amount--;
}
}
else {
BLI_remlink(&em->faces, efa);
free_editface(efa);
amount--;
}
}
if(test==0) {
/* set edge pointers */
efa->e1= findedgelist(efa->v1, efa->v2);
efa->e2= findedgelist(efa->v2, efa->v3);
if(efa->v4==0) {
efa->e3= findedgelist(efa->v3, efa->v1);
efa->e4= 0;
}
else {
efa->e3= findedgelist(efa->v3, efa->v4);
efa->e4= findedgelist(efa->v4, efa->v1);
}
}
}
efa= nextvl;
}
/* double faces: sort block */
/* count again, now all selected faces */
amount= 0;
efa= em->faces.first;
while(efa) {
efa->f1= 0;
if(faceselectedOR(efa, 1)) {
efa->f1= 1;
amount++;
}
efa= efa->next;
}
if(amount) {
/* double faces: sort block */
vsb= vlsortblock= MEM_mallocN(sizeof(struct facesort)*amount, "sortremovedoub");
efa= em->faces.first;
while(efa) {
if(efa->f1 & 1) {
if(efa->v4) vsb->x= (uintptr_t) MIN4( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3, (uintptr_t)efa->v4);
else vsb->x= (uintptr_t) MIN3( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3);
vsb->efa= efa;
vsb++;
}
efa= efa->next;
}
qsort(vlsortblock, amount, sizeof(struct facesort), vergface);
vsb= vlsortblock;
for(a=0; a<amount; a++) {
efa= vsb->efa;
if( (efa->f1 & 128)==0 ) {
vsb1= vsb+1;
for(b=a+1; b<amount; b++) {
/* first test: same pointer? */
if(vsb->x != vsb1->x) break;
/* second test: is test permitted? */
efa= vsb1->efa;
if( (efa->f1 & 128)==0 ) {
if( compareface(efa, vsb->efa)) efa->f1 |= 128;
}
vsb1++;
}
}
vsb++;
}
MEM_freeN(vlsortblock);
/* remove double faces */
efa= (struct EditFace *)em->faces.first;
while(efa) {
nextvl= efa->next;
if(efa->f1 & 128) {
BLI_remlink(&em->faces, efa);
free_editface(efa);
}
efa= nextvl;
}
}
/* remove double vertices */
a= 0;
eve= (struct EditVert *)em->verts.first;
while(eve) {
nextve= eve->next;
if(automerge || eve->f & flag) {
if(eve->f & 128) {
a++;
BLI_remlink(&em->verts, eve);
free_editvert(eve);
}
}
eve= nextve;
}
#ifdef WITH_VERSE
if((a>0) && (G.editMesh->vnode)) {
sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode);
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
}
#endif
return a; /* amount */
}
/* called from buttons */
static void xsortvert_flag__doSetX(void *userData, EditVert *eve, int x, int y, int index)
{
xvertsort *sortblock = userData;
sortblock[index].x = x;
}
void xsortvert_flag(int flag)
{
EditMesh *em = G.editMesh;
/* all verts with (flag & 'flag') are sorted */
EditVert *eve;
xvertsort *sortblock;
ListBase tbase;
int i, amount = BLI_countlist(&em->verts);
if(multires_test()) return;
sortblock = MEM_callocN(sizeof(xvertsort)*amount,"xsort");
for (i=0,eve=em->verts.first; eve; i++,eve=eve->next)
if(eve->f & flag)
sortblock[i].v1 = eve;
mesh_foreachScreenVert(xsortvert_flag__doSetX, sortblock, V3D_CLIP_TEST_OFF);
qsort(sortblock, amount, sizeof(xvertsort), vergxco);
/* make temporal listbase */
tbase.first= tbase.last= 0;
for (i=0; i<amount; i++) {
eve = sortblock[i].v1;
if (eve) {
BLI_remlink(&em->verts, eve);
BLI_addtail(&tbase, eve);
}
}
addlisttolist(&em->verts, &tbase);
MEM_freeN(sortblock);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Xsort");
}
/* called from buttons */
void hashvert_flag(int flag)
{
/* switch vertex order using hash table */
EditMesh *em = G.editMesh;
EditVert *eve;
struct xvertsort *sortblock, *sb, onth, *newsort;
ListBase tbase;
int amount, a, b;
if(multires_test()) return;
/* count */
eve= em->verts.first;
amount= 0;
while(eve) {
if(eve->f & flag) amount++;
eve= eve->next;
}
if(amount==0) return;
/* allocate memory */
sb= sortblock= (struct xvertsort *)MEM_mallocN(sizeof(struct xvertsort)*amount,"sortremovedoub");
eve= em->verts.first;
while(eve) {
if(eve->f & flag) {
sb->v1= eve;
sb++;
}
eve= eve->next;
}
BLI_srand(1);
sb= sortblock;
for(a=0; a<amount; a++, sb++) {
b= (int)(amount*BLI_drand());
if(b>=0 && b<amount) {
newsort= sortblock+b;
onth= *sb;
*sb= *newsort;
*newsort= onth;
}
}
/* make temporal listbase */
tbase.first= tbase.last= 0;
sb= sortblock;
while(amount--) {
eve= sb->v1;
BLI_remlink(&em->verts, eve);
BLI_addtail(&tbase, eve);
sb++;
}
addlisttolist(&em->verts, &tbase);
MEM_freeN(sortblock);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Hash");
}
/* generic extern called extruder */
void extrude_mesh(void)
{
float nor[3]= {0.0, 0.0, 0.0};
short nr, transmode= 0;
TEST_EDITMESH
if(multires_test()) return;
if(G.scene->selectmode & SCE_SELECT_VERTEX) {
if(G.totvertsel==0) nr= 0;
else if(G.totvertsel==1) nr= 4;
else if(G.totedgesel==0) nr= 4;
else if(G.totfacesel==0)
nr= pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4");
else if(G.totfacesel==1)
nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4");
else
nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4");
}
else if(G.scene->selectmode & SCE_SELECT_EDGE) {
if (G.totedgesel==0) nr = 0;
else if (G.totedgesel==1) nr = 3;
else if(G.totfacesel==0) nr = 3;
else if(G.totfacesel==1)
nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3");
else
nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3");
}
else {
if (G.totfacesel == 0) nr = 0;
else if (G.totfacesel == 1) nr = 1;
else
nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2");
}
if(nr<1) return;
if(nr==1) transmode= extrudeflag(SELECT, nor);
else if(nr==4) transmode= extrudeflag_verts_indiv(SELECT, nor);
else if(nr==3) transmode= extrudeflag_edges_indiv(SELECT, nor);
else transmode= extrudeflag_face_indiv(SELECT, nor);
if(transmode==0) {
error("No valid selection");
}
else {
EM_fgon_flags();
countall();
/* We need to force immediate calculation here because
* transform may use derived objects (which are now stale).
*
* This shouldn't be necessary, derived queries should be
* automatically building this data if invalid. Or something.
*/
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
object_handle_update(G.obedit);
/* individual faces? */
BIF_TransformSetUndo("Extrude");
if(nr==2) {
initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR);
Transform();
}
else {
initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR);
if(transmode=='n') {
mul_m4_v3(G.obedit->obmat, nor);
sub_v3_v3v3(nor, nor, G.obedit->obmat[3]);
BIF_setSingleAxisConstraint(nor, "along normal");
}
Transform();
}
}
}
void split_mesh(void)
{
TEST_EDITMESH
if(multires_test()) return;
if(okee(" Split ")==0) return;
waitcursor(1);
/* make duplicate first */
adduplicateflag(SELECT);
/* old faces have flag 128 set, delete them */
delfaceflag(128);
recalc_editnormals();
waitcursor(0);
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Split");
}
void extrude_repeat_mesh(int steps, float offs)
{
float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0};
short a;
TEST_EDITMESH
if(multires_test()) return;
/* dvec */
dvec[0]= G.vd->persinv[2][0];
dvec[1]= G.vd->persinv[2][1];
dvec[2]= G.vd->persinv[2][2];
normalize_v3(dvec);
dvec[0]*= offs;
dvec[1]*= offs;
dvec[2]*= offs;
/* base correction */
copy_m3_m4(bmat, G.obedit->obmat);
invert_m3_m3(tmat, bmat);
mul_m3_v3(tmat, dvec);
for(a=0; a<steps; a++) {
extrudeflag(SELECT, nor);
translateflag(SELECT, dvec);
}
recalc_editnormals();
EM_fgon_flags();
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
BIF_undo_push("Extrude Repeat");
}
void spin_mesh(int steps, float degr, float *dvec, int mode)
{
EditMesh *em = G.editMesh;
EditVert *eve,*nextve;
float nor[3]= {0.0, 0.0, 0.0};
float *curs, si,n[3],q[4],cmat[3][3],imat[3][3], tmat[3][3];
float cent[3],bmat[3][3];
float phi;
short a,ok;
TEST_EDITMESH
if(multires_test()) return;
/* imat and center and size */
copy_m3_m4(bmat, G.obedit->obmat);
invert_m3_m3(imat,bmat);
curs= give_cursor();
copy_v3_v3(cent, curs);
cent[0]-= G.obedit->obmat[3][0];
cent[1]-= G.obedit->obmat[3][1];
cent[2]-= G.obedit->obmat[3][2];
mul_m3_v3(imat, cent);
phi= degr*M_PI/360.0;
phi/= steps;
if(G.scene->toolsettings->editbutflag & B_CLOCKWISE) phi= -phi;
if(dvec) {
n[0]= G.vd->viewinv[1][0];
n[1]= G.vd->viewinv[1][1];
n[2]= G.vd->viewinv[1][2];
} else {
n[0]= G.vd->viewinv[2][0];
n[1]= G.vd->viewinv[2][1];
n[2]= G.vd->viewinv[2][2];
}
normalize_v3(n);
q[0]= (float)cos(phi);
si= (float)sin(phi);
q[1]= n[0]*si;
q[2]= n[1]*si;
q[3]= n[2]*si;
quat_to_mat3( cmat,q);
mul_m3_m3m3(tmat,cmat,bmat);
mul_m3_m3m3(bmat,imat,tmat);
if(mode==0) if(G.scene->toolsettings->editbutflag & B_KEEPORIG) adduplicateflag(1);
ok= 1;
for(a=0;a<steps;a++) {
if(mode==0) ok= extrudeflag(SELECT, nor);
else adduplicateflag(SELECT);
if(ok==0) {
error("No valid vertices are selected");
break;
}
rotateflag(SELECT, cent, bmat);
if(dvec) {
mul_m3_v3(bmat,dvec);
translateflag(SELECT, dvec);
}
}
if(ok==0) {
/* no vertices or only loose ones selected, remove duplicates */
eve= em->verts.first;
while(eve) {
nextve= eve->next;
if(eve->f & SELECT) {
BLI_remlink(&em->verts,eve);
free_editvert(eve);
}
eve= nextve;
}
}
recalc_editnormals();
EM_fgon_flags();
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
if(dvec==NULL) BIF_undo_push("Spin");
}
void screw_mesh(int steps, int turns)
{
EditMesh *em = G.editMesh;
EditVert *eve,*v1=0,*v2=0;
EditEdge *eed;
float dvec[3], nor[3];
TEST_EDITMESH
if(multires_test()) return;
/* clear flags */
eve= em->verts.first;
while(eve) {
eve->f1= 0;
eve= eve->next;
}
/* edges set flags in verts */
eed= em->edges.first;
while(eed) {
if(eed->v1->f & SELECT) {
if(eed->v2->f & SELECT) {
/* watch: f1 is a byte */
if(eed->v1->f1<2) eed->v1->f1++;
if(eed->v2->f1<2) eed->v2->f1++;
}
}
eed= eed->next;
}
/* find two vertices with eve->f1==1, more or less is wrong */
eve= em->verts.first;
while(eve) {
if(eve->f1==1) {
if(v1==0) v1= eve;
else if(v2==0) v2= eve;
else {
v1=0;
break;
}
}
eve= eve->next;
}
if(v1==0 || v2==0) {
error("You have to select a string of connected vertices too");
return;
}
/* calculate dvec */
dvec[0]= ( (v1->co[0]- v2->co[0]) )/(steps);
dvec[1]= ( (v1->co[1]- v2->co[1]) )/(steps);
dvec[2]= ( (v1->co[2]- v2->co[2]) )/(steps);
copy_v3_v3(nor, G.obedit->obmat[2]);
if(nor[0]*dvec[0]+nor[1]*dvec[1]+nor[2]*dvec[2]>0.000) {
dvec[0]= -dvec[0];
dvec[1]= -dvec[1];
dvec[2]= -dvec[2];
}
spin_mesh(turns*steps, turns*360, dvec, 0);
BIF_undo_push("Spin");
}
static void erase_edges(ListBase *l)
{
EditEdge *ed, *nexted;
ed = (EditEdge *) l->first;
while(ed) {
nexted= ed->next;
if( (ed->v1->f & SELECT) || (ed->v2->f & SELECT) ) {
remedge(ed);
free_editedge(ed);
}
ed= nexted;
}
}
static void erase_faces(ListBase *l)
{
EditFace *f, *nextf;
f = (EditFace *) l->first;
while(f) {
nextf= f->next;
if( faceselectedOR(f, SELECT) ) {
BLI_remlink(l, f);
free_editface(f);
}
f = nextf;
}
}
static void erase_vertices(ListBase *l)
{
EditVert *v, *nextv;
v = (EditVert *) l->first;
while(v) {
nextv= v->next;
if(v->f & 1) {
BLI_remlink(l, v);
free_editvert(v);
}
v = nextv;
}
}
void delete_mesh(void)
{
EditMesh *em = G.editMesh;
EditFace *efa, *nextvl;
EditVert *eve,*nextve;
EditEdge *eed,*nexted;
short event;
int count;
char *str="Erase";
TEST_EDITMESH
if(multires_test()) return;
event= pupmenu("Erase %t|Vertices%x10|Edges%x1|Faces%x2|All%x3|Edges & Faces%x4|Only Faces%x5|Edge Loop%x6");
if(event<1) return;
if(event==10 ) {
str= "Erase Vertices";
erase_edges(&em->edges);
erase_faces(&em->faces);
erase_vertices(&em->verts);
}
else if(event==6) {
if(!EdgeLoopDelete())
return;
str= "Erase Edge Loop";
}
else if(event==4) {
str= "Erase Edges & Faces";
efa= em->faces.first;
while(efa) {
nextvl= efa->next;
/* delete only faces with 1 or more edges selected */
count= 0;
if(efa->e1->f & SELECT) count++;
if(efa->e2->f & SELECT) count++;
if(efa->e3->f & SELECT) count++;
if(efa->e4 && (efa->e4->f & SELECT)) count++;
if(count) {
BLI_remlink(&em->faces, efa);
free_editface(efa);
}
efa= nextvl;
}
eed= em->edges.first;
while(eed) {
nexted= eed->next;
if(eed->f & SELECT) {
remedge(eed);
free_editedge(eed);
}
eed= nexted;
}
efa= em->faces.first;
while(efa) {
nextvl= efa->next;
event=0;
if( efa->v1->f & SELECT) event++;
if( efa->v2->f & SELECT) event++;
if( efa->v3->f & SELECT) event++;
if(efa->v4 && (efa->v4->f & SELECT)) event++;
if(event>1) {
BLI_remlink(&em->faces, efa);
free_editface(efa);
}
efa= nextvl;
}
}
else if(event==1) {
str= "Erase Edges";
// faces first
efa= em->faces.first;
while(efa) {
nextvl= efa->next;
event=0;
if( efa->e1->f & SELECT) event++;
if( efa->e2->f & SELECT) event++;
if( efa->e3->f & SELECT) event++;
if(efa->e4 && (efa->e4->f & SELECT)) event++;
if(event) {
BLI_remlink(&em->faces, efa);
free_editface(efa);
}
efa= nextvl;
}
eed= em->edges.first;
while(eed) {
nexted= eed->next;
if(eed->f & SELECT) {
remedge(eed);
free_editedge(eed);
}
eed= nexted;
}
/* to remove loose vertices: */
eed= em->edges.first;
while(eed) {
if( eed->v1->f & SELECT) eed->v1->f-=SELECT;
if( eed->v2->f & SELECT) eed->v2->f-=SELECT;
eed= eed->next;
}
eve= em->verts.first;
while(eve) {
nextve= eve->next;
if(eve->f & SELECT) {
BLI_remlink(&em->verts,eve);
free_editvert(eve);
}
eve= nextve;
}
}
else if(event==2) {
str="Erase Faces";
delfaceflag(SELECT);
}
else if(event==3) {
str= "Erase All";
if(em->verts.first) free_vertlist(&em->verts);
if(em->edges.first) free_edgelist(&em->edges);
if(em->faces.first) free_facelist(&em->faces);
if(em->selected.first) BLI_freelistN(&(em->selected));
}
else if(event==5) {
str= "Erase Only Faces";
efa= em->faces.first;
while(efa) {
nextvl= efa->next;
if(efa->f & SELECT) {
BLI_remlink(&em->faces, efa);
free_editface(efa);
}
efa= nextvl;
}
}
EM_fgon_flags(); // redo flags and indices for fgons
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
BIF_undo_push(str);
}
/* Got this from scanfill.c. You will need to juggle around the
* callbacks for the scanfill.c code a bit for this to work. */
void fill_mesh(void)
{
EditMesh *em = G.editMesh;
EditVert *eve,*v1;
EditEdge *eed,*e1,*nexted;
EditFace *efa,*nextvl, *efan;
short ok;
if(G.obedit==0 || (G.obedit->type!=OB_MESH)) return;
if(multires_test()) return;
waitcursor(1);
/* copy all selected vertices */
eve= em->verts.first;
while(eve) {
if(eve->f & SELECT) {
v1= BLI_addfillvert(eve->co);
eve->tmp.v= v1;
v1->tmp.v= eve;
v1->xs= 0; // used for counting edges
}
eve= eve->next;
}
/* copy all selected edges */
eed= em->edges.first;
while(eed) {
if( (eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) {
e1= BLI_addfilledge(eed->v1->tmp.v, eed->v2->tmp.v);
e1->v1->xs++;
e1->v2->xs++;
}
eed= eed->next;
}
/* from all selected faces: remove vertices and edges to prevent doubles */
/* all edges add values, faces subtract,
then remove edges with vertices ->xs<2 */
efa= em->faces.first;
ok= 0;
while(efa) {
nextvl= efa->next;
if( faceselectedAND(efa, 1) ) {
efa->v1->tmp.v->xs--;
efa->v2->tmp.v->xs--;
efa->v3->tmp.v->xs--;
if(efa->v4) efa->v4->tmp.v->xs--;
ok= 1;
}
efa= nextvl;
}
if(ok) { /* there are faces selected */
eed= filledgebase.first;
while(eed) {
nexted= eed->next;
if(eed->v1->xs<2 || eed->v2->xs<2) {
BLI_remlink(&filledgebase,eed);
}
eed= nexted;
}
}
if(BLI_edgefill(0, (G.obedit && G.obedit->actcol)?(G.obedit->actcol-1):0)) {
efa= fillfacebase.first;
while(efa) {
/* normals default pointing up */
efan= addfacelist(efa->v3->tmp.v, efa->v2->tmp.v,
efa->v1->tmp.v, 0, NULL, NULL);
if(efan) EM_select_face(efan, 1);
efa= efa->next;
}
}
BLI_end_edgefill();
waitcursor(0);
EM_select_flush();
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Fill");
}
/*GB*/
/*-------------------------------------------------------------------------------*/
/*--------------------------- Edge Based Subdivide ------------------------------*/
#define EDGENEW 2
#define FACENEW 2
#define EDGEINNER 4
#define EDGEOLD 8
/*used by faceloop cut to select only edges valid for edge slide*/
#define DOUBLEOPFILL 16
/* calculates offset for co, based on fractal, sphere or smooth settings */
static void alter_co(float *co, EditEdge *edge, float rad, int beauty, float perc)
{
float vec1[3], fac;
if(beauty & B_SMOOTH) {
/* we calculate an offset vector vec1[], to be added to *co */
float len, fac, nor[3], nor1[3], nor2[3];
sub_v3_v3v3(nor, edge->v1->co, edge->v2->co);
len= 0.5f*normalize_v3(nor);
copy_v3_v3(nor1, edge->v1->no);
copy_v3_v3(nor2, edge->v2->no);
/* cosine angle */
fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ;
vec1[0]= fac*nor1[0];
vec1[1]= fac*nor1[1];
vec1[2]= fac*nor1[2];
/* cosine angle */
fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ;
vec1[0]+= fac*nor2[0];
vec1[1]+= fac*nor2[1];
vec1[2]+= fac*nor2[2];
vec1[0]*= rad*len;
vec1[1]*= rad*len;
vec1[2]*= rad*len;
co[0] += vec1[0];
co[1] += vec1[1];
co[2] += vec1[2];
}
else {
if(rad > 0.0) { /* subdivide sphere */
normalize_v3(co);
co[0]*= rad;
co[1]*= rad;
co[2]*= rad;
}
else if(rad< 0.0) { /* fractal subdivide */
fac= rad* len_v3v3(edge->v1->co, edge->v2->co);
vec1[0]= fac*(float)(0.5-BLI_drand());
vec1[1]= fac*(float)(0.5-BLI_drand());
vec1[2]= fac*(float)(0.5-BLI_drand());
add_v3_v3v3(co, co, vec1);
}
}
}
/* assumes in the edge is the correct interpolated vertices already */
/* percent defines the interpolation, rad and beauty are for special options */
/* results in new vertex with correct coordinate, vertex normal and weight group info */
static EditVert *subdivide_edge_addvert(EditEdge *edge, float rad, int beauty, float percent)
{
EditVert *ev;
float co[3];
co[0] = (edge->v2->co[0]-edge->v1->co[0])*percent + edge->v1->co[0];
co[1] = (edge->v2->co[1]-edge->v1->co[1])*percent + edge->v1->co[1];
co[2] = (edge->v2->co[2]-edge->v1->co[2])*percent + edge->v1->co[2];
/* offset for smooth or sphere or fractal */
alter_co(co, edge, rad, beauty, percent);
/* clip if needed by mirror modifier */
if (edge->v1->f2) {
if ( edge->v1->f2 & edge->v2->f2 & 1) {
co[0]= 0.0f;
}
if ( edge->v1->f2 & edge->v2->f2 & 2) {
co[1]= 0.0f;
}
if ( edge->v1->f2 & edge->v2->f2 & 4) {
co[2]= 0.0f;
}
}
ev = addvertlist(co, NULL);
/* vert data (vgroups, ..) */
EM_data_interp_from_verts(edge->v1, edge->v2, ev, percent);
/* normal */
ev->no[0] = (edge->v2->no[0]-edge->v1->no[0])*percent + edge->v1->no[0];
ev->no[1] = (edge->v2->no[1]-edge->v1->no[1])*percent + edge->v1->no[1];
ev->no[2] = (edge->v2->no[2]-edge->v1->no[2])*percent + edge->v1->no[2];
normalize_v3(ev->no);
return ev;
}
static void flipvertarray(EditVert** arr, short size)
{
EditVert *hold;
int i;
for(i=0; i<size/2; i++) {
hold = arr[i];
arr[i] = arr[size-i-1];
arr[size-i-1] = hold;
}
}
static void facecopy(EditFace *source, EditFace *target)
{
EditMesh *em= G.editMesh;
float *v1 = source->v1->co, *v2 = source->v2->co, *v3 = source->v3->co;
float *v4 = source->v4? source->v4->co: NULL;
float w[4][4];
CustomData_em_copy_data(&em->fdata, &em->fdata, source->data, &target->data);
target->mat_nr = source->mat_nr;
target->flag = source->flag;
target->h = source->h;
interp_weights_face_v3( w[0],v1, v2, v3, v4, target->v1->co);
interp_weights_face_v3( w[1],v1, v2, v3, v4, target->v2->co);
interp_weights_face_v3( w[2],v1, v2, v3, v4, target->v3->co);
if (target->v4)
interp_weights_face_v3( w[3],v1, v2, v3, v4, target->v4->co);
CustomData_em_interp(&em->fdata, &source->data, NULL, (float*)w, 1, target->data);
}
static void fill_quad_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype)
{
EditEdge *cedge=NULL;
EditVert *v[4], **verts;
EditFace *hold;
short start=0, end, left, right, vertsize,i;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
v[3] = efa->v4;
if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;}
// Point verts to the array of new verts for cedge
verts = BLI_ghash_lookup(gh, cedge);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);}
end = (start+1)%4;
left = (start+2)%4;
right = (start+3)%4;
/*
We should have something like this now
end start
3 2 1 0
|---*---*---|
| |
| |
| |
-------------
left right
where start,end,left, right are indexes of EditFace->v1, etc (stored in v)
and 0,1,2... are the indexes of the new verts stored in verts
We will fill this case like this or this depending on even or odd cuts
|---*---*---| |---*---|
| / \ | | / \ |
| / \ | | / \ |
|/ \| |/ \|
------------- ---------
*/
// Make center face
if(vertsize % 2 == 0) {
hold = addfacelist(verts[(vertsize-1)/2],verts[((vertsize-1)/2)+1],v[left],v[right], NULL,NULL);
hold->e2->f2 |= EDGEINNER;
hold->e4->f2 |= EDGEINNER;
}else{
hold = addfacelist(verts[(vertsize-1)/2],v[left],v[right],NULL, NULL,NULL);
hold->e1->f2 |= EDGEINNER;
hold->e3->f2 |= EDGEINNER;
}
facecopy(efa,hold);
// Make side faces
for(i=0;i<(vertsize-1)/2;i++) {
hold = addfacelist(verts[i],verts[i+1],v[right],NULL,NULL,NULL);
facecopy(efa,hold);
if(i+1 != (vertsize-1)/2) {
if(seltype == SUBDIV_SELECT_INNER) {
hold->e2->f2 |= EDGEINNER;
}
}
hold = addfacelist(verts[vertsize-2-i],verts[vertsize-1-i],v[left],NULL,NULL,NULL);
facecopy(efa,hold);
if(i+1 != (vertsize-1)/2) {
if(seltype == SUBDIV_SELECT_INNER) {
hold->e3->f2 |= EDGEINNER;
}
}
}
}
static void fill_tri_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype)
{
EditEdge *cedge=NULL;
EditVert *v[3], **verts;
EditFace *hold;
short start=0, end, op, vertsize,i;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
// Point verts to the array of new verts for cedge
verts = BLI_ghash_lookup(gh, cedge);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);}
end = (start+1)%3;
op = (start+2)%3;
/*
We should have something like this now
end start
3 2 1 0
|---*---*---|
\ |
\ |
\ |
\ |
\ |
\ |
|op
where start,end,op are indexes of EditFace->v1, etc (stored in v)
and 0,1,2... are the indexes of the new verts stored in verts
We will fill this case like this or this depending on even or odd cuts
3 2 1 0
|---*---*---|
\ \ \ |
\ \ \ |
\ \ \ |
\ \ \|
\ \\|
\ |
|op
*/
// Make side faces
for(i=0;i<(vertsize-1);i++) {
hold = addfacelist(verts[i],verts[i+1],v[op],NULL,NULL,NULL);
if(i+1 != vertsize-1) {
if(seltype == SUBDIV_SELECT_INNER) {
hold->e2->f2 |= EDGEINNER;
}
}
facecopy(efa,hold);
}
}
static void fill_quad_double_op(EditFace *efa, struct GHash *gh, int numcuts)
{
EditEdge *cedge[2]={NULL, NULL};
EditVert *v[4], **verts[2];
EditFace *hold;
short start=0, end, left, right, vertsize,i;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
v[3] = efa->v4;
if(efa->e1->f & SELECT) { cedge[0] = efa->e1; cedge[1] = efa->e3; start = 0;}
else if(efa->e2->f & SELECT) { cedge[0] = efa->e2; cedge[1] = efa->e4; start = 1;}
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
end = (start+1)%4;
left = (start+2)%4;
right = (start+3)%4;
if(verts[1][0] != v[left]) {flipvertarray(verts[1],numcuts+2);}
/*
We should have something like this now
end start
3 2 1 0
|---*---*---|
| |
| |
| |
|---*---*---|
0 1 2 3
left right
We will fill this case like this or this depending on even or odd cuts
|---*---*---|
| | | |
| | | |
| | | |
|---*---*---|
*/
// Make side faces
for(i=0;i<vertsize-1;i++) {
hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-2-i],verts[1][vertsize-1-i],NULL,NULL);
if(i < vertsize-2) {
hold->e2->f2 |= EDGEINNER;
hold->e2->f2 |= DOUBLEOPFILL;
}
facecopy(efa,hold);
}
}
static void fill_quad_double_adj_path(EditFace *efa, struct GHash *gh, int numcuts)
{
EditEdge *cedge[2]={NULL, NULL};
EditVert *v[4], **verts[2];
EditFace *hold;
short start=0, start2=0, vertsize,i;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
v[3] = efa->v4;
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;}
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;}
if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3;}
if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0;}
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
/*
We should have something like this now
end start
3 2 1 0
start2 0|---*---*---|
| |
1* |
| |
2* |
| |
end2 3|-----------|
We will fill this case like this or this depending on even or odd cuts
|---*---*---|
| / / / |
* / / |
| / / |
* / |
| / |
|-----------|
*/
// Make outside tris
hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
/* when ctrl is depressed, only want verts on the cutline selected */
if (G.qual != LR_CTRLKEY)
hold->e3->f2 |= EDGEINNER;
facecopy(efa,hold);
hold = addfacelist(verts[0][0],verts[1][vertsize-1],v[(start2+2)%4],NULL,NULL,NULL);
/* when ctrl is depressed, only want verts on the cutline selected */
if (G.qual != LR_CTRLKEY)
hold->e1->f2 |= EDGEINNER;
facecopy(efa,hold);
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
// hold->e1->h |= EM_FGON;
//}
// Make side faces
for(i=0;i<numcuts;i++) {
hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
}
//EM_fgon_flags();
}
static void fill_quad_double_adj_fan(EditFace *efa, struct GHash *gh, int numcuts)
{
EditEdge *cedge[2]={NULL, NULL};
EditVert *v[4], *op=NULL, **verts[2];
EditFace *hold;
short start=0, start2=0, vertsize,i;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
v[3] = efa->v4;
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;}
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;}
if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;}
if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;}
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
/*
We should have something like this now
end start
3 2 1 0
start2 0|---*---*---|
| |
1* |
| |
2* |
| |
end2 3|-----------|op
We will fill this case like this or this (warning horrible ascii art follows)
|---*---*---|
| \ \ \ |
*---\ \ \ |
| \ \ \ \|
*---- \ \ \ |
| --- \\\|
|-----------|
*/
for(i=0;i<=numcuts;i++) {
hold = addfacelist(op,verts[1][numcuts-i],verts[1][numcuts-i+1],NULL,NULL,NULL);
hold->e1->f2 |= EDGEINNER;
facecopy(efa,hold);
hold = addfacelist(op,verts[0][i],verts[0][i+1],NULL,NULL,NULL);
hold->e3->f2 |= EDGEINNER;
facecopy(efa,hold);
}
}
static void fill_quad_double_adj_inner(EditFace *efa, struct GHash *gh, int numcuts)
{
EditEdge *cedge[2]={NULL, NULL};
EditVert *v[4], *op=NULL, **verts[2],**inner;
EditFace *hold;
short start=0, start2=0, vertsize,i;
float co[3];
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
v[3] = efa->v4;
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;}
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;}
if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;}
if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;}
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
/*
We should have something like this now
end start
3 2 1 0
start2 0|---*---*---|
| |
1* |
| |
2* |
| |
end2 3|-----------|op
We will fill this case like this or this (warning horrible ascii art follows)
|---*-----*---|
| * / |
* \ / |
| * |
| / \ |
* \ |
| \ |
|-------------|
*/
// Add Inner Vert(s)
inner = MEM_mallocN(sizeof(EditVert*)*numcuts,"New inner verts");
for(i=0;i<numcuts;i++) {
co[0] = (verts[0][numcuts-i]->co[0] + verts[1][i+1]->co[0] ) / 2 ;
co[1] = (verts[0][numcuts-i]->co[1] + verts[1][i+1]->co[1] ) / 2 ;
co[2] = (verts[0][numcuts-i]->co[2] + verts[1][i+1]->co[2] ) / 2 ;
inner[i] = addvertlist(co, NULL);
inner[i]->f2 |= EDGEINNER;
EM_data_interp_from_verts(verts[0][numcuts-i], verts[1][i+1], inner[i], 0.5f);
}
// Add Corner Quad
hold = addfacelist(verts[0][numcuts+1],verts[1][1],inner[0],verts[0][numcuts],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
hold->e3->f2 |= EDGEINNER;
facecopy(efa,hold);
// Add Bottom Quads
hold = addfacelist(verts[0][0],verts[0][1],inner[numcuts-1],op,NULL,NULL);
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
hold = addfacelist(op,inner[numcuts-1],verts[1][numcuts],verts[1][numcuts+1],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
// hold->e1->h |= EM_FGON;
//}
// Add Fill Quads (if # cuts > 1)
for(i=0;i<numcuts-1;i++) {
hold = addfacelist(inner[i],verts[1][i+1],verts[1][i+2],inner[i+1],NULL,NULL);
hold->e1->f2 |= EDGEINNER;
hold->e3->f2 |= EDGEINNER;
facecopy(efa,hold);
hold = addfacelist(inner[i],inner[i+1],verts[0][numcuts-1-i],verts[0][numcuts-i],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
hold->e4->f2 |= EDGEINNER;
facecopy(efa,hold);
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
// hold->e1->h |= EM_FGON;
//}
}
//EM_fgon_flags();
MEM_freeN(inner);
}
static void fill_tri_double(EditFace *efa, struct GHash *gh, int numcuts)
{
EditEdge *cedge[2]={NULL, NULL};
EditVert *v[3], **verts[2];
EditFace *hold;
short start=0, start2=0, vertsize,i;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;}
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;}
if(efa->e3->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e1; start = 2; start2 = 0;}
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
/*
We should have something like this now
end start
3 2 1 0
start2 0|---*---*---|
| /
1* /
| /
2* /
| /
end2 3|
We will fill this case like this or this depending on even or odd cuts
|---*---*---|
| / / /
* / /
| / /
* /
| /
|
*/
// Make outside tri
hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
hold->e3->f2 |= EDGEINNER;
facecopy(efa,hold);
// Make side faces
for(i=0;i<numcuts;i++) {
hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
}
}
static void fill_quad_triple(EditFace *efa, struct GHash *gh, int numcuts)
{
EditEdge *cedge[3]={0};
EditVert *v[4], **verts[3];
EditFace *hold;
short start=0, start2=0, start3=0, vertsize, i, repeats;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
v[3] = efa->v4;
if(!(efa->e1->f & SELECT)) {
cedge[0] = efa->e2;
cedge[1] = efa->e3;
cedge[2] = efa->e4;
start = 1;start2 = 2;start3 = 3;
}
if(!(efa->e2->f & SELECT)) {
cedge[0] = efa->e3;
cedge[1] = efa->e4;
cedge[2] = efa->e1;
start = 2;start2 = 3;start3 = 0;
}
if(!(efa->e3->f & SELECT)) {
cedge[0] = efa->e4;
cedge[1] = efa->e1;
cedge[2] = efa->e2;
start = 3;start2 = 0;start3 = 1;
}
if(!(efa->e4->f & SELECT)) {
cedge[0] = efa->e1;
cedge[1] = efa->e2;
cedge[2] = efa->e3;
start = 0;start2 = 1;start3 = 2;
}
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
verts[2] = BLI_ghash_lookup(gh, cedge[2]);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
if(verts[2][0] != v[start3]) {flipvertarray(verts[2],numcuts+2);}
/*
We should have something like this now
start2
3 2 1 0
start3 0|---*---*---|3
| |
1* *2
| |
2* *1
| |
3|-----------|0 start
We will fill this case like this or this depending on even or odd cuts
there are a couple of differences. For odd cuts, there is a tri in the
middle as well as 1 quad at the bottom (not including the extra quads
for odd cuts > 1
For even cuts, there is a quad in the middle and 2 quads on the bottom
they are numbered here for clarity
1 outer tris and bottom quads
2 inner tri or quad
3 repeating quads
|---*---*---*---|
|1/ / \ \ 1|
|/ 3 / \ 3 \|
* / 2 \ *
| / \ |
|/ \ |
*---------------*
| 3 |
| |
*---------------*
| |
| 1 |
| |
|---------------|
|---*---*---*---*---|
| 1/ / \ \ 1|
| / / \ \ |
|/ 3 / \ 3 \|
* / \ *
| / \ |
| / 2 \ |
|/ \|
*-------------------*
| |
| 3 |
| |
*-------------------*
| |
| 1 |
| |
*-------------------*
| |
| 1 |
| |
|-------------------|
*/
// Make outside tris
hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
hold->e3->f2 |= EDGEINNER;
facecopy(efa,hold);
hold = addfacelist(verts[1][vertsize-2],verts[1][vertsize-1],verts[2][1],NULL,NULL,NULL);
hold->e3->f2 |= EDGEINNER;
facecopy(efa,hold);
// Make bottom quad
hold = addfacelist(verts[0][0],verts[0][1],verts[2][vertsize-2],verts[2][vertsize-1],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
//If it is even cuts, add the 2nd lower quad
if(numcuts % 2 == 0) {
hold = addfacelist(verts[0][1],verts[0][2],verts[2][vertsize-3],verts[2][vertsize-2],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
// Also Make inner quad
hold = addfacelist(verts[1][numcuts/2],verts[1][(numcuts/2)+1],verts[2][numcuts/2],verts[0][(numcuts/2)+1],NULL,NULL);
hold->e3->f2 |= EDGEINNER;
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
// hold->e3->h |= EM_FGON;
//}
facecopy(efa,hold);
repeats = (numcuts / 2) -1;
} else {
// Make inner tri
hold = addfacelist(verts[1][(numcuts/2)+1],verts[2][(numcuts/2)+1],verts[0][(numcuts/2)+1],NULL,NULL,NULL);
hold->e2->f2 |= EDGEINNER;
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
// hold->e2->h |= EM_FGON;
//}
facecopy(efa,hold);
repeats = ((numcuts+1) / 2)-1;
}
// cuts for 1 and 2 do not have the repeating quads
if(numcuts < 3) {repeats = 0;}
for(i=0;i<repeats;i++) {
//Make side repeating Quads
hold = addfacelist(verts[1][i+1],verts[1][i+2],verts[0][vertsize-i-3],verts[0][vertsize-i-2],NULL,NULL);
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
hold = addfacelist(verts[1][vertsize-i-3],verts[1][vertsize-i-2],verts[2][i+1],verts[2][i+2],NULL,NULL);
hold->e4->f2 |= EDGEINNER;
facecopy(efa,hold);
}
// Do repeating bottom quads
for(i=0;i<repeats;i++) {
if(numcuts % 2 == 1) {
hold = addfacelist(verts[0][1+i],verts[0][2+i],verts[2][vertsize-3-i],verts[2][vertsize-2-i],NULL,NULL);
} else {
hold = addfacelist(verts[0][2+i],verts[0][3+i],verts[2][vertsize-4-i],verts[2][vertsize-3-i],NULL,NULL);
}
hold->e2->f2 |= EDGEINNER;
facecopy(efa,hold);
}
//EM_fgon_flags();
}
static void fill_quad_quadruple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty)
{
EditVert **verts[4], ***innerverts;
EditFace *hold;
EditEdge temp;
short vertsize, i, j;
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, efa->e1);
verts[1] = BLI_ghash_lookup(gh, efa->e2);
verts[2] = BLI_ghash_lookup(gh, efa->e3);
verts[3] = BLI_ghash_lookup(gh, efa->e4);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);}
if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);}
if(verts[2][0] == efa->v3) {flipvertarray(verts[2],numcuts+2);}
if(verts[3][0] == efa->v4) {flipvertarray(verts[3],numcuts+2);}
/*
We should have something like this now
1
3 2 1 0
0|---*---*---|0
| |
1* *1
2 | | 4
2* *2
| |
3|---*---*---|3
3 2 1 0
3
// we will fill a 2 dim array of editvert*s to make filling easier
// the innervert order is shown
0 0---1---2---3
| | | |
1 0---1---2---3
| | | |
2 0---1---2---3
| | | |
3 0---1---2---3
*/
innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts outer array");
for(i=0;i<numcuts+2;i++) {
innerverts[i] = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts inner array");
}
// first row is e1 last row is e3
for(i=0;i<numcuts+2;i++) {
innerverts[0][i] = verts[0][(numcuts+1)-i];
innerverts[numcuts+1][i] = verts[2][(numcuts+1)-i];
}
for(i=1;i<=numcuts;i++) {
/* we create a fake edge for the next loop */
temp.v2 = innerverts[i][0] = verts[1][i];
temp.v1 = innerverts[i][numcuts+1] = verts[3][i];
for(j=1;j<=numcuts;j++) {
float percent= (float)j/(float)(numcuts+1);
innerverts[i][(numcuts+1)-j]= subdivide_edge_addvert(&temp, rad, beauty, percent);
}
}
// Fill with faces
for(i=0;i<numcuts+1;i++) {
for(j=0;j<numcuts+1;j++) {
hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],innerverts[i+1][j+1],NULL,NULL);
hold->e1->f2 = EDGENEW;
hold->e2->f2 = EDGENEW;
hold->e3->f2 = EDGENEW;
hold->e4->f2 = EDGENEW;
if(i != 0) { hold->e1->f2 |= EDGEINNER; }
if(j != 0) { hold->e2->f2 |= EDGEINNER; }
if(i != numcuts) { hold->e3->f2 |= EDGEINNER; }
if(j != numcuts) { hold->e4->f2 |= EDGEINNER; }
facecopy(efa,hold);
}
}
// Clean up our dynamic multi-dim array
for(i=0;i<numcuts+2;i++) {
MEM_freeN(innerverts[i]);
}
MEM_freeN(innerverts);
}
static void fill_tri_triple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty)
{
EditVert **verts[3], ***innerverts;
short vertsize, i, j;
EditFace *hold;
EditEdge temp;
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
verts[0] = BLI_ghash_lookup(gh, efa->e1);
verts[1] = BLI_ghash_lookup(gh, efa->e2);
verts[2] = BLI_ghash_lookup(gh, efa->e3);
//This is the index size of the verts array
vertsize = numcuts+2;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);}
if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);}
if(verts[2][0] != efa->v3) {flipvertarray(verts[2],numcuts+2);}
/*
We should have something like this now
3
3 2 1 0
0|---*---*---|3
| /
1 1* *2
| /
2* *1 2
| /
3|/
0
we will fill a 2 dim array of editvert*s to make filling easier
3
0 0---1---2---3---4
| / | / |/ | /
1 0---1----2---3
1 | / | / | /
2 0----1---2 2
| / | /
|/ |/
3 0---1
| /
|/
4 0
*/
innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"tri-tri subdiv inner verts outer array");
for(i=0;i<numcuts+2;i++) {
innerverts[i] = MEM_mallocN(sizeof(EditVert*)*((numcuts+2)-i),"tri-tri subdiv inner verts inner array");
}
//top row is e3 backwards
for(i=0;i<numcuts+2;i++) {
innerverts[0][i] = verts[2][(numcuts+1)-i];
}
for(i=1;i<=numcuts+1;i++) {
//fake edge, first vert is from e1, last is from e2
temp.v1= innerverts[i][0] = verts[0][i];
temp.v2= innerverts[i][(numcuts+1)-i] = verts[1][(numcuts+1)-i];
for(j=1;j<(numcuts+1)-i;j++) {
float percent= (float)j/(float)((numcuts+1)-i);
innerverts[i][((numcuts+1)-i)-j]= subdivide_edge_addvert(&temp, rad, beauty, 1-percent);
}
}
// Now fill the verts with happy little tris :)
for(i=0;i<=numcuts+1;i++) {
for(j=0;j<(numcuts+1)-i;j++) {
//We always do the first tri
hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],NULL,NULL,NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
if(i != 0) { hold->e1->f2 |= EDGEINNER; }
if(j != 0) { hold->e2->f2 |= EDGEINNER; }
if(j+1 != (numcuts+1)-i) {hold->e3->f2 |= EDGEINNER;}
facecopy(efa,hold);
//if there are more to come, we do the 2nd
if(j+1 <= numcuts-i) {
hold = addfacelist(innerverts[i+1][j],innerverts[i+1][j+1],innerverts[i][j+1],NULL,NULL,NULL);
facecopy(efa,hold);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
}
}
}
// Clean up our dynamic multi-dim array
for(i=0;i<numcuts+2;i++) {
MEM_freeN(innerverts[i]);
}
MEM_freeN(innerverts);
}
//Next two fill types are for knife exact only and are provided to allow for knifing through vertices
//This means there is no multicut!
static void fill_quad_doublevert(EditFace *efa, int v1, int v2)
{
EditFace *hold;
/*
Depending on which two vertices have been knifed through (v1 and v2), we
triangulate like the patterns below.
X-------| |-------X
| \ | | / |
| \ | | / |
| \ | | / |
--------X X--------
*/
if(v1 == 1 && v2 == 3){
hold= addfacelist(efa->v1, efa->v2, efa->v3, 0, efa, NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e3->f2 |= EDGEINNER;
facecopy(efa, hold);
hold= addfacelist(efa->v1, efa->v3, efa->v4, 0, efa, NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e1->f2 |= EDGEINNER;
facecopy(efa, hold);
}
else{
hold= addfacelist(efa->v1, efa->v2, efa->v4, 0, efa, NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e2->f2 |= EDGEINNER;
facecopy(efa, hold);
hold= addfacelist(efa->v2, efa->v3, efa->v4, 0, efa, NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e3->f2 |= EDGEINNER;
facecopy(efa, hold);
}
}
static void fill_quad_singlevert(EditFace *efa, struct GHash *gh)
{
EditEdge *cedge=NULL;
EditVert *v[4], **verts;
EditFace *hold;
short start=0, end, left, right, vertsize;
v[0] = efa->v1;
v[1] = efa->v2;
v[2] = efa->v3;
v[3] = efa->v4;
if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;}
// Point verts to the array of new verts for cedge
verts = BLI_ghash_lookup(gh, cedge);
//This is the index size of the verts array
vertsize = 3;
// Is the original v1 the same as the first vert on the selected edge?
// if not, the edge is running the opposite direction in this face so flip
// the array to the correct direction
if(verts[0] != v[start]) {flipvertarray(verts,3);}
end = (start+1)%4;
left = (start+2)%4;
right = (start+3)%4;
/*
We should have something like this now
end start
2 1 0
|-----*-----|
| |
| |
| |
-------------
left right
where start,end,left, right are indexes of EditFace->v1, etc (stored in v)
and 0,1,2 are the indexes of the new verts stored in verts. We fill like
this, depending on whether its vertex 'left' or vertex 'right' thats
been knifed through...
|---*---| |---*---|
| / | | \ |
| / | | \ |
|/ | | \|
X-------- --------X
*/
if(v[left]->f1) {
//triangle is composed of cutvert, end and left
hold = addfacelist(verts[1],v[end],v[left],NULL, NULL,NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e3->f2 |= EDGEINNER;
facecopy(efa, hold);
//quad is composed of cutvert, left, right and start
hold = addfacelist(verts[1],v[left],v[right],v[start], NULL, NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e4->f2 |= EDGENEW;
hold->e1->f2 |= EDGEINNER;
facecopy(efa, hold);
}
else if(v[right]->f1) {
//triangle is composed of cutvert, right and start
hold = addfacelist(verts[1],v[right],v[start], NULL, NULL, NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e1->f2 |= EDGEINNER;
facecopy(efa, hold);
//quad is composed of cutvert, end, left, right
hold = addfacelist(verts[1],v[end], v[left], v[right], NULL, NULL);
hold->e1->f2 |= EDGENEW;
hold->e2->f2 |= EDGENEW;
hold->e3->f2 |= EDGENEW;
hold->e4->f2 |= EDGENEW;
hold->e4->f2 |= EDGEINNER;
facecopy(efa, hold);
}
}
// This function takes an example edge, the current point to create and
// the total # of points to create, then creates the point and return the
// editvert pointer to it.
static EditVert *subdivideedgenum(EditEdge *edge, int curpoint, int totpoint, float rad, int beauty)
{
EditVert *ev;
float percent;
if (beauty & (B_PERCENTSUBD) && totpoint == 1)
//percent=(float)(edge->tmp.l)/32768.0f;
percent= edge->tmp.fp;
else
percent= (float)curpoint/(float)(totpoint+1);
ev= subdivide_edge_addvert(edge, rad, beauty, percent);
ev->f = edge->v1->f;
return ev;
}
void esubdivideflag(int flag, float rad, int beauty, int numcuts, int seltype)
{
EditMesh *em = G.editMesh;
EditFace *ef;
EditEdge *eed, *cedge, *sort[4];
EditVert *eve, **templist;
struct GHash *gh;
float length[4], v1mat[3], v2mat[3], v3mat[3], v4mat[3];
int i, j, edgecount, touchcount, facetype,hold;
ModifierData *md= G.obedit->modifiers.first;
if(multires_test()) return;
//Set faces f1 to 0 cause we need it later
for(ef=em->faces.first;ef;ef = ef->next) ef->f1 = 0;
for(eve=em->verts.first; eve; eve=eve->next) {
if(!(beauty & B_KNIFE)) /* knife sets this flag for vertex cuts */
eve->f1 = 0;
eve->f2 = 0;
}
for (; md; md=md->next) {
if ((md->type==eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
MirrorModifierData *mmd = (MirrorModifierData*) md;
if(mmd->flag & MOD_MIR_CLIPPING) {
for (eve= em->verts.first; eve; eve= eve->next) {
eve->f2= 0;
switch(mmd->axis) {
case 0:
if (fabs(eve->co[0]) < mmd->tolerance)
eve->f2 |= 1;
break;
case 1:
if (fabs(eve->co[1]) < mmd->tolerance)
eve->f2 |= 2;
break;
case 2:
if (fabs(eve->co[2]) < mmd->tolerance)
eve->f2 |= 4;
break;
}
}
}
}
}
//Flush vertex flags upward to the edges
for(eed = em->edges.first;eed;eed = eed->next) {
//if(eed->f & flag && eed->v1->f == eed->v2->f) {
// eed->f |= eed->v1->f;
// }
eed->f2 = 0;
if(eed->f & flag) {
eed->f2 |= EDGEOLD;
}
}
// We store an array of verts for each edge that is subdivided,
// we put this array as a value in a ghash which is keyed by the EditEdge*
// Now for beauty subdivide deselect edges based on length
if(beauty & B_BEAUTY) {
for(ef = em->faces.first;ef;ef = ef->next) {
if(!ef->v4) {
continue;
}
if(ef->f & SELECT) {
copy_v3_v3(v1mat, ef->v1->co);
copy_v3_v3(v2mat, ef->v2->co);
copy_v3_v3(v3mat, ef->v3->co);
copy_v3_v3(v4mat, ef->v4->co);
mul_mat3_m4_v3(G.obedit->obmat, v1mat);
mul_mat3_m4_v3(G.obedit->obmat, v2mat);
mul_mat3_m4_v3(G.obedit->obmat, v3mat);
mul_mat3_m4_v3(G.obedit->obmat, v4mat);
length[0] = len_v3v3(v1mat, v2mat);
length[1] = len_v3v3(v2mat, v3mat);
length[2] = len_v3v3(v3mat, v4mat);
length[3] = len_v3v3(v4mat, v1mat);
sort[0] = ef->e1;
sort[1] = ef->e2;
sort[2] = ef->e3;
sort[3] = ef->e4;
// Beauty Short Edges
if(beauty & B_BEAUTY_SHORT) {
for(j=0;j<2;j++) {
hold = -1;
for(i=0;i<4;i++) {
if(length[i] < 0) {
continue;
} else if(hold == -1) {
hold = i;
} else {
if(length[hold] < length[i]) {
hold = i;
}
}
}
sort[hold]->f &= ~SELECT;
sort[hold]->f2 |= EDGENEW;
length[hold] = -1;
}
}
// Beauty Long Edges
else {
for(j=0;j<2;j++) {
hold = -1;
for(i=0;i<4;i++) {
if(length[i] < 0) {
continue;
} else if(hold == -1) {
hold = i;
} else {
if(length[hold] > length[i]) {
hold = i;
}
}
}
sort[hold]->f &= ~SELECT;
sort[hold]->f2 |= EDGENEW;
length[hold] = -1;
}
}
}
}
}
gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
// If we are knifing, We only need the selected edges that were cut, so deselect if it was not cut
if(beauty & B_KNIFE) {
for(eed= em->edges.first;eed;eed=eed->next) {
if( eed->tmp.fp == 0 ) {
EM_select_edge(eed,0);
}
}
}
// So for each edge, if it is selected, we allocate an array of size cuts+2
// so we can have a place for the v1, the new verts and v2
for(eed=em->edges.first;eed;eed = eed->next) {
if(eed->f & flag) {
templist = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"vertlist");
templist[0] = eed->v1;
for(i=0;i<numcuts;i++) {
// This function creates the new vert and returns it back
// to the array
templist[i+1] = subdivideedgenum(eed, i+1, numcuts, rad, beauty);
//while we are here, we can copy edge info from the original edge
cedge = addedgelist(templist[i],templist[i+1],eed);
// Also set the edge f2 to EDGENEW so that we can use this info later
cedge->f2 = EDGENEW;
}
templist[i+1] = eed->v2;
//Do the last edge too
cedge = addedgelist(templist[i],templist[i+1],eed);
cedge->f2 = EDGENEW;
// Now that the edge is subdivided, we can put its verts in the ghash
BLI_ghash_insert(gh, eed, templist);
}
}
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
// Now for each face in the mesh we need to figure out How many edges were cut
// and which filling method to use for that face
for(ef = em->faces.first;ef;ef = ef->next) {
edgecount = 0;
facetype = 3;
if(ef->e1->f & flag) {edgecount++;}
if(ef->e2->f & flag) {edgecount++;}
if(ef->e3->f & flag) {edgecount++;}
if(ef->v4) {
facetype = 4;
if(ef->e4->f & flag) {edgecount++;}
}
if(facetype == 4) {
switch(edgecount) {
case 0:
if(beauty & B_KNIFE && numcuts == 1){
/*Test for when knifing through two opposite verts but no edges*/
touchcount = 0;
if(ef->v1->f1) touchcount++;
if(ef->v2->f1) touchcount++;
if(ef->v3->f1) touchcount++;
if(ef->v4->f1) touchcount++;
if(touchcount == 2){
if(ef->v1->f1 && ef->v3->f1){
ef->f1 = SELECT;
fill_quad_doublevert(ef, 1, 3);
}
else if(ef->v2->f1 && ef->v4->f1){
ef->f1 = SELECT;
fill_quad_doublevert(ef, 2, 4);
}
}
}
break;
case 1:
if(beauty & B_KNIFE && numcuts == 1){
/*Test for when knifing through an edge and one vert*/
touchcount = 0;
if(ef->v1->f1) touchcount++;
if(ef->v2->f1) touchcount++;
if(ef->v3->f1) touchcount++;
if(ef->v4->f1) touchcount++;
if(touchcount == 1){
if( (ef->e1->f & flag && ( !ef->e1->v1->f1 && !ef->e1->v2->f1 )) ||
(ef->e2->f & flag && ( !ef->e2->v1->f1 && !ef->e2->v2->f1 )) ||
(ef->e3->f & flag && ( !ef->e3->v1->f1 && !ef->e3->v2->f1 )) ||
(ef->e4->f & flag && ( !ef->e4->v1->f1 && !ef->e4->v2->f1 )) ){
ef->f1 = SELECT;
fill_quad_singlevert(ef, gh);
}
else{
ef->f1 = SELECT;
fill_quad_single(ef, gh, numcuts, seltype);
}
}
else{
ef->f1 = SELECT;
fill_quad_single(ef, gh, numcuts, seltype);
}
}
else{
ef->f1 = SELECT;
fill_quad_single(ef, gh, numcuts, seltype);
}
break;
case 2: ef->f1 = SELECT;
// if there are 2, we check if edge 1 and 3 are either both on or off that way
// we can tell if the selected pair is Adjacent or Opposite of each other
if((ef->e1->f & flag && ef->e3->f & flag) ||
(ef->e2->f & flag && ef->e4->f & flag)) {
fill_quad_double_op(ef, gh, numcuts);
}else{
switch(G.scene->toolsettings->cornertype) {
case 0: fill_quad_double_adj_path(ef, gh, numcuts); break;
case 1: fill_quad_double_adj_inner(ef, gh, numcuts); break;
case 2: fill_quad_double_adj_fan(ef, gh, numcuts); break;
}
}
break;
case 3: ef->f1 = SELECT;
fill_quad_triple(ef, gh, numcuts);
break;
case 4: ef->f1 = SELECT;
fill_quad_quadruple(ef, gh, numcuts, rad, beauty);
break;
}
} else {
switch(edgecount) {
case 0: break;
case 1: ef->f1 = SELECT;
fill_tri_single(ef, gh, numcuts, seltype);
break;
case 2: ef->f1 = SELECT;
fill_tri_double(ef, gh, numcuts);
break;
case 3: ef->f1 = SELECT;
fill_tri_triple(ef, gh, numcuts, rad, beauty);
break;
}
}
}
// Delete Old Edges and Faces
for(eed = em->edges.first;eed;eed = eed->next) {
if(BLI_ghash_haskey(gh,eed)) {
eed->f1 = SELECT;
} else {
eed->f1 = 0;
}
}
free_tagged_edges_faces(em->edges.first, em->faces.first);
if(seltype == SUBDIV_SELECT_ORIG && G.qual != LR_CTRLKEY) {
/* bugfix: vertex could get flagged as "not-selected"
// solution: clear flags before, not at the same time as setting SELECT flag -dg
*/
for(eed = em->edges.first;eed;eed = eed->next) {
if(!(eed->f2 & EDGENEW || eed->f2 & EDGEOLD)) {
eed->f &= !flag;
EM_select_edge(eed,0);
}
}
for(eed = em->edges.first;eed;eed = eed->next) {
if(eed->f2 & EDGENEW || eed->f2 & EDGEOLD) {
eed->f |= flag;
EM_select_edge(eed,1);
}
}
} else if ((seltype == SUBDIV_SELECT_INNER || seltype == SUBDIV_SELECT_INNER_SEL)|| G.qual == LR_CTRLKEY) {
for(eed = em->edges.first;eed;eed = eed->next) {
if(eed->f2 & EDGEINNER) {
eed->f |= flag;
EM_select_edge(eed,1);
if(eed->v1->f & EDGEINNER) eed->v1->f |= SELECT;
if(eed->v2->f & EDGEINNER) eed->v2->f |= SELECT;
}else{
eed->f &= !flag;
EM_select_edge(eed,0);
}
}
} else if(seltype == SUBDIV_SELECT_LOOPCUT){
for(eed = em->edges.first;eed;eed = eed->next) {
if(eed->f2 & DOUBLEOPFILL){
eed->f |= flag;
EM_select_edge(eed,1);
}else{
eed->f &= !flag;
EM_select_edge(eed,0);
}
}
}
if(G.scene->selectmode & SCE_SELECT_VERTEX) {
for(eed = em->edges.first;eed;eed = eed->next) {
if(eed->f & SELECT) {
eed->v1->f |= SELECT;
eed->v2->f |= SELECT;
}
}
}
//fix hide flags for edges. First pass, hide edges of hidden faces
for(ef=em->faces.first; ef; ef=ef->next){
if(ef->h){
ef->e1->h |= 1;
ef->e2->h |= 1;
ef->e3->h |= 1;
if(ef->e4) ef->e4->h |= 1;
}
}
//second pass: unhide edges of visible faces adjacent to hidden faces
for(ef=em->faces.first; ef; ef=ef->next){
if(ef->h == 0){
ef->e1->h &= ~1;
ef->e2->h &= ~1;
ef->e3->h &= ~1;
if(ef->e4) ef->e4->h &= ~1;
}
}
// Free the ghash and call MEM_freeN on all the value entries to return
// that memory
BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN);
EM_selectmode_flush();
for(ef=em->faces.first;ef;ef = ef->next) {
if(ef->e4) {
if( (ef->e1->f & SELECT && ef->e2->f & SELECT) &&
(ef->e3->f & SELECT && ef->e4->f & SELECT) ) {
ef->f |= SELECT;
}
} else {
if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && ef->e3->f & SELECT) {
ef->f |= SELECT;
}
}
}
recalc_editnormals();
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
}
static int count_selected_edges(EditEdge *ed)
{
int totedge = 0;
while(ed) {
ed->tmp.p = 0;
if( ed->f & SELECT ) totedge++;
ed= ed->next;
}
return totedge;
}
/* hurms, as if this makes code readable! It's pointerpointer hiding... (ton) */
typedef EditFace *EVPtr;
typedef EVPtr EVPTuple[2];
/** builds EVPTuple array efaa of face tuples (in fact pointers to EditFaces)
sharing one edge.
arguments: selected edge list, face list.
Edges will also be tagged accordingly (see eed->f2) */
static int collect_quadedges(EVPTuple *efaa, EditEdge *eed, EditFace *efa)
{
EditEdge *e1, *e2, *e3;
EVPtr *evp;
int i = 0;
/* run through edges, if selected, set pointer edge-> facearray */
while(eed) {
eed->f2= 0;
eed->f1= 0;
if( eed->f & SELECT ) {
eed->tmp.p = (EditVert *) (&efaa[i]);
i++;
}
else eed->tmp.p = NULL;
eed= eed->next;
}
/* find edges pointing to 2 faces by procedure:
- run through faces and their edges, increase
face counter e->f1 for each face
*/
while(efa) {
efa->f1= 0;
if(efa->v4==0 && (efa->f & SELECT)) { /* if selected triangle */
e1= efa->e1;
e2= efa->e2;
e3= efa->e3;
if(e1->f2<3 && e1->tmp.p) {
if(e1->f2<2) {
evp= (EVPtr *) e1->tmp.p;
evp[(int)e1->f2] = efa;
}
e1->f2+= 1;
}
if(e2->f2<3 && e2->tmp.p) {
if(e2->f2<2) {
evp= (EVPtr *) e2->tmp.p;
evp[(int)e2->f2]= efa;
}
e2->f2+= 1;
}
if(e3->f2<3 && e3->tmp.p) {
if(e3->f2<2) {
evp= (EVPtr *) e3->tmp.p;
evp[(int)e3->f2]= efa;
}
e3->f2+= 1;
}
}
else {
/* set to 3 to make sure these are not flipped or joined */
efa->e1->f2= 3;
efa->e2->f2= 3;
efa->e3->f2= 3;
if (efa->e4) efa->e4->f2= 3;
}
efa= efa->next;
}
return i;
}
/* returns vertices of two adjacent triangles forming a quad
- can be righthand or lefthand
4-----3
|\ |
| \ 2 | <- efa1
| \ |
efa-> | 1 \ |
| \|
1-----2
*/
#define VTEST(face, num, other) \
(face->v##num != other->v1 && face->v##num != other->v2 && face->v##num != other->v3)
static void givequadverts(EditFace *efa, EditFace *efa1, EditVert **v1, EditVert **v2, EditVert **v3, EditVert **v4, int *vindex)
{
if VTEST(efa, 1, efa1) {
*v1= efa->v1;
*v2= efa->v2;
vindex[0]= 0;
vindex[1]= 1;
}
else if VTEST(efa, 2, efa1) {
*v1= efa->v2;
*v2= efa->v3;
vindex[0]= 1;
vindex[1]= 2;
}
else if VTEST(efa, 3, efa1) {
*v1= efa->v3;
*v2= efa->v1;
vindex[0]= 2;
vindex[1]= 0;
}
if VTEST(efa1, 1, efa) {
*v3= efa1->v1;
*v4= efa1->v2;
vindex[2]= 0;
vindex[3]= 1;
}
else if VTEST(efa1, 2, efa) {
*v3= efa1->v2;
*v4= efa1->v3;
vindex[2]= 1;
vindex[3]= 2;
}
else if VTEST(efa1, 3, efa) {
*v3= efa1->v3;
*v4= efa1->v1;
vindex[2]= 2;
vindex[3]= 0;
}
else
*v3= *v4= NULL;
}
/* Helper functions for edge/quad edit features*/
static void untag_edges(EditFace *f)
{
f->e1->f1 = 0;
f->e2->f1 = 0;
f->e3->f1 = 0;
if (f->e4) f->e4->f1 = 0;
}
/** remove and free list of tagged edges and faces */
static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa)
{
EditMesh *em= G.editMesh;
EditEdge *nexted;
EditFace *nextvl;
while(efa) {
nextvl= efa->next;
if(efa->f1) {
BLI_remlink(&em->faces, efa);
free_editface(efa);
}
else
/* avoid deleting edges that are still in use */
untag_edges(efa);
efa= nextvl;
}
while(eed) {
nexted= eed->next;
if(eed->f1) {
remedge(eed);
free_editedge(eed);
}
eed= nexted;
}
}
/* note; the EM_selectmode_set() calls here illustrate how badly constructed it all is... from before the
edge/face flags, with very mixed results.... */
void beauty_fill(void)
{
EditMesh *em = G.editMesh;
EditVert *v1, *v2, *v3, *v4;
EditEdge *eed, *nexted;
EditEdge dia1, dia2;
EditFace *efa, *w;
// void **efaar, **efaa;
EVPTuple *efaar;
EVPtr *efaa;
float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2;
int totedge, ok, notbeauty=8, onedone, vindex[4];
if(multires_test()) return;
/* - all selected edges with two faces
* - find the faces: store them in edges (using datablock)
* - per edge: - test convex
* - test edge: flip?
* - if true: remedge, addedge, all edges at the edge get new face pointers
*/
EM_selectmode_set(); // makes sure in selectmode 'face' the edges of selected faces are selected too
totedge = count_selected_edges(em->edges.first);
if(totedge==0) return;
/* temp block with face pointers */
efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "beautyfill");
while (notbeauty) {
notbeauty--;
ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
/* there we go */
onedone= 0;
eed= em->edges.first;
while(eed) {
nexted= eed->next;
/* f2 is set in collect_quadedges() */
if(eed->f2==2 && eed->h==0) {
efaa = (EVPtr *) eed->tmp.p;
/* none of the faces should be treated before, nor be part of fgon */
ok= 1;
efa= efaa[0];
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
if(efa->fgonf) ok= 0;
efa= efaa[1];
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
if(efa->fgonf) ok= 0;
if(ok) {
/* test convex */
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
if(v1 && v2 && v3 && v4) {
if( convex(v1->co, v2->co, v3->co, v4->co) ) {
/* test edges */
if( (v1) > (v3) ) {
dia1.v1= v3;
dia1.v2= v1;
}
else {
dia1.v1= v1;
dia1.v2= v3;
}
if( (v2) > (v4) ) {
dia2.v1= v4;
dia2.v2= v2;
}
else {
dia2.v1= v2;
dia2.v2= v4;
}
/* testing rule:
* the area divided by the total edge lengths
*/
len1= 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);
ok= 0;
if(fac1 > fac2) {
if(dia2.v1==eed->v1 && dia2.v2==eed->v2) {
eed->f1= 1;
efa= efaa[0];
efa->f1= 1;
efa= efaa[1];
efa->f1= 1;
w= EM_face_from_faces(efaa[0], efaa[1],
vindex[0], vindex[1], 4+vindex[2], -1);
w->f |= SELECT;
w= EM_face_from_faces(efaa[0], efaa[1],
vindex[0], 4+vindex[2], 4+vindex[3], -1);
w->f |= SELECT;
onedone= 1;
}
}
else if(fac1 < fac2) {
if(dia1.v1==eed->v1 && dia1.v2==eed->v2) {
eed->f1= 1;
efa= efaa[0];
efa->f1= 1;
efa= efaa[1];
efa->f1= 1;
w= EM_face_from_faces(efaa[0], efaa[1],
vindex[1], 4+vindex[2], 4+vindex[3], -1);
w->f |= SELECT;
w= EM_face_from_faces(efaa[0], efaa[1],
vindex[0], 4+vindex[1], 4+vindex[3], -1);
w->f |= SELECT;
onedone= 1;
}
}
}
}
}
}
eed= nexted;
}
free_tagged_edges_faces(em->edges.first, em->faces.first);
if(onedone==0) break;
EM_selectmode_set(); // new edges/faces were added
}
MEM_freeN(efaar);
EM_select_flush();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Beauty Fill");
}
/* ******************** BEGIN TRIANGLE TO QUAD ************************************* */
static float measure_facepair(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, float limit)
{
/*gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make*/
/*Note: this is more complicated than it needs to be and should be cleaned up...*/
float measure = 0.0, noA1[3], noA2[3], noB1[3], noB2[3], normalADiff, normalBDiff,
edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3], diff,
minarea, maxarea, areaA, areaB;
/*First Test: Normal difference*/
normal_tri_v3( noA1,v1->co, v2->co, v3->co);
normal_tri_v3( noA2,v1->co, v3->co, v4->co);
if(noA1[0] == noA2[0] && noA1[1] == noA2[1] && noA1[2] == noA2[2]) normalADiff = 0.0;
else normalADiff = angle_v2v2(noA1, noA2);
//if(!normalADiff) normalADiff = 179;
normal_tri_v3( noB1,v2->co, v3->co, v4->co);
normal_tri_v3( noB2,v4->co, v1->co, v2->co);
if(noB1[0] == noB2[0] && noB1[1] == noB2[1] && noB1[2] == noB2[2]) normalBDiff = 0.0;
else normalBDiff = angle_v2v2(noB1, noB2);
//if(!normalBDiff) normalBDiff = 179;
measure += (normalADiff/360) + (normalBDiff/360);
if(measure > limit) return measure;
/*Second test: Colinearity*/
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);
diff = 0.0;
diff = (
fabs(angle_v2v2(edgeVec1, edgeVec2) - 90) +
fabs(angle_v2v2(edgeVec2, edgeVec3) - 90) +
fabs(angle_v2v2(edgeVec3, edgeVec4) - 90) +
fabs(angle_v2v2(edgeVec4, edgeVec1) - 90)) / 360;
if(!diff) return 0.0;
measure += diff;
if(measure > limit) return measure;
/*Third test: Concavity*/
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.005
#define T2QCOL_LIMIT 3
static int compareFaceAttribs(EditFace *f1, EditFace *f2, EditEdge *eed)
{
/*Test to see if the per-face attributes for the joining edge match within limit*/
MTFace *tf1, *tf2;
unsigned int *col1, *col2;
short i,attrok=0, flag = G.scene->toolsettings->editbutflag, fe1[2], fe2[2];
tf1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MTFACE);
tf2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MTFACE);
col1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MCOL);
col2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MCOL);
/*store indices for faceedges*/
f1->v1->f1 = 0;
f1->v2->f1 = 1;
f1->v3->f1 = 2;
fe1[0] = eed->v1->f1;
fe1[1] = eed->v2->f1;
f2->v1->f1 = 0;
f2->v2->f1 = 1;
f2->v3->f1 = 2;
fe2[0] = eed->v1->f1;
fe2[1] = eed->v2->f1;
/*compare faceedges for each face attribute. Additional per face attributes can be added later*/
/*do UVs*/
if(flag & B_JOINTRIA_UV){
if(tf1 == NULL || tf2 == NULL) attrok |= B_JOINTRIA_UV;
else if(tf1->tpage != tf2->tpage); /*do nothing*/
else{
for(i = 0; i < 2; i++){
if(tf1->uv[fe1[i]][0] + T2QUV_LIMIT > tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][0] - T2QUV_LIMIT < tf2->uv[fe2[i]][0] &&
tf1->uv[fe1[i]][1] + T2QUV_LIMIT > tf2->uv[fe2[i]][1] && tf1->uv[fe1[i]][1] - T2QUV_LIMIT < tf2->uv[fe2[i]][1]) attrok |= B_JOINTRIA_UV;
}
}
}
/*do VCOLs*/
if(flag & B_JOINTRIA_VCOL){
if(!col1 || !col2) attrok |= B_JOINTRIA_VCOL;
else{
char *f1vcol, *f2vcol;
for(i = 0; i < 2; i++){
f1vcol = (char *)&(col1[fe1[i]]);
f2vcol = (char *)&(col2[fe2[i]]);
/*compare f1vcol with f2vcol*/
if( f1vcol[1] + T2QCOL_LIMIT > f2vcol[1] && f1vcol[1] - T2QCOL_LIMIT < f2vcol[1] &&
f1vcol[2] + T2QCOL_LIMIT > f2vcol[2] && f1vcol[2] - T2QCOL_LIMIT < f2vcol[2] &&
f1vcol[3] + T2QCOL_LIMIT > f2vcol[3] && f1vcol[3] - T2QCOL_LIMIT < f2vcol[3]) attrok |= B_JOINTRIA_VCOL;
}
}
}
if( ((attrok & B_JOINTRIA_UV) == (flag & B_JOINTRIA_UV)) && ((attrok & B_JOINTRIA_VCOL) == (flag & B_JOINTRIA_VCOL)) ) return 1;
return 0;
}
static int fplcmp(const void *v1, const void *v2)
{
const EditEdge *e1= *((EditEdge**)v1), *e2=*((EditEdge**)v2);
if( e1->crease > e2->crease) return 1;
else if( e1->crease < e2->crease) return -1;
return 0;
}
/*Bitflags for edges.*/
#define T2QDELETE 1
#define T2QCOMPLEX 2
#define T2QJOIN 4
void join_triangles(void)
{
EditMesh *em=G.editMesh;
EditVert *v1, *v2, *v3, *v4, *eve;
EditEdge *eed, **edsortblock = NULL, **edb = NULL;
EditFace *efa;
EVPTuple *efaar = NULL;
EVPtr *efaa = NULL;
float *creases = NULL;
float measure; /*Used to set tolerance*/
float limit = G.scene->toolsettings->jointrilimit;
int i, ok, totedge=0, totseledge=0, complexedges, vindex[4];
/*test for multi-resolution data*/
if(multires_test()) return;
/*if we take a long time on very dense meshes we want waitcursor to display*/
waitcursor(1);
totseledge = count_selected_edges(em->edges.first);
if(totseledge==0) return;
/*abusing crease value to store weights for edge pairs. Nasty*/
for(eed=em->edges.first; eed; eed=eed->next) totedge++;
if(totedge) creases = MEM_callocN(sizeof(float) * totedge, "Join Triangles Crease Array");
for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++){
creases[i] = eed->crease;
eed->crease = 0.0;
}
/*clear temp flags*/
for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = eve->f2 = 0;
for(eed=em->edges.first; eed; eed=eed->next) eed->f2 = eed->f1 = 0;
for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = efa->tmp.l = 0;
/*For every selected 2 manifold edge, create pointers to its two faces.*/
efaar= (EVPTuple *) MEM_callocN(totseledge * sizeof(EVPTuple), "Tri2Quad");
ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
complexedges = 0;
if(ok){
/*clear tmp.l flag and store number of faces that are selected and coincident to current face here.*/
for(eed=em->edges.first; eed; eed=eed->next){
/* eed->f2 is 2 only if this edge is part of exactly two
triangles, and both are selected, and it has EVPTuple assigned */
if(eed->f2 == 2){
efaa= (EVPtr *) eed->tmp.p;
efaa[0]->tmp.l++;
efaa[1]->tmp.l++;
}
}
for(eed=em->edges.first; eed; eed=eed->next){
if(eed->f2 == 2){
efaa= (EVPtr *) eed->tmp.p;
v1 = v2 = v3 = v4 = NULL;
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
if(v1 && v2 && v3 && v4){
/*test if simple island first. This mimics 2.42 behaviour and the tests are less restrictive.*/
if(efaa[0]->tmp.l == 1 && efaa[1]->tmp.l == 1){
if( convex(v1->co, v2->co, v3->co, v4->co) ){
eed->f1 |= T2QJOIN;
efaa[0]->f1 = 1; //mark for join
efaa[1]->f1 = 1; //mark for join
}
}
else{
/* The face pair is part of a 'complex' island, so the rules for dealing with it are more involved.
Depending on what options the user has chosen, this face pair can be 'thrown out' based upon the following criteria:
1: the two faces do not share the same material
2: the edge joining the two faces is marked as sharp.
3: the two faces UV's do not make a good match
4: the two faces Vertex colors do not make a good match
If the face pair passes all the applicable tests, it is then given a 'weight' with the measure_facepair() function.
This measures things like concavity, colinearity ect. If this weight is below the threshold set by the user
the edge joining them is marked as being 'complex' and will be compared against other possible pairs which contain one of the
same faces in the current pair later.
This technique is based upon an algorithm that Campbell Barton developed for his Tri2Quad script that was previously part of
the python scripts bundled with Blender releases.
*/
if(G.scene->toolsettings->editbutflag & B_JOINTRIA_SHARP && eed->sharp); /*do nothing*/
else if(G.scene->toolsettings->editbutflag & B_JOINTRIA_MAT && efaa[0]->mat_nr != efaa[1]->mat_nr); /*do nothing*/
else if(((G.scene->toolsettings->editbutflag & B_JOINTRIA_UV) || (G.scene->toolsettings->editbutflag & B_JOINTRIA_VCOL)) &&
compareFaceAttribs(efaa[0], efaa[1], eed) == 0); /*do nothing*/
else{
measure = measure_facepair(v1, v2, v3, v4, limit);
if(measure < limit){
complexedges++;
eed->f1 |= T2QCOMPLEX;
eed->crease = measure; /*we dont mark edges for join yet*/
}
}
}
}
}
}
/*Quicksort the complex edges according to their weighting*/
if(complexedges){
edsortblock = edb = MEM_callocN(sizeof(EditEdge*) * complexedges, "Face Pairs quicksort Array");
for(eed = em->edges.first; eed; eed=eed->next){
if(eed->f1 & T2QCOMPLEX){
*edb = eed;
edb++;
}
}
qsort(edsortblock, complexedges, sizeof(EditEdge*), fplcmp);
/*now go through and mark the edges who get the highest weighting*/
for(edb=edsortblock, i=0; i < complexedges; edb++, i++){
efaa = (EVPtr *)((*edb)->tmp.p); /*suspect!*/
if( !efaa[0]->f1 && !efaa[1]->f1){
efaa[0]->f1 = 1; //mark for join
efaa[1]->f1 = 1; //mark for join
(*edb)->f1 |= T2QJOIN;
}
}
}
/*finally go through all edges marked for join (simple and complex) and create new faces*/
for(eed=em->edges.first; eed; eed=eed->next){
if(eed->f1 & T2QJOIN){
efaa= (EVPtr *)eed->tmp.p;
v1 = v2 = v3 = v4 = NULL;
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
if((v1 && v2 && v3 && v4) && (exist_face(v1, v2, v3, v4)==0)){ /*exist_face is very slow! Needs to be adressed.*/
/*flag for delete*/
eed->f1 |= T2QDELETE;
/*create new quad and select*/
efa = EM_face_from_faces(efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], 4+vindex[3]);
EM_select_face(efa,1);
}
else{
efaa[0]->f1 = 0;
efaa[1]->f1 = 0;
}
}
}
}
/*free data and cleanup*/
if(creases){
for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++) eed->crease = creases[i];
MEM_freeN(creases);
}
for(eed=em->edges.first; eed; eed=eed->next){
if(eed->f1 & T2QDELETE) eed->f1 = 1;
else eed->f1 = 0;
}
free_tagged_edges_faces(em->edges.first, em->faces.first);
if(efaar) MEM_freeN(efaar);
if(edsortblock) MEM_freeN(edsortblock);
EM_selectmode_flush();
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
waitcursor(0);
BIF_undo_push("Convert Triangles to Quads");
}
/* ******************** END TRIANGLE TO QUAD ************************************* */
#define FACE_MARKCLEAR(f) (f->f1 = 1)
/* quick hack, basically a copy of beauty_fill */
void edge_flip(void)
{
EditMesh *em = G.editMesh;
EditVert *v1, *v2, *v3, *v4;
EditEdge *eed, *nexted;
EditFace *efa, *w;
//void **efaar, **efaa;
EVPTuple *efaar;
EVPtr *efaa;
int totedge, ok, vindex[4];
/* - all selected edges with two faces
* - find the faces: store them in edges (using datablock)
* - per edge: - test convex
* - test edge: flip?
- if true: remedge, addedge, all edges at the edge get new face pointers
*/
EM_selectmode_flush(); // makes sure in selectmode 'face' the edges of selected faces are selected too
totedge = count_selected_edges(em->edges.first);
if(totedge==0) return;
/* temporary array for : edge -> face[1], face[2] */
efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "edgeflip");
ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
eed= em->edges.first;
while(eed) {
nexted= eed->next;
if(eed->f2==2) { /* points to 2 faces */
efaa= (EVPtr *) eed->tmp.p;
/* don't do it if flagged */
ok= 1;
efa= efaa[0];
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
efa= efaa[1];
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
if(ok) {
/* test convex */
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
/*
4-----3 4-----3
|\ | | /|
| \ 1 | | 1 / |
| \ | -> | / |
| 0 \ | | / 0 |
| \| |/ |
1-----2 1-----2
*/
/* make new faces */
if (v1 && v2 && v3) {
if( convex(v1->co, v2->co, v3->co, v4->co) ) {
if(exist_face(v1, v2, v3, v4)==0) {
/* outch this may break seams */
w= EM_face_from_faces(efaa[0], efaa[1], vindex[0],
vindex[1], 4+vindex[2], -1);
EM_select_face(w, 1);
/* outch this may break seams */
w= EM_face_from_faces(efaa[0], efaa[1], vindex[0],
4+vindex[2], 4+vindex[3], -1);
EM_select_face(w, 1);
}
/* tag as to-be-removed */
FACE_MARKCLEAR(efaa[1]);
FACE_MARKCLEAR(efaa[0]);
eed->f1 = 1;
} /* endif test convex */
}
}
}
eed= nexted;
}
/* clear tagged edges and faces: */
free_tagged_edges_faces(em->edges.first, em->faces.first);
MEM_freeN(efaar);
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Flip Triangle Edges");
}
static void edge_rotate(EditEdge *eed,int dir)
{
EditMesh *em = G.editMesh;
EditVert **verts[2];
EditFace *face[2], *efa, *newFace[2];
EditEdge **edges[2], **hiddenedges, *srchedge;
int facecount, p1, p2, p3, p4, fac1, fac2, i, j;
int numhidden, numshared, p[2][4];
/* check to make sure that the edge is only part of 2 faces */
facecount = 0;
for(efa = em->faces.first;efa;efa = efa->next) {
if((efa->e1 == eed || efa->e2 == eed) || (efa->e3 == eed || efa->e4 == eed)) {
if(facecount >= 2) {
/* more than two faces with this edge */
return;
}
else {
face[facecount] = efa;
facecount++;
}
}
}
if(facecount < 2)
return;
/* how many edges does each face have */
if(face[0]->e4) fac1= 4;
else fac1= 3;
if(face[1]->e4) fac2= 4;
else fac2= 3;
/* make a handy array for verts and edges */
verts[0]= &face[0]->v1;
edges[0]= &face[0]->e1;
verts[1]= &face[1]->v1;
edges[1]= &face[1]->e1;
/* we don't want to rotate edges between faces that share more than one edge */
numshared= 0;
for(i=0; i<fac1; i++)
for(j=0; j<fac2; j++)
if (edges[0][i] == edges[1][j])
numshared++;
if(numshared > 1)
return;
/* coplaner faces only please */
if(dot_v3v3(face[0]->n,face[1]->n) <= 0.000001)
return;
/* we want to construct an array of vertex indicis in both faces, starting at
the last vertex of the edge being rotated.
- first we find the two vertices that lie on the rotating edge
- then we make sure they are ordered according to the face vertex order
- and then we construct the array */
p1= p2= p3= p4= 0;
for(i=0; i<4; i++) {
if(eed->v1 == verts[0][i]) p1 = i;
if(eed->v2 == verts[0][i]) p2 = i;
if(eed->v1 == verts[1][i]) p3 = i;
if(eed->v2 == verts[1][i]) p4 = i;
}
if((p1+1)%fac1 == p2)
SWAP(int, p1, p2);
if((p3+1)%fac2 == p4)
SWAP(int, p3, p4);
for (i = 0; i < 4; i++) {
p[0][i]= (p1 + i)%fac1;
p[1][i]= (p3 + i)%fac2;
}
/* create an Array of the Edges who have h set prior to rotate */
numhidden = 0;
for(srchedge = em->edges.first;srchedge;srchedge = srchedge->next)
if(srchedge->h && ((srchedge->v1->f & SELECT) || (srchedge->v2->f & SELECT)))
numhidden++;
hiddenedges = MEM_mallocN(sizeof(EditVert*)*numhidden+1, "RotateEdgeHiddenVerts");
if(!hiddenedges) {
error("Malloc Was not happy!");
return;
}
numhidden = 0;
for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next)
if(srchedge->h && (srchedge->v1->f & SELECT || srchedge->v2->f & SELECT))
hiddenedges[numhidden++] = srchedge;
/* create the 2 new faces */
if(fac1 == 3 && fac2 == 3) {
/* no need of reverse setup */
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1);
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1);
}
else if(fac1 == 4 && fac2 == 3) {
if(dir == 1) {
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]);
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1);
} else if (dir == 2) {
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], 4+p[1][1], p[0][0], p[0][1]);
newFace[1]= EM_face_from_faces(face[1], face[0], 4+p[0][2], p[1][0], p[1][1], -1);
verts[0][p[0][2]]->f |= SELECT;
verts[1][p[1][1]]->f |= SELECT;
}
}
else if(fac1 == 3 && fac2 == 4) {
if(dir == 1) {
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1);
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]);
} else if (dir == 2) {
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][0], p[0][1], 4+p[1][2], -1);
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], 4+p[0][2]);
verts[0][p[0][1]]->f |= SELECT;
verts[1][p[1][2]]->f |= SELECT;
}
}
else if(fac1 == 4 && fac2 == 4) {
if(dir == 1) {
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]);
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]);
} else if (dir == 2) {
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], p[0][3], 4+p[1][1], 4+p[1][2]);
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][2], p[1][3], 4+p[0][1], 4+p[0][2]);
verts[0][p[0][2]]->f |= SELECT;
verts[1][p[1][2]]->f |= SELECT;
}
}
else
return; /* This should never happen */
if(dir == 1 || (fac1 == 3 && fac2 == 3)) {
verts[0][p[0][1]]->f |= SELECT;
verts[1][p[1][1]]->f |= SELECT;
}
/* copy old edge's flags to new center edge*/
for(srchedge=em->edges.first;srchedge;srchedge=srchedge->next) {
if((srchedge->v1->f & SELECT) && (srchedge->v2->f & SELECT)) {
srchedge->f = eed->f;
srchedge->h = eed->h;
srchedge->dir = eed->dir;
srchedge->seam = eed->seam;
srchedge->crease = eed->crease;
srchedge->bweight = eed->bweight;
}
}
/* resetting hidden flag */
for(numhidden--; numhidden>=0; numhidden--)
hiddenedges[numhidden]->h= 1;
/* check for orhphan edges */
for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next)
srchedge->f1= -1;
/* cleanup */
MEM_freeN(hiddenedges);
/* get rid of the old edge and faces*/
remedge(eed);
free_editedge(eed);
BLI_remlink(&em->faces, face[0]);
free_editface(face[0]);
BLI_remlink(&em->faces, face[1]);
free_editface(face[1]);
}
/* only accepts 1 selected edge, or 2 selected faces */
void edge_rotate_selected(int dir)
{
EditEdge *eed;
EditFace *efa;
short edgeCount = 0;
/*clear new flag for new edges, count selected edges */
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
eed->f1= 0;
eed->f2 &= ~2;
if(eed->f & SELECT) edgeCount++;
}
if(edgeCount>1) {
/* more selected edges, check faces */
for(efa= G.editMesh->faces.first; efa; efa= efa->next) {
if(efa->f & SELECT) {
efa->e1->f1++;
efa->e2->f1++;
efa->e3->f1++;
if(efa->e4) efa->e4->f1++;
}
}
edgeCount= 0;
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
if(eed->f1==2) edgeCount++;
}
if(edgeCount==1) {
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
if(eed->f1==2) {
edge_rotate(eed,dir);
break;
}
}
}
else error("Select one edge or two adjacent faces");
}
else if(edgeCount==1) {
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
if(eed->f & SELECT) {
EM_select_edge(eed, 0);
edge_rotate(eed,dir);
break;
}
}
}
else error("Select one edge or two adjacent faces");
/* flush selected vertices (again) to edges/faces */
EM_select_flush();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
#ifdef WITH_VERSE
if(G.editMesh->vnode)
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
#endif
BIF_undo_push("Rotate Edge");
}
/******************* BEVEL CODE STARTS HERE ********************/
static void bevel_displace_vec(float *midvec, float *v1, float *v2, float *v3, float d, float no[3])
{
float a[3], c[3], n_a[3], n_c[3], mid[3], ac, ac2, fac;
sub_v3_v3v3(a, v1, v2);
sub_v3_v3v3(c, v3, v2);
cross_v3_v3v3(n_a, a, no);
normalize_v3(n_a);
cross_v3_v3v3(n_c, no, c);
normalize_v3(n_c);
normalize_v3(a);
normalize_v3(c);
ac = dot_v3v3(a, c);
if (ac == 1 || ac == -1) {
midvec[0] = midvec[1] = midvec[2] = 0;
return;
}
ac2 = ac * ac;
fac = (float)sqrt((ac2 + 2*ac + 1)/(1 - ac2) + 1);
add_v3_v3v3(mid, n_c, n_a);
normalize_v3(mid);
mul_v3_fl(mid, d * fac);
add_v3_v3v3(mid, mid, v2);
copy_v3_v3(midvec, mid);
}
/* Finds the new point using the sinus law to extrapolate a triangle
Lots of sqrts which would not be good for a real time algo
Using the mid point of the extrapolation of both sides
Useless for coplanar quads, but that doesn't happen too often */
static void fix_bevel_wrap(float *midvec, float *v1, float *v2, float *v3, float *v4, float d, float no[3])
{
float a[3], b[3], c[3], l_a, l_b, l_c, s_a, s_b, s_c, Pos1[3], Pos2[3], Dir[3];
sub_v3_v3v3(a, v3, v2);
l_a = normalize_v3(a);
sub_v3_v3v3(b, v4, v3);
normalize_v3(b);
sub_v3_v3v3(c, v1, v2);
normalize_v3(c);
s_b = dot_v3v3(a, c);
s_b = (float)sqrt(1 - (s_b * s_b));
s_a = dot_v3v3(b, c);
s_a = (float)sqrt(1 - (s_a * s_a));
mul_v3_fl(a, -1);
s_c = dot_v3v3(a, b);
s_c = (float)sqrt(1 - (s_c * s_c));
l_b = s_b * l_a / s_a;
l_c = s_c * l_a / s_a;
mul_v3_fl(b, l_b);
mul_v3_fl(c, l_c);
add_v3_v3v3(Pos1, v2, c);
add_v3_v3v3(Pos2, v3, b);
add_v3_v3v3(Dir, Pos1, Pos2);
mul_v3_fl(Dir, 0.5);
bevel_displace_vec(midvec, v3, Dir, v2, d, no);
}
static char detect_wrap(float *o_v1, float *o_v2, float *v1, float *v2, float *no)
{
float o_a[3], a[3], o_c[3], c[3];
sub_v3_v3v3(o_a, o_v1, o_v2);
sub_v3_v3v3(a, v1, v2);
cross_v3_v3v3(o_c, o_a, no);
cross_v3_v3v3(c, a, no);
if (dot_v3v3(c, o_c) <= 0)
return 1;
else
return 0;
}
// Detects and fix a quad wrapping after the resize
// Arguments are the orginal verts followed by the final verts and then the bevel size and the normal
static void fix_bevel_quad_wrap(float *o_v1, float *o_v2, float *o_v3, float *o_v4, float *v1, float *v2, float *v3, float *v4, float d, float *no)
{
float vec[3];
char wrap[4];
// Quads can wrap partially. Watch out
wrap[0] = detect_wrap(o_v1, o_v2, v1, v2, no); // Edge 1-2
wrap[1] = detect_wrap(o_v2, o_v3, v2, v3, no); // Edge 2-3
wrap[2] = detect_wrap(o_v3, o_v4, v3, v4, no); // Edge 3-4
wrap[3] = detect_wrap(o_v4, o_v1, v4, v1, no); // Edge 4-1
// Edge 1 inverted
if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 0) {
fix_bevel_wrap(vec, o_v2, o_v3, o_v4, o_v1, d, no);
copy_v3_v3(v1, vec);
copy_v3_v3(v2, vec);
}
// Edge 2 inverted
else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 0) {
fix_bevel_wrap(vec, o_v3, o_v4, o_v1, o_v2, d, no);
copy_v3_v3(v2, vec);
copy_v3_v3(v3, vec);
}
// Edge 3 inverted
else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) {
fix_bevel_wrap(vec, o_v4, o_v1, o_v2, o_v3, d, no);
copy_v3_v3(v3, vec);
copy_v3_v3(v4, vec);
}
// Edge 4 inverted
else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 1) {
fix_bevel_wrap(vec, o_v1, o_v2, o_v3, o_v4, d, no);
copy_v3_v3(v4, vec);
copy_v3_v3(v1, vec);
}
// Edge 2 and 4 inverted
else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 1) {
add_v3_v3v3(vec, v2, v3);
mul_v3_fl(vec, 0.5);
copy_v3_v3(v2, vec);
copy_v3_v3(v3, vec);
add_v3_v3v3(vec, v1, v4);
mul_v3_fl(vec, 0.5);
copy_v3_v3(v1, vec);
copy_v3_v3(v4, vec);
}
// Edge 1 and 3 inverted
else if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) {
add_v3_v3v3(vec, v1, v2);
mul_v3_fl(vec, 0.5);
copy_v3_v3(v1, vec);
copy_v3_v3(v2, vec);
add_v3_v3v3(vec, v3, v4);
mul_v3_fl(vec, 0.5);
copy_v3_v3(v3, vec);
copy_v3_v3(v4, vec);
}
// Totally inverted
else if (wrap[0] == 1 && wrap[1] == 1 && wrap[2] == 1 && wrap[3] == 1) {
add_v3_v3v3(vec, v1, v2);
add_v3_v3v3(vec, vec, v3);
add_v3_v3v3(vec, vec, v4);
mul_v3_fl(vec, 0.25);
copy_v3_v3(v1, vec);
copy_v3_v3(v2, vec);
copy_v3_v3(v3, vec);
copy_v3_v3(v4, vec);
}
}
// Detects and fix a tri wrapping after the resize
// Arguments are the orginal verts followed by the final verts and the normal
// Triangles cannot wrap partially (not in this situation
static void fix_bevel_tri_wrap(float *o_v1, float *o_v2, float *o_v3, float *v1, float *v2, float *v3, float *no)
{
if (detect_wrap(o_v1, o_v2, v1, v2, no)) {
float vec[3];
add_v3_v3v3(vec, o_v1, o_v2);
add_v3_v3v3(vec, vec, o_v3);
mul_v3_fl(vec, 1.0f/3.0f);
copy_v3_v3(v1, vec);
copy_v3_v3(v2, vec);
copy_v3_v3(v3, vec);
}
}
static void bevel_shrink_faces(float d, int flag)
{
EditMesh *em = G.editMesh;
EditFace *efa;
float vec[3], no[3], v1[3], v2[3], v3[3], v4[3];
/* move edges of all faces with efa->f1 & flag closer towards their centers */
efa= em->faces.first;
while (efa) {
if (efa->f1 & flag) {
copy_v3_v3(v1, efa->v1->co);
copy_v3_v3(v2, efa->v2->co);
copy_v3_v3(v3, efa->v3->co);
copy_v3_v3(no, efa->n);
if (efa->v4 == NULL) {
bevel_displace_vec(vec, v1, v2, v3, d, no);
copy_v3_v3(efa->v2->co, vec);
bevel_displace_vec(vec, v2, v3, v1, d, no);
copy_v3_v3(efa->v3->co, vec);
bevel_displace_vec(vec, v3, v1, v2, d, no);
copy_v3_v3(efa->v1->co, vec);
fix_bevel_tri_wrap(v1, v2, v3, efa->v1->co, efa->v2->co, efa->v3->co, no);
} else {
copy_v3_v3(v4, efa->v4->co);
bevel_displace_vec(vec, v1, v2, v3, d, no);
copy_v3_v3(efa->v2->co, vec);
bevel_displace_vec(vec, v2, v3, v4, d, no);
copy_v3_v3(efa->v3->co, vec);
bevel_displace_vec(vec, v3, v4, v1, d, no);
copy_v3_v3(efa->v4->co, vec);
bevel_displace_vec(vec, v4, v1, v2, d, no);
copy_v3_v3(efa->v1->co, vec);
fix_bevel_quad_wrap(v1, v2, v3, v4, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, d, no);
}
}
efa= efa->next;
}
}
static void bevel_shrink_draw(float d, int flag)
{
EditMesh *em = G.editMesh;
EditFace *efa;
float vec[3], no[3], v1[3], v2[3], v3[3], v4[3], fv1[3], fv2[3], fv3[3], fv4[3];
/* move edges of all faces with efa->f1 & flag closer towards their centers */
efa= em->faces.first;
while (efa) {
copy_v3_v3(v1, efa->v1->co);
copy_v3_v3(v2, efa->v2->co);
copy_v3_v3(v3, efa->v3->co);
copy_v3_v3(no, efa->n);
if (efa->v4 == NULL) {
bevel_displace_vec(vec, v1, v2, v3, d, no);
copy_v3_v3(fv2, vec);
bevel_displace_vec(vec, v2, v3, v1, d, no);
copy_v3_v3(fv3, vec);
bevel_displace_vec(vec, v3, v1, v2, d, no);
copy_v3_v3(fv1, vec);
fix_bevel_tri_wrap(v1, v2, v3, fv1, fv2, fv3, no);
glBegin(GL_LINES);
glVertex3fv(fv1);
glVertex3fv(fv2);
glEnd();
glBegin(GL_LINES);
glVertex3fv(fv2);
glVertex3fv(fv3);
glEnd();
glBegin(GL_LINES);
glVertex3fv(fv1);
glVertex3fv(fv3);
glEnd();
} else {
copy_v3_v3(v4, efa->v4->co);
bevel_displace_vec(vec, v4, v1, v2, d, no);
copy_v3_v3(fv1, vec);
bevel_displace_vec(vec, v1, v2, v3, d, no);
copy_v3_v3(fv2, vec);
bevel_displace_vec(vec, v2, v3, v4, d, no);
copy_v3_v3(fv3, vec);
bevel_displace_vec(vec, v3, v4, v1, d, no);
copy_v3_v3(fv4, vec);
fix_bevel_quad_wrap(v1, v2, v3, v4, fv1, fv2, fv3, fv4, d, no);
glBegin(GL_LINES);
glVertex3fv(fv1);
glVertex3fv(fv2);
glEnd();
glBegin(GL_LINES);
glVertex3fv(fv2);
glVertex3fv(fv3);
glEnd();
glBegin(GL_LINES);
glVertex3fv(fv3);
glVertex3fv(fv4);
glEnd();
glBegin(GL_LINES);
glVertex3fv(fv1);
glVertex3fv(fv4);
glEnd();
}
efa= efa->next;
}
}
static void bevel_mesh(float bsize, int allfaces)
{
EditMesh *em = G.editMesh;
//#define BEV_DEBUG
/* Enables debug printfs and assigns material indices: */
/* 2 = edge quad */
/* 3 = fill polygon (vertex clusters) */
EditFace *efa, *example; //, *nextvl;
EditEdge *eed, *eed2;
EditVert *neweve[1024], *eve, *eve2, *eve3, *v1, *v2, *v3, *v4; //, *eve4;
//short found4, search;
//float f1, f2, f3, f4;
float cent[3], min[3], max[3];
int a, b, c;
float limit= 0.001f;
if(multires_test()) return;
waitcursor(1);
removedoublesflag(1, 0, limit);
/* tag all original faces */
efa= em->faces.first;
while (efa) {
efa->f1= 0;
if (faceselectedAND(efa, 1)||allfaces) {
efa->f1= 1;
efa->v1->f |= 128;
efa->v2->f |= 128;
efa->v3->f |= 128;
if (efa->v4) efa->v4->f |= 128;
}
efa->v1->f &= ~64;
efa->v2->f &= ~64;
efa->v3->f &= ~64;
if (efa->v4) efa->v4->f &= ~64;
efa= efa->next;
}
#ifdef BEV_DEBUG
fprintf(stderr,"bevel_mesh: split\n");
#endif
efa= em->faces.first;
while (efa) {
if (efa->f1 & 1) {
efa->f1-= 1;
v1= addvertlist(efa->v1->co, efa->v1);
v1->f= efa->v1->f & ~128;
efa->v1->tmp.v = v1;
v1= addvertlist(efa->v2->co, efa->v2);
v1->f= efa->v2->f & ~128;
efa->v2->tmp.v = v1;
v1= addvertlist(efa->v3->co, efa->v3);
v1->f= efa->v3->f & ~128;
efa->v3->tmp.v = v1;
if (efa->v4) {
v1= addvertlist(efa->v4->co, efa->v4);
v1->f= efa->v4->f & ~128;
efa->v4->tmp.v = v1;
}
/* Needs better adaption of creases? */
addedgelist(efa->e1->v1->tmp.v,
efa->e1->v2->tmp.v,
efa->e1);
addedgelist(efa->e2->v1->tmp.v,
efa->e2->v2->tmp.v,
efa->e2);
addedgelist(efa->e3->v1->tmp.v,
efa->e3->v2->tmp.v,
efa->e3);
if (efa->e4) addedgelist(efa->e4->v1->tmp.v,
efa->e4->v2->tmp.v,
efa->e4);
if(efa->v4) {
v1 = efa->v1->tmp.v;
v2 = efa->v2->tmp.v;
v3 = efa->v3->tmp.v;
v4 = efa->v4->tmp.v;
addfacelist(v1, v2, v3, v4, efa,NULL);
} else {
v1= efa->v1->tmp.v;
v2= efa->v2->tmp.v;
v3= efa->v3->tmp.v;
addfacelist(v1, v2, v3, 0, efa,NULL);
}
efa= efa-> next;
} else {
efa= efa->next;
}
}
for(efa= em->faces.first; efa; efa= efa->next) {
if( (efa->v1->f & 128) && (efa->v2->f & 128) && (efa->v3->f & 128) ) {
if(efa->v4==NULL || (efa->v4->f & 128)) efa->f |= 128;
}
}
delfaceflag(128); // works with face flag now
/* tag all faces for shrink*/
efa= em->faces.first;
while (efa) {
if (faceselectedAND(efa, 1)||allfaces) {
efa->f1= 2;
}
efa= efa->next;
}
#ifdef BEV_DEBUG
fprintf(stderr,"bevel_mesh: make edge quads\n");
#endif
/* find edges that are on each other and make quads between them */
eed= em->edges.first;
while(eed) {
eed->f2= eed->f1= 0;
if ( ((eed->v1->f & eed->v2->f) & 1) || allfaces)
eed->f1 |= 4; /* original edges */
eed->tmp.v = 0;
eed= eed->next;
}
eed= em->edges.first;
while (eed) {
if ( ((eed->f1 & 2)==0) && (eed->f1 & 4) ) {
eed2= em->edges.first;
while (eed2) {
if ( (eed2 != eed) && ((eed2->f1 & 2)==0) && (eed->f1 & 4) ) {
if (
(eed->v1 != eed2->v1) &&
(eed->v1 != eed2->v2) &&
(eed->v2 != eed2->v1) &&
(eed->v2 != eed2->v2) && (
( compare_v3v3(eed->v1->co, eed2->v1->co, limit) &&
compare_v3v3(eed->v2->co, eed2->v2->co, limit) ) ||
( compare_v3v3(eed->v1->co, eed2->v2->co, limit) &&
compare_v3v3(eed->v2->co, eed2->v1->co, limit) ) ) )
{
#ifdef BEV_DEBUG
fprintf(stderr, "bevel_mesh: edge quad\n");
#endif
eed->f1 |= 2; /* these edges are finished */
eed2->f1 |= 2;
example= NULL;
efa= em->faces.first; /* search example face (for mat_nr, ME_SMOOTH, ...) */
while (efa) {
if ( (efa->e1 == eed) ||
(efa->e2 == eed) ||
(efa->e3 == eed) ||
(efa->e4 && (efa->e4 == eed)) ) {
example= efa;
efa= NULL;
}
if (efa) efa= efa->next;
}
neweve[0]= eed->v1; neweve[1]= eed->v2;
neweve[2]= eed2->v1; neweve[3]= eed2->v2;
if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) {
efa= NULL;
if (compare_v3v3(eed->v1->co, eed2->v2->co, limit)) {
efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], example,NULL);
} else {
efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], example,NULL);
}
if(efa) {
float inp;
normal_tri_v3( efa->n,efa->v1->co, efa->v2->co, efa->v3->co);
inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2];
if(inp < 0.0) flipface(efa);
#ifdef BEV_DEBUG
efa->mat_nr= 1;
#endif
} else fprintf(stderr,"bevel_mesh: error creating face\n");
}
eed2= NULL;
}
}
if (eed2) eed2= eed2->next;
}
}
eed= eed->next;
}
eed= em->edges.first;
while(eed) {
eed->f2= eed->f1= 0;
eed->f1= 0;
eed->v1->f1 &= ~1;
eed->v2->f1 &= ~1;
eed->tmp.v = 0;
eed= eed->next;
}
#ifdef BEV_DEBUG
fprintf(stderr,"bevel_mesh: find clusters\n");
#endif
/* Look for vertex clusters */
eve= em->verts.first;
while (eve) {
eve->f &= ~(64|128);
eve->tmp.v = NULL;
eve= eve->next;
}
/* eve->f: 128: first vertex in a list (->tmp.v) */
/* 64: vertex is in a list */
eve= em->verts.first;
while (eve) {
eve2= em->verts.first;
eve3= NULL;
while (eve2) {
if ((eve2 != eve) && ((eve2->f & (64|128))==0)) {
if (compare_v3v3(eve->co, eve2->co, limit)) {
if ((eve->f & (128|64)) == 0) {
/* fprintf(stderr,"Found vertex cluster:\n *\n *\n"); */
eve->f |= 128;
eve->tmp.v = eve2;
eve3= eve2;
} else if ((eve->f & 64) == 0) {
/* fprintf(stderr," *\n"); */
if (eve3) eve3->tmp.v = eve2;
eve2->f |= 64;
eve3= eve2;
}
}
}
eve2= eve2->next;
if (!eve2) {
if (eve3) eve3->tmp.v = NULL;
}
}
eve= eve->next;
}
#ifdef BEV_DEBUG
fprintf(stderr,"bevel_mesh: shrink faces\n");
#endif
bevel_shrink_faces(bsize, 2);
#ifdef BEV_DEBUG
fprintf(stderr,"bevel_mesh: fill clusters\n");
#endif
/* Make former vertex clusters faces */
eve= em->verts.first;
while (eve) {
eve->f &= ~64;
eve= eve->next;
}
eve= em->verts.first;
while (eve) {
if (eve->f & 128) {
eve->f &= ~128;
a= 0;
neweve[a]= eve;
eve2 = eve->tmp.v;
while (eve2) {
a++;
neweve[a]= eve2;
eve2 = eve2->tmp.v;
}
a++;
efa= NULL;
if (a>=3) {
example= NULL;
efa= em->faces.first; /* search example face */
while (efa) {
if ( (efa->v1 == neweve[0]) ||
(efa->v2 == neweve[0]) ||
(efa->v3 == neweve[0]) ||
(efa->v4 && (efa->v4 == neweve[0])) ) {
example= efa;
efa= NULL;
}
if (efa) efa= efa->next;
}
#ifdef BEV_DEBUG
fprintf(stderr,"bevel_mesh: Making %d-gon\n", a);
#endif
if (a>4) {
cent[0]= cent[1]= cent[2]= 0.0;
INIT_MINMAX(min, max);
for (b=0; b<a; b++) {
add_v3_v3v3(cent, cent, neweve[b]->co);
DO_MINMAX(neweve[b]->co, min, max);
}
cent[0]= (min[0]+max[0])/2;
cent[1]= (min[1]+max[1])/2;
cent[2]= (min[2]+max[2])/2;
eve2= addvertlist(cent, NULL);
eve2->f |= 1;
eed= em->edges.first;
while (eed) {
c= 0;
for (b=0; b<a; b++)
if ((neweve[b]==eed->v1) || (neweve[b]==eed->v2)) c++;
if (c==2) {
if(exist_face(eed->v1, eed->v2, eve2, 0)==0) {
efa= addfacelist(eed->v1, eed->v2, eve2, 0, example,NULL);
#ifdef BEV_DEBUG
efa->mat_nr= 2;
#endif
}
}
eed= eed->next;
}
} else if (a==4) {
if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) {
/* the order of vertices can be anything, three cases to check */
if( convex(neweve[0]->co, neweve[1]->co, neweve[2]->co, neweve[3]->co) ) {
efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], NULL, NULL);
}
else if( convex(neweve[0]->co, neweve[2]->co, neweve[3]->co, neweve[1]->co) ) {
efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], NULL, NULL);
}
else if( convex(neweve[0]->co, neweve[2]->co, neweve[1]->co, neweve[3]->co) ) {
efa= addfacelist(neweve[0], neweve[2], neweve[1], neweve[3], NULL, NULL);
}
}
}
else if (a==3) {
if(exist_face(neweve[0], neweve[1], neweve[2], 0)==0)
efa= addfacelist(neweve[0], neweve[1], neweve[2], 0, example, NULL);
}
if(efa) {
float inp;
normal_tri_v3( efa->n,neweve[0]->co, neweve[1]->co, neweve[2]->co);
inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2];
if(inp < 0.0) flipface(efa);
#ifdef BEV_DEBUG
efa->mat_nr= 2;
#endif
}
}
}
eve= eve->next;
}
eve= em->verts.first;
while (eve) {
eve->f1= 0;
eve->f &= ~(128|64);
eve->tmp.v= NULL;
eve= eve->next;
}
recalc_editnormals();
waitcursor(0);
countall();
allqueue(REDRAWVIEW3D, 0);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
removedoublesflag(1, 0, limit);
/* flush selected vertices to edges/faces */
EM_select_flush();
#undef BEV_DEBUG
}
static void bevel_mesh_recurs(float bsize, short recurs, int allfaces)
{
float d;
short nr;
d= bsize;
for (nr=0; nr<recurs; nr++) {
bevel_mesh(d, allfaces);
if (nr==0) d /= 3; else d /= 2;
}
}
void bevel_menu(void)
{
BME_Mesh *bm;
BME_TransData_Head *td;
TransInfo *t;
int options, res, gbm_free = 0;
t = BIF_GetTransInfo();
if (!G.editBMesh) {
G.editBMesh = MEM_callocN(sizeof(*(G.editBMesh)),"bevel_menu() G.editBMesh");
gbm_free = 1;
}
G.editBMesh->options = BME_BEVEL_RUNNING | BME_BEVEL_SELECT;
G.editBMesh->res = 1;
while(G.editBMesh->options & BME_BEVEL_RUNNING) {
options = G.editBMesh->options;
res = G.editBMesh->res;
bm = BME_editmesh_to_bmesh(G.editMesh);
BIF_undo_push("Pre-Bevel");
free_editMesh(G.editMesh);
BME_bevel(bm,0.1f,res,options,0,0,&td);
BME_bmesh_to_editmesh(bm, td);
EM_selectmode_flush();
G.editBMesh->bm = bm;
G.editBMesh->td = td;
initTransform(TFM_BEVEL,CTX_BMESH);
Transform();
BME_free_transdata(td);
BME_free_mesh(bm);
if (t->state != TRANS_CONFIRM) {
BIF_undo();
}
if (options == G.editBMesh->options) {
G.editBMesh->options &= ~BME_BEVEL_RUNNING;
}
}
if (gbm_free) {
MEM_freeN(G.editBMesh);
G.editBMesh = NULL;
}
}
void bevel_menu_old()
{
char Finished = 0, Canceled = 0, str[100], Recalc = 0;
short mval[2], oval[2], curval[2], event = 0, recurs = 1, nr;
float vec[3], d, drawd=0.0, center[3], fac = 1;
getmouseco_areawin(mval);
oval[0] = mval[0]; oval[1] = mval[1];
// Silly hackish code to initialise the variable (warning if not done)
// while still drawing in the first iteration (and without using another variable)
curval[0] = mval[0] + 1; curval[1] = mval[1] + 1;
// Init grabz for window to vec conversions
initgrabz(-G.vd->ofs[0], -G.vd->ofs[1], -G.vd->ofs[2]);
window_to_3d(center, mval[0], mval[1]);
if(button(&recurs, 1, 4, "Recursion:")==0) return;
for (nr=0; nr<recurs-1; nr++) {
if (nr==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * nr * 2.0f);
}
EM_set_flag_all(SELECT);
SetBlenderCursor(SYSCURSOR);
while (Finished == 0)
{
getmouseco_areawin(mval);
if (mval[0] != curval[0] || mval[1] != curval[1] || (Recalc == 1))
{
Recalc = 0;
curval[0] = mval[0];
curval[1] = mval[1];
window_to_3d(vec, mval[0]-oval[0], mval[1]-oval[1]);
d = normalize_v3(vec) / 10;
drawd = d * fac;
if (G.qual & LR_CTRLKEY)
drawd = (float) floor(drawd * 10.0f)/10.0f;
if (G.qual & LR_SHIFTKEY)
drawd /= 10;
/*------------- Preview lines--------------- */
/* uses callback mechanism to draw it all in current area */
scrarea_do_windraw(curarea);
/* set window matrix to perspective, default an area returns with buttons transform */
persp(PERSP_VIEW);
/* make a copy, for safety */
glPushMatrix();
/* multiply with the object transformation */
mymultmatrix(G.obedit->obmat);
glColor3ub(255, 255, 0);
// PREVIEW CODE GOES HERE
bevel_shrink_draw(drawd, 2);
/* restore matrix transform */
glPopMatrix();
sprintf(str, "Bevel Size: %.4f LMB to confirm, RMB to cancel, SPACE to input directly.", drawd);
headerprint(str);
/* this also verifies other area/windows for clean swap */
screen_swapbuffers();
persp(PERSP_WIN);
glDrawBuffer(GL_FRONT);
BIF_ThemeColor(TH_WIRE);
setlinestyle(3);
glBegin(GL_LINE_STRIP);
glVertex2sv(mval);
glVertex2sv(oval);
glEnd();
setlinestyle(0);
persp(PERSP_VIEW);
bglFlush(); // flush display for frontbuffer
glDrawBuffer(GL_BACK);
}
while(qtest()) {
short val=0;
event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle
/* val==0 on key-release event */
if(val && (event==ESCKEY || event==RIGHTMOUSE || event==LEFTMOUSE || event==RETKEY || event==ESCKEY)) {
if (event==RIGHTMOUSE || event==ESCKEY)
Canceled = 1;
Finished = 1;
}
else if (val && event==SPACEKEY) {
if (fbutton(&d, 0.000, 10.000, 10, 0, "Width:")!=0) {
drawd = d * fac;
Finished = 1;
}
}
else if (val) {
/* On any other keyboard event, recalc */
Recalc = 1;
}
}
}
if (Canceled==0) {
SetBlenderCursor(BC_WAITCURSOR);
bevel_mesh_recurs(drawd/fac, recurs, 1);
righthandfaces(1);
SetBlenderCursor(SYSCURSOR);
BIF_undo_push("Bevel");
}
}
/* -------------------- More tools ------------------ */
void mesh_set_face_flags(short mode)
{
EditMesh *em = G.editMesh;
EditFace *efa;
MTFace *tface;
short m_tex=0, m_tiles=0, m_shared=0,
m_light=0, m_invis=0, m_collision=0,
m_twoside=0, m_obcolor=0, m_halo=0,
m_billboard=0, m_shadow=0, m_text=0,
m_sort=0;
short flag = 0, change = 0;
if (!EM_texFaceCheck()) {
error("not a mesh with uv/image layers");
return;
}
add_numbut(0, TOG|SHO, "Texture", 0, 0, &m_tex, NULL);
add_numbut(1, TOG|SHO, "Tiles", 0, 0, &m_tiles, NULL);
add_numbut(2, TOG|SHO, "Light", 0, 0, &m_light, NULL);
add_numbut(3, TOG|SHO, "Invisible", 0, 0, &m_invis, NULL);
add_numbut(4, TOG|SHO, "Collision", 0, 0, &m_collision, NULL);
add_numbut(5, TOG|SHO, "Shared", 0, 0, &m_shared, NULL);
add_numbut(6, TOG|SHO, "Twoside", 0, 0, &m_twoside, NULL);
add_numbut(7, TOG|SHO, "ObColor", 0, 0, &m_obcolor, NULL);
add_numbut(8, TOG|SHO, "Halo", 0, 0, &m_halo, NULL);
add_numbut(9, TOG|SHO, "Billboard", 0, 0, &m_billboard, NULL);
add_numbut(10, TOG|SHO, "Shadow", 0, 0, &m_shadow, NULL);
add_numbut(11, TOG|SHO, "Text", 0, 0, &m_text, NULL);
add_numbut(12, TOG|SHO, "Sort", 0, 0, &m_sort, NULL);
if (!do_clever_numbuts((mode ? "Set Flags" : "Clear Flags"), 13, REDRAW))
return;
/* these 2 cant both be on */
if (mode) /* are we seeting*/
if (m_halo)
m_billboard = 0;
if (m_tex) flag |= TF_TEX;
if (m_tiles) flag |= TF_TILES;
if (m_shared) flag |= TF_SHAREDCOL;
if (m_light) flag |= TF_LIGHT;
if (m_invis) flag |= TF_INVISIBLE;
if (m_collision) flag |= TF_DYNAMIC;
if (m_twoside) flag |= TF_TWOSIDE;
if (m_obcolor) flag |= TF_OBCOL;
if (m_halo) flag |= TF_BILLBOARD;
if (m_billboard) flag |= TF_BILLBOARD2;
if (m_shadow) flag |= TF_SHADOW;
if (m_text) flag |= TF_BMFONT;
if (m_sort) flag |= TF_ALPHASORT;
if (flag==0)
return;
efa= em->faces.first;
while(efa) {
if(efa->f & SELECT) {
tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
if (mode) tface->mode |= flag;
else tface->mode &= ~flag;
change = 1;
}
efa= efa->next;
}
if (change) {
BIF_undo_push((mode ? "Set Flags" : "Clear Flags"));
allqueue(REDRAWIMAGE, 0);
allqueue(REDRAWVIEW3D, 0);
}
}
void mesh_set_smooth_faces(short event)
{
EditMesh *em = G.editMesh;
EditFace *efa;
if(G.obedit==0) return;
if(G.obedit->type != OB_MESH) return;
efa= em->faces.first;
while(efa) {
if(efa->f & SELECT) {
if(event==1) efa->flag |= ME_SMOOTH;
else if(event==0) efa->flag &= ~ME_SMOOTH;
}
efa= efa->next;
}
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
if(event==1) BIF_undo_push("Set Smooth");
else if(event==0) BIF_undo_push("Set Solid");
}
/* helper to find edge for edge_rip */
static float mesh_rip_edgedist(float mat[][4], float *co1, float *co2, short *mval)
{
float vec1[3], vec2[3], mvalf[2];
view3d_project_float(curarea, co1, vec1, mat);
view3d_project_float(curarea, co2, vec2, mat);
mvalf[0]= (float)mval[0];
mvalf[1]= (float)mval[1];
return dist_to_line_segment_v2(mvalf, vec1, vec2);
}
/* helper for below */
static void mesh_rip_setface(EditFace *sefa)
{
/* put new vertices & edges in best face */
if(sefa->v1->tmp.v) sefa->v1= sefa->v1->tmp.v;
if(sefa->v2->tmp.v) sefa->v2= sefa->v2->tmp.v;
if(sefa->v3->tmp.v) sefa->v3= sefa->v3->tmp.v;
if(sefa->v4 && sefa->v4->tmp.v) sefa->v4= sefa->v4->tmp.v;
sefa->e1= addedgelist(sefa->v1, sefa->v2, sefa->e1);
sefa->e2= addedgelist(sefa->v2, sefa->v3, sefa->e2);
if(sefa->v4) {
sefa->e3= addedgelist(sefa->v3, sefa->v4, sefa->e3);
sefa->e4= addedgelist(sefa->v4, sefa->v1, sefa->e4);
}
else
sefa->e3= addedgelist(sefa->v3, sefa->v1, sefa->e3);
}
/* based on mouse cursor position, it defines how is being ripped */
void mesh_rip(void)
{
extern void faceloop_select(EditEdge *startedge, int select);
EditMesh *em = G.editMesh;
EditVert *eve, *nextve;
EditEdge *eed, *seed= NULL;
EditFace *efa, *sefa= NULL;
float projectMat[4][4], viewMat[4][4], vec[3], dist, mindist;
short doit= 1, mval[2],propmode,prop;
propmode = G.scene->prop_mode;
G.scene->prop_mode = 0;
prop = G.scene->proportional;
G.scene->proportional = 0;
/* select flush... vertices are important */
EM_selectmode_set();
getmouseco_areawin(mval);
view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat);
/* find best face, exclude triangles and break on face select or faces with 2 edges select */
mindist= 1000000.0f;
for(efa= em->faces.first; efa; efa=efa->next) {
if( efa->f & 1)
break;
if(efa->v4 && faceselectedOR(efa, SELECT) ) {
int totsel=0;
if(efa->e1->f & SELECT) totsel++;
if(efa->e2->f & SELECT) totsel++;
if(efa->e3->f & SELECT) totsel++;
if(efa->e4->f & SELECT) totsel++;
if(totsel>1)
break;
view3d_project_float(curarea, efa->cent, vec, projectMat);
dist= sqrt( (vec[0]-mval[0])*(vec[0]-mval[0]) + (vec[1]-mval[1])*(vec[1]-mval[1]) );
if(dist<mindist) {
mindist= dist;
sefa= efa;
}
}
}
if(efa) {
error("Can't perform ripping with faces selected this way");
return;
}
if(sefa==NULL) {
error("No proper selection or faces included");
return;
}
/* duplicate vertices, new vertices get selected */
for(eve = em->verts.last; eve; eve= eve->prev) {
eve->tmp.v = NULL;
if(eve->f & SELECT) {
eve->tmp.v = addvertlist(eve->co, eve);
eve->f &= ~SELECT;
eve->tmp.v->f |= SELECT;
}
}
/* find the best candidate edge */
/* or one of sefa edges is selected... */
if(sefa->e1->f & SELECT) seed= sefa->e2;
if(sefa->e2->f & SELECT) seed= sefa->e1;
if(sefa->e3->f & SELECT) seed= sefa->e2;
if(sefa->e4 && sefa->e4->f & SELECT) seed= sefa->e3;
/* or we do the distance trick */
if(seed==NULL) {
mindist= 1000000.0f;
if(sefa->e1->v1->tmp.v || sefa->e1->v2->tmp.v) {
dist = mesh_rip_edgedist(projectMat,
sefa->e1->v1->co,
sefa->e1->v2->co, mval);
if(dist<mindist) {
seed= sefa->e1;
mindist= dist;
}
}
if(sefa->e2->v1->tmp.v || sefa->e2->v2->tmp.v) {
dist = mesh_rip_edgedist(projectMat,
sefa->e2->v1->co,
sefa->e2->v2->co, mval);
if(dist<mindist) {
seed= sefa->e2;
mindist= dist;
}
}
if(sefa->e3->v1->tmp.v || sefa->e3->v2->tmp.v) {
dist= mesh_rip_edgedist(projectMat,
sefa->e3->v1->co,
sefa->e3->v2->co, mval);
if(dist<mindist) {
seed= sefa->e3;
mindist= dist;
}
}
if(sefa->e4 && (sefa->e4->v1->tmp.v || sefa->e4->v2->tmp.v)) {
dist= mesh_rip_edgedist(projectMat,
sefa->e4->v1->co,
sefa->e4->v2->co, mval);
if(dist<mindist) {
seed= sefa->e4;
mindist= dist;
}
}
}
if(seed==NULL) { // never happens?
error("No proper edge found to start");
return;
}
faceloop_select(seed, 2); // tmp abuse for finding all edges that need duplicated, returns OK faces with f1
/* duplicate edges in the loop, with at least 1 vertex selected, needed for selection flip */
for(eed = em->edges.last; eed; eed= eed->prev) {
eed->tmp.v = NULL;
if((eed->v1->tmp.v) || (eed->v2->tmp.v)) {
EditEdge *newed;
newed= addedgelist(eed->v1->tmp.v?eed->v1->tmp.v:eed->v1,
eed->v2->tmp.v?eed->v2->tmp.v:eed->v2, eed);
if(eed->f & SELECT) {
eed->f &= ~SELECT;
newed->f |= SELECT;
}
eed->tmp.v = (EditVert *)newed;
}
}
/* first clear edges to help finding neighbours */
for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0;
/* put new vertices & edges && flag in best face */
mesh_rip_setface(sefa);
/* starting with neighbours of best face, we loop over the seam */
sefa->f1= 2;
doit= 1;
while(doit) {
doit= 0;
for(efa= em->faces.first; efa; efa=efa->next) {
/* new vert in face */
if (efa->v1->tmp.v || efa->v2->tmp.v ||
efa->v3->tmp.v || (efa->v4 && efa->v4->tmp.v)) {
/* face is tagged with loop */
if(efa->f1==1) {
mesh_rip_setface(efa);
efa->f1= 2;
doit= 1;
}
}
}
}
/* remove loose edges, that were part of a ripped face */
for(eve = em->verts.first; eve; eve= eve->next) eve->f1= 0;
for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0;
for(efa= em->faces.first; efa; efa=efa->next) {
efa->e1->f1= 1;
efa->e2->f1= 1;
efa->e3->f1= 1;
if(efa->e4) efa->e4->f1= 1;
}
for(eed = em->edges.last; eed; eed= seed) {
seed= eed->prev;
if(eed->f1==0) {
if(eed->v1->tmp.v || eed->v2->tmp.v ||
(eed->v1->f & SELECT) || (eed->v2->f & SELECT)) {
remedge(eed);
free_editedge(eed);
eed= NULL;
}
}
if(eed) {
eed->v1->f1= 1;
eed->v2->f1= 1;
}
}
/* and remove loose selected vertices, that got duplicated accidentally */
for(eve = em->verts.first; eve; eve= nextve) {
nextve= eve->next;
if(eve->f1==0 && (eve->tmp.v || (eve->f & SELECT))) {
BLI_remlink(&em->verts,eve);
free_editvert(eve);
}
}
countall(); // apparently always needed when adding stuff, derived mesh
#ifdef WITH_VERSE
if(G.editMesh->vnode) {
sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode);
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
}
#endif
BIF_TransformSetUndo("Rip");
initTransform(TFM_TRANSLATION, 0);
Transform();
G.scene->prop_mode = propmode;
G.scene->proportional = prop;
}
void shape_propagate(void)
{
EditMesh *em = G.editMesh;
EditVert *ev = NULL;
Mesh* me = (Mesh*)G.obedit->data;
Key* ky = NULL;
KeyBlock* kb = NULL;
Base* base=NULL;
if(me->key){
ky = me->key;
} else {
error("Object Has No Key");
return;
}
if(ky->block.first){
for(ev = em->verts.first; ev ; ev = ev->next){
if(ev->f & SELECT){
for(kb=ky->block.first;kb;kb = kb->next){
float *data;
data = kb->data;
copy_v3_v3(data+(ev->keyindex*3),ev->co);
}
}
}
} else {
error("Object Has No Blendshapes");
return;
}
//TAG Mesh Objects that share this data
for(base = G.scene->base.first; base; base = base->next){
if(base->object && base->object->data == me){
base->object->recalc = OB_RECALC_DATA;
}
}
BIF_undo_push("Propagate Blendshape Verts");
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
return;
}
void shape_copy_from_lerp(KeyBlock* thisBlock, KeyBlock* fromBlock)
{
EditMesh *em = G.editMesh;
EditVert *ev = NULL;
short mval[2], curval[2], event = 0, finished = 0, canceled = 0, fullcopy=0 ;
float perc = 0;
char str[64];
float *data, *odata;
data = fromBlock->data;
odata = thisBlock->data;
getmouseco_areawin(mval);
curval[0] = mval[0] + 1; curval[1] = mval[1] + 1;
while (finished == 0)
{
getmouseco_areawin(mval);
if (mval[0] != curval[0] || mval[1] != curval[1])
{
if(mval[0] > curval[0])
perc += 0.1;
else if(mval[0] < curval[0])
perc -= 0.1;
if(perc < 0) perc = 0;
if(perc > 1) perc = 1;
curval[0] = mval[0];
curval[1] = mval[1];
if(fullcopy == 1){
perc = 1;
}
for(ev = em->verts.first; ev ; ev = ev->next){
if(ev->f & SELECT){
interp_v3_v3v3(ev->co,odata+(ev->keyindex*3),data+(ev->keyindex*3),perc);
}
}
sprintf(str,"Blending at %d%c MMB to Copy at 100%c",(int)(perc*100),'%','%');
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
headerprint(str);
force_draw(0);
if(fullcopy == 1){
break;
}
} else {
PIL_sleep_ms(10);
}
while(qtest()) {
short val=0;
event= extern_qread(&val);
if(val){
if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)){
finished = 1;
}
else if (event == MIDDLEMOUSE){
fullcopy = 1;
}
else if (ELEM3(event,ESCKEY,RIGHTMOUSE,RIGHTMOUSE)){
canceled = 1;
finished = 1;
}
}
}
}
if(!canceled)
BIF_undo_push("Copy Blendshape Verts");
else
for(ev = em->verts.first; ev ; ev = ev->next){
if(ev->f & SELECT){
copy_v3_v3(ev->co, odata+(ev->keyindex*3));
}
}
return;
}
void shape_copy_select_from()
{
Mesh* me = (Mesh*)G.obedit->data;
EditMesh *em = G.editMesh;
EditVert *ev = NULL;
int totverts = 0,curshape = G.obedit->shapenr;
Key* ky = NULL;
KeyBlock *kb = NULL,*thisBlock = NULL;
int maxlen=32, nr=0, a=0;
char *menu;
if(me->key){
ky = me->key;
} else {
error("Object Has No Key");
return;
}
if(ky->block.first){
for(kb=ky->block.first;kb;kb = kb->next){
maxlen += 40; // Size of a block name
if(a == curshape-1){
thisBlock = kb;
}
a++;
}
a=0;
menu = MEM_callocN(maxlen, "Copy Shape Menu Text");
strcpy(menu, "Copy Vert Positions from Shape %t|");
for(kb=ky->block.first;kb;kb = kb->next){
if(a != curshape-1){
sprintf(menu,"%s %s %cx%d|",menu,kb->name,'%',a);
}
a++;
}
nr = pupmenu_col(menu, 20);
MEM_freeN(menu);
} else {
error("Object Has No Blendshapes");
return;
}
a = 0;
for(kb=ky->block.first;kb;kb = kb->next){
if(a == nr){
for(ev = em->verts.first;ev;ev = ev->next){
totverts++;
}
if(me->totvert != totverts){
error("Shape Has had Verts Added/Removed, please cycle editmode before copying");
return;
}
shape_copy_from_lerp(thisBlock,kb);
return;
}
a++;
}
return;
}
/* Collection Routines|Currently used by the improved merge code*/
/* buildEdge_collection() creates a list of lists*/
/* these lists are filled with edges that are topologically connected.*/
/* This whole tool needs to be redone, its rather poorly implemented...*/
typedef struct Collection{
struct Collection *next, *prev;
int index;
ListBase collectionbase;
} Collection;
typedef struct CollectedEdge{
struct CollectedEdge *next, *prev;
EditEdge *eed;
} CollectedEdge;
#define MERGELIMIT 0.000001
static void build_edgecollection(ListBase *allcollections)
{
EditEdge *eed;
Collection *edgecollection, *newcollection;
CollectedEdge *newedge;
int currtag = 1;
short ebalanced = 0;
short collectionfound = 0;
for (eed=G.editMesh->edges.first; eed; eed = eed->next){
eed->tmp.l = 0;
eed->v1->tmp.l = 0;
eed->v2->tmp.l = 0;
}
/*1st pass*/
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
if(eed->f&SELECT){
eed->v1->tmp.l = currtag;
eed->v2->tmp.l = currtag;
currtag +=1;
}
}
/*2nd pass - Brute force. Loop through selected faces until there are no 'unbalanced' edges left (those with both vertices 'tmp.l' tag matching */
while(ebalanced == 0){
ebalanced = 1;
for(eed=G.editMesh->edges.first; eed; eed = eed->next){
if(eed->f&SELECT){
if(eed->v1->tmp.l != eed->v2->tmp.l) /*unbalanced*/{
if(eed->v1->tmp.l > eed->v2->tmp.l && eed->v2->tmp.l !=0) eed->v1->tmp.l = eed->v2->tmp.l;
else if(eed->v1 != 0) eed->v2->tmp.l = eed->v1->tmp.l;
ebalanced = 0;
}
}
}
}
/*3rd pass, set all the edge flags (unnessecary?)*/
for(eed=G.editMesh->edges.first; eed; eed = eed->next){
if(eed->f&SELECT) eed->tmp.l = eed->v1->tmp.l;
}
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
if(eed->f&SELECT){
if(allcollections->first){
for(edgecollection = allcollections->first; edgecollection; edgecollection=edgecollection->next){
if(edgecollection->index == eed->tmp.l){
newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge");
newedge->eed = eed;
BLI_addtail(&(edgecollection->collectionbase), newedge);
collectionfound = 1;
break;
}
else collectionfound = 0;
}
}
if(allcollections->first == NULL || collectionfound == 0){
newcollection = MEM_mallocN(sizeof(Collection), "element collection");
newcollection->index = eed->tmp.l;
newcollection->collectionbase.first = 0;
newcollection->collectionbase.last = 0;
newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge");
newedge->eed = eed;
BLI_addtail(&(newcollection->collectionbase), newedge);
BLI_addtail(allcollections, newcollection);
}
}
}
}
static void freecollections(ListBase *allcollections)
{
struct Collection *curcollection;
for(curcollection = allcollections->first; curcollection; curcollection = curcollection->next)
BLI_freelistN(&(curcollection->collectionbase));
BLI_freelistN(allcollections);
}
/*Begin UV Edge Collapse Code
Like Edge subdivide, Edge Collapse should handle UV's intelligently, but since UV's are a per-face attribute, normal edge collapse will fail
in areas such as the boundries of 'UV islands'. So for each edge collection we need to build a set of 'welded' UV vertices and edges for it.
The welded UV edges can then be sorted and collapsed.
*/
typedef struct wUV{
struct wUV *next, *prev;
ListBase nodes;
float u, v; /*cached copy of UV coordinates pointed to by nodes*/
EditVert *eve;
int f;
} wUV;
typedef struct wUVNode{
struct wUVNode *next, *prev;
float *u; /*pointer to original tface data*/
float *v; /*pointer to original tface data*/
} wUVNode;
typedef struct wUVEdge{
struct wUVEdge *next, *prev;
float v1uv[2], v2uv[2]; /*nasty.*/
struct wUV *v1, *v2; /*oriented same as editedge*/
EditEdge *eed;
int f;
} wUVEdge;
typedef struct wUVEdgeCollect{ /*used for grouping*/
struct wUVEdgeCollect *next, *prev;
wUVEdge *uved;
int id;
} wUVEdgeCollect;
static void append_weldedUV(EditFace *efa, EditVert *eve, int tfindex, ListBase *uvverts)
{
wUV *curwvert, *newwvert;
wUVNode *newnode;
int found;
MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
found = 0;
for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
if(curwvert->eve == eve && curwvert->u == tf->uv[tfindex][0] && curwvert->v == tf->uv[tfindex][1]){
newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node");
newnode->u = &(tf->uv[tfindex][0]);
newnode->v = &(tf->uv[tfindex][1]);
BLI_addtail(&(curwvert->nodes), newnode);
found = 1;
break;
}
}
if(!found){
newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node");
newnode->u = &(tf->uv[tfindex][0]);
newnode->v = &(tf->uv[tfindex][1]);
newwvert = MEM_callocN(sizeof(wUV), "Welded UV Vert");
newwvert->u = *(newnode->u);
newwvert->v = *(newnode->v);
newwvert->eve = eve;
BLI_addtail(&(newwvert->nodes), newnode);
BLI_addtail(uvverts, newwvert);
}
}
static void build_weldedUVs(ListBase *uvverts)
{
EditFace *efa;
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
if(efa->v1->f1) append_weldedUV(efa, efa->v1, 0, uvverts);
if(efa->v2->f1) append_weldedUV(efa, efa->v2, 1, uvverts);
if(efa->v3->f1) append_weldedUV(efa, efa->v3, 2, uvverts);
if(efa->v4 && efa->v4->f1) append_weldedUV(efa, efa->v4, 3, uvverts);
}
}
static void append_weldedUVEdge(EditFace *efa, EditEdge *eed, ListBase *uvedges)
{
wUVEdge *curwedge, *newwedge;
int v1tfindex, v2tfindex, found;
MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
found = 0;
if(eed->v1 == efa->v1) v1tfindex = 0;
else if(eed->v1 == efa->v2) v1tfindex = 1;
else if(eed->v1 == efa->v3) v1tfindex = 2;
else /* if(eed->v1 == efa->v4) */ v1tfindex = 3;
if(eed->v2 == efa->v1) v2tfindex = 0;
else if(eed->v2 == efa->v2) v2tfindex = 1;
else if(eed->v2 == efa->v3) v2tfindex = 2;
else /* if(eed->v2 == efa->v4) */ v2tfindex = 3;
for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){
if(curwedge->eed == eed && curwedge->v1uv[0] == tf->uv[v1tfindex][0] && curwedge->v1uv[1] == tf->uv[v1tfindex][1] && curwedge->v2uv[0] == tf->uv[v2tfindex][0] && curwedge->v2uv[1] == tf->uv[v2tfindex][1]){
found = 1;
break; //do nothing, we don't need another welded uv edge
}
}
if(!found){
newwedge = MEM_callocN(sizeof(wUVEdge), "Welded UV Edge");
newwedge->v1uv[0] = tf->uv[v1tfindex][0];
newwedge->v1uv[1] = tf->uv[v1tfindex][1];
newwedge->v2uv[0] = tf->uv[v2tfindex][0];
newwedge->v2uv[1] = tf->uv[v2tfindex][1];
newwedge->eed = eed;
BLI_addtail(uvedges, newwedge);
}
}
static void build_weldedUVEdges(ListBase *uvedges, ListBase *uvverts)
{
wUV *curwvert;
wUVEdge *curwedge;
EditFace *efa;
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
if(efa->e1->f1) append_weldedUVEdge(efa, efa->e1, uvedges);
if(efa->e2->f1) append_weldedUVEdge(efa, efa->e2, uvedges);
if(efa->e3->f1) append_weldedUVEdge(efa, efa->e3, uvedges);
if(efa->e4 && efa->e4->f1) append_weldedUVEdge(efa, efa->e4, uvedges);
}
//link vertices: for each uvedge, search uvverts to populate v1 and v2 pointers
for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){
for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
if(curwedge->eed->v1 == curwvert->eve && curwedge->v1uv[0] == curwvert->u && curwedge->v1uv[1] == curwvert->v){
curwedge->v1 = curwvert;
break;
}
}
for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
if(curwedge->eed->v2 == curwvert->eve && curwedge->v2uv[0] == curwvert->u && curwedge->v2uv[1] == curwvert->v){
curwedge->v2 = curwvert;
break;
}
}
}
}
static void free_weldedUVs(ListBase *uvverts)
{
wUV *curwvert;
for(curwvert = uvverts->first; curwvert; curwvert=curwvert->next) BLI_freelistN(&(curwvert->nodes));
BLI_freelistN(uvverts);
}
static void collapse_edgeuvs(void)
{
ListBase uvedges, uvverts, allcollections;
wUVEdge *curwedge;
wUVNode *curwnode;
wUVEdgeCollect *collectedwuve, *newcollectedwuve;
Collection *wuvecollection, *newcollection;
int curtag, balanced, collectionfound= 0, vcount;
float avg[2];
if (!EM_texFaceCheck())
return;
uvverts.first = uvverts.last = uvedges.first = uvedges.last = allcollections.first = allcollections.last = NULL;
build_weldedUVs(&uvverts);
build_weldedUVEdges(&uvedges, &uvverts);
curtag = 0;
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
curwedge->v1->f = curtag;
curwedge->v2->f = curtag;
curtag +=1;
}
balanced = 0;
while(!balanced){
balanced = 1;
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
if(curwedge->v1->f != curwedge->v2->f){
if(curwedge->v1->f > curwedge->v2->f) curwedge->v1->f = curwedge->v2->f;
else curwedge->v2->f = curwedge->v1->f;
balanced = 0;
}
}
}
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next) curwedge->f = curwedge->v1->f;
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
if(allcollections.first){
for(wuvecollection = allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){
if(wuvecollection->index == curwedge->f){
newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge");
newcollectedwuve->uved = curwedge;
BLI_addtail(&(wuvecollection->collectionbase), newcollectedwuve);
collectionfound = 1;
break;
}
else collectionfound = 0;
}
}
if(allcollections.first == NULL || collectionfound == 0){
newcollection = MEM_callocN(sizeof(Collection), "element collection");
newcollection->index = curwedge->f;
newcollection->collectionbase.first = 0;
newcollection->collectionbase.last = 0;
newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge");
newcollectedwuve->uved = curwedge;
BLI_addtail(&(newcollection->collectionbase), newcollectedwuve);
BLI_addtail(&allcollections, newcollection);
}
}
for(wuvecollection=allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){
vcount = avg[0] = avg[1] = 0;
for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){
avg[0] += collectedwuve->uved->v1uv[0];
avg[1] += collectedwuve->uved->v1uv[1];
avg[0] += collectedwuve->uved->v2uv[0];
avg[1] += collectedwuve->uved->v2uv[1];
vcount +=2;
}
avg[0] /= vcount; avg[1] /= vcount;
for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){
for(curwnode=collectedwuve->uved->v1->nodes.first; curwnode; curwnode=curwnode->next){
*(curwnode->u) = avg[0];
*(curwnode->v) = avg[1];
}
for(curwnode=collectedwuve->uved->v2->nodes.first; curwnode; curwnode=curwnode->next){
*(curwnode->u) = avg[0];
*(curwnode->v) = avg[1];
}
}
}
free_weldedUVs(&uvverts);
BLI_freelistN(&uvedges);
freecollections(&allcollections);
}
/*End UV Edge collapse code*/
static void collapseuvs(EditVert *mergevert)
{
EditFace *efa;
MTFace *tf;
int uvcount;
float uvav[2];
if (!EM_texFaceCheck())
return;
uvcount = 0;
uvav[0] = 0;
uvav[1] = 0;
for(efa = G.editMesh->faces.first; efa; efa=efa->next){
tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
if(efa->v1->f1 && ELEM(mergevert, NULL, efa->v1)) {
uvav[0] += tf->uv[0][0];
uvav[1] += tf->uv[0][1];
uvcount += 1;
}
if(efa->v2->f1 && ELEM(mergevert, NULL, efa->v2)){
uvav[0] += tf->uv[1][0];
uvav[1] += tf->uv[1][1];
uvcount += 1;
}
if(efa->v3->f1 && ELEM(mergevert, NULL, efa->v3)){
uvav[0] += tf->uv[2][0];
uvav[1] += tf->uv[2][1];
uvcount += 1;
}
if(efa->v4 && efa->v4->f1 && ELEM(mergevert, NULL, efa->v4)){
uvav[0] += tf->uv[3][0];
uvav[1] += tf->uv[3][1];
uvcount += 1;
}
}
if(uvcount > 0) {
uvav[0] /= uvcount;
uvav[1] /= uvcount;
for(efa = G.editMesh->faces.first; efa; efa=efa->next){
tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
if(efa->v1->f1){
tf->uv[0][0] = uvav[0];
tf->uv[0][1] = uvav[1];
}
if(efa->v2->f1){
tf->uv[1][0] = uvav[0];
tf->uv[1][1] = uvav[1];
}
if(efa->v3->f1){
tf->uv[2][0] = uvav[0];
tf->uv[2][1] = uvav[1];
}
if(efa->v4 && efa->v4->f1){
tf->uv[3][0] = uvav[0];
tf->uv[3][1] = uvav[1];
}
}
}
}
int collapseEdges(void)
{
EditVert *eve;
EditEdge *eed;
ListBase allcollections;
CollectedEdge *curredge;
Collection *edgecollection;
int totedges, groupcount, mergecount,vcount;
float avgcount[3];
allcollections.first = 0;
allcollections.last = 0;
mergecount = 0;
if(multires_test()) return 0;
build_edgecollection(&allcollections);
groupcount = BLI_countlist(&allcollections);
for(edgecollection = allcollections.first; edgecollection; edgecollection = edgecollection->next){
totedges = BLI_countlist(&(edgecollection->collectionbase));
mergecount += totedges;
avgcount[0] = 0; avgcount[1] = 0; avgcount[2] = 0;
vcount = 0;
for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
avgcount[0] += ((EditEdge*)curredge->eed)->v1->co[0];
avgcount[1] += ((EditEdge*)curredge->eed)->v1->co[1];
avgcount[2] += ((EditEdge*)curredge->eed)->v1->co[2];
avgcount[0] += ((EditEdge*)curredge->eed)->v2->co[0];
avgcount[1] += ((EditEdge*)curredge->eed)->v2->co[1];
avgcount[2] += ((EditEdge*)curredge->eed)->v2->co[2];
vcount +=2;
}
avgcount[0] /= vcount; avgcount[1] /=vcount; avgcount[2] /= vcount;
for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
copy_v3_v3(((EditEdge*)curredge->eed)->v1->co,avgcount);
copy_v3_v3(((EditEdge*)curredge->eed)->v2->co,avgcount);
}
if (EM_texFaceCheck()) {
/*uv collapse*/
for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
curredge->eed->v1->f1 = 1;
curredge->eed->v2->f1 = 1;
curredge->eed->f1 = 1;
}
collapse_edgeuvs();
}
}
freecollections(&allcollections);
removedoublesflag(1, 0, MERGELIMIT);
/*get rid of this!*/
countall();
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
if (EM_texFaceCheck())
allqueue(REDRAWIMAGE, 0);
return mergecount;
}
int merge_firstlast(int first, int uvmerge)
{
EditVert *eve,*mergevert;
EditSelection *ese;
if(multires_test()) return 0;
/* do sanity check in mergemenu in edit.c ?*/
if(first == 0){
ese = G.editMesh->selected.last;
mergevert= (EditVert*)ese->data;
}
else{
ese = G.editMesh->selected.first;
mergevert = (EditVert*)ese->data;
}
if(mergevert->f&SELECT){
for (eve=G.editMesh->verts.first; eve; eve=eve->next){
if (eve->f&SELECT)
copy_v3_v3(eve->co,mergevert->co);
}
}
if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){
for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
if(eve->f&SELECT) eve->f1 = 1;
}
collapseuvs(mergevert);
}
countall();
return removedoublesflag(1, 0, MERGELIMIT);
}
int merge_target(int target, int uvmerge)
{
EditVert *eve;
if(multires_test()) return 0;
if(target) snap_sel_to_curs();
else snap_to_center();
if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){
for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
if(eve->f&SELECT) eve->f1 = 1;
}
collapseuvs(NULL);
}
countall();
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
return removedoublesflag(1, 0, MERGELIMIT);
}
#undef MERGELIMIT
typedef struct PathNode{
int u;
int visited;
ListBase edges;
} PathNode;
typedef struct PathEdge{
struct PathEdge *next, *prev;
int v;
float w;
} PathEdge;
void pathselect(void)
{
EditVert *eve, *s, *t;
EditEdge *eed;
EditSelection *ese;
PathEdge *newpe, *currpe;
PathNode *currpn;
PathNode *Q;
int v, *previous, pathvert, pnindex; /*pnindex redundant?*/
int unbalanced, totnodes;
short physical;
float *cost;
Heap *heap; /*binary heap for sorting pointers to PathNodes based upon a 'cost'*/
s = t = NULL;
countall(); /*paranoid?*/
ese = ((EditSelection*)G.editMesh->selected.last);
if(ese && ese->type == EDITVERT && ese->prev && ese->prev->type == EDITVERT){
physical= pupmenu("Distance Method? %t|Edge Length%x1|Topological%x0");
t = (EditVert*)ese->data;
s = (EditVert*)ese->prev->data;
/*need to find out if t is actually reachable by s....*/
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
eve->f1 = 0;
}
s->f1 = 1;
unbalanced = 1;
totnodes = 1;
while(unbalanced){
unbalanced = 0;
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
if(!eed->h){
if(eed->v1->f1 && !eed->v2->f1){
eed->v2->f1 = 1;
totnodes++;
unbalanced = 1;
}
else if(eed->v2->f1 && !eed->v1->f1){
eed->v1->f1 = 1;
totnodes++;
unbalanced = 1;
}
}
}
}
if(s->f1 && t->f1){ /*t can be reached by s*/
Q = MEM_callocN(sizeof(PathNode)*totnodes, "Path Select Nodes");
totnodes = 0;
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
if(eve->f1){
Q[totnodes].u = totnodes;
Q[totnodes].edges.first = 0;
Q[totnodes].edges.last = 0;
Q[totnodes].visited = 0;
eve->tmp.p = &(Q[totnodes]);
totnodes++;
}
else eve->tmp.p = NULL;
}
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
if(!eed->h){
if(eed->v1->f1){
currpn = ((PathNode*)eed->v1->tmp.p);
newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge");
newpe->v = ((PathNode*)eed->v2->tmp.p)->u;
if(physical){
newpe->w = len_v3v3(eed->v1->co, eed->v2->co);
}
else newpe->w = 1;
newpe->next = 0;
newpe->prev = 0;
BLI_addtail(&(currpn->edges), newpe);
}
if(eed->v2->f1){
currpn = ((PathNode*)eed->v2->tmp.p);
newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge");
newpe->v = ((PathNode*)eed->v1->tmp.p)->u;
if(physical){
newpe->w = len_v3v3(eed->v1->co, eed->v2->co);
}
else newpe->w = 1;
newpe->next = 0;
newpe->prev = 0;
BLI_addtail(&(currpn->edges), newpe);
}
}
}
heap = BLI_heap_new();
cost = MEM_callocN(sizeof(float)*totnodes, "Path Select Costs");
previous = MEM_callocN(sizeof(int)*totnodes, "PathNode indices");
for(v=0; v < totnodes; v++){
cost[v] = 1000000;
previous[v] = -1; /*array of indices*/
}
pnindex = ((PathNode*)s->tmp.p)->u;
cost[pnindex] = 0;
BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(pnindex));
while( !BLI_heap_empty(heap) ){
pnindex = GET_INT_FROM_POINTER(BLI_heap_popmin(heap));
currpn = &(Q[pnindex]);
if(currpn == (PathNode*)t->tmp.p) /*target has been reached....*/
break;
for(currpe=currpn->edges.first; currpe; currpe=currpe->next){
if(!Q[currpe->v].visited){
if( cost[currpe->v] > (cost[currpn->u ] + currpe->w) ){
cost[currpe->v] = cost[currpn->u] + currpe->w;
previous[currpe->v] = currpn->u;
Q[currpe->v].visited = 1;
BLI_heap_insert(heap, cost[currpe->v], SET_INT_IN_POINTER(currpe->v));
}
}
}
}
pathvert = ((PathNode*)t->tmp.p)->u;
while(pathvert != -1){
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
if(eve->f1){
if( ((PathNode*)eve->tmp.p)->u == pathvert) eve->f |= SELECT;
}
}
pathvert = previous[pathvert];
}
for(v=0; v < totnodes; v++) BLI_freelistN(&(Q[v].edges));
MEM_freeN(Q);
MEM_freeN(cost);
MEM_freeN(previous);
BLI_heap_free(heap, NULL);
EM_select_flush();
countall();
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
if (EM_texFaceCheck())
allqueue(REDRAWIMAGE, 0);
}
}
else{
error("Path Selection requires that exactly two vertices be selected");
return;
}
}
void region_to_loop(void)
{
EditEdge *eed;
EditFace *efa;
if(G.totfacesel){
for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
if(efa->f&SELECT){
efa->e1->f1++;
efa->e2->f1++;
efa->e3->f1++;
if(efa->e4)
efa->e4->f1++;
}
}
EM_clear_flag_all(SELECT);
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
if(eed->f1 == 1) EM_select_edge(eed, 1);
}
G.scene->selectmode = SCE_SELECT_EDGE;
EM_selectmode_set();
countall();
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
if (EM_texFaceCheck())
allqueue(REDRAWIMAGE, 0);
BIF_undo_push("Face Region to Edge Loop");
}
}
static int validate_loop(Collection *edgecollection)
{
EditEdge *eed;
EditFace *efa;
CollectedEdge *curredge;
/*1st test*/
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
curredge->eed->v1->f1 = 0;
curredge->eed->v2->f1 = 0;
}
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
curredge->eed->v1->f1++;
curredge->eed->v2->f1++;
}
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
if(curredge->eed->v1->f1 > 2) return(0); else
if(curredge->eed->v2->f1 > 2) return(0);
}
/*2nd test*/
for(eed = G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
efa->e1->f1++;
efa->e2->f1++;
efa->e3->f1++;
if(efa->e4) efa->e4->f1++;
}
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
if(curredge->eed->f1 > 2) return(0);
}
return(1);
}
static int loop_bisect(Collection *edgecollection)
{
EditFace *efa, *sf1, *sf2;
EditEdge *eed, *sed;
CollectedEdge *curredge;
int totsf1, totsf2, unbalanced,balancededges;
for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = eed->f2 = 0;
for(efa=G.editMesh->faces.first; efa; efa=efa->next) efa->f1 = 0;
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next) curredge->eed->f1 = 1;
sf1 = sf2 = NULL;
sed = ((CollectedEdge*)edgecollection->collectionbase.first)->eed;
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
if(sf2) break;
else if(sf1){
if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf2 = efa;
}
else{
if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf1 = efa;
}
}
if(sf1==NULL || sf2==NULL)
return(-1);
if(!(sf1->e1->f1)) sf1->e1->f2 = 1;
if(!(sf1->e2->f1)) sf1->e2->f2 = 1;
if(!(sf1->e3->f1)) sf1->e3->f2 = 1;
if(sf1->e4 && !(sf1->e4->f1)) sf1->e4->f2 = 1;
sf1->f1 = 1;
totsf1 = 1;
if(!(sf2->e1->f1)) sf2->e1->f2 = 2;
if(!(sf2->e2->f1)) sf2->e2->f2 = 2;
if(!(sf2->e3->f1)) sf2->e3->f2 = 2;
if(sf2->e4 && !(sf2->e4->f1)) sf2->e4->f2 = 2;
sf2->f1 = 2;
totsf2 = 1;
/*do sf1*/
unbalanced = 1;
while(unbalanced){
unbalanced = 0;
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
balancededges = 0;
if(efa->f1 == 0){
if(efa->e1->f2 == 1 || efa->e2->f2 == 1 || efa->e3->f2 == 1 || ( (efa->e4) ? efa->e4->f2 == 1 : 0) ){
balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 1;
balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 1;
balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 1;
if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 1;
if(balancededges){
unbalanced = 1;
efa->f1 = 1;
totsf1++;
}
}
}
}
}
/*do sf2*/
unbalanced = 1;
while(unbalanced){
unbalanced = 0;
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
balancededges = 0;
if(efa->f1 == 0){
if(efa->e1->f2 == 2 || efa->e2->f2 == 2 || efa->e3->f2 == 2 || ( (efa->e4) ? efa->e4->f2 == 2 : 0) ){
balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 2;
balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 2;
balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 2;
if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 2;
if(balancededges){
unbalanced = 1;
efa->f1 = 2;
totsf2++;
}
}
}
}
}
if(totsf1 < totsf2) return(1);
else return(2);
}
void loop_to_region(void)
{
EditFace *efa;
ListBase allcollections={NULL,NULL};
Collection *edgecollection;
int testflag;
build_edgecollection(&allcollections);
for(edgecollection = (Collection *)allcollections.first; edgecollection; edgecollection=edgecollection->next){
if(validate_loop(edgecollection)){
testflag = loop_bisect(edgecollection);
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
if(efa->f1 == testflag){
if(efa->f&SELECT) EM_select_face(efa, 0);
else EM_select_face(efa,1);
}
}
}
}
for(efa=G.editMesh->faces.first; efa; efa=efa->next){ /*fix this*/
if(efa->f&SELECT) EM_select_face(efa,1);
}
countall();
freecollections(&allcollections);
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
if (EM_texFaceCheck())
allqueue(REDRAWIMAGE, 0);
BIF_undo_push("Edge Loop to Face Region");
}
/* texface and vertex color editmode tools for the face menu */
void mesh_rotate_uvs(void)
{
EditMesh *em = G.editMesh;
EditFace *efa;
short change = 0, ccw;
MTFace *tf;
float u1, v1;
if (!EM_texFaceCheck()) {
error("mesh has no uv/image layers");
return;
}
ccw = (G.qual == LR_SHIFTKEY);
for(efa=em->faces.first; efa; efa=efa->next) {
if (efa->f & SELECT) {
tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
u1= tf->uv[0][0];
v1= tf->uv[0][1];
if (ccw) {
if(efa->v4) {
tf->uv[0][0]= tf->uv[3][0];
tf->uv[0][1]= tf->uv[3][1];
tf->uv[3][0]= tf->uv[2][0];
tf->uv[3][1]= tf->uv[2][1];
} else {
tf->uv[0][0]= tf->uv[2][0];
tf->uv[0][1]= tf->uv[2][1];
}
tf->uv[2][0]= tf->uv[1][0];
tf->uv[2][1]= tf->uv[1][1];
tf->uv[1][0]= u1;
tf->uv[1][1]= v1;
} else {
tf->uv[0][0]= tf->uv[1][0];
tf->uv[0][1]= tf->uv[1][1];
tf->uv[1][0]= tf->uv[2][0];
tf->uv[1][1]= tf->uv[2][1];
if(efa->v4) {
tf->uv[2][0]= tf->uv[3][0];
tf->uv[2][1]= tf->uv[3][1];
tf->uv[3][0]= u1;
tf->uv[3][1]= v1;
}
else {
tf->uv[2][0]= u1;
tf->uv[2][1]= v1;
}
}
change = 1;
}
}
if (change) {
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
BIF_undo_push("Rotate UV face");
}
}
void mesh_mirror_uvs(void)
{
EditMesh *em = G.editMesh;
EditFace *efa;
short change = 0, altaxis;
MTFace *tf;
float u1, v1;
if (!EM_texFaceCheck()) {
error("mesh has no uv/image layers");
return;
}
altaxis = (G.qual == LR_SHIFTKEY);
for(efa=em->faces.first; efa; efa=efa->next) {
if (efa->f & SELECT) {
tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
if (altaxis) {
u1= tf->uv[1][0];
v1= tf->uv[1][1];
if(efa->v4) {
tf->uv[1][0]= tf->uv[2][0];
tf->uv[1][1]= tf->uv[2][1];
tf->uv[2][0]= u1;
tf->uv[2][1]= v1;
u1= tf->uv[3][0];
v1= tf->uv[3][1];
tf->uv[3][0]= tf->uv[0][0];
tf->uv[3][1]= tf->uv[0][1];
tf->uv[0][0]= u1;
tf->uv[0][1]= v1;
}
else {
tf->uv[1][0]= tf->uv[2][0];
tf->uv[1][1]= tf->uv[2][1];
tf->uv[2][0]= u1;
tf->uv[2][1]= v1;
}
} else {
u1= tf->uv[0][0];
v1= tf->uv[0][1];
if(efa->v4) {
tf->uv[0][0]= tf->uv[1][0];
tf->uv[0][1]= tf->uv[1][1];
tf->uv[1][0]= u1;
tf->uv[1][1]= v1;
u1= tf->uv[3][0];
v1= tf->uv[3][1];
tf->uv[3][0]= tf->uv[2][0];
tf->uv[3][1]= tf->uv[2][1];
tf->uv[2][0]= u1;
tf->uv[2][1]= v1;
}
else {
tf->uv[0][0]= tf->uv[1][0];
tf->uv[0][1]= tf->uv[1][1];
tf->uv[1][0]= u1;
tf->uv[1][1]= v1;
}
}
change = 1;
}
}
if (change) {
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
BIF_undo_push("Mirror UV face");
}
}
void mesh_rotate_colors(void)
{
EditMesh *em = G.editMesh;
EditFace *efa;
short change = 0, ccw;
MCol tmpcol, *mcol;
if (!EM_vertColorCheck()) {
error("mesh has no color layers");
return;
}
ccw = (G.qual == LR_SHIFTKEY);
for(efa=em->faces.first; efa; efa=efa->next) {
if (efa->f & SELECT) {
mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
tmpcol= mcol[0];
if (ccw) {
if(efa->v4) {
mcol[0]= mcol[3];
mcol[3]= mcol[2];
} else {
mcol[0]= mcol[2];
}
mcol[2]= mcol[1];
mcol[1]= tmpcol;
} else {
mcol[0]= mcol[1];
mcol[1]= mcol[2];
if(efa->v4) {
mcol[2]= mcol[3];
mcol[3]= tmpcol;
}
else
mcol[2]= tmpcol;
}
change = 1;
}
}
if (change) {
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
BIF_undo_push("Rotate Color face");
}
}
void mesh_mirror_colors(void)
{
EditMesh *em = G.editMesh;
EditFace *efa;
short change = 0, altaxis;
MCol tmpcol, *mcol;
if (!EM_vertColorCheck()) {
error("mesh has no color layers");
return;
}
altaxis = (G.qual == LR_SHIFTKEY);
for(efa=em->faces.first; efa; efa=efa->next) {
if (efa->f & SELECT) {
mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
if (altaxis) {
tmpcol= mcol[1];
mcol[1]= mcol[2];
mcol[2]= tmpcol;
if(efa->v4) {
tmpcol= mcol[0];
mcol[0]= mcol[3];
mcol[3]= tmpcol;
}
} else {
tmpcol= mcol[0];
mcol[0]= mcol[1];
mcol[1]= tmpcol;
if(efa->v4) {
tmpcol= mcol[2];
mcol[2]= mcol[3];
mcol[3]= tmpcol;
}
}
change = 1;
}
}
if (change) {
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
allqueue(REDRAWVIEW3D, 0);
BIF_undo_push("Mirror Color face");
}
}
#endif

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

View File

@@ -0,0 +1,1215 @@
/*some of this may come back, such as split face or split edge, if necassary for speed*/
#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) 2004 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_eulers.c
* \ingroup bmesh
*
* BM Euler construction API.
*/
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "DNA_meshdata_types.h"
#include "DNA_mesh_types.h"
#include "BKE_customdata.h"
#include "BKE_utildefines.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
/*********************************************************
* "Euler API" *
* *
* *
* Primitive construction operators for mesh tools. *
* *
**********************************************************/
/*
The functions in this file represent the 'primitive' or 'atomic' operators that
mesh tools use to manipulate the topology of the structure.* The purpose of these
functions is to provide a trusted set of operators to manipulate the mesh topology
and which can also be combined together like building blocks to create more
sophisticated tools. It needs to be stressed that NO manipulation of an existing
mesh structure should be done outside of these functions.
In the BM system, each euler is named by an ancronym which describes what it actually does.
Furthermore each Euler has a logical inverse. An important design criteria of all Eulers is that
through a Euler's logical inverse you can 'undo' an operation. (Special note should
be taken of bmesh_loop_reverse, which is its own inverse).
bmesh_MF/KF: Make Face and Kill Face
bmesh_ME/KE: Make Edge and Kill Edge
bmesh_MV/KV: Make Vert and Kill Vert
bmesh_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert
bmesh_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge
bmesh_loop_reverse: Reverse a Polygon's loop cycle. (used for flip normals for one)
Using a combination of these eleven eulers any non-manifold modelling operation can be achieved.
Each Euler operator has a detailed explanation of what is does in the comments preceding its
code.
*The term "Euler Operator" is actually a misnomer when referring to a non-manifold
data structure. Its use is in keeping with the convention established by others.
BMESH_TODO:
-Make seperate 'debug levels' of validation
-Add in the UnglueFaceRegionMakeVert and GlueFaceRegionKillVert eulers.
NOTE:
-The functions in this file are notoriously difficult to debug and even understand sometimes.
better code comments would be nice....
*/
/*MAKE Eulers*/
/**
* bmesh_MV
*
* MAKE VERT EULER:
*
* Makes a single loose vertex.
*
* Returns -
* A BMVert pointer.
*/
BMVert *bmesh_mv(BMesh *bm, const float vec[3])
{
BMVert *v = bmesh_addvertlist(bm, NULL);
copy_v3_v3(v->co,vec);
return v;
}
/**
* bmesh_ME
*
* MAKE EDGE EULER:
*
* Makes a single wire edge between two vertices.
* If the caller does not want there to be duplicate
* edges between the vertices, it is up to them to check
* for this condition beforehand.
*
* Returns -
* A BMEdge pointer.
*/
BMEdge *bmesh_me(BMesh *bm, BMVert *v1, BMVert *v2)
{
BMEdge *e=NULL;
BMNode *d1=NULL, *d2=NULL;
int valance1=0, valance2=0, edok;
/*edge must be between two distinct vertices...*/
if(v1 == v2) return NULL;
#ifndef bmesh_FASTEULER
/*count valance of v1*/
if(v1->e) {
d1 = bmesh_disk_getpointer(v1->e,v1);
if(d1) valance1 = bmesh_cycle_length(d1);
else bmesh_error();
}
if(v2->e) {
d2 = bmesh_disk_getpointer(v2->e,v2);
if(d2) valance2 = bmesh_cycle_length(d2);
else bmesh_error();
}
#endif
/*go ahead and add*/
e = bmesh_addedgelist(bm, v1, v2, NULL);
bmesh_disk_append_edge(e, e->v1);
bmesh_disk_append_edge(e, e->v2);
#ifndef bmesh_FASTEULER
/*verify disk cycle lengths*/
d1 = bmesh_disk_getpointer(e, e->v1);
edok = bmesh_cycle_validate(valance1+1, d1);
if(!edok) bmesh_error();
d2 = bmesh_disk_getpointer(e, e->v2);
edok = bmesh_cycle_validate(valance2+1, d2);
if(!edok) bmesh_error();
/*verify that edge actually made it into the cycle*/
edok = bmesh_disk_hasedge(v1, e);
if(!edok) bmesh_error();
edok = bmesh_disk_hasedge(v2, e);
if(!edok) bmesh_error();
#endif
return e;
}
/**
* bmesh_MF
*
* MAKE FACE EULER:
* Takes a list of edge pointers which form a closed loop and makes a face
* from them. The first edge in elist is considered to be the start of the
* polygon, and v1 and v2 are its vertices and determine the winding of the face
* Other than the first edge, no other assumptions are made about the order of edges
* in the elist array. To verify that it is a single closed loop and derive the correct
* order a simple series of verifications is done and all elements are visited.
*
* Returns -
* A BMFace pointer
*/
#define MF_CANDIDATE 1
#define MF_VISITED 2
#define MF_TAKEN 4
BMFace *bmesh_mf(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **elist, int len)
{
BMFace *f = NULL;
BMEdge *curedge;
BMVert *curvert, *tv, **vlist;
int i, j, done, cont, edok;
if(len < 2) return NULL;
/*make sure that v1 and v2 are in elist[0]*/
//if(bmesh_verts_in_edge(v1,v2,elist[0]) == 0)
// return NULL;
/*clear euler flags*/
for(i=0;i<len;i++) {
BMNode *diskbase;
BMEdge *curedge;
BMVert *v1;
int j;
for (j=0; j<2; j++) {
int a, len=0;
v1 = j ? elist[i]->v2 : elist[i]->v1;
diskbase = bmesh_disk_getpointer(v1->e, v1);
len = bmesh_cycle_length(diskbase);
for(a=0,curedge=v1->e;a<len;a++,curedge = bmesh_disk_nextedge(curedge,v1)) {
curedge->head.eflag1 = curedge->head.eflag2 = 0;
}
}
}
for(i=0;i<len;i++) {
elist[i]->head.eflag1 |= MF_CANDIDATE;
/*if elist[i] has a loop, count its radial length*/
if(elist[i]->loop) elist[i]->head.eflag2 = bmesh_cycle_length(&(elist[i]->l->radial));
else elist[i]->head.eflag2 = 0;
}
/* For each vertex in each edge, it must have exactly two MF_CANDIDATE edges attached to it
Note that this does not gauruntee that face is a single closed loop. At best it gauruntees
that elist contains a finite number of seperate closed loops.
*/
// for(i=0; i<len; i++) {
// edok = bmesh_disk_count_edgeflag(elist[i]->v1, MF_CANDIDATE, 0);
// if(edok != 2) return NULL;
// edok = bmesh_disk_count_edgeflag(elist[i]->v2, MF_CANDIDATE, 0);
// if(edok != 2) return NULL;
// }
/*set start edge, start vert and target vert for our loop traversal*/
curedge = elist[0];
tv = v1;
curvert = v2;
if(bm->vtarlen < len) {
if (bm->vtar) MEM_freeN(bm->vtar);
bm->vtar = MEM_callocN(sizeof(BMVert *)* len, "BM Vert pointer array");
bm->vtarlen = len;
}
/*insert tv into vlist since its the first vertex in face*/
i=0;
vlist=bm->vtar;
vlist[i] = tv;
/* Basic procedure: Starting with curv we find the edge in it's disk cycle which hasn't
been visited yet. When we do, we put curv in a linked list and find the next MF_CANDIDATE
edge, loop until we find TV. We know TV is reachable because of test we did earlier.
*/
done=0;
while(!done) {
/*add curvert to vlist*/
/*insert some error cheking here for overflows*/
i++;
vlist[i] = curvert;
/*mark curedge as visited*/
curedge->head.eflag1 |= MF_VISITED;
/*find next edge and vert*/
curedge = bmesh_disk_next_edgeflag(curedge, curvert, MF_CANDIDATE, 0);
curvert = bmesh_edge_getothervert(curedge, curvert);
if(curvert == tv) {
curedge->head.eflag1 |= MF_VISITED;
done=1;
}
}
/* Verify that all edges have been visited It's possible that we did reach tv
from sv, but that several unconnected loops were passed in via elist.
*/
cont=1;
// for(i=0; i<len; i++) {
// if((elist[i]->head.eflag1 & MF_VISITED) == 0) cont = 0;
// }
/*if we get this far, its ok to allocate the face and add the loops*/
if(cont) {
BMLoop *l;
BMEdge *e;
f = bmesh_addpolylist(bm, NULL);
f->len = len;
for(i=0;i<len;i++) {
curvert = vlist[i];
l = bmesh_create_loop(bm,curvert,NULL,f,NULL);
if(!(f->loopbase)) f->lbase = l;
bmesh_cycle_append(f->lbase, l);
}
/*take care of edge pointers and radial cycle*/
for(i=0, l = f->loopbase; i<len; i++, l= l->next) {
e = NULL;
if(l == f->loopbase) e = elist[0]; /*first edge*/
else {/*search elist for others*/
for(j=1; j<len; j++) {
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, elist[j]);
if(edok) {
e = elist[j];
break;
}
}
}
l->e = e; /*set pointer*/
bmesh_radial_append(e, l); /*append into radial*/
}
f->len = len;
/*Validation Loop cycle*/
edok = bmesh_cycle_validate(len, f->lbase);
if(!edok) bmesh_error();
for(i=0, l = f->loopbase; i<len; i++, l=((l->next))) {
/*validate loop vert pointers*/
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, l->e);
if(!edok) bmesh_error();
/*validate the radial cycle of each edge*/
edok = bmesh_cycle_length(&(l->radial));
if(edok != (l->e->head.eflag2 + 1)) bmesh_error();
}
}
for(i=0;i<len;i++) elist[i]->head.eflag1=elist[i]->head.eflag2 = 0;
return f;
}
/* KILL Eulers */
/**
* bmesh_KV
*
* KILL VERT EULER:
*
* Kills a single loose vertex.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_kv(BMesh *bm, BMVert *v)
{
if(v->e == NULL) {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) bm->totvertsel--;
BLI_remlink(&(bm->verts), &(v->head));
bmesh_free_vert(bm,v);
return 1;
}
return 0;
}
/**
* bmesh_KE
*
* KILL EDGE EULER:
*
* Kills a wire edge.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_ke(BMesh *bm, BMEdge *e)
{
int edok;
/*Make sure that no faces!*/
if(e->l == NULL) {
bmesh_disk_remove_edge(e, e->v1);
bmesh_disk_remove_edge(e, e->v2);
/*verify that edge out of disk*/
edok = bmesh_disk_hasedge(e->v1, e);
if(edok) bmesh_error();
edok = bmesh_disk_hasedge(e->v2, e);
if(edok) bmesh_error();
/*remove and deallocate*/
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel--;
BLI_remlink(&(bm->edges), &(e->head));
bmesh_free_edge(bm, e);
return 1;
}
return 0;
}
/**
* bmesh_KF
*
* KILL FACE EULER:
*
* The logical inverse of bmesh_MF.
* Kills a face and removes each of its loops from the radial that it belongs to.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_kf(BMesh *bm, BMFace *bply)
{
BMLoop *newbase,*oldbase, *curloop;
int i,len=0;
/*add validation to make sure that radial cycle is cleaned up ok*/
/*deal with radial cycle first*/
len = bmesh_cycle_length(bply->lbase);
for(i=0, curloop=bply->loopbase; i < len; i++, curloop = ((curloop->next)))
bmesh_radial_remove_loop(curloop, curloop->e);
/*now deallocate the editloops*/
for(i=0; i < len; i++) {
newbase = ((bply->lbase->next));
oldbase = bply->lbase;
bmesh_cycle_remove(oldbase, oldbase);
bmesh_free_loop(bm, oldbase);
bply->loopbase = newbase;
}
if (BM_elem_flag_test(bply, BM_ELEM_SELECT)) bm->totfacesel--;
BLI_remlink(&(bm->polys), &(bply->head));
bmesh_free_poly(bm, bply);
return 1;
}
/*SPLIT Eulers*/
/**
* bmesh_SEMV
*
* SPLIT EDGE MAKE VERT:
* Takes a given edge and splits it into two, creating a new vert.
*
*
* Before: OV---------TV
* After: OV----NV---TV
*
* Returns -
* BMVert pointer.
*
*/
BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re)
{
BMVert *nv, *ov;
BMNode *diskbase;
BMEdge *ne;
int i, edok, valance1=0, valance2=0;
if(bmesh_vert_in_edge(e,tv) == 0) return NULL;
ov = bmesh_edge_getothervert(e,tv);
//v2 = tv;
/*count valance of v1*/
diskbase = bmesh_disk_getpointer(e, ov);
valance1 = bmesh_cycle_length(diskbase);
/*count valance of v2*/
diskbase = bmesh_disk_getpointer(e, tv);
valance2 = bmesh_cycle_length(diskbase);
nv = bmesh_addvertlist(bm, tv);
ne = bmesh_addedgelist(bm, nv, tv, e);
//e->v2 = nv;
/*remove e from v2's disk cycle*/
bmesh_disk_remove_edge(e, tv);
/*swap out tv for nv in e*/
bmesh_edge_swapverts(e, tv, nv);
/*add e to nv's disk cycle*/
bmesh_disk_append_edge(e, nv);
/*add ne to nv's disk cycle*/
bmesh_disk_append_edge(ne, nv);
/*add ne to tv's disk cycle*/
bmesh_disk_append_edge(ne, tv);
/*verify disk cycles*/
diskbase = bmesh_disk_getpointer(ov->e,ov);
edok = bmesh_cycle_validate(valance1, diskbase);
if(!edok) bmesh_error();
diskbase = bmesh_disk_getpointer(tv->e,tv);
edok = bmesh_cycle_validate(valance2, diskbase);
if(!edok) bmesh_error();
diskbase = bmesh_disk_getpointer(nv->e,nv);
edok = bmesh_cycle_validate(2, diskbase);
if(!edok) bmesh_error();
/*Split the radial cycle if present*/
if(e->l) {
BMLoop *nl,*l;
BMNode *radEBase=NULL, *radNEBase=NULL;
int radlen = bmesh_cycle_length(&(e->l->radial));
/*Take the next loop. Remove it from radial. Split it. Append to appropriate radials.*/
while(e->l) {
l=e->l;
l->f->len++;
bmesh_radial_remove_loop(l,e);
nl = bmesh_create_loop(bm,NULL,NULL,l->f,l);
nl->prev = (BMHeader*)l;
nl->next = (BMHeader*)(l->next);
nl->prev->next = (BMHeader*)nl;
nl->next->prev = (BMHeader*)nl;
nl->v = nv;
/*assign the correct edge to the correct loop*/
if(bmesh_verts_in_edge(nl->v, ((nl->next))->v, e)) {
nl->e = e;
l->e = ne;
/*append l into ne's rad cycle*/
if(!radNEBase) {
radNEBase = &(l->radial);
radNEBase->next = NULL;
radNEBase->prev = NULL;
}
if(!radEBase) {
radEBase = &(nl->radial);
radEBase->next = NULL;
radEBase->prev = NULL;
}
bmesh_cycle_append(radEBase,&(nl->radial));
bmesh_cycle_append(radNEBase,&(l->radial));
}
else if(bmesh_verts_in_edge(nl->v,((nl->next))->v,ne)) {
nl->e = ne;
l->e = e;
if(!radNEBase) {
radNEBase = &(nl->radial);
radNEBase->next = NULL;
radNEBase->prev = NULL;
}
if(!radEBase) {
radEBase = &(l->radial);
radEBase->next = NULL;
radEBase->prev = NULL;
}
bmesh_cycle_append(radEBase,&(l->radial));
bmesh_cycle_append(radNEBase,&(nl->radial));
}
}
e->l = radEBase->data;
ne->l = radNEBase->data;
/*verify length of radial cycle*/
edok = bmesh_cycle_validate(radlen,&(e->l->radial));
if(!edok) bmesh_error();
edok = bmesh_cycle_validate(radlen,&(ne->l->radial));
if(!edok) bmesh_error();
/*verify loop->v and loop->next->v pointers for e*/
for(i=0,l=e->l; i < radlen; i++, l = l->radial_next) {
if(!(l->e == e)) bmesh_error();
if(!(l->radial.data == l)) bmesh_error();
if( ((l->prev))->e != ne && ((l->next))->e != ne) bmesh_error();
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, e);
if(!edok) bmesh_error();
if(l->v == ((l->next))->v) bmesh_error();
if(l->e == ((l->next))->e) bmesh_error();
/*verify loop cycle for kloop->f*/
edok = bmesh_cycle_validate(l->f->len, l->f->lbase);
if(!edok) bmesh_error();
}
/*verify loop->v and loop->next->v pointers for ne*/
for(i=0,l=ne->l; i < radlen; i++, l = l->radial_next) {
if(!(l->e == ne)) bmesh_error();
if(!(l->radial.data == l)) bmesh_error();
if( ((l->prev))->e != e && ((l->next))->e != e) bmesh_error();
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, ne);
if(!edok) bmesh_error();
if(l->v == ((l->next))->v) bmesh_error();
if(l->e == ((l->next))->e) bmesh_error();
/*verify loop cycle for kloop->f. Redundant*/
edok = bmesh_cycle_validate(l->f->len, l->f->lbase);
if(!edok) bmesh_error();
}
}
if(re) *re = ne;
return nv;
}
/**
* bmesh_SFME
*
* SPLIT FACE MAKE EDGE:
*
* Takes as input two vertices in a single face. An edge is created which divides the original face
* into two distinct regions. One of the regions is assigned to the original face and it is closed off.
* The second region has a new face assigned to it.
*
* Examples:
*
* Before: After:
* ---------- ----------
* | | | |
* | | | f1 |
* v1 f1 v2 v1======v2
* | | | f2 |
* | | | |
* ---------- ----------
*
* Note that the input vertices can be part of the same edge. This will result in a two edged face.
* This is desirable for advanced construction tools and particularly essential for edge bevel. Because
* of this it is up to the caller to decide what to do with the extra edge.
*
* Note that the tesselator abuses eflag2 while using this euler! (don't ever ever do this....)
*
* Returns -
* A BMFace pointer
*/
BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **rl)
{
BMFace *f2;
BMLoop *v1loop = NULL, *v2loop = NULL, *curloop, *f1loop=NULL, *f2loop=NULL;
BMEdge *e;
int i, len, f1len, f2len;
/*verify that v1 and v2 are in face.*/
len = bmesh_cycle_length(f->lbase);
for(i = 0, curloop = f->loopbase; i < len; i++, curloop = ((curloop->next)) ) {
if(curloop->v == v1) v1loop = curloop;
else if(curloop->v == v2) v2loop = curloop;
}
if(!v1loop || !v2loop) return NULL;
/*allocate new edge between v1 and v2*/
e = bmesh_addedgelist(bm, v1, v2,NULL);
bmesh_disk_append_edge(e, v1);
bmesh_disk_append_edge(e, v2);
f2 = bmesh_addpolylist(bm,f);
f1loop = bmesh_create_loop(bm,v2,e,f,v2loop);
f2loop = bmesh_create_loop(bm,v1,e,f2,v1loop);
f1loop->prev = v2loop->prev;
f2loop->prev = v1loop->prev;
v2loop->prev->next = (BMHeader*)f1loop;
v1loop->prev->next = (BMHeader*)f2loop;
f1loop->next = (BMHeader*)v1loop;
f2loop->next = (BMHeader*)v2loop;
v1loop->prev = (BMHeader*)f1loop;
v2loop->prev = (BMHeader*)f2loop;
f2->loopbase = f2loop;
f->loopbase = f1loop;
/*validate both loops*/
/*I dont know how many loops are supposed to be in each face at this point! FIXME!*/
/*go through all of f2's loops and make sure they point to it properly.*/
f2len = bmesh_cycle_length(f2->lbase);
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->f = f2;
/*link up the new loops into the new edges radial*/
bmesh_radial_append(e, f1loop);
bmesh_radial_append(e, f2loop);
f2->len = f2len;
f1len = bmesh_cycle_length(f->lbase);
f->len = f1len;
if(rl) *rl = f2loop;
return f2;
}
/**
* bmesh_JEKV
*
* JOIN EDGE KILL VERT:
* Takes a an edge and pointer to one of its vertices and collapses
* the edge on that vertex.
*
* Before: OE KE
* ------- -------
* | || |
* OV KV TV
*
*
* After: OE
* ---------------
* | |
* OV TV
*
*
* Restrictions:
* KV is a vertex that must have a valance of exactly two. Furthermore
* both edges in KV's disk cycle (OE and KE) must be unique (no double
* edges).
*
* It should also be noted that this euler has the possibility of creating
* faces with just 2 edges. It is up to the caller to decide what to do with
* these faces.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv)
{
BMEdge *oe;
BMVert *ov, *tv;
BMNode *diskbase;
BMLoop *killoop,*nextl;
int len,radlen=0, halt = 0, i, valance1, valance2,edok;
if(bmesh_vert_in_edge(ke,kv) == 0) return 0;
diskbase = bmesh_disk_getpointer(kv->e, kv);
len = bmesh_cycle_length(diskbase);
if(len == 2) {
oe = bmesh_disk_nextedge(ke, kv);
tv = bmesh_edge_getothervert(ke, kv);
ov = bmesh_edge_getothervert(oe, kv);
halt = bmesh_verts_in_edge(kv, tv, oe); //check for double edges
if(halt) return 0;
else {
/*For verification later, count valance of ov and tv*/
diskbase = bmesh_disk_getpointer(ov->e, ov);
valance1 = bmesh_cycle_length(diskbase);
diskbase = bmesh_disk_getpointer(tv->e, tv);
valance2 = bmesh_cycle_length(diskbase);
/*remove oe from kv's disk cycle*/
bmesh_disk_remove_edge(oe,kv);
/*relink oe->kv to be oe->tv*/
bmesh_edge_swapverts(oe, kv, tv);
/*append oe to tv's disk cycle*/
bmesh_disk_append_edge(oe, tv);
/*remove ke from tv's disk cycle*/
bmesh_disk_remove_edge(ke, tv);
/*deal with radial cycle of ke*/
if(ke->l) {
/*first step, fix the neighboring loops of all loops in ke's radial cycle*/
radlen = bmesh_cycle_length(&(ke->l->radial));
for(i=0,killoop = ke->l; i<radlen; i++, killoop = bmesh_radial_nextloop(killoop)) {
/*relink loops and fix vertex pointer*/
killoop->next->prev = killoop->prev;
killoop->prev->next = killoop->next;
if( ((killoop->next))->v == kv) ((killoop->next))->v = tv;
/*fix len attribute of face*/
killoop->f->len--;
if(killoop->f->loopbase == killoop) killoop->f->lbase = ((killoop->next));
}
/*second step, remove all the hanging loops attached to ke*/
killoop = ke->l;
radlen = bmesh_cycle_length(&(ke->l->radial));
/*make sure we have enough room in bm->lpar*/
if(bm->lparlen < radlen) {
MEM_freeN(bm->lpar);
bm->lpar = MEM_callocN(sizeof(BMLoop *)* radlen, "BM Loop pointer array");
bm->lparlen = bm->lparlen * radlen;
}
/*this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well...*/
i=0;
while(i<radlen) {
bm->lpar[i] = killoop;
killoop = killoop->radial_next;
i++;
}
i=0;
while(i<radlen) {
bmesh_free_loop(bm,bm->lpar[i]);
i++;
}
/*Validate radial cycle of oe*/
edok = bmesh_cycle_validate(radlen,&(oe->l->radial));
}
/*Validate disk cycles*/
diskbase = bmesh_disk_getpointer(ov->e,ov);
edok = bmesh_cycle_validate(valance1, diskbase);
if(!edok) bmesh_error();
diskbase = bmesh_disk_getpointer(tv->e,tv);
edok = bmesh_cycle_validate(valance2, diskbase);
if(!edok) bmesh_error();
/*Validate loop cycle of all faces attached to oe*/
for(i=0,nextl = oe->l; i<radlen; i++, nextl = bmesh_radial_nextloop(nextl)) {
edok = bmesh_cycle_validate(nextl->f->len,nextl->f->lbase);
if(!edok) bmesh_error();
}
/*deallocate edge*/
BLI_remlink(&(bm->edges), &(ke->head));
bmesh_free_edge(bm, ke);
/*deallocate vertex*/
BLI_remlink(&(bm->verts), &(kv->head));
bmesh_free_vert(bm, kv);
return 1;
}
}
return 0;
}
/**
* bmesh_loop_reverse
*
* FLIP FACE EULER
*
* Changes the winding order of a face from CW to CCW or vice versa.
* This euler is a bit peculiar in compairson to others as it is its
* own inverse.
*
* BMESH_TODO: reinsert validation code.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_loop_reverse(BMesh *bm, BMFace *f)
{
BMLoop *l = f->loopbase, *curloop, *oldprev, *oldnext;
int i, j, edok, len = 0;
len = bmesh_cycle_length(l);
if(bm->edarlen < len) {
MEM_freeN(bm->edar);
bm->edar = MEM_callocN(sizeof(BMEdge *)* len, "BM Edge pointer array");
bm->edarlen = len;
}
for(i=0, curloop = l; i< len; i++, curloop= ((curloop->next)) ) {
curloop->e->head.eflag1 = 0;
curloop->e->head.eflag2 = bmesh_cycle_length(&curloop->radial);
bmesh_radial_remove_loop(curloop, curloop->e);
/*in case of border edges we HAVE to zero out curloop->radial Next/Prev*/
curloop->radial.next = curloop->radial.prev = NULL;
bm->edar[i] = curloop->e;
}
/*actually reverse the loop. This belongs in bmesh_cycle_reverse!*/
for(i=0, curloop = l; i < len; i++) {
oldnext = ((curloop->next));
oldprev = ((curloop->prev));
curloop->next = (BMHeader*)oldprev;
curloop->prev = (BMHeader*)oldnext;
curloop = oldnext;
}
if(len == 2) { //two edged face
//do some verification here!
l->e = bm->edar[1];
((l->next))->e = bm->edar[0];
}
else {
for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) {
edok = 0;
for(j=0; j < len; j++) {
edok = bmesh_verts_in_edge(curloop->v, ((curloop->next))->v, bm->edar[j]);
if(edok) {
curloop->e = bm->edar[j];
break;
}
}
}
}
/*rebuild radial*/
for(i=0, curloop = l; i < len; i++, curloop = curloop->next ) bmesh_radial_append(curloop->e, curloop);
/*validate radial*/
for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) {
edok = bmesh_cycle_validate(curloop->e->head.eflag2, &(curloop->radial));
if(!edok) {
bmesh_error();
}
}
return 1;
}
/**
* bmesh_JFKE
*
* JOIN FACE KILL EDGE:
*
* Takes two faces joined by a single 2-manifold edge and fuses them togather.
* The edge shared by the faces must not be connected to any other edges which have
* Both faces in its radial cycle
*
* Examples:
*
* A B
* ---------- ----------
* | | | |
* | f1 | | f1 |
* v1========v2 = Ok! v1==V2==v3 == Wrong!
* | f2 | | f2 |
* | | | |
* ---------- ----------
*
* In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used.
* In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller
* in this case should call bmesh_JEKV on the extra edges before attempting to fuse f1 and f2.
*
* Also note that the order of arguments decides whether or not certain per-face attributes are present
* in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited
* from f1, not f2.
*
* Returns -
* A BMFace pointer
*/
//disregarding f1loop and f2loop, if a vertex appears in a joined face more than once, we cancel
BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
{
BMLoop *curloop, *f1loop=NULL, *f2loop=NULL;
int loopok = 0, newlen = 0,i, f1len=0, f2len=0, radlen=0, edok, shared;
if(f1 == f2) return NULL; //can't join a face to itself
/*verify that e is in both f1 and f2*/
f1len = bmesh_cycle_length(f1->lbase);
f2len = bmesh_cycle_length(f2->lbase);
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) {
if(curloop->e == e) {
f1loop = curloop;
break;
}
}
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) {
if(curloop->e==e) {
f2loop = curloop;
break;
}
}
if(!(f1loop && f2loop)) return NULL;
/*validate that edge is 2-manifold edge*/
radlen = bmesh_cycle_length(&(f1loop->radial));
if(radlen != 2) return NULL;
/*validate direction of f2's loop cycle is compatible.*/
if(f1loop->v == f2loop->v) return NULL;
/*
validate that for each face, each vertex has another edge in its disk cycle that is
not e, and not shared.
*/
if(bmesh_radial_find_face( ((f1loop->next))->e,f2)) return NULL;
if(bmesh_radial_find_face( ((f1loop->prev))->e,f2)) return NULL;
if(bmesh_radial_find_face( ((f2loop->next))->e,f1)) return NULL;
if(bmesh_radial_find_face( ((f2loop->prev))->e,f1)) return NULL;
/*validate only one shared edge*/
shared = BM_face_share_edges(f1,f2);
if(shared > 1) return NULL;
/*validate no internal joins*/
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0;
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0;
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) {
if(curloop != f1loop)
curloop->v->head.eflag1++;
}
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) {
if(curloop != f2loop)
curloop->v->head.eflag1++;
}
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) {
if(curloop->v->head.eflag1 > 1)
return NULL;
}
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) {
if(curloop->v->head.eflag1 > 1)
return NULL;
}
/*join the two loops*/
f1loop->prev->next = f2loop->next;
f2loop->next->prev = f1loop->prev;
f1loop->next->prev = f2loop->prev;
f2loop->prev->next = f1loop->next;
/*if f1loop was baseloop, give f1loop->next the base.*/
if(f1->loopbase == f1loop) f1->lbase = ((f1loop->next));
/*validate the new loop*/
loopok = bmesh_cycle_validate((f1len+f2len)-2, f1->lbase);
if(!loopok) bmesh_error();
/*make sure each loop points to the proper face*/
newlen = bmesh_cycle_length(f1->lbase);
for(i = 0, curloop = f1->loopbase; i < newlen; i++, curloop = ((curloop->next)) ) curloop->f = f1;
f1->len = newlen;
edok = bmesh_cycle_validate(f1->len, f1->lbase);
if(!edok) bmesh_error();
/*remove edge from the disk cycle of its two vertices.*/
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1);
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2);
/*deallocate edge and its two loops as well as f2*/
BLI_remlink(&(bm->edges), &(f1loop->e->head));
BLI_remlink(&(bm->polys), &(f2->head));
bmesh_free_edge(bm, f1loop->e);
bmesh_free_loop(bm, f1loop);
bmesh_free_loop(bm, f2loop);
bmesh_free_poly(bm, f2);
return f1;
}
/**
* bmesh_URMV
*
* UNGLUE REGION MAKE VERT:
*
* Takes a locally manifold disk of face corners and 'unglues' it
* creating a new vertex
*
**/
#define URMV_VISIT 1
#define URMV_VISIT2 2
BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv)
{
BMVert *nv = NULL;
BMLoop *l = NULL, *sl = NULL;
BMEdge *curedge = NULL;
int numloops = 0, numedges = 0, i, maxedges, maxloops;
/*BMESH_TODO: Validation*/
/*validate radial cycle of all collected loops*/
/*validate the disk cycle of sv, and nv*/
/*validate the face length of all faces? overkill?*/
/*validate the l->e pointers of all affected faces, ie: l->v and l->next->v should be equivalent to l->e*/
/*verify that sv has edges*/
if(sv->e == NULL)
return NULL;
/*first verify no wire edges on sv*/
curedge = sv->e;
do {
if(curedge->l == NULL)
return NULL;
curedge = bmesh_disk_nextedge(curedge, sv);
} while(curedge != sv->e);
/*next verify that sv is in sf*/
l = sf->loopbase;
do {
if(l->v == sv) {
sl = l;
break;
}
l = (l->next);
} while(l != sf->lbase);
if(sl == NULL)
return NULL;
/*clear euler flags*/
sv->head.eflag1 = 0;
curedge = sv->e;
do {
curedge->head.eflag1 = 0;
l = curedge->l;
do {
l->head.eflag1 = 0;
l->f->head.eflag1 = 0;
l = bmesh_radial_nextloop(l);
} while(l != curedge->l);
curedge = bmesh_disk_nextedge(curedge, sv);
} while(curedge != sv->e);
/*search through face disk and flag elements as we go.*/
/*Note, test this to make sure that it works correct on
non-manifold faces!
*/
l = sl;
l->e->head.eflag1 |= URMV_VISIT;
l->f->head.eflag1 |= URMV_VISIT;
do {
if(l->v == sv)
l = bmesh_radial_nextloop((l->prev));
else
l = bmesh_radial_nextloop((l->next));
l->e->head.eflag1 |= URMV_VISIT;
l->f->head.eflag1 |= URMV_VISIT;
} while(l != sl && (bmesh_cycle_length(&(l->radial)) > 1) );
/*Verify that all visited edges are at least 1 or 2 manifold*/
curedge = sv->e;
do {
if(curedge->head.eflag1 && (bmesh_cycle_length(&(curedge->l->radial)) > 2) )
return NULL;
curedge = bmesh_disk_nextedge(curedge, sv);
} while(curedge != sv->e);
/*allocate temp storage - we overallocate here instead of trying to be clever*/
maxedges = 0;
maxloops = 0;
curedge = sv->e;
do {
if(curedge->l) {
l = curedge->l;
do {
maxloops += l->f->len;
l = bmesh_radial_nextloop(l);
} while(l != curedge->l);
}
maxedges+= 1;
curedge = bmesh_disk_nextedge(curedge,sv);
} while(curedge != sv->e);
if(bm->edarlen < maxedges) {
MEM_freeN(bm->edar);
bm->edar = MEM_callocN(sizeof(BMEdge *) * maxedges, "BM Edge pointer array");
bm->edarlen = maxedges;
}
if(bm->lparlen < maxloops) {
MEM_freeN(bm->lpar);
bm->lpar = MEM_callocN(sizeof(BMLoop *) * maxloops, "BM Loop pointer array");
bm->lparlen = maxloops;
}
/*first get loops by looping around edges and loops around that edges faces*/
curedge = sv->e;
do {
if(curedge->l) {
l = curedge->l;
do {
if( (l->head.eflag1 & URMV_VISIT) && (!(l->head.eflag1 & URMV_VISIT2)) ) {
bm->lpar[numloops] = l;
l->head.eflag1 |= URMV_VISIT2;
numloops++;
}
l = bmesh_radial_nextloop(l);
} while(l != curedge->l);
}
curedge = bmesh_disk_nextedge(curedge, sv);
} while(curedge != sv->e);
/*now collect edges by looping around edges and looking at visited flags*/
curedge = sv->e;
do {
if(curedge->head.eflag1 & URMV_VISIT) {
bm->edar[numedges] = curedge;
numedges++;
}
curedge = bmesh_disk_nextedge(curedge, sv);
} while(curedge != sv->e);
/*make new vertex*/
nv = bmesh_addvertlist(bm, sv);
/*go through and relink edges*/
for(i = 0; i < numedges; i++) {
curedge = bm->edar[i];
/*remove curedge from sv*/
bmesh_disk_remove_edge(curedge, sv);
/*swap out sv for nv in curedge*/
bmesh_edge_swapverts(curedge, sv, nv);
/*add curedge to nv's disk cycle*/
bmesh_disk_append_edge(curedge, nv);
}
/*go through and relink loops*/
for(i = 0; i < numloops; i ++) {
l = bm->lpar[i];
if(l->v == sv)
l->v = nv;
}
return nv;
}
#endif

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

View File

@@ -0,0 +1,2024 @@
/*
* ***** 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_newcore.c
* \ingroup bmesh
*
*/
#include "MEM_guardedalloc.h"
#include "BLI_math_vector.h"
#include "BKE_DerivedMesh.h"
#include "BLI_listbase.h"
#include "BLI_array.h"
#include "bmesh.h"
#include "bmesh_private.h"
/* use so valgrinds memcheck alerts us when undefined index is used.
* TESTING ONLY! */
// #define USE_DEBUG_INDEX_MEMCHECK
#ifdef USE_DEBUG_INDEX_MEMCHECK
#define DEBUG_MEMCHECK_INDEX_INVALIDATE(ele) \
{ \
int undef_idx; \
BM_elem_index_set(ele, undef_idx); /* set_ok_invalid */ \
} \
#endif
BMVert *BM_vert_create(BMesh *bm, const float co[3], const struct BMVert *example)
{
BMVert *v = BLI_mempool_calloc(bm->vpool);
#ifdef USE_DEBUG_INDEX_MEMCHECK
DEBUG_MEMCHECK_INDEX_INVALIDATE(v)
#else
BM_elem_index_set(v, -1); /* set_ok_invalid */
#endif
bm->elem_index_dirty |= BM_VERT; /* may add to middle of the pool */
bm->totvert++;
v->head.htype = BM_VERT;
/* 'v->no' is handled by BM_elem_attrs_copy */
if (co) copy_v3_v3(v->co, co);
/* allocate flag */
v->oflags = BLI_mempool_calloc(bm->toolflagpool);
CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
if (example) {
BM_elem_attrs_copy(bm, bm, (BMVert *)example, (BMVert *)v);
}
BM_CHECK_ELEMENT(bm, v);
return (BMVert *) v;
}
/**
* BMESH EDGE EXIST
*
* Finds out if two vertices already have an edge
* connecting them. Note that multiple edges may
* exist between any two vertices, and therefore
* This function only returns the first one found.
*
* Returns -
* BMEdge pointer
*/
BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2)
{
BMIter iter;
BMEdge *e;
BM_ITER(e, &iter, NULL, BM_EDGES_OF_VERT, v1) {
if (e->v1 == v2 || e->v2 == v2)
return e;
}
return NULL;
}
BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble)
{
BMEdge *e;
if (nodouble && (e = BM_edge_exists(v1, v2)))
return (BMEdge *)e;
e = BLI_mempool_calloc(bm->epool);
#ifdef USE_DEBUG_INDEX_MEMCHECK
DEBUG_MEMCHECK_INDEX_INVALIDATE(e)
#else
BM_elem_index_set(e, -1); /* set_ok_invalid */
#endif
bm->elem_index_dirty |= BM_EDGE; /* may add to middle of the pool */
bm->totedge++;
e->head.htype = BM_EDGE;
/* allocate flag */
e->oflags = BLI_mempool_calloc(bm->toolflagpool);
e->v1 = (BMVert *) v1;
e->v2 = (BMVert *) v2;
CustomData_bmesh_set_default(&bm->edata, &e->head.data);
bmesh_disk_append_edge(e, e->v1);
bmesh_disk_append_edge(e, e->v2);
if (example)
BM_elem_attrs_copy(bm, bm, (BMEdge *)example, (BMEdge *)e);
BM_CHECK_ELEMENT(bm, e);
return (BMEdge *) e;
}
static BMLoop *bmesh_create_loop(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, const BMLoop *example)
{
BMLoop *l = NULL;
l = BLI_mempool_calloc(bm->lpool);
l->next = l->prev = NULL;
l->v = v;
l->e = e;
l->f = f;
l->radial_next = l->radial_prev = NULL;
l->head.data = NULL;
l->head.htype = BM_LOOP;
bm->totloop++;
if (example)
CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, example->head.data, &l->head.data);
else
CustomData_bmesh_set_default(&bm->ldata, &l->head.data);
return l;
}
static BMLoop *bm_face_boundry_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte)
{
#ifdef USE_BMESH_HOLES
BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool);
#endif
BMLoop *l = bmesh_create_loop(bm, startv, starte, f, NULL);
bmesh_radial_append(starte, l);
#ifdef USE_BMESH_HOLES
lst->first = lst->last = l;
BLI_addtail(&f->loops, lst);
#else
f->l_first = l;
#endif
l->f = f;
return l;
}
BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts)
{
BMEdge **edges = NULL;
BMVert **verts = NULL;
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
BMLoop *l_iter;
BMLoop *l_first;
BMLoop *l2;
BMFace *f2;
int i;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (copyverts) {
BMVert *v = BM_vert_create(bm, l_iter->v->co, l_iter->v);
BLI_array_append(verts, v);
}
else {
BLI_array_append(verts, l_iter->v);
}
} while ((l_iter = l_iter->next) != l_first);
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
i = 0;
do {
if (copyedges) {
BMEdge *e;
BMVert *v1, *v2;
if (l_iter->e->v1 == verts[i]) {
v1 = verts[i];
v2 = verts[(i + 1) % f->len];
}
else {
v2 = verts[i];
v1 = verts[(i + 1) % f->len];
}
e = BM_edge_create(bm, v1, v2, l_iter->e, FALSE);
BLI_array_append(edges, e);
}
else {
BLI_array_append(edges, l_iter->e);
}
i++;
} while ((l_iter = l_iter->next) != l_first);
f2 = BM_face_create(bm, verts, edges, f->len, FALSE);
BM_elem_attrs_copy(bm, bm, f, f2);
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
l2 = BM_FACE_FIRST_LOOP(f2);
do {
BM_elem_attrs_copy(bm, bm, l_iter, l2);
l2 = l2->next;
} while ((l_iter = l_iter->next) != l_first);
return f2;
}
BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble)
{
BMFace *f = NULL;
BMLoop *l, *startl, *lastl;
int i, overlap;
if (len == 0) {
/* just return NULL for no */
return NULL;
}
if (nodouble) {
/* Check if face already exists */
overlap = BM_face_exists(bm, verts, len, &f);
if (overlap) {
return f;
}
else {
BLI_assert(f == NULL);
}
}
f = BLI_mempool_calloc(bm->fpool);
#ifdef USE_DEBUG_INDEX_MEMCHECK
DEBUG_MEMCHECK_INDEX_INVALIDATE(f)
#else
BM_elem_index_set(f, -1); /* set_ok_invalid */
#endif
bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */
bm->totface++;
f->head.htype = BM_FACE;
startl = lastl = bm_face_boundry_add(bm, (BMFace *)f, verts[0], edges[0]);
startl->v = (BMVert *)verts[0];
startl->e = (BMEdge *)edges[0];
for (i = 1; i < len; i++) {
l = bmesh_create_loop(bm, verts[i], edges[i], (BMFace *)f, edges[i]->l);
l->f = (BMFace *) f;
bmesh_radial_append(edges[i], l);
l->prev = lastl;
lastl->next = l;
lastl = l;
}
/* allocate flag */
f->oflags = BLI_mempool_calloc(bm->toolflagpool);
CustomData_bmesh_set_default(&bm->pdata, &f->head.data);
startl->prev = lastl;
lastl->next = startl;
f->len = len;
#ifdef USE_BMESH_HOLES
f->totbounds = 0;
#endif
BM_CHECK_ELEMENT(bm, f);
return (BMFace *) f;
}
int bmesh_check_element(BMesh *UNUSED(bm), void *element, const char htype)
{
BMHeader *head = element;
int err = 0;
if (!element)
return 1;
if (head->htype != htype)
return 2;
switch (htype) {
case BM_VERT: {
BMVert *v = element;
if (v->e && v->e->head.htype != BM_EDGE) {
err |= 4;
}
break;
}
case BM_EDGE: {
BMEdge *e = element;
if (e->l && e->l->head.htype != BM_LOOP)
err |= 8;
if (e->l && e->l->f->head.htype != BM_FACE)
err |= 16;
if (e->v1_disk_link.prev == NULL ||
e->v2_disk_link.prev == NULL ||
e->v1_disk_link.next == NULL ||
e->v2_disk_link.next == NULL)
{
err |= 32;
}
if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL))
err |= 64;
if (e->l && e->l->f->len <= 0)
err |= 128;
break;
}
case BM_LOOP: {
BMLoop *l = element, *l2;
int i;
if (l->f->head.htype != BM_FACE)
err |= 256;
if (l->e->head.htype != BM_EDGE)
err |= 512;
if (l->v->head.htype != BM_VERT)
err |= 1024;
if (!BM_vert_in_edge(l->e, l->v)) {
fprintf(stderr, "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n", __func__);
err |= 2048;
}
if (l->radial_next == NULL || l->radial_prev == NULL)
err |= (1 << 12);
if (l->f->len <= 0)
err |= (1 << 13);
/* validate boundary loop--invalid for hole loops, of course,
* but we won't be allowing those for a while ye */
l2 = l;
i = 0;
do {
if (i >= BM_NGON_MAX) {
break;
}
i++;
} while ((l2 = l2->next) != l);
if (i != l->f->len || l2 != l)
err |= (1 << 14);
if (!bmesh_radial_validate(bmesh_radial_length(l), l))
err |= (1 << 15);
break;
}
case BM_FACE: {
BMFace *f = element;
BMLoop *l_iter;
BMLoop *l_first;
int len = 0;
#ifdef USE_BMESH_HOLES
if (!f->loops.first)
#else
if (!f->l_first)
#endif
{
err |= (1 << 16);
}
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (l_iter->f != f) {
fprintf(stderr, "%s: loop inside one face points to another! (bmesh internal error)\n", __func__);
err |= (1 << 17);
}
if (!l_iter->e)
err |= (1 << 18);
if (!l_iter->v)
err |= (1 << 19);
if (!BM_vert_in_edge(l_iter->e, l_iter->v) || !BM_vert_in_edge(l_iter->e, l_iter->next->v)) {
err |= (1 << 20);
}
if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter))
err |= (1 << 21);
if (!bmesh_disk_count(l_iter->v) || !bmesh_disk_count(l_iter->next->v))
err |= (1 << 22);
len++;
} while ((l_iter = l_iter->next) != l_first);
if (len != f->len)
err |= (1 << 23);
}
}
if (err) {
bmesh_error();
}
return err;
}
/* low level function, only free's,
* does not change adjust surrounding geometry */
static void bmesh_kill_only_vert(BMesh *bm, BMVert *v)
{
bm->totvert--;
bm->elem_index_dirty |= BM_VERT;
BM_select_history_remove(bm, v);
if (v->head.data)
CustomData_bmesh_free_block(&bm->vdata, &v->head.data);
BLI_mempool_free(bm->toolflagpool, v->oflags);
BLI_mempool_free(bm->vpool, v);
}
static void bmesh_kill_only_edge(BMesh *bm, BMEdge *e)
{
bm->totedge--;
bm->elem_index_dirty |= BM_EDGE;
BM_select_history_remove(bm, e);
if (e->head.data)
CustomData_bmesh_free_block(&bm->edata, &e->head.data);
BLI_mempool_free(bm->toolflagpool, e->oflags);
BLI_mempool_free(bm->epool, e);
}
static void bmesh_kill_only_face(BMesh *bm, BMFace *f)
{
if (bm->act_face == f)
bm->act_face = NULL;
bm->totface--;
bm->elem_index_dirty |= BM_FACE;
BM_select_history_remove(bm, f);
if (f->head.data)
CustomData_bmesh_free_block(&bm->pdata, &f->head.data);
BLI_mempool_free(bm->toolflagpool, f->oflags);
BLI_mempool_free(bm->fpool, f);
}
static void bmesh_kill_only_loop(BMesh *bm, BMLoop *l)
{
bm->totloop--;
if (l->head.data)
CustomData_bmesh_free_block(&bm->ldata, &l->head.data);
BLI_mempool_free(bm->lpool, l);
}
void BM_face_edges_kill(BMesh *bm, BMFace *f)
{
BMEdge **edges = NULL;
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
BMLoop *l_iter;
BMLoop *l_first;
int i;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BLI_array_append(edges, l_iter->e);
} while ((l_iter = l_iter->next) != l_first);
for (i = 0; i < BLI_array_count(edges); i++) {
BM_edge_kill(bm, edges[i]);
}
BLI_array_free(edges);
}
void BM_face_verts_kill(BMesh *bm, BMFace *f)
{
BMVert **verts = NULL;
BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
BMLoop *l_iter;
BMLoop *l_first;
int i;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BLI_array_append(verts, l_iter->v);
} while ((l_iter = l_iter->next) != l_first);
for (i = 0; i < BLI_array_count(verts); i++) {
BM_vert_kill(bm, verts[i]);
}
BLI_array_free(verts);
}
void BM_face_kill(BMesh *bm, BMFace *f)
{
#ifdef USE_BMESH_HOLES
BMLoopList *ls, *ls_next;
#endif
BM_CHECK_ELEMENT(bm, f);
#ifdef USE_BMESH_HOLES
for (ls = f->loops.first; ls; ls = ls_next)
#else
if (f->l_first)
#endif
{
BMLoop *l_iter, *l_next, *l_first;
#ifdef USE_BMESH_HOLES
ls_next = ls->next;
l_iter = l_first = ls->first;
#else
l_iter = l_first = f->l_first;
#endif
do {
l_next = l_iter->next;
bmesh_radial_remove_loop(l_iter, l_iter->e);
bmesh_kill_only_loop(bm, l_iter);
} while ((l_iter = l_next) != l_first);
#ifdef USE_BMESH_HOLES
BLI_mempool_free(bm->looplistpool, ls);
#endif
}
bmesh_kill_only_face(bm, f);
}
void BM_edge_kill(BMesh *bm, BMEdge *e)
{
bmesh_disk_remove_edge(e, e->v1);
bmesh_disk_remove_edge(e, e->v2);
if (e->l) {
BMLoop *l = e->l, *lnext, *startl = e->l;
do {
lnext = l->radial_next;
if (lnext->f == l->f) {
BM_face_kill(bm, l->f);
break;
}
BM_face_kill(bm, l->f);
if (l == lnext)
break;
l = lnext;
} while (l != startl);
}
bmesh_kill_only_edge(bm, e);
}
void BM_vert_kill(BMesh *bm, BMVert *v)
{
if (v->e) {
BMEdge *e, *nexte;
e = v->e;
while (v->e) {
nexte = bmesh_disk_nextedge(e, v);
BM_edge_kill(bm, e);
e = nexte;
}
}
bmesh_kill_only_vert(bm, v);
}
/********** private disk and radial cycle functions ********** */
/**
* bmesh_loop_reverse
*
* FLIP FACE EULER
*
* Changes the winding order of a face from CW to CCW or vice versa.
* This euler is a bit peculiar in compairson to others as it is its
* own inverse.
*
* BMESH_TODO: reinsert validation code.
*
* Returns -
* 1 for success, 0 for failure.
*/
static int bmesh_loop_length(BMLoop *l)
{
BMLoop *l_first = l;
int i = 0;
do {
i++;
} while ((l = l->next) != l_first);
return i;
}
static int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f
#ifdef USE_BMESH_HOLES
, BMLoopList *lst
#endif
)
{
#ifdef USE_BMESH_HOLES
BMLoop *l_first = lst->first;
#else
BMLoop *l_first = f->l_first;
#endif
BMLoop *l_iter, *oldprev, *oldnext;
BMEdge **edar = NULL;
MDisps *md;
BLI_array_staticdeclare(edar, BM_NGON_STACK_SIZE);
int i, j, edok, len = 0, do_disps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
len = bmesh_loop_length(l_first);
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
BMEdge *curedge = l_iter->e;
bmesh_radial_remove_loop(l_iter, curedge);
BLI_array_append(edar, curedge);
}
/* actually reverse the loop */
for (i = 0, l_iter = l_first; i < len; i++) {
oldnext = l_iter->next;
oldprev = l_iter->prev;
l_iter->next = oldprev;
l_iter->prev = oldnext;
l_iter = oldnext;
if (do_disps) {
float (*co)[3];
int x, y, sides;
md = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS);
if (!md->totdisp || !md->disps)
continue;
sides = (int)sqrt(md->totdisp);
co = md->disps;
for (x = 0; x < sides; x++) {
for (y = 0; y < x; y++) {
swap_v3_v3(co[y * sides + x], co[sides * x + y]);
}
}
}
}
if (len == 2) { /* two edged face */
/* do some verification here! */
l_first->e = edar[1];
l_first->next->e = edar[0];
}
else {
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
edok = 0;
for (j = 0; j < len; j++) {
edok = bmesh_verts_in_edge(l_iter->v, l_iter->next->v, edar[j]);
if (edok) {
l_iter->e = edar[j];
break;
}
}
}
}
/* rebuild radia */
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next)
bmesh_radial_append(l_iter->e, l_iter);
/* validate radia */
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
BM_CHECK_ELEMENT(bm, l_iter);
BM_CHECK_ELEMENT(bm, l_iter->e);
BM_CHECK_ELEMENT(bm, l_iter->v);
BM_CHECK_ELEMENT(bm, l_iter->f);
}
BLI_array_free(edar);
BM_CHECK_ELEMENT(bm, f);
return 1;
}
int bmesh_loop_reverse(BMesh *bm, BMFace *f)
{
#ifdef USE_BMESH_HOLES
return bmesh_loop_reverse_loop(bm, f, f->loops.first);
#else
return bmesh_loop_reverse_loop(bm, f);
#endif
}
static void bmesh_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag)
{
BMHeader **eles = veles;
int i;
for (i = 0; i < tot; i++) {
BM_ELEM_API_FLAG_ENABLE((BMElemF *)eles[i], flag);
}
}
static void bmesh_clear_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag)
{
BMHeader **eles = veles;
int i;
for (i = 0; i < tot; i++) {
BM_ELEM_API_FLAG_DISABLE((BMElemF *)eles[i], flag);
}
}
#define FACE_MARK (1 << 10)
static int count_flagged_radial(BMesh *bm, BMLoop *l, int flag)
{
BMLoop *l2 = l;
int i = 0, c = 0;
do {
if (!l2) {
bmesh_error();
goto error;
}
i += BM_ELEM_API_FLAG_TEST(l2->f, flag) ? 1 : 0;
l2 = bmesh_radial_nextloop(l2);
if (c >= BM_LOOP_RADIAL_MAX) {
bmesh_error();
goto error;
}
c++;
} while (l2 != l);
return i;
error:
BMO_error_raise(bm, bm->currentop, BMERR_MESH_ERROR, NULL);
return 0;
}
static int UNUSED_FUNCTION(count_flagged_disk)(BMVert *v, int flag)
{
BMEdge *e = v->e;
int i = 0;
if (!e)
return 0;
do {
i += BM_ELEM_API_FLAG_TEST(e, flag) ? 1 : 0;
e = bmesh_disk_nextedge(e, v);
} while (e != v->e);
return i;
}
static int disk_is_flagged(BMVert *v, int flag)
{
BMEdge *e = v->e;
if (!e)
return FALSE;
do {
BMLoop *l = e->l;
if (!l) {
return FALSE;
}
if (bmesh_radial_length(l) == 1)
return FALSE;
do {
if (!BM_ELEM_API_FLAG_TEST(l->f, flag))
return FALSE;
l = l->radial_next;
} while (l != e->l);
e = bmesh_disk_nextedge(e, v);
} while (e != v->e);
return TRUE;
}
/* Midlevel Topology Manipulation Functions */
/*
* BM_faces_join
*
* Joins a collected group of faces into one. Only restriction on
* the input data is that the faces must be connected to each other.
*
* If a pair of faces share multiple edges, the pair of
* faces will be joined at every edge.
*
* Returns a pointer to the combined face.
*/
BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface)
{
BMFace *f, *newf;
#ifdef USE_BMESH_HOLES
BMLoopList *lst;
ListBase holes = {NULL, NULL};
#endif
BMLoop *l_iter;
BMLoop *l_first;
BMEdge **edges = NULL;
BMEdge **deledges = NULL;
BMVert **delverts = NULL;
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
BLI_array_staticdeclare(deledges, BM_NGON_STACK_SIZE);
BLI_array_staticdeclare(delverts, BM_NGON_STACK_SIZE);
BMVert *v1 = NULL, *v2 = NULL;
const char *err = NULL;
int i, tote = 0;
if (!totface) {
bmesh_error();
return NULL;
}
if (totface == 1)
return faces[0];
bmesh_systag_elements(bm, faces, totface, _FLAG_JF);
for (i = 0; i < totface; i++) {
f = faces[i];
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
int rlen = count_flagged_radial(bm, l_iter, _FLAG_JF);
if (rlen > 2) {
err = "Input faces do not form a contiguous manifold region";
goto error;
}
else if (rlen == 1) {
BLI_array_append(edges, l_iter->e);
if (!v1) {
v1 = l_iter->v;
v2 = BM_edge_other_vert(l_iter->e, l_iter->v);
}
tote++;
}
else if (rlen == 2) {
int d1, d2;
d1 = disk_is_flagged(l_iter->e->v1, _FLAG_JF);
d2 = disk_is_flagged(l_iter->e->v2, _FLAG_JF);
if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) {
/* don't remove an edge it makes up the side of another face
* else this will remove the face as well - campbell */
if (BM_edge_face_count(l_iter->e) <= 2) {
BLI_array_append(deledges, l_iter->e);
BM_ELEM_API_FLAG_ENABLE(l_iter->e, _FLAG_JF);
}
}
else {
if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) {
BLI_array_append(delverts, l_iter->e->v1);
BM_ELEM_API_FLAG_ENABLE(l_iter->e->v1, _FLAG_JF);
}
if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) {
BLI_array_append(delverts, l_iter->e->v2);
BM_ELEM_API_FLAG_ENABLE(l_iter->e->v2, _FLAG_JF);
}
}
}
} while ((l_iter = l_iter->next) != l_first);
#ifdef USE_BMESH_HOLES
for (lst = f->loops.first; lst; lst = lst->next) {
if (lst == f->loops.first) continue;
BLI_remlink(&f->loops, lst);
BLI_addtail(&holes, lst);
}
#endif
}
/* create region fac */
newf = BM_face_create_ngon(bm, v1, v2, edges, tote, FALSE);
if (!newf || BMO_error_occurred(bm)) {
if (!BMO_error_occurred(bm))
err = "Invalid boundary region to join faces";
goto error;
}
/* copy over loop data */
l_iter = l_first = BM_FACE_FIRST_LOOP(newf);
do {
BMLoop *l2 = l_iter->radial_next;
do {
if (BM_ELEM_API_FLAG_TEST(l2->f, _FLAG_JF))
break;
l2 = l2->radial_next;
} while (l2 != l_iter);
if (l2 != l_iter) {
/* I think this is correct */
if (l2->v != l_iter->v) {
l2 = l2->next;
}
BM_elem_attrs_copy(bm, bm, l2, l_iter);
}
} while ((l_iter = l_iter->next) != l_first);
BM_elem_attrs_copy(bm, bm, faces[0], newf);
#ifdef USE_BMESH_HOLES
/* add hole */
BLI_movelisttolist(&newf->loops, &holes);
#endif
/* update loop face pointer */
#ifdef USE_BMESH_HOLES
for (lst = newf->loops.first; lst; lst = lst->next)
#endif
{
#ifdef USE_BMESH_HOLES
l_iter = l_first = lst->first;
#else
l_iter = l_first = BM_FACE_FIRST_LOOP(newf);
#endif
do {
l_iter->f = newf;
} while ((l_iter = l_iter->next) != l_first);
}
bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF);
BM_ELEM_API_FLAG_DISABLE(newf, _FLAG_JF);
/* handle multires data */
if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
l_iter = l_first = BM_FACE_FIRST_LOOP(newf);
do {
for (i = 0; i < totface; i++) {
BM_loop_interp_multires(bm, l_iter, faces[i]);
}
} while ((l_iter = l_iter->next) != l_first);
}
/* delete old geometr */
for (i = 0; i < BLI_array_count(deledges); i++) {
BM_edge_kill(bm, deledges[i]);
}
for (i = 0; i < BLI_array_count(delverts); i++) {
BM_vert_kill(bm, delverts[i]);
}
BLI_array_free(edges);
BLI_array_free(deledges);
BLI_array_free(delverts);
BM_CHECK_ELEMENT(bm, newf);
return newf;
error:
bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF);
BLI_array_free(edges);
BLI_array_free(deledges);
BLI_array_free(delverts);
if (err) {
BMO_error_raise(bm, bm->currentop, BMERR_DISSOLVEFACES_FAILED, err);
}
return NULL;
}
static BMFace *bmesh_addpolylist(BMesh *bm, BMFace *UNUSED(example))
{
BMFace *f;
#ifdef USE_BMESH_HOLES
BMLoopList *lst;
#endif
f = BLI_mempool_calloc(bm->fpool);
#ifdef USE_BMESH_HOLES
lst = BLI_mempool_calloc(bm->looplistpool);
#endif
f->head.htype = BM_FACE;
#ifdef USE_BMESH_HOLES
BLI_addtail(&f->loops, lst);
#endif
#ifdef USE_DEBUG_INDEX_MEMCHECK
DEBUG_MEMCHECK_INDEX_INVALIDATE(f)
#else
BM_elem_index_set(f, -1); /* set_ok_invalid */
#endif
bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */
bm->totface++;
/* allocate flag */
f->oflags = BLI_mempool_calloc(bm->toolflagpool);
CustomData_bmesh_set_default(&bm->pdata, &f->head.data);
f->len = 0;
#ifdef USE_BMESH_HOLES
f->totbounds = 1;
#endif
return (BMFace *) f;
}
/**
* bmesh_SFME
*
* SPLIT FACE MAKE EDGE:
*
* Takes as input two vertices in a single face. An edge is created which divides the original face
* into two distinct regions. One of the regions is assigned to the original face and it is closed off.
* The second region has a new face assigned to it.
*
* Examples:
*
* Before: After:
* ---------- ----------
* | | | |
* | | | f1 |
* v1 f1 v2 v1======v2
* | | | f2 |
* | | | |
* ---------- ----------
*
* Note that the input vertices can be part of the same edge. This will
* result in a two edged face. This is desirable for advanced construction
* tools and particularly essential for edge bevel. Because of this it is
* up to the caller to decide what to do with the extra edge.
*
* If holes is NULL, then both faces will lose
* all holes from the original face. Also, you cannot split between
* a hole vert and a boundary vert; that case is handled by higher-
* level wrapping functions (when holes are fully implemented, anyway).
*
* Note that holes represents which holes goes to the new face, and of
* course this requires removing them from the exitsing face first, since
* you cannot have linked list links inside multiple lists.
*
* Returns -
* A BMFace pointer
*/
BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2,
BMLoop **rl
#ifdef USE_BMESH_HOLES
, ListBase *holes
#endif
)
{
#ifdef USE_BMESH_HOLES
BMLoopList *lst, *lst2;
#endif
BMFace *f2;
BMLoop *l_iter, *l_first;
BMLoop *v1loop = NULL, *v2loop = NULL, *f1loop = NULL, *f2loop = NULL;
BMEdge *e;
int i, len, f1len, f2len;
/* verify that v1 and v2 are in face */
len = f->len;
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < len; i++, l_iter = l_iter->next) {
if (l_iter->v == v1) v1loop = l_iter;
else if (l_iter->v == v2) v2loop = l_iter;
}
if (!v1loop || !v2loop) {
return NULL;
}
/* allocate new edge between v1 and v2 */
e = BM_edge_create(bm, v1, v2, NULL, FALSE);
f2 = bmesh_addpolylist(bm, f);
f1loop = bmesh_create_loop(bm, v2, e, f, v2loop);
f2loop = bmesh_create_loop(bm, v1, e, f2, v1loop);
f1loop->prev = v2loop->prev;
f2loop->prev = v1loop->prev;
v2loop->prev->next = f1loop;
v1loop->prev->next = f2loop;
f1loop->next = v1loop;
f2loop->next = v2loop;
v1loop->prev = f1loop;
v2loop->prev = f2loop;
#ifdef USE_BMESH_HOLES
lst = f->loops.first;
lst2 = f2->loops.first;
lst2->first = lst2->last = f2loop;
lst->first = lst->last = f1loop;
#else
f2->l_first = f2loop;
f->l_first = f1loop;
#endif
/* validate both loop */
/* I dont know how many loops are supposed to be in each face at this point! FIXME */
/* go through all of f2's loops and make sure they point to it properly */
l_iter = l_first = BM_FACE_FIRST_LOOP(f2);
f2len = 0;
do {
l_iter->f = f2;
f2len++;
} while ((l_iter = l_iter->next) != l_first);
/* link up the new loops into the new edges radia */
bmesh_radial_append(e, f1loop);
bmesh_radial_append(e, f2loop);
f2->len = f2len;
f1len = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
f1len++;
} while ((l_iter = l_iter->next) != l_first);
f->len = f1len;
if (rl) *rl = f2loop;
#ifdef USE_BMESH_HOLES
if (holes) {
BLI_movelisttolist(&f2->loops, holes);
}
else {
/* this code is not significant until holes actually work */
//printf("warning: call to split face euler without holes argument; holes will be tossed.\n");
for (lst = f->loops.last; lst != f->loops.first; lst = lst2) {
lst2 = lst->prev;
BLI_mempool_free(bm->looplistpool, lst);
}
}
#endif
BM_CHECK_ELEMENT(bm, e);
BM_CHECK_ELEMENT(bm, f);
BM_CHECK_ELEMENT(bm, f2);
return f2;
}
/**
* bmesh_SEMV
*
* SPLIT EDGE MAKE VERT:
* Takes a given edge and splits it into two, creating a new vert.
*
*
* Before: OV---------TV
* After: OV----NV---TV
*
* Returns -
* BMVert pointer.
*
*/
BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re)
{
BMLoop *nextl;
BMEdge *ne;
BMVert *nv, *ov;
int i, edok, valence1 = 0, valence2 = 0;
if (bmesh_vert_in_edge(e, tv) == 0) {
return NULL;
}
ov = bmesh_edge_getothervert(e, tv);
/* count valence of v1 */
valence1 = bmesh_disk_count(ov);
/* count valence of v2 */
valence2 = bmesh_disk_count(tv);
nv = BM_vert_create(bm, tv->co, tv);
ne = BM_edge_create(bm, nv, tv, e, FALSE);
bmesh_disk_remove_edge(ne, tv);
bmesh_disk_remove_edge(ne, nv);
/* remove e from v2's disk cycle */
bmesh_disk_remove_edge(e, tv);
/* swap out tv for nv in e */
bmesh_edge_swapverts(e, tv, nv);
/* add e to nv's disk cycl */
bmesh_disk_append_edge(e, nv);
/* add ne to nv's disk cycl */
bmesh_disk_append_edge(ne, nv);
/* add ne to tv's disk cycl */
bmesh_disk_append_edge(ne, tv);
/* verify disk cycle */
edok = bmesh_disk_validate(valence1, ov->e, ov);
if (!edok) bmesh_error();
edok = bmesh_disk_validate(valence2, tv->e, tv);
if (!edok) bmesh_error();
edok = bmesh_disk_validate(2, nv->e, nv);
if (!edok) bmesh_error();
/* Split the radial cycle if presen */
nextl = e->l;
e->l = NULL;
if (nextl) {
BMLoop *nl, *l;
int radlen = bmesh_radial_length(nextl);
int first1 = 0, first2 = 0;
/* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */
while (nextl) {
l = nextl;
l->f->len++;
nextl = nextl != nextl->radial_next ? nextl->radial_next : NULL;
bmesh_radial_remove_loop(l, NULL);
nl = bmesh_create_loop(bm, NULL, NULL, l->f, l);
nl->prev = l;
nl->next = (l->next);
nl->prev->next = nl;
nl->next->prev = nl;
nl->v = nv;
/* assign the correct edge to the correct loo */
if (bmesh_verts_in_edge(nl->v, nl->next->v, e)) {
nl->e = e;
l->e = ne;
/* append l into ne's rad cycl */
if (!first1) {
first1 = 1;
l->radial_next = l->radial_prev = NULL;
}
if (!first2) {
first2 = 1;
l->radial_next = l->radial_prev = NULL;
}
bmesh_radial_append(nl->e, nl);
bmesh_radial_append(l->e, l);
}
else if (bmesh_verts_in_edge(nl->v, nl->next->v, ne)) {
nl->e = ne;
l->e = e;
/* append l into ne's rad cycl */
if (!first1) {
first1 = 1;
l->radial_next = l->radial_prev = NULL;
}
if (!first2) {
first2 = 1;
l->radial_next = l->radial_prev = NULL;
}
bmesh_radial_append(nl->e, nl);
bmesh_radial_append(l->e, l);
}
}
/* verify length of radial cycl */
edok = bmesh_radial_validate(radlen, e->l);
if (!edok) bmesh_error();
edok = bmesh_radial_validate(radlen, ne->l);
if (!edok) bmesh_error();
/* verify loop->v and loop->next->v pointers for */
for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) {
if (!(l->e == e)) bmesh_error();
//if (!(l->radial_next == l)) bmesh_error();
if (l->prev->e != ne && l->next->e != ne) {
bmesh_error();
}
edok = bmesh_verts_in_edge(l->v, l->next->v, e);
if (!edok) bmesh_error();
if (l->v == l->next->v) bmesh_error();
if (l->e == l->next->e) bmesh_error();
/* verify loop cycle for kloop-> */
BM_CHECK_ELEMENT(bm, l);
BM_CHECK_ELEMENT(bm, l->v);
BM_CHECK_ELEMENT(bm, l->e);
BM_CHECK_ELEMENT(bm, l->f);
}
/* verify loop->v and loop->next->v pointers for n */
for (i = 0, l = ne->l; i < radlen; i++, l = l->radial_next) {
if (!(l->e == ne)) bmesh_error();
//if (!(l->radial_next == l)) bmesh_error();
if (l->prev->e != e && l->next->e != e) bmesh_error();
edok = bmesh_verts_in_edge(l->v, l->next->v, ne);
if (!edok) bmesh_error();
if (l->v == l->next->v) bmesh_error();
if (l->e == l->next->e) bmesh_error();
BM_CHECK_ELEMENT(bm, l);
BM_CHECK_ELEMENT(bm, l->v);
BM_CHECK_ELEMENT(bm, l->e);
BM_CHECK_ELEMENT(bm, l->f);
}
}
BM_CHECK_ELEMENT(bm, ne);
BM_CHECK_ELEMENT(bm, nv);
BM_CHECK_ELEMENT(bm, ov);
BM_CHECK_ELEMENT(bm, e);
BM_CHECK_ELEMENT(bm, tv);
if (re) *re = ne;
return nv;
}
/**
* bmesh_JEKV
*
* JOIN EDGE KILL VERT:
* Takes a an edge and pointer to one of its vertices and collapses
* the edge on that vertex.
*
* Before: OE KE
* ------- -------
* | || |
* OV KV TV
*
*
* After: OE
* ---------------
* | |
* OV TV
*
*
* Restrictions:
* KV is a vertex that must have a valance of exactly two. Furthermore
* both edges in KV's disk cycle (OE and KE) must be unique (no double
* edges).
*
* It should also be noted that this euler has the possibility of creating
* faces with just 2 edges. It is up to the caller to decide what to do with
* these faces.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv)
{
BMEdge *oe;
BMVert *ov, *tv;
BMLoop *killoop, *l;
int len, radlen = 0, halt = 0, i, valence1, valence2, edok;
if (bmesh_vert_in_edge(ke, kv) == 0) {
return FALSE;
}
len = bmesh_disk_count(kv);
if (len == 2) {
oe = bmesh_disk_nextedge(ke, kv);
tv = bmesh_edge_getothervert(ke, kv);
ov = bmesh_edge_getothervert(oe, kv);
halt = bmesh_verts_in_edge(kv, tv, oe); /* check for double edge */
if (halt) {
return FALSE;
}
else {
/* For verification later, count valence of ov and t */
valence1 = bmesh_disk_count(ov);
valence2 = bmesh_disk_count(tv);
/* remove oe from kv's disk cycl */
bmesh_disk_remove_edge(oe, kv);
/* relink oe->kv to be oe->t */
bmesh_edge_swapverts(oe, kv, tv);
/* append oe to tv's disk cycl */
bmesh_disk_append_edge(oe, tv);
/* remove ke from tv's disk cycl */
bmesh_disk_remove_edge(ke, tv);
/* deal with radial cycle of k */
radlen = bmesh_radial_length(ke->l);
if (ke->l) {
/* first step, fix the neighboring loops of all loops in ke's radial cycl */
for (i = 0, killoop = ke->l; i < radlen; i++, killoop = bmesh_radial_nextloop(killoop)) {
/* relink loops and fix vertex pointer */
if (killoop->next->v == kv) {
killoop->next->v = tv;
}
killoop->next->prev = killoop->prev;
killoop->prev->next = killoop->next;
if (BM_FACE_FIRST_LOOP(killoop->f) == killoop) {
BM_FACE_FIRST_LOOP(killoop->f) = killoop->next;
}
killoop->next = NULL;
killoop->prev = NULL;
/* fix len attribute of fac */
killoop->f->len--;
}
/* second step, remove all the hanging loops attached to k */
radlen = bmesh_radial_length(ke->l);
if (LIKELY(radlen)) {
BMLoop **loops = NULL;
BLI_array_fixedstack_declare(loops, BM_NGON_STACK_SIZE, radlen, __func__);
killoop = ke->l;
/* this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well.. */
for (i = 0; i < radlen; i++) {
loops[i] = killoop;
killoop = bmesh_radial_nextloop(killoop);
}
for (i = 0; i < radlen; i++) {
bm->totloop--;
BLI_mempool_free(bm->lpool, loops[i]);
}
BLI_array_fixedstack_free(loops);
}
/* Validate radial cycle of o */
edok = bmesh_radial_validate(radlen, oe->l);
if (!edok) {
bmesh_error();
}
}
/* deallocate edg */
bmesh_kill_only_edge(bm, ke);
/* deallocate verte */
bmesh_kill_only_vert(bm, kv);
/* Validate disk cycle lengths of ov, tv are unchange */
edok = bmesh_disk_validate(valence1, ov->e, ov);
if (!edok) bmesh_error();
edok = bmesh_disk_validate(valence2, tv->e, tv);
if (!edok) bmesh_error();
/* Validate loop cycle of all faces attached to o */
for (i = 0, l = oe->l; i < radlen; i++, l = bmesh_radial_nextloop(l)) {
if (l->e != oe) bmesh_error();
edok = bmesh_verts_in_edge(l->v, l->next->v, oe);
if (!edok) bmesh_error();
edok = bmesh_loop_validate(l->f);
if (!edok) bmesh_error();
BM_CHECK_ELEMENT(bm, l);
BM_CHECK_ELEMENT(bm, l->v);
BM_CHECK_ELEMENT(bm, l->e);
BM_CHECK_ELEMENT(bm, l->f);
}
BM_CHECK_ELEMENT(bm, ov);
BM_CHECK_ELEMENT(bm, tv);
BM_CHECK_ELEMENT(bm, oe);
return TRUE;
}
}
return FALSE;
}
/**
* bmesh_JFKE
*
* JOIN FACE KILL EDGE:
*
* Takes two faces joined by a single 2-manifold edge and fuses them togather.
* The edge shared by the faces must not be connected to any other edges which have
* Both faces in its radial cycle
*
* Examples:
*
* A B
* ---------- ----------
* | | | |
* | f1 | | f1 |
* v1========v2 = Ok! v1==V2==v3 == Wrong!
* | f2 | | f2 |
* | | | |
* ---------- ----------
*
* In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used.
* In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller
* in this case should call bmesh_JEKV on the extra edges before attempting to fuse f1 and f2.
*
* Also note that the order of arguments decides whether or not certain per-face attributes are present
* in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited
* from f1, not f2.
*
* Returns -
* A BMFace pointer
*/
BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
{
BMLoop *l_iter, *f1loop = NULL, *f2loop = NULL;
int newlen = 0, i, f1len = 0, f2len = 0, radlen = 0, edok, shared;
BMIter iter;
/* can't join a face to itsel */
if (f1 == f2) {
return NULL;
}
/* verify that e is in both f1 and f2 */
f1len = f1->len;
f2len = f2->len;
BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f1) {
if (l_iter->e == e) {
f1loop = l_iter;
break;
}
}
BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f2) {
if (l_iter->e == e) {
f2loop = l_iter;
break;
}
}
if (!(f1loop && f2loop)) {
return NULL;
}
/* validate that edge is 2-manifold edg */
radlen = bmesh_radial_length(f1loop);
if (radlen != 2) {
return NULL;
}
/* validate direction of f2's loop cycle is compatible */
if (f1loop->v == f2loop->v) {
return NULL;
}
/* validate that for each face, each vertex has another edge in its disk cycle that is
* not e, and not shared. */
if ( bmesh_radial_find_face(f1loop->next->e, f2) ||
bmesh_radial_find_face(f1loop->prev->e, f2) ||
bmesh_radial_find_face(f2loop->next->e, f1) ||
bmesh_radial_find_face(f2loop->prev->e, f1) )
{
return NULL;
}
/* validate only one shared edg */
shared = BM_face_share_edges(f1, f2);
if (shared > 1) {
return NULL;
}
/* validate no internal join */
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG);
}
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG);
}
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
if (l_iter != f1loop) {
BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
}
}
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
if (l_iter != f2loop) {
/* as soon as a duplicate is found, bail out */
if (BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) {
return NULL;
}
}
}
/* join the two loop */
f1loop->prev->next = f2loop->next;
f2loop->next->prev = f1loop->prev;
f1loop->next->prev = f2loop->prev;
f2loop->prev->next = f1loop->next;
/* if f1loop was baseloop, make f1loop->next the base. */
if (BM_FACE_FIRST_LOOP(f1) == f1loop)
BM_FACE_FIRST_LOOP(f1) = f1loop->next;
/* increase length of f1 */
f1->len += (f2->len - 2);
/* make sure each loop points to the proper fac */
newlen = f1->len;
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < newlen; i++, l_iter = l_iter->next)
l_iter->f = f1;
/* remove edge from the disk cycle of its two vertices */
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1);
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2);
/* deallocate edge and its two loops as well as f2 */
BLI_mempool_free(bm->toolflagpool, f1loop->e->oflags);
BLI_mempool_free(bm->epool, f1loop->e);
bm->totedge--;
BLI_mempool_free(bm->lpool, f1loop);
bm->totloop--;
BLI_mempool_free(bm->lpool, f2loop);
bm->totloop--;
BLI_mempool_free(bm->toolflagpool, f2->oflags);
BLI_mempool_free(bm->fpool, f2);
bm->totface--;
/* account for both above */
bm->elem_index_dirty |= BM_EDGE | BM_FACE;
BM_CHECK_ELEMENT(bm, f1);
/* validate the new loop cycle */
edok = bmesh_loop_validate(f1);
if (!edok) bmesh_error();
return f1;
}
/*
* BMESH SPLICE VERT
*
* merges two verts into one (v into vtarget).
*/
static int bmesh_splicevert(BMesh *bm, BMVert *v, BMVert *vtarget)
{
BMEdge *e;
BMLoop *l;
BMIter liter;
/* verts already spliced */
if (v == vtarget) {
return FALSE;
}
/* retarget all the loops of v to vtarget */
BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
l->v = vtarget;
}
/* move all the edges from v's disk to vtarget's disk */
e = v->e;
while (e != NULL) {
bmesh_disk_remove_edge(e, v);
bmesh_edge_swapverts(e, v, vtarget);
bmesh_disk_append_edge(e, vtarget);
e = v->e;
}
BM_CHECK_ELEMENT(bm, v);
BM_CHECK_ELEMENT(bm, vtarget);
/* v is unused now, and can be killed */
BM_vert_kill(bm, v);
return TRUE;
}
/* BMESH CUT VERT
*
* cut all disjoint fans that meet at a vertex, making a unique
* vertex for each region. returns an array of all resulting
* vertices.
*/
static int bmesh_cutvert(BMesh *bm, BMVert *v, BMVert ***vout, int *len)
{
BMEdge **stack = NULL;
BLI_array_declare(stack);
BMVert **verts = NULL;
GHash *visithash;
BMIter eiter, liter;
BMLoop *l;
BMEdge *e;
int i, maxindex;
BMLoop *nl;
visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh_cutvert visithash");
maxindex = 0;
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
if (BLI_ghash_haskey(visithash, e)) {
continue;
}
/* Prime the stack with this unvisited edge */
BLI_array_append(stack, e);
/* Considering only edges and faces incident on vertex v, walk
* the edges & faces and assign an index to each connected set */
while ((e = BLI_array_pop(stack))) {
BLI_ghash_insert(visithash, e, SET_INT_IN_POINTER(maxindex));
BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
nl = (l->v == v) ? l->prev : l->next;
if (!BLI_ghash_haskey(visithash, nl->e)) {
BLI_array_append(stack, nl->e);
}
}
}
maxindex++;
}
/* Make enough verts to split v for each group */
verts = MEM_callocN(sizeof(BMVert *) * maxindex, "bmesh_cutvert");
verts[0] = v;
for (i = 1; i < maxindex; i++) {
verts[i] = BM_vert_create(bm, v->co, v);
}
/* Replace v with the new verts in each group */
BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, l->e));
if (i == 0) {
continue;
}
/* Loops here should alway refer to an edge that has v as an
* endpoint. For each appearance of this vert in a face, there
* will actually be two iterations: one for the loop heading
* towards vertex v, and another for the loop heading out from
* vertex v. Only need to swap the vertex on one of those times,
* on the outgoing loop. */
if (l->v == v) {
l->v = verts[i];
}
}
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, e));
if (i == 0) {
continue;
}
BLI_assert(e->v1 == v || e->v2 == v);
bmesh_disk_remove_edge(e, v);
bmesh_edge_swapverts(e, v, verts[i]);
bmesh_disk_append_edge(e, verts[i]);
}
BLI_ghash_free(visithash, NULL, NULL);
BLI_array_free(stack);
for (i = 0; i < maxindex; i++) {
BM_CHECK_ELEMENT(bm, verts[i]);
}
if (len != NULL) {
*len = maxindex;
}
if (vout != NULL) {
*vout = verts;
}
else {
MEM_freeN(verts);
}
return TRUE;
}
/* BMESH SPLICE EDGE
*
* splice two unique edges which share the same two vertices into one edge.
*
* edges must already have the same vertices
*/
static int UNUSED_FUNCTION(bmesh_spliceedge)(BMesh *bm, BMEdge *e, BMEdge *etarget)
{
BMLoop *l;
if (!BM_vert_in_edge(e, etarget->v1) || !BM_vert_in_edge(e, etarget->v2)) {
/* not the same vertices can't splice */
return FALSE;
}
while (e->l) {
l = e->l;
BLI_assert(BM_vert_in_edge(etarget, l->v));
BLI_assert(BM_vert_in_edge(etarget, l->next->v));
bmesh_radial_remove_loop(l, e);
bmesh_radial_append(etarget, l);
}
BLI_assert(bmesh_radial_length(e->l) == 0);
BM_CHECK_ELEMENT(bm, e);
BM_CHECK_ELEMENT(bm, etarget);
BM_edge_kill(bm, e);
return TRUE;
}
/*
* BMESH CUT EDGE
*
* Cuts a single edge into two edge: the original edge and
* a new edge that has only "cutl" in its radial.
*
* Does nothing if cutl is already the only loop in the
* edge radial.
*/
static int bmesh_cutedge(BMesh *bm, BMEdge *e, BMLoop *cutl)
{
BMEdge *ne;
int radlen;
BLI_assert(cutl->e == e);
BLI_assert(e->l);
radlen = bmesh_radial_length(e->l);
if (radlen < 2) {
/* no cut required */
return TRUE;
}
if (cutl == e->l) {
e->l = cutl->radial_next;
}
ne = BM_edge_create(bm, e->v1, e->v2, e, FALSE);
bmesh_radial_remove_loop(cutl, e);
bmesh_radial_append(ne, cutl);
cutl->e = ne;
BLI_assert(bmesh_radial_length(e->l) == radlen - 1);
BLI_assert(bmesh_radial_length(ne->l) == 1);
BM_CHECK_ELEMENT(bm, ne);
BM_CHECK_ELEMENT(bm, e);
return TRUE;
}
/*
* BMESH UNGLUE REGION MAKE VERT
*
* Disconnects a face from its vertex fan at loop sl.
*/
static BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *sl)
{
BMVert **vtar;
int len, i;
BMVert *nv = NULL;
BMVert *sv = sl->v;
/* peel the face from the edge radials on both sides of the
* loop vert, disconnecting the face from its fan */
bmesh_cutedge(bm, sl->e, sl);
bmesh_cutedge(bm, sl->prev->e, sl->prev);
if (bmesh_disk_count(sv) == 2) {
/* If there are still only two edges out of sv, then
* this whole URMV was just a no-op, so exit now. */
return sv;
}
/* Update the disk start, so that v->e points to an edge
* not touching the split loop. This is so that bmesh_cutvert
* will leave the original sv on some *other* fan (not the
* one-face fan that holds the unglue face). */
while (sv->e == sl->e || sv->e == sl->prev->e) {
sv->e = bmesh_disk_nextedge(sv->e, sv);
}
/* Split all fans connected to the vert, duplicating it for
* each fans. */
bmesh_cutvert(bm, sv, &vtar, &len);
/* There should have been at least two fans cut apart here,
* otherwise the early exit would have kicked in. */
BLI_assert(len >= 2);
nv = sl->v;
/* Desired result here is that a new vert should always be
* created for the unglue face. This is so we can glue any
* extras back into the original vert. */
BLI_assert(nv != sv);
BLI_assert(sv == vtar[0]);
/* If there are more than two verts as a result, glue together
* all the verts except the one this URMV intended to create */
if (len > 2) {
for (i = 0; i < len; i++) {
if (vtar[i] == nv) {
break;
}
}
if (i != len) {
/* Swap the single vert that was needed for the
* unglue into the last array slot */
SWAP(BMVert *, vtar[i], vtar[len - 1]);
/* And then glue the rest back together */
for (i = 1; i < len - 1; i++) {
bmesh_splicevert(bm, vtar[i], vtar[0]);
}
}
}
MEM_freeN(vtar);
return nv;
}
/*
* BMESH UNGLUE REGION MAKE VERT
*
* Disconnects sf from the vertex fan at sv
*/
BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv)
{
BMLoop *l_first;
BMLoop *l_iter;
l_iter = l_first = BM_FACE_FIRST_LOOP(sf);
do {
if (l_iter->v == sv) {
break;
}
} while ((l_iter = l_iter->next) != l_first);
if (l_iter->v != sv) {
/* sv is not part of sf */
return NULL;
}
return bmesh_urmv_loop(bm, l_iter);
}

View File

@@ -0,0 +1,1145 @@
/*
* ***** 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_opdefines.c
* \ingroup bmesh
*
* BMesh operator definitions.
*
* This file defines (and documents) all bmesh operators (bmops).
*
* Do not rename any operator or slot names! otherwise you must go
* through the code and find all references to them!
*
* A word on slot names:
*
* For geometry input slots, the following are valid names:
* - verts
* - edges
* - faces
* - edgefacein
* - vertfacein
* - vertedgein
* - vertfacein
* - geom
*
* The basic rules are, for single-type geometry slots, use the plural of the
* type name (e.g. edges). for double-type slots, use the two type names plus
* "in" (e.g. edgefacein). for three-type slots, use geom.
*
* for output slots, for single-type geometry slots, use the type name plus "out",
* (e.g. vertout), for double-type slots, use the two type names plus "out",
* (e.g. vertfaceout), for three-type slots, use geom. note that you can also
* use more esohteric names (e.g. skirtout) so long as the comment next to the
* slot definition tells you what types of elements are in it.
*
*/
#include "bmesh.h"
#include "bmesh_private.h"
/* ok, I'm going to write a little docgen script. so all
* bmop comments must conform to the following template/rules:
*
* template (py quotes used because nested comments don't work
* on all C compilers):
*
* """
* Region Extend.
*
* paragraph1, Extends bleh bleh bleh.
* Bleh Bleh bleh.
*
* Another paragraph.
*
* Another paragraph.
* """
*
* so the first line is the "title" of the bmop.
* subsequent line blocks seperated by blank lines
* are paragraphs. individual descriptions of slots
* would be extracted from comments
* next to them, e.g.
*
* {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, boundary region
*
* the doc generator would automatically detect the presence of "output slot"
* and flag the slot as an output. the same happens for "input slot". also
* note that "edges", "faces", "verts", "loops", and "geometry" are valid
* substitutions for "slot".
*
* note that slots default to being input slots.
*/
/*
* Vertex Smooth
*
* Smoothes vertices by using a basic vertex averaging scheme.
*/
static BMOpDefine def_vertexsmooth = {
"vertexsmooth",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
{BMO_OP_SLOT_INT, "mirror_clip_x"}, //set vertices close to the x axis before the operation to 0
{BMO_OP_SLOT_INT, "mirror_clip_y"}, //set vertices close to the y axis before the operation to 0
{BMO_OP_SLOT_INT, "mirror_clip_z"}, //set vertices close to the z axis before the operation to 0
{BMO_OP_SLOT_FLT, "clipdist"}, //clipping threshod for the above three slots
{0} /* null-terminating sentine */,
},
bmesh_vertexsmooth_exec,
0
};
/*
* Right-Hand Faces
*
* Computes an "outside" normal for the specified input faces.
*/
static BMOpDefine def_righthandfaces = {
"righthandfaces",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"},
{BMO_OP_SLOT_INT, "doflip"}, //internal flag, used by bmesh_rationalize_normals
{0} /* null-terminating sentine */,
},
bmesh_righthandfaces_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Region Extend
*
* used to implement the select more/less tools.
* this puts some geometry surrounding regions of
* geometry in geom into geomout.
*
* if usefaces is 0 then geomout spits out verts and edges,
* otherwise it spits out faces.
*/
static BMOpDefine def_regionextend = {
"regionextend",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, computed boundary geometry.
{BMO_OP_SLOT_INT, "constrict"}, //find boundary inside the regions, not outside.
{BMO_OP_SLOT_INT, "usefaces"}, //extend from faces instead of edges
{0} /* null-terminating sentine */,
},
bmesh_regionextend_exec,
0
};
/*
* Edge Rotate
*
* Rotates edges topologically. Also known as "spin edge" to some people.
* Simple example: [/] becomes [|] then [\].
*/
static BMOpDefine def_edgerotate = {
"edgerotate",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //newly spun edges
{BMO_OP_SLOT_INT, "ccw"}, //rotate edge counter-clockwise if true, othewise clockwise
{0} /* null-terminating sentine */,
},
bmesh_edgerotate_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
/*
* Reverse Faces
*
* Reverses the winding (vertex order) of faces. This has the effect of
* flipping the normal.
*/
static BMOpDefine def_reversefaces = {
"reversefaces",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces
{0} /* null-terminating sentine */,
},
bmesh_reversefaces_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Edge Bisect
*
* Splits input edges (but doesn't do anything else).
* This creates a 2-valence vert.
*/
static BMOpDefine def_edgebisect = {
"edgebisect",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges
{BMO_OP_SLOT_INT, "numcuts"}, //number of cuts
{BMO_OP_SLOT_ELEMENT_BUF, "outsplit"}, //newly created vertices and edges
{0} /* null-terminating sentine */,
},
esplit_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
/*
* Mirror
*
* Mirrors geometry along an axis. The resulting geometry is welded on using
* mergedist. Pairs of original/mirrored vertices are welded using the mergedist
* parameter (which defines the minimum distance for welding to happen).
*/
static BMOpDefine def_mirror = {
"mirror",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry
{BMO_OP_SLOT_MAT, "mat"}, //matrix defining the mirror transformation
{BMO_OP_SLOT_FLT, "mergedist"}, //maximum distance for merging. does no merging if 0.
{BMO_OP_SLOT_ELEMENT_BUF, "newout"}, //output geometry, mirrored
{BMO_OP_SLOT_INT, "axis"}, //the axis to use, 0, 1, or 2 for x, y, z
{BMO_OP_SLOT_INT, "mirror_u"}, //mirror UVs across the u axis
{BMO_OP_SLOT_INT, "mirror_v"}, //mirror UVs across the v axis
{0, /* null-terminating sentine */}},
bmesh_mirror_exec,
0,
};
/*
* Find Doubles
*
* Takes input verts and find vertices they should weld to. Outputs a
* mapping slot suitable for use with the weld verts bmop.
*/
static BMOpDefine def_finddoubles = {
"finddoubles",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
{BMO_OP_SLOT_ELEMENT_BUF, "keepverts"}, //list of verts to keep
{BMO_OP_SLOT_FLT, "dist"}, //minimum distance
{BMO_OP_SLOT_MAPPING, "targetmapout"},
{0, /* null-terminating sentine */}},
bmesh_finddoubles_exec,
0,
};
/*
* Remove Doubles
*
* Finds groups of vertices closer then dist and merges them together,
* using the weld verts bmop.
*/
static BMOpDefine def_removedoubles = {
"removedoubles",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts
{BMO_OP_SLOT_FLT, "dist"}, //minimum distance
{0, /* null-terminating sentine */}},
bmesh_removedoubles_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Auto Merge
*
* Finds groups of vertices closer then dist and merges them together,
* using the weld verts bmop. The merges must go from a vert not in
* verts to one in verts.
*/
static BMOpDefine def_automerge = {
"automerge",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts
{BMO_OP_SLOT_FLT, "dist"}, //minimum distance
{0, /* null-terminating sentine */}},
bmesh_automerge_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Collapse Connected
*
* Collapses connected vertices
*/
static BMOpDefine def_collapse = {
"collapse",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
{0, /* null-terminating sentine */}},
bmesh_collapse_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Facedata point Merge
*
* Merge uv/vcols at a specific vertex.
*/
static BMOpDefine def_pointmerge_facedata = {
"pointmerge_facedata",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */
{BMO_OP_SLOT_ELEMENT_BUF, "snapv"}, /* snap verte */
{0, /* null-terminating sentine */}},
bmesh_pointmerge_facedata_exec,
0,
};
/*
* Average Vertices Facevert Data
*
* Merge uv/vcols associated with the input vertices at
* the bounding box center. (I know, it's not averaging but
* the vert_snap_to_bb_center is just too long).
*/
static BMOpDefine def_vert_average_facedata = {
"vert_average_facedata",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */
{0, /* null-terminating sentine */}},
bmesh_vert_average_facedata_exec,
0,
};
/*
* Point Merge
*
* Merge verts together at a point.
*/
static BMOpDefine def_pointmerge = {
"pointmerge",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */
{BMO_OP_SLOT_VEC, "mergeco"},
{0, /* null-terminating sentine */}},
bmesh_pointmerge_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Collapse Connected UVs
*
* Collapses connected UV vertices.
*/
static BMOpDefine def_collapse_uvs = {
"collapse_uvs",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
{0, /* null-terminating sentine */}},
bmesh_collapsecon_exec,
0,
};
/*
* Weld Verts
*
* Welds verts together (kindof like remove doubles, merge, etc, all of which
* use or will use this bmop). You pass in mappings from vertices to the vertices
* they weld with.
*/
static BMOpDefine def_weldverts = {
"weldverts",
{{BMO_OP_SLOT_MAPPING, "targetmap"}, /* maps welded vertices to verts they should weld to */
{0, /* null-terminating sentine */}},
bmesh_weldverts_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Make Vertex
*
* Creates a single vertex; this bmop was necassary
* for click-create-vertex.
*/
static BMOpDefine def_makevert = {
"makevert",
{{BMO_OP_SLOT_VEC, "co"}, //the coordinate of the new vert
{BMO_OP_SLOT_ELEMENT_BUF, "newvertout"}, //the new vert
{0, /* null-terminating sentine */}},
bmesh_makevert_exec,
0,
};
/*
* Join Triangles
*
* Tries to intelligently join triangles according
* to various settings and stuff.
*/
static BMOpDefine def_join_triangles = {
"join_triangles",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input geometry.
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //joined faces
{BMO_OP_SLOT_INT, "compare_sharp"},
{BMO_OP_SLOT_INT, "compare_uvs"},
{BMO_OP_SLOT_INT, "compare_vcols"},
{BMO_OP_SLOT_INT, "compare_materials"},
{BMO_OP_SLOT_FLT, "limit"},
{0, /* null-terminating sentine */}},
bmesh_jointriangles_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Contextual Create
*
* This is basically fkey, it creates
* new faces from vertices, makes stuff from edge nets,
* makes wire edges, etc. It also dissolves
* faces.
*
* Three verts become a triangle, four become a quad. Two
* become a wire edge.
*/
static BMOpDefine def_contextual_create = {
"contextual_create",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry.
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //newly-made face(s)
{0, /* null-terminating sentine */}},
bmesh_contextual_create_exec,
BMO_OP_FLAG_UNTAN_MULTIRES,
};
/*
* Bridge edge loops with faces
*/
static BMOpDefine def_bridge_loops = {
"bridge_loops",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */
{0, /* null-terminating sentine */}},
bmesh_bridge_loops_exec,
0,
};
static BMOpDefine def_edgenet_fill = {
"edgenet_fill",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
{BMO_OP_SLOT_MAPPING, "restrict"}, /* restricts edges to groups. maps edges to integer */
{BMO_OP_SLOT_INT, "use_restrict"},
{BMO_OP_SLOT_ELEMENT_BUF, "excludefaces"}, /* list of faces to ignore for manifold check */
{BMO_OP_SLOT_MAPPING, "faceout_groupmap"}, /* maps new faces to the group numbers they came fro */
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */
{0, /* null-terminating sentine */}},
bmesh_edgenet_fill_exec,
0,
};
/*
* Edgenet Prepare
*
* Identifies several useful edge loop cases and modifies them so
* they'll become a face when edgenet_fill is called. The cases covered are:
*
* - One single loop; an edge is added to connect the ends
* - Two loops; two edges are added to connect the endpoints (based on the
* shortest distance between each endpont).
*/
static BMOpDefine def_edgenet_prepare = {
"edgenet_prepare",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //new edges
{0, /* null-terminating sentine */}},
bmesh_edgenet_prepare,
0,
};
/*
* Rotate
*
* Rotate vertices around a center, using a 3x3 rotation
* matrix. Equivilent of the old rotateflag function.
*/
static BMOpDefine def_rotate = {
"rotate",
{{BMO_OP_SLOT_VEC, "cent"}, //center of rotation
{BMO_OP_SLOT_MAT, "mat"}, //matrix defining rotation
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
{0, /* null-terminating sentine */}},
bmesh_rotate_exec,
0,
};
/*
* Translate
*
* Translate vertices by an offset. Equivelent of the
* old translateflag function.
*/
static BMOpDefine def_translate = {
"translate",
{{BMO_OP_SLOT_VEC, "vec"}, //translation offset
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
{0, /* null-terminating sentine */}},
bmesh_translate_exec,
0,
};
/*
* Scale
*
* Scales vertices by an offset.
*/
static BMOpDefine def_scale = {
"scale",
{{BMO_OP_SLOT_VEC, "vec"}, //scale factor
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
{0, /* null-terminating sentine */}},
bmesh_scale_exec,
0,
};
/*
* Transform
*
* Transforms a set of vertices by a matrix. Multiplies
* the vertex coordinates with the matrix.
*/
static BMOpDefine def_transform = {
"transform",
{{BMO_OP_SLOT_MAT, "mat"}, //transform matrix
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
{0, /* null-terminating sentine */}},
bmesh_transform_exec,
0,
};
/*
* Object Load BMesh
*
* Loads a bmesh into an object/mesh. This is a "private"
* bmop.
*/
static BMOpDefine def_object_load_bmesh = {
"object_load_bmesh",
{{BMO_OP_SLOT_PNT, "scene"},
{BMO_OP_SLOT_PNT, "object"},
{0, /* null-terminating sentine */}},
object_load_bmesh_exec,
0,
};
/*
* BMesh to Mesh
*
* Converts a bmesh to a Mesh. This is reserved for exiting editmode.
*/
static BMOpDefine def_bmesh_to_mesh = {
"bmesh_to_mesh",
{{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a mesh structure to fill in
{BMO_OP_SLOT_PNT, "object"}, //pointer to an object structure
{BMO_OP_SLOT_INT, "notesselation"}, //don't calculate mfaces
{0, /* null-terminating sentine */}},
bmesh_to_mesh_exec,
0,
};
/*
* Mesh to BMesh
*
* Load the contents of a mesh into the bmesh. this bmop is private, it's
* reserved exclusively for entering editmode.
*/
static BMOpDefine def_mesh_to_bmesh = {
"mesh_to_bmesh",
{{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a Mesh structure
{BMO_OP_SLOT_PNT, "object"}, //pointer to an Object structure
{BMO_OP_SLOT_INT, "set_shapekey"}, //load active shapekey coordinates into verts
{0, /* null-terminating sentine */}},
mesh_to_bmesh_exec,
0
};
/*
* Individual Face Extrude
*
* Extrudes faces individually.
*/
static BMOpDefine def_extrude_indivface = {
"extrude_face_indiv",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //output faces
{BMO_OP_SLOT_ELEMENT_BUF, "skirtout"}, //output skirt geometry, faces and edges
{0} /* null-terminating sentine */},
bmesh_extrude_face_indiv_exec,
0
};
/*
* Extrude Only Edges
*
* Extrudes Edges into faces, note that this is very simple, there's no fancy
* winged extrusion.
*/
static BMOpDefine def_extrude_onlyedge = {
"extrude_edge_only",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input vertices
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output geometry
{0} /* null-terminating sentine */},
bmesh_extrude_onlyedge_exec,
0
};
/*
* Individual Vertex Extrude
*
* Extrudes wire edges from vertices.
*/
static BMOpDefine def_extrudeverts_indiv = {
"extrude_vert_indiv",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //output wire edges
{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output vertices
{0} /* null-terminating sentine */},
extrude_vert_indiv_exec,
0
};
static BMOpDefine def_connectverts = {
"connectverts",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"},
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"},
{0} /* null-terminating sentine */},
connectverts_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_extrudefaceregion = {
"extrudefaceregion",
{{BMO_OP_SLOT_ELEMENT_BUF, "edgefacein"},
{BMO_OP_SLOT_MAPPING, "exclude"},
{BMO_OP_SLOT_INT, "alwayskeeporig"},
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"},
{0} /* null-terminating sentine */},
extrude_edge_context_exec,
0
};
static BMOpDefine def_dissolvevertsop = {
"dissolveverts",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"},
{0} /* null-terminating sentine */},
dissolveverts_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_dissolveedgessop = {
"dissolveedges",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
{BMO_OP_SLOT_ELEMENT_BUF, "regionout"},
{BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges.
{0} /* null-terminating sentine */},
dissolveedges_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_dissolveedgeloopsop = {
"dissolveedgeloop",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
{BMO_OP_SLOT_ELEMENT_BUF, "regionout"},
{0} /* null-terminating sentine */},
dissolve_edgeloop_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_dissolvefacesop = {
"dissolvefaces",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"},
{BMO_OP_SLOT_ELEMENT_BUF, "regionout"},
{BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges.
{0} /* null-terminating sentine */},
dissolvefaces_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_dissolvelimitop = {
"dissolvelimit",
{{BMO_OP_SLOT_FLT, "angle_limit"}, /* total rotation angle (degrees) */
{BMO_OP_SLOT_ELEMENT_BUF, "verts"},
{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
{0} /* null-terminating sentine */},
dissolvelimit_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_triangop = {
"triangulate",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"},
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"},
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"},
{BMO_OP_SLOT_MAPPING, "facemap"},
{0} /* null-terminating sentine */},
triangulate_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_subdop = {
"esubd",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
{BMO_OP_SLOT_INT, "numcuts"},
{BMO_OP_SLOT_FLT, "smooth"},
{BMO_OP_SLOT_FLT, "fractal"},
{BMO_OP_SLOT_INT, "beauty"},
{BMO_OP_SLOT_INT, "seed"},
{BMO_OP_SLOT_MAPPING, "custompatterns"},
{BMO_OP_SLOT_MAPPING, "edgepercents"},
/* these next three can have multiple types of elements in them */
{BMO_OP_SLOT_ELEMENT_BUF, "outinner"},
{BMO_OP_SLOT_ELEMENT_BUF, "outsplit"},
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* contains all output geometr */
{BMO_OP_SLOT_INT, "quadcornertype"}, //quad corner type, see bmesh_operators.h
{BMO_OP_SLOT_INT, "gridfill"}, //fill in fully-selected faces with a grid
{BMO_OP_SLOT_INT, "singleedge"}, //tesselate the case of one edge selected in a quad or triangle
{0} /* null-terminating sentine */,
},
esubdivide_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
static BMOpDefine def_delop = {
"del",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, {BMO_OP_SLOT_INT, "context"},
{0} /* null-terminating sentine */},
delop_exec,
0
};
static BMOpDefine def_dupeop = {
"dupe",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
{BMO_OP_SLOT_ELEMENT_BUF, "origout"},
{BMO_OP_SLOT_ELEMENT_BUF, "newout"},
/* facemap maps from source faces to dupe
* faces, and from dupe faces to source faces */
{BMO_OP_SLOT_MAPPING, "facemap"},
{BMO_OP_SLOT_MAPPING, "boundarymap"},
{BMO_OP_SLOT_MAPPING, "isovertmap"},
{BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */
{0} /* null-terminating sentine */},
dupeop_exec,
0
};
static BMOpDefine def_splitop = {
"split",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"},
{BMO_OP_SLOT_MAPPING, "boundarymap"},
{BMO_OP_SLOT_MAPPING, "isovertmap"},
{BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */
{0} /* null-terminating sentine */},
splitop_exec,
0
};
/*
* Spin
*
* Extrude or duplicate geometry a number of times,
* rotating and possibly translating after each step
*/
static BMOpDefine def_spinop = {
"spin",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
{BMO_OP_SLOT_ELEMENT_BUF, "lastout"}, /* result of last step */
{BMO_OP_SLOT_VEC, "cent"}, /* rotation center */
{BMO_OP_SLOT_VEC, "axis"}, /* rotation axis */
{BMO_OP_SLOT_VEC, "dvec"}, /* translation delta per step */
{BMO_OP_SLOT_FLT, "ang"}, /* total rotation angle (degrees) */
{BMO_OP_SLOT_INT, "steps"}, /* number of steps */
{BMO_OP_SLOT_INT, "dupli"}, /* duplicate or extrude? */
{0} /* null-terminating sentine */},
spinop_exec,
0
};
/*
* Similar faces search
*
* Find similar faces (area/material/perimeter, ...).
*/
static BMOpDefine def_similarfaces = {
"similarfaces",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* output faces */
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
{BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */
{0} /* null-terminating sentine */},
bmesh_similarfaces_exec,
0
};
/*
* Similar edges search
*
* Find similar edges (length, direction, edge, seam, ...).
*/
static BMOpDefine def_similaredges = {
"similaredges",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, /* output edges */
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
{BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */
{0} /* null-terminating sentine */},
bmesh_similaredges_exec,
0
};
/*
* Similar vertices search
*
* Find similar vertices (normal, face, vertex group, ...).
*/
static BMOpDefine def_similarverts = {
"similarverts",
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertices */
{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
{BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */
{0} /* null-terminating sentine */},
bmesh_similarverts_exec,
0
};
/*
* uv rotation
* cycle the uvs
*/
static BMOpDefine def_meshrotateuvs = {
"meshrotateuvs",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
{BMO_OP_SLOT_INT, "dir"}, /* direction */
{0} /* null-terminating sentine */},
bmesh_rotateuvs_exec,
0
};
/*
* uv reverse
* reverse the uvs
*/
static BMOpDefine def_meshreverseuvs = {
"meshreverseuvs",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
{0} /* null-terminating sentine */},
bmesh_reverseuvs_exec,
0
};
/*
* color rotation
* cycle the colors
*/
static BMOpDefine def_meshrotatecolors = {
"meshrotatecolors",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
{BMO_OP_SLOT_INT, "dir"}, /* direction */
{0} /* null-terminating sentine */},
bmesh_rotatecolors_exec,
0
};
/*
* color reverse
* reverse the colors
*/
static BMOpDefine def_meshreversecolors = {
"meshreversecolors",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
{0} /* null-terminating sentine */},
bmesh_reversecolors_exec,
0
};
/*
* Similar vertices search
*
* Find similar vertices (normal, face, vertex group, ...).
*/
static BMOpDefine def_vertexshortestpath = {
"vertexshortestpath",
{{BMO_OP_SLOT_ELEMENT_BUF, "startv"}, /* start vertex */
{BMO_OP_SLOT_ELEMENT_BUF, "endv"}, /* end vertex */
{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
{0} /* null-terminating sentine */},
bmesh_vertexshortestpath_exec,
0
};
/*
* Edge Split
*
* Disconnects faces along input edges.
*/
static BMOpDefine def_edgesplit = {
"edgesplit",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout1"}, /* old output disconnected edges */
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout2"}, /* new output disconnected edges */
{0} /* null-terminating sentine */},
bmesh_edgesplitop_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
/*
* Create Grid
*
* Creates a grid with a variable number of subdivisions
*/
static BMOpDefine def_create_grid = {
"create_grid",
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
{BMO_OP_SLOT_INT, "xsegments"}, //number of x segments
{BMO_OP_SLOT_INT, "ysegments"}, //number of y segments
{BMO_OP_SLOT_FLT, "size"}, //size of the grid
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with
{0, /* null-terminating sentine */}},
bmesh_create_grid_exec,
0,
};
/*
* Create UV Sphere
*
* Creates a grid with a variable number of subdivisions
*/
static BMOpDefine def_create_uvsphere = {
"create_uvsphere",
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
{BMO_OP_SLOT_INT, "segments"}, //number of u segments
{BMO_OP_SLOT_INT, "revolutions"}, //number of v segment
{BMO_OP_SLOT_FLT, "diameter"}, //diameter
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
{0, /* null-terminating sentine */}},
bmesh_create_uvsphere_exec,
0,
};
/*
* Create Ico Sphere
*
* Creates a grid with a variable number of subdivisions
*/
static BMOpDefine def_create_icosphere = {
"create_icosphere",
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
{BMO_OP_SLOT_INT, "subdivisions"}, //how many times to recursively subdivide the sphere
{BMO_OP_SLOT_FLT, "diameter"}, //diameter
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with
{0, /* null-terminating sentine */}},
bmesh_create_icosphere_exec,
0,
};
/*
* Create Suzanne
*
* Creates a monkey. Be wary.
*/
static BMOpDefine def_create_monkey = {
"create_monkey",
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
{0, /* null-terminating sentine */}},
bmesh_create_monkey_exec,
0,
};
/*
* Create Cone
*
* Creates a cone with variable depth at both ends
*/
static BMOpDefine def_create_cone = {
"create_cone",
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
{BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces
{BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons
{BMO_OP_SLOT_INT, "segments"},
{BMO_OP_SLOT_FLT, "diameter1"}, //diameter of one end
{BMO_OP_SLOT_FLT, "diameter2"}, //diameter of the opposite
{BMO_OP_SLOT_FLT, "depth"}, //distance between ends
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
{0, /* null-terminating sentine */}},
bmesh_create_cone_exec,
0,
};
/*
* Creates a circle
*/
static BMOpDefine def_create_circle = {
"create_circle",
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
{BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces
{BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons
{BMO_OP_SLOT_INT, "segments"},
{BMO_OP_SLOT_FLT, "diameter"}, //diameter of one end
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
{0, /* null-terminating sentine */}},
bmesh_create_circle_exec,
0,
};
/*
* Create Cone
*
* Creates a cone with variable depth at both ends
*/
static BMOpDefine def_create_cube = {
"create_cube",
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
{BMO_OP_SLOT_FLT, "size"}, //size of the cube
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
{0, /* null-terminating sentine */}},
bmesh_create_cube_exec,
0,
};
/*
* Bevel
*
* Bevels edges and vertices
*/
static BMOpDefine def_bevel = {
"bevel",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, /* input edges and vertices */
{BMO_OP_SLOT_ELEMENT_BUF, "face_spans"}, /* new geometry */
{BMO_OP_SLOT_ELEMENT_BUF, "face_holes"}, /* new geometry */
{BMO_OP_SLOT_INT, "use_lengths"}, /* grab edge lengths from a PROP_FLT customdata laye */
{BMO_OP_SLOT_INT, "use_even"}, /* corner vert placement: use shell/angle calculations */
{BMO_OP_SLOT_INT, "use_dist"}, /* corner vert placement: evaluate percent as a distance,
* modifier uses this. We could do this as another float setting */
{BMO_OP_SLOT_INT, "lengthlayer"}, /* which PROP_FLT layer to us */
{BMO_OP_SLOT_FLT, "percent"}, /* percentage to expand bevelled edge */
{0} /* null-terminating sentine */},
bmesh_bevel_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
/*
* Beautify Fill
*
* Makes triangle a bit nicer
*/
static BMOpDefine def_beautify_fill = {
"beautify_fill",
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
{BMO_OP_SLOT_ELEMENT_BUF, "constrain_edges"}, /* edges that can't be flipped */
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new flipped faces and edges */
{0} /* null-terminating sentine */},
bmesh_beautify_fill_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
/*
* Triangle Fill
*
* Fill edges with triangles
*/
static BMOpDefine def_triangle_fill = {
"triangle_fill",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new faces and edges */
{0} /* null-terminating sentine */},
bmesh_triangle_fill_exec,
BMO_OP_FLAG_UNTAN_MULTIRES
};
/*
* Solidify
*
* Turns a mesh into a shell with thickness
*/
static BMOpDefine def_solidify = {
"solidify",
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
{BMO_OP_SLOT_FLT, "thickness"},
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"},
{0}},
bmesh_solidify_face_region_exec,
0
};
BMOpDefine *opdefines[] = {
&def_splitop,
&def_spinop,
&def_dupeop,
&def_delop,
&def_subdop,
&def_triangop,
&def_dissolvefacesop,
&def_dissolveedgessop,
&def_dissolveedgeloopsop,
&def_dissolvevertsop,
&def_dissolvelimitop,
&def_extrudefaceregion,
&def_connectverts,
//&def_makeprim,
&def_extrudeverts_indiv,
&def_mesh_to_bmesh,
&def_object_load_bmesh,
&def_transform,
&def_translate,
&def_rotate,
&def_edgenet_fill,
&def_contextual_create,
&def_makevert,
&def_weldverts,
&def_removedoubles,
&def_finddoubles,
&def_mirror,
&def_edgebisect,
&def_reversefaces,
&def_edgerotate,
&def_regionextend,
&def_righthandfaces,
&def_vertexsmooth,
&def_extrude_onlyedge,
&def_extrude_indivface,
&def_collapse_uvs,
&def_pointmerge,
&def_collapse,
&def_similarfaces,
&def_similaredges,
&def_similarverts,
&def_pointmerge_facedata,
&def_vert_average_facedata,
&def_meshrotateuvs,
&def_bmesh_to_mesh,
&def_meshreverseuvs,
&def_edgenet_prepare,
&def_meshrotatecolors,
&def_meshreversecolors,
&def_vertexshortestpath,
&def_scale,
&def_edgesplit,
&def_automerge,
&def_create_uvsphere,
&def_create_grid,
&def_create_icosphere,
&def_create_monkey,
&def_create_cube,
&def_create_circle,
&def_create_cone,
&def_join_triangles,
&def_bevel,
&def_beautify_fill,
&def_triangle_fill,
&def_bridge_loops,
&def_solidify,
};
int bmesh_total_ops = (sizeof(opdefines) / sizeof(void *));

View File

@@ -0,0 +1,1376 @@
/*
* ***** 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_operators.c
* \ingroup bmesh
*
* BMesh operator access.
*/
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_string.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_mempool.h"
#include "BLI_listbase.h"
#include "BLI_array.h"
#include "bmesh.h"
#include "bmesh_private.h"
/* forward declarations */
static void bmo_flag_layer_alloc(BMesh *bm);
static void bmo_flag_layer_free(BMesh *bm);
static void bmo_flag_layer_clear(BMesh *bm);
static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name);
static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name);
static int bmesh_opname_to_opcode(const char *opname);
static const char *bmo_error_messages[] = {
NULL,
"Self intersection error",
"Could not dissolve vert",
"Could not connect vertices",
"Could not traverse mesh",
"Could not dissolve faces",
"Could not dissolve vertices",
"Tesselation error",
"Can not deal with non-manifold geometry",
"Invalid selection",
"Internal mesh error",
};
/* operator slot type information - size of one element of the type given. */
const int BMO_OPSLOT_TYPEINFO[] = {
0,
sizeof(int),
sizeof(float),
sizeof(void *),
0, /* unused */
0, /* unused */
0, /* unused */
sizeof(void *), /* pointer buffer */
sizeof(BMOElemMapping)
};
/* Dummy slot so there is something to return when slot name lookup fails */
static BMOpSlot BMOpEmptySlot = {0};
void BMO_op_flag_enable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag)
{
op->flag |= op_flag;
}
void BMO_op_flag_disable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag)
{
op->flag &= ~op_flag;
}
/*
* BMESH OPSTACK PUSH
*
* Pushes the opstack down one level
* and allocates a new flag layer if
* appropriate.
*/
void BMO_push(BMesh *bm, BMOperator *UNUSED(op))
{
bm->stackdepth++;
/* add flag layer, if appropriate */
if (bm->stackdepth > 1)
bmo_flag_layer_alloc(bm);
else
bmo_flag_layer_clear(bm);
}
/*
* BMESH OPSTACK POP
*
* Pops the opstack one level
* and frees a flag layer if appropriate
* BMESH_TODO: investigate NOT freeing flag
* layers.
*/
void BMO_pop(BMesh *bm)
{
if (bm->stackdepth > 1)
bmo_flag_layer_free(bm);
bm->stackdepth--;
}
/*
* BMESH OPSTACK INIT OP
*
* Initializes an operator structure
* to a certain type
*/
void BMO_op_init(BMesh *bm, BMOperator *op, const char *opname)
{
int i, opcode = bmesh_opname_to_opcode(opname);
#ifdef DEBUG
BM_ELEM_INDEX_VALIDATE(bm, "pre bmo", opname);
#else
(void)bm;
#endif
if (opcode == -1) {
opcode = 0; /* error!, already printed, have a better way to handle this? */
}
memset(op, 0, sizeof(BMOperator));
op->type = opcode;
op->flag = opdefines[opcode]->flag;
/* initialize the operator slot types */
for (i = 0; opdefines[opcode]->slottypes[i].type; i++) {
op->slots[i].slottype = opdefines[opcode]->slottypes[i].type;
op->slots[i].index = i;
}
/* callback */
op->exec = opdefines[opcode]->exec;
/* memarena, used for operator's slot buffers */
op->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmesh operator");
BLI_memarena_use_calloc (op->arena);
}
/*
* BMESH OPSTACK EXEC OP
*
* Executes a passed in operator. This handles
* the allocation and freeing of temporary flag
* layers and starting/stopping the modelling
* loop. Can be called from other operators
* exec callbacks as well.
*/
void BMO_op_exec(BMesh *bm, BMOperator *op)
{
BMO_push(bm, op);
if (bm->stackdepth == 2)
bmesh_begin_edit(bm, op->flag);
op->exec(bm, op);
if (bm->stackdepth == 2)
bmesh_end_edit(bm, op->flag);
BMO_pop(bm);
}
/*
* BMESH OPSTACK FINISH OP
*
* Does housekeeping chores related to finishing
* up an operator.
*/
void BMO_op_finish(BMesh *bm, BMOperator *op)
{
BMOpSlot *slot;
int i;
for (i = 0; opdefines[op->type]->slottypes[i].type; i++) {
slot = &op->slots[i];
if (slot->slottype == BMO_OP_SLOT_MAPPING) {
if (slot->data.ghash)
BLI_ghash_free(slot->data.ghash, NULL, NULL);
}
}
BLI_memarena_free(op->arena);
#ifdef DEBUG
BM_ELEM_INDEX_VALIDATE(bm, "post bmo", opdefines[op->type]->name);
#else
(void)bm;
#endif
}
/*
* BMESH OPSTACK HAS SLOT
*
* Returns 1 if the named slot exists on the given operator,
* otherwise returns 0.
*/
int BMO_slot_exists(BMOperator *op, const char *slotname)
{
int slotcode = bmesh_name_to_slotcode(opdefines[op->type], slotname);
return (slotcode >= 0);
}
/*
* BMESH OPSTACK GET SLOT
*
* Returns a pointer to the slot of
* type 'slotcode'
*/
BMOpSlot *BMO_slot_get(BMOperator *op, const char *slotname)
{
int slotcode = bmesh_name_to_slotcode_check(opdefines[op->type], slotname);
if (slotcode < 0) {
return &BMOpEmptySlot;
}
return &(op->slots[slotcode]);
}
/*
* BMESH OPSTACK COPY SLOT
*
* Copies data from one slot to another
*/
void BMO_slot_copy(BMOperator *source_op, BMOperator *dest_op, const char *src, const char *dst)
{
BMOpSlot *source_slot = BMO_slot_get(source_op, src);
BMOpSlot *dest_slot = BMO_slot_get(dest_op, dst);
if (source_slot == dest_slot)
return;
if (source_slot->slottype != dest_slot->slottype)
return;
if (dest_slot->slottype > BMO_OP_SLOT_VEC) {
if (dest_slot->slottype != BMO_OP_SLOT_MAPPING) {
/* do buffer copy */
dest_slot->data.buf = NULL;
dest_slot->len = source_slot->len;
if (dest_slot->len) {
const int slot_alloc_size = BMO_OPSLOT_TYPEINFO[dest_slot->slottype] * dest_slot->len;
dest_slot->data.buf = BLI_memarena_alloc(dest_op->arena, slot_alloc_size);
memcpy(dest_slot->data.buf, source_slot->data.buf, slot_alloc_size);
}
}
else {
GHashIterator it;
BMOElemMapping *srcmap, *dstmap;
/* sanity check */
if (!source_slot->data.ghash) return;
if (!dest_slot->data.ghash) {
dest_slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash,
BLI_ghashutil_ptrcmp, "bmesh operator 2");
}
BLI_ghashIterator_init(&it, source_slot->data.ghash);
for ( ; (srcmap = BLI_ghashIterator_getValue(&it));
BLI_ghashIterator_step(&it))
{
dstmap = BLI_memarena_alloc(dest_op->arena, sizeof(*dstmap) + srcmap->len);
dstmap->element = srcmap->element;
dstmap->len = srcmap->len;
memcpy(dstmap + 1, srcmap + 1, srcmap->len);
BLI_ghash_insert(dest_slot->data.ghash, dstmap->element, dstmap);
}
}
}
else {
dest_slot->data = source_slot->data;
}
}
/*
* BMESH OPSTACK SET XXX
*
* Sets the value of a slot depending on it's type
*
*/
void BMO_slot_float_set(BMOperator *op, const char *slotname, const float f)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_FLT))
return;
slot->data.f = f;
}
void BMO_slot_int_set(BMOperator *op, const char *slotname, const int i)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_INT))
return;
slot->data.i = i;
}
/* only supports square mats */
void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_MAT))
return;
slot->len = 4;
slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float) * 4 * 4);
if (size == 4) {
memcpy(slot->data.p, mat, sizeof(float) * 4 * 4);
}
else if (size == 3) {
copy_m4_m3(slot->data.p, (float (*)[3])mat);
}
else {
fprintf(stderr, "%s: invalid size argument %d (bmesh internal error)\n", __func__, size);
memset(slot->data.p, 0, sizeof(float) * 4 * 4);
}
}
void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4])
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_MAT))
return;
copy_m4_m4(r_mat, (float (*)[4])slot->data.p);
}
void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3])
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_MAT))
return;
copy_m3_m4(r_mat, slot->data.p);
}
void BMO_slot_ptr_set(BMOperator *op, const char *slotname, void *p)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_PNT))
return;
slot->data.p = p;
}
void BMO_slot_vec_set(BMOperator *op, const char *slotname, const float vec[3])
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_VEC))
return;
copy_v3_v3(slot->data.vec, vec);
}
float BMO_slot_float_get(BMOperator *op, const char *slotname)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_FLT))
return 0.0f;
return slot->data.f;
}
int BMO_slot_int_get(BMOperator *op, const char *slotname)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_INT))
return 0;
return slot->data.i;
}
void *BMO_slot_ptr_get(BMOperator *op, const char *slotname)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_PNT))
return NULL;
return slot->data.p;
}
void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3])
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (!(slot->slottype == BMO_OP_SLOT_VEC))
return;
copy_v3_v3(r_vec, slot->data.vec);
}
/*
* BMO_COUNTFLAG
*
* Counts the number of elements of a certain type that
* have a specific flag set.
*
*/
int BMO_mesh_flag_count(BMesh *bm, const short oflag, const char htype)
{
BMIter elements;
int count = 0;
BMElemF *ele_f;
if (htype & BM_VERT) {
for (ele_f = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
if (BMO_elem_flag_test(bm, ele_f, oflag))
count++;
}
}
if (htype & BM_EDGE) {
for (ele_f = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
if (BMO_elem_flag_test(bm, ele_f, oflag))
count++;
}
}
if (htype & BM_FACE) {
for (ele_f = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
if (BMO_elem_flag_test(bm, ele_f, oflag))
count++;
}
}
return count;
}
void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *UNUSED(op), const char htype, const short oflag)
{
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};
BMIter iter;
BMElemF *ele;
int i;
for (i = 0; i < 3; i++) {
if (htype & flag_types[i]) {
BM_ITER(ele, &iter, bm, iter_types[i], NULL) {
BMO_elem_flag_disable(bm, ele, oflag);
}
}
}
}
int BMO_slot_buf_count(struct BMesh *UNUSED(bm), struct BMOperator *op, const char *slotname)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
/* check if its actually a buffer */
if (!(slot->slottype > BMO_OP_SLOT_VEC))
return 0;
return slot->len;
}
int BMO_slot_map_count(BMesh *UNUSED(bm), BMOperator *op, const char *slotname)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
/* check if its actually a buffer */
if (!(slot->slottype == BMO_OP_SLOT_MAPPING))
return 0;
return slot->data.ghash ? BLI_ghash_size(slot->data.ghash) : 0;
}
#if 0
void *BMO_Grow_Array(BMesh *bm, BMOperator *op, int slotcode, int totadd)
{
BMOpSlot *slot = &op->slots[slotcode];
void *tmp;
ssize_t allocsize;
/* check if its actually a buffer */
if (!(slot->slottype > BMO_OP_SLOT_VEC))
return NULL;
if (slot->flag & BMOS_DYNAMIC_ARRAY) {
if (slot->len >= slot->size) {
slot->size = (slot->size + 1 + totadd) * 2;
allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->size;
tmp = slot->data.buf;
slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array");
memcpy(slot->data.buf, tmp, allocsize);
MEM_freeN(tmp);
}
slot->len += totadd;
}
else {
slot->flag |= BMOS_DYNAMIC_ARRAY;
slot->len += totadd;
slot->size = slot->len + 2;
allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->len;
tmp = slot->data.buf;
slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array");
memcpy(slot->data.buf, tmp, allocsize);
}
return slot->data.buf;
}
#endif
void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op,
const char *slotname, const short oflag)
{
GHashIterator it;
BMOpSlot *slot = BMO_slot_get(op, slotname);
BMElemF *ele_f;
/* sanity check */
if (slot->slottype != BMO_OP_SLOT_MAPPING) return;
if (!slot->data.ghash) return;
BLI_ghashIterator_init(&it, slot->data.ghash);
for ( ; (ele_f = BLI_ghashIterator_getKey(&it)); BLI_ghashIterator_step(&it)) {
BMO_elem_flag_enable(bm, ele_f, oflag);
}
}
static void *bmo_slot_buffer_alloc(BMOperator *op, const char *slotname, int len)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
/* check if its actually a buffer */
if (!(slot->slottype > BMO_OP_SLOT_VEC))
return NULL;
slot->len = len;
if (len)
slot->data.buf = BLI_memarena_alloc(op->arena, BMO_OPSLOT_TYPEINFO[slot->slottype] * len);
return slot->data.buf;
}
/*
* BMO_ALL_TO_SLOT
*
* Copies all elements of a certain type into an operator slot.
*
*/
static void BMO_slot_from_all(BMesh *bm, BMOperator *op, const char *slotname, const char htype)
{
BMIter elements;
BMHeader *e;
BMOpSlot *output = BMO_slot_get(op, slotname);
int totelement = 0, i = 0;
if (htype & BM_VERT) totelement += bm->totvert;
if (htype & BM_EDGE) totelement += bm->totedge;
if (htype & BM_FACE) totelement += bm->totface;
if (totelement) {
bmo_slot_buffer_alloc(op, slotname, totelement);
if (htype & BM_VERT) {
for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
((BMHeader **)output->data.p)[i] = e;
i++;
}
}
if (htype & BM_EDGE) {
for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
((BMHeader **)output->data.p)[i] = e;
i++;
}
}
if (htype & BM_FACE) {
for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
((BMHeader **)output->data.p)[i] = e;
i++;
}
}
}
}
/*
* BMO_HEADERFLAG_TO_SLOT
*
* Copies elements of a certain type, which have a certain header flag set
* into a slot for an operator.
*/
void BMO_slot_from_hflag(BMesh *bm, BMOperator *op, const char *slotname,
const char hflag, const char htype)
{
BMIter elements;
BMHeader *e;
BMOpSlot *output = BMO_slot_get(op, slotname);
int totelement = 0, i = 0;
totelement = BM_mesh_count_flag(bm, htype, hflag, 1);
if (totelement) {
bmo_slot_buffer_alloc(op, slotname, totelement);
if (htype & BM_VERT) {
for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
((BMHeader **)output->data.p)[i] = e;
i++;
}
}
}
if (htype & BM_EDGE) {
for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
((BMHeader **)output->data.p)[i] = e;
i++;
}
}
}
if (htype & BM_FACE) {
for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
((BMHeader **)output->data.p)[i] = e;
i++;
}
}
}
}
else {
output->len = 0;
}
}
/*
* BMO_FLAG_TO_SLOT
*
* Copies elements of a certain type, which have a certain flag set
* into an output slot for an operator.
*/
void BMO_slot_from_flag(BMesh *bm, BMOperator *op, const char *slotname,
const short oflag, const char htype)
{
BMIter elements;
BMHeader *ele;
BMOpSlot *output = BMO_slot_get(op, slotname);
int totelement = BMO_mesh_flag_count(bm, oflag, htype), i = 0;
if (totelement) {
bmo_slot_buffer_alloc(op, slotname, totelement);
if (htype & BM_VERT) {
for (ele = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
((BMHeader **)output->data.p)[i] = ele;
i++;
}
}
}
if (htype & BM_EDGE) {
for (ele = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
((BMHeader **)output->data.p)[i] = ele;
i++;
}
}
}
if (htype & BM_FACE) {
for (ele = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
((BMHeader **)output->data.p)[i] = ele;
i++;
}
}
}
}
else {
output->len = 0;
}
}
/*
*
* BMO_FLAG_BUFFER
*
* Header Flags elements in a slots buffer, automatically
* using the selection API where appropriate.
*/
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOperator *op, const char *slotname,
const char hflag, const char htype)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
BMHeader **data = slot->data.p;
int i;
for (i = 0; i < slot->len; i++) {
if (!(htype & data[i]->htype))
continue;
if (hflag & BM_ELEM_SELECT) {
BM_elem_select_set(bm, data[i], TRUE);
}
BM_elem_flag_enable(data[i], hflag);
}
}
/*
*
* BMO_FLAG_BUFFER
*
* Removes flags from elements in a slots buffer, automatically
* using the selection API where appropriate.
*/
void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOperator *op, const char *slotname,
const char hflag, const char htype)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
BMHeader **data = slot->data.p;
int i;
for (i = 0; i < slot->len; i++) {
if (!(htype & data[i]->htype))
continue;
if (hflag & BM_ELEM_SELECT) {
BM_elem_select_set(bm, data[i], FALSE);
}
BM_elem_flag_disable(data[i], hflag);
}
}
int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag)
{
int count = 0;
if (v->e) {
BMEdge *curedge;
const int len = bmesh_disk_count(v);
int i;
for (i = 0, curedge = v->e; i < len; i++) {
if (BMO_elem_flag_test(bm, curedge, oflag))
count++;
curedge = bmesh_disk_nextedge(curedge, v);
}
}
return count;
}
/*
*
* BMO_FLAG_BUFFER
*
* Flags elements in a slots buffer
*/
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOperator *op, const char *slotname,
const short oflag, const char htype)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
BMHeader **data = slot->data.p;
int i;
for (i = 0; i < slot->len; i++) {
if (!(htype & data[i]->htype))
continue;
BMO_elem_flag_enable(bm, (BMElemF *)data[i], oflag);
}
}
/*
*
* BMO_FLAG_BUFFER
*
* Removes flags from elements in a slots buffer
*/
void BMO_slot_buffer_flag_disable(BMesh *bm, BMOperator *op, const char *slotname,
const short oflag, const char htype)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
BMHeader **data = slot->data.p;
int i;
for (i = 0; i < slot->len; i++) {
if (!(htype & data[i]->htype))
continue;
BMO_elem_flag_disable(bm, (BMElemF *)data[i], oflag);
}
}
/*
*
* ALLOC/FREE FLAG LAYER
*
* Used by operator stack to free/allocate
* private flag data. This is allocated
* using a mempool so the allocation/frees
* should be quite fast.
*
* BMESH_TODO:
* Investigate not freeing flag layers until
* all operators have been executed. This would
* save a lot of realloc potentially.
*/
static void bmo_flag_layer_alloc(BMesh *bm)
{
BMElemF *ele;
/* set the index values since we are looping over all data anyway,
* may save time later on */
int i;
BMIter iter;
BLI_mempool *oldpool = bm->toolflagpool; /* old flag pool */
BLI_mempool *newpool;
void *oldflags;
/* store memcpy size for reuse */
const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer));
bm->totflags++;
/* allocate new flag poo */
bm->toolflagpool = newpool = BLI_mempool_create(sizeof(BMFlagLayer)*bm->totflags, 512, 512, FALSE, FALSE);
/* now go through and memcpy all the flags. Loops don't get a flag layer at this time.. */
for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
oldflags = ele->oflags;
ele->oflags = BLI_mempool_calloc(newpool);
memcpy(ele->oflags, oldflags, old_totflags_size);
BM_elem_index_set(ele, i); /* set_inline */
}
for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
oldflags = ele->oflags;
ele->oflags = BLI_mempool_calloc(newpool);
memcpy(ele->oflags, oldflags, old_totflags_size);
BM_elem_index_set(ele, i); /* set_inline */
}
for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
oldflags = ele->oflags;
ele->oflags = BLI_mempool_calloc(newpool);
memcpy(ele->oflags, oldflags, old_totflags_size);
BM_elem_index_set(ele, i); /* set_inline */
}
bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
BLI_mempool_destroy(oldpool);
}
static void bmo_flag_layer_free(BMesh *bm)
{
BMElemF *ele;
/* set the index values since we are looping over all data anyway,
* may save time later on */
int i;
BMIter iter;
BLI_mempool *oldpool = bm->toolflagpool;
BLI_mempool *newpool;
void *oldflags;
/* store memcpy size for reuse */
const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer));
/* de-increment the totflags first.. */
bm->totflags--;
/* allocate new flag poo */
bm->toolflagpool = newpool = BLI_mempool_create(new_totflags_size, 512, 512, TRUE, FALSE);
/* now go through and memcpy all the flag */
for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
oldflags = ele->oflags;
ele->oflags = BLI_mempool_calloc(newpool);
memcpy(ele->oflags, oldflags, new_totflags_size);
BM_elem_index_set(ele, i); /* set_inline */
}
for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
oldflags = ele->oflags;
ele->oflags = BLI_mempool_calloc(newpool);
memcpy(ele->oflags, oldflags, new_totflags_size);
BM_elem_index_set(ele, i); /* set_inline */
}
for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
oldflags = ele->oflags;
ele->oflags = BLI_mempool_calloc(newpool);
memcpy(ele->oflags, oldflags, new_totflags_size);
BM_elem_index_set(ele, i); /* set_inline */
}
bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
BLI_mempool_destroy(oldpool);
}
static void bmo_flag_layer_clear(BMesh *bm)
{
BMElemF *ele;
/* set the index values since we are looping over all data anyway,
* may save time later on */
int i;
BMIter iter;
const int totflags_offset = bm->totflags - 1;
/* now go through and memcpy all the flag */
for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
BM_elem_index_set(ele, i); /* set_inline */
}
for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
BM_elem_index_set(ele, i); /* set_inline */
}
for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
BM_elem_index_set(ele, i); /* set_inline */
}
bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
}
void *BMO_slot_elem_first(BMOperator *op, const char *slotname)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
if (slot->slottype != BMO_OP_SLOT_ELEMENT_BUF)
return NULL;
return slot->data.buf ? *(void **)slot->data.buf : NULL;
}
void *BMO_iter_new(BMOIter *iter, BMesh *UNUSED(bm), BMOperator *op,
const char *slotname, const char restrictmask)
{
BMOpSlot *slot = BMO_slot_get(op, slotname);
memset(iter, 0, sizeof(BMOIter));
iter->slot = slot;
iter->cur = 0;
iter->restrictmask = restrictmask;
if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) {
if (iter->slot->data.ghash) {
BLI_ghashIterator_init(&iter->giter, slot->data.ghash);
}
else {
return NULL;
}
}
return BMO_iter_step(iter);
}
void *BMO_iter_step(BMOIter *iter)
{
if (iter->slot->slottype == BMO_OP_SLOT_ELEMENT_BUF) {
BMHeader *h;
if (iter->cur >= iter->slot->len) {
return NULL;
}
h = ((void **)iter->slot->data.buf)[iter->cur++];
while (!(iter->restrictmask & h->htype)) {
if (iter->cur >= iter->slot->len) {
return NULL;
}
h = ((void **)iter->slot->data.buf)[iter->cur++];
}
return h;
}
else if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) {
struct BMOElemMapping *map;
void *ret = BLI_ghashIterator_getKey(&iter->giter);
map = BLI_ghashIterator_getValue(&iter->giter);
iter->val = map + 1;
BLI_ghashIterator_step(&iter->giter);
return ret;
}
return NULL;
}
/* used for iterating over mapping */
void *BMO_iter_map_value(BMOIter *iter)
{
return iter->val;
}
void *BMO_iter_map_value_p(BMOIter *iter)
{
return *((void **)iter->val);
}
float BMO_iter_map_value_f(BMOIter *iter)
{
return *((float *)iter->val);
}
/* error syste */
typedef struct BMOpError {
struct BMOpError *next, *prev;
int errorcode;
BMOperator *op;
const char *msg;
} BMOpError;
void BMO_error_clear(BMesh *bm)
{
while (BMO_error_pop(bm, NULL, NULL));
}
void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg)
{
BMOpError *err = MEM_callocN(sizeof(BMOpError), "bmop_error");
err->errorcode = errcode;
if (!msg) msg = bmo_error_messages[errcode];
err->msg = msg;
err->op = owner;
BLI_addhead(&bm->errorstack, err);
}
int BMO_error_occurred(BMesh *bm)
{
return bm->errorstack.first != NULL;
}
/* returns error code or 0 if no erro */
int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op)
{
BMOpError *err = bm->errorstack.first;
if (!err) {
return 0;
}
if (msg) *msg = err->msg;
if (op) *op = err->op;
return err->errorcode;
}
int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op)
{
int errorcode = BMO_error_get(bm, msg, op);
if (errorcode) {
BMOpError *err = bm->errorstack.first;
BLI_remlink(&bm->errorstack, bm->errorstack.first);
MEM_freeN(err);
}
return errorcode;
}
/* example:
* BMO_CallOp(bm, "del %d %hv", DEL_ONLYFACES, BM_ELEM_SELECT);
*
* d - int
* i - int
* f - float
* hv - header flagged verts
* he - header flagged edges
* hf - header flagged faces
* fv - flagged verts
* fe - flagged edges
* ff - flagged faces
*/
#define NEXT_CHAR(fmt) ((fmt)[0] != 0 ? (fmt)[1] : 0)
static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name)
{
int i;
for (i = 0; def->slottypes[i].type; i++) {
if (!strncmp(name, def->slottypes[i].name, MAX_SLOTNAME)) {
return i;
}
}
return -1;
}
static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name)
{
int i = bmesh_name_to_slotcode(def, name);
if (i < 0) {
fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, name);
}
return i;
}
static int bmesh_opname_to_opcode(const char *opname)
{
int i;
for (i = 0; i < bmesh_total_ops; i++) {
if (!strcmp(opname, opdefines[i]->name)) {
return i;
}
}
fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, opname);
return -1;
}
int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *_fmt, va_list vlist)
{
BMOpDefine *def;
char *opname, *ofmt, *fmt;
char slotname[64] = {0};
int i /*, n = strlen(fmt) */, stop /*, slotcode = -1 */, ret, type, state;
int noslot = 0;
/* basic useful info to help find where bmop formatting strings fail */
int lineno = -1;
# define GOTO_ERROR { lineno = __LINE__; goto error; }
/* we muck around in here, so dup i */
fmt = ofmt = BLI_strdup(_fmt);
/* find operator nam */
i = strcspn(fmt, " \t");
opname = fmt;
if (!opname[i]) noslot = 1;
opname[i] = '\0';
fmt += i + (noslot ? 0 : 1);
i = bmesh_opname_to_opcode(opname);
if (i == -1) {
MEM_freeN(ofmt);
return FALSE;
}
BMO_op_init(bm, op, opname);
def = opdefines[i];
i = 0;
state = 1; /* 0: not inside slotcode name, 1: inside slotcode name */
while (*fmt) {
if (state) {
/* jump past leading whitespac */
i = strspn(fmt, " \t");
fmt += i;
/* ignore trailing whitespac */
if (!fmt[i])
break;
/* find end of slot name. currently this is
* a little flexible, allowing "slot=%f",
* "slot %f", "slot%f", and "slot\t%f". */
i = strcspn(fmt, "= \t%");
if (!fmt[i]) GOTO_ERROR;
fmt[i] = 0;
if (bmesh_name_to_slotcode_check(def, fmt) < 0) GOTO_ERROR;
BLI_strncpy(slotname, fmt, sizeof(slotname));
state = 0;
fmt += i;
}
else {
switch (*fmt) {
case ' ':
case '\t':
case '=':
case '%':
break;
case 'm': {
int size, c;
c = NEXT_CHAR(fmt);
fmt++;
if (c == '3') size = 3;
else if (c == '4') size = 4;
else GOTO_ERROR;
BMO_slot_mat_set(op, slotname, va_arg(vlist, void *), size);
state = 1;
break;
}
case 'v': {
BMO_slot_vec_set(op, slotname, va_arg(vlist, float *));
state = 1;
break;
}
case 'e': {
BMHeader *ele = va_arg(vlist, void *);
BMOpSlot *slot = BMO_slot_get(op, slotname);
slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(void *) * 4);
slot->len = 1;
*((void **)slot->data.buf) = ele;
state = 1;
break;
}
case 's': {
BMOperator *op2 = va_arg(vlist, void *);
const char *slotname2 = va_arg(vlist, char *);
BMO_slot_copy(op2, op, slotname2, slotname);
state = 1;
break;
}
case 'i':
case 'd':
BMO_slot_int_set(op, slotname, va_arg(vlist, int));
state = 1;
break;
case 'p':
BMO_slot_ptr_set(op, slotname, va_arg(vlist, void *));
state = 1;
break;
case 'f':
case 'h':
case 'a':
type = *fmt;
if (NEXT_CHAR(fmt) == ' ' || NEXT_CHAR(fmt) == '\t' || NEXT_CHAR(fmt) == '\0') {
BMO_slot_float_set(op, slotname, va_arg(vlist, double));
}
else {
ret = 0;
stop = 0;
while (1) {
switch (NEXT_CHAR(fmt)) {
case 'f': ret |= BM_FACE; break;
case 'e': ret |= BM_EDGE; break;
case 'v': ret |= BM_VERT; break;
default:
stop = 1;
break;
}
if (stop) {
break;
}
fmt++;
}
if (type == 'h') {
BMO_slot_from_hflag(bm, op, slotname, va_arg(vlist, int), ret);
}
else if (type == 'a') {
BMO_slot_from_all(bm, op, slotname, ret);
}
else {
BMO_slot_from_flag(bm, op, slotname, va_arg(vlist, int), ret);
}
}
state = 1;
break;
default:
fprintf(stderr,
"%s: unrecognized bmop format char: %c, %d in '%s'\n",
__func__, *fmt, (int)(fmt - ofmt), ofmt);
break;
}
}
fmt++;
}
MEM_freeN(ofmt);
return TRUE;
error:
/* non urgent todo - explain exactly what is failing */
fprintf(stderr,
"%s: error parsing formatting string, %d in '%s'\n see - %s:%d\n",
__func__, (int)(fmt - ofmt), _fmt, __FILE__, lineno);
MEM_freeN(ofmt);
BMO_op_finish(bm, op);
return FALSE;
#undef GOTO_ERROR
}
int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...)
{
va_list list;
va_start(list, fmt);
if (!BMO_op_vinitf(bm, op, fmt, list)) {
printf("%s: failed\n", __func__);
va_end(list);
return FALSE;
}
va_end(list);
return TRUE;
}
int BMO_op_callf(BMesh *bm, const char *fmt, ...)
{
va_list list;
BMOperator op;
va_start(list, fmt);
if (!BMO_op_vinitf(bm, &op, fmt, list)) {
printf("%s: failed, format is:\n \"%s\"\n", __func__, fmt);
va_end(list);
return FALSE;
}
BMO_op_exec(bm, &op);
BMO_op_finish(bm, &op);
va_end(list);
return TRUE;
}

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

View File

@@ -0,0 +1,1069 @@
/*
* ***** 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_polygon.c
* \ingroup bmesh
*
* This file contains code for dealing
* with polygons (normal/area calculation,
* tesselation, etc)
*
* BMESH_TODO:
* - Add in Tesselator frontend that creates
* BMTriangles from copied faces
*
* - Add in Function that checks for and flags
* degenerate faces.
*/
#include "BLI_math.h"
#include "BLI_array.h"
#include "MEM_guardedalloc.h"
#include "bmesh.h"
#include "bmesh_private.h"
/*
* TEST EDGE SIDE and POINT IN TRIANGLE
*
* Point in triangle tests stolen from scanfill.c.
* Used for tesselator
*
*/
static short testedgeside(const double v1[2], const double v2[2], const double v3[2])
{
/* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */
double inp;
//inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]);
inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]);
if (inp < 0.0) {
return FALSE;
}
else if (inp == 0) {
if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE;
if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE;
}
return TRUE;
}
static short testedgesidef(const float v1[2], const float v2[2], const float v3[2])
{
/* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */
double inp;
//inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]);
inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]);
if (inp < 0.0) {
return FALSE;
}
else if (inp == 0) {
if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE;
if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE;
}
return TRUE;
}
static int point_in_triangle(const double v1[2], const double v2[2], const double v3[2], const double pt[2])
{
if (testedgeside(v1, v2, pt) && testedgeside(v2, v3, pt) && testedgeside(v3, v1, pt)) {
return TRUE;
}
return FALSE;
}
/*
* COMPUTE POLY NORMAL
*
* Computes the normal of a planar
* polygon See Graphics Gems for
* computing newell normal.
*
*/
static void compute_poly_normal(float normal[3], float (*verts)[3], int nverts)
{
float u[3], v[3], w[3]; /*, *w, v1[3], v2[3]; */
float n[3] = {0.0f, 0.0f, 0.0f} /*, l, v1[3], v2[3] */;
/* double l2; */
int i /*, s = 0 */;
/* this fixes some weird numerical erro */
add_v3_fl(verts[0], 0.0001f);
for (i = 0; i < nverts; i++) {
copy_v3_v3(u, verts[i]);
copy_v3_v3(v, verts[(i + 1) % nverts]);
copy_v3_v3(w, verts[(i + 2) % nverts]);
#if 0
sub_v3_v3v3(v1, w, v);
sub_v3_v3v3(v2, v, u);
normalize_v3(v1);
normalize_v3(v2);
l = dot_v3v3(v1, v2);
if (fabsf(l - 1.0) < 0.1f) {
continue;
}
#endif
/* newell's method
so thats?:
(a[1] - b[1]) * (a[2] + b[2]);
a[1]*b[2] - b[1]*a[2] - b[1]*b[2] + a[1]*a[2]
odd. half of that is the cross product. . .what's the
other half?
also could be like a[1]*(b[2] + a[2]) - b[1]*(a[2] - b[2])
*/
n[0] += (u[1] - v[1]) * (u[2] + v[2]);
n[1] += (u[2] - v[2]) * (u[0] + v[0]);
n[2] += (u[0] - v[0]) * (u[1] + v[1]);
}
if (normalize_v3_v3(normal, n) == 0.0f) {
normal[2] = 1.0f; /* other axis set to 0.0 */
}
#if 0
l = len_v3(n);
/* fast square root, newton/babylonian method:
l2 = l * 0.1;
l2 = (l / l2 + l2) * 0.5;
l2 = (l / l2 + l2) * 0.5;
l2 = (l / l2 + l2) * 0.5;
*/
if (l == 0.0) {
normal[0] = 0.0f;
normal[1] = 0.0f;
normal[2] = 1.0f;
return;
}
else {
l = 1.0f / l;
}
mul_v3_fl(n, l);
copy_v3_v3(normal, n);
#endif
}
/*
* COMPUTE POLY CENTER
*
* Computes the centroid and
* area of a polygon in the X/Y
* plane.
*
*/
static int compute_poly_center(float center[3], float *r_area, float (*verts)[3], int nverts)
{
int i, j;
float atmp = 0.0f, xtmp = 0.0f, ytmp = 0.0f, ai;
zero_v3(center);
if (nverts < 3)
return FALSE;
i = nverts - 1;
j = 0;
while (j < nverts) {
ai = verts[i][0] * verts[j][1] - verts[j][0] * verts[i][1];
atmp += ai;
xtmp += (verts[j][0] + verts[i][0]) * ai;
ytmp += (verts[j][1] + verts[i][1]) * ai;
i = j;
j += 1;
}
if (r_area)
*r_area = atmp / 2.0f;
if (atmp != 0) {
center[0] = xtmp / (3.0f * atmp);
center[1] = xtmp / (3.0f * atmp);
return TRUE;
}
return FALSE;
}
float BM_face_area_calc(BMesh *bm, BMFace *f)
{
BMLoop *l;
BMIter iter;
float (*verts)[3];
float center[3];
float area = 0.0f;
int i;
BLI_array_fixedstack_declare(verts, BM_NGON_STACK_SIZE, f->len, __func__);
i = 0;
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
copy_v3_v3(verts[i], l->v->co);
i++;
}
compute_poly_center(center, &area, verts, f->len);
BLI_array_fixedstack_free(verts);
return area;
}
/*
* computes center of face in 3d. uses center of bounding box.
*/
void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float r_cent[3])
{
BMIter iter;
BMLoop *l;
float min[3], max[3];
int i;
INIT_MINMAX(min, max);
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
for (i = 0; l; l = BM_iter_step(&iter), i++) {
DO_MINMAX(l->v->co, min, max);
}
mid_v3_v3v3(r_cent, min, max);
}
void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float r_cent[3])
{
BMIter iter;
BMLoop *l;
int i;
zero_v3(r_cent);
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
for (i = 0; l; l = BM_iter_step(&iter), i++) {
add_v3_v3(r_cent, l->v->co);
}
if (f->len) mul_v3_fl(r_cent, 1.0f / (float)f->len);
}
/*
* COMPUTE POLY PLANE
*
* Projects a set polygon's vertices to
* a plane defined by the average
* of its edges cross products
*
*/
void compute_poly_plane(float (*verts)[3], int nverts)
{
float avgc[3], norm[3], mag, avgn[3];
float *v1, *v2, *v3;
int i;
if (nverts < 3)
return;
zero_v3(avgn);
zero_v3(avgc);
for (i = 0; i < nverts; i++) {
v1 = verts[i];
v2 = verts[(i + 1) % nverts];
v3 = verts[(i + 2) % nverts];
normal_tri_v3(norm, v1, v2, v3);
add_v3_v3(avgn, norm);
}
/* what was this bit for */
if (is_zero_v3(avgn)) {
avgn[0] = 0.0f;
avgn[1] = 0.0f;
avgn[2] = 1.0f;
}
else {
/* XXX, why is this being divided and _then_ normalized
* division could be removed? - campbell */
avgn[0] /= nverts;
avgn[1] /= nverts;
avgn[2] /= nverts;
normalize_v3(avgn);
}
for (i = 0; i < nverts; i++) {
v1 = verts[i];
mag = dot_v3v3(v1, avgn);
madd_v3_v3fl(v1, avgn, -mag);
}
}
/*
* BM LEGAL EDGES
*
* takes in a face and a list of edges, and sets to NULL any edge in
* the list that bridges a concave region of the face or intersects
* any of the faces's edges.
*/
#if 0 /* needs BLI math double versions of these functions */
static void shrink_edged(double *v1, double *v2, double fac)
{
double mid[3];
mid_v3_v3v3(mid, v1, v2);
sub_v3_v3v3(v1, v1, mid);
sub_v3_v3v3(v2, v2, mid);
mul_v3_fl(v1, fac);
mul_v3_fl(v2, fac);
add_v3_v3v3(v1, v1, mid);
add_v3_v3v3(v2, v2, mid);
}
#endif
static void shrink_edgef(float v1[3], float v2[3], const float fac)
{
float mid[3];
mid_v3_v3v3(mid, v1, v2);
sub_v3_v3v3(v1, v1, mid);
sub_v3_v3v3(v2, v2, mid);
mul_v3_fl(v1, fac);
mul_v3_fl(v2, fac);
add_v3_v3v3(v1, v1, mid);
add_v3_v3v3(v2, v2, mid);
}
/*
* POLY ROTATE PLANE
*
* Rotates a polygon so that it's
* normal is pointing towards the mesh Z axis
*
*/
void poly_rotate_plane(const float normal[3], float (*verts)[3], const int nverts)
{
float up[3] = {0.0f, 0.0f, 1.0f}, axis[3], q[4];
float mat[3][3];
double angle;
int i;
cross_v3_v3v3(axis, normal, up);
angle = saacos(dot_v3v3(normal, up));
if (angle == 0.0) return;
axis_angle_to_quat(q, axis, (float)angle);
quat_to_mat3(mat, q);
for (i = 0; i < nverts; i++)
mul_m3_v3(mat, verts[i]);
}
/*
* BMESH UPDATE FACE NORMAL
*
* Updates the stored normal for the
* given face. Requires that a buffer
* of sufficient length to store projected
* coordinates for all of the face's vertices
* is passed in as well.
*
*/
void BM_face_normal_update(BMesh *bm, BMFace *f)
{
if (f->len >= 3) {
float (*proj)[3];
BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__);
bmesh_update_face_normal(bm, f, f->no, proj);
BLI_array_fixedstack_free(proj);
}
}
/* same as BM_face_normal_update but takes vertex coords */
void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3])
{
if (f->len >= 3) {
float (*proj)[3];
BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__);
bmesh_update_face_normal_vertex_cos(bm, f, no, proj, vertexCos);
BLI_array_fixedstack_free(proj);
}
}
void BM_edge_normals_update(BMesh *bm, BMEdge *e)
{
BMIter iter;
BMFace *f;
f = BM_iter_new(&iter, bm, BM_FACES_OF_EDGE, e);
for ( ; f; f = BM_iter_step(&iter)) {
BM_face_normal_update(bm, f);
}
BM_vert_normal_update(bm, e->v1);
BM_vert_normal_update(bm, e->v2);
}
void BM_vert_normal_update(BMesh *bm, BMVert *v)
{
/* TODO, we can normalize each edge only once, then compare with previous edge */
BMIter eiter, liter;
BMEdge *e;
BMLoop *l;
float vec1[3], vec2[3], fac;
int len = 0;
zero_v3(v->no);
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
if (l->v == v) {
/* Same calculation used in BM_mesh_normals_update */
sub_v3_v3v3(vec1, l->v->co, l->prev->v->co);
sub_v3_v3v3(vec2, l->next->v->co, l->v->co);
normalize_v3(vec1);
normalize_v3(vec2);
fac = saacos(-dot_v3v3(vec1, vec2));
madd_v3_v3fl(v->no, l->f->no, fac);
len++;
}
}
}
if (len) {
normalize_v3(v->no);
}
}
void BM_vert_normal_update_all(BMesh *bm, BMVert *v)
{
BMIter iter;
BMFace *f;
int len = 0;
f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v);
for ( ; f; f = BM_iter_step(&iter), len++) {
BM_face_normal_update(bm, f);
}
BM_vert_normal_update(bm, v);
}
void bmesh_update_face_normal(BMesh *bm, BMFace *f, float no[3],
float (*projectverts)[3])
{
BMLoop *l;
/* common cases first */
switch (f->len) {
case 4:
{
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
BMVert *v2 = (l = l->next)->v;
BMVert *v3 = (l = l->next)->v;
BMVert *v4 = (l->next)->v;
normal_quad_v3(no, v1->co, v2->co, v3->co, v4->co);
break;
}
case 3:
{
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
BMVert *v2 = (l = l->next)->v;
BMVert *v3 = (l->next)->v;
normal_tri_v3(no, v1->co, v2->co, v3->co);
break;
}
case 0:
{
zero_v3(no);
break;
}
default:
{
BMIter iter;
int i = 0;
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
copy_v3_v3(projectverts[i], l->v->co);
i += 1;
}
compute_poly_normal(no, projectverts, f->len);
break;
}
}
}
/* exact same as 'bmesh_update_face_normal' but accepts vertex coords */
void bmesh_update_face_normal_vertex_cos(BMesh *bm, BMFace *f, float no[3],
float (*projectverts)[3], float (*vertexCos)[3])
{
BMLoop *l;
/* must have valid index data */
BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
/* common cases first */
switch (f->len) {
case 4:
{
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
BMVert *v2 = (l = l->next)->v;
BMVert *v3 = (l = l->next)->v;
BMVert *v4 = (l->next)->v;
normal_quad_v3(no,
vertexCos[BM_elem_index_get(v1)],
vertexCos[BM_elem_index_get(v2)],
vertexCos[BM_elem_index_get(v3)],
vertexCos[BM_elem_index_get(v4)]);
break;
}
case 3:
{
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
BMVert *v2 = (l = l->next)->v;
BMVert *v3 = (l->next)->v;
normal_tri_v3(no,
vertexCos[BM_elem_index_get(v1)],
vertexCos[BM_elem_index_get(v2)],
vertexCos[BM_elem_index_get(v3)]);
break;
}
case 0:
{
zero_v3(no);
break;
}
default:
{
BMIter iter;
int i = 0;
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
copy_v3_v3(projectverts[i], vertexCos[BM_elem_index_get(l->v)]);
i += 1;
}
compute_poly_normal(no, projectverts, f->len);
break;
}
}
}
/*
* BMESH FLIP NORMAL
*
* Reverses the winding of a face.
* Note that this updates the calculated
* normal.
*/
void BM_face_normal_flip(BMesh *bm, BMFace *f)
{
bmesh_loop_reverse(bm, f);
negate_v3(f->no);
}
/* detects if two line segments cross each other (intersects).
* note, there could be more winding cases then there needs to be. */
static int UNUSED_FUNCTION(linecrosses)(const double v1[2], const double v2[2], const double v3[2], const double v4[2])
{
int w1, w2, w3, w4, w5;
/* w1 = winding(v1, v3, v4);
w2 = winding(v2, v3, v4);
w3 = winding(v3, v1, v2);
w4 = winding(v4, v1, v2);
return (w1 == w2) && (w3 == w4); */
w1 = testedgeside(v1, v3, v2);
w2 = testedgeside(v2, v4, v1);
w3 = !testedgeside(v1, v2, v3);
w4 = testedgeside(v3, v2, v4);
w5 = !testedgeside(v3, v1, v4);
return w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5;
}
/* detects if two line segments cross each other (intersects).
* note, there could be more winding cases then there needs to be. */
static int linecrossesf(const float v1[2], const float v2[2], const float v3[2], const float v4[2])
{
int w1, w2, w3, w4, w5 /*, re */;
float mv1[2], mv2[2], mv3[2], mv4[2];
/* now test windin */
w1 = testedgesidef(v1, v3, v2);
w2 = testedgesidef(v2, v4, v1);
w3 = !testedgesidef(v1, v2, v3);
w4 = testedgesidef(v3, v2, v4);
w5 = !testedgesidef(v3, v1, v4);
if (w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5) {
return TRUE;
}
#define GETMIN2_AXIS(a, b, ma, mb, axis) ma[axis] = MIN2(a[axis], b[axis]), mb[axis] = MAX2(a[axis], b[axis])
#define GETMIN2(a, b, ma, mb) GETMIN2_AXIS(a, b, ma, mb, 0); GETMIN2_AXIS(a, b, ma, mb, 1);
GETMIN2(v1, v2, mv1, mv2);
GETMIN2(v3, v4, mv3, mv4);
/* do an interval test on the x and y axe */
/* first do x axi */
#define T FLT_EPSILON * 15
if ( ABS(v1[1] - v2[1]) < T &&
ABS(v3[1] - v4[1]) < T &&
ABS(v1[1] - v3[1]) < T)
{
return (mv4[0] >= mv1[0] && mv3[0] <= mv2[0]);
}
/* now do y axi */
if ( ABS(v1[0] - v2[0]) < T &&
ABS(v3[0] - v4[0]) < T &&
ABS(v1[0] - v3[0]) < T)
{
return (mv4[1] >= mv1[1] && mv3[1] <= mv2[1]);
}
return FALSE;
}
/*
* BM POINT IN FACE
*
* 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])
{
int ax, ay;
float co2[3], cent[3] = {0.0f, 0.0f, 0.0f}, out[3] = {FLT_MAX * 0.5f, FLT_MAX * 0.5f, 0};
BMLoop *l_iter;
BMLoop *l_first;
int crosses = 0;
float eps = 1.0f + (float)FLT_EPSILON * 150.0f;
if (dot_v3v3(f->no, f->no) <= FLT_EPSILON * 10)
BM_face_normal_update(bm, f);
/* find best projection of face XY, XZ or YZ: barycentric weights of
* the 2d projected coords are the same and faster to compute
*
* this probably isn't all that accurate, but it has the advantage of
* being fast (especially compared to projecting into the face orientation)
*/
axis_dominant_v3(&ax, &ay, f->no);
co2[0] = co[ax];
co2[1] = co[ay];
co2[2] = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
cent[0] += l_iter->v->co[ax];
cent[1] += l_iter->v->co[ay];
} while ((l_iter = l_iter->next) != l_first);
mul_v2_fl(cent, 1.0f / (float)f->len);
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
float v1[3], v2[3];
v1[0] = (l_iter->prev->v->co[ax] - cent[ax]) * eps + cent[ax];
v1[1] = (l_iter->prev->v->co[ay] - cent[ay]) * eps + cent[ay];
v1[2] = 0.0f;
v2[0] = (l_iter->v->co[ax] - cent[ax]) * eps + cent[ax];
v2[1] = (l_iter->v->co[ay] - cent[ay]) * eps + cent[ay];
v2[2] = 0.0f;
crosses += linecrossesf(v1, v2, co2, out) != 0;
} while ((l_iter = l_iter->next) != l_first);
return crosses % 2 != 0;
}
static int goodline(float (*projectverts)[3], BMFace *f, int v1i,
int v2i, int v3i, int UNUSED(nvert))
{
BMLoop *l_iter;
BMLoop *l_first;
double v1[3], v2[3], v3[3], pv1[3], pv2[3];
int i;
VECCOPY(v1, projectverts[v1i]);
VECCOPY(v2, projectverts[v2i]);
VECCOPY(v3, projectverts[v3i]);
if (testedgeside(v1, v2, v3)) {
return FALSE;
}
//for (i = 0; i < nvert; i++) {
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
i = BM_elem_index_get(l_iter->v);
if (i == v1i || i == v2i || i == v3i) {
continue;
}
VECCOPY(pv1, projectverts[BM_elem_index_get(l_iter->v)]);
VECCOPY(pv2, projectverts[BM_elem_index_get(l_iter->next->v)]);
//if (linecrosses(pv1, pv2, v1, v3)) return FALSE;
if ( point_in_triangle(v1, v2, v3, pv1) ||
point_in_triangle(v3, v2, v1, pv1))
{
return FALSE;
}
} while ((l_iter = l_iter->next) != l_first);
return TRUE;
}
/*
* FIND EAR
*
* Used by tesselator to find
* the next triangle to 'clip off'
* of a polygon while tesselating.
*
*/
static BMLoop *find_ear(BMesh *UNUSED(bm), BMFace *f, float (*verts)[3], const int nvert)
{
BMVert *v1, *v2, *v3;
BMLoop *bestear = NULL;
BMLoop *l_iter;
BMLoop *l_first;
/* float angle, bestangle = 180.0f; */
int isear /*, i = 0 */;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
isear = 1;
v1 = l_iter->prev->v;
v2 = l_iter->v;
v3 = l_iter->next->v;
if (BM_edge_exists(v1, v3)) {
isear = 0;
}
else if (!goodline(verts, f, BM_elem_index_get(v1), BM_elem_index_get(v2), BM_elem_index_get(v3), nvert)) {
isear = 0;
}
if (isear) {
#if 0
angle = angle_v3v3v3(verts[v1->head.eflag2], verts[v2->head.eflag2], verts[v3->head.eflag2]);
if (!bestear || ABS(angle-45.0f) < bestangle) {
bestear = l;
bestangle = ABS(45.0f - angle);
}
if (angle > 20 && angle < 90) break;
if (angle < 100 && i > 5) break;
i += 1;
#endif
bestear = l_iter;
break;
}
} while ((l_iter = l_iter->next) != l_first);
return bestear;
}
/*
* BMESH TRIANGULATE FACE
*
* Triangulates a face using a
* simple 'ear clipping' algorithm
* that tries to favor non-skinny
* triangles (angles less than
* 90 degrees). If the triangulator
* has bits left over (or cannot
* triangulate at all) it uses a
* simple fan triangulation
*
* newfaces, if non-null, must be an array of BMFace pointers,
* with a length equal to f->len. it will be filled with the new
* triangles, and will be NULL-terminated.
*/
void BM_face_triangulate(BMesh *bm, BMFace *f, float (*projectverts)[3],
const short newedge_oflag, const short newface_oflag, BMFace **newfaces)
{
int i, done, nvert, nf_i = 0;
BMLoop *newl, *nextloop;
BMLoop *l_iter;
BMLoop *l_first;
/* BMVert *v; */ /* UNUSED */
/* copy vertex coordinates to vertspace arra */
i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
copy_v3_v3(projectverts[i], l_iter->v->co);
BM_elem_index_set(l_iter->v, i); /* set dirty! */
i++;
} while ((l_iter = l_iter->next) != l_first);
bm->elem_index_dirty |= BM_VERT; /* see above */
///bmesh_update_face_normal(bm, f, f->no, projectverts);
compute_poly_normal(f->no, projectverts, f->len);
poly_rotate_plane(f->no, projectverts, i);
nvert = f->len;
//compute_poly_plane(projectverts, i);
for (i = 0; i < nvert; i++) {
projectverts[i][2] = 0.0f;
}
done = 0;
while (!done && f->len > 3) {
done = 1;
l_iter = find_ear(bm, f, projectverts, nvert);
if (l_iter) {
done = 0;
/* v = l->v; */ /* UNUSED */
f = BM_face_split(bm, l_iter->f, l_iter->prev->v,
l_iter->next->v,
&newl, NULL);
copy_v3_v3(f->no, l_iter->f->no);
if (!f) {
fprintf(stderr, "%s: triangulator failed to split face! (bmesh internal error)\n", __func__);
break;
}
BMO_elem_flag_enable(bm, newl->e, newedge_oflag);
BMO_elem_flag_enable(bm, f, newface_oflag);
if (newfaces) newfaces[nf_i++] = f;
/* l = f->loopbase;
do {
if (l->v == v) {
f->loopbase = l;
break;
}
l = l->next;
} while (l != f->loopbase); */
}
}
if (f->len > 3) {
l_iter = BM_FACE_FIRST_LOOP(f);
while (l_iter->f->len > 3) {
nextloop = l_iter->next->next;
f = BM_face_split(bm, l_iter->f, l_iter->v, nextloop->v,
&newl, NULL);
if (!f) {
printf("triangle fan step of triangulator failed.\n");
/* NULL-terminat */
if (newfaces) newfaces[nf_i] = NULL;
return;
}
if (newfaces) newfaces[nf_i++] = f;
BMO_elem_flag_enable(bm, newl->e, newedge_oflag);
BMO_elem_flag_enable(bm, f, newface_oflag);
l_iter = nextloop;
}
}
/* NULL-terminat */
if (newfaces) newfaces[nf_i] = NULL;
}
/* 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, or it intersects another split. in the case of
* intersecting splits, only the first of the set of intersecting
* splits survives */
void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len)
{
BMIter iter;
BMLoop *l;
float v1[3], v2[3], v3[3]/*, v4[3 */, no[3], mid[3], *p1, *p2, *p3, *p4;
float out[3] = {-234324.0f, -234324.0f, 0.0f};
float (*projverts)[3];
float (*edgeverts)[3];
float fac1 = 1.0000001f, fac2 = 0.9f; //9999f; //0.999f;
int i, j, a = 0, clen;
BLI_array_fixedstack_declare(projverts, BM_NGON_STACK_SIZE, f->len, "projvertsb");
BLI_array_fixedstack_declare(edgeverts, BM_NGON_STACK_SIZE * 2, len * 2, "edgevertsb");
i = 0;
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
for ( ; l; l = BM_iter_step(&iter)) {
BM_elem_index_set(l, i); /* set_loop */
copy_v3_v3(projverts[i], l->v->co);
i++;
}
for (i = 0; i < len; i++) {
copy_v3_v3(v1, loops[i][0]->v->co);
copy_v3_v3(v2, loops[i][1]->v->co);
shrink_edgef(v1, v2, fac2);
copy_v3_v3(edgeverts[a], v1);
a++;
copy_v3_v3(edgeverts[a], v2);
a++;
}
compute_poly_normal(no, projverts, f->len);
poly_rotate_plane(no, projverts, f->len);
poly_rotate_plane(no, edgeverts, len * 2);
for (i = 0, l = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l = l->next) {
p1 = projverts[i];
out[0] = MAX2(out[0], p1[0]) + 0.01f;
out[1] = MAX2(out[1], p1[1]) + 0.01f;
out[2] = 0.0f;
p1[2] = 0.0f;
//copy_v3_v3(l->v->co, p1);
}
for (i = 0; i < len; i++) {
edgeverts[i * 2][2] = 0.0f;
edgeverts[i * 2 + 1][2] = 0.0f;
}
/* do convexity test */
for (i = 0; i < len; i++) {
copy_v3_v3(v2, edgeverts[i * 2]);
copy_v3_v3(v3, edgeverts[i * 2 + 1]);
mid_v3_v3v3(mid, v2, v3);
clen = 0;
for (j = 0; j < f->len; j++) {
p1 = projverts[j];
p2 = projverts[(j + 1) % f->len];
copy_v3_v3(v1, p1);
copy_v3_v3(v2, p2);
shrink_edgef(v1, v2, fac1);
if (linecrossesf(p1, p2, mid, out)) clen++;
}
if (clen % 2 == 0) {
loops[i][0] = NULL;
}
}
/* do line crossing test */
for (i = 0; i < f->len; i++) {
p1 = projverts[i];
p2 = projverts[(i + 1) % f->len];
copy_v3_v3(v1, p1);
copy_v3_v3(v2, p2);
shrink_edgef(v1, v2, fac1);
for (j = 0; j < len; j++) {
if (!loops[j][0]) continue;
p3 = edgeverts[j * 2];
p4 = edgeverts[j * 2 + 1];
if (linecrossesf(v1, v2, p3, p4)) {
loops[j][0] = NULL;
}
}
}
for (i = 0; i < len; i++) {
for (j = 0; j < len; j++) {
if (j == i) continue;
if (!loops[i][0]) continue;
if (!loops[j][0]) continue;
p1 = edgeverts[i * 2];
p2 = edgeverts[i * 2 + 1];
p3 = edgeverts[j * 2];
p4 = edgeverts[j * 2 + 1];
copy_v3_v3(v1, p1);
copy_v3_v3(v2, p2);
shrink_edgef(v1, v2, fac1);
if (linecrossesf(v1, v2, p3, p4)) {
loops[i][0] = NULL;
}
}
}
BLI_array_fixedstack_free(projverts);
BLI_array_fixedstack_free(edgeverts);
}

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

View File

@@ -0,0 +1,1103 @@
/*
* ***** 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_structure.c
* \ingroup bmesh
*
* Low level routines for manipulating the BM structure.
*/
#include "bmesh.h"
#include "bmesh_private.h"
/**
* MISC utility functions.
*
*/
int bmesh_vert_in_edge(BMEdge *e, BMVert *v)
{
if (e->v1 == v || e->v2 == v) return TRUE;
return FALSE;
}
int bmesh_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e)
{
if (e->v1 == v1 && e->v2 == v2) return TRUE;
else if (e->v1 == v2 && e->v2 == v1) return TRUE;
return FALSE;
}
BMVert *bmesh_edge_getothervert(BMEdge *e, BMVert *v) {
if (e->v1 == v) {
return e->v2;
}
else if (e->v2 == v) {
return e->v1;
}
return NULL;
}
int bmesh_edge_swapverts(BMEdge *e, BMVert *orig, BMVert *newv)
{
if (e->v1 == orig) {
e->v1 = newv;
e->v1_disk_link.next = e->v1_disk_link.prev = NULL;
return TRUE;
}
else if (e->v2 == orig) {
e->v2 = newv;
e->v2_disk_link.next = e->v2_disk_link.prev = NULL;
return TRUE;
}
return FALSE;
}
/**
* BMESH CYCLES
* (this is somewhat outdate, though bits of its API are still used) - joeedh
*
* Cycles are circular doubly linked lists that form the basis of adjacency
* information in the BME modeller. Full adjacency relations can be derived
* from examining these cycles very quickly. Although each cycle is a double
* circular linked list, each one is considered to have a 'base' or 'head',
* and care must be taken by Euler code when modifying the contents of a cycle.
*
* The contents of this file are split into two parts. First there are the
* bmesh_cycle family of functions which are generic circular double linked list
* procedures. The second part contains higher level procedures for supporting
* modification of specific cycle types.
*
* The three cycles explicitly stored in the BM data structure are as follows:
*
* 1: The Disk Cycle - A circle of edges around a vertex
* Base: vertex->edge pointer.
*
* This cycle is the most complicated in terms of its structure. Each bmesh_Edge contains
* two bmesh_CycleNode structures to keep track of that edge's membership in the disk cycle
* of each of its vertices. However for any given vertex it may be the first in some edges
* in its disk cycle and the second for others. The bmesh_disk_XXX family of functions contain
* some nice utilities for navigating disk cycles in a way that hides this detail from the
* tool writer.
*
* Note that the disk cycle is completley independant from face data. One advantage of this
* is that wire edges are fully integrated into the topology database. Another is that the
* the disk cycle has no problems dealing with non-manifold conditions involving faces.
*
* Functions relating to this cycle:
*
* bmesh_disk_append_edge
* bmesh_disk_remove_edge
* bmesh_disk_nextedge
* bmesh_disk_getpointer
*
* 2: The Radial Cycle - A circle of face edges (bmesh_Loop) around an edge
* Base: edge->l->radial structure.
*
* The radial cycle is similar to the radial cycle in the radial edge data structure.*
* Unlike the radial edge however, the radial cycle does not require a large amount of memory
* to store non-manifold conditions since BM does not keep track of region/shell
* information.
*
* Functions relating to this cycle:
*
* bmesh_radial_append
* bmesh_radial_remove_loop
* bmesh_radial_nextloop
* bmesh_radial_find_face
*
*
* 3: The Loop Cycle - A circle of face edges around a polygon.
* Base: polygon->lbase.
*
* The loop cycle keeps track of a faces vertices and edges. It should be noted that the
* direction of a loop cycle is either CW or CCW depending on the face normal, and is
* not oriented to the faces editedges.
*
* Functions relating to this cycle:
*
* bmesh_cycle_XXX family of functions.
*
*
* Note that the order of elements in all cycles except the loop cycle is undefined. This
* leads to slightly increased seek time for deriving some adjacency relations, however the
* advantage is that no intrinsic properties of the data structures are dependant upon the
* cycle order and all non-manifold conditions are represented trivially.
*
*/
int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v)
{
if (!v->e) {
BMDiskLink *dl1 = BM_EDGE_DISK_LINK_GET(e, v);
v->e = e;
dl1->next = dl1->prev = e;
}
else {
BMDiskLink *dl1, *dl2, *dl3;
dl1 = BM_EDGE_DISK_LINK_GET(e, v);
dl2 = BM_EDGE_DISK_LINK_GET(v->e, v);
dl3 = dl2->prev ? BM_EDGE_DISK_LINK_GET(dl2->prev, v) : NULL;
dl1->next = v->e;
dl1->prev = dl2->prev;
dl2->prev = e;
if (dl3)
dl3->next = e;
}
return TRUE;
}
void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v)
{
BMDiskLink *dl1, *dl2;
dl1 = BM_EDGE_DISK_LINK_GET(e, v);
if (dl1->prev) {
dl2 = BM_EDGE_DISK_LINK_GET(dl1->prev, v);
dl2->next = dl1->next;
}
if (dl1->next) {
dl2 = BM_EDGE_DISK_LINK_GET(dl1->next, v);
dl2->prev = dl1->prev;
}
if (v->e == e)
v->e = (e != (BMEdge *)dl1->next) ? (BMEdge *)dl1->next : NULL;
dl1->next = dl1->prev = NULL;
}
struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v)
{
if (v == e->v1)
return e->v1_disk_link.next;
if (v == e->v2)
return e->v2_disk_link.next;
return NULL;
}
static BMEdge *bmesh_disk_prevedge(BMEdge *e, BMVert *v)
{
if (v == e->v1)
return e->v1_disk_link.prev;
if (v == e->v2)
return e->v2_disk_link.prev;
return NULL;
}
BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2)
{
BMEdge *curedge, *startedge;
if (v1->e) {
startedge = v1->e;
curedge = startedge;
do {
if (bmesh_verts_in_edge(v1, v2, curedge)) {
return curedge;
}
curedge = bmesh_disk_nextedge(curedge, v1);
} while (curedge != startedge);
}
return NULL;
}
int bmesh_disk_count(struct BMVert *v)
{
BMEdge *e = v->e;
int i = 0;
if (!e) {
return 0;
}
do {
if (!e) {
return 0;
}
e = bmesh_disk_nextedge(e, v);
if (i >= (1 << 20)) {
printf("bmesh error: infinite loop in disk cycle!\n");
return 0;
}
i++;
} while (e != v->e);
return i;
}
int bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
{
BMEdge *e2;
if (!BM_vert_in_edge(e, v))
return FALSE;
if (bmesh_disk_count(v) != len || len == 0)
return FALSE;
e2 = e;
do {
if (len != 1 && bmesh_disk_prevedge(e2, v) == e2) {
return FALSE;
}
e2 = bmesh_disk_nextedge(e2, v);
} while (e2 != e);
return TRUE;
}
/*
* BME DISK COUNT FACE VERT
*
* Counts the number of loop users
* for this vertex. Note that this is
* equivalent to counting the number of
* faces incident upon this vertex
*/
int bmesh_disk_count_facevert(BMVert *v)
{
BMEdge *curedge;
int count = 0;
/* is there an edge on this vert at all */
if (!v->e)
return count;
/* first, loop around edge */
curedge = v->e;
do {
if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v);
curedge = bmesh_disk_nextedge(curedge, v);
} while (curedge != v->e);
return count;
}
struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = e;
do {
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
return searchedge;
}
searchedge = bmesh_disk_nextedge(searchedge, v);
} while (searchedge != e);
return NULL;
}
struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = bmesh_disk_nextedge(e, v);
do {
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
return searchedge;
}
searchedge = bmesh_disk_nextedge(searchedge, v);
} while (searchedge != e);
return e;
}
/*****radial cycle functions, e.g. loops surrounding edges**** */
int bmesh_radial_validate(int radlen, BMLoop *l)
{
BMLoop *l2 = l;
int i = 0;
if (bmesh_radial_length(l) != radlen)
return FALSE;
do {
if (!l2) {
bmesh_error();
return FALSE;
}
if (l2->e != l->e)
return FALSE;
if (l2->v != l->e->v1 && l2->v != l->e->v2)
return FALSE;
if (i > BM_LOOP_RADIAL_MAX) {
bmesh_error();
return FALSE;
}
i++;
l2 = l2->radial_next;
} while (l2 != l);
return TRUE;
}
/*
* BMESH RADIAL REMOVE LOOP
*
* Removes a loop from an radial cycle. If edge e is non-NULL
* it should contain the radial cycle, and it will also get
* updated (in the case that the edge's link into the radial
* cycle was the loop which is being removed from the cycle).
*/
void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e)
{
/* if e is non-NULL, l must be in the radial cycle of e */
if (e && e != l->e) {
bmesh_error();
}
if (l->radial_next != l) {
if (e && l == e->l)
e->l = l->radial_next;
l->radial_next->radial_prev = l->radial_prev;
l->radial_prev->radial_next = l->radial_next;
}
else {
if (e) {
if (l == e->l) {
e->l = NULL;
}
else {
bmesh_error();
}
}
}
/* l is no longer in a radial cycle; empty the links
* to the cycle and the link back to an edge */
l->radial_next = l->radial_prev = NULL;
l->e = NULL;
}
/*
* BME RADIAL FIND FIRST FACE VERT
*
* Finds the first loop of v around radial
* cycle
*/
BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = l;
do {
if (curloop->v == v) {
return curloop;
}
curloop = bmesh_radial_nextloop(curloop);
} while (curloop != l);
return NULL;
}
BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = bmesh_radial_nextloop(l);
do {
if (curloop->v == v) {
return curloop;
}
curloop = bmesh_radial_nextloop(curloop);
} while (curloop != l);
return l;
}
BMLoop *bmesh_radial_nextloop(BMLoop *l)
{
return l->radial_next;
}
int bmesh_radial_length(BMLoop *l)
{
BMLoop *l2 = l;
int i = 0;
if (!l)
return 0;
do {
if (!l2) {
/* radial cycle is broken (not a circulat loop) */
bmesh_error();
return 0;
}
i++;
l2 = l2->radial_next;
if (i >= BM_LOOP_RADIAL_MAX) {
bmesh_error();
return -1;
}
} while (l2 != l);
return i;
}
void bmesh_radial_append(BMEdge *e, BMLoop *l)
{
if (e->l == NULL) {
e->l = l;
l->radial_next = l->radial_prev = l;
}
else {
l->radial_prev = e->l;
l->radial_next = e->l->radial_next;
e->l->radial_next->radial_prev = l;
e->l->radial_next = l;
e->l = l;
}
if (l->e && l->e != e) {
/* l is already in a radial cycle for a different edge */
bmesh_error();
}
l->e = e;
}
int bmesh_radial_find_face(BMEdge *e, BMFace *f)
{
BMLoop *curloop;
int i, len;
len = bmesh_radial_length(e->l);
for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) {
if (curloop->f == f)
return TRUE;
}
return FALSE;
}
/*
* BME RADIAL COUNT FACE VERT
*
* Returns the number of times a vertex appears
* in a radial cycle
*
*/
int bmesh_radial_count_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
int count = 0;
curloop = l;
do {
if (curloop->v == v) count++;
curloop = bmesh_radial_nextloop(curloop);
} while (curloop != l);
return count;
}
/*****loop cycle functions, e.g. loops surrounding a face**** */
int bmesh_loop_validate(BMFace *f)
{
int i;
int len = f->len;
BMLoop *l_iter, *l_first;
l_first = BM_FACE_FIRST_LOOP(f);
if (l_first == NULL) {
return FALSE;
}
/* Validate that the face loop cycle is the length specified by f->len */
for (i = 1, l_iter = l_first->next; i < len; i++, l_iter = l_iter->next) {
if ( (l_iter->f != f) ||
(l_iter == l_first))
{
return FALSE;
}
}
if (l_iter != l_first) {
return FALSE;
}
/* Validate the loop->prev links also form a cycle of length f->len */
for (i = 1, l_iter = l_first->prev; i < len; i++, l_iter = l_iter->prev) {
if (l_iter == l_first) {
return FALSE;
}
}
if (l_iter != l_first) {
return FALSE;
}
return TRUE;
}
#if 0
void bmesh_cycle_append(void *h, void *nt)
{
BMNode *oldtail, *head, *newtail;
head = (BMNode *)h;
newtail = (BMNode *)nt;
if (head->next == NULL) {
head->next = newtail;
head->prev = newtail;
newtail->next = head;
newtail->prev = head;
}
else {
oldtail = head->prev;
oldtail->next = newtail;
newtail->next = head;
newtail->prev = oldtail;
head->prev = newtail;
}
}
/**
* bmesh_cycle_length
*
* Count the nodes in a cycle.
*
* Returns -
* Integer
*/
int bmesh_cycle_length(BMEdge *e, BMVert *v)
{
BMEdge *next, *prev, *cur;
int len, vi = v == e->v1 ? 0 : 1;
/* should skip 2 forward if v is 1, happily reduces to (v * 2) */
prev = *(&e->v1_prev + vi * 2);
cur = e;
len = 1;
while (cur != prev) {
vi = cur->v1 == v ? 0 : 1;
len++;
cur = *(&cur->v1_next + vi * 2);
}
return len;
}
/**
* bmesh_cycle_remove
*
* Removes a node from a cycle.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_cycle_remove(void *h, void *remn)
{
int i, len;
BMNode *head, *remnode, *curnode;
head = (BMNode *)h;
remnode = (BMNode *)remn;
len = bmesh_cycle_length(h);
if (len == 1 && head == remnode) {
head->next = NULL;
head->prev = NULL;
return TRUE;
}
else {
for (i = 0, curnode = head; i < len; curnode = curnode->next) {
if (curnode == remnode) {
remnode->prev->next = remnode->next;
remnode->next->prev = remnode->prev;
/* zero out remnode pointers, important */
//remnode->next = NULL;
//remnode->prev = NULL;
return TRUE;
}
}
}
return FALSE;
}
/**
* bmesh_cycle_validate
*
* Validates a cycle. Takes as an argument the expected length of the cycle and
* a pointer to the cycle head or base.
*
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_cycle_validate(int len, void *h)
{
int i;
BMNode *curnode, *head;
head = (BMNode *)h;
/* forward validatio */
for (i = 0, curnode = head; i < len; i++, curnode = curnode->next);
if (curnode != head) {
return FALSE;
}
/* reverse validatio */
for (i = 0, curnode = head; i < len; i++, curnode = curnode->prev);
if (curnode != head) {
return FALSE;
}
return TRUE;
}
/* Begin Disk Cycle routine */
/**
* bmesh_disk_nextedge
*
* Find the next edge in a disk cycle
*
* Returns -
* Pointer to the next edge in the disk cycle for the vertex v.
*/
BMEdge *bmesh_disk_nextedge(BMEdge *e, BMVert *v)
{
if (bmesh_vert_in_edge(e, v)) {
if (e->v1 == v) {
return e->d1.next->data;
}
else if (e->v2 == v) {
return e->d2.next->data;
}
}
return NULL;
}
/**
* bmesh_disk_getpointer
*
* Given an edge and one of its vertices, find the apporpriate CycleNode
*
* Returns -
* Pointer to bmesh_CycleNode.
*/
BMNode *bmesh_disk_getpointer(BMEdge *e, BMVert *v)
{
/* returns pointer to the cycle node for the appropriate vertex in this dis */
if (e->v1 == v) {
return &(e->d1);
}
else if (e->v2 == v) {
return &(e->d2);
}
return NULL;
}
/**
* bmesh_disk_append_edge
*
* Appends edge to the end of a vertex disk cycle.
*
* Returns -
* 1 for success, 0 for failure
*/
int bmesh_disk_append_edge(BMEdge *e, BMVert *v)
{
BMNode *base, *tail;
/* check to make sure v is in */
if (bmesh_vert_in_edge(e, v) == 0) {
return FALSE;
}
/* check for loose vert firs */
if (v->e == NULL) {
v->e = e;
base = tail = bmesh_disk_getpointer(e, v);
bmesh_cycle_append(base, tail); /* circular reference is ok */
return TRUE;
}
/* insert e at the end of disk cycle and make it the new v-> */
base = bmesh_disk_getpointer(v->e, v);
tail = bmesh_disk_getpointer(e, v);
bmesh_cycle_append(base, tail);
return TRUE;
}
/**
* bmesh_disk_remove_edge
*
* Removes an edge from a disk cycle. If the edge to be removed is
* at the base of the cycle, the next edge becomes the new base.
*
*
* Returns -
* Nothing
*/
void bmesh_disk_remove_edge(BMEdge *e, BMVert *v)
{
BMNode *base, *remnode;
BMEdge *newbase;
int len;
base = bmesh_disk_getpointer(v->e, v);
remnode = bmesh_disk_getpointer(e, v);
/* first deal with v->e pointer.. */
len = bmesh_cycle_length(base);
if (len == 1) newbase = NULL;
else if (v->e == e) newbase = base->next-> data;
else newbase = v->e;
/* remove and rebas */
bmesh_cycle_remove(base, remnode);
v->e = newbase;
}
/**
* bmesh_disk_next_edgeflag
*
* Searches the disk cycle of v, starting with e, for the
* next edge that has either eflag or tflag.
*
* bmesh_Edge pointer.
*/
BMEdge *bmesh_disk_next_edgeflag(BMEdge *e, BMVert *v, int eflag, int tflag)
{
BMNode *diskbase;
BMEdge *curedge;
int len, ok;
if (eflag && tflag) {
return NULL;
}
ok = bmesh_vert_in_edge(e, v);
if (ok) {
diskbase = bmesh_disk_getpointer(e, v);
len = bmesh_cycle_length(diskbase);
curedge = bmesh_disk_nextedge(e, v);
while (curedge != e) {
if (eflag) {
if (curedge->head.eflag1 == eflag) {
return curedge;
}
}
curedge = bmesh_disk_nextedge(curedge, v);
}
}
return NULL;
}
/**
* bmesh_disk_count_edgeflag
*
* Counts number of edges in this verts disk cycle which have
* either eflag or tflag (but not both!)
*
* Returns -
* Integer.
*/
int bmesh_disk_count_edgeflag(BMVert *v, int eflag, int tflag)
{
BMNode *diskbase;
BMEdge *curedge;
int i, len = 0, count = 0;
if (v->e) {
/* tflag and eflag are reserved for different functions */
if (eflag && tflag) {
return 0;
}
diskbase = bmesh_disk_getpointer(v->e, v);
len = bmesh_cycle_length(diskbase);
for (i = 0, curedge = v->e; i < len; i++) {
if (eflag) {
if (curedge->head.eflag1 == eflag) count++;
}
curedge = bmesh_disk_nextedge(curedge, v);
}
}
return count;
}
int bmesh_disk_hasedge(BMVert *v, BMEdge *e)
{
BMNode *diskbase;
BMEdge *curedge;
int i, len = 0;
if (v->e) {
diskbase = bmesh_disk_getpointer(v->e, v);
len = bmesh_cycle_length(diskbase);
for (i = 0, curedge = v->e; i < len; i++) {
if (curedge == e) {
return TRUE;
}
else curedge = bmesh_disk_nextedge(curedge, v);
}
}
return FALSE;
}
BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2)
{
BMNode *diskbase;
BMEdge *curedge;
int i, len = 0;
if (v1->e) {
diskbase = bmesh_disk_getpointer(v1->e, v1);
len = bmesh_cycle_length(diskbase);
for (i = 0, curedge = v1->e; i < len; i++, curedge = bmesh_disk_nextedge(curedge, v1)) {
if (bmesh_verts_in_edge(v1, v2, curedge)) {
return curedge;
}
}
}
return NULL;
}
/* end disk cycle routine */
BMLoop *bmesh_radial_nextloop(BMLoop *l)
{
return l->radial_next;
}
void bmesh_radial_append(BMEdge *e, BMLoop *l)
{
if (e->l == NULL) e->l = l;
bmesh_cycle_append(&(e->l->radial), &(l->radial));
}
void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e)
{
BMLoop *newbase;
int len;
/* deal with edge->l pointe */
len = bmesh_cycle_length(&(e->l->radial));
if (len == 1) newbase = NULL;
else if (e->l == l) newbase = e->l->radial_next;
else newbase = e->l;
/* remove and rebas */
bmesh_cycle_remove(&(e->l->radial), &(l->radial));
e->l = newbase;
}
int bmesh_radial_find_face(BMEdge *e, BMFace *f)
{
BMLoop *curloop;
int i, len;
len = bmesh_cycle_length(&(e->l->radial));
for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) {
if (curloop->f == f) {
return TRUE;
}
}
return FALSE;
}
/*
* BME RADIAL COUNT FACE VERT
*
* Returns the number of times a vertex appears
* in a radial cycle
*
*/
int bmesh_radial_count_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
int count = 0;
curloop = l;
do {
if (curloop->v == v) count++;
curloop = bmesh_radial_nextloop(curloop);
} while (curloop != l);
return count;
}
/*
* BME DISK COUNT FACE VERT
*
* Counts the number of loop users
* for this vertex. Note that this is
* equivalent to counting the number of
* faces incident upon this vertex
*
*/
int bmesh_disk_count_facevert(BMVert *v)
{
BMEdge *curedge;
int count = 0;
/* is there an edge on this vert at all */
if (!v->e)
return count;
/* first, loop around edge */
curedge = v->e;
do {
if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v);
curedge = bmesh_disk_nextedge(curedge, v);
} while (curedge != v->e);
return count;
}
/*
* BME RADIAL FIND FIRST FACE VERT
*
* Finds the first loop of v around radial
* cycle
*
*/
BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = l;
do {
if (curloop->v == v) {
return curloop;
}
curloop = bmesh_radial_nextloop(curloop);
} while (curloop != l);
return NULL;
}
BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = bmesh_radial_nextloop(l);
do {
if (curloop->v == v) {
return curloop;
}
curloop = bmesh_radial_nextloop(curloop);
} while (curloop != l);
return l;
}
/*
* BME FIND FIRST FACE EDGE
*
* Finds the first edge in a vertices
* Disk cycle that has one of this
* vert's loops attached
* to it.
*/
BMEdge *bmesh_disk_find_first_faceedge(BMEdge *e, BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = e;
do {
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
return searchedge;
}
searchedge = bmesh_disk_nextedge(searchedge, v);
} while (searchedge != e);
return NULL;
}
BMEdge *bmesh_disk_find_next_faceedge(BMEdge *e, BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = bmesh_disk_nextedge(e, v);
do {
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
return searchedge;
}
searchedge = bmesh_disk_nextedge(searchedge, v);
} while (searchedge != e);
return e;
}
struct BMLoop *bmesh_loop_find_loop(struct BMFace *f, struct BMVert *v)
{
BMLoop *l;
int i, len;
len = bmesh_cycle_length(f->lbase);
for (i = 0, l = f->loopbase; i < len; i++, l = l->next) {
if (l->v == v) {
return l;
}
}
return NULL;
}
#endif

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

View File

@@ -0,0 +1,1412 @@
/*
* ***** 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_heap.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_array.h"
#include "BLI_smallhash.h"
#include "BLI_rand.h"
#include "bmesh.h"
#define EDGE_MARK 1
#define EDGE_VIS 2
#define FACE_NEW 1
#define ELE_NEW 1
#define ELE_OUT 2
#define ELE_ORIG 4
#define FACE_IGNORE 16
typedef struct EPathNode {
struct EPathNode *next, *prev;
BMVert *v;
BMEdge *e;
BMEdge *cure;
} EPathNode;
typedef struct EPath {
ListBase nodes;
float weight;
int group;
} EPath;
typedef struct PathBase {
BLI_mempool *nodepool, *pathpool;
} PathBase;
typedef struct EdgeData {
int tag;
int ftag;
BMDiskLink v1_disk_link, v2_disk_link;
} EdgeData;
typedef struct VertData {
BMEdge *e;
float no[3], offco[3], sco[3]; /* offco is vertex coordinate slightly offset randoml */
int tag;
} VertData;
static int count_edge_faces(BMesh *bm, BMEdge *e);
/**** rotation system code * */
#define RS_GET_EDGE_LINK(e, v, e_data) ( \
(v) == ((BMEdge *)(e))->v1 ? \
&(((EdgeData *)(e_data))->v1_disk_link) : \
&(((EdgeData *)(e_data))->v2_disk_link) \
)
static int rotsys_append_edge(struct BMEdge *e, struct BMVert *v,
EdgeData *edata, VertData *vdata)
{
EdgeData *ed = &edata[BM_elem_index_get(e)];
VertData *vd = &vdata[BM_elem_index_get(v)];
if (!vd->e) {
Link *e1 = (Link *)RS_GET_EDGE_LINK(e, v, ed);
vd->e = e;
e1->next = e1->prev = (Link *)e;
}
else {
BMDiskLink *dl1, *dl2, *dl3;
EdgeData *ved = &edata[BM_elem_index_get(vd->e)];
dl1 = RS_GET_EDGE_LINK(e, v, ed);
dl2 = RS_GET_EDGE_LINK(vd->e, v, ved);
dl3 = dl2->prev ? RS_GET_EDGE_LINK(dl2->prev, v, &edata[BM_elem_index_get(dl2->prev)]) : NULL;
dl1->next = vd->e;
dl1->prev = dl2->prev;
dl2->prev = e;
if (dl3) {
dl3->next = e;
}
}
return TRUE;
}
static void UNUSED_FUNCTION(rotsys_remove_edge)(struct BMEdge *e, struct BMVert *v,
EdgeData *edata, VertData *vdata)
{
EdgeData *ed = edata + BM_elem_index_get(e);
VertData *vd = vdata + BM_elem_index_get(v);
BMDiskLink *e1, *e2;
e1 = RS_GET_EDGE_LINK(e, v, ed);
if (e1->prev) {
e2 = RS_GET_EDGE_LINK(e1->prev, v, ed);
e2->next = e1->next;
}
if (e1->next) {
e2 = RS_GET_EDGE_LINK(e1->next, v, ed);
e2->prev = e1->prev;
}
if (vd->e == e)
vd->e = (e != (BMEdge *)e1->next) ? (BMEdge *)e1->next : NULL;
e1->next = e1->prev = NULL;
}
static struct BMEdge *rotsys_nextedge(struct BMEdge *e, struct BMVert *v,
EdgeData *edata, VertData *UNUSED(vdata))
{
if (v == e->v1)
return edata[BM_elem_index_get(e)].v1_disk_link.next;
if (v == e->v2)
return edata[BM_elem_index_get(e)].v2_disk_link.next;
return NULL;
}
static BMEdge *rotsys_prevedge(BMEdge *e, BMVert *v,
EdgeData *edata, VertData *UNUSED(vdata))
{
if (v == e->v1)
return edata[BM_elem_index_get(e)].v1_disk_link.prev;
if (v == e->v2)
return edata[BM_elem_index_get(e)].v2_disk_link.prev;
return NULL;
}
static void rotsys_reverse(struct BMEdge *UNUSED(e), struct BMVert *v, EdgeData *edata, VertData *vdata)
{
BMEdge **edges = NULL;
BMEdge *e_first;
BMEdge *e;
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
int i, totedge;
e = e_first = vdata[BM_elem_index_get(v)].e;
do {
BLI_array_append(edges, e);
e = rotsys_nextedge(e, v, edata, vdata);
} while (e != e_first);
totedge = BLI_array_count(edges);
for (i = 0; i < totedge / 2; i++) {
SWAP(BMEdge *, edges[i], edges[totedge - 1 - i]);
}
vdata[BM_elem_index_get(v)].e = NULL;
for (i = 0; i < totedge; i++) {
rotsys_append_edge(edges[i], v, edata, vdata);
}
BLI_array_free(edges);
}
static int UNUSED_FUNCTION(rotsys_count)(struct BMVert *v, EdgeData *edata, VertData *vdata)
{
BMEdge *e = vdata[BM_elem_index_get(v)].e;
int i = 0;
if (!e)
return 0;
do {
if (!e)
return 0;
e = rotsys_nextedge(e, v, edata, vdata);
if (i >= (1 << 20)) {
printf("bmesh error: infinite loop in disk cycle!\n");
return 0;
}
i += 1;
} while (e != vdata[BM_elem_index_get(v)].e);
return i;
}
static int UNUSED_FUNCTION(rotsys_fill_faces)(BMesh *bm, EdgeData *edata, VertData *vdata)
{
BMIter iter;
BMEdge *e, **edges = NULL;
BLI_array_declare(edges);
BMVert *v, **verts = NULL;
BMFace *f;
BLI_array_declare(verts);
SmallHash visithash, *hash = &visithash;
int i;
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
BMEdge *e2, *starte;
BMVert *startv;
int rad, ok;
rad = count_edge_faces(bm, e);
if (rad < 2)
starte = e;
else
continue;
/* do two passes, going forward then backwar */
for (i = 0; i < 2; i++) {
BLI_smallhash_init(hash);
BLI_array_empty(verts);
BLI_array_empty(edges);
startv = v = starte->v1;
e2 = starte;
ok = 1;
if (!v || !e2)
continue;
do {
if (BLI_smallhash_haskey(hash, (intptr_t)e2) ||
BLI_smallhash_haskey(hash, (intptr_t)v))
{
ok = 0;
break;
}
BLI_array_append(verts, v);
BLI_array_append(edges, e2);
BLI_smallhash_insert(hash, (intptr_t)e2, NULL);
v = BM_edge_other_vert(e2, v);
e2 = i ? rotsys_prevedge(e2, v, edata, vdata) : rotsys_nextedge(e2, v, edata, vdata);
} while (e2 != starte && v != startv);
BLI_smallhash_release(hash);
if (!ok || BLI_array_count(edges) < 3)
continue;
f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), TRUE);
if (!f)
continue;
}
}
return 0;
}
static void rotsys_make_consistent(BMesh *bm, EdgeData *edata, VertData *vdata)
{
BMIter iter;
BMEdge *e;
BMVert *v, **stack = NULL;
BLI_array_declare(stack);
int i;
for (i = 0; i < bm->totvert; i++) {
vdata[i].tag = 0;
}
while (1) {
VertData *vd;
BMVert *startv = NULL;
float dis;
v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
for (i = 0; i < bm->totvert; i++, BM_iter_step(&iter)) {
vd = vdata + BM_elem_index_get(v);
if (vd->tag)
continue;
if (!startv || dot_v3v3(vd->offco, vd->offco) > dis) {
dis = dot_v3v3(vd->offco, vd->offco);
startv = v;
}
}
if (!startv)
break;
vd = vdata + BM_elem_index_get(startv);
BLI_array_empty(stack);
BLI_array_append(stack, startv);
vd->tag = 1;
while (BLI_array_count(stack)) {
v = BLI_array_pop(stack);
vd = vdata + BM_elem_index_get(v);
if (!vd->e)
continue;
e = vd->e;
do {
BMVert *v2 = BM_edge_other_vert(e, v);
VertData *vd2 = vdata + BM_elem_index_get(v2);
if (dot_v3v3(vd->no, vd2->no) < 0.0f + FLT_EPSILON * 2) {
rotsys_reverse(e, v2, edata, vdata);
mul_v3_fl(vd2->no, -1.0f);
}
if (!vd2->tag) {
BLI_array_append(stack, v2);
vd2->tag = 1;
}
e = rotsys_nextedge(e, v, edata, vdata);
} while (e != vd->e);
}
}
BLI_array_free(stack);
}
static void init_rotsys(BMesh *bm, EdgeData *edata, VertData *vdata)
{
BMIter iter;
BMEdge *e;
BMEdge **edges = NULL;
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
BMVert *v;
/* BMVert **verts = NULL; */
/* BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); */ /* UNUSE */
int i;
#define SIGN(n) ((n)<0.0f)
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BMIter eiter;
float no[3], cent[3];
int j, k = 0, totedge = 0;
if (BM_elem_index_get(v) == -1)
continue;
BLI_array_empty(edges);
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
BLI_array_append(edges, e);
totedge++;
}
}
copy_v3_v3(cent, v->co);
zero_v3(no);
for (i = 0; i < totedge; i++) {
BMEdge *e1, *e2;
float cno[3], vec1[3], vec2[3];
e1 = edges[i];
e2 = edges[(i + 1) % totedge];
sub_v3_v3v3(vec1, (BM_edge_other_vert(e1, v))->co, v->co);
sub_v3_v3v3(vec2, (BM_edge_other_vert(e2, v))->co, v->co);
cross_v3_v3v3(cno, vec1, vec2);
normalize_v3(cno);
if (i && dot_v3v3(cno, no) < 0.0f + FLT_EPSILON * 10)
mul_v3_fl(cno, -1.0f);
add_v3_v3(no, cno);
normalize_v3(no);
}
/* generate plane-flattened coordinate */
for (i = 0; i < totedge; i++) {
BMEdge *e1;
BMVert *v2;
float cvec[3], vec1[3];
e1 = edges[i];
v2 = BM_edge_other_vert(e1, v);
sub_v3_v3v3(vec1, v2->co, v->co);
cross_v3_v3v3(cvec, vec1, no);
cross_v3_v3v3(vec1, cvec, no);
normalize_v3(vec1);
mul_v3_fl(vec1, len_v3v3(v2->co, v->co));
add_v3_v3(vec1, v->co);
copy_v3_v3(vdata[BM_elem_index_get(v2)].sco, vec1);
}
BLI_srandom(0);
/* first, ensure no 0 or 180 angles between adjacent
* (and that adjacent's adjacent) edges */
for (i = 0, k = 0; i < totedge; i++) {
BMEdge *e1, *e2, *e3 = NULL;
BMVert *v1, *v2, *v3;
VertData *vd1, *vd2, *vd3;
float vec1[3], vec2[3], vec3[3], size;
int s1, s2, s3;
if (totedge < 3)
continue;
e1 = edges[(i + totedge - 1) % totedge];
e2 = edges[i];
e3 = edges[(i + 1) % totedge];
v1 = BM_edge_other_vert(e1, v);
v2 = BM_edge_other_vert(e2, v);
v3 = BM_edge_other_vert(e3, v);
vd1 = vdata + BM_elem_index_get(v1);
vd2 = vdata + BM_elem_index_get(v2);
vd3 = vdata + BM_elem_index_get(v3);
sub_v3_v3v3(vec1, vd1->sco, cent);
sub_v3_v3v3(vec2, vd2->sco, cent);
sub_v3_v3v3(vec3, vd3->sco, cent);
size = (len_v3(vec1) + len_v3(vec3)) * 0.01f;
normalize_v3(vec1); normalize_v3(vec2); normalize_v3(vec3);
#ifdef STRAIGHT
#undef STRAIGHT
#endif
#define STRAIGHT(vec11, vec22) (fabsf(dot_v3v3((vec11), (vec22))) > 1.0f - ((float)FLT_EPSILON * 1000.0f))
s1 = STRAIGHT(vec1, vec2); s2 = STRAIGHT(vec2, vec3); s3 = STRAIGHT(vec1, vec3);
if (s1 || s2 || s3) {
copy_v3_v3(cent, v->co);
for (j = 0; j < 3; j++) {
float fac = (BLI_frand() - 0.5f)*size;
cent[j] += fac;
}
if (k < 2000) {
i = 0;
k++;
continue;
}
else {
k++;
continue;
}
}
}
copy_v3_v3(vdata[BM_elem_index_get(v)].offco, cent);
//copy_v3_v3(v->co, cent);
/* now, sort edges so the triangle fan of all edges
* has a consistent normal. this is the same as
* sorting by polar coordinates along a group normal */
for (j = 0; j < totedge; j++) {
for (i = 0; i < totedge; i++) {
BMEdge *e1, *e2, *e3 = NULL;
BMVert *v1, *v2, *v3;
VertData *vd1, *vd2, *vd3;
float vec1[3], vec2[3], vec3[3], n1[3], n2[3], n3[3];
int s1, s2, s3;
e1 = edges[(i + totedge - 1) % totedge];
e2 = edges[i];
e3 = edges[(i + 1) % totedge];
v1 = BM_edge_other_vert(e1, v);
v2 = BM_edge_other_vert(e2, v);
v3 = BM_edge_other_vert(e3, v);
vd1 = vdata + BM_elem_index_get(v1);
vd2 = vdata + BM_elem_index_get(v2);
vd3 = vdata + BM_elem_index_get(v3);
sub_v3_v3v3(vec1, vd1->sco, cent);
sub_v3_v3v3(vec2, vd2->sco, cent);
sub_v3_v3v3(vec3, vd3->sco, cent);
cross_v3_v3v3(n1, vec1, vec2);
cross_v3_v3v3(n2, vec2, vec3);
cross_v3_v3v3(n3, vec1, vec3);
/* Other way to determine if two vectors approach are (nearly) parallel: the
* cross product of the two vectors will approach zero */
s1 = (dot_v3v3(n1, n1) < (0.0f + FLT_EPSILON * 10));
s2 = (dot_v3v3(n2, n2) < (0.0f + FLT_EPSILON * 10));
s3 = (totedge < 3) ? 0 : (dot_v3v3(n3, n3) < (0.0f + FLT_EPSILON * 10));
normalize_v3(n1); normalize_v3(n2); normalize_v3(n3);
if (s1 || s2 || s3) {
fprintf(stderr, "%s: s1: %d, s2: %d, s3: %dx (bmesh internal error)\n", __func__, s1, s2, s3);
}
if (dot_v3v3(n1, n2) < 0.0f) {
if (dot_v3v3(n1, n3) >= 0.0f + FLT_EPSILON * 10) {
SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
}
else {
SWAP(BMEdge *, edges[(i + totedge - 1) % totedge], edges[(i + 1) % totedge]);
SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
}
}
}
}
#undef STRAIGHT
zero_v3(no);
/* yay, edges are sorted */
for (i = 0; i < totedge; i++) {
BMEdge *e1 = edges[i], *e2 = edges[(i + 1) % totedge];
float eno[3];
normal_tri_v3(eno, BM_edge_other_vert(e1, v)->co, v->co, BM_edge_other_vert(e2, v)->co);
add_v3_v3(no, eno);
rotsys_append_edge(edges[i], v, edata, vdata);
}
normalize_v3(no);
copy_v3_v3(vdata[BM_elem_index_get(v)].no, no);
}
/* now, make sure rotation system is topologically consistent
* (e.g. vert normals consistently point either inside or outside) */
rotsys_make_consistent(bm, edata, vdata);
//rotsys_fill_faces(bm, edata, vdata);
#if 0
/* create visualizing geometr */
BMVert *lastv;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BMVert *v2;
BMFace *f;
int totedge = BM_vert_edge_count(v);
if (BM_elem_index_get(v) == -1)
continue;
//cv = BM_vert_create(bm, cent, v);
//BM_elem_index_set(cv, -1); /* set_dirty! */
i = 0;
e = vdata[BM_elem_index_get(v)].e;
lastv = NULL;
do {
BMEdge *e2;
BMVert *v2;
float f = ((float)i / (float)totedge) * 0.35 + 0.05;
float co[3];
if (!e)
break;
if (!BM_edge_other_vert(e, v))
continue;
sub_v3_v3v3(co, (BM_edge_other_vert(e, v))->co, vdata[BM_elem_index_get(v)].offco);
mul_v3_fl(co, f);
add_v3_v3(co, vdata[BM_elem_index_get(v)].offco);
v2 = BM_vert_create(bm, co, NULL);
BM_elem_index_set(v2, -1); /* set_dirty! */
//BM_edge_create(bm, cv, v2, NULL, FALSE);
BM_elem_select_set(bm, v2, TRUE);
if (lastv) {
e2 = BM_edge_create(bm, lastv, v2, NULL, FALSE);
BM_elem_select_set(bm, e2, TRUE);
}
lastv = v2;
e = rotsys_nextedge(e, v, edata, vdata);
i++;
} while (e != vdata[BM_elem_index_get(v)].e);
}
#endif
BLI_array_free(edges);
}
static PathBase *edge_pathbase_new(void)
{
PathBase *pb = MEM_callocN(sizeof(PathBase), "PathBase");
pb->nodepool = BLI_mempool_create(sizeof(EPathNode), 1, 512, TRUE, FALSE);
pb->pathpool = BLI_mempool_create(sizeof(EPath), 1, 512, TRUE, FALSE);
return pb;
}
static void edge_pathbase_free(PathBase *pathbase)
{
BLI_mempool_destroy(pathbase->nodepool);
BLI_mempool_destroy(pathbase->pathpool);
MEM_freeN(pathbase);
}
static EPath *edge_copy_add_path(PathBase *pb, EPath *path, BMVert *appendv, BMEdge *e)
{
EPath *path2;
EPathNode *node, *node2;
path2 = BLI_mempool_alloc(pb->pathpool);
path2->nodes.first = path2->nodes.last = NULL;
path2->weight = 0.0f;
path2->group = path->group;
for (node = path->nodes.first; node; node = node->next) {
node2 = BLI_mempool_alloc(pb->nodepool);
*node2 = *node;
BLI_addtail(&path2->nodes, node2);
}
node2 = BLI_mempool_alloc(pb->nodepool);
node2->v = appendv;
node2->e = e;
node2->cure = NULL;
BLI_addtail(&path2->nodes, node2);
return path2;
}
static EPath *edge_path_new(PathBase *pb, BMVert *start, BMEdge *starte)
{
EPath *path;
EPathNode *node;
path = BLI_mempool_alloc(pb->pathpool);
node = BLI_mempool_alloc(pb->nodepool);
path->nodes.first = path->nodes.last = NULL;
node->v = start;
node->e = starte;
node->cure = NULL;
BLI_addtail(&path->nodes, node);
path->weight = 0.0f;
return path;
}
static float edge_weight_path(EPath *path, EdgeData *edata, VertData *UNUSED(vdata))
{
EPathNode *node, *first = path->nodes.first;
float w = 0.0;
for (node = path->nodes.first; node; node = node->next) {
if (node->e && node != path->nodes.first) {
w += edata[BM_elem_index_get(node->e)].ftag;
if (node->prev) {
/* BMESH_TOD */
(void)first;
//w += len_v3v3(node->v->co, first->e->v1->co) * 0.0001f;
//w += len_v3v3(node->v->co, first->e->v2->co) * 0.0001f;
}
}
w += 1.0f;
}
return w;
}
static void edge_free_path(PathBase *pathbase, EPath *path)
{
EPathNode *node, *next;
for (node = path->nodes.first; node; node = next) {
next = node->next;
BLI_mempool_free(pathbase->nodepool, node);
}
BLI_mempool_free(pathbase->pathpool, path);
}
static EPath *edge_find_shortest_path(BMesh *bm, BMOperator *op, BMEdge *edge, EdgeData *edata,
VertData *vdata, PathBase *pathbase, int group)
{
BMEdge *e;
GHash *gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "createops find shortest path");
BMVert *v1, *v2;
BMVert **verts = NULL;
BLI_array_staticdeclare(verts, 1024);
Heap *heap = BLI_heap_new();
EPath *path = NULL, *path2;
BMVert *startv;
BMVert *endv;
EPathNode *node;
int i, use_restrict = BMO_slot_int_get(op, "use_restrict");
startv = edata[BM_elem_index_get(edge)].ftag ? edge->v2 : edge->v1;
endv = edata[BM_elem_index_get(edge)].ftag ? edge->v1 : edge->v2;
path = edge_path_new(pathbase, startv, edge);
BLI_ghash_insert(gh, startv, NULL);
BLI_heap_insert(heap, path->weight, path);
path->group = group;
while (BLI_heap_size(heap)) {
VertData *vd;
EPathNode *last;
BMFace *f = NULL;
path = BLI_heap_popmin(heap);
last = path->nodes.last;
v1 = last->v;
if (v1 == endv) {
/* make sure this path loop doesn't already exist */
i = 0;
BLI_array_empty(verts);
for (i = 0, node = path->nodes.first; node; node = node->next, i++) {
BLI_array_growone(verts);
verts[i] = node->v;
}
if (BM_face_exists(bm, verts, i, &f)) {
if (!BMO_elem_flag_test(bm, f, FACE_IGNORE)) {
BLI_ghash_remove(gh, endv, NULL, NULL);
continue;
}
}
break;
}
vd = vdata + BM_elem_index_get(v1);
if (!vd->e)
continue;
v2 = NULL;
while (1) {
if (!last->cure) {
last->cure = e = vdata[BM_elem_index_get(last->v)].e;
}
else {
last->cure = e = rotsys_nextedge(last->cure, last->v, edata, vdata);
if (last->cure == vdata[BM_elem_index_get(last->v)].e) {
v2 = NULL;
break;
}
}
if (e == edge || !BMO_elem_flag_test(bm, e, EDGE_MARK)) {
continue;
}
v2 = BM_edge_other_vert(e, last->v);
if (BLI_ghash_haskey(gh, v2)) {
v2 = NULL;
continue;
}
if (use_restrict && BMO_slot_map_contains(bm, op, "restrict", e)) {
int group = BMO_slot_map_int_get(bm, op, "restrict", e);
if (!(group & path->group)) {
v2 = NULL;
continue;
}
}
break;
}
if (!v2) {
if (path) {
edge_free_path(pathbase, path);
path = NULL;
}
continue;
}
/* add path back into hea */
BLI_heap_insert(heap, path->weight, path);
/* put v2 in gh ma */
BLI_ghash_insert(gh, v2, NULL);
path2 = edge_copy_add_path(pathbase, path, v2, e);
path2->weight = edge_weight_path(path2, edata, vdata);
BLI_heap_insert(heap, path2->weight, path2);
}
if (path && ((EPathNode *)path->nodes.last)->v != endv) {
edge_free_path(pathbase, path);
path = NULL;
}
BLI_array_free(verts);
BLI_heap_free(heap, NULL);
BLI_ghash_free(gh, NULL, NULL);
return path;
}
static int count_edge_faces(BMesh *bm, BMEdge *e)
{
int i = 0;
BMLoop *l = e->l;
if (!l) {
return 0;
}
do {
if (!BMO_elem_flag_test(bm, l->f, FACE_IGNORE)) {
i++;
}
l = l->radial_next;
} while (l != e->l);
return i;
}
void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op)
{
BMIter iter;
BMOIter siter;
BMFace *f;
BMEdge *e, *edge;
BMVert **verts = NULL;
BLI_array_declare(verts);
EPath *path;
EPathNode *node;
EdgeData *edata;
VertData *vdata;
BMEdge **edges = NULL;
PathBase *pathbase = edge_pathbase_new();
BLI_array_declare(edges);
int use_restrict = BMO_slot_int_get(op, "use_restrict");
int i, j, group = 0;
unsigned int winding[2]; /* accumulte winding directions for each edge which has a face */
if (!bm->totvert || !bm->totedge)
return;
edata = MEM_callocN(sizeof(EdgeData)*bm->totedge, "EdgeData");
vdata = MEM_callocN(sizeof(VertData)*bm->totvert, "VertData");
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
BMO_slot_buffer_flag_enable(bm, op, "excludefaces", FACE_IGNORE, BM_FACE);
BM_mesh_elem_index_ensure(bm, BM_VERT);
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
BMO_elem_flag_enable(bm, f, ELE_ORIG);
}
i = 0;
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
BM_elem_index_set(e, i); /* set_inline */
if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
edata[i].tag = 2;
}
i++;
}
bm->elem_index_dirty &= ~BM_EDGE;
init_rotsys(bm, edata, vdata);
while (1) {
edge = NULL;
group = 0;
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
/* if restrict is on, only start on faces in the restrict map */
if (use_restrict && !BMO_slot_map_contains(bm, op, "restrict", e))
continue;
if (edata[BM_elem_index_get(e)].tag < 2) {
edge = e;
if (use_restrict) {
int i = 0, j = 0, gi = 0;
group = BMO_slot_map_int_get(bm, op, "restrict", e);
for (i = 0; i < 30; i++) {
if (group & (1 << i)) {
j++;
gi = i;
if (j - 1 == edata[BM_elem_index_get(e)].tag) {
break;
}
}
}
group = (1 << gi);
}
break;
}
}
if (!edge)
break;
edata[BM_elem_index_get(edge)].tag += 1;
path = edge_find_shortest_path(bm, op, edge, edata, vdata, pathbase, group);
if (!path)
continue;
winding[0] = winding[1] = 0;
BLI_array_empty(edges);
BLI_array_empty(verts);
i = 0;
for (node = path->nodes.first; node; node = node->next) {
if (!node->next)
continue;
e = BM_edge_exists(node->v, node->next->v);
/* this should never happe */
if (!e)
break;
/* check on the winding */
if (e->l) {
BMVert *test_v1, *test_v2;
/* we want to use the reverse winding to the existing order */
BM_edge_ordered_verts(edge, &test_v2, &test_v1);
/* edges vote on which winding wins out */
winding[(test_v1 == node->v)]++;
}
edata[BM_elem_index_get(e)].ftag++;
BLI_array_growone(edges);
edges[i++] = e;
BLI_array_append(verts, node->v);
}
BLI_array_growone(edges);
edges[i++] = edge;
edata[BM_elem_index_get(edge)].ftag++;
for (j = 0; j < i; j++) {
if (count_edge_faces(bm, edges[j]) >= 2) {
edge_free_path(pathbase, path);
break;
}
}
if (j != i) {
continue;
}
if (i) {
BMVert *v1, *v2;
/* to define the winding order must select first edge,
* otherwise we could leave this as-is */
edge = edges[0];
/* if these are even it doesnt really matter what to do,
* with consistent geometry one will be zero, the choice is clear */
if (winding[0] > winding[1]) {
v1 = verts[0];
v2 = verts[1];
}
else {
v1 = verts[1];
v2 = verts[0];
}
f = BM_face_create_ngon(bm, v1, v2, edges, i, TRUE);
if (f && !BMO_elem_flag_test(bm, f, ELE_ORIG)) {
BMO_elem_flag_enable(bm, f, FACE_NEW);
}
if (use_restrict)
BMO_slot_map_int_insert(bm, op, "faceout_groupmap", f, path->group);
}
edge_free_path(pathbase, path);
}
BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE);
BLI_array_free(edges);
BLI_array_free(verts);
edge_pathbase_free(pathbase);
MEM_freeN(edata);
MEM_freeN(vdata);
}
static BMEdge *edge_next(BMesh *bm, BMEdge *e)
{
BMIter iter;
BMEdge *e2;
int i;
for (i = 0; i < 2; i++) {
BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) {
if ( (BMO_elem_flag_test(bm, e2, EDGE_MARK)) &&
(!BMO_elem_flag_test(bm, e2, EDGE_VIS)) &&
(e2 != e))
{
return e2;
}
}
}
return NULL;
}
void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMEdge *e;
BMEdge **edges1 = NULL, **edges2 = NULL, **edges;
BLI_array_declare(edges1);
BLI_array_declare(edges2);
BLI_array_declare(edges);
int ok = 1;
int i, count;
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
/* validate that each edge has at most one other tagged edge in the
* disk cycle around each of it's vertices */
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
for (i = 0; i < 2; i++) {
count = BMO_vert_edge_flags_count(bm, i ? e->v2 : e->v1, EDGE_MARK);
if (count > 2) {
ok = 0;
break;
}
}
if (!ok) {
break;
}
}
/* we don't have valid edge layouts, retur */
if (!ok) {
return;
}
/* find connected loops within the input edge */
count = 0;
while (1) {
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
if (!BMO_elem_flag_test(bm, e, EDGE_VIS)) {
if ( BMO_vert_edge_flags_count(bm, e->v1, EDGE_MARK) == 1 ||
BMO_vert_edge_flags_count(bm, e->v2, EDGE_MARK) == 1)
{
break;
}
}
}
if (!e) {
break;
}
if (!count) {
edges = edges1;
}
else if (count == 1) {
edges = edges2;
}
else {
break;
}
i = 0;
while (e) {
BMO_elem_flag_enable(bm, e, EDGE_VIS);
BLI_array_growone(edges);
edges[i] = e;
e = edge_next(bm, e);
i++;
}
if (!count) {
edges1 = edges;
BLI_array_set_length(edges1, BLI_array_count(edges));
}
else {
edges2 = edges;
BLI_array_set_length(edges2, BLI_array_count(edges));
}
BLI_array_empty(edges);
count++;
}
if (edges1 && BLI_array_count(edges1) > 2 &&
BM_edge_share_vert(edges1[0], edges1[BLI_array_count(edges1) - 1]))
{
if (edges2 && BLI_array_count(edges2) > 2 &&
BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
{
BLI_array_free(edges1);
BLI_array_free(edges2);
return;
}
else {
edges1 = edges2;
edges2 = NULL;
}
}
if (edges2 && BLI_array_count(edges2) > 2 &&
BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
{
edges2 = NULL;
}
/* two unconnected loops, connect the */
if (edges1 && edges2) {
BMVert *v1, *v2, *v3, *v4;
if (BLI_array_count(edges1) == 1) {
v1 = edges1[0]->v1;
v2 = edges1[0]->v2;
}
else {
if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
v1 = edges1[0]->v2;
else v1 = edges1[0]->v1;
i = BLI_array_count(edges1) - 1;
if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
v2 = edges1[i]->v2;
else v2 = edges1[i]->v1;
}
if (BLI_array_count(edges2) == 1) {
v3 = edges2[0]->v1;
v4 = edges2[0]->v2;
}
else {
if (BM_vert_in_edge(edges2[1], edges2[0]->v1))
v3 = edges2[0]->v2;
else v3 = edges2[0]->v1;
i = BLI_array_count(edges2) - 1;
if (BM_vert_in_edge(edges2[i - 1], edges2[i]->v1))
v4 = edges2[i]->v2;
else v4 = edges2[i]->v1;
}
/* avoid sqrt for comparison */
if (len_squared_v3v3(v1->co, v3->co) + len_squared_v3v3(v2->co, v4->co) >
len_squared_v3v3(v1->co, v4->co) + len_squared_v3v3(v2->co, v3->co))
{
BMVert *v;
v = v3;
v3 = v4;
v4 = v;
}
e = BM_edge_create(bm, v1, v3, NULL, TRUE);
BMO_elem_flag_enable(bm, e, ELE_NEW);
e = BM_edge_create(bm, v2, v4, NULL, TRUE);
BMO_elem_flag_enable(bm, e, ELE_NEW);
}
else if (edges1) {
BMVert *v1, *v2;
if (BLI_array_count(edges1) > 1) {
if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
v1 = edges1[0]->v2;
else v1 = edges1[0]->v1;
i = BLI_array_count(edges1) - 1;
if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
v2 = edges1[i]->v2;
else v2 = edges1[i]->v1;
e = BM_edge_create(bm, v1, v2, NULL, TRUE);
BMO_elem_flag_enable(bm, e, ELE_NEW);
}
}
BMO_slot_from_flag(bm, op, "edgeout", ELE_NEW, BM_EDGE);
BLI_array_free(edges1);
BLI_array_free(edges2);
}
/* this is essentially new fke */
void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op)
{
BMOperator op2;
BMOIter oiter;
BMIter iter;
BMHeader *h;
BMVert *v, *verts[4];
BMEdge *e;
BMFace *f;
int totv = 0, tote = 0, totf = 0, amount;
/* count number of each element type we were passe */
BMO_ITER(h, &oiter, bm, op, "geom", BM_VERT|BM_EDGE|BM_FACE) {
switch (h->htype) {
case BM_VERT: totv++; break;
case BM_EDGE: tote++; break;
case BM_FACE: totf++; break;
}
BMO_elem_flag_enable(bm, (BMElemF *)h, ELE_NEW);
}
/* --- Support for Special Case ---
* where there is a contiguous edge ring with one isolated vertex.
*
* This example shows 2 edges created from 3 verts
* with 1 free standing vertex. Dotted lines denote the 2 edges that are created.
*
* note that this works for any sided shape.
*
* +--------+
* | .
* | .
* | .
* | .
* +........+ <-- starts out free standing.
*
*/
/* Here we check for consistancy and create 2 edges */
if (totf == 0 && totv >= 4 && totv == tote + 2) {
/* find a free standing vertex and 2 endpoint verts */
BMVert *v_free = NULL, *v_a = NULL, *v_b = NULL;
int ok = TRUE;
BMO_ITER(v, &oiter, bm, op, "geom", BM_VERT) {
/* count how many flagged edges this vertex uses */
int tot_edges = 0;
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
if (BMO_elem_flag_test(bm, e, ELE_NEW)) {
tot_edges++;
if (tot_edges > 2) {
break;
}
}
}
if (tot_edges == 0) {
/* only accept 1 free vert */
if (v_free == NULL) v_free = v;
else ok = FALSE; /* only ever want one of these */
}
else if (tot_edges == 1) {
if (v_a == NULL) v_a = v;
else if (v_b == NULL) v_b = v;
else ok = FALSE; /* only ever want 2 of these */
}
else if (tot_edges == 2) {
/* do nothing, regular case */
}
else {
ok = FALSE; /* if a vertex has 3+ edge users then cancel - this is only simple cases */
}
if (ok == FALSE) {
break;
}
}
if (ok == TRUE && v_free && v_a && v_b) {
e = BM_edge_create(bm, v_free, v_a, NULL, TRUE);
BMO_elem_flag_enable(bm, e, ELE_NEW);
e = BM_edge_create(bm, v_free, v_b, NULL, TRUE);
BMO_elem_flag_enable(bm, e, ELE_NEW);
}
}
/* --- end special case support, continue as normal --- */
/* possible bug?, selecting 2 triangles and pressing F will make a quad rather then joining them,
* perhaps this should be looked into? - campbell */
/* call edgenet create */
/* call edgenet prepare op so additional face creation cases wor */
BMO_op_initf(bm, &op2, "edgenet_prepare edges=%fe", ELE_NEW);
BMO_op_exec(bm, &op2);
BMO_slot_buffer_flag_enable(bm, &op2, "edgeout", ELE_NEW, BM_EDGE);
BMO_op_finish(bm, &op2);
BMO_op_initf(bm, &op2, "edgenet_fill edges=%fe", ELE_NEW);
BMO_op_exec(bm, &op2);
/* return if edge net create did somethin */
if (BMO_slot_buf_count(bm, &op2, "faceout")) {
BMO_slot_copy(&op2, op, "faceout", "faceout");
BMO_op_finish(bm, &op2);
return;
}
BMO_op_finish(bm, &op2);
/* now call dissolve face */
BMO_op_initf(bm, &op2, "dissolvefaces faces=%ff", ELE_NEW);
BMO_op_exec(bm, &op2);
/* if we dissolved anything, then return */
if (BMO_slot_buf_count(bm, &op2, "regionout")) {
BMO_slot_copy(&op2, op, "regionout", "faceout");
BMO_op_finish(bm, &op2);
return;
}
BMO_op_finish(bm, &op2);
/* now, count how many verts we hav */
amount = 0;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if (BMO_elem_flag_test(bm, v, ELE_NEW)) {
verts[amount] = v;
amount++;
if (amount > 4) break;
}
}
if (amount == 2) {
/* create edg */
e = BM_edge_create(bm, verts[0], verts[1], NULL, TRUE);
BMO_elem_flag_enable(bm, e, ELE_OUT);
}
else if (amount == 3) {
/* create triangl */
BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], NULL, NULL, TRUE);
}
else if (amount == 4) {
f = NULL;
/* the order of vertices can be anything, 6 cases to check */
if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], verts[3], NULL, TRUE);
}
else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[3]->co, verts[1]->co)) {
f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[3], verts[1], NULL, TRUE);
}
else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[1]->co, verts[3]->co)) {
f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[1], verts[3], NULL, TRUE);
}
else if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[3]->co, verts[2]->co)) {
f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[3], verts[2], NULL, TRUE);
}
else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[2]->co, verts[1]->co)) {
f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[2], verts[1], NULL, TRUE);
}
else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[1]->co, verts[2]->co)) {
f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[1], verts[2], NULL, TRUE);
}
else {
printf("cannot find nice quad from concave set of vertices\n");
}
if (f) BMO_elem_flag_enable(bm, f, ELE_OUT);
}
}

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

View File

@@ -0,0 +1,1104 @@
/*
* ***** 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_rand.h"
#include "BLI_array.h"
#include "BLI_noise.h"
#include "BKE_customdata.h"
#include "DNA_object_types.h"
#include "ED_mesh.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "bmesh_operators_private.h" /* own include */
#include "bmo_subdivide.h" /* own include */
/* flags for all elements share a common bitfield space */
#define SUBD_SPLIT 1
#define EDGE_PERCENT 2
/* I don't think new faces are flagged, currently, but
* better safe than sorry. */
#define FACE_CUSTOMFILL 4
#define ELE_INNER 8
#define ELE_SPLIT 16
/*
* NOTE: beauty has been renamed to flag!
*/
/* 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?
*/
/* connects face with smallest len, which I think should always be correct for
* edge subdivision */
static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **r_nf)
{
BMIter iter, iter2;
BMVert *v;
BMLoop *nl;
BMFace *face, *curf = NULL;
/* this isn't the best thing in the world. it doesn't handle cases where there's
* multiple faces yet. that might require a convexity test to figure out which
* face is "best," and who knows what for non-manifold conditions. */
for (face = 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) {
if (!curf || face->len < curf->len) curf = face;
}
}
}
if (curf) {
face = BM_face_split(bm, curf, v1, v2, &nl, NULL);
if (r_nf) *r_nf = face;
return nl ? nl->e : NULL;
}
return NULL;
}
/* calculates offset for co, based on fractal, sphere or smooth settings */
static void alter_co(BMesh *bm, BMVert *v, BMEdge *UNUSED(origed), const subdparams *params, float perc,
BMVert *vsta, BMVert *vend)
{
float tvec[3], prev_co[3], fac;
float *co = NULL;
int i, totlayer = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY);
BM_vert_normal_update_all(bm, v);
co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, params->origkey);
copy_v3_v3(prev_co, co);
if (params->beauty & B_SMOOTH) {
/* we calculate an offset vector vec1[], to be added to *co */
float len, nor[3], nor1[3], nor2[3], smooth = params->smooth;
sub_v3_v3v3(nor, vsta->co, vend->co);
len = 0.5f * normalize_v3(nor);
copy_v3_v3(nor1, vsta->no);
copy_v3_v3(nor2, vend->no);
/* cosine angle */
fac= dot_v3v3(nor, nor1);
mul_v3_v3fl(tvec, nor1, fac);
/* cosine angle */
fac = -dot_v3v3(nor, nor2);
madd_v3_v3fl(tvec, nor2, fac);
/* falloff for multi subdivide */
smooth *= sqrtf(fabsf(1.0f - 2.0f * fabsf(0.5f-perc)));
mul_v3_fl(tvec, smooth * len);
add_v3_v3(co, tvec);
}
else if (params->beauty & B_SPHERE) { /* subdivide sphere */
normalize_v3(co);
mul_v3_fl(co, params->smooth);
}
if (params->beauty & B_FRACTAL) {
float len = len_v3v3(vsta->co, vend->co);
float vec2[3] = {0.0f, 0.0f, 0.0f}, co2[3];
fac = params->fractal * len;
add_v3_v3(vec2, vsta->no);
add_v3_v3(vec2, vend->no);
mul_v3_fl(vec2, 0.5f);
add_v3_v3v3(co2, v->co, params->off);
tvec[0] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
tvec[1] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
tvec[2] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
mul_v3_v3(vec2, tvec);
/* add displacemen */
add_v3_v3v3(co, co, vec2);
}
/* apply the new difference to the rest of the shape keys,
* note that this doent take rotations into account, we _could_ support
* this by getting the normals and coords for each shape key and
* re-calculate the smooth value for each but this is quite involved.
* for now its ok to simply apply the difference IMHO - campbell */
sub_v3_v3v3(tvec, prev_co, co);
for (i = 0; i < totlayer; i++) {
if (params->origkey != i) {
co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, i);
sub_v3_v3(co, tvec);
}
}
}
/* assumes in the edge is the correct interpolated vertices already */
/* percent defines the interpolation, rad and flag are for special options */
/* results in new vertex with correct coordinate, vertex normal and weight group info */
static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge,
const subdparams *params, float percent,
float percent2,
BMEdge **out, BMVert *vsta, BMVert *vend)
{
BMVert *ev;
ev = BM_edge_split(bm, edge->v1, edge, out, percent);
BMO_elem_flag_enable(bm, ev, ELE_INNER);
/* offset for smooth or sphere or fractal */
alter_co(bm, ev, oedge, params, percent2, vsta, vend);
#if 0 //BMESH_TODO
/* clip if needed by mirror modifier */
if (edge->v1->f2) {
if (edge->v1->f2 & edge->v2->f2 & 1) {
co[0] = 0.0f;
}
if (edge->v1->f2 & edge->v2->f2 & 2) {
co[1] = 0.0f;
}
if (edge->v1->f2 & edge->v2->f2 & 4) {
co[2] = 0.0f;
}
}
#endif
return ev;
}
static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge, BMEdge *oedge,
int curpoint, int totpoint, const subdparams *params,
BMEdge **newe, BMVert *vsta, BMVert *vend)
{
BMVert *ev;
float percent, percent2 = 0.0f;
if (BMO_elem_flag_test(bm, edge, EDGE_PERCENT) && totpoint == 1)
percent = BMO_slot_map_float_get(bm, params->op, "edgepercents", edge);
else {
percent = 1.0f / (float)(totpoint + 1-curpoint);
percent2 = (float)(curpoint + 1) / (float)(totpoint + 1);
}
ev = bm_subdivide_edge_addvert(bm, edge, oedge, params, percent,
percent2, newe, vsta, vend);
return ev;
}
static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const subdparams *params,
BMVert *vsta, BMVert *vend)
{
BMEdge *eed = edge, *newe, temp = *edge;
BMVert *v, ov1 = *edge->v1, ov2 = *edge->v2, *v1 = edge->v1, *v2 = edge->v2;
int i, numcuts = params->numcuts;
temp.v1 = &ov1;
temp.v2 = &ov2;
for (i = 0; i < numcuts; i++) {
v = subdivideedgenum(bm, eed, &temp, i, params->numcuts, params, &newe, vsta, vend);
BMO_elem_flag_enable(bm, v, SUBD_SPLIT);
BMO_elem_flag_enable(bm, eed, SUBD_SPLIT);
BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
BMO_elem_flag_enable(bm, v, ELE_SPLIT);
BMO_elem_flag_enable(bm, eed, ELE_SPLIT);
BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
BM_CHECK_ELEMENT(bm, v);
if (v->e) BM_CHECK_ELEMENT(bm, v->e);
if (v->e && v->e->l) BM_CHECK_ELEMENT(bm, v->e->l->f);
}
alter_co(bm, v1, &temp, params, 0, &ov1, &ov2);
alter_co(bm, v2, &temp, params, 1.0, &ov1, &ov2);
}
/* note: the patterns are rotated as necassary to
* match the input geometry. they're based on the
* pre-split state of the face */
/*
* v3---------v2
* | |
* | |
* | |
* | |
* v4---v0---v1
*/
static void quad_1edge_split(BMesh *bm, BMFace *UNUSED(face),
BMVert **verts, const subdparams *params)
{
BMFace *nf;
int i, add, numcuts = params->numcuts;
/* if it's odd, the middle face is a quad, otherwise it's a triangl */
if ((numcuts % 2) == 0) {
add = 2;
for (i = 0; i < numcuts; i++) {
if (i == numcuts / 2) {
add -= 1;
}
connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
}
}
else {
add = 2;
for (i = 0; i < numcuts; i++) {
connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
if (i == numcuts/2) {
add -= 1;
connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
}
}
}
}
static SubDPattern quad_1edge = {
{1, 0, 0, 0},
quad_1edge_split,
4,
};
/*
* v6--------v5
* | |
* | |v4s
* | |v3s
* | s s |
* v7-v0--v1-v2
*/
static void quad_2edge_split_path(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
const subdparams *params)
{
BMFace *nf;
int i, numcuts = params->numcuts;
for (i = 0; i < numcuts; i++) {
connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
}
connect_smallest_face(bm, verts[numcuts * 2 + 3], verts[numcuts * 2 + 1], &nf);
}
static SubDPattern quad_2edge_path = {
{1, 1, 0, 0},
quad_2edge_split_path,
4,
};
/*
* v6--------v5
* | |
* | |v4s
* | |v3s
* | s s |
* v7-v0--v1-v2
*/
static void quad_2edge_split_innervert(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
const subdparams *params)
{
BMFace *nf;
BMVert *v, *lastv;
BMEdge *e, *ne, olde;
int i, numcuts = params->numcuts;
lastv = verts[numcuts];
for (i = numcuts - 1; i >= 0; i--) {
e = connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
olde = *e;
v = bm_subdivide_edge_addvert(bm, e, &olde, params, 0.5f, 0.5f, &ne, e->v1, e->v2);
if (i != numcuts - 1) {
connect_smallest_face(bm, lastv, v, &nf);
}
lastv = v;
}
connect_smallest_face(bm, lastv, verts[numcuts * 2 + 2], &nf);
}
static SubDPattern quad_2edge_innervert = {
{1, 1, 0, 0},
quad_2edge_split_innervert,
4,
};
/*
* v6--------v5
* | |
* | |v4s
* | |v3s
* | s s |
* v7-v0--v1-v2
*
*/
static void quad_2edge_split_fan(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
const subdparams *params)
{
BMFace *nf;
/* BMVert *v; */ /* UNUSED */
/* BMVert *lastv = verts[2]; */ /* UNUSED */
/* BMEdge *e, *ne; */ /* UNUSED */
int i, numcuts = params->numcuts;
for (i = 0; i < numcuts; i++) {
connect_smallest_face(bm, verts[i], verts[numcuts * 2 + 2], &nf);
connect_smallest_face(bm, verts[numcuts + (numcuts - i)], verts[numcuts * 2 + 2], &nf);
}
}
static SubDPattern quad_2edge_fan = {
{1, 1, 0, 0},
quad_2edge_split_fan,
4,
};
/*
* s s
* v8--v7--v6-v5
* | |
* | v4 s
* | |
* | v3 s
* | s s |
* v9-v0--v1-v2
*/
static void quad_3edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
const subdparams *params)
{
BMFace *nf;
int i, add = 0, numcuts = params->numcuts;
for (i = 0; i < numcuts; i++) {
if (i == numcuts / 2) {
if (numcuts % 2 != 0) {
connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
}
add = numcuts * 2 + 2;
}
connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
}
for (i = 0; i < numcuts / 2 + 1; i++) {
connect_smallest_face(bm, verts[i], verts[(numcuts - i) + numcuts * 2 + 1], &nf);
}
}
static SubDPattern quad_3edge = {
{1, 1, 1, 0},
quad_3edge_split,
4,
};
/*
* v8--v7-v6--v5
* | s |
* |v9 s s|v4
* first line | | last line
* |v10s s s|v3
* v11-v0--v1-v2
*
* it goes from bottom up
*/
static void quad_4edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
const subdparams *params)
{
BMFace *nf;
BMVert *v, *v1, *v2;
BMEdge *e, *ne, temp;
BMVert **lines;
int numcuts = params->numcuts;
int i, j, a, b, s = numcuts + 2 /* , totv = numcuts * 4 + 4 */;
lines = MEM_callocN(sizeof(BMVert *)*(numcuts + 2)*(numcuts + 2), "q_4edge_split");
/* build a 2-dimensional array of verts,
* containing every vert (and all new ones)
* in the face */
/* first line */
for (i = 0; i < numcuts + 2; i++) {
lines[i] = verts[numcuts * 3 + 2 + (numcuts - i + 1)];
}
/* last line */
for (i = 0; i < numcuts + 2; i++) {
lines[(s - 1) * s + i] = verts[numcuts + i];
}
/* first and last members of middle lines */
for (i = 0; i < numcuts; i++) {
a = i;
b = numcuts + 1 + numcuts + 1 + (numcuts - i - 1);
e = connect_smallest_face(bm, verts[a], verts[b], &nf);
if (!e)
continue;
BMO_elem_flag_enable(bm, e, ELE_INNER);
BMO_elem_flag_enable(bm, nf, ELE_INNER);
v1 = lines[(i + 1)*s] = verts[a];
v2 = lines[(i + 1)*s + s - 1] = verts[b];
temp = *e;
for (a = 0; a < numcuts; a++) {
v = subdivideedgenum(bm, e, &temp, a, numcuts, params, &ne,
v1, v2);
if (!v)
bmesh_error();
BMO_elem_flag_enable(bm, ne, ELE_INNER);
lines[(i + 1) * s + a + 1] = v;
}
}
for (i = 1; i < numcuts + 2; i++) {
for (j = 1; j < numcuts + 1; j++) {
a = i * s + j;
b = (i - 1) * s + j;
e = connect_smallest_face(bm, lines[a], lines[b], &nf);
if (!e)
continue;
BMO_elem_flag_enable(bm, e, ELE_INNER);
BMO_elem_flag_enable(bm, nf, ELE_INNER);
}
}
MEM_freeN(lines);
}
/*
* v3
* / \
* / \
* / \
* / \
* / \
* v4--v0--v1--v2
* s s
*/
static void tri_1edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
const subdparams *params)
{
BMFace *nf;
int i, numcuts = params->numcuts;
for (i = 0; i < numcuts; i++) {
connect_smallest_face(bm, verts[i], verts[numcuts + 1], &nf);
}
}
static SubDPattern tri_1edge = {
{1, 0, 0},
tri_1edge_split,
3,
};
/* v5
* / \
* s v6/---\ v4 s
* / \ / \
* sv7/---v---\ v3 s
* / \/ \/ \
* v8--v0--v1--v2
* s s
*/
static void tri_3edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
const subdparams *params)
{
BMFace *nf;
BMEdge *e, *ne, temp;
BMVert ***lines, *v, ov1, ov2;
void *stackarr[1];
int i, j, a, b, numcuts = params->numcuts;
/* number of verts in each lin */
lines = MEM_callocN(sizeof(void *)*(numcuts + 2), "triangle vert table");
lines[0] = (BMVert **) stackarr;
lines[0][0] = verts[numcuts * 2 + 1];
lines[numcuts + 1] = MEM_callocN(sizeof(void *) * (numcuts + 2), "triangle vert table 2");
for (i = 0; i < numcuts; i++) {
lines[numcuts + 1][i + 1] = verts[i];
}
lines[numcuts + 1][0] = verts[numcuts * 3 + 2];
lines[numcuts + 1][numcuts + 1] = verts[numcuts];
for (i = 0; i < numcuts; i++) {
lines[i + 1] = MEM_callocN(sizeof(void *)*(2 + i), "triangle vert table row");
a = numcuts * 2 + 2 + i;
b = numcuts + numcuts - i;
e = connect_smallest_face(bm, verts[a], verts[b], &nf);
if (!e) goto cleanup;
BMO_elem_flag_enable(bm, e, ELE_INNER);
BMO_elem_flag_enable(bm, nf, ELE_INNER);
lines[i + 1][0] = verts[a];
lines[i + 1][i + 1] = verts[b];
temp = *e;
ov1 = *verts[a];
ov2 = *verts[b];
temp.v1 = &ov1;
temp.v2 = &ov2;
for (j = 0; j < i; j++) {
v = subdivideedgenum(bm, e, &temp, j, i, params, &ne,
verts[a], verts[b]);
lines[i + 1][j + 1] = v;
BMO_elem_flag_enable(bm, ne, ELE_INNER);
}
}
/*
* v5
* / \
* s v6/---\ v4 s
* / \ / \
* sv7/---v---\ v3 s
* / \/ \/ \
* v8--v0--v1--v2
* s s
*/
for (i = 1; i < numcuts + 1; i++) {
for (j = 0; j < i; j++) {
e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &nf);
BMO_elem_flag_enable(bm, e, ELE_INNER);
BMO_elem_flag_enable(bm, nf, ELE_INNER);
e = connect_smallest_face(bm, lines[i][j + 1], lines[i + 1][j + 1], &nf);
BMO_elem_flag_enable(bm, e, ELE_INNER);
BMO_elem_flag_enable(bm, nf, ELE_INNER);
}
}
cleanup:
for (i = 1; i < numcuts + 2; i++) {
if (lines[i]) MEM_freeN(lines[i]);
}
MEM_freeN(lines);
}
static SubDPattern tri_3edge = {
{1, 1, 1},
tri_3edge_subdivide,
3,
};
static SubDPattern quad_4edge = {
{1, 1, 1, 1},
quad_4edge_subdivide,
4,
};
static SubDPattern *patterns[] = {
NULL, //quad single edge pattern is inserted here
NULL, //quad corner vert pattern is inserted here
NULL, //tri single edge pattern is inserted here
NULL,
&quad_3edge,
NULL,
};
#define PLEN (sizeof(patterns) / sizeof(void *))
typedef struct subd_facedata {
BMVert *start; SubDPattern *pat;
int totedgesel; //only used if pat was NULL, e.g. no pattern was found
BMFace *face;
} subd_facedata;
void esubdivide_exec(BMesh *bmesh, BMOperator *op)
{
BMOpSlot *einput;
SubDPattern *pat;
subdparams params;
subd_facedata *facedata = NULL;
BMIter viter, fiter, liter;
BMVert *v, **verts = NULL;
BMEdge *edge, **edges = NULL;
BMLoop *nl, *l, **splits = NULL, **loops = NULL;
BMFace *face;
BLI_array_declare(splits);
BLI_array_declare(loops);
BLI_array_declare(facedata);
BLI_array_declare(edges);
BLI_array_declare(verts);
float smooth, fractal;
int beauty, cornertype, singleedge, gridfill;
int skey, seed, i, j, matched, a, b, numcuts, totesel;
BMO_slot_buffer_flag_enable(bmesh, op, "edges", SUBD_SPLIT, BM_EDGE);
numcuts = BMO_slot_int_get(op, "numcuts");
seed = BMO_slot_int_get(op, "seed");
smooth = BMO_slot_float_get(op, "smooth");
fractal = BMO_slot_float_get(op, "fractal");
beauty = BMO_slot_int_get(op, "beauty");
cornertype = BMO_slot_int_get(op, "quadcornertype");
singleedge = BMO_slot_int_get(op, "singleedge");
gridfill = BMO_slot_int_get(op, "gridfill");
BLI_srandom(seed);
patterns[1] = NULL;
//straight cut is patterns[1] == NULL
switch (cornertype) {
case SUBD_PATH:
patterns[1] = &quad_2edge_path;
break;
case SUBD_INNERVERT:
patterns[1] = &quad_2edge_innervert;
break;
case SUBD_FAN:
patterns[1] = &quad_2edge_fan;
break;
}
if (singleedge) {
patterns[0] = &quad_1edge;
patterns[2] = &tri_1edge;
}
else {
patterns[0] = NULL;
patterns[2] = NULL;
}
if (gridfill) {
patterns[3] = &quad_4edge;
patterns[5] = &tri_3edge;
}
else {
patterns[3] = NULL;
patterns[5] = NULL;
}
/* add a temporary shapekey layer to store displacements on current geometr */
BM_data_layer_add(bmesh, &bmesh->vdata, CD_SHAPEKEY);
skey = CustomData_number_of_layers(&bmesh->vdata, CD_SHAPEKEY) - 1;
BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
copy_v3_v3(co, v->co);
}
/* first go through and tag edge */
BMO_slot_from_flag(bmesh, op, "edges",
SUBD_SPLIT, BM_EDGE);
params.numcuts = numcuts;
params.op = op;
params.smooth = smooth;
params.seed = seed;
params.fractal = fractal;
params.beauty = beauty;
params.origkey = skey;
params.off[0] = (float)BLI_drand() * 200.0f;
params.off[1] = (float)BLI_drand() * 200.0f;
params.off[2] = (float)BLI_drand() * 200.0f;
BMO_slot_map_to_flag(bmesh, op, "custompatterns",
FACE_CUSTOMFILL);
BMO_slot_map_to_flag(bmesh, op, "edgepercents",
EDGE_PERCENT);
for (face = BM_iter_new(&fiter, bmesh, BM_FACES_OF_MESH, NULL);
face;
face = BM_iter_step(&fiter))
{
BMEdge *e1 = NULL, *e2 = NULL;
float vec1[3], vec2[3];
/* figure out which pattern to us */
BLI_array_empty(edges);
BLI_array_empty(verts);
matched = 0;
i = 0;
totesel = 0;
for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
BLI_array_growone(edges);
BLI_array_growone(verts);
edges[i] = nl->e;
verts[i] = nl->v;
if (BMO_elem_flag_test(bmesh, edges[i], SUBD_SPLIT)) {
if (!e1) e1 = edges[i];
else e2 = edges[i];
totesel++;
}
i++;
}
/* make sure the two edges have a valid angle to each othe */
if (totesel == 2 && BM_edge_share_vert(e1, e2)) {
float angle;
sub_v3_v3v3(vec1, e1->v2->co, e1->v1->co);
sub_v3_v3v3(vec2, e2->v2->co, e2->v1->co);
normalize_v3(vec1);
normalize_v3(vec2);
angle = dot_v3v3(vec1, vec2);
angle = fabsf(angle);
if (fabsf(angle - 1.0f) < 0.01f) {
totesel = 0;
}
}
if (BMO_elem_flag_test(bmesh, face, FACE_CUSTOMFILL)) {
pat = BMO_slot_map_data_get(bmesh, op,
"custompatterns", face);
for (i = 0; i < pat->len; i++) {
matched = 1;
for (j = 0; j < pat->len; j++) {
a = (j + i) % pat->len;
if ((!!BMO_elem_flag_test(bmesh, edges[a], SUBD_SPLIT)) != (!!pat->seledges[j])) {
matched = 0;
break;
}
}
if (matched) {
BLI_array_growone(facedata);
b = BLI_array_count(facedata) - 1;
facedata[b].pat = pat;
facedata[b].start = verts[i];
facedata[b].face = face;
facedata[b].totedgesel = totesel;
BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
break;
}
}
/* obvously don't test for other patterns matchin */
continue;
}
for (i = 0; i < PLEN; i++) {
pat = patterns[i];
if (!pat) continue;
if (pat->len == face->len) {
for (a = 0; a < pat->len; a++) {
matched = 1;
for (b = 0; b < pat->len; b++) {
j = (b + a) % pat->len;
if ((!!BMO_elem_flag_test(bmesh, edges[j], SUBD_SPLIT)) != (!!pat->seledges[b])) {
matched = 0;
break;
}
}
if (matched) {
break;
}
}
if (matched) {
BLI_array_growone(facedata);
j = BLI_array_count(facedata) - 1;
BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
facedata[j].pat = pat;
facedata[j].start = verts[a];
facedata[j].face = face;
facedata[j].totedgesel = totesel;
break;
}
}
}
if (!matched && totesel) {
BLI_array_growone(facedata);
j = BLI_array_count(facedata) - 1;
BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
facedata[j].totedgesel = totesel;
facedata[j].face = face;
}
}
einput = BMO_slot_get(op, "edges");
/* go through and split edge */
for (i = 0; i < einput->len; i++) {
edge = ((BMEdge **)einput->data.p)[i];
bm_subdivide_multicut(bmesh, edge, &params, edge->v1, edge->v2);
}
i = 0;
for (i = 0; i < BLI_array_count(facedata); i++) {
face = facedata[i].face;
/* figure out which pattern to us */
BLI_array_empty(verts);
pat = facedata[i].pat;
if (!pat && facedata[i].totedgesel == 2) {
int vlen;
/* ok, no pattern. we still may be able to do something */
BLI_array_empty(loops);
BLI_array_empty(splits);
/* for case of two edges, connecting them shouldn't be too har */
BM_ITER(l, &liter, bmesh, BM_LOOPS_OF_FACE, face) {
BLI_array_growone(loops);
loops[BLI_array_count(loops) - 1] = l;
}
vlen = BLI_array_count(loops);
/* find the boundary of one of the split edge */
for (a = 1; a < vlen; a++) {
if (!BMO_elem_flag_test(bmesh, loops[a - 1]->v, ELE_INNER) &&
BMO_elem_flag_test(bmesh, loops[a]->v, ELE_INNER))
{
break;
}
}
if (BMO_elem_flag_test(bmesh, loops[(a + numcuts + 1) % vlen]->v, ELE_INNER)) {
b = (a + numcuts + 1) % vlen;
}
else {
/* find the boundary of the other edge. */
for (j = 0; j < vlen; j++) {
b = (j + a + numcuts + 1) % vlen;
if (!BMO_elem_flag_test(bmesh, loops[b == 0 ? vlen - 1 : b - 1]->v, ELE_INNER) &&
BMO_elem_flag_test(bmesh, loops[b]->v, ELE_INNER))
{
break;
}
}
}
b += numcuts - 1;
for (j = 0; j < numcuts; j++) {
BLI_array_growone(splits);
splits[BLI_array_count(splits) - 1] = loops[a];
BLI_array_growone(splits);
splits[BLI_array_count(splits) - 1] = loops[b];
b = (b - 1) % vlen;
a = (a + 1) % vlen;
}
//BM_face_legal_splits(bmesh, face, splits, BLI_array_count(splits)/2);
for (j = 0; j < BLI_array_count(splits) / 2; j++) {
if (splits[j * 2]) {
/* BMFace *nf = */ /* UNUSED */
BM_face_split(bmesh, face, splits[j * 2]->v, splits[j * 2 + 1]->v, &nl, NULL);
}
}
continue;
}
else if (!pat) {
continue;
}
j = a = 0;
for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face);
nl;
nl = BM_iter_step(&liter))
{
if (nl->v == facedata[i].start) {
a = j + 1;
break;
}
j++;
}
for (j = 0; j < face->len; j++) {
BLI_array_growone(verts);
}
j = 0;
for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
b = (j - a + face->len) % face->len;
verts[b] = nl->v;
j += 1;
}
BM_CHECK_ELEMENT(bmesh, face);
pat->connectexec(bmesh, face, verts, &params);
}
/* copy original-geometry displacements to current coordinate */
BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
copy_v3_v3(v->co, co);
}
BM_data_layer_free_n(bmesh, &bmesh->vdata, CD_SHAPEKEY, skey);
if (facedata) BLI_array_free(facedata);
if (edges) BLI_array_free(edges);
if (verts) BLI_array_free(verts);
BLI_array_free(splits);
BLI_array_free(loops);
BMO_slot_from_flag(bmesh, op, "outinner",
ELE_INNER, BM_ALL);
BMO_slot_from_flag(bmesh, op, "outsplit",
ELE_SPLIT, BM_ALL);
BMO_slot_from_flag(bmesh, op, "geomout",
ELE_INNER|ELE_SPLIT|SUBD_SPLIT, BM_ALL);
}
/* editmesh-emulating functio */
void BM_mesh_esubdivideflag(Object *UNUSED(obedit), BMesh *bm, int flag, float smooth,
float fractal, int beauty, int numcuts,
int seltype, int cornertype, int singleedge,
int gridfill, int seed)
{
BMOperator op;
BMO_op_initf(bm, &op, "esubd edges=%he smooth=%f fractal=%f "
"beauty=%d numcuts=%d quadcornertype=%d singleedge=%d "
"gridfill=%d seed=%d",
flag, smooth, fractal, beauty, numcuts,
cornertype, singleedge, gridfill, seed);
BMO_op_exec(bm, &op);
if (seltype == SUBDIV_SELECT_INNER) {
BMOIter iter;
BMHeader *ele;
// int i;
ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
for ( ; ele; ele = BMO_iter_step(&iter)) {
BM_elem_select_set(bm, ele, TRUE);
}
}
else if (seltype == SUBDIV_SELECT_LOOPCUT) {
BMOIter iter;
BMHeader *ele;
// int i;
/* deselect input */
BM_mesh_elem_flag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT);
ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
for ( ; ele; ele = BMO_iter_step(&iter)) {
BM_elem_select_set(bm, ele, TRUE);
if (ele->htype == BM_VERT) {
BMEdge *e;
BMIter eiter;
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, ele) {
if (!BM_elem_flag_test(e, BM_ELEM_SELECT) &&
BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
BM_elem_flag_test(e->v2, BM_ELEM_SELECT))
{
BM_elem_select_set(bm, e, TRUE);
bm->totedgesel += 1;
}
else if (BM_elem_flag_test(e, BM_ELEM_SELECT) &&
(!BM_elem_flag_test(e->v1, BM_ELEM_SELECT) ||
!BM_elem_flag_test(e->v2, BM_ELEM_SELECT)))
{
BM_elem_select_set(bm, e, FALSE);
bm->totedgesel -= 1;
}
}
}
}
}
BMO_op_finish(bm, &op);
}
void esplit_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMEdge *e;
subdparams params;
int skey;
params.numcuts = BMO_slot_get(op, "numcuts")->data.i;
params.op = op;
BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY);
skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1;
params.origkey = skey;
/* go through and split edge */
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
bm_subdivide_multicut(bm, e, &params, e->v1, e->v2);
}
BMO_slot_from_flag(bm, op, "outsplit", ELE_SPLIT, BM_ALL);
BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey);
}

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

View File

@@ -0,0 +1,1297 @@
/*
* ***** 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 "BLI_heap.h"
#include "BKE_customdata.h"
#include "bmesh.h"
#include "bmesh_operators_private.h" /* own include */
/*
* UTILS.C
*
* utility bmesh operators, e.g. transform,
* translate, rotate, scale, etc.
*
*/
void bmesh_makevert_exec(BMesh *bm, BMOperator *op)
{
float vec[3];
BMO_slot_vec_get(op, "co", vec);
BMO_elem_flag_enable(bm, BM_vert_create(bm, vec, NULL), 1);
BMO_slot_from_flag(bm, op, "newvertout", 1, BM_VERT);
}
void bmesh_transform_exec(BMesh *bm, BMOperator *op)
{
BMOIter iter;
BMVert *v;
float mat[4][4];
BMO_slot_mat4_get(op, "mat", mat);
BMO_ITER(v, &iter, bm, op, "verts", BM_VERT) {
mul_m4_v3(mat, v->co);
}
}
void bmesh_translate_exec(BMesh *bm, BMOperator *op)
{
float mat[4][4], vec[3];
BMO_slot_vec_get(op, "vec", vec);
unit_m4(mat);
copy_v3_v3(mat[3], vec);
BMO_op_callf(bm, "transform mat=%m4 verts=%s", mat, op, "verts");
}
void bmesh_scale_exec(BMesh *bm, BMOperator *op)
{
float mat[3][3], vec[3];
BMO_slot_vec_get(op, "vec", vec);
unit_m3(mat);
mat[0][0] = vec[0];
mat[1][1] = vec[1];
mat[2][2] = vec[2];
BMO_op_callf(bm, "transform mat=%m3 verts=%s", mat, op, "verts");
}
void bmesh_rotate_exec(BMesh *bm, BMOperator *op)
{
float vec[3];
BMO_slot_vec_get(op, "cent", vec);
/* there has to be a proper matrix way to do this, but
* this is how editmesh did it and I'm too tired to think
* through the math right now. */
mul_v3_fl(vec, -1.0f);
BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
BMO_op_callf(bm, "transform mat=%s verts=%s", op, "mat", op, "verts");
mul_v3_fl(vec, -1.0f);
BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
}
void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMFace *f;
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
BM_face_normal_flip(bm, f);
}
}
void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMEdge *e, *e2;
int ccw = BMO_slot_int_get(op, "ccw");
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
if (!(e2 = BM_edge_rotate(bm, e, ccw))) {
BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge");
return;
}
BMO_elem_flag_enable(bm, e2, 1);
}
BMO_slot_from_flag(bm, op, "edgeout", 1, BM_EDGE);
}
#define SEL_FLAG 1
#define SEL_ORIG 2
static void bmesh_regionextend_extend(BMesh *bm, BMOperator *op, int usefaces)
{
BMVert *v;
BMEdge *e;
BMIter eiter;
BMOIter siter;
if (!usefaces) {
BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
break;
}
if (e) {
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
BMO_elem_flag_enable(bm, e, SEL_FLAG);
BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG);
}
}
}
}
else {
BMIter liter, fiter;
BMFace *f, *f2;
BMLoop *l;
BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
if (!BMO_elem_flag_test(bm, f2, SEL_ORIG))
BMO_elem_flag_enable(bm, f2, SEL_FLAG);
}
}
}
}
}
static void bmesh_regionextend_constrict(BMesh *bm, BMOperator *op, int usefaces)
{
BMVert *v;
BMEdge *e;
BMIter eiter;
BMOIter siter;
if (!usefaces) {
BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
break;
}
if (e) {
BMO_elem_flag_enable(bm, v, SEL_FLAG);
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
BMO_elem_flag_enable(bm, e, SEL_FLAG);
}
}
}
}
else {
BMIter liter, fiter;
BMFace *f, *f2;
BMLoop *l;
BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) {
BMO_elem_flag_enable(bm, f, SEL_FLAG);
break;
}
}
}
}
}
}
void bmesh_regionextend_exec(BMesh *bm, BMOperator *op)
{
int usefaces = BMO_slot_int_get(op, "usefaces");
int constrict = BMO_slot_int_get(op, "constrict");
BMO_slot_buffer_flag_enable(bm, op, "geom", SEL_ORIG, BM_ALL);
if (constrict)
bmesh_regionextend_constrict(bm, op, usefaces);
else
bmesh_regionextend_extend(bm, op, usefaces);
BMO_slot_from_flag(bm, op, "geomout", SEL_FLAG, BM_ALL);
}
/********* righthand faces implementation ****** */
#define FACE_VIS 1
#define FACE_FLAG 2
#define FACE_MARK 4
#define FACE_FLIP 8
/* NOTE: these are the original righthandfaces comment in editmesh_mods.c,
* copied here for reference. */
/* based at a select-connected to witness loose objects */
/* count per edge the amount of faces
* find the ultimate left, front, upper face (not manhattan dist!!)
* also evaluate both triangle cases in quad, since these can be non-flat
*
* put normal to the outside, and set the first direction flags in edges
*
* then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces
* this is in fact the 'select connected'
*
* in case (selected) faces were not done: start over with 'find the ultimate ...' */
/* NOTE: this function uses recursion, which is a little unusual for a bmop
* function, but acceptable I think. */
/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */
void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op)
{
BMIter liter, liter2;
BMOIter siter;
BMFace *f, *startf, **fstack = NULL;
BLI_array_declare(fstack);
BMLoop *l, *l2;
float maxx, cent[3];
int i, maxi, flagflip = BMO_slot_int_get(op, "doflip");
startf = NULL;
maxx = -1.0e10;
BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_FLAG, BM_FACE);
/* find a starting face */
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
/* clear dirty flag */
BM_elem_flag_disable(f, BM_ELEM_TAG);
if (BMO_elem_flag_test(bm, f, FACE_VIS))
continue;
if (!startf) startf = f;
BM_face_center_bounds_calc(bm, f, cent);
cent[0] = cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2];
if (cent[0] > maxx) {
maxx = cent[0];
startf = f;
}
}
if (!startf) return;
BM_face_center_bounds_calc(bm, startf, cent);
/* make sure the starting face has the correct winding */
if (dot_v3v3(cent, startf->no) < 0.0f) {
BM_face_normal_flip(bm, startf);
BMO_elem_flag_toggle(bm, startf, FACE_FLIP);
if (flagflip)
BM_elem_flag_toggle(startf, BM_ELEM_TAG);
}
/* now that we've found our starting face, make all connected faces
* have the same winding. this is done recursively, using a manual
* stack (if we use simple function recursion, we'd end up overloading
* the stack on large meshes). */
BLI_array_growone(fstack);
fstack[0] = startf;
BMO_elem_flag_enable(bm, startf, FACE_VIS);
i = 0;
maxi = 1;
while (i >= 0) {
f = fstack[i];
i--;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_LOOP, l) {
if (!BMO_elem_flag_test(bm, l2->f, FACE_FLAG) || l2 == l)
continue;
if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) {
BMO_elem_flag_enable(bm, l2->f, FACE_VIS);
i++;
if (l2->v == l->v) {
BM_face_normal_flip(bm, l2->f);
BMO_elem_flag_toggle(bm, l2->f, FACE_FLIP);
if (flagflip)
BM_elem_flag_toggle(l2->f, BM_ELEM_TAG);
}
else if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
if (flagflip) {
BM_elem_flag_disable(l->f, BM_ELEM_TAG);
BM_elem_flag_disable(l2->f, BM_ELEM_TAG);
}
}
if (i == maxi) {
BLI_array_growone(fstack);
maxi++;
}
fstack[i] = l2->f;
}
}
}
}
BLI_array_free(fstack);
/* check if we have faces yet to do. if so, recurse */
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
if (!BMO_elem_flag_test(bm, f, FACE_VIS)) {
bmesh_righthandfaces_exec(bm, op);
break;
}
}
}
void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op)
{
BMOIter siter;
BMIter iter;
BMVert *v;
BMEdge *e;
BLI_array_declare(cos);
float (*cos)[3] = NULL;
float *co, *co2, clipdist = BMO_slot_float_get(op, "clipdist");
int i, j, clipx, clipy, clipz;
clipx = BMO_slot_int_get(op, "mirror_clip_x");
clipy = BMO_slot_int_get(op, "mirror_clip_y");
clipz = BMO_slot_int_get(op, "mirror_clip_z");
i = 0;
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
BLI_array_growone(cos);
co = cos[i];
j = 0;
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
co2 = BM_edge_other_vert(e, v)->co;
add_v3_v3v3(co, co, co2);
j += 1;
}
if (!j) {
copy_v3_v3(co, v->co);
i++;
continue;
}
mul_v3_fl(co, 1.0f / (float)j);
mid_v3_v3v3(co, co, v->co);
if (clipx && fabsf(v->co[0]) <= clipdist)
co[0] = 0.0f;
if (clipy && fabsf(v->co[1]) <= clipdist)
co[1] = 0.0f;
if (clipz && fabsf(v->co[2]) <= clipdist)
co[2] = 0.0f;
i++;
}
i = 0;
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
copy_v3_v3(v->co, cos[i]);
i++;
}
BLI_array_free(cos);
}
/*
* compute the perimeter of an ngon
*
* NOTE: This should probably go to bmesh_polygon.c
*/
static float ngon_perimeter(BMesh *bm, BMFace *f)
{
BMIter liter;
BMLoop *l;
int num_verts = 0;
float v[3], sv[3];
float perimeter = 0.0f;
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
if (num_verts == 0) {
copy_v3_v3(v, l->v->co);
copy_v3_v3(sv, l->v->co);
}
else {
perimeter += len_v3v3(v, l->v->co);
copy_v3_v3(v, l->v->co);
}
num_verts++;
}
perimeter += len_v3v3(v, sv);
return perimeter;
}
/*
* compute the fake surface of an ngon
* This is done by decomposing the ngon into triangles who share the centroid of the ngon
* while this method is far from being exact, it should garantee an invariance.
*
* NOTE: This should probably go to bmesh_polygon.c
*/
static float ngon_fake_area(BMesh *bm, BMFace *f)
{
BMIter liter;
BMLoop *l;
int num_verts = 0;
float v[3], sv[3], c[3];
float area = 0.0f;
BM_face_center_mean_calc(bm, f, c);
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
if (num_verts == 0) {
copy_v3_v3(v, l->v->co);
copy_v3_v3(sv, l->v->co);
num_verts++;
}
else {
area += area_tri_v3(v, c, l->v->co);
copy_v3_v3(v, l->v->co);
num_verts++;
}
}
area += area_tri_v3(v, c, sv);
return area;
}
/*
* extra face data (computed data)
*/
typedef struct tmp_face_ext {
BMFace *f; /* the face */
float c[3]; /* center */
union {
float area; /* area */
float perim; /* perimeter */
float d; /* 4th component of plane (the first three being the normal) */
struct Image *t; /* image pointer */
};
} tmp_face_ext;
/*
* Select similar faces, the choices are in the enum in source/blender/bmesh/bmesh_operators.h
* We select either similar faces based on material, image, area, perimeter, normal, or the coplanar faces
*/
void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op)
{
BMIter fm_iter;
BMFace *fs, *fm;
BMOIter fs_iter;
int num_sels = 0, num_total = 0, i = 0, idx = 0;
float angle = 0.0f;
tmp_face_ext *f_ext = NULL;
int *indices = NULL;
float t_no[3]; /* temporary normal */
int type = BMO_slot_int_get(op, "type");
float thresh = BMO_slot_float_get(op, "thresh");
num_total = BM_mesh_elem_count(bm, BM_FACE);
/*
** The first thing to do is to iterate through all the the selected items and mark them since
** they will be in the selection anyway.
** This will increase performance, (especially when the number of originaly selected faces is high)
** so the overall complexity will be less than $O(mn)$ where is the total number of selected faces,
** and n is the total number of faces
*/
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
if (!BMO_elem_flag_test(bm, fs, FACE_MARK)) { /* is this really needed ? */
BMO_elem_flag_enable(bm, fs, FACE_MARK);
num_sels++;
}
}
/* allocate memory for the selected faces indices and for all temporary faces */
indices = (int *)MEM_callocN(sizeof(int) * num_sels, "face indices util.c");
f_ext = (tmp_face_ext *)MEM_callocN(sizeof(tmp_face_ext) * num_total, "f_ext util.c");
/* loop through all the faces and fill the faces/indices structure */
BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) {
f_ext[i].f = fm;
if (BMO_elem_flag_test(bm, fm, FACE_MARK)) {
indices[idx] = i;
idx++;
}
i++;
}
/*
** Save us some computation burden: In case of perimeter/area/coplanar selection we compute
** only once.
*/
if (type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE) {
for (i = 0; i < num_total; i++) {
switch (type) {
case SIMFACE_PERIMETER:
/* set the perimeter */
f_ext[i].perim = ngon_perimeter(bm, f_ext[i].f);
break;
case SIMFACE_COPLANAR:
/* compute the center of the polygon */
BM_face_center_mean_calc(bm, f_ext[i].f, f_ext[i].c);
/* normalize the polygon normal */
copy_v3_v3(t_no, f_ext[i].f->no);
normalize_v3(t_no);
/* compute the plane distance */
f_ext[i].d = dot_v3v3(t_no, f_ext[i].c);
break;
case SIMFACE_AREA:
f_ext[i].area = ngon_fake_area(bm, f_ext[i].f);
break;
case SIMFACE_IMAGE:
f_ext[i].t = NULL;
if (CustomData_has_layer(&(bm->pdata), CD_MTEXPOLY)) {
MTexPoly *mtpoly = CustomData_bmesh_get(&bm->pdata, f_ext[i].f->head.data, CD_MTEXPOLY);
f_ext[i].t = mtpoly->tpage;
}
break;
}
}
}
/* now select the rest (if any) */
for (i = 0; i < num_total; i++) {
fm = f_ext[i].f;
if (!BMO_elem_flag_test(bm, fm, FACE_MARK) && !BM_elem_flag_test(fm, BM_ELEM_HIDDEN)) {
int cont = 1;
for (idx = 0; idx < num_sels && cont == 1; idx++) {
fs = f_ext[indices[idx]].f;
switch (type) {
case SIMFACE_MATERIAL:
if (fm->mat_nr == fs->mat_nr) {
BMO_elem_flag_enable(bm, fm, FACE_MARK);
cont = 0;
}
break;
case SIMFACE_IMAGE:
if (f_ext[i].t == f_ext[indices[idx]].t) {
BMO_elem_flag_enable(bm, fm, FACE_MARK);
cont = 0;
}
break;
case SIMFACE_NORMAL:
angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* if the angle between the normals -> 0 */
if (angle / 180.0f <= thresh) {
BMO_elem_flag_enable(bm, fm, FACE_MARK);
cont = 0;
}
break;
case SIMFACE_COPLANAR:
angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* angle -> 0 */
if (angle / 180.0f <= thresh) { /* and dot product difference -> 0 */
if (fabsf(f_ext[i].d - f_ext[indices[idx]].d) <= thresh) {
BMO_elem_flag_enable(bm, fm, FACE_MARK);
cont = 0;
}
}
break;
case SIMFACE_AREA:
if (fabsf(f_ext[i].area - f_ext[indices[idx]].area) <= thresh) {
BMO_elem_flag_enable(bm, fm, FACE_MARK);
cont = 0;
}
break;
case SIMFACE_PERIMETER:
if (fabsf(f_ext[i].perim - f_ext[indices[idx]].perim) <= thresh) {
BMO_elem_flag_enable(bm, fm, FACE_MARK);
cont = 0;
}
break;
}
}
}
}
MEM_freeN(f_ext);
MEM_freeN(indices);
/* transfer all marked faces to the output slot */
BMO_slot_from_flag(bm, op, "faceout", FACE_MARK, BM_FACE);
}
/******************************************************************************
** Similar Edges
**************************************************************************** */
#define EDGE_MARK 1
/*
* compute the angle of an edge (i.e. the angle between two faces)
*/
static float edge_angle(BMesh *bm, BMEdge *e)
{
BMIter fiter;
BMFace *f, *f_prev = NULL;
/* first edge faces, dont account for 3+ */
BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
if (f_prev == NULL) {
f_prev = f;
}
else {
return angle_v3v3(f_prev->no, f->no);
}
}
return 0.0f;
}
/*
* extra edge information
*/
typedef struct tmp_edge_ext {
BMEdge *e;
union {
float dir[3];
float angle; /* angle between the face */
};
union {
float length; /* edge length */
int faces; /* faces count */
};
} tmp_edge_ext;
/*
* select similar edges: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
* choices are length, direction, face, ...
*/
void bmesh_similaredges_exec(BMesh *bm, BMOperator *op)
{
BMOIter es_iter; /* selected edges iterator */
BMIter e_iter; /* mesh edges iterator */
BMEdge *es; /* selected edge */
BMEdge *e; /* mesh edge */
int idx = 0, i = 0 /* , f = 0 */;
int *indices = NULL;
tmp_edge_ext *e_ext = NULL;
// float *angles = NULL;
float angle;
int num_sels = 0, num_total = 0;
int type = BMO_slot_int_get(op, "type");
float thresh = BMO_slot_float_get(op, "thresh");
num_total = BM_mesh_elem_count(bm, BM_EDGE);
/* iterate through all selected edges and mark them */
BMO_ITER(es, &es_iter, bm, op, "edges", BM_EDGE) {
BMO_elem_flag_enable(bm, es, EDGE_MARK);
num_sels++;
}
/* allocate memory for the selected edges indices and for all temporary edges */
indices = (int *)MEM_callocN(sizeof(int) * num_sels, "indices util.c");
e_ext = (tmp_edge_ext *)MEM_callocN(sizeof(tmp_edge_ext) * num_total, "e_ext util.c");
/* loop through all the edges and fill the edges/indices structure */
BM_ITER(e, &e_iter, bm, BM_EDGES_OF_MESH, NULL) {
e_ext[i].e = e;
if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
indices[idx] = i;
idx++;
}
i++;
}
/* save us some computation time by doing heavy computation once */
if (type == SIMEDGE_LENGTH || type == SIMEDGE_FACE || type == SIMEDGE_DIR || type == SIMEDGE_FACE_ANGLE) {
for (i = 0; i < num_total; i++) {
switch (type) {
case SIMEDGE_LENGTH: /* compute the length of the edge */
e_ext[i].length = len_v3v3(e_ext[i].e->v1->co, e_ext[i].e->v2->co);
break;
case SIMEDGE_DIR: /* compute the direction */
sub_v3_v3v3(e_ext[i].dir, e_ext[i].e->v1->co, e_ext[i].e->v2->co);
break;
case SIMEDGE_FACE: /* count the faces around the edge */
e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
break;
case SIMEDGE_FACE_ANGLE:
e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
if (e_ext[i].faces == 2)
e_ext[i].angle = edge_angle(bm, e_ext[i].e);
break;
}
}
}
/* select the edges if any */
for (i = 0; i < num_total; i++) {
e = e_ext[i].e;
if (!BMO_elem_flag_test(bm, e, EDGE_MARK) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
int cont = 1;
for (idx = 0; idx < num_sels && cont == 1; idx++) {
es = e_ext[indices[idx]].e;
switch (type) {
case SIMEDGE_LENGTH:
if (fabsf(e_ext[i].length - e_ext[indices[idx]].length) <= thresh) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
cont = 0;
}
break;
case SIMEDGE_DIR:
/* compute the angle between the two edges */
angle = RAD2DEGF(angle_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir));
if (angle > 90.0f) /* use the smallest angle between the edges */
angle = fabsf(angle - 180.0f);
if (angle / 90.0f <= thresh) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
cont = 0;
}
break;
case SIMEDGE_FACE:
if (e_ext[i].faces == e_ext[indices[idx]].faces) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
cont = 0;
}
break;
case SIMEDGE_FACE_ANGLE:
if (e_ext[i].faces == 2) {
if (e_ext[indices[idx]].faces == 2) {
if (fabsf(e_ext[i].angle - e_ext[indices[idx]].angle) <= thresh) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
cont = 0;
}
}
}
else {
cont = 0;
}
break;
case SIMEDGE_CREASE:
if (CustomData_has_layer(&bm->edata, CD_CREASE)) {
float *c1, *c2;
c1 = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE);
c2 = CustomData_bmesh_get(&bm->edata, es->head.data, CD_CREASE);
if (c1 && c2 && fabsf(*c1 - *c2) <= thresh) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
cont = 0;
}
}
break;
case SIMEDGE_SEAM:
if (BM_elem_flag_test(e, BM_ELEM_SEAM) == BM_elem_flag_test(es, BM_ELEM_SEAM)) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
cont = 0;
}
break;
case SIMEDGE_SHARP:
if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == BM_elem_flag_test(es, BM_ELEM_SMOOTH)) {
BMO_elem_flag_enable(bm, e, EDGE_MARK);
cont = 0;
}
break;
}
}
}
}
MEM_freeN(e_ext);
MEM_freeN(indices);
/* transfer all marked edges to the output slot */
BMO_slot_from_flag(bm, op, "edgeout", EDGE_MARK, BM_EDGE);
}
/******************************************************************************
** Similar Vertices
**************************************************************************** */
#define VERT_MARK 1
typedef struct tmp_vert_ext {
BMVert *v;
union {
int num_faces; /* adjacent faces */
MDeformVert *dvert; /* deform vertex */
};
} tmp_vert_ext;
/*
* select similar vertices: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
* choices are normal, face, vertex group...
*/
void bmesh_similarverts_exec(BMesh *bm, BMOperator *op)
{
BMOIter vs_iter; /* selected verts iterator */
BMIter v_iter; /* mesh verts iterator */
BMVert *vs; /* selected vertex */
BMVert *v; /* mesh vertex */
tmp_vert_ext *v_ext = NULL;
int *indices = NULL;
int num_total = 0, num_sels = 0, i = 0, idx = 0;
int type = BMO_slot_int_get(op, "type");
float thresh = BMO_slot_float_get(op, "thresh");
num_total = BM_mesh_elem_count(bm, BM_VERT);
/* iterate through all selected edges and mark them */
BMO_ITER(vs, &vs_iter, bm, op, "verts", BM_VERT) {
BMO_elem_flag_enable(bm, vs, VERT_MARK);
num_sels++;
}
/* allocate memory for the selected vertices indices and for all temporary vertices */
indices = (int *)MEM_mallocN(sizeof(int) * num_sels, "vertex indices");
v_ext = (tmp_vert_ext *)MEM_mallocN(sizeof(tmp_vert_ext) * num_total, "vertex extra");
/* loop through all the vertices and fill the vertices/indices structure */
BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
v_ext[i].v = v;
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
indices[idx] = i;
idx++;
}
switch (type) {
case SIMVERT_FACE:
/* calling BM_vert_face_count every time is time consumming, so call it only once per vertex */
v_ext[i].num_faces = BM_vert_face_count(v);
break;
case SIMVERT_VGROUP:
if (CustomData_has_layer(&(bm->vdata), CD_MDEFORMVERT)) {
v_ext[i].dvert = CustomData_bmesh_get(&bm->vdata, v_ext[i].v->head.data, CD_MDEFORMVERT);
}
else {
v_ext[i].dvert = NULL;
}
break;
}
i++;
}
/* select the vertices if any */
for (i = 0; i < num_total; i++) {
v = v_ext[i].v;
if (!BMO_elem_flag_test(bm, v, VERT_MARK) && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
int cont = 1;
for (idx = 0; idx < num_sels && cont == 1; idx++) {
vs = v_ext[indices[idx]].v;
switch (type) {
case SIMVERT_NORMAL:
/* compare the angle between the normals */
if (RAD2DEGF(angle_v3v3(v->no, vs->no)) / 180.0f <= thresh) {
BMO_elem_flag_enable(bm, v, VERT_MARK);
cont = 0;
}
break;
case SIMVERT_FACE:
/* number of adjacent faces */
if (v_ext[i].num_faces == v_ext[indices[idx]].num_faces) {
BMO_elem_flag_enable(bm, v, VERT_MARK);
cont = 0;
}
break;
case SIMVERT_VGROUP:
if (v_ext[i].dvert != NULL && v_ext[indices[idx]].dvert != NULL) {
int v1, v2;
for (v1 = 0; v1 < v_ext[i].dvert->totweight && cont == 1; v1++) {
for (v2 = 0; v2 < v_ext[indices[idx]].dvert->totweight; v2++) {
if (v_ext[i].dvert->dw[v1].def_nr == v_ext[indices[idx]].dvert->dw[v2].def_nr) {
BMO_elem_flag_enable(bm, v, VERT_MARK);
cont = 0;
break;
}
}
}
}
break;
}
}
}
}
MEM_freeN(indices);
MEM_freeN(v_ext);
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}
/******************************************************************************
** Cycle UVs for a face
**************************************************************************** */
void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op)
{
BMOIter fs_iter; /* selected faces iterator */
BMFace *fs; /* current face */
BMIter l_iter; /* iteration loop */
// int n;
int dir = BMO_slot_int_get(op, "dir");
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
if (dir == DIRECTION_CW) { /* same loops direction */
BMLoop *lf; /* current face loops */
MLoopUV *f_luv; /* first face loop uv */
float p_uv[2]; /* previous uvs */
float t_uv[2]; /* tmp uvs */
int n = 0;
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
/* current loop uv is the previous loop uv */
MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
if (n == 0) {
f_luv = luv;
copy_v2_v2(p_uv, luv->uv);
}
else {
copy_v2_v2(t_uv, luv->uv);
copy_v2_v2(luv->uv, p_uv);
copy_v2_v2(p_uv, t_uv);
}
n++;
}
copy_v2_v2(f_luv->uv, p_uv);
}
else if (dir == DIRECTION_CCW) { /* counter loop direction */
BMLoop *lf; /* current face loops */
MLoopUV *p_luv; /* previous loop uv */
MLoopUV *luv;
float t_uv[2]; /* current uvs */
int n = 0;
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
/* previous loop uv is the current loop uv */
luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
if (n == 0) {
p_luv = luv;
copy_v2_v2(t_uv, luv->uv);
}
else {
copy_v2_v2(p_luv->uv, luv->uv);
p_luv = luv;
}
n++;
}
copy_v2_v2(luv->uv, t_uv);
}
}
}
}
/******************************************************************************
** Reverse UVs for a face
**************************************************************************** */
void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op)
{
BMOIter fs_iter; /* selected faces iterator */
BMFace *fs; /* current face */
BMIter l_iter; /* iteration loop */
BLI_array_declare(uvs);
float (*uvs)[2] = NULL;
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
BMLoop *lf; /* current face loops */
int i = 0;
BLI_array_empty(uvs);
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
/* current loop uv is the previous loop uv */
BLI_array_growone(uvs);
uvs[i][0] = luv->uv[0];
uvs[i][1] = luv->uv[1];
i++;
}
/* now that we have the uvs in the array, reverse! */
i = 0;
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
/* current loop uv is the previous loop uv */
MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
luv->uv[0] = uvs[(fs->len - i - 1)][0];
luv->uv[1] = uvs[(fs->len - i - 1)][1];
i++;
}
}
}
BLI_array_free(uvs);
}
/******************************************************************************
** Cycle colors for a face
**************************************************************************** */
void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op)
{
BMOIter fs_iter; /* selected faces iterator */
BMFace *fs; /* current face */
BMIter l_iter; /* iteration loop */
// int n;
int dir = BMO_slot_int_get(op, "dir");
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
if (dir == DIRECTION_CW) { /* same loops direction */
BMLoop *lf; /* current face loops */
MLoopCol *f_lcol; /* first face loop color */
MLoopCol p_col; /* previous color */
MLoopCol t_col; /* tmp color */
int n = 0;
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
/* current loop color is the previous loop color */
MLoopCol *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
if (n == 0) {
f_lcol = luv;
p_col = *luv;
}
else {
t_col = *luv;
*luv = p_col;
p_col = t_col;
}
n++;
}
*f_lcol = p_col;
}
else if (dir == DIRECTION_CCW) { /* counter loop direction */
BMLoop *lf; /* current face loops */
MLoopCol *p_lcol; /* previous loop color */
MLoopCol *lcol;
MLoopCol t_col; /* current color */
int n = 0;
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
/* previous loop color is the current loop color */
lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
if (n == 0) {
p_lcol = lcol;
t_col = *lcol;
}
else {
*p_lcol = *lcol;
p_lcol = lcol;
}
n++;
}
*lcol = t_col;
}
}
}
}
/******************************************************************************
** Reverse colors for a face
**************************************************************************** */
void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op)
{
BMOIter fs_iter; /* selected faces iterator */
BMFace *fs; /* current face */
BMIter l_iter; /* iteration loop */
BLI_array_declare(cols);
MLoopCol *cols = NULL;
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
BMLoop *lf; /* current face loops */
int i = 0;
BLI_array_empty(cols);
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
/* current loop uv is the previous loop color */
BLI_array_growone(cols);
cols[i] = *lcol;
i++;
}
/* now that we have the uvs in the array, reverse! */
i = 0;
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
/* current loop uv is the previous loop color */
MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
*lcol = cols[(fs->len - i - 1)];
i++;
}
}
}
BLI_array_free(cols);
}
/******************************************************************************
** shortest vertex path select
**************************************************************************** */
typedef struct element_node {
BMVert *v; /* vertex */
BMVert *parent; /* node parent id */
float weight; /* node weight */
HeapNode *hn; /* heap node */
} element_node;
void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op)
{
BMOIter vs_iter /* , vs2_iter */; /* selected verts iterator */
BMIter v_iter; /* mesh verts iterator */
BMVert *vs, *sv, *ev; /* starting vertex, ending vertex */
BMVert *v; /* mesh vertex */
Heap *h = NULL;
element_node *vert_list = NULL;
int num_total = 0 /*, num_sels = 0 */, i = 0;
int type = BMO_slot_int_get(op, "type");
BMO_ITER(vs, &vs_iter, bm, op, "startv", BM_VERT) {
sv = vs;
}
BMO_ITER(vs, &vs_iter, bm, op, "endv", BM_VERT) {
ev = vs;
}
num_total = BM_mesh_elem_count(bm, BM_VERT);
/* allocate memory for the nodes */
vert_list = (element_node *)MEM_mallocN(sizeof(element_node) * num_total, "vertex nodes");
/* iterate through all the mesh vertices */
/* loop through all the vertices and fill the vertices/indices structure */
i = 0;
BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
vert_list[i].v = v;
vert_list[i].parent = NULL;
vert_list[i].weight = FLT_MAX;
BM_elem_index_set(v, i); /* set_inline */
i++;
}
bm->elem_index_dirty &= ~BM_VERT;
/*
** we now have everything we need, start Dijkstra path finding algorithm
*/
/* set the distance/weight of the start vertex to 0 */
vert_list[BM_elem_index_get(sv)].weight = 0.0f;
h = BLI_heap_new();
for (i = 0; i < num_total; i++) {
vert_list[i].hn = BLI_heap_insert(h, vert_list[i].weight, vert_list[i].v);
}
while (!BLI_heap_empty(h)) {
BMEdge *e;
BMIter e_i;
float v_weight;
/* take the vertex with the lowest weight out of the heap */
BMVert *v = (BMVert *)BLI_heap_popmin(h);
if (vert_list[BM_elem_index_get(v)].weight == FLT_MAX) /* this means that there is no path */
break;
v_weight = vert_list[BM_elem_index_get(v)].weight;
BM_ITER(e, &e_i, bm, BM_EDGES_OF_VERT, v) {
BMVert *u;
float e_weight = v_weight;
if (type == VPATH_SELECT_EDGE_LENGTH)
e_weight += len_v3v3(e->v1->co, e->v2->co);
else e_weight += 1.0f;
u = (e->v1 == v) ? e->v2 : e->v1;
if (e_weight < vert_list[BM_elem_index_get(u)].weight) { /* is this path shorter ? */
/* add it if so */
vert_list[BM_elem_index_get(u)].parent = v;
vert_list[BM_elem_index_get(u)].weight = e_weight;
/* we should do a heap update node function!!! :-/ */
BLI_heap_remove(h, vert_list[BM_elem_index_get(u)].hn);
BLI_heap_insert(h, e_weight, u);
}
}
}
/* now we trace the path (if it exists) */
v = ev;
while (vert_list[BM_elem_index_get(v)].parent != NULL) {
BMO_elem_flag_enable(bm, v, VERT_MARK);
v = vert_list[BM_elem_index_get(v)].parent;
}
BLI_heap_free(h, NULL);
MEM_freeN(vert_list);
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
}

View File

@@ -0,0 +1,1011 @@
/*
* ***** 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 and Levi Schooley.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <math.h>
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "DNA_meshdata_types.h"
#include "DNA_mesh_types.h"
#include "BKE_utildefines.h"
#include "BKE_tessmesh.h"
#include "BKE_bmesh.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "bmesh.h"
#include "bmesh_private.h"
/* ------- Bevel code starts here -------- */
BME_TransData_Head *BME_init_transdata(int bufsize)
{
BME_TransData_Head *td;
td = MEM_callocN(sizeof(BME_TransData_Head), "BM transdata header");
td->gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "BME_init_transdata gh");
td->ma = BLI_memarena_new(bufsize, "BME_TransData arena");
BLI_memarena_use_calloc(td->ma);
return td;
}
void BME_free_transdata(BME_TransData_Head *td)
{
BLI_ghash_free(td->gh, NULL, NULL);
BLI_memarena_free(td->ma);
MEM_freeN(td);
}
BME_TransData *BME_assign_transdata(
BME_TransData_Head *td, BMesh *bm, BMVert *v,
float *co, float *org, float *vec, float *loc,
float factor, float weight, float maxfactor, float *max)
{
BME_TransData *vtd;
int is_new = 0;
if (v == NULL) {
return NULL;
}
if ((vtd = BLI_ghash_lookup(td->gh, v)) == NULL && bm != NULL) {
vtd = BLI_memarena_alloc(td->ma, sizeof(*vtd));
BLI_ghash_insert(td->gh, v, vtd);
td->len++;
is_new = 1;
}
vtd->bm = bm;
vtd->v = v;
if (co != NULL) {
copy_v3_v3(vtd->co, co);
}
if (org == NULL && is_new) {
copy_v3_v3(vtd->org, v->co); /* default */
}
else if (org != NULL) {
copy_v3_v3(vtd->org, org);
}
if (vec != NULL) {
copy_v3_v3(vtd->vec, vec);
normalize_v3(vtd->vec);
}
vtd->loc = loc;
vtd->factor = factor;
vtd->weight = weight;
vtd->maxfactor = maxfactor;
vtd->max = max;
return vtd;
}
BME_TransData *BME_get_transdata(BME_TransData_Head *td, BMVert *v)
{
BME_TransData *vtd;
vtd = BLI_ghash_lookup(td->gh, v);
return vtd;
}
/* a hack (?) to use the transdata memarena to allocate floats for use with the max limits */
float *BME_new_transdata_float(BME_TransData_Head *td)
{
return BLI_memarena_alloc(td->ma, sizeof(float));
}
/* BM_disk_dissolve is a real mess, and crashes bevel if called instead of this.
* The drawback, though, is that this code doesn't merge customdata. */
static int BME_Bevel_Dissolve_Disk(BMesh *bm, BMVert *v)
{
BMIter iter;
BMEdge *e, *elast;
BMLoop *l1, *l2;
if (!BM_vert_is_manifold(bm, v)) {
return 0;
}
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
if (BM_edge_face_count(e) != 2) {
return 0;
}
}
if (BM_vert_edge_count(v) > 2) {
while (BM_vert_edge_count(v) > 2) {
e = v->e;
l1 = e->l;
l2 = l1->radial_next;
bmesh_jfke(bm, l1->f, l2->f, e);
}
e = v->e;
elast = bmesh_disk_nextedge(e, v);
bmesh_jekv(bm, e, v);
l1 = elast->l;
l2 = l1->radial_next;
bmesh_jfke(bm, l1->f, l2->f, elast);
}
return 1;
}
static int BME_bevel_is_split_vert(BMesh *bm, BMLoop *l)
{
/* look for verts that have already been added to the edge when
* beveling other polys; this can be determined by testing the
* vert and the edges around it for originality
*/
if ( !BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG) &&
BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG))
{
return 1;
}
return 0;
}
/* get a vector, vec, that points from v1->co to wherever makes sense to
* the bevel operation as a whole based on the relationship between v1 and v2
* (won't necessarily be a vec from v1->co to v2->co, though it probably will be);
* the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */
static int BME_bevel_get_vec(float *vec, BMVert *v1, BMVert *v2, BME_TransData_Head *td)
{
BME_TransData *vtd1, *vtd2;
vtd1 = BME_get_transdata(td, v1);
vtd2 = BME_get_transdata(td, v2);
if (!vtd1 || !vtd2) {
//printf("BME_bevel_get_vec() got called without proper BME_TransData\n");
return -1;
}
/* compare the transform origins to see if we can use the vert co's;
* if they belong to different origins, then we will use the origins to determine
* the vector */
if (compare_v3v3(vtd1->org, vtd2->org, 0.000001f)) {
sub_v3_v3v3(vec, v2->co, v1->co);
if (len_v3(vec) < 0.000001f) {
zero_v3(vec);
}
return 0;
}
else {
sub_v3_v3v3(vec, vtd2->org, vtd1->org);
if (len_v3(vec) < 0.000001f) {
zero_v3(vec);
}
return 1;
}
}
/* "Projects" a vector perpendicular to vec2 against vec1, such that
* the projected vec1 + vec2 has a min distance of 1 from the "edge" defined by vec2.
* note: the direction, is_forward, is used in conjunction with up_vec to determine
* whether this is a convex or concave corner. If it is a concave corner, it will
* be projected "backwards." If vec1 is before vec2, is_forward should be 0 (we are projecting backwards).
* vec1 is the vector to project onto (expected to be normalized)
* vec2 is the direction of projection (pointing away from vec1)
* up_vec is used for orientation (expected to be normalized)
* returns the length of the projected vector that lies along vec1 */
static float BME_bevel_project_vec(float *vec1, float *vec2, float *up_vec, int is_forward, BME_TransData_Head *UNUSED(td))
{
float factor, vec3[3], tmp[3], c1, c2;
cross_v3_v3v3(tmp, vec1, vec2);
normalize_v3(tmp);
factor = dot_v3v3(up_vec, tmp);
if ((factor > 0 && is_forward) || (factor < 0 && !is_forward)) {
cross_v3_v3v3(vec3, vec2, tmp); /* hmm, maybe up_vec should be used instead of tmp */
}
else {
cross_v3_v3v3(vec3, tmp, vec2); /* hmm, maybe up_vec should be used instead of tmp */
}
normalize_v3(vec3);
c1 = dot_v3v3(vec3, vec1);
c2 = dot_v3v3(vec1, vec1);
if (fabsf(c1) < 0.000001f || fabsf(c2) < 0.000001f) {
factor = 0.0f;
}
else {
factor = c2 / c1;
}
return factor;
}
/* BME_bevel_split_edge() is the main math work-house; its responsibilities are:
* using the vert and the loop passed, get or make the split vert, set its coordinates
* and transform properties, and set the max limits.
* Finally, return the split vert. */
static BMVert *BME_bevel_split_edge(BMesh *bm, BMVert *v, BMVert *v1, BMLoop *l, float *up_vec, float value, BME_TransData_Head *td)
{
BME_TransData *vtd, *vtd1, *vtd2;
BMVert *sv, *v2, *v3, *ov;
BMLoop *lv1, *lv2;
BMEdge *ne, *e1, *e2;
float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3];
int is_edge, forward, is_split_vert;
if (l == NULL) {
/* what you call operator overloading in C :)
* I wanted to use the same function for both wire edges and poly loops
* so... here we walk around edges to find the needed verts */
forward = 1;
is_split_vert = 0;
if (v->e == NULL) {
//printf("We can't split a loose vert's edge!\n");
return NULL;
}
e1 = v->e; /* we just use the first two edges */
e2 = bmesh_disk_nextedge(v->e, v);
if (e1 == e2) {
//printf("You need at least two edges to use BME_bevel_split_edge()\n");
return NULL;
}
v2 = BM_edge_other_vert(e1, v);
v3 = BM_edge_other_vert(e2, v);
if (v1 != v2 && v1 != v3) {
//printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n");
return NULL;
}
if (v1 == v2) {
v2 = v3;
}
else {
e1 = e2;
}
ov = BM_edge_other_vert(e1, v);
sv = BM_edge_split(bm, v, e1, &ne, 0);
//BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
//BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
//BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
BME_bevel_get_vec(vec1, v1, v, td);
BME_bevel_get_vec(vec2, v2, v, td);
cross_v3_v3v3(t_up_vec, vec1, vec2);
normalize_v3(t_up_vec);
up_vec = t_up_vec;
}
else {
/* establish loop direction */
if (l->v == v) {
forward = 1;
lv1 = l->next;
lv2 = l->prev;
v1 = l->next->v;
v2 = l->prev->v;
}
else if (l->next->v == v) {
forward = 0;
lv1 = l;
lv2 = l->next->next;
v1 = l->v;
v2 = l->next->next->v;
}
else {
//printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n");
return NULL;
}
if (BME_bevel_is_split_vert(bm, lv1)) {
is_split_vert = 1;
sv = v1;
v1 = forward ? l->next->next->v : l->prev->v;
}
else {
is_split_vert = 0;
ov = BM_edge_other_vert(l->e, v);
sv = BM_edge_split(bm, v, l->e, &ne, 0);
//BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
//BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
//BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
}
if (BME_bevel_is_split_vert(bm, lv2)) {
v2 = forward ? lv2->prev->v : lv2->next->v;
}
}
is_edge = BME_bevel_get_vec(vec1, v, v1, td); /* get the vector we will be projecting onto */
BME_bevel_get_vec(vec2, v, v2, td); /* get the vector we will be projecting parallel to */
len = len_v3(vec1);
normalize_v3(vec1);
vtd = BME_get_transdata(td, sv);
vtd1 = BME_get_transdata(td, v);
vtd2 = BME_get_transdata(td, v1);
if (vtd1->loc == NULL) {
/* this is a vert with data only for calculating initial weights */
if (vtd1->weight < 0) {
vtd1->weight = 0;
}
scale = vtd1->weight / vtd1->factor;
if (!vtd1->max) {
vtd1->max = BME_new_transdata_float(td);
*vtd1->max = -1;
}
}
else {
scale = vtd1->weight;
}
vtd->max = vtd1->max;
if (is_edge && vtd1->loc != NULL) {
maxfactor = vtd1->maxfactor;
}
else {
maxfactor = scale * BME_bevel_project_vec(vec1, vec2, up_vec, forward, td);
if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) {
maxfactor = vtd->maxfactor;
}
}
dis = BMO_elem_flag_test(bm, v1, BME_BEVEL_ORIG) ? len / 3 : len / 2;
if (is_edge || dis > maxfactor * value) {
dis = maxfactor * value;
}
madd_v3_v3v3fl(sv->co, v->co, vec1, dis);
sub_v3_v3v3(vec1, sv->co, vtd1->org);
dis = len_v3(vec1);
normalize_v3(vec1);
BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max);
return sv;
}
#if 0 /* UNUSED */
static float BME_bevel_set_max(BMVert *v1, BMVert *v2, float value, BME_TransData_Head *td)
{
BME_TransData *vtd1, *vtd2;
float max, fac1, fac2, vec1[3], vec2[3], vec3[3];
BME_bevel_get_vec(vec1, v1, v2, td);
vtd1 = BME_get_transdata(td, v1);
vtd2 = BME_get_transdata(td, v2);
if (vtd1->loc == NULL) {
fac1 = 0;
}
else {
copy_v3_v3(vec2, vtd1->vec);
mul_v3_fl(vec2, vtd1->factor);
if (dot_v3v3(vec1, vec1)) {
project_v3_v3v3(vec2, vec2, vec1);
fac1 = len_v3(vec2) / value;
}
else {
fac1 = 0;
}
}
if (vtd2->loc == NULL) {
fac2 = 0;
}
else {
copy_v3_v3(vec3, vtd2->vec);
mul_v3_fl(vec3, vtd2->factor);
if (dot_v3v3(vec1, vec1)) {
project_v3_v3v3(vec2, vec3, vec1);
fac2 = len_v3(vec2) / value;
}
else {
fac2 = 0;
}
}
if (fac1 || fac2) {
max = len_v3(vec1) / (fac1 + fac2);
if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
*vtd1->max = max;
}
if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
*vtd2->max = max;
}
}
else {
max = -1;
}
return max;
}
#endif
#if 0 /* UNUSED */
static BMVert *BME_bevel_wire(BMesh *bm, BMVert *v, float value, int res, int UNUSED(options), BME_TransData_Head *td)
{
BMVert *ov1, *ov2, *v1, *v2;
ov1 = BM_edge_other_vert(v->e, v);
ov2 = BM_edge_other_vert(bmesh_disk_nextedge(v->e, v), v);
/* split the edges */
v1 = BME_bevel_split_edge(bm, v, ov1, NULL, NULL, value, td);
BMO_elem_flag_enable(bm, v1, BME_BEVEL_NONMAN);
v2 = BME_bevel_split_edge(bm, v, ov2, NULL, NULL, value, td);
BMO_elem_flag_enable(bm, v2, BME_BEVEL_NONMAN);
if (value > 0.5) {
BME_bevel_set_max(v1, ov1, value, td);
BME_bevel_set_max(v2, ov2, value, td);
}
/* remove the original vert */
if (res) {
/* bmesh_jekv; */
//void BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, int calcnorm){
//hrm, why is there a fac here? it just removes a vert
BM_vert_collapse_edges(bm, v->e, v);
}
return v1;
}
#endif
static BMLoop *BME_bevel_edge(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
{
BMVert *v1, *v2, *kv;
BMLoop *kl = NULL, *nl;
BMEdge *e, *ke, *se;
BMFace *f, *jf;
f = l->f;
e = l->e;
/* sanity check */
if ( !BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) &&
(BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_BEVEL)))
{
return l;
}
/* checks and operations for prev edge */
/* first, check to see if this edge was inset previously */
if ( !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG) &&
!BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN))
{
kl = l->prev->radial_next;
kl = (kl->v == l->v) ? kl->prev : kl->next;
kv = l->v;
}
else {
kv = NULL;
}
/* get/make the first vert to be used in SFME */
if (BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN)) {
v1 = l->v;
}
else { /* we'll need to split the previous edge */
v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
}
/* if we need to clean up geometry... */
if (kv) {
se = l->next->e;
jf = NULL;
if (kl->v == kv) {
BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
ke = kl->e;
/* BMESH-TODO: jfke doesn't handle customdata */
jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
BM_vert_collapse_edges(bm, ke, kv);
}
else {
BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
ke = kl->e;
/* BMESH-TODO: jfke doesn't handle customdata */
jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
BM_vert_collapse_edges(bm, ke, kv);
}
/* find saved loop pointer */
l = se->l;
while (l->f != jf) {
l = bmesh_radial_nextloop(l);
BLI_assert(l != se->l);
}
l = l->prev;
}
/* checks and operations for the next edge */
/* first, check to see if this edge was inset previously */
if ( !BMO_elem_flag_test(bm, l->next->e, BME_BEVEL_ORIG) &&
!BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN))
{
kl = l->next->radial_next;
kl = (kl->v == l->next->v) ? kl->prev : kl->next;
kv = l->next->v;
}
else {
kv = NULL;
}
/* get/make the second vert to be used in SFME */
if (BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN)) {
v2 = l->next->v;
}
else { /* we'll need to split the next edge */
v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
}
/* if we need to clean up geometry... */
if (kv) {
se = l->e;
jf = NULL;
if (kl->v == kv) {
BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
ke = kl->e;
/* BMESH-TODO: jfke doesn't handle customdata */
jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
BM_vert_collapse_edges(bm, ke, kv);
}
else {
BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
ke = kl->e;
/* BMESH-TODO: jfke doesn't handle customdata */
jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
BM_vert_collapse_edges(bm, ke, kv);
}
/* find saved loop pointer */
l = se->l;
while (l->f != jf) {
l = bmesh_radial_nextloop(l);
BLI_assert(l != se->l);
}
}
if (!BMO_elem_flag_test(bm, v1, BME_BEVEL_NONMAN) || !BMO_elem_flag_test(bm, v2, BME_BEVEL_NONMAN)) {
BM_face_split(bm, f, v2, v1, &l, e);
BMO_elem_flag_enable(bm, l->e, BME_BEVEL_BEVEL);
l = l->radial_next;
}
if (l->f != f){
//printf("Whoops! You got something out of order in BME_bevel_edge()!\n");
}
return l;
}
static BMLoop *BME_bevel_vert(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
{
BMVert *v1, *v2;
BMFace *f;
/* get/make the first vert to be used in SFME */
/* may need to split the previous edge */
v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
/* get/make the second vert to be used in SFME */
/* may need to split this edge (so move l) */
l = l->prev;
v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
l = l->next->next;
/* "cut off" this corner */
f = BM_face_split(bm, l->f, v2, v1, NULL, l->e);
return l;
}
/*
* BME_bevel_poly
*
* Polygon inset tool:
*
* Insets a polygon/face based on the flagss of its vertices
* and edges. Used by the bevel tool only, for now.
* The parameter "value" is the distance to inset (should be negative).
* The parameter "options" is not currently used.
*
* Returns -
* A BMFace pointer to the resulting inner face.
*/
static BMFace *BME_bevel_poly(BMesh *bm, BMFace *f, float value, int options, BME_TransData_Head *td)
{
BMLoop *l/*, *o */;
BME_TransData *vtd1, *vtd2;
float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1;
int len, i;
BMIter iter;
zero_v3(up_vec);
/* find a good normal for this face (there's better ways, I'm sure) */
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
BME_bevel_get_vec(vec1, l->v, l->next->v, td);
BME_bevel_get_vec(vec2, l->prev->v, l->v, td);
cross_v3_v3v3(vec3, vec2, vec1);
add_v3_v3(up_vec, vec3);
}
normalize_v3(up_vec);
/* Can't use a BM_LOOPS_OF_FACE iterator here, because the loops are being modified
* and so the end condition will never hi */
for (l = BM_FACE_FIRST_LOOP(f)->prev, i = 0, len = f->len; i < len; i++, l = l->next) {
if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
max = 1.0f;
l = BME_bevel_edge(bm, l, value, options, up_vec, td);
}
else if ( BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) &&
BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
!BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_BEVEL))
{
max = 1.0f;
l = BME_bevel_vert(bm, l, value, options, up_vec, td);
}
}
f = l->f;
/* max pass */
if (value > 0.5f && max > 0) {
max = -1;
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
BME_bevel_get_vec(vec1, l->v, l->next->v, td);
vtd1 = BME_get_transdata(td, l->v);
vtd2 = BME_get_transdata(td, l->next->v);
if (vtd1->loc == NULL) {
fac1 = 0;
}
else {
copy_v3_v3(vec2, vtd1->vec);
mul_v3_fl(vec2, vtd1->factor);
if (dot_v3v3(vec1, vec1)) {
project_v3_v3v3(vec2, vec2, vec1);
fac1 = len_v3(vec2) / value;
}
else {
fac1 = 0;
}
}
if (vtd2->loc == NULL) {
fac2 = 0;
}
else {
copy_v3_v3(vec3, vtd2->vec);
mul_v3_fl(vec3, vtd2->factor);
if (dot_v3v3(vec1, vec1)) {
project_v3_v3v3(vec2, vec3, vec1);
fac2 = len_v3(vec2) / value;
}
else {
fac2 = 0;
}
}
if (fac1 || fac2) {
max = len_v3(vec1)/(fac1 + fac2);
if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
*vtd1->max = max;
}
if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
*vtd2->max = max;
}
}
}
}
}
/* return l->f; */
return NULL;
}
static void BME_bevel_add_vweight(BME_TransData_Head *td, BMesh *bm, BMVert *v, float weight, float factor, int options)
{
BME_TransData *vtd;
if (BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) return;
BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
if ((vtd = BME_get_transdata(td, v))) {
if (options & BME_BEVEL_EMIN) {
vtd->factor = 1.0;
if (vtd->weight < 0 || weight < vtd->weight) {
vtd->weight = weight;
}
}
else if (options & BME_BEVEL_EMAX) {
vtd->factor = 1.0;
if (weight > vtd->weight) {
vtd->weight = weight;
}
}
else if (vtd->weight < 0) {
vtd->factor = factor;
vtd->weight = weight;
}
else {
vtd->factor += factor; /* increment number of edges with weights (will be averaged) */
vtd->weight += weight; /* accumulate all the weights */
}
}
else {
/* we'll use vtd->loc == NULL to mark that this vert is not moving */
vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL);
}
}
static void bevel_init_verts(BMesh *bm, int options, BME_TransData_Head *td)
{
BMVert *v;
BMIter iter;
float weight;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
weight = 0.0;
if (!BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) {
/* modifiers should not use selection */
if(options & BME_BEVEL_SELECT){
if(BM_elem_flag_test(v, BM_ELEM_SELECT)) weight = 1.0;
}
/* bevel weight NYI */
else if(options & BME_BEVEL_WEIGHT)
weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT);
else
weight = 1.0;
if(weight > 0.0){
BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, weight, -1, NULL);
}
}
}
}
static void bevel_init_edges(BMesh *bm, int options, BME_TransData_Head *td)
{
BMEdge *e;
int count;
float weight;
BMIter iter;
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
weight = 0.0;
if (!BMO_elem_flag_test(bm, e, BME_BEVEL_NONMAN)) {
if(options & BME_BEVEL_SELECT){
if(BM_elem_flag_test(e, BM_ELEM_SELECT)) weight = 1.0;
}
else if(options & BME_BEVEL_WEIGHT) {
weight = BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT);
}
else {
weight = 1.0;
}
if(weight > 0.0){
BMO_elem_flag_enable(bm, e, BME_BEVEL_BEVEL);
BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_BEVEL);
BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_BEVEL);
BME_bevel_add_vweight(td, bm, e->v1, weight, 1.0, options);
BME_bevel_add_vweight(td, bm, e->v2, weight, 1.0, options);
}
}
}
/* clean up edges with 2 faces that share more than one edg */
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL)) {
count = BM_face_share_edges(e->l->f, e->l->radial_next->f);
if(count > 1) BMO_elem_flag_disable(bm, e, BME_BEVEL_BEVEL);
}
}
}
static BMesh *BME_bevel_initialize(BMesh *bm, int options, int UNUSED(defgrp_index), float UNUSED(angle), BME_TransData_Head *td)
{
BMVert *v/*, *v2 */;
BMEdge *e/*, *curedg */;
BMFace *f;
BMIter iter;
int /* wire, */ len;
/* tag non-manifold geometr */
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
if(v->e){
BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL);
if (!BM_vert_is_manifold(bm, v))
BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
/* test wire ver */
len = BM_vert_edge_count(v);
if (len == 2 && BM_vert_is_wire(bm, v))
BMO_elem_flag_disable(bm, v, BME_BEVEL_NONMAN);
}
else
BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
}
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
if (!BM_edge_is_manifold(bm, e)) {
BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_NONMAN);
BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_NONMAN);
BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
}
if(BMO_elem_flag_test(bm, e->v1, BME_BEVEL_NONMAN) || BMO_elem_flag_test(bm, e->v2, BME_BEVEL_NONMAN)) BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
}
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL)
BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
if(options & BME_BEVEL_VERT) bevel_init_verts(bm, options, td);
else bevel_init_edges(bm, options, td);
return bm;
}
#if 0
static BMesh *BME_bevel_reinitialize(BMesh *bm)
{
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter iter;
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
}
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
}
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
}
return bm;
}
#endif
/**
* BME_bevel_mesh
*
* Mesh beveling tool:
*
* Bevels an entire mesh. It currently uses the flags of
* its vertices and edges to track topological changes.
* The parameter "value" is the distance to inset (should be negative).
* The parameter "options" is not currently used.
*
* Returns -
* A BMesh pointer to the BM passed as a parameter.
*/
static BMesh *BME_bevel_mesh(BMesh *bm, float value, int UNUSED(res), int options, int UNUSED(defgrp_index), BME_TransData_Head *td)
{
BMVert *v;
BMEdge *e, *curedge;
BMLoop *l, *l2;
BMFace *f;
BMIter iter;
/* unsigned int i, len; */
/* bevel poly */
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if(BMO_elem_flag_test(bm, f, BME_BEVEL_ORIG)) {
BME_bevel_poly(bm, f, value, options, td);
}
}
/* get rid of beveled edge */
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, e, BME_BEVEL_ORIG)) {
BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
}
}
/* link up corners and cli */
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if(BMO_elem_flag_test(bm, v, BME_BEVEL_ORIG) && BMO_elem_flag_test(bm, v, BME_BEVEL_BEVEL)) {
curedge = v->e;
do{
l = curedge->l;
l2 = l->radial_next;
if(l->v != v) l = l->next;
if(l2->v != v) l2 = l2->next;
if(l->f->len > 3)
BM_face_split(bm, l->f, l->next->v, l->prev->v, &l, l->e); /* clip this corner off */
if(l2->f->len > 3)
BM_face_split(bm, l2->f, l2->next->v, l2->prev->v, &l, l2->e); /* clip this corner off */
curedge = bmesh_disk_nextedge(curedge, v);
} while(curedge != v->e);
BME_Bevel_Dissolve_Disk(bm, v);
}
}
/* Debug print, remov */
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
if(f->len == 2){
printf("warning");
}
}
return bm;
}
BMesh *BME_bevel(BMEditMesh *em, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd)
{
BMesh *bm = em->bm;
BMVert *v;
BMIter iter;
BME_TransData_Head *td;
BME_TransData *vtd;
int i;
double fac = 1, d;
td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE);
/* recursion math courtesy of Martin Poirier (theeth) */
for (i = 0; i < res - 1; i++) {
if (i == 0) fac += 1.0f / 3.0f; else fac += 1.0f / (3 * i * 2.0f);
}
d = 1.0f / fac;
for (i = 0; i < res || (res == 0 && i == 0); i++) {
BMO_push(bm, NULL);
BME_bevel_initialize(bm, options, defgrp_index, angle, td);
//if (i != 0) BME_bevel_reinitialize(bm);
bmesh_begin_edit(bm, 0);
BME_bevel_mesh(bm, (float)d, res, options, defgrp_index, td);
bmesh_end_edit(bm, 0);
d /= (i == 0) ? 3.0 : 2.0;
BMO_pop(bm);
}
BMEdit_RecalcTesselation(em);
/* interactive preview? */
if (rtd) {
*rtd = td;
return bm;
}
/* otherwise apply transforms */
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
if ( (vtd = BME_get_transdata(td, v)) ) {
if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) {
d = *vtd->max;
}
else {
d = value;
}
madd_v3_v3v3fl(v->co, vtd->org, vtd->vec, vtd->factor * d);
}
}
BME_free_transdata(td);
return bm;
}

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