Blender  V3.3
abc_customdata.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2016 Kévin Dietrich. All rights reserved. */
3 
8 #include "abc_customdata.h"
9 #include "abc_axis_conversion.h"
10 
11 #include <Alembic/AbcGeom/All.h>
12 #include <algorithm>
13 #include <unordered_map>
14 
15 #include "DNA_customdata_types.h"
16 #include "DNA_mesh_types.h"
17 #include "DNA_meshdata_types.h"
18 
19 #include "BLI_math_base.h"
20 #include "BLI_utildefines.h"
21 
22 #include "BKE_customdata.h"
23 #include "BKE_mesh.h"
24 
25 /* NOTE: for now only UVs and Vertex Colors are supported for streaming.
26  * Although Alembic only allows for a single UV layer per {I|O}Schema, and does
27  * not have a vertex color concept, there is a convention between DCCs to write
28  * such data in a way that lets other DCC know what they are for. See comments
29  * in the write code for the conventions. */
30 
31 using Alembic::AbcGeom::kFacevaryingScope;
32 using Alembic::AbcGeom::kVaryingScope;
33 using Alembic::AbcGeom::kVertexScope;
34 
35 using Alembic::Abc::C4fArraySample;
36 using Alembic::Abc::UInt32ArraySample;
37 using Alembic::Abc::V2fArraySample;
38 
39 using Alembic::AbcGeom::OC4fGeomParam;
40 using Alembic::AbcGeom::OV2fGeomParam;
41 using Alembic::AbcGeom::OV3fGeomParam;
42 namespace blender::io::alembic {
43 
44 /* ORCO, Generated Coordinates, and Reference Points ("Pref") are all terms for the same thing.
45  * Other applications (Maya, Houdini) write these to a property called "Pref". */
46 static const std::string propNameOriginalCoordinates("Pref");
47 
48 static void get_uvs(const CDStreamConfig &config,
49  std::vector<Imath::V2f> &uvs,
50  std::vector<uint32_t> &uvidx,
51  const void *cd_data)
52 {
53  const MLoopUV *mloopuv_array = static_cast<const MLoopUV *>(cd_data);
54 
55  if (!mloopuv_array) {
56  return;
57  }
58 
59  const int num_poly = config.totpoly;
60  MPoly *polygons = config.mpoly;
61  MLoop *mloop = config.mloop;
62 
63  if (!config.pack_uvs) {
64  int count = 0;
65  uvidx.resize(config.totloop);
66  uvs.resize(config.totloop);
67 
68  /* Iterate in reverse order to match exported polygons. */
69  for (int i = 0; i < num_poly; i++) {
70  MPoly &current_poly = polygons[i];
71  const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop;
72 
73  for (int j = 0; j < current_poly.totloop; j++, count++) {
74  loopuv--;
75 
76  uvidx[count] = count;
77  uvs[count][0] = loopuv->uv[0];
78  uvs[count][1] = loopuv->uv[1];
79  }
80  }
81  }
82  else {
83  /* Mapping for indexed UVs, deduplicating UV coordinates at vertices. */
84  std::vector<std::vector<uint32_t>> idx_map(config.totvert);
85  int idx_count = 0;
86 
87  for (int i = 0; i < num_poly; i++) {
88  MPoly &current_poly = polygons[i];
89  MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop;
90  const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop;
91 
92  for (int j = 0; j < current_poly.totloop; j++) {
93  looppoly--;
94  loopuv--;
95 
96  Imath::V2f uv(loopuv->uv[0], loopuv->uv[1]);
97  bool found_same = false;
98 
99  /* Find UV already in uvs array. */
100  for (uint32_t uv_idx : idx_map[looppoly->v]) {
101  if (uvs[uv_idx] == uv) {
102  found_same = true;
103  uvidx.push_back(uv_idx);
104  break;
105  }
106  }
107 
108  /* UV doesn't exists for this vertex, add it. */
109  if (!found_same) {
110  uint32_t uv_idx = idx_count++;
111  idx_map[looppoly->v].push_back(uv_idx);
112  uvidx.push_back(uv_idx);
113  uvs.push_back(uv);
114  }
115  }
116  }
117  }
118 }
119 
121 {
122  const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV);
123 
124  if (active_uvlayer < 0) {
125  return "";
126  }
127 
128  const void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer);
129 
130  get_uvs(config, sample.uvs, sample.indices, cd_data);
131 
132  return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer);
133 }
134 
135 /* Convention to write UVs:
136  * - V2fGeomParam on the arbGeomParam
137  * - set scope as face varying
138  * - (optional due to its behavior) tag as UV using Alembic::AbcGeom::SetIsUV
139  */
140 static void write_uv(const OCompoundProperty &prop,
141  CDStreamConfig &config,
142  const void *data,
143  const char *name)
144 {
145  std::vector<uint32_t> indices;
146  std::vector<Imath::V2f> uvs;
147 
148  get_uvs(config, uvs, indices, data);
149 
150  if (indices.empty() || uvs.empty()) {
151  return;
152  }
153 
154  std::string uv_map_name(name);
155  OV2fGeomParam param = config.abc_uv_maps[uv_map_name];
156 
157  if (!param.valid()) {
158  param = OV2fGeomParam(prop, name, true, kFacevaryingScope, 1);
159  }
160  OV2fGeomParam::Sample sample(V2fArraySample(&uvs.front(), uvs.size()),
161  UInt32ArraySample(&indices.front(), indices.size()),
162  kFacevaryingScope);
163  param.set(sample);
164  param.setTimeSampling(config.timesample_index);
165 
166  config.abc_uv_maps[uv_map_name] = param;
167 }
168 
169 static void get_cols(const CDStreamConfig &config,
170  std::vector<Imath::C4f> &buffer,
171  std::vector<uint32_t> &uvidx,
172  const void *cd_data)
173 {
174  const float cscale = 1.0f / 255.0f;
175  const MPoly *polys = config.mpoly;
176  const MLoop *mloops = config.mloop;
177  const MCol *cfaces = static_cast<const MCol *>(cd_data);
178 
179  buffer.reserve(config.totvert);
180  uvidx.reserve(config.totvert);
181 
182  Imath::C4f col;
183 
184  for (int i = 0; i < config.totpoly; i++) {
185  const MPoly *p = &polys[i];
186  const MCol *cface = &cfaces[p->loopstart + p->totloop];
187  const MLoop *mloop = &mloops[p->loopstart + p->totloop];
188 
189  for (int j = 0; j < p->totloop; j++) {
190  cface--;
191  mloop--;
192 
193  col[0] = cface->a * cscale;
194  col[1] = cface->r * cscale;
195  col[2] = cface->g * cscale;
196  col[3] = cface->b * cscale;
197 
198  buffer.push_back(col);
199  uvidx.push_back(buffer.size() - 1);
200  }
201  }
202 }
203 
204 /* Convention to write Vertex Colors:
205  * - C3fGeomParam/C4fGeomParam on the arbGeomParam
206  * - set scope as vertex varying
207  */
208 static void write_mcol(const OCompoundProperty &prop,
209  CDStreamConfig &config,
210  const void *data,
211  const char *name)
212 {
213  std::vector<uint32_t> indices;
214  std::vector<Imath::C4f> buffer;
215 
216  get_cols(config, buffer, indices, data);
217 
218  if (indices.empty() || buffer.empty()) {
219  return;
220  }
221 
222  std::string vcol_name(name);
223  OC4fGeomParam param = config.abc_vertex_colors[vcol_name];
224 
225  if (!param.valid()) {
226  param = OC4fGeomParam(prop, name, true, kFacevaryingScope, 1);
227  }
228 
229  OC4fGeomParam::Sample sample(C4fArraySample(&buffer.front(), buffer.size()),
230  UInt32ArraySample(&indices.front(), indices.size()),
231  kVertexScope);
232 
233  param.set(sample);
234  param.setTimeSampling(config.timesample_index);
235 
236  config.abc_vertex_colors[vcol_name] = param;
237 }
238 
239 void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config)
240 {
241  Mesh *mesh = config.mesh;
242  const void *customdata = CustomData_get_layer(&mesh->vdata, CD_ORCO);
243  if (customdata == nullptr) {
244  /* Data not available, so don't even bother creating an Alembic property for it. */
245  return;
246  }
247  const float(*orcodata)[3] = static_cast<const float(*)[3]>(customdata);
248 
249  /* Convert 3D vertices from float[3] z=up to V3f y=up. */
250  std::vector<Imath::V3f> coords(config.totvert);
251  float orco_yup[3];
252  for (int vertex_idx = 0; vertex_idx < config.totvert; vertex_idx++) {
253  copy_yup_from_zup(orco_yup, orcodata[vertex_idx]);
254  coords[vertex_idx].setValue(orco_yup[0], orco_yup[1], orco_yup[2]);
255  }
256 
257  /* ORCOs are always stored in the normalized 0..1 range in Blender, but Alembic stores them
258  * unnormalized, so we need to unnormalize (invert transform) them. */
260  mesh, reinterpret_cast<float(*)[3]>(coords.data()), mesh->totvert, true);
261 
262  if (!config.abc_orco.valid()) {
263  /* Create the Alembic property and keep a reference so future frames can reuse it. */
264  config.abc_orco = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1);
265  }
266 
267  OV3fGeomParam::Sample sample(coords, kVertexScope);
268  config.abc_orco.set(sample);
269 }
270 
271 void write_custom_data(const OCompoundProperty &prop,
272  CDStreamConfig &config,
273  CustomData *data,
274  int data_type)
275 {
276  eCustomDataType cd_data_type = static_cast<eCustomDataType>(data_type);
277 
278  if (!CustomData_has_layer(data, cd_data_type)) {
279  return;
280  }
281 
282  const int active_layer = CustomData_get_active_layer(data, cd_data_type);
283  const int tot_layers = CustomData_number_of_layers(data, cd_data_type);
284 
285  for (int i = 0; i < tot_layers; i++) {
286  const void *cd_data = CustomData_get_layer_n(data, cd_data_type, i);
287  const char *name = CustomData_get_layer_name(data, cd_data_type, i);
288 
289  if (cd_data_type == CD_MLOOPUV) {
290  /* Already exported. */
291  if (i == active_layer) {
292  continue;
293  }
294 
295  write_uv(prop, config, cd_data, name);
296  }
297  else if (cd_data_type == CD_PROP_BYTE_COLOR) {
298  write_mcol(prop, config, cd_data, name);
299  }
300  }
301 }
302 
303 /* ************************************************************************** */
304 
305 using Alembic::Abc::C3fArraySamplePtr;
306 using Alembic::Abc::C4fArraySamplePtr;
307 using Alembic::Abc::PropertyHeader;
308 using Alembic::Abc::UInt32ArraySamplePtr;
309 
310 using Alembic::AbcGeom::IC3fGeomParam;
311 using Alembic::AbcGeom::IC4fGeomParam;
312 using Alembic::AbcGeom::IV2fGeomParam;
313 using Alembic::AbcGeom::IV3fGeomParam;
314 
315 static void read_uvs(const CDStreamConfig &config,
316  void *data,
317  const AbcUvScope uv_scope,
318  const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
319  const UInt32ArraySamplePtr &indices)
320 {
321  MPoly *mpolys = config.mpoly;
322  MLoop *mloops = config.mloop;
323  MLoopUV *mloopuvs = static_cast<MLoopUV *>(data);
324 
325  unsigned int uv_index, loop_index, rev_loop_index;
326 
327  BLI_assert(uv_scope != ABC_UV_SCOPE_NONE);
328  const bool do_uvs_per_loop = (uv_scope == ABC_UV_SCOPE_LOOP);
329 
330  for (int i = 0; i < config.totpoly; i++) {
331  MPoly &poly = mpolys[i];
332  unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1;
333 
334  for (int f = 0; f < poly.totloop; f++) {
335  rev_loop_index = rev_loop_offset - f;
336  loop_index = do_uvs_per_loop ? poly.loopstart + f : mloops[rev_loop_index].v;
337  uv_index = (*indices)[loop_index];
338  const Imath::V2f &uv = (*uvs)[uv_index];
339 
340  MLoopUV &loopuv = mloopuvs[rev_loop_index];
341  loopuv.uv[0] = uv[0];
342  loopuv.uv[1] = uv[1];
343  }
344  }
345 }
346 
347 static size_t mcols_out_of_bounds_check(const size_t color_index,
348  const size_t array_size,
349  const std::string &iobject_full_name,
350  const PropertyHeader &prop_header,
351  bool &r_is_out_of_bounds,
352  bool &r_bounds_warning_given)
353 {
354  if (color_index < array_size) {
355  return color_index;
356  }
357 
358  if (!r_bounds_warning_given) {
359  std::cerr << "Alembic: color index out of bounds "
360  "reading face colors for object "
361  << iobject_full_name << ", property " << prop_header.getName() << std::endl;
362  r_bounds_warning_given = true;
363  }
364  r_is_out_of_bounds = true;
365  return 0;
366 }
367 
368 static void read_custom_data_mcols(const std::string &iobject_full_name,
369  const ICompoundProperty &arbGeomParams,
370  const PropertyHeader &prop_header,
371  const CDStreamConfig &config,
372  const Alembic::Abc::ISampleSelector &iss)
373 {
374  C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
375  C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
376  Alembic::Abc::UInt32ArraySamplePtr indices;
377  bool use_c3f_ptr;
378  bool is_facevarying;
379 
380  /* Find the correct interpretation of the data */
381  if (IC3fGeomParam::matches(prop_header)) {
382  IC3fGeomParam color_param(arbGeomParams, prop_header.getName());
383  IC3fGeomParam::Sample sample;
384  BLI_assert(STREQ("rgb", color_param.getInterpretation()));
385 
386  color_param.getIndexed(sample, iss);
387  is_facevarying = sample.getScope() == kFacevaryingScope &&
388  config.totloop == sample.getIndices()->size();
389 
390  c3f_ptr = sample.getVals();
391  indices = sample.getIndices();
392  use_c3f_ptr = true;
393  }
394  else if (IC4fGeomParam::matches(prop_header)) {
395  IC4fGeomParam color_param(arbGeomParams, prop_header.getName());
396  IC4fGeomParam::Sample sample;
397  BLI_assert(STREQ("rgba", color_param.getInterpretation()));
398 
399  color_param.getIndexed(sample, iss);
400  is_facevarying = sample.getScope() == kFacevaryingScope &&
401  config.totloop == sample.getIndices()->size();
402 
403  c4f_ptr = sample.getVals();
404  indices = sample.getIndices();
405  use_c3f_ptr = false;
406  }
407  else {
408  /* this won't happen due to the checks in read_custom_data() */
409  return;
410  }
411  BLI_assert(c3f_ptr || c4f_ptr);
412 
413  /* Read the vertex colors */
414  void *cd_data = config.add_customdata_cb(
415  config.mesh, prop_header.getName().c_str(), CD_PROP_BYTE_COLOR);
416  MCol *cfaces = static_cast<MCol *>(cd_data);
417  MPoly *mpolys = config.mpoly;
418  MLoop *mloops = config.mloop;
419 
420  size_t face_index = 0;
421  size_t color_index;
422  bool bounds_warning_given = false;
423 
424  /* The colors can go through two layers of indexing. Often the 'indices'
425  * array doesn't do anything (i.e. indices[n] = n), but when it does, it's
426  * important. Blender 2.79 writes indices incorrectly (see T53745), which
427  * is why we have to check for indices->size() > 0 */
428  bool use_dual_indexing = is_facevarying && indices->size() > 0;
429 
430  for (int i = 0; i < config.totpoly; i++) {
431  MPoly *poly = &mpolys[i];
432  MCol *cface = &cfaces[poly->loopstart + poly->totloop];
433  MLoop *mloop = &mloops[poly->loopstart + poly->totloop];
434 
435  for (int j = 0; j < poly->totloop; j++, face_index++) {
436  cface--;
437  mloop--;
438 
439  color_index = is_facevarying ? face_index : mloop->v;
440  if (use_dual_indexing) {
441  color_index = (*indices)[color_index];
442  }
443  if (use_c3f_ptr) {
444  bool is_mcols_out_of_bounds = false;
445  color_index = mcols_out_of_bounds_check(color_index,
446  c3f_ptr->size(),
447  iobject_full_name,
448  prop_header,
449  is_mcols_out_of_bounds,
450  bounds_warning_given);
451  if (is_mcols_out_of_bounds) {
452  continue;
453  }
454  const Imath::C3f &color = (*c3f_ptr)[color_index];
455  cface->a = unit_float_to_uchar_clamp(color[0]);
456  cface->r = unit_float_to_uchar_clamp(color[1]);
457  cface->g = unit_float_to_uchar_clamp(color[2]);
458  cface->b = 255;
459  }
460  else {
461  bool is_mcols_out_of_bounds = false;
462  color_index = mcols_out_of_bounds_check(color_index,
463  c4f_ptr->size(),
464  iobject_full_name,
465  prop_header,
466  is_mcols_out_of_bounds,
467  bounds_warning_given);
468  if (is_mcols_out_of_bounds) {
469  continue;
470  }
471  const Imath::C4f &color = (*c4f_ptr)[color_index];
472  cface->a = unit_float_to_uchar_clamp(color[0]);
473  cface->r = unit_float_to_uchar_clamp(color[1]);
474  cface->g = unit_float_to_uchar_clamp(color[2]);
475  cface->b = unit_float_to_uchar_clamp(color[3]);
476  }
477  }
478  }
479 }
480 
481 static void read_custom_data_uvs(const ICompoundProperty &prop,
482  const PropertyHeader &prop_header,
483  const CDStreamConfig &config,
484  const Alembic::Abc::ISampleSelector &iss)
485 {
486  IV2fGeomParam uv_param(prop, prop_header.getName());
487 
488  if (!uv_param.isIndexed()) {
489  return;
490  }
491 
492  IV2fGeomParam::Sample sample;
493  uv_param.getIndexed(sample, iss);
494 
495  UInt32ArraySamplePtr uvs_indices = sample.getIndices();
496 
497  const AbcUvScope uv_scope = get_uv_scope(uv_param.getScope(), config, uvs_indices);
498 
499  if (uv_scope == ABC_UV_SCOPE_NONE) {
500  return;
501  }
502 
503  void *cd_data = config.add_customdata_cb(config.mesh, prop_header.getName().c_str(), CD_MLOOPUV);
504 
505  read_uvs(config, cd_data, uv_scope, sample.getVals(), uvs_indices);
506 }
507 
508 void read_generated_coordinates(const ICompoundProperty &prop,
509  const CDStreamConfig &config,
510  const Alembic::Abc::ISampleSelector &iss)
511 {
512  if (!prop.valid() || prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) {
513  /* The ORCO property isn't there, so don't bother trying to process it. */
514  return;
515  }
516 
517  IV3fGeomParam param(prop, propNameOriginalCoordinates);
518  if (!param.valid() || param.isIndexed()) {
519  /* Invalid or indexed coordinates aren't supported. */
520  return;
521  }
522  if (param.getScope() != kVertexScope) {
523  /* These are original vertex coordinates, so must be vertex-scoped. */
524  return;
525  }
526 
527  IV3fGeomParam::Sample sample = param.getExpandedValue(iss);
528  Alembic::AbcGeom::V3fArraySamplePtr abc_orco = sample.getVals();
529  const size_t totvert = abc_orco.get()->size();
530  Mesh *mesh = config.mesh;
531 
532  if (totvert != mesh->totvert) {
533  /* Either the data is somehow corrupted, or we have a dynamic simulation where only the ORCOs
534  * for the first frame were exported. */
535  return;
536  }
537 
538  void *cd_data;
540  cd_data = CustomData_get_layer(&mesh->vdata, CD_ORCO);
541  }
542  else {
543  cd_data = CustomData_add_layer(&mesh->vdata, CD_ORCO, CD_CALLOC, nullptr, totvert);
544  }
545 
546  float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data);
547  for (int vertex_idx = 0; vertex_idx < totvert; ++vertex_idx) {
548  const Imath::V3f &abc_coords = (*abc_orco)[vertex_idx];
549  copy_zup_from_yup(orcodata[vertex_idx], abc_coords.getValue());
550  }
551 
552  /* ORCOs are always stored in the normalized 0..1 range in Blender, but Alembic stores them
553  * unnormalized, so we need to normalize them. */
554  BKE_mesh_orco_verts_transform(mesh, orcodata, mesh->totvert, false);
555 }
556 
557 void read_custom_data(const std::string &iobject_full_name,
558  const ICompoundProperty &prop,
559  const CDStreamConfig &config,
560  const Alembic::Abc::ISampleSelector &iss)
561 {
562  if (!prop.valid()) {
563  return;
564  }
565 
566  int num_uvs = 0;
567 
568  const size_t num_props = prop.getNumProperties();
569 
570  for (size_t i = 0; i < num_props; i++) {
571  const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i);
572 
573  /* Read UVs according to convention. */
574  if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) {
575  if (++num_uvs > MAX_MTFACE) {
576  continue;
577  }
578 
579  read_custom_data_uvs(prop, prop_header, config, iss);
580  continue;
581  }
582 
583  /* Read vertex colors according to convention. */
584  if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
585  read_custom_data_mcols(iobject_full_name, prop, prop_header, config, iss);
586  continue;
587  }
588  }
589 }
590 
591 AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope,
592  const CDStreamConfig &config,
593  const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
594 {
595  if (scope == kFacevaryingScope && indices->size() == config.totloop) {
596  return ABC_UV_SCOPE_LOOP;
597  }
598 
599  /* kVaryingScope is sometimes used for vertex scopes as the values vary across the vertices. To
600  * be sure, one has to check the size of the data against the number of vertices, as it could
601  * also be a varying attribute across the faces (i.e. one value per face). */
602  if ((ELEM(scope, kVaryingScope, kVertexScope)) && indices->size() == config.totvert) {
603  return ABC_UV_SCOPE_VERTEX;
604  }
605 
606  return ABC_UV_SCOPE_NONE;
607 }
608 
609 } // namespace blender::io::alembic
typedef float(TangentPoint)[2]
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_layer_name(const struct CustomData *data, int type, int n)
int CustomData_number_of_layers(const struct CustomData *data, int type)
@ CD_CALLOC
bool CustomData_has_layer(const struct CustomData *data, int type)
int CustomData_get_active_layer(const struct CustomData *data, int type)
void * CustomData_get_layer_n(const struct CustomData *data, int type, int n)
void * CustomData_get_layer(const struct CustomData *data, int type)
void * CustomData_add_layer(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem)
Definition: customdata.cc:2776
void BKE_mesh_orco_verts_transform(struct Mesh *me, float(*orco)[3], int totvert, int invert)
Definition: mesh.cc:1331
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define ELEM(...)
#define STREQ(a, b)
eCustomDataType
@ CD_PROP_BYTE_COLOR
@ CD_MLOOPUV
#define MAX_MTFACE
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
uint col
int count
ccl_global float * buffer
ccl_gpu_kernel_postfix int ccl_global int * indices
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope, const CDStreamConfig &config, const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
BLI_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
const char * get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data)
static void write_uv(const OCompoundProperty &prop, CDStreamConfig &config, const void *data, const char *name)
static void read_custom_data_mcols(const std::string &iobject_full_name, const ICompoundProperty &arbGeomParams, const PropertyHeader &prop_header, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
void write_custom_data(const OCompoundProperty &prop, CDStreamConfig &config, CustomData *data, int data_type)
static void write_mcol(const OCompoundProperty &prop, CDStreamConfig &config, const void *data, const char *name)
BLI_INLINE void copy_zup_from_yup(float zup[3], const float yup[3])
void read_custom_data(const std::string &iobject_full_name, const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
static void get_uvs(const CDStreamConfig &config, std::vector< Imath::V2f > &uvs, std::vector< uint32_t > &uvidx, const void *cd_data)
static const std::string propNameOriginalCoordinates("Pref")
static void read_custom_data_uvs(const ICompoundProperty &prop, const PropertyHeader &prop_header, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
void read_generated_coordinates(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
static size_t mcols_out_of_bounds_check(const size_t color_index, const size_t array_size, const std::string &iobject_full_name, const PropertyHeader &prop_header, bool &r_is_out_of_bounds, bool &r_bounds_warning_given)
void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config)
static void get_cols(const CDStreamConfig &config, std::vector< Imath::C4f > &buffer, std::vector< uint32_t > &uvidx, const void *cd_data)
static void read_uvs(const CDStreamConfig &config, void *data, const AbcUvScope uv_scope, const Alembic::AbcGeom::V2fArraySamplePtr &uvs, const UInt32ArraySamplePtr &indices)
unsigned int uint32_t
Definition: stdint.h:80
unsigned char r
unsigned char a
unsigned char g
unsigned char b
unsigned int v
CustomData vdata
int totvert
std::map< std::string, Alembic::AbcGeom::OC4fGeomParam > abc_vertex_colors
void *(* add_customdata_cb)(Mesh *mesh, const char *name, int data_type)
Alembic::AbcGeom::OV3fGeomParam abc_orco
std::map< std::string, Alembic::AbcGeom::OV2fGeomParam > abc_uv_maps