diff options
author | Sybren A. Stüvel <sybren@stuvel.eu> | 2017-05-23 18:27:09 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@stuvel.eu> | 2017-05-23 18:27:15 +0300 |
commit | 7b25ffb618dd7509d425f7a5891c64d4a3668743 (patch) | |
tree | e955a54b4e84c0a517054b819de504f9d2a23fff | |
parent | cc0cc880de5a66d72f571bec4c7d3eb1219aefc0 (diff) |
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.
-rw-r--r-- | source/blender/alembic/intern/abc_customdata.cc | 95 | ||||
-rw-r--r-- | tests/python/bl_alembic_import_test.py | 46 |
2 files changed, 98 insertions, 43 deletions
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 |