using UnityEngine; //using Windows.Kinect; using System; using System.Collections; using System.Collections.Generic; using com.rfilkov.kinect; namespace com.rfilkov.components { /// /// Avatar controller is the component that transfers the captured user motion to a humanoid model (avatar). /// [RequireComponent(typeof(Animator))] public class AvatarController : MonoBehaviour { [Tooltip("Index of the player, tracked by this component. 0 means the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] public int playerIndex = 0; [Tooltip("Whether the avatar is facing the player or not.")] public bool mirroredMovement = false; [Tooltip("Whether the avatar is allowed to move vertically or not.")] public bool verticalMovement = false; [Tooltip("Whether the avatar's root motion is applied by other component or script.")] public bool externalRootMotion = false; [Tooltip("Whether the head rotation is controlled externally (e.g. by VR-headset).")] public bool externalHeadRotation = false; [Tooltip("Whether the hand and finger rotations are controlled externally (e.g. by LeapMotion controller)")] public bool externalHandRotations = false; //[Tooltip("Whether the finger orientations are allowed or not.")] //public bool fingerOrientations = false; [Tooltip("Rate at which the avatar will move through the scene.")] public float moveRate = 1f; [Tooltip("Smooth factor used for avatar movements and joint rotations.")] public float smoothFactor = 10f; [Tooltip("Whether to update the avatar in LateUpdate(), instead of in Update(). Needed for Mecanim animation blending.")] public bool lateUpdateAvatar = false; [Tooltip("Game object this transform is relative to (optional).")] public GameObject offsetNode; [Tooltip("If enabled, makes the avatar position relative to this camera to be the same as the player's position to the sensor.")] public Camera posRelativeToCamera; [Tooltip("Whether the avatar's position should match the color image (in Pos-rel-to-camera mode only).")] public bool posRelOverlayColor = false; [Tooltip("Plane used to render the color camera background to overlay.")] public Transform backgroundPlane; [Tooltip("Whether z-axis movement needs to be inverted (Pos-Relative mode only).")] public bool posRelInvertedZ = false; [Tooltip("Whether the avatar's feet must stick to the ground.")] public bool groundedFeet = false; [Tooltip("Whether to apply the humanoid model's muscle limits or not.")] public bool applyMuscleLimits = false; [Tooltip("Whether to flip left and right, relative to the sensor.")] public bool flipLeftRight = false; [Tooltip("Horizontal offset of the avatar with respect to the position of user's spine-base.")] [Range(-0.5f, 0.5f)] public float horizontalOffset = 0f; [Tooltip("Vertical offset of the avatar with respect to the position of user's spine-base.")] [Range(-0.5f, 0.5f)] public float verticalOffset = 0f; [Tooltip("Forward offset of the avatar with respect to the position of user's spine-base.")] [Range(-0.5f, 0.5f)] public float forwardOffset = 0f; // userId of the player [NonSerialized] public ulong playerId = 0; // The body root node protected Transform bodyRoot; // Variable to hold all them bones. It will initialize the same size as initialRotations. protected Transform[] bones; //protected Transform[] fingerBones; // Rotations of the bones when the Kinect tracking starts. protected Quaternion[] initialRotations; protected Quaternion[] localRotations; protected bool[] isBoneDisabled; // Local rotations of finger bones protected Dictionary fingerBoneLocalRotations = new Dictionary(); protected Dictionary fingerBoneLocalAxes = new Dictionary(); // Initial position and rotation of the transform protected Vector3 initialPosition; protected Quaternion initialRotation; protected Vector3 initialHipsPosition; protected Quaternion initialHipsRotation; protected Vector3 offsetNodePos; protected Quaternion offsetNodeRot; protected Vector3 bodyRootPosition; // Calibration Offset Variables for Character Position. [NonSerialized] public bool offsetCalibrated = false; protected Vector3 offsetPos = Vector3.zero; //protected float xOffset, yOffset, zOffset; //private Quaternion originalRotation; private Animator animatorComponent = null; private HumanPoseHandler humanPoseHandler = null; private HumanPose humanPose = new HumanPose(); // whether the parent transform obeys physics protected bool isRigidBody = false; // private instance of the KinectManager protected KinectManager kinectManager; //// last hand events //private InteractionManager.HandEventType lastLeftHandEvent = InteractionManager.HandEventType.Release; //private InteractionManager.HandEventType lastRightHandEvent = InteractionManager.HandEventType.Release; //// fist states //private bool bLeftFistDone = false; //private bool bRightFistDone = false; // grounder constants and variables private const int raycastLayers = ~2; // Ignore Raycast private const float maxFootDistanceGround = 0.02f; // maximum distance from lower foot to the ground private const float maxFootDistanceTime = 0.2f; // 1.0f; // maximum allowed time, the lower foot to be distant from the ground private Transform leftFoot, rightFoot; private float fFootDistanceInitial = 0f; private float fFootDistance = 0f; private float fFootDistanceTime = 0f; // background plane rectangle private Rect planeRect = new Rect(); private bool planeRectSet = false; /// /// Gets the number of bone transforms (array length). /// /// The number of bone transforms. public int GetBoneTransformCount() { return bones != null ? bones.Length : 0; } /// /// Gets the bone transform by index. /// /// The bone transform. /// Index public Transform GetBoneTransform(int index) { if (index >= 0 && bones != null && index < bones.Length) { return bones[index]; } return null; } /// /// Disables the bone and optionally resets its orientation. /// /// Bone index. /// If set to true resets bone orientation. public void DisableBone(int index, bool resetBone) { if (index >= 0 && index < bones.Length) { isBoneDisabled[index] = true; if (resetBone && bones[index] != null) { bones[index].rotation = localRotations[index]; } } } /// /// Enables the bone, so AvatarController could update its orientation. /// /// Bone index. public void EnableBone(int index) { if (index >= 0 && index < bones.Length) { isBoneDisabled[index] = false; } } /// /// Determines whether the bone orientation update is enabled or not. /// /// true if the bone update is enabled; otherwise, false. /// Bone index. public bool IsBoneEnabled(int index) { if (index >= 0 && index < bones.Length) { return !isBoneDisabled[index]; } return false; } /// /// Gets the bone index by joint type. /// /// The bone index. /// Joint type /// If set to true gets the mirrored joint index. public int GetBoneIndexByJoint(KinectInterop.JointType joint, bool bMirrored) { int boneIndex = -1; if (jointMap2boneIndex.ContainsKey(joint)) { boneIndex = !bMirrored ? jointMap2boneIndex[joint] : mirrorJointMap2boneIndex[joint]; } return boneIndex; } /// /// Gets the list of AC-controlled mecanim bones. /// /// List of AC-controlled mecanim bones public List GetMecanimBones() { List alMecanimBones = new List(); for (int boneIndex = 0; boneIndex < bones.Length; boneIndex++) { if (!boneIndex2MecanimMap.ContainsKey(boneIndex)) continue; alMecanimBones.Add(boneIndex2MecanimMap[boneIndex]); } return alMecanimBones; } // transform caching gives performance boost since Unity calls GetComponent() each time you call transform private Transform _transformCache; public new Transform transform { get { if (!_transformCache) { _transformCache = base.transform; } return _transformCache; } } public void Awake() { // check for double start if (bones != null) return; if (!gameObject.activeInHierarchy) return; // inits the bones array bones = new Transform[21]; // get the animator reference animatorComponent = GetComponent(); // Map bones to the points the Kinect tracks MapBones(); // Set model's arms to be in T-pose, if needed SetModelArmsInTpose(); // Initial rotations and directions of the bones. initialRotations = new Quaternion[bones.Length]; localRotations = new Quaternion[bones.Length]; isBoneDisabled = new bool[bones.Length]; // Get initial bone rotations GetInitialRotations(); // enable all bones for (int i = 0; i < bones.Length; i++) { isBoneDisabled[i] = false; } // get initial distance to ground fFootDistanceInitial = GetDistanceToGround(); fFootDistance = 0f; fFootDistanceTime = 0f; // if parent transform uses physics isRigidBody = (gameObject.GetComponent() != null); // get the pose handler reference if (animatorComponent && animatorComponent.avatar && animatorComponent.avatar.isHuman) { //Transform hipsTransform = animator.GetBoneTransform(HumanBodyBones.Hips); //Transform rootTransform = hipsTransform.parent; Transform rootTransform = transform; humanPoseHandler = new HumanPoseHandler(animatorComponent.avatar, rootTransform); humanPoseHandler.GetHumanPose(ref humanPose); initialHipsPosition = (humanPose.bodyPosition - rootTransform.position); // hipsTransform.position initialHipsRotation = humanPose.bodyRotation; } } public void Update() { if(kinectManager == null) { kinectManager = KinectManager.Instance; } ulong userId = kinectManager ? kinectManager.GetUserIdByIndex(playerIndex) : 0; if (playerId != userId) { if (/**playerId == 0 &&*/ userId != 0) SuccessfulCalibration(userId, false); else if (/**playerId != 0 &&*/ userId == 0) ResetToInitialPosition(); } if (!lateUpdateAvatar && playerId != 0) { UpdateAvatar(playerId); } } void LateUpdate() { if (lateUpdateAvatar && playerId != 0) { UpdateAvatar(playerId); } } // applies the muscle limits for humanoid avatar private void CheckMuscleLimits() { if (humanPoseHandler == null) return; humanPoseHandler.GetHumanPose(ref humanPose); //Debug.Log(playerId + " - Trans: " + transform.position + ", body: " + humanPose.bodyPosition); bool isPoseChanged = false; float muscleMin = -1f; float muscleMax = 1f; for (int i = 0; i < humanPose.muscles.Length; i++) { if (float.IsNaN(humanPose.muscles[i])) { //humanPose.muscles[i] = 0f; continue; } if (humanPose.muscles[i] < muscleMin) { humanPose.muscles[i] = muscleMin; isPoseChanged = true; } else if (humanPose.muscles[i] > muscleMax) { humanPose.muscles[i] = muscleMax; isPoseChanged = true; } } if (isPoseChanged) { //Quaternion localBodyRot = Quaternion.Inverse(transform.rotation) * humanPose.bodyRotation; Quaternion localBodyRot = Quaternion.Inverse(initialHipsRotation) * humanPose.bodyRotation; // recover the body position & orientation //humanPose.bodyPosition = Vector3.zero; //humanPose.bodyPosition.y = initialHipsPosition.y; humanPose.bodyPosition = initialHipsPosition; humanPose.bodyRotation = localBodyRot; // Quaternion.identity; humanPoseHandler.SetHumanPose(ref humanPose); //Debug.Log(" Human pose updated."); } } /// /// Updates the avatar each frame. /// /// User ID public void UpdateAvatar(ulong UserID) { if (!gameObject.activeInHierarchy) return; // Get the KinectManager instance if (kinectManager == null) { kinectManager = KinectManager.Instance; } // get the background plane rectangle if needed if (backgroundPlane && !planeRectSet && kinectManager && kinectManager.IsInitialized()) { planeRectSet = true; planeRect.width = 10f * Mathf.Abs(backgroundPlane.localScale.x); planeRect.height = 10f * Mathf.Abs(backgroundPlane.localScale.z); planeRect.x = backgroundPlane.position.x - planeRect.width / 2f; planeRect.y = backgroundPlane.position.y - planeRect.height / 2f; } // move the avatar to its Kinect position if (!externalRootMotion) { MoveAvatar(UserID); } //// get the left hand state and event //if (kinectManager && kinectManager.GetJointTrackingState(UserID, (int)KinectInterop.JointType.HandLeft) != KinectInterop.TrackingState.NotTracked) //{ // KinectInterop.HandState leftHandState = kinectManager.GetLeftHandState(UserID); // InteractionManager.HandEventType leftHandEvent = InteractionManager.HandStateToEvent(leftHandState, lastLeftHandEvent); // if (leftHandEvent != InteractionManager.HandEventType.None) // { // lastLeftHandEvent = leftHandEvent; // } //} //// get the right hand state and event //if (kinectManager && kinectManager.GetJointTrackingState(UserID, (int)KinectInterop.JointType.HandRight) != KinectInterop.TrackingState.NotTracked) //{ // KinectInterop.HandState rightHandState = kinectManager.GetRightHandState(UserID); // InteractionManager.HandEventType rightHandEvent = InteractionManager.HandStateToEvent(rightHandState, lastRightHandEvent); // if (rightHandEvent != InteractionManager.HandEventType.None) // { // lastRightHandEvent = rightHandEvent; // } //} // rotate the avatar bones for (var boneIndex = 0; boneIndex < bones.Length; boneIndex++) { if (!bones[boneIndex] || isBoneDisabled[boneIndex]) continue; if (boneIndex2JointMap.ContainsKey(boneIndex)) { KinectInterop.JointType joint = !(mirroredMovement ^ flipLeftRight) ? boneIndex2JointMap[boneIndex] : boneIndex2MirrorJointMap[boneIndex]; if (externalHeadRotation && joint == KinectInterop.JointType.Head) // skip head if moved externally { continue; } if (externalHandRotations && // skip hands if moved externally (joint == KinectInterop.JointType.WristLeft || joint == KinectInterop.JointType.WristRight || joint == KinectInterop.JointType.HandLeft || joint == KinectInterop.JointType.HandRight)) { continue; } TransformBone(UserID, joint, boneIndex, !(mirroredMovement ^ flipLeftRight)); } } if (applyMuscleLimits && kinectManager && kinectManager.IsUserTracked(UserID)) { // check for limits CheckMuscleLimits(); } } /// /// Resets bones to their initial positions and rotations. This also releases avatar control from KM, by settings playerId to 0 /// public virtual void ResetToInitialPosition() { playerId = 0; //Debug.Log("ResetToInitialPosition. UserId: " + playerId); if (bones == null) return; // For each bone that was defined, reset to initial position. transform.rotation = Quaternion.identity; for (int pass = 0; pass < 2; pass++) // 2 passes because clavicles are at the end { for (int i = 0; i < bones.Length; i++) { if (bones[i] != null) { bones[i].rotation = initialRotations[i]; } } } // reset finger bones to initial position //Animator animatorComponent = GetComponent(); foreach (HumanBodyBones bone in fingerBoneLocalRotations.Keys) { Transform boneTransform = animatorComponent ? animatorComponent.GetBoneTransform(bone) : null; if (boneTransform) { boneTransform.localRotation = fingerBoneLocalRotations[bone]; } } // Restore the offset's position and rotation if (offsetNode != null) { offsetNode.transform.position = offsetNodePos; offsetNode.transform.rotation = offsetNodeRot; } transform.position = initialPosition; transform.rotation = initialRotation; } /// /// Invoked on the successful calibration of the player. /// /// User identifier. public virtual void SuccessfulCalibration(ulong userId, bool resetInitialTransform) { playerId = userId; //Debug.Log("SuccessfulCalibration. UserId: " + playerId); // reset the models position if (offsetNode != null) { offsetNode.transform.position = offsetNodePos; offsetNode.transform.rotation = offsetNodeRot; } // reset initial position / rotation if needed if (resetInitialTransform) { bodyRootPosition = transform.position; initialPosition = transform.position; initialRotation = transform.rotation; } transform.position = initialPosition; transform.rotation = initialRotation; // re-calibrate the position offset offsetCalibrated = false; } /// /// Moves the avatar to its initial/base position /// /// world position /// rotation offset public void resetInitialTransform(Vector3 position, Vector3 rotation) { bodyRootPosition = position; initialPosition = position; initialRotation = Quaternion.Euler(rotation); transform.position = initialPosition; transform.rotation = initialRotation; offsetCalibrated = false; // this cause also calibrating kinect offset in moveAvatar function } // Checks if the given joint is part of the legs protected bool IsLegJoint(KinectInterop.JointType joint) { return ((joint == KinectInterop.JointType.HipLeft) || (joint == KinectInterop.JointType.HipRight) || (joint == KinectInterop.JointType.KneeLeft) || (joint == KinectInterop.JointType.KneeRight) || (joint == KinectInterop.JointType.AnkleLeft) || (joint == KinectInterop.JointType.AnkleRight)); } // Apply the rotations tracked by kinect to the joints. protected void TransformBone(ulong userId, KinectInterop.JointType joint, int boneIndex, bool flip) { Transform boneTransform = bones[boneIndex]; if (boneTransform == null || kinectManager == null) return; int iJoint = (int)joint; if (iJoint < 0 || !kinectManager.IsJointTracked(userId, iJoint)) return; // Get Kinect joint orientation Quaternion jointRotation = kinectManager.GetJointOrientation(userId, iJoint, flip); if (jointRotation == Quaternion.identity && !IsLegJoint(joint)) return; // calculate the new orientation Quaternion newRotation = Kinect2AvatarRot(jointRotation, boneIndex); if (externalRootMotion) { newRotation = transform.rotation * newRotation; } // Smoothly transition to the new rotation if (smoothFactor != 0f) boneTransform.rotation = Quaternion.Slerp(boneTransform.rotation, newRotation, smoothFactor * Time.deltaTime); else boneTransform.rotation = newRotation; } // Moves the avatar - gets the tracked position of the user and applies it to avatar. protected void MoveAvatar(ulong UserID) { if ((moveRate == 0f) || !kinectManager || !kinectManager.IsJointTracked(UserID, (int)KinectInterop.JointType.Pelvis)) { return; } // get the position of user's spine base Vector3 trans = kinectManager.GetUserPosition(UserID); if (flipLeftRight) trans.x = -trans.x; // use the color overlay position if needed if (posRelativeToCamera && posRelOverlayColor) { int sensorIndex = kinectManager.GetPrimaryBodySensorIndex(); if (backgroundPlane && planeRectSet) { // get the plane overlay position trans = kinectManager.GetJointPosColorOverlay(UserID, (int)KinectInterop.JointType.Pelvis, sensorIndex, planeRect); trans.z = backgroundPlane.position.z - posRelativeToCamera.transform.position.z - 0.1f; // 10cm offset } else { Rect backgroundRect = posRelativeToCamera.pixelRect; PortraitBackground portraitBack = PortraitBackground.Instance; if (portraitBack && portraitBack.enabled) { backgroundRect = portraitBack.GetBackgroundRect(); } trans = kinectManager.GetJointPosColorOverlay(UserID, (int)KinectInterop.JointType.Pelvis, sensorIndex, posRelativeToCamera, backgroundRect); } if (flipLeftRight) trans.x = -trans.x; } // invert the z-coordinate, if needed if (posRelativeToCamera && posRelInvertedZ) { trans.z = -trans.z; } if (!offsetCalibrated) { offsetCalibrated = true; offsetPos.x = trans.x; // !mirroredMovement ? trans.x * moveRate : -trans.x * moveRate; offsetPos.y = trans.y; // trans.y * moveRate; offsetPos.z = !mirroredMovement && !posRelativeToCamera ? -trans.z : trans.z; // -trans.z * moveRate; if (posRelativeToCamera) { Vector3 cameraPos = posRelativeToCamera.transform.position; Vector3 bodyRootPos = bodyRoot != null ? bodyRoot.position : transform.position; Vector3 hipCenterPos = bodyRoot != null ? bodyRoot.position : (bones != null && bones.Length > 0 && bones[0] != null ? bones[0].position : Vector3.zero); float yRelToAvatar = 0f; if (verticalMovement) { yRelToAvatar = (trans.y - cameraPos.y) - (hipCenterPos - bodyRootPos).magnitude; } else { yRelToAvatar = bodyRootPos.y - cameraPos.y; } Vector3 relativePos = new Vector3(trans.x, yRelToAvatar, trans.z); Vector3 newBodyRootPos = cameraPos + relativePos; // if(offsetNode != null) // { // newBodyRootPos += offsetNode.transform.position; // } if (bodyRoot != null) { bodyRoot.position = newBodyRootPos; } else { transform.position = newBodyRootPos; } bodyRootPosition = newBodyRootPos; } } // transition to the new position Vector3 targetPos = bodyRootPosition + Kinect2AvatarPos(trans, verticalMovement); if (isRigidBody && !verticalMovement) { // workaround for obeying the physics (e.g. gravity falling) targetPos.y = bodyRoot != null ? bodyRoot.position.y : transform.position.y; } // added by r618 if (horizontalOffset != 0f && bones[5] != null && bones[11] != null) { // { 5, HumanBodyBones.LeftUpperArm}, // { 11, HumanBodyBones.RightUpperArm}, //Vector3 dirSpine = bones[5].position - bones[11].position; Vector3 dirShoulders = bones[11].position - bones[5].position; targetPos += dirShoulders.normalized * horizontalOffset; } if (verticalMovement && verticalOffset != 0f && bones[0] != null && bones[3] != null) { Vector3 dirSpine = bones[3].position - bones[0].position; targetPos += dirSpine.normalized * verticalOffset; } if (forwardOffset != 0f && bones[0] != null && bones[3] != null && bones[5] != null && bones[11] != null) { Vector3 dirSpine = (bones[3].position - bones[0].position).normalized; Vector3 dirShoulders = (bones[11].position - bones[5].position).normalized; Vector3 dirForward = Vector3.Cross(dirShoulders, dirSpine).normalized; targetPos += dirForward * forwardOffset; } if (groundedFeet) { // keep the current correction float fLastTgtY = targetPos.y; targetPos.y += fFootDistance; float fNewDistance = GetDistanceToGround(); float fNewDistanceTime = Time.time; // Debug.Log(string.Format("PosY: {0:F2}, LastY: {1:F2}, TgrY: {2:F2}, NewDist: {3:F2}, Corr: {4:F2}, Time: {5:F2}", bodyRoot != null ? bodyRoot.position.y : transform.position.y, // fLastTgtY, targetPos.y, fNewDistance, fFootDistance, fNewDistanceTime)); if (Mathf.Abs(fNewDistance) >= 0.01f && Mathf.Abs(fNewDistance - fFootDistanceInitial) >= maxFootDistanceGround) { if ((fNewDistanceTime - fFootDistanceTime) >= maxFootDistanceTime) { fFootDistance += (fNewDistance - fFootDistanceInitial); fFootDistanceTime = fNewDistanceTime; targetPos.y = fLastTgtY + fFootDistance; // Debug.Log(string.Format(" >> change({0:F2})! - Corr: {1:F2}, LastY: {2:F2}, TgrY: {3:F2} at time {4:F2}", // (fNewDistance - fFootDistanceInitial), fFootDistance, fLastTgtY, targetPos.y, fFootDistanceTime)); } } else { fFootDistanceTime = fNewDistanceTime; } } if (bodyRoot != null) { bodyRoot.position = smoothFactor != 0f ? Vector3.Lerp(bodyRoot.position, targetPos, smoothFactor * Time.deltaTime) : targetPos; } else { transform.position = smoothFactor != 0f ? Vector3.Lerp(transform.position, targetPos, smoothFactor * Time.deltaTime) : targetPos; } } // Set model's arms to be in T-pose protected void SetModelArmsInTpose() { Vector3 vTposeLeftDir = transform.TransformDirection(Vector3.left); Vector3 vTposeRightDir = transform.TransformDirection(Vector3.right); Transform transLeftUarm = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.ShoulderLeft, false)); // animator.GetBoneTransform(HumanBodyBones.LeftUpperArm); Transform transLeftLarm = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.ElbowLeft, false)); // animator.GetBoneTransform(HumanBodyBones.LeftLowerArm); Transform transLeftHand = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.WristLeft, false)); // animator.GetBoneTransform(HumanBodyBones.LeftHand); if (transLeftUarm != null && transLeftLarm != null) { Vector3 vUarmLeftDir = transLeftLarm.position - transLeftUarm.position; float fUarmLeftAngle = Vector3.Angle(vUarmLeftDir, vTposeLeftDir); if (Mathf.Abs(fUarmLeftAngle) >= 5f) { Quaternion vFixRotation = Quaternion.FromToRotation(vUarmLeftDir, vTposeLeftDir); transLeftUarm.rotation = vFixRotation * transLeftUarm.rotation; } if (transLeftHand != null) { Vector3 vLarmLeftDir = transLeftHand.position - transLeftLarm.position; float fLarmLeftAngle = Vector3.Angle(vLarmLeftDir, vTposeLeftDir); if (Mathf.Abs(fLarmLeftAngle) >= 5f) { Quaternion vFixRotation = Quaternion.FromToRotation(vLarmLeftDir, vTposeLeftDir); transLeftLarm.rotation = vFixRotation * transLeftLarm.rotation; } } } Transform transRightUarm = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.ShoulderRight, false)); // animator.GetBoneTransform(HumanBodyBones.RightUpperArm); Transform transRightLarm = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.ElbowRight, false)); // animator.GetBoneTransform(HumanBodyBones.RightLowerArm); Transform transRightHand = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.WristRight, false)); // animator.GetBoneTransform(HumanBodyBones.RightHand); if (transRightUarm != null && transRightLarm != null) { Vector3 vUarmRightDir = transRightLarm.position - transRightUarm.position; float fUarmRightAngle = Vector3.Angle(vUarmRightDir, vTposeRightDir); if (Mathf.Abs(fUarmRightAngle) >= 5f) { Quaternion vFixRotation = Quaternion.FromToRotation(vUarmRightDir, vTposeRightDir); transRightUarm.rotation = vFixRotation * transRightUarm.rotation; } if (transRightHand != null) { Vector3 vLarmRightDir = transRightHand.position - transRightLarm.position; float fLarmRightAngle = Vector3.Angle(vLarmRightDir, vTposeRightDir); if (Mathf.Abs(fLarmRightAngle) >= 5f) { Quaternion vFixRotation = Quaternion.FromToRotation(vLarmRightDir, vTposeRightDir); transRightLarm.rotation = vFixRotation * transRightLarm.rotation; } } } } // If the bones to be mapped have been declared, map that bone to the model. protected virtual void MapBones() { for (int boneIndex = 0; boneIndex < bones.Length; boneIndex++) { if (!boneIndex2MecanimMap.ContainsKey(boneIndex)) continue; bones[boneIndex] = animatorComponent ? animatorComponent.GetBoneTransform(boneIndex2MecanimMap[boneIndex]) : null; } //// map finger bones, too //fingerBones = new Transform[fingerIndex2MecanimMap.Count]; //for (int boneIndex = 0; boneIndex < fingerBones.Length; boneIndex++) //{ // if (!fingerIndex2MecanimMap.ContainsKey(boneIndex)) // continue; // fingerBones[boneIndex] = animatorComponent ? animatorComponent.GetBoneTransform(fingerIndex2MecanimMap[boneIndex]) : null; //} } // Capture the initial rotations of the bones protected void GetInitialRotations() { // save the initial rotation if (offsetNode != null) { offsetNodePos = offsetNode.transform.position; offsetNodeRot = offsetNode.transform.rotation; } initialPosition = transform.position; initialRotation = transform.rotation; transform.rotation = Quaternion.identity; // save the body root initial position if (bodyRoot != null) { bodyRootPosition = bodyRoot.position; } else { bodyRootPosition = transform.position; } if (offsetNode != null) { bodyRootPosition = bodyRootPosition - offsetNodePos; } // save the initial bone rotations for (int i = 0; i < bones.Length; i++) { if (bones[i] != null) { initialRotations[i] = bones[i].rotation; localRotations[i] = bones[i].localRotation; } } // Restore the initial rotation transform.rotation = initialRotation; } // Converts kinect joint rotation to avatar joint rotation, depending on joint initial rotation and offset rotation protected Quaternion Kinect2AvatarRot(Quaternion jointRotation, int boneIndex) { Quaternion newRotation = jointRotation * initialRotations[boneIndex]; //newRotation = initialRotation * newRotation; // if(offsetNode != null) // { // newRotation = offsetNode.transform.rotation * newRotation; // } // else if (!externalRootMotion) // fix by Mathias Parger { newRotation = initialRotation * newRotation; } return newRotation; } // Converts Kinect position to avatar skeleton position, depending on initial position, mirroring and move rate protected Vector3 Kinect2AvatarPos(Vector3 jointPosition, bool bMoveVertically) { float xPos = (jointPosition.x - offsetPos.x) * moveRate; float yPos = (jointPosition.y - offsetPos.y) * moveRate; float zPos = !mirroredMovement && !posRelativeToCamera ? (-jointPosition.z - offsetPos.z) * moveRate : (jointPosition.z - offsetPos.z) * moveRate; Vector3 newPosition = new Vector3(xPos, bMoveVertically ? yPos : 0f, zPos); Quaternion posRotation = mirroredMovement ? Quaternion.Euler(0f, 180f, 0f) * initialRotation : initialRotation; newPosition = posRotation * newPosition; if (offsetNode != null) { //newPosition += offsetNode.transform.position; newPosition = offsetNode.transform.position; } return newPosition; } // returns distance from the given transform to the underlying object. The player must be in IgnoreRaycast layer. protected virtual float GetTransformDistanceToGround(Transform trans) { if (!trans) return 0f; // RaycastHit hit; // if(Physics.Raycast(trans.position, Vector3.down, out hit, 2f, raycastLayers)) // { // return -hit.distance; // } // else if(Physics.Raycast(trans.position, Vector3.up, out hit, 2f, raycastLayers)) // { // return hit.distance; // } // else // { // if (trans.position.y < 0) // return -trans.position.y; // else // return 1000f; // } return -trans.position.y; } // returns the lower distance distance from left or right foot to the ground, or 1000f if no LF/RF transforms are found protected virtual float GetDistanceToGround() { if (leftFoot == null && rightFoot == null) { leftFoot = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.FootLeft, false)); rightFoot = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.FootRight, false)); if (leftFoot == null || rightFoot == null) { leftFoot = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.AnkleLeft, false)); rightFoot = GetBoneTransform(GetBoneIndexByJoint(KinectInterop.JointType.AnkleRight, false)); } } float fDistMin = 1000f; float fDistLeft = leftFoot ? GetTransformDistanceToGround(leftFoot) : fDistMin; float fDistRight = rightFoot ? GetTransformDistanceToGround(rightFoot) : fDistMin; fDistMin = Mathf.Abs(fDistLeft) < Mathf.Abs(fDistRight) ? fDistLeft : fDistRight; if (fDistMin == 1000f) { fDistMin = 0f; // fFootDistanceInitial; } // Debug.Log (string.Format ("LFootY: {0:F2}, Dist: {1:F2}, RFootY: {2:F2}, Dist: {3:F2}, Min: {4:F2}", leftFoot ? leftFoot.position.y : 0f, fDistLeft, // rightFoot ? rightFoot.position.y : 0f, fDistRight, fDistMin)); return fDistMin; } // protected void OnCollisionEnter(Collision col) // { // Debug.Log("Collision entered"); // } // // protected void OnCollisionExit(Collision col) // { // Debug.Log("Collision exited"); // } // dictionaries to speed up bone processing protected static readonly Dictionary boneIndex2MecanimMap = new Dictionary { {0, HumanBodyBones.Hips}, {1, HumanBodyBones.Spine}, {2, HumanBodyBones.Chest}, {3, HumanBodyBones.Neck}, {4, HumanBodyBones.Head}, {5, HumanBodyBones.LeftShoulder}, {6, HumanBodyBones.LeftUpperArm}, {7, HumanBodyBones.LeftLowerArm}, {8, HumanBodyBones.LeftHand}, {9, HumanBodyBones.RightShoulder}, {10, HumanBodyBones.RightUpperArm}, {11, HumanBodyBones.RightLowerArm}, {12, HumanBodyBones.RightHand}, {13, HumanBodyBones.LeftUpperLeg}, {14, HumanBodyBones.LeftLowerLeg}, {15, HumanBodyBones.LeftFoot}, // {16, HumanBodyBones.LeftToes}, {17, HumanBodyBones.RightUpperLeg}, {18, HumanBodyBones.RightLowerLeg}, {19, HumanBodyBones.RightFoot}, // {20, HumanBodyBones.RightToes}, }; protected static readonly Dictionary boneIndex2JointMap = new Dictionary { {0, KinectInterop.JointType.Pelvis}, {1, KinectInterop.JointType.SpineNaval}, {2, KinectInterop.JointType.SpineChest}, {3, KinectInterop.JointType.Neck}, {4, KinectInterop.JointType.Head}, {5, KinectInterop.JointType.ClavicleLeft}, {6, KinectInterop.JointType.ShoulderLeft}, {7, KinectInterop.JointType.ElbowLeft}, {8, KinectInterop.JointType.WristLeft}, {9, KinectInterop.JointType.ClavicleRight}, {10, KinectInterop.JointType.ShoulderRight}, {11, KinectInterop.JointType.ElbowRight}, {12, KinectInterop.JointType.WristRight}, {13, KinectInterop.JointType.HipLeft}, {14, KinectInterop.JointType.KneeLeft}, {15, KinectInterop.JointType.AnkleLeft}, {16, KinectInterop.JointType.FootLeft}, {17, KinectInterop.JointType.HipRight}, {18, KinectInterop.JointType.KneeRight}, {19, KinectInterop.JointType.AnkleRight}, {20, KinectInterop.JointType.FootRight}, }; protected static readonly Dictionary boneIndex2MirrorJointMap = new Dictionary { {0, KinectInterop.JointType.Pelvis}, {1, KinectInterop.JointType.SpineNaval}, {2, KinectInterop.JointType.SpineChest}, {3, KinectInterop.JointType.Neck}, {4, KinectInterop.JointType.Head}, {5, KinectInterop.JointType.ClavicleRight}, {6, KinectInterop.JointType.ShoulderRight}, {7, KinectInterop.JointType.ElbowRight}, {8, KinectInterop.JointType.WristRight}, {9, KinectInterop.JointType.ClavicleLeft}, {10, KinectInterop.JointType.ShoulderLeft}, {11, KinectInterop.JointType.ElbowLeft}, {12, KinectInterop.JointType.WristLeft}, {13, KinectInterop.JointType.HipRight}, {14, KinectInterop.JointType.KneeRight}, {15, KinectInterop.JointType.AnkleRight}, {16, KinectInterop.JointType.FootRight}, {17, KinectInterop.JointType.HipLeft}, {18, KinectInterop.JointType.KneeLeft}, {19, KinectInterop.JointType.AnkleLeft}, {20, KinectInterop.JointType.FootLeft}, }; protected static readonly Dictionary jointMap2boneIndex = new Dictionary { {KinectInterop.JointType.Pelvis, 0}, {KinectInterop.JointType.SpineNaval, 1}, {KinectInterop.JointType.SpineChest, 2}, {KinectInterop.JointType.Neck, 3}, {KinectInterop.JointType.Head, 4}, {KinectInterop.JointType.ClavicleLeft, 5}, {KinectInterop.JointType.ShoulderLeft, 6}, {KinectInterop.JointType.ElbowLeft, 7}, {KinectInterop.JointType.WristLeft, 8}, {KinectInterop.JointType.ClavicleRight, 9}, {KinectInterop.JointType.ShoulderRight, 10}, {KinectInterop.JointType.ElbowRight, 11}, {KinectInterop.JointType.WristRight, 12}, {KinectInterop.JointType.HipLeft, 13}, {KinectInterop.JointType.KneeLeft, 14}, {KinectInterop.JointType.AnkleLeft, 15}, {KinectInterop.JointType.FootLeft, 16}, {KinectInterop.JointType.HipRight, 17}, {KinectInterop.JointType.KneeRight, 18}, {KinectInterop.JointType.AnkleRight, 19}, {KinectInterop.JointType.FootRight, 20}, }; protected static readonly Dictionary mirrorJointMap2boneIndex = new Dictionary { {KinectInterop.JointType.Pelvis, 0}, {KinectInterop.JointType.SpineNaval, 1}, {KinectInterop.JointType.SpineChest, 2}, {KinectInterop.JointType.Neck, 3}, {KinectInterop.JointType.Head, 4}, {KinectInterop.JointType.ClavicleRight, 5}, {KinectInterop.JointType.ShoulderRight, 6}, {KinectInterop.JointType.ElbowRight, 7}, {KinectInterop.JointType.WristRight, 8}, {KinectInterop.JointType.ClavicleLeft, 9}, {KinectInterop.JointType.ShoulderLeft, 10}, {KinectInterop.JointType.ElbowLeft, 11}, {KinectInterop.JointType.WristLeft, 12}, {KinectInterop.JointType.HipRight, 13}, {KinectInterop.JointType.KneeRight, 14}, {KinectInterop.JointType.AnkleRight, 15}, {KinectInterop.JointType.FootRight, 16}, {KinectInterop.JointType.HipLeft, 17}, {KinectInterop.JointType.KneeLeft, 18}, {KinectInterop.JointType.AnkleLeft, 19}, {KinectInterop.JointType.FootLeft, 20}, }; } }