From BlenderWiki

Jump to: navigation, search
Note: This is an archived version of the Blender Developer Wiki. The current and active wiki is available on wiki.blender.org.

New Compositor HelloWorld Example

Sit back, buckle up, and get ready to edit a hundred files before you see your new HelloWorld node!

TODO: Must touch COM_Converter.cpp in order to register the node too. *sigh*.

Register the Unique Node ID Value in source/blender/blenkernel/BKE_node.h

Example:

[...]
 #define CMP_NODE_COLOR_MATTE 259
 #define CMP_NODE_COLORBALANCE 260
 #define CMP_NODE_HUECORRECT 261
 #define CMP_NODE_HELLOWORLD 262 /* Add a Unique ID for our new node. */
 
 #define CMP_NODE_GLARE		301
 #define CMP_NODE_TONEMAP	302
[...]

Register the new node in source/blender/blenkernel/intern/node.c

Example:

[...]
	register_node_type_cmp_bokehblur(ttype);
	register_node_type_cmp_switch(ttype);
 
	register_node_type_cmp_mask(ttype);
	register_node_type_cmp_trackpos(ttype);
        register_node_type_cmp_helloworld(ttype);
[...]

Add the DNA that describes the data in source/blender/makesdna/DNA_node_types.h

WARNING
All DNA must be padded. This means that your data must be padded to an alignment that matches the data length. For more information, see the DNA section on the wiki. If you fail to do so, you will get an odd looking and nondescript error from makesdna.c.

Example:

[...]
 	float uspillr, uspillg, uspillb;
 }NodeColorspill;
 
typedef struct NodeHelloWorld {
	double helloDouble; /* Our Hello World data. */
} NodeHelloWorld;
 
 /* TEX_output */
 typedef struct TexNodeOutput {
 	char name[32];
[...]

Register the Python RNA in source/blender/makesrna/intern/rna_nodetree.c

Example:

 	RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 }
 
static void def_cmp_helloworld(StructRNA *srna)
{
/* Register elements we want exposed to Python access. */
}
 
 static void def_cmp_zcombine(StructRNA *srna)
 {
 	PropertyRNA *prop;

Register the node in Python RNA in /source/blender/nodes/NOD_static_types.h

Example:

[...]
DefNode( CompositorNode, CMP_NODE_KEYING,         def_cmp_keying,         "KEYING",         Keying,           "Keying",            ""              )
DefNode( CompositorNode, CMP_NODE_TRACKPOS,       def_cmp_trackpos,       "TRACKPOS",       TrackPos,         "Track Position",    ""              )
DefNode( CompositorNode, CMP_NODE_HELLOWORLD,     def_cmp_helloworld,     "HELLOWORLD",     HelloWorld,       "Hello World",       ""              )
 
DefNode( TextureNode,    TEX_NODE_OUTPUT,         def_tex_output,         "OUTPUT",         Output,           "Output",            ""              )
DefNode( TextureNode,    TEX_NODE_CHECKER,        0,                      "CHECKER",        Checker,          "Checker",           ""              )
[...]

Register the node in source/blender/nodes/NOD_composite.h

Example:

[...]
void register_node_type_cmp_bokehblur(struct bNodeTreeType *ttype);
void register_node_type_cmp_switch(struct bNodeTreeType *ttype);
 
void register_node_type_cmp_trackpos(struct bNodeTreeType *ttype);
void register_node_type_cmp_helloworld(struct bNodeTreeType *ttype);
 
#endif

Create a new file that defines node inputs and output in source/blender/nodes/composite/nodes/node_composite_helloworld.c

It should be noted that NODE_CLASS_CONVERTER determines where the node will appear in the Add menu.

Example:

#include "node_composite_util.h"
 
/* **************** Hello World Tool ******************** */
 
static bNodeSocketTemplate cmp_node_helloworld_in[]= {
	{	SOCK_RGBA, 1, N_("Image"),			1.0f, 1.0f, 1.0f, 1.0f},
	{	SOCK_FLOAT, 1, N_("Variable"),	1.0f, 0.0f, 0.0f, 0.0f, 0.001f, 10.0f, PROP_UNSIGNED},
	{	-1, 0, ""	}
};
static bNodeSocketTemplate cmp_node_helloworld_out[]= {
	{	SOCK_RGBA, 0, N_("Image")},
	{	-1, 0, ""	}
};
 
void register_node_type_cmp_helloworld(void)
{
	static bNodeType ntype;
 
	node_type_base(&ntype, CMP_NODE_HELLOWORLD, "Hello World", NODE_CLASS_CONVERTOR, NODE_OPTIONS);
	node_type_socket_templates(&ntype, cmp_node_helloworld_in, cmp_node_helloworld_out);
	node_type_size(&ntype, 140, 100, 320);
 
	nodeRegisterType(&ntype);
}

Make sure it is compiled by adding it to source/blender/nodes/CMakeLists.txt

[...]	
        composite/nodes/node_composite_bokehimage.c
	composite/nodes/node_composite_boxmask.c
	composite/nodes/node_composite_ellipsemask.c
	composite/nodes/node_composite_switch.c
	composite/nodes/node_composite_colorcorrection.c
        composite/nodes/node_composite_helloworld.c
 
	composite/node_composite_tree.c
	composite/node_composite_util.c
[...]

Create the header file for the node in source/blender/compositor/nodes/COM_HelloWorldNode.h

#ifndef _COM_HelloWorldNode_h_
#define _COM_HelloWorldNode_h_
 
#include "COM_Node.h"
 
/**
 * @brief HelloWorldNode
 * @ingroup Node
 */
class HelloWorldNode : public Node {
public:
	HelloWorldNode(bNode *editorNode);
	void convertToOperations(ExecutionSystem *graph, CompositorContext *context);
};
 
#endif

Create the main node file in source/blender/compositor/nodes/COM_HelloWorldNode.cpp

#include "COM_HelloWorldNode.h"
#include "COM_HelloWorldOperation.h"
#include "COM_ExecutionSystem.h"
 
HelloWorldNode::HelloWorldNode(bNode *editorNode) : Node(editorNode)
{
	/* pass */
}
 
void HelloWorldNode::convertToOperations(ExecutionSystem *graph, CompositorContext *context)
{
	HelloWorldOperation *operation = new HelloWorldOperation();
 
	this->getInputSocket(0)->relinkConnections(operation->getInputSocket(0), 0, graph);
	this->getInputSocket(1)->relinkConnections(operation->getInputSocket(1), 1, graph);
	this->getOutputSocket(0)->relinkConnections(operation->getOutputSocket(0));
	graph->addOperation(operation);
}

Write the header file for the operation in source/blender/compositor/operations/COM_HelloWorldOperation.h

#ifndef _COM_HelloWorldOperation_h
#define _COM_HelloWorldOperation_h
#include "COM_NodeOperation.h"
 
 
class HelloWorldOperation : public NodeOperation {
private:
	/**
	 * Cached reference to the inputProgram
	 */
	SocketReader *m_inputProgram;
	SocketReader *m_inputHelloWorldProgram;
 
public:
	HelloWorldOperation();
 
	/**
	 * the inner loop of this program
	 */
	void executePixel(float output[4], float x, float y, PixelSampler sampler);
 
	/**
	 * Initialize the execution
	 */
	void initExecution();
 
	/**
	 * Deinitialize the execution
	 */
	void deinitExecution();
};
#endif

Create the code that does the work in source/blender/compositor/operations/COM_HelloWorldOperation.cpp

#include "COM_HelloWorldOperation.h"
#include "BLI_math.h"
 
HelloWorldOperation::HelloWorldOperation() : NodeOperation()
{
	this->addInputSocket(COM_DT_COLOR);
	this->addInputSocket(COM_DT_VALUE);
	this->addOutputSocket(COM_DT_COLOR);
	this->m_inputProgram = NULL;
	this->m_inputHelloWorldProgram = NULL;
}
void HelloWorldOperation::initExecution()
{
	this->m_inputProgram = this->getInputSocketReader(0);
	this->m_inputHelloWorldProgram = this->getInputSocketReader(1);
}
 
void HelloWorldOperation::executePixel(float output[4], float x, float y, PixelSampler sampler)
{
	float inputValue[4];
	float inputHelloWorld[4];
 
	this->m_inputProgram->read(inputValue, x, y, sampler);
	this->m_inputHelloWorldProgram->read(inputHelloWorld, x, y, sampler);
	const float helloworld = inputHelloWorld[0];
	/* check for negative to avoid nan's */
	output[0] = inputValue[0] > 0.0f ? powf(inputValue[0], helloworld) : inputValue[0];
	output[1] = inputValue[1] > 0.0f ? powf(inputValue[1], helloworld) : inputValue[1];
	output[2] = inputValue[2] > 0.0f ? powf(inputValue[2], helloworld) : inputValue[2];
 
	output[3] = inputValue[3];
}
 
void HelloWorldOperation::deinitExecution()
{
	this->m_inputProgram = NULL;
	this->m_inputHelloWorldProgram = NULL;
}

Add the node to the converter code listing in source/blender/compositor/intern/COM_Converter.cpp

[...]
#include "COM_ViewerNode.h"
#include "COM_ZCombineNode.h"
#include "COM_HelloWorldNode.h"
 
Node *Converter::convert(bNode *b_node, bool fast)
{
	Node *node;
[...]
    case CMP_NODE_TRACKPOS:
        node = new TrackPositionNode(b_node);
        break;
    case CMP_NODE_HELLOWORLD:
        node = new HelloWorldNode(b_node);
        break;
    /* not inplemented yet */
    default:
        node = new MuteNode(b_node);
	break;
    }
    return node;
}
[...]

Make sure it will get compiled via cmake in source/blender/compositor/CMakeLists.txt

Example:

[...]
    operations/COM_SetAlphaOperation.cpp
    operations/COM_SetAlphaOperation.h
    operations/COM_MapValueOperation.cpp
    operations/COM_MapValueOperation.h
 
 
    # HelloWorld Nodes
    nodes/COM_HelloWorldNode.cpp
    nodes/COM_HelloWorldNode.h
    operations/COM_HelloWorldOperation.h
    operations/COM_HelloWorldOperation.cpp
 
    # Distort operation
    operations/COM_TranslateOperation.h
    operations/COM_TranslateOperation.cpp
[...]

And finally add it to release/scripts/startup/nodeitems_builtins.py so it will show up in the UI

[...]
    CompositorNodeCategory("CMP_CONVERTOR", "Converter", items=[
        NodeItem("CompositorNodeMath"),
        NodeItem("CompositorNodeHelloWorld"),
[...]

OPTIONAL: Write the code that would handle drawing in the node to source/blender/editors/space_node/drawnode.c, set the function and pointer, and set the flag in composite/nodes/nodename.c

This may need to be updated and expanded. There be dragons...

void register_node_type_cmp_composite(ListBase *lb)
{
	[...]
 
	node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT, NODE_PREVIEW);
        // OR NODE_PREVIEW with NODE_OPTIONS!
 
        [...]
}

New (as of 2.5) bare minimum

User:Coenspoor/Minimal_node_tutorial(2.5)

Contents