[Bf-blender-cvs] [a1c9d425844] blender-v2.83-release: Fix T77021: Alembic export of animated mesh with multiple UV maps fails

Sybren A. Stüvel noreply at git.blender.org
Tue May 26 16:44:14 CEST 2020


Commit: a1c9d425844c5c2299daf9a89438d164f605407c
Author: Sybren A. Stüvel
Date:   Tue May 26 16:38:47 2020 +0200
Branches: blender-v2.83-release
https://developer.blender.org/rBa1c9d425844c5c2299daf9a89438d164f605407c

Fix T77021: Alembic export of animated mesh with multiple UV maps fails

This was caused by a side-effect of our exporting code's memory
management (Alembic considers data "written" and "final" when its C++
objects go out of scope) in combination with my change in
rB65574463fa2d. I removed an "only export UVs on the first frame" clause
because it was unclear why this restriction was there. As it turns out,
it breaks the export of the 2nd and subsequent UV maps on an animated
mesh. Effectively, on every frame the Alembic library thought we want to
create a new UV map, instead of continuing to write a new frame of data
to the existing one.

This is resolved by keeping a reference to the C++ objects for the UV
maps in memory while the exporter is running.

===================================================================

M	source/blender/io/alembic/intern/abc_customdata.cc
M	source/blender/io/alembic/intern/abc_customdata.h
M	tests/python/alembic_tests.py

===================================================================

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):



More information about the Bf-blender-cvs mailing list