Beispiel #1
0
 def __init__(self, transform=None):
     logger.debug('Initializing: {}'.format(self))
     self._transform = om2.MTransformationMatrix() if transform is None \
         else om2.MTransformationMatrix(transform)
     self._preCallbacks = list()
     self._postCallbacks = list()
     self._parent = None
     # `isDirty` sets whether or not the primitive needs to be updated
     # before drawing.
     self.isDirty = False
Beispiel #2
0
    def compute(self, plug, dataBlock):
        """
        Node computation method:
            * plug is a connection point related to one of our node attributes (either an input or an output).
            * dataBlock contains the data on which we will base our computations.
        """
        rotation = dataBlock.inputValue(TwistExtractor.inRotation).asDouble3()
        rotOrder = dataBlock.inputValue(
            TwistExtractor.inRotationOrder).asShort()
        eRoll = om2.MEulerRotation(rotation, rotOrder)
        useUpObj = dataBlock.inputValue(TwistExtractor.inUseUpVec).asBool()
        revTwist = dataBlock.inputValue(TwistExtractor.inInvTwist).asBool()

        # Non flip ROO = XYZ
        # Not working = XZY
        twistOrder = om2.MEulerRotation.kXYZ
        eRoll.reorderIt(twistOrder)

        # Non-Roll orientation
        mtxFn = om2.MTransformationMatrix()
        mtxFn.rotateBy(eRoll, om2.MSpace.kWorld)
        mRoll = mtxFn.asMatrix()

        qNonRoll = om2.MQuaternion()

        nAim = om2.MVector(mRoll[0], mRoll[1], mRoll[2])
        nAim.normalize()
        nAimAxis = om2.MVector.kXaxisVector
        qAim = om2.MQuaternion(nAimAxis, nAim)
        qNonRoll *= qAim

        if useUpObj:
            vUp = om2.MVector(
                dataBlock.inputValue(TwistExtractor.inUpVec).asFloat3())
            nNormal = vUp - ((vUp * nAim) * nAim)
            nNormal.normalize()
            nUp = om2.MVector.kYaxisVector.rotateBy(qAim)
            angle = nUp.angle(nNormal)
            qNormal = om2.MQuaternion(angle, nAim)
            if not nNormal.isEquivalent(nUp.rotateBy(qNormal), 1.0e-5):
                angle = 2.0 * kPI - angle
                qNormal = om2.MQuaternion(angle, nAim)
            qNonRoll *= qNormal
        eNonRoll = qNonRoll.asEulerRotation()
        eNonRoll = om2.MEulerRotation(eNonRoll.x, eNonRoll.y, eNonRoll.z,
                                      twistOrder)

        # Extract Twist from orientations
        qRoll = eRoll.asQuaternion()
        qExtract180 = qNonRoll * qRoll.inverse()
        eExtract180 = qExtract180.asEulerRotation()
        twist = -eExtract180.x
        if revTwist:
            twist *= -1.0

        # Output Twist
        if plug == TwistExtractor.outTwist:
            outTwistHdle = dataBlock.outputValue(TwistExtractor.outTwist)
            outTwistHdle.setMAngle(om2.MAngle(twist))
            outTwistHdle.setClean()

        # Output Twist Distribution
        if plug == TwistExtractor.outTwistDist:
            invDist = dataBlock.inputValue(TwistExtractor.inRevDist).asBool()
            outTwistDistHdle = dataBlock.outputArrayValue(
                TwistExtractor.outTwistDist)
            outputs = len(outTwistDistHdle)
            step = twist / (outputs - 1) if outputs > 1 else twist
            outList = []
            outList.extend(range(outputs))
            if not invDist:
                outList.reverse()
            # pylint: disable=consider-using-enumerate
            for i in range(len(outList)):
                outTwistDistHdle.jumpToLogicalElement(i)
                resultHdle = outTwistDistHdle.outputValue()
                result = step * outList[i] if outputs > 1 else twist
                resultHdle.setMAngle(om2.MAngle(result))
            outTwistDistHdle.setAllClean()

        return
Beispiel #3
0
    def compute(self, plug, dataBlock):
        """
        Node computation method:
            * plug is a connection point related to one of our node attributes (either an input or an output).
            * dataBlock contains the data on which we will base our computations.
        """
        # pylint: disable=no-self-use
        # if not dataBlock.isClean(SpaceConstraint.inSpace):
        #     om2.MGlobal.displayInfo("\tSpace attribute is dirty.")
        #     om2.MUserEventMessage.postUserEvent("preCompute", self)

        curSpace = dataBlock.inputValue(SpaceConstraint.inSpace).asShort()

        offsetMatchHdle = dataBlock.inputArrayValue(
            SpaceConstraint.inOffsetMatches)
        offsetHdle = dataBlock.inputArrayValue(SpaceConstraint.inOffset)
        targetHdle = dataBlock.inputArrayValue(SpaceConstraint.inTarget)
        offsetMatchList = []
        offsetList = []
        targetList = []

        for i in range(len(offsetMatchHdle)):
            offsetMatchHdle.jumpToLogicalElement(i)
            mOffMatch = offsetMatchHdle.inputValue().asMatrix()
            offsetMatchList.append(mOffMatch)

        for i in range(len(offsetHdle)):
            offsetHdle.jumpToLogicalElement(i)
            mOff = offsetHdle.inputValue().asMatrix()
            offsetList.append(mOff)

        for i in range(len(targetHdle)):
            targetHdle.jumpToLogicalElement(i)
            mTgt = targetHdle.inputValue().asMatrix()
            targetList.append(mTgt)

        if len(offsetList) == 0 or len(targetList) == 0:
            mResult = om2.MMatrix()
        else:
            minRequired = min(len(offsetList), len(targetList)) - 1
            if curSpace > minRequired:
                curSpace = minRequired
            mResult = offsetMatchList[curSpace] * offsetList[
                curSpace] * targetList[curSpace]

        mtxFn = om2.MTransformationMatrix(mResult)

        if plug == SpaceConstraint.outConstTrans:
            vTransD = om2.MVector(mResult[12], mResult[13], mResult[14])
            vTrans = om2.MFloatVector(vTransD)
            outTransHdle = dataBlock.outputValue(SpaceConstraint.outConstTrans)
            outTransHdle.setMFloatVector(vTrans)
            outTransHdle.setClean()

        if plug == SpaceConstraint.outConstRot:
            eRot = mtxFn.rotation(asQuaternion=False)
            outRotHdle = dataBlock.outputValue(SpaceConstraint.outConstRot)
            outRotHdle.setMVector(eRot.asVector())
            outRotHdle.setClean()

        if plug == SpaceConstraint.outConstSca:
            outSca = mtxFn.scale(om2.MSpace.kWorld)
            outScaHdle = dataBlock.outputValue(SpaceConstraint.outConstSca)
            outScaHdle.set3Float(outSca[0], outSca[1], outSca[2])
            outScaHdle.setClean()
Beispiel #4
0
 def transform(self, value):
     value = om2.MTransformationMatrix(value)  # copy
     if self._transform != value:
         self._transform = value
         self.isDirty = True
Beispiel #5
0
    def compute(self, plug, dataBlock):
        """
        Node computation method:
            * plug is a connection point related to one of our node attributes (either an input or an output).
            * dataBlock contains the data on which we will base our computations.
        """
        # pylint: disable=no-self-use
        if plug != HelperJoint.outTransform:
            return om2.kUnknownParameter

        mSource = dataBlock.inputValue(HelperJoint.inSource).asMatrix()
        mtxFn = om2.MTransformationMatrix(mSource)
        mtxFn.setScale([1.0, 1.0, 1.0], om2.MSpace.kTransform)
        mSource = mtxFn.asMatrix()
        mSourceParent = dataBlock.inputValue(HelperJoint.inSourceParent).asMatrix()

        mParInv = dataBlock.inputValue(HelperJoint.inParInvMtx).asMatrix()
        sourceParSca = dataBlock.inputValue(HelperJoint.inSourceParSca).asFloat3()
        mtxFn = om2.MTransformationMatrix()
        mtxFn.scaleBy(sourceParSca, om2.MSpace.kTransform)
        mInvSca = mtxFn.asMatrix()
        targetListHandle = dataBlock.inputArrayValue(HelperJoint.inTargetList)

        outputList = []

        for i in range(len(targetListHandle)):
            targetListHandle.jumpToLogicalElement(i)
            targetHandle = targetListHandle.inputValue()
            vPosOffset = om2.MVector(targetHandle.child(HelperJoint.inPositionOffset).asFloat3())
            eRotOffset = om2.MEulerRotation(targetHandle.child(HelperJoint.inRotationOffset).asDouble3())
            angle = targetHandle.child(HelperJoint.inRotAngle).asAngle().asRadians()
            restAngle = targetHandle.child(HelperJoint.inRestAngle).asAngle().asRadians()
            rotInterp = targetHandle.child(HelperJoint.inRotInterp).asFloat()
            posMult = targetHandle.child(HelperJoint.inPosMult).asFloat()
            negMult = targetHandle.child(HelperJoint.inNegMult).asFloat()

            mPositionOffset = om2.MMatrix()
            mPositionOffset[12] = vPosOffset.x
            mPositionOffset[13] = vPosOffset.y
            mPositionOffset[14] = vPosOffset.z
            multTranslation = abs(angle) * posMult
            if angle < restAngle:
                multTranslation = abs(angle) * negMult
            vPosOffset.normalize()
            mMultiplier = om2.MMatrix()
            mMultiplier[12] = vPosOffset.x * multTranslation
            mMultiplier[13] = vPosOffset.y * multTranslation
            mMultiplier[14] = vPosOffset.z * multTranslation
            mTargetPoint = mMultiplier * mPositionOffset * mSource
            mTargetOrient = mInvSca * (mSource * (1.0 - rotInterp)) + (mSourceParent * rotInterp)

            vResultPos = om2.MVector(mTargetPoint[12], mTargetPoint[13], mTargetPoint[14])
            mtxFn = om2.MTransformationMatrix(mTargetOrient)
            eResultOri = eRotOffset + mtxFn.rotation(asQuaternion=False)
            mtxFn = om2.MTransformationMatrix()
            mtxFn.setRotation(eResultOri)
            mtxFn.setTranslation(vResultPos, om2.MSpace.kTransform)
            mResult = mtxFn.asMatrix() * mParInv
            outputList.append(mResult)

        outTransHandle = dataBlock.outputArrayValue(HelperJoint.outTransform)
        for i in range(len(outTransHandle)):
            outTransHandle.jumpToLogicalElement(i)
            resultHandle = outTransHandle.outputValue()
            if i < len(outTransHandle) and i < len(outputList):
                resultHandle.setMMatrix(outputList[i])
            else:
                resultHandle.setMMatrix(om2.MMatrix.kIdentity)
Beispiel #6
0

_MAYA_OUTPUT_ATTRIBUTE_NAME = 'output'


def getMRotFromNodeOutput(node_mob, rotOrder=om2.MEulerRotation.kXYZ):
    """
    finds the angular output of the argument node and returns it
     as a Euler rotation where that angle is the X element
    :param node_mob: [MObject] the node to get the output port from
    :param rotOrder: [int] the factory constant for the desired rotation order
                            the returned Euler rotation should be set to
    :return: [MEulerRotation] the Euler rotation composition where the
                                angular output on the argument node is the X value
    """
    mfn_dep = om2.MFnDependencyNode(node_mob)
    angle = om2.MAngle(0.0)
    if node_mob.hasFn(om2.MFn.kAnimBlend) and mfn_dep.hasAttribute(
            _MAYA_OUTPUT_ATTRIBUTE_NAME):
        plug = mfn_dep.findPlug(_MAYA_OUTPUT_ATTRIBUTE_NAME, False)
        angle = plug.asMAngle()

    rot = om2.MEulerRotation(angle.asRadians(), 0.0, 0.0, rotOrder)
    return rot


mobTuple = tuple(iterSelection())
if len(mobTuple) >= 2:
    if mobTuple[0] is not None:
        srtWMtx = om2.MTransformationMatrix(wMtxFromMob(mobTuple[0]))
        srtWMtx.rotateBy(getMRotFromNodeOutput(mobTuple[1]), om2.MSpace.kWorld)
Beispiel #7
0
    def compute(self, plug, dataBlock):
        """
        Node computation method:
            * plug is a connection point related to one of our node attributes (either an input or an output).
            * dataBlock contains the data on which we will base our computations.
        """
        # pylint: disable=no-self-use
        if plug != AimConstraint.outConstraint:
            return om2.kUnknownParameter

        upVecType = dataBlock.inputValue(AimConstraint.inUpVecType).asShort()
        eOffset = om2.MEulerRotation(
            dataBlock.inputValue(AimConstraint.inOffset).asDouble3())
        qOffset = eOffset.asQuaternion()
        mTargetW = dataBlock.inputValue(AimConstraint.inTargetWMtx).asMatrix()
        targetWeight = dataBlock.inputValue(
            AimConstraint.inTargetWeight).asDouble()
        mConstW = dataBlock.inputValue(AimConstraint.inConstWMtx).asMatrix()
        mConstParInv = dataBlock.inputValue(
            AimConstraint.inConstParInvMtx).asMatrix()
        eConstJntOri = om2.MEulerRotation(
            dataBlock.inputValue(AimConstraint.inConstJntOri).asDouble3())
        qConstJntOri = eConstJntOri.asQuaternion()
        constRotOrder = dataBlock.inputValue(
            AimConstraint.inConstRotOrder).asShort()

        vTarget = om2.MVector(mTargetW[12], mTargetW[13], mTargetW[14])
        vConst = om2.MVector(mConstW[12], mConstW[13], mConstW[14])
        mtxFn = om2.MTransformationMatrix(mConstParInv)
        qConstParInv = mtxFn.rotation(asQuaternion=True)

        primAxis = om2.MVector.kXaxisVector
        secAxis = om2.MVector.kYaxisVector
        qAimConst = om2.MQuaternion()

        nAim = vTarget - vConst
        nAim.normalize()
        qAim = om2.MQuaternion(primAxis, nAim)
        qAimConst *= qAim

        if upVecType != 0:
            if upVecType == 1:
                # World Up
                nWorldUp = om2.MVector(
                    dataBlock.inputValue(
                        AimConstraint.inWorldUpVector).asFloat3())
                nWorldUp.normalize()
                vUp = nWorldUp
            elif upVecType == 2:
                # Object Up
                mWorldUp = dataBlock.inputValue(
                    AimConstraint.inWorldUpMtx).asMatrix()
                vWorldUp = om2.MVector(mWorldUp[12], mWorldUp[13],
                                       mWorldUp[14])
                vUp = vWorldUp - vConst
            elif upVecType == 3:
                # Angle Up
                angleUp = dataBlock.inputValue(
                    AimConstraint.inAngleUp).asAngle().asRadians()
                qTwist = om2.MQuaternion(angleUp, nAim)
                vUp = secAxis.rotateBy(qTwist)
            nNormal = vUp - ((vUp * nAim) * nAim)
            nNormal.normalize()

            nUp = secAxis.rotateBy(qAim)
            angle = nUp.angle(nNormal)
            qNormal = om2.MQuaternion(angle, nAim)
            if not nNormal.isEquivalent(nUp.rotateBy(qNormal), 1.0e-5):
                angle = 2.0 * math.pi - angle
                qNormal = om2.MQuaternion(angle, nAim)
            qAimConst *= qNormal

        qResult = om2.MQuaternion()
        qResult *= qOffset.invertIt()
        qResult *= qAimConst
        qResult *= qConstParInv
        qResult *= qConstJntOri.invertIt()
        eResult = qResult.asEulerRotation()
        eResult.reorderIt(constRotOrder)
        eResult *= targetWeight
        vResult = eResult.asVector()
        outConstraintHandle = dataBlock.outputValue(
            AimConstraint.outConstraint)
        outConstraintHandle.setMVector(vResult)
        outConstraintHandle.setClean()
Beispiel #8
0
    def compute(self, plug, dataBlock):
        """
        Node computation method:
            * plug is a connection point related to one of our node attributes (either an input or an output).
            * dataBlock contains the data on which we will base our computations.
        """
        if plug != IKVChainSolver.outChain:
            return om2.kUnknownParameter

        # Get Basis Quaternion
        mRoot = dataBlock.inputValue(IKVChainSolver.inRoot).asMatrix()
        mHandle = dataBlock.inputValue(IKVChainSolver.inHandle).asMatrix()
        mPoleVector = dataBlock.inputValue(
            IKVChainSolver.inPoleVector).asMatrix()
        pvMode = dataBlock.inputValue(IKVChainSolver.inPvMode).asShort()
        prefAngle = dataBlock.inputValue(
            IKVChainSolver.inPreferredAngle).asAngle().asRadians()
        twist = dataBlock.inputValue(
            IKVChainSolver.inTwist).asAngle().asRadians()
        snap = dataBlock.inputValue(IKVChainSolver.inSnapUpVector).asFloat()
        mSnap = dataBlock.inputValue(IKVChainSolver.inSnap).asMatrix()
        flip = dataBlock.inputValue(IKVChainSolver.inFlip).asBool()

        vRoot = om2.MVector(mRoot[12], mRoot[13], mRoot[14])
        vHandle = om2.MVector(mHandle[12], mHandle[13], mHandle[14])
        vPoleVector = om2.MVector(mPoleVector[12], mPoleVector[13],
                                  mPoleVector[14])
        vSnap = om2.MVector(mSnap[12], mSnap[13], mSnap[14])

        primAxis = om2.MVector.kXaxisVector
        secAxis = om2.MVector.kYaxisVector
        if flip:
            primAxis = -om2.MVector.kXaxisVector
            secAxis = -om2.MVector.kYaxisVector
        binAxis = primAxis ^ secAxis
        qBasis = om2.MQuaternion()

        vAim = vHandle - vRoot
        nAim = vAim.normal()
        qAim = om2.MQuaternion(primAxis, nAim)
        qBasis *= qAim

        vStartSnap = vSnap - vRoot
        vEndSnap = vSnap - vHandle

        if pvMode == 0:
            vUp = vPoleVector - vRoot
        else:
            qTwist = om2.MQuaternion(prefAngle + twist, nAim)
            vUp = secAxis.rotateBy(qTwist)
        nNormalPole = vUp - ((vUp * nAim) * nAim)
        nNormalPole.normalize()
        if snap > 0.0:
            nNormalSnap = vStartSnap - ((vStartSnap * nAim) * nAim)
            nNormalSnap.normalize()
            nNormal = (1.0 - snap) * nNormalPole + snap * nNormalSnap
        else:
            nNormal = nNormalPole

        nUp = secAxis.rotateBy(qAim)
        angle = nUp.angle(nNormal)
        qNormal = om2.MQuaternion(angle, nAim)
        if not nNormal.isEquivalent(nUp.rotateBy(qNormal), 1.0e-5):
            angle = 2.0 * math.pi - angle
            qNormal = om2.MQuaternion(angle, nAim)
        qBasis *= qNormal

        # Solver Triangle
        restStartLen = dataBlock.inputValue(
            IKVChainSolver.inRestLenStart).asFloat()
        restEndLen = dataBlock.inputValue(
            IKVChainSolver.inRestLenEnd).asFloat()
        compressionLimit = dataBlock.inputValue(
            IKVChainSolver.inCompressionLimit).asFloat()
        softVal = dataBlock.inputValue(IKVChainSolver.inSoftness).asFloat()

        startSnapLen = vStartSnap.length()
        endSnapLen = vEndSnap.length()

        startLen = (1.0 - snap) * restStartLen + snap * startSnapLen
        endLen = (1.0 - snap) * restEndLen + snap * endSnapLen
        chainLen = (1.0 - snap) * (restStartLen + restEndLen) + snap * (
            startSnapLen + endSnapLen)
        handleLen = vAim.length()

        rigidLen = max(min(handleLen, chainLen), chainLen * compressionLimit)
        dc = chainLen
        da = (1.0 - softVal) * dc
        if handleLen > da and softVal > 0.0:
            ds = dc - da
            softLen = ds * (1.0 - math.pow(math.e, (da - handleLen) / ds)) + da
            solverLen = (1.0 - snap) * softLen + snap * rigidLen
        else:
            solverLen = rigidLen

        # Pre Calculations
        startLenSquared = math.pow(startLen, 2.0)
        endLenSquared = math.pow(endLen, 2.0)
        solverLenSquared = math.pow(solverLen, 2.0)
        stretch = dataBlock.inputValue(IKVChainSolver.inStretch).asDouble()
        squashMultStart = dataBlock.inputValue(
            IKVChainSolver.inSquashMultStart).asFloat2()
        squashMultEnd = dataBlock.inputValue(
            IKVChainSolver.inSquashMultEnd).asFloat2()
        if stretch > 0.0:
            clampStretch = dataBlock.inputValue(
                IKVChainSolver.inClampStretch).asDouble()
            clampValue = dataBlock.inputValue(
                IKVChainSolver.inClampValue).asDouble()
            squash = dataBlock.inputValue(IKVChainSolver.inSquash).asDouble()
            if handleLen > da and softVal > 0.0:
                scaleFactor = handleLen / solverLen
            else:
                scaleFactor = handleLen / chainLen
            if handleLen >= da:
                clampFactor = (
                    1.0 - clampStretch) * scaleFactor + clampStretch * min(
                        scaleFactor, clampValue)
                stretchFactor = (1.0 - stretch) + stretch * clampFactor
            else:
                stretchFactor = 1.0
            squashFactor = (1.0 -
                            squash) + squash * (1.0 / math.sqrt(stretchFactor))
        else:
            stretchFactor = 1.0
            squashFactor = 1.0

        hierarchyMode = dataBlock.inputValue(
            IKVChainSolver.inHierarchyMode).asBool()
        useScale = dataBlock.inputValue(IKVChainSolver.inUseScale).asBool()
        outChainHandle = dataBlock.outputArrayValue(IKVChainSolver.outChain)
        offsetHandle = dataBlock.inputArrayValue(IKVChainSolver.inOffset)
        jntOriHandle = dataBlock.inputArrayValue(IKVChainSolver.inJntOri)
        mParInv = dataBlock.inputValue(IKVChainSolver.inParInvMtx).asMatrix()
        srtList = []
        offsetList = []
        jntOriList = []

        for i in range(len(offsetHandle)):
            offsetHandle.jumpToLogicalElement(i)
            eOff = om2.MEulerRotation(offsetHandle.inputValue().asDouble3())
            qOff = eOff.asQuaternion()
            offsetList.append(qOff)

        for i in range(len(jntOriHandle)):
            jntOriHandle.jumpToLogicalElement(i)
            eOri = om2.MEulerRotation(jntOriHandle.inputValue().asDouble3())
            qOri = eOri.asQuaternion()
            jntOriList.append(qOri)

        # First Output
        # Scale
        firstStretch = stretchFactor
        firstScaX = firstStretch
        if not useScale:
            firstStretch = 1.0
        firstSquash = [
            squashFactor * squashMultStart[0],
            squashFactor * squashMultStart[1]
        ]
        firstSca = [firstStretch, firstSquash[0], firstSquash[1]]
        # Rotation
        betaCosPure = (startLenSquared + solverLenSquared -
                       endLenSquared) / (2.0 * startLen * solverLen)
        betaCos = min(max(betaCosPure, -1.0), 1.0)
        beta = math.acos(betaCos)
        qBeta = om2.MQuaternion(beta, binAxis)
        qFirstRotW = qBeta * qBasis
        qFirstRot = om2.MQuaternion()
        if len(offsetList) >= 1:
            qFirstRot *= offsetList[0].invertIt()
        qFirstRot *= qFirstRotW
        if len(jntOriList) >= 1:
            qFirstRot *= jntOriList[0].invertIt()
        # Translation
        vFirstPos = vRoot
        # Matrix Output
        mtxFn = om2.MTransformationMatrix()
        mtxFn.setScale(firstSca, om2.MSpace.kTransform)
        mtxFn.setRotation(qFirstRot)
        mtxFn.setTranslation(vFirstPos, om2.MSpace.kTransform)
        mFirst = mtxFn.asMatrix()
        mFirst *= mParInv
        srtList.append(mFirst)

        # Second Output
        # Scale
        secondStretch = stretchFactor
        secondScaX = secondStretch
        if not useScale:
            secondStretch = 1.0
        secondSquash = [
            squashFactor * squashMultEnd[0], squashFactor * squashMultEnd[1]
        ]
        secondSca = [secondStretch, secondSquash[0], secondSquash[1]]
        # Rotation
        gammaCosPure = (startLenSquared + endLenSquared -
                        solverLenSquared) / (2.0 * startLen * endLen)
        gammaCos = min(max(gammaCosPure, -1.0), 1.0)
        gamma = math.acos(gammaCos)
        gammaCmp = gamma + beta - math.pi
        qGamma = om2.MQuaternion(gammaCmp, binAxis)
        qSecondRotW = qGamma * qBasis
        qSecondRot = om2.MQuaternion()
        if len(offsetList) >= 2:
            qSecondRot *= offsetList[1].invertIt()
        qSecondRot *= qSecondRotW
        if hierarchyMode:
            qSecondRot *= qFirstRotW.invertIt()
            if len(offsetList) >= 1:
                qSecondRot *= offsetList[0].invertIt()
        if len(jntOriList) >= 2:
            qSecondRot *= jntOriList[1].invertIt()
        # Translation
        if hierarchyMode:
            vSecondPos = primAxis * startLen
            if not useScale:
                vSecondPos *= firstScaX
        else:
            vSecondOri = nAim.rotateBy(om2.MQuaternion(
                beta, nAim ^ nNormal)) * startLen
            if not useScale:
                vSecondOri *= firstScaX
            vSecondPos = vRoot + vSecondOri
        # Matrix Output
        mtxFn = om2.MTransformationMatrix()
        mtxFn.setScale(secondSca, om2.MSpace.kTransform)
        mtxFn.setRotation(qSecondRot)
        mtxFn.setTranslation(vSecondPos, om2.MSpace.kTransform)
        mSecond = mtxFn.asMatrix()
        if not hierarchyMode:
            mSecond *= mParInv
        srtList.append(mSecond)

        # Third Output
        # Rotation
        qThirdRot = qBasis
        if hierarchyMode:
            qThirdRot *= qSecondRotW.invertIt()
            if len(offsetList) >= 2:
                qThirdRot *= offsetList[1].invertIt()
        # Translation
        if hierarchyMode:
            vThirdPos = primAxis * endLen
            if not useScale:
                vThirdPos *= secondScaX
        else:
            vThirdPos = vRoot + nAim * solverLen
            if not useScale:
                vThirdPos = vRoot + nAim * solverLen * stretchFactor
        # Matrix Output
        mtxFn = om2.MTransformationMatrix()
        mtxFn.setRotation(qThirdRot)
        mtxFn.setTranslation(vThirdPos, om2.MSpace.kTransform)
        mThird = mtxFn.asMatrix()
        if not hierarchyMode:
            mThird *= mParInv
        srtList.append(mThird)

        # Set outputs
        for i in range(len(outChainHandle)):
            outChainHandle.jumpToLogicalElement(i)
            resultHandle = outChainHandle.outputValue()
            if i < len(outChainHandle) and i < len(srtList):
                resultHandle.setMMatrix(srtList[i])
            else:
                resultHandle.setMMatrix(om2.MMatrix.kIdentity)

        outChainHandle.setAllClean()
    def compute(self, plug, dataBlock):
        """
        Node computation method:
            * plug is a connection point related to one of our node attributes (either an input or an output).
            * dataBlock contains the data on which we will base our computations.
        """
        # pylint: disable=no-self-use
        eConstJntOri = om2.MEulerRotation(
            dataBlock.inputValue(
                ParentConstraint.inConstraintJntOri).asDouble3())
        mConstParInv = dataBlock.inputValue(
            ParentConstraint.inConstraintParInvMtx).asMatrix()
        constRotOrder = dataBlock.inputValue(
            ParentConstraint.inConstraintRotOrder).asShort()
        constParSca = dataBlock.inputValue(
            ParentConstraint.inConstraintParSca).asFloat3()
        targetListHandle = dataBlock.inputArrayValue(
            ParentConstraint.inTargetList)
        mTargetsAdded = om2.MMatrix()
        mtxFn = om2.MTransformationMatrix()
        mtxFn.scaleBy(constParSca, om2.MSpace.kTransform)
        mInvSca = mtxFn.asMatrix()

        for i in range(len(targetListHandle)):
            targetListHandle.jumpToLogicalElement(i)
            targetHandle = targetListHandle.inputValue()
            # targetJntOri = om2.MEulerRotation(targetHandle.child(ParentConstraint.inTargetJntOri).asDouble3())
            # targetRotOrder = targetHandle.child(ParentConstraint.inTargetRotOrder).asShort()
            mTargetW = targetHandle.child(
                ParentConstraint.inTargetWorldMatrix).asMatrix()
            mOffset = targetHandle.child(
                ParentConstraint.inTargetOffset).asMatrix()
            targetWeight = targetHandle.child(
                ParentConstraint.inTargetWeight).asFloat()

            mTarget = mOffset * (mTargetW * targetWeight)
            if mTargetsAdded == om2.MMatrix():
                mTargetsAdded = mTarget
            else:
                mTargetsAdded += mTarget

        mResult = mTargetsAdded * mConstParInv * mInvSca

        if plug == ParentConstraint.outConstTrans:
            outTransHandle = dataBlock.outputValue(
                ParentConstraint.outConstTrans)
            outTrans = om2.MFloatVector(mResult[12], mResult[13], mResult[14])
            outTransHandle.setMFloatVector(outTrans)
            outTransHandle.setClean()
        if plug == ParentConstraint.outConstRot:
            outRotHandle = dataBlock.outputValue(ParentConstraint.outConstRot)
            mtxFn = om2.MTransformationMatrix(mResult)
            eRotMtx = mtxFn.rotation(asQuaternion=False)
            qRotMtx = eRotMtx.asQuaternion()
            qConstJntOri = eConstJntOri.asQuaternion()
            qOutRot = qRotMtx * qConstJntOri.invertIt()
            outRot = qOutRot.asEulerRotation().reorderIt(constRotOrder)
            outRotHandle.setMVector(outRot.asVector())
            outRotHandle.setClean()
        if plug == ParentConstraint.outConstSca:
            outScaHandle = dataBlock.outputValue(ParentConstraint.outConstSca)
            mtxFn = om2.MTransformationMatrix(mResult)
            outSca = mtxFn.scale(om2.MSpace.kWorld)
            outScaHandle.set3Float(outSca[0], outSca[1], outSca[2])
            outScaHandle.setClean()