From 0dc78b6585165ed942642ef6ceb0bd0f1c9815f6 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 27 Jun 2015 22:59:21 -0400 Subject: [PATCH] Implement a SittingBehavior modifier --- korman/helpers.py | 8 ++ korman/nodes/node_avatar.py | 6 +- korman/properties/modifiers/__init__.py | 1 + korman/properties/modifiers/avatar.py | 104 ++++++++++++++++++++++++ korman/ui/modifiers/__init__.py | 1 + korman/ui/modifiers/avatar.py | 39 +++++++++ 6 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 korman/properties/modifiers/avatar.py create mode 100644 korman/ui/modifiers/avatar.py diff --git a/korman/helpers.py b/korman/helpers.py index a36d138..faaffea 100644 --- a/korman/helpers.py +++ b/korman/helpers.py @@ -62,6 +62,14 @@ def ensure_object_can_bake(bo, toggle): def ensure_power_of_two(value): return pow(2, math.floor(math.log(value, 2))) +def find_modifier(boname, modid): + """Given a Blender Object name, finds a given modifier and returns it or None""" + bo = bpy.data.objects.get(boname, None) + if bo is not None: + # if they give us the wrong modid, it is a bug and an AttributeError + return getattr(bo.plasma_modifiers, modid) + return None + def make_active_selection(bo): """Selects a single Blender Object and makes it active""" for i in bpy.data.objects: diff --git a/korman/nodes/node_avatar.py b/korman/nodes/node_avatar.py index a04c45e..69a552b 100644 --- a/korman/nodes/node_avatar.py +++ b/korman/nodes/node_avatar.py @@ -18,6 +18,7 @@ from bpy.props import * from PyHSPlasma import * from .node_core import PlasmaNodeBase, PlasmaNodeSocketBase +from ..properties.modifiers.avatar import sitting_approach_flags class PlasmaSittingBehaviorNode(PlasmaNodeBase, bpy.types.Node): bl_category = "AVATAR" @@ -27,10 +28,7 @@ class PlasmaSittingBehaviorNode(PlasmaNodeBase, bpy.types.Node): approach = EnumProperty(name="Approach", description="Directions an avatar can approach the seat from", - items=[("kApproachFront", "Front", "Approach from the font"), - ("kApproachLeft", "Left", "Approach from the left"), - ("kApproachRight", "Right", "Approach from the right"), - ("kApproachRear", "Rear", "Approach from the rear guard")], + items=sitting_approach_flags, default={"kApproachFront", "kApproachLeft", "kApproachRight"}, options={"ENUM_FLAG"}) diff --git a/korman/properties/modifiers/__init__.py b/korman/properties/modifiers/__init__.py index 2a1394b..25897be 100644 --- a/korman/properties/modifiers/__init__.py +++ b/korman/properties/modifiers/__init__.py @@ -17,6 +17,7 @@ import bpy import inspect from .base import PlasmaModifierProperties +from .avatar import * from .logic import * from .physics import * from .region import * diff --git a/korman/properties/modifiers/avatar.py b/korman/properties/modifiers/avatar.py new file mode 100644 index 0000000..a35185d --- /dev/null +++ b/korman/properties/modifiers/avatar.py @@ -0,0 +1,104 @@ +# This file is part of Korman. +# +# Korman is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Korman is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Korman. If not, see . + +import bpy +from bpy.props import * +from PyHSPlasma import * + +from .base import PlasmaModifierProperties, PlasmaModifierLogicWiz +from ...exporter.explosions import ExportError +from ...helpers import find_modifier + +sitting_approach_flags = [("kApproachFront", "Front", "Approach from the font"), + ("kApproachLeft", "Left", "Approach from the left"), + ("kApproachRight", "Right", "Approach from the right"), + ("kApproachRear", "Rear", "Approach from the rear guard")] + +class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): + pl_id = "sittingmod" + + bl_category = "Avatar" + bl_label = "Sitting Behavior" + bl_description = "Avatar sitting position" + + approach = EnumProperty(name="Approach", + description="Directions an avatar can approach the seat from", + items=sitting_approach_flags, + default={"kApproachFront", "kApproachLeft", "kApproachRight"}, + options={"ENUM_FLAG"}) + + clickable_obj = StringProperty(name="Clickable", + description="Object that defines the clickable area") + region_obj = StringProperty(name="Region", + description="Object that defines the region mesh") + + facing_enabled = BoolProperty(name="Avatar Facing", + description="The avatar must be facing the clickable's Y-axis", + default=True) + facing_degrees = IntProperty(name="Tolerance", + description="How far away we will tolerate the avatar facing the clickable", + min=-180, max=180, default=45) + + def created(self, obj): + self.display_name = "{}_SitBeh".format(obj.name) + + def export(self, exporter, bo, so): + # The user absolutely MUST specify a clickable or this won't export worth crap. + clickable_obj = bpy.data.objects.get(self.clickable_obj, None) + if clickable_obj is None: + raise ExportError("'{}': Sitting Behavior's clickable object is invalid") + + # Generate the logic nodes now + self.logicwiz(bo) + + # Now, export the node tree + self.node_tree.export(exporter, bo, so) + + def logicwiz(self, bo): + tree = self.node_tree + nodes = tree.nodes + nodes.clear() + + # Sitting Modifier + sittingmod = nodes.new("PlasmaSittingBehaviorNode") + sittingmod.approach = self.approach + sittingmod.name = "SittingBeh" + + # Clickable + clickable = nodes.new("PlasmaClickableNode") + clickable.link_output(tree, sittingmod, "satisfies", "condition") + clickable.clickable = self.clickable_obj + clickable.bounds = find_modifier(self.clickable_obj, "collision").bounds + + # Avatar Region (optional) + region_phys = find_modifier(self.region_obj, "collision") + if region_phys is not None: + region = nodes.new("PlasmaClickableRegionNode") + region.link_output(tree, clickable, "satisfies", "region") + region.name = "ClickableAvRegion" + region.region = self.region_obj + region.bounds = region_phys.bounds + + # Facing Target (optional) + if self.facing_enabled: + facing = nodes.new("PlasmaFacingTargetNode") + facing.link_output(tree, clickable, "satisfies", "facing") + facing.name = "FacingClickable" + facing.directional = True + facing.tolerance = self.facing_degrees + else: + # this socket must be explicitly disabled, otherwise it automatically generates a default + # facing target conditional for us. isn't that nice? + clickable.find_input_socket("facing").allow_simple = False diff --git a/korman/ui/modifiers/__init__.py b/korman/ui/modifiers/__init__.py index 06220b9..7dcf614 100644 --- a/korman/ui/modifiers/__init__.py +++ b/korman/ui/modifiers/__init__.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU General Public License # along with Korman. If not, see . +from .avatar import * from .logic import * from .physics import * from .region import * diff --git a/korman/ui/modifiers/avatar.py b/korman/ui/modifiers/avatar.py new file mode 100644 index 0000000..5e8d010 --- /dev/null +++ b/korman/ui/modifiers/avatar.py @@ -0,0 +1,39 @@ +# This file is part of Korman. +# +# Korman is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Korman is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Korman. If not, see . + +import bpy + +from ...helpers import find_modifier + +def sittingmod(modifier, layout, context): + layout.row().prop(modifier, "approach") + + col = layout.column() + col.prop_search(modifier, "clickable_obj", bpy.data, "objects", icon="MESH_DATA") + clickable = find_modifier(modifier.clickable_obj, "collision") + if clickable is not None: + col.prop(clickable, "bounds") + + col = layout.column() + col.prop_search(modifier, "region_obj", bpy.data, "objects", icon="MESH_DATA") + region = find_modifier(modifier.region_obj, "collision") + if region is not None: + col.prop(region, "bounds") + + split = layout.split() + split.column().prop(modifier, "facing_enabled") + col = split.column() + col.enabled = modifier.facing_enabled + col.prop(modifier, "facing_degrees")