This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/bmesh/operators/bmo_dupe.c
Campbell Barton ebaf1306b8 bmesh operator api:
avoid per vert/edge/face string lookups in BMO_slot_map_* functions --- used in array modifier, subdivide, remove doubles and other tools.
2012-11-20 13:29:27 +00:00

536 lines
15 KiB
C

/*
* ***** 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 *****
*/
/** \file blender/bmesh/operators/bmo_dupe.c
* \ingroup bmesh
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.h"
#include "BLI_math.h"
#include "bmesh.h"
#include "intern/bmesh_operators_private.h" /* own include */
/* local flag define */
#define DUPE_INPUT 1 /* input from operator */
#define DUPE_NEW 2
#define DUPE_DONE 4
// #define DUPE_MAPPED 8 // UNUSED
/**
* 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 vertex */
target_vertex = BM_vert_create(target_mesh, source_vertex->co, NULL);
/* Insert new vertex into the vert hash */
BLI_ghash_insert(vhash, source_vertex, target_vertex);
/* Copy attributes */
BM_elem_attrs_copy(source_mesh, target_mesh, source_vertex, target_vertex);
/* Set internal op flags */
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,
BMOpSlot *slot_boundarymap_out,
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 edge */
target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
/* add to new/old edge map if necassary */
if (rlen < 2) {
/* not sure what non-manifold cases of greater then three
* radial should do. */
BMO_slot_map_ptr_insert(op, slot_boundarymap_out,
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,
BMOpSlot *slot_facemap_out,
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 face */
target_face = BM_face_create(target_mesh, vtar, edar, source_face->len, FALSE);
BMO_slot_map_ptr_insert(op, slot_facemap_out, source_face, target_face);
BMO_slot_map_ptr_insert(op, slot_facemap_out, target_face, source_face);
BM_elem_attrs_copy(source_mesh, target_mesh, source_face, target_face);
/* mark the face for output */
BMO_elem_flag_enable(target_mesh, target_face, DUPE_NEW);
/* copy per-loop custom data */
BM_ITER_ELEM (source_loop, &iter, source_face, BM_LOOPS_OF_FACE) {
BM_ITER_ELEM (target_loop, &iter2, target_face, BM_LOOPS_OF_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 bmo_mesh_copy(BMOperator *op, BMesh *bm_src, BMesh *bm_dst)
{
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 viter, eiter, fiter;
GHash *vhash, *ehash;
BMOpSlot *slot_boundarymap_out = BMO_slot_get(op->slots_out, "boundarymap.out");
BMOpSlot *slot_facemap_out = BMO_slot_get(op->slots_out, "facemap.out");
BMOpSlot *slot_isovertmap_out = BMO_slot_get(op->slots_out, "isovertmap.out");
/* initialize pointer hashes */
vhash = BLI_ghash_ptr_new("bmesh dupeops v");
ehash = BLI_ghash_ptr_new("bmesh dupeops e");
/* duplicate flagged vertices */
BM_ITER_MESH (v, &viter, bm_src, BM_VERTS_OF_MESH) {
if (BMO_elem_flag_test(bm_src, v, DUPE_INPUT) &&
!BMO_elem_flag_test(bm_src, v, DUPE_DONE))
{
BMIter iter;
int isolated = 1;
v2 = copy_vertex(bm_src, v, bm_dst, vhash);
BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
if (BMO_elem_flag_test(bm_src, f, DUPE_INPUT)) {
isolated = 0;
break;
}
}
if (isolated) {
BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
if (BMO_elem_flag_test(bm_src, e, DUPE_INPUT)) {
isolated = 0;
break;
}
}
}
if (isolated) {
BMO_slot_map_ptr_insert(op, slot_isovertmap_out, v, v2);
}
BMO_elem_flag_enable(bm_src, v, DUPE_DONE);
}
}
/* now we dupe all the edges */
BM_ITER_MESH (e, &eiter, bm_src, BM_EDGES_OF_MESH) {
if (BMO_elem_flag_test(bm_src, e, DUPE_INPUT) &&
!BMO_elem_flag_test(bm_src, e, DUPE_DONE))
{
/* make sure that verts are copied */
if (!BMO_elem_flag_test(bm_src, e->v1, DUPE_DONE)) {
copy_vertex(bm_src, e->v1, bm_dst, vhash);
BMO_elem_flag_enable(bm_src, e->v1, DUPE_DONE);
}
if (!BMO_elem_flag_test(bm_src, e->v2, DUPE_DONE)) {
copy_vertex(bm_src, e->v2, bm_dst, vhash);
BMO_elem_flag_enable(bm_src, e->v2, DUPE_DONE);
}
/* now copy the actual edge */
copy_edge(op, slot_boundarymap_out, bm_src, e, bm_dst, vhash, ehash);
BMO_elem_flag_enable(bm_src, e, DUPE_DONE);
}
}
/* first we dupe all flagged faces and their elements from source */
BM_ITER_MESH (f, &fiter, bm_src, BM_FACES_OF_MESH) {
if (BMO_elem_flag_test(bm_src, f, DUPE_INPUT)) {
/* vertex pass */
BM_ITER_ELEM (v, &viter, f, BM_VERTS_OF_FACE) {
if (!BMO_elem_flag_test(bm_src, v, DUPE_DONE)) {
copy_vertex(bm_src, v, bm_dst, vhash);
BMO_elem_flag_enable(bm_src, v, DUPE_DONE);
}
}
/* edge pass */
BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
if (!BMO_elem_flag_test(bm_src, e, DUPE_DONE)) {
copy_edge(op, slot_boundarymap_out, bm_src, e, bm_dst, vhash, ehash);
BMO_elem_flag_enable(bm_src, e, DUPE_DONE);
}
}
/* ensure arrays are the right size */
BLI_array_empty(vtar);
BLI_array_empty(edar);
BLI_array_grow_items(vtar, f->len);
BLI_array_grow_items(edar, f->len);
copy_face(op, slot_facemap_out, bm_src, f, bm_dst, vtar, edar, vhash, ehash);
BMO_elem_flag_enable(bm_src, f, DUPE_DONE);
}
}
/* free pointer hashes */
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 bmo_duplicate_exec(BMesh *bm, BMOperator *op)
{
BMOperator *dupeop = op;
BMesh *bm2 = BMO_slot_ptr_get(op->slots_in, "dest");
if (!bm2)
bm2 = bm;
/* flag input */
BMO_slot_buffer_flag_enable(bm, dupeop->slots_in, "geom", BM_ALL, DUPE_INPUT);
/* use the internal copy function */
bmo_mesh_copy(dupeop, bm, bm2);
/* Output */
/* First copy the input buffers to output buffers - original data */
BMO_slot_copy(dupeop, slots_in, "geom",
dupeop, slots_out, "geom_orig.out");
/* Now alloc the new output buffers */
BMO_slot_buffer_from_enabled_flag(bm, dupeop, dupeop->slots_out, "geom.out", BM_ALL, DUPE_NEW);
}
#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 htype, const char hflag)
{
BMOperator dupeop;
BMO_op_init(bm, &dupeop, "duplicate");
BMO_slot_buffer_from_enabled_hflag(bm, &dupeop, "geom", htype, hflag);
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 bmo_split_exec(BMesh *bm, BMOperator *op)
{
BMOperator *splitop = op;
BMOperator dupeop;
BMOperator delop;
const short use_only_faces = BMO_slot_bool_get(op->slots_in, "use_only_faces");
/* initialize our sub-operator */
BMO_op_init(bm, &dupeop, op->flag, "duplicate");
BMO_op_init(bm, &delop, op->flag, "delete");
BMO_slot_copy(splitop, slots_in, "geom",
&dupeop, slots_in, "geom");
BMO_op_exec(bm, &dupeop);
BMO_slot_buffer_flag_enable(bm, splitop->slots_in, "geom", BM_ALL, SPLIT_INPUT);
if (use_only_faces) {
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter iter, iter2;
int found;
/* 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 geometry */
BMO_slot_int_set(delop.slots_in, "context", DEL_FACES);
BMO_slot_buffer_from_enabled_flag(bm, &delop, delop.slots_in, "geom", BM_ALL, SPLIT_INPUT);
BMO_op_exec(bm, &delop);
/* now we make our outputs by copying the dupe output */
BMO_slot_copy(&dupeop, slots_out, "geom.out",
splitop, slots_out, "geom.out");
BMO_slot_copy(&dupeop, slots_out, "boundarymap.out",
splitop, slots_out, "boundarymap.out");
BMO_slot_copy(&dupeop, slots_out, "isovertmap.out",
splitop, slots_out, "isovertmap.out");
/* cleanup */
BMO_op_finish(bm, &delop);
BMO_op_finish(bm, &dupeop);
}
void bmo_delete_exec(BMesh *bm, BMOperator *op)
{
#define DEL_INPUT 1
BMOperator *delop = op;
/* Mark Buffer */
BMO_slot_buffer_flag_enable(bm, delop->slots_in, "geom", BM_ALL, DEL_INPUT);
BMO_remove_tagged_context(bm, DEL_INPUT, BMO_slot_int_get(op->slots_in, "context"));
#undef DEL_INPUT
}
/**
* Spin Operator
*
* Extrude or duplicate geometry a number of times,
* rotating and possibly translating after each step
*/
void bmo_spin_exec(BMesh *bm, BMOperator *op)
{
BMOperator dupop, extop;
float cent[3], dvec[3];
float axis[3] = {0.0f, 0.0f, 1.0f};
float rmat[3][3];
float phi;
int steps, do_dupli, a, usedvec;
BMO_slot_vec_get(op->slots_in, "cent", cent);
BMO_slot_vec_get(op->slots_in, "axis", axis);
normalize_v3(axis);
BMO_slot_vec_get(op->slots_in, "dvec", dvec);
usedvec = !is_zero_v3(dvec);
steps = BMO_slot_int_get(op->slots_in, "steps");
phi = BMO_slot_float_get(op->slots_in, "angle") * DEG2RADF(1.0f) / steps;
do_dupli = BMO_slot_bool_get(op->slots_in, "use_duplicate");
axis_angle_to_mat3(rmat, axis, phi);
BMO_slot_copy(op, slots_in, "geom",
op, slots_out, "geom_last.out");
for (a = 0; a < steps; a++) {
if (do_dupli) {
BMO_op_initf(bm, &dupop, op->flag, "duplicate geom=%S", op, "geom_last.out");
BMO_op_exec(bm, &dupop);
BMO_op_callf(bm, op->flag,
"rotate cent=%v mat=%m3 verts=%S",
cent, rmat, &dupop, "geom.out");
BMO_slot_copy(&dupop, slots_out, "geom.out",
op, slots_out, "geom_last.out");
BMO_op_finish(bm, &dupop);
}
else {
BMO_op_initf(bm, &extop, op->flag, "extrude_face_region geom=%S",
op, "geom_last.out");
BMO_op_exec(bm, &extop);
BMO_op_callf(bm, op->flag,
"rotate cent=%v mat=%m3 verts=%S",
cent, rmat, &extop, "geom.out");
BMO_slot_copy(&extop, slots_out, "geom.out",
op, slots_out, "geom_last.out");
BMO_op_finish(bm, &extop);
}
if (usedvec) {
mul_m3_v3(rmat, dvec);
BMO_op_callf(bm, op->flag,
"translate vec=%v verts=%S",
dvec, op, "geom_last.out");
}
}
}