[SOLVED]How to know channel



  • On 27/01/2017 at 02:27, xxxxxxxx wrote:

    In this post https://plugincafe.maxon.net/topic/9910/13352_get-base-object-input-id-for-link-fields&PID=52874#52874 Andreas describe a bit more how link are done in C4D.
    And since I worked on a texture manager like.

    I was wondering how the c4d one acutally know to which channel a bitmap is attached to.

    Here is a quick test but it's only give me the name where the texture is linked not his canal.

    import c4d
      
    def main() :
        mat = doc.GetFirstMaterial()
        desc = mat.GetDescription(c4d.DESCFLAGS_DESC_0)
        for bc, paramid, groupid in desc:
            if bc[c4d.DESC_SHADERLINKFLAG]:
                if mat[paramid[0].id]:
                    print bc[c4d.DESC_NAME]
     
    main()
    

    Moreover in this exemple this work from material to shader (is it possible to do from shader to material ?) I guess instead of passing a material and checking each link and then comparing with a shader, is it possible to get the parent of a linked shader(I guess no but cost nothing to ask)



  • On 27/01/2017 at 14:23, xxxxxxxx wrote:

    The shortest way is to test each channel's bool value: print mat[c4d.MATERIAL_USE_COLOR]
    You'll need to do this for each channel. But if you don't want to write each of them by hand. You can scrape them from the c4d descriptions.
    Just limit how many times you do that. Because there's a ton of them, and it could slow down your code if you iterate that huge list of descriptions too often in loops and such.

    This code also works with older versions.
    There might be a way to use GetDescription() in a similar way.
    But since I'm using R13. That is not an option for me.

    #This gets all of the enabled channels in a material  
      
    import c4d  
    def main() :      
      
      mat = doc.GetFirstMaterial()  
      if mat is None: return False  
        
      #Because iterating the descriptions is labor intensive(it's a very long list)  
      #We only want to do it once  
      #So we store the small list of channel description enums in this much smaller list      
      channels = []  
      
      #Iterate through all of the descriptions that are registered in C4D      
      for i, v in c4d.__dict__.iteritems() :  
            
          #If the description ID belongs to one of the channel ID#s  
          #And the channel is enabled  
          #Print the name and ID of the channel  
          if i.startswith("MATERIAL_USE_") :  
              if mat[v] == 1:  
                  matName = mat.GetName()  
                  ch = "c4d." + i                 
                  channels.append(ch)  
                  print matName, ch, v  
                    
      #From now on...Use the small channels list rather than iterating the huge c4d.__dict__  
        
      c4d.EventAdd()  
      
    if __name__=='__main__':  
      main()
    

    -ScottA



  • On 28/01/2017 at 04:15, xxxxxxxx wrote:

    Thanks scotta it can be a workaround, but is not exactly what I want. And for exemple with a vray shader it didn't work (he jsut told me use specular) not the channel name.

    So i guess it's not like that c44d work. http://img11.hostingpics.net/pics/262976texturemanager.png As you could see in this screen it put the channel name.

    A workaround could be to do it manually by telling if any bitmap is a child of this linkfield than it's in specular if it's into this linkfield it's in transparency... But it's a bit boring (but it would work for sure.)



  • On 28/01/2017 at 07:25, xxxxxxxx wrote:

    I was asking a sort of similar question recently about the pop up menu.
    I didn't like having to dig all the way into the shader link just to get the type of shader the user added. It seemed to be not logical to me, and it would be quicker to get that from the item the user clicked on. But Sebastien says that's how we must to do it.

    Regarding your other question about getting the channel from the shader(going backwards).
    I don't think there's any way to do that because those channels are actually just simple checkbox gizmos. The same ones we use when making our plugins. And they are not bound to anything special. They're just simple boolean switches for determining which code blocks to run in the code.
    When I wrote my own custom material plugin I was surprised to learn that those check boxes weren't coded to interact with the rest of the plugin in any special way. They're just plain old check box gizmos. So AFAIK there's no actual code we can use to get the channel from a shader link.

    -ScottA



  • On 30/01/2017 at 02:02, xxxxxxxx wrote:

    Hi,

    as Scott already pointed out, there's no direct way to get from shader to channel (as Scott said, the channels are more a UI point of view).

    A suggestion, what you could do:
    Build yourself mapping tables per material type (this needs to be done only once), which store the DescID of the "first level" shader slots and the channel ID these are located in. Then, when you want to know, which channel a certain BitmapShader is in, you can use GetMain() (maybe several times) to get to the "first level" shader slot and to the material. And then you can look up in your mapping table, which channel the shader is located in.



  • On 31/01/2017 at 02:52, xxxxxxxx wrote:

    Make the mapping table it's what i'm afraid of... I'm lazy I prefer do multiple loop than boring stuff (moreover I have to done it with multiple shaders / render engine) but it's ok here is an exemple with c4d material

    import c4d
      
      
    def get_channel_from_shader(shader) :
        mat = shader.GetMain() 
        parent_shader = shader
      
        var=1
        while var == 1:
            buffer_sha = parent_shader.GetUp()
            if buffer_sha:
                parent_shader = buffer_sha
            else:
                break
        
        if parent_shader is None:
            return
      
        data = list()
        text = "Unknown"           
        if mat.CheckType(c4d.Mmaterial) :
            text = "c4dMat::"
            data = [
                {"link_id": [
                    c4d.MATERIAL_COLOR_SHADER],
                 "value": "Color"},
      
                {"link_id": [
                    c4d.MATERIAL_DIFFUSION_SHADER],
                 "value": "Diffusion"},
      
                {"link_id": [
                    c4d.MATERIAL_LUMINANCE_SHADER],
                    "value": "Luminance"},
      
                {"link_id": [
                    c4d.MATERIAL_TRANSPARENCY_SHADER],
                    "value": "Transparency"},
      
                {"link_id": [
                    c4d.REFLECTION_LAYER_COLOR_TEXTURE,
                    c4d.REFLECTION_LAYER_TRANS_TEXTURE],
                    "value": "Reflexion"},
      
                {"link_id": [
                    c4d.MATERIAL_ENVIRONMENT_SHADER],
                    "value": "Environment"},
      
                {"link_id": [
                    c4d.MATERIAL_BUMP_SHADER],
                    "value": "Bump"},
      
                {"link_id": [
                    c4d.MATERIAL_NORMAL_SHADER],
                    "value": "Normal"},
      
                {"link_id": [
                    c4d.MATERIAL_ALPHA_SHADER],
                    "value": "Alpha"},
      
                {"link_id": [
                    c4d.MATERIAL_DISPLACEMENT_SHADER],
                    "value": "Displace"}
            ]
        
        leave = False
        for x in data:
            for link in x["link_id"]:
                if mat[link] == parent_shader:
                    text += str(x["value"])
                    leave = True
                    break
            if leave:
                break
            
        return text
      
    def main() :
        sha = doc.GetFirstMaterial().GetFirstShader()
        print get_channel_from_shader(sha)
      
    if __name__=='__main__':
        main()
    

    Btw you said GetMain() get the first level but GetMain always return the holder of the shader (so the basematerial).
    You probably want to said GetUp ;)

    Anyway thanks for everything.



  • On 31/01/2017 at 06:27, xxxxxxxx wrote:

    Something like you describe as getMain( which return the direct parent will be a nice feature). Since GetUp() is not trustable.
    Btw here is a workaround using directly linkfield. (Was a bit tricky to code hope it's understable)

    import c4d
      
      
    def get_linked_shader(shader, all_shader_list=list()) :
        linkedData = get_linked_data(shader)
        all_shader_list += linkedData
      
        for sha in linkedData:
            linkedData = get_linked_shader(sha, all_shader_list)
            for linked_sha in linkedData:
                if linked_sha not in all_shader_list:
                    all_shader_list.append(linked_sha)
      
        return all_shader_list
      
    def get_linked_data(shader) :
        buffer = list()
      
        bc = shader.GetDataInstance()
        for i in xrange(len(bc)) :
            index = bc.GetIndexId(i)
      
            linked = bc.GetLink(index, doc, c4d.Xbase)
            if linked:
                buffer.append(linked)
      
        return buffer
      
    def get_channel_from_shader(shader) :
        mat = shader.GetMain() 
        parent_shader = shader
      
        var=1
        while var == 1:
            buffer_sha = parent_shader.GetUp()
            if buffer_sha:
                parent_shader = buffer_sha
            else:
                break
        
        if parent_shader is None:
            return
      
        data = list()
        text = "Unknown"           
        if mat.CheckType(c4d.Mmaterial) :
            text = "c4dMat::"
            data = [
                {"link_id": [
                    c4d.MATERIAL_COLOR_SHADER],
                 "value": "Color"},
      
                {"link_id": [
                    c4d.MATERIAL_DIFFUSION_SHADER],
                 "value": "Diffusion"},
      
                {"link_id": [
                    c4d.MATERIAL_LUMINANCE_SHADER],
                    "value": "Luminance"},
      
                {"link_id": [
                    c4d.MATERIAL_TRANSPARENCY_SHADER],
                    "value": "Transparency"},
      
                {"link_id": [
                    c4d.REFLECTION_LAYER_COLOR_TEXTURE,
                    c4d.REFLECTION_LAYER_TRANS_TEXTURE],
                    "value": "Reflexion"},
      
                {"link_id": [
                    c4d.MATERIAL_ENVIRONMENT_SHADER],
                    "value": "Environment"},
      
                {"link_id": [
                    c4d.MATERIAL_BUMP_SHADER],
                    "value": "Bump"},
      
                {"link_id": [
                    c4d.MATERIAL_NORMAL_SHADER],
                    "value": "Normal"},
      
                {"link_id": [
                    c4d.MATERIAL_ALPHA_SHADER],
                    "value": "Alpha"},
      
                {"link_id": [
                    c4d.MATERIAL_DISPLACEMENT_SHADER],
                    "value": "Displace"}
            ]
        
        leave = False
        for x in data:
            for link in x["link_id"]:
                if mat[link]:
                    list_linked = get_linked_shader(mat[link])
                    if parent_shader in list_linked or parent_shader == mat[link]:
                        text += str(x["value"])
                        leave = True
                        break
            if leave:
                break
            
        return text
      
      
    def main() :
        sha = doc.GetFirstMaterial().GetFirstShader()
        print get_channel_from_shader(sha)
      
    if __name__=='__main__':
        main()
    

Log in to reply