Blender  V2.93
abc_reader_mesh.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 
21 #include "abc_reader_mesh.h"
22 #include "abc_axis_conversion.h"
23 #include "abc_reader_transform.h"
24 #include "abc_util.h"
25 
26 #include <algorithm>
27 
28 #include "MEM_guardedalloc.h"
29 
30 #include "DNA_material_types.h"
31 #include "DNA_mesh_types.h"
32 #include "DNA_meshdata_types.h"
33 #include "DNA_object_types.h"
34 
35 #include "BLI_compiler_compat.h"
36 #include "BLI_listbase.h"
37 #include "BLI_math_geom.h"
38 
39 #include "BKE_main.h"
40 #include "BKE_material.h"
41 #include "BKE_mesh.h"
42 #include "BKE_modifier.h"
43 #include "BKE_object.h"
44 
45 using Alembic::Abc::Int32ArraySamplePtr;
46 using Alembic::Abc::P3fArraySamplePtr;
47 using Alembic::Abc::PropertyHeader;
48 
49 using Alembic::AbcGeom::IC3fGeomParam;
50 using Alembic::AbcGeom::IC4fGeomParam;
51 using Alembic::AbcGeom::IFaceSet;
52 using Alembic::AbcGeom::IFaceSetSchema;
53 using Alembic::AbcGeom::IN3fGeomParam;
54 using Alembic::AbcGeom::IObject;
55 using Alembic::AbcGeom::IPolyMesh;
56 using Alembic::AbcGeom::IPolyMeshSchema;
57 using Alembic::AbcGeom::ISampleSelector;
58 using Alembic::AbcGeom::ISubD;
59 using Alembic::AbcGeom::ISubDSchema;
60 using Alembic::AbcGeom::IV2fGeomParam;
61 using Alembic::AbcGeom::kWrapExisting;
62 using Alembic::AbcGeom::N3fArraySample;
63 using Alembic::AbcGeom::N3fArraySamplePtr;
64 using Alembic::AbcGeom::UInt32ArraySamplePtr;
65 using Alembic::AbcGeom::V2fArraySamplePtr;
66 
67 namespace blender::io::alembic {
68 
69 /* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
70 
71 /* Some helpers for mesh generation */
72 namespace utils {
73 
74 static std::map<std::string, Material *> build_material_map(const Main *bmain)
75 {
76  std::map<std::string, Material *> mat_map;
78  mat_map[material->id.name + 2] = material;
79  }
80  return mat_map;
81 }
82 
83 static void assign_materials(Main *bmain,
84  Object *ob,
85  const std::map<std::string, int> &mat_index_map)
86 {
87  std::map<std::string, int>::const_iterator it;
88  for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) {
89  if (!BKE_object_material_slot_add(bmain, ob)) {
90  return;
91  }
92  }
93 
94  std::map<std::string, Material *> matname_to_material = build_material_map(bmain);
95  std::map<std::string, Material *>::iterator mat_iter;
96 
97  for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) {
98  const std::string mat_name = it->first;
99  const int mat_index = it->second;
100 
101  Material *assigned_mat;
102  mat_iter = matname_to_material.find(mat_name);
103  if (mat_iter == matname_to_material.end()) {
104  assigned_mat = BKE_material_add(bmain, mat_name.c_str());
105  matname_to_material[mat_name] = assigned_mat;
106  }
107  else {
108  assigned_mat = mat_iter->second;
109  }
110 
111  BKE_object_material_assign(bmain, ob, assigned_mat, mat_index, BKE_MAT_ASSIGN_OBDATA);
112  }
113 }
114 
115 } /* namespace utils */
116 
117 struct AbcMeshData {
118  Int32ArraySamplePtr face_indices;
119  Int32ArraySamplePtr face_counts;
120 
121  P3fArraySamplePtr positions;
122  P3fArraySamplePtr ceil_positions;
123 
124  V2fArraySamplePtr uvs;
125  UInt32ArraySamplePtr uvs_indices;
126 };
127 
128 static void read_mverts_interp(MVert *mverts,
129  const P3fArraySamplePtr &positions,
130  const P3fArraySamplePtr &ceil_positions,
131  const float weight)
132 {
133  float tmp[3];
134  for (int i = 0; i < positions->size(); i++) {
135  MVert &mvert = mverts[i];
136  const Imath::V3f &floor_pos = (*positions)[i];
137  const Imath::V3f &ceil_pos = (*ceil_positions)[i];
138 
139  interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight);
140  copy_zup_from_yup(mvert.co, tmp);
141 
142  mvert.bweight = 0;
143  }
144 }
145 
146 static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
147 {
148  MVert *mverts = config.mvert;
149  const P3fArraySamplePtr &positions = mesh_data.positions;
150 
151  if (config.use_vertex_interpolation && config.weight != 0.0f &&
152  mesh_data.ceil_positions != nullptr &&
153  mesh_data.ceil_positions->size() == positions->size()) {
154  read_mverts_interp(mverts, positions, mesh_data.ceil_positions, config.weight);
155  return;
156  }
157 
158  read_mverts(mverts, positions, nullptr);
159 }
160 
161 void read_mverts(MVert *mverts, const P3fArraySamplePtr positions, const N3fArraySamplePtr normals)
162 {
163  for (int i = 0; i < positions->size(); i++) {
164  MVert &mvert = mverts[i];
165  Imath::V3f pos_in = (*positions)[i];
166 
167  copy_zup_from_yup(mvert.co, pos_in.getValue());
168 
169  mvert.bweight = 0;
170 
171  if (normals) {
172  Imath::V3f nor_in = (*normals)[i];
173 
174  short no[3];
175  normal_float_to_short_v3(no, nor_in.getValue());
176 
177  copy_zup_from_yup(mvert.no, no);
178  }
179  }
180 }
181 
182 static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
183 {
184  MPoly *mpolys = config.mpoly;
185  MLoop *mloops = config.mloop;
186  MLoopUV *mloopuvs = config.mloopuv;
187 
188  const Int32ArraySamplePtr &face_indices = mesh_data.face_indices;
189  const Int32ArraySamplePtr &face_counts = mesh_data.face_counts;
190  const V2fArraySamplePtr &uvs = mesh_data.uvs;
191  const size_t uvs_size = uvs == nullptr ? 0 : uvs->size();
192 
193  const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices;
194 
195  const bool do_uvs = (mloopuvs && uvs && uvs_indices) &&
196  (uvs_indices->size() == face_indices->size());
197  unsigned int loop_index = 0;
198  unsigned int rev_loop_index = 0;
199  unsigned int uv_index = 0;
200  bool seen_invalid_geometry = false;
201 
202  for (int i = 0; i < face_counts->size(); i++) {
203  const int face_size = (*face_counts)[i];
204 
205  MPoly &poly = mpolys[i];
206  poly.loopstart = loop_index;
207  poly.totloop = face_size;
208 
209  /* Polygons are always assumed to be smooth-shaded. If the Alembic mesh should be flat-shaded,
210  * this is encoded in custom loop normals. See T71246. */
211  poly.flag |= ME_SMOOTH;
212 
213  /* NOTE: Alembic data is stored in the reverse order. */
214  rev_loop_index = loop_index + (face_size - 1);
215 
216  uint last_vertex_index = 0;
217  for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) {
218  MLoop &loop = mloops[rev_loop_index];
219  loop.v = (*face_indices)[loop_index];
220 
221  if (f > 0 && loop.v == last_vertex_index) {
222  /* This face is invalid, as it has consecutive loops from the same vertex. This is caused
223  * by invalid geometry in the Alembic file, such as in T76514. */
224  seen_invalid_geometry = true;
225  }
226  last_vertex_index = loop.v;
227 
228  if (do_uvs) {
229  MLoopUV &loopuv = mloopuvs[rev_loop_index];
230 
231  uv_index = (*uvs_indices)[loop_index];
232 
233  /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */
234  if (uv_index >= uvs_size) {
235  continue;
236  }
237 
238  loopuv.uv[0] = (*uvs)[uv_index][0];
239  loopuv.uv[1] = (*uvs)[uv_index][1];
240  }
241  }
242  }
243 
244  BKE_mesh_calc_edges(config.mesh, false, false);
245  if (seen_invalid_geometry) {
246  if (config.modifier_error_message) {
247  *config.modifier_error_message = "Mesh hash invalid geometry; more details on the console";
248  }
249  BKE_mesh_validate(config.mesh, true, true);
250  }
251 }
252 
253 static void process_no_normals(CDStreamConfig &config)
254 {
255  /* Absence of normals in the Alembic mesh is interpreted as 'smooth'. */
256  BKE_mesh_calc_normals(config.mesh);
257 }
258 
259 static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr)
260 {
261  size_t loop_count = loop_normals_ptr->size();
262 
263  if (loop_count == 0) {
264  process_no_normals(config);
265  return;
266  }
267 
268  Mesh *mesh = config.mesh;
269  if (loop_count != mesh->totloop) {
270  /* This happens in certain Houdini exports. When a mesh is animated and then replaced by a
271  * fluid simulation, Houdini will still write the original mesh's loop normals, but the mesh
272  * verts/loops/polys are from the simulation. In such cases the normals cannot be mapped to the
273  * mesh, so it's better to ignore them. */
274  process_no_normals(config);
275  return;
276  }
277 
278  float(*lnors)[3] = static_cast<float(*)[3]>(
279  MEM_malloc_arrayN(loop_count, sizeof(float[3]), "ABC::FaceNormals"));
280 
281  MPoly *mpoly = mesh->mpoly;
282  const N3fArraySample &loop_normals = *loop_normals_ptr;
283  int abc_index = 0;
284  for (int i = 0, e = mesh->totpoly; i < e; i++, mpoly++) {
285  /* As usual, ABC orders the loops in reverse. */
286  for (int j = mpoly->totloop - 1; j >= 0; j--, abc_index++) {
287  int blender_index = mpoly->loopstart + j;
288  copy_zup_from_yup(lnors[blender_index], loop_normals[abc_index].getValue());
289  }
290  }
291 
292  mesh->flag |= ME_AUTOSMOOTH;
294 
295  MEM_freeN(lnors);
296 }
297 
299  const N3fArraySamplePtr vertex_normals_ptr)
300 {
301  size_t normals_count = vertex_normals_ptr->size();
302  if (normals_count == 0) {
303  process_no_normals(config);
304  return;
305  }
306 
307  float(*vnors)[3] = static_cast<float(*)[3]>(
308  MEM_malloc_arrayN(normals_count, sizeof(float[3]), "ABC::VertexNormals"));
309 
310  const N3fArraySample &vertex_normals = *vertex_normals_ptr;
311  for (int index = 0; index < normals_count; index++) {
312  copy_zup_from_yup(vnors[index], vertex_normals[index].getValue());
313  }
314 
315  config.mesh->flag |= ME_AUTOSMOOTH;
317  MEM_freeN(vnors);
318 }
319 
320 static void process_normals(CDStreamConfig &config,
321  const IN3fGeomParam &normals,
322  const ISampleSelector &selector)
323 {
324  if (!normals.valid()) {
325  process_no_normals(config);
326  return;
327  }
328 
329  IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector);
330  Alembic::AbcGeom::GeometryScope scope = normals.getScope();
331 
332  switch (scope) {
333  case Alembic::AbcGeom::kFacevaryingScope: /* 'Vertex Normals' in Houdini. */
334  process_loop_normals(config, normsamp.getVals());
335  break;
336  case Alembic::AbcGeom::kVertexScope:
337  case Alembic::AbcGeom::kVaryingScope: /* 'Point Normals' in Houdini. */
338  process_vertex_normals(config, normsamp.getVals());
339  break;
340  case Alembic::AbcGeom::kConstantScope:
341  case Alembic::AbcGeom::kUniformScope:
342  case Alembic::AbcGeom::kUnknownScope:
343  process_no_normals(config);
344  break;
345  }
346 }
347 
349  AbcMeshData &abc_data,
350  const IV2fGeomParam &uv,
351  const ISampleSelector &selector)
352 {
353  if (!uv.valid()) {
354  return;
355  }
356 
357  IV2fGeomParam::Sample uvsamp;
358  uv.getIndexed(uvsamp, selector);
359 
360  abc_data.uvs = uvsamp.getVals();
361  abc_data.uvs_indices = uvsamp.getIndices();
362 
363  if (abc_data.uvs_indices->size() == config.totloop) {
364  std::string name = Alembic::Abc::GetSourceName(uv.getMetaData());
365 
366  /* According to the convention, primary UVs should have had their name
367  * set using Alembic::Abc::SetSourceName, but you can't expect everyone
368  * to follow it! :) */
369  if (name.empty()) {
370  name = uv.getName();
371  }
372 
373  void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV);
374  config.mloopuv = static_cast<MLoopUV *>(cd_ptr);
375  }
376 }
377 
378 static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type)
379 {
380  CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
381  void *cd_ptr;
382  CustomData *loopdata;
383  int numloops;
384 
385  /* unsupported custom data type -- don't do anything. */
386  if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
387  return nullptr;
388  }
389 
390  loopdata = &mesh->ldata;
391  cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name);
392  if (cd_ptr != nullptr) {
393  /* layer already exists, so just return it. */
394  return cd_ptr;
395  }
396 
397  /* Create a new layer. */
398  numloops = mesh->totloop;
399  cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
400  return cd_ptr;
401 }
402 
404  Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling,
405  size_t samples_number)
406 {
407  Alembic::AbcGeom::index_t i0, i1;
408 
409  config.weight = get_weight_and_index(config.time, time_sampling, samples_number, i0, i1);
410 
411  config.index = i0;
412  config.ceil_index = i1;
413 }
414 
415 static void read_mesh_sample(const std::string &iobject_full_name,
416  ImportSettings *settings,
417  const IPolyMeshSchema &schema,
418  const ISampleSelector &selector,
419  CDStreamConfig &config)
420 {
421  const IPolyMeshSchema::Sample sample = schema.getValue(selector);
422 
423  AbcMeshData abc_mesh_data;
424  abc_mesh_data.face_counts = sample.getFaceCounts();
425  abc_mesh_data.face_indices = sample.getFaceIndices();
426  abc_mesh_data.positions = sample.getPositions();
427 
428  get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
429 
430  if (config.weight != 0.0f) {
431  Alembic::AbcGeom::IPolyMeshSchema::Sample ceil_sample;
432  schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index));
433  abc_mesh_data.ceil_positions = ceil_sample.getPositions();
434  }
435 
436  if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
437  read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
438  }
439 
440  if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
441  read_mverts(config, abc_mesh_data);
442  }
443 
444  if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
445  read_mpolys(config, abc_mesh_data);
446  process_normals(config, schema.getNormalsParam(), selector);
447  }
448 
449  if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
450  read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
451  }
452 }
453 
454 CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation)
455 {
456  CDStreamConfig config;
457 
458  BLI_assert(mesh->mvert || mesh->totvert == 0);
459 
460  config.mesh = mesh;
461  config.mvert = mesh->mvert;
462  config.mloop = mesh->mloop;
463  config.mpoly = mesh->mpoly;
464  config.totloop = mesh->totloop;
465  config.totpoly = mesh->totpoly;
466  config.loopdata = &mesh->ldata;
468  config.use_vertex_interpolation = use_vertex_interpolation;
469 
470  return config;
471 }
472 
473 /* ************************************************************************** */
474 
475 AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings)
476  : AbcObjectReader(object, settings)
477 {
479 
480  IPolyMesh ipoly_mesh(m_iobject, kWrapExisting);
481  m_schema = ipoly_mesh.getSchema();
482 
484 }
485 
487 {
488  return m_schema.valid();
489 }
490 
491 template<class typedGeomParam>
492 bool is_valid_animated(const ICompoundProperty arbGeomParams, const PropertyHeader &prop_header)
493 {
494  if (!typedGeomParam::matches(prop_header)) {
495  return false;
496  }
497 
498  typedGeomParam geom_param(arbGeomParams, prop_header.getName());
499  return geom_param.valid() && !geom_param.isConstant();
500 }
501 
502 static bool has_animated_geom_params(const ICompoundProperty arbGeomParams)
503 {
504  if (!arbGeomParams.valid()) {
505  return false;
506  }
507 
508  const int num_props = arbGeomParams.getNumProperties();
509  for (int i = 0; i < num_props; i++) {
510  const PropertyHeader &prop_header = arbGeomParams.getPropertyHeader(i);
511 
512  /* These are interpreted as vertex colors later (see 'read_custom_data'). */
513  if (is_valid_animated<IC3fGeomParam>(arbGeomParams, prop_header)) {
514  return true;
515  }
516  if (is_valid_animated<IC4fGeomParam>(arbGeomParams, prop_header)) {
517  return true;
518  }
519  }
520 
521  return false;
522 }
523 
524 /* Specialization of #has_animations() as defined in abc_reader_object.h. */
525 template<> bool has_animations(Alembic::AbcGeom::IPolyMeshSchema &schema, ImportSettings *settings)
526 {
527  if (settings->is_sequence || !schema.isConstant()) {
528  return true;
529  }
530 
531  IV2fGeomParam uvsParam = schema.getUVsParam();
532  if (uvsParam.valid() && !uvsParam.isConstant()) {
533  return true;
534  }
535 
536  IN3fGeomParam normalsParam = schema.getNormalsParam();
537  if (normalsParam.valid() && !normalsParam.isConstant()) {
538  return true;
539  }
540 
541  ICompoundProperty arbGeomParams = schema.getArbGeomParams();
542  if (has_animated_geom_params(arbGeomParams)) {
543  return true;
544  }
545 
546  return false;
547 }
548 
549 void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
550 {
551  Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
552 
554  m_object->data = mesh;
555 
556  Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr);
557  if (read_mesh != mesh) {
558  /* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */
559  /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
560  short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
562  mesh->flag |= autosmooth;
563  }
564 
566  BKE_mesh_validate(mesh, false, false);
567  }
568 
569  readFaceSetsSample(bmain, mesh, sample_sel);
570 
571  if (has_animations(m_schema, m_settings)) {
573  }
574 }
575 
577  const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
578  const Object *const ob,
579  const char **err_str) const
580 {
581  if (!Alembic::AbcGeom::IPolyMesh::matches(alembic_header)) {
582  *err_str =
583  "Object type mismatch, Alembic object path pointed to PolyMesh when importing, but not "
584  "any more.";
585  return false;
586  }
587 
588  if (ob->type != OB_MESH) {
589  *err_str = "Object type mismatch, Alembic object path points to PolyMesh.";
590  return false;
591  }
592 
593  return true;
594 }
595 
596 bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector &sample_sel)
597 {
598  IPolyMeshSchema::Sample sample;
599  try {
600  sample = m_schema.getValue(sample_sel);
601  }
602  catch (Alembic::Util::Exception &ex) {
603  printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
604  m_iobject.getFullName().c_str(),
605  m_schema.getName().c_str(),
606  sample_sel.getRequestedTime(),
607  ex.what());
608  /* A similar error in read_mesh() would just return existing_mesh. */
609  return false;
610  }
611 
612  const P3fArraySamplePtr &positions = sample.getPositions();
613  const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
614  const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
615 
616  return positions->size() != existing_mesh->totvert ||
617  face_counts->size() != existing_mesh->totpoly ||
618  face_indices->size() != existing_mesh->totloop;
619 }
620 
622  const ISampleSelector &sample_sel,
623  int read_flag,
624  const char **err_str)
625 {
626  IPolyMeshSchema::Sample sample;
627  try {
628  sample = m_schema.getValue(sample_sel);
629  }
630  catch (Alembic::Util::Exception &ex) {
631  if (err_str != nullptr) {
632  *err_str = "Error reading mesh sample; more detail on the console";
633  }
634  printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
635  m_iobject.getFullName().c_str(),
636  m_schema.getName().c_str(),
637  sample_sel.getRequestedTime(),
638  ex.what());
639  return existing_mesh;
640  }
641 
642  const P3fArraySamplePtr &positions = sample.getPositions();
643  const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
644  const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
645 
646  /* Do some very minimal mesh validation. */
647  const int poly_count = face_counts->size();
648  const int loop_count = face_indices->size();
649  /* This is the same test as in poly_to_tri_count(). */
650  if (poly_count > 0 && loop_count < poly_count * 2) {
651  if (err_str != nullptr) {
652  *err_str = "Invalid mesh; more detail on the console";
653  }
654  printf("Alembic: invalid mesh sample for '%s/%s' at time %f, less than 2 loops per face\n",
655  m_iobject.getFullName().c_str(),
656  m_schema.getName().c_str(),
657  sample_sel.getRequestedTime());
658  return existing_mesh;
659  }
660 
661  Mesh *new_mesh = nullptr;
662 
663  /* Only read point data when streaming meshes, unless we need to create new ones. */
664  ImportSettings settings;
665  settings.read_flag |= read_flag;
666 
667  if (topology_changed(existing_mesh, sample_sel)) {
669  existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size());
670 
671  settings.read_flag |= MOD_MESHSEQ_READ_ALL;
672  }
673  else {
674  /* If the face count changed (e.g. by triangulation), only read points.
675  * This prevents crash from T49813.
676  * TODO(kevin): perhaps find a better way to do this? */
677  if (face_counts->size() != existing_mesh->totpoly ||
678  face_indices->size() != existing_mesh->totloop) {
679  settings.read_flag = MOD_MESHSEQ_READ_VERT;
680 
681  if (err_str) {
682  *err_str =
683  "Topology has changed, perhaps by triangulating the"
684  " mesh. Only vertices will be read!";
685  }
686  }
687  }
688 
689  Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh;
690  const bool use_vertex_interpolation = read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES;
691  CDStreamConfig config = get_config(mesh_to_export, use_vertex_interpolation);
692  config.time = sample_sel.getRequestedTime();
693  config.modifier_error_message = err_str;
694 
695  read_mesh_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
696 
697  if (new_mesh) {
698  /* Here we assume that the number of materials doesn't change, i.e. that
699  * the material slots that were created when the object was loaded from
700  * Alembic are still valid now. */
701  size_t num_polys = new_mesh->totpoly;
702  if (num_polys > 0) {
703  std::map<std::string, int> mat_map;
704  assign_facesets_to_mpoly(sample_sel, new_mesh->mpoly, num_polys, mat_map);
705  }
706 
707  return new_mesh;
708  }
709 
710  return existing_mesh;
711 }
712 
713 void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
714  MPoly *mpoly,
715  int totpoly,
716  std::map<std::string, int> &r_mat_map)
717 {
718  std::vector<std::string> face_sets;
719  m_schema.getFaceSetNames(face_sets);
720 
721  if (face_sets.empty()) {
722  return;
723  }
724 
725  int current_mat = 0;
726 
727  for (const std::string &grp_name : face_sets) {
728  if (r_mat_map.find(grp_name) == r_mat_map.end()) {
729  r_mat_map[grp_name] = ++current_mat;
730  }
731 
732  const int assigned_mat = r_mat_map[grp_name];
733 
734  const IFaceSet faceset = m_schema.getFaceSet(grp_name);
735 
736  if (!faceset.valid()) {
737  std::cerr << " Face set " << grp_name << " invalid for " << m_object_name << "\n";
738  continue;
739  }
740 
741  const IFaceSetSchema face_schem = faceset.getSchema();
742  const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
743  const Int32ArraySamplePtr group_faces = face_sample.getFaces();
744  const size_t num_group_faces = group_faces->size();
745 
746  for (size_t l = 0; l < num_group_faces; l++) {
747  size_t pos = (*group_faces)[l];
748 
749  if (pos >= totpoly) {
750  std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
751  break;
752  }
753 
754  MPoly &poly = mpoly[pos];
755  poly.mat_nr = assigned_mat - 1;
756  }
757  }
758 }
759 
760 void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel)
761 {
762  std::map<std::string, int> mat_map;
763  assign_facesets_to_mpoly(sample_sel, mesh->mpoly, mesh->totpoly, mat_map);
764  utils::assign_materials(bmain, m_object, mat_map);
765 }
766 
767 /* ************************************************************************** */
768 
769 BLI_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
770 {
771  for (int i = 0, e = totedge; i < e; i++) {
772  MEdge &edge = edges[i];
773 
774  if (edge.v1 == v1 && edge.v2 == v2) {
775  return &edge;
776  }
777  }
778 
779  return nullptr;
780 }
781 
782 static void read_subd_sample(const std::string &iobject_full_name,
783  ImportSettings *settings,
784  const ISubDSchema &schema,
785  const ISampleSelector &selector,
786  CDStreamConfig &config)
787 {
788  const ISubDSchema::Sample sample = schema.getValue(selector);
789 
790  AbcMeshData abc_mesh_data;
791  abc_mesh_data.face_counts = sample.getFaceCounts();
792  abc_mesh_data.face_indices = sample.getFaceIndices();
793  abc_mesh_data.positions = sample.getPositions();
794 
795  get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
796 
797  if (config.weight != 0.0f) {
798  Alembic::AbcGeom::ISubDSchema::Sample ceil_sample;
799  schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index));
800  abc_mesh_data.ceil_positions = ceil_sample.getPositions();
801  }
802 
803  if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
804  read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
805  }
806 
807  if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
808  read_mverts(config, abc_mesh_data);
809  }
810 
811  if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
812  /* Alembic's 'SubD' scheme is used to store subdivision surfaces, i.e. the pre-subdivision
813  * mesh. Currently we don't add a subdivision modifier when we load such data. This code is
814  * assuming that the subdivided surface should be smooth. */
815  read_mpolys(config, abc_mesh_data);
816  process_no_normals(config);
817  }
818 
819  if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
820  read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
821  }
822 }
823 
824 /* ************************************************************************** */
825 
826 AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings)
827  : AbcObjectReader(object, settings)
828 {
830 
831  ISubD isubd_mesh(m_iobject, kWrapExisting);
832  m_schema = isubd_mesh.getSchema();
833 
835 }
836 
838 {
839  return m_schema.valid();
840 }
841 
843  const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
844  const Object *const ob,
845  const char **err_str) const
846 {
847  if (!Alembic::AbcGeom::ISubD::matches(alembic_header)) {
848  *err_str =
849  "Object type mismatch, Alembic object path pointed to SubD when importing, but not any "
850  "more.";
851  return false;
852  }
853 
854  if (ob->type != OB_MESH) {
855  *err_str = "Object type mismatch, Alembic object path points to SubD.";
856  return false;
857  }
858 
859  return true;
860 }
861 
862 void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
863 {
864  Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
865 
867  m_object->data = mesh;
868 
869  Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr);
870  if (read_mesh != mesh) {
872  }
873 
874  ISubDSchema::Sample sample;
875  try {
876  sample = m_schema.getValue(sample_sel);
877  }
878  catch (Alembic::Util::Exception &ex) {
879  printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
880  m_iobject.getFullName().c_str(),
881  m_schema.getName().c_str(),
882  sample_sel.getRequestedTime(),
883  ex.what());
884  return;
885  }
886 
887  Int32ArraySamplePtr indices = sample.getCreaseIndices();
888  Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses();
889 
890  if (indices && sharpnesses) {
891  MEdge *edges = mesh->medge;
892  int totedge = mesh->totedge;
893 
894  for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) {
895  int v1 = (*indices)[i];
896  int v2 = (*indices)[i + 1];
897 
898  if (v2 < v1) {
899  /* It appears to be common to store edges with the smallest index first, in which case this
900  * prevents us from doing the second search below. */
901  std::swap(v1, v2);
902  }
903 
904  MEdge *edge = find_edge(edges, totedge, v1, v2);
905  if (edge == nullptr) {
906  edge = find_edge(edges, totedge, v2, v1);
907  }
908 
909  if (edge) {
910  edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]);
911  }
912  }
913 
915  }
916 
918  BKE_mesh_validate(mesh, false, false);
919  }
920 
921  if (has_animations(m_schema, m_settings)) {
923  }
924 }
925 
927  const ISampleSelector &sample_sel,
928  int read_flag,
929  const char **err_str)
930 {
931  ISubDSchema::Sample sample;
932  try {
933  sample = m_schema.getValue(sample_sel);
934  }
935  catch (Alembic::Util::Exception &ex) {
936  if (err_str != nullptr) {
937  *err_str = "Error reading mesh sample; more detail on the console";
938  }
939  printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
940  m_iobject.getFullName().c_str(),
941  m_schema.getName().c_str(),
942  sample_sel.getRequestedTime(),
943  ex.what());
944  return existing_mesh;
945  }
946 
947  const P3fArraySamplePtr &positions = sample.getPositions();
948  const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
949  const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
950 
951  Mesh *new_mesh = nullptr;
952 
953  ImportSettings settings;
954  settings.read_flag |= read_flag;
955 
956  if (existing_mesh->totvert != positions->size()) {
958  existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size());
959 
960  settings.read_flag |= MOD_MESHSEQ_READ_ALL;
961  }
962  else {
963  /* If the face count changed (e.g. by triangulation), only read points.
964  * This prevents crash from T49813.
965  * TODO(kevin): perhaps find a better way to do this? */
966  if (face_counts->size() != existing_mesh->totpoly ||
967  face_indices->size() != existing_mesh->totloop) {
968  settings.read_flag = MOD_MESHSEQ_READ_VERT;
969 
970  if (err_str) {
971  *err_str =
972  "Topology has changed, perhaps by triangulating the"
973  " mesh. Only vertices will be read!";
974  }
975  }
976  }
977 
978  /* Only read point data when streaming meshes, unless we need to create new ones. */
979  Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh;
980  const bool use_vertex_interpolation = read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES;
981  CDStreamConfig config = get_config(mesh_to_export, use_vertex_interpolation);
982  config.time = sample_sel.getRequestedTime();
983  read_subd_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
984 
985  return mesh_to_export;
986 }
987 
988 } // namespace blender::io::alembic
typedef float(TangentPoint)[2]
@ CD_DEFAULT
void * CustomData_add_layer_named(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem, const char *name)
Definition: customdata.c:2637
void * CustomData_get_layer_named(const struct CustomData *data, int type, const char *name)
Definition: customdata.c:3217
const CustomData_MeshMasks CD_MASK_MESH
Definition: customdata.c:1933
General operations, lookup, etc. for materials.
@ BKE_MAT_ASSIGN_OBDATA
Definition: BKE_material.h:72
void BKE_object_material_assign(struct Main *bmain, struct Object *ob, struct Material *ma, short act, int assign_type)
Definition: material.c:850
struct Material * BKE_material_add(struct Main *bmain, const char *name)
Definition: material.c:301
bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob)
Definition: material.c:1091
struct Mesh * BKE_mesh_new_nomain_from_template(const struct Mesh *me_src, int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
void BKE_mesh_nomain_to_mesh(struct Mesh *mesh_src, struct Mesh *mesh_dst, struct Object *ob, const struct CustomData_MeshMasks *mask, bool take_ownership)
struct Mesh * BKE_mesh_add(struct Main *bmain, const char *name)
Definition: mesh.c:849
void BKE_mesh_calc_normals(struct Mesh *me)
void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, const bool select_new_edges)
void BKE_mesh_set_custom_normals_from_vertices(struct Mesh *mesh, float(*r_custom_vertnors)[3])
void BKE_mesh_set_custom_normals(struct Mesh *mesh, float(*r_custom_loopnors)[3])
bool BKE_mesh_validate(struct Mesh *me, const bool do_verbose, const bool cddata_check_mask)
General operations, lookup, etc. for blender objects.
struct Object * BKE_object_add_only_object(struct Main *bmain, int type, const char *name) ATTR_NONNULL(1) ATTR_RETURNS_NONNULL
Definition: object.c:2193
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define BLI_INLINE
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], const float t)
Definition: math_vector.c:49
MINLINE void normal_float_to_short_v3(short r[3], const float n[3])
unsigned int uint
Definition: BLI_sys_types.h:83
#define ELEM(...)
void swap(T &a, T &b)
Definition: Common.h:33
CustomDataType
@ CD_MLOOPCOL
@ CD_MLOOPUV
@ ME_CDFLAG_EDGE_CREASE
@ ME_AUTOSMOOTH
@ ME_SMOOTH
@ MOD_MESHSEQ_READ_COLOR
@ MOD_MESHSEQ_READ_VERT
@ MOD_MESHSEQ_INTERPOLATE_VERTICES
@ MOD_MESHSEQ_READ_UV
@ MOD_MESHSEQ_READ_POLY
#define MOD_MESHSEQ_READ_ALL
Object is a sort of wrapper for general info.
@ OB_MESH
_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 const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint i1
_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 const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
struct Mesh * read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char **err_str) override
void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, const char **err_str) const override
bool topology_changed(Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel) override
AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, const char **err_str) const
struct Mesh * read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char **err_str)
AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
Material material
static ushort indices[]
static float normals[][3]
uint pos
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:48
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
static void sample(SocketReader *reader, int x, int y, float color[4])
static void assign_materials(Main *bmain, Object *ob, const std::map< std::string, int > &mat_index_map)
static std::map< std::string, Material * > build_material_map(const Main *bmain)
BLI_INLINE MEdge * find_edge(MEdge *edges, int totedge, int v1, int v2)
static void process_normals(CDStreamConfig &config, const IN3fGeomParam &normals, const ISampleSelector &selector)
static void get_weight_and_index(CDStreamConfig &config, Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling, size_t samples_number)
void get_min_max_time(const Alembic::AbcGeom::IObject &object, const Schema &schema, chrono_t &min, chrono_t &max)
Definition: abc_util.h:80
static void read_mesh_sample(const std::string &iobject_full_name, ImportSettings *settings, const IPolyMeshSchema &schema, const ISampleSelector &selector, CDStreamConfig &config)
BLI_INLINE void read_uvs_params(CDStreamConfig &config, AbcMeshData &abc_data, const IV2fGeomParam &uv, const ISampleSelector &selector)
static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr)
bool is_valid_animated(const ICompoundProperty arbGeomParams, const PropertyHeader &prop_header)
static void read_mverts_interp(MVert *mverts, const P3fArraySamplePtr &positions, const P3fArraySamplePtr &ceil_positions, const float weight)
BLI_INLINE void copy_zup_from_yup(float zup[3], const float yup[3])
static void process_vertex_normals(CDStreamConfig &config, const N3fArraySamplePtr vertex_normals_ptr)
void read_custom_data(const std::string &iobject_full_name, const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation)
static void read_subd_sample(const std::string &iobject_full_name, ImportSettings *settings, const ISubDSchema &schema, const ISampleSelector &selector, CDStreamConfig &config)
static bool has_animated_geom_params(const ICompoundProperty arbGeomParams)
static void process_no_normals(CDStreamConfig &config)
static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
static void * add_customdata_cb(Mesh *mesh, const char *name, int data_type)
bool has_animations(Alembic::AbcGeom::IPolyMeshSchema &schema, ImportSettings *settings)
char name[66]
Definition: DNA_ID.h:283
unsigned int v1
unsigned int v2
unsigned int v
short mat_nr
float co[3]
short no[3]
Definition: BKE_main.h:116
ListBase materials
Definition: BKE_main.h:152
struct MEdge * medge
struct CustomData pdata ldata
struct MVert * mvert
float size[3]
int totedge
char cd_flag
int totvert
short flag
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
void * data
Alembic::AbcGeom::index_t index
Alembic::AbcGeom::index_t ceil_index
void *(* add_customdata_cb)(Mesh *mesh, const char *name, int data_type)