Blender  V3.3
abstract_hierarchy_iterator.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2019 Blender Foundation. All rights reserved. */
4 #include "dupli_parent_finder.hh"
5 
6 #include <climits>
7 #include <cstdio>
8 #include <iostream>
9 #include <sstream>
10 #include <string>
11 
12 #include "BKE_anim_data.h"
13 #include "BKE_duplilist.h"
14 #include "BKE_key.h"
15 #include "BKE_object.h"
16 #include "BKE_particle.h"
17 
18 #include "BLI_assert.h"
19 #include "BLI_listbase.h"
20 #include "BLI_math_matrix.h"
21 
22 #include "DNA_ID.h"
23 #include "DNA_layer_types.h"
24 #include "DNA_modifier_types.h"
25 #include "DNA_object_types.h"
26 #include "DNA_particle_types.h"
27 #include "DNA_rigidbody_types.h"
28 
29 #include "DEG_depsgraph_query.h"
30 
31 namespace blender::io {
32 
34 {
35  return nullptr;
36 }
37 
39 {
40  if (object != other.object) {
41  return object < other.object;
42  }
43  if (duplicator != nullptr && duplicator == other.duplicator) {
44  /* Only resort to string comparisons when both objects are created by the same duplicator. */
45  return export_name < other.export_name;
46  }
47 
48  return export_parent < other.export_parent;
49 }
50 
52 {
53  return !original_export_path.empty();
54 }
55 void HierarchyContext::mark_as_instance_of(const std::string &reference_export_path)
56 {
57  original_export_path = reference_export_path;
58 }
60 {
61  original_export_path.clear();
62 }
63 
64 bool HierarchyContext::is_object_visible(const enum eEvaluationMode evaluation_mode) const
65 {
66  const bool is_dupli = duplicator != nullptr;
67  int base_flag;
68 
69  if (is_dupli) {
70  /* Construct the object's base flags from its dupli-parent, just like is done in
71  * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing
72  * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents
73  * copying the Object for every dupli. */
74  base_flag = object->base_flag;
75  object->base_flag = duplicator->base_flag | BASE_FROM_DUPLI;
76  }
77 
78  const int visibility = BKE_object_visibility(object, evaluation_mode);
79 
80  if (is_dupli) {
81  object->base_flag = base_flag;
82  }
83 
84  return (visibility & OB_VISIBLE_SELF) != 0;
85 }
86 
87 EnsuredWriter::EnsuredWriter() : writer_(nullptr), newly_created_(false)
88 {
89 }
90 
91 EnsuredWriter::EnsuredWriter(AbstractHierarchyWriter *writer, bool newly_created)
92  : writer_(writer), newly_created_(newly_created)
93 {
94 }
95 
97 {
98  return EnsuredWriter(nullptr, false);
99 }
101 {
102  return EnsuredWriter(writer, false);
103 }
105 {
106  return EnsuredWriter(writer, true);
107 }
108 
110 {
111  return newly_created_;
112 }
113 
114 EnsuredWriter::operator bool() const
115 {
116  return writer_ != nullptr;
117 }
118 
120 {
121  return writer_;
122 }
123 
125 {
126  Object *object = context.object;
127 
128  if (BKE_animdata_id_is_animated(static_cast<ID *>(object->data))) {
129  return true;
130  }
131  if (BKE_key_from_object(object) != nullptr) {
132  return true;
133  }
135  return true;
136  }
137 
138  /* Test modifiers. */
139  /* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on
140  * time. */
141  ModifierData *md = static_cast<ModifierData *>(object->modifiers.first);
142  while (md) {
143  if (md->type != eModifierType_Subsurf) {
144  return true;
145  }
146  md = md->next;
147  }
148 
149  return false;
150 }
151 
153 {
154  const RigidBodyOb *rbo = context.object->rigidbody_object;
155  return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE;
156 }
157 
159 {
160  const RigidBodyOb *rbo = context.object->rigidbody_object;
161  return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0;
162 }
163 
165  : bmain_(bmain), depsgraph_(depsgraph), export_subset_({true, true})
166 {
167 }
168 
170 {
171  /* release_writers() cannot be called here directly, as it calls into the pure-virtual
172  * release_writer() function. By the time this destructor is called, the subclass that implements
173  * that pure-virtual function is already destructed. */
174  BLI_assert(
175  writers_.empty() ||
176  !"release_writers() should be called before the AbstractHierarchyIterator goes out of scope");
177 }
178 
180 {
181  export_graph_construct();
182  connect_loose_objects();
183  export_graph_prune();
184  determine_export_paths(HierarchyContext::root());
185  determine_duplication_references(HierarchyContext::root(), "");
186  make_writers(HierarchyContext::root());
187  export_graph_clear();
188 }
189 
191 {
192  for (WriterMap::value_type it : writers_) {
193  release_writer(it.second);
194  }
195  writers_.clear();
196 }
197 
199 {
200  export_subset_ = export_subset;
201 }
202 
203 std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const
204 {
205  return name;
206 }
207 
208 std::string AbstractHierarchyIterator::get_id_name(const ID *id) const
209 {
210  if (id == nullptr) {
211  return "";
212  }
213 
214  return make_valid_name(std::string(id->name + 2));
215 }
216 
218 {
219  BLI_assert(!context->export_path.empty());
220  BLI_assert(context->object->data);
221 
222  return path_concatenate(context->export_path, get_object_data_name(context->object));
223 }
224 
225 void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const
226 {
227  size_t total_graph_size = 0;
228  for (const ExportGraph::value_type &map_iter : graph) {
229  const ObjectIdentifier &parent_info = map_iter.first;
230  const Object *const export_parent = parent_info.object;
231  const Object *const duplicator = parent_info.duplicated_by;
232 
233  if (duplicator != nullptr) {
234  printf(" DU %s (as dupped by %s):\n",
235  export_parent == nullptr ? "-null-" : (export_parent->id.name + 2),
236  duplicator->id.name + 2);
237  }
238  else {
239  printf(" OB %s:\n", export_parent == nullptr ? "-null-" : (export_parent->id.name + 2));
240  }
241 
242  total_graph_size += map_iter.second.size();
243  for (HierarchyContext *child_ctx : map_iter.second) {
244  if (child_ctx->duplicator == nullptr) {
245  printf(" - %s%s%s\n",
246  child_ctx->export_name.c_str(),
247  child_ctx->weak_export ? " (weak)" : "",
248  child_ctx->original_export_path.empty() ?
249  "" :
250  (std::string("ref ") + child_ctx->original_export_path).c_str());
251  }
252  else {
253  printf(" - %s (dup by %s%s) %s\n",
254  child_ctx->export_name.c_str(),
255  child_ctx->duplicator->id.name + 2,
256  child_ctx->weak_export ? ", weak" : "",
257  child_ctx->original_export_path.empty() ?
258  "" :
259  (std::string("ref ") + child_ctx->original_export_path).c_str());
260  }
261  }
262  }
263  printf(" (Total graph size: %zu objects)\n", total_graph_size);
264 }
265 
266 void AbstractHierarchyIterator::export_graph_construct()
267 {
269 
270  /* Add a "null" root node with no children immediately for the case where the top-most node in
271  * the scene is not being exported and a root node otherwise wouldn't get added. */
272  ExportGraph::key_type root_node_id = ObjectIdentifier::for_real_object(nullptr);
273  export_graph_[root_node_id] = ExportChildren();
274 
276  object,
279  /* Non-instanced objects always have their object-parent as export-parent. */
280  const bool weak_export = mark_as_weak_export(object);
281  visit_object(object, object->parent, weak_export);
282 
283  if (weak_export) {
284  /* If a duplicator shouldn't be exported, its duplilist also shouldn't be. */
285  continue;
286  }
287 
288  /* Export the duplicated objects instanced by this object. */
289  ListBase *lb = object_duplilist(depsgraph_, scene, object);
290  if (lb) {
291  DupliParentFinder dupli_parent_finder;
292 
293  LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
294  PersistentID persistent_id(dupli_object);
295  if (!should_visit_dupli_object(dupli_object)) {
296  continue;
297  }
298  dupli_parent_finder.insert(dupli_object);
299  }
300 
301  LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
302  if (!should_visit_dupli_object(dupli_object)) {
303  continue;
304  }
305  visit_dupli_object(dupli_object, object, dupli_parent_finder);
306  }
307  }
308 
310  }
312 }
313 
314 void AbstractHierarchyIterator::connect_loose_objects()
315 {
316  /* Find those objects whose parent is not part of the export graph; these
317  * objects would be skipped when traversing the graph as a hierarchy.
318  * These objects will have to be re-attached to some parent object in order to
319  * fit into the hierarchy. */
320  ExportGraph loose_objects_graph = export_graph_;
321  for (const ExportGraph::value_type &map_iter : export_graph_) {
322  for (const HierarchyContext *child : map_iter.second) {
323  /* An object that is marked as a child of another object is not considered 'loose'. */
324  ObjectIdentifier child_oid = ObjectIdentifier::for_hierarchy_context(child);
325  loose_objects_graph.erase(child_oid);
326  }
327  }
328  /* The root of the hierarchy is always found, so it's never considered 'loose'. */
329  loose_objects_graph.erase(ObjectIdentifier::for_graph_root());
330 
331  /* Iterate over the loose objects and connect them to their export parent. */
332  for (const ExportGraph::value_type &map_iter : loose_objects_graph) {
333  const ObjectIdentifier &graph_key = map_iter.first;
334  Object *object = graph_key.object;
335 
336  while (true) {
337  /* Loose objects will all be real objects, as duplicated objects always have
338  * their duplicator or other exported duplicated object as ancestor. */
339 
340  ExportGraph::iterator found_parent_iter = export_graph_.find(
342  visit_object(object, object->parent, true);
343  if (found_parent_iter != export_graph_.end()) {
344  break;
345  }
346  /* 'object->parent' will never be nullptr here, as the export graph contains the
347  * root as nullptr and thus will cause a break above. */
348  BLI_assert(object->parent != nullptr);
349 
350  object = object->parent;
351  }
352  }
353 }
354 
357  const AbstractHierarchyIterator::ExportGraph &input_graph)
358 {
359  bool all_is_weak = context != nullptr && context->weak_export;
361 
362  AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator;
363 
364  child_iterator = input_graph.find(map_key);
365  if (child_iterator != input_graph.end()) {
366  for (HierarchyContext *child_context : child_iterator->second) {
367  bool child_tree_is_weak = remove_weak_subtrees(child_context, clean_graph, input_graph);
368  all_is_weak &= child_tree_is_weak;
369 
370  if (child_tree_is_weak) {
371  /* This subtree is all weak, so we can remove it from the current object's children. */
372  clean_graph[map_key].erase(child_context);
373  delete child_context;
374  }
375  }
376  }
377 
378  if (all_is_weak) {
379  /* This node and all its children are weak, so it can be removed from the export graph. */
380  clean_graph.erase(map_key);
381  }
382 
383  return all_is_weak;
384 }
385 
386 void AbstractHierarchyIterator::export_graph_prune()
387 {
388  /* Take a copy of the map so that we can modify while recusing. */
389  ExportGraph unpruned_export_graph = export_graph_;
390  remove_weak_subtrees(HierarchyContext::root(), export_graph_, unpruned_export_graph);
391 }
392 
393 void AbstractHierarchyIterator::export_graph_clear()
394 {
395  for (ExportGraph::iterator::value_type &it : export_graph_) {
396  for (HierarchyContext *context : it.second) {
397  delete context;
398  }
399  }
400  export_graph_.clear();
401 }
402 
403 void AbstractHierarchyIterator::visit_object(Object *object,
404  Object *export_parent,
405  bool weak_export)
406 {
407  HierarchyContext *context = new HierarchyContext();
408  context->object = object;
409  context->export_name = get_object_name(object);
410  context->export_parent = export_parent;
411  context->duplicator = nullptr;
412  context->weak_export = weak_export;
413  context->animation_check_include_parent = false;
414  context->export_path = "";
415  context->original_export_path = "";
416  context->higher_up_export_path = "";
417 
418  copy_m4_m4(context->matrix_world, object->obmat);
419 
420  ExportGraph::key_type graph_index = determine_graph_index_object(context);
421  context_update_for_graph_index(context, graph_index);
422 
423  /* Store this HierarchyContext as child of the export parent. */
424  export_graph_[graph_index].insert(context);
425 
426  /* Create an empty entry for this object to indicate it is part of the export. This will be used
427  * by connect_loose_objects(). Having such an "indicator" will make it possible to do an O(log n)
428  * check on whether an object is part of the export, rather than having to check all objects in
429  * the map. Note that it's not possible to simply search for (object->parent, nullptr), as the
430  * object's parent in Blender may not be the same as its export-parent. */
431  ExportGraph::key_type object_key = ObjectIdentifier::for_real_object(object);
432  if (export_graph_.find(object_key) == export_graph_.end()) {
433  export_graph_[object_key] = ExportChildren();
434  }
435 }
436 
437 AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator::
439 {
440  return ObjectIdentifier::for_real_object(context->export_parent);
441 }
442 
443 void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object,
444  Object *duplicator,
445  const DupliParentFinder &dupli_parent_finder)
446 {
448  context->object = dupli_object->ob;
449  context->duplicator = duplicator;
450  context->persistent_id = PersistentID(dupli_object);
451  context->weak_export = false;
452  context->export_path = "";
453  context->original_export_path = "";
454  context->animation_check_include_parent = false;
455 
456  copy_m4_m4(context->matrix_world, dupli_object->mat);
457 
458  /* Construct export name for the dupli-instance. */
459  std::stringstream export_name_stream;
460  export_name_stream << get_object_name(context->object) << "-"
461  << context->persistent_id.as_object_name_suffix();
462  context->export_name = make_valid_name(export_name_stream.str());
463 
464  ExportGraph::key_type graph_index = determine_graph_index_dupli(
465  context, dupli_object, dupli_parent_finder);
466  context_update_for_graph_index(context, graph_index);
467 
468  export_graph_[graph_index].insert(context);
469 }
470 
471 AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator::
473  const DupliObject *dupli_object,
474  const DupliParentFinder &dupli_parent_finder)
475 {
476  const DupliObject *dupli_parent = dupli_parent_finder.find_suitable_export_parent(dupli_object);
477 
478  if (dupli_parent != nullptr) {
479  return ObjectIdentifier::for_duplicated_object(dupli_parent, context->duplicator);
480  }
481  return ObjectIdentifier::for_real_object(context->duplicator);
482 }
483 
484 void AbstractHierarchyIterator::context_update_for_graph_index(
485  HierarchyContext *context, const ExportGraph::key_type &graph_index) const
486 {
487  /* Update the HierarchyContext so that it is consistent with the graph index. */
488  context->export_parent = graph_index.object;
489  if (context->export_parent != context->object->parent) {
490  /* The parent object in Blender is NOT used as the export parent. This means
491  * that the world transform of this object can be influenced by objects that
492  * are not part of its export graph. */
493  context->animation_check_include_parent = true;
494  }
495 }
496 
498  const HierarchyContext *context)
499 {
501 }
502 
503 void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context)
504 {
505  const std::string &parent_export_path = parent_context ? parent_context->export_path : "";
506 
507  for (HierarchyContext *context : graph_children(parent_context)) {
508  context->export_path = path_concatenate(parent_export_path, context->export_name);
509 
510  if (context->duplicator == nullptr) {
511  /* This is an original (i.e. non-instanced) object, so we should keep track of where it was
512  * exported to, just in case it gets instanced somewhere. */
513  ID *source_ob = &context->object->id;
514  duplisource_export_path_[source_ob] = context->export_path;
515 
516  if (context->object->data != nullptr) {
517  ID *source_data = static_cast<ID *>(context->object->data);
519  }
520  }
521 
522  determine_export_paths(context);
523  }
524 }
525 
526 void AbstractHierarchyIterator::determine_duplication_references(
527  const HierarchyContext *parent_context, std::string indent)
528 {
529  ExportChildren children = graph_children(parent_context);
530 
531  for (HierarchyContext *context : children) {
532  if (context->duplicator != nullptr) {
533  ID *source_id = &context->object->id;
534  const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_id);
535 
536  if (it == duplisource_export_path_.end()) {
537  /* The original was not found, so mark this instance as "the original". */
538  context->mark_as_not_instanced();
539  duplisource_export_path_[source_id] = context->export_path;
540  }
541  else {
542  context->mark_as_instance_of(it->second);
543  }
544 
545  if (context->object->data) {
546  ID *source_data_id = (ID *)context->object->data;
547  const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_data_id);
548 
549  if (it == duplisource_export_path_.end()) {
550  /* The original was not found, so mark this instance as "original". */
551  std::string data_path = get_object_data_path(context);
552  context->mark_as_not_instanced();
553  duplisource_export_path_[source_id] = context->export_path;
554  duplisource_export_path_[source_data_id] = data_path;
555  }
556  }
557  }
558 
559  determine_duplication_references(context, indent + " ");
560  }
561 }
562 
563 void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context)
564 {
565  float parent_matrix_inv_world[4][4];
566 
567  if (parent_context) {
568  invert_m4_m4(parent_matrix_inv_world, parent_context->matrix_world);
569  }
570  else {
571  unit_m4(parent_matrix_inv_world);
572  }
573 
574  for (HierarchyContext *context : graph_children(parent_context)) {
575  /* Update the context so that it is correct for this parent-child relation. */
576  copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world);
577  if (parent_context != nullptr) {
578  context->higher_up_export_path = parent_context->export_path;
579  }
580 
581  /* Get or create the transform writer. */
582  EnsuredWriter transform_writer = ensure_writer(
584 
585  if (!transform_writer) {
586  /* Unable to export, so there is nothing to attach any children to; just abort this entire
587  * branch of the export hierarchy. */
588  return;
589  }
590 
592  if (transform_writer.is_newly_created() || export_subset_.transforms) {
593  /* XXX This can lead to too many XForms being written. For example, a camera writer can
594  * refuse to write an orthographic camera. By the time that this is known, the XForm has
595  * already been written. */
596  transform_writer->write(*context);
597  }
598 
599  if (!context->weak_export) {
600  make_writers_particle_systems(context);
601  make_writer_object_data(context);
602  }
603 
604  /* Recurse into this object's children. */
605  make_writers(context);
606  }
607 
608  /* TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something.
609  */
610 }
611 
612 HierarchyContext AbstractHierarchyIterator::context_for_object_data(
613  const HierarchyContext *object_context) const
614 {
615  HierarchyContext data_context = *object_context;
616  data_context.higher_up_export_path = object_context->export_path;
617  data_context.export_name = get_object_data_name(data_context.object);
618  data_context.export_path = path_concatenate(data_context.higher_up_export_path,
619  data_context.export_name);
620  return data_context;
621 }
622 
623 void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context)
624 {
625  if (context->object->data == nullptr) {
626  return;
627  }
628 
629  HierarchyContext data_context = context_for_object_data(context);
630  if (data_context.is_instance()) {
631  ID *object_data = static_cast<ID *>(context->object->data);
632  data_context.original_export_path = duplisource_export_path_[object_data];
633 
634  /* If the object is marked as an instance, so should the object data. */
635  BLI_assert(data_context.is_instance());
636  }
637 
638  /* Always write upon creation, otherwise depend on which subset is active. */
639  EnsuredWriter data_writer = ensure_writer(&data_context,
641  if (!data_writer) {
642  return;
643  }
644 
645  if (data_writer.is_newly_created() || export_subset_.shapes) {
646  data_writer->write(data_context);
647  }
648 }
649 
650 void AbstractHierarchyIterator::make_writers_particle_systems(
651  const HierarchyContext *transform_context)
652 {
653  Object *object = transform_context->object;
654  ParticleSystem *psys = static_cast<ParticleSystem *>(object->particlesystem.first);
655  for (; psys; psys = psys->next) {
656  if (!psys_check_enabled(object, psys, true)) {
657  continue;
658  }
659 
660  HierarchyContext hair_context = *transform_context;
661  hair_context.export_name = make_valid_name(psys->name);
662  hair_context.export_path = path_concatenate(transform_context->export_path,
663  hair_context.export_name);
664  hair_context.higher_up_export_path = transform_context->export_path;
665  hair_context.particle_system = psys;
666 
667  EnsuredWriter writer;
668  switch (psys->part->type) {
669  case PART_HAIR:
670  writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer);
671  break;
672  case PART_EMITTER:
673  case PART_FLUID_FLIP:
674  case PART_FLUID_SPRAY:
675  case PART_FLUID_BUBBLE:
676  case PART_FLUID_FOAM:
677  case PART_FLUID_TRACER:
682  writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer);
683  break;
684  }
685  if (!writer) {
686  continue;
687  }
688 
689  /* Always write upon creation, otherwise depend on which subset is active. */
690  if (writer.is_newly_created() || export_subset_.shapes) {
691  writer->write(hair_context);
692  }
693  }
694 }
695 
696 std::string AbstractHierarchyIterator::get_object_name(const Object *object) const
697 {
698  return get_id_name(&object->id);
699 }
700 
701 std::string AbstractHierarchyIterator::get_object_data_name(const Object *object) const
702 {
703  ID *object_data = static_cast<ID *>(object->data);
704  return get_id_name(object_data);
705 }
706 
708  const std::string &export_path) const
709 {
710  WriterMap::const_iterator it = writers_.find(export_path);
711 
712  if (it == writers_.end()) {
713  return nullptr;
714  }
715  return it->second;
716 }
717 
718 EnsuredWriter AbstractHierarchyIterator::ensure_writer(
719  HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func)
720 {
721  AbstractHierarchyWriter *writer = get_writer(context->export_path);
722  if (writer != nullptr) {
723  return EnsuredWriter::existing(writer);
724  }
725 
726  writer = (this->*create_func)(context);
727  if (writer == nullptr) {
728  return EnsuredWriter::empty();
729  }
730 
731  writers_[context->export_path] = writer;
732  return EnsuredWriter::newly_created(writer);
733 }
734 
735 std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path,
736  const std::string &child_path) const
737 {
738  return parent_path + "/" + child_path;
739 }
740 
742 {
743  return false;
744 }
746 {
747  /* Removing dupli_object->no_draw hides things like custom bone shapes. */
748  return !dupli_object->no_draw;
749 }
750 
751 } // namespace blender::io
bool BKE_animdata_id_is_animated(const struct ID *id)
Definition: anim_data.c:238
struct ListBase * object_duplilist(struct Depsgraph *depsgraph, struct Scene *sce, struct Object *ob)
void free_object_duplilist(struct ListBase *lb)
struct Key * BKE_key_from_object(struct Object *ob)
Definition: key.c:1803
General operations, lookup, etc. for blender objects.
@ OB_VISIBLE_SELF
Definition: BKE_object.h:150
int BKE_object_visibility(const struct Object *ob, int dag_eval_mode)
bool psys_check_enabled(struct Object *ob, struct ParticleSystem *psys, bool use_render_params)
Definition: particle.c:801
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void unit_m4(float m[4][4])
Definition: rct.c:1090
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void copy_m4_m4(float m1[4][4], const float m2[4][4])
Definition: math_matrix.c:77
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
eEvaluationMode
Definition: DEG_depsgraph.h:44
#define DEG_OBJECT_ITER_END
bool DEG_is_evaluated_object(const struct Object *object)
#define DEG_OBJECT_ITER_BEGIN(graph_, instance_, flag_)
@ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY
@ DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET
struct Scene * DEG_get_evaluated_scene(const struct Depsgraph *graph)
ID and Library types, which are fundamental for sdna.
@ BASE_FROM_DUPLI
@ eModifierType_Subsurf
Object is a sort of wrapper for general info.
@ PART_FLUID_FLIP
@ PART_EMITTER
@ PART_FLUID_BUBBLE
@ PART_FLUID_SPRAYBUBBLE
@ PART_FLUID_TRACER
@ PART_FLUID_FOAM
@ PART_FLUID_SPRAYFOAMBUBBLE
@ PART_FLUID_SPRAYFOAM
@ PART_HAIR
@ PART_FLUID_SPRAY
@ PART_FLUID_FOAMBUBBLE
Types and defines for representing Rigid Body entities.
@ RBO_TYPE_ACTIVE
@ RBO_FLAG_USE_DEFORM
std::map< ObjectIdentifier, ExportChildren > ExportGraph
virtual ExportGraph::key_type determine_graph_index_object(const HierarchyContext *context)
virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const
virtual AbstractHierarchyWriter * create_data_writer(const HierarchyContext *context)=0
virtual void release_writer(AbstractHierarchyWriter *writer)=0
virtual AbstractHierarchyWriter * create_transform_writer(const HierarchyContext *context)=0
virtual AbstractHierarchyWriter * create_particle_writer(const HierarchyContext *context)=0
virtual bool mark_as_weak_export(const Object *object) const
virtual std::string get_id_name(const ID *id) const
AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph)
virtual AbstractHierarchyWriter * create_hair_writer(const HierarchyContext *context)=0
void set_export_subset(ExportSubset export_subset_)
ExportChildren & graph_children(const HierarchyContext *parent_context)
virtual std::string path_concatenate(const std::string &parent_path, const std::string &child_path) const
AbstractHierarchyWriter * get_writer(const std::string &export_path) const
virtual std::string make_valid_name(const std::string &name) const
virtual ExportGraph::key_type determine_graph_index_dupli(const HierarchyContext *context, const DupliObject *dupli_object, const DupliParentFinder &dupli_parent_finder)
virtual std::string get_object_data_path(const HierarchyContext *context) const
virtual bool check_is_animated(const HierarchyContext &context) const
static bool check_has_deforming_physics(const HierarchyContext &context)
static bool check_has_physics(const HierarchyContext &context)
const DupliObject * find_suitable_export_parent(const DupliObject *dupli_ob) const
static EnsuredWriter existing(AbstractHierarchyWriter *writer)
static EnsuredWriter newly_created(AbstractHierarchyWriter *writer)
AbstractHierarchyWriter * operator->()
static ObjectIdentifier for_graph_root()
static ObjectIdentifier for_duplicated_object(const DupliObject *dupli_object, Object *duplicated_by)
static ObjectIdentifier for_real_object(Object *object)
static ObjectIdentifier for_hierarchy_context(const HierarchyContext *context)
Depsgraph * graph
Scene scene
const Depsgraph * depsgraph
static bool remove_weak_subtrees(const HierarchyContext *context, AbstractHierarchyIterator::ExportGraph &clean_graph, const AbstractHierarchyIterator::ExportGraph &input_graph)
static PyObject * create_func(PyObject *, PyObject *args)
Definition: python.cpp:156
float mat[4][4]
Definition: BKE_duplilist.h:37
struct Object * ob
Definition: BKE_duplilist.h:34
Definition: DNA_ID.h:368
char name[66]
Definition: DNA_ID.h:378
Definition: BKE_main.h:121
struct ModifierData * next
short base_flag
float obmat[4][4]
struct Object * parent
void * data
ParticleSettings * part
struct ParticleSystem * next
bool is_object_visible(enum eEvaluationMode evaluation_mode) const
static const HierarchyContext * root()
bool operator<(const HierarchyContext &other) const
void mark_as_instance_of(const std::string &reference_export_path)