Blender  V3.3
curves_ops.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <atomic>
8 
10 #include "BLI_utildefines.h"
11 #include "BLI_vector_set.hh"
12 
13 #include "ED_curves.h"
14 #include "ED_object.h"
15 #include "ED_screen.h"
16 #include "ED_select_utils.h"
17 
18 #include "WM_api.h"
19 
20 #include "BKE_attribute_math.hh"
21 #include "BKE_bvhutils.h"
22 #include "BKE_context.h"
23 #include "BKE_curves.hh"
24 #include "BKE_geometry_set.hh"
25 #include "BKE_layer.h"
26 #include "BKE_lib_id.h"
27 #include "BKE_mesh.h"
29 #include "BKE_mesh_runtime.h"
30 #include "BKE_object.h"
31 #include "BKE_paint.h"
32 #include "BKE_particle.h"
33 #include "BKE_report.h"
34 
35 #include "DNA_mesh_types.h"
36 #include "DNA_meshdata_types.h"
37 #include "DNA_modifier_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_particle_types.h"
40 #include "DNA_scene_types.h"
41 
42 #include "DEG_depsgraph.h"
43 #include "DEG_depsgraph_query.h"
44 
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47 #include "RNA_enum_types.h"
48 #include "RNA_prototypes.h"
49 
51 
60 namespace blender::ed::curves {
61 
62 static bool object_has_editable_curves(const Main &bmain, const Object &object)
63 {
64  if (object.type != OB_CURVES) {
65  return false;
66  }
67  if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) {
68  return false;
69  }
70  if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
71  return false;
72  }
73  return true;
74 }
75 
77 {
78  VectorSet<Curves *> unique_curves;
79 
80  const Main &bmain = *CTX_data_main(&C);
81 
82  Object *object = CTX_data_active_object(&C);
83  if (object && object_has_editable_curves(bmain, *object)) {
84  unique_curves.add_new(static_cast<Curves *>(object->data));
85  }
86 
87  CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
88  if (object_has_editable_curves(bmain, *object)) {
89  unique_curves.add(static_cast<Curves *>(object->data));
90  }
91  }
93 
94  return unique_curves;
95 }
96 
97 static bool curves_poll_impl(bContext *C, const bool check_editable, const bool check_surface)
98 {
99  Object *object = CTX_data_active_object(C);
100  if (object == nullptr || object->type != OB_CURVES) {
101  return false;
102  }
103  if (check_editable) {
105  return false;
106  }
107  }
108  if (check_surface) {
109  Curves &curves = *static_cast<Curves *>(object->data);
110  if (curves.surface == nullptr || curves.surface->type != OB_MESH) {
111  CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set");
112  return false;
113  }
114  }
115  return true;
116 }
117 
119 {
120  return curves_poll_impl(C, true, true);
121 }
122 
124 {
125  return curves_poll_impl(C, false, true);
126 }
127 
129 {
130  return curves_poll_impl(C, false, false);
131 }
132 
134 {
135  return curves_poll_impl(C, false, false);
136 }
137 
138 using bke::CurvesGeometry;
139 
140 namespace convert_to_particle_system {
141 
143  const Span<int> possible_mface_indices,
144  const float3 &root_pos)
145 {
146  BLI_assert(possible_mface_indices.size() >= 1);
147  if (possible_mface_indices.size() == 1) {
148  return possible_mface_indices.first();
149  }
150  /* Find the closest #MFace to #root_pos. */
151  int mface_i;
152  float best_distance_sq = FLT_MAX;
153  for (const int possible_mface_i : possible_mface_indices) {
154  const MFace &possible_mface = mesh.mface[possible_mface_i];
155  {
156  float3 point_in_triangle;
157  closest_on_tri_to_point_v3(point_in_triangle,
158  root_pos,
159  mesh.mvert[possible_mface.v1].co,
160  mesh.mvert[possible_mface.v2].co,
161  mesh.mvert[possible_mface.v3].co);
162  const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
163  if (distance_sq < best_distance_sq) {
164  best_distance_sq = distance_sq;
165  mface_i = possible_mface_i;
166  }
167  }
168  /* Optionally check the second triangle if the #MFace is a quad. */
169  if (possible_mface.v4) {
170  float3 point_in_triangle;
171  closest_on_tri_to_point_v3(point_in_triangle,
172  root_pos,
173  mesh.mvert[possible_mface.v1].co,
174  mesh.mvert[possible_mface.v3].co,
175  mesh.mvert[possible_mface.v4].co);
176  const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
177  if (distance_sq < best_distance_sq) {
178  best_distance_sq = distance_sq;
179  mface_i = possible_mface_i;
180  }
181  }
182  }
183  return mface_i;
184 }
185 
190  const MFace &mface,
191  const float3 &position)
192 {
193  float4 mface_weights;
194  if (mface.v4) {
195  float mface_verts_su[4][3];
196  copy_v3_v3(mface_verts_su[0], mesh.mvert[mface.v1].co);
197  copy_v3_v3(mface_verts_su[1], mesh.mvert[mface.v2].co);
198  copy_v3_v3(mface_verts_su[2], mesh.mvert[mface.v3].co);
199  copy_v3_v3(mface_verts_su[3], mesh.mvert[mface.v4].co);
200  interp_weights_poly_v3(mface_weights, mface_verts_su, 4, position);
201  }
202  else {
203  interp_weights_tri_v3(mface_weights,
204  mesh.mvert[mface.v1].co,
205  mesh.mvert[mface.v2].co,
206  mesh.mvert[mface.v3].co,
207  position);
208  mface_weights[3] = 0.0f;
209  }
210  return mface_weights;
211 }
212 
213 static void try_convert_single_object(Object &curves_ob,
214  Main &bmain,
215  Scene &scene,
216  bool *r_could_not_convert_some_curves)
217 {
218  if (curves_ob.type != OB_CURVES) {
219  return;
220  }
221  Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
223  if (curves_id.surface == nullptr) {
224  return;
225  }
226  Object &surface_ob = *curves_id.surface;
227  if (surface_ob.type != OB_MESH) {
228  return;
229  }
230  Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
231 
232  BVHTreeFromMesh surface_bvh;
233  BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_LOOPTRI, 2);
234  BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
235 
236  const Span<float3> positions_cu = curves.positions();
237  const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me),
238  BKE_mesh_runtime_looptri_len(&surface_me)};
239 
240  if (looptris.is_empty()) {
241  *r_could_not_convert_some_curves = true;
242  }
243 
244  const int hair_num = curves.curves_num();
245  if (hair_num == 0) {
246  return;
247  }
248 
249  ParticleSystem *particle_system = nullptr;
250  LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
251  if (STREQ(psys->name, curves_ob.id.name + 2)) {
252  particle_system = psys;
253  break;
254  }
255  }
256  if (particle_system == nullptr) {
257  ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
258  object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2));
259  particle_system = psmd.psys;
260  particle_system->part->draw_step = 3;
261  }
262 
263  ParticleSettings &settings = *particle_system->part;
264 
265  psys_free_particles(particle_system);
266  settings.type = PART_HAIR;
267  settings.totpart = 0;
268  psys_changed_type(&surface_ob, particle_system);
269 
270  MutableSpan<ParticleData> particles{
271  static_cast<ParticleData *>(MEM_calloc_arrayN(hair_num, sizeof(ParticleData), __func__)),
272  hair_num};
273 
274  /* The old hair system still uses #MFace, so make sure those are available on the mesh. */
275  BKE_mesh_tessface_calc(&surface_me);
276 
277  /* Prepare utility data structure to map hair roots to #MFace's. */
278  const Span<int> mface_to_poly_map{
279  static_cast<const int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
280  surface_me.totface};
281  Array<Vector<int>> poly_to_mface_map(surface_me.totpoly);
282  for (const int mface_i : mface_to_poly_map.index_range()) {
283  const int poly_i = mface_to_poly_map[mface_i];
284  poly_to_mface_map[poly_i].append(mface_i);
285  }
286 
287  /* Prepare transformation matrices. */
288  const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
289 
290  for (const int new_hair_i : IndexRange(hair_num)) {
291  const int curve_i = new_hair_i;
292  const IndexRange points = curves.points_for_curve(curve_i);
293 
294  const float3 &root_pos_cu = positions_cu[points.first()];
295  const float3 root_pos_su = transforms.curves_to_surface * root_pos_cu;
296 
297  BVHTreeNearest nearest;
298  nearest.dist_sq = FLT_MAX;
300  surface_bvh.tree, root_pos_su, &nearest, surface_bvh.nearest_callback, &surface_bvh);
301  BLI_assert(nearest.index >= 0);
302 
303  const int looptri_i = nearest.index;
304  const MLoopTri &looptri = looptris[looptri_i];
305  const int poly_i = looptri.poly;
306 
307  const int mface_i = find_mface_for_root_position(
308  surface_me, poly_to_mface_map[poly_i], root_pos_su);
309  const MFace &mface = surface_me.mface[mface_i];
310 
311  const float4 mface_weights = compute_mface_weights_for_position(
312  surface_me, mface, root_pos_su);
313 
314  ParticleData &particle = particles[new_hair_i];
315  const int num_keys = points.size();
316  MutableSpan<HairKey> hair_keys{
317  static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), num_keys};
318 
319  particle.hair = hair_keys.data();
320  particle.totkey = hair_keys.size();
321  copy_v4_v4(particle.fuv, mface_weights);
322  particle.num = mface_i;
323  /* Not sure if there is a better way to initialize this. */
324  particle.num_dmcache = DMCACHE_NOTFOUND;
325 
326  float4x4 hair_to_surface_mat;
328  &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values);
329  /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
330  copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su);
331  const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted();
332 
333  for (const int key_i : hair_keys.index_range()) {
334  const float3 &key_pos_cu = positions_cu[points[key_i]];
335  const float3 key_pos_su = transforms.curves_to_surface * key_pos_cu;
336  const float3 key_pos_ha = surface_to_hair_mat * key_pos_su;
337 
338  HairKey &key = hair_keys[key_i];
339  copy_v3_v3(key.co, key_pos_ha);
340  key.time = 100.0f * key_i / (float)(hair_keys.size() - 1);
341  }
342  }
343 
344  particle_system->particles = particles.data();
345  particle_system->totpart = particles.size();
346  particle_system->flag |= PSYS_EDITED;
347  particle_system->recalc |= ID_RECALC_PSYS_RESET;
348 
351 }
352 
354 {
355  Main &bmain = *CTX_data_main(C);
357 
358  bool could_not_convert_some_curves = false;
359 
360  Object &active_object = *CTX_data_active_object(C);
361  try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves);
362 
363  CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
364  if (curves_ob != &active_object) {
365  try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves);
366  }
367  }
368  CTX_DATA_END;
369 
370  if (could_not_convert_some_curves) {
371  BKE_report(op->reports,
372  RPT_INFO,
373  "Some curves could not be converted because they were not attached to the surface");
374  }
375 
377 
378  return OPERATOR_FINISHED;
379 }
380 
381 } // namespace convert_to_particle_system
382 
384 {
385  ot->name = "Convert Curves to Particle System";
386  ot->idname = "CURVES_OT_convert_to_particle_system";
387  ot->description = "Add a new or update an existing hair particle system on the surface object";
388 
391 
393 }
394 
395 namespace convert_from_particle_system {
396 
398 {
399  ParticleSettings &settings = *psys.part;
400  if (psys.part->type != PART_HAIR) {
401  return {};
402  }
403 
404  const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0;
405 
406  const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached};
407  const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache};
408 
409  int points_num = 0;
410  Vector<int> curve_offsets;
411  Vector<int> parents_to_transfer;
412  Vector<int> children_to_transfer;
413  if (transfer_parents) {
414  for (const int parent_i : parents_cache.index_range()) {
415  const int segments = parents_cache[parent_i]->segments;
416  if (segments <= 0) {
417  continue;
418  }
419  parents_to_transfer.append(parent_i);
420  curve_offsets.append(points_num);
421  points_num += segments + 1;
422  }
423  }
424  for (const int child_i : children_cache.index_range()) {
425  const int segments = children_cache[child_i]->segments;
426  if (segments <= 0) {
427  continue;
428  }
429  children_to_transfer.append(child_i);
430  curve_offsets.append(points_num);
431  points_num += segments + 1;
432  }
433  const int curves_num = parents_to_transfer.size() + children_to_transfer.size();
434  curve_offsets.append(points_num);
435  BLI_assert(curve_offsets.size() == curves_num + 1);
436  bke::CurvesGeometry curves(points_num, curves_num);
437  curves.offsets_for_write().copy_from(curve_offsets);
438 
439  const float4x4 object_to_world_mat = object.obmat;
440  const float4x4 world_to_object_mat = object_to_world_mat.inverted();
441 
442  MutableSpan<float3> positions = curves.positions_for_write();
443 
444  const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache,
445  const Span<int> indices_to_transfer,
446  const int curve_index_offset) {
447  threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) {
448  for (const int i : range) {
449  const int hair_i = indices_to_transfer[i];
450  const int curve_i = i + curve_index_offset;
451  const IndexRange points = curves.points_for_curve(curve_i);
452  const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()};
453  for (const int key_i : keys.index_range()) {
454  const float3 key_pos_wo = keys[key_i].co;
455  positions[points[key_i]] = world_to_object_mat * key_pos_wo;
456  }
457  }
458  });
459  };
460 
461  if (transfer_parents) {
462  copy_hair_to_curves(parents_cache, parents_to_transfer, 0);
463  }
464  copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size());
465 
466  curves.update_curve_types();
467  curves.tag_topology_changed();
468  return curves;
469 }
470 
472 {
473  Main &bmain = *CTX_data_main(C);
474  ViewLayer &view_layer = *CTX_data_view_layer(C);
476  Object *ob_from_orig = ED_object_active_context(C);
477  ParticleSystem *psys_orig = static_cast<ParticleSystem *>(
478  CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data);
479  if (psys_orig == nullptr) {
480  psys_orig = psys_get_current(ob_from_orig);
481  }
482  if (psys_orig == nullptr) {
483  return OPERATOR_CANCELLED;
484  }
485  Object *ob_from_eval = DEG_get_evaluated_object(&depsgraph, ob_from_orig);
486  ParticleSystem *psys_eval = nullptr;
487  LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) {
488  if (md->type != eModifierType_ParticleSystem) {
489  continue;
490  }
491  ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md);
492  if (!STREQ(psmd->psys->name, psys_orig->name)) {
493  continue;
494  }
495  psys_eval = psmd->psys;
496  }
497 
498  Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name);
499  Curves *curves_id = static_cast<Curves *>(ob_new->data);
500  BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false);
501  bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval);
502 
503  DEG_relations_tag_update(&bmain);
505 
506  return OPERATOR_FINISHED;
507 }
508 
510 {
511  return ED_object_active_context(C) != nullptr;
512 }
513 
514 } // namespace convert_from_particle_system
515 
517 {
518  ot->name = "Convert Particle System to Curves";
519  ot->idname = "CURVES_OT_convert_from_particle_system";
520  ot->description = "Add a new curves object based on the current state of the particle system";
521 
524 
526 }
527 
528 namespace snap_curves_to_surface {
529 
530 enum class AttachMode {
531  Nearest,
532  Deform,
533 };
534 
536  const Object &surface_ob,
537  const AttachMode attach_mode,
538  bool *r_invalid_uvs,
539  bool *r_missing_uvs)
540 {
541  Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
543 
544  Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
545 
546  VArraySpan<float2> surface_uv_map;
547  if (curves_id.surface_uv_map != nullptr) {
548  const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh);
549  surface_uv_map = surface_attributes
551  .typed<float2>();
552  }
553 
554  MutableSpan<float3> positions_cu = curves.positions_for_write();
555  MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
556 
557  const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
558  BKE_mesh_runtime_looptri_len(&surface_mesh)};
559 
560  const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
561 
562  switch (attach_mode) {
563  case AttachMode::Nearest: {
564  BVHTreeFromMesh surface_bvh;
565  BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2);
566  BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
567 
568  threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
569  for (const int curve_i : curves_range) {
570  const IndexRange points = curves.points_for_curve(curve_i);
571  const int first_point_i = points.first();
572  const float3 old_first_point_pos_cu = positions_cu[first_point_i];
573  const float3 old_first_point_pos_su = transforms.curves_to_surface *
574  old_first_point_pos_cu;
575 
576  BVHTreeNearest nearest;
577  nearest.index = -1;
578  nearest.dist_sq = FLT_MAX;
579  BLI_bvhtree_find_nearest(surface_bvh.tree,
580  old_first_point_pos_su,
581  &nearest,
582  surface_bvh.nearest_callback,
583  &surface_bvh);
584  const int looptri_index = nearest.index;
585  if (looptri_index == -1) {
586  continue;
587  }
588 
589  const float3 new_first_point_pos_su = nearest.co;
590  const float3 new_first_point_pos_cu = transforms.surface_to_curves *
591  new_first_point_pos_su;
592  const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
593 
594  for (float3 &pos_cu : positions_cu.slice(points)) {
595  pos_cu += pos_diff_cu;
596  }
597 
598  if (!surface_uv_map.is_empty()) {
599  const MLoopTri &looptri = surface_looptris[looptri_index];
600  const int corner0 = looptri.tri[0];
601  const int corner1 = looptri.tri[1];
602  const int corner2 = looptri.tri[2];
603  const float2 &uv0 = surface_uv_map[corner0];
604  const float2 &uv1 = surface_uv_map[corner1];
605  const float2 &uv2 = surface_uv_map[corner2];
606  const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co;
607  const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co;
608  const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co;
609  float3 bary_coords;
610  interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su);
611  const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2);
612  surface_uv_coords[curve_i] = uv;
613  }
614  }
615  });
616  break;
617  }
618  case AttachMode::Deform: {
619  if (surface_uv_map.is_empty()) {
620  *r_missing_uvs = true;
621  break;
622  }
624  ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris};
625 
626  threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
627  for (const int curve_i : curves_range) {
628  const IndexRange points = curves.points_for_curve(curve_i);
629  const int first_point_i = points.first();
630  const float3 old_first_point_pos_cu = positions_cu[first_point_i];
631 
632  const float2 uv = surface_uv_coords[curve_i];
633  ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
634  if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
635  *r_invalid_uvs = true;
636  continue;
637  }
638 
639  const MLoopTri &looptri = *lookup_result.looptri;
640  const float3 &bary_coords = lookup_result.bary_weights;
641 
642  const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
643  const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
644  const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
645 
646  float3 new_first_point_pos_su;
647  interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
648  const float3 new_first_point_pos_cu = transforms.surface_to_curves *
649  new_first_point_pos_su;
650 
651  const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
652  for (float3 &pos_cu : positions_cu.slice(points)) {
653  pos_cu += pos_diff_cu;
654  }
655  }
656  });
657  break;
658  }
659  }
660 
661  DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
662 }
663 
665 {
666  const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
667 
668  bool found_invalid_uvs = false;
669  bool found_missing_uvs = false;
670 
671  CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
672  if (curves_ob->type != OB_CURVES) {
673  continue;
674  }
675  Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
676  if (curves_id.surface == nullptr) {
677  continue;
678  }
679  if (curves_id.surface->type != OB_MESH) {
680  continue;
681  }
683  *curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs);
684  }
685  CTX_DATA_END;
686 
687  if (found_missing_uvs) {
688  BKE_report(op->reports,
689  RPT_ERROR,
690  "Curves do not have attachment information that can be used for deformation");
691  }
692  if (found_invalid_uvs) {
693  BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
694  }
695 
696  /* Refresh the entire window to also clear eventual modifier and nodes editor warnings.*/
697  WM_event_add_notifier(C, NC_WINDOW, nullptr);
698 
699  return OPERATOR_FINISHED;
700 }
701 
702 } // namespace snap_curves_to_surface
703 
705 {
706  using namespace snap_curves_to_surface;
707 
708  ot->name = "Snap Curves to Surface";
709  ot->idname = "CURVES_OT_snap_curves_to_surface";
710  ot->description = "Move curves so that the first point is exactly on the surface mesh";
711 
714 
716 
717  static const EnumPropertyItem attach_mode_items[] = {
718  {static_cast<int>(AttachMode::Nearest),
719  "NEAREST",
720  0,
721  "Nearest",
722  "Find the closest point on the surface for the root point of every curve and move the root "
723  "there"},
724  {static_cast<int>(AttachMode::Deform),
725  "DEFORM",
726  0,
727  "Deform",
728  "Re-attach curves to a deformed surface using the existing attachment information. This "
729  "only works when the topology of the surface mesh has not changed"},
730  {0, nullptr, 0, nullptr, nullptr},
731  };
732 
734  "attach_mode",
735  attach_mode_items,
736  static_cast<int>(AttachMode::Nearest),
737  "Attach Mode",
738  "How to find the point on the surface to attach to");
739 }
740 
741 namespace set_selection_domain {
742 
744 {
745  const eAttrDomain domain = eAttrDomain(RNA_enum_get(op->ptr, "domain"));
746 
747  for (Curves *curves_id : get_unique_editable_curves(*C)) {
748  if (curves_id->selection_domain == domain && (curves_id->flag & CV_SCULPT_SELECTION_ENABLED)) {
749  continue;
750  }
751 
752  const eAttrDomain old_domain = eAttrDomain(curves_id->selection_domain);
753  curves_id->selection_domain = domain;
754  curves_id->flag |= CV_SCULPT_SELECTION_ENABLED;
755 
756  CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
757  bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
758  if (curves.points_num() == 0) {
759  continue;
760  }
761 
762  if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) {
763  VArray<float> curve_selection = curves.adapt_domain(
764  curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
765  curve_selection.materialize(curves.selection_curve_float_for_write());
766  attributes.remove(".selection_point_float");
767  }
768  else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) {
769  VArray<float> point_selection = curves.adapt_domain(
770  curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
771  point_selection.materialize(curves.selection_point_float_for_write());
772  attributes.remove(".selection_curve_float");
773  }
774 
775  /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
776  * attribute for now. */
777  DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
778  WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
779  }
780 
782 
783  return OPERATOR_FINISHED;
784 }
785 
786 } // namespace set_selection_domain
787 
789 {
790  PropertyRNA *prop;
791 
792  ot->name = "Set Select Mode";
793  ot->idname = __func__;
794  ot->description = "Change the mode used for selection masking in curves sculpt mode";
795 
798 
800 
801  ot->prop = prop = RNA_def_enum(
802  ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", "");
804 }
805 
806 namespace disable_selection {
807 
809 {
810  for (Curves *curves_id : get_unique_editable_curves(*C)) {
811  curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED;
812 
813  /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
814  * attribute for now. */
815  DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
816  WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
817  }
818 
820 
821  return OPERATOR_FINISHED;
822 }
823 
824 } // namespace disable_selection
825 
827 {
828  ot->name = "Disable Selection";
829  ot->idname = __func__;
830  ot->description = "Disable the drawing of influence of selection in sculpt mode";
831 
834 
836 }
837 
839 {
840  bool contains_nonzero = false;
841  devirtualize_varray(data, [&](const auto array) {
842  for (const int i : data.index_range()) {
843  if (array[i] != 0.0f) {
844  contains_nonzero = true;
845  break;
846  }
847  }
848  });
849  return contains_nonzero;
850 }
851 
852 bool has_anything_selected(const Curves &curves_id)
853 {
855  switch (curves_id.selection_domain) {
856  case ATTR_DOMAIN_POINT:
857  return varray_contains_nonzero(curves.selection_point_float());
858  case ATTR_DOMAIN_CURVE:
859  return varray_contains_nonzero(curves.selection_curve_float());
860  }
862  return false;
863 }
864 
866 {
867  return varray_contains_nonzero(curves.selection_point_float());
868 }
869 
870 static bool any_point_selected(const Span<Curves *> curves_ids)
871 {
872  for (const Curves *curves_id : curves_ids) {
873  if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) {
874  return true;
875  }
876  }
877  return false;
878 }
879 
880 namespace select_all {
881 
882 static void invert_selection(MutableSpan<float> selection)
883 {
884  threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) {
885  for (const int i : range) {
886  selection[i] = 1.0f - selection[i];
887  }
888  });
889 }
890 
892 {
893  int action = RNA_enum_get(op->ptr, "action");
894 
896 
897  if (action == SEL_TOGGLE) {
898  action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
899  }
900 
901  for (Curves *curves_id : unique_curves) {
902  CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
903  if (action == SEL_SELECT) {
904  /* As an optimization, just remove the selection attributes when everything is selected. */
905  bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
906  attributes.remove(".selection_point_float");
907  attributes.remove(".selection_curve_float");
908  }
909  else {
910  MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
911  curves.selection_point_float_for_write() :
912  curves.selection_curve_float_for_write();
913  if (action == SEL_DESELECT) {
914  selection.fill(0.0f);
915  }
916  else if (action == SEL_INVERT) {
917  invert_selection(selection);
918  }
919  }
920 
921  /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
922  * attribute for now. */
923  DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
924  WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
925  }
926 
927  return OPERATOR_FINISHED;
928 }
929 
930 } // namespace select_all
931 
933 {
934  ot->name = "(De)select All";
935  ot->idname = __func__;
936  ot->description = "(De)select all control points";
937 
940 
942 
944 }
945 
946 namespace surface_set {
947 
949 {
950  const Object *object = CTX_data_active_object(C);
951  if (object == nullptr) {
952  return false;
953  }
954  if (object->type != OB_MESH) {
955  return false;
956  }
957  return true;
958 }
959 
961 {
962  Main *bmain = CTX_data_main(C);
964 
965  Object &new_surface_ob = *CTX_data_active_object(C);
966 
967  Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data);
968  const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.ldata,
969  CD_MLOOPUV);
970 
971  CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) {
972  if (selected_ob->type != OB_CURVES) {
973  continue;
974  }
975  Object &curves_ob = *selected_ob;
976  Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
977 
978  MEM_SAFE_FREE(curves_id.surface_uv_map);
979  if (new_uv_map_name != nullptr) {
980  curves_id.surface_uv_map = BLI_strdup(new_uv_map_name);
981  }
982 
983  bool missing_uvs;
984  bool invalid_uvs;
986  curves_ob,
987  new_surface_ob,
988  snap_curves_to_surface::AttachMode::Nearest,
989  &invalid_uvs,
990  &missing_uvs);
991 
992  /* Add deformation modifier if necessary. */
994 
995  curves_id.surface = &new_surface_ob;
997  op->reports, C, scene, &curves_ob, &new_surface_ob, PAR_OBJECT, false, true, nullptr);
998 
1000  WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
1002 
1003  /* Required for deformation. */
1005  DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY);
1006  }
1007  CTX_DATA_END;
1008 
1009  DEG_relations_tag_update(bmain);
1010 
1011  return OPERATOR_FINISHED;
1012 }
1013 
1014 } // namespace surface_set
1015 
1017 {
1018  ot->name = "Set Curves Surface Object";
1019  ot->idname = __func__;
1020  ot->description =
1021  "Use the active object as surface for selected curves objects and set it as the parent";
1022 
1025 
1027 }
1028 
1029 } // namespace blender::ed::curves
1030 
1032 {
1033  using namespace blender::ed::curves;
1041 }
typedef float(TangentPoint)[2]
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_CURVE
Definition: BKE_attribute.h:31
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ ATTR_DOMAIN_CORNER
Definition: BKE_attribute.h:30
void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data)
Definition: bvhutils.cc:1410
@ BVHTREE_FROM_LOOPTRI
Definition: BKE_bvhutils.h:73
BVHTree * BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, const struct Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition: bvhutils.cc:1213
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:269
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
Definition: context.c:473
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
struct Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Definition: context.c:1505
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg)
Definition: context.c:1042
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
#define CTX_DATA_END
Definition: BKE_context.h:278
Low-level operations for curves.
const char * CustomData_get_active_layer_name(const struct CustomData *data, int type)
void * CustomData_get_layer(const struct CustomData *data, int type)
bool BKE_id_is_editable(const struct Main *bmain, const struct ID *id)
void BKE_mesh_tessface_calc(struct Mesh *mesh)
const struct MLoopTri * BKE_mesh_runtime_looptri_ensure(const struct Mesh *mesh)
int BKE_mesh_runtime_looptri_len(const struct Mesh *mesh)
General operations, lookup, etc. for blender objects.
struct Object * BKE_object_add(struct Main *bmain, struct ViewLayer *view_layer, int type, const char *name) ATTR_NONNULL(1
void BKE_object_apply_mat4(struct Object *ob, const float mat[4][4], bool use_compat, bool use_parent)
Definition: object.cc:3575
#define DMCACHE_NOTFOUND
Definition: BKE_particle.h:672
void psys_mat_hair_to_object(struct Object *ob, struct Mesh *mesh, short from, struct ParticleData *pa, float hairmat[4][4])
void psys_free_particles(struct ParticleSystem *psys)
Definition: particle.c:1022
struct ParticleSystem * psys_get_current(struct Object *ob)
Definition: particle.c:634
void psys_changed_type(struct Object *ob, struct ParticleSystem *psys)
struct ModifierData * object_add_particle_system(struct Main *bmain, struct Scene *scene, struct Object *ob, const char *name)
Definition: particle.c:4002
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
int BLI_bvhtree_find_nearest(BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
Definition: BLI_kdopbvh.c:1616
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void interp_weights_tri_v3(float w[3], const float v1[3], const float v2[3], const float v3[3], const float co[3])
Definition: math_geom.c:3603
void closest_on_tri_to_point_v3(float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3])
Definition: math_geom.c:980
void interp_weights_poly_v3(float w[], float v[][3], int n, const float co[3])
MINLINE void copy_v4_v4(float r[4], const float a[4])
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])
#define BLI_SCOPED_DEFER(function_to_defer)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:42
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
void DEG_id_tag_update(struct ID *id, int flag)
void DEG_relations_tag_update(struct Main *bmain)
struct Object * DEG_get_evaluated_object(const struct Depsgraph *depsgraph, struct Object *object)
@ ID_RECALC_TRANSFORM
Definition: DNA_ID.h:771
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:834
@ ID_RECALC_PSYS_RESET
Definition: DNA_ID.h:800
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
struct CurvesGeometry CurvesGeometry
@ CV_SCULPT_SELECTION_ENABLED
@ CD_ORIGINDEX
@ CD_PROP_FLOAT2
@ CD_MLOOPUV
@ eModifierType_ParticleSystem
@ OB_MODE_EDIT
@ OB_MODE_SCULPT_CURVES
Object is a sort of wrapper for general info.
@ OB_MODIFIER_FLAG_ADD_REST_POSITION
@ OB_MESH
@ OB_CURVES
#define PART_FROM_FACE
@ PART_DRAW_PARENT
@ PART_HAIR
#define PSYS_EDITED
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ PAR_OBJECT
Definition: ED_object.h:138
struct Object * ED_object_active_context(const struct bContext *C)
bool ED_object_parent_set(struct ReportList *reports, const struct bContext *C, struct Scene *scene, struct Object *const ob, struct Object *const par, int partype, bool xmirror, bool keep_transform, const int vert_par[3])
bool ED_operator_object_active_editable_ex(struct bContext *C, const Object *ob)
Definition: screen_ops.c:376
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
float float4x4[4][4]
#define MEM_SAFE_FREE(v)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to curves
PropertyFlag
Definition: RNA_types.h:183
@ PROP_SKIP_SAVE
Definition: RNA_types.h:218
@ PROP_HIDDEN
Definition: RNA_types.h:216
#define C
Definition: RandGen.cpp:25
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define NC_WINDOW
Definition: WM_types.h:325
#define NC_NODE
Definition: WM_types.h:344
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DRAW
Definition: WM_types.h:410
#define ND_DATA
Definition: WM_types.h:456
#define NA_ADDED
Definition: WM_types.h:525
#define NA_EDITED
Definition: WM_types.h:523
#define ND_PARTICLE
Definition: WM_types.h:414
#define ND_SPACE_VIEW3D
Definition: WM_types.h:471
#define NC_OBJECT
Definition: WM_types.h:329
#define NC_SPACE
Definition: WM_types.h:342
constexpr int64_t first() const
constexpr int64_t size() const
constexpr void fill(const T &value)
Definition: BLI_span.hh:527
constexpr IndexRange index_range() const
Definition: BLI_span.hh:661
constexpr const T & first() const
Definition: BLI_span.hh:303
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr bool is_empty() const
Definition: BLI_span.hh:248
void materialize(MutableSpan< T > r_span) const
bool add(const Key &key)
void add_new(const Key &key)
int64_t size() const
Definition: BLI_vector.hh:694
void append(const T &value)
Definition: BLI_vector.hh:433
GAttributeReader lookup(const AttributeIDRef &attribute_id) const
static CurvesGeometry & wrap(::CurvesGeometry &dna_struct)
Definition: BKE_curves.hh:138
bool remove(const AttributeIDRef &attribute_id)
void ED_operatortypes_curves()
Definition: curves_ops.cc:1031
Scene scene
const Depsgraph * depsgraph
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:32
static struct PartialUpdateUser * wrap(PartialUpdateUserImpl *user)
AttributeAccessor mesh_attributes(const Mesh &mesh)
static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNUSED(op))
Definition: curves_ops.cc:471
static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &psys)
Definition: curves_ops.cc:397
static bool curves_convert_from_particle_system_poll(bContext *C)
Definition: curves_ops.cc:509
static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
Definition: curves_ops.cc:353
static int find_mface_for_root_position(const Mesh &mesh, const Span< int > possible_mface_indices, const float3 &root_pos)
Definition: curves_ops.cc:142
static float4 compute_mface_weights_for_position(const Mesh &mesh, const MFace &mface, const float3 &position)
Definition: curves_ops.cc:189
static void try_convert_single_object(Object &curves_ob, Main &bmain, Scene &scene, bool *r_could_not_convert_some_curves)
Definition: curves_ops.cc:213
static int curves_disable_selection_exec(bContext *C, wmOperator *UNUSED(op))
Definition: curves_ops.cc:808
static void invert_selection(MutableSpan< float > selection)
Definition: curves_ops.cc:882
static int select_all_exec(bContext *C, wmOperator *op)
Definition: curves_ops.cc:891
static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
Definition: curves_ops.cc:743
static void snap_curves_to_surface_exec_object(Object &curves_ob, const Object &surface_ob, const AttachMode attach_mode, bool *r_invalid_uvs, bool *r_missing_uvs)
Definition: curves_ops.cc:535
static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
Definition: curves_ops.cc:664
static int surface_set_exec(bContext *C, wmOperator *op)
Definition: curves_ops.cc:960
static bool surface_set_poll(bContext *C)
Definition: curves_ops.cc:948
static void CURVES_OT_surface_set(wmOperatorType *ot)
Definition: curves_ops.cc:1016
static void CURVES_OT_convert_from_particle_system(wmOperatorType *ot)
Definition: curves_ops.cc:516
static bool object_has_editable_curves(const Main &bmain, const Object &object)
Definition: curves_ops.cc:62
static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
Definition: curves_ops.cc:383
static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot)
Definition: curves_ops.cc:932
bool editable_curves_poll(bContext *C)
Definition: curves_ops.cc:128
static bool any_point_selected(const Span< Curves * > curves_ids)
Definition: curves_ops.cc:870
bool has_anything_selected(const Curves &curves_id)
Definition: curves_ops.cc:852
VectorSet< Curves * > get_unique_editable_curves(const bContext &C)
Definition: curves_ops.cc:76
static bool varray_contains_nonzero(const VArray< float > &data)
Definition: curves_ops.cc:838
static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
Definition: curves_ops.cc:704
void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob)
Definition: curves_add.cc:58
bool curves_with_surface_poll(bContext *C)
Definition: curves_ops.cc:123
static void CURVES_OT_disable_selection(wmOperatorType *ot)
Definition: curves_ops.cc:826
static bool curves_poll_impl(bContext *C, const bool check_editable, const bool check_surface)
Definition: curves_ops.cc:97
bool curves_poll(bContext *C)
Definition: curves_ops.cc:133
static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
Definition: curves_ops.cc:788
bool editable_curves_with_surface_poll(bContext *C)
Definition: curves_ops.cc:118
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
MutableSpan< float3 > positions
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
const EnumPropertyItem rna_enum_attribute_curves_domain_items[]
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1490
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
struct BVHTree * tree
Definition: BKE_bvhutils.h:50
BVHTree_NearestPointCallback nearest_callback
Definition: BKE_bvhutils.h:53
CurvesGeometry geometry
char selection_domain
struct Object * surface
char * surface_uv_map
float co[3]
Definition: DNA_ID.h:368
char name[66]
Definition: DNA_ID.h:378
unsigned int v2
unsigned int v1
unsigned int v4
unsigned int v3
unsigned int poly
float co[3]
Definition: BKE_main.h:121
struct MVert * mvert
int totface
CustomData fdata
int totpoly
struct MFace * mface
CustomData ldata
ListBase particlesystem
ListBase modifiers
uint8_t modifier_flag
float obmat[4][4]
void * data
struct ParticleSystem * psys
ParticleData * particles
ParticleSettings * part
struct ParticleCacheKey ** pathcache
void * data
Definition: RNA_types.h:38
AttributeReader< T > typed() const
float values[4][4]
Definition: BLI_float4x4.hh:13
float4x4 inverted() const
const char * name
Definition: WM_types.h:888
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
PropertyRNA * prop
Definition: WM_types.h:981
struct ReportList * reports
struct PointerRNA * ptr
void WM_main_add_notifier(unsigned int type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3479
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))