如何在多个 Maya 网格中找到匹配的顶点

How to find matching vertices in multiple maya meshes

我正在尝试将一个网格上的顶点位置与另一个网格上的顶点位置进行比较,并生成成对顶点列表,(最终目的是将颈部几何体上的顶点与 body地理。)

我'pairing'他们的方式是比较两个网格中所有顶点之间的距离,然后通过在单独的列表中对它们进行排序来匹配彼此最接近的顶点,(neck_geo_verts [0] 与 body_geo_verts[0] 配对。)

我想使用 OpenMaya,因为我听说它比 cmds.xform 快得多。

这是我目前获取顶点的代码,尽管它使用的是 cmds 而不是 Maya API。我很难从 Maya 文档中找到我需要的东西。

# The user selects an edge on both the bottom of the neck and top of the body, then this code gets all the vertices in an edge border on both of those geos and populates two lists with the vertices

import maya.cmds as mc
import maya.api.OpenMaya as om
import re

mc.unloadPlugin('testingPlugin.py')
mc.loadPlugin('testingPlugin.py')

def main():
    geoOneVerts = []
    geoTwoVerts = []

    edges = cmds.ls(selection=True, sn=True)

    geoOneEdgeNum = re.search(r"\[([0-9_]+)\]", edges[0])
    geoTwoEdgeNum = re.search(r"\[([0-9_]+)\]", edges[1])

    cmds.polySelect(add=True, edgeBorder=int(geoOneEdgeNum.group(1)))
    geoOneEdgeBorder = cmds.ls(selection=True, sn=True)
    geoOneEdgeVerts = cmds.polyInfo(edgeToVertex=True)

    for vertex in geoOneEdgeVerts:
        vertexPairNums = re.search(r":\s*([0-9_]+)\s*([0-9_]+)", vertex)
        geoOneVerts.append(vertexPairNums.group(1))
        geoOneVerts.append(vertexPairNums.group(2))

    cmds.polySelect(replace=True, edgeBorder=int(geoTwoEdgeNum.group(1)))
    geoTwoEdgeBorder = cmds.ls(selection=True, sn=True)
    geoTwoEdgeVerts = cmds.polyInfo(edgeToVertex=True)

    for vertex in geoTwoEdgeVerts:
        vertexPairNums = re.search(r":\s*([0-9_]+)\s*([0-9_]+)", vertex)
        geoTwoVerts.append(vertexPairNums.group(1))
        geoTwoVerts.append(vertexPairNums.group(2))

    geoOneVerts = list(set(geoOneVerts))
    geoTwoVerts = list(set(geoTwoVerts))

    # How do I use OpenMaya to compare the distance from the verts in both lists?

main()

编辑:此代码为我提供了两个列表,其中包含两个网格上顶点的 DAG 名称。我不确定如何获得这些顶点的位置来比较两个列表中顶点之间的距离,我也不确定我是否应该为此使用 maya.cmds 而不是 maya.api.OpenMaya 考虑到我要操作的顶点数量。

EDIT2:感谢 Theodox 和数百次搜索的帮助。我最终制作了一个版本,该版本使用边界顶点工作,并且假设两个网格上的成对顶点将处于相同的全局 space。出于性能原因,我选择使用 Maya API 和完全放弃 Maya 命令。

版本 1(使用边界顶点):

import maya.OpenMaya as om

def main():
    geo1Verts = om.MFloatPointArray()
    geo2Verts = om.MFloatPointArray()

    selectionList = om.MSelectionList()
    om.MGlobal.getActiveSelectionList(selectionList)

    geo1SeamVerts = getSeamVertsOn(selectionList, 1)
    geo2SeamVerts = getSeamVertsOn(selectionList, 2)

    pairedVertsDict = pairSeamVerts(geo1SeamVerts, geo2SeamVerts)

def getSeamVertsOn(objectList, objectNumber):
    count = 0 
    indexPointDict = {}
    selectedObject = om.MObject()

    iter = om.MItSelectionList(objectList, om.MFn.kGeometric)
    while not iter.isDone():
        count += 1

        connectedVerts = om.MIntArray()

        if (count != objectNumber):
            iter.next()
        else:
            iter.getDependNode(selectedObject)
            vertexIter = om.MItMeshVertex(selectedObject)

            while not vertexIter.isDone():
                if (vertexIter.onBoundary()):
                    vertex = om.MPoint()
                    vertex = vertexIter.position()
                    indexPointDict[int(vertexIter.index())] = vertex

                vertexIter.next()

            return indexPointDict

def pairSeamVerts (dictSeamVerts1, dictSeamVerts2):
    pairedVerts = {}

    if (len(dictSeamVerts1) >= len(dictSeamVerts2)):
        for vert1 in dictSeamVerts1:
            distance = 0
            closestDistance = 1000000
            vertPair = 0

            for vert2 in dictSeamVerts2:
                distance = dictSeamVerts1[vert1].distanceTo(dictSeamVerts2[vert2])

                if (distance < closestDistance):
                    closestDistance = distance
                    vertPair = vert2

            pairedVerts[vert1] = vertPair

        return (pairedVerts)

    else:
        for vert1 in dictSeamVerts2:
            distance = 0
            closestDistance = 1000000
            vertPair = 0

            for vert2 in dictSeamVerts1:
                distance = dictSeamVerts2[vert1].distanceTo(dictSeamVerts1[vert2])

                if (distance < closestDistance):
                    closestDistance = distance
                    vertPair = vert2

            pairedVerts[vert1] = vertPair

        return (pairedVerts)

main()

版本 2(假设成对的顶点共享全局 Space):

import maya.OpenMaya as om

def main():   
    selectionList = om.MSelectionList()
    om.MGlobal.getActiveSelectionList(selectionList)

    meshOneVerts = getVertPositions(selectionList, 1)
    meshTwoVerts = getVertPositions(selectionList, 2)

    meshOneHashedPoints = hashPoints(meshOneVerts)
    meshTwoHashedPoints = hashPoints(meshTwoVerts)

    matchingVertList = set(meshOneHashedPoints).intersection(meshTwoHashedPoints)

    pairedVertList = getPairIndices(meshOneHashedPoints, meshTwoHashedPoints, matchingVertList)

def getVertPositions(objectList, objectNumber):
    count = 0
    pointList = []

    iter = om.MItSelectionList(objectList, om.MFn.kGeometric)
    while not iter.isDone():
        count = count + 1
        if (count != objectNumber):
            iter.next()

        dagPath = om.MDagPath()
        iter.getDagPath(dagPath)
        mesh = om.MFnMesh(dagPath)

        meshPoints = om.MPointArray()
        mesh.getPoints(meshPoints, om.MSpace.kWorld)

        for point in range(meshPoints.length()):
            pointList.append([meshPoints[point][0], meshPoints[point][1], meshPoints[point][2]])
        return pointList

def hashPoints(pointList):
    _clamp = lambda p: hash(int(p * 10000) / 10000.00)

    hashedPointList = []

    for point in pointList:
        hashedPointList.append(hash(tuple(map(_clamp, point))))

    return (hashedPointList)

def getPairIndices(hashListOne, hashListTwo, matchingHashList):
    pairedVertIndices = []
    vertOneIndexList = []
    vertTwoIndexList = []

    for hash in matchingHashList:
        vertListOne = []
        vertListTwo = []

        for hashOne in range(len(hashListOne)):
            if (hashListOne[hashOne] == hash):
                vertListOne.append(hashOne)

        for hashTwo in range(len(hashListTwo)):
            if (hashListTwo[hashTwo] == hash):
                vertListTwo.append(hashTwo)

        pairedVertIndices.append([vertListOne, vertListTwo])

    return pairedVertIndices

main()

API 对于距离比较方法来说明显更快,但在这种情况下,我认为真正的杀手很可能是算法。将每个顶点与其他顶点进行比较需要大量的数学运算。

可能最简单的事情是想出一种哈希顶点的方法:将每个 xyz 点变成一个可以与其他点进行比较而无需计算距离的值:具有相同哈希值的两个顶点将必须处于相同的位置。您可以调整哈希算法以稍微量化顶点位置以同时解决浮点错误。

这是一种散列点的方法(低至 4 位有效数字,您可以通过更改 _clamp 中的常量对其进行调整):

def point_hash(point):
    '''
    hash a tuple, probably a cmds vertex pos
    '''
    _clamp = lambda p: hash(int(p * 10000) / 10000.00)
    return hash(tuple(map(_clamp, point)))

只要两组 verts 在相同的 space(大概是 world space)中散列,相同的散列将意味着匹配的 verts。您所要做的就是遍历每个网格,创建一个字典,将顶点散列键控到顶点索引。这是在 cmds 中执行此操作的方法:

def vert_dict(obj):
    '''
    returns a dictionary of hash: index pairs representing the hashed verts of <obj>
    '''
    results = dict()
    verts = cmds.xform(obj + ".vtx[*]", q=True, t=True, ws=True)
    total = len(verts)/ 3
    for v in range(total):
        idx = v * 3
        hsh = point_hash (verts[idx: idx + 3])
        results[hsh] = v
    return results         

您可以通过将两个字典中的键相交来找到相交的顶点 - 两个网格中都存在的顶点。然后通过在两个字典中查找将两个网格中的匹配顶点转换回顶点索引。

除非网格真的很重,否则这在没有 API 的情况下应该是可行的,因为所有工作都在没有 API 模拟的散列函数中进行。

唯一可能的问题是确保顶点在同一 space 中。如果出于某种原因无法使顶点进入相同 space,则您将不得不退回到基于距离的策略。