Browse Source

Make node trees reusable.

Previously, reusing node trees required careful thought and
specification of specific nodes in the advanced logic modifier. This
tedious requirement has been removed in favor of ensuring that the nodes
themselves take into account whether or not they should generate and
attach plasma objects.
pull/156/head
Adam Johnson 5 years ago
parent
commit
9f450d0bdb
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 14
      korman/exporter/convert.py
  2. 4
      korman/nodes/node_avatar.py
  3. 54
      korman/nodes/node_conditions.py
  4. 48
      korman/nodes/node_core.py
  5. 6
      korman/nodes/node_logic.py
  6. 7
      korman/nodes/node_messages.py
  7. 7
      korman/nodes/node_python.py
  8. 7
      korman/nodes/node_responder.py
  9. 14
      korman/nodes/node_softvolume.py
  10. 19
      korman/properties/modifiers/logic.py
  11. 5
      korman/properties/modifiers/region.py
  12. 15
      korman/ui/modifiers/logic.py

14
korman/exporter/convert.py

@ -38,8 +38,8 @@ class Exporter:
self._op = op # Blender export operator self._op = op # Blender export operator
self._objects = [] self._objects = []
self.actors = set() self.actors = set()
self.node_trees_exported = set()
self.want_node_trees = {} self.want_node_trees = {}
self.exported_nodes = {}
def run(self): def run(self):
log = logger.ExportVerboseLogger if self._op.verbose else logger.ExportProgressLogger log = logger.ExportVerboseLogger if self._op.verbose else logger.ExportProgressLogger
@ -276,13 +276,11 @@ class Exporter:
inc_progress = self.report.progress_increment inc_progress = self.report.progress_increment
self.report.msg("\nChecking Logic Trees...") self.report.msg("\nChecking Logic Trees...")
need_to_export = [(name, bo, so) for name, (bo, so) in self.want_node_trees.items() for tree_name, references in self.want_node_trees.items():
if name not in self.node_trees_exported] self.report.msg("NodeTree '{}'", tree_name, indent=1)
self.report.progress_value = len(self.want_node_trees) - len(need_to_export) tree = bpy.data.node_groups[tree_name]
for bo, so in references:
for tree, bo, so in need_to_export: tree.export(self, bo, so)
self.report.msg("NodeTree '{}'", tree, indent=1)
bpy.data.node_groups[tree].export(self, bo, so)
inc_progress() inc_progress()
def _harvest_actors(self): def _harvest_actors(self):

4
korman/nodes/node_avatar.py

@ -56,10 +56,10 @@ class PlasmaSittingBehaviorNode(PlasmaNodeBase, bpy.types.Node):
layout.prop_menu_enum(self, "approach") layout.prop_menu_enum(self, "approach")
def get_key(self, exporter, so): def get_key(self, exporter, so):
return exporter.mgr.find_create_key(plSittingModifier, name=self.key_name, so=so) return self._find_create_key(plSittingModifier, exporter, so=so)
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
sitmod = self.get_key(exporter, so).object sitmod = self._find_create_object(plSittingModifier, exporter, so=so)
for flag in self.approach: for flag in self.approach:
sitmod.miscFlags |= getattr(plSittingModifier, flag) sitmod.miscFlags |= getattr(plSittingModifier, flag)
for i in self.find_outputs("satisfies"): for i in self.find_outputs("satisfies"):

54
korman/nodes/node_conditions.py

@ -74,9 +74,8 @@ class PlasmaClickableNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.N
if clickable_bo is None: if clickable_bo is None:
clickable_bo = parent_bo clickable_bo = parent_bo
name = self.key_name interface = self._find_create_object(plInterfaceInfoModifier, exporter, bl=clickable_bo, so=clickable_so)
interface = exporter.mgr.find_create_key(plInterfaceInfoModifier, name=name, so=clickable_so).object logicmod = self._find_create_key(plLogicModifier, exporter, bl=clickable_bo, so=clickable_so)
logicmod = exporter.mgr.find_create_key(plLogicModifier, name=name, so=clickable_so)
interface.addIntfKey(logicmod) interface.addIntfKey(logicmod)
# Matches data seen in Cyan's PRPs... # Matches data seen in Cyan's PRPs...
interface.addIntfKey(logicmod) interface.addIntfKey(logicmod)
@ -105,11 +104,11 @@ class PlasmaClickableNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.N
physical.LOSDBs |= plSimDefs.kLOSDBUIItems physical.LOSDBs |= plSimDefs.kLOSDBUIItems
# Picking Detector -- detect when the physical is clicked # Picking Detector -- detect when the physical is clicked
detector = exporter.mgr.find_create_key(plPickingDetector, name=name, so=clickable_so).object detector = self._find_create_object(plPickingDetector, exporter, bl=clickable_bo, so=clickable_so)
detector.addReceiver(logicmod.key) detector.addReceiver(logicmod.key)
# Clickable # Clickable
activator = exporter.mgr.find_create_key(plActivatorConditionalObject, name=name, so=clickable_so).object activator = self._find_create_object(plActivatorConditionalObject, exporter, bl=clickable_bo, so=clickable_so)
activator.addActivator(detector.key) activator.addActivator(detector.key)
logicmod.addCondition(activator.key) logicmod.addCondition(activator.key)
logicmod.setLogicFlag(plLogicModifier.kLocalElement, True) logicmod.setLogicFlag(plLogicModifier.kLocalElement, True)
@ -125,10 +124,14 @@ class PlasmaClickableNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.N
face_target = self.find_input_socket("facing") face_target = self.find_input_socket("facing")
face_target.convert_subcondition(exporter, clickable_bo, clickable_so, logicmod) face_target.convert_subcondition(exporter, clickable_bo, clickable_so, logicmod)
@property
def export_once(self):
return self.clickable_object is not None
def get_key(self, exporter, parent_so): def get_key(self, exporter, parent_so):
# careful... we really make lots of keys... # careful... we really make lots of keys...
clickable_bo, clickable_so = self._get_objects(exporter, parent_so) clickable_bo, clickable_so = self._get_objects(exporter, parent_so)
key = exporter.mgr.find_create_key(plLogicModifier, name=self.key_name, so=clickable_so) key = self._find_create_key(plLogicModifier, exporter, bl=clickable_bo, so=clickable_so)
return key return key
def _get_objects(self, exporter, parent_so): def _get_objects(self, exporter, parent_so):
@ -185,7 +188,7 @@ class PlasmaClickableRegionNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.t
region_bo = self.region_object region_bo = self.region_object
if region_bo is None: if region_bo is None:
self.raise_error("invalid Region") self.raise_error("invalid Region")
region_so = exporter.mgr.find_create_key(plSceneObject, bl=region_bo).object region_so = exporter.mgr.find_create_object(plSceneObject, bl=region_bo)
# Try to figure out the appropriate bounds type for the region.... # Try to figure out the appropriate bounds type for the region....
phys_mod = region_bo.plasma_modifiers.collision phys_mod = region_bo.plasma_modifiers.collision
@ -202,15 +205,13 @@ class PlasmaClickableRegionNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.t
# one detector for many unrelated logic mods. However, LogicMods and Conditions appear to # one detector for many unrelated logic mods. However, LogicMods and Conditions appear to
# assume they pwn each other... so we need a unique detector. This detector must be attached # assume they pwn each other... so we need a unique detector. This detector must be attached
# as a modifier to the region's SO however. # as a modifier to the region's SO however.
name = self.key_name detector = self._find_create_object(plObjectInVolumeDetector, exporter, bl=region_bo, so=region_so)
detector_key = exporter.mgr.find_create_key(plObjectInVolumeDetector, name=name, so=region_so)
detector = detector_key.object
detector.addReceiver(logicmod.key) detector.addReceiver(logicmod.key)
detector.type = plObjectInVolumeDetector.kTypeAny detector.type = plObjectInVolumeDetector.kTypeAny
# Now, the conditional object. At this point, these seem very silly. At least it's not a plModifier. # Now, the conditional object. At this point, these seem very silly. At least it's not a plModifier.
# All they really do is hold a satisfied boolean... # All they really do is hold a satisfied boolean...
objinbox_key = exporter.mgr.find_create_key(plObjectInBoxConditionalObject, name=name, so=parent_so) objinbox_key = self._find_create_key(plObjectInBoxConditionalObject, exporter, bl=region_bo, so=parent_so)
objinbox_key.object.satisfied = True objinbox_key.object.satisfied = True
logicmod.addCondition(objinbox_key) logicmod.addCondition(objinbox_key)
@ -270,19 +271,18 @@ class PlasmaFacingTargetSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket):
# First, gather the schtuff from the appropriate blah blah blah # First, gather the schtuff from the appropriate blah blah blah
if self.simple_mode: if self.simple_mode:
node = self.node
directional = True directional = True
tolerance = 45 tolerance = 45
name = "{}_SimpleFacing".format(self.node.key_name)
elif self.is_linked: elif self.is_linked:
node = self.links[0].from_node node = self.links[0].from_node
directional = node.directional directional = node.directional
tolerance = node.tolerance tolerance = node.tolerance
name = node.key_name
else: else:
# This is a programmer failure, so we need a traceback. # This is a programmer failure, so we need a traceback.
raise RuntimeError("Tried to export an unused PlasmaFacingTargetSocket") raise RuntimeError("Tried to export an unused PlasmaFacingTargetSocket")
facing_key = exporter.mgr.find_create_key(plFacingConditionalObject, name=name, so=so) facing_key = node._find_create_key(plFacingConditionalObject, exporter, bl=bo, so=so)
facing = facing_key.object facing = facing_key.object
facing.directional = directional facing.directional = directional
facing.satisfied = True facing.satisfied = True
@ -395,13 +395,12 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type
self.raise_error("Region cannot be empty") self.raise_error("Region cannot be empty")
so = exporter.mgr.find_create_object(plSceneObject, bl=bo) so = exporter.mgr.find_create_object(plSceneObject, bl=bo)
rgn_enter, rgn_exit = None, None rgn_enter, rgn_exit = None, None
parent_key = parent_so.key
if self.report_enters: if self.report_enters:
theName = "{}_{}_Enter".format(self.id_data.name, self.name) rgn_enter = self._find_create_key(plLogicModifier, exporter, suffix="Enter", bl=bo, so=so)
rgn_enter = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so)
if self.report_exits: if self.report_exits:
theName = "{}_{}_Exit".format(self.id_data.name, self.name) rgn_exit = self._find_create_key(plLogicModifier, exporter, suffix="Exit", bl=bo, so=so)
rgn_exit = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so)
if rgn_enter is None: if rgn_enter is None:
return rgn_exit return rgn_exit
@ -415,12 +414,12 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type
return (rgn_enter, rgn_exit) return (rgn_enter, rgn_exit)
def export(self, exporter, bo, parent_so): def export(self, exporter, bo, parent_so):
# We need to ensure we export to the correct SO
region_bo = self.region_object region_bo = self.region_object
if region_bo is None: if region_bo is None:
self.raise_error("Region cannot be empty") self.raise_error("Region cannot be empty")
region_so = exporter.mgr.find_create_object(plSceneObject, bl=region_bo) region_so = exporter.mgr.find_create_object(plSceneObject, bl=region_bo)
interface = exporter.mgr.find_create_object(plInterfaceInfoModifier, name=self.key_name, so=region_so) interface = self._find_create_object(plInterfaceInfoModifier, exporter, bl=region_bo, so=region_so)
# Region Enters # Region Enters
enter_simple = self.find_input_socket("enter").allow enter_simple = self.find_input_socket("enter").allow
@ -452,20 +451,15 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type
else: else:
suffix = "Exit" suffix = "Exit"
theName = "{}_{}_{}".format(self.id_data.name, self.name, suffix) logicKey = self._find_create_key(plLogicModifier, exporter, suffix=suffix, bl=bo, so=so)
exporter.report.msg("[LogicModifier '{}']", theName, indent=2)
logicKey = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so)
logicmod = logicKey.object logicmod = logicKey.object
logicmod.setLogicFlag(plLogicModifier.kMultiTrigger, True) logicmod.setLogicFlag(plLogicModifier.kMultiTrigger, True)
logicmod.notify = self.generate_notify_msg(exporter, so, "satisfies") logicmod.notify = self.generate_notify_msg(exporter, so, "satisfies")
# Now, the detector objects # Now, the detector objects
exporter.report.msg("[ObjectInVolumeDetector '{}']", theName, indent=2) det = self._find_create_object(plObjectInVolumeDetector, exporter, suffix=suffix, bl=bo, so=so)
detKey = exporter.mgr.find_create_key(plObjectInVolumeDetector, name=theName, so=so)
det = detKey.object
exporter.report.msg("[VolumeSensorConditionalObject '{}']", theName, indent=2) volKey = self._find_create_key(plVolumeSensorConditionalObject, exporter, suffix=suffix, bl=bo, so=so)
volKey = exporter.mgr.find_create_key(plVolumeSensorConditionalObject, name=theName, so=so)
volsens = volKey.object volsens = volKey.object
volsens.type = event volsens.type = event
@ -483,6 +477,10 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type
logicmod.addCondition(volKey) logicmod.addCondition(volKey)
return logicKey return logicKey
@property
def export_once(self):
return True
@classmethod @classmethod
def _idprop_mapping(cls): def _idprop_mapping(cls):
return {"region_object": "region"} return {"region_object": "region"}

48
korman/nodes/node_core.py

@ -15,7 +15,7 @@
import abc import abc
import bpy import bpy
from PyHSPlasma import plMessage, plNotifyMsg from PyHSPlasma import *
from ..exporter import ExportError from ..exporter import ExportError
@ -37,6 +37,20 @@ class PlasmaNodeBase:
def get_key(self, exporter, so): def get_key(self, exporter, so):
return None return None
def get_key_name(self, single, suffix=None, bl=None, so=None):
assert bl or so
if single:
name = bl.name if bl is not None else so.key.name
if suffix:
return "{}_{}_{}_{}".format(name, self.id_data.name, self.name, suffix)
else:
return "{}_{}_{}".format(name, self.id_data.name, self.name)
else:
if suffix:
return "{}_{}_{}".format(self.id_data.name, self.name, suffix)
else:
return "{}_{}".format(self.id_data.name, self.name)
def draw_label(self): def draw_label(self):
if hasattr(self, "pl_label_attr") and self.hide: if hasattr(self, "pl_label_attr") and self.hide:
return str(getattr(self, self.pl_label_attrib, self.bl_label)) return str(getattr(self, self.pl_label_attrib, self.bl_label))
@ -45,6 +59,27 @@ class PlasmaNodeBase:
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
pass pass
@property
def export_once(self):
"""This node can only be exported once because it is a targeted plSingleModifier"""
return False
def _find_create_object(self, pClass, exporter, **kwargs):
"""Finds or creates an hsKeyedObject specific to this node."""
assert "name" not in kwargs
kwargs["name"] = self.get_key_name(issubclass(pClass, (plObjInterface, plSingleModifier)),
kwargs.pop("suffix", ""), kwargs.get("bl"),
kwargs.get("so"))
return exporter.mgr.find_create_object(pClass, **kwargs)
def _find_create_key(self, pClass, exporter, **kwargs):
"""Finds or creates a plKey specific to this node."""
assert "name" not in kwargs
kwargs["name"] = self.get_key_name(issubclass(pClass, (plObjInterface, plSingleModifier)),
kwargs.pop("suffix", ""), kwargs.get("bl"),
kwargs.get("so"))
return exporter.mgr.find_create_key(pClass, **kwargs)
def find_input(self, key, idname=None): def find_input(self, key, idname=None):
for i in self.inputs: for i in self.inputs:
if i.alias == key: if i.alias == key:
@ -188,10 +223,6 @@ class PlasmaNodeBase:
def harvest_actors(self, bo): def harvest_actors(self, bo):
return set() return set()
@property
def key_name(self):
return "{}_{}".format(self.id_data.name, self.name)
def link_input(self, node, out_key, in_key): def link_input(self, node, out_key, in_key):
"""Links a given Node's output socket to a given input socket on this Node""" """Links a given Node's output socket to a given input socket on this Node"""
if isinstance(in_key, str): if isinstance(in_key, str):
@ -221,6 +252,9 @@ class PlasmaNodeBase:
"""Returns an absolute path to this Node. Needed because repr() uses an elipsis...""" """Returns an absolute path to this Node. Needed because repr() uses an elipsis..."""
return "{}.{}".format(repr(self.id_data), self.path_from_id()) return "{}.{}".format(repr(self.id_data), self.path_from_id())
def previously_exported(self, exporter):
return self.name in exporter.exported_nodes[self.id_data.name]
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return (context.bl_idname == "PlasmaNodeTree") return (context.bl_idname == "PlasmaNodeTree")
@ -420,9 +454,11 @@ class PlasmaNodeTree(bpy.types.NodeTree):
bl_icon = "NODETREE" bl_icon = "NODETREE"
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
# just pass it off to each node exported_nodes = exporter.exported_nodes.setdefault(self.name, set())
for node in self.nodes: for node in self.nodes:
if not (node.export_once and node.previously_exported(exporter)):
node.export(exporter, bo, so) node.export(exporter, bo, so)
exported_nodes.add(node.name)
def find_output(self, idname): def find_output(self, idname):
for node in self.nodes: for node in self.nodes:

6
korman/nodes/node_logic.py

@ -82,7 +82,7 @@ class PlasmaExcludeRegionNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.typ
def get_key(self, exporter, parent_so): def get_key(self, exporter, parent_so):
if self.region_object is None: if self.region_object is None:
self.raise_error("Region must be set") self.raise_error("Region must be set")
return exporter.mgr.find_create_key(plExcludeRegionModifier, bl=self.region_object, name=self.key_name) return self._find_create_key(plExcludeRegionModifier, exporter, bl=self.region_object)
def harvest_actors(self, bo): def harvest_actors(self, bo):
return [i.safepoint.name for i in self.find_input_sockets("safe_points") if i.safepoint is not None] return [i.safepoint.name for i in self.find_input_sockets("safe_points") if i.safepoint is not None]
@ -108,6 +108,10 @@ class PlasmaExcludeRegionNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.typ
physical.memberGroup = plSimDefs.kGroupDetector physical.memberGroup = plSimDefs.kGroupDetector
physical.collideGroup |= 1 << plSimDefs.kGroupDynamic physical.collideGroup |= 1 << plSimDefs.kGroupDynamic
@property
def export_once(self):
return True
@classmethod @classmethod
def _idprop_mapping(cls): def _idprop_mapping(cls):
return {"region_object": "region"} return {"region_object": "region"}

7
korman/nodes/node_messages.py

@ -544,6 +544,8 @@ class PlasmaOneShotMsgNode(idprops.IDPropObjectMixin, PlasmaMessageWithCallbacks
layout.prop(self, "seek") layout.prop(self, "seek")
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
# Note: we purposefully allow this to proceed because plOneShotMod is a MultiMod, so we
# want all referencing SOs to get a copy of the modifier.
oneshotmod = self.get_key(exporter, so).object oneshotmod = self.get_key(exporter, so).object
oneshotmod.animName = self.animation oneshotmod.animName = self.animation
oneshotmod.drivable = self.drivable oneshotmod.drivable = self.drivable
@ -553,12 +555,11 @@ class PlasmaOneShotMsgNode(idprops.IDPropObjectMixin, PlasmaMessageWithCallbacks
oneshotmod.seekDuration = 1.0 oneshotmod.seekDuration = 1.0
def get_key(self, exporter, so): def get_key(self, exporter, so):
name = self.key_name
if self.pos_object is not None: if self.pos_object is not None:
pos_so = exporter.mgr.find_create_object(plSceneObject, bl=self.pos_object) pos_so = exporter.mgr.find_create_object(plSceneObject, bl=self.pos_object)
return exporter.mgr.find_create_key(plOneShotMod, name=name, so=pos_so) return self._find_create_key(plOneShotMod, exporter, so=pos_so)
else: else:
return exporter.mgr.find_create_key(plOneShotMod, name=name, so=so) return self._find_create_key(plOneShotMod, exporter, so=so)
def harvest_actors(self, bo): def harvest_actors(self, bo):
actors = set() actors = set()

7
korman/nodes/node_python.py

@ -252,10 +252,15 @@ class PlasmaPythonFileNode(PlasmaVersionedNode, bpy.types.Node):
layout.label(text="Script '{}' is not loaded in Blender".format(self.filename), icon="ERROR") layout.label(text="Script '{}' is not loaded in Blender".format(self.filename), icon="ERROR")
def get_key(self, exporter, so): def get_key(self, exporter, so):
return exporter.mgr.find_create_key(plPythonFileMod, name=self.key_name, so=so) return self._find_create_key(plPythonFileMod, exporter, so=so)
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
pfm = self.get_key(exporter, so).object pfm = self.get_key(exporter, so).object
# No need to continue if the PFM was already generated.
if self.previously_exported(exporter):
return
py_name = Path(self.filename).stem py_name = Path(self.filename).stem
pfm.filename = py_name pfm.filename = py_name

7
korman/nodes/node_responder.py

@ -83,7 +83,7 @@ class PlasmaResponderNode(PlasmaVersionedNode, bpy.types.Node):
layout.prop(self, "no_ff_sounds") layout.prop(self, "no_ff_sounds")
def get_key(self, exporter, so): def get_key(self, exporter, so):
return exporter.mgr.find_create_key(plResponderModifier, name=self.key_name, so=so) return self._find_create_key(plResponderModifier, exporter, so=so)
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
responder = self.get_key(exporter, so).object responder = self.get_key(exporter, so).object
@ -135,6 +135,11 @@ class PlasmaResponderNode(PlasmaVersionedNode, bpy.types.Node):
stateMgr.register_state(stateNode) stateMgr.register_state(stateNode)
stateMgr.convert_states(exporter, so) stateMgr.convert_states(exporter, so)
@property
def export_once(self):
# What exactly is a reused responder? All the messages are directed, after all...
return True
@property @property
def latest_version(self): def latest_version(self):
return 2 return 2

14
korman/nodes/node_softvolume.py

@ -130,7 +130,7 @@ class PlasmaSoftVolumeInvertNode(PlasmaNodeBase, bpy.types.Node):
]) ])
def get_key(self, exporter, so): def get_key(self, exporter, so):
return exporter.mgr.find_create_key(plSoftVolumeInvert, name=self.key_name, so=so) return self._find_create_key(plSoftVolumeInvert, exporter, so=so)
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
parent = self.find_input("input") parent = self.find_input("input")
@ -147,6 +147,10 @@ class PlasmaSoftVolumeInvertNode(PlasmaNodeBase, bpy.types.Node):
sv.insideStrength = 1.0 sv.insideStrength = 1.0
sv.outsideStrength = 0.0 sv.outsideStrength = 0.0
@property
def export_once(self):
return True
class PlasmaSoftVolumeLinkNode(PlasmaNodeBase): class PlasmaSoftVolumeLinkNode(PlasmaNodeBase):
input_sockets = OrderedDict([ input_sockets = OrderedDict([
@ -180,6 +184,10 @@ class PlasmaSoftVolumeLinkNode(PlasmaNodeBase):
sv.insideStrength = 1.0 sv.insideStrength = 1.0
sv.outsideStrength = 0.0 sv.outsideStrength = 0.0
@property
def export_once(self):
return True
class PlasmaSoftVolumeIntersectNode(PlasmaSoftVolumeLinkNode, bpy.types.Node): class PlasmaSoftVolumeIntersectNode(PlasmaSoftVolumeLinkNode, bpy.types.Node):
bl_category = "SV" bl_category = "SV"
@ -188,7 +196,7 @@ class PlasmaSoftVolumeIntersectNode(PlasmaSoftVolumeLinkNode, bpy.types.Node):
def get_key(self, exporter, so): def get_key(self, exporter, so):
## FIXME: SoftVolumeIntersect should not be listed as an interface ## FIXME: SoftVolumeIntersect should not be listed as an interface
return exporter.mgr.find_create_key(plSoftVolumeIntersect, name=self.key_name, so=so) return self._find_create_key(plSoftVolumeIntersect, exporter, so=so)
class PlasmaSoftVolumeUnionNode(PlasmaSoftVolumeLinkNode, bpy.types.Node): class PlasmaSoftVolumeUnionNode(PlasmaSoftVolumeLinkNode, bpy.types.Node):
@ -198,4 +206,4 @@ class PlasmaSoftVolumeUnionNode(PlasmaSoftVolumeLinkNode, bpy.types.Node):
def get_key(self, exporter, so): def get_key(self, exporter, so):
## FIXME: SoftVolumeUnion should not be listed as an interface ## FIXME: SoftVolumeUnion should not be listed as an interface
return exporter.mgr.find_create_key(plSoftVolumeUnion, name=self.key_name, so=so) return self._find_create_key(plSoftVolumeUnion, exporter, so=so)

19
korman/properties/modifiers/logic.py

@ -23,7 +23,6 @@ from ...exporter import ExportError
from ... import idprops from ... import idprops
class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup): class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup):
name = StringProperty(name="Name")
version = EnumProperty(name="Version", version = EnumProperty(name="Version",
description="Plasma versions this node tree exports under", description="Plasma versions this node tree exports under",
items=game_versions, items=game_versions,
@ -32,8 +31,6 @@ class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup):
node_tree = PointerProperty(name="Node Tree", node_tree = PointerProperty(name="Node Tree",
description="Node Tree to export", description="Node Tree to export",
type=bpy.types.NodeTree) type=bpy.types.NodeTree)
node_name = StringProperty(name="Node Ref",
description="Attach a reference to this node")
@classmethod @classmethod
def _idprop_mapping(cls): def _idprop_mapping(cls):
@ -62,20 +59,8 @@ class PlasmaAdvancedLogic(PlasmaModifierProperties):
if i.node_tree is None: if i.node_tree is None:
raise ExportError("'{}': Advanced Logic is missing a node tree for '{}'".format(bo.name, i.name)) raise ExportError("'{}': Advanced Logic is missing a node tree for '{}'".format(bo.name, i.name))
# If node_name is defined, then we're only adding a reference. We will make sure that # Defer node tree export until all trees are harvested.
# the entire node tree is exported once before the post_export step, however. exporter.want_node_trees.setdefault(i.node_tree.name, set()).add((bo, so))
if i.node_name:
exporter.want_node_trees[i.node_tree.name] = (bo, so)
node = i.node_tree.nodes.get(i.node_name, None)
if node is None:
raise ExportError("Node '{}' does not exist in '{}'".format(i.node_name, i.node_tree.name))
# We are going to assume get_key will do the adding correctly. Single modifiers
# should fetch the appropriate SceneObject before doing anything, so this will
# be a no-op in that case. Multi modifiers should accept any SceneObject, however
node.get_key(exporter, so)
else:
exporter.node_trees_exported.add(i.node_tree.name)
i.node_tree.export(exporter, bo, so)
def harvest_actors(self): def harvest_actors(self):
actors = set() actors = set()

5
korman/properties/modifiers/region.py

@ -284,9 +284,8 @@ class PlasmaSoftVolume(idprops.IDPropMixin, PlasmaModifierProperties):
def _export_sv_nodes(self, exporter, bo, so): def _export_sv_nodes(self, exporter, bo, so):
tree = self.get_node_tree() tree = self.get_node_tree()
if tree.name not in exporter.node_trees_exported: # Stash for later
exporter.node_trees_exported.add(tree.name) exporter.want_node_trees.setdefault(tree.name, set()).add((bo, so))
tree.export(exporter, bo, so)
def get_node_tree(self): def get_node_tree(self):
if self.node_tree is None: if self.node_tree is None:

15
korman/ui/modifiers/logic.py

@ -19,25 +19,22 @@ from .. import ui_list
class LogicListUI(bpy.types.UIList): class LogicListUI(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0):
layout.prop(item, "name", emboss=False, text="", icon="NODETREE") if item.node_tree:
# Using layout.prop on the pointer prevents clicking on the item O.o
layout.label(item.node_tree.name, icon="NODETREE")
else:
layout.label("[Empty]")
def advanced_logic(modifier, layout, context): def advanced_logic(modifier, layout, context):
ui_list.draw_modifier_list(layout, "LogicListUI", modifier, "logic_groups", ui_list.draw_modifier_list(layout, "LogicListUI", modifier, "logic_groups",
"active_group_index", name_prefix="Logic", "active_group_index", rows=2, maxrows=3)
name_prop="name", rows=2, maxrows=3)
# Modify the logic groups # Modify the logic groups
if modifier.logic_groups: if modifier.logic_groups:
logic = modifier.logic_groups[modifier.active_group_index] logic = modifier.logic_groups[modifier.active_group_index]
layout.row().prop_menu_enum(logic, "version") layout.row().prop_menu_enum(logic, "version")
layout.prop(logic, "node_tree", icon="NODETREE") layout.prop(logic, "node_tree", icon="NODETREE")
try:
layout.prop_search(logic, "node_name", logic.node_tree, "nodes", icon="NODE")
except:
row = layout.row()
row.enabled = False
row.prop(logic, "node_name", icon="NODE")
def spawnpoint(modifier, layout, context): def spawnpoint(modifier, layout, context):
layout.label(text="Avatar faces negative Y.") layout.label(text="Avatar faces negative Y.")

Loading…
Cancel
Save