Keyframing the source file on an ImageTexture shader

I have this Octane material with a ImageTexture shader.
I can programmatically set the source file on the shader.
I want to be able to Keyframe this source file.
Here's what I got so far:

def changeTexture(shader, item, chosen, curTime) :

    doc = shader.GetDocument()
    doc.AddUndo(c4d.UNDOTYPE_CHANGE, shader)
    filepath = f'{item}_{chosen}.png'
    # Assign the new value
    print(f'Applying texture: {filepath}')
    print (shader[c4d.IMAGETEXTURE_FILE])
    shader[c4d.IMAGETEXTURE_FILE] = filepath
    shader.Message(c4d.MSG_UPDATE)
    print(shader[c4d.IMAGETEXTURE_FILE])

    track = shader.FindCTrack(c4d.IMAGETEXTURE_FILE)
    if not track:
        track = c4d.CTrack(shader, c4d.IMAGETEXTURE_FILE)
        shader.InsertTrackSorted(track)
    print(track)
    curve = track.GetCurve()
    result = curve.FindKey(curTime)
    if result is None:
        result = curve.AddKey(curTime)
    print(result)
    track.FillKey(doc, shader, result['key'])
    c4d.EventAdd()

This runs without bugs. The files do get changed but they're not keyframed. The first time I run this, the file keyframe indicator does show up orange. But not the subsequent times.

hi,

Your code is running fine with our regular bitmap shader and a standard material.

As you are using Octane i cannot check what is going on. What looks to be the issue is the DescID you are using to find the track or to create it track = shader.FindCTrack(c4d.IMAGETEXTURE_FILE). You can learn a bit more about DescIDs in our c++ manual. DescID allow you to define the Type for a parameter. you are creating the Track without using the right type, which could be the issue.

Below a code that is working as expected.

Cheers,
Manuel

from typing import Optional
import c4d

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


def ChangeTexture(shader, item, chosen, curTime) :

    doc = shader.GetDocument()
    doc.AddUndo(c4d.UNDOTYPE_CHANGE, shader)
    filepath = f'{item}_{chosen}.png'
    # Assign the new value
    print(f'Applying texture: {filepath}')
    print (shader[c4d.BITMAPSHADER_FILENAME])
    shader[c4d.BITMAPSHADER_FILENAME] = filepath
    shader.Message(c4d.MSG_UPDATE)
    print(shader[c4d.BITMAPSHADER_FILENAME])

    track = shader.FindCTrack(c4d.BITMAPSHADER_FILENAME)
    if not track:
        track = c4d.CTrack(shader, c4d.BITMAPSHADER_FILENAME)
        shader.InsertTrackSorted(track)
    print(track)
    curve = track.GetCurve()
    result = curve.FindKey(curTime)
    if result is None:
        result = curve.AddKey(curTime)
    print(result)
    track.FillKey(doc, shader, result['key'])
    c4d.EventAdd()


def main() -> None:
    # Creates a Standard C4D Material
    mat = c4d.Material()
    if mat is None:
        raise RuntimeError("Failed to create a new default material.")

    # Creates a bitmap shader
    sha = c4d.BaseList2D(c4d.Xbitmap)
    if sha is None:
        raise RuntimeError("Failed to create a bitmap shader.")

    # Defines the path of the bitmap shader
    sha[c4d.BITMAPSHADER_FILENAME] = "FileName"

    # Defines the material color shader to new created one.
    mat[c4d.MATERIAL_COLOR_SHADER] = sha

    # Inserts the shader into the material
    mat.InsertShader(sha)

    # Inserts a material in the active doc
    doc.InsertMaterial(mat)
    
    ChangeTexture(sha, 1, 2, doc.GetTime())
    ChangeTexture(sha, 1, 4, doc.GetTime() + c4d.BaseTime(1))
    # Pushes an update event to Cinema 4D
    c4d.EventAdd()


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

if __name__ == '__main__':
    main()

MAXON SDK Specialist

MAXON Registered Developer

Hi,

A simple addition on CTrack and DescId as I was also faced to this a time ago. You can consult the @ferdinand's explanations and exemples on CTrack to the following post :

https://plugincafe.maxon.net/topic/14315/solved-how-to-setup-a-ctrack-on-tag-plugin-ui-slider-with-extended-details/8

Cheers,
Christophe