Blender V4.3
geometry/intern/mesh_boolean.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <iostream>
6
7#include "BKE_attribute.hh"
8#include "BKE_customdata.hh"
9#include "BKE_lib_id.hh"
10#include "BKE_mesh.hh"
11
12#include "BLI_alloca.h"
13#include "BLI_array.hh"
14#include "BLI_math_geom.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_matrix.hh"
17#include "BLI_math_vector.h"
18#include "BLI_mesh_boolean.hh"
19#include "BLI_mesh_intersect.hh"
20#include "BLI_span.hh"
21#include "BLI_string.h"
22#include "BLI_task.hh"
23#include "BLI_virtual_array.hh"
24
25#include "DNA_node_types.h"
26
27#include "GEO_mesh_boolean.hh"
28
29#include "bmesh.hh"
30#include "bmesh_tools.hh"
33
35
36/* -------------------------------------------------------------------- */
40#ifdef WITH_GMP
41
42constexpr int estimated_max_facelen = 100; /* Used for initial size of some Vectors. */
43
44/* Snap entries that are near 0 or 1 or -1 to those values.
45 * Sometimes Blender's rotation matrices for multiples of 90 degrees have
46 * tiny numbers where there should be zeros. That messes makes some things
47 * every so slightly non-coplanar when users expect coplanarity,
48 * so this is a hack to clean up such matrices.
49 * Would be better to change the transformation code itself.
50 */
51static float4x4 clean_transform(const float4x4 &mat)
52{
53 float4x4 cleaned;
54 const float fuzz = 1e-6f;
55 for (int i = 0; i < 4; i++) {
56 for (int j = 0; j < 4; j++) {
57 float f = mat[i][j];
58 if (fabsf(f) <= fuzz) {
59 f = 0.0f;
60 }
61 else if (fabsf(f - 1.0f) <= fuzz) {
62 f = 1.0f;
63 }
64 else if (fabsf(f + 1.0f) <= fuzz) {
65 f = -1.0f;
66 }
67 cleaned[i][j] = f;
68 }
69 }
70 return cleaned;
71}
72
73/* `MeshesToIMeshInfo` keeps track of information used when combining a number
74 * of `Mesh`es into a single `IMesh` for doing boolean on.
75 * Mostly this means keeping track of the index offsets for various mesh elements. */
76class MeshesToIMeshInfo {
77 public:
78 /* The input meshes, */
79 Span<const Mesh *> meshes;
80 /* Numbering the vertices of the meshes in order of meshes,
81 * at what offset does the vertex range for mesh[i] start? */
82 Array<int> mesh_vert_offset;
83 /* Similarly for edges of meshes. */
84 Array<int> mesh_edge_offset;
85 /* Similarly for faces of meshes. */
86 Array<int> mesh_face_offset;
87 /* For each Mesh vertex in all the meshes (with concatenated indexing),
88 * what is the IMesh Vert* allocated for it in the input IMesh? */
89 Array<const meshintersect::Vert *> mesh_to_imesh_vert;
90 /* Similarly for each Mesh face. */
91 Array<meshintersect::Face *> mesh_to_imesh_face;
92 /* Transformation matrix to transform a coordinate in the corresponding
93 * Mesh to the local space of the first Mesh. */
94 Array<float4x4> to_target_transform;
95 /* For each input mesh, whether or not their transform is negative. */
96 Array<bool> has_negative_transform;
97 /* For each input mesh, how to remap the material slot numbers to
98 * the material slots in the first mesh. */
99 Span<Array<short>> material_remaps;
100 /* Total number of input mesh vertices. */
101 int tot_meshes_verts;
102 /* Total number of input mesh edges. */
103 int tot_meshes_edges;
104 /* Total number of input mesh polys. */
105 int tot_meshes_polys;
106
107 int input_mesh_for_imesh_vert(int imesh_v) const;
108 int input_mesh_for_imesh_edge(int imesh_e) const;
109 int input_mesh_for_imesh_face(int imesh_f) const;
110 const IndexRange input_face_for_orig_index(int orig_index,
111 const Mesh **r_orig_mesh,
112 int *r_orig_mesh_index,
113 int *r_index_in_orig_mesh) const;
114 void input_mvert_for_orig_index(int orig_index,
115 const Mesh **r_orig_mesh,
116 int *r_index_in_orig_mesh) const;
117 void input_medge_for_orig_index(int orig_index,
118 const Mesh **r_orig_mesh,
119 int *r_index_in_orig_mesh) const;
120};
121
122/* Given an index `imesh_v` in the `IMesh`, return the index of the
123 * input `Mesh` that contained the vertex that it came from. */
124int MeshesToIMeshInfo::input_mesh_for_imesh_vert(int imesh_v) const
125{
126 int n = int(mesh_vert_offset.size());
127 for (int i = 0; i < n - 1; ++i) {
128 if (imesh_v < mesh_vert_offset[i + 1]) {
129 return i;
130 }
131 }
132 return n - 1;
133}
134
135/* Given an index `imesh_e` used as an original index in the `IMesh`,
136 * return the index of the input `Mesh` that contained the vertex that it came from. */
137int MeshesToIMeshInfo::input_mesh_for_imesh_edge(int imesh_e) const
138{
139 int n = int(mesh_edge_offset.size());
140 for (int i = 0; i < n - 1; ++i) {
141 if (imesh_e < mesh_edge_offset[i + 1]) {
142 return i;
143 }
144 }
145 return n - 1;
146}
147
148/* Given an index `imesh_f` in the `IMesh`, return the index of the
149 * input `Mesh` that contained the face that it came from. */
150int MeshesToIMeshInfo::input_mesh_for_imesh_face(int imesh_f) const
151{
152 int n = int(mesh_face_offset.size());
153 for (int i = 0; i < n - 1; ++i) {
154 if (imesh_f < mesh_face_offset[i + 1]) {
155 return i;
156 }
157 }
158 return n - 1;
159}
160
161/* Given an index of an original face in the `IMesh`, find out the input
162 * `Mesh` that it came from and return it in `*r_orig_mesh`,
163 * and also return the index of that `Mesh` in `*r_orig_mesh_index`.
164 * Finally, return the index of the corresponding face in that `Mesh`
165 * in `*r_index_in_orig_mesh`. */
166const IndexRange MeshesToIMeshInfo::input_face_for_orig_index(int orig_index,
167 const Mesh **r_orig_mesh,
168 int *r_orig_mesh_index,
169 int *r_index_in_orig_mesh) const
170{
171 int orig_mesh_index = input_mesh_for_imesh_face(orig_index);
172 BLI_assert(0 <= orig_mesh_index && orig_mesh_index < meshes.size());
173 const Mesh *mesh = meshes[orig_mesh_index];
174 const OffsetIndices faces = mesh->faces();
175 int index_in_mesh = orig_index - mesh_face_offset[orig_mesh_index];
176 BLI_assert(0 <= index_in_mesh && index_in_mesh < mesh->faces_num);
177 const IndexRange face = faces[index_in_mesh];
178 if (r_orig_mesh) {
179 *r_orig_mesh = mesh;
180 }
181 if (r_orig_mesh_index) {
182 *r_orig_mesh_index = orig_mesh_index;
183 }
184 if (r_index_in_orig_mesh) {
185 *r_index_in_orig_mesh = index_in_mesh;
186 }
187 return face;
188}
189
190/* Given an index of an original vertex in the `IMesh`, find out the input
191 * `Mesh` that it came from and return it in `*r_orig_mesh`.
192 * Also find the index of the vertex in that `Mesh` and return it in
193 * `*r_index_in_orig_mesh`. */
194void MeshesToIMeshInfo::input_mvert_for_orig_index(int orig_index,
195 const Mesh **r_orig_mesh,
196 int *r_index_in_orig_mesh) const
197{
198 int orig_mesh_index = input_mesh_for_imesh_vert(orig_index);
199 BLI_assert(0 <= orig_mesh_index && orig_mesh_index < meshes.size());
200 const Mesh *mesh = meshes[orig_mesh_index];
201 int index_in_mesh = orig_index - mesh_vert_offset[orig_mesh_index];
202 BLI_assert(0 <= index_in_mesh && index_in_mesh < mesh->verts_num);
203 if (r_orig_mesh) {
204 *r_orig_mesh = mesh;
205 }
206 if (r_index_in_orig_mesh) {
207 *r_index_in_orig_mesh = index_in_mesh;
208 }
209}
210
211/* Similarly for edges. */
212void MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
213 const Mesh **r_orig_mesh,
214 int *r_index_in_orig_mesh) const
215{
216 int orig_mesh_index = input_mesh_for_imesh_edge(orig_index);
217 BLI_assert(0 <= orig_mesh_index && orig_mesh_index < meshes.size());
218 const Mesh *mesh = meshes[orig_mesh_index];
219 int index_in_mesh = orig_index - mesh_edge_offset[orig_mesh_index];
220 BLI_assert(0 <= index_in_mesh && index_in_mesh < mesh->edges_num);
221 if (r_orig_mesh) {
222 *r_orig_mesh = mesh;
223 }
224 if (r_index_in_orig_mesh) {
225 *r_index_in_orig_mesh = index_in_mesh;
226 }
227}
228
241static meshintersect::IMesh meshes_to_imesh(Span<const Mesh *> meshes,
242 Span<float4x4> obmats,
243 Span<Array<short>> material_remaps,
244 const float4x4 &target_transform,
245 meshintersect::IMeshArena &arena,
246 MeshesToIMeshInfo *r_info)
247{
248 int nmeshes = meshes.size();
249 BLI_assert(nmeshes > 0);
250 r_info->meshes = meshes;
251 r_info->tot_meshes_verts = 0;
252 r_info->tot_meshes_polys = 0;
253 int &totvert = r_info->tot_meshes_verts;
254 int &totedge = r_info->tot_meshes_edges;
255 int &faces_num = r_info->tot_meshes_polys;
256 for (const Mesh *mesh : meshes) {
257 totvert += mesh->verts_num;
258 totedge += mesh->edges_num;
259 faces_num += mesh->faces_num;
260 }
261
262 /* Estimate the number of vertices and faces in the boolean output,
263 * so that the memory arena can reserve some space. It is OK if these
264 * estimates are wrong. */
265 const int estimate_num_outv = 3 * totvert;
266 const int estimate_num_outf = 4 * faces_num;
267 arena.reserve(estimate_num_outv, estimate_num_outf);
268 r_info->mesh_to_imesh_vert.reinitialize(totvert);
269 r_info->mesh_to_imesh_face.reinitialize(faces_num);
270 r_info->mesh_vert_offset.reinitialize(nmeshes);
271 r_info->mesh_edge_offset.reinitialize(nmeshes);
272 r_info->mesh_face_offset.reinitialize(nmeshes);
273 r_info->to_target_transform.reinitialize(nmeshes);
274 r_info->has_negative_transform.reinitialize(nmeshes);
275 r_info->material_remaps = material_remaps;
276 int v = 0;
277 int e = 0;
278 int f = 0;
279
280 /* Put these Vectors here, with a size unlikely to need resizing,
281 * so that the loop to make new Faces will likely not need to allocate
282 * over and over. */
283 Vector<const meshintersect::Vert *, estimated_max_facelen> face_vert;
284 Vector<int, estimated_max_facelen> face_edge_orig;
285
286 /* To convert the coordinates of meshes 1, 2, etc. into the local space
287 * of the target, multiply each transform by the inverse of the
288 * target matrix. Exact Boolean works better if these matrices are 'cleaned'
289 * -- see the comment for the `clean_transform` function, above. */
290 const float4x4 inv_target_mat = math::invert(clean_transform(target_transform));
291
292 /* For each input `Mesh`, make `Vert`s and `Face`s for the corresponding
293 * vertices and polygons, and keep track of the original indices (using the
294 * concatenating offset scheme) inside the `Vert`s and `Face`s.
295 * When making `Face`s, we also put in the original indices for edges that
296 * make up the polygons using the same scheme. */
297 for (int mi : meshes.index_range()) {
298 const Mesh *mesh = meshes[mi];
299 r_info->mesh_vert_offset[mi] = v;
300 r_info->mesh_edge_offset[mi] = e;
301 r_info->mesh_face_offset[mi] = f;
302 /* Get matrix that transforms a coordinate in meshes[mi]'s local space
303 * to the target space. */
304 const float4x4 objn_mat = obmats.is_empty() ? float4x4::identity() :
305 clean_transform(obmats[mi]);
306 r_info->to_target_transform[mi] = inv_target_mat * objn_mat;
307 r_info->has_negative_transform[mi] = math::is_negative(objn_mat);
308
309 /* All meshes 1 and up will be transformed into the local space of operand 0.
310 * Historical behavior of the modifier has been to flip the faces of any meshes
311 * that would have a negative transform if you do that. */
312 bool need_face_flip = r_info->has_negative_transform[mi] != r_info->has_negative_transform[0];
313
314 Vector<meshintersect::Vert *> verts(mesh->verts_num);
315 const Span<float3> vert_positions = mesh->vert_positions();
316 const OffsetIndices faces = mesh->faces();
317 const Span<int> corner_verts = mesh->corner_verts();
318 const Span<int> corner_edges = mesh->corner_edges();
319
320 /* Allocate verts
321 * Skip the matrix multiplication for each point when there is no transform for a mesh,
322 * for example when the first mesh is already in the target space. (Note the logic
323 * directly above, which uses an identity matrix with an empty input transform). */
324 if (obmats.is_empty() || r_info->to_target_transform[mi] == float4x4::identity()) {
325 threading::parallel_for(vert_positions.index_range(), 2048, [&](IndexRange range) {
326 for (int i : range) {
327 float3 co = vert_positions[i];
328 mpq3 mco = mpq3(co.x, co.y, co.z);
329 double3 dco(mco[0].get_d(), mco[1].get_d(), mco[2].get_d());
330 verts[i] = new meshintersect::Vert(mco, dco, meshintersect::NO_INDEX, i);
331 }
332 });
333 }
334 else {
335 threading::parallel_for(vert_positions.index_range(), 2048, [&](IndexRange range) {
336 for (int i : range) {
337 float3 co = math::transform_point(r_info->to_target_transform[mi], vert_positions[i]);
338 mpq3 mco = mpq3(co.x, co.y, co.z);
339 double3 dco(mco[0].get_d(), mco[1].get_d(), mco[2].get_d());
340 verts[i] = new meshintersect::Vert(mco, dco, meshintersect::NO_INDEX, i);
341 }
342 });
343 }
344 for (int i : vert_positions.index_range()) {
345 r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(verts[i]);
346 ++v;
347 }
348
349 for (const int face_i : faces.index_range()) {
350 const IndexRange face = faces[face_i];
351 int flen = face.size();
352 face_vert.resize(flen);
353 face_edge_orig.resize(flen);
354 for (int i = 0; i < flen; ++i) {
355 const int corner_i = face[i];
356 int mverti = r_info->mesh_vert_offset[mi] + corner_verts[corner_i];
357 const meshintersect::Vert *fv = r_info->mesh_to_imesh_vert[mverti];
358 if (need_face_flip) {
359 face_vert[flen - i - 1] = fv;
360 int iedge = i < flen - 1 ? flen - i - 2 : flen - 1;
361 face_edge_orig[iedge] = e + corner_edges[corner_i];
362 }
363 else {
364 face_vert[i] = fv;
365 face_edge_orig[i] = e + corner_edges[corner_i];
366 }
367 }
368 r_info->mesh_to_imesh_face[f] = arena.add_face(face_vert, f, face_edge_orig);
369 ++f;
370 }
371 e += mesh->edges_num;
372 }
373 return meshintersect::IMesh(r_info->mesh_to_imesh_face);
374}
375
376/* Copy vertex attributes, including customdata, from `orig_mv` to `mv`.
377 * `mv` is in `dest_mesh` with index `mv_index`.
378 * The `orig_mv` vertex came from Mesh `orig_me` and had index `index_in_orig_me` there. */
379static void copy_vert_attributes(Mesh *dest_mesh,
380 const Mesh *orig_me,
381 int mv_index,
382 int index_in_orig_me)
383{
384 /* For all layers in the orig mesh, copy the layer information. */
385 CustomData *target_cd = &dest_mesh->vert_data;
386 const CustomData *source_cd = &orig_me->vert_data;
387 for (int source_layer_i = 0; source_layer_i < source_cd->totlayer; ++source_layer_i) {
388 const eCustomDataType ty = eCustomDataType(source_cd->layers[source_layer_i].type);
389 if (StringRef(source_cd->layers->name) == "position") {
390 continue;
391 }
392 const char *name = source_cd->layers[source_layer_i].name;
393 int target_layer_i = CustomData_get_named_layer_index(target_cd, ty, name);
394 /* Not all layers were merged in target: some are marked CD_FLAG_NOCOPY
395 * and some are not in the CD_MASK_MESH.vdata. */
396 if (target_layer_i != -1) {
398 source_cd, target_cd, source_layer_i, target_layer_i, index_in_orig_me, mv_index, 1);
399 }
400 }
401}
402
403/* Similar to copy_vert_attributes but for face attributes. */
404static void copy_face_attributes(Mesh *dest_mesh,
405 const Mesh *orig_me,
406 int face_index,
407 int index_in_orig_me,
408 Span<short> material_remap,
409 MutableSpan<int> dst_material_indices)
410{
411 CustomData *target_cd = &dest_mesh->face_data;
412 const CustomData *source_cd = &orig_me->face_data;
413 for (int source_layer_i = 0; source_layer_i < source_cd->totlayer; ++source_layer_i) {
414 const eCustomDataType ty = eCustomDataType(source_cd->layers[source_layer_i].type);
415 const char *name = source_cd->layers[source_layer_i].name;
416 int target_layer_i = CustomData_get_named_layer_index(target_cd, ty, name);
417 if (target_layer_i != -1) {
419 source_cd, target_cd, source_layer_i, target_layer_i, index_in_orig_me, face_index, 1);
420 }
421 }
422
423 /* Fix material indices after they have been transferred as a generic attribute. */
424 const VArray<int> src_material_indices = *orig_me->attributes().lookup_or_default<int>(
425 "material_index", bke::AttrDomain::Face, 0);
426 const int src_index = src_material_indices[index_in_orig_me];
427 if (material_remap.index_range().contains(src_index)) {
428 const int remapped_index = material_remap[src_index];
429 dst_material_indices[face_index] = remapped_index >= 0 ? remapped_index : src_index;
430 }
431 else {
432 dst_material_indices[face_index] = src_index;
433 }
434 BLI_assert(dst_material_indices[face_index] >= 0);
435}
436
437/* Similar to copy_vert_attributes but for edge attributes. */
438static void copy_edge_attributes(Mesh *dest_mesh,
439 const Mesh *orig_me,
440 int medge_index,
441 int index_in_orig_me)
442{
443 CustomData *target_cd = &dest_mesh->edge_data;
444 const CustomData *source_cd = &orig_me->edge_data;
445 for (int source_layer_i = 0; source_layer_i < source_cd->totlayer; ++source_layer_i) {
446 const eCustomDataType ty = eCustomDataType(source_cd->layers[source_layer_i].type);
447 if (ty == CD_PROP_INT32_2D) {
448 if (STREQ(source_cd->layers[source_layer_i].name, ".edge_verts")) {
449 continue;
450 }
451 }
452 const char *name = source_cd->layers[source_layer_i].name;
453 int target_layer_i = CustomData_get_named_layer_index(target_cd, ty, name);
454 if (target_layer_i != -1) {
456 source_cd, target_cd, source_layer_i, target_layer_i, index_in_orig_me, medge_index, 1);
457 }
458 }
459}
460
471static int fill_orig_loops(const meshintersect::Face *f,
472 const IndexRange orig_face,
473 const Mesh *orig_me,
474 int orig_me_index,
475 MeshesToIMeshInfo &mim,
476 MutableSpan<int> r_orig_loops)
477{
478 r_orig_loops.fill(-1);
479 const Span<int> orig_corner_verts = orig_me->corner_verts();
480
481 int orig_mplen = orig_face.size();
482 if (f->size() != orig_mplen) {
483 return 0;
484 }
485 BLI_assert(r_orig_loops.size() == orig_mplen);
486 /* We'll look for the case where the first vertex in f has an original vertex
487 * that is the same as one in orig_me (after correcting for offset in mim meshes).
488 * Then see that loop and any subsequent ones have the same start and end vertex.
489 * This may miss some cases of partial alignment, but that's OK since discovering
490 * aligned loops is only an optimization to avoid some re-interpolation.
491 */
492 int first_orig_v = f->vert[0]->orig;
493 if (first_orig_v == meshintersect::NO_INDEX) {
494 return 0;
495 }
496 /* It is possible that the original vert was merged with another in another mesh. */
497 if (orig_me_index != mim.input_mesh_for_imesh_vert(first_orig_v)) {
498 return 0;
499 }
500 int orig_me_vert_offset = mim.mesh_vert_offset[orig_me_index];
501 int first_orig_v_in_orig_me = first_orig_v - orig_me_vert_offset;
502 BLI_assert(0 <= first_orig_v_in_orig_me && first_orig_v_in_orig_me < orig_me->verts_num);
503 /* Assume all vertices in each face is unique. */
504 int offset = -1;
505 for (int i = 0; i < orig_mplen; ++i) {
506 int loop_i = i + orig_face.start();
507 if (orig_corner_verts[loop_i] == first_orig_v_in_orig_me) {
508 offset = i;
509 break;
510 }
511 }
512 if (offset == -1) {
513 return 0;
514 }
515 int num_orig_loops_found = 0;
516 for (int mp_loop_index = 0; mp_loop_index < orig_mplen; ++mp_loop_index) {
517 int orig_mp_loop_index = (mp_loop_index + offset) % orig_mplen;
518 const int vert_i = orig_corner_verts[orig_face.start() + orig_mp_loop_index];
519 int fv_orig = f->vert[mp_loop_index]->orig;
520 if (fv_orig != meshintersect::NO_INDEX) {
521 fv_orig -= orig_me_vert_offset;
522 if (fv_orig < 0 || fv_orig >= orig_me->verts_num) {
523 fv_orig = meshintersect::NO_INDEX;
524 }
525 }
526 if (vert_i == fv_orig) {
527 const int vert_next =
528 orig_corner_verts[orig_face.start() + ((orig_mp_loop_index + 1) % orig_mplen)];
529 int fvnext_orig = f->vert[(mp_loop_index + 1) % orig_mplen]->orig;
530 if (fvnext_orig != meshintersect::NO_INDEX) {
531 fvnext_orig -= orig_me_vert_offset;
532 if (fvnext_orig < 0 || fvnext_orig >= orig_me->verts_num) {
533 fvnext_orig = meshintersect::NO_INDEX;
534 }
535 }
536 if (vert_next == fvnext_orig) {
537 r_orig_loops[mp_loop_index] = orig_face.start() + orig_mp_loop_index;
538 ++num_orig_loops_found;
539 }
540 }
541 }
542 return num_orig_loops_found;
543}
544
545/* Fill `cos_2d` with the 2d coordinates found by projection face `face` along
546 * its normal. Also fill in r_axis_mat with the matrix that does that projection.
547 * But before projecting, also transform the 3d coordinate by multiplying by trans_mat.
548 * `cos_2d` should have room for `face.size()` entries. */
549static void get_poly2d_cos(const Mesh *mesh,
550 const IndexRange face,
551 float (*cos_2d)[2],
552 const float4x4 &trans_mat,
553 float r_axis_mat[3][3])
554{
555 const Span<float3> positions = mesh->vert_positions();
556 const Span<int> corner_verts = mesh->corner_verts();
557 const Span<int> face_verts = corner_verts.slice(face);
558
559 /* Project coordinates to 2d in cos_2d, using normal as projection axis. */
560 const float3 axis_dominant = bke::mesh::face_normal_calc(positions, face_verts);
561 axis_dominant_v3_to_m3(r_axis_mat, axis_dominant);
562 for (const int i : face_verts.index_range()) {
563 float3 co = positions[face_verts[i]];
564 co = math::transform_point(trans_mat, co);
565 *reinterpret_cast<float2 *>(&cos_2d[i]) = (float3x3(r_axis_mat) * co).xy();
566 }
567}
568
569/* For the loops of `face`, see if the face is unchanged from `orig_face`, and if so,
570 * copy the Loop attributes from corresponding loops to corresponding loops.
571 * Otherwise, interpolate the Loop attributes in the face `orig_face`. */
572static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
573 const meshintersect::Face *f,
574 const IndexRange face,
575 const IndexRange orig_face,
576 const Mesh *orig_me,
577 int orig_me_index,
578 MeshesToIMeshInfo &mim)
579{
580 Array<int> orig_loops(face.size());
581 int norig = fill_orig_loops(f, orig_face, orig_me, orig_me_index, mim, orig_loops);
582 /* We may need these arrays if we have to interpolate Loop attributes rather than just copy.
583 * Right now, trying Array<float[2]> complains, so declare cos_2d a different way. */
584 float(*cos_2d)[2];
585 Array<float> weights;
586 Array<const void *> src_blocks_ofs;
587 float axis_mat[3][3];
588 if (norig != face.size()) {
589 /* We will need to interpolate. Make `cos_2d` hold 2d-projected coordinates of `orig_face`,
590 * which are transformed into object 0's local space before projecting.
591 * At this point we cannot yet calculate the interpolation weights, as they depend on
592 * the coordinate where interpolation is to happen, but we can allocate the needed arrays,
593 * so they don't have to be allocated per-layer. */
594 cos_2d = (float(*)[2])BLI_array_alloca(cos_2d, orig_face.size());
595 weights = Array<float>(orig_face.size());
596 src_blocks_ofs = Array<const void *>(orig_face.size());
597 get_poly2d_cos(orig_me, orig_face, cos_2d, mim.to_target_transform[orig_me_index], axis_mat);
598 }
599 CustomData *target_cd = &dest_mesh->corner_data;
600 const Span<float3> dst_positions = dest_mesh->vert_positions();
601 const Span<int> dst_corner_verts = dest_mesh->corner_verts();
602 for (int i = 0; i < face.size(); ++i) {
603 int loop_index = face[i];
604 int orig_loop_index = norig > 0 ? orig_loops[i] : -1;
605 const CustomData *source_cd = &orig_me->corner_data;
606 if (orig_loop_index == -1) {
607 /* Will need interpolation weights for this loop's vertex's coordinates.
608 * The coordinate needs to be projected into 2d, just like the interpolating face's
609 * coordinates were. The `dest_mesh` coordinates are already in object 0 local space. */
610 float co[2];
611 mul_v2_m3v3(co, axis_mat, dst_positions[dst_corner_verts[loop_index]]);
612 interp_weights_poly_v2(weights.data(), cos_2d, orig_face.size(), co);
613 }
614 for (int source_layer_i = 0; source_layer_i < source_cd->totlayer; ++source_layer_i) {
615 const eCustomDataType ty = eCustomDataType(source_cd->layers[source_layer_i].type);
616 if (STR_ELEM(source_cd->layers[source_layer_i].name, ".corner_vert", ".corner_edge")) {
617 continue;
618 }
619 const char *name = source_cd->layers[source_layer_i].name;
620 int target_layer_i = CustomData_get_named_layer_index(target_cd, ty, name);
621 if (target_layer_i == -1) {
622 continue;
623 }
624 if (orig_loop_index != -1) {
626 source_cd, target_cd, source_layer_i, target_layer_i, orig_loop_index, loop_index, 1);
627 }
628 else {
629 /* NOTE: although CustomData_bmesh_interp_n function has bmesh in its name, nothing about
630 * it is BMesh-specific. We can't use CustomData_interp because it assumes that
631 * all source layers exist in the dest.
632 * A non bmesh version could have the benefit of not copying data into src_blocks_ofs -
633 * using the contiguous data instead. TODO: add to the custom data API. */
634 int target_layer_type_index = CustomData_get_named_layer(target_cd, ty, name);
635 if (!CustomData_layer_has_interp(source_cd, source_layer_i)) {
636 continue;
637 }
638 int source_layer_type_index = source_layer_i - source_cd->typemap[ty];
639 BLI_assert(target_layer_type_index != -1 && source_layer_type_index >= 0);
640 const int size = CustomData_sizeof(ty);
641 for (int j = 0; j < orig_face.size(); ++j) {
642 const void *layer = CustomData_get_layer_n(source_cd, ty, source_layer_type_index);
643 src_blocks_ofs[j] = POINTER_OFFSET(layer, size * (orig_face[j]));
644 }
645 void *dst_layer = CustomData_get_layer_n_for_write(
646 target_cd, ty, target_layer_type_index, dest_mesh->corners_num);
647 void *dst_block_ofs = POINTER_OFFSET(dst_layer, size * loop_index);
649 src_blocks_ofs.data(),
650 weights.data(),
651 nullptr,
652 orig_face.size(),
653 dst_block_ofs,
654 target_layer_i);
655 }
656 }
657 }
658}
659
666static void merge_vertex_loop_face_customdata_layers(Mesh *target, MeshesToIMeshInfo &mim)
667{
668 for (int mesh_index = 1; mesh_index < mim.meshes.size(); ++mesh_index) {
669 const Mesh *mesh = mim.meshes[mesh_index];
670 if (mesh->verts_num) {
671 CustomData_merge_layout(&mesh->vert_data,
672 &target->vert_data,
675 target->verts_num);
676 }
677 if (mesh->corners_num) {
678 CustomData_merge_layout(&mesh->corner_data,
679 &target->corner_data,
682 target->corners_num);
683 }
684 if (mesh->faces_num) {
685 CustomData_merge_layout(&mesh->face_data,
686 &target->face_data,
689 target->faces_num);
690 }
691 }
692}
693
694static void merge_edge_customdata_layers(Mesh *target, MeshesToIMeshInfo &mim)
695{
696 for (int mesh_index = 0; mesh_index < mim.meshes.size(); ++mesh_index) {
697 const Mesh *mesh = mim.meshes[mesh_index];
698 if (mesh->edges_num) {
699 CustomData_merge_layout(&mesh->edge_data,
700 &target->edge_data,
703 target->edges_num);
704 }
705 }
706}
707
712static Mesh *imesh_to_mesh(meshintersect::IMesh *im, MeshesToIMeshInfo &mim)
713{
714 constexpr int dbg_level = 0;
715
716 im->populate_vert();
717 int out_totvert = im->vert_size();
718 int out_faces_num = im->face_size();
719 int out_totloop = 0;
720 for (const meshintersect::Face *f : im->faces()) {
721 out_totloop += f->size();
722 }
723 /* Will calculate edges later. */
725 mim.meshes[0], out_totvert, 0, out_faces_num, out_totloop);
726
727 merge_vertex_loop_face_customdata_layers(result, mim);
728 /* Set the vertex coordinate values and other data. */
729 MutableSpan<float3> positions = result->vert_positions_for_write();
730 for (int vi : im->vert_index_range()) {
731 const meshintersect::Vert *v = im->vert(vi);
732 if (v->orig != meshintersect::NO_INDEX) {
733 const Mesh *orig_me;
734 int index_in_orig_me;
735 mim.input_mvert_for_orig_index(v->orig, &orig_me, &index_in_orig_me);
736 copy_vert_attributes(result, orig_me, vi, index_in_orig_me);
737 }
738 copy_v3fl_v3db(positions[vi], v->co);
739 }
740
741 /* Set the loop-start and total-loops for each output face,
742 * and set the vertices in the appropriate loops. */
743 bke::SpanAttributeWriter<int> dst_material_indices =
744 result->attributes_for_write().lookup_or_add_for_write_only_span<int>("material_index",
745 bke::AttrDomain::Face);
746 int cur_loop_index = 0;
747 MutableSpan<int> dst_corner_verts = result->corner_verts_for_write();
748 MutableSpan<int> dst_face_offsets = result->face_offsets_for_write();
749 for (int fi : im->face_index_range()) {
750 const meshintersect::Face *f = im->face(fi);
751 const Mesh *orig_me;
752 int index_in_orig_me;
753 int orig_me_index;
754 const IndexRange orig_face = mim.input_face_for_orig_index(
755 f->orig, &orig_me, &orig_me_index, &index_in_orig_me);
756 dst_face_offsets[fi] = cur_loop_index;
757 for (int j : f->index_range()) {
758 const meshintersect::Vert *vf = f->vert[j];
759 const int vfi = im->lookup_vert(vf);
760 dst_corner_verts[cur_loop_index] = vfi;
761 ++cur_loop_index;
762 }
763
764 copy_face_attributes(result,
765 orig_me,
766 fi,
767 index_in_orig_me,
768 (mim.material_remaps.size() > 0) ?
769 mim.material_remaps[orig_me_index].as_span() :
770 Span<short>(),
771 dst_material_indices.span);
772 copy_or_interp_loop_attributes(result,
773 f,
774 IndexRange(dst_face_offsets[fi], f->size()),
775 orig_face,
776 orig_me,
777 orig_me_index,
778 mim);
779 }
780 dst_material_indices.finish();
781
782 bke::mesh_calc_edges(*result, false, false);
783 merge_edge_customdata_layers(result, mim);
784
785 /* Now that the MEdges are populated, we can copy over the required attributes and custom layers.
786 */
787 const OffsetIndices dst_polys = result->faces();
788 const Span<int> dst_corner_edges = result->corner_edges();
789 for (int fi : im->face_index_range()) {
790 const meshintersect::Face *f = im->face(fi);
791 const IndexRange face = dst_polys[fi];
792 for (int j : f->index_range()) {
793 if (f->edge_orig[j] != meshintersect::NO_INDEX) {
794 const Mesh *orig_me;
795 int index_in_orig_me;
796 mim.input_medge_for_orig_index(f->edge_orig[j], &orig_me, &index_in_orig_me);
797 int e_index = dst_corner_edges[face[j]];
798 copy_edge_attributes(result, orig_me, e_index, index_in_orig_me);
799 }
800 }
801 }
802
803 if (dbg_level > 0) {
804 BKE_mesh_validate(result, true, true);
805 }
806 return result;
807}
808
809static meshintersect::BoolOpType operation_to_mesh_arr_mode(const Operation operation)
810{
811 switch (operation) {
812 case Operation::Intersect:
813 return meshintersect::BoolOpType::Intersect;
814 case Operation::Union:
815 return meshintersect::BoolOpType::Union;
816 case Operation::Difference:
817 return meshintersect::BoolOpType::Difference;
818 }
820 return meshintersect::BoolOpType::None;
821}
822
823static Mesh *mesh_boolean_mesh_arr(Span<const Mesh *> meshes,
824 Span<float4x4> transforms,
825 const float4x4 &target_transform,
826 Span<Array<short>> material_remaps,
827 const bool use_self,
828 const bool hole_tolerant,
829 const meshintersect::BoolOpType boolean_mode,
830 Vector<int> *r_intersecting_edges)
831{
832 BLI_assert(transforms.is_empty() || meshes.size() == transforms.size());
833 BLI_assert(material_remaps.is_empty() || material_remaps.size() == meshes.size());
834 if (meshes.size() <= 0) {
835 return nullptr;
836 }
837
838 const int dbg_level = 0;
839 if (dbg_level > 0) {
840 std::cout << "\nOLD_MESH_INTERSECT, nmeshes = " << meshes.size() << "\n";
841 }
842 MeshesToIMeshInfo mim;
843 meshintersect::IMeshArena arena;
844 meshintersect::IMesh m_in = meshes_to_imesh(
845 meshes, transforms, material_remaps, target_transform, arena, &mim);
846 std::function<int(int)> shape_fn = [&mim](int f) {
847 for (int mi = 0; mi < mim.mesh_face_offset.size() - 1; ++mi) {
848 if (f < mim.mesh_face_offset[mi + 1]) {
849 return mi;
850 }
851 }
852 return int(mim.mesh_face_offset.size()) - 1;
853 };
854 meshintersect::IMesh m_out = boolean_mesh(
855 m_in, boolean_mode, meshes.size(), shape_fn, use_self, hole_tolerant, nullptr, &arena);
856 if (dbg_level > 0) {
857 std::cout << m_out;
858 write_obj_mesh(m_out, "m_out");
859 }
860
861 Mesh *result = imesh_to_mesh(&m_out, mim);
862
863 /* Store intersecting edge indices. */
864 if (r_intersecting_edges != nullptr) {
865 const OffsetIndices faces = result->faces();
866 const Span<int> corner_edges = result->corner_edges();
867 for (int fi : m_out.face_index_range()) {
868 const meshintersect::Face &face = *m_out.face(fi);
869 const IndexRange mesh_face = faces[fi];
870 for (int i : face.index_range()) {
871 if (face.is_intersect[i]) {
872 int e_index = corner_edges[mesh_face[i]];
873 r_intersecting_edges->append(e_index);
874 }
875 }
876 }
877 }
878
879 return result;
880}
881
882#endif // WITH_GMP
883
886/* -------------------------------------------------------------------- */
890/* has no meaning for faces, do this so we can tell which face is which */
891#define BM_FACE_TAG BM_ELEM_DRAW
892
897static int face_boolean_operand(BMFace *f, void * /*user_data*/)
898{
899 return BM_elem_flag_test(f, BM_FACE_TAG) ? 0 : 1;
900}
901
902/* Create a BMesh that is the concatenation of the given meshes.
903 * The corresponding mesh-to-world transformations are also given,
904 * as well as a target_tranform.
905 * A triangulation is also calculated and returned in the last two
906 * parameters.
907 * The faces of the first mesh are tagged with BM_FACE_TAG so that the
908 * face_boolean_operand() function can distinguish those faces from the
909 * rest.
910 * The caller is responsible for using `BM_mesh_free` on the returned
911 * BMesh, and calling `MEM_freeN` on the returned looptris.
912 *
913 * TODO: maybe figure out how to use the join_geometries() function
914 * to join all the meshes into one mesh first, and then convert
915 * that single mesh to BMesh. Issues with that include needing
916 * to apply the transforms and material remaps.
917 */
919 Span<float4x4> transforms,
920 const float4x4 &target_transform,
921 Span<Array<short>> material_remaps,
922 Array<std::array<BMLoop *, 3>> &r_looptris)
923{
924 const int meshes_num = meshes.size();
925 BLI_assert(meshes_num >= 1);
926 bool ok;
927 float4x4 inv_target_mat = math::invert(target_transform, ok);
928 if (!ok) {
930 inv_target_mat = float4x4::identity();
931 }
932 Array<float4x4> to_target(meshes_num);
933 Array<bool> is_negative_transform(meshes_num);
934 Array<bool> is_flip(meshes_num);
935 const int tsize = transforms.size();
936 for (const int i : IndexRange(meshes_num)) {
937 if (tsize > i) {
938 to_target[i] = inv_target_mat * transforms[i];
939 is_negative_transform[i] = math::is_negative(transforms[i]);
940 is_flip[i] = is_negative_transform[i] != is_negative_transform[0];
941 }
942 else {
943 to_target[i] = inv_target_mat;
944 is_negative_transform[i] = false;
945 is_flip[i] = false;
946 }
947 }
948
949 /* Make a BMesh that will be a concatenation of the elements of all the meshes */
950 BMAllocTemplate allocsize;
951 allocsize.totvert = 0;
952 allocsize.totedge = 0;
953 allocsize.totloop = 0;
954 allocsize.totface = 0;
955 for (const int i : meshes.index_range()) {
956 allocsize.totvert += meshes[i]->verts_num;
957 allocsize.totedge += meshes[i]->edges_num;
958 allocsize.totloop += meshes[i]->corners_num;
959 allocsize.totface += meshes[i]->faces_num;
960 }
961
962 BMeshCreateParams bmesh_create_params{};
963 BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params);
964
966 bm, const_cast<const Mesh **>(meshes.begin()), meshes_num, &allocsize);
967
968 BMeshFromMeshParams bmesh_from_mesh_params{};
969 bmesh_from_mesh_params.calc_face_normal = true;
970 bmesh_from_mesh_params.calc_vert_normal = true;
971
972 Array<int> verts_end(meshes_num);
973 Array<int> faces_end(meshes_num);
974 verts_end[0] = meshes[0]->verts_num;
975 faces_end[0] = meshes[0]->faces_num;
976 for (const int i : meshes.index_range()) {
977 /* Append meshes[i] elements and data to bm. */
978 BM_mesh_bm_from_me(bm, meshes[i], &bmesh_from_mesh_params);
979 if (i > 0) {
980 verts_end[i] = verts_end[i - 1] + meshes[i]->verts_num;
981 faces_end[i] = faces_end[i - 1] + meshes[i]->faces_num;
982 if (is_flip[i]) {
983 /* Need to flip face normals to match that of mesh[0]. */
984 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
986 for (int j = faces_end[i - 1]; j < faces_end[i]; j++) {
987 BMFace *efa = bm->ftable[j];
988 BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
989 }
990 }
991 }
992 }
993
994 /* Make a triangulation of all polys before transforming vertices
995 * so we can use the original normals. */
996 const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
997 r_looptris.reinitialize(looptris_tot);
999
1000 /* Transform the vertices that into the desired target_transform space. */
1001 BMIter iter;
1002 BMVert *eve;
1003 int i = 0;
1004 int mesh_index = 0;
1005 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
1006 copy_v3_v3(eve->co, math::transform_point(to_target[mesh_index], float3(eve->co)));
1007 ++i;
1008 if (i == verts_end[mesh_index]) {
1009 mesh_index++;
1010 }
1011 }
1012
1013 /* Transform face normals and tag the first-operand faces.
1014 * Also, apply material remaps. */
1015 BMFace *efa;
1016 i = 0;
1017 mesh_index = 0;
1018 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1019 copy_v3_v3(efa->no, math::transform_direction(to_target[mesh_index], float3(efa->no)));
1020 if (is_negative_transform[mesh_index]) {
1021 negate_v3(efa->no);
1022 }
1023 normalize_v3(efa->no);
1024
1025 /* Temp tag used in `face_boolean_operand()` to test for operand 0. */
1026 if (i < faces_end[0]) {
1028 }
1029
1030 /* Remap material. */
1031 int cur_mat = efa->mat_nr;
1032 if (cur_mat < material_remaps[mesh_index].size()) {
1033 int new_mat = material_remaps[mesh_index][cur_mat];
1034 if (new_mat >= 0) {
1035 efa->mat_nr = material_remaps[mesh_index][cur_mat];
1036 }
1037 }
1038
1039 ++i;
1040 if (i == faces_end[mesh_index]) {
1041 mesh_index++;
1042 }
1043 }
1044
1045 return bm;
1046}
1047
1048static int operation_to_float_mode(const Operation operation)
1049{
1050 switch (operation) {
1051 case Operation::Intersect:
1053 case Operation::Union:
1055 case Operation::Difference:
1057 }
1060}
1061
1063 Span<float4x4> transforms,
1064 const float4x4 &target_transform,
1065 Span<Array<short>> material_remaps,
1066 const int boolean_mode,
1067 Vector<int> * /*r_intersecting_edges*/)
1068{
1069 BLI_assert(meshes.size() == transforms.size() || transforms.size() == 0);
1070 BLI_assert(material_remaps.size() == 0 || material_remaps.size() == meshes.size());
1071 if (meshes.is_empty()) {
1072 return nullptr;
1073 }
1074
1075 if (meshes.size() == 1) {
1076 /* The float solver doesn't do self union. Just return nullptr, which will
1077 * cause geometry nodes to leave the input as is. */
1078 return BKE_mesh_copy_for_eval(*meshes[0]);
1079 }
1080
1082 if (meshes.size() == 2) {
1083 BMesh *bm = mesh_bm_concat(meshes, transforms, target_transform, material_remaps, looptris);
1085 looptris,
1087 nullptr,
1088 false,
1089 false,
1090 true,
1091 true,
1092 false,
1093 false,
1094 boolean_mode,
1095 1e-6f);
1096 Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, meshes[0]);
1098 return result;
1099 }
1100
1101 /* Iteratively operate with each operand. */
1102 Array<const Mesh *> two_meshes = {meshes[0], meshes[1]};
1103 Array<float4x4> two_transforms = {transforms[0], transforms[1]};
1104 Array<Array<short>> two_remaps = {material_remaps[0], material_remaps[1]};
1105 Mesh *prev_result_mesh = nullptr;
1106 for (const int i : meshes.index_range().drop_back(1)) {
1108 two_meshes, two_transforms, float4x4::identity(), two_remaps, looptris);
1110 looptris,
1112 nullptr,
1113 false,
1114 false,
1115 true,
1116 true,
1117 false,
1118 false,
1119 boolean_mode,
1120 1e-6f);
1121 Mesh *result_i_mesh = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, meshes[0]);
1123 if (prev_result_mesh != nullptr) {
1124 /* Except in the first iteration, two_meshes[0] holds the intermediate
1125 * mesh result from the previous iteration. */
1126 BKE_id_free(nullptr, prev_result_mesh);
1127 }
1128 if (i < meshes.size() - 2) {
1129 two_meshes[0] = result_i_mesh;
1130 two_meshes[1] = meshes[i + 2];
1131 two_transforms[0] = float4x4::identity();
1132 two_transforms[1] = transforms[i + 2];
1133 two_remaps[0] = {};
1134 two_remaps[1] = material_remaps[i + 2];
1135 prev_result_mesh = result_i_mesh;
1136 }
1137 else {
1138 return result_i_mesh;
1139 }
1140 }
1141
1143 return nullptr;
1144}
1145
1149 Span<float4x4> transforms,
1150 const float4x4 &target_transform,
1151 Span<Array<short>> material_remaps,
1152 BooleanOpParameters op_params,
1153 Solver solver,
1154 Vector<int> *r_intersecting_edges)
1155{
1156
1157 switch (solver) {
1158 case Solver::Float:
1159 return mesh_boolean_float(meshes,
1160 transforms,
1161 target_transform,
1162 material_remaps,
1164 r_intersecting_edges);
1165 case Solver::MeshArr:
1166#ifdef WITH_GMP
1167 return mesh_boolean_mesh_arr(meshes,
1168 transforms,
1169 target_transform,
1170 material_remaps,
1171 !op_params.no_self_intersections,
1172 !op_params.watertight,
1173 operation_to_mesh_arr_mode(op_params.boolean_mode),
1174 r_intersecting_edges);
1175#else
1176 return nullptr;
1177#endif
1178 default:
1180 }
1181 return nullptr;
1182}
1183
1184} // namespace blender::geometry::boolean
CustomData interface, see also DNA_customdata_types.h.
int CustomData_sizeof(eCustomDataType type)
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
const void * CustomData_get_layer_n(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_named_layer(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool CustomData_layer_has_interp(const CustomData *data, int layer_n)
@ CD_SET_DEFAULT
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
void CustomData_copy_data_layer(const CustomData *source, CustomData *dest, int src_layer_index, int dst_layer_index, int src_index, int dst_index, int count)
bool CustomData_merge_layout(const CustomData *source, CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, int totelem)
void * CustomData_get_layer_n_for_write(CustomData *data, eCustomDataType type, int n, int totelem)
const CustomData_MeshMasks CD_MASK_MESH
void CustomData_bmesh_interp_n(CustomData *data, const void **src_blocks, const float *weights, const float *sub_weights, int count, void *dst_block_ofs, int n)
void BKE_id_free(Main *bmain, void *idv)
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
bool BKE_mesh_validate(Mesh *mesh, bool do_verbose, bool cddata_check_mask)
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
Mesh * BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings)
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
void interp_weights_poly_v2(float w[], float v[][2], int n, const float co[2])
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3(float r[3])
MINLINE void copy_v3fl_v3db(float r[3], const double a[3])
MINLINE float normalize_v3(float n[3])
#define STR_ELEM(...)
Definition BLI_string.h:653
#define POINTER_OFFSET(v, ofs)
#define STREQ(a, b)
@ CD_PROP_INT32_2D
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)
@ BMESH_ISECT_BOOLEAN_DIFFERENCE
@ BMESH_ISECT_BOOLEAN_NONE
@ BMESH_ISECT_BOOLEAN_UNION
@ BMESH_ISECT_BOOLEAN_ISECT
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_calc_tessellation_beauty(BMesh *bm, MutableSpan< std::array< BMLoop *, 3 > > looptris)
#define BM_FACE
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.
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
AttributeSet attributes
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr bool is_empty() const
Definition BLI_span.hh:261
#define fabsf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
static int operation_to_float_mode(const Operation operation)
static Mesh * mesh_boolean_float(Span< const Mesh * > meshes, Span< float4x4 > transforms, const float4x4 &target_transform, Span< Array< short > > material_remaps, const int boolean_mode, Vector< int > *)
static BMesh * mesh_bm_concat(Span< const Mesh * > meshes, Span< float4x4 > transforms, const float4x4 &target_transform, Span< Array< short > > material_remaps, Array< std::array< BMLoop *, 3 > > &r_looptris)
Mesh * mesh_boolean(Span< const Mesh * > meshes, Span< float4x4 > transforms, const float4x4 &target_transform, Span< Array< short > > material_remaps, BooleanOpParameters op_params, Solver solver, Vector< int > *r_intersecting_edges)
static int face_boolean_operand(BMFace *f, void *)
bool is_negative(const MatBase< T, Size, Size > &mat)
CartesianBasis invert(const CartesianBasis &basis)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 3 > float3x3
short mat_nr
float no[3]
float co[3]
int totloop
CustomData ldata
BMFace ** ftable
int totface
CustomDataLayer * layers
int corners_num
CustomData edge_data
CustomData corner_data
CustomData face_data
CustomData vert_data
int verts_num
int xy[2]
Definition wm_draw.cc:170