예제 #1
0
class LegComponentRig(LegComponent):
    """Leg Component"""

    def __init__(self, name='leg', parent=None):

        Profiler.getInstance().push("Construct Leg Rig Component:" + name)
        super(LegComponentRig, self).__init__(name, parent)


        # =========
        # Controls
        # =========
        # Femur
        self.femurFKCtrlSpace = CtrlSpace('femurFK', parent=self.ctrlCmpGrp)
        self.femurFKCtrl = Control('femurFK', parent=self.femurFKCtrlSpace, shape="cube")
        self.femurFKCtrl.alignOnXAxis()
        self.femurFKCtrl.lockTranslation(True, True, True)
        self.femurFKCtrl.lockScale(True, True, True)

        # Shin
        self.shinFKCtrlSpace = CtrlSpace('shinFK', parent=self.femurFKCtrl)
        self.shinFKCtrl = Control('shinFK', parent=self.shinFKCtrlSpace, shape="cube")
        self.shinFKCtrl.alignOnXAxis()
        self.shinFKCtrl.lockTranslation(True, True, True)
        self.shinFKCtrl.lockScale(True, True, True)

        # IK Handle
        self.legIKCtrlSpace = CtrlSpace('IK', parent=self.ctrlCmpGrp)
        self.legIKCtrl = Control('IK', parent=self.legIKCtrlSpace, shape="pin")
        self.legIKCtrl.lockScale(True, True, True)

        # Add Component Params to IK control
        legSettingsAttrGrp = AttributeGroup("DisplayInfo_LegSettings", parent=self.legIKCtrl)
        legDrawDebugInputAttr = BoolAttribute('drawDebug', value=False, parent=legSettingsAttrGrp)
        self.legRightSideInputAttr = BoolAttribute('rightSide', value=False, parent=legSettingsAttrGrp)
        self.legBone0LenInputAttr = ScalarAttribute('bone0Len', value=1.0, parent=legSettingsAttrGrp)
        self.legBone1LenInputAttr = ScalarAttribute('bone1Len', value=1.0, parent=legSettingsAttrGrp)
        legIKBlendInputAttr = ScalarAttribute('ikblend', value=1.0, minValue=0.0, maxValue=1.0, parent=legSettingsAttrGrp)

        # Util Objects
        self.ikRootPosition = Transform("ikRootPosition", parent=self.ctrlCmpGrp)

        # Connect Input Attrs
        self.drawDebugInputAttr.connect(legDrawDebugInputAttr)

        # Connect Output Attrs
        self.drawDebugOutputAttr.connect(legDrawDebugInputAttr)
        self.ikBlendOutputAttr.connect(legIKBlendInputAttr)

        # UpV
        self.legUpVCtrlSpace = CtrlSpace('UpV', parent=self.ctrlCmpGrp)
        self.legUpVCtrl = Control('UpV', parent=self.legUpVCtrlSpace, shape="triangle")
        self.legUpVCtrl.alignOnZAxis()
        self.legUpVCtrl.lockRotation(True, True, True)
        self.legUpVCtrl.lockScale(True, True, True)


        # ==========
        # Deformers
        # ==========
        deformersLayer = self.getOrCreateLayer('deformers')
        self.defCmpGrp = ComponentGroup(self.getName(), self, parent=deformersLayer)
        self.addItem('defCmpGrp', self.defCmpGrp)

        femurDef = Joint('femur', parent=self.defCmpGrp)
        femurDef.setComponent(self)

        kneeDef = Joint('knee', parent=self.defCmpGrp)
        kneeDef.setComponent(self)

        shinDef = Joint('shin', parent=self.defCmpGrp)
        shinDef.setComponent(self)

        # ==============
        # Constrain I/O
        # ==============
        # Constraint inputs
        self.legIKCtrlSpaceInputConstraint = PoseConstraint('_'.join([self.legIKCtrlSpace.getName(), 'To', self.globalSRTInputTgt.getName()]))
        self.legIKCtrlSpaceInputConstraint.setMaintainOffset(True)
        self.legIKCtrlSpaceInputConstraint.addConstrainer(self.globalSRTInputTgt)
        self.legIKCtrlSpace.addConstraint(self.legIKCtrlSpaceInputConstraint)

        self.legUpVCtrlSpaceInputConstraint = PoseConstraint('_'.join([self.legUpVCtrlSpace.getName(), 'To', self.globalSRTInputTgt.getName()]))
        self.legUpVCtrlSpaceInputConstraint.setMaintainOffset(True)
        self.legUpVCtrlSpaceInputConstraint.addConstrainer(self.globalSRTInputTgt)
        self.legUpVCtrlSpace.addConstraint(self.legUpVCtrlSpaceInputConstraint)

        self.legRootInputConstraint = PoseConstraint('_'.join([self.femurFKCtrlSpace.getName(), 'To', self.legPelvisInputTgt.getName()]))
        self.legRootInputConstraint.setMaintainOffset(True)
        self.legRootInputConstraint.addConstrainer(self.legPelvisInputTgt)
        self.femurFKCtrlSpace.addConstraint(self.legRootInputConstraint)

        self.ikRootPosInputConstraint = PoseConstraint('_'.join([self.ikRootPosition.getName(), 'To', self.legPelvisInputTgt.getName()]))
        self.ikRootPosInputConstraint.setMaintainOffset(True)
        self.ikRootPosInputConstraint.addConstrainer(self.legPelvisInputTgt)
        self.ikRootPosition.addConstraint(self.ikRootPosInputConstraint)

        # Constraint outputs
        self.legEndFKOutputConstraint = PoseConstraint('_'.join([self.legEndFKOutputTgt.getName(), 'To', self.shinFKCtrl.getName()]))
        self.legEndFKOutputConstraint.setMaintainOffset(True)
        self.legEndFKOutputConstraint.addConstrainer(self.shinFKCtrl)
        self.legEndFKOutputTgt.addConstraint(self.legEndFKOutputConstraint)

        self.ikHandleOutputConstraint = PoseConstraint('_'.join([self.ikHandleOutputTgt.getName(), 'To', self.legIKCtrl.getName()]))
        self.ikHandleOutputConstraint.setMaintainOffset(True)
        self.ikHandleOutputConstraint.addConstrainer(self.legIKCtrl)
        self.ikHandleOutputTgt.addConstraint(self.ikHandleOutputConstraint)


        # ===============
        # Add Splice Ops
        # ===============
        # Add Leg Splice Op
        self.legIKKLOp = KLOperator('legKLOp', 'TwoBoneIKSolver', 'Kraken')
        self.addOperator(self.legIKKLOp)

        # Add Att Inputs
        self.legIKKLOp.setInput('drawDebug', self.drawDebugInputAttr)
        self.legIKKLOp.setInput('rigScale', self.rigScaleInputAttr)

        self.legIKKLOp.setInput('bone0Len', self.legBone0LenInputAttr)
        self.legIKKLOp.setInput('bone1Len', self.legBone1LenInputAttr)
        self.legIKKLOp.setInput('ikblend', legIKBlendInputAttr)
        self.legIKKLOp.setInput('rightSide', self.legRightSideInputAttr)

        # Add Xfo Inputs
        self.legIKKLOp.setInput('root', self.ikRootPosition)
        self.legIKKLOp.setInput('bone0FK', self.femurFKCtrl)
        self.legIKKLOp.setInput('bone1FK', self.shinFKCtrl)
        self.legIKKLOp.setInput('ikHandle', self.legIKTargetInputTgt)
        self.legIKKLOp.setInput('upV', self.legUpVCtrl)

        # Add Xfo Outputs
        self.legIKKLOp.setOutput('bone0Out', self.femurOutputTgt)
        self.legIKKLOp.setOutput('bone1Out', self.shinOutputTgt)
        self.legIKKLOp.setOutput('bone2Out', self.legEndOutputTgt)
        self.legIKKLOp.setOutput('midJointOut', self.kneeOutputTgt)


        # Add Leg Deformer Splice Op
        self.outputsToDeformersKLOp = KLOperator('legDeformerKLOp', '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.femurOutputTgt, self.kneeOutputTgt, self.shinOutputTgt])

        # Add Xfo Outputs
        self.outputsToDeformersKLOp.setOutput('constrainees', [femurDef, kneeDef, shinDef])

        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(LegComponentRig, self).loadData( data )

        createIKHandle = data.get('createIKHandle')
        femurXfo = data.get('femurXfo')
        kneeXfo = data.get('kneeXfo')
        handleXfo = data.get('handleXfo')
        upVXfo = data.get('upVXfo')
        femurLen = data.get('femurLen')
        shinLen = data.get('shinLen')

        self.femurFKCtrlSpace.xfo = femurXfo
        self.femurFKCtrl.xfo = femurXfo
        self.femurFKCtrl.scalePoints(Vec3(femurLen, 1.75, 1.75))

        self.femurOutputTgt.xfo = femurXfo
        self.shinOutputTgt.xfo = kneeXfo

        self.shinFKCtrlSpace.xfo = kneeXfo
        self.shinFKCtrl.xfo = kneeXfo
        self.shinFKCtrl.scalePoints(Vec3(shinLen, 1.5, 1.5))

        self.legEndFKOutputTgt.xfo.tr = handleXfo.tr
        self.legEndFKOutputTgt.xfo.ori = kneeXfo.ori

        self.ikHandleOutputTgt.xfo = handleXfo

        self.ikRootPosition.xfo = femurXfo

        self.legIKCtrlSpace.xfo = handleXfo
        self.legIKCtrl.xfo = handleXfo

        if self.getLocation() == 'R':
            self.legIKCtrl.rotatePoints(0, 90, 0)
            self.legIKCtrl.translatePoints(Vec3(-1.0, 0.0, 0.0))
        else:
            self.legIKCtrl.rotatePoints(0, -90, 0)
            self.legIKCtrl.translatePoints(Vec3(1.0, 0.0, 0.0))

        self.legUpVCtrlSpace.xfo.tr = upVXfo.tr
        self.legUpVCtrl.xfo.tr = upVXfo.tr

        self.legRightSideInputAttr.setValue(self.getLocation() is 'R')
        self.legBone0LenInputAttr.setMin(0.0)
        self.legBone0LenInputAttr.setMax(femurLen * 3.0)
        self.legBone0LenInputAttr.setValue(femurLen)
        self.legBone1LenInputAttr.setMin(0.0)
        self.legBone1LenInputAttr.setMax(shinLen * 3.0)
        self.legBone1LenInputAttr.setValue(shinLen)

        self.legPelvisInputTgt.xfo = femurXfo
        self.legIKTargetInputTgt.xfo = handleXfo

        # TODO: We need the Rig class to be modified to handle the ability to
        # query if the ports are connected during loadData. Currently just a
        # place holder until that happens.

        # If IK Target input is not connected, switch to legIKCtrl
        # ikTargetInput = self.getInputByName('ikTarget')
        # if not ikTargetInput.isConnected():
            # self.legIKKLOp.setInput('ikHandle', self.legIKCtrl)

        # Eval Input Constraints
        self.ikRootPosInputConstraint.evaluate()
        self.legIKCtrlSpaceInputConstraint.evaluate()
        self.legUpVCtrlSpaceInputConstraint.evaluate()
        self.legRootInputConstraint.evaluate()

        # Eval Operators
        self.legIKKLOp.evaluate()
        self.outputsToDeformersKLOp.evaluate()

        # Eval Output Constraints
        self.legEndFKOutputConstraint.evaluate()
        self.ikHandleOutputConstraint.evaluate()
예제 #2
0
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.rightSideInputAttr = BoolAttribute('rightSide',
                                                value=False,
                                                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)
        self.addItem('defCmpGrp', self.defCmpGrp)

        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)

        upperXfo = data.get('upperXfo')
        upperLen = data.get('upperLen')
        lowerXfo = data.get('lowerXfo')
        lowerLen = data.get('lowerLen')
        endXfo = data.get('endXfo')
        upVXfo = data.get('upVXfo')

        self.upperFKCtrlSpace.xfo = upperXfo
        self.upperFKCtrl.xfo = upperXfo
        self.upperFKCtrl.scalePoints(Vec3(upperLen, 1.75, 1.75))

        self.limbUpperOutputTgt.xfo = upperXfo
        self.limbLowerOutputTgt.xfo = lowerXfo

        self.lowerFKCtrlSpace.xfo = lowerXfo
        self.lowerFKCtrl.xfo = lowerXfo
        self.lowerFKCtrl.scalePoints(Vec3(lowerLen, 1.5, 1.5))

        self.limbIKCtrlSpace.xfo.tr = endXfo.tr
        self.limbIKCtrl.xfo.tr = 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 = upVXfo
        self.limbUpVCtrl.xfo = upVXfo

        self.limbBone0LenInputAttr.setMin(0.0)
        self.limbBone0LenInputAttr.setMax(upperLen * 3.0)
        self.limbBone0LenInputAttr.setValue(upperLen)
        self.limbBone1LenInputAttr.setMin(0.0)
        self.limbBone1LenInputAttr.setMax(lowerLen * 3.0)
        self.limbBone1LenInputAttr.setValue(lowerLen)

        self.limbParentInputTgt.xfo = upperXfo

        # Set Attrs
        self.rightSideInputAttr.setValue(self.getLocation() is 'R')

        # Eval Constraints
        self.limbIKCtrlSpaceInputConstraint.evaluate()
        self.limbUpVCtrlSpaceInputConstraint.evaluate()
        self.limbRootInputConstraint.evaluate()

        # Eval Operators
        self.limbIKKLOp.evaluate()
        self.outputsToDeformersKLOp.evaluate()
예제 #3
0
파일: object_3d.py 프로젝트: AbedSHP/Kraken
class Object3D(SceneItem):
    """Kraken base object type for any 3D object."""

    def __init__(self, name, parent=None):
        super(Object3D, self).__init__(name, parent)
        self._children = []
        self._flags = {}
        self._attributeGroups = []
        self._constraints = []
        self._xfo = Xfo()
        self._ro = RotationOrder()
        self._color = None

        self._implicitAttrGrp = AttributeGroup("implicitAttrGrp", self)
        self._visibility = BoolAttribute('visibility',
                                         True,
                                         self._implicitAttrGrp)

        self._shapeVisibility = BoolAttribute('ShapeVisibility',
                                              True,
                                              self._implicitAttrGrp)

        if parent is not None:
            parent.addChild(self)


    # ==================
    # Property Methods
    # ==================
    @property
    def xfo(self):
        """Gets xfo property of this Object3D.

        Returns:
            Xfo: Xfo property of this Object3D.

        """

        return self._xfo

    @xfo.setter
    def xfo(self, value):
        """Sets xfo of this Object3D.

        Note:
            In Python, objects are always referenced, meaning to get a unique
            instance, an explicit clone is required. In KL, structs are passed
            by value, meaning that every assignment of a struct causes a clone.

            This means that in KL it is impossible for 2 objects to reference
            the same KL math object. This is an important performance feature
            of KL.

            The members of the KL Math objects have this property. 2 Xfos
            cannot share the same tr value. Here we implcitly clone the math
            object to ensure the same behavior as in KL.

        Args:
            value (Xfo): Vector to set the xfo by.

        Returns:
            bool: True if successful.

        """

        self._xfo = value.clone()

        return True


    @property
    def ro(self):
        """Gets Rotation Order property of this Object3D.

        Returns:
            RotationOrder: Rotation Order property of this Object3D.

        """

        return self._ro

    @ro.setter
    def ro(self, value):
        """Sets Rotation Order of this Object3D.

        Note:
            In Python, objects are always referenced, meaning to get a unique
            instance, an explicit clone is required. In KL, structs are passed
            by value, meaning that every assignment of a struct causes a clone.

            This means that in KL it is impossible for 2 objects to reference
            the same KL math object. This is an important performance feature
            of KL.

            The members of the KL Math objects have this property. 2 Xfos
            cannot share the same tr value. Here we implcitly clone the math
            object to ensure the same behavior as in KL.

        Args:
            value (RotationOrder): New rotation order.

        Returns:
            bool: True if successful.

        """

        self._ro = value.clone()

        return True

    @property
    def localXfo(self):
        """Gets local transform of this Object3D

        Returns:
            Xfo: Local Xfo of the object.

        """

        globalXfo = self.globalXfo

        parent = self.getParent()
        if not isinstance(parent, SceneItem):
            return globalXfo

        parentXfo = parent.globalXfo

        return parentXfo.inverse().multiply(globalXfo)

    @property
    def globalXfo(self):
        """Gets global transform of this Object3D

        Returns:
            Xfo: Global Xfo

        """

        for source in self.getSources():
            if isinstance(source, Object3D):
                continue
            if isinstance(source, Constraint):
                return source.compute()
            if isinstance(source, Operator):
                source.evaluate()
                break

        return self._xfo

    # =============
    # Name Methods
    # =============
    def getBuildName(self):
        """Returns the build name for the object.

        Returns:
            str: Name to be used in the DCC.

        """

        typeNameHierarchy = self.getTypeHierarchyNames()

        config = Config.getInstance()

        # If flag is set on object to use explicit name, return it.
        if config.getExplicitNaming() is True or \
                self.testFlag('EXPLICIT_NAME'):
                return self.getName()

        nameTemplate = config.getNameTemplate()

        # Get the token list for this type of object
        format = None
        for typeName in nameTemplate['formats'].keys():
            if typeName in typeNameHierarchy:
                format = nameTemplate['formats'][typeName]
                break

        if format is None:
            format = nameTemplate['formats']['default']

        objectType = None
        for eachType in typeNameHierarchy:
            if eachType in nameTemplate['types'].keys():
                objectType = eachType
                break

        if objectType is None:
            objectType = 'default'

        # Generate a name by concatenating the resolved tokens together.
        builtName = ""
        skipSep = False
        for token in format:

            if token is 'sep':
                if not skipSep:
                    builtName += nameTemplate['separator']

            elif token is 'location':
                if self.isTypeOf('Component'):
                    location = self.getLocation()
                else:
                    location = self.getComponent().getLocation()

                if location not in nameTemplate['locations']:
                    raise ValueError("Invalid location on: " + self.getPath())

                builtName += location

            elif token is 'type':

                if objectType == 'Locator' and self.testFlag('inputObject'):
                    objectType = 'ComponentInput'
                elif objectType == 'Locator' and self.testFlag('outputObject'):
                    objectType = 'ComponentOutput'

                builtName += nameTemplate['types'][objectType]

            elif token is 'name':
                builtName += self.getName()

            elif token is 'component':
                if self.getComponent() is None:
                    skipSep = True
                    continue
                builtName += self.getComponent().getName()

            elif token is 'container':
                if self.getContainer() is None:
                    skipSep = True
                    continue
                builtName += self.getContainer().getName()

            else:
                raise ValueError("Unresolvabled token '" + token +
                    "' used on: " + self.getPath())

        return builtName

    def setName(self, name):
        """Sets the name of the object with a string.

        Args:
            name (str): The new name for the item.

        Returns:
            bool: True if successful.

        """

        # check for name collision and adjust the name if they exist
        if self.getParent() is not None:
            # Increment name if it already exists
            initName = name
            suffix = 1
            collision = True
            while collision:
                child = self.getParent().getChildByDecoratedName(name + self.getNameDecoration())
                collision = child is not None and child is not self
                if not collision:
                    break

                result = re.split(r"(\d+)$", initName, 1)
                if len(result) > 1:
                    initName = result[0]
                    suffix = int(result[1])

                name = initName + str(suffix).zfill(2)
                suffix += 1

        super(Object3D, self).setName(name)

        return True

    # ==================
    # Hierarchy Methods
    # ==================
    def getContainer(self):
        """Returns the Container the object belongs to.

        Returns:
            Object: Container.

        """

        parent = self.getParent()
        while (parent is not None and 'Container' not in
               parent.getTypeHierarchyNames()):
            parent = parent.getParent()

        return parent

    def getLayer(self):
        """Returns the Layer the object belongs to.

        Returns:
            Object: Layer this object belongs to.

        """

        parent = self.getParent()
        while (parent is not None and not parent.isTypeOf('Layer')):
            parent = parent.getParent()

        return parent

    # ==============
    # Child Methods
    # ==============
    def hasChild(self, child):
        """Checks the supplied item is a child

        Args:
            child (Object): Object to check if is is a child of this object.

        """

        for i, eachChild in enumerate(self.getChildren()):
            if eachChild == child:
                return True

        return False

    def _checkChildIndex(self, index):
        """Checks the supplied index is valid.

        Args:
            index (int): Child index to check.

        """

        if index > len(self.getChildren()):
            raise IndexError("'" + str(index) +
                             "' is out of the range of the 'children' array.")

        return True

    def addChild(self, child):
        """Adds a child to this object.

        Note:
            We allow for duplicate child names as long as the types differ.

        Args:
            child (Object): Object that will be a child of this object.

        Returns:
            bool: True if successful.

        """

        if child.getParent() is not None:
            parent = child.getParent()
            if child in parent.getChildren():
                parent.getChildren().remove(child)


        # check for name collision and adjust the name if they exist
        # Increment name if it already exists
        initName = child.getName()
        name = initName
        suffix = 1

        while self.getChildByDecoratedName(name + child.getNameDecoration()) is not None:
            name = initName + str(suffix).zfill(2)
            suffix += 1

        if initName != name:
            child.setName(name)

        self.getChildren().append(child)
        child.setParent(self)

        # Assign the child the same component.
        if self._component is not None:
            child.setComponent(self._component)

        return True

    def removeChildByIndex(self, index):
        """Removes a child from this object by index.

        Args:
            index (int): Index of child to remove.

        Returns:
            bool: True if successful.

        """

        if self._checkChildIndex(index) is not True:
            return False

        del self.getChildren()[index]

        return True

    def removeChildByName(self, name):
        """Removes a child from this object by name.

        Args:
            name (str): Name of child to remove.

        Returns:
            bool: True if successful.

        """

        removeIndex = None

        for i, eachChild in enumerate(self.getChildren()):
            if eachChild.getName() == name:
                removeIndex = i

        if removeIndex is None:
            raise ValueError("'" + name +
                             "' is not a valid child of this object.")

        self.removeChildByIndex(removeIndex)

        return True

    def removeChild(self, child):
        """Removed the child as an child item of this object.

        Returns:
            bool: True if successful.

        """

        try:
            self._children.remove(child)
        except:
            names = []
            for c in self._children:
                names.append(c.getName())
            raise Exception("Object '" + self.getPath() +
                            "' does not have child:" + child.getPath() +
                            ". it does have:" + str(names))

        child.setParent(None)

        # Un-assign the child the component.
        if self._component is not None:
            child.setComponent(None)

        return True

    def getDescendents(self, nodeList=None, classType=None, inheritedClass=False):
        """Gets the children of this object.

        Args:
            nodeList: (list): optional list to append children to
            classType (str): Name of the type of class to limit the search to
            inheritedClass (bool): Match nodes that is a sub-class of type.

        Returns:
            list: Child objects.

        """

        if nodeList is None:
            nodeList = []

        for child in self._children:
                if classType is not None:
                    if inheritedClass is not None and child.isTypeOf(classType):
                        nodeList.append(child)
                    elif child.getTypeName() == classType:
                        nodeList.append(child)

                else:
                    nodeList.append(child)

                child.getDescendents(classType=classType,
                                     nodeList=nodeList,
                                     inheritedClass=inheritedClass)

        return nodeList

    def getChildren(self):
        """Gets the children of this object.

        Returns:
            list: Child objects.

        """

        return self._children

    def getNumChildren(self):
        """Returns the number of children this object has.

        Returns:
            int: Number of children of this object.

        """

        return len(self.getChildren())

    def getChildByIndex(self, index):
        """Returns the child object at specified index.

        Args:
            index (int): Index of the child to find.

        Returns:
            Object: Child object at specified index.

        """

        if self._checkChildIndex(index) is not True:
            return False

        return self.getChildren()[index]

    def getChildByName(self, name):
        """Returns the child object with the specified name.

        Args:
            name (str): Name of the child to return.

        Returns:
            Object: Object if found.

        """

        for eachChild in self.getChildren():
            if eachChild.getName() == name:
                return eachChild

        return None

    def getChildByDecoratedName(self, decoratedName):
        """Returns the child object with the specified name.

        Args:
            decoratedName (str): Decorated name of the child to find.

        Returns:
            Object: Object if found.

        """

        for eachChild in self.getChildren():
            if eachChild.getDecoratedName() == decoratedName:
                return eachChild

        return None

    def getChildrenByType(self, childType):
        """Returns all children that are of the specified type.

        Args:
            childType (str): Type of children to find.

        Returns:
            list: Array of child objects of the specified type.

        """

        childrenOfType = []
        for eachChild in self.getChildren():
            if eachChild.isTypeOf(childType):
                childrenOfType.append(eachChild)

        return childrenOfType

    # =============
    # Flag Methods
    # =============
    def setFlag(self, name):
        """Sets the flag of the specified name.

        Returns:
            bool: True if successful.

        """

        self._flags[name] = True

        return True

    def testFlag(self, name):
        """Tests if the specified flag is set.

        Args:
            name (str): Name of the flag to test.

        Returns:
            bool: True if flag is set.

        """

        return name in self._flags

    def clearFlag(self, name):
        """Clears the flag of the specified name.

        Args:
            name (str): Name of the flag to clear.

        Returns:
            bool: True if successful.

        """

        if name in self._flags:
            del self._flags[name]
            return True

        return False

    # ========================
    # Attribute Group Methods
    # ========================
    def _checkAttributeGroupIndex(self, index):
        """Checks the supplied index is valid.

        Args:
            index (int): Attribute index to check.

        Returns:
            bool: True if successful.

        """

        if index > len(self._attributeGroups):
            raise IndexError("'" + str(index) +
                             "' is out of the range of 'attributeGroups' array.")

        return True


    def addAttributeGroup(self, attributeGroup):
        """Adds an attributeGroup to this object.

        Args:
            attributeGroup (Object): Attribute Group object to add to this
                object.

        Returns:
            bool: True if successful.

        """

        if attributeGroup.getName() in [x.getName() for x in self._attributeGroups]:
            raise IndexError("Child with " + attributeGroup.getName() +
                             " already exists as a attributeGroup.")

        self._attributeGroups.append(attributeGroup)
        attributeGroup.setParent(self)

        return True

    def removeAttributeGroupByIndex(self, index):
        """Removes attribute at specified index.

        Args:
            index (int): Index of attribute to remove.

        Returns:
            bool: True if successful.

        """

        if self._checkAttributeGroupIndex(index) is not True:
            return False

        del self._attributeGroups[index]

        return True

    def removeAttributeGroupByName(self, name):
        """Removes the attribute with the specified name.

        Args:
            name (str): Name of the attribute to remove.

        Returns:
            bool: True if successful.

        """

        removeIndex = None

        for i, eachAttributeGroup in enumerate(self._attributeGroups):
            if eachAttributeGroup.getName() == name:
                removeIndex = i

        if removeIndex is None:
            return False

        self.removeAttributeGroupByIndex(removeIndex)

        return True

    def getNumAttributeGroups(self):
        """Returns the number of attributeGroups as an integer.

        Returns:
            int: Number of attributeGroups on this object.

        """

        return len(self._attributeGroups)

    def getAttributeGroupByIndex(self, index):
        """Returns the attribute at the specified index.

        Args:
            index (int): Index of the attribute to return.

        Returns:
            AttributeGroup: Attribute Group at the specified index.

        """

        if self._checkAttributeGroupIndex(index) is not True:
            return False

        return self._attributeGroups[index]

    def getAttributeGroupByName(self, name):
        """Return the attribute group with the specified name.

        Args:
            name (str): Name of the attribute group to return.

        Returns:
            Attribute: Attribute with the specified name.

        """

        for eachAttributeGroup in self._attributeGroups:
            if eachAttributeGroup.getName() == name:
                return eachAttributeGroup

        return None

    # ===================
    # Constraint Methods
    # ===================
    def checkConstraintIndex(self, index):
        """Checks the supplied index is valid.

        Args:
            index (int): Constraint index to check.

        Returns:
            bool: True if successful.

        """

        if index > len(self._constraints):
            raise IndexError("'" + str(index) +
                             "' is out of the range of 'constraints' array.")

        return True

    def constrainTo(self, constrainers, constraintType="Pose", maintainOffset=False, name=None):
        """Adds an constraint to this object.

        Args:
            constrainers (Object or Object list): Constraint object to add to
                this object or objects.
            constraintType (str): String name of the constraint type.
            maintainOffset (bool): Sets the constraint to maintain offset when
                creating the constraint.
            name (str): Name of the constraint. If set to None, a name is
                automatically generated.

        Returns:
            string: Constraint object

        """

        if name is None:
            constraintName = ""
            if hasattr(constrainers, '__iter__'):
                constraintName = '_'.join([self.getName(), 'To', constrainers[0].getName(), constraintType + 'Constraint'])
            else:
                constraintName = '_'.join([self.getName(), 'To', constrainers.getName(), constraintType + 'Constraint'])
        else:
            constraintName = name

        constraint = None
        if constraintType == "Orientation":
            constraint = OrientationConstraint(constraintName)
        elif constraintType == "Pose":
            constraint = PoseConstraint(constraintName)
        elif constraintType == "Position":
            constraint = PositionConstraint(constraintName)
        elif constraintType == "Scale":
            constraint = ScaleConstraint(constraintName)
        else:
            raise ValueError("'" + constraintType +
                "' is not a valid constraint type. Valid types are Orientation, Pose, Position, or Scale")

        # Accept a single object or a list of objects
        if hasattr(constrainers, '__iter__'):
            pass
        else:
            constrainers = [constrainers]

        for constrainer in constrainers:
            constraint.addConstrainer(constrainer)

        constraint.setMaintainOffset(maintainOffset)

        self.addConstraint(constraint)

        return constraint

    def addConstraint(self, constraint):
        """Adds an constraint to this object.

        Args:
            constraint (Object): Constraint object to add to this object.

        Returns:
            bool: True if successful.

        """

        if constraint.getName() in [x.getName() for x in self._constraints]:
            raise IndexError("Constraint with name '" + constraint.getName() +
                             "'' already exists as a constraint.")

        self._constraints.append(constraint)

        constraint.setParent(self)
        constraint.setConstrainee(self)

        return True

    def removeConstraintByIndex(self, index):
        """Removes constraint at specified index.

        Args:
            index (int): Index of constraint to remove.

        Returns:
            bool: True if successful.

        """

        if self.checkConstraintIndex(index) is not True:
            return False

        del self._constraints[index]

        return True

    def removeConstraintByName(self, name):
        """Removes the constraint with the specified name.

        Args:
            name (str): Name of the constraint to remove.

        Returns:
            bool: True if successful.

        """

        removeIndex = None

        for i, eachConstraint in enumerate(self._constraints):
            if eachConstraint.getName() == name:
                removeIndex = i

        if removeIndex is None:
            return False

        self.removeConstraintByIndex(removeIndex)

        return True

    def removeAllConstraints(self):
        """Removes all of the constraints for this object.

        Returns:
            bool: True if successful.

        """

        del self._constraints[:]

        return True

    def getNumConstraints(self):
        """Returns the number of constraints as an integer.

        Returns:
            int: Number of constraints on this object.

        """

        return len(self._constraints)

    def getConstraintByIndex(self, index):
        """Returns the constraint at the specified index.

        Args:
            index (int): Index of the constraint to return.

        Returns:
            Constraint: Constraint at the specified index.

        """

        if self.checkConstraintIndex(index) is not True:
            return False

        return self._constraints[index]

    def getConstraintByName(self, name):
        """Return the constraint group with the specified name.

        Args:
            name (str): Name of the constraint group to return.

        Returns:
            Attribute: Attribute with the specified name.

        """

        for eachConstraint in self._constraints:
            if eachConstraint.getName() == name:
                return eachConstraint

        return None

    # ===================
    # Visibility Methods
    # ===================
    def getVisibilityAttr(self):
        """Returns the Visibility attribute object.

        Returns:
            BoolAttribute: Attribute that holds the value of the visibility.

        """

        return self._visibility

    def getVisibility(self):
        """Returns the visibility status of the scene item.

        Returns:
            bool: Visible or not.

        """

        return self._visibility.getValue()

    def setVisibility(self, value):
        """Sets the visibility of the scene object.

        Args:
            value (bool): value of the visibility of the object.

        Returns:
            bool: True if successful.

        """

        self._visibility.setValue(value)

        return True

    def getShapeVisibilityAttr(self):
        """Returns the Shape Visibility attribute object.

        Returns:
            BoolAttribute: Attribute that holds the value of the shape
                visibility.

        """

        return self._shapeVisibility

    def getShapeVisibility(self):
        """Returns the shape visibility status of the scene item.

        Returns:
            bool: Visible or not.

        """

        return self._shapeVisibility.getValue()

    def setShapeVisibility(self, value):
        """Sets the shape visibility of the scene object.

        Args:
            value (bool): Value of the visibility of the object.

        Returns:
            bool: True if successful.

        """

        self._shapeVisibility.setValue(value)

        return True

    # ================
    # Display Methods
    # ================
    def setColor(self, color):
        """Sets the color of this object.

        Args:
            color (str): Name of the color you wish to set.

        Returns:
            bool: True if successful.

        """

        self._color = color

        return True

    def getColor(self):
        """Returns the color of the object.

        Returns:
            str: Color of the object.

        """

        return self._color

    # ==========================
    # Parameter Locking Methods
    # ==========================
    def lockRotation(self, x=False, y=False, z=False):
        """Sets flags for locking rotation parameters.

        Args:
            x (bool): Lock x axis.
            y (bool): Lock y axis.
            z (bool): Lock z axis.

        Returns:
            bool: True if successful.

        """

        if x is True:
            self.setFlag("lockXRotation")

        if y is True:
            self.setFlag("lockYRotation")

        if z is True:
            self.setFlag("lockZRotation")

        return True

    def lockScale(self, x=False, y=False, z=False):
        """Sets flags for locking scale parameters.

        Args:
            x (bool): Lock x axis.
            y (bool): Lock y axis.
            z (bool): Lock z axis.

        Returns:
            bool: True if successful.

        """

        if x is True:
            self.setFlag("lockXScale")

        if y is True:
            self.setFlag("lockYScale")

        if z is True:
            self.setFlag("lockZScale")

        return True

    def lockTranslation(self, x=False, y=False, z=False):
        """Sets flags for locking translation parameters.

        Args:
            x (bool): Lock x axis.
            y (bool): Lock x axis.
            z (bool): Lock x axis.

        Returns:
            bool: True if successful.

        """

        if x is True:
            self.setFlag("lockXTranslation")

        if y is True:
            self.setFlag("lockYTranslation")

        if z is True:
            self.setFlag("lockZTranslation")

        return True

    # ====================
    # Persistence Methods
    # ====================
    def jsonEncode(self, saver):
        """Encodes the object to a JSON structure.

        Args:
            saver (Object): saver object.

        Returns:
            Dict: A JSON structure containing the data for this SceneItem.

        """

        classHierarchy = self.getTypeHierarchyNames()

        jsonData = {
            '__typeHierarchy__': classHierarchy,
            'name': self.getName(),
            'parent': None,
            'children': [],
            'flags': self._flags,
            'attributeGroups': [],
            'constraints': [],
            'xfo': self.xfo.jsonEncode(),
            'color': self.getColor(),
            'visibility': self._visibility,
            'shapeVisibility': self._shapeVisibility,
        }

        if self.getParent() is not None:
            jsonData['parent'] = self.getParent().getName()

        if self.getColor() is not None:
            jsonData['color'] = saver.encodeValue(self.getColor())

        for child in self.getChildren():
            jsonData['children'].append(child.jsonEncode(saver))

        for attrGroup in self._attributeGroups:
            jsonData['attributeGroups'].append(attrGroup.jsonEncode(saver))

        for constr in self._constraints:
            jsonData['constraints'].append(constr.jsonEncode(saver))

        return jsonData

    def jsonDecode(self, loader, jsonData):
        """Returns the color of the object..

        Args:
            loader (Object): Loader object.
            jsonData (Dict): JSON object structure.

        Returns:
            bool: True if successful.

        """

        self._flags = jsonData['flags']
        self.xfo = loader.decodeValue(jsonData['xfo'])
        if 'color' in jsonData and jsonData['color'] is not None:
            self.setColor(loader.decodeValue(jsonData['color']))
        self._visibility = jsonData['visibility']
        self._shapeVisibility = jsonData['shapeVisibility']

        for child in jsonData['children']:
            self.addChild(loader.construct(child))

        for attrGroup in jsonData['attributeGroups']:
            # There is one default attribute group assigned to each scene item.
            # Load data into the existing item instead of constructing a new
            # one.
            if attrGroup['name'] == '':
                loader.registerItem(self._attributeGroups[0])
                self._attributeGroups[0].jsonDecode(loader, attrGroup)
            else:
                self.addAttributeGroup(loader.construct(attrGroup))

        for constr in jsonData['constraints']:
            self.addConstraint(loader.construct(constr))

        return True
예제 #4
0
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.rightSideInputAttr = BoolAttribute('rightSide', value=False, 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)
        self.addItem('defCmpGrp', self.defCmpGrp)

        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)

        upperXfo = data.get('upperXfo')
        upperLen = data.get('upperLen')
        lowerXfo = data.get('lowerXfo')
        lowerLen = data.get('lowerLen')
        endXfo = data.get('endXfo')
        upVXfo = data.get('upVXfo')

        self.upperFKCtrlSpace.xfo = upperXfo
        self.upperFKCtrl.xfo = upperXfo
        self.upperFKCtrl.scalePoints(Vec3(upperLen, 1.75, 1.75))

        self.limbUpperOutputTgt.xfo = upperXfo
        self.limbLowerOutputTgt.xfo = lowerXfo

        self.lowerFKCtrlSpace.xfo = lowerXfo
        self.lowerFKCtrl.xfo = lowerXfo
        self.lowerFKCtrl.scalePoints(Vec3(lowerLen, 1.5, 1.5))

        self.limbIKCtrlSpace.xfo.tr = endXfo.tr
        self.limbIKCtrl.xfo.tr = 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 = upVXfo
        self.limbUpVCtrl.xfo = upVXfo

        self.limbBone0LenInputAttr.setMin(0.0)
        self.limbBone0LenInputAttr.setMax(upperLen * 3.0)
        self.limbBone0LenInputAttr.setValue(upperLen)
        self.limbBone1LenInputAttr.setMin(0.0)
        self.limbBone1LenInputAttr.setMax(lowerLen * 3.0)
        self.limbBone1LenInputAttr.setValue(lowerLen)

        self.limbParentInputTgt.xfo = upperXfo

        # Set Attrs
        self.rightSideInputAttr.setValue(self.getLocation() is 'R')

        # Eval Constraints
        self.limbIKCtrlSpaceInputConstraint.evaluate()
        self.limbUpVCtrlSpaceInputConstraint.evaluate()
        self.limbRootInputConstraint.evaluate()

        # Eval Operators
        self.limbIKKLOp.evaluate()
        self.outputsToDeformersKLOp.evaluate()
예제 #5
0
class FootComponentRig(FootComponent):
    """Foot Component"""

    def __init__(self, name="foot", parent=None):

        Profiler.getInstance().push("Construct Neck Rig Component:" + name)
        super(FootComponentRig, self).__init__(name, parent)


        # =========
        # Controls
        # =========
        self.ankleLenInputAttr = ScalarAttribute('ankleLen', 1.0, maxValue=1.0, parent=self.cmpInputAttrGrp)
        self.toeLenInputAttr = ScalarAttribute('toeLen', 1.0, maxValue=1.0, parent=self.cmpInputAttrGrp)
        self.rightSideInputAttr = BoolAttribute('rightSide', False, parent=self.cmpInputAttrGrp)

        self.footAll = Locator('footAll', parent=self.ctrlCmpGrp)
        self.footAll.setShapeVisibility(False)

        self.ankleIKCtrlSpace = CtrlSpace('ankleIK', parent=self.footAll)
        self.ankleIKCtrl = Control('ankleIK', parent=self.ankleIKCtrlSpace, shape="square")
        self.ankleIKCtrl.alignOnXAxis(negative=True)
        self.ankleIKCtrl.lockTranslation(True, True, True)
        self.ankleIKCtrl.lockScale(True, True, True)

        self.toeIKCtrlSpace = CtrlSpace('toeIK', parent=self.footAll)
        self.toeIKCtrl = Control('toeIK', parent=self.toeIKCtrlSpace, shape="square")
        self.toeIKCtrl.alignOnXAxis()
        self.toeIKCtrl.lockTranslation(True, True, True)
        self.toeIKCtrl.lockScale(True, True, True)

        self.ankleFKCtrlSpace = CtrlSpace('ankleFK', parent=self.ctrlCmpGrp)
        self.ankleFKCtrl = Control('ankleFK', parent=self.ankleFKCtrlSpace, shape="cube")
        self.ankleFKCtrl.alignOnXAxis()
        self.ankleFKCtrl.lockTranslation(True, True, True)
        self.ankleFKCtrl.lockScale(True, True, True)

        self.toeFKCtrlSpace = CtrlSpace('toeFK', parent=self.ankleFKCtrl)
        self.toeFKCtrl = Control('toeFK', parent=self.toeFKCtrlSpace, shape="cube")
        self.toeFKCtrl.alignOnXAxis()
        self.toeFKCtrl.lockTranslation(True, True, True)
        self.toeFKCtrl.lockScale(True, True, True)

        self.footSettingsAttrGrp = AttributeGroup("DisplayInfo_FootSettings", parent=self.ankleIKCtrl)
        self.footDebugInputAttr = BoolAttribute('drawDebug', value=False, parent=self.footSettingsAttrGrp)
        self.footRockInputAttr = ScalarAttribute('footRock', value=0.0, minValue=-1.0, maxValue=1.0, parent=self.footSettingsAttrGrp)
        self.footBankInputAttr = ScalarAttribute('footBank', value=0.0, minValue=-1.0, maxValue=1.0, parent=self.footSettingsAttrGrp)

        self.drawDebugInputAttr.connect(self.footDebugInputAttr)

        self.pivotAll = Locator('pivotAll', parent=self.ctrlCmpGrp)
        self.pivotAll.setShapeVisibility(False)

        self.backPivotCtrl = Control('backPivot', parent=self.pivotAll, shape="axesHalfTarget")
        self.backPivotCtrl.scalePoints(Vec3(0.5, 0.5, 0.5))
        self.backPivotCtrl.lockScale(True, True, True)
        self.backPivotCtrlSpace = self.backPivotCtrl.insertCtrlSpace()

        self.frontPivotCtrl = Control('frontPivot', parent=self.pivotAll, shape="axesHalfTarget")
        self.frontPivotCtrl.rotatePoints(0.0, 180.0, 0.0)
        self.frontPivotCtrl.lockScale(True, True, True)
        self.frontPivotCtrlSpace = self.frontPivotCtrl.insertCtrlSpace()
        self.frontPivotCtrl.scalePoints(Vec3(0.5, 0.5, 0.5))

        self.outerPivotCtrl = Control('outerPivot', parent=self.pivotAll, shape="axesHalfTarget")
        self.outerPivotCtrl.rotatePoints(0.0, -90.0, 0.0)
        self.outerPivotCtrl.lockScale(True, True, True)
        self.outerPivotCtrlSpace = self.outerPivotCtrl.insertCtrlSpace()
        self.outerPivotCtrl.scalePoints(Vec3(0.5, 0.5, 0.5))

        self.innerPivotCtrl = Control('innerPivot', parent=self.pivotAll, shape="axesHalfTarget")
        self.innerPivotCtrl.rotatePoints(0.0, 90.0, 0.0)
        self.innerPivotCtrl.lockScale(True, True, True)
        self.innerPivotCtrlSpace = self.innerPivotCtrl.insertCtrlSpace()
        self.innerPivotCtrl.scalePoints(Vec3(0.5, 0.5, 0.5))


        # ==========
        # Deformers
        # ==========
        deformersLayer = self.getOrCreateLayer('deformers')
        self.defCmpGrp = ComponentGroup(self.getName(), self, parent=deformersLayer)
        self.addItem('defCmpGrp', self.defCmpGrp)

        self.ankleDef = Joint('ankle', parent=self.defCmpGrp)
        self.ankleDef.setComponent(self)

        self.toeDef = Joint('toe', parent=self.defCmpGrp)
        self.toeDef.setComponent(self)


        # ==============
        # Constrain I/O
        # ==============
        # Constraint to inputs
        self.pivotAllInputConstraint = PoseConstraint('_'.join([self.pivotAll.getName(), 'To', self.ikHandleInputTgt.getName()]))
        self.pivotAllInputConstraint.setMaintainOffset(True)
        self.pivotAllInputConstraint.addConstrainer(self.ikHandleInputTgt)
        self.pivotAll.addConstraint(self.pivotAllInputConstraint)

        self.ankleFKInputConstraint = PoseConstraint('_'.join([self.ankleFKCtrlSpace.getName(), 'To', self.legEndFKInputTgt.getName()]))
        self.ankleFKInputConstraint.setMaintainOffset(True)
        self.ankleFKInputConstraint.addConstrainer(self.legEndFKInputTgt)
        self.ankleFKCtrlSpace.addConstraint(self.ankleFKInputConstraint)

        # Constraint outputs
        self.ikTargetOutputConstraint = PoseConstraint('_'.join([self.ikTargetOutputTgt.getName(), 'To', self.ankleIKCtrl.getName()]))
        self.ikTargetOutputConstraint.setMaintainOffset(True)
        self.ikTargetOutputConstraint.addConstrainer(self.ankleIKCtrl)
        self.ikTargetOutputTgt.addConstraint(self.ikTargetOutputConstraint)


        # =========================
        # Add Foot Pivot Canvas Op
        # =========================
        # self.footPivotCanvasOp = CanvasOperator('footPivotCanvasOp', 'Kraken.Solvers.Biped.BipedFootPivotSolver')
        self.footPivotCanvasOp = KLOperator('footPivotKLOp', 'BipedFootPivotSolver', 'Kraken')

        self.addOperator(self.footPivotCanvasOp)

        # Add Att Inputs
        self.footPivotCanvasOp.setInput('drawDebug', self.drawDebugInputAttr)
        self.footPivotCanvasOp.setInput('rigScale', self.rigScaleInputAttr)
        self.footPivotCanvasOp.setInput('rightSide', self.rightSideInputAttr)
        self.footPivotCanvasOp.setInput('footRock', self.footRockInputAttr)
        self.footPivotCanvasOp.setInput('footBank', self.footBankInputAttr)

        # Add Xfo Inputs
        self.footPivotCanvasOp.setInput('pivotAll', self.pivotAll)
        self.footPivotCanvasOp.setInput('backPivot', self.backPivotCtrl)
        self.footPivotCanvasOp.setInput('frontPivot', self.frontPivotCtrl)
        self.footPivotCanvasOp.setInput('outerPivot', self.outerPivotCtrl)
        self.footPivotCanvasOp.setInput('innerPivot', self.innerPivotCtrl)

        # Add Xfo Outputs
        self.footPivotCanvasOp.setOutput('result', self.footAll)


        # =========================
        # Add Foot Solver Canvas Op
        # =========================
        # self.footSolverCanvasOp = CanvasOperator('footSolverCanvasOp', 'Kraken.Solvers.Biped.BipedFootSolver')
        self.footSolverCanvasOp = KLOperator('footSolverKLOp', 'BipedFootSolver', 'Kraken')
        self.addOperator(self.footSolverCanvasOp)

        # Add Att Inputs
        self.footSolverCanvasOp.setInput('drawDebug', self.drawDebugInputAttr)
        self.footSolverCanvasOp.setInput('rigScale', self.rigScaleInputAttr)
        self.footSolverCanvasOp.setInput('ikBlend', self.ikBlendInputAttr)
        self.footSolverCanvasOp.setInput('ankleLen', self.ankleLenInputAttr)
        self.footSolverCanvasOp.setInput('toeLen', self.toeLenInputAttr)

        # Add Xfo Inputs
        self.footSolverCanvasOp.setInput('legEnd', self.legEndInputTgt)
        self.footSolverCanvasOp.setInput('ankleIK', self.ankleIKCtrl)
        self.footSolverCanvasOp.setInput('toeIK', self.toeIKCtrl)
        self.footSolverCanvasOp.setInput('ankleFK', self.ankleFKCtrl)
        self.footSolverCanvasOp.setInput('toeFK', self.toeFKCtrl)

        # Add Xfo Outputs
        self.footSolverCanvasOp.setOutput('ankle_result', self.ankleOutputTgt)
        self.footSolverCanvasOp.setOutput('toe_result', self.toeOutputTgt)


        # ===================
        # Add Deformer KL Op
        # ===================
        self.outputsToDeformersKLOp = KLOperator('foot' + self.getLocation() + 'DeformerKLOp', '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.ankleOutputTgt, self.toeOutputTgt])

        # Add Xfo Outputs
        self.outputsToDeformersKLOp.setOutput('constrainees', [self.ankleDef, self.toeDef])

        Profiler.getInstance().pop()


    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(FootComponentRig, self).loadData( data )

        footXfo = data.get('footXfo')
        ankleXfo = data.get('ankleXfo')
        toeXfo = data.get('toeXfo')
        ankleFKXfo = data.get('ankleFKXfo')
        toeFKXfo = data.get('toeFKXfo')
        ankleLen = data.get('ankleLen')
        toeLen = data.get('toeLen')
        backPivotXfo = data.get('backPivotXfo')
        frontPivotXfo = data.get('frontPivotXfo')
        outerPivotXfo = data.get('outerPivotXfo')
        innerPivotXfo = data.get('innerPivotXfo')

        self.footAll.xfo = footXfo
        self.ankleIKCtrlSpace.xfo = ankleXfo
        self.ankleIKCtrl.xfo = ankleXfo
        self.toeIKCtrlSpace.xfo = toeXfo
        self.toeIKCtrl.xfo = toeXfo

        self.ankleFKCtrl.scalePoints(Vec3(ankleLen, 1.0, 1.0))
        self.toeFKCtrl.scalePoints(Vec3(toeLen, 1.0, 1.0))

        self.ankleFKCtrlSpace.xfo.tr = footXfo.tr
        self.ankleFKCtrlSpace.xfo.ori = ankleFKXfo.ori
        self.ankleFKCtrl.xfo.tr = footXfo.tr
        self.ankleFKCtrl.xfo.ori = ankleFKXfo.ori
        self.toeFKCtrlSpace.xfo = toeFKXfo
        self.toeFKCtrl.xfo = toeFKXfo

        self.pivotAll.xfo = footXfo
        self.backPivotCtrlSpace.xfo = backPivotXfo
        self.backPivotCtrl.xfo = backPivotXfo
        self.frontPivotCtrlSpace.xfo = frontPivotXfo
        self.frontPivotCtrl.xfo = frontPivotXfo
        self.outerPivotCtrlSpace.xfo = outerPivotXfo
        self.outerPivotCtrl.xfo = outerPivotXfo
        self.innerPivotCtrlSpace.xfo = innerPivotXfo
        self.innerPivotCtrl.xfo = innerPivotXfo

        if self.getLocation() == 'R':
            self.outerPivotCtrl.rotatePoints(0.0, 180.0, 0.0)
            self.innerPivotCtrl.rotatePoints(0.0, 180.0, 0.0)

        self.ankleIKCtrl.scalePoints(Vec3(ankleLen, 1.0, 1.5))
        self.toeIKCtrl.scalePoints(Vec3(toeLen, 1.0, 1.5))

        # Set Attribute Values
        self.rightSideInputAttr.setValue(self.getLocation() is 'R')
        self.ankleLenInputAttr.setValue(ankleLen)
        self.ankleLenInputAttr.setMax(ankleLen * 3.0)
        self.toeLenInputAttr.setValue(toeLen)
        self.toeLenInputAttr.setMax(toeLen * 3.0)

        # Set IO Xfos
        self.ikHandleInputTgt.xfo = footXfo
        self.legEndInputTgt.xfo.tr = footXfo.tr
        self.legEndInputTgt.xfo.ori = ankleXfo.ori
        self.legEndFKInputTgt.xfo.tr = footXfo.tr
        self.legEndFKInputTgt.xfo.ori = ankleXfo.ori

        self.ikTargetOutputTgt.xfo.tr = footXfo.tr
        self.ikTargetOutputTgt.xfo.ori = ankleXfo.ori

        # Eval Canvas Ops
        self.footPivotCanvasOp.evaluate()
        self.footSolverCanvasOp.evaluate()

        # Eval Constraints
        self.ikTargetOutputConstraint.evaluate()
        self.ankleFKInputConstraint.evaluate()
예제 #6
0
class Object3D(SceneItem):
    """Kraken base object type for any 3D object."""
    def __init__(self, name, parent=None, flags=None, metaData=None):
        super(Object3D, self).__init__(name, parent=parent, metaData=metaData)
        self._children = []
        self._flags = {}
        self._attributeGroups = []
        self._constraints = []
        self._xfo = Xfo()
        self._ro = RotationOrder()
        self._color = None

        self._implicitAttrGrp = AttributeGroup("implicitAttrGrp", self)
        self._visibility = BoolAttribute('visibility', True,
                                         self._implicitAttrGrp)

        self._shapeVisibility = BoolAttribute('ShapeVisibility', True,
                                              self._implicitAttrGrp)

        if parent is not None:
            parent.addChild(self)

        if flags is not None:
            assert type(
                flags
            ) is str, "Flags argument must be a comma separated string."

            for flag in flags.replace(' ', '').split(','):
                if not re.match("[\w]*$", flag):
                    msg = "{} '{}' {} ({}: {}) {}\n".format(
                        "Invalid flag", flag, "set on", self.getName(),
                        self.getPath(), ". Alphanumeric and underscores only!")
                    logger.warn(msg)
                    continue

                self.setFlag(flag)

    # ==================
    # Property Methods
    # ==================
    @property
    def xfo(self):
        """Gets xfo property of this Object3D.

        Returns:
            Xfo: Xfo property of this Object3D.

        """

        return self._xfo

    @xfo.setter
    def xfo(self, value):
        """Sets xfo of this Object3D.

        Note:
            In Python, objects are always referenced, meaning to get a unique
            instance, an explicit clone is required. In KL, structs are passed
            by value, meaning that every assignment of a struct causes a clone.

            This means that in KL it is impossible for 2 objects to reference
            the same KL math object. This is an important performance feature
            of KL.

            The members of the KL Math objects have this property. 2 Xfos
            cannot share the same tr value. Here we implcitly clone the math
            object to ensure the same behavior as in KL.

        Args:
            value (Xfo): Vector to set the xfo by.

        Returns:
            bool: True if successful.

        """

        self._xfo = value.clone()

        return True

    @property
    def ro(self):
        """Gets Rotation Order property of this Object3D.

        Returns:
            RotationOrder: Rotation Order property of this Object3D.

        """

        return self._ro

    @ro.setter
    def ro(self, value):
        """Sets Rotation Order of this Object3D.

        Note:
            In Python, objects are always referenced, meaning to get a unique
            instance, an explicit clone is required. In KL, structs are passed
            by value, meaning that every assignment of a struct causes a clone.

            This means that in KL it is impossible for 2 objects to reference
            the same KL math object. This is an important performance feature
            of KL.

            The members of the KL Math objects have this property. 2 Xfos
            cannot share the same tr value. Here we implcitly clone the math
            object to ensure the same behavior as in KL.

        Args:
            value (RotationOrder): New rotation order.

        Returns:
            bool: True if successful.

        """

        self._ro = value.clone()

        return True

    @property
    def localXfo(self):
        """Gets local transform of this Object3D

        Returns:
            Xfo: Local Xfo of the object.

        """

        globalXfo = self.globalXfo

        parent = self.getParent()
        if not isinstance(parent, SceneItem):
            return globalXfo

        parentXfo = parent.globalXfo

        return parentXfo.inverse().multiply(globalXfo)

    @property
    def globalXfo(self):
        """Gets global transform of this Object3D

        Returns:
            Xfo: Global Xfo

        """

        for source in self.getSources():
            if isinstance(source, Object3D):
                continue
            if isinstance(source, Constraint):
                return source.compute()
            if isinstance(source, Operator):
                source.evaluate()
                break

        return self._xfo

    # =============
    # Name Methods
    # =============
    def getBuildName(self):
        """Returns the build name for the object.

        Returns:
            str: Name to be used in the DCC.

        """

        typeNameHierarchy = self.getTypeHierarchyNames()

        config = Config.getInstance()

        # If flag is set on object to use explicit name, return it.
        if config.getExplicitNaming() is True or \
                self.testFlag('EXPLICIT_NAME'):
            return self.getName()

        nameTemplate = config.getNameTemplate()

        # Get the token list for this type of object
        format = None
        for typeName in nameTemplate['formats'].keys():
            if typeName in typeNameHierarchy:
                format = nameTemplate['formats'][typeName]
                break

        if format is None:
            format = nameTemplate['formats']['default']

        objectType = None
        for eachType in typeNameHierarchy:
            if eachType in nameTemplate['types'].keys():
                objectType = eachType
                break

        if objectType is None:
            objectType = 'default'

        # Generate a name by concatenating the resolved tokens together.
        builtName = ""
        skipSep = False
        for token in format:

            if token is 'sep':
                if not skipSep:
                    builtName += nameTemplate['separator']

            elif token is 'location':
                if self.isTypeOf('Component'):
                    location = self.getLocation()
                else:
                    component = self.getComponent()
                    if component is None:
                        raise ValueError(
                            "object [%s] does not have a component." %
                            self.getName())
                    location = component.getLocation()

                altLocation = self.getMetaDataItem("altLocation")
                if altLocation is not None and altLocation in nameTemplate[
                        'locations']:
                    location = altLocation

                if location not in nameTemplate['locations']:
                    msg = "Invalid location on '{}'. Location: {}. Valid locations: {}".format(
                        self.getPath(), location, nameTemplate['locations'])
                    raise ValueError(msg)

                builtName += location

            elif token is 'type':

                if objectType == 'Locator' and self.testFlag('inputObject'):
                    objectType = 'ComponentInput'
                elif objectType == 'Locator' and self.testFlag('outputObject'):
                    objectType = 'ComponentOutput'

                altType = self.getMetaDataItem("altType")
                if altType is not None and nameTemplate['types'].get(
                        altType, None) is not None:
                    objectType = altType

                builtName += nameTemplate['types'][objectType]

            elif token is 'name':
                builtName += self.getName()

            elif token is 'component':
                if self.getComponent() is None:
                    skipSep = True
                    continue
                builtName += self.getComponent().getName()

            elif token is 'container':
                if self.getContainer() is None:
                    skipSep = True
                    continue
                builtName += self.getContainer().getName()

            else:
                raise ValueError("Unresolvabled token '" + token +
                                 "' used on: " + self.getPath())

        return builtName

    def setName(self, name):
        """Sets the name of the object with a string.

        Args:
            name (str): The new name for the item.

        Returns:
            bool: True if successful.

        """

        # check for name collision and adjust the name if they exist
        if self.getParent() is not None:
            # Increment name if it already exists
            initName = name
            suffix = 1
            collision = True
            while collision:
                child = self.getParent().getChildByDecoratedName(
                    name + self.getNameDecoration())
                collision = child is not None and child is not self
                if not collision:
                    break

                result = re.split(r"(\d+)$", initName, 1)
                if len(result) > 1:
                    initName = result[0]
                    suffix = int(result[1])

                name = initName + str(suffix).zfill(2)
                suffix += 1

        super(Object3D, self).setName(name)

        return True

    # ==================
    # Hierarchy Methods
    # ==================
    def getContainer(self):
        """Returns the Container the object belongs to.

        Returns:
            Object: Container.

        """

        parent = self.getParent()
        while (parent is not None
               and 'Container' not in parent.getTypeHierarchyNames()):
            parent = parent.getParent()

        return parent

    def getLayer(self):
        """Returns the Layer the object belongs to.

        Returns:
            Object: Layer this object belongs to.

        """

        parent = self.getParent()
        while (parent is not None and not parent.isTypeOf('Layer')):
            parent = parent.getParent()

        return parent

    # ==============
    # Child Methods
    # ==============
    def hasChild(self, child):
        """Checks the supplied item is a child

        Args:
            child (Object): Object to check if is is a child of this object.

        """

        for i, eachChild in enumerate(self.getChildren()):
            if eachChild == child:
                return True

        return False

    def _checkChildIndex(self, index):
        """Checks the supplied index is valid.

        Args:
            index (int): Child index to check.

        """

        if index > len(self.getChildren()):
            raise IndexError("'" + str(index) +
                             "' is out of the range of the 'children' array.")

        return True

    def addChild(self, child):
        """Adds a child to this object.

        Note:
            We allow for duplicate child names as long as the types differ.

        Args:
            child (Object): Object that will be a child of this object.

        Returns:
            bool: True if successful.

        """
        SceneItem.setParent(child, self)

        if child.getParent() is not None:
            parent = child.getParent()
            if child in parent.getChildren():
                parent.getChildren().remove(child)

        child.setName(child.getName())

        self.getChildren().append(child)

        # Assign the child the same component.
        if self._component is not None:
            child.setComponent(self._component)

        return True

    def setParent(self, parent):
        """Sets the parent of this object.

        Arguments:
        parent (Object): Object that is the parent of this one.

        Returns:
            bool: True if successful.

        """
        if parent:
            parent.addChild(self)
        else:
            if self._parent is not None:
                parent.removeChild(self)
            SceneItem.setParent(self, None)

        return True

    def removeChildByIndex(self, index):
        """Removes a child from this object by index.

        Args:
            index (int): Index of child to remove.

        Returns:
            bool: True if successful.

        """

        if self._checkChildIndex(index) is not True:
            return False

        self.removeChild(self.getChildren()[index])

        return True

    def removeChildByName(self, name):
        """Removes a child from this object by name.

        Args:
            name (str): Name of child to remove.

        Returns:
            bool: True if successful.

        """

        removeIndex = None

        for i, eachChild in enumerate(self.getChildren()):
            if eachChild.getName() == name:
                removeIndex = i

        if removeIndex is None:
            raise ValueError("'" + name +
                             "' is not a valid child of this object.")

        self.removeChildByIndex(removeIndex)

        return True

    def removeChild(self, child):
        """Removed the child as an child item of this object.

        Returns:
            bool: True if successful.

        """

        try:
            self._children.remove(child)
        except:
            names = []
            for c in self._children:
                names.append(c.getName())
            raise Exception("Object '" + self.getPath() +
                            "' does not have child:" + child.getPath() +
                            ". it does have:" + str(names))

        SceneItem.setParent(child, None)

        # Un-assign the child the component.
        if self._component is not None:
            child.setComponent(None)

        return True

    def getDescendents(self,
                       nodeList=None,
                       classType=None,
                       inheritedClass=False):
        """Gets the children of this object.

        Args:
            nodeList: (list): optional list to append children to
            classType (str): Name of the type of class to limit the search to
            inheritedClass (bool): Match nodes that is a sub-class of type.

        Returns:
            list: Child objects.

        """

        if nodeList is None:
            nodeList = []

        for child in self._children:
            if classType is not None:
                if inheritedClass is not None and child.isTypeOf(classType):
                    nodeList.append(child)
                elif child.getTypeName() == classType:
                    nodeList.append(child)

            else:
                nodeList.append(child)

            child.getDescendents(classType=classType,
                                 nodeList=nodeList,
                                 inheritedClass=inheritedClass)

        return nodeList

    def getChildren(self):
        """Gets the children of this object.

        Returns:
            list: Child objects.

        """

        return self._children

    def getNumChildren(self):
        """Returns the number of children this object has.

        Returns:
            int: Number of children of this object.

        """

        return len(self.getChildren())

    def getChildByIndex(self, index):
        """Returns the child object at specified index.

        Args:
            index (int): Index of the child to find.

        Returns:
            Object: Child object at specified index.

        """

        if self._checkChildIndex(index) is not True:
            return False

        return self.getChildren()[index]

    def getChildByName(self, name):
        """Returns the child object with the specified name.

        Args:
            name (str): Name of the child to return.

        Returns:
            Object: Object if found.

        """

        for eachChild in self.getChildren():
            if eachChild.getName() == name:
                return eachChild

        return None

    def getChildByDecoratedName(self, decoratedName):
        """Returns the child object with the specified name.

        Args:
            decoratedName (str): Decorated name of the child to find.

        Returns:
            Object: Object if found.

        """

        for eachChild in self.getChildren():
            if eachChild.getDecoratedName() == decoratedName:
                return eachChild

        return None

    def getChildrenByType(self, childType):
        """Returns all children that are of the specified type.

        Args:
            childType (str): Type of children to find.

        Returns:
            list: Array of child objects of the specified type.

        """

        childrenOfType = []
        for eachChild in self.getChildren():
            if eachChild.isTypeOf(childType):
                childrenOfType.append(eachChild)

        return childrenOfType

    # ========================
    # Attribute Group Methods
    # ========================
    def _checkAttributeGroupIndex(self, index):
        """Checks the supplied index is valid.

        Args:
            index (int): Attribute index to check.

        Returns:
            bool: True if successful.

        """

        if index > len(self._attributeGroups):
            raise IndexError(
                "'" + str(index) +
                "' is out of the range of 'attributeGroups' array.")

        return True

    def addAttributeGroup(self, attributeGroup):
        """Adds an attributeGroup to this object.

        Args:
            attributeGroup (Object): Attribute Group object to add to this
                object.

        Returns:
            bool: True if successful.

        """

        if attributeGroup.getName() in [
                x.getName() for x in self._attributeGroups
        ]:
            raise IndexError("Child with " + attributeGroup.getName() +
                             " already exists as a attributeGroup.")

        self._attributeGroups.append(attributeGroup)
        attributeGroup.setParent(self)

        return True

    def removeAttributeGroupByIndex(self, index):
        """Removes attribute at specified index.

        Args:
            index (int): Index of attribute to remove.

        Returns:
            bool: True if successful.

        """

        if self._checkAttributeGroupIndex(index) is not True:
            return False

        del self._attributeGroups[index]

        return True

    def removeAttributeGroupByName(self, name):
        """Removes the attribute with the specified name.

        Args:
            name (str): Name of the attribute to remove.

        Returns:
            bool: True if successful.

        """

        removeIndex = None

        for i, eachAttributeGroup in enumerate(self._attributeGroups):
            if eachAttributeGroup.getName() == name:
                removeIndex = i

        if removeIndex is None:
            return False

        self.removeAttributeGroupByIndex(removeIndex)

        return True

    def getNumAttributeGroups(self):
        """Returns the number of attributeGroups as an integer.

        Returns:
            int: Number of attributeGroups on this object.

        """

        return len(self._attributeGroups)

    def getAttributeGroupByIndex(self, index):
        """Returns the attribute at the specified index.

        Args:
            index (int): Index of the attribute to return.

        Returns:
            AttributeGroup: Attribute Group at the specified index.

        """

        if self._checkAttributeGroupIndex(index) is not True:
            return False

        return self._attributeGroups[index]

    def getAttributeGroupByName(self, name):
        """Return the attribute group with the specified name.

        Args:
            name (str): Name of the attribute group to return.

        Returns:
            Attribute: Attribute with the specified name.

        """

        for eachAttributeGroup in self._attributeGroups:
            if eachAttributeGroup.getName() == name:
                return eachAttributeGroup

        return None

    # ===================
    # Constraint Methods
    # ===================
    def checkConstraintIndex(self, index):
        """Checks the supplied index is valid.

        Args:
            index (int): Constraint index to check.

        Returns:
            bool: True if successful.

        """

        if index > len(self._constraints):
            raise IndexError("'" + str(index) +
                             "' is out of the range of 'constraints' array.")

        return True

    def constrainTo(self,
                    constrainers,
                    constraintType="Pose",
                    maintainOffset=False,
                    name=None):
        """Adds an constraint to this object.

        Args:
            constrainers (Object or Object list): Constraint object to add to
                this object or objects.
            constraintType (str): String name of the constraint type.
            maintainOffset (bool): Sets the constraint to maintain offset when
                creating the constraint.
            name (str): Name of the constraint. If set to None, a name is
                automatically generated.

        Returns:
            string: Constraint object

        """

        if name is None:
            constraintName = ""
            if hasattr(constrainers, '__iter__'):
                constraintName = '_'.join([
                    self.getName(), 'To', constrainers[0].getName(),
                    constraintType + 'Constraint'
                ])
            else:
                constraintName = '_'.join([
                    self.getName(), 'To',
                    constrainers.getName(), constraintType + 'Constraint'
                ])
        else:
            constraintName = name

        constraint = None
        if constraintType == "Orientation":
            constraint = OrientationConstraint(constraintName)
        elif constraintType == "Pose":
            constraint = PoseConstraint(constraintName)
        elif constraintType == "Position":
            constraint = PositionConstraint(constraintName)
        elif constraintType == "Scale":
            constraint = ScaleConstraint(constraintName)
        else:
            raise ValueError(
                "'" + constraintType +
                "' is not a valid constraint type. Valid types are Orientation, Pose, Position, or Scale"
            )

        # Accept a single object or a list of objects
        if hasattr(constrainers, '__iter__'):
            pass
        else:
            constrainers = [constrainers]

        for constrainer in constrainers:
            constraint.addConstrainer(constrainer)

        constraint.setMaintainOffset(maintainOffset)

        self.addConstraint(constraint)

        return constraint

    def addConstraint(self, constraint):
        """Adds an constraint to this object.

        Args:
            constraint (Object): Constraint object to add to this object.

        Returns:
            bool: True if successful.

        """

        if constraint.getName() in [x.getName() for x in self._constraints]:
            raise IndexError("Constraint with name '" + constraint.getName() +
                             "'' already exists as a constraint.")

        for x in self._constraints:
            if x.isTypeOf(constraint.getTypeName()):
                raise IndexError("Constraint with type '" +
                                 constraint.getTypeName() +
                                 "'' already exists on object.")

        self._constraints.append(constraint)

        constraint.setParent(self)
        constraint.setConstrainee(self)

        return True

    def removeConstraintByIndex(self, index):
        """Removes constraint at specified index.

        Args:
            index (int): Index of constraint to remove.

        Returns:
            bool: True if successful.

        """

        if self.checkConstraintIndex(index) is not True:
            return False

        sourceIndex = self._sources.index(self._constraints[index])
        del self._sources[sourceIndex]
        del self._constraints[index]

        return True

    def removeConstraintByName(self, name):
        """Removes the constraint with the specified name.

        Args:
            name (str): Name of the constraint to remove.

        Returns:
            bool: True if successful.

        """

        removeIndex = None

        for i, eachConstraint in enumerate(self._constraints):
            if eachConstraint.getName() == name:
                removeIndex = i

        if removeIndex is None:
            return False

        self.removeConstraintByIndex(removeIndex)

        return True

    def removeAllConstraints(self):
        """Removes all of the constraints for this object.

        Returns:
            bool: True if successful.

        """

        while len(self._constraints) > 0:
            self.removeConstraintByIndex(0)

        return True

    def getNumConstraints(self):
        """Returns the number of constraints as an integer.

        Returns:
            int: Number of constraints on this object.

        """

        return len(self._constraints)

    def getConstraintByIndex(self, index):
        """Returns the constraint at the specified index.

        Args:
            index (int): Index of the constraint to return.

        Returns:
            Constraint: Constraint at the specified index.

        """

        if self.checkConstraintIndex(index) is not True:
            return False

        return self._constraints[index]

    def getConstraintByName(self, name):
        """Return the constraint group with the specified name.

        Args:
            name (str): Name of the constraint group to return.

        Returns:
            Attribute: Attribute with the specified name.

        """

        for eachConstraint in self._constraints:
            if eachConstraint.getName() == name:
                return eachConstraint

        return None

    # ===================
    # Visibility Methods
    # ===================
    def getVisibilityAttr(self):
        """Returns the Visibility attribute object.

        Returns:
            BoolAttribute: Attribute that holds the value of the visibility.

        """

        return self._visibility

    def getVisibility(self):
        """Returns the visibility status of the scene item.

        Returns:
            bool: Visible or not.

        """

        return self._visibility.getValue()

    def setVisibility(self, value):
        """Sets the visibility of the scene object.

        Args:
            value (bool): value of the visibility of the object.

        Returns:
            bool: True if successful.

        """

        self._visibility.setValue(value)

        return True

    def getShapeVisibilityAttr(self):
        """Returns the Shape Visibility attribute object.

        Returns:
            BoolAttribute: Attribute that holds the value of the shape
                visibility.

        """

        return self._shapeVisibility

    def getShapeVisibility(self):
        """Returns the shape visibility status of the scene item.

        Returns:
            bool: Visible or not.

        """

        return self._shapeVisibility.getValue()

    def setShapeVisibility(self, value):
        """Sets the shape visibility of the scene object.

        Args:
            value (bool): Value of the visibility of the object.

        Returns:
            bool: True if successful.

        """

        self._shapeVisibility.setValue(value)

        return True

    # ================
    # Display Methods
    # ================
    def setColor(self, color):
        """Sets the color of this object.

        Args:
            color (str, Color): Name of the color from the Config or a Color() object.

        Returns:
            bool: True if successful.

        """

        assert type(color).__name__ in ('str', 'Color'), self.getPath() + \
            ".setColor(), 'color' argument type is not of type 'str' or 'Color'."

        self._color = color

        return True

    def getColor(self):
        """Returns the color of the object.

        Returns:
            str: Color of the object.

        """

        return self._color

    # ==========================
    # Parameter Locking Methods
    # ==========================
    def lockRotation(self, x=False, y=False, z=False):
        """Sets flags for locking rotation parameters.

        Args:
            x (bool): Lock x axis.
            y (bool): Lock y axis.
            z (bool): Lock z axis.

        Returns:
            bool: True if successful.

        """

        if x is True:
            self.setFlag("lockXRotation")

        if y is True:
            self.setFlag("lockYRotation")

        if z is True:
            self.setFlag("lockZRotation")

        return True

    def lockScale(self, x=False, y=False, z=False):
        """Sets flags for locking scale parameters.

        Args:
            x (bool): Lock x axis.
            y (bool): Lock y axis.
            z (bool): Lock z axis.

        Returns:
            bool: True if successful.

        """

        if x is True:
            self.setFlag("lockXScale")

        if y is True:
            self.setFlag("lockYScale")

        if z is True:
            self.setFlag("lockZScale")

        return True

    def lockTranslation(self, x=False, y=False, z=False):
        """Sets flags for locking translation parameters.

        Args:
            x (bool): Lock x axis.
            y (bool): Lock x axis.
            z (bool): Lock x axis.

        Returns:
            bool: True if successful.

        """

        if x is True:
            self.setFlag("lockXTranslation")

        if y is True:
            self.setFlag("lockYTranslation")

        if z is True:
            self.setFlag("lockZTranslation")

        return True

    # ====================
    # Persistence Methods
    # ====================
    def jsonEncode(self, saver):
        """Encodes the object to a JSON structure.

        Args:
            saver (Object): saver object.

        Returns:
            Dict: A JSON structure containing the data for this SceneItem.

        """

        classHierarchy = self.getTypeHierarchyNames()

        jsonData = {
            '__typeHierarchy__': classHierarchy,
            'name': self.getName(),
            'parent': None,
            'children': [],
            'flags': self._flags,
            'attributeGroups': [],
            'constraints': [],
            'xfo': self.xfo.jsonEncode(),
            'color': self.getColor(),
            'visibility': self._visibility,
            'shapeVisibility': self._shapeVisibility,
        }

        if self.getParent() is not None:
            jsonData['parent'] = self.getParent().getName()

        if self.getColor() is not None:
            jsonData['color'] = saver.encodeValue(self.getColor())

        for child in self.getChildren():
            jsonData['children'].append(child.jsonEncode(saver))

        for attrGroup in self._attributeGroups:
            jsonData['attributeGroups'].append(attrGroup.jsonEncode(saver))

        for constr in self._constraints:
            jsonData['constraints'].append(constr.jsonEncode(saver))

        return jsonData

    def jsonDecode(self, loader, jsonData):
        """Returns the color of the object..

        Args:
            loader (Object): Loader object.
            jsonData (Dict): JSON object structure.

        Returns:
            bool: True if successful.

        """

        self._flags = jsonData['flags']
        self.xfo = loader.decodeValue(jsonData['xfo'])
        if 'color' in jsonData and jsonData['color'] is not None:
            self.setColor(loader.decodeValue(jsonData['color']))
        self._visibility = jsonData['visibility']
        self._shapeVisibility = jsonData['shapeVisibility']

        for child in jsonData['children']:
            self.addChild(loader.construct(child))

        for attrGroup in jsonData['attributeGroups']:
            # There is one default attribute group assigned to each scene item.
            # Load data into the existing item instead of constructing a new
            # one.
            if attrGroup['name'] == '':
                loader.registerItem(self._attributeGroups[0])
                self._attributeGroups[0].jsonDecode(loader, attrGroup)
            else:
                self.addAttributeGroup(loader.construct(attrGroup))

        for constr in jsonData['constraints']:
            self.addConstraint(loader.construct(constr))

        return True