def updateColliderUI(nodeName): """ Called when the attribute editor UI for the rigid body needs to be updated to reflect the latest attribute values. Enables and disables controls. """ logger.debug(maya.stringTable['y_RigidBodyUI.kUpdatingCollider'] % nodeName) cst = _getAttr(nodeName, 'colliderShapeType') autoFit = _getAttr(nodeName, 'autoFit') maya.cmds.editorTemplate(dimControl=(nodeName, "colliderShapeOffset", autoFit)) maya.cmds.editorTemplate( dimControl=(nodeName, "axis", autoFit or cst not in (eShapeType.kColliderCylinder, eShapeType.kColliderCapsule))) maya.cmds.editorTemplate( dimControl=(nodeName, "length", autoFit or cst not in (eShapeType.kColliderCylinder, eShapeType.kColliderCapsule))) maya.cmds.editorTemplate( dimControl=(nodeName, "radius", autoFit or cst not in (eShapeType.kColliderCylinder, eShapeType.kColliderCapsule, eShapeType.kColliderSphere))) maya.cmds.editorTemplate( dimControl=(nodeName, "extents", autoFit or cst not in (eShapeType.kColliderBox, )))
def updateSoftBodyUI(nodeName): """ Called when the attribute editor UI for the soft body needs to be updated to reflect the latest attribute values. Enables and disables controls. """ logger.debug(maya.stringTable['y_SoftBodyUI.kUpdatingSoftBody'] % nodeName) generateBendConstraints = cmds.getAttr( '{0}.generateBendConstraints'.format(nodeName)) enableVolumeMatching = cmds.getAttr( '{0}.enableVolumeMatching'.format(nodeName)) enableShapeMatching = cmds.getAttr( '{0}.enableShapeMatching'.format(nodeName)) cmds.editorTemplate(dimControl=(nodeName, "bendResistance", not generateBendConstraints)) cmds.editorTemplate(dimControl=(nodeName, "volumeCoefficient", not enableVolumeMatching)) cmds.editorTemplate(dimControl=(nodeName, "maxVolumeRatio", not enableVolumeMatching)) cmds.editorTemplate(dimControl=(nodeName, "shapeCoefficient", not enableShapeMatching))
def updateConstraintUI(nodeName): """ Called when the attribute editor UI for the rigid body constraints needs to be updated to reflect the latest attribute values. Enables and disables controls. """ logger.debug( maya.stringTable['y_RigidBodyConstraintUI.kUpdatingConstrUI'] % nodeName) # Limit Properties section constraintType = cmds.getAttr('{0}.constraintType'.format(nodeName)) for attrName in dictConstraintAttributes.keys(): _enableControl(nodeName, attrName, constraintType, dictConstraintAttributes[attrName]) linearMotorEnabled = cmds.getAttr( '{0}.linearMotorEnabled'.format(nodeName)) cmds.editorTemplate(dimControl=(nodeName, "linearMotorTargetSpeed", not linearMotorEnabled)) cmds.editorTemplate(dimControl=(nodeName, "linearMotorMaxForce", not linearMotorEnabled)) angularMotorEnabled = cmds.getAttr( '{0}.angularMotorEnabled'.format(nodeName)) cmds.editorTemplate(dimControl=(nodeName, "angularMotorTargetSpeed", not angularMotorEnabled)) cmds.editorTemplate(dimControl=(nodeName, "angularMotorMaxForce", not angularMotorEnabled))
def getRigidBodyFromTransform(transform): """ Given a transform (by name), return the rigid body. This method encapsulates the logic behind the RB hierarchies. Currently that hierarchy looks like: init_xform rb_xform rb_shape """ logger.debug(maya.stringTable['y_BulletUtils.kGettingRB'] % transform) if transform: xformNode = _longName(transform) else: return None # first check if this is a rigid body xform... shapeNodes = _getChildren(xformNode, type=("bulletRigidBodyShape", "bulletSoftBodyShape", "bulletSolverShape", "bulletRigidBodyConstraintShape", "bulletSoftConstraintShape")) if (len(shapeNodes) > 1): logger.warn(maya.stringTable['y_BulletUtils.kExceptedShapeUnder'] % xformNode) if (len(shapeNodes) > 0): return shapeNodes[0] # no match? check if the xform was the init xform... for childXformNode in _getChildren(xformNode, type="transform"): shapeNodes = _getChildren( childXformNode, type=("bulletRigidBodyShape", "bulletSoftBodyShape", "bulletSolverShape", "bulletRigidBodyConstraintShape", "bulletSoftConstraintShape")) if (len(shapeNodes) > 1): logger.warn( maya.stringTable['y_BulletUtils.kExceptedShapeUnder2'] % childXformNode) if (len(shapeNodes) > 0): return shapeNodes[0] # end-for # didn't find any RB shapes in the expected places return None
def disconnectFromSolver(bulletShape): """ Given a Bullet shape, disconnect its connections to any BulletSolverShape nodes. """ @Trace() def _inputs(obj): return zip( maya.cmds.listConnections( bulletShape, plugs=True, \ source=True, destination=False ), maya.cmds.listConnections( bulletShape, \ source=True, destination=False, sh=True ) ) @Trace() def _nodeFromPlug(plug): return _longName(plug.split('.')[0]) @Trace() def _disconnect(outPlug, inPlug): maya.cmds.disconnectAttr(outPlug, inPlug) bulletShape = _longName(bulletShape) logger.debug(maya.stringTable['y_BulletUtils.kDisconnectShape'] % bulletShape) # into the shape from other nodes for outPlug, outSrc in _inputs(bulletShape): if not _isType(outSrc, 'bulletSolverShape'): logger.debug(maya.stringTable['y_BulletUtils.kSkipNonSolverConn'] % outPlug) continue # get input plugs connected to output plug for inPlug in maya.cmds.connectionInfo(outPlug, \ destinationFromSource=True): # get the node from plug if (_nodeFromPlug(inPlug) != bulletShape): logger.debug( maya.stringTable['y_BulletUtils.kSkipUnrelatedConn'] % inPlug) continue logger.debug(maya.stringTable['y_BulletUtils.kDisconnectConn'] % (outPlug, inPlug)) _disconnect(outPlug, inPlug) # end-for # end-for # these are connections out from the shape to other nodes destinations = maya.cmds.listConnections( _longName(bulletShape), plugs=True, \ source=False, destination=True ) for inPlug in destinations: if (not _isType(inPlug, 'bulletSolverShape')): logger.debug( maya.stringTable['y_BulletUtils.kSkipNonSolverConn2'] % inPlug) continue outPlug \ = maya.cmds.connectionInfo(_name(inPlug), \ sourceFromDestination=True) if _nodeFromPlug(outPlug) != bulletShape: logger.debug( maya.stringTable['y_BulletUtils.kSkipUnrelatedConn2'] % outPlug) continue logger.debug(maya.stringTable['y_BulletUtils.kDisconnectConn2'] % (outPlug, inPlug)) _disconnect(outPlug, inPlug) # end-for return True
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 addCapsulesToSkeleton(rootJointName=None, capsuleBoneRatio=DEFAULT_CAPSULE_LENGTH, capsuleRadiusRatio=DEFAULT_CAPSULE_RADIUS): """ This method traverses a joint hierarchy, adding kinematic rigid body capsules to the bones. If the name of the root joint is provided, the traversal will begin at that joint. Otherwise, the currently selected joint(s) will be used. """ initialSelection = maya.cmds.ls(long=True, selection=True) nodesToVisit = deque(_getNodesToVisit(rootJointName)) if not nodesToVisit or len(nodesToVisit) < 1: maya.OpenMaya.MGlobal.displayError( maya.stringTable['y_Ragdoll.kAddCollidersSelectJoint']) return while (len(nodesToVisit) > 0): currentJoint = nodesToVisit.popleft() logger.debug(maya.stringTable['y_Ragdoll.kVisiting'] % currentJoint) # within the current iteration, we will consider only other # joints attached to the current joint children = [ child for child in _getChildren(currentJoint) \ if _isJoint(child) ] # future iterations need only consider joints with children nodesToVisit.extend( [child for child in children \ if _numChildren(child) > 0] ) prevCapsules = [child for child in _getChildren(currentJoint) \ if RB_XFORM_PREFIX in _name(child)] # examine the bones between this joint and its child joints... for childJoint in children: logger.debug(maya.stringTable['y_Ragdoll.kBoneFromTo'] % (currentJoint, childJoint)) boneVector = _getTranslation(childJoint) bCapsuleExists = False for prevCapsuleXform in prevCapsules: if _getTranslation(prevCapsuleXform) == boneVector * 0.5: bCapsuleExists = True break # end-if # end-for if (bCapsuleExists): logger.warn(maya.stringTable['y_Ragdoll.kSkippingBone']) continue # end-if _createCapsule( currentJoint, childJoint, bAttachToJoint=True, # colliders for animation should be kinematic, # with zero mass bodyType=eBodyType.kKinematicRigidBody, mass=0.0, boneLengthRatio=capsuleBoneRatio, lengthRadiusRatio=capsuleRadiusRatio) # end-for # end-while # restore the initial selection maya.cmds.select(initialSelection, replace=True)