Add Support for Geometry Node Cache #92890

Open
Jonas Dichelle wants to merge 14 commits from JonasDichelle/blender-asset-tracer:geonodes_support into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
3 changed files with 102 additions and 6 deletions
Showing only changes of commit 8a6c103f7d - Show all commits

5
.gitignore vendored
View File

@ -11,3 +11,8 @@ __pycache__
/build/ /build/
/docs/_build /docs/_build
# envs
JonasDichelle marked this conversation as resolved Outdated

The use of these directories is quite personal, and differs from developer to developer. Please put those into your .git/info/exclude file. That behaves like an extra .gitignore but isn't tracked by Git.

The use of these directories is quite personal, and differs from developer to developer. Please put those into your `.git/info/exclude` file. That behaves like an extra `.gitignore` but isn't tracked by Git.
/.env
/.venv

View File

@ -127,9 +127,7 @@ class BlendFile:
self.blocks = [] # type: BFBList self.blocks = [] # type: BFBList
"""BlendFileBlocks of this file, in disk order.""" """BlendFileBlocks of this file, in disk order."""
self.code_index = collections.defaultdict( self.code_index = collections.defaultdict(list) # type: typing.Dict[bytes, BFBList]
JonasDichelle marked this conversation as resolved Outdated

Please keep formatting changes out of this PR. There is no direct need for this PR to modify this file.

Of course non-functional improvements like this are always welcome, but shouldn't be part of the same PR as functional changes.

Please keep formatting changes out of this PR. There is no direct need for this PR to modify this file. Of course non-functional improvements like this are always welcome, but shouldn't be part of the same PR as functional changes.
list
) # type: typing.Dict[bytes, BFBList]
self.structs = [] # type: typing.List[dna.Struct] self.structs = [] # type: typing.List[dna.Struct]
self.sdna_index_from_id = {} # type: typing.Dict[bytes, int] self.sdna_index_from_id = {} # type: typing.Dict[bytes, int]
self.block_from_addr = {} # type: typing.Dict[int, BlendFileBlock] self.block_from_addr = {} # type: typing.Dict[int, BlendFileBlock]
@ -352,9 +350,25 @@ class BlendFile:
else: else:
dna_size = dna_type.size * dna_name.array_size dna_size = dna_type.size * dna_name.array_size
field = dna.Field(dna_type, dna_name, dna_size, dna_offset) if dna_name.name_only == b"bakes":
dna_struct.append_field(field) array_count = 3 # Total elements in the array
dna_offset += dna_size item_size = 48 # Size of each item in the array
dna_size = (
item_size * array_count
) # Total size occupied by the array
# Create a single field for the entire array
field = dna.Field(dna_type, dna_name, dna_size, dna_offset)
dna_struct.append_field(field)
dna_offset += (
dna_size # Increment offset by the total size of the array
)
else:
# For other fields, proceed as usual
field = dna.Field(dna_type, dna_name, dna_size, dna_offset)
dna_struct.append_field(field)
dna_offset += dna_size
def abspath(self, relpath: bpathlib.BlendPath) -> bpathlib.BlendPath: def abspath(self, relpath: bpathlib.BlendPath) -> bpathlib.BlendPath:
"""Construct an absolute path from a blendfile-relative path.""" """Construct an absolute path from a blendfile-relative path."""
@ -796,6 +810,29 @@ class BlendFileBlock:
continue continue
yield dereferenced yield dereferenced
def iter_bakes(self, array_size: int) -> typing.Iterator["BlendFileBlock"]:
"""Iterate over an array of NodesModifierBake pointers with known size."""
path = (
b"bakes" # Assuming 'bakes' is the DNA field name for the array of pointers
)
if array_size == 0:
return
array = self.get_pointer(path)
assert array and array.code == b"DATA", "Expected DATA block, got {}".format(
array.code.decode()
)
file_offset = array.file_offset
ps = self.bfile.header.pointer_size
for i in range(array_size):
address = self.bfile.read_pointer_at(file_offset + ps * i)
if address:
dereferenced = self.bfile.dereference_pointer(address)
if dereferenced:
yield dereferenced
def __getitem__(self, path: dna.FieldPath): def __getitem__(self, path: dna.FieldPath):
return self.get(path) return self.get(path)

View File

@ -22,12 +22,18 @@
The modifier_xxx() functions all yield result.BlockUsage objects for external The modifier_xxx() functions all yield result.BlockUsage objects for external
files used by the modifiers. files used by the modifiers.
""" """
import struct
import logging import logging
import typing import typing
from blender_asset_tracer.blendfile import iterators
from blender_asset_tracer.blendfile.dna import Struct
from blender_asset_tracer import blendfile, bpathlib, cdefs from blender_asset_tracer import blendfile, bpathlib, cdefs
from . import result from . import result
import ctypes
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
modifier_handlers = {} # type: typing.Dict[int, typing.Callable] modifier_handlers = {} # type: typing.Dict[int, typing.Callable]
@ -317,3 +323,51 @@ def modifier_cloth(
yield from _walk_point_cache( yield from _walk_point_cache(
ctx, block_name, modifier.bfile, pointcache, cdefs.PTCACHE_EXT ctx, block_name, modifier.bfile, pointcache, cdefs.PTCACHE_EXT
) )
def split_bytes_array(raw_data: bytes, item_size: int) -> typing.Iterator[bytes]:
for i in range(0, len(raw_data), item_size):
data_part = raw_data[i : i + item_size]
yield data_part
def bytes_to_struct(data_bytes: bytes, struct_type: Struct):
"""Convert raw bytes into a struct based on the provided Struct definition."""
struct_instance = {}
for field in struct_type.fields:
start = field.offset
end = start + field.size
field_data = data_bytes[start:end]
value = field_data.hex()
struct_instance[field.name.name_only] = value
return struct_instance
@mod_handler(cdefs.eModifierType_Nodes)
def modifier_nodes(
ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes
) -> typing.Iterator[result.BlockUsage]:
bake_directory = modifier.get_pointer(b"simulation_bake_directory")
# print(bake_directory)
bakes_ptr = modifier.get(b"bakes")
bakes_num = modifier.get(b"bakes_num")
bakes = modifier.get_pointer(b"bakes")
raw_bakes = bakes.raw_data()
dna_type = bakes.dna_type
print(list(bakes.values()))
bakes_split = split_bytes_array(raw_bakes, dna_type.size)
for bake_bytes in bakes_split:
bake_struct = bytes_to_struct(bake_bytes, dna_type)
print(bake_struct)
# yield bake_struct
quit()