Previously, .object.data and .instance_object.data were always the same. Now they can be different and it is correct to use .object.data. object_instance.object: A temporary object wrapping the instance. References to this object must not be stored between loop iterations.
Our addon used to map bpy.types.Object to our own data classes in a dictionary, something like this:
my_objs = dict()
for ob in [x for x in depsgraph.ids if isinstance(x, bpy.types.Object)]:
my_obj = export(ob)
my_objs[ob.original] = my_obj
# do something my_obj
however, with this new change this doesn’t seem to be the recommended any more. It seems the recommended way would be to use the object.data as the key. Something like:
my_objs = dict()
for ob_inst in depsgraph.object_instances:
ob = ob_inst.object
if ob.data.original in my_objs:
my_obj = my_objs.get(ob.data.original)
my_objs[ob.data.original] = my_obj
else:
my_obj = export(ob)
# do something with my_obj
This works in that my_obj gets re-used when the same object.data is used. However, this doesn’t seem to work when looping through despgraph.updates during a viewport render (ex: the user is editing the mesh). We can’t seem to use the DepsgraphUpdate.id.original as the key to the dictionary, as it says that key doesn’t exist.
Are we going about this the wrong way? I’d rather not use the .name as the key as I really don’t want to have to deal with renaming.
I think the difference is that you’e not comparing with what’s coming from depsgraph. Does this work for you?
import bpy
depsgraph = bpy.context.evaluated_depsgraph_get()
objs = dict()
cube = bpy.data.objects['Cube'].data.original
objs[cube] = True
print("Find cube in despgraph.object_instances")
for ob_inst in depsgraph.object_instances:
test = ob_inst.object.data.original
if test in objs:
print("\tFound cube")
print("Finished search")
i.e.: does “Found cube” get printed?
I thought these should be the same. It also seems object.data of instances of the Cube on a different object (ex: a curve that uses the Cube with the “instances on points” geometry node) are different than instances of the Cube itself.
I wrote a simple render engine addon to test this.
import bpy
import bgl
import blf
import numpy as np
bl_info = {
"name": "Test Render Engnie",
"author": "ihsieh",
"version": (1, 0, 0),
"blender": (3, 0, 0),
"location": "Info Header, render engine menu",
"description": "My Test RE",
"warning": "",
"category": "Render"}
class MyMesh:
def __init__(self, name):
self.name = name
self.points = []
def update(self, mesh):
nvertices = len(mesh.vertices)
P = np.zeros(nvertices*3, dtype=np.float32)
mesh.vertices.foreach_get('co', P)
P = np.reshape(P, (nvertices, 3))
self.points = P.tolist()
class TestRenderEngine(bpy.types.RenderEngine):
bl_idname = 'TEST_RENDER'
bl_label = "TestRenderEngine"
bl_use_preview = False
bl_use_save_buffers = True
bl_use_shading_nodes = True
bl_use_eevee_viewport = True
bl_use_postprocess = True
def __init__(self):
self.scene_data = None
def __del__(self):
pass
def render(self, depsgraph):
pass
def view_update(self, context, depsgraph):
region = context.region
view3d = context.space_data
scene = depsgraph.scene
objects_updated = list()
if not self.scene_data:
# First time initialization
self.scene_data = dict()
# Loop over all datablocks used in the scene.
for datablock in depsgraph.ids:
if not isinstance(datablock, bpy.types.Mesh):
continue
my_mesh = MyMesh(datablock.name)
self.scene_data[datablock.original] = my_mesh
# Loop over all instances
for instance in depsgraph.object_instances:
ob = instance.object
if ob.type != 'MESH':
continue
datablock = ob.data
if datablock.original not in self.scene_data:
# this datablock didn't get exported in the loop above
my_mesh = MyMesh(datablock.name)
self.scene_data[datablock.original] = my_mesh
else:
my_mesh = self.scene_data[datablock.original]
my_mesh.update(datablock)
else:
objects_updated = list()
# Test which datablocks changed
for update in depsgraph.updates:
if isinstance(update.id, bpy.types.Object):
print("Datablock updated: %s" % update.id.name)
objects_updated.append(update.id.original)
# Loop over all object instances, see which instances changed.
if depsgraph.id_type_updated('OBJECT'):
for instance in depsgraph.object_instances:
ob = instance.object
if ob.type != 'MESH':
continue
if instance.is_instance:
if instance.instance_object.original not in objects_updated:
print("\tInstance object not updated: %s" % ob.name)
continue
elif ob.original not in objects_updated:
print("\tObject not updated: %s" % ob.name)
continue
datablock = ob.data
if datablock.original not in self.scene_data:
print("\tNot in objects_updated: %s" % datablock.name)
continue
my_mesh = self.scene_data[datablock.original]
print("\tUpdating datablock: %s" % datablock.name)
my_mesh.update(datablock)
def view_draw(self, context, depsgraph):
pass
classes = [
TestRenderEngine,
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in classes:
try:
bpy.utils.unregister_class(cls)
except RuntimeError:
pass
Using the scene file attached (the forum doesn’t to allow for uploads of .blend file, so I name it .txt), how do I know if the cubes that the BezierCircle has instanced have been updated? Ex: open the scene, start a viewport render, select Cube and press Tab to go to edit mode. The depsgraph.updates loop let’s me know that BezierCurve object has updated, but I need to reference its data from our scene_data dictionary.