From 9f450d0bdb4045afaafd73463e456c74a9e7f135 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Wed, 23 Oct 2019 20:20:14 -0400 Subject: [PATCH] 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. --- korman/exporter/convert.py | 14 +++---- korman/nodes/node_avatar.py | 4 +- korman/nodes/node_conditions.py | 54 +++++++++++++-------------- korman/nodes/node_core.py | 50 +++++++++++++++++++++---- korman/nodes/node_logic.py | 6 ++- korman/nodes/node_messages.py | 7 ++-- korman/nodes/node_python.py | 7 +++- korman/nodes/node_responder.py | 7 +++- korman/nodes/node_softvolume.py | 14 +++++-- korman/properties/modifiers/logic.py | 19 +--------- korman/properties/modifiers/region.py | 5 +-- korman/ui/modifiers/logic.py | 15 +++----- 12 files changed, 119 insertions(+), 83 deletions(-) diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py index bd5353d..e1ed25a 100644 --- a/korman/exporter/convert.py +++ b/korman/exporter/convert.py @@ -38,8 +38,8 @@ class Exporter: self._op = op # Blender export operator self._objects = [] self.actors = set() - self.node_trees_exported = set() self.want_node_trees = {} + self.exported_nodes = {} def run(self): log = logger.ExportVerboseLogger if self._op.verbose else logger.ExportProgressLogger @@ -276,13 +276,11 @@ class Exporter: inc_progress = self.report.progress_increment self.report.msg("\nChecking Logic Trees...") - need_to_export = [(name, bo, so) for name, (bo, so) in self.want_node_trees.items() - if name not in self.node_trees_exported] - self.report.progress_value = len(self.want_node_trees) - len(need_to_export) - - for tree, bo, so in need_to_export: - self.report.msg("NodeTree '{}'", tree, indent=1) - bpy.data.node_groups[tree].export(self, bo, so) + for tree_name, references in self.want_node_trees.items(): + self.report.msg("NodeTree '{}'", tree_name, indent=1) + tree = bpy.data.node_groups[tree_name] + for bo, so in references: + tree.export(self, bo, so) inc_progress() def _harvest_actors(self): diff --git a/korman/nodes/node_avatar.py b/korman/nodes/node_avatar.py index 7d818e6..22deeb4 100644 --- a/korman/nodes/node_avatar.py +++ b/korman/nodes/node_avatar.py @@ -56,10 +56,10 @@ class PlasmaSittingBehaviorNode(PlasmaNodeBase, bpy.types.Node): layout.prop_menu_enum(self, "approach") 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): - sitmod = self.get_key(exporter, so).object + sitmod = self._find_create_object(plSittingModifier, exporter, so=so) for flag in self.approach: sitmod.miscFlags |= getattr(plSittingModifier, flag) for i in self.find_outputs("satisfies"): diff --git a/korman/nodes/node_conditions.py b/korman/nodes/node_conditions.py index 04a5673..90f741f 100644 --- a/korman/nodes/node_conditions.py +++ b/korman/nodes/node_conditions.py @@ -74,9 +74,8 @@ class PlasmaClickableNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.N if clickable_bo is None: clickable_bo = parent_bo - name = self.key_name - interface = exporter.mgr.find_create_key(plInterfaceInfoModifier, name=name, so=clickable_so).object - logicmod = exporter.mgr.find_create_key(plLogicModifier, name=name, so=clickable_so) + interface = self._find_create_object(plInterfaceInfoModifier, exporter, bl=clickable_bo, so=clickable_so) + logicmod = self._find_create_key(plLogicModifier, exporter, bl=clickable_bo, so=clickable_so) interface.addIntfKey(logicmod) # Matches data seen in Cyan's PRPs... interface.addIntfKey(logicmod) @@ -105,11 +104,11 @@ class PlasmaClickableNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.N physical.LOSDBs |= plSimDefs.kLOSDBUIItems # 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) # 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) logicmod.addCondition(activator.key) 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.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): # careful... we really make lots of keys... 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 def _get_objects(self, exporter, parent_so): @@ -185,7 +188,7 @@ class PlasmaClickableRegionNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.t region_bo = self.region_object if region_bo is None: 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.... 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 # 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. - name = self.key_name - detector_key = exporter.mgr.find_create_key(plObjectInVolumeDetector, name=name, so=region_so) - detector = detector_key.object + detector = self._find_create_object(plObjectInVolumeDetector, exporter, bl=region_bo, so=region_so) detector.addReceiver(logicmod.key) detector.type = plObjectInVolumeDetector.kTypeAny # 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... - 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 logicmod.addCondition(objinbox_key) @@ -270,19 +271,18 @@ class PlasmaFacingTargetSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): # First, gather the schtuff from the appropriate blah blah blah if self.simple_mode: + node = self.node directional = True tolerance = 45 - name = "{}_SimpleFacing".format(self.node.key_name) elif self.is_linked: node = self.links[0].from_node directional = node.directional tolerance = node.tolerance - name = node.key_name else: # This is a programmer failure, so we need a traceback. 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.directional = directional facing.satisfied = True @@ -395,13 +395,12 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type self.raise_error("Region cannot be empty") so = exporter.mgr.find_create_object(plSceneObject, bl=bo) rgn_enter, rgn_exit = None, None + parent_key = parent_so.key if self.report_enters: - theName = "{}_{}_Enter".format(self.id_data.name, self.name) - rgn_enter = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so) + rgn_enter = self._find_create_key(plLogicModifier, exporter, suffix="Enter", bl=bo, so=so) if self.report_exits: - theName = "{}_{}_Exit".format(self.id_data.name, self.name) - rgn_exit = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so) + rgn_exit = self._find_create_key(plLogicModifier, exporter, suffix="Exit", bl=bo, so=so) if rgn_enter is None: return rgn_exit @@ -415,12 +414,12 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type return (rgn_enter, rgn_exit) def export(self, exporter, bo, parent_so): - # We need to ensure we export to the correct SO region_bo = self.region_object if region_bo is None: self.raise_error("Region cannot be empty") + 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 enter_simple = self.find_input_socket("enter").allow @@ -452,20 +451,15 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type else: suffix = "Exit" - theName = "{}_{}_{}".format(self.id_data.name, self.name, suffix) - exporter.report.msg("[LogicModifier '{}']", theName, indent=2) - logicKey = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so) + logicKey = self._find_create_key(plLogicModifier, exporter, suffix=suffix, bl=bo, so=so) logicmod = logicKey.object logicmod.setLogicFlag(plLogicModifier.kMultiTrigger, True) logicmod.notify = self.generate_notify_msg(exporter, so, "satisfies") # Now, the detector objects - exporter.report.msg("[ObjectInVolumeDetector '{}']", theName, indent=2) - detKey = exporter.mgr.find_create_key(plObjectInVolumeDetector, name=theName, so=so) - det = detKey.object + det = self._find_create_object(plObjectInVolumeDetector, exporter, suffix=suffix, bl=bo, so=so) - exporter.report.msg("[VolumeSensorConditionalObject '{}']", theName, indent=2) - volKey = exporter.mgr.find_create_key(plVolumeSensorConditionalObject, name=theName, so=so) + volKey = self._find_create_key(plVolumeSensorConditionalObject, exporter, suffix=suffix, bl=bo, so=so) volsens = volKey.object volsens.type = event @@ -483,6 +477,10 @@ class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.type logicmod.addCondition(volKey) return logicKey + @property + def export_once(self): + return True + @classmethod def _idprop_mapping(cls): return {"region_object": "region"} diff --git a/korman/nodes/node_core.py b/korman/nodes/node_core.py index e84a1fb..073ad65 100644 --- a/korman/nodes/node_core.py +++ b/korman/nodes/node_core.py @@ -15,7 +15,7 @@ import abc import bpy -from PyHSPlasma import plMessage, plNotifyMsg +from PyHSPlasma import * from ..exporter import ExportError @@ -37,6 +37,20 @@ class PlasmaNodeBase: def get_key(self, exporter, so): 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): if hasattr(self, "pl_label_attr") and self.hide: return str(getattr(self, self.pl_label_attrib, self.bl_label)) @@ -45,6 +59,27 @@ class PlasmaNodeBase: def export(self, exporter, bo, so): 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): for i in self.inputs: if i.alias == key: @@ -188,10 +223,6 @@ class PlasmaNodeBase: def harvest_actors(self, bo): return set() - @property - def key_name(self): - return "{}_{}".format(self.id_data.name, self.name) - def link_input(self, node, out_key, in_key): """Links a given Node's output socket to a given input socket on this Node""" if isinstance(in_key, str): @@ -221,6 +252,9 @@ class PlasmaNodeBase: """Returns an absolute path to this Node. Needed because repr() uses an elipsis...""" 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 def poll(cls, context): return (context.bl_idname == "PlasmaNodeTree") @@ -420,9 +454,11 @@ class PlasmaNodeTree(bpy.types.NodeTree): bl_icon = "NODETREE" 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: - node.export(exporter, bo, so) + if not (node.export_once and node.previously_exported(exporter)): + node.export(exporter, bo, so) + exported_nodes.add(node.name) def find_output(self, idname): for node in self.nodes: diff --git a/korman/nodes/node_logic.py b/korman/nodes/node_logic.py index 812571d..27804a2 100644 --- a/korman/nodes/node_logic.py +++ b/korman/nodes/node_logic.py @@ -82,7 +82,7 @@ class PlasmaExcludeRegionNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.typ def get_key(self, exporter, parent_so): if self.region_object is None: 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): 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.collideGroup |= 1 << plSimDefs.kGroupDynamic + @property + def export_once(self): + return True + @classmethod def _idprop_mapping(cls): return {"region_object": "region"} diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py index 7ac4f09..3e6ad30 100644 --- a/korman/nodes/node_messages.py +++ b/korman/nodes/node_messages.py @@ -544,6 +544,8 @@ class PlasmaOneShotMsgNode(idprops.IDPropObjectMixin, PlasmaMessageWithCallbacks layout.prop(self, "seek") 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.animName = self.animation oneshotmod.drivable = self.drivable @@ -553,12 +555,11 @@ class PlasmaOneShotMsgNode(idprops.IDPropObjectMixin, PlasmaMessageWithCallbacks oneshotmod.seekDuration = 1.0 def get_key(self, exporter, so): - name = self.key_name if self.pos_object is not None: 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: - 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): actors = set() diff --git a/korman/nodes/node_python.py b/korman/nodes/node_python.py index 0780c6b..cc58c5c 100644 --- a/korman/nodes/node_python.py +++ b/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") 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): 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 pfm.filename = py_name diff --git a/korman/nodes/node_responder.py b/korman/nodes/node_responder.py index a79c9b8..a400182 100644 --- a/korman/nodes/node_responder.py +++ b/korman/nodes/node_responder.py @@ -83,7 +83,7 @@ class PlasmaResponderNode(PlasmaVersionedNode, bpy.types.Node): layout.prop(self, "no_ff_sounds") 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): responder = self.get_key(exporter, so).object @@ -135,6 +135,11 @@ class PlasmaResponderNode(PlasmaVersionedNode, bpy.types.Node): stateMgr.register_state(stateNode) 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 def latest_version(self): return 2 diff --git a/korman/nodes/node_softvolume.py b/korman/nodes/node_softvolume.py index bf68cc9..06b8144 100644 --- a/korman/nodes/node_softvolume.py +++ b/korman/nodes/node_softvolume.py @@ -130,7 +130,7 @@ class PlasmaSoftVolumeInvertNode(PlasmaNodeBase, bpy.types.Node): ]) 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): parent = self.find_input("input") @@ -147,6 +147,10 @@ class PlasmaSoftVolumeInvertNode(PlasmaNodeBase, bpy.types.Node): sv.insideStrength = 1.0 sv.outsideStrength = 0.0 + @property + def export_once(self): + return True + class PlasmaSoftVolumeLinkNode(PlasmaNodeBase): input_sockets = OrderedDict([ @@ -180,6 +184,10 @@ class PlasmaSoftVolumeLinkNode(PlasmaNodeBase): sv.insideStrength = 1.0 sv.outsideStrength = 0.0 + @property + def export_once(self): + return True + class PlasmaSoftVolumeIntersectNode(PlasmaSoftVolumeLinkNode, bpy.types.Node): bl_category = "SV" @@ -188,7 +196,7 @@ class PlasmaSoftVolumeIntersectNode(PlasmaSoftVolumeLinkNode, bpy.types.Node): def get_key(self, exporter, so): ## 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): @@ -198,4 +206,4 @@ class PlasmaSoftVolumeUnionNode(PlasmaSoftVolumeLinkNode, bpy.types.Node): def get_key(self, exporter, so): ## 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) diff --git a/korman/properties/modifiers/logic.py b/korman/properties/modifiers/logic.py index f291462..a37faf1 100644 --- a/korman/properties/modifiers/logic.py +++ b/korman/properties/modifiers/logic.py @@ -23,7 +23,6 @@ from ...exporter import ExportError from ... import idprops class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup): - name = StringProperty(name="Name") version = EnumProperty(name="Version", description="Plasma versions this node tree exports under", items=game_versions, @@ -32,8 +31,6 @@ class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup): node_tree = PointerProperty(name="Node Tree", description="Node Tree to export", type=bpy.types.NodeTree) - node_name = StringProperty(name="Node Ref", - description="Attach a reference to this node") @classmethod def _idprop_mapping(cls): @@ -62,20 +59,8 @@ class PlasmaAdvancedLogic(PlasmaModifierProperties): if i.node_tree is None: 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 - # the entire node tree is exported once before the post_export step, however. - 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) + # Defer node tree export until all trees are harvested. + exporter.want_node_trees.setdefault(i.node_tree.name, set()).add((bo, so)) def harvest_actors(self): actors = set() diff --git a/korman/properties/modifiers/region.py b/korman/properties/modifiers/region.py index 61ae21e..e45a59e 100644 --- a/korman/properties/modifiers/region.py +++ b/korman/properties/modifiers/region.py @@ -284,9 +284,8 @@ class PlasmaSoftVolume(idprops.IDPropMixin, PlasmaModifierProperties): def _export_sv_nodes(self, exporter, bo, so): tree = self.get_node_tree() - if tree.name not in exporter.node_trees_exported: - exporter.node_trees_exported.add(tree.name) - tree.export(exporter, bo, so) + # Stash for later + exporter.want_node_trees.setdefault(tree.name, set()).add((bo, so)) def get_node_tree(self): if self.node_tree is None: diff --git a/korman/ui/modifiers/logic.py b/korman/ui/modifiers/logic.py index 2a789a1..d657b5f 100644 --- a/korman/ui/modifiers/logic.py +++ b/korman/ui/modifiers/logic.py @@ -19,25 +19,22 @@ from .. import ui_list class LogicListUI(bpy.types.UIList): 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): ui_list.draw_modifier_list(layout, "LogicListUI", modifier, "logic_groups", - "active_group_index", name_prefix="Logic", - name_prop="name", rows=2, maxrows=3) + "active_group_index", rows=2, maxrows=3) # Modify the logic groups if modifier.logic_groups: logic = modifier.logic_groups[modifier.active_group_index] layout.row().prop_menu_enum(logic, "version") 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): layout.label(text="Avatar faces negative Y.")