Blender V4.3
bmo_create.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "MEM_guardedalloc.h"
12
13#include "BLI_listbase.h"
14
15#include "bmesh.hh"
16
17#include "intern/bmesh_operators_private.hh" /* own include */
18
19#define ELE_NEW 1
20#define ELE_OUT 2
21
23{
24 /* NOTE(@ideasman42): doing the best thing here isn't always easy create vs dissolve,
25 * its nice to support but it _really_ gives issues we might have to not call dissolve. */
26
27 BMOIter oiter;
28 BMHeader *h;
29 int totv = 0, tote = 0, totf = 0;
30 const short mat_nr = BMO_slot_int_get(op->slots_in, "mat_nr");
31 const bool use_smooth = BMO_slot_bool_get(op->slots_in, "use_smooth");
32
33 /* count number of each element type we were passe */
34 BMO_ITER (h, &oiter, op->slots_in, "geom", BM_VERT | BM_EDGE | BM_FACE) {
35 switch (h->htype) {
36 case BM_VERT:
38 totv++;
39 break;
40 case BM_EDGE:
42 tote++;
43 break;
44 case BM_FACE:
46 totf++;
47 break;
48 }
49 }
50
51 /* --- Support Edge Creation ---
52 * simple case when we only have 2 verts selected.
53 */
54 if (totv == 2 && tote == 0 && totf == 0) {
55 BMVert *verts[2];
56 BMEdge *e;
57
58 if (BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)verts, 2) == 2) {
59 /* create edge */
60 e = BM_edge_create(bm, verts[0], verts[1], nullptr, BM_CREATE_NO_DOUBLE);
62 tote += 1;
64 }
65 return;
66 }
67
68 /* --- Support for Special Case ---
69 * where there is a contiguous edge ring with one isolated vertex.
70 *
71 * This example shows 2 edges created from 3 verts
72 * with 1 free standing vertex. Dotted lines denote the 2 edges that are created.
73 *
74 * note that this works for any sided shape.
75 *
76 * +--------+
77 * | .
78 * | .
79 * | .
80 * | .
81 * +........+ <-- starts out free standing.
82 */
83
84 /* Here we check for consistency and create 2 edges */
85 if (totf == 0 && totv >= 4 && totv == tote + 2) {
86 /* find a free standing vertex and 2 endpoint verts */
87 BMVert *v, *v_free = nullptr, *v_a = nullptr, *v_b = nullptr;
88 bool ok = true;
89
90 BMO_ITER (v, &oiter, op->slots_in, "geom", BM_VERT) {
91 /* count how many flagged edges this vertex uses */
92 const int tot_edges = BMO_iter_elem_count_flag(bm, BM_EDGES_OF_VERT, v, ELE_NEW, true);
93 if (tot_edges == 0) {
94 /* only accept 1 free vert */
95 if (v_free == nullptr) {
96 v_free = v;
97 }
98 else {
99 ok = false;
100 } /* only ever want one of these */
101 }
102 else if (tot_edges == 1) {
103 if (v_a == nullptr) {
104 v_a = v;
105 }
106 else if (v_b == nullptr) {
107 v_b = v;
108 }
109 else {
110 ok = false;
111 } /* only ever want 2 of these */
112 }
113 else if (tot_edges == 2) {
114 /* do nothing, regular case */
115 }
116 else {
117 ok = false; /* if a vertex has 3+ edge users then cancel - this is only simple cases */
118 }
119
120 if (ok == false) {
121 break;
122 }
123 }
124
125 if (ok == true && v_free && v_a && v_b) {
126 BMEdge *e;
127
128 e = BM_edge_create(bm, v_free, v_a, nullptr, BM_CREATE_NO_DOUBLE);
130
131 e = BM_edge_create(bm, v_free, v_b, nullptr, BM_CREATE_NO_DOUBLE);
133 tote += 2;
134 }
135 }
136 /* --- end special case support, continue as normal --- */
137
138 /* -------------------------------------------------------------------- */
139 /* EdgeNet Create */
140 if (tote != 0) {
141 /* call edgenet prepare op so additional face creation cases work */
142
144 BMO_op_initf(bm, &op_sub, op->flag, "edgenet_prepare edges=%fe", ELE_NEW);
146 BMO_slot_buffer_flag_enable(bm, op_sub.slots_out, "edges.out", BM_EDGE, ELE_NEW);
148
150 &op_sub,
151 op->flag,
152 "edgenet_fill edges=%fe mat_nr=%i use_smooth=%b sides=%i",
153 ELE_NEW,
154 mat_nr,
155 use_smooth,
156 10000);
157
159
160 /* return if edge net create did something */
161 if (BMO_slot_buffer_len(op_sub.slots_out, "faces.out")) {
162 BMO_slot_copy(&op_sub, slots_out, "faces.out", op, slots_out, "faces.out");
164 return;
165 }
166
168 }
169
170 /* -------------------------------------------------------------------- */
171 /* Dissolve Face */
172 if (totf != 0) { /* should be (totf > 1)... see below */
173 /* NOTE: allow this to run on single faces so running on a single face
174 * won't go on to create a face, treating them as random */
176 BMO_op_initf(bm, &op_sub, op->flag, "dissolve_faces faces=%ff", ELE_NEW);
178
179 /* if we dissolved anything, then return */
180 if (BMO_slot_buffer_len(op_sub.slots_out, "region.out")) {
181 BMO_slot_copy(&op_sub, slots_out, "region.out", op, slots_out, "faces.out");
183 return;
184 }
185
187 }
188
189 /* -------------------------------------------------------------------- */
190 /* Fill EdgeLoop's - fills isolated loops, different from edgenet */
191 if (tote > 2) {
193 /* NOTE: in most cases 'edgenet_fill' will handle this case since in common cases
194 * users fill in empty spaces, however its possible to have an edge selection around
195 * existing geometry that makes 'edgenet_fill' fail. */
196 BMO_op_initf(bm, &op_sub, op->flag, "edgeloop_fill edges=%fe", ELE_NEW);
198
199 /* return if edge loop fill did something */
200 if (BMO_slot_buffer_len(op_sub.slots_out, "faces.out")) {
201 BMO_slot_copy(&op_sub, slots_out, "faces.out", op, slots_out, "faces.out");
203 return;
204 }
205
207 }
208
209 /* -------------------------------------------------------------------- */
210 /* Continue with ad-hoc fill methods since operators fail,
211 * edge, vcloud... may add more */
212
213 if (false) { /* nice feature but perhaps it should be a different tool? */
214
215 /* tricky feature for making a line/edge from selection history...
216 *
217 * Rather than do nothing, when 5+ verts are selected, check if they are in our history,
218 * when this is so, we can make edges from them, but _not_ a face,
219 * if it is the intention to make a face the user can just hit F again
220 * since there will be edges next time around.
221 *
222 * if all history verts have ELE_NEW flagged and the total number of history verts == totv,
223 * then we know the history contains all verts here and we can continue...
224 */
225 int tot_ese_v = 0;
226
228 if (ese->htype == BM_VERT) {
229 if (BMO_vert_flag_test(bm, (BMVert *)ese->ele, ELE_NEW)) {
230 tot_ese_v++;
231 }
232 else {
233 /* unflagged vert means we are not in sync */
234 tot_ese_v = -1;
235 break;
236 }
237 }
238 }
239
240 if (tot_ese_v == totv) {
241 BMVert *v_prev = nullptr;
242 /* yes, all select-history verts are accounted for, now make edges */
243
245 if (ese->htype == BM_VERT) {
246 BMVert *v = (BMVert *)ese->ele;
247 if (v_prev) {
248 BMEdge *e = BM_edge_create(bm, v, v_prev, nullptr, BM_CREATE_NO_DOUBLE);
250 }
251 v_prev = v;
252 }
253 }
254 }
256 /* done creating edges */
257
258 return;
259 }
260
261 /* -------------------------------------------------------------------- */
262 /* Fill Vertex Cloud
263 *
264 * last resort when all else fails.
265 */
266 if (totv > 2) {
267 /* TODO: some of these vertices may be connected by edges,
268 * this connectivity could be used rather than treating
269 * them as a bunch of isolated verts. */
270
271 BMVert **vert_arr = static_cast<BMVert **>(MEM_mallocN(sizeof(BMVert *) * totv, __func__));
272 BMFace *f;
273
274 totv = BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)vert_arr, totv);
275
276 BM_verts_sort_radial_plane(vert_arr, totv);
277
278 /* create edges and find the winding (if faces are attached to any existing edges) */
279 f = BM_face_create_ngon_verts(bm, vert_arr, totv, nullptr, BM_CREATE_NO_DOUBLE, true, true);
280
281 if (f) {
283 f->mat_nr = mat_nr;
284 if (use_smooth) {
286 }
287 BM_face_copy_shared(bm, f, nullptr, nullptr);
289 }
290
291 MEM_freeN(vert_arr);
292 }
293}
#define LISTBASE_FOREACH(type, var, list)
Read Guarded memory(de)allocation.
@ BM_ELEM_SMOOTH
void BM_verts_sort_radial_plane(BMVert **vert_arr, int len)
void BM_face_copy_shared(BMesh *bm, BMFace *f, BMLoopFilterFunc filter_fn, void *user_data)
copies face loop data from shared adjacent faces.
BMFace * BM_face_create_ngon_verts(BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool calc_winding, const bool create_edges)
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
@ BM_CREATE_NO_DOUBLE
Definition bmesh_core.hh:26
#define BM_elem_flag_enable(ele, hflag)
int BMO_iter_elem_count_flag(BMesh *bm, const char itype, void *data, const short oflag, const bool value)
Elem Iter Tool Flag Count.
int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, void **array, const int len)
Operator Iterator as Array.
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
BMO_FLAG_BUFFER.
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_vert_flag_enable(bm, e, oflag)
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
#define BMO_face_flag_enable(bm, e, oflag)
#define BMO_slot_copy(op_src, slots_src, slot_name_src, op_dst, slots_dst, slot_name_dst)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
#define BMO_vert_flag_test(bm, e, oflag)
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
int BMO_slot_int_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
int BMO_slot_buffer_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define ELE_NEW
Definition bmo_create.cc:19
#define ELE_OUT
Definition bmo_create.cc:20
void bmo_contextual_create_exec(BMesh *bm, BMOperator *op)
Definition bmo_create.cc:22
static double op_sub(double a, double b)
static float verts[][3]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
short mat_nr
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
ListBase selected