Blender V5.0
abstract_hierarchy_iterator.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
6
7#include <string>
8
9#include <fmt/core.h>
10
11#include "BKE_anim_data.hh"
12#include "BKE_duplilist.hh"
13#include "BKE_geometry_set.hh"
15#include "BKE_key.hh"
16#include "BKE_object.hh"
17#include "BKE_particle.h"
18
19#include "BLI_assert.h"
20#include "BLI_math_matrix.h"
21#include "BLI_set.hh"
22#include "BLI_string_utils.hh"
23
24#include "DNA_ID.h"
25#include "DNA_layer_types.h"
26#include "DNA_modifier_types.h"
27#include "DNA_object_types.h"
28#include "DNA_particle_types.h"
29#include "DNA_rigidbody_types.h"
30
32
33namespace blender::io {
34
36{
37 return nullptr;
38}
39
41{
42 return !original_export_path.empty();
43}
44void HierarchyContext::mark_as_instance_of(const std::string &reference_export_path)
45{
46 original_export_path = reference_export_path;
47}
52
54{
55 /* The context is for a prototype if it's for a duplisource or
56 * for a duplicated object that was designated to be a prototype
57 * because the original was not included in the export. */
58 return is_duplisource || (duplicator != nullptr && !is_instance());
59}
60
61bool HierarchyContext::is_object_visible(const enum eEvaluationMode evaluation_mode) const
62{
63 const bool is_dupli = duplicator != nullptr;
64 int base_flag;
65
66 if (is_dupli) {
67 /* Construct the object's base flags from its dupli-parent, just like is done in
68 * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing
69 * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents
70 * copying the Object for every dupli. */
71 base_flag = object->base_flag;
72 object->base_flag = duplicator->base_flag | BASE_FROM_DUPLI;
73 }
74
75 const int visibility = BKE_object_visibility(object, evaluation_mode);
76
77 if (is_dupli) {
78 object->base_flag = base_flag;
79 }
80
81 return (visibility & OB_VISIBLE_SELF) != 0;
82}
83
84EnsuredWriter::EnsuredWriter() : writer_(nullptr), newly_created_(false) {}
85
87 : writer_(writer), newly_created_(newly_created)
88{
89}
90
91EnsuredWriter EnsuredWriter::empty()
92{
93 return EnsuredWriter(nullptr, false);
94}
96{
97 return EnsuredWriter(writer, false);
98}
100{
101 return EnsuredWriter(writer, true);
102}
103
105{
106 return newly_created_;
107}
108
109EnsuredWriter::operator bool() const
110{
111 return writer_ != nullptr;
112}
113
115{
116 return writer_;
117}
118
120{
121 Object *object = context.object;
122
123 if (BKE_animdata_id_is_animated(static_cast<ID *>(object->data))) {
124 return true;
125 }
126 if (BKE_key_from_object(object) != nullptr) {
127 return true;
128 }
129 if (check_has_deforming_physics(context)) {
130 return true;
131 }
132
133 /* Test modifiers. */
134 /* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on
135 * time. */
136 ModifierData *md = static_cast<ModifierData *>(object->modifiers.first);
137 while (md) {
138 if (md->type != eModifierType_Subsurf) {
139 return true;
140 }
141 md = md->next;
142 }
143
144 return false;
145}
146
148{
149 const RigidBodyOb *rbo = context.object->rigidbody_object;
150 return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE;
151}
152
154{
155 const RigidBodyOb *rbo = context.object->rigidbody_object;
156 return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0;
157}
158
160{
161 if (!object) {
162 return false;
163 }
164
165 /* Collection instancers are handled elsewhere as part of Scene instancing. */
166 if (object->type == OB_EMPTY && object->instance_collection != nullptr) {
167 return false;
168 }
169
170 const bke::GeometrySet geometry_set = bke::object_get_evaluated_geometry_set(*object);
171 return geometry_set.has_instances();
172}
173
178
180{
181 /* release_writers() cannot be called here directly, as it calls into the pure-virtual
182 * release_writer() function. By the time this destructor is called, the subclass that implements
183 * that pure-virtual function is already destructed. */
185 writers_.is_empty(),
186 "release_writers() should be called before the AbstractHierarchyIterator goes out of scope");
187}
188
190{
191 export_graph_construct();
192 connect_loose_objects();
193 export_graph_prune();
194 determine_export_paths(HierarchyContext::root());
195 determine_duplication_references(HierarchyContext::root(), "");
196 make_writers(HierarchyContext::root());
197 export_graph_clear();
198}
199
201{
202 for (AbstractHierarchyWriter *writer : writers_.values()) {
203 release_writer(writer);
204 }
205 writers_.clear();
206}
207
209{
210 export_subset_ = export_subset;
211}
212
213std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const
214{
215 return name;
216}
217
218std::string AbstractHierarchyIterator::get_id_name(const ID *id) const
219{
220 if (id == nullptr) {
221 return "";
222 }
223
224 return make_valid_name(std::string(id->name + 2));
225}
226
227std::string AbstractHierarchyIterator::make_unique_name(const std::string &original_name,
228 Set<std::string> &used_names)
229{
230 if (original_name.empty()) {
231 return "";
232 }
233
234 std::string name = BLI_uniquename_cb(
235 [&](const StringRef check_name) { return used_names.contains_as(check_name); },
236 '_',
237 make_valid_name(original_name));
238
239 used_names.add_new(name);
240 return name;
241}
242
244{
245 BLI_assert(!context->export_path.empty());
246 BLI_assert(context->object->data);
247
248 return path_concatenate(context->export_path, get_object_data_name(context->object));
249}
250
251void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const
252{
253 size_t total_graph_size = 0;
254 for (const auto item : graph.items()) {
255 const ObjectIdentifier &parent_info = item.key;
256 const Object *const export_parent = parent_info.object;
257 const Object *const duplicator = parent_info.duplicated_by;
258
259 if (duplicator != nullptr) {
260 fmt::println(" DU {} (as dupped by {}):",
261 export_parent == nullptr ? "-null-" : (export_parent->id.name + 2),
262 duplicator->id.name + 2);
263 }
264 else {
265 fmt::println(" OB {}:",
266 export_parent == nullptr ? "-null-" : (export_parent->id.name + 2));
267 }
268
269 total_graph_size += item.value.size();
270 for (HierarchyContext *child_ctx : item.value) {
271 if (child_ctx->duplicator == nullptr) {
272 fmt::println(" - {}{}{}",
273 child_ctx->export_name.c_str(),
274 child_ctx->weak_export ? " (weak)" : "",
275 child_ctx->original_export_path.empty() ?
276 "" :
277 (std::string("ref ") + child_ctx->original_export_path).c_str());
278 }
279 else {
280 fmt::println(" - {} (dup by {}{}) {}",
281 child_ctx->export_name.c_str(),
282 child_ctx->duplicator->id.name + 2,
283 child_ctx->weak_export ? ", weak" : "",
284 child_ctx->original_export_path.empty() ?
285 "" :
286 (std::string("ref ") + child_ctx->original_export_path).c_str());
287 }
288 }
289 }
290 fmt::println(" (Total graph size: {} objects)", total_graph_size);
291}
292
293void AbstractHierarchyIterator::export_graph_construct()
294{
296
297 /* Add a "null" root node with no children immediately for the case where the top-most node in
298 * the scene is not being exported and a root node otherwise wouldn't get added. */
299 ObjectIdentifier root_node_id = ObjectIdentifier::for_real_object(nullptr);
300 export_graph_.add_new(root_node_id, {});
301
302 DEGObjectIterSettings deg_iter_settings{};
303 deg_iter_settings.depsgraph = depsgraph_;
304 deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
306 DupliList duplilist;
307 DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, object) {
308 /* Non-instanced objects always have their object-parent as export-parent. */
309 const bool weak_export = mark_as_weak_export(object);
310 visit_object(object, object->parent, weak_export);
311
312 if (weak_export) {
313 /* If a duplicator shouldn't be exported, its duplilist also shouldn't be. */
314 continue;
315 }
316
317 /* Export the duplicated objects instanced by this object. */
318 object_duplilist(depsgraph_, scene, object, nullptr, duplilist);
319 if (!duplilist.is_empty()) {
320 DupliParentFinder dupli_parent_finder;
321
322 for (const DupliObject &dupli_object : duplilist) {
323 if (!should_visit_dupli_object(&dupli_object)) {
324 continue;
325 }
326 dupli_parent_finder.insert(&dupli_object);
327 }
328
329 for (const DupliObject &dupli_object : duplilist) {
330 if (!should_visit_dupli_object(&dupli_object)) {
331 continue;
332 }
333 visit_dupli_object(&dupli_object, object, dupli_parent_finder);
334 }
335 }
336
337 duplilist.clear();
338 }
340}
341
342void AbstractHierarchyIterator::connect_loose_objects()
343{
344 /* Find those objects whose parent is not part of the export graph; these
345 * objects would be skipped when traversing the graph as a hierarchy.
346 * These objects will have to be re-attached to some parent object in order to
347 * fit into the hierarchy. */
348 ExportGraph loose_objects_graph = export_graph_;
349 for (const ExportChildren &children : export_graph_.values()) {
350 for (const HierarchyContext *child : children) {
351 /* An object that is marked as a child of another object is not considered 'loose'. */
352 ObjectIdentifier child_oid = ObjectIdentifier::for_hierarchy_context(child);
353 loose_objects_graph.remove(child_oid);
354 }
355 }
356 /* The root of the hierarchy is always found, so it's never considered 'loose'. */
357 loose_objects_graph.remove_contained(ObjectIdentifier::for_graph_root());
358
359 /* Iterate over the loose objects and connect them to their export parent. */
360 for (const ObjectIdentifier &graph_key : loose_objects_graph.keys()) {
361 Object *object = graph_key.object;
362
363 while (true) {
364 /* Loose objects will all be real objects, as duplicated objects always have
365 * their duplicator or other exported duplicated object as ancestor. */
366
367 const bool found = export_graph_.contains(ObjectIdentifier::for_real_object(object->parent));
368 visit_object(object, object->parent, true);
369 if (found) {
370 break;
371 }
372 /* 'object->parent' will never be nullptr here, as the export graph contains the
373 * root as nullptr and thus will cause a break above. */
374 BLI_assert(object->parent != nullptr);
375
376 object = object->parent;
377 }
378 }
379}
380
381static bool remove_weak_subtrees(const HierarchyContext *context,
384{
385 bool all_is_weak = context != nullptr && context->weak_export;
387
388 const AbstractHierarchyIterator::ExportChildren *children = input_graph.lookup_ptr(map_key);
389 if (children) {
390 for (HierarchyContext *child_context : *children) {
391 bool child_tree_is_weak = remove_weak_subtrees(child_context, clean_graph, input_graph);
392 all_is_weak &= child_tree_is_weak;
393
394 if (child_tree_is_weak) {
395 /* This subtree is all weak, so we can remove it from the current object's children. */
396 clean_graph.lookup(map_key).remove(child_context);
397 delete child_context;
398 }
399 }
400 }
401
402 if (all_is_weak) {
403 /* This node and all its children are weak, so it can be removed from the export graph. */
404 clean_graph.remove(map_key);
405 }
406
407 return all_is_weak;
408}
409
410void AbstractHierarchyIterator::export_graph_prune()
411{
412 /* Take a copy of the map so that we can modify while recusing. */
413 ExportGraph unpruned_export_graph = export_graph_;
415}
416
417void AbstractHierarchyIterator::export_graph_clear()
418{
419 for (const ExportChildren &children : export_graph_.values()) {
420 for (HierarchyContext *context : children) {
421 delete context;
422 }
423 }
424 export_graph_.clear();
425 used_names_.clear_and_keep_capacity();
426}
427
428void AbstractHierarchyIterator::visit_object(Object *object,
429 Object *export_parent,
430 bool weak_export)
431{
432 HierarchyContext *context = new HierarchyContext();
433 context->object = object;
434 context->is_object_data_context = false;
435 context->export_name = get_object_name(object, export_parent);
436 context->export_parent = export_parent;
437 context->duplicator = nullptr;
438 context->weak_export = weak_export;
439 context->animation_check_include_parent = false;
440 context->export_path = "";
441 context->original_export_path = "";
442 context->higher_up_export_path = "";
443 context->is_duplisource = false;
444
445 copy_m4_m4(context->matrix_world, object->object_to_world().ptr());
446
447 ObjectIdentifier graph_index = determine_graph_index_object(context);
448 context_update_for_graph_index(context, graph_index);
449
450 /* Store this HierarchyContext as child of the export parent. */
451 export_graph_.lookup_or_add(graph_index, {}).add_new(context);
452
453 /* Create an empty entry for this object to indicate it is part of the export. This will be used
454 * by connect_loose_objects(). Having such an "indicator" will make it possible to do an O(log n)
455 * check on whether an object is part of the export, rather than having to check all objects in
456 * the map. Note that it's not possible to simply search for (object->parent, nullptr), as the
457 * object's parent in Blender may not be the same as its export-parent. */
458 ObjectIdentifier object_key = ObjectIdentifier::for_real_object(object);
459 export_graph_.add(object_key, {});
460}
461
467
468void AbstractHierarchyIterator::visit_dupli_object(const DupliObject *dupli_object,
469 Object *duplicator,
470 const DupliParentFinder &dupli_parent_finder)
471{
472 HierarchyContext *context = new HierarchyContext();
473 context->object = dupli_object->ob;
474 context->is_object_data_context = false;
475 context->duplicator = duplicator;
476 context->persistent_id = PersistentID(dupli_object);
477 context->weak_export = false;
478 context->export_path = "";
479 context->original_export_path = "";
480 context->animation_check_include_parent = false;
481 context->is_duplisource = false;
482
483 copy_m4_m4(context->matrix_world, dupli_object->mat);
484
485 /* Construct export name for the dupli-instance. */
486 std::string export_name = get_object_name(context->object) + "-" +
487 context->persistent_id.as_object_name_suffix();
488
489 Set<std::string> &used_names = used_names_.lookup_or_add(duplicator->id.name, {});
490 context->export_name = make_unique_name(make_valid_name(export_name), used_names);
491
493 context, dupli_object, dupli_parent_finder);
494 context_update_for_graph_index(context, graph_index);
495
496 export_graph_.lookup_or_add(graph_index, {}).add_new(context);
497
498 if (dupli_object->ob) {
499 this->duplisources_.add(&dupli_object->ob->id);
500 }
501}
502
504 const HierarchyContext *context,
505 const DupliObject *dupli_object,
506 const DupliParentFinder &dupli_parent_finder)
507{
508 const DupliObject *dupli_parent = dupli_parent_finder.find_suitable_export_parent(dupli_object);
509
510 if (dupli_parent != nullptr) {
511 return ObjectIdentifier::for_duplicated_object(dupli_parent, context->duplicator);
512 }
513 return ObjectIdentifier::for_real_object(context->duplicator);
514}
515
516void AbstractHierarchyIterator::context_update_for_graph_index(
517 HierarchyContext *context, const ObjectIdentifier &graph_index) const
518{
519 /* Update the HierarchyContext so that it is consistent with the graph index. */
520 context->export_parent = graph_index.object;
521
522 /* If the parent type is such that it cannot be exported (at least not currently to USD or
523 * Alembic), always check the parent for animation. */
524 const short partype = context->object->partype & PARTYPE;
525 context->animation_check_include_parent |= ELEM(partype, PARBONE, PARVERT1, PARVERT3, PARSKEL);
526
527 if (context->export_parent != context->object->parent) {
528 /* The parent object in Blender is NOT used as the export parent. This means
529 * that the world transform of this object can be influenced by objects that
530 * are not part of its export graph. */
531 context->animation_check_include_parent = true;
532 }
533}
534
536 const HierarchyContext *context)
537{
538 /* Note: `graph_children` is called during recursive iteration and MUST NOT change the export
539 * graph, which would invalidate the iteration. As a result, we cannot add an entry in the
540 * graph if the incoming `context` is not found. */
541 return export_graph_.lookup_ptr(ObjectIdentifier::for_hierarchy_context(context));
542}
543
544void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context)
545{
546 const std::string &parent_export_path = parent_context ? parent_context->export_path : "";
547
548 const ExportChildren *children = graph_children(parent_context);
549 if (!children) {
550 return;
551 }
552
553 for (HierarchyContext *context : *children) {
554 context->export_path = path_concatenate(parent_export_path, context->export_name);
555
556 if (context->duplicator == nullptr) {
557 /* This is an original (i.e. non-instanced) object, so we should keep track of where it was
558 * exported to, just in case it gets instanced somewhere. */
559 ID *source_ob = &context->object->id;
560 duplisource_export_path_.add(source_ob, context->export_path);
561
562 if (context->object->data != nullptr) {
563 ID *source_data = static_cast<ID *>(context->object->data);
565 }
566 }
567
568 determine_export_paths(context);
569 }
570}
571
572bool AbstractHierarchyIterator::determine_duplication_references(
573 const HierarchyContext *parent_context, const std::string &indent)
574{
575 const ExportChildren *children = graph_children(parent_context);
576 if (!children) {
577 return false;
578 }
579
580 /* Will be set to true if any child contexts are instances that were designated
581 * as proxies for the original prototype. */
582 bool contains_proxy_prototype = false;
583
584 for (HierarchyContext *context : *children) {
585 if (context->duplicator != nullptr) {
586 ID *source_id = &context->object->id;
587 const std::string *source_path = duplisource_export_path_.lookup_ptr(source_id);
588 if (!source_path) {
589 /* The original was not found, so mark this instance as "the original". */
590 context->mark_as_not_instanced();
591 duplisource_export_path_.add_new(source_id, context->export_path);
592 contains_proxy_prototype = true;
593 }
594 else {
595 context->mark_as_instance_of(*source_path);
596 }
597
598 if (context->object->data) {
599 ID *source_data_id = (ID *)context->object->data;
600 if (!duplisource_export_path_.contains(source_data_id)) {
601 /* The original was not found, so mark this instance as "original". */
602 std::string data_path = get_object_data_path(context);
603 context->mark_as_not_instanced();
604 duplisource_export_path_.add_overwrite(source_id, context->export_path);
605 duplisource_export_path_.add_new(source_data_id, data_path);
606 }
607 }
608 }
609 else {
610 /* Determine is this context is for an instance prototype. */
611 ID *id = &context->object->id;
612 if (duplisources_.contains(id)) {
613 context->is_duplisource = true;
614 }
615 }
616
617 if (determine_duplication_references(context, indent + " ")) {
618 /* A descendant was designated a prototype proxy. If the current context
619 * is an instance, we must change it to a prototype proxy as well. */
620 if (context->is_instance()) {
621 context->mark_as_not_instanced();
622 ID *source_id = &context->object->id;
623 duplisource_export_path_.add_overwrite(source_id, context->export_path);
624 }
625 contains_proxy_prototype = true;
626 }
627 }
628 return contains_proxy_prototype;
629}
630
631void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context)
632{
633 float parent_matrix_inv_world[4][4];
634
635 if (parent_context) {
636 invert_m4_m4(parent_matrix_inv_world, parent_context->matrix_world);
637 }
638 else {
639 unit_m4(parent_matrix_inv_world);
640 }
641
642 const ExportChildren *children = graph_children(parent_context);
643 if (!children) {
644 return;
645 }
646
647 bool has_point_instance_ancestor = false;
648 if (parent_context &&
649 (parent_context->is_point_instance || parent_context->has_point_instance_ancestor))
650 {
651 has_point_instance_ancestor = true;
652 }
653
654 for (HierarchyContext *context : *children) {
655 context->has_point_instance_ancestor = has_point_instance_ancestor;
656
657 /* Update the context so that it is correct for this parent-child relation. */
658 copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world);
659 if (parent_context != nullptr) {
660 context->higher_up_export_path = parent_context->export_path;
661 }
662
663 /* Get or create the transform writer. */
664 EnsuredWriter transform_writer = ensure_writer(
666
667 if (!transform_writer) {
668 /* Unable to export, so there is nothing to attach any children to; just abort this entire
669 * branch of the export hierarchy. */
670 continue;
671 }
672
673 const bool need_writers = context->is_point_proto || (!context->is_point_instance &&
674 !context->has_point_instance_ancestor);
675
677 if ((transform_writer.is_newly_created() || export_subset_.transforms) && need_writers) {
678 /* XXX This can lead to too many XForms being written. For example, a camera writer can
679 * refuse to write an orthographic camera. By the time that this is known, the XForm has
680 * already been written. */
681 transform_writer->write(*context);
682 }
683
684 if (!context->weak_export && include_data_writers(context) && need_writers) {
685 make_writers_particle_systems(context);
686 make_writer_object_data(context);
687 }
688
689 if (include_child_writers(context)) {
690 /* Recurse into this object's children. */
691 make_writers(context);
692 }
693 }
694
695 /* TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something.
696 */
697}
698
699HierarchyContext AbstractHierarchyIterator::context_for_object_data(
700 const HierarchyContext *object_context) const
701{
702 HierarchyContext data_context = *object_context;
703 data_context.is_object_data_context = true;
704 data_context.higher_up_export_path = object_context->export_path;
705 data_context.export_name = get_object_data_name(data_context.object);
706 data_context.export_path = path_concatenate(data_context.higher_up_export_path,
707 data_context.export_name);
708
709 const ObjectIdentifier object_key = ObjectIdentifier::for_hierarchy_context(&data_context);
710 const ExportChildren *children = export_graph_.lookup_ptr(object_key);
711 data_context.is_parent = children ? (children->size() > 0) : false;
712
713 return data_context;
714}
715
716void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context)
717{
718 if (context->object->data == nullptr) {
719 return;
720 }
721
722 HierarchyContext data_context = context_for_object_data(context);
723 if (data_context.is_instance()) {
724 ID *object_data = static_cast<ID *>(context->object->data);
725 data_context.original_export_path = duplisource_export_path_.lookup(object_data);
726
727 /* If the object is marked as an instance, so should the object data. */
728 BLI_assert(data_context.is_instance());
729 }
730
731 /* Always write upon creation, otherwise depend on which subset is active. */
732 EnsuredWriter data_writer = ensure_writer(&data_context,
734 if (!data_writer) {
735 return;
736 }
737
738 if (data_writer.is_newly_created() || export_subset_.shapes) {
739 data_writer->write(data_context);
740 }
741}
742
743void AbstractHierarchyIterator::make_writers_particle_systems(
744 const HierarchyContext *transform_context)
745{
746 Object *object = transform_context->object;
747 ParticleSystem *psys = static_cast<ParticleSystem *>(object->particlesystem.first);
748 for (; psys; psys = psys->next) {
749 if (!psys_check_enabled(object, psys, true)) {
750 continue;
751 }
752
753 HierarchyContext hair_context = *transform_context;
754 hair_context.export_name = make_valid_name(psys->name);
755 hair_context.export_path = path_concatenate(transform_context->export_path,
756 hair_context.export_name);
757 hair_context.higher_up_export_path = transform_context->export_path;
758 hair_context.particle_system = psys;
759
760 EnsuredWriter writer;
761 switch (psys->part->type) {
762 case PART_HAIR:
763 writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer);
764 break;
765 case PART_EMITTER:
766 case PART_FLUID_FLIP:
767 case PART_FLUID_SPRAY:
769 case PART_FLUID_FOAM:
775 writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer);
776 break;
777 }
778 if (!writer) {
779 continue;
780 }
781
782 /* Always write upon creation, otherwise depend on which subset is active. */
783 if (writer.is_newly_created() || export_subset_.shapes) {
784 writer->write(hair_context);
785 }
786 }
787}
788
789std::string AbstractHierarchyIterator::get_object_name(const Object *object) const
790{
791 return get_id_name(&object->id);
792}
793
794std::string AbstractHierarchyIterator::get_object_name(const Object *object, const Object *parent)
795{
796 Set<std::string> &used_names = used_names_.lookup_or_add(parent ? parent->id.name : "", {});
797 return make_unique_name(object->id.name + 2, used_names);
798}
799
800std::string AbstractHierarchyIterator::get_object_data_name(const Object *object) const
801{
802 const ID *object_data = static_cast<ID *>(object->data);
803 return get_id_name(object_data);
804}
805
807 const std::string &export_path) const
808{
809 return writers_.lookup_default(export_path, nullptr);
810}
811
812EnsuredWriter AbstractHierarchyIterator::ensure_writer(
813 const HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func)
814{
815 AbstractHierarchyWriter *writer = get_writer(context->export_path);
816 if (writer != nullptr) {
817 return EnsuredWriter::existing(writer);
818 }
819
820 writer = (this->*create_func)(context);
821 if (writer == nullptr) {
822 return EnsuredWriter::empty();
823 }
824
825 writers_.add_new(context->export_path, writer);
826 return EnsuredWriter::newly_created(writer);
827}
828
829std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path,
830 const std::string &child_path) const
831{
832 return parent_path + "/" + child_path;
833}
834
836{
837 return false;
838}
840{
841 /* Do not visit dupli objects if their `no_draw` flag is set (things like custom bone shapes) or
842 * if they are meta-balls / text objects. */
843 if (dupli_object->no_draw || ELEM(dupli_object->ob->type, OB_MBALL, OB_FONT)) {
844 return false;
845 }
846
847 return true;
848}
849
850} // namespace blender::io
bool BKE_animdata_id_is_animated(const ID *id)
Definition anim_data.cc:238
blender::VectorList< DupliObject > DupliList
void object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob, blender::Set< const Object * > *include_objects, DupliList &r_duplilist)
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1791
General operations, lookup, etc. for blender objects.
@ OB_VISIBLE_SELF
int BKE_object_visibility(const Object *ob, int dag_eval_mode)
bool psys_check_enabled(struct Object *ob, struct ParticleSystem *psys, bool use_render_params)
Definition particle.cc:710
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void copy_m4_m4(float m1[4][4], const float m2[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void unit_m4(float m[4][4])
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
#define ELEM(...)
eEvaluationMode
#define DEG_OBJECT_ITER_BEGIN(settings_, instance_)
bool DEG_is_evaluated_id(const ID *id)
#define DEG_OBJECT_ITER_END
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
@ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY
@ DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET
ID and Library types, which are fundamental for SDNA.
struct ID ID
@ BASE_FROM_DUPLI
@ eModifierType_Subsurf
Object is a sort of wrapper for general info.
@ PARVERT1
@ PARSKEL
@ PARTYPE
@ PARVERT3
@ PARBONE
@ OB_MBALL
@ OB_EMPTY
@ OB_FONT
struct Object Object
@ 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
struct ParticleSystem ParticleSystem
Types and defines for representing Rigid Body entities.
@ RBO_TYPE_ACTIVE
@ RBO_FLAG_USE_DEFORM
struct Scene Scene
BPy_StructRNA * depsgraph
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
ValueIterator values() const &
Definition BLI_map.hh:884
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
bool remove(const Key &key)
Definition BLI_map.hh:368
Value & lookup_or_add(const Key &key, const Value &value)
Definition BLI_map.hh:588
bool contains_as(const ForwardKey &key) const
Definition BLI_set.hh:314
bool add(const Key &key)
Definition BLI_set.hh:248
void add_new(const Key &key)
Definition BLI_set.hh:233
virtual ObjectIdentifier determine_graph_index_object(const HierarchyContext *context)
virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const
virtual bool include_child_writers(const HierarchyContext *) const
virtual void release_writer(AbstractHierarchyWriter *writer)=0
ExportChildren * graph_children(const HierarchyContext *context)
virtual bool mark_as_weak_export(const Object *object) const
void set_export_subset(ExportSubset export_subset)
virtual std::string get_id_name(const ID *id) const
blender::Map< ObjectIdentifier, ExportChildren > ExportGraph
virtual AbstractHierarchyWriter * create_particle_writer(const HierarchyContext *context)=0
virtual AbstractHierarchyWriter * create_transform_writer(const HierarchyContext *context)=0
AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph)
virtual std::string make_unique_name(const std::string &original_name, Set< std::string > &used_names)
virtual std::string path_concatenate(const std::string &parent_path, const std::string &child_path) const
virtual ObjectIdentifier determine_graph_index_dupli(const HierarchyContext *context, const DupliObject *dupli_object, const DupliParentFinder &dupli_parent_finder)
AbstractHierarchyWriter * get_writer(const std::string &export_path) const
virtual AbstractHierarchyWriter * create_data_writer(const HierarchyContext *context)=0
virtual AbstractHierarchyWriter * create_hair_writer(const HierarchyContext *context)=0
virtual std::string make_valid_name(const std::string &name) const
blender::Set< HierarchyContext * > ExportChildren
virtual std::string get_object_data_path(const HierarchyContext *context) const
virtual bool include_data_writers(const HierarchyContext *) 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)
GPU_SHADER_INTERFACE_INFO(depth_2d_update_iface).smooth(Type fragColor push_constant(Type::float2_t, "extent") .push_constant(Type source_data
GeometrySet object_get_evaluated_geometry_set(const Object &object, bool apply_subdiv=true)
int context(const bContext *C, const char *member, bContextDataResult *result)
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:157
const char * name
float mat[4][4]
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
struct ModifierData * next
struct Object * parent
ParticleSettings * part
struct ParticleSystem * next
bool is_object_visible(enum eEvaluationMode evaluation_mode) const
static const HierarchyContext * root()
void mark_as_instance_of(const std::string &reference_export_path)