def setOptionVars(self, optionVarDict): '''Only set the optionVars that are non-default. Remove optionVar if value=defaultValue. ''' # update dict from optionVars for k,v in optionVarDict.iteritems(): optVarKey = self.optionVarPrefix+k logger.info(maya.stringTable[ 'y_CommandWithOptionVars.kSettingOptVar' ] %(optVarKey,v)) if self.optionVarDefaults[k] == v: maya.cmds.optionVar(remove=optVarKey) # remove if it exists else: if isinstance(v, (float)): maya.cmds.optionVar(fv=(optVarKey,v)) elif isinstance(v, (int)): maya.cmds.optionVar(iv=(optVarKey,v)) elif isinstance( v, (str,unicode) ): maya.cmds.optionVar(sv=(optVarKey,v)) elif isinstance(v, (list)) and len(v) > 0 and isinstance(v[0], (float)): maya.cmds.optionVar(fv=(optVarKey,v[0])) # explicitly add first value for f in v[1:]: # then append the rest maya.cmds.optionVar(fva=(optVarKey,f)) elif isinstance(v, (list)) and len(v) > 0 and isinstance(v[0], (int)): maya.cmds.optionVar(iv=(optVarKey,v[0])) # explicitly add first value for f in v[1:]: # then append the rest maya.cmds.optionVar(iva=(optVarKey,f)) elif isinstance(v, (list)) and len(v) > 0 and isinstance(v[0], (str,unicode)): maya.cmds.optionVar(sv=(optVarKey,v[0])) # explicitly add first value for f in v[1:]: # then append the rest maya.cmds.optionVar(sva=(optVarKey,f)) else: raise Exception('Unknown type %s for %s'%(type(v), v))
def executeCommandCB(self, miscBool=None): '''Callback to be used by a menuItem. Performs command with the specified optionVar preferences. ''' logger.info(maya.stringTable[ 'y_RigidBody.kGetOptVarValues' ]) optionVarDict = self.getOptionVars() # REVISIT: May want to pass in parameters to the command a different way logger.info(maya.stringTable[ 'y_RigidBody.kExecuteCmd' ]) optionVarDictWithDefaults = self.optionVarDefaults.copy() optionVarDictWithDefaults.update(optionVarDict) # called directly from menu item if miscBool: # if there's only 1 object in select then use its name + 'Set' # otherwise let command determine name name = None list = maya.cmds.ls(sl=True,type='transform') if list and len(list)==1: name = list[0] + 'Set' baseName = name numInstances = 1 while maya.cmds.objExists(name): name = '{0}{1}'.format(baseName,numInstances) numInstances += 1 else: # otherwise use the name stored in the options name = optionVarDictWithDefaults['name'] optionVarDictWithDefaults['name'] = self.uniqueName(name) returnVal = self.command(**optionVarDictWithDefaults) return returnVal
def resetOptionBoxToDefaultsCB(self, miscBool=None): '''Callback for the "Reset" Option Dialog menuitem. Resets the optionVars in the dialog to the Prefs default. Note: Requires OptionBox Dialog to be created. ''' logger.info(maya.stringTable['y_CommandWithOptionVars.kResetDefaults' ]%miscBool) self.setOptionVars(self.optionVarDefaults) self.setWidgetValues(self.optionVarDefaults)
def saveOptionBoxPreferencesCB(self, miscBool=None): '''Callback for the "Save" Option Dialog menuitem. Saves the optionVars from the dialog Note: Requires OptionBox Dialog to be created. ''' logger.info(maya.stringTable['y_CommandWithOptionVars.kRetrieveWidgetValue']) optionVarDict = self.getWidgetValues() logger.info(maya.stringTable['y_CommandWithOptionVars.kSavingOptVars' ]) self.setOptionVars(optionVarDict)
def hideOptionBoxCB(self, miscBool=None): '''Callback for "Close" Option Dialog button Saves the optionVars from the dialog, and hides dialog. Note: Requires OptionBox Dialog to be created. ''' logger.info(maya.stringTable['y_CommandWithOptionVars.kCloseOBox' ]) if (self.optionBox != ''): maya.cmds.control(self.optionBox, edit=True, visibleChangeCommand='') self.saveOptionBoxPreferencesCB() self.optionBoxClosing() maya.mel.eval('hideOptionBox()')
def executeCommandAndHideOptionBoxCB(self, miscBool=None): '''Callback for "Apply and Close" Option Dialog button Saves the optionVars from the dialog, executes the command, and hides dialog. Note: Requires OptionBox Dialog to be created. ''' logger.info(maya.stringTable['y_CommandWithOptionVars.kExecCmdAndCloseOBox' ]) returnVal = self.executeCommandAndSaveCB() if (self.optionBox != ''): maya.cmds.control(self.optionBox, edit=True, visibleChangeCommand='') self.optionBoxClosing() maya.mel.eval('hideOptionBox()') return returnVal
def executeCommandCB(self, miscBool=None): '''Callback to be used by a menuItem. Performs command with the specified optionVar preferences. ''' logger.info(maya.stringTable[ 'y_CommandWithOptionVars.kGetOptVarValues' ]) optionVarDict = self.getOptionVars() # REVISIT: May want to pass in parameters to the command a different way logger.info(maya.stringTable[ 'y_CommandWithOptionVars.kExecuteCmd' ]) optionVarDictWithDefaults = self.optionVarDefaults.copy() optionVarDictWithDefaults.update(optionVarDict) returnVal = self.command(**optionVarDictWithDefaults) return returnVal
def getOptionVars(self): '''Create a dict by retrieving the optionVars, use the default value if optionVar not found. ''' # start with defaults optionVarDict = self.optionVarDefaults.copy() # update dict from optionVars for k,v in optionVarDict.iteritems(): # NOTE: optionVar has the prefix added. The optionVarDict key does not optVarKey = self.optionVarPrefix+k if (maya.cmds.optionVar(exists=optVarKey)): optionVarDict[k] = maya.cmds.optionVar(q=optVarKey) logger.info(maya.stringTable[ 'y_CommandWithOptionVars.kGettingOptVar' ]%(optVarKey,v)) # return dict return optionVarDict
def createOptionDialog(self, optionVarOverrideDict=None, saveOptionVars=True): '''Callback for the MenuItem OptionBox. Create and show the Option Dialog for this command. Supplies the header and footer for the dialog. Calls `addOptionDialogWidgets` to create the widgets. ''' # == Retrieve optionVars == optionVarDict = self.getOptionVars() # override specified values with incoming dict if != None if optionVarOverrideDict != None: optionVarDict.update(optionVarOverrideDict) # == Dialog header == layout = maya.mel.eval('getOptionBox()') maya.cmds.setParent(layout) maya.mel.eval('setOptionBoxCommandName("'+self.commandName+'")') maya.cmds.setUITemplate('DefaultTemplate', pushTemplate=True) maya.cmds.waitCursor(state=True) maya.cmds.tabLayout(tv=False, scr=True) parent = maya.cmds.columnLayout(adjustableColumn=True) # == Dialog attrs == # Add parameters logger.info(maya.stringTable['y_CommandWithOptionVars.kAddWidgets']) self.optionVarToWidgetDict = self.addOptionDialogWidgets() # If nothing returned by the function addOptionDialogWidgets(), # then set it to an empty dict if self.optionVarToWidgetDict == None: logger.warning(maya.stringTable['y_CommandWithOptionVars.kReturnedNone']) self.optionVarToWidgetDict = {} # Create reverse dict (for optionMenuGrp widget) self.optionMenuGrp_enumToLabel = {} for k_labelToEnum, v_labelToEnum in self.optionMenuGrp_labelToEnum.iteritems(): self.optionMenuGrp_enumToLabel[k_labelToEnum] = dict([(v,k) for k,v in v_labelToEnum.iteritems()]) # Verify there is a defaultValue for each widgetKey missingDefaults = set(self.optionVarToWidgetDict.keys()) - set(self.optionVarDefaults.keys()) if len(missingDefaults) > 0: raise ValueError('Missing default optionVar keys: %s'%str(missingDefaults)) omittedWidgetKeys = set(self.optionVarDefaults.keys()) - set(self.optionVarToWidgetDict.keys()) if len(omittedWidgetKeys) > 0: logger.warning(maya.stringTable['y_CommandWithOptionVars.kMissingWidgets']%str(missingDefaults)) # Set Widget Values logger.info(maya.stringTable['y_CommandWithOptionVars.kSetWidgetValues']) # REVISIT: Put a try/catch around this?? self.setWidgetValues(optionVarDict) # == Dialog footer == maya.cmds.waitCursor(state=False) maya.cmds.setUITemplate(popTemplate=True) # * Buttons applyBtn = maya.mel.eval('getOptionBoxApplyBtn()') maya.cmds.button(applyBtn, edit=True, command=self.executeCommandAndSaveCB); # * Titling and Help dlgTitle = self.l10nCommandName + maya.stringTable['y_CommandWithOptionVars.kOptionsTitle' ] maya.mel.eval('setOptionBoxTitle("'+dlgTitle+'")') if not self.commandHelpTag: self.commandHelpTag = '{0}Options'.format(self.commandName) maya.mel.eval('setOptionBoxHelpTag( "{0}" )'.format(self.commandHelpTag)) # == Show OptionBox == maya.mel.eval('showOptionBox()') # == Post show to set the menu items # Reference: performTextureToGeom # Handle Menu items gOptionBoxEditMenuSaveItem = maya.mel.eval('global string $gOptionBoxEditMenuSaveItem; string $bullet_TMPSTR = $gOptionBoxEditMenuSaveItem;') gOptionBoxEditMenuResetItem = maya.mel.eval('global string $gOptionBoxEditMenuResetItem; string $bullet_TMPSTR = $gOptionBoxEditMenuResetItem;') maya.cmds.menuItem(gOptionBoxEditMenuSaveItem, edit=True, command=self.saveOptionBoxPreferencesCB) maya.cmds.menuItem(gOptionBoxEditMenuResetItem, edit=True, command=self.resetOptionBoxToDefaultsCB) maya.cmds.control(layout, edit=True, visibleChangeCommand=self.visibilityChangedCB) # Handle apply and close button here since showOptionBox() does not respect the set command applyAndCloseBtn = maya.mel.eval('getOptionBoxApplyAndCloseBtn()') closeBtn = maya.mel.eval('getOptionBoxCloseBtn()') maya.cmds.button(applyAndCloseBtn, edit=True, command=self.executeCommandAndHideOptionBoxCB) maya.cmds.button(closeBtn, edit=True, command=self.hideOptionBoxCB) # Allow the subclass to make any modifications self.updateOptionBox()
def command( rigidBodyA=None, rigidBodyB=None, parent=None, # Attrs constraintType=None, useReferenceFrame=None, linearDamping=None, linearSoftness=None, linearRestitution=None, angularDamping=None, angularSoftness=None, angularRestitution=None, linearConstraintX=None, linearConstraintY=None, linearConstraintZ=None, linearConstraintMin=None, linearConstraintMax=None, angularConstraintX=None, angularConstraintY=None, angularConstraintZ=None, angularConstraintMin=None, angularConstraintMax=None, linearLimitSoftness=None, linearLimitBias=None, linearLimitRelaxation=None, angularLimitSoftness=None, angularLimitBias=None, angularLimitRelaxation=None, linearMotorEnabled=None, linearMotorTargetSpeed=None, linearMotorMaxForce=None, angularMotorEnabled=None, angularMotorTargetSpeed=None, angularMotorMaxForce=None, linearSpringEnabledX=None, linearSpringEnabledY=None, linearSpringEnabledZ=None, linearSpringStiffness=None, linearSpringDamping=None, angularSpringEnabledX=None, angularSpringEnabledY=None, angularSpringEnabledZ=None, angularSpringStiffness=None, angularSpringDamping=None, breakable=None, breakingThreshold=None, ): logger.debug( maya.stringTable[ 'y_RigidBodyConstraint.kCreatingRBC' ] \ % (rigidBodyA, rigidBodyB) ) # List settable attrs (setAttr below) settableAttrs = [ 'constraintType', 'useReferenceFrame', 'linearDamping', 'linearSoftness', 'linearRestitution', 'angularDamping', 'angularSoftness', 'angularRestitution', 'linearConstraintX', 'linearConstraintY', 'linearConstraintZ', 'linearConstraintMin', 'linearConstraintMax', 'angularConstraintX', 'angularConstraintY', 'angularConstraintZ', 'angularConstraintMin', 'angularConstraintMax', 'linearLimitSoftness', 'linearLimitBias', 'linearLimitRelaxation', 'angularLimitSoftness', 'angularLimitBias', 'angularLimitRelaxation', 'linearMotorEnabled', 'linearMotorTargetSpeed', 'linearMotorMaxForce', 'angularMotorEnabled', 'angularMotorTargetSpeed', 'angularMotorMaxForce', 'linearSpringEnabledX', 'linearSpringEnabledY', 'linearSpringEnabledZ', 'linearSpringStiffness', 'linearSpringDamping', 'angularSpringEnabledX', 'angularSpringEnabledY', 'angularSpringEnabledZ', 'angularSpringStiffness', 'angularSpringDamping', 'breakable', 'breakingThreshold', ] # Get the rigid body/bodies. rigids = [i for i in [rigidBodyA, rigidBodyB] if i != None] # list of non-None rigid bodies if len(rigids) == 0: # if no rigids specified, then look at selection rigids = BulletUtils.getConnectedRigidBodies() logger.info( maya.stringTable['y_RigidBodyConstraint.kRigidBodiesToConnect'] % (rigids, parent)) if len(rigids) == 0 or len(rigids) > 2: # TODO: this produces a pretty difficult to read error for the user. maya.OpenMaya.MGlobal.displayError( maya. stringTable['y_RigidBodyConstraint.kPleaseSelectRigidbodies']) return [] if len(rigids) == 1 and locals()['constraintType'] in ( eConstraintType.kRBConstraintHinge2, eConstraintType.kRBConstraintSixDOF2): maya.OpenMaya.MGlobal.displayError(maya.stringTable[ 'y_RigidBodyConstraint.kPleaseSelectTwoRigidbodies']) return [] # Get Solver solver = BulletUtils.getSolver() # Create Node constraint = maya.cmds.createNode('bulletRigidBodyConstraintShape', parent=parent) # Set Attrs (optional, set if value != None) # Use the settableAttrs list above to qualify kwargs passed into the # function for k, v in locals().iteritems(): if k in settableAttrs and v != None: if isinstance(v, list): maya.cmds.setAttr('%s.%s' % (constraint, k), *v) # covers float3 cases else: maya.cmds.setAttr('%s.%s' % (constraint, k), v) # Connect maya.cmds.connectAttr((solver + ".outSolverInitialized"), (constraint + ".solverInitialized")) maya.cmds.connectAttr((rigids[0] + ".outRigidBodyData"), (constraint + ".rigidBodyA")) if len(rigids) > 1: maya.cmds.connectAttr((rigids[1] + ".outRigidBodyData"), (constraint + ".rigidBodyB")) maya.cmds.connectAttr((constraint + ".outConstraintData"), (solver + ".rigidBodyConstraints"), na=True) # REVISIT: Consider alternatives like a single initSystem bool attr # instead of startTime and currentTime. # Might be able to get around needing it at all maya.cmds.connectAttr((solver + ".startTime"), (constraint + ".startTime")) maya.cmds.connectAttr((solver + ".currentTime"), (constraint + ".currentTime")) # Translate (avg the rigid body positions) t = maya.cmds.xform(maya.cmds.listRelatives(rigids[0], fullPath=True, parent=True), q=True, ws=True, t=True) if len(rigids) > 1: t2 = maya.cmds.xform(maya.cmds.listRelatives(rigids[1], fullPath=True, parent=True), q=True, ws=True, t=True) t = [(t[0] + t2[0]) * 0.5, (t[1] + t2[1]) * 0.5, (t[2] + t2[2]) * 0.5] constraintT = maya.cmds.listRelatives(constraint, parent=True) maya.cmds.xform(constraintT, ws=True, t=t) ret = [constraint] # If command echoing is off, echo this short line. if (not maya.cmds.commandEcho(query=True, state=True)): print( "RigidBodyConstraint.CreateRigidBodyConstraint.executeCommandCB()" ) print "// Result: %s //" % constraint return ret
def createRagdoll(rootJoint=None, angularDamping=DEFAULT_ANGULAR_DAMPING, angularSoftness=DEFAULT_ANGULAR_SOFTNESS, angularRestitution=DEFAULT_ANGULAR_RESTITUTION, capsuleBoneRatio=DEFAULT_CAPSULE_LENGTH, capsuleRadiusRatio=DEFAULT_CAPSULE_RADIUS, capsuleMass=DEFAULT_CAPSULE_MASS, jointNameSeparator=DEFAULT_NAME_SEPARATOR): """ Creates a ragdoll of capsules joined by constraints, that matches the skeleton starting from the joint named by rootJoint. If no root is specified, the current selection is used. """ nodesToVisit = deque(_getNodesToVisit(rootJoint)) if not nodesToVisit or len(nodesToVisit) < 1: maya.OpenMaya.MGlobal.displayError( maya.stringTable['y_Ragdoll.kCreateRagdollSelectJoint']) return ragdollRootNode = maya.cmds.createNode('transform', name="Ragdoll#", skipSelect=True) currentJoint = None while (len(nodesToVisit) > 0): logger.debug( maya.stringTable[ 'y_Ragdoll.kStartingIterCurrJoint' ] \ % (currentJoint, nodesToVisit) ) currentJoint = nodesToVisit.popleft() # NOTE: assumes joints are directly connected to each other try: parentJoint = _firstParent(currentJoint) if (not _isJoint(parentJoint)): parentJoint = None except: parentJoint = None # end-try logger.debug(maya.stringTable['y_Ragdoll.kVisiting2'] % currentJoint) (rbAXform, rbA) = _getRagdollCapsule(ragdollRootNode, parentJoint, currentJoint, jointNameSeparator) childJoints = _getChildren(currentJoint, type="joint") nodesToVisit.extend(childJoints) prevChildJoint = None for childJoint in childJoints: # Here's what we're working with within this loop: # # parent current child # joint --[rbA]--> joint --[rbB]--> joint (rbBXform, rbB) \ = _createCapsule( currentJoint, childJoint, False, eBodyType.kDynamicRigidBody, capsuleMass, capsuleBoneRatio, capsuleRadiusRatio, "%s%s%s" % (_name(currentJoint), jointNameSeparator, _name(childJoint)) ) rbBXform = _setParent(rbBXform, ragdollRootNode) rbBName = None if (rbB is not None): rbBName = _getChildren(rbBXform, type='bulletRigidBodyShape')[0] rbBXformName = None if (rbBXform is not None): rbBXformName = _longName(rbBXform) # figure out what to anchor this rigid body to if (rbA is None and prevChildJoint is not None): # prevChildJoint implies that a sibling capsule was # created in a previous iteration, which this capsule # can be constrained to. (sibXform, sib) \ = _getRagdollCapsule( ragdollRootNode, currentJoint, prevChildJoint, jointNameSeparator ) rbAnchorName = _longName(sib) elif (rbA is not None): rbAnchorName = _longName(rbA) else: # NOTE: don't create constraints without an anchor, or # else the ragdoll will end up constrained to the world. rbAnchorName = None # end-if if (rbAnchorName is not None): logger.info( maya.stringTable[ 'y_Ragdoll.kCreatingConstr' ] \ % (rbAnchorName, rbB) ) # "constraint" abbreviated "constr" constrName = _createRigidBodyConstraint( \ constraintType=eConstraintType.kRBConstraintSixDOF, rigidBodyA=rbAnchorName, rigidBodyB=rbBName, parent=None )[0] constrNode = constrName constrXformNode = _firstParent(constrNode) # configure the transform constrXformNode = maya.cmds.rename( constrXformNode, "constraint_%s" % _name(currentJoint)) constrXformNode = _setParent(constrXformNode, ragdollRootNode) constrNode = _getChildren( constrXformNode, type='bulletRigidBodyConstraintShape')[0] _setTranslation(constrXformNode, _getTranslation(currentJoint, space="world")) _setRotation(constrXformNode, _getRotation(currentJoint, space="world")) # configure the constraint _setAttr(constrNode, 'angularDamping', angularDamping) _setAttr(constrNode, 'angularSoftness', angularSoftness) _setAttr(constrNode, 'angularRestitution', angularRestitution) # lock the linear motion, to behave like a point constraint _setAttr(constrNode,'linearConstraintX', \ eConstraintLimitType.kRBConstraintLimitLocked ) _setAttr(constrNode,'linearConstraintY', \ eConstraintLimitType.kRBConstraintLimitLocked ) _setAttr(constrNode,'linearConstraintZ', \ eConstraintLimitType.kRBConstraintLimitLocked ) # set the rotational limits to match the joint _applyJointLimits(currentJoint, constrNode) # end-if prevChildJoint = childJoint # end-for # end-while maya.cmds.select(ragdollRootNode, replace=True)
def _createCapsule(currentJoint, childJoint, bAttachToJoint=True, bodyType=eBodyType.kKinematicRigidBody, mass=0.0, boneLengthRatio=DEFAULT_CAPSULE_LENGTH, lengthRadiusRatio=DEFAULT_CAPSULE_RADIUS, transformName=None): """ Create a capsule collider around the bone from currentJoint to childJoint. If bAttachToJoint is True, the capsule will be parented to the current joint, otherwise it will be created at the top level of the scene. BodyType and mass can be specified, and transform name can be used to specify the name of the new top-level rigid body transform. This implementation is based on the current rigid body hierarchy, which looks like: rbXform --> bulletXform --> rbShape (capsule) Returns (rbXform, rbShape) """ if (transformName is None): transformName = '%s#' % RB_XFORM_PREFIX if (bAttachToJoint): parent = currentJoint else: parent = None rbXform = maya.cmds.createNode('transform', name=transformName, parent=parent, skipSelect=True) # these values will work when adding capsules to a joint hierarchy boneVector = _getTranslation(childJoint) center = _vector(_vectorLength(*boneVector) * .5, 0, 0) worldOffset = _vector(0, 0, 0) if (not bAttachToJoint): # if this capsule won't be inheriting the transform from the # parent joint, these values need to be adjusted for world-space worldOffset = _getTranslation(currentJoint, space="world") boneVector = _getTranslation( childJoint, space="world") \ - _getTranslation(currentJoint, space="world") center = _vector(_vectorLength(*boneVector) * .5, 0, 0) # rotation is from the x axis. # end-if rotateQuat = _quaternion(_vector(1, 0, 0), boneVector) rotateAngles = rotateQuat.asEulerRotation() _setRotation(rbXform, rotateAngles) _setTranslation(rbXform, worldOffset) logger.info(maya.stringTable['y_Ragdoll.kAddingRB'] % rbXform) (rbParentName, rbName) = _createRigidBody( \ transformName=_longName(rbXform), colliderShapeType=eShapeType.kColliderCapsule, axis=eAxisType.kXAxis, ignoreShape=True, bAttachSelected=False ) rbNode = rbName _setAttr(rbNode, 'bodyType', bodyType) _setAttr(rbNode, 'mass', mass) _setAttr(rbNode, 'centerOfMass', center) capsuleRadius = _calcCapsuleRadius(currentJoint, childJoint, lengthRadiusRatio) _setAttr(rbNode, 'length', \ max(0.0, math.fabs( _vectorLength(*boneVector) * boneLengthRatio) - capsuleRadius) ) _setAttr(rbNode, 'radius', capsuleRadius) return (rbXform, rbNode)