Browse Source

PFM exporting

Hopefully this isn't too holey
pull/10/head
Adam Johnson 9 years ago
parent
commit
0b4ddd2bc0
  1. 247
      korman/nodes/node_python.py
  2. 2
      korman/operators/op_nodes.py

247
korman/nodes/node_python.py

@ -20,18 +20,6 @@ from PyHSPlasma import *
from .node_core import * from .node_core import *
_attrib_colors = {
"ptAttribActivator": (0.451, 0.0, 0.263, 1.0),
"ptAttribActivatorList": (0.451, 0.0, 0.263, 1.0),
"ptAttribBoolean": (0.71, 0.706, 0.655, 1.0),
"ptAttribFloat": (0.443, 0.439, 0.392, 1.0),
("ptAttribFloat", "ptAttribInt"): (0.443, 0.439, 0.392, 1.0),
"ptAttribInt": (0.443, 0.439, 0.392, 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),
}
_single_user_attribs = { _single_user_attribs = {
"ptAttribBoolean", "ptAttribInt", "ptAttribFloat", "ptAttribString", "ptAttribDropDownList", "ptAttribBoolean", "ptAttribInt", "ptAttribFloat", "ptAttribString", "ptAttribDropDownList",
"ptAttribSceneobject", "ptAttribDynamicMap", "ptAttribGUIDialog", "ptAttribExcludeRegion", "ptAttribSceneobject", "ptAttribDynamicMap", "ptAttribGUIDialog", "ptAttribExcludeRegion",
@ -40,6 +28,61 @@ _single_user_attribs = {
"ptAttribGrassShader", "ptAttribGrassShader",
} }
_attrib2param = {
"ptAttribInt": plPythonParameter.kInt,
"ptAttribFloat": plPythonParameter.kFloat,
"ptAttribBoolean": plPythonParameter.kBoolean,
"ptAttribString": plPythonParameter.kString,
"ptAttribSceneobject": plPythonParameter.kSceneObject,
"ptAttribSceneobjectList": plPythonParameter.kSceneObjectList,
"ptAttribActivator": plPythonParameter.kActivator,
"ptAttribActivatorList": plPythonParameter.kActivator,
"ptAttribNamedActivator": plPythonParameter.kActivator,
"ptAttribResponder": plPythonParameter.kResponder,
"ptAttribResponderList": plPythonParameter.kResponder,
"ptAttribNamedResponder": plPythonParameter.kResponder,
"ptAttribDynamicMap": plPythonParameter.kDynamicText,
"ptAttribGUIDialog": plPythonParameter.kGUIDialog,
"ptAttribExcludeRegion": plPythonParameter.kExcludeRegion,
"ptAttribAnimation": plPythonParameter.kAnimation,
"ptAttribBehavior": plPythonParameter.kBehavior,
"ptAttribMaterial": plPythonParameter.kMaterial,
"ptAttribMaterialList": plPythonParameter.kMaterial,
"ptAttribGUIPopUpMenu": plPythonParameter.kGUIPopUpMenu,
"ptAttribGUISkin": plPythonParameter.kGUISkin,
"ptAttribWaveSet": plPythonParameter.kWaterComponent,
"ptAttribSwimCurrent": plPythonParameter.kSwimCurrentInterface,
"ptAttribClusterList": plPythonParameter.kClusterComponent,
"ptAttribMaterialAnimation": plPythonParameter.kMaterialAnimation,
"ptAttribGrassShader": plPythonParameter.kGrassShaderComponent,
}
_attrib_key_types = {
"ptAttribSceneobject": plFactory.ClassIndex("plSceneObject"),
"ptAttribSceneobjectList": plFactory.ClassIndex("plSceneObject"),
"ptAttribActivator": plFactory.ClassIndex("plLogicModifier"),
"ptAttribActivatorList": plFactory.ClassIndex("plLogicModifier"),
"ptAttribNamedActivator": plFactory.ClassIndex("plLogicModifier"),
"ptAttribResponder": plFactory.ClassIndex("plResponderModifier"),
"ptAttribResponderList": plFactory.ClassIndex("plResponderModifier"),
"ptAttribNamedResponder": plFactory.ClassIndex("plResponderModifier"),
"ptAttribDynamicMap": plFactory.ClassIndex("plDynamicTextMap"),
"ptAttribGUIDialog": plFactory.ClassIndex("pfGUIDialogMod"),
"ptAttribExcludeRegion": plFactory.ClassIndex("plExcludeRegionMod"),
"ptAttribAnimation": plFactory.ClassIndex("plAGMasterMod"),
"ptAttribBehavior": plFactory.ClassIndex("plMultistageBehMod"),
"ptAttribMaterial": plFactory.ClassIndex("plLayer"),
"ptAttribMaterialList": plFactory.ClassIndex("plLayer"),
"ptAttribGUIPopUpMenu": plFactory.ClassIndex("pfGUIPopUpMenu"),
"ptAttribGUISkin": plFactory.ClassIndex("pfGUISkin"),
"ptAttribWaveSet": plFactory.ClassIndex("plWaveSet7"),
"ptAttribSwimCurrent": (plFactory.ClassIndex("plSwimCircularCurrentRegion"),
plFactory.ClassIndex("plSwimStraightCurrentRegion")),
"ptAttribClusterList": plFactory.ClassIndex("plClusterGroup"),
"ptAttribMaterialAnimation": plFactory.ClassIndex("plLayerAnimation"),
"ptAttribGrassShader": plFactory.ClassIndex("plGrassShaderMod"),
}
class PlasmaAttribute(bpy.types.PropertyGroup): class PlasmaAttribute(bpy.types.PropertyGroup):
attribute_id = IntProperty() attribute_id = IntProperty()
attribute_type = StringProperty() attribute_type = StringProperty()
@ -76,12 +119,18 @@ class PlasmaPythonFileNode(PlasmaNodeBase, bpy.types.Node):
bl_label = "Python File" bl_label = "Python File"
bl_width_default = 210 bl_width_default = 210
class _NoUpdate:
def __init__(self, node):
self._node = node
def __enter__(self):
self._node.no_update = True
def __exit__(self, type, value, traceback):
self._node.no_update = False
def _update_pyfile(self, context): def _update_pyfile(self, context):
# Changing the file path? let's start anew. with self._NoUpdate(self) as _hack:
self.attributes.clear() self.attributes.clear()
self.inputs.clear() self.inputs.clear()
# Now populate that BAMF
bpy.ops.node.plasma_attributes_to_node(node_path=self.node_path, python_path=self.filepath) bpy.ops.node.plasma_attributes_to_node(node_path=self.node_path, python_path=self.filepath)
filename = StringProperty(name="File", filename = StringProperty(name="File",
@ -90,7 +139,7 @@ class PlasmaPythonFileNode(PlasmaNodeBase, bpy.types.Node):
options={"HIDDEN"}) options={"HIDDEN"})
attributes = CollectionProperty(type=PlasmaAttribute, options={"HIDDEN"}) attributes = CollectionProperty(type=PlasmaAttribute, options={"HIDDEN"})
dirty_attributes = BoolProperty(options={"HIDDEN"}) no_update = BoolProperty(default=False, options={"HIDDEN", "SKIP_SAVE"})
@property @property
def attribute_map(self): def attribute_map(self):
@ -111,6 +160,41 @@ class PlasmaPythonFileNode(PlasmaNodeBase, bpy.types.Node):
operator.filepath_property = "filepath" operator.filepath_property = "filepath"
operator.filename_property = "filename" operator.filename_property = "filename"
def get_key(self, exporter, so):
return exporter.mgr.find_create_key(plPythonFileMod, name=self.key_name, so=so)
def export(self, exporter, bo, so):
pfm = self.get_key(exporter, so).object
pfm.filename = os.path.splitext(self.filename)[0]
attrib_sockets = (i for i in self.inputs if i.is_linked)
for socket in attrib_sockets:
attrib = socket.attribute_type
from_node = socket.links[0].from_node
param = plPythonParameter()
param.id = socket.attribute_id
param.valueType = _attrib2param[attrib]
if socket.is_simple_value:
param.value = from_node.value
else:
key = from_node.get_key(exporter, so)
if key is None:
msg = "'{}' Node '{}' didn't return a key and therefore will be unavailable to Python".format(
self.id_data.name, from_node.name)
exporter.report.warn(msg, indent=3)
else:
key_type = _attrib_key_types[attrib]
if isinstance(key_type, tuple):
good_key = key.type in key_type
else:
good_key = key.type == key.type
if not good_key:
msg = "'{}' Node '{}' returned an unexpected key type '{}'".format(
self.id_data.name, from_node.name, plFactory.ClassName(key.type))
exporter.report.warn(msg, indent=3)
param.value = key
pfm.addParameter(param)
def _get_attrib_sockets(self, idx): def _get_attrib_sockets(self, idx):
for i in self.inputs: for i in self.inputs:
if i.attribute_id == idx: if i.attribute_id == idx:
@ -124,24 +208,27 @@ class PlasmaPythonFileNode(PlasmaNodeBase, bpy.types.Node):
new_pos = i new_pos = i
break break
old_pos = len(self.inputs) old_pos = len(self.inputs)
socket = self.inputs.new("PlasmaPythonFileNodeSocket", "", "") socket = self.inputs.new("PlasmaPythonFileNodeSocket", attrib.attribute_name)
socket.attribute_id = attrib.attribute_id socket.attribute_id = attrib.attribute_id
if not is_init and new_pos != old_pos: if not is_init and new_pos != old_pos:
self.inputs.move(old_pos, new_pos) self.inputs.move(old_pos, new_pos)
def update(self): def update(self):
if self.no_update:
return
with self._NoUpdate(self) as _no_recurse:
attribs = self.attribute_map attribs = self.attribute_map
empty = not self.inputs empty = not self.inputs
for idx in sorted(attribs): for idx in sorted(attribs):
attrib = attribs[idx] attrib = attribs[idx]
# Delete any attribute sockets whose type changed # Delete any attribute sockets whose type changed
for i in self._get_attrib_sockets(attrib.attribute_id): for i in self._get_attrib_sockets(idx):
if i.attribute_type != attrib.attribute_type: if i.attribute_type != attrib.attribute_type:
self.inputs.remove(i) self.inputs.remove(i)
# Fetch the list of sockets again because we may have nuked some # Fetch the list of sockets again because we may have nuked some
inputs = list(self._get_attrib_sockets(attrib.attribute_id)) inputs = list(self._get_attrib_sockets(idx))
if not inputs: if not inputs:
self._make_attrib_socket(attrib, empty) self._make_attrib_socket(attrib, empty)
elif attrib.attribute_type not in _single_user_attribs: elif attrib.attribute_type not in _single_user_attribs:
@ -217,6 +304,15 @@ class PlasmaAttribNodeBase(PlasmaNodeBase):
return socket.links[0].to_socket return socket.links[0].to_socket
return None return None
@classmethod
def register(cls):
pl_attrib = cls.pl_attrib
if isinstance(pl_attrib, tuple):
color = _attrib_colors.get(pl_attrib, None)
if color is not None:
for i in pl_attrib:
_attrib_colors[i] = color
def update(self): def update(self):
pl_id = self.pl_attrib pl_id = self.pl_attrib
socket = self.outputs[0] socket = self.outputs[0]
@ -295,6 +391,54 @@ class PlasmaAttribNumericNode(PlasmaAttribNodeBase, bpy.types.Node):
self.value = attrib.simple_value self.value = attrib.simple_value
self.inited = True self.inited = True
@property
def value(self):
attrib = self.to_socket
if attrib is None or attrib.attribute_type == "ptAttribInt":
return self.value_int
else:
return self.value_float
class PlasmaAttribObjectNode(PlasmaAttribNodeBase, bpy.types.Node):
bl_category = "PYTHON"
bl_idname = "PlasmaAttribObjectNode"
bl_label = "Object Attribute"
pl_attrib = ("ptAttribSceneobject", "ptAttribSceneobjectList", "ptAttribAnimation")
object_name = StringProperty(name="Object",
description="Object containing the required data")
def init(self, context):
super().init(context)
# keep the code simple
self.outputs[0].link_limit = 1
def draw_buttons(self, context, layout):
layout.prop_search(self, "object_name", bpy.data, "objects", text=self.attribute_name)
def get_key(self, exporter, so):
attrib = self.to_socket
if attrib is None:
self.raise_error("must be connected to a Python File node!")
attrib = attrib.attribute_type
bo = bpy.objects.data.get(self.object_name, None)
if bo is None:
self.raise_error("invalid object specified: '{}'".format(self.object_name))
ref_so_key = exporter.mgr.find_create_key(plSceneObject, bl=bo)
ref_so = ref_so_key.object
# Add your attribute type handling here...
if attrib in {"ptAttribSceneobject", "ptAttribSceneobjectList"}:
return ref_so_key
elif attrib == "ptAttribAnimation":
anim = bo.plasma_modifiers.animation
agmod = exporter.mgr.find_create_key(plAGModifier, so=ref_so, name=anim.display_name)
agmaster = exporter.mgr.find_create_key(plAGMasterModifier, so=ref_so, name=anim.display_name)
return agmaster
class PlasmaAttribStringNode(PlasmaAttribNodeBase, bpy.types.Node): class PlasmaAttribStringNode(PlasmaAttribNodeBase, bpy.types.Node):
bl_category = "PYTHON" bl_category = "PYTHON"
@ -312,3 +456,68 @@ class PlasmaAttribStringNode(PlasmaAttribNodeBase, bpy.types.Node):
attrib = self.to_socket attrib = self.to_socket
if attrib is not None: if attrib is not None:
self.value = attrib.simple_value self.value = attrib.simple_value
class PlasmaAttribTextureNode(PlasmaAttribNodeBase, bpy.types.Node):
bl_category = "PYTHON"
bl_idname = "PlasmaAttribTextureNode"
bl_label = "Texture Attribute"
bl_width_default = 175
pl_attrib = ("ptAttribMaterial", "ptAttribMaterialList",
"ptAttribDynamicMap", "ptAttribMaterialAnimation")
material_name = StringProperty(name="Material")
texture_name = StringProperty(name="Texture")
def init(self, context):
super().init(context)
# keep the code simple
self.outputs[0].link_limit = 1
def draw_buttons(self, context, layout):
layout.prop_search(self, "material_name", bpy.data, "materials")
material = bpy.data.materials.get(self.material_name, None)
if material is not None:
layout.prop_search(self, "texture_name", material, "texture_slots")
def get_key(self, exporter, so):
material = bpy.data.materials.get(self.material_name, None)
if material is None:
self.raise_error("invalid Material '{}'".format(self.material_name))
tex_slot = material.texture_slots.get(self.texture_name, None)
if tex_slot is None:
self.raise_error("invalid Texture '{}'".format(self.texture_name))
attrib = self.attribute_type
# Helpers
texture = tex_slot.texture
is_animated = ((material.animation_data is not None and material.animation_data.action is not None)
or (texture.animation_data is not None and texture.animation_data.action is not None))
is_dyntext = texture.type == "IMAGE" and texture.image is None
# Your attribute stuff here...
if attrib == "ptAttribDynamicMap":
if not is_dyntext:
self.raise_error("Texture '{}' is not a Dynamic Text Map".format(self.texture_name))
name = "{}_{}_DynText".format(self.material_name, self.texture_name)
return exporter.mgr.find_create_key(plDynamicTextMap, name=name, so=so)
elif is_animated:
name = "{}_{}_LayerAnim".format(self.material_name, self.texture_name)
return exporter.mgr.find_create_key(plLayerAnimation, name=name, so=so)
else:
name = "{}_{}".format(self.material_name, self.texture_name)
return exporter.mgr.find_create_key(plLayer, name=name, so=so)
_attrib_colors = {
"ptAttribActivator": (0.031, 0.110, 0.290, 1.0),
"ptAttribActivatorList": (0.451, 0.0, 0.263, 1.0),
"ptAttribBoolean": (0.71, 0.706, 0.655, 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),
PlasmaAttribNumericNode.pl_attrib: (0.443, 0.439, 0.392, 1.0),
PlasmaAttribObjectNode.pl_attrib: (0.565, 0.267, 0.0, 1.0),
PlasmaAttribTextureNode.pl_attrib: (0.035, 0.353, 0.0, 1.0),
}

2
korman/operators/op_nodes.py

@ -81,7 +81,5 @@ class PlPyAttributeNodeOperator(NodeOperator, bpy.types.Operator):
default = attrib.get("default", None) default = attrib.get("default", None)
if default is not None and cached.is_simple_value: if default is not None and cached.is_simple_value:
cached.simple_value = default cached.simple_value = default
# Manually cause the node to update its inputs
node.update() node.update()
return {"FINISHED"} return {"FINISHED"}

Loading…
Cancel
Save