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