Blender  V3.3
IO_abstract_hierarchy_iterator.h
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2019 Blender Foundation. All rights reserved. */
3 
4 /*
5  * This file contains the AbstractHierarchyIterator. It is intended for exporters for file
6  * formats that concern an entire hierarchy of objects (rather than, for example, an OBJ file that
7  * contains only a single mesh). Examples are Universal Scene Description (USD) and Alembic.
8  * AbstractHierarchyIterator is intended to be subclassed to support concrete file formats.
9  *
10  * The AbstractHierarchyIterator makes a distinction between the actual object hierarchy and the
11  * export hierarchy. The former is the parent/child structure in Blender, which can have multiple
12  * parent-like objects. For example, a duplicated object can have both a duplicator and a parent,
13  * both determining the final transform. The export hierarchy is the hierarchy as written to the
14  * file, and every object has only one export-parent.
15  *
16  * Currently the AbstractHierarchyIterator does not make any decisions about *what* to export.
17  * Selections like "selected only" or "no hair systems" are left to concrete subclasses.
18  */
19 
20 #pragma once
21 
23 
24 #include "DEG_depsgraph.h"
25 
26 #include <map>
27 #include <set>
28 #include <string>
29 
30 struct Depsgraph;
31 struct DupliObject;
32 struct ID;
33 struct Main;
34 struct Object;
35 struct ParticleSystem;
36 
37 namespace blender::io {
38 
39 class AbstractHierarchyWriter;
40 class DupliParentFinder;
41 
42 /* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext
43  * struct contains everything necessary to export a single object to a file. */
45  /*********** Determined during hierarchy iteration: ***************/
46  Object *object; /* Evaluated object. */
50  float matrix_world[4][4];
51  std::string export_name;
52 
53  /* When weak_export=true, the object will be exported only as transform, and only if is an
54  * ancestor of an object with weak_export=false.
55  *
56  * In other words: when weak_export=true but this object has no children, or all descendants also
57  * have weak_export=true, this object (and by recursive reasoning all its descendants) will be
58  * excluded from the export.
59  *
60  * The export hierarchy is kept as close to the hierarchy in Blender as possible. As such, an
61  * object that serves as a parent for another object, but which should NOT be exported itself, is
62  * exported only as transform (i.e. as empty). This happens with objects that are invisible when
63  * exporting with "Visible Only" enabled, for example. */
65 
66  /* When true, this object should check its parents for animation data when determining whether
67  * it's animated. This is necessary when a parent object in Blender is not part of the export. */
69 
70  /*********** Determined during writer creation: ***************/
71  float parent_matrix_inv_world[4][4]; /* Inverse of the parent's world matrix. */
72  std::string export_path; /* Hierarchical path, such as "/grandparent/parent/objectname". */
73  ParticleSystem *particle_system; /* Only set for particle/hair writers. */
74 
75  /* Hierarchical path of the object this object is duplicating; only set when this object should
76  * be stored as a reference to its original. It can happen that the original is not part of the
77  * exported objects, in which case this string is empty even though 'duplicator' is set. */
78  std::string original_export_path;
79 
80  /* Export path of the higher-up exported data. For transforms, this is the export path of the
81  * parent object. For object data, this is the export path of that object's transform.
82  *
83  * From the exported file's point of view, this is the path to the parent in that file. The term
84  * "parent" is not used here to avoid confusion with Blender's meaning of the word (which always
85  * refers to a different object). */
86  std::string higher_up_export_path;
87 
88  bool operator<(const HierarchyContext &other) const;
89 
90  /* Return a HierarchyContext representing the root of the export hierarchy. */
91  static const HierarchyContext *root();
92 
93  /* For handling instanced collections, instances created by particles, etc. */
94  bool is_instance() const;
95  void mark_as_instance_of(const std::string &reference_export_path);
96  void mark_as_not_instanced();
97 
98  bool is_object_visible(enum eEvaluationMode evaluation_mode) const;
99 };
100 
101 /* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc.
102  *
103  * Instantiated by the AbstractHierarchyIterator on the first frame an object exists. Generally
104  * that's the first frame to be exported, but can be later, for example when objects are
105  * instantiated by particles. The AbstractHierarchyWriter::write() function is called on every
106  * frame the object exists in the dependency graph and should be exported.
107  */
109  public:
110  virtual ~AbstractHierarchyWriter() = default;
111  virtual void write(HierarchyContext &context) = 0;
112  /* TODO(Sybren): add function like absent() that's called when a writer was previously created,
113  * but wasn't used while exporting the current frame (for example, a particle-instanced mesh of
114  * which the particle is no longer alive). */
115  protected:
116  /* Return true if the data written by this writer changes over time.
117  * Note that this function assumes this is an object data writer. Transform writers should not
118  * call this but implement their own logic. */
119  virtual bool check_is_animated(const HierarchyContext &context) const;
120 
121  /* Helper functions for animation checks. */
122  static bool check_has_physics(const HierarchyContext &context);
124 };
125 
126 /* Determines which subset of the writers actually gets to write. */
127 struct ExportSubset {
128  bool transforms : 1;
129  bool shapes : 1;
130 };
131 
132 /* EnsuredWriter represents an AbstractHierarchyWriter* combined with information whether it was
133  * newly created or not. It's returned by AbstractHierarchyIterator::ensure_writer(). */
135  private:
136  AbstractHierarchyWriter *writer_;
137 
138  /* Is set to truth when ensure_writer() did not find existing writer and created a new one.
139  * Is set to false when writer has been re-used or when allocation of the new one has failed
140  * (`writer` will be `nullptr` in that case and bool(ensured_writer) will be false). */
141  bool newly_created_;
142 
144 
145  public:
146  EnsuredWriter();
147 
148  static EnsuredWriter empty();
151 
152  bool is_newly_created() const;
153 
154  /* These operators make an EnsuredWriter* act as an AbstractHierarchyWriter* */
155  operator bool() const;
157 };
158 
159 /* Unique identifier for a (potentially duplicated) object.
160  *
161  * Instances of this class serve as key in the export graph of the
162  * AbstractHierarchyIterator. */
164  public:
166  Object *duplicated_by; /* nullptr for real objects. */
168 
169  protected:
171 
172  public:
174  static ObjectIdentifier for_real_object(Object *object);
176  static ObjectIdentifier for_duplicated_object(const DupliObject *dupli_object,
178 
179  bool is_root() const;
180 };
181 
182 bool operator<(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b);
183 bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b);
184 
185 /* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export
186  * writers. These writers are then called to perform the actual writing to a USD or Alembic file.
187  *
188  * Dealing with file- and scene-level data (for example, creating a USD scene, setting the frame
189  * rate, etc.) is not part of the AbstractHierarchyIterator class structure, and should be done
190  * in separate code.
191  */
193  public:
194  /* Mapping from export path to writer. */
195  typedef std::map<std::string, AbstractHierarchyWriter *> WriterMap;
196  /* All the children of some object, as per the export hierarchy. */
197  typedef std::set<HierarchyContext *> ExportChildren;
198  /* Mapping from an object and its duplicator to the object's export-children. */
199  typedef std::map<ObjectIdentifier, ExportChildren> ExportGraph;
200  /* Mapping from ID to its export path. This is used for instancing; given an
201  * instanced datablock, the export path of the original can be looked up. */
202  typedef std::map<ID *, std::string> ExportPathMap;
203 
204  protected:
211 
212  public:
214  virtual ~AbstractHierarchyIterator();
215 
216  /* Iterate over the depsgraph, create writers, and tell the writers to write.
217  * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported
218  * (sub)frame. */
219  virtual void iterate_and_write();
220 
221  /* Release all writers. Call after all frames have been exported. */
222  void release_writers();
223 
224  /* Determine which subset of writers is used for exporting.
225  * Set this before calling iterate_and_write().
226  *
227  * Note that writers are created for each iterated object, regardless of this option. When a
228  * writer is created it will also write the current iteration, to ensure the hierarchy is
229  * complete. The `export_subset` option is only in effect when the writer already existed from a
230  * previous iteration. */
232 
233  /* Convert the given name to something that is valid for the exported file format.
234  * This base implementation is a no-op; override in a concrete subclass. */
235  virtual std::string make_valid_name(const std::string &name) const;
236 
237  /* Return the name of this ID datablock that is valid for the exported file format. Overriding is
238  * only necessary if make_valid_name(id->name+2) is not suitable for the exported file format.
239  * NULL-safe: when `id == nullptr` this returns an empty string. */
240  virtual std::string get_id_name(const ID *id) const;
241 
242  /* Given a HierarchyContext of some Object *, return an export path that is valid for its
243  * object->data. Overriding is necessary when the exported format does NOT expect the object's
244  * data to be a child of the object. */
245  virtual std::string get_object_data_path(const HierarchyContext *context) const;
246 
247  private:
248  void debug_print_export_graph(const ExportGraph &graph) const;
249 
250  void export_graph_construct();
251  void connect_loose_objects();
252  void export_graph_prune();
253  void export_graph_clear();
254 
255  void visit_object(Object *object, Object *export_parent, bool weak_export);
256  void visit_dupli_object(DupliObject *dupli_object,
257  Object *duplicator,
258  const DupliParentFinder &dupli_parent_finder);
259 
260  void context_update_for_graph_index(HierarchyContext *context,
261  const ExportGraph::key_type &graph_index) const;
262 
263  void determine_export_paths(const HierarchyContext *parent_context);
264  void determine_duplication_references(const HierarchyContext *parent_context,
265  std::string indent);
266 
267  /* These three functions create writers and call their write() method. */
268  void make_writers(const HierarchyContext *parent_context);
269  void make_writer_object_data(const HierarchyContext *context);
270  void make_writers_particle_systems(const HierarchyContext *context);
271 
272  /* Return the appropriate HierarchyContext for the data of the object represented by
273  * object_context. */
274  HierarchyContext context_for_object_data(const HierarchyContext *object_context) const;
275 
276  /* Convenience wrappers around get_id_name(). */
277  std::string get_object_name(const Object *object) const;
278  std::string get_object_data_name(const Object *object) const;
279 
280  typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)(
281  const HierarchyContext *);
282  /* Ensure that a writer exists; if it doesn't, call create_func(context).
283  *
284  * The create_func function should be one of the create_XXXX_writer(context) functions declared
285  * below. */
286  EnsuredWriter ensure_writer(HierarchyContext *context, create_writer_func create_func);
287 
288  protected:
289  /* Construct a valid path for the export file format. This class concatenates by using '/' as a
290  * path separator, which is valid for both Alembic and USD. */
291  virtual std::string path_concatenate(const std::string &parent_path,
292  const std::string &child_path) const;
293 
294  /* Return whether this object should be marked as 'weak export' or not.
295  *
296  * When this returns false, writers for the transform and data are created,
297  * and dupli-objects dupli-object generated from this object will be passed to
298  * should_visit_dupli_object().
299  *
300  * When this returns true, only a transform writer is created and marked as
301  * 'weak export'. In this case, the transform writer will be removed before
302  * exporting starts, unless a descendant of this object is to be exported.
303  * Dupli-object generated from this object will also be skipped.
304  *
305  * See HierarchyContext::weak_export.
306  */
307  virtual bool mark_as_weak_export(const Object *object) const;
308 
309  virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const;
310 
311  virtual ExportGraph::key_type determine_graph_index_object(const HierarchyContext *context);
312  virtual ExportGraph::key_type determine_graph_index_dupli(
313  const HierarchyContext *context,
314  const DupliObject *dupli_object,
315  const DupliParentFinder &dupli_parent_finder);
316 
317  /* These functions should create an AbstractHierarchyWriter subclass instance, or return
318  * nullptr if the object or its data should not be exported. Returning a nullptr for
319  * data/hair/particle will NOT prevent the transform to be written.
320  *
321  * The returned writer is owned by the AbstractHierarchyWriter, and should be freed in
322  * delete_object_writer().
323  *
324  * The created AbstractHierarchyWriter instances should NOT keep a copy of the context pointer.
325  * The context can be stack-allocated and go out of scope. */
330 
331  /* Called by release_writers() to free what the create_XXX_writer() functions allocated. */
332  virtual void release_writer(AbstractHierarchyWriter *writer) = 0;
333 
334  AbstractHierarchyWriter *get_writer(const std::string &export_path) const;
335  ExportChildren &graph_children(const HierarchyContext *parent_context);
336 };
337 
338 } // namespace blender::io
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
eEvaluationMode
Definition: DEG_depsgraph.h:44
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
std::map< std::string, AbstractHierarchyWriter * > WriterMap
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
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)
Depsgraph * graph
const Depsgraph * depsgraph
bool operator<(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b)
bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b)
static PyObject * create_func(PyObject *, PyObject *args)
Definition: python.cpp:156
Definition: DNA_ID.h:368
Definition: BKE_main.h:121
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)