Blender V5.0
MOD_solidify_extrude.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "BLI_math_vector.h"
12#include "BLI_utildefines.h"
13
14#include "BLI_bitmap.h"
15#include "BLI_math_geom.h"
17
18#include "DNA_mesh_types.h"
19#include "DNA_meshdata_types.h"
20#include "DNA_object_types.h"
21
22#include "MEM_guardedalloc.h"
23
24#include "BKE_attribute.hh"
25#include "BKE_customdata.hh"
26#include "BKE_deform.hh"
27#include "BKE_mesh.hh"
28
29#include "MOD_solidify_util.hh" /* own include */
30#include "MOD_util.hh"
31
32/* -------------------------------------------------------------------- */
38
39/* skip shell thickness for non-manifold edges, see #35710. */
40#define USE_NONMANIFOLD_WORKAROUND
41
43 int p1; /* init as -1 */
44 int p2;
45};
46
48{
49 return !((edge_ref->p1 == 0) && (edge_ref->p2 == 0));
50}
51
57static void mesh_calc_hq_normal(Mesh *mesh,
58 const blender::Span<blender::float3> face_normals,
59 float (*r_vert_nors)[3],
61 BLI_bitmap *edge_tmp_tag
62#endif
63)
64{
65 const int verts_num = mesh->verts_num;
66 const blender::Span<blender::int2> edges = mesh->edges();
67 const blender::OffsetIndices faces = mesh->faces();
68 const blender::Span<int> corner_edges = mesh->corner_edges();
69
70 {
71 EdgeFaceRef *edge_ref_array = MEM_calloc_arrayN<EdgeFaceRef>(edges.size(), __func__);
72 EdgeFaceRef *edge_ref;
73 float edge_normal[3];
74
75 /* Add an edge reference if it's not there, pointing back to the face index. */
76 for (const int i : faces.index_range()) {
77 for (const int edge_i : corner_edges.slice(faces[i])) {
78 /* --- add edge ref to face --- */
79 edge_ref = &edge_ref_array[edge_i];
80 if (!edgeref_is_init(edge_ref)) {
81 edge_ref->p1 = i;
82 edge_ref->p2 = -1;
83 }
84 else if ((edge_ref->p1 != -1) && (edge_ref->p2 == -1)) {
85 edge_ref->p2 = i;
86 }
87 else {
88 /* 3+ faces using an edge, we can't handle this usefully */
89 edge_ref->p1 = edge_ref->p2 = -1;
90#ifdef USE_NONMANIFOLD_WORKAROUND
91 BLI_BITMAP_ENABLE(edge_tmp_tag, edge_i);
92#endif
93 }
94 /* --- done --- */
95 }
96 }
97
98 int i;
99 const blender::int2 *edge;
100 for (i = 0, edge = edges.data(), edge_ref = edge_ref_array; i < edges.size();
101 i++, edge++, edge_ref++)
102 {
103 /* Get the edge vert indices, and edge value (the face indices that use it) */
104
105 if (edgeref_is_init(edge_ref) && (edge_ref->p1 != -1)) {
106 if (edge_ref->p2 != -1) {
107 /* We have 2 faces using this edge, calculate the edges normal
108 * using the angle between the 2 faces as a weighting */
109#if 0
110 add_v3_v3v3(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]);
112 edge_normal,
113 angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2]));
114#else
116 edge_normal, face_normals[edge_ref->p1], face_normals[edge_ref->p2]);
117#endif
118 }
119 else {
120 /* only one face attached to that edge */
121 /* an edge without another attached- the weight on this is undefined */
122 copy_v3_v3(edge_normal, face_normals[edge_ref->p1]);
123 }
124 add_v3_v3(r_vert_nors[(*edge)[0]], edge_normal);
125 add_v3_v3(r_vert_nors[(*edge)[1]], edge_normal);
126 }
127 }
128 MEM_freeN(edge_ref_array);
129 }
130
131 /* normalize vertex normals and assign */
132 const blender::Span<blender::float3> vert_normals = mesh->vert_normals_true();
133 for (int i = 0; i < verts_num; i++) {
134 if (normalize_v3(r_vert_nors[i]) == 0.0f) {
135 copy_v3_v3(r_vert_nors[i], vert_normals[i]);
136 }
137 }
138}
139
141
142/* -------------------------------------------------------------------- */
145
146/* NOLINTNEXTLINE: readability-function-size */
148{
149 using namespace blender;
150 Mesh *result;
152
153 const uint verts_num = uint(mesh->verts_num);
154 const uint edges_num = uint(mesh->edges_num);
155 const uint faces_num = uint(mesh->faces_num);
156 const uint loops_num = uint(mesh->corners_num);
157 uint newLoops = 0, newPolys = 0, newEdges = 0, newVerts = 0, rimVerts = 0;
158
159 /* Only use material offsets if we have 2 or more materials. */
160 const short mat_nr_max = ctx->object->totcol > 1 ? ctx->object->totcol - 1 : 0;
161 const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0;
162 const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0;
163
164 /* use for edges */
165 /* Over-allocate new_vert_arr, old_vert_arr. */
166 uint *new_vert_arr = nullptr;
167 STACK_DECLARE(new_vert_arr);
168
169 uint *new_edge_arr = nullptr;
170 STACK_DECLARE(new_edge_arr);
171
172 uint *old_vert_arr = MEM_calloc_arrayN<uint>(verts_num, "old_vert_arr in solidify");
173
174 uint *edge_users = nullptr;
175 int *edge_order = nullptr;
176
177 float (*vert_nors)[3] = nullptr;
179
180 const bool need_face_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) ||
181 (smd->flag & MOD_SOLIDIFY_EVEN) ||
183 (smd->bevel_convex != 0);
184
185 const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset);
186 const float ofs_new = smd->offset + ofs_orig;
187 const float offset_fac_vg = smd->offset_fac_vg;
188 const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg;
189 const float bevel_convex = smd->bevel_convex;
190 const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0;
191 const bool do_clamp = (smd->offset_clamp != 0.0f);
192 const bool do_angle_clamp = do_clamp && (smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP) != 0;
193 const bool do_bevel_convex = bevel_convex != 0.0f;
194 const bool do_rim = (smd->flag & MOD_SOLIDIFY_RIM) != 0;
195 const bool do_shell = !(do_rim && (smd->flag & MOD_SOLIDIFY_NOSHELL) != 0);
196
197 /* weights */
198 const MDeformVert *dvert;
199 const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0;
200 int defgrp_index;
201 const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name);
202 const int rim_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->rim_defgrp_name);
203
204 /* array size is doubled in case of using a shell */
205 const uint stride = do_shell ? 2 : 1;
206
207 const blender::Span<blender::float3> vert_normals = mesh->vert_normals_true();
208
209 MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index);
210
211 const blender::Span<blender::float3> orig_vert_positions = mesh->vert_positions();
212 const blender::Span<blender::int2> orig_edges = mesh->edges();
213 const blender::OffsetIndices orig_faces = mesh->faces();
214 const blender::Span<int> orig_corner_verts = mesh->corner_verts();
215 const blender::Span<int> orig_corner_edges = mesh->corner_edges();
216 const blender::bke::AttributeAccessor orig_attributes = mesh->attributes();
217
218 if (need_face_normals) {
219 /* calculate only face normals */
220 face_normals = mesh->face_normals_true();
221 }
222
223 STACK_INIT(new_vert_arr, verts_num * 2);
224 STACK_INIT(new_edge_arr, edges_num * 2);
225
226 if (do_rim) {
227 BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(verts_num, __func__);
228 uint eidx;
229 uint i;
230
231#define INVALID_UNUSED uint(-1)
232#define INVALID_PAIR uint(-2)
233
234 new_vert_arr = MEM_malloc_arrayN<uint>(2 * verts_num, __func__);
235 new_edge_arr = MEM_malloc_arrayN<uint>(((edges_num * 2) + verts_num), __func__);
236
237 edge_users = MEM_malloc_arrayN<uint>(edges_num, __func__);
238 edge_order = MEM_malloc_arrayN<int>(edges_num, __func__);
239
240 /* save doing 2 loops here... */
241#if 0
242 copy_vn_i(edge_users, edges_num, INVALID_UNUSED);
243#endif
244
245 for (eidx = 0; eidx < edges_num; eidx++) {
246 edge_users[eidx] = INVALID_UNUSED;
247 }
248
249 for (const int64_t i : orig_faces.index_range()) {
250 const blender::IndexRange face = orig_faces[i];
251 int j;
252
253 int corner_i_prev = face.last();
254
255 for (j = 0; j < face.size(); j++) {
256 const int corner_i = face[j];
257 const int vert_i = orig_corner_verts[corner_i];
258 const int prev_vert_i = orig_corner_verts[corner_i_prev];
259 /* add edge user */
260 eidx = int(orig_corner_edges[corner_i_prev]);
261 if (edge_users[eidx] == INVALID_UNUSED) {
262 const blender::int2 &edge = orig_edges[eidx];
263 BLI_assert(ELEM(prev_vert_i, edge[0], edge[1]) && ELEM(vert_i, edge[0], edge[1]));
264 edge_users[eidx] = (prev_vert_i > vert_i) == (edge[0] < edge[1]) ? uint(i) :
265 (uint(i) + faces_num);
266 edge_order[eidx] = j;
267 }
268 else {
269 edge_users[eidx] = INVALID_PAIR;
270 }
271 corner_i_prev = corner_i;
272 }
273 }
274
275 for (eidx = 0; eidx < edges_num; eidx++) {
276 if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) {
277 BLI_BITMAP_ENABLE(orig_mvert_tag, orig_edges[eidx][0]);
278 BLI_BITMAP_ENABLE(orig_mvert_tag, orig_edges[eidx][1]);
279 STACK_PUSH(new_edge_arr, eidx);
280 newPolys++;
281 newLoops += 4;
282 }
283 }
284
285 for (i = 0; i < verts_num; i++) {
286 if (BLI_BITMAP_TEST(orig_mvert_tag, i)) {
287 old_vert_arr[i] = STACK_SIZE(new_vert_arr);
288 STACK_PUSH(new_vert_arr, i);
289 rimVerts++;
290 }
291 else {
292 old_vert_arr[i] = INVALID_UNUSED;
293 }
294 }
295
296 MEM_freeN(orig_mvert_tag);
297 }
298
299 if (do_shell == false) {
300 /* only add rim vertices */
301 newVerts = rimVerts;
302 /* each extruded face needs an opposite edge */
303 newEdges = newPolys;
304 }
305 else {
306 /* (stride == 2) in this case, so no need to add newVerts/newEdges */
307 BLI_assert(newVerts == 0);
308 BLI_assert(newEdges == 0);
309 }
310
311#ifdef USE_NONMANIFOLD_WORKAROUND
312 BLI_bitmap *edge_tmp_tag = BLI_BITMAP_NEW(mesh->edges_num, __func__);
313#endif
314
315 if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) {
316 vert_nors = MEM_calloc_arrayN<float[3]>(verts_num, __func__);
318 face_normals,
319 vert_nors
321 ,
322 edge_tmp_tag
323#endif
324 );
325 }
326
328 int((verts_num * stride) + newVerts),
329 int((edges_num * stride) + newEdges + rimVerts),
330 int((faces_num * stride) + newPolys),
331 int((loops_num * stride) + newLoops));
332
333 blender::MutableSpan<blender::float3> vert_positions = result->vert_positions_for_write();
334 blender::MutableSpan<blender::int2> edges = result->edges_for_write();
335 blender::MutableSpan<int> face_offsets = result->face_offsets_for_write();
336 blender::MutableSpan<int> corner_verts = result->corner_verts_for_write();
337 blender::MutableSpan<int> corner_edges = result->corner_edges_for_write();
338 bke::MutableAttributeAccessor result_attributes = result->attributes_for_write();
339
340 if (do_shell) {
341 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, 0, int(verts_num));
342 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, int(verts_num), int(verts_num));
343
344 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, 0, int(edges_num));
345 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, int(edges_num), int(edges_num));
346
347 CustomData_copy_data(&mesh->corner_data, &result->corner_data, 0, 0, int(loops_num));
348 /* DO NOT copy here the 'copied' part of loop data, we want to reverse loops
349 * (so that winding of copied face get reversed, so that normals get reversed
350 * and point in expected direction...).
351 * If we also copy data here, then this data get overwritten
352 * (and allocated memory becomes a memory leak). */
353
354 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, 0, int(faces_num));
355 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, int(faces_num), int(faces_num));
356 face_offsets.take_front(faces_num).copy_from(mesh->face_offsets().drop_back(1));
357 for (const int i : orig_faces.index_range()) {
358 face_offsets[faces_num + i] = orig_faces[i].start() + mesh->corners_num;
359 }
360 }
361 else {
362 int i, j;
363 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, 0, int(verts_num));
364 for (i = 0, j = int(verts_num); i < verts_num; i++) {
365 if (old_vert_arr[i] != INVALID_UNUSED) {
366 CustomData_copy_data(&mesh->vert_data, &result->vert_data, i, j, 1);
367 j++;
368 }
369 }
370
371 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, 0, int(edges_num));
372
373 for (i = 0, j = int(edges_num); i < edges_num; i++) {
374 if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) {
375 blender::int2 *ed_src, *ed_dst;
376 CustomData_copy_data(&mesh->edge_data, &result->edge_data, i, j, 1);
377
378 ed_src = &edges[i];
379 ed_dst = &edges[j];
380 (*ed_dst)[0] = old_vert_arr[(*ed_src)[0]] + verts_num;
381 (*ed_dst)[1] = old_vert_arr[(*ed_src)[1]] + verts_num;
382 j++;
383 }
384 }
385
386 /* will be created later */
387 CustomData_copy_data(&mesh->corner_data, &result->corner_data, 0, 0, int(loops_num));
388 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, 0, int(faces_num));
389 face_offsets.take_front(faces_num).copy_from(mesh->face_offsets().drop_back(1));
390 }
391
392 const VArraySpan orig_vert_bweight = *orig_attributes.lookup<float>("bevel_weight_vert",
394 bke::SpanAttributeWriter<float> result_edge_bweight;
395 if (orig_attributes.contains("bevel_weight_edge") ||
396 (do_bevel_convex || !orig_vert_bweight.is_empty()))
397 {
398 result_edge_bweight = result_attributes.lookup_or_add_for_write_span<float>(
399 "bevel_weight_edge", blender::bke::AttrDomain::Edge);
400 }
401
402 /* Initializes: (`i_end`, `do_shell_align`, `vert_index`). */
403#define INIT_VERT_ARRAY_OFFSETS(test) \
404 if (((ofs_new >= ofs_orig) == do_flip) == test) { \
405 i_end = verts_num; \
406 do_shell_align = true; \
407 vert_index = 0; \
408 } \
409 else { \
410 if (do_shell) { \
411 i_end = verts_num; \
412 do_shell_align = true; \
413 } \
414 else { \
415 i_end = newVerts; \
416 do_shell_align = false; \
417 } \
418 vert_index = verts_num; \
419 } \
420 (void)0
421
422 bke::SpanAttributeWriter dst_material_index =
423 result_attributes.lookup_or_add_for_write_span<int>("material_index", bke::AttrDomain::Face);
424
425 /* flip normals */
426
427 if (do_shell) {
428 for (const int64_t i : blender::IndexRange(mesh->faces_num)) {
429 const blender::IndexRange face = orig_faces[i];
430 const int loop_end = face.size() - 1;
431 int e;
432 int j;
433
434 /* reverses the loop direction (corner verts as well as custom-data)
435 * Corner edges also need to be corrected too, done in a separate loop below. */
436 const int corner_2 = face.start() + mesh->corners_num;
437#if 0
438 for (j = 0; j < face.size(); j++) {
439 CustomData_copy_data(&mesh->ldata,
440 &result->ldata,
441 face.start() + j,
442 face.start() + (loop_end - j) + mesh->corners_num,
443 1);
444 }
445#else
446 /* slightly more involved, keep the first vertex the same for the copy,
447 * ensures the diagonals in the new face match the original. */
448 j = 0;
449 for (int j_prev = loop_end; j < face.size(); j_prev = j++) {
451 &result->corner_data,
452 face.start() + j,
453 face.start() + (loop_end - j_prev) + mesh->corners_num,
454 1);
455 }
456#endif
457
458 if (mat_ofs) {
459 dst_material_index.span[faces_num + i] += mat_ofs;
460 CLAMP(dst_material_index.span[faces_num + i], 0, mat_nr_max);
461 }
462
463 e = corner_edges[corner_2 + 0];
464 for (j = 0; j < loop_end; j++) {
465 corner_edges[corner_2 + j] = corner_edges[corner_2 + j + 1];
466 }
467 corner_edges[corner_2 + loop_end] = e;
468
469 for (j = 0; j < face.size(); j++) {
470 corner_verts[corner_2 + j] += verts_num;
471 corner_edges[corner_2 + j] += edges_num;
472 }
473 }
474
475 for (blender::int2 &edge : edges.slice(edges_num, edges_num)) {
476 edge += verts_num;
477 }
478 }
479
480 /* NOTE: copied vertex layers don't have flipped normals yet. do this after applying offset. */
481 if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) {
482 /* no even thickness, very simple */
483 float ofs_new_vgroup;
484
485 /* for clamping */
486 float *vert_lens = nullptr;
487 float *vert_angs = nullptr;
488 const float offset = fabsf(smd->offset) * smd->offset_clamp;
489 const float offset_sq = offset * offset;
490
491 /* for bevel weight */
492 float *edge_angs = nullptr;
493
494 if (do_clamp) {
495 vert_lens = MEM_malloc_arrayN<float>(verts_num, "vert_lens");
496 copy_vn_fl(vert_lens, int(verts_num), FLT_MAX);
497 for (uint i = 0; i < edges_num; i++) {
498 const float ed_len_sq = len_squared_v3v3(vert_positions[edges[i][0]],
499 vert_positions[edges[i][1]]);
500 vert_lens[edges[i][0]] = min_ff(vert_lens[edges[i][0]], ed_len_sq);
501 vert_lens[edges[i][1]] = min_ff(vert_lens[edges[i][1]], ed_len_sq);
502 }
503 }
504
505 if (do_angle_clamp || do_bevel_convex) {
506 uint eidx;
507 if (do_angle_clamp) {
508 vert_angs = MEM_malloc_arrayN<float>(verts_num, "vert_angs");
509 copy_vn_fl(vert_angs, int(verts_num), 0.5f * M_PI);
510 }
511 if (do_bevel_convex) {
512 edge_angs = MEM_malloc_arrayN<float>(edges_num, "edge_angs");
513 if (!do_rim) {
514 edge_users = MEM_malloc_arrayN<uint>(edges_num, "solid_mod edges");
515 }
516 }
517 uint(*edge_user_pairs)[2] = MEM_malloc_arrayN<uint[2]>(edges_num, "edge_user_pairs");
518 for (eidx = 0; eidx < edges_num; eidx++) {
519 edge_user_pairs[eidx][0] = INVALID_UNUSED;
520 edge_user_pairs[eidx][1] = INVALID_UNUSED;
521 }
522 for (const int64_t i : orig_faces.index_range()) {
523 const blender::IndexRange face = orig_faces[i];
524 int prev_corner_i = face.last();
525 for (const int corner_i : face) {
526 const int vert_i = orig_corner_verts[corner_i];
527 const int prev_vert_i = orig_corner_verts[prev_corner_i];
528 /* add edge user */
529 eidx = orig_corner_edges[prev_corner_i];
530 const blender::int2 &ed = orig_edges[eidx];
531 BLI_assert(ELEM(prev_vert_i, ed[0], ed[1]) && ELEM(vert_i, ed[0], ed[1]));
532 char flip = char((prev_vert_i > vert_i) == (ed[0] < ed[1]));
533 if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
534 edge_user_pairs[eidx][flip] = uint(i);
535 }
536 else {
537 edge_user_pairs[eidx][0] = INVALID_PAIR;
538 edge_user_pairs[eidx][1] = INVALID_PAIR;
539 }
540 prev_corner_i = corner_i;
541 }
542 }
543 float e[3];
544 for (uint i = 0; i < edges_num; i++) {
545 const blender::int2 &edge = orig_edges[i];
546 if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
547 !ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR))
548 {
549 const float *n0 = face_normals[edge_user_pairs[i][0]];
550 const float *n1 = face_normals[edge_user_pairs[i][1]];
551 sub_v3_v3v3(e, orig_vert_positions[edge[0]], orig_vert_positions[edge[1]]);
553 const float angle = angle_signed_on_axis_v3v3_v3(n0, n1, e);
554 if (do_angle_clamp) {
555 vert_angs[edge[0]] = max_ff(vert_angs[edge[0]], angle);
556 vert_angs[edge[1]] = max_ff(vert_angs[edge[1]], angle);
557 }
558 if (do_bevel_convex) {
559 edge_angs[i] = angle;
560 if (!do_rim) {
561 edge_users[i] = INVALID_PAIR;
562 }
563 }
564 }
565 }
566 MEM_freeN(edge_user_pairs);
567 }
568
569 if (ofs_new != 0.0f) {
570 uint i_orig, i_end;
571 bool do_shell_align;
572
573 ofs_new_vgroup = ofs_new;
574
575 uint vert_index;
577
578 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
579 const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
580 if (dvert) {
581 const MDeformVert *dv = &dvert[i];
582 if (defgrp_invert) {
583 ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
584 }
585 else {
586 ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
587 }
588 ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_new;
589 }
590 if (do_clamp && offset > FLT_EPSILON) {
591 /* always reset because we may have set before */
592 if (dvert == nullptr) {
593 ofs_new_vgroup = ofs_new;
594 }
595 if (do_angle_clamp) {
596 float cos_ang = cosf(((2 * M_PI) - vert_angs[i]) * 0.5f);
597 if (cos_ang > 0) {
598 float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
599 if (max_off < offset * 0.5f) {
600 ofs_new_vgroup *= max_off / offset * 2;
601 }
602 }
603 }
604 else {
605 if (vert_lens[i] < offset_sq) {
606 float scalar = sqrtf(vert_lens[i]) / offset;
607 ofs_new_vgroup *= scalar;
608 }
609 }
610 }
611 if (vert_nors) {
612 madd_v3_v3fl(vert_positions[vert_index], vert_nors[i], ofs_new_vgroup);
613 }
614 else {
615 madd_v3_v3fl(vert_positions[vert_index], vert_normals[i], ofs_new_vgroup);
616 }
617 }
618 }
619
620 if (ofs_orig != 0.0f) {
621 uint i_orig, i_end;
622 bool do_shell_align;
623
624 ofs_new_vgroup = ofs_orig;
625
626 /* as above but swapped */
627 uint vert_index;
629
630 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
631 const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
632 if (dvert) {
633 const MDeformVert *dv = &dvert[i];
634 if (defgrp_invert) {
635 ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
636 }
637 else {
638 ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
639 }
640 ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_orig;
641 }
642 if (do_clamp && offset > FLT_EPSILON) {
643 /* always reset because we may have set before */
644 if (dvert == nullptr) {
645 ofs_new_vgroup = ofs_orig;
646 }
647 if (do_angle_clamp) {
648 float cos_ang = cosf(vert_angs[i_orig] * 0.5f);
649 if (cos_ang > 0) {
650 float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
651 if (max_off < offset * 0.5f) {
652 ofs_new_vgroup *= max_off / offset * 2;
653 }
654 }
655 }
656 else {
657 if (vert_lens[i] < offset_sq) {
658 float scalar = sqrtf(vert_lens[i]) / offset;
659 ofs_new_vgroup *= scalar;
660 }
661 }
662 }
663 if (vert_nors) {
664 madd_v3_v3fl(vert_positions[vert_index], vert_nors[i], ofs_new_vgroup);
665 }
666 else {
667 madd_v3_v3fl(vert_positions[vert_index], vert_normals[i], ofs_new_vgroup);
668 }
669 }
670 }
671
672 if (do_bevel_convex) {
673 for (uint i = 0; i < edges_num; i++) {
674 if (edge_users[i] == INVALID_PAIR) {
675 float angle = edge_angs[i];
676 result_edge_bweight.span[i] = clamp_f(
677 result_edge_bweight.span[i] + (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
678 clamp_f(bevel_convex, -1.0f, 0.0f)),
679 0.0f,
680 1.0f);
681 if (do_shell) {
682 result_edge_bweight.span[i + edges_num] = clamp_f(
683 result_edge_bweight.span[i + edges_num] + (angle > M_PI ?
684 clamp_f(bevel_convex, 0.0f, 1.0f) :
685 clamp_f(bevel_convex, -1.0f, 0.0f)),
686 0,
687 1.0f);
688 }
689 }
690 }
691 if (!do_rim) {
692 MEM_freeN(edge_users);
693 }
694 MEM_freeN(edge_angs);
695 }
696
697 if (do_clamp) {
698 MEM_freeN(vert_lens);
699 if (do_angle_clamp) {
700 MEM_freeN(vert_angs);
701 }
702 }
703 }
704 else {
705#ifdef USE_NONMANIFOLD_WORKAROUND
706 const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0;
707#endif
708 /* same as EM_solidify() in editmesh_lib.c */
709 float *vert_angles = MEM_calloc_arrayN<float>(2 * verts_num, "mod_solid_pair"); /* 2 in 1 */
710 float *vert_accum = vert_angles + verts_num;
711 uint vidx;
712 uint i;
713
714 if (vert_nors == nullptr) {
715 vert_nors = MEM_malloc_arrayN<float[3]>(verts_num, "mod_solid_vno");
716 for (i = 0; i < verts_num; i++) {
717 copy_v3_v3(vert_nors[i], vert_normals[i]);
718 }
719 }
720
721 for (const int64_t i : blender::IndexRange(faces_num)) {
722 const blender::IndexRange face = orig_faces[i];
723 /* #bke::mesh::face_angles_calc logic is inlined here */
724 float nor_prev[3];
725 float nor_next[3];
726
727 int i_curr = face.size() - 1;
728 int i_next = 0;
729
730 const int *face_verts = &corner_verts[face.start()];
731 const int *face_edges = &corner_edges[face.start()];
732
734 nor_prev, vert_positions[face_verts[i_curr - 1]], vert_positions[face_verts[i_curr]]);
735 normalize_v3(nor_prev);
736
737 while (i_next < face.size()) {
738 float angle;
740 nor_next, vert_positions[face_verts[i_curr]], vert_positions[face_verts[i_next]]);
741 normalize_v3(nor_next);
742 angle = angle_normalized_v3v3(nor_prev, nor_next);
743
744 /* --- not related to angle calc --- */
745 angle = std::max(angle, FLT_EPSILON);
746
747 vidx = face_verts[i_curr];
748 vert_accum[vidx] += angle;
749
750#ifdef USE_NONMANIFOLD_WORKAROUND
751 /* skip 3+ face user edges */
752 if ((check_non_manifold == false) ||
753 LIKELY(!BLI_BITMAP_TEST(edge_tmp_tag, face_edges[i_curr]) &&
754 !BLI_BITMAP_TEST(edge_tmp_tag, face_edges[i_next])))
755 {
756 vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_normals[i]) *
757 angle;
758 }
759 else {
760 vert_angles[vidx] += angle;
761 }
762#else
763 vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_normals[i]) *
764 angle;
765#endif
766 /* --- end non-angle-calc section --- */
767
768 /* step */
769 copy_v3_v3(nor_prev, nor_next);
770 i_curr = i_next;
771 i_next++;
772 }
773 }
774
775 /* vertex group support */
776 if (dvert) {
777 const MDeformVert *dv = dvert;
778 float scalar;
779
780 if (defgrp_invert) {
781 for (i = 0; i < verts_num; i++, dv++) {
782 scalar = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
783 scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
784 vert_angles[i] *= scalar;
785 }
786 }
787 else {
788 for (i = 0; i < verts_num; i++, dv++) {
789 scalar = BKE_defvert_find_weight(dv, defgrp_index);
790 scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
791 vert_angles[i] *= scalar;
792 }
793 }
794 }
795
796 /* for angle clamp */
797 float *vert_angs = nullptr;
798 /* for bevel convex */
799 float *edge_angs = nullptr;
800
801 if (do_angle_clamp || do_bevel_convex) {
802 uint eidx;
803 if (do_angle_clamp) {
804 vert_angs = MEM_malloc_arrayN<float>(verts_num, "vert_angs even");
805 copy_vn_fl(vert_angs, int(verts_num), 0.5f * M_PI);
806 }
807 if (do_bevel_convex) {
808 edge_angs = MEM_malloc_arrayN<float>(edges_num, "edge_angs even");
809 if (!do_rim) {
810 edge_users = MEM_malloc_arrayN<uint>(edges_num, "solid_mod edges");
811 }
812 }
813 uint(*edge_user_pairs)[2] = MEM_malloc_arrayN<uint[2]>(edges_num, "edge_user_pairs");
814 for (eidx = 0; eidx < edges_num; eidx++) {
815 edge_user_pairs[eidx][0] = INVALID_UNUSED;
816 edge_user_pairs[eidx][1] = INVALID_UNUSED;
817 }
818 for (const int i : orig_faces.index_range()) {
819 const blender::IndexRange face = orig_faces[i];
820 int prev_corner_i = face.start() + face.size() - 1;
821 for (int j = 0; j < face.size(); j++) {
822 const int corner_i = face.start() + j;
823 const int vert_i = orig_corner_verts[corner_i];
824 const int prev_vert_i = orig_corner_verts[prev_corner_i];
825
826 /* add edge user */
827 eidx = orig_corner_edges[prev_corner_i];
828 const blender::int2 &edge = orig_edges[eidx];
829 BLI_assert(ELEM(prev_vert_i, edge[0], edge[1]) && ELEM(vert_i, edge[0], edge[1]));
830 char flip = char((prev_vert_i > vert_i) == (edge[0] < edge[1]));
831 if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
832 edge_user_pairs[eidx][flip] = uint(i);
833 }
834 else {
835 edge_user_pairs[eidx][0] = INVALID_PAIR;
836 edge_user_pairs[eidx][1] = INVALID_PAIR;
837 }
838 prev_corner_i = corner_i;
839 }
840 }
841 float e[3];
842 for (i = 0; i < edges_num; i++) {
843 const blender::int2 &edge = orig_edges[i];
844 if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
845 !ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR))
846 {
847 const float *n0 = face_normals[edge_user_pairs[i][0]];
848 const float *n1 = face_normals[edge_user_pairs[i][1]];
849 if (do_angle_clamp) {
850 const float angle = M_PI - angle_normalized_v3v3(n0, n1);
851 vert_angs[edge[0]] = max_ff(vert_angs[edge[0]], angle);
852 vert_angs[edge[1]] = max_ff(vert_angs[edge[1]], angle);
853 }
854 if (do_bevel_convex) {
855 sub_v3_v3v3(e, orig_vert_positions[edge[0]], orig_vert_positions[edge[1]]);
857 edge_angs[i] = angle_signed_on_axis_v3v3_v3(n0, n1, e);
858 if (!do_rim) {
859 edge_users[i] = INVALID_PAIR;
860 }
861 }
862 }
863 }
864 MEM_freeN(edge_user_pairs);
865 }
866
867 if (do_clamp) {
868 const float clamp_fac = 1 + (do_angle_clamp ? fabsf(smd->offset_fac) : 0);
869 const float offset = fabsf(smd->offset) * smd->offset_clamp * clamp_fac;
870 if (offset > FLT_EPSILON) {
871 float *vert_lens_sq = MEM_malloc_arrayN<float>(verts_num, "vert_lens_sq");
872 const float offset_sq = offset * offset;
873 copy_vn_fl(vert_lens_sq, int(verts_num), FLT_MAX);
874 for (i = 0; i < edges_num; i++) {
875 const float ed_len = len_squared_v3v3(vert_positions[edges[i][0]],
876 vert_positions[edges[i][1]]);
877 vert_lens_sq[edges[i][0]] = min_ff(vert_lens_sq[edges[i][0]], ed_len);
878 vert_lens_sq[edges[i][1]] = min_ff(vert_lens_sq[edges[i][1]], ed_len);
879 }
880 if (do_angle_clamp) {
881 for (i = 0; i < verts_num; i++) {
882 float cos_ang = cosf(vert_angs[i] * 0.5f);
883 if (cos_ang > 0) {
884 float max_off = sqrtf(vert_lens_sq[i]) * 0.5f / cos_ang;
885 if (max_off < offset * 0.5f) {
886 vert_angles[i] *= max_off / offset * 2;
887 }
888 }
889 }
890 MEM_freeN(vert_angs);
891 }
892 else {
893 for (i = 0; i < verts_num; i++) {
894 if (vert_lens_sq[i] < offset_sq) {
895 float scalar = sqrtf(vert_lens_sq[i]) / offset;
896 vert_angles[i] *= scalar;
897 }
898 }
899 }
900 MEM_freeN(vert_lens_sq);
901 }
902 }
903
904 if (do_bevel_convex) {
905 for (i = 0; i < edges_num; i++) {
906 if (edge_users[i] == INVALID_PAIR) {
907 float angle = edge_angs[i];
908 result_edge_bweight.span[i] = clamp_f(
909 result_edge_bweight.span[i] + (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
910 clamp_f(bevel_convex, -1.0f, 0.0f)),
911 0.0f,
912 1.0f);
913 if (do_shell) {
914 result_edge_bweight.span[i + edges_num] = clamp_f(
915 result_edge_bweight.span[i + edges_num] +
916 (angle > M_PI ? clamp_f(bevel_convex, 0, 1) : clamp_f(bevel_convex, -1, 0)),
917 0.0f,
918 1.0f);
919 }
920 }
921 }
922 if (!do_rim) {
923 MEM_freeN(edge_users);
924 }
925 MEM_freeN(edge_angs);
926 }
927
928#undef INVALID_UNUSED
929#undef INVALID_PAIR
930
931 if (ofs_new != 0.0f) {
932 uint i_orig, i_end;
933 bool do_shell_align;
934
935 uint vert_index;
937
938 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
939 const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
940 if (vert_accum[i_other]) { /* zero if unselected */
941 madd_v3_v3fl(vert_positions[vert_index],
942 vert_nors[i_other],
943 ofs_new * (vert_angles[i_other] / vert_accum[i_other]));
944 }
945 }
946 }
947
948 if (ofs_orig != 0.0f) {
949 uint i_orig, i_end;
950 bool do_shell_align;
951
952 /* same as above but swapped, intentional use of 'ofs_new' */
953 uint vert_index;
955
956 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
957 const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
958 if (vert_accum[i_other]) { /* zero if unselected */
959 madd_v3_v3fl(vert_positions[vert_index],
960 vert_nors[i_other],
961 ofs_orig * (vert_angles[i_other] / vert_accum[i_other]));
962 }
963 }
964 }
965
966 MEM_freeN(vert_angles);
967 }
968
969#ifdef USE_NONMANIFOLD_WORKAROUND
970 MEM_SAFE_FREE(edge_tmp_tag);
971#endif
972
973 if (vert_nors) {
974 MEM_freeN(vert_nors);
975 }
976
977 /* Add vertex weights for rim and shell vgroups. */
978 if (shell_defgrp_index != -1 || rim_defgrp_index != -1) {
979 MDeformVert *dst_dvert = result->deform_verts_for_write().data();
980
981 /* Ultimate security check. */
982 if (dst_dvert != nullptr) {
983
984 if (rim_defgrp_index != -1) {
985 for (uint i = 0; i < rimVerts; i++) {
986 BKE_defvert_ensure_index(&dst_dvert[new_vert_arr[i]], rim_defgrp_index)->weight = 1.0f;
987 BKE_defvert_ensure_index(&dst_dvert[(do_shell ? new_vert_arr[i] : i) + verts_num],
988 rim_defgrp_index)
989 ->weight = 1.0f;
990 }
991 }
992
993 if (shell_defgrp_index != -1) {
994 for (uint i = verts_num; i < result->verts_num; i++) {
995 BKE_defvert_ensure_index(&dst_dvert[i], shell_defgrp_index)->weight = 1.0f;
996 }
997 }
998 }
999 }
1000 if (do_rim) {
1001 uint i;
1002
1003 const float crease_rim = smd->crease_rim;
1004 const float crease_outer = smd->crease_outer;
1005 const float crease_inner = smd->crease_inner;
1006
1007 int *origindex_edge;
1008 int *orig_ed;
1009 uint j;
1010
1011 bke::SpanAttributeWriter<float> result_edge_crease;
1012 if (crease_rim || crease_outer || crease_inner) {
1013 result_edge_crease = result_attributes.lookup_or_add_for_write_span<float>(
1014 "crease_edge", blender::bke::AttrDomain::Edge);
1015 }
1016
1017 /* add faces & edges */
1018 origindex_edge = static_cast<int *>(
1019 CustomData_get_layer_for_write(&result->edge_data, CD_ORIGINDEX, result->edges_num));
1020 orig_ed = (origindex_edge) ? &origindex_edge[(edges_num * stride) + newEdges] : nullptr;
1021 /* Start after copied edges. */
1022 int new_edge_index = int(edges_num * stride + newEdges);
1023 for (i = 0; i < rimVerts; i++) {
1024 edges[new_edge_index][0] = new_vert_arr[i];
1025 edges[new_edge_index][1] = (do_shell ? new_vert_arr[i] : i) + verts_num;
1026
1027 if (result_edge_bweight && !orig_vert_bweight.is_empty()) {
1028 result_edge_bweight.span[new_edge_index] = orig_vert_bweight[new_vert_arr[i]];
1029 }
1030
1031 if (orig_ed) {
1032 *orig_ed = ORIGINDEX_NONE;
1033 orig_ed++;
1034 }
1035
1036 if (result_edge_crease && crease_rim) {
1037 result_edge_crease.span[new_edge_index] = crease_rim;
1038 }
1039 new_edge_index++;
1040 }
1041
1042 /* faces */
1043 int new_face_index = int(faces_num * stride);
1044 blender::MutableSpan<int> new_corner_verts = corner_verts.drop_front(loops_num * stride);
1045 blender::MutableSpan<int> new_corner_edges = corner_edges.drop_front(loops_num * stride);
1046 j = 0;
1047 for (i = 0; i < newPolys; i++) {
1048 uint eidx = new_edge_arr[i];
1049 uint pidx = edge_users[eidx];
1050 int k1, k2;
1051 bool flip;
1052
1053 if (pidx >= faces_num) {
1054 pidx -= faces_num;
1055 flip = true;
1056 }
1057 else {
1058 flip = false;
1059 }
1060
1061 const blender::int2 &edge = edges[eidx];
1062
1063 /* copy most of the face settings */
1065 &mesh->face_data, &result->face_data, int(pidx), int((faces_num * stride) + i), 1);
1066
1067 const int old_face_size = orig_faces[pidx].size();
1068 face_offsets[new_face_index] = int(j + (loops_num * stride));
1069
1070 /* prev loop */
1071 k1 = face_offsets[pidx] + (((edge_order[eidx] - 1) + old_face_size) % old_face_size);
1072
1073 k2 = face_offsets[pidx] + (edge_order[eidx]);
1074
1076 &mesh->corner_data, &result->corner_data, k2, int((loops_num * stride) + j + 0), 1);
1078 &mesh->corner_data, &result->corner_data, k1, int((loops_num * stride) + j + 1), 1);
1080 &mesh->corner_data, &result->corner_data, k1, int((loops_num * stride) + j + 2), 1);
1082 &mesh->corner_data, &result->corner_data, k2, int((loops_num * stride) + j + 3), 1);
1083
1084 if (flip == false) {
1085 new_corner_verts[j] = edge[0];
1086 new_corner_edges[j++] = eidx;
1087
1088 new_corner_verts[j] = edge[1];
1089 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[1]] + newEdges;
1090
1091 new_corner_verts[j] = (do_shell ? edge[1] : old_vert_arr[edge[1]]) + verts_num;
1092 new_corner_edges[j++] = (do_shell ? eidx : i) + edges_num;
1093
1094 new_corner_verts[j] = (do_shell ? edge[0] : old_vert_arr[edge[0]]) + verts_num;
1095 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[0]] + newEdges;
1096 }
1097 else {
1098 new_corner_verts[j] = edge[1];
1099 new_corner_edges[j++] = eidx;
1100
1101 new_corner_verts[j] = edge[0];
1102 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[0]] + newEdges;
1103
1104 new_corner_verts[j] = (do_shell ? edge[0] : old_vert_arr[edge[0]]) + verts_num;
1105 new_corner_edges[j++] = (do_shell ? eidx : i) + edges_num;
1106
1107 new_corner_verts[j] = (do_shell ? edge[1] : old_vert_arr[edge[1]]) + verts_num;
1108 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[1]] + newEdges;
1109 }
1110
1111 if (origindex_edge) {
1112 origindex_edge[new_corner_edges[j - 3]] = ORIGINDEX_NONE;
1113 origindex_edge[new_corner_edges[j - 1]] = ORIGINDEX_NONE;
1114 }
1115
1116 /* use the next material index if option enabled */
1117 if (mat_ofs_rim) {
1118 dst_material_index.span[new_face_index] += mat_ofs_rim;
1119 CLAMP(dst_material_index.span[new_face_index], 0, mat_nr_max);
1120 }
1121 if (result_edge_crease && crease_outer) {
1122 /* crease += crease_outer; without wrapping */
1123 float *cr = &(result_edge_crease.span[eidx]);
1124 float tcr = *cr + crease_outer;
1125 *cr = tcr > 1.0f ? 1.0f : tcr;
1126 }
1127
1128 if (result_edge_crease && crease_inner) {
1129 /* crease += crease_inner; without wrapping */
1130 float *cr = &(result_edge_crease.span[edges_num + (do_shell ? eidx : i)]);
1131 float tcr = *cr + crease_inner;
1132 *cr = tcr > 1.0f ? 1.0f : tcr;
1133 }
1134
1135 new_face_index++;
1136 }
1137
1138 MEM_freeN(new_vert_arr);
1139 MEM_freeN(new_edge_arr);
1140
1141 MEM_freeN(edge_users);
1142 MEM_freeN(edge_order);
1143
1144 result_edge_crease.finish();
1145 }
1146
1147 if (old_vert_arr) {
1148 MEM_freeN(old_vert_arr);
1149 }
1150
1151 result_edge_bweight.finish();
1152 dst_material_index.finish();
1153
1154 return result;
1155}
1156
CustomData interface, see also DNA_customdata_types.h.
#define ORIGINDEX_NONE
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)
support for deformation groups and hooks.
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:825
int BKE_id_defgroup_name_index(const ID *id, blender::StringRef name)
Definition deform.cc:549
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:774
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:37
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:61
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:78
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
#define BLI_INLINE
MINLINE float max_ff(float a, float b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
#define M_PI
MINLINE float shell_v3v3_normalized_to_dist(const float a[3], const float b[3])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
void copy_vn_fl(float *array_tar, int size, float val)
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
void mid_v3_v3v3_angle_weighted(float r[3], const float a[3], const float b[3])
float angle_signed_on_axis_v3v3_v3(const float v1[3], const float v2[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
void copy_vn_i(int *array_tar, int size, int val)
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3_length(float n[3], float unit_length)
MINLINE float normalize_v3(float n[3])
unsigned int uint
#define CLAMP(a, b, c)
#define ELEM(...)
#define LIKELY(x)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
@ MOD_SOLIDIFY_RIM
@ MOD_SOLIDIFY_FLIP
@ MOD_SOLIDIFY_NORMAL_CALC
@ MOD_SOLIDIFY_EVEN
@ MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP
@ MOD_SOLIDIFY_VGROUP_INV
@ MOD_SOLIDIFY_NOSHELL
Object is a sort of wrapper for general info.
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref)
Mesh * MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
#define INVALID_UNUSED
#define USE_NONMANIFOLD_WORKAROUND
#define INVALID_PAIR
#define INIT_VERT_ARRAY_OFFSETS(test)
static void mesh_calc_hq_normal(Mesh *mesh, const blender::Span< blender::float3 > face_normals, float(*r_vert_nors)[3], BLI_bitmap *edge_tmp_tag)
void MOD_get_vgroup(const Object *ob, const Mesh *mesh, const char *name, const MDeformVert **dvert, int *defgrp_index)
Definition MOD_util.cc:156
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
long long int int64_t
AttributeSet attributes
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:607
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:629
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
bool contains(StringRef attribute_id) const
GAttributeReader lookup(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
nullptr float
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
VecBase< int32_t, 2 > int2
#define fabsf
#define sqrtf
#define cosf
#define FLT_MAX
Definition stdcycles.h:14
int corners_num
CustomData edge_data
int edges_num
CustomData corner_data
CustomData face_data
CustomData vert_data
int faces_num
int verts_num
i
Definition text_draw.cc:230