Blender V4.3
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
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(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, IDP_Int(id_property));
62 break;
63 case IDP_FLOAT:
64 set_scalar_property<OFloatArrayProperty, float>(id_property->name, IDP_Float(id_property));
65 break;
66 case IDP_DOUBLE:
67 set_scalar_property<ODoubleArrayProperty, double>(id_property->name,
68 IDP_Double(id_property));
69 break;
70 case IDP_BOOLEAN:
71 set_scalar_property<OBoolArrayProperty, bool>(id_property->name, IDP_Bool(id_property));
72 break;
73 case IDP_ARRAY:
74 write_array(id_property);
75 break;
76 case IDP_IDPARRAY:
77 write_idparray(id_property);
78 break;
79 }
80}
81
82void CustomPropertiesExporter::write_array(const IDProperty *id_property)
83{
84 BLI_assert(id_property->type == IDP_ARRAY);
85
86 switch (id_property->subtype) {
87 case IDP_INT: {
88 const int *array = (int *)IDP_Array(id_property);
89 static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
90 set_array_property<OInt32ArrayProperty, int32_t>(id_property->name, array, id_property->len);
91 break;
92 }
93 case IDP_FLOAT: {
94 const float *array = (float *)IDP_Array(id_property);
95 set_array_property<OFloatArrayProperty, float>(id_property->name, array, id_property->len);
96 break;
97 }
98 case IDP_DOUBLE: {
99 const double *array = (double *)IDP_Array(id_property);
100 set_array_property<ODoubleArrayProperty, double>(id_property->name, array, id_property->len);
101 break;
102 }
103 case IDP_BOOLEAN: {
104 const int8_t *array = static_cast<const int8_t *>(IDP_Array(id_property));
105 set_array_property<OBoolArrayProperty, int8_t>(id_property->name, array, id_property->len);
106 break;
107 }
108 }
109}
110
111void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array)
112{
113 BLI_assert(idp_array->type == IDP_IDPARRAY);
114
115 if (idp_array->len == 0) {
116 /* Don't bother writing dataless arrays. */
117 return;
118 }
119
120 IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
121
122#ifndef NDEBUG
123 /* Sanity check that all elements of the array have the same type.
124 * Blender should already enforce this, hence it's only used in debug mode. */
125 for (int i = 1; i < idp_array->len; i++) {
126 if (idp_elements[i].type == idp_elements[0].type) {
127 continue;
128 }
129 std::cerr << "Custom property " << idp_array->name << " has elements of varying type";
130 BLI_assert_msg(0, "Mixed type IDP_ARRAY custom property found");
131 }
132#endif
133
134 switch (idp_elements[0].type) {
135 case IDP_STRING:
136 write_idparray_of_strings(idp_array);
137 break;
138 case IDP_ARRAY:
139 write_idparray_of_numbers(idp_array);
140 break;
141 }
142}
143
144void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_array)
145{
146 BLI_assert(idp_array->type == IDP_IDPARRAY);
147 BLI_assert(idp_array->len > 0);
148
149 /* Convert to an array of std::strings, because Alembic doesn't like zero-delimited strings. */
150 IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
151 std::vector<std::string> strings(idp_array->len);
152 for (int i = 0; i < idp_array->len; i++) {
153 BLI_assert(idp_elements[i].type == IDP_STRING);
154 strings[i] = IDP_String(&idp_elements[i]);
155 }
156
157 /* Alembic needs a pointer to the first value of the array. */
158 const std::string *array_of_strings = strings.data();
159 set_array_property<OStringArrayProperty, std::string>(
160 idp_array->name, array_of_strings, strings.size());
161}
162
163void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_array)
164{
165 BLI_assert(idp_array->type == IDP_IDPARRAY);
166 BLI_assert(idp_array->len > 0);
167
168 /* This must be an array of arrays. */
169 IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
170 BLI_assert(idp_rows[0].type == IDP_ARRAY);
171
172 const int subtype = idp_rows[0].subtype;
173 if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE, IDP_BOOLEAN)) {
174 /* Non-numerical types are not supported. */
175 return;
176 }
177
178 switch (subtype) {
179 case IDP_INT:
180 static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
181 write_idparray_flattened_typed<OInt32ArrayProperty, int32_t>(idp_array);
182 break;
183 case IDP_FLOAT:
184 write_idparray_flattened_typed<OFloatArrayProperty, float>(idp_array);
185 break;
186 case IDP_DOUBLE:
187 write_idparray_flattened_typed<ODoubleArrayProperty, double>(idp_array);
188 break;
189 case IDP_BOOLEAN:
190 write_idparray_flattened_typed<OBoolArrayProperty, int8_t>(idp_array);
191 break;
192 }
193}
194
195template<typename ABCPropertyType, typename BlenderValueType>
196void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty *idp_array)
197{
198 BLI_assert(idp_array->type == IDP_IDPARRAY);
199 BLI_assert(idp_array->len > 0);
200
201 const IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
202 BLI_assert(idp_rows[0].type == IDP_ARRAY);
203 BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE, IDP_BOOLEAN));
204
205 const uint64_t num_rows = idp_array->len;
206 std::vector<BlenderValueType> matrix_values;
207 for (size_t row_idx = 0; row_idx < num_rows; ++row_idx) {
208 const BlenderValueType *row = (BlenderValueType *)IDP_Array(&idp_rows[row_idx]);
209 for (size_t col_idx = 0; col_idx < idp_rows[row_idx].len; col_idx++) {
210 matrix_values.push_back(row[col_idx]);
211 }
212 }
213
214 set_array_property<ABCPropertyType, BlenderValueType>(
215 idp_array->name, matrix_values.data(), matrix_values.size());
216}
217
218template<typename ABCPropertyType, typename BlenderValueType>
219void CustomPropertiesExporter::set_scalar_property(const StringRef property_name,
220 const BlenderValueType property_value)
221{
222 set_array_property<ABCPropertyType, BlenderValueType>(property_name, &property_value, 1);
223}
224
225template<typename ABCPropertyType, typename BlenderValueType>
226void CustomPropertiesExporter::set_array_property(const StringRef property_name,
227 const BlenderValueType *array_values,
228 const size_t num_array_items)
229{
230 auto create_callback = [this, property_name]() -> OArrayProperty {
231 return create_abc_property<ABCPropertyType>(property_name);
232 };
233
234 OArrayProperty array_prop = abc_properties_.lookup_or_add_cb(property_name, create_callback);
235 Alembic::Util::Dimensions array_dimensions(num_array_items);
236 ArraySample sample(array_values, array_prop.getDataType(), array_dimensions);
237 array_prop.set(sample);
238}
239
240template<typename ABCPropertyType>
241OArrayProperty CustomPropertiesExporter::create_abc_property(const StringRef property_name)
242{
243 /* Get the necessary info from our owner. */
244 OCompoundProperty abc_prop_for_custom_props = owner_->abc_prop_for_custom_props();
245 const uint32_t timesample_index = owner_->timesample_index();
246
247 /* Construct the Alembic property. */
248 ABCPropertyType abc_property(abc_prop_for_custom_props, property_name);
249 abc_property.setTimeSampling(timesample_index);
250 return abc_property;
251}
252
253} // namespace blender::io::alembic
#define IDP_Float(prop)
#define IDP_Int(prop)
#define IDP_Bool(prop)
#define IDP_String(prop)
#define IDP_Double(prop)
#define IDP_Array(prop)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define LISTBASE_FOREACH(type, var, list)
#define ELEM(...)
ID and Library types, which are fundamental for SDNA.
@ IDP_DOUBLE
@ IDP_FLOAT
@ IDP_STRING
@ IDP_BOOLEAN
@ IDP_IDPARRAY
@ IDP_INT
@ IDP_GROUP
@ IDP_ARRAY
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:582
virtual Alembic::Abc::OCompoundProperty abc_prop_for_custom_props()=0
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
unsigned int uint32_t
Definition stdint.h:80
signed int int32_t
Definition stdint.h:77
unsigned __int64 uint64_t
Definition stdint.h:90
signed char int8_t
Definition stdint.h:75
ListBase group
Definition DNA_ID.h:146
int len
Definition DNA_ID.h:174
char name[64]
Definition DNA_ID.h:163
IDPropertyData data
Definition DNA_ID.h:168
char subtype
Definition DNA_ID.h:159
char type
Definition DNA_ID.h:154