Note: This is an archived version of the Blender Developer Wiki (archived 2024). The current developer documentation is available on developer.blender.org/docs.

User:OmarSquircleArt/GSoC2019/Documentation/Adding A New Shading Node

Adding A New Shading Node

This guide covers the necessary steps to add a new shading node to Blender. This guide shall not cover the details of the SVM, OSL, or GPU backends, please read the guides on SVM, OSL, and GPU for more details. We will start by providing a summary then we will cover each of the steps in details. Check the code of existing nodes as a reference to get the most out of this guide.

  • Add a new node type ID SH_NODE_* in BKE_node.h.
  • If needed, create a NodeShader* struct in DNA_node_types.h. If needed, also create definitions and enums for the node properties.
  • If the node have properties, add a def_sh_* function in rna_nodetree.c.
  • Add a case for the node in NOD_static_types.h.
  • If needed, add a case for the node in the node_shader_set_butfunc function in drawnode.c.
  • Add a new function register_node_type_sh_* in a new file node_shader_*.c in source/blender/nodes/shader/nodes/.
  • Add the node_shader_*.c file to the source list in source/blender/nodes/CMakeLists.txt.
  • Add the register_node_type_sh_* function prototype to NOD_shader.h.
  • Call the register_node_type_sh_* function in the registerShaderNodes function in source/blender/blenkernel/intern/node.c.
  • Create a Cycles class for the node in intern/cycles/render/nodes.h.
  • Instantiate the Cycles class in the add_node function in intern/cycles/blender/blender_shader.cpp.
  • Define the node in intern/cycles/render/nodes.cpp.
  • Add the node to the add menus in nodeitems_builtins.py.

Defining A New Node Type ID

Start by defining a new node type ID SH_NODE_* for the node in BKE_node.h, making sure not to change other definitions. This node type ID will be used to identify the node in the code base.

Creating A DNA Struct

The bNode struct provides two shorts (custom1 and custom2) and two floats (custom3 and custom4) to be used for storing node properties. So most nodes don't need a dedicated DNA struct. However, if needed, a new DNA struct can be defined in DNA_node_types.h to be used as a storage for the node properties, we call such node a node with custom storage. This is also where you create definitions and enums for your properties if needed.

Creating RNA Properties

If the node have properties, create a def_sh_* RNA definition function in rna_nodetree.c. Nodes with custom storage should call the RNA_def_struct_sdna_from function to create the struct from the the node storage.

void RNA_def_struct_sdna_from(StructRNA *srna, const char *structname, const char *propname);

Nodes that have an update callback for updating sockets should use the rna_ShaderNode_socket_update update function.

Static Types

Add a case/entry for the node in NOD_static_types.h using the DefNode macro. The entries will be used to associate the node type ID SH_NODE_* with the RNA struct type and the bNodeType struct.

The DefNode macro have the following signature:

DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc)
  • Categroy - The node tree type this node is used in. It can be ShaderNode, CompositorNode, TextureNode, or Node.
  • ID - The node type ID SH_NODE_*.
  • Definition Function - The RNA definition function def_sh_* we just created in rna_nodetree.c. This should be 0 if no definition function exist.
  • Enum Name - A unique name used in the API.
  • Struct Name - The name of the RNA struct. The final name will be prefixed with the aformentioned category.
  • UI Name - The name used in the UI.
  • UI Description - The node description.

Draw Function

If the node have properties, add a drawing function node_buts_* in source/blender/editors/space_node/drawnode.c and add a case for it in the node_shader_set_butfunc function.

Creating And Registering A New Node Type

A node is defined by a bNodeType struct, where it stores the node attributes and callback functions implementing the node behavior. Create a file node_shader_*.c in source/blender/nodes/shader/nodes/ and add it to the source list in source/blender/nodes/CMakeLists.txt. In this file, create a function register_node_type_sh_* where we will create the bNodeType struct, initialize it, and register it.

void register_node_type_sh_*(void)
{
  static bNodeType ntype;

  // Initialization ...

  nodeRegisterType(&ntype);
}

Initialization

Node Type Base

We usually start by calling the sh_node_type_base function to set some common defaults for shading nodes.

void sh_node_type_base(
  struct bNodeType *ntype, int type, const char *name, short nclass, short flag);

It is also used to initialize the node with the following basic attributes:

  • Type - The node type ID we just created SH_NODE_*.
  • Name - The node name.
  • Class - The node class NODE_CLASS_*, which classify the node for the add menu and for UI theming. See BKE_node.h for the available classes.
  • Flag - The node flag. See DNA_node_types.h for the available flags.

Inputs And Outputs

To define the input and output sockets of the node, we call the node_type_socket_templates function.

void node_type_socket_templates(struct bNodeType *ntype,
                                struct bNodeSocketTemplate *inputs,
                                struct bNodeSocketTemplate *outputs);

This function takes two arrays of bNodeSocketTemplate struct, one representing the inputs and the other representing the outputs. Both arrays should always end with the terminating struct {-1, 0, ""} to mark the end of the array.

typedef struct bNodeSocketTemplate {
  int type, limit;
  char name[64];
  float val1, val2, val3, val4;
  float min, max;
  int subtype;
  int flag;
  ...
} bNodeSocketTemplate;

The bNodeSocketTemplate struct defines:

  • Type - The socket type. For instance SOCK_FLOAT or SOCK_VECTOR. See the eNodeSocketDatatype enum in DNA_node_types.h for the available types.
  • Limit - A limit to the number of links that can be connected to the socket. This is typically 1 for inputs and 0 for outputs, where 0 means any number of links.
  • Name - The name of the socket. This name should be enclosed in the translation-marker macro N_(msgid).
  • Default - The default value of the socket. The default value is defined by four floats, some of which may be ignored depending on the type. For instance, for a float socket, only the first float val1 is used and the last three are ignored, and for a vector socket, only the first three floats val1, val2, and val3 are used and the last one is ignored.
  • Min/Max - The soft minimum and maximum values of the socket.
  • Subtype - The socket property subtype. For instance, PROP_EULER to mark the vector socket as an euler. See the PropertySubType enum in RNA_types.h for the available subtypes.
  • Flag - The socket flag SOCK_*. For instance, SOCK_HIDE_VALUE to hide the socket value if it gets an auto default, like the normal inputs of the BSDF nodes. See the eNodeSocketFlag enum in DNA_node_types.h for the available flags.

Node Initialization

Nodes that have custom storage should call the function node_type_init to set an initfunc for the node.

void node_type_init(struct bNodeType *ntype,
                    void (*initfunc)(struct bNodeTree *ntree, struct bNode *node));

The initfunc, in most cases, just dynamically allocate the DNA struct of the node, set some defaults, and set the DNA struct to the storage attribute of the bNode. For instance:

static void node_shader_init_example(bNodeTree *UNUSED(ntree), bNode *node)
{
  NodeShaderExample *attr = MEM_callocN(sizeof(NodeShaderExample), "NodeShaderExample");
  node->storage = attr;
}

Node Storage

If your node have properties, then you have to call the node_type_storage function.

void node_type_storage(struct bNodeType *ntype,
                       const char *storagename,
                       void (*freefunc)(struct bNode *node),
                       void (*copyfunc)(struct bNodeTree *dest_ntree,
                                        struct bNode *dest_node,
                                        const struct bNode *src_node));

The functions sets the following:

  • Storage Name - The name of the DNA structure used for storage. For nodes that don't use custom storage, this should be an empty string.
  • Free Function - A function that frees the allocated data in the init function, typically the DNA struct. For nodes that don't use custom storage, this should be NULL. In most cases, the node_free_standard_storage function can be supplied, which just calls MEM_freeN on the node storage.
  • Copy Function - A function that copies the storage from a node to another. For nodes that don't use custom storage, this should be NULL. In most cases, the node_copy_standard_storage function can be supplied, which just calls MEM_dupallocN on the source node storage and set the new struct to the destination node storage.

Update Callback

A node can have an update function to be called whenever an update is required. This is typically used to make some sockets available/unavailable based on some node property. If needed, call the node_type_update to set the update function.

void node_type_update(struct bNodeType *ntype,
                      void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node));

GPU Execution

The node_type_gpu function should be called to set the GPU execution function for OpenGL/EEVEE display. The details of the GPU execution function is available in the GPU guide.

void node_type_gpu(struct bNodeType *ntype, NodeGPUExecFunction gpufunc);

Other Attributes And Callbacks

There are numerous other less commonly used attributes and callbacks you can set. For instance node_type_label to dynamically set node labels based on node properties or node_type_size to set the size attributes of the node.

void node_type_size(struct bNodeType *ntype, int width, int minwidth, int maxwidth);

void node_type_label(
    struct bNodeType *ntype,
    void (*labelfunc)(struct bNodeTree *ntree, struct bNode *, char *label, int maxlen));

Registering The Node Type

To actually register the node type, first, add the function prototype to NOD_shader.h. Then add a call to the register_node_type_sh_* function in the registerShaderNodes function in source/blender/blenkernel/intern/node.c.

Add Node To Menu

Add the node to a menu in nodeitems_builtins.py.

Cycles Class

A node in cycles is represented by a class that inherit from the base class ShaderNode. The SHADER_NODE_CLASS macro can be used to quickly populate the class with the essential virtual functions, namely the clone and SVM and OSL compile functions. The class should have a public variable declaration for every input and property in the node.

class ExampleNode : public ShaderNode {
 public:
  SHADER_NODE_CLASS(ExampleNode)

  // Inputs and properties declarations.
};

A more detailed description of the class is be provided in the TODO guide.

Cycles Class Instantiation

Add a new case for the node in the add_node function in intern/cycles/blender/blender_shader.cpp. In this case, instantiate a new instance of the node class and set the required class variables using the functions provided by the Blender class.

Cycles Node Definition

TODO