diff options
Diffstat (limited to 'source/blender/io/alembic/exporter/abc_custom_props.cc')
-rw-r--r-- | source/blender/io/alembic/exporter/abc_custom_props.cc | 268 |
1 files changed, 268 insertions, 0 deletions
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 |