Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Adding polygon UVs & Normals.

    Cinema 4D SDK
    python r20
    2
    13
    2.5k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • S
      sheilan
      last edited by

      Hello,

      I've made a custom binary file format that contains model data similar to .OBJ (vertices, normals, uvs & indices for each triangle)
      I've managed to create the polygons with using the vertices via the faces indices, but adding Normals & UVs seems to be a little trickier. I've read the SDK about UVWTag but I can't seem to understand how to translate the two values used for each face (U & V) into a UVWTag via python.

      Any help appreciated!

      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by

        Hi @sheilan, UVs and Normals are stored in appropriate tags.
        UVs are stored per Polygon Vertex as described in the C++ UVWTag Manual. Find an example in python in this topic.
        Normals are stored in the same way, as described in the C++ NormalTag Manual. Find example in python in this topic.

        If you have any questions, please let me know and do not hesitate.
        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        S 1 Reply Last reply Reply Quote 0
        • S
          sheilan @m_adam
          last edited by sheilan

          @m_adam

          Thanks a lot!
          I've managed to do UV's thanks to the first method, trying my luck with Normals now.

          One more thing I'm curious about is Phong Tag. When I'm importing the same mesh in OBJ format, it seems to create Normal tag & Phong tag, both seem to affect the normals of the mesh. Now since I'm working with the same data, should I use the vertex normal data to generate both Normal tag & Phong tag, or would one be enough?

          Thanks,
          Sheilan.

          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by

            Phong tag is just for the display of the normals. So the viewport interpolates the normal set by the NormalTag.
            So basically you have to define the normal in the NormalTag and then apply a Phong tag that will tell the viewport to render how to read these normal data in order to smooth them.

            For more information see this following picture, this is the same sphere, same polygon but they get different shading.
            4d125755-57cc-411e-9c66-c3037e7bfe2d-image.png

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            S 1 Reply Last reply Reply Quote 0
            • S
              sheilan @m_adam
              last edited by sheilan

              This post is deleted!
              1 Reply Last reply Reply Quote 0
              • S
                sheilan
                last edited by sheilan

                Ok I seem to be doing something wrong with the Normals.

                Even though the Normal Tag is there, it doesn't seem to be doing anything.
                Here's the code (using functions from the post you posted)

                # Create UV Tag
                uv_tag = c4d.UVWTag(polyCount)
                # Create Normal Tag
                nrmTag = c4d.NormalTag(polyCount)
                # Create Normal List
                normalList = []
                
                for f in range(polyCount):
                    # Loading & Creating points from the binary file
                    ### I chose to not add this part as it's irrelevant
                
                    # Load UV's for first point
                    uv1 = vertices_uvs[tri_uvs[0]]
                    # Load UV's for second point
                    uv2 = vertices_uvs[tri_uvs[1]]
                    # Load UV's for third point
                    uv3 = vertices_uvs[tri_uvs[2]]
                    # Create polygon with 3 points
                    mypoly.SetPolygon(f, c4d.CPolygon(surface_vertices[vertex1],surface_vertices[vertex2],surface_vertices[vertex3]))
                    # Create poly UV
                    uv_tag.SetSlow(f, c4d.Vector(uv1[0], uv1[1], 0),
                    
                                c4d.Vector(uv2[0], uv2[1], 0),  
                    
                                c4d.Vector(uv3[0], uv3[1], 0),  
                    
                                c4d.Vector(0,0,0))  
                    
                    # Load Normals for first point
                    normal1 = vertices_normals[tri_normals[0]]
                    # Load Normals for second point    
                    normal2 = vertices_normals[tri_normals[1]]
                    # Load Normals for third point
                    normal3 = vertices_normals[tri_normals[2]]
                    
                    # Add normals to their points
                    a = c4d.Vector(normal1[0],normal1[1],normal1[2])
                    b = c4d.Vector(normal2[0],normal2[1],normal2[2])
                    c = c4d.Vector(normal3[0],normal3[1],normal3[2])
                    d = c4d.Vector() # I tried setting as empty vector or same as C (it's a triangle poly)
                
                    # Add normals to normalList
                    normalList.append(a)
                    normalList.append(b)
                    normalList.append(c)
                    normalList.append(d)
                
                # Set normalList to Normal Tag of object
                SetAllHighLevelNormals(nrmTag, normalList)
                mypoly.Message(c4d.MSG_UPDATE)
                # Add object
                doc.InsertObject(mypoly,None,None)
                doc.AddUndo(c4d.UNDOTYPE_NEW, mypoly)
                # Add UV Tag
                mypoly.InsertTag(uv_tag)
                doc.AddUndo(c4d.UNDOTYPE_NEW, uv_tag)
                # Add Normal Tag
                mypoly.InsertTag(nrmTag)
                doc.AddUndo(c4d.UNDOTYPE_NEW, nrmTag) 
                
                mypoly.Message(c4d.MSG_UPDATE)
                c4d.EventAdd()
                doc.EndUndo()
                

                I checked one of the normalLists before & after the conversion using the SetAllHighLevelNormals() function and those were the results
                before:

                [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0]
                

                after:

                [32000, 0, 0, 32000, 0, 0, 0, 0, 0, 32000, 0, 0, 32000, 0, 0, 32000, 0, 0, 0, 0, 0, 32000, 0, 0, 0, 32000, 0, 0, 32000, 0, 0, 0, 0, 0, 32000, 0, 0, 32000, 0, 0, 32000, 0, 0, 0, 0, 0, 32000, 0, 0, 0, 32000, 0, 0, 32000, 0, 0, 0, 0, 0, 32000, 0, 0, 32000, 0, 0, 32000, 0, 0, 0, 0, 0, 32000, 0, 33536, 0, 0, 33536, 0, 0, 0, 0, 0, 33536, 0, 0, 33536, 0, 0, 33536, 0, 0, 0, 0, 0, 33536, 0, 33536, 0, 0, 33536, 0, 0, 0, 0, 0, 33536, 0, 0, 33536, 0, 0, 33536, 0, 0, 0, 0, 0, 33536, 0, 0]
                

                UVW Tag works perfectly, Normal Tag doesn't.

                Thanks,
                Sheilan.

                1 Reply Last reply Reply Quote 0
                • M
                  m_adam
                  last edited by

                  This is due to a bug currently affecting NormalMap SetAllHighLevelNormals and GetAllHighLevelNormals the only workaround is the one described in the previous post I posted. This bug is going to be fixed for the next release.

                  Cheers Maxime

                  MAXON SDK Specialist

                  Development Blog, MAXON Registered Developer

                  S 1 Reply Last reply Reply Quote 0
                  • S
                    sheilan @m_adam
                    last edited by sheilan

                    @m_adam
                    I'm getting an error when trying to use the functions he later posted as a workaround.

                    Here's the updated code

                    # Create UV Tag
                    uv_tag = c4d.UVWTag(polyCount)
                    # Create Normal Tag
                    nrmTag = c4d.NormalTag(polyCount)
                    # Create Normal List
                    normalList = []
                    
                    for f in range(polyCount):
                        # Loading & Creating points from the binary file
                        ### I chose to not add this part as it's irrelevant
                    
                        # Load UV's for first point
                        uv1 = vertices_uvs[tri_uvs[0]]
                        # Load UV's for second point
                        uv2 = vertices_uvs[tri_uvs[1]]
                        # Load UV's for third point
                        uv3 = vertices_uvs[tri_uvs[2]]
                        # Create polygon with 3 points
                        mypoly.SetPolygon(f, c4d.CPolygon(surface_vertices[vertex1],surface_vertices[vertex2],surface_vertices[vertex3]))
                        # Create poly UV
                        uv_tag.SetSlow(f, c4d.Vector(uv1[0], uv1[1], 0),
                        
                                    c4d.Vector(uv2[0], uv2[1], 0),  
                        
                                    c4d.Vector(uv3[0], uv3[1], 0),  
                        
                                    c4d.Vector(0,0,0))  
                        
                        # Load Normals for first point
                        normal1 = vertices_normals[tri_normals[0]]
                        # Load Normals for second point    
                        normal2 = vertices_normals[tri_normals[1]]
                        # Load Normals for third point
                        normal3 = vertices_normals[tri_normals[2]]
                        
                        # Add normals to their points
                        a = c4d.Vector(normal1[0],normal1[1],normal1[2])
                        b = c4d.Vector(normal2[0],normal2[1],normal2[2])
                        c = c4d.Vector(normal3[0],normal3[1],normal3[2])
                        d = c4d.Vector() # I tried setting as empty vector or same as C (it's a triangle poly)
                    
                        # Add normals to normalList
                        normalList.append(a)
                        normalList.append(b)
                        normalList.append(c)
                        normalList.append(d)
                    
                    # Set normalList to Normal Tag of object
                    SetAllHighLevelNormals(nrmTag, normalList)
                    normalList = GetLowLevelNormals(nrmTag,mypoly)
                    SetAllHighLevelNormals(nrmTag, normalList)
                    
                    mypoly.Message(c4d.MSG_UPDATE)
                    # Add object
                    doc.InsertObject(mypoly,None,None)
                    doc.AddUndo(c4d.UNDOTYPE_NEW, mypoly)
                    # Add UV Tag
                    mypoly.InsertTag(uv_tag)
                    doc.AddUndo(c4d.UNDOTYPE_NEW, uv_tag)
                    # Add Normal Tag
                    mypoly.InsertTag(nrmTag)
                    doc.AddUndo(c4d.UNDOTYPE_NEW, nrmTag) 
                    mypoly.Message(c4d.MSG_UPDATE)
                    c4d.EventAdd()
                    doc.EndUndo()
                    

                    I get TypeError: unsupported operand type(s) for *: 'int' and 'c4d.PolygonObject'
                    I'm rather confused about what type of variable polygon is in his function as theres no context.

                    1 Reply Last reply Reply Quote 0
                    • M
                      m_adam
                      last edited by m_adam

                      Hi @sheilan, NormalTag is a bit special, as I said there is currently a bug in S/GetAllHighlevelData for NormalTag so you need to use S/GetLowlevelDataAddressR/W.

                      With that's said since you access the low-level data, the current data structure for the normal tag is a list of int16. Then a polygon (whatever if it's a tri or a quad) is represented by 12 int16 such in order to give use 4 vectors (4 vectors x 3 values for each vector = 12 values and in this case 12 int16)

                      Not sure it's very clear however here it's a code.

                      """
                      Copyright: MAXON Computer GmbH
                      Author: Maxime Adam
                      
                      Description:
                          - Reads and Write the Raw Data of a Normal Tag.
                          - Normals are stored for each vertex of each polygon.
                          - Raw Data normal structure for one polygon is 12 int16 value (4 vectors for each vertex of a Cpolygon * 3 component for each vector) even if the Cpolygon is a Triangle.
                      
                      Class/method highlighted:
                          - c4d.NormalTag
                          - c4d.VariableTag.GetLowlevelDataAddressR()
                          - c4d.VariableTag.SetLowlevelDataAddressR()
                      
                      Compatible:
                          - Win / Mac
                          - R13, R14, R15, R16, R17, R18, R19, R20
                      """
                      import c4d
                      import array
                      
                      
                      def CreateNormalTag(op):
                          """
                           Creates a NormalTag on the passed PolygonObject.
                      
                          :param op: The PolygonObject that will received a normal Tag.
                          :type op: c4d.PolygonObject
                          :return: The created tag.
                          :rtype: c4d.NormalTag
                          """
                          # Checks if the passed object is a polygon object.
                          if not isinstance(op, c4d.PolygonObject):
                              raise TypeError("op is not a c4d.PolygonObject.")
                          
                          # Retrieves the polygonCount
                          polyCnt = op.GetPolygonCount()
                      
                          # Creates a Normal Tag in memory only
                          normalTag = c4d.NormalTag(polyCnt)
                          if normalTag is None:
                              raise MemoryError("Failed to creates a normal Tag.")
                      
                          # Inserts the tag to the passed object
                          op.InsertTag(normalTag)
                      
                          # Notifies the object he need to update to take care of the newly created normal tag
                          op.Message(c4d.MSG_UPDATE)
                          return normalTag
                      
                      
                      def ReadNormalTag(tag):
                          """
                          Read the raw data stored in Normal Tag.
                      
                          :param tag: The Normal Tag to read the data from.
                          :type tag: c4d.NormalTag
                          :return: A list with all the raw data.
                          :rtype: list[int]
                          """
                          # Retrieves the read buffer array
                          buffer = tag.GetLowlevelDataAddressR()
                      
                          # Converts this BitSeq buffer to a list of short int (int16)
                          intArray = array.array('h')
                          intArray.fromstring(buffer)
                          data = intArray.tolist()
                      
                          # Returns the data
                          return data
                      
                      
                      def WriteNormalTag(tag, normalList):
                          """
                          Write the raw data to a Normal Tag.
                      
                          :param tag: The Normal Tag to write the data to.
                          :type tag: c4d.NormalTag
                          :param normalList: A list with all the raw data.
                          :type normalList: list[int]
                          """
                          # Retrieves the write buffer array
                          buffer = tag.GetLowlevelDataAddressW()
                          if buffer is None:
                              raise RuntimeError("Failed to retrieves internal write data for the normal tag.")
                      
                          # Translates list of short int 16 to a BitSeq (string are byte in Python 2.7)
                          intArray = array.array('h')
                          intArray.fromlist(normalList)
                          data = intArray.tostring()
                          buffer[:len(data)] = data
                      
                      
                      def main():
                          # Checks if selected object is valid
                          if op is None:
                              raise ValueError("op is none, please select one object.")
                      
                          # Checks if the selected object is a polygon object
                          if not isinstance(op, c4d.PolygonObject):
                              raise TypeError("op is not a c4d.PolygonObject.")
                      
                          # Creates a normal tag
                          tag = CreateNormalTag(op)
                      
                          # Retrieves the raw data stored into the tag (all values will be equal to 0 since we just created the normal tag)
                          rawNormalData = ReadNormalTag(tag)
                      
                          # Prints the current value stored in tag, since data are stored as int16 and not float you have to divide them by 32000.0
                          print [normal / 32000.0 for normal in rawNormalData]
                      
                          # Creates a list representing a float gradient value from 0 to 1 then remap these value from float to int16 by multiplying them
                          valueToSet = [int(float(normalID) / len(rawNormalData) * 32000.0) for normalID in xrange(len(rawNormalData))]
                      
                          # Writes the previous list to the normal tag.
                          WriteNormalTag(tag, valueToSet)
                      
                          # Reads back the normal stored in the normal tag, value should go from 0 to 1
                          print [normal / 32000.0 for normal in ReadNormalTag(tag)]
                      
                          c4d.EventAdd()
                      
                      
                      if __name__ == "__main__":
                          main()
                      

                      So in your case instead to write normals like

                      # Add normals to their points
                      a = c4d.Vector(normal1[0],normal1[1],normal1[2])
                      b = c4d.Vector(normal2[0],normal2[1],normal2[2])
                      c = c4d.Vector(normal3[0],normal3[1],normal3[2])
                      d = c4d.Vector() # I tried setting as empty vector or same as C (it's a triangle poly)
                      
                      # Add normals to normalList
                      normalList.append(a)
                      normalList.append(b)
                      normalList.append(c)
                      normalList.append(d)
                      

                      You will have to do

                      a = [normal1[0],normal1[1],normal1[2]]
                      b = [normal2[0],normal2[1],normal2[2]]
                      c = [normal3[0],normal3[1],normal3[2]]
                      d = [0.0, 0.0, 0.0] # Even if it's a Tri, you should pass a value.
                      
                      # Add normals to normalList
                      normalList.extend(a)
                      normalList.extend(b)
                      normalList.extend(c)
                      normalList.extend(d)
                      
                      # Maps data from float to int16 value
                      normalListToSet = [int(normal * 32000.0) for normal in normalList]
                      
                      # Writes the previous list to the normal tag.
                      WriteNormalTag(nrmTag, normalListToSet)
                      

                      Hope it helps, if you have any questions, please do not hesitate!
                      Cheers,
                      Maxime.

                      MAXON SDK Specialist

                      Development Blog, MAXON Registered Developer

                      S 1 Reply Last reply Reply Quote 1
                      • S
                        sheilan @m_adam
                        last edited by sheilan

                        Hello @m_adam ,

                        Skip to the edit below.

                        ────────────────────
                        Thanks again for the reply but the normal tag still doesn't seem to be doing anything.
                        Here's how the code looks right now :

                        # Looping through each surface (polygon object/mesh/model) in the scene
                        for i in range(surfaceCount):
                            #Create new polygon object
                            mypoly = c4d.BaseObject(c4d.Opolygon) #Create an empty polygon object
                            mypoly.SetName(materialName)
                            uv_tag = c4d.UVWTag(triCount)   
                            # triCount & materialName are variables I use to store the name of the object + the polycount
                            nrm_tag = CreateNormalTag(mypoly,triCount)
                            normalList = [] # Fresh new normalList created for each PolygonObject I'm working on
                        
                            # loop through each triangle/poly and gather it's data from my binary file
                            for f in range(triCount):
                                # The points, polys & uv creation part is unnecessary here so I skipped right to normals
                        
                                a = [normal1[0],normal1[1],normal1[2]]
                                b = [normal2[0],normal2[1],normal2[2]]
                                c = [normal3[0],normal3[1],normal3[2]]
                                d = [0.0, 0.0, 0.0] 
                        
                                # Add normals to normalList
                                normalList.extend(a)
                                normalList.extend(b)
                                normalList.extend(c)
                                normalList.extend(d)
                        
                            # After we are done looping through the triangles, all polys are created including their points, polygons, UVs & the normalList
                            normalListToSet = [int(normal * 32000.0) for normal in normalList] # set of all normals for this polygon object
                            WriteNormalTag(nrm_tag, normalListToSet)
                            nrm_tag = mypoly.GetTag(c4d.Tnormal) # This is probably unnecessary but I used it just in case
                        
                            # Retrieves the raw data stored into the tag
                            rawNormalData = ReadNormalTag(nrm_tag)
                        
                            # Prints the current value stored in tag, since data are stored as int16 and not float you have to divide them by 32000.0
                            print "current data stored in tag - should go from 0 to 1"
                            print [normal / 32000.0 for normal in rawNormalData]
                        
                            # Creates a list representing a float gradient value from 0 to 1 then remap these value from float to int16 by multiplying them
                            valueToSet = [int(float(normalID) / len(rawNormalData) * 32000.0) for normalID in xrange(len(rawNormalData))]
                        
                            # Writes the previous list to the normal tag.
                            WriteNormalTag(nrm_tag, valueToSet)
                        
                            print "new values of normal tag"        
                            print [normal / 32000.0 for normal in ReadNormalTag(nrm_tag)]
                        

                        Now here are results of few of the normal tags in the scene

                        old data stored in tag
                        
                        [0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.0, 0.0, 0.0, 0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.0, 0.0, 0.0, 0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.0, 0.0, 0.0, 0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.994125, -0.07825, -0.05478125, 0.0, 0.0, 0.0]
                        
                        new data of normal tag
                        
                        [0.0, 0.0208125, 0.04165625, 0.0625, 0.0833125, 0.10415625, 0.125, 0.1458125, 0.16665625, 0.1875, 0.2083125, 0.22915625, 0.25, 0.2708125, 0.29165625, 0.3125, 0.3333125, 0.35415625, 0.375, 0.3958125, 0.41665625, 0.4375, 0.4583125, 0.47915625, 0.5, 0.5208125, 0.54165625, 0.5625, 0.5833125, 0.60415625, 0.625, 0.6458125, 0.66665625, 0.6875, 0.7083125, 0.72915625, 0.75, 0.7708125, 0.79165625, 0.8125, 0.8333125, 0.85415625, 0.875, 0.8958125, 0.91665625, 0.9375, 0.9583125, 0.97915625]
                        
                        old data stored in tag
                        
                        [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0]
                        
                        new data of normal tag
                        
                        [0.0, 0.0208125, 0.04165625, 0.0625, 0.0833125, 0.10415625, 0.125, 0.1458125, 0.16665625, 0.1875, 0.2083125, 0.22915625, 0.25, 0.2708125, 0.29165625, 0.3125, 0.3333125, 0.35415625, 0.375, 0.3958125, 0.41665625, 0.4375, 0.4583125, 0.47915625, 0.5, 0.5208125, 0.54165625, 0.5625, 0.5833125, 0.60415625, 0.625, 0.6458125, 0.66665625, 0.6875, 0.7083125, 0.72915625, 0.75, 0.7708125, 0.79165625, 0.8125, 0.8333125, 0.85415625, 0.875, 0.8958125, 0.91665625, 0.9375, 0.9583125, 0.97915625]
                        
                        old data stored in tag
                        
                        [-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0]
                        
                        new data of normal tag
                        
                        [0.0, 0.0208125, 0.04165625, 0.0625, 0.0833125, 0.10415625, 0.125, 0.1458125, 0.16665625, 0.1875, 0.2083125, 0.22915625, 0.25, 0.2708125, 0.29165625, 0.3125, 0.3333125, 0.35415625, 0.375, 0.3958125, 0.41665625, 0.4375, 0.4583125, 0.47915625, 0.5, 0.5208125, 0.54165625, 0.5625, 0.5833125, 0.60415625, 0.625, 0.6458125, 0.66665625, 0.6875, 0.7083125, 0.72915625, 0.75, 0.7708125, 0.79165625, 0.8125, 0.8333125, 0.85415625, 0.875, 0.8958125, 0.91665625, 0.9375, 0.9583125, 0.97915625]
                        
                        old data stored in tag
                        
                        [-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7064375, 0.7064375, 0.0, 0.7064375, 0.7064375, 0.0, 0.7064375, 0.7064375, 0.0, 0.0, 0.0, 0.0, 0.7064375, 0.7064375, 0.0, 0.7064375, 0.7064375, 0.0, 0.7064375, 0.7064375, 0.0, 0.0, 0.0, 0.0]
                        
                        new data of normal tag
                        
                        [0.0, 0.0208125, 0.04165625, 0.0625, 0.0833125, 0.10415625, 0.125, 0.1458125, 0.16665625, 0.1875, 0.2083125, 0.22915625, 0.25, 0.2708125, 0.29165625, 0.3125, 0.3333125, 0.35415625, 0.375, 0.3958125, 0.41665625, 0.4375, 0.4583125, 0.47915625, 0.5, 0.5208125, 0.54165625, 0.5625, 0.5833125, 0.60415625, 0.625, 0.6458125, 0.66665625, 0.6875, 0.7083125, 0.72915625, 0.75, 0.7708125, 0.79165625, 0.8125, 0.8333125, 0.85415625, 0.875, 0.8958125, 0.91665625, 0.9375, 0.9583125, 0.97915625]
                        

                        Could this happen because I don't use Phong Tag?

                        Edit:

                        Yes it was definitely Phong Tags! Also, this was enough using my initial normal data

                        # Maps data from float to int16 value
                        normalListToSet = [int(normal * 32000.0) for normal in normalList]
                                
                        # Writes the previous list to the normal tag.
                        WriteNormalTag(nrm_tag, normalListToSet)
                        

                        Anyway, I imported an .obj file that shares the same data as my binary file, and once I copied the Phong Tag settings and applied to my generated objects it was identical, which means Normal Tags are correct!

                        I can't seem to find a page for Phong Tag though. How would I create a new Phong Tag, and set it's values?

                        Your help has already done a lot and I really appreciate your responses.
                        Thanks,
                        Sheilan.

                        1 Reply Last reply Reply Quote 0
                        • M
                          m_adam
                          last edited by

                          Phong tag are common tags, (data count stored does not change), so you have to create them with the usual way.

                          tPhong = c4d.BaseTag(c4d.Tphong)
                          op.InsertTag(tPhong)
                          

                          or either

                          op.MakeTag(c4d.Tphong)
                          

                          You can find the list of all the tags symbols in Tag Types and I recommend you to read the C++ BaseTag and VariableTag Manual to understand the difference between both.

                          Cheers,
                          Maxime.

                          MAXON SDK Specialist

                          Development Blog, MAXON Registered Developer

                          S 1 Reply Last reply Reply Quote 0
                          • S
                            sheilan @m_adam
                            last edited by sheilan

                            Well then that'll be it. Thanks a lot for all the help!
                            I'll definitely read the manual to get a better idea of each tag.

                            Here is the final, working code to anyone who reads this thread having the same problems:

                            def CreateNormalTag(op,polyCnt):
                                """
                                 Creates a NormalTag on the passed PolygonObject.
                            
                                :param op: The PolygonObject that will received a normal Tag.
                                :type op: c4d.PolygonObject
                                :return: The created tag.
                                :rtype: c4d.NormalTag
                                """
                                # Checks if the passed object is a polygon object.
                                if not isinstance(op, c4d.PolygonObject):
                                    raise TypeError("op is not a c4d.PolygonObject.")
                                
                            
                                # Creates a Normal Tag in memory only
                                normalTag = c4d.NormalTag(polyCnt)
                                if normalTag is None:
                                    raise MemoryError("Failed to creates a normal Tag.")
                            
                                # Inserts the tag to the passed object
                                op.InsertTag(normalTag)
                            
                                # Notifies the object he need to update to take care of the newly created normal tag
                                op.Message(c4d.MSG_UPDATE)
                                return normalTag
                            def WriteNormalTag(tag, normalList):
                                """
                                Write the raw data to a Normal Tag.
                            
                                :param tag: The Normal Tag to write the data to.
                                :type tag: c4d.NormalTag
                                :param normalList: A list with all the raw data.
                                :type normalList: list[int]
                                """
                                # Retrieves the write buffer array
                                buffer = tag.GetLowlevelDataAddressW()
                                if buffer is None:
                                    raise RuntimeError("Failed to retrieves internal write data for the normal tag.")
                            
                                # Translates list of short int 16 to a BitSeq (string are byte in Python 2.7)
                                intArray = array.array('h')
                                intArray.fromlist(normalList)
                                data = intArray.tostring()
                                buffer[:len(data)] = data
                            
                            # Some of the data was read in a binary file/determined earlier but the variable names should be enough to tell what they are
                            for i in range(surfaceCount):
                                    # Surface triangles count
                                    triCount = read_ushort(c2m)
                                    # Surface Material Name
                                    materialName = read_string(c2m)
                                    tri_vertexgroups = {}
                                    tri_uvgroups = {}
                                    tri_normalgroups = {}
                                    for f in range(triCount):
                                        vertex = vertex1,vertex2,vertex3 # pre-loaded 3 vertex indices for current face
                                        uv = uv1,uv2,uv3 # pre-loaded 3 UV indices for current face
                                        normal = normal1,normal2,normal3 # pre-loaded 3 normal indices for current face
                            
                                        # Add them to a dictionary
                                        tri_vertexgroups[f] = vertex
                                        tri_uvgroups[f] = uv
                                        tri_normalgroups[f] = normal
                            
                                    # Create dictionary for surface's vertices
                                    surface_vertices = {}
                                    # Unique vertex index (used to avoid creating the same vertex twice)
                                    unique_vertex = 0
                            
                                    # Create new poly
                                    mypoly = c4d.BaseObject(c4d.Opolygon) #Create an empty polygon object
                                    mypoly.SetName(materialName) 
                                    uv_tag = c4d.UVWTag(triCount)   
                                    nrm_tag = CreateNormalTag(mypoly,triCount)
                                    phong_tag = c4d.BaseTag(c4d.Tphong)
                                    phong_tag[c4d.PHONGTAG_PHONG_ANGLELIMIT]=True
                                    phong_tag[c4d.PHONGTAG_PHONG_ANGLE]=c4d.utils.Rad(25.5)
                                    phong_tag[c4d.PHONGTAG_PHONG_USEEDGES]=True
                                    normalList = []
                                    # Loop through number of triangles
                                    for f in range(triCount):
                                        # Load vertices for current triangle from the list of polygon's vertices
                                        tri_vertices = tri_vertexgroups[f]
                            
                                        # Load each vertex of the triangle from a dictionary with all the UVs in the scene using it's index
                                        vertex1 = vertices_positions[tri_vertices[0]]
                                        vertex2 = vertices_positions[tri_vertices[1]]
                                        vertex3 = vertices_positions[tri_vertices[2]]
                            
                                        # Check if vertex was not added already, add if not.
                                        if vertex1 not in surface_vertices:
                                            # Add vertex1 to surface(polygon object)'s dictionary, with a unique index
                                            surface_vertices[vertex1] = unique_vertex
                                            unique_vertex += 1
                                            # Resize our object to allow another point to be created
                                            mypoly.ResizeObject(unique_vertex, triCount)
                                            # Create new point
                                            mypoly.SetPoint(surface_vertices[vertex1],c4d.Vector(vertex1[0],vertex1[1],vertex1[2]))
                            
                                        if vertex2 not in surface_vertices:
                                            surface_vertices[vertex2] = unique_vertex
                                            unique_vertex += 1
                                            mypoly.ResizeObject(unique_vertex, triCount)
                                            mypoly.SetPoint(surface_vertices[vertex2],c4d.Vector(vertex2[0],vertex2[1],vertex2[2]))
                            
                                        if vertex3 not in surface_vertices:
                                            surface_vertices[vertex3] = unique_vertex
                                            unique_vertex += 1
                                            mypoly.ResizeObject(unique_vertex, triCount)
                                            mypoly.SetPoint(surface_vertices[vertex3],c4d.Vector(vertex3[0],vertex3[1],vertex3[2]))
                            
                                        # Load UVs for current triangle from the list of polygon's UVs
                                        tri_uvs = tri_uvgroups[f]
                                        
                                        # Load each UV of the triangle from a dictionary with all the UVs in the scene using it's index
                                        uv1 = vertices_uvs[tri_uvs[0]]
                                        uv2 = vertices_uvs[tri_uvs[1]]
                                        uv3 = vertices_uvs[tri_uvs[2]]
                            
                                        # Create polygon(triangle)
                                        mypoly.SetPolygon(f, c4d.CPolygon(surface_vertices[vertex1],surface_vertices[vertex2],surface_vertices[vertex3]))
                                        # Add our UV data to the UV tag
                                        uv_tag.SetSlow(f, c4d.Vector(uv1[0], uv1[1], 0),
                                                    c4d.Vector(uv2[0], uv2[1], 0),  
                                                    c4d.Vector(uv3[0], uv3[1], 0),  
                                                    c4d.Vector(0,0,0))  
                            
                                        # Load normals for current triangle from the list of polygon's normals
                                        tri_normals = tri_normalgroups[f]
                            
                                        # Load each normal of the triangle from a dictionary with all the normals in the scene using it's index
                                        normal1 = vertices_normals[tri_normals[0]] # Vector3 of normal direction
                                        normal2 = vertices_normals[tri_normals[1]]
                                        normal3 = vertices_normals[tri_normals[2]]
                                        normal4 = [0.0, 0.0, 0.0]  # Even if it's a Tri, you should pass a value.
                            
                                        # Add normals to normalList
                                        normalList.extend(normal1)
                                        normalList.extend(normal2)
                                        normalList.extend(normal3)
                                        normalList.extend(normal4)
                                        
                                    # Maps data from float to int16 value
                                    normalListToSet = [int(normal * 32000.0) for normal in normalList]
                            
                                    # Writes the previous list to the normal tag.
                                    WriteNormalTag(nrm_tag, normalListToSet)
                            
                                    doc.InsertObject(mypoly,None,None)
                                    mypoly.Message(c4d.MSG_UPDATE)
                                    mypoly.InsertTag(uv_tag)
                                    mypoly.Message(c4d.MSG_UPDATE)
                                    mypoly.InsertTag(phong_tag)
                                    mypoly.Message(c4d.MSG_UPDATE)
                                    
                                    c4d.EventAdd()
                            

                            Thank you once again Maxime, your help is appreciated!

                            1 Reply Last reply Reply Quote 2
                            • M
                              m_adam
                              last edited by m_adam

                              Just to add additional information,

                              In R21 GetAllHighlevelData() and SetAllHighlevelData() are working as expected for NormalTag, so data are not any more "shifted" and correctly interpreted internally.

                              See What's New: Fixes:

                              • Fixed an issue where VariableTag.GetAllHighlevelData() and VariableTag.SetAllHighlevelData() used Uint16 instead of int16 for Tnormal.
                              • Fixed an issue where VariableTag.GetAllHighlevelData() returned data were shifted and not complete for Tnormal.

                              Cheers,
                              Maxime.

                              MAXON SDK Specialist

                              Development Blog, MAXON Registered Developer

                              1 Reply Last reply Reply Quote 0
                              • First post
                                Last post