using System;
using UnityEngine;
using Framework.Services;
using Framework.Support;
namespace Framework.Actors {
/// Bone assignments of a poseable limb
[Serializable]
public struct Limb {
/// Name of the limb
public string Name;
/// Near bone of the limb, typically thigh or upper arm
public Transform NearBone;
/// Far bone of the limb, typically calf or forearm
public Transform FarBone;
/// Limb that is being posed
public Transform LimbBone;
}
/// Allows the limbs of a character to be posed manually
public class LimbController : ScriptComponent {
/// Limbs the limb controller manages
public Limb[] ControlledLimbs;
/// Poses a limb using inverse kinematics
/// Name of the limb that will be posed
/// Position the limb's end should be placed at
/// Orientation of the limb's end
public void Pose(string limbName, Vector3 targetPosition, Quaternion targetOrientation) {
int limbIndex = getLimbIndexByName(limbName);
//Vector3 nearBoneHead = this.ControlledLimbs[limbIndex].NearBone.transform.position;
//Vector3 farBoneHead = this.ControlledLimbs[limbIndex].FarBone.transform.position;
//Vector3 limbBoneHead = this.ControlledLimbs[limbIndex].LimbBone.transform.position;
//LineSegment2 limbSegment = new LineSegment2(nearBoneHead, limbBoneHead);
//Vector3 poleTargetHead = Intersector.GetClosestPointOnLineSegment(limbSegment, farBoneHead);
//Vector3 poleTargetNormal = Vector3.Normalize(farBoneHead - poleTargetHead);
// Normal of the plane on which the 2D IK solution will be calculated.
// Use the current pose's limb angle under the assumption that this code
// is used to (slightly) adjust the final pose of limbs on an existing
// animation clip.
Vector3 planeNormal = getTriangleNormal(
this.ControlledLimbs[limbIndex].NearBone.transform.position,
this.ControlledLimbs[limbIndex].FarBone.transform.position,
this.ControlledLimbs[limbIndex].LimbBone.transform.position
);
// Calculate the bone angles to put the limb bone at the desired location
InverseKinematicsHelper.SolveTwoBoneIK(
this.ControlledLimbs[limbIndex].NearBone,
this.ControlledLimbs[limbIndex].FarBone,
this.ControlledLimbs[limbIndex].LimbBone,
targetPosition,
planeNormal,
1.0f
);
// Altering the bone rotations will also have changed the direction the limb
// is pointing at. This is easily fixed by applying the desired orientation.
this.ControlledLimbs[limbIndex].LimbBone.rotation = targetOrientation;
}
/// Looks up the index of a limb by its name
/// Name of the limb for which the index will be returned
/// The index of the limb with the specified name or -1 if not found
private int getLimbIndexByName(string limbName) {
if(this.ControlledLimbs == null) {
return -1;
}
int limbCount = this.ControlledLimbs.Length;
for(int index = 0; index < limbCount; ++index) {
if(this.ControlledLimbs[index].Name == limbName) {
return index;
}
}
return -1;
}
/// Calculates the normal vector of a triangle
/// First corner of the triangle
/// Second corner of the triangle
/// Third corner of the triangle
/// The normal vector of the specified triangle
private static Vector3 getTriangleNormal(Vector3 a, Vector3 b, Vector3 c) {
return Vector3.Normalize(Vector3.Cross(b - a, c - a));
}
}
} // namespace Framework.Actors