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.
5 changed files with 24 additions and 27 deletions
Showing only changes of commit 0f904d03c5 - Show all commits

5
.gitignore vendored
View File

@ -10,7 +10,4 @@ __pycache__
/dist/
/build/
/docs/_build
/.env
/.venv
/docs/_build

View File

@ -127,7 +127,9 @@ class BlendFile:
self.blocks = [] # type: BFBList
"""BlendFileBlocks of this file, in disk order."""
self.code_index = collections.defaultdict(list) # type: typing.Dict[bytes, BFBList]
self.code_index = collections.defaultdict(
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.sdna_index_from_id = {} # type: typing.Dict[bytes, int]
self.block_from_addr = {} # type: typing.Dict[int, BlendFileBlock]

View File

@ -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
@ -72,26 +73,22 @@ def modifiers(object_block: BlendFileBlock) -> typing.Iterator[BlendFileBlock]:
yield from listbase(mods, next_path=(b"modifier", b"next"))
JonasDichelle marked this conversation as resolved

I think it's fine to make this function a method on BlendFileBlock. It could then simply be named .clone() or .copy().

Then again, is there any reason to not use copy.copy() and avoid the need for this function altogether?

I think it's fine to make this function a method on `BlendFileBlock`. It could then simply be named `.clone()` or `.copy()`. Then again, is there any reason to not use [copy.copy()](https://docs.python.org/3/library/copy.html#copy.copy) and avoid the need for this function altogether?
Review

yes copy should work fine for this too.

yes copy should work fine for this too.
def copy_block(block: BlendFileBlock) -> BlendFileBlock:
"""Create a new BlendFileBlock instance with the same slot data as the provided block."""
if not isinstance(block, BlendFileBlock):
raise ValueError("The existing_block must be an instance of BlendFileBlock")
new_block = BlendFileBlock(block.bfile)
for slot in BlendFileBlock.__slots__:
setattr(new_block, slot, getattr(block, slot))
return new_block
def dynamic_array(block: BlendFileBlock) -> typing.Iterator[BlendFileBlock]:
"""Generator, yields all blocks in a block that is a dynamic array."""
"""
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.
"""
offset = block.file_offset
element_size = block.dna_type.size
for i in range(block.count):
new_block = copy_block(block)
new_block = copy.copy(block)

Please add a bit more explanation to this documentation. This first line is fine, but then I would love to see some explanation of what a "dynamic array" is in this context.

Please add a bit more explanation to this documentation. This first line is fine, but then I would love to see some explanation of what a "dynamic array" is in this context.
new_block.file_offset = offset
new_block.size = block.dna_type.size // block.count
new_block.size = element_size
yield new_block
offset += block.dna_type.size
offset += element_size
JonasDichelle marked this conversation as resolved Outdated

Instead of making Python do the block.dna_type.size lookup twice per loop, do that once outside the loop and store the value in a local variable.

block_size = block.dna_type.size // block.count
Instead of making Python do the `block.dna_type.size` lookup twice per loop, do that once outside the loop and store the value in a local variable. ``` block_size = block.dna_type.size // block.count ```

View File

@ -52,6 +52,9 @@ eModifierType_MeshSequenceCache = 52
eModifierType_Fluid = 56
eModifierType_Nodes = 57
# NodesModifierBakeFlag
NODES_MODIFIER_BAKE_CUSTOM_PATH = 1 << 1
# DNA_particle_types.h
PART_DRAW_OB = 7
PART_DRAW_GR = 8
@ -101,4 +104,4 @@ PTCACHE_PATH = b"blendcache_"
# BKE_node.h
SH_NODE_TEX_IMAGE = 143
CMP_NODE_R_LAYERS = 221
CMP_NODE_R_LAYERS = 221

View File

@ -369,9 +369,7 @@ def modifier_nodes(
)
flag = bake.get(b"flag")
flag_bin = bin(flag)
flag_bin_padded = flag_bin[2:].zfill(2)
use_custom_directory = flag_bin_padded[0] == "1"
use_custom_directory = bool(flag & cdefs.NODES_MODIFIER_BAKE_CUSTOM_PATH)
if use_custom_directory:
JonasDichelle marked this conversation as resolved

Please don't use string operations to get a single bit flag. Add a constant to cdefs.py with the name of the flag, then use something like:

use_custom_path = bool(flag & cdefs.NODES_MODIFIER_BAKE_CUSTOM_PATH)
Please don't use string operations to get a single bit flag. Add a constant to `cdefs.py` with the name of the flag, then use something like: ```python use_custom_path = bool(flag & cdefs.NODES_MODIFIER_BAKE_CUSTOM_PATH) ```
directory_ptr = bake_directory_ptr
@ -382,7 +380,7 @@ def modifier_nodes(
field = mod_directory_field
block = modifier
if directory_ptr == 0:
if not directory_ptr:
continue
directory = bake.bfile.dereference_pointer(directory_ptr)
JonasDichelle marked this conversation as resolved

I think it's slightly nicer to not compare to a concrete value, and just use if not directory_ptr:

I think it's slightly nicer to not compare to a concrete value, and just use `if not directory_ptr:`
if not directory: