Support linked collections used as input in a Geometry Nodes modifier

Add support for linked collections that are used as input in a Geometry
Nodes modifier. This requires iterating over the geometry nodes modifier
settings, which consists of ID properties. If such an ID property is of
type `IDP_ID`, its pointer is followed and the pointed-to datablock +
its library are visited.

This following of pointers happens in the 'expand' phase, which was only
done for linked library blend files. Since this commit, the old
behaviour of simply looping over all non-`DATA` datablocks of the
to-be-packed blend file is not enough, and datablock expansion is done
for all local datablocks as well.
This commit is contained in:
Sybren A. Stüvel 2021-07-27 16:06:41 +02:00
parent 932283be65
commit 0bd18594f6
8 changed files with 69 additions and 5 deletions

View File

@ -3,6 +3,11 @@
This file logs the changes that are actually interesting to users (new features,
changed functionality, fixed bugs).
## Version 1.6 (in development)
- Support linked collections used as input in a Geometry Nodes modifier.
## Version 1.5.1 (2021-07-22)
- Add log warning if SegmentationFault caused by dereferencing invalid pointer is silenced when strict_pointer_mode is turned off.

View File

@ -61,6 +61,17 @@ OB_DUPLIGROUP = 1 << 8
PTCACHE_DISK_CACHE = 64
PTCACHE_EXTERNAL = 512
# DNA_ID_types.h
IDP_STRING = 0
IDP_INT = 1
IDP_FLOAT = 2
IDP_ARRAY = 5
IDP_GROUP = 6
IDP_ID = 7
IDP_DOUBLE = 8
IDP_IDPARRAY = 9
IDP_NUMTYPES = 10
# BKE_pointcache.h
PTCACHE_FILE_PTCACHE = 0
PTCACHE_FILE_OPENVDB = 1

View File

@ -103,6 +103,28 @@ def _expand_generic_nodetree(block: blendfile.BlendFileBlock):
yield node.get_pointer(b"id")
def _expand_generic_idprops(block: blendfile.BlendFileBlock):
"""Yield ID datablocks and their libraries referenced from ID properties."""
# TODO(@sybren): this code is very crude, and happens to work on ID
# properties of Geometry Nodes modifiers, which is what it was written for.
# It should probably be rewritten to properly iterate over & recurse into
# all groups.
settings_props = block.get_pointer((b"settings", b"properties"))
if not settings_props:
return
subprops = settings_props.get_pointer((b"data", b"group", b"first"))
for idprop in iterators.listbase(subprops):
if idprop[b"type"] != cdefs.IDP_ID:
continue
id_datablock = idprop.get_pointer((b"data", b"pointer"))
if not id_datablock:
continue
yield id_datablock
yield id_datablock.get_pointer(b"lib")
def _expand_generic_nodetree_id(block: blendfile.BlendFileBlock):
block_ntree = block.get_pointer(b"nodetree", None)
if block_ntree is not None:
@ -251,6 +273,7 @@ def _expand_object(block: blendfile.BlendFileBlock):
# Currently only node groups are supported. If the support should expand
# to more types, something more intelligent than this should be made.
if mod_type == cdefs.eModifierType_Nodes:
yield from _expand_generic_idprops(block_mod)
yield block_mod.get_pointer(b"node_group")

View File

@ -110,10 +110,6 @@ class BlockIterator:
self.to_visit.put(lib)
continue
if limit_to:
# We're limiting the blocks, so we have to expand them to make
# sure we don't miss anything. Otherwise we're yielding the
# entire file anyway, and no expansion is necessary.
self._queue_dependencies(block)
self.blocks_yielded.add((bpath, block.addr_old))
yield block
@ -167,6 +163,7 @@ class BlockIterator:
def _queue_dependencies(self, block: blendfile.BlendFileBlock):
for block in expanders.expand_block(block):
assert isinstance(block, blendfile.BlendFileBlock), "unexpected %r" % block
self.to_visit.put(block)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -436,6 +436,34 @@ class DepsTest(AbstractTracerTest):
},
)
def test_geometry_nodes_modifier_input(self):
"""Test linked collection as input to geom nodes modifier.
Here a Geometry Nodes modifier references a collection that is not
instanced into the scene, which caused it to be missed.
"""
self.assert_deps(
"geometry-nodes-2/shot_file.blend",
{
b"LIset_file.blend": Expect(
type="Library",
full_field="name[1024]",
dirname_field=None,
basename_field=None,
asset_path=b"//set_file.blend",
is_sequence=False,
),
b"LIlib_trash.blend": Expect(
type="Library",
full_field="name[1024]",
dirname_field=None,
basename_field=None,
asset_path=b"//lib_trash.blend",
is_sequence=False,
),
},
)
def test_usage_abspath(self):
deps = [
dep