BlendFileBlock.get to support accessing different items in the file-block (#92898)

Add an `array_index` parameter to `block.get(property_name)` to get a
specific item from an array.

Example:
```python
verts = self.bf.block_from_addr[verts_ptr]
assert verts.get(b"co") == [-1.0, -1.0, -1.0]  # index 0
assert verts.get(b"co", array_index=1) == [-1.0, -1.0, 1.0]
```

Reviewed-on: https://projects.blender.org/blender/blender-asset-tracer/pulls/92898
This commit is contained in:
Andrej730 2025-11-24 14:53:15 +01:00 committed by Sybren A. Stüvel
parent b1c4f5e116
commit c69612b264
3 changed files with 23 additions and 1 deletions

View File

@ -596,6 +596,7 @@ class BlendFileBlock:
null_terminated=True, null_terminated=True,
as_str=False, as_str=False,
return_field=False, return_field=False,
array_index=0,
) -> typing.Any: ) -> typing.Any:
"""Read a property and return the value. """Read a property and return the value.
@ -612,8 +613,20 @@ class BlendFileBlock:
(assumes UTF-8 encoding). (assumes UTF-8 encoding).
:param return_field: When True, returns tuple (dna.Field, value). :param return_field: When True, returns tuple (dna.Field, value).
Otherwise just returns the value. Otherwise just returns the value.
:param array_index: If the property is an array, this determines the
index of the returned item from that array. Also see
`blendfile.iterators.dynamic_array()` for iterating such arrays.
""" """
self.bfile.fileobj.seek(self.file_offset, os.SEEK_SET) file_offset = self.file_offset
if array_index:
if not (0 <= array_index < self.count):
raise IndexError(
"Invalid 'array_index' for file-block. "
f"Expected int value in range 0-{self.count - 1}, got {array_index}."
)
file_offset += array_index * self.dna_type.size
self.bfile.fileobj.seek(file_offset, os.SEEK_SET)
dna_struct = self.bfile.structs[self.sdna_index] dna_struct = self.bfile.structs[self.sdna_index]
field, value = dna_struct.field_get( field, value = dna_struct.field_get(

View File

@ -81,6 +81,9 @@ def dynamic_array(block: BlendFileBlock) -> typing.Iterator[BlendFileBlock]:
pointer. BAT interprets these as a single data block, making it hard to pointer. BAT interprets these as a single data block, making it hard to
access individual elements. This function divides the array into individual access individual elements. This function divides the array into individual
blocks by creating modified copies of the original block. blocks by creating modified copies of the original block.
See `some_block.get(b'name', array_index)` if you want to access elements by
index (instead of iterating).
""" """
element_size = block.dna_type.size element_size = block.dna_type.size

View File

@ -46,6 +46,12 @@ class BlendFileBlockTest(AbstractBlendFileTest):
mname = mesh.get((b"id", b"name"), as_str=True) mname = mesh.get((b"id", b"name"), as_str=True)
self.assertEqual("MECube³", mname) self.assertEqual("MECube³", mname)
# Try to access different file-block items.
verts_ptr = mesh.get(b"mvert")
verts = self.bf.block_from_addr[verts_ptr]
assert verts.get(b"co") == [-1.0, -1.0, -1.0]
assert verts.get(b"co", array_index=1) == [-1.0, -1.0, 1.0]
def test_get_recursive_iter(self): def test_get_recursive_iter(self):
ob = self.bf.code_index[b"OB"][0] ob = self.bf.code_index[b"OB"][0]
assert isinstance(ob, blendfile.BlendFileBlock) assert isinstance(ob, blendfile.BlendFileBlock)