How to Add Enum Values to a Node Attribute?
-
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,
FerdinandResult:
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; } }