Blender V5.0
MOD_boolean.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_utildefines.h"
10
11#include "BLI_array.hh"
12#include "BLI_math_geom.h"
13#include "BLI_math_matrix.h"
15#include "BLI_vector.hh"
16#include "BLI_vector_set.hh"
17
18#include "BLT_translation.hh"
19
20#include "DNA_defaults.h"
21#include "DNA_mesh_types.h"
22#include "DNA_object_types.h"
23#include "DNA_scene_types.h"
24#include "DNA_screen_types.h"
25
26#include "BKE_collection.hh"
27#include "BKE_global.hh" /* only to check G.debug */
28#include "BKE_lib_id.hh"
29#include "BKE_lib_query.hh"
30#include "BKE_material.hh"
31#include "BKE_mesh.hh"
32#include "BKE_mesh_wrapper.hh"
33#include "BKE_modifier.hh"
34
36#include "UI_resources.hh"
37
38#include "RNA_access.hh"
39#include "RNA_prototypes.hh"
40
41#include "MOD_ui_common.hh"
42
43#include "MEM_guardedalloc.h"
44
45#include "GEO_mesh_boolean.hh"
46#include "GEO_randomize.hh"
47
48#include "bmesh.hh"
50
51// #define DEBUG_TIME
52
53#ifdef DEBUG_TIME
54# include "BLI_timeit.hh"
55#endif
56
57using blender::Array;
58using blender::float3;
62using blender::Span;
63using blender::Vector;
65
74
75static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
76{
79
81 return !bmd->object || bmd->object->type != OB_MESH;
82 }
84 /* The Exact solver tolerates an empty collection. */
86 }
87 return false;
88}
89
90static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
91{
93
94 walk(user_data, ob, (ID **)&bmd->collection, IDWALK_CB_USER);
95 walk(user_data, ob, (ID **)&bmd->object, IDWALK_CB_NOP);
96}
97
99{
101 if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != nullptr) {
102 DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
103 DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
104 }
105
106 Collection *col = bmd->collection;
107
108 if ((bmd->flag & eBooleanModifierFlag_Collection) && col != nullptr) {
109 DEG_add_collection_geometry_relation(ctx->node, col, "Boolean Modifier");
110 }
111 /* We need own transformation as well. */
112 DEG_add_depends_on_transform_relation(ctx->node, "Boolean Modifier");
113}
114
116 Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
117{
118 Mesh *result = nullptr;
119
120 if (mesh_self->faces_num == 0 || mesh_operand_ob->faces_num == 0) {
121 switch (operation) {
123 result = BKE_mesh_new_nomain(0, 0, 0, 0);
124 break;
125
127 if (mesh_self->faces_num != 0) {
128 result = mesh_self;
129 }
130 else {
132 nullptr, &mesh_operand_ob->id, nullptr, LIB_ID_COPY_LOCALIZE);
133
134 float imat[4][4];
135 float omat[4][4];
136 invert_m4_m4(imat, ob_self->object_to_world().ptr());
137 mul_m4_m4m4(omat, imat, ob_operand_ob->object_to_world().ptr());
138
139 MutableSpan<float3> positions = result->vert_positions_for_write();
140 for (const int i : positions.index_range()) {
141 mul_m4_v3(omat, positions[i]);
142 }
143
144 result->tag_positions_changed();
145 }
146
147 break;
148
150 result = mesh_self;
151 break;
152 }
153 }
154
155 return result;
156}
157
158/* has no meaning for faces, do this so we can tell which face is which */
159#define BM_FACE_TAG BM_ELEM_SELECT_UV
160
164static int bm_face_isect_pair(BMFace *f, void * /*user_data*/)
165{
166 return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
167}
168
169static bool BMD_error_messages(const Object *ob, ModifierData *md)
170{
172 Collection *col = bmd->collection;
173
174 bool error_returns_result = false;
175
176 const bool operand_collection = (bmd->flag & eBooleanModifierFlag_Collection) != 0;
177 const bool use_exact = bmd->solver == eBooleanModifierSolver_Mesh_Arr;
178 const bool use_manifold = bmd->solver == eBooleanModifierSolver_Manifold;
179 const bool operation_intersect = bmd->operation == eBooleanModifierOp_Intersect;
180
181#ifndef WITH_GMP
182 /* If compiled without GMP, return a error. */
183 if (use_exact) {
184 BKE_modifier_set_error(ob, md, "Compiled without GMP, using fast solver");
185 error_returns_result = false;
186 }
187#endif
188
189 /* If intersect is selected using fast solver, return a error. */
190 if (operand_collection && operation_intersect && !(use_exact || use_manifold)) {
191 BKE_modifier_set_error(ob, md, "Cannot execute, intersect only available using exact solver");
192 error_returns_result = true;
193 }
194
195 /* If the selected collection is empty and using fast solver, return a error. */
196 if (operand_collection) {
197 if (!use_exact && BKE_collection_is_empty(col)) {
198 BKE_modifier_set_error(ob, md, "Cannot execute, non-exact solver and empty collection");
199 error_returns_result = true;
200 }
201
202 /* If the selected collection contain non mesh objects, return a error. */
203 if (col) {
205 if (operand_ob->type != OB_MESH) {
207 ob, md, "Cannot execute, the selected collection contains non mesh objects");
208 error_returns_result = true;
209 }
210 }
212 }
213 }
214
215 return error_returns_result;
216}
217
219 Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
220{
221#ifdef DEBUG_TIME
222 SCOPED_TIMER(__func__);
223#endif
224
225 *r_is_flip = (is_negative_m4(object->object_to_world().ptr()) !=
226 is_negative_m4(operand_ob->object_to_world().ptr()));
227
228 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob);
229
230 BMeshCreateParams bmesh_create_params{};
231 BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params);
232
233 /* Keep `mesh` first, needed so active layers are set based on `mesh` not `mesh_operand_ob`,
234 * otherwise the wrong active render layer is used, see #92384.
235 *
236 * NOTE: while initializing customer data layers the is not essential,
237 * it avoids the overhead of having to re-allocate #BMHeader.data when the 2nd mesh is added
238 * (if it contains additional custom-data layers). */
239 const Mesh *mesh_array[2] = {mesh, mesh_operand_ob};
240 BM_mesh_copy_init_customdata_from_mesh_array(bm, mesh_array, ARRAY_SIZE(mesh_array), &allocsize);
241
242 BMeshFromMeshParams bmesh_from_mesh_params{};
243 bmesh_from_mesh_params.calc_face_normal = true;
244 bmesh_from_mesh_params.calc_vert_normal = true;
245 BM_mesh_bm_from_me(bm, mesh_operand_ob, &bmesh_from_mesh_params);
246
247 if (UNLIKELY(*r_is_flip)) {
248 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
249 BMIter iter;
250 BMFace *efa;
251 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
252 BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
253 }
254 }
255
256 BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params);
257
258 return bm;
259}
260
262 ModifierData *md,
263 const ModifierEvalContext *ctx,
264 Mesh *mesh_operand_ob,
265 Object *object,
266 Object *operand_ob,
267 bool is_flip)
268{
269#ifdef DEBUG_TIME
270 SCOPED_TIMER(__func__);
271#endif
272
274
275 /* Main BMesh intersection setup. */
276 /* Create tessellation & intersect. */
277 const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
278 blender::Array<std::array<BMLoop *, 3>> looptris(looptris_tot);
280
281 /* postpone this until after tessellating
282 * so we can use the original normals before the vertex are moved */
283 {
284 BMIter iter;
285 int i;
286 const int i_verts_end = mesh_operand_ob->verts_num;
287 const int i_faces_end = mesh_operand_ob->faces_num;
288
289 float imat[4][4];
290 float omat[4][4];
291 invert_m4_m4(imat, object->object_to_world().ptr());
292 mul_m4_m4m4(omat, imat, operand_ob->object_to_world().ptr());
293
294 BMVert *eve;
295 i = 0;
296 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
297 mul_m4_v3(omat, eve->co);
298 if (++i == i_verts_end) {
299 break;
300 }
301 }
302
303 /* we need face normals because of 'BM_face_split_edgenet'
304 * we could calculate on the fly too (before calling split). */
305 float nmat[3][3];
306 copy_m3_m4(nmat, omat);
307 invert_m3(nmat);
308
309 if (UNLIKELY(is_flip)) {
310 negate_m3(nmat);
311 }
312
313 Array<short> material_remap(operand_ob->totcol ? operand_ob->totcol : 1);
314
315 /* Using original (not evaluated) object here since we are writing to it. */
316 /* XXX Pretty sure comment above is fully wrong now with copy-on-eval & co ? */
317 BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap.data());
318
319 BMFace *efa;
320 i = 0;
321 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
322 mul_transposed_m3_v3(nmat, efa->no);
323 normalize_v3(efa->no);
324
325 /* Temp tag to test which side split faces are from. */
327
328 /* remap material */
329 if (LIKELY(efa->mat_nr < operand_ob->totcol)) {
330 efa->mat_nr = material_remap[efa->mat_nr];
331 }
332 else {
333 efa->mat_nr = 0;
334 }
335
336 if (++i == i_faces_end) {
337 break;
338 }
339 }
340 }
341
342 /* not needed, but normals for 'dm' will be invalid,
343 * currently this is ok for 'BM_mesh_intersect' */
344 // BM_mesh_normals_update(bm);
345
346 bool use_separate = false;
347 bool use_dissolve = true;
348 bool use_island_connect = true;
349
350 /* change for testing */
351 if (G.debug & G_DEBUG) {
352 use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0;
353 use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0;
354 use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == 0;
355 }
356
358 looptris,
360 nullptr,
361 false,
362 use_separate,
363 use_dissolve,
364 use_island_connect,
365 false,
366 false,
367 bmd->operation,
368 bmd->double_threshold);
369}
370
371#ifdef WITH_GMP
372
373/* Get a mapping from material slot numbers in the src_ob to slot numbers in the dst_ob.
374 * If a material doesn't exist in the dst_ob, the mapping just goes to the same slot
375 * or to zero if there aren't enough slots in the destination. */
376static Array<short> get_material_remap_index_based(Object *dest_ob, Object *src_ob)
377{
378 const int n = src_ob->totcol;
379 if (n <= 0) {
380 return Array<short>(1, 0);
381 }
382 Array<short> remap(n);
383 BKE_object_material_remap_calc(dest_ob, src_ob, remap.data());
384 return remap;
385}
386
387/* Get a mapping from material slot numbers in the source geometry to slot numbers in the result
388 * geometry. The material is added to the result geometry if it doesn't already use it. */
389static Array<short> get_material_remap_transfer(Object &object,
390 const Mesh &mesh,
391 VectorSet<Material *> &materials)
392{
393 const int material_num = mesh.totcol;
394 Array<short> map(material_num);
395 for (const int i : IndexRange(material_num)) {
396 Material *material = BKE_object_material_get_eval(&object, i + 1);
397 map[i] = material ? materials.index_of_or_add(material) : -1;
398 }
399 return map;
400}
401
402static Mesh *non_float_boolean_mesh(BooleanModifierData *bmd,
403 const ModifierEvalContext *ctx,
404 Mesh *mesh)
405{
406 const float4x4 &world_to_object = ctx->object->world_to_object();
408 Vector<float4x4> transforms;
409
410 Vector<Array<short>> material_remaps;
411
412# ifdef DEBUG_TIME
413 SCOPED_TIMER(__func__);
414# endif
415
416 if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object == nullptr) {
417 return mesh;
418 }
419
423 meshes.append(mesh);
424 transforms.append(float4x4::identity());
425 material_remaps.append({});
426
428 bmd->material_mode);
429 VectorSet<Material *> materials;
430 if (material_mode == eBooleanModifierMaterialMode_Transfer) {
431 if (mesh->totcol == 0) {
432 /* Necessary for faces using the default material when there are no material slots. */
433 materials.add(nullptr);
434 }
435 else {
436 materials.add_multiple({mesh->mat, mesh->totcol});
437 }
438 }
439
442 if (!mesh_operand) {
443 return mesh;
444 }
445 BKE_mesh_wrapper_ensure_mdata(mesh_operand);
446 meshes.append(mesh_operand);
447 transforms.append(world_to_object * bmd->object->object_to_world());
448 if (material_mode == eBooleanModifierMaterialMode_Index) {
449 material_remaps.append(get_material_remap_index_based(ctx->object, bmd->object));
450 }
451 else {
452 material_remaps.append(get_material_remap_transfer(*bmd->object, *mesh_operand, materials));
453 }
454 }
455 else if (bmd->flag & eBooleanModifierFlag_Collection) {
456 Collection *collection = bmd->collection;
457 /* Allow collection to be empty; then target mesh will just removed self-intersections. */
458 if (collection) {
460 if (ob->type == OB_MESH && ob != ctx->object) {
462 if (!collection_mesh) {
463 continue;
464 }
465 BKE_mesh_wrapper_ensure_mdata(collection_mesh);
466 meshes.append(collection_mesh);
467 transforms.append(world_to_object * ob->object_to_world());
468 if (material_mode == eBooleanModifierMaterialMode_Index) {
469 material_remaps.append(get_material_remap_index_based(ctx->object, ob));
470 }
471 else {
472 material_remaps.append(get_material_remap_transfer(*ob, *collection_mesh, materials));
473 }
474 }
475 }
477 }
478 }
479
480 const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
481 const bool hole_tolerant = (bmd->flag & eBooleanModifierFlag_HoleTolerant) != 0;
484 op_params.no_self_intersections = !use_self;
485 op_params.watertight = !hole_tolerant;
486 op_params.no_nested_components = false;
490 meshes, transforms, material_remaps, op_params, solver, nullptr, &error);
491
495 ctx->object, (ModifierData *)bmd, "Cannot execute, non-manifold inputs");
496 }
498 BKE_modifier_set_error(ctx->object, (ModifierData *)(bmd), "Cannot execute, unknown error");
499 }
500 return result;
501 }
502 if (material_mode == eBooleanModifierMaterialMode_Transfer) {
503 MEM_SAFE_FREE(result->mat);
504 result->mat = MEM_malloc_arrayN<Material *>(size_t(materials.size()), __func__);
505 result->totcol = materials.size();
506 MutableSpan(result->mat, result->totcol).copy_from(materials);
507 }
508
510
511 return result;
512}
513#endif
514
515static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
516{
518 Object *object = ctx->object;
519 Mesh *result = mesh;
520 Collection *collection = bmd->collection;
521
522 /* Return result for certain errors. */
523 if (BMD_error_messages(ctx->object, md)) {
524 return result;
525 }
526
527#ifdef WITH_GMP
529 return non_float_boolean_mesh(bmd, ctx, mesh);
530 }
531#endif
532
533#ifdef DEBUG_TIME
534 SCOPED_TIMER(__func__);
535#endif
536
538 if (bmd->object == nullptr) {
539 return result;
540 }
541
542 Object *operand_ob = bmd->object;
543
544 Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob);
545
546 if (mesh_operand_ob) {
547 /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
548 * But for 2.90 better not try to be smart here. */
549 BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
550 /* when one of objects is empty (has got no faces) we could speed up
551 * calculation a bit returning one of objects' derived meshes (or empty one)
552 * Returning mesh is dependent on modifiers operation (sergey) */
553 result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
554
555 if (result == nullptr) {
556 bool is_flip;
557 BMesh *bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
558
559 BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
560
562
564 }
565
566 if (result == nullptr) {
567 BKE_modifier_set_error(object, md, "Cannot execute boolean operation");
568 }
569 }
570 }
571 else {
572 if (collection == nullptr) {
573 return result;
574 }
575
576 FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, operand_ob) {
577 if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
578 Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob);
579
580 if (mesh_operand_ob == nullptr) {
581 continue;
582 }
583
584 /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
585 * But for 2.90 better not try to be smart here. */
586 BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
587
588 bool is_flip;
589 BMesh *bm = BMD_mesh_bm_create(result, object, mesh_operand_ob, operand_ob, &is_flip);
590
591 BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
592
593 /* Needed for multiple objects to work. */
594 if (result == mesh) {
596 }
597 else {
598 BMeshToMeshParams bmesh_to_mesh_params{};
599 bmesh_to_mesh_params.calc_object_remap = false;
600 BM_mesh_bm_to_me(nullptr, bm, result, &bmesh_to_mesh_params);
601 }
603 }
604 }
606 }
607
609
610 return result;
611}
612
613static void required_data_mask(ModifierData * /*md*/, CustomData_MeshMasks *r_cddata_masks)
614{
615 r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
616 r_cddata_masks->fmask |= CD_MASK_MTFACE;
617}
618
619static void panel_draw(const bContext * /*C*/, Panel *panel)
620{
621 uiLayout *layout = panel->layout;
623
624 layout->prop(ptr, "operation", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
625
626 layout->use_property_split_set(true);
627
628 layout->prop(ptr, "operand_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
629 if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
630 layout->prop(ptr, "object", UI_ITEM_NONE, std::nullopt, ICON_NONE);
631 }
632 else {
633 layout->prop(ptr, "collection", UI_ITEM_NONE, std::nullopt, ICON_NONE);
634 }
635
636 layout->prop(ptr, "solver", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
637
639}
640
641static void solver_options_panel_draw(const bContext * /*C*/, Panel *panel)
642{
643 uiLayout *layout = panel->layout;
645
646 const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Mesh_Arr;
647 const bool use_manifold = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Manifold;
648
649 layout->use_property_split_set(true);
650
651 uiLayout *col = &layout->column(true);
652 if (use_exact) {
653 col->prop(ptr, "material_mode", UI_ITEM_NONE, IFACE_("Materials"), ICON_NONE);
654 /* When operand is collection, we always use_self. */
655 if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
656 col->prop(ptr, "use_self", UI_ITEM_NONE, std::nullopt, ICON_NONE);
657 }
658 col->prop(ptr, "use_hole_tolerant", UI_ITEM_NONE, std::nullopt, ICON_NONE);
659 }
660 else if (use_manifold) {
661 col->prop(ptr, "material_mode", UI_ITEM_NONE, IFACE_("Materials"), ICON_NONE);
662 }
663 else {
664 col->prop(ptr, "double_threshold", UI_ITEM_NONE, std::nullopt, ICON_NONE);
665 }
666
667 if (G.debug) {
668 col->prop(ptr, "debug_options", UI_ITEM_NONE, std::nullopt, ICON_NONE);
669 }
670}
671
672static void panel_register(ARegionType *region_type)
673{
676 region_type, "solver_options", "Solver Options", nullptr, solver_options_panel_draw, panel);
677}
678
680 /*idname*/ "Boolean",
681 /*name*/ N_("Boolean"),
682 /*struct_name*/ "BooleanModifierData",
683 /*struct_size*/ sizeof(BooleanModifierData),
684 /*srna*/ &RNA_BooleanModifier,
686 /*flags*/
688 /*icon*/ ICON_MOD_BOOLEAN,
689
690 /*copy_data*/ BKE_modifier_copydata_generic,
691
692 /*deform_verts*/ nullptr,
693 /*deform_matrices*/ nullptr,
694 /*deform_verts_EM*/ nullptr,
695 /*deform_matrices_EM*/ nullptr,
696 /*modify_mesh*/ modify_mesh,
697 /*modify_geometry_set*/ nullptr,
698
699 /*init_data*/ init_data,
700 /*required_data_mask*/ required_data_mask,
701 /*free_data*/ nullptr,
702 /*is_disabled*/ is_disabled,
703 /*update_depsgraph*/ update_depsgraph,
704 /*depends_on_time*/ nullptr,
705 /*depends_on_normals*/ nullptr,
706 /*foreach_ID_link*/ foreach_ID_link,
707 /*foreach_tex_link*/ nullptr,
708 /*free_runtime_data*/ nullptr,
709 /*panel_register*/ panel_register,
710 /*blend_write*/ nullptr,
711 /*blend_read*/ nullptr,
712 /*foreach_cache*/ nullptr,
713 /*foreach_working_space_color*/ nullptr,
714};
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_END
bool BKE_collection_is_empty(const Collection *collection)
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(_collection, _object)
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
@ G_DEBUG
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:777
@ LIB_ID_COPY_LOCALIZE
@ IDWALK_CB_USER
@ IDWALK_CB_NOP
General operations, lookup, etc. for materials.
void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap_src_to_dst)
Material * BKE_object_material_get_eval(Object *ob, short act)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
Mesh * BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings)
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
Mesh * BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval)
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_SupportsEditmode
@ eModifierTypeFlag_AcceptsMesh
void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
void negate_m3(float R[3][3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void mul_m4_v3(const float M[4][4], float r[3])
bool is_negative_m4(const float mat[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_transposed_m3_v3(const float M[3][3], float r[3])
bool invert_m3(float mat[3][3])
MINLINE float normalize_v3(float n[3])
#define SCOPED_TIMER(name)
Definition BLI_timeit.hh:70
#define ARRAY_SIZE(arr)
#define UNLIKELY(x)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define LIKELY(x)
#define IFACE_(msgid)
void DEG_add_depends_on_transform_relation(DepsNodeHandle *node_handle, const char *description)
void DEG_add_collection_geometry_relation(DepsNodeHandle *node_handle, Collection *collection, const char *description)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_GEOMETRY
@ DEG_OB_COMP_TRANSFORM
#define CD_MASK_MDEFORMVERT
#define CD_MASK_MTFACE
#define DNA_struct_default_get(struct_name)
@ eBooleanModifierSolver_Float
@ eBooleanModifierSolver_Manifold
@ eBooleanModifierSolver_Mesh_Arr
BooleanModifierMaterialMode
@ eBooleanModifierMaterialMode_Index
@ eBooleanModifierMaterialMode_Transfer
@ eBooleanModifierBMeshFlag_BMesh_NoDissolve
@ eBooleanModifierBMeshFlag_BMesh_Separate
@ eBooleanModifierBMeshFlag_BMesh_NoConnectRegions
@ eBooleanModifierFlag_Collection
@ eBooleanModifierFlag_Self
@ eBooleanModifierFlag_HoleTolerant
@ eBooleanModifierFlag_Object
@ eModifierType_Boolean
@ eBooleanModifierOp_Intersect
@ eBooleanModifierOp_Union
@ eBooleanModifierOp_Difference
Object is a sort of wrapper for general info.
@ OB_MESH
static bool is_disabled
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
static void init_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void required_data_mask(ModifierData *, CustomData_MeshMasks *r_cddata_masks)
static void panel_draw(const bContext *, Panel *panel)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
Definition MOD_array.cc:862
static void init_data(ModifierData *md)
static void solver_options_panel_draw(const bContext *, Panel *panel)
static void panel_register(ARegionType *region_type)
static BMesh * BMD_mesh_bm_create(Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
static void required_data_mask(ModifierData *, CustomData_MeshMasks *r_cddata_masks)
static void BMD_mesh_intersection(BMesh *bm, ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh_operand_ob, Object *object, Object *operand_ob, bool is_flip)
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
static Mesh * get_quick_mesh(Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
static bool BMD_error_messages(const Object *ob, ModifierData *md)
static void panel_draw(const bContext *, Panel *panel)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
ModifierTypeInfo modifierType_Boolean
static int bm_face_isect_pair(BMFace *f, void *)
PanelType * modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelType *parent)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
@ UI_ITEM_R_EXPAND
#define UI_ITEM_NONE
void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst, const Mesh *me_src_array[], const int me_src_array_len, const BMAllocTemplate *allocsize)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
bool BM_mesh_intersect(BMesh *bm, const blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *f, void *user_data), void *user_data, const bool use_self, const bool use_separate, const bool use_dissolve, const bool use_island_connect, const bool use_partial_connect, const bool use_edge_tag, const int boolean_mode, const float eps)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh * bm
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
#define BMALLOC_TEMPLATE_FROM_ME(...)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
void BM_mesh_calc_tessellation_beauty(BMesh *bm, MutableSpan< std::array< BMLoop *, 3 > > looptris)
void BM_face_normal_flip_ex(BMesh *bm, BMFace *f, const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip)
Face Flip Normal.
const T * data() const
Definition BLI_array.hh:312
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
bool add(const Key &key)
int64_t index_of_or_add(const Key &key)
void add_multiple(Span< Key > keys)
int64_t size() const
void append(const T &value)
static int bm_face_isect_pair(BMFace *f, void *)
uint col
static void update_depsgraph(tGraphSliderOp *gso)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
#define G(x, y, z)
static void error(const char *str)
Mesh * mesh_boolean(Span< const Mesh * > meshes, Span< float4x4 > transforms, Span< Array< short > > material_remaps, BooleanOpParameters op_params, Solver solver, Vector< int > *r_intersecting_edges, BooleanError *r_error)
void debug_randomize_mesh_order(Mesh *mesh)
Definition randomize.cc:288
MatBase< float, 4, 4 > float4x4
VecBase< float, 3 > float3
int RNA_enum_get(PointerRNA *ptr, const char *name)
short mat_nr
float no[3]
float co[3]
struct Collection * collection
Definition DNA_ID.h:414
struct Material ** mat
short totcol
int faces_num
int verts_num
struct uiLayout * layout
uiLayout & column(bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238