using System.Collections; using System.Collections.Generic; using UnityEngine; namespace RootMotion.Dynamics { [System.Serializable] public class MuscleLite { [HideInInspector] public string name; public ConfigurableJoint joint; public Transform target; public float pinWeightMlp = 1f; public float muscleWeightMlp = 1f; public float muscleDamperMlp = 1f; public float mappingWeightMlp = 1f; public Transform transform { get; private set; } public Rigidbody rigidbody { get; private set; } public Vector3 positionOffset { get; private set; } public int index { get; private set; } private JointDrive slerpDrive = new JointDrive(); private Quaternion defaultLocalRotation = Quaternion.identity; private Quaternion toJointSpaceInverse = Quaternion.identity; private Quaternion toJointSpaceDefault = Quaternion.identity; private Quaternion targetAnimatedRotation = Quaternion.identity; private Quaternion defaultTargetLocalRotation = Quaternion.identity; private Quaternion toParentSpace = Quaternion.identity; private Quaternion targetAnimatedWorldRotation = Quaternion.identity; private Quaternion defaultRotation = Quaternion.identity; private Vector3 defaultPosition; private Vector3 defaultTargetLocalPosition; private float lastJointDriveRotationWeight, lastRotationDamper; private bool initiated; private Transform connectedBodyTarget; private Transform connectedBodyTransform; private Transform targetParent; private bool directTargetParent; private Vector3 targetVelocity; private Vector3 targetAnimatedCenterOfMass; public void Initiate(MuscleLite[] colleagues) { name = joint.name; transform = joint.transform; rigidbody = joint.GetComponent(); if (joint.connectedBody != null) { for (int i = 0; i < colleagues.Length; i++) { if (colleagues[i].joint.GetComponent() == joint.connectedBody) { connectedBodyTarget = colleagues[i].target; } if (colleagues[i] == this) index = i; } joint.autoConfigureConnectedAnchor = false; connectedBodyTransform = joint.connectedBody.transform; directTargetParent = target.parent == connectedBodyTarget; } targetParent = connectedBodyTarget != null ? connectedBodyTarget : target.parent; toParentSpace = Quaternion.Inverse(targetParentRotation) * parentRotation; // Joint space Vector3 forward = Vector3.Cross(joint.axis, joint.secondaryAxis).normalized; Vector3 up = Vector3.Cross(forward, joint.axis).normalized; defaultLocalRotation = localRotation; Quaternion toJointSpace = Quaternion.LookRotation(forward, up); toJointSpaceInverse = Quaternion.Inverse(toJointSpace); toJointSpaceDefault = defaultLocalRotation * toJointSpace; // Set joint params joint.rotationDriveMode = RotationDriveMode.Slerp; joint.configuredInWorldSpace = false; // Fix target Transforms defaultTargetLocalPosition = target.localPosition; defaultTargetLocalRotation = target.localRotation; targetAnimatedCenterOfMass = V3Tools.TransformPointUnscaled(target, rigidbody.centerOfMass); // Resetting if (joint.connectedBody == null) { defaultPosition = transform.localPosition; defaultRotation = transform.localRotation; } else { defaultPosition = joint.connectedBody.transform.InverseTransformPoint(transform.position); defaultRotation = Quaternion.Inverse(joint.connectedBody.transform.rotation) * transform.rotation; } rigidbody.isKinematic = false; Read(); initiated = true; } public void FixTargetTransforms() { if (!initiated) return; target.localRotation = defaultTargetLocalRotation; target.localPosition = defaultTargetLocalPosition; } // Reset the Transform to the default state. This is necessary for activating/deactivating the ragdoll without messing it up public void Reset() { if (!initiated) return; if (joint == null) return; if (joint.connectedBody == null) { transform.localPosition = defaultPosition; transform.localRotation = defaultRotation; } else { transform.position = joint.connectedBody.transform.TransformPoint(defaultPosition); transform.rotation = joint.connectedBody.transform.rotation * defaultRotation; } lastRotationDamper = -1f; } // Moves and rotates the muscle to match its target public void MoveToTarget() { if (!initiated) return; // Moving rigidbodies only won't animate the pose. MoveRotation does not work on a kinematic Rigidbody that is connected to another by a Joint transform.SetPositionAndRotation(target.position, target.rotation); rigidbody.MovePosition(transform.position); rigidbody.MoveRotation(transform.rotation); } public void ClearVelocities() { rigidbody.velocity = Vector3.zero; rigidbody.angularVelocity = Vector3.zero; targetVelocity = Vector3.zero; targetAnimatedCenterOfMass = V3Tools.TransformPointUnscaled(target, rigidbody.centerOfMass); } public void Read() { Vector3 tAM = V3Tools.TransformPointUnscaled(target, rigidbody.centerOfMass); targetVelocity = (tAM - targetAnimatedCenterOfMass) / Time.deltaTime; targetAnimatedCenterOfMass = tAM; if (joint.connectedBody != null) { targetAnimatedRotation = targetLocalRotation; } targetAnimatedWorldRotation = target.rotation; } public void Update(float pinWeightMaster, float muscleWeightMaster, float muscleSpring, float muscleDamper, bool angularPinning) { Pin(pinWeightMaster, 4, 0f, angularPinning); MuscleRotation(muscleWeightMaster, muscleSpring, muscleDamper); } private void Pin(float pinWeightMaster, float pinPow, float pinDistanceFalloff, bool angularPinning) { positionOffset = targetAnimatedCenterOfMass - rigidbody.worldCenterOfMass; if (float.IsNaN(positionOffset.x)) positionOffset = Vector3.zero; float w = pinWeightMaster * pinWeightMlp; if (w <= 0f) return; w = Mathf.Pow(w, pinPow); if (Time.deltaTime > 0f) positionOffset /= Time.deltaTime; Vector3 force = -rigidbody.velocity + targetVelocity + positionOffset; force *= w; if (pinDistanceFalloff > 0f) force /= 1f + positionOffset.sqrMagnitude * pinDistanceFalloff; rigidbody.AddForce(force, ForceMode.VelocityChange); // Angular pinning if (angularPinning) { Vector3 torque = PhysXTools.GetAngularAcceleration(rigidbody.rotation, targetAnimatedWorldRotation); torque -= rigidbody.angularVelocity; torque *= w; rigidbody.AddTorque(torque, ForceMode.VelocityChange); } } private void MuscleRotation(float muscleWeightMaster, float muscleSpring, float muscleDamper) { float w = muscleWeightMaster * muscleSpring * muscleWeightMlp * 10f; if (joint.connectedBody == null) w = 0f; else if (w > 0f) joint.targetRotation = LocalToJointSpace(targetAnimatedRotation); float d = muscleDamper * muscleDamperMlp; if (w == lastJointDriveRotationWeight && d == lastRotationDamper) return; lastJointDriveRotationWeight = w; lastRotationDamper = d; slerpDrive.positionSpring = w; slerpDrive.maximumForce = Mathf.Max(w, d); slerpDrive.positionDamper = d; joint.slerpDrive = slerpDrive; } public void Map(float masterWeight) { float w = masterWeight * mappingWeightMlp; if (w <= 0f) return; Quaternion r = transform.rotation; Vector3 p = transform.position; if (w >= 1f) { // Position if (connectedBodyTransform != null) { Vector3 relativePosition = connectedBodyTransform.InverseTransformPoint(transform.position); p = connectedBodyTarget.TransformPoint(relativePosition); } target.SetPositionAndRotation(p, r); return; } // Rotation r = Quaternion.Lerp(target.rotation, r, w); // Position if (connectedBodyTransform != null) { Vector3 relativePosition = connectedBodyTransform.InverseTransformPoint(transform.position); p = Vector3.Lerp(target.position, connectedBodyTarget.TransformPoint(relativePosition), w); } else { p = Vector3.Lerp(target.position, transform.position, w); } target.SetPositionAndRotation(p, r); } // Update Joint connected anchor public void UpdateAnchor(bool supportTranslationAnimation) { //if (state.isDisconnected) return; if (joint.connectedBody == null || connectedBodyTarget == null) return; if (directTargetParent && !supportTranslationAnimation) return; Vector3 anchorUnscaled = joint.connectedAnchor = InverseTransformPointUnscaled(connectedBodyTarget.position, connectedBodyTarget.rotation * toParentSpace, target.position); float uniformScaleF = 1f / connectedBodyTransform.lossyScale.x; joint.connectedAnchor = anchorUnscaled * uniformScaleF; } private Quaternion localRotation { get { return Quaternion.Inverse(parentRotation) * transform.rotation; } } // Get the rotation of the target private Quaternion targetLocalRotation { get { return Quaternion.Inverse(targetParentRotation * toParentSpace) * target.rotation; } } private Quaternion parentRotation { get { if (joint.connectedBody != null) return joint.connectedBody.rotation; if (transform.parent == null) return Quaternion.identity; return transform.parent.rotation; } } private Quaternion targetParentRotation { get { if (targetParent == null) return Quaternion.identity; return targetParent.rotation; } } // Convert a local rotation to local joint space rotation private Quaternion LocalToJointSpace(Quaternion localRotation) { return toJointSpaceInverse * Quaternion.Inverse(localRotation) * toJointSpaceDefault; } // Inversetransforms a point by the specified position and rotation private static Vector3 InverseTransformPointUnscaled(Vector3 position, Quaternion rotation, Vector3 point) { return Quaternion.Inverse(rotation) * (point - position); } } }