Proving Default Graph Setups

Table of Contents

Describes how to implement a default state for a node graph.

The default state of a node graph determines what nodes, with which values and connections, will be contained by default in a newly created graph. A custom node called net.maxonexample.handbook.noderenderer.usernode_shade is used in this example and must be created to provide expected functionality.

Code

To implement a default state, a function has to be created that will provide this default setup by creating the required nodes, setting the values and making the port connections. This function has to be registered in NodeRendererNodeSpaceImpl::Init().

static maxon::Result<void> CreateMaterialGraphDefaultSetup(const maxon::nodes::NodesGraphModelRef& graph)
{
// Instantiate a node that only has a description, but no process function.
maxon::nodes::NodeTemplate userNodeSystem = maxon::nodes::NodesLib::LoadTemplate(maxon::AssetInterface::GetBuiltinRepository(), maxon::Id("net.maxonexample.handbook.noderenderer.usernode")) iferr_return;
maxon::GraphNode userGraphNode = graph.AddChild(maxon::Id("user node instance"), userNodeSystem) iferr_return;
maxon::nodes::NodeTemplate shaderNodeSystem = maxon::nodes::NodesLib::LoadTemplate(maxon::AssetInterface::GetBuiltinRepository(), maxon::Id("net.maxonexample.handbook.noderenderer.usernode_shade")) iferr_return;
maxon::GraphNode shaderNode = graph.AddChild(maxon::Id("shader node instance"), shaderNodeSystem) iferr_return;
// Connect the two instances.
maxon::GraphNode outPort = userGraphNode.GetOutputs().FindChild(maxon::Id("net.maxonexample.handbook.node.usernode.out")) iferr_return;
maxon::GraphNode inPort = shaderNode.GetInputs().FindChild(maxon::Id("usernode_shade.colorA")) iferr_return;
outPort.Connect(inPort) iferr_return;
// Replace the default value for the first instance.
maxon::GraphNode valuePort = shaderNode.GetInputs().FindChild(maxon::Id("usernode_shade.colorB")) iferr_return;
valuePort.SetDefaultValue(maxon::Data(maxon::ColorA(1.0, 1.0, 0.0, 1.0))) iferr_return;
return maxon::OK;
}

This function then has to be written to the DataDictionary of the node space as a callback for providing a default setup.

// Defines the callback function that will create the default setup for the graph.
spaceData.Set(maxon::nodes::NODESPACE::CREATEMATERIALGRAPHFUNC, maxon::nodes::NODESPACE::CreateMaterialGraphFunc(CreateMaterialGraphDefaultSetup)) iferr_return;

Final Code

If all the steps have been followed, the code should look like the following:

#include "maxon/module.h"
// This description processor must be used for all nodes of the namespace. This will define if the node is compatible with the current NodeSpace.
MAXON_DECLARATION_REGISTER(maxon::DescriptionProcessors, "net.maxonexample.handbook.nodespace.noderendererprocessor")
{
return maxon::nodes::CoreNodesDescriptionProcessor().Create(
[](const maxon::nodes::NodeSystemClass& cls) -> maxon::Result<maxon::Bool>
{
return cls.GetClass() == NodeRendererNodeSystemClassImpl::GetClass();
});
}
static maxon::Result<void> HandleUserNodeDescriptions(const maxon::Id& databaseId)
{
maxon::nodes::NodeSystemClass nodeSystemClass = NodeRendererNodeSystemClassImpl::GetClass().Create() iferr_return;
for (const maxon::IdAndVersion& id : ids)
{
const maxon::DataDescription description = maxon::DataDescriptionDatabaseInterface::LoadDescription(maxon::DATADESCRIPTION_CATEGORY_DATA, maxon::LanguageRef(), id.first) iferr_return;
const maxon::DataDictionary info = description.GetInfo();
const maxon::Id classification = info.Get(maxon::DESCRIPTION::DATA::INFO::CLASSIFICATION, maxon::DESCRIPTION::DATA::INFO::CLASSIFICATION.ENUM_UNCLASSIFIED);
if (classification == maxon::DESCRIPTION::DATA::INFO::CLASSIFICATION.ENUM_NODE)
{
maxon::nodes::NodeTemplate node = maxon::nodes::NodesLib::CreateLazyTemplate(id.first,
[id, nodeSystemClass]() -> maxon::Result<maxon::nodes::NodeTemplate>
{
return maxon::nodes::NodesLib::BuildNodeFromDescription(id.first, nodeSystemClass);
}, nodeSystemClass) iferr_return;
maxon::GenericData data = maxon::nodes::BuiltinNodes::Register(id.first, node) iferr_return;
g_freeAtEnd.Append(std::move(data)) iferr_return;
}
}
return maxon::OK;
}
static maxon::Result<void> CreateMaterialGraphDefaultSetup(const maxon::nodes::NodesGraphModelRef& graph)
{
// Instantiate a node that only has a description, but no process function.
maxon::nodes::NodeTemplate userNodeSystem = maxon::nodes::NodesLib::LoadTemplate(maxon::AssetInterface::GetBuiltinRepository(), maxon::Id("net.maxonexample.handbook.noderenderer.usernode")) iferr_return;
maxon::GraphNode userGraphNode = graph.AddChild(maxon::Id("user node instance"), userNodeSystem) iferr_return;
maxon::nodes::NodeTemplate shaderNodeSystem = maxon::nodes::NodesLib::LoadTemplate(maxon::AssetInterface::GetBuiltinRepository(), maxon::Id("net.maxonexample.handbook.noderenderer.usernode_shade")) iferr_return;
maxon::GraphNode shaderNode = graph.AddChild(maxon::Id("shader node instance"), shaderNodeSystem) iferr_return;
// Connect the two instances.
maxon::GraphNode outPort = userGraphNode.GetOutputs().FindChild(maxon::Id("net.maxonexample.handbook.node.usernode.out")) iferr_return;
maxon::GraphNode inPort = shaderNode.GetInputs().FindChild(maxon::Id("usernode_shade.colorA")) iferr_return;
outPort.Connect(inPort) iferr_return;
// Replace the default value for the first instance.
maxon::GraphNode valuePort = shaderNode.GetInputs().FindChild(maxon::Id("usernode_shade.colorB")) iferr_return;
valuePort.SetDefaultValue(maxon::Data(maxon::ColorA(1.0, 1.0, 0.0, 1.0))) iferr_return;
return maxon::OK;
}
static maxon::Result<void> ConfigurePreviewImageRequest(maxon::DataDictionaryObjectRef request)
{
request.Set(maxon::nodes::PREVIEWIMAGEREQUEST::PROVIDER, PreviewImageProviderExampleHandbook::GetClass()) iferr_return;
return maxon::OK;
}
MAXON_METHOD maxon::Result<void> NodeRendererNodeSpaceImpl::Init(maxon::DataDictionary spaceData)
{
_class = NodeRendererNodeSystemClassImpl::GetClass().Create() iferr_return;
spaceData.Set(maxon::nodes::NODESPACE::NODESYSTEMCLASS, _class) iferr_return;
// Defines the End Node for this node systems
maxon::BaseArray<maxon::Id> materialEndNodeIds;
// Define the node we want to be defined as the end node.
materialEndNodeIds.Append(maxon::Id("net.maxonexample.handbook.noderenderer.usernode_shade")) iferr_return;
// Define the information in the DataDictionnary.
spaceData.Set(maxon::nodes::NODESPACE::MATERIALENDNODEIDS, std::move(materialEndNodeIds)) iferr_return;
// Defines the supported render
// Create a BaseArray that store the supporter's engine ID.
maxon::BaseArray<maxon::Int> supportedRenderers;
// Here, g_nodeRendererId is the ID of a render engine.
supportedRenderers.Append(g_nodeRendererId) iferr_return;
supportedRenderers.Append(1023342) iferr_return;
supportedRenderers.Append(0) iferr_return;
// Add the array in the DataDictionnary.
spaceData.Set(maxon::nodes::NODESPACE::RENDERERS, std::move(supportedRenderers)) iferr_return;
// Defines the callback function that will create the default setup for the graph.
spaceData.Set(maxon::nodes::NODESPACE::CREATEMATERIALGRAPHFUNC, maxon::nodes::NODESPACE::CreateMaterialGraphFunc(CreateMaterialGraphDefaultSetup)) iferr_return;
super.Init(spaceData) iferr_return;
return maxon::OK;
}
static maxon::Id g_nodeRendererDatabaseID = maxon::Id("net.maxonexample.handbook.nodes.registereddatabase");
static maxon::GenericData g_exampleNodeSpace;
static maxon::Result<void> LoadResources()
{
{
err.CritStop();
return err;
};
// Get plugin location
const maxon::Url& binaryUrl = maxon::g_maxon.GetUrl();
// Get plugin folder
maxon::Url pluginDir = binaryUrl.GetDirectory();
// Get resource folder (this folder must exist)
const maxon::Url resourceUrl = pluginDir.Append("res"_s).Append("nodes"_s) iferr_return;
// Register database
// Register the node space
// Use the same ID as in the resource files
const maxon::Id spaceDescriptionId = maxon::Id("net.maxonexample.handbook.nodespace.node_renderer");
// Load the description
maxon::DataDictionary nodeRendererData = maxon::nodes::NodeSpaceHelpersInterface::LoadDescription(spaceDescriptionId) iferr_return;
maxon::nodes::NodeSpaceRef nodeRendererSpace = NodeRendererNodeSpaceImpl::CreateInit(nodeRendererData) iferr_return;
// Register the node space implementation
g_exampleNodeSpace = maxon::nodes::MaterialNodeSpaces::Register(NodeRendererNodeSpaceImpl::GetDescriptor().GetId(), nodeRendererSpace) iferr_return;
HandleUserNodeDescriptions(g_nodeRendererDatabaseID) iferr_return;
return maxon::OK;
}
static void FreeResources()
{
{
err.CritStop();
return;
};
// Unregister a database
g_exampleNodeSpace = maxon::GenericData();
g_freeAtEnd.Reset();
}
MAXON_INITIALIZATION(LoadResources, FreeResources);
maxon::DataDescriptionDefinitionDatabaseInterface::RegisterDatabaseWithUrl
static MAXON_METHOD Result< void > RegisterDatabaseWithUrl(const Id &databaseId, const Url &url)
maxon::DataDescriptionDatabaseInterface::LoadDescription
static MAXON_METHOD Result< DataDescription > LoadDescription(const Id &category, const LanguageRef &language, const Id &dataType)
MAXON_DECLARATION_REGISTER
#define MAXON_DECLARATION_REGISTER(...)
Definition: module.h:883
maxon::ColorA
Col4< Float, 1 > ColorA
Definition: vector4d.h:60
maxon::IdAndVersion
Tuple< Id, Id > IdAndVersion
IdAndVersion is used for assets to store the asset id (first) and version (second) in a single tuple.
Definition: idandversion.h:14
node_spaces.h
maxon
The maxon namespace contains all declarations of the MAXON API.
Definition: c4d_basedocument.h:15
datadescriptiondatabase.h
nodes
Definition: nodes.h:3802
module.h
maxon::DataDescriptionDefinitionDatabaseInterface::GetRegisteredDescriptions
static MAXON_METHOD Result< BaseArray< IdAndVersion > > GetRegisteredDescriptions(const Id &databaseId, const Id &category, const LanguageRef &language)
maxon::AssetInterface::GetBuiltinRepository
static const MAXON_METHOD AssetRepositoryRef & GetBuiltinRepository()
maxon::OK
return OK
Definition: apibase.h:2547
maxon::Id
Definition: apibaseid.h:250
iferr_return
#define iferr_return
Definition: resultbase.h:1434
nodes_corenodes_base.h
datadescriptiondefinitiondatabase.h
previewimageprovider.h
maxon::BaseArray
Definition: basearray.h:366
maxon::Url
Definition: url.h:856
MAXON_INITIALIZATION
#define MAXON_INITIALIZATION(...)
Definition: module.h:746
maxon::DATADESCRIPTION_CATEGORY_DATA
const Id DATADESCRIPTION_CATEGORY_DATA
Definition: datadescriptiondatabase.h:15
maxon::Result< void >
MAXON_METHOD
#define MAXON_METHOD
Definition: interfacebase.h:855
OK
OK
Ok.
Definition: ge_prepass.h:2
maxon::BaseArray::Append
MAXON_ATTRIBUTE_FORCE_INLINE ResultRef< T > Append()
Definition: basearray.h:569
iferr_scope
#define iferr_scope
Definition: resultbase.h:1343
maxon::DataDescriptionDefinitionDatabaseInterface::UnregisterDatabase
static MAXON_METHOD Result< void > UnregisterDatabase(const Id &databaseId)
maxon::GenericData
Definition: genericdata.h:19
iferr_scope_handler
#define iferr_scope_handler
Definition: resultbase.h:1361
maxon::nodes::NodeSpaceHelpersInterface::LoadDescription
static MAXON_METHOD Result< DataDictionary > LoadDescription(const Id &spaceDescriptionId)
maxon::BaseArray::Reset
void Reset()
Deletes all elements (calls destructors and frees memory).
Definition: basearray.h:495
maxon::Delegate
Definition: delegate.h:235
datadescription_nodes.h
maxon::LiteralId::Get
const Id & Get() const
Definition: apibaseid.h:183