soundvision/UnityProject/Assets/ThridParty/AzureKinectExamples/KinectScripts/KinectUserManager.cs
2019-10-26 22:49:14 +02:00

421 lines
16 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace com.rfilkov.kinect
{
/// <summary>
/// Kinect user manager is the component that tracks the users in front of the sensor.
/// </summary>
public class KinectUserManager : MonoBehaviour
{
[System.Serializable]
public class KinectUserEvent : UnityEvent<ulong, int> { }
/// <summary>
/// Fired when new user gets detected.
/// </summary>
//public event System.Action<ulong, int> OnUserAdded;
public KinectUserEvent OnUserAdded;
/// <summary>
/// Fired when user gets removed.
/// </summary>
//public event System.Action<ulong, int> OnUserRemoved;
public KinectUserEvent OnUserRemoved;
// List of all users
internal List<ulong> alUserIds = new List<ulong>();
internal Dictionary<ulong, int> dictUserIdToIndex = new Dictionary<ulong, int>();
internal ulong[] aUserIndexIds = new ulong[KinectInterop.Constants.MaxBodyCount];
internal Dictionary<ulong, float> dictUserIdToTime = new Dictionary<ulong, float>();
// Primary (first or closest) user ID
internal ulong liPrimaryUserId = 0;
// Calibration gesture data for each player
internal Dictionary<ulong, KinectGestureManager.GestureData> playerCalibrationData = new Dictionary<ulong, KinectGestureManager.GestureData>();
// reference to KM
private KinectManager kinectManager = null;
protected virtual void Start()
{
kinectManager = KinectManager.Instance;
}
/// <summary>
/// Rearranges the user indices, according to the current criteria
/// </summary>
public virtual void RearrangeUserIndices(KinectManager.UserDetectionOrder userDetectionOrder)
{
if (userDetectionOrder != KinectManager.UserDetectionOrder.Appearance)
{
// get current user positions
Vector3[] userPos = new Vector3[aUserIndexIds.Length];
for (int i = 0; i < aUserIndexIds.Length; i++)
{
ulong userId = aUserIndexIds[i];
userPos[i] = userId != 0 ? kinectManager.GetUserPosition(userId) : Vector3.zero;
}
// bubble sort
bool reorderDone = false;
for (int i = aUserIndexIds.Length - 1; i >= 1; i--)
{
bool switchDone = false;
for (int j = 0; j < i; j++)
{
float userDist1 = 0f;
if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
userDist1 = Mathf.Abs(userPos[j].x) + Mathf.Abs(userPos[j].z);
else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
userDist1 = userPos[j].x;
if (Mathf.Abs(userDist1) < 0.01f)
userDist1 = 1000f; // far away
float userDist2 = 0f;
if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
userDist2 = Mathf.Abs(userPos[j + 1].x) + Mathf.Abs(userPos[j + 1].z);
else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
userDist2 = userPos[j + 1].x;
if (Mathf.Abs(userDist2) < 0.01f)
userDist2 = 1000f; // far away
if (userDist1 > userDist2)
{
// switch them
ulong tmpUserId = aUserIndexIds[j];
aUserIndexIds[j] = aUserIndexIds[j + 1];
aUserIndexIds[j + 1] = tmpUserId;
reorderDone = switchDone = true;
}
}
if (!switchDone) // check for sorted array
break;
}
if (reorderDone)
{
System.Text.StringBuilder sbUsersOrder = new System.Text.StringBuilder();
sbUsersOrder.Append("Users reindexed: ");
for (int i = 0; i < aUserIndexIds.Length; i++)
{
if (aUserIndexIds[i] != 0)
{
sbUsersOrder.Append(i).Append(":").Append(aUserIndexIds[i]).Append(" ");
}
}
Debug.Log(sbUsersOrder.ToString());
}
}
}
// Returns empty user slot for the given user Id
protected virtual int GetEmptyUserSlot(ulong userId, int bodyIndex, ref KinectInterop.BodyData[] alTrackedBodies, KinectManager.UserDetectionOrder userDetectionOrder)
{
// rearrange current users
RearrangeUserIndices(userDetectionOrder);
int uidIndex = -1;
if (userDetectionOrder != KinectManager.UserDetectionOrder.Appearance)
{
// add the new user, depending on the distance
Vector3 userPos = alTrackedBodies[bodyIndex].position;
float userDist = 0f;
if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
userDist = Mathf.Abs(userPos.x) + Mathf.Abs(userPos.z);
else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
userDist = userPos.x;
for (int i = 0; i < aUserIndexIds.Length; i++)
{
if (aUserIndexIds[i] == 0)
{
// free user slot
uidIndex = i;
break;
}
else
{
ulong uidUserId = aUserIndexIds[i];
Vector3 uidUserPos = kinectManager.GetUserPosition(uidUserId);
float uidUserDist = 0;
if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
uidUserDist = Mathf.Abs(uidUserPos.x) + Mathf.Abs(uidUserPos.z);
else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
uidUserDist = uidUserPos.x;
if (userDist <= uidUserDist)
{
// current user is left to the compared one
for (int u = (aUserIndexIds.Length - 2); u >= i; u--)
{
aUserIndexIds[u + 1] = aUserIndexIds[u];
if (aUserIndexIds[u] != 0)
{
Debug.Log(string.Format("Reindexing user {0} to {1}, ID: {2}.", u, u + 1, aUserIndexIds[u]));
}
}
aUserIndexIds[i] = 0; // cleanup current index
uidIndex = i;
break;
}
}
}
}
else
{
// look for the 1st available slot
for (int i = 0; i < aUserIndexIds.Length; i++)
{
if (aUserIndexIds[i] == 0)
{
uidIndex = i;
break;
}
}
}
return uidIndex;
}
// releases the user slot. rearranges the remaining users.
protected virtual void FreeEmptyUserSlot(int uidIndex, KinectManager.UserDetectionOrder userDetectionOrder)
{
aUserIndexIds[uidIndex] = 0;
if (userDetectionOrder != KinectManager.UserDetectionOrder.Appearance)
{
// rearrange the remaining users
for (int u = uidIndex; u < (aUserIndexIds.Length - 1); u++)
{
aUserIndexIds[u] = aUserIndexIds[u + 1];
if (aUserIndexIds[u + 1] != 0)
{
Debug.Log(string.Format("Reindexing user {0} to {1}, ID: {2}.", u + 1, u, aUserIndexIds[u + 1]));
}
}
// make sure the last slot is free
aUserIndexIds[aUserIndexIds.Length - 1] = 0;
}
// rearrange the remaining users
RearrangeUserIndices(userDetectionOrder);
}
// Adds UserId to the list of users
public virtual int CalibrateUser(ulong userId, int bodyIndex, ref KinectInterop.BodyData[] alTrackedBodies,
KinectManager.UserDetectionOrder userDetectionOrder, GestureType playerCalibrationPose, KinectGestureManager gestureManager)
{
if (!alUserIds.Contains(userId))
{
if (CheckForCalibrationPose(userId, bodyIndex, playerCalibrationPose, gestureManager, ref alTrackedBodies))
{
//int uidIndex = alUserIds.Count;
int uidIndex = GetEmptyUserSlot(userId, bodyIndex, ref alTrackedBodies, userDetectionOrder);
if (uidIndex >= 0)
{
aUserIndexIds[uidIndex] = userId;
}
else
{
// no empty user-index slot
return -1;
}
dictUserIdToIndex[userId] = bodyIndex;
dictUserIdToTime[userId] = Time.time;
alUserIds.Add(userId);
// set primary user-id, if there is none
if (liPrimaryUserId == 0 && aUserIndexIds.Length > 0)
{
liPrimaryUserId = aUserIndexIds[0]; // userId
}
return uidIndex;
}
}
return -1;
}
// fires the OnUserAdded-event
internal void FireOnUserAdded(ulong userId, int userIndex)
{
OnUserAdded?.Invoke(userId, userIndex);
}
// Remove a lost UserId
public virtual int RemoveUser(ulong userId, KinectManager.UserDetectionOrder userDetectionOrder)
{
//int uidIndex = alUserIds.IndexOf(userId);
int uidIndex = System.Array.IndexOf(aUserIndexIds, userId);
// clear calibration data for this user
if (playerCalibrationData.ContainsKey(userId))
{
playerCalibrationData.Remove(userId);
}
// clean up the outdated calibration data in the data dictionary
List<ulong> alCalDataKeys = new List<ulong>(playerCalibrationData.Keys);
foreach (ulong calUserID in alCalDataKeys)
{
KinectGestureManager.GestureData gestureData = playerCalibrationData[calUserID];
if ((gestureData.timestamp + 60f) < Time.realtimeSinceStartup)
{
playerCalibrationData.Remove(calUserID);
}
}
alCalDataKeys.Clear();
// remove user-id from the global users lists
dictUserIdToIndex.Remove(userId);
dictUserIdToTime.Remove(userId);
alUserIds.Remove(userId);
if (uidIndex >= 0)
{
FreeEmptyUserSlot(uidIndex, userDetectionOrder);
}
// if this was the primary user, update the primary user-id
if (liPrimaryUserId == userId)
{
if (aUserIndexIds.Length > 0)
{
liPrimaryUserId = aUserIndexIds[0];
}
else
{
liPrimaryUserId = 0;
}
}
if (alUserIds.Count == 0)
{
//Debug.Log("Waiting for users.");
}
return uidIndex;
}
// fires the OnUserRemoved-event
internal void FireOnUserRemoved(ulong userId, int userIndex)
{
OnUserRemoved?.Invoke(userId, userIndex);
}
// check if the calibration pose is complete for given user
protected virtual bool CheckForCalibrationPose(ulong UserId, int bodyIndex, GestureType calibrationGesture,
KinectGestureManager gestureManager, ref KinectInterop.BodyData[] alTrackedBodies)
{
if (calibrationGesture == GestureType.None)
return true;
if (!gestureManager)
return false;
KinectGestureManager.GestureData gestureData = playerCalibrationData.ContainsKey(UserId) ?
playerCalibrationData[UserId] : new KinectGestureManager.GestureData();
// init gesture data if needed
if (gestureData.userId != UserId)
{
gestureData.userId = UserId;
gestureData.gesture = calibrationGesture;
gestureData.state = 0;
gestureData.timestamp = Time.realtimeSinceStartup;
gestureData.joint = 0;
gestureData.progress = 0f;
gestureData.complete = false;
gestureData.cancelled = false;
}
// get joint positions and tracking
int iAllJointsCount = (int)KinectInterop.JointType.Count;
bool[] playerJointsTracked = new bool[iAllJointsCount];
Vector3[] playerJointsPos = new Vector3[iAllJointsCount];
int[] aiNeededJointIndexes = gestureManager.GetNeededJointIndexes();
int iNeededJointsCount = aiNeededJointIndexes.Length;
for (int i = 0; i < iNeededJointsCount; i++)
{
int joint = aiNeededJointIndexes[i];
if (joint >= 0)
{
KinectInterop.JointData jointData = alTrackedBodies[bodyIndex].joint[joint];
playerJointsTracked[joint] = jointData.trackingState != KinectInterop.TrackingState.NotTracked;
playerJointsPos[joint] = jointData.kinectPos;
if (!playerJointsTracked[joint] && (joint == (int)KinectInterop.JointType.Neck))
{
KinectInterop.JointData lShoulderData = alTrackedBodies[bodyIndex].joint[(int)KinectInterop.JointType.ShoulderLeft];
KinectInterop.JointData rShoulderData = alTrackedBodies[bodyIndex].joint[(int)KinectInterop.JointType.ShoulderRight];
if (lShoulderData.trackingState != KinectInterop.TrackingState.NotTracked && rShoulderData.trackingState != KinectInterop.TrackingState.NotTracked)
{
playerJointsTracked[joint] = true;
playerJointsPos[joint] = (lShoulderData.kinectPos + rShoulderData.kinectPos) / 2f;
}
}
}
}
// estimate the gesture progess
gestureManager.CheckForGesture(UserId, ref gestureData, Time.realtimeSinceStartup,
ref playerJointsPos, ref playerJointsTracked);
playerCalibrationData[UserId] = gestureData;
// check if gesture is complete
if (gestureData.complete)
{
gestureData.userId = 0;
playerCalibrationData[UserId] = gestureData;
return true;
}
return false;
}
}
}