[Bf-blender-cvs] [7b25ffb618d] master: Fix T51534: Alembic: added support for face-varying vertex colours

Sybren A. Stüvel noreply at git.blender.org
Tue May 23 17:29:15 CEST 2017


Commit: 7b25ffb618dd7509d425f7a5891c64d4a3668743
Author: Sybren A. Stüvel
Date:   Tue May 23 17:27:09 2017 +0200
Branches: master
https://developer.blender.org/rB7b25ffb618dd7509d425f7a5891c64d4a3668743

Fix T51534: Alembic: added support for face-varying vertex colours

Houdini writes vertex data in a different format than Blender does; Houdini
uses "face-varying scope", which means that the vertex colours are indexed
by an ever-increasing number over all vertices of all faces instead of the
vertex index.

I've also merged the read_custom_data_mcols() and read_mcols() functions,
because the latter was only called from the former, and the changes in this
commit would add yet more function parameters to pass.

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

M	source/blender/alembic/intern/abc_customdata.cc
M	tests/python/bl_alembic_import_test.py

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

diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc
index c42e5f808b5..1d2bc689027 100644
--- a/source/blender/alembic/intern/abc_customdata.cc
+++ b/source/blender/alembic/intern/abc_customdata.cc
@@ -227,44 +227,6 @@ using Alembic::AbcGeom::IC3fGeomParam;
 using Alembic::AbcGeom::IC4fGeomParam;
 using Alembic::AbcGeom::IV2fGeomParam;
 
-static void read_mcols(const CDStreamConfig &config, void *data,
-                       const C3fArraySamplePtr &c3f_ptr,
-                       const C4fArraySamplePtr &c4f_ptr)
-{
-	MCol *cfaces = static_cast<MCol *>(data);
-	MPoly *polys = config.mpoly;
-	MLoop *mloops = config.mloop;
-
-	/* Either one or the other should be given. */
-	BLI_assert(c3f_ptr || c4f_ptr);
-	const bool use_c3f_ptr = (c3f_ptr.get() != nullptr);
-
-	for (int i = 0; i < config.totpoly; ++i) {
-		MPoly *p = &polys[i];
-		MCol *cface = &cfaces[p->loopstart + p->totloop];
-		MLoop *mloop = &mloops[p->loopstart + p->totloop];
-
-		for (int j = 0; j < p->totloop; ++j) {
-			cface--;
-			mloop--;
-
-			if (use_c3f_ptr) {
-				const Imath::C3f &color = (*c3f_ptr)[mloop->v];
-				cface->a = FTOCHAR(color[0]);
-				cface->r = FTOCHAR(color[1]);
-				cface->g = FTOCHAR(color[2]);
-				cface->b = 255;
-			}
-			else {
-				const Imath::C4f &color = (*c4f_ptr)[mloop->v];
-				cface->a = FTOCHAR(color[0]);
-				cface->r = FTOCHAR(color[1]);
-				cface->g = FTOCHAR(color[2]);
-				cface->b = FTOCHAR(color[3]);
-			}
-		}
-	}
-}
 
 static void read_uvs(const CDStreamConfig &config, void *data,
                      const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
@@ -290,34 +252,83 @@ static void read_uvs(const CDStreamConfig &config, void *data,
 	}
 }
 
-static void read_custom_data_mcols(const ICompoundProperty &prop,
+static void read_custom_data_mcols(const ICompoundProperty &arbGeomParams,
                                    const PropertyHeader &prop_header,
                                    const CDStreamConfig &config,
                                    const Alembic::Abc::ISampleSelector &iss)
 {
 	C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
 	C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
+	bool use_c3f_ptr;
+	bool is_facevarying;
 
+	/* Find the correct interpretation of the data */
 	if (IC3fGeomParam::matches(prop_header)) {
-		IC3fGeomParam color_param(prop, prop_header.getName());
+		IC3fGeomParam color_param(arbGeomParams, prop_header.getName());
 		IC3fGeomParam::Sample sample;
+		BLI_assert(!strcmp("rgb", color_param.getInterpretation()));
+
 		color_param.getIndexed(sample, iss);
+		is_facevarying = sample.getScope() == kFacevaryingScope &&
+		                 config.totloop == sample.getIndices()->size();
 
 		c3f_ptr = sample.getVals();
+		use_c3f_ptr = true;
 	}
 	else if (IC4fGeomParam::matches(prop_header)) {
-		IC4fGeomParam color_param(prop, prop_header.getName());
+		IC4fGeomParam color_param(arbGeomParams, prop_header.getName());
 		IC4fGeomParam::Sample sample;
+		BLI_assert(!strcmp("rgba", color_param.getInterpretation()));
+
 		color_param.getIndexed(sample, iss);
+		is_facevarying = sample.getScope() == kFacevaryingScope &&
+		                 config.totloop == sample.getIndices()->size();
 
 		c4f_ptr = sample.getVals();
+		use_c3f_ptr = false;
+	}
+	else {
+		/* this won't happen due to the checks in read_custom_data() */
+		return;
 	}
+	BLI_assert(c3f_ptr || c4f_ptr);
 
+	/* Read the vertex colors */
 	void *cd_data = config.add_customdata_cb(config.user_data,
 	                                         prop_header.getName().c_str(),
 	                                         CD_MLOOPCOL);
+	MCol *cfaces = static_cast<MCol *>(cd_data);
+	MPoly *mpolys = config.mpoly;
+	MLoop *mloops = config.mloop;
+
+	size_t face_index = 0;
+	size_t color_index;
+	for (int i = 0; i < config.totpoly; ++i) {
+		MPoly *poly = &mpolys[i];
+		MCol *cface = &cfaces[poly->loopstart + poly->totloop];
+		MLoop *mloop = &mloops[poly->loopstart + poly->totloop];
+
+		for (int j = 0; j < poly->totloop; ++j, ++face_index) {
+			--cface;
+			--mloop;
+			color_index = is_facevarying ? face_index : mloop->v;
 
-	read_mcols(config, cd_data, c3f_ptr, c4f_ptr);
+			if (use_c3f_ptr) {
+				const Imath::C3f &color = (*c3f_ptr)[color_index];
+				cface->a = FTOCHAR(color[0]);
+				cface->r = FTOCHAR(color[1]);
+				cface->g = FTOCHAR(color[2]);
+				cface->b = 255;
+			}
+			else {
+				const Imath::C4f &color = (*c4f_ptr)[color_index];
+				cface->a = FTOCHAR(color[0]);
+				cface->r = FTOCHAR(color[1]);
+				cface->g = FTOCHAR(color[2]);
+				cface->b = FTOCHAR(color[3]);
+			}
+		}
+	}
 }
 
 static void read_custom_data_uvs(const ICompoundProperty &prop,
diff --git a/tests/python/bl_alembic_import_test.py b/tests/python/bl_alembic_import_test.py
index ef5ae372333..358b8a3c758 100644
--- a/tests/python/bl_alembic_import_test.py
+++ b/tests/python/bl_alembic_import_test.py
@@ -31,7 +31,7 @@ import bpy
 args = None
 
 
-class SimpleImportTest(unittest.TestCase):
+class AbstractAlembicTest(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
         cls.testdir = args.testdir
@@ -43,6 +43,18 @@ class SimpleImportTest(unittest.TestCase):
         # Make sure we always start with a known-empty file.
         bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend"))
 
+    def assertAlmostEqualFloatArray(self, actual, expect, places=6, delta=None):
+        """Asserts that the arrays of floats are almost equal."""
+
+        self.assertEqual(len(actual), len(expect),
+                         'Actual array has %d items, expected %d' % (len(actual), len(expect)))
+
+        for idx, (act, exp) in enumerate(zip(actual, expect)):
+            self.assertAlmostEqual(act, exp, places=places, delta=delta,
+                                   msg='%f != %f at index %d' % (act, exp, idx))
+
+
+class SimpleImportTest(AbstractAlembicTest):
     def test_import_cube_hierarchy(self):
         res = bpy.ops.wm.alembic_import(
             filepath=str(self.testdir / "cubes-hierarchy.abc"),
@@ -158,6 +170,38 @@ class SimpleImportTest(unittest.TestCase):
         self.assertEqual('CubeShape', bpy.data.objects['Cube'].data.name)
 
 
+class VertexColourImportTest(AbstractAlembicTest):
+    def test_import_from_houdini(self):
+        # Houdini saved "face-varying", and as RGB.
+        res = bpy.ops.wm.alembic_import(
+            filepath=str(self.testdir / "vertex-colours-houdini.abc"),
+            as_background_job=False)
+        self.assertEqual({'FINISHED'}, res)
+
+        ob = bpy.context.active_object
+        layer = ob.data.vertex_colors['Cf']  # MeshLoopColorLayer
+
+        # Test some known-good values.
+        self.assertAlmostEqualFloatArray(layer.data[0].color, (0, 0, 0))
+        self.assertAlmostEqualFloatArray(layer.data[98].color, (0.9019607, 0.4745098, 0.2666666))
+        self.assertAlmostEqualFloatArray(layer.data[99].color, (0.8941176, 0.4705882, 0.2627451))
+
+    def test_import_from_blender(self):
+        # Blender saved per-vertex, and as RGBA.
+        res = bpy.ops.wm.alembic_import(
+            filepath=str(self.testdir / "vertex-colours-blender.abc"),
+            as_background_job=False)
+        self.assertEqual({'FINISHED'}, res)
+
+        ob = bpy.context.active_object
+        layer = ob.data.vertex_colors['Cf']  # MeshLoopColorLayer
+
+        # Test some known-good values.
+        self.assertAlmostEqualFloatArray(layer.data[0].color, (1.0, 0.0156862, 0.3607843))
+        self.assertAlmostEqualFloatArray(layer.data[98].color, (0.0941176, 0.1215686, 0.9137254))
+        self.assertAlmostEqualFloatArray(layer.data[99].color, (0.1294117, 0.3529411, 0.7529411))
+
+
 def main():
     global args
     import argparse




More information about the Bf-blender-cvs mailing list