diff --git a/.gitignore b/.gitignore index ee5b3e9..abe64b1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__ /.coverage /tests/blendfiles/cache_ocean/ +/tests/blendfiles/T53562/blendcache_bam_pack_bug/ diff --git a/blender_asset_tracer/tracer/block_walkers.py b/blender_asset_tracer/tracer/block_walkers.py index 32c2e00..fd3decc 100644 --- a/blender_asset_tracer/tracer/block_walkers.py +++ b/blender_asset_tracer/tracer/block_walkers.py @@ -12,7 +12,7 @@ import typing from blender_asset_tracer import blendfile, bpathlib from blender_asset_tracer.blendfile import iterators -from . import result, cdefs +from . import result, cdefs, modifier_walkers log = logging.getLogger(__name__) @@ -105,20 +105,14 @@ def _from_block_ob(block: blendfile.BlendFileBlock) -> typing.Iterator[result.Bl mods = block.get_pointer((b'modifiers', b'first')) for mod_idx, block_mod in enumerate(iterators.listbase(mods, next_path=(b'modifier', b'next'))): block_name = b'%s.modifiers[%d]' % (ob_idname, mod_idx) - log.debug('Tracing modifier %s', block_name.decode()) - mod_type = block_mod[b'modifier', b'type'] - if mod_type == cdefs.eModifierType_Ocean: - if block_mod[b'cached']: - path, field = block_mod.get(b'cachepath', return_field=True) - # The path indicates the directory containing the cached files. - yield result.BlockUsage(block_mod, path, is_sequence=True, path_full_field=field, - block_name=block_name) + log.debug('Tracing modifier %s, type=%d', block_name.decode(), mod_type) - elif mod_type == cdefs.eModifierType_MeshCache: - path, field = block_mod.get(b'filepath', return_field=True) - yield result.BlockUsage(block_mod, path, is_sequence=False, path_full_field=field, - block_name=block_name) + try: + mod_handler = modifier_walkers.modifier_handlers[mod_type] + except KeyError: + continue + yield from mod_handler(block_mod, block_name) def _from_block_sc(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]: diff --git a/blender_asset_tracer/tracer/cdefs.py b/blender_asset_tracer/tracer/cdefs.py index fc5b064..7b658a7 100644 --- a/blender_asset_tracer/tracer/cdefs.py +++ b/blender_asset_tracer/tracer/cdefs.py @@ -34,6 +34,7 @@ IMA_SRC_SEQUENCE = 2 IMA_SRC_MOVIE = 3 # DNA_modifier_types.h +eModifierType_ParticleSystem = 19 eModifierType_Ocean = 39 eModifierType_MeshCache = 46 @@ -45,4 +46,10 @@ PART_DRAW_GR = 8 # Object.transflag OB_DUPLIGROUP = 1 << 8 -CACHE_LIBRARY_SOURCE_CACHE = 1 +# DNA_object_force_types.h +PTCACHE_DISK_CACHE = 64 +PTCACHE_EXTERNAL = 512 + +# BKE_pointcache.h +PTCACHE_EXT = b'.bphys' +PTCACHE_PATH = b'blendcache_' diff --git a/blender_asset_tracer/tracer/modifier_walkers.py b/blender_asset_tracer/tracer/modifier_walkers.py new file mode 100644 index 0000000..d717daf --- /dev/null +++ b/blender_asset_tracer/tracer/modifier_walkers.py @@ -0,0 +1,64 @@ +"""Modifier handling code used in block_walkers.py + +The _modifier_xxx() functions all yield result.BlockUsage objects for external +files used by the modifiers. +""" +import typing + +from blender_asset_tracer import blendfile, bpathlib +from . import result, cdefs + + +def _modifier_filepath(modifier: blendfile.BlendFileBlock, block_name: bytes) \ + -> typing.Iterator[result.BlockUsage]: + """Just yield the 'filepath' field.""" + path, field = modifier.get(b'filepath', return_field=True) + yield result.BlockUsage(modifier, path, path_full_field=field, block_name=block_name) + + +def _modifier_ocean(modifier: blendfile.BlendFileBlock, block_name: bytes) \ + -> typing.Iterator[result.BlockUsage]: + if not modifier[b'cached']: + return + + path, field = modifier.get(b'cachepath', return_field=True) + # The path indicates the directory containing the cached files. + yield result.BlockUsage(modifier, path, is_sequence=True, path_full_field=field, + block_name=block_name) + + +def _modifier_particle_system(modifier: blendfile.BlendFileBlock, block_name: bytes) \ + -> typing.Iterator[result.BlockUsage]: + psys = modifier.get_pointer(b'psys') + if psys is None: + return + + pointcache = psys.get_pointer(b'pointcache') + if pointcache is None: + return + + flag = pointcache[b'flag'] + + if flag & cdefs.PTCACHE_DISK_CACHE: + # See ptcache_path() in pointcache.c + name, field = pointcache.get(b'name', return_field=True) + path = b'//%b%b/%b_*%b' % ( + cdefs.PTCACHE_PATH, + modifier.bfile.filepath.stem.encode(), + name, + cdefs.PTCACHE_EXT) + bpath = bpathlib.BlendPath(path) + yield result.BlockUsage(pointcache, bpath, path_full_field=field, + is_sequence=True, block_name=block_name) + + if flag & cdefs.PTCACHE_EXTERNAL: + path, field = pointcache.get(b'path', return_field=True) + yield result.BlockUsage(pointcache, path, path_full_field=field, + is_sequence=True, block_name=block_name) + + +modifier_handlers = { + cdefs.eModifierType_ParticleSystem: _modifier_particle_system, + cdefs.eModifierType_Ocean: _modifier_ocean, + cdefs.eModifierType_MeshCache: _modifier_filepath, +} diff --git a/blender_asset_tracer/tracer/result.py b/blender_asset_tracer/tracer/result.py index ba9eab1..1e0e51f 100644 --- a/blender_asset_tracer/tracer/result.py +++ b/blender_asset_tracer/tracer/result.py @@ -8,7 +8,9 @@ class BlockUsage: :ivar block_name: an identifying name for this block. Defaults to the ID name of the block. :ivar block: - :ivar asset_path: + :ivar asset_path: The path of the asset, if is_sequence=False. Otherwise + it can be either a glob pattern (must contain a * byte), a directory, + or the path of the first file in the sequence. :ivar is_sequence: Indicates whether this file is alone (False), the first of a sequence (True, and the path points to a file), or a directory containing a sequence (True, and path points to a directory). diff --git a/tests/blendfiles/T53562/bam_pack_bug.blend b/tests/blendfiles/T53562/bam_pack_bug.blend new file mode 100644 index 0000000..af0e24c Binary files /dev/null and b/tests/blendfiles/T53562/bam_pack_bug.blend differ diff --git a/tests/test_tracer.py b/tests/test_tracer.py index c20de51..ae309c4 100644 --- a/tests/test_tracer.py +++ b/tests/test_tracer.py @@ -196,3 +196,10 @@ class DepsTest(AbstractTracerTest): 'Image', 'name[1024]', None, None, b'//textures/Textures/Buildings/buildings_roof_04-color.png', False), }, recursive=True) + + def test_sim_data(self): + self.assert_deps('T53562/bam_pack_bug.blend', { + b'OBEmitter.modifiers[0]': Expect( + 'PointCache', 'name[64]', None, None, + b'//blendcache_bam_pack_bug/particles_*.bphys', True), + })