Blender V4.3
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
11#include "MEM_guardedalloc.h"
12
13#include "BLI_utildefines.h"
14
15#include "BLI_math_matrix.h"
16#include "BLI_math_vector.h"
17#include "BLI_span.hh"
18
19#include "BLT_translation.hh"
20
21#include "DNA_defaults.h"
22#include "DNA_meshdata_types.h"
23#include "DNA_object_types.h"
24#include "DNA_screen_types.h"
25
26#include "BKE_anim_path.h"
27#include "BKE_attribute.hh"
28#include "BKE_curve.hh"
29#include "BKE_customdata.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_lib_query.hh"
32#include "BKE_mesh.hh"
33#include "BKE_mesh_wrapper.hh"
34#include "BKE_modifier.hh"
35#include "BKE_object_deform.h"
36#include "BKE_object_types.hh"
37
38#include "UI_interface.hh"
39#include "UI_resources.hh"
40
41#include "RNA_access.hh"
42#include "RNA_prototypes.hh"
43
44#include "MOD_ui_common.hh"
45
46#include "DEG_depsgraph.hh"
47
49
50using namespace blender;
51
52static void init_data(ModifierData *md)
53{
55
57
59
60 /* Open the first sub-panel by default,
61 * it corresponds to Relative offset which is enabled too. */
63}
64
65static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
66{
68
69 walk(user_data, ob, (ID **)&amd->start_cap, IDWALK_CB_NOP);
70 walk(user_data, ob, (ID **)&amd->end_cap, IDWALK_CB_NOP);
71 walk(user_data, ob, (ID **)&amd->curve_ob, IDWALK_CB_NOP);
72 walk(user_data, ob, (ID **)&amd->offset_ob, IDWALK_CB_NOP);
73}
74
76{
78 bool need_transform_dependency = false;
79 if (amd->start_cap != nullptr) {
81 ctx->node, amd->start_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier Start Cap");
82 }
83 if (amd->end_cap != nullptr) {
85 ctx->node, amd->end_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier End Cap");
86 }
87 if (amd->curve_ob) {
89 ctx->node, amd->curve_ob, DEG_OB_COMP_GEOMETRY, "Array Modifier Curve");
91 }
92 if (amd->offset_ob != nullptr) {
94 ctx->node, amd->offset_ob, DEG_OB_COMP_TRANSFORM, "Array Modifier Offset");
95 need_transform_dependency = true;
96 }
97
98 if (need_transform_dependency) {
99 DEG_add_depends_on_transform_relation(ctx->node, "Array Modifier");
100 }
101}
102
103BLI_INLINE float sum_v3(const float v[3])
104{
105 return v[0] + v[1] + v[2];
106}
107
108/* Structure used for sorting vertices, when processing doubles */
110 int vertex_num; /* The original index of the vertex, prior to sorting */
111 float co[3]; /* Its coordinates */
112 float sum_co; /* `sum_v3(co)`: just so we don't do the sum many times. */
113};
114
115static int svert_sum_cmp(const void *e1, const void *e2)
116{
117 const SortVertsElem *sv1 = static_cast<const SortVertsElem *>(e1);
118 const SortVertsElem *sv2 = static_cast<const SortVertsElem *>(e2);
119
120 if (sv1->sum_co > sv2->sum_co) {
121 return 1;
122 }
123 if (sv1->sum_co < sv2->sum_co) {
124 return -1;
125 }
126
127 return 0;
128}
129
131 const Span<float3> vert_positions,
132 const int i_begin,
133 const int i_end)
134{
135 int i;
136 for (i = i_begin; i < i_end; i++, sv++) {
137 sv->vertex_num = i;
138 copy_v3_v3(sv->co, vert_positions[i]);
139 sv->sum_co = sum_v3(vert_positions[i]);
140 }
141}
142
150static void dm_mvert_map_doubles(int *doubles_map,
151 const Span<float3> vert_positions,
152 const int target_start,
153 const int target_verts_num,
154 const int source_start,
155 const int source_verts_num,
156 const float dist)
157{
158 const float dist3 = (float(M_SQRT3) + 0.00005f) * dist; /* Just above `sqrt(3)`. */
159 int i_source, i_target, i_target_low_bound, target_end, source_end;
160 SortVertsElem *sve_source, *sve_target, *sve_target_low_bound;
161 bool target_scan_completed;
162
163 target_end = target_start + target_verts_num;
164 source_end = source_start + source_verts_num;
165
166 /* build array of MVerts to be tested for merging */
167 SortVertsElem *sorted_verts_target = static_cast<SortVertsElem *>(
168 MEM_malloc_arrayN(target_verts_num, sizeof(SortVertsElem), __func__));
169 SortVertsElem *sorted_verts_source = static_cast<SortVertsElem *>(
170 MEM_malloc_arrayN(source_verts_num, sizeof(SortVertsElem), __func__));
171
172 /* Copy target vertices index and cos into SortVertsElem array */
173 svert_from_mvert(sorted_verts_target, vert_positions, target_start, target_end);
174
175 /* Copy source vertices index and cos into SortVertsElem array */
176 svert_from_mvert(sorted_verts_source, vert_positions, source_start, source_end);
177
178 /* sort arrays according to sum of vertex coordinates (sumco) */
179 qsort(sorted_verts_target, target_verts_num, sizeof(SortVertsElem), svert_sum_cmp);
180 qsort(sorted_verts_source, source_verts_num, sizeof(SortVertsElem), svert_sum_cmp);
181
182 sve_target_low_bound = sorted_verts_target;
183 i_target_low_bound = 0;
184 target_scan_completed = false;
185
186 /* Scan source vertices, in #SortVertsElem sorted array,
187 * all the while maintaining the lower bound of possible doubles in target vertices. */
188 for (i_source = 0, sve_source = sorted_verts_source; i_source < source_verts_num;
189 i_source++, sve_source++)
190 {
191 int best_target_vertex = -1;
192 float best_dist_sq = dist * dist;
193 float sve_source_sumco;
194
195 /* If source has already been assigned to a target (in an earlier call, with other chunks) */
196 if (doubles_map[sve_source->vertex_num] != -1) {
197 continue;
198 }
199
200 /* If target fully scanned already, then all remaining source vertices cannot have a double */
201 if (target_scan_completed) {
202 doubles_map[sve_source->vertex_num] = -1;
203 continue;
204 }
205
206 sve_source_sumco = sum_v3(sve_source->co);
207
208 /* Skip all target vertices that are more than dist3 lower in terms of sumco */
209 /* and advance the overall lower bound, applicable to all remaining vertices as well. */
210 while ((i_target_low_bound < target_verts_num) &&
211 (sve_target_low_bound->sum_co < sve_source_sumco - dist3))
212 {
213 i_target_low_bound++;
214 sve_target_low_bound++;
215 }
216 /* If end of target list reached, then no more possible doubles */
217 if (i_target_low_bound >= target_verts_num) {
218 doubles_map[sve_source->vertex_num] = -1;
219 target_scan_completed = true;
220 continue;
221 }
222 /* Test target candidates starting at the low bound of possible doubles,
223 * ordered in terms of sumco. */
224 i_target = i_target_low_bound;
225 sve_target = sve_target_low_bound;
226
227 /* i_target will scan vertices in the
228 * [v_source_sumco - dist3; v_source_sumco + dist3] range */
229
230 while ((i_target < target_verts_num) && (sve_target->sum_co <= sve_source_sumco + dist3)) {
231 /* Testing distance for candidate double in target */
232 /* v_target is within dist3 of v_source in terms of sumco; check real distance */
233 float dist_sq;
234 if ((dist_sq = len_squared_v3v3(sve_source->co, sve_target->co)) <= best_dist_sq) {
235 /* Potential double found */
236 best_dist_sq = dist_sq;
237 best_target_vertex = sve_target->vertex_num;
238
239 /* If target is already mapped, we only follow that mapping if final target remains
240 * close enough from current vert (otherwise no mapping at all).
241 * Note that if we later find another target closer than this one, then we check it.
242 * But if other potential targets are farther,
243 * then there will be no mapping at all for this source. */
244 while (best_target_vertex != -1 &&
245 !ELEM(doubles_map[best_target_vertex], -1, best_target_vertex))
246 {
247 if (compare_len_v3v3(vert_positions[sve_source->vertex_num],
248 vert_positions[doubles_map[best_target_vertex]],
249 dist))
250 {
251 best_target_vertex = doubles_map[best_target_vertex];
252 }
253 else {
254 best_target_vertex = -1;
255 }
256 }
257 }
258 i_target++;
259 sve_target++;
260 }
261 /* End of candidate scan: if none found then no doubles */
262 doubles_map[sve_source->vertex_num] = best_target_vertex;
263 }
264
265 MEM_freeN(sorted_verts_source);
266 MEM_freeN(sorted_verts_target);
267}
268
269static void mesh_merge_transform(Mesh *result,
270 Mesh *cap_mesh,
271 const float cap_offset[4][4],
272 uint cap_verts_index,
273 uint cap_edges_index,
274 int cap_loops_index,
275 int cap_faces_index,
276 int cap_nverts,
277 int cap_nedges,
278 int cap_nloops,
279 int cap_nfaces,
280 int *remap,
281 int remap_len,
282 MutableSpan<float3> dst_vert_normals)
283{
284 using namespace blender;
285 int *index_orig;
286 int i;
287 int2 *edge;
288 const blender::Span<int> cap_face_offsets = cap_mesh->face_offsets();
289 blender::MutableSpan<float3> result_positions = result->vert_positions_for_write();
290 blender::MutableSpan<int2> result_edges = result->edges_for_write();
291 blender::MutableSpan<int> result_face_offsets = result->face_offsets_for_write();
292 blender::MutableSpan<int> result_corner_verts = result->corner_verts_for_write();
293 blender::MutableSpan<int> result_corner_edges = result->corner_edges_for_write();
294
295 CustomData_copy_data(&cap_mesh->vert_data, &result->vert_data, 0, cap_verts_index, cap_nverts);
296 CustomData_copy_data(&cap_mesh->edge_data, &result->edge_data, 0, cap_edges_index, cap_nedges);
298 &cap_mesh->corner_data, &result->corner_data, 0, cap_loops_index, cap_nloops);
299 CustomData_copy_data(&cap_mesh->face_data, &result->face_data, 0, cap_faces_index, cap_nfaces);
300
301 for (i = 0; i < cap_nverts; i++) {
302 mul_m4_v3(cap_offset, result_positions[cap_verts_index + i]);
303 }
304
305 /* We have to correct normals too, if we do not tag them as dirty later! */
306 if (!dst_vert_normals.is_empty()) {
307 for (i = 0; i < cap_nverts; i++) {
308 mul_mat3_m4_v3(cap_offset, dst_vert_normals[cap_verts_index + i]);
309 normalize_v3(dst_vert_normals[cap_verts_index + i]);
310 }
311 }
312
313 /* remap the vertex groups if necessary */
314 if (!result->deform_verts().is_empty()) {
315 MDeformVert *dvert = result->deform_verts_for_write().data();
316 BKE_object_defgroup_index_map_apply(&dvert[cap_verts_index], cap_nverts, remap, remap_len);
317 }
318
319 /* adjust cap edge vertex indices */
320 edge = &result_edges[cap_edges_index];
321 for (i = 0; i < cap_nedges; i++, edge++) {
322 (*edge) += cap_verts_index;
323 }
324
325 /* Adjust cap face loop-start indices. */
326 for (i = 0; i < cap_nfaces; i++) {
327 result_face_offsets[cap_faces_index + i] = cap_face_offsets[i] + cap_loops_index;
328 }
329
330 /* adjust cap loop vertex and edge indices */
331 for (i = 0; i < cap_nloops; i++) {
332 result_corner_verts[cap_loops_index + i] += cap_verts_index;
333 result_corner_edges[cap_loops_index + i] += cap_edges_index;
334 }
335
336 const bke::AttributeAccessor cap_attributes = cap_mesh->attributes();
337 if (const VArray cap_material_indices = *cap_attributes.lookup<int>("material_index",
338 bke::AttrDomain::Face))
339 {
340 bke::MutableAttributeAccessor result_attributes = result->attributes_for_write();
341 bke::SpanAttributeWriter<int> result_material_indices =
342 result_attributes.lookup_or_add_for_write_span<int>("material_index",
343 bke::AttrDomain::Face);
344 cap_material_indices.materialize(
345 result_material_indices.span.slice(cap_faces_index, cap_nfaces));
346 result_material_indices.finish();
347 }
348
349 /* Set #CD_ORIGINDEX. */
350 index_orig = static_cast<int *>(
351 CustomData_get_layer_for_write(&result->vert_data, CD_ORIGINDEX, result->verts_num));
352 if (index_orig) {
353 copy_vn_i(index_orig + cap_verts_index, cap_nverts, ORIGINDEX_NONE);
354 }
355
356 index_orig = static_cast<int *>(
357 CustomData_get_layer_for_write(&result->edge_data, CD_ORIGINDEX, result->edges_num));
358 if (index_orig) {
359 copy_vn_i(index_orig + cap_edges_index, cap_nedges, ORIGINDEX_NONE);
360 }
361
362 index_orig = static_cast<int *>(
363 CustomData_get_layer_for_write(&result->face_data, CD_ORIGINDEX, result->faces_num));
364 if (index_orig) {
365 copy_vn_i(index_orig + cap_faces_index, cap_nfaces, ORIGINDEX_NONE);
366 }
367
368 index_orig = static_cast<int *>(
369 CustomData_get_layer_for_write(&result->corner_data, CD_ORIGINDEX, result->corners_num));
370 if (index_orig) {
371 copy_vn_i(index_orig + cap_loops_index, cap_nloops, ORIGINDEX_NONE);
372 }
373}
374
376 const ModifierEvalContext *ctx,
377 Mesh *mesh)
378{
379 using namespace blender;
380 if (mesh->verts_num == 0) {
381 /* Output just the start cap even if the mesh is empty. */
382 Object *start_cap_ob = amd->start_cap;
383 if (start_cap_ob && start_cap_ob != ctx->object) {
384 Mesh *start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob);
385 if (start_cap_mesh) {
386 BKE_mesh_wrapper_ensure_mdata(start_cap_mesh);
387 return BKE_mesh_copy_for_eval(*start_cap_mesh);
388 }
389 }
390 return mesh;
391 }
392
393 int2 *edge;
394 int i, j, c, count;
395 float length = amd->length;
396 /* offset matrix */
397 float offset[4][4];
398 float scale[3];
399 bool offset_has_scale;
400 float current_offset[4][4];
401 float final_offset[4][4];
402 int *full_doubles_map = nullptr;
403 int tot_doubles;
404
405 const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0;
406 const bool use_recalc_normals = BKE_mesh_vert_normals_are_dirty(mesh) || use_merge;
407 const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob != nullptr);
408
409 int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_nfaces = 0, start_cap_nloops = 0;
410 int end_cap_nverts = 0, end_cap_nedges = 0, end_cap_nfaces = 0, end_cap_nloops = 0;
411 int result_nverts = 0, result_nedges = 0, result_nfaces = 0, result_nloops = 0;
412 int chunk_nverts, chunk_nedges, chunk_nloops, chunk_nfaces;
413 int first_chunk_start, first_chunk_nverts, last_chunk_start, last_chunk_nverts;
414
415 Mesh *result, *start_cap_mesh = nullptr, *end_cap_mesh = nullptr;
416
417 int *vgroup_start_cap_remap = nullptr;
418 int vgroup_start_cap_remap_len = 0;
419 int *vgroup_end_cap_remap = nullptr;
420 int vgroup_end_cap_remap_len = 0;
421
422 chunk_nverts = mesh->verts_num;
423 chunk_nedges = mesh->edges_num;
424 chunk_nloops = mesh->corners_num;
425 chunk_nfaces = mesh->faces_num;
426
427 count = amd->count;
428
429 Object *start_cap_ob = amd->start_cap;
430 if (start_cap_ob && start_cap_ob != ctx->object) {
431 if (start_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) {
432 vgroup_start_cap_remap = BKE_object_defgroup_index_map_create(
433 start_cap_ob, ctx->object, &vgroup_start_cap_remap_len);
434 }
435
436 start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob);
437 if (start_cap_mesh) {
438 BKE_mesh_wrapper_ensure_mdata(start_cap_mesh);
439 start_cap_nverts = start_cap_mesh->verts_num;
440 start_cap_nedges = start_cap_mesh->edges_num;
441 start_cap_nloops = start_cap_mesh->corners_num;
442 start_cap_nfaces = start_cap_mesh->faces_num;
443 }
444 }
445 Object *end_cap_ob = amd->end_cap;
446 if (end_cap_ob && end_cap_ob != ctx->object) {
447 if (end_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) {
448 vgroup_end_cap_remap = BKE_object_defgroup_index_map_create(
449 end_cap_ob, ctx->object, &vgroup_end_cap_remap_len);
450 }
451
453 if (end_cap_mesh) {
454 BKE_mesh_wrapper_ensure_mdata(end_cap_mesh);
455 end_cap_nverts = end_cap_mesh->verts_num;
456 end_cap_nedges = end_cap_mesh->edges_num;
457 end_cap_nloops = end_cap_mesh->corners_num;
458 end_cap_nfaces = end_cap_mesh->faces_num;
459 }
460 }
461
462 /* Build up offset array, accumulating all settings options. */
463
464 unit_m4(offset);
465
466 if (amd->offset_type & MOD_ARR_OFF_CONST) {
467 add_v3_v3(offset[3], amd->offset);
468 }
469
471 const Bounds<float3> bounds = *mesh->bounds_min_max();
472 for (j = 3; j--;) {
473 offset[3][j] += amd->scale[j] * (bounds.max[j] - bounds.min[j]);
474 }
475 }
476
477 if (use_offset_ob) {
478 float obinv[4][4];
479 float result_mat[4][4];
480
481 if (ctx->object) {
482 invert_m4_m4(obinv, ctx->object->object_to_world().ptr());
483 }
484 else {
485 unit_m4(obinv);
486 }
487
488 mul_m4_series(result_mat, offset, obinv, amd->offset_ob->object_to_world().ptr());
489 copy_m4_m4(offset, result_mat);
490 }
491
492 /* Check if there is some scaling. If scaling, then we will not translate mapping */
493 mat4_to_size(scale, offset);
494 offset_has_scale = !is_one_v3(scale);
495
496 if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob != nullptr) {
497 Object *curve_ob = amd->curve_ob;
498 CurveCache *curve_cache = curve_ob->runtime->curve_cache;
499 if (curve_cache != nullptr && curve_cache->anim_path_accum_length != nullptr) {
500 float scale_fac = mat4_to_scale(curve_ob->object_to_world().ptr());
501 length = scale_fac * BKE_anim_path_get_length(curve_cache);
502 }
503 }
504
505 /* About 67 million vertices max seems a decent limit for now. */
506 const size_t max_verts_num = 1 << 26;
507
508 /* calculate the maximum number of copies which will fit within the
509 * prescribed length */
511 const float float_epsilon = 1e-6f;
512 bool offset_is_too_small = false;
513 float dist = len_v3(offset[3]);
514
515 if (dist > float_epsilon) {
516 /* this gives length = first copy start to last copy end
517 * add a tiny offset for floating point rounding errors */
518 count = (length + float_epsilon) / dist + 1;
519
520 /* Ensure we keep things to a reasonable level, in terms of rough total amount of generated
521 * vertices.
522 */
523 if ((size_t(count) * size_t(chunk_nverts) + size_t(start_cap_nverts) +
524 size_t(end_cap_nverts)) > max_verts_num)
525 {
526 count = 1;
527 offset_is_too_small = true;
528 }
529 }
530 else {
531 /* if the offset has no translation, just make one copy */
532 count = 1;
533 offset_is_too_small = true;
534 }
535
536 if (offset_is_too_small) {
538 ctx->object,
539 &amd->modifier,
540 "The offset is too small, we cannot generate the amount of geometry it would require");
541 }
542 }
543 /* Ensure we keep things to a reasonable level, in terms of rough total amount of generated
544 * vertices.
545 */
546 else if ((size_t(count) * size_t(chunk_nverts) + size_t(start_cap_nverts) +
547 size_t(end_cap_nverts)) > max_verts_num)
548 {
549 count = 1;
551 &amd->modifier,
552 "The amount of copies is too high, we cannot generate the amount of "
553 "geometry it would require");
554 }
555
556 if (count < 1) {
557 count = 1;
558 }
559
560 /* The number of verts, edges, loops, faces, before eventually merging doubles */
561 result_nverts = chunk_nverts * count + start_cap_nverts + end_cap_nverts;
562 result_nedges = chunk_nedges * count + start_cap_nedges + end_cap_nedges;
563 result_nloops = chunk_nloops * count + start_cap_nloops + end_cap_nloops;
564 result_nfaces = chunk_nfaces * count + start_cap_nfaces + end_cap_nfaces;
565
566 /* Initialize a result dm */
568 mesh, result_nverts, result_nedges, result_nfaces, result_nloops);
569 blender::MutableSpan<float3> result_positions = result->vert_positions_for_write();
570 blender::MutableSpan<int2> result_edges = result->edges_for_write();
571 blender::MutableSpan<int> result_face_offsets = result->face_offsets_for_write();
572 blender::MutableSpan<int> result_corner_verts = result->corner_verts_for_write();
573 blender::MutableSpan<int> result_corner_edges = result->corner_edges_for_write();
574
575 if (use_merge) {
576 /* Will need full_doubles_map for handling merge */
577 full_doubles_map = static_cast<int *>(MEM_malloc_arrayN(result_nverts, sizeof(int), __func__));
578 copy_vn_i(full_doubles_map, result_nverts, -1);
579 }
580
581 /* copy customdata to original geometry */
582 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, 0, chunk_nverts);
583 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, 0, chunk_nedges);
584 CustomData_copy_data(&mesh->corner_data, &result->corner_data, 0, 0, chunk_nloops);
585 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, 0, chunk_nfaces);
586
587 result_face_offsets.take_front(mesh->faces_num).copy_from(mesh->face_offsets().drop_back(1));
588
589 /* Remember first chunk, in case of cap merge */
590 first_chunk_start = 0;
591 first_chunk_nverts = chunk_nverts;
592
593 unit_m4(current_offset);
594 blender::Span<blender::float3> src_vert_normals;
595 Vector<float3> dst_vert_normals;
596 if (!use_recalc_normals) {
597 src_vert_normals = mesh->vert_normals();
598 dst_vert_normals.reinitialize(result->verts_num);
599 dst_vert_normals.as_mutable_span()
600 .take_front(src_vert_normals.size())
601 .copy_from(src_vert_normals);
602 }
603
604 for (c = 1; c < count; c++) {
605 /* copy customdata to new geometry */
606 CustomData_copy_data(&mesh->vert_data, &result->vert_data, 0, c * chunk_nverts, chunk_nverts);
607 CustomData_copy_data(&mesh->edge_data, &result->edge_data, 0, c * chunk_nedges, chunk_nedges);
609 &mesh->corner_data, &result->corner_data, 0, c * chunk_nloops, chunk_nloops);
610 CustomData_copy_data(&mesh->face_data, &result->face_data, 0, c * chunk_nfaces, chunk_nfaces);
611
612 const int vert_offset = c * chunk_nverts;
613
614 /* recalculate cumulative offset here */
615 mul_m4_m4m4(current_offset, current_offset, offset);
616
617 /* apply offset to all new verts */
618 for (i = 0; i < chunk_nverts; i++) {
619 const int i_dst = vert_offset + i;
620 mul_m4_v3(current_offset, result_positions[i_dst]);
621
622 /* We have to correct normals too, if we do not tag them as dirty! */
623 if (!dst_vert_normals.is_empty()) {
624 copy_v3_v3(dst_vert_normals[i_dst], src_vert_normals[i]);
625 mul_mat3_m4_v3(current_offset, dst_vert_normals[i_dst]);
626 normalize_v3(dst_vert_normals[i_dst]);
627 }
628 }
629
630 /* adjust edge vertex indices */
631 edge = &result_edges[c * chunk_nedges];
632 for (i = 0; i < chunk_nedges; i++, edge++) {
633 (*edge) += c * chunk_nverts;
634 }
635
636 for (i = 0; i < chunk_nfaces; i++) {
637 result_face_offsets[c * chunk_nfaces + i] = result_face_offsets[i] + c * chunk_nloops;
638 }
639
640 /* adjust loop vertex and edge indices */
641 const int chunk_corner_start = c * chunk_nloops;
642 for (i = 0; i < chunk_nloops; i++) {
643 result_corner_verts[chunk_corner_start + i] += c * chunk_nverts;
644 result_corner_edges[chunk_corner_start + i] += c * chunk_nedges;
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 dmloopuv += 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; dmloopuv++) {
702 (*dmloopuv)[0] += uv_offset[0];
703 (*dmloopuv)[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;
845 result = geometry::mesh_merge_verts(
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 uiLayoutSetPropSep(layout, true);
899
900 uiItemR(layout, ptr, "fit_type", UI_ITEM_NONE, nullptr, ICON_NONE);
901
902 int fit_type = RNA_enum_get(ptr, "fit_type");
903 if (fit_type == MOD_ARR_FIXEDCOUNT) {
904 uiItemR(layout, ptr, "count", UI_ITEM_NONE, nullptr, ICON_NONE);
905 }
906 else if (fit_type == MOD_ARR_FITLENGTH) {
907 uiItemR(layout, ptr, "fit_length", UI_ITEM_NONE, nullptr, ICON_NONE);
908 }
909 else if (fit_type == MOD_ARR_FITCURVE) {
910 uiItemR(layout, ptr, "curve", UI_ITEM_NONE, nullptr, ICON_NONE);
911 }
912
913 modifier_panel_end(layout, ptr);
914}
915
916static void relative_offset_header_draw(const bContext * /*C*/, Panel *panel)
917{
918 uiLayout *layout = panel->layout;
919
921
922 uiItemR(layout, ptr, "use_relative_offset", UI_ITEM_NONE, nullptr, ICON_NONE);
923}
924
925static void relative_offset_draw(const bContext * /*C*/, Panel *panel)
926{
927 uiLayout *layout = panel->layout;
928
930
931 uiLayoutSetPropSep(layout, true);
932
933 uiLayout *col = uiLayoutColumn(layout, false);
934
935 uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_relative_offset"));
936 uiItemR(col, 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 uiItemR(layout, ptr, "use_constant_offset", UI_ITEM_NONE, nullptr, ICON_NONE);
946}
947
948static void constant_offset_draw(const bContext * /*C*/, Panel *panel)
949{
950 uiLayout *layout = panel->layout;
951
953
954 uiLayoutSetPropSep(layout, true);
955
956 uiLayout *col = uiLayoutColumn(layout, false);
957
958 uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_constant_offset"));
959 uiItemR(col, 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 uiItemR(layout, ptr, "use_object_offset", UI_ITEM_NONE, nullptr, ICON_NONE);
972}
973
974static void object_offset_draw(const bContext * /*C*/, Panel *panel)
975{
976 uiLayout *layout = panel->layout;
977
979
980 uiLayoutSetPropSep(layout, true);
981
982 uiLayout *col = uiLayoutColumn(layout, false);
983
984 uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_object_offset"));
985 uiItemR(col, 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 uiItemR(layout, 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 uiLayoutSetPropSep(layout, true);
1004
1005 uiLayout *col = uiLayoutColumn(layout, false);
1006 uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_merge_vertices"));
1007 uiItemR(col, ptr, "merge_threshold", UI_ITEM_NONE, IFACE_("Distance"), ICON_NONE);
1008 uiItemR(col,
1009 ptr,
1010 "use_merge_vertices_cap",
1012 IFACE_("First and Last Copies"),
1013 ICON_NONE);
1014}
1015
1016static void uv_panel_draw(const bContext * /*C*/, Panel *panel)
1017{
1018 uiLayout *col;
1019 uiLayout *layout = panel->layout;
1020
1022
1023 uiLayoutSetPropSep(layout, true);
1024
1025 col = uiLayoutColumn(layout, true);
1026 uiItemR(col, ptr, "offset_u", UI_ITEM_R_EXPAND, IFACE_("Offset U"), ICON_NONE);
1027 uiItemR(col, ptr, "offset_v", UI_ITEM_R_EXPAND, IFACE_("V"), ICON_NONE);
1028}
1029
1030static void caps_panel_draw(const bContext * /*C*/, Panel *panel)
1031{
1032 uiLayout *col;
1033 uiLayout *layout = panel->layout;
1034
1036
1037 uiLayoutSetPropSep(layout, true);
1038
1039 col = uiLayoutColumn(layout, false);
1040 uiItemR(col, ptr, "start_cap", UI_ITEM_NONE, IFACE_("Cap Start"), ICON_NONE);
1041 uiItemR(col, ptr, "end_cap", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
1042}
1043
1044static void panel_register(ARegionType *region_type)
1045{
1047 modifier_subpanel_register(region_type,
1048 "relative_offset",
1049 "",
1052 panel_type);
1053 modifier_subpanel_register(region_type,
1054 "constant_offset",
1055 "",
1058 panel_type);
1060 region_type, "object_offset", "", object_offset_header_draw, object_offset_draw, panel_type);
1062 region_type, "merge", "", symmetry_panel_header_draw, symmetry_panel_draw, panel_type);
1063 modifier_subpanel_register(region_type, "uv", "UVs", nullptr, uv_panel_draw, panel_type);
1064 modifier_subpanel_register(region_type, "caps", "Caps", nullptr, caps_panel_draw, panel_type);
1065}
1066
1068 /*idname*/ "Array",
1069 /*name*/ N_("Array"),
1070 /*struct_name*/ "ArrayModifierData",
1071 /*struct_size*/ sizeof(ArrayModifierData),
1072 /*srna*/ &RNA_ArrayModifier,
1077 /*icon*/ ICON_MOD_ARRAY,
1078
1079 /*copy_data*/ BKE_modifier_copydata_generic,
1080
1081 /*deform_verts*/ nullptr,
1082 /*deform_matrices*/ nullptr,
1083 /*deform_verts_EM*/ nullptr,
1084 /*deform_matrices_EM*/ nullptr,
1085 /*modify_mesh*/ modify_mesh,
1086 /*modify_geometry_set*/ nullptr,
1087
1088 /*init_data*/ init_data,
1089 /*required_data_mask*/ nullptr,
1090 /*free_data*/ nullptr,
1091 /*is_disabled*/ is_disabled,
1092 /*update_depsgraph*/ update_depsgraph,
1093 /*depends_on_time*/ nullptr,
1094 /*depends_on_normals*/ nullptr,
1095 /*foreach_ID_link*/ foreach_ID_link,
1096 /*foreach_tex_link*/ nullptr,
1097 /*free_runtime_data*/ nullptr,
1098 /*panel_register*/ panel_register,
1099 /*blend_write*/ nullptr,
1100 /*blend_read*/ nullptr,
1101 /*foreach_cache*/ nullptr,
1102};
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)
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
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(*)(void *user_data, Object *ob, ID **idpoin, int cb_flag) IDWalkFunc
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:50
#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 unit_m4(float m[4][4])
Definition rct.c:1127
void mul_m4_v3(const float M[4][4], float r[3])
#define mul_m4_series(...)
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])
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)
struct ArrayModifierData ArrayModifierData
@ eModifierType_Array
@ MOD_ARR_MERGE
@ MOD_ARR_MERGEFINAL
@ MOD_ARR_OFF_RELATIVE
@ MOD_ARR_OFF_CONST
@ MOD_ARR_OFF_OBJ
@ MOD_ARR_FITCURVE
@ MOD_ARR_FIXEDCOUNT
@ MOD_ARR_FITLENGTH
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)
Definition MOD_array.cc:52
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:115
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:130
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:65
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:269
BLI_INLINE float sum_v3(const float v[3])
Definition MOD_array.cc:103
static void uv_panel_draw(const bContext *, Panel *panel)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
Definition MOD_array.cc:75
static Mesh * arrayModifier_doArray(ArrayModifierData *amd, const ModifierEvalContext *ctx, Mesh *mesh)
Definition MOD_array.cc:375
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:150
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)
void modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_EXPAND
ATTR_WARN_UNUSED_RESULT const BMVert * v
AttributeSet attributes
constexpr bool is_empty() const
Definition BLI_span.hh:510
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:630
constexpr int64_t size() const
Definition BLI_span.hh:253
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:62
GAttributeReader lookup(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
draw_view in_light_buf[] float
uint col
int count
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void mesh_vert_normals_assign(Mesh &mesh, Span< float3 > vert_normals)
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:46
Definition DNA_ID.h:413
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:111
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4126