diff options
-rw-r--r-- | source/blender/io/alembic/intern/abc_customdata.cc | 13 | ||||
-rw-r--r-- | source/blender/io/alembic/intern/abc_customdata.h | 10 | ||||
-rwxr-xr-x | tests/python/alembic_tests.py | 55 |
3 files changed, 73 insertions, 5 deletions
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index c5f60ac3e29..b769d00cd9c 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -146,7 +146,7 @@ const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, Custom * - (optional due to its behavior) tag as UV using Alembic::AbcGeom::SetIsUV */ static void write_uv(const OCompoundProperty &prop, - const CDStreamConfig &config, + CDStreamConfig &config, void *data, const char *name) { @@ -159,13 +159,18 @@ static void write_uv(const OCompoundProperty &prop, return; } - OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1); + std::string uv_map_name(name); + OV2fGeomParam param = config.abc_uv_maps[uv_map_name]; + if (!param.valid()) { + param = OV2fGeomParam(prop, name, true, kFacevaryingScope, 1); + } OV2fGeomParam::Sample sample(V2fArraySample(&uvs.front(), uvs.size()), UInt32ArraySample(&indices.front(), indices.size()), kFacevaryingScope); - param.set(sample); + + config.abc_uv_maps[uv_map_name] = param; } /* Convention to write Vertex Colors: @@ -219,7 +224,7 @@ static void write_mcol(const OCompoundProperty &prop, } void write_custom_data(const OCompoundProperty &prop, - const CDStreamConfig &config, + CDStreamConfig &config, CustomData *data, int data_type) { diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index 11b005eb66a..5e9300b0db6 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -27,6 +27,8 @@ #include <Alembic/Abc/All.h> #include <Alembic/AbcGeom/All.h> +#include <map> + struct CustomData; struct MLoop; struct MLoopUV; @@ -70,6 +72,12 @@ struct CDStreamConfig { const char **modifier_error_message; + /* Alembic needs Blender to keep references to C++ objects (the destructors + * finalise the writing to ABC). This map stores OV2fGeomParam objects for the + * 2nd and subsequent UV maps; the primary UV map is kept alive by the Alembic + * mesh sample itself. */ + std::map<std::string, Alembic::AbcGeom::OV2fGeomParam> abc_uv_maps; + CDStreamConfig() : mloop(NULL), totloop(0), @@ -95,7 +103,7 @@ struct CDStreamConfig { const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data); void write_custom_data(const OCompoundProperty &prop, - const CDStreamConfig &config, + CDStreamConfig &config, CustomData *data, int data_type); diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_tests.py index 9de1bc06d84..875e17e97f0 100755 --- a/tests/python/alembic_tests.py +++ b/tests/python/alembic_tests.py @@ -84,6 +84,7 @@ class AbstractAlembicTest(AbstractBlenderRunnerTest): 'uint8_t': int, 'int16_t': int, 'int32_t': int, + 'uint32_t': int, 'uint64_t': int, 'float64_t': float, 'float32_t': float, @@ -325,6 +326,60 @@ class HairParticlesExportTest(AbstractAlembicTest): self.assertIn('.faceIndices', abcprop) +class UVMapExportTest(AbstractAlembicTest): + @with_tempdir + def test_uvmap_export(self, tempdir: pathlib.Path): + """Minimal test for exporting multiple UV maps on an animated mesh. + + This covers the issue reported in T77021. + """ + basename = 'T77021-multiple-uvmaps-animated-mesh' + abc = tempdir / f'{basename}.abc' + script = f"import bpy; bpy.ops.wm.alembic_export(filepath='{abc.as_posix()}', start=1, end=1, " \ + f"renderable_only=True, visible_objects_only=True, flatten=False)" + self.run_blender(f'{basename}.blend', script) + + self.maxDiff = 1000 + + # The main UV map should be written to .geom + abcprop = self.abcprop(abc, '/Cube/CubeShape/.geom/uv') + self.assertEqual(abcprop['.vals'], [ + [0.625, 0.75], + [0.875, 0.75], + [0.875, 0.5], + [0.625, 0.5], + [0.375, 1.0], + [0.625, 1.0], + [0.375, 0.75], + [0.375, 0.25], + [0.625, 0.25], + [0.625, 0.0], + [0.375, 0.0], + [0.125, 0.75], + [0.375, 0.5], + [0.125, 0.5], + ]) + + # The second UV map should be written to .arbGeomParams + abcprop = self.abcprop(abc, '/Cube/CubeShape/.geom/.arbGeomParams/Secondary') + self.assertEqual(abcprop['.vals'], [ + [0.75, 0.375], + [0.75, 0.125], + [0.5, 0.125], + [0.5, 0.375], + [1.0, 0.625], + [1.0, 0.375], + [0.75, 0.625], + [0.25, 0.625], + [0.25, 0.375], + [0.0, 0.375], + [0.0, 0.625], + [0.75, 0.875], + [0.5, 0.625], + [0.5, 0.875], + ]) + + class LongNamesExportTest(AbstractAlembicTest): @with_tempdir def test_export_long_names(self, tempdir: pathlib.Path): |