146 lines
5.7 KiB
C#
146 lines
5.7 KiB
C#
using UnityEngine;
|
|
|
|
namespace CreatureControl {
|
|
|
|
/// <summary>
|
|
/// A leg in a TargetRig which is used to control a leg in a model
|
|
/// </summary>
|
|
[System.Serializable]
|
|
public class TargetLeg : MonoBehaviour {
|
|
public Leg bones;
|
|
|
|
public Transform target; // for the tarsus
|
|
|
|
public Quaternion targetToBoneFemur;
|
|
public Quaternion targetToBoneTibia;
|
|
|
|
public void MatchTo(Leg leg) {
|
|
if (this.bones.femur == null || this.bones.tibia == null || this.bones.tarsus == null)
|
|
return;
|
|
if (leg.femur == null || leg.tibia == null || leg.tarsus == null)
|
|
return;
|
|
|
|
this.bones.femur.position = leg.femur.position;
|
|
this.bones.tibia.position = leg.tibia.position;
|
|
this.bones.tarsus.position = leg.tarsus.position;
|
|
|
|
targetToBoneFemur = TargetRig.TargetToBoneRotation(leg.femur, leg.tibia);
|
|
targetToBoneTibia = TargetRig.TargetToBoneRotation(leg.tibia, leg.tarsus);
|
|
|
|
// Put the end-effector target for IK in a sensible place
|
|
Vector3 legDirection = (this.bones.tarsus.position - this.bones.femur.position).normalized;
|
|
Vector3 targetPosition = this.bones.femur.position + 0.7f * this.bones.length * legDirection.normalized;
|
|
Quaternion targetRotation = Quaternion.LookRotation(legDirection);
|
|
this.target.SetPositionAndRotation(targetPosition, targetRotation);
|
|
this.target.localPosition = new(this.target.localPosition.x, 0, this.target.localPosition.z);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pose the target limb
|
|
/// </summary>
|
|
public void PoseLimb() {
|
|
if (target == null)
|
|
return;
|
|
if (bones.femur == null || bones.tibia == null || bones.tarsus == null)
|
|
return;
|
|
|
|
Quaternion femurOrientation = FemurRotation(target.position);
|
|
Quaternion tibiaOrientation = TibiaRotation(target.position);
|
|
Quaternion tarsusOrientation = TarsusRotation(target.rotation);
|
|
|
|
bones.femur.rotation = femurOrientation;
|
|
bones.tibia.rotation = tibiaOrientation;
|
|
bones.tarsus.rotation = tarsusOrientation;
|
|
}
|
|
|
|
public void UpdateBones(Leg leg) {
|
|
UpdateFemur(leg.femur);
|
|
UpdateTibia(leg.tibia);
|
|
}
|
|
|
|
protected Quaternion FemurRotation(Vector3 targetPosition) {
|
|
if (this.bones.femur == null || this.bones.tibia == null || this.bones.tarsus == null)
|
|
return Quaternion.identity;
|
|
|
|
Vector3 toTarget = targetPosition - this.bones.femur.position;
|
|
// Debug.DrawRay(femur.position, toTarget, Color.magenta);
|
|
float targetDistance = toTarget.magnitude;
|
|
float femurLength = Vector3.Distance(this.bones.femur.position, this.bones.tibia.position);
|
|
float tibiaLength = Vector3.Distance(this.bones.tibia.position, this.bones.tarsus.position);
|
|
|
|
float hipAngle = CosineRule(targetDistance, femurLength, tibiaLength);
|
|
// NaN happens when the distance to the footTarget is longer than the length of the leg
|
|
// We will stretch the leg full then (angle = 0)
|
|
if (float.IsNaN(hipAngle))
|
|
hipAngle = 0;
|
|
|
|
Quaternion femurOrientation = Quaternion.LookRotation(toTarget, Vector3.up);
|
|
femurOrientation = Quaternion.AngleAxis(hipAngle, femurOrientation * Vector3.left) * femurOrientation;
|
|
// Debug.DrawRay(femur.position, femurOrientation * Vector3.forward, Color.blue);
|
|
// Debug.DrawRay(femur.position, femurOrientation * Vector3.up, Color.green);
|
|
|
|
return femurOrientation;
|
|
}
|
|
|
|
protected Quaternion TibiaRotation(Vector3 targetPosition) {
|
|
if (this.bones.tibia == null)
|
|
return Quaternion.identity;
|
|
|
|
Vector3 directionToTarget = targetPosition - this.bones.tibia.position;
|
|
|
|
Quaternion tibiaOrientation = Quaternion.LookRotation(directionToTarget, Vector3.up); // femur.up);
|
|
return tibiaOrientation; // In world space
|
|
}
|
|
|
|
protected Quaternion TarsusRotation(Quaternion targetRotation) {
|
|
return targetRotation;
|
|
}
|
|
|
|
public void UpdateFemur(Transform femurBone) {
|
|
if (femurBone == null || this.bones.femur == null)
|
|
return;
|
|
|
|
femurBone.rotation = this.bones.femur.rotation * targetToBoneFemur;
|
|
}
|
|
|
|
public void UpdateTibia(Transform tibiaBone) {
|
|
if (tibiaBone == null || this.bones.tibia == null)
|
|
return;
|
|
|
|
tibiaBone.rotation = this.bones.tibia.rotation * targetToBoneTibia;
|
|
}
|
|
|
|
#region Math
|
|
|
|
public static float CosineRule(float a, float b, float c) {
|
|
float a2 = a * a;
|
|
float b2 = b * b;
|
|
float c2 = c * c;
|
|
|
|
double angle = System.Math.Acos((a2 + b2 - c2) / (2 * a * b)) * Mathf.Rad2Deg;
|
|
if (double.IsNaN(angle))
|
|
angle = 0;
|
|
return (float)angle;
|
|
}
|
|
|
|
#endregion Math
|
|
|
|
#region Scene
|
|
|
|
public virtual void OnDrawGizmosSelected() {
|
|
if (this.enabled == false)
|
|
return;
|
|
|
|
Gizmos.color = Color.white;
|
|
if (this.bones.femur != null && this.bones.tibia != null)
|
|
Gizmos.DrawLine(this.bones.femur.position, this.bones.tibia.position);
|
|
if (bones.tibia != null && this.bones.tarsus != null)
|
|
Gizmos.DrawLine(this.bones.tibia.position, this.bones.tarsus.position);
|
|
|
|
PoseLimb();
|
|
}
|
|
|
|
#endregion Scene
|
|
}
|
|
|
|
} |