2013-03-18 16:34:57 +00:00
|
|
|
import bpy
|
2023-08-30 12:37:21 +02:00
|
|
|
from bpy.types import NodeTree, Node, NodeSocket, NodeTreeInterfaceSocket
|
2024-12-17 15:59:06 +01:00
|
|
|
from bl_ui import node_add_menu
|
2013-03-18 16:34:57 +00:00
|
|
|
|
|
|
|
# Implementation of custom nodes from Python
|
|
|
|
|
|
|
|
|
|
|
|
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
|
2013-06-09 09:42:34 +00:00
|
|
|
class MyCustomTree(NodeTree):
|
2013-03-18 16:34:57 +00:00
|
|
|
# Description string
|
2019-06-28 09:53:39 -03:00
|
|
|
'''A custom node tree type that will show up in the editor type list'''
|
2013-03-18 16:34:57 +00:00
|
|
|
# Optional identifier string. If not explicitly defined, the python class name is used.
|
|
|
|
bl_idname = 'CustomTreeType'
|
|
|
|
# Label for nice name display
|
2017-09-07 05:18:44 +10:00
|
|
|
bl_label = "Custom Node Tree"
|
2013-03-18 16:34:57 +00:00
|
|
|
# Icon identifier
|
|
|
|
bl_icon = 'NODETREE'
|
|
|
|
|
|
|
|
|
|
|
|
# Custom socket type
|
2013-06-09 09:42:34 +00:00
|
|
|
class MyCustomSocket(NodeSocket):
|
2013-03-18 16:34:57 +00:00
|
|
|
# Description string
|
2023-04-18 10:42:00 +10:00
|
|
|
"""Custom node socket type"""
|
2013-03-18 16:34:57 +00:00
|
|
|
# Optional identifier string. If not explicitly defined, the python class name is used.
|
|
|
|
bl_idname = 'CustomSocketType'
|
|
|
|
# Label for nice name display
|
2017-09-07 05:18:44 +10:00
|
|
|
bl_label = "Custom Node Socket"
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
input_value: bpy.props.FloatProperty(
|
|
|
|
name="Value",
|
|
|
|
description="Value when the socket is not connected",
|
2018-07-11 22:18:09 +02:00
|
|
|
)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
|
|
|
# Optional function for drawing the socket input value
|
2013-05-08 16:17:04 +00:00
|
|
|
def draw(self, context, layout, node, text):
|
2013-10-10 12:58:35 +00:00
|
|
|
if self.is_output or self.is_linked:
|
2018-11-14 18:19:41 +01:00
|
|
|
layout.label(text=text)
|
2013-05-08 16:17:04 +00:00
|
|
|
else:
|
2023-08-30 12:37:21 +02:00
|
|
|
layout.prop(self, "input_value", text=text)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2013-03-22 13:17:16 +00:00
|
|
|
# Socket color
|
2023-08-30 12:37:21 +02:00
|
|
|
@classmethod
|
|
|
|
def draw_color_simple(cls):
|
2013-03-22 13:17:16 +00:00
|
|
|
return (1.0, 0.4, 0.216, 0.5)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2014-04-15 13:11:48 +10:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
# Customizable interface properties to generate a socket from.
|
|
|
|
class MyCustomInterfaceSocket(NodeTreeInterfaceSocket):
|
|
|
|
# The type of socket that is generated.
|
|
|
|
bl_socket_idname = 'CustomSocketType'
|
|
|
|
|
|
|
|
default_value: bpy.props.FloatProperty(default=1.0, description="Default input value for new sockets",)
|
|
|
|
|
|
|
|
def draw(self, context, layout):
|
|
|
|
# Display properties of the interface.
|
|
|
|
layout.prop(self, "default_value")
|
|
|
|
|
|
|
|
# Set properties of newly created sockets
|
|
|
|
def init_socket(self, node, socket, data_path):
|
|
|
|
socket.input_value = self.default_value
|
|
|
|
|
|
|
|
# Use an existing socket to initialize the group interface
|
|
|
|
def from_socket(self, node, socket):
|
|
|
|
# Current value of the socket becomes the default
|
|
|
|
self.default_value = socket.input_value
|
|
|
|
|
|
|
|
|
2013-06-09 09:42:34 +00:00
|
|
|
# Mix-in class for all custom nodes in this tree type.
|
2013-03-18 16:34:57 +00:00
|
|
|
# Defines a poll function to enable instantiation.
|
2014-04-15 13:11:48 +10:00
|
|
|
class MyCustomTreeNode:
|
2013-03-18 16:34:57 +00:00
|
|
|
@classmethod
|
|
|
|
def poll(cls, ntree):
|
|
|
|
return ntree.bl_idname == 'CustomTreeType'
|
|
|
|
|
2014-04-15 13:11:48 +10:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
# Derived from the Node base type.
|
2022-09-29 11:59:02 -05:00
|
|
|
class MyCustomNode(MyCustomTreeNode, Node):
|
2013-03-18 16:34:57 +00:00
|
|
|
# === Basics ===
|
|
|
|
# Description string
|
|
|
|
'''A custom node'''
|
|
|
|
# Optional identifier string. If not explicitly defined, the python class name is used.
|
|
|
|
bl_idname = 'CustomNodeType'
|
|
|
|
# Label for nice name display
|
2017-09-07 05:18:44 +10:00
|
|
|
bl_label = "Custom Node"
|
2013-03-18 16:34:57 +00:00
|
|
|
# Icon identifier
|
|
|
|
bl_icon = 'SOUND'
|
|
|
|
|
|
|
|
# === Custom Properties ===
|
|
|
|
# These work just like custom properties in ID data blocks
|
|
|
|
# Extensive information can be found under
|
2023-07-03 12:21:06 +02:00
|
|
|
# https://docs.blender.org/api/current/bpy.props.html
|
2018-07-11 22:18:09 +02:00
|
|
|
my_string_prop: bpy.props.StringProperty()
|
|
|
|
my_float_prop: bpy.props.FloatProperty(default=3.1415926)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
|
|
|
# === 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):
|
|
|
|
self.inputs.new('CustomSocketType', "Hello")
|
|
|
|
self.inputs.new('NodeSocketFloat', "World")
|
2024-02-12 20:28:56 +01:00
|
|
|
self.inputs.new('NodeSocketVector', "!", use_multi_input=True)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
|
|
|
self.outputs.new('NodeSocketColor', "How")
|
|
|
|
self.outputs.new('NodeSocketColor', "are")
|
|
|
|
self.outputs.new('NodeSocketFloat', "you")
|
|
|
|
|
|
|
|
# Copy function to initialize a copied node from an existing one.
|
|
|
|
def copy(self, node):
|
|
|
|
print("Copying from node ", node)
|
|
|
|
|
|
|
|
# Free function to clean up on removal.
|
|
|
|
def free(self):
|
|
|
|
print("Removing node ", self, ", Goodbye!")
|
|
|
|
|
|
|
|
# Additional buttons displayed on the node.
|
|
|
|
def draw_buttons(self, context, layout):
|
2018-08-28 12:34:51 +10:00
|
|
|
layout.label(text="Node settings")
|
2017-09-07 05:18:44 +10:00
|
|
|
layout.prop(self, "my_float_prop")
|
2013-03-18 16:34:57 +00:00
|
|
|
|
|
|
|
# Detail buttons in the sidebar.
|
|
|
|
# If this function is not defined, the draw_buttons function is used instead
|
|
|
|
def draw_buttons_ext(self, context, layout):
|
2017-09-07 05:18:44 +10:00
|
|
|
layout.prop(self, "my_float_prop")
|
|
|
|
# my_string_prop button will only be visible in the sidebar
|
|
|
|
layout.prop(self, "my_string_prop")
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2013-11-12 18:18:06 +00:00
|
|
|
# Optional: custom label
|
|
|
|
# Explicit user label overrides this, but here we can define a label dynamically
|
|
|
|
def draw_label(self):
|
|
|
|
return "I am a custom node"
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2024-12-17 15:59:06 +01:00
|
|
|
# Add custom nodes to the Add menu.
|
|
|
|
def draw_add_menu(self, context):
|
|
|
|
layout = self.layout
|
|
|
|
if context.space_data.tree_type != MyCustomTree.bl_idname:
|
|
|
|
# Avoid adding nodes to built-in node tree
|
|
|
|
return
|
|
|
|
# Add nodes to the layout. Can use submenus, separators, etc. as in any other menu.
|
|
|
|
node_add_menu.add_node_type(layout, "CustomNodeType")
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2017-09-07 05:18:44 +10:00
|
|
|
|
|
|
|
classes = (
|
|
|
|
MyCustomTree,
|
|
|
|
MyCustomSocket,
|
2023-08-30 12:37:21 +02:00
|
|
|
MyCustomInterfaceSocket,
|
2017-09-07 05:18:44 +10:00
|
|
|
MyCustomNode,
|
|
|
|
)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2018-06-26 19:41:37 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
def register():
|
2017-09-07 05:18:44 +10:00
|
|
|
from bpy.utils import register_class
|
|
|
|
for cls in classes:
|
|
|
|
register_class(cls)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2024-12-17 15:59:06 +01:00
|
|
|
bpy.types.NODE_MT_add.append(draw_add_menu)
|
2013-06-09 08:46:48 +00:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
|
|
|
|
def unregister():
|
2024-12-17 15:59:06 +01:00
|
|
|
bpy.types.NODE_MT_add.remove(draw_add_menu)
|
2013-06-09 08:46:48 +00:00
|
|
|
|
2017-09-07 05:18:44 +10:00
|
|
|
from bpy.utils import unregister_class
|
|
|
|
for cls in reversed(classes):
|
|
|
|
unregister_class(cls)
|
2013-03-18 16:34:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
register()
|