From 2b8c16da1b1d253f41dc3f658e5dad7121d2c50d Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 11 Jul 2015 23:28:06 -0400 Subject: [PATCH] Add an animation group modifier Now, an object can be a "master" animation object controlling many animated objects when you address it with the Animation Command logic node. This also includes a fix for a bug that would potentially break an animation if it were addressed by an Animation Command node. --- korman/nodes/node_messages.py | 11 +++++- korman/properties/modifiers/anim.py | 59 ++++++++++++++++++++++++++++- korman/ui/modifiers/anim.py | 22 +++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py index 96c6e2a..edff453 100644 --- a/korman/nodes/node_messages.py +++ b/korman/nodes/node_messages.py @@ -168,7 +168,16 @@ class PlasmaAnimCmdMsgNode(PlasmaMessageNode, bpy.types.Node): anim = obj.plasma_modifiers.animation if not anim.enabled: self.raise_error("invalid animation", tree) - target = exporter.mgr.find_create_key(plAGMasterMod, bl=obj, name=anim.display_name) + group = obj.plasma_modifiers.animation_group + if group.enabled: + # we might be controlling more than one animation. isn't that cute? + # https://www.youtube.com/watch?v=hspNaoxzNbs + # (but obviously this is not wrong...) + target = exporter.mgr.find_create_key(plMsgForwarder, bl=obj, name=group.display_name) + else: + # remember, the AGModifier MUST exist first... so just in case... + exporter.mgr.find_create_key(plAGModifier, bl=obj, name=anim.display_name) + target = exporter.mgr.find_create_key(plAGMasterMod, bl=obj, name=anim.display_name) else: material = bpy.data.materials.get(self.material_name, None) if material is None: diff --git a/korman/properties/modifiers/anim.py b/korman/properties/modifiers/anim.py index dfce8c8..478986c 100644 --- a/korman/properties/modifiers/anim.py +++ b/korman/properties/modifiers/anim.py @@ -122,7 +122,7 @@ class PlasmaAnimationModifier(PlasmaModifierProperties): channel.affine = affine # We need both an AGModifier and an AGMasterMod - # TODO: grouped animations (eg one door, two objects) + # NOTE: mandatory order--otherwise the animation will not work in game! agmod = exporter.mgr.find_create_object(plAGModifier, so=so, name=self.display_name) agmod.channelName = bo.name agmaster = exporter.mgr.find_create_object(plAGMasterMod, so=so, name=self.display_name) @@ -142,6 +142,63 @@ class PlasmaAnimationModifier(PlasmaModifierProperties): phys.mass = 1.0 +class AnimGroupObject(bpy.types.PropertyGroup): + object_name = StringProperty(name="Child", + description="Object whose action is a child animation") + + +class PlasmaAnimationGroupModifier(PlasmaModifierProperties): + pl_id = "animation_group" + pl_depends = {"animation"} + + bl_category = "Animation" + bl_label = "Group" + bl_description = "Defines related animations" + bl_icon = "GROUP" + + children = CollectionProperty(name="Child Animations", + description="Animations that will execute the same commands as this one", + type=AnimGroupObject) + active_child_index = IntProperty(options={"HIDDEN"}) + + def created(self, obj): + self.display_name = "{}_AnimGroup".format(obj.name) + + def export(self, exporter, bo, so): + action = _get_blender_action(bo) + key_name = bo.plasma_modifiers.animation.display_name + + # See above... AGModifier must always be inited first... + agmod = exporter.mgr.find_create_object(plAGModifier, so=so, name=key_name) + + # The message forwarder is the guy that makes sure that everybody knows WTF is going on + msgfwd = exporter.mgr.find_create_object(plMsgForwarder, so=so, name=self.display_name) + + # Now, this is da swhiz... + agmaster = exporter.mgr.find_create_object(plAGMasterMod, so=so, name=key_name) + agmaster.msgForwarder = msgfwd.key + agmaster.isGrouped, agmaster.isGroupMaster = True, True + for i in self.children: + child_bo = bpy.data.objects.get(i.object_name, None) + if child_bo is None: + msg = "Animation Group '{}' specifies an invalid object '{}'. Ignoring..." + exporter.report.warn(msg.format(self.display_name, i.object_name), ident=2) + continue + if child_bo.animation_data is None or child_bo.animation_data.action is None: + msg = "Animation Group '{}' specifies an object '{}' with no valid animation data. Ignoring..." + exporter.report.warn(msg.format(self.display_name, i.object_name), indent=2) + continue + child_animation = child_bo.plasma_modifiers.animation + if not child_animation.enabled: + msg = "Animation Group '{}' specifies an object '{}' with no Plasma Animation modifier. Ignoring..." + exporter.report.warn(msg.format(self.display_name, i.object_name), indent=2) + continue + child_agmod = exporter.mgr.find_create_key(plAGModifier, bl=child_bo, name=child_animation.display_name) + child_agmaster = exporter.mgr.find_create_key(plAGMasterMod, bl=child_bo, name=child_animation.display_name) + msgfwd.addForwardKey(child_agmaster) + msgfwd.addForwardKey(agmaster.key) + + class LoopMarker(bpy.types.PropertyGroup): loop_name = StringProperty(name="Loop Name", description="Name of this loop") diff --git a/korman/ui/modifiers/anim.py b/korman/ui/modifiers/anim.py index 198eed4..e9b2fd5 100644 --- a/korman/ui/modifiers/anim.py +++ b/korman/ui/modifiers/anim.py @@ -39,6 +39,28 @@ def animation(modifier, layout, context): col.prop_search(modifier, "loop_end", action, "pose_markers", icon="PMARKER") +class GroupListUI(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): + layout.prop_search(item, "object_name", bpy.data, "objects", icon="ACTION") + + +def animation_group(modifier, layout, context): + if not _check_for_anim(layout, context): + return + + row = layout.row() + row.template_list("GroupListUI", "children", modifier, "children", modifier, "active_child_index", + rows=3, maxrows=4) + col = row.column(align=True) + op = col.operator("object.plasma_modifier_collection_add", icon="ZOOMIN", text="") + op.modifier = modifier.pl_id + op.collection = "children" + op = col.operator("object.plasma_modifier_collection_remove", icon="ZOOMOUT", text="") + op.modifier = modifier.pl_id + op.collection = "children" + op.index = modifier.active_child_index + + class LoopListUI(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): layout.prop(item, "loop_name", emboss=False, text="", icon="PMARKER_ACT")