You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

323 lines
12 KiB
C#

3 years ago
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<Rigidbody>();
if (joint.connectedBody != null)
{
for (int i = 0; i < colleagues.Length; i++)
{
if (colleagues[i].joint.GetComponent<Rigidbody>() == 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);
}
}
}