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"}