I've tried to figure this out for quite some time, with no luck. If anyone can help, that would be awesome! I'm essentially trying to convert the X and Y outputs of the Separate XYZ node into a radius value using the Pythagorean Theorem. Seems like it should be easy, but I don't think I'm getting the proper values from the Inputs, nor am I setting the proper value in the outputs.
When getting the values of the inputs using default_value
, they always come up 0.0 for both x and y. When setting the output, I get the error:
ValueError: bpy_struct: item.attr = val: sequence expected at dimension 1, not 'float'
I based my code off of the custom_nodes.py template and from a tutorial I found online. I have no idea how correct it is and there seems to be a dearth of other samples out there for me to compare against. Any help would be much appreciated! ^_^
# Implementation of custom nodes from Python
class MyCustomTree(NodeTree):
'''some stuff'''
bl_idname = 'CustomTreeType'
bl_label = 'Custom Node Tree'
# Mix-in class for all custom nodes in this tree type.
# Defines a poll function to enable instantiation.
class MyCustomTreeNode:
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'ShaderNodeTree'
# Derived from the Node base type.
class XYToRadiusNode(Node, MyCustomTreeNode):
# === Basics ===
# Description string
'''XY coordinates to radius'''
# Optional identifier string. If not explicitly defined, the python class name is used.
bl_idname = 'XYToRadius'
# Label for nice name display
bl_label = 'XY To Radius'
def update_value(self, context):
print("updatevalue");
inX = self.inputs["X"]
inY = self.inputs["Y"]
if inX.is_linked and inX.links[0].is_valid and inY.is_linked and inY.links[0].is_valid:
x_val = inX.links[0].from_socket.default_value
y_val = inY.links[0].from_socket.default_value
self.radius = math.sqrt(pow(x_val, 2.) + pow(y_val, 2.))
self.outputs["Radius"].default_value = self.radius
else:
self.outputs["Radius"].default_value = 0.0
self.radius = 0.0
radius = 0.0
# === Optional Functions ===
# Initialization function, called when a new node is created.
# This is the most common place to create the sockets for a node, as shown below.
# NOTE: this is not the same as the standard __init__ function in Python, which is
# a purely internal Python method and unknown to the node system!
def init(self, context):
print("init")
self.inputs.new('NodeSocketFloat', "X")
self.inputs.new('NodeSocketFloat', "Y")
self.outputs.new('NodeSocketFloat', "Radius")
# Additional buttons displayed on the node.
def draw_buttons(self, context, layout):
return
# Optional: custom label
# Explicit user label overrides this, but here we can define a label dynamically
def draw_label(self):
return "XY to Radius"
def update(self):
print("update")
try:
out = self.outputs["Radius"]
inY = self.inputs["Y"]
inX = self.inputs["X"]
can_continue = True
except:
can_continue = False
if can_continue:
if out.is_linked and inX.is_linked and inY.is_linked:
for o in out.links:
if o.is_valid:
self.update_value(None)
print(self.radius)
o.to_socket.default_value = self.outputs["Radius"].default_value
### Node Categories ###
# Node categories are a python system for automatically
# extending the Add menu, toolbar panels and search operator.
# For more examples see release/scripts/startup/nodeitems_builtins.py
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem
# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type
class MyNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'ShaderNodeTree'
# all categories in a list
node_categories = [
# identifier, label, items list
MyNodeCategory("MY_NODES", "My Nodes", items=[
# our basic node
NodeItem("XYToRadius"),
])]
def register():
bpy.utils.register_class(XYToRadiusNode)
nodeitems_utils.register_node_categories("MY_NODES", node_categories)
def unregister():
nodeitems_utils.unregister_node_categories("MY_NODES")
bpy.utils.unregister_class(XYToRadiusNode)
def pre_frame_change(scene):
if scene.render.engine == 'CYCLES':
for m in bpy.data.materials:
if m.node_tree != None:
for n in m.node_tree.nodes:
if n.bl_idname == 'XYToRadius':
print(n.bl_idname)
v = n.radius
n.radius = v
if __name__ == "__main__":
register()
bpy.app.handlers.frame_change_pre.append(pre_frame_change)