Solved How to get animation tracks of Redshift material using python in Cinema 4D 2023

How to get animation tracks of Redshift material using python in Cinema 4D 2023? (Ultimately, I want to set keyframes for the material's Diffuse Color and animate it.)

Here is what I have tried.

I manually added keyframes to the color parameter of RS materials, such as RS Material, RS Standard, and RS C4D Shader, and executed the followin script to get the animation tracks.
The script returns nothing but the name of materials and no tracks. (Of course, the animation information can be seen in the dope sheet.)

for mat in doc.GetMaterials():
    print(f"material:{mat.GetName()}")
    for track in mat.GetCTracks():
        print(f"->track:{track.GetName()}")

My environment is Windows 10, Cinema 4D 2023.1.3, Redshift 3.5.13.

Thanks in advance.

hi,

this should not be different than any node inside c4d (including scene node).
Tracks are stored inside a Baselist2D object created and linked to any node.
You must get the path of the node and retrieve the object using GetBaseListForNode.

Once you got the object, it will work as expected with GetCTracks for example. I included in the example below how to get directly the descID of a port and the corresponding track.

from typing import Optional
import c4d
import maxon

# for this example we are creating keys for the color and the intensity parameter of the BSDF node inside
# a standard/physical material


doc: c4d.documents.BaseDocument  # The active document
op: Optional[c4d.BaseObject]  # The active object, None if unselected

def main() -> None:
    # Get the active material
    mat = doc.GetActiveMaterial()
    if mat is None:
        raise ValueError("There is no selected BaseMaterial")

    # Retrieve the reference of the material as a node Material.
    nodeMaterial = mat.GetNodeMaterialReference()
    if nodeMaterial is None:
        raise ValueError("can't retrieve nodeMaterial reference")

    # Retrieve the current node space Id
    nodespaceId = c4d.GetActiveNodeSpaceId()
    # Get the nimbusref, this is useful to retrieve the DescID of a parameter.
    nimbusRef =  nodeMaterial.GetNimbusRef(nodespaceId)


    graph = nodeMaterial.GetGraph(nodespaceId)
    if graph is None:
        raise ValueError("can't retrieve the graph of this nimbus ref")

    root = graph.GetRoot()

    # Delegate function that will retrieve the tracks for a port.
    def PrintInfo(port):
        # Get the path of the node. We are looking for port that must have "color" somewhere in their path.
        portPath = port.GetPath()
        if "color" in portPath:
            # Retrieve information of the True node this port is part of
            # For each GraphNode (node and port) a BaseList2D is created.
            parentNode = port.GetAncestor(maxon.NODE_KIND.NODE)
            parentNodePath = parentNode.GetPath()

            # Retrieve the descID corresponding to this port.
            portDescID =  nimbusRef.GetDescID(portPath)          
            # Retrieve the BaseList2D object corresponding to the True Node and NOT the port.
            obj = nodeMaterial.GetBaseListForNode(nodespaceId, parentNodePath)
            if obj is None:
                return False
            # As this parameter is a color, the parameter will have one track per sub channel.
            # The last level of the descID must be added.
            portDescID.PushId(c4d.DescLevel(c4d.COLORA_R))
            # Find the right track with this DescID on the True Node Baselist2D object
            track = obj.FindCTrack(portDescID)
            print (track)

        return True

    nodesList = []
    root.GetChildren(nodesList, maxon.NODE_KIND.NODE)
    for node in nodesList:
        # Another way of printing all the track a node have. Retrieve the object corresponding to the node.
        # Once the right object is founded, track works the same as any regular c4d object.
        # nodePath = node.GetPath()
        # obj = nodeMaterial.GetBaseListForNode(nodespaceId, nodePath)
        # for track in obj.GetCTracks():
        #     print (track)
        node.GetInputs().GetChildren(PrintInfo, maxon.NODE_KIND.PORT_MASK)

"""
def state():
    # Defines the state of the command in a menu. Similar to CommandData.GetState.
    return c4d.CMD_ENABLED
"""

if __name__ == '__main__':
    main()

Cheers,
Manuel

MAXON SDK Specialist

MAXON Registered Developer

Hi Manuel,

Thank you very much.
Your script works perfectly in my environment.

Only one modification I made was
if "color" in portPath:
to
if "color" in str(portPath):

Thank you.