Blender V5.0
abc_custom_props.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "abc_custom_props.h"
10
11#include "abc_writer_abstract.h"
12
13#include <string>
14
15#include <Alembic/Abc/OTypedArrayProperty.h>
16
17#include "BLI_listbase.h"
18
19#include "BKE_idprop.hh"
20#include "DNA_ID.h"
21
22using Alembic::Abc::ArraySample;
23using Alembic::Abc::OArrayProperty;
24using Alembic::Abc::OBoolArrayProperty;
25using Alembic::Abc::OCompoundProperty;
26using Alembic::Abc::ODoubleArrayProperty;
27using Alembic::Abc::OFloatArrayProperty;
28using Alembic::Abc::OInt32ArrayProperty;
29using Alembic::Abc::OStringArrayProperty;
30
31namespace blender::io::alembic {
32
34
36{
37 if (group == nullptr) {
38 return;
39 }
40 BLI_assert(group->type == IDP_GROUP);
41
42 /* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */
43 LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) {
44 write(id_property);
45 }
46}
47
48void CustomPropertiesExporter::write(const IDProperty *id_property)
49{
50 BLI_assert(id_property->name[0] != '\0');
51
52 switch (id_property->type) {
53 case IDP_STRING: {
54 /* The Alembic library doesn't accept null-terminated character arrays. */
55 const std::string prop_value(IDP_string_get(id_property), id_property->len - 1);
56 set_scalar_property<OStringArrayProperty, std::string>(id_property->name, prop_value);
57 break;
58 }
59 case IDP_INT:
60 static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
61 set_scalar_property<OInt32ArrayProperty, int32_t>(id_property->name,
62 IDP_int_get(id_property));
63 break;
64 case IDP_FLOAT:
65 set_scalar_property<OFloatArrayProperty, float>(id_property->name,
66 IDP_float_get(id_property));
67 break;
68 case IDP_DOUBLE:
69 set_scalar_property<ODoubleArrayProperty, double>(id_property->name,
70 IDP_double_get(id_property));
71 break;
72 case IDP_BOOLEAN:
73 set_scalar_property<OBoolArrayProperty, bool>(id_property->name, IDP_bool_get(id_property));
74 break;
75 case IDP_ARRAY:
76 write_array(id_property);
77 break;
78 case IDP_IDPARRAY:
79 write_idparray(id_property);
80 break;
81 }
82}
83
84void CustomPropertiesExporter::write_array(const IDProperty *id_property)
85{
86 BLI_assert(id_property->type == IDP_ARRAY);
87
88 switch (id_property->subtype) {
89 case IDP_INT: {
90 const int *array = IDP_array_int_get(id_property);
91 static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
92 set_array_property<OInt32ArrayProperty, int32_t>(id_property->name, array, id_property->len);
93 break;
94 }
95 case IDP_FLOAT: {
96 const float *array = IDP_array_float_get(id_property);
97 set_array_property<OFloatArrayProperty, float>(id_property->name, array, id_property->len);
98 break;
99 }
100 case IDP_DOUBLE: {
101 const double *array = IDP_array_double_get(id_property);
102 set_array_property<ODoubleArrayProperty, double>(id_property->name, array, id_property->len);
103 break;
104 }
105 case IDP_BOOLEAN: {
106 const int8_t *array = IDP_array_bool_get(id_property);
107 set_array_property<OBoolArrayProperty, int8_t>(id_property->name, array, id_property->len);
108 break;
109 }
110 }
111}
112
113void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array)
114{
115 BLI_assert(idp_array->type == IDP_IDPARRAY);
116
117 if (idp_array->len == 0) {
118 /* Don't bother writing dataless arrays. */
119 return;
120 }
121
122 IDProperty *idp_elements = IDP_property_array_get(idp_array);
123
124#ifndef NDEBUG
125 /* Sanity check that all elements of the array have the same type.
126 * Blender should already enforce this, hence it's only used in debug mode. */
127 for (int i = 1; i < idp_array->len; i++) {
128 if (idp_elements[i].type == idp_elements[0].type) {
129 continue;
130 }
131 std::cerr << "Custom property " << idp_array->name << " has elements of varying type";
132 BLI_assert_msg(0, "Mixed type IDP_ARRAY custom property found");
133 }
134#endif
135
136 switch (idp_elements[0].type) {
137 case IDP_STRING:
138 write_idparray_of_strings(idp_array);
139 break;
140 case IDP_ARRAY:
141 write_idparray_of_numbers(idp_array);
142 break;
143 }
144}
145
146void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_array)
147{
148 BLI_assert(idp_array->type == IDP_IDPARRAY);
149 BLI_assert(idp_array->len > 0);
150
151 /* Convert to an array of std::strings, because Alembic doesn't like zero-delimited strings. */
152 IDProperty *idp_elements = IDP_property_array_get(idp_array);
153 std::vector<std::string> strings(idp_array->len);
154 for (int i = 0; i < idp_array->len; i++) {
155 BLI_assert(idp_elements[i].type == IDP_STRING);
156 strings[i] = IDP_string_get(&idp_elements[i]);
157 }
158
159 /* Alembic needs a pointer to the first value of the array. */
160 const std::string *array_of_strings = strings.data();
161 set_array_property<OStringArrayProperty, std::string>(
162 idp_array->name, array_of_strings, strings.size());
163}
164
165void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_array)
166{
167 BLI_assert(idp_array->type == IDP_IDPARRAY);
168 BLI_assert(idp_array->len > 0);
169
170 /* This must be an array of arrays. */
171 IDProperty *idp_rows = IDP_property_array_get(idp_array);
172 BLI_assert(idp_rows[0].type == IDP_ARRAY);
173
174 const int subtype = idp_rows[0].subtype;
175 if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE, IDP_BOOLEAN)) {
176 /* Non-numerical types are not supported. */
177 return;
178 }
179
180 switch (subtype) {
181 case IDP_INT:
182 static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
183 write_idparray_flattened_typed<OInt32ArrayProperty, int32_t>(idp_array);
184 break;
185 case IDP_FLOAT:
186 write_idparray_flattened_typed<OFloatArrayProperty, float>(idp_array);
187 break;
188 case IDP_DOUBLE:
189 write_idparray_flattened_typed<ODoubleArrayProperty, double>(idp_array);
190 break;
191 case IDP_BOOLEAN:
192 write_idparray_flattened_typed<OBoolArrayProperty, int8_t>(idp_array);
193 break;
194 }
195}
196
197template<typename ABCPropertyType, typename BlenderValueType>
198void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty *idp_array)
199{
200 BLI_assert(idp_array->type == IDP_IDPARRAY);
201 BLI_assert(idp_array->len > 0);
202
203 const IDProperty *idp_rows = IDP_property_array_get(idp_array);
204 BLI_assert(idp_rows[0].type == IDP_ARRAY);
205 BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE, IDP_BOOLEAN));
206
207 const uint64_t num_rows = idp_array->len;
208 std::vector<BlenderValueType> matrix_values;
209 for (size_t row_idx = 0; row_idx < num_rows; ++row_idx) {
210 const BlenderValueType *row = (BlenderValueType *)IDP_array_voidp_get(&idp_rows[row_idx]);
211 for (size_t col_idx = 0; col_idx < idp_rows[row_idx].len; col_idx++) {
212 matrix_values.push_back(row[col_idx]);
213 }
214 }
215
216 set_array_property<ABCPropertyType, BlenderValueType>(
217 idp_array->name, matrix_values.data(), matrix_values.size());
218}
219
220template<typename ABCPropertyType, typename BlenderValueType>
221void CustomPropertiesExporter::set_scalar_property(const StringRef property_name,
222 const BlenderValueType property_value)
223{
224 set_array_property<ABCPropertyType, BlenderValueType>(property_name, &property_value, 1);
225}
226
227template<typename ABCPropertyType, typename BlenderValueType>
228void CustomPropertiesExporter::set_array_property(const StringRef property_name,
229 const BlenderValueType *array_values,
230 const size_t num_array_items)
231{
232 auto create_callback = [this, property_name]() -> OArrayProperty {
233 return create_abc_property<ABCPropertyType>(property_name);
234 };
235
236 OArrayProperty array_prop = abc_properties_.lookup_or_add_cb(property_name, create_callback);
237 Alembic::Util::Dimensions array_dimensions(num_array_items);
238 ArraySample sample(array_values, array_prop.getDataType(), array_dimensions);
239 array_prop.set(sample);
240}
241
242template<typename ABCPropertyType>
243OArrayProperty CustomPropertiesExporter::create_abc_property(const StringRef property_name)
244{
245 /* Get the necessary info from our owner. */
246 OCompoundProperty abc_prop_for_custom_props = owner_->abc_prop_for_custom_props();
247 const uint32_t timesample_index = owner_->timesample_index();
248
249 /* Construct the Alembic property. */
250 ABCPropertyType abc_property(abc_prop_for_custom_props, property_name);
251 abc_property.setTimeSampling(timesample_index);
252 return abc_property;
253}
254
255} // namespace blender::io::alembic
#define IDP_float_get(prop)
#define IDP_array_voidp_get(prop)
#define IDP_int_get(prop)
#define IDP_array_double_get(prop)
#define IDP_string_get(prop)
#define IDP_double_get(prop)
#define IDP_array_bool_get(prop)
#define IDP_array_int_get(prop)
#define IDP_property_array_get(prop)
#define IDP_array_float_get(prop)
#define IDP_bool_get(prop)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
#define ELEM(...)
ID and Library types, which are fundamental for SDNA.
struct IDProperty IDProperty
@ IDP_DOUBLE
@ IDP_FLOAT
@ IDP_STRING
@ IDP_BOOLEAN
@ IDP_IDPARRAY
@ IDP_INT
@ IDP_GROUP
@ IDP_ARRAY
unsigned long long int uint64_t
ListBase group
Definition DNA_ID.h:143
int len
Definition DNA_ID.h:175
char name[64]
Definition DNA_ID.h:164
IDPropertyData data
Definition DNA_ID.h:169
char subtype
Definition DNA_ID.h:161
char type
Definition DNA_ID.h:156
i
Definition text_draw.cc:230