Changeset View
Changeset View
Standalone View
Standalone View
tests/python/alembic_export_tests.py
| Show First 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | def assertAlmostEqualFloatArray(self, actual, expect, places=6, delta=None): | ||||
| msg='%f != %f at index %d' % (act, exp, idx)) | msg='%f != %f at index %d' % (act, exp, idx)) | ||||
| class HierarchicalAndFlatExportTest(AbstractAlembicTest): | class HierarchicalAndFlatExportTest(AbstractAlembicTest): | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_hierarchical_export(self, tempdir: pathlib.Path): | def test_hierarchical_export(self, tempdir: pathlib.Path): | ||||
| abc = tempdir / 'cubes_hierarchical.abc' | abc = tempdir / 'cubes_hierarchical.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | ||||
| "renderable_only=True, visible_objects_only=True, flatten=False)" % abc.as_posix() | "visible_objects_only=True, flatten=False)" % abc.as_posix() | ||||
| self.run_blender('cubes-hierarchy.blend', script) | self.run_blender('cubes-hierarchy.blend', script) | ||||
| # Now check the resulting Alembic file. | # Now check the resulting Alembic file. | ||||
| xform = self.abcprop(abc, '/Cube/Cube_002/Cube_012/.xform') | xform = self.abcprop(abc, '/Cube/Cube_002/Cube_012/.xform') | ||||
| self.assertEqual(1, xform['.inherits']) | self.assertEqual(1, xform['.inherits']) | ||||
| self.assertAlmostEqualFloatArray( | self.assertAlmostEqualFloatArray( | ||||
| xform['.vals'], | xform['.vals'], | ||||
| [1.0, 0.0, 0.0, 0.0, | [1.0, 0.0, 0.0, 0.0, | ||||
| 0.0, 1.0, 0.0, 0.0, | 0.0, 1.0, 0.0, 0.0, | ||||
| 0.0, 0.0, 1.0, 0.0, | 0.0, 0.0, 1.0, 0.0, | ||||
| 3.07484, -2.92265, 0.0586434, 1.0] | 3.07484, -2.92265, 0.0586434, 1.0] | ||||
| ) | ) | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_flat_export(self, tempdir: pathlib.Path): | def test_flat_export(self, tempdir: pathlib.Path): | ||||
| abc = tempdir / 'cubes_flat.abc' | abc = tempdir / 'cubes_flat.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | ||||
| "renderable_only=True, visible_objects_only=True, flatten=True)" % abc.as_posix() | "visible_objects_only=True, flatten=True)" % abc.as_posix() | ||||
| self.run_blender('cubes-hierarchy.blend', script) | self.run_blender('cubes-hierarchy.blend', script) | ||||
| # Now check the resulting Alembic file. | # Now check the resulting Alembic file. | ||||
| xform = self.abcprop(abc, '/Cube_012/.xform') | xform = self.abcprop(abc, '/Cube_012/.xform') | ||||
| self.assertEqual(1, xform['.inherits'], "Blender transforms always inherit") | self.assertEqual(1, xform['.inherits'], "Blender transforms always inherit") | ||||
| self.assertAlmostEqualFloatArray( | self.assertAlmostEqualFloatArray( | ||||
| xform['.vals'], | xform['.vals'], | ||||
| [0.343134, 0.485243, 0.804238, 0, | [0.343134, 0.485243, 0.804238, 0, | ||||
| 0.0, 0.856222, -0.516608, 0, | 0.0, 0.856222, -0.516608, 0, | ||||
| -0.939287, 0.177266, 0.293799, 0, | -0.939287, 0.177266, 0.293799, 0, | ||||
| 1, 3, 4, 1], | 1, 3, 4, 1], | ||||
| ) | ) | ||||
| class DupliGroupExportTest(AbstractAlembicTest): | class DupliGroupExportTest(AbstractAlembicTest): | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_hierarchical_export(self, tempdir: pathlib.Path): | def test_hierarchical_export(self, tempdir: pathlib.Path): | ||||
| abc = tempdir / 'dupligroup_hierarchical.abc' | abc = tempdir / 'dupligroup_hierarchical.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | ||||
| "renderable_only=True, visible_objects_only=True, flatten=False)" % abc.as_posix() | "visible_objects_only=True, flatten=False)" % abc.as_posix() | ||||
| self.run_blender('dupligroup-scene.blend', script) | self.run_blender('dupligroup-scene.blend', script) | ||||
| # Now check the resulting Alembic file. | # Now check the resulting Alembic file. | ||||
| xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder-0/Suzanne-1/.xform') | xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder-0/Suzanne-1/.xform') | ||||
| self.assertEqual(1, xform['.inherits']) | self.assertEqual(1, xform['.inherits']) | ||||
| self.assertAlmostEqualFloatArray( | self.assertAlmostEqualFloatArray( | ||||
| xform['.vals'], | xform['.vals'], | ||||
| [1.0, 0.0, 0.0, 0.0, | [1.0, 0.0, 0.0, 0.0, | ||||
| 0.0, 1.0, 0.0, 0.0, | 0.0, 1.0, 0.0, 0.0, | ||||
| 0.0, 0.0, 1.0, 0.0, | 0.0, 0.0, 1.0, 0.0, | ||||
| 0.0, 2.0, 0.0, 1.0] | 0.0, 2.0, 0.0, 1.0] | ||||
| ) | ) | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_flat_export(self, tempdir: pathlib.Path): | def test_flat_export(self, tempdir: pathlib.Path): | ||||
| abc = tempdir / 'dupligroup_hierarchical.abc' | abc = tempdir / 'dupligroup_hierarchical.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | ||||
| "renderable_only=True, visible_objects_only=True, flatten=True)" % abc.as_posix() | "visible_objects_only=True, flatten=True)" % abc.as_posix() | ||||
| self.run_blender('dupligroup-scene.blend', script) | self.run_blender('dupligroup-scene.blend', script) | ||||
| # Now check the resulting Alembic file. | # Now check the resulting Alembic file. | ||||
| xform = self.abcprop(abc, '/Suzanne-1/.xform') | xform = self.abcprop(abc, '/Suzanne-1/.xform') | ||||
| self.assertEqual(1, xform['.inherits']) | self.assertEqual(1, xform['.inherits']) | ||||
| self.assertAlmostEqualFloatArray( | self.assertAlmostEqualFloatArray( | ||||
| xform['.vals'], | xform['.vals'], | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | def test_multiple_duplicated_hierarchies(self, tempdir: pathlib.Path): | ||||
| self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/Block-0-1/.xform') | self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/Block-0-1/.xform') | ||||
| class CurveExportTest(AbstractAlembicTest): | class CurveExportTest(AbstractAlembicTest): | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_export_single_curve(self, tempdir: pathlib.Path): | def test_export_single_curve(self, tempdir: pathlib.Path): | ||||
| abc = tempdir / 'single-curve.abc' | abc = tempdir / 'single-curve.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | ||||
| "renderable_only=True, visible_objects_only=True, flatten=False)" % abc.as_posix() | "visible_objects_only=True, flatten=False)" % abc.as_posix() | ||||
| self.run_blender('single-curve.blend', script) | self.run_blender('single-curve.blend', script) | ||||
| # Now check the resulting Alembic file. | # Now check the resulting Alembic file. | ||||
| abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom') | abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom') | ||||
| self.assertEqual(abcprop['.orders'], [4]) | self.assertEqual(abcprop['.orders'], [4]) | ||||
| abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom/.userProperties') | abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom/.userProperties') | ||||
| self.assertEqual(abcprop['blender:resolution'], 10) | self.assertEqual(abcprop['blender:resolution'], 10) | ||||
| class HairParticlesExportTest(AbstractAlembicTest): | class HairParticlesExportTest(AbstractAlembicTest): | ||||
| """Tests exporting with/without hair/particles. | """Tests exporting with/without hair/particles. | ||||
| Just a basic test to ensure that the enabling/disabling works, and that export | Just a basic test to ensure that the enabling/disabling works, and that export | ||||
| works at all. NOT testing the quality/contents of the exported file. | works at all. NOT testing the quality/contents of the exported file. | ||||
| """ | """ | ||||
| def _do_test(self, tempdir: pathlib.Path, export_hair: bool, export_particles: bool) -> pathlib.Path: | def _do_test(self, tempdir: pathlib.Path, export_hair: bool, export_particles: bool) -> pathlib.Path: | ||||
| abc = tempdir / 'hair-particles.abc' | abc = tempdir / 'hair-particles.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | ||||
| "renderable_only=True, visible_objects_only=True, flatten=False, " \ | "visible_objects_only=True, flatten=False, " \ | ||||
| "export_hair=%r, export_particles=%r, as_background_job=False)" \ | "export_hair=%r, export_particles=%r, as_background_job=False)" \ | ||||
| % (abc.as_posix(), export_hair, export_particles) | % (abc.as_posix(), export_hair, export_particles) | ||||
| self.run_blender('hair-particles.blend', script) | self.run_blender('hair-particles.blend', script) | ||||
| return abc | return abc | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_with_both(self, tempdir: pathlib.Path): | def test_with_both(self, tempdir: pathlib.Path): | ||||
| abc = self._do_test(tempdir, True, True) | abc = self._do_test(tempdir, True, True) | ||||
| ▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | class UVMapExportTest(AbstractAlembicTest): | ||||
| def test_uvmap_export(self, tempdir: pathlib.Path): | def test_uvmap_export(self, tempdir: pathlib.Path): | ||||
| """Minimal test for exporting multiple UV maps on an animated mesh. | """Minimal test for exporting multiple UV maps on an animated mesh. | ||||
| This covers the issue reported in T77021. | This covers the issue reported in T77021. | ||||
| """ | """ | ||||
| basename = 'T77021-multiple-uvmaps-animated-mesh' | basename = 'T77021-multiple-uvmaps-animated-mesh' | ||||
| abc = tempdir / f'{basename}.abc' | abc = tempdir / f'{basename}.abc' | ||||
| script = f"import bpy; bpy.ops.wm.alembic_export(filepath='{abc.as_posix()}', start=1, end=1, " \ | 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)" | f"visible_objects_only=True, flatten=False)" | ||||
| self.run_blender(f'{basename}.blend', script) | self.run_blender(f'{basename}.blend', script) | ||||
| self.maxDiff = 1000 | self.maxDiff = 1000 | ||||
| # The main UV map should be written to .geom | # The main UV map should be written to .geom | ||||
| abcprop = self.abcprop(abc, '/Cube/Cube/.geom/uv') | abcprop = self.abcprop(abc, '/Cube/Cube/.geom/uv') | ||||
| self.assertEqual(abcprop['.vals'], [ | self.assertEqual(abcprop['.vals'], [ | ||||
| [0.625, 0.75], | [0.625, 0.75], | ||||
| Show All 32 Lines | def test_uvmap_export(self, tempdir: pathlib.Path): | ||||
| ]) | ]) | ||||
| class LongNamesExportTest(AbstractAlembicTest): | class LongNamesExportTest(AbstractAlembicTest): | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_export_long_names(self, tempdir: pathlib.Path): | def test_export_long_names(self, tempdir: pathlib.Path): | ||||
| abc = tempdir / 'long-names.abc' | abc = tempdir / 'long-names.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ | ||||
| "renderable_only=False, visible_objects_only=False, flatten=False)" % abc.as_posix() | "visible_objects_only=False, flatten=False)" % abc.as_posix() | ||||
| self.run_blender('long-names.blend', script) | self.run_blender('long-names.blend', script) | ||||
| name_parts = [ | name_parts = [ | ||||
| 'foG9aeLahgoh5goacee1dah6Hethaghohjaich5pasizairuWigee1ahPeekiGh', | 'foG9aeLahgoh5goacee1dah6Hethaghohjaich5pasizairuWigee1ahPeekiGh', | ||||
| 'yoNgoisheedah2ua0eigh2AeCaiTee5bo0uphoo7Aixephah9racahvaingeeH4', | 'yoNgoisheedah2ua0eigh2AeCaiTee5bo0uphoo7Aixephah9racahvaingeeH4', | ||||
| 'zuthohnoi1thooS3eezoo8seuph2Boo5aefacaethuvee1aequoonoox1sookie', | 'zuthohnoi1thooS3eezoo8seuph2Boo5aefacaethuvee1aequoonoox1sookie', | ||||
| 'wugh4ciTh3dipiepeequait5uug7thiseek5ca7Eijei5ietaizokohhaecieto', | 'wugh4ciTh3dipiepeequait5uug7thiseek5ca7Eijei5ietaizokohhaecieto', | ||||
| 'up9aeheenein9oteiX6fohP3thiez6Ahvah0oohah1ep2Eesho4Beboechaipoh', | 'up9aeheenein9oteiX6fohP3thiez6Ahvah0oohah1ep2Eesho4Beboechaipoh', | ||||
| ▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | class InvisibleObjectExportTest(AbstractAlembicTest): | ||||
| written, and that it has the correct initial value. This is a limitation | written, and that it has the correct initial value. This is a limitation | ||||
| caused by these tests relying on `abcls`. | caused by these tests relying on `abcls`. | ||||
| """ | """ | ||||
| @with_tempdir | @with_tempdir | ||||
| def test_hierarchical_export(self, tempdir: pathlib.Path): | def test_hierarchical_export(self, tempdir: pathlib.Path): | ||||
| abc = tempdir / 'visibility.abc' | abc = tempdir / 'visibility.abc' | ||||
| script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=2, " \ | script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=2, " \ | ||||
| "renderable_only=False, visible_objects_only=False)" % abc.as_posix() | "visible_objects_only=False)" % abc.as_posix() | ||||
| self.run_blender('visibility.blend', script) | self.run_blender('visibility.blend', script) | ||||
| def test(cube_name: str, expect_visible: bool): | def test(cube_name: str, expect_visible: bool): | ||||
| returncode, output = self.abcls('-va', f'{abc}/{cube_name}') | returncode, output = self.abcls('-va', f'{abc}/{cube_name}') | ||||
| if returncode: | if returncode: | ||||
| self.fail(f"abcls failed: {output}") | self.fail(f"abcls failed: {output}") | ||||
| output = output.strip() | output = output.strip() | ||||
| self.assertEqual(f'Cube .xform visible {int(expect_visible)}', output) | self.assertEqual(f'Cube .xform visible {int(expect_visible)}', output) | ||||
| ▲ Show 20 Lines • Show All 87 Lines • Show Last 20 Lines | |||||