Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKévin Dietrich <kevin.dietrich@mailoo.org>2021-06-15 03:11:07 +0300
committerKévin Dietrich <kevin.dietrich@mailoo.org>2021-06-16 09:22:10 +0300
commit3385c04598f210e0fb927845122d1fb5b3a8e81e (patch)
tree2d549a2da18e7bf203ff4332bfe0f581e86e9f4d
parent9fed00341e172830fe7bff42fc37e44cf48e9477 (diff)
Alembic: support reading per-vertex UV sets
This adds support for importing UV sets which are defined per vertex, instead of per face corners. Such UV sets can be generated when the mesh is split according to UV islands, or when there is only one UV island, in which cases only a single UV value can be stored per vertex since vertices will never be on a seam. Reviewed By: sybren Differential Revision: https://developer.blender.org/D11584
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc43
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.h10
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc41
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;