Created
July 17, 2018 21:22
-
-
Save AzureDVBB/d3c2ad0b46ece40d180760f17ad94973 to your computer and use it in GitHub Desktop.
A simple node tree addon template for blender.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# the little less documented way of adding a custom node tree | |
# and populate it with nodes of varying types of I/O | |
# sockets that work together, discombobulated | |
# first we import the blender API | |
import bpy | |
# then we create the UI space, the node tree as it is called | |
# but in actualy fact this is similar to a UI panel/menu | |
# and mostly handled in the background in terms of creation | |
# so let's define out custom node tree, inheriting from its base type | |
class CustomNodeTree(bpy.types.NodeTree): | |
# the docstring here is used to generate documentation but | |
# also used to display a description to the user | |
'''A custom node tree type''' | |
# then we can give it a custom id to access it, if not given | |
# it will use the classname by default | |
bl_idname='CustomNodeTree' | |
# the label is the name that will be displayed to the user | |
bl_label='Custom Node Tree' | |
# the icon that will be displayed in the UI | |
# NOTE: check the blender dev plugins to see icons in text editor | |
bl_icon='BLENDER' | |
# that is all we needed to make a nodetree | |
# first for convenience we make a class from which all our nodes | |
# will inherit from, saving us some typing | |
class CustomNode(bpy.types.Node): | |
# this line makes the node visible only to the 'CustomNodeTree' | |
# node tree, essentially checking context | |
@classmethod | |
def poll(cls, ntree): | |
return ntree.bl_idname == 'CustomNodeTree' | |
# now we start making a simple input node that uses builtin | |
# blender properties, this is the simplest node | |
# we define the node class that inherits from its mixin class | |
class CustomSimpleInputNode(CustomNode): | |
# we can add a docstring that will be interpreted as description | |
'''A simple input node''' | |
# optionally we define an id that we can reference the node by | |
bl_idname = 'CustomSimpleInputNode' | |
# we add a label that the node will show the user as its name | |
bl_label = 'Simple Input Node' | |
# we can also add an icon to it | |
bl_icon = 'PLUS' | |
# we can add properties here that the node uses locally | |
# NOTE: does not get drawn automatically | |
intProp = bpy.props.IntProperty() | |
# init function that is automagickally is called when the | |
# node is instantiated into the treem setup sockets here | |
# for both inputs and outputs | |
def init(self, context): | |
# makes a new output socket of type 'NodeSocketInt' with the | |
# label 'output' on it | |
# NOTE: no elements will be drawn for output sockets | |
self.outputs.new('NodeSocketInt', "output") | |
# copy function is ran to initialize a copied node from | |
# an existing one | |
def copy(self, node): | |
print("copied node", node) | |
# free function is called when an existing node is deleted | |
def free(self): | |
print("Node removed", self) | |
# draw method for drawing node UI just like any other | |
# NOTE: input sockets are drawn by their respective methods | |
# but output ones DO NOT for some reason, do it manually | |
# and connect the drawn value to the output socket | |
def draw_buttons(self, context, layout): | |
# create a slider for int values | |
layout.prop(self, 'intProp') | |
# this method lets you design how the node properties | |
# are drawn on the side panel (to the right) | |
# if it is not defined, draw_buttons will be used instead | |
#def draw_buttons_ext(self, context, layout): | |
#OPTIONAL | |
#we can use this function to dynamically define the label of | |
# the node, however defining the bl_label explicitly overrides it | |
#def draw_label(self): | |
# return "this label is shown" | |
# now to be able to see the nodes in the add node menu AND see it on the | |
# tool shelf (left panel) we need to define node categories and then | |
# register them, the rest is handled automagickally by blender | |
# so first we import the utilities used for handling node categories | |
import nodeitems_utils | |
# now we can make our own category using the NodeCategory baseclass | |
# as before we first make a mixin class to save space | |
class CustomNodeCategory(nodeitems_utils.NodeCategory): | |
# define the classmethod that tells blender which node tree | |
# the categories made with this class belong to (is visible to) | |
@classmethod | |
def poll(cls, context): | |
return context.space_data.tree_type == 'CustomNodeTree' | |
# make a list of node categories for registration | |
node_categories = [ | |
# NOTE: did not find documentation other then template script for it | |
# esentially: | |
# we instantiate a new 'nodeitems_utils.NodeCategory' class, that | |
# has been extended with a poll method that makes sure that the | |
# category and node only shows up in the desired node tree | |
# The first argument is a string with its id we will use to access it by | |
# the second argument is the name displayed to the user | |
# the third argument is a list of (items) nodes that are under | |
# that category, the list contains instances 'nodeitems_utils.NodeItem' | |
CustomNodeCategory("CUSTOMINPUTNODES", "Custom Input Nodes", items=[ | |
# the nodes (items) in this category are instantiated in this list | |
# with the 'nodeitems_utils.NodeItem' class, which can have | |
# additional settings | |
# the first argument is the node class idname we want to add | |
# then there can be keyword arguments like label | |
# another argument can be a 'settings' keyword argument | |
# that takes a dictionary that can override default values of all | |
# properties | |
# NOTE: use 'repr()' to convert the value to string IMPORTANT | |
nodeitems_utils.NodeItem("CustomSimpleInputNode", | |
label="Simple Input", settings={"intProp":repr(1.0)}), | |
# minimalistic node addition is like this | |
nodeitems_utils.NodeItem("CustomSimpleInputNode"), | |
]), | |
] | |
#finally we register our classes so we can install as plugin | |
#to that end we create a list of classes to be loaded and unloaded | |
classes=( | |
CustomNodeTree, | |
CustomSimpleInputNode, | |
) | |
# for loading we define the registering of all defined classes | |
def register(): | |
# we register all our classes into blender | |
for cls in classes: | |
bpy.utils.register_class(cls) | |
# we register the node categories with the node tree | |
# the first argument is a string that is the idname for this collection | |
# of categories | |
# the second is the actual list of node categories to be registered under | |
# this name | |
nodeitems_utils.register_node_categories("CUSTOM_NODES", node_categories) | |
# for unloading we define the unregistering of all defined classes | |
def unregister(): | |
# we unregister our node categories first | |
nodeitems_utils.unregister_node_categories("CUSTOM_NODES") | |
# then we unregister all classes from the blender | |
for cls in classes: | |
bpy.utils.unregister_class(cls) | |
# finally we make it runnable in the text editor for quick testing | |
if __name__=='__main__': | |
# during test running there is a bug where registering or adding | |
# references to certain groups and categories cannot override | |
# existing ones, here is a workaround | |
# we enter a try..finally block | |
try: | |
# first try unregistering the existing category | |
# WARNING: ALWAYS triple check that you unload the correct | |
# things in this block, as it will not raise errors | |
nodeitems_utils.unregister_node_categories("CUSTOM_NODES") | |
finally: | |
# finally, wether it suceeded or not, we can now register it again | |
# this essentially reloads existing/register changes made | |
# and thanks to the nature of the finally block, is always run | |
register() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey thanks for the code!
I had an issue with intProp, due to error :
Fixed by change:
Maybe it will help someone...