|
|
//========= Copyright 2016-2020, HTC Corporation. All rights reserved. ===========
using HTC.UnityPlugin.Utility;using HTC.UnityPlugin.VRModuleManagement;using System;using System.Collections.Generic;using UnityEngine;using UnityEngine.Events;
namespace HTC.UnityPlugin.Vive{ public static partial class ViveRole { public struct MappingChangedEventArg { public int roleValue; public uint previousDeviceIndex; public uint currentDeviceIndex; }
public struct MappingChangedEventArg<TRole> { public TRole role; public uint previousDeviceIndex; public uint currentDeviceIndex; }
public interface IMap { ViveRoleEnum.IInfo RoleValueInfo { get; } IMapHandler Handler { get; } int BindingCount { get; } IIndexedTableReadOnly<string, int> BindingTable { get; }
bool IsRoleValueMapped(int roleValue); bool IsDeviceMapped(uint deviceIndex); uint GetMappedDeviceByRoleValue(int roleValue); int GetMappedRoleValueByDevice(uint deviceIndex);
[Obsolete("Use BindDeviceToRoleValue instead")] void BindRoleValue(int roleValue, string deviceSN); void BindDeviceToRoleValue(string deviceSN, int roleValue); void BindAll(); bool UnbindRoleValue(int roleValue); // return true if role is ready for bind
bool UnbindDevice(string deviceSN); // return true if device is ready for bind
bool UnbindConnectedDevice(uint deviceIndex); // return true if device is ready for bind
void UnbindAll();
bool IsRoleValueBound(int roleValue); bool IsDeviceBound(string deviceSN); bool IsDeviceConnectedAndBound(uint deviceIndex); string GetBoundDeviceByRoleValue(int roleValue); /// <summary>
/// Should use IsDeviceBound to validate deviceSN before calling this function
/// </summary>
int GetBoundRoleValueByDevice(string deviceSN); /// <summary>
/// Should use IsDeviceConnectedAndBound to validate deviceIndex before calling this function
/// </summary>
int GetBoundRoleValueByConnectedDevice(uint deviceIndex);
event UnityAction<IMap, MappingChangedEventArg> onRoleValueMappingChanged; }
private sealed class Map : IMap { private readonly ViveRoleEnum.IInfo m_info; private IMapHandler m_handler; private bool m_lockInternalMapping;
// mapping table
private readonly uint[] m_role2index; private readonly int[] m_index2role;
// binding table
private readonly IndexedSet<uint>[] m_roleBoundDevices; // connected devices only
private readonly IndexedTable<string, int> m_sn2role;
public Map(Type roleType) { m_info = ViveRoleEnum.GetInfo(roleType);
m_role2index = new uint[m_info.ValidRoleLength]; m_index2role = new int[VRModule.MAX_DEVICE_COUNT];
m_roleBoundDevices = new IndexedSet<uint>[m_info.ValidRoleLength]; m_sn2role = new IndexedTable<string, int>(Mathf.Min(m_info.ValidRoleLength, (int)VRModule.MAX_DEVICE_COUNT));
for (int i = 0; i < m_role2index.Length; ++i) { m_role2index[i] = VRModule.INVALID_DEVICE_INDEX; }
for (int i = 0; i < m_index2role.Length; ++i) { m_index2role[i] = m_info.InvalidRoleValue; } }
public ViveRoleEnum.IInfo RoleValueInfo { get { return m_info; } } public int BindingCount { get { return m_sn2role.Count; } } public IIndexedTableReadOnly<string, int> BindingTable { get { return m_sn2role.ReadOnly; } }
public IMapHandler Handler { get { return m_handler; } set { if (m_handler == value) { return; }
if (m_handler != null) { m_handler.OnDivestedOfCurrentMapHandler(); m_handler = null; }
if (value != null) { if (value.BlockBindings) { UnbindAll(); }
m_handler = value; m_handler.OnAssignedAsCurrentMapHandler(); } } }
public event UnityAction<IMap, MappingChangedEventArg> onRoleValueMappingChanged;
private string DeviceSN(uint deviceIndex) { return VRModule.GetCurrentDeviceState(deviceIndex).serialNumber; }
public void OnConnectedDeviceChanged(uint deviceIndex, VRModuleDeviceClass deviceClass, string deviceSN, bool connected) { if (connected) { if (IsDeviceBound(deviceSN)) { InternalInsertRoleBoundDevice(deviceSN, deviceIndex, GetBoundRoleValueByDevice(deviceSN)); } } else { if (IsDeviceMapped(deviceIndex)) { if (IsDeviceBound(deviceSN)) { InternalRemoveRoleBoundDevice(deviceSN, deviceIndex, GetBoundRoleValueByDevice(deviceSN)); }
if (IsDeviceMapped(deviceIndex)) { InternalUnmapping(GetMappedRoleValueByDevice(deviceIndex), deviceIndex); } } }
if (m_handler != null) { m_handler.OnConnectedDeviceChanged(deviceIndex, deviceClass, deviceSN, connected); } }
public void OnTrackedDeviceRoleChanged() { if (m_handler != null) { m_handler.OnTrackedDeviceRoleChanged(); } }
#region retrieve state
public bool IsRoleValueMapped(int roleValue) { if (!m_info.IsValidRoleValue(roleValue)) { return false; } return IsRoleOffsetMapped(m_info.RoleValueToRoleOffset(roleValue)); }
public bool IsRoleOffsetMapped(int roleOffset) { return VRModule.IsValidDeviceIndex(m_role2index[roleOffset]); }
public bool IsDeviceMapped(uint deviceIndex) { return VRModule.IsValidDeviceIndex(deviceIndex) && m_info.IsValidRoleValue(m_index2role[deviceIndex]); }
public bool IsRoleValueBound(int roleValue) { if (!m_info.IsValidRoleValue(roleValue)) { return false; }
var roleOffset = m_info.RoleValueToRoleOffset(roleValue); return m_roleBoundDevices[roleOffset] != null && m_roleBoundDevices[roleOffset].Count > 0; }
public bool IsDeviceBound(string deviceSN) { return string.IsNullOrEmpty(deviceSN) ? false : m_sn2role.ContainsKey(deviceSN); }
public bool IsDeviceConnectedAndBound(uint deviceIndex) { return IsDeviceBound(DeviceSN(deviceIndex)); }
public uint GetMappedDeviceByRoleValue(int roleValue) { if (m_info.IsValidRoleValue(roleValue)) { return m_role2index[m_info.RoleValueToRoleOffset(roleValue)]; } else { return VRModule.INVALID_DEVICE_INDEX; } }
public int GetMappedRoleValueByDevice(uint deviceIndex) { if (VRModule.IsValidDeviceIndex(deviceIndex)) { return m_index2role[deviceIndex]; } else { return m_info.InvalidRoleValue; } }
public string GetBoundDeviceByRoleValue(int roleValue) { if (!IsRoleValueBound(roleValue)) { return string.Empty; } return DeviceSN(GetMappedDeviceByRoleValue(roleValue)); }
public int GetBoundRoleValueByDevice(string deviceSN) { return m_sn2role[deviceSN]; }
public int GetBoundRoleValueByConnectedDevice(uint deviceIndex) { return GetBoundRoleValueByDevice(DeviceSN(deviceIndex)); } #endregion retrieve state
#region internal operation
// both roleValue and deviceIndex must be valid
// ignore binding state
private void InternalMapping(int roleValue, uint deviceIndex) { if (m_lockInternalMapping) { throw new Exception("Recursive calling InternalMapping"); } m_lockInternalMapping = true;
var previousRoleValue = m_index2role[deviceIndex]; if (roleValue == previousRoleValue) { m_lockInternalMapping = false; return; }
if (m_info.IsValidRoleValue(previousRoleValue)) { m_lockInternalMapping = false; InternalUnmapping(previousRoleValue, deviceIndex); m_lockInternalMapping = true; }
var roleOffset = m_info.RoleValueToRoleOffset(roleValue); var previousDeviceIndex = m_role2index[roleOffset]; var eventArg = new MappingChangedEventArg() { roleValue = roleValue, previousDeviceIndex = previousDeviceIndex, currentDeviceIndex = deviceIndex, };
m_role2index[roleOffset] = deviceIndex; m_index2role[deviceIndex] = roleValue;
if (VRModule.IsValidDeviceIndex(previousDeviceIndex)) { m_index2role[previousDeviceIndex] = m_info.InvalidRoleValue; }
if (onRoleValueMappingChanged != null) { onRoleValueMappingChanged(this, eventArg); }
m_lockInternalMapping = false; }
// both roleValue and deviceIndex must be valid
// ignore binding state
private void InternalUnmapping(int roleValue, uint deviceIndex) { if (m_lockInternalMapping) { throw new Exception("Recursive calling InternalMapping"); } m_lockInternalMapping = true;
var roleOffset = m_info.RoleValueToRoleOffset(roleValue); var eventArg = new MappingChangedEventArg() { roleValue = roleValue, previousDeviceIndex = deviceIndex, currentDeviceIndex = VRModule.INVALID_DEVICE_INDEX, };
m_role2index[roleOffset] = VRModule.INVALID_DEVICE_INDEX; m_index2role[deviceIndex] = m_info.InvalidRoleValue;
if (onRoleValueMappingChanged != null) { onRoleValueMappingChanged(this, eventArg); }
m_lockInternalMapping = false; }
// device must be valid and connected and have bound role value
// device must not exist in role bound devices
// boundRoleValue can be whether valid or not
private void InternalInsertRoleBoundDevice(string deviceSN, uint deviceIndex, int boundRoleValue) { if (m_info.IsValidRoleValue(boundRoleValue)) { var roleBoundDevices = InternalGetRoleBoundDevices(boundRoleValue);
roleBoundDevices.Add(deviceIndex); // if key already added here, means that this device already in role bound devices
InternalMapping(boundRoleValue, deviceIndex); } }
// device must be valid and connected and have bound role value
// device must already exist in role bound devices
// boundRoleValue can be whether valid or not
private void InternalRemoveRoleBoundDevice(string deviceSN, uint deviceIndex, int boundRoleValue) { if (m_info.IsValidRoleValue(boundRoleValue)) { var roleBoundDevices = InternalGetRoleBoundDevices(boundRoleValue);
if (!roleBoundDevices.Remove(deviceIndex)) { throw new Exception("device([" + deviceIndex + "]" + deviceSN + ") has not been InternalMappingRoleBoundDevice"); }
if (roleBoundDevices.Count > 0) { InternalMapping(boundRoleValue, roleBoundDevices[0]); } } }
// deviceSN must be valid
// device can be whether bound or not
// device can be whether connected or not
private void InternalBind(string deviceSN, int roleValue) { var deviceIndex = VRModule.GetConnectedDeviceIndex(deviceSN);
bool previousIsBound = false; int previousBoundRoleValue = m_info.InvalidRoleValue; if (m_sn2role.TryGetValue(deviceSN, out previousBoundRoleValue)) { if (previousBoundRoleValue == roleValue) { return; }
previousIsBound = true;
m_sn2role.Remove(deviceSN);
if (VRModule.IsValidDeviceIndex(deviceIndex)) { InternalRemoveRoleBoundDevice(deviceSN, deviceIndex, previousBoundRoleValue); } }
m_sn2role[deviceSN] = roleValue;
if (VRModule.IsValidDeviceIndex(deviceIndex)) { InternalInsertRoleBoundDevice(deviceSN, deviceIndex, roleValue); }
if (m_handler != null) { m_handler.OnBindingRoleValueChanged(deviceSN, previousIsBound, previousBoundRoleValue, true, roleValue); } }
// deviceSN must be valid
// device must be bound
// device can be whether connected or not
private void InternalUnbind(string deviceSN, int boundRoleValue) { var deviceIndex = VRModule.GetConnectedDeviceIndex(deviceSN);
if (!m_sn2role.Remove(deviceSN)) { throw new Exception("device([" + deviceIndex + "]" + deviceSN + ") already unbound"); }
if (VRModule.IsValidDeviceIndex(deviceIndex)) { InternalRemoveRoleBoundDevice(deviceSN, deviceIndex, boundRoleValue); }
if (m_handler != null) { m_handler.OnBindingRoleValueChanged(deviceSN, true, boundRoleValue, false, m_info.InvalidRoleValue); } } #endregion internal operation
#region mapping
public void MappingRoleValue(int roleValue, uint deviceIndex) { if (!m_info.IsValidRoleValue(roleValue)) { throw new ArgumentException("Cannot mapping invalid roleValue(" + m_info.RoleEnumType.Name + "[" + roleValue + "])"); }
if (!VRModule.IsValidDeviceIndex(deviceIndex)) { throw new ArgumentException("Cannot mapping invalid deviceIndex(" + deviceIndex + ")"); }
if (IsRoleValueBound(roleValue)) { throw new ArgumentException("roleValue(" + m_info.RoleEnumType.Name + "[" + roleValue + "]) is already bound, unbind first."); }
if (IsDeviceConnectedAndBound(deviceIndex)) { throw new ArgumentException("deviceIndex(" + deviceIndex + ") is already bound, unbind first"); }
InternalMapping(roleValue, deviceIndex); }
// return true if role is ready for mapping
public bool UnmappingRoleValue(int roleValue) { // is mapped?
if (!IsRoleValueMapped(roleValue)) { return false; }
// is bound?
if (IsRoleValueBound(roleValue)) { return false; }
InternalUnmapping(roleValue, GetMappedDeviceByRoleValue(roleValue));
return true; }
// return true if device is ready for mapping
public bool UnmappingDevice(uint deviceIndex) { // is mapped?
if (!IsDeviceMapped(deviceIndex)) { return false; }
// is bound?
if (IsDeviceConnectedAndBound(deviceIndex)) { return false; }
InternalUnmapping(GetMappedRoleValueByDevice(deviceIndex), deviceIndex);
return true; }
public void UnmappingAll() { for (int roleValue = m_info.MinValidRoleValue; roleValue <= m_info.MaxValidRoleValue; ++roleValue) { if (!m_info.IsValidRoleValue(roleValue)) { continue; }
UnmappingRoleValue(roleValue); } } #endregion mapping
#region bind
[Obsolete("Use BindDeviceToRoleValue instead")] public void BindRoleValue(int roleValue, string deviceSN) { BindDeviceToRoleValue(deviceSN, roleValue); }
public void BindDeviceToRoleValue(string deviceSN, int roleValue) { if (string.IsNullOrEmpty(deviceSN)) { throw new ArgumentException("deviceSN cannot be null or empty."); }
if (m_handler != null && m_handler.BlockBindings) { return; }
InternalBind(deviceSN, roleValue); }
// bind all mapped roles & devices
public void BindAll() { if (m_handler != null && m_handler.BlockBindings) { return; }
for (int roleValue = m_info.MinValidRoleValue; roleValue <= m_info.MaxValidRoleValue; ++roleValue) { if (!m_info.IsValidRoleValue(roleValue)) { continue; }
if (IsRoleValueMapped(roleValue) && !IsRoleValueBound(roleValue)) { InternalBind(DeviceSN(GetMappedDeviceByRoleValue(roleValue)), roleValue); } } }
public bool UnbindRoleValue(int roleValue) { if (!IsRoleValueBound(roleValue)) { return false; }
var roleBoundDevices = InternalGetRoleBoundDevices(roleValue); var boundDeviceIndex = GetMappedDeviceByRoleValue(roleValue);
// unbind other bound device first, to avoid redundent mapping changes event
while (roleBoundDevices.Count > 1) { for (int i = roleBoundDevices.Count - 1; i >= 0; --i) { if (roleBoundDevices[i] != boundDeviceIndex) { InternalUnbind(DeviceSN(roleBoundDevices[i]), roleValue); break; } } };
if (roleBoundDevices.Count == 1) { InternalUnbind(DeviceSN(boundDeviceIndex), roleValue); }
return true; }
public bool UnbindDevice(string deviceSN) { if (!IsDeviceBound(deviceSN)) { return false; }
InternalUnbind(deviceSN, GetBoundRoleValueByDevice(deviceSN));
return true; }
public bool UnbindConnectedDevice(uint deviceIndex) { return UnbindDevice(DeviceSN(deviceIndex)); }
public void UnbindAll() { for (int i = m_sn2role.Count - 1; i >= 0; --i) { UnbindDevice(m_sn2role.GetKeyByIndex(i)); } }
// roleValue must be valid
private IndexedSet<uint> InternalGetRoleBoundDevices(int roleValue) { var roleOffset = m_info.RoleValueToRoleOffset(roleValue);
if (m_roleBoundDevices[roleOffset] == null) { m_roleBoundDevices[roleOffset] = new IndexedSet<uint>(); }
return m_roleBoundDevices[roleOffset]; } #endregion bind
}
public interface IMap<TRole> : IMap { ViveRoleEnum.IInfo<TRole> RoleInfo { get; }
bool IsRoleMapped(TRole role); uint GetMappedDeviceByRole(TRole role); TRole GetMappedRoleByDevice(uint deviceIndex);
[Obsolete("Use BindDeviceToRole instead")] void BindRole(TRole role, string deviceSN); void BindDeviceToRole(string deviceSN, TRole role); bool UnbindRole(TRole role); // return true if role is ready for bind
bool IsRoleBound(TRole role); string GetBoundDeviceByRole(TRole role); TRole GetBoundRoleByDevice(string deviceSN); TRole GetBoundRoleByConnectedDevice(uint deviceIndex);
event UnityAction<IMap<TRole>, MappingChangedEventArg<TRole>> onRoleMappingChanged; }
private sealed class GenericMap<TRole> : IMap<TRole> { public static GenericMap<TRole> s_instance;
private readonly ViveRoleEnum.IInfo<TRole> m_info; private readonly Map m_map;
public GenericMap() { m_info = ViveRoleEnum.GetInfo<TRole>(); m_map = GetInternalMap(typeof(TRole));
if (s_instance == null) { s_instance = this; } else { Debug.LogWarning("duplicated instance for RoleInfo<" + typeof(TRole).Name + ">"); }
m_map.onRoleValueMappingChanged += OnMappingChanged; }
public ViveRoleEnum.IInfo RoleValueInfo { get { return m_map.RoleValueInfo; } } public ViveRoleEnum.IInfo<TRole> RoleInfo { get { return m_info; } } public IMapHandler Handler { get { return m_map.Handler; } } public int BindingCount { get { return m_map.BindingCount; } } public IIndexedTableReadOnly<string, int> BindingTable { get { return m_map.BindingTable; } }
public event UnityAction<IMap, MappingChangedEventArg> onRoleValueMappingChanged { add { m_map.onRoleValueMappingChanged += value; } remove { m_map.onRoleValueMappingChanged -= value; } }
public event UnityAction<IMap<TRole>, MappingChangedEventArg<TRole>> onRoleMappingChanged;
private void OnMappingChanged(IMap map, MappingChangedEventArg arg) { if (onRoleMappingChanged != null) { onRoleMappingChanged(this, new MappingChangedEventArg<TRole>() { role = m_info.ToRole(arg.roleValue), previousDeviceIndex = arg.previousDeviceIndex, currentDeviceIndex = arg.currentDeviceIndex, }); } }
public void MappingRole(TRole role, uint deviceIndex) { m_map.MappingRoleValue(m_info.ToRoleValue(role), deviceIndex); } public void MappingRoleValue(int roleValue, uint deviceIndex) { m_map.MappingRoleValue(roleValue, deviceIndex); } public bool UnmappingRole(TRole role) { return m_map.UnmappingRoleValue(m_info.ToRoleValue(role)); } public bool UnmappingRoleValue(int roleValue) { return m_map.UnmappingRoleValue(roleValue); } public bool UnmappingDevice(uint deviceIndex) { return m_map.UnmappingDevice(deviceIndex); } public void UnmappingAll() { m_map.UnmappingAll(); }
public bool IsRoleMapped(TRole role) { return m_map.IsRoleValueMapped(m_info.ToRoleValue(role)); } public bool IsRoleValueMapped(int roleValue) { return m_map.IsRoleValueMapped(roleValue); } public bool IsDeviceMapped(uint deviceIndex) { return m_map.IsDeviceMapped(deviceIndex); } public uint GetMappedDeviceByRole(TRole role) { return m_map.GetMappedDeviceByRoleValue(m_info.ToRoleValue(role)); } public TRole GetMappedRoleByDevice(uint deviceIndex) { return m_info.ToRole(m_map.GetMappedRoleValueByDevice(deviceIndex)); } public uint GetMappedDeviceByRoleValue(int roleValue) { return m_map.GetMappedDeviceByRoleValue(roleValue); } public int GetMappedRoleValueByDevice(uint deviceIndex) { return m_map.GetMappedRoleValueByDevice(deviceIndex); }
[Obsolete("Use BindDeviceToRole instead")] public void BindRole(TRole role, string deviceSN) { m_map.BindDeviceToRoleValue(deviceSN, m_info.ToRoleValue(role)); } [Obsolete("Use BindDeviceToRoleValue instead")] public void BindRoleValue(int roleValue, string deviceSN) { m_map.BindDeviceToRoleValue(deviceSN, roleValue); } public void BindDeviceToRole(string deviceSN, TRole role) { m_map.BindDeviceToRoleValue(deviceSN, m_info.ToRoleValue(role)); } public void BindDeviceToRoleValue(string deviceSN, int roleValue) { m_map.BindDeviceToRoleValue(deviceSN, roleValue); } public void BindAll() { m_map.BindAll(); } public bool UnbindRole(TRole role) { return m_map.UnbindRoleValue(m_info.ToRoleValue(role)); } public bool UnbindRoleValue(int roleValue) { return m_map.UnbindRoleValue(roleValue); } public bool UnbindDevice(string deviceSN) { return m_map.UnbindDevice(deviceSN); } public bool UnbindConnectedDevice(uint deviceIndex) { return UnbindConnectedDevice(deviceIndex); } public void UnbindAll() { m_map.UnbindAll(); }
public bool IsRoleBound(TRole role) { return m_map.IsRoleValueBound(m_info.ToRoleValue(role)); } public bool IsRoleValueBound(int roleValue) { return m_map.IsRoleValueBound(roleValue); } public bool IsDeviceBound(string deviceSN) { return m_map.IsDeviceBound(deviceSN); } public bool IsDeviceConnectedAndBound(uint deviceIndex) { return m_map.IsDeviceConnectedAndBound(deviceIndex); } public TRole GetBoundRoleByDevice(string deviceSN) { return m_info.ToRole(m_map.GetBoundRoleValueByDevice(deviceSN)); } public TRole GetBoundRoleByConnectedDevice(uint deviceIndex) { return m_info.ToRole(m_map.GetBoundRoleValueByConnectedDevice(deviceIndex)); } public string GetBoundDeviceByRole(TRole role) { return m_map.GetBoundDeviceByRoleValue(m_info.ToRoleValue(role)); } public string GetBoundDeviceByRoleValue(int roleValue) { return m_map.GetBoundDeviceByRoleValue(roleValue); } public int GetBoundRoleValueByDevice(string deviceSN) { return m_map.GetBoundRoleValueByDevice(deviceSN); } public int GetBoundRoleValueByConnectedDevice(uint deviceIndex) { return m_map.GetBoundRoleValueByConnectedDevice(deviceIndex); } }
private static IndexedTable<Type, Map> s_mapTable;
private static Map GetInternalMap(Type roleType) { if (s_mapTable == null) { s_mapTable = new IndexedTable<Type, Map>(); }
Map map; if (!s_mapTable.TryGetValue(roleType, out map)) { var validateResult = ViveRoleEnum.ValidateViveRoleEnum(roleType); if (validateResult != ViveRoleEnumValidateResult.Valid) { Debug.LogWarning(roleType.Name + " is not valid ViveRole type. " + validateResult); return null; }
map = new Map(roleType); s_mapTable.Add(roleType, map); }
return map; }
public static IMap GetMap(Type roleType) { return GetInternalMap(roleType); }
private static GenericMap<TRole> GetInternalMap<TRole>() { var roleEnumType = typeof(TRole); if (GenericMap<TRole>.s_instance == null) { var validateResult = ViveRoleEnum.ValidateViveRoleEnum(roleEnumType); if (validateResult != ViveRoleEnumValidateResult.Valid) { Debug.LogWarning(roleEnumType.Name + " is not valid ViveRole type. " + validateResult); return null; }
new GenericMap<TRole>(); }
return GenericMap<TRole>.s_instance; }
public static IMap<TRole> GetMap<TRole>() { return GetInternalMap<TRole>(); }
public static void AssignMapHandler<TRole>(MapHandler<TRole> mapHandler) { Initialize(); GetInternalMap(typeof(TRole)).Handler = mapHandler; } }}
|