2 Commits

Author SHA1 Message Date
srl87
45b4e46f74 version 2.2.0 2024-01-10 14:20:05 +08:00
srl87
11b730e79b update installer 2023-09-14 18:39:28 +08:00
212 changed files with 107851 additions and 1162 deletions

Binary file not shown.

View File

@@ -11,6 +11,8 @@ using UnityEditor.XR.OpenXR.Features;
using UnityEngine; using UnityEngine;
using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features; using UnityEngine.XR.OpenXR.Features;
using UnityEngine.XR.OpenXR.Features.Interactions;
using VIVE.OpenXR.FacialTracking;
using VIVE.OpenXR.Hand; using VIVE.OpenXR.Hand;
using VIVE.OpenXR.Tracker; using VIVE.OpenXR.Tracker;
@@ -249,54 +251,75 @@ namespace VIVE.OpenXR.Editor
internal void AddOpenXRFeatures() internal void AddOpenXRFeatures()
{ {
bool enableViveWristTracker = false; bool enableHandtracking = false;
bool enableViveHandTracking = false; bool enableTracker = false;
bool enableEyetracking = false;
bool enableLipexpression = false;
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android); var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
if (null == settings) if (null == settings)
return; return;
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>()) foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
{ {
if (feature is ViveWristTracker) if ((feature is ViveWristTracker || feature is ViveXRTracker) && feature.enabled)
{ {
enableViveWristTracker = feature.enabled; enableHandtracking = true;
break; enableTracker = true;
}
if (feature is EyeGazeInteraction && feature.enabled)
{
enableEyetracking = true;
}
if (feature is ViveHandInteraction && feature.enabled)
{
enableHandtracking = true;
} }
} }
foreach (var feature in settings.GetFeatures<OpenXRFeature>()) foreach (var feature in settings.GetFeatures<OpenXRFeature>())
{ {
if (feature is ViveHandTracking) if (feature is ViveHandTracking && feature.enabled)
{ {
enableViveHandTracking = feature.enabled; enableHandtracking = true;
break; ; }
if (feature is ViveFacialTracking && feature.enabled)
{
enableEyetracking = true;
enableLipexpression = true;
} }
} }
//Debug.Log("enableViveWristTracker " + enableViveWristTracker); if (enableHandtracking)
if (enableViveWristTracker)
{
{ {
var newUsesFeature = CreateElement("uses-feature"); var newUsesFeature = CreateElement("uses-feature");
newUsesFeature.Attributes.Append(CreateAndroidAttribute("name", "wave.feature.handtracking")); newUsesFeature.Attributes.Append(CreateAndroidAttribute("name", "wave.feature.handtracking"));
newUsesFeature.Attributes.Append(CreateAndroidAttribute("required", "true")); newUsesFeature.Attributes.Append(CreateAndroidAttribute("required", "true"));
ManifestElement.AppendChild(newUsesFeature); ManifestElement.AppendChild(newUsesFeature);
} }
if (enableTracker)
{ {
var newUsesFeature = CreateElement("uses-feature"); var newUsesFeature = CreateElement("uses-feature");
newUsesFeature.Attributes.Append(CreateAndroidAttribute("name", "wave.feature.tracker")); newUsesFeature.Attributes.Append(CreateAndroidAttribute("name", "wave.feature.tracker"));
newUsesFeature.Attributes.Append(CreateAndroidAttribute("required", "true")); newUsesFeature.Attributes.Append(CreateAndroidAttribute("required", "true"));
ManifestElement.AppendChild(newUsesFeature); ManifestElement.AppendChild(newUsesFeature);
} }
}
else if (enableViveHandTracking) if (enableEyetracking)
{
{ {
var newUsesFeature = CreateElement("uses-feature"); var newUsesFeature = CreateElement("uses-feature");
newUsesFeature.Attributes.Append(CreateAndroidAttribute("name", "wave.feature.handtracking")); newUsesFeature.Attributes.Append(CreateAndroidAttribute("name", "wave.feature.eyetracking"));
newUsesFeature.Attributes.Append(CreateAndroidAttribute("required", "true")); newUsesFeature.Attributes.Append(CreateAndroidAttribute("required", "true"));
ManifestElement.AppendChild(newUsesFeature); ManifestElement.AppendChild(newUsesFeature);
} }
if (enableLipexpression)
{
var newUsesFeature = CreateElement("uses-feature");
newUsesFeature.Attributes.Append(CreateAndroidAttribute("name", "wave.feature.lipexpression"));
newUsesFeature.Attributes.Append(CreateAndroidAttribute("required", "true"));
ManifestElement.AppendChild(newUsesFeature);
} }
if (CheckIfSimultaneousInteractionEnabled.IsEnabled) if (CheckIfSimultaneousInteractionEnabled.IsEnabled)

View File

@@ -101,7 +101,7 @@ namespace VIVE.OpenXR.Editor
return true; return true;
} }
static void OnUpdate() static void CheckScriptingDefineSymbols()
{ {
// Adds the script symbol if Vive OpenXR Plugin - Android is imported and assigned in XR Plugin-in Management. // Adds the script symbol if Vive OpenXR Plugin - Android is imported and assigned in XR Plugin-in Management.
if (ViveOpenXRAndroidAssigned) if (ViveOpenXRAndroidAssigned)
@@ -122,6 +122,10 @@ namespace VIVE.OpenXR.Editor
} }
} }
} }
static void OnUpdate()
{
//CheckScriptingDefineSymbols();
}
static CheckIfVIVEEnabled() static CheckIfVIVEEnabled()
{ {
EditorApplication.update += OnUpdate; EditorApplication.update += OnUpdate;

Binary file not shown.

View File

@@ -0,0 +1,70 @@
fileFormatVersion: 2
guid: f62b07ae6f5a8984b87047c2172cb605
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: x86_64
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System; using System;
using UnityEngine; using UnityEngine;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System; using System;
using System.Collections; using System.Collections;

View File

@@ -1,3 +1,5 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using AOT; using AOT;
using System; using System;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor; using UnityEditor;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor; using UnityEditor;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor; using UnityEditor;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor; using UnityEditor;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -1,3 +1,5 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;

View File

@@ -1,3 +1,5 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features; using UnityEngine.XR.OpenXR.Features;

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View File

@@ -1,4 +1,5 @@
// Copyright HTC Corporation All Rights Reserved. // Copyright HTC Corporation All Rights Reserved.
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor; using UnityEditor;
using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR;

View File

@@ -92,8 +92,7 @@ namespace VIVE.OpenXR.Hand
/// </summary> /// </summary>
public const string devicePose = "/input/grip/pose"; public const string devicePose = "/input/grip/pose";
[Preserve, InputControlLayout(displayName = "VIVE Hand Interaction (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" }, isGenericTypeOfDevice = true)]
[Preserve, InputControlLayout(displayName = "Vive Hand Interaction (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" }, isGenericTypeOfDevice = true)]
public class HandInteractionDevice : OpenXRDevice public class HandInteractionDevice : OpenXRDevice
{ {
const string LOG_TAG = "VIVE.OpenXR.Hand.ViveHandInteraction.HandInteractionDevice"; const string LOG_TAG = "VIVE.OpenXR.Hand.ViveHandInteraction.HandInteractionDevice";

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features; using UnityEngine.XR.OpenXR.Features;
@@ -275,9 +267,12 @@ namespace VIVE.OpenXR.Hand
hasRightHandTracker = false; hasRightHandTracker = false;
} }
if (m_XrSession == xrSession)
{
m_XrSession = 0; m_XrSession = 0;
m_XrSessionCreated = false; m_XrSessionCreated = false;
} }
}
#endregion #endregion
#region OpenXR function delegates #region OpenXR function delegates

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System; using System;

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4b787466a3699564f96519310fcc3333
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d8ae30c5b12cba64bb90ca25a485bd85
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
# 12.1. XR_HTC_path_enumeration
## Name String
XR_HTC_path_enumeration
## Revision
1
## Overview
The XR devices may offer the diversity and versatility in the practical use cases. For instance, some tracking devices can be bound to the hand-held objects and the application can locate the object via the offset between them. Another instance is to bind the tracking devices to the body parts, e.g. wrist, knee, and etc., to track the movement of body. Such XR devices cannot define all user paths beforehand due to they may be used by the multiple XR devices with one type or multiple types of hardware simultaneously.
This extension allows the application dynamically obtaining the user paths and input/output source paths associated with an interaction profile of XR device that the runtime has supported. When this extension is enabled, the application can get supported user paths for the interaction profile if setting user path XR_NULL_PATH. And if the application inputs the valid user path as well, the output paths will be the supported input/output source paths.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 33866d0b0b0b6b24986140a3599409e4
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0971d976cb6f80b41bc9fb13a78609c1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,122 @@
// Copyright HTC Corporation All Rights Reserved.
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
#endif
namespace VIVE.OpenXR
{
#if UNITY_EDITOR
[OpenXRFeature(UiName = "VIVE XR Path Enumeration",
BuildTargetGroups = new[] { BuildTargetGroup.Android },
Company = "HTC",
Desc = "The extension provides more flexibility for the user paths and input/output source paths related to an interaction profile. Developers can use this extension to obtain the path that the user has decided on.",
DocumentationLink = "..\\Documentation",
Version = "1.0.6",
OpenxrExtensionStrings = kOpenxrExtensionString,
FeatureId = featureId)]
#endif
public class VivePathEnumeration : OpenXRFeature
{
const string LOG_TAG = "VIVE.OpenXR.VivePathEnumeration ";
StringBuilder m_sb = null;
StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
/// <summary>
/// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_path_enumeration">12.1. XR_HTC_path_enumeration</see>.
/// </summary>
public const string kOpenxrExtensionString = "XR_HTC_path_enumeration";
/// <summary>
/// The feature id string. This is used to give the feature a well known id for reference.
/// </summary>
public const string featureId = "vive.wave.openxr.feature.pathenumeration";
#region OpenXR Life Cycle
#pragma warning disable
private bool m_XrInstanceCreated = false;
#pragma warning enable
private XrInstance m_XrInstance = 0;
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
/// </summary>
/// <param name="xrInstance">The created instance.</param>
/// <returns>True for valid <see cref="XrInstance">XrInstance</see></returns>
protected override bool OnInstanceCreate(ulong xrInstance)
{
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
{
sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(kOpenxrExtensionString).Append(" is NOT enabled."); WARNING(sb);
return false;
}
m_XrInstanceCreated = true;
m_XrInstance = xrInstance;
sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(m_XrInstance); DEBUG(sb);
return base.OnInstanceCreate(xrInstance);
}
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyInstance">xrDestroyInstance</see> is done.
/// </summary>
/// <param name="xrInstance">The instance to destroy.</param>
protected override void OnInstanceDestroy(ulong xrInstance)
{
if (m_XrInstance == xrInstance)
{
m_XrInstanceCreated = false;
m_XrInstance = 0;
}
sb.Clear().Append(LOG_TAG).Append("OnInstanceDestroy() ").Append(xrInstance); DEBUG(sb);
}
private XrSystemId m_XrSystemId = 0;
/// <summary>
/// Called when the <see cref="XrSystemId">XrSystemId</see> retrieved by <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystem">xrGetSystem</see> is changed.
/// </summary>
/// <param name="xrSystem">The system id.</param>
protected override void OnSystemChange(ulong xrSystem)
{
m_XrSystemId = xrSystem;
sb.Clear().Append(LOG_TAG).Append("OnSystemChange() ").Append(m_XrSystemId); DEBUG(sb);
}
private bool m_XrSessionCreated = false;
private XrSession m_XrSession = 0;
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateSession">xrCreateSession</see> is done.
/// </summary>
/// <param name="xrSession">The created session ID.</param>
protected override void OnSessionCreate(ulong xrSession)
{
m_XrSession = xrSession;
m_XrSessionCreated = true;
sb.Clear().Append(LOG_TAG).Append("OnSessionCreate() ").Append(m_XrSession); DEBUG(sb);
}
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroySession">xrDestroySession</see> is done.
/// </summary>
/// <param name="xrSession">The session ID to destroy.</param>
protected override void OnSessionDestroy(ulong xrSession)
{
sb.Clear().Append(LOG_TAG).Append("OnSessionDestroy() ").Append(xrSession); DEBUG(sb);
m_XrSession = 0;
m_XrSessionCreated = false;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80a759a91a5fd35479312cdd18e70a32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Runtime.InteropServices;
namespace VIVE.OpenXR
{
/// <summary>
/// When developers would like to retrieve the supported user paths from the interactionProfile, userPath should be <see cref="OpenXRHelper.XR_NULL_PATH">XR_NULL_PATH</see>. If the interaction profile for any of the suggested bindings does not exist in the allowlist defined in Interaction Profile Paths, the runtime must return <see cref="XrResult.XR_ERROR_PATH_UNSUPPORTED">XR_ERROR_PATH_UNSUPPORTED</see>.
///
/// If developers would like to retrieve the input/output paths related to the interactionProfile, userPath should be a valid user path.If userPath is not one of the device input subpaths described in section /user paths, the runtime must return <see cref="XrResult.XR_ERROR_PATH_UNSUPPORTED">XR_ERROR_PATH_UNSUPPORTED</see>.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct XrPathsForInteractionProfileEnumerateInfoHTC
{
/// <summary>
/// The <see cref="XrStructureType">XrStructureType</see> of this structure.
/// </summary>
public XrStructureType type;
/// <summary>
/// NULL or a pointer to the next structure in a structure chain.
/// </summary>
public IntPtr next;
/// <summary>
/// The <see cref="XrPath">XrPath</see> of an interaction profile.
/// </summary>
public XrPath interactionProfile;
/// <summary>
/// The top level user path the application would like to retrieve the interaction profile for. Set <see cref="OpenXRHelper.XR_NULL_PATH">XR_NULL_PATH</see> is used to enumerate all user paths from the input interactionProfile. Set as a valid user path is used to enumerate all input/output source paths.
/// </summary>
public XrPath userPath;
}
public static class VivePathEnumerationHelper
{
/// <summary>
/// Provided by XR_HTC_path_enumerate.
/// Developers should call this API with the value of pathCapacityInput equals to 0 to retrieve the size of paths from pathCountOutput. Then developers allocate the array of <see cref="XrPath">XrPath</see> data and assign the pathCapacityInput and call the API in the second time.
///
/// If the input pathCapacityInput is not sufficient to contain all output indices, the runtime must return <see cref="XrResult.XR_ERROR_SIZE_INSUFFICIENT">XR_ERROR_SIZE_INSUFFICIENT</see> on calls to <see cref="xrEnumeratePathsForInteractionProfileHTC">xrEnumeratePathsForInteractionProfileHTC</see> and not change the content in paths.
/// </summary>
/// <param name="instance">An <see cref="XrInstance">XrInstance</see> previously created.</param>
/// <param name="enumerateInfo">A <see cref="XrPathsForInteractionProfileEnumerateInfoHTC">XrPathsForInteractionProfileEnumerateInfoHTC</see> providing the query information.</param>
/// <param name="pathCapacityInput">The capacity of the paths array, or 0 to indicate a request to retrieve the required capacity.</param>
/// <param name="pathCountOutput">A pointer to the count of paths written, or a pointer to the required capacity in the case that pathCapacityInput is insufficient.</param>
/// <param name="paths">A pointer to an array of <see cref="XrPath">XrPath</see>, but can be NULL if pathCapacityInput is 0.</param>
/// <returns>XR_SUCCESS for success.</returns>
public delegate XrResult xrEnumeratePathsForInteractionProfileHTCDelegate(
XrInstance instance,
ref XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo,
UInt32 pathCapacityInput,
ref UInt32 pathCountOutput,
[In, Out] XrPath[] paths);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9d4f3e0bc01e6c34a9b9a1faf40aa93f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a2803d7b02d541e4aa45a020925a4b15
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -3,6 +3,9 @@
XR_HTC_vive_wrist_tracker_interaction XR_HTC_vive_wrist_tracker_interaction
## Revision ## Revision
1 1
## Overview
This extension provides an XrPath for getting device input from a VIVE wrist tracker to enable its interactions. VIVE wrist tracker is a tracked device mainly worn on user¡¦s wrist for pose tracking. Besides this use case, user also can tie it to a physical object to track its object pose, e.g. tie on a gun.
## VIVE Wrist Tracker input ## VIVE Wrist Tracker input
### Interaction profile path: ### Interaction profile path:
- /interaction_profiles/htc/vive_wrist_tracker - /interaction_profiles/htc/vive_wrist_tracker

View File

@@ -1,12 +1,4 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using UnityEngine.Scripting; using UnityEngine.Scripting;
using UnityEngine.XR.OpenXR.Features; using UnityEngine.XR.OpenXR.Features;
@@ -19,6 +11,7 @@ using UnityEngine.InputSystem;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine.XR; using UnityEngine.XR;
using UnityEngine.XR.OpenXR.Input; using UnityEngine.XR.OpenXR.Input;
using System.Text;
#if UNITY_EDITOR #if UNITY_EDITOR
using UnityEditor; using UnityEditor;
@@ -51,8 +44,15 @@ namespace VIVE.OpenXR.Tracker
public class ViveWristTracker : OpenXRInteractionFeature public class ViveWristTracker : OpenXRInteractionFeature
{ {
const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveWristTracker "; const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveWristTracker ";
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); } StringBuilder m_sb = null;
void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); } StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
/// <summary> /// <summary>
/// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_vive_wrist_tracker_interaction">12.72. XR_HTC_vive_wrist_tracker_interaction</see>. /// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_vive_wrist_tracker_interaction">12.72. XR_HTC_vive_wrist_tracker_interaction</see>.
@@ -97,11 +97,18 @@ namespace VIVE.OpenXR.Tracker
/// </summary> /// </summary>
public const string entityPose = "/input/entity_htc/pose"; public const string entityPose = "/input/entity_htc/pose";
[Preserve, InputControlLayout(displayName = "Vive Wrist Tracker (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" }, isGenericTypeOfDevice = true)] [Preserve, InputControlLayout(displayName = "VIVE Wrist Tracker (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" }, isGenericTypeOfDevice = true)]
public class WristTrackerDevice : OpenXRDevice public class WristTrackerDevice : OpenXRDevice
{ {
const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveWristTracker.WristTrackerDevice "; const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveWristTracker.WristTrackerDevice ";
void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); } StringBuilder m_sb = null;
StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
/// <summary> /// <summary>
/// A <see href="https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Controls.ButtonControl.html">ButtonControl</see> that represents the <see cref="buttonA"/> <see cref="buttonX"/> OpenXR bindings, depending on handedness. /// A <see href="https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Controls.ButtonControl.html">ButtonControl</see> that represents the <see cref="buttonA"/> <see cref="buttonX"/> OpenXR bindings, depending on handedness.
@@ -150,14 +157,6 @@ namespace VIVE.OpenXR.Tracker
/// </summary> /// </summary>
protected override void FinishSetup() protected override void FinishSetup()
{ {
/*for (int i = 0; i < InputSystem.devices.Count; i++)
{
var description = InputSystem.devices[i].description;
DEBUG("FinishSetup() device[" + i + "], interfaceName: " + description.interfaceName
+ ", deviceClass: " + description.deviceClass
+ ", product: " + description.product);
}*/
base.FinishSetup(); base.FinishSetup();
primaryButton = GetChildControl<ButtonControl>("primaryButton"); primaryButton = GetChildControl<ButtonControl>("primaryButton");
@@ -167,6 +166,13 @@ namespace VIVE.OpenXR.Tracker
trackingState = GetChildControl<IntegerControl>("trackingState"); trackingState = GetChildControl<IntegerControl>("trackingState");
devicePosition = GetChildControl<Vector3Control>("devicePosition"); devicePosition = GetChildControl<Vector3Control>("devicePosition");
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation"); deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
sb.Clear().Append(LOG_TAG)
.Append(" FinishSetup() device interfaceName: ").Append(description.interfaceName)
.Append(", deviceClass: ").Append(description.deviceClass)
.Append(", product: ").Append(description.product)
.Append(", serial: ").Append(description.serial);
DEBUG(sb);
} }
} }
@@ -181,16 +187,9 @@ namespace VIVE.OpenXR.Tracker
/// <returns>True for valid <see cref="XrInstance">XrInstance</see></returns> /// <returns>True for valid <see cref="XrInstance">XrInstance</see></returns>
protected override bool OnInstanceCreate(ulong xrInstance) protected override bool OnInstanceCreate(ulong xrInstance)
{ {
// Requires the eye tracking extension
/*if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
{
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
return false;
}*/
m_XrInstanceCreated = true; m_XrInstanceCreated = true;
m_XrInstance = xrInstance; m_XrInstance = xrInstance;
DEBUG("OnInstanceCreate() " + m_XrInstance); sb.Clear().Append(LOG_TAG).Append(" OnInstanceCreate() " + m_XrInstance); DEBUG(sb);
return base.OnInstanceCreate(xrInstance); return base.OnInstanceCreate(xrInstance);
} }

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fd92cd073be07db44b618664155722f5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4783b79dbfbfd324389e68597dd47bae
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
# 12.2. XR_HTC_vive_xr_tracker_interaction
## Name String
XR_HTC_vive_xr_tracker_interaction
## Revision
1
## Overview
This extension provides an XrPath for getting device input from VIVE XR tracker to enable its interactions. VIVE XR tracker is the tracked device which can be bound to the physical objects to make them trackable. VIVE XR tracker is a generic name means the series of various tracked devices, not just meaning one specific type of hardware. For example, VIVE XR tracker can be bound to user<65><72>s hands or feet to track the motion of human body. It also can be bound to any other objects that the user wants to track and interact with.
## VIVE Wrist Tracker input
### Interaction profile path:
- /interaction_profiles/htc/vive_xr_tracker
### Valid for user paths:
- /user/xr_tracker_htc/vive_self_tracker_0
- /user/xr_tracker_htc/vive_self_tracker_1
- /user/xr_tracker_htc/vive_self_tracker_2
- /user/xr_tracker_htc/vive_self_tracker_3
- /user/xr_tracker_htc/vive_self_tracker_4
### Supported input source
- <20>K/input/entity_htc/pose
The entity_htc pose allows the applications to recognize the origin of a tracked input device, especially for the wearable devices which are not held in the user<65><72>s hand. The entity_htc pose is defined as follows:
- The entity position: The center position of the tracked device.
- The entity orientation: Oriented with +Y up, +X to the right, and -Z forward.
## Vive Plugin
After adding the "VIVE XR Tracker" to "Project Settings > XR Plugin-in Management > OpenXR > Android Tab > Interaction Profiles", you can use the following Input Action Pathes.
- <ViveXRTracker>{TrackerN}/devicePose: Tracker N's pose.
- <ViveXRTracker>{TrackerN}/isTracked: Tracker N's pose is tracked.
- <ViveXRTracker>{TrackerN}/trackingState: Tracker N's tracking state.
- <ViveXRTracker>{TrackerN}/devicePosition: Tracker N's position.
- <ViveXRTracker>{TrackerN}/deviceRotation: Tracker N's rotation.
Refer to the <Vive XR OpenXR sample path>/Toolkits/Commons/ActionMap/InputActions.inputActions about the "Input Action Path" usage and the sample <Vive XR OpenXR sample path>/Toolkits/Input/OpenXRInput.unity.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f792cf08762516244848f0b8e059e4fb
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c18260151d6911542bbab0dc34679c51
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,850 @@
// Copyright HTC Corporation All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.XR;
using UnityEngine.Scripting;
using UnityEngine.XR;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine.XR.OpenXR.Input;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
using System.Linq;
#endif
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
#else
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
#endif
namespace VIVE.OpenXR.Tracker
{
/// <summary>
/// This <see cref="OpenXRInteractionFeature"/> enables the use of tracker interaction profiles in OpenXR. It enables XR_HTC_vive_xr_tracker_interaction in the underyling runtime.
/// </summary>
#if UNITY_EDITOR
[OpenXRFeature(UiName = "VIVE XR Tracker",
BuildTargetGroups = new[] { BuildTargetGroup.Android },
Company = "HTC",
Desc = "Support for enabling the vive xr tracker interaction profile. Will register the controller map for xr tracker if enabled.",
DocumentationLink = "..\\Documentation",
Version = "1.0.6",
OpenxrExtensionStrings = kOpenxrExtensionString,
Category = FeatureCategory.Interaction,
FeatureId = featureId)]
#endif
public class ViveXRTracker : OpenXRInteractionFeature
{
const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveXRTracker ";
static StringBuilder m_sb = null;
static StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
static void DEBUG(StringBuilder msg) { Debug.Log(msg); }
static void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
static void ERROR(StringBuilder msg) { Debug.LogError(msg); }
private static ViveXRTracker m_Instance = null;
/// <summary>
/// OpenXR specification.
/// </summary>
public const string kOpenxrExtensionString = "XR_HTC_vive_xr_tracker_interaction";
/// <summary>
/// The feature id string. This is used to give the feature a well known id for reference.
/// </summary>
public const string featureId = "vive.wave.openxr.feature.xrtracker";
#region Tracker Product Name
public const string kProductUltimateTracker = "VIVE Ultimate Tracker";
public const string kProductTrackingTag = "VIVE Tracking Tag";
public const string kProductUltimateTracker0 = "VIVE Ultimate Tracker 0";
public const string kProductUltimateTracker1 = "VIVE Ultimate Tracker 1";
public const string kProductUltimateTracker2 = "VIVE Ultimate Tracker 2";
public const string kProductUltimateTracker3 = "VIVE Ultimate Tracker 3";
public const string kProductUltimateTracker4 = "VIVE Ultimate Tracker 4";
private const string kProducts = "^(" + kProductUltimateTracker
+ ")|^(" + kProductUltimateTracker0 + ")|^(" + kProductUltimateTracker1 + ")|^(" + kProductUltimateTracker2 + ")|^(" + kProductUltimateTracker3 + ")|^(" + kProductUltimateTracker4
+ ")|^(" + kProductTrackingTag + ")";
private readonly string[] s_UltimateTrackerProduct = { kProductUltimateTracker0, kProductUltimateTracker1, kProductUltimateTracker2, kProductUltimateTracker3, kProductUltimateTracker4 };
#endregion
#region Tracker Action Map Name
const string kUltimateTrackerActionMap0 = "viveultimatetracker0";
const string kUltimateTrackerActionMap1 = "viveultimatetracker1";
const string kUltimateTrackerActionMap2 = "viveultimatetracker2";
const string kUltimateTrackerActionMap3 = "viveultimatetracker3";
const string kUltimateTrackerActionMap4 = "viveultimatetracker4";
#endregion
#region Tracker Usage
const string kTrackerUsage0 = "Tracker 0";
const string kTrackerUsage1 = "Tracker 1";
const string kTrackerUsage2 = "Tracker 2";
const string kTrackerUsage3 = "Tracker 3";
const string kTrackerUsage4 = "Tracker 4";
public const string kTrackerRoleUndefined = "Undefined";
public const string kTrackerRoleWaist = "Waist";
public const string kTrackerRoleChest = "Chest";
public const string kTrackerRoleLeftElbow = "Left Elbow";
public const string kTrackerRoleLeftWrist = "Left Wrist";
public const string kTrackerRoleLeftKnee = "Left Knee";
public const string kTrackerRoleLeftAnkle = "Left Ankle";
public const string kTrackerRoleLeftFoot = "Left Foot";
public const string kTrackerRoleRightElbow = "Right Elbow";
public const string kTrackerRoleRightWrist = "Right Wrist";
public const string kTrackerRoleRightKnee = "Right Knee";
public const string kTrackerRoleRightAnkle = "Right Ankle";
public const string kTrackerRoleRightFoot = "Right Foot";
private readonly List<string> s_TrackerRoleName = new List<string>()
{
kTrackerRoleWaist, kTrackerRoleChest,
kTrackerRoleLeftElbow, kTrackerRoleLeftWrist, kTrackerRoleLeftKnee, kTrackerRoleLeftAnkle, kTrackerRoleLeftFoot,
kTrackerRoleRightElbow, kTrackerRoleRightWrist, kTrackerRoleRightKnee, kTrackerRoleRightAnkle, kTrackerRoleRightFoot,
};
#endregion
#region Tracker User Path
public const string kUltimateTrackerPath0 = "/user/xr_tracker_htc/vive_ultimate_tracker_0";
public const string kUltimateTrackerPath1 = "/user/xr_tracker_htc/vive_ultimate_tracker_1";
public const string kUltimateTrackerPath2 = "/user/xr_tracker_htc/vive_ultimate_tracker_2";
public const string kUltimateTrackerPath3 = "/user/xr_tracker_htc/vive_ultimate_tracker_3";
public const string kUltimateTrackerPath4 = "/user/xr_tracker_htc/vive_ultimate_tracker_4";
#endregion
#region Tracker Serial Number
public const string kUltimateTrackerSN0 = "VIVE_Ultimate_Tracker_0";
public const string kUltimateTrackerSN1 = "VIVE_Ultimate_Tracker_1";
public const string kUltimateTrackerSN2 = "VIVE_Ultimate_Tracker_2";
public const string kUltimateTrackerSN3 = "VIVE_Ultimate_Tracker_3";
public const string kUltimateTrackerSN4 = "VIVE_Ultimate_Tracker_4";
public const string kTrackingTagSN0 = "VIVE_Tracking_Tag_0";
public const string kTrackingTagSN1 = "VIVE_Tracking_Tag_1";
public const string kTrackingTagSN2 = "VIVE_Tracking_Tag_2";
public const string kTrackingTagSN3 = "VIVE_Tracking_Tag_3";
public const string kTrackingTagSN4 = "VIVE_Tracking_Tag_4";
public const string k6DoFTrackerSN0 = "VIVE_6DoF_Tracker_a_0";
public const string k6DoFTrackerSN1 = "VIVE_6DoF_Tracker_a_1";
private readonly List<string> s_TrackerSerialNumber = new List<string>()
{
kUltimateTrackerSN0, kUltimateTrackerSN1, kUltimateTrackerSN2, kUltimateTrackerSN3, kUltimateTrackerSN4,
kTrackingTagSN0, kTrackingTagSN1, kTrackingTagSN2, kTrackingTagSN3, kTrackingTagSN4,
k6DoFTrackerSN0, k6DoFTrackerSN1
};
#endregion
#region Tracker Product Maps
private Dictionary<string, string> m_UltimateTrackerActionMap = new Dictionary<string, string>() {
{ kProductUltimateTracker0, kUltimateTrackerActionMap0 },
{ kProductUltimateTracker1, kUltimateTrackerActionMap1 },
{ kProductUltimateTracker2, kUltimateTrackerActionMap2 },
{ kProductUltimateTracker3, kUltimateTrackerActionMap3 },
{ kProductUltimateTracker4, kUltimateTrackerActionMap4 },
};
/// <summary> Mapping from product to tracker usage. </summary>
private static Dictionary<string, string> m_UltimateTrackerUsageMap = new Dictionary<string, string>() {
{ kProductUltimateTracker0, kTrackerUsage0 },
{ kProductUltimateTracker1, kTrackerUsage1 },
{ kProductUltimateTracker2, kTrackerUsage2 },
{ kProductUltimateTracker3, kTrackerUsage3 },
{ kProductUltimateTracker4, kTrackerUsage4 },
};
/// <summary> Mapping from product to user path. </summary>
private Dictionary<string, string> m_UltimateTrackerPathMap = new Dictionary<string, string>() {
{ kProductUltimateTracker0, kUltimateTrackerPath0 },
{ kProductUltimateTracker1, kUltimateTrackerPath1 },
{ kProductUltimateTracker2, kUltimateTrackerPath2 },
{ kProductUltimateTracker3, kUltimateTrackerPath3 },
{ kProductUltimateTracker4, kUltimateTrackerPath4 },
};
/// <summary> Mapping from product to serial number. </summary>
private Dictionary<string, string> m_UltimateTrackerSerialMap = new Dictionary<string, string>() {
{ kProductUltimateTracker0, kUltimateTrackerSN0 },
{ kProductUltimateTracker1, kUltimateTrackerSN1 },
{ kProductUltimateTracker2, kUltimateTrackerSN2 },
{ kProductUltimateTracker3, kUltimateTrackerSN3 },
{ kProductUltimateTracker4, kUltimateTrackerSN4 },
};
#endregion
[Preserve, InputControlLayout(displayName = "VIVE XR Tracker (OpenXR)", commonUsages = new[] {
kTrackerUsage0, kTrackerUsage1, kTrackerUsage2, kTrackerUsage3, kTrackerUsage4,
//kTrackerRoleWaist, kTrackerRoleChest,
//kTrackerRoleLeftElbow, kTrackerRoleLeftWrist, kTrackerRoleLeftKnee, kTrackerRoleLeftAnkle, kTrackerRoleLeftFoot,
//kTrackerRoleRightElbow, kTrackerRoleRightWrist, kTrackerRoleRightKnee, kTrackerRoleRightAnkle, kTrackerRoleRightFoot,
}, isGenericTypeOfDevice = true)]
public class XrTrackerDevice : OpenXRDevice, IInputUpdateCallbackReceiver
{
const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveXRTracker.XrTrackerDevice ";
StringBuilder m_sb = null;
StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
/// <summary>
/// A <see cref="PoseControl"/> that represents the <see cref="entityPose"/> OpenXR binding. The entity pose represents the location of the tracker.
/// </summary>
[Preserve, InputControl(offset = 0, aliases = new[] { "device", "entityPose" }, usage = "Device")]
public PoseControl devicePose { get; private set; }
/// <summary>
/// A <see href="https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Controls.ButtonControl.html">ButtonControl</see> required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked.
/// </summary>
[Preserve, InputControl(offset = 0, usage = "IsTracked")]
public ButtonControl isTracked { get; private set; }
/// <summary>
/// A <see href="https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Controls.IntegerControl.html">IntegerControl</see> required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping devicePose/trackingState.
/// </summary>
[Preserve, InputControl(offset = 4, usage = "TrackingState")]
public IntegerControl trackingState { get; private set; }
/// <summary>
/// A <see href="https://docs.unity3d.com/Packages/com.unity.inputsystem@0.1/api/UnityEngine.Experimental.Input.Controls.Vector3Control.html">Vector3Control</see> required for backwards compatibility with the XRSDK layouts. This is the device position. For the VIVE XR device, this is both the device and the pointer position. This value is equivalent to mapping devicePose/position.
/// </summary>
[Preserve, InputControl(offset = 8, alias = "gripPosition")]
public Vector3Control devicePosition { get; private set; }
/// <summary>
/// A <see href="https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Controls.QuaternionControl.html">QuaternionControl</see> required for backwards compatibility with the XRSDK layouts. This is the device orientation. For the VIVE XR device, this is both the device and the pointer rotation. This value is equivalent to mapping devicePose/rotation.
/// </summary>
[Preserve, InputControl(offset = 20, alias = "gripOrientation")]
public QuaternionControl deviceRotation { get; private set; }
// Unity action binding path: <ViveXRTracker>{Tracker 0}/isTracked
private bool UpdateInputDeviceInRuntime = true;
private InputAction inputActionIsTracked = null;
/// <summary>
/// Internal call used to assign controls to the the correct element.
/// </summary>
protected override void FinishSetup()
{
base.FinishSetup();
if (!m_UltimateTrackerUsageMap.ContainsKey(description.product))
{
sb.Clear().Append(LOG_TAG).Append("FinishSetup() Not support the product " + description.product); WARNING(sb);
return;
}
devicePose = GetChildControl<PoseControl>("devicePose");
isTracked = GetChildControl<ButtonControl>("isTracked");
trackingState = GetChildControl<IntegerControl>("trackingState");
devicePosition = GetChildControl<Vector3Control>("devicePosition");
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
/// After RegisterActionMapsWithRuntime finished, each XrTrackerDevice will have a product name (e.g. kProductUltimateTracker0)
/// We have to assign the XrTrackerDeivce to a commonUsage (e.g. kTrackerUsage0)
///
/// Since we already established the m_UltimateTrackerUsageMap (kProductUltimateTracker0, kTrackerUsage0),
/// we can simply call SetDeviceUsage(m_UltimateTrackerUsageMap[description.product])
/// to set assign the XrTrackerDevice with product name kProductUltimateTracker0 to the commonUsage kTrackerUsage0.
InputSystem.SetDeviceUsage(this, m_UltimateTrackerUsageMap[description.product]);
if (inputActionIsTracked == null)
{
//string actionBindingIsTracked = "<XRController>{LeftHand}/isTracked";
string actionBindingIsTracked = "<" + kLayoutName + ">{" + m_UltimateTrackerUsageMap[description.product] + "}/isTracked";
sb.Clear().Append(LOG_TAG).Append("FinishSetup() ").Append(m_UltimateTrackerUsageMap[description.product]).Append(", acion binding of IsTracked: ").Append(actionBindingIsTracked);
DEBUG(sb);
inputActionIsTracked = new InputAction(
type: InputActionType.Value,
binding: actionBindingIsTracked);
inputActionIsTracked.Enable();
}
sb.Clear().Append(LOG_TAG)
.Append("FinishSetup() device interfaceName: ").Append(description.interfaceName)
.Append(", deviceClass: ").Append(description.deviceClass)
.Append(", product: ").Append(description.product)
.Append(", serial: ").Append(description.serial)
.Append(", usage: ").Append(m_UltimateTrackerUsageMap[description.product])
.Append(", current profile: ").Append(profile);
DEBUG(sb);
}
private bool bRoleUpdated = false;
public void OnUpdate()
{
if (!UpdateInputDeviceInRuntime) { return; }
if (m_Instance == null) { return; }
if (inputActionIsTracked == null) { return; }
/// Updates the Usage (tracker role) when IsTracked becomes true.
if (inputActionIsTracked.ReadValue<float>() > 0 && !bRoleUpdated)
{
sb.Clear().Append(LOG_TAG)
.Append("OnUpdate() Update the InputDevice with product: ").Append(description.product); DEBUG(sb);
//m_Instance.UpdateInputDevice(description.product, entityPose);
bRoleUpdated = true;
}
}
}
/// <summary>The interaction profile string used to reference the wrist tracker interaction input device.</summary>
private const string profile = "/interaction_profiles/htc/vive_xr_tracker";
#region OpenXR Life Cycle
#pragma warning disable
private bool m_XrInstanceCreated = false;
#pragma warning restore
private XrInstance m_XrInstance = 0;
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
/// </summary>
/// <param name="xrInstance">The created instance.</param>
/// <returns>True for valid <see cref="XrInstance">XrInstance</see></returns>
protected override bool OnInstanceCreate(ulong xrInstance)
{
m_XrInstanceCreated = true;
m_XrInstance = xrInstance;
m_Instance = this;
sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(m_XrInstance); DEBUG(sb);
GetXrFunctionDelegates(m_XrInstance);
return true;
}
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyInstance">xrDestroyInstance</see> is done.
/// </summary>
/// <param name="xrInstance">The instance to destroy.</param>
protected override void OnInstanceDestroy(ulong xrInstance)
{
if (m_XrInstance == xrInstance)
{
m_XrInstanceCreated = false;
m_XrInstance = 0;
}
sb.Clear().Append(LOG_TAG).Append("OnInstanceDestroy() ").Append(xrInstance); DEBUG(sb);
}
private static bool m_XrSessionCreated = false;
private static XrSession m_XrSession = 0;
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateSession">xrCreateSession</see> is done.
/// </summary>
/// <param name="xrSession">The created session ID.</param>
protected override void OnSessionCreate(ulong xrSession)
{
m_XrSession = xrSession;
m_XrSessionCreated = true;
sb.Clear().Append(LOG_TAG).Append("OnSessionCreate() ").Append(m_XrSession); DEBUG(sb);
}
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroySession">xrDestroySession</see> is done.
/// </summary>
/// <param name="xrSession">The session ID to destroy.</param>
protected override void OnSessionDestroy(ulong xrSession)
{
sb.Clear().Append(LOG_TAG).Append("OnSessionDestroy() ").Append(xrSession); DEBUG(sb);
if (m_XrSession == xrSession)
{
m_XrSession = 0;
m_XrSessionCreated = false;
}
}
#endregion
// "<" + kLayoutName + ">{" + m_UltimateTrackerUsageMap[description.product] + "}/isTracked"
private const string kLayoutName = "ViveXRTracker";
/// <summary>
/// Registers the <see cref="XrTrackerDevice"/> layout with product name to the Input System.
/// </summary>
protected override void RegisterDeviceLayout()
{
sb.Clear().Append(LOG_TAG).Append("RegisterDeviceLayout() ").Append(kLayoutName).Append(", product: ").Append(@kProducts); DEBUG(sb);
InputSystem.RegisterLayout(typeof(XrTrackerDevice),
kLayoutName,
matches: new InputDeviceMatcher()
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
.WithProduct(@kProducts));
}
/// <summary>
/// Removes the <see cref="XrTrackerDevice"/> layout from the Input System.
/// </summary>
protected override void UnregisterDeviceLayout()
{
sb.Clear().Append(LOG_TAG).Append("UnregisterDeviceLayout() ").Append(kLayoutName); DEBUG(sb);
InputSystem.RemoveLayout(kLayoutName);
}
#region OpenXR function delegates
/// xrGetInstanceProcAddr
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
/// xrEnumeratePathsForInteractionProfileHTC
VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate xrEnumeratePathsForInteractionProfileHTC = null;
/// xrGetInputSourceLocalizedName
static OpenXRHelper.xrGetInputSourceLocalizedNameDelegate xrGetInputSourceLocalizedName = null;
/// xrEnumerateInstanceExtensionProperties
OpenXRHelper.xrEnumerateInstanceExtensionPropertiesDelegate xrEnumerateInstanceExtensionProperties = null;
private bool GetXrFunctionDelegates(XrInstance xrInstance)
{
/// xrGetInstanceProcAddr
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
{
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrGetInstanceProcAddr."); DEBUG(sb);
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
xrGetInstanceProcAddr,
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInstanceProcAddr"); ERROR(sb);
return false;
}
IntPtr funcPtr = IntPtr.Zero;
/// xrEnumerateInstanceExtensionProperties
if (XrGetInstanceProcAddr(xrInstance, "xrEnumerateInstanceExtensionProperties", out funcPtr) == XrResult.XR_SUCCESS)
{
if (funcPtr != IntPtr.Zero)
{
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrEnumerateInstanceExtensionProperties."); DEBUG(sb);
xrEnumerateInstanceExtensionProperties = Marshal.GetDelegateForFunctionPointer(
funcPtr,
typeof(OpenXRHelper.xrEnumerateInstanceExtensionPropertiesDelegate)) as OpenXRHelper.xrEnumerateInstanceExtensionPropertiesDelegate;
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumerateInstanceExtensionProperties.");
ERROR(sb);
}
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumerateInstanceExtensionProperties");
ERROR(sb);
}
/// xrEnumeratePathsForInteractionProfileHTC
if (XrGetInstanceProcAddr(xrInstance, "xrEnumeratePathsForInteractionProfileHTC", out funcPtr) == XrResult.XR_SUCCESS)
{
if (funcPtr != IntPtr.Zero)
{
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrEnumeratePathsForInteractionProfileHTC."); DEBUG(sb);
xrEnumeratePathsForInteractionProfileHTC = Marshal.GetDelegateForFunctionPointer(
funcPtr,
typeof(VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate)) as VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate;
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC.");
ERROR(sb);
}
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC");
ERROR(sb);
}
/// xrGetInputSourceLocalizedName
if (XrGetInstanceProcAddr(xrInstance, "xrGetInputSourceLocalizedName", out funcPtr) == XrResult.XR_SUCCESS)
{
if (funcPtr != IntPtr.Zero)
{
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrGetInputSourceLocalizedName."); DEBUG(sb);
xrGetInputSourceLocalizedName = Marshal.GetDelegateForFunctionPointer(
funcPtr,
typeof(OpenXRHelper.xrGetInputSourceLocalizedNameDelegate)) as OpenXRHelper.xrGetInputSourceLocalizedNameDelegate;
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInputSourceLocalizedName.");
ERROR(sb);
}
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInputSourceLocalizedName");
ERROR(sb);
}
return true;
}
#endregion
/*private List<T> CreateList<T>(UInt32 count, T initialValue)
{
List<T> list = new List<T>();
for (int i = 0; i < count; i++)
list.Add(initialValue);
return list;
}
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
private bool EnumeratePath()
{
if (!m_XrInstanceCreated) { return false; }
string func = "EnumeratePath() ";
if (xrEnumeratePathsForInteractionProfileHTC == null)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC"); WARNING(sb);
return false;
}
// 1. Get user path count of /interaction_profiles/htc/vive_xr_tracker profile.
UInt32 trackerCount = 0;
enumerateInfo.type = XrStructureType.XR_TYPE_UNKNOWN;
enumerateInfo.next = IntPtr.Zero;
enumerateInfo.interactionProfile = StringToPath(profile); // /interaction_profiles/htc/vive_xr_tracker
enumerateInfo.userPath = OpenXRHelper.XR_NULL_PATH;
XrResult result = xrEnumeratePathsForInteractionProfileHTC(m_XrInstance, ref enumerateInfo, 0, ref trackerCount, null);
if (result != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Retrieves trackerCount failed."); ERROR(sb);
return false;
}
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Get profile ").Append(profile).Append(" user path count: ").Append(trackerCount); DEBUG(sb);
if (trackerCount > 0)
{
// 2. Get user paths of /interaction_profiles/htc/vive_xr_tracker profile.
List<XrPath> trackerList = CreateList<XrPath>(trackerCount, OpenXRHelper.XR_NULL_PATH);
XrPath[] trackers = trackerList.ToArray();
result = xrEnumeratePathsForInteractionProfileHTC(
m_XrInstance,
ref enumerateInfo,
pathCapacityInput: (UInt32)(trackers.Length & 0x7FFFFFFF),
pathCountOutput: ref trackerCount,
paths: trackers);
if (result != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Retrieves trackers failed."); ERROR(sb);
return false;
}
UpdateMapsUltimateTracker(trackers, (Int32)(trackerCount & 0x7FFFFFFF));
}
return true;
}*/
#region Usage Map of Ultimate Tracker
/*private void UpdateMapsUltimateTracker(XrPath[] paths, int pathLength)
{
for (int i = 0; i < pathLength && i < s_UltimateTrackerProduct.Length; i++)
{
UpdateMapsUltimateTracker(paths[i], s_UltimateTrackerProduct[i]);
}
}
private void UpdateMapsUltimateTracker(string[] paths, string[] products)
{
for (int i = 0; i < paths.Length; i++)
{
XrPath path = StringToPath(paths[i]);
UpdateMapsUltimateTracker(path, products[i]);
}
}*/
private bool UpdateMapsUltimateTracker(XrPath path, string product)
{
string func = "UpdateMapsUltimateTracker() ";
string s_path = PathToString(path);
// -------------------- Tracker Role --------------------
sb.Clear().Append(LOG_TAG).Append(func)
.Append("path: ").Append(s_path)
.Append(", product: ").Append(product).Append(" GetInputSourceName(TrackerRole)"); DEBUG(sb);
if (GetInputSourceName(path, InputSourceType.TrackerRole, out string role) != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("GetInputSourceName of TrackerRole failed."); ERROR(sb);
return false;
}
sb.Clear().Append(LOG_TAG).Append(func)
.Append(s_path).Append(" has role: ").Append(role); DEBUG(sb);
if (!s_TrackerRoleName.Contains(role))
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("role ").Append(role).Append(" is NOT definied."); ERROR(sb);
return false;
}
/// For sample:
/// A user path (e.g. "/user/tracker_htc/index0") has the "sourceName" kTrackerRoleLeftWrist ("Left Wrist") which means
/// the corresponding product (e.g. kProductUltimateTracker0 = "VIVE Ultimate Tracker 0") has the "sourceName" kTrackerRoleLeftWrist.
/// So we have to set the usage of the product name kProductUltimateTracker0 to kTrackerRoleLeftWrist.
if (!m_UltimateTrackerUsageMap.ContainsKey(product))
m_UltimateTrackerUsageMap.Add(product, role);
else
m_UltimateTrackerUsageMap[product] = role;
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Sets product ").Append(product).Append(" with usage ").Append(role); DEBUG(sb);
// -------------------- Tracker Serial Number --------------------
sb.Clear().Append(LOG_TAG).Append(func)
.Append("path: ").Append(s_path)
.Append(", product: ").Append(product).Append(" GetInputSourceName(SerialNumber)"); DEBUG(sb);
if (GetInputSourceName(path, InputSourceType.SerialNumber, out string sn) != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("GetInputSourceName of SerialNumber failed."); ERROR(sb);
return false;
}
sb.Clear().Append(LOG_TAG).Append(func).Append(s_path).Append(" has serial number: ").Append(sn); DEBUG(sb);
if (!s_TrackerSerialNumber.Contains(sn))
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("serial number ").Append(sn).Append(" is NOT definied."); ERROR(sb);
return false;
}
/// For sample:
/// A user path (e.g. "/user/tracker_htc/index0") has the "sourceName" kUltimateTrackerSN0 ("VIVE_Ultimate_Tracker_0") which means
/// the corresponding product (e.g. kProductUltimateTracker0 = "VIVE Ultimate Tracker 0") has the "sourceName" kUltimateTrackerSN0.
/// So we have to set the serial of the product name kProductUltimateTracker0 to kUltimateTrackerSN0.
if (!m_UltimateTrackerSerialMap.ContainsKey(product))
m_UltimateTrackerSerialMap.Add(product, sn);
else
m_UltimateTrackerSerialMap[product] = sn;
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Sets product ").Append(product).Append(" with serial number ").Append(sn); DEBUG(sb);
return true;
}
#endregion
public enum InputSourceType : UInt64
{
SerialNumber = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC,
TrackerRole = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT,
}
public static XrResult GetInputSourceName(XrPath path, InputSourceType sourceType, out string sourceName)
{
string func = "GetInputSourceName() ";
sourceName = "";
if (!m_XrSessionCreated || xrGetInputSourceLocalizedName == null) { return XrResult.XR_ERROR_VALIDATION_FAILURE; }
string userPath = PathToString(path);
sb.Clear().Append(LOG_TAG).Append(func)
.Append("userPath: ").Append(userPath).Append(", flag: ").Append((UInt64)sourceType); DEBUG(sb);
XrInputSourceLocalizedNameGetInfo nameInfo = new XrInputSourceLocalizedNameGetInfo(
XrStructureType.XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO,
IntPtr.Zero, path, (XrInputSourceLocalizedNameFlags)sourceType);
UInt32 nameSizeIn = 0;
UInt32 nameSizeOut = 0;
char[] buffer = new char[0];
sb.Clear().Append(LOG_TAG).Append(func)
.Append("1.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut);
DEBUG(sb);
XrResult result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
if (result == XrResult.XR_SUCCESS)
{
if (nameSizeOut < 1)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append("bufferCountOutput size is invalid!");
ERROR(sb);
return XrResult.XR_ERROR_VALIDATION_FAILURE;
}
nameSizeIn = nameSizeOut;
buffer = new char[nameSizeIn];
sb.Clear().Append(LOG_TAG).Append(func)
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut);
DEBUG(sb);
result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
if (result == XrResult.XR_SUCCESS)
{
sourceName = new string(buffer).TrimEnd('\0');
sb.Clear().Append(LOG_TAG).Append(func)
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut)
.Append(", sourceName: ").Append(sourceName);
DEBUG(sb);
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut)
.Append(" result: ").Append(result);
ERROR(sb);
}
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("1.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut)
.Append(" result: ").Append(result);
ERROR(sb);
}
return result;
}
/*private void UpdateInputDevice(string product, string actionPath)
{
string func = "UpdateInputDevice() ";
sb.Clear().Append(LOG_TAG).Append(func)
.Append("product: ").Append(product)
.Append(", usage: ").Append(m_UltimateTrackerUsageMap[product])
.Append(" with user path: ").Append(m_UltimateTrackerPathMap[product]);
DEBUG(sb);
XrPath path = StringToPath(m_UltimateTrackerPathMap[product]);
if (UpdateMapsUltimateTracker(path, product))
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Maps of ").Append(product)
.Append(" with user path ").Append(path).Append(" are updated.");
DEBUG(sb);
bool foundProduct = false;
for (int i = 0; i < InputSystem.devices.Count; i++)
{
if (InputSystem.devices[i] is XrTrackerDevice &&
InputSystem.devices[i].description.product.Equals(product))
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Removes the XrTrackerDevice product ").Append(product);
DEBUG(sb);
InputSystem.RemoveDevice(InputSystem.devices[i]);
foundProduct = true;
break;
}
}
if (foundProduct)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("Adds a XrTrackerDevice product ").Append(product);
RegisterActionMap(product);
}
}
}*/
// Available Bindings
/// <summary>
/// Constant for a pose interaction binding '.../input/entity_htc/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs.
/// </summary>
public const string entityPose = "/input/entity_htc/pose";
private void RegisterActionMap(string product)
{
sb.Clear().Append(LOG_TAG).Append("RegisterActionMapsWithRuntime() Added ActionMapConfig of ").Append(m_UltimateTrackerPathMap[product])
.Append(", localizedName = ").Append(product)
.Append(" { name = ").Append(m_UltimateTrackerActionMap[product])
.Append(", desiredInteractionProfile = ").Append(profile)
.Append(", serialNumber = ").Append(m_UltimateTrackerSerialMap[product]);
DEBUG(sb);
ActionMapConfig actionMap = new ActionMapConfig()
{
name = m_UltimateTrackerActionMap[product],
localizedName = product,
desiredInteractionProfile = profile,
manufacturer = "HTC",
serialNumber = m_UltimateTrackerSerialMap[product],
deviceInfos = new List<DeviceConfig>()
{
new DeviceConfig()
{
characteristics = InputDeviceCharacteristics.TrackedDevice,
userPath = m_UltimateTrackerPathMap[product]
}
},
actions = new List<ActionConfig>()
{
// Device Pose
new ActionConfig()
{
name = "devicePose",
localizedName = "Grip Pose",
type = ActionType.Pose,
usages = new List<string>()
{
"Device"
},
bindings = new List<ActionBinding>()
{
new ActionBinding()
{
interactionPath = entityPose,
interactionProfileName = profile,
}
}
}
}
};
AddActionMap(actionMap);
}
/// <summary>
/// Registers action maps to Unity XR.
/// </summary>
protected override void RegisterActionMapsWithRuntime()
{
if (OpenXRHelper.IsExtensionSupported(xrEnumerateInstanceExtensionProperties, kOpenxrExtensionString) != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append("RegisterActionMapsWithRuntime() ").Append(kOpenxrExtensionString).Append(" is NOT supported."); DEBUG(sb);
return;
}
for (int userIndex = 0; userIndex < s_UltimateTrackerProduct.Length; userIndex++)
{
RegisterActionMap(s_UltimateTrackerProduct[userIndex]);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0c983a6e70e9b04885eb94528d82f46
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
using UnityEngine.XR; using UnityEngine.XR;
@@ -1068,6 +1069,134 @@ namespace VIVE.OpenXR
} }
} }
/// <summary>
/// Action sets are application-defined collections of actions. They are attached to a given XrSession with a xrAttachSessionActionSets call. They are enabled or disabled by the application via xrSyncActions depending on the current application context. For example, a game may have one set of actions that apply to controlling a character and another set for navigating a menu system. When these actions are grouped into two XrActionSet handles they can be selectively enabled and disabled using a single function call.
/// </summary>
public struct XrActionSet : IEquatable<UInt64>
{
private readonly UInt64 value;
public XrActionSet(UInt64 u)
{
value = u;
}
public static implicit operator UInt64(XrActionSet equatable)
{
return equatable.value;
}
public static implicit operator XrActionSet(UInt64 u)
{
return new XrActionSet(u);
}
public bool Equals(XrActionSet other)
{
return value == other.value;
}
public bool Equals(UInt64 other)
{
return value == other;
}
public override bool Equals(object obj)
{
return obj is XrActionSet && Equals((XrActionSet)obj);
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public override string ToString()
{
return value.ToString();
}
public static bool operator ==(XrActionSet a, XrActionSet b) { return a.Equals(b); }
public static bool operator !=(XrActionSet a, XrActionSet b) { return !a.Equals(b); }
public static bool operator >=(XrActionSet a, XrActionSet b) { return a.value >= b.value; }
public static bool operator <=(XrActionSet a, XrActionSet b) { return a.value <= b.value; }
public static bool operator >(XrActionSet a, XrActionSet b) { return a.value > b.value; }
public static bool operator <(XrActionSet a, XrActionSet b) { return a.value < b.value; }
public static XrActionSet operator +(XrActionSet a, XrActionSet b) { return a.value + b.value; }
public static XrActionSet operator -(XrActionSet a, XrActionSet b) { return a.value - b.value; }
public static XrActionSet operator *(XrActionSet a, XrActionSet b) { return a.value * b.value; }
public static XrActionSet operator /(XrActionSet a, XrActionSet b)
{
if (b.value == 0)
{
throw new DivideByZeroException();
}
return a.value / b.value;
}
}
/// <summary>
/// Action handles are used to refer to individual actions when retrieving action data, creating action spaces, or sending haptic events.
/// </summary>
public struct XrAction : IEquatable<UInt64>
{
private readonly UInt64 value;
public XrAction(UInt64 u)
{
value = u;
}
public static implicit operator UInt64(XrAction equatable)
{
return equatable.value;
}
public static implicit operator XrAction(UInt64 u)
{
return new XrAction(u);
}
public bool Equals(XrAction other)
{
return value == other.value;
}
public bool Equals(UInt64 other)
{
return value == other;
}
public override bool Equals(object obj)
{
return obj is XrAction && Equals((XrAction)obj);
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public override string ToString()
{
return value.ToString();
}
public static bool operator ==(XrAction a, XrAction b) { return a.Equals(b); }
public static bool operator !=(XrAction a, XrAction b) { return !a.Equals(b); }
public static bool operator >=(XrAction a, XrAction b) { return a.value >= b.value; }
public static bool operator <=(XrAction a, XrAction b) { return a.value <= b.value; }
public static bool operator >(XrAction a, XrAction b) { return a.value > b.value; }
public static bool operator <(XrAction a, XrAction b) { return a.value < b.value; }
public static XrAction operator +(XrAction a, XrAction b) { return a.value + b.value; }
public static XrAction operator -(XrAction a, XrAction b) { return a.value - b.value; }
public static XrAction operator *(XrAction a, XrAction b) { return a.value * b.value; }
public static XrAction operator /(XrAction a, XrAction b)
{
if (b.value == 0)
{
throw new DivideByZeroException();
}
return a.value / b.value;
}
}
/// <summary> /// <summary>
/// Flag bits for <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrSpaceLocationFlags">XrSpaceLocationFlags</see>. /// Flag bits for <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrSpaceLocationFlags">XrSpaceLocationFlags</see>.
/// </summary> /// </summary>
@@ -1091,6 +1220,35 @@ namespace VIVE.OpenXR
/// </summary> /// </summary>
XR_SPACE_LOCATION_POSITION_TRACKED_BIT = 0x00000008, XR_SPACE_LOCATION_POSITION_TRACKED_BIT = 0x00000008,
} }
public enum XrInputSourceLocalizedNameFlags : UInt64
{
/// <summary>
/// XrInputSourceLocalizedNameFlags bitmask 0x00000001 indicates that the runtime must include the user path portion of the string in the result, if available. E.g. Left Hand.
/// </summary>
XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT = 0x00000001,
/// <summary>
/// XrInputSourceLocalizedNameFlags bitmask 0x00000002 indicates that the runtime must include the interaction profile portion of the string in the result, if available. E.g. Vive Controller.
/// </summary>
XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT = 0x00000002,
/// <summary>
/// XrInputSourceLocalizedNameFlags bitmask 0x00000004 indicates that the runtime must include the input component portion of the string in the result, if available. E.g. Trigger.
/// </summary>
XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT = 0x00000004,
XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC = 0x1000000000000000,
}
public enum XrActionType : UInt64
{
XR_ACTION_TYPE_BOOLEAN_INPUT = 1,
XR_ACTION_TYPE_FLOAT_INPUT = 2,
XR_ACTION_TYPE_VECTOR2F_INPUT = 3,
XR_ACTION_TYPE_POSE_INPUT = 4,
XR_ACTION_TYPE_VIBRATION_OUTPUT = 100,
XR_ACTION_TYPE_MAX_ENUM = 0x7FFFFFFF
}
/// <summary> /// <summary>
/// An XrSystemId is an opaque atom used by the runtime to identify a system. The value <see cref="OpenXRHelper.XR_NULL_SYSTEM_ID">XR_NULL_SYSTEM_ID</see> is considered an invalid system. /// An XrSystemId is an opaque atom used by the runtime to identify a system. The value <see cref="OpenXRHelper.XR_NULL_SYSTEM_ID">XR_NULL_SYSTEM_ID</see> is considered an invalid system.
/// </summary> /// </summary>
@@ -1722,6 +1880,58 @@ namespace VIVE.OpenXR
public UInt32 mipCount; public UInt32 mipCount;
} }
public struct XrInputSourceLocalizedNameGetInfo
{
public XrStructureType type;
public IntPtr next;
public XrPath sourcePath;
public XrInputSourceLocalizedNameFlags whichComponents;
public XrInputSourceLocalizedNameGetInfo(XrStructureType in_type, IntPtr in_next, XrPath in_path, XrInputSourceLocalizedNameFlags in_flags)
{
type = in_type;
next = in_next;
sourcePath = in_path;
whichComponents = in_flags;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct XrExtensionProperties
{
public XrStructureType type;
public IntPtr next;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string extensionName;
public UInt32 extensionVersion;
}
public struct XrActionCreateInfo
{
public XrStructureType type;
public IntPtr next;
public char[] actionName;
public XrActionType actionType;
public UInt32 countSubactionPaths;
public XrPath[] subactionPaths;
public char[] localizedActionName;
};
public struct XrActionStateGetInfo
{
public XrStructureType type;
public IntPtr next;
public XrAction action;
public XrPath subactionPath;
}
public struct XrActionStatePose
{
public XrStructureType type;
public IntPtr next;
public XrBool32 isActive;
};
public static class OpenXRHelper public static class OpenXRHelper
{ {
/// <summary> /// <summary>
@@ -1738,19 +1948,23 @@ namespace VIVE.OpenXR
{ {
msg = "Null reference."; msg = "Null reference.";
return false; return false;
} else if (actionReference.action == null) }
else if (actionReference.action == null)
{ {
msg = "Null reference action."; msg = "Null reference action.";
return false; return false;
} else if (!actionReference.action.enabled) }
else if (!actionReference.action.enabled)
{ {
msg = "Reference action disabled."; msg = "Reference action disabled.";
return false; return false;
} else if (actionReference.action.activeControl == null) }
else if (actionReference.action.activeControl == null)
{ {
msg = "No active control of the reference action."; msg = "No active control of the reference action.";
return false; return false;
} else if (actionReference.action.controls.Count <= 0) }
else if (actionReference.action.controls.Count <= 0)
{ {
msg = "Action control count is " + actionReference.action.controls.Count; msg = "Action control count is " + actionReference.action.controls.Count;
return false; return false;
@@ -2002,6 +2216,103 @@ namespace VIVE.OpenXR
UInt32 displayRefreshRateCapacityInput, UInt32 displayRefreshRateCapacityInput,
out UInt32 displayRefreshRateCountOutput, out UInt32 displayRefreshRateCountOutput,
out float displayRefreshRates); out float displayRefreshRates);
public delegate XrResult xrGetInputSourceLocalizedNameDelegate(
XrSession session,
ref XrInputSourceLocalizedNameGetInfo getInfo,
[In] UInt32 bufferCapacityInput,
ref UInt32 bufferCountOutput,
[In, Out] char[] buffer);
public delegate XrResult xrEnumerateInstanceExtensionPropertiesDelegate(
[In] char[] layerName,
UInt32 propertyCapacityInput,
ref UInt32 propertyCountOutput,
[In, Out] XrExtensionProperties[] properties);
#region API
const string LOG_TAG = "VIVE.OpenXR.OpenXRHelper ";
static StringBuilder m_sb = null;
static StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
static void DEBUG(StringBuilder msg) { Debug.Log(msg); }
static void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
static void ERROR(StringBuilder msg) { Debug.LogError(msg); }
/// <summary>
/// Use <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrEnumerateInstanceExtensionProperties">xrEnumerateInstanceExtensionProperties</see> to check if an extension is supported by OpenXR Runtime.
/// </summary>
/// <param name="xrEnumerateInstanceExtensionProperties">Function pointer of <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrEnumerateInstanceExtensionProperties">xrEnumerateInstanceExtensionProperties</see>.</param>
/// <param name="extension">An OpenXR extension.</param>
/// <returns>XR_SUCCESS for supported.</returns>
public static XrResult IsExtensionSupported(xrEnumerateInstanceExtensionPropertiesDelegate xrEnumerateInstanceExtensionProperties, string extension)
{
XrResult result = XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
if (xrEnumerateInstanceExtensionProperties == null)
{
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() no xrEnumerateInstanceExtensionProperties function."); ERROR(sb);
return result;
}
UInt32 ext_count = 0;
result = xrEnumerateInstanceExtensionProperties(null, 0, ref ext_count, null);
if (result != XrResult.XR_SUCCESS || ext_count == 0)
{
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() Failed to enumerate number of extension properties, result: ").Append(result); ERROR(sb);
return result;
}
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() Runtime supports ").Append(ext_count).Append(" extensions"); DEBUG(sb);
XrExtensionProperties[] extensionProperties = new XrExtensionProperties[ext_count];
for (int i = 0; i < ext_count; i++)
{
extensionProperties[i].type = XrStructureType.XR_TYPE_EXTENSION_PROPERTIES;
extensionProperties[i].next = IntPtr.Zero;
}
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() xrEnumerateInstanceExtensionProperties propertyCapacityInput: ").Append(ext_count).Append(", propertyCountOutput: ").Append(ext_count).Append(", extensionProperties size: ").Append(extensionProperties.Length); DEBUG(sb);
result = xrEnumerateInstanceExtensionProperties(null, ext_count, ref ext_count, extensionProperties);
if (result != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() Failed to enumerate extension properties, result: ").Append(result); ERROR(sb);
return result;
}
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() Enumerate ").Append(ext_count).Append(" extensions"); DEBUG(sb);
bool supported = false;
for (UInt32 i = 0; i < ext_count; i++)
{
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() Extension[").Append(i).Append("] ").Append(extensionProperties[i].type)
.Append(", name: ").Append(extensionProperties[i].extensionName)
.Append(", version: ").Append(extensionProperties[i].extensionVersion);
DEBUG(sb);
if (extensionProperties[i].extensionName.Equals(extension))
{
supported = true;
break;
}
}
sb.Clear().Append(LOG_TAG).Append("IsExtensionSupported() ").Append(extension).Append(" is ").Append(supported ? "supported." : "not supported."); DEBUG(sb);
return supported ? XrResult.XR_SUCCESS : XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
}
#endregion
public delegate XrResult xrCreateActionDelegate(
XrActionSet actionSet,
ref XrActionCreateInfo createInfo,
ref XrAction action);
public delegate XrRect2Di xrGetActionStatePoseDelegate(
XrSession session,
ref XrActionStateGetInfo getInfo,
ref XrActionStatePose state);
} }
public static class ClientInterface public static class ClientInterface

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4b98be4b1bc959c4a914f4b7a484cdf8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4c76f10664e4be14c9f57109107c63ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5f35db36b366ffe458967ad9820f0b34
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,155 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace VIVE.OpenXR.Toolkits.CustomGesture
{
[System.Serializable]
public class CustomGesture
{
public string GestureName = "NewGesture";
public CGEnums.HandFlag TargetHand;
[HideInInspector] public CGEnums.FingerStatus ThumbStatus;
[HideInInspector] public CGEnums.FingerStatus IndexStatus;
[HideInInspector] public CGEnums.FingerStatus MiddleStatus;
[HideInInspector] public CGEnums.FingerStatus RingStatus;
[HideInInspector] public CGEnums.FingerStatus PinkyStatus;
public TargetFingerStatus ThumbStatusIs;
public TargetFingerStatus IndexStatusIs;
public TargetFingerStatus MiddleStatusIs;
public TargetFingerStatus RingStatusIs;
public TargetFingerStatus PinkyStatusIs;
public JointDistance DualHandDistance;
[Range(20, 0)]
public float DualNear = 0;
[Range(20, 0)]
public float DualFar = 0;
public JointDistance ThumbIndexDistance;
[Range(10, 0)]
public float SingleNear = 0;
[Range(10, 0)]
public float SingleFar = 0;
}
public enum JointDistance
{
DontCare = 0,
Near = 1,
Far = 2,
}
public class HandJoint
{
/// <summary>
/// Tells whether the data of this <see cref = "HandJoint">HandJoints</see> is valid or not; the data shouldn't be used if <c>isValid</c> returns false
/// </summary>
public bool isValid;
/// <summary>
/// Holds the position of the <see cref = "HandJoint">HandJoints</see>
/// </summary>
public Vector3 position;
/// <summary>
/// Holds the rotation of the <see cref = "HandJoint">HandJoints</see>
/// </summary>
public Quaternion rotation;
public HandJoint()
{
isValid = false;
position = Vector3.zero;
rotation = Quaternion.identity;
}
}
public class FingerStatusExpresstion
{
public bool Is = true;
public CGEnums.FingerStatus Status = CGEnums.FingerStatus.None; //Straight;
}
public enum TargetFingerStatus
{
DontCare = 0,
Straight = 1,
Bending = 2,
Bended = 3,
NotStraight = 4,
NotBending = 5,
NotBended = 6,
}
static class EnumExtensions
{
public static FingerStatusExpresstion ToExpresstion(this TargetFingerStatus _Status)
{
FingerStatusExpresstion _Expresstion = new FingerStatusExpresstion();
switch (_Status)
{
case TargetFingerStatus.Straight:
_Expresstion.Is = true;
_Expresstion.Status = CGEnums.FingerStatus.Straight;
break;
case TargetFingerStatus.Bending:
_Expresstion.Is = true;
_Expresstion.Status = CGEnums.FingerStatus.Bending;
break;
case TargetFingerStatus.Bended:
_Expresstion.Is = true;
_Expresstion.Status = CGEnums.FingerStatus.Bended;
break;
case TargetFingerStatus.NotStraight: //using
_Expresstion.Is = false;
_Expresstion.Status = CGEnums.FingerStatus.Straight;
break;
case TargetFingerStatus.NotBending:
_Expresstion.Is = false;
_Expresstion.Status = CGEnums.FingerStatus.Bending;
break;
case TargetFingerStatus.NotBended: //using
_Expresstion.Is = false;
_Expresstion.Status = CGEnums.FingerStatus.Bended;
break;
case TargetFingerStatus.DontCare:
_Expresstion.Is = false;//true;
_Expresstion.Status = CGEnums.FingerStatus.None;
break;
}
return _Expresstion;
}
}
[System.Serializable]
public class FingerStatusDefiner
{
[Range(180, 0)]
public float StraightDistalLowBound = 160;
[Range(180, 0)]
public float StraightIntermediateLowBound = 160;
[Range(180, 0)]
public float StraightProximalLowBound = 160;
[Range(180, 0)]
public float BendingDistalLowBound = 120;
[Range(180, 0)]
public float BendingIntermediateLowBound = 120;
[Range(180, 0)]
public float BendingProximalLowBound = 120;
[System.Serializable]
public class AngleRange
{
[Range(180, 0)]
public float LowBound = 0;
[Range(180, 0)]
public float HeighBound = 180;
public AngleRange() { }
public AngleRange(float _Heigh, float _Low)
{
HeighBound = _Heigh;
LowBound = _Low;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a35a47ea932ca8845b42e834c36d2205
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,154 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine.UI;
using VIVE.OpenXR.Hand;
namespace VIVE.OpenXR.Toolkits.CustomGesture
{
[RequireComponent(typeof(CustomGestureManager))]
public class CustomGestureDefiner : MonoBehaviour
{
public List<CustomGesture> DefinedGestures = new List<CustomGesture>();
static List<CustomGesture> definedGestures = new List<CustomGesture>();
CustomGestureManager HGM;
static CustomGestureManager hGM;
void Start()
{
HGM = GetComponent<CustomGestureManager>();
definedGestures = DefinedGestures;
hGM = HGM;
}
void Update()
{
}
public static bool IsCurrentGestureTriiggered(string _GestureName, CGEnums.HandFlag _Hand)
{
bool SingleDsLeft = true, SingleDsRight = true;
CustomGesture _Gesture = definedGestures.Find(_x => _x.GestureName == _GestureName);
if(_Gesture == null)
{
Debug.LogWarning("HandGesture is not definded");
return false;
}
if (hGM == null)
{
Debug.LogError("hGM is null");
return false;
}
float SingledisXLeft = 0.0f, SingledisYLeft = 0.0f, SingledisXRight = 0.0f, SingledisYRight = 0.0f, /*SingledisZ = 0.0f,*/ SingledistanceLeft = 0.0f, SingledistanceRight = 0.0f;
HandJoint[] _JointsL, _JointsR;
switch (_Hand)//(_Gesture.TargetHand)
{
case CGEnums.HandFlag.Left:
_JointsL = CustomGestureManager.GetHandJointLocations(CGEnums.HandFlag.Left);
SingledisXLeft = _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.x - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.x;
SingledisYLeft = _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.y - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.y;
//SingledisZ = _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.z - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.z;
SingledistanceLeft = Mathf.Sqrt(Mathf.Pow(SingledisXLeft, 2) + Mathf.Pow(SingledisYLeft, 2));
//Debug.Log("Single CheckHandDistance Thumb L: " + SingledisXLeft + ", " + SingledisYLeft + ", " + SingledisZ + ", distance:" + SingledistanceLeft*100);
if (_Gesture.ThumbIndexDistance != JointDistance.DontCare)
SingleDsLeft = (_Gesture.ThumbIndexDistance == JointDistance.Far) ? ((SingledistanceLeft * 100) > _Gesture.SingleFar) : ((SingledistanceLeft * 100) < _Gesture.SingleNear);
if ((_Gesture.ThumbStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Thumb) == _Gesture.ThumbStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Thumb) != _Gesture.ThumbStatusIs.ToExpresstion().Status) &&
(_Gesture.IndexStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Index) == _Gesture.IndexStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Index) != _Gesture.IndexStatusIs.ToExpresstion().Status) &&
(_Gesture.MiddleStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Middle) == _Gesture.MiddleStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Middle) != _Gesture.MiddleStatusIs.ToExpresstion().Status) &&
(_Gesture.RingStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Ring) == _Gesture.RingStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Ring) != _Gesture.RingStatusIs.ToExpresstion().Status) &&
(_Gesture.PinkyStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Pinky) == _Gesture.PinkyStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Pinky) != _Gesture.PinkyStatusIs.ToExpresstion().Status))
{
return (true && SingleDsLeft);
}
return false;
case CGEnums.HandFlag.Right:
_JointsR = CustomGestureManager.GetHandJointLocations(CGEnums.HandFlag.Right);
SingledisXRight = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.x - _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.x;
SingledisYRight = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.y - _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.y;
//SingledisZ = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.z - _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.z;
SingledistanceRight = Mathf.Sqrt(Mathf.Pow(SingledisXRight, 2) + Mathf.Pow(SingledisYRight, 2));
//Debug.Log("Single CheckHandDistance Thumb R: " + SingledisXRight + ", " + SingledisYRight + ", " + SingledisZ + ", distance:" + SingledistanceRight*100);
if (_Gesture.ThumbIndexDistance != JointDistance.DontCare)
SingleDsRight = (_Gesture.ThumbIndexDistance == JointDistance.Far) ? ((SingledistanceRight * 100) > _Gesture.SingleFar) : ((SingledistanceRight * 100) < _Gesture.SingleNear);
if ((_Gesture.ThumbStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Thumb) == _Gesture.ThumbStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Thumb) != _Gesture.ThumbStatusIs.ToExpresstion().Status) &&
(_Gesture.IndexStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Index) == _Gesture.IndexStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Index) != _Gesture.IndexStatusIs.ToExpresstion().Status) &&
(_Gesture.MiddleStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Middle) == _Gesture.MiddleStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Middle) != _Gesture.MiddleStatusIs.ToExpresstion().Status) &&
(_Gesture.RingStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Ring) == _Gesture.RingStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Ring) != _Gesture.RingStatusIs.ToExpresstion().Status) &&
(_Gesture.PinkyStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Pinky) == _Gesture.PinkyStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Pinky) != _Gesture.PinkyStatusIs.ToExpresstion().Status))
{
return (true && SingleDsRight);
}
return false;
case CGEnums.HandFlag.Either:
if ((_Gesture.ThumbStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Thumb) == _Gesture.ThumbStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Thumb) != _Gesture.ThumbStatusIs.ToExpresstion().Status) &&
(_Gesture.IndexStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Index) == _Gesture.IndexStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Index) != _Gesture.IndexStatusIs.ToExpresstion().Status) &&
(_Gesture.MiddleStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Middle) == _Gesture.MiddleStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Middle) != _Gesture.MiddleStatusIs.ToExpresstion().Status) &&
(_Gesture.RingStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Ring) == _Gesture.RingStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Ring) != _Gesture.RingStatusIs.ToExpresstion().Status) &&
(_Gesture.PinkyStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Pinky) == _Gesture.PinkyStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Pinky) != _Gesture.PinkyStatusIs.ToExpresstion().Status))
{
return true;
}
else if ((_Gesture.ThumbStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Thumb) == _Gesture.ThumbStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Thumb) != _Gesture.ThumbStatusIs.ToExpresstion().Status) &&
(_Gesture.IndexStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Index) == _Gesture.IndexStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Index) != _Gesture.IndexStatusIs.ToExpresstion().Status) &&
(_Gesture.MiddleStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Middle) == _Gesture.MiddleStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Middle) != _Gesture.MiddleStatusIs.ToExpresstion().Status) &&
(_Gesture.RingStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Ring) == _Gesture.RingStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Ring) != _Gesture.RingStatusIs.ToExpresstion().Status) &&
(_Gesture.PinkyStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Pinky) == _Gesture.PinkyStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Pinky) != _Gesture.PinkyStatusIs.ToExpresstion().Status))
{
return true;
}
return false;
case CGEnums.HandFlag.Dual:
_JointsL = CustomGestureManager.GetHandJointLocations(CGEnums.HandFlag.Left);
_JointsR = CustomGestureManager.GetHandJointLocations(CGEnums.HandFlag.Right);
SingledisXLeft = _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.x - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.x;
SingledisYLeft = _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.y - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.y;
//SingledisZ = _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.z - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.z;
SingledistanceLeft = Mathf.Sqrt(Mathf.Pow(SingledisXLeft, 2) + Mathf.Pow(SingledisYLeft, 2));
SingledisXRight = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.x - _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.x;
SingledisYRight = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.y - _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.y;
//SingledisZ = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.z - _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].position.z;
SingledistanceRight = Mathf.Sqrt(Mathf.Pow(SingledisXLeft, 2) + Mathf.Pow(SingledisYLeft, 2));
//Debug.Log("Single CheckHandDistance Thumb: " + SingledisYLeft + ", " + SingledisYLeft + ", " + SingledisZ + ", distance:" + SingledistanceLeft);
float DualdisX = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.x - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.x;
float DualdisY = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.y - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.y;
//float DualdisZ = _JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.z - _JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].position.z;
float Dualdistance = Mathf.Sqrt(Mathf.Pow(DualdisX, 2) + Mathf.Pow(DualdisY, 2));
//Debug.Log("Dual CheckHandDistance Index: " + DualdisX + ", " + DualdisY + ", " + DualdisZ + ", distance:" + Dualdistance);
bool LGesture = false, RGesture = false, DualDs = true;
if(_Gesture.DualHandDistance != JointDistance.DontCare)
DualDs = (_Gesture.DualHandDistance == JointDistance.Far) ? ((Dualdistance * 100) > _Gesture.DualFar) : ((Dualdistance * 100) < _Gesture.DualNear);
if(_Gesture.ThumbIndexDistance != JointDistance.DontCare)
SingleDsLeft = (_Gesture.ThumbIndexDistance == JointDistance.Far) ? ((SingledistanceLeft * 100) > _Gesture.SingleFar) : ((SingledistanceLeft * 100) < _Gesture.SingleNear);
if (_Gesture.ThumbIndexDistance != JointDistance.DontCare)
SingleDsRight = (_Gesture.ThumbIndexDistance == JointDistance.Far) ? ((SingledistanceRight * 100) > _Gesture.SingleFar) : ((SingledistanceRight * 100) < _Gesture.SingleNear);
LGesture = (_Gesture.ThumbStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Thumb) == _Gesture.ThumbStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Thumb) != _Gesture.ThumbStatusIs.ToExpresstion().Status) &&
(_Gesture.IndexStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Index) == _Gesture.IndexStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Index) != _Gesture.IndexStatusIs.ToExpresstion().Status) &&
(_Gesture.MiddleStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Middle) == _Gesture.MiddleStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Middle) != _Gesture.MiddleStatusIs.ToExpresstion().Status) &&
(_Gesture.RingStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Ring) == _Gesture.RingStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Ring) != _Gesture.RingStatusIs.ToExpresstion().Status) &&
(_Gesture.PinkyStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Pinky) == _Gesture.PinkyStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Left, CGEnums.FingerFlag.Pinky) != _Gesture.PinkyStatusIs.ToExpresstion().Status);
RGesture = (_Gesture.ThumbStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Thumb) == _Gesture.ThumbStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Thumb) != _Gesture.ThumbStatusIs.ToExpresstion().Status) &&
(_Gesture.IndexStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Index) == _Gesture.IndexStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Index) != _Gesture.IndexStatusIs.ToExpresstion().Status) &&
(_Gesture.MiddleStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Middle) == _Gesture.MiddleStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Middle) != _Gesture.MiddleStatusIs.ToExpresstion().Status) &&
(_Gesture.RingStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Ring) == _Gesture.RingStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Ring) != _Gesture.RingStatusIs.ToExpresstion().Status) &&
(_Gesture.PinkyStatusIs.ToExpresstion().Is ? hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Pinky) == _Gesture.PinkyStatusIs.ToExpresstion().Status : hGM.GetFingerStatus(CGEnums.HandFlag.Right, CGEnums.FingerFlag.Pinky) != _Gesture.PinkyStatusIs.ToExpresstion().Status);
//Debug.Log("Dual Gesture rs: "+ _GestureName + LGesture + ", " + RGesture + ", " + SingleDs + ", " + DualDs );
return (LGesture && RGesture && SingleDsLeft && DualDs && SingleDsRight);
default:
Debug.LogError("The HandFlag can only be set to Lef, Right or Both");
return false;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d622bde162fe6524aa1cf99ee5bbda11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
// Copyright HTC Corporation All Rights Reserved.
namespace VIVE.OpenXR.Toolkits.CustomGesture
{
public class CGEnums
{
public enum HandFlag
{
/// <summary>
/// The flag indicating no hand
/// </summary>
None = 0,
/// <summary>
/// The flag indicating the left hand
/// </summary>
Left = 1,
/// <summary>
/// The flag indicating the right hand
/// </summary>
Right = 2,
Either = 3,
Dual = 4,
Num = 5,
}
public enum FingerStatus
{
None = 0,
Straight = 1,
Bending = 2,
Bended = 3,
Num = 4,
}
public enum FingerFlag
{
None = 0,
Thumb = 1,
Index = 2,
Middle = 3,
Ring = 4,
Pinky = 5,
Num = 6,
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9ff3816ca3a86354f81e7ad4d29b2937
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,249 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using VIVE.OpenXR.Hand;
namespace VIVE.OpenXR.Toolkits.CustomGesture
{
//Straight = 1,
//Bending = 2,
//Bended = 3,
public class CustomGestureManager : MonoBehaviour
{
//public CGEnums.HandFlag Hand;
public FingerStatusDefiner ThumbDefiner;
public FingerStatusDefiner IndexDefiner;
public FingerStatusDefiner MiddleDefiner;
public FingerStatusDefiner RingDefiner;
public FingerStatusDefiner PinkyDefiner;
static HandJoint[] LeftHandJoints = NewHandJointArray();
static HandJoint[] RightHandJoints = NewHandJointArray();
static int LeftLastCalculationTime = -1;
static int RightLastCalculationTime = -1;
static HandJoint[] NewHandJointArray()
{
HandJoint[] _Joints = new HandJoint[(int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT];
for (int i = 0; i < _Joints.Length; i++)
{
_Joints[i] = new HandJoint();
}
return _Joints;
}
public static HandJoint[] GetHandJointLocations(CGEnums.HandFlag hand)
{
Update_HandJoints(hand);
return hand == CGEnums.HandFlag.Left ? LeftHandJoints : RightHandJoints;
}
static void Update_HandJoints(CGEnums.HandFlag hand)
{
if ((hand == CGEnums.HandFlag.Left ? LeftLastCalculationTime : RightLastCalculationTime) == Time.frameCount)
{
return;
}
if (hand == CGEnums.HandFlag.Left)
{
LeftLastCalculationTime = Time.frameCount;
}
else
{
RightLastCalculationTime = Time.frameCount;
}
XrHandJointLocationEXT[] jointLocations = new XrHandJointLocationEXT[(int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT];
ViveHandTracking feature = OpenXRSettings.Instance.GetFeature<ViveHandTracking>();
//Debug.Log("CustomGestureManager GetHandJointLocations() feat: " + feature
//+", fGetJLocLeft: " + feature.GetJointLocations(hand == CGEnums.HandFlag.Left, out jointLocations)
//+", fGetJLocRight: " + feature.GetJointLocations(hand == CGEnums.HandFlag.Right, out jointLocations));
if (feature && feature.GetJointLocations(hand == CGEnums.HandFlag.Left, out jointLocations))
{
//Debug.Log("CustomGestureManager GetHandJointLocations()!");
for (int i = 0; i < (int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT; i++)
{
if (hand == CGEnums.HandFlag.Left)
{
LeftHandJoints[i].position.x = jointLocations[i].pose.position.x;
LeftHandJoints[i].position.y = jointLocations[i].pose.position.y;
LeftHandJoints[i].position.z = -jointLocations[i].pose.position.z;
LeftHandJoints[i].rotation.x = jointLocations[i].pose.orientation.x;
LeftHandJoints[i].rotation.y = jointLocations[i].pose.orientation.y;
LeftHandJoints[i].rotation.z = -jointLocations[i].pose.orientation.z;
LeftHandJoints[i].rotation.w = -jointLocations[i].pose.orientation.w;
}
else
{
RightHandJoints[i].position.x = jointLocations[i].pose.position.x;
RightHandJoints[i].position.y = jointLocations[i].pose.position.y;
RightHandJoints[i].position.z = -jointLocations[i].pose.position.z;
RightHandJoints[i].rotation.x = jointLocations[i].pose.orientation.x;
RightHandJoints[i].rotation.y = jointLocations[i].pose.orientation.y;
RightHandJoints[i].rotation.z = -jointLocations[i].pose.orientation.z;
RightHandJoints[i].rotation.w = -jointLocations[i].pose.orientation.w;
}
//if ((jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0)
//Debug.Log("CustomGestureManager GetHandJointLocations() ORIENTATION_VALID_BIT not 0");
//if ((jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0)
//Debug.Log("CustomGestureManager GetHandJointLocations() LOCATION_POSITION_VALID_BIT not 0");
//if ((jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_TRACKED_BIT) != 0)
//Debug.Log("CustomGestureManager GetHandJointLocations() POSITION_TRACKED_BIT not 0");
//if ((jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) != 0)
//Debug.Log("CustomGestureManager GetHandJointLocations() ORIENTATION_TRACKED_BIT not 0");
if ((jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_TRACKED_BIT) != 0 && (jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) != 0
&& (jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0 && (jointLocations[i].locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0)
{
(hand == CGEnums.HandFlag.Left ? LeftHandJoints[i] : RightHandJoints[i]).isValid = true;
//if (hand == CGEnums.HandFlag.Left) Debug.Log("CustomGestureManager GetHandJointLocations() set isValid to true(If_BIT)! Left");
//else Debug.Log("CustomGestureManager GetHandJointLocations() set isValid to true(If_BIT)! Right");
}
else
{
(hand == CGEnums.HandFlag.Left ? LeftHandJoints[i] : RightHandJoints[i]).isValid = false;
//if (hand == CGEnums.HandFlag.Left) Debug.Log("CustomGestureManager GetHandJointLocations() set isValid to false(If)! Left");
//else Debug.Log("CustomGestureManager GetHandJointLocations() set isValid to true(If)! Right");
}
}
}
else
{
if (hand == CGEnums.HandFlag.Left)
{
for (int i = 0; i < (int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT; i++)
{
LeftHandJoints[i].isValid = false;
}
//Debug.Log("CustomGestureManager GetHandJointLocations() set isValid to false(else) Left!");
}
else
{
for (int i = 0; i < (int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT; i++)
{
RightHandJoints[i].isValid = false;
}
//Debug.Log("CustomGestureManager GetHandJointLocations() set isValid to false(else) Right!");
}
}
}
public CGEnums.FingerStatus GetFingerStatus(CGEnums.HandFlag _Hand, CGEnums.FingerFlag _Finger)
{
switch (_Finger)
{
case CGEnums.FingerFlag.Thumb:
if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT) == 0.0f &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_THUMB_PROXIMAL_EXT) == 0.0f)
{
return CGEnums.FingerStatus.None;
}
else if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT) > ThumbDefiner.StraightDistalLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_THUMB_PROXIMAL_EXT) > ThumbDefiner.StraightProximalLowBound)
{
return CGEnums.FingerStatus.Straight;
}
else if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT) < ThumbDefiner.BendingDistalLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_THUMB_PROXIMAL_EXT) < ThumbDefiner.BendingProximalLowBound)
{
return CGEnums.FingerStatus.Bended;
}
return CGEnums.FingerStatus.Bending;
case CGEnums.FingerFlag.Index:
if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_DISTAL_EXT) == 0.0f &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT) == 0.0f &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_PROXIMAL_EXT) == 0.0f)
{
return CGEnums.FingerStatus.None;
}
else if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_DISTAL_EXT) > IndexDefiner.StraightDistalLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT) > IndexDefiner.StraightIntermediateLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_DISTAL_EXT) > IndexDefiner.StraightProximalLowBound)
{
return CGEnums.FingerStatus.Straight;
}
else if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_DISTAL_EXT) < IndexDefiner.BendingDistalLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT) < IndexDefiner.BendingIntermediateLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_INDEX_PROXIMAL_EXT) < IndexDefiner.BendingProximalLowBound)
{
return CGEnums.FingerStatus.Bended;
}
return CGEnums.FingerStatus.Bending;
case CGEnums.FingerFlag.Middle:
if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_MIDDLE_DISTAL_EXT) > MiddleDefiner.StraightDistalLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT) > MiddleDefiner.StraightIntermediateLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_MIDDLE_DISTAL_EXT) > MiddleDefiner.StraightProximalLowBound)
{
return CGEnums.FingerStatus.Straight;
}
else if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_MIDDLE_DISTAL_EXT) < MiddleDefiner.BendingDistalLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT) < MiddleDefiner.BendingIntermediateLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT) < MiddleDefiner.BendingProximalLowBound)
{
return CGEnums.FingerStatus.Bended;
}
return CGEnums.FingerStatus.Bending;
case CGEnums.FingerFlag.Ring:
if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_RING_DISTAL_EXT) > RingDefiner.StraightDistalLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_RING_INTERMEDIATE_EXT) > RingDefiner.StraightIntermediateLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_RING_DISTAL_EXT) > RingDefiner.StraightProximalLowBound)
{
return CGEnums.FingerStatus.Straight;
}
else if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_RING_DISTAL_EXT) < RingDefiner.BendingDistalLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_RING_INTERMEDIATE_EXT) < RingDefiner.BendingIntermediateLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_RING_PROXIMAL_EXT) < RingDefiner.BendingProximalLowBound)
{
return CGEnums.FingerStatus.Bended;
}
return CGEnums.FingerStatus.Bending;
case CGEnums.FingerFlag.Pinky:
if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_LITTLE_DISTAL_EXT) > PinkyDefiner.StraightDistalLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT) > PinkyDefiner.StraightIntermediateLowBound &&
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_LITTLE_DISTAL_EXT) > PinkyDefiner.StraightProximalLowBound)
{
return CGEnums.FingerStatus.Straight;
}
else if (GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_LITTLE_DISTAL_EXT) < PinkyDefiner.BendingDistalLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT) < PinkyDefiner.BendingIntermediateLowBound ||
GetAngleofHandNode(_Hand, XrHandJointEXT.XR_HAND_JOINT_LITTLE_PROXIMAL_EXT) < PinkyDefiner.BendingProximalLowBound)
{
return CGEnums.FingerStatus.Bended;
}
return CGEnums.FingerStatus.Bending;
}
return CGEnums.FingerStatus.None;
}
static float GetAngleofHandNode(CGEnums.HandFlag _Hand, XrHandJointEXT _HandJoint)
{
Vector3 _Bone1Dir = Vector3.zero, _Bone2Dir = Vector3.zero;
if (_HandJoint != XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_RING_DISTAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_MIDDLE_DISTAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_LITTLE_DISTAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_INDEX_DISTAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_RING_INTERMEDIATE_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_THUMB_PROXIMAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_RING_PROXIMAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_LITTLE_PROXIMAL_EXT &&
_HandJoint != XrHandJointEXT.XR_HAND_JOINT_INDEX_PROXIMAL_EXT)
{
Debug.LogError("_HandJoint has to be a joint between two bones on the fingers!");
return (float)double.NaN;
}
HandJoint[] _Joints = GetHandJointLocations(_Hand);
_Bone1Dir = _Joints[(int)(_HandJoint + 1)].position - _Joints[(int)_HandJoint].position;
_Bone2Dir = _Joints[(int)(_HandJoint - 1)].position - _Joints[(int)_HandJoint].position;
return Vector3.Angle(_Bone1Dir, _Bone2Dir);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1fc3b0937a9ed7d4a82e0a0c262beaf4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,5 @@
using System.Collections; // Copyright HTC Corporation All Rights Reserved.
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace VIVE.OpenXR namespace VIVE.OpenXR

View File

@@ -1,4 +1,5 @@
// Copyright HTC Corporation All Rights Reserved. // Copyright HTC Corporation All Rights Reserved.
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using UnityEditor; using UnityEditor;

View File

@@ -1,6 +1,10 @@
// Copyright HTC Corporation All Rights Reserved. // Copyright HTC Corporation All Rights Reserved.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.Layouts;
@@ -40,6 +44,19 @@ namespace VIVE.OpenXR
#endif #endif
public class VIVEFocus3Profile : OpenXRInteractionFeature public class VIVEFocus3Profile : OpenXRInteractionFeature
{ {
const string LOG_TAG = "VIVE.OpenXR.VIVEFocus3Profile ";
StringBuilder m_sb = null;
StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
void ERROR(StringBuilder msg) { Debug.LogError(msg); }
private static VIVEFocus3Profile m_Instance = null;
public const string kOpenxrExtensionString = "XR_HTC_vive_focus3_controller_interaction"; public const string kOpenxrExtensionString = "XR_HTC_vive_focus3_controller_interaction";
/// <summary> /// <summary>
@@ -51,8 +68,19 @@ namespace VIVE.OpenXR
/// An Input System device based on the hand interaction profile in the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_vive_focus3_controller_interaction">Interaction Profile</a>. /// An Input System device based on the hand interaction profile in the <a href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_vive_focus3_controller_interaction">Interaction Profile</a>.
/// </summary> /// </summary>
[Preserve, InputControlLayout(displayName = "VIVE Focus 3 Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })] [Preserve, InputControlLayout(displayName = "VIVE Focus 3 Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })]
public class VIVEFocus3Controller : XRControllerWithRumble public class VIVEFocus3Controller : XRControllerWithRumble, IInputUpdateCallbackReceiver
{ {
const string LOG_TAG = "VIVE.OpenXR.VIVEFocus3Profile.VIVEFocus3Controller ";
StringBuilder m_sb = null;
StringBuilder sb {
get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
}
}
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
void ERROR(StringBuilder msg) { Debug.LogError(msg); }
/// <summary> /// <summary>
/// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control) that represents the <see cref="VIVEFocus3Profile.thumbstick"/> OpenXR binding. /// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control) that represents the <see cref="VIVEFocus3Profile.thumbstick"/> OpenXR binding.
/// </summary> /// </summary>
@@ -185,6 +213,8 @@ namespace VIVE.OpenXR
[Preserve, InputControl(usage = "Haptic")] [Preserve, InputControl(usage = "Haptic")]
public HapticControl haptic { get; private set; } public HapticControl haptic { get; private set; }
private bool UpdateInputDeviceInRuntime = false;
/// <summary> /// <summary>
/// Internal call used to assign controls to the the correct element. /// Internal call used to assign controls to the the correct element.
/// </summary> /// </summary>
@@ -216,6 +246,91 @@ namespace VIVE.OpenXR
pointerPosition = GetChildControl<Vector3Control>("pointerPosition"); pointerPosition = GetChildControl<Vector3Control>("pointerPosition");
pointerRotation = GetChildControl<QuaternionControl>("pointerRotation"); pointerRotation = GetChildControl<QuaternionControl>("pointerRotation");
haptic = GetChildControl<HapticControl>("haptic"); haptic = GetChildControl<HapticControl>("haptic");
sb.Clear().Append(LOG_TAG)
.Append("FinishSetup() device interfaceName: ").Append(description.interfaceName)
.Append(", deviceClass: ").Append(description.deviceClass)
.Append(", product: ").Append(description.product)
.Append(", serial: ").Append(description.serial)
.Append(", capabilities: ").Append(description.capabilities);
DEBUG(sb);
}
private bool bRoleUpdatedLeft = false, bRoleUpdatedRight = false;
public void OnUpdate()
{
if (!UpdateInputDeviceInRuntime) { return; }
if (m_Instance == null) { return; }
string func = "OnUpdate() ";
if (leftHand.isTracked.ReadValue() > 0 && !bRoleUpdatedLeft)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("product: ").Append(description.product)
.Append(" with user path: ").Append(UserPaths.leftHand).Append(" is_tracked."); DEBUG(sb);
XrPath path = StringToPath(UserPaths.leftHand);
if (m_Instance.GetInputSourceName(path, XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT, out string role) != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("GetInputSourceName XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT failed."); ERROR(sb);
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("product: ").Append(description.product)
.Append(" with user path: ").Append(UserPaths.leftHand).Append(" has role: ").Append(role); DEBUG(sb);
}
if (m_Instance.GetInputSourceName(path, XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC, out string sn) != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("GetInputSourceName XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC failed."); ERROR(sb);
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("product: ").Append(description.product)
.Append(" with user path: ").Append(UserPaths.leftHand).Append(" has serial number: ").Append(role); DEBUG(sb);
}
bRoleUpdatedLeft = true;
}
if (rightHand.isTracked.ReadValue() > 0 && !bRoleUpdatedRight)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("product: ").Append(description.product)
.Append(" with user path: ").Append(UserPaths.rightHand).Append(" is_tracked."); DEBUG(sb);
XrPath path = StringToPath(UserPaths.rightHand);
if (m_Instance.GetInputSourceName(path, XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT, out string role) != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("GetInputSourceName XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT failed."); ERROR(sb);
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("product: ").Append(description.product)
.Append(" with user path: ").Append(UserPaths.rightHand).Append(" has role: ").Append(role); DEBUG(sb);
}
if (m_Instance.GetInputSourceName(path, XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC, out string sn) != XrResult.XR_SUCCESS)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("GetInputSourceName XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC failed."); ERROR(sb);
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("product: ").Append(description.product)
.Append(" with user path: ").Append(UserPaths.leftHand).Append(" has serial number: ").Append(role); DEBUG(sb);
}
bRoleUpdatedRight = true;
}
} }
} }
@@ -682,5 +797,210 @@ namespace VIVE.OpenXR
AddActionMap(actionMap); AddActionMap(actionMap);
} }
#region OpenXR function delegates
/// xrGetInstanceProcAddr
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
/// xrEnumeratePathsForInteractionProfileHTC
VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate xrEnumeratePathsForInteractionProfileHTC = null;
/// xrEnumerateDisplayRefreshRatesFB
OpenXRHelper.xrGetInputSourceLocalizedNameDelegate xrGetInputSourceLocalizedName = null;
private bool GetXrFunctionDelegates(XrInstance xrInstance)
{
/// xrGetInstanceProcAddr
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
{
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrGetInstanceProcAddr."); DEBUG(sb);
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
xrGetInstanceProcAddr,
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInstanceProcAddr"); ERROR(sb);
return false;
}
IntPtr funcPtr = IntPtr.Zero;
/// xrEnumeratePathsForInteractionProfileHTC
if (XrGetInstanceProcAddr(xrInstance, "xrEnumeratePathsForInteractionProfileHTC", out funcPtr) == XrResult.XR_SUCCESS)
{
if (funcPtr != IntPtr.Zero)
{
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrEnumeratePathsForInteractionProfileHTC."); DEBUG(sb);
xrEnumeratePathsForInteractionProfileHTC = Marshal.GetDelegateForFunctionPointer(
funcPtr,
typeof(VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate)) as VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate;
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC.");
ERROR(sb);
}
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC");
ERROR(sb);
}
/// xrGetInputSourceLocalizedName
if (XrGetInstanceProcAddr(xrInstance, "xrGetInputSourceLocalizedName", out funcPtr) == XrResult.XR_SUCCESS)
{
if (funcPtr != IntPtr.Zero)
{
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrGetInputSourceLocalizedName."); DEBUG(sb);
xrGetInputSourceLocalizedName = Marshal.GetDelegateForFunctionPointer(
funcPtr,
typeof(OpenXRHelper.xrGetInputSourceLocalizedNameDelegate)) as OpenXRHelper.xrGetInputSourceLocalizedNameDelegate;
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInputSourceLocalizedName.");
ERROR(sb);
}
}
else
{
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInputSourceLocalizedName");
ERROR(sb);
}
return true;
}
#endregion
private XrResult GetInputSourceName(XrPath path, XrInputSourceLocalizedNameFlags sourceType, out string sourceName)
{
string func = "GetInputSourceName() ";
sourceName = "";
if (!m_XrSessionCreated || xrGetInputSourceLocalizedName == null) { return XrResult.XR_ERROR_VALIDATION_FAILURE; }
string userPath = PathToString(path);
sb.Clear().Append(LOG_TAG).Append(func)
.Append("userPath: ").Append(userPath).Append(", flag: ").Append((UInt64)sourceType); DEBUG(sb);
XrInputSourceLocalizedNameGetInfo nameInfo = new XrInputSourceLocalizedNameGetInfo(
XrStructureType.XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO,
IntPtr.Zero, path, (XrInputSourceLocalizedNameFlags)sourceType);
UInt32 nameSizeIn = 0;
UInt32 nameSizeOut = 0;
char[] buffer = new char[0];
XrResult result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
if (result == XrResult.XR_SUCCESS)
{
if (nameSizeOut < 1)
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append("bufferCountOutput size is invalid!");
ERROR(sb);
return XrResult.XR_ERROR_VALIDATION_FAILURE;
}
nameSizeIn = nameSizeOut;
buffer = new char[nameSizeIn];
result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
if (result == XrResult.XR_SUCCESS)
{
sourceName = new string(buffer).TrimEnd('\0');
sb.Clear().Append(LOG_TAG).Append(func)
.Append("xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut)
.Append(", sourceName: ").Append(sourceName);
DEBUG(sb);
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut)
.Append(" result: ").Append(result);
ERROR(sb);
}
}
else
{
sb.Clear().Append(LOG_TAG).Append(func)
.Append("1.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
.Append(", flag: ").Append((UInt64)sourceType)
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
.Append(", bufferCountOutput: ").Append(nameSizeOut)
.Append(" result: ").Append(result);
ERROR(sb);
}
return result;
}
#region OpenXR Life Cycle
#pragma warning disable
private bool m_XrInstanceCreated = false;
#pragma warning restore
private XrInstance m_XrInstance = 0;
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
/// </summary>
/// <param name="xrInstance">The created instance.</param>
/// <returns>True for valid <see cref="XrInstance">XrInstance</see></returns>
protected override bool OnInstanceCreate(ulong xrInstance)
{
m_XrInstanceCreated = true;
m_XrInstance = xrInstance;
m_Instance = this;
sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(m_XrInstance); DEBUG(sb);
GetXrFunctionDelegates(m_XrInstance);
return true;
}
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyInstance">xrDestroyInstance</see> is done.
/// </summary>
/// <param name="xrInstance">The instance to destroy.</param>
protected override void OnInstanceDestroy(ulong xrInstance)
{
if (m_XrInstance == xrInstance)
{
m_XrInstanceCreated = false;
m_XrInstance = 0;
}
sb.Clear().Append(LOG_TAG).Append("OnInstanceDestroy() ").Append(xrInstance); DEBUG(sb);
}
private bool m_XrSessionCreated = false;
private XrSession m_XrSession = 0;
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateSession">xrCreateSession</see> is done.
/// </summary>
/// <param name="xrSession">The created session ID.</param>
protected override void OnSessionCreate(ulong xrSession)
{
m_XrSession = xrSession;
m_XrSessionCreated = true;
sb.Clear().Append(LOG_TAG).Append("OnSessionCreate() ").Append(m_XrSession); DEBUG(sb);
}
/// <summary>
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroySession">xrDestroySession</see> is done.
/// </summary>
/// <param name="xrSession">The session ID to destroy.</param>
protected override void OnSessionDestroy(ulong xrSession)
{
sb.Clear().Append(LOG_TAG).Append("OnSessionDestroy() ").Append(xrSession); DEBUG(sb);
if (m_XrSession == xrSession)
{
m_XrSession = 0;
m_XrSessionCreated = false;
}
}
#endregion
} }
} }

View File

@@ -1,16 +1,12 @@
// "VIVE SDK // Copyright HTC Corporation All Rights Reserved.
// © 2020 HTC Corporation. All Rights Reserved.
//
// Unless otherwise required by copyright law and practice,
// upon the execution of HTC SDK license agreement,
// HTC grants you access to and use of the VIVE SDK(s).
// You shall fully comply with all of HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.XR; using UnityEngine.XR;
using System.Text;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace VIVE.OpenXR namespace VIVE.OpenXR
{ {
@@ -18,10 +14,14 @@ namespace VIVE.OpenXR
public sealed class VIVERig : MonoBehaviour public sealed class VIVERig : MonoBehaviour
{ {
const string LOG_TAG = "VIVE.OpenXR.VIVERig "; const string LOG_TAG = "VIVE.OpenXR.VIVERig ";
void DEBUG(string msg) StringBuilder m_sb = null;
{ StringBuilder sb {
Debug.Log(LOG_TAG + " " + msg); get {
if (m_sb == null) { m_sb = new StringBuilder(); }
return m_sb;
} }
}
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
[SerializeField] [SerializeField]
private GameObject m_CameraOffset = null; private GameObject m_CameraOffset = null;
@@ -42,6 +42,12 @@ namespace VIVE.OpenXR
private float m_CameraYOffset = 1; private float m_CameraYOffset = 1;
public float CameraYOffset { get { return m_CameraYOffset; } set { m_CameraYOffset = value; } } public float CameraYOffset { get { return m_CameraYOffset; } set { m_CameraYOffset = value; } }
#if ENABLE_INPUT_SYSTEM
[SerializeField]
private InputActionAsset m_ActionAsset;
public InputActionAsset actionAsset { get => m_ActionAsset; set => m_ActionAsset = value; }
#endif
static List<XRInputSubsystem> s_InputSubsystems = new List<XRInputSubsystem>(); static List<XRInputSubsystem> s_InputSubsystems = new List<XRInputSubsystem>();
private void OnEnable() private void OnEnable()
{ {
@@ -50,6 +56,13 @@ namespace VIVE.OpenXR
{ {
s_InputSubsystems[i].trackingOriginUpdated += TrackingOriginUpdated; s_InputSubsystems[i].trackingOriginUpdated += TrackingOriginUpdated;
} }
#if ENABLE_INPUT_SYSTEM
if (m_ActionAsset != null)
{
m_ActionAsset.Enable();
}
#endif
} }
private void OnDisable() private void OnDisable()
{ {
@@ -64,7 +77,7 @@ namespace VIVE.OpenXR
private void TrackingOriginUpdated(XRInputSubsystem obj) private void TrackingOriginUpdated(XRInputSubsystem obj)
{ {
m_LastRecenteredTime = Time.time; m_LastRecenteredTime = Time.time;
DEBUG("TrackingOriginUpdated() m_LastRecenteredTime: " + m_LastRecenteredTime); sb.Clear().Append(LOG_TAG).Append("TrackingOriginUpdated() m_LastRecenteredTime: ").Append(m_LastRecenteredTime); DEBUG(sb);
} }
XRInputSubsystem m_InputSystem = null; XRInputSubsystem m_InputSystem = null;
@@ -84,11 +97,11 @@ namespace VIVE.OpenXR
m_InputSystem.TrySetTrackingOriginMode(m_TrackingOrigin); m_InputSystem.TrySetTrackingOriginMode(m_TrackingOrigin);
TrackingOriginModeFlags mode = m_InputSystem.GetTrackingOriginMode(); TrackingOriginModeFlags mode = m_InputSystem.GetTrackingOriginMode();
DEBUG("Awake() Tracking mode is set to " + mode); sb.Clear().Append(LOG_TAG).Append("Awake() Tracking mode is set to ").Append(mode); DEBUG(sb);
} }
else else
{ {
DEBUG("Awake() no XRInputSubsystem."); sb.Clear().Append(LOG_TAG).Append("Awake() no XRInputSubsystem."); DEBUG(sb);
} }
m_TrackingOriginEx = m_TrackingOrigin; m_TrackingOriginEx = m_TrackingOrigin;
} }
@@ -104,7 +117,7 @@ namespace VIVE.OpenXR
m_InputSystem.TrySetTrackingOriginMode(m_TrackingOrigin); m_InputSystem.TrySetTrackingOriginMode(m_TrackingOrigin);
mode = m_InputSystem.GetTrackingOriginMode(); mode = m_InputSystem.GetTrackingOriginMode();
DEBUG("Update() Tracking mode is set to " + mode); sb.Clear().Append(LOG_TAG).Append("Update() Tracking mode is set to " + mode);
m_TrackingOriginEx = m_TrackingOrigin; m_TrackingOriginEx = m_TrackingOrigin;
} }
} }

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d666d7e462cfc904b8684b09ffaab15f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: eff108a5209eb0a48804dfdba0dabb63
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,285 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &8419823203306955676
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8419823203306955648}
- component: {fileID: 8419823203306955651}
- component: {fileID: 8419823203306955650}
- component: {fileID: 8419823203306955677}
m_Layer: 0
m_Name: CustomGestureDefiner
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8419823203306955648
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8419823203306955676}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.63137037, y: 1.8553617, z: -2.3483706}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8419823203306955651
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8419823203306955676}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a4717f5ed1f0b0a4a8a1aa02c8d30b06, type: 3}
m_Name:
m_EditorClassIdentifier:
LFingers:
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
RFingers:
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
CurrentGestureL: {fileID: 0}
CurrentGestureR: {fileID: 0}
CurrentGestureDual: {fileID: 0}
--- !u!114 &8419823203306955650
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8419823203306955676}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1fc3b0937a9ed7d4a82e0a0c262beaf4, type: 3}
m_Name:
m_EditorClassIdentifier:
ThumbDefiner:
StraightDistalLowBound: 150
StraightIntermediateLowBound: 150
StraightProximalLowBound: 150
BendingDistalLowBound: 140
BendingIntermediateLowBound: 140
BendingProximalLowBound: 140
IndexDefiner:
StraightDistalLowBound: 160
StraightIntermediateLowBound: 160
StraightProximalLowBound: 160
BendingDistalLowBound: 120
BendingIntermediateLowBound: 120
BendingProximalLowBound: 120
MiddleDefiner:
StraightDistalLowBound: 160
StraightIntermediateLowBound: 160
StraightProximalLowBound: 160
BendingDistalLowBound: 120
BendingIntermediateLowBound: 120
BendingProximalLowBound: 120
RingDefiner:
StraightDistalLowBound: 160
StraightIntermediateLowBound: 160
StraightProximalLowBound: 160
BendingDistalLowBound: 120
BendingIntermediateLowBound: 120
BendingProximalLowBound: 120
PinkyDefiner:
StraightDistalLowBound: 160
StraightIntermediateLowBound: 160
StraightProximalLowBound: 160
BendingDistalLowBound: 120
BendingIntermediateLowBound: 120
BendingProximalLowBound: 120
--- !u!114 &8419823203306955677
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8419823203306955676}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d622bde162fe6524aa1cf99ee5bbda11, type: 3}
m_Name:
m_EditorClassIdentifier:
DefinedGestures:
- GestureName: Fist
TargetHand: 3
ThumbStatus: 0
IndexStatus: 0
MiddleStatus: 0
RingStatus: 0
PinkyStatus: 0
ThumbStatusIs: 3
IndexStatusIs: 3
MiddleStatusIs: 3
RingStatusIs: 3
PinkyStatusIs: 4
DualHandDistance: 0
DualNear: 0
DualFar: 0
ThumbIndexDistance: 0
SingleNear: 0
SingleFar: 0
- GestureName: Point
TargetHand: 3
ThumbStatus: 3
IndexStatus: 1
MiddleStatus: 3
RingStatus: 3
PinkyStatus: 0
ThumbStatusIs: 3
IndexStatusIs: 1
MiddleStatusIs: 4
RingStatusIs: 4
PinkyStatusIs: 4
DualHandDistance: 0
DualNear: 0
DualFar: 0
ThumbIndexDistance: 0
SingleNear: 0
SingleFar: 0
- GestureName: Victory
TargetHand: 3
ThumbStatus: 3
IndexStatus: 1
MiddleStatus: 1
RingStatus: 3
PinkyStatus: 0
ThumbStatusIs: 4
IndexStatusIs: 1
MiddleStatusIs: 1
RingStatusIs: 4
PinkyStatusIs: 4
DualHandDistance: 0
DualNear: 0
DualFar: 0
ThumbIndexDistance: 2
SingleNear: 0
SingleFar: 0
- GestureName: OK
TargetHand: 3
ThumbStatus: 3
IndexStatus: 1
MiddleStatus: 1
RingStatus: 1
PinkyStatus: 0
ThumbStatusIs: 6
IndexStatusIs: 4
MiddleStatusIs: 6
RingStatusIs: 6
PinkyStatusIs: 6
DualHandDistance: 0
DualNear: 0
DualFar: 0
ThumbIndexDistance: 1
SingleNear: 3
SingleFar: 5.5
- GestureName: Like
TargetHand: 3
ThumbStatus: 3
IndexStatus: 1
MiddleStatus: 1
RingStatus: 1
PinkyStatus: 0
ThumbStatusIs: 6
IndexStatusIs: 3
MiddleStatusIs: 3
RingStatusIs: 3
PinkyStatusIs: 3
DualHandDistance: 0
DualNear: 0
DualFar: 0
ThumbIndexDistance: 2
SingleNear: 3
SingleFar: 5.5
- GestureName: Five
TargetHand: 3
ThumbStatus: 1
IndexStatus: 1
MiddleStatus: 1
RingStatus: 1
PinkyStatus: 0
ThumbStatusIs: 6
IndexStatusIs: 1
MiddleStatusIs: 1
RingStatusIs: 1
PinkyStatusIs: 1
DualHandDistance: 0
DualNear: 0
DualFar: 0
ThumbIndexDistance: 0
SingleNear: 0
SingleFar: 0
- GestureName: Rock
TargetHand: 3
ThumbStatus: 1
IndexStatus: 1
MiddleStatus: 3
RingStatus: 3
PinkyStatus: 0
ThumbStatusIs: 1
IndexStatusIs: 1
MiddleStatusIs: 3
RingStatusIs: 3
PinkyStatusIs: 1
DualHandDistance: 0
DualNear: 0
DualFar: 0
ThumbIndexDistance: 0
SingleNear: 0
SingleFar: 0
- GestureName: Vive
TargetHand: 4
ThumbStatus: 1
IndexStatus: 1
MiddleStatus: 3
RingStatus: 3
PinkyStatus: 0
ThumbStatusIs: 6
IndexStatusIs: 6
MiddleStatusIs: 0
RingStatusIs: 0
PinkyStatusIs: 0
DualHandDistance: 1
DualNear: 10
DualFar: 20
ThumbIndexDistance: 2
SingleNear: 5
SingleFar: 10
- GestureName: Push
TargetHand: 4
ThumbStatus: 1
IndexStatus: 1
MiddleStatus: 3
RingStatus: 3
PinkyStatus: 0
ThumbStatusIs: 1
IndexStatusIs: 1
MiddleStatusIs: 1
RingStatusIs: 1
PinkyStatusIs: 1
DualHandDistance: 2
DualNear: 10
DualFar: 20
ThumbIndexDistance: 0
SingleNear: 5
SingleFar: 10

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d40280e42e98ad344b4cc9286448e6ff
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 760bbc4da8f43814c8291367d09bc819
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,162 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using VIVE.OpenXR.Hand;
using VIVE.OpenXR.Toolkits.CustomGesture;
public class DebugHandGesture : MonoBehaviour
{
public Text[] LFingers;
public Text[] RFingers;
public Text CurrentGestureL;
public Text CurrentGestureR;
public Text CurrentGestureDual;
CustomGestureManager HGM;
CustomGestureDefiner GD;
void Start()
{
GD = GetComponent<CustomGestureDefiner>();
HGM = GetComponent<CustomGestureManager>();
}
void Update()
{
UpdateFingerStatus(CGEnums.HandFlag.Left); //get real left hand finger status
UpdateFingerStatus(CGEnums.HandFlag.Right); //get real right hand finger status
ShowCurrentGesture();
}
void ShowCurrentGesture()
{
CurrentGestureL.text = "LGesture: " + "No Gesture";
CurrentGestureR.text = "RGesture: " + "No Gesture";
CurrentGestureDual.text = "DualGesture: " + "No Gesture";
if (!IsGestureReady())
return;
foreach (CustomGesture _Gestures in GD.DefinedGestures)
{
switch (_Gestures.TargetHand)
{
case CGEnums.HandFlag.Either:
//check left hand gesture
if (CustomGestureDefiner.IsCurrentGestureTriiggered(_Gestures.GestureName, CGEnums.HandFlag.Left) && CheckHandValid(CGEnums.HandFlag.Left))
CurrentGestureL.text = "LGesture: " + _Gestures.GestureName;
//check right hand gesture
if (CustomGestureDefiner.IsCurrentGestureTriiggered(_Gestures.GestureName, CGEnums.HandFlag.Right) && CheckHandValid(CGEnums.HandFlag.Right))
CurrentGestureR.text = "RGesture: " + _Gestures.GestureName;
//Debug.Log("DebugHandGesture ShowCurrentGesture() " + _Gestures.GestureName);
break;
case CGEnums.HandFlag.Dual:
if (CustomGestureDefiner.IsCurrentGestureTriiggered(_Gestures.GestureName, CGEnums.HandFlag.Dual) && CheckHandValid(CGEnums.HandFlag.Left) && CheckHandValid(CGEnums.HandFlag.Right))
{
CurrentGestureDual.text = "DualGesture: " + _Gestures.GestureName;
}
break;
default:
CurrentGestureL.text = "LGesture: " + "No Gesture";
CurrentGestureR.text = "RGesture: " + "No Gesture";
CurrentGestureDual.text = "DualGesture: " + "No Gesture";
break;
}
}
}
void UpdateFingerStatus(CGEnums.HandFlag _Hand)
{
Text[] _Fingers = (_Hand == CGEnums.HandFlag.Left) ? LFingers : RFingers;
_Fingers[0].text = HGM.GetFingerStatus(_Hand, CGEnums.FingerFlag.Thumb).ToString();
_Fingers[1].text = HGM.GetFingerStatus(_Hand, CGEnums.FingerFlag.Index).ToString();
_Fingers[2].text = HGM.GetFingerStatus(_Hand, CGEnums.FingerFlag.Middle).ToString();
_Fingers[3].text = HGM.GetFingerStatus(_Hand, CGEnums.FingerFlag.Ring).ToString();
_Fingers[4].text = HGM.GetFingerStatus(_Hand, CGEnums.FingerFlag.Pinky).ToString();
}
bool CheckHandValid(CGEnums.HandFlag _Hand) {
HandJoint[] _Joints = CustomGestureManager.GetHandJointLocations(_Hand);
//Debug.Log("CheckHandValid() 0:" + _Joints[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].isValid + ", 1:" +
//_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_WRIST_EXT].isValid + ", 2:" +
//_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].isValid + ", 3:" +
//_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].isValid + ", 4:" +
//_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_MIDDLE_TIP_EXT].isValid);
if (!(_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_WRIST_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_TIP_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_PROXIMAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_METACARPAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_PROXIMAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_DISTAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_INDEX_TIP_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_MIDDLE_METACARPAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_MIDDLE_DISTAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_MIDDLE_TIP_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_RING_METACARPAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_RING_PROXIMAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_RING_INTERMEDIATE_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_RING_DISTAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_RING_TIP_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_LITTLE_METACARPAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_LITTLE_PROXIMAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_LITTLE_DISTAL_EXT].isValid &&
_Joints[(int)XrHandJointEXT.XR_HAND_JOINT_LITTLE_TIP_EXT].isValid))
{
//Debug.Log("DebugHandGesture CheckHandValid() not valid hand: "+ _Hand);
return false;
}
//Debug.Log("DebugHandGesture CheckHandValid() valid hand: " + _Hand);
return true;
}
bool IsGestureReady() {
HandJoint[] _JointsL = CustomGestureManager.GetHandJointLocations(CGEnums.HandFlag.Left);
HandJoint[] _JointsR = CustomGestureManager.GetHandJointLocations(CGEnums.HandFlag.Right);
//Debug.Log("IsGestureReady left before: " + HandTracking.GetHandJointLocations(HandFlag.Left)[0].isValid + ", " +
//_JointsL[1].isValid + ", " + _JointsL[2].isValid + ", " + _JointsL[3].isValid + ", " + _JointsL[4].isValid + ", " +
//_JointsL[5].isValid + ", " + _JointsL[6].isValid + ", " + _JointsL[7].isValid + ", " + _JointsL[8].isValid + ", " +
//_JointsL[9].isValid + ", " + _JointsL[10].isValid + ", " + _JointsL[11].isValid + ", " + _JointsL[12].isValid + ", " +
//_JointsL[13].isValid + ", " + _JointsL[14].isValid + ", " + _JointsL[15].isValid + ", " + _JointsL[16].isValid + ", " +
//_JointsL[17].isValid + ", " + _JointsL[18].isValid + ", " + _JointsL[19].isValid + ", " + _JointsL[20].isValid);*/
if (_JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].position.x == 0 &&
_JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].position.y == 0 &&
_JointsL[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].position.z == 0 &&
_JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].position.x == 0 &&
_JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].position.y == 0 &&
_JointsR[(int)XrHandJointEXT.XR_HAND_JOINT_PALM_EXT].position.z == 0)
{
//Debug.Log("IsGestureReady left palm before: " + _JointsL[0].position.x + ", " + _JointsL[0].position.y + ", " + _JointsL[0].position.z);
//Debug.Log("IsGestureReady right palm before: " + _JointsR[0].position.x + ", " + _JointsR[0].position.y + ", " + _JointsR[0].position.z);
//Debug.Log("IsGestureReady left wrist before: " + _JointsL[1].position.x + ", " + _JointsL[1].position.y + ", " + _JointsL[1].position.z);
//Debug.Log("IsGestureReady right wrist before: " + _JointsR[1].position.x + ", " + _JointsR[1].position.y + ", " + _JointsR[1].position.z);
//Debug.Log("DebugHandGesture IsGestureReady() not ready");
return false;
}
//Vector3 _LBone1Dir = Vector3.zero, _LBone2Dir = Vector3.zero, _RBone1Dir = Vector3.zero, _RBone2Dir = Vector3.zero;
//HandJoint[] _LJoints = /*HandTracking.*/CustomGestureManager.GetHandJointLocations(HandFlag.Left);
//_LBone1Dir = _LJoints[(int)(XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT + 1)].position - _LJoints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT].position;
//_LBone2Dir = _LJoints[(int)(XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT - 1)].position - _LJoints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT].position;
//Debug.Log("IsGestureReady left : " + Vector3.Angle(_LBone1Dir, _LBone2Dir));
//HandJoint[] _RJoints = /*HandTracking.*/CustomGestureManager.GetHandJointLocations(HandFlag.Right);
//_RBone1Dir = _RJoints[(int)(XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT + 1)].position - _RJoints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT].position;
//_RBone2Dir = _RJoints[(int)(XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT - 1)].position - _RJoints[(int)XrHandJointEXT.XR_HAND_JOINT_THUMB_DISTAL_EXT].position;
//Debug.Log("IsGestureReady right : " + Vector3.Angle(_RBone1Dir, _RBone2Dir));
return true;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4717f5ed1f0b0a4a8a1aa02c8d30b06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -4,6 +4,7 @@ using UnityEngine;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
using UnityEngine.UI; using UnityEngine.UI;
using UnityEngine.XR; using UnityEngine.XR;
using UnityEngine.XR.OpenXR;
namespace VIVE.OpenXR.Samples.OpenXRInput namespace VIVE.OpenXR.Samples.OpenXRInput
{ {
@@ -159,9 +160,12 @@ namespace VIVE.OpenXR.Samples.OpenXRInput
private float GetRefreshRate() private float GetRefreshRate()
{ {
if (!OpenXRRuntime.IsExtensionEnabled("XR_FB_display_refresh_rate"))
return 0.0f;
if (XR_FB_display_refresh_rate.GetDisplayRefreshRate(out float rate) == XrResult.XR_SUCCESS) { return rate; } if (XR_FB_display_refresh_rate.GetDisplayRefreshRate(out float rate) == XrResult.XR_SUCCESS) { return rate; }
return 0; return 0.0f;
} }
} }
} }

View File

@@ -0,0 +1,118 @@
// Copyright HTC Corporation All Rights Reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR;
namespace VIVE.OpenXR.Samples.OpenXRInput
{
[RequireComponent(typeof(Text))]
public class InputDevicesText : MonoBehaviour
{
public bool useXrNode = false;
public bool useRole = false;
public int StartIndex = 0, EndIndex = 0;
private Text m_Text = null;
private void Awake()
{
m_Text = GetComponent<Text>();
}
private readonly XRNode[] s_XrNodes = new XRNode[]
{
XRNode.LeftEye, // 0
XRNode.RightEye,
XRNode.CenterEye,
XRNode.Head,
XRNode.LeftHand,
XRNode.RightHand, // 5
XRNode.TrackingReference,
XRNode.HardwareTracker,
};
private readonly InputDeviceRole[] s_DeviceRoles = new InputDeviceRole[]
{
InputDeviceRole.Generic, // 0
InputDeviceRole.LeftHanded,
InputDeviceRole.RightHanded,
InputDeviceRole.GameController,
InputDeviceRole.TrackingReference,
InputDeviceRole.HardwareTracker, // 5
InputDeviceRole.LegacyController,
};
private readonly InputDeviceCharacteristics[] s_Characteristics = new InputDeviceCharacteristics[]
{
InputDeviceCharacteristics.HeadMounted, // 0
InputDeviceCharacteristics.Camera,
InputDeviceCharacteristics.HeldInHand,
InputDeviceCharacteristics.HandTracking,
InputDeviceCharacteristics.EyeTracking,
InputDeviceCharacteristics.TrackedDevice, // 5
InputDeviceCharacteristics.Controller,
InputDeviceCharacteristics.TrackingReference,
InputDeviceCharacteristics.Left,
InputDeviceCharacteristics.Right,
InputDeviceCharacteristics.Simulated6DOF,
};
internal static List<InputDevice> s_InputDevicesXrNode = new List<InputDevice>();
internal static List<InputDevice> s_InputDevices = new List<InputDevice>();
internal static List<InputDevice> s_InputDevicesRole = new List<InputDevice>();
private void Update()
{
if (m_Text == null) { return; }
if (useXrNode)
{
m_Text.text = "";
//for (int node = 0; node < s_XrNodes.Length; node++)
{
InputDevices.GetDevicesAtXRNode(s_XrNodes[7], s_InputDevicesXrNode);
m_Text.text += "Node " + s_XrNodes[7] + " (" + s_InputDevicesXrNode.Count + "):\n";
for (int i = 0; i < s_InputDevicesXrNode.Count; i++)
{
m_Text.text += "\n" + i + "." + s_InputDevicesXrNode[i].name;
m_Text.text += "\n characteristics: " + s_InputDevicesXrNode[i].characteristics;
m_Text.text += "\n serialNumber: " + s_InputDevicesXrNode[i].serialNumber;
m_Text.text += "\n";
}
m_Text.text += "\n";
}
return;
}
if (useRole)
{
m_Text.text = "";
//for (int role = 0; role < s_DeviceRoles.Length; role++)
{
#pragma warning disable
InputDevices.GetDevicesWithRole(s_DeviceRoles[5], s_InputDevicesRole);
#pragma warning enable
//InputDevices.GetDevicesWithCharacteristics(s_Characteristics[5], s_InputDevicesRole);
m_Text.text += "Role " + s_DeviceRoles[5] + " (" + s_InputDevicesRole.Count + "):\n";
for (int i = 0; i < s_InputDevicesRole.Count; i++)
{
m_Text.text += "\n" + i + "." + s_InputDevicesRole[i].name;
m_Text.text += "\n characteristics: " + s_InputDevicesRole[i].characteristics;
m_Text.text += "\n serialNumber: " + s_InputDevicesRole[i].serialNumber;
m_Text.text += "\n";
}
m_Text.text += "\n";
}
return;
}
InputDevices.GetDevices(s_InputDevices);
m_Text.text = "Input Devices (" + s_InputDevices.Count + "):\n";
for (int i = StartIndex; i < s_InputDevices.Count && i <= EndIndex; i++)
{
m_Text.text += "\n" + i + "." + s_InputDevices[i].name;
m_Text.text += "\n characteristics: " + s_InputDevices[i].characteristics;
m_Text.text += "\n serialNumber: " + s_InputDevices[i].serialNumber;
m_Text.text += "\n";
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 871111764a2c66043bf49bde40b9bb1f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -29,8 +29,8 @@ namespace VIVE.OpenXR.Samples.OpenXRInput
} }
[SerializeField] [SerializeField]
private int m_Index = 0; private string m_TrackerName = "";
public int Index { get { return m_Index; } set { m_Index = value; } } public string TrackerName { get { return m_TrackerName; } set { m_TrackerName = value; } }
[SerializeField] [SerializeField]
private InputActionReference m_IsTracked = null; private InputActionReference m_IsTracked = null;
@@ -58,7 +58,7 @@ namespace VIVE.OpenXR.Samples.OpenXRInput
{ {
if (m_Text == null) { return; } if (m_Text == null) { return; }
m_Text.text = "Tracker" + m_Index; m_Text.text = m_TrackerName;
m_Text.text += " isTracked: "; m_Text.text += " isTracked: ";
{ {

View File

@@ -34,18 +34,6 @@ namespace VIVE.OpenXR.CompositionLayer.Samples.Passthrough
private void Update() private void Update()
{ {
if (passthroughMesh != null && passthroughMeshTransform != null)
{
if (activePassthroughID == 0)
{
StartPassthrough();
}
else
{
SetPassthroughMesh();
}
}
if (VRSInputManager.instance.GetButtonDown(VRSButtonReference.B)) //Set Passthrough as Overlay if (VRSInputManager.instance.GetButtonDown(VRSButtonReference.B)) //Set Passthrough as Overlay
{ {
SetPassthroughToOverlay(); SetPassthroughToOverlay();
@@ -62,6 +50,18 @@ namespace VIVE.OpenXR.CompositionLayer.Samples.Passthrough
{ {
SetHeadLock(); SetHeadLock();
} }
if (passthroughMesh != null && passthroughMeshTransform != null)
{
if (activePassthroughID == 0)
{
StartPassthrough();
}
else
{
SetPassthroughMesh();
}
}
} }
public void SetPassthroughToOverlay() public void SetPassthroughToOverlay()

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6dd2b37427619d249ae9aeaa746c2f6f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 80e642904ff57634e9191ad924cdbc48
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ba676113e4d2dfc4095675f6fb934d9e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a343ad8228ed4924eab4fc954f5620bc
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9000000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d453ebaff8bdac64baaad756b9b24d9f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9000000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c5f5e4b11c3eb7849b74217c3351ca23
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9000000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e5bee8db40a5a941a38710195e3219e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ca57a546da07d9146aa710d82ec06e64
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a7f896cc07ac6a34a8789d0e7d4ed42f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9c4ca191297f69c46a9c041390be6f08
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ffc5d9eb6949adf4dacde705ecd5660c
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: f66ead3390398f443aa127b741826ad9, type: 3}
MigrateToVrm1: 1
RenderPipeline: 0

View File

@@ -0,0 +1,93 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Female1_Hair1
m_Shader: {fileID: 4800000, guid: e0edbf68d81d1f340ae8b110086b7063, type: 3}
m_ShaderKeywords: _ALPHATEST_ON _MTOON_EMISSIVEMAP _MTOON_RIMMAP _NORMALMAP
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 630021176738988104, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 1899353872833492360, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2016903317187815046, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MatcapTex:
m_Texture: {fileID: 3414263392403339549, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineWidthTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _RimTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ShadeTex:
m_Texture: {fileID: 2016903317187815046, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ShadingShiftTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _UvAnimMaskTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _AlphaMode: 1
- _BumpScale: 1
- _Cutoff: 0.5
- _DoubleSided: 1
- _GiEqualization: 0.9
- _M_AlphaToMask: 1
- _M_CullMode: 0
- _M_DebugMode: 0
- _M_DstBlend: 0
- _M_EditMode: 1
- _M_SrcBlend: 1
- _M_ZWrite: 1
- _OutlineLightingMix: 1
- _OutlineWidth: 0
- _OutlineWidthMode: 0
- _RenderQueueOffset: 0
- _RimFresnelPower: 100
- _RimLift: 0.1
- _RimLightingMix: 1
- _ShadingShiftFactor: -0.41333333
- _ShadingShiftTexScale: 1
- _ShadingToonyFactor: 0.85333335
- _TransparentWithZWrite: 0
- _UvAnimRotationSpeed: 0
- _UvAnimScrollXSpeed: 0
- _UvAnimScrollYSpeed: 0
m_Colors:
- _Color: {r: 0.99990994, g: 0.69411767, b: 0.95686275, a: 1}
- _EmissionColor: {r: 0.85882354, g: 0.5411765, b: 0.23137255, a: 1}
- _MatcapColor: {r: 1, g: 1, b: 1, a: 1}
- _OutlineColor: {r: 0.2745097, g: 0.090195976, b: 0.12549007, a: 1}
- _RimColor: {r: 0.24999997, g: 0.24999997, b: 0.24999997, a: 1}
- _ShadeColor: {r: 1, g: 0.6933962, b: 0.9550255, a: 1}
m_BuildTextureStacks: []

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8731be491fbbaf5468e698d6ce56d37f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Female1_Hair2
m_Shader: {fileID: 4800000, guid: e0edbf68d81d1f340ae8b110086b7063, type: 3}
m_ShaderKeywords: _ALPHATEST_ON _MTOON_EMISSIVEMAP _MTOON_RIMMAP _NORMALMAP
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 630021176738988104, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 1899353872833492360, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2016903317187815046, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MatcapTex:
m_Texture: {fileID: 3414263392403339549, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineWidthTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _RimTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ShadeTex:
m_Texture: {fileID: 2016903317187815046, guid: ffc5d9eb6949adf4dacde705ecd5660c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ShadingShiftTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _UvAnimMaskTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _AlphaMode: 1
- _BumpScale: 1
- _Cutoff: 0.5
- _DoubleSided: 1
- _GiEqualization: 0.9
- _M_AlphaToMask: 1
- _M_CullMode: 0
- _M_DebugMode: 0
- _M_DstBlend: 0
- _M_EditMode: 1
- _M_SrcBlend: 1
- _M_ZWrite: 1
- _OutlineLightingMix: 1
- _OutlineWidth: 0
- _OutlineWidthMode: 0
- _RenderQueueOffset: 0
- _RimFresnelPower: 100
- _RimLift: 0.1
- _RimLightingMix: 1
- _ShadingShiftFactor: -0.41333333
- _ShadingShiftTexScale: 1
- _ShadingToonyFactor: 0.85333335
- _TransparentWithZWrite: 0
- _UvAnimRotationSpeed: 0
- _UvAnimScrollXSpeed: 0
- _UvAnimScrollYSpeed: 0
m_Colors:
- _Color: {r: 0.39215687, g: 1, b: 0.39215687, a: 1}
- _EmissionColor: {r: 0.85882354, g: 0.5411765, b: 0.23137255, a: 1}
- _MatcapColor: {r: 1, g: 1, b: 1, a: 1}
- _OutlineColor: {r: 0.2745097, g: 0.090195976, b: 0.12549007, a: 1}
- _RimColor: {r: 0.24999997, g: 0.24999997, b: 0.24999997, a: 1}
- _ShadeColor: {r: 0.39215687, g: 1, b: 0.39215687, a: 1}
m_BuildTextureStacks: []

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2da33923e42511f4180d426efa7c2915
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More