Blender V4.3
blenkernel/intern/mesh_mirror.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_array.hh"
10#include "BLI_math_geom.h"
11#include "BLI_math_matrix.h"
12#include "BLI_math_vector.h"
13
14#include "DNA_meshdata_types.h"
15#include "DNA_object_types.h"
16
17#include "BKE_attribute.hh"
18#include "BKE_deform.hh"
19#include "BKE_lib_id.hh"
20#include "BKE_mesh.hh"
21#include "BKE_mesh_mirror.hh"
22#include "BKE_modifier.hh"
23
24#include "bmesh.hh"
25#include "bmesh_tools.hh"
26
27#include "MEM_guardedalloc.h"
28
30 const Mesh *mesh,
31 int axis,
32 const float plane_co[3],
33 float plane_no[3])
34{
35 bool do_bisect_flip_axis = ((axis == 0 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_X) ||
36 (axis == 1 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Y) ||
37 (axis == 2 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Z));
38
39 const float bisect_distance = mmd->bisect_threshold;
40
41 Mesh *result;
42 BMesh *bm;
43 BMIter viter;
44 BMVert *v, *v_next;
45
46 BMeshCreateParams bmesh_create_params{false};
47
48 BMeshFromMeshParams bmesh_from_mesh_params{};
49 bmesh_from_mesh_params.calc_face_normal = true;
50 bmesh_from_mesh_params.calc_vert_normal = true;
51 bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_ORIGINDEX;
52 bmesh_from_mesh_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX;
53 bmesh_from_mesh_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX;
54
55 bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
56
57 /* Define bisecting plane (aka mirror plane). */
58 float plane[4];
59 if (!do_bisect_flip_axis) {
60 /* That reversed condition is a little weird, but for some reason that's how you keep
61 * the part of the mesh which is on the non-mirrored side when flip option is disabled.
62 * I think this is the expected behavior. */
63 negate_v3(plane_no);
64 }
65 plane_from_point_normal_v3(plane, plane_co, plane_no);
66
67 BM_mesh_bisect_plane(bm, plane, true, false, 0, 0, bisect_distance);
68
69 /* Plane definitions for vert killing. */
70 float plane_offset[4];
71 copy_v3_v3(plane_offset, plane);
72 plane_offset[3] = plane[3] - bisect_distance;
73
74 /* Delete verts across the mirror plane. */
75 BM_ITER_MESH_MUTABLE (v, v_next, &viter, bm, BM_VERTS_OF_MESH) {
76 if (plane_point_side_v3(plane_offset, v->co) > 0.0f) {
78 }
79 }
80
81 result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
83
84 return result;
85}
86
88 Mesh *mesh,
89 const int axis,
90 const float dist)
91{
92 BMeshCreateParams bmesh_create_params{};
93 bmesh_create_params.use_toolflags = true;
94
95 BMeshFromMeshParams bmesh_from_mesh_params{};
96 bmesh_from_mesh_params.calc_face_normal = true;
97 bmesh_from_mesh_params.calc_vert_normal = true;
98 bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_SHAPEKEY;
99
100 BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
103 "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b",
104 axis,
105 dist,
106 true);
107
108 BMeshToMeshParams bmesh_to_mesh_params{};
109 bmesh_to_mesh_params.calc_object_remap = true;
110
111 BM_mesh_bm_to_me(bmain, bm, mesh, &bmesh_to_mesh_params);
113}
114
116 Object *ob,
117 const Mesh *mesh,
118 const int axis,
119 const bool use_correct_order_on_merge,
120 int **r_vert_merge_map,
121 int *r_vert_merge_map_len)
122{
123 using namespace blender;
124 using namespace blender::bke;
125 const float tolerance_sq = mmd->tolerance * mmd->tolerance;
126 const bool do_vtargetmap = (mmd->flag & MOD_MIR_NO_MERGE) == 0 && r_vert_merge_map != nullptr;
127
128 const bool do_bisect = ((axis == 0 && mmd->flag & MOD_MIR_BISECT_AXIS_X) ||
129 (axis == 1 && mmd->flag & MOD_MIR_BISECT_AXIS_Y) ||
130 (axis == 2 && mmd->flag & MOD_MIR_BISECT_AXIS_Z));
131
132 float mtx[4][4];
133 float plane_co[3], plane_no[3];
134 int a, totshape;
135 int *vtmap_a = nullptr, *vtmap_b = nullptr;
136
137 /* mtx is the mirror transformation */
138 unit_m4(mtx);
139 mtx[axis][axis] = -1.0f;
140
141 Object *mirror_ob = mmd->mirror_ob;
142 if (mirror_ob != nullptr) {
143 float tmp[4][4];
144 float itmp[4][4];
145
146 /* tmp is a transform from coords relative to the object's own origin,
147 * to coords relative to the mirror object origin */
148 invert_m4_m4(tmp, mirror_ob->object_to_world().ptr());
149 mul_m4_m4m4(tmp, tmp, ob->object_to_world().ptr());
150
151 /* itmp is the reverse transform back to origin-relative coordinates */
152 invert_m4_m4(itmp, tmp);
153
154 /* combine matrices to get a single matrix that translates coordinates into
155 * mirror-object-relative space, does the mirror, and translates back to
156 * origin-relative space */
157 mul_m4_series(mtx, itmp, mtx, tmp);
158
159 if (do_bisect) {
160 copy_v3_v3(plane_co, itmp[3]);
161 copy_v3_v3(plane_no, itmp[axis]);
162
163 /* Account for non-uniform scale in `ob`, see: #87592. */
164 float ob_scale[3] = {
165 len_squared_v3(ob->object_to_world().ptr()[0]),
166 len_squared_v3(ob->object_to_world().ptr()[1]),
167 len_squared_v3(ob->object_to_world().ptr()[2]),
168 };
169 /* Scale to avoid precision loss with extreme values. */
170 const float ob_scale_max = max_fff(UNPACK3(ob_scale));
171 if (LIKELY(ob_scale_max != 0.0f)) {
172 mul_v3_fl(ob_scale, 1.0f / ob_scale_max);
173 mul_v3_v3(plane_no, ob_scale);
174 }
175 }
176 }
177 else if (do_bisect) {
178 copy_v3_v3(plane_co, mtx[3]);
179 /* Need to negate here, since that axis is inverted (for mirror transform). */
180 negate_v3_v3(plane_no, mtx[axis]);
181 }
182
183 Mesh *mesh_bisect = nullptr;
184 if (do_bisect) {
186 mmd, mesh, axis, plane_co, plane_no);
187 mesh = mesh_bisect;
188 }
189
190 const int src_verts_num = mesh->verts_num;
191 const int src_edges_num = mesh->edges_num;
192 const blender::OffsetIndices src_faces = mesh->faces();
193 const int src_loops_num = mesh->corners_num;
194
196 mesh, src_verts_num * 2, src_edges_num * 2, src_faces.size() * 2, src_loops_num * 2);
197
198 /* Copy custom-data to original geometry. */
199 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, 0, src_verts_num);
200 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, 0, src_edges_num);
201 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, 0, src_faces.size());
202 CustomData_copy_data(&mesh->corner_data, &result->corner_data, 0, 0, src_loops_num);
203
204 /* Copy custom data to mirrored geometry. Loops are copied later. */
205 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, src_verts_num, src_verts_num);
206 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, src_edges_num, src_edges_num);
208 &mesh->face_data, &result->face_data, 0, src_faces.size(), src_faces.size());
209
210 if (do_vtargetmap) {
211 /* second half is filled with -1 */
212 *r_vert_merge_map = static_cast<int *>(
213 MEM_malloc_arrayN(src_verts_num, sizeof(int[2]), "MOD_mirror tarmap"));
214
215 vtmap_a = *r_vert_merge_map;
216 vtmap_b = *r_vert_merge_map + src_verts_num;
217
218 *r_vert_merge_map_len = 0;
219 }
220
221 /* mirror vertex coordinates */
222 blender::MutableSpan<blender::float3> positions = result->vert_positions_for_write();
223 for (int i = 0; i < src_verts_num; i++) {
224 const int vert_index_prev = i;
225 const int vert_index = src_verts_num + i;
226 mul_m4_v3(mtx, positions[vert_index]);
227
228 if (do_vtargetmap) {
229 /* Compare location of the original and mirrored vertex,
230 * to see if they should be mapped for merging.
231 *
232 * Always merge from the copied into the original vertices so it's possible to
233 * generate a 1:1 mapping by scanning vertices from the beginning of the array
234 * as is done in #BKE_editmesh_vert_coords_when_deformed. Without this,
235 * the coordinates returned will sometimes point to the copied vertex locations, see:
236 * #91444.
237 *
238 * However, such a change also affects non-versionable things like some modifiers binding, so
239 * we cannot enforce that behavior on existing modifiers, in which case we keep using the
240 * old, incorrect behavior of merging the source vertex into its copy.
241 */
242 if (use_correct_order_on_merge) {
243 if (UNLIKELY(len_squared_v3v3(positions[vert_index_prev], positions[vert_index]) <
244 tolerance_sq))
245 {
246 *vtmap_b = i;
247 (*r_vert_merge_map_len)++;
248
249 /* average location */
250 mid_v3_v3v3(positions[vert_index], positions[vert_index_prev], positions[vert_index]);
251 copy_v3_v3(positions[vert_index_prev], positions[vert_index]);
252 }
253 else {
254 *vtmap_b = -1;
255 }
256
257 /* Fill here to avoid 2x loops. */
258 *vtmap_a = -1;
259 }
260 else {
261 if (UNLIKELY(len_squared_v3v3(positions[vert_index_prev], positions[vert_index]) <
262 tolerance_sq))
263 {
264 *vtmap_a = src_verts_num + i;
265 (*r_vert_merge_map_len)++;
266
267 /* average location */
268 mid_v3_v3v3(positions[vert_index], positions[vert_index_prev], positions[vert_index]);
269 copy_v3_v3(positions[vert_index_prev], positions[vert_index]);
270 }
271 else {
272 *vtmap_a = -1;
273 }
274
275 /* Fill here to avoid 2x loops. */
276 *vtmap_b = -1;
277 }
278
279 vtmap_a++;
280 vtmap_b++;
281 }
282 }
283
284 /* handle shape keys */
285 totshape = CustomData_number_of_layers(&result->vert_data, CD_SHAPEKEY);
286 for (a = 0; a < totshape; a++) {
287 float(*cos)[3] = static_cast<float(*)[3]>(
288 CustomData_get_layer_n_for_write(&result->vert_data, CD_SHAPEKEY, a, result->verts_num));
289 for (int i = src_verts_num; i < result->verts_num; i++) {
290 mul_m4_v3(mtx, cos[i]);
291 }
292 }
293
294 blender::MutableSpan<blender::int2> result_edges = result->edges_for_write();
295 blender::MutableSpan<int> result_face_offsets = result->face_offsets_for_write();
296 blender::MutableSpan<int> result_corner_verts = result->corner_verts_for_write();
297 blender::MutableSpan<int> result_corner_edges = result->corner_edges_for_write();
298
299 /* adjust mirrored edge vertex indices */
300 for (const int i : result_edges.index_range().drop_front(src_edges_num)) {
301 result_edges[i] += src_verts_num;
302 }
303
304 result_face_offsets.take_front(src_faces.size()).copy_from(mesh->face_offsets().drop_back(1));
305 for (const int i : src_faces.index_range()) {
306 result_face_offsets[src_faces.size() + i] = src_faces[i].start() + src_loops_num;
307 }
308 const blender::OffsetIndices result_faces = result->faces();
309
310 /* reverse loop order (normals) */
311 for (const int i : src_faces.index_range()) {
312 const blender::IndexRange src_face = src_faces[i];
313 const int mirror_i = src_faces.size() + i;
314 const blender::IndexRange mirror_face = result_faces[mirror_i];
315
316 /* reverse the loop, but we keep the first vertex in the face the same,
317 * to ensure that quads are split the same way as on the other side */
319 &mesh->corner_data, &result->corner_data, src_face.start(), mirror_face.start(), 1);
320
321 for (int j = 1; j < mirror_face.size(); j++) {
323 &mesh->corner_data, &result->corner_data, src_face[j], mirror_face.last(j - 1), 1);
324 }
325
326 blender::MutableSpan<int> mirror_face_edges = result_corner_edges.slice(mirror_face);
327 const int e = mirror_face_edges.first();
328 for (int j = 0; j < mirror_face.size() - 1; j++) {
329 mirror_face_edges[j] = mirror_face_edges[j + 1];
330 }
331 mirror_face_edges.last() = e;
332 }
333
334 /* adjust mirrored loop vertex and edge indices */
335 for (const int i : result_corner_verts.index_range().drop_front(src_loops_num)) {
336 result_corner_verts[i] += src_verts_num;
337 }
338 for (const int i : result_corner_edges.index_range().drop_front(src_loops_num)) {
339 result_corner_edges[i] += src_edges_num;
340 }
341
342 if (!mesh->runtime->subsurf_optimal_display_edges.is_empty()) {
343 const blender::BoundedBitSpan src = mesh->runtime->subsurf_optimal_display_edges;
344 result->runtime->subsurf_optimal_display_edges.resize(result->edges_num);
345 blender::MutableBoundedBitSpan dst = result->runtime->subsurf_optimal_display_edges;
346 dst.take_front(src.size()).copy_from(src);
347 dst.take_back(src.size()).copy_from(src);
348 }
349
350 /* handle uvs,
351 * let tessface recalc handle updating the MTFace data */
352 if (mmd->flag & (MOD_MIR_MIRROR_U | MOD_MIR_MIRROR_V) ||
353 (is_zero_v2(mmd->uv_offset_copy) == false))
354 {
355 const bool do_mirr_u = (mmd->flag & MOD_MIR_MIRROR_U) != 0;
356 const bool do_mirr_v = (mmd->flag & MOD_MIR_MIRROR_V) != 0;
357 /* If set, flip around center of each tile. */
358 const bool do_mirr_udim = (mmd->flag & MOD_MIR_MIRROR_UDIM) != 0;
359
360 const int totuv = CustomData_number_of_layers(&result->corner_data, CD_PROP_FLOAT2);
361
362 for (a = 0; a < totuv; a++) {
363 float(*dmloopuv)[2] = static_cast<float(*)[2]>(CustomData_get_layer_n_for_write(
364 &result->corner_data, CD_PROP_FLOAT2, a, result->corners_num));
365 int j = src_loops_num;
366 dmloopuv += j; /* second set of loops only */
367 for (; j-- > 0; dmloopuv++) {
368 if (do_mirr_u) {
369 float u = (*dmloopuv)[0];
370 if (do_mirr_udim) {
371 (*dmloopuv)[0] = ceilf(u) - fmodf(u, 1.0f) + mmd->uv_offset[0];
372 }
373 else {
374 (*dmloopuv)[0] = 1.0f - u + mmd->uv_offset[0];
375 }
376 }
377 if (do_mirr_v) {
378 float v = (*dmloopuv)[1];
379 if (do_mirr_udim) {
380 (*dmloopuv)[1] = ceilf(v) - fmodf(v, 1.0f) + mmd->uv_offset[1];
381 }
382 else {
383 (*dmloopuv)[1] = 1.0f - v + mmd->uv_offset[1];
384 }
385 }
386 (*dmloopuv)[0] += mmd->uv_offset_copy[0];
387 (*dmloopuv)[1] += mmd->uv_offset_copy[1];
388 }
389 }
390 }
391
392 /* handle custom split normals */
393 if (ob->type == OB_MESH && CustomData_has_layer(&result->corner_data, CD_CUSTOMLOOPNORMAL) &&
394 result->faces_num > 0)
395 {
396 blender::Array<blender::float3> corner_normals(result_corner_verts.size());
398 &result->corner_data, CD_CUSTOMLOOPNORMAL, result->corners_num));
400
401 /* The transform matrix of a normal must be
402 * the transpose of inverse of transform matrix of the geometry... */
403 float mtx_nor[4][4];
404 invert_m4_m4(mtx_nor, mtx);
405 transpose_m4(mtx_nor);
406
407 /* calculate custom normals into corner_normals, then mirror first half into second half */
408 const bke::AttributeAccessor attributes = result->attributes();
409 const VArraySpan sharp_edges = *attributes.lookup<bool>("sharp_edge", AttrDomain::Edge);
410 const VArraySpan sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
411 blender::bke::mesh::normals_calc_corners(result->vert_positions(),
412 result_edges,
413 result_faces,
414 result_corner_verts,
415 result_corner_edges,
416 result->corner_to_face_map(),
417 result->vert_normals(),
418 result->face_normals(),
419 sharp_edges,
420 sharp_faces,
421 clnors,
422 &lnors_spacearr,
423 corner_normals);
424
425 /* mirroring has to account for loops being reversed in faces in second half */
426 for (const int i : src_faces.index_range()) {
427 const blender::IndexRange src_face = src_faces[i];
428 const int mirror_i = src_faces.size() + i;
429
430 for (const int j : src_face) {
431 int mirrorj = result_faces[mirror_i].start();
432 if (j > src_face.start()) {
433 mirrorj += result_faces[mirror_i].size() - (j - src_face.start());
434 }
435
436 copy_v3_v3(corner_normals[mirrorj], corner_normals[j]);
437 mul_m4_v3(mtx_nor, corner_normals[mirrorj]);
438
439 const int space_index = lnors_spacearr.corner_space_indices[mirrorj];
441 lnors_spacearr.spaces[space_index], corner_normals[mirrorj]);
442 }
443 }
444 }
445
446 /* handle vgroup stuff */
448 if ((mmd->flag & MOD_MIR_VGROUP) && CustomData_has_layer(&result->vert_data, CD_MDEFORMVERT)) {
449 MDeformVert *dvert = result->deform_verts_for_write().data() + src_verts_num;
450 int flip_map_len = 0;
451 int *flip_map = BKE_object_defgroup_flip_map(ob, false, &flip_map_len);
452 if (flip_map) {
453 for (int i = 0; i < src_verts_num; dvert++, i++) {
454 /* merged vertices get both groups, others get flipped */
455 if (use_correct_order_on_merge && do_vtargetmap &&
456 ((*r_vert_merge_map)[i + src_verts_num] != -1))
457 {
458 BKE_defvert_flip_merged(dvert - src_verts_num, flip_map, flip_map_len);
459 }
460 else if (!use_correct_order_on_merge && do_vtargetmap && ((*r_vert_merge_map)[i] != -1))
461 {
462 BKE_defvert_flip_merged(dvert, flip_map, flip_map_len);
463 }
464 else {
465 BKE_defvert_flip(dvert, flip_map, flip_map_len);
466 }
467 }
468
469 MEM_freeN(flip_map);
470 }
471 }
472 }
473
474 if (mesh_bisect != nullptr) {
475 BKE_id_free(nullptr, mesh_bisect);
476 }
477 return result;
478}
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
void CustomData_copy_data(const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
void * CustomData_get_layer_n_for_write(CustomData *data, eCustomDataType type, int n, int totelem)
support for deformation groups and hooks.
void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, int flip_map_num)
Definition deform.cc:410
bool BKE_object_supports_vertex_groups(const Object *ob)
Definition deform.cc:457
void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, int flip_map_num)
Definition deform.cc:424
int * BKE_object_defgroup_flip_map(const Object *ob, bool use_default, int *r_flip_map_num)
Definition deform.cc:661
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)
Mesh * BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings)
BMesh * BKE_mesh_to_bmesh_ex(const Mesh *mesh, const BMeshCreateParams *create_params, const BMeshFromMeshParams *convert_params)
MINLINE float max_fff(float a, float b, float c)
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:215
MINLINE float plane_point_side_v3(const float plane[4], const float co[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void unit_m4(float m[4][4])
Definition rct.c:1127
void mul_m4_v3(const float M[4][4], float r[3])
#define mul_m4_series(...)
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void transpose_m4(float R[4][4])
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_v3(float r[3], const float a[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE bool is_zero_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3(float r[3])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
#define UNPACK3(a)
#define UNLIKELY(x)
#define LIKELY(x)
#define CD_MASK_ORIGINDEX
@ CD_CUSTOMLOOPNORMAL
@ CD_MDEFORMVERT
@ CD_PROP_FLOAT2
#define CD_MASK_SHAPEKEY
@ MOD_MIR_BISECT_AXIS_Z
@ MOD_MIR_MIRROR_U
@ MOD_MIR_BISECT_AXIS_X
@ MOD_MIR_MIRROR_V
@ MOD_MIR_BISECT_FLIP_AXIS_X
@ MOD_MIR_VGROUP
@ MOD_MIR_BISECT_AXIS_Y
@ MOD_MIR_BISECT_FLIP_AXIS_Z
@ MOD_MIR_NO_MERGE
@ MOD_MIR_BISECT_FLIP_AXIS_Y
@ MOD_MIR_MIRROR_UDIM
Object is a sort of wrapper for general info.
@ OB_MESH
Read Guarded memory(de)allocation.
Mesh * BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, Object *ob, const Mesh *mesh, const int axis, const bool use_correct_order_on_merge, int **r_vert_merge_map, int *r_vert_merge_map_len)
void BKE_mesh_mirror_apply_mirror_on_axis(Main *bmain, Mesh *mesh, const int axis, const float dist)
Mesh * BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mmd, const Mesh *mesh, int axis, const float plane_co[3], float plane_no[3])
void BM_mesh_bisect_plane(BMesh *bm, const float plane[4], const bool use_snap_center, const bool use_tag, const short oflag_center, const short oflag_new, const float eps)
void BM_vert_kill(BMesh *bm, BMVert *v)
@ BM_VERTS_OF_MESH
#define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
#define BMO_FLAG_DEFAULTS
@ BMO_FLAG_RESPECT_HIDE
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr IndexRange drop_front(int64_t n) const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr T & first() const
Definition BLI_span.hh:680
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:630
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
int64_t size() const
MutableBitSpan take_back(const int64_t n) const
MutableBoundedBitSpan take_front(const int64_t n) const
#define ceilf(x)
#define fmodf(x, y)
draw_view in_light_buf[] float
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float3 cos(float3 v)
void normals_calc_corners(Span< float3 > vert_positions, Span< int2 > edges, OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, Span< int > corner_to_face_map, Span< float3 > vert_normals, Span< float3 > face_normals, Span< bool > sharp_edges, Span< bool > sharp_faces, const short2 *clnors_data, CornerNormalSpaceArray *r_lnors_spacearr, MutableSpan< float3 > r_corner_normals)
short2 corner_space_custom_normal_to_data(const CornerNormalSpace &lnor_space, const float3 &custom_lnor)
float co[3]
int verts_num
struct Object * mirror_ob
Array< CornerNormalSpace > spaces
Definition BKE_mesh.hh:141