Add Support for Geometry Node Cache (#92890)
Add support for geometry node simulation cache files. This also adds support for dealing with dynamic arrays in Blender's DNA, because `modifier.bakes` is a pointer to such an array. Co-authored-by: Sybren A. Stüvel <sybren@blender.org> Reviewed-on: https://projects.blender.org/blender/blender-asset-tracer/pulls/92890
This commit is contained in:
committed by
Sybren A. Stüvel
co-authored by
Sybren A. Stüvel
parent
94486a3218
commit
50578ac62a
@@ -633,8 +633,8 @@ class BlendFileBlock:
|
||||
self.bfile.fileobj.seek(self.file_offset, os.SEEK_SET)
|
||||
return self.bfile.fileobj.read(self.size)
|
||||
|
||||
def as_string(self) -> str:
|
||||
"""Interpret the bytes of this datablock as null-terminated utf8 string."""
|
||||
def as_bytes_string(self) -> bytes:
|
||||
"""Interpret the bytes of this datablock as null-terminated string of raw bytes."""
|
||||
the_bytes = self.raw_data()
|
||||
try:
|
||||
first_null = the_bytes.index(0)
|
||||
@@ -642,6 +642,11 @@ class BlendFileBlock:
|
||||
pass
|
||||
else:
|
||||
the_bytes = the_bytes[:first_null]
|
||||
return the_bytes
|
||||
|
||||
def as_string(self) -> str:
|
||||
"""Interpret the bytes of this datablock as null-terminated utf8 string."""
|
||||
the_bytes = self.as_bytes_string()
|
||||
return the_bytes.decode()
|
||||
|
||||
def get_recursive_iter(
|
||||
@@ -820,6 +825,10 @@ class BlendFileBlock:
|
||||
def __setitem__(self, item: bytes, value) -> None:
|
||||
self.set(item, value)
|
||||
|
||||
def has_field(self, name: bytes) -> bool:
|
||||
dna_struct = self.bfile.structs[self.sdna_index]
|
||||
return dna_struct.has_field(name)
|
||||
|
||||
def keys(self) -> typing.Iterator[bytes]:
|
||||
"""Generator, yields all field names of this block."""
|
||||
return (f.name.name_only for f in self.dna_type.fields)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
# (c) 2014, Blender Foundation - Campbell Barton
|
||||
# (c) 2018, Blender Foundation - Sybren A. Stüvel
|
||||
import typing
|
||||
import copy
|
||||
|
||||
from blender_asset_tracer import cdefs
|
||||
from . import BlendFileBlock
|
||||
@@ -70,3 +71,24 @@ def modifiers(object_block: BlendFileBlock) -> typing.Iterator[BlendFileBlock]:
|
||||
# 'ob->modifiers[...]'
|
||||
mods = object_block.get_pointer((b"modifiers", b"first"))
|
||||
yield from listbase(mods, next_path=(b"modifier", b"next"))
|
||||
|
||||
|
||||
def dynamic_array(block: BlendFileBlock) -> typing.Iterator[BlendFileBlock]:
|
||||
"""
|
||||
Generator that yields each element of a dynamic array as a separate block.
|
||||
|
||||
Dynamic arrays are multiple contiguous elements accessed via a single
|
||||
pointer. BAT interprets these as a single data block, making it hard to
|
||||
access individual elements. This function divides the array into individual
|
||||
blocks by creating modified copies of the original block.
|
||||
"""
|
||||
|
||||
element_size = block.dna_type.size
|
||||
|
||||
sub_block = copy.copy(block)
|
||||
sub_block.size = element_size
|
||||
|
||||
for i in range(block.count):
|
||||
# When sub_block's data is read, it'll be read from this offset in the blend file.
|
||||
sub_block.file_offset = block.file_offset + i * element_size
|
||||
yield sub_block
|
||||
|
||||
@@ -52,6 +52,14 @@ eModifierType_MeshSequenceCache = 52
|
||||
eModifierType_Fluid = 56
|
||||
eModifierType_Nodes = 57
|
||||
|
||||
# NodesModifierBakeFlag
|
||||
NODES_MODIFIER_BAKE_CUSTOM_PATH = 1 << 1
|
||||
|
||||
# NodesModifierBakeTarget
|
||||
NODES_MODIFIER_BAKE_TARGET_INHERIT = 0
|
||||
NODES_MODIFIER_BAKE_TARGET_PACKED = 1
|
||||
NODES_MODIFIER_BAKE_TARGET_DISK = 2
|
||||
|
||||
# DNA_particle_types.h
|
||||
PART_DRAW_OB = 7
|
||||
PART_DRAW_GR = 8
|
||||
|
||||
@@ -27,6 +27,7 @@ import logging
|
||||
import typing
|
||||
|
||||
from blender_asset_tracer import blendfile, bpathlib, cdefs
|
||||
from blender_asset_tracer.blendfile import iterators
|
||||
from . import result
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -337,7 +338,7 @@ def modifier_dynamic_paint(
|
||||
|
||||
surfaces = canvas_settings.get_pointer((b"surfaces", b"first"))
|
||||
|
||||
for surf_idx, surface in enumerate(blendfile.iterators.listbase(surfaces)):
|
||||
for surf_idx, surface in enumerate(iterators.listbase(surfaces)):
|
||||
surface_block_name = block_name + b".canvas_settings.surfaces[%d]" % (surf_idx)
|
||||
point_cache = surface.get_pointer(b"pointcache")
|
||||
if point_cache is None:
|
||||
@@ -351,3 +352,62 @@ def modifier_dynamic_paint(
|
||||
yield from _walk_point_cache(
|
||||
ctx, surface_block_name, modifier.bfile, point_cache, cdefs.PTCACHE_EXT
|
||||
)
|
||||
|
||||
|
||||
@mod_handler(cdefs.eModifierType_Nodes)
|
||||
def modifier_nodes(
|
||||
ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes
|
||||
) -> typing.Iterator[result.BlockUsage]:
|
||||
if not modifier.has_field(b"simulation_bake_directory"):
|
||||
return
|
||||
|
||||
mod_directory_ptr, mod_directory_field = modifier.get(
|
||||
b"simulation_bake_directory", return_field=True
|
||||
)
|
||||
|
||||
bakes = modifier.get_pointer(b"bakes")
|
||||
if not bakes:
|
||||
return
|
||||
|
||||
mod_bake_target = modifier.get(b"bake_target")
|
||||
|
||||
for bake_idx, bake in enumerate(iterators.dynamic_array(bakes)):
|
||||
# Check for packed data.
|
||||
bake_target = bake.get(b"bake_target")
|
||||
if bake_target == cdefs.NODES_MODIFIER_BAKE_TARGET_INHERIT:
|
||||
bake_target = mod_bake_target
|
||||
if bake_target == cdefs.NODES_MODIFIER_BAKE_TARGET_PACKED:
|
||||
# This data is packed in the blend file, it's not a dependency to trace.
|
||||
continue
|
||||
|
||||
flag = bake.get(b"flag")
|
||||
use_custom_directory = bool(flag & cdefs.NODES_MODIFIER_BAKE_CUSTOM_PATH)
|
||||
|
||||
if use_custom_directory:
|
||||
bake_directory_ptr, bake_directory_field = bake.get(
|
||||
b"directory", return_field=True
|
||||
)
|
||||
directory_ptr = bake_directory_ptr
|
||||
field = bake_directory_field
|
||||
block = bake
|
||||
else:
|
||||
directory_ptr = mod_directory_ptr
|
||||
field = mod_directory_field
|
||||
block = modifier
|
||||
|
||||
if not directory_ptr:
|
||||
continue
|
||||
directory = bake.bfile.dereference_pointer(directory_ptr)
|
||||
if not directory:
|
||||
continue
|
||||
|
||||
bpath = bpathlib.BlendPath(directory.as_bytes_string())
|
||||
bake_block_name = block_name + b".bakes[%d]" % bake_idx
|
||||
|
||||
yield result.BlockUsage(
|
||||
block,
|
||||
bpath,
|
||||
block_name=bake_block_name,
|
||||
path_full_field=field,
|
||||
is_sequence=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user