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
@@ -0,0 +1,6384 @@
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2004 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Johnny Matthews, Geoffrey Bantle.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise in mods.c
|
||||
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BMF_Api.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_editVert.h"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_heap.h"
|
||||
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_utildefines.h"
|
||||
#include "BKE_bmesh.h"
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
#include "BKE_verse.h"
|
||||
#endif
|
||||
|
||||
#include "BIF_cursors.h"
|
||||
#include "BIF_editmesh.h"
|
||||
#include "BIF_gl.h"
|
||||
#include "BIF_glutil.h"
|
||||
#include "BIF_graphics.h"
|
||||
#include "BIF_interface.h"
|
||||
#include "BIF_mywindow.h"
|
||||
#include "BIF_screen.h"
|
||||
#include "BIF_space.h"
|
||||
#include "BIF_resources.h"
|
||||
#include "BIF_toolbox.h"
|
||||
#include "BIF_transform.h"
|
||||
#include "transform.h"
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
#include "BIF_verse.h"
|
||||
#endif
|
||||
|
||||
#include "BDR_drawobject.h"
|
||||
#include "BDR_editobject.h"
|
||||
|
||||
#include "BSE_view.h"
|
||||
#include "BSE_edit.h"
|
||||
|
||||
#include "blendef.h"
|
||||
#include "multires.h"
|
||||
#include "mydevice.h"
|
||||
|
||||
#include "editmesh.h"
|
||||
|
||||
#include "MTC_vectorops.h"
|
||||
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "BLO_sys_types.h" // for intptr_t support
|
||||
|
||||
/* local prototypes ---------------*/
|
||||
void bevel_menu(void);
|
||||
static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa);
|
||||
|
||||
/********* qsort routines *********/
|
||||
|
||||
|
||||
typedef struct xvertsort {
|
||||
float x;
|
||||
EditVert *v1;
|
||||
} xvertsort;
|
||||
|
||||
static int vergxco(const void *v1, const void *v2)
|
||||
{
|
||||
const xvertsort *x1=v1, *x2=v2;
|
||||
|
||||
if( x1->x > x2->x ) return 1;
|
||||
else if( x1->x < x2->x) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct facesort {
|
||||
uintptr_t x;
|
||||
struct EditFace *efa;
|
||||
};
|
||||
|
||||
|
||||
static int vergface(const void *v1, const void *v2)
|
||||
{
|
||||
const struct facesort *x1=v1, *x2=v2;
|
||||
|
||||
if( x1->x > x2->x ) return 1;
|
||||
else if( x1->x < x2->x) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* *********************************** */
|
||||
|
||||
void convert_to_triface(int direction)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa, *efan, *next;
|
||||
float fac;
|
||||
|
||||
if(multires_test()) return;
|
||||
|
||||
efa= em->faces.last;
|
||||
while(efa) {
|
||||
next= efa->prev;
|
||||
if(efa->v4) {
|
||||
if(efa->f & SELECT) {
|
||||
/* choose shortest diagonal for split */
|
||||
fac= len_v3v3(efa->v1->co, efa->v3->co) - len_v3v3(efa->v2->co, efa->v4->co);
|
||||
/* this makes sure exact squares get split different in both cases */
|
||||
if( (direction==0 && fac<FLT_EPSILON) || (direction && fac>0.0f) ) {
|
||||
efan= EM_face_from_faces(efa, NULL, 0, 1, 2, -1);
|
||||
if(efa->f & SELECT) EM_select_face(efan, 1);
|
||||
efan= EM_face_from_faces(efa, NULL, 0, 2, 3, -1);
|
||||
if(efa->f & SELECT) EM_select_face(efan, 1);
|
||||
}
|
||||
else {
|
||||
efan= EM_face_from_faces(efa, NULL, 0, 1, 3, -1);
|
||||
if(efa->f & SELECT) EM_select_face(efan, 1);
|
||||
efan= EM_face_from_faces(efa, NULL, 1, 2, 3, -1);
|
||||
if(efa->f & SELECT) EM_select_face(efan, 1);
|
||||
}
|
||||
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
}
|
||||
}
|
||||
efa= next;
|
||||
}
|
||||
|
||||
EM_fgon_flags(); // redo flags and indices for fgons
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
BIF_undo_push("Convert Quads to Triangles");
|
||||
|
||||
}
|
||||
|
||||
int removedoublesflag(short flag, short automerge, float limit) /* return amount */
|
||||
{
|
||||
/*
|
||||
flag - Test with vert->flags
|
||||
automerge - Alternative operation, merge unselected into selected.
|
||||
Used for "Auto Weld" mode. warning.
|
||||
limit - Quick manhattan distance between verts.
|
||||
*/
|
||||
|
||||
EditMesh *em = G.editMesh;
|
||||
/* all verts with (flag & 'flag') are being evaluated */
|
||||
EditVert *eve, *v1, *nextve;
|
||||
EditEdge *eed, *e1, *nexted;
|
||||
EditFace *efa, *nextvl;
|
||||
xvertsort *sortblock, *sb, *sb1;
|
||||
struct facesort *vlsortblock, *vsb, *vsb1;
|
||||
int a, b, test, amount;
|
||||
|
||||
if(multires_test()) return 0;
|
||||
|
||||
|
||||
/* flag 128 is cleared, count */
|
||||
|
||||
/* Normal non weld operation */
|
||||
eve= em->verts.first;
|
||||
amount= 0;
|
||||
while(eve) {
|
||||
eve->f &= ~128;
|
||||
if(eve->h==0 && (automerge || (eve->f & flag))) amount++;
|
||||
eve= eve->next;
|
||||
}
|
||||
if(amount==0) return 0;
|
||||
|
||||
/* allocate memory and qsort */
|
||||
sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub");
|
||||
eve= em->verts.first;
|
||||
while(eve) {
|
||||
if(eve->h==0 && (automerge || (eve->f & flag))) {
|
||||
sb->x= eve->co[0]+eve->co[1]+eve->co[2];
|
||||
sb->v1= eve;
|
||||
sb++;
|
||||
}
|
||||
eve= eve->next;
|
||||
}
|
||||
qsort(sortblock, amount, sizeof(xvertsort), vergxco);
|
||||
|
||||
|
||||
/* test for doubles */
|
||||
sb= sortblock;
|
||||
if (automerge) {
|
||||
for(a=0; a<amount; a++, sb++) {
|
||||
eve= sb->v1;
|
||||
if( (eve->f & 128)==0 ) {
|
||||
sb1= sb+1;
|
||||
for(b=a+1; b<amount && (eve->f & 128)==0; b++, sb1++) {
|
||||
if(sb1->x - sb->x > limit) break;
|
||||
|
||||
/* when automarge, only allow unselected->selected */
|
||||
v1= sb1->v1;
|
||||
if( (v1->f & 128)==0 ) {
|
||||
if ((eve->f & flag)==0 && (v1->f & flag)==1) {
|
||||
if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
|
||||
(float)fabs(v1->co[1]-eve->co[1])<=limit &&
|
||||
(float)fabs(v1->co[2]-eve->co[2])<=limit)
|
||||
{ /* unique bit */
|
||||
eve->f|= 128;
|
||||
eve->tmp.v = v1;
|
||||
}
|
||||
} else if( (eve->f & flag)==1 && (v1->f & flag)==0 ) {
|
||||
if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
|
||||
(float)fabs(v1->co[1]-eve->co[1])<=limit &&
|
||||
(float)fabs(v1->co[2]-eve->co[2])<=limit)
|
||||
{ /* unique bit */
|
||||
v1->f|= 128;
|
||||
v1->tmp.v = eve;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(a=0; a<amount; a++, sb++) {
|
||||
eve= sb->v1;
|
||||
if( (eve->f & 128)==0 ) {
|
||||
sb1= sb+1;
|
||||
for(b=a+1; b<amount; b++, sb1++) {
|
||||
/* first test: simpel dist */
|
||||
if(sb1->x - sb->x > limit) break;
|
||||
v1= sb1->v1;
|
||||
|
||||
/* second test: is vertex allowed */
|
||||
if( (v1->f & 128)==0 ) {
|
||||
if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
|
||||
(float)fabs(v1->co[1]-eve->co[1])<=limit &&
|
||||
(float)fabs(v1->co[2]-eve->co[2])<=limit)
|
||||
{
|
||||
v1->f|= 128;
|
||||
v1->tmp.v = eve;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_freeN(sortblock);
|
||||
|
||||
if (!automerge)
|
||||
for(eve = em->verts.first; eve; eve=eve->next)
|
||||
if((eve->f & flag) && (eve->f & 128))
|
||||
EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f);
|
||||
|
||||
/* test edges and insert again */
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
eed->f2= 0;
|
||||
eed= eed->next;
|
||||
}
|
||||
eed= em->edges.last;
|
||||
while(eed) {
|
||||
nexted= eed->prev;
|
||||
|
||||
if(eed->f2==0) {
|
||||
if( (eed->v1->f & 128) || (eed->v2->f & 128) ) {
|
||||
remedge(eed);
|
||||
|
||||
if(eed->v1->f & 128) eed->v1 = eed->v1->tmp.v;
|
||||
if(eed->v2->f & 128) eed->v2 = eed->v2->tmp.v;
|
||||
e1= addedgelist(eed->v1, eed->v2, eed);
|
||||
|
||||
if(e1) {
|
||||
e1->f2= 1;
|
||||
if(eed->f & SELECT)
|
||||
e1->f |= SELECT;
|
||||
}
|
||||
if(e1!=eed) free_editedge(eed);
|
||||
}
|
||||
}
|
||||
eed= nexted;
|
||||
}
|
||||
|
||||
/* first count amount of test faces */
|
||||
efa= (struct EditFace *)em->faces.first;
|
||||
amount= 0;
|
||||
while(efa) {
|
||||
efa->f1= 0;
|
||||
if(efa->v1->f & 128) efa->f1= 1;
|
||||
else if(efa->v2->f & 128) efa->f1= 1;
|
||||
else if(efa->v3->f & 128) efa->f1= 1;
|
||||
else if(efa->v4 && (efa->v4->f & 128)) efa->f1= 1;
|
||||
|
||||
if(efa->f1==1) amount++;
|
||||
efa= efa->next;
|
||||
}
|
||||
|
||||
/* test faces for double vertices, and if needed remove them */
|
||||
efa= (struct EditFace *)em->faces.first;
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
if(efa->f1==1) {
|
||||
|
||||
if(efa->v1->f & 128) efa->v1= efa->v1->tmp.v;
|
||||
if(efa->v2->f & 128) efa->v2= efa->v2->tmp.v;
|
||||
if(efa->v3->f & 128) efa->v3= efa->v3->tmp.v;
|
||||
if(efa->v4 && (efa->v4->f & 128)) efa->v4= efa->v4->tmp.v;
|
||||
|
||||
test= 0;
|
||||
if(efa->v1==efa->v2) test+=1;
|
||||
if(efa->v2==efa->v3) test+=2;
|
||||
if(efa->v3==efa->v1) test+=4;
|
||||
if(efa->v4==efa->v1) test+=8;
|
||||
if(efa->v3==efa->v4) test+=16;
|
||||
if(efa->v2==efa->v4) test+=32;
|
||||
|
||||
if(test) {
|
||||
if(efa->v4) {
|
||||
if(test==1 || test==2) {
|
||||
efa->v2= efa->v3;
|
||||
efa->v3= efa->v4;
|
||||
efa->v4= 0;
|
||||
|
||||
EM_data_interp_from_faces(efa, NULL, efa, 0, 2, 3, 3);
|
||||
|
||||
test= 0;
|
||||
}
|
||||
else if(test==8 || test==16) {
|
||||
efa->v4= 0;
|
||||
test= 0;
|
||||
}
|
||||
else {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
amount--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
amount--;
|
||||
}
|
||||
}
|
||||
|
||||
if(test==0) {
|
||||
/* set edge pointers */
|
||||
efa->e1= findedgelist(efa->v1, efa->v2);
|
||||
efa->e2= findedgelist(efa->v2, efa->v3);
|
||||
if(efa->v4==0) {
|
||||
efa->e3= findedgelist(efa->v3, efa->v1);
|
||||
efa->e4= 0;
|
||||
}
|
||||
else {
|
||||
efa->e3= findedgelist(efa->v3, efa->v4);
|
||||
efa->e4= findedgelist(efa->v4, efa->v1);
|
||||
}
|
||||
}
|
||||
}
|
||||
efa= nextvl;
|
||||
}
|
||||
|
||||
/* double faces: sort block */
|
||||
/* count again, now all selected faces */
|
||||
amount= 0;
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
efa->f1= 0;
|
||||
if(faceselectedOR(efa, 1)) {
|
||||
efa->f1= 1;
|
||||
amount++;
|
||||
}
|
||||
efa= efa->next;
|
||||
}
|
||||
|
||||
if(amount) {
|
||||
/* double faces: sort block */
|
||||
vsb= vlsortblock= MEM_mallocN(sizeof(struct facesort)*amount, "sortremovedoub");
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
if(efa->f1 & 1) {
|
||||
if(efa->v4) vsb->x= (uintptr_t) MIN4( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3, (uintptr_t)efa->v4);
|
||||
else vsb->x= (uintptr_t) MIN3( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3);
|
||||
|
||||
vsb->efa= efa;
|
||||
vsb++;
|
||||
}
|
||||
efa= efa->next;
|
||||
}
|
||||
|
||||
qsort(vlsortblock, amount, sizeof(struct facesort), vergface);
|
||||
|
||||
vsb= vlsortblock;
|
||||
for(a=0; a<amount; a++) {
|
||||
efa= vsb->efa;
|
||||
if( (efa->f1 & 128)==0 ) {
|
||||
vsb1= vsb+1;
|
||||
|
||||
for(b=a+1; b<amount; b++) {
|
||||
|
||||
/* first test: same pointer? */
|
||||
if(vsb->x != vsb1->x) break;
|
||||
|
||||
/* second test: is test permitted? */
|
||||
efa= vsb1->efa;
|
||||
if( (efa->f1 & 128)==0 ) {
|
||||
if( compareface(efa, vsb->efa)) efa->f1 |= 128;
|
||||
|
||||
}
|
||||
vsb1++;
|
||||
}
|
||||
}
|
||||
vsb++;
|
||||
}
|
||||
|
||||
MEM_freeN(vlsortblock);
|
||||
|
||||
/* remove double faces */
|
||||
efa= (struct EditFace *)em->faces.first;
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
if(efa->f1 & 128) {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
}
|
||||
efa= nextvl;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove double vertices */
|
||||
a= 0;
|
||||
eve= (struct EditVert *)em->verts.first;
|
||||
while(eve) {
|
||||
nextve= eve->next;
|
||||
if(automerge || eve->f & flag) {
|
||||
if(eve->f & 128) {
|
||||
a++;
|
||||
BLI_remlink(&em->verts, eve);
|
||||
free_editvert(eve);
|
||||
}
|
||||
}
|
||||
eve= nextve;
|
||||
}
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
if((a>0) && (G.editMesh->vnode)) {
|
||||
sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode);
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
}
|
||||
#endif
|
||||
|
||||
return a; /* amount */
|
||||
}
|
||||
|
||||
/* called from buttons */
|
||||
static void xsortvert_flag__doSetX(void *userData, EditVert *eve, int x, int y, int index)
|
||||
{
|
||||
xvertsort *sortblock = userData;
|
||||
|
||||
sortblock[index].x = x;
|
||||
}
|
||||
void xsortvert_flag(int flag)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
/* all verts with (flag & 'flag') are sorted */
|
||||
EditVert *eve;
|
||||
xvertsort *sortblock;
|
||||
ListBase tbase;
|
||||
int i, amount = BLI_countlist(&em->verts);
|
||||
|
||||
if(multires_test()) return;
|
||||
|
||||
sortblock = MEM_callocN(sizeof(xvertsort)*amount,"xsort");
|
||||
for (i=0,eve=em->verts.first; eve; i++,eve=eve->next)
|
||||
if(eve->f & flag)
|
||||
sortblock[i].v1 = eve;
|
||||
mesh_foreachScreenVert(xsortvert_flag__doSetX, sortblock, V3D_CLIP_TEST_OFF);
|
||||
qsort(sortblock, amount, sizeof(xvertsort), vergxco);
|
||||
|
||||
/* make temporal listbase */
|
||||
tbase.first= tbase.last= 0;
|
||||
for (i=0; i<amount; i++) {
|
||||
eve = sortblock[i].v1;
|
||||
|
||||
if (eve) {
|
||||
BLI_remlink(&em->verts, eve);
|
||||
BLI_addtail(&tbase, eve);
|
||||
}
|
||||
}
|
||||
|
||||
addlisttolist(&em->verts, &tbase);
|
||||
|
||||
MEM_freeN(sortblock);
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
|
||||
BIF_undo_push("Xsort");
|
||||
|
||||
}
|
||||
|
||||
/* called from buttons */
|
||||
void hashvert_flag(int flag)
|
||||
{
|
||||
/* switch vertex order using hash table */
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *eve;
|
||||
struct xvertsort *sortblock, *sb, onth, *newsort;
|
||||
ListBase tbase;
|
||||
int amount, a, b;
|
||||
|
||||
if(multires_test()) return;
|
||||
|
||||
/* count */
|
||||
eve= em->verts.first;
|
||||
amount= 0;
|
||||
while(eve) {
|
||||
if(eve->f & flag) amount++;
|
||||
eve= eve->next;
|
||||
}
|
||||
if(amount==0) return;
|
||||
|
||||
/* allocate memory */
|
||||
sb= sortblock= (struct xvertsort *)MEM_mallocN(sizeof(struct xvertsort)*amount,"sortremovedoub");
|
||||
eve= em->verts.first;
|
||||
while(eve) {
|
||||
if(eve->f & flag) {
|
||||
sb->v1= eve;
|
||||
sb++;
|
||||
}
|
||||
eve= eve->next;
|
||||
}
|
||||
|
||||
BLI_srand(1);
|
||||
|
||||
sb= sortblock;
|
||||
for(a=0; a<amount; a++, sb++) {
|
||||
b= (int)(amount*BLI_drand());
|
||||
if(b>=0 && b<amount) {
|
||||
newsort= sortblock+b;
|
||||
onth= *sb;
|
||||
*sb= *newsort;
|
||||
*newsort= onth;
|
||||
}
|
||||
}
|
||||
|
||||
/* make temporal listbase */
|
||||
tbase.first= tbase.last= 0;
|
||||
sb= sortblock;
|
||||
while(amount--) {
|
||||
eve= sb->v1;
|
||||
BLI_remlink(&em->verts, eve);
|
||||
BLI_addtail(&tbase, eve);
|
||||
sb++;
|
||||
}
|
||||
|
||||
addlisttolist(&em->verts, &tbase);
|
||||
|
||||
MEM_freeN(sortblock);
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
BIF_undo_push("Hash");
|
||||
|
||||
}
|
||||
|
||||
/* generic extern called extruder */
|
||||
void extrude_mesh(void)
|
||||
{
|
||||
float nor[3]= {0.0, 0.0, 0.0};
|
||||
short nr, transmode= 0;
|
||||
|
||||
TEST_EDITMESH
|
||||
if(multires_test()) return;
|
||||
|
||||
if(G.scene->selectmode & SCE_SELECT_VERTEX) {
|
||||
if(G.totvertsel==0) nr= 0;
|
||||
else if(G.totvertsel==1) nr= 4;
|
||||
else if(G.totedgesel==0) nr= 4;
|
||||
else if(G.totfacesel==0)
|
||||
nr= pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4");
|
||||
else if(G.totfacesel==1)
|
||||
nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4");
|
||||
else
|
||||
nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4");
|
||||
}
|
||||
else if(G.scene->selectmode & SCE_SELECT_EDGE) {
|
||||
if (G.totedgesel==0) nr = 0;
|
||||
else if (G.totedgesel==1) nr = 3;
|
||||
else if(G.totfacesel==0) nr = 3;
|
||||
else if(G.totfacesel==1)
|
||||
nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3");
|
||||
else
|
||||
nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3");
|
||||
}
|
||||
else {
|
||||
if (G.totfacesel == 0) nr = 0;
|
||||
else if (G.totfacesel == 1) nr = 1;
|
||||
else
|
||||
nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2");
|
||||
}
|
||||
|
||||
if(nr<1) return;
|
||||
|
||||
if(nr==1) transmode= extrudeflag(SELECT, nor);
|
||||
else if(nr==4) transmode= extrudeflag_verts_indiv(SELECT, nor);
|
||||
else if(nr==3) transmode= extrudeflag_edges_indiv(SELECT, nor);
|
||||
else transmode= extrudeflag_face_indiv(SELECT, nor);
|
||||
|
||||
if(transmode==0) {
|
||||
error("No valid selection");
|
||||
}
|
||||
else {
|
||||
EM_fgon_flags();
|
||||
countall();
|
||||
|
||||
/* We need to force immediate calculation here because
|
||||
* transform may use derived objects (which are now stale).
|
||||
*
|
||||
* This shouldn't be necessary, derived queries should be
|
||||
* automatically building this data if invalid. Or something.
|
||||
*/
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
object_handle_update(G.obedit);
|
||||
|
||||
/* individual faces? */
|
||||
BIF_TransformSetUndo("Extrude");
|
||||
if(nr==2) {
|
||||
initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR);
|
||||
Transform();
|
||||
}
|
||||
else {
|
||||
initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR);
|
||||
if(transmode=='n') {
|
||||
mul_m4_v3(G.obedit->obmat, nor);
|
||||
sub_v3_v3v3(nor, nor, G.obedit->obmat[3]);
|
||||
BIF_setSingleAxisConstraint(nor, "along normal");
|
||||
}
|
||||
Transform();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void split_mesh(void)
|
||||
{
|
||||
|
||||
TEST_EDITMESH
|
||||
if(multires_test()) return;
|
||||
|
||||
if(okee(" Split ")==0) return;
|
||||
|
||||
waitcursor(1);
|
||||
|
||||
/* make duplicate first */
|
||||
adduplicateflag(SELECT);
|
||||
/* old faces have flag 128 set, delete them */
|
||||
delfaceflag(128);
|
||||
recalc_editnormals();
|
||||
|
||||
waitcursor(0);
|
||||
|
||||
countall();
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
|
||||
BIF_undo_push("Split");
|
||||
|
||||
}
|
||||
|
||||
void extrude_repeat_mesh(int steps, float offs)
|
||||
{
|
||||
float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0};
|
||||
short a;
|
||||
|
||||
TEST_EDITMESH
|
||||
if(multires_test()) return;
|
||||
|
||||
/* dvec */
|
||||
dvec[0]= G.vd->persinv[2][0];
|
||||
dvec[1]= G.vd->persinv[2][1];
|
||||
dvec[2]= G.vd->persinv[2][2];
|
||||
normalize_v3(dvec);
|
||||
dvec[0]*= offs;
|
||||
dvec[1]*= offs;
|
||||
dvec[2]*= offs;
|
||||
|
||||
/* base correction */
|
||||
copy_m3_m4(bmat, G.obedit->obmat);
|
||||
invert_m3_m3(tmat, bmat);
|
||||
mul_m3_v3(tmat, dvec);
|
||||
|
||||
for(a=0; a<steps; a++) {
|
||||
extrudeflag(SELECT, nor);
|
||||
translateflag(SELECT, dvec);
|
||||
}
|
||||
|
||||
recalc_editnormals();
|
||||
|
||||
EM_fgon_flags();
|
||||
countall();
|
||||
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
|
||||
BIF_undo_push("Extrude Repeat");
|
||||
}
|
||||
|
||||
void spin_mesh(int steps, float degr, float *dvec, int mode)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *eve,*nextve;
|
||||
float nor[3]= {0.0, 0.0, 0.0};
|
||||
float *curs, si,n[3],q[4],cmat[3][3],imat[3][3], tmat[3][3];
|
||||
float cent[3],bmat[3][3];
|
||||
float phi;
|
||||
short a,ok;
|
||||
|
||||
TEST_EDITMESH
|
||||
if(multires_test()) return;
|
||||
|
||||
/* imat and center and size */
|
||||
copy_m3_m4(bmat, G.obedit->obmat);
|
||||
invert_m3_m3(imat,bmat);
|
||||
|
||||
curs= give_cursor();
|
||||
copy_v3_v3(cent, curs);
|
||||
cent[0]-= G.obedit->obmat[3][0];
|
||||
cent[1]-= G.obedit->obmat[3][1];
|
||||
cent[2]-= G.obedit->obmat[3][2];
|
||||
mul_m3_v3(imat, cent);
|
||||
|
||||
phi= degr*M_PI/360.0;
|
||||
phi/= steps;
|
||||
if(G.scene->toolsettings->editbutflag & B_CLOCKWISE) phi= -phi;
|
||||
|
||||
if(dvec) {
|
||||
n[0]= G.vd->viewinv[1][0];
|
||||
n[1]= G.vd->viewinv[1][1];
|
||||
n[2]= G.vd->viewinv[1][2];
|
||||
} else {
|
||||
n[0]= G.vd->viewinv[2][0];
|
||||
n[1]= G.vd->viewinv[2][1];
|
||||
n[2]= G.vd->viewinv[2][2];
|
||||
}
|
||||
normalize_v3(n);
|
||||
|
||||
q[0]= (float)cos(phi);
|
||||
si= (float)sin(phi);
|
||||
q[1]= n[0]*si;
|
||||
q[2]= n[1]*si;
|
||||
q[3]= n[2]*si;
|
||||
quat_to_mat3( cmat,q);
|
||||
|
||||
mul_m3_m3m3(tmat,cmat,bmat);
|
||||
mul_m3_m3m3(bmat,imat,tmat);
|
||||
|
||||
if(mode==0) if(G.scene->toolsettings->editbutflag & B_KEEPORIG) adduplicateflag(1);
|
||||
ok= 1;
|
||||
|
||||
for(a=0;a<steps;a++) {
|
||||
if(mode==0) ok= extrudeflag(SELECT, nor);
|
||||
else adduplicateflag(SELECT);
|
||||
if(ok==0) {
|
||||
error("No valid vertices are selected");
|
||||
break;
|
||||
}
|
||||
rotateflag(SELECT, cent, bmat);
|
||||
if(dvec) {
|
||||
mul_m3_v3(bmat,dvec);
|
||||
translateflag(SELECT, dvec);
|
||||
}
|
||||
}
|
||||
|
||||
if(ok==0) {
|
||||
/* no vertices or only loose ones selected, remove duplicates */
|
||||
eve= em->verts.first;
|
||||
while(eve) {
|
||||
nextve= eve->next;
|
||||
if(eve->f & SELECT) {
|
||||
BLI_remlink(&em->verts,eve);
|
||||
free_editvert(eve);
|
||||
}
|
||||
eve= nextve;
|
||||
}
|
||||
}
|
||||
recalc_editnormals();
|
||||
|
||||
EM_fgon_flags();
|
||||
countall();
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
|
||||
|
||||
if(dvec==NULL) BIF_undo_push("Spin");
|
||||
}
|
||||
|
||||
void screw_mesh(int steps, int turns)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *eve,*v1=0,*v2=0;
|
||||
EditEdge *eed;
|
||||
float dvec[3], nor[3];
|
||||
|
||||
TEST_EDITMESH
|
||||
if(multires_test()) return;
|
||||
|
||||
/* clear flags */
|
||||
eve= em->verts.first;
|
||||
while(eve) {
|
||||
eve->f1= 0;
|
||||
eve= eve->next;
|
||||
}
|
||||
/* edges set flags in verts */
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
if(eed->v1->f & SELECT) {
|
||||
if(eed->v2->f & SELECT) {
|
||||
/* watch: f1 is a byte */
|
||||
if(eed->v1->f1<2) eed->v1->f1++;
|
||||
if(eed->v2->f1<2) eed->v2->f1++;
|
||||
}
|
||||
}
|
||||
eed= eed->next;
|
||||
}
|
||||
/* find two vertices with eve->f1==1, more or less is wrong */
|
||||
eve= em->verts.first;
|
||||
while(eve) {
|
||||
if(eve->f1==1) {
|
||||
if(v1==0) v1= eve;
|
||||
else if(v2==0) v2= eve;
|
||||
else {
|
||||
v1=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
eve= eve->next;
|
||||
}
|
||||
if(v1==0 || v2==0) {
|
||||
error("You have to select a string of connected vertices too");
|
||||
return;
|
||||
}
|
||||
|
||||
/* calculate dvec */
|
||||
dvec[0]= ( (v1->co[0]- v2->co[0]) )/(steps);
|
||||
dvec[1]= ( (v1->co[1]- v2->co[1]) )/(steps);
|
||||
dvec[2]= ( (v1->co[2]- v2->co[2]) )/(steps);
|
||||
|
||||
copy_v3_v3(nor, G.obedit->obmat[2]);
|
||||
|
||||
if(nor[0]*dvec[0]+nor[1]*dvec[1]+nor[2]*dvec[2]>0.000) {
|
||||
dvec[0]= -dvec[0];
|
||||
dvec[1]= -dvec[1];
|
||||
dvec[2]= -dvec[2];
|
||||
}
|
||||
|
||||
spin_mesh(turns*steps, turns*360, dvec, 0);
|
||||
|
||||
BIF_undo_push("Spin");
|
||||
}
|
||||
|
||||
|
||||
static void erase_edges(ListBase *l)
|
||||
{
|
||||
EditEdge *ed, *nexted;
|
||||
|
||||
ed = (EditEdge *) l->first;
|
||||
while(ed) {
|
||||
nexted= ed->next;
|
||||
if( (ed->v1->f & SELECT) || (ed->v2->f & SELECT) ) {
|
||||
remedge(ed);
|
||||
free_editedge(ed);
|
||||
}
|
||||
ed= nexted;
|
||||
}
|
||||
}
|
||||
|
||||
static void erase_faces(ListBase *l)
|
||||
{
|
||||
EditFace *f, *nextf;
|
||||
|
||||
f = (EditFace *) l->first;
|
||||
|
||||
while(f) {
|
||||
nextf= f->next;
|
||||
if( faceselectedOR(f, SELECT) ) {
|
||||
BLI_remlink(l, f);
|
||||
free_editface(f);
|
||||
}
|
||||
f = nextf;
|
||||
}
|
||||
}
|
||||
|
||||
static void erase_vertices(ListBase *l)
|
||||
{
|
||||
EditVert *v, *nextv;
|
||||
|
||||
v = (EditVert *) l->first;
|
||||
while(v) {
|
||||
nextv= v->next;
|
||||
if(v->f & 1) {
|
||||
BLI_remlink(l, v);
|
||||
free_editvert(v);
|
||||
}
|
||||
v = nextv;
|
||||
}
|
||||
}
|
||||
|
||||
void delete_mesh(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa, *nextvl;
|
||||
EditVert *eve,*nextve;
|
||||
EditEdge *eed,*nexted;
|
||||
short event;
|
||||
int count;
|
||||
char *str="Erase";
|
||||
|
||||
TEST_EDITMESH
|
||||
if(multires_test()) return;
|
||||
|
||||
event= pupmenu("Erase %t|Vertices%x10|Edges%x1|Faces%x2|All%x3|Edges & Faces%x4|Only Faces%x5|Edge Loop%x6");
|
||||
if(event<1) return;
|
||||
|
||||
if(event==10 ) {
|
||||
str= "Erase Vertices";
|
||||
erase_edges(&em->edges);
|
||||
erase_faces(&em->faces);
|
||||
erase_vertices(&em->verts);
|
||||
}
|
||||
else if(event==6) {
|
||||
if(!EdgeLoopDelete())
|
||||
return;
|
||||
|
||||
str= "Erase Edge Loop";
|
||||
}
|
||||
else if(event==4) {
|
||||
str= "Erase Edges & Faces";
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
/* delete only faces with 1 or more edges selected */
|
||||
count= 0;
|
||||
if(efa->e1->f & SELECT) count++;
|
||||
if(efa->e2->f & SELECT) count++;
|
||||
if(efa->e3->f & SELECT) count++;
|
||||
if(efa->e4 && (efa->e4->f & SELECT)) count++;
|
||||
if(count) {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
}
|
||||
efa= nextvl;
|
||||
}
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
nexted= eed->next;
|
||||
if(eed->f & SELECT) {
|
||||
remedge(eed);
|
||||
free_editedge(eed);
|
||||
}
|
||||
eed= nexted;
|
||||
}
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
event=0;
|
||||
if( efa->v1->f & SELECT) event++;
|
||||
if( efa->v2->f & SELECT) event++;
|
||||
if( efa->v3->f & SELECT) event++;
|
||||
if(efa->v4 && (efa->v4->f & SELECT)) event++;
|
||||
|
||||
if(event>1) {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
}
|
||||
efa= nextvl;
|
||||
}
|
||||
}
|
||||
else if(event==1) {
|
||||
str= "Erase Edges";
|
||||
// faces first
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
event=0;
|
||||
if( efa->e1->f & SELECT) event++;
|
||||
if( efa->e2->f & SELECT) event++;
|
||||
if( efa->e3->f & SELECT) event++;
|
||||
if(efa->e4 && (efa->e4->f & SELECT)) event++;
|
||||
|
||||
if(event) {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
}
|
||||
efa= nextvl;
|
||||
}
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
nexted= eed->next;
|
||||
if(eed->f & SELECT) {
|
||||
remedge(eed);
|
||||
free_editedge(eed);
|
||||
}
|
||||
eed= nexted;
|
||||
}
|
||||
/* to remove loose vertices: */
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
if( eed->v1->f & SELECT) eed->v1->f-=SELECT;
|
||||
if( eed->v2->f & SELECT) eed->v2->f-=SELECT;
|
||||
eed= eed->next;
|
||||
}
|
||||
eve= em->verts.first;
|
||||
while(eve) {
|
||||
nextve= eve->next;
|
||||
if(eve->f & SELECT) {
|
||||
BLI_remlink(&em->verts,eve);
|
||||
free_editvert(eve);
|
||||
}
|
||||
eve= nextve;
|
||||
}
|
||||
|
||||
}
|
||||
else if(event==2) {
|
||||
str="Erase Faces";
|
||||
delfaceflag(SELECT);
|
||||
}
|
||||
else if(event==3) {
|
||||
str= "Erase All";
|
||||
if(em->verts.first) free_vertlist(&em->verts);
|
||||
if(em->edges.first) free_edgelist(&em->edges);
|
||||
if(em->faces.first) free_facelist(&em->faces);
|
||||
if(em->selected.first) BLI_freelistN(&(em->selected));
|
||||
}
|
||||
else if(event==5) {
|
||||
str= "Erase Only Faces";
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
if(efa->f & SELECT) {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
}
|
||||
efa= nextvl;
|
||||
}
|
||||
}
|
||||
|
||||
EM_fgon_flags(); // redo flags and indices for fgons
|
||||
|
||||
countall();
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
BIF_undo_push(str);
|
||||
}
|
||||
|
||||
|
||||
/* Got this from scanfill.c. You will need to juggle around the
|
||||
* callbacks for the scanfill.c code a bit for this to work. */
|
||||
void fill_mesh(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *eve,*v1;
|
||||
EditEdge *eed,*e1,*nexted;
|
||||
EditFace *efa,*nextvl, *efan;
|
||||
short ok;
|
||||
|
||||
if(G.obedit==0 || (G.obedit->type!=OB_MESH)) return;
|
||||
if(multires_test()) return;
|
||||
|
||||
waitcursor(1);
|
||||
|
||||
/* copy all selected vertices */
|
||||
eve= em->verts.first;
|
||||
while(eve) {
|
||||
if(eve->f & SELECT) {
|
||||
v1= BLI_addfillvert(eve->co);
|
||||
eve->tmp.v= v1;
|
||||
v1->tmp.v= eve;
|
||||
v1->xs= 0; // used for counting edges
|
||||
}
|
||||
eve= eve->next;
|
||||
}
|
||||
/* copy all selected edges */
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
if( (eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) {
|
||||
e1= BLI_addfilledge(eed->v1->tmp.v, eed->v2->tmp.v);
|
||||
e1->v1->xs++;
|
||||
e1->v2->xs++;
|
||||
}
|
||||
eed= eed->next;
|
||||
}
|
||||
/* from all selected faces: remove vertices and edges to prevent doubles */
|
||||
/* all edges add values, faces subtract,
|
||||
then remove edges with vertices ->xs<2 */
|
||||
efa= em->faces.first;
|
||||
ok= 0;
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
if( faceselectedAND(efa, 1) ) {
|
||||
efa->v1->tmp.v->xs--;
|
||||
efa->v2->tmp.v->xs--;
|
||||
efa->v3->tmp.v->xs--;
|
||||
if(efa->v4) efa->v4->tmp.v->xs--;
|
||||
ok= 1;
|
||||
|
||||
}
|
||||
efa= nextvl;
|
||||
}
|
||||
if(ok) { /* there are faces selected */
|
||||
eed= filledgebase.first;
|
||||
while(eed) {
|
||||
nexted= eed->next;
|
||||
if(eed->v1->xs<2 || eed->v2->xs<2) {
|
||||
BLI_remlink(&filledgebase,eed);
|
||||
}
|
||||
eed= nexted;
|
||||
}
|
||||
}
|
||||
|
||||
if(BLI_edgefill(0, (G.obedit && G.obedit->actcol)?(G.obedit->actcol-1):0)) {
|
||||
efa= fillfacebase.first;
|
||||
while(efa) {
|
||||
/* normals default pointing up */
|
||||
efan= addfacelist(efa->v3->tmp.v, efa->v2->tmp.v,
|
||||
efa->v1->tmp.v, 0, NULL, NULL);
|
||||
if(efan) EM_select_face(efan, 1);
|
||||
efa= efa->next;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_end_edgefill();
|
||||
|
||||
waitcursor(0);
|
||||
EM_select_flush();
|
||||
countall();
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
|
||||
BIF_undo_push("Fill");
|
||||
}
|
||||
/*GB*/
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/*--------------------------- Edge Based Subdivide ------------------------------*/
|
||||
|
||||
#define EDGENEW 2
|
||||
#define FACENEW 2
|
||||
#define EDGEINNER 4
|
||||
#define EDGEOLD 8
|
||||
|
||||
/*used by faceloop cut to select only edges valid for edge slide*/
|
||||
#define DOUBLEOPFILL 16
|
||||
|
||||
/* calculates offset for co, based on fractal, sphere or smooth settings */
|
||||
static void alter_co(float *co, EditEdge *edge, float rad, int beauty, float perc)
|
||||
{
|
||||
float vec1[3], fac;
|
||||
|
||||
if(beauty & B_SMOOTH) {
|
||||
/* we calculate an offset vector vec1[], to be added to *co */
|
||||
float len, fac, nor[3], nor1[3], nor2[3];
|
||||
|
||||
sub_v3_v3v3(nor, edge->v1->co, edge->v2->co);
|
||||
len= 0.5f*normalize_v3(nor);
|
||||
|
||||
copy_v3_v3(nor1, edge->v1->no);
|
||||
copy_v3_v3(nor2, edge->v2->no);
|
||||
|
||||
/* cosine angle */
|
||||
fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ;
|
||||
|
||||
vec1[0]= fac*nor1[0];
|
||||
vec1[1]= fac*nor1[1];
|
||||
vec1[2]= fac*nor1[2];
|
||||
|
||||
/* cosine angle */
|
||||
fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ;
|
||||
|
||||
vec1[0]+= fac*nor2[0];
|
||||
vec1[1]+= fac*nor2[1];
|
||||
vec1[2]+= fac*nor2[2];
|
||||
|
||||
vec1[0]*= rad*len;
|
||||
vec1[1]*= rad*len;
|
||||
vec1[2]*= rad*len;
|
||||
|
||||
co[0] += vec1[0];
|
||||
co[1] += vec1[1];
|
||||
co[2] += vec1[2];
|
||||
}
|
||||
else {
|
||||
if(rad > 0.0) { /* subdivide sphere */
|
||||
normalize_v3(co);
|
||||
co[0]*= rad;
|
||||
co[1]*= rad;
|
||||
co[2]*= rad;
|
||||
}
|
||||
else if(rad< 0.0) { /* fractal subdivide */
|
||||
fac= rad* len_v3v3(edge->v1->co, edge->v2->co);
|
||||
vec1[0]= fac*(float)(0.5-BLI_drand());
|
||||
vec1[1]= fac*(float)(0.5-BLI_drand());
|
||||
vec1[2]= fac*(float)(0.5-BLI_drand());
|
||||
add_v3_v3v3(co, co, vec1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* assumes in the edge is the correct interpolated vertices already */
|
||||
/* percent defines the interpolation, rad and beauty are for special options */
|
||||
/* results in new vertex with correct coordinate, vertex normal and weight group info */
|
||||
static EditVert *subdivide_edge_addvert(EditEdge *edge, float rad, int beauty, float percent)
|
||||
{
|
||||
EditVert *ev;
|
||||
float co[3];
|
||||
|
||||
co[0] = (edge->v2->co[0]-edge->v1->co[0])*percent + edge->v1->co[0];
|
||||
co[1] = (edge->v2->co[1]-edge->v1->co[1])*percent + edge->v1->co[1];
|
||||
co[2] = (edge->v2->co[2]-edge->v1->co[2])*percent + edge->v1->co[2];
|
||||
|
||||
/* offset for smooth or sphere or fractal */
|
||||
alter_co(co, edge, rad, beauty, percent);
|
||||
|
||||
/* clip if needed by mirror modifier */
|
||||
if (edge->v1->f2) {
|
||||
if ( edge->v1->f2 & edge->v2->f2 & 1) {
|
||||
co[0]= 0.0f;
|
||||
}
|
||||
if ( edge->v1->f2 & edge->v2->f2 & 2) {
|
||||
co[1]= 0.0f;
|
||||
}
|
||||
if ( edge->v1->f2 & edge->v2->f2 & 4) {
|
||||
co[2]= 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
ev = addvertlist(co, NULL);
|
||||
|
||||
/* vert data (vgroups, ..) */
|
||||
EM_data_interp_from_verts(edge->v1, edge->v2, ev, percent);
|
||||
|
||||
/* normal */
|
||||
ev->no[0] = (edge->v2->no[0]-edge->v1->no[0])*percent + edge->v1->no[0];
|
||||
ev->no[1] = (edge->v2->no[1]-edge->v1->no[1])*percent + edge->v1->no[1];
|
||||
ev->no[2] = (edge->v2->no[2]-edge->v1->no[2])*percent + edge->v1->no[2];
|
||||
normalize_v3(ev->no);
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
static void flipvertarray(EditVert** arr, short size)
|
||||
{
|
||||
EditVert *hold;
|
||||
int i;
|
||||
|
||||
for(i=0; i<size/2; i++) {
|
||||
hold = arr[i];
|
||||
arr[i] = arr[size-i-1];
|
||||
arr[size-i-1] = hold;
|
||||
}
|
||||
}
|
||||
|
||||
static void facecopy(EditFace *source, EditFace *target)
|
||||
{
|
||||
EditMesh *em= G.editMesh;
|
||||
float *v1 = source->v1->co, *v2 = source->v2->co, *v3 = source->v3->co;
|
||||
float *v4 = source->v4? source->v4->co: NULL;
|
||||
float w[4][4];
|
||||
|
||||
CustomData_em_copy_data(&em->fdata, &em->fdata, source->data, &target->data);
|
||||
|
||||
target->mat_nr = source->mat_nr;
|
||||
target->flag = source->flag;
|
||||
target->h = source->h;
|
||||
|
||||
interp_weights_face_v3( w[0],v1, v2, v3, v4, target->v1->co);
|
||||
interp_weights_face_v3( w[1],v1, v2, v3, v4, target->v2->co);
|
||||
interp_weights_face_v3( w[2],v1, v2, v3, v4, target->v3->co);
|
||||
if (target->v4)
|
||||
interp_weights_face_v3( w[3],v1, v2, v3, v4, target->v4->co);
|
||||
|
||||
CustomData_em_interp(&em->fdata, &source->data, NULL, (float*)w, 1, target->data);
|
||||
}
|
||||
|
||||
static void fill_quad_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype)
|
||||
{
|
||||
EditEdge *cedge=NULL;
|
||||
EditVert *v[4], **verts;
|
||||
EditFace *hold;
|
||||
short start=0, end, left, right, vertsize,i;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
v[3] = efa->v4;
|
||||
|
||||
if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
|
||||
else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
|
||||
else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
|
||||
else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;}
|
||||
|
||||
// Point verts to the array of new verts for cedge
|
||||
verts = BLI_ghash_lookup(gh, cedge);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);}
|
||||
end = (start+1)%4;
|
||||
left = (start+2)%4;
|
||||
right = (start+3)%4;
|
||||
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
3 2 1 0
|
||||
|---*---*---|
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
-------------
|
||||
left right
|
||||
|
||||
where start,end,left, right are indexes of EditFace->v1, etc (stored in v)
|
||||
and 0,1,2... are the indexes of the new verts stored in verts
|
||||
|
||||
We will fill this case like this or this depending on even or odd cuts
|
||||
|
||||
|---*---*---| |---*---|
|
||||
| / \ | | / \ |
|
||||
| / \ | | / \ |
|
||||
|/ \| |/ \|
|
||||
------------- ---------
|
||||
*/
|
||||
|
||||
// Make center face
|
||||
if(vertsize % 2 == 0) {
|
||||
hold = addfacelist(verts[(vertsize-1)/2],verts[((vertsize-1)/2)+1],v[left],v[right], NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
hold->e4->f2 |= EDGEINNER;
|
||||
}else{
|
||||
hold = addfacelist(verts[(vertsize-1)/2],v[left],v[right],NULL, NULL,NULL);
|
||||
hold->e1->f2 |= EDGEINNER;
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
}
|
||||
facecopy(efa,hold);
|
||||
|
||||
// Make side faces
|
||||
for(i=0;i<(vertsize-1)/2;i++) {
|
||||
hold = addfacelist(verts[i],verts[i+1],v[right],NULL,NULL,NULL);
|
||||
facecopy(efa,hold);
|
||||
if(i+1 != (vertsize-1)/2) {
|
||||
if(seltype == SUBDIV_SELECT_INNER) {
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
}
|
||||
}
|
||||
hold = addfacelist(verts[vertsize-2-i],verts[vertsize-1-i],v[left],NULL,NULL,NULL);
|
||||
facecopy(efa,hold);
|
||||
if(i+1 != (vertsize-1)/2) {
|
||||
if(seltype == SUBDIV_SELECT_INNER) {
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_tri_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype)
|
||||
{
|
||||
EditEdge *cedge=NULL;
|
||||
EditVert *v[3], **verts;
|
||||
EditFace *hold;
|
||||
short start=0, end, op, vertsize,i;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
|
||||
if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
|
||||
else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
|
||||
else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
|
||||
|
||||
// Point verts to the array of new verts for cedge
|
||||
verts = BLI_ghash_lookup(gh, cedge);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);}
|
||||
end = (start+1)%3;
|
||||
op = (start+2)%3;
|
||||
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
3 2 1 0
|
||||
|---*---*---|
|
||||
\ |
|
||||
\ |
|
||||
\ |
|
||||
\ |
|
||||
\ |
|
||||
\ |
|
||||
|op
|
||||
|
||||
where start,end,op are indexes of EditFace->v1, etc (stored in v)
|
||||
and 0,1,2... are the indexes of the new verts stored in verts
|
||||
|
||||
We will fill this case like this or this depending on even or odd cuts
|
||||
|
||||
3 2 1 0
|
||||
|---*---*---|
|
||||
\ \ \ |
|
||||
\ \ \ |
|
||||
\ \ \ |
|
||||
\ \ \|
|
||||
\ \\|
|
||||
\ |
|
||||
|op
|
||||
*/
|
||||
|
||||
// Make side faces
|
||||
for(i=0;i<(vertsize-1);i++) {
|
||||
hold = addfacelist(verts[i],verts[i+1],v[op],NULL,NULL,NULL);
|
||||
if(i+1 != vertsize-1) {
|
||||
if(seltype == SUBDIV_SELECT_INNER) {
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
}
|
||||
}
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_quad_double_op(EditFace *efa, struct GHash *gh, int numcuts)
|
||||
{
|
||||
EditEdge *cedge[2]={NULL, NULL};
|
||||
EditVert *v[4], **verts[2];
|
||||
EditFace *hold;
|
||||
short start=0, end, left, right, vertsize,i;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
v[3] = efa->v4;
|
||||
|
||||
if(efa->e1->f & SELECT) { cedge[0] = efa->e1; cedge[1] = efa->e3; start = 0;}
|
||||
else if(efa->e2->f & SELECT) { cedge[0] = efa->e2; cedge[1] = efa->e4; start = 1;}
|
||||
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
|
||||
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
|
||||
end = (start+1)%4;
|
||||
left = (start+2)%4;
|
||||
right = (start+3)%4;
|
||||
if(verts[1][0] != v[left]) {flipvertarray(verts[1],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
3 2 1 0
|
||||
|---*---*---|
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
|---*---*---|
|
||||
0 1 2 3
|
||||
left right
|
||||
|
||||
We will fill this case like this or this depending on even or odd cuts
|
||||
|
||||
|---*---*---|
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
|---*---*---|
|
||||
*/
|
||||
|
||||
// Make side faces
|
||||
for(i=0;i<vertsize-1;i++) {
|
||||
hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-2-i],verts[1][vertsize-1-i],NULL,NULL);
|
||||
if(i < vertsize-2) {
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
hold->e2->f2 |= DOUBLEOPFILL;
|
||||
}
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_quad_double_adj_path(EditFace *efa, struct GHash *gh, int numcuts)
|
||||
{
|
||||
EditEdge *cedge[2]={NULL, NULL};
|
||||
EditVert *v[4], **verts[2];
|
||||
EditFace *hold;
|
||||
short start=0, start2=0, vertsize,i;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
v[3] = efa->v4;
|
||||
|
||||
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;}
|
||||
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;}
|
||||
if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3;}
|
||||
if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0;}
|
||||
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
|
||||
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
|
||||
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
3 2 1 0
|
||||
start2 0|---*---*---|
|
||||
| |
|
||||
1* |
|
||||
| |
|
||||
2* |
|
||||
| |
|
||||
end2 3|-----------|
|
||||
|
||||
We will fill this case like this or this depending on even or odd cuts
|
||||
|---*---*---|
|
||||
| / / / |
|
||||
* / / |
|
||||
| / / |
|
||||
* / |
|
||||
| / |
|
||||
|-----------|
|
||||
*/
|
||||
|
||||
// Make outside tris
|
||||
hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
|
||||
/* when ctrl is depressed, only want verts on the cutline selected */
|
||||
if (G.qual != LR_CTRLKEY)
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
hold = addfacelist(verts[0][0],verts[1][vertsize-1],v[(start2+2)%4],NULL,NULL,NULL);
|
||||
/* when ctrl is depressed, only want verts on the cutline selected */
|
||||
if (G.qual != LR_CTRLKEY)
|
||||
hold->e1->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
|
||||
// hold->e1->h |= EM_FGON;
|
||||
//}
|
||||
// Make side faces
|
||||
|
||||
for(i=0;i<numcuts;i++) {
|
||||
hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
//EM_fgon_flags();
|
||||
|
||||
}
|
||||
|
||||
static void fill_quad_double_adj_fan(EditFace *efa, struct GHash *gh, int numcuts)
|
||||
{
|
||||
EditEdge *cedge[2]={NULL, NULL};
|
||||
EditVert *v[4], *op=NULL, **verts[2];
|
||||
EditFace *hold;
|
||||
short start=0, start2=0, vertsize,i;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
v[3] = efa->v4;
|
||||
|
||||
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;}
|
||||
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;}
|
||||
if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;}
|
||||
if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;}
|
||||
|
||||
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
|
||||
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
|
||||
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
3 2 1 0
|
||||
start2 0|---*---*---|
|
||||
| |
|
||||
1* |
|
||||
| |
|
||||
2* |
|
||||
| |
|
||||
end2 3|-----------|op
|
||||
|
||||
We will fill this case like this or this (warning horrible ascii art follows)
|
||||
|---*---*---|
|
||||
| \ \ \ |
|
||||
*---\ \ \ |
|
||||
| \ \ \ \|
|
||||
*---- \ \ \ |
|
||||
| --- \\\|
|
||||
|-----------|
|
||||
*/
|
||||
|
||||
for(i=0;i<=numcuts;i++) {
|
||||
hold = addfacelist(op,verts[1][numcuts-i],verts[1][numcuts-i+1],NULL,NULL,NULL);
|
||||
hold->e1->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
|
||||
hold = addfacelist(op,verts[0][i],verts[0][i+1],NULL,NULL,NULL);
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_quad_double_adj_inner(EditFace *efa, struct GHash *gh, int numcuts)
|
||||
{
|
||||
EditEdge *cedge[2]={NULL, NULL};
|
||||
EditVert *v[4], *op=NULL, **verts[2],**inner;
|
||||
EditFace *hold;
|
||||
short start=0, start2=0, vertsize,i;
|
||||
float co[3];
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
v[3] = efa->v4;
|
||||
|
||||
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;}
|
||||
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;}
|
||||
if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;}
|
||||
if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;}
|
||||
|
||||
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
|
||||
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
|
||||
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
3 2 1 0
|
||||
start2 0|---*---*---|
|
||||
| |
|
||||
1* |
|
||||
| |
|
||||
2* |
|
||||
| |
|
||||
end2 3|-----------|op
|
||||
|
||||
We will fill this case like this or this (warning horrible ascii art follows)
|
||||
|---*-----*---|
|
||||
| * / |
|
||||
* \ / |
|
||||
| * |
|
||||
| / \ |
|
||||
* \ |
|
||||
| \ |
|
||||
|-------------|
|
||||
*/
|
||||
|
||||
// Add Inner Vert(s)
|
||||
inner = MEM_mallocN(sizeof(EditVert*)*numcuts,"New inner verts");
|
||||
|
||||
for(i=0;i<numcuts;i++) {
|
||||
co[0] = (verts[0][numcuts-i]->co[0] + verts[1][i+1]->co[0] ) / 2 ;
|
||||
co[1] = (verts[0][numcuts-i]->co[1] + verts[1][i+1]->co[1] ) / 2 ;
|
||||
co[2] = (verts[0][numcuts-i]->co[2] + verts[1][i+1]->co[2] ) / 2 ;
|
||||
inner[i] = addvertlist(co, NULL);
|
||||
inner[i]->f2 |= EDGEINNER;
|
||||
|
||||
EM_data_interp_from_verts(verts[0][numcuts-i], verts[1][i+1], inner[i], 0.5f);
|
||||
}
|
||||
|
||||
// Add Corner Quad
|
||||
hold = addfacelist(verts[0][numcuts+1],verts[1][1],inner[0],verts[0][numcuts],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
// Add Bottom Quads
|
||||
hold = addfacelist(verts[0][0],verts[0][1],inner[numcuts-1],op,NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
|
||||
hold = addfacelist(op,inner[numcuts-1],verts[1][numcuts],verts[1][numcuts+1],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
|
||||
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
|
||||
// hold->e1->h |= EM_FGON;
|
||||
//}
|
||||
// Add Fill Quads (if # cuts > 1)
|
||||
|
||||
for(i=0;i<numcuts-1;i++) {
|
||||
hold = addfacelist(inner[i],verts[1][i+1],verts[1][i+2],inner[i+1],NULL,NULL);
|
||||
hold->e1->f2 |= EDGEINNER;
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
|
||||
hold = addfacelist(inner[i],inner[i+1],verts[0][numcuts-1-i],verts[0][numcuts-i],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
hold->e4->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
|
||||
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
|
||||
// hold->e1->h |= EM_FGON;
|
||||
//}
|
||||
}
|
||||
|
||||
//EM_fgon_flags();
|
||||
|
||||
MEM_freeN(inner);
|
||||
}
|
||||
|
||||
static void fill_tri_double(EditFace *efa, struct GHash *gh, int numcuts)
|
||||
{
|
||||
EditEdge *cedge[2]={NULL, NULL};
|
||||
EditVert *v[3], **verts[2];
|
||||
EditFace *hold;
|
||||
short start=0, start2=0, vertsize,i;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
|
||||
if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;}
|
||||
if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;}
|
||||
if(efa->e3->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e1; start = 2; start2 = 0;}
|
||||
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
|
||||
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
|
||||
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
3 2 1 0
|
||||
start2 0|---*---*---|
|
||||
| /
|
||||
1* /
|
||||
| /
|
||||
2* /
|
||||
| /
|
||||
end2 3|
|
||||
|
||||
We will fill this case like this or this depending on even or odd cuts
|
||||
|---*---*---|
|
||||
| / / /
|
||||
* / /
|
||||
| / /
|
||||
* /
|
||||
| /
|
||||
|
|
||||
*/
|
||||
|
||||
// Make outside tri
|
||||
hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
// Make side faces
|
||||
|
||||
for(i=0;i<numcuts;i++) {
|
||||
hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_quad_triple(EditFace *efa, struct GHash *gh, int numcuts)
|
||||
{
|
||||
EditEdge *cedge[3]={0};
|
||||
EditVert *v[4], **verts[3];
|
||||
EditFace *hold;
|
||||
short start=0, start2=0, start3=0, vertsize, i, repeats;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
v[3] = efa->v4;
|
||||
|
||||
if(!(efa->e1->f & SELECT)) {
|
||||
cedge[0] = efa->e2;
|
||||
cedge[1] = efa->e3;
|
||||
cedge[2] = efa->e4;
|
||||
start = 1;start2 = 2;start3 = 3;
|
||||
}
|
||||
if(!(efa->e2->f & SELECT)) {
|
||||
cedge[0] = efa->e3;
|
||||
cedge[1] = efa->e4;
|
||||
cedge[2] = efa->e1;
|
||||
start = 2;start2 = 3;start3 = 0;
|
||||
}
|
||||
if(!(efa->e3->f & SELECT)) {
|
||||
cedge[0] = efa->e4;
|
||||
cedge[1] = efa->e1;
|
||||
cedge[2] = efa->e2;
|
||||
start = 3;start2 = 0;start3 = 1;
|
||||
}
|
||||
if(!(efa->e4->f & SELECT)) {
|
||||
cedge[0] = efa->e1;
|
||||
cedge[1] = efa->e2;
|
||||
cedge[2] = efa->e3;
|
||||
start = 0;start2 = 1;start3 = 2;
|
||||
}
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, cedge[0]);
|
||||
verts[1] = BLI_ghash_lookup(gh, cedge[1]);
|
||||
verts[2] = BLI_ghash_lookup(gh, cedge[2]);
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
|
||||
if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
|
||||
if(verts[2][0] != v[start3]) {flipvertarray(verts[2],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
start2
|
||||
3 2 1 0
|
||||
start3 0|---*---*---|3
|
||||
| |
|
||||
1* *2
|
||||
| |
|
||||
2* *1
|
||||
| |
|
||||
3|-----------|0 start
|
||||
|
||||
We will fill this case like this or this depending on even or odd cuts
|
||||
there are a couple of differences. For odd cuts, there is a tri in the
|
||||
middle as well as 1 quad at the bottom (not including the extra quads
|
||||
for odd cuts > 1
|
||||
|
||||
For even cuts, there is a quad in the middle and 2 quads on the bottom
|
||||
|
||||
they are numbered here for clarity
|
||||
|
||||
1 outer tris and bottom quads
|
||||
2 inner tri or quad
|
||||
3 repeating quads
|
||||
|
||||
|---*---*---*---|
|
||||
|1/ / \ \ 1|
|
||||
|/ 3 / \ 3 \|
|
||||
* / 2 \ *
|
||||
| / \ |
|
||||
|/ \ |
|
||||
*---------------*
|
||||
| 3 |
|
||||
| |
|
||||
*---------------*
|
||||
| |
|
||||
| 1 |
|
||||
| |
|
||||
|---------------|
|
||||
|
||||
|---*---*---*---*---|
|
||||
| 1/ / \ \ 1|
|
||||
| / / \ \ |
|
||||
|/ 3 / \ 3 \|
|
||||
* / \ *
|
||||
| / \ |
|
||||
| / 2 \ |
|
||||
|/ \|
|
||||
*-------------------*
|
||||
| |
|
||||
| 3 |
|
||||
| |
|
||||
*-------------------*
|
||||
| |
|
||||
| 1 |
|
||||
| |
|
||||
*-------------------*
|
||||
| |
|
||||
| 1 |
|
||||
| |
|
||||
|-------------------|
|
||||
|
||||
*/
|
||||
|
||||
// Make outside tris
|
||||
hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
hold = addfacelist(verts[1][vertsize-2],verts[1][vertsize-1],verts[2][1],NULL,NULL,NULL);
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
// Make bottom quad
|
||||
hold = addfacelist(verts[0][0],verts[0][1],verts[2][vertsize-2],verts[2][vertsize-1],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
//If it is even cuts, add the 2nd lower quad
|
||||
if(numcuts % 2 == 0) {
|
||||
hold = addfacelist(verts[0][1],verts[0][2],verts[2][vertsize-3],verts[2][vertsize-2],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
// Also Make inner quad
|
||||
hold = addfacelist(verts[1][numcuts/2],verts[1][(numcuts/2)+1],verts[2][numcuts/2],verts[0][(numcuts/2)+1],NULL,NULL);
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
|
||||
// hold->e3->h |= EM_FGON;
|
||||
//}
|
||||
facecopy(efa,hold);
|
||||
repeats = (numcuts / 2) -1;
|
||||
} else {
|
||||
// Make inner tri
|
||||
hold = addfacelist(verts[1][(numcuts/2)+1],verts[2][(numcuts/2)+1],verts[0][(numcuts/2)+1],NULL,NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
//if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
|
||||
// hold->e2->h |= EM_FGON;
|
||||
//}
|
||||
facecopy(efa,hold);
|
||||
repeats = ((numcuts+1) / 2)-1;
|
||||
}
|
||||
|
||||
// cuts for 1 and 2 do not have the repeating quads
|
||||
if(numcuts < 3) {repeats = 0;}
|
||||
for(i=0;i<repeats;i++) {
|
||||
//Make side repeating Quads
|
||||
hold = addfacelist(verts[1][i+1],verts[1][i+2],verts[0][vertsize-i-3],verts[0][vertsize-i-2],NULL,NULL);
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
hold = addfacelist(verts[1][vertsize-i-3],verts[1][vertsize-i-2],verts[2][i+1],verts[2][i+2],NULL,NULL);
|
||||
hold->e4->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
// Do repeating bottom quads
|
||||
for(i=0;i<repeats;i++) {
|
||||
if(numcuts % 2 == 1) {
|
||||
hold = addfacelist(verts[0][1+i],verts[0][2+i],verts[2][vertsize-3-i],verts[2][vertsize-2-i],NULL,NULL);
|
||||
} else {
|
||||
hold = addfacelist(verts[0][2+i],verts[0][3+i],verts[2][vertsize-4-i],verts[2][vertsize-3-i],NULL,NULL);
|
||||
}
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
//EM_fgon_flags();
|
||||
}
|
||||
|
||||
static void fill_quad_quadruple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty)
|
||||
{
|
||||
EditVert **verts[4], ***innerverts;
|
||||
EditFace *hold;
|
||||
EditEdge temp;
|
||||
short vertsize, i, j;
|
||||
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, efa->e1);
|
||||
verts[1] = BLI_ghash_lookup(gh, efa->e2);
|
||||
verts[2] = BLI_ghash_lookup(gh, efa->e3);
|
||||
verts[3] = BLI_ghash_lookup(gh, efa->e4);
|
||||
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);}
|
||||
if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);}
|
||||
if(verts[2][0] == efa->v3) {flipvertarray(verts[2],numcuts+2);}
|
||||
if(verts[3][0] == efa->v4) {flipvertarray(verts[3],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
1
|
||||
|
||||
3 2 1 0
|
||||
0|---*---*---|0
|
||||
| |
|
||||
1* *1
|
||||
2 | | 4
|
||||
2* *2
|
||||
| |
|
||||
3|---*---*---|3
|
||||
3 2 1 0
|
||||
|
||||
3
|
||||
// we will fill a 2 dim array of editvert*s to make filling easier
|
||||
// the innervert order is shown
|
||||
|
||||
0 0---1---2---3
|
||||
| | | |
|
||||
1 0---1---2---3
|
||||
| | | |
|
||||
2 0---1---2---3
|
||||
| | | |
|
||||
3 0---1---2---3
|
||||
|
||||
*/
|
||||
innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts outer array");
|
||||
for(i=0;i<numcuts+2;i++) {
|
||||
innerverts[i] = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts inner array");
|
||||
}
|
||||
|
||||
// first row is e1 last row is e3
|
||||
for(i=0;i<numcuts+2;i++) {
|
||||
innerverts[0][i] = verts[0][(numcuts+1)-i];
|
||||
innerverts[numcuts+1][i] = verts[2][(numcuts+1)-i];
|
||||
}
|
||||
|
||||
for(i=1;i<=numcuts;i++) {
|
||||
/* we create a fake edge for the next loop */
|
||||
temp.v2 = innerverts[i][0] = verts[1][i];
|
||||
temp.v1 = innerverts[i][numcuts+1] = verts[3][i];
|
||||
|
||||
for(j=1;j<=numcuts;j++) {
|
||||
float percent= (float)j/(float)(numcuts+1);
|
||||
|
||||
innerverts[i][(numcuts+1)-j]= subdivide_edge_addvert(&temp, rad, beauty, percent);
|
||||
}
|
||||
}
|
||||
// Fill with faces
|
||||
for(i=0;i<numcuts+1;i++) {
|
||||
for(j=0;j<numcuts+1;j++) {
|
||||
hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],innerverts[i+1][j+1],NULL,NULL);
|
||||
hold->e1->f2 = EDGENEW;
|
||||
hold->e2->f2 = EDGENEW;
|
||||
hold->e3->f2 = EDGENEW;
|
||||
hold->e4->f2 = EDGENEW;
|
||||
|
||||
if(i != 0) { hold->e1->f2 |= EDGEINNER; }
|
||||
if(j != 0) { hold->e2->f2 |= EDGEINNER; }
|
||||
if(i != numcuts) { hold->e3->f2 |= EDGEINNER; }
|
||||
if(j != numcuts) { hold->e4->f2 |= EDGEINNER; }
|
||||
|
||||
facecopy(efa,hold);
|
||||
}
|
||||
}
|
||||
// Clean up our dynamic multi-dim array
|
||||
for(i=0;i<numcuts+2;i++) {
|
||||
MEM_freeN(innerverts[i]);
|
||||
}
|
||||
MEM_freeN(innerverts);
|
||||
}
|
||||
|
||||
static void fill_tri_triple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty)
|
||||
{
|
||||
EditVert **verts[3], ***innerverts;
|
||||
short vertsize, i, j;
|
||||
EditFace *hold;
|
||||
EditEdge temp;
|
||||
|
||||
// Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
|
||||
verts[0] = BLI_ghash_lookup(gh, efa->e1);
|
||||
verts[1] = BLI_ghash_lookup(gh, efa->e2);
|
||||
verts[2] = BLI_ghash_lookup(gh, efa->e3);
|
||||
|
||||
//This is the index size of the verts array
|
||||
vertsize = numcuts+2;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);}
|
||||
if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);}
|
||||
if(verts[2][0] != efa->v3) {flipvertarray(verts[2],numcuts+2);}
|
||||
/*
|
||||
We should have something like this now
|
||||
3
|
||||
|
||||
3 2 1 0
|
||||
0|---*---*---|3
|
||||
| /
|
||||
1 1* *2
|
||||
| /
|
||||
2* *1 2
|
||||
| /
|
||||
3|/
|
||||
0
|
||||
|
||||
we will fill a 2 dim array of editvert*s to make filling easier
|
||||
|
||||
3
|
||||
|
||||
0 0---1---2---3---4
|
||||
| / | / |/ | /
|
||||
1 0---1----2---3
|
||||
1 | / | / | /
|
||||
2 0----1---2 2
|
||||
| / | /
|
||||
|/ |/
|
||||
3 0---1
|
||||
| /
|
||||
|/
|
||||
4 0
|
||||
|
||||
*/
|
||||
|
||||
innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"tri-tri subdiv inner verts outer array");
|
||||
for(i=0;i<numcuts+2;i++) {
|
||||
innerverts[i] = MEM_mallocN(sizeof(EditVert*)*((numcuts+2)-i),"tri-tri subdiv inner verts inner array");
|
||||
}
|
||||
//top row is e3 backwards
|
||||
for(i=0;i<numcuts+2;i++) {
|
||||
innerverts[0][i] = verts[2][(numcuts+1)-i];
|
||||
}
|
||||
|
||||
for(i=1;i<=numcuts+1;i++) {
|
||||
//fake edge, first vert is from e1, last is from e2
|
||||
temp.v1= innerverts[i][0] = verts[0][i];
|
||||
temp.v2= innerverts[i][(numcuts+1)-i] = verts[1][(numcuts+1)-i];
|
||||
|
||||
for(j=1;j<(numcuts+1)-i;j++) {
|
||||
float percent= (float)j/(float)((numcuts+1)-i);
|
||||
|
||||
innerverts[i][((numcuts+1)-i)-j]= subdivide_edge_addvert(&temp, rad, beauty, 1-percent);
|
||||
}
|
||||
}
|
||||
|
||||
// Now fill the verts with happy little tris :)
|
||||
for(i=0;i<=numcuts+1;i++) {
|
||||
for(j=0;j<(numcuts+1)-i;j++) {
|
||||
//We always do the first tri
|
||||
hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],NULL,NULL,NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
if(i != 0) { hold->e1->f2 |= EDGEINNER; }
|
||||
if(j != 0) { hold->e2->f2 |= EDGEINNER; }
|
||||
if(j+1 != (numcuts+1)-i) {hold->e3->f2 |= EDGEINNER;}
|
||||
|
||||
facecopy(efa,hold);
|
||||
//if there are more to come, we do the 2nd
|
||||
if(j+1 <= numcuts-i) {
|
||||
hold = addfacelist(innerverts[i+1][j],innerverts[i+1][j+1],innerverts[i][j+1],NULL,NULL,NULL);
|
||||
facecopy(efa,hold);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up our dynamic multi-dim array
|
||||
for(i=0;i<numcuts+2;i++) {
|
||||
MEM_freeN(innerverts[i]);
|
||||
}
|
||||
MEM_freeN(innerverts);
|
||||
}
|
||||
|
||||
//Next two fill types are for knife exact only and are provided to allow for knifing through vertices
|
||||
//This means there is no multicut!
|
||||
static void fill_quad_doublevert(EditFace *efa, int v1, int v2)
|
||||
{
|
||||
EditFace *hold;
|
||||
/*
|
||||
Depending on which two vertices have been knifed through (v1 and v2), we
|
||||
triangulate like the patterns below.
|
||||
X-------| |-------X
|
||||
| \ | | / |
|
||||
| \ | | / |
|
||||
| \ | | / |
|
||||
--------X X--------
|
||||
*/
|
||||
|
||||
if(v1 == 1 && v2 == 3){
|
||||
hold= addfacelist(efa->v1, efa->v2, efa->v3, 0, efa, NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
|
||||
hold= addfacelist(efa->v1, efa->v3, efa->v4, 0, efa, NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e1->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
}
|
||||
else{
|
||||
hold= addfacelist(efa->v1, efa->v2, efa->v4, 0, efa, NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
|
||||
hold= addfacelist(efa->v2, efa->v3, efa->v4, 0, efa, NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_quad_singlevert(EditFace *efa, struct GHash *gh)
|
||||
{
|
||||
EditEdge *cedge=NULL;
|
||||
EditVert *v[4], **verts;
|
||||
EditFace *hold;
|
||||
short start=0, end, left, right, vertsize;
|
||||
|
||||
v[0] = efa->v1;
|
||||
v[1] = efa->v2;
|
||||
v[2] = efa->v3;
|
||||
v[3] = efa->v4;
|
||||
|
||||
if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
|
||||
else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
|
||||
else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
|
||||
else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;}
|
||||
|
||||
// Point verts to the array of new verts for cedge
|
||||
verts = BLI_ghash_lookup(gh, cedge);
|
||||
//This is the index size of the verts array
|
||||
vertsize = 3;
|
||||
|
||||
// Is the original v1 the same as the first vert on the selected edge?
|
||||
// if not, the edge is running the opposite direction in this face so flip
|
||||
// the array to the correct direction
|
||||
|
||||
if(verts[0] != v[start]) {flipvertarray(verts,3);}
|
||||
end = (start+1)%4;
|
||||
left = (start+2)%4;
|
||||
right = (start+3)%4;
|
||||
|
||||
/*
|
||||
We should have something like this now
|
||||
|
||||
end start
|
||||
2 1 0
|
||||
|-----*-----|
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
-------------
|
||||
left right
|
||||
|
||||
where start,end,left, right are indexes of EditFace->v1, etc (stored in v)
|
||||
and 0,1,2 are the indexes of the new verts stored in verts. We fill like
|
||||
this, depending on whether its vertex 'left' or vertex 'right' thats
|
||||
been knifed through...
|
||||
|
||||
|---*---| |---*---|
|
||||
| / | | \ |
|
||||
| / | | \ |
|
||||
|/ | | \|
|
||||
X-------- --------X
|
||||
*/
|
||||
|
||||
if(v[left]->f1) {
|
||||
//triangle is composed of cutvert, end and left
|
||||
hold = addfacelist(verts[1],v[end],v[left],NULL, NULL,NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
|
||||
//quad is composed of cutvert, left, right and start
|
||||
hold = addfacelist(verts[1],v[left],v[right],v[start], NULL, NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e4->f2 |= EDGENEW;
|
||||
hold->e1->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
}
|
||||
else if(v[right]->f1) {
|
||||
//triangle is composed of cutvert, right and start
|
||||
hold = addfacelist(verts[1],v[right],v[start], NULL, NULL, NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e1->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
//quad is composed of cutvert, end, left, right
|
||||
hold = addfacelist(verts[1],v[end], v[left], v[right], NULL, NULL);
|
||||
hold->e1->f2 |= EDGENEW;
|
||||
hold->e2->f2 |= EDGENEW;
|
||||
hold->e3->f2 |= EDGENEW;
|
||||
hold->e4->f2 |= EDGENEW;
|
||||
hold->e4->f2 |= EDGEINNER;
|
||||
facecopy(efa, hold);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This function takes an example edge, the current point to create and
|
||||
// the total # of points to create, then creates the point and return the
|
||||
// editvert pointer to it.
|
||||
static EditVert *subdivideedgenum(EditEdge *edge, int curpoint, int totpoint, float rad, int beauty)
|
||||
{
|
||||
EditVert *ev;
|
||||
float percent;
|
||||
|
||||
if (beauty & (B_PERCENTSUBD) && totpoint == 1)
|
||||
//percent=(float)(edge->tmp.l)/32768.0f;
|
||||
percent= edge->tmp.fp;
|
||||
else
|
||||
percent= (float)curpoint/(float)(totpoint+1);
|
||||
|
||||
ev= subdivide_edge_addvert(edge, rad, beauty, percent);
|
||||
ev->f = edge->v1->f;
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
void esubdivideflag(int flag, float rad, int beauty, int numcuts, int seltype)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *ef;
|
||||
EditEdge *eed, *cedge, *sort[4];
|
||||
EditVert *eve, **templist;
|
||||
struct GHash *gh;
|
||||
float length[4], v1mat[3], v2mat[3], v3mat[3], v4mat[3];
|
||||
int i, j, edgecount, touchcount, facetype,hold;
|
||||
ModifierData *md= G.obedit->modifiers.first;
|
||||
|
||||
if(multires_test()) return;
|
||||
|
||||
//Set faces f1 to 0 cause we need it later
|
||||
for(ef=em->faces.first;ef;ef = ef->next) ef->f1 = 0;
|
||||
for(eve=em->verts.first; eve; eve=eve->next) {
|
||||
if(!(beauty & B_KNIFE)) /* knife sets this flag for vertex cuts */
|
||||
eve->f1 = 0;
|
||||
eve->f2 = 0;
|
||||
}
|
||||
|
||||
for (; md; md=md->next) {
|
||||
if ((md->type==eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
|
||||
MirrorModifierData *mmd = (MirrorModifierData*) md;
|
||||
|
||||
if(mmd->flag & MOD_MIR_CLIPPING) {
|
||||
for (eve= em->verts.first; eve; eve= eve->next) {
|
||||
eve->f2= 0;
|
||||
switch(mmd->axis) {
|
||||
case 0:
|
||||
if (fabs(eve->co[0]) < mmd->tolerance)
|
||||
eve->f2 |= 1;
|
||||
break;
|
||||
case 1:
|
||||
if (fabs(eve->co[1]) < mmd->tolerance)
|
||||
eve->f2 |= 2;
|
||||
break;
|
||||
case 2:
|
||||
if (fabs(eve->co[2]) < mmd->tolerance)
|
||||
eve->f2 |= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Flush vertex flags upward to the edges
|
||||
for(eed = em->edges.first;eed;eed = eed->next) {
|
||||
//if(eed->f & flag && eed->v1->f == eed->v2->f) {
|
||||
// eed->f |= eed->v1->f;
|
||||
// }
|
||||
eed->f2 = 0;
|
||||
if(eed->f & flag) {
|
||||
eed->f2 |= EDGEOLD;
|
||||
}
|
||||
}
|
||||
|
||||
// We store an array of verts for each edge that is subdivided,
|
||||
// we put this array as a value in a ghash which is keyed by the EditEdge*
|
||||
|
||||
// Now for beauty subdivide deselect edges based on length
|
||||
if(beauty & B_BEAUTY) {
|
||||
for(ef = em->faces.first;ef;ef = ef->next) {
|
||||
if(!ef->v4) {
|
||||
continue;
|
||||
}
|
||||
if(ef->f & SELECT) {
|
||||
copy_v3_v3(v1mat, ef->v1->co);
|
||||
copy_v3_v3(v2mat, ef->v2->co);
|
||||
copy_v3_v3(v3mat, ef->v3->co);
|
||||
copy_v3_v3(v4mat, ef->v4->co);
|
||||
mul_mat3_m4_v3(G.obedit->obmat, v1mat);
|
||||
mul_mat3_m4_v3(G.obedit->obmat, v2mat);
|
||||
mul_mat3_m4_v3(G.obedit->obmat, v3mat);
|
||||
mul_mat3_m4_v3(G.obedit->obmat, v4mat);
|
||||
|
||||
length[0] = len_v3v3(v1mat, v2mat);
|
||||
length[1] = len_v3v3(v2mat, v3mat);
|
||||
length[2] = len_v3v3(v3mat, v4mat);
|
||||
length[3] = len_v3v3(v4mat, v1mat);
|
||||
sort[0] = ef->e1;
|
||||
sort[1] = ef->e2;
|
||||
sort[2] = ef->e3;
|
||||
sort[3] = ef->e4;
|
||||
|
||||
|
||||
// Beauty Short Edges
|
||||
if(beauty & B_BEAUTY_SHORT) {
|
||||
for(j=0;j<2;j++) {
|
||||
hold = -1;
|
||||
for(i=0;i<4;i++) {
|
||||
if(length[i] < 0) {
|
||||
continue;
|
||||
} else if(hold == -1) {
|
||||
hold = i;
|
||||
} else {
|
||||
if(length[hold] < length[i]) {
|
||||
hold = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort[hold]->f &= ~SELECT;
|
||||
sort[hold]->f2 |= EDGENEW;
|
||||
length[hold] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Beauty Long Edges
|
||||
else {
|
||||
for(j=0;j<2;j++) {
|
||||
hold = -1;
|
||||
for(i=0;i<4;i++) {
|
||||
if(length[i] < 0) {
|
||||
continue;
|
||||
} else if(hold == -1) {
|
||||
hold = i;
|
||||
} else {
|
||||
if(length[hold] > length[i]) {
|
||||
hold = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort[hold]->f &= ~SELECT;
|
||||
sort[hold]->f2 |= EDGENEW;
|
||||
length[hold] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
|
||||
|
||||
// If we are knifing, We only need the selected edges that were cut, so deselect if it was not cut
|
||||
if(beauty & B_KNIFE) {
|
||||
for(eed= em->edges.first;eed;eed=eed->next) {
|
||||
if( eed->tmp.fp == 0 ) {
|
||||
EM_select_edge(eed,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// So for each edge, if it is selected, we allocate an array of size cuts+2
|
||||
// so we can have a place for the v1, the new verts and v2
|
||||
for(eed=em->edges.first;eed;eed = eed->next) {
|
||||
if(eed->f & flag) {
|
||||
templist = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"vertlist");
|
||||
templist[0] = eed->v1;
|
||||
for(i=0;i<numcuts;i++) {
|
||||
// This function creates the new vert and returns it back
|
||||
// to the array
|
||||
templist[i+1] = subdivideedgenum(eed, i+1, numcuts, rad, beauty);
|
||||
//while we are here, we can copy edge info from the original edge
|
||||
cedge = addedgelist(templist[i],templist[i+1],eed);
|
||||
// Also set the edge f2 to EDGENEW so that we can use this info later
|
||||
cedge->f2 = EDGENEW;
|
||||
}
|
||||
templist[i+1] = eed->v2;
|
||||
//Do the last edge too
|
||||
cedge = addedgelist(templist[i],templist[i+1],eed);
|
||||
cedge->f2 = EDGENEW;
|
||||
// Now that the edge is subdivided, we can put its verts in the ghash
|
||||
BLI_ghash_insert(gh, eed, templist);
|
||||
}
|
||||
}
|
||||
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
// Now for each face in the mesh we need to figure out How many edges were cut
|
||||
// and which filling method to use for that face
|
||||
for(ef = em->faces.first;ef;ef = ef->next) {
|
||||
edgecount = 0;
|
||||
facetype = 3;
|
||||
if(ef->e1->f & flag) {edgecount++;}
|
||||
if(ef->e2->f & flag) {edgecount++;}
|
||||
if(ef->e3->f & flag) {edgecount++;}
|
||||
if(ef->v4) {
|
||||
facetype = 4;
|
||||
if(ef->e4->f & flag) {edgecount++;}
|
||||
}
|
||||
if(facetype == 4) {
|
||||
switch(edgecount) {
|
||||
case 0:
|
||||
if(beauty & B_KNIFE && numcuts == 1){
|
||||
/*Test for when knifing through two opposite verts but no edges*/
|
||||
touchcount = 0;
|
||||
if(ef->v1->f1) touchcount++;
|
||||
if(ef->v2->f1) touchcount++;
|
||||
if(ef->v3->f1) touchcount++;
|
||||
if(ef->v4->f1) touchcount++;
|
||||
if(touchcount == 2){
|
||||
if(ef->v1->f1 && ef->v3->f1){
|
||||
ef->f1 = SELECT;
|
||||
fill_quad_doublevert(ef, 1, 3);
|
||||
}
|
||||
else if(ef->v2->f1 && ef->v4->f1){
|
||||
ef->f1 = SELECT;
|
||||
fill_quad_doublevert(ef, 2, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(beauty & B_KNIFE && numcuts == 1){
|
||||
/*Test for when knifing through an edge and one vert*/
|
||||
touchcount = 0;
|
||||
if(ef->v1->f1) touchcount++;
|
||||
if(ef->v2->f1) touchcount++;
|
||||
if(ef->v3->f1) touchcount++;
|
||||
if(ef->v4->f1) touchcount++;
|
||||
|
||||
if(touchcount == 1){
|
||||
if( (ef->e1->f & flag && ( !ef->e1->v1->f1 && !ef->e1->v2->f1 )) ||
|
||||
(ef->e2->f & flag && ( !ef->e2->v1->f1 && !ef->e2->v2->f1 )) ||
|
||||
(ef->e3->f & flag && ( !ef->e3->v1->f1 && !ef->e3->v2->f1 )) ||
|
||||
(ef->e4->f & flag && ( !ef->e4->v1->f1 && !ef->e4->v2->f1 )) ){
|
||||
|
||||
ef->f1 = SELECT;
|
||||
fill_quad_singlevert(ef, gh);
|
||||
}
|
||||
else{
|
||||
ef->f1 = SELECT;
|
||||
fill_quad_single(ef, gh, numcuts, seltype);
|
||||
}
|
||||
}
|
||||
else{
|
||||
ef->f1 = SELECT;
|
||||
fill_quad_single(ef, gh, numcuts, seltype);
|
||||
}
|
||||
}
|
||||
else{
|
||||
ef->f1 = SELECT;
|
||||
fill_quad_single(ef, gh, numcuts, seltype);
|
||||
}
|
||||
break;
|
||||
case 2: ef->f1 = SELECT;
|
||||
// if there are 2, we check if edge 1 and 3 are either both on or off that way
|
||||
// we can tell if the selected pair is Adjacent or Opposite of each other
|
||||
if((ef->e1->f & flag && ef->e3->f & flag) ||
|
||||
(ef->e2->f & flag && ef->e4->f & flag)) {
|
||||
fill_quad_double_op(ef, gh, numcuts);
|
||||
}else{
|
||||
switch(G.scene->toolsettings->cornertype) {
|
||||
case 0: fill_quad_double_adj_path(ef, gh, numcuts); break;
|
||||
case 1: fill_quad_double_adj_inner(ef, gh, numcuts); break;
|
||||
case 2: fill_quad_double_adj_fan(ef, gh, numcuts); break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case 3: ef->f1 = SELECT;
|
||||
fill_quad_triple(ef, gh, numcuts);
|
||||
break;
|
||||
case 4: ef->f1 = SELECT;
|
||||
fill_quad_quadruple(ef, gh, numcuts, rad, beauty);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(edgecount) {
|
||||
case 0: break;
|
||||
case 1: ef->f1 = SELECT;
|
||||
fill_tri_single(ef, gh, numcuts, seltype);
|
||||
break;
|
||||
case 2: ef->f1 = SELECT;
|
||||
fill_tri_double(ef, gh, numcuts);
|
||||
break;
|
||||
case 3: ef->f1 = SELECT;
|
||||
fill_tri_triple(ef, gh, numcuts, rad, beauty);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Old Edges and Faces
|
||||
for(eed = em->edges.first;eed;eed = eed->next) {
|
||||
if(BLI_ghash_haskey(gh,eed)) {
|
||||
eed->f1 = SELECT;
|
||||
} else {
|
||||
eed->f1 = 0;
|
||||
}
|
||||
}
|
||||
free_tagged_edges_faces(em->edges.first, em->faces.first);
|
||||
|
||||
if(seltype == SUBDIV_SELECT_ORIG && G.qual != LR_CTRLKEY) {
|
||||
/* bugfix: vertex could get flagged as "not-selected"
|
||||
// solution: clear flags before, not at the same time as setting SELECT flag -dg
|
||||
*/
|
||||
for(eed = em->edges.first;eed;eed = eed->next) {
|
||||
if(!(eed->f2 & EDGENEW || eed->f2 & EDGEOLD)) {
|
||||
eed->f &= !flag;
|
||||
EM_select_edge(eed,0);
|
||||
}
|
||||
}
|
||||
for(eed = em->edges.first;eed;eed = eed->next) {
|
||||
if(eed->f2 & EDGENEW || eed->f2 & EDGEOLD) {
|
||||
eed->f |= flag;
|
||||
EM_select_edge(eed,1);
|
||||
}
|
||||
}
|
||||
} else if ((seltype == SUBDIV_SELECT_INNER || seltype == SUBDIV_SELECT_INNER_SEL)|| G.qual == LR_CTRLKEY) {
|
||||
for(eed = em->edges.first;eed;eed = eed->next) {
|
||||
if(eed->f2 & EDGEINNER) {
|
||||
eed->f |= flag;
|
||||
EM_select_edge(eed,1);
|
||||
if(eed->v1->f & EDGEINNER) eed->v1->f |= SELECT;
|
||||
if(eed->v2->f & EDGEINNER) eed->v2->f |= SELECT;
|
||||
}else{
|
||||
eed->f &= !flag;
|
||||
EM_select_edge(eed,0);
|
||||
}
|
||||
}
|
||||
} else if(seltype == SUBDIV_SELECT_LOOPCUT){
|
||||
for(eed = em->edges.first;eed;eed = eed->next) {
|
||||
if(eed->f2 & DOUBLEOPFILL){
|
||||
eed->f |= flag;
|
||||
EM_select_edge(eed,1);
|
||||
}else{
|
||||
eed->f &= !flag;
|
||||
EM_select_edge(eed,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(G.scene->selectmode & SCE_SELECT_VERTEX) {
|
||||
for(eed = em->edges.first;eed;eed = eed->next) {
|
||||
if(eed->f & SELECT) {
|
||||
eed->v1->f |= SELECT;
|
||||
eed->v2->f |= SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fix hide flags for edges. First pass, hide edges of hidden faces
|
||||
for(ef=em->faces.first; ef; ef=ef->next){
|
||||
if(ef->h){
|
||||
ef->e1->h |= 1;
|
||||
ef->e2->h |= 1;
|
||||
ef->e3->h |= 1;
|
||||
if(ef->e4) ef->e4->h |= 1;
|
||||
}
|
||||
}
|
||||
//second pass: unhide edges of visible faces adjacent to hidden faces
|
||||
for(ef=em->faces.first; ef; ef=ef->next){
|
||||
if(ef->h == 0){
|
||||
ef->e1->h &= ~1;
|
||||
ef->e2->h &= ~1;
|
||||
ef->e3->h &= ~1;
|
||||
if(ef->e4) ef->e4->h &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the ghash and call MEM_freeN on all the value entries to return
|
||||
// that memory
|
||||
BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN);
|
||||
|
||||
EM_selectmode_flush();
|
||||
for(ef=em->faces.first;ef;ef = ef->next) {
|
||||
if(ef->e4) {
|
||||
if( (ef->e1->f & SELECT && ef->e2->f & SELECT) &&
|
||||
(ef->e3->f & SELECT && ef->e4->f & SELECT) ) {
|
||||
ef->f |= SELECT;
|
||||
}
|
||||
} else {
|
||||
if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && ef->e3->f & SELECT) {
|
||||
ef->f |= SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recalc_editnormals();
|
||||
countall();
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
}
|
||||
|
||||
static int count_selected_edges(EditEdge *ed)
|
||||
{
|
||||
int totedge = 0;
|
||||
while(ed) {
|
||||
ed->tmp.p = 0;
|
||||
if( ed->f & SELECT ) totedge++;
|
||||
ed= ed->next;
|
||||
}
|
||||
return totedge;
|
||||
}
|
||||
|
||||
/* hurms, as if this makes code readable! It's pointerpointer hiding... (ton) */
|
||||
typedef EditFace *EVPtr;
|
||||
typedef EVPtr EVPTuple[2];
|
||||
|
||||
/** builds EVPTuple array efaa of face tuples (in fact pointers to EditFaces)
|
||||
sharing one edge.
|
||||
arguments: selected edge list, face list.
|
||||
Edges will also be tagged accordingly (see eed->f2) */
|
||||
|
||||
static int collect_quadedges(EVPTuple *efaa, EditEdge *eed, EditFace *efa)
|
||||
{
|
||||
EditEdge *e1, *e2, *e3;
|
||||
EVPtr *evp;
|
||||
int i = 0;
|
||||
|
||||
/* run through edges, if selected, set pointer edge-> facearray */
|
||||
while(eed) {
|
||||
eed->f2= 0;
|
||||
eed->f1= 0;
|
||||
if( eed->f & SELECT ) {
|
||||
eed->tmp.p = (EditVert *) (&efaa[i]);
|
||||
i++;
|
||||
}
|
||||
else eed->tmp.p = NULL;
|
||||
|
||||
eed= eed->next;
|
||||
}
|
||||
|
||||
|
||||
/* find edges pointing to 2 faces by procedure:
|
||||
|
||||
- run through faces and their edges, increase
|
||||
face counter e->f1 for each face
|
||||
*/
|
||||
|
||||
while(efa) {
|
||||
efa->f1= 0;
|
||||
if(efa->v4==0 && (efa->f & SELECT)) { /* if selected triangle */
|
||||
e1= efa->e1;
|
||||
e2= efa->e2;
|
||||
e3= efa->e3;
|
||||
if(e1->f2<3 && e1->tmp.p) {
|
||||
if(e1->f2<2) {
|
||||
evp= (EVPtr *) e1->tmp.p;
|
||||
evp[(int)e1->f2] = efa;
|
||||
}
|
||||
e1->f2+= 1;
|
||||
}
|
||||
if(e2->f2<3 && e2->tmp.p) {
|
||||
if(e2->f2<2) {
|
||||
evp= (EVPtr *) e2->tmp.p;
|
||||
evp[(int)e2->f2]= efa;
|
||||
}
|
||||
e2->f2+= 1;
|
||||
}
|
||||
if(e3->f2<3 && e3->tmp.p) {
|
||||
if(e3->f2<2) {
|
||||
evp= (EVPtr *) e3->tmp.p;
|
||||
evp[(int)e3->f2]= efa;
|
||||
}
|
||||
e3->f2+= 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* set to 3 to make sure these are not flipped or joined */
|
||||
efa->e1->f2= 3;
|
||||
efa->e2->f2= 3;
|
||||
efa->e3->f2= 3;
|
||||
if (efa->e4) efa->e4->f2= 3;
|
||||
}
|
||||
|
||||
efa= efa->next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/* returns vertices of two adjacent triangles forming a quad
|
||||
- can be righthand or lefthand
|
||||
|
||||
4-----3
|
||||
|\ |
|
||||
| \ 2 | <- efa1
|
||||
| \ |
|
||||
efa-> | 1 \ |
|
||||
| \|
|
||||
1-----2
|
||||
|
||||
*/
|
||||
#define VTEST(face, num, other) \
|
||||
(face->v##num != other->v1 && face->v##num != other->v2 && face->v##num != other->v3)
|
||||
|
||||
static void givequadverts(EditFace *efa, EditFace *efa1, EditVert **v1, EditVert **v2, EditVert **v3, EditVert **v4, int *vindex)
|
||||
{
|
||||
if VTEST(efa, 1, efa1) {
|
||||
*v1= efa->v1;
|
||||
*v2= efa->v2;
|
||||
vindex[0]= 0;
|
||||
vindex[1]= 1;
|
||||
}
|
||||
else if VTEST(efa, 2, efa1) {
|
||||
*v1= efa->v2;
|
||||
*v2= efa->v3;
|
||||
vindex[0]= 1;
|
||||
vindex[1]= 2;
|
||||
}
|
||||
else if VTEST(efa, 3, efa1) {
|
||||
*v1= efa->v3;
|
||||
*v2= efa->v1;
|
||||
vindex[0]= 2;
|
||||
vindex[1]= 0;
|
||||
}
|
||||
|
||||
if VTEST(efa1, 1, efa) {
|
||||
*v3= efa1->v1;
|
||||
*v4= efa1->v2;
|
||||
vindex[2]= 0;
|
||||
vindex[3]= 1;
|
||||
}
|
||||
else if VTEST(efa1, 2, efa) {
|
||||
*v3= efa1->v2;
|
||||
*v4= efa1->v3;
|
||||
vindex[2]= 1;
|
||||
vindex[3]= 2;
|
||||
}
|
||||
else if VTEST(efa1, 3, efa) {
|
||||
*v3= efa1->v3;
|
||||
*v4= efa1->v1;
|
||||
vindex[2]= 2;
|
||||
vindex[3]= 0;
|
||||
}
|
||||
else
|
||||
*v3= *v4= NULL;
|
||||
}
|
||||
|
||||
/* Helper functions for edge/quad edit features*/
|
||||
static void untag_edges(EditFace *f)
|
||||
{
|
||||
f->e1->f1 = 0;
|
||||
f->e2->f1 = 0;
|
||||
f->e3->f1 = 0;
|
||||
if (f->e4) f->e4->f1 = 0;
|
||||
}
|
||||
|
||||
/** remove and free list of tagged edges and faces */
|
||||
static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa)
|
||||
{
|
||||
EditMesh *em= G.editMesh;
|
||||
EditEdge *nexted;
|
||||
EditFace *nextvl;
|
||||
|
||||
while(efa) {
|
||||
nextvl= efa->next;
|
||||
if(efa->f1) {
|
||||
BLI_remlink(&em->faces, efa);
|
||||
free_editface(efa);
|
||||
}
|
||||
else
|
||||
/* avoid deleting edges that are still in use */
|
||||
untag_edges(efa);
|
||||
efa= nextvl;
|
||||
}
|
||||
|
||||
while(eed) {
|
||||
nexted= eed->next;
|
||||
if(eed->f1) {
|
||||
remedge(eed);
|
||||
free_editedge(eed);
|
||||
}
|
||||
eed= nexted;
|
||||
}
|
||||
}
|
||||
|
||||
/* note; the EM_selectmode_set() calls here illustrate how badly constructed it all is... from before the
|
||||
edge/face flags, with very mixed results.... */
|
||||
void beauty_fill(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *v1, *v2, *v3, *v4;
|
||||
EditEdge *eed, *nexted;
|
||||
EditEdge dia1, dia2;
|
||||
EditFace *efa, *w;
|
||||
// void **efaar, **efaa;
|
||||
EVPTuple *efaar;
|
||||
EVPtr *efaa;
|
||||
float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2;
|
||||
int totedge, ok, notbeauty=8, onedone, vindex[4];
|
||||
|
||||
if(multires_test()) return;
|
||||
|
||||
/* - all selected edges with two faces
|
||||
* - find the faces: store them in edges (using datablock)
|
||||
* - per edge: - test convex
|
||||
* - test edge: flip?
|
||||
* - if true: remedge, addedge, all edges at the edge get new face pointers
|
||||
*/
|
||||
|
||||
EM_selectmode_set(); // makes sure in selectmode 'face' the edges of selected faces are selected too
|
||||
|
||||
totedge = count_selected_edges(em->edges.first);
|
||||
if(totedge==0) return;
|
||||
|
||||
/* temp block with face pointers */
|
||||
efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "beautyfill");
|
||||
|
||||
while (notbeauty) {
|
||||
notbeauty--;
|
||||
|
||||
ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
|
||||
|
||||
/* there we go */
|
||||
onedone= 0;
|
||||
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
nexted= eed->next;
|
||||
|
||||
/* f2 is set in collect_quadedges() */
|
||||
if(eed->f2==2 && eed->h==0) {
|
||||
|
||||
efaa = (EVPtr *) eed->tmp.p;
|
||||
|
||||
/* none of the faces should be treated before, nor be part of fgon */
|
||||
ok= 1;
|
||||
efa= efaa[0];
|
||||
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
|
||||
if(efa->fgonf) ok= 0;
|
||||
efa= efaa[1];
|
||||
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
|
||||
if(efa->fgonf) ok= 0;
|
||||
|
||||
if(ok) {
|
||||
/* test convex */
|
||||
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
|
||||
if(v1 && v2 && v3 && v4) {
|
||||
if( convex(v1->co, v2->co, v3->co, v4->co) ) {
|
||||
|
||||
/* test edges */
|
||||
if( (v1) > (v3) ) {
|
||||
dia1.v1= v3;
|
||||
dia1.v2= v1;
|
||||
}
|
||||
else {
|
||||
dia1.v1= v1;
|
||||
dia1.v2= v3;
|
||||
}
|
||||
|
||||
if( (v2) > (v4) ) {
|
||||
dia2.v1= v4;
|
||||
dia2.v2= v2;
|
||||
}
|
||||
else {
|
||||
dia2.v1= v2;
|
||||
dia2.v2= v4;
|
||||
}
|
||||
|
||||
/* testing rule:
|
||||
* the area divided by the total edge lengths
|
||||
*/
|
||||
|
||||
len1= len_v3v3(v1->co, v2->co);
|
||||
len2= len_v3v3(v2->co, v3->co);
|
||||
len3= len_v3v3(v3->co, v4->co);
|
||||
len4= len_v3v3(v4->co, v1->co);
|
||||
len5= len_v3v3(v1->co, v3->co);
|
||||
len6= len_v3v3(v2->co, v4->co);
|
||||
|
||||
opp1= area_tri_v3(v1->co, v2->co, v3->co);
|
||||
opp2= area_tri_v3(v1->co, v3->co, v4->co);
|
||||
|
||||
fac1= opp1/(len1+len2+len5) + opp2/(len3+len4+len5);
|
||||
|
||||
opp1= area_tri_v3(v2->co, v3->co, v4->co);
|
||||
opp2= area_tri_v3(v2->co, v4->co, v1->co);
|
||||
|
||||
fac2= opp1/(len2+len3+len6) + opp2/(len4+len1+len6);
|
||||
|
||||
ok= 0;
|
||||
if(fac1 > fac2) {
|
||||
if(dia2.v1==eed->v1 && dia2.v2==eed->v2) {
|
||||
eed->f1= 1;
|
||||
efa= efaa[0];
|
||||
efa->f1= 1;
|
||||
efa= efaa[1];
|
||||
efa->f1= 1;
|
||||
|
||||
w= EM_face_from_faces(efaa[0], efaa[1],
|
||||
vindex[0], vindex[1], 4+vindex[2], -1);
|
||||
w->f |= SELECT;
|
||||
|
||||
|
||||
w= EM_face_from_faces(efaa[0], efaa[1],
|
||||
vindex[0], 4+vindex[2], 4+vindex[3], -1);
|
||||
w->f |= SELECT;
|
||||
|
||||
onedone= 1;
|
||||
}
|
||||
}
|
||||
else if(fac1 < fac2) {
|
||||
if(dia1.v1==eed->v1 && dia1.v2==eed->v2) {
|
||||
eed->f1= 1;
|
||||
efa= efaa[0];
|
||||
efa->f1= 1;
|
||||
efa= efaa[1];
|
||||
efa->f1= 1;
|
||||
|
||||
|
||||
w= EM_face_from_faces(efaa[0], efaa[1],
|
||||
vindex[1], 4+vindex[2], 4+vindex[3], -1);
|
||||
w->f |= SELECT;
|
||||
|
||||
|
||||
w= EM_face_from_faces(efaa[0], efaa[1],
|
||||
vindex[0], 4+vindex[1], 4+vindex[3], -1);
|
||||
w->f |= SELECT;
|
||||
|
||||
onedone= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
eed= nexted;
|
||||
}
|
||||
|
||||
free_tagged_edges_faces(em->edges.first, em->faces.first);
|
||||
|
||||
if(onedone==0) break;
|
||||
|
||||
EM_selectmode_set(); // new edges/faces were added
|
||||
}
|
||||
|
||||
MEM_freeN(efaar);
|
||||
|
||||
EM_select_flush();
|
||||
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
BIF_undo_push("Beauty Fill");
|
||||
}
|
||||
|
||||
|
||||
/* ******************** BEGIN TRIANGLE TO QUAD ************************************* */
|
||||
static float measure_facepair(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, float limit)
|
||||
{
|
||||
|
||||
/*gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make*/
|
||||
/*Note: this is more complicated than it needs to be and should be cleaned up...*/
|
||||
float measure = 0.0, noA1[3], noA2[3], noB1[3], noB2[3], normalADiff, normalBDiff,
|
||||
edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3], diff,
|
||||
minarea, maxarea, areaA, areaB;
|
||||
|
||||
/*First Test: Normal difference*/
|
||||
normal_tri_v3( noA1,v1->co, v2->co, v3->co);
|
||||
normal_tri_v3( noA2,v1->co, v3->co, v4->co);
|
||||
|
||||
if(noA1[0] == noA2[0] && noA1[1] == noA2[1] && noA1[2] == noA2[2]) normalADiff = 0.0;
|
||||
else normalADiff = angle_v2v2(noA1, noA2);
|
||||
//if(!normalADiff) normalADiff = 179;
|
||||
normal_tri_v3( noB1,v2->co, v3->co, v4->co);
|
||||
normal_tri_v3( noB2,v4->co, v1->co, v2->co);
|
||||
|
||||
if(noB1[0] == noB2[0] && noB1[1] == noB2[1] && noB1[2] == noB2[2]) normalBDiff = 0.0;
|
||||
else normalBDiff = angle_v2v2(noB1, noB2);
|
||||
//if(!normalBDiff) normalBDiff = 179;
|
||||
|
||||
measure += (normalADiff/360) + (normalBDiff/360);
|
||||
if(measure > limit) return measure;
|
||||
|
||||
/*Second test: Colinearity*/
|
||||
sub_v3_v3v3(edgeVec1, v1->co, v2->co);
|
||||
sub_v3_v3v3(edgeVec2, v2->co, v3->co);
|
||||
sub_v3_v3v3(edgeVec3, v3->co, v4->co);
|
||||
sub_v3_v3v3(edgeVec4, v4->co, v1->co);
|
||||
|
||||
diff = 0.0;
|
||||
|
||||
diff = (
|
||||
fabs(angle_v2v2(edgeVec1, edgeVec2) - 90) +
|
||||
fabs(angle_v2v2(edgeVec2, edgeVec3) - 90) +
|
||||
fabs(angle_v2v2(edgeVec3, edgeVec4) - 90) +
|
||||
fabs(angle_v2v2(edgeVec4, edgeVec1) - 90)) / 360;
|
||||
if(!diff) return 0.0;
|
||||
|
||||
measure += diff;
|
||||
if(measure > limit) return measure;
|
||||
|
||||
/*Third test: Concavity*/
|
||||
areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co);
|
||||
areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co);
|
||||
|
||||
if(areaA <= areaB) minarea = areaA;
|
||||
else minarea = areaB;
|
||||
|
||||
if(areaA >= areaB) maxarea = areaA;
|
||||
else maxarea = areaB;
|
||||
|
||||
if(!maxarea) measure += 1;
|
||||
else measure += (1 - (minarea / maxarea));
|
||||
|
||||
return measure;
|
||||
}
|
||||
|
||||
#define T2QUV_LIMIT 0.005
|
||||
#define T2QCOL_LIMIT 3
|
||||
static int compareFaceAttribs(EditFace *f1, EditFace *f2, EditEdge *eed)
|
||||
{
|
||||
/*Test to see if the per-face attributes for the joining edge match within limit*/
|
||||
MTFace *tf1, *tf2;
|
||||
unsigned int *col1, *col2;
|
||||
short i,attrok=0, flag = G.scene->toolsettings->editbutflag, fe1[2], fe2[2];
|
||||
|
||||
tf1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MTFACE);
|
||||
tf2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MTFACE);
|
||||
|
||||
col1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MCOL);
|
||||
col2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MCOL);
|
||||
|
||||
/*store indices for faceedges*/
|
||||
f1->v1->f1 = 0;
|
||||
f1->v2->f1 = 1;
|
||||
f1->v3->f1 = 2;
|
||||
|
||||
fe1[0] = eed->v1->f1;
|
||||
fe1[1] = eed->v2->f1;
|
||||
|
||||
f2->v1->f1 = 0;
|
||||
f2->v2->f1 = 1;
|
||||
f2->v3->f1 = 2;
|
||||
|
||||
fe2[0] = eed->v1->f1;
|
||||
fe2[1] = eed->v2->f1;
|
||||
|
||||
/*compare faceedges for each face attribute. Additional per face attributes can be added later*/
|
||||
/*do UVs*/
|
||||
if(flag & B_JOINTRIA_UV){
|
||||
|
||||
if(tf1 == NULL || tf2 == NULL) attrok |= B_JOINTRIA_UV;
|
||||
else if(tf1->tpage != tf2->tpage); /*do nothing*/
|
||||
else{
|
||||
for(i = 0; i < 2; i++){
|
||||
if(tf1->uv[fe1[i]][0] + T2QUV_LIMIT > tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][0] - T2QUV_LIMIT < tf2->uv[fe2[i]][0] &&
|
||||
tf1->uv[fe1[i]][1] + T2QUV_LIMIT > tf2->uv[fe2[i]][1] && tf1->uv[fe1[i]][1] - T2QUV_LIMIT < tf2->uv[fe2[i]][1]) attrok |= B_JOINTRIA_UV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*do VCOLs*/
|
||||
if(flag & B_JOINTRIA_VCOL){
|
||||
if(!col1 || !col2) attrok |= B_JOINTRIA_VCOL;
|
||||
else{
|
||||
char *f1vcol, *f2vcol;
|
||||
for(i = 0; i < 2; i++){
|
||||
f1vcol = (char *)&(col1[fe1[i]]);
|
||||
f2vcol = (char *)&(col2[fe2[i]]);
|
||||
|
||||
/*compare f1vcol with f2vcol*/
|
||||
if( f1vcol[1] + T2QCOL_LIMIT > f2vcol[1] && f1vcol[1] - T2QCOL_LIMIT < f2vcol[1] &&
|
||||
f1vcol[2] + T2QCOL_LIMIT > f2vcol[2] && f1vcol[2] - T2QCOL_LIMIT < f2vcol[2] &&
|
||||
f1vcol[3] + T2QCOL_LIMIT > f2vcol[3] && f1vcol[3] - T2QCOL_LIMIT < f2vcol[3]) attrok |= B_JOINTRIA_VCOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ((attrok & B_JOINTRIA_UV) == (flag & B_JOINTRIA_UV)) && ((attrok & B_JOINTRIA_VCOL) == (flag & B_JOINTRIA_VCOL)) ) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fplcmp(const void *v1, const void *v2)
|
||||
{
|
||||
const EditEdge *e1= *((EditEdge**)v1), *e2=*((EditEdge**)v2);
|
||||
|
||||
if( e1->crease > e2->crease) return 1;
|
||||
else if( e1->crease < e2->crease) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*Bitflags for edges.*/
|
||||
#define T2QDELETE 1
|
||||
#define T2QCOMPLEX 2
|
||||
#define T2QJOIN 4
|
||||
void join_triangles(void)
|
||||
{
|
||||
EditMesh *em=G.editMesh;
|
||||
EditVert *v1, *v2, *v3, *v4, *eve;
|
||||
EditEdge *eed, **edsortblock = NULL, **edb = NULL;
|
||||
EditFace *efa;
|
||||
EVPTuple *efaar = NULL;
|
||||
EVPtr *efaa = NULL;
|
||||
float *creases = NULL;
|
||||
float measure; /*Used to set tolerance*/
|
||||
float limit = G.scene->toolsettings->jointrilimit;
|
||||
int i, ok, totedge=0, totseledge=0, complexedges, vindex[4];
|
||||
|
||||
/*test for multi-resolution data*/
|
||||
if(multires_test()) return;
|
||||
|
||||
/*if we take a long time on very dense meshes we want waitcursor to display*/
|
||||
waitcursor(1);
|
||||
|
||||
totseledge = count_selected_edges(em->edges.first);
|
||||
if(totseledge==0) return;
|
||||
|
||||
/*abusing crease value to store weights for edge pairs. Nasty*/
|
||||
for(eed=em->edges.first; eed; eed=eed->next) totedge++;
|
||||
if(totedge) creases = MEM_callocN(sizeof(float) * totedge, "Join Triangles Crease Array");
|
||||
for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++){
|
||||
creases[i] = eed->crease;
|
||||
eed->crease = 0.0;
|
||||
}
|
||||
|
||||
/*clear temp flags*/
|
||||
for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = eve->f2 = 0;
|
||||
for(eed=em->edges.first; eed; eed=eed->next) eed->f2 = eed->f1 = 0;
|
||||
for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = efa->tmp.l = 0;
|
||||
|
||||
/*For every selected 2 manifold edge, create pointers to its two faces.*/
|
||||
efaar= (EVPTuple *) MEM_callocN(totseledge * sizeof(EVPTuple), "Tri2Quad");
|
||||
ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
|
||||
complexedges = 0;
|
||||
|
||||
if(ok){
|
||||
|
||||
|
||||
/*clear tmp.l flag and store number of faces that are selected and coincident to current face here.*/
|
||||
for(eed=em->edges.first; eed; eed=eed->next){
|
||||
/* eed->f2 is 2 only if this edge is part of exactly two
|
||||
triangles, and both are selected, and it has EVPTuple assigned */
|
||||
if(eed->f2 == 2){
|
||||
efaa= (EVPtr *) eed->tmp.p;
|
||||
efaa[0]->tmp.l++;
|
||||
efaa[1]->tmp.l++;
|
||||
}
|
||||
}
|
||||
|
||||
for(eed=em->edges.first; eed; eed=eed->next){
|
||||
if(eed->f2 == 2){
|
||||
efaa= (EVPtr *) eed->tmp.p;
|
||||
v1 = v2 = v3 = v4 = NULL;
|
||||
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
|
||||
if(v1 && v2 && v3 && v4){
|
||||
/*test if simple island first. This mimics 2.42 behaviour and the tests are less restrictive.*/
|
||||
if(efaa[0]->tmp.l == 1 && efaa[1]->tmp.l == 1){
|
||||
if( convex(v1->co, v2->co, v3->co, v4->co) ){
|
||||
eed->f1 |= T2QJOIN;
|
||||
efaa[0]->f1 = 1; //mark for join
|
||||
efaa[1]->f1 = 1; //mark for join
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
||||
/* The face pair is part of a 'complex' island, so the rules for dealing with it are more involved.
|
||||
Depending on what options the user has chosen, this face pair can be 'thrown out' based upon the following criteria:
|
||||
|
||||
1: the two faces do not share the same material
|
||||
2: the edge joining the two faces is marked as sharp.
|
||||
3: the two faces UV's do not make a good match
|
||||
4: the two faces Vertex colors do not make a good match
|
||||
|
||||
If the face pair passes all the applicable tests, it is then given a 'weight' with the measure_facepair() function.
|
||||
This measures things like concavity, colinearity ect. If this weight is below the threshold set by the user
|
||||
the edge joining them is marked as being 'complex' and will be compared against other possible pairs which contain one of the
|
||||
same faces in the current pair later.
|
||||
|
||||
This technique is based upon an algorithm that Campbell Barton developed for his Tri2Quad script that was previously part of
|
||||
the python scripts bundled with Blender releases.
|
||||
*/
|
||||
|
||||
if(G.scene->toolsettings->editbutflag & B_JOINTRIA_SHARP && eed->sharp); /*do nothing*/
|
||||
else if(G.scene->toolsettings->editbutflag & B_JOINTRIA_MAT && efaa[0]->mat_nr != efaa[1]->mat_nr); /*do nothing*/
|
||||
else if(((G.scene->toolsettings->editbutflag & B_JOINTRIA_UV) || (G.scene->toolsettings->editbutflag & B_JOINTRIA_VCOL)) &&
|
||||
compareFaceAttribs(efaa[0], efaa[1], eed) == 0); /*do nothing*/
|
||||
else{
|
||||
measure = measure_facepair(v1, v2, v3, v4, limit);
|
||||
if(measure < limit){
|
||||
complexedges++;
|
||||
eed->f1 |= T2QCOMPLEX;
|
||||
eed->crease = measure; /*we dont mark edges for join yet*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Quicksort the complex edges according to their weighting*/
|
||||
if(complexedges){
|
||||
edsortblock = edb = MEM_callocN(sizeof(EditEdge*) * complexedges, "Face Pairs quicksort Array");
|
||||
for(eed = em->edges.first; eed; eed=eed->next){
|
||||
if(eed->f1 & T2QCOMPLEX){
|
||||
*edb = eed;
|
||||
edb++;
|
||||
}
|
||||
}
|
||||
qsort(edsortblock, complexedges, sizeof(EditEdge*), fplcmp);
|
||||
/*now go through and mark the edges who get the highest weighting*/
|
||||
for(edb=edsortblock, i=0; i < complexedges; edb++, i++){
|
||||
efaa = (EVPtr *)((*edb)->tmp.p); /*suspect!*/
|
||||
if( !efaa[0]->f1 && !efaa[1]->f1){
|
||||
efaa[0]->f1 = 1; //mark for join
|
||||
efaa[1]->f1 = 1; //mark for join
|
||||
(*edb)->f1 |= T2QJOIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*finally go through all edges marked for join (simple and complex) and create new faces*/
|
||||
for(eed=em->edges.first; eed; eed=eed->next){
|
||||
if(eed->f1 & T2QJOIN){
|
||||
efaa= (EVPtr *)eed->tmp.p;
|
||||
v1 = v2 = v3 = v4 = NULL;
|
||||
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
|
||||
if((v1 && v2 && v3 && v4) && (exist_face(v1, v2, v3, v4)==0)){ /*exist_face is very slow! Needs to be adressed.*/
|
||||
/*flag for delete*/
|
||||
eed->f1 |= T2QDELETE;
|
||||
/*create new quad and select*/
|
||||
efa = EM_face_from_faces(efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], 4+vindex[3]);
|
||||
EM_select_face(efa,1);
|
||||
}
|
||||
else{
|
||||
efaa[0]->f1 = 0;
|
||||
efaa[1]->f1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*free data and cleanup*/
|
||||
if(creases){
|
||||
for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++) eed->crease = creases[i];
|
||||
MEM_freeN(creases);
|
||||
}
|
||||
for(eed=em->edges.first; eed; eed=eed->next){
|
||||
if(eed->f1 & T2QDELETE) eed->f1 = 1;
|
||||
else eed->f1 = 0;
|
||||
}
|
||||
free_tagged_edges_faces(em->edges.first, em->faces.first);
|
||||
if(efaar) MEM_freeN(efaar);
|
||||
if(edsortblock) MEM_freeN(edsortblock);
|
||||
|
||||
EM_selectmode_flush();
|
||||
countall();
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
waitcursor(0);
|
||||
BIF_undo_push("Convert Triangles to Quads");
|
||||
}
|
||||
/* ******************** END TRIANGLE TO QUAD ************************************* */
|
||||
|
||||
#define FACE_MARKCLEAR(f) (f->f1 = 1)
|
||||
|
||||
/* quick hack, basically a copy of beauty_fill */
|
||||
void edge_flip(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *v1, *v2, *v3, *v4;
|
||||
EditEdge *eed, *nexted;
|
||||
EditFace *efa, *w;
|
||||
//void **efaar, **efaa;
|
||||
EVPTuple *efaar;
|
||||
EVPtr *efaa;
|
||||
int totedge, ok, vindex[4];
|
||||
|
||||
/* - all selected edges with two faces
|
||||
* - find the faces: store them in edges (using datablock)
|
||||
* - per edge: - test convex
|
||||
* - test edge: flip?
|
||||
- if true: remedge, addedge, all edges at the edge get new face pointers
|
||||
*/
|
||||
|
||||
EM_selectmode_flush(); // makes sure in selectmode 'face' the edges of selected faces are selected too
|
||||
|
||||
totedge = count_selected_edges(em->edges.first);
|
||||
if(totedge==0) return;
|
||||
|
||||
/* temporary array for : edge -> face[1], face[2] */
|
||||
efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "edgeflip");
|
||||
|
||||
ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
|
||||
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
nexted= eed->next;
|
||||
|
||||
if(eed->f2==2) { /* points to 2 faces */
|
||||
|
||||
efaa= (EVPtr *) eed->tmp.p;
|
||||
|
||||
/* don't do it if flagged */
|
||||
|
||||
ok= 1;
|
||||
efa= efaa[0];
|
||||
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
|
||||
efa= efaa[1];
|
||||
if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
|
||||
|
||||
if(ok) {
|
||||
/* test convex */
|
||||
givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
|
||||
|
||||
/*
|
||||
4-----3 4-----3
|
||||
|\ | | /|
|
||||
| \ 1 | | 1 / |
|
||||
| \ | -> | / |
|
||||
| 0 \ | | / 0 |
|
||||
| \| |/ |
|
||||
1-----2 1-----2
|
||||
*/
|
||||
/* make new faces */
|
||||
if (v1 && v2 && v3) {
|
||||
if( convex(v1->co, v2->co, v3->co, v4->co) ) {
|
||||
if(exist_face(v1, v2, v3, v4)==0) {
|
||||
/* outch this may break seams */
|
||||
w= EM_face_from_faces(efaa[0], efaa[1], vindex[0],
|
||||
vindex[1], 4+vindex[2], -1);
|
||||
|
||||
EM_select_face(w, 1);
|
||||
|
||||
/* outch this may break seams */
|
||||
w= EM_face_from_faces(efaa[0], efaa[1], vindex[0],
|
||||
4+vindex[2], 4+vindex[3], -1);
|
||||
|
||||
EM_select_face(w, 1);
|
||||
}
|
||||
/* tag as to-be-removed */
|
||||
FACE_MARKCLEAR(efaa[1]);
|
||||
FACE_MARKCLEAR(efaa[0]);
|
||||
eed->f1 = 1;
|
||||
|
||||
} /* endif test convex */
|
||||
}
|
||||
}
|
||||
}
|
||||
eed= nexted;
|
||||
}
|
||||
|
||||
/* clear tagged edges and faces: */
|
||||
free_tagged_edges_faces(em->edges.first, em->faces.first);
|
||||
|
||||
MEM_freeN(efaar);
|
||||
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
BIF_undo_push("Flip Triangle Edges");
|
||||
|
||||
}
|
||||
|
||||
static void edge_rotate(EditEdge *eed,int dir)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert **verts[2];
|
||||
EditFace *face[2], *efa, *newFace[2];
|
||||
EditEdge **edges[2], **hiddenedges, *srchedge;
|
||||
int facecount, p1, p2, p3, p4, fac1, fac2, i, j;
|
||||
int numhidden, numshared, p[2][4];
|
||||
|
||||
/* check to make sure that the edge is only part of 2 faces */
|
||||
facecount = 0;
|
||||
for(efa = em->faces.first;efa;efa = efa->next) {
|
||||
if((efa->e1 == eed || efa->e2 == eed) || (efa->e3 == eed || efa->e4 == eed)) {
|
||||
if(facecount >= 2) {
|
||||
/* more than two faces with this edge */
|
||||
return;
|
||||
}
|
||||
else {
|
||||
face[facecount] = efa;
|
||||
facecount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(facecount < 2)
|
||||
return;
|
||||
|
||||
/* how many edges does each face have */
|
||||
if(face[0]->e4) fac1= 4;
|
||||
else fac1= 3;
|
||||
|
||||
if(face[1]->e4) fac2= 4;
|
||||
else fac2= 3;
|
||||
|
||||
/* make a handy array for verts and edges */
|
||||
verts[0]= &face[0]->v1;
|
||||
edges[0]= &face[0]->e1;
|
||||
verts[1]= &face[1]->v1;
|
||||
edges[1]= &face[1]->e1;
|
||||
|
||||
/* we don't want to rotate edges between faces that share more than one edge */
|
||||
numshared= 0;
|
||||
for(i=0; i<fac1; i++)
|
||||
for(j=0; j<fac2; j++)
|
||||
if (edges[0][i] == edges[1][j])
|
||||
numshared++;
|
||||
|
||||
if(numshared > 1)
|
||||
return;
|
||||
|
||||
/* coplaner faces only please */
|
||||
if(dot_v3v3(face[0]->n,face[1]->n) <= 0.000001)
|
||||
return;
|
||||
|
||||
/* we want to construct an array of vertex indicis in both faces, starting at
|
||||
the last vertex of the edge being rotated.
|
||||
- first we find the two vertices that lie on the rotating edge
|
||||
- then we make sure they are ordered according to the face vertex order
|
||||
- and then we construct the array */
|
||||
p1= p2= p3= p4= 0;
|
||||
|
||||
for(i=0; i<4; i++) {
|
||||
if(eed->v1 == verts[0][i]) p1 = i;
|
||||
if(eed->v2 == verts[0][i]) p2 = i;
|
||||
if(eed->v1 == verts[1][i]) p3 = i;
|
||||
if(eed->v2 == verts[1][i]) p4 = i;
|
||||
}
|
||||
|
||||
if((p1+1)%fac1 == p2)
|
||||
SWAP(int, p1, p2);
|
||||
if((p3+1)%fac2 == p4)
|
||||
SWAP(int, p3, p4);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
p[0][i]= (p1 + i)%fac1;
|
||||
p[1][i]= (p3 + i)%fac2;
|
||||
}
|
||||
|
||||
/* create an Array of the Edges who have h set prior to rotate */
|
||||
numhidden = 0;
|
||||
for(srchedge = em->edges.first;srchedge;srchedge = srchedge->next)
|
||||
if(srchedge->h && ((srchedge->v1->f & SELECT) || (srchedge->v2->f & SELECT)))
|
||||
numhidden++;
|
||||
|
||||
hiddenedges = MEM_mallocN(sizeof(EditVert*)*numhidden+1, "RotateEdgeHiddenVerts");
|
||||
if(!hiddenedges) {
|
||||
error("Malloc Was not happy!");
|
||||
return;
|
||||
}
|
||||
|
||||
numhidden = 0;
|
||||
for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next)
|
||||
if(srchedge->h && (srchedge->v1->f & SELECT || srchedge->v2->f & SELECT))
|
||||
hiddenedges[numhidden++] = srchedge;
|
||||
|
||||
/* create the 2 new faces */
|
||||
if(fac1 == 3 && fac2 == 3) {
|
||||
/* no need of reverse setup */
|
||||
|
||||
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1);
|
||||
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1);
|
||||
}
|
||||
else if(fac1 == 4 && fac2 == 3) {
|
||||
if(dir == 1) {
|
||||
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]);
|
||||
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1);
|
||||
} else if (dir == 2) {
|
||||
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], 4+p[1][1], p[0][0], p[0][1]);
|
||||
newFace[1]= EM_face_from_faces(face[1], face[0], 4+p[0][2], p[1][0], p[1][1], -1);
|
||||
|
||||
verts[0][p[0][2]]->f |= SELECT;
|
||||
verts[1][p[1][1]]->f |= SELECT;
|
||||
}
|
||||
}
|
||||
else if(fac1 == 3 && fac2 == 4) {
|
||||
if(dir == 1) {
|
||||
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1);
|
||||
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]);
|
||||
} else if (dir == 2) {
|
||||
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][0], p[0][1], 4+p[1][2], -1);
|
||||
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], 4+p[0][2]);
|
||||
|
||||
verts[0][p[0][1]]->f |= SELECT;
|
||||
verts[1][p[1][2]]->f |= SELECT;
|
||||
}
|
||||
|
||||
}
|
||||
else if(fac1 == 4 && fac2 == 4) {
|
||||
if(dir == 1) {
|
||||
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]);
|
||||
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]);
|
||||
} else if (dir == 2) {
|
||||
newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], p[0][3], 4+p[1][1], 4+p[1][2]);
|
||||
newFace[1]= EM_face_from_faces(face[1], face[0], p[1][2], p[1][3], 4+p[0][1], 4+p[0][2]);
|
||||
|
||||
verts[0][p[0][2]]->f |= SELECT;
|
||||
verts[1][p[1][2]]->f |= SELECT;
|
||||
}
|
||||
}
|
||||
else
|
||||
return; /* This should never happen */
|
||||
|
||||
if(dir == 1 || (fac1 == 3 && fac2 == 3)) {
|
||||
verts[0][p[0][1]]->f |= SELECT;
|
||||
verts[1][p[1][1]]->f |= SELECT;
|
||||
}
|
||||
|
||||
/* copy old edge's flags to new center edge*/
|
||||
for(srchedge=em->edges.first;srchedge;srchedge=srchedge->next) {
|
||||
if((srchedge->v1->f & SELECT) && (srchedge->v2->f & SELECT)) {
|
||||
srchedge->f = eed->f;
|
||||
srchedge->h = eed->h;
|
||||
srchedge->dir = eed->dir;
|
||||
srchedge->seam = eed->seam;
|
||||
srchedge->crease = eed->crease;
|
||||
srchedge->bweight = eed->bweight;
|
||||
}
|
||||
}
|
||||
|
||||
/* resetting hidden flag */
|
||||
for(numhidden--; numhidden>=0; numhidden--)
|
||||
hiddenedges[numhidden]->h= 1;
|
||||
|
||||
/* check for orhphan edges */
|
||||
for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next)
|
||||
srchedge->f1= -1;
|
||||
|
||||
/* cleanup */
|
||||
MEM_freeN(hiddenedges);
|
||||
|
||||
/* get rid of the old edge and faces*/
|
||||
remedge(eed);
|
||||
free_editedge(eed);
|
||||
BLI_remlink(&em->faces, face[0]);
|
||||
free_editface(face[0]);
|
||||
BLI_remlink(&em->faces, face[1]);
|
||||
free_editface(face[1]);
|
||||
}
|
||||
|
||||
/* only accepts 1 selected edge, or 2 selected faces */
|
||||
void edge_rotate_selected(int dir)
|
||||
{
|
||||
EditEdge *eed;
|
||||
EditFace *efa;
|
||||
short edgeCount = 0;
|
||||
|
||||
/*clear new flag for new edges, count selected edges */
|
||||
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
|
||||
eed->f1= 0;
|
||||
eed->f2 &= ~2;
|
||||
if(eed->f & SELECT) edgeCount++;
|
||||
}
|
||||
|
||||
if(edgeCount>1) {
|
||||
/* more selected edges, check faces */
|
||||
for(efa= G.editMesh->faces.first; efa; efa= efa->next) {
|
||||
if(efa->f & SELECT) {
|
||||
efa->e1->f1++;
|
||||
efa->e2->f1++;
|
||||
efa->e3->f1++;
|
||||
if(efa->e4) efa->e4->f1++;
|
||||
}
|
||||
}
|
||||
edgeCount= 0;
|
||||
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
|
||||
if(eed->f1==2) edgeCount++;
|
||||
}
|
||||
if(edgeCount==1) {
|
||||
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
|
||||
if(eed->f1==2) {
|
||||
edge_rotate(eed,dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else error("Select one edge or two adjacent faces");
|
||||
}
|
||||
else if(edgeCount==1) {
|
||||
for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
|
||||
if(eed->f & SELECT) {
|
||||
EM_select_edge(eed, 0);
|
||||
edge_rotate(eed,dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else error("Select one edge or two adjacent faces");
|
||||
|
||||
|
||||
/* flush selected vertices (again) to edges/faces */
|
||||
EM_select_flush();
|
||||
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode)
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
#endif
|
||||
|
||||
BIF_undo_push("Rotate Edge");
|
||||
}
|
||||
|
||||
/******************* BEVEL CODE STARTS HERE ********************/
|
||||
|
||||
static void bevel_displace_vec(float *midvec, float *v1, float *v2, float *v3, float d, float no[3])
|
||||
{
|
||||
float a[3], c[3], n_a[3], n_c[3], mid[3], ac, ac2, fac;
|
||||
|
||||
sub_v3_v3v3(a, v1, v2);
|
||||
sub_v3_v3v3(c, v3, v2);
|
||||
|
||||
cross_v3_v3v3(n_a, a, no);
|
||||
normalize_v3(n_a);
|
||||
cross_v3_v3v3(n_c, no, c);
|
||||
normalize_v3(n_c);
|
||||
|
||||
normalize_v3(a);
|
||||
normalize_v3(c);
|
||||
ac = dot_v3v3(a, c);
|
||||
|
||||
if (ac == 1 || ac == -1) {
|
||||
midvec[0] = midvec[1] = midvec[2] = 0;
|
||||
return;
|
||||
}
|
||||
ac2 = ac * ac;
|
||||
fac = (float)sqrt((ac2 + 2*ac + 1)/(1 - ac2) + 1);
|
||||
add_v3_v3v3(mid, n_c, n_a);
|
||||
normalize_v3(mid);
|
||||
mul_v3_fl(mid, d * fac);
|
||||
add_v3_v3v3(mid, mid, v2);
|
||||
copy_v3_v3(midvec, mid);
|
||||
}
|
||||
|
||||
/* Finds the new point using the sinus law to extrapolate a triangle
|
||||
Lots of sqrts which would not be good for a real time algo
|
||||
Using the mid point of the extrapolation of both sides
|
||||
Useless for coplanar quads, but that doesn't happen too often */
|
||||
static void fix_bevel_wrap(float *midvec, float *v1, float *v2, float *v3, float *v4, float d, float no[3])
|
||||
{
|
||||
float a[3], b[3], c[3], l_a, l_b, l_c, s_a, s_b, s_c, Pos1[3], Pos2[3], Dir[3];
|
||||
|
||||
sub_v3_v3v3(a, v3, v2);
|
||||
l_a = normalize_v3(a);
|
||||
sub_v3_v3v3(b, v4, v3);
|
||||
normalize_v3(b);
|
||||
sub_v3_v3v3(c, v1, v2);
|
||||
normalize_v3(c);
|
||||
|
||||
s_b = dot_v3v3(a, c);
|
||||
s_b = (float)sqrt(1 - (s_b * s_b));
|
||||
s_a = dot_v3v3(b, c);
|
||||
s_a = (float)sqrt(1 - (s_a * s_a));
|
||||
mul_v3_fl(a, -1);
|
||||
s_c = dot_v3v3(a, b);
|
||||
s_c = (float)sqrt(1 - (s_c * s_c));
|
||||
|
||||
l_b = s_b * l_a / s_a;
|
||||
l_c = s_c * l_a / s_a;
|
||||
|
||||
mul_v3_fl(b, l_b);
|
||||
mul_v3_fl(c, l_c);
|
||||
|
||||
add_v3_v3v3(Pos1, v2, c);
|
||||
add_v3_v3v3(Pos2, v3, b);
|
||||
|
||||
add_v3_v3v3(Dir, Pos1, Pos2);
|
||||
mul_v3_fl(Dir, 0.5);
|
||||
|
||||
bevel_displace_vec(midvec, v3, Dir, v2, d, no);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static char detect_wrap(float *o_v1, float *o_v2, float *v1, float *v2, float *no)
|
||||
{
|
||||
float o_a[3], a[3], o_c[3], c[3];
|
||||
|
||||
sub_v3_v3v3(o_a, o_v1, o_v2);
|
||||
sub_v3_v3v3(a, v1, v2);
|
||||
|
||||
cross_v3_v3v3(o_c, o_a, no);
|
||||
cross_v3_v3v3(c, a, no);
|
||||
|
||||
if (dot_v3v3(c, o_c) <= 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Detects and fix a quad wrapping after the resize
|
||||
// Arguments are the orginal verts followed by the final verts and then the bevel size and the normal
|
||||
static void fix_bevel_quad_wrap(float *o_v1, float *o_v2, float *o_v3, float *o_v4, float *v1, float *v2, float *v3, float *v4, float d, float *no)
|
||||
{
|
||||
float vec[3];
|
||||
char wrap[4];
|
||||
|
||||
// Quads can wrap partially. Watch out
|
||||
wrap[0] = detect_wrap(o_v1, o_v2, v1, v2, no); // Edge 1-2
|
||||
wrap[1] = detect_wrap(o_v2, o_v3, v2, v3, no); // Edge 2-3
|
||||
wrap[2] = detect_wrap(o_v3, o_v4, v3, v4, no); // Edge 3-4
|
||||
wrap[3] = detect_wrap(o_v4, o_v1, v4, v1, no); // Edge 4-1
|
||||
|
||||
// Edge 1 inverted
|
||||
if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 0) {
|
||||
fix_bevel_wrap(vec, o_v2, o_v3, o_v4, o_v1, d, no);
|
||||
copy_v3_v3(v1, vec);
|
||||
copy_v3_v3(v2, vec);
|
||||
}
|
||||
// Edge 2 inverted
|
||||
else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 0) {
|
||||
fix_bevel_wrap(vec, o_v3, o_v4, o_v1, o_v2, d, no);
|
||||
copy_v3_v3(v2, vec);
|
||||
copy_v3_v3(v3, vec);
|
||||
}
|
||||
// Edge 3 inverted
|
||||
else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) {
|
||||
fix_bevel_wrap(vec, o_v4, o_v1, o_v2, o_v3, d, no);
|
||||
copy_v3_v3(v3, vec);
|
||||
copy_v3_v3(v4, vec);
|
||||
}
|
||||
// Edge 4 inverted
|
||||
else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 1) {
|
||||
fix_bevel_wrap(vec, o_v1, o_v2, o_v3, o_v4, d, no);
|
||||
copy_v3_v3(v4, vec);
|
||||
copy_v3_v3(v1, vec);
|
||||
}
|
||||
// Edge 2 and 4 inverted
|
||||
else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 1) {
|
||||
add_v3_v3v3(vec, v2, v3);
|
||||
mul_v3_fl(vec, 0.5);
|
||||
copy_v3_v3(v2, vec);
|
||||
copy_v3_v3(v3, vec);
|
||||
add_v3_v3v3(vec, v1, v4);
|
||||
mul_v3_fl(vec, 0.5);
|
||||
copy_v3_v3(v1, vec);
|
||||
copy_v3_v3(v4, vec);
|
||||
}
|
||||
// Edge 1 and 3 inverted
|
||||
else if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) {
|
||||
add_v3_v3v3(vec, v1, v2);
|
||||
mul_v3_fl(vec, 0.5);
|
||||
copy_v3_v3(v1, vec);
|
||||
copy_v3_v3(v2, vec);
|
||||
add_v3_v3v3(vec, v3, v4);
|
||||
mul_v3_fl(vec, 0.5);
|
||||
copy_v3_v3(v3, vec);
|
||||
copy_v3_v3(v4, vec);
|
||||
}
|
||||
// Totally inverted
|
||||
else if (wrap[0] == 1 && wrap[1] == 1 && wrap[2] == 1 && wrap[3] == 1) {
|
||||
add_v3_v3v3(vec, v1, v2);
|
||||
add_v3_v3v3(vec, vec, v3);
|
||||
add_v3_v3v3(vec, vec, v4);
|
||||
mul_v3_fl(vec, 0.25);
|
||||
copy_v3_v3(v1, vec);
|
||||
copy_v3_v3(v2, vec);
|
||||
copy_v3_v3(v3, vec);
|
||||
copy_v3_v3(v4, vec);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Detects and fix a tri wrapping after the resize
|
||||
// Arguments are the orginal verts followed by the final verts and the normal
|
||||
// Triangles cannot wrap partially (not in this situation
|
||||
static void fix_bevel_tri_wrap(float *o_v1, float *o_v2, float *o_v3, float *v1, float *v2, float *v3, float *no)
|
||||
{
|
||||
if (detect_wrap(o_v1, o_v2, v1, v2, no)) {
|
||||
float vec[3];
|
||||
add_v3_v3v3(vec, o_v1, o_v2);
|
||||
add_v3_v3v3(vec, vec, o_v3);
|
||||
mul_v3_fl(vec, 1.0f/3.0f);
|
||||
copy_v3_v3(v1, vec);
|
||||
copy_v3_v3(v2, vec);
|
||||
copy_v3_v3(v3, vec);
|
||||
}
|
||||
}
|
||||
|
||||
static void bevel_shrink_faces(float d, int flag)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
float vec[3], no[3], v1[3], v2[3], v3[3], v4[3];
|
||||
|
||||
/* move edges of all faces with efa->f1 & flag closer towards their centers */
|
||||
efa= em->faces.first;
|
||||
while (efa) {
|
||||
if (efa->f1 & flag) {
|
||||
copy_v3_v3(v1, efa->v1->co);
|
||||
copy_v3_v3(v2, efa->v2->co);
|
||||
copy_v3_v3(v3, efa->v3->co);
|
||||
copy_v3_v3(no, efa->n);
|
||||
if (efa->v4 == NULL) {
|
||||
bevel_displace_vec(vec, v1, v2, v3, d, no);
|
||||
copy_v3_v3(efa->v2->co, vec);
|
||||
bevel_displace_vec(vec, v2, v3, v1, d, no);
|
||||
copy_v3_v3(efa->v3->co, vec);
|
||||
bevel_displace_vec(vec, v3, v1, v2, d, no);
|
||||
copy_v3_v3(efa->v1->co, vec);
|
||||
|
||||
fix_bevel_tri_wrap(v1, v2, v3, efa->v1->co, efa->v2->co, efa->v3->co, no);
|
||||
} else {
|
||||
copy_v3_v3(v4, efa->v4->co);
|
||||
bevel_displace_vec(vec, v1, v2, v3, d, no);
|
||||
copy_v3_v3(efa->v2->co, vec);
|
||||
bevel_displace_vec(vec, v2, v3, v4, d, no);
|
||||
copy_v3_v3(efa->v3->co, vec);
|
||||
bevel_displace_vec(vec, v3, v4, v1, d, no);
|
||||
copy_v3_v3(efa->v4->co, vec);
|
||||
bevel_displace_vec(vec, v4, v1, v2, d, no);
|
||||
copy_v3_v3(efa->v1->co, vec);
|
||||
|
||||
fix_bevel_quad_wrap(v1, v2, v3, v4, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, d, no);
|
||||
}
|
||||
}
|
||||
efa= efa->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void bevel_shrink_draw(float d, int flag)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
float vec[3], no[3], v1[3], v2[3], v3[3], v4[3], fv1[3], fv2[3], fv3[3], fv4[3];
|
||||
|
||||
/* move edges of all faces with efa->f1 & flag closer towards their centers */
|
||||
efa= em->faces.first;
|
||||
while (efa) {
|
||||
copy_v3_v3(v1, efa->v1->co);
|
||||
copy_v3_v3(v2, efa->v2->co);
|
||||
copy_v3_v3(v3, efa->v3->co);
|
||||
copy_v3_v3(no, efa->n);
|
||||
if (efa->v4 == NULL) {
|
||||
bevel_displace_vec(vec, v1, v2, v3, d, no);
|
||||
copy_v3_v3(fv2, vec);
|
||||
bevel_displace_vec(vec, v2, v3, v1, d, no);
|
||||
copy_v3_v3(fv3, vec);
|
||||
bevel_displace_vec(vec, v3, v1, v2, d, no);
|
||||
copy_v3_v3(fv1, vec);
|
||||
|
||||
fix_bevel_tri_wrap(v1, v2, v3, fv1, fv2, fv3, no);
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(fv1);
|
||||
glVertex3fv(fv2);
|
||||
glEnd();
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(fv2);
|
||||
glVertex3fv(fv3);
|
||||
glEnd();
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(fv1);
|
||||
glVertex3fv(fv3);
|
||||
glEnd();
|
||||
} else {
|
||||
copy_v3_v3(v4, efa->v4->co);
|
||||
bevel_displace_vec(vec, v4, v1, v2, d, no);
|
||||
copy_v3_v3(fv1, vec);
|
||||
bevel_displace_vec(vec, v1, v2, v3, d, no);
|
||||
copy_v3_v3(fv2, vec);
|
||||
bevel_displace_vec(vec, v2, v3, v4, d, no);
|
||||
copy_v3_v3(fv3, vec);
|
||||
bevel_displace_vec(vec, v3, v4, v1, d, no);
|
||||
copy_v3_v3(fv4, vec);
|
||||
|
||||
fix_bevel_quad_wrap(v1, v2, v3, v4, fv1, fv2, fv3, fv4, d, no);
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(fv1);
|
||||
glVertex3fv(fv2);
|
||||
glEnd();
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(fv2);
|
||||
glVertex3fv(fv3);
|
||||
glEnd();
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(fv3);
|
||||
glVertex3fv(fv4);
|
||||
glEnd();
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(fv1);
|
||||
glVertex3fv(fv4);
|
||||
glEnd();
|
||||
}
|
||||
efa= efa->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void bevel_mesh(float bsize, int allfaces)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
//#define BEV_DEBUG
|
||||
/* Enables debug printfs and assigns material indices: */
|
||||
/* 2 = edge quad */
|
||||
/* 3 = fill polygon (vertex clusters) */
|
||||
|
||||
EditFace *efa, *example; //, *nextvl;
|
||||
EditEdge *eed, *eed2;
|
||||
EditVert *neweve[1024], *eve, *eve2, *eve3, *v1, *v2, *v3, *v4; //, *eve4;
|
||||
//short found4, search;
|
||||
//float f1, f2, f3, f4;
|
||||
float cent[3], min[3], max[3];
|
||||
int a, b, c;
|
||||
float limit= 0.001f;
|
||||
|
||||
if(multires_test()) return;
|
||||
|
||||
waitcursor(1);
|
||||
|
||||
removedoublesflag(1, 0, limit);
|
||||
|
||||
/* tag all original faces */
|
||||
efa= em->faces.first;
|
||||
while (efa) {
|
||||
efa->f1= 0;
|
||||
if (faceselectedAND(efa, 1)||allfaces) {
|
||||
efa->f1= 1;
|
||||
efa->v1->f |= 128;
|
||||
efa->v2->f |= 128;
|
||||
efa->v3->f |= 128;
|
||||
if (efa->v4) efa->v4->f |= 128;
|
||||
}
|
||||
efa->v1->f &= ~64;
|
||||
efa->v2->f &= ~64;
|
||||
efa->v3->f &= ~64;
|
||||
if (efa->v4) efa->v4->f &= ~64;
|
||||
|
||||
efa= efa->next;
|
||||
}
|
||||
|
||||
#ifdef BEV_DEBUG
|
||||
fprintf(stderr,"bevel_mesh: split\n");
|
||||
#endif
|
||||
|
||||
efa= em->faces.first;
|
||||
while (efa) {
|
||||
if (efa->f1 & 1) {
|
||||
efa->f1-= 1;
|
||||
v1= addvertlist(efa->v1->co, efa->v1);
|
||||
v1->f= efa->v1->f & ~128;
|
||||
efa->v1->tmp.v = v1;
|
||||
|
||||
v1= addvertlist(efa->v2->co, efa->v2);
|
||||
v1->f= efa->v2->f & ~128;
|
||||
efa->v2->tmp.v = v1;
|
||||
|
||||
v1= addvertlist(efa->v3->co, efa->v3);
|
||||
v1->f= efa->v3->f & ~128;
|
||||
efa->v3->tmp.v = v1;
|
||||
|
||||
if (efa->v4) {
|
||||
v1= addvertlist(efa->v4->co, efa->v4);
|
||||
v1->f= efa->v4->f & ~128;
|
||||
efa->v4->tmp.v = v1;
|
||||
}
|
||||
|
||||
/* Needs better adaption of creases? */
|
||||
addedgelist(efa->e1->v1->tmp.v,
|
||||
efa->e1->v2->tmp.v,
|
||||
efa->e1);
|
||||
addedgelist(efa->e2->v1->tmp.v,
|
||||
efa->e2->v2->tmp.v,
|
||||
efa->e2);
|
||||
addedgelist(efa->e3->v1->tmp.v,
|
||||
efa->e3->v2->tmp.v,
|
||||
efa->e3);
|
||||
if (efa->e4) addedgelist(efa->e4->v1->tmp.v,
|
||||
efa->e4->v2->tmp.v,
|
||||
efa->e4);
|
||||
|
||||
if(efa->v4) {
|
||||
v1 = efa->v1->tmp.v;
|
||||
v2 = efa->v2->tmp.v;
|
||||
v3 = efa->v3->tmp.v;
|
||||
v4 = efa->v4->tmp.v;
|
||||
addfacelist(v1, v2, v3, v4, efa,NULL);
|
||||
} else {
|
||||
v1= efa->v1->tmp.v;
|
||||
v2= efa->v2->tmp.v;
|
||||
v3= efa->v3->tmp.v;
|
||||
addfacelist(v1, v2, v3, 0, efa,NULL);
|
||||
}
|
||||
|
||||
efa= efa-> next;
|
||||
} else {
|
||||
efa= efa->next;
|
||||
}
|
||||
}
|
||||
|
||||
for(efa= em->faces.first; efa; efa= efa->next) {
|
||||
if( (efa->v1->f & 128) && (efa->v2->f & 128) && (efa->v3->f & 128) ) {
|
||||
if(efa->v4==NULL || (efa->v4->f & 128)) efa->f |= 128;
|
||||
}
|
||||
}
|
||||
|
||||
delfaceflag(128); // works with face flag now
|
||||
|
||||
/* tag all faces for shrink*/
|
||||
efa= em->faces.first;
|
||||
while (efa) {
|
||||
if (faceselectedAND(efa, 1)||allfaces) {
|
||||
efa->f1= 2;
|
||||
}
|
||||
efa= efa->next;
|
||||
}
|
||||
|
||||
#ifdef BEV_DEBUG
|
||||
fprintf(stderr,"bevel_mesh: make edge quads\n");
|
||||
#endif
|
||||
|
||||
/* find edges that are on each other and make quads between them */
|
||||
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
eed->f2= eed->f1= 0;
|
||||
if ( ((eed->v1->f & eed->v2->f) & 1) || allfaces)
|
||||
eed->f1 |= 4; /* original edges */
|
||||
eed->tmp.v = 0;
|
||||
eed= eed->next;
|
||||
}
|
||||
|
||||
eed= em->edges.first;
|
||||
while (eed) {
|
||||
if ( ((eed->f1 & 2)==0) && (eed->f1 & 4) ) {
|
||||
eed2= em->edges.first;
|
||||
while (eed2) {
|
||||
if ( (eed2 != eed) && ((eed2->f1 & 2)==0) && (eed->f1 & 4) ) {
|
||||
if (
|
||||
(eed->v1 != eed2->v1) &&
|
||||
(eed->v1 != eed2->v2) &&
|
||||
(eed->v2 != eed2->v1) &&
|
||||
(eed->v2 != eed2->v2) && (
|
||||
( compare_v3v3(eed->v1->co, eed2->v1->co, limit) &&
|
||||
compare_v3v3(eed->v2->co, eed2->v2->co, limit) ) ||
|
||||
( compare_v3v3(eed->v1->co, eed2->v2->co, limit) &&
|
||||
compare_v3v3(eed->v2->co, eed2->v1->co, limit) ) ) )
|
||||
{
|
||||
|
||||
#ifdef BEV_DEBUG
|
||||
fprintf(stderr, "bevel_mesh: edge quad\n");
|
||||
#endif
|
||||
|
||||
eed->f1 |= 2; /* these edges are finished */
|
||||
eed2->f1 |= 2;
|
||||
|
||||
example= NULL;
|
||||
efa= em->faces.first; /* search example face (for mat_nr, ME_SMOOTH, ...) */
|
||||
while (efa) {
|
||||
if ( (efa->e1 == eed) ||
|
||||
(efa->e2 == eed) ||
|
||||
(efa->e3 == eed) ||
|
||||
(efa->e4 && (efa->e4 == eed)) ) {
|
||||
example= efa;
|
||||
efa= NULL;
|
||||
}
|
||||
if (efa) efa= efa->next;
|
||||
}
|
||||
|
||||
neweve[0]= eed->v1; neweve[1]= eed->v2;
|
||||
neweve[2]= eed2->v1; neweve[3]= eed2->v2;
|
||||
|
||||
if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) {
|
||||
efa= NULL;
|
||||
|
||||
if (compare_v3v3(eed->v1->co, eed2->v2->co, limit)) {
|
||||
efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], example,NULL);
|
||||
} else {
|
||||
efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], example,NULL);
|
||||
}
|
||||
|
||||
if(efa) {
|
||||
float inp;
|
||||
normal_tri_v3( efa->n,efa->v1->co, efa->v2->co, efa->v3->co);
|
||||
inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2];
|
||||
if(inp < 0.0) flipface(efa);
|
||||
#ifdef BEV_DEBUG
|
||||
efa->mat_nr= 1;
|
||||
#endif
|
||||
} else fprintf(stderr,"bevel_mesh: error creating face\n");
|
||||
}
|
||||
eed2= NULL;
|
||||
}
|
||||
}
|
||||
if (eed2) eed2= eed2->next;
|
||||
}
|
||||
}
|
||||
eed= eed->next;
|
||||
}
|
||||
|
||||
eed= em->edges.first;
|
||||
while(eed) {
|
||||
eed->f2= eed->f1= 0;
|
||||
eed->f1= 0;
|
||||
eed->v1->f1 &= ~1;
|
||||
eed->v2->f1 &= ~1;
|
||||
eed->tmp.v = 0;
|
||||
eed= eed->next;
|
||||
}
|
||||
|
||||
#ifdef BEV_DEBUG
|
||||
fprintf(stderr,"bevel_mesh: find clusters\n");
|
||||
#endif
|
||||
|
||||
/* Look for vertex clusters */
|
||||
|
||||
eve= em->verts.first;
|
||||
while (eve) {
|
||||
eve->f &= ~(64|128);
|
||||
eve->tmp.v = NULL;
|
||||
eve= eve->next;
|
||||
}
|
||||
|
||||
/* eve->f: 128: first vertex in a list (->tmp.v) */
|
||||
/* 64: vertex is in a list */
|
||||
|
||||
eve= em->verts.first;
|
||||
while (eve) {
|
||||
eve2= em->verts.first;
|
||||
eve3= NULL;
|
||||
while (eve2) {
|
||||
if ((eve2 != eve) && ((eve2->f & (64|128))==0)) {
|
||||
if (compare_v3v3(eve->co, eve2->co, limit)) {
|
||||
if ((eve->f & (128|64)) == 0) {
|
||||
/* fprintf(stderr,"Found vertex cluster:\n *\n *\n"); */
|
||||
eve->f |= 128;
|
||||
eve->tmp.v = eve2;
|
||||
eve3= eve2;
|
||||
} else if ((eve->f & 64) == 0) {
|
||||
/* fprintf(stderr," *\n"); */
|
||||
if (eve3) eve3->tmp.v = eve2;
|
||||
eve2->f |= 64;
|
||||
eve3= eve2;
|
||||
}
|
||||
}
|
||||
}
|
||||
eve2= eve2->next;
|
||||
if (!eve2) {
|
||||
if (eve3) eve3->tmp.v = NULL;
|
||||
}
|
||||
}
|
||||
eve= eve->next;
|
||||
}
|
||||
|
||||
#ifdef BEV_DEBUG
|
||||
fprintf(stderr,"bevel_mesh: shrink faces\n");
|
||||
#endif
|
||||
|
||||
bevel_shrink_faces(bsize, 2);
|
||||
|
||||
#ifdef BEV_DEBUG
|
||||
fprintf(stderr,"bevel_mesh: fill clusters\n");
|
||||
#endif
|
||||
|
||||
/* Make former vertex clusters faces */
|
||||
|
||||
eve= em->verts.first;
|
||||
while (eve) {
|
||||
eve->f &= ~64;
|
||||
eve= eve->next;
|
||||
}
|
||||
|
||||
eve= em->verts.first;
|
||||
while (eve) {
|
||||
if (eve->f & 128) {
|
||||
eve->f &= ~128;
|
||||
a= 0;
|
||||
neweve[a]= eve;
|
||||
eve2 = eve->tmp.v;
|
||||
while (eve2) {
|
||||
a++;
|
||||
neweve[a]= eve2;
|
||||
eve2 = eve2->tmp.v;
|
||||
}
|
||||
a++;
|
||||
efa= NULL;
|
||||
if (a>=3) {
|
||||
example= NULL;
|
||||
efa= em->faces.first; /* search example face */
|
||||
while (efa) {
|
||||
if ( (efa->v1 == neweve[0]) ||
|
||||
(efa->v2 == neweve[0]) ||
|
||||
(efa->v3 == neweve[0]) ||
|
||||
(efa->v4 && (efa->v4 == neweve[0])) ) {
|
||||
example= efa;
|
||||
efa= NULL;
|
||||
}
|
||||
if (efa) efa= efa->next;
|
||||
}
|
||||
#ifdef BEV_DEBUG
|
||||
fprintf(stderr,"bevel_mesh: Making %d-gon\n", a);
|
||||
#endif
|
||||
if (a>4) {
|
||||
cent[0]= cent[1]= cent[2]= 0.0;
|
||||
INIT_MINMAX(min, max);
|
||||
for (b=0; b<a; b++) {
|
||||
add_v3_v3v3(cent, cent, neweve[b]->co);
|
||||
DO_MINMAX(neweve[b]->co, min, max);
|
||||
}
|
||||
cent[0]= (min[0]+max[0])/2;
|
||||
cent[1]= (min[1]+max[1])/2;
|
||||
cent[2]= (min[2]+max[2])/2;
|
||||
eve2= addvertlist(cent, NULL);
|
||||
eve2->f |= 1;
|
||||
eed= em->edges.first;
|
||||
while (eed) {
|
||||
c= 0;
|
||||
for (b=0; b<a; b++)
|
||||
if ((neweve[b]==eed->v1) || (neweve[b]==eed->v2)) c++;
|
||||
if (c==2) {
|
||||
if(exist_face(eed->v1, eed->v2, eve2, 0)==0) {
|
||||
efa= addfacelist(eed->v1, eed->v2, eve2, 0, example,NULL);
|
||||
#ifdef BEV_DEBUG
|
||||
efa->mat_nr= 2;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
eed= eed->next;
|
||||
}
|
||||
} else if (a==4) {
|
||||
if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) {
|
||||
/* the order of vertices can be anything, three cases to check */
|
||||
if( convex(neweve[0]->co, neweve[1]->co, neweve[2]->co, neweve[3]->co) ) {
|
||||
efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], NULL, NULL);
|
||||
}
|
||||
else if( convex(neweve[0]->co, neweve[2]->co, neweve[3]->co, neweve[1]->co) ) {
|
||||
efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], NULL, NULL);
|
||||
}
|
||||
else if( convex(neweve[0]->co, neweve[2]->co, neweve[1]->co, neweve[3]->co) ) {
|
||||
efa= addfacelist(neweve[0], neweve[2], neweve[1], neweve[3], NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a==3) {
|
||||
if(exist_face(neweve[0], neweve[1], neweve[2], 0)==0)
|
||||
efa= addfacelist(neweve[0], neweve[1], neweve[2], 0, example, NULL);
|
||||
}
|
||||
if(efa) {
|
||||
float inp;
|
||||
normal_tri_v3( efa->n,neweve[0]->co, neweve[1]->co, neweve[2]->co);
|
||||
inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2];
|
||||
if(inp < 0.0) flipface(efa);
|
||||
#ifdef BEV_DEBUG
|
||||
efa->mat_nr= 2;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
eve= eve->next;
|
||||
}
|
||||
|
||||
eve= em->verts.first;
|
||||
while (eve) {
|
||||
eve->f1= 0;
|
||||
eve->f &= ~(128|64);
|
||||
eve->tmp.v= NULL;
|
||||
eve= eve->next;
|
||||
}
|
||||
|
||||
recalc_editnormals();
|
||||
waitcursor(0);
|
||||
countall();
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
|
||||
removedoublesflag(1, 0, limit);
|
||||
|
||||
/* flush selected vertices to edges/faces */
|
||||
EM_select_flush();
|
||||
|
||||
#undef BEV_DEBUG
|
||||
}
|
||||
|
||||
static void bevel_mesh_recurs(float bsize, short recurs, int allfaces)
|
||||
{
|
||||
float d;
|
||||
short nr;
|
||||
|
||||
d= bsize;
|
||||
for (nr=0; nr<recurs; nr++) {
|
||||
bevel_mesh(d, allfaces);
|
||||
if (nr==0) d /= 3; else d /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void bevel_menu(void)
|
||||
{
|
||||
BME_Mesh *bm;
|
||||
BME_TransData_Head *td;
|
||||
TransInfo *t;
|
||||
int options, res, gbm_free = 0;
|
||||
|
||||
t = BIF_GetTransInfo();
|
||||
if (!G.editBMesh) {
|
||||
G.editBMesh = MEM_callocN(sizeof(*(G.editBMesh)),"bevel_menu() G.editBMesh");
|
||||
gbm_free = 1;
|
||||
}
|
||||
|
||||
G.editBMesh->options = BME_BEVEL_RUNNING | BME_BEVEL_SELECT;
|
||||
G.editBMesh->res = 1;
|
||||
|
||||
while(G.editBMesh->options & BME_BEVEL_RUNNING) {
|
||||
options = G.editBMesh->options;
|
||||
res = G.editBMesh->res;
|
||||
bm = BME_editmesh_to_bmesh(G.editMesh);
|
||||
BIF_undo_push("Pre-Bevel");
|
||||
free_editMesh(G.editMesh);
|
||||
BME_bevel(bm,0.1f,res,options,0,0,&td);
|
||||
BME_bmesh_to_editmesh(bm, td);
|
||||
EM_selectmode_flush();
|
||||
G.editBMesh->bm = bm;
|
||||
G.editBMesh->td = td;
|
||||
initTransform(TFM_BEVEL,CTX_BMESH);
|
||||
Transform();
|
||||
BME_free_transdata(td);
|
||||
BME_free_mesh(bm);
|
||||
if (t->state != TRANS_CONFIRM) {
|
||||
BIF_undo();
|
||||
}
|
||||
if (options == G.editBMesh->options) {
|
||||
G.editBMesh->options &= ~BME_BEVEL_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
if (gbm_free) {
|
||||
MEM_freeN(G.editBMesh);
|
||||
G.editBMesh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bevel_menu_old()
|
||||
{
|
||||
char Finished = 0, Canceled = 0, str[100], Recalc = 0;
|
||||
short mval[2], oval[2], curval[2], event = 0, recurs = 1, nr;
|
||||
float vec[3], d, drawd=0.0, center[3], fac = 1;
|
||||
|
||||
getmouseco_areawin(mval);
|
||||
oval[0] = mval[0]; oval[1] = mval[1];
|
||||
|
||||
// Silly hackish code to initialise the variable (warning if not done)
|
||||
// while still drawing in the first iteration (and without using another variable)
|
||||
curval[0] = mval[0] + 1; curval[1] = mval[1] + 1;
|
||||
|
||||
// Init grabz for window to vec conversions
|
||||
initgrabz(-G.vd->ofs[0], -G.vd->ofs[1], -G.vd->ofs[2]);
|
||||
window_to_3d(center, mval[0], mval[1]);
|
||||
|
||||
if(button(&recurs, 1, 4, "Recursion:")==0) return;
|
||||
|
||||
for (nr=0; nr<recurs-1; nr++) {
|
||||
if (nr==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * nr * 2.0f);
|
||||
}
|
||||
|
||||
EM_set_flag_all(SELECT);
|
||||
|
||||
SetBlenderCursor(SYSCURSOR);
|
||||
|
||||
while (Finished == 0)
|
||||
{
|
||||
getmouseco_areawin(mval);
|
||||
if (mval[0] != curval[0] || mval[1] != curval[1] || (Recalc == 1))
|
||||
{
|
||||
Recalc = 0;
|
||||
curval[0] = mval[0];
|
||||
curval[1] = mval[1];
|
||||
|
||||
window_to_3d(vec, mval[0]-oval[0], mval[1]-oval[1]);
|
||||
d = normalize_v3(vec) / 10;
|
||||
|
||||
|
||||
drawd = d * fac;
|
||||
if (G.qual & LR_CTRLKEY)
|
||||
drawd = (float) floor(drawd * 10.0f)/10.0f;
|
||||
if (G.qual & LR_SHIFTKEY)
|
||||
drawd /= 10;
|
||||
|
||||
/*------------- Preview lines--------------- */
|
||||
|
||||
/* uses callback mechanism to draw it all in current area */
|
||||
scrarea_do_windraw(curarea);
|
||||
|
||||
/* set window matrix to perspective, default an area returns with buttons transform */
|
||||
persp(PERSP_VIEW);
|
||||
/* make a copy, for safety */
|
||||
glPushMatrix();
|
||||
/* multiply with the object transformation */
|
||||
mymultmatrix(G.obedit->obmat);
|
||||
|
||||
glColor3ub(255, 255, 0);
|
||||
|
||||
// PREVIEW CODE GOES HERE
|
||||
bevel_shrink_draw(drawd, 2);
|
||||
|
||||
/* restore matrix transform */
|
||||
glPopMatrix();
|
||||
|
||||
sprintf(str, "Bevel Size: %.4f LMB to confirm, RMB to cancel, SPACE to input directly.", drawd);
|
||||
headerprint(str);
|
||||
|
||||
/* this also verifies other area/windows for clean swap */
|
||||
screen_swapbuffers();
|
||||
|
||||
persp(PERSP_WIN);
|
||||
|
||||
glDrawBuffer(GL_FRONT);
|
||||
|
||||
BIF_ThemeColor(TH_WIRE);
|
||||
|
||||
setlinestyle(3);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
glVertex2sv(mval);
|
||||
glVertex2sv(oval);
|
||||
glEnd();
|
||||
setlinestyle(0);
|
||||
|
||||
persp(PERSP_VIEW);
|
||||
bglFlush(); // flush display for frontbuffer
|
||||
glDrawBuffer(GL_BACK);
|
||||
}
|
||||
while(qtest()) {
|
||||
short val=0;
|
||||
event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle
|
||||
|
||||
/* val==0 on key-release event */
|
||||
if(val && (event==ESCKEY || event==RIGHTMOUSE || event==LEFTMOUSE || event==RETKEY || event==ESCKEY)) {
|
||||
if (event==RIGHTMOUSE || event==ESCKEY)
|
||||
Canceled = 1;
|
||||
Finished = 1;
|
||||
}
|
||||
else if (val && event==SPACEKEY) {
|
||||
if (fbutton(&d, 0.000, 10.000, 10, 0, "Width:")!=0) {
|
||||
drawd = d * fac;
|
||||
Finished = 1;
|
||||
}
|
||||
}
|
||||
else if (val) {
|
||||
/* On any other keyboard event, recalc */
|
||||
Recalc = 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (Canceled==0) {
|
||||
SetBlenderCursor(BC_WAITCURSOR);
|
||||
bevel_mesh_recurs(drawd/fac, recurs, 1);
|
||||
righthandfaces(1);
|
||||
SetBlenderCursor(SYSCURSOR);
|
||||
BIF_undo_push("Bevel");
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------- More tools ------------------ */
|
||||
|
||||
void mesh_set_face_flags(short mode)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
MTFace *tface;
|
||||
short m_tex=0, m_tiles=0, m_shared=0,
|
||||
m_light=0, m_invis=0, m_collision=0,
|
||||
m_twoside=0, m_obcolor=0, m_halo=0,
|
||||
m_billboard=0, m_shadow=0, m_text=0,
|
||||
m_sort=0;
|
||||
short flag = 0, change = 0;
|
||||
|
||||
if (!EM_texFaceCheck()) {
|
||||
error("not a mesh with uv/image layers");
|
||||
return;
|
||||
}
|
||||
|
||||
add_numbut(0, TOG|SHO, "Texture", 0, 0, &m_tex, NULL);
|
||||
add_numbut(1, TOG|SHO, "Tiles", 0, 0, &m_tiles, NULL);
|
||||
add_numbut(2, TOG|SHO, "Light", 0, 0, &m_light, NULL);
|
||||
add_numbut(3, TOG|SHO, "Invisible", 0, 0, &m_invis, NULL);
|
||||
add_numbut(4, TOG|SHO, "Collision", 0, 0, &m_collision, NULL);
|
||||
add_numbut(5, TOG|SHO, "Shared", 0, 0, &m_shared, NULL);
|
||||
add_numbut(6, TOG|SHO, "Twoside", 0, 0, &m_twoside, NULL);
|
||||
add_numbut(7, TOG|SHO, "ObColor", 0, 0, &m_obcolor, NULL);
|
||||
add_numbut(8, TOG|SHO, "Halo", 0, 0, &m_halo, NULL);
|
||||
add_numbut(9, TOG|SHO, "Billboard", 0, 0, &m_billboard, NULL);
|
||||
add_numbut(10, TOG|SHO, "Shadow", 0, 0, &m_shadow, NULL);
|
||||
add_numbut(11, TOG|SHO, "Text", 0, 0, &m_text, NULL);
|
||||
add_numbut(12, TOG|SHO, "Sort", 0, 0, &m_sort, NULL);
|
||||
|
||||
if (!do_clever_numbuts((mode ? "Set Flags" : "Clear Flags"), 13, REDRAW))
|
||||
return;
|
||||
|
||||
/* these 2 cant both be on */
|
||||
if (mode) /* are we seeting*/
|
||||
if (m_halo)
|
||||
m_billboard = 0;
|
||||
|
||||
if (m_tex) flag |= TF_TEX;
|
||||
if (m_tiles) flag |= TF_TILES;
|
||||
if (m_shared) flag |= TF_SHAREDCOL;
|
||||
if (m_light) flag |= TF_LIGHT;
|
||||
if (m_invis) flag |= TF_INVISIBLE;
|
||||
if (m_collision) flag |= TF_DYNAMIC;
|
||||
if (m_twoside) flag |= TF_TWOSIDE;
|
||||
if (m_obcolor) flag |= TF_OBCOL;
|
||||
if (m_halo) flag |= TF_BILLBOARD;
|
||||
if (m_billboard) flag |= TF_BILLBOARD2;
|
||||
if (m_shadow) flag |= TF_SHADOW;
|
||||
if (m_text) flag |= TF_BMFONT;
|
||||
if (m_sort) flag |= TF_ALPHASORT;
|
||||
|
||||
if (flag==0)
|
||||
return;
|
||||
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
if(efa->f & SELECT) {
|
||||
tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
|
||||
if (mode) tface->mode |= flag;
|
||||
else tface->mode &= ~flag;
|
||||
change = 1;
|
||||
}
|
||||
efa= efa->next;
|
||||
}
|
||||
|
||||
if (change) {
|
||||
BIF_undo_push((mode ? "Set Flags" : "Clear Flags"));
|
||||
|
||||
allqueue(REDRAWIMAGE, 0);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_set_smooth_faces(short event)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
|
||||
if(G.obedit==0) return;
|
||||
|
||||
if(G.obedit->type != OB_MESH) return;
|
||||
|
||||
efa= em->faces.first;
|
||||
while(efa) {
|
||||
if(efa->f & SELECT) {
|
||||
if(event==1) efa->flag |= ME_SMOOTH;
|
||||
else if(event==0) efa->flag &= ~ME_SMOOTH;
|
||||
}
|
||||
efa= efa->next;
|
||||
}
|
||||
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
|
||||
if(event==1) BIF_undo_push("Set Smooth");
|
||||
else if(event==0) BIF_undo_push("Set Solid");
|
||||
}
|
||||
|
||||
/* helper to find edge for edge_rip */
|
||||
static float mesh_rip_edgedist(float mat[][4], float *co1, float *co2, short *mval)
|
||||
{
|
||||
float vec1[3], vec2[3], mvalf[2];
|
||||
|
||||
view3d_project_float(curarea, co1, vec1, mat);
|
||||
view3d_project_float(curarea, co2, vec2, mat);
|
||||
mvalf[0]= (float)mval[0];
|
||||
mvalf[1]= (float)mval[1];
|
||||
|
||||
return dist_to_line_segment_v2(mvalf, vec1, vec2);
|
||||
}
|
||||
|
||||
/* helper for below */
|
||||
static void mesh_rip_setface(EditFace *sefa)
|
||||
{
|
||||
/* put new vertices & edges in best face */
|
||||
if(sefa->v1->tmp.v) sefa->v1= sefa->v1->tmp.v;
|
||||
if(sefa->v2->tmp.v) sefa->v2= sefa->v2->tmp.v;
|
||||
if(sefa->v3->tmp.v) sefa->v3= sefa->v3->tmp.v;
|
||||
if(sefa->v4 && sefa->v4->tmp.v) sefa->v4= sefa->v4->tmp.v;
|
||||
|
||||
sefa->e1= addedgelist(sefa->v1, sefa->v2, sefa->e1);
|
||||
sefa->e2= addedgelist(sefa->v2, sefa->v3, sefa->e2);
|
||||
if(sefa->v4) {
|
||||
sefa->e3= addedgelist(sefa->v3, sefa->v4, sefa->e3);
|
||||
sefa->e4= addedgelist(sefa->v4, sefa->v1, sefa->e4);
|
||||
}
|
||||
else
|
||||
sefa->e3= addedgelist(sefa->v3, sefa->v1, sefa->e3);
|
||||
|
||||
}
|
||||
|
||||
/* based on mouse cursor position, it defines how is being ripped */
|
||||
void mesh_rip(void)
|
||||
{
|
||||
extern void faceloop_select(EditEdge *startedge, int select);
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *eve, *nextve;
|
||||
EditEdge *eed, *seed= NULL;
|
||||
EditFace *efa, *sefa= NULL;
|
||||
float projectMat[4][4], viewMat[4][4], vec[3], dist, mindist;
|
||||
short doit= 1, mval[2],propmode,prop;
|
||||
|
||||
propmode = G.scene->prop_mode;
|
||||
G.scene->prop_mode = 0;
|
||||
prop = G.scene->proportional;
|
||||
G.scene->proportional = 0;
|
||||
|
||||
/* select flush... vertices are important */
|
||||
EM_selectmode_set();
|
||||
|
||||
getmouseco_areawin(mval);
|
||||
view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat);
|
||||
|
||||
/* find best face, exclude triangles and break on face select or faces with 2 edges select */
|
||||
mindist= 1000000.0f;
|
||||
for(efa= em->faces.first; efa; efa=efa->next) {
|
||||
if( efa->f & 1)
|
||||
break;
|
||||
if(efa->v4 && faceselectedOR(efa, SELECT) ) {
|
||||
int totsel=0;
|
||||
|
||||
if(efa->e1->f & SELECT) totsel++;
|
||||
if(efa->e2->f & SELECT) totsel++;
|
||||
if(efa->e3->f & SELECT) totsel++;
|
||||
if(efa->e4->f & SELECT) totsel++;
|
||||
|
||||
if(totsel>1)
|
||||
break;
|
||||
view3d_project_float(curarea, efa->cent, vec, projectMat);
|
||||
dist= sqrt( (vec[0]-mval[0])*(vec[0]-mval[0]) + (vec[1]-mval[1])*(vec[1]-mval[1]) );
|
||||
if(dist<mindist) {
|
||||
mindist= dist;
|
||||
sefa= efa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(efa) {
|
||||
error("Can't perform ripping with faces selected this way");
|
||||
return;
|
||||
}
|
||||
if(sefa==NULL) {
|
||||
error("No proper selection or faces included");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* duplicate vertices, new vertices get selected */
|
||||
for(eve = em->verts.last; eve; eve= eve->prev) {
|
||||
eve->tmp.v = NULL;
|
||||
if(eve->f & SELECT) {
|
||||
eve->tmp.v = addvertlist(eve->co, eve);
|
||||
eve->f &= ~SELECT;
|
||||
eve->tmp.v->f |= SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
/* find the best candidate edge */
|
||||
/* or one of sefa edges is selected... */
|
||||
if(sefa->e1->f & SELECT) seed= sefa->e2;
|
||||
if(sefa->e2->f & SELECT) seed= sefa->e1;
|
||||
if(sefa->e3->f & SELECT) seed= sefa->e2;
|
||||
if(sefa->e4 && sefa->e4->f & SELECT) seed= sefa->e3;
|
||||
|
||||
/* or we do the distance trick */
|
||||
if(seed==NULL) {
|
||||
mindist= 1000000.0f;
|
||||
if(sefa->e1->v1->tmp.v || sefa->e1->v2->tmp.v) {
|
||||
dist = mesh_rip_edgedist(projectMat,
|
||||
sefa->e1->v1->co,
|
||||
sefa->e1->v2->co, mval);
|
||||
if(dist<mindist) {
|
||||
seed= sefa->e1;
|
||||
mindist= dist;
|
||||
}
|
||||
}
|
||||
if(sefa->e2->v1->tmp.v || sefa->e2->v2->tmp.v) {
|
||||
dist = mesh_rip_edgedist(projectMat,
|
||||
sefa->e2->v1->co,
|
||||
sefa->e2->v2->co, mval);
|
||||
if(dist<mindist) {
|
||||
seed= sefa->e2;
|
||||
mindist= dist;
|
||||
}
|
||||
}
|
||||
if(sefa->e3->v1->tmp.v || sefa->e3->v2->tmp.v) {
|
||||
dist= mesh_rip_edgedist(projectMat,
|
||||
sefa->e3->v1->co,
|
||||
sefa->e3->v2->co, mval);
|
||||
if(dist<mindist) {
|
||||
seed= sefa->e3;
|
||||
mindist= dist;
|
||||
}
|
||||
}
|
||||
if(sefa->e4 && (sefa->e4->v1->tmp.v || sefa->e4->v2->tmp.v)) {
|
||||
dist= mesh_rip_edgedist(projectMat,
|
||||
sefa->e4->v1->co,
|
||||
sefa->e4->v2->co, mval);
|
||||
if(dist<mindist) {
|
||||
seed= sefa->e4;
|
||||
mindist= dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(seed==NULL) { // never happens?
|
||||
error("No proper edge found to start");
|
||||
return;
|
||||
}
|
||||
|
||||
faceloop_select(seed, 2); // tmp abuse for finding all edges that need duplicated, returns OK faces with f1
|
||||
|
||||
/* duplicate edges in the loop, with at least 1 vertex selected, needed for selection flip */
|
||||
for(eed = em->edges.last; eed; eed= eed->prev) {
|
||||
eed->tmp.v = NULL;
|
||||
if((eed->v1->tmp.v) || (eed->v2->tmp.v)) {
|
||||
EditEdge *newed;
|
||||
|
||||
newed= addedgelist(eed->v1->tmp.v?eed->v1->tmp.v:eed->v1,
|
||||
eed->v2->tmp.v?eed->v2->tmp.v:eed->v2, eed);
|
||||
if(eed->f & SELECT) {
|
||||
eed->f &= ~SELECT;
|
||||
newed->f |= SELECT;
|
||||
}
|
||||
eed->tmp.v = (EditVert *)newed;
|
||||
}
|
||||
}
|
||||
|
||||
/* first clear edges to help finding neighbours */
|
||||
for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0;
|
||||
|
||||
/* put new vertices & edges && flag in best face */
|
||||
mesh_rip_setface(sefa);
|
||||
|
||||
/* starting with neighbours of best face, we loop over the seam */
|
||||
sefa->f1= 2;
|
||||
doit= 1;
|
||||
while(doit) {
|
||||
doit= 0;
|
||||
|
||||
for(efa= em->faces.first; efa; efa=efa->next) {
|
||||
/* new vert in face */
|
||||
if (efa->v1->tmp.v || efa->v2->tmp.v ||
|
||||
efa->v3->tmp.v || (efa->v4 && efa->v4->tmp.v)) {
|
||||
/* face is tagged with loop */
|
||||
if(efa->f1==1) {
|
||||
mesh_rip_setface(efa);
|
||||
efa->f1= 2;
|
||||
doit= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* remove loose edges, that were part of a ripped face */
|
||||
for(eve = em->verts.first; eve; eve= eve->next) eve->f1= 0;
|
||||
for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0;
|
||||
for(efa= em->faces.first; efa; efa=efa->next) {
|
||||
efa->e1->f1= 1;
|
||||
efa->e2->f1= 1;
|
||||
efa->e3->f1= 1;
|
||||
if(efa->e4) efa->e4->f1= 1;
|
||||
}
|
||||
|
||||
for(eed = em->edges.last; eed; eed= seed) {
|
||||
seed= eed->prev;
|
||||
if(eed->f1==0) {
|
||||
if(eed->v1->tmp.v || eed->v2->tmp.v ||
|
||||
(eed->v1->f & SELECT) || (eed->v2->f & SELECT)) {
|
||||
remedge(eed);
|
||||
free_editedge(eed);
|
||||
eed= NULL;
|
||||
}
|
||||
}
|
||||
if(eed) {
|
||||
eed->v1->f1= 1;
|
||||
eed->v2->f1= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* and remove loose selected vertices, that got duplicated accidentally */
|
||||
for(eve = em->verts.first; eve; eve= nextve) {
|
||||
nextve= eve->next;
|
||||
if(eve->f1==0 && (eve->tmp.v || (eve->f & SELECT))) {
|
||||
BLI_remlink(&em->verts,eve);
|
||||
free_editvert(eve);
|
||||
}
|
||||
}
|
||||
|
||||
countall(); // apparently always needed when adding stuff, derived mesh
|
||||
|
||||
#ifdef WITH_VERSE
|
||||
if(G.editMesh->vnode) {
|
||||
sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode);
|
||||
sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
|
||||
}
|
||||
#endif
|
||||
|
||||
BIF_TransformSetUndo("Rip");
|
||||
initTransform(TFM_TRANSLATION, 0);
|
||||
Transform();
|
||||
|
||||
G.scene->prop_mode = propmode;
|
||||
G.scene->proportional = prop;
|
||||
}
|
||||
|
||||
void shape_propagate(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *ev = NULL;
|
||||
Mesh* me = (Mesh*)G.obedit->data;
|
||||
Key* ky = NULL;
|
||||
KeyBlock* kb = NULL;
|
||||
Base* base=NULL;
|
||||
|
||||
|
||||
if(me->key){
|
||||
ky = me->key;
|
||||
} else {
|
||||
error("Object Has No Key");
|
||||
return;
|
||||
}
|
||||
|
||||
if(ky->block.first){
|
||||
for(ev = em->verts.first; ev ; ev = ev->next){
|
||||
if(ev->f & SELECT){
|
||||
for(kb=ky->block.first;kb;kb = kb->next){
|
||||
float *data;
|
||||
data = kb->data;
|
||||
copy_v3_v3(data+(ev->keyindex*3),ev->co);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error("Object Has No Blendshapes");
|
||||
return;
|
||||
}
|
||||
|
||||
//TAG Mesh Objects that share this data
|
||||
for(base = G.scene->base.first; base; base = base->next){
|
||||
if(base->object && base->object->data == me){
|
||||
base->object->recalc = OB_RECALC_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
BIF_undo_push("Propagate Blendshape Verts");
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void shape_copy_from_lerp(KeyBlock* thisBlock, KeyBlock* fromBlock)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *ev = NULL;
|
||||
short mval[2], curval[2], event = 0, finished = 0, canceled = 0, fullcopy=0 ;
|
||||
float perc = 0;
|
||||
char str[64];
|
||||
float *data, *odata;
|
||||
|
||||
data = fromBlock->data;
|
||||
odata = thisBlock->data;
|
||||
|
||||
getmouseco_areawin(mval);
|
||||
curval[0] = mval[0] + 1; curval[1] = mval[1] + 1;
|
||||
|
||||
while (finished == 0)
|
||||
{
|
||||
getmouseco_areawin(mval);
|
||||
if (mval[0] != curval[0] || mval[1] != curval[1])
|
||||
{
|
||||
|
||||
if(mval[0] > curval[0])
|
||||
perc += 0.1;
|
||||
else if(mval[0] < curval[0])
|
||||
perc -= 0.1;
|
||||
|
||||
if(perc < 0) perc = 0;
|
||||
if(perc > 1) perc = 1;
|
||||
|
||||
curval[0] = mval[0];
|
||||
curval[1] = mval[1];
|
||||
|
||||
if(fullcopy == 1){
|
||||
perc = 1;
|
||||
}
|
||||
|
||||
for(ev = em->verts.first; ev ; ev = ev->next){
|
||||
if(ev->f & SELECT){
|
||||
interp_v3_v3v3(ev->co,odata+(ev->keyindex*3),data+(ev->keyindex*3),perc);
|
||||
}
|
||||
}
|
||||
sprintf(str,"Blending at %d%c MMB to Copy at 100%c",(int)(perc*100),'%','%');
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
headerprint(str);
|
||||
force_draw(0);
|
||||
|
||||
if(fullcopy == 1){
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
PIL_sleep_ms(10);
|
||||
}
|
||||
|
||||
while(qtest()) {
|
||||
short val=0;
|
||||
event= extern_qread(&val);
|
||||
if(val){
|
||||
if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)){
|
||||
finished = 1;
|
||||
}
|
||||
else if (event == MIDDLEMOUSE){
|
||||
fullcopy = 1;
|
||||
}
|
||||
else if (ELEM3(event,ESCKEY,RIGHTMOUSE,RIGHTMOUSE)){
|
||||
canceled = 1;
|
||||
finished = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!canceled)
|
||||
BIF_undo_push("Copy Blendshape Verts");
|
||||
else
|
||||
for(ev = em->verts.first; ev ; ev = ev->next){
|
||||
if(ev->f & SELECT){
|
||||
copy_v3_v3(ev->co, odata+(ev->keyindex*3));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void shape_copy_select_from()
|
||||
{
|
||||
Mesh* me = (Mesh*)G.obedit->data;
|
||||
EditMesh *em = G.editMesh;
|
||||
EditVert *ev = NULL;
|
||||
int totverts = 0,curshape = G.obedit->shapenr;
|
||||
|
||||
Key* ky = NULL;
|
||||
KeyBlock *kb = NULL,*thisBlock = NULL;
|
||||
int maxlen=32, nr=0, a=0;
|
||||
char *menu;
|
||||
|
||||
if(me->key){
|
||||
ky = me->key;
|
||||
} else {
|
||||
error("Object Has No Key");
|
||||
return;
|
||||
}
|
||||
|
||||
if(ky->block.first){
|
||||
for(kb=ky->block.first;kb;kb = kb->next){
|
||||
maxlen += 40; // Size of a block name
|
||||
if(a == curshape-1){
|
||||
thisBlock = kb;
|
||||
}
|
||||
|
||||
a++;
|
||||
}
|
||||
a=0;
|
||||
menu = MEM_callocN(maxlen, "Copy Shape Menu Text");
|
||||
strcpy(menu, "Copy Vert Positions from Shape %t|");
|
||||
for(kb=ky->block.first;kb;kb = kb->next){
|
||||
if(a != curshape-1){
|
||||
sprintf(menu,"%s %s %cx%d|",menu,kb->name,'%',a);
|
||||
}
|
||||
a++;
|
||||
}
|
||||
nr = pupmenu_col(menu, 20);
|
||||
MEM_freeN(menu);
|
||||
} else {
|
||||
error("Object Has No Blendshapes");
|
||||
return;
|
||||
}
|
||||
|
||||
a = 0;
|
||||
|
||||
for(kb=ky->block.first;kb;kb = kb->next){
|
||||
if(a == nr){
|
||||
|
||||
for(ev = em->verts.first;ev;ev = ev->next){
|
||||
totverts++;
|
||||
}
|
||||
|
||||
if(me->totvert != totverts){
|
||||
error("Shape Has had Verts Added/Removed, please cycle editmode before copying");
|
||||
return;
|
||||
}
|
||||
shape_copy_from_lerp(thisBlock,kb);
|
||||
|
||||
return;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Collection Routines|Currently used by the improved merge code*/
|
||||
/* buildEdge_collection() creates a list of lists*/
|
||||
/* these lists are filled with edges that are topologically connected.*/
|
||||
/* This whole tool needs to be redone, its rather poorly implemented...*/
|
||||
|
||||
typedef struct Collection{
|
||||
struct Collection *next, *prev;
|
||||
int index;
|
||||
ListBase collectionbase;
|
||||
} Collection;
|
||||
|
||||
typedef struct CollectedEdge{
|
||||
struct CollectedEdge *next, *prev;
|
||||
EditEdge *eed;
|
||||
} CollectedEdge;
|
||||
|
||||
#define MERGELIMIT 0.000001
|
||||
|
||||
static void build_edgecollection(ListBase *allcollections)
|
||||
{
|
||||
EditEdge *eed;
|
||||
Collection *edgecollection, *newcollection;
|
||||
CollectedEdge *newedge;
|
||||
|
||||
int currtag = 1;
|
||||
short ebalanced = 0;
|
||||
short collectionfound = 0;
|
||||
|
||||
for (eed=G.editMesh->edges.first; eed; eed = eed->next){
|
||||
eed->tmp.l = 0;
|
||||
eed->v1->tmp.l = 0;
|
||||
eed->v2->tmp.l = 0;
|
||||
}
|
||||
|
||||
/*1st pass*/
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
|
||||
if(eed->f&SELECT){
|
||||
eed->v1->tmp.l = currtag;
|
||||
eed->v2->tmp.l = currtag;
|
||||
currtag +=1;
|
||||
}
|
||||
}
|
||||
|
||||
/*2nd pass - Brute force. Loop through selected faces until there are no 'unbalanced' edges left (those with both vertices 'tmp.l' tag matching */
|
||||
while(ebalanced == 0){
|
||||
ebalanced = 1;
|
||||
for(eed=G.editMesh->edges.first; eed; eed = eed->next){
|
||||
if(eed->f&SELECT){
|
||||
if(eed->v1->tmp.l != eed->v2->tmp.l) /*unbalanced*/{
|
||||
if(eed->v1->tmp.l > eed->v2->tmp.l && eed->v2->tmp.l !=0) eed->v1->tmp.l = eed->v2->tmp.l;
|
||||
else if(eed->v1 != 0) eed->v2->tmp.l = eed->v1->tmp.l;
|
||||
ebalanced = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*3rd pass, set all the edge flags (unnessecary?)*/
|
||||
for(eed=G.editMesh->edges.first; eed; eed = eed->next){
|
||||
if(eed->f&SELECT) eed->tmp.l = eed->v1->tmp.l;
|
||||
}
|
||||
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
|
||||
if(eed->f&SELECT){
|
||||
if(allcollections->first){
|
||||
for(edgecollection = allcollections->first; edgecollection; edgecollection=edgecollection->next){
|
||||
if(edgecollection->index == eed->tmp.l){
|
||||
newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge");
|
||||
newedge->eed = eed;
|
||||
BLI_addtail(&(edgecollection->collectionbase), newedge);
|
||||
collectionfound = 1;
|
||||
break;
|
||||
}
|
||||
else collectionfound = 0;
|
||||
}
|
||||
}
|
||||
if(allcollections->first == NULL || collectionfound == 0){
|
||||
newcollection = MEM_mallocN(sizeof(Collection), "element collection");
|
||||
newcollection->index = eed->tmp.l;
|
||||
newcollection->collectionbase.first = 0;
|
||||
newcollection->collectionbase.last = 0;
|
||||
|
||||
newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge");
|
||||
newedge->eed = eed;
|
||||
|
||||
BLI_addtail(&(newcollection->collectionbase), newedge);
|
||||
BLI_addtail(allcollections, newcollection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void freecollections(ListBase *allcollections)
|
||||
{
|
||||
struct Collection *curcollection;
|
||||
|
||||
for(curcollection = allcollections->first; curcollection; curcollection = curcollection->next)
|
||||
BLI_freelistN(&(curcollection->collectionbase));
|
||||
BLI_freelistN(allcollections);
|
||||
}
|
||||
|
||||
/*Begin UV Edge Collapse Code
|
||||
Like Edge subdivide, Edge Collapse should handle UV's intelligently, but since UV's are a per-face attribute, normal edge collapse will fail
|
||||
in areas such as the boundries of 'UV islands'. So for each edge collection we need to build a set of 'welded' UV vertices and edges for it.
|
||||
The welded UV edges can then be sorted and collapsed.
|
||||
*/
|
||||
typedef struct wUV{
|
||||
struct wUV *next, *prev;
|
||||
ListBase nodes;
|
||||
float u, v; /*cached copy of UV coordinates pointed to by nodes*/
|
||||
EditVert *eve;
|
||||
int f;
|
||||
} wUV;
|
||||
|
||||
typedef struct wUVNode{
|
||||
struct wUVNode *next, *prev;
|
||||
float *u; /*pointer to original tface data*/
|
||||
float *v; /*pointer to original tface data*/
|
||||
} wUVNode;
|
||||
|
||||
typedef struct wUVEdge{
|
||||
struct wUVEdge *next, *prev;
|
||||
float v1uv[2], v2uv[2]; /*nasty.*/
|
||||
struct wUV *v1, *v2; /*oriented same as editedge*/
|
||||
EditEdge *eed;
|
||||
int f;
|
||||
} wUVEdge;
|
||||
|
||||
typedef struct wUVEdgeCollect{ /*used for grouping*/
|
||||
struct wUVEdgeCollect *next, *prev;
|
||||
wUVEdge *uved;
|
||||
int id;
|
||||
} wUVEdgeCollect;
|
||||
|
||||
static void append_weldedUV(EditFace *efa, EditVert *eve, int tfindex, ListBase *uvverts)
|
||||
{
|
||||
wUV *curwvert, *newwvert;
|
||||
wUVNode *newnode;
|
||||
int found;
|
||||
MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
|
||||
|
||||
found = 0;
|
||||
|
||||
for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
|
||||
if(curwvert->eve == eve && curwvert->u == tf->uv[tfindex][0] && curwvert->v == tf->uv[tfindex][1]){
|
||||
newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node");
|
||||
newnode->u = &(tf->uv[tfindex][0]);
|
||||
newnode->v = &(tf->uv[tfindex][1]);
|
||||
BLI_addtail(&(curwvert->nodes), newnode);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found){
|
||||
newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node");
|
||||
newnode->u = &(tf->uv[tfindex][0]);
|
||||
newnode->v = &(tf->uv[tfindex][1]);
|
||||
|
||||
newwvert = MEM_callocN(sizeof(wUV), "Welded UV Vert");
|
||||
newwvert->u = *(newnode->u);
|
||||
newwvert->v = *(newnode->v);
|
||||
newwvert->eve = eve;
|
||||
|
||||
BLI_addtail(&(newwvert->nodes), newnode);
|
||||
BLI_addtail(uvverts, newwvert);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void build_weldedUVs(ListBase *uvverts)
|
||||
{
|
||||
EditFace *efa;
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
if(efa->v1->f1) append_weldedUV(efa, efa->v1, 0, uvverts);
|
||||
if(efa->v2->f1) append_weldedUV(efa, efa->v2, 1, uvverts);
|
||||
if(efa->v3->f1) append_weldedUV(efa, efa->v3, 2, uvverts);
|
||||
if(efa->v4 && efa->v4->f1) append_weldedUV(efa, efa->v4, 3, uvverts);
|
||||
}
|
||||
}
|
||||
|
||||
static void append_weldedUVEdge(EditFace *efa, EditEdge *eed, ListBase *uvedges)
|
||||
{
|
||||
wUVEdge *curwedge, *newwedge;
|
||||
int v1tfindex, v2tfindex, found;
|
||||
MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
|
||||
|
||||
found = 0;
|
||||
|
||||
if(eed->v1 == efa->v1) v1tfindex = 0;
|
||||
else if(eed->v1 == efa->v2) v1tfindex = 1;
|
||||
else if(eed->v1 == efa->v3) v1tfindex = 2;
|
||||
else /* if(eed->v1 == efa->v4) */ v1tfindex = 3;
|
||||
|
||||
if(eed->v2 == efa->v1) v2tfindex = 0;
|
||||
else if(eed->v2 == efa->v2) v2tfindex = 1;
|
||||
else if(eed->v2 == efa->v3) v2tfindex = 2;
|
||||
else /* if(eed->v2 == efa->v4) */ v2tfindex = 3;
|
||||
|
||||
for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){
|
||||
if(curwedge->eed == eed && curwedge->v1uv[0] == tf->uv[v1tfindex][0] && curwedge->v1uv[1] == tf->uv[v1tfindex][1] && curwedge->v2uv[0] == tf->uv[v2tfindex][0] && curwedge->v2uv[1] == tf->uv[v2tfindex][1]){
|
||||
found = 1;
|
||||
break; //do nothing, we don't need another welded uv edge
|
||||
}
|
||||
}
|
||||
|
||||
if(!found){
|
||||
newwedge = MEM_callocN(sizeof(wUVEdge), "Welded UV Edge");
|
||||
newwedge->v1uv[0] = tf->uv[v1tfindex][0];
|
||||
newwedge->v1uv[1] = tf->uv[v1tfindex][1];
|
||||
newwedge->v2uv[0] = tf->uv[v2tfindex][0];
|
||||
newwedge->v2uv[1] = tf->uv[v2tfindex][1];
|
||||
newwedge->eed = eed;
|
||||
|
||||
BLI_addtail(uvedges, newwedge);
|
||||
}
|
||||
}
|
||||
|
||||
static void build_weldedUVEdges(ListBase *uvedges, ListBase *uvverts)
|
||||
{
|
||||
wUV *curwvert;
|
||||
wUVEdge *curwedge;
|
||||
EditFace *efa;
|
||||
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
if(efa->e1->f1) append_weldedUVEdge(efa, efa->e1, uvedges);
|
||||
if(efa->e2->f1) append_weldedUVEdge(efa, efa->e2, uvedges);
|
||||
if(efa->e3->f1) append_weldedUVEdge(efa, efa->e3, uvedges);
|
||||
if(efa->e4 && efa->e4->f1) append_weldedUVEdge(efa, efa->e4, uvedges);
|
||||
}
|
||||
|
||||
|
||||
//link vertices: for each uvedge, search uvverts to populate v1 and v2 pointers
|
||||
for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){
|
||||
for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
|
||||
if(curwedge->eed->v1 == curwvert->eve && curwedge->v1uv[0] == curwvert->u && curwedge->v1uv[1] == curwvert->v){
|
||||
curwedge->v1 = curwvert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
|
||||
if(curwedge->eed->v2 == curwvert->eve && curwedge->v2uv[0] == curwvert->u && curwedge->v2uv[1] == curwvert->v){
|
||||
curwedge->v2 = curwvert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void free_weldedUVs(ListBase *uvverts)
|
||||
{
|
||||
wUV *curwvert;
|
||||
for(curwvert = uvverts->first; curwvert; curwvert=curwvert->next) BLI_freelistN(&(curwvert->nodes));
|
||||
BLI_freelistN(uvverts);
|
||||
}
|
||||
|
||||
static void collapse_edgeuvs(void)
|
||||
{
|
||||
ListBase uvedges, uvverts, allcollections;
|
||||
wUVEdge *curwedge;
|
||||
wUVNode *curwnode;
|
||||
wUVEdgeCollect *collectedwuve, *newcollectedwuve;
|
||||
Collection *wuvecollection, *newcollection;
|
||||
int curtag, balanced, collectionfound= 0, vcount;
|
||||
float avg[2];
|
||||
|
||||
if (!EM_texFaceCheck())
|
||||
return;
|
||||
|
||||
uvverts.first = uvverts.last = uvedges.first = uvedges.last = allcollections.first = allcollections.last = NULL;
|
||||
|
||||
build_weldedUVs(&uvverts);
|
||||
build_weldedUVEdges(&uvedges, &uvverts);
|
||||
|
||||
curtag = 0;
|
||||
|
||||
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
|
||||
curwedge->v1->f = curtag;
|
||||
curwedge->v2->f = curtag;
|
||||
curtag +=1;
|
||||
}
|
||||
|
||||
balanced = 0;
|
||||
while(!balanced){
|
||||
balanced = 1;
|
||||
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
|
||||
if(curwedge->v1->f != curwedge->v2->f){
|
||||
if(curwedge->v1->f > curwedge->v2->f) curwedge->v1->f = curwedge->v2->f;
|
||||
else curwedge->v2->f = curwedge->v1->f;
|
||||
balanced = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next) curwedge->f = curwedge->v1->f;
|
||||
|
||||
|
||||
for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
|
||||
if(allcollections.first){
|
||||
for(wuvecollection = allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){
|
||||
if(wuvecollection->index == curwedge->f){
|
||||
newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge");
|
||||
newcollectedwuve->uved = curwedge;
|
||||
BLI_addtail(&(wuvecollection->collectionbase), newcollectedwuve);
|
||||
collectionfound = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
else collectionfound = 0;
|
||||
}
|
||||
}
|
||||
if(allcollections.first == NULL || collectionfound == 0){
|
||||
newcollection = MEM_callocN(sizeof(Collection), "element collection");
|
||||
newcollection->index = curwedge->f;
|
||||
newcollection->collectionbase.first = 0;
|
||||
newcollection->collectionbase.last = 0;
|
||||
|
||||
newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge");
|
||||
newcollectedwuve->uved = curwedge;
|
||||
|
||||
BLI_addtail(&(newcollection->collectionbase), newcollectedwuve);
|
||||
BLI_addtail(&allcollections, newcollection);
|
||||
}
|
||||
}
|
||||
|
||||
for(wuvecollection=allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){
|
||||
|
||||
vcount = avg[0] = avg[1] = 0;
|
||||
|
||||
for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){
|
||||
avg[0] += collectedwuve->uved->v1uv[0];
|
||||
avg[1] += collectedwuve->uved->v1uv[1];
|
||||
|
||||
avg[0] += collectedwuve->uved->v2uv[0];
|
||||
avg[1] += collectedwuve->uved->v2uv[1];
|
||||
|
||||
vcount +=2;
|
||||
|
||||
}
|
||||
|
||||
avg[0] /= vcount; avg[1] /= vcount;
|
||||
|
||||
for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){
|
||||
for(curwnode=collectedwuve->uved->v1->nodes.first; curwnode; curwnode=curwnode->next){
|
||||
*(curwnode->u) = avg[0];
|
||||
*(curwnode->v) = avg[1];
|
||||
}
|
||||
for(curwnode=collectedwuve->uved->v2->nodes.first; curwnode; curwnode=curwnode->next){
|
||||
*(curwnode->u) = avg[0];
|
||||
*(curwnode->v) = avg[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_weldedUVs(&uvverts);
|
||||
BLI_freelistN(&uvedges);
|
||||
freecollections(&allcollections);
|
||||
}
|
||||
|
||||
/*End UV Edge collapse code*/
|
||||
|
||||
static void collapseuvs(EditVert *mergevert)
|
||||
{
|
||||
EditFace *efa;
|
||||
MTFace *tf;
|
||||
int uvcount;
|
||||
float uvav[2];
|
||||
|
||||
if (!EM_texFaceCheck())
|
||||
return;
|
||||
|
||||
uvcount = 0;
|
||||
uvav[0] = 0;
|
||||
uvav[1] = 0;
|
||||
|
||||
for(efa = G.editMesh->faces.first; efa; efa=efa->next){
|
||||
tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
|
||||
|
||||
if(efa->v1->f1 && ELEM(mergevert, NULL, efa->v1)) {
|
||||
uvav[0] += tf->uv[0][0];
|
||||
uvav[1] += tf->uv[0][1];
|
||||
uvcount += 1;
|
||||
}
|
||||
if(efa->v2->f1 && ELEM(mergevert, NULL, efa->v2)){
|
||||
uvav[0] += tf->uv[1][0];
|
||||
uvav[1] += tf->uv[1][1];
|
||||
uvcount += 1;
|
||||
}
|
||||
if(efa->v3->f1 && ELEM(mergevert, NULL, efa->v3)){
|
||||
uvav[0] += tf->uv[2][0];
|
||||
uvav[1] += tf->uv[2][1];
|
||||
uvcount += 1;
|
||||
}
|
||||
if(efa->v4 && efa->v4->f1 && ELEM(mergevert, NULL, efa->v4)){
|
||||
uvav[0] += tf->uv[3][0];
|
||||
uvav[1] += tf->uv[3][1];
|
||||
uvcount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(uvcount > 0) {
|
||||
uvav[0] /= uvcount;
|
||||
uvav[1] /= uvcount;
|
||||
|
||||
for(efa = G.editMesh->faces.first; efa; efa=efa->next){
|
||||
tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
|
||||
|
||||
if(efa->v1->f1){
|
||||
tf->uv[0][0] = uvav[0];
|
||||
tf->uv[0][1] = uvav[1];
|
||||
}
|
||||
if(efa->v2->f1){
|
||||
tf->uv[1][0] = uvav[0];
|
||||
tf->uv[1][1] = uvav[1];
|
||||
}
|
||||
if(efa->v3->f1){
|
||||
tf->uv[2][0] = uvav[0];
|
||||
tf->uv[2][1] = uvav[1];
|
||||
}
|
||||
if(efa->v4 && efa->v4->f1){
|
||||
tf->uv[3][0] = uvav[0];
|
||||
tf->uv[3][1] = uvav[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int collapseEdges(void)
|
||||
{
|
||||
EditVert *eve;
|
||||
EditEdge *eed;
|
||||
|
||||
ListBase allcollections;
|
||||
CollectedEdge *curredge;
|
||||
Collection *edgecollection;
|
||||
|
||||
int totedges, groupcount, mergecount,vcount;
|
||||
float avgcount[3];
|
||||
|
||||
allcollections.first = 0;
|
||||
allcollections.last = 0;
|
||||
|
||||
mergecount = 0;
|
||||
|
||||
if(multires_test()) return 0;
|
||||
|
||||
build_edgecollection(&allcollections);
|
||||
groupcount = BLI_countlist(&allcollections);
|
||||
|
||||
|
||||
for(edgecollection = allcollections.first; edgecollection; edgecollection = edgecollection->next){
|
||||
totedges = BLI_countlist(&(edgecollection->collectionbase));
|
||||
mergecount += totedges;
|
||||
avgcount[0] = 0; avgcount[1] = 0; avgcount[2] = 0;
|
||||
|
||||
vcount = 0;
|
||||
|
||||
for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
|
||||
avgcount[0] += ((EditEdge*)curredge->eed)->v1->co[0];
|
||||
avgcount[1] += ((EditEdge*)curredge->eed)->v1->co[1];
|
||||
avgcount[2] += ((EditEdge*)curredge->eed)->v1->co[2];
|
||||
|
||||
avgcount[0] += ((EditEdge*)curredge->eed)->v2->co[0];
|
||||
avgcount[1] += ((EditEdge*)curredge->eed)->v2->co[1];
|
||||
avgcount[2] += ((EditEdge*)curredge->eed)->v2->co[2];
|
||||
|
||||
vcount +=2;
|
||||
}
|
||||
|
||||
avgcount[0] /= vcount; avgcount[1] /=vcount; avgcount[2] /= vcount;
|
||||
|
||||
for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
|
||||
copy_v3_v3(((EditEdge*)curredge->eed)->v1->co,avgcount);
|
||||
copy_v3_v3(((EditEdge*)curredge->eed)->v2->co,avgcount);
|
||||
}
|
||||
|
||||
if (EM_texFaceCheck()) {
|
||||
/*uv collapse*/
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
|
||||
for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
|
||||
curredge->eed->v1->f1 = 1;
|
||||
curredge->eed->v2->f1 = 1;
|
||||
curredge->eed->f1 = 1;
|
||||
}
|
||||
collapse_edgeuvs();
|
||||
}
|
||||
|
||||
}
|
||||
freecollections(&allcollections);
|
||||
removedoublesflag(1, 0, MERGELIMIT);
|
||||
/*get rid of this!*/
|
||||
countall();
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
if (EM_texFaceCheck())
|
||||
allqueue(REDRAWIMAGE, 0);
|
||||
return mergecount;
|
||||
}
|
||||
|
||||
int merge_firstlast(int first, int uvmerge)
|
||||
{
|
||||
EditVert *eve,*mergevert;
|
||||
EditSelection *ese;
|
||||
|
||||
if(multires_test()) return 0;
|
||||
|
||||
/* do sanity check in mergemenu in edit.c ?*/
|
||||
if(first == 0){
|
||||
ese = G.editMesh->selected.last;
|
||||
mergevert= (EditVert*)ese->data;
|
||||
}
|
||||
else{
|
||||
ese = G.editMesh->selected.first;
|
||||
mergevert = (EditVert*)ese->data;
|
||||
}
|
||||
|
||||
if(mergevert->f&SELECT){
|
||||
for (eve=G.editMesh->verts.first; eve; eve=eve->next){
|
||||
if (eve->f&SELECT)
|
||||
copy_v3_v3(eve->co,mergevert->co);
|
||||
}
|
||||
}
|
||||
|
||||
if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){
|
||||
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
|
||||
if(eve->f&SELECT) eve->f1 = 1;
|
||||
}
|
||||
collapseuvs(mergevert);
|
||||
}
|
||||
|
||||
countall();
|
||||
return removedoublesflag(1, 0, MERGELIMIT);
|
||||
}
|
||||
|
||||
int merge_target(int target, int uvmerge)
|
||||
{
|
||||
EditVert *eve;
|
||||
|
||||
if(multires_test()) return 0;
|
||||
|
||||
if(target) snap_sel_to_curs();
|
||||
else snap_to_center();
|
||||
|
||||
if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
|
||||
if(eve->f&SELECT) eve->f1 = 1;
|
||||
}
|
||||
collapseuvs(NULL);
|
||||
}
|
||||
|
||||
countall();
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
return removedoublesflag(1, 0, MERGELIMIT);
|
||||
|
||||
}
|
||||
#undef MERGELIMIT
|
||||
|
||||
typedef struct PathNode{
|
||||
int u;
|
||||
int visited;
|
||||
ListBase edges;
|
||||
} PathNode;
|
||||
|
||||
typedef struct PathEdge{
|
||||
struct PathEdge *next, *prev;
|
||||
int v;
|
||||
float w;
|
||||
} PathEdge;
|
||||
|
||||
void pathselect(void)
|
||||
{
|
||||
EditVert *eve, *s, *t;
|
||||
EditEdge *eed;
|
||||
EditSelection *ese;
|
||||
PathEdge *newpe, *currpe;
|
||||
PathNode *currpn;
|
||||
PathNode *Q;
|
||||
int v, *previous, pathvert, pnindex; /*pnindex redundant?*/
|
||||
int unbalanced, totnodes;
|
||||
short physical;
|
||||
float *cost;
|
||||
Heap *heap; /*binary heap for sorting pointers to PathNodes based upon a 'cost'*/
|
||||
|
||||
s = t = NULL;
|
||||
|
||||
countall(); /*paranoid?*/
|
||||
|
||||
ese = ((EditSelection*)G.editMesh->selected.last);
|
||||
if(ese && ese->type == EDITVERT && ese->prev && ese->prev->type == EDITVERT){
|
||||
physical= pupmenu("Distance Method? %t|Edge Length%x1|Topological%x0");
|
||||
|
||||
t = (EditVert*)ese->data;
|
||||
s = (EditVert*)ese->prev->data;
|
||||
|
||||
/*need to find out if t is actually reachable by s....*/
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
|
||||
eve->f1 = 0;
|
||||
}
|
||||
|
||||
s->f1 = 1;
|
||||
|
||||
unbalanced = 1;
|
||||
totnodes = 1;
|
||||
while(unbalanced){
|
||||
unbalanced = 0;
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
|
||||
if(!eed->h){
|
||||
if(eed->v1->f1 && !eed->v2->f1){
|
||||
eed->v2->f1 = 1;
|
||||
totnodes++;
|
||||
unbalanced = 1;
|
||||
}
|
||||
else if(eed->v2->f1 && !eed->v1->f1){
|
||||
eed->v1->f1 = 1;
|
||||
totnodes++;
|
||||
unbalanced = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(s->f1 && t->f1){ /*t can be reached by s*/
|
||||
Q = MEM_callocN(sizeof(PathNode)*totnodes, "Path Select Nodes");
|
||||
totnodes = 0;
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
|
||||
if(eve->f1){
|
||||
Q[totnodes].u = totnodes;
|
||||
Q[totnodes].edges.first = 0;
|
||||
Q[totnodes].edges.last = 0;
|
||||
Q[totnodes].visited = 0;
|
||||
eve->tmp.p = &(Q[totnodes]);
|
||||
totnodes++;
|
||||
}
|
||||
else eve->tmp.p = NULL;
|
||||
}
|
||||
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
|
||||
if(!eed->h){
|
||||
if(eed->v1->f1){
|
||||
currpn = ((PathNode*)eed->v1->tmp.p);
|
||||
|
||||
newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge");
|
||||
newpe->v = ((PathNode*)eed->v2->tmp.p)->u;
|
||||
if(physical){
|
||||
newpe->w = len_v3v3(eed->v1->co, eed->v2->co);
|
||||
}
|
||||
else newpe->w = 1;
|
||||
newpe->next = 0;
|
||||
newpe->prev = 0;
|
||||
BLI_addtail(&(currpn->edges), newpe);
|
||||
}
|
||||
if(eed->v2->f1){
|
||||
currpn = ((PathNode*)eed->v2->tmp.p);
|
||||
newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge");
|
||||
newpe->v = ((PathNode*)eed->v1->tmp.p)->u;
|
||||
if(physical){
|
||||
newpe->w = len_v3v3(eed->v1->co, eed->v2->co);
|
||||
}
|
||||
else newpe->w = 1;
|
||||
newpe->next = 0;
|
||||
newpe->prev = 0;
|
||||
BLI_addtail(&(currpn->edges), newpe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
heap = BLI_heap_new();
|
||||
cost = MEM_callocN(sizeof(float)*totnodes, "Path Select Costs");
|
||||
previous = MEM_callocN(sizeof(int)*totnodes, "PathNode indices");
|
||||
|
||||
for(v=0; v < totnodes; v++){
|
||||
cost[v] = 1000000;
|
||||
previous[v] = -1; /*array of indices*/
|
||||
}
|
||||
|
||||
pnindex = ((PathNode*)s->tmp.p)->u;
|
||||
cost[pnindex] = 0;
|
||||
BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(pnindex));
|
||||
|
||||
while( !BLI_heap_empty(heap) ){
|
||||
|
||||
pnindex = GET_INT_FROM_POINTER(BLI_heap_popmin(heap));
|
||||
currpn = &(Q[pnindex]);
|
||||
|
||||
if(currpn == (PathNode*)t->tmp.p) /*target has been reached....*/
|
||||
break;
|
||||
|
||||
for(currpe=currpn->edges.first; currpe; currpe=currpe->next){
|
||||
if(!Q[currpe->v].visited){
|
||||
if( cost[currpe->v] > (cost[currpn->u ] + currpe->w) ){
|
||||
cost[currpe->v] = cost[currpn->u] + currpe->w;
|
||||
previous[currpe->v] = currpn->u;
|
||||
Q[currpe->v].visited = 1;
|
||||
BLI_heap_insert(heap, cost[currpe->v], SET_INT_IN_POINTER(currpe->v));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pathvert = ((PathNode*)t->tmp.p)->u;
|
||||
while(pathvert != -1){
|
||||
for(eve=G.editMesh->verts.first; eve; eve=eve->next){
|
||||
if(eve->f1){
|
||||
if( ((PathNode*)eve->tmp.p)->u == pathvert) eve->f |= SELECT;
|
||||
}
|
||||
}
|
||||
pathvert = previous[pathvert];
|
||||
}
|
||||
|
||||
for(v=0; v < totnodes; v++) BLI_freelistN(&(Q[v].edges));
|
||||
MEM_freeN(Q);
|
||||
MEM_freeN(cost);
|
||||
MEM_freeN(previous);
|
||||
BLI_heap_free(heap, NULL);
|
||||
EM_select_flush();
|
||||
countall();
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
if (EM_texFaceCheck())
|
||||
allqueue(REDRAWIMAGE, 0);
|
||||
}
|
||||
}
|
||||
else{
|
||||
error("Path Selection requires that exactly two vertices be selected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void region_to_loop(void)
|
||||
{
|
||||
EditEdge *eed;
|
||||
EditFace *efa;
|
||||
|
||||
if(G.totfacesel){
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
|
||||
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
if(efa->f&SELECT){
|
||||
efa->e1->f1++;
|
||||
efa->e2->f1++;
|
||||
efa->e3->f1++;
|
||||
if(efa->e4)
|
||||
efa->e4->f1++;
|
||||
}
|
||||
}
|
||||
|
||||
EM_clear_flag_all(SELECT);
|
||||
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next){
|
||||
if(eed->f1 == 1) EM_select_edge(eed, 1);
|
||||
}
|
||||
|
||||
G.scene->selectmode = SCE_SELECT_EDGE;
|
||||
EM_selectmode_set();
|
||||
countall();
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
if (EM_texFaceCheck())
|
||||
allqueue(REDRAWIMAGE, 0);
|
||||
BIF_undo_push("Face Region to Edge Loop");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int validate_loop(Collection *edgecollection)
|
||||
{
|
||||
EditEdge *eed;
|
||||
EditFace *efa;
|
||||
CollectedEdge *curredge;
|
||||
|
||||
/*1st test*/
|
||||
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
|
||||
curredge->eed->v1->f1 = 0;
|
||||
curredge->eed->v2->f1 = 0;
|
||||
}
|
||||
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
|
||||
curredge->eed->v1->f1++;
|
||||
curredge->eed->v2->f1++;
|
||||
}
|
||||
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
|
||||
if(curredge->eed->v1->f1 > 2) return(0); else
|
||||
if(curredge->eed->v2->f1 > 2) return(0);
|
||||
}
|
||||
|
||||
/*2nd test*/
|
||||
for(eed = G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
efa->e1->f1++;
|
||||
efa->e2->f1++;
|
||||
efa->e3->f1++;
|
||||
if(efa->e4) efa->e4->f1++;
|
||||
}
|
||||
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
|
||||
if(curredge->eed->f1 > 2) return(0);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
static int loop_bisect(Collection *edgecollection)
|
||||
{
|
||||
|
||||
EditFace *efa, *sf1, *sf2;
|
||||
EditEdge *eed, *sed;
|
||||
CollectedEdge *curredge;
|
||||
int totsf1, totsf2, unbalanced,balancededges;
|
||||
|
||||
for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = eed->f2 = 0;
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next) efa->f1 = 0;
|
||||
|
||||
for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next) curredge->eed->f1 = 1;
|
||||
|
||||
sf1 = sf2 = NULL;
|
||||
sed = ((CollectedEdge*)edgecollection->collectionbase.first)->eed;
|
||||
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
if(sf2) break;
|
||||
else if(sf1){
|
||||
if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf2 = efa;
|
||||
}
|
||||
else{
|
||||
if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf1 = efa;
|
||||
}
|
||||
}
|
||||
|
||||
if(sf1==NULL || sf2==NULL)
|
||||
return(-1);
|
||||
|
||||
if(!(sf1->e1->f1)) sf1->e1->f2 = 1;
|
||||
if(!(sf1->e2->f1)) sf1->e2->f2 = 1;
|
||||
if(!(sf1->e3->f1)) sf1->e3->f2 = 1;
|
||||
if(sf1->e4 && !(sf1->e4->f1)) sf1->e4->f2 = 1;
|
||||
sf1->f1 = 1;
|
||||
totsf1 = 1;
|
||||
|
||||
if(!(sf2->e1->f1)) sf2->e1->f2 = 2;
|
||||
if(!(sf2->e2->f1)) sf2->e2->f2 = 2;
|
||||
if(!(sf2->e3->f1)) sf2->e3->f2 = 2;
|
||||
if(sf2->e4 && !(sf2->e4->f1)) sf2->e4->f2 = 2;
|
||||
sf2->f1 = 2;
|
||||
totsf2 = 1;
|
||||
|
||||
/*do sf1*/
|
||||
unbalanced = 1;
|
||||
while(unbalanced){
|
||||
unbalanced = 0;
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
balancededges = 0;
|
||||
if(efa->f1 == 0){
|
||||
if(efa->e1->f2 == 1 || efa->e2->f2 == 1 || efa->e3->f2 == 1 || ( (efa->e4) ? efa->e4->f2 == 1 : 0) ){
|
||||
balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 1;
|
||||
balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 1;
|
||||
balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 1;
|
||||
if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 1;
|
||||
if(balancededges){
|
||||
unbalanced = 1;
|
||||
efa->f1 = 1;
|
||||
totsf1++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*do sf2*/
|
||||
unbalanced = 1;
|
||||
while(unbalanced){
|
||||
unbalanced = 0;
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
balancededges = 0;
|
||||
if(efa->f1 == 0){
|
||||
if(efa->e1->f2 == 2 || efa->e2->f2 == 2 || efa->e3->f2 == 2 || ( (efa->e4) ? efa->e4->f2 == 2 : 0) ){
|
||||
balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 2;
|
||||
balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 2;
|
||||
balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 2;
|
||||
if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 2;
|
||||
if(balancededges){
|
||||
unbalanced = 1;
|
||||
efa->f1 = 2;
|
||||
totsf2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(totsf1 < totsf2) return(1);
|
||||
else return(2);
|
||||
}
|
||||
|
||||
void loop_to_region(void)
|
||||
{
|
||||
EditFace *efa;
|
||||
ListBase allcollections={NULL,NULL};
|
||||
Collection *edgecollection;
|
||||
int testflag;
|
||||
|
||||
build_edgecollection(&allcollections);
|
||||
|
||||
for(edgecollection = (Collection *)allcollections.first; edgecollection; edgecollection=edgecollection->next){
|
||||
if(validate_loop(edgecollection)){
|
||||
testflag = loop_bisect(edgecollection);
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){
|
||||
if(efa->f1 == testflag){
|
||||
if(efa->f&SELECT) EM_select_face(efa, 0);
|
||||
else EM_select_face(efa,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(efa=G.editMesh->faces.first; efa; efa=efa->next){ /*fix this*/
|
||||
if(efa->f&SELECT) EM_select_face(efa,1);
|
||||
}
|
||||
|
||||
countall();
|
||||
freecollections(&allcollections);
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
if (EM_texFaceCheck())
|
||||
allqueue(REDRAWIMAGE, 0);
|
||||
BIF_undo_push("Edge Loop to Face Region");
|
||||
}
|
||||
|
||||
|
||||
/* texface and vertex color editmode tools for the face menu */
|
||||
|
||||
void mesh_rotate_uvs(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
short change = 0, ccw;
|
||||
MTFace *tf;
|
||||
float u1, v1;
|
||||
|
||||
if (!EM_texFaceCheck()) {
|
||||
error("mesh has no uv/image layers");
|
||||
return;
|
||||
}
|
||||
|
||||
ccw = (G.qual == LR_SHIFTKEY);
|
||||
|
||||
for(efa=em->faces.first; efa; efa=efa->next) {
|
||||
if (efa->f & SELECT) {
|
||||
tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
|
||||
u1= tf->uv[0][0];
|
||||
v1= tf->uv[0][1];
|
||||
|
||||
if (ccw) {
|
||||
if(efa->v4) {
|
||||
tf->uv[0][0]= tf->uv[3][0];
|
||||
tf->uv[0][1]= tf->uv[3][1];
|
||||
|
||||
tf->uv[3][0]= tf->uv[2][0];
|
||||
tf->uv[3][1]= tf->uv[2][1];
|
||||
} else {
|
||||
tf->uv[0][0]= tf->uv[2][0];
|
||||
tf->uv[0][1]= tf->uv[2][1];
|
||||
}
|
||||
|
||||
tf->uv[2][0]= tf->uv[1][0];
|
||||
tf->uv[2][1]= tf->uv[1][1];
|
||||
|
||||
tf->uv[1][0]= u1;
|
||||
tf->uv[1][1]= v1;
|
||||
} else {
|
||||
tf->uv[0][0]= tf->uv[1][0];
|
||||
tf->uv[0][1]= tf->uv[1][1];
|
||||
|
||||
tf->uv[1][0]= tf->uv[2][0];
|
||||
tf->uv[1][1]= tf->uv[2][1];
|
||||
|
||||
if(efa->v4) {
|
||||
tf->uv[2][0]= tf->uv[3][0];
|
||||
tf->uv[2][1]= tf->uv[3][1];
|
||||
|
||||
tf->uv[3][0]= u1;
|
||||
tf->uv[3][1]= v1;
|
||||
}
|
||||
else {
|
||||
tf->uv[2][0]= u1;
|
||||
tf->uv[2][1]= v1;
|
||||
}
|
||||
}
|
||||
change = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (change) {
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
BIF_undo_push("Rotate UV face");
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_mirror_uvs(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
short change = 0, altaxis;
|
||||
MTFace *tf;
|
||||
float u1, v1;
|
||||
|
||||
if (!EM_texFaceCheck()) {
|
||||
error("mesh has no uv/image layers");
|
||||
return;
|
||||
}
|
||||
|
||||
altaxis = (G.qual == LR_SHIFTKEY);
|
||||
|
||||
for(efa=em->faces.first; efa; efa=efa->next) {
|
||||
if (efa->f & SELECT) {
|
||||
tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
|
||||
if (altaxis) {
|
||||
u1= tf->uv[1][0];
|
||||
v1= tf->uv[1][1];
|
||||
if(efa->v4) {
|
||||
|
||||
tf->uv[1][0]= tf->uv[2][0];
|
||||
tf->uv[1][1]= tf->uv[2][1];
|
||||
|
||||
tf->uv[2][0]= u1;
|
||||
tf->uv[2][1]= v1;
|
||||
|
||||
u1= tf->uv[3][0];
|
||||
v1= tf->uv[3][1];
|
||||
|
||||
tf->uv[3][0]= tf->uv[0][0];
|
||||
tf->uv[3][1]= tf->uv[0][1];
|
||||
|
||||
tf->uv[0][0]= u1;
|
||||
tf->uv[0][1]= v1;
|
||||
}
|
||||
else {
|
||||
tf->uv[1][0]= tf->uv[2][0];
|
||||
tf->uv[1][1]= tf->uv[2][1];
|
||||
tf->uv[2][0]= u1;
|
||||
tf->uv[2][1]= v1;
|
||||
}
|
||||
|
||||
} else {
|
||||
u1= tf->uv[0][0];
|
||||
v1= tf->uv[0][1];
|
||||
if(efa->v4) {
|
||||
|
||||
tf->uv[0][0]= tf->uv[1][0];
|
||||
tf->uv[0][1]= tf->uv[1][1];
|
||||
|
||||
tf->uv[1][0]= u1;
|
||||
tf->uv[1][1]= v1;
|
||||
|
||||
u1= tf->uv[3][0];
|
||||
v1= tf->uv[3][1];
|
||||
|
||||
tf->uv[3][0]= tf->uv[2][0];
|
||||
tf->uv[3][1]= tf->uv[2][1];
|
||||
|
||||
tf->uv[2][0]= u1;
|
||||
tf->uv[2][1]= v1;
|
||||
}
|
||||
else {
|
||||
tf->uv[0][0]= tf->uv[1][0];
|
||||
tf->uv[0][1]= tf->uv[1][1];
|
||||
tf->uv[1][0]= u1;
|
||||
tf->uv[1][1]= v1;
|
||||
}
|
||||
}
|
||||
change = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (change) {
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
BIF_undo_push("Mirror UV face");
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_rotate_colors(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
short change = 0, ccw;
|
||||
MCol tmpcol, *mcol;
|
||||
if (!EM_vertColorCheck()) {
|
||||
error("mesh has no color layers");
|
||||
return;
|
||||
}
|
||||
|
||||
ccw = (G.qual == LR_SHIFTKEY);
|
||||
|
||||
for(efa=em->faces.first; efa; efa=efa->next) {
|
||||
if (efa->f & SELECT) {
|
||||
mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
|
||||
tmpcol= mcol[0];
|
||||
|
||||
if (ccw) {
|
||||
if(efa->v4) {
|
||||
mcol[0]= mcol[3];
|
||||
mcol[3]= mcol[2];
|
||||
} else {
|
||||
mcol[0]= mcol[2];
|
||||
}
|
||||
mcol[2]= mcol[1];
|
||||
mcol[1]= tmpcol;
|
||||
} else {
|
||||
mcol[0]= mcol[1];
|
||||
mcol[1]= mcol[2];
|
||||
|
||||
if(efa->v4) {
|
||||
mcol[2]= mcol[3];
|
||||
mcol[3]= tmpcol;
|
||||
}
|
||||
else
|
||||
mcol[2]= tmpcol;
|
||||
}
|
||||
change = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (change) {
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
BIF_undo_push("Rotate Color face");
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_mirror_colors(void)
|
||||
{
|
||||
EditMesh *em = G.editMesh;
|
||||
EditFace *efa;
|
||||
short change = 0, altaxis;
|
||||
MCol tmpcol, *mcol;
|
||||
if (!EM_vertColorCheck()) {
|
||||
error("mesh has no color layers");
|
||||
return;
|
||||
}
|
||||
|
||||
altaxis = (G.qual == LR_SHIFTKEY);
|
||||
|
||||
for(efa=em->faces.first; efa; efa=efa->next) {
|
||||
if (efa->f & SELECT) {
|
||||
mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
|
||||
if (altaxis) {
|
||||
tmpcol= mcol[1];
|
||||
mcol[1]= mcol[2];
|
||||
mcol[2]= tmpcol;
|
||||
|
||||
if(efa->v4) {
|
||||
tmpcol= mcol[0];
|
||||
mcol[0]= mcol[3];
|
||||
mcol[3]= tmpcol;
|
||||
}
|
||||
} else {
|
||||
tmpcol= mcol[0];
|
||||
mcol[0]= mcol[1];
|
||||
mcol[1]= tmpcol;
|
||||
|
||||
if(efa->v4) {
|
||||
tmpcol= mcol[2];
|
||||
mcol[2]= mcol[3];
|
||||
mcol[3]= tmpcol;
|
||||
}
|
||||
}
|
||||
change = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (change) {
|
||||
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
||||
allqueue(REDRAWVIEW3D, 0);
|
||||
BIF_undo_push("Mirror Color face");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
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
@@ -0,0 +1,1215 @@
|
||||
/*some of this may come back, such as split face or split edge, if necassary for speed*/
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2004 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Geoffrey Bantle.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_eulers.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM Euler construction API.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_utildefines.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
/*********************************************************
|
||||
* "Euler API" *
|
||||
* *
|
||||
* *
|
||||
* Primitive construction operators for mesh tools. *
|
||||
* *
|
||||
**********************************************************/
|
||||
|
||||
|
||||
/*
|
||||
The functions in this file represent the 'primitive' or 'atomic' operators that
|
||||
mesh tools use to manipulate the topology of the structure.* The purpose of these
|
||||
functions is to provide a trusted set of operators to manipulate the mesh topology
|
||||
and which can also be combined together like building blocks to create more
|
||||
sophisticated tools. It needs to be stressed that NO manipulation of an existing
|
||||
mesh structure should be done outside of these functions.
|
||||
|
||||
In the BM system, each euler is named by an ancronym which describes what it actually does.
|
||||
Furthermore each Euler has a logical inverse. An important design criteria of all Eulers is that
|
||||
through a Euler's logical inverse you can 'undo' an operation. (Special note should
|
||||
be taken of bmesh_loop_reverse, which is its own inverse).
|
||||
|
||||
bmesh_MF/KF: Make Face and Kill Face
|
||||
bmesh_ME/KE: Make Edge and Kill Edge
|
||||
bmesh_MV/KV: Make Vert and Kill Vert
|
||||
bmesh_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert
|
||||
bmesh_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge
|
||||
bmesh_loop_reverse: Reverse a Polygon's loop cycle. (used for flip normals for one)
|
||||
|
||||
Using a combination of these eleven eulers any non-manifold modelling operation can be achieved.
|
||||
Each Euler operator has a detailed explanation of what is does in the comments preceding its
|
||||
code.
|
||||
|
||||
*The term "Euler Operator" is actually a misnomer when referring to a non-manifold
|
||||
data structure. Its use is in keeping with the convention established by others.
|
||||
|
||||
BMESH_TODO:
|
||||
-Make seperate 'debug levels' of validation
|
||||
-Add in the UnglueFaceRegionMakeVert and GlueFaceRegionKillVert eulers.
|
||||
|
||||
NOTE:
|
||||
-The functions in this file are notoriously difficult to debug and even understand sometimes.
|
||||
better code comments would be nice....
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*MAKE Eulers*/
|
||||
|
||||
/**
|
||||
* bmesh_MV
|
||||
*
|
||||
* MAKE VERT EULER:
|
||||
*
|
||||
* Makes a single loose vertex.
|
||||
*
|
||||
* Returns -
|
||||
* A BMVert pointer.
|
||||
*/
|
||||
|
||||
BMVert *bmesh_mv(BMesh *bm, const float vec[3])
|
||||
{
|
||||
BMVert *v = bmesh_addvertlist(bm, NULL);
|
||||
copy_v3_v3(v->co,vec);
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_ME
|
||||
*
|
||||
* MAKE EDGE EULER:
|
||||
*
|
||||
* Makes a single wire edge between two vertices.
|
||||
* If the caller does not want there to be duplicate
|
||||
* edges between the vertices, it is up to them to check
|
||||
* for this condition beforehand.
|
||||
*
|
||||
* Returns -
|
||||
* A BMEdge pointer.
|
||||
*/
|
||||
|
||||
BMEdge *bmesh_me(BMesh *bm, BMVert *v1, BMVert *v2)
|
||||
{
|
||||
BMEdge *e=NULL;
|
||||
BMNode *d1=NULL, *d2=NULL;
|
||||
int valance1=0, valance2=0, edok;
|
||||
|
||||
/*edge must be between two distinct vertices...*/
|
||||
if(v1 == v2) return NULL;
|
||||
|
||||
#ifndef bmesh_FASTEULER
|
||||
/*count valance of v1*/
|
||||
if(v1->e) {
|
||||
d1 = bmesh_disk_getpointer(v1->e,v1);
|
||||
if(d1) valance1 = bmesh_cycle_length(d1);
|
||||
else bmesh_error();
|
||||
}
|
||||
if(v2->e) {
|
||||
d2 = bmesh_disk_getpointer(v2->e,v2);
|
||||
if(d2) valance2 = bmesh_cycle_length(d2);
|
||||
else bmesh_error();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*go ahead and add*/
|
||||
e = bmesh_addedgelist(bm, v1, v2, NULL);
|
||||
bmesh_disk_append_edge(e, e->v1);
|
||||
bmesh_disk_append_edge(e, e->v2);
|
||||
|
||||
#ifndef bmesh_FASTEULER
|
||||
/*verify disk cycle lengths*/
|
||||
d1 = bmesh_disk_getpointer(e, e->v1);
|
||||
edok = bmesh_cycle_validate(valance1+1, d1);
|
||||
if(!edok) bmesh_error();
|
||||
d2 = bmesh_disk_getpointer(e, e->v2);
|
||||
edok = bmesh_cycle_validate(valance2+1, d2);
|
||||
if(!edok) bmesh_error();
|
||||
|
||||
/*verify that edge actually made it into the cycle*/
|
||||
edok = bmesh_disk_hasedge(v1, e);
|
||||
if(!edok) bmesh_error();
|
||||
edok = bmesh_disk_hasedge(v2, e);
|
||||
if(!edok) bmesh_error();
|
||||
#endif
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* bmesh_MF
|
||||
*
|
||||
* MAKE FACE EULER:
|
||||
* Takes a list of edge pointers which form a closed loop and makes a face
|
||||
* from them. The first edge in elist is considered to be the start of the
|
||||
* polygon, and v1 and v2 are its vertices and determine the winding of the face
|
||||
* Other than the first edge, no other assumptions are made about the order of edges
|
||||
* in the elist array. To verify that it is a single closed loop and derive the correct
|
||||
* order a simple series of verifications is done and all elements are visited.
|
||||
*
|
||||
* Returns -
|
||||
* A BMFace pointer
|
||||
*/
|
||||
|
||||
#define MF_CANDIDATE 1
|
||||
#define MF_VISITED 2
|
||||
#define MF_TAKEN 4
|
||||
|
||||
BMFace *bmesh_mf(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **elist, int len)
|
||||
{
|
||||
BMFace *f = NULL;
|
||||
BMEdge *curedge;
|
||||
BMVert *curvert, *tv, **vlist;
|
||||
int i, j, done, cont, edok;
|
||||
|
||||
if(len < 2) return NULL;
|
||||
|
||||
/*make sure that v1 and v2 are in elist[0]*/
|
||||
//if(bmesh_verts_in_edge(v1,v2,elist[0]) == 0)
|
||||
// return NULL;
|
||||
|
||||
/*clear euler flags*/
|
||||
for(i=0;i<len;i++) {
|
||||
BMNode *diskbase;
|
||||
BMEdge *curedge;
|
||||
BMVert *v1;
|
||||
int j;
|
||||
|
||||
for (j=0; j<2; j++) {
|
||||
int a, len=0;
|
||||
|
||||
v1 = j ? elist[i]->v2 : elist[i]->v1;
|
||||
diskbase = bmesh_disk_getpointer(v1->e, v1);
|
||||
len = bmesh_cycle_length(diskbase);
|
||||
|
||||
for(a=0,curedge=v1->e;a<len;a++,curedge = bmesh_disk_nextedge(curedge,v1)) {
|
||||
curedge->head.eflag1 = curedge->head.eflag2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0;i<len;i++) {
|
||||
elist[i]->head.eflag1 |= MF_CANDIDATE;
|
||||
|
||||
/*if elist[i] has a loop, count its radial length*/
|
||||
if(elist[i]->loop) elist[i]->head.eflag2 = bmesh_cycle_length(&(elist[i]->l->radial));
|
||||
else elist[i]->head.eflag2 = 0;
|
||||
}
|
||||
|
||||
/* For each vertex in each edge, it must have exactly two MF_CANDIDATE edges attached to it
|
||||
Note that this does not gauruntee that face is a single closed loop. At best it gauruntees
|
||||
that elist contains a finite number of seperate closed loops.
|
||||
*/
|
||||
// for(i=0; i<len; i++) {
|
||||
// edok = bmesh_disk_count_edgeflag(elist[i]->v1, MF_CANDIDATE, 0);
|
||||
// if(edok != 2) return NULL;
|
||||
// edok = bmesh_disk_count_edgeflag(elist[i]->v2, MF_CANDIDATE, 0);
|
||||
// if(edok != 2) return NULL;
|
||||
// }
|
||||
|
||||
/*set start edge, start vert and target vert for our loop traversal*/
|
||||
curedge = elist[0];
|
||||
tv = v1;
|
||||
curvert = v2;
|
||||
|
||||
if(bm->vtarlen < len) {
|
||||
if (bm->vtar) MEM_freeN(bm->vtar);
|
||||
bm->vtar = MEM_callocN(sizeof(BMVert *)* len, "BM Vert pointer array");
|
||||
bm->vtarlen = len;
|
||||
}
|
||||
/*insert tv into vlist since its the first vertex in face*/
|
||||
|
||||
i=0;
|
||||
vlist=bm->vtar;
|
||||
vlist[i] = tv;
|
||||
|
||||
/* Basic procedure: Starting with curv we find the edge in it's disk cycle which hasn't
|
||||
been visited yet. When we do, we put curv in a linked list and find the next MF_CANDIDATE
|
||||
edge, loop until we find TV. We know TV is reachable because of test we did earlier.
|
||||
*/
|
||||
done=0;
|
||||
while(!done) {
|
||||
/*add curvert to vlist*/
|
||||
/*insert some error cheking here for overflows*/
|
||||
i++;
|
||||
vlist[i] = curvert;
|
||||
|
||||
/*mark curedge as visited*/
|
||||
curedge->head.eflag1 |= MF_VISITED;
|
||||
|
||||
/*find next edge and vert*/
|
||||
curedge = bmesh_disk_next_edgeflag(curedge, curvert, MF_CANDIDATE, 0);
|
||||
curvert = bmesh_edge_getothervert(curedge, curvert);
|
||||
if(curvert == tv) {
|
||||
curedge->head.eflag1 |= MF_VISITED;
|
||||
done=1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that all edges have been visited It's possible that we did reach tv
|
||||
from sv, but that several unconnected loops were passed in via elist.
|
||||
*/
|
||||
cont=1;
|
||||
// for(i=0; i<len; i++) {
|
||||
// if((elist[i]->head.eflag1 & MF_VISITED) == 0) cont = 0;
|
||||
// }
|
||||
|
||||
/*if we get this far, its ok to allocate the face and add the loops*/
|
||||
if(cont) {
|
||||
BMLoop *l;
|
||||
BMEdge *e;
|
||||
f = bmesh_addpolylist(bm, NULL);
|
||||
f->len = len;
|
||||
for(i=0;i<len;i++) {
|
||||
curvert = vlist[i];
|
||||
l = bmesh_create_loop(bm,curvert,NULL,f,NULL);
|
||||
if(!(f->loopbase)) f->lbase = l;
|
||||
bmesh_cycle_append(f->lbase, l);
|
||||
}
|
||||
|
||||
/*take care of edge pointers and radial cycle*/
|
||||
for(i=0, l = f->loopbase; i<len; i++, l= l->next) {
|
||||
e = NULL;
|
||||
if(l == f->loopbase) e = elist[0]; /*first edge*/
|
||||
|
||||
else {/*search elist for others*/
|
||||
for(j=1; j<len; j++) {
|
||||
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, elist[j]);
|
||||
if(edok) {
|
||||
e = elist[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
l->e = e; /*set pointer*/
|
||||
bmesh_radial_append(e, l); /*append into radial*/
|
||||
}
|
||||
|
||||
f->len = len;
|
||||
|
||||
/*Validation Loop cycle*/
|
||||
edok = bmesh_cycle_validate(len, f->lbase);
|
||||
if(!edok) bmesh_error();
|
||||
for(i=0, l = f->loopbase; i<len; i++, l=((l->next))) {
|
||||
/*validate loop vert pointers*/
|
||||
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, l->e);
|
||||
if(!edok) bmesh_error();
|
||||
/*validate the radial cycle of each edge*/
|
||||
edok = bmesh_cycle_length(&(l->radial));
|
||||
if(edok != (l->e->head.eflag2 + 1)) bmesh_error();
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0;i<len;i++) elist[i]->head.eflag1=elist[i]->head.eflag2 = 0;
|
||||
return f;
|
||||
}
|
||||
|
||||
/* KILL Eulers */
|
||||
|
||||
/**
|
||||
* bmesh_KV
|
||||
*
|
||||
* KILL VERT EULER:
|
||||
*
|
||||
* Kills a single loose vertex.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
int bmesh_kv(BMesh *bm, BMVert *v)
|
||||
{
|
||||
if(v->e == NULL) {
|
||||
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) bm->totvertsel--;
|
||||
|
||||
BLI_remlink(&(bm->verts), &(v->head));
|
||||
bmesh_free_vert(bm,v);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_KE
|
||||
*
|
||||
* KILL EDGE EULER:
|
||||
*
|
||||
* Kills a wire edge.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
int bmesh_ke(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
int edok;
|
||||
|
||||
/*Make sure that no faces!*/
|
||||
if(e->l == NULL) {
|
||||
bmesh_disk_remove_edge(e, e->v1);
|
||||
bmesh_disk_remove_edge(e, e->v2);
|
||||
|
||||
/*verify that edge out of disk*/
|
||||
edok = bmesh_disk_hasedge(e->v1, e);
|
||||
if(edok) bmesh_error();
|
||||
edok = bmesh_disk_hasedge(e->v2, e);
|
||||
if(edok) bmesh_error();
|
||||
|
||||
/*remove and deallocate*/
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel--;
|
||||
BLI_remlink(&(bm->edges), &(e->head));
|
||||
bmesh_free_edge(bm, e);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_KF
|
||||
*
|
||||
* KILL FACE EULER:
|
||||
*
|
||||
* The logical inverse of bmesh_MF.
|
||||
* Kills a face and removes each of its loops from the radial that it belongs to.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
int bmesh_kf(BMesh *bm, BMFace *bply)
|
||||
{
|
||||
BMLoop *newbase,*oldbase, *curloop;
|
||||
int i,len=0;
|
||||
|
||||
/*add validation to make sure that radial cycle is cleaned up ok*/
|
||||
/*deal with radial cycle first*/
|
||||
len = bmesh_cycle_length(bply->lbase);
|
||||
for(i=0, curloop=bply->loopbase; i < len; i++, curloop = ((curloop->next)))
|
||||
bmesh_radial_remove_loop(curloop, curloop->e);
|
||||
|
||||
/*now deallocate the editloops*/
|
||||
for(i=0; i < len; i++) {
|
||||
newbase = ((bply->lbase->next));
|
||||
oldbase = bply->lbase;
|
||||
bmesh_cycle_remove(oldbase, oldbase);
|
||||
bmesh_free_loop(bm, oldbase);
|
||||
bply->loopbase = newbase;
|
||||
}
|
||||
|
||||
if (BM_elem_flag_test(bply, BM_ELEM_SELECT)) bm->totfacesel--;
|
||||
BLI_remlink(&(bm->polys), &(bply->head));
|
||||
bmesh_free_poly(bm, bply);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*SPLIT Eulers*/
|
||||
|
||||
/**
|
||||
* bmesh_SEMV
|
||||
*
|
||||
* SPLIT EDGE MAKE VERT:
|
||||
* Takes a given edge and splits it into two, creating a new vert.
|
||||
*
|
||||
*
|
||||
* Before: OV---------TV
|
||||
* After: OV----NV---TV
|
||||
*
|
||||
* Returns -
|
||||
* BMVert pointer.
|
||||
*
|
||||
*/
|
||||
|
||||
BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re)
|
||||
{
|
||||
BMVert *nv, *ov;
|
||||
BMNode *diskbase;
|
||||
BMEdge *ne;
|
||||
int i, edok, valance1=0, valance2=0;
|
||||
|
||||
if(bmesh_vert_in_edge(e,tv) == 0) return NULL;
|
||||
ov = bmesh_edge_getothervert(e,tv);
|
||||
//v2 = tv;
|
||||
|
||||
/*count valance of v1*/
|
||||
diskbase = bmesh_disk_getpointer(e, ov);
|
||||
valance1 = bmesh_cycle_length(diskbase);
|
||||
/*count valance of v2*/
|
||||
diskbase = bmesh_disk_getpointer(e, tv);
|
||||
valance2 = bmesh_cycle_length(diskbase);
|
||||
|
||||
nv = bmesh_addvertlist(bm, tv);
|
||||
ne = bmesh_addedgelist(bm, nv, tv, e);
|
||||
|
||||
//e->v2 = nv;
|
||||
/*remove e from v2's disk cycle*/
|
||||
bmesh_disk_remove_edge(e, tv);
|
||||
/*swap out tv for nv in e*/
|
||||
bmesh_edge_swapverts(e, tv, nv);
|
||||
/*add e to nv's disk cycle*/
|
||||
bmesh_disk_append_edge(e, nv);
|
||||
/*add ne to nv's disk cycle*/
|
||||
bmesh_disk_append_edge(ne, nv);
|
||||
/*add ne to tv's disk cycle*/
|
||||
bmesh_disk_append_edge(ne, tv);
|
||||
/*verify disk cycles*/
|
||||
diskbase = bmesh_disk_getpointer(ov->e,ov);
|
||||
edok = bmesh_cycle_validate(valance1, diskbase);
|
||||
if(!edok) bmesh_error();
|
||||
diskbase = bmesh_disk_getpointer(tv->e,tv);
|
||||
edok = bmesh_cycle_validate(valance2, diskbase);
|
||||
if(!edok) bmesh_error();
|
||||
diskbase = bmesh_disk_getpointer(nv->e,nv);
|
||||
edok = bmesh_cycle_validate(2, diskbase);
|
||||
if(!edok) bmesh_error();
|
||||
|
||||
/*Split the radial cycle if present*/
|
||||
if(e->l) {
|
||||
BMLoop *nl,*l;
|
||||
BMNode *radEBase=NULL, *radNEBase=NULL;
|
||||
int radlen = bmesh_cycle_length(&(e->l->radial));
|
||||
/*Take the next loop. Remove it from radial. Split it. Append to appropriate radials.*/
|
||||
while(e->l) {
|
||||
l=e->l;
|
||||
l->f->len++;
|
||||
bmesh_radial_remove_loop(l,e);
|
||||
|
||||
nl = bmesh_create_loop(bm,NULL,NULL,l->f,l);
|
||||
nl->prev = (BMHeader*)l;
|
||||
nl->next = (BMHeader*)(l->next);
|
||||
nl->prev->next = (BMHeader*)nl;
|
||||
nl->next->prev = (BMHeader*)nl;
|
||||
nl->v = nv;
|
||||
|
||||
/*assign the correct edge to the correct loop*/
|
||||
if(bmesh_verts_in_edge(nl->v, ((nl->next))->v, e)) {
|
||||
nl->e = e;
|
||||
l->e = ne;
|
||||
|
||||
/*append l into ne's rad cycle*/
|
||||
if(!radNEBase) {
|
||||
radNEBase = &(l->radial);
|
||||
radNEBase->next = NULL;
|
||||
radNEBase->prev = NULL;
|
||||
}
|
||||
|
||||
if(!radEBase) {
|
||||
radEBase = &(nl->radial);
|
||||
radEBase->next = NULL;
|
||||
radEBase->prev = NULL;
|
||||
}
|
||||
|
||||
bmesh_cycle_append(radEBase,&(nl->radial));
|
||||
bmesh_cycle_append(radNEBase,&(l->radial));
|
||||
|
||||
}
|
||||
else if(bmesh_verts_in_edge(nl->v,((nl->next))->v,ne)) {
|
||||
nl->e = ne;
|
||||
l->e = e;
|
||||
|
||||
if(!radNEBase) {
|
||||
radNEBase = &(nl->radial);
|
||||
radNEBase->next = NULL;
|
||||
radNEBase->prev = NULL;
|
||||
}
|
||||
if(!radEBase) {
|
||||
radEBase = &(l->radial);
|
||||
radEBase->next = NULL;
|
||||
radEBase->prev = NULL;
|
||||
}
|
||||
bmesh_cycle_append(radEBase,&(l->radial));
|
||||
bmesh_cycle_append(radNEBase,&(nl->radial));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
e->l = radEBase->data;
|
||||
ne->l = radNEBase->data;
|
||||
|
||||
/*verify length of radial cycle*/
|
||||
edok = bmesh_cycle_validate(radlen,&(e->l->radial));
|
||||
if(!edok) bmesh_error();
|
||||
edok = bmesh_cycle_validate(radlen,&(ne->l->radial));
|
||||
if(!edok) bmesh_error();
|
||||
|
||||
/*verify loop->v and loop->next->v pointers for e*/
|
||||
for(i=0,l=e->l; i < radlen; i++, l = l->radial_next) {
|
||||
if(!(l->e == e)) bmesh_error();
|
||||
if(!(l->radial.data == l)) bmesh_error();
|
||||
if( ((l->prev))->e != ne && ((l->next))->e != ne) bmesh_error();
|
||||
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, e);
|
||||
if(!edok) bmesh_error();
|
||||
if(l->v == ((l->next))->v) bmesh_error();
|
||||
if(l->e == ((l->next))->e) bmesh_error();
|
||||
/*verify loop cycle for kloop->f*/
|
||||
edok = bmesh_cycle_validate(l->f->len, l->f->lbase);
|
||||
if(!edok) bmesh_error();
|
||||
}
|
||||
/*verify loop->v and loop->next->v pointers for ne*/
|
||||
for(i=0,l=ne->l; i < radlen; i++, l = l->radial_next) {
|
||||
if(!(l->e == ne)) bmesh_error();
|
||||
if(!(l->radial.data == l)) bmesh_error();
|
||||
if( ((l->prev))->e != e && ((l->next))->e != e) bmesh_error();
|
||||
edok = bmesh_verts_in_edge(l->v, ((l->next))->v, ne);
|
||||
if(!edok) bmesh_error();
|
||||
if(l->v == ((l->next))->v) bmesh_error();
|
||||
if(l->e == ((l->next))->e) bmesh_error();
|
||||
/*verify loop cycle for kloop->f. Redundant*/
|
||||
edok = bmesh_cycle_validate(l->f->len, l->f->lbase);
|
||||
if(!edok) bmesh_error();
|
||||
}
|
||||
}
|
||||
|
||||
if(re) *re = ne;
|
||||
return nv;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_SFME
|
||||
*
|
||||
* SPLIT FACE MAKE EDGE:
|
||||
*
|
||||
* Takes as input two vertices in a single face. An edge is created which divides the original face
|
||||
* into two distinct regions. One of the regions is assigned to the original face and it is closed off.
|
||||
* The second region has a new face assigned to it.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Before: After:
|
||||
* ---------- ----------
|
||||
* | | | |
|
||||
* | | | f1 |
|
||||
* v1 f1 v2 v1======v2
|
||||
* | | | f2 |
|
||||
* | | | |
|
||||
* ---------- ----------
|
||||
*
|
||||
* Note that the input vertices can be part of the same edge. This will result in a two edged face.
|
||||
* This is desirable for advanced construction tools and particularly essential for edge bevel. Because
|
||||
* of this it is up to the caller to decide what to do with the extra edge.
|
||||
*
|
||||
* Note that the tesselator abuses eflag2 while using this euler! (don't ever ever do this....)
|
||||
*
|
||||
* Returns -
|
||||
* A BMFace pointer
|
||||
*/
|
||||
BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **rl)
|
||||
{
|
||||
|
||||
BMFace *f2;
|
||||
BMLoop *v1loop = NULL, *v2loop = NULL, *curloop, *f1loop=NULL, *f2loop=NULL;
|
||||
BMEdge *e;
|
||||
int i, len, f1len, f2len;
|
||||
|
||||
|
||||
/*verify that v1 and v2 are in face.*/
|
||||
len = bmesh_cycle_length(f->lbase);
|
||||
for(i = 0, curloop = f->loopbase; i < len; i++, curloop = ((curloop->next)) ) {
|
||||
if(curloop->v == v1) v1loop = curloop;
|
||||
else if(curloop->v == v2) v2loop = curloop;
|
||||
}
|
||||
|
||||
if(!v1loop || !v2loop) return NULL;
|
||||
|
||||
/*allocate new edge between v1 and v2*/
|
||||
e = bmesh_addedgelist(bm, v1, v2,NULL);
|
||||
bmesh_disk_append_edge(e, v1);
|
||||
bmesh_disk_append_edge(e, v2);
|
||||
|
||||
f2 = bmesh_addpolylist(bm,f);
|
||||
f1loop = bmesh_create_loop(bm,v2,e,f,v2loop);
|
||||
f2loop = bmesh_create_loop(bm,v1,e,f2,v1loop);
|
||||
|
||||
f1loop->prev = v2loop->prev;
|
||||
f2loop->prev = v1loop->prev;
|
||||
v2loop->prev->next = (BMHeader*)f1loop;
|
||||
v1loop->prev->next = (BMHeader*)f2loop;
|
||||
|
||||
f1loop->next = (BMHeader*)v1loop;
|
||||
f2loop->next = (BMHeader*)v2loop;
|
||||
v1loop->prev = (BMHeader*)f1loop;
|
||||
v2loop->prev = (BMHeader*)f2loop;
|
||||
|
||||
f2->loopbase = f2loop;
|
||||
f->loopbase = f1loop;
|
||||
|
||||
/*validate both loops*/
|
||||
/*I dont know how many loops are supposed to be in each face at this point! FIXME!*/
|
||||
|
||||
/*go through all of f2's loops and make sure they point to it properly.*/
|
||||
f2len = bmesh_cycle_length(f2->lbase);
|
||||
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->f = f2;
|
||||
|
||||
/*link up the new loops into the new edges radial*/
|
||||
bmesh_radial_append(e, f1loop);
|
||||
bmesh_radial_append(e, f2loop);
|
||||
|
||||
|
||||
f2->len = f2len;
|
||||
|
||||
f1len = bmesh_cycle_length(f->lbase);
|
||||
f->len = f1len;
|
||||
|
||||
if(rl) *rl = f2loop;
|
||||
return f2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bmesh_JEKV
|
||||
*
|
||||
* JOIN EDGE KILL VERT:
|
||||
* Takes a an edge and pointer to one of its vertices and collapses
|
||||
* the edge on that vertex.
|
||||
*
|
||||
* Before: OE KE
|
||||
* ------- -------
|
||||
* | || |
|
||||
* OV KV TV
|
||||
*
|
||||
*
|
||||
* After: OE
|
||||
* ---------------
|
||||
* | |
|
||||
* OV TV
|
||||
*
|
||||
*
|
||||
* Restrictions:
|
||||
* KV is a vertex that must have a valance of exactly two. Furthermore
|
||||
* both edges in KV's disk cycle (OE and KE) must be unique (no double
|
||||
* edges).
|
||||
*
|
||||
* It should also be noted that this euler has the possibility of creating
|
||||
* faces with just 2 edges. It is up to the caller to decide what to do with
|
||||
* these faces.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv)
|
||||
{
|
||||
BMEdge *oe;
|
||||
BMVert *ov, *tv;
|
||||
BMNode *diskbase;
|
||||
BMLoop *killoop,*nextl;
|
||||
int len,radlen=0, halt = 0, i, valance1, valance2,edok;
|
||||
|
||||
if(bmesh_vert_in_edge(ke,kv) == 0) return 0;
|
||||
diskbase = bmesh_disk_getpointer(kv->e, kv);
|
||||
len = bmesh_cycle_length(diskbase);
|
||||
|
||||
if(len == 2) {
|
||||
oe = bmesh_disk_nextedge(ke, kv);
|
||||
tv = bmesh_edge_getothervert(ke, kv);
|
||||
ov = bmesh_edge_getothervert(oe, kv);
|
||||
halt = bmesh_verts_in_edge(kv, tv, oe); //check for double edges
|
||||
|
||||
if(halt) return 0;
|
||||
else {
|
||||
|
||||
/*For verification later, count valance of ov and tv*/
|
||||
diskbase = bmesh_disk_getpointer(ov->e, ov);
|
||||
valance1 = bmesh_cycle_length(diskbase);
|
||||
diskbase = bmesh_disk_getpointer(tv->e, tv);
|
||||
valance2 = bmesh_cycle_length(diskbase);
|
||||
|
||||
/*remove oe from kv's disk cycle*/
|
||||
bmesh_disk_remove_edge(oe,kv);
|
||||
/*relink oe->kv to be oe->tv*/
|
||||
bmesh_edge_swapverts(oe, kv, tv);
|
||||
/*append oe to tv's disk cycle*/
|
||||
bmesh_disk_append_edge(oe, tv);
|
||||
/*remove ke from tv's disk cycle*/
|
||||
bmesh_disk_remove_edge(ke, tv);
|
||||
|
||||
|
||||
|
||||
/*deal with radial cycle of ke*/
|
||||
if(ke->l) {
|
||||
/*first step, fix the neighboring loops of all loops in ke's radial cycle*/
|
||||
radlen = bmesh_cycle_length(&(ke->l->radial));
|
||||
for(i=0,killoop = ke->l; i<radlen; i++, killoop = bmesh_radial_nextloop(killoop)) {
|
||||
/*relink loops and fix vertex pointer*/
|
||||
killoop->next->prev = killoop->prev;
|
||||
killoop->prev->next = killoop->next;
|
||||
if( ((killoop->next))->v == kv) ((killoop->next))->v = tv;
|
||||
|
||||
/*fix len attribute of face*/
|
||||
killoop->f->len--;
|
||||
if(killoop->f->loopbase == killoop) killoop->f->lbase = ((killoop->next));
|
||||
}
|
||||
/*second step, remove all the hanging loops attached to ke*/
|
||||
killoop = ke->l;
|
||||
radlen = bmesh_cycle_length(&(ke->l->radial));
|
||||
/*make sure we have enough room in bm->lpar*/
|
||||
if(bm->lparlen < radlen) {
|
||||
MEM_freeN(bm->lpar);
|
||||
bm->lpar = MEM_callocN(sizeof(BMLoop *)* radlen, "BM Loop pointer array");
|
||||
bm->lparlen = bm->lparlen * radlen;
|
||||
}
|
||||
/*this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well...*/
|
||||
i=0;
|
||||
while(i<radlen) {
|
||||
bm->lpar[i] = killoop;
|
||||
killoop = killoop->radial_next;
|
||||
i++;
|
||||
}
|
||||
i=0;
|
||||
while(i<radlen) {
|
||||
bmesh_free_loop(bm,bm->lpar[i]);
|
||||
i++;
|
||||
}
|
||||
/*Validate radial cycle of oe*/
|
||||
edok = bmesh_cycle_validate(radlen,&(oe->l->radial));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*Validate disk cycles*/
|
||||
diskbase = bmesh_disk_getpointer(ov->e,ov);
|
||||
edok = bmesh_cycle_validate(valance1, diskbase);
|
||||
if(!edok) bmesh_error();
|
||||
diskbase = bmesh_disk_getpointer(tv->e,tv);
|
||||
edok = bmesh_cycle_validate(valance2, diskbase);
|
||||
if(!edok) bmesh_error();
|
||||
|
||||
/*Validate loop cycle of all faces attached to oe*/
|
||||
for(i=0,nextl = oe->l; i<radlen; i++, nextl = bmesh_radial_nextloop(nextl)) {
|
||||
edok = bmesh_cycle_validate(nextl->f->len,nextl->f->lbase);
|
||||
if(!edok) bmesh_error();
|
||||
}
|
||||
/*deallocate edge*/
|
||||
BLI_remlink(&(bm->edges), &(ke->head));
|
||||
bmesh_free_edge(bm, ke);
|
||||
/*deallocate vertex*/
|
||||
BLI_remlink(&(bm->verts), &(kv->head));
|
||||
bmesh_free_vert(bm, kv);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bmesh_loop_reverse
|
||||
*
|
||||
* FLIP FACE EULER
|
||||
*
|
||||
* Changes the winding order of a face from CW to CCW or vice versa.
|
||||
* This euler is a bit peculiar in compairson to others as it is its
|
||||
* own inverse.
|
||||
*
|
||||
* BMESH_TODO: reinsert validation code.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
int bmesh_loop_reverse(BMesh *bm, BMFace *f)
|
||||
{
|
||||
BMLoop *l = f->loopbase, *curloop, *oldprev, *oldnext;
|
||||
int i, j, edok, len = 0;
|
||||
|
||||
len = bmesh_cycle_length(l);
|
||||
if(bm->edarlen < len) {
|
||||
MEM_freeN(bm->edar);
|
||||
bm->edar = MEM_callocN(sizeof(BMEdge *)* len, "BM Edge pointer array");
|
||||
bm->edarlen = len;
|
||||
}
|
||||
|
||||
for(i=0, curloop = l; i< len; i++, curloop= ((curloop->next)) ) {
|
||||
curloop->e->head.eflag1 = 0;
|
||||
curloop->e->head.eflag2 = bmesh_cycle_length(&curloop->radial);
|
||||
bmesh_radial_remove_loop(curloop, curloop->e);
|
||||
/*in case of border edges we HAVE to zero out curloop->radial Next/Prev*/
|
||||
curloop->radial.next = curloop->radial.prev = NULL;
|
||||
bm->edar[i] = curloop->e;
|
||||
}
|
||||
|
||||
/*actually reverse the loop. This belongs in bmesh_cycle_reverse!*/
|
||||
for(i=0, curloop = l; i < len; i++) {
|
||||
oldnext = ((curloop->next));
|
||||
oldprev = ((curloop->prev));
|
||||
curloop->next = (BMHeader*)oldprev;
|
||||
curloop->prev = (BMHeader*)oldnext;
|
||||
curloop = oldnext;
|
||||
}
|
||||
|
||||
if(len == 2) { //two edged face
|
||||
//do some verification here!
|
||||
l->e = bm->edar[1];
|
||||
((l->next))->e = bm->edar[0];
|
||||
}
|
||||
else {
|
||||
for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) {
|
||||
edok = 0;
|
||||
for(j=0; j < len; j++) {
|
||||
edok = bmesh_verts_in_edge(curloop->v, ((curloop->next))->v, bm->edar[j]);
|
||||
if(edok) {
|
||||
curloop->e = bm->edar[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*rebuild radial*/
|
||||
for(i=0, curloop = l; i < len; i++, curloop = curloop->next ) bmesh_radial_append(curloop->e, curloop);
|
||||
|
||||
/*validate radial*/
|
||||
for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) {
|
||||
edok = bmesh_cycle_validate(curloop->e->head.eflag2, &(curloop->radial));
|
||||
if(!edok) {
|
||||
bmesh_error();
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_JFKE
|
||||
*
|
||||
* JOIN FACE KILL EDGE:
|
||||
*
|
||||
* Takes two faces joined by a single 2-manifold edge and fuses them togather.
|
||||
* The edge shared by the faces must not be connected to any other edges which have
|
||||
* Both faces in its radial cycle
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* A B
|
||||
* ---------- ----------
|
||||
* | | | |
|
||||
* | f1 | | f1 |
|
||||
* v1========v2 = Ok! v1==V2==v3 == Wrong!
|
||||
* | f2 | | f2 |
|
||||
* | | | |
|
||||
* ---------- ----------
|
||||
*
|
||||
* In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used.
|
||||
* In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller
|
||||
* in this case should call bmesh_JEKV on the extra edges before attempting to fuse f1 and f2.
|
||||
*
|
||||
* Also note that the order of arguments decides whether or not certain per-face attributes are present
|
||||
* in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited
|
||||
* from f1, not f2.
|
||||
*
|
||||
* Returns -
|
||||
* A BMFace pointer
|
||||
*/
|
||||
|
||||
//disregarding f1loop and f2loop, if a vertex appears in a joined face more than once, we cancel
|
||||
|
||||
BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
|
||||
{
|
||||
|
||||
BMLoop *curloop, *f1loop=NULL, *f2loop=NULL;
|
||||
int loopok = 0, newlen = 0,i, f1len=0, f2len=0, radlen=0, edok, shared;
|
||||
|
||||
if(f1 == f2) return NULL; //can't join a face to itself
|
||||
/*verify that e is in both f1 and f2*/
|
||||
f1len = bmesh_cycle_length(f1->lbase);
|
||||
f2len = bmesh_cycle_length(f2->lbase);
|
||||
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) {
|
||||
if(curloop->e == e) {
|
||||
f1loop = curloop;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) {
|
||||
if(curloop->e==e) {
|
||||
f2loop = curloop;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!(f1loop && f2loop)) return NULL;
|
||||
|
||||
/*validate that edge is 2-manifold edge*/
|
||||
radlen = bmesh_cycle_length(&(f1loop->radial));
|
||||
if(radlen != 2) return NULL;
|
||||
|
||||
/*validate direction of f2's loop cycle is compatible.*/
|
||||
if(f1loop->v == f2loop->v) return NULL;
|
||||
|
||||
/*
|
||||
validate that for each face, each vertex has another edge in its disk cycle that is
|
||||
not e, and not shared.
|
||||
*/
|
||||
if(bmesh_radial_find_face( ((f1loop->next))->e,f2)) return NULL;
|
||||
if(bmesh_radial_find_face( ((f1loop->prev))->e,f2)) return NULL;
|
||||
if(bmesh_radial_find_face( ((f2loop->next))->e,f1)) return NULL;
|
||||
if(bmesh_radial_find_face( ((f2loop->prev))->e,f1)) return NULL;
|
||||
|
||||
/*validate only one shared edge*/
|
||||
shared = BM_face_share_edges(f1,f2);
|
||||
if(shared > 1) return NULL;
|
||||
|
||||
/*validate no internal joins*/
|
||||
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0;
|
||||
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0;
|
||||
|
||||
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) {
|
||||
if(curloop != f1loop)
|
||||
curloop->v->head.eflag1++;
|
||||
}
|
||||
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) {
|
||||
if(curloop != f2loop)
|
||||
curloop->v->head.eflag1++;
|
||||
}
|
||||
|
||||
for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) {
|
||||
if(curloop->v->head.eflag1 > 1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) {
|
||||
if(curloop->v->head.eflag1 > 1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*join the two loops*/
|
||||
f1loop->prev->next = f2loop->next;
|
||||
f2loop->next->prev = f1loop->prev;
|
||||
|
||||
f1loop->next->prev = f2loop->prev;
|
||||
f2loop->prev->next = f1loop->next;
|
||||
|
||||
/*if f1loop was baseloop, give f1loop->next the base.*/
|
||||
if(f1->loopbase == f1loop) f1->lbase = ((f1loop->next));
|
||||
|
||||
/*validate the new loop*/
|
||||
loopok = bmesh_cycle_validate((f1len+f2len)-2, f1->lbase);
|
||||
if(!loopok) bmesh_error();
|
||||
|
||||
/*make sure each loop points to the proper face*/
|
||||
newlen = bmesh_cycle_length(f1->lbase);
|
||||
for(i = 0, curloop = f1->loopbase; i < newlen; i++, curloop = ((curloop->next)) ) curloop->f = f1;
|
||||
|
||||
f1->len = newlen;
|
||||
|
||||
edok = bmesh_cycle_validate(f1->len, f1->lbase);
|
||||
if(!edok) bmesh_error();
|
||||
|
||||
/*remove edge from the disk cycle of its two vertices.*/
|
||||
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1);
|
||||
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2);
|
||||
|
||||
/*deallocate edge and its two loops as well as f2*/
|
||||
BLI_remlink(&(bm->edges), &(f1loop->e->head));
|
||||
BLI_remlink(&(bm->polys), &(f2->head));
|
||||
bmesh_free_edge(bm, f1loop->e);
|
||||
bmesh_free_loop(bm, f1loop);
|
||||
bmesh_free_loop(bm, f2loop);
|
||||
bmesh_free_poly(bm, f2);
|
||||
return f1;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_URMV
|
||||
*
|
||||
* UNGLUE REGION MAKE VERT:
|
||||
*
|
||||
* Takes a locally manifold disk of face corners and 'unglues' it
|
||||
* creating a new vertex
|
||||
*
|
||||
**/
|
||||
|
||||
#define URMV_VISIT 1
|
||||
#define URMV_VISIT2 2
|
||||
|
||||
BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv)
|
||||
{
|
||||
BMVert *nv = NULL;
|
||||
BMLoop *l = NULL, *sl = NULL;
|
||||
BMEdge *curedge = NULL;
|
||||
int numloops = 0, numedges = 0, i, maxedges, maxloops;
|
||||
|
||||
|
||||
/*BMESH_TODO: Validation*/
|
||||
/*validate radial cycle of all collected loops*/
|
||||
/*validate the disk cycle of sv, and nv*/
|
||||
/*validate the face length of all faces? overkill?*/
|
||||
/*validate the l->e pointers of all affected faces, ie: l->v and l->next->v should be equivalent to l->e*/
|
||||
|
||||
/*verify that sv has edges*/
|
||||
if(sv->e == NULL)
|
||||
return NULL;
|
||||
|
||||
/*first verify no wire edges on sv*/
|
||||
curedge = sv->e;
|
||||
do {
|
||||
if(curedge->l == NULL)
|
||||
return NULL;
|
||||
curedge = bmesh_disk_nextedge(curedge, sv);
|
||||
} while(curedge != sv->e);
|
||||
|
||||
/*next verify that sv is in sf*/
|
||||
l = sf->loopbase;
|
||||
do {
|
||||
if(l->v == sv) {
|
||||
sl = l;
|
||||
break;
|
||||
}
|
||||
l = (l->next);
|
||||
} while(l != sf->lbase);
|
||||
|
||||
if(sl == NULL)
|
||||
return NULL;
|
||||
|
||||
/*clear euler flags*/
|
||||
sv->head.eflag1 = 0;
|
||||
|
||||
curedge = sv->e;
|
||||
do {
|
||||
curedge->head.eflag1 = 0;
|
||||
l = curedge->l;
|
||||
do {
|
||||
l->head.eflag1 = 0;
|
||||
l->f->head.eflag1 = 0;
|
||||
l = bmesh_radial_nextloop(l);
|
||||
} while(l != curedge->l);
|
||||
curedge = bmesh_disk_nextedge(curedge, sv);
|
||||
} while(curedge != sv->e);
|
||||
|
||||
/*search through face disk and flag elements as we go.*/
|
||||
/*Note, test this to make sure that it works correct on
|
||||
non-manifold faces!
|
||||
*/
|
||||
l = sl;
|
||||
l->e->head.eflag1 |= URMV_VISIT;
|
||||
l->f->head.eflag1 |= URMV_VISIT;
|
||||
do {
|
||||
if(l->v == sv)
|
||||
l = bmesh_radial_nextloop((l->prev));
|
||||
else
|
||||
l = bmesh_radial_nextloop((l->next));
|
||||
l->e->head.eflag1 |= URMV_VISIT;
|
||||
l->f->head.eflag1 |= URMV_VISIT;
|
||||
} while(l != sl && (bmesh_cycle_length(&(l->radial)) > 1) );
|
||||
|
||||
/*Verify that all visited edges are at least 1 or 2 manifold*/
|
||||
curedge = sv->e;
|
||||
do {
|
||||
if(curedge->head.eflag1 && (bmesh_cycle_length(&(curedge->l->radial)) > 2) )
|
||||
return NULL;
|
||||
curedge = bmesh_disk_nextedge(curedge, sv);
|
||||
} while(curedge != sv->e);
|
||||
|
||||
/*allocate temp storage - we overallocate here instead of trying to be clever*/
|
||||
maxedges = 0;
|
||||
maxloops = 0;
|
||||
curedge = sv->e;
|
||||
do {
|
||||
if(curedge->l) {
|
||||
l = curedge->l;
|
||||
do {
|
||||
maxloops += l->f->len;
|
||||
l = bmesh_radial_nextloop(l);
|
||||
} while(l != curedge->l);
|
||||
}
|
||||
maxedges+= 1;
|
||||
curedge = bmesh_disk_nextedge(curedge,sv);
|
||||
} while(curedge != sv->e);
|
||||
|
||||
if(bm->edarlen < maxedges) {
|
||||
MEM_freeN(bm->edar);
|
||||
bm->edar = MEM_callocN(sizeof(BMEdge *) * maxedges, "BM Edge pointer array");
|
||||
bm->edarlen = maxedges;
|
||||
}
|
||||
if(bm->lparlen < maxloops) {
|
||||
MEM_freeN(bm->lpar);
|
||||
bm->lpar = MEM_callocN(sizeof(BMLoop *) * maxloops, "BM Loop pointer array");
|
||||
bm->lparlen = maxloops;
|
||||
}
|
||||
|
||||
/*first get loops by looping around edges and loops around that edges faces*/
|
||||
curedge = sv->e;
|
||||
do {
|
||||
if(curedge->l) {
|
||||
l = curedge->l;
|
||||
do {
|
||||
if( (l->head.eflag1 & URMV_VISIT) && (!(l->head.eflag1 & URMV_VISIT2)) ) {
|
||||
bm->lpar[numloops] = l;
|
||||
l->head.eflag1 |= URMV_VISIT2;
|
||||
numloops++;
|
||||
}
|
||||
l = bmesh_radial_nextloop(l);
|
||||
} while(l != curedge->l);
|
||||
}
|
||||
curedge = bmesh_disk_nextedge(curedge, sv);
|
||||
} while(curedge != sv->e);
|
||||
|
||||
/*now collect edges by looping around edges and looking at visited flags*/
|
||||
curedge = sv->e;
|
||||
do {
|
||||
if(curedge->head.eflag1 & URMV_VISIT) {
|
||||
bm->edar[numedges] = curedge;
|
||||
numedges++;
|
||||
}
|
||||
curedge = bmesh_disk_nextedge(curedge, sv);
|
||||
} while(curedge != sv->e);
|
||||
|
||||
/*make new vertex*/
|
||||
nv = bmesh_addvertlist(bm, sv);
|
||||
|
||||
/*go through and relink edges*/
|
||||
for(i = 0; i < numedges; i++) {
|
||||
curedge = bm->edar[i];
|
||||
/*remove curedge from sv*/
|
||||
bmesh_disk_remove_edge(curedge, sv);
|
||||
/*swap out sv for nv in curedge*/
|
||||
bmesh_edge_swapverts(curedge, sv, nv);
|
||||
/*add curedge to nv's disk cycle*/
|
||||
bmesh_disk_append_edge(curedge, nv);
|
||||
}
|
||||
|
||||
/*go through and relink loops*/
|
||||
for(i = 0; i < numloops; i ++) {
|
||||
l = bm->lpar[i];
|
||||
if(l->v == sv)
|
||||
l->v = nv;
|
||||
}
|
||||
return nv;
|
||||
}
|
||||
#endif
|
||||
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
@@ -0,0 +1,2024 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_newcore.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "BKE_DerivedMesh.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_array.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/* use so valgrinds memcheck alerts us when undefined index is used.
|
||||
* TESTING ONLY! */
|
||||
// #define USE_DEBUG_INDEX_MEMCHECK
|
||||
|
||||
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
||||
#define DEBUG_MEMCHECK_INDEX_INVALIDATE(ele) \
|
||||
{ \
|
||||
int undef_idx; \
|
||||
BM_elem_index_set(ele, undef_idx); /* set_ok_invalid */ \
|
||||
} \
|
||||
|
||||
#endif
|
||||
|
||||
BMVert *BM_vert_create(BMesh *bm, const float co[3], const struct BMVert *example)
|
||||
{
|
||||
BMVert *v = BLI_mempool_calloc(bm->vpool);
|
||||
|
||||
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
||||
DEBUG_MEMCHECK_INDEX_INVALIDATE(v)
|
||||
#else
|
||||
BM_elem_index_set(v, -1); /* set_ok_invalid */
|
||||
#endif
|
||||
|
||||
bm->elem_index_dirty |= BM_VERT; /* may add to middle of the pool */
|
||||
|
||||
bm->totvert++;
|
||||
|
||||
v->head.htype = BM_VERT;
|
||||
|
||||
/* 'v->no' is handled by BM_elem_attrs_copy */
|
||||
if (co) copy_v3_v3(v->co, co);
|
||||
|
||||
/* allocate flag */
|
||||
v->oflags = BLI_mempool_calloc(bm->toolflagpool);
|
||||
|
||||
CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
|
||||
|
||||
if (example) {
|
||||
BM_elem_attrs_copy(bm, bm, (BMVert *)example, (BMVert *)v);
|
||||
}
|
||||
|
||||
BM_CHECK_ELEMENT(bm, v);
|
||||
|
||||
return (BMVert *) v;
|
||||
}
|
||||
|
||||
/**
|
||||
* BMESH EDGE EXIST
|
||||
*
|
||||
* Finds out if two vertices already have an edge
|
||||
* connecting them. Note that multiple edges may
|
||||
* exist between any two vertices, and therefore
|
||||
* This function only returns the first one found.
|
||||
*
|
||||
* Returns -
|
||||
* BMEdge pointer
|
||||
*/
|
||||
BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
|
||||
BM_ITER(e, &iter, NULL, BM_EDGES_OF_VERT, v1) {
|
||||
if (e->v1 == v2 || e->v2 == v2)
|
||||
return e;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble)
|
||||
{
|
||||
BMEdge *e;
|
||||
|
||||
if (nodouble && (e = BM_edge_exists(v1, v2)))
|
||||
return (BMEdge *)e;
|
||||
|
||||
e = BLI_mempool_calloc(bm->epool);
|
||||
|
||||
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
||||
DEBUG_MEMCHECK_INDEX_INVALIDATE(e)
|
||||
#else
|
||||
BM_elem_index_set(e, -1); /* set_ok_invalid */
|
||||
#endif
|
||||
|
||||
bm->elem_index_dirty |= BM_EDGE; /* may add to middle of the pool */
|
||||
|
||||
bm->totedge++;
|
||||
|
||||
e->head.htype = BM_EDGE;
|
||||
|
||||
/* allocate flag */
|
||||
e->oflags = BLI_mempool_calloc(bm->toolflagpool);
|
||||
|
||||
e->v1 = (BMVert *) v1;
|
||||
e->v2 = (BMVert *) v2;
|
||||
|
||||
|
||||
CustomData_bmesh_set_default(&bm->edata, &e->head.data);
|
||||
|
||||
bmesh_disk_append_edge(e, e->v1);
|
||||
bmesh_disk_append_edge(e, e->v2);
|
||||
|
||||
if (example)
|
||||
BM_elem_attrs_copy(bm, bm, (BMEdge *)example, (BMEdge *)e);
|
||||
|
||||
BM_CHECK_ELEMENT(bm, e);
|
||||
|
||||
return (BMEdge *) e;
|
||||
}
|
||||
|
||||
static BMLoop *bmesh_create_loop(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, const BMLoop *example)
|
||||
{
|
||||
BMLoop *l = NULL;
|
||||
|
||||
l = BLI_mempool_calloc(bm->lpool);
|
||||
l->next = l->prev = NULL;
|
||||
l->v = v;
|
||||
l->e = e;
|
||||
l->f = f;
|
||||
l->radial_next = l->radial_prev = NULL;
|
||||
l->head.data = NULL;
|
||||
l->head.htype = BM_LOOP;
|
||||
|
||||
bm->totloop++;
|
||||
|
||||
if (example)
|
||||
CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, example->head.data, &l->head.data);
|
||||
else
|
||||
CustomData_bmesh_set_default(&bm->ldata, &l->head.data);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static BMLoop *bm_face_boundry_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte)
|
||||
{
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool);
|
||||
#endif
|
||||
BMLoop *l = bmesh_create_loop(bm, startv, starte, f, NULL);
|
||||
|
||||
bmesh_radial_append(starte, l);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
lst->first = lst->last = l;
|
||||
BLI_addtail(&f->loops, lst);
|
||||
#else
|
||||
f->l_first = l;
|
||||
#endif
|
||||
|
||||
l->f = f;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts)
|
||||
{
|
||||
BMEdge **edges = NULL;
|
||||
BMVert **verts = NULL;
|
||||
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
|
||||
BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
BMLoop *l2;
|
||||
BMFace *f2;
|
||||
int i;
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
if (copyverts) {
|
||||
BMVert *v = BM_vert_create(bm, l_iter->v->co, l_iter->v);
|
||||
BLI_array_append(verts, v);
|
||||
}
|
||||
else {
|
||||
BLI_array_append(verts, l_iter->v);
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
i = 0;
|
||||
do {
|
||||
if (copyedges) {
|
||||
BMEdge *e;
|
||||
BMVert *v1, *v2;
|
||||
|
||||
if (l_iter->e->v1 == verts[i]) {
|
||||
v1 = verts[i];
|
||||
v2 = verts[(i + 1) % f->len];
|
||||
}
|
||||
else {
|
||||
v2 = verts[i];
|
||||
v1 = verts[(i + 1) % f->len];
|
||||
}
|
||||
|
||||
e = BM_edge_create(bm, v1, v2, l_iter->e, FALSE);
|
||||
BLI_array_append(edges, e);
|
||||
}
|
||||
else {
|
||||
BLI_array_append(edges, l_iter->e);
|
||||
}
|
||||
|
||||
i++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
f2 = BM_face_create(bm, verts, edges, f->len, FALSE);
|
||||
|
||||
BM_elem_attrs_copy(bm, bm, f, f2);
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
l2 = BM_FACE_FIRST_LOOP(f2);
|
||||
do {
|
||||
BM_elem_attrs_copy(bm, bm, l_iter, l2);
|
||||
l2 = l2->next;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
return f2;
|
||||
}
|
||||
|
||||
BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble)
|
||||
{
|
||||
BMFace *f = NULL;
|
||||
BMLoop *l, *startl, *lastl;
|
||||
int i, overlap;
|
||||
|
||||
if (len == 0) {
|
||||
/* just return NULL for no */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nodouble) {
|
||||
/* Check if face already exists */
|
||||
overlap = BM_face_exists(bm, verts, len, &f);
|
||||
if (overlap) {
|
||||
return f;
|
||||
}
|
||||
else {
|
||||
BLI_assert(f == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
f = BLI_mempool_calloc(bm->fpool);
|
||||
|
||||
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
||||
DEBUG_MEMCHECK_INDEX_INVALIDATE(f)
|
||||
#else
|
||||
BM_elem_index_set(f, -1); /* set_ok_invalid */
|
||||
#endif
|
||||
|
||||
bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */
|
||||
|
||||
bm->totface++;
|
||||
|
||||
f->head.htype = BM_FACE;
|
||||
|
||||
startl = lastl = bm_face_boundry_add(bm, (BMFace *)f, verts[0], edges[0]);
|
||||
|
||||
startl->v = (BMVert *)verts[0];
|
||||
startl->e = (BMEdge *)edges[0];
|
||||
for (i = 1; i < len; i++) {
|
||||
l = bmesh_create_loop(bm, verts[i], edges[i], (BMFace *)f, edges[i]->l);
|
||||
|
||||
l->f = (BMFace *) f;
|
||||
bmesh_radial_append(edges[i], l);
|
||||
|
||||
l->prev = lastl;
|
||||
lastl->next = l;
|
||||
lastl = l;
|
||||
}
|
||||
|
||||
/* allocate flag */
|
||||
f->oflags = BLI_mempool_calloc(bm->toolflagpool);
|
||||
|
||||
CustomData_bmesh_set_default(&bm->pdata, &f->head.data);
|
||||
|
||||
startl->prev = lastl;
|
||||
lastl->next = startl;
|
||||
|
||||
f->len = len;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
f->totbounds = 0;
|
||||
#endif
|
||||
|
||||
BM_CHECK_ELEMENT(bm, f);
|
||||
|
||||
return (BMFace *) f;
|
||||
}
|
||||
|
||||
int bmesh_check_element(BMesh *UNUSED(bm), void *element, const char htype)
|
||||
{
|
||||
BMHeader *head = element;
|
||||
int err = 0;
|
||||
|
||||
if (!element)
|
||||
return 1;
|
||||
|
||||
if (head->htype != htype)
|
||||
return 2;
|
||||
|
||||
switch (htype) {
|
||||
case BM_VERT: {
|
||||
BMVert *v = element;
|
||||
if (v->e && v->e->head.htype != BM_EDGE) {
|
||||
err |= 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BM_EDGE: {
|
||||
BMEdge *e = element;
|
||||
if (e->l && e->l->head.htype != BM_LOOP)
|
||||
err |= 8;
|
||||
if (e->l && e->l->f->head.htype != BM_FACE)
|
||||
err |= 16;
|
||||
if (e->v1_disk_link.prev == NULL ||
|
||||
e->v2_disk_link.prev == NULL ||
|
||||
e->v1_disk_link.next == NULL ||
|
||||
e->v2_disk_link.next == NULL)
|
||||
{
|
||||
err |= 32;
|
||||
}
|
||||
if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL))
|
||||
err |= 64;
|
||||
if (e->l && e->l->f->len <= 0)
|
||||
err |= 128;
|
||||
break;
|
||||
}
|
||||
case BM_LOOP: {
|
||||
BMLoop *l = element, *l2;
|
||||
int i;
|
||||
|
||||
if (l->f->head.htype != BM_FACE)
|
||||
err |= 256;
|
||||
if (l->e->head.htype != BM_EDGE)
|
||||
err |= 512;
|
||||
if (l->v->head.htype != BM_VERT)
|
||||
err |= 1024;
|
||||
if (!BM_vert_in_edge(l->e, l->v)) {
|
||||
fprintf(stderr, "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n", __func__);
|
||||
err |= 2048;
|
||||
}
|
||||
|
||||
if (l->radial_next == NULL || l->radial_prev == NULL)
|
||||
err |= (1 << 12);
|
||||
if (l->f->len <= 0)
|
||||
err |= (1 << 13);
|
||||
|
||||
/* validate boundary loop--invalid for hole loops, of course,
|
||||
* but we won't be allowing those for a while ye */
|
||||
l2 = l;
|
||||
i = 0;
|
||||
do {
|
||||
if (i >= BM_NGON_MAX) {
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
} while ((l2 = l2->next) != l);
|
||||
|
||||
if (i != l->f->len || l2 != l)
|
||||
err |= (1 << 14);
|
||||
|
||||
if (!bmesh_radial_validate(bmesh_radial_length(l), l))
|
||||
err |= (1 << 15);
|
||||
|
||||
break;
|
||||
}
|
||||
case BM_FACE: {
|
||||
BMFace *f = element;
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
int len = 0;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
if (!f->loops.first)
|
||||
#else
|
||||
if (!f->l_first)
|
||||
#endif
|
||||
{
|
||||
err |= (1 << 16);
|
||||
}
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
if (l_iter->f != f) {
|
||||
fprintf(stderr, "%s: loop inside one face points to another! (bmesh internal error)\n", __func__);
|
||||
err |= (1 << 17);
|
||||
}
|
||||
|
||||
if (!l_iter->e)
|
||||
err |= (1 << 18);
|
||||
if (!l_iter->v)
|
||||
err |= (1 << 19);
|
||||
if (!BM_vert_in_edge(l_iter->e, l_iter->v) || !BM_vert_in_edge(l_iter->e, l_iter->next->v)) {
|
||||
err |= (1 << 20);
|
||||
}
|
||||
|
||||
if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter))
|
||||
err |= (1 << 21);
|
||||
|
||||
if (!bmesh_disk_count(l_iter->v) || !bmesh_disk_count(l_iter->next->v))
|
||||
err |= (1 << 22);
|
||||
|
||||
len++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
if (len != f->len)
|
||||
err |= (1 << 23);
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
bmesh_error();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* low level function, only free's,
|
||||
* does not change adjust surrounding geometry */
|
||||
static void bmesh_kill_only_vert(BMesh *bm, BMVert *v)
|
||||
{
|
||||
bm->totvert--;
|
||||
bm->elem_index_dirty |= BM_VERT;
|
||||
|
||||
BM_select_history_remove(bm, v);
|
||||
if (v->head.data)
|
||||
CustomData_bmesh_free_block(&bm->vdata, &v->head.data);
|
||||
|
||||
BLI_mempool_free(bm->toolflagpool, v->oflags);
|
||||
BLI_mempool_free(bm->vpool, v);
|
||||
}
|
||||
|
||||
static void bmesh_kill_only_edge(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
bm->totedge--;
|
||||
bm->elem_index_dirty |= BM_EDGE;
|
||||
|
||||
BM_select_history_remove(bm, e);
|
||||
|
||||
if (e->head.data)
|
||||
CustomData_bmesh_free_block(&bm->edata, &e->head.data);
|
||||
|
||||
BLI_mempool_free(bm->toolflagpool, e->oflags);
|
||||
BLI_mempool_free(bm->epool, e);
|
||||
}
|
||||
|
||||
static void bmesh_kill_only_face(BMesh *bm, BMFace *f)
|
||||
{
|
||||
if (bm->act_face == f)
|
||||
bm->act_face = NULL;
|
||||
|
||||
bm->totface--;
|
||||
bm->elem_index_dirty |= BM_FACE;
|
||||
|
||||
BM_select_history_remove(bm, f);
|
||||
|
||||
if (f->head.data)
|
||||
CustomData_bmesh_free_block(&bm->pdata, &f->head.data);
|
||||
|
||||
BLI_mempool_free(bm->toolflagpool, f->oflags);
|
||||
BLI_mempool_free(bm->fpool, f);
|
||||
}
|
||||
|
||||
static void bmesh_kill_only_loop(BMesh *bm, BMLoop *l)
|
||||
{
|
||||
bm->totloop--;
|
||||
if (l->head.data)
|
||||
CustomData_bmesh_free_block(&bm->ldata, &l->head.data);
|
||||
|
||||
BLI_mempool_free(bm->lpool, l);
|
||||
}
|
||||
|
||||
void BM_face_edges_kill(BMesh *bm, BMFace *f)
|
||||
{
|
||||
BMEdge **edges = NULL;
|
||||
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
int i;
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BLI_array_append(edges, l_iter->e);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
for (i = 0; i < BLI_array_count(edges); i++) {
|
||||
BM_edge_kill(bm, edges[i]);
|
||||
}
|
||||
|
||||
BLI_array_free(edges);
|
||||
}
|
||||
|
||||
void BM_face_verts_kill(BMesh *bm, BMFace *f)
|
||||
{
|
||||
BMVert **verts = NULL;
|
||||
BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
int i;
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BLI_array_append(verts, l_iter->v);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
for (i = 0; i < BLI_array_count(verts); i++) {
|
||||
BM_vert_kill(bm, verts[i]);
|
||||
}
|
||||
|
||||
BLI_array_free(verts);
|
||||
}
|
||||
|
||||
void BM_face_kill(BMesh *bm, BMFace *f)
|
||||
{
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BMLoopList *ls, *ls_next;
|
||||
#endif
|
||||
|
||||
BM_CHECK_ELEMENT(bm, f);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
for (ls = f->loops.first; ls; ls = ls_next)
|
||||
#else
|
||||
if (f->l_first)
|
||||
#endif
|
||||
{
|
||||
BMLoop *l_iter, *l_next, *l_first;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
ls_next = ls->next;
|
||||
l_iter = l_first = ls->first;
|
||||
#else
|
||||
l_iter = l_first = f->l_first;
|
||||
#endif
|
||||
|
||||
do {
|
||||
l_next = l_iter->next;
|
||||
|
||||
bmesh_radial_remove_loop(l_iter, l_iter->e);
|
||||
bmesh_kill_only_loop(bm, l_iter);
|
||||
|
||||
} while ((l_iter = l_next) != l_first);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BLI_mempool_free(bm->looplistpool, ls);
|
||||
#endif
|
||||
}
|
||||
|
||||
bmesh_kill_only_face(bm, f);
|
||||
}
|
||||
|
||||
void BM_edge_kill(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
|
||||
bmesh_disk_remove_edge(e, e->v1);
|
||||
bmesh_disk_remove_edge(e, e->v2);
|
||||
|
||||
if (e->l) {
|
||||
BMLoop *l = e->l, *lnext, *startl = e->l;
|
||||
|
||||
do {
|
||||
lnext = l->radial_next;
|
||||
if (lnext->f == l->f) {
|
||||
BM_face_kill(bm, l->f);
|
||||
break;
|
||||
}
|
||||
|
||||
BM_face_kill(bm, l->f);
|
||||
|
||||
if (l == lnext)
|
||||
break;
|
||||
l = lnext;
|
||||
} while (l != startl);
|
||||
}
|
||||
|
||||
bmesh_kill_only_edge(bm, e);
|
||||
}
|
||||
|
||||
void BM_vert_kill(BMesh *bm, BMVert *v)
|
||||
{
|
||||
if (v->e) {
|
||||
BMEdge *e, *nexte;
|
||||
|
||||
e = v->e;
|
||||
while (v->e) {
|
||||
nexte = bmesh_disk_nextedge(e, v);
|
||||
BM_edge_kill(bm, e);
|
||||
e = nexte;
|
||||
}
|
||||
}
|
||||
|
||||
bmesh_kill_only_vert(bm, v);
|
||||
}
|
||||
|
||||
/********** private disk and radial cycle functions ********** */
|
||||
|
||||
/**
|
||||
* bmesh_loop_reverse
|
||||
*
|
||||
* FLIP FACE EULER
|
||||
*
|
||||
* Changes the winding order of a face from CW to CCW or vice versa.
|
||||
* This euler is a bit peculiar in compairson to others as it is its
|
||||
* own inverse.
|
||||
*
|
||||
* BMESH_TODO: reinsert validation code.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
static int bmesh_loop_length(BMLoop *l)
|
||||
{
|
||||
BMLoop *l_first = l;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
i++;
|
||||
} while ((l = l->next) != l_first);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f
|
||||
#ifdef USE_BMESH_HOLES
|
||||
, BMLoopList *lst
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BMLoop *l_first = lst->first;
|
||||
#else
|
||||
BMLoop *l_first = f->l_first;
|
||||
#endif
|
||||
|
||||
BMLoop *l_iter, *oldprev, *oldnext;
|
||||
BMEdge **edar = NULL;
|
||||
MDisps *md;
|
||||
BLI_array_staticdeclare(edar, BM_NGON_STACK_SIZE);
|
||||
int i, j, edok, len = 0, do_disps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
|
||||
|
||||
len = bmesh_loop_length(l_first);
|
||||
|
||||
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
|
||||
BMEdge *curedge = l_iter->e;
|
||||
bmesh_radial_remove_loop(l_iter, curedge);
|
||||
BLI_array_append(edar, curedge);
|
||||
}
|
||||
|
||||
/* actually reverse the loop */
|
||||
for (i = 0, l_iter = l_first; i < len; i++) {
|
||||
oldnext = l_iter->next;
|
||||
oldprev = l_iter->prev;
|
||||
l_iter->next = oldprev;
|
||||
l_iter->prev = oldnext;
|
||||
l_iter = oldnext;
|
||||
|
||||
if (do_disps) {
|
||||
float (*co)[3];
|
||||
int x, y, sides;
|
||||
|
||||
md = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS);
|
||||
if (!md->totdisp || !md->disps)
|
||||
continue;
|
||||
|
||||
sides = (int)sqrt(md->totdisp);
|
||||
co = md->disps;
|
||||
|
||||
for (x = 0; x < sides; x++) {
|
||||
for (y = 0; y < x; y++) {
|
||||
swap_v3_v3(co[y * sides + x], co[sides * x + y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 2) { /* two edged face */
|
||||
/* do some verification here! */
|
||||
l_first->e = edar[1];
|
||||
l_first->next->e = edar[0];
|
||||
}
|
||||
else {
|
||||
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
|
||||
edok = 0;
|
||||
for (j = 0; j < len; j++) {
|
||||
edok = bmesh_verts_in_edge(l_iter->v, l_iter->next->v, edar[j]);
|
||||
if (edok) {
|
||||
l_iter->e = edar[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* rebuild radia */
|
||||
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next)
|
||||
bmesh_radial_append(l_iter->e, l_iter);
|
||||
|
||||
/* validate radia */
|
||||
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
|
||||
BM_CHECK_ELEMENT(bm, l_iter);
|
||||
BM_CHECK_ELEMENT(bm, l_iter->e);
|
||||
BM_CHECK_ELEMENT(bm, l_iter->v);
|
||||
BM_CHECK_ELEMENT(bm, l_iter->f);
|
||||
}
|
||||
|
||||
BLI_array_free(edar);
|
||||
|
||||
BM_CHECK_ELEMENT(bm, f);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bmesh_loop_reverse(BMesh *bm, BMFace *f)
|
||||
{
|
||||
#ifdef USE_BMESH_HOLES
|
||||
return bmesh_loop_reverse_loop(bm, f, f->loops.first);
|
||||
#else
|
||||
return bmesh_loop_reverse_loop(bm, f);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void bmesh_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag)
|
||||
{
|
||||
BMHeader **eles = veles;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tot; i++) {
|
||||
BM_ELEM_API_FLAG_ENABLE((BMElemF *)eles[i], flag);
|
||||
}
|
||||
}
|
||||
|
||||
static void bmesh_clear_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag)
|
||||
{
|
||||
BMHeader **eles = veles;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tot; i++) {
|
||||
BM_ELEM_API_FLAG_DISABLE((BMElemF *)eles[i], flag);
|
||||
}
|
||||
}
|
||||
|
||||
#define FACE_MARK (1 << 10)
|
||||
|
||||
static int count_flagged_radial(BMesh *bm, BMLoop *l, int flag)
|
||||
{
|
||||
BMLoop *l2 = l;
|
||||
int i = 0, c = 0;
|
||||
|
||||
do {
|
||||
if (!l2) {
|
||||
bmesh_error();
|
||||
goto error;
|
||||
}
|
||||
|
||||
i += BM_ELEM_API_FLAG_TEST(l2->f, flag) ? 1 : 0;
|
||||
l2 = bmesh_radial_nextloop(l2);
|
||||
if (c >= BM_LOOP_RADIAL_MAX) {
|
||||
bmesh_error();
|
||||
goto error;
|
||||
}
|
||||
c++;
|
||||
} while (l2 != l);
|
||||
|
||||
return i;
|
||||
|
||||
error:
|
||||
BMO_error_raise(bm, bm->currentop, BMERR_MESH_ERROR, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int UNUSED_FUNCTION(count_flagged_disk)(BMVert *v, int flag)
|
||||
{
|
||||
BMEdge *e = v->e;
|
||||
int i = 0;
|
||||
|
||||
if (!e)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
i += BM_ELEM_API_FLAG_TEST(e, flag) ? 1 : 0;
|
||||
e = bmesh_disk_nextedge(e, v);
|
||||
} while (e != v->e);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int disk_is_flagged(BMVert *v, int flag)
|
||||
{
|
||||
BMEdge *e = v->e;
|
||||
|
||||
if (!e)
|
||||
return FALSE;
|
||||
|
||||
do {
|
||||
BMLoop *l = e->l;
|
||||
|
||||
if (!l) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (bmesh_radial_length(l) == 1)
|
||||
return FALSE;
|
||||
|
||||
do {
|
||||
if (!BM_ELEM_API_FLAG_TEST(l->f, flag))
|
||||
return FALSE;
|
||||
|
||||
l = l->radial_next;
|
||||
} while (l != e->l);
|
||||
|
||||
e = bmesh_disk_nextedge(e, v);
|
||||
} while (e != v->e);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Midlevel Topology Manipulation Functions */
|
||||
|
||||
/*
|
||||
* BM_faces_join
|
||||
*
|
||||
* Joins a collected group of faces into one. Only restriction on
|
||||
* the input data is that the faces must be connected to each other.
|
||||
*
|
||||
* If a pair of faces share multiple edges, the pair of
|
||||
* faces will be joined at every edge.
|
||||
*
|
||||
* Returns a pointer to the combined face.
|
||||
*/
|
||||
BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface)
|
||||
{
|
||||
BMFace *f, *newf;
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BMLoopList *lst;
|
||||
ListBase holes = {NULL, NULL};
|
||||
#endif
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
BMEdge **edges = NULL;
|
||||
BMEdge **deledges = NULL;
|
||||
BMVert **delverts = NULL;
|
||||
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
|
||||
BLI_array_staticdeclare(deledges, BM_NGON_STACK_SIZE);
|
||||
BLI_array_staticdeclare(delverts, BM_NGON_STACK_SIZE);
|
||||
BMVert *v1 = NULL, *v2 = NULL;
|
||||
const char *err = NULL;
|
||||
int i, tote = 0;
|
||||
|
||||
if (!totface) {
|
||||
bmesh_error();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (totface == 1)
|
||||
return faces[0];
|
||||
|
||||
bmesh_systag_elements(bm, faces, totface, _FLAG_JF);
|
||||
|
||||
for (i = 0; i < totface; i++) {
|
||||
f = faces[i];
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
int rlen = count_flagged_radial(bm, l_iter, _FLAG_JF);
|
||||
|
||||
if (rlen > 2) {
|
||||
err = "Input faces do not form a contiguous manifold region";
|
||||
goto error;
|
||||
}
|
||||
else if (rlen == 1) {
|
||||
BLI_array_append(edges, l_iter->e);
|
||||
|
||||
if (!v1) {
|
||||
v1 = l_iter->v;
|
||||
v2 = BM_edge_other_vert(l_iter->e, l_iter->v);
|
||||
}
|
||||
tote++;
|
||||
}
|
||||
else if (rlen == 2) {
|
||||
int d1, d2;
|
||||
|
||||
d1 = disk_is_flagged(l_iter->e->v1, _FLAG_JF);
|
||||
d2 = disk_is_flagged(l_iter->e->v2, _FLAG_JF);
|
||||
|
||||
if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) {
|
||||
/* don't remove an edge it makes up the side of another face
|
||||
* else this will remove the face as well - campbell */
|
||||
if (BM_edge_face_count(l_iter->e) <= 2) {
|
||||
BLI_array_append(deledges, l_iter->e);
|
||||
BM_ELEM_API_FLAG_ENABLE(l_iter->e, _FLAG_JF);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) {
|
||||
BLI_array_append(delverts, l_iter->e->v1);
|
||||
BM_ELEM_API_FLAG_ENABLE(l_iter->e->v1, _FLAG_JF);
|
||||
}
|
||||
|
||||
if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) {
|
||||
BLI_array_append(delverts, l_iter->e->v2);
|
||||
BM_ELEM_API_FLAG_ENABLE(l_iter->e->v2, _FLAG_JF);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
for (lst = f->loops.first; lst; lst = lst->next) {
|
||||
if (lst == f->loops.first) continue;
|
||||
|
||||
BLI_remlink(&f->loops, lst);
|
||||
BLI_addtail(&holes, lst);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* create region fac */
|
||||
newf = BM_face_create_ngon(bm, v1, v2, edges, tote, FALSE);
|
||||
if (!newf || BMO_error_occurred(bm)) {
|
||||
if (!BMO_error_occurred(bm))
|
||||
err = "Invalid boundary region to join faces";
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* copy over loop data */
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(newf);
|
||||
do {
|
||||
BMLoop *l2 = l_iter->radial_next;
|
||||
|
||||
do {
|
||||
if (BM_ELEM_API_FLAG_TEST(l2->f, _FLAG_JF))
|
||||
break;
|
||||
l2 = l2->radial_next;
|
||||
} while (l2 != l_iter);
|
||||
|
||||
if (l2 != l_iter) {
|
||||
/* I think this is correct */
|
||||
if (l2->v != l_iter->v) {
|
||||
l2 = l2->next;
|
||||
}
|
||||
|
||||
BM_elem_attrs_copy(bm, bm, l2, l_iter);
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
BM_elem_attrs_copy(bm, bm, faces[0], newf);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
/* add hole */
|
||||
BLI_movelisttolist(&newf->loops, &holes);
|
||||
#endif
|
||||
|
||||
/* update loop face pointer */
|
||||
#ifdef USE_BMESH_HOLES
|
||||
for (lst = newf->loops.first; lst; lst = lst->next)
|
||||
#endif
|
||||
{
|
||||
#ifdef USE_BMESH_HOLES
|
||||
l_iter = l_first = lst->first;
|
||||
#else
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(newf);
|
||||
#endif
|
||||
do {
|
||||
l_iter->f = newf;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF);
|
||||
BM_ELEM_API_FLAG_DISABLE(newf, _FLAG_JF);
|
||||
|
||||
/* handle multires data */
|
||||
if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(newf);
|
||||
do {
|
||||
for (i = 0; i < totface; i++) {
|
||||
BM_loop_interp_multires(bm, l_iter, faces[i]);
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
/* delete old geometr */
|
||||
for (i = 0; i < BLI_array_count(deledges); i++) {
|
||||
BM_edge_kill(bm, deledges[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < BLI_array_count(delverts); i++) {
|
||||
BM_vert_kill(bm, delverts[i]);
|
||||
}
|
||||
|
||||
BLI_array_free(edges);
|
||||
BLI_array_free(deledges);
|
||||
BLI_array_free(delverts);
|
||||
|
||||
BM_CHECK_ELEMENT(bm, newf);
|
||||
return newf;
|
||||
error:
|
||||
bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF);
|
||||
BLI_array_free(edges);
|
||||
BLI_array_free(deledges);
|
||||
BLI_array_free(delverts);
|
||||
|
||||
if (err) {
|
||||
BMO_error_raise(bm, bm->currentop, BMERR_DISSOLVEFACES_FAILED, err);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BMFace *bmesh_addpolylist(BMesh *bm, BMFace *UNUSED(example))
|
||||
{
|
||||
BMFace *f;
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BMLoopList *lst;
|
||||
#endif
|
||||
|
||||
f = BLI_mempool_calloc(bm->fpool);
|
||||
#ifdef USE_BMESH_HOLES
|
||||
lst = BLI_mempool_calloc(bm->looplistpool);
|
||||
#endif
|
||||
|
||||
f->head.htype = BM_FACE;
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BLI_addtail(&f->loops, lst);
|
||||
#endif
|
||||
|
||||
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
||||
DEBUG_MEMCHECK_INDEX_INVALIDATE(f)
|
||||
#else
|
||||
BM_elem_index_set(f, -1); /* set_ok_invalid */
|
||||
#endif
|
||||
|
||||
bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */
|
||||
|
||||
bm->totface++;
|
||||
|
||||
/* allocate flag */
|
||||
f->oflags = BLI_mempool_calloc(bm->toolflagpool);
|
||||
|
||||
CustomData_bmesh_set_default(&bm->pdata, &f->head.data);
|
||||
|
||||
f->len = 0;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
f->totbounds = 1;
|
||||
#endif
|
||||
|
||||
return (BMFace *) f;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_SFME
|
||||
*
|
||||
* SPLIT FACE MAKE EDGE:
|
||||
*
|
||||
* Takes as input two vertices in a single face. An edge is created which divides the original face
|
||||
* into two distinct regions. One of the regions is assigned to the original face and it is closed off.
|
||||
* The second region has a new face assigned to it.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Before: After:
|
||||
* ---------- ----------
|
||||
* | | | |
|
||||
* | | | f1 |
|
||||
* v1 f1 v2 v1======v2
|
||||
* | | | f2 |
|
||||
* | | | |
|
||||
* ---------- ----------
|
||||
*
|
||||
* Note that the input vertices can be part of the same edge. This will
|
||||
* result in a two edged face. This is desirable for advanced construction
|
||||
* tools and particularly essential for edge bevel. Because of this it is
|
||||
* up to the caller to decide what to do with the extra edge.
|
||||
*
|
||||
* If holes is NULL, then both faces will lose
|
||||
* all holes from the original face. Also, you cannot split between
|
||||
* a hole vert and a boundary vert; that case is handled by higher-
|
||||
* level wrapping functions (when holes are fully implemented, anyway).
|
||||
*
|
||||
* Note that holes represents which holes goes to the new face, and of
|
||||
* course this requires removing them from the exitsing face first, since
|
||||
* you cannot have linked list links inside multiple lists.
|
||||
*
|
||||
* Returns -
|
||||
* A BMFace pointer
|
||||
*/
|
||||
BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2,
|
||||
BMLoop **rl
|
||||
#ifdef USE_BMESH_HOLES
|
||||
, ListBase *holes
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#ifdef USE_BMESH_HOLES
|
||||
BMLoopList *lst, *lst2;
|
||||
#endif
|
||||
|
||||
BMFace *f2;
|
||||
BMLoop *l_iter, *l_first;
|
||||
BMLoop *v1loop = NULL, *v2loop = NULL, *f1loop = NULL, *f2loop = NULL;
|
||||
BMEdge *e;
|
||||
int i, len, f1len, f2len;
|
||||
|
||||
/* verify that v1 and v2 are in face */
|
||||
len = f->len;
|
||||
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < len; i++, l_iter = l_iter->next) {
|
||||
if (l_iter->v == v1) v1loop = l_iter;
|
||||
else if (l_iter->v == v2) v2loop = l_iter;
|
||||
}
|
||||
|
||||
if (!v1loop || !v2loop) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate new edge between v1 and v2 */
|
||||
e = BM_edge_create(bm, v1, v2, NULL, FALSE);
|
||||
|
||||
f2 = bmesh_addpolylist(bm, f);
|
||||
f1loop = bmesh_create_loop(bm, v2, e, f, v2loop);
|
||||
f2loop = bmesh_create_loop(bm, v1, e, f2, v1loop);
|
||||
|
||||
f1loop->prev = v2loop->prev;
|
||||
f2loop->prev = v1loop->prev;
|
||||
v2loop->prev->next = f1loop;
|
||||
v1loop->prev->next = f2loop;
|
||||
|
||||
f1loop->next = v1loop;
|
||||
f2loop->next = v2loop;
|
||||
v1loop->prev = f1loop;
|
||||
v2loop->prev = f2loop;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
lst = f->loops.first;
|
||||
lst2 = f2->loops.first;
|
||||
|
||||
lst2->first = lst2->last = f2loop;
|
||||
lst->first = lst->last = f1loop;
|
||||
#else
|
||||
f2->l_first = f2loop;
|
||||
f->l_first = f1loop;
|
||||
#endif
|
||||
|
||||
/* validate both loop */
|
||||
/* I dont know how many loops are supposed to be in each face at this point! FIXME */
|
||||
|
||||
/* go through all of f2's loops and make sure they point to it properly */
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f2);
|
||||
f2len = 0;
|
||||
do {
|
||||
l_iter->f = f2;
|
||||
f2len++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
/* link up the new loops into the new edges radia */
|
||||
bmesh_radial_append(e, f1loop);
|
||||
bmesh_radial_append(e, f2loop);
|
||||
|
||||
f2->len = f2len;
|
||||
|
||||
f1len = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
f1len++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
f->len = f1len;
|
||||
|
||||
if (rl) *rl = f2loop;
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
if (holes) {
|
||||
BLI_movelisttolist(&f2->loops, holes);
|
||||
}
|
||||
else {
|
||||
/* this code is not significant until holes actually work */
|
||||
//printf("warning: call to split face euler without holes argument; holes will be tossed.\n");
|
||||
for (lst = f->loops.last; lst != f->loops.first; lst = lst2) {
|
||||
lst2 = lst->prev;
|
||||
BLI_mempool_free(bm->looplistpool, lst);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
BM_CHECK_ELEMENT(bm, e);
|
||||
BM_CHECK_ELEMENT(bm, f);
|
||||
BM_CHECK_ELEMENT(bm, f2);
|
||||
|
||||
return f2;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_SEMV
|
||||
*
|
||||
* SPLIT EDGE MAKE VERT:
|
||||
* Takes a given edge and splits it into two, creating a new vert.
|
||||
*
|
||||
*
|
||||
* Before: OV---------TV
|
||||
* After: OV----NV---TV
|
||||
*
|
||||
* Returns -
|
||||
* BMVert pointer.
|
||||
*
|
||||
*/
|
||||
|
||||
BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re)
|
||||
{
|
||||
BMLoop *nextl;
|
||||
BMEdge *ne;
|
||||
BMVert *nv, *ov;
|
||||
int i, edok, valence1 = 0, valence2 = 0;
|
||||
|
||||
if (bmesh_vert_in_edge(e, tv) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
ov = bmesh_edge_getothervert(e, tv);
|
||||
|
||||
/* count valence of v1 */
|
||||
valence1 = bmesh_disk_count(ov);
|
||||
|
||||
/* count valence of v2 */
|
||||
valence2 = bmesh_disk_count(tv);
|
||||
|
||||
nv = BM_vert_create(bm, tv->co, tv);
|
||||
ne = BM_edge_create(bm, nv, tv, e, FALSE);
|
||||
|
||||
bmesh_disk_remove_edge(ne, tv);
|
||||
bmesh_disk_remove_edge(ne, nv);
|
||||
|
||||
/* remove e from v2's disk cycle */
|
||||
bmesh_disk_remove_edge(e, tv);
|
||||
|
||||
/* swap out tv for nv in e */
|
||||
bmesh_edge_swapverts(e, tv, nv);
|
||||
|
||||
/* add e to nv's disk cycl */
|
||||
bmesh_disk_append_edge(e, nv);
|
||||
|
||||
/* add ne to nv's disk cycl */
|
||||
bmesh_disk_append_edge(ne, nv);
|
||||
|
||||
/* add ne to tv's disk cycl */
|
||||
bmesh_disk_append_edge(ne, tv);
|
||||
|
||||
/* verify disk cycle */
|
||||
edok = bmesh_disk_validate(valence1, ov->e, ov);
|
||||
if (!edok) bmesh_error();
|
||||
edok = bmesh_disk_validate(valence2, tv->e, tv);
|
||||
if (!edok) bmesh_error();
|
||||
edok = bmesh_disk_validate(2, nv->e, nv);
|
||||
if (!edok) bmesh_error();
|
||||
|
||||
/* Split the radial cycle if presen */
|
||||
nextl = e->l;
|
||||
e->l = NULL;
|
||||
if (nextl) {
|
||||
BMLoop *nl, *l;
|
||||
int radlen = bmesh_radial_length(nextl);
|
||||
int first1 = 0, first2 = 0;
|
||||
|
||||
/* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */
|
||||
while (nextl) {
|
||||
l = nextl;
|
||||
l->f->len++;
|
||||
nextl = nextl != nextl->radial_next ? nextl->radial_next : NULL;
|
||||
bmesh_radial_remove_loop(l, NULL);
|
||||
|
||||
nl = bmesh_create_loop(bm, NULL, NULL, l->f, l);
|
||||
nl->prev = l;
|
||||
nl->next = (l->next);
|
||||
nl->prev->next = nl;
|
||||
nl->next->prev = nl;
|
||||
nl->v = nv;
|
||||
|
||||
/* assign the correct edge to the correct loo */
|
||||
if (bmesh_verts_in_edge(nl->v, nl->next->v, e)) {
|
||||
nl->e = e;
|
||||
l->e = ne;
|
||||
|
||||
/* append l into ne's rad cycl */
|
||||
if (!first1) {
|
||||
first1 = 1;
|
||||
l->radial_next = l->radial_prev = NULL;
|
||||
}
|
||||
|
||||
if (!first2) {
|
||||
first2 = 1;
|
||||
l->radial_next = l->radial_prev = NULL;
|
||||
}
|
||||
|
||||
bmesh_radial_append(nl->e, nl);
|
||||
bmesh_radial_append(l->e, l);
|
||||
}
|
||||
else if (bmesh_verts_in_edge(nl->v, nl->next->v, ne)) {
|
||||
nl->e = ne;
|
||||
l->e = e;
|
||||
|
||||
/* append l into ne's rad cycl */
|
||||
if (!first1) {
|
||||
first1 = 1;
|
||||
l->radial_next = l->radial_prev = NULL;
|
||||
}
|
||||
|
||||
if (!first2) {
|
||||
first2 = 1;
|
||||
l->radial_next = l->radial_prev = NULL;
|
||||
}
|
||||
|
||||
bmesh_radial_append(nl->e, nl);
|
||||
bmesh_radial_append(l->e, l);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* verify length of radial cycl */
|
||||
edok = bmesh_radial_validate(radlen, e->l);
|
||||
if (!edok) bmesh_error();
|
||||
edok = bmesh_radial_validate(radlen, ne->l);
|
||||
if (!edok) bmesh_error();
|
||||
|
||||
/* verify loop->v and loop->next->v pointers for */
|
||||
for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) {
|
||||
if (!(l->e == e)) bmesh_error();
|
||||
//if (!(l->radial_next == l)) bmesh_error();
|
||||
if (l->prev->e != ne && l->next->e != ne) {
|
||||
bmesh_error();
|
||||
}
|
||||
edok = bmesh_verts_in_edge(l->v, l->next->v, e);
|
||||
if (!edok) bmesh_error();
|
||||
if (l->v == l->next->v) bmesh_error();
|
||||
if (l->e == l->next->e) bmesh_error();
|
||||
|
||||
/* verify loop cycle for kloop-> */
|
||||
BM_CHECK_ELEMENT(bm, l);
|
||||
BM_CHECK_ELEMENT(bm, l->v);
|
||||
BM_CHECK_ELEMENT(bm, l->e);
|
||||
BM_CHECK_ELEMENT(bm, l->f);
|
||||
}
|
||||
/* verify loop->v and loop->next->v pointers for n */
|
||||
for (i = 0, l = ne->l; i < radlen; i++, l = l->radial_next) {
|
||||
if (!(l->e == ne)) bmesh_error();
|
||||
//if (!(l->radial_next == l)) bmesh_error();
|
||||
if (l->prev->e != e && l->next->e != e) bmesh_error();
|
||||
edok = bmesh_verts_in_edge(l->v, l->next->v, ne);
|
||||
if (!edok) bmesh_error();
|
||||
if (l->v == l->next->v) bmesh_error();
|
||||
if (l->e == l->next->e) bmesh_error();
|
||||
|
||||
BM_CHECK_ELEMENT(bm, l);
|
||||
BM_CHECK_ELEMENT(bm, l->v);
|
||||
BM_CHECK_ELEMENT(bm, l->e);
|
||||
BM_CHECK_ELEMENT(bm, l->f);
|
||||
}
|
||||
}
|
||||
|
||||
BM_CHECK_ELEMENT(bm, ne);
|
||||
BM_CHECK_ELEMENT(bm, nv);
|
||||
BM_CHECK_ELEMENT(bm, ov);
|
||||
BM_CHECK_ELEMENT(bm, e);
|
||||
BM_CHECK_ELEMENT(bm, tv);
|
||||
|
||||
if (re) *re = ne;
|
||||
return nv;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_JEKV
|
||||
*
|
||||
* JOIN EDGE KILL VERT:
|
||||
* Takes a an edge and pointer to one of its vertices and collapses
|
||||
* the edge on that vertex.
|
||||
*
|
||||
* Before: OE KE
|
||||
* ------- -------
|
||||
* | || |
|
||||
* OV KV TV
|
||||
*
|
||||
*
|
||||
* After: OE
|
||||
* ---------------
|
||||
* | |
|
||||
* OV TV
|
||||
*
|
||||
*
|
||||
* Restrictions:
|
||||
* KV is a vertex that must have a valance of exactly two. Furthermore
|
||||
* both edges in KV's disk cycle (OE and KE) must be unique (no double
|
||||
* edges).
|
||||
*
|
||||
* It should also be noted that this euler has the possibility of creating
|
||||
* faces with just 2 edges. It is up to the caller to decide what to do with
|
||||
* these faces.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv)
|
||||
{
|
||||
BMEdge *oe;
|
||||
BMVert *ov, *tv;
|
||||
BMLoop *killoop, *l;
|
||||
int len, radlen = 0, halt = 0, i, valence1, valence2, edok;
|
||||
|
||||
if (bmesh_vert_in_edge(ke, kv) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
len = bmesh_disk_count(kv);
|
||||
|
||||
if (len == 2) {
|
||||
oe = bmesh_disk_nextedge(ke, kv);
|
||||
tv = bmesh_edge_getothervert(ke, kv);
|
||||
ov = bmesh_edge_getothervert(oe, kv);
|
||||
halt = bmesh_verts_in_edge(kv, tv, oe); /* check for double edge */
|
||||
|
||||
if (halt) {
|
||||
return FALSE;
|
||||
}
|
||||
else {
|
||||
/* For verification later, count valence of ov and t */
|
||||
valence1 = bmesh_disk_count(ov);
|
||||
valence2 = bmesh_disk_count(tv);
|
||||
|
||||
/* remove oe from kv's disk cycl */
|
||||
bmesh_disk_remove_edge(oe, kv);
|
||||
/* relink oe->kv to be oe->t */
|
||||
bmesh_edge_swapverts(oe, kv, tv);
|
||||
/* append oe to tv's disk cycl */
|
||||
bmesh_disk_append_edge(oe, tv);
|
||||
/* remove ke from tv's disk cycl */
|
||||
bmesh_disk_remove_edge(ke, tv);
|
||||
|
||||
/* deal with radial cycle of k */
|
||||
radlen = bmesh_radial_length(ke->l);
|
||||
if (ke->l) {
|
||||
/* first step, fix the neighboring loops of all loops in ke's radial cycl */
|
||||
for (i = 0, killoop = ke->l; i < radlen; i++, killoop = bmesh_radial_nextloop(killoop)) {
|
||||
/* relink loops and fix vertex pointer */
|
||||
if (killoop->next->v == kv) {
|
||||
killoop->next->v = tv;
|
||||
}
|
||||
|
||||
killoop->next->prev = killoop->prev;
|
||||
killoop->prev->next = killoop->next;
|
||||
if (BM_FACE_FIRST_LOOP(killoop->f) == killoop) {
|
||||
BM_FACE_FIRST_LOOP(killoop->f) = killoop->next;
|
||||
}
|
||||
killoop->next = NULL;
|
||||
killoop->prev = NULL;
|
||||
|
||||
/* fix len attribute of fac */
|
||||
killoop->f->len--;
|
||||
}
|
||||
/* second step, remove all the hanging loops attached to k */
|
||||
radlen = bmesh_radial_length(ke->l);
|
||||
|
||||
if (LIKELY(radlen)) {
|
||||
BMLoop **loops = NULL;
|
||||
BLI_array_fixedstack_declare(loops, BM_NGON_STACK_SIZE, radlen, __func__);
|
||||
|
||||
killoop = ke->l;
|
||||
|
||||
/* this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well.. */
|
||||
for (i = 0; i < radlen; i++) {
|
||||
loops[i] = killoop;
|
||||
killoop = bmesh_radial_nextloop(killoop);
|
||||
}
|
||||
for (i = 0; i < radlen; i++) {
|
||||
bm->totloop--;
|
||||
BLI_mempool_free(bm->lpool, loops[i]);
|
||||
}
|
||||
BLI_array_fixedstack_free(loops);
|
||||
}
|
||||
|
||||
/* Validate radial cycle of o */
|
||||
edok = bmesh_radial_validate(radlen, oe->l);
|
||||
if (!edok) {
|
||||
bmesh_error();
|
||||
}
|
||||
}
|
||||
|
||||
/* deallocate edg */
|
||||
bmesh_kill_only_edge(bm, ke);
|
||||
|
||||
/* deallocate verte */
|
||||
bmesh_kill_only_vert(bm, kv);
|
||||
|
||||
/* Validate disk cycle lengths of ov, tv are unchange */
|
||||
edok = bmesh_disk_validate(valence1, ov->e, ov);
|
||||
if (!edok) bmesh_error();
|
||||
edok = bmesh_disk_validate(valence2, tv->e, tv);
|
||||
if (!edok) bmesh_error();
|
||||
|
||||
/* Validate loop cycle of all faces attached to o */
|
||||
for (i = 0, l = oe->l; i < radlen; i++, l = bmesh_radial_nextloop(l)) {
|
||||
if (l->e != oe) bmesh_error();
|
||||
edok = bmesh_verts_in_edge(l->v, l->next->v, oe);
|
||||
if (!edok) bmesh_error();
|
||||
edok = bmesh_loop_validate(l->f);
|
||||
if (!edok) bmesh_error();
|
||||
|
||||
BM_CHECK_ELEMENT(bm, l);
|
||||
BM_CHECK_ELEMENT(bm, l->v);
|
||||
BM_CHECK_ELEMENT(bm, l->e);
|
||||
BM_CHECK_ELEMENT(bm, l->f);
|
||||
}
|
||||
|
||||
BM_CHECK_ELEMENT(bm, ov);
|
||||
BM_CHECK_ELEMENT(bm, tv);
|
||||
BM_CHECK_ELEMENT(bm, oe);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_JFKE
|
||||
*
|
||||
* JOIN FACE KILL EDGE:
|
||||
*
|
||||
* Takes two faces joined by a single 2-manifold edge and fuses them togather.
|
||||
* The edge shared by the faces must not be connected to any other edges which have
|
||||
* Both faces in its radial cycle
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* A B
|
||||
* ---------- ----------
|
||||
* | | | |
|
||||
* | f1 | | f1 |
|
||||
* v1========v2 = Ok! v1==V2==v3 == Wrong!
|
||||
* | f2 | | f2 |
|
||||
* | | | |
|
||||
* ---------- ----------
|
||||
*
|
||||
* In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used.
|
||||
* In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller
|
||||
* in this case should call bmesh_JEKV on the extra edges before attempting to fuse f1 and f2.
|
||||
*
|
||||
* Also note that the order of arguments decides whether or not certain per-face attributes are present
|
||||
* in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited
|
||||
* from f1, not f2.
|
||||
*
|
||||
* Returns -
|
||||
* A BMFace pointer
|
||||
*/
|
||||
BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
|
||||
{
|
||||
BMLoop *l_iter, *f1loop = NULL, *f2loop = NULL;
|
||||
int newlen = 0, i, f1len = 0, f2len = 0, radlen = 0, edok, shared;
|
||||
BMIter iter;
|
||||
|
||||
/* can't join a face to itsel */
|
||||
if (f1 == f2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* verify that e is in both f1 and f2 */
|
||||
f1len = f1->len;
|
||||
f2len = f2->len;
|
||||
BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f1) {
|
||||
if (l_iter->e == e) {
|
||||
f1loop = l_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f2) {
|
||||
if (l_iter->e == e) {
|
||||
f2loop = l_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(f1loop && f2loop)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* validate that edge is 2-manifold edg */
|
||||
radlen = bmesh_radial_length(f1loop);
|
||||
if (radlen != 2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* validate direction of f2's loop cycle is compatible */
|
||||
if (f1loop->v == f2loop->v) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* validate that for each face, each vertex has another edge in its disk cycle that is
|
||||
* not e, and not shared. */
|
||||
if ( bmesh_radial_find_face(f1loop->next->e, f2) ||
|
||||
bmesh_radial_find_face(f1loop->prev->e, f2) ||
|
||||
bmesh_radial_find_face(f2loop->next->e, f1) ||
|
||||
bmesh_radial_find_face(f2loop->prev->e, f1) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* validate only one shared edg */
|
||||
shared = BM_face_share_edges(f1, f2);
|
||||
if (shared > 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* validate no internal join */
|
||||
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
|
||||
BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG);
|
||||
}
|
||||
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
|
||||
BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG);
|
||||
}
|
||||
|
||||
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
|
||||
if (l_iter != f1loop) {
|
||||
BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
|
||||
}
|
||||
}
|
||||
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
|
||||
if (l_iter != f2loop) {
|
||||
/* as soon as a duplicate is found, bail out */
|
||||
if (BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* join the two loop */
|
||||
f1loop->prev->next = f2loop->next;
|
||||
f2loop->next->prev = f1loop->prev;
|
||||
|
||||
f1loop->next->prev = f2loop->prev;
|
||||
f2loop->prev->next = f1loop->next;
|
||||
|
||||
/* if f1loop was baseloop, make f1loop->next the base. */
|
||||
if (BM_FACE_FIRST_LOOP(f1) == f1loop)
|
||||
BM_FACE_FIRST_LOOP(f1) = f1loop->next;
|
||||
|
||||
/* increase length of f1 */
|
||||
f1->len += (f2->len - 2);
|
||||
|
||||
/* make sure each loop points to the proper fac */
|
||||
newlen = f1->len;
|
||||
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < newlen; i++, l_iter = l_iter->next)
|
||||
l_iter->f = f1;
|
||||
|
||||
/* remove edge from the disk cycle of its two vertices */
|
||||
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1);
|
||||
bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2);
|
||||
|
||||
/* deallocate edge and its two loops as well as f2 */
|
||||
BLI_mempool_free(bm->toolflagpool, f1loop->e->oflags);
|
||||
BLI_mempool_free(bm->epool, f1loop->e);
|
||||
bm->totedge--;
|
||||
BLI_mempool_free(bm->lpool, f1loop);
|
||||
bm->totloop--;
|
||||
BLI_mempool_free(bm->lpool, f2loop);
|
||||
bm->totloop--;
|
||||
BLI_mempool_free(bm->toolflagpool, f2->oflags);
|
||||
BLI_mempool_free(bm->fpool, f2);
|
||||
bm->totface--;
|
||||
/* account for both above */
|
||||
bm->elem_index_dirty |= BM_EDGE | BM_FACE;
|
||||
|
||||
BM_CHECK_ELEMENT(bm, f1);
|
||||
|
||||
/* validate the new loop cycle */
|
||||
edok = bmesh_loop_validate(f1);
|
||||
if (!edok) bmesh_error();
|
||||
|
||||
return f1;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH SPLICE VERT
|
||||
*
|
||||
* merges two verts into one (v into vtarget).
|
||||
*/
|
||||
static int bmesh_splicevert(BMesh *bm, BMVert *v, BMVert *vtarget)
|
||||
{
|
||||
BMEdge *e;
|
||||
BMLoop *l;
|
||||
BMIter liter;
|
||||
|
||||
/* verts already spliced */
|
||||
if (v == vtarget) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* retarget all the loops of v to vtarget */
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
|
||||
l->v = vtarget;
|
||||
}
|
||||
|
||||
/* move all the edges from v's disk to vtarget's disk */
|
||||
e = v->e;
|
||||
while (e != NULL) {
|
||||
bmesh_disk_remove_edge(e, v);
|
||||
bmesh_edge_swapverts(e, v, vtarget);
|
||||
bmesh_disk_append_edge(e, vtarget);
|
||||
e = v->e;
|
||||
}
|
||||
|
||||
BM_CHECK_ELEMENT(bm, v);
|
||||
BM_CHECK_ELEMENT(bm, vtarget);
|
||||
|
||||
/* v is unused now, and can be killed */
|
||||
BM_vert_kill(bm, v);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* BMESH CUT VERT
|
||||
*
|
||||
* cut all disjoint fans that meet at a vertex, making a unique
|
||||
* vertex for each region. returns an array of all resulting
|
||||
* vertices.
|
||||
*/
|
||||
static int bmesh_cutvert(BMesh *bm, BMVert *v, BMVert ***vout, int *len)
|
||||
{
|
||||
BMEdge **stack = NULL;
|
||||
BLI_array_declare(stack);
|
||||
BMVert **verts = NULL;
|
||||
GHash *visithash;
|
||||
BMIter eiter, liter;
|
||||
BMLoop *l;
|
||||
BMEdge *e;
|
||||
int i, maxindex;
|
||||
BMLoop *nl;
|
||||
|
||||
visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh_cutvert visithash");
|
||||
|
||||
maxindex = 0;
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
if (BLI_ghash_haskey(visithash, e)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Prime the stack with this unvisited edge */
|
||||
BLI_array_append(stack, e);
|
||||
|
||||
/* Considering only edges and faces incident on vertex v, walk
|
||||
* the edges & faces and assign an index to each connected set */
|
||||
while ((e = BLI_array_pop(stack))) {
|
||||
BLI_ghash_insert(visithash, e, SET_INT_IN_POINTER(maxindex));
|
||||
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
|
||||
nl = (l->v == v) ? l->prev : l->next;
|
||||
if (!BLI_ghash_haskey(visithash, nl->e)) {
|
||||
BLI_array_append(stack, nl->e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxindex++;
|
||||
}
|
||||
|
||||
/* Make enough verts to split v for each group */
|
||||
verts = MEM_callocN(sizeof(BMVert *) * maxindex, "bmesh_cutvert");
|
||||
verts[0] = v;
|
||||
for (i = 1; i < maxindex; i++) {
|
||||
verts[i] = BM_vert_create(bm, v->co, v);
|
||||
}
|
||||
|
||||
/* Replace v with the new verts in each group */
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
|
||||
i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, l->e));
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Loops here should alway refer to an edge that has v as an
|
||||
* endpoint. For each appearance of this vert in a face, there
|
||||
* will actually be two iterations: one for the loop heading
|
||||
* towards vertex v, and another for the loop heading out from
|
||||
* vertex v. Only need to swap the vertex on one of those times,
|
||||
* on the outgoing loop. */
|
||||
if (l->v == v) {
|
||||
l->v = verts[i];
|
||||
}
|
||||
}
|
||||
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, e));
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BLI_assert(e->v1 == v || e->v2 == v);
|
||||
bmesh_disk_remove_edge(e, v);
|
||||
bmesh_edge_swapverts(e, v, verts[i]);
|
||||
bmesh_disk_append_edge(e, verts[i]);
|
||||
}
|
||||
|
||||
BLI_ghash_free(visithash, NULL, NULL);
|
||||
BLI_array_free(stack);
|
||||
|
||||
for (i = 0; i < maxindex; i++) {
|
||||
BM_CHECK_ELEMENT(bm, verts[i]);
|
||||
}
|
||||
|
||||
if (len != NULL) {
|
||||
*len = maxindex;
|
||||
}
|
||||
|
||||
if (vout != NULL) {
|
||||
*vout = verts;
|
||||
}
|
||||
else {
|
||||
MEM_freeN(verts);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* BMESH SPLICE EDGE
|
||||
*
|
||||
* splice two unique edges which share the same two vertices into one edge.
|
||||
*
|
||||
* edges must already have the same vertices
|
||||
*/
|
||||
static int UNUSED_FUNCTION(bmesh_spliceedge)(BMesh *bm, BMEdge *e, BMEdge *etarget)
|
||||
{
|
||||
BMLoop *l;
|
||||
|
||||
if (!BM_vert_in_edge(e, etarget->v1) || !BM_vert_in_edge(e, etarget->v2)) {
|
||||
/* not the same vertices can't splice */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (e->l) {
|
||||
l = e->l;
|
||||
BLI_assert(BM_vert_in_edge(etarget, l->v));
|
||||
BLI_assert(BM_vert_in_edge(etarget, l->next->v));
|
||||
bmesh_radial_remove_loop(l, e);
|
||||
bmesh_radial_append(etarget, l);
|
||||
}
|
||||
|
||||
BLI_assert(bmesh_radial_length(e->l) == 0);
|
||||
|
||||
BM_CHECK_ELEMENT(bm, e);
|
||||
BM_CHECK_ELEMENT(bm, etarget);
|
||||
|
||||
BM_edge_kill(bm, e);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH CUT EDGE
|
||||
*
|
||||
* Cuts a single edge into two edge: the original edge and
|
||||
* a new edge that has only "cutl" in its radial.
|
||||
*
|
||||
* Does nothing if cutl is already the only loop in the
|
||||
* edge radial.
|
||||
*/
|
||||
static int bmesh_cutedge(BMesh *bm, BMEdge *e, BMLoop *cutl)
|
||||
{
|
||||
BMEdge *ne;
|
||||
int radlen;
|
||||
|
||||
BLI_assert(cutl->e == e);
|
||||
BLI_assert(e->l);
|
||||
|
||||
radlen = bmesh_radial_length(e->l);
|
||||
if (radlen < 2) {
|
||||
/* no cut required */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (cutl == e->l) {
|
||||
e->l = cutl->radial_next;
|
||||
}
|
||||
|
||||
ne = BM_edge_create(bm, e->v1, e->v2, e, FALSE);
|
||||
bmesh_radial_remove_loop(cutl, e);
|
||||
bmesh_radial_append(ne, cutl);
|
||||
cutl->e = ne;
|
||||
|
||||
BLI_assert(bmesh_radial_length(e->l) == radlen - 1);
|
||||
BLI_assert(bmesh_radial_length(ne->l) == 1);
|
||||
|
||||
BM_CHECK_ELEMENT(bm, ne);
|
||||
BM_CHECK_ELEMENT(bm, e);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH UNGLUE REGION MAKE VERT
|
||||
*
|
||||
* Disconnects a face from its vertex fan at loop sl.
|
||||
*/
|
||||
static BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *sl)
|
||||
{
|
||||
BMVert **vtar;
|
||||
int len, i;
|
||||
BMVert *nv = NULL;
|
||||
BMVert *sv = sl->v;
|
||||
|
||||
/* peel the face from the edge radials on both sides of the
|
||||
* loop vert, disconnecting the face from its fan */
|
||||
bmesh_cutedge(bm, sl->e, sl);
|
||||
bmesh_cutedge(bm, sl->prev->e, sl->prev);
|
||||
|
||||
if (bmesh_disk_count(sv) == 2) {
|
||||
/* If there are still only two edges out of sv, then
|
||||
* this whole URMV was just a no-op, so exit now. */
|
||||
return sv;
|
||||
}
|
||||
|
||||
/* Update the disk start, so that v->e points to an edge
|
||||
* not touching the split loop. This is so that bmesh_cutvert
|
||||
* will leave the original sv on some *other* fan (not the
|
||||
* one-face fan that holds the unglue face). */
|
||||
while (sv->e == sl->e || sv->e == sl->prev->e) {
|
||||
sv->e = bmesh_disk_nextedge(sv->e, sv);
|
||||
}
|
||||
|
||||
/* Split all fans connected to the vert, duplicating it for
|
||||
* each fans. */
|
||||
bmesh_cutvert(bm, sv, &vtar, &len);
|
||||
|
||||
/* There should have been at least two fans cut apart here,
|
||||
* otherwise the early exit would have kicked in. */
|
||||
BLI_assert(len >= 2);
|
||||
|
||||
nv = sl->v;
|
||||
|
||||
/* Desired result here is that a new vert should always be
|
||||
* created for the unglue face. This is so we can glue any
|
||||
* extras back into the original vert. */
|
||||
BLI_assert(nv != sv);
|
||||
BLI_assert(sv == vtar[0]);
|
||||
|
||||
/* If there are more than two verts as a result, glue together
|
||||
* all the verts except the one this URMV intended to create */
|
||||
if (len > 2) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (vtar[i] == nv) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != len) {
|
||||
/* Swap the single vert that was needed for the
|
||||
* unglue into the last array slot */
|
||||
SWAP(BMVert *, vtar[i], vtar[len - 1]);
|
||||
|
||||
/* And then glue the rest back together */
|
||||
for (i = 1; i < len - 1; i++) {
|
||||
bmesh_splicevert(bm, vtar[i], vtar[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(vtar);
|
||||
|
||||
return nv;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH UNGLUE REGION MAKE VERT
|
||||
*
|
||||
* Disconnects sf from the vertex fan at sv
|
||||
*/
|
||||
BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv)
|
||||
{
|
||||
BMLoop *l_first;
|
||||
BMLoop *l_iter;
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(sf);
|
||||
do {
|
||||
if (l_iter->v == sv) {
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
if (l_iter->v != sv) {
|
||||
/* sv is not part of sf */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bmesh_urmv_loop(bm, l_iter);
|
||||
}
|
||||
1145
source/blender/bmesh/intern/bmesh_opdefines.c
Normal file
1145
source/blender/bmesh/intern/bmesh_opdefines.c
Normal file
@@ -0,0 +1,1145 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_opdefines.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BMesh operator definitions.
|
||||
*
|
||||
* This file defines (and documents) all bmesh operators (bmops).
|
||||
*
|
||||
* Do not rename any operator or slot names! otherwise you must go
|
||||
* through the code and find all references to them!
|
||||
*
|
||||
* A word on slot names:
|
||||
*
|
||||
* For geometry input slots, the following are valid names:
|
||||
* - verts
|
||||
* - edges
|
||||
* - faces
|
||||
* - edgefacein
|
||||
* - vertfacein
|
||||
* - vertedgein
|
||||
* - vertfacein
|
||||
* - geom
|
||||
*
|
||||
* The basic rules are, for single-type geometry slots, use the plural of the
|
||||
* type name (e.g. edges). for double-type slots, use the two type names plus
|
||||
* "in" (e.g. edgefacein). for three-type slots, use geom.
|
||||
*
|
||||
* for output slots, for single-type geometry slots, use the type name plus "out",
|
||||
* (e.g. vertout), for double-type slots, use the two type names plus "out",
|
||||
* (e.g. vertfaceout), for three-type slots, use geom. note that you can also
|
||||
* use more esohteric names (e.g. skirtout) so long as the comment next to the
|
||||
* slot definition tells you what types of elements are in it.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/* ok, I'm going to write a little docgen script. so all
|
||||
* bmop comments must conform to the following template/rules:
|
||||
*
|
||||
* template (py quotes used because nested comments don't work
|
||||
* on all C compilers):
|
||||
*
|
||||
* """
|
||||
* Region Extend.
|
||||
*
|
||||
* paragraph1, Extends bleh bleh bleh.
|
||||
* Bleh Bleh bleh.
|
||||
*
|
||||
* Another paragraph.
|
||||
*
|
||||
* Another paragraph.
|
||||
* """
|
||||
*
|
||||
* so the first line is the "title" of the bmop.
|
||||
* subsequent line blocks seperated by blank lines
|
||||
* are paragraphs. individual descriptions of slots
|
||||
* would be extracted from comments
|
||||
* next to them, e.g.
|
||||
*
|
||||
* {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, boundary region
|
||||
*
|
||||
* the doc generator would automatically detect the presence of "output slot"
|
||||
* and flag the slot as an output. the same happens for "input slot". also
|
||||
* note that "edges", "faces", "verts", "loops", and "geometry" are valid
|
||||
* substitutions for "slot".
|
||||
*
|
||||
* note that slots default to being input slots.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Vertex Smooth
|
||||
*
|
||||
* Smoothes vertices by using a basic vertex averaging scheme.
|
||||
*/
|
||||
static BMOpDefine def_vertexsmooth = {
|
||||
"vertexsmooth",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
|
||||
{BMO_OP_SLOT_INT, "mirror_clip_x"}, //set vertices close to the x axis before the operation to 0
|
||||
{BMO_OP_SLOT_INT, "mirror_clip_y"}, //set vertices close to the y axis before the operation to 0
|
||||
{BMO_OP_SLOT_INT, "mirror_clip_z"}, //set vertices close to the z axis before the operation to 0
|
||||
{BMO_OP_SLOT_FLT, "clipdist"}, //clipping threshod for the above three slots
|
||||
{0} /* null-terminating sentine */,
|
||||
},
|
||||
bmesh_vertexsmooth_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Right-Hand Faces
|
||||
*
|
||||
* Computes an "outside" normal for the specified input faces.
|
||||
*/
|
||||
|
||||
static BMOpDefine def_righthandfaces = {
|
||||
"righthandfaces",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"},
|
||||
{BMO_OP_SLOT_INT, "doflip"}, //internal flag, used by bmesh_rationalize_normals
|
||||
{0} /* null-terminating sentine */,
|
||||
},
|
||||
bmesh_righthandfaces_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Region Extend
|
||||
*
|
||||
* used to implement the select more/less tools.
|
||||
* this puts some geometry surrounding regions of
|
||||
* geometry in geom into geomout.
|
||||
*
|
||||
* if usefaces is 0 then geomout spits out verts and edges,
|
||||
* otherwise it spits out faces.
|
||||
*/
|
||||
static BMOpDefine def_regionextend = {
|
||||
"regionextend",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, computed boundary geometry.
|
||||
{BMO_OP_SLOT_INT, "constrict"}, //find boundary inside the regions, not outside.
|
||||
{BMO_OP_SLOT_INT, "usefaces"}, //extend from faces instead of edges
|
||||
{0} /* null-terminating sentine */,
|
||||
},
|
||||
bmesh_regionextend_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Edge Rotate
|
||||
*
|
||||
* Rotates edges topologically. Also known as "spin edge" to some people.
|
||||
* Simple example: [/] becomes [|] then [\].
|
||||
*/
|
||||
static BMOpDefine def_edgerotate = {
|
||||
"edgerotate",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //newly spun edges
|
||||
{BMO_OP_SLOT_INT, "ccw"}, //rotate edge counter-clockwise if true, othewise clockwise
|
||||
{0} /* null-terminating sentine */,
|
||||
},
|
||||
bmesh_edgerotate_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
/*
|
||||
* Reverse Faces
|
||||
*
|
||||
* Reverses the winding (vertex order) of faces. This has the effect of
|
||||
* flipping the normal.
|
||||
*/
|
||||
static BMOpDefine def_reversefaces = {
|
||||
"reversefaces",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces
|
||||
{0} /* null-terminating sentine */,
|
||||
},
|
||||
bmesh_reversefaces_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Edge Bisect
|
||||
*
|
||||
* Splits input edges (but doesn't do anything else).
|
||||
* This creates a 2-valence vert.
|
||||
*/
|
||||
static BMOpDefine def_edgebisect = {
|
||||
"edgebisect",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges
|
||||
{BMO_OP_SLOT_INT, "numcuts"}, //number of cuts
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "outsplit"}, //newly created vertices and edges
|
||||
{0} /* null-terminating sentine */,
|
||||
},
|
||||
esplit_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
/*
|
||||
* Mirror
|
||||
*
|
||||
* Mirrors geometry along an axis. The resulting geometry is welded on using
|
||||
* mergedist. Pairs of original/mirrored vertices are welded using the mergedist
|
||||
* parameter (which defines the minimum distance for welding to happen).
|
||||
*/
|
||||
|
||||
static BMOpDefine def_mirror = {
|
||||
"mirror",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix defining the mirror transformation
|
||||
{BMO_OP_SLOT_FLT, "mergedist"}, //maximum distance for merging. does no merging if 0.
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "newout"}, //output geometry, mirrored
|
||||
{BMO_OP_SLOT_INT, "axis"}, //the axis to use, 0, 1, or 2 for x, y, z
|
||||
{BMO_OP_SLOT_INT, "mirror_u"}, //mirror UVs across the u axis
|
||||
{BMO_OP_SLOT_INT, "mirror_v"}, //mirror UVs across the v axis
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_mirror_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Find Doubles
|
||||
*
|
||||
* Takes input verts and find vertices they should weld to. Outputs a
|
||||
* mapping slot suitable for use with the weld verts bmop.
|
||||
*/
|
||||
static BMOpDefine def_finddoubles = {
|
||||
"finddoubles",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "keepverts"}, //list of verts to keep
|
||||
{BMO_OP_SLOT_FLT, "dist"}, //minimum distance
|
||||
{BMO_OP_SLOT_MAPPING, "targetmapout"},
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_finddoubles_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Remove Doubles
|
||||
*
|
||||
* Finds groups of vertices closer then dist and merges them together,
|
||||
* using the weld verts bmop.
|
||||
*/
|
||||
static BMOpDefine def_removedoubles = {
|
||||
"removedoubles",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts
|
||||
{BMO_OP_SLOT_FLT, "dist"}, //minimum distance
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_removedoubles_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Auto Merge
|
||||
*
|
||||
* Finds groups of vertices closer then dist and merges them together,
|
||||
* using the weld verts bmop. The merges must go from a vert not in
|
||||
* verts to one in verts.
|
||||
*/
|
||||
static BMOpDefine def_automerge = {
|
||||
"automerge",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts
|
||||
{BMO_OP_SLOT_FLT, "dist"}, //minimum distance
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_automerge_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Collapse Connected
|
||||
*
|
||||
* Collapses connected vertices
|
||||
*/
|
||||
static BMOpDefine def_collapse = {
|
||||
"collapse",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_collapse_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Facedata point Merge
|
||||
*
|
||||
* Merge uv/vcols at a specific vertex.
|
||||
*/
|
||||
static BMOpDefine def_pointmerge_facedata = {
|
||||
"pointmerge_facedata",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "snapv"}, /* snap verte */
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_pointmerge_facedata_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Average Vertices Facevert Data
|
||||
*
|
||||
* Merge uv/vcols associated with the input vertices at
|
||||
* the bounding box center. (I know, it's not averaging but
|
||||
* the vert_snap_to_bb_center is just too long).
|
||||
*/
|
||||
static BMOpDefine def_vert_average_facedata = {
|
||||
"vert_average_facedata",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_vert_average_facedata_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Point Merge
|
||||
*
|
||||
* Merge verts together at a point.
|
||||
*/
|
||||
static BMOpDefine def_pointmerge = {
|
||||
"pointmerge",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */
|
||||
{BMO_OP_SLOT_VEC, "mergeco"},
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_pointmerge_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Collapse Connected UVs
|
||||
*
|
||||
* Collapses connected UV vertices.
|
||||
*/
|
||||
static BMOpDefine def_collapse_uvs = {
|
||||
"collapse_uvs",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_collapsecon_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Weld Verts
|
||||
*
|
||||
* Welds verts together (kindof like remove doubles, merge, etc, all of which
|
||||
* use or will use this bmop). You pass in mappings from vertices to the vertices
|
||||
* they weld with.
|
||||
*/
|
||||
static BMOpDefine def_weldverts = {
|
||||
"weldverts",
|
||||
{{BMO_OP_SLOT_MAPPING, "targetmap"}, /* maps welded vertices to verts they should weld to */
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_weldverts_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Make Vertex
|
||||
*
|
||||
* Creates a single vertex; this bmop was necassary
|
||||
* for click-create-vertex.
|
||||
*/
|
||||
static BMOpDefine def_makevert = {
|
||||
"makevert",
|
||||
{{BMO_OP_SLOT_VEC, "co"}, //the coordinate of the new vert
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "newvertout"}, //the new vert
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_makevert_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Join Triangles
|
||||
*
|
||||
* Tries to intelligently join triangles according
|
||||
* to various settings and stuff.
|
||||
*/
|
||||
static BMOpDefine def_join_triangles = {
|
||||
"join_triangles",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input geometry.
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //joined faces
|
||||
{BMO_OP_SLOT_INT, "compare_sharp"},
|
||||
{BMO_OP_SLOT_INT, "compare_uvs"},
|
||||
{BMO_OP_SLOT_INT, "compare_vcols"},
|
||||
{BMO_OP_SLOT_INT, "compare_materials"},
|
||||
{BMO_OP_SLOT_FLT, "limit"},
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_jointriangles_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Contextual Create
|
||||
*
|
||||
* This is basically fkey, it creates
|
||||
* new faces from vertices, makes stuff from edge nets,
|
||||
* makes wire edges, etc. It also dissolves
|
||||
* faces.
|
||||
*
|
||||
* Three verts become a triangle, four become a quad. Two
|
||||
* become a wire edge.
|
||||
*/
|
||||
static BMOpDefine def_contextual_create = {
|
||||
"contextual_create",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry.
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //newly-made face(s)
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_contextual_create_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES,
|
||||
};
|
||||
|
||||
/*
|
||||
* Bridge edge loops with faces
|
||||
*/
|
||||
static BMOpDefine def_bridge_loops = {
|
||||
"bridge_loops",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_bridge_loops_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
static BMOpDefine def_edgenet_fill = {
|
||||
"edgenet_fill",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */
|
||||
{BMO_OP_SLOT_MAPPING, "restrict"}, /* restricts edges to groups. maps edges to integer */
|
||||
{BMO_OP_SLOT_INT, "use_restrict"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "excludefaces"}, /* list of faces to ignore for manifold check */
|
||||
{BMO_OP_SLOT_MAPPING, "faceout_groupmap"}, /* maps new faces to the group numbers they came fro */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_edgenet_fill_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Edgenet Prepare
|
||||
*
|
||||
* Identifies several useful edge loop cases and modifies them so
|
||||
* they'll become a face when edgenet_fill is called. The cases covered are:
|
||||
*
|
||||
* - One single loop; an edge is added to connect the ends
|
||||
* - Two loops; two edges are added to connect the endpoints (based on the
|
||||
* shortest distance between each endpont).
|
||||
*/
|
||||
static BMOpDefine def_edgenet_prepare = {
|
||||
"edgenet_prepare",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //new edges
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_edgenet_prepare,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Rotate
|
||||
*
|
||||
* Rotate vertices around a center, using a 3x3 rotation
|
||||
* matrix. Equivilent of the old rotateflag function.
|
||||
*/
|
||||
static BMOpDefine def_rotate = {
|
||||
"rotate",
|
||||
{{BMO_OP_SLOT_VEC, "cent"}, //center of rotation
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix defining rotation
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_rotate_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Translate
|
||||
*
|
||||
* Translate vertices by an offset. Equivelent of the
|
||||
* old translateflag function.
|
||||
*/
|
||||
static BMOpDefine def_translate = {
|
||||
"translate",
|
||||
{{BMO_OP_SLOT_VEC, "vec"}, //translation offset
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_translate_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Scale
|
||||
*
|
||||
* Scales vertices by an offset.
|
||||
*/
|
||||
static BMOpDefine def_scale = {
|
||||
"scale",
|
||||
{{BMO_OP_SLOT_VEC, "vec"}, //scale factor
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_scale_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Transform
|
||||
*
|
||||
* Transforms a set of vertices by a matrix. Multiplies
|
||||
* the vertex coordinates with the matrix.
|
||||
*/
|
||||
static BMOpDefine def_transform = {
|
||||
"transform",
|
||||
{{BMO_OP_SLOT_MAT, "mat"}, //transform matrix
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_transform_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Object Load BMesh
|
||||
*
|
||||
* Loads a bmesh into an object/mesh. This is a "private"
|
||||
* bmop.
|
||||
*/
|
||||
static BMOpDefine def_object_load_bmesh = {
|
||||
"object_load_bmesh",
|
||||
{{BMO_OP_SLOT_PNT, "scene"},
|
||||
{BMO_OP_SLOT_PNT, "object"},
|
||||
{0, /* null-terminating sentine */}},
|
||||
object_load_bmesh_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* BMesh to Mesh
|
||||
*
|
||||
* Converts a bmesh to a Mesh. This is reserved for exiting editmode.
|
||||
*/
|
||||
static BMOpDefine def_bmesh_to_mesh = {
|
||||
"bmesh_to_mesh",
|
||||
{{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a mesh structure to fill in
|
||||
{BMO_OP_SLOT_PNT, "object"}, //pointer to an object structure
|
||||
{BMO_OP_SLOT_INT, "notesselation"}, //don't calculate mfaces
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_to_mesh_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Mesh to BMesh
|
||||
*
|
||||
* Load the contents of a mesh into the bmesh. this bmop is private, it's
|
||||
* reserved exclusively for entering editmode.
|
||||
*/
|
||||
static BMOpDefine def_mesh_to_bmesh = {
|
||||
"mesh_to_bmesh",
|
||||
{{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a Mesh structure
|
||||
{BMO_OP_SLOT_PNT, "object"}, //pointer to an Object structure
|
||||
{BMO_OP_SLOT_INT, "set_shapekey"}, //load active shapekey coordinates into verts
|
||||
{0, /* null-terminating sentine */}},
|
||||
mesh_to_bmesh_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Individual Face Extrude
|
||||
*
|
||||
* Extrudes faces individually.
|
||||
*/
|
||||
static BMOpDefine def_extrude_indivface = {
|
||||
"extrude_face_indiv",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //output faces
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "skirtout"}, //output skirt geometry, faces and edges
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_extrude_face_indiv_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Extrude Only Edges
|
||||
*
|
||||
* Extrudes Edges into faces, note that this is very simple, there's no fancy
|
||||
* winged extrusion.
|
||||
*/
|
||||
static BMOpDefine def_extrude_onlyedge = {
|
||||
"extrude_edge_only",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input vertices
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output geometry
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_extrude_onlyedge_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Individual Vertex Extrude
|
||||
*
|
||||
* Extrudes wire edges from vertices.
|
||||
*/
|
||||
static BMOpDefine def_extrudeverts_indiv = {
|
||||
"extrude_vert_indiv",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //output wire edges
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output vertices
|
||||
{0} /* null-terminating sentine */},
|
||||
extrude_vert_indiv_exec,
|
||||
0
|
||||
};
|
||||
|
||||
static BMOpDefine def_connectverts = {
|
||||
"connectverts",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"},
|
||||
{0} /* null-terminating sentine */},
|
||||
connectverts_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_extrudefaceregion = {
|
||||
"extrudefaceregion",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edgefacein"},
|
||||
{BMO_OP_SLOT_MAPPING, "exclude"},
|
||||
{BMO_OP_SLOT_INT, "alwayskeeporig"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"},
|
||||
{0} /* null-terminating sentine */},
|
||||
extrude_edge_context_exec,
|
||||
0
|
||||
};
|
||||
|
||||
static BMOpDefine def_dissolvevertsop = {
|
||||
"dissolveverts",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"},
|
||||
{0} /* null-terminating sentine */},
|
||||
dissolveverts_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_dissolveedgessop = {
|
||||
"dissolveedges",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "regionout"},
|
||||
{BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges.
|
||||
{0} /* null-terminating sentine */},
|
||||
dissolveedges_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_dissolveedgeloopsop = {
|
||||
"dissolveedgeloop",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "regionout"},
|
||||
{0} /* null-terminating sentine */},
|
||||
dissolve_edgeloop_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_dissolvefacesop = {
|
||||
"dissolvefaces",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "regionout"},
|
||||
{BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges.
|
||||
{0} /* null-terminating sentine */},
|
||||
dissolvefaces_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_dissolvelimitop = {
|
||||
"dissolvelimit",
|
||||
{{BMO_OP_SLOT_FLT, "angle_limit"}, /* total rotation angle (degrees) */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "verts"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
|
||||
{0} /* null-terminating sentine */},
|
||||
dissolvelimit_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_triangop = {
|
||||
"triangulate",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"},
|
||||
{BMO_OP_SLOT_MAPPING, "facemap"},
|
||||
{0} /* null-terminating sentine */},
|
||||
triangulate_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_subdop = {
|
||||
"esubd",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
|
||||
{BMO_OP_SLOT_INT, "numcuts"},
|
||||
{BMO_OP_SLOT_FLT, "smooth"},
|
||||
{BMO_OP_SLOT_FLT, "fractal"},
|
||||
{BMO_OP_SLOT_INT, "beauty"},
|
||||
{BMO_OP_SLOT_INT, "seed"},
|
||||
{BMO_OP_SLOT_MAPPING, "custompatterns"},
|
||||
{BMO_OP_SLOT_MAPPING, "edgepercents"},
|
||||
|
||||
/* these next three can have multiple types of elements in them */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "outinner"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "outsplit"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* contains all output geometr */
|
||||
|
||||
{BMO_OP_SLOT_INT, "quadcornertype"}, //quad corner type, see bmesh_operators.h
|
||||
{BMO_OP_SLOT_INT, "gridfill"}, //fill in fully-selected faces with a grid
|
||||
{BMO_OP_SLOT_INT, "singleedge"}, //tesselate the case of one edge selected in a quad or triangle
|
||||
|
||||
{0} /* null-terminating sentine */,
|
||||
},
|
||||
esubdivide_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
static BMOpDefine def_delop = {
|
||||
"del",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, {BMO_OP_SLOT_INT, "context"},
|
||||
{0} /* null-terminating sentine */},
|
||||
delop_exec,
|
||||
0
|
||||
};
|
||||
|
||||
static BMOpDefine def_dupeop = {
|
||||
"dupe",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "origout"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "newout"},
|
||||
/* facemap maps from source faces to dupe
|
||||
* faces, and from dupe faces to source faces */
|
||||
{BMO_OP_SLOT_MAPPING, "facemap"},
|
||||
{BMO_OP_SLOT_MAPPING, "boundarymap"},
|
||||
{BMO_OP_SLOT_MAPPING, "isovertmap"},
|
||||
{BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */
|
||||
{0} /* null-terminating sentine */},
|
||||
dupeop_exec,
|
||||
0
|
||||
};
|
||||
|
||||
static BMOpDefine def_splitop = {
|
||||
"split",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"},
|
||||
{BMO_OP_SLOT_MAPPING, "boundarymap"},
|
||||
{BMO_OP_SLOT_MAPPING, "isovertmap"},
|
||||
{BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */
|
||||
{0} /* null-terminating sentine */},
|
||||
splitop_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Spin
|
||||
*
|
||||
* Extrude or duplicate geometry a number of times,
|
||||
* rotating and possibly translating after each step
|
||||
*/
|
||||
static BMOpDefine def_spinop = {
|
||||
"spin",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "lastout"}, /* result of last step */
|
||||
{BMO_OP_SLOT_VEC, "cent"}, /* rotation center */
|
||||
{BMO_OP_SLOT_VEC, "axis"}, /* rotation axis */
|
||||
{BMO_OP_SLOT_VEC, "dvec"}, /* translation delta per step */
|
||||
{BMO_OP_SLOT_FLT, "ang"}, /* total rotation angle (degrees) */
|
||||
{BMO_OP_SLOT_INT, "steps"}, /* number of steps */
|
||||
{BMO_OP_SLOT_INT, "dupli"}, /* duplicate or extrude? */
|
||||
{0} /* null-terminating sentine */},
|
||||
spinop_exec,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Similar faces search
|
||||
*
|
||||
* Find similar faces (area/material/perimeter, ...).
|
||||
*/
|
||||
static BMOpDefine def_similarfaces = {
|
||||
"similarfaces",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* output faces */
|
||||
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
|
||||
{BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_similarfaces_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Similar edges search
|
||||
*
|
||||
* Find similar edges (length, direction, edge, seam, ...).
|
||||
*/
|
||||
static BMOpDefine def_similaredges = {
|
||||
"similaredges",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, /* output edges */
|
||||
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
|
||||
{BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_similaredges_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Similar vertices search
|
||||
*
|
||||
* Find similar vertices (normal, face, vertex group, ...).
|
||||
*/
|
||||
static BMOpDefine def_similarverts = {
|
||||
"similarverts",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertices */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */
|
||||
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
|
||||
{BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_similarverts_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* uv rotation
|
||||
* cycle the uvs
|
||||
*/
|
||||
static BMOpDefine def_meshrotateuvs = {
|
||||
"meshrotateuvs",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
|
||||
{BMO_OP_SLOT_INT, "dir"}, /* direction */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_rotateuvs_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* uv reverse
|
||||
* reverse the uvs
|
||||
*/
|
||||
static BMOpDefine def_meshreverseuvs = {
|
||||
"meshreverseuvs",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_reverseuvs_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* color rotation
|
||||
* cycle the colors
|
||||
*/
|
||||
static BMOpDefine def_meshrotatecolors = {
|
||||
"meshrotatecolors",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
|
||||
{BMO_OP_SLOT_INT, "dir"}, /* direction */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_rotatecolors_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* color reverse
|
||||
* reverse the colors
|
||||
*/
|
||||
static BMOpDefine def_meshreversecolors = {
|
||||
"meshreversecolors",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_reversecolors_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Similar vertices search
|
||||
*
|
||||
* Find similar vertices (normal, face, vertex group, ...).
|
||||
*/
|
||||
static BMOpDefine def_vertexshortestpath = {
|
||||
"vertexshortestpath",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "startv"}, /* start vertex */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "endv"}, /* end vertex */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */
|
||||
{BMO_OP_SLOT_INT, "type"}, /* type of selection */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_vertexshortestpath_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Edge Split
|
||||
*
|
||||
* Disconnects faces along input edges.
|
||||
*/
|
||||
static BMOpDefine def_edgesplit = {
|
||||
"edgesplit",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout1"}, /* old output disconnected edges */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "edgeout2"}, /* new output disconnected edges */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_edgesplitop_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
/*
|
||||
* Create Grid
|
||||
*
|
||||
* Creates a grid with a variable number of subdivisions
|
||||
*/
|
||||
static BMOpDefine def_create_grid = {
|
||||
"create_grid",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
|
||||
{BMO_OP_SLOT_INT, "xsegments"}, //number of x segments
|
||||
{BMO_OP_SLOT_INT, "ysegments"}, //number of y segments
|
||||
{BMO_OP_SLOT_FLT, "size"}, //size of the grid
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_create_grid_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create UV Sphere
|
||||
*
|
||||
* Creates a grid with a variable number of subdivisions
|
||||
*/
|
||||
static BMOpDefine def_create_uvsphere = {
|
||||
"create_uvsphere",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
|
||||
{BMO_OP_SLOT_INT, "segments"}, //number of u segments
|
||||
{BMO_OP_SLOT_INT, "revolutions"}, //number of v segment
|
||||
{BMO_OP_SLOT_FLT, "diameter"}, //diameter
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_create_uvsphere_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create Ico Sphere
|
||||
*
|
||||
* Creates a grid with a variable number of subdivisions
|
||||
*/
|
||||
static BMOpDefine def_create_icosphere = {
|
||||
"create_icosphere",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
|
||||
{BMO_OP_SLOT_INT, "subdivisions"}, //how many times to recursively subdivide the sphere
|
||||
{BMO_OP_SLOT_FLT, "diameter"}, //diameter
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_create_icosphere_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create Suzanne
|
||||
*
|
||||
* Creates a monkey. Be wary.
|
||||
*/
|
||||
static BMOpDefine def_create_monkey = {
|
||||
"create_monkey",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_create_monkey_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create Cone
|
||||
*
|
||||
* Creates a cone with variable depth at both ends
|
||||
*/
|
||||
static BMOpDefine def_create_cone = {
|
||||
"create_cone",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
|
||||
{BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces
|
||||
{BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons
|
||||
{BMO_OP_SLOT_INT, "segments"},
|
||||
{BMO_OP_SLOT_FLT, "diameter1"}, //diameter of one end
|
||||
{BMO_OP_SLOT_FLT, "diameter2"}, //diameter of the opposite
|
||||
{BMO_OP_SLOT_FLT, "depth"}, //distance between ends
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_create_cone_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a circle
|
||||
*/
|
||||
static BMOpDefine def_create_circle = {
|
||||
"create_circle",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
|
||||
{BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces
|
||||
{BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons
|
||||
{BMO_OP_SLOT_INT, "segments"},
|
||||
{BMO_OP_SLOT_FLT, "diameter"}, //diameter of one end
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_create_circle_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create Cone
|
||||
*
|
||||
* Creates a cone with variable depth at both ends
|
||||
*/
|
||||
static BMOpDefine def_create_cube = {
|
||||
"create_cube",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts
|
||||
{BMO_OP_SLOT_FLT, "size"}, //size of the cube
|
||||
{BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with--
|
||||
{0, /* null-terminating sentine */}},
|
||||
bmesh_create_cube_exec,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Bevel
|
||||
*
|
||||
* Bevels edges and vertices
|
||||
*/
|
||||
static BMOpDefine def_bevel = {
|
||||
"bevel",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, /* input edges and vertices */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "face_spans"}, /* new geometry */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "face_holes"}, /* new geometry */
|
||||
{BMO_OP_SLOT_INT, "use_lengths"}, /* grab edge lengths from a PROP_FLT customdata laye */
|
||||
{BMO_OP_SLOT_INT, "use_even"}, /* corner vert placement: use shell/angle calculations */
|
||||
{BMO_OP_SLOT_INT, "use_dist"}, /* corner vert placement: evaluate percent as a distance,
|
||||
* modifier uses this. We could do this as another float setting */
|
||||
{BMO_OP_SLOT_INT, "lengthlayer"}, /* which PROP_FLT layer to us */
|
||||
{BMO_OP_SLOT_FLT, "percent"}, /* percentage to expand bevelled edge */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_bevel_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
/*
|
||||
* Beautify Fill
|
||||
*
|
||||
* Makes triangle a bit nicer
|
||||
*/
|
||||
static BMOpDefine def_beautify_fill = {
|
||||
"beautify_fill",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "constrain_edges"}, /* edges that can't be flipped */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new flipped faces and edges */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_beautify_fill_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
/*
|
||||
* Triangle Fill
|
||||
*
|
||||
* Fill edges with triangles
|
||||
*/
|
||||
static BMOpDefine def_triangle_fill = {
|
||||
"triangle_fill",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new faces and edges */
|
||||
{0} /* null-terminating sentine */},
|
||||
bmesh_triangle_fill_exec,
|
||||
BMO_OP_FLAG_UNTAN_MULTIRES
|
||||
};
|
||||
|
||||
/*
|
||||
* Solidify
|
||||
*
|
||||
* Turns a mesh into a shell with thickness
|
||||
*/
|
||||
static BMOpDefine def_solidify = {
|
||||
"solidify",
|
||||
{{BMO_OP_SLOT_ELEMENT_BUF, "geom"},
|
||||
{BMO_OP_SLOT_FLT, "thickness"},
|
||||
{BMO_OP_SLOT_ELEMENT_BUF, "geomout"},
|
||||
{0}},
|
||||
bmesh_solidify_face_region_exec,
|
||||
0
|
||||
};
|
||||
|
||||
BMOpDefine *opdefines[] = {
|
||||
&def_splitop,
|
||||
&def_spinop,
|
||||
&def_dupeop,
|
||||
&def_delop,
|
||||
&def_subdop,
|
||||
&def_triangop,
|
||||
&def_dissolvefacesop,
|
||||
&def_dissolveedgessop,
|
||||
&def_dissolveedgeloopsop,
|
||||
&def_dissolvevertsop,
|
||||
&def_dissolvelimitop,
|
||||
&def_extrudefaceregion,
|
||||
&def_connectverts,
|
||||
//&def_makeprim,
|
||||
&def_extrudeverts_indiv,
|
||||
&def_mesh_to_bmesh,
|
||||
&def_object_load_bmesh,
|
||||
&def_transform,
|
||||
&def_translate,
|
||||
&def_rotate,
|
||||
&def_edgenet_fill,
|
||||
&def_contextual_create,
|
||||
&def_makevert,
|
||||
&def_weldverts,
|
||||
&def_removedoubles,
|
||||
&def_finddoubles,
|
||||
&def_mirror,
|
||||
&def_edgebisect,
|
||||
&def_reversefaces,
|
||||
&def_edgerotate,
|
||||
&def_regionextend,
|
||||
&def_righthandfaces,
|
||||
&def_vertexsmooth,
|
||||
&def_extrude_onlyedge,
|
||||
&def_extrude_indivface,
|
||||
&def_collapse_uvs,
|
||||
&def_pointmerge,
|
||||
&def_collapse,
|
||||
&def_similarfaces,
|
||||
&def_similaredges,
|
||||
&def_similarverts,
|
||||
&def_pointmerge_facedata,
|
||||
&def_vert_average_facedata,
|
||||
&def_meshrotateuvs,
|
||||
&def_bmesh_to_mesh,
|
||||
&def_meshreverseuvs,
|
||||
&def_edgenet_prepare,
|
||||
&def_meshrotatecolors,
|
||||
&def_meshreversecolors,
|
||||
&def_vertexshortestpath,
|
||||
&def_scale,
|
||||
&def_edgesplit,
|
||||
&def_automerge,
|
||||
&def_create_uvsphere,
|
||||
&def_create_grid,
|
||||
&def_create_icosphere,
|
||||
&def_create_monkey,
|
||||
&def_create_cube,
|
||||
&def_create_circle,
|
||||
&def_create_cone,
|
||||
&def_join_triangles,
|
||||
&def_bevel,
|
||||
&def_beautify_fill,
|
||||
&def_triangle_fill,
|
||||
&def_bridge_loops,
|
||||
&def_solidify,
|
||||
};
|
||||
|
||||
int bmesh_total_ops = (sizeof(opdefines) / sizeof(void *));
|
||||
1376
source/blender/bmesh/intern/bmesh_operators.c
Normal file
1376
source/blender/bmesh/intern/bmesh_operators.c
Normal file
@@ -0,0 +1,1376 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_operators.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BMesh operator access.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_memarena.h"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_array.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/* forward declarations */
|
||||
static void bmo_flag_layer_alloc(BMesh *bm);
|
||||
static void bmo_flag_layer_free(BMesh *bm);
|
||||
static void bmo_flag_layer_clear(BMesh *bm);
|
||||
static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name);
|
||||
static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name);
|
||||
static int bmesh_opname_to_opcode(const char *opname);
|
||||
|
||||
static const char *bmo_error_messages[] = {
|
||||
NULL,
|
||||
"Self intersection error",
|
||||
"Could not dissolve vert",
|
||||
"Could not connect vertices",
|
||||
"Could not traverse mesh",
|
||||
"Could not dissolve faces",
|
||||
"Could not dissolve vertices",
|
||||
"Tesselation error",
|
||||
"Can not deal with non-manifold geometry",
|
||||
"Invalid selection",
|
||||
"Internal mesh error",
|
||||
};
|
||||
|
||||
|
||||
/* operator slot type information - size of one element of the type given. */
|
||||
const int BMO_OPSLOT_TYPEINFO[] = {
|
||||
0,
|
||||
sizeof(int),
|
||||
sizeof(float),
|
||||
sizeof(void *),
|
||||
0, /* unused */
|
||||
0, /* unused */
|
||||
0, /* unused */
|
||||
sizeof(void *), /* pointer buffer */
|
||||
sizeof(BMOElemMapping)
|
||||
};
|
||||
|
||||
/* Dummy slot so there is something to return when slot name lookup fails */
|
||||
static BMOpSlot BMOpEmptySlot = {0};
|
||||
|
||||
void BMO_op_flag_enable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag)
|
||||
{
|
||||
op->flag |= op_flag;
|
||||
}
|
||||
|
||||
void BMO_op_flag_disable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag)
|
||||
{
|
||||
op->flag &= ~op_flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK PUSH
|
||||
*
|
||||
* Pushes the opstack down one level
|
||||
* and allocates a new flag layer if
|
||||
* appropriate.
|
||||
*/
|
||||
void BMO_push(BMesh *bm, BMOperator *UNUSED(op))
|
||||
{
|
||||
bm->stackdepth++;
|
||||
|
||||
/* add flag layer, if appropriate */
|
||||
if (bm->stackdepth > 1)
|
||||
bmo_flag_layer_alloc(bm);
|
||||
else
|
||||
bmo_flag_layer_clear(bm);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK POP
|
||||
*
|
||||
* Pops the opstack one level
|
||||
* and frees a flag layer if appropriate
|
||||
* BMESH_TODO: investigate NOT freeing flag
|
||||
* layers.
|
||||
*/
|
||||
void BMO_pop(BMesh *bm)
|
||||
{
|
||||
if (bm->stackdepth > 1)
|
||||
bmo_flag_layer_free(bm);
|
||||
|
||||
bm->stackdepth--;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK INIT OP
|
||||
*
|
||||
* Initializes an operator structure
|
||||
* to a certain type
|
||||
*/
|
||||
void BMO_op_init(BMesh *bm, BMOperator *op, const char *opname)
|
||||
{
|
||||
int i, opcode = bmesh_opname_to_opcode(opname);
|
||||
|
||||
#ifdef DEBUG
|
||||
BM_ELEM_INDEX_VALIDATE(bm, "pre bmo", opname);
|
||||
#else
|
||||
(void)bm;
|
||||
#endif
|
||||
|
||||
if (opcode == -1) {
|
||||
opcode = 0; /* error!, already printed, have a better way to handle this? */
|
||||
}
|
||||
|
||||
memset(op, 0, sizeof(BMOperator));
|
||||
op->type = opcode;
|
||||
op->flag = opdefines[opcode]->flag;
|
||||
|
||||
/* initialize the operator slot types */
|
||||
for (i = 0; opdefines[opcode]->slottypes[i].type; i++) {
|
||||
op->slots[i].slottype = opdefines[opcode]->slottypes[i].type;
|
||||
op->slots[i].index = i;
|
||||
}
|
||||
|
||||
/* callback */
|
||||
op->exec = opdefines[opcode]->exec;
|
||||
|
||||
/* memarena, used for operator's slot buffers */
|
||||
op->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmesh operator");
|
||||
BLI_memarena_use_calloc (op->arena);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK EXEC OP
|
||||
*
|
||||
* Executes a passed in operator. This handles
|
||||
* the allocation and freeing of temporary flag
|
||||
* layers and starting/stopping the modelling
|
||||
* loop. Can be called from other operators
|
||||
* exec callbacks as well.
|
||||
*/
|
||||
void BMO_op_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
|
||||
BMO_push(bm, op);
|
||||
|
||||
if (bm->stackdepth == 2)
|
||||
bmesh_begin_edit(bm, op->flag);
|
||||
op->exec(bm, op);
|
||||
|
||||
if (bm->stackdepth == 2)
|
||||
bmesh_end_edit(bm, op->flag);
|
||||
|
||||
BMO_pop(bm);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK FINISH OP
|
||||
*
|
||||
* Does housekeeping chores related to finishing
|
||||
* up an operator.
|
||||
*/
|
||||
void BMO_op_finish(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOpSlot *slot;
|
||||
int i;
|
||||
|
||||
for (i = 0; opdefines[op->type]->slottypes[i].type; i++) {
|
||||
slot = &op->slots[i];
|
||||
if (slot->slottype == BMO_OP_SLOT_MAPPING) {
|
||||
if (slot->data.ghash)
|
||||
BLI_ghash_free(slot->data.ghash, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_memarena_free(op->arena);
|
||||
|
||||
#ifdef DEBUG
|
||||
BM_ELEM_INDEX_VALIDATE(bm, "post bmo", opdefines[op->type]->name);
|
||||
#else
|
||||
(void)bm;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK HAS SLOT
|
||||
*
|
||||
* Returns 1 if the named slot exists on the given operator,
|
||||
* otherwise returns 0.
|
||||
*/
|
||||
int BMO_slot_exists(BMOperator *op, const char *slotname)
|
||||
{
|
||||
int slotcode = bmesh_name_to_slotcode(opdefines[op->type], slotname);
|
||||
return (slotcode >= 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK GET SLOT
|
||||
*
|
||||
* Returns a pointer to the slot of
|
||||
* type 'slotcode'
|
||||
*/
|
||||
BMOpSlot *BMO_slot_get(BMOperator *op, const char *slotname)
|
||||
{
|
||||
int slotcode = bmesh_name_to_slotcode_check(opdefines[op->type], slotname);
|
||||
|
||||
if (slotcode < 0) {
|
||||
return &BMOpEmptySlot;
|
||||
}
|
||||
|
||||
return &(op->slots[slotcode]);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK COPY SLOT
|
||||
*
|
||||
* Copies data from one slot to another
|
||||
*/
|
||||
void BMO_slot_copy(BMOperator *source_op, BMOperator *dest_op, const char *src, const char *dst)
|
||||
{
|
||||
BMOpSlot *source_slot = BMO_slot_get(source_op, src);
|
||||
BMOpSlot *dest_slot = BMO_slot_get(dest_op, dst);
|
||||
|
||||
if (source_slot == dest_slot)
|
||||
return;
|
||||
|
||||
if (source_slot->slottype != dest_slot->slottype)
|
||||
return;
|
||||
|
||||
if (dest_slot->slottype > BMO_OP_SLOT_VEC) {
|
||||
if (dest_slot->slottype != BMO_OP_SLOT_MAPPING) {
|
||||
/* do buffer copy */
|
||||
dest_slot->data.buf = NULL;
|
||||
dest_slot->len = source_slot->len;
|
||||
if (dest_slot->len) {
|
||||
const int slot_alloc_size = BMO_OPSLOT_TYPEINFO[dest_slot->slottype] * dest_slot->len;
|
||||
dest_slot->data.buf = BLI_memarena_alloc(dest_op->arena, slot_alloc_size);
|
||||
memcpy(dest_slot->data.buf, source_slot->data.buf, slot_alloc_size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
GHashIterator it;
|
||||
BMOElemMapping *srcmap, *dstmap;
|
||||
|
||||
/* sanity check */
|
||||
if (!source_slot->data.ghash) return;
|
||||
|
||||
if (!dest_slot->data.ghash) {
|
||||
dest_slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash,
|
||||
BLI_ghashutil_ptrcmp, "bmesh operator 2");
|
||||
}
|
||||
|
||||
BLI_ghashIterator_init(&it, source_slot->data.ghash);
|
||||
for ( ; (srcmap = BLI_ghashIterator_getValue(&it));
|
||||
BLI_ghashIterator_step(&it))
|
||||
{
|
||||
dstmap = BLI_memarena_alloc(dest_op->arena, sizeof(*dstmap) + srcmap->len);
|
||||
|
||||
dstmap->element = srcmap->element;
|
||||
dstmap->len = srcmap->len;
|
||||
memcpy(dstmap + 1, srcmap + 1, srcmap->len);
|
||||
|
||||
BLI_ghash_insert(dest_slot->data.ghash, dstmap->element, dstmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dest_slot->data = source_slot->data;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH OPSTACK SET XXX
|
||||
*
|
||||
* Sets the value of a slot depending on it's type
|
||||
*
|
||||
*/
|
||||
|
||||
void BMO_slot_float_set(BMOperator *op, const char *slotname, const float f)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_FLT))
|
||||
return;
|
||||
|
||||
slot->data.f = f;
|
||||
}
|
||||
|
||||
void BMO_slot_int_set(BMOperator *op, const char *slotname, const int i)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_INT))
|
||||
return;
|
||||
|
||||
slot->data.i = i;
|
||||
}
|
||||
|
||||
/* only supports square mats */
|
||||
void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_MAT))
|
||||
return;
|
||||
|
||||
slot->len = 4;
|
||||
slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float) * 4 * 4);
|
||||
|
||||
if (size == 4) {
|
||||
memcpy(slot->data.p, mat, sizeof(float) * 4 * 4);
|
||||
}
|
||||
else if (size == 3) {
|
||||
copy_m4_m3(slot->data.p, (float (*)[3])mat);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: invalid size argument %d (bmesh internal error)\n", __func__, size);
|
||||
|
||||
memset(slot->data.p, 0, sizeof(float) * 4 * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4])
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_MAT))
|
||||
return;
|
||||
|
||||
copy_m4_m4(r_mat, (float (*)[4])slot->data.p);
|
||||
}
|
||||
|
||||
void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3])
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_MAT))
|
||||
return;
|
||||
|
||||
copy_m3_m4(r_mat, slot->data.p);
|
||||
}
|
||||
|
||||
void BMO_slot_ptr_set(BMOperator *op, const char *slotname, void *p)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_PNT))
|
||||
return;
|
||||
|
||||
slot->data.p = p;
|
||||
}
|
||||
|
||||
void BMO_slot_vec_set(BMOperator *op, const char *slotname, const float vec[3])
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_VEC))
|
||||
return;
|
||||
|
||||
copy_v3_v3(slot->data.vec, vec);
|
||||
}
|
||||
|
||||
|
||||
float BMO_slot_float_get(BMOperator *op, const char *slotname)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_FLT))
|
||||
return 0.0f;
|
||||
|
||||
return slot->data.f;
|
||||
}
|
||||
|
||||
int BMO_slot_int_get(BMOperator *op, const char *slotname)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_INT))
|
||||
return 0;
|
||||
|
||||
return slot->data.i;
|
||||
}
|
||||
|
||||
|
||||
void *BMO_slot_ptr_get(BMOperator *op, const char *slotname)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_PNT))
|
||||
return NULL;
|
||||
|
||||
return slot->data.p;
|
||||
}
|
||||
|
||||
void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3])
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
if (!(slot->slottype == BMO_OP_SLOT_VEC))
|
||||
return;
|
||||
|
||||
copy_v3_v3(r_vec, slot->data.vec);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMO_COUNTFLAG
|
||||
*
|
||||
* Counts the number of elements of a certain type that
|
||||
* have a specific flag set.
|
||||
*
|
||||
*/
|
||||
|
||||
int BMO_mesh_flag_count(BMesh *bm, const short oflag, const char htype)
|
||||
{
|
||||
BMIter elements;
|
||||
int count = 0;
|
||||
BMElemF *ele_f;
|
||||
|
||||
if (htype & BM_VERT) {
|
||||
for (ele_f = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
|
||||
if (BMO_elem_flag_test(bm, ele_f, oflag))
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (htype & BM_EDGE) {
|
||||
for (ele_f = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
|
||||
if (BMO_elem_flag_test(bm, ele_f, oflag))
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (htype & BM_FACE) {
|
||||
for (ele_f = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
|
||||
if (BMO_elem_flag_test(bm, ele_f, oflag))
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *UNUSED(op), const char htype, const short oflag)
|
||||
{
|
||||
const char iter_types[3] = {BM_VERTS_OF_MESH,
|
||||
BM_EDGES_OF_MESH,
|
||||
BM_FACES_OF_MESH};
|
||||
|
||||
const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE};
|
||||
|
||||
BMIter iter;
|
||||
BMElemF *ele;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (htype & flag_types[i]) {
|
||||
BM_ITER(ele, &iter, bm, iter_types[i], NULL) {
|
||||
BMO_elem_flag_disable(bm, ele, oflag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int BMO_slot_buf_count(struct BMesh *UNUSED(bm), struct BMOperator *op, const char *slotname)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
/* check if its actually a buffer */
|
||||
if (!(slot->slottype > BMO_OP_SLOT_VEC))
|
||||
return 0;
|
||||
|
||||
return slot->len;
|
||||
}
|
||||
|
||||
int BMO_slot_map_count(BMesh *UNUSED(bm), BMOperator *op, const char *slotname)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
/* check if its actually a buffer */
|
||||
if (!(slot->slottype == BMO_OP_SLOT_MAPPING))
|
||||
return 0;
|
||||
|
||||
return slot->data.ghash ? BLI_ghash_size(slot->data.ghash) : 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void *BMO_Grow_Array(BMesh *bm, BMOperator *op, int slotcode, int totadd)
|
||||
{
|
||||
BMOpSlot *slot = &op->slots[slotcode];
|
||||
void *tmp;
|
||||
ssize_t allocsize;
|
||||
|
||||
/* check if its actually a buffer */
|
||||
if (!(slot->slottype > BMO_OP_SLOT_VEC))
|
||||
return NULL;
|
||||
|
||||
if (slot->flag & BMOS_DYNAMIC_ARRAY) {
|
||||
if (slot->len >= slot->size) {
|
||||
slot->size = (slot->size + 1 + totadd) * 2;
|
||||
|
||||
allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->size;
|
||||
|
||||
tmp = slot->data.buf;
|
||||
slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array");
|
||||
memcpy(slot->data.buf, tmp, allocsize);
|
||||
MEM_freeN(tmp);
|
||||
}
|
||||
|
||||
slot->len += totadd;
|
||||
}
|
||||
else {
|
||||
slot->flag |= BMOS_DYNAMIC_ARRAY;
|
||||
slot->len += totadd;
|
||||
slot->size = slot->len + 2;
|
||||
|
||||
allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->len;
|
||||
|
||||
tmp = slot->data.buf;
|
||||
slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array");
|
||||
memcpy(slot->data.buf, tmp, allocsize);
|
||||
}
|
||||
|
||||
return slot->data.buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op,
|
||||
const char *slotname, const short oflag)
|
||||
{
|
||||
GHashIterator it;
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
BMElemF *ele_f;
|
||||
|
||||
/* sanity check */
|
||||
if (slot->slottype != BMO_OP_SLOT_MAPPING) return;
|
||||
if (!slot->data.ghash) return;
|
||||
|
||||
BLI_ghashIterator_init(&it, slot->data.ghash);
|
||||
for ( ; (ele_f = BLI_ghashIterator_getKey(&it)); BLI_ghashIterator_step(&it)) {
|
||||
BMO_elem_flag_enable(bm, ele_f, oflag);
|
||||
}
|
||||
}
|
||||
|
||||
static void *bmo_slot_buffer_alloc(BMOperator *op, const char *slotname, int len)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
/* check if its actually a buffer */
|
||||
if (!(slot->slottype > BMO_OP_SLOT_VEC))
|
||||
return NULL;
|
||||
|
||||
slot->len = len;
|
||||
if (len)
|
||||
slot->data.buf = BLI_memarena_alloc(op->arena, BMO_OPSLOT_TYPEINFO[slot->slottype] * len);
|
||||
return slot->data.buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMO_ALL_TO_SLOT
|
||||
*
|
||||
* Copies all elements of a certain type into an operator slot.
|
||||
*
|
||||
*/
|
||||
|
||||
static void BMO_slot_from_all(BMesh *bm, BMOperator *op, const char *slotname, const char htype)
|
||||
{
|
||||
BMIter elements;
|
||||
BMHeader *e;
|
||||
BMOpSlot *output = BMO_slot_get(op, slotname);
|
||||
int totelement = 0, i = 0;
|
||||
|
||||
if (htype & BM_VERT) totelement += bm->totvert;
|
||||
if (htype & BM_EDGE) totelement += bm->totedge;
|
||||
if (htype & BM_FACE) totelement += bm->totface;
|
||||
|
||||
if (totelement) {
|
||||
bmo_slot_buffer_alloc(op, slotname, totelement);
|
||||
|
||||
if (htype & BM_VERT) {
|
||||
for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
|
||||
((BMHeader **)output->data.p)[i] = e;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (htype & BM_EDGE) {
|
||||
for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
|
||||
((BMHeader **)output->data.p)[i] = e;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (htype & BM_FACE) {
|
||||
for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
|
||||
((BMHeader **)output->data.p)[i] = e;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BMO_HEADERFLAG_TO_SLOT
|
||||
*
|
||||
* Copies elements of a certain type, which have a certain header flag set
|
||||
* into a slot for an operator.
|
||||
*/
|
||||
|
||||
void BMO_slot_from_hflag(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
const char hflag, const char htype)
|
||||
{
|
||||
BMIter elements;
|
||||
BMHeader *e;
|
||||
BMOpSlot *output = BMO_slot_get(op, slotname);
|
||||
int totelement = 0, i = 0;
|
||||
|
||||
totelement = BM_mesh_count_flag(bm, htype, hflag, 1);
|
||||
|
||||
if (totelement) {
|
||||
bmo_slot_buffer_alloc(op, slotname, totelement);
|
||||
|
||||
if (htype & BM_VERT) {
|
||||
for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
|
||||
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
|
||||
((BMHeader **)output->data.p)[i] = e;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (htype & BM_EDGE) {
|
||||
for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
|
||||
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
|
||||
((BMHeader **)output->data.p)[i] = e;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (htype & BM_FACE) {
|
||||
for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
|
||||
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
|
||||
((BMHeader **)output->data.p)[i] = e;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
output->len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BMO_FLAG_TO_SLOT
|
||||
*
|
||||
* Copies elements of a certain type, which have a certain flag set
|
||||
* into an output slot for an operator.
|
||||
*/
|
||||
void BMO_slot_from_flag(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
const short oflag, const char htype)
|
||||
{
|
||||
BMIter elements;
|
||||
BMHeader *ele;
|
||||
BMOpSlot *output = BMO_slot_get(op, slotname);
|
||||
int totelement = BMO_mesh_flag_count(bm, oflag, htype), i = 0;
|
||||
|
||||
if (totelement) {
|
||||
bmo_slot_buffer_alloc(op, slotname, totelement);
|
||||
|
||||
if (htype & BM_VERT) {
|
||||
for (ele = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
|
||||
if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
|
||||
((BMHeader **)output->data.p)[i] = ele;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (htype & BM_EDGE) {
|
||||
for (ele = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
|
||||
if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
|
||||
((BMHeader **)output->data.p)[i] = ele;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (htype & BM_FACE) {
|
||||
for (ele = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
|
||||
if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
|
||||
((BMHeader **)output->data.p)[i] = ele;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
output->len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* BMO_FLAG_BUFFER
|
||||
*
|
||||
* Header Flags elements in a slots buffer, automatically
|
||||
* using the selection API where appropriate.
|
||||
*/
|
||||
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
const char hflag, const char htype)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
BMHeader **data = slot->data.p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < slot->len; i++) {
|
||||
if (!(htype & data[i]->htype))
|
||||
continue;
|
||||
|
||||
if (hflag & BM_ELEM_SELECT) {
|
||||
BM_elem_select_set(bm, data[i], TRUE);
|
||||
}
|
||||
BM_elem_flag_enable(data[i], hflag);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* BMO_FLAG_BUFFER
|
||||
*
|
||||
* Removes flags from elements in a slots buffer, automatically
|
||||
* using the selection API where appropriate.
|
||||
*/
|
||||
void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
const char hflag, const char htype)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
BMHeader **data = slot->data.p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < slot->len; i++) {
|
||||
if (!(htype & data[i]->htype))
|
||||
continue;
|
||||
|
||||
if (hflag & BM_ELEM_SELECT) {
|
||||
BM_elem_select_set(bm, data[i], FALSE);
|
||||
}
|
||||
|
||||
BM_elem_flag_disable(data[i], hflag);
|
||||
}
|
||||
}
|
||||
int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (v->e) {
|
||||
BMEdge *curedge;
|
||||
const int len = bmesh_disk_count(v);
|
||||
int i;
|
||||
|
||||
for (i = 0, curedge = v->e; i < len; i++) {
|
||||
if (BMO_elem_flag_test(bm, curedge, oflag))
|
||||
count++;
|
||||
curedge = bmesh_disk_nextedge(curedge, v);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* BMO_FLAG_BUFFER
|
||||
*
|
||||
* Flags elements in a slots buffer
|
||||
*/
|
||||
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
const short oflag, const char htype)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
BMHeader **data = slot->data.p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < slot->len; i++) {
|
||||
if (!(htype & data[i]->htype))
|
||||
continue;
|
||||
|
||||
BMO_elem_flag_enable(bm, (BMElemF *)data[i], oflag);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* BMO_FLAG_BUFFER
|
||||
*
|
||||
* Removes flags from elements in a slots buffer
|
||||
*/
|
||||
void BMO_slot_buffer_flag_disable(BMesh *bm, BMOperator *op, const char *slotname,
|
||||
const short oflag, const char htype)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
BMHeader **data = slot->data.p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < slot->len; i++) {
|
||||
if (!(htype & data[i]->htype))
|
||||
continue;
|
||||
|
||||
BMO_elem_flag_disable(bm, (BMElemF *)data[i], oflag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* ALLOC/FREE FLAG LAYER
|
||||
*
|
||||
* Used by operator stack to free/allocate
|
||||
* private flag data. This is allocated
|
||||
* using a mempool so the allocation/frees
|
||||
* should be quite fast.
|
||||
*
|
||||
* BMESH_TODO:
|
||||
* Investigate not freeing flag layers until
|
||||
* all operators have been executed. This would
|
||||
* save a lot of realloc potentially.
|
||||
*/
|
||||
static void bmo_flag_layer_alloc(BMesh *bm)
|
||||
{
|
||||
BMElemF *ele;
|
||||
/* set the index values since we are looping over all data anyway,
|
||||
* may save time later on */
|
||||
int i;
|
||||
|
||||
BMIter iter;
|
||||
BLI_mempool *oldpool = bm->toolflagpool; /* old flag pool */
|
||||
BLI_mempool *newpool;
|
||||
void *oldflags;
|
||||
|
||||
/* store memcpy size for reuse */
|
||||
const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer));
|
||||
|
||||
bm->totflags++;
|
||||
|
||||
/* allocate new flag poo */
|
||||
bm->toolflagpool = newpool = BLI_mempool_create(sizeof(BMFlagLayer)*bm->totflags, 512, 512, FALSE, FALSE);
|
||||
|
||||
/* now go through and memcpy all the flags. Loops don't get a flag layer at this time.. */
|
||||
for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
oldflags = ele->oflags;
|
||||
ele->oflags = BLI_mempool_calloc(newpool);
|
||||
memcpy(ele->oflags, oldflags, old_totflags_size);
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
oldflags = ele->oflags;
|
||||
ele->oflags = BLI_mempool_calloc(newpool);
|
||||
memcpy(ele->oflags, oldflags, old_totflags_size);
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
oldflags = ele->oflags;
|
||||
ele->oflags = BLI_mempool_calloc(newpool);
|
||||
memcpy(ele->oflags, oldflags, old_totflags_size);
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
|
||||
bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
|
||||
|
||||
BLI_mempool_destroy(oldpool);
|
||||
}
|
||||
|
||||
static void bmo_flag_layer_free(BMesh *bm)
|
||||
{
|
||||
BMElemF *ele;
|
||||
/* set the index values since we are looping over all data anyway,
|
||||
* may save time later on */
|
||||
int i;
|
||||
|
||||
BMIter iter;
|
||||
BLI_mempool *oldpool = bm->toolflagpool;
|
||||
BLI_mempool *newpool;
|
||||
void *oldflags;
|
||||
|
||||
/* store memcpy size for reuse */
|
||||
const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer));
|
||||
|
||||
/* de-increment the totflags first.. */
|
||||
bm->totflags--;
|
||||
/* allocate new flag poo */
|
||||
bm->toolflagpool = newpool = BLI_mempool_create(new_totflags_size, 512, 512, TRUE, FALSE);
|
||||
|
||||
/* now go through and memcpy all the flag */
|
||||
for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
oldflags = ele->oflags;
|
||||
ele->oflags = BLI_mempool_calloc(newpool);
|
||||
memcpy(ele->oflags, oldflags, new_totflags_size);
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
oldflags = ele->oflags;
|
||||
ele->oflags = BLI_mempool_calloc(newpool);
|
||||
memcpy(ele->oflags, oldflags, new_totflags_size);
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
oldflags = ele->oflags;
|
||||
ele->oflags = BLI_mempool_calloc(newpool);
|
||||
memcpy(ele->oflags, oldflags, new_totflags_size);
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
|
||||
bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
|
||||
|
||||
BLI_mempool_destroy(oldpool);
|
||||
}
|
||||
|
||||
static void bmo_flag_layer_clear(BMesh *bm)
|
||||
{
|
||||
BMElemF *ele;
|
||||
/* set the index values since we are looping over all data anyway,
|
||||
* may save time later on */
|
||||
int i;
|
||||
|
||||
BMIter iter;
|
||||
const int totflags_offset = bm->totflags - 1;
|
||||
|
||||
/* now go through and memcpy all the flag */
|
||||
for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
|
||||
memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
|
||||
BM_elem_index_set(ele, i); /* set_inline */
|
||||
}
|
||||
|
||||
bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
|
||||
}
|
||||
|
||||
void *BMO_slot_elem_first(BMOperator *op, const char *slotname)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
if (slot->slottype != BMO_OP_SLOT_ELEMENT_BUF)
|
||||
return NULL;
|
||||
|
||||
return slot->data.buf ? *(void **)slot->data.buf : NULL;
|
||||
}
|
||||
|
||||
void *BMO_iter_new(BMOIter *iter, BMesh *UNUSED(bm), BMOperator *op,
|
||||
const char *slotname, const char restrictmask)
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
memset(iter, 0, sizeof(BMOIter));
|
||||
|
||||
iter->slot = slot;
|
||||
iter->cur = 0;
|
||||
iter->restrictmask = restrictmask;
|
||||
|
||||
if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) {
|
||||
if (iter->slot->data.ghash) {
|
||||
BLI_ghashIterator_init(&iter->giter, slot->data.ghash);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return BMO_iter_step(iter);
|
||||
}
|
||||
|
||||
void *BMO_iter_step(BMOIter *iter)
|
||||
{
|
||||
if (iter->slot->slottype == BMO_OP_SLOT_ELEMENT_BUF) {
|
||||
BMHeader *h;
|
||||
|
||||
if (iter->cur >= iter->slot->len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h = ((void **)iter->slot->data.buf)[iter->cur++];
|
||||
while (!(iter->restrictmask & h->htype)) {
|
||||
if (iter->cur >= iter->slot->len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h = ((void **)iter->slot->data.buf)[iter->cur++];
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
else if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) {
|
||||
struct BMOElemMapping *map;
|
||||
void *ret = BLI_ghashIterator_getKey(&iter->giter);
|
||||
map = BLI_ghashIterator_getValue(&iter->giter);
|
||||
|
||||
iter->val = map + 1;
|
||||
|
||||
BLI_ghashIterator_step(&iter->giter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* used for iterating over mapping */
|
||||
void *BMO_iter_map_value(BMOIter *iter)
|
||||
{
|
||||
return iter->val;
|
||||
}
|
||||
|
||||
void *BMO_iter_map_value_p(BMOIter *iter)
|
||||
{
|
||||
return *((void **)iter->val);
|
||||
}
|
||||
|
||||
float BMO_iter_map_value_f(BMOIter *iter)
|
||||
{
|
||||
return *((float *)iter->val);
|
||||
}
|
||||
|
||||
/* error syste */
|
||||
typedef struct BMOpError {
|
||||
struct BMOpError *next, *prev;
|
||||
int errorcode;
|
||||
BMOperator *op;
|
||||
const char *msg;
|
||||
} BMOpError;
|
||||
|
||||
void BMO_error_clear(BMesh *bm)
|
||||
{
|
||||
while (BMO_error_pop(bm, NULL, NULL));
|
||||
}
|
||||
|
||||
void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg)
|
||||
{
|
||||
BMOpError *err = MEM_callocN(sizeof(BMOpError), "bmop_error");
|
||||
|
||||
err->errorcode = errcode;
|
||||
if (!msg) msg = bmo_error_messages[errcode];
|
||||
err->msg = msg;
|
||||
err->op = owner;
|
||||
|
||||
BLI_addhead(&bm->errorstack, err);
|
||||
}
|
||||
|
||||
int BMO_error_occurred(BMesh *bm)
|
||||
{
|
||||
return bm->errorstack.first != NULL;
|
||||
}
|
||||
|
||||
/* returns error code or 0 if no erro */
|
||||
int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op)
|
||||
{
|
||||
BMOpError *err = bm->errorstack.first;
|
||||
if (!err) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg) *msg = err->msg;
|
||||
if (op) *op = err->op;
|
||||
|
||||
return err->errorcode;
|
||||
}
|
||||
|
||||
int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op)
|
||||
{
|
||||
int errorcode = BMO_error_get(bm, msg, op);
|
||||
|
||||
if (errorcode) {
|
||||
BMOpError *err = bm->errorstack.first;
|
||||
|
||||
BLI_remlink(&bm->errorstack, bm->errorstack.first);
|
||||
MEM_freeN(err);
|
||||
}
|
||||
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
/* example:
|
||||
* BMO_CallOp(bm, "del %d %hv", DEL_ONLYFACES, BM_ELEM_SELECT);
|
||||
*
|
||||
* d - int
|
||||
* i - int
|
||||
* f - float
|
||||
* hv - header flagged verts
|
||||
* he - header flagged edges
|
||||
* hf - header flagged faces
|
||||
* fv - flagged verts
|
||||
* fe - flagged edges
|
||||
* ff - flagged faces
|
||||
*/
|
||||
|
||||
#define NEXT_CHAR(fmt) ((fmt)[0] != 0 ? (fmt)[1] : 0)
|
||||
|
||||
static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; def->slottypes[i].type; i++) {
|
||||
if (!strncmp(name, def->slottypes[i].name, MAX_SLOTNAME)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name)
|
||||
{
|
||||
int i = bmesh_name_to_slotcode(def, name);
|
||||
if (i < 0) {
|
||||
fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, name);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int bmesh_opname_to_opcode(const char *opname)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bmesh_total_ops; i++) {
|
||||
if (!strcmp(opname, opdefines[i]->name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, opname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *_fmt, va_list vlist)
|
||||
{
|
||||
BMOpDefine *def;
|
||||
char *opname, *ofmt, *fmt;
|
||||
char slotname[64] = {0};
|
||||
int i /*, n = strlen(fmt) */, stop /*, slotcode = -1 */, ret, type, state;
|
||||
int noslot = 0;
|
||||
|
||||
|
||||
/* basic useful info to help find where bmop formatting strings fail */
|
||||
int lineno = -1;
|
||||
# define GOTO_ERROR { lineno = __LINE__; goto error; }
|
||||
|
||||
|
||||
/* we muck around in here, so dup i */
|
||||
fmt = ofmt = BLI_strdup(_fmt);
|
||||
|
||||
/* find operator nam */
|
||||
i = strcspn(fmt, " \t");
|
||||
|
||||
opname = fmt;
|
||||
if (!opname[i]) noslot = 1;
|
||||
opname[i] = '\0';
|
||||
|
||||
fmt += i + (noslot ? 0 : 1);
|
||||
|
||||
i = bmesh_opname_to_opcode(opname);
|
||||
|
||||
if (i == -1) {
|
||||
MEM_freeN(ofmt);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BMO_op_init(bm, op, opname);
|
||||
def = opdefines[i];
|
||||
|
||||
i = 0;
|
||||
state = 1; /* 0: not inside slotcode name, 1: inside slotcode name */
|
||||
|
||||
while (*fmt) {
|
||||
if (state) {
|
||||
/* jump past leading whitespac */
|
||||
i = strspn(fmt, " \t");
|
||||
fmt += i;
|
||||
|
||||
/* ignore trailing whitespac */
|
||||
if (!fmt[i])
|
||||
break;
|
||||
|
||||
/* find end of slot name. currently this is
|
||||
* a little flexible, allowing "slot=%f",
|
||||
* "slot %f", "slot%f", and "slot\t%f". */
|
||||
i = strcspn(fmt, "= \t%");
|
||||
if (!fmt[i]) GOTO_ERROR;
|
||||
|
||||
fmt[i] = 0;
|
||||
|
||||
if (bmesh_name_to_slotcode_check(def, fmt) < 0) GOTO_ERROR;
|
||||
|
||||
BLI_strncpy(slotname, fmt, sizeof(slotname));
|
||||
|
||||
state = 0;
|
||||
fmt += i;
|
||||
}
|
||||
else {
|
||||
switch (*fmt) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '=':
|
||||
case '%':
|
||||
break;
|
||||
case 'm': {
|
||||
int size, c;
|
||||
|
||||
c = NEXT_CHAR(fmt);
|
||||
fmt++;
|
||||
|
||||
if (c == '3') size = 3;
|
||||
else if (c == '4') size = 4;
|
||||
else GOTO_ERROR;
|
||||
|
||||
BMO_slot_mat_set(op, slotname, va_arg(vlist, void *), size);
|
||||
state = 1;
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
BMO_slot_vec_set(op, slotname, va_arg(vlist, float *));
|
||||
state = 1;
|
||||
break;
|
||||
}
|
||||
case 'e': {
|
||||
BMHeader *ele = va_arg(vlist, void *);
|
||||
BMOpSlot *slot = BMO_slot_get(op, slotname);
|
||||
|
||||
slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(void *) * 4);
|
||||
slot->len = 1;
|
||||
*((void **)slot->data.buf) = ele;
|
||||
|
||||
state = 1;
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
BMOperator *op2 = va_arg(vlist, void *);
|
||||
const char *slotname2 = va_arg(vlist, char *);
|
||||
|
||||
BMO_slot_copy(op2, op, slotname2, slotname);
|
||||
state = 1;
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
case 'd':
|
||||
BMO_slot_int_set(op, slotname, va_arg(vlist, int));
|
||||
state = 1;
|
||||
break;
|
||||
case 'p':
|
||||
BMO_slot_ptr_set(op, slotname, va_arg(vlist, void *));
|
||||
state = 1;
|
||||
break;
|
||||
case 'f':
|
||||
case 'h':
|
||||
case 'a':
|
||||
type = *fmt;
|
||||
|
||||
if (NEXT_CHAR(fmt) == ' ' || NEXT_CHAR(fmt) == '\t' || NEXT_CHAR(fmt) == '\0') {
|
||||
BMO_slot_float_set(op, slotname, va_arg(vlist, double));
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
stop = 0;
|
||||
while (1) {
|
||||
switch (NEXT_CHAR(fmt)) {
|
||||
case 'f': ret |= BM_FACE; break;
|
||||
case 'e': ret |= BM_EDGE; break;
|
||||
case 'v': ret |= BM_VERT; break;
|
||||
default:
|
||||
stop = 1;
|
||||
break;
|
||||
}
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
fmt++;
|
||||
}
|
||||
|
||||
if (type == 'h') {
|
||||
BMO_slot_from_hflag(bm, op, slotname, va_arg(vlist, int), ret);
|
||||
}
|
||||
else if (type == 'a') {
|
||||
BMO_slot_from_all(bm, op, slotname, ret);
|
||||
}
|
||||
else {
|
||||
BMO_slot_from_flag(bm, op, slotname, va_arg(vlist, int), ret);
|
||||
}
|
||||
}
|
||||
|
||||
state = 1;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"%s: unrecognized bmop format char: %c, %d in '%s'\n",
|
||||
__func__, *fmt, (int)(fmt - ofmt), ofmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fmt++;
|
||||
}
|
||||
|
||||
MEM_freeN(ofmt);
|
||||
return TRUE;
|
||||
error:
|
||||
|
||||
/* non urgent todo - explain exactly what is failing */
|
||||
fprintf(stderr,
|
||||
"%s: error parsing formatting string, %d in '%s'\n see - %s:%d\n",
|
||||
__func__, (int)(fmt - ofmt), _fmt, __FILE__, lineno);
|
||||
MEM_freeN(ofmt);
|
||||
|
||||
BMO_op_finish(bm, op);
|
||||
return FALSE;
|
||||
|
||||
#undef GOTO_ERROR
|
||||
|
||||
}
|
||||
|
||||
|
||||
int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...)
|
||||
{
|
||||
va_list list;
|
||||
|
||||
va_start(list, fmt);
|
||||
if (!BMO_op_vinitf(bm, op, fmt, list)) {
|
||||
printf("%s: failed\n", __func__);
|
||||
va_end(list);
|
||||
return FALSE;
|
||||
}
|
||||
va_end(list);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int BMO_op_callf(BMesh *bm, const char *fmt, ...)
|
||||
{
|
||||
va_list list;
|
||||
BMOperator op;
|
||||
|
||||
va_start(list, fmt);
|
||||
if (!BMO_op_vinitf(bm, &op, fmt, list)) {
|
||||
printf("%s: failed, format is:\n \"%s\"\n", __func__, fmt);
|
||||
va_end(list);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BMO_op_exec(bm, &op);
|
||||
BMO_op_finish(bm, &op);
|
||||
|
||||
va_end(list);
|
||||
return TRUE;
|
||||
}
|
||||
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
@@ -0,0 +1,1069 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_polygon.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* This file contains code for dealing
|
||||
* with polygons (normal/area calculation,
|
||||
* tesselation, etc)
|
||||
*
|
||||
* BMESH_TODO:
|
||||
* - Add in Tesselator frontend that creates
|
||||
* BMTriangles from copied faces
|
||||
*
|
||||
* - Add in Function that checks for and flags
|
||||
* degenerate faces.
|
||||
*/
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_array.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/*
|
||||
* TEST EDGE SIDE and POINT IN TRIANGLE
|
||||
*
|
||||
* Point in triangle tests stolen from scanfill.c.
|
||||
* Used for tesselator
|
||||
*
|
||||
*/
|
||||
|
||||
static short testedgeside(const double v1[2], const double v2[2], const double v3[2])
|
||||
{
|
||||
/* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */
|
||||
double inp;
|
||||
|
||||
//inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]);
|
||||
inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]);
|
||||
|
||||
if (inp < 0.0) {
|
||||
return FALSE;
|
||||
}
|
||||
else if (inp == 0) {
|
||||
if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE;
|
||||
if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static short testedgesidef(const float v1[2], const float v2[2], const float v3[2])
|
||||
{
|
||||
/* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */
|
||||
double inp;
|
||||
|
||||
//inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]);
|
||||
inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]);
|
||||
|
||||
if (inp < 0.0) {
|
||||
return FALSE;
|
||||
}
|
||||
else if (inp == 0) {
|
||||
if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE;
|
||||
if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int point_in_triangle(const double v1[2], const double v2[2], const double v3[2], const double pt[2])
|
||||
{
|
||||
if (testedgeside(v1, v2, pt) && testedgeside(v2, v3, pt) && testedgeside(v3, v1, pt)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* COMPUTE POLY NORMAL
|
||||
*
|
||||
* Computes the normal of a planar
|
||||
* polygon See Graphics Gems for
|
||||
* computing newell normal.
|
||||
*
|
||||
*/
|
||||
|
||||
static void compute_poly_normal(float normal[3], float (*verts)[3], int nverts)
|
||||
{
|
||||
|
||||
float u[3], v[3], w[3]; /*, *w, v1[3], v2[3]; */
|
||||
float n[3] = {0.0f, 0.0f, 0.0f} /*, l, v1[3], v2[3] */;
|
||||
/* double l2; */
|
||||
int i /*, s = 0 */;
|
||||
|
||||
/* this fixes some weird numerical erro */
|
||||
add_v3_fl(verts[0], 0.0001f);
|
||||
|
||||
for (i = 0; i < nverts; i++) {
|
||||
copy_v3_v3(u, verts[i]);
|
||||
copy_v3_v3(v, verts[(i + 1) % nverts]);
|
||||
copy_v3_v3(w, verts[(i + 2) % nverts]);
|
||||
|
||||
#if 0
|
||||
sub_v3_v3v3(v1, w, v);
|
||||
sub_v3_v3v3(v2, v, u);
|
||||
normalize_v3(v1);
|
||||
normalize_v3(v2);
|
||||
|
||||
l = dot_v3v3(v1, v2);
|
||||
if (fabsf(l - 1.0) < 0.1f) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* newell's method
|
||||
|
||||
so thats?:
|
||||
(a[1] - b[1]) * (a[2] + b[2]);
|
||||
a[1]*b[2] - b[1]*a[2] - b[1]*b[2] + a[1]*a[2]
|
||||
|
||||
odd. half of that is the cross product. . .what's the
|
||||
other half?
|
||||
|
||||
also could be like a[1]*(b[2] + a[2]) - b[1]*(a[2] - b[2])
|
||||
*/
|
||||
|
||||
n[0] += (u[1] - v[1]) * (u[2] + v[2]);
|
||||
n[1] += (u[2] - v[2]) * (u[0] + v[0]);
|
||||
n[2] += (u[0] - v[0]) * (u[1] + v[1]);
|
||||
}
|
||||
|
||||
if (normalize_v3_v3(normal, n) == 0.0f) {
|
||||
normal[2] = 1.0f; /* other axis set to 0.0 */
|
||||
}
|
||||
|
||||
#if 0
|
||||
l = len_v3(n);
|
||||
/* fast square root, newton/babylonian method:
|
||||
l2 = l * 0.1;
|
||||
|
||||
l2 = (l / l2 + l2) * 0.5;
|
||||
l2 = (l / l2 + l2) * 0.5;
|
||||
l2 = (l / l2 + l2) * 0.5;
|
||||
*/
|
||||
|
||||
if (l == 0.0) {
|
||||
normal[0] = 0.0f;
|
||||
normal[1] = 0.0f;
|
||||
normal[2] = 1.0f;
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
l = 1.0f / l;
|
||||
}
|
||||
|
||||
mul_v3_fl(n, l);
|
||||
|
||||
copy_v3_v3(normal, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* COMPUTE POLY CENTER
|
||||
*
|
||||
* Computes the centroid and
|
||||
* area of a polygon in the X/Y
|
||||
* plane.
|
||||
*
|
||||
*/
|
||||
|
||||
static int compute_poly_center(float center[3], float *r_area, float (*verts)[3], int nverts)
|
||||
{
|
||||
int i, j;
|
||||
float atmp = 0.0f, xtmp = 0.0f, ytmp = 0.0f, ai;
|
||||
|
||||
zero_v3(center);
|
||||
|
||||
if (nverts < 3)
|
||||
return FALSE;
|
||||
|
||||
i = nverts - 1;
|
||||
j = 0;
|
||||
|
||||
while (j < nverts) {
|
||||
ai = verts[i][0] * verts[j][1] - verts[j][0] * verts[i][1];
|
||||
atmp += ai;
|
||||
xtmp += (verts[j][0] + verts[i][0]) * ai;
|
||||
ytmp += (verts[j][1] + verts[i][1]) * ai;
|
||||
i = j;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if (r_area)
|
||||
*r_area = atmp / 2.0f;
|
||||
|
||||
if (atmp != 0) {
|
||||
center[0] = xtmp / (3.0f * atmp);
|
||||
center[1] = xtmp / (3.0f * atmp);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
float BM_face_area_calc(BMesh *bm, BMFace *f)
|
||||
{
|
||||
BMLoop *l;
|
||||
BMIter iter;
|
||||
float (*verts)[3];
|
||||
float center[3];
|
||||
float area = 0.0f;
|
||||
int i;
|
||||
|
||||
BLI_array_fixedstack_declare(verts, BM_NGON_STACK_SIZE, f->len, __func__);
|
||||
|
||||
i = 0;
|
||||
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
copy_v3_v3(verts[i], l->v->co);
|
||||
i++;
|
||||
}
|
||||
|
||||
compute_poly_center(center, &area, verts, f->len);
|
||||
|
||||
BLI_array_fixedstack_free(verts);
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
/*
|
||||
* computes center of face in 3d. uses center of bounding box.
|
||||
*/
|
||||
void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float r_cent[3])
|
||||
{
|
||||
BMIter iter;
|
||||
BMLoop *l;
|
||||
float min[3], max[3];
|
||||
int i;
|
||||
|
||||
INIT_MINMAX(min, max);
|
||||
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
|
||||
for (i = 0; l; l = BM_iter_step(&iter), i++) {
|
||||
DO_MINMAX(l->v->co, min, max);
|
||||
}
|
||||
|
||||
mid_v3_v3v3(r_cent, min, max);
|
||||
}
|
||||
|
||||
void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float r_cent[3])
|
||||
{
|
||||
BMIter iter;
|
||||
BMLoop *l;
|
||||
int i;
|
||||
|
||||
zero_v3(r_cent);
|
||||
|
||||
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
|
||||
for (i = 0; l; l = BM_iter_step(&iter), i++) {
|
||||
add_v3_v3(r_cent, l->v->co);
|
||||
}
|
||||
|
||||
if (f->len) mul_v3_fl(r_cent, 1.0f / (float)f->len);
|
||||
}
|
||||
|
||||
/*
|
||||
* COMPUTE POLY PLANE
|
||||
*
|
||||
* Projects a set polygon's vertices to
|
||||
* a plane defined by the average
|
||||
* of its edges cross products
|
||||
*
|
||||
*/
|
||||
|
||||
void compute_poly_plane(float (*verts)[3], int nverts)
|
||||
{
|
||||
|
||||
float avgc[3], norm[3], mag, avgn[3];
|
||||
float *v1, *v2, *v3;
|
||||
int i;
|
||||
|
||||
if (nverts < 3)
|
||||
return;
|
||||
|
||||
zero_v3(avgn);
|
||||
zero_v3(avgc);
|
||||
|
||||
for (i = 0; i < nverts; i++) {
|
||||
v1 = verts[i];
|
||||
v2 = verts[(i + 1) % nverts];
|
||||
v3 = verts[(i + 2) % nverts];
|
||||
normal_tri_v3(norm, v1, v2, v3);
|
||||
|
||||
add_v3_v3(avgn, norm);
|
||||
}
|
||||
|
||||
/* what was this bit for */
|
||||
if (is_zero_v3(avgn)) {
|
||||
avgn[0] = 0.0f;
|
||||
avgn[1] = 0.0f;
|
||||
avgn[2] = 1.0f;
|
||||
}
|
||||
else {
|
||||
/* XXX, why is this being divided and _then_ normalized
|
||||
* division could be removed? - campbell */
|
||||
avgn[0] /= nverts;
|
||||
avgn[1] /= nverts;
|
||||
avgn[2] /= nverts;
|
||||
normalize_v3(avgn);
|
||||
}
|
||||
|
||||
for (i = 0; i < nverts; i++) {
|
||||
v1 = verts[i];
|
||||
mag = dot_v3v3(v1, avgn);
|
||||
madd_v3_v3fl(v1, avgn, -mag);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BM LEGAL EDGES
|
||||
*
|
||||
* takes in a face and a list of edges, and sets to NULL any edge in
|
||||
* the list that bridges a concave region of the face or intersects
|
||||
* any of the faces's edges.
|
||||
*/
|
||||
#if 0 /* needs BLI math double versions of these functions */
|
||||
static void shrink_edged(double *v1, double *v2, double fac)
|
||||
{
|
||||
double mid[3];
|
||||
|
||||
mid_v3_v3v3(mid, v1, v2);
|
||||
|
||||
sub_v3_v3v3(v1, v1, mid);
|
||||
sub_v3_v3v3(v2, v2, mid);
|
||||
|
||||
mul_v3_fl(v1, fac);
|
||||
mul_v3_fl(v2, fac);
|
||||
|
||||
add_v3_v3v3(v1, v1, mid);
|
||||
add_v3_v3v3(v2, v2, mid);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void shrink_edgef(float v1[3], float v2[3], const float fac)
|
||||
{
|
||||
float mid[3];
|
||||
|
||||
mid_v3_v3v3(mid, v1, v2);
|
||||
|
||||
sub_v3_v3v3(v1, v1, mid);
|
||||
sub_v3_v3v3(v2, v2, mid);
|
||||
|
||||
mul_v3_fl(v1, fac);
|
||||
mul_v3_fl(v2, fac);
|
||||
|
||||
add_v3_v3v3(v1, v1, mid);
|
||||
add_v3_v3v3(v2, v2, mid);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* POLY ROTATE PLANE
|
||||
*
|
||||
* Rotates a polygon so that it's
|
||||
* normal is pointing towards the mesh Z axis
|
||||
*
|
||||
*/
|
||||
|
||||
void poly_rotate_plane(const float normal[3], float (*verts)[3], const int nverts)
|
||||
{
|
||||
|
||||
float up[3] = {0.0f, 0.0f, 1.0f}, axis[3], q[4];
|
||||
float mat[3][3];
|
||||
double angle;
|
||||
int i;
|
||||
|
||||
cross_v3_v3v3(axis, normal, up);
|
||||
|
||||
angle = saacos(dot_v3v3(normal, up));
|
||||
|
||||
if (angle == 0.0) return;
|
||||
|
||||
axis_angle_to_quat(q, axis, (float)angle);
|
||||
quat_to_mat3(mat, q);
|
||||
|
||||
for (i = 0; i < nverts; i++)
|
||||
mul_m3_v3(mat, verts[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH UPDATE FACE NORMAL
|
||||
*
|
||||
* Updates the stored normal for the
|
||||
* given face. Requires that a buffer
|
||||
* of sufficient length to store projected
|
||||
* coordinates for all of the face's vertices
|
||||
* is passed in as well.
|
||||
*
|
||||
*/
|
||||
|
||||
void BM_face_normal_update(BMesh *bm, BMFace *f)
|
||||
{
|
||||
if (f->len >= 3) {
|
||||
float (*proj)[3];
|
||||
|
||||
BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__);
|
||||
|
||||
bmesh_update_face_normal(bm, f, f->no, proj);
|
||||
|
||||
BLI_array_fixedstack_free(proj);
|
||||
}
|
||||
}
|
||||
/* same as BM_face_normal_update but takes vertex coords */
|
||||
void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3])
|
||||
{
|
||||
if (f->len >= 3) {
|
||||
float (*proj)[3];
|
||||
|
||||
BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__);
|
||||
|
||||
bmesh_update_face_normal_vertex_cos(bm, f, no, proj, vertexCos);
|
||||
|
||||
BLI_array_fixedstack_free(proj);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_edge_normals_update(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
|
||||
f = BM_iter_new(&iter, bm, BM_FACES_OF_EDGE, e);
|
||||
for ( ; f; f = BM_iter_step(&iter)) {
|
||||
BM_face_normal_update(bm, f);
|
||||
}
|
||||
|
||||
BM_vert_normal_update(bm, e->v1);
|
||||
BM_vert_normal_update(bm, e->v2);
|
||||
}
|
||||
|
||||
void BM_vert_normal_update(BMesh *bm, BMVert *v)
|
||||
{
|
||||
/* TODO, we can normalize each edge only once, then compare with previous edge */
|
||||
|
||||
BMIter eiter, liter;
|
||||
BMEdge *e;
|
||||
BMLoop *l;
|
||||
float vec1[3], vec2[3], fac;
|
||||
int len = 0;
|
||||
|
||||
zero_v3(v->no);
|
||||
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
|
||||
if (l->v == v) {
|
||||
/* Same calculation used in BM_mesh_normals_update */
|
||||
sub_v3_v3v3(vec1, l->v->co, l->prev->v->co);
|
||||
sub_v3_v3v3(vec2, l->next->v->co, l->v->co);
|
||||
normalize_v3(vec1);
|
||||
normalize_v3(vec2);
|
||||
|
||||
fac = saacos(-dot_v3v3(vec1, vec2));
|
||||
|
||||
madd_v3_v3fl(v->no, l->f->no, fac);
|
||||
|
||||
len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len) {
|
||||
normalize_v3(v->no);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_vert_normal_update_all(BMesh *bm, BMVert *v)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int len = 0;
|
||||
|
||||
f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v);
|
||||
for ( ; f; f = BM_iter_step(&iter), len++) {
|
||||
BM_face_normal_update(bm, f);
|
||||
}
|
||||
|
||||
BM_vert_normal_update(bm, v);
|
||||
}
|
||||
|
||||
void bmesh_update_face_normal(BMesh *bm, BMFace *f, float no[3],
|
||||
float (*projectverts)[3])
|
||||
{
|
||||
BMLoop *l;
|
||||
|
||||
/* common cases first */
|
||||
switch (f->len) {
|
||||
case 4:
|
||||
{
|
||||
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
|
||||
BMVert *v2 = (l = l->next)->v;
|
||||
BMVert *v3 = (l = l->next)->v;
|
||||
BMVert *v4 = (l->next)->v;
|
||||
normal_quad_v3(no, v1->co, v2->co, v3->co, v4->co);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
|
||||
BMVert *v2 = (l = l->next)->v;
|
||||
BMVert *v3 = (l->next)->v;
|
||||
normal_tri_v3(no, v1->co, v2->co, v3->co);
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
zero_v3(no);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
BMIter iter;
|
||||
int i = 0;
|
||||
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
copy_v3_v3(projectverts[i], l->v->co);
|
||||
i += 1;
|
||||
}
|
||||
compute_poly_normal(no, projectverts, f->len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* exact same as 'bmesh_update_face_normal' but accepts vertex coords */
|
||||
void bmesh_update_face_normal_vertex_cos(BMesh *bm, BMFace *f, float no[3],
|
||||
float (*projectverts)[3], float (*vertexCos)[3])
|
||||
{
|
||||
BMLoop *l;
|
||||
|
||||
/* must have valid index data */
|
||||
BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
|
||||
|
||||
/* common cases first */
|
||||
switch (f->len) {
|
||||
case 4:
|
||||
{
|
||||
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
|
||||
BMVert *v2 = (l = l->next)->v;
|
||||
BMVert *v3 = (l = l->next)->v;
|
||||
BMVert *v4 = (l->next)->v;
|
||||
normal_quad_v3(no,
|
||||
vertexCos[BM_elem_index_get(v1)],
|
||||
vertexCos[BM_elem_index_get(v2)],
|
||||
vertexCos[BM_elem_index_get(v3)],
|
||||
vertexCos[BM_elem_index_get(v4)]);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v;
|
||||
BMVert *v2 = (l = l->next)->v;
|
||||
BMVert *v3 = (l->next)->v;
|
||||
normal_tri_v3(no,
|
||||
vertexCos[BM_elem_index_get(v1)],
|
||||
vertexCos[BM_elem_index_get(v2)],
|
||||
vertexCos[BM_elem_index_get(v3)]);
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
zero_v3(no);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
BMIter iter;
|
||||
int i = 0;
|
||||
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
copy_v3_v3(projectverts[i], vertexCos[BM_elem_index_get(l->v)]);
|
||||
i += 1;
|
||||
}
|
||||
compute_poly_normal(no, projectverts, f->len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH FLIP NORMAL
|
||||
*
|
||||
* Reverses the winding of a face.
|
||||
* Note that this updates the calculated
|
||||
* normal.
|
||||
*/
|
||||
void BM_face_normal_flip(BMesh *bm, BMFace *f)
|
||||
{
|
||||
bmesh_loop_reverse(bm, f);
|
||||
negate_v3(f->no);
|
||||
}
|
||||
|
||||
/* detects if two line segments cross each other (intersects).
|
||||
* note, there could be more winding cases then there needs to be. */
|
||||
static int UNUSED_FUNCTION(linecrosses)(const double v1[2], const double v2[2], const double v3[2], const double v4[2])
|
||||
{
|
||||
int w1, w2, w3, w4, w5;
|
||||
|
||||
/* w1 = winding(v1, v3, v4);
|
||||
w2 = winding(v2, v3, v4);
|
||||
w3 = winding(v3, v1, v2);
|
||||
w4 = winding(v4, v1, v2);
|
||||
|
||||
return (w1 == w2) && (w3 == w4); */
|
||||
|
||||
w1 = testedgeside(v1, v3, v2);
|
||||
w2 = testedgeside(v2, v4, v1);
|
||||
w3 = !testedgeside(v1, v2, v3);
|
||||
w4 = testedgeside(v3, v2, v4);
|
||||
w5 = !testedgeside(v3, v1, v4);
|
||||
return w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5;
|
||||
}
|
||||
|
||||
/* detects if two line segments cross each other (intersects).
|
||||
* note, there could be more winding cases then there needs to be. */
|
||||
static int linecrossesf(const float v1[2], const float v2[2], const float v3[2], const float v4[2])
|
||||
{
|
||||
int w1, w2, w3, w4, w5 /*, re */;
|
||||
float mv1[2], mv2[2], mv3[2], mv4[2];
|
||||
|
||||
/* now test windin */
|
||||
w1 = testedgesidef(v1, v3, v2);
|
||||
w2 = testedgesidef(v2, v4, v1);
|
||||
w3 = !testedgesidef(v1, v2, v3);
|
||||
w4 = testedgesidef(v3, v2, v4);
|
||||
w5 = !testedgesidef(v3, v1, v4);
|
||||
|
||||
if (w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define GETMIN2_AXIS(a, b, ma, mb, axis) ma[axis] = MIN2(a[axis], b[axis]), mb[axis] = MAX2(a[axis], b[axis])
|
||||
#define GETMIN2(a, b, ma, mb) GETMIN2_AXIS(a, b, ma, mb, 0); GETMIN2_AXIS(a, b, ma, mb, 1);
|
||||
|
||||
GETMIN2(v1, v2, mv1, mv2);
|
||||
GETMIN2(v3, v4, mv3, mv4);
|
||||
|
||||
/* do an interval test on the x and y axe */
|
||||
/* first do x axi */
|
||||
#define T FLT_EPSILON * 15
|
||||
if ( ABS(v1[1] - v2[1]) < T &&
|
||||
ABS(v3[1] - v4[1]) < T &&
|
||||
ABS(v1[1] - v3[1]) < T)
|
||||
{
|
||||
return (mv4[0] >= mv1[0] && mv3[0] <= mv2[0]);
|
||||
}
|
||||
|
||||
/* now do y axi */
|
||||
if ( ABS(v1[0] - v2[0]) < T &&
|
||||
ABS(v3[0] - v4[0]) < T &&
|
||||
ABS(v1[0] - v3[0]) < T)
|
||||
{
|
||||
return (mv4[1] >= mv1[1] && mv3[1] <= mv2[1]);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* BM POINT IN FACE
|
||||
*
|
||||
* Projects co onto face f, and returns true if it is inside
|
||||
* the face bounds. Note that this uses a best-axis projection
|
||||
* test, instead of projecting co directly into f's orientation
|
||||
* space, so there might be accuracy issues.
|
||||
*/
|
||||
int BM_face_point_inside_test(BMesh *bm, BMFace *f, const float co[3])
|
||||
{
|
||||
int ax, ay;
|
||||
float co2[3], cent[3] = {0.0f, 0.0f, 0.0f}, out[3] = {FLT_MAX * 0.5f, FLT_MAX * 0.5f, 0};
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
int crosses = 0;
|
||||
float eps = 1.0f + (float)FLT_EPSILON * 150.0f;
|
||||
|
||||
if (dot_v3v3(f->no, f->no) <= FLT_EPSILON * 10)
|
||||
BM_face_normal_update(bm, f);
|
||||
|
||||
/* find best projection of face XY, XZ or YZ: barycentric weights of
|
||||
* the 2d projected coords are the same and faster to compute
|
||||
*
|
||||
* this probably isn't all that accurate, but it has the advantage of
|
||||
* being fast (especially compared to projecting into the face orientation)
|
||||
*/
|
||||
axis_dominant_v3(&ax, &ay, f->no);
|
||||
|
||||
co2[0] = co[ax];
|
||||
co2[1] = co[ay];
|
||||
co2[2] = 0;
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
cent[0] += l_iter->v->co[ax];
|
||||
cent[1] += l_iter->v->co[ay];
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
mul_v2_fl(cent, 1.0f / (float)f->len);
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
float v1[3], v2[3];
|
||||
|
||||
v1[0] = (l_iter->prev->v->co[ax] - cent[ax]) * eps + cent[ax];
|
||||
v1[1] = (l_iter->prev->v->co[ay] - cent[ay]) * eps + cent[ay];
|
||||
v1[2] = 0.0f;
|
||||
|
||||
v2[0] = (l_iter->v->co[ax] - cent[ax]) * eps + cent[ax];
|
||||
v2[1] = (l_iter->v->co[ay] - cent[ay]) * eps + cent[ay];
|
||||
v2[2] = 0.0f;
|
||||
|
||||
crosses += linecrossesf(v1, v2, co2, out) != 0;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
return crosses % 2 != 0;
|
||||
}
|
||||
|
||||
static int goodline(float (*projectverts)[3], BMFace *f, int v1i,
|
||||
int v2i, int v3i, int UNUSED(nvert))
|
||||
{
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
double v1[3], v2[3], v3[3], pv1[3], pv2[3];
|
||||
int i;
|
||||
|
||||
VECCOPY(v1, projectverts[v1i]);
|
||||
VECCOPY(v2, projectverts[v2i]);
|
||||
VECCOPY(v3, projectverts[v3i]);
|
||||
|
||||
if (testedgeside(v1, v2, v3)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//for (i = 0; i < nvert; i++) {
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
i = BM_elem_index_get(l_iter->v);
|
||||
if (i == v1i || i == v2i || i == v3i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VECCOPY(pv1, projectverts[BM_elem_index_get(l_iter->v)]);
|
||||
VECCOPY(pv2, projectverts[BM_elem_index_get(l_iter->next->v)]);
|
||||
|
||||
//if (linecrosses(pv1, pv2, v1, v3)) return FALSE;
|
||||
|
||||
if ( point_in_triangle(v1, v2, v3, pv1) ||
|
||||
point_in_triangle(v3, v2, v1, pv1))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
return TRUE;
|
||||
}
|
||||
/*
|
||||
* FIND EAR
|
||||
*
|
||||
* Used by tesselator to find
|
||||
* the next triangle to 'clip off'
|
||||
* of a polygon while tesselating.
|
||||
*
|
||||
*/
|
||||
|
||||
static BMLoop *find_ear(BMesh *UNUSED(bm), BMFace *f, float (*verts)[3], const int nvert)
|
||||
{
|
||||
BMVert *v1, *v2, *v3;
|
||||
BMLoop *bestear = NULL;
|
||||
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
/* float angle, bestangle = 180.0f; */
|
||||
int isear /*, i = 0 */;
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
isear = 1;
|
||||
|
||||
v1 = l_iter->prev->v;
|
||||
v2 = l_iter->v;
|
||||
v3 = l_iter->next->v;
|
||||
|
||||
if (BM_edge_exists(v1, v3)) {
|
||||
isear = 0;
|
||||
}
|
||||
else if (!goodline(verts, f, BM_elem_index_get(v1), BM_elem_index_get(v2), BM_elem_index_get(v3), nvert)) {
|
||||
isear = 0;
|
||||
}
|
||||
|
||||
if (isear) {
|
||||
#if 0
|
||||
angle = angle_v3v3v3(verts[v1->head.eflag2], verts[v2->head.eflag2], verts[v3->head.eflag2]);
|
||||
if (!bestear || ABS(angle-45.0f) < bestangle) {
|
||||
bestear = l;
|
||||
bestangle = ABS(45.0f - angle);
|
||||
}
|
||||
|
||||
if (angle > 20 && angle < 90) break;
|
||||
if (angle < 100 && i > 5) break;
|
||||
i += 1;
|
||||
#endif
|
||||
|
||||
bestear = l_iter;
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
return bestear;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH TRIANGULATE FACE
|
||||
*
|
||||
* Triangulates a face using a
|
||||
* simple 'ear clipping' algorithm
|
||||
* that tries to favor non-skinny
|
||||
* triangles (angles less than
|
||||
* 90 degrees). If the triangulator
|
||||
* has bits left over (or cannot
|
||||
* triangulate at all) it uses a
|
||||
* simple fan triangulation
|
||||
*
|
||||
* newfaces, if non-null, must be an array of BMFace pointers,
|
||||
* with a length equal to f->len. it will be filled with the new
|
||||
* triangles, and will be NULL-terminated.
|
||||
*/
|
||||
void BM_face_triangulate(BMesh *bm, BMFace *f, float (*projectverts)[3],
|
||||
const short newedge_oflag, const short newface_oflag, BMFace **newfaces)
|
||||
{
|
||||
int i, done, nvert, nf_i = 0;
|
||||
BMLoop *newl, *nextloop;
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
/* BMVert *v; */ /* UNUSED */
|
||||
|
||||
/* copy vertex coordinates to vertspace arra */
|
||||
i = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
copy_v3_v3(projectverts[i], l_iter->v->co);
|
||||
BM_elem_index_set(l_iter->v, i); /* set dirty! */
|
||||
i++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
bm->elem_index_dirty |= BM_VERT; /* see above */
|
||||
|
||||
///bmesh_update_face_normal(bm, f, f->no, projectverts);
|
||||
|
||||
compute_poly_normal(f->no, projectverts, f->len);
|
||||
poly_rotate_plane(f->no, projectverts, i);
|
||||
|
||||
nvert = f->len;
|
||||
|
||||
//compute_poly_plane(projectverts, i);
|
||||
for (i = 0; i < nvert; i++) {
|
||||
projectverts[i][2] = 0.0f;
|
||||
}
|
||||
|
||||
done = 0;
|
||||
while (!done && f->len > 3) {
|
||||
done = 1;
|
||||
l_iter = find_ear(bm, f, projectverts, nvert);
|
||||
if (l_iter) {
|
||||
done = 0;
|
||||
/* v = l->v; */ /* UNUSED */
|
||||
f = BM_face_split(bm, l_iter->f, l_iter->prev->v,
|
||||
l_iter->next->v,
|
||||
&newl, NULL);
|
||||
copy_v3_v3(f->no, l_iter->f->no);
|
||||
|
||||
if (!f) {
|
||||
fprintf(stderr, "%s: triangulator failed to split face! (bmesh internal error)\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
BMO_elem_flag_enable(bm, newl->e, newedge_oflag);
|
||||
BMO_elem_flag_enable(bm, f, newface_oflag);
|
||||
|
||||
if (newfaces) newfaces[nf_i++] = f;
|
||||
|
||||
/* l = f->loopbase;
|
||||
do {
|
||||
if (l->v == v) {
|
||||
f->loopbase = l;
|
||||
break;
|
||||
}
|
||||
l = l->next;
|
||||
} while (l != f->loopbase); */
|
||||
}
|
||||
}
|
||||
|
||||
if (f->len > 3) {
|
||||
l_iter = BM_FACE_FIRST_LOOP(f);
|
||||
while (l_iter->f->len > 3) {
|
||||
nextloop = l_iter->next->next;
|
||||
f = BM_face_split(bm, l_iter->f, l_iter->v, nextloop->v,
|
||||
&newl, NULL);
|
||||
if (!f) {
|
||||
printf("triangle fan step of triangulator failed.\n");
|
||||
|
||||
/* NULL-terminat */
|
||||
if (newfaces) newfaces[nf_i] = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (newfaces) newfaces[nf_i++] = f;
|
||||
|
||||
BMO_elem_flag_enable(bm, newl->e, newedge_oflag);
|
||||
BMO_elem_flag_enable(bm, f, newface_oflag);
|
||||
l_iter = nextloop;
|
||||
}
|
||||
}
|
||||
|
||||
/* NULL-terminat */
|
||||
if (newfaces) newfaces[nf_i] = NULL;
|
||||
}
|
||||
|
||||
/* each pair of loops defines a new edge, a split. this function goes
|
||||
* through and sets pairs that are geometrically invalid to null. a
|
||||
* split is invalid, if it forms a concave angle or it intersects other
|
||||
* edges in the face, or it intersects another split. in the case of
|
||||
* intersecting splits, only the first of the set of intersecting
|
||||
* splits survives */
|
||||
void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len)
|
||||
{
|
||||
BMIter iter;
|
||||
BMLoop *l;
|
||||
float v1[3], v2[3], v3[3]/*, v4[3 */, no[3], mid[3], *p1, *p2, *p3, *p4;
|
||||
float out[3] = {-234324.0f, -234324.0f, 0.0f};
|
||||
float (*projverts)[3];
|
||||
float (*edgeverts)[3];
|
||||
float fac1 = 1.0000001f, fac2 = 0.9f; //9999f; //0.999f;
|
||||
int i, j, a = 0, clen;
|
||||
|
||||
BLI_array_fixedstack_declare(projverts, BM_NGON_STACK_SIZE, f->len, "projvertsb");
|
||||
BLI_array_fixedstack_declare(edgeverts, BM_NGON_STACK_SIZE * 2, len * 2, "edgevertsb");
|
||||
|
||||
i = 0;
|
||||
l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
|
||||
for ( ; l; l = BM_iter_step(&iter)) {
|
||||
BM_elem_index_set(l, i); /* set_loop */
|
||||
copy_v3_v3(projverts[i], l->v->co);
|
||||
i++;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
copy_v3_v3(v1, loops[i][0]->v->co);
|
||||
copy_v3_v3(v2, loops[i][1]->v->co);
|
||||
|
||||
shrink_edgef(v1, v2, fac2);
|
||||
|
||||
copy_v3_v3(edgeverts[a], v1);
|
||||
a++;
|
||||
copy_v3_v3(edgeverts[a], v2);
|
||||
a++;
|
||||
}
|
||||
|
||||
compute_poly_normal(no, projverts, f->len);
|
||||
poly_rotate_plane(no, projverts, f->len);
|
||||
poly_rotate_plane(no, edgeverts, len * 2);
|
||||
|
||||
for (i = 0, l = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l = l->next) {
|
||||
p1 = projverts[i];
|
||||
out[0] = MAX2(out[0], p1[0]) + 0.01f;
|
||||
out[1] = MAX2(out[1], p1[1]) + 0.01f;
|
||||
out[2] = 0.0f;
|
||||
p1[2] = 0.0f;
|
||||
|
||||
//copy_v3_v3(l->v->co, p1);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
edgeverts[i * 2][2] = 0.0f;
|
||||
edgeverts[i * 2 + 1][2] = 0.0f;
|
||||
}
|
||||
|
||||
/* do convexity test */
|
||||
for (i = 0; i < len; i++) {
|
||||
copy_v3_v3(v2, edgeverts[i * 2]);
|
||||
copy_v3_v3(v3, edgeverts[i * 2 + 1]);
|
||||
|
||||
mid_v3_v3v3(mid, v2, v3);
|
||||
|
||||
clen = 0;
|
||||
for (j = 0; j < f->len; j++) {
|
||||
p1 = projverts[j];
|
||||
p2 = projverts[(j + 1) % f->len];
|
||||
|
||||
copy_v3_v3(v1, p1);
|
||||
copy_v3_v3(v2, p2);
|
||||
|
||||
shrink_edgef(v1, v2, fac1);
|
||||
|
||||
if (linecrossesf(p1, p2, mid, out)) clen++;
|
||||
}
|
||||
|
||||
if (clen % 2 == 0) {
|
||||
loops[i][0] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* do line crossing test */
|
||||
for (i = 0; i < f->len; i++) {
|
||||
p1 = projverts[i];
|
||||
p2 = projverts[(i + 1) % f->len];
|
||||
|
||||
copy_v3_v3(v1, p1);
|
||||
copy_v3_v3(v2, p2);
|
||||
|
||||
shrink_edgef(v1, v2, fac1);
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
if (!loops[j][0]) continue;
|
||||
|
||||
p3 = edgeverts[j * 2];
|
||||
p4 = edgeverts[j * 2 + 1];
|
||||
|
||||
if (linecrossesf(v1, v2, p3, p4)) {
|
||||
loops[j][0] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
for (j = 0; j < len; j++) {
|
||||
if (j == i) continue;
|
||||
if (!loops[i][0]) continue;
|
||||
if (!loops[j][0]) continue;
|
||||
|
||||
p1 = edgeverts[i * 2];
|
||||
p2 = edgeverts[i * 2 + 1];
|
||||
p3 = edgeverts[j * 2];
|
||||
p4 = edgeverts[j * 2 + 1];
|
||||
|
||||
copy_v3_v3(v1, p1);
|
||||
copy_v3_v3(v2, p2);
|
||||
|
||||
shrink_edgef(v1, v2, fac1);
|
||||
|
||||
if (linecrossesf(v1, v2, p3, p4)) {
|
||||
loops[i][0] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_array_fixedstack_free(projverts);
|
||||
BLI_array_fixedstack_free(edgeverts);
|
||||
}
|
||||
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
@@ -0,0 +1,1103 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2007 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Geoffrey Bantle.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_structure.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Low level routines for manipulating the BM structure.
|
||||
*/
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/**
|
||||
* MISC utility functions.
|
||||
*
|
||||
*/
|
||||
|
||||
int bmesh_vert_in_edge(BMEdge *e, BMVert *v)
|
||||
{
|
||||
if (e->v1 == v || e->v2 == v) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
int bmesh_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e)
|
||||
{
|
||||
if (e->v1 == v1 && e->v2 == v2) return TRUE;
|
||||
else if (e->v1 == v2 && e->v2 == v1) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BMVert *bmesh_edge_getothervert(BMEdge *e, BMVert *v) {
|
||||
if (e->v1 == v) {
|
||||
return e->v2;
|
||||
}
|
||||
else if (e->v2 == v) {
|
||||
return e->v1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bmesh_edge_swapverts(BMEdge *e, BMVert *orig, BMVert *newv)
|
||||
{
|
||||
if (e->v1 == orig) {
|
||||
e->v1 = newv;
|
||||
e->v1_disk_link.next = e->v1_disk_link.prev = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
else if (e->v2 == orig) {
|
||||
e->v2 = newv;
|
||||
e->v2_disk_link.next = e->v2_disk_link.prev = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* BMESH CYCLES
|
||||
* (this is somewhat outdate, though bits of its API are still used) - joeedh
|
||||
*
|
||||
* Cycles are circular doubly linked lists that form the basis of adjacency
|
||||
* information in the BME modeller. Full adjacency relations can be derived
|
||||
* from examining these cycles very quickly. Although each cycle is a double
|
||||
* circular linked list, each one is considered to have a 'base' or 'head',
|
||||
* and care must be taken by Euler code when modifying the contents of a cycle.
|
||||
*
|
||||
* The contents of this file are split into two parts. First there are the
|
||||
* bmesh_cycle family of functions which are generic circular double linked list
|
||||
* procedures. The second part contains higher level procedures for supporting
|
||||
* modification of specific cycle types.
|
||||
*
|
||||
* The three cycles explicitly stored in the BM data structure are as follows:
|
||||
*
|
||||
* 1: The Disk Cycle - A circle of edges around a vertex
|
||||
* Base: vertex->edge pointer.
|
||||
*
|
||||
* This cycle is the most complicated in terms of its structure. Each bmesh_Edge contains
|
||||
* two bmesh_CycleNode structures to keep track of that edge's membership in the disk cycle
|
||||
* of each of its vertices. However for any given vertex it may be the first in some edges
|
||||
* in its disk cycle and the second for others. The bmesh_disk_XXX family of functions contain
|
||||
* some nice utilities for navigating disk cycles in a way that hides this detail from the
|
||||
* tool writer.
|
||||
*
|
||||
* Note that the disk cycle is completley independant from face data. One advantage of this
|
||||
* is that wire edges are fully integrated into the topology database. Another is that the
|
||||
* the disk cycle has no problems dealing with non-manifold conditions involving faces.
|
||||
*
|
||||
* Functions relating to this cycle:
|
||||
*
|
||||
* bmesh_disk_append_edge
|
||||
* bmesh_disk_remove_edge
|
||||
* bmesh_disk_nextedge
|
||||
* bmesh_disk_getpointer
|
||||
*
|
||||
* 2: The Radial Cycle - A circle of face edges (bmesh_Loop) around an edge
|
||||
* Base: edge->l->radial structure.
|
||||
*
|
||||
* The radial cycle is similar to the radial cycle in the radial edge data structure.*
|
||||
* Unlike the radial edge however, the radial cycle does not require a large amount of memory
|
||||
* to store non-manifold conditions since BM does not keep track of region/shell
|
||||
* information.
|
||||
*
|
||||
* Functions relating to this cycle:
|
||||
*
|
||||
* bmesh_radial_append
|
||||
* bmesh_radial_remove_loop
|
||||
* bmesh_radial_nextloop
|
||||
* bmesh_radial_find_face
|
||||
*
|
||||
*
|
||||
* 3: The Loop Cycle - A circle of face edges around a polygon.
|
||||
* Base: polygon->lbase.
|
||||
*
|
||||
* The loop cycle keeps track of a faces vertices and edges. It should be noted that the
|
||||
* direction of a loop cycle is either CW or CCW depending on the face normal, and is
|
||||
* not oriented to the faces editedges.
|
||||
*
|
||||
* Functions relating to this cycle:
|
||||
*
|
||||
* bmesh_cycle_XXX family of functions.
|
||||
*
|
||||
*
|
||||
* Note that the order of elements in all cycles except the loop cycle is undefined. This
|
||||
* leads to slightly increased seek time for deriving some adjacency relations, however the
|
||||
* advantage is that no intrinsic properties of the data structures are dependant upon the
|
||||
* cycle order and all non-manifold conditions are represented trivially.
|
||||
*
|
||||
*/
|
||||
int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v)
|
||||
{
|
||||
if (!v->e) {
|
||||
BMDiskLink *dl1 = BM_EDGE_DISK_LINK_GET(e, v);
|
||||
|
||||
v->e = e;
|
||||
dl1->next = dl1->prev = e;
|
||||
}
|
||||
else {
|
||||
BMDiskLink *dl1, *dl2, *dl3;
|
||||
|
||||
dl1 = BM_EDGE_DISK_LINK_GET(e, v);
|
||||
dl2 = BM_EDGE_DISK_LINK_GET(v->e, v);
|
||||
dl3 = dl2->prev ? BM_EDGE_DISK_LINK_GET(dl2->prev, v) : NULL;
|
||||
|
||||
dl1->next = v->e;
|
||||
dl1->prev = dl2->prev;
|
||||
|
||||
dl2->prev = e;
|
||||
if (dl3)
|
||||
dl3->next = e;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v)
|
||||
{
|
||||
BMDiskLink *dl1, *dl2;
|
||||
|
||||
dl1 = BM_EDGE_DISK_LINK_GET(e, v);
|
||||
if (dl1->prev) {
|
||||
dl2 = BM_EDGE_DISK_LINK_GET(dl1->prev, v);
|
||||
dl2->next = dl1->next;
|
||||
}
|
||||
|
||||
if (dl1->next) {
|
||||
dl2 = BM_EDGE_DISK_LINK_GET(dl1->next, v);
|
||||
dl2->prev = dl1->prev;
|
||||
}
|
||||
|
||||
if (v->e == e)
|
||||
v->e = (e != (BMEdge *)dl1->next) ? (BMEdge *)dl1->next : NULL;
|
||||
|
||||
dl1->next = dl1->prev = NULL;
|
||||
}
|
||||
|
||||
struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v)
|
||||
{
|
||||
if (v == e->v1)
|
||||
return e->v1_disk_link.next;
|
||||
if (v == e->v2)
|
||||
return e->v2_disk_link.next;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BMEdge *bmesh_disk_prevedge(BMEdge *e, BMVert *v)
|
||||
{
|
||||
if (v == e->v1)
|
||||
return e->v1_disk_link.prev;
|
||||
if (v == e->v2)
|
||||
return e->v2_disk_link.prev;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2)
|
||||
{
|
||||
BMEdge *curedge, *startedge;
|
||||
|
||||
if (v1->e) {
|
||||
startedge = v1->e;
|
||||
curedge = startedge;
|
||||
do {
|
||||
if (bmesh_verts_in_edge(v1, v2, curedge)) {
|
||||
return curedge;
|
||||
}
|
||||
|
||||
curedge = bmesh_disk_nextedge(curedge, v1);
|
||||
} while (curedge != startedge);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bmesh_disk_count(struct BMVert *v)
|
||||
{
|
||||
BMEdge *e = v->e;
|
||||
int i = 0;
|
||||
|
||||
if (!e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
e = bmesh_disk_nextedge(e, v);
|
||||
|
||||
if (i >= (1 << 20)) {
|
||||
printf("bmesh error: infinite loop in disk cycle!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
i++;
|
||||
} while (e != v->e);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
|
||||
{
|
||||
BMEdge *e2;
|
||||
|
||||
if (!BM_vert_in_edge(e, v))
|
||||
return FALSE;
|
||||
if (bmesh_disk_count(v) != len || len == 0)
|
||||
return FALSE;
|
||||
|
||||
e2 = e;
|
||||
do {
|
||||
if (len != 1 && bmesh_disk_prevedge(e2, v) == e2) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
e2 = bmesh_disk_nextedge(e2, v);
|
||||
} while (e2 != e);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* BME DISK COUNT FACE VERT
|
||||
*
|
||||
* Counts the number of loop users
|
||||
* for this vertex. Note that this is
|
||||
* equivalent to counting the number of
|
||||
* faces incident upon this vertex
|
||||
*/
|
||||
|
||||
int bmesh_disk_count_facevert(BMVert *v)
|
||||
{
|
||||
BMEdge *curedge;
|
||||
int count = 0;
|
||||
|
||||
/* is there an edge on this vert at all */
|
||||
if (!v->e)
|
||||
return count;
|
||||
|
||||
/* first, loop around edge */
|
||||
curedge = v->e;
|
||||
do {
|
||||
if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v);
|
||||
curedge = bmesh_disk_nextedge(curedge, v);
|
||||
} while (curedge != v->e);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v)
|
||||
{
|
||||
BMEdge *searchedge = NULL;
|
||||
searchedge = e;
|
||||
do {
|
||||
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
|
||||
return searchedge;
|
||||
}
|
||||
|
||||
searchedge = bmesh_disk_nextedge(searchedge, v);
|
||||
} while (searchedge != e);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v)
|
||||
{
|
||||
BMEdge *searchedge = NULL;
|
||||
searchedge = bmesh_disk_nextedge(e, v);
|
||||
do {
|
||||
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
|
||||
return searchedge;
|
||||
}
|
||||
searchedge = bmesh_disk_nextedge(searchedge, v);
|
||||
} while (searchedge != e);
|
||||
return e;
|
||||
}
|
||||
|
||||
/*****radial cycle functions, e.g. loops surrounding edges**** */
|
||||
int bmesh_radial_validate(int radlen, BMLoop *l)
|
||||
{
|
||||
BMLoop *l2 = l;
|
||||
int i = 0;
|
||||
|
||||
if (bmesh_radial_length(l) != radlen)
|
||||
return FALSE;
|
||||
|
||||
do {
|
||||
if (!l2) {
|
||||
bmesh_error();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (l2->e != l->e)
|
||||
return FALSE;
|
||||
if (l2->v != l->e->v1 && l2->v != l->e->v2)
|
||||
return FALSE;
|
||||
|
||||
if (i > BM_LOOP_RADIAL_MAX) {
|
||||
bmesh_error();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
i++;
|
||||
l2 = l2->radial_next;
|
||||
} while (l2 != l);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMESH RADIAL REMOVE LOOP
|
||||
*
|
||||
* Removes a loop from an radial cycle. If edge e is non-NULL
|
||||
* it should contain the radial cycle, and it will also get
|
||||
* updated (in the case that the edge's link into the radial
|
||||
* cycle was the loop which is being removed from the cycle).
|
||||
*/
|
||||
void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e)
|
||||
{
|
||||
/* if e is non-NULL, l must be in the radial cycle of e */
|
||||
if (e && e != l->e) {
|
||||
bmesh_error();
|
||||
}
|
||||
|
||||
if (l->radial_next != l) {
|
||||
if (e && l == e->l)
|
||||
e->l = l->radial_next;
|
||||
|
||||
l->radial_next->radial_prev = l->radial_prev;
|
||||
l->radial_prev->radial_next = l->radial_next;
|
||||
}
|
||||
else {
|
||||
if (e) {
|
||||
if (l == e->l) {
|
||||
e->l = NULL;
|
||||
}
|
||||
else {
|
||||
bmesh_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* l is no longer in a radial cycle; empty the links
|
||||
* to the cycle and the link back to an edge */
|
||||
l->radial_next = l->radial_prev = NULL;
|
||||
l->e = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BME RADIAL FIND FIRST FACE VERT
|
||||
*
|
||||
* Finds the first loop of v around radial
|
||||
* cycle
|
||||
*/
|
||||
BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v)
|
||||
{
|
||||
BMLoop *curloop;
|
||||
curloop = l;
|
||||
do {
|
||||
if (curloop->v == v) {
|
||||
return curloop;
|
||||
}
|
||||
|
||||
curloop = bmesh_radial_nextloop(curloop);
|
||||
} while (curloop != l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v)
|
||||
{
|
||||
BMLoop *curloop;
|
||||
curloop = bmesh_radial_nextloop(l);
|
||||
do {
|
||||
if (curloop->v == v) {
|
||||
return curloop;
|
||||
}
|
||||
|
||||
curloop = bmesh_radial_nextloop(curloop);
|
||||
} while (curloop != l);
|
||||
return l;
|
||||
}
|
||||
|
||||
BMLoop *bmesh_radial_nextloop(BMLoop *l)
|
||||
{
|
||||
return l->radial_next;
|
||||
}
|
||||
|
||||
int bmesh_radial_length(BMLoop *l)
|
||||
{
|
||||
BMLoop *l2 = l;
|
||||
int i = 0;
|
||||
|
||||
if (!l)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
if (!l2) {
|
||||
/* radial cycle is broken (not a circulat loop) */
|
||||
bmesh_error();
|
||||
return 0;
|
||||
}
|
||||
|
||||
i++;
|
||||
l2 = l2->radial_next;
|
||||
if (i >= BM_LOOP_RADIAL_MAX) {
|
||||
bmesh_error();
|
||||
return -1;
|
||||
}
|
||||
} while (l2 != l);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void bmesh_radial_append(BMEdge *e, BMLoop *l)
|
||||
{
|
||||
if (e->l == NULL) {
|
||||
e->l = l;
|
||||
l->radial_next = l->radial_prev = l;
|
||||
}
|
||||
else {
|
||||
l->radial_prev = e->l;
|
||||
l->radial_next = e->l->radial_next;
|
||||
|
||||
e->l->radial_next->radial_prev = l;
|
||||
e->l->radial_next = l;
|
||||
|
||||
e->l = l;
|
||||
}
|
||||
|
||||
if (l->e && l->e != e) {
|
||||
/* l is already in a radial cycle for a different edge */
|
||||
bmesh_error();
|
||||
}
|
||||
|
||||
l->e = e;
|
||||
}
|
||||
|
||||
int bmesh_radial_find_face(BMEdge *e, BMFace *f)
|
||||
{
|
||||
BMLoop *curloop;
|
||||
int i, len;
|
||||
|
||||
len = bmesh_radial_length(e->l);
|
||||
for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) {
|
||||
if (curloop->f == f)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* BME RADIAL COUNT FACE VERT
|
||||
*
|
||||
* Returns the number of times a vertex appears
|
||||
* in a radial cycle
|
||||
*
|
||||
*/
|
||||
|
||||
int bmesh_radial_count_facevert(BMLoop *l, BMVert *v)
|
||||
{
|
||||
BMLoop *curloop;
|
||||
int count = 0;
|
||||
curloop = l;
|
||||
do {
|
||||
if (curloop->v == v) count++;
|
||||
curloop = bmesh_radial_nextloop(curloop);
|
||||
} while (curloop != l);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*****loop cycle functions, e.g. loops surrounding a face**** */
|
||||
int bmesh_loop_validate(BMFace *f)
|
||||
{
|
||||
int i;
|
||||
int len = f->len;
|
||||
BMLoop *l_iter, *l_first;
|
||||
|
||||
l_first = BM_FACE_FIRST_LOOP(f);
|
||||
|
||||
if (l_first == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Validate that the face loop cycle is the length specified by f->len */
|
||||
for (i = 1, l_iter = l_first->next; i < len; i++, l_iter = l_iter->next) {
|
||||
if ( (l_iter->f != f) ||
|
||||
(l_iter == l_first))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (l_iter != l_first) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Validate the loop->prev links also form a cycle of length f->len */
|
||||
for (i = 1, l_iter = l_first->prev; i < len; i++, l_iter = l_iter->prev) {
|
||||
if (l_iter == l_first) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (l_iter != l_first) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void bmesh_cycle_append(void *h, void *nt)
|
||||
{
|
||||
BMNode *oldtail, *head, *newtail;
|
||||
|
||||
head = (BMNode *)h;
|
||||
newtail = (BMNode *)nt;
|
||||
|
||||
if (head->next == NULL) {
|
||||
head->next = newtail;
|
||||
head->prev = newtail;
|
||||
newtail->next = head;
|
||||
newtail->prev = head;
|
||||
}
|
||||
else {
|
||||
oldtail = head->prev;
|
||||
oldtail->next = newtail;
|
||||
newtail->next = head;
|
||||
newtail->prev = oldtail;
|
||||
head->prev = newtail;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_cycle_length
|
||||
*
|
||||
* Count the nodes in a cycle.
|
||||
*
|
||||
* Returns -
|
||||
* Integer
|
||||
*/
|
||||
|
||||
int bmesh_cycle_length(BMEdge *e, BMVert *v)
|
||||
{
|
||||
BMEdge *next, *prev, *cur;
|
||||
int len, vi = v == e->v1 ? 0 : 1;
|
||||
|
||||
/* should skip 2 forward if v is 1, happily reduces to (v * 2) */
|
||||
prev = *(&e->v1_prev + vi * 2);
|
||||
|
||||
cur = e;
|
||||
len = 1;
|
||||
while (cur != prev) {
|
||||
vi = cur->v1 == v ? 0 : 1;
|
||||
|
||||
len++;
|
||||
cur = *(&cur->v1_next + vi * 2);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_cycle_remove
|
||||
*
|
||||
* Removes a node from a cycle.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
int bmesh_cycle_remove(void *h, void *remn)
|
||||
{
|
||||
int i, len;
|
||||
BMNode *head, *remnode, *curnode;
|
||||
|
||||
head = (BMNode *)h;
|
||||
remnode = (BMNode *)remn;
|
||||
len = bmesh_cycle_length(h);
|
||||
|
||||
if (len == 1 && head == remnode) {
|
||||
head->next = NULL;
|
||||
head->prev = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
for (i = 0, curnode = head; i < len; curnode = curnode->next) {
|
||||
if (curnode == remnode) {
|
||||
remnode->prev->next = remnode->next;
|
||||
remnode->next->prev = remnode->prev;
|
||||
/* zero out remnode pointers, important */
|
||||
//remnode->next = NULL;
|
||||
//remnode->prev = NULL;
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_cycle_validate
|
||||
*
|
||||
* Validates a cycle. Takes as an argument the expected length of the cycle and
|
||||
* a pointer to the cycle head or base.
|
||||
*
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
int bmesh_cycle_validate(int len, void *h)
|
||||
{
|
||||
int i;
|
||||
BMNode *curnode, *head;
|
||||
head = (BMNode *)h;
|
||||
|
||||
/* forward validatio */
|
||||
for (i = 0, curnode = head; i < len; i++, curnode = curnode->next);
|
||||
if (curnode != head) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* reverse validatio */
|
||||
for (i = 0, curnode = head; i < len; i++, curnode = curnode->prev);
|
||||
if (curnode != head) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Begin Disk Cycle routine */
|
||||
|
||||
/**
|
||||
* bmesh_disk_nextedge
|
||||
*
|
||||
* Find the next edge in a disk cycle
|
||||
*
|
||||
* Returns -
|
||||
* Pointer to the next edge in the disk cycle for the vertex v.
|
||||
*/
|
||||
|
||||
BMEdge *bmesh_disk_nextedge(BMEdge *e, BMVert *v)
|
||||
{
|
||||
if (bmesh_vert_in_edge(e, v)) {
|
||||
if (e->v1 == v) {
|
||||
return e->d1.next->data;
|
||||
}
|
||||
else if (e->v2 == v) {
|
||||
return e->d2.next->data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_disk_getpointer
|
||||
*
|
||||
* Given an edge and one of its vertices, find the apporpriate CycleNode
|
||||
*
|
||||
* Returns -
|
||||
* Pointer to bmesh_CycleNode.
|
||||
*/
|
||||
BMNode *bmesh_disk_getpointer(BMEdge *e, BMVert *v)
|
||||
{
|
||||
/* returns pointer to the cycle node for the appropriate vertex in this dis */
|
||||
if (e->v1 == v) {
|
||||
return &(e->d1);
|
||||
}
|
||||
else if (e->v2 == v) {
|
||||
return &(e->d2);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_disk_append_edge
|
||||
*
|
||||
* Appends edge to the end of a vertex disk cycle.
|
||||
*
|
||||
* Returns -
|
||||
* 1 for success, 0 for failure
|
||||
*/
|
||||
|
||||
int bmesh_disk_append_edge(BMEdge *e, BMVert *v)
|
||||
{
|
||||
|
||||
BMNode *base, *tail;
|
||||
|
||||
/* check to make sure v is in */
|
||||
if (bmesh_vert_in_edge(e, v) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check for loose vert firs */
|
||||
if (v->e == NULL) {
|
||||
v->e = e;
|
||||
base = tail = bmesh_disk_getpointer(e, v);
|
||||
bmesh_cycle_append(base, tail); /* circular reference is ok */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* insert e at the end of disk cycle and make it the new v-> */
|
||||
base = bmesh_disk_getpointer(v->e, v);
|
||||
tail = bmesh_disk_getpointer(e, v);
|
||||
bmesh_cycle_append(base, tail);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_disk_remove_edge
|
||||
*
|
||||
* Removes an edge from a disk cycle. If the edge to be removed is
|
||||
* at the base of the cycle, the next edge becomes the new base.
|
||||
*
|
||||
*
|
||||
* Returns -
|
||||
* Nothing
|
||||
*/
|
||||
|
||||
void bmesh_disk_remove_edge(BMEdge *e, BMVert *v)
|
||||
{
|
||||
BMNode *base, *remnode;
|
||||
BMEdge *newbase;
|
||||
int len;
|
||||
|
||||
base = bmesh_disk_getpointer(v->e, v);
|
||||
remnode = bmesh_disk_getpointer(e, v);
|
||||
|
||||
/* first deal with v->e pointer.. */
|
||||
len = bmesh_cycle_length(base);
|
||||
if (len == 1) newbase = NULL;
|
||||
else if (v->e == e) newbase = base->next-> data;
|
||||
else newbase = v->e;
|
||||
|
||||
/* remove and rebas */
|
||||
bmesh_cycle_remove(base, remnode);
|
||||
v->e = newbase;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_disk_next_edgeflag
|
||||
*
|
||||
* Searches the disk cycle of v, starting with e, for the
|
||||
* next edge that has either eflag or tflag.
|
||||
*
|
||||
* bmesh_Edge pointer.
|
||||
*/
|
||||
|
||||
BMEdge *bmesh_disk_next_edgeflag(BMEdge *e, BMVert *v, int eflag, int tflag)
|
||||
{
|
||||
|
||||
BMNode *diskbase;
|
||||
BMEdge *curedge;
|
||||
int len, ok;
|
||||
|
||||
if (eflag && tflag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ok = bmesh_vert_in_edge(e, v);
|
||||
if (ok) {
|
||||
diskbase = bmesh_disk_getpointer(e, v);
|
||||
len = bmesh_cycle_length(diskbase);
|
||||
curedge = bmesh_disk_nextedge(e, v);
|
||||
while (curedge != e) {
|
||||
if (eflag) {
|
||||
if (curedge->head.eflag1 == eflag) {
|
||||
return curedge;
|
||||
}
|
||||
}
|
||||
|
||||
curedge = bmesh_disk_nextedge(curedge, v);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* bmesh_disk_count_edgeflag
|
||||
*
|
||||
* Counts number of edges in this verts disk cycle which have
|
||||
* either eflag or tflag (but not both!)
|
||||
*
|
||||
* Returns -
|
||||
* Integer.
|
||||
*/
|
||||
|
||||
int bmesh_disk_count_edgeflag(BMVert *v, int eflag, int tflag)
|
||||
{
|
||||
BMNode *diskbase;
|
||||
BMEdge *curedge;
|
||||
int i, len = 0, count = 0;
|
||||
|
||||
if (v->e) {
|
||||
|
||||
/* tflag and eflag are reserved for different functions */
|
||||
if (eflag && tflag) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
diskbase = bmesh_disk_getpointer(v->e, v);
|
||||
len = bmesh_cycle_length(diskbase);
|
||||
|
||||
for (i = 0, curedge = v->e; i < len; i++) {
|
||||
if (eflag) {
|
||||
if (curedge->head.eflag1 == eflag) count++;
|
||||
}
|
||||
curedge = bmesh_disk_nextedge(curedge, v);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int bmesh_disk_hasedge(BMVert *v, BMEdge *e)
|
||||
{
|
||||
BMNode *diskbase;
|
||||
BMEdge *curedge;
|
||||
int i, len = 0;
|
||||
|
||||
if (v->e) {
|
||||
diskbase = bmesh_disk_getpointer(v->e, v);
|
||||
len = bmesh_cycle_length(diskbase);
|
||||
|
||||
for (i = 0, curedge = v->e; i < len; i++) {
|
||||
if (curedge == e) {
|
||||
return TRUE;
|
||||
}
|
||||
else curedge = bmesh_disk_nextedge(curedge, v);
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2)
|
||||
{
|
||||
BMNode *diskbase;
|
||||
BMEdge *curedge;
|
||||
int i, len = 0;
|
||||
|
||||
if (v1->e) {
|
||||
diskbase = bmesh_disk_getpointer(v1->e, v1);
|
||||
len = bmesh_cycle_length(diskbase);
|
||||
|
||||
for (i = 0, curedge = v1->e; i < len; i++, curedge = bmesh_disk_nextedge(curedge, v1)) {
|
||||
if (bmesh_verts_in_edge(v1, v2, curedge)) {
|
||||
return curedge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* end disk cycle routine */
|
||||
BMLoop *bmesh_radial_nextloop(BMLoop *l)
|
||||
{
|
||||
return l->radial_next;
|
||||
}
|
||||
|
||||
void bmesh_radial_append(BMEdge *e, BMLoop *l)
|
||||
{
|
||||
if (e->l == NULL) e->l = l;
|
||||
bmesh_cycle_append(&(e->l->radial), &(l->radial));
|
||||
}
|
||||
|
||||
void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e)
|
||||
{
|
||||
BMLoop *newbase;
|
||||
int len;
|
||||
|
||||
/* deal with edge->l pointe */
|
||||
len = bmesh_cycle_length(&(e->l->radial));
|
||||
if (len == 1) newbase = NULL;
|
||||
else if (e->l == l) newbase = e->l->radial_next;
|
||||
else newbase = e->l;
|
||||
|
||||
/* remove and rebas */
|
||||
bmesh_cycle_remove(&(e->l->radial), &(l->radial));
|
||||
e->l = newbase;
|
||||
}
|
||||
|
||||
int bmesh_radial_find_face(BMEdge *e, BMFace *f)
|
||||
{
|
||||
|
||||
BMLoop *curloop;
|
||||
int i, len;
|
||||
|
||||
len = bmesh_cycle_length(&(e->l->radial));
|
||||
for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) {
|
||||
if (curloop->f == f) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BME RADIAL COUNT FACE VERT
|
||||
*
|
||||
* Returns the number of times a vertex appears
|
||||
* in a radial cycle
|
||||
*
|
||||
*/
|
||||
|
||||
int bmesh_radial_count_facevert(BMLoop *l, BMVert *v)
|
||||
{
|
||||
BMLoop *curloop;
|
||||
int count = 0;
|
||||
curloop = l;
|
||||
do {
|
||||
if (curloop->v == v) count++;
|
||||
curloop = bmesh_radial_nextloop(curloop);
|
||||
} while (curloop != l);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* BME DISK COUNT FACE VERT
|
||||
*
|
||||
* Counts the number of loop users
|
||||
* for this vertex. Note that this is
|
||||
* equivalent to counting the number of
|
||||
* faces incident upon this vertex
|
||||
*
|
||||
*/
|
||||
|
||||
int bmesh_disk_count_facevert(BMVert *v)
|
||||
{
|
||||
BMEdge *curedge;
|
||||
int count = 0;
|
||||
|
||||
/* is there an edge on this vert at all */
|
||||
if (!v->e)
|
||||
return count;
|
||||
|
||||
/* first, loop around edge */
|
||||
curedge = v->e;
|
||||
do {
|
||||
if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v);
|
||||
curedge = bmesh_disk_nextedge(curedge, v);
|
||||
} while (curedge != v->e);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* BME RADIAL FIND FIRST FACE VERT
|
||||
*
|
||||
* Finds the first loop of v around radial
|
||||
* cycle
|
||||
*
|
||||
*/
|
||||
BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v)
|
||||
{
|
||||
BMLoop *curloop;
|
||||
curloop = l;
|
||||
do {
|
||||
if (curloop->v == v) {
|
||||
return curloop;
|
||||
}
|
||||
|
||||
curloop = bmesh_radial_nextloop(curloop);
|
||||
} while (curloop != l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v)
|
||||
{
|
||||
BMLoop *curloop;
|
||||
curloop = bmesh_radial_nextloop(l);
|
||||
do {
|
||||
if (curloop->v == v) {
|
||||
return curloop;
|
||||
}
|
||||
|
||||
curloop = bmesh_radial_nextloop(curloop);
|
||||
} while (curloop != l);
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BME FIND FIRST FACE EDGE
|
||||
*
|
||||
* Finds the first edge in a vertices
|
||||
* Disk cycle that has one of this
|
||||
* vert's loops attached
|
||||
* to it.
|
||||
*/
|
||||
|
||||
BMEdge *bmesh_disk_find_first_faceedge(BMEdge *e, BMVert *v)
|
||||
{
|
||||
BMEdge *searchedge = NULL;
|
||||
searchedge = e;
|
||||
do {
|
||||
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
|
||||
return searchedge;
|
||||
}
|
||||
searchedge = bmesh_disk_nextedge(searchedge, v);
|
||||
} while (searchedge != e);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BMEdge *bmesh_disk_find_next_faceedge(BMEdge *e, BMVert *v)
|
||||
{
|
||||
BMEdge *searchedge = NULL;
|
||||
searchedge = bmesh_disk_nextedge(e, v);
|
||||
do {
|
||||
if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) {
|
||||
return searchedge;
|
||||
}
|
||||
searchedge = bmesh_disk_nextedge(searchedge, v);
|
||||
} while (searchedge != e);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct BMLoop *bmesh_loop_find_loop(struct BMFace *f, struct BMVert *v)
|
||||
{
|
||||
BMLoop *l;
|
||||
int i, len;
|
||||
|
||||
len = bmesh_cycle_length(f->lbase);
|
||||
for (i = 0, l = f->loopbase; i < len; i++, l = l->next) {
|
||||
if (l->v == v) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
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
@@ -0,0 +1,1412 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_heap.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_smallhash.h"
|
||||
#include "BLI_rand.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
|
||||
#define EDGE_MARK 1
|
||||
#define EDGE_VIS 2
|
||||
|
||||
#define FACE_NEW 1
|
||||
|
||||
#define ELE_NEW 1
|
||||
#define ELE_OUT 2
|
||||
#define ELE_ORIG 4
|
||||
|
||||
#define FACE_IGNORE 16
|
||||
|
||||
typedef struct EPathNode {
|
||||
struct EPathNode *next, *prev;
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMEdge *cure;
|
||||
} EPathNode;
|
||||
|
||||
typedef struct EPath {
|
||||
ListBase nodes;
|
||||
float weight;
|
||||
int group;
|
||||
} EPath;
|
||||
|
||||
typedef struct PathBase {
|
||||
BLI_mempool *nodepool, *pathpool;
|
||||
} PathBase;
|
||||
|
||||
typedef struct EdgeData {
|
||||
int tag;
|
||||
int ftag;
|
||||
BMDiskLink v1_disk_link, v2_disk_link;
|
||||
} EdgeData;
|
||||
|
||||
typedef struct VertData {
|
||||
BMEdge *e;
|
||||
float no[3], offco[3], sco[3]; /* offco is vertex coordinate slightly offset randoml */
|
||||
int tag;
|
||||
} VertData;
|
||||
|
||||
static int count_edge_faces(BMesh *bm, BMEdge *e);
|
||||
|
||||
/**** rotation system code * */
|
||||
|
||||
#define RS_GET_EDGE_LINK(e, v, e_data) ( \
|
||||
(v) == ((BMEdge *)(e))->v1 ? \
|
||||
&(((EdgeData *)(e_data))->v1_disk_link) : \
|
||||
&(((EdgeData *)(e_data))->v2_disk_link) \
|
||||
)
|
||||
|
||||
|
||||
static int rotsys_append_edge(struct BMEdge *e, struct BMVert *v,
|
||||
EdgeData *edata, VertData *vdata)
|
||||
{
|
||||
EdgeData *ed = &edata[BM_elem_index_get(e)];
|
||||
VertData *vd = &vdata[BM_elem_index_get(v)];
|
||||
|
||||
if (!vd->e) {
|
||||
Link *e1 = (Link *)RS_GET_EDGE_LINK(e, v, ed);
|
||||
|
||||
vd->e = e;
|
||||
e1->next = e1->prev = (Link *)e;
|
||||
}
|
||||
else {
|
||||
BMDiskLink *dl1, *dl2, *dl3;
|
||||
EdgeData *ved = &edata[BM_elem_index_get(vd->e)];
|
||||
|
||||
dl1 = RS_GET_EDGE_LINK(e, v, ed);
|
||||
dl2 = RS_GET_EDGE_LINK(vd->e, v, ved);
|
||||
dl3 = dl2->prev ? RS_GET_EDGE_LINK(dl2->prev, v, &edata[BM_elem_index_get(dl2->prev)]) : NULL;
|
||||
|
||||
dl1->next = vd->e;
|
||||
dl1->prev = dl2->prev;
|
||||
|
||||
dl2->prev = e;
|
||||
if (dl3) {
|
||||
dl3->next = e;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void UNUSED_FUNCTION(rotsys_remove_edge)(struct BMEdge *e, struct BMVert *v,
|
||||
EdgeData *edata, VertData *vdata)
|
||||
{
|
||||
EdgeData *ed = edata + BM_elem_index_get(e);
|
||||
VertData *vd = vdata + BM_elem_index_get(v);
|
||||
BMDiskLink *e1, *e2;
|
||||
|
||||
e1 = RS_GET_EDGE_LINK(e, v, ed);
|
||||
if (e1->prev) {
|
||||
e2 = RS_GET_EDGE_LINK(e1->prev, v, ed);
|
||||
e2->next = e1->next;
|
||||
}
|
||||
|
||||
if (e1->next) {
|
||||
e2 = RS_GET_EDGE_LINK(e1->next, v, ed);
|
||||
e2->prev = e1->prev;
|
||||
}
|
||||
|
||||
if (vd->e == e)
|
||||
vd->e = (e != (BMEdge *)e1->next) ? (BMEdge *)e1->next : NULL;
|
||||
|
||||
e1->next = e1->prev = NULL;
|
||||
}
|
||||
|
||||
static struct BMEdge *rotsys_nextedge(struct BMEdge *e, struct BMVert *v,
|
||||
EdgeData *edata, VertData *UNUSED(vdata))
|
||||
{
|
||||
if (v == e->v1)
|
||||
return edata[BM_elem_index_get(e)].v1_disk_link.next;
|
||||
if (v == e->v2)
|
||||
return edata[BM_elem_index_get(e)].v2_disk_link.next;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BMEdge *rotsys_prevedge(BMEdge *e, BMVert *v,
|
||||
EdgeData *edata, VertData *UNUSED(vdata))
|
||||
{
|
||||
if (v == e->v1)
|
||||
return edata[BM_elem_index_get(e)].v1_disk_link.prev;
|
||||
if (v == e->v2)
|
||||
return edata[BM_elem_index_get(e)].v2_disk_link.prev;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rotsys_reverse(struct BMEdge *UNUSED(e), struct BMVert *v, EdgeData *edata, VertData *vdata)
|
||||
{
|
||||
BMEdge **edges = NULL;
|
||||
BMEdge *e_first;
|
||||
BMEdge *e;
|
||||
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
|
||||
int i, totedge;
|
||||
|
||||
e = e_first = vdata[BM_elem_index_get(v)].e;
|
||||
do {
|
||||
BLI_array_append(edges, e);
|
||||
e = rotsys_nextedge(e, v, edata, vdata);
|
||||
} while (e != e_first);
|
||||
|
||||
totedge = BLI_array_count(edges);
|
||||
for (i = 0; i < totedge / 2; i++) {
|
||||
SWAP(BMEdge *, edges[i], edges[totedge - 1 - i]);
|
||||
}
|
||||
|
||||
vdata[BM_elem_index_get(v)].e = NULL;
|
||||
for (i = 0; i < totedge; i++) {
|
||||
rotsys_append_edge(edges[i], v, edata, vdata);
|
||||
}
|
||||
|
||||
BLI_array_free(edges);
|
||||
}
|
||||
|
||||
static int UNUSED_FUNCTION(rotsys_count)(struct BMVert *v, EdgeData *edata, VertData *vdata)
|
||||
{
|
||||
BMEdge *e = vdata[BM_elem_index_get(v)].e;
|
||||
int i = 0;
|
||||
|
||||
if (!e)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
if (!e)
|
||||
return 0;
|
||||
e = rotsys_nextedge(e, v, edata, vdata);
|
||||
|
||||
if (i >= (1 << 20)) {
|
||||
printf("bmesh error: infinite loop in disk cycle!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
} while (e != vdata[BM_elem_index_get(v)].e);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int UNUSED_FUNCTION(rotsys_fill_faces)(BMesh *bm, EdgeData *edata, VertData *vdata)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e, **edges = NULL;
|
||||
BLI_array_declare(edges);
|
||||
BMVert *v, **verts = NULL;
|
||||
BMFace *f;
|
||||
BLI_array_declare(verts);
|
||||
SmallHash visithash, *hash = &visithash;
|
||||
int i;
|
||||
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
BMEdge *e2, *starte;
|
||||
BMVert *startv;
|
||||
int rad, ok;
|
||||
|
||||
rad = count_edge_faces(bm, e);
|
||||
|
||||
if (rad < 2)
|
||||
starte = e;
|
||||
else
|
||||
continue;
|
||||
|
||||
/* do two passes, going forward then backwar */
|
||||
for (i = 0; i < 2; i++) {
|
||||
BLI_smallhash_init(hash);
|
||||
|
||||
BLI_array_empty(verts);
|
||||
BLI_array_empty(edges);
|
||||
|
||||
startv = v = starte->v1;
|
||||
e2 = starte;
|
||||
ok = 1;
|
||||
if (!v || !e2)
|
||||
continue;
|
||||
|
||||
do {
|
||||
if (BLI_smallhash_haskey(hash, (intptr_t)e2) ||
|
||||
BLI_smallhash_haskey(hash, (intptr_t)v))
|
||||
{
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_array_append(verts, v);
|
||||
BLI_array_append(edges, e2);
|
||||
|
||||
BLI_smallhash_insert(hash, (intptr_t)e2, NULL);
|
||||
|
||||
v = BM_edge_other_vert(e2, v);
|
||||
e2 = i ? rotsys_prevedge(e2, v, edata, vdata) : rotsys_nextedge(e2, v, edata, vdata);
|
||||
} while (e2 != starte && v != startv);
|
||||
|
||||
BLI_smallhash_release(hash);
|
||||
|
||||
if (!ok || BLI_array_count(edges) < 3)
|
||||
continue;
|
||||
|
||||
f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), TRUE);
|
||||
if (!f)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rotsys_make_consistent(BMesh *bm, EdgeData *edata, VertData *vdata)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
BMVert *v, **stack = NULL;
|
||||
BLI_array_declare(stack);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bm->totvert; i++) {
|
||||
vdata[i].tag = 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
VertData *vd;
|
||||
BMVert *startv = NULL;
|
||||
float dis;
|
||||
|
||||
v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
|
||||
for (i = 0; i < bm->totvert; i++, BM_iter_step(&iter)) {
|
||||
vd = vdata + BM_elem_index_get(v);
|
||||
|
||||
if (vd->tag)
|
||||
continue;
|
||||
|
||||
if (!startv || dot_v3v3(vd->offco, vd->offco) > dis) {
|
||||
dis = dot_v3v3(vd->offco, vd->offco);
|
||||
startv = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (!startv)
|
||||
break;
|
||||
|
||||
vd = vdata + BM_elem_index_get(startv);
|
||||
|
||||
BLI_array_empty(stack);
|
||||
BLI_array_append(stack, startv);
|
||||
|
||||
vd->tag = 1;
|
||||
|
||||
while (BLI_array_count(stack)) {
|
||||
v = BLI_array_pop(stack);
|
||||
vd = vdata + BM_elem_index_get(v);
|
||||
|
||||
if (!vd->e)
|
||||
continue;
|
||||
|
||||
e = vd->e;
|
||||
do {
|
||||
BMVert *v2 = BM_edge_other_vert(e, v);
|
||||
VertData *vd2 = vdata + BM_elem_index_get(v2);
|
||||
|
||||
if (dot_v3v3(vd->no, vd2->no) < 0.0f + FLT_EPSILON * 2) {
|
||||
rotsys_reverse(e, v2, edata, vdata);
|
||||
mul_v3_fl(vd2->no, -1.0f);
|
||||
}
|
||||
|
||||
if (!vd2->tag) {
|
||||
BLI_array_append(stack, v2);
|
||||
vd2->tag = 1;
|
||||
}
|
||||
|
||||
e = rotsys_nextedge(e, v, edata, vdata);
|
||||
} while (e != vd->e);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_array_free(stack);
|
||||
}
|
||||
|
||||
static void init_rotsys(BMesh *bm, EdgeData *edata, VertData *vdata)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
BMEdge **edges = NULL;
|
||||
BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
|
||||
BMVert *v;
|
||||
/* BMVert **verts = NULL; */
|
||||
/* BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); */ /* UNUSE */
|
||||
int i;
|
||||
|
||||
#define SIGN(n) ((n)<0.0f)
|
||||
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
BMIter eiter;
|
||||
float no[3], cent[3];
|
||||
int j, k = 0, totedge = 0;
|
||||
|
||||
if (BM_elem_index_get(v) == -1)
|
||||
continue;
|
||||
|
||||
BLI_array_empty(edges);
|
||||
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
|
||||
BLI_array_append(edges, e);
|
||||
totedge++;
|
||||
}
|
||||
}
|
||||
|
||||
copy_v3_v3(cent, v->co);
|
||||
|
||||
zero_v3(no);
|
||||
for (i = 0; i < totedge; i++) {
|
||||
BMEdge *e1, *e2;
|
||||
float cno[3], vec1[3], vec2[3];
|
||||
|
||||
e1 = edges[i];
|
||||
e2 = edges[(i + 1) % totedge];
|
||||
|
||||
sub_v3_v3v3(vec1, (BM_edge_other_vert(e1, v))->co, v->co);
|
||||
sub_v3_v3v3(vec2, (BM_edge_other_vert(e2, v))->co, v->co);
|
||||
|
||||
cross_v3_v3v3(cno, vec1, vec2);
|
||||
normalize_v3(cno);
|
||||
|
||||
if (i && dot_v3v3(cno, no) < 0.0f + FLT_EPSILON * 10)
|
||||
mul_v3_fl(cno, -1.0f);
|
||||
|
||||
add_v3_v3(no, cno);
|
||||
normalize_v3(no);
|
||||
}
|
||||
|
||||
/* generate plane-flattened coordinate */
|
||||
for (i = 0; i < totedge; i++) {
|
||||
BMEdge *e1;
|
||||
BMVert *v2;
|
||||
float cvec[3], vec1[3];
|
||||
|
||||
e1 = edges[i];
|
||||
v2 = BM_edge_other_vert(e1, v);
|
||||
|
||||
sub_v3_v3v3(vec1, v2->co, v->co);
|
||||
|
||||
cross_v3_v3v3(cvec, vec1, no);
|
||||
cross_v3_v3v3(vec1, cvec, no);
|
||||
normalize_v3(vec1);
|
||||
|
||||
mul_v3_fl(vec1, len_v3v3(v2->co, v->co));
|
||||
add_v3_v3(vec1, v->co);
|
||||
|
||||
copy_v3_v3(vdata[BM_elem_index_get(v2)].sco, vec1);
|
||||
}
|
||||
|
||||
BLI_srandom(0);
|
||||
|
||||
/* first, ensure no 0 or 180 angles between adjacent
|
||||
* (and that adjacent's adjacent) edges */
|
||||
for (i = 0, k = 0; i < totedge; i++) {
|
||||
BMEdge *e1, *e2, *e3 = NULL;
|
||||
BMVert *v1, *v2, *v3;
|
||||
VertData *vd1, *vd2, *vd3;
|
||||
float vec1[3], vec2[3], vec3[3], size;
|
||||
int s1, s2, s3;
|
||||
|
||||
if (totedge < 3)
|
||||
continue;
|
||||
|
||||
e1 = edges[(i + totedge - 1) % totedge];
|
||||
e2 = edges[i];
|
||||
e3 = edges[(i + 1) % totedge];
|
||||
|
||||
v1 = BM_edge_other_vert(e1, v);
|
||||
v2 = BM_edge_other_vert(e2, v);
|
||||
v3 = BM_edge_other_vert(e3, v);
|
||||
|
||||
vd1 = vdata + BM_elem_index_get(v1);
|
||||
vd2 = vdata + BM_elem_index_get(v2);
|
||||
vd3 = vdata + BM_elem_index_get(v3);
|
||||
|
||||
sub_v3_v3v3(vec1, vd1->sco, cent);
|
||||
sub_v3_v3v3(vec2, vd2->sco, cent);
|
||||
sub_v3_v3v3(vec3, vd3->sco, cent);
|
||||
|
||||
size = (len_v3(vec1) + len_v3(vec3)) * 0.01f;
|
||||
normalize_v3(vec1); normalize_v3(vec2); normalize_v3(vec3);
|
||||
|
||||
#ifdef STRAIGHT
|
||||
#undef STRAIGHT
|
||||
#endif
|
||||
#define STRAIGHT(vec11, vec22) (fabsf(dot_v3v3((vec11), (vec22))) > 1.0f - ((float)FLT_EPSILON * 1000.0f))
|
||||
|
||||
s1 = STRAIGHT(vec1, vec2); s2 = STRAIGHT(vec2, vec3); s3 = STRAIGHT(vec1, vec3);
|
||||
|
||||
if (s1 || s2 || s3) {
|
||||
copy_v3_v3(cent, v->co);
|
||||
|
||||
for (j = 0; j < 3; j++) {
|
||||
float fac = (BLI_frand() - 0.5f)*size;
|
||||
cent[j] += fac;
|
||||
}
|
||||
|
||||
if (k < 2000) {
|
||||
i = 0;
|
||||
k++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
k++;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
copy_v3_v3(vdata[BM_elem_index_get(v)].offco, cent);
|
||||
//copy_v3_v3(v->co, cent);
|
||||
|
||||
/* now, sort edges so the triangle fan of all edges
|
||||
* has a consistent normal. this is the same as
|
||||
* sorting by polar coordinates along a group normal */
|
||||
for (j = 0; j < totedge; j++) {
|
||||
for (i = 0; i < totedge; i++) {
|
||||
BMEdge *e1, *e2, *e3 = NULL;
|
||||
BMVert *v1, *v2, *v3;
|
||||
VertData *vd1, *vd2, *vd3;
|
||||
float vec1[3], vec2[3], vec3[3], n1[3], n2[3], n3[3];
|
||||
int s1, s2, s3;
|
||||
|
||||
e1 = edges[(i + totedge - 1) % totedge];
|
||||
e2 = edges[i];
|
||||
e3 = edges[(i + 1) % totedge];
|
||||
|
||||
v1 = BM_edge_other_vert(e1, v);
|
||||
v2 = BM_edge_other_vert(e2, v);
|
||||
v3 = BM_edge_other_vert(e3, v);
|
||||
|
||||
vd1 = vdata + BM_elem_index_get(v1);
|
||||
vd2 = vdata + BM_elem_index_get(v2);
|
||||
vd3 = vdata + BM_elem_index_get(v3);
|
||||
|
||||
sub_v3_v3v3(vec1, vd1->sco, cent);
|
||||
sub_v3_v3v3(vec2, vd2->sco, cent);
|
||||
sub_v3_v3v3(vec3, vd3->sco, cent);
|
||||
|
||||
cross_v3_v3v3(n1, vec1, vec2);
|
||||
cross_v3_v3v3(n2, vec2, vec3);
|
||||
cross_v3_v3v3(n3, vec1, vec3);
|
||||
|
||||
/* Other way to determine if two vectors approach are (nearly) parallel: the
|
||||
* cross product of the two vectors will approach zero */
|
||||
s1 = (dot_v3v3(n1, n1) < (0.0f + FLT_EPSILON * 10));
|
||||
s2 = (dot_v3v3(n2, n2) < (0.0f + FLT_EPSILON * 10));
|
||||
s3 = (totedge < 3) ? 0 : (dot_v3v3(n3, n3) < (0.0f + FLT_EPSILON * 10));
|
||||
|
||||
normalize_v3(n1); normalize_v3(n2); normalize_v3(n3);
|
||||
|
||||
if (s1 || s2 || s3) {
|
||||
fprintf(stderr, "%s: s1: %d, s2: %d, s3: %dx (bmesh internal error)\n", __func__, s1, s2, s3);
|
||||
}
|
||||
if (dot_v3v3(n1, n2) < 0.0f) {
|
||||
if (dot_v3v3(n1, n3) >= 0.0f + FLT_EPSILON * 10) {
|
||||
SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
|
||||
}
|
||||
else {
|
||||
SWAP(BMEdge *, edges[(i + totedge - 1) % totedge], edges[(i + 1) % totedge]);
|
||||
SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef STRAIGHT
|
||||
|
||||
zero_v3(no);
|
||||
|
||||
/* yay, edges are sorted */
|
||||
for (i = 0; i < totedge; i++) {
|
||||
BMEdge *e1 = edges[i], *e2 = edges[(i + 1) % totedge];
|
||||
float eno[3];
|
||||
|
||||
normal_tri_v3(eno, BM_edge_other_vert(e1, v)->co, v->co, BM_edge_other_vert(e2, v)->co);
|
||||
add_v3_v3(no, eno);
|
||||
|
||||
rotsys_append_edge(edges[i], v, edata, vdata);
|
||||
}
|
||||
|
||||
normalize_v3(no);
|
||||
copy_v3_v3(vdata[BM_elem_index_get(v)].no, no);
|
||||
}
|
||||
|
||||
/* now, make sure rotation system is topologically consistent
|
||||
* (e.g. vert normals consistently point either inside or outside) */
|
||||
rotsys_make_consistent(bm, edata, vdata);
|
||||
|
||||
//rotsys_fill_faces(bm, edata, vdata);
|
||||
|
||||
#if 0
|
||||
/* create visualizing geometr */
|
||||
BMVert *lastv;
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
BMVert *v2;
|
||||
BMFace *f;
|
||||
int totedge = BM_vert_edge_count(v);
|
||||
|
||||
if (BM_elem_index_get(v) == -1)
|
||||
continue;
|
||||
|
||||
//cv = BM_vert_create(bm, cent, v);
|
||||
//BM_elem_index_set(cv, -1); /* set_dirty! */
|
||||
i = 0;
|
||||
e = vdata[BM_elem_index_get(v)].e;
|
||||
lastv = NULL;
|
||||
do {
|
||||
BMEdge *e2;
|
||||
BMVert *v2;
|
||||
float f = ((float)i / (float)totedge) * 0.35 + 0.05;
|
||||
float co[3];
|
||||
|
||||
if (!e)
|
||||
break;
|
||||
|
||||
if (!BM_edge_other_vert(e, v))
|
||||
continue;
|
||||
|
||||
sub_v3_v3v3(co, (BM_edge_other_vert(e, v))->co, vdata[BM_elem_index_get(v)].offco);
|
||||
mul_v3_fl(co, f);
|
||||
add_v3_v3(co, vdata[BM_elem_index_get(v)].offco);
|
||||
|
||||
v2 = BM_vert_create(bm, co, NULL);
|
||||
BM_elem_index_set(v2, -1); /* set_dirty! */
|
||||
//BM_edge_create(bm, cv, v2, NULL, FALSE);
|
||||
|
||||
BM_elem_select_set(bm, v2, TRUE);
|
||||
if (lastv) {
|
||||
e2 = BM_edge_create(bm, lastv, v2, NULL, FALSE);
|
||||
BM_elem_select_set(bm, e2, TRUE);
|
||||
}
|
||||
|
||||
lastv = v2;
|
||||
|
||||
e = rotsys_nextedge(e, v, edata, vdata);
|
||||
i++;
|
||||
} while (e != vdata[BM_elem_index_get(v)].e);
|
||||
}
|
||||
#endif
|
||||
|
||||
BLI_array_free(edges);
|
||||
}
|
||||
|
||||
static PathBase *edge_pathbase_new(void)
|
||||
{
|
||||
PathBase *pb = MEM_callocN(sizeof(PathBase), "PathBase");
|
||||
|
||||
pb->nodepool = BLI_mempool_create(sizeof(EPathNode), 1, 512, TRUE, FALSE);
|
||||
pb->pathpool = BLI_mempool_create(sizeof(EPath), 1, 512, TRUE, FALSE);
|
||||
|
||||
return pb;
|
||||
}
|
||||
|
||||
static void edge_pathbase_free(PathBase *pathbase)
|
||||
{
|
||||
BLI_mempool_destroy(pathbase->nodepool);
|
||||
BLI_mempool_destroy(pathbase->pathpool);
|
||||
MEM_freeN(pathbase);
|
||||
}
|
||||
|
||||
static EPath *edge_copy_add_path(PathBase *pb, EPath *path, BMVert *appendv, BMEdge *e)
|
||||
{
|
||||
EPath *path2;
|
||||
EPathNode *node, *node2;
|
||||
|
||||
path2 = BLI_mempool_alloc(pb->pathpool);
|
||||
path2->nodes.first = path2->nodes.last = NULL;
|
||||
path2->weight = 0.0f;
|
||||
path2->group = path->group;
|
||||
|
||||
for (node = path->nodes.first; node; node = node->next) {
|
||||
node2 = BLI_mempool_alloc(pb->nodepool);
|
||||
*node2 = *node;
|
||||
BLI_addtail(&path2->nodes, node2);
|
||||
}
|
||||
|
||||
node2 = BLI_mempool_alloc(pb->nodepool);
|
||||
node2->v = appendv;
|
||||
node2->e = e;
|
||||
node2->cure = NULL;
|
||||
|
||||
BLI_addtail(&path2->nodes, node2);
|
||||
|
||||
return path2;
|
||||
}
|
||||
|
||||
static EPath *edge_path_new(PathBase *pb, BMVert *start, BMEdge *starte)
|
||||
{
|
||||
EPath *path;
|
||||
EPathNode *node;
|
||||
|
||||
path = BLI_mempool_alloc(pb->pathpool);
|
||||
node = BLI_mempool_alloc(pb->nodepool);
|
||||
|
||||
path->nodes.first = path->nodes.last = NULL;
|
||||
|
||||
node->v = start;
|
||||
node->e = starte;
|
||||
node->cure = NULL;
|
||||
|
||||
BLI_addtail(&path->nodes, node);
|
||||
path->weight = 0.0f;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static float edge_weight_path(EPath *path, EdgeData *edata, VertData *UNUSED(vdata))
|
||||
{
|
||||
EPathNode *node, *first = path->nodes.first;
|
||||
float w = 0.0;
|
||||
|
||||
for (node = path->nodes.first; node; node = node->next) {
|
||||
if (node->e && node != path->nodes.first) {
|
||||
w += edata[BM_elem_index_get(node->e)].ftag;
|
||||
if (node->prev) {
|
||||
/* BMESH_TOD */
|
||||
(void)first;
|
||||
//w += len_v3v3(node->v->co, first->e->v1->co) * 0.0001f;
|
||||
//w += len_v3v3(node->v->co, first->e->v2->co) * 0.0001f;
|
||||
}
|
||||
}
|
||||
|
||||
w += 1.0f;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
static void edge_free_path(PathBase *pathbase, EPath *path)
|
||||
{
|
||||
EPathNode *node, *next;
|
||||
|
||||
for (node = path->nodes.first; node; node = next) {
|
||||
next = node->next;
|
||||
BLI_mempool_free(pathbase->nodepool, node);
|
||||
}
|
||||
|
||||
BLI_mempool_free(pathbase->pathpool, path);
|
||||
}
|
||||
|
||||
static EPath *edge_find_shortest_path(BMesh *bm, BMOperator *op, BMEdge *edge, EdgeData *edata,
|
||||
VertData *vdata, PathBase *pathbase, int group)
|
||||
{
|
||||
BMEdge *e;
|
||||
GHash *gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "createops find shortest path");
|
||||
BMVert *v1, *v2;
|
||||
BMVert **verts = NULL;
|
||||
BLI_array_staticdeclare(verts, 1024);
|
||||
Heap *heap = BLI_heap_new();
|
||||
EPath *path = NULL, *path2;
|
||||
BMVert *startv;
|
||||
BMVert *endv;
|
||||
EPathNode *node;
|
||||
int i, use_restrict = BMO_slot_int_get(op, "use_restrict");
|
||||
|
||||
startv = edata[BM_elem_index_get(edge)].ftag ? edge->v2 : edge->v1;
|
||||
endv = edata[BM_elem_index_get(edge)].ftag ? edge->v1 : edge->v2;
|
||||
|
||||
path = edge_path_new(pathbase, startv, edge);
|
||||
BLI_ghash_insert(gh, startv, NULL);
|
||||
BLI_heap_insert(heap, path->weight, path);
|
||||
path->group = group;
|
||||
|
||||
while (BLI_heap_size(heap)) {
|
||||
VertData *vd;
|
||||
EPathNode *last;
|
||||
BMFace *f = NULL;
|
||||
|
||||
path = BLI_heap_popmin(heap);
|
||||
last = path->nodes.last;
|
||||
v1 = last->v;
|
||||
|
||||
if (v1 == endv) {
|
||||
/* make sure this path loop doesn't already exist */
|
||||
i = 0;
|
||||
BLI_array_empty(verts);
|
||||
for (i = 0, node = path->nodes.first; node; node = node->next, i++) {
|
||||
BLI_array_growone(verts);
|
||||
verts[i] = node->v;
|
||||
}
|
||||
|
||||
if (BM_face_exists(bm, verts, i, &f)) {
|
||||
if (!BMO_elem_flag_test(bm, f, FACE_IGNORE)) {
|
||||
BLI_ghash_remove(gh, endv, NULL, NULL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
vd = vdata + BM_elem_index_get(v1);
|
||||
if (!vd->e)
|
||||
continue;
|
||||
|
||||
v2 = NULL;
|
||||
while (1) {
|
||||
if (!last->cure) {
|
||||
last->cure = e = vdata[BM_elem_index_get(last->v)].e;
|
||||
}
|
||||
else {
|
||||
last->cure = e = rotsys_nextedge(last->cure, last->v, edata, vdata);
|
||||
if (last->cure == vdata[BM_elem_index_get(last->v)].e) {
|
||||
v2 = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (e == edge || !BMO_elem_flag_test(bm, e, EDGE_MARK)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
v2 = BM_edge_other_vert(e, last->v);
|
||||
|
||||
if (BLI_ghash_haskey(gh, v2)) {
|
||||
v2 = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_restrict && BMO_slot_map_contains(bm, op, "restrict", e)) {
|
||||
int group = BMO_slot_map_int_get(bm, op, "restrict", e);
|
||||
|
||||
if (!(group & path->group)) {
|
||||
v2 = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!v2) {
|
||||
if (path) {
|
||||
edge_free_path(pathbase, path);
|
||||
path = NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* add path back into hea */
|
||||
BLI_heap_insert(heap, path->weight, path);
|
||||
|
||||
/* put v2 in gh ma */
|
||||
BLI_ghash_insert(gh, v2, NULL);
|
||||
|
||||
path2 = edge_copy_add_path(pathbase, path, v2, e);
|
||||
path2->weight = edge_weight_path(path2, edata, vdata);
|
||||
|
||||
BLI_heap_insert(heap, path2->weight, path2);
|
||||
}
|
||||
|
||||
if (path && ((EPathNode *)path->nodes.last)->v != endv) {
|
||||
edge_free_path(pathbase, path);
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
BLI_array_free(verts);
|
||||
BLI_heap_free(heap, NULL);
|
||||
BLI_ghash_free(gh, NULL, NULL);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static int count_edge_faces(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
int i = 0;
|
||||
BMLoop *l = e->l;
|
||||
|
||||
if (!l) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!BMO_elem_flag_test(bm, l->f, FACE_IGNORE)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
l = l->radial_next;
|
||||
} while (l != e->l);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMIter iter;
|
||||
BMOIter siter;
|
||||
BMFace *f;
|
||||
BMEdge *e, *edge;
|
||||
BMVert **verts = NULL;
|
||||
BLI_array_declare(verts);
|
||||
EPath *path;
|
||||
EPathNode *node;
|
||||
EdgeData *edata;
|
||||
VertData *vdata;
|
||||
BMEdge **edges = NULL;
|
||||
PathBase *pathbase = edge_pathbase_new();
|
||||
BLI_array_declare(edges);
|
||||
int use_restrict = BMO_slot_int_get(op, "use_restrict");
|
||||
int i, j, group = 0;
|
||||
unsigned int winding[2]; /* accumulte winding directions for each edge which has a face */
|
||||
|
||||
if (!bm->totvert || !bm->totedge)
|
||||
return;
|
||||
|
||||
edata = MEM_callocN(sizeof(EdgeData)*bm->totedge, "EdgeData");
|
||||
vdata = MEM_callocN(sizeof(VertData)*bm->totvert, "VertData");
|
||||
|
||||
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
|
||||
BMO_slot_buffer_flag_enable(bm, op, "excludefaces", FACE_IGNORE, BM_FACE);
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
BMO_elem_flag_enable(bm, f, ELE_ORIG);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
BM_elem_index_set(e, i); /* set_inline */
|
||||
|
||||
if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
|
||||
edata[i].tag = 2;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_EDGE;
|
||||
|
||||
init_rotsys(bm, edata, vdata);
|
||||
|
||||
while (1) {
|
||||
edge = NULL;
|
||||
group = 0;
|
||||
|
||||
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
|
||||
/* if restrict is on, only start on faces in the restrict map */
|
||||
if (use_restrict && !BMO_slot_map_contains(bm, op, "restrict", e))
|
||||
continue;
|
||||
|
||||
if (edata[BM_elem_index_get(e)].tag < 2) {
|
||||
edge = e;
|
||||
|
||||
if (use_restrict) {
|
||||
int i = 0, j = 0, gi = 0;
|
||||
|
||||
group = BMO_slot_map_int_get(bm, op, "restrict", e);
|
||||
|
||||
for (i = 0; i < 30; i++) {
|
||||
if (group & (1 << i)) {
|
||||
j++;
|
||||
gi = i;
|
||||
|
||||
if (j - 1 == edata[BM_elem_index_get(e)].tag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group = (1 << gi);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!edge)
|
||||
break;
|
||||
|
||||
edata[BM_elem_index_get(edge)].tag += 1;
|
||||
|
||||
path = edge_find_shortest_path(bm, op, edge, edata, vdata, pathbase, group);
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
winding[0] = winding[1] = 0;
|
||||
|
||||
BLI_array_empty(edges);
|
||||
BLI_array_empty(verts);
|
||||
i = 0;
|
||||
for (node = path->nodes.first; node; node = node->next) {
|
||||
if (!node->next)
|
||||
continue;
|
||||
|
||||
e = BM_edge_exists(node->v, node->next->v);
|
||||
|
||||
/* this should never happe */
|
||||
if (!e)
|
||||
break;
|
||||
|
||||
/* check on the winding */
|
||||
if (e->l) {
|
||||
BMVert *test_v1, *test_v2;
|
||||
/* we want to use the reverse winding to the existing order */
|
||||
BM_edge_ordered_verts(edge, &test_v2, &test_v1);
|
||||
|
||||
/* edges vote on which winding wins out */
|
||||
winding[(test_v1 == node->v)]++;
|
||||
}
|
||||
|
||||
edata[BM_elem_index_get(e)].ftag++;
|
||||
BLI_array_growone(edges);
|
||||
edges[i++] = e;
|
||||
|
||||
BLI_array_append(verts, node->v);
|
||||
}
|
||||
|
||||
BLI_array_growone(edges);
|
||||
edges[i++] = edge;
|
||||
edata[BM_elem_index_get(edge)].ftag++;
|
||||
|
||||
for (j = 0; j < i; j++) {
|
||||
if (count_edge_faces(bm, edges[j]) >= 2) {
|
||||
edge_free_path(pathbase, path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j != i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
BMVert *v1, *v2;
|
||||
|
||||
/* to define the winding order must select first edge,
|
||||
* otherwise we could leave this as-is */
|
||||
edge = edges[0];
|
||||
|
||||
/* if these are even it doesnt really matter what to do,
|
||||
* with consistent geometry one will be zero, the choice is clear */
|
||||
if (winding[0] > winding[1]) {
|
||||
v1 = verts[0];
|
||||
v2 = verts[1];
|
||||
}
|
||||
else {
|
||||
v1 = verts[1];
|
||||
v2 = verts[0];
|
||||
}
|
||||
|
||||
f = BM_face_create_ngon(bm, v1, v2, edges, i, TRUE);
|
||||
if (f && !BMO_elem_flag_test(bm, f, ELE_ORIG)) {
|
||||
BMO_elem_flag_enable(bm, f, FACE_NEW);
|
||||
}
|
||||
|
||||
if (use_restrict)
|
||||
BMO_slot_map_int_insert(bm, op, "faceout_groupmap", f, path->group);
|
||||
}
|
||||
|
||||
edge_free_path(pathbase, path);
|
||||
}
|
||||
|
||||
BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE);
|
||||
|
||||
BLI_array_free(edges);
|
||||
BLI_array_free(verts);
|
||||
edge_pathbase_free(pathbase);
|
||||
MEM_freeN(edata);
|
||||
MEM_freeN(vdata);
|
||||
}
|
||||
|
||||
static BMEdge *edge_next(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) {
|
||||
if ( (BMO_elem_flag_test(bm, e2, EDGE_MARK)) &&
|
||||
(!BMO_elem_flag_test(bm, e2, EDGE_VIS)) &&
|
||||
(e2 != e))
|
||||
{
|
||||
return e2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter siter;
|
||||
BMEdge *e;
|
||||
BMEdge **edges1 = NULL, **edges2 = NULL, **edges;
|
||||
BLI_array_declare(edges1);
|
||||
BLI_array_declare(edges2);
|
||||
BLI_array_declare(edges);
|
||||
int ok = 1;
|
||||
int i, count;
|
||||
|
||||
BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
|
||||
|
||||
/* validate that each edge has at most one other tagged edge in the
|
||||
* disk cycle around each of it's vertices */
|
||||
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
count = BMO_vert_edge_flags_count(bm, i ? e->v2 : e->v1, EDGE_MARK);
|
||||
if (count > 2) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* we don't have valid edge layouts, retur */
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* find connected loops within the input edge */
|
||||
count = 0;
|
||||
while (1) {
|
||||
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
|
||||
if (!BMO_elem_flag_test(bm, e, EDGE_VIS)) {
|
||||
if ( BMO_vert_edge_flags_count(bm, e->v1, EDGE_MARK) == 1 ||
|
||||
BMO_vert_edge_flags_count(bm, e->v2, EDGE_MARK) == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!e) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
edges = edges1;
|
||||
}
|
||||
else if (count == 1) {
|
||||
edges = edges2;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (e) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_VIS);
|
||||
BLI_array_growone(edges);
|
||||
edges[i] = e;
|
||||
|
||||
e = edge_next(bm, e);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
edges1 = edges;
|
||||
BLI_array_set_length(edges1, BLI_array_count(edges));
|
||||
}
|
||||
else {
|
||||
edges2 = edges;
|
||||
BLI_array_set_length(edges2, BLI_array_count(edges));
|
||||
}
|
||||
|
||||
BLI_array_empty(edges);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (edges1 && BLI_array_count(edges1) > 2 &&
|
||||
BM_edge_share_vert(edges1[0], edges1[BLI_array_count(edges1) - 1]))
|
||||
{
|
||||
if (edges2 && BLI_array_count(edges2) > 2 &&
|
||||
BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
|
||||
{
|
||||
BLI_array_free(edges1);
|
||||
BLI_array_free(edges2);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
edges1 = edges2;
|
||||
edges2 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (edges2 && BLI_array_count(edges2) > 2 &&
|
||||
BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
|
||||
{
|
||||
edges2 = NULL;
|
||||
}
|
||||
|
||||
/* two unconnected loops, connect the */
|
||||
if (edges1 && edges2) {
|
||||
BMVert *v1, *v2, *v3, *v4;
|
||||
|
||||
if (BLI_array_count(edges1) == 1) {
|
||||
v1 = edges1[0]->v1;
|
||||
v2 = edges1[0]->v2;
|
||||
}
|
||||
else {
|
||||
if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
|
||||
v1 = edges1[0]->v2;
|
||||
else v1 = edges1[0]->v1;
|
||||
|
||||
i = BLI_array_count(edges1) - 1;
|
||||
if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
|
||||
v2 = edges1[i]->v2;
|
||||
else v2 = edges1[i]->v1;
|
||||
}
|
||||
|
||||
if (BLI_array_count(edges2) == 1) {
|
||||
v3 = edges2[0]->v1;
|
||||
v4 = edges2[0]->v2;
|
||||
}
|
||||
else {
|
||||
if (BM_vert_in_edge(edges2[1], edges2[0]->v1))
|
||||
v3 = edges2[0]->v2;
|
||||
else v3 = edges2[0]->v1;
|
||||
|
||||
i = BLI_array_count(edges2) - 1;
|
||||
if (BM_vert_in_edge(edges2[i - 1], edges2[i]->v1))
|
||||
v4 = edges2[i]->v2;
|
||||
else v4 = edges2[i]->v1;
|
||||
}
|
||||
|
||||
/* avoid sqrt for comparison */
|
||||
if (len_squared_v3v3(v1->co, v3->co) + len_squared_v3v3(v2->co, v4->co) >
|
||||
len_squared_v3v3(v1->co, v4->co) + len_squared_v3v3(v2->co, v3->co))
|
||||
{
|
||||
BMVert *v;
|
||||
v = v3;
|
||||
v3 = v4;
|
||||
v4 = v;
|
||||
}
|
||||
|
||||
e = BM_edge_create(bm, v1, v3, NULL, TRUE);
|
||||
BMO_elem_flag_enable(bm, e, ELE_NEW);
|
||||
e = BM_edge_create(bm, v2, v4, NULL, TRUE);
|
||||
BMO_elem_flag_enable(bm, e, ELE_NEW);
|
||||
}
|
||||
else if (edges1) {
|
||||
BMVert *v1, *v2;
|
||||
|
||||
if (BLI_array_count(edges1) > 1) {
|
||||
if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
|
||||
v1 = edges1[0]->v2;
|
||||
else v1 = edges1[0]->v1;
|
||||
|
||||
i = BLI_array_count(edges1) - 1;
|
||||
if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
|
||||
v2 = edges1[i]->v2;
|
||||
else v2 = edges1[i]->v1;
|
||||
|
||||
e = BM_edge_create(bm, v1, v2, NULL, TRUE);
|
||||
BMO_elem_flag_enable(bm, e, ELE_NEW);
|
||||
}
|
||||
}
|
||||
|
||||
BMO_slot_from_flag(bm, op, "edgeout", ELE_NEW, BM_EDGE);
|
||||
|
||||
BLI_array_free(edges1);
|
||||
BLI_array_free(edges2);
|
||||
}
|
||||
|
||||
/* this is essentially new fke */
|
||||
void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOperator op2;
|
||||
BMOIter oiter;
|
||||
BMIter iter;
|
||||
BMHeader *h;
|
||||
BMVert *v, *verts[4];
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
int totv = 0, tote = 0, totf = 0, amount;
|
||||
|
||||
/* count number of each element type we were passe */
|
||||
BMO_ITER(h, &oiter, bm, op, "geom", BM_VERT|BM_EDGE|BM_FACE) {
|
||||
switch (h->htype) {
|
||||
case BM_VERT: totv++; break;
|
||||
case BM_EDGE: tote++; break;
|
||||
case BM_FACE: totf++; break;
|
||||
}
|
||||
|
||||
BMO_elem_flag_enable(bm, (BMElemF *)h, ELE_NEW);
|
||||
}
|
||||
|
||||
/* --- Support for Special Case ---
|
||||
* where there is a contiguous edge ring with one isolated vertex.
|
||||
*
|
||||
* This example shows 2 edges created from 3 verts
|
||||
* with 1 free standing vertex. Dotted lines denote the 2 edges that are created.
|
||||
*
|
||||
* note that this works for any sided shape.
|
||||
*
|
||||
* +--------+
|
||||
* | .
|
||||
* | .
|
||||
* | .
|
||||
* | .
|
||||
* +........+ <-- starts out free standing.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Here we check for consistancy and create 2 edges */
|
||||
if (totf == 0 && totv >= 4 && totv == tote + 2) {
|
||||
/* find a free standing vertex and 2 endpoint verts */
|
||||
BMVert *v_free = NULL, *v_a = NULL, *v_b = NULL;
|
||||
int ok = TRUE;
|
||||
|
||||
|
||||
BMO_ITER(v, &oiter, bm, op, "geom", BM_VERT) {
|
||||
/* count how many flagged edges this vertex uses */
|
||||
int tot_edges = 0;
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
|
||||
if (BMO_elem_flag_test(bm, e, ELE_NEW)) {
|
||||
tot_edges++;
|
||||
if (tot_edges > 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tot_edges == 0) {
|
||||
/* only accept 1 free vert */
|
||||
if (v_free == NULL) v_free = v;
|
||||
else ok = FALSE; /* only ever want one of these */
|
||||
}
|
||||
else if (tot_edges == 1) {
|
||||
if (v_a == NULL) v_a = v;
|
||||
else if (v_b == NULL) v_b = v;
|
||||
else ok = FALSE; /* only ever want 2 of these */
|
||||
}
|
||||
else if (tot_edges == 2) {
|
||||
/* do nothing, regular case */
|
||||
}
|
||||
else {
|
||||
ok = FALSE; /* if a vertex has 3+ edge users then cancel - this is only simple cases */
|
||||
}
|
||||
|
||||
if (ok == FALSE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok == TRUE && v_free && v_a && v_b) {
|
||||
e = BM_edge_create(bm, v_free, v_a, NULL, TRUE);
|
||||
BMO_elem_flag_enable(bm, e, ELE_NEW);
|
||||
|
||||
e = BM_edge_create(bm, v_free, v_b, NULL, TRUE);
|
||||
BMO_elem_flag_enable(bm, e, ELE_NEW);
|
||||
}
|
||||
}
|
||||
/* --- end special case support, continue as normal --- */
|
||||
|
||||
|
||||
/* possible bug?, selecting 2 triangles and pressing F will make a quad rather then joining them,
|
||||
* perhaps this should be looked into? - campbell */
|
||||
|
||||
/* call edgenet create */
|
||||
/* call edgenet prepare op so additional face creation cases wor */
|
||||
BMO_op_initf(bm, &op2, "edgenet_prepare edges=%fe", ELE_NEW);
|
||||
BMO_op_exec(bm, &op2);
|
||||
BMO_slot_buffer_flag_enable(bm, &op2, "edgeout", ELE_NEW, BM_EDGE);
|
||||
BMO_op_finish(bm, &op2);
|
||||
|
||||
BMO_op_initf(bm, &op2, "edgenet_fill edges=%fe", ELE_NEW);
|
||||
BMO_op_exec(bm, &op2);
|
||||
|
||||
/* return if edge net create did somethin */
|
||||
if (BMO_slot_buf_count(bm, &op2, "faceout")) {
|
||||
BMO_slot_copy(&op2, op, "faceout", "faceout");
|
||||
BMO_op_finish(bm, &op2);
|
||||
return;
|
||||
}
|
||||
|
||||
BMO_op_finish(bm, &op2);
|
||||
|
||||
/* now call dissolve face */
|
||||
BMO_op_initf(bm, &op2, "dissolvefaces faces=%ff", ELE_NEW);
|
||||
BMO_op_exec(bm, &op2);
|
||||
|
||||
/* if we dissolved anything, then return */
|
||||
if (BMO_slot_buf_count(bm, &op2, "regionout")) {
|
||||
BMO_slot_copy(&op2, op, "regionout", "faceout");
|
||||
BMO_op_finish(bm, &op2);
|
||||
return;
|
||||
}
|
||||
|
||||
BMO_op_finish(bm, &op2);
|
||||
|
||||
/* now, count how many verts we hav */
|
||||
amount = 0;
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if (BMO_elem_flag_test(bm, v, ELE_NEW)) {
|
||||
verts[amount] = v;
|
||||
amount++;
|
||||
|
||||
if (amount > 4) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (amount == 2) {
|
||||
/* create edg */
|
||||
e = BM_edge_create(bm, verts[0], verts[1], NULL, TRUE);
|
||||
BMO_elem_flag_enable(bm, e, ELE_OUT);
|
||||
}
|
||||
else if (amount == 3) {
|
||||
/* create triangl */
|
||||
BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], NULL, NULL, TRUE);
|
||||
}
|
||||
else if (amount == 4) {
|
||||
f = NULL;
|
||||
|
||||
/* the order of vertices can be anything, 6 cases to check */
|
||||
if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
|
||||
f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], verts[3], NULL, TRUE);
|
||||
}
|
||||
else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[3]->co, verts[1]->co)) {
|
||||
f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[3], verts[1], NULL, TRUE);
|
||||
}
|
||||
else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[1]->co, verts[3]->co)) {
|
||||
f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[1], verts[3], NULL, TRUE);
|
||||
}
|
||||
else if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[3]->co, verts[2]->co)) {
|
||||
f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[3], verts[2], NULL, TRUE);
|
||||
}
|
||||
else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[2]->co, verts[1]->co)) {
|
||||
f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[2], verts[1], NULL, TRUE);
|
||||
}
|
||||
else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[1]->co, verts[2]->co)) {
|
||||
f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[1], verts[2], NULL, TRUE);
|
||||
}
|
||||
else {
|
||||
printf("cannot find nice quad from concave set of vertices\n");
|
||||
}
|
||||
|
||||
if (f) BMO_elem_flag_enable(bm, f, ELE_OUT);
|
||||
}
|
||||
}
|
||||
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
@@ -0,0 +1,1104 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_noise.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "ED_mesh.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
#include "bmesh_operators_private.h" /* own include */
|
||||
|
||||
#include "bmo_subdivide.h" /* own include */
|
||||
|
||||
/* flags for all elements share a common bitfield space */
|
||||
#define SUBD_SPLIT 1
|
||||
|
||||
#define EDGE_PERCENT 2
|
||||
|
||||
/* I don't think new faces are flagged, currently, but
|
||||
* better safe than sorry. */
|
||||
#define FACE_CUSTOMFILL 4
|
||||
#define ELE_INNER 8
|
||||
#define ELE_SPLIT 16
|
||||
|
||||
/*
|
||||
* NOTE: beauty has been renamed to flag!
|
||||
*/
|
||||
|
||||
/* generic subdivision rules:
|
||||
*
|
||||
* - two selected edges in a face should make a link
|
||||
* between them.
|
||||
*
|
||||
* - one edge should do, what? make pretty topology, or just
|
||||
* split the edge only?
|
||||
*/
|
||||
|
||||
/* connects face with smallest len, which I think should always be correct for
|
||||
* edge subdivision */
|
||||
static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **r_nf)
|
||||
{
|
||||
BMIter iter, iter2;
|
||||
BMVert *v;
|
||||
BMLoop *nl;
|
||||
BMFace *face, *curf = NULL;
|
||||
|
||||
/* this isn't the best thing in the world. it doesn't handle cases where there's
|
||||
* multiple faces yet. that might require a convexity test to figure out which
|
||||
* face is "best," and who knows what for non-manifold conditions. */
|
||||
for (face = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v1); face; face = BM_iter_step(&iter)) {
|
||||
for (v = BM_iter_new(&iter2, bm, BM_VERTS_OF_FACE, face); v; v = BM_iter_step(&iter2)) {
|
||||
if (v == v2) {
|
||||
if (!curf || face->len < curf->len) curf = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (curf) {
|
||||
face = BM_face_split(bm, curf, v1, v2, &nl, NULL);
|
||||
|
||||
if (r_nf) *r_nf = face;
|
||||
return nl ? nl->e : NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* calculates offset for co, based on fractal, sphere or smooth settings */
|
||||
static void alter_co(BMesh *bm, BMVert *v, BMEdge *UNUSED(origed), const subdparams *params, float perc,
|
||||
BMVert *vsta, BMVert *vend)
|
||||
{
|
||||
float tvec[3], prev_co[3], fac;
|
||||
float *co = NULL;
|
||||
int i, totlayer = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY);
|
||||
|
||||
BM_vert_normal_update_all(bm, v);
|
||||
|
||||
co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, params->origkey);
|
||||
copy_v3_v3(prev_co, co);
|
||||
|
||||
if (params->beauty & B_SMOOTH) {
|
||||
/* we calculate an offset vector vec1[], to be added to *co */
|
||||
float len, nor[3], nor1[3], nor2[3], smooth = params->smooth;
|
||||
|
||||
sub_v3_v3v3(nor, vsta->co, vend->co);
|
||||
len = 0.5f * normalize_v3(nor);
|
||||
|
||||
copy_v3_v3(nor1, vsta->no);
|
||||
copy_v3_v3(nor2, vend->no);
|
||||
|
||||
/* cosine angle */
|
||||
fac= dot_v3v3(nor, nor1);
|
||||
mul_v3_v3fl(tvec, nor1, fac);
|
||||
|
||||
/* cosine angle */
|
||||
fac = -dot_v3v3(nor, nor2);
|
||||
madd_v3_v3fl(tvec, nor2, fac);
|
||||
|
||||
/* falloff for multi subdivide */
|
||||
smooth *= sqrtf(fabsf(1.0f - 2.0f * fabsf(0.5f-perc)));
|
||||
|
||||
mul_v3_fl(tvec, smooth * len);
|
||||
|
||||
add_v3_v3(co, tvec);
|
||||
}
|
||||
else if (params->beauty & B_SPHERE) { /* subdivide sphere */
|
||||
normalize_v3(co);
|
||||
mul_v3_fl(co, params->smooth);
|
||||
}
|
||||
|
||||
if (params->beauty & B_FRACTAL) {
|
||||
float len = len_v3v3(vsta->co, vend->co);
|
||||
float vec2[3] = {0.0f, 0.0f, 0.0f}, co2[3];
|
||||
|
||||
fac = params->fractal * len;
|
||||
|
||||
add_v3_v3(vec2, vsta->no);
|
||||
add_v3_v3(vec2, vend->no);
|
||||
mul_v3_fl(vec2, 0.5f);
|
||||
|
||||
add_v3_v3v3(co2, v->co, params->off);
|
||||
tvec[0] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
|
||||
tvec[1] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
|
||||
tvec[2] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
|
||||
|
||||
mul_v3_v3(vec2, tvec);
|
||||
|
||||
/* add displacemen */
|
||||
add_v3_v3v3(co, co, vec2);
|
||||
}
|
||||
|
||||
/* apply the new difference to the rest of the shape keys,
|
||||
* note that this doent take rotations into account, we _could_ support
|
||||
* this by getting the normals and coords for each shape key and
|
||||
* re-calculate the smooth value for each but this is quite involved.
|
||||
* for now its ok to simply apply the difference IMHO - campbell */
|
||||
sub_v3_v3v3(tvec, prev_co, co);
|
||||
|
||||
for (i = 0; i < totlayer; i++) {
|
||||
if (params->origkey != i) {
|
||||
co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, i);
|
||||
sub_v3_v3(co, tvec);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* assumes in the edge is the correct interpolated vertices already */
|
||||
/* percent defines the interpolation, rad and flag are for special options */
|
||||
/* results in new vertex with correct coordinate, vertex normal and weight group info */
|
||||
static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge,
|
||||
const subdparams *params, float percent,
|
||||
float percent2,
|
||||
BMEdge **out, BMVert *vsta, BMVert *vend)
|
||||
{
|
||||
BMVert *ev;
|
||||
|
||||
ev = BM_edge_split(bm, edge->v1, edge, out, percent);
|
||||
|
||||
BMO_elem_flag_enable(bm, ev, ELE_INNER);
|
||||
|
||||
/* offset for smooth or sphere or fractal */
|
||||
alter_co(bm, ev, oedge, params, percent2, vsta, vend);
|
||||
|
||||
#if 0 //BMESH_TODO
|
||||
/* clip if needed by mirror modifier */
|
||||
if (edge->v1->f2) {
|
||||
if (edge->v1->f2 & edge->v2->f2 & 1) {
|
||||
co[0] = 0.0f;
|
||||
}
|
||||
if (edge->v1->f2 & edge->v2->f2 & 2) {
|
||||
co[1] = 0.0f;
|
||||
}
|
||||
if (edge->v1->f2 & edge->v2->f2 & 4) {
|
||||
co[2] = 0.0f;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge, BMEdge *oedge,
|
||||
int curpoint, int totpoint, const subdparams *params,
|
||||
BMEdge **newe, BMVert *vsta, BMVert *vend)
|
||||
{
|
||||
BMVert *ev;
|
||||
float percent, percent2 = 0.0f;
|
||||
|
||||
if (BMO_elem_flag_test(bm, edge, EDGE_PERCENT) && totpoint == 1)
|
||||
percent = BMO_slot_map_float_get(bm, params->op, "edgepercents", edge);
|
||||
else {
|
||||
percent = 1.0f / (float)(totpoint + 1-curpoint);
|
||||
percent2 = (float)(curpoint + 1) / (float)(totpoint + 1);
|
||||
|
||||
}
|
||||
|
||||
ev = bm_subdivide_edge_addvert(bm, edge, oedge, params, percent,
|
||||
percent2, newe, vsta, vend);
|
||||
return ev;
|
||||
}
|
||||
|
||||
static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const subdparams *params,
|
||||
BMVert *vsta, BMVert *vend)
|
||||
{
|
||||
BMEdge *eed = edge, *newe, temp = *edge;
|
||||
BMVert *v, ov1 = *edge->v1, ov2 = *edge->v2, *v1 = edge->v1, *v2 = edge->v2;
|
||||
int i, numcuts = params->numcuts;
|
||||
|
||||
temp.v1 = &ov1;
|
||||
temp.v2 = &ov2;
|
||||
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
v = subdivideedgenum(bm, eed, &temp, i, params->numcuts, params, &newe, vsta, vend);
|
||||
|
||||
BMO_elem_flag_enable(bm, v, SUBD_SPLIT);
|
||||
BMO_elem_flag_enable(bm, eed, SUBD_SPLIT);
|
||||
BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
|
||||
|
||||
BMO_elem_flag_enable(bm, v, ELE_SPLIT);
|
||||
BMO_elem_flag_enable(bm, eed, ELE_SPLIT);
|
||||
BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
|
||||
|
||||
BM_CHECK_ELEMENT(bm, v);
|
||||
if (v->e) BM_CHECK_ELEMENT(bm, v->e);
|
||||
if (v->e && v->e->l) BM_CHECK_ELEMENT(bm, v->e->l->f);
|
||||
}
|
||||
|
||||
alter_co(bm, v1, &temp, params, 0, &ov1, &ov2);
|
||||
alter_co(bm, v2, &temp, params, 1.0, &ov1, &ov2);
|
||||
}
|
||||
|
||||
/* note: the patterns are rotated as necassary to
|
||||
* match the input geometry. they're based on the
|
||||
* pre-split state of the face */
|
||||
|
||||
/*
|
||||
* v3---------v2
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* v4---v0---v1
|
||||
*/
|
||||
static void quad_1edge_split(BMesh *bm, BMFace *UNUSED(face),
|
||||
BMVert **verts, const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
int i, add, numcuts = params->numcuts;
|
||||
|
||||
/* if it's odd, the middle face is a quad, otherwise it's a triangl */
|
||||
if ((numcuts % 2) == 0) {
|
||||
add = 2;
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
if (i == numcuts / 2) {
|
||||
add -= 1;
|
||||
}
|
||||
connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
|
||||
}
|
||||
}
|
||||
else {
|
||||
add = 2;
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
|
||||
if (i == numcuts/2) {
|
||||
add -= 1;
|
||||
connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static SubDPattern quad_1edge = {
|
||||
{1, 0, 0, 0},
|
||||
quad_1edge_split,
|
||||
4,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* v6--------v5
|
||||
* | |
|
||||
* | |v4s
|
||||
* | |v3s
|
||||
* | s s |
|
||||
* v7-v0--v1-v2
|
||||
*/
|
||||
static void quad_2edge_split_path(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
|
||||
const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
int i, numcuts = params->numcuts;
|
||||
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
|
||||
}
|
||||
connect_smallest_face(bm, verts[numcuts * 2 + 3], verts[numcuts * 2 + 1], &nf);
|
||||
}
|
||||
|
||||
static SubDPattern quad_2edge_path = {
|
||||
{1, 1, 0, 0},
|
||||
quad_2edge_split_path,
|
||||
4,
|
||||
};
|
||||
|
||||
/*
|
||||
* v6--------v5
|
||||
* | |
|
||||
* | |v4s
|
||||
* | |v3s
|
||||
* | s s |
|
||||
* v7-v0--v1-v2
|
||||
*/
|
||||
static void quad_2edge_split_innervert(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
|
||||
const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
BMVert *v, *lastv;
|
||||
BMEdge *e, *ne, olde;
|
||||
int i, numcuts = params->numcuts;
|
||||
|
||||
lastv = verts[numcuts];
|
||||
|
||||
for (i = numcuts - 1; i >= 0; i--) {
|
||||
e = connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
|
||||
|
||||
olde = *e;
|
||||
v = bm_subdivide_edge_addvert(bm, e, &olde, params, 0.5f, 0.5f, &ne, e->v1, e->v2);
|
||||
|
||||
if (i != numcuts - 1) {
|
||||
connect_smallest_face(bm, lastv, v, &nf);
|
||||
}
|
||||
|
||||
lastv = v;
|
||||
}
|
||||
|
||||
connect_smallest_face(bm, lastv, verts[numcuts * 2 + 2], &nf);
|
||||
}
|
||||
|
||||
static SubDPattern quad_2edge_innervert = {
|
||||
{1, 1, 0, 0},
|
||||
quad_2edge_split_innervert,
|
||||
4,
|
||||
};
|
||||
|
||||
/*
|
||||
* v6--------v5
|
||||
* | |
|
||||
* | |v4s
|
||||
* | |v3s
|
||||
* | s s |
|
||||
* v7-v0--v1-v2
|
||||
*
|
||||
*/
|
||||
static void quad_2edge_split_fan(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
|
||||
const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
/* BMVert *v; */ /* UNUSED */
|
||||
/* BMVert *lastv = verts[2]; */ /* UNUSED */
|
||||
/* BMEdge *e, *ne; */ /* UNUSED */
|
||||
int i, numcuts = params->numcuts;
|
||||
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
connect_smallest_face(bm, verts[i], verts[numcuts * 2 + 2], &nf);
|
||||
connect_smallest_face(bm, verts[numcuts + (numcuts - i)], verts[numcuts * 2 + 2], &nf);
|
||||
}
|
||||
}
|
||||
|
||||
static SubDPattern quad_2edge_fan = {
|
||||
{1, 1, 0, 0},
|
||||
quad_2edge_split_fan,
|
||||
4,
|
||||
};
|
||||
|
||||
/*
|
||||
* s s
|
||||
* v8--v7--v6-v5
|
||||
* | |
|
||||
* | v4 s
|
||||
* | |
|
||||
* | v3 s
|
||||
* | s s |
|
||||
* v9-v0--v1-v2
|
||||
*/
|
||||
static void quad_3edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
|
||||
const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
int i, add = 0, numcuts = params->numcuts;
|
||||
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
if (i == numcuts / 2) {
|
||||
if (numcuts % 2 != 0) {
|
||||
connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
|
||||
}
|
||||
add = numcuts * 2 + 2;
|
||||
}
|
||||
connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
|
||||
}
|
||||
|
||||
for (i = 0; i < numcuts / 2 + 1; i++) {
|
||||
connect_smallest_face(bm, verts[i], verts[(numcuts - i) + numcuts * 2 + 1], &nf);
|
||||
}
|
||||
}
|
||||
|
||||
static SubDPattern quad_3edge = {
|
||||
{1, 1, 1, 0},
|
||||
quad_3edge_split,
|
||||
4,
|
||||
};
|
||||
|
||||
/*
|
||||
* v8--v7-v6--v5
|
||||
* | s |
|
||||
* |v9 s s|v4
|
||||
* first line | | last line
|
||||
* |v10s s s|v3
|
||||
* v11-v0--v1-v2
|
||||
*
|
||||
* it goes from bottom up
|
||||
*/
|
||||
static void quad_4edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
|
||||
const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
BMVert *v, *v1, *v2;
|
||||
BMEdge *e, *ne, temp;
|
||||
BMVert **lines;
|
||||
int numcuts = params->numcuts;
|
||||
int i, j, a, b, s = numcuts + 2 /* , totv = numcuts * 4 + 4 */;
|
||||
|
||||
lines = MEM_callocN(sizeof(BMVert *)*(numcuts + 2)*(numcuts + 2), "q_4edge_split");
|
||||
/* build a 2-dimensional array of verts,
|
||||
* containing every vert (and all new ones)
|
||||
* in the face */
|
||||
|
||||
/* first line */
|
||||
for (i = 0; i < numcuts + 2; i++) {
|
||||
lines[i] = verts[numcuts * 3 + 2 + (numcuts - i + 1)];
|
||||
}
|
||||
|
||||
/* last line */
|
||||
for (i = 0; i < numcuts + 2; i++) {
|
||||
lines[(s - 1) * s + i] = verts[numcuts + i];
|
||||
}
|
||||
|
||||
/* first and last members of middle lines */
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
a = i;
|
||||
b = numcuts + 1 + numcuts + 1 + (numcuts - i - 1);
|
||||
|
||||
e = connect_smallest_face(bm, verts[a], verts[b], &nf);
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
BMO_elem_flag_enable(bm, e, ELE_INNER);
|
||||
BMO_elem_flag_enable(bm, nf, ELE_INNER);
|
||||
|
||||
|
||||
v1 = lines[(i + 1)*s] = verts[a];
|
||||
v2 = lines[(i + 1)*s + s - 1] = verts[b];
|
||||
|
||||
temp = *e;
|
||||
for (a = 0; a < numcuts; a++) {
|
||||
v = subdivideedgenum(bm, e, &temp, a, numcuts, params, &ne,
|
||||
v1, v2);
|
||||
if (!v)
|
||||
bmesh_error();
|
||||
|
||||
BMO_elem_flag_enable(bm, ne, ELE_INNER);
|
||||
lines[(i + 1) * s + a + 1] = v;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1; i < numcuts + 2; i++) {
|
||||
for (j = 1; j < numcuts + 1; j++) {
|
||||
a = i * s + j;
|
||||
b = (i - 1) * s + j;
|
||||
e = connect_smallest_face(bm, lines[a], lines[b], &nf);
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
BMO_elem_flag_enable(bm, e, ELE_INNER);
|
||||
BMO_elem_flag_enable(bm, nf, ELE_INNER);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(lines);
|
||||
}
|
||||
|
||||
/*
|
||||
* v3
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* v4--v0--v1--v2
|
||||
* s s
|
||||
*/
|
||||
static void tri_1edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
|
||||
const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
int i, numcuts = params->numcuts;
|
||||
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
connect_smallest_face(bm, verts[i], verts[numcuts + 1], &nf);
|
||||
}
|
||||
}
|
||||
|
||||
static SubDPattern tri_1edge = {
|
||||
{1, 0, 0},
|
||||
tri_1edge_split,
|
||||
3,
|
||||
};
|
||||
|
||||
/* v5
|
||||
* / \
|
||||
* s v6/---\ v4 s
|
||||
* / \ / \
|
||||
* sv7/---v---\ v3 s
|
||||
* / \/ \/ \
|
||||
* v8--v0--v1--v2
|
||||
* s s
|
||||
*/
|
||||
static void tri_3edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
|
||||
const subdparams *params)
|
||||
{
|
||||
BMFace *nf;
|
||||
BMEdge *e, *ne, temp;
|
||||
BMVert ***lines, *v, ov1, ov2;
|
||||
void *stackarr[1];
|
||||
int i, j, a, b, numcuts = params->numcuts;
|
||||
|
||||
/* number of verts in each lin */
|
||||
lines = MEM_callocN(sizeof(void *)*(numcuts + 2), "triangle vert table");
|
||||
|
||||
lines[0] = (BMVert **) stackarr;
|
||||
lines[0][0] = verts[numcuts * 2 + 1];
|
||||
|
||||
lines[numcuts + 1] = MEM_callocN(sizeof(void *) * (numcuts + 2), "triangle vert table 2");
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
lines[numcuts + 1][i + 1] = verts[i];
|
||||
}
|
||||
lines[numcuts + 1][0] = verts[numcuts * 3 + 2];
|
||||
lines[numcuts + 1][numcuts + 1] = verts[numcuts];
|
||||
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
lines[i + 1] = MEM_callocN(sizeof(void *)*(2 + i), "triangle vert table row");
|
||||
a = numcuts * 2 + 2 + i;
|
||||
b = numcuts + numcuts - i;
|
||||
e = connect_smallest_face(bm, verts[a], verts[b], &nf);
|
||||
if (!e) goto cleanup;
|
||||
|
||||
BMO_elem_flag_enable(bm, e, ELE_INNER);
|
||||
BMO_elem_flag_enable(bm, nf, ELE_INNER);
|
||||
|
||||
lines[i + 1][0] = verts[a];
|
||||
lines[i + 1][i + 1] = verts[b];
|
||||
|
||||
temp = *e;
|
||||
ov1 = *verts[a];
|
||||
ov2 = *verts[b];
|
||||
temp.v1 = &ov1;
|
||||
temp.v2 = &ov2;
|
||||
for (j = 0; j < i; j++) {
|
||||
v = subdivideedgenum(bm, e, &temp, j, i, params, &ne,
|
||||
verts[a], verts[b]);
|
||||
lines[i + 1][j + 1] = v;
|
||||
|
||||
BMO_elem_flag_enable(bm, ne, ELE_INNER);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* v5
|
||||
* / \
|
||||
* s v6/---\ v4 s
|
||||
* / \ / \
|
||||
* sv7/---v---\ v3 s
|
||||
* / \/ \/ \
|
||||
* v8--v0--v1--v2
|
||||
* s s
|
||||
*/
|
||||
for (i = 1; i < numcuts + 1; i++) {
|
||||
for (j = 0; j < i; j++) {
|
||||
e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &nf);
|
||||
|
||||
BMO_elem_flag_enable(bm, e, ELE_INNER);
|
||||
BMO_elem_flag_enable(bm, nf, ELE_INNER);
|
||||
|
||||
e = connect_smallest_face(bm, lines[i][j + 1], lines[i + 1][j + 1], &nf);
|
||||
|
||||
BMO_elem_flag_enable(bm, e, ELE_INNER);
|
||||
BMO_elem_flag_enable(bm, nf, ELE_INNER);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
for (i = 1; i < numcuts + 2; i++) {
|
||||
if (lines[i]) MEM_freeN(lines[i]);
|
||||
}
|
||||
|
||||
MEM_freeN(lines);
|
||||
}
|
||||
|
||||
static SubDPattern tri_3edge = {
|
||||
{1, 1, 1},
|
||||
tri_3edge_subdivide,
|
||||
3,
|
||||
};
|
||||
|
||||
|
||||
static SubDPattern quad_4edge = {
|
||||
{1, 1, 1, 1},
|
||||
quad_4edge_subdivide,
|
||||
4,
|
||||
};
|
||||
|
||||
static SubDPattern *patterns[] = {
|
||||
NULL, //quad single edge pattern is inserted here
|
||||
NULL, //quad corner vert pattern is inserted here
|
||||
NULL, //tri single edge pattern is inserted here
|
||||
NULL,
|
||||
&quad_3edge,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define PLEN (sizeof(patterns) / sizeof(void *))
|
||||
|
||||
typedef struct subd_facedata {
|
||||
BMVert *start; SubDPattern *pat;
|
||||
int totedgesel; //only used if pat was NULL, e.g. no pattern was found
|
||||
BMFace *face;
|
||||
} subd_facedata;
|
||||
|
||||
void esubdivide_exec(BMesh *bmesh, BMOperator *op)
|
||||
{
|
||||
BMOpSlot *einput;
|
||||
SubDPattern *pat;
|
||||
subdparams params;
|
||||
subd_facedata *facedata = NULL;
|
||||
BMIter viter, fiter, liter;
|
||||
BMVert *v, **verts = NULL;
|
||||
BMEdge *edge, **edges = NULL;
|
||||
BMLoop *nl, *l, **splits = NULL, **loops = NULL;
|
||||
BMFace *face;
|
||||
BLI_array_declare(splits);
|
||||
BLI_array_declare(loops);
|
||||
BLI_array_declare(facedata);
|
||||
BLI_array_declare(edges);
|
||||
BLI_array_declare(verts);
|
||||
float smooth, fractal;
|
||||
int beauty, cornertype, singleedge, gridfill;
|
||||
int skey, seed, i, j, matched, a, b, numcuts, totesel;
|
||||
|
||||
BMO_slot_buffer_flag_enable(bmesh, op, "edges", SUBD_SPLIT, BM_EDGE);
|
||||
|
||||
numcuts = BMO_slot_int_get(op, "numcuts");
|
||||
seed = BMO_slot_int_get(op, "seed");
|
||||
smooth = BMO_slot_float_get(op, "smooth");
|
||||
fractal = BMO_slot_float_get(op, "fractal");
|
||||
beauty = BMO_slot_int_get(op, "beauty");
|
||||
cornertype = BMO_slot_int_get(op, "quadcornertype");
|
||||
singleedge = BMO_slot_int_get(op, "singleedge");
|
||||
gridfill = BMO_slot_int_get(op, "gridfill");
|
||||
|
||||
BLI_srandom(seed);
|
||||
|
||||
patterns[1] = NULL;
|
||||
//straight cut is patterns[1] == NULL
|
||||
switch (cornertype) {
|
||||
case SUBD_PATH:
|
||||
patterns[1] = &quad_2edge_path;
|
||||
break;
|
||||
case SUBD_INNERVERT:
|
||||
patterns[1] = &quad_2edge_innervert;
|
||||
break;
|
||||
case SUBD_FAN:
|
||||
patterns[1] = &quad_2edge_fan;
|
||||
break;
|
||||
}
|
||||
|
||||
if (singleedge) {
|
||||
patterns[0] = &quad_1edge;
|
||||
patterns[2] = &tri_1edge;
|
||||
}
|
||||
else {
|
||||
patterns[0] = NULL;
|
||||
patterns[2] = NULL;
|
||||
}
|
||||
|
||||
if (gridfill) {
|
||||
patterns[3] = &quad_4edge;
|
||||
patterns[5] = &tri_3edge;
|
||||
}
|
||||
else {
|
||||
patterns[3] = NULL;
|
||||
patterns[5] = NULL;
|
||||
}
|
||||
|
||||
/* add a temporary shapekey layer to store displacements on current geometr */
|
||||
BM_data_layer_add(bmesh, &bmesh->vdata, CD_SHAPEKEY);
|
||||
skey = CustomData_number_of_layers(&bmesh->vdata, CD_SHAPEKEY) - 1;
|
||||
|
||||
BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
|
||||
float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
|
||||
copy_v3_v3(co, v->co);
|
||||
}
|
||||
|
||||
/* first go through and tag edge */
|
||||
BMO_slot_from_flag(bmesh, op, "edges",
|
||||
SUBD_SPLIT, BM_EDGE);
|
||||
|
||||
params.numcuts = numcuts;
|
||||
params.op = op;
|
||||
params.smooth = smooth;
|
||||
params.seed = seed;
|
||||
params.fractal = fractal;
|
||||
params.beauty = beauty;
|
||||
params.origkey = skey;
|
||||
params.off[0] = (float)BLI_drand() * 200.0f;
|
||||
params.off[1] = (float)BLI_drand() * 200.0f;
|
||||
params.off[2] = (float)BLI_drand() * 200.0f;
|
||||
|
||||
BMO_slot_map_to_flag(bmesh, op, "custompatterns",
|
||||
FACE_CUSTOMFILL);
|
||||
|
||||
BMO_slot_map_to_flag(bmesh, op, "edgepercents",
|
||||
EDGE_PERCENT);
|
||||
|
||||
for (face = BM_iter_new(&fiter, bmesh, BM_FACES_OF_MESH, NULL);
|
||||
face;
|
||||
face = BM_iter_step(&fiter))
|
||||
{
|
||||
BMEdge *e1 = NULL, *e2 = NULL;
|
||||
float vec1[3], vec2[3];
|
||||
|
||||
/* figure out which pattern to us */
|
||||
|
||||
BLI_array_empty(edges);
|
||||
BLI_array_empty(verts);
|
||||
matched = 0;
|
||||
|
||||
i = 0;
|
||||
totesel = 0;
|
||||
for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
|
||||
BLI_array_growone(edges);
|
||||
BLI_array_growone(verts);
|
||||
edges[i] = nl->e;
|
||||
verts[i] = nl->v;
|
||||
|
||||
if (BMO_elem_flag_test(bmesh, edges[i], SUBD_SPLIT)) {
|
||||
if (!e1) e1 = edges[i];
|
||||
else e2 = edges[i];
|
||||
|
||||
totesel++;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/* make sure the two edges have a valid angle to each othe */
|
||||
if (totesel == 2 && BM_edge_share_vert(e1, e2)) {
|
||||
float angle;
|
||||
|
||||
sub_v3_v3v3(vec1, e1->v2->co, e1->v1->co);
|
||||
sub_v3_v3v3(vec2, e2->v2->co, e2->v1->co);
|
||||
normalize_v3(vec1);
|
||||
normalize_v3(vec2);
|
||||
|
||||
angle = dot_v3v3(vec1, vec2);
|
||||
angle = fabsf(angle);
|
||||
if (fabsf(angle - 1.0f) < 0.01f) {
|
||||
totesel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (BMO_elem_flag_test(bmesh, face, FACE_CUSTOMFILL)) {
|
||||
pat = BMO_slot_map_data_get(bmesh, op,
|
||||
"custompatterns", face);
|
||||
for (i = 0; i < pat->len; i++) {
|
||||
matched = 1;
|
||||
for (j = 0; j < pat->len; j++) {
|
||||
a = (j + i) % pat->len;
|
||||
if ((!!BMO_elem_flag_test(bmesh, edges[a], SUBD_SPLIT)) != (!!pat->seledges[j])) {
|
||||
matched = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
BLI_array_growone(facedata);
|
||||
b = BLI_array_count(facedata) - 1;
|
||||
facedata[b].pat = pat;
|
||||
facedata[b].start = verts[i];
|
||||
facedata[b].face = face;
|
||||
facedata[b].totedgesel = totesel;
|
||||
BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* obvously don't test for other patterns matchin */
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < PLEN; i++) {
|
||||
pat = patterns[i];
|
||||
if (!pat) continue;
|
||||
|
||||
if (pat->len == face->len) {
|
||||
for (a = 0; a < pat->len; a++) {
|
||||
matched = 1;
|
||||
for (b = 0; b < pat->len; b++) {
|
||||
j = (b + a) % pat->len;
|
||||
if ((!!BMO_elem_flag_test(bmesh, edges[j], SUBD_SPLIT)) != (!!pat->seledges[b])) {
|
||||
matched = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
BLI_array_growone(facedata);
|
||||
j = BLI_array_count(facedata) - 1;
|
||||
|
||||
BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
|
||||
|
||||
facedata[j].pat = pat;
|
||||
facedata[j].start = verts[a];
|
||||
facedata[j].face = face;
|
||||
facedata[j].totedgesel = totesel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!matched && totesel) {
|
||||
BLI_array_growone(facedata);
|
||||
j = BLI_array_count(facedata) - 1;
|
||||
|
||||
BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
|
||||
facedata[j].totedgesel = totesel;
|
||||
facedata[j].face = face;
|
||||
}
|
||||
}
|
||||
|
||||
einput = BMO_slot_get(op, "edges");
|
||||
|
||||
/* go through and split edge */
|
||||
for (i = 0; i < einput->len; i++) {
|
||||
edge = ((BMEdge **)einput->data.p)[i];
|
||||
bm_subdivide_multicut(bmesh, edge, ¶ms, edge->v1, edge->v2);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (i = 0; i < BLI_array_count(facedata); i++) {
|
||||
face = facedata[i].face;
|
||||
|
||||
/* figure out which pattern to us */
|
||||
BLI_array_empty(verts);
|
||||
|
||||
pat = facedata[i].pat;
|
||||
|
||||
if (!pat && facedata[i].totedgesel == 2) {
|
||||
int vlen;
|
||||
|
||||
/* ok, no pattern. we still may be able to do something */
|
||||
BLI_array_empty(loops);
|
||||
BLI_array_empty(splits);
|
||||
|
||||
/* for case of two edges, connecting them shouldn't be too har */
|
||||
BM_ITER(l, &liter, bmesh, BM_LOOPS_OF_FACE, face) {
|
||||
BLI_array_growone(loops);
|
||||
loops[BLI_array_count(loops) - 1] = l;
|
||||
}
|
||||
|
||||
vlen = BLI_array_count(loops);
|
||||
|
||||
/* find the boundary of one of the split edge */
|
||||
for (a = 1; a < vlen; a++) {
|
||||
if (!BMO_elem_flag_test(bmesh, loops[a - 1]->v, ELE_INNER) &&
|
||||
BMO_elem_flag_test(bmesh, loops[a]->v, ELE_INNER))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (BMO_elem_flag_test(bmesh, loops[(a + numcuts + 1) % vlen]->v, ELE_INNER)) {
|
||||
b = (a + numcuts + 1) % vlen;
|
||||
}
|
||||
else {
|
||||
/* find the boundary of the other edge. */
|
||||
for (j = 0; j < vlen; j++) {
|
||||
b = (j + a + numcuts + 1) % vlen;
|
||||
if (!BMO_elem_flag_test(bmesh, loops[b == 0 ? vlen - 1 : b - 1]->v, ELE_INNER) &&
|
||||
BMO_elem_flag_test(bmesh, loops[b]->v, ELE_INNER))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b += numcuts - 1;
|
||||
|
||||
for (j = 0; j < numcuts; j++) {
|
||||
BLI_array_growone(splits);
|
||||
splits[BLI_array_count(splits) - 1] = loops[a];
|
||||
|
||||
BLI_array_growone(splits);
|
||||
splits[BLI_array_count(splits) - 1] = loops[b];
|
||||
|
||||
b = (b - 1) % vlen;
|
||||
a = (a + 1) % vlen;
|
||||
}
|
||||
|
||||
//BM_face_legal_splits(bmesh, face, splits, BLI_array_count(splits)/2);
|
||||
|
||||
for (j = 0; j < BLI_array_count(splits) / 2; j++) {
|
||||
if (splits[j * 2]) {
|
||||
/* BMFace *nf = */ /* UNUSED */
|
||||
BM_face_split(bmesh, face, splits[j * 2]->v, splits[j * 2 + 1]->v, &nl, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (!pat) {
|
||||
continue;
|
||||
}
|
||||
|
||||
j = a = 0;
|
||||
for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face);
|
||||
nl;
|
||||
nl = BM_iter_step(&liter))
|
||||
{
|
||||
if (nl->v == facedata[i].start) {
|
||||
a = j + 1;
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
for (j = 0; j < face->len; j++) {
|
||||
BLI_array_growone(verts);
|
||||
}
|
||||
|
||||
j = 0;
|
||||
for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
|
||||
b = (j - a + face->len) % face->len;
|
||||
verts[b] = nl->v;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
BM_CHECK_ELEMENT(bmesh, face);
|
||||
pat->connectexec(bmesh, face, verts, ¶ms);
|
||||
}
|
||||
|
||||
/* copy original-geometry displacements to current coordinate */
|
||||
BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
|
||||
float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
|
||||
copy_v3_v3(v->co, co);
|
||||
}
|
||||
|
||||
BM_data_layer_free_n(bmesh, &bmesh->vdata, CD_SHAPEKEY, skey);
|
||||
|
||||
if (facedata) BLI_array_free(facedata);
|
||||
if (edges) BLI_array_free(edges);
|
||||
if (verts) BLI_array_free(verts);
|
||||
BLI_array_free(splits);
|
||||
BLI_array_free(loops);
|
||||
|
||||
BMO_slot_from_flag(bmesh, op, "outinner",
|
||||
ELE_INNER, BM_ALL);
|
||||
BMO_slot_from_flag(bmesh, op, "outsplit",
|
||||
ELE_SPLIT, BM_ALL);
|
||||
|
||||
BMO_slot_from_flag(bmesh, op, "geomout",
|
||||
ELE_INNER|ELE_SPLIT|SUBD_SPLIT, BM_ALL);
|
||||
}
|
||||
|
||||
/* editmesh-emulating functio */
|
||||
void BM_mesh_esubdivideflag(Object *UNUSED(obedit), BMesh *bm, int flag, float smooth,
|
||||
float fractal, int beauty, int numcuts,
|
||||
int seltype, int cornertype, int singleedge,
|
||||
int gridfill, int seed)
|
||||
{
|
||||
BMOperator op;
|
||||
|
||||
BMO_op_initf(bm, &op, "esubd edges=%he smooth=%f fractal=%f "
|
||||
"beauty=%d numcuts=%d quadcornertype=%d singleedge=%d "
|
||||
"gridfill=%d seed=%d",
|
||||
flag, smooth, fractal, beauty, numcuts,
|
||||
cornertype, singleedge, gridfill, seed);
|
||||
|
||||
BMO_op_exec(bm, &op);
|
||||
|
||||
if (seltype == SUBDIV_SELECT_INNER) {
|
||||
BMOIter iter;
|
||||
BMHeader *ele;
|
||||
// int i;
|
||||
|
||||
ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
|
||||
for ( ; ele; ele = BMO_iter_step(&iter)) {
|
||||
BM_elem_select_set(bm, ele, TRUE);
|
||||
}
|
||||
}
|
||||
else if (seltype == SUBDIV_SELECT_LOOPCUT) {
|
||||
BMOIter iter;
|
||||
BMHeader *ele;
|
||||
// int i;
|
||||
|
||||
/* deselect input */
|
||||
BM_mesh_elem_flag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT);
|
||||
|
||||
ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
|
||||
for ( ; ele; ele = BMO_iter_step(&iter)) {
|
||||
BM_elem_select_set(bm, ele, TRUE);
|
||||
|
||||
if (ele->htype == BM_VERT) {
|
||||
BMEdge *e;
|
||||
BMIter eiter;
|
||||
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, ele) {
|
||||
if (!BM_elem_flag_test(e, BM_ELEM_SELECT) &&
|
||||
BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
|
||||
BM_elem_flag_test(e->v2, BM_ELEM_SELECT))
|
||||
{
|
||||
BM_elem_select_set(bm, e, TRUE);
|
||||
bm->totedgesel += 1;
|
||||
}
|
||||
else if (BM_elem_flag_test(e, BM_ELEM_SELECT) &&
|
||||
(!BM_elem_flag_test(e->v1, BM_ELEM_SELECT) ||
|
||||
!BM_elem_flag_test(e->v2, BM_ELEM_SELECT)))
|
||||
{
|
||||
BM_elem_select_set(bm, e, FALSE);
|
||||
bm->totedgesel -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BMO_op_finish(bm, &op);
|
||||
}
|
||||
|
||||
void esplit_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter siter;
|
||||
BMEdge *e;
|
||||
subdparams params;
|
||||
int skey;
|
||||
|
||||
params.numcuts = BMO_slot_get(op, "numcuts")->data.i;
|
||||
params.op = op;
|
||||
|
||||
BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY);
|
||||
skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1;
|
||||
|
||||
params.origkey = skey;
|
||||
|
||||
/* go through and split edge */
|
||||
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
|
||||
bm_subdivide_multicut(bm, e, ¶ms, e->v1, e->v2);
|
||||
}
|
||||
|
||||
BMO_slot_from_flag(bm, op, "outsplit", ELE_SPLIT, BM_ALL);
|
||||
|
||||
BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey);
|
||||
}
|
||||
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
@@ -0,0 +1,1297 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Joseph Eagar.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_heap.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "bmesh_operators_private.h" /* own include */
|
||||
|
||||
/*
|
||||
* UTILS.C
|
||||
*
|
||||
* utility bmesh operators, e.g. transform,
|
||||
* translate, rotate, scale, etc.
|
||||
*
|
||||
*/
|
||||
|
||||
void bmesh_makevert_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
float vec[3];
|
||||
|
||||
BMO_slot_vec_get(op, "co", vec);
|
||||
|
||||
BMO_elem_flag_enable(bm, BM_vert_create(bm, vec, NULL), 1);
|
||||
BMO_slot_from_flag(bm, op, "newvertout", 1, BM_VERT);
|
||||
}
|
||||
|
||||
void bmesh_transform_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter iter;
|
||||
BMVert *v;
|
||||
float mat[4][4];
|
||||
|
||||
BMO_slot_mat4_get(op, "mat", mat);
|
||||
|
||||
BMO_ITER(v, &iter, bm, op, "verts", BM_VERT) {
|
||||
mul_m4_v3(mat, v->co);
|
||||
}
|
||||
}
|
||||
|
||||
void bmesh_translate_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
float mat[4][4], vec[3];
|
||||
|
||||
BMO_slot_vec_get(op, "vec", vec);
|
||||
|
||||
unit_m4(mat);
|
||||
copy_v3_v3(mat[3], vec);
|
||||
|
||||
BMO_op_callf(bm, "transform mat=%m4 verts=%s", mat, op, "verts");
|
||||
}
|
||||
|
||||
void bmesh_scale_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
float mat[3][3], vec[3];
|
||||
|
||||
BMO_slot_vec_get(op, "vec", vec);
|
||||
|
||||
unit_m3(mat);
|
||||
mat[0][0] = vec[0];
|
||||
mat[1][1] = vec[1];
|
||||
mat[2][2] = vec[2];
|
||||
|
||||
BMO_op_callf(bm, "transform mat=%m3 verts=%s", mat, op, "verts");
|
||||
}
|
||||
|
||||
void bmesh_rotate_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
float vec[3];
|
||||
|
||||
BMO_slot_vec_get(op, "cent", vec);
|
||||
|
||||
/* there has to be a proper matrix way to do this, but
|
||||
* this is how editmesh did it and I'm too tired to think
|
||||
* through the math right now. */
|
||||
mul_v3_fl(vec, -1.0f);
|
||||
BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
|
||||
|
||||
BMO_op_callf(bm, "transform mat=%s verts=%s", op, "mat", op, "verts");
|
||||
|
||||
mul_v3_fl(vec, -1.0f);
|
||||
BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
|
||||
}
|
||||
|
||||
void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter siter;
|
||||
BMFace *f;
|
||||
|
||||
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
|
||||
BM_face_normal_flip(bm, f);
|
||||
}
|
||||
}
|
||||
|
||||
void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter siter;
|
||||
BMEdge *e, *e2;
|
||||
int ccw = BMO_slot_int_get(op, "ccw");
|
||||
|
||||
BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
|
||||
if (!(e2 = BM_edge_rotate(bm, e, ccw))) {
|
||||
BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge");
|
||||
return;
|
||||
}
|
||||
|
||||
BMO_elem_flag_enable(bm, e2, 1);
|
||||
}
|
||||
|
||||
BMO_slot_from_flag(bm, op, "edgeout", 1, BM_EDGE);
|
||||
}
|
||||
|
||||
#define SEL_FLAG 1
|
||||
#define SEL_ORIG 2
|
||||
|
||||
static void bmesh_regionextend_extend(BMesh *bm, BMOperator *op, int usefaces)
|
||||
{
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMIter eiter;
|
||||
BMOIter siter;
|
||||
|
||||
if (!usefaces) {
|
||||
BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
|
||||
break;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
BMO_elem_flag_enable(bm, e, SEL_FLAG);
|
||||
BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
BMIter liter, fiter;
|
||||
BMFace *f, *f2;
|
||||
BMLoop *l;
|
||||
|
||||
BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
|
||||
if (!BMO_elem_flag_test(bm, f2, SEL_ORIG))
|
||||
BMO_elem_flag_enable(bm, f2, SEL_FLAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bmesh_regionextend_constrict(BMesh *bm, BMOperator *op, int usefaces)
|
||||
{
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMIter eiter;
|
||||
BMOIter siter;
|
||||
|
||||
if (!usefaces) {
|
||||
BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
|
||||
break;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
BMO_elem_flag_enable(bm, v, SEL_FLAG);
|
||||
|
||||
BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
|
||||
BMO_elem_flag_enable(bm, e, SEL_FLAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
BMIter liter, fiter;
|
||||
BMFace *f, *f2;
|
||||
BMLoop *l;
|
||||
|
||||
BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
|
||||
if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) {
|
||||
BMO_elem_flag_enable(bm, f, SEL_FLAG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bmesh_regionextend_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
int usefaces = BMO_slot_int_get(op, "usefaces");
|
||||
int constrict = BMO_slot_int_get(op, "constrict");
|
||||
|
||||
BMO_slot_buffer_flag_enable(bm, op, "geom", SEL_ORIG, BM_ALL);
|
||||
|
||||
if (constrict)
|
||||
bmesh_regionextend_constrict(bm, op, usefaces);
|
||||
else
|
||||
bmesh_regionextend_extend(bm, op, usefaces);
|
||||
|
||||
BMO_slot_from_flag(bm, op, "geomout", SEL_FLAG, BM_ALL);
|
||||
}
|
||||
|
||||
/********* righthand faces implementation ****** */
|
||||
|
||||
#define FACE_VIS 1
|
||||
#define FACE_FLAG 2
|
||||
#define FACE_MARK 4
|
||||
#define FACE_FLIP 8
|
||||
|
||||
/* NOTE: these are the original righthandfaces comment in editmesh_mods.c,
|
||||
* copied here for reference. */
|
||||
|
||||
/* based at a select-connected to witness loose objects */
|
||||
|
||||
/* count per edge the amount of faces
|
||||
* find the ultimate left, front, upper face (not manhattan dist!!)
|
||||
* also evaluate both triangle cases in quad, since these can be non-flat
|
||||
*
|
||||
* put normal to the outside, and set the first direction flags in edges
|
||||
*
|
||||
* then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces
|
||||
* this is in fact the 'select connected'
|
||||
*
|
||||
* in case (selected) faces were not done: start over with 'find the ultimate ...' */
|
||||
|
||||
/* NOTE: this function uses recursion, which is a little unusual for a bmop
|
||||
* function, but acceptable I think. */
|
||||
|
||||
/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */
|
||||
|
||||
void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMIter liter, liter2;
|
||||
BMOIter siter;
|
||||
BMFace *f, *startf, **fstack = NULL;
|
||||
BLI_array_declare(fstack);
|
||||
BMLoop *l, *l2;
|
||||
float maxx, cent[3];
|
||||
int i, maxi, flagflip = BMO_slot_int_get(op, "doflip");
|
||||
|
||||
startf = NULL;
|
||||
maxx = -1.0e10;
|
||||
|
||||
BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_FLAG, BM_FACE);
|
||||
|
||||
/* find a starting face */
|
||||
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
|
||||
|
||||
/* clear dirty flag */
|
||||
BM_elem_flag_disable(f, BM_ELEM_TAG);
|
||||
|
||||
if (BMO_elem_flag_test(bm, f, FACE_VIS))
|
||||
continue;
|
||||
|
||||
if (!startf) startf = f;
|
||||
|
||||
BM_face_center_bounds_calc(bm, f, cent);
|
||||
|
||||
cent[0] = cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2];
|
||||
if (cent[0] > maxx) {
|
||||
maxx = cent[0];
|
||||
startf = f;
|
||||
}
|
||||
}
|
||||
|
||||
if (!startf) return;
|
||||
|
||||
BM_face_center_bounds_calc(bm, startf, cent);
|
||||
|
||||
/* make sure the starting face has the correct winding */
|
||||
if (dot_v3v3(cent, startf->no) < 0.0f) {
|
||||
BM_face_normal_flip(bm, startf);
|
||||
BMO_elem_flag_toggle(bm, startf, FACE_FLIP);
|
||||
|
||||
if (flagflip)
|
||||
BM_elem_flag_toggle(startf, BM_ELEM_TAG);
|
||||
}
|
||||
|
||||
/* now that we've found our starting face, make all connected faces
|
||||
* have the same winding. this is done recursively, using a manual
|
||||
* stack (if we use simple function recursion, we'd end up overloading
|
||||
* the stack on large meshes). */
|
||||
|
||||
BLI_array_growone(fstack);
|
||||
fstack[0] = startf;
|
||||
BMO_elem_flag_enable(bm, startf, FACE_VIS);
|
||||
|
||||
i = 0;
|
||||
maxi = 1;
|
||||
while (i >= 0) {
|
||||
f = fstack[i];
|
||||
i--;
|
||||
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_LOOP, l) {
|
||||
if (!BMO_elem_flag_test(bm, l2->f, FACE_FLAG) || l2 == l)
|
||||
continue;
|
||||
|
||||
if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) {
|
||||
BMO_elem_flag_enable(bm, l2->f, FACE_VIS);
|
||||
i++;
|
||||
|
||||
if (l2->v == l->v) {
|
||||
BM_face_normal_flip(bm, l2->f);
|
||||
|
||||
BMO_elem_flag_toggle(bm, l2->f, FACE_FLIP);
|
||||
if (flagflip)
|
||||
BM_elem_flag_toggle(l2->f, BM_ELEM_TAG);
|
||||
}
|
||||
else if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
|
||||
if (flagflip) {
|
||||
BM_elem_flag_disable(l->f, BM_ELEM_TAG);
|
||||
BM_elem_flag_disable(l2->f, BM_ELEM_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == maxi) {
|
||||
BLI_array_growone(fstack);
|
||||
maxi++;
|
||||
}
|
||||
|
||||
fstack[i] = l2->f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_array_free(fstack);
|
||||
|
||||
/* check if we have faces yet to do. if so, recurse */
|
||||
BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
|
||||
if (!BMO_elem_flag_test(bm, f, FACE_VIS)) {
|
||||
bmesh_righthandfaces_exec(bm, op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter siter;
|
||||
BMIter iter;
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BLI_array_declare(cos);
|
||||
float (*cos)[3] = NULL;
|
||||
float *co, *co2, clipdist = BMO_slot_float_get(op, "clipdist");
|
||||
int i, j, clipx, clipy, clipz;
|
||||
|
||||
clipx = BMO_slot_int_get(op, "mirror_clip_x");
|
||||
clipy = BMO_slot_int_get(op, "mirror_clip_y");
|
||||
clipz = BMO_slot_int_get(op, "mirror_clip_z");
|
||||
|
||||
i = 0;
|
||||
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
|
||||
BLI_array_growone(cos);
|
||||
co = cos[i];
|
||||
|
||||
j = 0;
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
|
||||
co2 = BM_edge_other_vert(e, v)->co;
|
||||
add_v3_v3v3(co, co, co2);
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if (!j) {
|
||||
copy_v3_v3(co, v->co);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
mul_v3_fl(co, 1.0f / (float)j);
|
||||
mid_v3_v3v3(co, co, v->co);
|
||||
|
||||
if (clipx && fabsf(v->co[0]) <= clipdist)
|
||||
co[0] = 0.0f;
|
||||
if (clipy && fabsf(v->co[1]) <= clipdist)
|
||||
co[1] = 0.0f;
|
||||
if (clipz && fabsf(v->co[2]) <= clipdist)
|
||||
co[2] = 0.0f;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
|
||||
copy_v3_v3(v->co, cos[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
BLI_array_free(cos);
|
||||
}
|
||||
|
||||
/*
|
||||
* compute the perimeter of an ngon
|
||||
*
|
||||
* NOTE: This should probably go to bmesh_polygon.c
|
||||
*/
|
||||
static float ngon_perimeter(BMesh *bm, BMFace *f)
|
||||
{
|
||||
BMIter liter;
|
||||
BMLoop *l;
|
||||
int num_verts = 0;
|
||||
float v[3], sv[3];
|
||||
float perimeter = 0.0f;
|
||||
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
if (num_verts == 0) {
|
||||
copy_v3_v3(v, l->v->co);
|
||||
copy_v3_v3(sv, l->v->co);
|
||||
}
|
||||
else {
|
||||
perimeter += len_v3v3(v, l->v->co);
|
||||
copy_v3_v3(v, l->v->co);
|
||||
}
|
||||
num_verts++;
|
||||
}
|
||||
|
||||
perimeter += len_v3v3(v, sv);
|
||||
|
||||
return perimeter;
|
||||
}
|
||||
|
||||
/*
|
||||
* compute the fake surface of an ngon
|
||||
* This is done by decomposing the ngon into triangles who share the centroid of the ngon
|
||||
* while this method is far from being exact, it should garantee an invariance.
|
||||
*
|
||||
* NOTE: This should probably go to bmesh_polygon.c
|
||||
*/
|
||||
static float ngon_fake_area(BMesh *bm, BMFace *f)
|
||||
{
|
||||
BMIter liter;
|
||||
BMLoop *l;
|
||||
int num_verts = 0;
|
||||
float v[3], sv[3], c[3];
|
||||
float area = 0.0f;
|
||||
|
||||
BM_face_center_mean_calc(bm, f, c);
|
||||
|
||||
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
if (num_verts == 0) {
|
||||
copy_v3_v3(v, l->v->co);
|
||||
copy_v3_v3(sv, l->v->co);
|
||||
num_verts++;
|
||||
}
|
||||
else {
|
||||
area += area_tri_v3(v, c, l->v->co);
|
||||
copy_v3_v3(v, l->v->co);
|
||||
num_verts++;
|
||||
}
|
||||
}
|
||||
|
||||
area += area_tri_v3(v, c, sv);
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
/*
|
||||
* extra face data (computed data)
|
||||
*/
|
||||
typedef struct tmp_face_ext {
|
||||
BMFace *f; /* the face */
|
||||
float c[3]; /* center */
|
||||
union {
|
||||
float area; /* area */
|
||||
float perim; /* perimeter */
|
||||
float d; /* 4th component of plane (the first three being the normal) */
|
||||
struct Image *t; /* image pointer */
|
||||
};
|
||||
} tmp_face_ext;
|
||||
|
||||
/*
|
||||
* Select similar faces, the choices are in the enum in source/blender/bmesh/bmesh_operators.h
|
||||
* We select either similar faces based on material, image, area, perimeter, normal, or the coplanar faces
|
||||
*/
|
||||
void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMIter fm_iter;
|
||||
BMFace *fs, *fm;
|
||||
BMOIter fs_iter;
|
||||
int num_sels = 0, num_total = 0, i = 0, idx = 0;
|
||||
float angle = 0.0f;
|
||||
tmp_face_ext *f_ext = NULL;
|
||||
int *indices = NULL;
|
||||
float t_no[3]; /* temporary normal */
|
||||
int type = BMO_slot_int_get(op, "type");
|
||||
float thresh = BMO_slot_float_get(op, "thresh");
|
||||
|
||||
num_total = BM_mesh_elem_count(bm, BM_FACE);
|
||||
|
||||
/*
|
||||
** The first thing to do is to iterate through all the the selected items and mark them since
|
||||
** they will be in the selection anyway.
|
||||
** This will increase performance, (especially when the number of originaly selected faces is high)
|
||||
** so the overall complexity will be less than $O(mn)$ where is the total number of selected faces,
|
||||
** and n is the total number of faces
|
||||
*/
|
||||
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
|
||||
if (!BMO_elem_flag_test(bm, fs, FACE_MARK)) { /* is this really needed ? */
|
||||
BMO_elem_flag_enable(bm, fs, FACE_MARK);
|
||||
num_sels++;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate memory for the selected faces indices and for all temporary faces */
|
||||
indices = (int *)MEM_callocN(sizeof(int) * num_sels, "face indices util.c");
|
||||
f_ext = (tmp_face_ext *)MEM_callocN(sizeof(tmp_face_ext) * num_total, "f_ext util.c");
|
||||
|
||||
/* loop through all the faces and fill the faces/indices structure */
|
||||
BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
f_ext[i].f = fm;
|
||||
if (BMO_elem_flag_test(bm, fm, FACE_MARK)) {
|
||||
indices[idx] = i;
|
||||
idx++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Save us some computation burden: In case of perimeter/area/coplanar selection we compute
|
||||
** only once.
|
||||
*/
|
||||
if (type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE) {
|
||||
for (i = 0; i < num_total; i++) {
|
||||
switch (type) {
|
||||
case SIMFACE_PERIMETER:
|
||||
/* set the perimeter */
|
||||
f_ext[i].perim = ngon_perimeter(bm, f_ext[i].f);
|
||||
break;
|
||||
|
||||
case SIMFACE_COPLANAR:
|
||||
/* compute the center of the polygon */
|
||||
BM_face_center_mean_calc(bm, f_ext[i].f, f_ext[i].c);
|
||||
|
||||
/* normalize the polygon normal */
|
||||
copy_v3_v3(t_no, f_ext[i].f->no);
|
||||
normalize_v3(t_no);
|
||||
|
||||
/* compute the plane distance */
|
||||
f_ext[i].d = dot_v3v3(t_no, f_ext[i].c);
|
||||
break;
|
||||
|
||||
case SIMFACE_AREA:
|
||||
f_ext[i].area = ngon_fake_area(bm, f_ext[i].f);
|
||||
break;
|
||||
|
||||
case SIMFACE_IMAGE:
|
||||
f_ext[i].t = NULL;
|
||||
if (CustomData_has_layer(&(bm->pdata), CD_MTEXPOLY)) {
|
||||
MTexPoly *mtpoly = CustomData_bmesh_get(&bm->pdata, f_ext[i].f->head.data, CD_MTEXPOLY);
|
||||
f_ext[i].t = mtpoly->tpage;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* now select the rest (if any) */
|
||||
for (i = 0; i < num_total; i++) {
|
||||
fm = f_ext[i].f;
|
||||
if (!BMO_elem_flag_test(bm, fm, FACE_MARK) && !BM_elem_flag_test(fm, BM_ELEM_HIDDEN)) {
|
||||
int cont = 1;
|
||||
for (idx = 0; idx < num_sels && cont == 1; idx++) {
|
||||
fs = f_ext[indices[idx]].f;
|
||||
switch (type) {
|
||||
case SIMFACE_MATERIAL:
|
||||
if (fm->mat_nr == fs->mat_nr) {
|
||||
BMO_elem_flag_enable(bm, fm, FACE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMFACE_IMAGE:
|
||||
if (f_ext[i].t == f_ext[indices[idx]].t) {
|
||||
BMO_elem_flag_enable(bm, fm, FACE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMFACE_NORMAL:
|
||||
angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* if the angle between the normals -> 0 */
|
||||
if (angle / 180.0f <= thresh) {
|
||||
BMO_elem_flag_enable(bm, fm, FACE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMFACE_COPLANAR:
|
||||
angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* angle -> 0 */
|
||||
if (angle / 180.0f <= thresh) { /* and dot product difference -> 0 */
|
||||
if (fabsf(f_ext[i].d - f_ext[indices[idx]].d) <= thresh) {
|
||||
BMO_elem_flag_enable(bm, fm, FACE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMFACE_AREA:
|
||||
if (fabsf(f_ext[i].area - f_ext[indices[idx]].area) <= thresh) {
|
||||
BMO_elem_flag_enable(bm, fm, FACE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMFACE_PERIMETER:
|
||||
if (fabsf(f_ext[i].perim - f_ext[indices[idx]].perim) <= thresh) {
|
||||
BMO_elem_flag_enable(bm, fm, FACE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(f_ext);
|
||||
MEM_freeN(indices);
|
||||
|
||||
/* transfer all marked faces to the output slot */
|
||||
BMO_slot_from_flag(bm, op, "faceout", FACE_MARK, BM_FACE);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Similar Edges
|
||||
**************************************************************************** */
|
||||
#define EDGE_MARK 1
|
||||
|
||||
/*
|
||||
* compute the angle of an edge (i.e. the angle between two faces)
|
||||
*/
|
||||
static float edge_angle(BMesh *bm, BMEdge *e)
|
||||
{
|
||||
BMIter fiter;
|
||||
BMFace *f, *f_prev = NULL;
|
||||
|
||||
/* first edge faces, dont account for 3+ */
|
||||
|
||||
BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
|
||||
if (f_prev == NULL) {
|
||||
f_prev = f;
|
||||
}
|
||||
else {
|
||||
return angle_v3v3(f_prev->no, f->no);
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
/*
|
||||
* extra edge information
|
||||
*/
|
||||
typedef struct tmp_edge_ext {
|
||||
BMEdge *e;
|
||||
union {
|
||||
float dir[3];
|
||||
float angle; /* angle between the face */
|
||||
};
|
||||
|
||||
union {
|
||||
float length; /* edge length */
|
||||
int faces; /* faces count */
|
||||
};
|
||||
} tmp_edge_ext;
|
||||
|
||||
/*
|
||||
* select similar edges: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
|
||||
* choices are length, direction, face, ...
|
||||
*/
|
||||
void bmesh_similaredges_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter es_iter; /* selected edges iterator */
|
||||
BMIter e_iter; /* mesh edges iterator */
|
||||
BMEdge *es; /* selected edge */
|
||||
BMEdge *e; /* mesh edge */
|
||||
int idx = 0, i = 0 /* , f = 0 */;
|
||||
int *indices = NULL;
|
||||
tmp_edge_ext *e_ext = NULL;
|
||||
// float *angles = NULL;
|
||||
float angle;
|
||||
|
||||
int num_sels = 0, num_total = 0;
|
||||
int type = BMO_slot_int_get(op, "type");
|
||||
float thresh = BMO_slot_float_get(op, "thresh");
|
||||
|
||||
num_total = BM_mesh_elem_count(bm, BM_EDGE);
|
||||
|
||||
/* iterate through all selected edges and mark them */
|
||||
BMO_ITER(es, &es_iter, bm, op, "edges", BM_EDGE) {
|
||||
BMO_elem_flag_enable(bm, es, EDGE_MARK);
|
||||
num_sels++;
|
||||
}
|
||||
|
||||
/* allocate memory for the selected edges indices and for all temporary edges */
|
||||
indices = (int *)MEM_callocN(sizeof(int) * num_sels, "indices util.c");
|
||||
e_ext = (tmp_edge_ext *)MEM_callocN(sizeof(tmp_edge_ext) * num_total, "e_ext util.c");
|
||||
|
||||
/* loop through all the edges and fill the edges/indices structure */
|
||||
BM_ITER(e, &e_iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
e_ext[i].e = e;
|
||||
if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
|
||||
indices[idx] = i;
|
||||
idx++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
/* save us some computation time by doing heavy computation once */
|
||||
if (type == SIMEDGE_LENGTH || type == SIMEDGE_FACE || type == SIMEDGE_DIR || type == SIMEDGE_FACE_ANGLE) {
|
||||
for (i = 0; i < num_total; i++) {
|
||||
switch (type) {
|
||||
case SIMEDGE_LENGTH: /* compute the length of the edge */
|
||||
e_ext[i].length = len_v3v3(e_ext[i].e->v1->co, e_ext[i].e->v2->co);
|
||||
break;
|
||||
|
||||
case SIMEDGE_DIR: /* compute the direction */
|
||||
sub_v3_v3v3(e_ext[i].dir, e_ext[i].e->v1->co, e_ext[i].e->v2->co);
|
||||
break;
|
||||
|
||||
case SIMEDGE_FACE: /* count the faces around the edge */
|
||||
e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
|
||||
break;
|
||||
|
||||
case SIMEDGE_FACE_ANGLE:
|
||||
e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
|
||||
if (e_ext[i].faces == 2)
|
||||
e_ext[i].angle = edge_angle(bm, e_ext[i].e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* select the edges if any */
|
||||
for (i = 0; i < num_total; i++) {
|
||||
e = e_ext[i].e;
|
||||
if (!BMO_elem_flag_test(bm, e, EDGE_MARK) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
|
||||
int cont = 1;
|
||||
for (idx = 0; idx < num_sels && cont == 1; idx++) {
|
||||
es = e_ext[indices[idx]].e;
|
||||
switch (type) {
|
||||
case SIMEDGE_LENGTH:
|
||||
if (fabsf(e_ext[i].length - e_ext[indices[idx]].length) <= thresh) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMEDGE_DIR:
|
||||
/* compute the angle between the two edges */
|
||||
angle = RAD2DEGF(angle_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir));
|
||||
|
||||
if (angle > 90.0f) /* use the smallest angle between the edges */
|
||||
angle = fabsf(angle - 180.0f);
|
||||
|
||||
if (angle / 90.0f <= thresh) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMEDGE_FACE:
|
||||
if (e_ext[i].faces == e_ext[indices[idx]].faces) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMEDGE_FACE_ANGLE:
|
||||
if (e_ext[i].faces == 2) {
|
||||
if (e_ext[indices[idx]].faces == 2) {
|
||||
if (fabsf(e_ext[i].angle - e_ext[indices[idx]].angle) <= thresh) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMEDGE_CREASE:
|
||||
if (CustomData_has_layer(&bm->edata, CD_CREASE)) {
|
||||
float *c1, *c2;
|
||||
|
||||
c1 = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE);
|
||||
c2 = CustomData_bmesh_get(&bm->edata, es->head.data, CD_CREASE);
|
||||
|
||||
if (c1 && c2 && fabsf(*c1 - *c2) <= thresh) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMEDGE_SEAM:
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SEAM) == BM_elem_flag_test(es, BM_ELEM_SEAM)) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMEDGE_SHARP:
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == BM_elem_flag_test(es, BM_ELEM_SMOOTH)) {
|
||||
BMO_elem_flag_enable(bm, e, EDGE_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(e_ext);
|
||||
MEM_freeN(indices);
|
||||
|
||||
/* transfer all marked edges to the output slot */
|
||||
BMO_slot_from_flag(bm, op, "edgeout", EDGE_MARK, BM_EDGE);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Similar Vertices
|
||||
**************************************************************************** */
|
||||
#define VERT_MARK 1
|
||||
|
||||
typedef struct tmp_vert_ext {
|
||||
BMVert *v;
|
||||
union {
|
||||
int num_faces; /* adjacent faces */
|
||||
MDeformVert *dvert; /* deform vertex */
|
||||
};
|
||||
} tmp_vert_ext;
|
||||
|
||||
/*
|
||||
* select similar vertices: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
|
||||
* choices are normal, face, vertex group...
|
||||
*/
|
||||
void bmesh_similarverts_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter vs_iter; /* selected verts iterator */
|
||||
BMIter v_iter; /* mesh verts iterator */
|
||||
BMVert *vs; /* selected vertex */
|
||||
BMVert *v; /* mesh vertex */
|
||||
tmp_vert_ext *v_ext = NULL;
|
||||
int *indices = NULL;
|
||||
int num_total = 0, num_sels = 0, i = 0, idx = 0;
|
||||
int type = BMO_slot_int_get(op, "type");
|
||||
float thresh = BMO_slot_float_get(op, "thresh");
|
||||
|
||||
num_total = BM_mesh_elem_count(bm, BM_VERT);
|
||||
|
||||
/* iterate through all selected edges and mark them */
|
||||
BMO_ITER(vs, &vs_iter, bm, op, "verts", BM_VERT) {
|
||||
BMO_elem_flag_enable(bm, vs, VERT_MARK);
|
||||
num_sels++;
|
||||
}
|
||||
|
||||
/* allocate memory for the selected vertices indices and for all temporary vertices */
|
||||
indices = (int *)MEM_mallocN(sizeof(int) * num_sels, "vertex indices");
|
||||
v_ext = (tmp_vert_ext *)MEM_mallocN(sizeof(tmp_vert_ext) * num_total, "vertex extra");
|
||||
|
||||
/* loop through all the vertices and fill the vertices/indices structure */
|
||||
BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
v_ext[i].v = v;
|
||||
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
|
||||
indices[idx] = i;
|
||||
idx++;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SIMVERT_FACE:
|
||||
/* calling BM_vert_face_count every time is time consumming, so call it only once per vertex */
|
||||
v_ext[i].num_faces = BM_vert_face_count(v);
|
||||
break;
|
||||
|
||||
case SIMVERT_VGROUP:
|
||||
if (CustomData_has_layer(&(bm->vdata), CD_MDEFORMVERT)) {
|
||||
v_ext[i].dvert = CustomData_bmesh_get(&bm->vdata, v_ext[i].v->head.data, CD_MDEFORMVERT);
|
||||
}
|
||||
else {
|
||||
v_ext[i].dvert = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/* select the vertices if any */
|
||||
for (i = 0; i < num_total; i++) {
|
||||
v = v_ext[i].v;
|
||||
if (!BMO_elem_flag_test(bm, v, VERT_MARK) && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
|
||||
int cont = 1;
|
||||
for (idx = 0; idx < num_sels && cont == 1; idx++) {
|
||||
vs = v_ext[indices[idx]].v;
|
||||
switch (type) {
|
||||
case SIMVERT_NORMAL:
|
||||
/* compare the angle between the normals */
|
||||
if (RAD2DEGF(angle_v3v3(v->no, vs->no)) / 180.0f <= thresh) {
|
||||
BMO_elem_flag_enable(bm, v, VERT_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
case SIMVERT_FACE:
|
||||
/* number of adjacent faces */
|
||||
if (v_ext[i].num_faces == v_ext[indices[idx]].num_faces) {
|
||||
BMO_elem_flag_enable(bm, v, VERT_MARK);
|
||||
cont = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIMVERT_VGROUP:
|
||||
if (v_ext[i].dvert != NULL && v_ext[indices[idx]].dvert != NULL) {
|
||||
int v1, v2;
|
||||
for (v1 = 0; v1 < v_ext[i].dvert->totweight && cont == 1; v1++) {
|
||||
for (v2 = 0; v2 < v_ext[indices[idx]].dvert->totweight; v2++) {
|
||||
if (v_ext[i].dvert->dw[v1].def_nr == v_ext[indices[idx]].dvert->dw[v2].def_nr) {
|
||||
BMO_elem_flag_enable(bm, v, VERT_MARK);
|
||||
cont = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(indices);
|
||||
MEM_freeN(v_ext);
|
||||
|
||||
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Cycle UVs for a face
|
||||
**************************************************************************** */
|
||||
|
||||
void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter fs_iter; /* selected faces iterator */
|
||||
BMFace *fs; /* current face */
|
||||
BMIter l_iter; /* iteration loop */
|
||||
// int n;
|
||||
|
||||
int dir = BMO_slot_int_get(op, "dir");
|
||||
|
||||
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
|
||||
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
|
||||
if (dir == DIRECTION_CW) { /* same loops direction */
|
||||
BMLoop *lf; /* current face loops */
|
||||
MLoopUV *f_luv; /* first face loop uv */
|
||||
float p_uv[2]; /* previous uvs */
|
||||
float t_uv[2]; /* tmp uvs */
|
||||
|
||||
int n = 0;
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
/* current loop uv is the previous loop uv */
|
||||
MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
|
||||
if (n == 0) {
|
||||
f_luv = luv;
|
||||
copy_v2_v2(p_uv, luv->uv);
|
||||
}
|
||||
else {
|
||||
copy_v2_v2(t_uv, luv->uv);
|
||||
copy_v2_v2(luv->uv, p_uv);
|
||||
copy_v2_v2(p_uv, t_uv);
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
copy_v2_v2(f_luv->uv, p_uv);
|
||||
}
|
||||
else if (dir == DIRECTION_CCW) { /* counter loop direction */
|
||||
BMLoop *lf; /* current face loops */
|
||||
MLoopUV *p_luv; /* previous loop uv */
|
||||
MLoopUV *luv;
|
||||
float t_uv[2]; /* current uvs */
|
||||
|
||||
int n = 0;
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
/* previous loop uv is the current loop uv */
|
||||
luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
|
||||
if (n == 0) {
|
||||
p_luv = luv;
|
||||
copy_v2_v2(t_uv, luv->uv);
|
||||
}
|
||||
else {
|
||||
copy_v2_v2(p_luv->uv, luv->uv);
|
||||
p_luv = luv;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
copy_v2_v2(luv->uv, t_uv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Reverse UVs for a face
|
||||
**************************************************************************** */
|
||||
|
||||
void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter fs_iter; /* selected faces iterator */
|
||||
BMFace *fs; /* current face */
|
||||
BMIter l_iter; /* iteration loop */
|
||||
BLI_array_declare(uvs);
|
||||
float (*uvs)[2] = NULL;
|
||||
|
||||
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
|
||||
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
|
||||
BMLoop *lf; /* current face loops */
|
||||
int i = 0;
|
||||
|
||||
BLI_array_empty(uvs);
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
|
||||
|
||||
/* current loop uv is the previous loop uv */
|
||||
BLI_array_growone(uvs);
|
||||
uvs[i][0] = luv->uv[0];
|
||||
uvs[i][1] = luv->uv[1];
|
||||
i++;
|
||||
}
|
||||
|
||||
/* now that we have the uvs in the array, reverse! */
|
||||
i = 0;
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
/* current loop uv is the previous loop uv */
|
||||
MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
|
||||
luv->uv[0] = uvs[(fs->len - i - 1)][0];
|
||||
luv->uv[1] = uvs[(fs->len - i - 1)][1];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_array_free(uvs);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Cycle colors for a face
|
||||
**************************************************************************** */
|
||||
|
||||
void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter fs_iter; /* selected faces iterator */
|
||||
BMFace *fs; /* current face */
|
||||
BMIter l_iter; /* iteration loop */
|
||||
// int n;
|
||||
|
||||
int dir = BMO_slot_int_get(op, "dir");
|
||||
|
||||
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
|
||||
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
|
||||
if (dir == DIRECTION_CW) { /* same loops direction */
|
||||
BMLoop *lf; /* current face loops */
|
||||
MLoopCol *f_lcol; /* first face loop color */
|
||||
MLoopCol p_col; /* previous color */
|
||||
MLoopCol t_col; /* tmp color */
|
||||
|
||||
int n = 0;
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
/* current loop color is the previous loop color */
|
||||
MLoopCol *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
|
||||
if (n == 0) {
|
||||
f_lcol = luv;
|
||||
p_col = *luv;
|
||||
}
|
||||
else {
|
||||
t_col = *luv;
|
||||
*luv = p_col;
|
||||
p_col = t_col;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
*f_lcol = p_col;
|
||||
}
|
||||
else if (dir == DIRECTION_CCW) { /* counter loop direction */
|
||||
BMLoop *lf; /* current face loops */
|
||||
MLoopCol *p_lcol; /* previous loop color */
|
||||
MLoopCol *lcol;
|
||||
MLoopCol t_col; /* current color */
|
||||
|
||||
int n = 0;
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
/* previous loop color is the current loop color */
|
||||
lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
|
||||
if (n == 0) {
|
||||
p_lcol = lcol;
|
||||
t_col = *lcol;
|
||||
}
|
||||
else {
|
||||
*p_lcol = *lcol;
|
||||
p_lcol = lcol;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
*lcol = t_col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Reverse colors for a face
|
||||
**************************************************************************** */
|
||||
|
||||
void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter fs_iter; /* selected faces iterator */
|
||||
BMFace *fs; /* current face */
|
||||
BMIter l_iter; /* iteration loop */
|
||||
BLI_array_declare(cols);
|
||||
MLoopCol *cols = NULL;
|
||||
|
||||
BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
|
||||
if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
|
||||
BMLoop *lf; /* current face loops */
|
||||
int i = 0;
|
||||
|
||||
BLI_array_empty(cols);
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
|
||||
|
||||
/* current loop uv is the previous loop color */
|
||||
BLI_array_growone(cols);
|
||||
cols[i] = *lcol;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* now that we have the uvs in the array, reverse! */
|
||||
i = 0;
|
||||
BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
|
||||
/* current loop uv is the previous loop color */
|
||||
MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
|
||||
*lcol = cols[(fs->len - i - 1)];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_array_free(cols);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
** shortest vertex path select
|
||||
**************************************************************************** */
|
||||
|
||||
typedef struct element_node {
|
||||
BMVert *v; /* vertex */
|
||||
BMVert *parent; /* node parent id */
|
||||
float weight; /* node weight */
|
||||
HeapNode *hn; /* heap node */
|
||||
} element_node;
|
||||
|
||||
void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMOIter vs_iter /* , vs2_iter */; /* selected verts iterator */
|
||||
BMIter v_iter; /* mesh verts iterator */
|
||||
BMVert *vs, *sv, *ev; /* starting vertex, ending vertex */
|
||||
BMVert *v; /* mesh vertex */
|
||||
Heap *h = NULL;
|
||||
|
||||
element_node *vert_list = NULL;
|
||||
|
||||
int num_total = 0 /*, num_sels = 0 */, i = 0;
|
||||
int type = BMO_slot_int_get(op, "type");
|
||||
|
||||
BMO_ITER(vs, &vs_iter, bm, op, "startv", BM_VERT) {
|
||||
sv = vs;
|
||||
}
|
||||
BMO_ITER(vs, &vs_iter, bm, op, "endv", BM_VERT) {
|
||||
ev = vs;
|
||||
}
|
||||
|
||||
num_total = BM_mesh_elem_count(bm, BM_VERT);
|
||||
|
||||
/* allocate memory for the nodes */
|
||||
vert_list = (element_node *)MEM_mallocN(sizeof(element_node) * num_total, "vertex nodes");
|
||||
|
||||
/* iterate through all the mesh vertices */
|
||||
/* loop through all the vertices and fill the vertices/indices structure */
|
||||
i = 0;
|
||||
BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
vert_list[i].v = v;
|
||||
vert_list[i].parent = NULL;
|
||||
vert_list[i].weight = FLT_MAX;
|
||||
BM_elem_index_set(v, i); /* set_inline */
|
||||
i++;
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_VERT;
|
||||
|
||||
/*
|
||||
** we now have everything we need, start Dijkstra path finding algorithm
|
||||
*/
|
||||
|
||||
/* set the distance/weight of the start vertex to 0 */
|
||||
vert_list[BM_elem_index_get(sv)].weight = 0.0f;
|
||||
|
||||
h = BLI_heap_new();
|
||||
|
||||
for (i = 0; i < num_total; i++) {
|
||||
vert_list[i].hn = BLI_heap_insert(h, vert_list[i].weight, vert_list[i].v);
|
||||
}
|
||||
|
||||
while (!BLI_heap_empty(h)) {
|
||||
BMEdge *e;
|
||||
BMIter e_i;
|
||||
float v_weight;
|
||||
|
||||
/* take the vertex with the lowest weight out of the heap */
|
||||
BMVert *v = (BMVert *)BLI_heap_popmin(h);
|
||||
|
||||
if (vert_list[BM_elem_index_get(v)].weight == FLT_MAX) /* this means that there is no path */
|
||||
break;
|
||||
|
||||
v_weight = vert_list[BM_elem_index_get(v)].weight;
|
||||
|
||||
BM_ITER(e, &e_i, bm, BM_EDGES_OF_VERT, v) {
|
||||
BMVert *u;
|
||||
float e_weight = v_weight;
|
||||
|
||||
if (type == VPATH_SELECT_EDGE_LENGTH)
|
||||
e_weight += len_v3v3(e->v1->co, e->v2->co);
|
||||
else e_weight += 1.0f;
|
||||
|
||||
u = (e->v1 == v) ? e->v2 : e->v1;
|
||||
|
||||
if (e_weight < vert_list[BM_elem_index_get(u)].weight) { /* is this path shorter ? */
|
||||
/* add it if so */
|
||||
vert_list[BM_elem_index_get(u)].parent = v;
|
||||
vert_list[BM_elem_index_get(u)].weight = e_weight;
|
||||
|
||||
/* we should do a heap update node function!!! :-/ */
|
||||
BLI_heap_remove(h, vert_list[BM_elem_index_get(u)].hn);
|
||||
BLI_heap_insert(h, e_weight, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* now we trace the path (if it exists) */
|
||||
v = ev;
|
||||
|
||||
while (vert_list[BM_elem_index_get(v)].parent != NULL) {
|
||||
BMO_elem_flag_enable(bm, v, VERT_MARK);
|
||||
v = vert_list[BM_elem_index_get(v)].parent;
|
||||
}
|
||||
|
||||
BLI_heap_free(h, NULL);
|
||||
MEM_freeN(vert_list);
|
||||
|
||||
BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
|
||||
}
|
||||
1011
source/blender/bmesh/tools/BME_bevel.c
Normal file
1011
source/blender/bmesh/tools/BME_bevel.c
Normal file
@@ -0,0 +1,1011 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2004 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Geoffrey Bantle and Levi Schooley.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BKE_utildefines.h"
|
||||
#include "BKE_tessmesh.h"
|
||||
#include "BKE_bmesh.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/* ------- Bevel code starts here -------- */
|
||||
|
||||
BME_TransData_Head *BME_init_transdata(int bufsize)
|
||||
{
|
||||
BME_TransData_Head *td;
|
||||
|
||||
td = MEM_callocN(sizeof(BME_TransData_Head), "BM transdata header");
|
||||
td->gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "BME_init_transdata gh");
|
||||
td->ma = BLI_memarena_new(bufsize, "BME_TransData arena");
|
||||
BLI_memarena_use_calloc(td->ma);
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
void BME_free_transdata(BME_TransData_Head *td)
|
||||
{
|
||||
BLI_ghash_free(td->gh, NULL, NULL);
|
||||
BLI_memarena_free(td->ma);
|
||||
MEM_freeN(td);
|
||||
}
|
||||
|
||||
BME_TransData *BME_assign_transdata(
|
||||
BME_TransData_Head *td, BMesh *bm, BMVert *v,
|
||||
float *co, float *org, float *vec, float *loc,
|
||||
float factor, float weight, float maxfactor, float *max)
|
||||
{
|
||||
BME_TransData *vtd;
|
||||
int is_new = 0;
|
||||
|
||||
if (v == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((vtd = BLI_ghash_lookup(td->gh, v)) == NULL && bm != NULL) {
|
||||
vtd = BLI_memarena_alloc(td->ma, sizeof(*vtd));
|
||||
BLI_ghash_insert(td->gh, v, vtd);
|
||||
td->len++;
|
||||
is_new = 1;
|
||||
}
|
||||
|
||||
vtd->bm = bm;
|
||||
vtd->v = v;
|
||||
|
||||
if (co != NULL) {
|
||||
copy_v3_v3(vtd->co, co);
|
||||
}
|
||||
|
||||
if (org == NULL && is_new) {
|
||||
copy_v3_v3(vtd->org, v->co); /* default */
|
||||
}
|
||||
else if (org != NULL) {
|
||||
copy_v3_v3(vtd->org, org);
|
||||
}
|
||||
|
||||
if (vec != NULL) {
|
||||
copy_v3_v3(vtd->vec, vec);
|
||||
normalize_v3(vtd->vec);
|
||||
}
|
||||
|
||||
vtd->loc = loc;
|
||||
|
||||
vtd->factor = factor;
|
||||
vtd->weight = weight;
|
||||
vtd->maxfactor = maxfactor;
|
||||
vtd->max = max;
|
||||
|
||||
return vtd;
|
||||
}
|
||||
|
||||
BME_TransData *BME_get_transdata(BME_TransData_Head *td, BMVert *v)
|
||||
{
|
||||
BME_TransData *vtd;
|
||||
vtd = BLI_ghash_lookup(td->gh, v);
|
||||
return vtd;
|
||||
}
|
||||
|
||||
/* a hack (?) to use the transdata memarena to allocate floats for use with the max limits */
|
||||
float *BME_new_transdata_float(BME_TransData_Head *td)
|
||||
{
|
||||
return BLI_memarena_alloc(td->ma, sizeof(float));
|
||||
}
|
||||
|
||||
/* BM_disk_dissolve is a real mess, and crashes bevel if called instead of this.
|
||||
* The drawback, though, is that this code doesn't merge customdata. */
|
||||
static int BME_Bevel_Dissolve_Disk(BMesh *bm, BMVert *v)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e, *elast;
|
||||
BMLoop *l1, *l2;
|
||||
|
||||
if (!BM_vert_is_manifold(bm, v)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
|
||||
if (BM_edge_face_count(e) != 2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (BM_vert_edge_count(v) > 2) {
|
||||
while (BM_vert_edge_count(v) > 2) {
|
||||
e = v->e;
|
||||
l1 = e->l;
|
||||
l2 = l1->radial_next;
|
||||
bmesh_jfke(bm, l1->f, l2->f, e);
|
||||
}
|
||||
|
||||
e = v->e;
|
||||
elast = bmesh_disk_nextedge(e, v);
|
||||
bmesh_jekv(bm, e, v);
|
||||
|
||||
l1 = elast->l;
|
||||
l2 = l1->radial_next;
|
||||
bmesh_jfke(bm, l1->f, l2->f, elast);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int BME_bevel_is_split_vert(BMesh *bm, BMLoop *l)
|
||||
{
|
||||
/* look for verts that have already been added to the edge when
|
||||
* beveling other polys; this can be determined by testing the
|
||||
* vert and the edges around it for originality
|
||||
*/
|
||||
if ( !BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
|
||||
BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG) &&
|
||||
BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get a vector, vec, that points from v1->co to wherever makes sense to
|
||||
* the bevel operation as a whole based on the relationship between v1 and v2
|
||||
* (won't necessarily be a vec from v1->co to v2->co, though it probably will be);
|
||||
* the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */
|
||||
static int BME_bevel_get_vec(float *vec, BMVert *v1, BMVert *v2, BME_TransData_Head *td)
|
||||
{
|
||||
BME_TransData *vtd1, *vtd2;
|
||||
|
||||
vtd1 = BME_get_transdata(td, v1);
|
||||
vtd2 = BME_get_transdata(td, v2);
|
||||
if (!vtd1 || !vtd2) {
|
||||
//printf("BME_bevel_get_vec() got called without proper BME_TransData\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* compare the transform origins to see if we can use the vert co's;
|
||||
* if they belong to different origins, then we will use the origins to determine
|
||||
* the vector */
|
||||
if (compare_v3v3(vtd1->org, vtd2->org, 0.000001f)) {
|
||||
sub_v3_v3v3(vec, v2->co, v1->co);
|
||||
if (len_v3(vec) < 0.000001f) {
|
||||
zero_v3(vec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
sub_v3_v3v3(vec, vtd2->org, vtd1->org);
|
||||
if (len_v3(vec) < 0.000001f) {
|
||||
zero_v3(vec);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* "Projects" a vector perpendicular to vec2 against vec1, such that
|
||||
* the projected vec1 + vec2 has a min distance of 1 from the "edge" defined by vec2.
|
||||
* note: the direction, is_forward, is used in conjunction with up_vec to determine
|
||||
* whether this is a convex or concave corner. If it is a concave corner, it will
|
||||
* be projected "backwards." If vec1 is before vec2, is_forward should be 0 (we are projecting backwards).
|
||||
* vec1 is the vector to project onto (expected to be normalized)
|
||||
* vec2 is the direction of projection (pointing away from vec1)
|
||||
* up_vec is used for orientation (expected to be normalized)
|
||||
* returns the length of the projected vector that lies along vec1 */
|
||||
static float BME_bevel_project_vec(float *vec1, float *vec2, float *up_vec, int is_forward, BME_TransData_Head *UNUSED(td))
|
||||
{
|
||||
float factor, vec3[3], tmp[3], c1, c2;
|
||||
|
||||
cross_v3_v3v3(tmp, vec1, vec2);
|
||||
normalize_v3(tmp);
|
||||
factor = dot_v3v3(up_vec, tmp);
|
||||
if ((factor > 0 && is_forward) || (factor < 0 && !is_forward)) {
|
||||
cross_v3_v3v3(vec3, vec2, tmp); /* hmm, maybe up_vec should be used instead of tmp */
|
||||
}
|
||||
else {
|
||||
cross_v3_v3v3(vec3, tmp, vec2); /* hmm, maybe up_vec should be used instead of tmp */
|
||||
}
|
||||
normalize_v3(vec3);
|
||||
c1 = dot_v3v3(vec3, vec1);
|
||||
c2 = dot_v3v3(vec1, vec1);
|
||||
if (fabsf(c1) < 0.000001f || fabsf(c2) < 0.000001f) {
|
||||
factor = 0.0f;
|
||||
}
|
||||
else {
|
||||
factor = c2 / c1;
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
/* BME_bevel_split_edge() is the main math work-house; its responsibilities are:
|
||||
* using the vert and the loop passed, get or make the split vert, set its coordinates
|
||||
* and transform properties, and set the max limits.
|
||||
* Finally, return the split vert. */
|
||||
static BMVert *BME_bevel_split_edge(BMesh *bm, BMVert *v, BMVert *v1, BMLoop *l, float *up_vec, float value, BME_TransData_Head *td)
|
||||
{
|
||||
BME_TransData *vtd, *vtd1, *vtd2;
|
||||
BMVert *sv, *v2, *v3, *ov;
|
||||
BMLoop *lv1, *lv2;
|
||||
BMEdge *ne, *e1, *e2;
|
||||
float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3];
|
||||
int is_edge, forward, is_split_vert;
|
||||
|
||||
if (l == NULL) {
|
||||
/* what you call operator overloading in C :)
|
||||
* I wanted to use the same function for both wire edges and poly loops
|
||||
* so... here we walk around edges to find the needed verts */
|
||||
forward = 1;
|
||||
is_split_vert = 0;
|
||||
if (v->e == NULL) {
|
||||
//printf("We can't split a loose vert's edge!\n");
|
||||
return NULL;
|
||||
}
|
||||
e1 = v->e; /* we just use the first two edges */
|
||||
e2 = bmesh_disk_nextedge(v->e, v);
|
||||
if (e1 == e2) {
|
||||
//printf("You need at least two edges to use BME_bevel_split_edge()\n");
|
||||
return NULL;
|
||||
}
|
||||
v2 = BM_edge_other_vert(e1, v);
|
||||
v3 = BM_edge_other_vert(e2, v);
|
||||
if (v1 != v2 && v1 != v3) {
|
||||
//printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n");
|
||||
return NULL;
|
||||
}
|
||||
if (v1 == v2) {
|
||||
v2 = v3;
|
||||
}
|
||||
else {
|
||||
e1 = e2;
|
||||
}
|
||||
ov = BM_edge_other_vert(e1, v);
|
||||
sv = BM_edge_split(bm, v, e1, &ne, 0);
|
||||
//BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
|
||||
//BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
|
||||
//BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
|
||||
BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
|
||||
BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
|
||||
BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
|
||||
BME_bevel_get_vec(vec1, v1, v, td);
|
||||
BME_bevel_get_vec(vec2, v2, v, td);
|
||||
cross_v3_v3v3(t_up_vec, vec1, vec2);
|
||||
normalize_v3(t_up_vec);
|
||||
up_vec = t_up_vec;
|
||||
}
|
||||
else {
|
||||
/* establish loop direction */
|
||||
if (l->v == v) {
|
||||
forward = 1;
|
||||
lv1 = l->next;
|
||||
lv2 = l->prev;
|
||||
v1 = l->next->v;
|
||||
v2 = l->prev->v;
|
||||
}
|
||||
else if (l->next->v == v) {
|
||||
forward = 0;
|
||||
lv1 = l;
|
||||
lv2 = l->next->next;
|
||||
v1 = l->v;
|
||||
v2 = l->next->next->v;
|
||||
}
|
||||
else {
|
||||
//printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BME_bevel_is_split_vert(bm, lv1)) {
|
||||
is_split_vert = 1;
|
||||
sv = v1;
|
||||
v1 = forward ? l->next->next->v : l->prev->v;
|
||||
}
|
||||
else {
|
||||
is_split_vert = 0;
|
||||
ov = BM_edge_other_vert(l->e, v);
|
||||
sv = BM_edge_split(bm, v, l->e, &ne, 0);
|
||||
//BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
|
||||
//BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
|
||||
//BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
|
||||
BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
|
||||
BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
|
||||
BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
|
||||
}
|
||||
|
||||
if (BME_bevel_is_split_vert(bm, lv2)) {
|
||||
v2 = forward ? lv2->prev->v : lv2->next->v;
|
||||
}
|
||||
}
|
||||
|
||||
is_edge = BME_bevel_get_vec(vec1, v, v1, td); /* get the vector we will be projecting onto */
|
||||
BME_bevel_get_vec(vec2, v, v2, td); /* get the vector we will be projecting parallel to */
|
||||
len = len_v3(vec1);
|
||||
normalize_v3(vec1);
|
||||
|
||||
vtd = BME_get_transdata(td, sv);
|
||||
vtd1 = BME_get_transdata(td, v);
|
||||
vtd2 = BME_get_transdata(td, v1);
|
||||
|
||||
if (vtd1->loc == NULL) {
|
||||
/* this is a vert with data only for calculating initial weights */
|
||||
if (vtd1->weight < 0) {
|
||||
vtd1->weight = 0;
|
||||
}
|
||||
scale = vtd1->weight / vtd1->factor;
|
||||
if (!vtd1->max) {
|
||||
vtd1->max = BME_new_transdata_float(td);
|
||||
*vtd1->max = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
scale = vtd1->weight;
|
||||
}
|
||||
vtd->max = vtd1->max;
|
||||
|
||||
if (is_edge && vtd1->loc != NULL) {
|
||||
maxfactor = vtd1->maxfactor;
|
||||
}
|
||||
else {
|
||||
maxfactor = scale * BME_bevel_project_vec(vec1, vec2, up_vec, forward, td);
|
||||
if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) {
|
||||
maxfactor = vtd->maxfactor;
|
||||
}
|
||||
}
|
||||
|
||||
dis = BMO_elem_flag_test(bm, v1, BME_BEVEL_ORIG) ? len / 3 : len / 2;
|
||||
if (is_edge || dis > maxfactor * value) {
|
||||
dis = maxfactor * value;
|
||||
}
|
||||
madd_v3_v3v3fl(sv->co, v->co, vec1, dis);
|
||||
sub_v3_v3v3(vec1, sv->co, vtd1->org);
|
||||
dis = len_v3(vec1);
|
||||
normalize_v3(vec1);
|
||||
BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max);
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
#if 0 /* UNUSED */
|
||||
static float BME_bevel_set_max(BMVert *v1, BMVert *v2, float value, BME_TransData_Head *td)
|
||||
{
|
||||
BME_TransData *vtd1, *vtd2;
|
||||
float max, fac1, fac2, vec1[3], vec2[3], vec3[3];
|
||||
|
||||
BME_bevel_get_vec(vec1, v1, v2, td);
|
||||
vtd1 = BME_get_transdata(td, v1);
|
||||
vtd2 = BME_get_transdata(td, v2);
|
||||
|
||||
if (vtd1->loc == NULL) {
|
||||
fac1 = 0;
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(vec2, vtd1->vec);
|
||||
mul_v3_fl(vec2, vtd1->factor);
|
||||
if (dot_v3v3(vec1, vec1)) {
|
||||
project_v3_v3v3(vec2, vec2, vec1);
|
||||
fac1 = len_v3(vec2) / value;
|
||||
}
|
||||
else {
|
||||
fac1 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (vtd2->loc == NULL) {
|
||||
fac2 = 0;
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(vec3, vtd2->vec);
|
||||
mul_v3_fl(vec3, vtd2->factor);
|
||||
if (dot_v3v3(vec1, vec1)) {
|
||||
project_v3_v3v3(vec2, vec3, vec1);
|
||||
fac2 = len_v3(vec2) / value;
|
||||
}
|
||||
else {
|
||||
fac2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (fac1 || fac2) {
|
||||
max = len_v3(vec1) / (fac1 + fac2);
|
||||
if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
|
||||
*vtd1->max = max;
|
||||
}
|
||||
if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
|
||||
*vtd2->max = max;
|
||||
}
|
||||
}
|
||||
else {
|
||||
max = -1;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0 /* UNUSED */
|
||||
static BMVert *BME_bevel_wire(BMesh *bm, BMVert *v, float value, int res, int UNUSED(options), BME_TransData_Head *td)
|
||||
{
|
||||
BMVert *ov1, *ov2, *v1, *v2;
|
||||
|
||||
ov1 = BM_edge_other_vert(v->e, v);
|
||||
ov2 = BM_edge_other_vert(bmesh_disk_nextedge(v->e, v), v);
|
||||
|
||||
/* split the edges */
|
||||
v1 = BME_bevel_split_edge(bm, v, ov1, NULL, NULL, value, td);
|
||||
BMO_elem_flag_enable(bm, v1, BME_BEVEL_NONMAN);
|
||||
v2 = BME_bevel_split_edge(bm, v, ov2, NULL, NULL, value, td);
|
||||
BMO_elem_flag_enable(bm, v2, BME_BEVEL_NONMAN);
|
||||
|
||||
if (value > 0.5) {
|
||||
BME_bevel_set_max(v1, ov1, value, td);
|
||||
BME_bevel_set_max(v2, ov2, value, td);
|
||||
}
|
||||
|
||||
/* remove the original vert */
|
||||
if (res) {
|
||||
/* bmesh_jekv; */
|
||||
|
||||
//void BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, int calcnorm){
|
||||
//hrm, why is there a fac here? it just removes a vert
|
||||
BM_vert_collapse_edges(bm, v->e, v);
|
||||
}
|
||||
|
||||
return v1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static BMLoop *BME_bevel_edge(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
|
||||
{
|
||||
BMVert *v1, *v2, *kv;
|
||||
BMLoop *kl = NULL, *nl;
|
||||
BMEdge *e, *ke, *se;
|
||||
BMFace *f, *jf;
|
||||
|
||||
f = l->f;
|
||||
e = l->e;
|
||||
|
||||
/* sanity check */
|
||||
if ( !BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) &&
|
||||
(BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_BEVEL)))
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
/* checks and operations for prev edge */
|
||||
/* first, check to see if this edge was inset previously */
|
||||
if ( !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG) &&
|
||||
!BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN))
|
||||
{
|
||||
kl = l->prev->radial_next;
|
||||
kl = (kl->v == l->v) ? kl->prev : kl->next;
|
||||
kv = l->v;
|
||||
}
|
||||
else {
|
||||
kv = NULL;
|
||||
}
|
||||
/* get/make the first vert to be used in SFME */
|
||||
if (BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN)) {
|
||||
v1 = l->v;
|
||||
}
|
||||
else { /* we'll need to split the previous edge */
|
||||
v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
|
||||
}
|
||||
/* if we need to clean up geometry... */
|
||||
if (kv) {
|
||||
se = l->next->e;
|
||||
jf = NULL;
|
||||
if (kl->v == kv) {
|
||||
BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
|
||||
ke = kl->e;
|
||||
/* BMESH-TODO: jfke doesn't handle customdata */
|
||||
jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
|
||||
BM_vert_collapse_edges(bm, ke, kv);
|
||||
}
|
||||
else {
|
||||
BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
|
||||
ke = kl->e;
|
||||
/* BMESH-TODO: jfke doesn't handle customdata */
|
||||
jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
|
||||
BM_vert_collapse_edges(bm, ke, kv);
|
||||
}
|
||||
/* find saved loop pointer */
|
||||
l = se->l;
|
||||
while (l->f != jf) {
|
||||
l = bmesh_radial_nextloop(l);
|
||||
BLI_assert(l != se->l);
|
||||
}
|
||||
l = l->prev;
|
||||
}
|
||||
|
||||
/* checks and operations for the next edge */
|
||||
/* first, check to see if this edge was inset previously */
|
||||
if ( !BMO_elem_flag_test(bm, l->next->e, BME_BEVEL_ORIG) &&
|
||||
!BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN))
|
||||
{
|
||||
kl = l->next->radial_next;
|
||||
kl = (kl->v == l->next->v) ? kl->prev : kl->next;
|
||||
kv = l->next->v;
|
||||
}
|
||||
else {
|
||||
kv = NULL;
|
||||
}
|
||||
/* get/make the second vert to be used in SFME */
|
||||
if (BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN)) {
|
||||
v2 = l->next->v;
|
||||
}
|
||||
else { /* we'll need to split the next edge */
|
||||
v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
|
||||
}
|
||||
/* if we need to clean up geometry... */
|
||||
if (kv) {
|
||||
se = l->e;
|
||||
jf = NULL;
|
||||
if (kl->v == kv) {
|
||||
BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
|
||||
ke = kl->e;
|
||||
/* BMESH-TODO: jfke doesn't handle customdata */
|
||||
jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
|
||||
BM_vert_collapse_edges(bm, ke, kv);
|
||||
}
|
||||
else {
|
||||
BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
|
||||
ke = kl->e;
|
||||
/* BMESH-TODO: jfke doesn't handle customdata */
|
||||
jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
|
||||
BM_vert_collapse_edges(bm, ke, kv);
|
||||
}
|
||||
/* find saved loop pointer */
|
||||
l = se->l;
|
||||
while (l->f != jf) {
|
||||
l = bmesh_radial_nextloop(l);
|
||||
BLI_assert(l != se->l);
|
||||
}
|
||||
}
|
||||
|
||||
if (!BMO_elem_flag_test(bm, v1, BME_BEVEL_NONMAN) || !BMO_elem_flag_test(bm, v2, BME_BEVEL_NONMAN)) {
|
||||
BM_face_split(bm, f, v2, v1, &l, e);
|
||||
BMO_elem_flag_enable(bm, l->e, BME_BEVEL_BEVEL);
|
||||
l = l->radial_next;
|
||||
}
|
||||
|
||||
if (l->f != f){
|
||||
//printf("Whoops! You got something out of order in BME_bevel_edge()!\n");
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static BMLoop *BME_bevel_vert(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
|
||||
{
|
||||
BMVert *v1, *v2;
|
||||
BMFace *f;
|
||||
|
||||
/* get/make the first vert to be used in SFME */
|
||||
/* may need to split the previous edge */
|
||||
v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
|
||||
|
||||
/* get/make the second vert to be used in SFME */
|
||||
/* may need to split this edge (so move l) */
|
||||
l = l->prev;
|
||||
v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
|
||||
l = l->next->next;
|
||||
|
||||
/* "cut off" this corner */
|
||||
f = BM_face_split(bm, l->f, v2, v1, NULL, l->e);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/*
|
||||
* BME_bevel_poly
|
||||
*
|
||||
* Polygon inset tool:
|
||||
*
|
||||
* Insets a polygon/face based on the flagss of its vertices
|
||||
* and edges. Used by the bevel tool only, for now.
|
||||
* The parameter "value" is the distance to inset (should be negative).
|
||||
* The parameter "options" is not currently used.
|
||||
*
|
||||
* Returns -
|
||||
* A BMFace pointer to the resulting inner face.
|
||||
*/
|
||||
static BMFace *BME_bevel_poly(BMesh *bm, BMFace *f, float value, int options, BME_TransData_Head *td)
|
||||
{
|
||||
BMLoop *l/*, *o */;
|
||||
BME_TransData *vtd1, *vtd2;
|
||||
float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1;
|
||||
int len, i;
|
||||
BMIter iter;
|
||||
|
||||
zero_v3(up_vec);
|
||||
|
||||
/* find a good normal for this face (there's better ways, I'm sure) */
|
||||
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
BME_bevel_get_vec(vec1, l->v, l->next->v, td);
|
||||
BME_bevel_get_vec(vec2, l->prev->v, l->v, td);
|
||||
cross_v3_v3v3(vec3, vec2, vec1);
|
||||
add_v3_v3(up_vec, vec3);
|
||||
}
|
||||
normalize_v3(up_vec);
|
||||
|
||||
/* Can't use a BM_LOOPS_OF_FACE iterator here, because the loops are being modified
|
||||
* and so the end condition will never hi */
|
||||
for (l = BM_FACE_FIRST_LOOP(f)->prev, i = 0, len = f->len; i < len; i++, l = l->next) {
|
||||
if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
|
||||
max = 1.0f;
|
||||
l = BME_bevel_edge(bm, l, value, options, up_vec, td);
|
||||
}
|
||||
else if ( BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) &&
|
||||
BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
|
||||
!BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_BEVEL))
|
||||
{
|
||||
max = 1.0f;
|
||||
l = BME_bevel_vert(bm, l, value, options, up_vec, td);
|
||||
}
|
||||
}
|
||||
|
||||
f = l->f;
|
||||
|
||||
/* max pass */
|
||||
if (value > 0.5f && max > 0) {
|
||||
max = -1;
|
||||
BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
|
||||
if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
|
||||
BME_bevel_get_vec(vec1, l->v, l->next->v, td);
|
||||
vtd1 = BME_get_transdata(td, l->v);
|
||||
vtd2 = BME_get_transdata(td, l->next->v);
|
||||
if (vtd1->loc == NULL) {
|
||||
fac1 = 0;
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(vec2, vtd1->vec);
|
||||
mul_v3_fl(vec2, vtd1->factor);
|
||||
if (dot_v3v3(vec1, vec1)) {
|
||||
project_v3_v3v3(vec2, vec2, vec1);
|
||||
fac1 = len_v3(vec2) / value;
|
||||
}
|
||||
else {
|
||||
fac1 = 0;
|
||||
}
|
||||
}
|
||||
if (vtd2->loc == NULL) {
|
||||
fac2 = 0;
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(vec3, vtd2->vec);
|
||||
mul_v3_fl(vec3, vtd2->factor);
|
||||
if (dot_v3v3(vec1, vec1)) {
|
||||
project_v3_v3v3(vec2, vec3, vec1);
|
||||
fac2 = len_v3(vec2) / value;
|
||||
}
|
||||
else {
|
||||
fac2 = 0;
|
||||
}
|
||||
}
|
||||
if (fac1 || fac2) {
|
||||
max = len_v3(vec1)/(fac1 + fac2);
|
||||
if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
|
||||
*vtd1->max = max;
|
||||
}
|
||||
if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
|
||||
*vtd2->max = max;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return l->f; */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void BME_bevel_add_vweight(BME_TransData_Head *td, BMesh *bm, BMVert *v, float weight, float factor, int options)
|
||||
{
|
||||
BME_TransData *vtd;
|
||||
|
||||
if (BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) return;
|
||||
BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
|
||||
if ((vtd = BME_get_transdata(td, v))) {
|
||||
if (options & BME_BEVEL_EMIN) {
|
||||
vtd->factor = 1.0;
|
||||
if (vtd->weight < 0 || weight < vtd->weight) {
|
||||
vtd->weight = weight;
|
||||
}
|
||||
}
|
||||
else if (options & BME_BEVEL_EMAX) {
|
||||
vtd->factor = 1.0;
|
||||
if (weight > vtd->weight) {
|
||||
vtd->weight = weight;
|
||||
}
|
||||
}
|
||||
else if (vtd->weight < 0) {
|
||||
vtd->factor = factor;
|
||||
vtd->weight = weight;
|
||||
}
|
||||
else {
|
||||
vtd->factor += factor; /* increment number of edges with weights (will be averaged) */
|
||||
vtd->weight += weight; /* accumulate all the weights */
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* we'll use vtd->loc == NULL to mark that this vert is not moving */
|
||||
vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void bevel_init_verts(BMesh *bm, int options, BME_TransData_Head *td)
|
||||
{
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
float weight;
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
weight = 0.0;
|
||||
if (!BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) {
|
||||
/* modifiers should not use selection */
|
||||
if(options & BME_BEVEL_SELECT){
|
||||
if(BM_elem_flag_test(v, BM_ELEM_SELECT)) weight = 1.0;
|
||||
}
|
||||
/* bevel weight NYI */
|
||||
else if(options & BME_BEVEL_WEIGHT)
|
||||
weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT);
|
||||
else
|
||||
weight = 1.0;
|
||||
if(weight > 0.0){
|
||||
BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
|
||||
BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, weight, -1, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bevel_init_edges(BMesh *bm, int options, BME_TransData_Head *td)
|
||||
{
|
||||
BMEdge *e;
|
||||
int count;
|
||||
float weight;
|
||||
BMIter iter;
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
weight = 0.0;
|
||||
if (!BMO_elem_flag_test(bm, e, BME_BEVEL_NONMAN)) {
|
||||
if(options & BME_BEVEL_SELECT){
|
||||
if(BM_elem_flag_test(e, BM_ELEM_SELECT)) weight = 1.0;
|
||||
}
|
||||
else if(options & BME_BEVEL_WEIGHT) {
|
||||
weight = BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT);
|
||||
}
|
||||
else {
|
||||
weight = 1.0;
|
||||
}
|
||||
if(weight > 0.0){
|
||||
BMO_elem_flag_enable(bm, e, BME_BEVEL_BEVEL);
|
||||
BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_BEVEL);
|
||||
BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_BEVEL);
|
||||
BME_bevel_add_vweight(td, bm, e->v1, weight, 1.0, options);
|
||||
BME_bevel_add_vweight(td, bm, e->v2, weight, 1.0, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* clean up edges with 2 faces that share more than one edg */
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL)) {
|
||||
count = BM_face_share_edges(e->l->f, e->l->radial_next->f);
|
||||
if(count > 1) BMO_elem_flag_disable(bm, e, BME_BEVEL_BEVEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BMesh *BME_bevel_initialize(BMesh *bm, int options, int UNUSED(defgrp_index), float UNUSED(angle), BME_TransData_Head *td)
|
||||
{
|
||||
BMVert *v/*, *v2 */;
|
||||
BMEdge *e/*, *curedg */;
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
int /* wire, */ len;
|
||||
|
||||
/* tag non-manifold geometr */
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
|
||||
if(v->e){
|
||||
BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL);
|
||||
if (!BM_vert_is_manifold(bm, v))
|
||||
BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
|
||||
/* test wire ver */
|
||||
len = BM_vert_edge_count(v);
|
||||
if (len == 2 && BM_vert_is_wire(bm, v))
|
||||
BMO_elem_flag_disable(bm, v, BME_BEVEL_NONMAN);
|
||||
}
|
||||
else
|
||||
BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
|
||||
}
|
||||
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
|
||||
if (!BM_edge_is_manifold(bm, e)) {
|
||||
BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_NONMAN);
|
||||
BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_NONMAN);
|
||||
BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
|
||||
}
|
||||
if(BMO_elem_flag_test(bm, e->v1, BME_BEVEL_NONMAN) || BMO_elem_flag_test(bm, e->v2, BME_BEVEL_NONMAN)) BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
|
||||
}
|
||||
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL)
|
||||
BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
|
||||
if(options & BME_BEVEL_VERT) bevel_init_verts(bm, options, td);
|
||||
else bevel_init_edges(bm, options, td);
|
||||
return bm;
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static BMesh *BME_bevel_reinitialize(BMesh *bm)
|
||||
{
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
|
||||
}
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
|
||||
}
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
|
||||
}
|
||||
return bm;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* BME_bevel_mesh
|
||||
*
|
||||
* Mesh beveling tool:
|
||||
*
|
||||
* Bevels an entire mesh. It currently uses the flags of
|
||||
* its vertices and edges to track topological changes.
|
||||
* The parameter "value" is the distance to inset (should be negative).
|
||||
* The parameter "options" is not currently used.
|
||||
*
|
||||
* Returns -
|
||||
* A BMesh pointer to the BM passed as a parameter.
|
||||
*/
|
||||
|
||||
static BMesh *BME_bevel_mesh(BMesh *bm, float value, int UNUSED(res), int options, int UNUSED(defgrp_index), BME_TransData_Head *td)
|
||||
{
|
||||
BMVert *v;
|
||||
BMEdge *e, *curedge;
|
||||
BMLoop *l, *l2;
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
|
||||
/* unsigned int i, len; */
|
||||
|
||||
/* bevel poly */
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
if(BMO_elem_flag_test(bm, f, BME_BEVEL_ORIG)) {
|
||||
BME_bevel_poly(bm, f, value, options, td);
|
||||
}
|
||||
}
|
||||
|
||||
/* get rid of beveled edge */
|
||||
BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
|
||||
if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, e, BME_BEVEL_ORIG)) {
|
||||
BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
|
||||
}
|
||||
}
|
||||
|
||||
/* link up corners and cli */
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if(BMO_elem_flag_test(bm, v, BME_BEVEL_ORIG) && BMO_elem_flag_test(bm, v, BME_BEVEL_BEVEL)) {
|
||||
curedge = v->e;
|
||||
do{
|
||||
l = curedge->l;
|
||||
l2 = l->radial_next;
|
||||
if(l->v != v) l = l->next;
|
||||
if(l2->v != v) l2 = l2->next;
|
||||
if(l->f->len > 3)
|
||||
BM_face_split(bm, l->f, l->next->v, l->prev->v, &l, l->e); /* clip this corner off */
|
||||
if(l2->f->len > 3)
|
||||
BM_face_split(bm, l2->f, l2->next->v, l2->prev->v, &l, l2->e); /* clip this corner off */
|
||||
curedge = bmesh_disk_nextedge(curedge, v);
|
||||
} while(curedge != v->e);
|
||||
BME_Bevel_Dissolve_Disk(bm, v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Debug print, remov */
|
||||
BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
|
||||
if(f->len == 2){
|
||||
printf("warning");
|
||||
}
|
||||
}
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
BMesh *BME_bevel(BMEditMesh *em, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd)
|
||||
{
|
||||
BMesh *bm = em->bm;
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
|
||||
BME_TransData_Head *td;
|
||||
BME_TransData *vtd;
|
||||
int i;
|
||||
double fac = 1, d;
|
||||
|
||||
td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE);
|
||||
/* recursion math courtesy of Martin Poirier (theeth) */
|
||||
for (i = 0; i < res - 1; i++) {
|
||||
if (i == 0) fac += 1.0f / 3.0f; else fac += 1.0f / (3 * i * 2.0f);
|
||||
}
|
||||
d = 1.0f / fac;
|
||||
|
||||
for (i = 0; i < res || (res == 0 && i == 0); i++) {
|
||||
BMO_push(bm, NULL);
|
||||
BME_bevel_initialize(bm, options, defgrp_index, angle, td);
|
||||
//if (i != 0) BME_bevel_reinitialize(bm);
|
||||
bmesh_begin_edit(bm, 0);
|
||||
BME_bevel_mesh(bm, (float)d, res, options, defgrp_index, td);
|
||||
bmesh_end_edit(bm, 0);
|
||||
d /= (i == 0) ? 3.0 : 2.0;
|
||||
BMO_pop(bm);
|
||||
}
|
||||
|
||||
BMEdit_RecalcTesselation(em);
|
||||
|
||||
/* interactive preview? */
|
||||
if (rtd) {
|
||||
*rtd = td;
|
||||
return bm;
|
||||
}
|
||||
|
||||
/* otherwise apply transforms */
|
||||
BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
|
||||
if ( (vtd = BME_get_transdata(td, v)) ) {
|
||||
if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) {
|
||||
d = *vtd->max;
|
||||
}
|
||||
else {
|
||||
d = value;
|
||||
}
|
||||
madd_v3_v3v3fl(v->co, vtd->org, vtd->vec, vtd->factor * d);
|
||||
}
|
||||
}
|
||||
|
||||
BME_free_transdata(td);
|
||||
return bm;
|
||||
}
|
||||
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