Solved How to find and delete a node in a NodeGraph?

I have a redshift material node graph. I would like to delete the default Material (com.redshift3d.redshift4c4d.nodes.core.material) within that graph. I can't figure out how to find this node or how to delete it once I find it.

BaseMaterial* pMat = BaseMaterial::Alloc(Mmaterial);

NodeMaterial* pNodeMat = static_cast<NodeMaterial*>(pMat);

pNodeMat->AddGraph(RedshiftNodeSpaceIds::nodespace) iferr_return;

const maxon::nodes::NodesGraphModelRef& nodeGraph = pNodeMat->GetGraph(nodeSpaceID) iferr_return;

const maxon::GraphNode rootNode = nodeGraph.GetRoot();

maxon::NodePath materialNodePath;
pNodeMat->GetMaterialNodePath(nodeSpaceID, materialNodePath) iferr_return;
materialNode = nodeGraph.GetNode(materialNodePath);

materialNode now contains a node with the id "com.redshift3d.redshift4c4d.nodes.core.material". I only know this because I can see it in the NodeGraph editor in C4D. How do I find this node and how can I delete it?

Thanks!
Kent

Hey @kbar,

Thank you for reaching out to us and thank you @Dunhou for providing an answer in Python.

All C++ entity links in my answer intentionally point to the 2023.1 C++ documentation.

NodeMaterial Methods for Creating Graphs

The easiest way to do what you probably want to do, creating an empty graph, is using the method NodeMaterial::CreateEmptyGraph. I would not recommend using ::AddGraph, as this method has been deprecated; this already applies to 2023.¹ When you want to create the default graph, you now should use ::CreateDefaultGraph instead.

You can also compare the 2023.1 Create a Redshift Material C++ example with its 2023.2 variant, as the example itself makes the shift from ::AddGraph to ::CreateEmptyGraph.

Predicting Node IDs in a Default Graph

When you create a non-empty Redshift graph, you must identify nodes in it via their asset ID. A node in a graph is identified by two things, its node ID which is unique to the instance, and the asset ID which is the same for all nodes of the same type.

3d4206a0-ecaa-4eca-8fd4-f880d943fa42-image.png

Fig. I: The two IDs are displayed in the node editor info panel for the selected node. Make sure to have Preferences\Node Editor\Ids enabled to also see the node ID in addition to the asset ID.

The 2023.1 example mentioned above makes use of asset IDs in this stretch of code to select the RS Material node contained in the (old) default graph:

// Find the RS-Material node which is contained within a standard Redshift material graph. This
    // example assumes that there is exactly one RS-Material node in the graph that is already 
    // connected to the output node (which is true for the RS default material as of S26).
    maxon::BaseArray<maxon::GraphNode> results;
    maxon::GraphModelHelper::FindNodesByAssetId(
      graph, rsmaterialId, false, results) iferr_return;
 
    if (results.GetCount() < 1)
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not find RS-Material node."_s);
 
    maxon::GraphNode rsmaterialNode = results[0];

This also highlights the problem with it, as you are unable to distinguish two node instances of the same type in this manner. The default graph in the standard material system has predictable/persistent node IDs.

95e16d67-fd76-4f2e-aa25-5075d4064711-image.png
Fig. II: The two nodes in the Standard Renderer default graph will always have the IDs material, and bsdf.

df209011-7ad7-4010-ba00-17061a3018a7-image.png
Fig. III: The two nodes in the Redshift Renderer default material on the other hand will not have predictable IDs, as they are newly hashed for each graph.

This forces you then to search the graph over asset IDs just as shown in the 2023.1 example.

Resources

  • 2024 Python Node Examples: I just added three scripts which cover the very basics in Python. Their code is fully transferable to 2023.X C++.
  • 2023.1 Create a Redshift Material: Highlights the '::AdGraph or ::CreateDefaultGraph and then sorting out what is what'-worflow discussed above.
  • 2023.2 Create a Redshift Material: Highlights the '::CreateEmptyGraph-workflow of starting with a blank canvas. Especially in the context of Redshift I would recommend using this.

Cheers,
Ferdinand

[1] I am aware that our documentation does not mention the deprecation on entities. In this case, the deprecation still has been covered by our old and manual change notes. The new automated changes notes do not cover this anymore. The underlying problem is here that Doxygen does parse C++ attributes, i.e., this:

8fc51e0c-3acd-4bcb-9c9f-5d0ab5042b54-image.png

Is not automatically converted into a @deprecated. I would have to mess with the Doxygen parsing to fix this, or pre-parse the frameworks to inject @deprecated markup. I consider both things too costly as we will move away from Doxygen rather sooner than later anyway.

MAXON SDK Specialist
developers.maxon.net

Hi@kbar, I didn't know C++, but I think the method should be the same as python. I have two options for this task:

  • all maxon sdk stuffs, I modified the github example here

  • use my renderEngine library (custom wrapper for node gragh us maxon sdk)
    Hoping this helps.

Cheers~
DunHou


maxon sdk

import c4d
import maxon

doc: c4d.documents.BaseDocument # The active document.

def main():

    # For each #material in the currently active document get its #nodeMaterial reference.
    material: c4d.BaseMaterial
    for material in doc.GetMaterials():
        nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
    
        # To test if a material has a graph for a specific space, we must use the method #HasSpace. 
        # To support 3rd party render engines, you must ask them for their node space ID.
        for nid in ("net.maxon.nodespace.standard",                 # Standard Renderer
                    "com.redshift3d.redshift4c4d.class.nodespace"): # Redshift Renderer
            if not nodeMaterial.HasSpace(nid):
                continue
            
            # This material has a graph for the space #nid, we retrieve it.
            graph: maxon.GraphModelInterface = nodeMaterial.GetGraph(nid)
            if graph.IsNullValue():
                raise RuntimeError("Found malformed empty graph associated with node space.")
            
            result: list[maxon.GraphNode] = []
            asset_id = "com.redshift3d.redshift4c4d.nodes.core.material"
            maxon.GraphModelHelper.FindNodesByAssetId(graph, asset_id, True, result)
            
            # start remove
            with graph.BeginTransaction() as transaction:
                for i in result:
                    i.Remove()
                transaction.Commit()
            
    c4d.EventAdd()

if __name__ == "__main__":
    main()

renderEngine

import c4d,maxon
from renderEngine.redshift import redshift_helper as rs
doc: c4d.documents.BaseDocument # The active document.

def ModifyMaterial(material: rs.MaterialHelper):
    if material is None:
        return
    # transfer it to a instance of rs.MaterialHelper                
    if isinstance(material, c4d.BaseMaterial):
        material = rs.MaterialHelper(material)
        # modification has to be done within a transaction
        with rs.RSMaterialTransaction(material) as transaction:

            # return a list of material with giving node id
            RsMat = material.helper.GetNodes("com.redshift3d.redshift4c4d.nodes.core.material")
            for mat in RsMat:
                # remove it
                material.helper.RemoveShader(mat)
                       
if __name__ == "__main__":
    ModifyMaterial(doc.GetActiveMaterial())

Hey @kbar,

Thank you for reaching out to us and thank you @Dunhou for providing an answer in Python.

All C++ entity links in my answer intentionally point to the 2023.1 C++ documentation.

NodeMaterial Methods for Creating Graphs

The easiest way to do what you probably want to do, creating an empty graph, is using the method NodeMaterial::CreateEmptyGraph. I would not recommend using ::AddGraph, as this method has been deprecated; this already applies to 2023.¹ When you want to create the default graph, you now should use ::CreateDefaultGraph instead.

You can also compare the 2023.1 Create a Redshift Material C++ example with its 2023.2 variant, as the example itself makes the shift from ::AddGraph to ::CreateEmptyGraph.

Predicting Node IDs in a Default Graph

When you create a non-empty Redshift graph, you must identify nodes in it via their asset ID. A node in a graph is identified by two things, its node ID which is unique to the instance, and the asset ID which is the same for all nodes of the same type.

3d4206a0-ecaa-4eca-8fd4-f880d943fa42-image.png

Fig. I: The two IDs are displayed in the node editor info panel for the selected node. Make sure to have Preferences\Node Editor\Ids enabled to also see the node ID in addition to the asset ID.

The 2023.1 example mentioned above makes use of asset IDs in this stretch of code to select the RS Material node contained in the (old) default graph:

// Find the RS-Material node which is contained within a standard Redshift material graph. This
    // example assumes that there is exactly one RS-Material node in the graph that is already 
    // connected to the output node (which is true for the RS default material as of S26).
    maxon::BaseArray<maxon::GraphNode> results;
    maxon::GraphModelHelper::FindNodesByAssetId(
      graph, rsmaterialId, false, results) iferr_return;
 
    if (results.GetCount() < 1)
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not find RS-Material node."_s);
 
    maxon::GraphNode rsmaterialNode = results[0];

This also highlights the problem with it, as you are unable to distinguish two node instances of the same type in this manner. The default graph in the standard material system has predictable/persistent node IDs.

95e16d67-fd76-4f2e-aa25-5075d4064711-image.png
Fig. II: The two nodes in the Standard Renderer default graph will always have the IDs material, and bsdf.

df209011-7ad7-4010-ba00-17061a3018a7-image.png
Fig. III: The two nodes in the Redshift Renderer default material on the other hand will not have predictable IDs, as they are newly hashed for each graph.

This forces you then to search the graph over asset IDs just as shown in the 2023.1 example.

Resources

  • 2024 Python Node Examples: I just added three scripts which cover the very basics in Python. Their code is fully transferable to 2023.X C++.
  • 2023.1 Create a Redshift Material: Highlights the '::AdGraph or ::CreateDefaultGraph and then sorting out what is what'-worflow discussed above.
  • 2023.2 Create a Redshift Material: Highlights the '::CreateEmptyGraph-workflow of starting with a blank canvas. Especially in the context of Redshift I would recommend using this.

Cheers,
Ferdinand

[1] I am aware that our documentation does not mention the deprecation on entities. In this case, the deprecation still has been covered by our old and manual change notes. The new automated changes notes do not cover this anymore. The underlying problem is here that Doxygen does parse C++ attributes, i.e., this:

8fc51e0c-3acd-4bcb-9c9f-5d0ab5042b54-image.png

Is not automatically converted into a @deprecated. I would have to mess with the Doxygen parsing to fix this, or pre-parse the frameworks to inject @deprecated markup. I consider both things too costly as we will move away from Doxygen rather sooner than later anyway.

MAXON SDK Specialist
developers.maxon.net

Thanks @ferdinand and @Dunhou! Using FindNodesByAssetId has done the trick.

Unfortunately I can't jump to using the latest frameworks for everything just yet, but I will make sure to use the new methods for anything in 2024 onwards.

@ferdinand said in How to find and delete a node in a NodeGraph?:

Fig. I: The two IDs are displayed in the node editor info panel for the selected node. Make sure to have Preferences\Node Editor\Ids enabled to also see the node ID in addition to the asset ID.

I didn't know about this! I have been copying and pasting the selected nodes into a text editor up until now, which in itself is a very cool feature.