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.
		
		
		
		
		
			
		
			
				
	
	
		
			128 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			128 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
using System.Collections;
 | 
						|
using System.Collections.Generic;
 | 
						|
using UnityEngine;
 | 
						|
using System;
 | 
						|
using System.Reflection;
 | 
						|
 | 
						|
namespace RootMotion
 | 
						|
{
 | 
						|
    public class TQ
 | 
						|
    {
 | 
						|
        public TQ() { }
 | 
						|
 | 
						|
        public TQ(Vector3 translation, Quaternion rotation)
 | 
						|
        {
 | 
						|
            t = translation;
 | 
						|
            q = rotation;
 | 
						|
        }
 | 
						|
 | 
						|
        public Vector3 t;
 | 
						|
        public Quaternion q;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
    Written with the kind help of the one commonly known as Mecanim-Dev.
 | 
						|
    */
 | 
						|
    public class AvatarUtility
 | 
						|
    {
 | 
						|
 | 
						|
        public static Quaternion GetPostRotation(Avatar avatar, AvatarIKGoal avatarIKGoal)
 | 
						|
        {
 | 
						|
            int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
 | 
						|
            if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
 | 
						|
 | 
						|
            MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
 | 
						|
            if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
 | 
						|
 | 
						|
            return (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Get IK position and rotation for foot/hand bone position/rotation.
 | 
						|
        /// </summary>
 | 
						|
        public static TQ GetIKGoalTQ(Avatar avatar, float humanScale, AvatarIKGoal avatarIKGoal, TQ bodyPositionRotation, TQ boneTQ)
 | 
						|
        {
 | 
						|
            int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
 | 
						|
            if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
 | 
						|
 | 
						|
            MethodInfo methodGetAxisLength = typeof(Avatar).GetMethod("GetAxisLength", BindingFlags.Instance | BindingFlags.NonPublic);
 | 
						|
            if (methodGetAxisLength == null) throw new InvalidOperationException("Cannot find GetAxisLength method.");
 | 
						|
 | 
						|
            MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
 | 
						|
            if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
 | 
						|
 | 
						|
            Quaternion postRotation = (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
 | 
						|
 | 
						|
            var goalTQ = new TQ(boneTQ.t, boneTQ.q * postRotation);
 | 
						|
 | 
						|
            if (avatarIKGoal == AvatarIKGoal.LeftFoot || avatarIKGoal == AvatarIKGoal.RightFoot)
 | 
						|
            {
 | 
						|
                // Here you could use animator.leftFeetBottomHeight or animator.rightFeetBottomHeight rather than GetAxisLenght
 | 
						|
                // Both are equivalent but GetAxisLength is the generic way and work for all human bone
 | 
						|
                float axislength = (float)methodGetAxisLength.Invoke(avatar, new object[] { humanId });
 | 
						|
                Vector3 footBottom = new Vector3(axislength, 0, 0);
 | 
						|
                goalTQ.t += (goalTQ.q * footBottom);
 | 
						|
            }
 | 
						|
 | 
						|
            // IK goal are in avatar body local space
 | 
						|
            Quaternion invRootQ = Quaternion.Inverse(bodyPositionRotation.q);
 | 
						|
            goalTQ.t = invRootQ * (goalTQ.t - bodyPositionRotation.t);
 | 
						|
            goalTQ.q = invRootQ * goalTQ.q;
 | 
						|
            goalTQ.t /= humanScale;
 | 
						|
 | 
						|
            goalTQ.q = Quaternion.LookRotation(goalTQ.q * Vector3.forward, goalTQ.q * Vector3.up);
 | 
						|
 | 
						|
            return goalTQ;
 | 
						|
        }
 | 
						|
 | 
						|
        public static TQ WorldSpaceIKGoalToBone(TQ goalTQ, Avatar avatar, AvatarIKGoal avatarIKGoal)
 | 
						|
        {
 | 
						|
            int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
 | 
						|
            if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
 | 
						|
 | 
						|
            MethodInfo methodGetAxisLength = typeof(Avatar).GetMethod("GetAxisLength", BindingFlags.Instance | BindingFlags.NonPublic);
 | 
						|
            if (methodGetAxisLength == null) throw new InvalidOperationException("Cannot find GetAxisLength method.");
 | 
						|
 | 
						|
            MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
 | 
						|
            if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
 | 
						|
 | 
						|
            Quaternion postRotation = (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
 | 
						|
 | 
						|
            if (avatarIKGoal == AvatarIKGoal.LeftFoot || avatarIKGoal == AvatarIKGoal.RightFoot)
 | 
						|
            {
 | 
						|
                // Here you could use animator.leftFeetBottomHeight or animator.rightFeetBottomHeight rather than GetAxisLenght
 | 
						|
                // Both are equivalent but GetAxisLength is the generic way and work for all human bone
 | 
						|
                float axislength = (float)methodGetAxisLength.Invoke(avatar, new object[] { humanId });
 | 
						|
                Vector3 footBottom = new Vector3(axislength, 0, 0);
 | 
						|
                goalTQ.t -= (goalTQ.q * footBottom);
 | 
						|
            }
 | 
						|
 | 
						|
            TQ boneTQ = new TQ(goalTQ.t, goalTQ.q * Quaternion.Inverse(postRotation));
 | 
						|
 | 
						|
            return boneTQ;
 | 
						|
        }
 | 
						|
 | 
						|
        public static TQ GetWorldSpaceIKGoal(BakerHumanoidQT ikQT, BakerHumanoidQT rootQT, float time, float humanScale)
 | 
						|
        {
 | 
						|
            var tq = ikQT.Evaluate(time);
 | 
						|
            var rTQ = rootQT.Evaluate(time);
 | 
						|
 | 
						|
            tq.q = rTQ.q * tq.q;
 | 
						|
            tq.t = rTQ.t + rTQ.q * tq.t;
 | 
						|
            tq.t *= humanScale;
 | 
						|
            return tq;
 | 
						|
        }
 | 
						|
 | 
						|
        public static HumanBodyBones HumanIDFromAvatarIKGoal(AvatarIKGoal avatarIKGoal)
 | 
						|
        {
 | 
						|
            switch (avatarIKGoal)
 | 
						|
            {
 | 
						|
                case AvatarIKGoal.LeftFoot: return HumanBodyBones.LeftFoot;
 | 
						|
                case AvatarIKGoal.RightFoot: return HumanBodyBones.RightFoot;
 | 
						|
                case AvatarIKGoal.LeftHand: return HumanBodyBones.LeftHand;
 | 
						|
                case AvatarIKGoal.RightHand: return HumanBodyBones.RightHand;
 | 
						|
                default: return HumanBodyBones.LastBone;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
} |