From 72547b0570939bfa74478fb83c76a14cb98ab51b Mon Sep 17 00:00:00 2001 From: Joseph Davies Date: Fri, 15 Sep 2017 17:19:46 -0700 Subject: [PATCH] Use Python Node Attribute Arguments. Parses and loads the PFM arguments into accessible properties, used by numeric nodes for range, dropdown nodes for enums, etc. Also moves PlasmaAttribDropDownListNode into its proper alphabetical position. --- korman/nodes/node_python.py | 116 +++++++++++++++++++++++++++++------ korman/operators/op_nodes.py | 57 +++++++++++++++++ 2 files changed, 154 insertions(+), 19 deletions(-) diff --git a/korman/nodes/node_python.py b/korman/nodes/node_python.py index 89e52bf..fc36536 100644 --- a/korman/nodes/node_python.py +++ b/korman/nodes/node_python.py @@ -86,6 +86,52 @@ _attrib_key_types = { "ptAttribGrassShader": plFactory.ClassIndex("plGrassShaderMod"), } + +class StringVectorProperty(bpy.types.PropertyGroup): + value = StringProperty() + + +class PlasmaAttributeArguments(bpy.types.PropertyGroup): + byObject = BoolProperty() + default = StringProperty() + options = CollectionProperty(type=StringVectorProperty) + range_values = FloatVectorProperty(size=2) + netForce = BoolProperty() + netPropagate = BoolProperty() + stateList = CollectionProperty(type=StringVectorProperty) + visListId = IntProperty() + visListStates = CollectionProperty(type=StringVectorProperty) + + def set_arguments(self, args): + for name in args: + if name == "byObject": + self.byObject = bool(args[name]) + elif name == "default": + self.default = str(args[name]) + elif name == "options": + for option in args[name]: + item = self.options.add() + item.value = str(option) + elif name in ("range", "rang"): + self.range_values = args[name] + elif name == "netForce": + self.netForce = bool(args[name]) + elif name in ("netPropagate", "netProp"): + self.netPropagate = bool(args[name]) + elif name == "stateList": + for state in args[name]: + item = self.stateList.add() + item.value = str(state) + elif name == "vislistid": + self.visListId = int(args[name]) + elif name == "visliststates": + for state in args[name]: + item = self.visListStates.add() + item.value = str(state) + else: + print("Unknown argument '{}' with value '{}'!".format(name, args[name])) + + class PlasmaAttribute(bpy.types.PropertyGroup): attribute_id = IntProperty() attribute_type = StringProperty() @@ -98,6 +144,9 @@ class PlasmaAttribute(bpy.types.PropertyGroup): value_float = FloatProperty() value_bool = BoolProperty() + # Special Arguments + attribute_arguments = PointerProperty(type=PlasmaAttributeArguments) + _simple_attrs = { "ptAttribString": "value_string", "ptAttribDropDownList": "value_string", @@ -292,6 +341,10 @@ class PlasmaPythonFileNodeSocket(bpy.types.NodeSocket): def simple_value(self): return self.node.attribute_map[self.attribute_id].simple_value + @property + def attribute_arguments(self): + return self.node.attribute_map[self.attribute_id].attribute_arguments + class PlasmaPythonAttribNodeSocket(bpy.types.NodeSocket): def draw(self, context, layout, node, text): @@ -372,6 +425,31 @@ class PlasmaAttribBoolNode(PlasmaAttribNodeBase, bpy.types.Node): self.inited = True +class PlasmaAttribDropDownListNode(PlasmaAttribNodeBase, bpy.types.Node): + bl_category = "PYTHON" + bl_idname = "PlasmaAttribDropDownListNode" + bl_label = "Drop Down List Attribute" + + pl_attrib = "ptAttribDropDownList" + + def _list_items(self, context): + attrib = self.to_socket + if attrib is not None: + return [(option.value, option.value, "") for option in attrib.attribute_arguments.options] + else: + return [] + value = EnumProperty(items=_list_items) + + def draw_buttons(self, context, layout): + layout.prop(self, "value", text=self.attribute_name) + + def update(self): + super().update() + attrib = self.to_socket + if attrib is not None and not self.value: + self.value = attrib.simple_value + + class PlasmaAttribNumericNode(PlasmaAttribNodeBase, bpy.types.Node): bl_category = "PYTHON" bl_idname = "PlasmaAttribNumericNode" @@ -397,11 +475,16 @@ class PlasmaAttribNumericNode(PlasmaAttribNodeBase, bpy.types.Node): def draw_buttons(self, context, layout): attrib = self.to_socket + if attrib is None: layout.prop(self, "value_int", text="Value") elif attrib.attribute_type == "ptAttribFloat": + self._range_label(layout) + layout.alert = self._out_of_range(self.value_float) layout.prop(self, "value_float", text=attrib.name) elif attrib.attribute_type == "ptAttribInt": + self._range_label(layout) + layout.alert = self._out_of_range(self.value_int) layout.prop(self, "value_int", text=attrib.name) else: raise RuntimeError() @@ -421,6 +504,19 @@ class PlasmaAttribNumericNode(PlasmaAttribNodeBase, bpy.types.Node): else: return self.value_float + def _range_label(self, layout): + attrib = self.to_socket + layout.label(text="Range: [{}, {}]".format(attrib.attribute_arguments.range_values[0], attrib.attribute_arguments.range_values[1])) + + def _out_of_range(self, value): + attrib = self.to_socket + if attrib.attribute_arguments.range_values[0] == attrib.attribute_arguments.range_values[1]: + # Ignore degenerate intervals + return False + if attrib.attribute_arguments.range_values[0] <= value <= attrib.attribute_arguments.range_values[1]: + return False + return True + class PlasmaAttribObjectNode(idprops.IDPropObjectMixin, PlasmaAttribNodeBase, bpy.types.Node): bl_category = "PYTHON" @@ -476,24 +572,6 @@ class PlasmaAttribObjectNode(idprops.IDPropObjectMixin, PlasmaAttribNodeBase, bp return {"target_object": "object_name"} -class PlasmaAttribDropDownListNode(PlasmaAttribNodeBase, bpy.types.Node): - bl_category = "PYTHON" - bl_idname = "PlasmaAttribDropDownListNode" - bl_label = "Drop Down List Attribute" - - pl_attrib = "ptAttribDropDownList" - value = StringProperty() - - def draw_buttons(self, context, layout): - layout.prop(self, "value", text=self.attribute_name) - - def update(self): - super().update() - attrib = self.to_socket - if attrib is not None and not self.value: - self.value = attrib.simple_value - - class PlasmaAttribStringNode(PlasmaAttribNodeBase, bpy.types.Node): bl_category = "PYTHON" bl_idname = "PlasmaAttribStringNode" @@ -605,12 +683,12 @@ _attrib_colors = { "ptAttribActivatorList": (0.188, 0.086, 0.349, 1.0), "ptAttribBoolean": (0.71, 0.706, 0.655, 1.0), "ptAttribExcludeRegion": (0.031, 0.110, 0.290, 1.0), + "ptAttribDropDownList": (0.475, 0.459, 0.494, 1.0), "ptAttribNamedActivator": (0.188, 0.086, 0.349, 1.0), "ptAttribNamedResponder": (0.031, 0.110, 0.290, 1.0), "ptAttribResponder": (0.031, 0.110, 0.290, 1.0), "ptAttribResponderList": (0.031, 0.110, 0.290, 1.0), "ptAttribString": (0.675, 0.659, 0.494, 1.0), - "ptAttribDropDownList": (0.475, 0.459, 0.494, 1.0), PlasmaAttribNumericNode.pl_attrib: (0.443, 0.439, 0.392, 1.0), PlasmaAttribObjectNode.pl_attrib: (0.565, 0.267, 0.0, 1.0), diff --git a/korman/operators/op_nodes.py b/korman/operators/op_nodes.py index 689b308..803275b 100644 --- a/korman/operators/op_nodes.py +++ b/korman/operators/op_nodes.py @@ -47,6 +47,47 @@ class SelectFileOperator(NodeOperator, bpy.types.Operator): context.window_manager.fileselect_add(self) return {"RUNNING_MODAL"} +pyAttribArgMap= { + "ptAttribute": + ["vislistid", "visliststates"], + "ptAttribBoolean": + ["default"], + "ptAttribInt": + ["default", "rang"], + "ptAttribFloat": + ["default", "rang"], + "ptAttribString": + ["default"], + "ptAttribDropDownList": + ["options"], + "ptAttribSceneobject": + ["netForce"], + "ptAttribSceneobjectList": + ["byObject", "netForce"], + "ptAttributeKeyList": + ["byObject", "netForce"], + "ptAttribActivator": + ["byObject", "netForce"], + "ptAttribActivatorList": + ["byObject", "netForce"], + "ptAttribResponder": + ["stateList", "byObject", "netForce", "netPropagate"], + "ptAttribResponderList": + ["stateList", "byObject", "netForce", "netPropagate"], + "ptAttribNamedActivator": + ["byObject", "netForce"], + "ptAttribNamedResponder": + ["stateList", "byObject", "netForce", "netPropagate"], + "ptAttribDynamicMap": + ["netForce"], + "ptAttribAnimation": + ["byObject", "netForce"], + "ptAttribBehavior": + ["netForce", "netProp"], + "ptAttribMaterialList": + ["byObject", "netForce"], + } + class PlPyAttributeNodeOperator(NodeOperator, bpy.types.Operator): bl_idname = "node.plasma_attributes_to_node" @@ -81,5 +122,21 @@ class PlPyAttributeNodeOperator(NodeOperator, bpy.types.Operator): default = attrib.get("default", None) if default is not None and cached.is_simple_value: cached.simple_value = default + + argmap = {} + args = attrib.get("args", None) + # Load our default argument mapping + if args is not None: + if cached.attribute_type in pyAttribArgMap.keys(): + argmap.update(dict(zip(pyAttribArgMap[cached.attribute_type], args))) + else: + print("Found ptAttribute type '{}' with unknown arguments: {}".format(cached.attribute_type, args)) + # Add in/set any arguments provided by keyword + if not set(pyAttribArgMap[cached.attribute_type]).isdisjoint(attrib.keys()): + argmap.update({key: attrib[key] for key in attrib if key in pyAttribArgMap[cached.attribute_type]}) + # Attach the arguments to the attribute + if argmap: + cached.attribute_arguments.set_arguments(argmap) + node.update() return {"FINISHED"}