Finding duplicate materials - Octane Render



  • Hi @Cairyn,

    Thank you very much for your thorough explanation! 😃
    That makes a lot a sense to me and seems to be the way to proceed with this.
    I will give it a go and see where it leads me.

    Have a good one and thanks again!



  • Hi all,

    So I after a bit of tinkering and based on what @Cairyn explained, this is the outcome.
    Please do let me know if I can improve on something.

    My only doubt is regarding the PyCObject types and what to do with it. In the code below I'm just ignoring it as I can't do anything with it anyway in a Python data context (I think!) and it doesn't seem to create any unusal cases when comparing the materials values.

    import c4d
    
    
    def get_data(data, lst):
        """
        Get the parameters from the materials container and return a list.
        """
    
        for i in range(len(data) - 1):
            index = data.GetIndexId(i)
            try:
                if isinstance(data[index], (c4d.BaseShader, c4d.BaseTime)):
                    sub_data = data[index].GetDataInstance()
                    get_data(sub_data, lst)
                else:
                    if type(data[index]).__name__ == 'PyCObject':
                        continue
                    lst.append(data[index])
            except AttributeError:
                continue
    
        return lst
    
    
    def compare_data(lst_a, lst_b):
        """
        Compare the data between lists a and b. If data is the same, than material is a duplicate.
        """
    
        zipped = zip(lst_a, lst_b)
    
        for params in zipped:
            if params[0] != params[1]:
                return False
    
        return True
    
    def main():
    
        mat_lst = doc.GetMaterials()
        duplicate_materials = []
    
        for a, mat_a in enumerate(mat_lst):
            data_a = get_data(mat_a, [])
    
            for b, mat_b in enumerate(mat_lst):
                if a != b:
    
                    data_b = get_data(mat_b, [])
                    data_difference = compare_data(data_a, data_b)
    
                    if data_difference:
                        duplicate_materials.append(mat_b)
    
    
        assert not duplicate_materials, "You have {0} materials duplicated! " \
                                        "See list below:\n\n{1}".format(len(duplicate_materials),
                                                                        ", ".join(mat.GetName() for mat in duplicate_materials))
                                                                        
    if __name__ == '__main__':
    
        main() 
    

    Hope this helps anyone else!

    Cheers! 😃



  • Thanks @AndreAnjos for coming up with the final code.

    Two notes:

    • first reading your initial question: was hard to understand what you meant with regard to "duplicated" materials and how you were defining the equality among twos. Standing on the solution you presented, basically two materials are identical only and only if the values of all their parameters are equal
    • second the get_data function expects two arguments, whilst, in your code, only one is given to both calls in the main.

    Finally, as pointed out by @PluginStudent, BaseMaterial::Compare() actually does exactly what you're trying to achieve, and any material implementing MaterialData can be checked via this method belonging to the BaseMaterial class to which they inherit from (independently from the rendering engine they belong and assuming they following up with the best practice of storing data in BaseContainers)

    The Compare actually does:

    • compares BaseChannels
    • compares BaseContainers
    • compares Ckeys found in CCurves belonging to CTrack.

    Only when these three checks give all positive results, the two materials are considered identical

    Best, R



  • Hi @r_gigante,

    Thank you very much for pointing stuff out! 😃

    first reading your initial question: was hard to understand what you meant with regard to "duplicated" materials and how you were defining the equality among twos. Standing on the solution you presented, basically two materials are identical only and only if the values of all their parameters are equal

    Apologies for this! Should have been more specific with my description.

    second the get_data function expects two arguments, whilst, in your code, only one is given to both calls in the main.

    This has now been updated! Thanks for pointing it out.

    Finally, as pointed out by @PluginStudent, BaseMaterial::Compare() actually does exactly what you're trying to achieve, and any material implementing MaterialData can be checked via this method belonging to the BaseMaterial class to which they inherit from (independently from the rendering engine they belong and assuming they following up with the best practice of storing data in BaseContainers)
    The Compare actually does:

    • compares BaseChannels
    • compares BaseContainers
    • compares Ckeys found in CCurves belonging to CTrack.

    Only when these three checks give all positive results, the two materials are considered identical

    Great! Thank you for your explanation! Did not know if that would be the case since my tests were not coming with the results. So will give it another go!

    Thanks again! 🙂



  • Hi @r_gigante

    I must be really stupid here, but can't have the result you are saying with this function.
    A simple test of having 2 supposedly duplicate materials and still returns False.

    import c4d
    
    def main():
    
        mat_lst = doc.GetMaterials()
        result = mat_lst[0].Compare(mat_lst[1])
        print(result)
    
    if __name__ == '__main__':
        main()
    

    I must be missing something very obvious and I can't figure it out. 😆

    Thanks for your help!



  • Also as an update!
    I've been talking with a couple of artists here and after a few simple tests using the Delete Duplicate Materials in Cinema R19 and R21, does not work as well.

    Is this a common issue?

    Thanks!



  • Hi @AndreAnjos, looking at the code it seems I've spotted a bug but it's still under investigation. I'll let you know if that's the case and report back.

    Cheers, R



  • Hi @r_gigante,

    Any luck with this? It's no rush at all for us, just curious if you have an update 😃

    Cheers!

    Andre



  • Hi @AndreAnjos, no updates so far.

    Cheers, R.



  • Hi @r_gigante,

    Thanks for letting me know!

    Andre


Log in to reply