Discussion:
[Maya-Python] Maya API, getting UV border vertices struggle
Benjam901
2017-11-09 16:03:12 UTC
Permalink
Hello all,

So here is my struggle at the moment.

I am trying to guesstimate the "actual" in-engine vert count for a given
object in Maya so we can validate the LOD steps per object before we export.

*Proposed solution:*
The way I am trying to do this is by checking that a vertex is on a UV
border edge and adding this to the total vert count.

So for example an object with 500 verts which has a UV border straight
through the middle will end up with 1000 verts in-engine because of the UV
split.

The problem I am having is wrapping my head around the underlying data
models for verts, faces, edges and uvs.

*Solution 1:*
I have a sort of working solution that does not use the API but instead
relies on the mel command: *polySelectBorderShell 1*

import pymel.core as pm
import maya.mel as mel

sel = pm.ls(sl=True)[0] # Get object selection

# Edges must be selected here
mel.eval("polySelectBorderShell 1")
uvs = pm.polyListComponentConversion(fe=True, tuv=True)
uvList = pm.ls(uvs, flatten=True)
uvDict = {}
for i in uvList:
print i
uvNum = int(i.split('[')[-1].split(']')[0])
uvDict.update({uvNum:i})

vertBorders = {} # e.g. {'map1':[1, 2, 3, 5, 71]}
uvSets = pm.polyUVSet(sel, query=True, allUVSets=True)
for uvSet in uvSets:
vertBorders.update({uvSet:[]})
uvs = pm.polyEvaluate(sel, uvs=uvSet, uv=True)
for uvNum in range(uvs):
if uvNum in uvDict:
vert = pm.polyListComponentConversion(uvDict[uvNum], fuv=True,
tv=True)[0]
vertBorders[uvSet].append(vert)

count = 0
multiList = vertBorders.values()
result = set(multiList[0]).intersection(*multiList[:1])
count += len(result)
newMultiList = []
for L in multiList:
newList = [i for i in L if i not in result]
count += len(newList)
print count


The above solution kinda gets me there and I need to make sure its legit
and patch it up but for the most part it works.
Problem is that it is *SLOW *hence trying to use the API.

*Solution 2:*
For the API I am following along with this mode of thinking here:
*http://forums.cgsociety.org/archive/index.php?t-729364.html*

Mu current solution and where I am stuck is here. I know I need to run some
comparisons but how and on what I am deadlocked at.

def getVertFromId(dagPath, vertID):
vertIt = OM.MItMeshVertex(dagPath)
vtxIdUtil = OM.MScriptUtil()
vtxIdUtil.createFromInt(0)
vtxIdPtr = vtxIdUtil.asIntPtr()
vertIt.setIndex(vertID, vtxIdPtr)
return vertIt

def getFaceFromId(dagPath, faceId):
faceIt = OM.MItMeshPolygon(dagPath)
faceIdUtil = OM.MScriptUtil()
faceIdUtil.createFromInt(0)
faceIdPtr = faceIdUtil.asIntPtr()
faceIt.setIndex(faceId, faceIdPtr)
return faceIt

def getVertUVInfo(vertIn):
uArr = OM.MFloatArray()
vArr = OM.MFloatArray()
fIDs = OM.MIntArray()
uvIndices = OM.MIntArray()
uvSet = 'map1'
vertIn.getUVs(uArr, vArr, fIDs, uvSet)
vertIn.getUVIndices(uvIndices, uvSet)

print uArr, vArr, fIDs, uvIndices
return fIDs, uvIndices

def stripUnecessaryFaces(currentEdgeFaces, faceIDs1, faceIDs2):
fID1 = []
fID2 = []
for fID in faceIDs1:
if fID in currentEdgeFaces:
fID1.append(fID)

for fID in faceIDs2:
if fID in currentEdgeFaces:
fID2.append(fID)

return fID1, fID2

def main():
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)

dagPath = OM.MDagPath()
sel.getDagPath(dagPath)
dagPath.extendToShape()
connFaces = OM.MIntArray()

edgeIter = OM.MItMeshEdge(dagPath)
while not edgeIter.isDone():
f1 = None
f2 = None
edgeIter.getConnectedFaces(connFaces)
if len(connFaces) == 1:
# open edge
print 'Open edge'

f1 = connFaces[0] # face 1
try:
f2 = connFaces[1] # face 2
except:
pass

vert1Index = edgeIter.index(0)
vert2Index = edgeIter.index(1)

MfVert1 = getVertFromId(dagPath, vert1Index)
MfVert2 = getVertFromId(dagPath, vert2Index)

fIdsvert1, uvIndicesVert1 = getVertUVInfo(MfVert1)
fIdsvert2, uvIndicesVert2 = getVertUVInfo(MfVert2)

edgeIter.next()


Any suggestions on where to go from here or how to solve this is very much
appreciated!

// Ben
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/1b0555d7-7ffb-4df6-9c1d-5f9883cee64b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
damon shelton
2017-11-09 17:27:46 UTC
Permalink
Would just retrieving the number of uvs in a given uvset give you the count
you are looking for?
I created a sphere, then started creating uv shells out of part of the mesh.

len(cmds.ls('pSphere1.map[*]', fl=True))
Post by Benjam901
Hello all,
So here is my struggle at the moment.
I am trying to guesstimate the "actual" in-engine vert count for a given
object in Maya so we can validate the LOD steps per object before we export.
*Proposed solution:*
The way I am trying to do this is by checking that a vertex is on a UV
border edge and adding this to the total vert count.
So for example an object with 500 verts which has a UV border straight
through the middle will end up with 1000 verts in-engine because of the UV
split.
The problem I am having is wrapping my head around the underlying data
models for verts, faces, edges and uvs.
*Solution 1:*
I have a sort of working solution that does not use the API but instead
relies on the mel command: *polySelectBorderShell 1*
import pymel.core as pm
import maya.mel as mel
sel = pm.ls(sl=True)[0] # Get object selection
# Edges must be selected here
mel.eval("polySelectBorderShell 1")
uvs = pm.polyListComponentConversion(fe=True, tuv=True)
uvList = pm.ls(uvs, flatten=True)
uvDict = {}
print i
uvNum = int(i.split('[')[-1].split(']')[0])
uvDict.update({uvNum:i})
vertBorders = {} # e.g. {'map1':[1, 2, 3, 5, 71]}
uvSets = pm.polyUVSet(sel, query=True, allUVSets=True)
vertBorders.update({uvSet:[]})
uvs = pm.polyEvaluate(sel, uvs=uvSet, uv=True)
vert = pm.polyListComponentConversion(uvDict[uvNum], fuv=True,
tv=True)[0]
vertBorders[uvSet].append(vert)
count = 0
multiList = vertBorders.values()
result = set(multiList[0]).intersection(*multiList[:1])
count += len(result)
newMultiList = []
newList = [i for i in L if i not in result]
count += len(newList)
print count
The above solution kinda gets me there and I need to make sure its legit
and patch it up but for the most part it works.
Problem is that it is *SLOW *hence trying to use the API.
*Solution 2:*
For the API I am following along with this mode of thinking here: *http://forums.cgsociety.org/archive/index.php?t-729364.html
<http://forums.cgsociety.org/archive/index.php?t-729364.html>*
Mu current solution and where I am stuck is here. I know I need to run
some comparisons but how and on what I am deadlocked at.
vertIt = OM.MItMeshVertex(dagPath)
vtxIdUtil = OM.MScriptUtil()
vtxIdUtil.createFromInt(0)
vtxIdPtr = vtxIdUtil.asIntPtr()
vertIt.setIndex(vertID, vtxIdPtr)
return vertIt
faceIt = OM.MItMeshPolygon(dagPath)
faceIdUtil = OM.MScriptUtil()
faceIdUtil.createFromInt(0)
faceIdPtr = faceIdUtil.asIntPtr()
faceIt.setIndex(faceId, faceIdPtr)
return faceIt
uArr = OM.MFloatArray()
vArr = OM.MFloatArray()
fIDs = OM.MIntArray()
uvIndices = OM.MIntArray()
uvSet = 'map1'
vertIn.getUVs(uArr, vArr, fIDs, uvSet)
vertIn.getUVIndices(uvIndices, uvSet)
print uArr, vArr, fIDs, uvIndices
return fIDs, uvIndices
fID1 = []
fID2 = []
fID1.append(fID)
fID2.append(fID)
return fID1, fID2
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
dagPath = OM.MDagPath()
sel.getDagPath(dagPath)
dagPath.extendToShape()
connFaces = OM.MIntArray()
edgeIter = OM.MItMeshEdge(dagPath)
f1 = None
f2 = None
edgeIter.getConnectedFaces(connFaces)
# open edge
print 'Open edge'
f1 = connFaces[0] # face 1
f2 = connFaces[1] # face 2
pass
vert1Index = edgeIter.index(0)
vert2Index = edgeIter.index(1)
MfVert1 = getVertFromId(dagPath, vert1Index)
MfVert2 = getVertFromId(dagPath, vert2Index)
fIdsvert1, uvIndicesVert1 = getVertUVInfo(MfVert1)
fIdsvert2, uvIndicesVert2 = getVertUVInfo(MfVert2)
edgeIter.next()
Any suggestions on where to go from here or how to solve this is very much
appreciated!
// Ben
--
You received this message because you are subscribed to the Google Groups
"Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit https://groups.google.com/d/
msgid/python_inside_maya/1b0555d7-7ffb-4df6-9c1d-
5f9883cee64b%40googlegroups.com
<https://groups.google.com/d/msgid/python_inside_maya/1b0555d7-7ffb-4df6-9c1d-5f9883cee64b%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAM9RXoJcU7%3DDDejFQAmpVXf5XmExHmSEgwCxN0S6Wu92P_oFuw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Michael Boon
2017-11-10 01:57:06 UTC
Permalink
For speed, you definitely want to use OpenMaya, but more importantly, avoid
creating new Python objects every time you loop over a vert. Try to create
some lists of regular Python ints upfront, then just count them.

Once you have a MFnMesh, you can get a list of the UV IDs used at each
corner of each face using MFnMesh.getVertices() and MFnMesh.
getAssignedUVs().
polyVertices = fnMesh.getVertices()
polyUVIDs = fnMesh.getAssignedUVs() # with optional UV set name
Both those functions return two lists. The first list in each is the list
of vertex counts in each polygon, which we don't care about. The second
lists are the vert IDs, and the corresponding UV IDs. It's significant that
these are corresponding lists, since we're going to zip them together.

Create a list with an entry for each vert in the mesh, then you can put the
UV IDs each vert uses into its entry in the list. You know the number of
verts, so you can create the main list in one hit, for speed. By using
sets, you only record the unique IDs used at each vert.

vertUVIDs = [set() for _ in range(fnMesh.numVertices)]


Then go through the getAssignedUVs result in a loop and record which UV IDs
are used at each vert:
(Note: itertools.izip will be much faster than regular zip here because it
doesn't create a new list)
for vID, uvID in izip(polyVertices[1], polyUVIDs[1]):
vertUVIDs[vID].add(uvID)

Once you have the UV IDs at each vert, you just sum the lengths of the sets:
totalVertCount = sum(len(s) for s in vertUVIDs)

Note that verts with no UVs will not be counted at all this way. You might
want to add a check that every set has at least one element in it.

I can count ~20k verts in ~30ms that way, which is certainly good enough
for games work :)
Post by Benjam901
Hello all,
So here is my struggle at the moment.
I am trying to guesstimate the "actual" in-engine vert count for a given
object in Maya so we can validate the LOD steps per object before we export.
*Proposed solution:*
The way I am trying to do this is by checking that a vertex is on a UV
border edge and adding this to the total vert count.
So for example an object with 500 verts which has a UV border straight
through the middle will end up with 1000 verts in-engine because of the UV
split.
The problem I am having is wrapping my head around the underlying data
models for verts, faces, edges and uvs.
*Solution 1:*
I have a sort of working solution that does not use the API but instead
relies on the mel command: *polySelectBorderShell 1*
import pymel.core as pm
import maya.mel as mel
sel = pm.ls(sl=True)[0] # Get object selection
# Edges must be selected here
mel.eval("polySelectBorderShell 1")
uvs = pm.polyListComponentConversion(fe=True, tuv=True)
uvList = pm.ls(uvs, flatten=True)
uvDict = {}
print i
uvNum = int(i.split('[')[-1].split(']')[0])
uvDict.update({uvNum:i})
vertBorders = {} # e.g. {'map1':[1, 2, 3, 5, 71]}
uvSets = pm.polyUVSet(sel, query=True, allUVSets=True)
vertBorders.update({uvSet:[]})
uvs = pm.polyEvaluate(sel, uvs=uvSet, uv=True)
vert = pm.polyListComponentConversion(uvDict[uvNum], fuv=True,
tv=True)[0]
vertBorders[uvSet].append(vert)
count = 0
multiList = vertBorders.values()
result = set(multiList[0]).intersection(*multiList[:1])
count += len(result)
newMultiList = []
newList = [i for i in L if i not in result]
count += len(newList)
print count
The above solution kinda gets me there and I need to make sure its legit
and patch it up but for the most part it works.
Problem is that it is *SLOW *hence trying to use the API.
*Solution 2:*
For the API I am following along with this mode of thinking here: *http://forums.cgsociety.org/archive/index.php?t-729364.html
<http://forums.cgsociety.org/archive/index.php?t-729364.html>*
Mu current solution and where I am stuck is here. I know I need to run
some comparisons but how and on what I am deadlocked at.
vertIt = OM.MItMeshVertex(dagPath)
vtxIdUtil = OM.MScriptUtil()
vtxIdUtil.createFromInt(0)
vtxIdPtr = vtxIdUtil.asIntPtr()
vertIt.setIndex(vertID, vtxIdPtr)
return vertIt
faceIt = OM.MItMeshPolygon(dagPath)
faceIdUtil = OM.MScriptUtil()
faceIdUtil.createFromInt(0)
faceIdPtr = faceIdUtil.asIntPtr()
faceIt.setIndex(faceId, faceIdPtr)
return faceIt
uArr = OM.MFloatArray()
vArr = OM.MFloatArray()
fIDs = OM.MIntArray()
uvIndices = OM.MIntArray()
uvSet = 'map1'
vertIn.getUVs(uArr, vArr, fIDs, uvSet)
vertIn.getUVIndices(uvIndices, uvSet)
print uArr, vArr, fIDs, uvIndices
return fIDs, uvIndices
fID1 = []
fID2 = []
fID1.append(fID)
fID2.append(fID)
return fID1, fID2
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
dagPath = OM.MDagPath()
sel.getDagPath(dagPath)
dagPath.extendToShape()
connFaces = OM.MIntArray()
edgeIter = OM.MItMeshEdge(dagPath)
f1 = None
f2 = None
edgeIter.getConnectedFaces(connFaces)
# open edge
print 'Open edge'
f1 = connFaces[0] # face 1
f2 = connFaces[1] # face 2
pass
vert1Index = edgeIter.index(0)
vert2Index = edgeIter.index(1)
MfVert1 = getVertFromId(dagPath, vert1Index)
MfVert2 = getVertFromId(dagPath, vert2Index)
fIdsvert1, uvIndicesVert1 = getVertUVInfo(MfVert1)
fIdsvert2, uvIndicesVert2 = getVertUVInfo(MfVert2)
edgeIter.next()
Any suggestions on where to go from here or how to solve this is very much
appreciated!
// Ben
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/6c39ea8d-8a5b-4f3e-a75b-621ee0983ac2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Benjam901
2017-11-10 09:21:18 UTC
Permalink
Hey Michael,

Thank you for the tips on the API and techniques to use for speed.

I was testing out your code and it works great. I am a bit confused though
(I have not managed to wrap my head around how each return list corresponds
to each other yet).

I need border edges for the UV shells. This way I can iterate each uv set,
get the border uvs, check that this particular UVs vertex has not already
been counted and add this to the total vert count rather than each UV.

- Ben
Post by Michael Boon
For speed, you definitely want to use OpenMaya, but more importantly,
avoid creating new Python objects every time you loop over a vert. Try to
create some lists of regular Python ints upfront, then just count them.
Once you have a MFnMesh, you can get a list of the UV IDs used at each
corner of each face using MFnMesh.getVertices() and MFnMesh.
getAssignedUVs().
polyVertices = fnMesh.getVertices()
polyUVIDs = fnMesh.getAssignedUVs() # with optional UV set name
Both those functions return two lists. The first list in each is the list
of vertex counts in each polygon, which we don't care about. The second
lists are the vert IDs, and the corresponding UV IDs. It's significant that
these are corresponding lists, since we're going to zip them together.
Create a list with an entry for each vert in the mesh, then you can put
the UV IDs each vert uses into its entry in the list. You know the number
of verts, so you can create the main list in one hit, for speed. By using
sets, you only record the unique IDs used at each vert.
vertUVIDs = [set() for _ in range(fnMesh.numVertices)]
Then go through the getAssignedUVs result in a loop and record which UV
(Note: itertools.izip will be much faster than regular zip here because it
doesn't create a new list)
vertUVIDs[vID].add(uvID)
totalVertCount = sum(len(s) for s in vertUVIDs)
Note that verts with no UVs will not be counted at all this way. You might
want to add a check that every set has at least one element in it.
I can count ~20k verts in ~30ms that way, which is certainly good enough
for games work :)
Post by Benjam901
Hello all,
So here is my struggle at the moment.
I am trying to guesstimate the "actual" in-engine vert count for a given
object in Maya so we can validate the LOD steps per object before we export.
*Proposed solution:*
The way I am trying to do this is by checking that a vertex is on a UV
border edge and adding this to the total vert count.
So for example an object with 500 verts which has a UV border straight
through the middle will end up with 1000 verts in-engine because of the UV
split.
The problem I am having is wrapping my head around the underlying data
models for verts, faces, edges and uvs.
*Solution 1:*
I have a sort of working solution that does not use the API but instead
relies on the mel command: *polySelectBorderShell 1*
import pymel.core as pm
import maya.mel as mel
sel = pm.ls(sl=True)[0] # Get object selection
# Edges must be selected here
mel.eval("polySelectBorderShell 1")
uvs = pm.polyListComponentConversion(fe=True, tuv=True)
uvList = pm.ls(uvs, flatten=True)
uvDict = {}
print i
uvNum = int(i.split('[')[-1].split(']')[0])
uvDict.update({uvNum:i})
vertBorders = {} # e.g. {'map1':[1, 2, 3, 5, 71]}
uvSets = pm.polyUVSet(sel, query=True, allUVSets=True)
vertBorders.update({uvSet:[]})
uvs = pm.polyEvaluate(sel, uvs=uvSet, uv=True)
vert = pm.polyListComponentConversion(uvDict[uvNum], fuv=True,
tv=True)[0]
vertBorders[uvSet].append(vert)
count = 0
multiList = vertBorders.values()
result = set(multiList[0]).intersection(*multiList[:1])
count += len(result)
newMultiList = []
newList = [i for i in L if i not in result]
count += len(newList)
print count
The above solution kinda gets me there and I need to make sure its legit
and patch it up but for the most part it works.
Problem is that it is *SLOW *hence trying to use the API.
*Solution 2:*
For the API I am following along with this mode of thinking here: *http://forums.cgsociety.org/archive/index.php?t-729364.html
<http://forums.cgsociety.org/archive/index.php?t-729364.html>*
Mu current solution and where I am stuck is here. I know I need to run
some comparisons but how and on what I am deadlocked at.
vertIt = OM.MItMeshVertex(dagPath)
vtxIdUtil = OM.MScriptUtil()
vtxIdUtil.createFromInt(0)
vtxIdPtr = vtxIdUtil.asIntPtr()
vertIt.setIndex(vertID, vtxIdPtr)
return vertIt
faceIt = OM.MItMeshPolygon(dagPath)
faceIdUtil = OM.MScriptUtil()
faceIdUtil.createFromInt(0)
faceIdPtr = faceIdUtil.asIntPtr()
faceIt.setIndex(faceId, faceIdPtr)
return faceIt
uArr = OM.MFloatArray()
vArr = OM.MFloatArray()
fIDs = OM.MIntArray()
uvIndices = OM.MIntArray()
uvSet = 'map1'
vertIn.getUVs(uArr, vArr, fIDs, uvSet)
vertIn.getUVIndices(uvIndices, uvSet)
print uArr, vArr, fIDs, uvIndices
return fIDs, uvIndices
fID1 = []
fID2 = []
fID1.append(fID)
fID2.append(fID)
return fID1, fID2
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
dagPath = OM.MDagPath()
sel.getDagPath(dagPath)
dagPath.extendToShape()
connFaces = OM.MIntArray()
edgeIter = OM.MItMeshEdge(dagPath)
f1 = None
f2 = None
edgeIter.getConnectedFaces(connFaces)
# open edge
print 'Open edge'
f1 = connFaces[0] # face 1
f2 = connFaces[1] # face 2
pass
vert1Index = edgeIter.index(0)
vert2Index = edgeIter.index(1)
MfVert1 = getVertFromId(dagPath, vert1Index)
MfVert2 = getVertFromId(dagPath, vert2Index)
fIdsvert1, uvIndicesVert1 = getVertUVInfo(MfVert1)
fIdsvert2, uvIndicesVert2 = getVertUVInfo(MfVert2)
edgeIter.next()
Any suggestions on where to go from here or how to solve this is very
much appreciated!
// Ben
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/2d436e81-b03e-405e-b87b-63f49173a9e7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael and Amanda
2017-11-10 11:00:05 UTC
Permalink
Hey Ben,

I have to admit I can't follow the UV shell technique you described. I
would expect a 500 vert object with a seam up the middle to have 500 +
(number of verts in the seam) verts in the game. The technique I described
does that, by counting verts twice if they are on UV seams (or more than
twice if they are on more than one seam).

I'll try to describe that technique a little better.

Actually it might help to describe how a mesh is put together first. There
is a list of vertex positions (which are points in space), and a list of
faces (each face is just a list of IDs into the vertex list). However for
efficiency, all the faces are run together into a single list, and there's
a second list that tells how many verts are in each face so you can count
through them to get to whichever face you want. Get your head around that,
then consider UVs are exactly the same. First there's a list of UVs (points
in 2D space), then there's a list of faces where each face is a list of IDs
into the UV list, and the list of faces is all run together just like it is
for vertices.

getVertices and getAssignedUVs each return two lists. The first list for
each is the number of verts in each face (which we don't care about in this
case). For getVertices, the second list is a list of vertex IDs. For
getAssignedUVs, the second list is a list of UV IDs. Those two lists are in
the same order, so we can zip them together to see which vertex corresponds
to which UV. Remember these lists are for every vertex on every face, so
each vertex ID will come up multiple times. If a vertex is not on a seam,
every time that vertex ID comes up, the corresponding UV ID will be the
same. If a vertex is on a seam, there will be two different UV IDs
corresponding to that vertex ID. My technique just counts how many
different UV IDs each vertex has, then adds then all together.

Now, if that's not what you need, to get UV shell information, you could
use MFnMesh.getUvShellsIds. To get border edges, you will have to use an
iterator. I might be able to take a look at that tomorrow, though no
guarantees :)


On 10 Nov. 2017 8:21 pm, "Benjam901" <***@gmail.com> wrote:

Hey Michael,

Thank you for the tips on the API and techniques to use for speed.

I was testing out your code and it works great. I am a bit confused though
(I have not managed to wrap my head around how each return list corresponds
to each other yet).

I need border edges for the UV shells. This way I can iterate each uv set,
get the border uvs, check that this particular UVs vertex has not already
been counted and add this to the total vert count rather than each UV.

- Ben
Post by Michael Boon
For speed, you definitely want to use OpenMaya, but more importantly,
avoid creating new Python objects every time you loop over a vert. Try to
create some lists of regular Python ints upfront, then just count them.
Once you have a MFnMesh, you can get a list of the UV IDs used at each
corner of each face using MFnMesh.getVertices() and MFnMesh.
getAssignedUVs().
polyVertices = fnMesh.getVertices()
polyUVIDs = fnMesh.getAssignedUVs() # with optional UV set name
Both those functions return two lists. The first list in each is the list
of vertex counts in each polygon, which we don't care about. The second
lists are the vert IDs, and the corresponding UV IDs. It's significant that
these are corresponding lists, since we're going to zip them together.
Create a list with an entry for each vert in the mesh, then you can put
the UV IDs each vert uses into its entry in the list. You know the number
of verts, so you can create the main list in one hit, for speed. By using
sets, you only record the unique IDs used at each vert.
vertUVIDs = [set() for _ in range(fnMesh.numVertices)]
Then go through the getAssignedUVs result in a loop and record which UV
(Note: itertools.izip will be much faster than regular zip here because it
doesn't create a new list)
vertUVIDs[vID].add(uvID)
totalVertCount = sum(len(s) for s in vertUVIDs)
Note that verts with no UVs will not be counted at all this way. You might
want to add a check that every set has at least one element in it.
I can count ~20k verts in ~30ms that way, which is certainly good enough
for games work :)
Post by Benjam901
Hello all,
So here is my struggle at the moment.
I am trying to guesstimate the "actual" in-engine vert count for a given
object in Maya so we can validate the LOD steps per object before we export.
*Proposed solution:*
The way I am trying to do this is by checking that a vertex is on a UV
border edge and adding this to the total vert count.
So for example an object with 500 verts which has a UV border straight
through the middle will end up with 1000 verts in-engine because of the UV
split.
The problem I am having is wrapping my head around the underlying data
models for verts, faces, edges and uvs.
*Solution 1:*
I have a sort of working solution that does not use the API but instead
relies on the mel command: *polySelectBorderShell 1*
import pymel.core as pm
import maya.mel as mel
sel = pm.ls(sl=True)[0] # Get object selection
# Edges must be selected here
mel.eval("polySelectBorderShell 1")
uvs = pm.polyListComponentConversion(fe=True, tuv=True)
uvList = pm.ls(uvs, flatten=True)
uvDict = {}
print i
uvNum = int(i.split('[')[-1].split(']')[0])
uvDict.update({uvNum:i})
vertBorders = {} # e.g. {'map1':[1, 2, 3, 5, 71]}
uvSets = pm.polyUVSet(sel, query=True, allUVSets=True)
vertBorders.update({uvSet:[]})
uvs = pm.polyEvaluate(sel, uvs=uvSet, uv=True)
vert = pm.polyListComponentConversion(uvDict[uvNum],
fuv=True, tv=True)[0]
vertBorders[uvSet].append(vert)
count = 0
multiList = vertBorders.values()
result = set(multiList[0]).intersection(*multiList[:1])
count += len(result)
newMultiList = []
newList = [i for i in L if i not in result]
count += len(newList)
print count
The above solution kinda gets me there and I need to make sure its legit
and patch it up but for the most part it works.
Problem is that it is *SLOW *hence trying to use the API.
*Solution 2:*
For the API I am following along with this mode of thinking here: *http://forums.cgsociety.org/archive/index.php?t-729364.html
<http://forums.cgsociety.org/archive/index.php?t-729364.html>*
Mu current solution and where I am stuck is here. I know I need to run
some comparisons but how and on what I am deadlocked at.
vertIt = OM.MItMeshVertex(dagPath)
vtxIdUtil = OM.MScriptUtil()
vtxIdUtil.createFromInt(0)
vtxIdPtr = vtxIdUtil.asIntPtr()
vertIt.setIndex(vertID, vtxIdPtr)
return vertIt
faceIt = OM.MItMeshPolygon(dagPath)
faceIdUtil = OM.MScriptUtil()
faceIdUtil.createFromInt(0)
faceIdPtr = faceIdUtil.asIntPtr()
faceIt.setIndex(faceId, faceIdPtr)
return faceIt
uArr = OM.MFloatArray()
vArr = OM.MFloatArray()
fIDs = OM.MIntArray()
uvIndices = OM.MIntArray()
uvSet = 'map1'
vertIn.getUVs(uArr, vArr, fIDs, uvSet)
vertIn.getUVIndices(uvIndices, uvSet)
print uArr, vArr, fIDs, uvIndices
return fIDs, uvIndices
fID1 = []
fID2 = []
fID1.append(fID)
fID2.append(fID)
return fID1, fID2
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
dagPath = OM.MDagPath()
sel.getDagPath(dagPath)
dagPath.extendToShape()
connFaces = OM.MIntArray()
edgeIter = OM.MItMeshEdge(dagPath)
f1 = None
f2 = None
edgeIter.getConnectedFaces(connFaces)
# open edge
print 'Open edge'
f1 = connFaces[0] # face 1
f2 = connFaces[1] # face 2
pass
vert1Index = edgeIter.index(0)
vert2Index = edgeIter.index(1)
MfVert1 = getVertFromId(dagPath, vert1Index)
MfVert2 = getVertFromId(dagPath, vert2Index)
fIdsvert1, uvIndicesVert1 = getVertUVInfo(MfVert1)
fIdsvert2, uvIndicesVert2 = getVertUVInfo(MfVert2)
edgeIter.next()
Any suggestions on where to go from here or how to solve this is very
much appreciated!
// Ben
--
You received this message because you are subscribed to a topic in the
Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/
topic/python_inside_maya/5rvHKbU9ipM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to
python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/
msgid/python_inside_maya/2d436e81-b03e-405e-b87b-
63f49173a9e7%40googlegroups.com
<https://groups.google.com/d/msgid/python_inside_maya/2d436e81-b03e-405e-b87b-63f49173a9e7%40googlegroups.com?utm_medium=email&utm_source=footer>
.

For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAA27_yJ3-FnWM_AwkjcNV37d2a524NbCnHQb3yxKq7D%2B4CpQwA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Ben Hearn
2017-11-10 12:51:46 UTC
Permalink
Hello Michael,

You explained it precisely how I needed it explained thank you! I was very
confused about the return values for vertex IDs and UV UDs and how they
tied together.

I am going to expand further and iterate each map and check that each
vertex has already been split or not. Am I correct in thinking that if a
vertex already belongs to a seam it would not split it again if it was on
another map?

- Ben
Post by Michael and Amanda
Hey Ben,
I have to admit I can't follow the UV shell technique you described. I
would expect a 500 vert object with a seam up the middle to have 500 +
(number of verts in the seam) verts in the game. The technique I described
does that, by counting verts twice if they are on UV seams (or more than
twice if they are on more than one seam).
I'll try to describe that technique a little better.
Actually it might help to describe how a mesh is put together first. There
is a list of vertex positions (which are points in space), and a list of
faces (each face is just a list of IDs into the vertex list). However for
efficiency, all the faces are run together into a single list, and there's
a second list that tells how many verts are in each face so you can count
through them to get to whichever face you want. Get your head around that,
then consider UVs are exactly the same. First there's a list of UVs (points
in 2D space), then there's a list of faces where each face is a list of IDs
into the UV list, and the list of faces is all run together just like it is
for vertices.
getVertices and getAssignedUVs each return two lists. The first list for
each is the number of verts in each face (which we don't care about in this
case). For getVertices, the second list is a list of vertex IDs. For
getAssignedUVs, the second list is a list of UV IDs. Those two lists are in
the same order, so we can zip them together to see which vertex corresponds
to which UV. Remember these lists are for every vertex on every face, so
each vertex ID will come up multiple times. If a vertex is not on a seam,
every time that vertex ID comes up, the corresponding UV ID will be the
same. If a vertex is on a seam, there will be two different UV IDs
corresponding to that vertex ID. My technique just counts how many
different UV IDs each vertex has, then adds then all together.
Now, if that's not what you need, to get UV shell information, you could
use MFnMesh.getUvShellsIds. To get border edges, you will have to use an
iterator. I might be able to take a look at that tomorrow, though no
guarantees :)
Hey Michael,
Thank you for the tips on the API and techniques to use for speed.
I was testing out your code and it works great. I am a bit confused though
(I have not managed to wrap my head around how each return list corresponds
to each other yet).
I need border edges for the UV shells. This way I can iterate each uv set,
get the border uvs, check that this particular UVs vertex has not already
been counted and add this to the total vert count rather than each UV.
- Ben
Post by Michael Boon
For speed, you definitely want to use OpenMaya, but more importantly,
avoid creating new Python objects every time you loop over a vert. Try to
create some lists of regular Python ints upfront, then just count them.
Once you have a MFnMesh, you can get a list of the UV IDs used at each
corner of each face using MFnMesh.getVertices() and MFnMesh.
getAssignedUVs().
polyVertices = fnMesh.getVertices()
polyUVIDs = fnMesh.getAssignedUVs() # with optional UV set name
Both those functions return two lists. The first list in each is the list
of vertex counts in each polygon, which we don't care about. The second
lists are the vert IDs, and the corresponding UV IDs. It's significant that
these are corresponding lists, since we're going to zip them together.
Create a list with an entry for each vert in the mesh, then you can put
the UV IDs each vert uses into its entry in the list. You know the number
of verts, so you can create the main list in one hit, for speed. By using
sets, you only record the unique IDs used at each vert.
vertUVIDs = [set() for _ in range(fnMesh.numVertices)]
Then go through the getAssignedUVs result in a loop and record which UV
(Note: itertools.izip will be much faster than regular zip here because
it doesn't create a new list)
vertUVIDs[vID].add(uvID)
totalVertCount = sum(len(s) for s in vertUVIDs)
Note that verts with no UVs will not be counted at all this way. You
might want to add a check that every set has at least one element in it.
I can count ~20k verts in ~30ms that way, which is certainly good enough
for games work :)
Post by Benjam901
Hello all,
So here is my struggle at the moment.
I am trying to guesstimate the "actual" in-engine vert count for a given
object in Maya so we can validate the LOD steps per object before we export.
*Proposed solution:*
The way I am trying to do this is by checking that a vertex is on a UV
border edge and adding this to the total vert count.
So for example an object with 500 verts which has a UV border straight
through the middle will end up with 1000 verts in-engine because of the UV
split.
The problem I am having is wrapping my head around the underlying data
models for verts, faces, edges and uvs.
*Solution 1:*
I have a sort of working solution that does not use the API but instead
relies on the mel command: *polySelectBorderShell 1*
import pymel.core as pm
import maya.mel as mel
sel = pm.ls(sl=True)[0] # Get object selection
# Edges must be selected here
mel.eval("polySelectBorderShell 1")
uvs = pm.polyListComponentConversion(fe=True, tuv=True)
uvList = pm.ls(uvs, flatten=True)
uvDict = {}
print i
uvNum = int(i.split('[')[-1].split(']')[0])
uvDict.update({uvNum:i})
vertBorders = {} # e.g. {'map1':[1, 2, 3, 5, 71]}
uvSets = pm.polyUVSet(sel, query=True, allUVSets=True)
vertBorders.update({uvSet:[]})
uvs = pm.polyEvaluate(sel, uvs=uvSet, uv=True)
vert = pm.polyListComponentConversion(uvDict[uvNum],
fuv=True, tv=True)[0]
vertBorders[uvSet].append(vert)
count = 0
multiList = vertBorders.values()
result = set(multiList[0]).intersection(*multiList[:1])
count += len(result)
newMultiList = []
newList = [i for i in L if i not in result]
count += len(newList)
print count
The above solution kinda gets me there and I need to make sure its legit
and patch it up but for the most part it works.
Problem is that it is *SLOW *hence trying to use the API.
*Solution 2:*
For the API I am following along with this mode of thinking here: *http://forums.cgsociety.org/archive/index.php?t-729364.html
<http://forums.cgsociety.org/archive/index.php?t-729364.html>*
Mu current solution and where I am stuck is here. I know I need to run
some comparisons but how and on what I am deadlocked at.
vertIt = OM.MItMeshVertex(dagPath)
vtxIdUtil = OM.MScriptUtil()
vtxIdUtil.createFromInt(0)
vtxIdPtr = vtxIdUtil.asIntPtr()
vertIt.setIndex(vertID, vtxIdPtr)
return vertIt
faceIt = OM.MItMeshPolygon(dagPath)
faceIdUtil = OM.MScriptUtil()
faceIdUtil.createFromInt(0)
faceIdPtr = faceIdUtil.asIntPtr()
faceIt.setIndex(faceId, faceIdPtr)
return faceIt
uArr = OM.MFloatArray()
vArr = OM.MFloatArray()
fIDs = OM.MIntArray()
uvIndices = OM.MIntArray()
uvSet = 'map1'
vertIn.getUVs(uArr, vArr, fIDs, uvSet)
vertIn.getUVIndices(uvIndices, uvSet)
print uArr, vArr, fIDs, uvIndices
return fIDs, uvIndices
fID1 = []
fID2 = []
fID1.append(fID)
fID2.append(fID)
return fID1, fID2
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
dagPath = OM.MDagPath()
sel.getDagPath(dagPath)
dagPath.extendToShape()
connFaces = OM.MIntArray()
edgeIter = OM.MItMeshEdge(dagPath)
f1 = None
f2 = None
edgeIter.getConnectedFaces(connFaces)
# open edge
print 'Open edge'
f1 = connFaces[0] # face 1
f2 = connFaces[1] # face 2
pass
vert1Index = edgeIter.index(0)
vert2Index = edgeIter.index(1)
MfVert1 = getVertFromId(dagPath, vert1Index)
MfVert2 = getVertFromId(dagPath, vert2Index)
fIdsvert1, uvIndicesVert1 = getVertUVInfo(MfVert1)
fIdsvert2, uvIndicesVert2 = getVertUVInfo(MfVert2)
edgeIter.next()
Any suggestions on where to go from here or how to solve this is very
much appreciated!
// Ben
--
You received this message because you are subscribed to a topic in the
Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/to
pic/python_inside_maya/5rvHKbU9ipM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to
To view this discussion on the web visit https://groups.google.com/d/ms
gid/python_inside_maya/2d436e81-b03e-405e-b87b-63f49173a9e7%
40googlegroups.com
<https://groups.google.com/d/msgid/python_inside_maya/2d436e81-b03e-405e-b87b-63f49173a9e7%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to a topic in the
Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/
topic/python_inside_maya/5rvHKbU9ipM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to
To view this discussion on the web visit https://groups.google.com/d/
msgid/python_inside_maya/CAA27_yJ3-FnWM_AwkjcNV37d2a524NbCnHQb3yxKq7D%
2B4CpQwA%40mail.gmail.com
<https://groups.google.com/d/msgid/python_inside_maya/CAA27_yJ3-FnWM_AwkjcNV37d2a524NbCnHQb3yxKq7D%2B4CpQwA%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
Tel - +46 76245 92 90 (Sweden)
LinkedIn: http://www.linkedin.com/pub/ben-hearn/50/a64/33b
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAM2ybkUhnbOORU2VHpkDbLe8pxm12Z-H83GaUPFG_yNK_arH8g%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Benjam901
2017-11-10 14:10:22 UTC
Permalink
Hey Michael,

I copied some UVs into a new UV map and tried the same method and to
highlight what was happening I am selecting the UVs.

For the 2nd UV set however the method looks like it fails, when I select
the UVs it selects almost all the verts in the map.

I amended the code to give me the additional vert count i.e. verts only on
seams adn the output for the 2nd map is 166 which is not correct.

Can you replicate this error also?

Code and screenshot is below:

import pymel.core as pm
import maya.OpenMaya as OM
from itertools import izip

sel = pm.ls(sl=True)[0]
selName = str(sel.nodeName())

mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
path = OM.MDagPath()
sel.getDagPath(path)
fnMesh = OM.MFnMesh(path)

vertCount = OM.MIntArray()
vertList = OM.MIntArray()

uvCounts = OM.MIntArray()
uvIDs = OM.MIntArray()
#uvSet='map1'

set2 = 'uvSet'
tempSets = ['map1', 'uvSet']

additional = 0

#for uvSet in tempSets:
fnMesh.getVertices(vertCount, vertList)
#fnMesh.getAssignedUVs(uvCounts, uvIDs, uvSet)
fnMesh.getAssignedUVs(uvCounts, uvIDs, set2)

vertUVIDs = [set() for _ in range(fnMesh.numVertices())]

for vID, uvID in izip(vertList, uvIDs):
vertUVIDs[vID].add(uvID)

totalVertCount = sum(len(s) for s in vertUVIDs if len(s)>1)
additional += totalVertCount
print additional

totes = [i for i in vertUVIDs if len(i) > 1]
for i in totes:
for j in i:
pm.select('{0}.map[{1}]'.format(selName, j), add=True)



[image: Inline images 1]
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/ec93bf53-ff47-41f5-868e-4912c73b8cc8%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael Boon
2017-11-15 05:39:43 UTC
Permalink
Hey, in case you haven't figured this out yourself by now, here's how I'd
do it. This is very similar to the approach from my first post, but it
grabs UVs from the first two channels, allows for faces with no UVs, and
also allows for easy extension to handle vertex colors or normals (as I
expect your engine splits on normals as well as UVs).

I've put a bunch of comments inline. With all the lists of vertex IDs it
gets very hard to name variables so they make sense. I create a bunch of
generators that compartmentalize the code more, but whether that would
actually make it clearer is debatable.

A couple of essential concepts:

- zip, (and izip, izip_longest)
zip takes two or more lists, and returns the first element of all of
them, then the second, then the third, etc. izip does the same but is more
memory efficient and hence faster. izip_longest is only different when the
lists have different lengths. zip and izip will stop when they reach the
end of the shortest list. izip_longest will keep returning values until it
reaches the end of the longest list, and you can supply a "fillvalue" that
it returns for the shorter lists.
- vertex splitting
I'm assuming your engine works like the others I've seen. This means
that each vertex has a position, one UV for each channel, and probably one
normal too. One way to go from Maya verts to engine verts, is to split the
Maya mesh along UV seams (and probably along hard edges). The way I do that
is by actually creating those engine-style vertices (a position and 1 or 2
UVs) for every corner of every face, and then counting how many unique
vertices I get. It's simpler to do that than to chase edges looking for UV
seams or UV shells.

Anyhow, hopefully this code works for you, and makes sense!

import maya.api.OpenMaya as om
from itertools import izip, izip_longest

# Get the currently selected mesh.
mSelList = om.MGlobal.getActiveSelectionList()
path = mSelList.getDagPath(0)
fnMesh = om.MFnMesh(path)

# So the engine splits vertices if their UVs are different.
# Another way of looking at it, is that the engine stores each vertex like
this:
# (position, uv0, uv1)
# So that's what we're going to do. We'll collect the position and the uvs
of each
# corner of each face, then put each of those corners into a set to find
out how many
# unique corners there are. That should tell us exactly how many vertices
the engine
# will count.
# Note that we are actually storing IDs rather than real values, just
because it's
# easier.
allFacesVertCounts = []
allFacesVertIDs = []
counter = []

# The first "channel" of face-vertex properties we want is the regular
vertex positions.
polyVertices = fnMesh.getVertices()
allFacesVertCounts.append(polyVertices[0])
allFacesVertIDs.append(polyVertices[1])
counter.append(0)

# The next 2 channels are the UVs. This collects the first 2 UV channels
but you could
# change that to whatever you want.
numUVChannelsUsed = 2
uvSets = fnMesh.getUVSetNames()
for uvSet in uvSets[:numUVChannelsUsed]:
f, ids = fnMesh.getAssignedUVs(uvSet)
allFacesVertCounts.append(f)
allFacesVertIDs.append(ids)
counter.append(0)

# If you wanted to, you could also add normals, tangents, binormals and/or
colors here.

numVertProperties = len(counter)
print 'Counting {} properties for {} vertex-faces...'.format(
numVertProperties, len(allFacesVertIDs[0]))

uniqueVerts = set()
assert [len(c) == len(allFacesVertCounts[0]) for c in allFacesVertCounts]
for vertCounts in izip(*allFacesVertCounts):
# Inside this loop, we're looking at one face at a time.
# vertCounts contains the number of verts this face has in each channel.
# vertCounts[0] will be the number of actual verts.
# The other channels will either have the same number (if the face is
mapped)
# or 0 (if the face is not mapped in this channel).
assert len(vertCounts) == numVertProperties, (len(vertCounts),
numVertProperties)
faceVertIds = [None] * numVertProperties
for i in range(numVertProperties):
# Some faces don't have UVs in some channels, so they will have 0
verts in
# that channel. We need to count through the UV id lists by the
correct
# amount for each channel.
assert isinstance(vertCounts[i], int), type(vertCounts[i])
assert (vertCounts[i] == 0 or vertCounts[i] == vertCounts[0]),
(vertCounts[i], vertCounts[0])
faceVertIds[i] =
allFacesVertIDs[i][counter[i]:counter[i]+vertCounts[i]]
counter[i] += vertCounts[i]
# izip_longest lets us fill any empty faces with a default value (None
in this case)
for vertParams in izip_longest(*faceVertIds, fillvalue=None):
# vertParams is a list of ids for this corner of the face.
# The first id is the vertex id. The second is the uv id in the
first uv set.
# We store all these ids because if any of them are different, the
game engine
# will create a new vert, so we want to count it as a new vert.
uniqueVerts.add(vertParams)
for i in range(numVertProperties):
assert counter[i] == len(allFacesVertIDs[i]), (i, counter[i],
len(allFacesVertIDs[i]))
print 'Done.'
print 'Number of Maya verts:', fnMesh.numVertices
print 'Number of face-verts:', len(allFacesVertIDs[0])
print 'Number of engine verts:', len(uniqueVerts)
Post by Ben Hearn
Hello Michael,
Thanks for the support on this it is much appreciated.
I was not aware that I was using API 1.0, I have been reading the C++ docs
for my reference so I assumed it was the correct API. I will make the
switch to 2.0.
The selection of the UVs is just a visual test to check that I am getting
the correct number of borders so we can ignore that issue (I switch UV sets
to make sure in my tests)
The reason I am only counting UVs on seams is those are the only ones that
split the vertex when we use them in the engine. We are trying to
"guess-timate" the total vert count before we jump into the engine so the
artist can be as efficient as possible with their source maya files and
using the UVs for the time being can get us as close as possible.
Not all faces have UVs in uvSet that I can confirm for sure so the lists
are out of sync. What if I iterated the face vertices and checked each
vertex has UVs first and then cross referenced each list?
Hi Ben,
I can't see your image, but I can run your code on a mesh of my own. In
really basic tests it seems to work correctly.
It's a bit tricky to read through. There's a fair bit of temp code in
there, and things that are assigned and not used. Also, why are you using
Python API 1.0? It's more difficult to use than 2.0 (unless you're more
familiar with C++, I guess).
There are a few ways it could go wrong.
- The most likely problem, I think, is that the code I gave you
assumes that every face has UVs. I said "Note that verts with no UVs will
not be counted at all this way. You might want to add a check that every
set has at least one element in it," and that the two face lists (from
getVertices and getAssignedUVs) will be identical. That was incorrect. If
any faces have no UVs, they will get a 0 in the face list from
getAssignedUVs and your vertex ID and UV ID lists will no longer correspond.
- There's also the problem that selecting '{0}.map[{1}]' will select
UVs in the currently active UV set, which may or may not be 'uvSet'. You
can set the currently active UV set in script, or in the UV Editor.
- The basic idea of doing "totalVertCount = sum(len(s) for s in
vertUVIDs if len(s)>1)" seems flawed to me. You're only counting
verts that are on seams. How are you going to use that number?
If you can get your image attached properly (I think the problem is at
your end...), and confirm that your mesh has UVs for all faces in 'uvSet',
we can build up from there.
Post by Benjam901
Hey Michael,
I copied some UVs into a new UV map and tried the same method and to
highlight what was happening I am selecting the UVs.
For the 2nd UV set however the method looks like it fails, when I select
the UVs it selects almost all the verts in the map.
I amended the code to give me the additional vert count i.e. verts only
on seams adn the output for the 2nd map is 166 which is not correct.
Can you replicate this error also?
import pymel.core as pm
import maya.OpenMaya as OM
from itertools import izip
sel = pm.ls(sl=True)[0]
selName = str(sel.nodeName())
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
path = OM.MDagPath()
sel.getDagPath(path)
fnMesh = OM.MFnMesh(path)
vertCount = OM.MIntArray()
vertList = OM.MIntArray()
uvCounts = OM.MIntArray()
uvIDs = OM.MIntArray()
#uvSet='map1'
set2 = 'uvSet'
tempSets = ['map1', 'uvSet']
additional = 0
fnMesh.getVertices(vertCount, vertList)
#fnMesh.getAssignedUVs(uvCounts, uvIDs, uvSet)
fnMesh.getAssignedUVs(uvCounts, uvIDs, set2)
vertUVIDs = [set() for _ in range(fnMesh.numVertices())]
vertUVIDs[vID].add(uvID)
totalVertCount = sum(len(s) for s in vertUVIDs if len(s)>1)
additional += totalVertCount
print additional
totes = [i for i in vertUVIDs if len(i) > 1]
pm.select('{0}.map[{1}]'.format(selName, j), add=True)
[image: Inline images 1]
--
You received this message because you are subscribed to a topic in the
Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit
https://groups.google.com/d/topic/python_inside_maya/5rvHKbU9ipM/unsubscribe
.
To unsubscribe from this group and all its topics, send an email to
To view this discussion on the web visit
https://groups.google.com/d/msgid/python_inside_maya/5c8e3e30-ad49-4d04-ae11-2a446fdf81dc%40googlegroups.com
<https://groups.google.com/d/msgid/python_inside_maya/5c8e3e30-ad49-4d04-ae11-2a446fdf81dc%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
Tel - +46 76245 92 90 (Sweden)
LinkedIn: http://www.linkedin.com/pub/ben-hearn/50/a64/33b
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/23ac14f9-6484-4bbf-8e86-e2003d9666e1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Ben Hearn
2017-11-23 15:16:56 UTC
Permalink
Hey Michael,

Thank you for the help on this I have learned a ton of good knowledge on
how engines store and process vertices from a mesh.

It took me a while to dig through your code and understand the relationship
between vert counts, ids and uv ids and how you have assembled the data but
I think I got it :D

Thanks again!

Ben
Post by Michael Boon
Hey, in case you haven't figured this out yourself by now, here's how I'd
do it. This is very similar to the approach from my first post, but it
grabs UVs from the first two channels, allows for faces with no UVs, and
also allows for easy extension to handle vertex colors or normals (as I
expect your engine splits on normals as well as UVs).
I've put a bunch of comments inline. With all the lists of vertex IDs it
gets very hard to name variables so they make sense. I create a bunch of
generators that compartmentalize the code more, but whether that would
actually make it clearer is debatable.
- zip, (and izip, izip_longest)
zip takes two or more lists, and returns the first element of all of
them, then the second, then the third, etc. izip does the same but is more
memory efficient and hence faster. izip_longest is only different when the
lists have different lengths. zip and izip will stop when they reach the
end of the shortest list. izip_longest will keep returning values until it
reaches the end of the longest list, and you can supply a "fillvalue" that
it returns for the shorter lists.
- vertex splitting
I'm assuming your engine works like the others I've seen. This means
that each vertex has a position, one UV for each channel, and probably one
normal too. One way to go from Maya verts to engine verts, is to split the
Maya mesh along UV seams (and probably along hard edges). The way I do that
is by actually creating those engine-style vertices (a position and 1 or 2
UVs) for every corner of every face, and then counting how many unique
vertices I get. It's simpler to do that than to chase edges looking for UV
seams or UV shells.
Anyhow, hopefully this code works for you, and makes sense!
import maya.api.OpenMaya as om
from itertools import izip, izip_longest
# Get the currently selected mesh.
mSelList = om.MGlobal.getActiveSelectionList()
path = mSelList.getDagPath(0)
fnMesh = om.MFnMesh(path)
# So the engine splits vertices if their UVs are different.
# Another way of looking at it, is that the engine stores each vertex like
# (position, uv0, uv1)
# So that's what we're going to do. We'll collect the position and the uvs
of each
# corner of each face, then put each of those corners into a set to find
out how many
# unique corners there are. That should tell us exactly how many vertices
the engine
# will count.
# Note that we are actually storing IDs rather than real values, just
because it's
# easier.
allFacesVertCounts = []
allFacesVertIDs = []
counter = []
# The first "channel" of face-vertex properties we want is the regular
vertex positions.
polyVertices = fnMesh.getVertices()
allFacesVertCounts.append(polyVertices[0])
allFacesVertIDs.append(polyVertices[1])
counter.append(0)
# The next 2 channels are the UVs. This collects the first 2 UV channels
but you could
# change that to whatever you want.
numUVChannelsUsed = 2
uvSets = fnMesh.getUVSetNames()
f, ids = fnMesh.getAssignedUVs(uvSet)
allFacesVertCounts.append(f)
allFacesVertIDs.append(ids)
counter.append(0)
# If you wanted to, you could also add normals, tangents, binormals and/or
colors here.
numVertProperties = len(counter)
print 'Counting {} properties for {} vertex-faces...'.format(
numVertProperties, len(allFacesVertIDs[0]))
uniqueVerts = set()
assert [len(c) == len(allFacesVertCounts[0]) for c in allFacesVertCounts]
# Inside this loop, we're looking at one face at a time.
# vertCounts contains the number of verts this face has in each channel.
# vertCounts[0] will be the number of actual verts.
# The other channels will either have the same number (if the face is
mapped)
# or 0 (if the face is not mapped in this channel).
assert len(vertCounts) == numVertProperties, (len(vertCounts),
numVertProperties)
faceVertIds = [None] * numVertProperties
# Some faces don't have UVs in some channels, so they will have 0
verts in
# that channel. We need to count through the UV id lists by the
correct
# amount for each channel.
assert isinstance(vertCounts[i], int), type(vertCounts[i])
assert (vertCounts[i] == 0 or vertCounts[i] == vertCounts[0]),
(vertCounts[i], vertCounts[0])
counter[i]+vertCounts[i]]
counter[i] += vertCounts[i]
# izip_longest lets us fill any empty faces with a default value (None
in this case)
# vertParams is a list of ids for this corner of the face.
# The first id is the vertex id. The second is the uv id in the
first uv set.
# We store all these ids because if any of them are different, the
game engine
# will create a new vert, so we want to count it as a new vert.
uniqueVerts.add(vertParams)
assert counter[i] == len(allFacesVertIDs[i]), (i, counter[i],
len(allFacesVertIDs[i]))
print 'Done.'
print 'Number of Maya verts:', fnMesh.numVertices
print 'Number of face-verts:', len(allFacesVertIDs[0])
print 'Number of engine verts:', len(uniqueVerts)
Post by Ben Hearn
Hello Michael,
Thanks for the support on this it is much appreciated.
I was not aware that I was using API 1.0, I have been reading the C++
docs for my reference so I assumed it was the correct API. I will make the
switch to 2.0.
The selection of the UVs is just a visual test to check that I am getting
the correct number of borders so we can ignore that issue (I switch UV sets
to make sure in my tests)
The reason I am only counting UVs on seams is those are the only ones
that split the vertex when we use them in the engine. We are trying to
"guess-timate" the total vert count before we jump into the engine so the
artist can be as efficient as possible with their source maya files and
using the UVs for the time being can get us as close as possible.
Not all faces have UVs in uvSet that I can confirm for sure so the lists
are out of sync. What if I iterated the face vertices and checked each
vertex has UVs first and then cross referenced each list?
Hi Ben,
I can't see your image, but I can run your code on a mesh of my own. In
really basic tests it seems to work correctly.
It's a bit tricky to read through. There's a fair bit of temp code in
there, and things that are assigned and not used. Also, why are you using
Python API 1.0? It's more difficult to use than 2.0 (unless you're more
familiar with C++, I guess).
There are a few ways it could go wrong.
- The most likely problem, I think, is that the code I gave you
assumes that every face has UVs. I said "Note that verts with no UVs will
not be counted at all this way. You might want to add a check that every
set has at least one element in it," and that the two face lists (from
getVertices and getAssignedUVs) will be identical. That was incorrect. If
any faces have no UVs, they will get a 0 in the face list from
getAssignedUVs and your vertex ID and UV ID lists will no longer correspond.
- There's also the problem that selecting '{0}.map[{1}]' will select
UVs in the currently active UV set, which may or may not be 'uvSet'. You
can set the currently active UV set in script, or in the UV Editor.
- The basic idea of doing "totalVertCount = sum(len(s) for s in
vertUVIDs if len(s)>1)" seems flawed to me. You're only counting
verts that are on seams. How are you going to use that number?
If you can get your image attached properly (I think the problem is at
your end...), and confirm that your mesh has UVs for all faces in 'uvSet',
we can build up from there.
Post by Benjam901
Hey Michael,
I copied some UVs into a new UV map and tried the same method and to
highlight what was happening I am selecting the UVs.
For the 2nd UV set however the method looks like it fails, when I
select the UVs it selects almost all the verts in the map.
I amended the code to give me the additional vert count i.e. verts only
on seams adn the output for the 2nd map is 166 which is not correct.
Can you replicate this error also?
import pymel.core as pm
import maya.OpenMaya as OM
from itertools import izip
sel = pm.ls(sl=True)[0]
selName = str(sel.nodeName())
mSelList = OM.MSelectionList()
OM.MGlobal.getActiveSelectionList(mSelList)
sel = OM.MItSelectionList(mSelList)
path = OM.MDagPath()
sel.getDagPath(path)
fnMesh = OM.MFnMesh(path)
vertCount = OM.MIntArray()
vertList = OM.MIntArray()
uvCounts = OM.MIntArray()
uvIDs = OM.MIntArray()
#uvSet='map1'
set2 = 'uvSet'
tempSets = ['map1', 'uvSet']
additional = 0
fnMesh.getVertices(vertCount, vertList)
#fnMesh.getAssignedUVs(uvCounts, uvIDs, uvSet)
fnMesh.getAssignedUVs(uvCounts, uvIDs, set2)
vertUVIDs = [set() for _ in range(fnMesh.numVertices())]
vertUVIDs[vID].add(uvID)
totalVertCount = sum(len(s) for s in vertUVIDs if len(s)>1)
additional += totalVertCount
print additional
totes = [i for i in vertUVIDs if len(i) > 1]
pm.select('{0}.map[{1}]'.format(selName, j), add=True)
[image: Inline images 1]
--
You received this message because you are subscribed to a topic in the
Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/to
pic/python_inside_maya/5rvHKbU9ipM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to
To view this discussion on the web visit https://groups.google.com/d/ms
gid/python_inside_maya/5c8e3e30-ad49-4d04-ae11-2a446fdf81dc%
40googlegroups.com
<https://groups.google.com/d/msgid/python_inside_maya/5c8e3e30-ad49-4d04-ae11-2a446fdf81dc%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
Tel - +46 76245 92 90 (Sweden)
LinkedIn: http://www.linkedin.com/pub/ben-hearn/50/a64/33b
--
You received this message because you are subscribed to a topic in the
Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/
topic/python_inside_maya/5rvHKbU9ipM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to
To view this discussion on the web visit https://groups.google.com/d/
msgid/python_inside_maya/23ac14f9-6484-4bbf-8e86-
e2003d9666e1%40googlegroups.com
<https://groups.google.com/d/msgid/python_inside_maya/23ac14f9-6484-4bbf-8e86-e2003d9666e1%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
Tel - +46 76245 92 90 (Sweden)
LinkedIn: http://www.linkedin.com/pub/ben-hearn/50/a64/33b
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAM2ybkVEH8LyKd9EDV%2BPwKDY19CgTDUjMoZs%2BSxeZEr4Cdy8sQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Loading...