To the doubters, oracles, know alls, righteous, disingenuous & those that like to find a solution to something that they say can’t be done
test this out.
It uses the suggestion (aka terrible workaround) of setting up a driver and using driver targets as a way to keep object references.
The bake object list has a field to keep the driver variable name that references the cage object, & another to flag to see if it is a valid object.
AFAICT it passes the rename, copy and ‘UNDO’ tests.
It will fail ofcourse if the driver is deleted, (or drivers are not enabled) or the power is off or whatever.
I also have code that does the same with id props, but I find this way preferable… less tests and more efficient, as there is the need to iterate thru the objects in some cases.
import bpy
from bpy.props import (CollectionProperty,
IntProperty,
BoolProperty,
FloatProperty,
StringProperty)
def set_cage(self, objname):
self.name = objname
bake_object = self
scene = self.id_data
self.save_cage_object = objname
obj = scene.objects.get(objname)
if obj is not None:
self.valid_cage_object = True
dref = [d for d in scene.animation_data.drivers
if d.data_path.startswith('["cage_object"]')]
if len(dref):
d = dref[0]
var = d.driver.variables.get(self.driver_var_name)
if var is None:
var = d.driver.variables.new()
self.driver_var_name = var.name
var.targets[0].id = obj
return None
def get_cage(self):
# if there is an object return it
scene = self.id_data
obj = scene.objects.get(self.save_cage_object)
if obj is not None:
self.valid_cage_object = True
return self.save_cage_object
# ok it's been removed or renamed
dref = [d for d in scene.animation_data.drivers if d.data_path.startswith('["cage_object"]')]
if len(dref):
d = dref[0].driver
var = d.variables.get(self.driver_var_name)
if var is not None:
test = var.targets[0].id
if test is not None:
print("setting from name")
self.save_cage_object = test.name
else:
self.valid_cage_object = False
return self.save_cage_object
class BakeObjectList(bpy.types.PropertyGroup):
'''name = bpy.props.PointerProperty()''' #name of the object
index = IntProperty()
driver_var_name = StringProperty() # var name matching object
save_cage_object = StringProperty() # save the object name for quick ref.
cage_object = StringProperty(set=set_cage, get=get_cage)
extrusion = FloatProperty(name="Extrusion", default = 0)
active = BoolProperty()
valid_cage_object = BoolProperty(default=False)
uv_map = StringProperty()
def register():
bpy.utils.register_module(__name__)
bpy.types.Scene.bake_objects = CollectionProperty(type=BakeObjectList)
bpy.types.Scene.bake_object_index = IntProperty()
def unregister():
bpy.utils.unregister_module(__name__)
del bpy.types.Scene.bake_objects
del bpy.types.Scene.bake_object_index
if __name__ == '__main__':
register()
scene = bpy.context.scene
bpy.context.scene['cage_object'] = 0
dref = [d for d in scene.animation_data.drivers
if d.data_path.startswith('["cage_object"]')]
if not len(dref):
scene.driver_add('["cage_object"]')
else:
scene["cage_object"] = len(dref[0].driver.variables)
#add the Cube to bake_objects
o = bpy.context.scene.bake_objects.add()
o.cage_object = "Cube"
And a quick panel code to test
import bpy
class LayoutDemoPanel(bpy.types.Panel):
"""Creates a Panel in the scene context of the properties editor"""
bl_label = "Layout Demo"
bl_idname = "SCENE_PT_layout"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
bos = context.scene.bake_objects
for bo in bos:
row = layout.row()
row.alert = not bo.valid_cage_object
row.prop(bo, "cage_object")
row.prop_search(bo, "cage_object", scene, "objects", text="")
def register():
bpy.utils.register_class(LayoutDemoPanel)
def unregister():
bpy.utils.unregister_class(LayoutDemoPanel)
if __name__ == "__main__":
register()