From a95f8635967360e07ecb556e2b82099880ec1e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 17 Aug 2020 16:58:09 +0200 Subject: Fix T75936: Alembic, allow exporting of invisible objects Add a new depsgraph builder class that includes invisible objects and use that in the Alembic exporter. Alembic supports three options for visibility, "visible", "inherited", and "hidden". This means that parents can be hidden and still have visible children (contrary to USD, where invisibility is used to prune an entire scene graph subtree). Because of this, the visibility is stored on the transform node, as that represents the Object in Blender and thus keeps the Alembic file as close to Blender's own structure as possible. Reviewed By: Sergey Differential Revision: https://developer.blender.org/D8595 --- tests/python/alembic_export_tests.py | 70 ++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/tests/python/alembic_export_tests.py b/tests/python/alembic_export_tests.py index 800e450776c..cacd2f2dd0e 100644 --- a/tests/python/alembic_export_tests.py +++ b/tests/python/alembic_export_tests.py @@ -32,6 +32,7 @@ import pathlib import subprocess import sys import unittest +from typing import Tuple from modules.test_utils import ( with_tempdir, @@ -59,19 +60,13 @@ class AbstractAlembicTest(AbstractBlenderRunnerTest): # 'abcls' array notation, like "name[16]" cls.abcls_array = re.compile(r'^(?P[^\[]+)(\[(?P\d+)\])?$') - def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict: - """Uses abcls to obtain compound property values from an Alembic object. - - A dict of subproperties is returned, where the values are Python values. + def abcls(self, *arguments) -> Tuple[int, str]: + """Uses abcls and return its output. - The Python bindings for Alembic are old, and only compatible with Python 2.x, - so that's why we can't use them here, and have to rely on other tooling. + :return: tuple (process exit status code, stdout) """ - import collections - abcls = self.alembic_root / 'bin' / 'abcls' - - command = (str(abcls), '-vl', '%s%s' % (filepath, proppath)) + command = (self.alembic_root / 'bin' / 'abcls', *arguments) proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=30) @@ -84,7 +79,25 @@ class AbstractAlembicTest(AbstractBlenderRunnerTest): output = output.replace('\r\n', '\n').replace('\r', '\n') if proc.returncode: - raise AbcPropError('Error %d running %s:\n%s' % (proc.returncode, ' '.join(command), output)) + str_command = " ".join(str(c) for c in command) + print(f'command {str_command} failed with status {proc.returncode}') + + return (proc.returncode, output) + + def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict: + """Uses abcls to obtain compound property values from an Alembic object. + + A dict of subproperties is returned, where the values are Python values. + + The Python bindings for Alembic are old, and only compatible with Python 2.x, + so that's why we can't use them here, and have to rely on other tooling. + """ + import collections + + command = ('-vl', '%s%s' % (filepath, proppath)) + returncode, output = self.abcls(*command) + if returncode: + raise AbcPropError('Error %d running abcls:\n%s' % (returncode, output)) # Mapping from value type to callable that can convert a string to Python values. converters = { @@ -536,6 +549,41 @@ class LongNamesExportTest(AbstractAlembicTest): self.assertIn('.faceCounts', abcprop) +class InvisibleObjectExportTest(AbstractAlembicTest): + """Export an object which is invisible. + + This test only tests a small subset of the functionality that is required to + export invisible objects. It just tests that the visibility property is + written, and that it has the correct initial value. This is a limitation + caused by these tests relying on `abcls`. + """ + + @with_tempdir + def test_hierarchical_export(self, tempdir: pathlib.Path): + abc = tempdir / 'visibility.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=2, " \ + "renderable_only=False, visible_objects_only=False)" % abc.as_posix() + self.run_blender('visibility.blend', script) + + def test(cube_name: str, expect_visible: bool): + returncode, output = self.abcls('-va', f'{abc}/{cube_name}') + if returncode: + self.fail(f"abcls failed: {output}") + output = output.strip() + self.assertEqual(f'Cube .xform visible {int(expect_visible)}', output) + + # This cube is always visible. + test('VisibleCube', True) + + # This cube is never visible, and thus will not be pulled into the + # depsgraph by the standard builder, only by the all-objects builder. + test('InvisibleCube', False) + + # This cube has animated visibility, and thus will be pulled into the + # depsgraph by the standard builder as well as the all-objects builder. + test('InvisibleAnimatedCube', False) + + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--blender', required=True) -- cgit v1.2.3