Blender  V3.3
MOD_array.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2005 Blender Foundation. All rights reserved. */
3 
10 #include "MEM_guardedalloc.h"
11 
12 #include "BLI_utildefines.h"
13 
14 #include "BLI_math.h"
15 
16 #include "BLT_translation.h"
17 
18 #include "DNA_curve_types.h"
19 #include "DNA_defaults.h"
20 #include "DNA_mesh_types.h"
21 #include "DNA_meshdata_types.h"
22 #include "DNA_object_types.h"
23 #include "DNA_scene_types.h"
24 #include "DNA_screen_types.h"
25 
26 #include "BKE_anim_path.h"
27 #include "BKE_context.h"
28 #include "BKE_curve.h"
29 #include "BKE_displist.h"
30 #include "BKE_lib_id.h"
31 #include "BKE_lib_query.h"
32 #include "BKE_mesh.h"
33 #include "BKE_modifier.h"
34 #include "BKE_object_deform.h"
35 #include "BKE_screen.h"
36 
37 #include "UI_interface.h"
38 #include "UI_resources.h"
39 
40 #include "RNA_access.h"
41 #include "RNA_prototypes.h"
42 
43 #include "MOD_ui_common.h"
44 #include "MOD_util.h"
45 
46 #include "DEG_depsgraph.h"
47 #include "DEG_depsgraph_query.h"
48 
49 static void initData(ModifierData *md)
50 {
52 
54 
56 
57  /* Open the first sub-panel by default,
58  * it corresponds to Relative offset which is enabled too. */
60 }
61 
62 static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
63 {
65 
66  walk(userData, ob, (ID **)&amd->start_cap, IDWALK_CB_NOP);
67  walk(userData, ob, (ID **)&amd->end_cap, IDWALK_CB_NOP);
68  walk(userData, ob, (ID **)&amd->curve_ob, IDWALK_CB_NOP);
69  walk(userData, ob, (ID **)&amd->offset_ob, IDWALK_CB_NOP);
70 }
71 
73 {
75  bool need_transform_dependency = false;
76  if (amd->start_cap != NULL) {
78  ctx->node, amd->start_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier Start Cap");
79  }
80  if (amd->end_cap != NULL) {
82  ctx->node, amd->end_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier End Cap");
83  }
84  if (amd->curve_ob) {
86  ctx->node, amd->curve_ob, DEG_OB_COMP_GEOMETRY, "Array Modifier Curve");
88  }
89  if (amd->offset_ob != NULL) {
91  ctx->node, amd->offset_ob, DEG_OB_COMP_TRANSFORM, "Array Modifier Offset");
92  need_transform_dependency = true;
93  }
94 
95  if (need_transform_dependency) {
96  DEG_add_modifier_to_transform_relation(ctx->node, "Array Modifier");
97  }
98 }
99 
100 BLI_INLINE float sum_v3(const float v[3])
101 {
102  return v[0] + v[1] + v[2];
103 }
104 
105 /* Structure used for sorting vertices, when processing doubles */
106 typedef struct SortVertsElem {
107  int vertex_num; /* The original index of the vertex, prior to sorting */
108  float co[3]; /* Its coordinates */
109  float sum_co; /* `sum_v3(co)`: just so we don't do the sum many times. */
111 
112 static int svert_sum_cmp(const void *e1, const void *e2)
113 {
114  const SortVertsElem *sv1 = e1;
115  const SortVertsElem *sv2 = e2;
116 
117  if (sv1->sum_co > sv2->sum_co) {
118  return 1;
119  }
120  if (sv1->sum_co < sv2->sum_co) {
121  return -1;
122  }
123 
124  return 0;
125 }
126 
128  const MVert *mv,
129  const int i_begin,
130  const int i_end)
131 {
132  int i;
133  for (i = i_begin; i < i_end; i++, sv++, mv++) {
134  sv->vertex_num = i;
135  copy_v3_v3(sv->co, mv->co);
136  sv->sum_co = sum_v3(mv->co);
137  }
138 }
139 
147 static void dm_mvert_map_doubles(int *doubles_map,
148  const MVert *mverts,
149  const int target_start,
150  const int target_verts_num,
151  const int source_start,
152  const int source_verts_num,
153  const float dist)
154 {
155  const float dist3 = ((float)M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */
156  int i_source, i_target, i_target_low_bound, target_end, source_end;
157  SortVertsElem *sorted_verts_target, *sorted_verts_source;
158  SortVertsElem *sve_source, *sve_target, *sve_target_low_bound;
159  bool target_scan_completed;
160 
161  target_end = target_start + target_verts_num;
162  source_end = source_start + source_verts_num;
163 
164  /* build array of MVerts to be tested for merging */
165  sorted_verts_target = MEM_malloc_arrayN(target_verts_num, sizeof(SortVertsElem), __func__);
166  sorted_verts_source = MEM_malloc_arrayN(source_verts_num, sizeof(SortVertsElem), __func__);
167 
168  /* Copy target vertices index and cos into SortVertsElem array */
169  svert_from_mvert(sorted_verts_target, mverts + target_start, target_start, target_end);
170 
171  /* Copy source vertices index and cos into SortVertsElem array */
172  svert_from_mvert(sorted_verts_source, mverts + source_start, source_start, source_end);
173 
174  /* sort arrays according to sum of vertex coordinates (sumco) */
175  qsort(sorted_verts_target, target_verts_num, sizeof(SortVertsElem), svert_sum_cmp);
176  qsort(sorted_verts_source, source_verts_num, sizeof(SortVertsElem), svert_sum_cmp);
177 
178  sve_target_low_bound = sorted_verts_target;
179  i_target_low_bound = 0;
180  target_scan_completed = false;
181 
182  /* Scan source vertices, in #SortVertsElem sorted array,
183  * all the while maintaining the lower bound of possible doubles in target vertices. */
184  for (i_source = 0, sve_source = sorted_verts_source; i_source < source_verts_num;
185  i_source++, sve_source++) {
186  int best_target_vertex = -1;
187  float best_dist_sq = dist * dist;
188  float sve_source_sumco;
189 
190  /* If source has already been assigned to a target (in an earlier call, with other chunks) */
191  if (doubles_map[sve_source->vertex_num] != -1) {
192  continue;
193  }
194 
195  /* If target fully scanned already, then all remaining source vertices cannot have a double */
196  if (target_scan_completed) {
197  doubles_map[sve_source->vertex_num] = -1;
198  continue;
199  }
200 
201  sve_source_sumco = sum_v3(sve_source->co);
202 
203  /* Skip all target vertices that are more than dist3 lower in terms of sumco */
204  /* and advance the overall lower bound, applicable to all remaining vertices as well. */
205  while ((i_target_low_bound < target_verts_num) &&
206  (sve_target_low_bound->sum_co < sve_source_sumco - dist3)) {
207  i_target_low_bound++;
208  sve_target_low_bound++;
209  }
210  /* If end of target list reached, then no more possible doubles */
211  if (i_target_low_bound >= target_verts_num) {
212  doubles_map[sve_source->vertex_num] = -1;
213  target_scan_completed = true;
214  continue;
215  }
216  /* Test target candidates starting at the low bound of possible doubles,
217  * ordered in terms of sumco. */
218  i_target = i_target_low_bound;
219  sve_target = sve_target_low_bound;
220 
221  /* i_target will scan vertices in the
222  * [v_source_sumco - dist3; v_source_sumco + dist3] range */
223 
224  while ((i_target < target_verts_num) && (sve_target->sum_co <= sve_source_sumco + dist3)) {
225  /* Testing distance for candidate double in target */
226  /* v_target is within dist3 of v_source in terms of sumco; check real distance */
227  float dist_sq;
228  if ((dist_sq = len_squared_v3v3(sve_source->co, sve_target->co)) <= best_dist_sq) {
229  /* Potential double found */
230  best_dist_sq = dist_sq;
231  best_target_vertex = sve_target->vertex_num;
232 
233  /* If target is already mapped, we only follow that mapping if final target remains
234  * close enough from current vert (otherwise no mapping at all).
235  * Note that if we later find another target closer than this one, then we check it.
236  * But if other potential targets are farther,
237  * then there will be no mapping at all for this source. */
238  while (best_target_vertex != -1 &&
239  !ELEM(doubles_map[best_target_vertex], -1, best_target_vertex)) {
240  if (compare_len_v3v3(mverts[sve_source->vertex_num].co,
241  mverts[doubles_map[best_target_vertex]].co,
242  dist)) {
243  best_target_vertex = doubles_map[best_target_vertex];
244  }
245  else {
246  best_target_vertex = -1;
247  }
248  }
249  }
250  i_target++;
251  sve_target++;
252  }
253  /* End of candidate scan: if none found then no doubles */
254  doubles_map[sve_source->vertex_num] = best_target_vertex;
255  }
256 
257  MEM_freeN(sorted_verts_source);
258  MEM_freeN(sorted_verts_target);
259 }
260 
262  Mesh *cap_mesh,
263  const float cap_offset[4][4],
264  uint cap_verts_index,
265  uint cap_edges_index,
266  int cap_loops_index,
267  int cap_polys_index,
268  int cap_nverts,
269  int cap_nedges,
270  int cap_nloops,
271  int cap_npolys,
272  int *remap,
273  int remap_len,
274  const bool recalc_normals_later)
275 {
276  int *index_orig;
277  int i;
278  MVert *mv;
279  MEdge *me;
280  MLoop *ml;
281  MPoly *mp;
282 
283  CustomData_copy_data(&cap_mesh->vdata, &result->vdata, 0, cap_verts_index, cap_nverts);
284  CustomData_copy_data(&cap_mesh->edata, &result->edata, 0, cap_edges_index, cap_nedges);
285  CustomData_copy_data(&cap_mesh->ldata, &result->ldata, 0, cap_loops_index, cap_nloops);
286  CustomData_copy_data(&cap_mesh->pdata, &result->pdata, 0, cap_polys_index, cap_npolys);
287 
288  mv = result->mvert + cap_verts_index;
289 
290  for (i = 0; i < cap_nverts; i++, mv++) {
291  mul_m4_v3(cap_offset, mv->co);
292  /* Reset MVert flags for caps */
293  mv->flag = mv->bweight = 0;
294  }
295 
296  /* We have to correct normals too, if we do not tag them as dirty later! */
297  if (!recalc_normals_later) {
298  float(*dst_vert_normals)[3] = BKE_mesh_vertex_normals_for_write(result);
299  for (i = 0; i < cap_nverts; i++) {
300  mul_mat3_m4_v3(cap_offset, dst_vert_normals[cap_verts_index + i]);
301  normalize_v3(dst_vert_normals[cap_verts_index + i]);
302  }
303  }
304 
305  /* remap the vertex groups if necessary */
306  if (result->dvert != NULL) {
308  &result->dvert[cap_verts_index], cap_nverts, remap, remap_len);
309  }
310 
311  /* adjust cap edge vertex indices */
312  me = result->medge + cap_edges_index;
313  for (i = 0; i < cap_nedges; i++, me++) {
314  me->v1 += cap_verts_index;
315  me->v2 += cap_verts_index;
316  }
317 
318  /* adjust cap poly loopstart indices */
319  mp = result->mpoly + cap_polys_index;
320  for (i = 0; i < cap_npolys; i++, mp++) {
321  mp->loopstart += cap_loops_index;
322  }
323 
324  /* adjust cap loop vertex and edge indices */
325  ml = result->mloop + cap_loops_index;
326  for (i = 0; i < cap_nloops; i++, ml++) {
327  ml->v += cap_verts_index;
328  ml->e += cap_edges_index;
329  }
330 
331  /* Set #CD_ORIGINDEX. */
332  index_orig = CustomData_get_layer(&result->vdata, CD_ORIGINDEX);
333  if (index_orig) {
334  copy_vn_i(index_orig + cap_verts_index, cap_nverts, ORIGINDEX_NONE);
335  }
336 
337  index_orig = CustomData_get_layer(&result->edata, CD_ORIGINDEX);
338  if (index_orig) {
339  copy_vn_i(index_orig + cap_edges_index, cap_nedges, ORIGINDEX_NONE);
340  }
341 
342  index_orig = CustomData_get_layer(&result->pdata, CD_ORIGINDEX);
343  if (index_orig) {
344  copy_vn_i(index_orig + cap_polys_index, cap_npolys, ORIGINDEX_NONE);
345  }
346 
347  index_orig = CustomData_get_layer(&result->ldata, CD_ORIGINDEX);
348  if (index_orig) {
349  copy_vn_i(index_orig + cap_loops_index, cap_nloops, ORIGINDEX_NONE);
350  }
351 }
352 
354  const ModifierEvalContext *ctx,
355  Mesh *mesh)
356 {
357  const MVert *src_mvert;
358  MVert *result_dm_verts;
359 
360  MEdge *me;
361  MLoop *ml;
362  MPoly *mp;
363  int i, j, c, count;
364  float length = amd->length;
365  /* offset matrix */
366  float offset[4][4];
367  float scale[3];
368  bool offset_has_scale;
369  float current_offset[4][4];
370  float final_offset[4][4];
371  int *full_doubles_map = NULL;
372  int tot_doubles;
373 
374  const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0;
375  const bool use_recalc_normals = BKE_mesh_vertex_normals_are_dirty(mesh) || use_merge;
376  const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob != NULL);
377 
378  int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0;
379  int end_cap_nverts = 0, end_cap_nedges = 0, end_cap_npolys = 0, end_cap_nloops = 0;
380  int result_nverts = 0, result_nedges = 0, result_npolys = 0, result_nloops = 0;
381  int chunk_nverts, chunk_nedges, chunk_nloops, chunk_npolys;
382  int first_chunk_start, first_chunk_nverts, last_chunk_start, last_chunk_nverts;
383 
384  Mesh *result, *start_cap_mesh = NULL, *end_cap_mesh = NULL;
385 
386  int *vgroup_start_cap_remap = NULL;
387  int vgroup_start_cap_remap_len = 0;
388  int *vgroup_end_cap_remap = NULL;
389  int vgroup_end_cap_remap_len = 0;
390 
391  chunk_nverts = mesh->totvert;
392  chunk_nedges = mesh->totedge;
393  chunk_nloops = mesh->totloop;
394  chunk_npolys = mesh->totpoly;
395 
396  count = amd->count;
397 
398  Object *start_cap_ob = amd->start_cap;
399  if (start_cap_ob && start_cap_ob != ctx->object) {
400  if (start_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) {
401  vgroup_start_cap_remap = BKE_object_defgroup_index_map_create(
402  start_cap_ob, ctx->object, &vgroup_start_cap_remap_len);
403  }
404 
405  start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob);
406  if (start_cap_mesh) {
407  start_cap_nverts = start_cap_mesh->totvert;
408  start_cap_nedges = start_cap_mesh->totedge;
409  start_cap_nloops = start_cap_mesh->totloop;
410  start_cap_npolys = start_cap_mesh->totpoly;
411  }
412  }
413  Object *end_cap_ob = amd->end_cap;
414  if (end_cap_ob && end_cap_ob != ctx->object) {
415  if (end_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) {
416  vgroup_end_cap_remap = BKE_object_defgroup_index_map_create(
417  end_cap_ob, ctx->object, &vgroup_end_cap_remap_len);
418  }
419 
421  if (end_cap_mesh) {
422  end_cap_nverts = end_cap_mesh->totvert;
423  end_cap_nedges = end_cap_mesh->totedge;
424  end_cap_nloops = end_cap_mesh->totloop;
425  end_cap_npolys = end_cap_mesh->totpoly;
426  }
427  }
428 
429  /* Build up offset array, accumulating all settings options. */
430 
431  unit_m4(offset);
432  src_mvert = mesh->mvert;
433 
434  if (amd->offset_type & MOD_ARR_OFF_CONST) {
435  add_v3_v3(offset[3], amd->offset);
436  }
437 
438  if (amd->offset_type & MOD_ARR_OFF_RELATIVE) {
439  float min[3], max[3];
440  const MVert *src_mv;
441 
442  INIT_MINMAX(min, max);
443  for (src_mv = src_mvert, j = chunk_nverts; j--; src_mv++) {
444  minmax_v3v3_v3(min, max, src_mv->co);
445  }
446 
447  for (j = 3; j--;) {
448  offset[3][j] += amd->scale[j] * (max[j] - min[j]);
449  }
450  }
451 
452  if (use_offset_ob) {
453  float obinv[4][4];
454  float result_mat[4][4];
455 
456  if (ctx->object) {
457  invert_m4_m4(obinv, ctx->object->obmat);
458  }
459  else {
460  unit_m4(obinv);
461  }
462 
463  mul_m4_series(result_mat, offset, obinv, amd->offset_ob->obmat);
464  copy_m4_m4(offset, result_mat);
465  }
466 
467  /* Check if there is some scaling. If scaling, then we will not translate mapping */
468  mat4_to_size(scale, offset);
469  offset_has_scale = !is_one_v3(scale);
470 
471  if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob != NULL) {
472  Object *curve_ob = amd->curve_ob;
473  CurveCache *curve_cache = curve_ob->runtime.curve_cache;
474  if (curve_cache != NULL && curve_cache->anim_path_accum_length != NULL) {
475  float scale_fac = mat4_to_scale(curve_ob->obmat);
476  length = scale_fac * BKE_anim_path_get_length(curve_cache);
477  }
478  }
479 
480  /* About 67 million vertices max seems a decent limit for now. */
481  const size_t max_vertices_num = 1 << 26;
482 
483  /* calculate the maximum number of copies which will fit within the
484  * prescribed length */
486  const float float_epsilon = 1e-6f;
487  bool offset_is_too_small = false;
488  float dist = len_v3(offset[3]);
489 
490  if (dist > float_epsilon) {
491  /* this gives length = first copy start to last copy end
492  * add a tiny offset for floating point rounding errors */
493  count = (length + float_epsilon) / dist + 1;
494 
495  /* Ensure we keep things to a reasonable level, in terms of rough total amount of generated
496  * vertices.
497  */
498  if (((size_t)count * (size_t)chunk_nverts + (size_t)start_cap_nverts +
499  (size_t)end_cap_nverts) > max_vertices_num) {
500  count = 1;
501  offset_is_too_small = true;
502  }
503  }
504  else {
505  /* if the offset has no translation, just make one copy */
506  count = 1;
507  offset_is_too_small = true;
508  }
509 
510  if (offset_is_too_small) {
512  ctx->object,
513  &amd->modifier,
514  "The offset is too small, we cannot generate the amount of geometry it would require");
515  }
516  }
517  /* Ensure we keep things to a reasonable level, in terms of rough total amount of generated
518  * vertices.
519  */
520  else if (((size_t)count * (size_t)chunk_nverts + (size_t)start_cap_nverts +
521  (size_t)end_cap_nverts) > max_vertices_num) {
522  count = 1;
524  &amd->modifier,
525  "The amount of copies is too high, we cannot generate the amount of "
526  "geometry it would require");
527  }
528 
529  if (count < 1) {
530  count = 1;
531  }
532 
533  /* The number of verts, edges, loops, polys, before eventually merging doubles */
534  result_nverts = chunk_nverts * count + start_cap_nverts + end_cap_nverts;
535  result_nedges = chunk_nedges * count + start_cap_nedges + end_cap_nedges;
536  result_nloops = chunk_nloops * count + start_cap_nloops + end_cap_nloops;
537  result_npolys = chunk_npolys * count + start_cap_npolys + end_cap_npolys;
538 
539  /* Initialize a result dm */
541  mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys);
542  result_dm_verts = result->mvert;
543 
544  if (use_merge) {
545  /* Will need full_doubles_map for handling merge */
546  full_doubles_map = MEM_malloc_arrayN(result_nverts, sizeof(int), "mod array doubles map");
547  copy_vn_i(full_doubles_map, result_nverts, -1);
548  }
549 
550  /* copy customdata to original geometry */
551  CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, chunk_nverts);
552  CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, chunk_nedges);
553  CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, chunk_nloops);
554  CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, chunk_npolys);
555 
556  /* Subsurf for eg won't have mesh data in the custom data arrays.
557  * now add mvert/medge/mpoly layers. */
559  memcpy(result->mvert, mesh->mvert, sizeof(*result->mvert) * mesh->totvert);
560  }
562  memcpy(result->medge, mesh->medge, sizeof(*result->medge) * mesh->totedge);
563  }
565  memcpy(result->mloop, mesh->mloop, sizeof(*result->mloop) * mesh->totloop);
566  memcpy(result->mpoly, mesh->mpoly, sizeof(*result->mpoly) * mesh->totpoly);
567  }
568 
569  /* Remember first chunk, in case of cap merge */
570  first_chunk_start = 0;
571  first_chunk_nverts = chunk_nverts;
572 
573  unit_m4(current_offset);
574  const float(*src_vert_normals)[3] = NULL;
575  float(*dst_vert_normals)[3] = NULL;
576  if (!use_recalc_normals) {
577  src_vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
578  dst_vert_normals = BKE_mesh_vertex_normals_for_write(result);
580  }
581 
582  for (c = 1; c < count; c++) {
583  /* copy customdata to new geometry */
584  CustomData_copy_data(&mesh->vdata, &result->vdata, 0, c * chunk_nverts, chunk_nverts);
585  CustomData_copy_data(&mesh->edata, &result->edata, 0, c * chunk_nedges, chunk_nedges);
586  CustomData_copy_data(&mesh->ldata, &result->ldata, 0, c * chunk_nloops, chunk_nloops);
587  CustomData_copy_data(&mesh->pdata, &result->pdata, 0, c * chunk_npolys, chunk_npolys);
588 
589  const int vert_offset = c * chunk_nverts;
590 
591  /* recalculate cumulative offset here */
592  mul_m4_m4m4(current_offset, current_offset, offset);
593 
594  /* apply offset to all new verts */
595  for (i = 0; i < chunk_nverts; i++) {
596  const int i_dst = vert_offset + i;
597  mul_m4_v3(current_offset, result_dm_verts[i_dst].co);
598 
599  /* We have to correct normals too, if we do not tag them as dirty! */
600  if (!use_recalc_normals) {
601  copy_v3_v3(dst_vert_normals[i_dst], src_vert_normals[i]);
602  mul_mat3_m4_v3(current_offset, dst_vert_normals[i_dst]);
603  normalize_v3(dst_vert_normals[i_dst]);
604  }
605  }
606 
607  /* adjust edge vertex indices */
608  me = result->medge + c * chunk_nedges;
609  for (i = 0; i < chunk_nedges; i++, me++) {
610  me->v1 += c * chunk_nverts;
611  me->v2 += c * chunk_nverts;
612  }
613 
614  mp = result->mpoly + c * chunk_npolys;
615  for (i = 0; i < chunk_npolys; i++, mp++) {
616  mp->loopstart += c * chunk_nloops;
617  }
618 
619  /* adjust loop vertex and edge indices */
620  ml = result->mloop + c * chunk_nloops;
621  for (i = 0; i < chunk_nloops; i++, ml++) {
622  ml->v += c * chunk_nverts;
623  ml->e += c * chunk_nedges;
624  }
625 
626  /* Handle merge between chunk n and n-1 */
627  if (use_merge && (c >= 1)) {
628  if (!offset_has_scale && (c >= 2)) {
629  /* Mapping chunk 3 to chunk 2 is a translation of mapping 2 to 1
630  * ... that is except if scaling makes the distance grow */
631  int k;
632  int this_chunk_index = c * chunk_nverts;
633  int prev_chunk_index = (c - 1) * chunk_nverts;
634  for (k = 0; k < chunk_nverts; k++, this_chunk_index++, prev_chunk_index++) {
635  int target = full_doubles_map[prev_chunk_index];
636  if (target != -1) {
637  target += chunk_nverts; /* translate mapping */
638  while (target != -1 && !ELEM(full_doubles_map[target], -1, target)) {
639  /* If target is already mapped, we only follow that mapping if final target remains
640  * close enough from current vert (otherwise no mapping at all). */
641  if (compare_len_v3v3(result_dm_verts[this_chunk_index].co,
642  result_dm_verts[full_doubles_map[target]].co,
643  amd->merge_dist)) {
644  target = full_doubles_map[target];
645  }
646  else {
647  target = -1;
648  }
649  }
650  }
651  full_doubles_map[this_chunk_index] = target;
652  }
653  }
654  else {
655  dm_mvert_map_doubles(full_doubles_map,
656  result_dm_verts,
657  (c - 1) * chunk_nverts,
658  chunk_nverts,
659  c * chunk_nverts,
660  chunk_nverts,
661  amd->merge_dist);
662  }
663  }
664  }
665 
666  /* handle UVs */
667  if (chunk_nloops > 0 && is_zero_v2(amd->uv_offset) == false) {
668  const int totuv = CustomData_number_of_layers(&result->ldata, CD_MLOOPUV);
669  for (i = 0; i < totuv; i++) {
670  MLoopUV *dmloopuv = CustomData_get_layer_n(&result->ldata, CD_MLOOPUV, i);
671  dmloopuv += chunk_nloops;
672  for (c = 1; c < count; c++) {
673  const float uv_offset[2] = {
674  amd->uv_offset[0] * (float)c,
675  amd->uv_offset[1] * (float)c,
676  };
677  int l_index = chunk_nloops;
678  for (; l_index-- != 0; dmloopuv++) {
679  dmloopuv->uv[0] += uv_offset[0];
680  dmloopuv->uv[1] += uv_offset[1];
681  }
682  }
683  }
684  }
685 
686  last_chunk_start = (count - 1) * chunk_nverts;
687  last_chunk_nverts = chunk_nverts;
688 
689  copy_m4_m4(final_offset, current_offset);
690 
691  if (use_merge && (amd->flags & MOD_ARR_MERGEFINAL) && (count > 1)) {
692  /* Merge first and last copies */
693  dm_mvert_map_doubles(full_doubles_map,
694  result_dm_verts,
695  last_chunk_start,
696  last_chunk_nverts,
697  first_chunk_start,
698  first_chunk_nverts,
699  amd->merge_dist);
700  }
701 
702  /* start capping */
703  if (start_cap_mesh) {
704  float start_offset[4][4];
705  int start_cap_start = result_nverts - start_cap_nverts - end_cap_nverts;
706  invert_m4_m4(start_offset, offset);
708  start_cap_mesh,
709  start_offset,
710  result_nverts - start_cap_nverts - end_cap_nverts,
711  result_nedges - start_cap_nedges - end_cap_nedges,
712  result_nloops - start_cap_nloops - end_cap_nloops,
713  result_npolys - start_cap_npolys - end_cap_npolys,
714  start_cap_nverts,
715  start_cap_nedges,
716  start_cap_nloops,
717  start_cap_npolys,
718  vgroup_start_cap_remap,
719  vgroup_start_cap_remap_len,
720  use_recalc_normals);
721  /* Identify doubles with first chunk */
722  if (use_merge) {
723  dm_mvert_map_doubles(full_doubles_map,
724  result_dm_verts,
725  first_chunk_start,
726  first_chunk_nverts,
727  start_cap_start,
728  start_cap_nverts,
729  amd->merge_dist);
730  }
731  }
732 
733  if (end_cap_mesh) {
734  float end_offset[4][4];
735  int end_cap_start = result_nverts - end_cap_nverts;
736  mul_m4_m4m4(end_offset, current_offset, offset);
738  end_cap_mesh,
739  end_offset,
740  result_nverts - end_cap_nverts,
741  result_nedges - end_cap_nedges,
742  result_nloops - end_cap_nloops,
743  result_npolys - end_cap_npolys,
744  end_cap_nverts,
745  end_cap_nedges,
746  end_cap_nloops,
747  end_cap_npolys,
748  vgroup_end_cap_remap,
749  vgroup_end_cap_remap_len,
750  use_recalc_normals);
751  /* Identify doubles with last chunk */
752  if (use_merge) {
753  dm_mvert_map_doubles(full_doubles_map,
754  result_dm_verts,
755  last_chunk_start,
756  last_chunk_nverts,
757  end_cap_start,
758  end_cap_nverts,
759  amd->merge_dist);
760  }
761  }
762  /* done capping */
763 
764  /* Handle merging */
765  tot_doubles = 0;
766  if (use_merge) {
767  for (i = 0; i < result_nverts; i++) {
768  int new_i = full_doubles_map[i];
769  if (new_i != -1) {
770  /* We have to follow chains of doubles
771  * (merge start/end especially is likely to create some),
772  * those are not supported at all by BKE_mesh_merge_verts! */
773  while (!ELEM(full_doubles_map[new_i], -1, new_i)) {
774  new_i = full_doubles_map[new_i];
775  }
776  if (i == new_i) {
777  full_doubles_map[i] = -1;
778  }
779  else {
780  full_doubles_map[i] = new_i;
781  tot_doubles++;
782  }
783  }
784  }
785  if (tot_doubles > 0) {
787  result, full_doubles_map, tot_doubles, MESH_MERGE_VERTS_DUMP_IF_EQUAL);
788  }
789  MEM_freeN(full_doubles_map);
790  }
791 
792  if (vgroup_start_cap_remap) {
793  MEM_freeN(vgroup_start_cap_remap);
794  }
795  if (vgroup_end_cap_remap) {
796  MEM_freeN(vgroup_end_cap_remap);
797  }
798 
799  return result;
800 }
801 
803 {
805  return arrayModifier_doArray(amd, ctx, mesh);
806 }
807 
808 static bool isDisabled(const struct Scene *UNUSED(scene),
809  ModifierData *md,
810  bool UNUSED(useRenderParams))
811 {
813 
814  /* The object type check is only needed here in case we have a placeholder
815  * object assigned (because the library containing the curve/mesh is missing).
816  *
817  * In other cases it should be impossible to have a type mismatch.
818  */
819 
820  if (amd->curve_ob && amd->curve_ob->type != OB_CURVES_LEGACY) {
821  return true;
822  }
823  if (amd->start_cap && amd->start_cap->type != OB_MESH) {
824  return true;
825  }
826  if (amd->end_cap && amd->end_cap->type != OB_MESH) {
827  return true;
828  }
829 
830  return false;
831 }
832 
833 static void panel_draw(const bContext *UNUSED(C), Panel *panel)
834 {
835  uiLayout *layout = panel->layout;
836 
837  PointerRNA ob_ptr;
839 
840  uiLayoutSetPropSep(layout, true);
841 
842  uiItemR(layout, ptr, "fit_type", 0, NULL, ICON_NONE);
843 
844  int fit_type = RNA_enum_get(ptr, "fit_type");
845  if (fit_type == MOD_ARR_FIXEDCOUNT) {
846  uiItemR(layout, ptr, "count", 0, NULL, ICON_NONE);
847  }
848  else if (fit_type == MOD_ARR_FITLENGTH) {
849  uiItemR(layout, ptr, "fit_length", 0, NULL, ICON_NONE);
850  }
851  else if (fit_type == MOD_ARR_FITCURVE) {
852  uiItemR(layout, ptr, "curve", 0, NULL, ICON_NONE);
853  }
854 
855  modifier_panel_end(layout, ptr);
856 }
857 
858 static void relative_offset_header_draw(const bContext *UNUSED(C), Panel *panel)
859 {
860  uiLayout *layout = panel->layout;
861 
863 
864  uiItemR(layout, ptr, "use_relative_offset", 0, NULL, ICON_NONE);
865 }
866 
867 static void relative_offset_draw(const bContext *UNUSED(C), Panel *panel)
868 {
869  uiLayout *layout = panel->layout;
870 
872 
873  uiLayoutSetPropSep(layout, true);
874 
875  uiLayout *col = uiLayoutColumn(layout, false);
876 
877  uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_relative_offset"));
878  uiItemR(col, ptr, "relative_offset_displace", 0, IFACE_("Factor"), ICON_NONE);
879 }
880 
881 static void constant_offset_header_draw(const bContext *UNUSED(C), Panel *panel)
882 {
883  uiLayout *layout = panel->layout;
884 
886 
887  uiItemR(layout, ptr, "use_constant_offset", 0, NULL, ICON_NONE);
888 }
889 
890 static void constant_offset_draw(const bContext *UNUSED(C), Panel *panel)
891 {
892  uiLayout *layout = panel->layout;
893 
895 
896  uiLayoutSetPropSep(layout, true);
897 
898  uiLayout *col = uiLayoutColumn(layout, false);
899 
900  uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_constant_offset"));
901  uiItemR(col, ptr, "constant_offset_displace", 0, IFACE_("Distance"), ICON_NONE);
902 }
903 
907 static void object_offset_header_draw(const bContext *UNUSED(C), Panel *panel)
908 {
909  uiLayout *layout = panel->layout;
910 
912 
913  uiItemR(layout, ptr, "use_object_offset", 0, NULL, ICON_NONE);
914 }
915 
916 static void object_offset_draw(const bContext *UNUSED(C), Panel *panel)
917 {
918  uiLayout *layout = panel->layout;
919 
921 
922  uiLayoutSetPropSep(layout, true);
923 
924  uiLayout *col = uiLayoutColumn(layout, false);
925 
926  uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_object_offset"));
927  uiItemR(col, ptr, "offset_object", 0, IFACE_("Object"), ICON_NONE);
928 }
929 
930 static void symmetry_panel_header_draw(const bContext *UNUSED(C), Panel *panel)
931 {
932  uiLayout *layout = panel->layout;
933 
935 
936  uiItemR(layout, ptr, "use_merge_vertices", 0, IFACE_("Merge"), ICON_NONE);
937 }
938 
939 static void symmetry_panel_draw(const bContext *UNUSED(C), Panel *panel)
940 {
941  uiLayout *layout = panel->layout;
942 
944 
945  uiLayoutSetPropSep(layout, true);
946 
947  uiLayout *col = uiLayoutColumn(layout, false);
948  uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_merge_vertices"));
949  uiItemR(col, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE);
950  uiItemR(col, ptr, "use_merge_vertices_cap", 0, IFACE_("First and Last Copies"), ICON_NONE);
951 }
952 
953 static void uv_panel_draw(const bContext *UNUSED(C), Panel *panel)
954 {
955  uiLayout *col;
956  uiLayout *layout = panel->layout;
957 
959 
960  uiLayoutSetPropSep(layout, true);
961 
962  col = uiLayoutColumn(layout, true);
963  uiItemR(col, ptr, "offset_u", UI_ITEM_R_EXPAND, IFACE_("Offset U"), ICON_NONE);
964  uiItemR(col, ptr, "offset_v", UI_ITEM_R_EXPAND, IFACE_("V"), ICON_NONE);
965 }
966 
967 static void caps_panel_draw(const bContext *UNUSED(C), Panel *panel)
968 {
969  uiLayout *col;
970  uiLayout *layout = panel->layout;
971 
973 
974  uiLayoutSetPropSep(layout, true);
975 
976  col = uiLayoutColumn(layout, false);
977  uiItemR(col, ptr, "start_cap", 0, IFACE_("Cap Start"), ICON_NONE);
978  uiItemR(col, ptr, "end_cap", 0, IFACE_("End"), ICON_NONE);
979 }
980 
981 static void panelRegister(ARegionType *region_type)
982 {
984  modifier_subpanel_register(region_type,
985  "relative_offset",
986  "",
989  panel_type);
990  modifier_subpanel_register(region_type,
991  "constant_offset",
992  "",
995  panel_type);
997  region_type, "object_offset", "", object_offset_header_draw, object_offset_draw, panel_type);
999  region_type, "merge", "", symmetry_panel_header_draw, symmetry_panel_draw, panel_type);
1000  modifier_subpanel_register(region_type, "uv", "UVs", NULL, uv_panel_draw, panel_type);
1001  modifier_subpanel_register(region_type, "caps", "Caps", NULL, caps_panel_draw, panel_type);
1002 }
1003 
1005  /* name */ N_("Array"),
1006  /* structName */ "ArrayModifierData",
1007  /* structSize */ sizeof(ArrayModifierData),
1008  /* srna */ &RNA_ArrayModifier,
1009  /* type */ eModifierTypeType_Constructive,
1013  /* icon */ ICON_MOD_ARRAY,
1014 
1015  /* copyData */ BKE_modifier_copydata_generic,
1016 
1017  /* deformVerts */ NULL,
1018  /* deformMatrices */ NULL,
1019  /* deformVertsEM */ NULL,
1020  /* deformMatricesEM */ NULL,
1021  /* modifyMesh */ modifyMesh,
1022  /* modifyGeometrySet */ NULL,
1023 
1024  /* initData */ initData,
1025  /* requiredDataMask */ NULL,
1026  /* freeData */ NULL,
1027  /* isDisabled */ isDisabled,
1028  /* updateDepsgraph */ updateDepsgraph,
1029  /* dependsOnTime */ NULL,
1030  /* dependsOnNormals */ NULL,
1031  /* foreachIDLink */ foreachIDLink,
1032  /* foreachTexLink */ NULL,
1033  /* freeRuntimeData */ NULL,
1034  /* panelRegister */ panelRegister,
1035  /* blendWrite */ NULL,
1036  /* blendRead */ NULL,
1037 };
typedef float(TangentPoint)[2]
float BKE_anim_path_get_length(const struct CurveCache *curve_cache)
int CustomData_number_of_layers(const struct CustomData *data, int type)
bool CustomData_has_layer(const struct CustomData *data, int type)
#define ORIGINDEX_NONE
void * CustomData_get_layer_n(const struct CustomData *data, int type, int n)
void * CustomData_get_layer(const struct CustomData *data, int type)
void CustomData_copy_data(const struct CustomData *source, struct CustomData *dest, int source_index, int dest_index, int count)
display list (or rather multi purpose list) stuff.
@ IDWALK_CB_NOP
Definition: BKE_lib_query.h:33
float(* BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3]
@ MESH_MERGE_VERTS_DUMP_IF_EQUAL
Definition: BKE_mesh.h:819
struct Mesh * BKE_mesh_new_nomain_from_template(const struct Mesh *me_src, int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
const float(* BKE_mesh_vertex_normals_ensure(const struct Mesh *mesh))[3]
bool BKE_mesh_vertex_normals_are_dirty(const struct Mesh *mesh)
struct Mesh * BKE_mesh_merge_verts(struct Mesh *mesh, const int *vtargetmap, int tot_vtargetmap, int merge_mode)
Definition: mesh_merge.c:192
void BKE_mesh_vertex_normals_clear_dirty(struct Mesh *mesh)
void(* IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag)
Definition: BKE_modifier.h:107
@ eModifierTypeFlag_AcceptsCVs
Definition: BKE_modifier.h:67
@ eModifierTypeFlag_SupportsMapping
Definition: BKE_modifier.h:68
@ eModifierTypeFlag_EnableInEditmode
Definition: BKE_modifier.h:78
@ eModifierTypeFlag_SupportsEditmode
Definition: BKE_modifier.h:69
@ eModifierTypeFlag_AcceptsMesh
Definition: BKE_modifier.h:66
void BKE_modifier_copydata_generic(const struct ModifierData *md, struct ModifierData *md_dst, int flag)
struct Mesh * BKE_modifier_get_evaluated_mesh_from_evaluated_object(struct Object *ob_eval)
@ eModifierTypeType_Constructive
Definition: BKE_modifier.h:47
void BKE_modifier_set_error(const struct Object *ob, struct 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
Definition: BLI_math_base.h:35
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:259
void unit_m4(float m[4][4])
Definition: rct.c:1090
void mul_mat3_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:790
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
#define mul_m4_series(...)
void copy_m4_m4(float m1[4][4], const float m2[4][4])
Definition: math_matrix.c:77
float mat4_to_scale(const float M[4][4])
Definition: math_matrix.c:2185
void mat4_to_size(float size[3], const float M[4][4])
Definition: math_matrix.c:2138
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
Definition: math_vector.c:867
MINLINE float normalize_v3(float r[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) 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)
Definition: math_vector.c:1223
MINLINE bool is_one_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE bool compare_len_v3v3(const float a[3], const float b[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
Definition: BLI_sys_types.h:67
#define INIT_MINMAX(min, max)
#define UNUSED(x)
#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
Definition: DEG_depsgraph.h:54
void DEG_add_object_relation(struct DepsNodeHandle *node_handle, struct Object *object, eDepsObjectComponentType component, const char *description)
void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle, const char *description)
@ DEG_OB_COMP_GEOMETRY
@ DEG_OB_COMP_TRANSFORM
void DEG_add_special_eval_flag(struct DepsNodeHandle *handle, struct ID *id, uint32_t flag)
@ CD_ORIGINDEX
@ CD_MEDGE
@ CD_MVERT
@ CD_MLOOPUV
#define DNA_struct_default_get(struct_name)
Definition: DNA_defaults.h:29
@ MOD_ARR_OFF_RELATIVE
@ MOD_ARR_OFF_CONST
@ MOD_ARR_OFF_OBJ
@ MOD_ARR_FITCURVE
@ MOD_ARR_FIXEDCOUNT
@ MOD_ARR_FITLENGTH
struct ArrayModifierData ArrayModifierData
@ eModifierType_Array
@ MOD_ARR_MERGE
@ MOD_ARR_MERGEFINAL
Object is a sort of wrapper for general info.
@ OB_MESH
@ OB_CURVES_LEGACY
@ UI_PANEL_DATA_EXPAND_ROOT
@ UI_SUBPANEL_DATA_EXPAND_1
Read Guarded memory(de)allocation.
static void uv_panel_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:953
static void constant_offset_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:890
static Mesh * modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
Definition: MOD_array.c:802
static void svert_from_mvert(SortVertsElem *sv, const MVert *mv, const int i_begin, const int i_end)
Definition: MOD_array.c:127
static void symmetry_panel_header_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:930
static void relative_offset_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:867
static int svert_sum_cmp(const void *e1, const void *e2)
Definition: MOD_array.c:112
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
Definition: MOD_array.c:72
static void relative_offset_header_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:858
static bool isDisabled(const struct Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams))
Definition: MOD_array.c:808
ModifierTypeInfo modifierType_Array
Definition: MOD_array.c:1004
static void constant_offset_header_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:881
static void object_offset_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:916
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_polys_index, int cap_nverts, int cap_nedges, int cap_nloops, int cap_npolys, int *remap, int remap_len, const bool recalc_normals_later)
Definition: MOD_array.c:261
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
Definition: MOD_array.c:62
static void dm_mvert_map_doubles(int *doubles_map, const MVert *mverts, const int target_start, const int target_verts_num, const int source_start, const int source_verts_num, const float dist)
Definition: MOD_array.c:147
struct SortVertsElem SortVertsElem
static Mesh * arrayModifier_doArray(ArrayModifierData *amd, const ModifierEvalContext *ctx, Mesh *mesh)
Definition: MOD_array.c:353
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:833
BLI_INLINE float sum_v3(const float v[3])
Definition: MOD_array.c:100
static void initData(ModifierData *md)
Definition: MOD_array.c:49
static void panelRegister(ARegionType *region_type)
Definition: MOD_array.c:981
static void symmetry_panel_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:939
static void caps_panel_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:967
static void object_offset_header_draw(const bContext *UNUSED(C), Panel *panel)
Definition: MOD_array.c:907
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
Definition: MOD_ui_common.c:91
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PanelType * modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelType *parent)
#define C
Definition: RandGen.cpp:25
void uiLayoutSetActive(uiLayout *layout, bool active)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
@ UI_ITEM_R_EXPAND
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
ATTR_WARN_UNUSED_RESULT const BMVert * v
Scene scene
uint col
int count
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
static unsigned c
Definition: RandGen.cpp:83
T length(const vec_base< T, Size > &a)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
#define min(a, b)
Definition: sort.c:35
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.h:42
Definition: DNA_ID.h:368
unsigned int v1
unsigned int v2
unsigned int e
unsigned int v
float co[3]
struct MEdge * medge
CustomData vdata
struct MVert * mvert
int totedge
int totvert
struct MLoop * mloop
CustomData pdata
int totpoly
CustomData edata
int totloop
struct MPoly * mpoly
CustomData ldata
struct Object * object
Definition: BKE_modifier.h:141
struct DepsNodeHandle * node
Definition: BKE_modifier.h:134
struct CurveCache * curve_cache
Object_Runtime runtime
float obmat[4][4]
struct uiLayout * layout
float co[3]
Definition: MOD_array.c:108
float sum_co
Definition: MOD_array.c:109
float max
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480