Helper script for action node editing
This commit is contained in:
282
assets/blender/buildings/parts/edit-firniture-props.py
Normal file
282
assets/blender/buildings/parts/edit-firniture-props.py
Normal file
@@ -0,0 +1,282 @@
|
||||
import bpy
|
||||
|
||||
ACTION_PROPS = ["name", "action", "action_text", "height", "radius", "tags"]
|
||||
FURNITURE_PROPS = ["name", "furniture_tags"]
|
||||
|
||||
class OBJECT_OT_CreateActionProps(bpy.types.Operator):
|
||||
"""Initializes custom properties on the selected action- empty"""
|
||||
bl_idname = "object.create_action_props"
|
||||
bl_label = "Create Action"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.object
|
||||
for p in ACTION_PROPS:
|
||||
if p not in obj:
|
||||
if p in ["height"]:
|
||||
obj[p] = 1.0
|
||||
elif p in ["radius"]:
|
||||
obj[p] = 0.5
|
||||
elif p == "name":
|
||||
obj[p] = obj.name.replace("action-","")
|
||||
elif p == "action_text":
|
||||
if "action" in obj:
|
||||
obj[p] = obj["action"].capitalize()
|
||||
else:
|
||||
obj[p] = ""
|
||||
else:
|
||||
obj[p] = "" # Initialize as empty text string
|
||||
return {'FINISHED'}
|
||||
|
||||
class OBJECT_OT_CreateFurnitureProps(bpy.types.Operator):
|
||||
"""Initializes custom properties on the selected furniture- empty"""
|
||||
bl_idname = "object.create_furniture_props"
|
||||
bl_label = "Create Furniture"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.object
|
||||
for p in FURNITURE_PROPS:
|
||||
if p not in obj:
|
||||
if p == "name":
|
||||
obj[p] = obj.name.replace("furniture-","")
|
||||
elif p == "furniture_tags":
|
||||
obj[p] = ""
|
||||
otags = []
|
||||
if obj.name.find("toilet") >= 0:
|
||||
otags.append("wc")
|
||||
otags.append("bathroom")
|
||||
otags.append("toilet")
|
||||
elif obj.name.find("bed") >= 0:
|
||||
otags.append("bed")
|
||||
otags.append("bedroom")
|
||||
obj[p] = ", ".join(otags)
|
||||
else:
|
||||
obj[p] = "" # Initialize as empty text string
|
||||
return {'FINISHED'}
|
||||
|
||||
class OBJECT_OT_CreateChildEmpties(bpy.types.Operator):
|
||||
"""Creates two child empties"""
|
||||
bl_idname = "object.create_child_empties"
|
||||
bl_label = "Create Child Nodes"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
target_val: bpy.props.StringProperty()
|
||||
def execute(self, context):
|
||||
parent = context.object
|
||||
# Define offsets and their corresponding labels for the name
|
||||
configs = []
|
||||
configs_enter = [((0, 0.5, -parent.location.z), "enter")]
|
||||
configs_exit = [((0, -0.5, -parent.location.z), "exit")]
|
||||
if self.target_val == "enter":
|
||||
configs = configs_enter
|
||||
elif self.target_val == "exit":
|
||||
configs = configs_exit
|
||||
elif self.target_val == "all":
|
||||
configs = configs_enter + configs_exit
|
||||
|
||||
for offset, label in configs:
|
||||
# Create new Empty with the "position-" prefix
|
||||
child_name = f"position-{label}.000"
|
||||
child = bpy.data.objects.new(child_name, None)
|
||||
|
||||
# Link to the current collection
|
||||
context.collection.objects.link(child)
|
||||
|
||||
# Set parent and local offset
|
||||
child.parent = parent
|
||||
child.location = offset
|
||||
child["name"] = label
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class OBJECT_OT_CreateChildActionNode(bpy.types.Operator):
|
||||
"""Creates child action node"""
|
||||
bl_idname = "object.create_child_action_node"
|
||||
bl_label = "Create Child Action Node"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
target_val: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
parent = context.object
|
||||
# Define offsets and their corresponding labels for the name
|
||||
configs = [((0, 0.1, -parent.location.z + 0.5), self.target_val)]
|
||||
|
||||
for offset, label in configs:
|
||||
# Create new Empty with the "position-" prefix
|
||||
child_name = f"action-{label}.000"
|
||||
child = bpy.data.objects.new(child_name, None)
|
||||
|
||||
# Link to the current collection
|
||||
context.collection.objects.link(child)
|
||||
|
||||
# Set parent and local offset
|
||||
child.parent = parent
|
||||
child.location = offset
|
||||
child["name"] = label
|
||||
child["action"] = label
|
||||
for p in ACTION_PROPS:
|
||||
if p not in child:
|
||||
if p in ["height"]:
|
||||
child[p] = 1.0
|
||||
elif p in ["radius"]:
|
||||
child[p] = 0.5
|
||||
elif p == "name":
|
||||
child[p] = child.name.replace("action-","")
|
||||
elif p == "action_text":
|
||||
if "action" in child:
|
||||
child[p] = child["action"].capitalize()
|
||||
else:
|
||||
child[p] = ""
|
||||
else:
|
||||
child[p] = "" # Initialize as empty text string
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class OBJECT_OT_DestroyChildByProp(bpy.types.Operator):
|
||||
"""Deletes children starting with 'position-' where custom prop 'name' matches value"""
|
||||
bl_idname = "object.destroy_child_by_prop"
|
||||
bl_label = "Destroy Child"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
target_val: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
parent = context.object
|
||||
# Collect children to delete to avoid modifying list during iteration
|
||||
to_delete = [
|
||||
child for child in parent.children
|
||||
if child.name.startswith("position-") and child.get("name") == self.target_val
|
||||
]
|
||||
|
||||
for child in to_delete:
|
||||
bpy.data.objects.remove(child, do_unlink=True)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class OBJECT_PT_ActionEmptyPanel(bpy.types.Panel):
|
||||
"""Custom GUI panel for 'action-' prefixed Empties"""
|
||||
bl_label = "Action Controls"
|
||||
bl_idname = "OBJECT_PT_action_empty_panel"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.object
|
||||
return (obj is not None and
|
||||
obj.type == 'EMPTY' and
|
||||
obj.name.startswith("action-"))
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = context.object
|
||||
|
||||
# Check if all required properties already exist
|
||||
props_exist = all(p in obj for p in ACTION_PROPS)
|
||||
|
||||
if not props_exist:
|
||||
# Show ONLY the button if properties are missing
|
||||
layout.operator("object.create_action_props", icon='PLUS')
|
||||
else:
|
||||
# Show the text fields if properties exist
|
||||
box = layout.box()
|
||||
box.label(text="Action Settings", icon='PROPERTIES')
|
||||
for p in ACTION_PROPS:
|
||||
box.prop(obj, f'["{p}"]', text=p.replace("_", " ").title())
|
||||
layout.separator()
|
||||
have_enter = False
|
||||
have_exit = False
|
||||
for c in obj.children:
|
||||
if c.name.startswith("position-") and "name" in c and c["name"] == "enter":
|
||||
have_enter = True
|
||||
break
|
||||
for c in obj.children:
|
||||
if c.name.startswith("position-") and "name" in c and c["name"] == "exit":
|
||||
have_exit = True
|
||||
break
|
||||
if not have_enter:
|
||||
op_enter = layout.operator("object.create_child_empties", icon='EMPTY_DATA', text="Add Enter Position")
|
||||
op_enter.target_val = "enter"
|
||||
else:
|
||||
op_enter = layout.operator("object.destroy_child_by_prop", text="Destroy 'Enter' Node", icon='X')
|
||||
op_enter.target_val = "enter"
|
||||
if not have_exit:
|
||||
op_exit = layout.operator("object.create_child_empties", icon='EMPTY_DATA', text="Add Exit Position")
|
||||
op_exit.target_val = "exit"
|
||||
else:
|
||||
op_exit = layout.operator("object.destroy_child_by_prop", text="Destroy 'Exit' Node", icon='X')
|
||||
op_exit.target_val = "exit"
|
||||
|
||||
|
||||
class OBJECT_PT_FurnitureEmptyPanel(bpy.types.Panel):
|
||||
"""Custom GUI panel for 'furniture-' prefixed Empties"""
|
||||
bl_label = "Furniture Controls"
|
||||
bl_idname = "OBJECT_PT_action_furniture_panel"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.object
|
||||
return (obj is not None and
|
||||
obj.type == 'EMPTY' and
|
||||
obj.name.startswith("furniture-"))
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = context.object
|
||||
|
||||
# Check if all required properties already exist
|
||||
props_exist = all(p in obj for p in FURNITURE_PROPS)
|
||||
|
||||
if not props_exist:
|
||||
# Show ONLY the button if properties are missing
|
||||
layout.operator("object.create_furniture_props", icon='PLUS')
|
||||
else:
|
||||
# Show the text fields if properties exist
|
||||
box = layout.box()
|
||||
box.label(text="Furnitore Settings", icon='PROPERTIES')
|
||||
for p in FURNITURE_PROPS:
|
||||
box.prop(obj, f'["{p}"]', text=p.replace("_", " ").title())
|
||||
op_use = layout.operator("object.create_child_action_node", icon='EMPTY_DATA', text="Add Use Action Node")
|
||||
op_use.target_val = "use"
|
||||
op_use = layout.operator("object.create_child_action_node", icon='EMPTY_DATA', text="Add Sit Action Node")
|
||||
op_use.target_val = "sit"
|
||||
|
||||
classes = (TEST_PT_CharacterPanel, TEST_OT_LinkAndPlay, TEST_OT_Cleanup)
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(OBJECT_OT_CreateActionProps)
|
||||
bpy.utils.register_class(OBJECT_OT_CreateFurnitureProps)
|
||||
bpy.utils.register_class(OBJECT_PT_ActionEmptyPanel)
|
||||
bpy.utils.register_class(OBJECT_OT_CreateChildEmpties)
|
||||
bpy.utils.register_class(OBJECT_OT_DestroyChildByProp)
|
||||
bpy.utils.register_class(OBJECT_PT_FurnitureEmptyPanel)
|
||||
bpy.utils.register_class(OBJECT_OT_CreateChildActionNode)
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.types.Scene.test_filepath = bpy.props.StringProperty(name="File Path", subtype='FILE_PATH', default="//char.blend")
|
||||
bpy.types.Scene.test_obj_name = bpy.props.StringProperty(name="Rig Name", default="Character_Rig")
|
||||
bpy.types.Scene.test_action_name = bpy.props.StringProperty(name="Action", default="sitting")
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
del bpy.types.Scene.test_filepath
|
||||
del bpy.types.Scene.test_obj_name
|
||||
del bpy.types.Scene.test_action_name
|
||||
bpy.utils.register_class(OBJECT_OT_CreateActionProps)
|
||||
bpy.utils.register_class(OBJECT_OT_CreateFurnitureProps)
|
||||
bpy.utils.unregister_class(OBJECT_PT_ActionEmptyPanel)
|
||||
bpy.utils.unregister_class(OBJECT_OT_CreateChildEmpties)
|
||||
bpy.utils.unregister_class(OBJECT_OT_DestroyChildByProp)
|
||||
bpy.utils.unregister_class(OBJECT_PT_FurnitureEmptyPanel)
|
||||
bpy.utils.unregister_class(OBJECT_OT_CreateChildActionNode)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
|
||||
Reference in New Issue
Block a user