Blender  V3.3
abc_custom_props.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2020 Blender Foundation. All rights reserved. */
3 
8 #include "abc_custom_props.h"
9 
10 #include "abc_writer_abstract.h"
11 
12 #include <functional>
13 #include <iostream>
14 #include <memory>
15 #include <string>
16 
17 #include <Alembic/Abc/OTypedArrayProperty.h>
18 #include <Alembic/Abc/OTypedScalarProperty.h>
19 
20 #include "BKE_idprop.h"
21 #include "DNA_ID.h"
22 
23 using Alembic::Abc::ArraySample;
24 using Alembic::Abc::OArrayProperty;
25 using Alembic::Abc::OBoolArrayProperty;
26 using Alembic::Abc::OCompoundProperty;
27 using Alembic::Abc::ODoubleArrayProperty;
28 using Alembic::Abc::OFloatArrayProperty;
29 using Alembic::Abc::OInt32ArrayProperty;
30 using Alembic::Abc::OStringArrayProperty;
31 
32 namespace blender::io::alembic {
33 
35 {
36 }
37 
39 {
40  if (group == nullptr) {
41  return;
42  }
43  BLI_assert(group->type == IDP_GROUP);
44 
45  /* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */
46  LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) {
47  write(id_property);
48  }
49 }
50 
51 void CustomPropertiesExporter::write(const IDProperty *id_property)
52 {
53  BLI_assert(id_property->name[0] != '\0');
54 
55  switch (id_property->type) {
56  case IDP_STRING: {
57  /* The Alembic library doesn't accept NULL-terminated character arrays. */
58  const std::string prop_value(IDP_String(id_property), id_property->len - 1);
59  set_scalar_property<OStringArrayProperty, std::string>(id_property->name, prop_value);
60  break;
61  }
62  case IDP_INT:
63  static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
64  set_scalar_property<OInt32ArrayProperty, int32_t>(id_property->name, IDP_Int(id_property));
65  break;
66  case IDP_FLOAT:
67  set_scalar_property<OFloatArrayProperty, float>(id_property->name, IDP_Float(id_property));
68  break;
69  case IDP_DOUBLE:
70  set_scalar_property<ODoubleArrayProperty, double>(id_property->name,
71  IDP_Double(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 
82 void 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  }
104 }
105 
106 void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array)
107 {
108  BLI_assert(idp_array->type == IDP_IDPARRAY);
109 
110  if (idp_array->len == 0) {
111  /* Don't bother writing dataless arrays. */
112  return;
113  }
114 
115  IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
116 
117 #ifndef NDEBUG
118  /* Sanity check that all elements of the array have the same type.
119  * Blender should already enforce this, hence it's only used in debug mode. */
120  for (int i = 1; i < idp_array->len; i++) {
121  if (idp_elements[i].type == idp_elements[0].type) {
122  continue;
123  }
124  std::cerr << "Custom property " << idp_array->name << " has elements of varying type";
125  BLI_assert_msg(0, "Mixed type IDP_ARRAY custom property found");
126  }
127 #endif
128 
129  switch (idp_elements[0].type) {
130  case IDP_STRING:
131  write_idparray_of_strings(idp_array);
132  break;
133  case IDP_ARRAY:
134  write_idparray_of_numbers(idp_array);
135  break;
136  }
137 }
138 
139 void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_array)
140 {
141  BLI_assert(idp_array->type == IDP_IDPARRAY);
142  BLI_assert(idp_array->len > 0);
143 
144  /* Convert to an array of std::strings, because Alembic doesn't like zero-delimited strings. */
145  IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
146  std::vector<std::string> strings(idp_array->len);
147  for (int i = 0; i < idp_array->len; i++) {
148  BLI_assert(idp_elements[i].type == IDP_STRING);
149  strings[i] = IDP_String(&idp_elements[i]);
150  }
151 
152  /* Alembic needs a pointer to the first value of the array. */
153  const std::string *array_of_strings = strings.data();
154  set_array_property<OStringArrayProperty, std::string>(
155  idp_array->name, array_of_strings, strings.size());
156 }
157 
158 void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_array)
159 {
160  BLI_assert(idp_array->type == IDP_IDPARRAY);
161  BLI_assert(idp_array->len > 0);
162 
163  /* This must be an array of arrays. */
164  IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
165  BLI_assert(idp_rows[0].type == IDP_ARRAY);
166 
167  const int subtype = idp_rows[0].subtype;
168  if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
169  /* Non-numerical types are not supported. */
170  return;
171  }
172 
173  switch (subtype) {
174  case IDP_INT:
175  static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
176  write_idparray_flattened_typed<OInt32ArrayProperty, int32_t>(idp_array);
177  break;
178  case IDP_FLOAT:
179  write_idparray_flattened_typed<OFloatArrayProperty, float>(idp_array);
180  break;
181  case IDP_DOUBLE:
182  write_idparray_flattened_typed<ODoubleArrayProperty, double>(idp_array);
183  break;
184  }
185 }
186 
187 template<typename ABCPropertyType, typename BlenderValueType>
188 void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty *idp_array)
189 {
190  BLI_assert(idp_array->type == IDP_IDPARRAY);
191  BLI_assert(idp_array->len > 0);
192 
193  const IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
194  BLI_assert(idp_rows[0].type == IDP_ARRAY);
195  BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE));
196 
197  const uint64_t num_rows = idp_array->len;
198  std::vector<BlenderValueType> matrix_values;
199  for (size_t row_idx = 0; row_idx < num_rows; ++row_idx) {
200  const BlenderValueType *row = (BlenderValueType *)IDP_Array(&idp_rows[row_idx]);
201  for (size_t col_idx = 0; col_idx < idp_rows[row_idx].len; col_idx++) {
202  matrix_values.push_back(row[col_idx]);
203  }
204  }
205 
206  set_array_property<ABCPropertyType, BlenderValueType>(
207  idp_array->name, matrix_values.data(), matrix_values.size());
208 }
209 
210 template<typename ABCPropertyType, typename BlenderValueType>
211 void CustomPropertiesExporter::set_scalar_property(const StringRef property_name,
212  const BlenderValueType property_value)
213 {
214  set_array_property<ABCPropertyType, BlenderValueType>(property_name, &property_value, 1);
215 }
216 
217 template<typename ABCPropertyType, typename BlenderValueType>
218 void CustomPropertiesExporter::set_array_property(const StringRef property_name,
219  const BlenderValueType *array_values,
220  const size_t num_array_items)
221 {
222  auto create_callback = [this, property_name]() -> OArrayProperty {
223  return create_abc_property<ABCPropertyType>(property_name);
224  };
225 
226  OArrayProperty array_prop = abc_properties_.lookup_or_add_cb(property_name, create_callback);
227  Alembic::Util::Dimensions array_dimensions(num_array_items);
228  ArraySample sample(array_values, array_prop.getDataType(), array_dimensions);
229  array_prop.set(sample);
230 }
231 
232 template<typename ABCPropertyType>
233 OArrayProperty CustomPropertiesExporter::create_abc_property(const StringRef property_name)
234 {
235  /* Get the necessary info from our owner. */
236  OCompoundProperty abc_prop_for_custom_props = owner_->abc_prop_for_custom_props();
237  const uint32_t timesample_index = owner_->timesample_index();
238 
239  /* Construct the Alembic property. */
240  ABCPropertyType abc_property(abc_prop_for_custom_props, property_name);
241  abc_property.setTimeSampling(timesample_index);
242  return abc_property;
243 }
244 
245 } // namespace blender::io::alembic
#define IDP_Float(prop)
Definition: BKE_idprop.h:269
#define IDP_Int(prop)
Definition: BKE_idprop.h:244
#define IDP_String(prop)
Definition: BKE_idprop.h:271
#define IDP_Double(prop)
Definition: BKE_idprop.h:270
#define IDP_Array(prop)
Definition: BKE_idprop.h:245
#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)
Definition: BLI_listbase.h:336
#define ELEM(...)
ID and Library types, which are fundamental for sdna.
@ IDP_DOUBLE
Definition: DNA_ID.h:143
@ IDP_FLOAT
Definition: DNA_ID.h:138
@ IDP_STRING
Definition: DNA_ID.h:136
@ IDP_IDPARRAY
Definition: DNA_ID.h:144
@ IDP_INT
Definition: DNA_ID.h:137
@ IDP_GROUP
Definition: DNA_ID.h:141
@ IDP_ARRAY
Definition: DNA_ID.h:140
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition: BLI_map.hh:561
virtual Alembic::Abc::OCompoundProperty abc_prop_for_custom_props()=0
unsigned int uint32_t
Definition: stdint.h:80
signed int int32_t
Definition: stdint.h:77
unsigned __int64 uint64_t
Definition: stdint.h:90
ListBase group
Definition: DNA_ID.h:101
int len
Definition: DNA_ID.h:121
char name[64]
Definition: DNA_ID.h:111
IDPropertyData data
Definition: DNA_ID.h:117
char subtype
Definition: DNA_ID.h:108
char type
Definition: DNA_ID.h:108