[Bf-blender-cvs] [b0cb45f2b9f] temp-alembic-exporter-T73363-ms2: Abstract Hierarchy Iterator: allow exporting transforms OR shapes

Sybren A. Stüvel noreply at git.blender.org
Fri Mar 20 12:36:46 CET 2020


Commit: b0cb45f2b9fef6a794ed15d8b9012b221c2b1b03
Author: Sybren A. Stüvel
Date:   Fri Mar 20 11:41:54 2020 +0100
Branches: temp-alembic-exporter-T73363-ms2
https://developer.blender.org/rBb0cb45f2b9fef6a794ed15d8b9012b221c2b1b03

Abstract Hierarchy Iterator: allow exporting transforms OR shapes

The Alembic exporter allows different sampling rates for transforms and
for shapes. This is necessary, for example, to have control over motion
blur: when only using rigid objects, it's enough to export sub-frame
samples of transforms.

===================================================================

M	source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
M	source/blender/io/usd/intern/abstract_hierarchy_iterator.h
M	tests/gtests/usd/abstract_hierarchy_iterator_test.cc

===================================================================

diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
index 924ca3e4802..333a0bd74a5 100644
--- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
@@ -105,7 +105,7 @@ bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context)
 }
 
 AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph)
-    : depsgraph_(depsgraph), writers_()
+    : depsgraph_(depsgraph), writers_(), export_subset_({true, true})
 {
 }
 
@@ -132,6 +132,11 @@ void AbstractHierarchyIterator::release_writers()
   writers_.clear();
 }
 
+void AbstractHierarchyIterator::set_export_subset(ExportSubset export_subset)
+{
+  export_subset_ = export_subset;
+}
+
 std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const
 {
   return name;
@@ -473,6 +478,7 @@ void AbstractHierarchyIterator::determine_duplication_references(
 void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context)
 {
   AbstractHierarchyWriter *transform_writer = nullptr;
+  bool created = false;
   float parent_matrix_inv_world[4][4];
 
   if (parent_context) {
@@ -488,7 +494,8 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont
     context->parent_context = parent_context;
 
     // Get or create the transform writer.
-    transform_writer = ensure_writer(context, &AbstractHierarchyIterator::create_transform_writer);
+    std::tie(transform_writer, created) = ensure_writer(
+        context, &AbstractHierarchyIterator::create_transform_writer);
     if (transform_writer == nullptr) {
       // Unable to export, so there is nothing to attach any children to; just abort this entire
       // branch of the export hierarchy.
@@ -496,10 +503,12 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont
     }
 
     BLI_assert(DEG_is_evaluated_object(context->object));
-    /* XXX This can lead to too many XForms being written. For example, a camera writer can refuse
-     * to write an orthographic camera. By the time that this is known, the XForm has already been
-     * written. */
-    transform_writer->write(*context);
+    if (created || export_subset_.transforms) {
+      /* XXX This can lead to too many XForms being written. For example, a camera writer can
+       * refuse to write an orthographic camera. By the time that this is known, the XForm has
+       * already been written. */
+      transform_writer->write(*context);
+    }
 
     if (!context->weak_export) {
       make_writers_particle_systems(context);
@@ -532,13 +541,18 @@ void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *
     BLI_assert(data_context.is_instance());
   }
 
+  /* Always write upon creation, otherwise depend on which subset is active. */
   AbstractHierarchyWriter *data_writer;
-  data_writer = ensure_writer(&data_context, &AbstractHierarchyIterator::create_data_writer);
+  bool created;
+  std::tie(data_writer, created) = ensure_writer(&data_context,
+                                                 &AbstractHierarchyIterator::create_data_writer);
   if (data_writer == nullptr) {
     return;
   }
 
-  data_writer->write(data_context);
+  if (created || export_subset_.shapes) {
+    data_writer->write(data_context);
+  }
 }
 
 void AbstractHierarchyIterator::make_writers_particle_systems(
@@ -557,16 +571,23 @@ void AbstractHierarchyIterator::make_writers_particle_systems(
     hair_context.particle_system = psys;
 
     AbstractHierarchyWriter *writer = nullptr;
+    bool created = false;
     switch (psys->part->type) {
       case PART_HAIR:
-        writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer);
+        std::tie(writer, created) = ensure_writer(&hair_context,
+                                                  &AbstractHierarchyIterator::create_hair_writer);
         break;
       case PART_EMITTER:
-        writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer);
+        std::tie(writer, created) = ensure_writer(
+            &hair_context, &AbstractHierarchyIterator::create_particle_writer);
         break;
     }
+    if (writer == nullptr) {
+      continue;
+    }
 
-    if (writer != nullptr) {
+    /* Always write upon creation, otherwise depend on which subset is active. */
+    if (created || export_subset_.shapes) {
       writer->write(hair_context);
     }
   }
@@ -593,22 +614,21 @@ AbstractHierarchyWriter *AbstractHierarchyIterator::get_writer(const std::string
   return it->second;
 }
 
-AbstractHierarchyWriter *AbstractHierarchyIterator::ensure_writer(
+std::pair<AbstractHierarchyWriter *, bool> AbstractHierarchyIterator::ensure_writer(
     HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func)
 {
   AbstractHierarchyWriter *writer = get_writer(context->export_path);
   if (writer != nullptr) {
-    return writer;
+    return std::make_pair(writer, false);
   }
 
   writer = (this->*create_func)(context);
   if (writer == nullptr) {
-    return nullptr;
+    return std::make_pair(nullptr, false);
   }
 
   writers_[context->export_path] = writer;
-
-  return writer;
+  return std::make_pair(writer, true);
 }
 
 std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path,
diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h b/source/blender/io/usd/intern/abstract_hierarchy_iterator.h
index f7ec0de1dcd..5e11fed193f 100644
--- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h
+++ b/source/blender/io/usd/intern/abstract_hierarchy_iterator.h
@@ -124,6 +124,12 @@ class AbstractHierarchyWriter {
   virtual bool check_is_animated(const HierarchyContext &context) const;
 };
 
+/* Determines which subset of the writers actually gets to write. */
+struct ExportSubset {
+  bool transforms : 1;
+  bool shapes : 1;
+};
+
 /* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export
  * writers. These writers are then called to perform the actual writing to a USD or Alembic file.
  *
@@ -152,6 +158,7 @@ class AbstractHierarchyIterator {
   ExportPathMap duplisource_export_path_;
   Depsgraph *depsgraph_;
   WriterMap writers_;
+  ExportSubset export_subset_;
 
  public:
   explicit AbstractHierarchyIterator(Depsgraph *depsgraph);
@@ -159,12 +166,21 @@ class AbstractHierarchyIterator {
 
   /* Iterate over the depsgraph, create writers, and tell the writers to write.
    * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported
-   * frame. */
+   * (sub)frame. */
   void iterate_and_write();
 
   /* Release all writers. Call after all frames have been exported. */
   void release_writers();
 
+  /* Determine which subset of writers is used for exporting.
+   * Set this before calling iterate_and_write().
+   *
+   * Note that writers are created for each iterated object, regardless of this option. When a
+   * writer is created it will also write the current iteration, to ensure the hierarchy is
+   * complete. The `export_subset` option is only in effect when the writer already existed from a
+   * previous iteration. */
+  void set_export_subset(ExportSubset export_subset_);
+
   /* Convert the given name to something that is valid for the exported file format.
    * This base implementation is a no-op; override in a concrete subclass. */
   virtual std::string make_valid_name(const std::string &name) const;
@@ -198,6 +214,7 @@ class AbstractHierarchyIterator {
   void determine_duplication_references(const HierarchyContext *parent_context,
                                         std::string indent);
 
+  /* These three functions create writers and call their write() method. */
   void make_writers(const HierarchyContext *parent_context);
   void make_writer_object_data(const HierarchyContext *context);
   void make_writers_particle_systems(const HierarchyContext *context);
@@ -210,10 +227,12 @@ class AbstractHierarchyIterator {
 
   typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)(
       const HierarchyContext *);
-  /* Ensure that a writer exists; if it doesn't, call create_func(context). The create_func
-   * function should be one of the create_XXXX_writer(context) functions declared below. */
-  AbstractHierarchyWriter *ensure_writer(HierarchyContext *context,
-                                         create_writer_func create_func);
+  /* Ensure that a writer exists; if it doesn't, call create_func(context).
+   *
+   * The create_func function should be one of the create_XXXX_writer(context) functions declared
+   * below. Returns a pair <writer, created>. */
+  std::pair<AbstractHierarchyWriter *, bool> ensure_writer(HierarchyContext *context,
+                                                           create_writer_func create_func);
 
  protected:
   /* Construct a valid path for the export file format. This class concatenates by using '/' as a
diff --git a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
index 375d59863d7..fbb97bd0743 100644
--- a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
+++ b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
@@ -200,4 +200,118 @@ TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest)
   // The scene has no hair or particle systems.
   EXPECT_EQ(0, iterator->hair_writers.size());
   EXPECT_EQ(0, iterator->particle_writers.size());
+
+  // On the second iteration, everything should be written as well.
+  // This tests the default value of iterator->export_subset_.
+  iterator->transform_writers.clear();
+  iterator->data_writers.clear();
+  iterator->iterate_and_write();
+  EXPECT_EQ(expected_transforms, iterator->transform_writers);
+  EXPECT_EQ(expected_data, iterator->data_writers);
+}
+
+TEST_F(USDHierarchyIteratorTest, ExportSubsetTest)
+{
+  // The scene has no hair or particle systems, and this is already covered by ExportHierarchyTest,
+  // so not included here. Update this test when hair & particle systems

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list