copying bmesh dir on its own from bmesh branch
This commit is contained in:
137
source/blender/bmesh/CMakeLists.txt
Normal file
137
source/blender/bmesh/CMakeLists.txt
Normal file
@@ -0,0 +1,137 @@
|
||||
# $Id: CMakeLists.txt 31746 2010-09-04 05:31:25Z joeedh $
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# The Original Code is Copyright (C) 2006, Blender Foundation
|
||||
# All rights reserved.
|
||||
#
|
||||
# The Original Code is: all of this file.
|
||||
#
|
||||
# Contributor(s): Jacques Beaurain.
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
set(INC
|
||||
.
|
||||
intern
|
||||
operators
|
||||
../avi
|
||||
../blenfont
|
||||
../blenkernel
|
||||
../blenlib
|
||||
../blenloader
|
||||
../editors/include
|
||||
../editors/mesh
|
||||
../gpu
|
||||
../ikplugin
|
||||
../imbuf
|
||||
../makesdna
|
||||
../makesrna
|
||||
../modifiers
|
||||
../nodes
|
||||
../render/extern/include
|
||||
../../../extern/glew/include
|
||||
../../../intern/audaspace/intern
|
||||
../../../intern/bsp/extern
|
||||
../../../intern/decimation/extern
|
||||
../../../intern/elbeem/extern
|
||||
../../../intern/guardedalloc
|
||||
../../../intern/iksolver/extern
|
||||
../../../intern/memutil
|
||||
../../../intern/mikktspace
|
||||
../../../intern/opennl/extern
|
||||
../../../intern/smoke/extern
|
||||
# XXX - BAD LEVEL CALL WM_api.h
|
||||
../../../source/blender/windowmanager
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(SRC
|
||||
operators/bmo_bevel.c
|
||||
operators/bmo_connect.c
|
||||
operators/bmo_create.c
|
||||
operators/bmo_dissolve.c
|
||||
operators/bmo_dupe.c
|
||||
operators/bmo_edgesplit.c
|
||||
operators/bmo_extrude.c
|
||||
operators/bmo_join_triangles.c
|
||||
operators/bmo_mesh_conv.c
|
||||
operators/bmo_mirror.c
|
||||
operators/bmo_primitive.c
|
||||
operators/bmo_removedoubles.c
|
||||
operators/bmo_subdivide.c
|
||||
operators/bmo_subdivide.h
|
||||
operators/bmo_triangulate.c
|
||||
operators/bmo_utils.c
|
||||
|
||||
intern/bmesh_newcore.c
|
||||
intern/bmesh_interp.c
|
||||
intern/bmesh_iterators.c
|
||||
intern/bmesh_iterators_inline.c
|
||||
intern/bmesh_marking.c
|
||||
intern/bmesh_mesh.c
|
||||
intern/bmesh_mods.c
|
||||
intern/bmesh_structure.h
|
||||
intern/bmesh_construct.c
|
||||
intern/bmesh_operators_private.h
|
||||
intern/bmesh_structure.c
|
||||
intern/bmesh_polygon.c
|
||||
intern/bmesh_queries.c
|
||||
intern/bmesh_opdefines.c
|
||||
intern/bmesh_eulers.c
|
||||
intern/bmesh_operators.c
|
||||
intern/bmesh_private.h
|
||||
intern/bmesh_walkers.c
|
||||
intern/bmesh_walkers_impl.c
|
||||
intern/bmesh_walkers_private.h
|
||||
intern/bmesh_inline.c
|
||||
|
||||
tools/BME_bevel.c
|
||||
bmesh.h
|
||||
bmesh_class.h
|
||||
bmesh_error.h
|
||||
bmesh_iterators.h
|
||||
bmesh_marking.h
|
||||
bmesh_operator_api.h
|
||||
bmesh_operators.h
|
||||
bmesh_queries.h
|
||||
bmesh_walkers.h
|
||||
)
|
||||
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
||||
if(WITH_LZO)
|
||||
add_definitions(-DWITH_LZO)
|
||||
list(APPEND INC_SYS
|
||||
../../../extern/lzo/minilzo
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_LZMA)
|
||||
add_definitions(-DWITH_LZMA)
|
||||
list(APPEND INC_SYS
|
||||
../../../extern/lzma
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}")
|
||||
40
source/blender/bmesh/SConscript
Normal file
40
source/blender/bmesh/SConscript
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/python
|
||||
Import ('env')
|
||||
|
||||
cflags=''
|
||||
"""
|
||||
sources = ['intern/bmesh_eulers.c']
|
||||
sources.append('intern/bmesh_mesh.c')
|
||||
sources.append('intern/bmesh_polygon.c')
|
||||
sources.append('intern/bmesh_structure.c')
|
||||
sources.append('intern/bmesh_marking.c')
|
||||
|
||||
sources.append('intern/bmesh_construct.c')
|
||||
sources.append('intern/bmesh_interp.c')
|
||||
sources.append('intern/bmesh_filters.c')
|
||||
sources.append('intern/bmesh_iterators.c')
|
||||
sources.append('intern/bmesh_mods.c')
|
||||
sources.append('intern/bmesh_queries.c')
|
||||
sources.append('intern/bmesh_operators.c')
|
||||
"""
|
||||
#sources.append('api/BME_walkers.c')
|
||||
|
||||
|
||||
sources = env.Glob('intern/*.c')
|
||||
sources += env.Glob('operators/*.c')
|
||||
|
||||
#sources += env.Glob('tools/*.c')
|
||||
|
||||
incs = ['#/intern/guardedalloc']
|
||||
incs.append('../blenlib')
|
||||
incs.append('../blenloader')
|
||||
incs.append('../makesdna')
|
||||
incs.append('../makesrna')
|
||||
incs.append('../blenkernel')
|
||||
incs.append('./')
|
||||
incs.append('./intern')
|
||||
incs.append('../editors/mesh')
|
||||
incs.append('../editors/include')
|
||||
|
||||
defs = []
|
||||
env.BlenderLib ( libname = 'bf_bmesh', sources = sources, includes = Split(incs), libtype = 'core', defines=defs, priority=100, compileflags=cflags )
|
||||
380
source/blender/bmesh/bmesh.h
Normal file
380
source/blender/bmesh/bmesh.h
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Geoffrey Bantle, Levi Schooley.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_H__
|
||||
#define __BMESH_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh_class.h"
|
||||
|
||||
/*
|
||||
* short introduction:
|
||||
*
|
||||
* the bmesh structure is a boundary representation, supporting non-manifold
|
||||
* locally modifiable topology. the API is designed to allow clean, maintainable
|
||||
* code, that never (or almost never) directly inspects the underlying structure.
|
||||
*
|
||||
* The API includes iterators, including many useful topological iterators;
|
||||
* walkers, which walk over a mesh, without the risk of hitting the recursion
|
||||
* limit; operators, which are logical, reusable mesh modules; topological
|
||||
* modification functions (like split face, join faces, etc), which are used for
|
||||
* topological manipulations; and some (not yet finished) geometric utility
|
||||
* functions.
|
||||
*
|
||||
* some definitions:
|
||||
*
|
||||
* tool flags: private flags for tools. each operator has it's own private
|
||||
* tool flag "layer", which it can use to flag elements.
|
||||
* tool flags are also used by various other parts of the api.
|
||||
* header flags: stores persistent flags, such as selection state, hide state,
|
||||
* etc. be careful of touching these.
|
||||
*/
|
||||
|
||||
/*forward declarations*/
|
||||
struct BMesh;
|
||||
struct BMVert;
|
||||
struct BMEdge;
|
||||
struct BMFace;
|
||||
struct BMLoop;
|
||||
struct BMOperator;
|
||||
struct Mesh;
|
||||
struct EditMesh;
|
||||
|
||||
/*
|
||||
* BMHeader
|
||||
*
|
||||
* All mesh elements begin with a BMHeader. This structure
|
||||
* hold several types of data
|
||||
*
|
||||
* 1: The type of the element (vert, edge, loop or face)
|
||||
* 2: Persistant "header" flags/markings (sharp, seam, select, hidden, ect)
|
||||
note that this is different from the "tool" flags.
|
||||
* 3: Unique ID in the bmesh.
|
||||
* 4: some elements for internal record keeping.
|
||||
*
|
||||
*/
|
||||
|
||||
/* BMHeader->htype (char) */
|
||||
#define BM_VERT 1
|
||||
#define BM_EDGE 2
|
||||
#define BM_LOOP 4
|
||||
#define BM_FACE 8
|
||||
#define BM_ALL (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)
|
||||
|
||||
/* BMHeader->hflag (char) */
|
||||
#define BM_ELEM_SELECT (1 << 0)
|
||||
#define BM_ELEM_HIDDEN (1 << 1)
|
||||
#define BM_ELEM_SEAM (1 << 2)
|
||||
#define BM_ELEM_SMOOTH (1 << 3) /* used for faces and edges, note from the user POV,
|
||||
* this is a sharp edge when disabled */
|
||||
|
||||
#define BM_ELEM_TAG (1 << 4) /* internal flag, used for ensuring correct normals
|
||||
* during multires interpolation, and any other time
|
||||
* when temp tagging is handy.
|
||||
* always assume dirty & clear before use. */
|
||||
|
||||
/* we have 3 spare flags which is awesome but since we're limited to 8
|
||||
* only add new flags with care! - campbell */
|
||||
/* #define BM_ELEM_SPARE (1<<5) */
|
||||
/* #define BM_ELEM_SPARE (1<<6) */
|
||||
/* #define BM_ELEM_NONORMCALC (1<<7) */ /* UNUSED */
|
||||
|
||||
/* stub */
|
||||
void bmesh_error(void);
|
||||
|
||||
/* Mesh Level Ops */
|
||||
extern int bm_mesh_allocsize_default[4];
|
||||
|
||||
/* ob is needed by multires */
|
||||
BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4]);
|
||||
BMesh *BM_mesh_copy(BMesh *bmold);
|
||||
void BM_mesh_free(BMesh *bm);
|
||||
|
||||
/* frees mesh, but not actual BMesh struct */
|
||||
void BM_mesh_data_free(BMesh *bm);
|
||||
void BM_mesh_normals_update(BMesh *bm);
|
||||
|
||||
/* Construction */
|
||||
BMVert *BM_vert_create(BMesh *bm, const float co[3], const BMVert *example);
|
||||
BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble);
|
||||
BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble);
|
||||
|
||||
BMFace *BM_face_create_quad_tri_v(BMesh *bm,
|
||||
BMVert **verts, int len,
|
||||
const BMFace *example, const int nodouble);
|
||||
|
||||
/* easier to use version of BM_face_create_quad_tri_v.
|
||||
* creates edges if necassary. */
|
||||
BMFace *BM_face_create_quad_tri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
|
||||
const BMFace *example, const int nodouble);
|
||||
|
||||
/* makes an ngon from an unordered list of edges. v1 and v2 must be the verts
|
||||
* defining edges[0], and define the winding of the new face. */
|
||||
BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble);
|
||||
|
||||
/* stuff for dealing with header flags */
|
||||
BM_INLINE char BM_elem_flag_test(const void *element, const char hflag);
|
||||
|
||||
/* stuff for dealing with header flags */
|
||||
BM_INLINE void BM_elem_flag_enable(void *element, const char hflag);
|
||||
|
||||
/* stuff for dealing with header flags */
|
||||
BM_INLINE void BM_elem_flag_disable(void *element, const char hflag);
|
||||
|
||||
/* stuff for dealing BM_elem_flag_toggle header flags */
|
||||
BM_INLINE void BM_elem_flag_toggle(void *element, const char hflag);
|
||||
BM_INLINE void BM_elem_flag_merge(void *element_a, void *element_b);
|
||||
|
||||
/* notes on BM_elem_index_set(...) usage,
|
||||
* Set index is sometimes abused as temp storage, other times we cant be
|
||||
* sure if the index values are valid because certain operations have modified
|
||||
* the mesh structure.
|
||||
*
|
||||
* To set the elements to valid indicies 'BM_mesh_elem_index_ensure' should be used
|
||||
* rather then adding inline loops, however there are cases where we still
|
||||
* set the index directly
|
||||
*
|
||||
* In an attempt to manage this, here are 3 tags Im adding to uses of
|
||||
* 'BM_elem_index_set'
|
||||
*
|
||||
* - 'set_inline' -- since the data is already being looped over set to a
|
||||
* valid value inline.
|
||||
*
|
||||
* - 'set_dirty!' -- intentionally sets the index to an invalid value,
|
||||
* flagging 'bm->elem_index_dirty' so we dont use it.
|
||||
*
|
||||
* - 'set_ok' -- this is valid use since the part of the code is low level.
|
||||
*
|
||||
* - 'set_ok_invalid' -- set to -1 on purpose since this should not be
|
||||
* used without a full array re-index, do this on
|
||||
* adding new vert/edge/faces since they may be added at
|
||||
* the end of the array.
|
||||
*
|
||||
* - 'set_loop' -- currently loop index values are not used used much so
|
||||
* assume each case they are dirty.
|
||||
* - campbell */
|
||||
|
||||
BM_INLINE void BM_elem_index_set(void *element, const int index);
|
||||
BM_INLINE int BM_elem_index_get(const void *element);
|
||||
|
||||
/* todo */
|
||||
BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts);
|
||||
|
||||
/* copies loop data from adjacent faces */
|
||||
void BM_face_copy_shared(BMesh *bm, BMFace *f);
|
||||
|
||||
/* copies attributes, e.g. customdata, header flags, etc, from one element
|
||||
* to another of the same type.*/
|
||||
void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target);
|
||||
|
||||
/* Modification */
|
||||
/* join two adjacent faces together along an edge. note that
|
||||
* the faces must only be joined by on edge. e is the edge you
|
||||
* wish to dissolve.*/
|
||||
BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e);
|
||||
|
||||
/* generic, flexible join faces function; note that most everything uses
|
||||
* this, including BM_faces_join_pair */
|
||||
BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface);
|
||||
|
||||
/* split a face along two vertices. returns the newly made face, and sets
|
||||
* the nl member to a loop in the newly created edge.*/
|
||||
BMFace *BM_face_split(BMesh *bm, BMFace *f,
|
||||
BMVert *v1, BMVert *v2,
|
||||
struct BMLoop **nl, BMEdge *example);
|
||||
|
||||
/* these 2 functions are very similar */
|
||||
BMEdge* BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, const int join_faces);
|
||||
BMEdge* BM_vert_collapse_edges(BMesh *bm, BMEdge *ke, BMVert *kv);
|
||||
|
||||
|
||||
/* splits an edge. ne is set to the new edge created. */
|
||||
BMVert *BM_edge_split(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent);
|
||||
|
||||
/* split an edge multiple times evenly */
|
||||
BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts);
|
||||
|
||||
/* connect two verts together, through a face they share. this function may
|
||||
* be removed in the future. */
|
||||
BMEdge *BM_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf);
|
||||
|
||||
/* rotates an edge topologically, either clockwise (if ccw=0) or counterclockwise
|
||||
* (if ccw is 1). */
|
||||
BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw);
|
||||
|
||||
/* Rip a single face from a vertex fan */
|
||||
BMVert *BM_vert_rip(BMesh *bm, BMFace *sf, BMVert *sv);
|
||||
|
||||
/*updates a face normal*/
|
||||
void BM_face_normal_update(BMesh *bm, BMFace *f);
|
||||
void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3]);
|
||||
|
||||
/*updates face and vertex normals incident on an edge*/
|
||||
void BM_edge_normals_update(BMesh *bm, BMEdge *e);
|
||||
|
||||
/*update a vert normal (but not the faces incident on it)*/
|
||||
void BM_vert_normal_update(BMesh *bm, BMVert *v);
|
||||
void BM_vert_normal_update_all(BMesh *bm, BMVert *v);
|
||||
|
||||
void BM_face_normal_flip(BMesh *bm, BMFace *f);
|
||||
|
||||
/*dissolves all faces around a vert, and removes it.*/
|
||||
int BM_disk_dissolve(BMesh *bm, BMVert *v);
|
||||
|
||||
/* dissolves vert, in more situations then BM_disk_dissolve
|
||||
* (e.g. if the vert is part of a wire edge, etc).*/
|
||||
int BM_vert_dissolve(BMesh *bm, BMVert *v);
|
||||
|
||||
/* Projects co onto face f, and returns true if it is inside
|
||||
* the face bounds. Note that this uses a best-axis projection
|
||||
* test, instead of projecting co directly into f's orientation
|
||||
* space, so there might be accuracy issues.*/
|
||||
int BM_face_point_inside_test(BMesh *bm, BMFace *f, const float co[3]);
|
||||
|
||||
/* Interpolation */
|
||||
|
||||
/* projects target onto source for customdata interpolation. note: only
|
||||
* does loop customdata. multires is handled. */
|
||||
void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source);
|
||||
|
||||
/* projects a single loop, target, onto source for customdata interpolation. multires is handled.
|
||||
* if do_vertex is true, target's vert data will also get interpolated.*/
|
||||
void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
|
||||
int do_vertex, int do_multires);
|
||||
|
||||
/* smoothes boundaries between multires grids, including some borders in adjacent faces */
|
||||
void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f);
|
||||
|
||||
/* project the multires grid in target onto source's set of multires grids */
|
||||
void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source);
|
||||
void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source);
|
||||
|
||||
void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac);
|
||||
void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, struct BMEdge *e1, const float fac);
|
||||
void BM_data_layer_add(BMesh *em, CustomData *data, int type);
|
||||
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name);
|
||||
void BM_data_layer_free(BMesh *em, CustomData *data, int type);
|
||||
void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n);
|
||||
float BM_elem_float_data_get(struct CustomData *cd, void *element, int type);
|
||||
void BM_elem_float_data_set(struct CustomData *cd, void *element, int type, const float val);
|
||||
|
||||
/* get the area of the face */
|
||||
float BM_face_area_calc(BMesh *bm, BMFace *f);
|
||||
/* computes the centroid of a face, using the center of the bounding box */
|
||||
void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float center[3]);
|
||||
/* computes the centroid of a face, using the mean average */
|
||||
void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float center[3]);
|
||||
|
||||
void BM_mesh_select_mode_flush(BMesh *bm);
|
||||
|
||||
/* mode independant flushing up/down */
|
||||
void BM_mesh_deselect_flush(BMesh *bm);
|
||||
void BM_mesh_select_flush(BMesh *bm);
|
||||
|
||||
/* flag conversion funcs */
|
||||
char BM_face_flag_from_mflag(const char mflag);
|
||||
char BM_edge_flag_from_mflag(const short mflag);
|
||||
char BM_vert_flag_from_mflag(const char mflag);
|
||||
/* reverse */
|
||||
char BM_face_flag_to_mflag(BMFace *f);
|
||||
short BM_edge_flag_to_mflag(BMEdge *e);
|
||||
char BM_vert_flag_to_mflag(BMVert *v);
|
||||
|
||||
|
||||
/* convert MLoop*** in a bmface to mtface and mcol in
|
||||
* an MFace*/
|
||||
void BM_loops_to_corners(BMesh *bm, struct Mesh *me, int findex,
|
||||
BMFace *f, int numTex, int numCol);
|
||||
|
||||
void BM_loop_kill(BMesh *bm, BMLoop *l);
|
||||
void BM_face_kill(BMesh *bm, BMFace *f);
|
||||
void BM_edge_kill(BMesh *bm, BMEdge *e);
|
||||
void BM_vert_kill(BMesh *bm, BMVert *v);
|
||||
|
||||
/* kills all edges associated with f, along with any other faces containing
|
||||
* those edges*/
|
||||
void BM_face_edges_kill(BMesh *bm, BMFace *f);
|
||||
|
||||
/* kills all verts associated with f, along with any other faces containing
|
||||
* those vertices*/
|
||||
void BM_face_verts_kill(BMesh *bm, BMFace *f);
|
||||
|
||||
/*clear all data in bm*/
|
||||
void BM_mesh_clear(BMesh *bm);
|
||||
|
||||
void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag);
|
||||
|
||||
void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func,
|
||||
const char *msg_a, const char *msg_b);
|
||||
|
||||
BMVert *BM_vert_at_index(BMesh *bm, const int index);
|
||||
BMEdge *BM_edge_at_index(BMesh *bm, const int index);
|
||||
BMFace *BM_face_at_index(BMesh *bm, const int index);
|
||||
|
||||
/*start/stop edit*/
|
||||
void bmesh_begin_edit(BMesh *bm, int flag);
|
||||
void bmesh_end_edit(BMesh *bm, int flag);
|
||||
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
# define BM_FACE_FIRST_LOOP(p) (((BMLoopList *)((p)->loops.first))->first)
|
||||
#else
|
||||
# define BM_FACE_FIRST_LOOP(p) ((p)->l_first)
|
||||
#endif
|
||||
|
||||
/* size to use for static arrays when dealing with NGons,
|
||||
* alloc after this limit is reached.
|
||||
* this value is rather arbitrary */
|
||||
#define BM_NGON_STACK_SIZE 32
|
||||
|
||||
/* avoid inf loop, this value is arbtrary
|
||||
* but should not error on valid cases */
|
||||
#define BM_LOOP_RADIAL_MAX 10000
|
||||
#define BM_NGON_MAX 100000
|
||||
|
||||
/* include the rest of the API */
|
||||
#include "bmesh_marking.h"
|
||||
#include "bmesh_operator_api.h"
|
||||
#include "bmesh_operators.h"
|
||||
#include "bmesh_error.h"
|
||||
#include "bmesh_queries.h"
|
||||
#include "bmesh_iterators.h"
|
||||
#include "bmesh_walkers.h"
|
||||
#include "intern/bmesh_inline.c"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BMESH_H__ */
|
||||
190
source/blender/bmesh/bmesh_class.h
Normal file
190
source/blender/bmesh/bmesh_class.h
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Geoffrey Bantle, Levi Schooley, Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_CLASS_H__
|
||||
#define __BMESH_CLASS_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh_class.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
/* bmesh data structures */
|
||||
|
||||
/* dissable holes for now, these are ifdef'd because they use more memory and cant be saved in DNA currently */
|
||||
// define USE_BMESH_HOLES
|
||||
|
||||
struct BMesh;
|
||||
struct BMVert;
|
||||
struct BMEdge;
|
||||
struct BMLoop;
|
||||
struct BMFace;
|
||||
struct BMFlagLayer;
|
||||
struct BMLayerType;
|
||||
struct BMSubClassLayer;
|
||||
|
||||
struct BLI_mempool;
|
||||
struct Object;
|
||||
|
||||
/*note: it is very important for BMHeader to start with two
|
||||
pointers. this is a requirement of mempool's method of
|
||||
iteration.
|
||||
*/
|
||||
typedef struct BMHeader {
|
||||
void *data; /* customdata layers */
|
||||
int index; /* notes:
|
||||
* - Use BM_elem_index_get/SetIndex macros for index
|
||||
* - Unitialized to -1 so we can easily tell its not set.
|
||||
* - Used for edge/vert/face, check BMesh.elem_index_dirty for valid index values,
|
||||
* this is abused by various tools which set it dirty.
|
||||
* - For loops this is used for sorting during tesselation. */
|
||||
|
||||
char htype; /* element geometric type (verts/edges/loops/faces) */
|
||||
char hflag; /* this would be a CD layer, see below */
|
||||
} BMHeader;
|
||||
|
||||
/* note: need some way to specify custom locations for custom data layers. so we can
|
||||
* make them point directly into structs. and some way to make it only happen to the
|
||||
* active layer, and properly update when switching active layers.*/
|
||||
|
||||
typedef struct BMVert {
|
||||
BMHeader head;
|
||||
struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
|
||||
|
||||
float co[3];
|
||||
float no[3];
|
||||
struct BMEdge *e;
|
||||
} BMVert;
|
||||
|
||||
/* disk link structure, only used by edges */
|
||||
typedef struct BMDiskLink {
|
||||
struct BMEdge *next, *prev;
|
||||
} BMDiskLink;
|
||||
|
||||
typedef struct BMEdge {
|
||||
BMHeader head;
|
||||
struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
|
||||
|
||||
struct BMVert *v1, *v2;
|
||||
struct BMLoop *l;
|
||||
|
||||
/* disk cycle pointers */
|
||||
BMDiskLink v1_disk_link, v2_disk_link;
|
||||
} BMEdge;
|
||||
|
||||
typedef struct BMLoop {
|
||||
BMHeader head;
|
||||
/* notice no flags layer */
|
||||
|
||||
struct BMVert *v;
|
||||
struct BMEdge *e;
|
||||
struct BMFace *f;
|
||||
|
||||
struct BMLoop *radial_next, *radial_prev;
|
||||
|
||||
/* these were originally commented as private but are used all over the code */
|
||||
/* can't use ListBase API, due to head */
|
||||
struct BMLoop *next, *prev;
|
||||
} BMLoop;
|
||||
|
||||
/* can cast BMFace/BMEdge/BMVert, but NOT BMLoop, since these dont have a flag layer */
|
||||
typedef struct BMElemF {
|
||||
BMHeader head;
|
||||
struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
|
||||
} BMElemF;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
/* eventually, this structure will be used for supporting holes in faces */
|
||||
typedef struct BMLoopList {
|
||||
struct BMLoopList *next, *prev;
|
||||
struct BMLoop *first, *last;
|
||||
} BMLoopList;
|
||||
#endif
|
||||
|
||||
typedef struct BMFace {
|
||||
BMHeader head;
|
||||
struct BMFlagLayer *oflags; /* an array of flags, mostly used by the operator stack */
|
||||
|
||||
int len; /*includes all boundary loops*/
|
||||
#ifdef USE_BMESH_HOLES
|
||||
int totbounds; /*total boundaries, is one plus the number of holes in the face*/
|
||||
ListBase loops;
|
||||
#else
|
||||
BMLoop *l_first;
|
||||
#endif
|
||||
float no[3]; /*yes, we do store this here*/
|
||||
short mat_nr;
|
||||
} BMFace;
|
||||
|
||||
typedef struct BMFlagLayer {
|
||||
short f, pflag; /* flags */
|
||||
} BMFlagLayer;
|
||||
|
||||
typedef struct BMesh {
|
||||
int totvert, totedge, totloop, totface;
|
||||
int totvertsel, totedgesel, totfacesel;
|
||||
|
||||
/* flag index arrays as being dirty so we can check if they are clean and
|
||||
* avoid looping over the entire vert/edge/face array in those cases.
|
||||
* valid flags are - BM_VERT | BM_EDGE | BM_FACE.
|
||||
* BM_LOOP isnt handled so far. */
|
||||
char elem_index_dirty;
|
||||
|
||||
/*element pools*/
|
||||
struct BLI_mempool *vpool, *epool, *lpool, *fpool;
|
||||
|
||||
/*operator api stuff*/
|
||||
struct BLI_mempool *toolflagpool;
|
||||
int stackdepth;
|
||||
struct BMOperator *currentop;
|
||||
|
||||
CustomData vdata, edata, ldata, pdata;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
struct BLI_mempool *looplistpool;
|
||||
#endif
|
||||
|
||||
/* should be copy of scene select mode */
|
||||
/* stored in BMEditMesh too, this is a bit confusing,
|
||||
* make sure the're in sync!
|
||||
* Only use when the edit mesh cant be accessed - campbell */
|
||||
short selectmode;
|
||||
|
||||
/*ID of the shape key this bmesh came from*/
|
||||
int shapenr;
|
||||
|
||||
int walkers, totflags;
|
||||
ListBase selected, error_stack;
|
||||
|
||||
BMFace *act_face;
|
||||
|
||||
ListBase errorstack;
|
||||
struct Object *ob; /* owner object */
|
||||
|
||||
int opflag; /* current operator flag */
|
||||
} BMesh;
|
||||
|
||||
#define BM_VERT 1
|
||||
#define BM_EDGE 2
|
||||
#define BM_LOOP 4
|
||||
#define BM_FACE 8
|
||||
|
||||
#endif /* __BMESH_CLASS_H__ */
|
||||
72
source/blender/bmesh/bmesh_error.h
Normal file
72
source/blender/bmesh/bmesh_error.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_ERROR_H__
|
||||
#define __BMESH_ERROR_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh_error.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
/*----------- bmop error system ----------*/
|
||||
|
||||
/* pushes an error onto the bmesh error stack.
|
||||
* if msg is null, then the default message for the errorcode is used.*/
|
||||
void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg);
|
||||
|
||||
/* gets the topmost error from the stack.
|
||||
* returns error code or 0 if no error.*/
|
||||
int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op);
|
||||
int BMO_error_occurred(BMesh *bm);
|
||||
|
||||
/* same as geterror, only pops the error off the stack as well */
|
||||
int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op);
|
||||
void BMO_error_clear(BMesh *bm);
|
||||
|
||||
#if 0
|
||||
//this is meant for handling errors, like self-intersection test failures.
|
||||
//it's dangerous to handle errors in general though, so disabled for now.
|
||||
|
||||
/* catches an error raised by the op pointed to by catchop.
|
||||
* errorcode is either the errorcode, or BMERR_ALL for any
|
||||
* error.*/
|
||||
int BMO_error_catch_op(BMesh *bm, BMOperator *catchop, int errorcode, char **msg);
|
||||
#endif
|
||||
|
||||
#define BM_ELEM_INDEX_VALIDATE(_bm, _msg_a, _msg_b) \
|
||||
BM_mesh_elem_index_validate(_bm, __FILE__ ":" STRINGIFY(__LINE__), __func__, _msg_a, _msg_b)
|
||||
|
||||
/*------ error code defines -------*/
|
||||
|
||||
/*error messages*/
|
||||
#define BMERR_SELF_INTERSECTING 1
|
||||
#define BMERR_DISSOLVEDISK_FAILED 2
|
||||
#define BMERR_CONNECTVERT_FAILED 3
|
||||
#define BMERR_WALKER_FAILED 4
|
||||
#define BMERR_DISSOLVEFACES_FAILED 5
|
||||
#define BMERR_DISSOLVEVERTS_FAILED 6
|
||||
#define BMERR_TESSELATION 7
|
||||
#define BMERR_NONMANIFOLD 8
|
||||
#define BMERR_INVALID_SELECTION 9
|
||||
#define BMERR_MESH_ERROR 10
|
||||
|
||||
#endif /* __BMESH_ERROR_H__ */
|
||||
136
source/blender/bmesh/bmesh_iterators.h
Normal file
136
source/blender/bmesh/bmesh_iterators.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_ITERATORS_H__
|
||||
#define __BMESH_ITERATORS_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh_iterators.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
/*
|
||||
* BMESH ITERATORS
|
||||
*
|
||||
* The functions and structures in this file
|
||||
* provide a unified method for iterating over
|
||||
* the elements of a mesh and answering simple
|
||||
* adjacency queries. Tool authors should use
|
||||
* the iterators provided in this file instead
|
||||
* of inspecting the structure directly.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "BLI_mempool.h"
|
||||
|
||||
/* Defines for passing to BM_iter_new.
|
||||
*
|
||||
* "OF" can be substituted for "around"
|
||||
* so BM_VERTS_OF_FACE means "vertices
|
||||
* around a face."
|
||||
*/
|
||||
|
||||
/* these iterator over all elements of a specific
|
||||
* type in the mesh.*/
|
||||
#define BM_VERTS_OF_MESH 1
|
||||
#define BM_EDGES_OF_MESH 2
|
||||
#define BM_FACES_OF_MESH 3
|
||||
|
||||
/*these are topological iterators.*/
|
||||
#define BM_EDGES_OF_VERT 4
|
||||
#define BM_FACES_OF_VERT 5
|
||||
#define BM_LOOPS_OF_VERT 6
|
||||
#define BM_FACES_OF_EDGE 7
|
||||
#define BM_VERTS_OF_FACE 8
|
||||
#define BM_EDGES_OF_FACE 9
|
||||
#define BM_LOOPS_OF_FACE 10
|
||||
/* returns elements from all boundaries, and returns
|
||||
* the first element at the end to flag that we're entering
|
||||
* a different face hole boundary*/
|
||||
#define BM_ALL_LOOPS_OF_FACE 11
|
||||
|
||||
/* iterate through loops around this loop, which are fetched
|
||||
* from the other faces in the radial cycle surrounding the
|
||||
* input loop's edge.*/
|
||||
#define BM_LOOPS_OF_LOOP 12
|
||||
#define BM_LOOPS_OF_EDGE 13
|
||||
|
||||
#define BM_ITER(ele, iter, bm, itype, data) \
|
||||
ele = BM_iter_new(iter, bm, itype, data); \
|
||||
for ( ; ele; ele=BM_iter_step(iter))
|
||||
|
||||
#define BM_ITER_INDEX(ele, iter, bm, itype, data, indexvar) \
|
||||
ele = BM_iter_new(iter, bm, itype, data); \
|
||||
for (indexvar=0; ele; indexvar++, ele=BM_iter_step(iter))
|
||||
|
||||
/*Iterator Structure*/
|
||||
typedef struct BMIter {
|
||||
BLI_mempool_iter pooliter;
|
||||
|
||||
struct BMVert *firstvert, *nextvert, *vdata;
|
||||
struct BMEdge *firstedge, *nextedge, *edata;
|
||||
struct BMLoop *firstloop, *nextloop, *ldata, *l;
|
||||
struct BMFace *firstpoly, *nextpoly, *pdata;
|
||||
struct BMesh *bm;
|
||||
void (*begin)(struct BMIter *iter);
|
||||
void *(*step)(struct BMIter *iter);
|
||||
union {
|
||||
void *p;
|
||||
int i;
|
||||
long l;
|
||||
float f;
|
||||
} filter;
|
||||
int count;
|
||||
char itype;
|
||||
} BMIter;
|
||||
|
||||
void *BM_iter_at_index(struct BMesh *bm, const char htype, void *data, int index);
|
||||
int BM_iter_as_array(struct BMesh *bm, const char htype, void *data, void **array, const int len);
|
||||
|
||||
/* private for bmesh_iterators_inline.c */
|
||||
void bmiter__vert_of_mesh_begin(struct BMIter *iter);
|
||||
void *bmiter__vert_of_mesh_step(struct BMIter *iter);
|
||||
void bmiter__edge_of_mesh_begin(struct BMIter *iter);
|
||||
void *bmiter__edge_of_mesh_step(struct BMIter *iter);
|
||||
void bmiter__face_of_mesh_begin(struct BMIter *iter);
|
||||
void *bmiter__face_of_mesh_step(struct BMIter *iter);
|
||||
void bmiter__edge_of_vert_begin(struct BMIter *iter);
|
||||
void *bmiter__edge_of_vert_step(struct BMIter *iter);
|
||||
void bmiter__face_of_vert_begin(struct BMIter *iter);
|
||||
void *bmiter__face_of_vert_step(struct BMIter *iter);
|
||||
void bmiter__loop_of_vert_begin(struct BMIter *iter);
|
||||
void *bmiter__loop_of_vert_step(struct BMIter *iter);
|
||||
void bmiter__loops_of_edge_begin(struct BMIter *iter);
|
||||
void *bmiter__loops_of_edge_step(struct BMIter *iter);
|
||||
void bmiter__loops_of_loop_begin(struct BMIter *iter);
|
||||
void *bmiter__loops_of_loop_step(struct BMIter *iter);
|
||||
void bmiter__face_of_edge_begin(struct BMIter *iter);
|
||||
void *bmiter__face_of_edge_step(struct BMIter *iter);
|
||||
void bmiter__vert_of_face_begin(struct BMIter *iter);
|
||||
void *bmiter__vert_of_face_step(struct BMIter *iter);
|
||||
void bmiter__edge_of_face_begin(struct BMIter *iter);
|
||||
void *bmiter__edge_of_face_step(struct BMIter *iter);
|
||||
void bmiter__loop_of_face_begin(struct BMIter *iter);
|
||||
void *bmiter__loop_of_face_step(struct BMIter *iter);
|
||||
|
||||
#include "intern/bmesh_iterators_inline.c"
|
||||
|
||||
#endif /* __BMESH_ITERATORS_H__ */
|
||||
75
source/blender/bmesh/bmesh_marking.h
Normal file
75
source/blender/bmesh/bmesh_marking.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_MARKING_H__
|
||||
#define __BMESH_MARKING_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh_marking.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
typedef struct BMEditSelection
|
||||
{
|
||||
struct BMEditSelection *next, *prev;
|
||||
void *data;
|
||||
char htype;
|
||||
} BMEditSelection;
|
||||
|
||||
/* geometry hiding code */
|
||||
void BM_elem_hide_set(BMesh *bm, void *element, int hide);
|
||||
void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide);
|
||||
void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide);
|
||||
void BM_face_hide_set(BMesh *bm, BMFace *f, int hide);
|
||||
|
||||
/* Selection code */
|
||||
void BM_elem_select_set(struct BMesh *bm, void *element, int select);
|
||||
|
||||
/* use BM_elem_flag_test(ele, BM_ELEM_SELECT) to test selection */
|
||||
|
||||
void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag);
|
||||
void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag);
|
||||
|
||||
/* individual element select functions, BM_elem_select_set is a shortcut for these
|
||||
* that automatically detects which one to use*/
|
||||
void BM_vert_select_set(struct BMesh *bm, struct BMVert *v, int select);
|
||||
void BM_edge_select_set(struct BMesh *bm, struct BMEdge *e, int select);
|
||||
void BM_face_select_set(struct BMesh *bm, struct BMFace *f, int select);
|
||||
|
||||
void BM_select_mode_set(struct BMesh *bm, int selectmode);
|
||||
|
||||
/* counts number of elements with flag set */
|
||||
int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide);
|
||||
|
||||
/* edit selection stuff */
|
||||
void BM_active_face_set(BMesh *em, BMFace *f);
|
||||
BMFace *BM_active_face_get(BMesh *bm, int sloppy);
|
||||
void BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese);
|
||||
void BM_editselection_normal(float r_normal[3], BMEditSelection *ese);
|
||||
void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese);
|
||||
|
||||
int BM_select_history_check(BMesh *bm, void *data);
|
||||
void BM_select_history_remove(BMesh *bm, void *data);
|
||||
void BM_select_history_store(BMesh *bm, void *data);
|
||||
void BM_select_history_validate(BMesh *bm);
|
||||
void BM_select_history_clear(BMesh *em);
|
||||
|
||||
#endif /* __BMESH_MARKING_H__ */
|
||||
555
source/blender/bmesh/bmesh_operator_api.h
Normal file
555
source/blender/bmesh/bmesh_operator_api.h
Normal file
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_OPERATOR_API_H__
|
||||
#define __BMESH_OPERATOR_API_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh_operator_api.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "BLI_memarena.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include "BKE_utildefines.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h> /* for memcpy */
|
||||
|
||||
/*
|
||||
* operators represent logical, executable mesh modules. all topological
|
||||
* operations involving a bmesh has to go through them.
|
||||
*
|
||||
* operators are nested, as are tool flags, which are private to an operator
|
||||
* when it's executed. tool flags are allocated in layers, one per operator
|
||||
* execution, and are used for all internal flagging a tool needs to do.
|
||||
*
|
||||
* each operator has a series of "slots," which can be of the following types:
|
||||
* - simple numerical types
|
||||
* - arrays of elements (e.g. arrays of faces).
|
||||
* - hash mappings.
|
||||
*
|
||||
* each slot is identified by a slot code, as are each operator.
|
||||
* operators, and their slots, are defined in bmesh_opdefines.c (with their
|
||||
* execution functions prototyped in bmesh_operators_private.h), with all their
|
||||
* operator code and slot codes defined in bmesh_operators.h. see
|
||||
* bmesh_opdefines.c and the BMOpDefine struct for how to define new operators.
|
||||
*
|
||||
* in general, operators are fed arrays of elements, created using either
|
||||
* BM_HeaderFlag_To_Slot or BM_Flag_To_Slot (or through one of the format
|
||||
* specifyers in BMO_op_callf or BMO_op_initf). Note that multiple element
|
||||
* types (e.g. faces and edges) can be fed to the same slot array. Operators
|
||||
* act on this data, and possibly spit out data into output slots.
|
||||
*
|
||||
* some notes:
|
||||
* - operators should never read from header flags (e.g. element->head.flag). for
|
||||
* example, if you want an operator to only operate on selected faces, you
|
||||
* should use BM_HeaderFlag_To_Slot to put the selected elements into a slot.
|
||||
* - when you read from an element slot array or mapping, you can either tool-flag
|
||||
* all the elements in it, or read them using an iterator APi (which is
|
||||
* semantically similar to the iterator api in bmesh_iterators.h).
|
||||
*/
|
||||
|
||||
struct GHashIterator;
|
||||
|
||||
/* slot type arrays are terminated by the last member
|
||||
* having a slot type of 0.*/
|
||||
#define BMO_OP_SLOT_SENTINEL 0
|
||||
#define BMO_OP_SLOT_INT 1
|
||||
#define BMO_OP_SLOT_FLT 2
|
||||
#define BMO_OP_SLOT_PNT 3
|
||||
#define BMO_OP_SLOT_MAT 4
|
||||
#define BMO_OP_SLOT_VEC 7
|
||||
|
||||
/* after BMO_OP_SLOT_VEC, everything is
|
||||
|
||||
* dynamically allocated arrays. we
|
||||
* leave a space in the identifiers
|
||||
* for future growth.
|
||||
*/
|
||||
//it's very important this remain a power of two
|
||||
#define BMO_OP_SLOT_ELEMENT_BUF 8
|
||||
#define BMO_OP_SLOT_MAPPING 9
|
||||
/* #define BMO_OP_SLOT_TOTAL_TYPES 10 */ /* not used yet */
|
||||
|
||||
/* please ignore all these structures, don't touch them in tool code, except
|
||||
* for when your defining an operator with BMOpDefine.*/
|
||||
|
||||
typedef struct BMOpSlot{
|
||||
int slottype;
|
||||
int len;
|
||||
int flag;
|
||||
int index; /* index within slot array */
|
||||
union {
|
||||
int i;
|
||||
float f;
|
||||
void *p;
|
||||
float vec[3];
|
||||
void *buf;
|
||||
GHash *ghash;
|
||||
} data;
|
||||
} BMOpSlot;
|
||||
|
||||
#define BMO_OP_MAX_SLOTS 16 /* way more than probably needed */
|
||||
|
||||
#ifdef slots
|
||||
#undef slots
|
||||
#endif
|
||||
|
||||
typedef struct BMOperator {
|
||||
int type;
|
||||
int slottype;
|
||||
int needflag;
|
||||
int flag;
|
||||
struct BMOpSlot slots[BMO_OP_MAX_SLOTS];
|
||||
void (*exec)(struct BMesh *bm, struct BMOperator *op);
|
||||
MemArena *arena;
|
||||
} BMOperator;
|
||||
|
||||
#define MAX_SLOTNAME 32
|
||||
|
||||
typedef struct BMOSlotType {
|
||||
int type;
|
||||
char name[MAX_SLOTNAME];
|
||||
} BMOSlotType;
|
||||
|
||||
typedef struct BMOpDefine {
|
||||
const char *name;
|
||||
BMOSlotType slottypes[BMO_OP_MAX_SLOTS];
|
||||
void (*exec)(BMesh *bm, BMOperator *op);
|
||||
int flag;
|
||||
} BMOpDefine;
|
||||
|
||||
/* BMOpDefine->flag */
|
||||
#define BMO_OP_FLAG_UNTAN_MULTIRES 1 /*switch from multires tangent space to absolute coordinates*/
|
||||
|
||||
/* ensures consistent normals before operator execution,
|
||||
* restoring the original ones windings/normals afterwards.
|
||||
* keep in mind, this won't work if the input mesh isn't
|
||||
* manifold.*/
|
||||
#define BMO_OP_FLAG_RATIONALIZE_NORMALS 2
|
||||
|
||||
/*------------- Operator API --------------*/
|
||||
|
||||
/* data types that use pointers (arrays, etc) should never
|
||||
* have it set directly. and never use BMO_slot_ptr_set to
|
||||
* pass in a list of edges or any arrays, really.*/
|
||||
|
||||
void BMO_op_init(struct BMesh *bm, struct BMOperator *op, const char *opname);
|
||||
|
||||
/* executes an operator, pushing and popping a new tool flag
|
||||
* layer as appropriate.*/
|
||||
void BMO_op_exec(struct BMesh *bm, struct BMOperator *op);
|
||||
|
||||
/* finishes an operator (though note the operator's tool flag is removed
|
||||
* after it finishes executing in BMO_op_exec).*/
|
||||
void BMO_op_finish(struct BMesh *bm, struct BMOperator *op);
|
||||
|
||||
|
||||
/* tool flag API. never, ever ever should tool code put junk in
|
||||
* header flags (element->head.flag), nor should they use
|
||||
* element->head.eflag1/eflag2. instead, use this api to set
|
||||
* flags.
|
||||
*
|
||||
* if you need to store a value per element, use a
|
||||
* ghash or a mapping slot to do it. */
|
||||
|
||||
/* flags 15 and 16 (1<<14 and 1<<15) are reserved for bmesh api use */
|
||||
#define BMO_elem_flag_test(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f & (oflag))
|
||||
#define BMO_elem_flag_enable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f |= (oflag))
|
||||
#define BMO_elem_flag_disable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f &= ~(oflag))
|
||||
#define BMO_elem_flag_toggle(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f ^= (oflag))
|
||||
|
||||
/* profiling showed a significant amount of time spent in BMO_elem_flag_test */
|
||||
#if 0
|
||||
void BMO_elem_flag_enable(struct BMesh *bm, void *element, const short oflag);
|
||||
void BMO_elem_flag_disable(struct BMesh *bm, void *element, const short oflag);
|
||||
int BMO_elem_flag_test(struct BMesh *bm, void *element, const short oflag);
|
||||
#endif
|
||||
|
||||
/* count the number of elements with a specific flag.
|
||||
* type can be a bitmask of BM_FACE, BM_EDGE, or BM_FACE. */
|
||||
int BMO_mesh_flag_count(struct BMesh *bm, const short oflag, const char htype);
|
||||
|
||||
/*---------formatted operator initialization/execution-----------*/
|
||||
/*
|
||||
* this system is used to execute or initialize an operator,
|
||||
* using a formatted-string system.
|
||||
*
|
||||
* for example, BMO_op_callf(bm, "del geom=%hf context=%d", BM_ELEM_SELECT, DEL_FACES);
|
||||
* . . .will execute the delete operator, feeding in selected faces, deleting them.
|
||||
*
|
||||
* the basic format for the format string is:
|
||||
* [operatorname] [slotname]=%[code] [slotname]=%[code]
|
||||
*
|
||||
* as in printf, you pass in one additional argument to the function
|
||||
* for every code.
|
||||
*
|
||||
* the formatting codes are:
|
||||
* %d - put int in slot
|
||||
* %f - put float in slot
|
||||
* %p - put pointer in slot
|
||||
* %h[f/e/v] - put elements with a header flag in slot.
|
||||
* the letters after %h define which element types to use,
|
||||
* so e.g. %hf will do faces, %hfe will do faces and edges,
|
||||
* %hv will do verts, etc. must pass in at least one
|
||||
* element type letter.
|
||||
* %f[f/e/v] - same as %h, except it deals with tool flags instead of
|
||||
* header flags.
|
||||
* %a[f/e/v] - pass all elements (of types specified by f/e/v) to the
|
||||
* slot.
|
||||
* %e - pass in a single element.
|
||||
* %v - pointer to a float vector of length 3.
|
||||
* %m[3/4] - matrix, 3/4 refers to the matrix size, 3 or 4. the
|
||||
* corrusponding argument must be a pointer to
|
||||
* a float matrix.
|
||||
* %s - copy a slot from another op, instead of mapping to one
|
||||
* argument, it maps to two, a pointer to an operator and
|
||||
* a slot name.
|
||||
*/
|
||||
void BMO_push(BMesh *bm, BMOperator *op);
|
||||
void BMO_pop(BMesh *bm);
|
||||
|
||||
/*executes an operator*/
|
||||
int BMO_op_callf(BMesh *bm, const char *fmt, ...);
|
||||
|
||||
/* initializes, but doesn't execute an operator. this is so you can
|
||||
* gain access to the outputs of the operator. note that you have
|
||||
* to execute/finitsh (BMO_op_exec and BMO_op_finish) yourself. */
|
||||
int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...);
|
||||
|
||||
/* va_list version, used to implement the above two functions,
|
||||
* plus EDBM_CallOpf in bmeshutils.c. */
|
||||
int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *fmt, va_list vlist);
|
||||
|
||||
/* test whether a named slot exists */
|
||||
int BMO_slot_exists(struct BMOperator *op, const char *slotname);
|
||||
|
||||
/* get a pointer to a slot. this may be removed layer on from the public API. */
|
||||
BMOpSlot *BMO_slot_get(struct BMOperator *op, const char *slotname);
|
||||
|
||||
/* copies the data of a slot from one operator to another. src and dst are the
|
||||
* source/destination slot codes, respectively. */
|
||||
void BMO_slot_copy(struct BMOperator *source_op, struct BMOperator *dest_op,
|
||||
const char *src, const char *dst);
|
||||
|
||||
/* remove tool flagged elements */
|
||||
void BMO_remove_tagged_faces(struct BMesh *bm, const short oflag);
|
||||
void BMO_remove_tagged_edges(struct BMesh *bm, const short oflag);
|
||||
void BMO_remove_tagged_verts(struct BMesh *bm, const short oflag);
|
||||
|
||||
/* take care, uses operator flag DEL_WIREVERT */
|
||||
void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type);
|
||||
|
||||
/* del "context" slot values, used for operator too */
|
||||
enum {
|
||||
DEL_VERTS = 1,
|
||||
DEL_EDGES,
|
||||
DEL_ONLYFACES,
|
||||
DEL_EDGESFACES,
|
||||
DEL_FACES,
|
||||
DEL_ALL ,
|
||||
DEL_ONLYTAGGED
|
||||
};
|
||||
|
||||
void BMO_op_flag_enable(struct BMesh *bm, struct BMOperator *op, const int op_flag);
|
||||
void BMO_op_flag_disable(struct BMesh *bm, struct BMOperator *op, const int op_flag);
|
||||
|
||||
void BMO_slot_float_set(struct BMOperator *op, const char *slotname, const float f);
|
||||
float BMO_slot_float_get(BMOperator *op, const char *slotname);
|
||||
void BMO_slot_int_set(struct BMOperator *op, const char *slotname, const int i);
|
||||
int BMO_slot_int_get(BMOperator *op, const char *slotname);
|
||||
|
||||
/* don't pass in arrays that are supposed to map to elements this way.
|
||||
*
|
||||
* so, e.g. passing in list of floats per element in another slot is bad.
|
||||
* passing in, e.g. pointer to an editmesh for the conversion operator is fine
|
||||
* though. */
|
||||
void BMO_slot_ptr_set(struct BMOperator *op, const char *slotname, void *p);
|
||||
void *BMO_slot_ptr_get(BMOperator *op, const char *slotname);
|
||||
void BMO_slot_vec_set(struct BMOperator *op, const char *slotname, const float vec[3]);
|
||||
void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3]);
|
||||
|
||||
/* only supports square mats */
|
||||
/* size must be 3 or 4; this api is meant only for transformation matrices.
|
||||
* note that internally the matrix is stored in 4x4 form, and it's safe to
|
||||
* call whichever BMO_Get_Mat* function you want. */
|
||||
void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size);
|
||||
void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4]);
|
||||
void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3]);
|
||||
|
||||
void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *op, const char htype, const short oflag);
|
||||
|
||||
/* puts every element of type type (which is a bitmask) with tool flag flag,
|
||||
* into a slot. */
|
||||
void BMO_slot_from_flag(struct BMesh *bm, struct BMOperator *op, const char *slotname,
|
||||
const short oflag, const char htype);
|
||||
|
||||
/* tool-flags all elements inside an element slot array with flag flag. */
|
||||
void BMO_slot_buffer_flag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
|
||||
const short oflag, const char htype);
|
||||
/* clears tool-flag flag from all elements inside a slot array. */
|
||||
void BMO_slot_buffer_flag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
|
||||
const short oflag, const char htype);
|
||||
|
||||
/* tool-flags all elements inside an element slot array with flag flag. */
|
||||
void BMO_slot_buffer_hflag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
|
||||
const char hflag, const char htype);
|
||||
/* clears tool-flag flag from all elements inside a slot array. */
|
||||
void BMO_slot_buffer_hflag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
|
||||
const char hflag, const char htype);
|
||||
|
||||
/* puts every element of type type (which is a bitmask) with header flag
|
||||
* flag, into a slot. note: ignores hidden elements (e.g. elements with
|
||||
* header flag BM_ELEM_HIDDEN set).*/
|
||||
void BMO_slot_from_hflag(struct BMesh *bm, struct BMOperator *op, const char *slotname,
|
||||
const char hflag, const char htype);
|
||||
|
||||
/* counts number of elements inside a slot array. */
|
||||
int BMO_slot_buf_count(struct BMesh *bm, struct BMOperator *op, const char *slotname);
|
||||
int BMO_slot_map_count(struct BMesh *bm, struct BMOperator *op, const char *slotname);
|
||||
|
||||
/* Counts the number of edges with tool flag toolflag around
|
||||
*/
|
||||
int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag);
|
||||
|
||||
/* inserts a key/value mapping into a mapping slot. note that it copies the
|
||||
* value, it doesn't store a reference to it. */
|
||||
|
||||
#if 0
|
||||
|
||||
BM_INLINE void BMO_slot_map_insert(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element, void *data, int len);
|
||||
|
||||
/* inserts a key/float mapping pair into a mapping slot. */
|
||||
BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element, float val);
|
||||
|
||||
/* returns 1 if the specified pointer is in the map. */
|
||||
BM_INLINE int BMO_slot_map_contains(BMesh *bm, BMOperator *op, const char *slotname, void *element);
|
||||
|
||||
/* returns a point to the value of a specific key. */
|
||||
BM_INLINE void *BMO_slot_map_data_get(BMesh *bm, BMOperator *op, const char *slotname, void *element);
|
||||
|
||||
/* returns the float part of a key/float pair. */
|
||||
BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname, void *element);
|
||||
|
||||
#endif
|
||||
|
||||
/* flags all elements in a mapping. note that the mapping must only have
|
||||
* bmesh elements in it.*/
|
||||
void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op,
|
||||
const char *slotname, const short oflag);
|
||||
|
||||
/* pointer versoins of BMO_slot_map_float_get and BMO_slot_map_float_insert.
|
||||
*
|
||||
* do NOT use these for non-operator-api-allocated memory! instead
|
||||
* use BMO_slot_map_data_get and BMO_slot_map_insert, which copies the data. */
|
||||
|
||||
#if 0
|
||||
BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname, void *key, void *val);
|
||||
BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname, void *key);
|
||||
#endif
|
||||
|
||||
/* this part of the API is used to iterate over element buffer or
|
||||
* mapping slots.
|
||||
*
|
||||
* for example, iterating over the faces in a slot is:
|
||||
*
|
||||
* BMOIter oiter;
|
||||
* BMFace *f;
|
||||
*
|
||||
* f = BMO_iter_new(&oiter, bm, some_operator, "slotname", BM_FACE);
|
||||
* for (; f; f=BMO_iter_step(&oiter)) {
|
||||
* /do something with the face
|
||||
* }
|
||||
*
|
||||
* another example, iterating over a mapping:
|
||||
* BMOIter oiter;
|
||||
* void *key;
|
||||
* void *val;
|
||||
*
|
||||
* key = BMO_iter_new(&oiter, bm, some_operator, "slotname", 0);
|
||||
* for (; key; key=BMO_iter_step(&oiter)) {
|
||||
* val = BMO_iter_map_value(&oiter);
|
||||
* //do something with the key/val pair
|
||||
* //note that val is a pointer to the val data,
|
||||
* //whether it's a float, pointer, whatever.
|
||||
* //
|
||||
* // so to get a pointer, for example, use:
|
||||
* // *((void**)BMO_iter_map_value(&oiter));
|
||||
* //or something like that.
|
||||
* }
|
||||
*/
|
||||
|
||||
/* contents of this structure are private,
|
||||
* don't directly access. */
|
||||
typedef struct BMOIter {
|
||||
BMOpSlot *slot;
|
||||
int cur; //for arrays
|
||||
struct GHashIterator giter;
|
||||
void *val;
|
||||
char restrictmask; /* bitwise '&' with BMHeader.htype */
|
||||
} BMOIter;
|
||||
|
||||
void *BMO_slot_elem_first(BMOperator *op, const char *slotname);
|
||||
|
||||
/* restrictmask restricts the iteration to certain element types
|
||||
* (e.g. combination of BM_VERT, BM_EDGE, BM_FACE), if iterating
|
||||
* over an element buffer (not a mapping).*/
|
||||
void *BMO_iter_new(BMOIter *iter, BMesh *bm, BMOperator *op,
|
||||
const char *slotname, const char restrictmask);
|
||||
void *BMO_iter_step(BMOIter *iter);
|
||||
|
||||
/* returns a pointer to the key value when iterating over mappings.
|
||||
* remember for pointer maps this will be a pointer to a pointer.*/
|
||||
void *BMO_iter_map_value(BMOIter *iter);
|
||||
|
||||
/* use this for pointer mappings */
|
||||
void *BMO_iter_map_value_p(BMOIter *iter);
|
||||
|
||||
/* use this for float mappings */
|
||||
float BMO_iter_map_value_f(BMOIter *iter);
|
||||
|
||||
#define BMO_ITER(ele, iter, bm, op, slotname, restrict) \
|
||||
ele = BMO_iter_new(iter, bm, op, slotname, restrict); \
|
||||
for ( ; ele; ele=BMO_iter_step(iter))
|
||||
|
||||
/******************* Inlined Functions********************/
|
||||
typedef void (*opexec)(struct BMesh *bm, struct BMOperator *op);
|
||||
|
||||
/* mappings map elements to data, which
|
||||
* follows the mapping struct in memory. */
|
||||
typedef struct BMOElemMapping {
|
||||
BMHeader *element;
|
||||
int len;
|
||||
} BMOElemMapping;
|
||||
|
||||
extern const int BMO_OPSLOT_TYPEINFO[];
|
||||
|
||||
BM_INLINE void BMO_slot_map_insert(BMesh *UNUSED(bm), BMOperator *op, const char *slotname,
|
||||
void *element, void *data, int len)
|
||||
{
|
||||
BMOElemMapping *mapping;
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
/*sanity check*/
|
||||
if (slot->slottype != BMO_OP_SLOT_MAPPING) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapping = (BMOElemMapping *) BLI_memarena_alloc(op->arena, sizeof(*mapping) + len);
|
||||
|
||||
mapping->element = (BMHeader*) element;
|
||||
mapping->len = len;
|
||||
memcpy(mapping + 1, data, len);
|
||||
|
||||
if (!slot->data.ghash) {
|
||||
slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash,
|
||||
BLI_ghashutil_ptrcmp, "bmesh op");
|
||||
}
|
||||
|
||||
BLI_ghash_insert(slot->data.ghash, element, mapping);
|
||||
}
|
||||
|
||||
BM_INLINE void BMO_slot_map_int_insert(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element, int val)
|
||||
{
|
||||
BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(int));
|
||||
}
|
||||
|
||||
BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element, float val)
|
||||
{
|
||||
BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(float));
|
||||
}
|
||||
|
||||
BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element, void *val)
|
||||
{
|
||||
BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(void*));
|
||||
}
|
||||
|
||||
BM_INLINE int BMO_slot_map_contains(BMesh *UNUSED(bm), BMOperator *op, const char *slotname, void *element)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
/*sanity check*/
|
||||
if (slot->slottype != BMO_OP_SLOT_MAPPING) return 0;
|
||||
if (!slot->data.ghash) return 0;
|
||||
|
||||
return BLI_ghash_haskey(slot->data.ghash, element);
|
||||
}
|
||||
|
||||
BM_INLINE void *BMO_slot_map_data_get(BMesh *UNUSED(bm), BMOperator *op, const char *slotname,
|
||||
void *element)
|
||||
{
|
||||
BMOElemMapping *mapping;
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
/*sanity check*/
|
||||
if (slot->slottype != BMO_OP_SLOT_MAPPING) return NULL;
|
||||
if (!slot->data.ghash) return NULL;
|
||||
|
||||
mapping = (BMOElemMapping *)BLI_ghash_lookup(slot->data.ghash, element);
|
||||
|
||||
if (!mapping) return NULL;
|
||||
|
||||
return mapping + 1;
|
||||
}
|
||||
|
||||
BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element)
|
||||
{
|
||||
float *val = (float*) BMO_slot_map_data_get(bm, op, slotname, element);
|
||||
if (val) return *val;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
BM_INLINE int BMO_slot_map_int_get(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element)
|
||||
{
|
||||
int *val = (int*) BMO_slot_map_data_get(bm, op, slotname, element);
|
||||
if (val) return *val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
void *element)
|
||||
{
|
||||
void **val = (void**) BMO_slot_map_data_get(bm, op, slotname, element);
|
||||
if (val) return *val;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BMESH_OPERATOR_API_H__ */
|
||||
105
source/blender/bmesh/bmesh_operators.h
Normal file
105
source/blender/bmesh/bmesh_operators.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_OPERATORS_H__
|
||||
#define __BMESH_OPERATORS_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh_operators.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
/*see comments in intern/bmesh_opdefines.c for documentation of specific operators*/
|
||||
|
||||
/*--------defines/enumerations for specific operators-------*/
|
||||
|
||||
/*quad innervert values*/
|
||||
enum {
|
||||
SUBD_INNERVERT,
|
||||
SUBD_PATH,
|
||||
SUBD_FAN,
|
||||
SUBD_STRAIGHT_CUT
|
||||
};
|
||||
|
||||
/* similar face selection slot values */
|
||||
enum {
|
||||
SIMFACE_MATERIAL = 201,
|
||||
SIMFACE_IMAGE,
|
||||
SIMFACE_AREA,
|
||||
SIMFACE_PERIMETER,
|
||||
SIMFACE_NORMAL,
|
||||
SIMFACE_COPLANAR
|
||||
};
|
||||
|
||||
/* similar edge selection slot values */
|
||||
enum {
|
||||
SIMEDGE_LENGTH = 101,
|
||||
SIMEDGE_DIR,
|
||||
SIMEDGE_FACE,
|
||||
SIMEDGE_FACE_ANGLE,
|
||||
SIMEDGE_CREASE,
|
||||
SIMEDGE_SEAM,
|
||||
SIMEDGE_SHARP
|
||||
};
|
||||
|
||||
/* similar vertex selection slot values */
|
||||
enum {
|
||||
SIMVERT_NORMAL = 0,
|
||||
SIMVERT_FACE,
|
||||
SIMVERT_VGROUP
|
||||
};
|
||||
|
||||
enum {
|
||||
OPUVC_AXIS_X = 1,
|
||||
OPUVC_AXIS_Y
|
||||
};
|
||||
|
||||
enum {
|
||||
DIRECTION_CW = 1,
|
||||
DIRECTION_CCW
|
||||
};
|
||||
|
||||
/* vertex path selection values */
|
||||
enum {
|
||||
VPATH_SELECT_EDGE_LENGTH = 0,
|
||||
VPATH_SELECT_TOPOLOGICAL
|
||||
};
|
||||
|
||||
extern BMOpDefine *opdefines[];
|
||||
extern int bmesh_total_ops;
|
||||
|
||||
/*------specific operator helper functions-------*/
|
||||
|
||||
/* executes the duplicate operation, feeding elements of
|
||||
* type flag etypeflag and header flag flag to it. note,
|
||||
* to get more useful information (such as the mapping from
|
||||
* original to new elements) you should run the dupe op manually.*/
|
||||
struct Object;
|
||||
struct EditMesh;
|
||||
|
||||
#if 0
|
||||
void BMO_dupe_from_flag(struct BMesh *bm, int etypeflag, const char hflag);
|
||||
#endif
|
||||
void BM_mesh_esubdivideflag(struct Object *obedit, BMesh *bm, int flag, float smooth,
|
||||
float fractal, int beauty, int numcuts, int seltype,
|
||||
int cornertype, int singleedge, int gridfill, int seed);
|
||||
|
||||
#endif /* __BMESH_OPERATORS_H__ */
|
||||
123
source/blender/bmesh/bmesh_queries.h
Normal file
123
source/blender/bmesh/bmesh_queries.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_QUERIES_H__
|
||||
#define __BMESH_QUERIES_H__
|
||||
|
||||
/** \file blender/bmesh/bmesh_queries.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Queries */
|
||||
|
||||
/* counts number of elements of type type are in the mesh. */
|
||||
int BM_mesh_elem_count(struct BMesh *bm, const char htype);
|
||||
|
||||
/*returns true if v is in f*/
|
||||
int BM_vert_in_face(struct BMFace *f, struct BMVert *v);
|
||||
|
||||
// int BM_verts_in_face(struct BMFace *f, struct BMVert **varr, int len);
|
||||
int BM_verts_in_face(struct BMesh *bm, struct BMFace *f, struct BMVert **varr, int len);
|
||||
|
||||
int BM_edge_in_face(struct BMFace *f, struct BMEdge *e);
|
||||
|
||||
int BM_vert_in_edge(struct BMEdge *e, struct BMVert *v);
|
||||
|
||||
int BM_verts_in_edge(struct BMVert *v1, struct BMVert *v2, struct BMEdge *e);
|
||||
|
||||
/*get opposing vert from v in edge e.*/
|
||||
struct BMVert *BM_edge_other_vert(struct BMEdge *e, struct BMVert *v);
|
||||
|
||||
/*finds other loop that shares v with e's loop in f.*/
|
||||
struct BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v);
|
||||
|
||||
/*returns the edge existing between v1 and v2, or NULL if there isn't one.*/
|
||||
struct BMEdge *BM_edge_exists(struct BMVert *v1, struct BMVert *v2);
|
||||
|
||||
|
||||
/*returns number of edges aroudn a vert*/
|
||||
int BM_vert_edge_count(struct BMVert *v);
|
||||
|
||||
/*returns number of faces around an edge*/
|
||||
int BM_edge_face_count(struct BMEdge *e);
|
||||
|
||||
/*returns number of faces around a vert.*/
|
||||
int BM_vert_face_count(struct BMVert *v);
|
||||
|
||||
|
||||
/*returns true if v is a wire vert*/
|
||||
int BM_vert_is_wire(struct BMesh *bm, struct BMVert *v);
|
||||
|
||||
/*returns true if e is a wire edge*/
|
||||
int BM_edge_is_wire(struct BMesh *bm, struct BMEdge *e);
|
||||
|
||||
/* returns FALSE if v is part of a non-manifold edge in the mesh,
|
||||
* I believe this includes if it's part of both a wire edge and
|
||||
* a face.*/
|
||||
int BM_vert_is_manifold(struct BMesh *bm, struct BMVert *v);
|
||||
|
||||
/* returns FALSE if e is shared by more then two faces. */
|
||||
int BM_edge_is_manifold(struct BMesh *bm, struct BMEdge *e);
|
||||
|
||||
/* returns true if e is a boundary edge, e.g. has only 1 face bordering it. */
|
||||
int BM_edge_is_boundry(struct BMEdge *e);
|
||||
|
||||
|
||||
/* returns angle of two faces surrounding an edge. note there must be
|
||||
* exactly two faces sharing the edge.*/
|
||||
float BM_edge_face_angle(struct BMesh *bm, struct BMEdge *e);
|
||||
|
||||
/* returns angle of two faces surrounding edges. note there must be
|
||||
* exactly two edges sharing the vertex.*/
|
||||
float BM_vert_edge_angle(struct BMesh *bm, struct BMVert *v);
|
||||
|
||||
/* checks overlapping of existing faces with the verts in varr. */
|
||||
int BM_face_exists_overlap(struct BMesh *bm, struct BMVert **varr, int len, struct BMFace **existface);
|
||||
|
||||
/* checks if a face defined by varr already exists. */
|
||||
int BM_face_exists(BMesh *bm, BMVert **varr, int len, BMFace **existface);
|
||||
|
||||
|
||||
/* returns number of edges f1 and f2 share. */
|
||||
int BM_face_share_edges(struct BMFace *f1, struct BMFace *f2);
|
||||
|
||||
/* returns number of faces e1 and e2 share. */
|
||||
int BM_edge_share_faces(struct BMEdge *e1, struct BMEdge *e2);
|
||||
|
||||
/* returns bool 1/0 if the edges share a vertex */
|
||||
int BM_edge_share_vert(struct BMEdge *e1, struct BMEdge *e2);
|
||||
|
||||
/* edge verts in winding order from face */
|
||||
void BM_edge_ordered_verts(struct BMEdge *edge, struct BMVert **r_v1, struct BMVert **r_v2);
|
||||
|
||||
/* checks if a face is valid in the data structure */
|
||||
int BM_face_validate(BMesh *bm, BMFace *face, FILE *err);
|
||||
|
||||
/* each pair of loops defines a new edge, a split. this function goes
|
||||
* through and sets pairs that are geometrically invalid to null. a
|
||||
* split is invalid, if it forms a concave angle or it intersects other
|
||||
* edges in the face.*/
|
||||
void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len);
|
||||
|
||||
#endif /* __BMESH_QUERIES_H__ */
|
||||
139
source/blender/bmesh/bmesh_walkers.h
Normal file
139
source/blender/bmesh/bmesh_walkers.h
Normal 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__ */
|
||||
6384
source/blender/bmesh/editmesh_tools.c
Normal file
6384
source/blender/bmesh/editmesh_tools.c
Normal file
File diff suppressed because it is too large
Load Diff
773
source/blender/bmesh/intern/bmesh_construct.c
Normal file
773
source/blender/bmesh/intern/bmesh_construct.c
Normal 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)
|
||||
);
|
||||
}
|
||||
1215
source/blender/bmesh/intern/bmesh_eulers.c
Normal file
1215
source/blender/bmesh/intern/bmesh_eulers.c
Normal file
File diff suppressed because it is too large
Load Diff
71
source/blender/bmesh/intern/bmesh_inline.c
Normal file
71
source/blender/bmesh/intern/bmesh_inline.c
Normal 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__ */
|
||||
984
source/blender/bmesh/intern/bmesh_interp.c
Normal file
984
source/blender/bmesh/intern/bmesh_interp.c
Normal 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;
|
||||
}
|
||||
417
source/blender/bmesh/intern/bmesh_iterators.c
Normal file
417
source/blender/bmesh/intern/bmesh_iterators.c
Normal 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;
|
||||
}
|
||||
160
source/blender/bmesh/intern/bmesh_iterators_inline.c
Normal file
160
source/blender/bmesh/intern/bmesh_iterators_inline.c
Normal 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__ */
|
||||
910
source/blender/bmesh/intern/bmesh_marking.c
Normal file
910
source/blender/bmesh/intern/bmesh_marking.c
Normal file
@@ -0,0 +1,910 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_marking.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Selection routines for bmesh structures.
|
||||
* This is actually all old code ripped from
|
||||
* editmesh_lib.c and slightly modified to work
|
||||
* for bmesh's. This also means that it has some
|
||||
* of the same problems.... something that
|
||||
* that should be addressed eventually.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
|
||||
/*
|
||||
* BMESH SELECTMODE FLUSH
|
||||
*
|
||||
* Makes sure to flush selections
|
||||
* 'upwards' (ie: all verts of an edge
|
||||
* selects the edge and so on). This
|
||||
* should only be called by system and not
|
||||
* tool authors.
|
||||
*
|
||||
*/
|
||||
|
||||
static void recount_totsels(BMesh *bm)
|
||||
{
|
||||
BMIter iter;
|
||||
BMHeader *ele;
|
||||
const char iter_types[3] = {BM_VERTS_OF_MESH,
|
||||
BM_EDGES_OF_MESH,
|
||||
BM_FACES_OF_MESH};
|
||||
int *tots[3];
|
||||
int i;
|
||||
|
||||
/* recount (tot * sel) variables */
|
||||
bm->totvertsel = bm->totedgesel = bm->totfacesel = 0;
|
||||
tots[0] = &bm->totvertsel;
|
||||
tots[1] = &bm->totedgesel;
|
||||
tots[2] = &bm->totfacesel;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ele = BM_iter_new(&iter, bm, iter_types[i], NULL);
|
||||
for ( ; ele; ele = BM_iter_step(&iter)) {
|
||||
if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) *tots[i] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_select_mode_flush(BMesh *bm)
|
||||
{
|
||||
BMEdge *e;
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
BMFace *f;
|
||||
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
|
||||
int ok;
|
||||
|
||||
if (bm->selectmode & SCE_SELECT_VERTEX) {
|
||||
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
|
||||
if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
|
||||
BM_elem_flag_test(e->v2, BM_ELEM_SELECT) &&
|
||||
!BM_elem_flag_test(e, BM_ELEM_HIDDEN))
|
||||
{
|
||||
BM_elem_flag_enable(e, BM_ELEM_SELECT);
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_disable(e, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
|
||||
ok = TRUE;
|
||||
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
|
||||
ok = FALSE;
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
else {
|
||||
ok = FALSE;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
BM_elem_flag_enable(f, BM_ELEM_SELECT);
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_disable(f, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bm->selectmode & SCE_SELECT_EDGE) {
|
||||
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
|
||||
ok = TRUE;
|
||||
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
if (!BM_elem_flag_test(&(l_iter->e->head), BM_ELEM_SELECT)) {
|
||||
ok = FALSE;
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
else {
|
||||
ok = FALSE;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
BM_elem_flag_enable(f, BM_ELEM_SELECT);
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_disable(f, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove any deselected elements from the BMEditSelection */
|
||||
BM_select_history_validate(bm);
|
||||
|
||||
recount_totsels(bm);
|
||||
}
|
||||
|
||||
/* BMESH NOTE: matches EM_deselect_flush() behavior from trunk */
|
||||
void BM_mesh_deselect_flush(BMesh *bm)
|
||||
{
|
||||
BMEdge *e;
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
BMFace *f;
|
||||
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
|
||||
int ok;
|
||||
|
||||
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
|
||||
if (!(BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
|
||||
BM_elem_flag_test(e->v2, BM_ELEM_SELECT) &&
|
||||
!BM_elem_flag_test(e, BM_ELEM_HIDDEN)))
|
||||
{
|
||||
BM_elem_flag_disable(e, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
|
||||
ok = TRUE;
|
||||
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
|
||||
ok = FALSE;
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
else {
|
||||
ok = FALSE;
|
||||
}
|
||||
|
||||
if (ok == FALSE) {
|
||||
BM_elem_flag_disable(f, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove any deselected elements from the BMEditSelection */
|
||||
BM_select_history_validate(bm);
|
||||
|
||||
recount_totsels(bm);
|
||||
}
|
||||
|
||||
|
||||
/* BMESH NOTE: matches EM_select_flush() behavior from trunk */
|
||||
void BM_mesh_select_flush(BMesh *bm)
|
||||
{
|
||||
BMEdge *e;
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
BMFace *f;
|
||||
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
|
||||
int ok;
|
||||
|
||||
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
|
||||
if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
|
||||
BM_elem_flag_test(e->v2, BM_ELEM_SELECT) &&
|
||||
!BM_elem_flag_test(e, BM_ELEM_HIDDEN))
|
||||
{
|
||||
BM_elem_flag_enable(e, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
|
||||
ok = TRUE;
|
||||
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
|
||||
ok = FALSE;
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
else {
|
||||
ok = FALSE;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
BM_elem_flag_enable(f, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
recount_totsels(bm);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH SELECT VERT
|
||||
*
|
||||
* Changes selection state of a single vertex
|
||||
* in a mesh
|
||||
*
|
||||
*/
|
||||
|
||||
void BM_vert_select_set(BMesh *bm, BMVert *v, int select)
|
||||
{
|
||||
/* BMIter iter; */
|
||||
/* BMEdge *e; */
|
||||
|
||||
if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) {
|
||||
bm->totvertsel += 1;
|
||||
BM_elem_flag_enable(v, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
|
||||
bm->totvertsel -= 1;
|
||||
BM_elem_flag_disable(v, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH SELECT EDGE
|
||||
*
|
||||
* Changes selection state of a single edge
|
||||
* in a mesh.
|
||||
*
|
||||
*/
|
||||
|
||||
void BM_edge_select_set(BMesh *bm, BMEdge *e, int select)
|
||||
{
|
||||
if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel += 1;
|
||||
|
||||
BM_elem_flag_enable(&(e->head), BM_ELEM_SELECT);
|
||||
BM_elem_select_set(bm, e->v1, TRUE);
|
||||
BM_elem_select_set(bm, e->v2, TRUE);
|
||||
}
|
||||
else {
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel -= 1;
|
||||
BM_elem_flag_disable(&(e->head), BM_ELEM_SELECT);
|
||||
|
||||
if ( bm->selectmode == SCE_SELECT_EDGE ||
|
||||
bm->selectmode == SCE_SELECT_FACE ||
|
||||
bm->selectmode == (SCE_SELECT_EDGE | SCE_SELECT_FACE))
|
||||
{
|
||||
|
||||
BMIter iter;
|
||||
BMVert *verts[2] = {e->v1, e->v2};
|
||||
BMEdge *e2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int deselect = 1;
|
||||
|
||||
for (e2 = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, verts[i]); e2; e2 = BM_iter_step(&iter)) {
|
||||
if (e2 == e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BM_elem_flag_test(e2, BM_ELEM_SELECT)) {
|
||||
deselect = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (deselect) BM_vert_select_set(bm, verts[i], FALSE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BM_elem_select_set(bm, e->v1, FALSE);
|
||||
BM_elem_select_set(bm, e->v2, FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* BMESH SELECT FACE
|
||||
*
|
||||
* Changes selection state of a single
|
||||
* face in a mesh.
|
||||
*
|
||||
*/
|
||||
|
||||
void BM_face_select_set(BMesh *bm, BMFace *f, int select)
|
||||
{
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
|
||||
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) {
|
||||
bm->totfacesel++;
|
||||
}
|
||||
|
||||
BM_elem_flag_enable(&(f->head), BM_ELEM_SELECT);
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BM_vert_select_set(bm, l_iter->v, TRUE);
|
||||
BM_edge_select_set(bm, l_iter->e, TRUE);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
else {
|
||||
BMIter liter;
|
||||
BMLoop *l;
|
||||
|
||||
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) bm->totfacesel -= 1;
|
||||
BM_elem_flag_disable(&(f->head), BM_ELEM_SELECT);
|
||||
|
||||
/* flush down to edges */
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
BMIter fiter;
|
||||
BMFace *f2;
|
||||
BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
|
||||
if (BM_elem_flag_test(f2, BM_ELEM_SELECT))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!f2)
|
||||
{
|
||||
BM_elem_select_set(bm, l->e, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/* flush down to verts */
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
BMIter eiter;
|
||||
BMEdge *e;
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, l->v) {
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SELECT))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!e) {
|
||||
BM_elem_select_set(bm, l->v, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH SELECTMODE SET
|
||||
*
|
||||
* Sets the selection mode for the bmesh
|
||||
*
|
||||
*/
|
||||
|
||||
void BM_select_mode_set(BMesh *bm, int selectmode)
|
||||
{
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
|
||||
BMIter verts;
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
|
||||
bm->selectmode = selectmode;
|
||||
|
||||
if (bm->selectmode & SCE_SELECT_VERTEX) {
|
||||
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges))
|
||||
BM_elem_flag_disable(e, 0);
|
||||
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces))
|
||||
BM_elem_flag_disable(f, 0);
|
||||
BM_mesh_select_mode_flush(bm);
|
||||
}
|
||||
else if (bm->selectmode & SCE_SELECT_EDGE) {
|
||||
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts))
|
||||
BM_elem_flag_disable(v, 0);
|
||||
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
|
||||
if (BM_elem_flag_test(&(e->head), BM_ELEM_SELECT)) {
|
||||
BM_edge_select_set(bm, e, TRUE);
|
||||
}
|
||||
}
|
||||
BM_mesh_select_mode_flush(bm);
|
||||
}
|
||||
else if (bm->selectmode & SCE_SELECT_FACE) {
|
||||
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges))
|
||||
BM_elem_flag_disable(e, 0);
|
||||
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
|
||||
if (BM_elem_flag_test(&(f->head), BM_ELEM_SELECT)) {
|
||||
BM_face_select_set(bm, f, TRUE);
|
||||
}
|
||||
}
|
||||
BM_mesh_select_mode_flush(bm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide)
|
||||
{
|
||||
BMHeader *head;
|
||||
BMIter iter;
|
||||
int tot = 0;
|
||||
|
||||
if (htype & BM_VERT) {
|
||||
for (head = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); head; head = BM_iter_step(&iter)) {
|
||||
if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue;
|
||||
if (BM_elem_flag_test(head, hflag)) tot++;
|
||||
}
|
||||
}
|
||||
if (htype & BM_EDGE) {
|
||||
for (head = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) {
|
||||
if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue;
|
||||
if (BM_elem_flag_test(head, hflag)) tot++;
|
||||
}
|
||||
}
|
||||
if (htype & BM_FACE) {
|
||||
for (head = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) {
|
||||
if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue;
|
||||
if (BM_elem_flag_test(head, hflag)) tot++;
|
||||
}
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
/* note: by design, this will not touch the editselection history stuff */
|
||||
void BM_elem_select_set(struct BMesh *bm, void *element, int select)
|
||||
{
|
||||
BMHeader *head = element;
|
||||
|
||||
if (head->htype == BM_VERT) BM_vert_select_set(bm, (BMVert *)element, select);
|
||||
else if (head->htype == BM_EDGE) BM_edge_select_set(bm, (BMEdge *)element, select);
|
||||
else if (head->htype == BM_FACE) BM_face_select_set(bm, (BMFace *)element, select);
|
||||
}
|
||||
|
||||
/* this replaces the active flag used in uv/face mode */
|
||||
void BM_active_face_set(BMesh *bm, BMFace *efa)
|
||||
{
|
||||
bm->act_face = efa;
|
||||
}
|
||||
|
||||
BMFace *BM_active_face_get(BMesh *bm, int sloppy)
|
||||
{
|
||||
if (bm->act_face) {
|
||||
return bm->act_face;
|
||||
}
|
||||
else if (sloppy) {
|
||||
BMIter iter;
|
||||
BMFace *f = NULL;
|
||||
BMEditSelection *ese;
|
||||
|
||||
/* Find the latest non-hidden face from the BMEditSelection */
|
||||
ese = bm->selected.last;
|
||||
for ( ; ese; ese = ese->prev) {
|
||||
if (ese->htype == BM_FACE) {
|
||||
f = (BMFace *)ese->data;
|
||||
|
||||
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
||||
f = NULL;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Last attempt: try to find any selected face */
|
||||
if (f == NULL) {
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return f; /* can still be null */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Generic way to get data from an EditSelection type
|
||||
* These functions were written to be used by the Modifier widget
|
||||
* when in Rotate about active mode, but can be used anywhere.
|
||||
*
|
||||
* - EM_editselection_center
|
||||
* - EM_editselection_normal
|
||||
* - EM_editselection_plane
|
||||
*/
|
||||
void BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese)
|
||||
{
|
||||
if (ese->htype == BM_VERT) {
|
||||
BMVert *eve = ese->data;
|
||||
copy_v3_v3(r_center, eve->co);
|
||||
}
|
||||
else if (ese->htype == BM_EDGE) {
|
||||
BMEdge *eed = ese->data;
|
||||
add_v3_v3v3(r_center, eed->v1->co, eed->v2->co);
|
||||
mul_v3_fl(r_center, 0.5);
|
||||
}
|
||||
else if (ese->htype == BM_FACE) {
|
||||
BMFace *efa = ese->data;
|
||||
BM_face_center_bounds_calc(bm, efa, r_center);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_editselection_normal(float r_normal[3], BMEditSelection *ese)
|
||||
{
|
||||
if (ese->htype == BM_VERT) {
|
||||
BMVert *eve = ese->data;
|
||||
copy_v3_v3(r_normal, eve->no);
|
||||
}
|
||||
else if (ese->htype == BM_EDGE) {
|
||||
BMEdge *eed = ese->data;
|
||||
float plane[3]; /* need a plane to correct the normal */
|
||||
float vec[3]; /* temp vec storage */
|
||||
|
||||
add_v3_v3v3(r_normal, eed->v1->no, eed->v2->no);
|
||||
sub_v3_v3v3(plane, eed->v2->co, eed->v1->co);
|
||||
|
||||
/* the 2 vertex normals will be close but not at rightangles to the edge
|
||||
* for rotate about edge we want them to be at right angles, so we need to
|
||||
* do some extra colculation to correct the vert normals,
|
||||
* we need the plane for this */
|
||||
cross_v3_v3v3(vec, r_normal, plane);
|
||||
cross_v3_v3v3(r_normal, plane, vec);
|
||||
normalize_v3(r_normal);
|
||||
|
||||
}
|
||||
else if (ese->htype == BM_FACE) {
|
||||
BMFace *efa = ese->data;
|
||||
copy_v3_v3(r_normal, efa->no);
|
||||
}
|
||||
}
|
||||
|
||||
/* ref - editmesh_lib.cL:EM_editselection_plane() */
|
||||
|
||||
/* Calculate a plane that is rightangles to the edge/vert/faces normal
|
||||
* also make the plane run along an axis that is related to the geometry,
|
||||
* because this is used for the manipulators Y axis. */
|
||||
void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese)
|
||||
{
|
||||
if (ese->htype == BM_VERT) {
|
||||
BMVert *eve = ese->data;
|
||||
float vec[3] = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
if (ese->prev) { /* use previously selected data to make a useful vertex plane */
|
||||
BM_editselection_center(bm, vec, ese->prev);
|
||||
sub_v3_v3v3(r_plane, vec, eve->co);
|
||||
}
|
||||
else {
|
||||
/* make a fake plane thats at rightangles to the normal
|
||||
* we cant make a crossvec from a vec thats the same as the vec
|
||||
* unlikely but possible, so make sure if the normal is (0, 0, 1)
|
||||
* that vec isnt the same or in the same direction even. */
|
||||
if (eve->no[0] < 0.5f) vec[0] = 1.0f;
|
||||
else if (eve->no[1] < 0.5f) vec[1] = 1.0f;
|
||||
else vec[2] = 1.0f;
|
||||
cross_v3_v3v3(r_plane, eve->no, vec);
|
||||
}
|
||||
}
|
||||
else if (ese->htype == BM_EDGE) {
|
||||
BMEdge *eed = ese->data;
|
||||
|
||||
/* the plane is simple, it runs along the edge
|
||||
* however selecting different edges can swap the direction of the y axis.
|
||||
* this makes it less likely for the y axis of the manipulator
|
||||
* (running along the edge).. to flip less often.
|
||||
* at least its more pradictable */
|
||||
if (eed->v2->co[1] > eed->v1->co[1]) { /* check which to do first */
|
||||
sub_v3_v3v3(r_plane, eed->v2->co, eed->v1->co);
|
||||
}
|
||||
else {
|
||||
sub_v3_v3v3(r_plane, eed->v1->co, eed->v2->co);
|
||||
}
|
||||
|
||||
}
|
||||
else if (ese->htype == BM_FACE) {
|
||||
BMFace *efa = ese->data;
|
||||
float vec[3] = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
/* for now, use face normal */
|
||||
|
||||
/* make a fake plane thats at rightangles to the normal
|
||||
* we cant make a crossvec from a vec thats the same as the vec
|
||||
* unlikely but possible, so make sure if the normal is (0, 0, 1)
|
||||
* that vec isnt the same or in the same direction even. */
|
||||
if (efa->len < 3) {
|
||||
/* crappy fallback method */
|
||||
if (efa->no[0] < 0.5f) vec[0] = 1.0f;
|
||||
else if (efa->no[1] < 0.5f) vec[1] = 1.0f;
|
||||
else vec[2] = 1.0f;
|
||||
cross_v3_v3v3(r_plane, efa->no, vec);
|
||||
}
|
||||
else {
|
||||
BMVert *verts[4] = {NULL};
|
||||
|
||||
BM_iter_as_array(bm, BM_VERTS_OF_FACE, efa, (void **)verts, 4);
|
||||
|
||||
if (efa->len == 4) {
|
||||
float vecA[3], vecB[3];
|
||||
sub_v3_v3v3(vecA, verts[3]->co, verts[2]->co);
|
||||
sub_v3_v3v3(vecB, verts[0]->co, verts[1]->co);
|
||||
add_v3_v3v3(r_plane, vecA, vecB);
|
||||
|
||||
sub_v3_v3v3(vecA, verts[0]->co, verts[3]->co);
|
||||
sub_v3_v3v3(vecB, verts[1]->co, verts[2]->co);
|
||||
add_v3_v3v3(vec, vecA, vecB);
|
||||
/* use the biggest edge length */
|
||||
if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) {
|
||||
copy_v3_v3(r_plane, vec);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* BMESH_TODO (not urgent, use longest ngon edge for alignment) */
|
||||
|
||||
/* start with v1-2 */
|
||||
sub_v3_v3v3(r_plane, verts[0]->co, verts[1]->co);
|
||||
|
||||
/* test the edge between v2-3, use if longer */
|
||||
sub_v3_v3v3(vec, verts[1]->co, verts[2]->co);
|
||||
if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec))
|
||||
copy_v3_v3(r_plane, vec);
|
||||
|
||||
/* test the edge between v1-3, use if longer */
|
||||
sub_v3_v3v3(vec, verts[2]->co, verts[0]->co);
|
||||
if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) {
|
||||
copy_v3_v3(r_plane, vec);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
normalize_v3(r_plane);
|
||||
}
|
||||
|
||||
int BM_select_history_check(BMesh *bm, void *data)
|
||||
{
|
||||
BMEditSelection *ese;
|
||||
|
||||
for (ese = bm->selected.first; ese; ese = ese->next) {
|
||||
if (ese->data == data) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void BM_select_history_remove(BMesh *bm, void *data)
|
||||
{
|
||||
BMEditSelection *ese;
|
||||
for (ese = bm->selected.first; ese; ese = ese->next) {
|
||||
if (ese->data == data) {
|
||||
BLI_freelinkN(&(bm->selected), ese);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BM_select_history_clear(BMesh *bm)
|
||||
{
|
||||
BLI_freelistN(&bm->selected);
|
||||
bm->selected.first = bm->selected.last = NULL;
|
||||
}
|
||||
|
||||
void BM_select_history_store(BMesh *bm, void *data)
|
||||
{
|
||||
BMEditSelection *ese;
|
||||
if (!BM_select_history_check(bm, data)) {
|
||||
ese = (BMEditSelection *) MEM_callocN(sizeof(BMEditSelection), "BMEdit Selection");
|
||||
ese->htype = ((BMHeader *)data)->htype;
|
||||
ese->data = data;
|
||||
BLI_addtail(&(bm->selected), ese);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_select_history_validate(BMesh *bm)
|
||||
{
|
||||
BMEditSelection *ese, *nextese;
|
||||
|
||||
ese = bm->selected.first;
|
||||
|
||||
while (ese) {
|
||||
nextese = ese->next;
|
||||
if (!BM_elem_flag_test(ese->data, BM_ELEM_SELECT)) {
|
||||
BLI_freelinkN(&(bm->selected), ese);
|
||||
}
|
||||
ese = nextese;
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag)
|
||||
{
|
||||
const char iter_types[3] = {BM_VERTS_OF_MESH,
|
||||
BM_EDGES_OF_MESH,
|
||||
BM_FACES_OF_MESH};
|
||||
BMIter iter;
|
||||
BMHeader *ele;
|
||||
int i;
|
||||
|
||||
if (hflag & BM_ELEM_SELECT) {
|
||||
BM_select_history_clear(bm);
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (htype & iter_types[i]) {
|
||||
ele = BM_iter_new(&iter, bm, iter_types[i], NULL);
|
||||
for ( ; ele; ele = BM_iter_step(&iter)) {
|
||||
if (hflag & BM_ELEM_SELECT) {
|
||||
BM_elem_select_set(bm, ele, FALSE);
|
||||
}
|
||||
BM_elem_flag_disable(ele, hflag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag)
|
||||
{
|
||||
const char iter_types[3] = {BM_VERTS_OF_MESH,
|
||||
BM_EDGES_OF_MESH,
|
||||
BM_FACES_OF_MESH};
|
||||
BMIter iter;
|
||||
BMHeader *ele;
|
||||
int i;
|
||||
|
||||
if (hflag & BM_ELEM_SELECT) {
|
||||
BM_select_history_clear(bm);
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (htype & iter_types[i]) {
|
||||
ele = BM_iter_new(&iter, bm, iter_types[i], NULL);
|
||||
for ( ; ele; ele = BM_iter_step(&iter)) {
|
||||
if (hflag & BM_ELEM_SELECT) {
|
||||
BM_elem_select_set(bm, ele, TRUE);
|
||||
}
|
||||
BM_elem_flag_enable(ele, hflag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** Mesh Hiding stuff *********** */
|
||||
|
||||
#define BM_ELEM_HIDE_SET(ele, hide) \
|
||||
(hide) ? BM_elem_flag_enable(ele, BM_ELEM_HIDDEN) : BM_elem_flag_disable(ele, BM_ELEM_HIDDEN);
|
||||
|
||||
static void vert_flush_hide_set(BMesh *bm, BMVert *v)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
int hide = TRUE;
|
||||
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
|
||||
hide = hide && BM_elem_flag_test(e, BM_ELEM_HIDDEN);
|
||||
}
|
||||
|
||||
BM_ELEM_HIDE_SET(v, hide);
|
||||
}
|
||||
|
||||
static void edge_flush_hide(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int hide = TRUE;
|
||||
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) {
|
||||
hide = hide && BM_elem_flag_test(f, BM_ELEM_HIDDEN);
|
||||
}
|
||||
|
||||
BM_ELEM_HIDE_SET(e, hide);
|
||||
}
|
||||
|
||||
void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide)
|
||||
{
|
||||
/* vert hiding: vert + surrounding edges and faces */
|
||||
BMIter iter, fiter;
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
|
||||
BM_ELEM_HIDE_SET(v, hide);
|
||||
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
|
||||
BM_ELEM_HIDE_SET(e, hide);
|
||||
|
||||
BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
|
||||
BM_ELEM_HIDE_SET(f, hide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
/* BMVert *v; */
|
||||
|
||||
/* edge hiding: faces around the edge */
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) {
|
||||
BM_ELEM_HIDE_SET(f, hide);
|
||||
}
|
||||
|
||||
BM_ELEM_HIDE_SET(e, hide);
|
||||
|
||||
/* hide vertices if necassary */
|
||||
vert_flush_hide_set(bm, e->v1);
|
||||
vert_flush_hide_set(bm, e->v2);
|
||||
}
|
||||
|
||||
void BM_face_hide_set(BMesh *bm, BMFace *f, int hide)
|
||||
{
|
||||
BMIter iter;
|
||||
BMLoop *l;
|
||||
|
||||
BM_ELEM_HIDE_SET(f, hide);
|
||||
|
||||
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
edge_flush_hide(bm, l->e);
|
||||
}
|
||||
|
||||
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
vert_flush_hide_set(bm, l->v);
|
||||
}
|
||||
}
|
||||
|
||||
#undef BM_ELEM_HIDE_SET
|
||||
|
||||
|
||||
void BM_elem_hide_set(BMesh *bm, void *element, int hide)
|
||||
{
|
||||
BMHeader *h = element;
|
||||
|
||||
/* Follow convention of always deselecting before
|
||||
* hiding an element */
|
||||
if (hide) {
|
||||
BM_elem_select_set(bm, element, FALSE);
|
||||
}
|
||||
|
||||
switch (h->htype) {
|
||||
case BM_VERT:
|
||||
BM_vert_hide_set(bm, element, hide);
|
||||
break;
|
||||
case BM_EDGE:
|
||||
BM_edge_hide_set(bm, element, hide);
|
||||
break;
|
||||
case BM_FACE:
|
||||
BM_face_hide_set(bm, element, hide);
|
||||
break;
|
||||
}
|
||||
}
|
||||
625
source/blender/bmesh/intern/bmesh_mesh.c
Normal file
625
source/blender/bmesh/intern/bmesh_mesh.c
Normal file
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Geoffrey Bantle.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_mesh.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM mesh level functions.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_utildefines.h"
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_tessmesh.h"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_multires.h"
|
||||
|
||||
#include "ED_mesh.h"
|
||||
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/* used as an extern, defined in bmesh.h */
|
||||
int bm_mesh_allocsize_default[4] = {512, 512, 2048, 512};
|
||||
|
||||
/* bmesh_error stub */
|
||||
void bmesh_error(void)
|
||||
{
|
||||
printf("BM modelling error!\n");
|
||||
|
||||
/* This placeholder assert makes modelling errors easier to catch
|
||||
* in the debugger, until bmesh_error is replaced with something
|
||||
* better. */
|
||||
BLI_assert(0);
|
||||
}
|
||||
|
||||
static void bmesh_mempool_init(BMesh *bm, const int allocsize[4])
|
||||
{
|
||||
bm->vpool = BLI_mempool_create(sizeof(BMVert), allocsize[0], allocsize[0], FALSE, TRUE);
|
||||
bm->epool = BLI_mempool_create(sizeof(BMEdge), allocsize[1], allocsize[1], FALSE, TRUE);
|
||||
bm->lpool = BLI_mempool_create(sizeof(BMLoop), allocsize[2], allocsize[2], FALSE, FALSE);
|
||||
bm->fpool = BLI_mempool_create(sizeof(BMFace), allocsize[3], allocsize[3], FALSE, TRUE);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
bm->looplistpool = BLI_mempool_create(sizeof(BMLoopList), allocsize[3], allocsize[3], FALSE, FALSE);
|
||||
#endif
|
||||
|
||||
/* allocate one flag pool that we dont get rid of. */
|
||||
bm->toolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), 512, 512, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH MAKE MESH
|
||||
*
|
||||
* Allocates a new BMesh structure.
|
||||
* Returns -
|
||||
* Pointer to a BM
|
||||
*
|
||||
*/
|
||||
|
||||
BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4])
|
||||
{
|
||||
/* allocate the structure */
|
||||
BMesh *bm = MEM_callocN(sizeof(BMesh), __func__);
|
||||
|
||||
bm->ob = ob;
|
||||
|
||||
/* allocate the memory pools for the mesh elements */
|
||||
bmesh_mempool_init(bm, allocsize);
|
||||
|
||||
/* allocate one flag pool that we dont get rid of. */
|
||||
bm->stackdepth = 1;
|
||||
bm->totflags = 1;
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH FREE MESH
|
||||
*
|
||||
* Frees a BMesh structure.
|
||||
*/
|
||||
|
||||
void BM_mesh_data_free(BMesh *bm)
|
||||
{
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMLoop *l;
|
||||
BMFace *f;
|
||||
|
||||
|
||||
BMIter verts;
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
BMIter loops;
|
||||
|
||||
for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) {
|
||||
CustomData_bmesh_free_block(&(bm->vdata), &(v->head.data));
|
||||
}
|
||||
for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) {
|
||||
CustomData_bmesh_free_block(&(bm->edata), &(e->head.data));
|
||||
}
|
||||
for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) {
|
||||
CustomData_bmesh_free_block(&(bm->pdata), &(f->head.data));
|
||||
for (l = BM_iter_new(&loops, bm, BM_LOOPS_OF_FACE, f); l; l = BM_iter_step(&loops)) {
|
||||
CustomData_bmesh_free_block(&(bm->ldata), &(l->head.data));
|
||||
}
|
||||
}
|
||||
|
||||
/* Free custom data pools, This should probably go in CustomData_free? */
|
||||
if (bm->vdata.totlayer) BLI_mempool_destroy(bm->vdata.pool);
|
||||
if (bm->edata.totlayer) BLI_mempool_destroy(bm->edata.pool);
|
||||
if (bm->ldata.totlayer) BLI_mempool_destroy(bm->ldata.pool);
|
||||
if (bm->pdata.totlayer) BLI_mempool_destroy(bm->pdata.pool);
|
||||
|
||||
/* free custom data */
|
||||
CustomData_free(&bm->vdata, 0);
|
||||
CustomData_free(&bm->edata, 0);
|
||||
CustomData_free(&bm->ldata, 0);
|
||||
CustomData_free(&bm->pdata, 0);
|
||||
|
||||
/* destroy element pools */
|
||||
BLI_mempool_destroy(bm->vpool);
|
||||
BLI_mempool_destroy(bm->epool);
|
||||
BLI_mempool_destroy(bm->lpool);
|
||||
BLI_mempool_destroy(bm->fpool);
|
||||
|
||||
/* destroy flag pool */
|
||||
BLI_mempool_destroy(bm->toolflagpool);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BLI_mempool_destroy(bm->looplistpool);
|
||||
#endif
|
||||
|
||||
/* These tables aren't used yet, so it's not stricly necessary
|
||||
* to 'end' them (with 'e' param) but if someone tries to start
|
||||
* using them, having these in place will save a lot of pain */
|
||||
mesh_octree_table(NULL, NULL, NULL, 'e');
|
||||
mesh_mirrtopo_table(NULL, 'e');
|
||||
|
||||
BLI_freelistN(&bm->selected);
|
||||
|
||||
BMO_error_clear(bm);
|
||||
}
|
||||
|
||||
void BM_mesh_clear(BMesh *bm)
|
||||
{
|
||||
Object *ob = bm->ob;
|
||||
|
||||
/* free old mesh */
|
||||
BM_mesh_data_free(bm);
|
||||
memset(bm, 0, sizeof(BMesh));
|
||||
|
||||
/* re-initialize mesh */
|
||||
bm->ob = ob;
|
||||
|
||||
/* allocate the memory pools for the mesh elements */
|
||||
bmesh_mempool_init(bm, bm_mesh_allocsize_default);
|
||||
|
||||
bm->stackdepth = 1;
|
||||
bm->totflags = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH FREE MESH
|
||||
*
|
||||
* Frees a BMesh structure.
|
||||
*/
|
||||
|
||||
void BM_mesh_free(BMesh *bm)
|
||||
{
|
||||
BM_mesh_data_free(bm);
|
||||
MEM_freeN(bm);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH COMPUTE NORMALS
|
||||
*
|
||||
* Updates the normals of a mesh.
|
||||
* Note that this can only be called
|
||||
*
|
||||
*/
|
||||
|
||||
void BM_mesh_normals_update(BMesh *bm)
|
||||
{
|
||||
BMVert *v;
|
||||
BMFace *f;
|
||||
BMLoop *l;
|
||||
BMEdge *e;
|
||||
BMIter verts;
|
||||
BMIter faces;
|
||||
BMIter loops;
|
||||
BMIter edges;
|
||||
unsigned int maxlength = 0;
|
||||
int index;
|
||||
float (*projectverts)[3];
|
||||
float (*edgevec)[3];
|
||||
|
||||
/* first, find out the largest face in mesh */
|
||||
BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) {
|
||||
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN))
|
||||
continue;
|
||||
|
||||
if (f->len > maxlength) maxlength = f->len;
|
||||
}
|
||||
|
||||
/* make sure we actually have something to do */
|
||||
if (maxlength < 3) return;
|
||||
|
||||
/* allocate projectverts array */
|
||||
projectverts = MEM_callocN(sizeof(float) * maxlength * 3, "BM normal computation array");
|
||||
|
||||
/* calculate all face normals */
|
||||
BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) {
|
||||
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN))
|
||||
continue;
|
||||
#if 0 /* UNUSED */
|
||||
if (f->head.flag & BM_NONORMCALC)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
bmesh_update_face_normal(bm, f, f->no, projectverts);
|
||||
}
|
||||
|
||||
/* Zero out vertex normals */
|
||||
BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if (BM_elem_flag_test(v, BM_ELEM_HIDDEN))
|
||||
continue;
|
||||
|
||||
zero_v3(v->no);
|
||||
}
|
||||
|
||||
/* compute normalized direction vectors for each edge. directions will be
|
||||
* used below for calculating the weights of the face normals on the vertex
|
||||
* normals */
|
||||
index = 0;
|
||||
edgevec = MEM_callocN(sizeof(float) * 3 * bm->totedge, "BM normal computation array");
|
||||
BM_ITER(e, &edges, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
BM_elem_index_set(e, index); /* set_inline */
|
||||
|
||||
if (e->l) {
|
||||
sub_v3_v3v3(edgevec[index], e->v2->co, e->v1->co);
|
||||
normalize_v3(edgevec[index]);
|
||||
}
|
||||
else {
|
||||
/* the edge vector will not be needed when the edge has no radial */
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_EDGE;
|
||||
|
||||
/* add weighted face normals to vertices */
|
||||
BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) {
|
||||
|
||||
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN))
|
||||
continue;
|
||||
|
||||
BM_ITER(l, &loops, bm, BM_LOOPS_OF_FACE, f) {
|
||||
float *e1diff, *e2diff;
|
||||
float dotprod;
|
||||
float fac;
|
||||
|
||||
/* calculate the dot product of the two edges that
|
||||
* meet at the loop's vertex */
|
||||
e1diff = edgevec[BM_elem_index_get(l->prev->e)];
|
||||
e2diff = edgevec[BM_elem_index_get(l->e)];
|
||||
dotprod = dot_v3v3(e1diff, e2diff);
|
||||
|
||||
/* edge vectors are calculated from e->v1 to e->v2, so
|
||||
* adjust the dot product if one but not both loops
|
||||
* actually runs from from e->v2 to e->v1 */
|
||||
if ((l->prev->e->v1 == l->prev->v) ^ (l->e->v1 == l->v)) {
|
||||
dotprod = -dotprod;
|
||||
}
|
||||
|
||||
fac = saacos(-dotprod);
|
||||
|
||||
/* accumulate weighted face normal into the vertex's normal */
|
||||
madd_v3_v3fl(l->v->no, f->no, fac);
|
||||
}
|
||||
}
|
||||
|
||||
/* normalize the accumulated vertex normals */
|
||||
BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if (BM_elem_flag_test(v, BM_ELEM_HIDDEN))
|
||||
continue;
|
||||
|
||||
if (normalize_v3(v->no) == 0.0f) {
|
||||
normalize_v3_v3(v->no, v->co);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(edgevec);
|
||||
MEM_freeN(projectverts);
|
||||
}
|
||||
|
||||
/*
|
||||
This function ensures correct normals for the mesh, but
|
||||
sets the flag BM_ELEM_TAG in flipped faces, to allow restoration
|
||||
of original normals.
|
||||
|
||||
if undo is 0: calculate right normals
|
||||
if undo is 1: restore original normals
|
||||
*/
|
||||
//keep in sycn with utils.c!
|
||||
#define FACE_FLIP 8
|
||||
static void bmesh_rationalize_normals(BMesh *bm, int undo)
|
||||
{
|
||||
BMOperator bmop;
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
|
||||
if (undo) {
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
if (BM_elem_flag_test(f, BM_ELEM_TAG)) {
|
||||
BM_face_normal_flip(bm, f);
|
||||
}
|
||||
BM_elem_flag_disable(f, BM_ELEM_TAG);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BMO_op_initf(bm, &bmop, "righthandfaces faces=%af doflip=%d", FALSE);
|
||||
|
||||
BMO_push(bm, &bmop);
|
||||
bmesh_righthandfaces_exec(bm, &bmop);
|
||||
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
if (BMO_elem_flag_test(bm, f, FACE_FLIP))
|
||||
BM_elem_flag_enable(f, BM_ELEM_TAG);
|
||||
else BM_elem_flag_disable(f, BM_ELEM_TAG);
|
||||
}
|
||||
|
||||
BMO_pop(bm);
|
||||
BMO_op_finish(bm, &bmop);
|
||||
}
|
||||
|
||||
static void bmesh_set_mdisps_space(BMesh *bm, int from, int to)
|
||||
{
|
||||
/* switch multires data out of tangent space */
|
||||
if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
|
||||
Object *ob = bm->ob;
|
||||
BMEditMesh *em = BMEdit_Create(bm, FALSE);
|
||||
DerivedMesh *dm = CDDM_from_BMEditMesh(em, NULL, TRUE, FALSE);
|
||||
MDisps *mdisps;
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
// int i = 0; // UNUSED
|
||||
|
||||
multires_set_space(dm, ob, from, to);
|
||||
|
||||
mdisps = CustomData_get_layer(&dm->loopData, CD_MDISPS);
|
||||
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
BMLoop *l;
|
||||
BMIter liter;
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
MDisps *lmd = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS);
|
||||
|
||||
if (!lmd->disps) {
|
||||
printf("%s: warning - 'lmd->disps' == NULL\n", __func__);
|
||||
}
|
||||
|
||||
if (lmd->disps && lmd->totdisp == mdisps->totdisp) {
|
||||
memcpy(lmd->disps, mdisps->disps, sizeof(float) * 3 * lmd->totdisp);
|
||||
}
|
||||
else if (mdisps->disps) {
|
||||
if (lmd->disps)
|
||||
MEM_freeN(lmd->disps);
|
||||
|
||||
lmd->disps = MEM_dupallocN(mdisps->disps);
|
||||
lmd->totdisp = mdisps->totdisp;
|
||||
}
|
||||
|
||||
mdisps++;
|
||||
// i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
dm->needsFree = 1;
|
||||
dm->release(dm);
|
||||
|
||||
/* setting this to NULL prevents BMEdit_Free from freeing it */
|
||||
em->bm = NULL;
|
||||
BMEdit_Free(em);
|
||||
MEM_freeN(em);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH BEGIN/END EDIT
|
||||
*
|
||||
* Functions for setting up a mesh for editing and cleaning up after
|
||||
* the editing operations are done. These are called by the tools/operator
|
||||
* API for each time a tool is executed.
|
||||
*/
|
||||
void bmesh_begin_edit(BMesh *bm, int flag)
|
||||
{
|
||||
bm->opflag = flag;
|
||||
|
||||
/* Most operators seem to be using BMO_OP_FLAG_UNTAN_MULTIRES to change the MDisps to
|
||||
* absolute space during mesh edits. With this enabled, changes to the topology
|
||||
* (loop cuts, edge subdivides, etc) are not reflected in the higher levels of
|
||||
* the mesh at all, which doesn't seem right. Turning off completely for now,
|
||||
* until this is shown to be better for certain types of mesh edits. */
|
||||
#if BMOP_UNTAN_MULTIRES_ENABLED
|
||||
/* switch multires data out of tangent space */
|
||||
if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
|
||||
bmesh_set_mdisps_space(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE);
|
||||
|
||||
/* ensure correct normals, if possible */
|
||||
bmesh_rationalize_normals(bm, 0);
|
||||
BM_mesh_normals_update(bm);
|
||||
}
|
||||
else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
|
||||
bmesh_rationalize_normals(bm, 0);
|
||||
}
|
||||
#else
|
||||
if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
|
||||
bmesh_rationalize_normals(bm, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void bmesh_end_edit(BMesh *bm, int flag)
|
||||
{
|
||||
/* BMO_OP_FLAG_UNTAN_MULTIRES disabled for now, see comment above in bmesh_begin_edit. */
|
||||
#if BMOP_UNTAN_MULTIRES_ENABLED
|
||||
/* switch multires data into tangent space */
|
||||
if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
|
||||
/* set normals to their previous winding */
|
||||
bmesh_rationalize_normals(bm, 1);
|
||||
bmesh_set_mdisps_space(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT);
|
||||
}
|
||||
else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
|
||||
bmesh_rationalize_normals(bm, 1);
|
||||
}
|
||||
#else
|
||||
if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
|
||||
bmesh_rationalize_normals(bm, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
bm->opflag = 0;
|
||||
|
||||
/* compute normals, clear temp flags and flush selections */
|
||||
BM_mesh_normals_update(bm);
|
||||
BM_mesh_select_mode_flush(bm);
|
||||
}
|
||||
|
||||
void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag)
|
||||
{
|
||||
BMIter iter;
|
||||
BMHeader *ele;
|
||||
|
||||
#ifdef DEBUG
|
||||
BM_ELEM_INDEX_VALIDATE(bm, "Should Never Fail!", __func__);
|
||||
#endif
|
||||
|
||||
if (hflag & BM_VERT) {
|
||||
if (bm->elem_index_dirty & BM_VERT) {
|
||||
int index = 0;
|
||||
BM_ITER(ele, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
BM_elem_index_set(ele, index); /* set_ok */
|
||||
index++;
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_VERT;
|
||||
BLI_assert(index == bm->totvert);
|
||||
}
|
||||
else {
|
||||
// printf("%s: skipping vert index calc!\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
if (hflag & BM_EDGE) {
|
||||
if (bm->elem_index_dirty & BM_EDGE) {
|
||||
int index = 0;
|
||||
BM_ITER(ele, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
BM_elem_index_set(ele, index); /* set_ok */
|
||||
index++;
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_EDGE;
|
||||
BLI_assert(index == bm->totedge);
|
||||
}
|
||||
else {
|
||||
// printf("%s: skipping edge index calc!\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
if (hflag & BM_FACE) {
|
||||
if (bm->elem_index_dirty & BM_FACE) {
|
||||
int index = 0;
|
||||
BM_ITER(ele, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
BM_elem_index_set(ele, index); /* set_ok */
|
||||
index++;
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_FACE;
|
||||
BLI_assert(index == bm->totface);
|
||||
}
|
||||
else {
|
||||
// printf("%s: skipping face index calc!\n", __func__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* array checking/setting macros */
|
||||
/* currently vert/edge/loop/face index data is being abused, but we should
|
||||
* eventually be able to rely on it being valid. To this end, there are macros
|
||||
* that validate them (so blender doesnt crash), but also print errors so we can
|
||||
* fix the offending parts of the code, this way after some months we can
|
||||
* confine this code for debug mode.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func,
|
||||
const char *msg_a, const char *msg_b)
|
||||
{
|
||||
const char iter_types[3] = {BM_VERTS_OF_MESH,
|
||||
BM_EDGES_OF_MESH,
|
||||
BM_FACES_OF_MESH};
|
||||
|
||||
const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE};
|
||||
const char *type_names[3] = {"vert", "edge", "face"};
|
||||
|
||||
BMIter iter;
|
||||
BMHeader *ele;
|
||||
int i;
|
||||
int is_any_error = 0;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
const int is_dirty = (flag_types[i] & bm->elem_index_dirty);
|
||||
int index = 0;
|
||||
int is_error = FALSE;
|
||||
int err_val = 0;
|
||||
int err_idx = 0;
|
||||
|
||||
BM_ITER(ele, &iter, bm, iter_types[i], NULL) {
|
||||
if (!is_dirty) {
|
||||
if (BM_elem_index_get(ele) != index) {
|
||||
err_val = BM_elem_index_get(ele);
|
||||
err_idx = index;
|
||||
is_error = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
BM_elem_index_set(ele, index); /* set_ok */
|
||||
index++;
|
||||
}
|
||||
|
||||
if ((is_error == TRUE) && (is_dirty == FALSE)) {
|
||||
is_any_error = TRUE;
|
||||
fprintf(stderr,
|
||||
"Invalid Index: at %s, %s, %s[%d] invalid index %d, '%s', '%s'\n",
|
||||
location, func, type_names[i], err_idx, err_val, msg_a, msg_b);
|
||||
}
|
||||
else if ((is_error == FALSE) && (is_dirty == TRUE)) {
|
||||
|
||||
#if 0 /* mostly annoying */
|
||||
|
||||
/* dirty may have been incorrectly set */
|
||||
fprintf(stderr,
|
||||
"Invalid Dirty: at %s, %s (%s), dirty flag was set but all index values are correct, '%s', '%s'\n",
|
||||
location, func, type_names[i], msg_a, msg_b);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* mostly annoying, even in debug mode */
|
||||
#ifdef DEBUG
|
||||
if (is_any_error == 0) {
|
||||
fprintf(stderr,
|
||||
"Valid Index Success: at %s, %s, '%s', '%s'\n",
|
||||
location, func, msg_a, msg_b);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
(void) is_any_error; /* shut up the compiler */
|
||||
|
||||
}
|
||||
|
||||
BMVert *BM_vert_at_index(BMesh *bm, const int index)
|
||||
{
|
||||
return BLI_mempool_findelem(bm->vpool, index);
|
||||
}
|
||||
|
||||
BMEdge *BM_edge_at_index(BMesh *bm, const int index)
|
||||
{
|
||||
return BLI_mempool_findelem(bm->epool, index);
|
||||
}
|
||||
|
||||
BMFace *BM_face_at_index(BMesh *bm, const int index)
|
||||
{
|
||||
return BLI_mempool_findelem(bm->fpool, index);
|
||||
}
|
||||
769
source/blender/bmesh/intern/bmesh_mods.c
Normal file
769
source/blender/bmesh/intern/bmesh_mods.c
Normal 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);
|
||||
}
|
||||
2024
source/blender/bmesh/intern/bmesh_newcore.c
Normal file
2024
source/blender/bmesh/intern/bmesh_newcore.c
Normal file
File diff suppressed because it is too large
Load Diff
1145
source/blender/bmesh/intern/bmesh_opdefines.c
Normal file
1145
source/blender/bmesh/intern/bmesh_opdefines.c
Normal file
File diff suppressed because it is too large
Load Diff
1376
source/blender/bmesh/intern/bmesh_operators.c
Normal file
1376
source/blender/bmesh/intern/bmesh_operators.c
Normal file
File diff suppressed because it is too large
Load Diff
106
source/blender/bmesh/intern/bmesh_operators_private.h
Normal file
106
source/blender/bmesh/intern/bmesh_operators_private.h
Normal 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__ */
|
||||
1069
source/blender/bmesh/intern/bmesh_polygon.c
Normal file
1069
source/blender/bmesh/intern/bmesh_polygon.c
Normal file
File diff suppressed because it is too large
Load Diff
98
source/blender/bmesh/intern/bmesh_private.h
Normal file
98
source/blender/bmesh/intern/bmesh_private.h
Normal 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__ */
|
||||
658
source/blender/bmesh/intern/bmesh_queries.c
Normal file
658
source/blender/bmesh/intern/bmesh_queries.c
Normal 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;
|
||||
}
|
||||
1103
source/blender/bmesh/intern/bmesh_structure.c
Normal file
1103
source/blender/bmesh/intern/bmesh_structure.c
Normal file
File diff suppressed because it is too large
Load Diff
106
source/blender/bmesh/intern/bmesh_structure.h
Normal file
106
source/blender/bmesh/intern/bmesh_structure.h
Normal 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__ */
|
||||
266
source/blender/bmesh/intern/bmesh_walkers.c
Normal file
266
source/blender/bmesh/intern/bmesh_walkers.c
Normal 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");
|
||||
}
|
||||
911
source/blender/bmesh/intern/bmesh_walkers_impl.c
Normal file
911
source/blender/bmesh/intern/bmesh_walkers_impl.c
Normal 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);
|
||||
89
source/blender/bmesh/intern/bmesh_walkers_private.h
Normal file
89
source/blender/bmesh/intern/bmesh_walkers_private.h
Normal 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__ */
|
||||
479
source/blender/bmesh/intern/in-progress/BME_conversions.c
Normal file
479
source/blender/bmesh/intern/in-progress/BME_conversions.c
Normal 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
|
||||
881
source/blender/bmesh/operators/bmo_bevel.c
Normal file
881
source/blender/bmesh/operators/bmo_bevel.c
Normal 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);
|
||||
}
|
||||
414
source/blender/bmesh/operators/bmo_connect.c
Normal file
414
source/blender/bmesh/operators/bmo_connect.c
Normal 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);
|
||||
}
|
||||
1412
source/blender/bmesh/operators/bmo_create.c
Normal file
1412
source/blender/bmesh/operators/bmo_create.c
Normal file
File diff suppressed because it is too large
Load Diff
559
source/blender/bmesh/operators/bmo_dissolve.c
Normal file
559
source/blender/bmesh/operators/bmo_dissolve.c
Normal 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(®walker, bm, BMW_ISLAND,
|
||||
BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
|
||||
BMW_NIL_LAY);
|
||||
f2 = BMW_begin(®walker, f);
|
||||
for ( ; f2; f2 = BMW_step(®walker)) {
|
||||
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(®walker);
|
||||
|
||||
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(®walker, bm, BMW_ISLAND,
|
||||
BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
|
||||
BMW_NIL_LAY);
|
||||
|
||||
f2 = BMW_begin(®walker, f);
|
||||
for ( ; f2; f2 = BMW_step(®walker)) {
|
||||
BLI_array_append(faces, f2);
|
||||
}
|
||||
BMW_end(®walker);
|
||||
|
||||
for (i = 0; i < BLI_array_count(faces); i++) {
|
||||
f2 = faces[i];
|
||||
BMO_elem_flag_disable(bm, f2, FACE_MARK);
|
||||
BMO_elem_flag_enable(bm, f2, FACE_ORIG);
|
||||
}
|
||||
|
||||
if (BMO_error_occurred(bm)) {
|
||||
BMO_error_clear(bm);
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
BLI_array_append(faces, NULL);
|
||||
BLI_array_append(regions, faces);
|
||||
}
|
||||
|
||||
for (i = 0; i < BLI_array_count(regions); i++) {
|
||||
int tot = 0;
|
||||
|
||||
faces = regions[i];
|
||||
if (!faces[0]) {
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
|
||||
"Could not find boundary of dissolve region");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
while (faces[tot])
|
||||
tot++;
|
||||
|
||||
f = BM_faces_join(bm, faces, tot);
|
||||
if (!f) {
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
|
||||
"Could not create merged face");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* if making the new face failed (e.g. overlapping test)
|
||||
* unmark the original faces for deletion */
|
||||
BMO_elem_flag_disable(bm, f, FACE_ORIG);
|
||||
BMO_elem_flag_enable(bm, f, FACE_NEW);
|
||||
|
||||
}
|
||||
|
||||
BMO_op_callf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES);
|
||||
|
||||
|
||||
if (use_verts) {
|
||||
BMIter viter;
|
||||
BMVert *v;
|
||||
|
||||
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
|
||||
if (BM_vert_edge_count(v) == 2) {
|
||||
BM_vert_collapse_edges(bm, v->e, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BMO_error_occurred(bm)) goto cleanup;
|
||||
|
||||
BMO_slot_from_flag(bm, op, "regionout", FACE_NEW, BM_FACE);
|
||||
|
||||
cleanup:
|
||||
/* free/cleanu */
|
||||
for (i = 0; i < BLI_array_count(regions); i++) {
|
||||
if (regions[i]) MEM_freeN(regions[i]);
|
||||
}
|
||||
|
||||
BLI_array_free(regions);
|
||||
}
|
||||
|
||||
/* almost identical to dissolve edge, except it cleans up vertice */
|
||||
void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
/* BMOperator fop; */
|
||||
BMOIter oiter;
|
||||
BMIter iter;
|
||||
BMVert *v, **verts = NULL;
|
||||
BLI_array_declare(verts);
|
||||
BMEdge *e;
|
||||
/* BMFace *f; */
|
||||
int i;
|
||||
|
||||
BMO_ITER(e, &oiter, bm, op, "edges", BM_EDGE) {
|
||||
if (BM_edge_face_count(e) == 2) {
|
||||
BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
|
||||
BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
|
||||
|
||||
BM_faces_join_pair(bm, e->l->f,
|
||||
e->l->radial_next->f,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if (BMO_elem_flag_test(bm, v, VERT_MARK) && BM_vert_edge_count(v) == 2) {
|
||||
BLI_array_append(verts, v);
|
||||
}
|
||||
}
|
||||
|
||||
/* clean up extreneous 2-valence vertice */
|
||||
for (i = 0; i < BLI_array_count(verts); i++) {
|
||||
if (verts[i]->e) {
|
||||
BM_vert_collapse_edges(bm, verts[i]->e, verts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_array_free(verts);
|
||||
|
||||
//BMO_op_initf(bm, &fop, "dissolvefaces faces=%ff", FACE_MARK);
|
||||
//BMO_op_exec(bm, &fop);
|
||||
|
||||
//BMO_slot_copy(op, &fop, "regionout", "regionout");
|
||||
|
||||
//BMO_op_finish(bm, &fop);
|
||||
}
|
||||
|
||||
|
||||
void dissolveedges_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
/* might want to make this an option or mode - campbell */
|
||||
|
||||
/* BMOperator fop; */
|
||||
BMOIter eiter;
|
||||
BMEdge *e;
|
||||
|
||||
BMIter viter;
|
||||
BMVert *v;
|
||||
|
||||
int use_verts = BMO_slot_int_get(op, "use_verts");
|
||||
|
||||
if (use_verts) {
|
||||
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if (BM_vert_edge_count(v) == 2) {
|
||||
BMO_elem_flag_disable(bm, v, VERT_MARK);
|
||||
}
|
||||
else {
|
||||
BMO_elem_flag_enable(bm, v, VERT_MARK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BMO_ITER(e, &eiter, bm, op, "edges", BM_EDGE) {
|
||||
const int edge_face_count = BM_edge_face_count(e);
|
||||
if (edge_face_count == 2) {
|
||||
|
||||
/* join faces */
|
||||
BM_faces_join_pair(bm, e->l->f,
|
||||
e->l->radial_next->f,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_verts) {
|
||||
BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
|
||||
if (BM_vert_edge_count(v) == 2) {
|
||||
BM_vert_collapse_edges(bm, v->e, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int test_extra_verts(BMesh *bm, BMVert *v)
|
||||
{
|
||||
BMIter iter, liter, iter2, iter3;
|
||||
BMFace *f, *f2;
|
||||
BMLoop *l;
|
||||
BMEdge *e;
|
||||
int found;
|
||||
|
||||
/* test faces around verts for verts that would be wronly killed
|
||||
* by dissolve faces. */
|
||||
f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v);
|
||||
for ( ; f; f = BM_iter_step(&iter)) {
|
||||
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
|
||||
for ( ; l; l = BM_iter_step(&liter)) {
|
||||
if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) {
|
||||
/* if an edge around a vert is a boundary edge,
|
||||
* then dissolve faces won't destroy it.
|
||||
* also if it forms a boundary with one
|
||||
* of the face region */
|
||||
found = FALSE;
|
||||
e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, l->v);
|
||||
for ( ; e; e = BM_iter_step(&iter2)) {
|
||||
if (BM_edge_face_count(e) == 1) {
|
||||
found = TRUE;
|
||||
}
|
||||
f2 = BM_iter_new(&iter3, bm, BM_FACES_OF_EDGE, e);
|
||||
for ( ; f2; f2 = BM_iter_step(&iter3)) {
|
||||
if (!BMO_elem_flag_test(bm, f2, FACE_MARK)) {
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == TRUE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
void dissolveverts_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOpSlot *vinput;
|
||||
BMIter iter, fiter;
|
||||
BMVert *v;
|
||||
BMFace *f;
|
||||
/* int i; */
|
||||
|
||||
vinput = BMO_slot_get(op, "verts");
|
||||
BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_MARK, BM_VERT);
|
||||
|
||||
for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
|
||||
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
|
||||
/* check if it's a two-valence ver */
|
||||
if (BM_vert_edge_count(v) == 2) {
|
||||
|
||||
/* collapse the ver */
|
||||
BM_vert_collapse_faces(bm, v->e, v, 1.0f, TRUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
|
||||
for ( ; f; f = BM_iter_step(&fiter)) {
|
||||
BMO_elem_flag_enable(bm, f, FACE_ORIG);
|
||||
BMO_elem_flag_enable(bm, f, FACE_MARK);
|
||||
}
|
||||
|
||||
/* check if our additions to the input to face dissolve
|
||||
* will destroy nonmarked vertices. */
|
||||
if (!test_extra_verts(bm, v)) {
|
||||
f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
|
||||
for ( ; f; f = BM_iter_step(&fiter)) {
|
||||
if (BMO_elem_flag_test(bm, f, FACE_ORIG)) {
|
||||
BMO_elem_flag_disable(bm, f, FACE_MARK);
|
||||
BMO_elem_flag_disable(bm, f, FACE_ORIG);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
|
||||
for ( ; f; f = BM_iter_step(&fiter)) {
|
||||
BMO_elem_flag_disable(bm, f, FACE_ORIG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_MARK);
|
||||
if (BMO_error_occurred(bm)) {
|
||||
const char *msg;
|
||||
|
||||
BMO_error_get(bm, &msg, NULL);
|
||||
BMO_error_clear(bm);
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, msg);
|
||||
}
|
||||
|
||||
/* clean up any remainin */
|
||||
for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
|
||||
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
|
||||
if (!BM_vert_dissolve(bm, v)) {
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* this code is for cleaning up two-edged faces, it shall become
|
||||
* it's own function one day */
|
||||
#if 0
|
||||
void dummy_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
{
|
||||
/* clean up two-edged face */
|
||||
/* basic idea is to keep joining 2-edged faces until their
|
||||
* gone. this however relies on joining two 2-edged faces
|
||||
* together to work, which doesn't */
|
||||
found3 = 1;
|
||||
while (found3) {
|
||||
found3 = 0;
|
||||
for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
|
||||
if (!BM_face_validate(bm, f, stderr)) {
|
||||
printf("error.\n");
|
||||
}
|
||||
|
||||
if (f->len == 2) {
|
||||
//this design relies on join faces working
|
||||
//with two-edged faces properly.
|
||||
//commenting this line disables the
|
||||
//outermost loop.
|
||||
//found3 = 1;
|
||||
found2 = 0;
|
||||
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
|
||||
fe = l->e;
|
||||
for ( ; l; l = BM_iter_step(&liter)) {
|
||||
f2 = BM_iter_new(&fiter, bm,
|
||||
BM_FACES_OF_EDGE, l->e);
|
||||
for ( ; f2; f2 = BM_iter_step(&fiter)) {
|
||||
if (f2 != f) {
|
||||
BM_faces_join_pair(bm, f, f2, l->e);
|
||||
found2 = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found2) break;
|
||||
}
|
||||
|
||||
if (!found2) {
|
||||
bmesh_kf(bm, f);
|
||||
bmesh_ke(bm, fe);
|
||||
}
|
||||
} /* else if (f->len == 3) {
|
||||
BMEdge *ed[3];
|
||||
BMVert *vt[3];
|
||||
BMLoop *lp[3];
|
||||
int i = 0;
|
||||
|
||||
//check for duplicate edges
|
||||
l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
|
||||
for ( ; l; l = BM_iter_step(&liter)) {
|
||||
ed[i] = l->e;
|
||||
lp[i] = l;
|
||||
vt[i++] = l->v;
|
||||
}
|
||||
if (vt[0] == vt[1] || vt[0] == vt[2]) {
|
||||
i += 1;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
if (oldlen == len) break;
|
||||
oldlen = len;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**/
|
||||
typedef struct DissolveElemWeight_t {
|
||||
BMHeader *ele;
|
||||
float weight;
|
||||
} DissolveElemWeight_t;
|
||||
|
||||
static int dissolve_elem_cmp(const void *a1, const void *a2)
|
||||
{
|
||||
const struct DissolveElemWeight_t *d1 = a1, *d2 = a2;
|
||||
|
||||
if (d1->weight > d2->weight) return 1;
|
||||
else if (d1->weight < d2->weight) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dissolvelimit_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOpSlot *einput = BMO_slot_get(op, "edges");
|
||||
BMOpSlot *vinput = BMO_slot_get(op, "verts");
|
||||
const float angle_max = (float)M_PI / 2.0f;
|
||||
const float angle_limit = minf(angle_max, BMO_slot_float_get(op, "angle_limit"));
|
||||
DissolveElemWeight_t *weight_elems = MEM_mallocN(MAX2(einput->len, vinput->len) *
|
||||
sizeof(DissolveElemWeight_t), __func__);
|
||||
int i, tot_found;
|
||||
|
||||
/* --- first edges --- */
|
||||
|
||||
/* go through and split edge */
|
||||
for (i = 0, tot_found = 0; i < einput->len; i++) {
|
||||
BMEdge *e = ((BMEdge **)einput->data.p)[i];
|
||||
const float angle = BM_edge_face_angle(bm, e);
|
||||
|
||||
if (angle < angle_limit) {
|
||||
weight_elems[i].ele = (BMHeader *)e;
|
||||
weight_elems[i].weight = angle;
|
||||
tot_found++;
|
||||
}
|
||||
else {
|
||||
weight_elems[i].ele = NULL;
|
||||
weight_elems[i].weight = angle_max;
|
||||
}
|
||||
}
|
||||
|
||||
if (tot_found != 0) {
|
||||
qsort(weight_elems, einput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
|
||||
|
||||
for (i = 0; i < tot_found; i++) {
|
||||
BMEdge *e = (BMEdge *)weight_elems[i].ele;
|
||||
/* check twice because cumulative effect could disolve over angle limit */
|
||||
if (BM_edge_face_angle(bm, e) < angle_limit) {
|
||||
BMFace *nf = BM_faces_join_pair(bm, e->l->f,
|
||||
e->l->radial_next->f,
|
||||
e); /* join faces */
|
||||
|
||||
/* there may be some errors, we dont mind, just move on */
|
||||
if (nf == NULL) {
|
||||
BMO_error_clear(bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --- second verts --- */
|
||||
for (i = 0, tot_found = 0; i < vinput->len; i++) {
|
||||
BMVert *v = ((BMVert **)vinput->data.p)[i];
|
||||
const float angle = BM_vert_edge_angle(bm, v);
|
||||
|
||||
if (angle < angle_limit) {
|
||||
weight_elems[i].ele = (BMHeader *)v;
|
||||
weight_elems[i].weight = angle;
|
||||
tot_found++;
|
||||
}
|
||||
else {
|
||||
weight_elems[i].ele = NULL;
|
||||
weight_elems[i].weight = angle_max;
|
||||
}
|
||||
}
|
||||
|
||||
if (tot_found != 0) {
|
||||
qsort(weight_elems, vinput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
|
||||
|
||||
for (i = 0; i < tot_found; i++) {
|
||||
BMVert *v = (BMVert *)weight_elems[i].ele;
|
||||
/* check twice because cumulative effect could disolve over angle limit */
|
||||
if (BM_vert_edge_angle(bm, v) < angle_limit) {
|
||||
BM_vert_collapse_edges(bm, v->e, v); /* join edges */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(weight_elems);
|
||||
}
|
||||
512
source/blender/bmesh/operators/bmo_dupe.c
Normal file
512
source/blender/bmesh/operators/bmo_dupe.c
Normal 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");
|
||||
}
|
||||
}
|
||||
426
source/blender/bmesh/operators/bmo_edgesplit.c
Normal file
426
source/blender/bmesh/operators/bmo_edgesplit.c
Normal 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
|
||||
591
source/blender/bmesh/operators/bmo_extrude.c
Normal file
591
source/blender/bmesh/operators/bmo_extrude.c
Normal 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);
|
||||
}
|
||||
373
source/blender/bmesh/operators/bmo_join_triangles.c
Normal file
373
source/blender/bmesh/operators/bmo_join_triangles.c
Normal 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);
|
||||
}
|
||||
906
source/blender/bmesh/operators/bmo_mesh_conv.c
Normal file
906
source/blender/bmesh/operators/bmo_mesh_conv.c
Normal 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);
|
||||
}
|
||||
126
source/blender/bmesh/operators/bmo_mirror.c
Normal file
126
source/blender/bmesh/operators/bmo_mirror.c
Normal 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);
|
||||
}
|
||||
734
source/blender/bmesh/operators/bmo_primitive.c
Normal file
734
source/blender/bmesh/operators/bmo_primitive.c
Normal 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);
|
||||
}
|
||||
590
source/blender/bmesh/operators/bmo_removedoubles.c
Normal file
590
source/blender/bmesh/operators/bmo_removedoubles.c
Normal 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);
|
||||
}
|
||||
1104
source/blender/bmesh/operators/bmo_subdivide.c
Normal file
1104
source/blender/bmesh/operators/bmo_subdivide.c
Normal file
File diff suppressed because it is too large
Load Diff
66
source/blender/bmesh/operators/bmo_subdivide.h
Normal file
66
source/blender/bmesh/operators/bmo_subdivide.h
Normal 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__ */
|
||||
219
source/blender/bmesh/operators/bmo_triangulate.c
Normal file
219
source/blender/bmesh/operators/bmo_triangulate.c
Normal 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);
|
||||
}
|
||||
1297
source/blender/bmesh/operators/bmo_utils.c
Normal file
1297
source/blender/bmesh/operators/bmo_utils.c
Normal file
File diff suppressed because it is too large
Load Diff
1011
source/blender/bmesh/tools/BME_bevel.c
Normal file
1011
source/blender/bmesh/tools/BME_bevel.c
Normal file
File diff suppressed because it is too large
Load Diff
322
source/blender/bmesh/tools/BME_dupe_ops.c
Normal file
322
source/blender/bmesh/tools/BME_dupe_ops.c
Normal file
@@ -0,0 +1,322 @@
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* BME_DUPLICATE.C
|
||||
*
|
||||
* This file contains functions for duplicating, copying, and splitting
|
||||
* elements from a bmesh.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* COPY VERTEX
|
||||
*
|
||||
* Copy an existing vertex from one bmesh to another.
|
||||
*
|
||||
*/
|
||||
|
||||
static BMVert *copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash)
|
||||
{
|
||||
BMVert *target_vertex = NULL;
|
||||
|
||||
/*create a new vertex*/
|
||||
target_vertex = BM_vert_create(target, source_vertex->co, NULL);
|
||||
|
||||
/*insert new vertex into the vert hash*/
|
||||
BLI_ghash_insert(vhash, source_vertex, target_vertex);
|
||||
|
||||
/*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
|
||||
CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data);
|
||||
|
||||
/*Copy Markings*/
|
||||
if(BM_Is_Selected((BMHeader*)source_vertex)) BM_vert_select_set(target_mesh, target_vertex, TRUE);
|
||||
if(BM_Is_Hidden((BMHeader*)source_vertex)) BM_Mark_Hidden((BMHeader*)target_vertex, 1);
|
||||
|
||||
BMO_elem_flag_enable(target_mesh, (BMHeader*)target_vertex, DUPE_NEW);
|
||||
|
||||
return target_vertex;
|
||||
}
|
||||
|
||||
/*
|
||||
* COPY EDGE
|
||||
*
|
||||
* Copy an existing edge from one bmesh to another.
|
||||
*
|
||||
*/
|
||||
|
||||
static BMEdge *copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash)
|
||||
{
|
||||
BMEdge *target_edge = NULL;
|
||||
BMVert *target_vert1, *target_vert2;
|
||||
|
||||
/*lookup v1 and v2*/
|
||||
target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1);
|
||||
target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2);
|
||||
|
||||
/*create a new edge*/
|
||||
target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
|
||||
|
||||
/*insert new edge into the edge hash*/
|
||||
BLI_ghash_insert(ehash, source_edge, target_edge);
|
||||
|
||||
/*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
|
||||
CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data);
|
||||
|
||||
/*copy flags*/
|
||||
if(BM_Is_Selected((BMHeader*) source_edge)) BM_edge_select_set(target_mesh, target_edge, TRUE);
|
||||
if(BM_Is_Hidden((BMHeader*) source_edge)) BM_Mark_Hidden(target_mesh, target_edge, 1);
|
||||
if(BM_Is_Sharp((BMHeader*) source_edge)) BM_Mark_Sharp(target_edge, 1);
|
||||
if(BM_Is_Seam((BMHeader*) source_edge)) BM_Mark_Seam(target_edge, 1);
|
||||
if(BM_Is_Fgon((BMHeader*) source_edge)) BM_Mark_Fgon(target_edge, 1);
|
||||
|
||||
BMO_elem_flag_enable(target_mesh, (BMHeader*)target_edge, DUPE_NEW);
|
||||
|
||||
return target_edge;
|
||||
}
|
||||
|
||||
/*
|
||||
* COPY FACE
|
||||
*
|
||||
* Copy an existing face from one bmesh to another.
|
||||
*
|
||||
*/
|
||||
|
||||
static BMFace *copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash)
|
||||
{
|
||||
BMEdge *target_edge;
|
||||
BMVert *target_vert1, *target_vert2;
|
||||
BMLoop *source_loop, *target_loop;
|
||||
BMFace *target_face = NULL;
|
||||
int i;
|
||||
|
||||
/*lookup the first and second verts*/
|
||||
target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v);
|
||||
target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
|
||||
|
||||
/*lookup edges*/
|
||||
i = 0;
|
||||
source_loop = source_face->lbase;
|
||||
do{
|
||||
edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
|
||||
i++;
|
||||
source_loop = source_loop->next;
|
||||
}while(source_loop != source_face->lbase);
|
||||
|
||||
/*create new face*/
|
||||
target_face = BM_face_create_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0);
|
||||
|
||||
/*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/
|
||||
CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data);
|
||||
|
||||
/*copy flags*/
|
||||
if(BM_Is_Selected((BMHeader*)source_face)) BM_Select_face(target, target_face, TRUE);
|
||||
if(BM_Is_Hidden((BMHeader*)source_face)) BM_Mark_Hidden((BMHeader*)target_face, 1);
|
||||
|
||||
/*mark the face for output*/
|
||||
BMO_elem_flag_enable(target_mesh, (BMHeader*)target_face, DUPE_NEW);
|
||||
|
||||
/*copy per-loop custom data*/
|
||||
source_loop = source_face->lbase;
|
||||
target_loop = target_face->lbase;
|
||||
do{
|
||||
CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data);
|
||||
source_loop = source_loop->next;
|
||||
target_loop = target_loop->next;
|
||||
}while(source_loop != source_face->lbase);
|
||||
|
||||
return target_face;
|
||||
}
|
||||
|
||||
/*
|
||||
* COPY MESH
|
||||
*
|
||||
* Internal Copy function.
|
||||
*/
|
||||
|
||||
/*local flag defines*/
|
||||
|
||||
#define DUPE_INPUT 1 /*input from operator*/
|
||||
#define DUPE_NEW 2
|
||||
#define DUPE_DONE 3
|
||||
|
||||
static void copy_mesh(BMMesh *source, BMMesh *target)
|
||||
{
|
||||
|
||||
BMVert *v = NULL;
|
||||
BMEdge *e = NULL, **edar = NULL;
|
||||
BMLoop *l = NULL;
|
||||
BMFace *f = NULL;
|
||||
|
||||
BMIter verts;
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
BMIter loops;
|
||||
|
||||
GHash *vhash;
|
||||
GHash *ehash;
|
||||
|
||||
int maxlength = 0, flag;
|
||||
|
||||
/*initialize pointer hashes*/
|
||||
vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
|
||||
ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp);
|
||||
|
||||
/*initialize edge pointer array*/
|
||||
for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f = BM_iter_step(&faces)){
|
||||
if(f->len > maxlength) maxlength = f->len;
|
||||
}
|
||||
edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array");
|
||||
|
||||
|
||||
/*first we dupe all flagged faces and their elements from source*/
|
||||
for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f= BM_iter_step(&faces)){
|
||||
if(BMO_elem_flag_test(source, (BMHeader*)f, DUPE_INPUT)){
|
||||
/*vertex pass*/
|
||||
for(v = BM_iter_new(&verts, source, BM_VERT_OF_FACE, f, 0, NULL); v; v = BM_iter_step(&verts)){
|
||||
if(!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE)){
|
||||
copy_vertex(source,v, target, vhash);
|
||||
BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/*edge pass*/
|
||||
for(e = BM_iter_new(&edges, source, BM_EDGE_OF_FACE, f, 0, NULL); e; e = BMeshIter_step(&edges)){
|
||||
if(!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE)){
|
||||
copy_edge(source, e, target, vhash, ehash);
|
||||
BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
|
||||
}
|
||||
}
|
||||
copy_face(source, f, target, edar, vhash, ehash);
|
||||
BMO_elem_flag_enable(source, (BMHeader*)f, DUPE_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/*now we dupe all the edges*/
|
||||
for(e = BM_iter_new(&edges, source, BM_EDGES, source, 0, NULL); e; e = BM_iter_step(&edges)){
|
||||
if(BMO_elem_flag_test(source, (BMHeader*)e, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE))){
|
||||
/*make sure that verts are copied*/
|
||||
if(!BMO_elem_flag_test(source, (BMHeader*)e->v1, DUPE_DONE){
|
||||
copy_vertex(source, e->v1, target, vhash);
|
||||
BMO_elem_flag_enable(source, (BMHeader*)e->v1, DUPE_DONE);
|
||||
}
|
||||
if(!BMO_elem_flag_test(source, (BMHeader*)e->v2, DUPE_DONE){
|
||||
copy_vertex(source, e->v2, target, vhash);
|
||||
BMO_elem_flag_enable(source, (BMHeader*)e->v2, DUPE_DONE);
|
||||
}
|
||||
/*now copy the actual edge*/
|
||||
copy_edge(source, e, target, vhash, ehash);
|
||||
BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/*finally dupe all loose vertices*/
|
||||
for(v = BM_iter_new(&verts, source, BM_VERTS, source, 0, NULL); v; v = BM_iter_step(&verts)){
|
||||
if(BMO_elem_flag_test(source, (BMHeader*)v, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE))){
|
||||
copy_vertex(source, v, target, vhash);
|
||||
BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/*free pointer hashes*/
|
||||
BLI_ghash_free(vhash, NULL, NULL);
|
||||
BLI_ghash_free(ehash, NULL, NULL);
|
||||
|
||||
/*free edge pointer array*/
|
||||
if(edar)
|
||||
MEM_freeN(edar);
|
||||
}
|
||||
/*
|
||||
BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4])
|
||||
{
|
||||
BMMesh *target = NULL;
|
||||
target = bmesh_make_mesh(allocsize);
|
||||
|
||||
|
||||
CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
|
||||
|
||||
CustomData_bmesh_init_pool(&target->vdata, allocsize[0]);
|
||||
CustomData_bmesh_init_pool(&target->edata, allocsize[1]);
|
||||
CustomData_bmesh_init_pool(&target->ldata, allocsize[2]);
|
||||
CustomData_bmesh_init_pool(&target->pdata, allocsize[3]);
|
||||
|
||||
bmesh_begin_edit(bm);
|
||||
bmesh_begin_edit(target);
|
||||
|
||||
bmesh_copy_mesh(bm, target, 0);
|
||||
|
||||
bmesh_end_edit(bm);
|
||||
bmesh_end_edit(target);
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
void dupeop_exec(BMMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOperator *dupeop = op;
|
||||
BMOpSlot *vinput, *einput, *finput, *vnew, *enew, *fnew;
|
||||
int i;
|
||||
|
||||
vinput = BMO_Get_Slot(dupeop, BMOP_DUPE_VINPUT);
|
||||
einput = BMO_Get_Slot(dupeop, BMOP_DUPE_EINPUT);
|
||||
finput = BMO_Get_Slot(dupeop, BMOP_DUPE_FINPUT);
|
||||
|
||||
/*go through vinput, einput, and finput and flag elements with private flags*/
|
||||
BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_VINPUT, DUPE_INPUT);
|
||||
BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_EINPUT, DUPE_INPUT);
|
||||
BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_FINPUT, DUPE_INPUT);
|
||||
|
||||
/*use the internal copy function*/
|
||||
copy_mesh(bm, bm);
|
||||
|
||||
/*Output*/
|
||||
/*First copy the input buffers to output buffers - original data*/
|
||||
BMO_Copy_Opslot_Buffer_Alloc(dupeop, vinput, BMO_Get_Slot(dupeop, BMOP_DUPE_VORIGINAL));
|
||||
BMO_Copy_Opslot_Buffer_Alloc(dupeop, einput, BMO_Get_Slot(dupeop, BMOP_DUPE_EORIGINAL));
|
||||
BMO_Copy_Opslot_Buffer_Alloc(dupeop, finput, BMO_Get_Slot(dupeop, BMOP_DUPE_FORIGINAL));
|
||||
|
||||
/*Now alloc the new output buffers*/
|
||||
BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_VNEW, DUPE_NEW, BMESH_VERT);
|
||||
BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_ENEW, DUPE_NEW, BMESH_EDGE);
|
||||
BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_FNEW, DUPE_NEW, BMESH_FACE);
|
||||
}
|
||||
|
||||
void splitop_exec(BMMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOperator *splitop = op;
|
||||
BMOperator dupeop;
|
||||
BMOperator delop;
|
||||
|
||||
/*initialize our sub-operators*/
|
||||
BMO_op_init(&dupeop, BMOP_DUPE);
|
||||
BMO_op_init(&delop, BMOP_DEL);
|
||||
|
||||
BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_VINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_VINPUT));
|
||||
BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_EINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_EINPUT));
|
||||
BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_FINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_FINPUT));
|
||||
|
||||
BMO_op_exec(&dupeop);
|
||||
|
||||
/*connect outputs of dupe to delete*/
|
||||
BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_VORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_VINPUT));
|
||||
BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_EORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_EINPUT));
|
||||
BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_FORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_FINPUT));
|
||||
|
||||
BMO_op_exec(&delop);
|
||||
|
||||
/*now we make our outputs by copying the dupe outputs*/
|
||||
BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_VNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_VOUTPUT));
|
||||
BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_ENEW), BMO_Get_Slot(splitop, BMOP_SPLIT_EOUTPUT));
|
||||
BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_FNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_FOUTPUT));
|
||||
|
||||
/*cleanup*/
|
||||
BMO_op_finish(&dupeop);
|
||||
BMO_op_finish(&delop);
|
||||
}
|
||||
|
||||
#endif
|
||||
310
source/blender/bmesh/tools/BME_duplicate.c
Normal file
310
source/blender/bmesh/tools/BME_duplicate.c
Normal file
@@ -0,0 +1,310 @@
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* BME_DUPLICATE.C
|
||||
*
|
||||
* This file contains functions for duplicating, copying, and splitting
|
||||
* elements from a bmesh.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* BMESH COPY VERTEX
|
||||
*
|
||||
* Copy an existing vertex from one bmesh to another.
|
||||
*
|
||||
*/
|
||||
|
||||
static BMVert *bmesh_copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash)
|
||||
{
|
||||
BMVert *target_vertex = NULL;
|
||||
|
||||
/*create a new vertex*/
|
||||
target_vertex = bmesh_make_vert(target, source_vertex->co, NULL);
|
||||
|
||||
/*insert new vertex into the vert hash*/
|
||||
BLI_ghash_insert(vhash, source_vertex, target_vertex);
|
||||
|
||||
/*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
|
||||
CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data);
|
||||
|
||||
/*copy flags*/
|
||||
if(bmesh_test_flag(source_vertex, BMESH_SELECT)) bmesh_set_flag(target_vertex, BMESH_SELECT);
|
||||
if(bmesh_test_flag(source_vertex, BMESH_HIDDEN)) bmesh_set_flag(target_vertex, BMESH_HIDDEN);
|
||||
|
||||
return target_vertex;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH COPY EDGE
|
||||
*
|
||||
* Copy an existing edge from one bmesh to another.
|
||||
*
|
||||
*/
|
||||
|
||||
static BMEdge *bmesh_copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash)
|
||||
{
|
||||
BMEdge *target_edge = NULL;
|
||||
BMVert *target_vert1, *target_vert2;
|
||||
|
||||
/*lookup v1 and v2*/
|
||||
target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1);
|
||||
target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2);
|
||||
|
||||
/*create a new edge*/
|
||||
target_edge = bmesh_make_edge(target_mesh, target_vert1, target_vert2, NULL, 0);
|
||||
|
||||
/*insert new edge into the edge hash*/
|
||||
BLI_ghash_insert(ehash, source_edge, target_edge);
|
||||
|
||||
/*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
|
||||
CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data);
|
||||
|
||||
/*copy flags*/
|
||||
if(bmesh_test_flag(source_edge, BMESH_SELECT)) bmesh_set_flag(target_edge, BMESH_SELECT);
|
||||
if(bmesh_test_flag(source_edge, BMESH_HIDDEN)) bmesh_set_flag(target_edge, BMESH_SELECT);
|
||||
if(bmesh_test_flag(source_edge, BMESH_SHARP)) bmesh_set_flag(target_edge, BMESH_SHARP);
|
||||
if(bmesh_test_flag(source_edge, BMESH_SEAM)) bmesh_set_flag(target_edge, BMESH_SEAM);
|
||||
if(bmesh_test_flag(source_edge, BMESH_FGON)) bmesh_set_flag(target_edge, BMESH_FGON);
|
||||
|
||||
return target_edge;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH COPY FACE
|
||||
*
|
||||
* Copy an existing face from one bmesh to another.
|
||||
*
|
||||
*/
|
||||
|
||||
static BMFace *bmesh_copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash)
|
||||
{
|
||||
BMEdge *target_edge;
|
||||
BMVert *target_vert1, *target_vert2;
|
||||
BMLoop *source_loop, *target_loop;
|
||||
BMFace *target_face = NULL;
|
||||
int i;
|
||||
|
||||
|
||||
/*lookup the first and second verts*/
|
||||
target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v);
|
||||
target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
|
||||
|
||||
/*lookup edges*/
|
||||
i = 0;
|
||||
source_loop = source_face->lbase;
|
||||
do{
|
||||
edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
|
||||
i++;
|
||||
source_loop = source_loop->next;
|
||||
}while(source_loop != source_face->lbase);
|
||||
|
||||
/*create new face*/
|
||||
target_face = bmesh_make_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0);
|
||||
|
||||
/*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/
|
||||
CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data);
|
||||
|
||||
/*copy flags*/
|
||||
if(bmesh_test_flag(source_face, BMESH_SELECT)) bmesh_set_flag(target_face, BMESH_SELECT);
|
||||
if(bmesh_test_flag(source_face, BMESH_HIDDEN)) bmesh_set_flag(target_face, BMESH_HIDDEN);
|
||||
|
||||
/*mark the face as dirty for normal and tesselation calcs*/
|
||||
bmesh_set_flag(target_face, BMESH_DIRTY);
|
||||
|
||||
/*copy per-loop custom data*/
|
||||
source_loop = source_face->lbase;
|
||||
target_loop = target_face->lbase;
|
||||
do{
|
||||
CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data);
|
||||
source_loop = source_loop->next;
|
||||
target_loop = target_loop->next;
|
||||
}while(source_loop != source_face->lbase);
|
||||
|
||||
return target_face;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH COPY MESH
|
||||
*
|
||||
* Internal Copy function. copies flagged elements from
|
||||
* source to target, which may in fact be the same mesh.
|
||||
* Note that if __flag is 0, all elements will be copied.
|
||||
*
|
||||
*/
|
||||
|
||||
static void bmesh_copy_mesh(BMMesh *source, BMMesh *target, int __flag)
|
||||
{
|
||||
|
||||
BMVert *v;
|
||||
BMEdge *e, **edar;
|
||||
BMLoop *l;
|
||||
BMFace *f;
|
||||
|
||||
BMIter verts;
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
BMIter loops;
|
||||
|
||||
GHash *vhash;
|
||||
GHash *ehash;
|
||||
|
||||
int maxlength = 0, flag;
|
||||
|
||||
/*initialize pointer hashes*/
|
||||
vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
|
||||
ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp);
|
||||
|
||||
/*initialize edge pointer array*/
|
||||
for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)){
|
||||
if(f->len > maxlength) maxlength = f->len;
|
||||
}
|
||||
edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array");
|
||||
|
||||
/*begin modelling loop for target*/
|
||||
bmesh_begin_edit(target);
|
||||
|
||||
/*we make special exception for __flag == 0... we copy all*/
|
||||
if(!__flag){
|
||||
flag = BMESH_DUPE;
|
||||
for(v = BMeshIter_init(verts, BM_VERTS, source, 0); v; v = BMeshIter_step(verts)) bmesh_set_flag(v, BMESH_DUPE);
|
||||
for(e = BMeshIter_init(verts, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)) bmesh_set_flag(e, BMESH_DUPE);
|
||||
for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)) bmesh_set_flag(f, BMESH_DUPE);
|
||||
} else{
|
||||
flag = __flag;
|
||||
}
|
||||
|
||||
/*first we dupe all flagged faces and their elements from source*/
|
||||
for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f= BMeshIter_step(faces)){
|
||||
if(bmesh_test_flag(f, flag)){
|
||||
/*vertex pass*/
|
||||
for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){
|
||||
if(!bmesh_test_flag(l->v, BMESH_DUPED)){
|
||||
bmesh_copy_vertex(source,l->v, target, vhash);
|
||||
bmesh_set_flag(l->v, BMESH_DUPED);
|
||||
}
|
||||
}
|
||||
|
||||
/*edge pass*/
|
||||
for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){
|
||||
if(!bmesh_test_flag(l->e, BMESH_DUPED)){
|
||||
bmesh_copy_edge(source, l->e, target, vhash, ehash);
|
||||
bmesh_set_flag(l->e, BMESH_DUPED);
|
||||
}
|
||||
}
|
||||
bmesh_copy_face(source, f, target, edar, vhash, ehash);
|
||||
bmesh_set_flag(f, BMESH_DUPED);
|
||||
}
|
||||
}
|
||||
|
||||
/*now we dupe all the edges*/
|
||||
for(e = BMeshIter_init(edges, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)){
|
||||
if(bmesh_test_flag(e, flag) && (!bmesh_test_flag(e, BMESH_DUPED))){
|
||||
/*make sure that verts are copied*/
|
||||
if(!bmesh_test_flag(e->v1, BMESH_DUPED)){
|
||||
bmesh_copy_vertex(source, e->v1, target, vhash);
|
||||
bmesh_set_flag(e->v1, BMESH_DUPED);
|
||||
}
|
||||
if(!bmesh_test_flag(e->v2, BMESH_DUPED)){
|
||||
bmesh_copy_vertex(source, e->v2, target, vhash);
|
||||
bmesh_set_flag(e->v2, BMESH_DUPED);
|
||||
}
|
||||
/*now copy the actual edge*/
|
||||
bmesh_copy_edge(source, e, target, vhash, ehash);
|
||||
bmesh_set_flag(e, BMESH_DUPED);
|
||||
}
|
||||
}
|
||||
|
||||
/*finally dupe all loose vertices*/
|
||||
for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){
|
||||
if(bmesh_test_flag(v, flag) && (!bmesh_test_flag(v, BMESH_DUPED))){
|
||||
bmesh_copy_vertex(source, v, target, vhash);
|
||||
bmesh_set_flag(v, BMESH_DUPED);
|
||||
}
|
||||
}
|
||||
|
||||
/*finish*/
|
||||
bmesh_end_edit(target, BMESH_CALC_NORM | BMESH_CALC_TESS);
|
||||
|
||||
/*free pointer hashes*/
|
||||
BLI_ghash_free(vhash, NULL, NULL);
|
||||
BLI_ghash_free(ehash, NULL, NULL);
|
||||
|
||||
/*free edge pointer array*/
|
||||
MEM_freeN(edar);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH MAKE MESH FROM MESH
|
||||
*
|
||||
* Creates a new mesh by duplicating an existing one.
|
||||
*
|
||||
*/
|
||||
|
||||
BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4])
|
||||
{
|
||||
BMMesh *target = NULL;
|
||||
target = bmesh_make_mesh(allocsize);
|
||||
|
||||
/*copy custom data layout*/
|
||||
CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
|
||||
/*initialize memory pools*/
|
||||
CustomData_bmesh_init_pool(&target->vdata, allocsize[0]);
|
||||
CustomData_bmesh_init_pool(&target->edata, allocsize[1]);
|
||||
CustomData_bmesh_init_pool(&target->ldata, allocsize[2]);
|
||||
CustomData_bmesh_init_pool(&target->pdata, allocsize[3]);
|
||||
|
||||
bmesh_begin_edit(bm);
|
||||
bmesh_begin_edit(target);
|
||||
|
||||
bmesh_copy_mesh(bm, target, 0); /* copy all elements */
|
||||
|
||||
bmesh_end_edit(bm);
|
||||
bmesh_end_edit(target);
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH SPLIT MESH
|
||||
*
|
||||
* Copies flagged elements then deletes them.
|
||||
*
|
||||
*/
|
||||
|
||||
void bmesh_split_mesh(BMMesh *bm, int flag)
|
||||
{
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
|
||||
BMIter verts;
|
||||
BMIter edges;
|
||||
BMIter faces;
|
||||
|
||||
bmesh_begin_edit(bm);
|
||||
bmesh_copy_mesh(bm, bm, flag);
|
||||
|
||||
/*mark verts for deletion*/
|
||||
for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){
|
||||
if(bmesh_test_flag(v, flag)) bmesh_delete_vert(bm, v);
|
||||
}
|
||||
/*mark edges for deletion*/
|
||||
for(e = BMeshIter_init(edges, BM_EDGES, bm, 0); e; e = BMeshIter_step(edges)){
|
||||
if(bmesh_test_flag(e, flag)) bmesh_delete_edge(bm, e);
|
||||
|
||||
}
|
||||
/*mark faces for deletion*/
|
||||
for(f = BMeshIter_init(faces, BM_FACES, bm, 0); f; f= BMeshIter_step(faces)){
|
||||
if(bmesh_tes t_flag(f, flag)) bmesh_delete_face(bm, f);
|
||||
|
||||
}
|
||||
bmesh_end_edit(bm);
|
||||
}
|
||||
|
||||
#endif
|
||||
341
source/blender/bmesh/tools/BME_weld.c
Normal file
341
source/blender/bmesh/tools/BME_weld.c
Normal 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
|
||||
Reference in New Issue
Block a user