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()
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()
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
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()
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()
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