Blender V4.5
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"
14#include "BKE_key.hh"
15#include "BKE_modifier.hh"
17#include "BKE_object.hh"
18#include "BKE_particle.h"
19
20#include "BLI_assert.h"
21#include "BLI_listbase.h"
22#include "BLI_math_matrix.h"
23#include "BLI_set.hh"
24#include "BLI_string_utils.hh"
25
26#include "DNA_ID.h"
27#include "DNA_layer_types.h"
28#include "DNA_modifier_types.h"
29#include "DNA_node_types.h"
30#include "DNA_object_types.h"
31#include "DNA_particle_types.h"
32#include "DNA_rigidbody_types.h"
33
35
36namespace blender::io {
37
39{
40 return nullptr;
41}
42
44{
45 return !original_export_path.empty();
46}
47void HierarchyContext::mark_as_instance_of(const std::string &reference_export_path)
48{
49 original_export_path = reference_export_path;
50}
55
57{
58 /* The context is for a prototype if it's for a duplisource or
59 * for a duplicated object that was designated to be a prototype
60 * because the original was not included in the export.*/
61 return is_duplisource || (duplicator != nullptr && !is_instance());
62}
63
64bool 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
87EnsuredWriter::EnsuredWriter() : writer_(nullptr), newly_created_(false) {}
88
90 : writer_(writer), newly_created_(newly_created)
91{
92}
93
94EnsuredWriter EnsuredWriter::empty()
95{
96 return EnsuredWriter(nullptr, false);
97}
99{
100 return EnsuredWriter(writer, false);
101}
103{
104 return EnsuredWriter(writer, true);
105}
106
108{
109 return newly_created_;
110}
111
112EnsuredWriter::operator bool() const
113{
114 return writer_ != nullptr;
115}
116
118{
119 return writer_;
120}
121
123{
124 Object *object = context.object;
125
126 if (BKE_animdata_id_is_animated(static_cast<ID *>(object->data))) {
127 return true;
128 }
129 if (BKE_key_from_object(object) != nullptr) {
130 return true;
131 }
132 if (check_has_deforming_physics(context)) {
133 return true;
134 }
135
136 /* Test modifiers. */
137 /* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on
138 * time. */
139 ModifierData *md = static_cast<ModifierData *>(object->modifiers.first);
140 while (md) {
141 if (md->type != eModifierType_Subsurf) {
142 return true;
143 }
144 md = md->next;
145 }
146
147 return false;
148}
149
151{
152 const RigidBodyOb *rbo = context.object->rigidbody_object;
153 return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE;
154}
155
157{
158 const RigidBodyOb *rbo = context.object->rigidbody_object;
159 return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0;
160}
161
163{
164 if (!object) {
165 return false;
166 }
167
168 /* Collection instancers are handled elsewhere as part of Scene instancing. */
169 if (object->type == OB_EMPTY && object->instance_collection != nullptr) {
170 return false;
171 }
172
173 const bke::GeometrySet geometry_set = bke::object_get_evaluated_geometry_set(*object);
174 return geometry_set.has_instances();
175}
176
181
183{
184 /* release_writers() cannot be called here directly, as it calls into the pure-virtual
185 * release_writer() function. By the time this destructor is called, the subclass that implements
186 * that pure-virtual function is already destructed. */
188 writers_.is_empty(),
189 "release_writers() should be called before the AbstractHierarchyIterator goes out of scope");
190}
191
193{
194 export_graph_construct();
195 connect_loose_objects();
196 export_graph_prune();
197 determine_export_paths(HierarchyContext::root());
198 determine_duplication_references(HierarchyContext::root(), "");
199 make_writers(HierarchyContext::root());
200 export_graph_clear();
201}
202
204{
205 for (AbstractHierarchyWriter *writer : writers_.values()) {
206 release_writer(writer);
207 }
208 writers_.clear();
209}
210
212{
213 export_subset_ = export_subset;
214}
215
216std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const
217{
218 return name;
219}
220
221std::string AbstractHierarchyIterator::get_id_name(const ID *id) const
222{
223 if (id == nullptr) {
224 return "";
225 }
226
227 return make_valid_name(std::string(id->name + 2));
228}
229
230std::string AbstractHierarchyIterator::make_unique_name(const std::string &original_name,
231 Set<std::string> &used_names)
232{
233 if (original_name.empty()) {
234 return "";
235 }
236
237 std::string name = BLI_uniquename_cb(
238 [&](const StringRef check_name) { return used_names.contains_as(check_name); },
239 '_',
240 make_valid_name(original_name));
241
242 used_names.add_new(name);
243 return name;
244}
245
247{
248 BLI_assert(!context->export_path.empty());
249 BLI_assert(context->object->data);
250
251 return path_concatenate(context->export_path, get_object_data_name(context->object));
252}
253
254void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const
255{
256 size_t total_graph_size = 0;
257 for (const auto item : graph.items()) {
258 const ObjectIdentifier &parent_info = item.key;
259 const Object *const export_parent = parent_info.object;
260 const Object *const duplicator = parent_info.duplicated_by;
261
262 if (duplicator != nullptr) {
263 fmt::println(" DU {} (as dupped by {}):",
264 export_parent == nullptr ? "-null-" : (export_parent->id.name + 2),
265 duplicator->id.name + 2);
266 }
267 else {
268 fmt::println(" OB {}:",
269 export_parent == nullptr ? "-null-" : (export_parent->id.name + 2));
270 }
271
272 total_graph_size += item.value.size();
273 for (HierarchyContext *child_ctx : item.value) {
274 if (child_ctx->duplicator == nullptr) {
275 fmt::println(" - {}{}{}",
276 child_ctx->export_name.c_str(),
277 child_ctx->weak_export ? " (weak)" : "",
278 child_ctx->original_export_path.empty() ?
279 "" :
280 (std::string("ref ") + child_ctx->original_export_path).c_str());
281 }
282 else {
283 fmt::println(" - {} (dup by {}{}) {}",
284 child_ctx->export_name.c_str(),
285 child_ctx->duplicator->id.name + 2,
286 child_ctx->weak_export ? ", weak" : "",
287 child_ctx->original_export_path.empty() ?
288 "" :
289 (std::string("ref ") + child_ctx->original_export_path).c_str());
290 }
291 }
292 }
293 fmt::println(" (Total graph size: {} objects)", total_graph_size);
294}
295
296void AbstractHierarchyIterator::export_graph_construct()
297{
299
300 /* Add a "null" root node with no children immediately for the case where the top-most node in
301 * the scene is not being exported and a root node otherwise wouldn't get added. */
302 ObjectIdentifier root_node_id = ObjectIdentifier::for_real_object(nullptr);
303 export_graph_.add_new(root_node_id, {});
304
305 DEGObjectIterSettings deg_iter_settings{};
306 deg_iter_settings.depsgraph = depsgraph_;
307 deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
309 DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, object) {
310 /* Non-instanced objects always have their object-parent as export-parent. */
311 const bool weak_export = mark_as_weak_export(object);
312 visit_object(object, object->parent, weak_export);
313
314 if (weak_export) {
315 /* If a duplicator shouldn't be exported, its duplilist also shouldn't be. */
316 continue;
317 }
318
319 /* Export the duplicated objects instanced by this object. */
320 ListBase *lb = object_duplilist(depsgraph_, scene, object);
321 if (lb) {
322 DupliParentFinder dupli_parent_finder;
323
324 LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
325 PersistentID persistent_id(dupli_object);
326 if (!should_visit_dupli_object(dupli_object)) {
327 continue;
328 }
329 dupli_parent_finder.insert(dupli_object);
330 }
331
332 LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
333 if (!should_visit_dupli_object(dupli_object)) {
334 continue;
335 }
336 visit_dupli_object(dupli_object, object, dupli_parent_finder);
337 }
338 }
339
341 }
343}
344
345void AbstractHierarchyIterator::connect_loose_objects()
346{
347 /* Find those objects whose parent is not part of the export graph; these
348 * objects would be skipped when traversing the graph as a hierarchy.
349 * These objects will have to be re-attached to some parent object in order to
350 * fit into the hierarchy. */
351 ExportGraph loose_objects_graph = export_graph_;
352 for (const ExportChildren &children : export_graph_.values()) {
353 for (const HierarchyContext *child : children) {
354 /* An object that is marked as a child of another object is not considered 'loose'. */
355 ObjectIdentifier child_oid = ObjectIdentifier::for_hierarchy_context(child);
356 loose_objects_graph.remove(child_oid);
357 }
358 }
359 /* The root of the hierarchy is always found, so it's never considered 'loose'. */
360 loose_objects_graph.remove_contained(ObjectIdentifier::for_graph_root());
361
362 /* Iterate over the loose objects and connect them to their export parent. */
363 for (const ObjectIdentifier &graph_key : loose_objects_graph.keys()) {
364 Object *object = graph_key.object;
365
366 while (true) {
367 /* Loose objects will all be real objects, as duplicated objects always have
368 * their duplicator or other exported duplicated object as ancestor. */
369
370 const bool found = export_graph_.contains(ObjectIdentifier::for_real_object(object->parent));
371 visit_object(object, object->parent, true);
372 if (found) {
373 break;
374 }
375 /* 'object->parent' will never be nullptr here, as the export graph contains the
376 * root as nullptr and thus will cause a break above. */
377 BLI_assert(object->parent != nullptr);
378
379 object = object->parent;
380 }
381 }
382}
383
384static bool remove_weak_subtrees(const HierarchyContext *context,
387{
388 bool all_is_weak = context != nullptr && context->weak_export;
390
391 const AbstractHierarchyIterator::ExportChildren *children = input_graph.lookup_ptr(map_key);
392 if (children) {
393 for (HierarchyContext *child_context : *children) {
394 bool child_tree_is_weak = remove_weak_subtrees(child_context, clean_graph, input_graph);
395 all_is_weak &= child_tree_is_weak;
396
397 if (child_tree_is_weak) {
398 /* This subtree is all weak, so we can remove it from the current object's children. */
399 clean_graph.lookup(map_key).remove(child_context);
400 delete child_context;
401 }
402 }
403 }
404
405 if (all_is_weak) {
406 /* This node and all its children are weak, so it can be removed from the export graph. */
407 clean_graph.remove(map_key);
408 }
409
410 return all_is_weak;
411}
412
413void AbstractHierarchyIterator::export_graph_prune()
414{
415 /* Take a copy of the map so that we can modify while recusing. */
416 ExportGraph unpruned_export_graph = export_graph_;
418}
419
420void AbstractHierarchyIterator::export_graph_clear()
421{
422 for (const ExportChildren &children : export_graph_.values()) {
423 for (HierarchyContext *context : children) {
424 delete context;
425 }
426 }
427 export_graph_.clear();
428 used_names_.clear_and_keep_capacity();
429}
430
431void AbstractHierarchyIterator::visit_object(Object *object,
432 Object *export_parent,
433 bool weak_export)
434{
435 HierarchyContext *context = new HierarchyContext();
436 context->object = object;
437 context->is_object_data_context = false;
438 context->export_name = get_object_name(object, export_parent);
439 context->export_parent = export_parent;
440 context->duplicator = nullptr;
441 context->weak_export = weak_export;
442 context->animation_check_include_parent = false;
443 context->export_path = "";
444 context->original_export_path = "";
445 context->higher_up_export_path = "";
446 context->is_duplisource = false;
447
448 copy_m4_m4(context->matrix_world, object->object_to_world().ptr());
449
450 ObjectIdentifier graph_index = determine_graph_index_object(context);
451 context_update_for_graph_index(context, graph_index);
452
453 /* Store this HierarchyContext as child of the export parent. */
454 export_graph_.lookup_or_add(graph_index, {}).add_new(context);
455
456 /* Create an empty entry for this object to indicate it is part of the export. This will be used
457 * by connect_loose_objects(). Having such an "indicator" will make it possible to do an O(log n)
458 * check on whether an object is part of the export, rather than having to check all objects in
459 * the map. Note that it's not possible to simply search for (object->parent, nullptr), as the
460 * object's parent in Blender may not be the same as its export-parent. */
461 ObjectIdentifier object_key = ObjectIdentifier::for_real_object(object);
462 export_graph_.add(object_key, {});
463}
464
470
471void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object,
472 Object *duplicator,
473 const DupliParentFinder &dupli_parent_finder)
474{
475 HierarchyContext *context = new HierarchyContext();
476 context->object = dupli_object->ob;
477 context->is_object_data_context = false;
478 context->duplicator = duplicator;
479 context->persistent_id = PersistentID(dupli_object);
480 context->weak_export = false;
481 context->export_path = "";
482 context->original_export_path = "";
483 context->animation_check_include_parent = false;
484 context->is_duplisource = false;
485
486 copy_m4_m4(context->matrix_world, dupli_object->mat);
487
488 /* Construct export name for the dupli-instance. */
489 std::string export_name = get_object_name(context->object) + "-" +
490 context->persistent_id.as_object_name_suffix();
491
492 Set<std::string> &used_names = used_names_.lookup_or_add(duplicator->id.name, {});
493 context->export_name = make_unique_name(make_valid_name(export_name), used_names);
494
496 context, dupli_object, dupli_parent_finder);
497 context_update_for_graph_index(context, graph_index);
498
499 export_graph_.lookup_or_add(graph_index, {}).add_new(context);
500
501 if (dupli_object->ob) {
502 this->duplisources_.add(&dupli_object->ob->id);
503 }
504}
505
507 const HierarchyContext *context,
508 const DupliObject *dupli_object,
509 const DupliParentFinder &dupli_parent_finder)
510{
511 const DupliObject *dupli_parent = dupli_parent_finder.find_suitable_export_parent(dupli_object);
512
513 if (dupli_parent != nullptr) {
514 return ObjectIdentifier::for_duplicated_object(dupli_parent, context->duplicator);
515 }
516 return ObjectIdentifier::for_real_object(context->duplicator);
517}
518
519void AbstractHierarchyIterator::context_update_for_graph_index(
520 HierarchyContext *context, const ObjectIdentifier &graph_index) const
521{
522 /* Update the HierarchyContext so that it is consistent with the graph index. */
523 context->export_parent = graph_index.object;
524
525 /* If the parent type is such that it cannot be exported (at least not currently to USD or
526 * Alembic), always check the parent for animation. */
527 const short partype = context->object->partype & PARTYPE;
528 context->animation_check_include_parent |= ELEM(partype, PARBONE, PARVERT1, PARVERT3, PARSKEL);
529
530 if (context->export_parent != context->object->parent) {
531 /* The parent object in Blender is NOT used as the export parent. This means
532 * that the world transform of this object can be influenced by objects that
533 * are not part of its export graph. */
534 context->animation_check_include_parent = true;
535 }
536}
537
539 const HierarchyContext *context)
540{
541 /* Note: `graph_children` is called during recursive iteration and MUST NOT change the export
542 * graph, which would invalidate the iteration. As a result, we cannot add an entry in the
543 * graph if the incoming `context` is not found. */
544 return export_graph_.lookup_ptr(ObjectIdentifier::for_hierarchy_context(context));
545}
546
547void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context)
548{
549 const std::string &parent_export_path = parent_context ? parent_context->export_path : "";
550
551 const ExportChildren *children = graph_children(parent_context);
552 if (!children) {
553 return;
554 }
555
556 for (HierarchyContext *context : *children) {
557 context->export_path = path_concatenate(parent_export_path, context->export_name);
558
559 if (context->duplicator == nullptr) {
560 /* This is an original (i.e. non-instanced) object, so we should keep track of where it was
561 * exported to, just in case it gets instanced somewhere. */
562 ID *source_ob = &context->object->id;
563 duplisource_export_path_.add(source_ob, context->export_path);
564
565 if (context->object->data != nullptr) {
566 ID *source_data = static_cast<ID *>(context->object->data);
568 }
569 }
570
571 determine_export_paths(context);
572 }
573}
574
575bool AbstractHierarchyIterator::determine_duplication_references(
576 const HierarchyContext *parent_context, const std::string &indent)
577{
578 const ExportChildren *children = graph_children(parent_context);
579 if (!children) {
580 return false;
581 }
582
583 /* Will be set to true if any child contexts are instances that were designated
584 * as proxies for the original prototype.*/
585 bool contains_proxy_prototype = false;
586
587 for (HierarchyContext *context : *children) {
588 if (context->duplicator != nullptr) {
589 ID *source_id = &context->object->id;
590 const std::string *source_path = duplisource_export_path_.lookup_ptr(source_id);
591 if (!source_path) {
592 /* The original was not found, so mark this instance as "the original". */
593 context->mark_as_not_instanced();
594 duplisource_export_path_.add_new(source_id, context->export_path);
595 contains_proxy_prototype = true;
596 }
597 else {
598 context->mark_as_instance_of(*source_path);
599 }
600
601 if (context->object->data) {
602 ID *source_data_id = (ID *)context->object->data;
603 if (!duplisource_export_path_.contains(source_data_id)) {
604 /* The original was not found, so mark this instance as "original". */
605 std::string data_path = get_object_data_path(context);
606 context->mark_as_not_instanced();
607 duplisource_export_path_.add_overwrite(source_id, context->export_path);
608 duplisource_export_path_.add_new(source_data_id, data_path);
609 }
610 }
611 }
612 else {
613 /* Determine is this context is for an instance prototype. */
614 ID *id = &context->object->id;
615 if (duplisources_.contains(id)) {
616 context->is_duplisource = true;
617 }
618 }
619
620 if (determine_duplication_references(context, indent + " ")) {
621 /* A descendant was designated a prototype proxy. If the current context
622 * is an instance, we must change it to a prototype proxy as well. */
623 if (context->is_instance()) {
624 context->mark_as_not_instanced();
625 ID *source_id = &context->object->id;
626 duplisource_export_path_.add_overwrite(source_id, context->export_path);
627 }
628 contains_proxy_prototype = true;
629 }
630 }
631 return contains_proxy_prototype;
632}
633
634void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context)
635{
636 float parent_matrix_inv_world[4][4];
637
638 if (parent_context) {
639 invert_m4_m4(parent_matrix_inv_world, parent_context->matrix_world);
640 }
641 else {
642 unit_m4(parent_matrix_inv_world);
643 }
644
645 const ExportChildren *children = graph_children(parent_context);
646 if (!children) {
647 return;
648 }
649
650 for (HierarchyContext *context : *children) {
651 if (parent_context) {
652 if (parent_context->is_point_instance || parent_context->has_point_instance_ancestor) {
653 context->has_point_instance_ancestor = true;
654 }
655 }
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 return;
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)
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 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 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:237
void free_object_duplilist(ListBase *lb)
ListBase * object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob, blender::Set< const Object * > *include_objects=nullptr)
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1824
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:709
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
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
struct ListBase ListBase
@ eModifierType_Subsurf
Object is a sort of wrapper for general info.
@ OB_MBALL
@ OB_EMPTY
@ OB_FONT
struct Object Object
@ PARVERT1
@ PARSKEL
@ PARTYPE
@ PARVERT3
@ PARBONE
@ 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
float mat[4][4]
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
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)