Fix Blender 5.0 compatibility and add console logging for pack operations
Guard DNA field accesses with has_field() checks for fields renamed or removed across Blender versions (proxy, dup_group, texcomesh, nodetree, etc.), fix mesh block pointer mismatch in path rewriting, fix mkrelative IndexError when asset path is shorter than blend file path, and add [BAT] console prints for tracing progress. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4085f0015f
commit
f79c6a276d
@ -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()
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user