Dear Community,
This question reached us via mail and since answering it does not require confidential information and since I thought others find this interesting too, I am sharing my answer here. The question was How to add enum values dynamically to a node attribute that uses an Enum GUI?".
Find my answer below.
Cheers,
Ferdinand
Result:
Code:
#include "c4d_basematerial.h"
#include "maxon/datadescription_nodes.h"
#include "maxon/graph.h"
#include "maxon/graph_helper.h"
#include "maxon/nodesgraph.h"
#include "maxon/nodesystem.h"
namespace maxon
{
/// @brief Demonstrates how to modify the enum values of an attribute.
/// @details One place where one could do this is inside the #::InstantiateImpl method of a
/// #NodeTemplateInterface one is implementing so that every new node is being populated with
/// enum values of our liking. But as demonstrated by the example below, nothing prevents us
/// from doing the same thing at runtime on a graph.
Result<void> AddEnumValues(BaseDocument* doc)
{
iferr_scope;
// Get the active material's node graph and start a transaction.
NodeMaterial* const material = static_cast<NodeMaterial*>(doc->GetActiveMaterial());
CheckArgument(material);
nodes::NodesGraphModelRef graph = material->GetGraph(GetActiveNodeSpaceId()) iferr_return;
GraphTransaction transaction = graph.BeginTransaction() iferr_return;
{
// Get the mutable root for #graph. This way is not only shorter than first getting the
// mutable node system for #graph and then its mutable root, but also the only way that
// actually works here. We can do this because starting a transaction on a graph model also
// implies modifying the node system. So, we do not have to call NodeSystem::BeginModification
// in this case.
nodes::MutableNode root = nodes::ToMutableNode(graph.GetRoot()) iferr_return;
// Iterate over all children of the root to get hold of nodes which have our port.
for (auto node : root.GetChildren())
{
// Attempt to get hold of our enum port.
nodes::MutablePort enumPort = node.GetInputs().FindPort(
Id("in@eSp1K8T8GNcuPwKSds8Lvs")) iferr_return;
// We could also add a non-existing port here with MutableNode::AddPort
if (!enumPort || !enumPort.IsValid())
continue;
// NodeTemplateInterface::InstantiateImpl code would start here.
// Set the data type and label of the port, doing this is obviously optional.
enumPort.SetType<String>() iferr_return;
enumPort.SetValue(NODE::BASE::NAME, "My Enumeration"_s) iferr_return;
// Build the enum data and write it into the port.
BaseArray<Tuple<Id, Data>> entries;
for (const Int32 i : {1, 2, 3, 4, 5})
{
const String data = FormatString("Item @", i);
const Id id = Id::Create(label) iferr_return;
entries.Append(Tuple<Id, Data>(id, data)) iferr_return;
}
DataDictionary enumPortData;
enumPortData.Set(DESCRIPTION::DATA::BASE::ENUM, entries) iferr_return;
enumPort.SetValue(nodes::PortDescriptionData, std::move(enumPortData)) iferr_return;
// And set the default value of the port.
enumPort.SetDefaultValue("Item 1"_s) iferr_return;
}
// Commit the transaction and with it the node system modification.
} transaction.Commit() iferr_return;
return OK;
}
}