Blender V4.5
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
217 if (need_face_normals) {
218 /* calculate only face normals */
219 face_normals = mesh->face_normals_true();
220 }
221
222 STACK_INIT(new_vert_arr, verts_num * 2);
223 STACK_INIT(new_edge_arr, edges_num * 2);
224
225 if (do_rim) {
226 BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(verts_num, __func__);
227 uint eidx;
228 uint i;
229
230#define INVALID_UNUSED uint(-1)
231#define INVALID_PAIR uint(-2)
232
233 new_vert_arr = MEM_malloc_arrayN<uint>(2 * verts_num, __func__);
234 new_edge_arr = MEM_malloc_arrayN<uint>(((edges_num * 2) + verts_num), __func__);
235
236 edge_users = MEM_malloc_arrayN<uint>(edges_num, __func__);
237 edge_order = MEM_malloc_arrayN<int>(edges_num, __func__);
238
239 /* save doing 2 loops here... */
240#if 0
241 copy_vn_i(edge_users, edges_num, INVALID_UNUSED);
242#endif
243
244 for (eidx = 0; eidx < edges_num; eidx++) {
245 edge_users[eidx] = INVALID_UNUSED;
246 }
247
248 for (const int64_t i : orig_faces.index_range()) {
249 const blender::IndexRange face = orig_faces[i];
250 int j;
251
252 int corner_i_prev = face.last();
253
254 for (j = 0; j < face.size(); j++) {
255 const int corner_i = face[j];
256 const int vert_i = orig_corner_verts[corner_i];
257 const int prev_vert_i = orig_corner_verts[corner_i_prev];
258 /* add edge user */
259 eidx = int(orig_corner_edges[corner_i_prev]);
260 if (edge_users[eidx] == INVALID_UNUSED) {
261 const blender::int2 &edge = orig_edges[eidx];
262 BLI_assert(ELEM(prev_vert_i, edge[0], edge[1]) && ELEM(vert_i, edge[0], edge[1]));
263 edge_users[eidx] = (prev_vert_i > vert_i) == (edge[0] < edge[1]) ? uint(i) :
264 (uint(i) + faces_num);
265 edge_order[eidx] = j;
266 }
267 else {
268 edge_users[eidx] = INVALID_PAIR;
269 }
270 corner_i_prev = corner_i;
271 }
272 }
273
274 for (eidx = 0; eidx < edges_num; eidx++) {
275 if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) {
276 BLI_BITMAP_ENABLE(orig_mvert_tag, orig_edges[eidx][0]);
277 BLI_BITMAP_ENABLE(orig_mvert_tag, orig_edges[eidx][1]);
278 STACK_PUSH(new_edge_arr, eidx);
279 newPolys++;
280 newLoops += 4;
281 }
282 }
283
284 for (i = 0; i < verts_num; i++) {
285 if (BLI_BITMAP_TEST(orig_mvert_tag, i)) {
286 old_vert_arr[i] = STACK_SIZE(new_vert_arr);
287 STACK_PUSH(new_vert_arr, i);
288 rimVerts++;
289 }
290 else {
291 old_vert_arr[i] = INVALID_UNUSED;
292 }
293 }
294
295 MEM_freeN(orig_mvert_tag);
296 }
297
298 if (do_shell == false) {
299 /* only add rim vertices */
300 newVerts = rimVerts;
301 /* each extruded face needs an opposite edge */
302 newEdges = newPolys;
303 }
304 else {
305 /* (stride == 2) in this case, so no need to add newVerts/newEdges */
306 BLI_assert(newVerts == 0);
307 BLI_assert(newEdges == 0);
308 }
309
310#ifdef USE_NONMANIFOLD_WORKAROUND
311 BLI_bitmap *edge_tmp_tag = BLI_BITMAP_NEW(mesh->edges_num, __func__);
312#endif
313
314 if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) {
315 vert_nors = MEM_calloc_arrayN<float[3]>(verts_num, __func__);
317 face_normals,
318 vert_nors
320 ,
321 edge_tmp_tag
322#endif
323 );
324 }
325
327 int((verts_num * stride) + newVerts),
328 int((edges_num * stride) + newEdges + rimVerts),
329 int((faces_num * stride) + newPolys),
330 int((loops_num * stride) + newLoops));
331
332 blender::MutableSpan<blender::float3> vert_positions = result->vert_positions_for_write();
333 blender::MutableSpan<blender::int2> edges = result->edges_for_write();
334 blender::MutableSpan<int> face_offsets = result->face_offsets_for_write();
335 blender::MutableSpan<int> corner_verts = result->corner_verts_for_write();
336 blender::MutableSpan<int> corner_edges = result->corner_edges_for_write();
337
338 if (do_shell) {
339 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, 0, int(verts_num));
340 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, int(verts_num), int(verts_num));
341
342 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, 0, int(edges_num));
343 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, int(edges_num), int(edges_num));
344
345 CustomData_copy_data(&mesh->corner_data, &result->corner_data, 0, 0, int(loops_num));
346 /* DO NOT copy here the 'copied' part of loop data, we want to reverse loops
347 * (so that winding of copied face get reversed, so that normals get reversed
348 * and point in expected direction...).
349 * If we also copy data here, then this data get overwritten
350 * (and allocated memory becomes a memory leak). */
351
352 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, 0, int(faces_num));
353 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, int(faces_num), int(faces_num));
354 face_offsets.take_front(faces_num).copy_from(mesh->face_offsets().drop_back(1));
355 for (const int i : orig_faces.index_range()) {
356 face_offsets[faces_num + i] = orig_faces[i].start() + mesh->corners_num;
357 }
358 }
359 else {
360 int i, j;
361 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, 0, int(verts_num));
362 for (i = 0, j = int(verts_num); i < verts_num; i++) {
363 if (old_vert_arr[i] != INVALID_UNUSED) {
364 CustomData_copy_data(&mesh->vert_data, &result->vert_data, i, j, 1);
365 j++;
366 }
367 }
368
369 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, 0, int(edges_num));
370
371 for (i = 0, j = int(edges_num); i < edges_num; i++) {
372 if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) {
373 blender::int2 *ed_src, *ed_dst;
374 CustomData_copy_data(&mesh->edge_data, &result->edge_data, i, j, 1);
375
376 ed_src = &edges[i];
377 ed_dst = &edges[j];
378 (*ed_dst)[0] = old_vert_arr[(*ed_src)[0]] + verts_num;
379 (*ed_dst)[1] = old_vert_arr[(*ed_src)[1]] + verts_num;
380 j++;
381 }
382 }
383
384 /* will be created later */
385 CustomData_copy_data(&mesh->corner_data, &result->corner_data, 0, 0, int(loops_num));
386 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, 0, int(faces_num));
387 face_offsets.take_front(faces_num).copy_from(mesh->face_offsets().drop_back(1));
388 }
389
390 const float *orig_vert_bweight = static_cast<const float *>(
391 CustomData_get_layer_named(&mesh->vert_data, CD_PROP_FLOAT, "bevel_weight_vert"));
392 float *result_edge_bweight = static_cast<float *>(CustomData_get_layer_named_for_write(
393 &result->edge_data, CD_PROP_FLOAT, "bevel_weight_edge", result->edges_num));
394 if (!result_edge_bweight && (do_bevel_convex || orig_vert_bweight)) {
395 result_edge_bweight = static_cast<float *>(CustomData_add_layer_named(&result->edge_data,
398 result->edges_num,
399 "bevel_weight_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::MutableAttributeAccessor dst_attributes = result->attributes_for_write();
423 bke::SpanAttributeWriter dst_material_index = dst_attributes.lookup_or_add_for_write_span<int>(
424 "material_index", bke::AttrDomain::Face);
425
426 /* flip normals */
427
428 if (do_shell) {
429 for (const int64_t i : blender::IndexRange(mesh->faces_num)) {
430 const blender::IndexRange face = orig_faces[i];
431 const int loop_end = face.size() - 1;
432 int e;
433 int j;
434
435 /* reverses the loop direction (corner verts as well as custom-data)
436 * Corner edges also need to be corrected too, done in a separate loop below. */
437 const int corner_2 = face.start() + mesh->corners_num;
438#if 0
439 for (j = 0; j < face.size(); j++) {
440 CustomData_copy_data(&mesh->ldata,
441 &result->ldata,
442 face.start() + j,
443 face.start() + (loop_end - j) + mesh->corners_num,
444 1);
445 }
446#else
447 /* slightly more involved, keep the first vertex the same for the copy,
448 * ensures the diagonals in the new face match the original. */
449 j = 0;
450 for (int j_prev = loop_end; j < face.size(); j_prev = j++) {
452 &result->corner_data,
453 face.start() + j,
454 face.start() + (loop_end - j_prev) + mesh->corners_num,
455 1);
456 }
457#endif
458
459 if (mat_ofs) {
460 dst_material_index.span[faces_num + i] += mat_ofs;
461 CLAMP(dst_material_index.span[faces_num + i], 0, mat_nr_max);
462 }
463
464 e = corner_edges[corner_2 + 0];
465 for (j = 0; j < loop_end; j++) {
466 corner_edges[corner_2 + j] = corner_edges[corner_2 + j + 1];
467 }
468 corner_edges[corner_2 + loop_end] = e;
469
470 for (j = 0; j < face.size(); j++) {
471 corner_verts[corner_2 + j] += verts_num;
472 corner_edges[corner_2 + j] += edges_num;
473 }
474 }
475
476 for (blender::int2 &edge : edges.slice(edges_num, edges_num)) {
477 edge += verts_num;
478 }
479 }
480
481 /* NOTE: copied vertex layers don't have flipped normals yet. do this after applying offset. */
482 if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) {
483 /* no even thickness, very simple */
484 float ofs_new_vgroup;
485
486 /* for clamping */
487 float *vert_lens = nullptr;
488 float *vert_angs = nullptr;
489 const float offset = fabsf(smd->offset) * smd->offset_clamp;
490 const float offset_sq = offset * offset;
491
492 /* for bevel weight */
493 float *edge_angs = nullptr;
494
495 if (do_clamp) {
496 vert_lens = MEM_malloc_arrayN<float>(verts_num, "vert_lens");
497 copy_vn_fl(vert_lens, int(verts_num), FLT_MAX);
498 for (uint i = 0; i < edges_num; i++) {
499 const float ed_len_sq = len_squared_v3v3(vert_positions[edges[i][0]],
500 vert_positions[edges[i][1]]);
501 vert_lens[edges[i][0]] = min_ff(vert_lens[edges[i][0]], ed_len_sq);
502 vert_lens[edges[i][1]] = min_ff(vert_lens[edges[i][1]], ed_len_sq);
503 }
504 }
505
506 if (do_angle_clamp || do_bevel_convex) {
507 uint eidx;
508 if (do_angle_clamp) {
509 vert_angs = MEM_malloc_arrayN<float>(verts_num, "vert_angs");
510 copy_vn_fl(vert_angs, int(verts_num), 0.5f * M_PI);
511 }
512 if (do_bevel_convex) {
513 edge_angs = MEM_malloc_arrayN<float>(edges_num, "edge_angs");
514 if (!do_rim) {
515 edge_users = MEM_malloc_arrayN<uint>(edges_num, "solid_mod edges");
516 }
517 }
518 uint(*edge_user_pairs)[2] = MEM_malloc_arrayN<uint[2]>(edges_num, "edge_user_pairs");
519 for (eidx = 0; eidx < edges_num; eidx++) {
520 edge_user_pairs[eidx][0] = INVALID_UNUSED;
521 edge_user_pairs[eidx][1] = INVALID_UNUSED;
522 }
523 for (const int64_t i : orig_faces.index_range()) {
524 const blender::IndexRange face = orig_faces[i];
525 int prev_corner_i = face.last();
526 for (const int corner_i : face) {
527 const int vert_i = orig_corner_verts[corner_i];
528 const int prev_vert_i = orig_corner_verts[prev_corner_i];
529 /* add edge user */
530 eidx = orig_corner_edges[prev_corner_i];
531 const blender::int2 &ed = orig_edges[eidx];
532 BLI_assert(ELEM(prev_vert_i, ed[0], ed[1]) && ELEM(vert_i, ed[0], ed[1]));
533 char flip = char((prev_vert_i > vert_i) == (ed[0] < ed[1]));
534 if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
535 edge_user_pairs[eidx][flip] = uint(i);
536 }
537 else {
538 edge_user_pairs[eidx][0] = INVALID_PAIR;
539 edge_user_pairs[eidx][1] = INVALID_PAIR;
540 }
541 prev_corner_i = corner_i;
542 }
543 }
544 float e[3];
545 for (uint i = 0; i < edges_num; i++) {
546 const blender::int2 &edge = orig_edges[i];
547 if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
548 !ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR))
549 {
550 const float *n0 = face_normals[edge_user_pairs[i][0]];
551 const float *n1 = face_normals[edge_user_pairs[i][1]];
552 sub_v3_v3v3(e, orig_vert_positions[edge[0]], orig_vert_positions[edge[1]]);
554 const float angle = angle_signed_on_axis_v3v3_v3(n0, n1, e);
555 if (do_angle_clamp) {
556 vert_angs[edge[0]] = max_ff(vert_angs[edge[0]], angle);
557 vert_angs[edge[1]] = max_ff(vert_angs[edge[1]], angle);
558 }
559 if (do_bevel_convex) {
560 edge_angs[i] = angle;
561 if (!do_rim) {
562 edge_users[i] = INVALID_PAIR;
563 }
564 }
565 }
566 }
567 MEM_freeN(edge_user_pairs);
568 }
569
570 if (ofs_new != 0.0f) {
571 uint i_orig, i_end;
572 bool do_shell_align;
573
574 ofs_new_vgroup = ofs_new;
575
576 uint vert_index;
578
579 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
580 const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
581 if (dvert) {
582 const MDeformVert *dv = &dvert[i];
583 if (defgrp_invert) {
584 ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
585 }
586 else {
587 ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
588 }
589 ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_new;
590 }
591 if (do_clamp && offset > FLT_EPSILON) {
592 /* always reset because we may have set before */
593 if (dvert == nullptr) {
594 ofs_new_vgroup = ofs_new;
595 }
596 if (do_angle_clamp) {
597 float cos_ang = cosf(((2 * M_PI) - vert_angs[i]) * 0.5f);
598 if (cos_ang > 0) {
599 float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
600 if (max_off < offset * 0.5f) {
601 ofs_new_vgroup *= max_off / offset * 2;
602 }
603 }
604 }
605 else {
606 if (vert_lens[i] < offset_sq) {
607 float scalar = sqrtf(vert_lens[i]) / offset;
608 ofs_new_vgroup *= scalar;
609 }
610 }
611 }
612 if (vert_nors) {
613 madd_v3_v3fl(vert_positions[vert_index], vert_nors[i], ofs_new_vgroup);
614 }
615 else {
616 madd_v3_v3fl(vert_positions[vert_index], vert_normals[i], ofs_new_vgroup);
617 }
618 }
619 }
620
621 if (ofs_orig != 0.0f) {
622 uint i_orig, i_end;
623 bool do_shell_align;
624
625 ofs_new_vgroup = ofs_orig;
626
627 /* as above but swapped */
628 uint vert_index;
630
631 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
632 const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
633 if (dvert) {
634 const MDeformVert *dv = &dvert[i];
635 if (defgrp_invert) {
636 ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
637 }
638 else {
639 ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
640 }
641 ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_orig;
642 }
643 if (do_clamp && offset > FLT_EPSILON) {
644 /* always reset because we may have set before */
645 if (dvert == nullptr) {
646 ofs_new_vgroup = ofs_orig;
647 }
648 if (do_angle_clamp) {
649 float cos_ang = cosf(vert_angs[i_orig] * 0.5f);
650 if (cos_ang > 0) {
651 float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
652 if (max_off < offset * 0.5f) {
653 ofs_new_vgroup *= max_off / offset * 2;
654 }
655 }
656 }
657 else {
658 if (vert_lens[i] < offset_sq) {
659 float scalar = sqrtf(vert_lens[i]) / offset;
660 ofs_new_vgroup *= scalar;
661 }
662 }
663 }
664 if (vert_nors) {
665 madd_v3_v3fl(vert_positions[vert_index], vert_nors[i], ofs_new_vgroup);
666 }
667 else {
668 madd_v3_v3fl(vert_positions[vert_index], vert_normals[i], ofs_new_vgroup);
669 }
670 }
671 }
672
673 if (do_bevel_convex) {
674 for (uint i = 0; i < edges_num; i++) {
675 if (edge_users[i] == INVALID_PAIR) {
676 float angle = edge_angs[i];
677 result_edge_bweight[i] = clamp_f(result_edge_bweight[i] +
678 (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
679 clamp_f(bevel_convex, -1.0f, 0.0f)),
680 0.0f,
681 1.0f);
682 if (do_shell) {
683 result_edge_bweight[i + edges_num] = clamp_f(
684 result_edge_bweight[i + edges_num] + (angle > M_PI ?
685 clamp_f(bevel_convex, 0.0f, 1.0f) :
686 clamp_f(bevel_convex, -1.0f, 0.0f)),
687 0,
688 1.0f);
689 }
690 }
691 }
692 if (!do_rim) {
693 MEM_freeN(edge_users);
694 }
695 MEM_freeN(edge_angs);
696 }
697
698 if (do_clamp) {
699 MEM_freeN(vert_lens);
700 if (do_angle_clamp) {
701 MEM_freeN(vert_angs);
702 }
703 }
704 }
705 else {
706#ifdef USE_NONMANIFOLD_WORKAROUND
707 const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0;
708#endif
709 /* same as EM_solidify() in editmesh_lib.c */
710 float *vert_angles = MEM_calloc_arrayN<float>(2 * verts_num, "mod_solid_pair"); /* 2 in 1 */
711 float *vert_accum = vert_angles + verts_num;
712 uint vidx;
713 uint i;
714
715 if (vert_nors == nullptr) {
716 vert_nors = MEM_malloc_arrayN<float[3]>(verts_num, "mod_solid_vno");
717 for (i = 0; i < verts_num; i++) {
718 copy_v3_v3(vert_nors[i], vert_normals[i]);
719 }
720 }
721
722 for (const int64_t i : blender::IndexRange(faces_num)) {
723 const blender::IndexRange face = orig_faces[i];
724 /* #bke::mesh::face_angles_calc logic is inlined here */
725 float nor_prev[3];
726 float nor_next[3];
727
728 int i_curr = face.size() - 1;
729 int i_next = 0;
730
731 const int *face_verts = &corner_verts[face.start()];
732 const int *face_edges = &corner_edges[face.start()];
733
735 nor_prev, vert_positions[face_verts[i_curr - 1]], vert_positions[face_verts[i_curr]]);
736 normalize_v3(nor_prev);
737
738 while (i_next < face.size()) {
739 float angle;
741 nor_next, vert_positions[face_verts[i_curr]], vert_positions[face_verts[i_next]]);
742 normalize_v3(nor_next);
743 angle = angle_normalized_v3v3(nor_prev, nor_next);
744
745 /* --- not related to angle calc --- */
746 angle = std::max(angle, FLT_EPSILON);
747
748 vidx = face_verts[i_curr];
749 vert_accum[vidx] += angle;
750
751#ifdef USE_NONMANIFOLD_WORKAROUND
752 /* skip 3+ face user edges */
753 if ((check_non_manifold == false) ||
754 LIKELY(!BLI_BITMAP_TEST(edge_tmp_tag, face_edges[i_curr]) &&
755 !BLI_BITMAP_TEST(edge_tmp_tag, face_edges[i_next])))
756 {
757 vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_normals[i]) *
758 angle;
759 }
760 else {
761 vert_angles[vidx] += angle;
762 }
763#else
764 vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_normals[i]) *
765 angle;
766#endif
767 /* --- end non-angle-calc section --- */
768
769 /* step */
770 copy_v3_v3(nor_prev, nor_next);
771 i_curr = i_next;
772 i_next++;
773 }
774 }
775
776 /* vertex group support */
777 if (dvert) {
778 const MDeformVert *dv = dvert;
779 float scalar;
780
781 if (defgrp_invert) {
782 for (i = 0; i < verts_num; i++, dv++) {
783 scalar = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
784 scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
785 vert_angles[i] *= scalar;
786 }
787 }
788 else {
789 for (i = 0; i < verts_num; i++, dv++) {
790 scalar = BKE_defvert_find_weight(dv, defgrp_index);
791 scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
792 vert_angles[i] *= scalar;
793 }
794 }
795 }
796
797 /* for angle clamp */
798 float *vert_angs = nullptr;
799 /* for bevel convex */
800 float *edge_angs = nullptr;
801
802 if (do_angle_clamp || do_bevel_convex) {
803 uint eidx;
804 if (do_angle_clamp) {
805 vert_angs = MEM_malloc_arrayN<float>(verts_num, "vert_angs even");
806 copy_vn_fl(vert_angs, int(verts_num), 0.5f * M_PI);
807 }
808 if (do_bevel_convex) {
809 edge_angs = MEM_malloc_arrayN<float>(edges_num, "edge_angs even");
810 if (!do_rim) {
811 edge_users = MEM_malloc_arrayN<uint>(edges_num, "solid_mod edges");
812 }
813 }
814 uint(*edge_user_pairs)[2] = MEM_malloc_arrayN<uint[2]>(edges_num, "edge_user_pairs");
815 for (eidx = 0; eidx < edges_num; eidx++) {
816 edge_user_pairs[eidx][0] = INVALID_UNUSED;
817 edge_user_pairs[eidx][1] = INVALID_UNUSED;
818 }
819 for (const int i : orig_faces.index_range()) {
820 const blender::IndexRange face = orig_faces[i];
821 int prev_corner_i = face.start() + face.size() - 1;
822 for (int j = 0; j < face.size(); j++) {
823 const int corner_i = face.start() + j;
824 const int vert_i = orig_corner_verts[corner_i];
825 const int prev_vert_i = orig_corner_verts[prev_corner_i];
826
827 /* add edge user */
828 eidx = orig_corner_edges[prev_corner_i];
829 const blender::int2 &edge = orig_edges[eidx];
830 BLI_assert(ELEM(prev_vert_i, edge[0], edge[1]) && ELEM(vert_i, edge[0], edge[1]));
831 char flip = char((prev_vert_i > vert_i) == (edge[0] < edge[1]));
832 if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
833 edge_user_pairs[eidx][flip] = uint(i);
834 }
835 else {
836 edge_user_pairs[eidx][0] = INVALID_PAIR;
837 edge_user_pairs[eidx][1] = INVALID_PAIR;
838 }
839 prev_corner_i = corner_i;
840 }
841 }
842 float e[3];
843 for (i = 0; i < edges_num; i++) {
844 const blender::int2 &edge = orig_edges[i];
845 if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
846 !ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR))
847 {
848 const float *n0 = face_normals[edge_user_pairs[i][0]];
849 const float *n1 = face_normals[edge_user_pairs[i][1]];
850 if (do_angle_clamp) {
851 const float angle = M_PI - angle_normalized_v3v3(n0, n1);
852 vert_angs[edge[0]] = max_ff(vert_angs[edge[0]], angle);
853 vert_angs[edge[1]] = max_ff(vert_angs[edge[1]], angle);
854 }
855 if (do_bevel_convex) {
856 sub_v3_v3v3(e, orig_vert_positions[edge[0]], orig_vert_positions[edge[1]]);
858 edge_angs[i] = angle_signed_on_axis_v3v3_v3(n0, n1, e);
859 if (!do_rim) {
860 edge_users[i] = INVALID_PAIR;
861 }
862 }
863 }
864 }
865 MEM_freeN(edge_user_pairs);
866 }
867
868 if (do_clamp) {
869 const float clamp_fac = 1 + (do_angle_clamp ? fabsf(smd->offset_fac) : 0);
870 const float offset = fabsf(smd->offset) * smd->offset_clamp * clamp_fac;
871 if (offset > FLT_EPSILON) {
872 float *vert_lens_sq = MEM_malloc_arrayN<float>(verts_num, "vert_lens_sq");
873 const float offset_sq = offset * offset;
874 copy_vn_fl(vert_lens_sq, int(verts_num), FLT_MAX);
875 for (i = 0; i < edges_num; i++) {
876 const float ed_len = len_squared_v3v3(vert_positions[edges[i][0]],
877 vert_positions[edges[i][1]]);
878 vert_lens_sq[edges[i][0]] = min_ff(vert_lens_sq[edges[i][0]], ed_len);
879 vert_lens_sq[edges[i][1]] = min_ff(vert_lens_sq[edges[i][1]], ed_len);
880 }
881 if (do_angle_clamp) {
882 for (i = 0; i < verts_num; i++) {
883 float cos_ang = cosf(vert_angs[i] * 0.5f);
884 if (cos_ang > 0) {
885 float max_off = sqrtf(vert_lens_sq[i]) * 0.5f / cos_ang;
886 if (max_off < offset * 0.5f) {
887 vert_angles[i] *= max_off / offset * 2;
888 }
889 }
890 }
891 MEM_freeN(vert_angs);
892 }
893 else {
894 for (i = 0; i < verts_num; i++) {
895 if (vert_lens_sq[i] < offset_sq) {
896 float scalar = sqrtf(vert_lens_sq[i]) / offset;
897 vert_angles[i] *= scalar;
898 }
899 }
900 }
901 MEM_freeN(vert_lens_sq);
902 }
903 }
904
905 if (do_bevel_convex) {
906 for (i = 0; i < edges_num; i++) {
907 if (edge_users[i] == INVALID_PAIR) {
908 float angle = edge_angs[i];
909 result_edge_bweight[i] = clamp_f(result_edge_bweight[i] +
910 (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
911 clamp_f(bevel_convex, -1.0f, 0.0f)),
912 0.0f,
913 1.0f);
914 if (do_shell) {
915 result_edge_bweight[i + edges_num] = clamp_f(
916 result_edge_bweight[i + edges_num] +
917 (angle > M_PI ? clamp_f(bevel_convex, 0, 1) : clamp_f(bevel_convex, -1, 0)),
918 0.0f,
919 1.0f);
920 }
921 }
922 }
923 if (!do_rim) {
924 MEM_freeN(edge_users);
925 }
926 MEM_freeN(edge_angs);
927 }
928
929#undef INVALID_UNUSED
930#undef INVALID_PAIR
931
932 if (ofs_new != 0.0f) {
933 uint i_orig, i_end;
934 bool do_shell_align;
935
936 uint vert_index;
938
939 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
940 const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
941 if (vert_accum[i_other]) { /* zero if unselected */
942 madd_v3_v3fl(vert_positions[vert_index],
943 vert_nors[i_other],
944 ofs_new * (vert_angles[i_other] / vert_accum[i_other]));
945 }
946 }
947 }
948
949 if (ofs_orig != 0.0f) {
950 uint i_orig, i_end;
951 bool do_shell_align;
952
953 /* same as above but swapped, intentional use of 'ofs_new' */
954 uint vert_index;
956
957 for (i_orig = 0; i_orig < i_end; i_orig++, vert_index++) {
958 const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig];
959 if (vert_accum[i_other]) { /* zero if unselected */
960 madd_v3_v3fl(vert_positions[vert_index],
961 vert_nors[i_other],
962 ofs_orig * (vert_angles[i_other] / vert_accum[i_other]));
963 }
964 }
965 }
966
967 MEM_freeN(vert_angles);
968 }
969
970#ifdef USE_NONMANIFOLD_WORKAROUND
971 MEM_SAFE_FREE(edge_tmp_tag);
972#endif
973
974 if (vert_nors) {
975 MEM_freeN(vert_nors);
976 }
977
978 /* Add vertex weights for rim and shell vgroups. */
979 if (shell_defgrp_index != -1 || rim_defgrp_index != -1) {
980 MDeformVert *dst_dvert = result->deform_verts_for_write().data();
981
982 /* Ultimate security check. */
983 if (dst_dvert != nullptr) {
984
985 if (rim_defgrp_index != -1) {
986 for (uint i = 0; i < rimVerts; i++) {
987 BKE_defvert_ensure_index(&dst_dvert[new_vert_arr[i]], rim_defgrp_index)->weight = 1.0f;
988 BKE_defvert_ensure_index(&dst_dvert[(do_shell ? new_vert_arr[i] : i) + verts_num],
989 rim_defgrp_index)
990 ->weight = 1.0f;
991 }
992 }
993
994 if (shell_defgrp_index != -1) {
995 for (uint i = verts_num; i < result->verts_num; i++) {
996 BKE_defvert_ensure_index(&dst_dvert[i], shell_defgrp_index)->weight = 1.0f;
997 }
998 }
999 }
1000 }
1001 if (do_rim) {
1002 uint i;
1003
1004 const float crease_rim = smd->crease_rim;
1005 const float crease_outer = smd->crease_outer;
1006 const float crease_inner = smd->crease_inner;
1007
1008 int *origindex_edge;
1009 int *orig_ed;
1010 uint j;
1011
1012 float *result_edge_crease = nullptr;
1013 if (crease_rim || crease_outer || crease_inner) {
1014 result_edge_crease = static_cast<float *>(CustomData_get_layer_named_for_write(
1015 &result->edge_data, CD_PROP_FLOAT, "crease_edge", result->edges_num));
1016 if (!result_edge_crease) {
1017 result_edge_crease = static_cast<float *>(CustomData_add_layer_named(
1018 &result->edge_data, CD_PROP_FLOAT, CD_SET_DEFAULT, result->edges_num, "crease_edge"));
1019 }
1020 }
1021
1022 /* add faces & edges */
1023 origindex_edge = static_cast<int *>(
1024 CustomData_get_layer_for_write(&result->edge_data, CD_ORIGINDEX, result->edges_num));
1025 orig_ed = (origindex_edge) ? &origindex_edge[(edges_num * stride) + newEdges] : nullptr;
1026 /* Start after copied edges. */
1027 int new_edge_index = int(edges_num * stride + newEdges);
1028 for (i = 0; i < rimVerts; i++) {
1029 edges[new_edge_index][0] = new_vert_arr[i];
1030 edges[new_edge_index][1] = (do_shell ? new_vert_arr[i] : i) + verts_num;
1031
1032 if (orig_vert_bweight) {
1033 result_edge_bweight[new_edge_index] = orig_vert_bweight[new_vert_arr[i]];
1034 }
1035
1036 if (orig_ed) {
1037 *orig_ed = ORIGINDEX_NONE;
1038 orig_ed++;
1039 }
1040
1041 if (crease_rim) {
1042 result_edge_crease[new_edge_index] = crease_rim;
1043 }
1044 new_edge_index++;
1045 }
1046
1047 /* faces */
1048 int new_face_index = int(faces_num * stride);
1049 blender::MutableSpan<int> new_corner_verts = corner_verts.drop_front(loops_num * stride);
1050 blender::MutableSpan<int> new_corner_edges = corner_edges.drop_front(loops_num * stride);
1051 j = 0;
1052 for (i = 0; i < newPolys; i++) {
1053 uint eidx = new_edge_arr[i];
1054 uint pidx = edge_users[eidx];
1055 int k1, k2;
1056 bool flip;
1057
1058 if (pidx >= faces_num) {
1059 pidx -= faces_num;
1060 flip = true;
1061 }
1062 else {
1063 flip = false;
1064 }
1065
1066 const blender::int2 &edge = edges[eidx];
1067
1068 /* copy most of the face settings */
1070 &mesh->face_data, &result->face_data, int(pidx), int((faces_num * stride) + i), 1);
1071
1072 const int old_face_size = orig_faces[pidx].size();
1073 face_offsets[new_face_index] = int(j + (loops_num * stride));
1074
1075 /* prev loop */
1076 k1 = face_offsets[pidx] + (((edge_order[eidx] - 1) + old_face_size) % old_face_size);
1077
1078 k2 = face_offsets[pidx] + (edge_order[eidx]);
1079
1081 &mesh->corner_data, &result->corner_data, k2, int((loops_num * stride) + j + 0), 1);
1083 &mesh->corner_data, &result->corner_data, k1, int((loops_num * stride) + j + 1), 1);
1085 &mesh->corner_data, &result->corner_data, k1, int((loops_num * stride) + j + 2), 1);
1087 &mesh->corner_data, &result->corner_data, k2, int((loops_num * stride) + j + 3), 1);
1088
1089 if (flip == false) {
1090 new_corner_verts[j] = edge[0];
1091 new_corner_edges[j++] = eidx;
1092
1093 new_corner_verts[j] = edge[1];
1094 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[1]] + newEdges;
1095
1096 new_corner_verts[j] = (do_shell ? edge[1] : old_vert_arr[edge[1]]) + verts_num;
1097 new_corner_edges[j++] = (do_shell ? eidx : i) + edges_num;
1098
1099 new_corner_verts[j] = (do_shell ? edge[0] : old_vert_arr[edge[0]]) + verts_num;
1100 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[0]] + newEdges;
1101 }
1102 else {
1103 new_corner_verts[j] = edge[1];
1104 new_corner_edges[j++] = eidx;
1105
1106 new_corner_verts[j] = edge[0];
1107 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[0]] + newEdges;
1108
1109 new_corner_verts[j] = (do_shell ? edge[0] : old_vert_arr[edge[0]]) + verts_num;
1110 new_corner_edges[j++] = (do_shell ? eidx : i) + edges_num;
1111
1112 new_corner_verts[j] = (do_shell ? edge[1] : old_vert_arr[edge[1]]) + verts_num;
1113 new_corner_edges[j++] = (edges_num * stride) + old_vert_arr[edge[1]] + newEdges;
1114 }
1115
1116 if (origindex_edge) {
1117 origindex_edge[new_corner_edges[j - 3]] = ORIGINDEX_NONE;
1118 origindex_edge[new_corner_edges[j - 1]] = ORIGINDEX_NONE;
1119 }
1120
1121 /* use the next material index if option enabled */
1122 if (mat_ofs_rim) {
1123 dst_material_index.span[new_face_index] += mat_ofs_rim;
1124 CLAMP(dst_material_index.span[new_face_index], 0, mat_nr_max);
1125 }
1126 if (crease_outer) {
1127 /* crease += crease_outer; without wrapping */
1128 float *cr = &(result_edge_crease[eidx]);
1129 float tcr = *cr + crease_outer;
1130 *cr = tcr > 1.0f ? 1.0f : tcr;
1131 }
1132
1133 if (crease_inner) {
1134 /* crease += crease_inner; without wrapping */
1135 float *cr = &(result_edge_crease[edges_num + (do_shell ? eidx : i)]);
1136 float tcr = *cr + crease_inner;
1137 *cr = tcr > 1.0f ? 1.0f : tcr;
1138 }
1139
1140 new_face_index++;
1141 }
1142
1143 MEM_freeN(new_vert_arr);
1144 MEM_freeN(new_edge_arr);
1145
1146 MEM_freeN(edge_users);
1147 MEM_freeN(edge_order);
1148 }
1149
1150 if (old_vert_arr) {
1151 MEM_freeN(old_vert_arr);
1152 }
1153
1154 dst_material_index.finish();
1155
1156 return result;
1157}
1158
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
@ CD_SET_DEFAULT
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
void * CustomData_add_layer_named(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, blender::StringRef name)
#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:814
int BKE_id_defgroup_name_index(const ID *id, blender::StringRef name)
Definition deform.cc:538
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:763
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)
@ CD_PROP_FLOAT
@ 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.
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
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
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
#define cosf(x)
#define fabsf(x)
#define sqrtf(x)
#define MEM_SAFE_FREE(v)
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 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