Blender  V2.93
abc_custom_props.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation.
17  * All rights reserved.
18  */
19 
24 #include "abc_custom_props.h"
25 
26 #include "abc_writer_abstract.h"
27 
28 #include <functional>
29 #include <iostream>
30 #include <memory>
31 #include <string>
32 
33 #include <Alembic/Abc/OTypedArrayProperty.h>
34 #include <Alembic/Abc/OTypedScalarProperty.h>
35 
36 #include "BKE_idprop.h"
37 #include "DNA_ID.h"
38 
39 using Alembic::Abc::ArraySample;
40 using Alembic::Abc::OArrayProperty;
41 using Alembic::Abc::OBoolArrayProperty;
42 using Alembic::Abc::OCompoundProperty;
43 using Alembic::Abc::ODoubleArrayProperty;
44 using Alembic::Abc::OFloatArrayProperty;
45 using Alembic::Abc::OInt32ArrayProperty;
46 using Alembic::Abc::OStringArrayProperty;
47 
48 namespace blender::io::alembic {
49 
51 {
52 }
53 
55 {
56  if (group == nullptr) {
57  return;
58  }
59  BLI_assert(group->type == IDP_GROUP);
60 
61  /* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */
62  LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) {
63  if (STREQ(id_property->name, "_RNA_UI")) {
64  continue;
65  }
66  write(id_property);
67  }
68 }
69 
70 void CustomPropertiesExporter::write(const IDProperty *id_property)
71 {
72  BLI_assert(id_property->name[0] != '\0');
73 
74  switch (id_property->type) {
75  case IDP_STRING: {
76  /* The Alembic library doesn't accept NULL-terminated character arrays. */
77  const std::string prop_value(IDP_String(id_property), id_property->len - 1);
78  set_scalar_property<OStringArrayProperty, std::string>(id_property->name, prop_value);
79  break;
80  }
81  case IDP_INT:
82  static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
83  set_scalar_property<OInt32ArrayProperty, int32_t>(id_property->name, IDP_Int(id_property));
84  break;
85  case IDP_FLOAT:
86  set_scalar_property<OFloatArrayProperty, float>(id_property->name, IDP_Float(id_property));
87  break;
88  case IDP_DOUBLE:
89  set_scalar_property<ODoubleArrayProperty, double>(id_property->name,
90  IDP_Double(id_property));
91  break;
92  case IDP_ARRAY:
93  write_array(id_property);
94  break;
95  case IDP_IDPARRAY:
96  write_idparray(id_property);
97  break;
98  }
99 }
100 
101 void CustomPropertiesExporter::write_array(const IDProperty *id_property)
102 {
103  BLI_assert(id_property->type == IDP_ARRAY);
104 
105  switch (id_property->subtype) {
106  case IDP_INT: {
107  const int *array = (int *)IDP_Array(id_property);
108  static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
109  set_array_property<OInt32ArrayProperty, int32_t>(id_property->name, array, id_property->len);
110  break;
111  }
112  case IDP_FLOAT: {
113  const float *array = (float *)IDP_Array(id_property);
114  set_array_property<OFloatArrayProperty, float>(id_property->name, array, id_property->len);
115  break;
116  }
117  case IDP_DOUBLE: {
118  const double *array = (double *)IDP_Array(id_property);
119  set_array_property<ODoubleArrayProperty, double>(id_property->name, array, id_property->len);
120  break;
121  }
122  }
123 }
124 
125 void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array)
126 {
127  BLI_assert(idp_array->type == IDP_IDPARRAY);
128 
129  if (idp_array->len == 0) {
130  /* Don't bother writing dataless arrays. */
131  return;
132  }
133 
134  IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
135 
136 #ifndef NDEBUG
137  /* Sanity check that all elements of the array have the same type.
138  * Blender should already enforce this, hence it's only used in debug mode. */
139  for (int i = 1; i < idp_array->len; i++) {
140  if (idp_elements[i].type == idp_elements[0].type) {
141  continue;
142  }
143  std::cerr << "Custom property " << idp_array->name << " has elements of varying type";
144  BLI_assert(!"Mixed type IDP_ARRAY custom property found");
145  }
146 #endif
147 
148  switch (idp_elements[0].type) {
149  case IDP_STRING:
150  write_idparray_of_strings(idp_array);
151  break;
152  case IDP_ARRAY:
153  write_idparray_of_numbers(idp_array);
154  break;
155  }
156 }
157 
158 void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_array)
159 {
160  BLI_assert(idp_array->type == IDP_IDPARRAY);
161  BLI_assert(idp_array->len > 0);
162 
163  /* Convert to an array of std::strings, because Alembic doesn't like zero-delimited strings. */
164  IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
165  std::vector<std::string> strings(idp_array->len);
166  for (int i = 0; i < idp_array->len; i++) {
167  BLI_assert(idp_elements[i].type == IDP_STRING);
168  strings[i] = IDP_String(&idp_elements[i]);
169  }
170 
171  /* Alembic needs a pointer to the first value of the array. */
172  const std::string *array_of_strings = &strings[0];
173  set_array_property<OStringArrayProperty, std::string>(
174  idp_array->name, array_of_strings, strings.size());
175 }
176 
177 void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_array)
178 {
179  BLI_assert(idp_array->type == IDP_IDPARRAY);
180  BLI_assert(idp_array->len > 0);
181 
182  /* This must be an array of arrays. */
183  IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
184  BLI_assert(idp_rows[0].type == IDP_ARRAY);
185 
186  const int subtype = idp_rows[0].subtype;
187  if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
188  /* Non-numerical types are not supported. */
189  return;
190  }
191 
192  switch (subtype) {
193  case IDP_INT:
194  static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
195  write_idparray_flattened_typed<OInt32ArrayProperty, int32_t>(idp_array);
196  break;
197  case IDP_FLOAT:
198  write_idparray_flattened_typed<OFloatArrayProperty, float>(idp_array);
199  break;
200  case IDP_DOUBLE:
201  write_idparray_flattened_typed<ODoubleArrayProperty, double>(idp_array);
202  break;
203  }
204 }
205 
206 template<typename ABCPropertyType, typename BlenderValueType>
207 void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty *idp_array)
208 {
209  BLI_assert(idp_array->type == IDP_IDPARRAY);
210  BLI_assert(idp_array->len > 0);
211 
212  const IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
213  BLI_assert(idp_rows[0].type == IDP_ARRAY);
214  BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE));
215 
216  const uint64_t num_rows = idp_array->len;
217  std::vector<BlenderValueType> matrix_values;
218  for (size_t row_idx = 0; row_idx < num_rows; ++row_idx) {
219  const BlenderValueType *row = (BlenderValueType *)IDP_Array(&idp_rows[row_idx]);
220  for (size_t col_idx = 0; col_idx < idp_rows[row_idx].len; col_idx++) {
221  matrix_values.push_back(row[col_idx]);
222  }
223  }
224 
225  set_array_property<ABCPropertyType, BlenderValueType>(
226  idp_array->name, &matrix_values[0], matrix_values.size());
227 }
228 
229 template<typename ABCPropertyType, typename BlenderValueType>
230 void CustomPropertiesExporter::set_scalar_property(const StringRef property_name,
231  const BlenderValueType property_value)
232 {
233  set_array_property<ABCPropertyType, BlenderValueType>(property_name, &property_value, 1);
234 }
235 
236 template<typename ABCPropertyType, typename BlenderValueType>
237 void CustomPropertiesExporter::set_array_property(const StringRef property_name,
238  const BlenderValueType *array_values,
239  const size_t num_array_items)
240 {
241  auto create_callback = [this, property_name]() -> OArrayProperty {
242  return create_abc_property<ABCPropertyType>(property_name);
243  };
244 
245  OArrayProperty array_prop = abc_properties_.lookup_or_add_cb(property_name, create_callback);
246  Alembic::Util::Dimensions array_dimensions(num_array_items);
247  ArraySample sample(array_values, array_prop.getDataType(), array_dimensions);
248  array_prop.set(sample);
249 }
250 
251 template<typename ABCPropertyType>
252 OArrayProperty CustomPropertiesExporter::create_abc_property(const StringRef property_name)
253 {
254  /* Get the necessary info from our owner. */
255  OCompoundProperty abc_prop_for_custom_props = owner_->abc_prop_for_custom_props();
256  const uint32_t timesample_index = owner_->timesample_index();
257 
258  /* Construct the Alembic property. */
259  ABCPropertyType abc_property(abc_prop_for_custom_props, property_name);
260  abc_property.setTimeSampling(timesample_index);
261  return abc_property;
262 }
263 
264 } // namespace blender::io::alembic
#define IDP_Float(prop)
Definition: BKE_idprop.h:179
#define IDP_Int(prop)
Definition: BKE_idprop.h:154
#define IDP_String(prop)
Definition: BKE_idprop.h:181
#define IDP_Double(prop)
Definition: BKE_idprop.h:180
#define IDP_Array(prop)
Definition: BKE_idprop.h:155
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
#define ELEM(...)
#define STREQ(a, b)
ID and Library types, which are fundamental for sdna.
@ IDP_DOUBLE
Definition: DNA_ID.h:103
@ IDP_FLOAT
Definition: DNA_ID.h:99
@ IDP_STRING
Definition: DNA_ID.h:97
@ IDP_IDPARRAY
Definition: DNA_ID.h:104
@ IDP_INT
Definition: DNA_ID.h:98
@ IDP_GROUP
Definition: DNA_ID.h:101
@ IDP_ARRAY
Definition: DNA_ID.h:100
_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:575
virtual Alembic::Abc::OCompoundProperty abc_prop_for_custom_props()=0
static void sample(SocketReader *reader, int x, int y, float color[4])
unsigned int uint32_t
Definition: stdint.h:83
signed int int32_t
Definition: stdint.h:80
unsigned __int64 uint64_t
Definition: stdint.h:93
ListBase group
Definition: DNA_ID.h:64
int len
Definition: DNA_ID.h:84
char name[64]
Definition: DNA_ID.h:74
IDPropertyData data
Definition: DNA_ID.h:80
char subtype
Definition: DNA_ID.h:71
char type
Definition: DNA_ID.h:71