SOLVED Get shader's parent ID

Hi all,

Is it possible to get the parent ID of a shader?
For example have a image texture and want to know if is in the diffuse or opacity channel, etc?

Cheers! ☺

Andre

Hey,

if I do understand your initial question correctly the linked post does not contain your answer. While both BaseShaders and BaseMaterials are GeListNodes only BaseShaders that are attached to each other (a noise shader in a fusion shader for example) are in child - parent relationship. BaseMaterials do not carry their associated BaseShaders as children.

While it is possible to figure out the hosting material for a shader by walking over all materials in the document, actually implementing that will be probably more work than you expect as you have to accommodate the mess that is Cinemas material system under the hood: reflectance channel, layer shader, multiple material types, and the list goes on.

Let me clarify this (before an angry maxon employee points out that this all follows a bigger plan :)): I always felt that dealing with the material system is a bit of mess 😉

Cheers
zipit

@zipit said in Get shader's parent ID:

Hey,

if I do understand your initial question correctly the linked post does not contain your answer. While both BaseShaders and BaseMaterials are GeListNodes only BaseShaders that are attached to each other (a noise shader in a fusion shader for example) are in child - parent relationship. BaseMaterials do not carry their associated BaseShaders as children.

While it is possible to figure out the hosting material for a shader by walking over all materials in the document, actually implementing that will be probably more work than you expect as you have to accommodate the mess that is Cinemas material system under the hood: reflectance channel, layer shader, multiple material types, and the list goes on.

Let me clarify this (before an angry maxon employee points out that this all follows a bigger plan :)): I always felt that dealing with the material system is a bit of mess 😉

Cheers
zipit

Hi Zipit,

Thanks for your help and reply!
Yes you are absolutely correct in regards of the relationship between BaseShaders and BaseMaterials (and how the material system works ;)). Perhaps I should have explained it better above, before marking it as solved. So thank you for that! 👍

The solution I linked seems to be doing the opposite, where it checks the channel first and if enabled and have a shader, returns the channel. (Think this is what is written, but please correct me if i'm wrong! :))

In my case, I have a specific attribute I want to change with all of the Octane materials. The Image Texture node has a type attribute that need changing to float for specific channels. This is reduce to memory when Octane is loading and rendering textures.

Have to think of a different solution and come back...

Thanks for your help again! ☺

Andre

Hi,

ah, ok, that was indeed a misunderstanding then. I was just pointing out that you cannot go (easily) from a BaseShader instance to its associated BaseMaterial but you can of course retrieve all shaders associated with a BaseList2D via .GetFirstShader (which includes a BaseMaterial).

Cheers
zipit

@zipit said in Get shader's parent ID:

Hi,

ah, ok, that was indeed a misunderstanding then. I was just pointing out that you cannot go (easily) from a BaseShader instance to its associated BaseMaterial but you can of course retrieve all shaders associated with a BaseList2D via .GetFirstShader (which includes a BaseMaterial).

Cheers
zipit

Hi Zipit,

Absolutely correct! Thank you! 👍

I've actually end up hardcoding it a bit. To be honest it works for now and if needed to be changed, it's just a matter of changing the channels list. As such:

import c4d

count = 0
total_size = 0.00


def change_to_float(mat):
    """
    If channel contains linked textures, change attribute type to Float.
    """

    channels = [c4d.OCT_MATERIAL_ROUGHNESS_LINK,
                c4d.OCT_MATERIAL_SPECULAR_LINK,
                c4d.OCT_MAT_SPECULAR_MAP_LINK,
                c4d.OCT_MATERIAL_BUMP_LINK,
                c4d.OCT_MATERIAL_DISPLACEMENT_LINK,
                c4d.OCT_MIXMATERIAL_DISPLACEMENT_LINK,
                c4d.BLENDMAT_DISPLACEMENT_LINK,
                c4d.OCT_MATERIAL_OPACITY_LINK,
                c4d.OCT_MAT_REFLECTION_LINK,
                c4d.MIXMATERIAL_AMOUNT_LNK]

    for channel in channels:
        if mat[channel]:

            linked = mat[channel]

            if channel == c4d.OCT_MATERIAL_DISPLACEMENT_LINK or \
                    channel == c4d.BLENDMAT_DISPLACEMENT_LINK or \
                    channel == c4d.OCT_MIXMATERIAL_DISPLACEMENT_LINK:

                linked = linked[c4d.DISPLACEMENT_INPUT]

            if linked is not None and linked[c4d.IMAGETEXTURE_MODE] == 0:

                linked[c4d.IMAGETEXTURE_MODE] = 1  # Float
                path = linked[c4d.IMAGETEXTURE_FILE]
                bmp = c4d.bitmaps.BaseBitmap()
                bmp.InitWith(path)

                x, y = bmp.GetSize()  # Gets the X and Y dimensions of the image

                size_bmp = (x * y) / 1024
                global total_size
                total_size += size_bmp

                global count
                count += 1

    return count, total_size


def main():
    """
    Main function for textureTypeFloat().
    """

    mat = DOC.GetFirstMaterial()
    c4d.gui.SetMousePointer(c4d.MOUSE_NORMAL)

    if not mat:
        c4d.gui.MessageDialog("No material(s) in the scene!")
        c4d.StatusSetText("No material(s) in the scene!")
        return False

    while mat:

        c4d.gui.SetMousePointer(c4d.MOUSE_BUSY)

        if mat.GetTypeName() == 'Octane Material' or \
                mat.GetTypeName() == 'Octane Mix Material' or \
                mat.GetTypeName() == 'Octane Blend Material':

            count, total_size = change_to_float(mat)
            print("{0} Checked!".format(mat.GetName()))

        mat = mat.GetNext()

    c4d.EventAdd()
    c4d.gui.SetMousePointer(c4d.MOUSE_NORMAL)
    c4d.gui.MessageDialog("{0} Shaders Changed! Saving {1:.2f} Mb!".format(count, (total_size / 1024)))


if __name__ == '__main__':
    main()

Thank you very much for your help! ☺

Andre

hello,
thanks @zipit ^^

just one thing, in the context of the script manager, you don't need DOC = c4d.documents.GetActiveDocument() there is already doc.

cheers,
Manuel.

@m_magalhaes said in Get shader's parent ID:

hello,
thanks @zipit ^^

just one thing, in the context of the script manager, you don't need DOC = c4d.documents.GetActiveDocument() there is already doc.

cheers,
Manuel.

Corrected! ☺

Thank you Manuel!