Hello!
I'm creating a polygon object in an ObjectData plugin. I would like to layout its UVs with Flat projection (similar to BodyPaint's Flat Projection button). I found this post with code taken from this post on the forum. This seems to be what I need, but it's old and I ran into some issues with methods unavailable to the Python API. Here is my port of the code to Python:
import c4d,math
#This code creates the equivalent to the flat mapping button in the BP UV options
#Define a value macro to be used several times later on
SIGDIG=5.0
def TrimDecimal(num, digits):
n = None
n = num * math.pow(10.0, digits)
n = math.copysign(1, n) * abs(math.floor(n + 0.5))
return n / math.pow(10.0, digits)
def FrontalMapUVs(op, mg):
if op is None:
return false
numFaces = op.GetPolygonCount()
numVerts = op.GetPointCount()
pVerts = op.GetPointW() #GetPointW doesn't exist in the Python API
pPolys = op.GetPolygonW()
pUVTag = op.MakeVariableTag(c4d.Tuvw, numFaces)
if pUVTag is None:
return false
pUVHndl = pUVTag.GetDataAddressW() #GetDataAddressW doesn't exist in the Python API
if pUVHndl is None:
return false
lenxinv, lenyinv, lenzinv = None, None, None
vMin = c4d.Vector(100000.0)
vMax = c4d.Vector(-100000.0)
for ndx in range(numVerts):
pt = pVerts[ndx] * mg #pVerts won't work because GetPointW doesn't exist
if pt.x < vMin.x: vMin.x = pt.x
if pt.x > vMax.x: vMax.x = pt.x
if pt.y < vMin.y: vMin.y = pt.y
if pt.y > vMax.y: vMax.y = pt.y
if pt.z < vMin.z: vMin.z = pt.z
if pt.z > vMax.z: vMax.z = pt.z
mapSize = c4d.Vector(abs(vMax.x - vMin.x),abs(vMax.y - vMin.y),abs(vMax.z - vMin.z))
mapCenter = c4d.Vector(vMin.x+(mapSize.x*0.5),vMin.y+(mapSize.y*0.5),vMin.z+(mapSize.z*0.5))
if mapSize.x != 0.0:
lenxinv = 1.0 / mapSize.x
else:
lenxinv = 0.0
if mapSize.y != 0.0:
lenyinv = 1.0 / mapSize.y
else:
lenyinv = 0.0
if mapSize.z != 0.0:
lenzinv = 1.0 / mapSize.z
else:
lenzinv = 0.0
# Walk the list of polygons and map the UVs
for ndx in range(numFaces):
"""
Not sure what to do here as UVWStruct & UVWTag.Get do not exist in the Python API
uvw = c4d.UVWStruct()
pUVTag.Get(pUVHndl, ndx, uvw)
"""
pt_a = (pVerts[pPolys[ndx].a] - mapCenter) + mg.off
pt_b = (pVerts[pPolys[ndx].b] - mapCenter) + mg.off
pt_c = (pVerts[pPolys[ndx].c] - mapCenter) + mg.off
pt_d = (pVerts[pPolys[ndx].d] - mapCenter) + mg.off
uvw.a.x = TrimDecimal((pt_a.x*lenxinv)+0.5, SIGDIG)
uvw.a.y = TrimDecimal((-pt_a.y*lenyinv)+0.5, SIGDIG)
uvw.b.x = TrimDecimal((pt_b.x*lenxinv)+0.5, SIGDIG)
uvw.b.y = TrimDecimal((-pt_b.y*lenyinv)+0.5, SIGDIG)
uvw.c.x = TrimDecimal((pt_c.x*lenxinv)+0.5, SIGDIG)
uvw.c.y = TrimDecimal((-pt_c.y*lenyinv)+0.5, SIGDIG)
uvw.d.x = TrimDecimal((pt_d.x*lenxinv)+0.5, SIGDIG)
uvw.d.y = TrimDecimal((-pt_d.y*lenyinv)+0.5, SIGDIG)
"""
commented out because of UVWStruct missing above
"""
#pUVTag.Set(pUVHndl, ndx, uvw)
return true
if __name__=='__main__':
FrontalMapUVs(op,op.GetMg()) #I'm sending the selected object's global matrix, but I'm not sure if it's correct to do.
I noted some of the issues in the code's comments, but to sum up, I couldn't get it to work because these were missing in Python:
- PolygonObject.GetPointW: Gets the start of the writable points array.
- UVWTag.GetDataAddressW: Gets a handle to the writable UVW data.
- UVWTag.Get: Gets the UVW coordinates for a polygon.
- UVWTag.Set: Sets the UVW coordinates for a polygon.
- UVWStruct: Holds UVW tag variable coordinates data.
I also didn't know which Global Matrix I should send to the FrontalMapUVs
function. I'd assume it was op
, but it seems strange for that to be a parameter if it's already accepting op
.
If there are no Python equivalents to these missing C++ methods, is there another way to do Flat UV Projection in Python with an ObjectData plugin?
Thank you!