【问题标题】:Calculating the new position of a parented controller after rotating the parent in Maya using Python使用 Python 在 Maya 中旋转父级后计算父级控制器的新位置
【发布时间】:2017-09-12 02:41:08
【问题描述】:

我正在创建代码以根据它在 Maya 中的关键帧位置创建控制器的运动路径。尝试使用此代码创建父控制器的运动路径时遇到问题。如果我旋转并平移父对象,则生成的运动路径不会反映实际的运动路径。相反,它会创建运动路径,就好像它不受父级影响一样。我环顾四周,找到了使用矩阵变换将旋转应用于当前位置的信息,但它似乎旋转得太多了。我已经包含了创建运动路径的函数,它有点长,但是在处理上躯干控制器时,不工作的部分在 else 语句中。

旧代码

#
# This function creates an animation curve within the scene that follows the path of motion
# of the selected controller. It requires keyframe information in order to genereate the curve
# and uses the range of frames given by the user.
#
def createAnimCurve( bodyField, startField, endField, firstColor ):
    # Takes the value of the text field to select the controller
    obj = cmds.textField(bodyField, query=True, text=True)
    print obj
    # Takes in the string input of the paramter values and turns them into integer values
    startFrame = cmds.intField(startField, query=True, value=True)
    print startFrame
    endFrame = cmds.intField(endField, query=True, value=True)
    print endFrame
    color = cmds.colorIndexSliderGrp( firstColor, query=True, value=True ) - 1
    print color

    if obj == "":
        cmds.warning( "WARNING: Need to Select Body Part from Diagram" )
        return
    if cmds.objExists(obj[:-3]+'Path'):
        # Creates a warning pop up that double checks if the user wants to remove the curve
        delRes = cmds.confirmDialog( title='Delete Path Warning', message='Recreation will delete current path. Are you sure?', button=['Yes','No'], defaultButton='Yes', cancelButton='No', dismissString='No' )
        # If yes then the curve is deleted
        if delRes == 'Yes':
            #cmds.delete(obj[:-3]+'ScalePath')  
            #cmds.delete(obj[:-3]+'ScalePath_LOC')  
            cmds.delete(obj[:-3]+'Path')     
            cmds.delete(obj[:-3]+'Path_LOC')
        else:
            return
    # Sorts through the list of keyframes of the selected obj from the selected time line
    global keyframes
    keyframes = sorted(list(set(cmds.keyframe(obj, q=True, time=(startFrame,endFrame), timeChange=True))))
    # Creates the arrays for the required point positions
    points = []
    centerPoints = []
    centerRotates = []
    combinedPoints = []

    # Special cases for controllers that are named differently than their joints
    if obj == "L_foot_CTL" or obj == "R_foot_CTL":
        loc = obj[:-4] + "Ankle_LOC"
    elif obj == "M_upTorso_CTL":
        loc = "M_spineTip_LOC"
    else:    
        loc = obj[:-3] + "LOC"
    # Grabs the original world space position to calculate the approraite motion points
    locPos = cmds.getAttr(loc+".translate")
    centerLocPos = cmds.getAttr("M_centerMass_LOC.translate")

    #for step in range( startFrame, endFrame+2, int(curveCVstep)):
    for step in range(len(keyframes)):
        # Moves throughout the specified timeline to find point results
        cmds.currentTime( keyframes[step] )
        if obj != "M_upTorso_CTL":
            # Queries the position of the controller to draw the curve
            # Adds the position of the controller in world space to draw it relative to the control
            pos = cmds.xform( obj,q=True,ws=True,t=True )
            pos[0] = pos[0] + locPos[0][0]
            pos[1] = pos[1] + locPos[0][1] 
            pos[2] = pos[2] + locPos[0][2]
            # convert the tuple (vector) to a string
            points.append(pos)
            print pos
        else:
            spineLength = cmds.getAttr('spineCurveInfo.arcLength')

            # Queries the position of the controller to draw the curve
            # Adds the position of the controller in world space to draw it relative to the control
            # adds in the spine length to the y position to take into consideration the offset of the centerMass controller
            pos = cmds.xform( obj,q=True,ws=True,t=True )
            pos[0] = pos[0] + locPos[0][0]
            pos[1] = pos[1] + locPos[0][1]
            pos[2] = pos[2] + locPos[0][2]
            # convert the tuple (vector) to a string
            print "Printing out points"
            points.append(pos)
            print pos

            # Queries the position of the center of mass controller 
            centerPos = cmds.xform( "M_centerMass_CTL",q=1,os=1,t=1 )
            centerPos[0] = centerPos[0] #+ centerLocPos[0][0]
            centerPos[1] = centerPos[1] #+ centerLocPos[0][1]
            centerPos[2] = centerPos[2] #+ centerLocPos[0][2]
            # convert the tuple (vector) to a string
            print "Printing out center Points"
            centerPoints.append(centerPos)
            print centerPos

            # Combine the two point positions to find the relative position 
            combinedPos = []
            combinedPos1 = pos[0] + centerPos[0]
            combinedPos.append(combinedPos1)
            combinedPos2 = pos[1] + centerPos[1]
            combinedPos.append(combinedPos2)
            combinedPos3 = pos[2] + centerPos[2]
            combinedPos.append(combinedPos3)
            print "Printing out combined Points"
            print combinedPos

            # Queries the rotation of the center of mass controller
            #centerRot = cmds.xform( "M_centerMass_CTL",q=1,ws=1,ro=1 )
            #centerRotates.append(centerRot)
            #print "Printing out rotations"
            #print centerRot
            # applies rotation of the center of mass controller to the upper torso controller
            # rotation around the Z axis
            #tempX = combinedPos[0]*math.cos(math.radians(centerRot[2])) - combinedPos[1]*math.sin(math.radians(centerRot[2]))
            #tempY = combinedPos[0]*math.sin(math.radians(centerRot[2])) + combinedPos[1]*math.cos(math.radians(centerRot[2]))
            # rotation around the Y axis
            #tempX2 = tempX*math.cos(math.radians(centerRot[1])) + combinedPos[2]*math.sin(math.radians(centerRot[1]))
            #tempZ = combinedPos[2]*math.cos(math.radians(centerRot[1])) - tempX*math.sin(math.radians(centerRot[1]))
            # rotation around the X axis
            #tempY2 = tempY*math.cos(math.radians(centerRot[0])) - tempZ*math.sin(math.radians(centerRot[0]))
            #tempZ2 = tempY*math.sin(math.radians(centerRot[0])) + tempZ*math.cos(math.radians(centerRot[0]))

            #combinedPos[0] = tempX2
            #combinedPos[1] = tempY2
            #combinedPos[2] = tempZ2
            #print "Printing out rotated Points"
            combinedPoints.append(combinedPos)
            print combinedPos

    # if the obj is the upper torso controller we need to take into consideration the center of mass controller
    # Creates the motion curve with the required cvs
    if obj == "M_upTorso_CTL":
        cur = cmds.curve(d=2, ws=True, p=combinedPoints, n=obj[:-3]+'Path')
        cmds.setAttr(cur + '.overrideEnabled', 1)
        cmds.setAttr(cur + '.overrideColor', color)
        print cur
        cmds.move(points[0][0], points[0][1], points[0][2], cur+".scalePivot", cur+".rotatePivot", absolute=True)
    else:
        cur = cmds.curve(d=2, ws=True, p=points, n=obj[:-3]+'Path')
        cmds.setAttr(cur + '.overrideEnabled', 1)
        cmds.setAttr(cur + '.overrideColor', color) 
        print cur
        cmds.move(points[0][0], points[0][1], points[0][2], cur+".scalePivot", cur+".rotatePivot", absolute=True)
    # command that runs through each cv of the curve and returns their position within a list.
    cvs = cmds.getAttr( obj[:-3]+'Path.cv[*]' )
    print cvs

    global initCVS
    initCVS = cvs
    # Create a locator for the motion path that the controller will now follow
    locate = cmds.spaceLocator( n=obj[:-3]+"Path_LOC" )
    #for step in range( startFrame, endFrame+2, int(curveCVstep)):
    for step in range(len(keyframes)):
        # Moves throughout the specified timeline to find point results
        cmds.currentTime( keyframes[step] )
        # Moves the locator to match the position of the controller
        cmds.move( cvs[step][0], cvs[step][1], cvs[step][2], locate)
        # Keyframes the locator
        cmds.setKeyframe( locate )
    # Position obj at the location of locate.
    cmds.pointConstraint( locate, obj, n=obj[:-3]+"LOC1_PNT" )
    cmds.setAttr( loc+'.visibility', 0)
    # keys the weight of the point constraint to 0 before and after time frame (set to 1 during time frame)
    #Before startFrame
    cmds.currentTime( startFrame - 1 )
    cmds.setAttr(obj+'.blendPoint1', 0 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    #After startframe
    cmds.currentTime( startFrame )
    cmds.setAttr(obj+'.blendPoint1', 1 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    #Before endframe
    cmds.currentTime( endFrame )
    cmds.setAttr(obj+'.blendPoint1', 1 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    #After endframe
    cmds.currentTime( endFrame + 1 )
    cmds.setAttr(obj+'.blendPoint1', 0 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    cmds.select(obj)

代码的问题是我冻结了控制器上的转换,将轴心设置为世界空间中的 (0,0,0)。解决此问题的最佳方法是创建一个临时定位器并让它跟随控制器。使用临时定位器的位置来创建控制器的运动路径。创建后删除临时定位器。

新代码

#
# This function creates an animation curve within the scene that follows the path of motion
# of the selected controller. It requires keyframe information in order to genereate the curve
# and uses the range of frames given by the user.
#
def createAnimCurve( bodyField, startField, endField, firstColor ):
    # Takes the value of the text field to select the controller
    obj = cmds.textField(bodyField, query=True, text=True)
    print obj
    # Takes in the string input of the paramter values and turns them into integer values
    startFrame = cmds.intField(startField, query=True, value=True)
    print startFrame
    endFrame = cmds.intField(endField, query=True, value=True)
    print endFrame
    color = cmds.colorIndexSliderGrp( firstColor, query=True, value=True ) - 1
    print color

    if obj == "":
        cmds.warning( "WARNING: Need to Select Body Part from Diagram" )
        return
    if cmds.objExists(obj[:-3]+'Path'):
        # Creates a warning pop up that double checks if the user wants to remove the curve
        delRes = cmds.confirmDialog( title='Delete Path Warning', message='Recreation will delete current path. Are you sure?', button=['Yes','No'], defaultButton='Yes', cancelButton='No', dismissString='No' )
        # If yes then the curve is deleted
        if delRes == 'Yes':
            cmds.delete(obj[:-3]+'Path')     
            cmds.delete(obj[:-3]+'Path_LOC')
        else:
            return
    # Sorts through the list of keyframes of the selected obj from the selected time line
    global keyframes
    keyframes = sorted(list(set(cmds.keyframe(obj, q=True, time=(startFrame,endFrame), timeChange=True))))
    # Creates the arrays for the required point positions
    points = []

    # Creates a temporary locator to find the world space values of the controller
    cmds.spaceLocator( n="tempLoc" )
    cmds.parentConstraint( obj, 'tempLoc', n='temp_PRT_CST' )

    #for step in range( startFrame, endFrame+2, int(curveCVstep)):
    for step in range(len(keyframes)):
        # Moves throughout the specified timeline to find point results
        cmds.currentTime( keyframes[step] )
        # Queries the position of the controller to draw the curve
        # Adds the position of the controller in world space to draw it relative to the control
        pos = cmds.xform( "tempLoc",q=True,ws=True,t=True )
        pos[0] = pos[0] 
        pos[1] = pos[1] 
        pos[2] = pos[2] 
        # convert the tuple (vector) to a string
        points.append(pos)
        print pos

    print "Creating the basic motion curve"
    cur = cmds.curve(d=2, ws=True, p=points, n=obj[:-3]+'Path')
    cmds.setAttr(cur + '.overrideEnabled', 1)
    cmds.setAttr(cur + '.overrideColor', color) 
    print cur
    cmds.move(points[0][0], points[0][1], points[0][2], cur+".scalePivot", cur+".rotatePivot", absolute=True)
    # command that runs through each cv of the curve and returns their position within a list.
    cvs = cmds.getAttr( obj[:-3]+'Path.cv[*]' )
    print cvs

    # Deletes the temp locator
    cmds.select("temp_PRT_CST")
    cmds.delete()
    cmds.select("tempLoc")
    cmds.delete()

    global initCVS
    initCVS = cvs
    # Create a locator for the motion path that the controller will now follow
    locate = cmds.spaceLocator( n=obj[:-3]+"Path_LOC" )
    #for step in range( startFrame, endFrame+2, int(curveCVstep)):
    for step in range(len(keyframes)):
        # Moves throughout the specified timeline to find point results
        cmds.currentTime( keyframes[step] )
        # Moves the locator to match the position of the controller
        cmds.move( cvs[step][0], cvs[step][1], cvs[step][2], locate)
        # Keyframes the locator
        cmds.setKeyframe( locate )
    # Position obj at the location of locate.
    cmds.pointConstraint( locate, obj, n=obj[:-3]+"LOC1_PNT" )
    # keys the weight of the point constraint to 0 before and after time frame (set to 1 during time frame)
    #Before startFrame
    cmds.currentTime( startFrame - 1 )
    cmds.setAttr(obj+'.blendPoint1', 0 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    #After startframe
    cmds.currentTime( startFrame )
    cmds.setAttr(obj+'.blendPoint1', 1 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    #Before endframe
    cmds.currentTime( endFrame )
    cmds.setAttr(obj+'.blendPoint1', 1 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    #After endframe
    cmds.currentTime( endFrame + 1 )
    cmds.setAttr(obj+'.blendPoint1', 0 )
    cmds.setKeyframe(obj+'.blendPoint1' )
    cmds.select(obj)

生成的运动路径如下所示

以下是正确的弧线应该遵循的内容

这是基于新代码的新曲线。它似乎在沿着运动路径移动,但它被压缩了。

【问题讨论】:

  • 只是想让你知道我已经更新了我的答案,提供了更多想法,以防他们真正回答你的问题......
  • 设法取得了一点进展。我使用对象空间来创建运动路径,但它仍然需要由父控制器旋转以遵循正确的路径。如果不这样做,它只是假设什么都没有发生并创建一条没有父母影响的路径。

标签: python rotation maya curves matrix-transform


【解决方案1】:

我刚刚想到的另一个想法是将临时定位器作为控件的父级(位于控件的枢轴处)。然后您可以使用定位器中的worldPosition xyz attr 值来绘制曲线点。得到曲线后可以删除定位器。

通过将定位器定位在控件的枢轴上以绘制曲线,它应该可以让您在没有太多麻烦的情况下进行约束(至少在理论上)。由于定位器已经具有“易于查询”的世界空间位置值,因此它的数学强度也较低。

当然它不像做所有的数学计算那么酷,但如果它有效......

【讨论】:

  • 控制器的父级不会破坏创建的层次结构吗?我假设我必须在当前控制器的父级下设置定位器以保持一致。此外,我确实保留了用于构建关节结构并将控制器放置在空间中的原始定位器。我应该使用这些定位器还是使用一组新的定位器?老实说,现在任何有效的东西对我来说都很好。数学不是必需的,只是我在制作工具时想到的第一件事。
  • 对不起,我不太明白什么会破坏层次结构......我正在谈论创建一个新的临时定位器(它不会控制任何东西)并将该定位器作为父级来控制你'正在为(将该定位器定位在控件的旋转枢轴上)创建路径。然后,您可以使用定位器的worldPosition xyz 值来绘制路径的 cvs。一旦你有了路径,定位器就不再需要并且可以被删除。放置在枢轴上的定位器应确保生成的曲线正确穿过控件的位置,从而使您的约束系统能够正常工作
  • 如果定位器的父级仍然不能为您的路径的 cvs 提供真实的世界空间位置,那么让定位器在世界空间中不作为父级,并且父级将其约束到控件...然后您可以使用翻译 xyz 值...
  • 对不起,那是我的误会。我的印象是控制器跟随定位器而不是定位器跟随控制器。我只是担心经常从它的父级中删除一个控制器会如何影响它。
  • 好吧!很高兴我们有工作要做!获胜的父约束定位器!!!
【解决方案2】:

如果你只是想获得某物的世界空间位置,你可以使用:

position = cmds.xform(item, q=True, ws=True, t=True)

然后根据需要使用该位置数据 - 无论父母如何旋转,它都应该工作... xform 命令也可用于设置世界空间中事物的位置:

cmds.xform(item, ws=True, t=position)

我不确定这是否真的回答了您的问题 - 有点不清楚您要在代码中实现什么;我不能完全理解打开/关闭定位器和点约束的原因。您是否尝试创建可编辑的运动轨迹?

如果是这样,您是否尝试过使用 Maya 内置的 editable motion trail 工具?

如果您出于某种原因尝试自己制作,我想您可以遵循“2 步”方法,首先创建一条曲线,其中每个 cv 通过您指定的起点位于控件的世界空间位置/时间结束。然后编辑曲线 cv 位置以清理圆弧并运行一个单独的函数来查询每个 cv 的世界空间位置,然后将它们应用于控件并在适当的时间设置关键帧。由于您是在世界空间位置执行所有这些操作,因此您无需担心任何父位置/旋转(幸运的是,这应该使代码相对简单)。

【讨论】:

  • 该项目的目的是创建我自己的运动路径,因为我将创建动画师可以编辑的不同版本。 Maya 的运动路径是我的第一选择,但它没有我需要的灵活性,所以我自己制作。定位器允许控制器跟随运动路径。在最终版本中,当创建多个路径(例如此默认路径和其中一个可编辑路径)时,动画师可以调整定位器的影响,以便控制器可以在路径之间切换。
  • 我会将 1 替换为 True,但它们应该具有相同的效果。由于某种奇怪的原因,它没有给我实际的世界空间坐标,所以我添加了用于创建装备关节结构的定位器(我正在沿着定位器位置生成关节,以便可以针对不同的模型调整装备)。
  • 看起来很奇怪 xform 命令没有给你真正的世界空间位置......我想你可以尝试在 worldMatrix[0] attr 上使用 getAttr 并从该列表中提取 xyz 翻译值价值观...
  • 如何提取 xyz 翻译值?我能够得到 attr,但我并不真正理解返回的矩阵。
  • 你应该在 youtube 上查看 Maya 矩阵解释视频以获得更深入的概述(那里应该有很多非常好的视频),但简单地说,你应该得到一个列表16 个值。矩阵是用于描述变换的 16 个值(4 列/4 行)。第 1 行用于 x 旋转,第 2 行用于 y 旋转,第 3 行用于 z 旋转,第 4 行用于位置。因此,列表的索引 12-14 将是您的 xyz 位置值(即:matrix[12] = x,matrix[13] = y,matrix[14] = z)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-26
  • 2012-01-31
  • 2015-08-23
  • 2018-10-31
  • 2016-01-25
  • 2021-12-07
  • 1970-01-01
相关资源
最近更新 更多