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_*
inBKE_node.h
. - If needed, create a
NodeShader*
struct inDNA_node_types.h
. If needed, also create definitions and enums for the node properties. - If the node have properties, add a
def_sh_*
function inrna_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 indrawnode.c
. - Add a new function
register_node_type_sh_*
in a new filenode_shader_*.c
insource/blender/nodes/shader/nodes/
. - Add the
node_shader_*.c
file to the source list insource/blender/nodes/CMakeLists.txt
. - Add the
register_node_type_sh_*
function prototype toNOD_shader.h
. - Call the
register_node_type_sh_*
function in theregisterShaderNodes
function insource/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 inintern/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
, orNode
. - ID - The node type ID
SH_NODE_*
. - Definition Function - The RNA definition function
def_sh_*
we just created inrna_nodetree.c
. This should be0
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. SeeBKE_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
orSOCK_VECTOR
. See theeNodeSocketDatatype
enum inDNA_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 and0
for outputs, where0
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 floatsval1
,val2
, andval3
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 thePropertySubType
enum inRNA_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 theeNodeSocketFlag
enum inDNA_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, thenode_free_standard_storage
function can be supplied, which just callsMEM_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, thenode_copy_standard_storage
function can be supplied, which just callsMEM_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