diff options
-rw-r--r-- | source/blender/io/alembic/intern/abc_customdata.cc | 43 | ||||
-rw-r--r-- | source/blender/io/alembic/intern/abc_customdata.h | 10 | ||||
-rw-r--r-- | source/blender/io/alembic/intern/abc_reader_mesh.cc | 41 |
3 files changed, 74 insertions, 20 deletions
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index ccf353595c9..eccb529e4d0 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -44,6 +44,7 @@ * in the write code for the conventions. */ using Alembic::AbcGeom::kFacevaryingScope; +using Alembic::AbcGeom::kVaryingScope; using Alembic::AbcGeom::kVertexScope; using Alembic::Abc::C4fArraySample; @@ -292,6 +293,7 @@ void write_custom_data(const OCompoundProperty &prop, using Alembic::Abc::C3fArraySamplePtr; using Alembic::Abc::C4fArraySamplePtr; using Alembic::Abc::PropertyHeader; +using Alembic::Abc::UInt32ArraySamplePtr; using Alembic::AbcGeom::IC3fGeomParam; using Alembic::AbcGeom::IC4fGeomParam; @@ -300,21 +302,26 @@ using Alembic::AbcGeom::IV3fGeomParam; static void read_uvs(const CDStreamConfig &config, void *data, + const AbcUvScope uv_scope, const Alembic::AbcGeom::V2fArraySamplePtr &uvs, - const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) + const UInt32ArraySamplePtr &indices) { MPoly *mpolys = config.mpoly; + MLoop *mloops = config.mloop; MLoopUV *mloopuvs = static_cast<MLoopUV *>(data); unsigned int uv_index, loop_index, rev_loop_index; + BLI_assert(uv_scope != ABC_UV_SCOPE_NONE); + const bool do_uvs_per_loop = (uv_scope == ABC_UV_SCOPE_LOOP); + for (int i = 0; i < config.totpoly; i++) { MPoly &poly = mpolys[i]; unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1; for (int f = 0; f < poly.totloop; f++) { - loop_index = poly.loopstart + f; rev_loop_index = rev_loop_offset - f; + loop_index = do_uvs_per_loop ? poly.loopstart + f : mloops[rev_loop_index].v; uv_index = (*indices)[loop_index]; const Imath::V2f &uv = (*uvs)[uv_index]; @@ -473,13 +480,17 @@ static void read_custom_data_uvs(const ICompoundProperty &prop, IV2fGeomParam::Sample sample; uv_param.getIndexed(sample, iss); - if (uv_param.getScope() != kFacevaryingScope) { + UInt32ArraySamplePtr uvs_indices = sample.getIndices(); + + const AbcUvScope uv_scope = get_uv_scope(uv_param.getScope(), config, uvs_indices); + + if (uv_scope == ABC_UV_SCOPE_NONE) { return; } void *cd_data = config.add_customdata_cb(config.mesh, prop_header.getName().c_str(), CD_MLOOPUV); - read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); + read_uvs(config, cd_data, uv_scope, sample.getVals(), uvs_indices); } void read_generated_coordinates(const ICompoundProperty &prop, @@ -559,4 +570,28 @@ void read_custom_data(const std::string &iobject_full_name, } } +/* UVs can be defined per-loop (one value per vertex per face), or per-vertex (one value per + * vertex). The first case is the most common, as this is the standard way of storing this data + * given that some vertices might be on UV seams and have multiple possible UV coordinates; the + * second case can happen when the mesh is split according to the UV islands, in which case storing + * a single UV value per vertex allows to deduplicate data and thus to reduce the file size since + * vertices are guaranteed to only have a single UV coordinate. */ +AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope, + const CDStreamConfig &config, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) +{ + if (scope == kFacevaryingScope && indices->size() == config.totloop) { + return ABC_UV_SCOPE_LOOP; + } + + /* kVaryingScope is sometimes used for vertex scopes as the values vary across the vertices. To + * be sure, one has to check the size of the data against the number of vertices, as it could + * also be a varying attribute across the faces (i.e. one value per face). */ + if ((scope == kVaryingScope || scope == kVertexScope) && indices->size() == config.totvert) { + return ABC_UV_SCOPE_VERTEX; + } + + return ABC_UV_SCOPE_NONE; +} + } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index 9ee964c0545..e9736555ead 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -122,4 +122,14 @@ void read_custom_data(const std::string &iobject_full_name, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss); +typedef enum { + ABC_UV_SCOPE_NONE, + ABC_UV_SCOPE_LOOP, + ABC_UV_SCOPE_VERTEX, +} AbcUvScope; + +AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope, + const CDStreamConfig &config, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices); + } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 11b6c1c18ca..79f34f671c7 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -121,6 +121,7 @@ struct AbcMeshData { P3fArraySamplePtr positions; P3fArraySamplePtr ceil_positions; + AbcUvScope uv_scope; V2fArraySamplePtr uvs; UInt32ArraySamplePtr uvs_indices; }; @@ -192,8 +193,9 @@ static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices; - const bool do_uvs = (mloopuvs && uvs && uvs_indices) && - (uvs_indices->size() == face_indices->size()); + const bool do_uvs = (mloopuvs && uvs && uvs_indices); + const bool do_uvs_per_loop = do_uvs && mesh_data.uv_scope == ABC_UV_SCOPE_LOOP; + BLI_assert(!do_uvs || mesh_data.uv_scope != ABC_UV_SCOPE_NONE); unsigned int loop_index = 0; unsigned int rev_loop_index = 0; unsigned int uv_index = 0; @@ -227,8 +229,7 @@ static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) if (do_uvs) { MLoopUV &loopuv = mloopuvs[rev_loop_index]; - - uv_index = (*uvs_indices)[loop_index]; + uv_index = (*uvs_indices)[do_uvs_per_loop ? loop_index : loop.v]; /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */ if (uv_index >= uvs_size) { @@ -357,22 +358,29 @@ BLI_INLINE void read_uvs_params(CDStreamConfig &config, IV2fGeomParam::Sample uvsamp; uv.getIndexed(uvsamp, selector); - abc_data.uvs = uvsamp.getVals(); - abc_data.uvs_indices = uvsamp.getIndices(); + UInt32ArraySamplePtr uvs_indices = uvsamp.getIndices(); - if (abc_data.uvs_indices->size() == config.totloop) { - std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); + const AbcUvScope uv_scope = get_uv_scope(uv.getScope(), config, uvs_indices); - /* According to the convention, primary UVs should have had their name - * set using Alembic::Abc::SetSourceName, but you can't expect everyone - * to follow it! :) */ - if (name.empty()) { - name = uv.getName(); - } + if (uv_scope == ABC_UV_SCOPE_NONE) { + return; + } - void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV); - config.mloopuv = static_cast<MLoopUV *>(cd_ptr); + abc_data.uv_scope = uv_scope; + abc_data.uvs = uvsamp.getVals(); + abc_data.uvs_indices = uvs_indices; + + std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); + + /* According to the convention, primary UVs should have had their name + * set using Alembic::Abc::SetSourceName, but you can't expect everyone + * to follow it! :) */ + if (name.empty()) { + name = uv.getName(); } + + void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV); + config.mloopuv = static_cast<MLoopUV *>(cd_ptr); } static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type) @@ -462,6 +470,7 @@ CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation) config.mvert = mesh->mvert; config.mloop = mesh->mloop; config.mpoly = mesh->mpoly; + config.totvert = mesh->totvert; config.totloop = mesh->totloop; config.totpoly = mesh->totpoly; config.loopdata = &mesh->ldata; |