コード例 #1
1
def setSkinWeights( skinCluster, vertJointWeightData ):
	'''
	vertJointWeightData is a list of 2-tuples containing the vertex component name, and a list of 2-tuples
	containing the joint name and weight.  ie it looks like this:
	[ ('someMesh.vtx[0]', [('joint1', 0.25), 'joint2', 0.75)]),
	  ('someMesh.vtx[1]', [('joint1', 0.2), 'joint2', 0.7, 'joint3', 0.1)]),
	  ... ]
	'''

	#convert the vertex component names into vertex indices
	idxJointWeight = []
	for vert, jointsAndWeights in vertJointWeightData:
		idx = int( vert[ vert.rindex( '[' )+1:-1 ] )
		idxJointWeight.append( (idx, jointsAndWeights) )

	#get an MObject for the skin cluster node
	skinCluster = apiExtensions.asMObject( skinCluster )
	skinFn = MFnSkinCluster( skinCluster )

	#construct a dict mapping joint names to joint indices
	jApiIndices = {}
	_tmp = MDagPathArray()
	skinFn.influenceObjects( _tmp )
	for n in range( _tmp.length() ):
		jApiIndices[ str( _tmp[n].node() ) ] = skinFn.indexForInfluenceObject( _tmp[n] )

	weightListP = skinFn.findPlug( "weightList" )
	weightListObj = weightListP.attribute()
	weightsP = skinFn.findPlug( "weights" )

	tmpIntArray = MIntArray()
	baseFmtStr = str( skinCluster ) +'.weightList[%d]'  #pre build this string: fewer string ops == faster-ness!

	for vertIdx, jointsAndWeights in idxJointWeight:

		#we need to use the api to query the physical indices used
		weightsP.selectAncestorLogicalIndex( vertIdx, weightListObj )
		weightsP.getExistingArrayAttributeIndices( tmpIntArray )

		weightFmtStr = baseFmtStr % vertIdx +'.weights[%d]'

		#clear out any existing skin data - and awesomely we cannot do this with the api - so we need to use a weird ass mel command
		for n in range( tmpIntArray.length() ):
			removeMultiInstance( weightFmtStr % tmpIntArray[n] )

		#at this point using the api or mel to set the data is a moot point...  we have the strings already so just use mel
		for joint, weight in jointsAndWeights:
			if weight:
				try:
					infIdx = jApiIndices[ joint ]
				except KeyError:
					try:
						infIdx = jApiIndices[ joint.split( '|' )[0] ]
					except KeyError: continue

				setAttr( weightFmtStr % infIdx, weight )
コード例 #2
0
def setSkinWeights(skinCluster, vertJointWeightData):
    '''
	vertJointWeightData is a list of 2-tuples containing the vertex component name, and a list of 2-tuples
	containing the joint name and weight.  ie it looks like this:
	[ ('someMesh.vtx[0]', [('joint1', 0.25), 'joint2', 0.75)]),
	  ('someMesh.vtx[1]', [('joint1', 0.2), 'joint2', 0.7, 'joint3', 0.1)]),
	  ... ]
	'''

    #convert the vertex component names into vertex indices
    idxJointWeight = []
    for vert, jointsAndWeights in vertJointWeightData:
        idx = int(vert[vert.rindex('[') + 1:-1])
        idxJointWeight.append((idx, jointsAndWeights))

    #get an MObject for the skin cluster node
    skinCluster = apiExtensions.asMObject(skinCluster)
    skinFn = MFnSkinCluster(skinCluster)

    #construct a dict mapping joint names to joint indices
    jApiIndices = {}
    _tmp = MDagPathArray()
    skinFn.influenceObjects(_tmp)
    for n in range(_tmp.length()):
        jApiIndices[str(_tmp[n].node())] = skinFn.indexForInfluenceObject(
            _tmp[n])

    weightListP = skinFn.findPlug("weightList")
    weightListObj = weightListP.attribute()
    weightsP = skinFn.findPlug("weights")

    tmpIntArray = MIntArray()
    baseFmtStr = str(
        skinCluster
    ) + '.weightList[%d]'  #pre build this string: fewer string ops == faster-ness!

    for vertIdx, jointsAndWeights in idxJointWeight:

        #we need to use the api to query the physical indices used
        weightsP.selectAncestorLogicalIndex(vertIdx, weightListObj)
        weightsP.getExistingArrayAttributeIndices(tmpIntArray)

        weightFmtStr = baseFmtStr % vertIdx + '.weights[%d]'

        #clear out any existing skin data - and awesomely we cannot do this with the api - so we need to use a weird ass mel command
        for n in range(tmpIntArray.length()):
            removeMultiInstance(weightFmtStr % tmpIntArray[n])

        #at this point using the api or mel to set the data is a moot point...  we have the strings already so just use mel
        for joint, weight in jointsAndWeights:
            if weight:
                try:
                    infIdx = jApiIndices[joint]
                except KeyError:
                    try:
                        infIdx = jApiIndices[joint.split('|')[0]]
                    except KeyError:
                        continue

                setAttr(weightFmtStr % infIdx, weight)
コード例 #3
0
def SaveTFXBoneBinaryFile(filepath, selected_mesh_shape_name, meshShapedagPath,
                          rootPositions):
    #---------------------------------------------------------------------------
    # Build a face/triangle index list to convert face index into triangle index
    #---------------------------------------------------------------------------
    faceIter = OpenMaya.MItMeshPolygon(meshShapedagPath)
    triangleCount = 0
    faceTriaIndexList = []
    index = 0

    util = OpenMaya.MScriptUtil()
    util.createFromInt(0)

    while not faceIter.isDone():
        faceTriaIndexList.append(triangleCount)

        if faceIter.hasValidTriangulation():
            numTrianglesPtr = util.asIntPtr()
            faceIter.numTriangles(numTrianglesPtr)
            numTriangles = util.getInt(numTrianglesPtr)
            triangleCount += numTriangles

        faceIter.next()

    #print "TressFX: Triangle count:%d\n" % triangleCount

    #----------------------
    # Find the closest face
    #----------------------
    meshFn = OpenMaya.MFnMesh(meshShapedagPath)
    meshIntersector = OpenMaya.MMeshIntersector()
    meshIntersector.create(meshShapedagPath.node())

    triangleCounts = OpenMaya.MIntArray()
    triangleVertexIndices = OpenMaya.MIntArray(
    )  # the size of this array is three times of the number of total triangles
    meshFn.getTriangles(triangleCounts, triangleVertexIndices)

    vertexTriangleList = []

    triangleIdForStrandsList = []
    baryCoordList = []
    uvCoordList = []
    pointOnMeshList = []

    progressBar = ProgressBar('Collecting bone data', len(rootPositions))

    for i in range(len(rootPositions)):
        rootPoint = rootPositions[i]

        # Find the closest point info
        meshPt = OpenMaya.MPointOnMesh()
        meshIntersector.getClosestPoint(rootPoint, meshPt)
        pt = meshPt.getPoint()

        pointOnMesh = OpenMaya.MPoint()
        pointOnMesh.x = pt.x
        pointOnMesh.y = pt.y
        pointOnMesh.z = pt.z
        pointOnMeshList.append(pointOnMesh)

        # Find face index
        faceId = meshPt.faceIndex()

        # Find triangle index
        triangleId = faceTriaIndexList[faceId] + meshPt.triangleIndex()

        dummy = OpenMaya.MScriptUtil()
        dummyIntPtr = dummy.asIntPtr()
        triangleId_local = meshPt.triangleIndex(
        )  # This values is either 0 or 1. It is not a global triangle index. triangleId is the global triangle index.
        pointArray = OpenMaya.MPointArray()
        vertIdList = OpenMaya.MIntArray()
        faceIter.setIndex(faceId, dummyIntPtr)
        faceIter.getTriangle(triangleId_local, pointArray, vertIdList,
                             OpenMaya.MSpace.kWorld)

        vertexTriangleList.append(
            (vertIdList[0], vertIdList[1], vertIdList[2]))
        triangleIdForStrandsList.append(triangleId)

        # Find three vertex indices for the triangle. Following two lines should give us three correct vertex indices for the triangle. I haven't really verified though.
        #vertexIndex = [triangleVertexIndices[triangleId*3], triangleVertexIndices[triangleId*3+1], triangleVertexIndices[triangleId*3+2]]
        vertexIndex = [vertIdList[0], vertIdList[1], vertIdList[2]]

        # Find barycentric coordinates
        uvw = OpenMaya.MPoint()

        # Somehow, below code gives me negative barycentric coordinates sometimes.
        # uPtr = OpenMaya.MScriptUtil().asFloatPtr()
        # vPtr = OpenMaya.MScriptUtil().asFloatPtr()
        # meshPt.getBarycentricCoords(uPtr,vPtr)
        # uvw.x = OpenMaya.MScriptUtil(uPtr).asFloat()
        # uvw.y = OpenMaya.MScriptUtil(vPtr).asFloat()
        # uvw.z = 1.0 - uvw.x - uvw.y

        # Instead getting barycentric coords from getBarycentricCoords, we compute it by the following function.
        uvw_a = ComputeBarycentricCoordinates(pointArray[0], pointArray[1],
                                              pointArray[2], pointOnMesh)

        uvw.x = uvw_a[0]
        uvw.y = uvw_a[1]
        uvw.z = uvw_a[2]

        # barycentric coordinates should be non-zero
        uvw.x = max(uvw.x, 0)
        uvw.y = max(uvw.y, 0)
        uvw.z = max(uvw.z, 0)

        # normalize barycentric coordinates so that their sum is equal to 1
        sum = uvw.x + uvw.y + uvw.z
        uvw.x /= sum
        uvw.y /= sum
        uvw.z /= sum

        baryCoordList.append(uvw)

        # Find UV coordinates - We don't really use UV coords for tfxbone file.
        # util = OpenMaya.MScriptUtil()
        # util.createFromList([0.0, 0.0], 2)
        # uv_ptr = util.asFloat2Ptr()
        # meshFn.getUVAtPoint(rootPoint, uv_ptr)
        # u = OpenMaya.MScriptUtil.getFloat2ArrayItem(uv_ptr, 0, 0)
        # v = OpenMaya.MScriptUtil.getFloat2ArrayItem(uv_ptr, 0, 1)
        # uv_coord = OpenMaya.MPoint()
        # uv_coord.x = u
        # uv_coord.y = v
        # uv_coord.z = 0
        # uvCoordList.append(uv_coord)

        # update progress gui
        progressBar.Increment()

    progressBar.Kill()

    #-------------------------
    # Get skin cluster object
    #-------------------------
    skinClusterName = ''
    skinClusters = cmds.listHistory(selected_mesh_shape_name)
    skinClusters = cmds.ls(skinClusters, type="skinCluster")
    if skinClusters:
        skinClusterName = skinClusters[0]
    else:
        cmds.warning('No skin cluster found on ' + selected_mesh_shape_name)
        return

    #print skinClusterName

    #---------------------------------------------------------------------------------------------------
    # TODO: Try the following method.
    # skins = filter(lambda skin: mesh.getShape() in skin.getOutputGeometry(), ls(type='skinCluster'))

    # if len(skins) > 0 :
    # skin = skins[0]
    # skinJoints = skin.influenceObjects();
    # root = skinJoints[0]

    # while root.getParent() :
    # root = root.getParent()

    # skin.getWeights(mesh.verts[index])

    # select(root, hierarchy=True, replace=True)
    # joints = ls(selection=True, transforms=True, type='joint')
    #---------------------------------------------------------------------------------------------------

    # get the MFnSkinCluster using skinClusterName
    selList = OpenMaya.MSelectionList()
    selList.add(skinClusterName)
    skinClusterNode = OpenMaya.MObject()
    selList.getDependNode(0, skinClusterNode)
    skinFn = OpenMayaAnim.MFnSkinCluster(skinClusterNode)

    dagPaths = MDagPathArray()
    skinFn.influenceObjects(dagPaths)

    # influence object is a joint
    influenceObjectsNames = []

    # get joint names
    for i in range(dagPaths.length()):
        influenceName = dagPaths[i].partialPathName()
        influenceObjectsNames.append(
            influenceName)  # Need to remove namespace?

    skinMeshes = cmds.skinCluster(skinClusterName, query=1, geometry=1)
    geoIter = OpenMaya.MItGeometry(meshShapedagPath)
    infCount = OpenMaya.MScriptUtil()
    infCountPtr = infCount.asUintPtr()
    numVertices = geoIter.count()

    weightArray = [
        0
    ] * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT * numVertices  # joint weight array for all vertices. Each vertex will have TRESSFX_MAX_INFLUENTIAL_BONE_COUNT  weights.
    # It is initialized with zero for empty weight in case there are less weights than TRESSFX_MAX_INFLUENTIAL_BONE_COUNT .
    jointIndexArray = [
        -1
    ] * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT * numVertices  # joint index array for all vertices. It is initialized with -1 for an empty element in case
    # there are less weights than TRESSFX_MAX_INFLUENTIAL_BONE_COUNT .

    # collect bone weights for all vertices in the mesh
    index = 0

    while geoIter.isDone() == False:
        weights = OpenMaya.MDoubleArray()
        skinFn.getWeights(meshShapedagPath, geoIter.currentItem(), weights,
                          infCountPtr)

        weightJointIndexPairs = []

        for i in range(len(weights)):
            pair = WeightJointIndexPair()
            pair.weight = weights[i]
            pair.joint_index = i
            weightJointIndexPairs.append(pair)

        weightJointIndexPairs.sort()

        a = 0

        for j in range(
                min(len(weightJointIndexPairs),
                    TRESSFX_MAX_INFLUENTIAL_BONE_COUNT)):
            weightArray[index * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT +
                        a] = weightJointIndexPairs[j].weight
            jointIndexArray[index * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT +
                            a] = weightJointIndexPairs[j].joint_index
            a += 1

        index += 1
        geoIter.next()

    #------------------------
    # Save the tfxbone file.
    #------------------------
    progressBar = ProgressBar(
        'Saving a tfxbone file',
        len(influenceObjectsNames) + len(triangleIdForStrandsList))
    f = open(filepath, "wb")
    # Number of Bones
    f.write(ctypes.c_int(len(influenceObjectsNames)))

    # Write all bone (joint) names
    for i in range(len(influenceObjectsNames)):
        # Bone Joint Index
        f.write(ctypes.c_int(i))
        # Size of the string, add 1 to leave room for the nullterminate.
        f.write(ctypes.c_int(len(influenceObjectsNames[i]) + 1))
        # Print the characters of the string 1 by 1.
        for j in range(len(influenceObjectsNames[i])):
            f.write(ctypes.c_byte(ord(influenceObjectsNames[i][j])))
        # Add a zero to null terminate the string.
        f.write(ctypes.c_byte(0))
        progressBar.Increment()

    # Number of Strands
    f.write(ctypes.c_int(len(triangleIdForStrandsList)))

    for i in range(len(triangleIdForStrandsList)):
        triangleId = triangleIdForStrandsList[i]

        # three vertex indices from one triangle - Following two lines should work equally but I haven't verified it yet.
        #vertexIndices = [triangleVertexIndices[triangleId*3], triangleVertexIndices[triangleId*3+1], triangleVertexIndices[triangleId*3+2]]
        vertexIndices = vertexTriangleList[i]

        baryCoord = baryCoordList[i]
        weightJointIndexPairs = GetSortedWeightsFromTriangleVertices(
            TRESSFX_MAX_INFLUENTIAL_BONE_COUNT, vertexIndices, jointIndexArray,
            weightArray, baryCoord)

        # Index, the rest should be self explanatory.
        f.write(ctypes.c_int(i))
        f.write(ctypes.c_int(weightJointIndexPairs[0].joint_index))
        f.write(ctypes.c_float(weightJointIndexPairs[0].weight))
        f.write(ctypes.c_int(weightJointIndexPairs[1].joint_index))
        f.write(ctypes.c_float(weightJointIndexPairs[1].weight))
        f.write(ctypes.c_int(weightJointIndexPairs[2].joint_index))
        f.write(ctypes.c_float(weightJointIndexPairs[2].weight))
        f.write(ctypes.c_int(weightJointIndexPairs[3].joint_index))
        f.write(ctypes.c_float(weightJointIndexPairs[3].weight))
        progressBar.Increment()

    f.close()
    progressBar.Kill()
    return
コード例 #4
0
def ExportMesh(filepath, meshShapedagPath):
    meshFn = OpenMaya.MFnMesh(meshShapedagPath)
    meshIntersector = OpenMaya.MMeshIntersector()
    meshIntersector.create(meshShapedagPath.node())

    faceIdList = []
    baryCoordList = []

    points = OpenMaya.MPointArray()
    meshFn.getPoints(points, OpenMaya.MSpace.kWorld)

    normals = OpenMaya.MFloatVectorArray()
    meshFn.getVertexNormals(False, normals, OpenMaya.MSpace.kWorld)

    triangleCounts = OpenMaya.MIntArray()
    triangleVertexIndices = OpenMaya.MIntArray(
    )  # the size of this array is three times of the number of total triangles
    meshFn.getTriangles(triangleCounts, triangleVertexIndices)

    #-------------------------
    # Get skin cluster object
    #-------------------------
    skinClusterName = ''
    mesh_shape_name = meshFn.name()
    skinClusters = cmds.listHistory(mesh_shape_name)
    skinClusters = cmds.ls(skinClusters, type="skinCluster")
    if skinClusters:
        skinClusterName = skinClusters[0]
    else:
        cmds.warning('No skin cluster found on ' + mesh_shape_name)
        return

    #print skinClusterName

    # get the MFnSkinCluster using skinClusterName
    selList = OpenMaya.MSelectionList()
    selList.add(skinClusterName)
    skinClusterNode = OpenMaya.MObject()
    selList.getDependNode(0, skinClusterNode)
    skinFn = OpenMayaAnim.MFnSkinCluster(skinClusterNode)

    dagPaths = MDagPathArray()
    skinFn.influenceObjects(dagPaths)

    # influence object is a joint
    influenceObjectsNames = []

    # get joint names
    for i in range(dagPaths.length()):
        influenceName = dagPaths[i].partialPathName()
        influenceObjectsNames.append(
            influenceName)  # Need to remove namespace?

    skinMeshes = cmds.skinCluster(skinClusterName, query=1, geometry=1)
    geoIter = OpenMaya.MItGeometry(meshShapedagPath)
    infCount = OpenMaya.MScriptUtil()
    infCountPtr = infCount.asUintPtr()
    numVertices = geoIter.count()

    weightArray = [
        0
    ] * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT * numVertices  # joint weight array for all vertices. Each vertex will have TRESSFX_MAX_INFLUENTIAL_BONE_COUNT  weights.
    # It is initialized with zero for empty weight in case there are less weights than TRESSFX_MAX_INFLUENTIAL_BONE_COUNT .
    jointIndexArray = [
        -1
    ] * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT * numVertices  # joint index array for all vertices. It is initialized with -1 for an empty element in case
    # there are less weights than TRESSFX_MAX_INFLUENTIAL_BONE_COUNT .

    # collect bone weights for all vertices in the mesh
    index = 0

    progressBar = ProgressBar('Collect data', numVertices)

    while geoIter.isDone() == False:
        weights = OpenMaya.MDoubleArray()
        skinFn.getWeights(meshShapedagPath, geoIter.currentItem(), weights,
                          infCountPtr)

        weightJointIndexPairs = []

        for i in range(len(weights)):
            pair = WeightJointIndexPair()
            pair.weight = weights[i]
            pair.joint_index = i
            weightJointIndexPairs.append(pair)

        weightJointIndexPairs.sort()

        a = 0

        for j in range(
                min(len(weightJointIndexPairs),
                    TRESSFX_MAX_INFLUENTIAL_BONE_COUNT)):
            weightArray[index * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT +
                        a] = weightJointIndexPairs[j].weight
            jointIndexArray[index * TRESSFX_MAX_INFLUENTIAL_BONE_COUNT +
                            a] = weightJointIndexPairs[j].joint_index
            a += 1

        index += 1
        progressBar.Increment()
        geoIter.next()

    progressBar.Kill()

    #----------------------------------------------------------
    # We collected all necessary data. Now save them in file.
    #----------------------------------------------------------
    totalProgress = points.length() + triangleVertexIndices.length() / 3 + len(
        influenceObjectsNames)
    progressBar = ProgressBar('Export collision mesh', totalProgress)

    f = open(filepath, "w")
    f.write("# TressFX collision mesh exported by TressFX Exporter in Maya\n")

    # Write all bone (joint) names
    f.write("numOfBones %g\n" % (len(influenceObjectsNames)))
    f.write("# bone index, bone name\n")
    for i in range(len(influenceObjectsNames)):
        f.write("%d %s\n" % (i, influenceObjectsNames[i]))
        progressBar.Increment()

    # write vertex positions and skinning data
    f.write("numOfVertices %g\n" % (points.length()))
    f.write(
        "# vertex index, vertex position x, y, z, normal x, y, z, joint index 0, joint index 1, joint index 2, joint index 3, weight 0, weight 1, weight 2, weight 3\n"
    )
    for vertexIndex in xrange(points.length()):
        point = points[vertexIndex]
        normal = normals[vertexIndex]
        weightJointIndexPairs = GetSortedWeightsFromOneVertex(
            TRESSFX_MAX_INFLUENTIAL_BONE_COUNT, vertexIndex, jointIndexArray,
            weightArray)
        f.write(
            "%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g\n" %
            (vertexIndex, point.x, point.y, point.z, normal.x, normal.y,
             normal.z, weightJointIndexPairs[0].joint_index,
             weightJointIndexPairs[1].joint_index,
             weightJointIndexPairs[2].joint_index,
             weightJointIndexPairs[3].joint_index,
             weightJointIndexPairs[0].weight, weightJointIndexPairs[1].weight,
             weightJointIndexPairs[2].weight, weightJointIndexPairs[3].weight))
        progressBar.Increment()

    # write triangle face indices
    f.write("numOfTriangles %g\n" % (triangleVertexIndices.length() / 3))
    f.write(
        "# triangle index, vertex index 0, vertex index 1, vertex index 2\n")
    for i in range(triangleVertexIndices.length() / 3):
        f.write("%g %d %d %d\n" % (i, triangleVertexIndices[i * 3 + 0],
                                   triangleVertexIndices[i * 3 + 1],
                                   triangleVertexIndices[i * 3 + 2]))
        progressBar.Increment()

    f.close()
    progressBar.Kill()
    return