Solved How to change bitmap in layered shader?

I'm writing a plugin to change the bitmaps within layered shaders. I've found here some great examples on how to change parameters in LayerShaderLayer but have yet to figure out exactly how to change and update a bitmap in the layered shader.

Here's my latest attempt. I know that I probably don't have to update all elements, but to be sure, I updated a few.

mat = doc.GetFirstMaterial()
    shd = mat.GetFirstShader()
    if shd.GetName() == "Layer":
        layShd = mat[c4d.MATERIAL_COLOR_SHADER]
        layShdFirst = layShd.GetFirstLayer()
        print layShdFirst.GetParameter(c4d.LAYER_S_PARAM_SHADER_LINK)
        theBitmap = c4d.BaseList2D(c4d.Xbitmap)  #create a bitmap baseshader
        theBitmap[c4d.BITMAPSHADER_FILENAME] = "/Woven_Black_Carpet_Flat.jpg"
        theBitmap.InsertUnder(layShd)
        #layShdFirst.SetParameter(c4d.LAYER_S_PARAM_ALL_ACTIVE, True)
        
        #print layShdFirst.GetBitmap()
        layShdFirst.SetParameter(c4d.LAYER_S_PARAM_SHADER_LINK, theBitmap)
        layShd.Message(c4d.MSG_UPDATE)#update
        shd.Message(c4d.MSG_UPDATE)#update
        mat.Update(True, True)
        print layShdFirst.GetParameter(c4d.LAYER_S_PARAM_SHADER_LINK)

Hi @visualride you don't need anything specific when you update a texture path, just adding an EventAdd to notify Cinema 4D that something has changed is enough.

So find bellow a code that will update the first layer if this is a Bitmap Shader and then create a new Bitmap Shader Layer

import c4d

def ChangeTexturePathOfLayer(layer: c4d.LayerShaderLayer, texturePath: str) -> None:
    """Change the texture path of an existing Bitmap Layer
    """
    # Check if the layer is really a shader (bitmap being a shader)
    if layer.GetType() != c4d.TypeShader:
        raise ValueError("First layer is not a shader")
    
    # Retrieve the linked shader of this layer and check if this is really a bitmap shader
    bmpShader = layer.GetParameter(c4d.LAYER_S_PARAM_SHADER_LINK)
    if not bmpShader.CheckType(c4d.Xbitmap):
        raise ValueError("First layer is not a bitmap shader")
    
    # Update the texture path of the shader linked to this layer
    bmpShader[c4d.BITMAPSHADER_FILENAME] = texturePath
    
    
def CreateBitmapLayer(layerShader: c4d.LayerShader, texturePath: str) -> None:
    """Create a new Bitmap Layer in the given layerShader
    """
    # Create the Bitmap Shader and assign its texture path 
    bmpShader = c4d.BaseShader(c4d.Xbitmap)
    bmpShader[c4d.BITMAPSHADER_FILENAME] = texturePath
    
    # Insert this Bitmap Shader as a child of the Layer Shader
    bmpShader.InsertUnder(layerShader)
    
    # Add a new Shader Layer within the Layer Shader
    newLayer = layerShader.AddLayer(c4d.TypeShader)
    
    # Link the previously created Shader Layer to the Bitmap Shader previously created
    newLayer.SetParameter(c4d.LAYER_S_PARAM_SHADER_LINK, bmpShader)


def main() -> None:
    mat = doc.GetFirstMaterial()
    if not mat:
        return
    
    shd = mat.GetFirstShader()
    if shd.CheckType(c4d.Xlayer):
        # Update an existing Bimtap Layer
        ChangeTexturePathOfLayer(shd.GetFirstLayer(), r"C:\Users\m_adam\Desktop\random.jpg")
        
        # Create a new Bimtap Layer
        CreateBitmapLayer(shd, r"C:\Users\m_adam\Desktop\random.jpg")
        
        c4d.EventAdd()
        
    
if __name__ == '__main__':
    main()

If you want more insight of how the Layer Shader work I would advertise you to take a look at How to Create and Populate a Layer Shader? topic.

Cheers,
Maxime.

Hi @visualride you don't need anything specific when you update a texture path, just adding an EventAdd to notify Cinema 4D that something has changed is enough.

So find bellow a code that will update the first layer if this is a Bitmap Shader and then create a new Bitmap Shader Layer

import c4d

def ChangeTexturePathOfLayer(layer: c4d.LayerShaderLayer, texturePath: str) -> None:
    """Change the texture path of an existing Bitmap Layer
    """
    # Check if the layer is really a shader (bitmap being a shader)
    if layer.GetType() != c4d.TypeShader:
        raise ValueError("First layer is not a shader")
    
    # Retrieve the linked shader of this layer and check if this is really a bitmap shader
    bmpShader = layer.GetParameter(c4d.LAYER_S_PARAM_SHADER_LINK)
    if not bmpShader.CheckType(c4d.Xbitmap):
        raise ValueError("First layer is not a bitmap shader")
    
    # Update the texture path of the shader linked to this layer
    bmpShader[c4d.BITMAPSHADER_FILENAME] = texturePath
    
    
def CreateBitmapLayer(layerShader: c4d.LayerShader, texturePath: str) -> None:
    """Create a new Bitmap Layer in the given layerShader
    """
    # Create the Bitmap Shader and assign its texture path 
    bmpShader = c4d.BaseShader(c4d.Xbitmap)
    bmpShader[c4d.BITMAPSHADER_FILENAME] = texturePath
    
    # Insert this Bitmap Shader as a child of the Layer Shader
    bmpShader.InsertUnder(layerShader)
    
    # Add a new Shader Layer within the Layer Shader
    newLayer = layerShader.AddLayer(c4d.TypeShader)
    
    # Link the previously created Shader Layer to the Bitmap Shader previously created
    newLayer.SetParameter(c4d.LAYER_S_PARAM_SHADER_LINK, bmpShader)


def main() -> None:
    mat = doc.GetFirstMaterial()
    if not mat:
        return
    
    shd = mat.GetFirstShader()
    if shd.CheckType(c4d.Xlayer):
        # Update an existing Bimtap Layer
        ChangeTexturePathOfLayer(shd.GetFirstLayer(), r"C:\Users\m_adam\Desktop\random.jpg")
        
        # Create a new Bimtap Layer
        CreateBitmapLayer(shd, r"C:\Users\m_adam\Desktop\random.jpg")
        
        c4d.EventAdd()
        
    
if __name__ == '__main__':
    main()

If you want more insight of how the Layer Shader work I would advertise you to take a look at How to Create and Populate a Layer Shader? topic.

Cheers,
Maxime.

Perfect! Thank you so much!