diff --git a/Exts/Kraken/Kraken.fpm.json b/Exts/Kraken/Kraken.fpm.json index 9a4e0319..d4fa8007 100644 --- a/Exts/Kraken/Kraken.fpm.json +++ b/Exts/Kraken/Kraken.fpm.json @@ -10,6 +10,7 @@ "Solvers/KrakenMultiPoseConstraintSolver.kl" "Solvers/BezierSpineSolver.kl", "Solvers/TwoBoneIKSolver.kl", + "Solvers/TwoBoneStretchyIKSolver.kl" "Solvers/NBoneIKSolver.kl", "Solvers/TentacleSolver.kl", "Solvers/DynamicChainSolver.kl", diff --git a/Exts/Kraken/Solvers/TwoBoneStretchyIKSolver.kl b/Exts/Kraken/Solvers/TwoBoneStretchyIKSolver.kl new file mode 100644 index 00000000..13c1d04a --- /dev/null +++ b/Exts/Kraken/Solvers/TwoBoneStretchyIKSolver.kl @@ -0,0 +1,267 @@ +require InlineDrawing; +require Kraken; +require Math; +require Animation; + + +object TwoBoneStretchyIKSolver : KrakenSolver { + Xfo initPose[]; +}; + + +// Default Constructor +function TwoBoneStretchyIKSolver() +{ + +} + + +function TwoBoneStretchyIKSolver( + Xfo initPose[]) +{ + this.initPose = initPose; +} + + +// Return Arguments for Kraken +function KrakenSolverArg[] TwoBoneStretchyIKSolver.getArguments(){ + KrakenSolverArg args[] = this.parent.getArguments(); + args.push(KrakenSolverArg('rightSide', 'In', 'Boolean')); + + args.push(KrakenSolverArg('ikblend', 'In', 'Scalar')); + args.push(KrakenSolverArg('softIK', 'In', 'Boolean')); + args.push(KrakenSolverArg('softRatio', 'In', 'Scalar')); + args.push(KrakenSolverArg('stretch', 'In', 'Boolean')); + args.push(KrakenSolverArg('stretchBlend', 'In', 'Scalar')); + args.push(KrakenSolverArg('slide', 'In', 'Scalar')); + args.push(KrakenSolverArg('pin', 'In', 'Scalar')); + + args.push(KrakenSolverArg('root', 'In', 'Mat44')); + args.push(KrakenSolverArg('bone0FK', 'In', 'Mat44')); + args.push(KrakenSolverArg('bone1FK', 'In', 'Mat44')); + args.push(KrakenSolverArg('ikHandle', 'In', 'Mat44')); + args.push(KrakenSolverArg('upV', 'In', 'Mat44')); + + args.push(KrakenSolverArg('bone0Len', 'In', 'Scalar')); + args.push(KrakenSolverArg('bone1Len', 'In', 'Scalar')); + args.push(KrakenSolverArg('bone0Out', 'Out', 'Mat44')); + args.push(KrakenSolverArg('bone1Out', 'Out', 'Mat44')); + args.push(KrakenSolverArg('bone2Out', 'Out', 'Mat44')); + return args; +} + + +// Solve +function TwoBoneStretchyIKSolver.solve! +( + Boolean drawDebug, + Scalar rigScale, + Boolean rightSide, + + Scalar ikblend, + Boolean softIK, + Scalar softRatio, + Boolean stretch, + Scalar stretchBlend, + Scalar slide, + Scalar pin, + + Mat44 root, + Mat44 bone0FK, + Mat44 bone1FK, + Mat44 ikHandle, + Mat44 upV, + + Scalar bone0Len, + Scalar bone1Len, + io Mat44 bone0Out, + io Mat44 bone1Out, + io Mat44 bone2Out +){ + + Xfo bone0FkXfo = Xfo(bone0FK); + Xfo bone1FkXfo = Xfo(bone1FK); + + Xfo bone0Xfo; + Xfo bone1Xfo; + Xfo bone2Xfo; + + // Scale to global rig scale + Scalar scaledBone0Len = bone0Len * rigScale; + Scalar scaledBone1Len = bone1Len * rigScale; + + // Solve IK + if(ikblend > 0.0) + { + + Vec3 ikHandlePos = ikHandle.translation(); + Vec3 rootPos = root.translation(); + Scalar distanceToIK = (rootPos - ikHandlePos).length(); + + // Lock mid to the upVector (pole vector) + // e.g. could be used to lock an elbow on a table + if (pin > 0.0) + { + Vec3 pinPt = upV.translation(); + scaledBone0Len = Math_linearInterpolate(scaledBone0Len, (pinPt - rootPos).length(), pin); + scaledBone1Len = Math_linearInterpolate(scaledBone1Len, (pinPt - ikHandlePos).length(), pin); + } + + if (pin != 1.0) + { + + // TODO: Allow scale of zero and the evaluation to finish + Scalar chainLen = scaledBone0Len + scaledBone1Len; + //if (chainLen == 0) return; + + // Slide mid to end (+1) or to start (-1) + if (slide != 0.0) + { + Scalar shift; + if (slide > 0.0) + shift = slide * scaledBone1Len; + else + shift = slide * scaledBone0Len; + + // Fix pinning blend (when 0 < pin < 1) + if (pin > 0.0) shift = Math_linearInterpolate(shift, 0.0, pin); + + // Update the bone lengths + scaledBone0Len += shift; + scaledBone1Len -= shift; + chainLen = scaledBone0Len + scaledBone1Len; + } + + // Soft IK scale + Scalar finalRatio = 1.0; + if (softIK && softRatio > 0.0) + { + Scalar softLen = softRatio * chainLen; + Scalar hardLen = chainLen - softLen; + + // If we left the hardDistArea we entered the soft area + if (distanceToIK > hardLen) + { + Scalar exponent = -(distanceToIK - hardLen) / softLen; + Scalar softTargetDistance = softLen * (1 - exp(exponent)) + hardLen; + + finalRatio = softTargetDistance / distanceToIK; + + // Fix pinning blend (when 0 < pin < 1) + if (pin > 0.0) finalRatio = Math_linearInterpolate(finalRatio, 1.0, pin); + + if (!stretch || stretchBlend < 1.0) + { + // For this we drag the IK handle behind after the softDist + // See: http://www.softimageblog.com/archives/108 + // Compute soft IK location for non-stretchy + Scalar nonStretchyFinalRatio = Math_linearInterpolate(finalRatio, 1.0, stretchBlend); + Vec3 direction = ikHandlePos - rootPos; + direction = direction.multiplyScalar(nonStretchyFinalRatio); + ikHandlePos = rootPos + direction; + distanceToIK *= nonStretchyFinalRatio; + } + + if (stretch && stretchBlend > 0.0) + { + // Scale bones by ratio between IK and Soft IK so that we preserve the soft IK + // motion while hitting the IK handle. See: http://www.softimageblog.com/archives/109 + Scalar stretchyFinalRatio = 1.0 / Math_linearInterpolate(1.0, finalRatio, stretchBlend); + + chainLen *= stretchyFinalRatio; + scaledBone0Len *= stretchyFinalRatio; + scaledBone1Len *= stretchyFinalRatio; + } + + } + } + + // Stretchy + if (stretch && stretchBlend > 0.0) + { + if (chainLen < distanceToIK) + { + Scalar diff = distanceToIK / chainLen; + diff = Math_linearInterpolate(1.0, diff, stretchBlend); + scaledBone0Len *= diff; + scaledBone1Len *= diff; + } + + } + } + + solve2BoneIK( + scaledBone0Len, + scaledBone1Len, + root.translation(), + upV.translation(), + ikHandlePos, + bone0Xfo, + bone1Xfo + ); + + // Project bone2 to the end of bone1 + bone2Xfo = bone1Xfo; + bone2Xfo.tr = bone1Xfo.transformVector(Vec3(scaledBone1Len, 0.0, 0.0)); + + // Set IK bone scaling + bone0Xfo.sc = Vec3(scaledBone0Len / bone0Len, rigScale, rigScale); + bone1Xfo.sc = Vec3(scaledBone1Len / bone1Len, rigScale, rigScale); + bone2Xfo.sc = Vec3(rigScale, rigScale, rigScale); + + } + + // Solve FK + if (ikblend < 1.0) + { + // Project bone2 to the end of bone1 + Xfo bone2FkXfo = bone1FkXfo; + bone2FkXfo.tr = bone1FkXfo.transformVector(Vec3(bone1Len, 0.0, 0.0)); + + // Set FK scale + bone0FkXfo.sc = bone0FkXfo.sc.multiplyScalar(rigScale); + bone1FkXfo.sc = bone1FkXfo.sc.multiplyScalar(rigScale); + bone2FkXfo.sc = bone2FkXfo.sc.multiplyScalar(rigScale); + + // Only FK + if (ikblend == 0.0) + { + bone0Xfo = bone0FkXfo; + bone1Xfo = bone1FkXfo; + bone2Xfo = bone2FkXfo; + } + // Solve IK/FK blend + else + { + // Orient and scale the bone and then position the child bone based on the parent's length + // We don't use the Xfo.transformVector() so we don't get the "scaled" transform value along + // with the length of the bone that already contains that "scale". Otherwise we'd have double + // transformation. + bone0Xfo.ori = bone0FkXfo.ori.sphericalLinearInterpolate(bone0Xfo.ori, ikblend); + bone0Xfo.sc = bone0FkXfo.sc.linearInterpolate(bone0Xfo.sc, ikblend); + + Scalar bone0OutLen = Math_linearInterpolate(bone0FkXfo.sc.x * bone0Len * rigScale, scaledBone0Len, ikblend); + bone1Xfo.tr = bone0Xfo.tr + bone0Xfo.ori.rotateVector(Vec3(bone0OutLen, 0, 0)); + bone1Xfo.ori = bone1FkXfo.ori.sphericalLinearInterpolate(bone1Xfo.ori, ikblend); + bone1Xfo.sc = bone1FkXfo.sc.linearInterpolate(bone1Xfo.sc, ikblend); + + Scalar bone1OutLen = Math_linearInterpolate(bone1FkXfo.sc.x * bone1Len * rigScale, scaledBone1Len, ikblend); + bone2Xfo.tr = bone1Xfo.tr + bone1Xfo.ori.rotateVector(Vec3(bone1OutLen, 0, 0)); + bone2Xfo.ori = bone2FkXfo.ori.sphericalLinearInterpolate(bone2Xfo.ori, ikblend); + bone2Xfo.sc = bone2FkXfo.sc.linearInterpolate(bone2Xfo.sc, ikblend); + } + } + + bone0Out = bone0Xfo.toMat44(); + bone1Out = bone1Xfo.toMat44(); + bone2Out = bone2Xfo.toMat44(); + + // Set debugging visibility. + this.setDebug(drawDebug); + if(this.drawDebug){ + + Color boneColor(1.0, 1.0, 0); + etDrawBone(this.handle.rootTransform, 'bone0', bone0Xfo, scaledBone0Len, scaledBone0Len * 0.15, boneColor); + etDrawBone(this.handle.rootTransform, 'bone1', bone1Xfo, scaledBone1Len, scaledBone1Len * 0.15, boneColor); + } +} \ No newline at end of file diff --git a/Python/kraken_examples/stretchyLimb_component.py b/Python/kraken_examples/stretchyLimb_component.py new file mode 100644 index 00000000..38769513 --- /dev/null +++ b/Python/kraken_examples/stretchyLimb_component.py @@ -0,0 +1,404 @@ +from kraken.core.maths import Vec3 +from kraken.core.maths.xfo import Xfo +from kraken.core.maths.xfo import xfoFromDirAndUpV + +from kraken.core.objects.components.base_example_component import BaseExampleComponent + +from kraken.core.objects.attributes.attribute_group import AttributeGroup +from kraken.core.objects.attributes.scalar_attribute import ScalarAttribute +from kraken.core.objects.attributes.bool_attribute import BoolAttribute + +from kraken.core.objects.constraints.pose_constraint import PoseConstraint + +from kraken.core.objects.component_group import ComponentGroup +from kraken.core.objects.locator import Locator +from kraken.core.objects.joint import Joint +from kraken.core.objects.ctrlSpace import CtrlSpace +from kraken.core.objects.control import Control + +from kraken.core.objects.operators.kl_operator import KLOperator + +from kraken.core.profiler import Profiler + + +class StretchyLimbComponent(BaseExampleComponent): + """StretchyLimb Component""" + + def __init__(self, name='limbBase', parent=None): + + super(StretchyLimbComponent, self).__init__(name, parent) + + # =========== + # Declare IO + # =========== + # Declare Inputs Xfos + self.globalSRTInputTgt = self.createInput('globalSRT', dataType='Xfo', parent=self.inputHrcGrp).getTarget() + self.limbParentInputTgt = self.createInput('pelvisInput', dataType='Xfo', parent=self.inputHrcGrp).getTarget() + + # Declare Output Xfos + self.limbUpperOutputTgt = self.createOutput('limbUpperXfo', dataType='Xfo', parent=self.outputHrcGrp).getTarget() + self.limbLowerOutputTgt = self.createOutput('limbLowerXfo', dataType='Xfo', parent=self.outputHrcGrp).getTarget() + self.limbEndOutputTgt = self.createOutput('limbEndXfo', dataType='Xfo', parent=self.outputHrcGrp).getTarget() + + # Declare Input Attrs + self.drawDebugInputAttr = self.createInput('drawDebug', dataType='Boolean', value=False, parent=self.cmpInputAttrGrp).getTarget() + self.rigScaleInputAttr = self.createInput('rigScale', value=1.0, dataType='Float', parent=self.cmpInputAttrGrp).getTarget() + self.rightSideInputAttr = self.createInput('rightSide', dataType='Boolean', value=False, parent=self.cmpInputAttrGrp).getTarget() + + # Declare Output Attrs + self.drawDebugOutputAttr = self.createOutput('drawDebug', dataType='Boolean', value=False, parent=self.cmpOutputAttrGrp).getTarget() + + +class StretchyLimbComponentGuide(StretchyLimbComponent): + """StretchyLimb Component Guide""" + + def __init__(self, name='limb', parent=None): + + Profiler.getInstance().push("Construct StretchyLimb Guide Component:" + name) + super(StretchyLimbComponentGuide, self).__init__(name, parent) + + + # ========= + # Controls + # ======== + + guideSettingsAttrGrp = AttributeGroup("GuideSettings", parent=self) + + # Guide Controls + self.upperCtl = Control('upper', parent=self.ctrlCmpGrp, shape="sphere") + self.lowerCtl = Control('lower', parent=self.ctrlCmpGrp, shape="sphere") + self.endCtl = Control('end', parent=self.ctrlCmpGrp, shape="sphere") + + data = { + "name": name, + "location": "L", + "upperXfo": Xfo(Vec3(0.9811, 9.769, -0.4572)), + "lowerXfo": Xfo(Vec3(1.4488, 5.4418, -0.5348)), + "endXfo": Xfo(Vec3(1.841, 1.1516, -1.237)), + } + + self.loadData(data) + + Profiler.getInstance().pop() + + # ============= + # Data Methods + # ============= + def saveData(self): + """Save the data for the component to be persisted. + + + Return: + The JSON data object + + """ + + data = super(StretchyLimbComponentGuide, self).saveData() + + data['upperXfo'] = self.upperCtl.xfo + data['lowerXfo'] = self.lowerCtl.xfo + data['endXfo'] = self.endCtl.xfo + + return data + + def loadData(self, data): + """Load a saved guide representation from persisted data. + + Arguments: + data -- object, The JSON data object. + + Return: + True if successful. + + """ + + super(StretchyLimbComponentGuide, self).loadData( data ) + + self.upperCtl.xfo = data['upperXfo'] + self.lowerCtl.xfo = data['lowerXfo'] + self.endCtl.xfo = data['endXfo'] + + return True + + def getRigBuildData(self): + """Returns the Guide data used by the Rig Component to define the layout of the final rig.. + + Return: + The JSON rig data object. + + """ + + data = super(StretchyLimbComponentGuide, self).getRigBuildData() + + # Values + startPos = self.upperCtl.xfo.tr + midPos = self.lowerCtl.xfo.tr + endPos = self.endCtl.xfo.tr + + # Calculate Upper Xfo + startToEnd = endPos.subtract(startPos).unit() + startToMid = midPos.subtract(startPos).unit() + + bone1Normal = startToEnd.cross(startToMid).unit() + bone1ZAxis = startToMid.cross(bone1Normal).unit() + + upperXfo = Xfo() + upperXfo.setFromVectors(startToMid, bone1Normal, bone1ZAxis, startPos) + + # Calculate Lower Xfo + midToEnd = endPos.subtract(midPos).unit() + midToStart = startPos.subtract(midPos).unit() + bone2Normal = midToStart.cross(midToEnd).unit() + bone2ZAxis = midToEnd.cross(bone2Normal).unit() + + lowerXfo = Xfo() + lowerXfo.setFromVectors(midToEnd, bone2Normal, bone2ZAxis, midPos) + + upperLen = startPos.subtract(midPos).length() + lowerLen = endPos.subtract(midPos).length() + + handleXfo = Xfo() + handleXfo.tr = endPos + + endXfo = Xfo() + endXfo.tr = endPos + # endXfo.ori = lowerXfo.ori + + upVXfo = xfoFromDirAndUpV(startPos, endPos, midPos) + upVXfo.tr = midPos + upVXfo.tr = upVXfo.transformVector(Vec3(0, 0, 5)) + + data['upperXfo'] = upperXfo + data['lowerXfo'] = lowerXfo + data['endXfo'] = endXfo + data['handleXfo'] = handleXfo + data['upVXfo'] = upVXfo + data['upperLen'] = upperLen + data['lowerLen'] = lowerLen + + return data + + # ============== + # Class Methods + # ============== + @classmethod + def getComponentType(cls): + """Enables introspection of the class prior to construction to determine if it is a guide component. + + Return: + The true if this component is a guide component. + + """ + + return 'Guide' + + @classmethod + def getRigComponentClass(cls): + """Returns the corresponding rig component class for this guide component class + + Return: + The rig component class. + + """ + + return StretchyLimbComponentRig + + +class StretchyLimbComponentRig(StretchyLimbComponent): + """StretchyLimb Component""" + + def __init__(self, name='limb', parent=None): + + Profiler.getInstance().push("Construct StretchyLimb Rig Component:" + name) + super(StretchyLimbComponentRig, self).__init__(name, parent) + + # ========= + # Controls + # ========= + # Upper (FK) + self.upperFKCtrlSpace = CtrlSpace('upperFK', parent=self.ctrlCmpGrp) + self.upperFKCtrl = Control('upperFK', parent=self.upperFKCtrlSpace, shape="cube") + self.upperFKCtrl.alignOnXAxis() + + # Lower (FK) + self.lowerFKCtrlSpace = CtrlSpace('lowerFK', parent=self.upperFKCtrl) + self.lowerFKCtrl = Control('lowerFK', parent=self.lowerFKCtrlSpace, shape="cube") + self.lowerFKCtrl.alignOnXAxis() + + # End (IK) + self.limbIKCtrlSpace = CtrlSpace('IK', parent=self.ctrlCmpGrp) + self.limbIKCtrl = Control('IK', parent=self.limbIKCtrlSpace, shape="pin") + + # Add Component Params to IK control + # TODO: Move these separate control + limbSettingsAttrGrp = AttributeGroup("DisplayInfo_StretchyLimbSettings", parent=self.limbIKCtrl) + limbDrawDebugInputAttr = BoolAttribute('drawDebug', value=False, parent=limbSettingsAttrGrp) + self.limbBone0LenInputAttr = ScalarAttribute('bone0Len', value=1.0, parent=limbSettingsAttrGrp) + self.limbBone1LenInputAttr = ScalarAttribute('bone1Len', value=1.0, parent=limbSettingsAttrGrp) + limbIKBlendInputAttr = ScalarAttribute('ikblend', value=1.0, minValue=0.0, maxValue=1.0, parent=limbSettingsAttrGrp) + limbSoftIKInputAttr = BoolAttribute('softIK', value=True, parent=limbSettingsAttrGrp) + limbSoftRatioInputAttr = ScalarAttribute('softRatio', value=0.0, minValue=0.0, maxValue=1.0, parent=limbSettingsAttrGrp) + limbStretchInputAttr = BoolAttribute('stretch', value=True, parent=limbSettingsAttrGrp) + limbStretchBlendInputAttr = ScalarAttribute('stretchBlend', value=0.0, minValue=0.0, maxValue=1.0, parent=limbSettingsAttrGrp) + limbSlideInputAttr = ScalarAttribute('slide', value=0.0, minValue=-1.0, maxValue=1.0, parent=limbSettingsAttrGrp) + limbPinInputAttr = ScalarAttribute('pin', value=0.0, minValue=0.0, maxValue=1.0, parent=limbSettingsAttrGrp) + + self.drawDebugInputAttr.connect(limbDrawDebugInputAttr) + + # UpV (IK Pole Vector) + self.limbUpVCtrlSpace = CtrlSpace('UpV', parent=self.ctrlCmpGrp) + self.limbUpVCtrl = Control('UpV', parent=self.limbUpVCtrlSpace, shape="triangle") + self.limbUpVCtrl.alignOnZAxis() + + # ========== + # Deformers + # ========== + deformersLayer = self.getOrCreateLayer('deformers') + self.defCmpGrp = ComponentGroup(self.getName(), self, parent=deformersLayer) + + upperDef = Joint('upper', parent=self.defCmpGrp) + upperDef.setComponent(self) + + lowerDef = Joint('lower', parent=self.defCmpGrp) + lowerDef.setComponent(self) + + endDef = Joint('end', parent=self.defCmpGrp) + endDef.setComponent(self) + + # ============== + # Constrain I/O + # ============== + # Constraint inputs + self.limbIKCtrlSpaceInputConstraint = PoseConstraint('_'.join([self.limbIKCtrlSpace.getName(), 'To', self.globalSRTInputTgt.getName()])) + self.limbIKCtrlSpaceInputConstraint.setMaintainOffset(True) + self.limbIKCtrlSpaceInputConstraint.addConstrainer(self.globalSRTInputTgt) + self.limbIKCtrlSpace.addConstraint(self.limbIKCtrlSpaceInputConstraint) + + self.limbUpVCtrlSpaceInputConstraint = PoseConstraint('_'.join([self.limbUpVCtrlSpace.getName(), 'To', self.globalSRTInputTgt.getName()])) + self.limbUpVCtrlSpaceInputConstraint.setMaintainOffset(True) + self.limbUpVCtrlSpaceInputConstraint.addConstrainer(self.globalSRTInputTgt) + self.limbUpVCtrlSpace.addConstraint(self.limbUpVCtrlSpaceInputConstraint) + + self.limbRootInputConstraint = PoseConstraint('_'.join([self.limbIKCtrl.getName(), 'To', self.limbParentInputTgt.getName()])) + self.limbRootInputConstraint.setMaintainOffset(True) + self.limbRootInputConstraint.addConstrainer(self.limbParentInputTgt) + self.upperFKCtrlSpace.addConstraint(self.limbRootInputConstraint) + + # =============== + # Add Splice Ops + # =============== + # Add StretchyLimb Splice Op + self.limbIKKLOp = KLOperator('limbKLOp', 'TwoBoneStretchyIKSolver', 'Kraken') + self.addOperator(self.limbIKKLOp) + + # Add Att Inputs + self.limbIKKLOp.setInput('drawDebug', self.drawDebugInputAttr) + self.limbIKKLOp.setInput('rigScale', self.rigScaleInputAttr) + + self.limbIKKLOp.setInput('bone0Len', self.limbBone0LenInputAttr) + self.limbIKKLOp.setInput('bone1Len', self.limbBone1LenInputAttr) + self.limbIKKLOp.setInput('ikblend', limbIKBlendInputAttr) + self.limbIKKLOp.setInput('softIK', limbSoftIKInputAttr) + self.limbIKKLOp.setInput('softRatio', limbSoftRatioInputAttr) + self.limbIKKLOp.setInput('stretch', limbStretchInputAttr) + self.limbIKKLOp.setInput('stretchBlend', limbStretchBlendInputAttr) + self.limbIKKLOp.setInput('slide', limbSlideInputAttr) + self.limbIKKLOp.setInput('pin', limbPinInputAttr) + self.limbIKKLOp.setInput('rightSide', self.rightSideInputAttr) + + # Add Xfo Inputs + self.limbIKKLOp.setInput('root', self.limbParentInputTgt) + self.limbIKKLOp.setInput('bone0FK', self.upperFKCtrl) + self.limbIKKLOp.setInput('bone1FK', self.lowerFKCtrl) + self.limbIKKLOp.setInput('ikHandle', self.limbIKCtrl) + self.limbIKKLOp.setInput('upV', self.limbUpVCtrl) + + # Add Xfo Outputs + self.limbIKKLOp.setOutput('bone0Out', self.limbUpperOutputTgt) + self.limbIKKLOp.setOutput('bone1Out', self.limbLowerOutputTgt) + self.limbIKKLOp.setOutput('bone2Out', self.limbEndOutputTgt) + + # ===================== + # Connect the deformers + # ===================== + + # Add StretchyLimb Deformer Splice Op + self.outputsToDeformersKLOp = KLOperator('limbDeformerKLOp', 'MultiPoseConstraintSolver', 'Kraken') + self.addOperator(self.outputsToDeformersKLOp) + + # Add Att Inputs + self.outputsToDeformersKLOp.setInput('drawDebug', self.drawDebugInputAttr) + self.outputsToDeformersKLOp.setInput('rigScale', self.rigScaleInputAttr) + + # Add Xfo Inputs + self.outputsToDeformersKLOp.setInput('constrainers', [self.limbUpperOutputTgt, self.limbLowerOutputTgt, self.limbEndOutputTgt]) + + # Add Xfo Outputs + self.outputsToDeformersKLOp.setOutput('constrainees', [upperDef, lowerDef, endDef]) + + Profiler.getInstance().pop() + + # ============= + # Data Methods + # ============= + def loadData(self, data=None): + """Load a saved guide representation from persisted data. + + Arguments: + data -- object, The JSON data object. + + Return: + True if successful. + + """ + + super(StretchyLimbComponentRig, self).loadData( data ) + + self.upperFKCtrlSpace.xfo = data['upperXfo'] + self.upperFKCtrl.xfo = data['upperXfo'] + self.upperFKCtrl.scalePoints(Vec3(data['upperLen'], 1.75, 1.75)) + + self.limbUpperOutputTgt.xfo = data['upperXfo'] + self.limbLowerOutputTgt.xfo = data['lowerXfo'] + + self.lowerFKCtrlSpace.xfo = data['lowerXfo'] + self.lowerFKCtrl.xfo = data['lowerXfo'] + self.lowerFKCtrl.scalePoints(Vec3(data['lowerLen'], 1.5, 1.5)) + + self.limbIKCtrlSpace.xfo.tr = data['endXfo'].tr + self.limbIKCtrl.xfo.tr = data['endXfo'].tr + + if self.getLocation() == "R": + self.limbIKCtrl.rotatePoints(0, 90, 0) + self.limbIKCtrl.translatePoints(Vec3(-1.0, 0.0, 0.0)) + else: + self.limbIKCtrl.rotatePoints(0, -90, 0) + self.limbIKCtrl.translatePoints(Vec3(1.0, 0.0, 0.0)) + + self.limbUpVCtrlSpace.xfo = data['upVXfo'] + self.limbUpVCtrl.xfo = data['upVXfo'] + + self.rightSideInputAttr.setValue(self.getLocation() is 'R') + self.limbBone0LenInputAttr.setMin(0.0) + self.limbBone0LenInputAttr.setMax(data['upperLen'] * 3.0) + self.limbBone0LenInputAttr.setValue(data['upperLen']) + self.limbBone1LenInputAttr.setMin(0.0) + self.limbBone1LenInputAttr.setMax(data['lowerLen'] * 3.0) + self.limbBone1LenInputAttr.setValue(data['lowerLen']) + + self.limbParentInputTgt.xfo = data['upperXfo'] + + # Eval Constraints + self.limbIKCtrlSpaceInputConstraint.evaluate() + self.limbUpVCtrlSpaceInputConstraint.evaluate() + self.limbRootInputConstraint.evaluate() + + # Eval Operators + self.limbIKKLOp.evaluate() + self.outputsToDeformersKLOp.evaluate() + + +from kraken.core.kraken_system import KrakenSystem +ks = KrakenSystem.getInstance() +ks.registerComponent(StretchyLimbComponentGuide) +ks.registerComponent(StretchyLimbComponentRig)