├── Hololens-Receiver ├── BodyDataReceiver.cs ├── BodyView.cs └── CustomMessages2.cs ├── Hololens-Sender ├── BodyDataConverter.cs ├── BodyDataSender.cs ├── BodyView.cs ├── CustomMessages2.cs └── KinectBodyData.cs └── README.md /Hololens-Receiver/BodyDataReceiver.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * BodyDataReceiver.cs 3 | * 4 | * Receives body data from the network 5 | * Requires CustomMessages2.cs 6 | */ 7 | 8 | using HoloToolkit.Sharing; 9 | using HoloToolkit.Unity; 10 | using System.Collections.Generic; 11 | using UnityEngine; 12 | 13 | // Receives the body data messages 14 | public class BodyDataReceiver : Singleton { 15 | 16 | private Dictionary _Bodies = new Dictionary(); 17 | 18 | public Dictionary GetData() { 19 | return _Bodies; 20 | } 21 | 22 | void Start() { 23 | CustomMessages2.Instance.MessageHandlers[CustomMessages2.TestMessageID.BodyData] = 24 | this.UpdateBodyData; 25 | } 26 | 27 | // Called when reading in Kinect body data 28 | void UpdateBodyData(NetworkInMessage msg) { 29 | // Parse the message 30 | long trackingID = msg.ReadInt64(); 31 | Vector3 jointPos; 32 | Vector3[] jointPositions = new Vector3[25]; 33 | 34 | for (int i = 0; i < 25; i++) { 35 | jointPos = CustomMessages2.Instance.ReadVector3(msg); 36 | jointPositions[i] = jointPos; 37 | } 38 | 39 | _Bodies[trackingID] = jointPositions; 40 | } 41 | } -------------------------------------------------------------------------------- /Hololens-Receiver/BodyView.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * BodyView.cs 3 | * 4 | * Displays spheres for Kinect body joints 5 | * Requires the BodyDataConverter script or the BodyDataReceiver script 6 | */ 7 | 8 | using UnityEngine; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | 12 | public class BodyView : MonoBehaviour { 13 | 14 | public GameObject BodySourceManager; 15 | 16 | // Dictionary relating tracking IDs to displayed GameObjects 17 | private Dictionary _Bodies = new Dictionary(); 18 | private BodyDataReceiver _BodyDataReceiver; 19 | 20 | void Update() { 21 | 22 | if (BodySourceManager == null) { 23 | return; 24 | } 25 | 26 | // Dictionary of tracked bodies from the Kinect or from data 27 | // sent over the server 28 | Dictionary bodies; 29 | 30 | // Is the body data coming from the BodyDataReceriver script? 31 | _BodyDataReceiver = BodySourceManager.GetComponent(); 32 | if (_BodyDataReceiver == null) { 33 | return; 34 | } else { 35 | bodies = _BodyDataReceiver.GetData(); 36 | } 37 | 38 | if (bodies == null) { 39 | return; 40 | } 41 | 42 | // Delete untracked bodies 43 | List trackedIDs = new List(bodies.Keys); 44 | List knownIDs = new List(_Bodies.Keys); 45 | foreach (long trackingID in knownIDs) { 46 | 47 | if (!trackedIDs.Contains(trackingID)) { 48 | Destroy(_Bodies[trackingID]); 49 | _Bodies.Remove(trackingID); 50 | } 51 | } 52 | 53 | // Add and update tracked bodies 54 | foreach (long trackingID in bodies.Keys) { 55 | 56 | // Add tracked bodies if they are not already being displayed 57 | if (!_Bodies.ContainsKey(trackingID)) { 58 | _Bodies[trackingID] = CreateBodyObject(trackingID); 59 | } 60 | 61 | // Update the positions of each body's joints 62 | RefreshBodyObject(bodies[trackingID], _Bodies[trackingID]); 63 | } 64 | } 65 | 66 | // Create a GameObject given a tracking ID 67 | private GameObject CreateBodyObject(long id) { 68 | 69 | GameObject body = new GameObject("Body:" + id); 70 | 71 | for (int i = 0; i < 25; i++) { 72 | GameObject jointObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); 73 | 74 | jointObj.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f); 75 | jointObj.name = i.ToString(); 76 | jointObj.transform.parent = body.transform; 77 | } 78 | 79 | return body; 80 | } 81 | 82 | // Update the joint GameObjects of a given body 83 | private void RefreshBodyObject(Vector3[] jointPositions, GameObject bodyObj) { 84 | 85 | for (int i = 0; i < 25; i++) { 86 | Vector3 jointPos = jointPositions[i]; 87 | 88 | Transform jointObj = bodyObj.transform.FindChild(i.ToString()); 89 | jointObj.localPosition = jointPos; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Hololens-Receiver/CustomMessages2.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * CustomMessages2.cs 3 | * 4 | * Allows for sending body data as custom messages to the Hololens 5 | * Requires a SharingStage GameObject 6 | */ 7 | 8 | using HoloToolkit.Sharing; 9 | using HoloToolkit.Unity; 10 | using System.Collections.Generic; 11 | using UnityEngine; 12 | 13 | public class CustomMessages2 : Singleton { 14 | 15 | // Message enum containing information bytes to share 16 | // The first message type has to start with UserMessageIDStart 17 | // so as not to conflict with HoloToolkit internal messages 18 | public enum TestMessageID : byte { 19 | BodyData = MessageID.UserMessageIDStart, 20 | Max 21 | } 22 | 23 | public enum UserMessageChannels { 24 | Anchors = MessageChannel.UserMessageChannelStart, 25 | } 26 | 27 | // Cache the local user's ID to use when sending messages 28 | public long localUserID { 29 | get; set; 30 | } 31 | 32 | public delegate void MessageCallback(NetworkInMessage msg); 33 | private Dictionary _MessageHandlers = 34 | new Dictionary(); 35 | public Dictionary MessageHandlers { 36 | get { 37 | return _MessageHandlers; 38 | } 39 | } 40 | 41 | // Helper object that we use to route incoming message callbacks to the member 42 | // functions of this class 43 | NetworkConnectionAdapter connectionAdapter; 44 | 45 | // Cache the connection object for the sharing service 46 | NetworkConnection serverConnection; 47 | 48 | void Start() { 49 | InitializeMessageHandlers(); 50 | } 51 | 52 | void InitializeMessageHandlers() { 53 | 54 | SharingStage sharingStage = SharingStage.Instance; 55 | 56 | if (sharingStage == null) { 57 | Debug.Log("Cannot Initialize CustomMessages. No SharingStage instance found."); 58 | return; 59 | } 60 | 61 | serverConnection = sharingStage.Manager.GetServerConnection(); 62 | if (serverConnection == null) { 63 | Debug.Log("Cannot initialize CustomMessages. Cannot get a server connection."); 64 | return; 65 | } 66 | 67 | connectionAdapter = new NetworkConnectionAdapter(); 68 | connectionAdapter.MessageReceivedCallback += OnMessageReceived; 69 | 70 | // Cache the local user ID 71 | this.localUserID = SharingStage.Instance.Manager.GetLocalUser().GetID(); 72 | 73 | for (byte index = (byte)TestMessageID.BodyData; index < (byte)TestMessageID.Max; index++) { 74 | 75 | if (MessageHandlers.ContainsKey((TestMessageID)index) == false) { 76 | MessageHandlers.Add((TestMessageID)index, null); 77 | } 78 | 79 | serverConnection.AddListener(index, connectionAdapter); 80 | } 81 | } 82 | 83 | private NetworkOutMessage CreateMessage(byte MessageType) { 84 | NetworkOutMessage msg = serverConnection.CreateMessage(MessageType); 85 | msg.Write(MessageType); 86 | return msg; 87 | } 88 | 89 | // Sends body data in the form of the tracking ID and then each joint's 90 | // Vector3 coordinates 91 | public void SendBodyData(ulong trackingID, Vector3[] bodyData) { 92 | // If we are connected to a session, broadcast our info 93 | if (this.serverConnection != null && this.serverConnection.IsConnected()) { 94 | // Create an outgoing network message to contain all the info we want to send 95 | NetworkOutMessage msg = CreateMessage((byte)TestMessageID.BodyData); 96 | 97 | msg.Write(trackingID); 98 | 99 | foreach (Vector3 jointPos in bodyData) { 100 | AppendVector3(msg, jointPos); 101 | } 102 | 103 | // Send the message as a broadcast 104 | this.serverConnection.Broadcast( 105 | msg, 106 | MessagePriority.Immediate, 107 | MessageReliability.UnreliableSequenced, 108 | MessageChannel.Avatar); 109 | } 110 | } 111 | 112 | void OnDestroy() { 113 | 114 | if (this.serverConnection != null) { 115 | 116 | for (byte index = (byte)TestMessageID.BodyData; index < (byte)TestMessageID.Max; index++) { 117 | this.serverConnection.RemoveListener(index, this.connectionAdapter); 118 | } 119 | this.connectionAdapter.MessageReceivedCallback -= OnMessageReceived; 120 | } 121 | } 122 | 123 | void OnMessageReceived(NetworkConnection connection, NetworkInMessage msg) { 124 | 125 | byte messageType = msg.ReadByte(); 126 | MessageCallback messageHandler = MessageHandlers[(TestMessageID)messageType]; 127 | if (messageHandler != null) { 128 | messageHandler(msg); 129 | } 130 | } 131 | 132 | #region HelperFunctionsForWriting 133 | 134 | void AppendVector3(NetworkOutMessage msg, Vector3 vector) { 135 | msg.Write(vector.x); 136 | msg.Write(vector.y); 137 | msg.Write(vector.z); 138 | } 139 | 140 | #endregion HelperFunctionsForWriting 141 | 142 | #region HelperFunctionsForReading 143 | 144 | public Vector3 ReadVector3(NetworkInMessage msg) { 145 | return new Vector3(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat()); 146 | } 147 | 148 | #endregion HelperFunctionsForReading 149 | } -------------------------------------------------------------------------------- /Hololens-Sender/BodyDataConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * BodyDataConverter.cs 3 | * 4 | * Converts Kinect body data into a format that can easily be 5 | * sent and read over the network 6 | * Requires KinectBodyData.cs 7 | */ 8 | 9 | using UnityEngine; 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | using Kinect = Windows.Kinect; 13 | 14 | public class BodyDataConverter : MonoBehaviour { 15 | 16 | public GameObject KinectBodyData; 17 | 18 | // Dictionary that maps body trackingIDs to an array of joint locations 19 | private Dictionary _Bodies = 20 | new Dictionary(); 21 | private KinectBodyData _BodyManager; 22 | 23 | // Public function so other scripts can send out the data 24 | public Dictionary GetData() { 25 | return _Bodies; 26 | } 27 | 28 | void Update() { 29 | 30 | if (KinectBodyData == null) { 31 | return; 32 | } 33 | 34 | _BodyManager = KinectBodyData.GetComponent(); 35 | if (_BodyManager == null) { 36 | return; 37 | } 38 | 39 | Kinect.Body[] data = _BodyManager.GetData(); 40 | if (data == null) { 41 | return; 42 | } 43 | 44 | // Get tracked IDs 45 | List trackedIds = new List(); 46 | foreach (var body in data) { 47 | 48 | if (body == null) { 49 | continue; 50 | } 51 | 52 | if (body.IsTracked) { 53 | trackedIds.Add(body.TrackingId); 54 | } 55 | } 56 | 57 | List knownIds = new List(_Bodies.Keys); 58 | 59 | // Delete untracked bodies 60 | foreach (ulong trackingId in knownIds){ 61 | 62 | if (!trackedIds.Contains(trackingId)) { 63 | _Bodies.Remove(trackingId); 64 | } 65 | } 66 | 67 | // If a body is tracked, update the dictionary 68 | foreach (var body in data) { 69 | 70 | if (body == null) { 71 | continue; 72 | } 73 | 74 | if (body.IsTracked) { 75 | 76 | if (!_Bodies.ContainsKey(body.TrackingId)) { 77 | _Bodies[body.TrackingId] = CreateBodyData(); 78 | } 79 | 80 | UpdateBodyData(body, _Bodies[body.TrackingId]); 81 | } 82 | } 83 | } 84 | 85 | private Vector3[] CreateBodyData() { 86 | Vector3[] body = new Vector3[25]; 87 | return body; 88 | } 89 | 90 | private void UpdateBodyData(Kinect.Body body, Vector3[] bodyData) { 91 | 92 | for (Kinect.JointType jt = Kinect.JointType.SpineBase; jt <= Kinect.JointType.ThumbRight; jt++) { 93 | Kinect.Joint sourceJoint = body.Joints[jt]; 94 | bodyData[(int)jt] = GetVector3FromJoint(sourceJoint); 95 | } 96 | } 97 | 98 | private static Vector3 GetVector3FromJoint(Kinect.Joint joint) { 99 | return new Vector3(joint.Position.X * 10, joint.Position.Y * 10, joint.Position.Z * 10); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Hololens-Sender/BodyDataSender.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * BodyDataSender.cs 3 | * 4 | * Broadcasts body data over the network 5 | * Requires CustomMessages2.cs 6 | */ 7 | 8 | using HoloToolkit.Sharing; 9 | using HoloToolkit.Unity; 10 | using System.Collections.Generic; 11 | using UnityEngine; 12 | 13 | public class BodyDataSender : Singleton { 14 | 15 | public GameObject BodyDataConverter; 16 | 17 | private BodyDataConverter _BodyDataConverter; 18 | 19 | void Update() { 20 | 21 | // Get the parsed Kinect body data 22 | if (BodyDataConverter == null) { 23 | return; 24 | } 25 | 26 | _BodyDataConverter = BodyDataConverter.GetComponent(); 27 | if (_BodyDataConverter == null) { 28 | return; 29 | } 30 | 31 | Dictionary bodyData = _BodyDataConverter.GetData(); 32 | if (bodyData == null) { 33 | return; 34 | } 35 | 36 | // Send over the bodyData one tracked body at a time 37 | List trackingIDs = new List(bodyData.Keys); 38 | foreach (ulong trackingID in trackingIDs) { 39 | CustomMessages2.Instance.SendBodyData(trackingID, bodyData[trackingID]); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Hololens-Sender/BodyView.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * BodyView.cs 3 | * 4 | * Displays spheres for Kinect body joints 5 | * Requires the BodyDataConverter script or the BodyDataReceiver script 6 | */ 7 | 8 | using UnityEngine; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using Kinect = Windows.Kinect; 12 | 13 | public class BodyView : MonoBehaviour { 14 | 15 | public GameObject BodySourceManager; 16 | 17 | // Dictionary relating tracking IDs to displayed GameObjects 18 | private Dictionary _Bodies = new Dictionary(); 19 | private BodyDataConverter _BodyDataConverter; 20 | private BodyDataReceiver _BodyDataReceiver; 21 | 22 | void Update() { 23 | 24 | if (BodySourceManager == null) { 25 | return; 26 | } 27 | 28 | // Dictionary of tracked bodies from the Kinect or from data 29 | // sent over the server 30 | Dictionary bodies; 31 | 32 | // Is the body data coming from the BodyDataConverter script? 33 | _BodyDataConverter = BodySourceManager.GetComponent(); 34 | if (_BodyDataConverter == null) { 35 | // Is the body data coming from the BodyDataReceriver script? 36 | _BodyDataReceiver = BodySourceManager.GetComponent(); 37 | if (_BodyDataReceiver == null) { 38 | return; 39 | } else { 40 | bodies = _BodyDataReceiver.GetData(); 41 | } 42 | } else { 43 | bodies = _BodyDataConverter.GetData(); 44 | } 45 | 46 | if (bodies == null) { 47 | return; 48 | } 49 | 50 | // Delete untracked bodies 51 | List trackedIDs = new List(bodies.Keys); 52 | List knownIDs = new List(_Bodies.Keys); 53 | foreach (ulong trackingID in knownIDs) { 54 | 55 | if (!trackedIDs.Contains(trackingID)) { 56 | Destroy(_Bodies[trackingID]); 57 | _Bodies.Remove(trackingID); 58 | } 59 | } 60 | 61 | // Add and update tracked bodies 62 | foreach (ulong trackingID in bodies.Keys) { 63 | 64 | // Add tracked bodies if they are not already being displayed 65 | if (!_Bodies.ContainsKey(trackingID)) { 66 | _Bodies[trackingID] = CreateBodyObject(trackingID); 67 | } 68 | 69 | // Update the positions of each body's joints 70 | RefreshBodyObject(bodies[trackingID], _Bodies[trackingID]); 71 | } 72 | } 73 | 74 | // Create a GameObject given a tracking ID 75 | private GameObject CreateBodyObject(ulong id) { 76 | 77 | GameObject body = new GameObject("Body:" + id); 78 | 79 | for (int i = 0; i < 25; i++) { 80 | GameObject jointObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); 81 | 82 | jointObj.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f); 83 | jointObj.name = i.ToString(); 84 | jointObj.transform.parent = body.transform; 85 | } 86 | 87 | return body; 88 | } 89 | 90 | // Update the joint GameObjects of a given body 91 | private void RefreshBodyObject(Vector3[] jointPositions, GameObject bodyObj) { 92 | 93 | for (int i = 0; i < 25; i++) { 94 | Vector3 jointPos = jointPositions[i]; 95 | 96 | Transform jointObj = bodyObj.transform.FindChild(i.ToString()); 97 | jointObj.localPosition = jointPos; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Hololens-Sender/CustomMessages2.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * CustomMessages.cs 3 | * 4 | * Allows for sending body data as custom messages to the Hololens 5 | * Requires a SharingStage GameObject 6 | */ 7 | 8 | using HoloToolkit.Sharing; 9 | using HoloToolkit.Unity; 10 | using System.Collections.Generic; 11 | using UnityEngine; 12 | 13 | public class CustomMessages2 : Singleton { 14 | 15 | // Message enum containing information bytes to share 16 | // The first message type has to start with UserMessageIDStart 17 | // so as not to conflict with HoloToolkit internal messages 18 | public enum TestMessageID : byte { 19 | BodyData = MessageID.UserMessageIDStart, 20 | Max 21 | } 22 | 23 | public enum UserMessageChannels { 24 | Anchors = MessageChannel.UserMessageChannelStart, 25 | } 26 | 27 | // Cache the local user's ID to use when sending messages 28 | public long localUserID { 29 | get; set; 30 | } 31 | 32 | public delegate void MessageCallback(NetworkInMessage msg); 33 | private Dictionary _MessageHandlers = 34 | new Dictionary(); 35 | public Dictionary MessageHandlers { 36 | get { 37 | return _MessageHandlers; 38 | } 39 | } 40 | 41 | // Helper object that we use to route incoming message callbacks to the member 42 | // functions of this class 43 | NetworkConnectionAdapter connectionAdapter; 44 | 45 | // Cache the connection object for the sharing service 46 | NetworkConnection serverConnection; 47 | 48 | void Start() { 49 | InitializeMessageHandlers(); 50 | } 51 | 52 | void InitializeMessageHandlers() { 53 | 54 | SharingStage sharingStage = SharingStage.Instance; 55 | 56 | if (sharingStage == null) { 57 | Debug.Log("Cannot Initialize CustomMessages. No SharingStage instance found."); 58 | return; 59 | } 60 | 61 | serverConnection = sharingStage.Manager.GetServerConnection(); 62 | if (serverConnection == null) { 63 | Debug.Log("Cannot initialize CustomMessages. Cannot get a server connection."); 64 | return; 65 | } 66 | 67 | connectionAdapter = new NetworkConnectionAdapter(); 68 | connectionAdapter.MessageReceivedCallback += OnMessageReceived; 69 | 70 | // Cache the local user ID 71 | this.localUserID = SharingStage.Instance.Manager.GetLocalUser().GetID(); 72 | 73 | for (byte index = (byte)TestMessageID.BodyData; index < (byte)TestMessageID.Max; index++) { 74 | 75 | if (MessageHandlers.ContainsKey((TestMessageID)index) == false) { 76 | MessageHandlers.Add((TestMessageID)index, null); 77 | } 78 | 79 | serverConnection.AddListener(index, connectionAdapter); 80 | } 81 | } 82 | 83 | private NetworkOutMessage CreateMessage(byte MessageType) { 84 | NetworkOutMessage msg = serverConnection.CreateMessage(MessageType); 85 | msg.Write(MessageType); 86 | return msg; 87 | } 88 | 89 | // Sends body data in the form of the tracking ID and then each joint's 90 | // Vector3 coordinates 91 | public void SendBodyData(ulong trackingID, Vector3[] bodyData) { 92 | // If we are connected to a session, broadcast our info 93 | if (this.serverConnection != null && this.serverConnection.IsConnected()) { 94 | // Create an outgoing network message to contain all the info we want to send 95 | NetworkOutMessage msg = CreateMessage((byte)TestMessageID.BodyData); 96 | 97 | msg.Write((long)trackingID); 98 | 99 | foreach (Vector3 jointPos in bodyData) { 100 | AppendVector3(msg, jointPos); 101 | } 102 | 103 | // Send the message as a broadcast 104 | this.serverConnection.Broadcast( 105 | msg, 106 | MessagePriority.Immediate, 107 | MessageReliability.UnreliableSequenced, 108 | MessageChannel.Avatar); 109 | } 110 | } 111 | 112 | void OnDestroy() { 113 | 114 | if (this.serverConnection != null) { 115 | 116 | for (byte index = (byte)TestMessageID.BodyData; index < (byte)TestMessageID.Max; index++) { 117 | this.serverConnection.RemoveListener(index, this.connectionAdapter); 118 | } 119 | this.connectionAdapter.MessageReceivedCallback -= OnMessageReceived; 120 | } 121 | } 122 | 123 | void OnMessageReceived(NetworkConnection connection, NetworkInMessage msg) { 124 | 125 | byte messageType = msg.ReadByte(); 126 | MessageCallback messageHandler = MessageHandlers[(TestMessageID)messageType]; 127 | if (messageHandler != null) { 128 | messageHandler(msg); 129 | } 130 | } 131 | 132 | #region HelperFunctionsForWriting 133 | 134 | void AppendVector3(NetworkOutMessage msg, Vector3 vector) { 135 | msg.Write(vector.x); 136 | msg.Write(vector.y); 137 | msg.Write(vector.z); 138 | } 139 | 140 | #endregion HelperFunctionsForWriting 141 | 142 | #region HelperFunctionsForReading 143 | 144 | public Vector3 ReadVector3(NetworkInMessage msg) { 145 | return new Vector3(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat()); 146 | } 147 | 148 | #endregion HelperFunctionsForReading 149 | } -------------------------------------------------------------------------------- /Hololens-Sender/KinectBodyData.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * KinectBodyData.cs 3 | * 4 | * Retrieves Kinect skeletal data 5 | */ 6 | 7 | using UnityEngine; 8 | using System.Collections; 9 | using Windows.Kinect; 10 | 11 | public class KinectBodyData : MonoBehaviour { 12 | 13 | private KinectSensor _Sensor; 14 | private BodyFrameReader _Reader; 15 | private Body[] _Bodies = null; 16 | 17 | // Public function so other scripts can grab the Kinect Data 18 | public Body[] GetData() { 19 | return _Bodies; 20 | } 21 | 22 | // Open connections to the Kinect 23 | void Start () { 24 | _Sensor = KinectSensor.GetDefault(); 25 | 26 | if (_Sensor != null) { 27 | _Reader = _Sensor.BodyFrameSource.OpenReader(); 28 | 29 | if (!_Sensor.IsOpen) { 30 | _Sensor.Open(); 31 | } 32 | } 33 | } 34 | 35 | // Update Kinect body data on every frame 36 | void Update () { 37 | 38 | if (_Reader != null) { 39 | var frame = _Reader.AcquireLatestFrame(); 40 | 41 | if (frame != null) { 42 | 43 | if (_Bodies == null) { 44 | _Bodies = new Body[_Sensor.BodyFrameSource.BodyCount]; 45 | } 46 | 47 | frame.GetAndRefreshBodyData(_Bodies); 48 | 49 | frame.Dispose(); 50 | frame = null; 51 | } 52 | } 53 | } 54 | 55 | // Close connections to the Kinect 56 | void OnApplicationQuit() { 57 | 58 | if (_Reader != null) { 59 | _Reader.Dispose(); 60 | _Reader = null; 61 | } 62 | 63 | if (_Sensor != null) { 64 | 65 | if (_Sensor.IsOpen) { 66 | _Sensor.Close(); 67 | } 68 | 69 | _Sensor = null; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hololens-Kinect 2 | Unity scripts to send Kinect data to the Hololens. The kinect joints are displayed as spheres in front of the user. 3 | 4 | ## Sources 5 | These scripts are derived from the [Hololens Sharing](https://github.com/Microsoft/HoloToolkit-Unity/tree/master/Assets/HoloToolkit/Sharing/Tests) example in the [HoloToolkit](https://github.com/Microsoft/HoloToolkit-Unity) and the KinectView example from the [Kinect Tool Unity Pro Packages](https://developer.microsoft.com/en-us/windows/kinect/tools). 6 | 7 | ## Requirements 8 | In order to use these scripts, you must have: 9 | - **Windows 10** 10 | - **Kinect 2** 11 | - [**Hololens Unity**](http://unity3d.com/pages/windows/hololens) 12 | - [**Visual Studio 2015 Update 2**](https://developer.microsoft.com/en-us/windows/downloads) 13 | - The [**Kinect SDK 2.0**](https://developer.microsoft.com/en-us/windows/kinect/tools) 14 | 15 | In addition, you will need to download: 16 | - [**HoloToolkit**](https://github.com/Microsoft/HoloToolkit-Unity) - Make sure the Assets, External, and ProjectSettings folder are in your Unity Hololens-Sender and Hololens-Receiver Unity project. 17 | - [**Kinect Unity Pro Packages**](https://developer.microsoft.com/en-us/windows/kinect/tools) from Kinect Tools - Make sure the Kinect.2.0.XXXXXXXXXX Unity package is imported in your Hololens-Sender Unity Project - Read [this](http://www.imaginativeuniversal.com/blog/post/2015/03/27/unity-5-and-kinect-2-integration.aspx) post for a good tutorial on Unity 5 and Kinect 2 integration) 18 | 19 | Here are some useful developer links to get started with the Hololens: 20 | - [Hololens Academy](https://developer.microsoft.com/en-us/windows/holographic/academy) 21 | - [Hololens Installation Checklist](https://developer.microsoft.com/en-us/windows/holographic/install_the_tools) 22 | - [Windows Holographic Developer Forum](https://forums.hololens.com/) 23 | - [Using the Windows Device Portal](https://developer.microsoft.com/en-us/windows/holographic/using_the_windows_device_portal) 24 | 25 | ## Usage 26 | Before running/deploying either app, start the **Sharing Session Manager** from the HoloToolkit menu in Unity. Take note of the IP address for the Sharing objects in each app. 27 | 28 | ### Hololens-Sender 29 | The Hololens-Sender Unity app is a program that reads in Kinect skeletal data and broadcasts it to other devices through the Sharing Session Manager. It can be run from the Unity editor and displays the resulting data. 30 | 31 | ### Setup 32 | Make sure you include the Kinect Unity Package and HoloToolkit in your new Unity project. See above instructions for setting those up. 33 | The scene should have 5 things in the hierarchy: 34 | 35 | 1. Directional Light 36 | 2. Sharing (Empty GameObject with 4 scripts attached) 37 | * **SharingStage.cs** - From HoloToolkit Sharing. You will need to change the Server Address here 38 | * **SharingSessionTracker.cs** - From HoloToolkit Sharing. 39 | * **AutoJoinSession.cs** - From HoloToolkit Sharing. 40 | * **CustomMessages2.cs** - Modified from HoloToolkit Sharing. Allows for customized messages to be broadcasted. In this case, body tracking IDs and joint data. 41 | 3. BodyManager (Empty GameObject with 3 scripts attached) 42 | * **KinectBodyData.cs** - From Kinect View example. Reads in Kinect skeletal data. 43 | * **BodyDataConverter.cs** - Parses the Kinect body data. Takes the scene's BodyManager as a parameter. 44 | * **BodyDataSender.cs** - Broadcasts the converted body data using custom messages. Takes the scene's BodyManager as a parameter. 45 | 4. BodyView (Empty GameObject with 1 script and 1 child) 46 | * **BodyView.cs** - Displays the converted body data. Takes the scene's BodyManager as a parameter. 47 | * Cube - You can disable the Mesh Renderer 48 | 5. MainCamera (HoloToolkit camera prefab) 49 | 50 | When you run the app you should be able to see your machine join the Sharing Session in the console. 51 | 52 | ### Hololens-Receiver 53 | The Hololens-Receiver Unity app is a program that can be deployed on the Hololens that listens for messages about Kinect skeletal data and renders it to screen. 54 | 55 | ### Setup 56 | Make sure you include HoloToolkit in your new Unity project. Also make sure that Virtual Reality is enabled in your player settings, and that **internetClient**, **internetClientServer**, and **privateNetworkClientServer** are enabled in the checklist. 57 | The scene should have 5 things in the hierarchy: 58 | 59 | 1. Directional Light 60 | 2. Sharing (Empty GameObject with 4 scripts attached, same as above) 61 | 3. BodyReceiver (Empty GameObject with 1 script attached) 62 | * **BodyDataReceiver.cs** - Listens for custom messages containing skeletal data 63 | 4. BodyView (Empty GameObject with 1 script and 1 child) 64 | * **BodyView.cs** - Displays the body data. Takes the scene's BodyReceiver as a parameter. 65 | * Cube - You can disable the Mesh Renderer 66 | 5. MainCamera (HoloToolkit camera prefab) 67 | 68 | Build the Hololens app and deploy. If you don't know how to do this be sure to read over the developer links. 69 | When you deploy the app you should be able to see your Hololens device join the Sharing Session in the console. 70 | 71 | ## Needed Improvements 72 | - Properly scale the Kinect skeletons 73 | - Delete skeletons that leave the screen 74 | - Render lines for bones like in the Kinect View example 75 | - Reduce noise in the movements 76 | - Track the location of the Kinect so that bodies can be placed in their real location 77 | 78 | ## Known Errors 79 | - Sometimes the Hololens-Receiver app crashes in the Unity Editor upon receiving skeletal data 80 | 81 | ## Credits 82 | This was made for Hololens development at the Studio for Creative Inquiry. Feel free to use these scripts for your own Hololens + Kinect projects. 83 | --------------------------------------------------------------------------------