Support UDIM images

This commit is contained in:
Sybren A. Stüvel 2022-02-18 15:39:23 +01:00
parent 4ebd535b5f
commit 843c34c3c0
12 changed files with 51 additions and 4 deletions

View File

@ -3,6 +3,10 @@
This file logs the changes that are actually interesting to users (new features, This file logs the changes that are actually interesting to users (new features,
changed functionality, fixed bugs). changed functionality, fixed bugs).
# Version 1.11 (in development)
- Support UDIM images.
# Version 1.10 (2022-02-03) # Version 1.10 (2022-02-03)
- Avoid doubly-compressing ZStandard (Blender 3) compressed files. - Avoid doubly-compressing ZStandard (Blender 3) compressed files.

View File

@ -32,6 +32,7 @@ SEQ_TYPE_EFFECT = 8
IMA_SRC_FILE = 1 IMA_SRC_FILE = 1
IMA_SRC_SEQUENCE = 2 IMA_SRC_SEQUENCE = 2
IMA_SRC_MOVIE = 3 IMA_SRC_MOVIE = 3
IMA_SRC_TILED = 6 # UDIM
# DNA_modifier_types.h # DNA_modifier_types.h
eModifierType_Wave = 7 eModifierType_Wave = 7

View File

@ -555,7 +555,7 @@ class Packer:
def _copy_asset_and_deps(self, asset_path: pathlib.Path, action: AssetAction): 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 # Copy the asset itself, but only if it's not a sequence (sequences are
# handled below in the for-loop). # handled below in the for-loop).
if "*" not in str(asset_path): if "*" not in str(asset_path) and '<UDIM>' not in asset_path.name:
packed_path = action.new_path packed_path = action.new_path
assert packed_path is not None assert packed_path is not None
read_path = action.read_from or asset_path read_path = action.read_from or asset_path

View File

@ -96,11 +96,13 @@ def image(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]
cdefs.IMA_SRC_FILE, cdefs.IMA_SRC_FILE,
cdefs.IMA_SRC_SEQUENCE, cdefs.IMA_SRC_SEQUENCE,
cdefs.IMA_SRC_MOVIE, cdefs.IMA_SRC_MOVIE,
cdefs.IMA_SRC_TILED,
}: }:
log.debug("skiping image source type %s", image_source)
return return
pathname, field = block.get(b"name", return_field=True) 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) yield result.BlockUsage(block, pathname, is_sequence, path_full_field=field)

View File

@ -39,6 +39,12 @@ def expand_sequence(path: pathlib.Path) -> typing.Iterator[pathlib.Path]:
or the path of the first file in the sequence. or the path of the first file in the sequence.
""" """
if "<UDIM>" in path.name: # UDIM tiles
# Change <UDIM> 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('<UDIM>', '*'))
if "*" in str(path): # assume it is a glob if "*" in str(path): # assume it is a glob
import 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)): for fname in sorted(glob.glob(str(path), recursive=True)):
yield pathlib.Path(fname) yield pathlib.Path(fname)
return return
if not path.exists(): if not path.exists():
raise DoesNotExist(path) raise DoesNotExist(path)

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

View File

@ -366,6 +366,21 @@ class PackTest(AbstractPackTest):
self.assertEqual(b"SQ000210.png", seq[b"name"]) self.assertEqual(b"SQ000210.png", seq[b"name"])
self.assertEqual(relpath, seq_strip[b"dir"]) self.assertEqual(relpath, seq_strip[b"dir"])
def test_sequence_udim(self):
# UDIM tiles are special, because the filename itself has a <UDIM>
# 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): def test_noop(self):
ppath = self.blendfiles / "subdir" ppath = self.blendfiles / "subdir"
infile = ppath / "doubly_linked_up.blend" infile = ppath / "doubly_linked_up.blend"

View File

@ -62,7 +62,6 @@ class AssetHoldingBlocksTest(AbstractTracerTest):
self.assertEqual(965, len(self.bf.blocks)) self.assertEqual(965, len(self.bf.blocks))
self.assertEqual(4, blocks_seen) self.assertEqual(4, blocks_seen)
class DepsTest(AbstractTracerTest): class DepsTest(AbstractTracerTest):
@staticmethod @staticmethod
def field_name(field: dna.Field) -> typing.Optional[str]: def field_name(field: dna.Field) -> typing.Optional[str]:
@ -189,6 +188,19 @@ class DepsTest(AbstractTracerTest):
actual = list(dep.files()) actual = list(dep.files())
self.assertEqual(actual, expected) 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.<UDIM>.png',
True,
),
}
self.assert_deps("udim/v01_UDIM_BAT_debugging.blend", expects)
def test_block_cf(self): def test_block_cf(self):
self.assert_deps( self.assert_deps(
"alembic-user.blend", "alembic-user.blend",

View File

@ -20,6 +20,14 @@ class ExpandFileSequenceTest(AbstractBlendFileTest):
actual = list(file_sequence.expand_sequence(path)) actual = list(file_sequence.expand_sequence(path))
self.assertEqual(self.imgseq, actual) self.assertEqual(self.imgseq, actual)
def test_udim_sequence(self):
path = self.blendfiles / "udim/cube_UDIM.color.<UDIM>.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): def test_nonexistent(self):
path = self.blendfiles / "nonexistant" path = self.blendfiles / "nonexistant"
with self.assertRaises(file_sequence.DoesNotExist) as raises: with self.assertRaises(file_sequence.DoesNotExist) as raises: