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