Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@blender.org>2020-09-11 15:06:13 +0300
committerSybren A. Stüvel <sybren@blender.org>2020-09-14 13:49:27 +0300
commitee97add4c404a414addab5cbb6107fac002da77a (patch)
treef6a7adadf1ee27470b3a53dd9b247078f83a3af1 /source/blender/io/alembic
parentb8a25bbd8a400b52de150a08c041e2f6f2a9dce1 (diff)
Alembic export: write custom properties
Write custom properties (aka ID properties) to Alembic, to the `.userProperties` compound property. Manifest Task: https://developer.blender.org/T50725 Scalar properties (so single-value/non-array properties) are written as single-element array properties to Alembic. This is also what's done by Houdini and Maya exporters, so it seems to be the standard way of doing things. It also simplifies the implementation. Two-dimensional arrays are flattened by concatenating all the numbers into a single array. This is because ID properties have a limited type system. This means that a 3x3 "matrix" could just as well be a list of three 3D vectors. Alembic has two container properties to store custom data: - `.userProperties`, which is meant for properties that aren't necessarily understood by other software packages, and - `.arbGeomParams`, which can contain the same kind of data as `.userProperties`, but can also specify that these vary per face of a mesh. This property is mostly intended for renderers. Most industry packages write their custom data to `.arbGeomParams`. However, given their goals I feel that `.userProperties` is the more appropriate one for Blender's ID Properties. The code is a bit more involved than I would have liked. An `ABCAbstractWriter` has a `uniqueptr` to its `CustomPropertiesExporter`, but the `CustomPropertiesExporter` also has a pointer back to its owning `ABCAbstractWriter`. It's the latter pointer that I'm not too happy with, but it has a reason. Getting the aforementioned `.userProperties` from the Alembic library will automatically create it if it doesn't exist already. If it's not used to actually add custom properties to, it will crash the Alembic CLI tools (and maybe others too). This is what the pointer back to the `ABCAbstractWriter` is used for: to get the `.userProperties` at the last moment, when it's 100% sure at least one custom property will be written. Differential Revision: https://developer.blender.org/D8869 Reviewed by: sergey, dbystedt
Diffstat (limited to 'source/blender/io/alembic')
-rw-r--r--source/blender/io/alembic/ABC_alembic.h1
-rw-r--r--source/blender/io/alembic/CMakeLists.txt2
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.cc268
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.h96
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.cc41
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h41
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.cc10
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.h2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc8
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.cc11
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.cc11
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.h4
22 files changed, 521 insertions, 0 deletions
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index 9a2c74c64a3..9785f6d68ab 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -60,6 +60,7 @@ struct AlembicExportParams {
bool triangulate;
bool export_hair;
bool export_particles;
+ bool export_custom_properties;
bool use_instancing;
/* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx
diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt
index 2b44146e475..d55f2382a9b 100644
--- a/source/blender/io/alembic/CMakeLists.txt
+++ b/source/blender/io/alembic/CMakeLists.txt
@@ -56,6 +56,7 @@ set(SRC
intern/alembic_capi.cc
exporter/abc_archive.cc
+ exporter/abc_custom_props.cc
exporter/abc_export_capi.cc
exporter/abc_hierarchy_iterator.cc
exporter/abc_subdiv_disabler.cc
@@ -84,6 +85,7 @@ set(SRC
intern/abc_util.h
exporter/abc_archive.h
+ exporter/abc_custom_props.h
exporter/abc_hierarchy_iterator.h
exporter/abc_subdiv_disabler.h
exporter/abc_writer_abstract.h
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc
new file mode 100644
index 00000000000..382afdc294d
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_custom_props.cc
@@ -0,0 +1,268 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup Alembic
+ */
+
+#include "abc_custom_props.h"
+
+#include "abc_writer_abstract.h"
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include <Alembic/Abc/OTypedArrayProperty.h>
+#include <Alembic/Abc/OTypedScalarProperty.h>
+
+#include "BKE_idprop.h"
+#include "DNA_ID.h"
+
+using Alembic::Abc::ArraySample;
+using Alembic::Abc::OArrayProperty;
+using Alembic::Abc::OBoolArrayProperty;
+using Alembic::Abc::OCompoundProperty;
+using Alembic::Abc::ODoubleArrayProperty;
+using Alembic::Abc::OFloatArrayProperty;
+using Alembic::Abc::OInt32ArrayProperty;
+using Alembic::Abc::OStringArrayProperty;
+
+namespace blender::io::alembic {
+
+CustomPropertiesExporter::CustomPropertiesExporter(ABCAbstractWriter *owner) : owner_(owner)
+{
+}
+
+CustomPropertiesExporter::~CustomPropertiesExporter()
+{
+}
+
+void CustomPropertiesExporter::write_all(const IDProperty *group)
+{
+ if (group == nullptr) {
+ return;
+ }
+ BLI_assert(group->type == IDP_GROUP);
+
+ /* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */
+ LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) {
+ if (STREQ(id_property->name, "_RNA_UI")) {
+ continue;
+ }
+ write(id_property);
+ }
+}
+
+void CustomPropertiesExporter::write(const IDProperty *id_property)
+{
+ BLI_assert(id_property->name[0] != '\0');
+
+ switch (id_property->type) {
+ case IDP_STRING: {
+ /* The Alembic library doesn't accept NULL-terminated character arrays. */
+ const std::string prop_value(IDP_String(id_property), id_property->len - 1);
+ set_scalar_property<OStringArrayProperty, std::string>(id_property->name, prop_value);
+ break;
+ }
+ case IDP_INT:
+ static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
+ set_scalar_property<OInt32ArrayProperty, int32_t>(id_property->name, IDP_Int(id_property));
+ break;
+ case IDP_FLOAT:
+ set_scalar_property<OFloatArrayProperty, float>(id_property->name, IDP_Float(id_property));
+ break;
+ case IDP_DOUBLE:
+ set_scalar_property<ODoubleArrayProperty, double>(id_property->name,
+ IDP_Double(id_property));
+ break;
+ case IDP_ARRAY:
+ write_array(id_property);
+ break;
+ case IDP_IDPARRAY:
+ write_idparray(id_property);
+ break;
+ }
+}
+
+void CustomPropertiesExporter::write_array(const IDProperty *id_property)
+{
+ BLI_assert(id_property->type == IDP_ARRAY);
+
+ switch (id_property->subtype) {
+ case IDP_INT: {
+ const int *array = (int *)IDP_Array(id_property);
+ static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
+ set_array_property<OInt32ArrayProperty, int32_t>(id_property->name, array, id_property->len);
+ break;
+ }
+ case IDP_FLOAT: {
+ const float *array = (float *)IDP_Array(id_property);
+ set_array_property<OFloatArrayProperty, float>(id_property->name, array, id_property->len);
+ break;
+ }
+ case IDP_DOUBLE: {
+ const double *array = (double *)IDP_Array(id_property);
+ set_array_property<ODoubleArrayProperty, double>(id_property->name, array, id_property->len);
+ break;
+ }
+ }
+}
+
+void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+
+ if (idp_array->len == 0) {
+ /* Don't bother writing dataless arrays. */
+ return;
+ }
+
+ IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
+
+#ifndef NDEBUG
+ /* Sanity check that all elements of the array have the same type.
+ * Blender should already enforce this, hence it's only used in debug mode. */
+ for (int i = 1; i < idp_array->len; i++) {
+ if (idp_elements[i].type == idp_elements[0].type) {
+ continue;
+ }
+ std::cerr << "Custom property " << idp_array->name << " has elements of varying type";
+ BLI_assert(!"Mixed type IDP_ARRAY custom property found");
+ }
+#endif
+
+ switch (idp_elements[0].type) {
+ case IDP_STRING:
+ write_idparray_of_strings(idp_array);
+ break;
+ case IDP_ARRAY:
+ write_idparray_of_numbers(idp_array);
+ break;
+ }
+}
+
+void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+ BLI_assert(idp_array->len > 0);
+
+ /* Convert to an array of std::strings, because Alembic doesn't like zero-delimited strings. */
+ IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
+ std::vector<std::string> strings(idp_array->len);
+ for (int i = 0; i < idp_array->len; i++) {
+ BLI_assert(idp_elements[i].type == IDP_STRING);
+ strings[i] = IDP_String(&idp_elements[i]);
+ }
+
+ /* Alembic needs a pointer to the first value of the array. */
+ const std::string *array_of_strings = &strings[0];
+ set_array_property<OStringArrayProperty, std::string>(
+ idp_array->name, array_of_strings, strings.size());
+}
+
+void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+ BLI_assert(idp_array->len > 0);
+
+ /* This must be an array of arrays. */
+ IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
+ BLI_assert(idp_rows[0].type == IDP_ARRAY);
+
+ const int subtype = idp_rows[0].subtype;
+ if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
+ /* Non-numerical types are not supported. */
+ return;
+ }
+
+ switch (subtype) {
+ case IDP_INT:
+ static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
+ write_idparray_flattened_typed<OInt32ArrayProperty, int32_t>(idp_array);
+ break;
+ case IDP_FLOAT:
+ write_idparray_flattened_typed<OFloatArrayProperty, float>(idp_array);
+ break;
+ case IDP_DOUBLE:
+ write_idparray_flattened_typed<ODoubleArrayProperty, double>(idp_array);
+ break;
+ }
+}
+
+template<typename ABCPropertyType, typename BlenderValueType>
+void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+ BLI_assert(idp_array->len > 0);
+
+ const IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
+ BLI_assert(idp_rows[0].type == IDP_ARRAY);
+ BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE));
+
+ const uint64_t num_rows = idp_array->len;
+ std::vector<BlenderValueType> matrix_values;
+ for (size_t row_idx = 0; row_idx < num_rows; ++row_idx) {
+ const BlenderValueType *row = (BlenderValueType *)IDP_Array(&idp_rows[row_idx]);
+ for (size_t col_idx = 0; col_idx < idp_rows[row_idx].len; col_idx++) {
+ matrix_values.push_back(row[col_idx]);
+ }
+ }
+
+ set_array_property<ABCPropertyType, BlenderValueType>(
+ idp_array->name, &matrix_values[0], matrix_values.size());
+}
+
+template<typename ABCPropertyType, typename BlenderValueType>
+void CustomPropertiesExporter::set_scalar_property(const StringRef property_name,
+ const BlenderValueType property_value)
+{
+ set_array_property<ABCPropertyType, BlenderValueType>(property_name, &property_value, 1);
+}
+
+template<typename ABCPropertyType, typename BlenderValueType>
+void CustomPropertiesExporter::set_array_property(const StringRef property_name,
+ const BlenderValueType *array_values,
+ const size_t num_array_items)
+{
+ auto create_callback = [this, property_name]() -> OArrayProperty {
+ return create_abc_property<ABCPropertyType>(property_name);
+ };
+
+ OArrayProperty array_prop = abc_properties_.lookup_or_add_cb(property_name, create_callback);
+ Alembic::Util::Dimensions array_dimensions(num_array_items);
+ ArraySample sample(array_values, array_prop.getDataType(), array_dimensions);
+ array_prop.set(sample);
+}
+
+template<typename ABCPropertyType>
+OArrayProperty CustomPropertiesExporter::create_abc_property(const StringRef property_name)
+{
+ /* Get the necessary info from our owner. */
+ OCompoundProperty abc_prop_for_custom_props = owner_->abc_prop_for_custom_props();
+ const uint32_t timesample_index = owner_->timesample_index();
+
+ /* Construct the Alembic property. */
+ ABCPropertyType abc_property(abc_prop_for_custom_props, property_name);
+ abc_property.setTimeSampling(timesample_index);
+ return abc_property;
+}
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.h b/source/blender/io/alembic/exporter/abc_custom_props.h
new file mode 100644
index 00000000000..d3f9b2fc43c
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_custom_props.h
@@ -0,0 +1,96 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup Alembic
+ */
+
+#pragma once
+
+#include <Alembic/Abc/OArrayProperty.h>
+#include <Alembic/Abc/OCompoundProperty.h>
+
+#include "BLI_map.hh"
+
+#include <memory>
+
+struct IDProperty;
+
+namespace blender::io::alembic {
+
+class ABCAbstractWriter;
+
+/* Write values of Custom Properties (a.k.a. ID Properties) to Alembic.
+ *
+ * Each Alembic Writer instance optionally has one CustomPropertiesExporter (CPE). This CPE not
+ * only writes the custom properties to Alembic, but also keeps references in memory so that the
+ * Alembic library doesn't prematurely finalize the data. */
+class CustomPropertiesExporter {
+ private:
+ /* Owner is used to get the OCompoundProperty and time sample index. The former should only be
+ * requested from the Alembic library when it's actually going to be used to add custom
+ * properties (otherwise an invalid Alembic file is written). */
+ ABCAbstractWriter *owner_;
+
+ /* The Compound Property that will contain the exported custom properties.
+ *
+ * Typically this the return value of abc_schema.getArbGeomParams() or
+ * abc_schema.getUserProperties(). */
+ Alembic::Abc::OCompoundProperty abc_compound_prop_;
+
+ /* Mapping from property name in Blender to property in Alembic.
+ * Here Blender does the same as other software (Maya, Houdini), and writes
+ * scalar properties as single-element arrays. */
+ Map<std::string, Alembic::Abc::OArrayProperty> abc_properties_;
+
+ public:
+ CustomPropertiesExporter(ABCAbstractWriter *owner);
+ virtual ~CustomPropertiesExporter();
+
+ void write_all(const IDProperty *group);
+
+ private:
+ void write(const IDProperty *id_property);
+ void write_array(const IDProperty *id_property);
+
+ /* IDProperty arrays are used to store arrays-of-arrays or arrays-of-strings. */
+ void write_idparray(const IDProperty *idp_array);
+ void write_idparray_of_strings(const IDProperty *idp_array);
+ void write_idparray_of_numbers(const IDProperty *idp_array);
+
+ /* Flatten an array-of-arrays into one long array, then write that.
+ * It is tempting to write an array of NxM numbers as a matrix, but there is
+ * no guarantee that the data actually represents a matrix. */
+ template<typename ABCPropertyType, typename BlenderValueType>
+ void write_idparray_flattened_typed(const IDProperty *idp_array);
+
+ /* Write a single scalar (i.e. non-array) property as single-value array. */
+ template<typename ABCPropertyType, typename BlenderValueType>
+ void set_scalar_property(StringRef property_name, const BlenderValueType property_value);
+
+ template<typename ABCPropertyType, typename BlenderValueType>
+ void set_array_property(StringRef property_name,
+ const BlenderValueType *array_values,
+ size_t num_array_items);
+
+ template<typename ABCPropertyType>
+ Alembic::Abc::OArrayProperty create_abc_property(StringRef property_name);
+};
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
index 0b08d8c4e58..e99048cc0ee 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
@@ -59,6 +59,7 @@ void ABCAbstractWriter::write(HierarchyContext &context)
if (!frame_has_been_written_) {
is_animated_ = (args_.export_params->frame_start != args_.export_params->frame_end) &&
check_is_animated(context);
+ ensure_custom_properties_exporter(context);
}
else if (!is_animated_) {
/* A frame has already been written, and without animation one frame is enough. */
@@ -67,9 +68,49 @@ void ABCAbstractWriter::write(HierarchyContext &context)
do_write(context);
+ if (custom_props_) {
+ custom_props_->write_all(get_id_properties(context));
+ }
+
frame_has_been_written_ = true;
}
+void ABCAbstractWriter::ensure_custom_properties_exporter(const HierarchyContext &context)
+{
+ if (!args_.export_params->export_custom_properties) {
+ return;
+ }
+
+ if (custom_props_) {
+ /* Custom properties exporter already created. */
+ return;
+ }
+
+ /* Avoid creating a custom properties exporter if there are no custom properties to export. */
+ const IDProperty *id_properties = get_id_properties(context);
+ if (id_properties == nullptr || id_properties->len == 0) {
+ return;
+ }
+
+ custom_props_ = std::make_unique<CustomPropertiesExporter>(this);
+}
+
+const IDProperty *ABCAbstractWriter::get_id_properties(const HierarchyContext &context) const
+{
+ Object *object = context.object;
+ if (object->data == nullptr) {
+ return nullptr;
+ }
+
+ /* Most subclasses write object data, so default to the object data's ID properties. */
+ return static_cast<ID *>(object->data)->properties;
+}
+
+uint32_t ABCAbstractWriter::timesample_index() const
+{
+ return timesample_index_;
+}
+
const Imath::Box3d &ABCAbstractWriter::bounding_box() const
{
return bounding_box_;
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
index 59c55330443..d23e69cf73e 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.h
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -19,6 +19,7 @@
#pragma once
#include "IO_abstract_hierarchy_iterator.h"
+#include "abc_custom_props.h"
#include "abc_hierarchy_iterator.h"
#include <Alembic/Abc/OObject.h>
@@ -27,6 +28,7 @@
#include "DEG_depsgraph_query.h"
#include "DNA_material_types.h"
+struct IDProperty;
struct Material;
struct Object;
@@ -44,6 +46,9 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
/* Visibility of this writer's data in Alembic. */
Alembic::Abc::OCharProperty abc_visibility_;
+ /* Optional writer for custom properties. */
+ std::unique_ptr<CustomPropertiesExporter> custom_props_;
+
public:
explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args);
virtual ~ABCAbstractWriter();
@@ -59,6 +64,7 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
* Empty). */
virtual bool is_supported(const HierarchyContext *context) const;
+ uint32_t timesample_index() const;
const Imath::Box3d &bounding_box() const;
/* Called by AlembicHierarchyCreator after checking that the data is supported via
@@ -67,12 +73,47 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
virtual Alembic::Abc::OObject get_alembic_object() const = 0;
+ /* Return the Alembic object's CompoundProperty that'll contain the custom properties.
+ *
+ * This function is called whenever there are custom properties to be written to Alembic. It
+ * should call abc_schema_prop_for_custom_props() with the writer's Alembic schema object.
+ *
+ * If custom properties are not supported by a specific subclass, it should return an empty
+ * OCompoundProperty() and override ensure_custom_properties_exporter() to do nothing.
+ */
+ virtual Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() = 0;
+
protected:
virtual void do_write(HierarchyContext &context) = 0;
virtual void update_bounding_box(Object *object);
+ /* Return ID properties of whatever ID datablock is written by this writer. Defaults to the
+ * properties of the object data. Can return nullptr if no custom properties are to be written.
+ */
+ virtual const IDProperty *get_id_properties(const HierarchyContext &context) const;
+
+ virtual void ensure_custom_properties_exporter(const HierarchyContext &context);
+
void write_visibility(const HierarchyContext &context);
+
+ /* Return the Alembic schema's compound property, which will be used for writing custom
+ * properties.
+ *
+ * This can return either abc_schema.getUserProperties() or abc_schema.getArbGeomParams(). The
+ * former only holds values similar to Blender's custom properties, whereas the latter can also
+ * specify that certain custom properties vary per mesh component (so per face, vertex, etc.). As
+ * such, .userProperties is more suitable for custom properties. However, Maya, Houdini use
+ * .arbGeomParams for custom data.
+ *
+ * Because of this, the code uses this templated function so that there is one place that
+ * determines where custom properties are exporter to.
+ */
+ template<typename T>
+ Alembic::Abc::OCompoundProperty abc_schema_prop_for_custom_props(T abc_schema)
+ {
+ return abc_schema.getUserProperties();
+ }
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc
index 0ce6c3dc07f..82a741699e4 100644
--- a/source/blender/io/alembic/exporter/abc_writer_camera.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc
@@ -65,6 +65,11 @@ Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const
return abc_camera_;
}
+Alembic::Abc::OCompoundProperty ABCCameraWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_camera_schema_);
+}
+
void ABCCameraWriter::do_write(HierarchyContext &context)
{
Camera *cam = static_cast<Camera *>(context.object->data);
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h
index 1b3e5898517..41885ed66d6 100644
--- a/source/blender/io/alembic/exporter/abc_writer_camera.h
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.h
@@ -43,6 +43,7 @@ class ABCCameraWriter : public ABCAbstractWriter {
protected:
virtual bool is_supported(const HierarchyContext *context) const override;
virtual void do_write(HierarchyContext &context) override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc
index 6a12e4c59f3..b57af345a3c 100644
--- a/source/blender/io/alembic/exporter/abc_writer_curves.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc
@@ -66,6 +66,11 @@ Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const
return abc_curve_;
}
+Alembic::Abc::OCompoundProperty ABCCurveWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_curve_schema_);
+}
+
void ABCCurveWriter::do_write(HierarchyContext &context)
{
Curve *curve = static_cast<Curve *>(context.object->data);
diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h
index d15f008f947..e210363557c 100644
--- a/source/blender/io/alembic/exporter/abc_writer_curves.h
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.h
@@ -41,6 +41,7 @@ class ABCCurveWriter : public ABCAbstractWriter {
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
protected:
virtual void do_write(HierarchyContext &context) override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc
index 80034245b84..072feb2a90a 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc
@@ -62,6 +62,11 @@ Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const
return abc_curves_;
}
+Alembic::Abc::OCompoundProperty ABCHairWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_curves_schema_);
+}
+
bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const
{
/* We assume that hair particles are always animated. */
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h
index f7d988ecbc4..3759ffa4310 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.h
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.h
@@ -44,6 +44,7 @@ class ABCHairWriter : public ABCAbstractWriter {
protected:
virtual void do_write(HierarchyContext &context) override;
virtual bool check_is_animated(const HierarchyContext &context) const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
private:
void write_hair_sample(const HierarchyContext &context,
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.cc b/source/blender/io/alembic/exporter/abc_writer_instance.cc
index 14c65e2a2e2..7f3b044cb8b 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.cc
@@ -50,6 +50,16 @@ void ABCInstanceWriter::create_alembic_objects(const HierarchyContext *context)
CLOG_INFO(&LOG, 2, "exporting instance %s", args_.abc_path.c_str());
}
+void ABCInstanceWriter::ensure_custom_properties_exporter(const HierarchyContext & /*context*/)
+{
+ /* Intentionally do nothing. Instances should not have their own custom properties. */
+}
+
+Alembic::Abc::OCompoundProperty ABCInstanceWriter::abc_prop_for_custom_props()
+{
+ return Alembic::Abc::OCompoundProperty();
+}
+
OObject ABCInstanceWriter::get_alembic_object() const
{
/* There is no OObject for an instance. */
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.h b/source/blender/io/alembic/exporter/abc_writer_instance.h
index 067c4af7aed..f7d6450055a 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.h
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.h
@@ -39,6 +39,8 @@ class ABCInstanceWriter : public ABCAbstractWriter {
protected:
virtual bool is_supported(const HierarchyContext *context) const override;
virtual void do_write(HierarchyContext &context) override;
+ void ensure_custom_properties_exporter(const HierarchyContext &context) override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index b762ad47932..fbc662113cc 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -125,6 +125,14 @@ Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
return abc_poly_mesh_;
}
+Alembic::Abc::OCompoundProperty ABCGenericMeshWriter::abc_prop_for_custom_props()
+{
+ if (is_subd_) {
+ return abc_schema_prop_for_custom_props(abc_subdiv_schema_);
+ }
+ return abc_schema_prop_for_custom_props(abc_poly_mesh_schema_);
+}
+
bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const
{
ModifierData *md = static_cast<ModifierData *>(ob_eval->modifiers.last);
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h
index 956587df7c0..fdf2d3cc1e3 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.h
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h
@@ -55,6 +55,7 @@ class ABCGenericMeshWriter : public ABCAbstractWriter {
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
protected:
virtual bool is_supported(const HierarchyContext *context) const override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
index de1870fefd9..7595a0eba63 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
@@ -78,6 +78,17 @@ OObject ABCNurbsWriter::get_alembic_object() const
return abc_nurbs_[0];
}
+Alembic::Abc::OCompoundProperty ABCNurbsWriter::abc_prop_for_custom_props()
+{
+ if (abc_nurbs_.empty()) {
+ return Alembic::Abc::OCompoundProperty();
+ }
+
+ /* A single NURBS object in Blender is expanded to multiple curves in Alembic.
+ * Just store the custom properties on the first one for simplicity. */
+ return abc_schema_prop_for_custom_props(abc_nurbs_schemas_[0]);
+}
+
bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const
{
/* Check if object has shape keys. */
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
index 691390ffc9f..0f206d34682 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.h
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
@@ -40,6 +40,7 @@ class ABCNurbsWriter : public ABCAbstractWriter {
virtual bool is_supported(const HierarchyContext *context) const override;
virtual void do_write(HierarchyContext &context) override;
virtual bool check_is_animated(const HierarchyContext &context) const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
class ABCNurbsMeshWriter : public ABCGenericMeshWriter {
diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc
index 83d33577b3b..557f580e8aa 100644
--- a/source/blender/io/alembic/exporter/abc_writer_points.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_points.cc
@@ -58,6 +58,11 @@ Alembic::Abc::OObject ABCPointsWriter::get_alembic_object() const
return abc_points_;
}
+Alembic::Abc::OCompoundProperty ABCPointsWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_points_schema_);
+}
+
bool ABCPointsWriter::is_supported(const HierarchyContext *context) const
{
return ELEM(context->particle_system->part->type,
diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h
index fec5e84f3f2..0447c41db3e 100644
--- a/source/blender/io/alembic/exporter/abc_writer_points.h
+++ b/source/blender/io/alembic/exporter/abc_writer_points.h
@@ -37,6 +37,7 @@ class ABCPointsWriter : public ABCAbstractWriter {
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
virtual bool is_supported(const HierarchyContext *context) const override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc
index a72a6b47aa9..79e460e56e9 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc
@@ -53,6 +53,17 @@ void ABCTransformWriter::create_alembic_objects(const HierarchyContext * /*conte
abc_xform_schema_ = abc_xform_.getSchema();
}
+Alembic::Abc::OCompoundProperty ABCTransformWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props<OXformSchema>(abc_xform_schema_);
+}
+
+const IDProperty *ABCTransformWriter::get_id_properties(const HierarchyContext &context) const
+{
+ const Object *object = context.object;
+ return object->id.properties;
+}
+
void ABCTransformWriter::do_write(HierarchyContext &context)
{
float parent_relative_matrix[4][4]; // The object matrix relative to the parent.
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h
index a334fe610ee..4542b9de506 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.h
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.h
@@ -21,6 +21,8 @@
#include "abc_writer_abstract.h"
+#include <memory>
+
#include <Alembic/AbcGeom/OXform.h>
namespace blender::io::alembic {
@@ -38,6 +40,8 @@ class ABCTransformWriter : public ABCAbstractWriter {
virtual void do_write(HierarchyContext &context) override;
virtual bool check_is_animated(const HierarchyContext &context) const override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ const IDProperty *get_id_properties(const HierarchyContext &context) const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
} // namespace blender::io::alembic