Blender V5.0
abc_reader_mesh.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "abc_reader_mesh.h"
10#include "abc_axis_conversion.h"
11#include "abc_customdata.h"
12#include "abc_util.h"
13
15#include "DNA_material_types.h"
16#include "DNA_modifier_types.h"
17
18#include "DNA_object_types.h"
19
20#include "BLI_compiler_compat.h"
21#include "BLI_listbase.h"
22#include "BLI_map.hh"
23#include "BLI_math_vector.h"
24#include "BLI_ordered_edge.hh"
25
26#include "BLT_translation.hh"
27
28#include "BKE_customdata.hh"
29#include "BKE_geometry_set.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_main.hh"
32#include "BKE_material.hh"
33#include "BKE_mesh.hh"
34#include "BKE_object.hh"
35#include "BKE_subdiv.hh"
36
37using Alembic::Abc::FloatArraySamplePtr;
38using Alembic::Abc::Int32ArraySamplePtr;
39using Alembic::Abc::P3fArraySamplePtr;
40using Alembic::Abc::PropertyHeader;
41using Alembic::Abc::V3fArraySamplePtr;
42
43using Alembic::AbcGeom::IC3fGeomParam;
44using Alembic::AbcGeom::IC4fGeomParam;
45using Alembic::AbcGeom::IFaceSet;
46using Alembic::AbcGeom::IFaceSetSchema;
47using Alembic::AbcGeom::IN3fGeomParam;
48using Alembic::AbcGeom::IObject;
49using Alembic::AbcGeom::IPolyMesh;
50using Alembic::AbcGeom::IPolyMeshSchema;
51using Alembic::AbcGeom::ISampleSelector;
52using Alembic::AbcGeom::ISubD;
53using Alembic::AbcGeom::ISubDSchema;
54using Alembic::AbcGeom::IV2fGeomParam;
55using Alembic::AbcGeom::kWrapExisting;
56using Alembic::AbcGeom::N3fArraySample;
57using Alembic::AbcGeom::N3fArraySamplePtr;
58using Alembic::AbcGeom::UInt32ArraySamplePtr;
59using Alembic::AbcGeom::V2fArraySamplePtr;
60
61namespace blender::io::alembic {
62
63/* NOTE: Alembic's face winding order is clockwise, to match with Renderman. */
64
65/* Some helpers for mesh generation */
66namespace utils {
67
68static std::map<std::string, Material *> build_material_map(const Main *bmain)
69{
70 std::map<std::string, Material *> mat_map;
71 LISTBASE_FOREACH (Material *, material, &bmain->materials) {
72 mat_map[material->id.name + 2] = material;
73 }
74 return mat_map;
75}
76
77static void assign_materials(Main *bmain,
78 Object *ob,
79 const std::map<std::string, int> &mat_index_map)
80{
81 std::map<std::string, int>::const_iterator it;
82 if (mat_index_map.size() > MAXMAT) {
83 return;
84 }
85
86 std::map<std::string, Material *> matname_to_material = build_material_map(bmain);
87 std::map<std::string, Material *>::iterator mat_iter;
88
89 for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) {
90 const std::string mat_name = it->first;
91 const int mat_index = it->second;
92
93 Material *assigned_mat;
94 mat_iter = matname_to_material.find(mat_name);
95 if (mat_iter == matname_to_material.end()) {
96 assigned_mat = BKE_material_add(bmain, mat_name.c_str());
97 id_us_min(&assigned_mat->id);
98 matname_to_material[mat_name] = assigned_mat;
99 }
100 else {
101 assigned_mat = mat_iter->second;
102 }
103
104 BKE_object_material_assign_single_obdata(bmain, ob, assigned_mat, mat_index);
105 }
106 if (ob->totcol > 0) {
107 ob->actcol = 1;
108 }
109}
110
111} /* namespace utils */
112
114 Int32ArraySamplePtr face_indices;
115 Int32ArraySamplePtr face_counts;
116
117 /* Optional settings for reading interpolated vertices. If present, `ceil_positions` has to be
118 * valid. */
119 std::optional<SampleInterpolationSettings> interpolation_settings;
120 P3fArraySamplePtr positions;
121 P3fArraySamplePtr ceil_positions;
122
124 V2fArraySamplePtr uvs;
125 UInt32ArraySamplePtr uvs_indices;
126};
127
128static void read_mverts_interp(float3 *vert_positions,
129 const P3fArraySamplePtr &positions,
130 const P3fArraySamplePtr &ceil_positions,
131 const double weight)
132{
133 float tmp[3];
134 for (int i = 0; i < positions->size(); i++) {
135 const Imath::V3f &floor_pos = (*positions)[i];
136 const Imath::V3f &ceil_pos = (*ceil_positions)[i];
137
138 interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), float(weight));
139 copy_zup_from_yup(vert_positions[i], tmp);
140 }
141}
142
143static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
144{
145 float3 *vert_positions = config.positions;
146 const P3fArraySamplePtr &positions = mesh_data.positions;
147
148 if (mesh_data.interpolation_settings.has_value()) {
150 mesh_data.ceil_positions != nullptr,
151 "AbcMeshData does not have ceil positions although it has some interpolation settings.");
152
153 const double weight = mesh_data.interpolation_settings->weight;
154 read_mverts_interp(vert_positions, positions, mesh_data.ceil_positions, weight);
155 config.mesh->tag_positions_changed();
156 return;
157 }
158
159 read_mverts(*config.mesh, positions, nullptr);
160}
161
162void read_mverts(Mesh &mesh, const P3fArraySamplePtr positions, const N3fArraySamplePtr normals)
163{
164 MutableSpan<float3> vert_positions = mesh.vert_positions_for_write();
165 for (int i = 0; i < positions->size(); i++) {
166 Imath::V3f pos_in = (*positions)[i];
167
168 copy_zup_from_yup(vert_positions[i], pos_in.getValue());
169 }
170 mesh.tag_positions_changed();
171
172 if (normals) {
173 Vector<float3> vert_normals(mesh.verts_num);
174 for (const int64_t i : IndexRange(normals->size())) {
175 Imath::V3f nor_in = (*normals)[i];
176 copy_zup_from_yup(vert_normals[i], nor_in.getValue());
177 }
178 bke::mesh_vert_normals_assign(mesh, std::move(vert_normals));
179 }
180}
181
182static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
183{
184 int *face_offsets = config.face_offsets;
185 int *corner_verts = config.corner_verts;
186 float2 *uv_maps = config.uv_map;
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 = (uv_maps && uvs && uvs_indices);
196 const bool do_uvs_per_loop = do_uvs && mesh_data.uv_scope == ABC_UV_SCOPE_LOOP;
197 BLI_assert(!do_uvs || mesh_data.uv_scope != ABC_UV_SCOPE_NONE);
198 uint loop_index = 0;
199 uint rev_loop_index = 0;
200 uint uv_index = 0;
201 bool seen_invalid_geometry = false;
202
203 for (int i = 0; i < face_counts->size(); i++) {
204 const int face_size = (*face_counts)[i];
205
206 face_offsets[i] = loop_index;
207
208 /* Polygons are always assumed to be smooth-shaded. If the Alembic mesh should be flat-shaded,
209 * this is encoded in custom loop normals. See #71246. */
210
211 /* NOTE: Alembic data is stored in the reverse order. */
212 rev_loop_index = loop_index + (face_size - 1);
213
214 uint last_vertex_index = 0;
215 for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) {
216 const int vert = (*face_indices)[loop_index];
217 corner_verts[rev_loop_index] = vert;
218
219 if (f > 0 && vert == last_vertex_index) {
220 /* This face is invalid, as it has consecutive loops from the same vertex. This is caused
221 * by invalid geometry in the Alembic file, such as in #76514. */
222 seen_invalid_geometry = true;
223 }
224 last_vertex_index = vert;
225
226 if (do_uvs) {
227 uv_index = (*uvs_indices)[do_uvs_per_loop ? loop_index : last_vertex_index];
228
229 /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */
230 if (uv_index >= uvs_size) {
231 continue;
232 }
233
234 uv_maps[rev_loop_index][0] = (*uvs)[uv_index][0];
235 uv_maps[rev_loop_index][1] = (*uvs)[uv_index][1];
236 }
237 }
238 }
239
240 bke::mesh_calc_edges(*config.mesh, false, false);
241 if (seen_invalid_geometry) {
242 if (config.modifier_error_message) {
243 *config.modifier_error_message = "Mesh hash invalid geometry; more details on the console";
244 }
245 BKE_mesh_validate(config.mesh, true, true);
246 }
247}
248
249static void process_no_normals(CDStreamConfig & /*config*/)
250{
251 /* Absence of normals in the Alembic mesh is interpreted as 'smooth'. */
252}
253
254static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr)
255{
256 size_t loop_count = loop_normals_ptr->size();
257
258 if (loop_count == 0) {
259 process_no_normals(config);
260 return;
261 }
262
263 Mesh *mesh = config.mesh;
264 if (loop_count != mesh->corners_num) {
265 /* This happens in certain Houdini exports. When a mesh is animated and then replaced by a
266 * fluid simulation, Houdini will still write the original mesh's loop normals, but the mesh
267 * verts/loops/faces are from the simulation. In such cases the normals cannot be mapped to the
268 * mesh, so it's better to ignore them. */
269 process_no_normals(config);
270 return;
271 }
272
273 Array<float3> corner_normals(loop_count);
274
275 const OffsetIndices faces = mesh->faces();
276 const N3fArraySample &loop_normals = *loop_normals_ptr;
277 int abc_index = 0;
278 for (int i = 0, e = mesh->faces_num; i < e; i++) {
279 const IndexRange face = faces[i];
280 /* As usual, ABC orders the loops in reverse. */
281 for (int j = face.size() - 1; j >= 0; j--, abc_index++) {
282 int blender_index = face[j];
283 copy_zup_from_yup(corner_normals[blender_index], loop_normals[abc_index].getValue());
284 }
285 }
286
287 bke::mesh_set_custom_normals(*mesh, corner_normals);
288}
289
291 const N3fArraySamplePtr vertex_normals_ptr)
292{
293 size_t normals_count = vertex_normals_ptr->size();
294 if (normals_count == 0) {
295 process_no_normals(config);
296 return;
297 }
298
299 Array<float3> vert_normals(normals_count);
300
301 const N3fArraySample &vertex_normals = *vertex_normals_ptr;
302 for (int index = 0; index < normals_count; index++) {
303 copy_zup_from_yup(vert_normals[index], vertex_normals[index].getValue());
304 }
305
306 bke::mesh_set_custom_normals_from_verts(*config.mesh, vert_normals);
307}
308
309static void process_normals(CDStreamConfig &config,
310 const IN3fGeomParam &normals,
311 const ISampleSelector &selector)
312{
313 if (!normals.valid()) {
314 process_no_normals(config);
315 return;
316 }
317
318 IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector);
319 Alembic::AbcGeom::GeometryScope scope = normals.getScope();
320
321 switch (scope) {
322 case Alembic::AbcGeom::kFacevaryingScope: /* 'Vertex Normals' in Houdini. */
323 process_loop_normals(config, normsamp.getVals());
324 break;
325 case Alembic::AbcGeom::kVertexScope:
326 case Alembic::AbcGeom::kVaryingScope: /* 'Point Normals' in Houdini. */
327 process_vertex_normals(config, normsamp.getVals());
328 break;
329 case Alembic::AbcGeom::kConstantScope:
330 case Alembic::AbcGeom::kUniformScope:
331 case Alembic::AbcGeom::kUnknownScope:
332 process_no_normals(config);
333 break;
334 }
335}
336
338 AbcMeshData &abc_data,
339 const IV2fGeomParam &uv,
340 const ISampleSelector &selector)
341{
342 if (!uv.valid()) {
343 return;
344 }
345
346 IV2fGeomParam::Sample uvsamp;
347 uv.getIndexed(uvsamp, selector);
348
349 UInt32ArraySamplePtr uvs_indices = uvsamp.getIndices();
350
351 const AbcUvScope uv_scope = get_uv_scope(uv.getScope(), config, uvs_indices);
352
353 if (uv_scope == ABC_UV_SCOPE_NONE) {
354 return;
355 }
356
357 abc_data.uv_scope = uv_scope;
358 abc_data.uvs = uvsamp.getVals();
359 abc_data.uvs_indices = uvs_indices;
360
361 std::string name = Alembic::Abc::GetSourceName(uv.getMetaData());
362
363 /* According to the convention, primary UVs should have had their name
364 * set using Alembic::Abc::SetSourceName, but you can't expect everyone
365 * to follow it! :) */
366 if (name.empty()) {
367 name = uv.getName();
368 }
369
370 void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_PROP_FLOAT2);
371 config.uv_map = static_cast<float2 *>(cd_ptr);
372}
373
374static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type)
375{
376 eCustomDataType cd_data_type = eCustomDataType(data_type);
377
378 /* unsupported custom data type -- don't do anything. */
379 if (!ELEM(cd_data_type, CD_PROP_FLOAT2, CD_PROP_BYTE_COLOR)) {
380 return nullptr;
381 }
382
384 &mesh->corner_data, cd_data_type, name, mesh->corners_num);
385 if (cd_ptr != nullptr) {
386 /* layer already exists, so just return it. */
387 return cd_ptr;
388 }
389
390 /* Create a new layer. */
391 int numloops = mesh->corners_num;
393 &mesh->corner_data, cd_data_type, CD_SET_DEFAULT, numloops, name);
394 return cd_ptr;
395}
396
397template<typename SampleType>
398static bool samples_have_same_topology(const SampleType &sample, const SampleType &ceil_sample)
399{
400 const P3fArraySamplePtr &positions = sample.getPositions();
401 const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
402 const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
403
404 const P3fArraySamplePtr &ceil_positions = ceil_sample.getPositions();
405 const Alembic::Abc::Int32ArraySamplePtr &ceil_face_indices = ceil_sample.getFaceIndices();
406 const Alembic::Abc::Int32ArraySamplePtr &ceil_face_counts = ceil_sample.getFaceCounts();
407
408 /* It the counters are different, we can be sure the topology is different. */
409 const bool different_counters = positions->size() != ceil_positions->size() ||
410 face_counts->size() != ceil_face_counts->size() ||
411 face_indices->size() != ceil_face_indices->size();
412 if (different_counters) {
413 return false;
414 }
415
416 /* Otherwise, we need to check the connectivity as files from e.g. videogrammetry may have the
417 * same face count, but different connections between faces. */
418
419 if (memcmp(face_counts->get(), ceil_face_counts->get(), face_counts->size() * sizeof(int))) {
420 return false;
421 }
422
423 if (memcmp(face_indices->get(), ceil_face_indices->get(), face_indices->size() * sizeof(int))) {
424 return false;
425 }
426
427 return true;
428}
429
430static void read_mesh_sample(const std::string &iobject_full_name,
431 ImportSettings *settings,
432 const IPolyMeshSchema &schema,
433 const ISampleSelector &selector,
434 CDStreamConfig &config)
435{
436 const IPolyMeshSchema::Sample sample = schema.getValue(selector);
437
438 AbcMeshData abc_mesh_data;
439 abc_mesh_data.face_counts = sample.getFaceCounts();
440 abc_mesh_data.face_indices = sample.getFaceIndices();
441 abc_mesh_data.positions = sample.getPositions();
442
443 const std::optional<SampleInterpolationSettings> interpolation_settings =
445 selector, schema.getTimeSampling(), schema.getNumSamples());
446
447 const bool use_vertex_interpolation = settings->read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES;
448 if (use_vertex_interpolation && interpolation_settings.has_value()) {
449 Alembic::AbcGeom::IPolyMeshSchema::Sample ceil_sample;
450 schema.get(ceil_sample, Alembic::Abc::ISampleSelector(interpolation_settings->ceil_index));
451 if (samples_have_same_topology(sample, ceil_sample)) {
452 /* Only set interpolation data if the samples are compatible. */
453 abc_mesh_data.ceil_positions = ceil_sample.getPositions();
454 abc_mesh_data.interpolation_settings = interpolation_settings;
455 }
456 }
457
458 if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
459 read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
460 }
461
462 if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
463 read_mverts(config, abc_mesh_data);
464 read_generated_coordinates(schema.getArbGeomParams(), config, selector);
465 }
466
467 if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
468 read_mpolys(config, abc_mesh_data);
469 process_normals(config, schema.getNormalsParam(), selector);
470 }
471
472 if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
473 read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
474 }
475
476 if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) {
477 V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name);
478 if (velocities) {
479 read_velocity(velocities, config, settings->velocity_scale);
480 }
481 }
482}
483
485{
486 CDStreamConfig config;
487 config.mesh = mesh;
488 config.positions = mesh->vert_positions_for_write().data();
489 config.corner_verts = mesh->corner_verts_for_write().data();
490 config.face_offsets = mesh->face_offsets_for_write().data();
491 config.totvert = mesh->verts_num;
492 config.totloop = mesh->corners_num;
493 config.faces_num = mesh->faces_num;
494 config.loopdata = &mesh->corner_data;
496
497 return config;
498}
499
500/* ************************************************************************** */
501
502AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings)
503 : AbcObjectReader(object, settings)
504{
505 m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
506
507 IPolyMesh ipoly_mesh(m_iobject, kWrapExisting);
508 m_schema = ipoly_mesh.getSchema();
509
511}
512
514{
515 return m_schema.valid();
516}
517
518template<class typedGeomParam>
519bool is_valid_animated(const ICompoundProperty arbGeomParams, const PropertyHeader &prop_header)
520{
521 if (!typedGeomParam::matches(prop_header)) {
522 return false;
523 }
524
525 typedGeomParam geom_param(arbGeomParams, prop_header.getName());
526 return geom_param.valid() && !geom_param.isConstant();
527}
528
529static bool has_animated_geom_params(const ICompoundProperty arbGeomParams)
530{
531 if (!arbGeomParams.valid()) {
532 return false;
533 }
534
535 const int num_props = arbGeomParams.getNumProperties();
536 for (int i = 0; i < num_props; i++) {
537 const PropertyHeader &prop_header = arbGeomParams.getPropertyHeader(i);
538
539 /* These are interpreted as vertex colors later (see 'read_custom_data'). */
540 if (is_valid_animated<IC3fGeomParam>(arbGeomParams, prop_header)) {
541 return true;
542 }
543 if (is_valid_animated<IC4fGeomParam>(arbGeomParams, prop_header)) {
544 return true;
545 }
546 }
547
548 return false;
549}
550
551/* Specialization of #has_animations() as defined in abc_reader_object.h. */
552template<> bool has_animations(Alembic::AbcGeom::IPolyMeshSchema &schema, ImportSettings *settings)
553{
554 if (settings->is_sequence || !schema.isConstant()) {
555 return true;
556 }
557
558 IV2fGeomParam uvsParam = schema.getUVsParam();
559 if (uvsParam.valid() && !uvsParam.isConstant()) {
560 return true;
561 }
562
563 IN3fGeomParam normalsParam = schema.getNormalsParam();
564 if (normalsParam.valid() && !normalsParam.isConstant()) {
565 return true;
566 }
567
568 ICompoundProperty arbGeomParams = schema.getArbGeomParams();
569 if (has_animated_geom_params(arbGeomParams)) {
570 return true;
571 }
572
573 return false;
574}
575
576template<> bool has_animations(Alembic::AbcGeom::ISubDSchema &schema, ImportSettings *settings)
577{
578 if (settings->is_sequence || !schema.isConstant()) {
579 return true;
580 }
581
582 IV2fGeomParam uvsParam = schema.getUVsParam();
583 if (uvsParam.valid() && !uvsParam.isConstant()) {
584 return true;
585 }
586
587 Alembic::AbcGeom::IInt32ArrayProperty creaseIndices = schema.getCreaseIndicesProperty();
588 if (creaseIndices.valid() && !creaseIndices.isConstant()) {
589 return true;
590 }
591 Alembic::AbcGeom::IFloatArrayProperty creaseSharpnesses = schema.getCreaseSharpnessesProperty();
592 if (creaseSharpnesses.valid() && !creaseSharpnesses.isConstant()) {
593 return true;
594 }
595
596 Alembic::AbcGeom::IInt32ArrayProperty cornerIndices = schema.getCornerIndicesProperty();
597 if (cornerIndices.valid() && !cornerIndices.isConstant()) {
598 return true;
599 }
600 Alembic::AbcGeom::IFloatArrayProperty cornerSharpnesses = schema.getCornerSharpnessesProperty();
601 if (cornerSharpnesses.valid() && !cornerSharpnesses.isConstant()) {
602 return true;
603 }
604
605 ICompoundProperty arbGeomParams = schema.getArbGeomParams();
606 if (has_animated_geom_params(arbGeomParams)) {
607 return true;
608 }
609
610 return false;
611}
612
613void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
614{
615 Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
616
618 m_object->data = mesh;
619
620 Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
621 if (read_mesh != mesh) {
623 }
624
625 if (m_settings->validate_meshes) {
626 BKE_mesh_validate(mesh, false, false);
627 }
628
629 readFaceSetsSample(bmain, mesh, sample_sel);
630
631 if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) {
633 }
634}
635
637 const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
638 const Object *const ob,
639 const char **r_err_str) const
640{
641 if (!Alembic::AbcGeom::IPolyMesh::matches(alembic_header)) {
642 *r_err_str = RPT_(
643 "Object type mismatch, Alembic object path pointed to PolyMesh when importing, but not "
644 "any more");
645 return false;
646 }
647
648 if (ob->type != OB_MESH) {
649 *r_err_str = RPT_("Object type mismatch, Alembic object path points to PolyMesh");
650 return false;
651 }
652
653 return true;
654}
655
656bool AbcMeshReader::topology_changed(const Mesh *existing_mesh, const ISampleSelector &sample_sel)
657{
658 IPolyMeshSchema::Sample sample;
659 try {
660 sample = m_schema.getValue(sample_sel);
661 }
662 catch (Alembic::Util::Exception &ex) {
663 printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
664 m_iobject.getFullName().c_str(),
665 m_schema.getName().c_str(),
666 sample_sel.getRequestedTime(),
667 ex.what());
668 /* A similar error in read_mesh() would just return existing_mesh. */
669 return false;
670 }
671
672 const P3fArraySamplePtr &positions = sample.getPositions();
673 const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
674 const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
675
676 /* It the counters are different, we can be sure the topology is different. */
677 const bool different_counters = positions->size() != existing_mesh->verts_num ||
678 face_counts->size() != existing_mesh->faces_num ||
679 face_indices->size() != existing_mesh->corners_num;
680 if (different_counters) {
681 return true;
682 }
683
684 /* Check first if we indeed have multiple samples, unless we read a file sequence in which case
685 * we need to do a full topology comparison. */
686 if (!m_is_reading_a_file_sequence && (m_schema.getFaceIndicesProperty().getNumSamples() == 1 &&
687 m_schema.getFaceCountsProperty().getNumSamples() == 1))
688 {
689 return false;
690 }
691
692 /* Otherwise, we need to check the connectivity as files from e.g. videogrammetry may have the
693 * same face count, but different connections between faces. */
694 uint abc_index = 0;
695
696 const int *mesh_corner_verts = existing_mesh->corner_verts().data();
697 const int *mesh_face_offsets = existing_mesh->face_offsets().data();
698
699 for (int i = 0; i < face_counts->size(); i++) {
700 if (mesh_face_offsets[i] != abc_index) {
701 return true;
702 }
703
704 const int abc_face_size = (*face_counts)[i];
705 /* NOTE: Alembic data is stored in the reverse order. */
706 uint rev_loop_index = abc_index + (abc_face_size - 1);
707 for (int f = 0; f < abc_face_size; f++, abc_index++, rev_loop_index--) {
708 const int mesh_vert = mesh_corner_verts[rev_loop_index];
709 const int abc_vert = (*face_indices)[abc_index];
710 if (mesh_vert != abc_vert) {
711 return true;
712 }
713 }
714 }
715
716 return false;
717}
718
720 const Alembic::Abc::ISampleSelector &sample_sel,
721 const int read_flag,
722 const char *velocity_name,
723 const float velocity_scale,
724 const char **r_err_str)
725{
726 Mesh *mesh = geometry_set.get_mesh_for_write();
727
728 if (mesh == nullptr) {
729 return;
730 }
731
732 Mesh *new_mesh = read_mesh(
733 mesh, sample_sel, read_flag, velocity_name, velocity_scale, r_err_str);
734
735 geometry_set.replace_mesh(new_mesh);
736}
737
739 const ISampleSelector &sample_sel,
740 const int read_flag,
741 const char *velocity_name,
742 const float velocity_scale,
743 const char **r_err_str)
744{
745 IPolyMeshSchema::Sample sample;
746 try {
747 sample = m_schema.getValue(sample_sel);
748 }
749 catch (Alembic::Util::Exception &ex) {
750 if (r_err_str != nullptr) {
751 *r_err_str = RPT_("Error reading mesh sample; more detail on the console");
752 }
753 printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
754 m_iobject.getFullName().c_str(),
755 m_schema.getName().c_str(),
756 sample_sel.getRequestedTime(),
757 ex.what());
758 return existing_mesh;
759 }
760
761 const P3fArraySamplePtr &positions = sample.getPositions();
762 const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
763 const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
764
765 /* Do some very minimal mesh validation. */
766 const int poly_count = face_counts->size();
767 const int loop_count = face_indices->size();
768 /* This is the same test as in poly_to_tri_count(). */
769 if (poly_count > 0 && loop_count < poly_count * 2) {
770 if (r_err_str != nullptr) {
771 *r_err_str = RPT_("Invalid mesh; more detail on the console");
772 }
773 printf("Alembic: invalid mesh sample for '%s/%s' at time %f, less than 2 loops per face\n",
774 m_iobject.getFullName().c_str(),
775 m_schema.getName().c_str(),
776 sample_sel.getRequestedTime());
777 return existing_mesh;
778 }
779
780 Mesh *new_mesh = nullptr;
781
782 /* Only read point data when streaming meshes, unless we need to create new ones. */
783 ImportSettings settings;
784 settings.read_flag |= read_flag;
785 settings.velocity_name = velocity_name;
786 settings.velocity_scale = velocity_scale;
787
788 if (topology_changed(existing_mesh, sample_sel)) {
790 existing_mesh, positions->size(), 0, face_counts->size(), face_indices->size());
791
793 }
794 else {
795 /* If the face count changed (e.g. by triangulation), only read points.
796 * This prevents crash from #49813.
797 * TODO(kevin): perhaps find a better way to do this? */
798 if (face_counts->size() != existing_mesh->faces_num ||
799 face_indices->size() != existing_mesh->corners_num)
800 {
802
803 if (r_err_str) {
804 *r_err_str = RPT_(
805 "Topology has changed, perhaps by triangulating the mesh. Only vertices will be "
806 "read!");
807 }
808 }
809 }
810
811 Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh;
812 CDStreamConfig config = get_config(mesh_to_export);
813 config.time = sample_sel.getRequestedTime();
814 config.modifier_error_message = r_err_str;
815
816 read_mesh_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
817
818 if (new_mesh) {
819 /* Here we assume that the number of materials doesn't change, i.e. that
820 * the material slots that were created when the object was loaded from
821 * Alembic are still valid now. */
822 size_t num_faces = new_mesh->faces_num;
823 if (num_faces > 0) {
824 std::map<std::string, int> mat_map;
825 bke::MutableAttributeAccessor attributes = new_mesh->attributes_for_write();
826 bke::SpanAttributeWriter<int> material_indices =
827 attributes.lookup_or_add_for_write_span<int>("material_index", bke::AttrDomain::Face);
828 assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map);
829 material_indices.finish();
830 }
831
832 return new_mesh;
833 }
834
835 return existing_mesh;
836}
837
838void AbcMeshReader::assign_facesets_to_material_indices(const ISampleSelector &sample_sel,
839 MutableSpan<int> material_indices,
840 std::map<std::string, int> &r_mat_map)
841{
842 std::vector<std::string> face_sets;
843 m_schema.getFaceSetNames(face_sets);
844
845 if (face_sets.empty()) {
846 return;
847 }
848
849 int current_mat = 0;
850
851 for (const std::string &grp_name : face_sets) {
852 if (r_mat_map.find(grp_name) == r_mat_map.end()) {
853 r_mat_map[grp_name] = ++current_mat;
854 }
855
856 const int assigned_mat = r_mat_map[grp_name];
857
858 const IFaceSet faceset = m_schema.getFaceSet(grp_name);
859
860 if (!faceset.valid()) {
861 std::cerr << " Face set " << grp_name << " invalid for " << m_object_name << "\n";
862 continue;
863 }
864
865 const IFaceSetSchema face_schem = faceset.getSchema();
866 const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
867 const Int32ArraySamplePtr group_faces = face_sample.getFaces();
868 const size_t num_group_faces = group_faces->size();
869
870 for (size_t l = 0; l < num_group_faces; l++) {
871 size_t pos = (*group_faces)[l];
872
873 if (pos >= material_indices.size()) {
874 std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
875 break;
876 }
877
878 material_indices[pos] = assigned_mat - 1;
879 }
880 }
881}
882
883void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel)
884{
885 std::map<std::string, int> mat_map;
886 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
887 bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
888 "material_index", bke::AttrDomain::Face);
889 assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map);
890 material_indices.finish();
891 utils::assign_materials(bmain, m_object, mat_map);
892}
893
894/* ************************************************************************** */
895
896static void read_subd_sample(const std::string &iobject_full_name,
897 ImportSettings *settings,
898 const ISubDSchema &schema,
899 const ISampleSelector &selector,
900 CDStreamConfig &config)
901{
902 const ISubDSchema::Sample sample = schema.getValue(selector);
903
904 AbcMeshData abc_mesh_data;
905 abc_mesh_data.face_counts = sample.getFaceCounts();
906 abc_mesh_data.face_indices = sample.getFaceIndices();
907 abc_mesh_data.positions = sample.getPositions();
908
909 const std::optional<SampleInterpolationSettings> interpolation_settings =
911 selector, schema.getTimeSampling(), schema.getNumSamples());
912
913 const bool use_vertex_interpolation = settings->read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES;
914 if (use_vertex_interpolation && interpolation_settings.has_value()) {
915 Alembic::AbcGeom::ISubDSchema::Sample ceil_sample;
916 schema.get(ceil_sample, Alembic::Abc::ISampleSelector(interpolation_settings->ceil_index));
917 if (samples_have_same_topology(sample, ceil_sample)) {
918 /* Only set interpolation data if the samples are compatible. */
919 abc_mesh_data.ceil_positions = ceil_sample.getPositions();
920 abc_mesh_data.interpolation_settings = interpolation_settings;
921 }
922 }
923
924 if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
925 read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
926 }
927
928 if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
929 read_mverts(config, abc_mesh_data);
930 }
931
932 if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
933 /* Alembic's 'SubD' scheme is used to store subdivision surfaces, i.e. the pre-subdivision
934 * mesh. Currently we don't add a subdivision modifier when we load such data. This code is
935 * assuming that the subdivided surface should be smooth. */
936 read_mpolys(config, abc_mesh_data);
937 process_no_normals(config);
938 }
939
940 if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
941 read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
942 }
943
944 if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) {
945 V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name);
946 if (velocities) {
947 read_velocity(velocities, config, settings->velocity_scale);
948 }
949 }
950}
951
952static void read_vertex_creases(Mesh *mesh,
953 const Int32ArraySamplePtr &indices,
954 const FloatArraySamplePtr &sharpnesses,
955 const ImportSettings *settings)
956{
957 if (!(indices && sharpnesses && indices->size() == sharpnesses->size() && indices->size() != 0))
958 {
959 return;
960 }
961
962 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
964 "crease_vert", bke::AttrDomain::Point);
965 creases.span.fill(0.0f);
966
967 const int totvert = mesh->verts_num;
968
969 for (int i = 0, v = indices->size(); i < v; ++i) {
970 const int idx = (*indices)[i];
971
972 if (idx >= totvert) {
973 continue;
974 }
975
976 const float crease = settings->blender_archive_version_prior_44 ?
977 (*sharpnesses)[i] :
978 bke::subdiv::sharpness_to_crease((*sharpnesses)[i]);
979 creases.span[idx] = std::clamp(crease, 0.0f, 1.0f);
980 }
981
982 creases.finish();
983}
984
985static void read_edge_creases(Mesh *mesh,
986 const Int32ArraySamplePtr &indices,
987 const FloatArraySamplePtr &sharpnesses,
988 const ImportSettings *settings)
989{
990 if (!(indices && sharpnesses)) {
991 return;
992 }
993
994 const Span<int2> edges = mesh->edges_for_write();
995 Map<OrderedEdge, int> edge_hash;
996 edge_hash.reserve(edges.size());
997 for (const int i : edges.index_range()) {
998 edge_hash.add(edges[i], i);
999 }
1000
1001 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1003 "crease_edge", bke::AttrDomain::Edge);
1004
1005 for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) {
1006 int v1 = (*indices)[i];
1007 int v2 = (*indices)[i + 1];
1008 const int *index = edge_hash.lookup_ptr({v1, v2});
1009 if (!index) {
1010 continue;
1011 }
1012
1013 const float crease = settings->blender_archive_version_prior_44 ?
1014 (*sharpnesses)[s] :
1015 bke::subdiv::sharpness_to_crease((*sharpnesses)[s]);
1016 creases.span[*index] = std::clamp(crease, 0.0f, 1.0f);
1017 }
1018
1019 creases.finish();
1020}
1021
1022/* ************************************************************************** */
1023
1024AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings)
1025 : AbcObjectReader(object, settings)
1026{
1027 m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
1028
1029 ISubD isubd_mesh(m_iobject, kWrapExisting);
1030 m_schema = isubd_mesh.getSchema();
1031
1033}
1034
1036{
1037 return m_schema.valid();
1038}
1039
1041 const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
1042 const Object *const ob,
1043 const char **r_err_str) const
1044{
1045 if (!Alembic::AbcGeom::ISubD::matches(alembic_header)) {
1046 *r_err_str = RPT_(
1047 "Object type mismatch, Alembic object path pointed to SubD when importing, but not any "
1048 "more");
1049 return false;
1050 }
1051
1052 if (ob->type != OB_MESH) {
1053 *r_err_str = RPT_("Object type mismatch, Alembic object path points to SubD");
1054 return false;
1055 }
1056
1057 return true;
1058}
1059
1060void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
1061{
1062 Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
1063
1065 m_object->data = mesh;
1066
1067 Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
1068 if (read_mesh != mesh) {
1069 BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object);
1070 }
1071
1072 if (m_settings->validate_meshes) {
1073 BKE_mesh_validate(mesh, false, false);
1074 }
1075
1076 if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) {
1078 }
1079}
1080
1081Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh,
1082 const ISampleSelector &sample_sel,
1083 const int read_flag,
1084 const char *velocity_name,
1085 const float velocity_scale,
1086 const char **r_err_str)
1087{
1088 ISubDSchema::Sample sample;
1089 try {
1090 sample = m_schema.getValue(sample_sel);
1091 }
1092 catch (Alembic::Util::Exception &ex) {
1093 if (r_err_str != nullptr) {
1094 *r_err_str = RPT_("Error reading mesh sample; more detail on the console");
1095 }
1096 printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
1097 m_iobject.getFullName().c_str(),
1098 m_schema.getName().c_str(),
1099 sample_sel.getRequestedTime(),
1100 ex.what());
1101 return existing_mesh;
1102 }
1103
1104 const P3fArraySamplePtr &positions = sample.getPositions();
1105 const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
1106 const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
1107
1108 Mesh *new_mesh = nullptr;
1109
1110 ImportSettings settings;
1111 settings.read_flag |= read_flag;
1112 settings.velocity_name = velocity_name;
1113 settings.velocity_scale = velocity_scale;
1114
1115 if (existing_mesh->verts_num != positions->size()) {
1117 existing_mesh, positions->size(), 0, face_counts->size(), face_indices->size());
1118
1119 settings.read_flag |= MOD_MESHSEQ_READ_ALL;
1120 }
1121 else {
1122 /* If the face count changed (e.g. by triangulation), only read points.
1123 * This prevents crash from #49813.
1124 * TODO(kevin): perhaps find a better way to do this? */
1125 if (face_counts->size() != existing_mesh->faces_num ||
1126 face_indices->size() != existing_mesh->corners_num)
1127 {
1128 settings.read_flag = MOD_MESHSEQ_READ_VERT;
1129
1130 if (r_err_str) {
1131 *r_err_str = RPT_(
1132 "Topology has changed, perhaps by triangulating the mesh. Only vertices will be "
1133 "read!");
1134 }
1135 }
1136 }
1137
1138 /* Only read point data when streaming meshes, unless we need to create new ones. */
1139 Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh;
1140 CDStreamConfig config = get_config(mesh_to_export);
1141 config.time = sample_sel.getRequestedTime();
1142 config.modifier_error_message = r_err_str;
1143 read_subd_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
1144
1146 mesh_to_export, sample.getCreaseIndices(), sample.getCreaseSharpnesses(), m_settings);
1147
1149 mesh_to_export, sample.getCornerIndices(), sample.getCornerSharpnesses(), m_settings);
1150
1151 return mesh_to_export;
1152}
1153
1155 const Alembic::Abc::ISampleSelector &sample_sel,
1156 const int read_flag,
1157 const char *velocity_name,
1158 const float velocity_scale,
1159 const char **r_err_str)
1160{
1161 Mesh *mesh = geometry_set.get_mesh_for_write();
1162
1163 if (mesh == nullptr) {
1164 return;
1165 }
1166
1167 Mesh *new_mesh = read_mesh(
1168 mesh, sample_sel, read_flag, velocity_name, velocity_scale, r_err_str);
1169
1170 geometry_set.replace_mesh(new_mesh);
1171}
1172
1173} // namespace blender::io::alembic
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
@ CD_SET_DEFAULT
void * CustomData_add_layer_named(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, blender::StringRef name)
void id_us_min(ID *id)
Definition lib_id.cc:366
General operations, lookup, etc. for materials.
void BKE_object_material_assign_single_obdata(Main *bmain, Object *ob, Material *ma, short act)
Material * BKE_material_add(Main *bmain, const char *name)
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
bool BKE_mesh_validate(Mesh *mesh, bool do_verbose, bool cddata_check_mask)
Mesh * BKE_mesh_add(Main *bmain, const char *name)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob, bool process_shape_keys=true)
General operations, lookup, etc. for blender objects.
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BLI_INLINE
#define LISTBASE_FOREACH(type, var, list)
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
unsigned int uint
#define ELEM(...)
#define RPT_(msgid)
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT2
#define MAXMAT
struct Mesh Mesh
@ 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
static NewVert * mesh_vert(VMesh *vm, int i, int j, int k)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
constexpr int64_t size() const
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
void reserve(int64_t n)
Definition BLI_map.hh:1028
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
struct Mesh * read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char *velocity_name, float velocity_scale, const char **r_err_str)
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 **r_err_str) const override
void read_geometry(bke::GeometrySet &geometry_set, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char *velocity_name, float velocity_scale, const char **r_err_str) override
bool topology_changed(const Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel) override
AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, const char **r_err_str) const override
void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override
void read_geometry(bke::GeometrySet &geometry_set, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char *velocity_name, const float velocity_scale, const char **r_err_str) override
AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
static ushort indices[]
static float normals[][3]
uint pos
#define printf(...)
static char faces[256]
BLI_INLINE float sharpness_to_crease(float sharpness)
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
void mesh_set_custom_normals_from_verts(Mesh &mesh, MutableSpan< float3 > vert_normals)
void mesh_vert_normals_assign(Mesh &mesh, Span< float3 > vert_normals)
void mesh_set_custom_normals(Mesh &mesh, MutableSpan< float3 > corner_normals)
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)
AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope, const CDStreamConfig &config, const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
static void process_normals(CDStreamConfig &config, const IN3fGeomParam &normals, const ISampleSelector &selector)
static void read_vertex_creases(Mesh *mesh, const Int32ArraySamplePtr &indices, const FloatArraySamplePtr &sharpnesses, const ImportSettings *settings)
void read_velocity(const V3fArraySamplePtr &velocities, const CDStreamConfig &config, const float velocity_scale)
void get_min_max_time(const Alembic::AbcGeom::IObject &object, const Schema &schema, chrono_t &min, chrono_t &max)
Definition abc_util.h:69
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 read_mverts_interp(float3 *vert_positions, const P3fArraySamplePtr &positions, const P3fArraySamplePtr &ceil_positions, const double weight)
static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr)
bool is_valid_animated(const ICompoundProperty arbGeomParams, const PropertyHeader &prop_header)
static CDStreamConfig get_config(Mesh *mesh)
static bool samples_have_same_topology(const SampleType &sample, const SampleType &ceil_sample)
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)
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)
V3fArraySamplePtr get_velocity_prop(const Alembic::Abc::ICompoundProperty &schema, const Alembic::AbcGeom::ISampleSelector &selector, const std::string &name)
Definition abc_util.cc:84
static void process_no_normals(CDStreamConfig &)
void read_generated_coordinates(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
bool has_animations(Alembic::AbcGeom::IPolyMeshSchema &schema, ImportSettings *settings)
std::optional< SampleInterpolationSettings > get_sample_interpolation_settings(const Alembic::AbcGeom::ISampleSelector &selector, const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling, size_t samples_number)
Definition abc_util.cc:122
static void read_edge_creases(Mesh *mesh, const Int32ArraySamplePtr &indices, const FloatArraySamplePtr &sharpnesses, const ImportSettings *settings)
static void * add_customdata_cb(Mesh *mesh, const char *name, int data_type)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
const char * name
ListBase materials
Definition BKE_main.hh:284
int corners_num
CustomData corner_data
int faces_num
int verts_num
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
std::optional< SampleInterpolationSettings > interpolation_settings
void *(* add_customdata_cb)(Mesh *mesh, const char *name, int data_type)
i
Definition text_draw.cc:230