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:
Jonas Dichelle
2025-11-24 13:06:38 +01:00
committed by Sybren A. Stüvel
co-authored by Sybren A. Stüvel
parent 94486a3218
commit 50578ac62a
40 changed files with 192 additions and 3 deletions
+11 -2
View File
@@ -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