diff options
Diffstat (limited to 'source/blender')
23 files changed, 530 insertions, 0 deletions
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 0c4064b5693..292d8e6066c 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -133,6 +133,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op) .use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"), .export_hair = RNA_boolean_get(op->ptr, "export_hair"), .export_particles = RNA_boolean_get(op->ptr, "export_particles"), + .export_custom_properties = RNA_boolean_get(op->ptr, "export_custom_properties"), .use_instancing = RNA_boolean_get(op->ptr, "use_instancing"), .packuv = RNA_boolean_get(op->ptr, "packuv"), .triangulate = RNA_boolean_get(op->ptr, "triangulate"), @@ -191,6 +192,8 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "flatten", 0, NULL, ICON_NONE); uiItemR(sub, imfptr, "use_instancing", 0, IFACE_("Use Instancing"), ICON_NONE); + uiItemR(sub, imfptr, "export_custom_properties", 0, IFACE_("Custom Properties"), ICON_NONE); + sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only")); uiItemR(sub, imfptr, "selected", 0, IFACE_("Selected Objects"), ICON_NONE); uiItemR(sub, imfptr, "renderable_only", 0, IFACE_("Renderable Objects"), ICON_NONE); @@ -449,6 +452,12 @@ void WM_OT_alembic_export(wmOperatorType *ot) RNA_def_boolean( ot->srna, "export_particles", 1, "Export Particles", "Exports non-hair particle systems"); + RNA_def_boolean(ot->srna, + "export_custom_properties", + true, + "Export Custom Properties", + "Export custom properties to Alembic .userProperties"); + RNA_def_boolean( ot->srna, "as_background_job", 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 |