Describes how to implement the description of a custom user node programmatically.
Most of the time, Custom user nodes descriptions are defined using the resource editor. But, for more advanced users that want to add lots of nodes, this is time consuming and repetitive. A new interface have been created and solve such an issue. With it, nodes descriptions can be created, defined and stored programmatically. The descriptions will be stored in a DataBase, just like with the resource editor. The interface allow to register this DataBase and manage it as nodes can be spread out on different DataBase. Node description can be defined in several DataDictionary representing several categories, It's not mandatory but highly recommended to create a class to help registering those DataDictionary. Once all the DataDictionary are build, the interface As those descriptions can be very specific and depend on users need, it's impossible to share a common base.
Code
First thing to do is create an implementation of DataDescriptionDefinitionDatabaseImplInterface and override all the MAXON_METHOD the interface declare. And of course, this implementation must be registered with MAXON_COMPONENT_CLASS_REGISTER
Once it's done, registering the database is just a few lines of code.
Id dbId("net.maxonexample.handbook.nodes.proceduraldatabase");
DataDescriptionDefinitionDatabaseImplRef db = ProceduralNodeDataDescriptionDefinitionDatabaseImpl::CreateInit(dbId)
iferr_return;
DataDescriptionDefinitionDatabaseInterface::RegisterDatabase(dbId, db)
iferr_return;
ProceduralNodeBuilder b(*impl);
const Class< R > & Get(const Id &cls)
Definition: objectbase.h:2073
#define iferr_return
Definition: resultbase.h:1519
The helper class can look something like the code below.
class ProceduralNodeBuilder
{
public:
explicit ProceduralNodeBuilder(ProceduralNodeDataDescriptionDefinitionDatabaseImpl& db) : _db(db)
{
}
void BeginNode(
const Id&
node)
{
_definitions.Flush();
}
Result<DataDescriptionDefinition&> GetDefinition(const Id& category)
{
DataDescriptionDefinition&
def = _definitions.InsertKey(category, created)
iferr_return;
if (created)
{
{
info.Set(DESCRIPTION::DATA::INFO::CLASSIFICATION, DESCRIPTION::DATA::INFO::CLASSIFICATION.ENUM_NODE)
iferr_return;
}
InternedId baseInclude;
DataDictionary dd;
dd.Set(DESCRIPTION::DATA::BASE::INCLUDE, baseInclude.Get())
iferr_return;
}
}
Result<void> SetMenuCategory(const Id& mc)
{
info.Set(DESCRIPTION::UI::INFO::MENUCATEGORY, Id(
"net.maxon.nodecategory.math"))
iferr_return;
}
Result<void> SetName(
const Id& language,
const String&
name)
{
DataDictionary dd;
}
Result<void> BeginPort(
PORT_DIR dir,
const Id& portId)
{
_portDir = dir;
_portDefinitions.Flush();
return _port.Init(portId);
}
Result<DataDictionary&> GetPortDictionary(const Id& category)
{
DataDictionary&
dict = _portDefinitions.InsertKey(category, created)
iferr_return;
if (created)
{
dict.Set(DESCRIPTION::DATA::BASE::CLASSIFICATION, _portDir ==
PORT_DIR::INPUT ? DESCRIPTION::DATA::BASE::CLASSIFICATION.ENUM_INPUT : DESCRIPTION::DATA::BASE::CLASSIFICATION.ENUM_OUTPUT)
iferr_return;
}
}
template <
typename ATTR> Result<void>
Set(
const Id& category,
const ATTR& attr,
const typename ATTR::ValueType&
value)
{
}
Result<void> EndPort()
{
for (
const auto&
e : _portDefinitions)
{
}
_portDefinitions.Flush();
}
Result<void> EndNode()
{
for (
const auto&
e : _definitions)
{
}
_definitions.Flush();
}
private:
ProceduralNodeDataDescriptionDefinitionDatabaseImpl& _db;
HashMap<Id, DataDescriptionDefinition> _definitions;
InternedId _port;
HashMap<Id, DataDictionary> _portDefinitions;
};
#define Set(a0, a1, a2, a3, a4, a5)
Definition: Python-ast.h:586
PyObject * value
Definition: abstract.h:715
const char const char * name
Definition: abstract.h:195
Definition: c4d_string.h:39
PyObject PyObject * dict
Definition: abstract.h:150
COMMAND
Command. (CommandData)
Definition: ge_prepass.h:4
maxon::Bool Bool
Definition: ge_sys_math.h:55
IDENTIFIER
An arbitrary symbol that is neither a string, nor a value. It may contain unicode characters.
Definition: lexer.h:6
Py_ssize_t * e
Definition: longobject.h:89
PORT_DIR
Definition: graph_basics.h:34
const Id DATADESCRIPTION_CATEGORY_UI
Definition: datadescriptiondatabase.h:16
const Id DATADESCRIPTION_CATEGORY_DATA
Definition: datadescriptiondatabase.h:15
INPUT
Definition: parametertype.h:1
PyObject struct PyModuleDef * def
Definition: pycore_pystate.h:130
_Py_clock_info_t * info
Definition: pytime.h:197
#define iferr_scope
Definition: resultbase.h:1384
#define NAME
Definition: token.h:14
Once this helper class is done, creating a new node is as simple as that
const Id firstNodeId {"net.maxon.node.procedural.firstexample"};
b.BeginNode(firstNodeId);
b.SetMenuCategory(Id(
"net.maxon.nodecategory.math"))
iferr_return;
@ DEFAULTVALUE
Dummy value for the default value GeData constructor.
Definition: c4d_gedata.h:65
Definition: string.h:1490
Definition: apibaseid.h:253
The TimeValue class encapsulates a timer value.
Definition: timevalue.h:33
void Quantize(Float64 frameRate)
quantize the time for a given frame rate, so that its frame value is a multiple of the specified fram...
maxon::Float Float
Definition: ge_sys_math.h:66
OUTPUT
Output flags for OutputWithFlags and DebugOutput.
Definition: debugdiagnostics.h:53
static const Id LANGUAGE_ENGLISH_ID
Definition: stringresource.h:62
Of course the template must be generated and registered
NodeTemplate t = NodesLib::CreateLazyTemplate(firstNodeId,
[firstNodeId]() -> Result<NodeTemplate>
{
return NodesLib::BuildNodeFromDescription(firstNodeId, CoreNodesNodeSystemClass());
Py_UCS4 * res
Definition: unicodeobject.h:1113