简体   繁体   中英

Maya API Python symmetry table with MRichSelection?

I was wondering if there is a way to access the symmetry table of the MRichSelection having as a result the positive, the seam and the negative side with the positive and the negative ordered by vertex id correspondence. ie: vertex id 15 is the symmetry correlated to vert id 350. They are both at index 5 in the positive and negative list. I know I can achieve something similar using the filterXpand, but I believe the lists are not ordered in the way I can access the opposite vertex.

I don't know if you ever found a solution to this, but I will post mine for future TD's looking for a solution.

So let's assume you want to get the corresponding verts between left and right on the YZ plane. you have 2 different options. Using the MRichSelection to handle you symmetry table. Or calculate the vert yourself, by getting the smallest distance vector on the opposite side. Note: if you use the MRichSelection method, you will need to make sure that symmetry mode is enbaled in the viewport. I will show both answers, so lets get started:

Also note: I will be calculating the YZ Plane, as mentioned earlier. So adjust to your liking if needed.

Solution 1(Calculating yourself):

#importing the OpenMaya Module
from maya.api import OpenMaya as om
#converting selected object into MObject and MFnMesh functionset
mSel=om.MSelectionList()
mSel.add(cmds.ls(sl=1)[0])
mObj=mSel.getDagPath(0)
mfnMesh=om.MFnMesh(mObj)
#getting our basePoints
baseShape = mfnMesh.getPoints()
#this function can be used to revert the object back to the baseShape
mfnMesh.setPoints(baseShape)
#getting l and r verts
mtol=0.02# this will be our mid tolerance, if the mesh is not completely symmetric on the mid
lVerts=[]#for storing left Verts
rVerts=[]#for storing right Verts
mVerts=[]#for storing mid Verts
corrVerts={} #for storing correspondign verts
for i in range(mfnMesh.numVertices): #iteratign through all the verts on the mesh
    thisPoint = mfnMesh.getPoint(i) #getting current point position
    if thisPoint.x>0+mtol: # if pointValue on x axis is bigger than 0+midTolerance
        lVerts.append((i, thisPoint))#append to left vert storage list(i = vert index, thisPoint = vert Mpoint position)
    elif thisPoint.x<0-mtol: #opposite of left vert calculation
        rVerts.append((i, thisPoint))
    else: #if none of the above, assign to mid verts
        mVerts.append((i, thisPoint))
rVertspoints=[i for v,i in rVerts] #getting the vert mPoint positions of the right side
for vert, mp in lVerts: #going through our left points, unpacking our vert index and mPoint position()
    nmp=om.MPoint(-mp.x, mp.y, mp.z) #storing the reversed mpoint of the left side vert
    rp = mfnMesh.getClosestPoint(nmp)#getting the closest point on the mesh
    if rp[0] in rVertspoints: #cheking if the point is in the right side
        corrVerts[vert] = rVerts[rVertspoints.index(rp[0])][0] #adding it if it is true
    else:#if it is not, calculate closest vert
        #iterating through rVertspoints and find smallest distance
        dList=[nmp.distanceTo(rVert) for rVert in rVertspoints]#distance list for each vert based on input point
        mindist = min(dList)#getting the closest distance
        corrVerts[vert] = rVerts[dList.index(mindist)][0]#adding the vert
#now the corrVerts will have stored the corresponding vertices from left to right

Solution 2(using MRichSelection):

#MAKE SURE SYMMETRY IN THE VIEWPORT IS TURNED ON TO WORK! (will also work with topological symmetry)
#importing the OpenMaya Module
from maya.api import OpenMaya as om
#converting selected object into MObject and MFnMesh functionset
mSel=om.MSelectionList()
mSel.add(cmds.ls(sl=1)[0])
mObj=mSel.getDagPath(0)
mfnMesh=om.MFnMesh(mObj)
#getting our basePoints
baseShape = mfnMesh.getPoints()
#this function can be used to revert the object back to the baseShape
mfnMesh.setPoints(baseShape)
#getting l and r verts
mtol=0.02# this will be our mid tolerance, if the mesh is not completely symmetric on the mid
lVerts=[]#for storing left Verts
corrVerts={} #for storing correspondign verts
for i in range(mfnMesh.numVertices): #iteratign through all the verts on the mesh
    thisPoint = mfnMesh.getPoint(i) #getting current point position
    if thisPoint.x>0+mtol: # if pointValue on x axis is bigger than 0+midTolerance
        lVerts.append((i, thisPoint))#append to left vert storage list(i = vert index, thisPoint = vert Mpoint position)
#selecting our verts with symmetry on
SymSelection = cmds.select(["%s.vtx[%s]"%(mObj,i)  for i,v in lVerts], sym=True)
#getting the rich selection. it will store the symmetry iformation for us
mRichBase = om.MGlobal.getRichSelection()
lCor = mRichBase.getSelection()#this will store our lSide verts as an MSelectionList
rCor = mRichBase.getSymmetry()#this will symmetry verts as an MSelectionList
mitL = om.MItSelectionList(lCor)#creating iterative lists so we can get the components
mitR = om.MItSelectionList(rCor)
while not mitL.isDone():#iterating through the left list
    mitLComp = mitL.getComponent()#getting dag path and components of leftside
    mitRComp = mitR.getComponent()#getting dag path and components of rightside
    mitLCorVert = om.MItMeshVertex(mitLComp[0], mitLComp[1]) #creating our iterative vertex lists
    mitRCorVert = om.MItMeshVertex(mitRComp[0], mitRComp[1])
    while not mitLCorVert.isDone():#iterating through our verts
        corrVerts[mitLCorVert.index()] = mitRCorVert.index()#adding corresponding verts to our dictionary
        mitLCorVert.next()#go to next vert. needed to stop loop
        mitRCorVert.next()#go to next vert. needed to stop loop
    mitL.next()#go to next selection in list if more. needed to stop loop
    mitR.next()#go to next selection in list if more. needed to stop loop
cmds.select(cl=1)#deseleting our verts
#now the corrVerts will have stored the corresponding vertices from left to right

Hope it will help you all, looking for a few solutions. Cheers Bjarke Rauff, Rigging TD.

The answer by @Bjarke Rauff was very helpful, wanted to add a note about speed.

MFnMesh.getClosestPoint() builds an octree to efficiently find the point, but it will do that on every call. A mesh with 100k points can take up to 45s to process.

Use a MMeshIntersector() to cache the data between lookups. This speeds up the table creation by 900x for 100k points to .05s.

mesh # MDagpath obj to poly
flip_matrix # MTransformMatrix to flop the point
itMesh = om.MItMeshPolygon(mesh)

mesh.extendToShape()
matrix = mesh.inclusiveMatrix()
node = mesh.node()
intersector = om.MMeshIntersector()
intersector.create(node, matrix)
if not (intersector.isCreated):
    print("Failed to create mesh intersector")
    return
flipped_ids={}
while not itMesh.isDone():
    id = itMesh.index()
    face_center = itMesh.center()
    # flop the point across the axis
    flipped_point = face_center*flip_matrix
    MpointOnMesh = intersector.getClosestPoint(flipped_point)
    if MpointOnMesh is not None:
        # get face id property from MPointOnMesh
        flipped_id = MpointOnMesh.face
        flipped_ids[id] = flipped_id
    else:
        print("No intersection")
    itMesh.next()

NOTE I tried hash tables with a tuple of the point as the key, but the point positions had slight variations, even with rounding, which created different hashes. I've tested the MRichSelection approach and it doesn't actually work consistently in practice. It seems like it works when you have a perfectly mirrored mesh, but that can't be assumed. The component lists are not necessarily in sync.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM