Best way to get vertices from a point

On 05/12/2017 at 03:04, xxxxxxxx wrote:

I'm looking for a "best-practice" approach to this problem. I need to read out an object and convert it to its vertex positions (not point positions! vertices.). As for the why: this appears to be the only way to retain UV coordinates for each vertex (using points' UVs will cause issues because a point is shared between multiple polygons, usually). So far it looks like making it editable and calling Disconnect on it seems like the only way to do this - however I was wondering whether there might be a more elegant solution whereby I could get the vertices per point?

Cheers
Michael

On 05/12/2017 at 04:10, xxxxxxxx wrote:

For some understanding purpose, and to be sure we talk about the same.

Polygon is define by his edge.

Edge is 2 Vertexs.

c4d.CPolygon actually represent a polygon.
c4d.CPolygon get in maximum 4 points (a, b, c, d) if it's triangle c and d are egual.
c4d.CPolygon vertex (a,b,c,d presented in the last sentence) are VertexID
c4d.PolygonObject inherite from c4d.PointObject
VertexID are shared accross the PointObject
UVVertexID are shared accross CPolygon

To get position of a specific point you have to do

#op is c4d.PolygonObject
localPointPos = op.GetPoint(0) # Get the local position of the VertexID 0
globalPointPos = op.GetPoint(0) * op.GetMg() # Get the world position of the VertexID 0

If you want to go from PolygonID to VertexID

#op is c4d.PolygonObject
poly = op.GetPolygon(0) #Get the c4d.CPolygon with the polygonID 0
localPointPos = op.GetPoint(poly.a) # Get the local position of the first VertexID inside CPolygon (called Cpolygon.a)

About UV since they belong to CPolygon you have to use GetPointPolys to find all the shared CPolygon accross a given VertexID. Then for each CPolygon you have to iterate over (a,b,c,d) to get the desired VertexID. Then you have to move it using SetSlow.

For more informations please read this script that allow you to set UVVertexID from VertexID (at least if I understand correctly your question).

I hope it's andwerd your question.

On 05/12/2017 at 04:26, xxxxxxxx wrote:

Hi,

no, it's not quite it. I'm aware of all the steps to get UVs and Points from the geometry. The problem is that in Cinema all the points usually are treated as shared points. So if you have two polygons next to each other, their bordering points (on the same edge) are shared point IDs.

1 - 2 - 3
|    |    |
4 - 5 - 6

So you get CPolygon with 1245 and 2356. Now if you UV map a texture onto this, with a repeating UV for example, you end up with these UV coordinates for _each_ of the two polygons.

(0,0) - (1,0)(0,0) - (1,0)
  |              |             |
(0,1) - (1,1)(0,1) - (1,1)

This results in point 2, for example, having two UVs: (1,0) (from the left polygon) and (0,0) from the right polygon.

To properly handle this, you need to have two vertices (or more, depending on how many polygons share this point), where each vertex stores the position of the Point and also its individual UV (and normal).

This is something that apparently is handled entirely internally in Cinema - my question now is, can I get this data through the means of the Python API somehow, without jumping through hoops for it (e.g. Make Editable, then Triangulate, the Disconnect (which may even screw up the normals?)).

Michael

On 05/12/2017 at 05:04, xxxxxxxx wrote:

Originally posted by xxxxxxxx

To properly handle this, you need to have two vertices (or more, depending on how many polygons share this point), where each vertex stores the position of the Point and also its individual UV (and normal).

It's actually the case since as said in my previous post, UVVertexID belong to CPolygon vertex(a,b,c,d). Maybe I missunderstand something about how you do your UV stuff but basicly here is a code that show you that you have 2 UVVertexID for VertexID 1 (2 in your schema, since ID start at 0 and not 1)

import c4d
  
def CreateObject() :
    obj  = c4d.BaseObject(c4d.Opolygon)
    
    points = [c4d.Vector(0, 0, -100), # Coordonnées des points
    c4d.Vector(0, 0, 0),
    c4d.Vector(0, 0, 100),
    c4d.Vector(100, 0, -100),
    c4d.Vector(100, 0, 0),
    c4d.Vector(100, 0, 100)]
    
    polys = [c4d.CPolygon(0, 1, 4, 3), # Points ABCD du polygone
    c4d.CPolygon(1, 2, 5, 4)]
    
    obj.ResizeObject(len(points), len(polys))
    
    obj.SetAllPoints(points)
    
    for i, p in enumerate(polys) :
        obj.SetPolygon(i, p)
    
    obj.Message(c4d.MSG_UPDATE)
    doc.InsertObject(obj)
    
    return obj
  
def CreateUVTag(obj) :
    tag = c4d.UVWTag(obj.GetPolygonCount())
    obj.InsertTag(tag)
    
    return tag
    
def main() :
    obj = CreateObject()
    if not obj: return
    
    uvTag = CreateUVTag(obj)
    if not uvTag: return
    
    # Now everything is setup, let's change VertexID 1 (2 in your schema, since ID start at 0 and not 1)
    nbr = c4d.utils.Neighbor()
    nbr.Init(obj)
    
    polys = nbr.GetPointPolys(1) # Get all the PolygonID shared with vertexID 1
    polys_data = obj.GetAllPolygons()
    
    # Loop for each CPolygon and add to list_id, the PolygonID["poly_id"], and the actual CPolygon vertex ID (a,b,c,d are mapped to 0,1,2,3)["pt_num"]
    list_id = list()
    for poly_id in polys:
        poly = polys_data[poly_id]
        found_id = poly.Find(1) # here we search for want VertexID 1
        
        if found_id != c4d.NOTOK:
            buffer_data = dict()
            buffer_data["poly_id"] = poly_id
            buffer_data["pt_num"] = found_id
            
            list_id.append(buffer_data)
  
    # Now we get list of all Cpolygon with the corresponding (a,b,c,d) that are equal to VertexID 1
    for pt in list_id:
        buffer_uv = uvTag.GetSlow(pt["poly_id"])
        
        list_uv = [buffer_uv["a"],
                   buffer_uv["b"],
                   buffer_uv["c"],
                   buffer_uv["d"]]
                
        # change the value only for the corresponding CPolygon vertexID look comment at line 48
        list_uv[pt["pt_num"]] = c4d.Vector(0.5, 0.0, 0.0)
        
        # Set the point where you want
        uvTag.SetSlow(pt["poly_id"],
                    list_uv[0],
                    list_uv[1],
                    list_uv[2],
                    list_uv[3]
                    )
        
    c4d.EventAdd()
  
if __name__=='__main__':
    main()

Please take a look at my script linked in my previous post

But maybe I complettly missunderstand your question and someone else get it ! 😉

On 05/12/2017 at 05:17, xxxxxxxx wrote:

No, it's not a complete misunderstanding, but it's not the result I'm looking for 🙂

What I am looking for is simply a simple way to say "for Point X, return an Array of Vertices and their UVs" but I think it's just not there and I'll write my own.

Thanks anyway 🙂

On 07/12/2017 at 14:06, xxxxxxxx wrote:

Hi Michael, thanks for writing us.

if I correctly understood your request, the following code should satisfy your need.

  
import c4d  
  
def main() :  
  obj = doc.GetActiveObject()  
    
  # check something is active  
  if obj is None:  
      print "No object selected"  
      return  
    
  # check the object to be an editable polygon object  
  if obj.GetType() != c4d.Opolygon:  
      print "Selected object is a generator, please make it editable"  
      return  
    
  # attempt to retrive a UVWTag  
  uvwTag = obj.GetTag(c4d.Tuvw)  
  if uvwTag is None:  
      print "uvwTag doesn't exists"  
      return  
    
  # attempt to access points and polygons lists  
  points = obj.GetAllPoints()  
  polygons = obj.GetAllPolygons()  
  if polygons is None or points is None:  
      print "points or polygons don't exist"  
      return  
    
  # store the polygons count  
  polyCnt = len(polygons)  
    
  # preallocate the list containing the final pos and uvw data  
  alldata = [None] * polyCnt  
    
  # loop through the polygons  
  for i in xrange(polyCnt) :  
      polygon = polygons[i]  
      # access the UVW data for the current polygon  
      uvwPolygon = uvwTag.GetSlow(i)  
      # check whatever the polygon has three or four vertices  
      if polygon.IsTriangle() :  
          # cycle over all the poiygon data and store in alldata  
          pos = points[polygon.a]  
          uvw = uvwPolygon["a"]  
          data = [[pos, uvw]]  
          pos = points[polygon.b]  
          uvw = uvwPolygon["b"]  
          data.append([pos, uvw])  
          pos = points[polygon.c]  
          uvw = uvwPolygon["c"]  
          data.append([pos, uvw])  
          alldata[i] = data  
      else:  
          pos = points[polygon.a]  
          uvw = uvwPolygon["a"]  
          data = [[pos, uvw]]  
          pos = points[polygon.b]  
          uvw = uvwPolygon["b"]  
          data.append([pos, uvw])  
          pos = points[polygon.c]  
          uvw = uvwPolygon["c"]  
          data.append([pos, uvw])  
          pos = points[polygon.d]  
          uvw = uvwPolygon["d"]  
          data.append([pos, uvw])  
          alldata[i] = data  
            
  # just print out to check the result  
  for j in xrange(len(alldata)) :  
      data = alldata[j]  
      print "Polygon["+str(j) +"]"  
      for k in xrange(len(data)) :  
          spacer = " ---> vtx [" + str (k) + "]"  
          posString = " pos:[" + str(data[k][0].x) + ", " + str(data[k][0].y) +", "+ str(data[k][0].z) + "]"   
          uvwString = " uvw:[" + str(data[k][1].x) + ", " + str(data[k][1].y) +", "+ str(data[k][1].z) + "]"   
          print spacer, posString, uvwString  
  
if __name__=='__main__':  
  main()  

Best, Riccardo