mirror of
https://github.com/H-uru/korman.git
synced 2025-07-14 22:36:52 +00:00
Responder nodes now export themselves!
Please note this does not address logic triggers or region sensors. Those will be added in a future commit.
This commit is contained in:
@ -111,7 +111,7 @@ class Exporter:
|
||||
|
||||
# Instead of exporting a skeleton now, we'll just make an orphaned CI.
|
||||
# The bl_obj export will make this work.
|
||||
parent_ci = self.mgr.find_create_key(parent, plCoordinateInterface).object
|
||||
parent_ci = self.mgr.find_create_key(plCoordinateInterface, bl=bo, so=so).object
|
||||
parent_ci.addChild(so.key)
|
||||
else:
|
||||
self.report.warn("You have parented Plasma Object '{}' to '{}', which has not been marked for export. \
|
||||
@ -122,7 +122,7 @@ class Exporter:
|
||||
"""Ensures that the SceneObject has a CoordinateInterface"""
|
||||
if not so.coord:
|
||||
print(" Exporting CoordinateInterface")
|
||||
ci = self.mgr.find_create_key(bo, plCoordinateInterface).object
|
||||
ci = self.mgr.find_create_key(plCoordinateInterface, bl=bo, so=so).object
|
||||
|
||||
# Now we have the "fun" work of filling in the CI
|
||||
ci.localToWorld = utils.matrix44(bo.matrix_basis)
|
||||
@ -148,7 +148,7 @@ class Exporter:
|
||||
# Create a sceneobject if one does not exist.
|
||||
# Before we call the export_fn, we need to determine if this object is an actor of any
|
||||
# sort, and barf out a CI.
|
||||
sceneobject = self.mgr.find_create_key(bl_obj, plSceneObject).object
|
||||
sceneobject = self.mgr.find_create_key(plSceneObject, bl=bl_obj).object
|
||||
self._export_actor(sceneobject, bl_obj)
|
||||
export_fn(sceneobject, bl_obj)
|
||||
|
||||
|
@ -91,7 +91,7 @@ class ExportManager:
|
||||
|
||||
if isinstance(pl, plObjInterface):
|
||||
if so is None:
|
||||
key = self.find_key(bl, plSceneObject)
|
||||
key = self.find_key(plSceneObject, bl)
|
||||
# prevent race conditions
|
||||
if key is None:
|
||||
so = self.add_object(plSceneObject, name=name, loc=location)
|
||||
@ -158,22 +158,26 @@ class ExportManager:
|
||||
self._nodes[location] = None
|
||||
return location
|
||||
|
||||
def find_create_key(self, bl_obj, pClass, so=None):
|
||||
key = self.find_key(bl_obj, pClass, so=so)
|
||||
def find_create_key(self, pClass, bl=None, name=None, so=None):
|
||||
key = self.find_key(pClass, bl, name, so)
|
||||
if key is None:
|
||||
key = self.add_object(pl=pClass, bl=bl_obj, so=so).key
|
||||
key = self.add_object(pl=pClass, name=name, bl=bl, so=so).key
|
||||
return key
|
||||
|
||||
def find_key(self, bl_obj, pClass, so=None):
|
||||
def find_key(self, pClass, bl=None, name=None, so=None):
|
||||
"""Given a blender Object and a Plasma class, find (or create) an exported plKey"""
|
||||
assert (bl or name) and (bl or so)
|
||||
|
||||
if so is None:
|
||||
location = self._pages[bl_obj.plasma_object.page]
|
||||
location = self._pages[bl.plasma_object.page]
|
||||
else:
|
||||
location = so.key.location
|
||||
if name is None:
|
||||
name = bl.name
|
||||
|
||||
index = plFactory.ClassIndex(pClass.__name__)
|
||||
for key in self.mgr.getKeys(location, index):
|
||||
if bl_obj.name == key.name:
|
||||
if name == key.name:
|
||||
return key
|
||||
return None
|
||||
|
||||
|
@ -92,7 +92,7 @@ class LightConverter:
|
||||
def _create_light_key(self, bo, bl_light, so):
|
||||
try:
|
||||
xlate = _BL2PL[bl_light.type]
|
||||
return self.mgr.find_create_key(bo, xlate, so=so)
|
||||
return self.mgr.find_create_key(xlate, bl=bo, so=so)
|
||||
except LookupError:
|
||||
raise BlenderOptionNotSupported("Object ('{}') lamp type '{}'".format(bo.name, bl_light.type))
|
||||
|
||||
|
@ -13,9 +13,19 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import abc
|
||||
import bpy
|
||||
|
||||
class PlasmaNodeBase:
|
||||
def create_key_name(self, tree):
|
||||
return "{}_{}".format(tree.name, self.name)
|
||||
|
||||
def get_key(self, exporter, tree, so):
|
||||
return None
|
||||
|
||||
def export(self, exporter, tree, bo, so):
|
||||
pass
|
||||
|
||||
def find_input(self, key, idname=None):
|
||||
for i in self.inputs:
|
||||
if i.identifier == key:
|
||||
@ -34,6 +44,18 @@ class PlasmaNodeBase:
|
||||
return i
|
||||
raise KeyError(key)
|
||||
|
||||
def find_output(self, key, idname=None):
|
||||
for i in self.outputs:
|
||||
if i.identifier == key:
|
||||
if i.links:
|
||||
node = i.links[0].to_node
|
||||
if idname is not None and idname != node.bl_idname:
|
||||
return None
|
||||
return node
|
||||
else:
|
||||
return None
|
||||
raise KeyError(key)
|
||||
|
||||
def find_outputs(self, key, idname=None):
|
||||
for i in self.outputs:
|
||||
if i.identifier == key:
|
||||
@ -107,6 +129,11 @@ class PlasmaNodeTree(bpy.types.NodeTree):
|
||||
bl_label = "Plasma"
|
||||
bl_icon = "NODETREE"
|
||||
|
||||
def export(self, exporter, bo, so):
|
||||
# just pass it off to each node
|
||||
for node in self.nodes:
|
||||
node.export(exporter, self, bo, so)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.scene.render.engine == "PLASMA_GAME")
|
||||
|
@ -1,31 +0,0 @@
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from .node_core import *
|
||||
|
||||
class PlasmaLogicTriggerNode(PlasmaNodeBase, bpy.types.Node):
|
||||
bl_category = "LOGIC"
|
||||
bl_idname = "PlasmaLogicTriggerNode"
|
||||
bl_label = "Logic Trigger"
|
||||
|
||||
def init(self, context):
|
||||
self.inputs.new("PlasmaConditionSocket", "Condition", "condition")
|
||||
self.outputs.new("PlasmaRespTriggerSocket", "Trigger", "trigger")
|
||||
|
||||
|
||||
class PlasmaRespTriggerSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket):
|
||||
bl_color = (0.384, 0.239, 0.239, 1.0)
|
@ -15,6 +15,7 @@
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from PyHSPlasma import *
|
||||
|
||||
from .node_core import *
|
||||
from ..properties.modifiers.region import footstep_surfaces, footstep_surface_ids
|
||||
@ -38,3 +39,8 @@ class PlasmaFootstepSoundMsgNode(PlasmaNodeBase, bpy.types.Node):
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, "surface")
|
||||
|
||||
def convert_message(self, exporter):
|
||||
msg = plArmatureEffectStateMsg()
|
||||
msg.surface = footstep_surface_ids[self.surface]
|
||||
return msg
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from PyHSPlasma import *
|
||||
import uuid
|
||||
|
||||
from .node_core import *
|
||||
@ -28,6 +29,40 @@ class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node):
|
||||
self.inputs.new("PlasmaRespTriggerSocket", "Trigger", "whodoneit")
|
||||
self.outputs.new("PlasmaRespStateSocket", "States", "states")
|
||||
|
||||
def get_key(self, exporter, tree, so):
|
||||
return exporter.mgr.find_create_key(plResponderModifier, name=self.create_key_name(tree), so=so)
|
||||
|
||||
def export(self, exporter, tree, bo, so):
|
||||
responder = self.get_key(exporter, tree, so).object
|
||||
if not bo.plasma_net.manual_sdl:
|
||||
responder.setExclude("Responder")
|
||||
|
||||
class ResponderStateMgr:
|
||||
def __init__(self, respNode, respMod):
|
||||
self.states = []
|
||||
self.parent = respNode
|
||||
self.responder = respMod
|
||||
|
||||
def get_state(self, node):
|
||||
for idx, (theNode, theState) in enumerate(self.states):
|
||||
if theNode == node:
|
||||
return (idx, theState)
|
||||
state = plResponderModifier_State()
|
||||
self.states.append((node, state))
|
||||
return (len(self.states) - 1, state)
|
||||
|
||||
def save(self):
|
||||
resp = self.responder
|
||||
resp.clearStates()
|
||||
for node, state in self.states:
|
||||
resp.addState(state)
|
||||
|
||||
# Convert the Responder states
|
||||
stateMgr = ResponderStateMgr(self, responder)
|
||||
for stateNode in self.find_outputs("states", "PlasmaResponderStateNode"):
|
||||
stateNode.convert_state(exporter, stateMgr)
|
||||
stateMgr.save()
|
||||
|
||||
|
||||
class PlasmaResponderStateNode(PlasmaNodeVariableInput, bpy.types.Node):
|
||||
bl_category = "LOGIC"
|
||||
@ -50,6 +85,59 @@ class PlasmaResponderStateNode(PlasmaNodeVariableInput, bpy.types.Node):
|
||||
# Now draw a prop
|
||||
layout.prop(self, "default_state")
|
||||
|
||||
def convert_state(self, exporter, stateMgr):
|
||||
idx, state = stateMgr.get_state(self)
|
||||
|
||||
# No sanity checking here. Hopefully nothing crazy has happened in the UI.
|
||||
if self.default_state:
|
||||
stateMgr.responder.curState = idx
|
||||
|
||||
# Where do we go from heah?
|
||||
toStateNode = self.find_output("gotostate", "PlasmaResponderStateNode")
|
||||
if toStateNode is None:
|
||||
state.switchToState = idx
|
||||
else:
|
||||
toIdx, toState = stateMgr.get_state(toStateNode)
|
||||
state.switchToState = toIdx
|
||||
|
||||
class CommandMgr:
|
||||
def __init__(self):
|
||||
self.commands = []
|
||||
self.waits = {}
|
||||
|
||||
def add_command(self, node):
|
||||
cmd = type("ResponderCommand", (), {"msg": None, "waitOn": -1})
|
||||
self.commands.append((node, cmd))
|
||||
return (len(self.commands) - 1, cmd)
|
||||
|
||||
def add_wait(self, parentCmd):
|
||||
try:
|
||||
idx = self.commands.index(parentCmd)
|
||||
except ValueError:
|
||||
# The parent command didn't export for some reason... Probably no message.
|
||||
# So, wait on nothing!
|
||||
return -1
|
||||
else:
|
||||
wait = len(self.waits)
|
||||
self.waits[wait] = idx
|
||||
return idx
|
||||
|
||||
def save(self, state):
|
||||
for node, cmd in self.commands:
|
||||
# Amusing, PyHSPlasma doesn't actually want a plResponderModifier_Cmd
|
||||
# Meh, I'll let this one slide.
|
||||
state.addCommand(cmd.msg, cmd.waitOn)
|
||||
state.numCallbacks = len(self.waits)
|
||||
state.waitToCmd = self.waits
|
||||
|
||||
# Convert the commands
|
||||
commands = CommandMgr()
|
||||
for i in self.find_outputs("cmds", "PlasmaResponderCommandNode"):
|
||||
# slight optimization--commands attached to states can't wait on other commands
|
||||
# namely because it's impossible to wait on a command that doesn't exist...
|
||||
i.convert_command(exporter, stateMgr.responder, commands, True)
|
||||
commands.save(state)
|
||||
|
||||
|
||||
class PlasmaRespStateSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket):
|
||||
bl_color = (0.388, 0.78, 0.388, 1.0)
|
||||
@ -65,6 +153,55 @@ class PlasmaResponderCommandNode(PlasmaNodeBase, bpy.types.Node):
|
||||
self.outputs.new("PlasmaMessageSocket", "Message", "msg")
|
||||
self.outputs.new("PlasmaRespCommandSocket", "Trigger", "trigger")
|
||||
|
||||
def convert_command(self, exporter, responder, commandMgr, forceNoWait=False):
|
||||
# If this command has no message, there is no need to export it...
|
||||
msgNode = self.find_output("msg")
|
||||
if msgNode is not None:
|
||||
idx, command = commandMgr.add_command(self)
|
||||
|
||||
# If the thingthatdoneit is another command, we need to register a wait.
|
||||
# We could hack and assume the parent is idx-1, but that won't work if the parent has many
|
||||
# child commands. Le whoops!
|
||||
if not forceNoWait:
|
||||
parentCmd = self.find_input("whodoneit", "PlasmaResponderCommandNode")
|
||||
if parentCmd is not None:
|
||||
command.waitOn = commandMgr.add_wait(parentCmd)
|
||||
|
||||
# Finally, convert our message...
|
||||
msg = msgNode.convert_message(exporter)
|
||||
self._finalize_message(exporter, responder, msg)
|
||||
|
||||
# If we have child commands, we need to make sure that we support chaining this message as a callback
|
||||
# If not, we'll export our children and tell them to not actually wait on us.
|
||||
haveChildren = self.find_output("trigger", "PlasmaResponderCommandNode") is not None
|
||||
if haveChildren:
|
||||
nowait = not self._add_msg_callback(exporter, responder, msg)
|
||||
command.msg = msg
|
||||
else:
|
||||
nowait = True
|
||||
|
||||
# Export any child commands
|
||||
for i in self.find_outputs("trigger", "PlasmaResponderCommandNode"):
|
||||
i.convert_command(exporter, responder, commandMgr, nowait)
|
||||
|
||||
_bcast_flags = {
|
||||
plArmatureEffectStateMsg: (plMessage.kPropagateToModifiers | plMessage.kNetPropagate),
|
||||
}
|
||||
|
||||
def _finalize_message(self, exporter, responder, msg):
|
||||
msg.sender = responder.key
|
||||
|
||||
# BCast Flags are pretty common...
|
||||
_cls = msg.__class__
|
||||
if _cls in self._bcast_flags:
|
||||
msg.BCastFlags = self._bcast_flags[_cls]
|
||||
msg.BCastFlags |= plMessage.kLocalPropagate
|
||||
|
||||
def _add_msg_callback(self, exporter, responder, msg):
|
||||
"""Prepares a given message to be a callback to the responder"""
|
||||
# We do not support callback messages ATM
|
||||
return False
|
||||
|
||||
|
||||
class PlasmaRespCommandSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket):
|
||||
bl_color = (0.451, 0.0, 0.263, 1.0)
|
||||
|
@ -74,6 +74,13 @@ class PlasmaFootstepRegion(PlasmaModifierProperties, PlasmaModifierLogicWiz):
|
||||
def created(self, obj):
|
||||
self.display_name = "{}_FootRgn".format(obj.name)
|
||||
|
||||
def export(self, exporter, bo, so):
|
||||
# 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
|
||||
|
Reference in New Issue
Block a user