You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
13 KiB
328 lines
13 KiB
//========= Copyright 2016-2020, HTC Corporation. All rights reserved. ===========
|
|
|
|
#pragma warning disable 0618
|
|
#if UNITY_2017_1_OR_NEWER
|
|
|
|
using HTC.UnityPlugin.Utility;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
#if UNITY_2017_2_OR_NEWER
|
|
using UnityEngine.XR;
|
|
#else
|
|
using UnityEngine.VR;
|
|
using XRSettings = UnityEngine.VR.VRSettings;
|
|
using XRDevice = UnityEngine.VR.VRDevice;
|
|
using XRNodeState = UnityEngine.VR.VRNodeState;
|
|
using XRNode = UnityEngine.VR.VRNode;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
namespace HTC.UnityPlugin.VRModuleManagement
|
|
{
|
|
public sealed partial class UnityEngineVRModule : VRModule.ModuleBase
|
|
{
|
|
#if UNITY_2017_1_OR_NEWER && !UNITY_2020_1_OR_NEWER
|
|
private static readonly VRModuleDeviceClass[] s_nodeType2DeviceClass;
|
|
|
|
private uint m_leftIndex = INVALID_DEVICE_INDEX;
|
|
private uint m_rightIndex = INVALID_DEVICE_INDEX;
|
|
|
|
private List<XRNodeState> m_nodeStateList = new List<XRNodeState>();
|
|
private Dictionary<ulong, uint> m_node2Index = new Dictionary<ulong, uint>();
|
|
private ulong[] m_index2nodeID;
|
|
private bool[] m_index2nodeValidity;
|
|
private bool[] m_index2nodeTouched;
|
|
|
|
private TrackingSpaceType m_prevTrackingSpace;
|
|
|
|
static UnityEngineVRModule()
|
|
{
|
|
s_nodeType2DeviceClass = new VRModuleDeviceClass[EnumUtils.GetMaxValue(typeof(XRNode)) + 1];
|
|
for (int i = 0; i < s_nodeType2DeviceClass.Length; ++i) { s_nodeType2DeviceClass[i] = VRModuleDeviceClass.Invalid; }
|
|
s_nodeType2DeviceClass[(int)XRNode.Head] = VRModuleDeviceClass.HMD;
|
|
s_nodeType2DeviceClass[(int)XRNode.RightHand] = VRModuleDeviceClass.Controller;
|
|
s_nodeType2DeviceClass[(int)XRNode.LeftHand] = VRModuleDeviceClass.Controller;
|
|
s_nodeType2DeviceClass[(int)XRNode.GameController] = VRModuleDeviceClass.Controller;
|
|
s_nodeType2DeviceClass[(int)XRNode.HardwareTracker] = VRModuleDeviceClass.GenericTracker;
|
|
s_nodeType2DeviceClass[(int)XRNode.TrackingReference] = VRModuleDeviceClass.TrackingReference;
|
|
}
|
|
|
|
public override void OnActivated()
|
|
{
|
|
m_prevTrackingSpace = XRDevice.GetTrackingSpaceType();
|
|
UpdateTrackingSpaceType();
|
|
|
|
EnsureDeviceStateLength(16);
|
|
m_index2nodeID = new ulong[GetDeviceStateLength()];
|
|
m_index2nodeValidity = new bool[GetDeviceStateLength()];
|
|
m_index2nodeTouched = new bool[GetDeviceStateLength()];
|
|
}
|
|
|
|
public override void OnDeactivated()
|
|
{
|
|
m_rightIndex = INVALID_DEVICE_INDEX;
|
|
m_leftIndex = INVALID_DEVICE_INDEX;
|
|
|
|
RemoveAllValidNodes();
|
|
XRDevice.SetTrackingSpaceType(m_prevTrackingSpace);
|
|
}
|
|
|
|
public override uint GetLeftControllerDeviceIndex() { return m_leftIndex; }
|
|
|
|
public override uint GetRightControllerDeviceIndex() { return m_rightIndex; }
|
|
|
|
public override void UpdateTrackingSpaceType()
|
|
{
|
|
switch (VRModule.trackingSpaceType)
|
|
{
|
|
case VRModuleTrackingSpaceType.Stationary:
|
|
XRDevice.SetTrackingSpaceType(TrackingSpaceType.Stationary);
|
|
break;
|
|
case VRModuleTrackingSpaceType.RoomScale:
|
|
#if UNITY_2019_2_OR_NEWER && !UNITY_2019_3_OR_NEWER
|
|
var prev_trackingOrigin = XRDevice.trackingOriginMode;
|
|
XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale);
|
|
if (prev_trackingOrigin == XRDevice.trackingOriginMode)
|
|
{
|
|
XRDevice.SetTrackingSpaceType(TrackingSpaceType.Stationary);
|
|
}
|
|
#else
|
|
XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
private bool IsTrackingDeviceNode(XRNodeState nodeState)
|
|
{
|
|
switch (nodeState.nodeType)
|
|
{
|
|
case XRNode.Head:
|
|
case XRNode.RightHand:
|
|
case XRNode.LeftHand:
|
|
case XRNode.GameController:
|
|
case XRNode.HardwareTracker:
|
|
case XRNode.TrackingReference:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private bool TryGetAndTouchNodeDeviceIndex(XRNodeState nodeState, out uint deviceIndex)
|
|
{
|
|
// only tracking certain type of node (some nodes share same uniqueID)
|
|
if (!IsTrackingDeviceNode(nodeState)) { deviceIndex = INVALID_DEVICE_INDEX; return false; }
|
|
//Debug.Log(Time.frameCount + " TryGetNodeDeviceIndex " + nodeState.nodeType + " tracked=" + nodeState.tracked + " id=" + nodeState.uniqueID + " name=" + (InputTracking.GetNodeName(nodeState.uniqueID) ?? string.Empty));
|
|
if (!m_node2Index.TryGetValue(nodeState.uniqueID, out deviceIndex))
|
|
{
|
|
// FIXME: 0ul is invalid id?
|
|
if (nodeState.uniqueID == 0ul) { return false; }
|
|
|
|
var validIndexFound = false;
|
|
|
|
if (nodeState.nodeType == XRNode.Head)
|
|
{
|
|
if (m_index2nodeValidity[0])
|
|
{
|
|
//Debug.LogWarning("[" + Time.frameCount + "] Multiple Head node found! drop node id:" + nodeState.uniqueID.ToString("X8") + " type:" + nodeState.nodeType + " name:" + InputTracking.GetNodeName(nodeState.uniqueID) + " tracked=" + nodeState.tracked);
|
|
deviceIndex = INVALID_DEVICE_INDEX;
|
|
return false;
|
|
}
|
|
|
|
validIndexFound = true;
|
|
m_index2nodeID[0] = nodeState.uniqueID;
|
|
m_index2nodeValidity[0] = true;
|
|
m_node2Index.Add(nodeState.uniqueID, 0u);
|
|
deviceIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
for (uint i = 1u, imax = (uint)m_index2nodeValidity.Length; i < imax; ++i)
|
|
{
|
|
if (!m_index2nodeValidity[i])
|
|
{
|
|
validIndexFound = true;
|
|
m_index2nodeID[i] = nodeState.uniqueID;
|
|
m_index2nodeValidity[i] = true;
|
|
m_node2Index.Add(nodeState.uniqueID, i);
|
|
deviceIndex = i;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!validIndexFound)
|
|
{
|
|
Debug.LogWarning("[" + Time.frameCount + "] XRNode added, but device index out of range, drop the node id:" + nodeState.uniqueID.ToString("X8") + " type:" + nodeState.nodeType + " name:" + InputTracking.GetNodeName(nodeState.uniqueID) + " tracked=" + nodeState.tracked);
|
|
deviceIndex = INVALID_DEVICE_INDEX;
|
|
return false;
|
|
}
|
|
|
|
//Debug.Log("[" + Time.frameCount + "] Add node device index [" + deviceIndex + "] id=" + nodeState.uniqueID.ToString("X8") + " type=" + nodeState.nodeType + " tracked=" + nodeState.tracked);
|
|
}
|
|
|
|
m_index2nodeTouched[deviceIndex] = true;
|
|
return true;
|
|
}
|
|
|
|
private void TrimUntouchedNodes(System.Action<uint> onTrimmed)
|
|
{
|
|
for (uint i = 0u, imax = (uint)m_index2nodeValidity.Length; i < imax; ++i)
|
|
{
|
|
if (!m_index2nodeTouched[i])
|
|
{
|
|
if (m_index2nodeValidity[i])
|
|
{
|
|
m_node2Index.Remove(m_index2nodeID[i]);
|
|
//m_index2nodeID[i] = 0;
|
|
m_index2nodeValidity[i] = false;
|
|
|
|
onTrimmed(i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert(m_index2nodeValidity[i]);
|
|
m_index2nodeTouched[i] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RemoveAllValidNodes()
|
|
{
|
|
for (int i = 0, imax = m_index2nodeValidity.Length; i < imax; ++i)
|
|
{
|
|
if (m_index2nodeValidity[i])
|
|
{
|
|
m_node2Index.Remove(m_index2nodeID[i]);
|
|
m_index2nodeID[i] = 0;
|
|
m_index2nodeValidity[i] = false;
|
|
m_index2nodeTouched[i] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void BeforeRenderUpdate()
|
|
{
|
|
var roleChanged = false;
|
|
var rightIndex = INVALID_DEVICE_INDEX;
|
|
var leftIndex = INVALID_DEVICE_INDEX;
|
|
|
|
FlushDeviceState();
|
|
|
|
if (XRSettings.isDeviceActive && XRDevice.isPresent)
|
|
{
|
|
InputTracking.GetNodeStates(m_nodeStateList);
|
|
}
|
|
|
|
for (int i = 0, imax = m_nodeStateList.Count; i < imax; ++i)
|
|
{
|
|
uint deviceIndex;
|
|
if (!TryGetAndTouchNodeDeviceIndex(m_nodeStateList[i], out deviceIndex)) { continue; }
|
|
|
|
switch (m_nodeStateList[i].nodeType)
|
|
{
|
|
case XRNode.RightHand: rightIndex = deviceIndex; break;
|
|
case XRNode.LeftHand: leftIndex = deviceIndex; break;
|
|
}
|
|
|
|
IVRModuleDeviceState prevState;
|
|
IVRModuleDeviceStateRW currState;
|
|
EnsureValidDeviceState(deviceIndex, out prevState, out currState);
|
|
|
|
if (m_rightIndex != rightIndex || m_leftIndex != leftIndex)
|
|
{
|
|
m_rightIndex = rightIndex;
|
|
m_leftIndex = leftIndex;
|
|
roleChanged = true;
|
|
}
|
|
|
|
if (!prevState.isConnected)
|
|
{
|
|
currState.isConnected = true;
|
|
currState.deviceClass = s_nodeType2DeviceClass[(int)m_nodeStateList[i].nodeType];
|
|
// FIXME: getting wrong name in Unity 2017.1f1
|
|
//currDeviceState.serialNumber = InputTracking.GetNodeName(m_nodeStateList[i].uniqueID) ?? string.Empty;
|
|
//Debug.Log("connected " + InputTracking.GetNodeName(m_nodeStateList[i].uniqueID));
|
|
|
|
if (!XRDevice.model.Equals("None"))
|
|
{
|
|
currState.serialNumber = XRDevice.model + " " + m_nodeStateList[i].uniqueID.ToString("X8");
|
|
currState.modelNumber = XRDevice.model + " " + m_nodeStateList[i].nodeType;
|
|
currState.renderModelName = XRDevice.model + " " + m_nodeStateList[i].nodeType;
|
|
}
|
|
else
|
|
{
|
|
currState.serialNumber = XRSettings.loadedDeviceName + " " + m_nodeStateList[i].uniqueID.ToString("X8");
|
|
currState.modelNumber = XRSettings.loadedDeviceName + " " + m_nodeStateList[i].nodeType;
|
|
currState.renderModelName = XRSettings.loadedDeviceName + " " + m_nodeStateList[i].nodeType;
|
|
}
|
|
|
|
SetupKnownDeviceModel(currState);
|
|
}
|
|
|
|
// update device status
|
|
currState.isPoseValid = m_nodeStateList[i].tracked;
|
|
|
|
var velocity = default(Vector3);
|
|
if (m_nodeStateList[i].TryGetVelocity(out velocity)) { currState.velocity = velocity; }
|
|
|
|
var position = default(Vector3);
|
|
if (m_nodeStateList[i].TryGetPosition(out position)) { currState.position = position; }
|
|
|
|
var rotation = default(Quaternion);
|
|
if (m_nodeStateList[i].TryGetRotation(out rotation)) { currState.rotation = rotation; }
|
|
|
|
#if UNITY_2017_2_OR_NEWER
|
|
var angularVelocity = default(Vector3);
|
|
if (m_nodeStateList[i].TryGetAngularVelocity(out angularVelocity)) { currState.angularVelocity = angularVelocity; }
|
|
#endif
|
|
}
|
|
|
|
m_nodeStateList.Clear();
|
|
|
|
// update right hand input
|
|
if (VRModule.IsValidDeviceIndex(rightIndex))
|
|
{
|
|
IVRModuleDeviceState rightPrevState;
|
|
IVRModuleDeviceStateRW rightCurrState;
|
|
EnsureValidDeviceState(rightIndex, out rightPrevState, out rightCurrState);
|
|
UpdateRightControllerInput(rightPrevState, rightCurrState);
|
|
}
|
|
|
|
//// update left hand input
|
|
if (VRModule.IsValidDeviceIndex(leftIndex))
|
|
{
|
|
IVRModuleDeviceState leftPrevState;
|
|
IVRModuleDeviceStateRW leftCurrState;
|
|
EnsureValidDeviceState(leftIndex, out leftPrevState, out leftCurrState);
|
|
UpdateLeftControllerInput(leftPrevState, leftCurrState);
|
|
}
|
|
|
|
TrimUntouchedNodes(trimmedIndex =>
|
|
{
|
|
IVRModuleDeviceState ps;
|
|
IVRModuleDeviceStateRW cs;
|
|
if (TryGetValidDeviceState(trimmedIndex, out ps, out cs))
|
|
{
|
|
cs.Reset();
|
|
}
|
|
});
|
|
|
|
ProcessConnectedDeviceChanged();
|
|
|
|
if (roleChanged)
|
|
{
|
|
InvokeControllerRoleChangedEvent();
|
|
}
|
|
|
|
ProcessDevicePoseChanged();
|
|
ProcessDeviceInputChanged();
|
|
}
|
|
#endif
|
|
}
|
|
}
|