Blender V5.0
MOD_array.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <algorithm>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_utildefines.h"
16
17#include "BLI_math_matrix.h"
18#include "BLI_math_vector.h"
19#include "BLI_span.hh"
20
21#include "BLT_translation.hh"
22
23#include "DNA_defaults.h"
24#include "DNA_meshdata_types.h"
25#include "DNA_object_types.h"
26#include "DNA_screen_types.h"
27
28#include "BKE_anim_path.h"
29#include "BKE_attribute.hh"
30#include "BKE_curve.hh"
31#include "BKE_customdata.hh"
32#include "BKE_lib_id.hh"
33#include "BKE_lib_query.hh"
34#include "BKE_mesh.hh"
35#include "BKE_mesh_wrapper.hh"
36#include "BKE_modifier.hh"
37#include "BKE_object_deform.h"
38#include "BKE_object_types.hh"
39
41#include "UI_resources.hh"
42
43#include "RNA_access.hh"
44#include "RNA_prototypes.hh"
45
46#include "MOD_ui_common.hh"
47
48#include "DEG_depsgraph.hh"
49
51
52using namespace blender;
53
54static void init_data(ModifierData *md)
55{
57
59
61
62 /* Open the first sub-panel by default,
63 * it corresponds to Relative offset which is enabled too. */
65}
66
67static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
68{
70
71 walk(user_data, ob, (ID **)&amd->start_cap, IDWALK_CB_NOP);
72 walk(user_data, ob, (ID **)&amd->end_cap, IDWALK_CB_NOP);
73 walk(user_data, ob, (ID **)&amd->curve_ob, IDWALK_CB_NOP);
74 walk(user_data, ob, (ID **)&amd->offset_ob, IDWALK_CB_NOP);
75}
76
78{
80 bool need_transform_dependency = false;
81 if (amd->start_cap != nullptr) {
83 ctx->node, amd->start_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier Start Cap");
84 }
85 if (amd->end_cap != nullptr) {
87 ctx->node, amd->end_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier End Cap");
88 }
89 if (amd->curve_ob) {
91 ctx->node, amd->curve_ob, DEG_OB_COMP_GEOMETRY, "Array Modifier Curve");
93 }
94 if (amd->offset_ob != nullptr) {
96 ctx->node, amd->offset_ob, DEG_OB_COMP_TRANSFORM, "Array Modifier Offset");
97 need_transform_dependency = true;
98 }
99
100 if (need_transform_dependency) {
101 DEG_add_depends_on_transform_relation(ctx->node, "Array Modifier");
102 }
103}
104
105BLI_INLINE float sum_v3(const float v[3])
106{
107 return v[0] + v[1] + v[2];
108}
109
110/* Structure used for sorting vertices, when processing doubles */
112 int vertex_num; /* The original index of the vertex, prior to sorting */
113 float co[3]; /* Its coordinates */
114 float sum_co; /* `sum_v3(co)`: just so we don't do the sum many times. */
115};
116
117static int svert_sum_cmp(const void *e1, const void *e2)
118{
119 const SortVertsElem *sv1 = static_cast<const SortVertsElem *>(e1);
120 const SortVertsElem *sv2 = static_cast<const SortVertsElem *>(e2);
121
122 if (sv1->sum_co > sv2->sum_co) {
123 return 1;
124 }
125 if (sv1->sum_co < sv2->sum_co) {
126 return -1;
127 }
128
129 return 0;
130}
131
133 const Span<float3> vert_positions,
134 const int i_begin,
135 const int i_end)
136{
137 int i;
138 for (i = i_begin; i < i_end; i++, sv++) {
139 sv->vertex_num = i;
140 copy_v3_v3(sv->co, vert_positions[i]);
141 sv->sum_co = sum_v3(vert_positions[i]);
142 }
143}
144
152static void dm_mvert_map_doubles(int *doubles_map,
153 const Span<float3> vert_positions,
154 const int target_start,
155 const int target_verts_num,
156 const int source_start,
157 const int source_verts_num,
158 const float dist)
159{
160 const float dist3 = (float(M_SQRT3) + 0.00005f) * dist; /* Just above `sqrt(3)`. */
161 int i_source, i_target, i_target_low_bound, target_end, source_end;
162 SortVertsElem *sve_source, *sve_target, *sve_target_low_bound;
163 bool target_scan_completed;
164
165 target_end = target_start + target_verts_num;
166 source_end = source_start + source_verts_num;
167
168 /* build array of MVerts to be tested for merging */
169 SortVertsElem *sorted_verts_target = MEM_malloc_arrayN<SortVertsElem>(size_t(target_verts_num),
170 __func__);
171 SortVertsElem *sorted_verts_source = MEM_malloc_arrayN<SortVertsElem>(size_t(source_verts_num),
172 __func__);
173
174 /* Copy target vertices index and cos into SortVertsElem array */
175 svert_from_mvert(sorted_verts_target, vert_positions, target_start, target_end);
176
177 /* Copy source vertices index and cos into SortVertsElem array */
178 svert_from_mvert(sorted_verts_source, vert_positions, source_start, source_end);
179
180 /* sort arrays according to sum of vertex coordinates (sumco) */
181 qsort(sorted_verts_target, target_verts_num, sizeof(SortVertsElem), svert_sum_cmp);
182 qsort(sorted_verts_source, source_verts_num, sizeof(SortVertsElem), svert_sum_cmp);
183
184 sve_target_low_bound = sorted_verts_target;
185 i_target_low_bound = 0;
186 target_scan_completed = false;
187
188 /* Scan source vertices, in #SortVertsElem sorted array,
189 * all the while maintaining the lower bound of possible doubles in target vertices. */
190 for (i_source = 0, sve_source = sorted_verts_source; i_source < source_verts_num;
191 i_source++, sve_source++)
192 {
193 int best_target_vertex = -1;
194 float best_dist_sq = dist * dist;
195 float sve_source_sumco;
196
197 /* If source has already been assigned to a target (in an earlier call, with other chunks) */
198 if (doubles_map[sve_source->vertex_num] != -1) {
199 continue;
200 }
201
202 /* If target fully scanned already, then all remaining source vertices cannot have a double */
203 if (target_scan_completed) {
204 doubles_map[sve_source->vertex_num] = -1;
205 continue;
206 }
207
208 sve_source_sumco = sum_v3(sve_source->co);
209
210 /* Skip all target vertices that are more than dist3 lower in terms of sumco */
211 /* and advance the overall lower bound, applicable to all remaining vertices as well. */
212 while ((i_target_low_bound < target_verts_num) &&
213 (sve_target_low_bound->sum_co < sve_source_sumco - dist3))
214 {
215 i_target_low_bound++;
216 sve_target_low_bound++;
217 }
218 /* If end of target list reached, then no more possible doubles */
219 if (i_target_low_bound >= target_verts_num) {
220 doubles_map[sve_source->vertex_num] = -1;
221 target_scan_completed = true;
222 continue;
223 }
224 /* Test target candidates starting at the low bound of possible doubles,
225 * ordered in terms of sumco. */
226 i_target = i_target_low_bound;
227 sve_target = sve_target_low_bound;
228
229 /* i_target will scan vertices in the
230 * [v_source_sumco - dist3; v_source_sumco + dist3] range */
231
232 while ((i_target < target_verts_num) && (sve_target->sum_co <= sve_source_sumco + dist3)) {
233 /* Testing distance for candidate double in target */
234 /* v_target is within dist3 of v_source in terms of sumco; check real distance */
235 const float dist_sq = len_squared_v3v3(sve_source->co, sve_target->co);
236 if (dist_sq <= best_dist_sq) {
237 /* Potential double found */
238 best_dist_sq = dist_sq;
239 best_target_vertex = sve_target->vertex_num;
240
241 /* If target is already mapped, we only follow that mapping if final target remains
242 * close enough from current vert (otherwise no mapping at all).
243 * Note that if we later find another target closer than this one, then we check it.
244 * But if other potential targets are farther,
245 * then there will be no mapping at all for this source. */
246 while (best_target_vertex != -1 &&
247 !ELEM(doubles_map[best_target_vertex], -1, best_target_vertex))
248 {
249 if (compare_len_v3v3(vert_positions[sve_source->vertex_num],
250 vert_positions[doubles_map[best_target_vertex]],
251 dist))
252 {
253 best_target_vertex = doubles_map[best_target_vertex];
254 }
255 else {
256 best_target_vertex = -1;
257 }
258 }
259 }
260 i_target++;
261 sve_target++;
262 }
263 /* End of candidate scan: if none found then no doubles */
264 doubles_map[sve_source->vertex_num] = best_target_vertex;
265 }
266
267 MEM_freeN(sorted_verts_source);
268 MEM_freeN(sorted_verts_target);
269}
270
272 Mesh *cap_mesh,
273 const float cap_offset[4][4],
274 uint cap_verts_index,
275 uint cap_edges_index,
276 int cap_loops_index,
277 int cap_faces_index,
278 int cap_nverts,
279 int cap_nedges,
280 int cap_nloops,
281 int cap_nfaces,
282 int *remap,
283 int remap_len,
284 MutableSpan<float3> dst_vert_normals)
285{
286 using namespace blender;
287 int *index_orig;
288 int i;
289 int2 *edge;
290 const blender::Span<int> cap_face_offsets = cap_mesh->face_offsets();
291 blender::MutableSpan<float3> result_positions = result->vert_positions_for_write();
292 blender::MutableSpan<int2> result_edges = result->edges_for_write();
293 blender::MutableSpan<int> result_face_offsets = result->face_offsets_for_write();
294 blender::MutableSpan<int> result_corner_verts = result->corner_verts_for_write();
295 blender::MutableSpan<int> result_corner_edges = result->corner_edges_for_write();
296
297 CustomData_copy_data(&cap_mesh->vert_data, &result->vert_data, 0, cap_verts_index, cap_nverts);
298 CustomData_copy_data(&cap_mesh->edge_data, &result->edge_data, 0, cap_edges_index, cap_nedges);
300 &cap_mesh->corner_data, &result->corner_data, 0, cap_loops_index, cap_nloops);
301 CustomData_copy_data(&cap_mesh->face_data, &result->face_data, 0, cap_faces_index, cap_nfaces);
302
303 for (i = 0; i < cap_nverts; i++) {
304 mul_m4_v3(cap_offset, result_positions[cap_verts_index + i]);
305 }
306
307 /* We have to correct normals too, if we do not tag them as dirty later! */
308 if (!dst_vert_normals.is_empty()) {
309 for (i = 0; i < cap_nverts; i++) {
310 mul_mat3_m4_v3(cap_offset, dst_vert_normals[cap_verts_index + i]);
311 normalize_v3(dst_vert_normals[cap_verts_index + i]);
312 }
313 }
314
315 /* remap the vertex groups if necessary */
316 if (!result->deform_verts().is_empty()) {
317 MDeformVert *dvert = result->deform_verts_for_write().data();
318 BKE_object_defgroup_index_map_apply(&dvert[cap_verts_index], cap_nverts, remap, remap_len);
319 }
320
321 /* adjust cap edge vertex indices */
322 edge = &result_edges[cap_edges_index];
323 for (i = 0; i < cap_nedges; i++, edge++) {
324 (*edge) += cap_verts_index;
325 }
326
327 /* Adjust cap face loop-start indices. */
328 for (i = 0; i < cap_nfaces; i++) {
329 result_face_offsets[cap_faces_index + i] = cap_face_offsets[i] + cap_loops_index;
330 }
331
332 /* adjust cap loop vertex and edge indices */
333 for (i = 0; i < cap_nloops; i++) {
334 result_corner_verts[cap_loops_index + i] += cap_verts_index;
335 result_corner_edges[cap_loops_index + i] += cap_edges_index;
336 }
337
338 const bke::AttributeAccessor cap_attributes = cap_mesh->attributes();
339 if (const VArray cap_material_indices = *cap_attributes.lookup<int>("material_index",
341 {
342 bke::MutableAttributeAccessor result_attributes = result->attributes_for_write();
343 bke::SpanAttributeWriter<int> result_material_indices =
344 result_attributes.lookup_or_add_for_write_span<int>("material_index",
346 cap_material_indices.materialize(
347 result_material_indices.span.slice(cap_faces_index, cap_nfaces));
348 result_material_indices.finish();
349 }
350
351 /* Set #CD_ORIGINDEX. */
352 index_orig = static_cast<int *>(
353 CustomData_get_layer_for_write(&result->vert_data, CD_ORIGINDEX, result->verts_num));
354 if (index_orig) {
355 copy_vn_i(index_orig + cap_verts_index, cap_nverts, ORIGINDEX_NONE);
356 }
357
358 index_orig = static_cast<int *>(
359 CustomData_get_layer_for_write(&result->edge_data, CD_ORIGINDEX, result->edges_num));
360 if (index_orig) {
361 copy_vn_i(index_orig + cap_edges_index, cap_nedges, ORIGINDEX_NONE);
362 }
363
364 index_orig = static_cast<int *>(
365 CustomData_get_layer_for_write(&result->face_data, CD_ORIGINDEX, result->faces_num));
366 if (index_orig) {
367 copy_vn_i(index_orig + cap_faces_index, cap_nfaces, ORIGINDEX_NONE);
368 }
369
370 index_orig = static_cast<int *>(
371 CustomData_get_layer_for_write(&result->corner_data, CD_ORIGINDEX, result->corners_num));
372 if (index_orig) {
373 copy_vn_i(index_orig + cap_loops_index, cap_nloops, ORIGINDEX_NONE);
374 }
375}
376
378 const ModifierEvalContext *ctx,
379 Mesh *mesh)
380{
381 /* NOTE(@ideasman4): the `mesh` may be empty, the following code must account for this,
382 * merging start/end caps into the empty mesh. Avoid an early return here as it can cause
383 * problems if the expected custom-data layers don't exist in the resulting mesh,
384 * see: #107353, #132991. */
385
386 using namespace blender;
387
388 int2 *edge;
389 int i, j, c, count;
390 float length = amd->length;
391 /* offset matrix */
392 float offset[4][4];
393 float scale[3];
394 bool offset_has_scale;
395 float current_offset[4][4];
396 float final_offset[4][4];
397 int *full_doubles_map = nullptr;
398 int tot_doubles;
399
400 const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0;
401 const bool use_recalc_normals = BKE_mesh_vert_normals_are_dirty(mesh) || use_merge;
402 const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob != nullptr);
403
404 int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_nfaces = 0, start_cap_nloops = 0;
405 int end_cap_nverts = 0, end_cap_nedges = 0, end_cap_nfaces = 0, end_cap_nloops = 0;
406 int result_nverts = 0, result_nedges = 0, result_nfaces = 0, result_nloops = 0;
407 int chunk_nverts, chunk_nedges, chunk_nloops, chunk_nfaces;
408 int first_chunk_start, first_chunk_nverts, last_chunk_start, last_chunk_nverts;
409
410 Mesh *result, *start_cap_mesh = nullptr, *end_cap_mesh = nullptr;
411
412 int *vgroup_start_cap_remap = nullptr;
413 int vgroup_start_cap_remap_len = 0;
414 int *vgroup_end_cap_remap = nullptr;
415 int vgroup_end_cap_remap_len = 0;
416
417 chunk_nverts = mesh->verts_num;
418 chunk_nedges = mesh->edges_num;
419 chunk_nloops = mesh->corners_num;
420 chunk_nfaces = mesh->faces_num;
421
422 count = amd->count;
423
424 Object *start_cap_ob = amd->start_cap;
425 if (start_cap_ob && start_cap_ob != ctx->object) {
426 if (start_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) {
427 vgroup_start_cap_remap = BKE_object_defgroup_index_map_create(
428 start_cap_ob, ctx->object, &vgroup_start_cap_remap_len);
429 }
430
431 start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob);
432 if (start_cap_mesh) {
433 BKE_mesh_wrapper_ensure_mdata(start_cap_mesh);
434 start_cap_nverts = start_cap_mesh->verts_num;
435 start_cap_nedges = start_cap_mesh->edges_num;
436 start_cap_nloops = start_cap_mesh->corners_num;
437 start_cap_nfaces = start_cap_mesh->faces_num;
438 }
439 }
440 Object *end_cap_ob = amd->end_cap;
441 if (end_cap_ob && end_cap_ob != ctx->object) {
442 if (end_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) {
443 vgroup_end_cap_remap = BKE_object_defgroup_index_map_create(
444 end_cap_ob, ctx->object, &vgroup_end_cap_remap_len);
445 }
446
448 if (end_cap_mesh) {
449 BKE_mesh_wrapper_ensure_mdata(end_cap_mesh);
450 end_cap_nverts = end_cap_mesh->verts_num;
451 end_cap_nedges = end_cap_mesh->edges_num;
452 end_cap_nloops = end_cap_mesh->corners_num;
453 end_cap_nfaces = end_cap_mesh->faces_num;
454 }
455 }
456
457 /* Build up offset array, accumulating all settings options. */
458
459 unit_m4(offset);
460
461 if (amd->offset_type & MOD_ARR_OFF_CONST) {
462 add_v3_v3(offset[3], amd->offset);
463 }
464
466 const Bounds<float3> bounds = *mesh->bounds_min_max();
467 for (j = 3; j--;) {
468 offset[3][j] += amd->scale[j] * (bounds.max[j] - bounds.min[j]);
469 }
470 }
471
472 if (use_offset_ob) {
473 float obinv[4][4];
474 float result_mat[4][4];
475
476 if (ctx->object) {
477 invert_m4_m4(obinv, ctx->object->object_to_world().ptr());
478 }
479 else {
480 unit_m4(obinv);
481 }
482
483 mul_m4_series(result_mat, offset, obinv, amd->offset_ob->object_to_world().ptr());
484 copy_m4_m4(offset, result_mat);
485 }
486
487 /* Check if there is some scaling. If scaling, then we will not translate mapping */
488 mat4_to_size(scale, offset);
489 offset_has_scale = !is_one_v3(scale);
490
491 if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob != nullptr) {
492 Object *curve_ob = amd->curve_ob;
493 CurveCache *curve_cache = curve_ob->runtime->curve_cache;
494 if (curve_cache != nullptr && curve_cache->anim_path_accum_length != nullptr) {
495 float scale_fac = mat4_to_scale(curve_ob->object_to_world().ptr());
496 length = scale_fac * BKE_anim_path_get_length(curve_cache);
497 }
498 }
499
500 /* About 67 million vertices max seems a decent limit for now. */
501 const size_t max_verts_num = 1 << 26;
502
503 /* calculate the maximum number of copies which will fit within the
504 * prescribed length */
506 const float float_epsilon = 1e-6f;
507 bool offset_is_too_small = false;
508 float dist = len_v3(offset[3]);
509
510 if (dist > float_epsilon) {
511 /* this gives length = first copy start to last copy end
512 * add a tiny offset for floating point rounding errors */
513 count = (length + float_epsilon) / dist + 1;
514
515 /* Ensure we keep things to a reasonable level, in terms of rough total amount of generated
516 * vertices.
517 */
518 if ((size_t(count) * size_t(chunk_nverts) + size_t(start_cap_nverts) +
519 size_t(end_cap_nverts)) > max_verts_num)
520 {
521 count = 1;
522 offset_is_too_small = true;
523 }
524 }
525 else {
526 /* if the offset has no translation, just make one copy */
527 count = 1;
528 offset_is_too_small = true;
529 }
530
531 if (offset_is_too_small) {
533 ctx->object,
534 &amd->modifier,
535 "The offset is too small, we cannot generate the amount of geometry it would require");
536 }
537 }
538 /* Ensure we keep things to a reasonable level, in terms of rough total amount of generated
539 * vertices.
540 */
541 else if ((size_t(count) * size_t(chunk_nverts) + size_t(start_cap_nverts) +
542 size_t(end_cap_nverts)) > max_verts_num)
543 {
544 count = 1;
546 &amd->modifier,
547 "The amount of copies is too high, we cannot generate the amount of "
548 "geometry it would require");
549 }
550
551 count = std::max(count, 1);
552
553 /* The number of verts, edges, loops, faces, before eventually merging doubles */
554 result_nverts = chunk_nverts * count + start_cap_nverts + end_cap_nverts;
555 result_nedges = chunk_nedges * count + start_cap_nedges + end_cap_nedges;
556 result_nloops = chunk_nloops * count + start_cap_nloops + end_cap_nloops;
557 result_nfaces = chunk_nfaces * count + start_cap_nfaces + end_cap_nfaces;
558
559 /* Initialize a result dm */
561 mesh, result_nverts, result_nedges, result_nfaces, result_nloops);
562 blender::MutableSpan<float3> result_positions = result->vert_positions_for_write();
563 blender::MutableSpan<int2> result_edges = result->edges_for_write();
564 blender::MutableSpan<int> result_face_offsets = result->face_offsets_for_write();
565 blender::MutableSpan<int> result_corner_verts = result->corner_verts_for_write();
566 blender::MutableSpan<int> result_corner_edges = result->corner_edges_for_write();
567
568 if (use_merge) {
569 /* Will need full_doubles_map for handling merge */
570 full_doubles_map = MEM_malloc_arrayN<int>(size_t(result_nverts), __func__);
571 copy_vn_i(full_doubles_map, result_nverts, -1);
572 }
573
574 /* copy customdata to original geometry */
575 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, 0, chunk_nverts);
576 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, 0, chunk_nedges);
577 CustomData_copy_data(&mesh->corner_data, &result->corner_data, 0, 0, chunk_nloops);
578 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, 0, chunk_nfaces);
579
580 result_face_offsets.take_front(mesh->faces_num).copy_from(mesh->face_offsets().drop_back(1));
581
582 /* Remember first chunk, in case of cap merge */
583 first_chunk_start = 0;
584 first_chunk_nverts = chunk_nverts;
585
586 unit_m4(current_offset);
587 blender::Span<blender::float3> src_vert_normals;
588 Vector<float3> dst_vert_normals;
589 if (!use_recalc_normals) {
590 src_vert_normals = mesh->vert_normals();
591 dst_vert_normals.reinitialize(result->verts_num);
592 dst_vert_normals.as_mutable_span()
593 .take_front(src_vert_normals.size())
594 .copy_from(src_vert_normals);
595 }
596
597 for (c = 1; c < count; c++) {
598 /* copy customdata to new geometry */
599 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, c * chunk_nverts, chunk_nverts);
600 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, c * chunk_nedges, chunk_nedges);
602 &mesh->corner_data, &result->corner_data, 0, c * chunk_nloops, chunk_nloops);
603 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, c * chunk_nfaces, chunk_nfaces);
604
605 /* recalculate cumulative offset here */
606 mul_m4_m4m4(current_offset, current_offset, offset);
607
608 if (chunk_nverts) {
609 /* Apply offset to all new verts. */
610 const int vert_offset = c * chunk_nverts;
611 for (i = 0; i < chunk_nverts; i++) {
612 const int i_dst = vert_offset + i;
613 mul_m4_v3(current_offset, result_positions[i_dst]);
614
615 /* We have to correct normals too, if we do not tag them as dirty! */
616 if (!dst_vert_normals.is_empty()) {
617 copy_v3_v3(dst_vert_normals[i_dst], src_vert_normals[i]);
618 mul_mat3_m4_v3(current_offset, dst_vert_normals[i_dst]);
619 normalize_v3(dst_vert_normals[i_dst]);
620 }
621 }
622 }
623
624 if (chunk_nedges) {
625 /* Adjust edge vertex indices. */
626 edge = &result_edges[c * chunk_nedges];
627 for (i = 0; i < chunk_nedges; i++, edge++) {
628 (*edge) += c * chunk_nverts;
629 }
630 }
631
632 if (chunk_nfaces) {
633 for (i = 0; i < chunk_nfaces; i++) {
634 result_face_offsets[c * chunk_nfaces + i] = result_face_offsets[i] + c * chunk_nloops;
635 }
636 }
637
638 if (chunk_nloops) {
639 /* Adjust loop vertex and edge indices. */
640 const int chunk_corner_start = c * chunk_nloops;
641 for (i = 0; i < chunk_nloops; i++) {
642 result_corner_verts[chunk_corner_start + i] += c * chunk_nverts;
643 result_corner_edges[chunk_corner_start + i] += c * chunk_nedges;
644 }
645 }
646
647 /* Handle merge between chunk n and n-1 */
648 if (use_merge && (c >= 1)) {
649 if (!offset_has_scale && (c >= 2)) {
650 /* Mapping chunk 3 to chunk 2 is a translation of mapping 2 to 1
651 * ... that is except if scaling makes the distance grow */
652 int k;
653 int this_chunk_index = c * chunk_nverts;
654 int prev_chunk_index = (c - 1) * chunk_nverts;
655 for (k = 0; k < chunk_nverts; k++, this_chunk_index++, prev_chunk_index++) {
656 int target = full_doubles_map[prev_chunk_index];
657 if (target != -1) {
658 target += chunk_nverts; /* translate mapping */
659 while (target != -1 && !ELEM(full_doubles_map[target], -1, target)) {
660 /* If target is already mapped, we only follow that mapping if final target remains
661 * close enough from current vert (otherwise no mapping at all). */
662 if (compare_len_v3v3(result_positions[this_chunk_index],
663 result_positions[full_doubles_map[target]],
664 amd->merge_dist))
665 {
666 target = full_doubles_map[target];
667 }
668 else {
669 target = -1;
670 }
671 }
672 }
673 full_doubles_map[this_chunk_index] = target;
674 }
675 }
676 else {
677 dm_mvert_map_doubles(full_doubles_map,
678 result_positions,
679 (c - 1) * chunk_nverts,
680 chunk_nverts,
681 c * chunk_nverts,
682 chunk_nverts,
683 amd->merge_dist);
684 }
685 }
686 }
687
688 /* handle UVs */
689 if (chunk_nloops > 0 && is_zero_v2(amd->uv_offset) == false) {
690 const int totuv = CustomData_number_of_layers(&result->corner_data, CD_PROP_FLOAT2);
691 for (i = 0; i < totuv; i++) {
693 &result->corner_data, CD_PROP_FLOAT2, i, result->corners_num));
694 uv_map += chunk_nloops;
695 for (c = 1; c < count; c++) {
696 const float uv_offset[2] = {
697 amd->uv_offset[0] * float(c),
698 amd->uv_offset[1] * float(c),
699 };
700 int l_index = chunk_nloops;
701 for (; l_index-- != 0; uv_map++) {
702 (*uv_map)[0] += uv_offset[0];
703 (*uv_map)[1] += uv_offset[1];
704 }
705 }
706 }
707 }
708
709 if (!use_merge && !mesh->runtime->subsurf_optimal_display_edges.is_empty()) {
710 const BoundedBitSpan src = mesh->runtime->subsurf_optimal_display_edges;
711
712 result->runtime->subsurf_optimal_display_edges.resize(result->edges_num);
713 MutableBoundedBitSpan dst = result->runtime->subsurf_optimal_display_edges;
714 for (const int i : IndexRange(count)) {
715 dst.slice({i * mesh->edges_num, mesh->edges_num}).copy_from(src);
716 }
717
718 if (start_cap_mesh) {
719 MutableBitSpan cap_bits = dst.slice(
720 {result_nedges - start_cap_nedges - end_cap_nedges, start_cap_mesh->edges_num});
721 if (start_cap_mesh->runtime->subsurf_optimal_display_edges.is_empty()) {
722 cap_bits.set_all(true);
723 }
724 else {
725 cap_bits.copy_from(start_cap_mesh->runtime->subsurf_optimal_display_edges);
726 }
727 }
728 if (end_cap_mesh) {
729 MutableBitSpan cap_bits = dst.slice(
730 {result_nedges - end_cap_nedges, end_cap_mesh->edges_num});
731 if (end_cap_mesh->runtime->subsurf_optimal_display_edges.is_empty()) {
732 cap_bits.set_all(true);
733 }
734 else {
735 cap_bits.copy_from(end_cap_mesh->runtime->subsurf_optimal_display_edges);
736 }
737 }
738 }
739
740 last_chunk_start = (count - 1) * chunk_nverts;
741 last_chunk_nverts = chunk_nverts;
742
743 copy_m4_m4(final_offset, current_offset);
744
745 if (use_merge && (amd->flags & MOD_ARR_MERGEFINAL) && (count > 1)) {
746 /* Merge first and last copies */
747 dm_mvert_map_doubles(full_doubles_map,
748 result_positions,
749 last_chunk_start,
750 last_chunk_nverts,
751 first_chunk_start,
752 first_chunk_nverts,
753 amd->merge_dist);
754 }
755
756 /* start capping */
757 if (start_cap_mesh) {
758 float start_offset[4][4];
759 int start_cap_start = result_nverts - start_cap_nverts - end_cap_nverts;
760 invert_m4_m4(start_offset, offset);
762 start_cap_mesh,
763 start_offset,
764 result_nverts - start_cap_nverts - end_cap_nverts,
765 result_nedges - start_cap_nedges - end_cap_nedges,
766 result_nloops - start_cap_nloops - end_cap_nloops,
767 result_nfaces - start_cap_nfaces - end_cap_nfaces,
768 start_cap_nverts,
769 start_cap_nedges,
770 start_cap_nloops,
771 start_cap_nfaces,
772 vgroup_start_cap_remap,
773 vgroup_start_cap_remap_len,
774 dst_vert_normals);
775 /* Identify doubles with first chunk */
776 if (use_merge) {
777 dm_mvert_map_doubles(full_doubles_map,
778 result_positions,
779 first_chunk_start,
780 first_chunk_nverts,
781 start_cap_start,
782 start_cap_nverts,
783 amd->merge_dist);
784 }
785 }
786
787 if (end_cap_mesh) {
788 float end_offset[4][4];
789 int end_cap_start = result_nverts - end_cap_nverts;
790 mul_m4_m4m4(end_offset, current_offset, offset);
792 end_cap_mesh,
793 end_offset,
794 result_nverts - end_cap_nverts,
795 result_nedges - end_cap_nedges,
796 result_nloops - end_cap_nloops,
797 result_nfaces - end_cap_nfaces,
798 end_cap_nverts,
799 end_cap_nedges,
800 end_cap_nloops,
801 end_cap_nfaces,
802 vgroup_end_cap_remap,
803 vgroup_end_cap_remap_len,
804 dst_vert_normals);
805 /* Identify doubles with last chunk */
806 if (use_merge) {
807 dm_mvert_map_doubles(full_doubles_map,
808 result_positions,
809 last_chunk_start,
810 last_chunk_nverts,
811 end_cap_start,
812 end_cap_nverts,
813 amd->merge_dist);
814 }
815 }
816 /* done capping */
817
818 if (!dst_vert_normals.is_empty()) {
819 blender::bke::mesh_vert_normals_assign(*result, std::move(dst_vert_normals));
820 }
821
822 /* Handle merging */
823 tot_doubles = 0;
824 if (use_merge) {
825 for (i = 0; i < result_nverts; i++) {
826 int new_i = full_doubles_map[i];
827 if (new_i != -1) {
828 /* We have to follow chains of doubles
829 * (merge start/end especially is likely to create some),
830 * those are not supported at all by `geometry::mesh_merge_verts`! */
831 while (!ELEM(full_doubles_map[new_i], -1, new_i)) {
832 new_i = full_doubles_map[new_i];
833 }
834 if (i == new_i) {
835 full_doubles_map[i] = -1;
836 }
837 else {
838 full_doubles_map[i] = new_i;
839 tot_doubles++;
840 }
841 }
842 }
843 if (tot_doubles > 0) {
844 Mesh *tmp = result;
846 *tmp, MutableSpan<int>{full_doubles_map, result->verts_num}, tot_doubles, false);
847 BKE_id_free(nullptr, tmp);
848 }
849 MEM_freeN(full_doubles_map);
850 }
851
852 if (vgroup_start_cap_remap) {
853 MEM_freeN(vgroup_start_cap_remap);
854 }
855 if (vgroup_end_cap_remap) {
856 MEM_freeN(vgroup_end_cap_remap);
857 }
858
859 return result;
860}
861
862static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
863{
865 return arrayModifier_doArray(amd, ctx, mesh);
866}
867
868static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
869{
871
872 /* The object type check is only needed here in case we have a placeholder
873 * object assigned (because the library containing the curve/mesh is missing).
874 *
875 * In other cases it should be impossible to have a type mismatch.
876 */
877
878 if (amd->curve_ob && amd->curve_ob->type != OB_CURVES_LEGACY) {
879 return true;
880 }
881 if (amd->start_cap && amd->start_cap->type != OB_MESH) {
882 return true;
883 }
884 if (amd->end_cap && amd->end_cap->type != OB_MESH) {
885 return true;
886 }
887
888 return false;
889}
890
891static void panel_draw(const bContext * /*C*/, Panel *panel)
892{
893 uiLayout *layout = panel->layout;
894
895 PointerRNA ob_ptr;
897
898 layout->use_property_split_set(true);
899
900 layout->prop(ptr, "fit_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
901
902 int fit_type = RNA_enum_get(ptr, "fit_type");
903 if (fit_type == MOD_ARR_FIXEDCOUNT) {
904 layout->prop(ptr, "count", UI_ITEM_NONE, std::nullopt, ICON_NONE);
905 }
906 else if (fit_type == MOD_ARR_FITLENGTH) {
907 layout->prop(ptr, "fit_length", UI_ITEM_NONE, std::nullopt, ICON_NONE);
908 }
909 else if (fit_type == MOD_ARR_FITCURVE) {
910 layout->prop(ptr, "curve", UI_ITEM_NONE, std::nullopt, ICON_NONE);
911 }
912
914}
915
916static void relative_offset_header_draw(const bContext * /*C*/, Panel *panel)
917{
918 uiLayout *layout = panel->layout;
919
921
922 layout->prop(ptr, "use_relative_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
923}
924
925static void relative_offset_draw(const bContext * /*C*/, Panel *panel)
926{
927 uiLayout *layout = panel->layout;
928
930
931 layout->use_property_split_set(true);
932
933 uiLayout *col = &layout->column(false);
934
935 col->active_set(RNA_boolean_get(ptr, "use_relative_offset"));
936 col->prop(ptr, "relative_offset_displace", UI_ITEM_NONE, IFACE_("Factor"), ICON_NONE);
937}
938
939static void constant_offset_header_draw(const bContext * /*C*/, Panel *panel)
940{
941 uiLayout *layout = panel->layout;
942
944
945 layout->prop(ptr, "use_constant_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
946}
947
948static void constant_offset_draw(const bContext * /*C*/, Panel *panel)
949{
950 uiLayout *layout = panel->layout;
951
953
954 layout->use_property_split_set(true);
955
956 uiLayout *col = &layout->column(false);
957
958 col->active_set(RNA_boolean_get(ptr, "use_constant_offset"));
959 col->prop(ptr, "constant_offset_displace", UI_ITEM_NONE, IFACE_("Distance"), ICON_NONE);
960}
961
965static void object_offset_header_draw(const bContext * /*C*/, Panel *panel)
966{
967 uiLayout *layout = panel->layout;
968
970
971 layout->prop(ptr, "use_object_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
972}
973
974static void object_offset_draw(const bContext * /*C*/, Panel *panel)
975{
976 uiLayout *layout = panel->layout;
977
979
980 layout->use_property_split_set(true);
981
982 uiLayout *col = &layout->column(false);
983
984 col->active_set(RNA_boolean_get(ptr, "use_object_offset"));
985 col->prop(ptr, "offset_object", UI_ITEM_NONE, IFACE_("Object"), ICON_NONE);
986}
987
988static void symmetry_panel_header_draw(const bContext * /*C*/, Panel *panel)
989{
990 uiLayout *layout = panel->layout;
991
993
994 layout->prop(ptr, "use_merge_vertices", UI_ITEM_NONE, IFACE_("Merge"), ICON_NONE);
995}
996
997static void symmetry_panel_draw(const bContext * /*C*/, Panel *panel)
998{
999 uiLayout *layout = panel->layout;
1000
1002
1003 layout->use_property_split_set(true);
1004
1005 uiLayout *col = &layout->column(false);
1006 col->active_set(RNA_boolean_get(ptr, "use_merge_vertices"));
1007 col->prop(ptr, "merge_threshold", UI_ITEM_NONE, IFACE_("Distance"), ICON_NONE);
1008 col->prop(
1009 ptr, "use_merge_vertices_cap", UI_ITEM_NONE, IFACE_("First and Last Copies"), ICON_NONE);
1010}
1011
1012static void uv_panel_draw(const bContext * /*C*/, Panel *panel)
1013{
1014 uiLayout *col;
1015 uiLayout *layout = panel->layout;
1016
1018
1019 layout->use_property_split_set(true);
1020
1021 col = &layout->column(true);
1022 col->prop(ptr, "offset_u", UI_ITEM_R_EXPAND, IFACE_("Offset U"), ICON_NONE);
1023 col->prop(ptr, "offset_v", UI_ITEM_R_EXPAND, IFACE_("V"), ICON_NONE);
1024}
1025
1026static void caps_panel_draw(const bContext * /*C*/, Panel *panel)
1027{
1028 uiLayout *col;
1029 uiLayout *layout = panel->layout;
1030
1032
1033 layout->use_property_split_set(true);
1034
1035 col = &layout->column(false);
1036 col->prop(ptr, "start_cap", UI_ITEM_NONE, IFACE_("Cap Start"), ICON_NONE);
1037 col->prop(ptr, "end_cap", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
1038}
1039
1040static void panel_register(ARegionType *region_type)
1041{
1043 modifier_subpanel_register(region_type,
1044 "relative_offset",
1045 "",
1048 panel_type);
1049 modifier_subpanel_register(region_type,
1050 "constant_offset",
1051 "",
1054 panel_type);
1056 region_type, "object_offset", "", object_offset_header_draw, object_offset_draw, panel_type);
1058 region_type, "merge", "", symmetry_panel_header_draw, symmetry_panel_draw, panel_type);
1059 modifier_subpanel_register(region_type, "uv", "UVs", nullptr, uv_panel_draw, panel_type);
1060 modifier_subpanel_register(region_type, "caps", "Caps", nullptr, caps_panel_draw, panel_type);
1061}
1062
1064 /*idname*/ "Array",
1065 /*name*/ N_("Array"),
1066 /*struct_name*/ "ArrayModifierData",
1067 /*struct_size*/ sizeof(ArrayModifierData),
1068 /*srna*/ &RNA_ArrayModifier,
1073 /*icon*/ ICON_MOD_ARRAY,
1074
1075 /*copy_data*/ BKE_modifier_copydata_generic,
1076
1077 /*deform_verts*/ nullptr,
1078 /*deform_matrices*/ nullptr,
1079 /*deform_verts_EM*/ nullptr,
1080 /*deform_matrices_EM*/ nullptr,
1081 /*modify_mesh*/ modify_mesh,
1082 /*modify_geometry_set*/ nullptr,
1083
1084 /*init_data*/ init_data,
1085 /*required_data_mask*/ nullptr,
1086 /*free_data*/ nullptr,
1087 /*is_disabled*/ is_disabled,
1088 /*update_depsgraph*/ update_depsgraph,
1089 /*depends_on_time*/ nullptr,
1090 /*depends_on_normals*/ nullptr,
1091 /*foreach_ID_link*/ foreach_ID_link,
1092 /*foreach_tex_link*/ nullptr,
1093 /*free_runtime_data*/ nullptr,
1094 /*panel_register*/ panel_register,
1095 /*blend_write*/ nullptr,
1096 /*blend_read*/ nullptr,
1097 /*foreach_cache*/ nullptr,
1098 /*foreach_working_space_color*/ nullptr,
1099};
float BKE_anim_path_get_length(const struct CurveCache *curve_cache)
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)
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)
void BKE_id_free(Main *bmain, void *idv)
@ IDWALK_CB_NOP
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
bool BKE_mesh_vert_normals_are_dirty(const Mesh *mesh)
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
Mesh * BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval)
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_AcceptsCVs
@ eModifierTypeFlag_SupportsMapping
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
@ eModifierTypeFlag_AcceptsMesh
void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(3
Functions for dealing with objects and deform verts, used by painting and tools.
int * BKE_object_defgroup_index_map_create(struct Object *ob_src, struct Object *ob_dst, int *r_map_len)
void BKE_object_defgroup_index_map_apply(struct MDeformVert *dvert, int dvert_len, const int *map, int map_len)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
#define M_SQRT3
float mat4_to_scale(const float mat[4][4])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void mul_m4_v3(const float M[4][4], float r[3])
#define mul_m4_series(...)
void copy_m4_m4(float m1[4][4], const float m2[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mat4_to_size(float size[3], const float M[4][4])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
void unit_m4(float m[4][4])
MINLINE bool is_one_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
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])
void copy_vn_i(int *array_tar, int size, int val)
MINLINE bool compare_len_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
#define ELEM(...)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define IFACE_(msgid)
@ DAG_EVAL_NEED_CURVE_PATH
void DEG_add_depends_on_transform_relation(DepsNodeHandle *node_handle, const char *description)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
void DEG_add_special_eval_flag(DepsNodeHandle *handle, ID *id, uint32_t flag)
@ DEG_OB_COMP_GEOMETRY
@ DEG_OB_COMP_TRANSFORM
@ CD_PROP_FLOAT2
#define DNA_struct_default_get(struct_name)
@ MOD_ARR_OFF_RELATIVE
@ MOD_ARR_OFF_CONST
@ MOD_ARR_OFF_OBJ
@ MOD_ARR_FITCURVE
@ MOD_ARR_FIXEDCOUNT
@ MOD_ARR_FITLENGTH
@ MOD_ARR_MERGE
@ MOD_ARR_MERGEFINAL
@ eModifierType_Array
Object is a sort of wrapper for general info.
@ OB_MESH
@ OB_CURVES_LEGACY
@ UI_PANEL_DATA_EXPAND_ROOT
@ UI_SUBPANEL_DATA_EXPAND_1
static bool is_disabled
Read Guarded memory(de)allocation.
static void init_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void panel_draw(const bContext *, Panel *panel)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void init_data(ModifierData *md)
Definition MOD_array.cc:54
static void object_offset_header_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:965
static void caps_panel_draw(const bContext *, Panel *panel)
static void panel_register(ARegionType *region_type)
static int svert_sum_cmp(const void *e1, const void *e2)
Definition MOD_array.cc:117
ModifierTypeInfo modifierType_Array
static void symmetry_panel_header_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:988
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
Definition MOD_array.cc:862
static void constant_offset_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:948
static void svert_from_mvert(SortVertsElem *sv, const Span< float3 > vert_positions, const int i_begin, const int i_end)
Definition MOD_array.cc:132
static void symmetry_panel_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:997
static void relative_offset_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:925
static void panel_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:891
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
Definition MOD_array.cc:67
static void mesh_merge_transform(Mesh *result, Mesh *cap_mesh, const float cap_offset[4][4], uint cap_verts_index, uint cap_edges_index, int cap_loops_index, int cap_faces_index, int cap_nverts, int cap_nedges, int cap_nloops, int cap_nfaces, int *remap, int remap_len, MutableSpan< float3 > dst_vert_normals)
Definition MOD_array.cc:271
BLI_INLINE float sum_v3(const float v[3])
Definition MOD_array.cc:105
static void uv_panel_draw(const bContext *, Panel *panel)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
Definition MOD_array.cc:77
static Mesh * arrayModifier_doArray(ArrayModifierData *amd, const ModifierEvalContext *ctx, Mesh *mesh)
Definition MOD_array.cc:377
static void dm_mvert_map_doubles(int *doubles_map, const Span< float3 > vert_positions, const int target_start, const int target_verts_num, const int source_start, const int source_verts_num, const float dist)
Definition MOD_array.cc:152
static void relative_offset_header_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:916
static void object_offset_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:974
static void constant_offset_header_draw(const bContext *, Panel *panel)
Definition MOD_array.cc:939
PanelType * modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelType *parent)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
@ UI_ITEM_R_EXPAND
#define UI_ITEM_NONE
ATTR_WARN_UNUSED_RESULT const BMVert * v
AttributeSet attributes
constexpr bool is_empty() const
Definition BLI_span.hh:509
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 int64_t size() const
Definition BLI_span.hh:252
bool is_empty() const
MutableSpan< T > as_mutable_span()
void reinitialize(const int64_t new_size)
MutableBitSpan slice(const IndexRange range) const
void copy_from(const BitSpan other)
Definition bit_span.cc:66
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
uint col
float length(VecOp< float, D >) RET
static void update_depsgraph(tGraphSliderOp *gso)
int count
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
void mesh_vert_normals_assign(Mesh &mesh, Span< float3 > vert_normals)
Mesh * mesh_merge_verts(const Mesh &mesh, MutableSpan< int > vert_dest_map, int vert_dest_map_len, const bool do_mix_data)
VecBase< float, 2 > float2
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
struct Object * start_cap
struct Object * offset_ob
struct Object * curve_ob
struct Object * end_cap
const float * anim_path_accum_length
Definition BKE_curve.hh:49
Definition DNA_ID.h:414
int corners_num
CustomData edge_data
int edges_num
MeshRuntimeHandle * runtime
CustomData corner_data
CustomData face_data
CustomData vert_data
int faces_num
int verts_num
ObjectRuntimeHandle * runtime
struct uiLayout * layout
float co[3]
Definition MOD_array.cc:113
uiLayout & column(bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238