Modifiying the normal-tag



  • On 20/02/2014 at 05:26, xxxxxxxx wrote:

    Hey guys.
    I'm trying to understand how the normal-tag works and I stumble right at the beginning.

    import c4d  
      
    def main() :  
      
    tag = op.GetTag(c4d.Tnormal)  
    ndata = tag.GetAllHighlevelData()  
    tag.SetAllHighlevelData(ndata)  
    c4d.EventAdd()  
      
    if __name__=='__main__':  
    main()
    

    This piece of code is just for reading the data and writing it back to the tag.
    But all I get is this error message:

    ValueError: The passed object has not the same length
    

    Does somebody know what this means? The data should be exactly the same and should be compatible.
    Thanks
    Phil



  • On 16/03/2014 at 07:12, xxxxxxxx wrote:

    Well, that is a little bit tricky, it took me a while to figure it out how to set the normal vectors at the normaltag.
    Here are my utility functions for setting them (you have to implement similar functions for getting them.

      
    def float2bytes(f) :   
        int_value = int(math.fabs(f * 32000.0))   
        high_byte = int(int_value / 256)   
        low_byte = int_value - 256 * high_byte   
        if f < 0:   
            high_byte = 255-high_byte   
            low_byte = 255-low_byte   
        return (low_byte,high_byte)   
      
    def set_normals(normal_tag,polygon,normal_a,normal_b,normal_c,normal_d) :   
        normal_list = [normal_a,normal_b,normal_c,normal_d]   
        normal_buffer = normal_tag.GetLowlevelDataAddressW()   
        vector_size = 6   
        component_size = 2   
        for v in range(0,4) :   
            normal = normal_list[v]   
            for c in range(0,3) :   
                low_byte, high_byte = float2bytes(normal[c])   
                normal_buffer[normal_tag.GetDataSize()*polygon+v*vector_size+c*component_size+0] = chr(low_byte)   
                normal_buffer[normal_tag.GetDataSize()*polygon+v*vector_size+c*component_size+1] = chr(high_byte)   
      
    


  • On 17/03/2014 at 01:49, xxxxxxxx wrote:

    I was afraid something with byte-reading and -writing would be inevitable.
    Thank you so much for you code, I'll try to wrap my head around it.
    I still don't get why the "SetAllHighlevelData()" funtction does not work as expected. At least it should accept the data you read using the "GetAllHighlevelData()" function.



  • On 17/03/2014 at 11:53, xxxxxxxx wrote:

    Isn't the normal tag a read-only tag?
    I've always thought it was.

    -ScottA



  • On 17/03/2014 at 15:04, xxxxxxxx wrote:

    I had exactly the same problem as hgcafe, as I wanted to set the normals with the "HighLevelData" functions which did not work at all (in Python). After some searching I found a C++ version of a similar code and tried to understand how the normals are stored. The main issue is to understand that a float value has to be converted to a 2byte signed integer. The normal vector has to be normalized, for each component a mapping between -1.0 ... 1.0 to -32000 ... 32000 has to be calculated. As the normaltag is a variabletag, the lowlevel functions

      
        VariableTag.GetLowlevelDataAddressW()   
        VariableTag.GetLowlevelDataAddressR()   
    

    can be used to get and set the bytes of a byte sequence. But be careful with that...



  • On 17/03/2014 at 17:29, xxxxxxxx wrote:

    I'm having some trouble using your custom set_normals() method.
    It's throwing a type error.
    Any idea why? Am I using it wrong?

    import c4d  
    def float2bytes(f) :  
      int_value = int(math.fabs(f * 32000.0))  
      high_byte = int(int_value / 256)  
      low_byte = int_value - 256 * high_byte  
      
      if f < 0:  
          high_byte = 255-high_byte  
          low_byte = 255-low_byte  
      
      return (low_byte,high_byte)  
      
      
    def set_normals(normal_tag,polygon,normal_a,normal_b,normal_c,normal_d) :  
      normal_list = [normal_a,normal_b,normal_c,normal_d]  
      normal_buffer = normal_tag.GetLowlevelDataAddressW()  
      vector_size = 6  
      component_size = 2  
      
      for v in range(0,4) :  
          normal = normal_list[v]  
      
          for c in range(0,3) :  
              low_byte, high_byte = float2bytes(normal[c]) #<--- TypeError: int is unscriptable  
      
              normal_buffer[normal_tag.GetDataSize()*polygon+v*vector_size+c*component_size+0] = chr(low_byte)  
              normal_buffer[normal_tag.GetDataSize()*polygon+v*vector_size+c*component_size+1] = chr(high_byte)  
      
    def main() :  
        
      obj = doc.GetActiveObject()  
      normalTag = obj.GetFirstTag() #Assuming the normal tag is the first tag  
      polys = obj.GetAllPolygons()  
      
      #Change the normals for the first polygon in the normal tag  
      set_normals(normalTag, polys[0], polys[0].a, polys[0].b, polys[0].c, polys[0].d)  
      
    if __name__=='__main__':  
      main()
    

    -ScottA



  • On 18/03/2014 at 01:57, xxxxxxxx wrote:

    Just to add something here, the function "SetAllHighlevelData()" works and you can set normals, just only some of them.
    Let's say we have only polygon, according to the documentation there should be 12 integers to set (for each of the 4 vectors 3 values (x,y,z)). Reading the normals using the "GetAllHighlevelData()" function you get an integer array with the length of 12 as expected. Trying to set the normals using this array throws the error I mentioned before. When you only set an array with the length of 1 the function works (but isn't useful at all, because you can only set the x-value of the first vector).
    If you have 2 polygons, you are able to set an array with the length of 2, 3 polygons -> array with the length of 3, and so on. Maybe there is a trick to this, I don't know. But in fact you can set normals using this mehtod, only not the way you'd expect.
    Also a funny thing: "GetAllHighlevelData()" seems to scramble the order of the normals if you have more than one polygon. I don't think this is intended. Anyway it's easier to get just the normals using the "CreatePhongNormals()"-function.
    I'll give reverend's approach a try as soon as I have time.
    Phil



  • On 18/03/2014 at 02:16, xxxxxxxx wrote:

    sorry, I did not explain how to call my functions...
    and thanks hgcafe for pointing out what works with the highlevel functions.
    I made the implementation similar to the uvwtag.SetSlow() function (for the parameters to use), so setting the normals for points a,b,c,d of polygon 0 to the "up" vector (0,1,0) would be:

      
            normal_a = c4d.Vector(0.0,1.0,0.0)   
            normal_b = c4d.Vector(0.0,1.0,0.0)   
            normal_c = c4d.Vector(0.0,1.0,0.0)   
            normal_d = c4d.Vector(0.0,1.0,0.0)   
            set_normals(normal_tag,0,normal_a,normal_b,normal_c,normal_d)   
    

    so set_normals expects a normal tag as 1st parameter, a polygon index number (int) as the 2nd parameter and the 4 normalized normal vectors.
    Unfortunately there is no SetSlow() for a normal tag...



  • On 18/03/2014 at 08:40, xxxxxxxx wrote:

    Hmm. Nope.
    That just changes the error to this: TypeError: 'c4d.Vector' object is unsubscriptable

    -ScottA



  • On 18/03/2014 at 10:35, xxxxxxxx wrote:

    hmm, which version of Cinema4D do you use?
    iterating over Vector components was introduced in R14.014.
    see the current Python SDK.

      
    Vector.__getitem__(self, key)   
      
        New in version R14.014.   
      
        Gets X, Y and Z components by index.   
      
      
    Note: The Vector objects does not support item deletion. For instance by calling del(v[0]).   
    Parameters:        
      
        key (int) – The component index.   
        value (float) – The new vector component.   
      
    Raises IndexError:   
            
      
    If the index key is out of range : 0<=key<2.   
    

    if you have a previous version you might to have to change the code...



  • On 18/03/2014 at 10:42, xxxxxxxx wrote:

    maybe its another problem...
    in your code you wrote:

      
        polys = obj.GetAllPolygons()   
      
        #Change the normals for the first polygon in the normal tag   
        set_normals(normalTag, polys[0], polys[0].a, polys[0].b, polys[0].c, polys[0].d)   
    

    but polys is a list of CPolygons and therefore polys[0].a etc is an int and not a c4d.Vector.
    You need to support c4d.Vector's for normal_a (and so on) as in my example call.



  • On 18/03/2014 at 12:05, xxxxxxxx wrote:

    I'm using R13.
    I'll keep trying to figure it out.
    You've posted enough code that I should be able to figure it out eventually. It's just going to take me some time and effort to re-write it so it works for me.

    Thanks a lot for the help and the code.
    -ScottA



  • On 18/03/2014 at 13:07, xxxxxxxx wrote:

    I think a modification like this should be enough to try it out:

      
            component = [normal.x,normal.y,normal.z]   
            for c in range(0,3) :   
                low_byte, high_byte = float2bytes(component[c])   
    


  • On 18/03/2014 at 14:43, xxxxxxxx wrote:

    Yes. That fixed it.
    Thanks a lot for the extra effort.🍺

    I'll post my complete working script code so that way people can at least have something that works that they can cut and paste into their script manager to start off with.

    import c4d  
    import math  
      
    def float2bytes(f) :  
      int_value = int(math.fabs(f * 32000.0))  
      high_byte = int(int_value / 256)  
      low_byte = int_value - 256 * high_byte  
      
      if f < 0:  
          high_byte = 255-high_byte  
          low_byte = 255-low_byte  
      
      return (low_byte,high_byte)  
      
      
    def set_normals(normal_tag,polygon,normal_a,normal_b,normal_c,normal_d) :  
      
      normal_list = [normal_a, normal_b, normal_c, normal_d]  
      normal_buffer = normal_tag.GetLowlevelDataAddressW()  
      vector_size = 6  
      component_size = 2  
      
      for v in range(0,4) :  
          normal = normal_list[v]  
          component = [normal.x, normal.y, normal.z]  
      
          for c in range(0,3) :  
              low_byte, high_byte = float2bytes(component[c])  
      
              normal_buffer[normal_tag.GetDataSize()*polygon+v*vector_size+c*component_size+0] = chr(low_byte)  
              normal_buffer[normal_tag.GetDataSize()*polygon+v*vector_size+c*component_size+1] = chr(high_byte)  
      
      
    def main() :  
        
      obj = doc.GetActiveObject()  
      normal_tag = obj.GetFirstTag() #Assuming the normal tag is the first tag  
      
      #These four variabls will be used to change the normal values of a polygon  
      normal_a = c4d.Vector(0.0,1.0,0.0)  
      normal_b = c4d.Vector(0.0,1.0,0.0)  
      normal_c = c4d.Vector(0.0,1.0,0.0)  
      normal_d = c4d.Vector(0.0,1.0,0.0)  
      
      #Set the first polygon normals in the normals tag using the above values  
      set_normals(normal_tag, 0, normal_a, normal_b, normal_c, normal_d)  
        
      obj.Message(c4d.MSG_UPDATE)  
      c4d.EventAdd()  
      
    if __name__=='__main__':  
      main()
    

    -ScottA



  • On 19/03/2014 at 03:03, xxxxxxxx wrote:

    Hey guys. Thank you very much for your replies and input.
    I had time to test reverend's approach and it works. Really cool. Now editing the phong-normals should be a breeze.
    Phil


Log in to reply