Blender V5.0
IO_abstract_hierarchy_iterator.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5/*
6 * This file contains the AbstractHierarchyIterator. It is intended for exporters for file
7 * formats that concern an entire hierarchy of objects (rather than, for example, an OBJ file that
8 * contains only a single mesh). Examples are Universal Scene Description (USD) and Alembic.
9 * AbstractHierarchyIterator is intended to be subclassed to support concrete file formats.
10 *
11 * The AbstractHierarchyIterator makes a distinction between the actual object hierarchy and the
12 * export hierarchy. The former is the parent/child structure in Blender, which can have multiple
13 * parent-like objects. For example, a duplicated object can have both a duplicator and a parent,
14 * both determining the final transform. The export hierarchy is the hierarchy as written to the
15 * file, and every object has only one export-parent.
16 *
17 * Currently the AbstractHierarchyIterator does not make any decisions about *what* to export.
18 * Selections like "selected only" or "no hair systems" are left to concrete subclasses.
19 */
20
21#pragma once
22
24
25#include "BLI_hash.hh"
26#include "BLI_map.hh"
27#include "BLI_set.hh"
28
29#include "DEG_depsgraph.hh"
30
31#include <string>
32
33struct Depsgraph;
34struct DupliObject;
35struct ID;
36struct Main;
37struct Object;
38struct ParticleSystem;
39
40namespace blender::io {
41
44
45/* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext
46 * struct contains everything necessary to export a single object to a file. */
48 /*********** Determined during hierarchy iteration: ***************/
49 Object *object; /* Evaluated object. */
53 float matrix_world[4][4];
54 std::string export_name;
55
56 /* When weak_export=true, the object will be exported only as transform, and only if is an
57 * ancestor of an object with weak_export=false.
58 *
59 * In other words: when weak_export=true but this object has no children, or all descendants also
60 * have weak_export=true, this object (and by recursive reasoning all its descendants) will be
61 * excluded from the export.
62 *
63 * The export hierarchy is kept as close to the hierarchy in Blender as possible. As such, an
64 * object that serves as a parent for another object, but which should NOT be exported itself, is
65 * exported only as transform (i.e. as empty). This happens with objects that are invisible when
66 * exporting with "Visible Only" enabled, for example. */
68
69 /* When true, this object should check its parents for animation data when determining whether
70 * it's animated. This is necessary when a parent object in Blender is not part of the export. */
72
73 /* The flag makes unambiguous the fact that the current context targets object or data. This is
74 * notably used in USDHierarchyIterator::create_usd_export_context: options like
75 * merge_parent_xform option is meaningless for object, it only makes sense for data. */
77
78 /* This flag tells, within a object data context, if an object is the parent of other objects.
79 * This is useful when exporting UsdGeomGprim: those cannot be nested into each other. For
80 * example, an UsdGeomMesh cannot have other UsdGeomMesh as descendants and other hierarchy
81 * strategies need to be adopted.
82 */
84
85 /* When true this is duplisource object. This flag is used to identify instance prototypes. */
87
88 /* This flag tells whether an object is a valid point instance of other objects.
89 * If true, it means the object has a valid reference path and its value can be included
90 * in the instances data of UsdGeomPointInstancer. */
92
93 /* This flag tells if an object is a valid prototype of a point instancer. */
95
96 /* True if this context is a descendant of any context with is_point_instance set to true.
97 * This helps skip redundant instancing data during export. */
99
100 /*********** Determined during writer creation: ***************/
101 float parent_matrix_inv_world[4][4]; /* Inverse of the parent's world matrix. */
102 std::string export_path; /* Hierarchical path, such as "/grandparent/parent/object_name". */
103 ParticleSystem *particle_system; /* Only set for particle/hair writers. */
104
105 /* Hierarchical path of the object this object is duplicating; only set when this object should
106 * be stored as a reference to its original. It can happen that the original is not part of the
107 * exported objects, in which case this string is empty even though 'duplicator' is set. */
109
110 /* Export path of the higher-up exported data. For transforms, this is the export path of the
111 * parent object. For object data, this is the export path of that object's transform.
112 *
113 * From the exported file's point of view, this is the path to the parent in that file. The term
114 * "parent" is not used here to avoid confusion with Blender's meaning of the word (which always
115 * refers to a different object). */
117
118 /* Return a HierarchyContext representing the root of the export hierarchy. */
119 static const HierarchyContext *root();
120
121 /* For handling instanced collections, instances created by particles, etc. */
122 bool is_instance() const;
123 void mark_as_instance_of(const std::string &reference_export_path);
125 bool is_prototype() const;
126
127 /* For handling point instancing (Instance on Points geometry node). */
128 bool is_point_instancer() const;
129
130 bool is_object_visible(enum eEvaluationMode evaluation_mode) const;
131};
132
133/* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc.
134 *
135 * Instantiated by the AbstractHierarchyIterator on the first frame an object exists. Generally
136 * that's the first frame to be exported, but can be later, for example when objects are
137 * instantiated by particles. The AbstractHierarchyWriter::write() function is called on every
138 * frame the object exists in the dependency graph and should be exported.
139 */
141 public:
142 virtual ~AbstractHierarchyWriter() = default;
143 virtual void write(HierarchyContext &context) = 0;
144 /* TODO(Sybren): add function like absent() that's called when a writer was previously created,
145 * but wasn't used while exporting the current frame (for example, a particle-instanced mesh of
146 * which the particle is no longer alive). */
147 protected:
148 /* Return true if the data written by this writer changes over time.
149 * Note that this function assumes this is an object data writer. Transform writers should not
150 * call this but implement their own logic. */
151 virtual bool check_is_animated(const HierarchyContext &context) const;
152
153 /* Helper functions for animation checks. */
154 static bool check_has_physics(const HierarchyContext &context);
155 static bool check_has_deforming_physics(const HierarchyContext &context);
156};
157
158/* Determines which subset of the writers actually gets to write. */
160 bool transforms : 1;
161 bool shapes : 1;
162};
163
164/* EnsuredWriter represents an AbstractHierarchyWriter* combined with information whether it was
165 * newly created or not. It's returned by AbstractHierarchyIterator::ensure_writer(). */
166class EnsuredWriter {
167 private:
169
170 /* Is set to truth when ensure_writer() did not find existing writer and created a new one.
171 * Is set to false when writer has been re-used or when allocation of the new one has failed
172 * (`writer` will be `nullptr` in that case and bool(ensured_writer) will be false). */
173 bool newly_created_;
174
176
177 public:
179
180 static EnsuredWriter empty();
181 static EnsuredWriter existing(AbstractHierarchyWriter *writer);
182 static EnsuredWriter newly_created(AbstractHierarchyWriter *writer);
183
184 bool is_newly_created() const;
185
186 /* These operators make an EnsuredWriter* act as an AbstractHierarchyWriter* */
187 operator bool() const;
189};
190
191/* Unique identifier for a (potentially duplicated) object.
192 *
193 * Instances of this class serve as key in the export graph of the
194 * AbstractHierarchyIterator. */
196 public:
198 Object *duplicated_by; /* nullptr for real objects. */
200
201 protected:
203
204 public:
208 static ObjectIdentifier for_duplicated_object(const DupliObject *dupli_object,
210
211 bool is_root() const;
212
214 {
216 }
217};
218
219bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b);
220
221/* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export
222 * writers. These writers are then called to perform the actual writing to a USD or Alembic file.
223 *
224 * Dealing with file- and scene-level data (for example, creating a USD scene, setting the frame
225 * rate, etc.) is not part of the AbstractHierarchyIterator class structure, and should be done
226 * in separate code.
227 */
229 public:
230 /* Mapping from export path to writer. */
232 /* All the children of some object, as per the export hierarchy. */
234 /* Mapping from an object and its duplicator to the object's export-children. */
236 /* Mapping from ID to its export path. This is used for instancing; given an
237 * instanced datablock, the export path of the original can be looked up. */
239 /* Mapping from ID name to a set of names logically residing "under" it. Used for unique
240 * name generation. */
242 /* IDs of all duplisource objects, used to identify instance prototypes. */
244
245 protected:
249 Depsgraph *depsgraph_;
254
255 public:
256 explicit AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph);
258
259 /* Iterate over the depsgraph, create writers, and tell the writers to write.
260 * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported
261 * (sub)frame. */
262 virtual void iterate_and_write();
263
264 /* Release all writers. Call after all frames have been exported. */
265 void release_writers();
266
267 /* Determine which subset of writers is used for exporting.
268 * Set this before calling iterate_and_write().
269 *
270 * Note that writers are created for each iterated object, regardless of this option. When a
271 * writer is created it will also write the current iteration, to ensure the hierarchy is
272 * complete. The `export_subset` option is only in effect when the writer already existed from a
273 * previous iteration. */
274 void set_export_subset(ExportSubset export_subset);
275
276 /* Convert the given name to something that is valid for the exported file format.
277 * This base implementation is a no-op; override in a concrete subclass. */
278 virtual std::string make_valid_name(const std::string &name) const;
279
280 virtual std::string make_unique_name(const std::string &original_name,
281 Set<std::string> &used_names);
282
283 /* Return the name of this ID datablock that is valid for the exported file format. Overriding is
284 * only necessary if make_valid_name(id->name+2) is not suitable for the exported file format.
285 * NULL-safe: when `id == nullptr` this returns an empty string. */
286 virtual std::string get_id_name(const ID *id) const;
287
288 /* Given a HierarchyContext of some Object *, return an export path that is valid for its
289 * object->data. Overriding is necessary when the exported format does NOT expect the object's
290 * data to be a child of the object. */
291 virtual std::string get_object_data_path(const HierarchyContext *context) const;
292
293 private:
294 void debug_print_export_graph(const ExportGraph &graph) const;
295
296 void export_graph_construct();
297 void connect_loose_objects();
298 void export_graph_prune();
299 void export_graph_clear();
300
301 void visit_object(Object *object, Object *export_parent, bool weak_export);
302 void visit_dupli_object(const DupliObject *dupli_object,
303 Object *duplicator,
304 const DupliParentFinder &dupli_parent_finder);
305
306 void context_update_for_graph_index(HierarchyContext *context,
307 const ObjectIdentifier &graph_index) const;
308
309 void determine_export_paths(const HierarchyContext *parent_context);
310 bool determine_duplication_references(const HierarchyContext *parent_context,
311 const std::string &indent);
312
313 /* These three functions create writers and call their write() method. */
314 void make_writers(const HierarchyContext *parent_context);
315 void make_writer_object_data(const HierarchyContext *context);
316 void make_writers_particle_systems(const HierarchyContext *transform_context);
317
318 /* Return the appropriate HierarchyContext for the data of the object represented by
319 * object_context. */
320 HierarchyContext context_for_object_data(const HierarchyContext *object_context) const;
321
322 /* Convenience wrappers around get_id_name(). */
323 std::string get_object_name(const Object *object) const;
324 std::string get_object_name(const Object *object, const Object *parent);
325 std::string get_object_data_name(const Object *object) const;
326
327 using create_writer_func =
329 /* Ensure that a writer exists; if it doesn't, call create_func(context).
330 *
331 * The create_func function should be one of the create_XXXX_writer(context) functions declared
332 * below. */
333 EnsuredWriter ensure_writer(const HierarchyContext *context, create_writer_func create_func);
334
335 protected:
336 /* Construct a valid path for the export file format. This class concatenates by using '/' as a
337 * path separator, which is valid for both Alembic and USD. */
338 virtual std::string path_concatenate(const std::string &parent_path,
339 const std::string &child_path) const;
340
341 /* Return whether this object should be marked as 'weak export' or not.
342 *
343 * When this returns false, writers for the transform and data are created,
344 * and dupli-objects dupli-object generated from this object will be passed to
345 * should_visit_dupli_object().
346 *
347 * When this returns true, only a transform writer is created and marked as
348 * 'weak export'. In this case, the transform writer will be removed before
349 * exporting starts, unless a descendant of this object is to be exported.
350 * Dupli-object generated from this object will also be skipped.
351 *
352 * See HierarchyContext::weak_export.
353 */
354 virtual bool mark_as_weak_export(const Object *object) const;
355
356 virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const;
357
360 const HierarchyContext *context,
361 const DupliObject *dupli_object,
362 const DupliParentFinder &dupli_parent_finder);
363
364 /* These functions should create an AbstractHierarchyWriter subclass instance, or return
365 * nullptr if the object or its data should not be exported. Returning a nullptr for
366 * data/hair/particle will NOT prevent the transform to be written.
367 *
368 * The returned writer is owned by the AbstractHierarchyWriter, and should be freed in
369 * delete_object_writer().
370 *
371 * The created AbstractHierarchyWriter instances should NOT keep a copy of the context pointer.
372 * The context can be stack-allocated and go out of scope. */
377
378 /* Called by release_writers() to free what the create_XXX_writer() functions allocated. */
379 virtual void release_writer(AbstractHierarchyWriter *writer) = 0;
380
381 /* Return true if data writers should be created for this context. */
382 virtual bool include_data_writers(const HierarchyContext *) const
383 {
384 return true;
385 }
386
387 /* Return true if children of the context should be converted to writers. */
388 virtual bool include_child_writers(const HierarchyContext *) const
389 {
390 return true;
391 }
392
393 AbstractHierarchyWriter *get_writer(const std::string &export_path) const;
395};
396
397} // namespace blender::io
eEvaluationMode
BPy_StructRNA * depsgraph
unsigned long long int uint64_t
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
blender::Map< std::string, blender::Set< std::string > > ExportUsedNameMap
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
blender::Map< std::string, AbstractHierarchyWriter * > WriterMap
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
virtual void write(HierarchyContext &context)=0
static bool check_has_deforming_physics(const HierarchyContext &context)
virtual ~AbstractHierarchyWriter()=default
static bool check_has_physics(const HierarchyContext &context)
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)
ObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id)
bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
static PyObject * create_func(PyObject *, PyObject *args)
Definition python.cpp:157
const char * name
Definition DNA_ID.h:414
bool is_object_visible(enum eEvaluationMode evaluation_mode) const
static const HierarchyContext * root()
void mark_as_instance_of(const std::string &reference_export_path)