diff --git a/blender_asset_tracer/bpathlib.py b/blender_asset_tracer/bpathlib.py index 477292c..b2d7628 100644 --- a/blender_asset_tracer/bpathlib.py +++ b/blender_asset_tracer/bpathlib.py @@ -75,7 +75,7 @@ class BlendPath(bytes): # Remove matching initial parts. What is left in bdir_parts represents # the number of '..' we need. What is left in asset_parts represents # what we need after the '../../../'. - while bdir_parts: + while bdir_parts and asset_parts: if bdir_parts[0] != asset_parts[0]: break bdir_parts.popleft() diff --git a/blender_asset_tracer/operators.py b/blender_asset_tracer/operators.py index c8dc807..f797c77 100644 --- a/blender_asset_tracer/operators.py +++ b/blender_asset_tracer/operators.py @@ -30,8 +30,12 @@ class ExportBatPack(Operator, ExportHelper): Path(bpy.data.filepath).parent, str(self.filepath), ) as packer: + print("[BAT] Strategising (tracing dependencies)...") packer.strategise() + print(f"[BAT] Found {len(packer._actions)} assets to process") + print("[BAT] Executing (rewriting paths and copying files)...") packer.execute() + print("[BAT] Packing complete!") self.report({"INFO"}, "Packing successful!") with zipfile.ZipFile(str(self.filepath)) as inzip: @@ -123,8 +127,12 @@ class BAT_OT_export_zip(Operator, ExportHelper): self.report({"INFO"}, "Packing with hierarchy...") with packer_cls(bfile, project, target, keep_hierarchy=True) as packer: + print("[BAT] Strategising (tracing dependencies)...") packer.strategise() + print(f"[BAT] Found {len(packer._actions)} assets to process") + print("[BAT] Executing (rewriting paths and copying files)...") packer.execute() + print("[BAT] Packing complete!") if self.use_zip: with zipfile.ZipFile(target) as inzip: diff --git a/blender_asset_tracer/trace/__init__.py b/blender_asset_tracer/trace/__init__.py index f88f5f3..3c86574 100644 --- a/blender_asset_tracer/trace/__init__.py +++ b/blender_asset_tracer/trace/__init__.py @@ -64,6 +64,7 @@ def deps( if usage_hash in seen_hashes: continue seen_hashes.add(usage_hash) + print(f"[BAT] Found reference: {block_usage.asset_path} (block {block.code.decode()})") yield block_usage diff --git a/blender_asset_tracer/trace/blocks2assets.py b/blender_asset_tracer/trace/blocks2assets.py index 48727bf..be6649a 100644 --- a/blender_asset_tracer/trace/blocks2assets.py +++ b/blender_asset_tracer/trace/blocks2assets.py @@ -133,14 +133,18 @@ def library(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsag @dna_code("ME") def mesh(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]: """Mesh data blocks.""" - block_external = block.get_pointer((b"ldata", b"external"), None) - if block_external is None: - block_external = block.get_pointer((b"fdata", b"external"), None) + block_external = None + for field_name in (b"ldata", b"fdata"): + if not block.has_field(field_name): + continue + block_external = block.get_pointer((field_name, b"external"), None) + if block_external is not None: + break if block_external is None: return path, field = block_external.get(b"filename", return_field=True) - yield result.BlockUsage(block, path, path_full_field=field) + yield result.BlockUsage(block_external, path, path_full_field=field) @dna_code("MC") diff --git a/blender_asset_tracer/trace/expanders.py b/blender_asset_tracer/trace/expanders.py index 0437333..397992b 100644 --- a/blender_asset_tracer/trace/expanders.py +++ b/blender_asset_tracer/trace/expanders.py @@ -152,16 +152,20 @@ def _expand_generic_idprops(block: blendfile.BlendFileBlock): def _expand_generic_nodetree_id(block: blendfile.BlendFileBlock): - if block.bfile.header.version >= 500 and block.bfile.file_subversion >= 4: - # Introduced in Blender 5.0, commit bd61e69be5a7c96f1e5da1c86aafc17b839e049f + # compositing_node_group replaced nodetree for Scene blocks in Blender 5.0. + if block.has_field(b"compositing_node_group"): block_ntree = block.get_pointer(b"compositing_node_group", None) - else: + elif block.has_field(b"nodetree"): block_ntree = block.get_pointer(b"nodetree", None) + else: + block_ntree = None if block_ntree is not None: yield from _expand_generic_nodetree(block_ntree) def _expand_generic_animdata(block: blendfile.BlendFileBlock): + if not block.has_field(b"adt"): + return block_adt = block.get_pointer(b"adt") if block_adt: yield block_adt.get_pointer(b"action") @@ -187,7 +191,8 @@ def _expand_curve(block: blendfile.BlendFileBlock): b"taperobj", b"textoncurve", ): - yield block.get_pointer(fieldname) + if block.has_field(fieldname): + yield block.get_pointer(fieldname) @dna_code("GR") @@ -258,8 +263,9 @@ def _expand_metaball(block: blendfile.BlendFileBlock): def _expand_mesh(block: blendfile.BlendFileBlock): yield from _expand_generic_animdata(block) yield from _expand_generic_material(block) - yield block.get_pointer(b"texcomesh") - # TODO, TexFace? - it will be slow, we could simply ignore :S + # texcomesh was removed in Blender 4.0. + if block.has_field(b"texcomesh"): + yield block.get_pointer(b"texcomesh") @dna_code("NT") @@ -275,11 +281,17 @@ def _expand_object(block: blendfile.BlendFileBlock): yield block.get_pointer(b"data") - if block[b"transflag"] & cdefs.OB_DUPLIGROUP: + # dup_group was renamed to instance_collection in Blender 2.80. + if block.has_field(b"instance_collection"): + yield block.get_pointer(b"instance_collection", default=None) + elif block[b"transflag"] & cdefs.OB_DUPLIGROUP: yield block.get_pointer(b"dup_group") - yield block.get_pointer(b"proxy") - yield block.get_pointer(b"proxy_group") + # proxy and proxy_group were removed in Blender 3.0. + if block.has_field(b"proxy"): + yield block.get_pointer(b"proxy") + if block.has_field(b"proxy_group"): + yield block.get_pointer(b"proxy_group") # 'ob->pose->chanbase[...].custom' block_pose = block.get_pointer(b"pose") @@ -312,11 +324,18 @@ def _expand_particle_settings(block: blendfile.BlendFileBlock): yield from _expand_generic_animdata(block) yield from _expand_generic_mtex(block) + # dup_group/dup_ob were renamed to instance_collection/instance_object in Blender 2.80. block_ren_as = block[b"ren_as"] if block_ren_as == cdefs.PART_DRAW_GR: - yield block.get_pointer(b"dup_group") + if block.has_field(b"instance_collection"): + yield block.get_pointer(b"instance_collection") + else: + yield block.get_pointer(b"dup_group") elif block_ren_as == cdefs.PART_DRAW_OB: - yield block.get_pointer(b"dup_ob") + if block.has_field(b"instance_object"): + yield block.get_pointer(b"instance_object") + else: + yield block.get_pointer(b"dup_ob") @dna_code("SC") diff --git a/blender_asset_tracer/trace/file2blocks.py b/blender_asset_tracer/trace/file2blocks.py index af5ff2e..08d8c53 100644 --- a/blender_asset_tracer/trace/file2blocks.py +++ b/blender_asset_tracer/trace/file2blocks.py @@ -69,6 +69,7 @@ class BlockIterator: """Open a blend file, sending notification about this to the progress callback.""" log.info("opening: %s", bfilepath) + print(f"[BAT] Reading blend file: {bfilepath}") self.progress_cb.trace_blendfile(bfilepath) return blendfile.open_cached(bfilepath) diff --git a/blender_asset_tracer/trace/modifier_walkers.py b/blender_asset_tracer/trace/modifier_walkers.py index a4d29b7..19d6b33 100644 --- a/blender_asset_tracer/trace/modifier_walkers.py +++ b/blender_asset_tracer/trace/modifier_walkers.py @@ -233,8 +233,12 @@ def modifier_particle_system( def modifier_fluid_sim( ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes ) -> typing.Iterator[result.BlockUsage]: + """Legacy fluid sim, removed in Blender 3.0.""" my_log = log.getChild("modifier_fluid_sim") + if not modifier.has_field(b"fss"): + return + fss = modifier.get_pointer(b"fss") if fss is None: my_log.debug( @@ -257,8 +261,12 @@ def modifier_fluid_sim( def modifier_smoke_sim( ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes ) -> typing.Iterator[result.BlockUsage]: + """Legacy smoke sim, removed in Blender 2.82 (replaced by Fluid modifier).""" my_log = log.getChild("modifier_smoke_sim") + if not modifier.has_field(b"domain"): + return + domain = modifier.get_pointer(b"domain") if domain is None: my_log.debug(