Action nodes work better now

This commit is contained in:
2026-02-01 00:13:33 +03:00
parent 201aa92fe7
commit e6efd89bb0
14 changed files with 548 additions and 126 deletions

View File

@@ -3,6 +3,90 @@ import bpy
ACTION_PROPS = ["name", "action", "action_text", "height", "radius", "tags"]
FURNITURE_PROPS = ["name", "furniture_tags"]
test_tracking = {"lib_name": None}
class TEST_OT_LinkAndPlay(bpy.types.Operator):
bl_idname = "test.link_and_play"
bl_label = "Link and Play Sitting"
filepath: bpy.props.StringProperty()
rig_name: bpy.props.StringProperty()
action_name: bpy.props.StringProperty()
def execute(self, context):
target = context.active_object
path = bpy.path.abspath(self.filepath)
mesh_name = "Body"
with bpy.data.libraries.load(path, link=True) as (data_from, data_to):
# We request all three components
data_to.objects = [n for n in [self.rig_name, mesh_name] if n in data_from.objects]
data_to.actions = [self.action_name] if self.action_name in data_from.actions else []
# 2. Add to scene and create Override
rig_obj = None
for obj in data_to.objects:
bpy.context.collection.objects.link(obj)
obj.matrix_world = target.matrix_world
if obj.type == 'ARMATURE':
rig_obj = obj
if obj.library:
test_tracking["lib_name"] = obj.library.name
if rig_obj:
if not rig_obj.animation_data:
rig_obj.animation_data_create()
if data_to.actions:
rig_obj.animation_data.action = data_to.actions[0]
bpy.ops.screen.animation_play()
return {'FINISHED'}
class TEST_OT_Cleanup(bpy.types.Operator):
bl_idname = "test.cleanup"
bl_label = "Stop & Cleanup"
def execute(self, context):
if context.screen.is_animation_playing:
bpy.ops.screen.animation_cancel(restore_frame=True)
lib_name = test_tracking.get("lib_name")
if lib_name and lib_name in bpy.data.libraries:
lib = bpy.data.libraries[lib_name]
bpy.data.batch_remove(ids=(lib,))
test_tracking["lib_name"] = None
# Deep purge orphans (meshes, armatures, etc)
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
return {'FINISHED'}
class TEST_PT_ObjectPanel(bpy.types.Panel):
"""Only appears in Object Properties when a 'position-' Empty is selected"""
bl_label = "Character Placement Test"
bl_idname = "TEST_PT_object_panel"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == 'EMPTY' and obj.name.startswith("position-")
def draw(self, context):
layout = self.layout
layout.label(text="Test Controls:", icon='OUTLINER_OB_EMPTY')
# Link button with preset arguments
row = layout.row(align=True)
op = row.operator("test.link_and_play", text="Link & Sit", icon='PLAY')
op.filepath = "//../../edited-normal-male.blend" # SET YOUR FILEPATH
op.rig_name = "male" # SET YOUR RIG NAME
op.action_name = "sitting-chair" # SET YOUR ACTION NAME
# Cleanup button
layout.operator("test.cleanup", text="Stop & Clear", icon='TRASH')
class OBJECT_OT_CreateActionProps(bpy.types.Operator):
"""Initializes custom properties on the selected action- empty"""
bl_idname = "object.create_action_props"
@@ -246,8 +330,6 @@ class OBJECT_PT_FurnitureEmptyPanel(bpy.types.Panel):
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)
@@ -256,19 +338,12 @@ def register():
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")
bpy.utils.register_class(TEST_OT_LinkAndPlay)
bpy.utils.register_class(TEST_OT_Cleanup)
bpy.utils.register_class(TEST_PT_ObjectPanel)
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)
@@ -276,6 +351,9 @@ def unregister():
bpy.utils.unregister_class(OBJECT_OT_DestroyChildByProp)
bpy.utils.unregister_class(OBJECT_PT_FurnitureEmptyPanel)
bpy.utils.unregister_class(OBJECT_OT_CreateChildActionNode)
bpy.utils.unregister_class(TEST_OT_LinkAndPlay)
bpy.utils.unregister_class(TEST_OT_Cleanup)
bpy.utils.unregister_class(TEST_PT_ObjectPanel)
if __name__ == "__main__":
register()

View File

@@ -84,6 +84,20 @@ def export_root_objects_to_gltf(output_dir):
action["name"] = child["name"]
else:
action["name"] = desc["name"] + "_" + str(len(desc["actions"]))
if "tags" in child:
atags = child["tags"]
if "," in atags:
atags = [m.strip() for m in atags.split(",")]
action["tags"] = atags
else:
action["tags"] = [atags]
else:
action["tags"] = []
for k, v in child.items():
if k.startswith("goap_prereq_"):
action[k] = v
elif k.startswith("goap_effect_"):
action[k] = v
action["furniture"] = {}
action["furniture"]["name"] = desc["name"]
action["furniture"]["tags"] = desc["tags"]

Binary file not shown.