diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a8f26c..fa48fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ This file logs the changes that are actually interesting to users (new features, changed functionality, fixed bugs). +# Version 1.11 (in development) + +- Support UDIM images. + # Version 1.10 (2022-02-03) - Avoid doubly-compressing ZStandard (Blender 3) compressed files. diff --git a/blender_asset_tracer/cdefs.py b/blender_asset_tracer/cdefs.py index a77eac0..798782e 100644 --- a/blender_asset_tracer/cdefs.py +++ b/blender_asset_tracer/cdefs.py @@ -32,6 +32,7 @@ SEQ_TYPE_EFFECT = 8 IMA_SRC_FILE = 1 IMA_SRC_SEQUENCE = 2 IMA_SRC_MOVIE = 3 +IMA_SRC_TILED = 6 # UDIM # DNA_modifier_types.h eModifierType_Wave = 7 diff --git a/blender_asset_tracer/pack/__init__.py b/blender_asset_tracer/pack/__init__.py index c6d1a8b..6b5bd7c 100644 --- a/blender_asset_tracer/pack/__init__.py +++ b/blender_asset_tracer/pack/__init__.py @@ -555,7 +555,7 @@ class Packer: def _copy_asset_and_deps(self, asset_path: pathlib.Path, action: AssetAction): # Copy the asset itself, but only if it's not a sequence (sequences are # handled below in the for-loop). - if "*" not in str(asset_path): + if "*" not in str(asset_path) and '' not in asset_path.name: packed_path = action.new_path assert packed_path is not None read_path = action.read_from or asset_path diff --git a/blender_asset_tracer/trace/blocks2assets.py b/blender_asset_tracer/trace/blocks2assets.py index e23de4b..962f82a 100644 --- a/blender_asset_tracer/trace/blocks2assets.py +++ b/blender_asset_tracer/trace/blocks2assets.py @@ -96,11 +96,13 @@ def image(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage] cdefs.IMA_SRC_FILE, cdefs.IMA_SRC_SEQUENCE, cdefs.IMA_SRC_MOVIE, + cdefs.IMA_SRC_TILED, }: + log.debug("skiping image source type %s", image_source) return pathname, field = block.get(b"name", return_field=True) - is_sequence = image_source == cdefs.IMA_SRC_SEQUENCE + is_sequence = image_source in {cdefs.IMA_SRC_SEQUENCE, cdefs.IMA_SRC_TILED} yield result.BlockUsage(block, pathname, is_sequence, path_full_field=field) diff --git a/blender_asset_tracer/trace/file_sequence.py b/blender_asset_tracer/trace/file_sequence.py index 37d9c1e..8e37418 100644 --- a/blender_asset_tracer/trace/file_sequence.py +++ b/blender_asset_tracer/trace/file_sequence.py @@ -39,6 +39,12 @@ def expand_sequence(path: pathlib.Path) -> typing.Iterator[pathlib.Path]: or the path of the first file in the sequence. """ + if "" in path.name: # UDIM tiles + # Change marker to a glob pattern, then let the glob case handle it. + # This assumes that all files that match the glob are actually UDIM + # tiles; this could cause some false-positives. + path = path.with_name(path.name.replace('', '*')) + if "*" in str(path): # assume it is a glob import glob @@ -46,7 +52,6 @@ def expand_sequence(path: pathlib.Path) -> typing.Iterator[pathlib.Path]: for fname in sorted(glob.glob(str(path), recursive=True)): yield pathlib.Path(fname) return - if not path.exists(): raise DoesNotExist(path) diff --git a/tests/blendfiles/udim/cube_UDIM.color.1001.png b/tests/blendfiles/udim/cube_UDIM.color.1001.png new file mode 100644 index 0000000..0b54eda Binary files /dev/null and b/tests/blendfiles/udim/cube_UDIM.color.1001.png differ diff --git a/tests/blendfiles/udim/cube_UDIM.color.1002.png b/tests/blendfiles/udim/cube_UDIM.color.1002.png new file mode 100644 index 0000000..e1c9484 Binary files /dev/null and b/tests/blendfiles/udim/cube_UDIM.color.1002.png differ diff --git a/tests/blendfiles/udim/cube_UDIM.color.1003.png b/tests/blendfiles/udim/cube_UDIM.color.1003.png new file mode 100644 index 0000000..6bd1bac Binary files /dev/null and b/tests/blendfiles/udim/cube_UDIM.color.1003.png differ diff --git a/tests/blendfiles/udim/v01_UDIM_BAT_debugging.blend b/tests/blendfiles/udim/v01_UDIM_BAT_debugging.blend new file mode 100644 index 0000000..e75bc10 Binary files /dev/null and b/tests/blendfiles/udim/v01_UDIM_BAT_debugging.blend differ diff --git a/tests/test_pack.py b/tests/test_pack.py index 11b0660..2916c18 100644 --- a/tests/test_pack.py +++ b/tests/test_pack.py @@ -366,6 +366,21 @@ class PackTest(AbstractPackTest): self.assertEqual(b"SQ000210.png", seq[b"name"]) self.assertEqual(relpath, seq_strip[b"dir"]) + def test_sequence_udim(self): + # UDIM tiles are special, because the filename itself has a + # marker in there and thus doesn't exist itself. + ppath = self.blendfiles / "udim" + infile = ppath / "v01_UDIM_BAT_debugging.blend" + + with pack.Packer(infile, ppath, self.tpath) as packer: + packer.strategise() + packer.execute() + + # The UDIM files should have been copied. + self.assertTrue((self.tpath / "cube_UDIM.color.1001.png").exists()) + self.assertTrue((self.tpath / "cube_UDIM.color.1002.png").exists()) + self.assertTrue((self.tpath / "cube_UDIM.color.1003.png").exists()) + def test_noop(self): ppath = self.blendfiles / "subdir" infile = ppath / "doubly_linked_up.blend" diff --git a/tests/test_tracer.py b/tests/test_tracer.py index 96c11e5..34e11ee 100644 --- a/tests/test_tracer.py +++ b/tests/test_tracer.py @@ -62,7 +62,6 @@ class AssetHoldingBlocksTest(AbstractTracerTest): self.assertEqual(965, len(self.bf.blocks)) self.assertEqual(4, blocks_seen) - class DepsTest(AbstractTracerTest): @staticmethod def field_name(field: dna.Field) -> typing.Optional[str]: @@ -189,6 +188,19 @@ class DepsTest(AbstractTracerTest): actual = list(dep.files()) self.assertEqual(actual, expected) + def test_seq_image_udim_sequence(self): + expects = { + b"IMcube_UDIM.color": Expect( + 'Image', + 'name[1024]', + None, + None, + b'//cube_UDIM.color..png', + True, + ), + } + self.assert_deps("udim/v01_UDIM_BAT_debugging.blend", expects) + def test_block_cf(self): self.assert_deps( "alembic-user.blend", diff --git a/tests/test_tracer_file_sequence.py b/tests/test_tracer_file_sequence.py index 9237337..a074a12 100644 --- a/tests/test_tracer_file_sequence.py +++ b/tests/test_tracer_file_sequence.py @@ -20,6 +20,14 @@ class ExpandFileSequenceTest(AbstractBlendFileTest): actual = list(file_sequence.expand_sequence(path)) self.assertEqual(self.imgseq, actual) + def test_udim_sequence(self): + path = self.blendfiles / "udim/cube_UDIM.color..png" + actual = list(file_sequence.expand_sequence(path)) + imgseq = [ + self.blendfiles / ("udim/cube_UDIM.color.%04d.png" % num) for num in range(1001, 1004) + ] + self.assertEqual(imgseq, actual) + def test_nonexistent(self): path = self.blendfiles / "nonexistant" with self.assertRaises(file_sequence.DoesNotExist) as raises: