上传YomovSDK
This commit is contained in:
8
Packages/com.htc.upm.vive.openxr/Runtime/Android.meta
Normal file
8
Packages/com.htc.upm.vive.openxr/Runtime/Android.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c51db0e4c8f60a8408a42a24c814c91f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 371f6068195afb245930c68ab49cbbac
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/com.htc.upm.vive.openxr/Runtime/Android/arm64/libopenxr_loader.so
(Stored with Git LFS)
Normal file
BIN
Packages/com.htc.upm.vive.openxr/Runtime/Android/arm64/libopenxr_loader.so
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc0da4ddf5dc37048a2731233e55e0bc
|
||||
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: ARM64
|
||||
- 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: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/com.htc.upm.vive.openxr/Runtime/Android/arm64/libviveopenxr.so
(Stored with Git LFS)
Normal file
BIN
Packages/com.htc.upm.vive.openxr/Runtime/Android/arm64/libviveopenxr.so
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67f3377461b49714d814642f3a49b69d
|
||||
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: ARM64
|
||||
- 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: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/com.htc.upm.vive.openxr/Runtime/Android/openxr_loader.aar
(Stored with Git LFS)
Normal file
BIN
Packages/com.htc.upm.vive.openxr/Runtime/Android/openxr_loader.aar
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -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:
|
||||
8
Packages/com.htc.upm.vive.openxr/Runtime/Common.meta
Normal file
8
Packages/com.htc.upm.vive.openxr/Runtime/Common.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d55a23fb1f9c7b0479d1f15716620072
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace VIVE.OpenXR.Feature
|
||||
{
|
||||
public interface IViveFeatureWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// OnInstanceCreate might be called multiple times. Because many features might be using the same instance.
|
||||
/// </summary>
|
||||
/// <param name="xrInstance"></param>
|
||||
/// <param name="xrGetInstanceProcAddr"></param>
|
||||
/// <returns></returns>
|
||||
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddr);
|
||||
|
||||
/// <summary>
|
||||
/// OnInstanceDestroy might be called multiple times. Because many features might be using the same instance.
|
||||
/// </summary>
|
||||
public void OnInstanceDestroy();
|
||||
}
|
||||
|
||||
public class ViveFeatureWrapperBase<T> where T : ViveFeatureWrapperBase<T>, new()
|
||||
{
|
||||
private static readonly Lazy<T> lazyInstance = new Lazy<T>(() => new T());
|
||||
|
||||
public static T Instance => lazyInstance.Value;
|
||||
|
||||
// Set true in yourfeature's OnInstanceCreate
|
||||
public bool IsInited { get; protected set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// If the feature is inited not successfully, Set this true. Use to avoid multiple inits.
|
||||
/// </summary>
|
||||
public bool TryInited { get; protected set; } = false;
|
||||
|
||||
public OpenXRHelper.xrGetInstanceProcAddrDelegate xrGetInstanceProcAddr;
|
||||
|
||||
/// <summary>
|
||||
/// Complete the xrGetInstanceProcAddr by set the pointer received in OnInstanceCreate
|
||||
/// </summary>
|
||||
/// <param name="intPtr"></param>
|
||||
public void SetGetInstanceProcAddrPtr(IntPtr intPtr)
|
||||
{
|
||||
if (intPtr == null || intPtr == IntPtr.Zero)
|
||||
throw new Exception("xrGetInstanceProcAddr is null");
|
||||
|
||||
xrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(intPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a27dc5505cdb29347aeda46676cedaa8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
228
Packages/com.htc.upm.vive.openxr/Runtime/Common/MemoryTools.cs
Normal file
228
Packages/com.htc.upm.vive.openxr/Runtime/Common/MemoryTools.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
internal static class MemoryTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Make sure the input ptr is a OpenXR XrBaseStructure derived struct.
|
||||
/// </summary>
|
||||
/// <param name="ptr">the struct to get its next.</param>
|
||||
/// <returns>the next's value</returns>
|
||||
public static unsafe IntPtr GetNext(IntPtr ptr)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
return IntPtr.Zero;
|
||||
//Profiler.BeginSample("GetNext");
|
||||
XrBaseStructure* ptrToStruct = (XrBaseStructure*)ptr.ToPointer();
|
||||
//Profiler.EndSample();
|
||||
return ptrToStruct->next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure the input ptr is a OpenXR XrBaseStructure derived struct.
|
||||
/// </summary>
|
||||
/// <param name="ptr">the struct to get its type</param>
|
||||
/// <returns>the struct's type</returns>
|
||||
public static unsafe XrStructureType GetType(IntPtr ptr)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
throw new Exception("The input pointer is null.");
|
||||
|
||||
//Profiler.BeginSample("GetType");
|
||||
XrBaseStructure* ptrToStruct = (XrBaseStructure*)ptr.ToPointer();
|
||||
//Profiler.EndSample();
|
||||
return ptrToStruct->type;
|
||||
}
|
||||
|
||||
public static unsafe XrBaseStructure ToBaseStructure(IntPtr ptr)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
throw new Exception("The input pointer is null.");
|
||||
|
||||
//Profiler.BeginSample("ToBaseStructure");
|
||||
XrBaseStructure* ptrToStruct = (XrBaseStructure*)ptr.ToPointer();
|
||||
//Profiler.EndSample();
|
||||
return *ptrToStruct;
|
||||
}
|
||||
|
||||
public static unsafe T PtrToStructure<T>(IntPtr ptr) where T : unmanaged
|
||||
{
|
||||
//Profiler.BeginSample("PtrToStructure");
|
||||
// Not to use Marshal.PtrToStructure<T> because it is slow.
|
||||
T t = default; // Use new T() will cause GC alloc.
|
||||
Buffer.MemoryCopy((void*)ptr, &t, sizeof(T), sizeof(T));
|
||||
//Profiler.EndSample();
|
||||
return t;
|
||||
}
|
||||
|
||||
public static unsafe void PtrToStructure<T>(IntPtr ptr, ref T t) where T : unmanaged
|
||||
{
|
||||
//Profiler.BeginSample("PtrToStructure");
|
||||
fixed (T* destinationPtr = &t)
|
||||
{
|
||||
Buffer.MemoryCopy((void*)ptr, destinationPtr, sizeof(T), sizeof(T));
|
||||
}
|
||||
//Profiler.EndSample();
|
||||
}
|
||||
|
||||
public static unsafe void StructureToPtr<T>(T t, IntPtr ptr) where T : unmanaged
|
||||
{
|
||||
//Profiler.BeginSample("StructureToPtr");
|
||||
// Not to use Marshal.StructureToPtr<T> because it is slow.
|
||||
Buffer.MemoryCopy(&t, (void*)ptr, sizeof(T), sizeof(T));
|
||||
//Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the enum array to IntPtr. Should call <see cref="ReleaseRawMemory(IntPtr)"/> after use.
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe IntPtr ToIntPtr<T>(T[] array) where T : Enum
|
||||
{
|
||||
int size = sizeof(int) * array.Length;
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
|
||||
int* intPtr = (int*)ptr.ToPointer();
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
// Convert enum to int. This has better performance than Convert.ToInt32.
|
||||
intPtr[i] = (int)(object)array[i];
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the struct to IntPtr. Should call <see cref="ReleaseRawMemory(IntPtr)"/> after use.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="structure"></param>
|
||||
/// <returns></returns>
|
||||
public static IntPtr ToIntPtr<T>(T structure) where T : struct
|
||||
{
|
||||
int size = Marshal.SizeOf(structure);
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
Marshal.StructureToPtr(structure, ptr, true);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the same size raw buffer from input array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Data type could be primitive type or struct. Should call <see cref="ReleaseRawMemory(IntPtr)"/> after use.</typeparam>
|
||||
/// <param name="refArray">The data array</param>
|
||||
/// <returns>The memory handle. Should release by <see cref="ReleaseRawMemory(IntPtr)"/></returns>
|
||||
public static unsafe IntPtr MakeRawMemory<T>(T[] refArray) where T : unmanaged
|
||||
{
|
||||
int size = Marshal.SizeOf(typeof(T)) * refArray.Length;
|
||||
return Marshal.AllocHGlobal(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the raw memory to the array. You should make sure the array has the same size as the raw memory.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Convert the memory to this type array.</typeparam>
|
||||
/// <param name="array">The output array.</param>
|
||||
/// <param name="raw">The data source in raw memory form.</param>
|
||||
/// <param name="count">Specify the copy count. Count should be less than array length.</param>
|
||||
public static unsafe void CopyFromRawMemory<T>(T[] array, IntPtr raw, int count = 0) where T : unmanaged
|
||||
{
|
||||
//Profiler.BeginSample("CopyFromRawMemory");
|
||||
int N = array.Length;
|
||||
if (count > 0 && count < array.Length)
|
||||
N = count;
|
||||
int step = sizeof(T);
|
||||
int bufferSize = step * N;
|
||||
|
||||
// Pin array's address. Prevent GC move it.
|
||||
fixed (T* destPtr = array)
|
||||
{
|
||||
T* sourcePtr = (T*)raw.ToPointer();
|
||||
Buffer.MemoryCopy(sourcePtr, destPtr, bufferSize, bufferSize);
|
||||
}
|
||||
//Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy all raw memory to the array. This has higher performance than <see cref="CopyFromRawMemory"/>.
|
||||
/// Use this method if you have frequent update requirements.
|
||||
/// You need prepare a byte buffer to store the raw memory. The byte buffer size should be tSize * array.Length.
|
||||
/// tSize is used for checking the byte buffer size. If tSize is 0, it will use Marshal.SizeOf(typeof(T)).
|
||||
/// You can save the size at your size to avoid the Marshal.Sizeof(typeof(T)) call repeatedly.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Convert the memory to this type array.</typeparam>
|
||||
/// <param name="array">The output array.</param>
|
||||
/// <param name="raw">The data source in raw memory form.</param>
|
||||
public static unsafe void CopyAllFromRawMemory<T>(T[] array, IntPtr raw) where T : unmanaged
|
||||
{
|
||||
#if DEBUG
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array), "Output array cannot be null.");
|
||||
if (raw == IntPtr.Zero)
|
||||
throw new ArgumentNullException(nameof(raw), "Raw memory pointer cannot be null.");
|
||||
#endif
|
||||
|
||||
//Profiler.BeginSample("CopyAllFromRawMemory");
|
||||
int elementSize = sizeof(T);
|
||||
int requiredBufferSize = elementSize * array.Length;
|
||||
|
||||
// Pin array's address. Prevent GC move it.
|
||||
fixed (T* destPtr = array)
|
||||
{
|
||||
T* sourcePtr = (T*)raw.ToPointer();
|
||||
Buffer.MemoryCopy(sourcePtr, destPtr, requiredBufferSize, requiredBufferSize);
|
||||
}
|
||||
//Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the same size raw buffer from input array. Make sure the raw has enough size.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Convert this type array to raw memory.</typeparam>
|
||||
/// <param name="raw">The output data in raw memory form</param>
|
||||
/// <param name="array">The data source</param>
|
||||
public static unsafe void CopyToRawMemory<T>(IntPtr raw, T[] array) where T : unmanaged
|
||||
{
|
||||
//Profiler.BeginSample("CopyToRawMemory");
|
||||
int step = sizeof(T);
|
||||
int bufferSize = step * array.Length;
|
||||
// Pin array's address. Prevent GC move it.
|
||||
fixed (T* destPtr = array)
|
||||
{
|
||||
void* ptr = raw.ToPointer();
|
||||
Buffer.MemoryCopy(destPtr, ptr, bufferSize, bufferSize);
|
||||
}
|
||||
//Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the raw memory handle which is created by <see cref="MakeRawMemory{T}(T[])"/>
|
||||
/// </summary>
|
||||
/// <param name="ptr"></param>
|
||||
public static void ReleaseRawMemory(IntPtr ptr)
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a pointer in the next chain. Make sure the input next pointer is a OpenXR XrBaseStructure derived struct.
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="next"></param>
|
||||
/// <returns>true if exist</returns>
|
||||
public static bool HasPtrInNextChain(IntPtr target, IntPtr next)
|
||||
{
|
||||
while (next != IntPtr.Zero)
|
||||
{
|
||||
if (next == target)
|
||||
return true;
|
||||
next = GetNext(next);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a887cb158a37cf45b17458a4f27d7ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,278 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
namespace VIVE.OpenXR.Enterprise
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Enterprise Command",
|
||||
Desc = "Support Enterprise request with special command",
|
||||
Company = "HTC",
|
||||
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||
Version = "0.1",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId,
|
||||
Hidden = true
|
||||
)]
|
||||
#endif
|
||||
public class ViveEnterpriseCommand : OpenXRFeature
|
||||
{
|
||||
#region Log
|
||||
const string LOG_TAG = "VIVE.OpenXR.Enterprise.Command ";
|
||||
private static void DEBUG(String msg) { Debug.Log(LOG_TAG + msg); }
|
||||
private static void ERROR(String msg) { Debug.LogError(LOG_TAG + msg); }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.enterprise.command";
|
||||
|
||||
/// <summary>
|
||||
/// The extension string.
|
||||
/// </summary>
|
||||
public const string kOpenxrExtensionString = "XR_HTC_enterprise_command";
|
||||
|
||||
#region OpenXR Life Cycle
|
||||
private static bool m_XrInstanceCreated = false;
|
||||
private static bool m_XrSessionCreated = false;
|
||||
private static XrInstance m_XrInstance = 0;
|
||||
private static XrSession m_XrSession = 0;
|
||||
private static XrSystemId m_XrSystemId = 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))
|
||||
{
|
||||
ERROR($"OnInstanceCreate() {kOpenxrExtensionString} is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_XrInstanceCreated = true;
|
||||
m_XrInstance = xrInstance;
|
||||
DEBUG($"OnInstanceCreate() {m_XrInstance}");
|
||||
|
||||
return GetXrFunctionDelegates(m_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;
|
||||
}
|
||||
DEBUG($"OnInstanceDestroy() {xrInstance}");
|
||||
}
|
||||
|
||||
/// <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;
|
||||
DEBUG($"OnSystemChange() {m_XrSystemId}");
|
||||
}
|
||||
|
||||
/// <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;
|
||||
DEBUG($"OnSessionCreate() {m_XrSession}");
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
DEBUG($"OnSessionDestroy() {xrSession}");
|
||||
|
||||
if (m_XrSession == xrSession)
|
||||
{
|
||||
m_XrSession = 0;
|
||||
m_XrSessionCreated = false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OpenXR function delegates
|
||||
/// xrEnterpriseCommandHTC
|
||||
private static ViveEnterpriseCommandHelper.xrEnterpriseCommandHTCDelegate xrEnterpriseCommandHTC;
|
||||
/// xrGetInstanceProcAddr
|
||||
private static OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||
|
||||
/// <summary>
|
||||
/// Enterprise command request for special functionality.
|
||||
/// </summary>
|
||||
/// <param name="request">The request of enterprise command</param>
|
||||
/// <param name="result">The result of enterprise command</param>
|
||||
/// <returns>Return XR_SUCCESS if request successfully. False otherwise.</returns>
|
||||
private static XrResult EnterpriseCommandHTC(XrEnterpriseCommandBufferHTC request, ref XrEnterpriseCommandBufferHTC result)
|
||||
{
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
ERROR("EnterpriseCommandHTC() XR_ERROR_SESSION_LOST.");
|
||||
return XrResult.XR_ERROR_SESSION_LOST;
|
||||
}
|
||||
if (!m_XrInstanceCreated)
|
||||
{
|
||||
ERROR("EnterpriseCommandHTC() XR_ERROR_INSTANCE_LOST.");
|
||||
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||
}
|
||||
|
||||
DEBUG($"EnterpriseCommandHTC() code: {request.code}, data: {CharArrayToString(request.data)}");
|
||||
return xrEnterpriseCommandHTC(m_XrSession, request, ref result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the OpenXR function via XrInstance.
|
||||
/// </summary>
|
||||
/// <param name="xrInstance">The XrInstance is provided by the Unity OpenXR Plugin.</param>
|
||||
/// <returns>Return true if request successfully. False otherwise.</returns>
|
||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||
{
|
||||
/// xrGetInstanceProcAddr
|
||||
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrGetInstanceProcAddr.");
|
||||
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
||||
xrGetInstanceProcAddr,
|
||||
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrGetInstanceProcAddr");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// xrEnterpriseCommandHTC
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrEnterpriseCommandHTC", out IntPtr funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrEnterpriseCommandHTC.");
|
||||
xrEnterpriseCommandHTC = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(ViveEnterpriseCommandHelper.xrEnterpriseCommandHTCDelegate)) as ViveEnterpriseCommandHelper.xrEnterpriseCommandHTCDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrEnterpriseCommandHTC");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
private const int kCharLength = 256;
|
||||
private const char kEndChar = '\0';
|
||||
private static char[] charArray = new char[kCharLength];
|
||||
|
||||
/// <summary>
|
||||
/// Request special feature with command, it should take code and command string.
|
||||
/// </summary>
|
||||
/// <param name="requestCode">The type of request code is integer.</param>
|
||||
/// <param name="requestCommand">The maximum length of request command is 256.</param>
|
||||
/// <param name="resultCode">The output of result code.</param>
|
||||
/// <param name="resultCommand">The output of result command.</param>
|
||||
/// <returns>Return true if request successfully. False otherwise.</returns>
|
||||
public static bool CommandRequest(int requestCode, string requestCommand, out int resultCode, out string resultCommand)
|
||||
{
|
||||
resultCode = 0;
|
||||
resultCommand = string.Empty;
|
||||
XrEnterpriseCommandBufferHTC request = new XrEnterpriseCommandBufferHTC(requestCode, StringToCharArray(requestCommand));
|
||||
XrEnterpriseCommandBufferHTC result = new XrEnterpriseCommandBufferHTC(resultCode, StringToCharArray(resultCommand));
|
||||
if (EnterpriseCommandHTC(request, ref result) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
resultCode = result.code;
|
||||
resultCommand = CharArrayToString(result.data);
|
||||
DEBUG($"CommandRequest Result code: {resultCode}, data: {resultCommand}");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static char[] StringToCharArray(string str)
|
||||
{
|
||||
Array.Clear(charArray, 0, kCharLength);
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
{
|
||||
int arrayLength = Math.Min(str.Length, kCharLength);
|
||||
for (int i = 0; i < arrayLength; i++)
|
||||
{
|
||||
charArray[i] = str[i];
|
||||
}
|
||||
charArray[kCharLength - 1] = kEndChar;
|
||||
}
|
||||
return charArray;
|
||||
}
|
||||
|
||||
private static string CharArrayToString(char[] charArray)
|
||||
{
|
||||
int actualLength = Array.FindIndex(charArray, c => c == kEndChar);
|
||||
if (actualLength == -1)
|
||||
{
|
||||
actualLength = charArray.Length;
|
||||
}
|
||||
|
||||
return new string(charArray, 0, actualLength);
|
||||
}
|
||||
}
|
||||
|
||||
#region Helper
|
||||
public struct XrEnterpriseCommandBufferHTC
|
||||
{
|
||||
public Int32 code;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
|
||||
public char[] data;
|
||||
|
||||
public XrEnterpriseCommandBufferHTC(int in_code, char[] in_data)
|
||||
{
|
||||
code = (Int32)in_code;
|
||||
data = new char[in_data.Length];
|
||||
Array.Copy(in_data, data, in_data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public class ViveEnterpriseCommandHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// The function delegate of xrEnterpriseCommandHTC.
|
||||
/// </summary>
|
||||
/// <param name="session">An <see cref="XrSession">XrSession</see> in which the enterprise command will be active.</param>
|
||||
/// <param name="request">The request of enterprise command</param>
|
||||
/// <param name="result">The result of enterprise command</param>
|
||||
/// <returns>Return XR_SUCCESS if request successfully. False otherwise.</returns>
|
||||
public delegate XrResult xrEnterpriseCommandHTCDelegate(
|
||||
XrSession session,
|
||||
XrEnterpriseCommandBufferHTC request,
|
||||
ref XrEnterpriseCommandBufferHTC result);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5b2125d5dc73694eaed34e97a0962e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,252 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace VIVE.OpenXR.Feature
|
||||
{
|
||||
/// <summary>
|
||||
/// To use this wrapper, you need to call CommonWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate(),
|
||||
/// and call CommonWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
|
||||
///
|
||||
/// Note:
|
||||
/// In Standardalone's OpenXR MockRuntime, the CreateSwapchain and EnumerateSwapchainImages will work and return success,
|
||||
/// but the images's native pointer will be null.
|
||||
/// </summary>
|
||||
internal class CommonWrapper : ViveFeatureWrapperBase<CommonWrapper>, IViveFeatureWrapper
|
||||
{
|
||||
const string TAG = "CommonWrapper";
|
||||
OpenXRHelper.xrGetSystemPropertiesDelegate XrGetSystemProperties;
|
||||
OpenXRHelper.xrCreateSwapchainDelegate XrCreateSwapchain;
|
||||
OpenXRHelper.xrDestroySwapchainDelegate XrDestroySwapchain;
|
||||
OpenXRHelper.xrEnumerateSwapchainFormatsDelegate XrEnumerateSwapchainFormats;
|
||||
OpenXRHelper.xrEnumerateSwapchainImagesDelegate XrEnumerateSwapchainImages;
|
||||
OpenXRHelper.xrWaitSwapchainImageDelegate XrWaitSwapchainImage;
|
||||
OpenXRHelper.xrAcquireSwapchainImageDelegate XrAcquireSwapchainImage;
|
||||
OpenXRHelper.xrReleaseSwapchainImageDelegate XrReleaseSwapchainImage;
|
||||
|
||||
/// <summary>
|
||||
/// In feature's OnInstanceCreate(), call CommonWrapper.Instance.OnInstanceCreate() for init common APIs.
|
||||
/// </summary>
|
||||
/// <param name="xrInstance">Passed in feature's OnInstanceCreate.</param>
|
||||
/// <param name="xrGetInstanceProcAddr">Pass OpenXRFeature.xrGetInstanceProcAddr in.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception">If input data not valid.</exception>
|
||||
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrPtr)
|
||||
{
|
||||
if (IsInited) return true;
|
||||
if (TryInited) return false;
|
||||
TryInited = true;
|
||||
|
||||
if (xrInstance == 0)
|
||||
throw new Exception("CommonWrapper: xrInstance is null");
|
||||
|
||||
Log.D(TAG, "OnInstanceCreate()");
|
||||
SetGetInstanceProcAddrPtr(xrGetInstanceProcAddrPtr);
|
||||
|
||||
bool ret = true;
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrGetSystemProperties", out XrGetSystemProperties);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrCreateSwapchain", out XrCreateSwapchain);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrDestroySwapchain", out XrDestroySwapchain);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrEnumerateSwapchainFormats", out XrEnumerateSwapchainFormats);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrEnumerateSwapchainImages", out XrEnumerateSwapchainImages);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrWaitSwapchainImage", out XrWaitSwapchainImage);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrAcquireSwapchainImage", out XrAcquireSwapchainImage);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrReleaseSwapchainImage", out XrReleaseSwapchainImage);
|
||||
|
||||
if (!ret)
|
||||
throw new Exception("CommonWrapper: Get function pointers failed.");
|
||||
|
||||
IsInited = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In feature's OnInstanceDestroy(), call CommonWrapper.Instance.OnInstanceDestroy() for disable common APIs.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void OnInstanceDestroy()
|
||||
{
|
||||
// Do not destroy twice
|
||||
if (IsInited == false) return;
|
||||
IsInited = false;
|
||||
XrGetSystemProperties = null;
|
||||
Log.D(TAG, "OnInstanceDestroy()");
|
||||
}
|
||||
|
||||
public XrResult GetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (IsInited == false || xrGetInstanceProcAddr == null)
|
||||
{
|
||||
function = IntPtr.Zero;
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
return xrGetInstanceProcAddr(instance, name, out function);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get system properties. Need input your features' xrInstance and xrSystemId. Fill the system properites in next for you feature.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||
/// </summary>
|
||||
/// <param name="instance"></param>
|
||||
/// <param name="systemId"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
public XrResult GetSystemProperties(XrInstance instance, XrSystemId systemId, ref XrSystemProperties properties)
|
||||
{
|
||||
if (IsInited == false || XrGetSystemProperties == null)
|
||||
{
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
return XrGetSystemProperties(instance, systemId, ref properties);
|
||||
}
|
||||
|
||||
|
||||
public XrResult GetProperties<T>(XrInstance instance, XrSystemId systemId, ref T featureProperty)
|
||||
{
|
||||
XrSystemProperties systemProperties = new XrSystemProperties();
|
||||
systemProperties.type = XrStructureType.XR_TYPE_SYSTEM_PROPERTIES;
|
||||
systemProperties.next = Marshal.AllocHGlobal(Marshal.SizeOf(featureProperty));
|
||||
|
||||
long offset = 0;
|
||||
if (IntPtr.Size == 4)
|
||||
offset = systemProperties.next.ToInt32();
|
||||
else
|
||||
offset = systemProperties.next.ToInt64();
|
||||
|
||||
IntPtr pdPropertiesPtr = new IntPtr(offset);
|
||||
Marshal.StructureToPtr(featureProperty, pdPropertiesPtr, false);
|
||||
|
||||
var ret = GetSystemProperties(instance, systemId, ref systemProperties);
|
||||
if (ret == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (IntPtr.Size == 4)
|
||||
offset = systemProperties.next.ToInt32();
|
||||
else
|
||||
offset = systemProperties.next.ToInt64();
|
||||
|
||||
pdPropertiesPtr = new IntPtr(offset);
|
||||
featureProperty = Marshal.PtrToStructure<T>(pdPropertiesPtr);
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(systemProperties.next);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public XrResult CreateSwapchain(XrSession session, ref XrSwapchainCreateInfo createInfo, out XrSwapchain swapchain)
|
||||
{
|
||||
if (IsInited == false || XrCreateSwapchain == null)
|
||||
{
|
||||
swapchain = default;
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
return XrCreateSwapchain(session, ref createInfo, out swapchain);
|
||||
}
|
||||
|
||||
public XrResult DestroySwapchain(XrSwapchain swapchain)
|
||||
{
|
||||
if (IsInited == false || XrDestroySwapchain == null)
|
||||
{
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
return XrDestroySwapchain(swapchain);
|
||||
}
|
||||
|
||||
public XrResult EnumerateSwapchainFormats(XrSession session, uint formatCapacityInput, ref uint formatCountOutput, ref long[] formats)
|
||||
{
|
||||
if (IsInited == false || XrEnumerateSwapchainFormats == null)
|
||||
{
|
||||
formatCountOutput = 0;
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
if (formatCapacityInput != 0 && (formats == null || formats.Length < formatCapacityInput))
|
||||
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
|
||||
|
||||
if (formatCapacityInput == 0)
|
||||
{
|
||||
Log.D(TAG, "EnumerateSwapchainFormats(ci=" + formatCapacityInput + ")");
|
||||
return XrEnumerateSwapchainFormats(session, 0, ref formatCountOutput, IntPtr.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.D(TAG, "EnumerateSwapchainFormats(ci=" + formatCapacityInput + ", formats=long[" + formats.Length + "])");
|
||||
IntPtr formatsPtr = MemoryTools.MakeRawMemory(formats);
|
||||
var ret = XrEnumerateSwapchainFormats(session, formatCapacityInput, ref formatCountOutput, formatsPtr);
|
||||
if (ret == XrResult.XR_SUCCESS)
|
||||
MemoryTools.CopyFromRawMemory(formats, formatsPtr, (int)formatCountOutput);
|
||||
MemoryTools.ReleaseRawMemory(formatsPtr);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public XrResult EnumerateSwapchainImages(XrSwapchain swapchain, uint imageCapacityInput, ref uint imageCountOutput, IntPtr imagesPtr)
|
||||
{
|
||||
if (IsInited == false || XrEnumerateSwapchainImages == null)
|
||||
{
|
||||
imageCountOutput = 0;
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
return XrEnumerateSwapchainImages(swapchain, imageCapacityInput, ref imageCountOutput, imagesPtr);
|
||||
}
|
||||
|
||||
[DllImport("viveopenxr", EntryPoint = "CwAcquireSwapchainImage")]
|
||||
public static extern XrResult CwAcquireSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageAcquireInfo acquireInfo, out uint index);
|
||||
|
||||
public XrResult AcquireSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageAcquireInfo acquireInfo, out uint index)
|
||||
{
|
||||
if (IsInited == false || XrAcquireSwapchainImage == null)
|
||||
{
|
||||
index = 0;
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
Profiler.BeginSample("ASW:xrAcqScImg");
|
||||
var res = XrAcquireSwapchainImage(swapchain, ref acquireInfo, out index);
|
||||
Profiler.EndSample();
|
||||
return res;
|
||||
}
|
||||
|
||||
[DllImport("viveopenxr", EntryPoint = "CwWaitSwapchainImage")]
|
||||
public static extern XrResult CwWaitSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageWaitInfo waitInfo);
|
||||
|
||||
public XrResult WaitSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageWaitInfo waitInfo)
|
||||
{
|
||||
if (IsInited == false || XrWaitSwapchainImage == null)
|
||||
{
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
Profiler.BeginSample("ASW:xrWaitScImg");
|
||||
var res = XrWaitSwapchainImage(swapchain, ref waitInfo);
|
||||
Profiler.EndSample();
|
||||
return res;
|
||||
}
|
||||
|
||||
[DllImport("viveopenxr", EntryPoint = "CwReleaseSwapchainImage")]
|
||||
public static extern XrResult CwReleaseSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageReleaseInfo releaseInfo);
|
||||
|
||||
public XrResult ReleaseSwapchainImage(XrSwapchain swapchain, ref XrSwapchainImageReleaseInfo releaseInfo)
|
||||
{
|
||||
if (IsInited == false || XrReleaseSwapchainImage == null)
|
||||
{
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
// Add Profiler
|
||||
Profiler.BeginSample("ASW:xrRelScImg");
|
||||
var res = XrReleaseSwapchainImage(swapchain, ref releaseInfo);
|
||||
Profiler.EndSample();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b940cd65f52cd5c44bd79869c5d521b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,211 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
|
||||
namespace VIVE.OpenXR.Feature
|
||||
{
|
||||
using XrFutureEXT = System.IntPtr;
|
||||
|
||||
/// <summary>
|
||||
/// To use this wrapper,
|
||||
/// 1. Add the "XR_EXT_Future" extension to the instance's enabled extensions list.
|
||||
/// 2. Call FutureWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate().
|
||||
/// 3. Call FutureWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
|
||||
///
|
||||
/// <see cref="VIVE.OpenXR.Toolkits.FutureTask.Poll"/> function helps make async Task.
|
||||
/// </summary>
|
||||
public class FutureWrapper : ViveFeatureWrapperBase<FutureWrapper>, IViveFeatureWrapper
|
||||
{
|
||||
const string TAG = "ViveFuture";
|
||||
|
||||
public enum XrFutureStateEXT
|
||||
{
|
||||
None = 0, // Not defined in extension. A default value.
|
||||
Pending = 1,
|
||||
Ready = 2,
|
||||
MAX = 0x7FFFFFFF
|
||||
}
|
||||
|
||||
public struct XrFuturePollInfoEXT {
|
||||
public XrStructureType type; // XR_TYPE_FUTURE_POLL_INFO_EXT
|
||||
public IntPtr next;
|
||||
public XrFutureEXT future;
|
||||
}
|
||||
|
||||
public struct XrFuturePollResultEXT {
|
||||
public XrStructureType type; // XR_TYPE_FUTURE_POLL_RESULT_EXT
|
||||
public IntPtr next;
|
||||
public XrFutureStateEXT state;
|
||||
}
|
||||
|
||||
public struct XrFutureCancelInfoEXT
|
||||
{
|
||||
public XrStructureType type; // XR_TYPE_FUTURE_CANCEL_INFO_EXT
|
||||
public IntPtr next;
|
||||
public XrFutureEXT future;
|
||||
}
|
||||
|
||||
public struct XrFutureCompletionBaseHeaderEXT
|
||||
{
|
||||
public XrStructureType type; // XR_TYPE_FUTURE_COMPLETION_EXT
|
||||
public IntPtr next;
|
||||
public XrResult futureResult;
|
||||
}
|
||||
|
||||
public struct XrFutureCompletionEXT
|
||||
{
|
||||
public XrStructureType type; // XR_TYPE_FUTURE_COMPLETION_EXT
|
||||
public IntPtr next;
|
||||
public XrResult futureResult;
|
||||
}
|
||||
|
||||
public delegate XrResult XrPollFutureEXTDelegate(XrInstance instance, ref XrFuturePollInfoEXT pollInfo, out XrFuturePollResultEXT pollResult);
|
||||
public delegate XrResult XrCancelFutureEXTDelegate(XrInstance instance, ref XrFutureCancelInfoEXT cancelInfo);
|
||||
|
||||
XrPollFutureEXTDelegate XrPollFutureEXT;
|
||||
XrCancelFutureEXTDelegate XrCancelFutureEXT;
|
||||
|
||||
XrInstance xrInstance;
|
||||
|
||||
/// <summary>
|
||||
/// Features should call FutureWrapper.Instance.OnInstanceCreate() in their OnInstanceCreate().
|
||||
/// </summary>
|
||||
/// <param name="xrInstance"></param>
|
||||
/// <param name="xrGetInstanceProcAddrPtr"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrPtr)
|
||||
{
|
||||
if (IsInited) return true;
|
||||
if (TryInited) return false;
|
||||
TryInited = true;
|
||||
|
||||
if (xrInstance == null)
|
||||
throw new Exception("FutureWrapper: xrInstance is null");
|
||||
this.xrInstance = xrInstance;
|
||||
|
||||
if (xrGetInstanceProcAddrPtr == null)
|
||||
throw new Exception("FutureWrapper: xrGetInstanceProcAddr is null");
|
||||
SetGetInstanceProcAddrPtr(xrGetInstanceProcAddrPtr);
|
||||
|
||||
Log.D(TAG, "OnInstanceCreate()");
|
||||
|
||||
bool hasFuture = OpenXRRuntime.IsExtensionEnabled("XR_EXT_future");
|
||||
if (!hasFuture)
|
||||
{
|
||||
Log.E(TAG, "FutureWrapper: XR_EXT_future is not enabled. Check your feature's kOpenxrExtensionString.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrPollFutureEXT", out XrPollFutureEXT);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrCancelFutureEXT", out XrCancelFutureEXT);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
Log.E(TAG,"FutureWrapper: Failed to get function pointer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
IsInited = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void OnInstanceDestroy()
|
||||
{
|
||||
Log.D(TAG, "OnInstanceDestroy()");
|
||||
IsInited = false;
|
||||
XrPollFutureEXT = null;
|
||||
XrCancelFutureEXT = null;
|
||||
xrInstance = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to get the state of a future. If Ready, Call complete functions to get the result.
|
||||
/// </summary>
|
||||
/// <param name="pollInfo"></param>
|
||||
/// <param name="pollResult"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public XrResult PollFuture(ref XrFuturePollInfoEXT pollInfo, out XrFuturePollResultEXT pollResult)
|
||||
{
|
||||
pollResult= new XrFuturePollResultEXT()
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_FUTURE_POLL_RESULT_EXT,
|
||||
next = IntPtr.Zero,
|
||||
state = XrFutureStateEXT.None
|
||||
};
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
|
||||
return XrPollFutureEXT(xrInstance, ref pollInfo, out pollResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to get the state of a future. If Ready, Call complete functions to get the result.
|
||||
/// </summary>
|
||||
/// <param name="future"></param>
|
||||
/// <param name="pollResult"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public XrResult PollFuture(XrFutureEXT future, out XrFuturePollResultEXT pollResult)
|
||||
{
|
||||
pollResult = new XrFuturePollResultEXT()
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_FUTURE_POLL_RESULT_EXT,
|
||||
next = IntPtr.Zero,
|
||||
state = XrFutureStateEXT.None
|
||||
};
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
|
||||
XrFuturePollInfoEXT pollInfo = new XrFuturePollInfoEXT()
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_FUTURE_POLL_INFO_EXT,
|
||||
next = IntPtr.Zero,
|
||||
future = future
|
||||
};
|
||||
|
||||
return XrPollFutureEXT(xrInstance, ref pollInfo, out pollResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function cancels the future and signals that the async operation is not required.
|
||||
/// After a future has been cancelled any functions using this future must return XR_ERROR_FUTURE_INVALID_EXT.
|
||||
/// </summary>
|
||||
/// <param name="cancelInfo"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public XrResult CancelFuture(ref XrFutureCancelInfoEXT cancelInfo)
|
||||
{
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
|
||||
return XrCancelFutureEXT(xrInstance, ref cancelInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="CancelFuture(ref XrFutureCancelInfoEXT)"/>
|
||||
/// </summary>
|
||||
/// <param name="future"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public XrResult CancelFuture(XrFutureEXT future)
|
||||
{
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
|
||||
XrFutureCancelInfoEXT cancelInfo = new XrFutureCancelInfoEXT()
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_FUTURE_CANCEL_INFO_EXT,
|
||||
next = IntPtr.Zero,
|
||||
future = future
|
||||
};
|
||||
|
||||
return XrCancelFutureEXT(xrInstance, ref cancelInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8522c7af0a4127409a8800e1ddd5985
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,227 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VIVE.OpenXR.Feature
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// To use this wrapper, you need to call CommonWrapper.Instance.OnInstanceCreate() in your feature's OnInstanceCreate(),
|
||||
/// and call CommonWrapper.Instance.OnInstanceDestroy() in your feature's OnInstanceDestroy().
|
||||
/// </summary>
|
||||
public class SpaceWrapper : ViveFeatureWrapperBase<SpaceWrapper>, IViveFeatureWrapper
|
||||
{
|
||||
const string TAG = "ViveSpaceWrapper";
|
||||
|
||||
public delegate XrResult DelegateXrEnumerateReferenceSpaces(XrSession session, uint spaceCapacityInput, out uint spaceCountOutput, [Out] XrReferenceSpaceType[] spaces);
|
||||
delegate XrResult DelegateXrLocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location);
|
||||
delegate XrResult DelegateXrDestroySpace(XrSpace space);
|
||||
|
||||
DelegateXrEnumerateReferenceSpaces XrEnumerateReferenceSpaces;
|
||||
OpenXRHelper.xrCreateReferenceSpaceDelegate XrCreateReferenceSpace;
|
||||
DelegateXrLocateSpace XrLocateSpace;
|
||||
DelegateXrDestroySpace XrDestroySpace;
|
||||
|
||||
/// <summary>
|
||||
/// Features should call ViveSpaceWrapper.Instance.OnInstanceCreate() in their OnInstanceCreate().
|
||||
/// </summary>
|
||||
/// <param name="xrInstance"></param>
|
||||
/// <param name="GetAddr"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public bool OnInstanceCreate(XrInstance xrInstance, IntPtr GetAddr)
|
||||
{
|
||||
if (IsInited) return true;
|
||||
if (TryInited) return false;
|
||||
TryInited = true;
|
||||
|
||||
if (xrInstance == null)
|
||||
throw new Exception("ViveSpaceWrapper: xrInstance is null");
|
||||
|
||||
SetGetInstanceProcAddrPtr(GetAddr);
|
||||
|
||||
Log.D(TAG, "OnInstanceCreate()");
|
||||
|
||||
bool ret = true;
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrEnumerateReferenceSpaces", out XrEnumerateReferenceSpaces);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrCreateReferenceSpace", out XrCreateReferenceSpace);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrLocateSpace", out XrLocateSpace);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(xrGetInstanceProcAddr, xrInstance, "xrDestroySpace", out XrDestroySpace);
|
||||
IsInited = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void OnInstanceDestroy()
|
||||
{
|
||||
// Do not destroy twice
|
||||
if (IsInited == false) return;
|
||||
IsInited = false;
|
||||
XrEnumerateReferenceSpaces = null;
|
||||
XrCreateReferenceSpace = null;
|
||||
XrLocateSpace = null;
|
||||
XrDestroySpace = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="spaceCapacityInput"></param>
|
||||
/// <param name="spaceCountOutput"></param>
|
||||
/// <param name="spaces"></param>
|
||||
/// <returns></returns>
|
||||
public XrResult EnumerateReferenceSpaces(XrSession session, int spaceCapacityInput, ref int spaceCountOutput, ref XrReferenceSpaceType[] spaces)
|
||||
{
|
||||
spaceCountOutput = 0;
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
if (spaceCapacityInput != 0 && spaces != null && spaces.Length < spaceCapacityInput)
|
||||
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
|
||||
var ret = XrEnumerateReferenceSpaces(session, (uint)spaceCapacityInput, out uint spaceCountOutputXR, spaces);
|
||||
spaceCountOutput = (int)spaceCountOutputXR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a reference space without create info.
|
||||
/// Example:
|
||||
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosef.Identity, out space);
|
||||
/// CreateReferenceSpace(session, XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE, XrPosef.Identity, out space);
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="referenceSpaceType"></param>
|
||||
/// <param name="pose"></param>
|
||||
/// <param name="space"></param>
|
||||
/// <returns></returns>
|
||||
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpaceType, XrPosef pose, out XrSpace space)
|
||||
{
|
||||
space = 0;
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
|
||||
var createInfo = new XrReferenceSpaceCreateInfo();
|
||||
createInfo.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||
createInfo.next = IntPtr.Zero;
|
||||
createInfo.referenceSpaceType = referenceSpaceType;
|
||||
createInfo.poseInReferenceSpace = pose;
|
||||
return XrCreateReferenceSpace(session, ref createInfo, out space);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a reference space.
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="createInfo"></param>
|
||||
/// <param name="space"></param>
|
||||
/// <returns></returns>
|
||||
public XrResult CreateReferenceSpace(XrSession session, XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||
{
|
||||
space = 0;
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
|
||||
return XrCreateReferenceSpace(session, ref createInfo, out space);
|
||||
}
|
||||
|
||||
public XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location)
|
||||
{
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
//Debug.Log($"LocateSpace(s={space}, bs={baseSpace}, t={time}");
|
||||
return XrLocateSpace(space, baseSpace, time, ref location);
|
||||
}
|
||||
|
||||
public XrResult DestroySpace(XrSpace space)
|
||||
{
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_HANDLE_INVALID;
|
||||
Log.D(TAG, $"DestroySpace({space})");
|
||||
return XrDestroySpace(space);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The XrSpace's Unity wrapper. Input and output are in Unity coordinate system.
|
||||
/// After use it, you should call Dispose() to release the XrSpace.
|
||||
/// </summary>
|
||||
public class Space : IDisposable
|
||||
{
|
||||
protected XrSpace space;
|
||||
private bool disposed = false;
|
||||
|
||||
public Space(XrSpace space)
|
||||
{
|
||||
Log.D($"Space({space})");
|
||||
this.space = space;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the raw XrSpace. Only use it when class Space instance is alive.
|
||||
/// You should not try to store this XrSpace, because it may be destroyed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XrSpace GetXrSpace()
|
||||
{
|
||||
return space;
|
||||
}
|
||||
|
||||
public bool GetRelatedPose(XrSpace baseSpace, XrTime time, out UnityEngine.Pose pose)
|
||||
{
|
||||
// If the xrBaseSpace is changed, the pose will be updated.
|
||||
pose = default;
|
||||
XrSpaceLocation location = new XrSpaceLocation();
|
||||
location.type = XrStructureType.XR_TYPE_SPACE_LOCATION;
|
||||
location.next = IntPtr.Zero;
|
||||
var ret = SpaceWrapper.Instance.LocateSpace(space, baseSpace, time, ref location);
|
||||
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
{
|
||||
//Debug.Log("Space: LocateSpace ret=" + ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Debug.Log("Space: baseSpace=" + baseSpace + ", space=" + space + ", time=" + time + ", ret=" + ret);
|
||||
//Debug.Log("Space: location.locationFlags=" + location.locationFlags);
|
||||
//Debug.Log("Space: location.pose.position=" + location.pose.position.x + "," + location.pose.position.y + "," + location.pose.position.z);
|
||||
//Debug.Log("Space: location.pose.orientation=" + location.pose.orientation.x + "," + location.pose.orientation.y + "," + location.pose.orientation.z + "," + location.pose.orientation.w);
|
||||
if ((location.locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_VALID_BIT) > 0 &&
|
||||
(location.locationFlags & XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) > 0)
|
||||
{
|
||||
pose = new Pose(location.pose.position.ToUnityVector(), location.pose.orientation.ToUnityQuaternion());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Managered resource
|
||||
}
|
||||
// Non managered resource
|
||||
//Debug.Log($"Space: DestroySpace({space})");
|
||||
SpaceWrapper.Instance.DestroySpace(space);
|
||||
space = 0;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~Space()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8fbeadd31afbe14996c061ac261041d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using AOT;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
internal class HookHandlerAttribute : Attribute
|
||||
{
|
||||
public string xrFuncName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this function to handle the hook process in <see cref="ViveInterceptors.XrGetInstanceProcAddrInterceptor" />
|
||||
/// </summary>
|
||||
/// <param name="xrFuncName">The hooked openxr function name</param>
|
||||
public HookHandlerAttribute(string xrFuncName)
|
||||
{
|
||||
this.xrFuncName = xrFuncName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is made for all features that need to intercept OpenXR API calls.
|
||||
/// Some APIs will be called by Unity internally, and we need to intercept them in c# to get some information.
|
||||
/// Append more interceptable functions for this class by adding a new partial class.
|
||||
/// The partial class can help the delegate name be nice to read and search.
|
||||
/// Please create per function in one partial class.
|
||||
///
|
||||
/// For all features want to use this class, please call <see cref="HookGetInstanceProcAddr" /> in your feature class.
|
||||
/// For example:
|
||||
/// protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
/// {
|
||||
/// return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||
/// }
|
||||
/// </summary>
|
||||
|
||||
// For extending the ViveInterceptors class, create a new partial class and implement the required functions.
|
||||
// For example:
|
||||
// public partial class ViveInterceptors
|
||||
// {
|
||||
// [HookHandler("xrYourFunction")]
|
||||
// private static XrResult OnHookXrYourFunction(XrInstance instance, string name, out IntPtr function)
|
||||
// { ... }
|
||||
// }
|
||||
partial class ViveInterceptors
|
||||
{
|
||||
public const string TAG = "VIVE.OpenXR.ViveInterceptors";
|
||||
static StringBuilder m_sb = null;
|
||||
static StringBuilder sb {
|
||||
get {
|
||||
if (m_sb == null) { m_sb = new StringBuilder(); }
|
||||
return m_sb;
|
||||
}
|
||||
}
|
||||
|
||||
public static ViveInterceptors instance = null;
|
||||
public static ViveInterceptors Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new ViveInterceptors();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public ViveInterceptors()
|
||||
{
|
||||
Log.D("ViveInterceptors");
|
||||
RegisterFunctions();
|
||||
}
|
||||
|
||||
delegate XrResult HookHandler(XrInstance instance, string name, out IntPtr function);
|
||||
static readonly Dictionary<string, HookHandler> interceptors = new Dictionary<string, HookHandler>();
|
||||
|
||||
private static void RegisterFunctions()
|
||||
{
|
||||
var methods = typeof(ViveInterceptors).GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var attribute = method.GetCustomAttributes(typeof(HookHandlerAttribute), false).FirstOrDefault() as HookHandlerAttribute;
|
||||
if (attribute != null)
|
||||
{
|
||||
Log.I(TAG, $"Registering hook handler {attribute.xrFuncName}");
|
||||
interceptors.Add(attribute.xrFuncName, (HookHandler)method.CreateDelegate(typeof(HookHandler)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly OpenXRHelper.xrGetInstanceProcAddrDelegate hookXrGetInstanceProcAddrHandle = new OpenXRHelper.xrGetInstanceProcAddrDelegate(XrGetInstanceProcAddrInterceptor);
|
||||
private static readonly IntPtr hookGetInstanceProcAddrHandlePtr = Marshal.GetFunctionPointerForDelegate(hookXrGetInstanceProcAddrHandle);
|
||||
static OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddrOriginal = null;
|
||||
|
||||
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
|
||||
private static XrResult XrGetInstanceProcAddrInterceptor(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
// Used to check if the original function is already hooked.
|
||||
if (instance == 0 && name == "ViveInterceptorHooked")
|
||||
{
|
||||
function = IntPtr.Zero;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
|
||||
// Check if the function is intercepted by other features
|
||||
if (interceptors.ContainsKey(name))
|
||||
{
|
||||
// If no request for this function, call the original function directly.
|
||||
if (!requiredFunctions.Contains(name))
|
||||
return XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
|
||||
var ret = interceptors[name](instance, name, out function);
|
||||
if (ret == XrResult.XR_SUCCESS)
|
||||
Log.I(TAG, name + " is intercepted");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
}
|
||||
|
||||
public IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
Log.D(TAG, "HookGetInstanceProcAddr");
|
||||
if (XrGetInstanceProcAddrOriginal == null)
|
||||
{
|
||||
Log.D(TAG, "registering our own xrGetInstanceProcAddr");
|
||||
XrGetInstanceProcAddrOriginal = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(func);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor) {
|
||||
// This is a trick to check if the original function is already hooked by this class. Sometimes, the static XrGetInstanceProcAddrOriginal didn't work as expected.
|
||||
Log.D(TAG, "Check if duplicate hooked by this script with instance=0 and \"ViveInterceptorHooked\" name. If following a loader error, ignore it.");
|
||||
// E OpenXR-Loader: Error [SPEC | xrGetInstanceProcAddr | VUID-xrGetInstanceProcAddr-instance-parameter] : XR_NULL_HANDLE for instance but query for ViveInterceptorHooked requires a valid instance
|
||||
|
||||
// Call XrGetInstanceProcAddrOriginal to check if the original function is already hooked by this class
|
||||
if (XrGetInstanceProcAddrOriginal(0, "ViveInterceptorHooked", out IntPtr function) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
// If it is called successfully, it means the original function is already hooked. So we should return the original function.
|
||||
Log.D(TAG, "Already hooked");
|
||||
return func;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return hookGetInstanceProcAddrHandlePtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dont return hookGetInstanceProcAddrHandlePtr again.
|
||||
// If this hook function is called by multiple features, it should only work at the first time.
|
||||
// If called by other features, it should return the original function.
|
||||
return func;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly List<string> requiredFunctions = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Call before <see cref="HookGetInstanceProcAddr" /> to add required functions."/>
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public void AddRequiredFunction(string name)
|
||||
{
|
||||
Log.D(TAG, $"AddRequiredFunction({name})");
|
||||
if (!interceptors.ContainsKey(name))
|
||||
{
|
||||
Log.E(TAG, $"AddRequiredFunction({name}) failed. No such function.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!requiredFunctions.Contains(name))
|
||||
requiredFunctions.Add(name);
|
||||
|
||||
// If your function support unregister, you can add the reference count here.
|
||||
if (name == "xrLocateViews")
|
||||
xrLocateViewsReferenceCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If no need to use this hooked function, call this will remove your requirement.
|
||||
/// If all requirements are removed, the original function will be called directly.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public void RemoveRequiredFunction(string name)
|
||||
{
|
||||
// If your function support unregister, you can add the reference count here.
|
||||
if (requiredFunctions.Contains(name))
|
||||
{
|
||||
if (name == "xrLocateViews")
|
||||
xrLocateViewsReferenceCount = Mathf.Max(xrLocateViewsReferenceCount--, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 118a2474c266d174d834b364821865b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
#define DEBUG
|
||||
|
||||
using AOT;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.Profiling;
|
||||
using VIVE.OpenXR.FrameSynchronization;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
partial class ViveInterceptors
|
||||
{
|
||||
[HookHandler("xrBeginSession")]
|
||||
private static XrResult OnHookXrBeginSession(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (xrBeginSessionOrigin == null)
|
||||
{
|
||||
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
return ret;
|
||||
xrBeginSessionOrigin = Marshal.GetDelegateForFunctionPointer<xrBeginSessionDelegate>(function);
|
||||
}
|
||||
function = xrBeginSessionPtr;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#region xrBeginSession
|
||||
public delegate XrResult xrBeginSessionDelegate(XrSession session, ref XrSessionBeginInfo beginInfo);
|
||||
private static xrBeginSessionDelegate xrBeginSessionOrigin = null;
|
||||
|
||||
[MonoPInvokeCallback(typeof(xrBeginSessionDelegate))]
|
||||
private static XrResult xrBeginSessionInterceptor(XrSession session, ref XrSessionBeginInfo beginInfo)
|
||||
{
|
||||
Profiler.BeginSample("VI:BeginSession");
|
||||
XrResult result = XrResult.XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
|
||||
if (xrBeginSessionOrigin != null)
|
||||
{
|
||||
if (m_EnableFrameSynchronization)
|
||||
{
|
||||
frameSynchronizationSessionBeginInfo.mode = m_FrameSynchronizationMode;
|
||||
frameSynchronizationSessionBeginInfo.next = beginInfo.next;
|
||||
beginInfo.next = Marshal.AllocHGlobal(Marshal.SizeOf(frameSynchronizationSessionBeginInfo));
|
||||
|
||||
long offset = 0;
|
||||
if (IntPtr.Size == 4)
|
||||
offset = beginInfo.next.ToInt32();
|
||||
else
|
||||
offset = beginInfo.next.ToInt64();
|
||||
|
||||
IntPtr frame_synchronization_session_begin_info_ptr = new IntPtr(offset);
|
||||
Marshal.StructureToPtr(frameSynchronizationSessionBeginInfo, frame_synchronization_session_begin_info_ptr, false);
|
||||
|
||||
#if DEBUG
|
||||
if (IntPtr.Size == 4)
|
||||
offset = beginInfo.next.ToInt32();
|
||||
else
|
||||
offset = beginInfo.next.ToInt64();
|
||||
|
||||
IntPtr fs_begin_info_ptr = new IntPtr(offset);
|
||||
XrFrameSynchronizationSessionBeginInfoHTC fsBeginInfo = (XrFrameSynchronizationSessionBeginInfoHTC)Marshal.PtrToStructure(fs_begin_info_ptr, typeof(XrFrameSynchronizationSessionBeginInfoHTC));
|
||||
|
||||
sb.Clear().Append("xrBeginSessionInterceptor() beginInfo.next = (").Append(fsBeginInfo.type).Append(", ").Append(fsBeginInfo.mode).Append(")");
|
||||
Log.D(sb);
|
||||
#endif
|
||||
}
|
||||
|
||||
result = xrBeginSessionOrigin(session, ref beginInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.E("xrBeginSessionInterceptor() Not assign xrBeginSession!");
|
||||
}
|
||||
Profiler.EndSample();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly xrBeginSessionDelegate xrBeginSession = new xrBeginSessionDelegate(xrBeginSessionInterceptor);
|
||||
private static readonly IntPtr xrBeginSessionPtr = Marshal.GetFunctionPointerForDelegate(xrBeginSession);
|
||||
#endregion
|
||||
|
||||
private static XrFrameSynchronizationSessionBeginInfoHTC frameSynchronizationSessionBeginInfo = XrFrameSynchronizationSessionBeginInfoHTC.identity;
|
||||
private static bool m_EnableFrameSynchronization = false;
|
||||
private static XrFrameSynchronizationModeHTC m_FrameSynchronizationMode = XrFrameSynchronizationModeHTC.XR_FRAME_SYNCHRONIZATION_MODE_STABILIZED_HTC;
|
||||
/// <summary>
|
||||
/// Activate or deactivate the Frame Synchronization feature.
|
||||
/// </summary>
|
||||
/// <param name="active">True for activate</param>
|
||||
/// <param name="mode">The <see cref="XrFrameSynchronizationModeHTC"/> used for Frame Synchronization.</param>
|
||||
public void ActivateFrameSynchronization(bool active, XrFrameSynchronizationModeHTC mode)
|
||||
{
|
||||
m_EnableFrameSynchronization = active;
|
||||
m_FrameSynchronizationMode = mode;
|
||||
sb.Clear().Append("ActivateFrameSynchronization() ").Append(active ? "enable " : "disable ").Append(mode);
|
||||
Log.D(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c222b96d7eb4ca4bb6390e07b1967bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using AOT;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
public partial class ViveInterceptors
|
||||
{
|
||||
[HookHandler("xrLocateViews")]
|
||||
private static XrResult OnHookXrLocateViews(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (xrLocateViewsOriginal == null)
|
||||
{
|
||||
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
return ret;
|
||||
xrLocateViewsOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrLocateViews>(function);
|
||||
}
|
||||
function = xrLocateViewsInterceptorPtr;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
|
||||
public struct XrViewLocateInfo
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrViewConfigurationType viewConfigurationType;
|
||||
public XrTime displayTime;
|
||||
public XrSpace space;
|
||||
}
|
||||
|
||||
public struct XrView
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrPosef pose;
|
||||
public XrFovf fov;
|
||||
}
|
||||
|
||||
public enum XrViewStateFlags {
|
||||
ORIENTATION_VALID_BIT = 0x00000001,
|
||||
POSITION_VALID_BIT = 0x00000002,
|
||||
ORIENTATION_TRACKED_BIT = 0x00000004,
|
||||
POSITION_TRACKED_BIT = 0x00000008,
|
||||
}
|
||||
|
||||
public struct XrViewState
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrViewStateFlags viewStateFlags;
|
||||
}
|
||||
|
||||
public delegate XrResult DelegateXrLocateViews(XrSession session, IntPtr /*XrViewLocateInfo*/ viewLocateInfo, IntPtr /*XrViewState*/ viewState, uint viewCapacityInput, ref uint viewCountOutput, IntPtr /*XrView*/ views);
|
||||
|
||||
private static readonly DelegateXrLocateViews xrLocateViewsInterceptorHandle = new DelegateXrLocateViews(XrLocateViewsInterceptor);
|
||||
private static readonly IntPtr xrLocateViewsInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrLocateViewsInterceptorHandle);
|
||||
static DelegateXrLocateViews xrLocateViewsOriginal = null;
|
||||
static int xrLocateViewsReferenceCount = 0;
|
||||
|
||||
[MonoPInvokeCallback(typeof(DelegateXrLocateViews))]
|
||||
private static XrResult XrLocateViewsInterceptor(XrSession session, IntPtr viewLocateInfo, IntPtr viewState, uint viewCapacityInput, ref uint viewCountOutput, IntPtr views)
|
||||
{
|
||||
// Call the original function if the reference count is less than or equal to 0
|
||||
if (xrLocateViewsReferenceCount <= 0)
|
||||
return xrLocateViewsOriginal(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||
|
||||
Profiler.BeginSample("VI:LocateViewsA");
|
||||
XrResult result = XrResult.XR_SUCCESS;
|
||||
if (instance.BeforeOriginalLocateViews != null)
|
||||
instance.BeforeOriginalLocateViews(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||
Profiler.EndSample();
|
||||
result = xrLocateViewsOriginal(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||
Profiler.BeginSample("VI:LocateViewsB");
|
||||
instance.AfterOriginalLocateViews?.Invoke(session, viewLocateInfo, viewState, viewCapacityInput, ref viewCountOutput, views);
|
||||
Profiler.EndSample();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If you return false, the original function will not be called.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public delegate bool DelegateXrLocateViewsInterceptor(XrSession session, IntPtr viewLocateInfo, IntPtr viewState, uint viewCapacityInput, ref uint viewCountOutput, IntPtr views);
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called before the original function.
|
||||
/// </summary>
|
||||
public DelegateXrLocateViewsInterceptor BeforeOriginalLocateViews;
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called after the original function.
|
||||
/// </summary>
|
||||
public DelegateXrLocateViewsInterceptor AfterOriginalLocateViews;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5dfd24c69475c3740975bf5538de3869
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using AOT;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.Profiling;
|
||||
using VIVE.OpenXR.DisplayRefreshRate;
|
||||
using VIVE.OpenXR.Passthrough;
|
||||
using VIVE.OpenXR.UserPresence;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
partial class ViveInterceptors
|
||||
{
|
||||
[HookHandler("xrPollEvent")]
|
||||
private static XrResult OnHookXrPollEvent(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (xrPollEventOrigin == null)
|
||||
{
|
||||
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
return ret;
|
||||
xrPollEventOrigin = Marshal.GetDelegateForFunctionPointer<xrPollEventDelegate>(function);
|
||||
}
|
||||
function = xrPollEventPtr;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
|
||||
#region xrPollEvent
|
||||
public delegate XrResult xrPollEventDelegate(XrInstance instance, ref XrEventDataBuffer eventData);
|
||||
private static xrPollEventDelegate xrPollEventOrigin = null;
|
||||
|
||||
[MonoPInvokeCallback(typeof(xrPollEventDelegate))]
|
||||
private static XrResult xrPollEventInterceptor(XrInstance instance, ref XrEventDataBuffer eventData)
|
||||
{
|
||||
Profiler.BeginSample("VI:PollEvent");
|
||||
XrResult result = XrResult.XR_SUCCESS;
|
||||
|
||||
if (xrPollEventOrigin != null)
|
||||
{
|
||||
result = xrPollEventOrigin(instance, ref eventData);
|
||||
|
||||
if (result == XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append("xrPollEventInterceptor() xrPollEvent ").Append(eventData.type); Log.D("PollEvent", sb);
|
||||
switch(eventData.type)
|
||||
{
|
||||
case XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_CHANGED_HTC:
|
||||
if (XrEventDataPassthroughConfigurationImageRateChangedHTC.Get(eventData, out XrEventDataPassthroughConfigurationImageRateChangedHTC eventDataPassthroughConfigurationImageRate))
|
||||
{
|
||||
fromImageRate = eventDataPassthroughConfigurationImageRate.fromImageRate;
|
||||
toImageRate = eventDataPassthroughConfigurationImageRate.toImageRate;
|
||||
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_RATE_CHANGED_HTC")
|
||||
.Append(", fromImageRate.srcImageRate: ").Append(fromImageRate.srcImageRate)
|
||||
.Append(", fromImageRatesrc.dstImageRate: ").Append(fromImageRate.dstImageRate)
|
||||
.Append(", toImageRate.srcImageRate: ").Append(toImageRate.srcImageRate)
|
||||
.Append(", toImageRate.dstImageRate: ").Append(toImageRate.dstImageRate);
|
||||
Log.D("PollEvent", sb.ToString());
|
||||
VivePassthroughImageRateChanged.Send(fromImageRate.srcImageRate, fromImageRate.dstImageRate, toImageRate.srcImageRate, toImageRate.dstImageRate);
|
||||
}
|
||||
break;
|
||||
case XrStructureType.XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_CHANGED_HTC:
|
||||
if (XrEventDataPassthroughConfigurationImageQualityChangedHTC.Get(eventData, out XrEventDataPassthroughConfigurationImageQualityChangedHTC eventDataPassthroughConfigurationImageQuality))
|
||||
{
|
||||
fromImageQuality = eventDataPassthroughConfigurationImageQuality.fromImageQuality;
|
||||
toImageQuality = eventDataPassthroughConfigurationImageQuality.toImageQuality;
|
||||
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_PASSTHROUGH_CONFIGURATION_IMAGE_QUALITY_CHANGED_HTC")
|
||||
.Append(", fromImageQuality: ").Append(fromImageQuality.scale)
|
||||
.Append(", toImageQuality: ").Append(toImageQuality.scale);
|
||||
Log.D("PollEvent", sb);
|
||||
VivePassthroughImageQualityChanged.Send(fromImageQuality.scale, toImageQuality.scale);
|
||||
}
|
||||
break;
|
||||
case XrStructureType.XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB:
|
||||
if(XrEventDataDisplayRefreshRateChangedFB.Get(eventData, out XrEventDataDisplayRefreshRateChangedFB eventDataDisplayRefreshRate))
|
||||
{
|
||||
fromDisplayRefreshRate = eventDataDisplayRefreshRate.fromDisplayRefreshRate;
|
||||
toDisplayRefreshRate = eventDataDisplayRefreshRate.toDisplayRefreshRate;
|
||||
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB")
|
||||
.Append(", fromDisplayRefreshRate: ").Append(fromDisplayRefreshRate)
|
||||
.Append(", toDisplayRefreshRate: ").Append(toDisplayRefreshRate);
|
||||
Log.D("PollEvent", sb);
|
||||
ViveDisplayRefreshRateChanged.Send(fromDisplayRefreshRate, toDisplayRefreshRate);
|
||||
}
|
||||
break;
|
||||
case XrStructureType.XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
|
||||
if (XrEventDataSessionStateChanged.Get(eventData, out XrEventDataSessionStateChanged eventDataSession))
|
||||
{
|
||||
switch(eventDataSession.state)
|
||||
{
|
||||
case XrSessionState.XR_SESSION_STATE_READY:
|
||||
isUserPresent = true;
|
||||
break;
|
||||
case XrSessionState.XR_SESSION_STATE_STOPPING:
|
||||
isUserPresent = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED")
|
||||
.Append(", session: ").Append(eventDataSession.session)
|
||||
.Append(", state: ").Append(eventDataSession.state)
|
||||
.Append(", isUserPresent: ").Append(isUserPresent);
|
||||
Log.D("PollEvent", sb);
|
||||
}
|
||||
break;
|
||||
case XrStructureType.XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT:
|
||||
if (XrEventDataUserPresenceChangedEXT.Get(eventData, out XrEventDataUserPresenceChangedEXT eventDataUserPresence))
|
||||
{
|
||||
isUserPresent = eventDataUserPresence.isUserPresent;
|
||||
sb.Clear().Append("xrPollEventInterceptor() XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT")
|
||||
.Append(", session: ").Append(eventDataUserPresence.session)
|
||||
.Append(", isUserPresent: ").Append(isUserPresent);
|
||||
Log.D("PollEvent", sb);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//sb.Clear().Append("xrPollEventInterceptor() xrPollEvent result: ").Append(result).Append(", isUserPresent: ").Append(isUserPresent); Log.d("PollEvent", sb);
|
||||
}
|
||||
Profiler.EndSample();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly xrPollEventDelegate xrPollEvent = new xrPollEventDelegate(xrPollEventInterceptor);
|
||||
private static readonly IntPtr xrPollEventPtr = Marshal.GetFunctionPointerForDelegate(xrPollEvent);
|
||||
#endregion
|
||||
|
||||
private static bool isUserPresent = true;
|
||||
public bool IsUserPresent() { return isUserPresent; }
|
||||
|
||||
private static float fromDisplayRefreshRate, toDisplayRefreshRate;
|
||||
public float FromDisplayRefreshRate() { return fromDisplayRefreshRate; }
|
||||
public float ToDisplayRefreshRate() { return toDisplayRefreshRate; }
|
||||
|
||||
private static XrPassthroughConfigurationImageRateHTC fromImageRate, toImageRate;
|
||||
private static XrPassthroughConfigurationImageQualityHTC fromImageQuality, toImageQuality;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2cc5716d3f563f49a47da6c1bd8ccbe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using AOT;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
partial class ViveInterceptors
|
||||
{
|
||||
[HookHandler("xrEndFrame")]
|
||||
private static XrResult OnHookXrEndFrame(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (XrEndFrameOriginal == null)
|
||||
{
|
||||
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
return ret;
|
||||
XrEndFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrEndFrame>(function);
|
||||
}
|
||||
function = xrEndFrameInterceptorPtr;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
|
||||
public struct XrCompositionLayerBaseHeader
|
||||
{
|
||||
public XrStructureType type; // This base structure itself has no associated XrStructureType value.
|
||||
public System.IntPtr next;
|
||||
public XrCompositionLayerFlags layerFlags;
|
||||
public XrSpace space;
|
||||
}
|
||||
|
||||
public struct XrFrameEndInfo
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrTime displayTime;
|
||||
public XrEnvironmentBlendMode environmentBlendMode;
|
||||
public uint layerCount;
|
||||
public IntPtr layers; // XrCompositionLayerBaseHeader IntPtr array
|
||||
}
|
||||
|
||||
public delegate XrResult DelegateXrEndFrame(XrSession session, ref XrFrameEndInfo frameEndInfo);
|
||||
private static readonly DelegateXrEndFrame xrEndFrameInterceptorHandle = new DelegateXrEndFrame(XrEndFrameInterceptor);
|
||||
private static readonly IntPtr xrEndFrameInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrEndFrameInterceptorHandle);
|
||||
static DelegateXrEndFrame XrEndFrameOriginal = null;
|
||||
|
||||
[MonoPInvokeCallback(typeof(DelegateXrEndFrame))]
|
||||
private static XrResult XrEndFrameInterceptor(XrSession session, ref XrFrameEndInfo frameEndInfo)
|
||||
{
|
||||
// instance must not null
|
||||
//if (instance == null)
|
||||
// return XrEndFrameOriginal(session, ref frameEndInfo);
|
||||
Profiler.BeginSample("VI:EndFrameB");
|
||||
XrResult result = XrResult.XR_SUCCESS;
|
||||
bool ret = true;
|
||||
if (instance.BeforeOriginalEndFrame != null)
|
||||
ret = instance.BeforeOriginalEndFrame(session, ref frameEndInfo, ref result);
|
||||
Profiler.EndSample();
|
||||
if (!ret)
|
||||
return result;
|
||||
result = XrEndFrameOriginal(session, ref frameEndInfo);
|
||||
Profiler.BeginSample("VI:EndFrameA");
|
||||
instance.AfterOriginalEndFrame?.Invoke(session, ref frameEndInfo, ref result);
|
||||
Profiler.EndSample();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If you return false, the original function will not be called.
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="frameEndInfo"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
public delegate bool DelegateXrEndFrameInterceptor(XrSession session, ref XrFrameEndInfo frameEndInfo, ref XrResult result);
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called before the original function.
|
||||
/// </summary>
|
||||
public DelegateXrEndFrameInterceptor BeforeOriginalEndFrame;
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called after the original function.
|
||||
/// </summary>
|
||||
public DelegateXrEndFrameInterceptor AfterOriginalEndFrame;
|
||||
|
||||
#if PERFORMANCE_TEST
|
||||
public delegate XrResult DelegateXrLocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location);
|
||||
private static readonly DelegateXrLocateSpace xrLocateSpaceInterceptorHandle = new DelegateXrLocateSpace(XrLocateSpaceInterceptor);
|
||||
private static readonly IntPtr xrLocateSpaceInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrLocateSpaceInterceptorHandle);
|
||||
static DelegateXrLocateSpace XrLocateSpaceOriginal = null;
|
||||
|
||||
[MonoPInvokeCallback(typeof(DelegateXrLocateSpace))]
|
||||
public static XrResult XrLocateSpaceInterceptor(XrSpace space, XrSpace baseSpace, XrTime time, ref XrSpaceLocation location)
|
||||
{
|
||||
Profiler.BeginSample("VI:LocateSpace");
|
||||
var ret = XrLocateSpaceOriginal(space, baseSpace, time, ref location);
|
||||
Profiler.EndSample();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bf7cf55d82ac6343b4eda92d1197a66
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using AOT;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
public partial class ViveInterceptors
|
||||
{
|
||||
[HookHandler("xrGetVisibilityMaskKHR")]
|
||||
private static XrResult OnHookXrGetVisibilityMaskKHR(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (xrGetVisibilityMaskKHROriginal == null)
|
||||
{
|
||||
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
return ret;
|
||||
xrGetVisibilityMaskKHROriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrGetVisibilityMaskKHR>(function);
|
||||
}
|
||||
function = xrGetVisibilityMaskKHRInterceptorPtr;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
|
||||
public enum XrVisibilityMaskTypeKHR
|
||||
{
|
||||
HIDDEN_TRIANGLE_MESH_KHR = 1,
|
||||
VISIBLE_TRIANGLE_MESH_KHR = 2,
|
||||
LINE_LOOP_KHR = 3,
|
||||
}
|
||||
|
||||
public struct XrVisibilityMaskKHR
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public uint vertexCapacityInput;
|
||||
public uint vertexCountOutput;
|
||||
public IntPtr vertices; // XrVector2f array
|
||||
public uint indexCapacityInput;
|
||||
public uint indexCountOutput;
|
||||
public IntPtr indices; // uint array
|
||||
}
|
||||
|
||||
// XrCompositionLayerSpaceWarpInfoFlagsFB bits
|
||||
public delegate XrResult DelegateXrGetVisibilityMaskKHR(XrSession session, XrViewConfigurationType viewConfigurationType, uint viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, ref XrVisibilityMaskKHR visibilityMask);
|
||||
|
||||
private static readonly DelegateXrGetVisibilityMaskKHR xrGetVisibilityMaskKHRInterceptorHandle = new DelegateXrGetVisibilityMaskKHR(XrGetVisibilityMaskKHRInterceptor);
|
||||
private static readonly IntPtr xrGetVisibilityMaskKHRInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrGetVisibilityMaskKHRInterceptorHandle);
|
||||
static DelegateXrGetVisibilityMaskKHR xrGetVisibilityMaskKHROriginal = null;
|
||||
|
||||
[MonoPInvokeCallback(typeof(DelegateXrGetVisibilityMaskKHR))]
|
||||
private static XrResult XrGetVisibilityMaskKHRInterceptor(XrSession session, XrViewConfigurationType viewConfigurationType, uint viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, ref XrVisibilityMaskKHR visibilityMask)
|
||||
{
|
||||
// instance must not null
|
||||
//if (instance == null)
|
||||
// return XrGetVisibilityMaskKHROriginal(session, ref frameEndInfo);
|
||||
Profiler.BeginSample("VI:GetVMB");
|
||||
XrResult result = XrResult.XR_SUCCESS;
|
||||
bool ret = true;
|
||||
if (instance.BeforeOriginalGetVisibilityMaskKHR != null)
|
||||
ret = instance.BeforeOriginalGetVisibilityMaskKHR(session, viewConfigurationType, viewIndex, visibilityMaskType, ref visibilityMask, ref result);
|
||||
Profiler.EndSample();
|
||||
if (!ret)
|
||||
return result;
|
||||
result = xrGetVisibilityMaskKHROriginal(session, viewConfigurationType, viewIndex, visibilityMaskType, ref visibilityMask);
|
||||
Profiler.BeginSample("VI:GetVMA");
|
||||
instance.AfterOriginalGetVisibilityMaskKHR?.Invoke(session, viewConfigurationType, viewIndex, visibilityMaskType, ref visibilityMask, ref result);
|
||||
Profiler.EndSample();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If you return false, the original function will not be called.
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="frameEndInfo"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
public delegate bool DelegateXrGetVisibilityMaskKHRInterceptor(XrSession session, XrViewConfigurationType viewConfigurationType, uint viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, ref XrVisibilityMaskKHR visibilityMask, ref XrResult result);
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called before the original function.
|
||||
/// </summary>
|
||||
public DelegateXrGetVisibilityMaskKHRInterceptor BeforeOriginalGetVisibilityMaskKHR;
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called after the original function.
|
||||
/// </summary>
|
||||
public DelegateXrGetVisibilityMaskKHRInterceptor AfterOriginalGetVisibilityMaskKHR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4c00b0b7df78d34d89cd728c9de0672
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using AOT;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
partial class ViveInterceptors
|
||||
{
|
||||
[HookHandler("xrWaitFrame")]
|
||||
private static XrResult OnHookXrWaitFrame(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (XrWaitFrameOriginal == null)
|
||||
{
|
||||
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
return ret;
|
||||
XrWaitFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrWaitFrame>(function);
|
||||
}
|
||||
function = xrWaitFrameInterceptorPtr;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
|
||||
public struct XrFrameWaitInfo
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
}
|
||||
|
||||
public struct XrFrameState
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrTime predictedDisplayTime;
|
||||
public XrDuration predictedDisplayPeriod;
|
||||
public XrBool32 shouldRender;
|
||||
}
|
||||
|
||||
bool isWaitFrameIntercepted = false;
|
||||
|
||||
public delegate XrResult DelegateXrWaitFrame(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState);
|
||||
private static readonly DelegateXrWaitFrame xrWaitFrameInterceptorHandle = new DelegateXrWaitFrame(XrWaitFrameInterceptor);
|
||||
private static readonly IntPtr xrWaitFrameInterceptorPtr = Marshal.GetFunctionPointerForDelegate(xrWaitFrameInterceptorHandle);
|
||||
static DelegateXrWaitFrame XrWaitFrameOriginal = null;
|
||||
|
||||
[MonoPInvokeCallback(typeof(DelegateXrWaitFrame))]
|
||||
private static XrResult XrWaitFrameInterceptor(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
||||
{
|
||||
// instance must not null
|
||||
//if (instance == null)
|
||||
// return XrWaitFrameOriginal(session, ref frameWaitInfo, ref frameState);
|
||||
Profiler.BeginSample("VI:WaitFrame");
|
||||
instance.isWaitFrameIntercepted = true;
|
||||
XrResult result = XrResult.XR_SUCCESS;
|
||||
if (instance.BeforeOriginalWaitFrame != null &&
|
||||
!instance.BeforeOriginalWaitFrame(session, ref frameWaitInfo, ref frameState, ref result))
|
||||
{
|
||||
Profiler.EndSample();
|
||||
return result;
|
||||
}
|
||||
var ret = XrWaitFrameOriginal(session, ref frameWaitInfo, ref frameState);
|
||||
instance.AfterOriginalWaitFrame?.Invoke(session, ref frameWaitInfo, ref frameState, ref result);
|
||||
currentFrameState = frameState;
|
||||
Profiler.EndSample();
|
||||
return result;
|
||||
}
|
||||
|
||||
static XrFrameState currentFrameState = new XrFrameState() { predictedDisplayTime = 0 };
|
||||
|
||||
/// <summary>
|
||||
/// Get the waitframe's result: XrFrameState. This result used in update is not matching the current frame. Use it after onBeforeRender.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public XrFrameState GetCurrentFrameState()
|
||||
{
|
||||
if (!isWaitFrameIntercepted) throw new Exception("ViveInterceptors is not intercepted");
|
||||
|
||||
return currentFrameState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Must request xrWaitFrame before calling this function. This result used in update is not matching the current frame. Use it after onBeforeRender.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public XrTime GetPredictTime()
|
||||
{
|
||||
if (!isWaitFrameIntercepted) throw new Exception("ViveInterceptors is not intercepted");
|
||||
|
||||
//Debug.Log($"{TAG}: XrWaitFrameInterceptor(predictedDisplayTime={currentFrameState.predictedDisplayTime}");
|
||||
if (currentFrameState.predictedDisplayTime == 0)
|
||||
return new XrTime((long)(1000000L * (Time.unscaledTimeAsDouble + 0.011f)));
|
||||
else
|
||||
return currentFrameState.predictedDisplayTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register WaitFrame event
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="frameWaitInfo"></param>
|
||||
/// <param name="frameState"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
public delegate bool DelegateXrWaitFrameInterceptor(XrSession session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState, ref XrResult result);
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called before the original function.
|
||||
/// </summary>
|
||||
public DelegateXrWaitFrameInterceptor BeforeOriginalWaitFrame;
|
||||
|
||||
/// <summary>
|
||||
/// Use this to intercept the original function. This will be called after the original function.
|
||||
/// </summary>
|
||||
public DelegateXrWaitFrameInterceptor AfterOriginalWaitFrame;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bfc07f267ee39b47a4bcbb8c1c786cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
247
Packages/com.htc.upm.vive.openxr/Runtime/Common/ViveLog.cs
Normal file
247
Packages/com.htc.upm.vive.openxr/Runtime/Common/ViveLog.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
using System.Text;
|
||||
// Non android will need UnityEngine
|
||||
using UnityEngine;
|
||||
|
||||
namespace VIVE.OpenXR
|
||||
{
|
||||
public static class Log
|
||||
{
|
||||
public const string TAG = "VIVE.OpenXR";
|
||||
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
[DllImport("liblog.so")]
|
||||
private static extern int __android_log_print(int prio, string tag, string fmt, string msg);
|
||||
#endif
|
||||
|
||||
// Use ("%s", message) instead of just (message) is because of the following reason:
|
||||
// In case message contains special characters like %, \n, \r, etc. It will be treated as format string.
|
||||
// This is a little waste of performance, but it's safer.
|
||||
|
||||
/// <summary>
|
||||
/// Not show in Standalone
|
||||
/// </summary>
|
||||
public static void D(string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(3, TAG, "%s", message); // Android Debug
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void I(string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(4, TAG, "%s", message); // Android Info
|
||||
#else
|
||||
Debug.Log(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void W(string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(5, TAG, "%s", message); // Android Warning
|
||||
#else
|
||||
Debug.LogWarning(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void E(string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(6, TAG, "%s", message); // Android Error
|
||||
#else
|
||||
Debug.LogError(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not show in Standalone
|
||||
/// </summary>
|
||||
public static void D(string tag, string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(3, tag, "%s", message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void I(string tag, string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(4, tag, "%s", message);
|
||||
#else
|
||||
Debug.LogFormat("{0}: {1}", tag, message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void W(string tag, string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(5, tag, "%s", message); // Android Warning
|
||||
#else
|
||||
Debug.LogWarningFormat("{0}: {1}", tag, message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void E(string tag, string message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(6, tag, "%s", message); // Android Error
|
||||
#else
|
||||
Debug.LogErrorFormat("{0}: {1}", tag, message);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not show in Standalone
|
||||
/// </summary>
|
||||
public static void D(StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(3, TAG, "%s", message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void I(StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(4, TAG, "%s", message.ToString());
|
||||
#else
|
||||
Debug.Log(message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void W(StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(5, TAG, "%s", message.ToString()); // Android Warning
|
||||
#else
|
||||
Debug.LogWarning(message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void E(StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(6, TAG, "%s", message.ToString()); // Android Error
|
||||
#else
|
||||
Debug.LogError(message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not show in Standalone
|
||||
/// </summary>
|
||||
public static void D(string tag, StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(3, tag, "%s", message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void I(string tag, StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(4, tag, "%s", message.ToString());
|
||||
#else
|
||||
Debug.LogFormat("{0}: {1}", tag, message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void W(string tag, StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(5, tag, "%s", message.ToString()); // Android Warning
|
||||
#else
|
||||
Debug.LogWarningFormat("{0}: {1}", tag, message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void E(string tag, StringBuilder message)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(6, tag, "%s", message.ToString()); // Android Error
|
||||
#else
|
||||
Debug.LogErrorFormat("{0}: {1}", tag, message.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not show in Standalone
|
||||
/// </summary>
|
||||
public static void DFmt(string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(3, TAG, "%s", string.Format(fmt, args));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void IFmt(string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(4, TAG, "%s", string.Format(fmt, args));
|
||||
#else
|
||||
Debug.LogFormat(fmt, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void WFmt(string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(5, TAG, "%s", string.Format(fmt, args)); // Android Warning
|
||||
#else
|
||||
Debug.LogWarningFormat(fmt, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void EFmt(string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(6, TAG, "%s", string.Format(fmt, args)); // Android Error
|
||||
#else
|
||||
Debug.LogErrorFormat(fmt, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not show in Standalone
|
||||
/// </summary>
|
||||
public static void DFmt(string tag, string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(3, tag, "%s", string.Format(fmt, args));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void IFmt(string tag, string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(4, tag, "%s", string.Format(fmt, args));
|
||||
#else
|
||||
Debug.LogFormat("{0}: {1}", tag, string.Format(fmt, args));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void WFmt(string tag, string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(5, tag, "%s", string.Format(fmt, args)); // Android Warning
|
||||
#else
|
||||
Debug.LogWarningFormat("{0}: {1}", tag, fmt, string.Format(fmt, args));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void EFmt(string tag, string fmt, params object[] args)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
__android_log_print(6, tag, "%s", string.Format(fmt, args)); // Android Error
|
||||
#else
|
||||
Debug.LogErrorFormat("{0}: {1}", tag, fmt, string.Format(fmt, args));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9750d8d4e8eb4994088534cb111510d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,338 @@
|
||||
using AOT;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
|
||||
namespace VIVE.OpenXR.Common.RenderThread
|
||||
{
|
||||
#region syncObject
|
||||
public class Message
|
||||
{
|
||||
public bool isFree = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MessagePool class manages a pool of message objects for reuse. You can enter any kind of message object.
|
||||
/// However when obtain, the message object will not able to cast to the type you want.
|
||||
/// You should only use one kind of message. Not mix different kind of message.
|
||||
/// </summary>
|
||||
public class MessagePool
|
||||
{
|
||||
// pool member is used to store message objects in a list.
|
||||
// Note that the size of this list will dynamically adjust as needed but will not automatically shrink.
|
||||
private readonly List<Message> pool = new List<Message>(2) { };
|
||||
private int index = 0;
|
||||
|
||||
public MessagePool() { }
|
||||
|
||||
// Next method calculates the next index value for cycling through message objects in the pool.
|
||||
private int Next(int value)
|
||||
{
|
||||
if (++value >= pool.Count)
|
||||
value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
// Obtain method retrieves a message object from the pool.
|
||||
// Ensure proper state setup for the message after retrieval and call Release() to the message after use.
|
||||
public T Obtain<T>() where T : Message, new()
|
||||
{
|
||||
int c = pool.Count;
|
||||
int i = index;
|
||||
for (int j = 0; j < c; i++, j++)
|
||||
{
|
||||
if (i >= c)
|
||||
i = 0;
|
||||
if (pool[i].isFree)
|
||||
{
|
||||
//Debug.LogError("Obtain idx=" + i);
|
||||
index = i;
|
||||
return (T)pool[i];
|
||||
}
|
||||
}
|
||||
index = Next(i);
|
||||
var newItem = new T()
|
||||
{
|
||||
isFree = true
|
||||
};
|
||||
pool.Insert(index, newItem);
|
||||
//Log.d("RT.MessagePool.Obtain<" + typeof(T) + ">() pool count=" + pool.Count); // Not to expose developer's type.
|
||||
Log.D("RT.MessagePool.Obtain() pool count=" + pool.Count);
|
||||
return newItem;
|
||||
}
|
||||
|
||||
// Lock method marks a message as "in use" to prevent other code from reusing it.
|
||||
// This is already called to the message obtained from the pool.
|
||||
public static void Lock(Message msg)
|
||||
{
|
||||
msg.isFree = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release method marks a message as "free" so that other code can reuse it.
|
||||
/// You can use it in RenderThread. It will not trigger the GC event.
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
public static void Release(Message msg)
|
||||
{
|
||||
msg.isFree = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PreAllocatedQueue class is a message queue based on MessagePool for preallocating message objects.
|
||||
/// Its main functionality is to add message objects to the queue and retrieve them from the queue.
|
||||
/// Messages should be enqueued in GameThread and dequeued in RenderThread.
|
||||
/// In render thread, dequeue will not trigger the GC event. Because the queue is preallocated.
|
||||
/// The 'lock' expression is not used for list's size change. Because lock should be avoid used in RenderThread.
|
||||
/// Set the queueSize as the double count of message you want to pass to render thread in one frame, and the
|
||||
/// list will never change size during runtime. Therefore we don't need to use 'lock' to protect the list.
|
||||
/// </summary>
|
||||
public class PreAllocatedQueue : MessagePool
|
||||
{
|
||||
// list member is used to store preallocated message objects in a list.
|
||||
// Note that the size of this list is set during initialization and does not dynamically adjust.
|
||||
private List<Message> list = new List<Message>();
|
||||
private int queueBegin = 0;
|
||||
private int queueEnd = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The queueSize should be the double count of message you want to pass to render thread in one frame.
|
||||
/// </summary>
|
||||
/// <param name="queueSize"></param>
|
||||
public PreAllocatedQueue(int queueSize = 2) : base()
|
||||
{
|
||||
for (int i = 0; i < queueSize; i++)
|
||||
{
|
||||
list.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
private int Next(int value)
|
||||
{
|
||||
if (++value >= list.Count)
|
||||
value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue method adds a message object to the queue.
|
||||
/// If the queue is full, the new message is added to the end of the list.
|
||||
///
|
||||
/// This function is designed to use the message object obtained from the MessagePool.
|
||||
/// Ensure only one type of message object is used in the queue.
|
||||
///
|
||||
/// Enqueue will increase the queue size if the queue is full. This may trigger GC.Alloc.
|
||||
/// This function should be used in GameThread.
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
public void Enqueue(Message msg)
|
||||
{
|
||||
Lock(msg);
|
||||
queueEnd = Next(queueEnd);
|
||||
|
||||
// If the queue is full, add the message to the end of the list. Should not let it happen.
|
||||
// Use larger queue size to avoid this issue.
|
||||
// If you see the error log here, you should increase the queue size in your design.
|
||||
if (queueEnd == queueBegin)
|
||||
{
|
||||
// Should let Insert and queueBegin be atomic. No lock protection here.
|
||||
list.Insert(queueEnd, msg);
|
||||
queueBegin++;
|
||||
Debug.LogError("RT.MessagePool.Enqueue() list count=" + list.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
list[queueEnd] = msg;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeue method retrieves a message object from the queue.
|
||||
/// This method returns the first message object in the queue and removes it from the queue.
|
||||
/// This function will not trigger the GC event. Free to use in RenderThread.
|
||||
/// After use the Message, call Release() to the message.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Message Dequeue()
|
||||
{
|
||||
// No lock protection here. If list is not change size, it is safe.
|
||||
// However if list changed size, it is safe in most case.
|
||||
queueBegin = Next(queueBegin);
|
||||
return list[queueBegin];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RenderThreadTask class is used to execute specified tasks on the rendering thread.
|
||||
/// You don't need to develop a native function to run your task on the rendering thread.
|
||||
/// And you don't need to design how to pass data to render thread.
|
||||
/// This class can be run in Unity Editor since Unity 2021. Test your code in Unity Editor can save your time.
|
||||
///
|
||||
/// You should only create RenderThreadTask as static readonly. Do not create RenderThreadTask in dynamic.
|
||||
///
|
||||
/// You should not run Unity.Engine code in RenderThread. It will cause the Unity.Engine to hang.
|
||||
/// Any exception will not be caught and shown in RenderThread.
|
||||
/// You should print your error message out to clearify your issue.
|
||||
///
|
||||
/// The 'lock' expression is not used here. Because I believe the lock is not necessary in this case.
|
||||
/// And the lock will cause the performance issue. All the design here help you not to use 'lock'.
|
||||
/// </summary>
|
||||
public class RenderThreadTask
|
||||
{
|
||||
private static IntPtr GetFunctionPointerForDelegate(Delegate del)
|
||||
{
|
||||
return System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(del);
|
||||
}
|
||||
|
||||
public delegate void RenderEventDelegate(int e);
|
||||
private static readonly RenderEventDelegate handle = new RenderEventDelegate(RunSyncObjectInRenderThread);
|
||||
private static readonly IntPtr handlePtr = GetFunctionPointerForDelegate(handle);
|
||||
|
||||
public delegate void Receiver(PreAllocatedQueue dataQueue);
|
||||
|
||||
// CommandList is used to store all RenderThreadTask objects.
|
||||
// Do not create RenderThreadTask object in dynamic. It will cause the CommandList to increase infinitly.
|
||||
private static List<RenderThreadTask> CommandList = new List<RenderThreadTask>();
|
||||
|
||||
private PreAllocatedQueue queue;
|
||||
public PreAllocatedQueue Queue { get { return queue; } }
|
||||
|
||||
private readonly Receiver receiver;
|
||||
private readonly int id;
|
||||
|
||||
/// <summary>
|
||||
/// Input the receiver as render thread callback. The receiver will be executed in render thread.
|
||||
/// queueSize should be the double count of message you want to pass to render thread in one frame.
|
||||
/// </summary>
|
||||
/// <param name="render">The callback in render thread.</param>
|
||||
/// <param name="queueSize">If issue this event once in a frame, set queueSize as 2.</param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public RenderThreadTask(Receiver render, int queueSize = 2)
|
||||
{
|
||||
queue = new PreAllocatedQueue(queueSize);
|
||||
receiver = render;
|
||||
if (receiver == null)
|
||||
throw new ArgumentNullException("receiver should not be null");
|
||||
|
||||
CommandList.Add(this);
|
||||
id = CommandList.IndexOf(this);
|
||||
}
|
||||
|
||||
~RenderThreadTask()
|
||||
{
|
||||
// Remove could be in a random order, and will cause orderId change. DO not remove any of them.
|
||||
//try { CommandList.Remove(this); } finally { }
|
||||
}
|
||||
|
||||
void IssuePluginEvent(IntPtr callback, int eventID)
|
||||
{
|
||||
// Older version will hang after run script in render thread.
|
||||
GL.IssuePluginEvent(callback, eventID);
|
||||
return;
|
||||
}
|
||||
|
||||
void IssuePluginEvent(CommandBuffer cmdBuf, IntPtr callback, int eventID)
|
||||
{
|
||||
cmdBuf.IssuePluginEvent(callback, eventID);
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IssueEvent method submits this task's receiver, which is set in constructor, to be executed on the rendering thread.
|
||||
/// </summary>
|
||||
public void IssueEvent()
|
||||
{
|
||||
// Let the render thread run the RunSyncObjectInRenderThread(id)
|
||||
IssuePluginEvent(handlePtr, id);
|
||||
}
|
||||
|
||||
public void IssueInCommandBuffer(CommandBuffer cmdBuf)
|
||||
{
|
||||
// Let the render thread run the RunSyncObjectInRenderThread(id)
|
||||
IssuePluginEvent(cmdBuf, handlePtr, id);
|
||||
}
|
||||
|
||||
// Called by RunSyncObjectInRenderThread()
|
||||
private void Receive()
|
||||
{
|
||||
receiver(queue);
|
||||
}
|
||||
|
||||
// RunSyncObjectInRenderThread method is a static method used to execute a specified task on the rendering thread.
|
||||
// This method is invoked by Unity's rendering event mechanism and does not need to be called directly by developers.
|
||||
[MonoPInvokeCallback(typeof(RenderEventDelegate))]
|
||||
private static void RunSyncObjectInRenderThread(int id)
|
||||
{
|
||||
CommandList[id].Receive();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region sample
|
||||
// Not to compile this sample into your application. Just for reference. You can run this sample in Unity Editor and it will work.
|
||||
#if UNITY_EDITOR
|
||||
public class ViveRenderThreadTaskSample : MonoBehaviour
|
||||
{
|
||||
// Create your own message class.
|
||||
internal class SampleMessage : Message
|
||||
{
|
||||
public int dataPassedToRenderThread;
|
||||
}
|
||||
|
||||
// Use static readonly to create RenderThreadTask. Keep internal to avoid miss use by other developers.
|
||||
internal static readonly RenderThreadTask sampleRenderThreadTask1 = new RenderThreadTask(SampleReceiver1);
|
||||
// Different task use different RenderThreadTask and different recevier.
|
||||
internal static readonly RenderThreadTask sampleRenderThreadTask2 = new RenderThreadTask(SampleReceiver2);
|
||||
|
||||
private static void SampleReceiver1(PreAllocatedQueue dataQueue)
|
||||
{
|
||||
var msg = dataQueue.Dequeue() as SampleMessage;
|
||||
if (msg != null)
|
||||
{
|
||||
// Keep data before release. Use local variable to keep data and release msg early. Should not keep the msg instance itself.
|
||||
var data = msg.dataPassedToRenderThread;
|
||||
// Make sure release the msg if finished. Other wise the memory will keep increasing when Obtain.
|
||||
MessagePool.Release(msg);
|
||||
Debug.Log("Task1, the data passed to render thread: " + data);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SampleReceiver2(PreAllocatedQueue dataQueue)
|
||||
{
|
||||
var msg = dataQueue.Dequeue() as SampleMessage;
|
||||
if (msg != null)
|
||||
{
|
||||
// Keep data before release. Use local variable to keep data and release msg early. Should not keep the msg instance itself.
|
||||
var data = msg.dataPassedToRenderThread;
|
||||
// Make sure release the msg if finished. Other wise the memory will keep increasing when Obtain.
|
||||
MessagePool.Release(msg);
|
||||
Debug.Log("Task2, the data passed to render thread: " + data);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a message to the render thread every frame.
|
||||
private void Update()
|
||||
{
|
||||
// Make sure only one kind of message object is used in the queue.
|
||||
var msg = sampleRenderThreadTask1.Queue.Obtain<SampleMessage>();
|
||||
msg.dataPassedToRenderThread = 123;
|
||||
sampleRenderThreadTask1.Queue.Enqueue(msg);
|
||||
sampleRenderThreadTask1.IssueEvent();
|
||||
}
|
||||
|
||||
// Send a message to render thread when something clicked. Make sure only one click in one frame because the queue size is only two.
|
||||
public void OnClicked()
|
||||
{
|
||||
// Reuse the same message type is ok.
|
||||
var msg = sampleRenderThreadTask2.Queue.Obtain<SampleMessage>();
|
||||
msg.dataPassedToRenderThread = 234;
|
||||
sampleRenderThreadTask2.Queue.Enqueue(msg);
|
||||
sampleRenderThreadTask2.IssueEvent();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 251b4bedf6420fc4e84be778e501343f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 978c35d873ccc3d449ae82aea475ac75
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e3437a00c6e7214d91d99576ac4c563
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae071276547dc9f4f92d41c0c48666dc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,129 @@
|
||||
// "VIVE SDK
|
||||
// © 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 HTC’s SDK license agreement terms and
|
||||
// conditions signed by you and all SDK and API requirements,
|
||||
// specifications, and documentation provided by HTC to You."
|
||||
|
||||
Shader "VIVE/OpenXR/CompositionLayerUICanvas/MultiLayerCanvasUI"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
|
||||
_Color("Tint", Color) = (1,1,1,1)
|
||||
|
||||
_SrcColBlendMode("Source Color Blend Mode", Float) = 5
|
||||
_SrcAlpBlendMode("Source Alpha Blend Mode", Float) = 5
|
||||
_DstColBlendMode("Destination Color Blend Mode", Float) = 10
|
||||
_DstAlpBlendMode("Destination Alpha Blend Mode", Float) = 10
|
||||
|
||||
_StencilComp("Stencil Comparison", Float) = 8
|
||||
_Stencil("Stencil ID", Float) = 0
|
||||
_StencilOp("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask("Color Mask", Float) = 15
|
||||
|
||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"Queue" = "Transparent"
|
||||
"IgnoreProjector" = "True"
|
||||
"RenderType" = "Transparent"
|
||||
"PreviewType" = "Plane"
|
||||
"CanUseSpriteAtlas" = "True"
|
||||
}
|
||||
|
||||
Stencil
|
||||
{
|
||||
Ref[_Stencil]
|
||||
Comp[_StencilComp]
|
||||
Pass[_StencilOp]
|
||||
ReadMask[_StencilReadMask]
|
||||
WriteMask[_StencilWriteMask]
|
||||
}
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
ZTest[unity_GUIZTestMode]
|
||||
Blend [_SrcColBlendMode] [_DstColBlendMode], [_SrcAlpBlendMode] [_DstAlpBlendMode]
|
||||
ColorMask[_ColorMask]
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Default"
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma target 2.0
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityUI.cginc"
|
||||
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
|
||||
struct appdata_t
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
float4 worldPosition : TEXCOORD1;
|
||||
UNITY_VERTEX_OUTPUT_STEREO
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
fixed4 _Color;
|
||||
fixed4 _TextureSampleAdd;
|
||||
float4 _ClipRect;
|
||||
float4 _MainTex_ST;
|
||||
|
||||
v2f vert(appdata_t v)
|
||||
{
|
||||
v2f OUT;
|
||||
UNITY_SETUP_INSTANCE_ID(v);
|
||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
||||
OUT.worldPosition = v.vertex;
|
||||
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
|
||||
|
||||
OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
|
||||
|
||||
OUT.color = v.color * _Color;
|
||||
return OUT;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f IN) : SV_Target
|
||||
{
|
||||
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
|
||||
#endif
|
||||
|
||||
#ifdef UNITY_UI_ALPHACLIP
|
||||
clip(color.a - 0.001);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be5f9629105213e4b9b6269706555141
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
// "VIVE SDK
|
||||
// © 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 HTC’s SDK license agreement terms and
|
||||
// conditions signed by you and all SDK and API requirements,
|
||||
// specifications, and documentation provided by HTC to You."
|
||||
|
||||
Shader "VIVE/OpenXR/CompositionLayer/Texture2DBlitShader"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
|
||||
|
||||
ZWrite Off
|
||||
ColorMask RGBA
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#pragma multi_compile_local _ LINEAR_TO_SRGB_COLOR
|
||||
#pragma multi_compile_local _ LINEAR_TO_SRGB_ALPHA
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertex : SV_POSITION;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
int _LinearColorSpaceConversionNeeded;
|
||||
float4 _MainTex_ST;
|
||||
|
||||
v2f vert (appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
|
||||
#if UNITY_UV_STARTS_AT_TOP
|
||||
o.uv.y = 1.0 - o.uv.y;
|
||||
#endif
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target
|
||||
{
|
||||
// sample the texture
|
||||
fixed4 col = tex2D(_MainTex, i.uv);
|
||||
|
||||
//Linear to sRGB approximation
|
||||
#ifdef LINEAR_TO_SRGB_COLOR
|
||||
float3 S1c = sqrt(col.rgb);
|
||||
float3 S2c = sqrt(S1c);
|
||||
float3 S3c = sqrt(S2c);
|
||||
col.rgb = 0.662002687 * S1c + 0.684122060 * S2c - 0.323583601 * S3c - 0.0225411470 * col.rgb;
|
||||
#endif
|
||||
|
||||
#ifdef LINEAR_TO_SRGB_ALPHA
|
||||
float S1a = sqrt(col.a);
|
||||
float S2a = sqrt(S1a);
|
||||
float S3a = sqrt(S2a);
|
||||
col.a = 0.662002687 * S1a + 0.684122060 * S2a - 0.323583601 * S3a - 0.0225411470 * col.a;
|
||||
#endif
|
||||
|
||||
return col;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94841cafb08321549b83677281ddc49b
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
// "VIVE SDK
|
||||
// © 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 HTC’s SDK license agreement terms and
|
||||
// conditions signed by you and all SDK and API requirements,
|
||||
// specifications, and documentation provided by HTC to You."
|
||||
|
||||
Shader "VIVE/OpenXR/CompositionLayer/UnderlayAlphaZero"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex("Texture", 2D) = "white" {}
|
||||
_ColorScale("Color Scale", Color) = (1,1,1,1)
|
||||
_ColorBias("Color Bias", Color) = (0,0,0,0)
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags {"Queue" = "Transparent" "RenderType" = "Transparent"}
|
||||
LOD 500
|
||||
|
||||
ZWrite On
|
||||
Blend Zero OneMinusSrcAlpha
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#pragma multi_compile_local _ COLOR_SCALE_BIAS_ENABLED
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertex : SV_POSITION;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
|
||||
fixed4 _ColorScale;
|
||||
fixed4 _ColorBias;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
fixed4 col = tex2D(_MainTex, i.uv);
|
||||
|
||||
#ifdef COLOR_SCALE_BIAS_ENABLED
|
||||
|
||||
col *= _ColorScale.a;
|
||||
col += _ColorBias.a;
|
||||
|
||||
#endif
|
||||
|
||||
return col;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33fd51a7cf157a44683ddd081be6c46c
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
// "VIVE SDK
|
||||
// © 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 HTC’s SDK license agreement terms and
|
||||
// conditions signed by you and all SDK and API requirements,
|
||||
// specifications, and documentation provided by HTC to You."
|
||||
|
||||
Shader "VIVE/OpenXR/CompositionLayer/UnderlayAlphaZeroSolid"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex("Texture", 2D) = "white" {}
|
||||
_ColorScale("Color Scale", Color) = (1,1,1,1)
|
||||
_ColorBias("Color Bias", Color) = (0,0,0,0)
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags {"Queue" = "Transparent" "RenderType" = "Transparent"}
|
||||
LOD 500
|
||||
|
||||
ZWrite On
|
||||
Blend Zero Zero
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#pragma multi_compile_local _ COLOR_SCALE_BIAS_ENABLED
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertex : SV_POSITION;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
|
||||
fixed4 _ColorScale;
|
||||
fixed4 _ColorBias;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
fixed4 col = tex2D(_MainTex, i.uv);
|
||||
|
||||
return col;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33a39a24d09f3cf48ae13735ee2209f6
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 140ef7005df5ced47a85fa81ac8a8a20
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 221c4e845ba39fa4396461ac8c3b9e8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,339 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using VIVE.OpenXR.CompositionLayer;
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
public class CompositionLayerManager : MonoBehaviour
|
||||
{
|
||||
private uint maxLayerCount = 0;
|
||||
|
||||
private static CompositionLayerManager instance = null;
|
||||
private List<CompositionLayer> compositionLayers = new List<CompositionLayer>();
|
||||
private List<CompositionLayer> compositionLayersToBeSubscribed = new List<CompositionLayer>();
|
||||
private List<CompositionLayer> compositionLayersToBeUnsubscribed = new List<CompositionLayer>();
|
||||
|
||||
private bool isOnBeforeRenderSubscribed = false;
|
||||
private ViveCompositionLayer compositionLayerFeature = null;
|
||||
|
||||
private const string LOG_TAG = "VIVE_CompositionLayerManager";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
#region public parameter access functions
|
||||
public static CompositionLayerManager GetInstance()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
GameObject CompositionLayerManagerGO = new GameObject("MultiLayerManager", typeof(CompositionLayerManager));
|
||||
instance = CompositionLayerManagerGO.GetComponent<CompositionLayerManager>();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static bool CompositionLayerManagerExists()
|
||||
{
|
||||
return (instance != null);
|
||||
}
|
||||
|
||||
public int MaxLayerCount()
|
||||
{
|
||||
return (int)maxLayerCount;
|
||||
}
|
||||
|
||||
public int RemainingLayerCount()
|
||||
{
|
||||
int count = (int)maxLayerCount - compositionLayers.Count;
|
||||
if (count < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public int CurrentLayerCount()
|
||||
{
|
||||
return compositionLayers.Count;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Monobehaviour Lifecycle
|
||||
void Awake()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
else if (instance != this)
|
||||
{
|
||||
Destroy(instance);
|
||||
instance = this;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
UpdateMaxLayerCount();
|
||||
}
|
||||
|
||||
public delegate void CompositionLayerManagerOnBeforeRender();
|
||||
public event CompositionLayerManagerOnBeforeRender CompositionLayerManagerOnBeforeRenderDelegate = null;
|
||||
|
||||
private void OnBeforeRender()
|
||||
{
|
||||
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
|
||||
|
||||
if (compositionLayerFeature != null)
|
||||
{
|
||||
if (compositionLayerFeature.XrSessionEnding)
|
||||
{
|
||||
DEBUG("XrSession is ending, stop all layers");
|
||||
foreach (CompositionLayer layer in compositionLayers) //All active layers
|
||||
{
|
||||
if (!compositionLayersToBeUnsubscribed.Contains(layer) && !compositionLayersToBeSubscribed.Contains(layer))
|
||||
{
|
||||
//Add currently active layers that are not scheduled for termination to the "To be subscribed" list
|
||||
compositionLayersToBeSubscribed.Add(layer);
|
||||
}
|
||||
|
||||
layer.TerminateLayer();
|
||||
}
|
||||
compositionLayers.Clear();
|
||||
|
||||
foreach (CompositionLayer layer in compositionLayersToBeUnsubscribed) //All layers to be terminated
|
||||
{
|
||||
layer.TerminateLayer();
|
||||
}
|
||||
compositionLayersToBeUnsubscribed.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("compositionLayerFeature not found");
|
||||
}
|
||||
|
||||
bool CompositionLayerStatusUpdateNeeded = false;
|
||||
|
||||
//Process Sub and Unsub list in bulk at once per frame
|
||||
if (compositionLayersToBeUnsubscribed.Count > 0)
|
||||
{
|
||||
foreach (CompositionLayer layerToBeRemoved in compositionLayersToBeUnsubscribed)
|
||||
{
|
||||
DEBUG("CompositionLayersToBeUnsubscribed: Processing");
|
||||
if (compositionLayers.Contains(layerToBeRemoved) && !compositionLayersToBeSubscribed.Contains(layerToBeRemoved))
|
||||
{
|
||||
layerToBeRemoved.TerminateLayer();
|
||||
compositionLayers.Remove(layerToBeRemoved);
|
||||
}
|
||||
}
|
||||
compositionLayersToBeUnsubscribed.Clear();
|
||||
CompositionLayerStatusUpdateNeeded = true;
|
||||
}
|
||||
|
||||
if (compositionLayersToBeSubscribed.Count > 0)
|
||||
{
|
||||
DEBUG("CompositionLayersToBeSubscribed: Processing");
|
||||
foreach (CompositionLayer layerToBeAdded in compositionLayersToBeSubscribed)
|
||||
{
|
||||
if (!compositionLayers.Contains(layerToBeAdded))
|
||||
{
|
||||
compositionLayers.Add(layerToBeAdded);
|
||||
DEBUG("Add new layer");
|
||||
}
|
||||
else if (layerToBeAdded.isRenderPriorityChanged)
|
||||
{
|
||||
DEBUG("Layer RenderPriority changed");
|
||||
}
|
||||
}
|
||||
compositionLayersToBeSubscribed.Clear();
|
||||
CompositionLayerStatusUpdateNeeded = true;
|
||||
}
|
||||
|
||||
if (CompositionLayerStatusUpdateNeeded)
|
||||
{
|
||||
DEBUG("CompositionLayerStatusUpdateNeeded");
|
||||
UpdateLayerStatus();
|
||||
CompositionLayerStatusUpdateNeeded = false;
|
||||
}
|
||||
|
||||
CompositionLayerManagerOnBeforeRenderDelegate?.Invoke();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (!isOnBeforeRenderSubscribed)
|
||||
{
|
||||
Application.onBeforeRender += OnBeforeRender;
|
||||
isOnBeforeRenderSubscribed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (isOnBeforeRenderSubscribed)
|
||||
{
|
||||
Application.onBeforeRender -= OnBeforeRender;
|
||||
isOnBeforeRenderSubscribed = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (isOnBeforeRenderSubscribed)
|
||||
{
|
||||
Application.onBeforeRender -= OnBeforeRender;
|
||||
isOnBeforeRenderSubscribed = false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void SubscribeToLayerManager(CompositionLayer layerToBeAdded)
|
||||
{
|
||||
if (compositionLayersToBeSubscribed == null)
|
||||
{
|
||||
DEBUG("SubscribeToLayerManager: Layer List not found. Creating a new one.");
|
||||
compositionLayersToBeSubscribed = new List<CompositionLayer>();
|
||||
}
|
||||
|
||||
if (!compositionLayersToBeSubscribed.Contains(layerToBeAdded))
|
||||
{
|
||||
DEBUG("SubscribeToLayerManager: Add layer");
|
||||
compositionLayersToBeSubscribed.Add(layerToBeAdded);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnsubscribeFromLayerManager(CompositionLayer layerToBeRemoved, bool isImmediate)
|
||||
{
|
||||
if (compositionLayersToBeUnsubscribed == null)
|
||||
{
|
||||
DEBUG("UnsubscribeFromLayerManager: Layer List not found. Creating a new one.");
|
||||
compositionLayersToBeUnsubscribed = new List<CompositionLayer>();
|
||||
}
|
||||
|
||||
if (!compositionLayersToBeUnsubscribed.Contains(layerToBeRemoved) && !isImmediate)
|
||||
{
|
||||
DEBUG("UnsubscribeFromLayerManager: Remove layer");
|
||||
compositionLayersToBeUnsubscribed.Add(layerToBeRemoved);
|
||||
}
|
||||
else if (isImmediate)
|
||||
{
|
||||
layerToBeRemoved.TerminateLayer();
|
||||
|
||||
if (compositionLayersToBeUnsubscribed.Contains(layerToBeRemoved))
|
||||
{
|
||||
compositionLayersToBeUnsubscribed.Remove(layerToBeRemoved);
|
||||
}
|
||||
|
||||
if (compositionLayersToBeSubscribed.Contains(layerToBeRemoved))
|
||||
{
|
||||
compositionLayersToBeSubscribed.Remove(layerToBeRemoved);
|
||||
}
|
||||
|
||||
if (compositionLayers.Contains(layerToBeRemoved))
|
||||
{
|
||||
compositionLayers.Remove(layerToBeRemoved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLayerStatus()
|
||||
{
|
||||
SortCompositionLayers();
|
||||
RenderCompositionLayers();
|
||||
}
|
||||
|
||||
private void SortCompositionLayers()
|
||||
{
|
||||
if (compositionLayers == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CompositionLayerRenderPriorityComparer renderPriorityComparer = new CompositionLayerRenderPriorityComparer();
|
||||
compositionLayers.Sort(renderPriorityComparer);
|
||||
}
|
||||
|
||||
private void RenderCompositionLayers()
|
||||
{
|
||||
UpdateMaxLayerCount();
|
||||
|
||||
for (int layerIndex=0; layerIndex < compositionLayers.Count; layerIndex++)
|
||||
{
|
||||
if (layerIndex < maxLayerCount) //Render as normal layers
|
||||
{
|
||||
if (compositionLayers[layerIndex].RenderAsLayer()) //Successfully initialized
|
||||
{
|
||||
DEBUG("RenderCompositionLayers: Layer " + compositionLayers[layerIndex].name + " Initialized successfully, Priority: " + compositionLayers[layerIndex].GetRenderPriority() + " layerIndex: " + layerIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG("RenderCompositionLayers: Layer Initialization failed." + " layerIndex: " + layerIndex);
|
||||
}
|
||||
}
|
||||
else //Fallback if enabled
|
||||
{
|
||||
compositionLayers[layerIndex].RenderInGame();
|
||||
DEBUG("RenderCompositionLayers: Layer " + compositionLayers[layerIndex].name + " Rendering in game, Priority: " + compositionLayers[layerIndex].GetRenderPriority() + " layerIndex: " + layerIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMaxLayerCount()
|
||||
{
|
||||
XrSystemProperties xrSystemProperties = new XrSystemProperties();
|
||||
XrResult result;
|
||||
|
||||
compositionLayerFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayer>();
|
||||
|
||||
if (compositionLayerFeature != null)
|
||||
{
|
||||
if ((result = compositionLayerFeature.GetSystemProperties(ref xrSystemProperties)) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
maxLayerCount = xrSystemProperties.graphicsProperties.maxLayerCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("Failed to get max layer count: " + result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("compositionLayerFeature not found");
|
||||
maxLayerCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class CompositionLayerRenderPriorityComparer : IComparer<CompositionLayer>
|
||||
{
|
||||
public int Compare(CompositionLayer layerX, CompositionLayer layerY)
|
||||
{
|
||||
//Rule1: Higher Render Priority -> Front of the list
|
||||
//Rule2: Same Render Priority -> Do not move layer
|
||||
if (layerX.GetRenderPriority() > layerY.GetRenderPriority())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (layerX.GetRenderPriority() < layerY.GetRenderPriority())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f9ba7acd7178cf4c9eabce25b055324
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,890 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using VIVE.OpenXR.CompositionLayer;
|
||||
using VIVE.OpenXR.CompositionLayer.Passthrough;
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
||||
{
|
||||
[Obsolete("This class is deprecated. Please use PassthroughAPI instead.")]
|
||||
public static class CompositionLayerPassthroughAPI
|
||||
{
|
||||
const string LOG_TAG = "CompositionLayerPassthroughAPI";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
private static ViveCompositionLayerPassthrough passthroughFeature = null;
|
||||
private static bool checkPassthroughFeatureInstance()
|
||||
{
|
||||
passthroughFeature = OpenXRSettings.Instance.GetFeature<ViveCompositionLayerPassthrough>();
|
||||
|
||||
if (!passthroughFeature) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
private static Dictionary<int, XrCompositionLayerPassthroughHTC> passthrough2Layer = new Dictionary<int, XrCompositionLayerPassthroughHTC>();
|
||||
private static Dictionary<int, IntPtr> passthrough2LayerPtr = new Dictionary<int, IntPtr>();
|
||||
private static Dictionary<int, bool> passthrough2IsUnderLay= new Dictionary<int, bool>();
|
||||
private static Dictionary<int, XrPassthroughMeshTransformInfoHTC> passthrough2meshTransform = new Dictionary<int, XrPassthroughMeshTransformInfoHTC>();
|
||||
private static Dictionary<int, IntPtr> passthrough2meshTransformInfoPtr = new Dictionary<int, IntPtr>();
|
||||
#endif
|
||||
#region Public APIs
|
||||
|
||||
/// <summary>
|
||||
/// For creating a fullscreen passthrough.
|
||||
/// Passthroughs will be destroyed automatically when the current XrSession is destroyed.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// ID of the created passthrough.
|
||||
/// Value will be 0 if passthrough is not created successfully.
|
||||
/// </returns>
|
||||
/// <param name="layerType">
|
||||
/// Specify whether the passthrough is an overlay or underlay. See <see cref="LayerType"/> for details.
|
||||
/// </param>
|
||||
/// <param name="onDestroyPassthroughSessionHandler">
|
||||
/// Delegate of type <see cref="ViveCompositionLayerPassthrough.OnPassthroughSessionDestroyDelegate">OnPassthroughSessionDestroyDelegate</see>.
|
||||
/// This delegate will be invoked when the current OpenXR Session is going to be destroyed, which is when the created passthrough layer should be destroyed if not.
|
||||
/// </param>
|
||||
/// <param name="alpha">
|
||||
/// Specify the alpha of the passthrough layer.
|
||||
/// Should be within range [0, 1]
|
||||
/// 1 (Opaque) by default.
|
||||
/// </param>
|
||||
/// <param name="compositionDepth">
|
||||
/// Specify the composition depth relative to other composition layers if present.
|
||||
/// 0 by default.
|
||||
/// </param>
|
||||
public static int CreatePlanarPassthrough(LayerType layerType, ViveCompositionLayerPassthrough.OnPassthroughSessionDestroyDelegate onDestroyPassthroughSessionHandler = null, float alpha = 1f, uint compositionDepth = 0)
|
||||
{
|
||||
int passthroughID = 0;
|
||||
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return passthroughID;
|
||||
}
|
||||
#if UNITY_ANDROID
|
||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Planar, onDestroyPassthroughSessionHandler, compositionDepth);
|
||||
#endif
|
||||
#if UNITY_STANDALONE
|
||||
XrPassthroughHTC passthrough = 0;
|
||||
XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC(
|
||||
XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC,
|
||||
new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe.
|
||||
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PLANAR_HTC
|
||||
);
|
||||
XrResult res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough);
|
||||
if(res == XrResult.XR_SUCCESS)
|
||||
{
|
||||
ulong passthrough_ulong = passthrough;
|
||||
passthroughID = (int)passthrough_ulong;
|
||||
XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC(
|
||||
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC,
|
||||
in_next: IntPtr.Zero,
|
||||
in_alpha: alpha);
|
||||
XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC(
|
||||
in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC,
|
||||
in_next: IntPtr.Zero,
|
||||
in_layerFlags: ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT,
|
||||
in_space: 0,
|
||||
in_passthrough: passthrough,
|
||||
in_color: passthroughColor);
|
||||
passthrough2Layer.Add(passthroughID, compositionLayerPassthrough);
|
||||
IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
|
||||
passthrough2LayerPtr.Add(passthroughID, layerPtr);
|
||||
if (layerType == LayerType.Underlay)
|
||||
passthrough2IsUnderLay.Add(passthroughID, true);
|
||||
if (layerType == LayerType.Overlay)
|
||||
passthrough2IsUnderLay.Add(passthroughID, false);
|
||||
}
|
||||
#endif
|
||||
if (passthroughID == 0)
|
||||
{
|
||||
ERROR("Failed to create projected pasthrough");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPassthroughAlpha(passthroughID, alpha);
|
||||
}
|
||||
|
||||
return passthroughID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For creating a projected passthrough (i.e. Passthrough is only partially visible).
|
||||
/// Visible region of the projected passthrough is determined by the mesh and its transform.
|
||||
/// Passthroughs will be destroyed automatically when the current XrSession is destroyed.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// ID of the created passthrough.
|
||||
/// Value will be 0 if passthrough is not created successfully.
|
||||
/// </returns>
|
||||
/// <param name="layerType">
|
||||
/// Specify whether the passthrough is an overlay or underlay. See <see cref="LayerType"/> for details.
|
||||
/// </param>
|
||||
/// <param name="vertexBuffer">
|
||||
/// Positions of the vertices in the mesh.
|
||||
/// </param>
|
||||
///<param name="indexBuffer">
|
||||
/// List of triangles represented by indices into the <paramref name="vertexBuffer"/>.
|
||||
/// </param>
|
||||
/// <param name="spaceType">
|
||||
/// Specify the type of space the projected passthrough is in. See <see cref="ProjectedPassthroughSpaceType"/> for details.
|
||||
/// </param>
|
||||
/// <param name="meshPosition">
|
||||
/// Position of the mesh.
|
||||
/// </param>
|
||||
/// <param name="meshOrientation">
|
||||
/// Orientation of the mesh.
|
||||
/// </param>
|
||||
/// <param name="meshScale">
|
||||
/// Scale of the mesh.
|
||||
/// </param>
|
||||
/// <param name="onDestroyPassthroughSessionHandler">
|
||||
/// Delegate of type <see cref="ViveCompositionLayerPassthrough.OnPassthroughSessionDestroyDelegate">OnPassthroughSessionDestroyDelegate</see>.
|
||||
/// This delegate will be invoked when the current OpenXR Session is going to be destroyed, which is when the created passthrough layer should be destroyed if not.
|
||||
/// </param>
|
||||
/// <param name="alpha">
|
||||
/// Specify the alpha of the passthrough layer.
|
||||
/// Should be within range [0, 1]
|
||||
/// 1 (Opaque) by default.
|
||||
/// </param>
|
||||
/// <param name="compositionDepth">
|
||||
/// Specify the composition depth relative to other composition layers if present.
|
||||
/// 0 by default.
|
||||
/// </param>
|
||||
/// <param name="trackingToWorldSpace">
|
||||
/// Specify whether or not the position and rotation of the mesh transform have to be converted from tracking space to world space.
|
||||
/// </param>
|
||||
/// <param name="convertFromUnityToOpenXR">
|
||||
/// Specify whether the parameters
|
||||
/// <paramref name="vertexBuffer"/>, <paramref name="indexBuffer"/>, <paramref name="meshPosition"/> and <paramref name="meshOrientation"/> have to be converted for OpenXR.
|
||||
/// </param>
|
||||
public static int CreateProjectedPassthrough(LayerType layerType,
|
||||
[In, Out] Vector3[] vertexBuffer, [In, Out] int[] indexBuffer, //For Mesh
|
||||
ProjectedPassthroughSpaceType spaceType, Vector3 meshPosition, Quaternion meshOrientation, Vector3 meshScale, //For Mesh Transform
|
||||
ViveCompositionLayerPassthrough.OnPassthroughSessionDestroyDelegate onDestroyPassthroughSessionHandler = null,
|
||||
float alpha = 1f, uint compositionDepth = 0, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true)
|
||||
{
|
||||
int passthroughID = 0;
|
||||
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return passthroughID;
|
||||
}
|
||||
|
||||
if (vertexBuffer.Length < 3 || indexBuffer.Length % 3 != 0) //Must have at least 3 vertices and complete triangles
|
||||
{
|
||||
ERROR("Mesh data invalid.");
|
||||
return passthroughID;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
XrPassthroughHTC passthrough = 0;
|
||||
XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC(
|
||||
XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC,
|
||||
new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe.
|
||||
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC
|
||||
);
|
||||
XrResult res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough);
|
||||
if (res == XrResult.XR_SUCCESS)
|
||||
{
|
||||
ulong passthrough_ulong = passthrough;
|
||||
passthroughID = (int)passthrough_ulong;
|
||||
XrPassthroughMeshTransformInfoHTC PassthroughMeshTransformInfo = new XrPassthroughMeshTransformInfoHTC(
|
||||
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC,
|
||||
in_next: IntPtr.Zero,
|
||||
in_vertexCount: 0,
|
||||
in_vertices: new XrVector3f[0],
|
||||
in_indexCount: 0,
|
||||
in_indices: new UInt32[0],
|
||||
in_baseSpace: XR_HTC_passthrough.Interop.GetTrackingSpace(),
|
||||
in_time: XR_HTC_passthrough.Interop.GetFrameState().predictedDisplayTime,
|
||||
in_pose: new XrPosef(),
|
||||
in_scale: new XrVector3f()
|
||||
);
|
||||
IntPtr meshTransformInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC)));
|
||||
Marshal.StructureToPtr(PassthroughMeshTransformInfo, meshTransformInfoPtr, false);
|
||||
XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC(
|
||||
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC,
|
||||
in_next: IntPtr.Zero,
|
||||
in_alpha: alpha);
|
||||
XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC(
|
||||
in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC,
|
||||
in_next: meshTransformInfoPtr,
|
||||
in_layerFlags: ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT,
|
||||
in_space: 0,
|
||||
in_passthrough: passthrough,
|
||||
in_color: passthroughColor);
|
||||
passthrough2meshTransform.Add(passthroughID, PassthroughMeshTransformInfo);
|
||||
passthrough2meshTransformInfoPtr.Add(passthroughID, meshTransformInfoPtr);
|
||||
passthrough2Layer.Add(passthroughID, compositionLayerPassthrough);
|
||||
IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
|
||||
passthrough2LayerPtr.Add(passthroughID, layerPtr);
|
||||
if (layerType == LayerType.Underlay)
|
||||
passthrough2IsUnderLay.Add(passthroughID, true);
|
||||
if (layerType == LayerType.Overlay)
|
||||
passthrough2IsUnderLay.Add(passthroughID, false);
|
||||
}
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler, compositionDepth);
|
||||
#endif
|
||||
if (passthroughID == 0)
|
||||
{
|
||||
ERROR("Failed to create projected pasthrough");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPassthroughAlpha(passthroughID, alpha);
|
||||
SetProjectedPassthroughMesh(passthroughID, vertexBuffer, indexBuffer, convertFromUnityToOpenXR);
|
||||
SetProjectedPassthroughMeshTransform(passthroughID, spaceType, meshPosition, meshOrientation, meshScale, trackingToWorldSpace, convertFromUnityToOpenXR);
|
||||
}
|
||||
|
||||
return passthroughID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creating a projected passthrough (i.e. Passthrough is only partially visible).
|
||||
/// Visible region of the projected passthrough is determined by the mesh and its transform.
|
||||
/// Passthroughs will be destroyed automatically when the current XrSession is destroyed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When using this overload, <see cref="SetProjectedPassthroughMesh"/> and <see cref="SetProjectedPassthroughMeshTransform"/> must be called afterwards immediately.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// int PassthroughID = CompositionLayerPassthroughAPI.CreateProjectedPassthrough(layerType, passthroughSessionDestroyHandler, alpha);
|
||||
/// CompositionLayerPassthroughAPI.SetProjectedPassthroughMesh(PassthroughID, quadVertices, quadIndicies, true);
|
||||
/// CompositionLayerPassthroughAPI.SetProjectedPassthroughMeshTranform(PassthroughID, spaceType, position, rotation, scale, true);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>
|
||||
/// ID of the created passthrough.
|
||||
/// Value will be 0 if passthrough is not created successfully.
|
||||
/// </returns>
|
||||
/// <param name="layerType">
|
||||
/// Specify whether the passthrough is an overlay or underlay. See <see cref="LayerType"/> for details.
|
||||
/// </param>
|
||||
/// <param name="onDestroyPassthroughSessionHandler">
|
||||
/// Delegate of type <see cref="ViveCompositionLayerPassthrough.OnPassthroughSessionDestroyDelegate">OnPassthroughSessionDestroyDelegate</see>.
|
||||
/// This delegate will be invoked when the current OpenXR Session is going to be destroyed, which is when the created passthrough layer should be destroyed if not.
|
||||
/// </param>
|
||||
/// <param name="alpha">
|
||||
/// Specify the alpha of the passthrough layer.
|
||||
/// Should be within range [0, 1].
|
||||
/// 1 (Opaque) by default.
|
||||
/// </param>
|
||||
/// <param name="compositionDepth">
|
||||
/// Specify the composition depth relative to other composition layers if present.
|
||||
/// 0 by default.
|
||||
/// </param>
|
||||
public static int CreateProjectedPassthrough(LayerType layerType, ViveCompositionLayerPassthrough.OnPassthroughSessionDestroyDelegate onDestroyPassthroughSessionHandler = null, float alpha = 1f, uint compositionDepth = 0)
|
||||
{
|
||||
int passthroughID = 0;
|
||||
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return passthroughID;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
XrPassthroughHTC passthrough = 0;
|
||||
XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC(
|
||||
XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC,
|
||||
new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe.
|
||||
XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC
|
||||
);
|
||||
XrResult res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough);
|
||||
if (res == XrResult.XR_SUCCESS)
|
||||
{
|
||||
ulong passthrough_ulong = passthrough;
|
||||
passthroughID = (int)passthrough_ulong;
|
||||
XrPassthroughMeshTransformInfoHTC PassthroughMeshTransformInfo = new XrPassthroughMeshTransformInfoHTC(
|
||||
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC,
|
||||
in_next: IntPtr.Zero,
|
||||
in_vertexCount: 0,
|
||||
in_vertices: new XrVector3f[0],
|
||||
in_indexCount: 0,
|
||||
in_indices: new UInt32[0],
|
||||
in_baseSpace: XR_HTC_passthrough.Interop.GetTrackingSpace(),
|
||||
in_time: XR_HTC_passthrough.Interop.GetFrameState().predictedDisplayTime,
|
||||
in_pose: new XrPosef(),
|
||||
in_scale: new XrVector3f()
|
||||
);
|
||||
IntPtr meshTransformInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC)));
|
||||
Marshal.StructureToPtr(PassthroughMeshTransformInfo, meshTransformInfoPtr, false);
|
||||
XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC(
|
||||
in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC,
|
||||
in_next: IntPtr.Zero,
|
||||
in_alpha: alpha);
|
||||
XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC(
|
||||
in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC,
|
||||
in_next: meshTransformInfoPtr,
|
||||
in_layerFlags: ViveCompositionLayerHelper.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT,
|
||||
in_space: 0,
|
||||
in_passthrough: passthrough,
|
||||
in_color: passthroughColor);
|
||||
passthrough2meshTransform.Add(passthroughID, PassthroughMeshTransformInfo);
|
||||
passthrough2meshTransformInfoPtr.Add(passthroughID, meshTransformInfoPtr);
|
||||
passthrough2Layer.Add(passthroughID, compositionLayerPassthrough);
|
||||
IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC)));
|
||||
passthrough2LayerPtr.Add(passthroughID, layerPtr);
|
||||
if (layerType == LayerType.Underlay)
|
||||
passthrough2IsUnderLay.Add(passthroughID, true);
|
||||
if (layerType == LayerType.Overlay)
|
||||
passthrough2IsUnderLay.Add(passthroughID, false);
|
||||
}
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler);
|
||||
#endif
|
||||
if (passthroughID == 0)
|
||||
{
|
||||
ERROR("Failed to create projected pasthrough");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPassthroughAlpha(passthroughID, alpha);
|
||||
}
|
||||
|
||||
return passthroughID;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
private static void SubmitLayer()
|
||||
{
|
||||
XR_HTC_passthrough.Interop.GetOriginEndFrameLayerList(out List<IntPtr> layerList);//GetOriginEndFrameLayers
|
||||
foreach(var passthrough in passthrough2IsUnderLay.Keys)
|
||||
{
|
||||
//Get and submit layer list
|
||||
if (layerList.Count != 0)
|
||||
{
|
||||
Marshal.StructureToPtr(passthrough2Layer[passthrough], passthrough2LayerPtr[passthrough], false);
|
||||
if (passthrough2IsUnderLay[passthrough])
|
||||
layerList.Insert(0, passthrough2LayerPtr[passthrough]);
|
||||
else
|
||||
layerList.Insert(1, passthrough2LayerPtr[passthrough]);
|
||||
}
|
||||
}
|
||||
XR_HTC_passthrough.Interop.SubmitLayers(layerList);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// For destroying a passthrough created previously.
|
||||
/// This function should be called in the delegate instance of type <see cref="ViveCompositionLayerPassthrough.OnPassthroughSessionDestroyDelegate">OnPassthroughSessionDestroyDelegate</see> that is previously assigned when creating a passthrough.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully destroying the specified passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the passthrough to be destroyed.
|
||||
/// </param>
|
||||
public static bool DestroyPassthrough(int passthroughID)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
if (!passthroughFeature.PassthroughIDList.Contains(passthroughID))
|
||||
{
|
||||
ERROR("Passthrough to be destroyed not found");
|
||||
return false;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
XrPassthroughHTC passthrough = passthrough2Layer[passthroughID].passthrough;
|
||||
passthroughFeature.DestroyPassthroughHTC(passthrough);
|
||||
passthrough2IsUnderLay.Remove(passthroughID);
|
||||
SubmitLayer();
|
||||
passthrough2Layer.Remove(passthroughID);
|
||||
if(passthrough2LayerPtr.ContainsKey(passthroughID)) Marshal.FreeHGlobal(passthrough2LayerPtr[passthroughID]);
|
||||
passthrough2LayerPtr.Remove(passthroughID);
|
||||
if(passthrough2meshTransformInfoPtr.ContainsKey(passthroughID)) Marshal.FreeHGlobal(passthrough2meshTransformInfoPtr[passthroughID]);
|
||||
passthrough2meshTransformInfoPtr.Remove(passthroughID);
|
||||
passthrough2meshTransform.Remove(passthroughID);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_DestroyPassthrough(passthroughID);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying the opacity of a specific passthrough layer.
|
||||
/// Can be used for both Planar and Projected passthroughs.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the opacity the specified passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="alpha">
|
||||
/// Specify the alpha of the passthrough layer.
|
||||
/// Should be within range [0, 1]
|
||||
/// 1 (Opaque) by default.
|
||||
/// </param>
|
||||
/// <param name="autoClamp">
|
||||
/// Specify whether out of range alpha values should be clamped automatically.
|
||||
/// When set to true, the function will clamp and apply the alpha value automatically.
|
||||
/// When set to false, the function will return false if the alpha is out of range.
|
||||
/// Set to true by default.
|
||||
/// </param>
|
||||
public static bool SetPassthroughAlpha(int passthroughID, float alpha, bool autoClamp = true)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
#if UNITY_ANDROID
|
||||
if (autoClamp)
|
||||
{
|
||||
return passthroughFeature.HTCPassthrough_SetAlpha(passthroughID, Mathf.Clamp01(alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alpha < 0f || alpha > 1f)
|
||||
{
|
||||
ERROR("SetPassthroughAlpha: Alpha out of range");
|
||||
return false;
|
||||
}
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetAlpha(passthroughID, alpha);
|
||||
}
|
||||
#endif
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2Layer.ContainsKey(passthroughID))
|
||||
{
|
||||
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||
layer.color.alpha = alpha;
|
||||
passthrough2Layer[passthroughID] = layer;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying the mesh data of a projected passthrough layer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the mesh data of the projected passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="vertexBuffer">
|
||||
/// Positions of the vertices in the mesh.
|
||||
/// </param>
|
||||
///<param name="indexBuffer">
|
||||
/// List of triangles represented by indices into the <paramref name="vertexBuffer"/>.
|
||||
/// </param>
|
||||
/// <param name="convertFromUnityToOpenXR">
|
||||
/// Specify whether the parameters
|
||||
/// <paramref name="vertexBuffer"/> and <paramref name="indexBuffer"/> have to be converted for OpenXR.
|
||||
/// </param>
|
||||
public static bool SetProjectedPassthroughMesh(int passthroughID, [In, Out] Vector3[] vertexBuffer, [In, Out] int[] indexBuffer, bool convertFromUnityToOpenXR = true)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vertexBuffer.Length < 3 || indexBuffer.Length % 3 != 0) //Must have at least 3 vertices and complete triangles
|
||||
{
|
||||
ERROR("Mesh data invalid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
XrVector3f[] vertexBufferXrVector = new XrVector3f[vertexBuffer.Length];
|
||||
|
||||
for (int i = 0; i < vertexBuffer.Length; i++)
|
||||
{
|
||||
vertexBufferXrVector[i] = OpenXRHelper.ToOpenXRVector(vertexBuffer[i], convertFromUnityToOpenXR);
|
||||
}
|
||||
|
||||
uint[] indexBufferUint = new uint[indexBuffer.Length];
|
||||
|
||||
for (int i = 0; i < indexBuffer.Length; i++)
|
||||
{
|
||||
indexBufferUint[i] = (uint)indexBuffer[i];
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||
{
|
||||
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||
MeshTransformInfo.vertexCount = (uint)vertexBuffer.Length;
|
||||
MeshTransformInfo.vertices = vertexBufferXrVector;
|
||||
MeshTransformInfo.indexCount = (uint)indexBuffer.Length;
|
||||
MeshTransformInfo.indices = indexBufferUint;
|
||||
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||
passthrough2Layer[passthroughID] = layer;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
//Note: Ignore Clock-Wise definition of index buffer for now as passthrough extension does not have back-face culling
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_SetMesh(passthroughID, (uint)vertexBuffer.Length, vertexBufferXrVector, (uint)indexBuffer.Length, indexBufferUint); ;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying the mesh transform of a projected passthrough layer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the mesh data of the projected passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="spaceType">
|
||||
/// Specify the type of space the projected passthrough is in. See <see cref="ProjectedPassthroughSpaceType"/> for details.
|
||||
/// </param>
|
||||
/// <param name="meshPosition">
|
||||
/// Position of the mesh.
|
||||
/// </param>
|
||||
/// <param name="meshOrientation">
|
||||
/// Orientation of the mesh.
|
||||
/// </param>
|
||||
/// <param name="meshScale">
|
||||
/// Scale of the mesh.
|
||||
/// </param>
|
||||
/// <param name="trackingToWorldSpace">
|
||||
/// Specify whether or not the position and rotation of the mesh transform have to be converted from tracking space to world space.
|
||||
/// </param>
|
||||
/// <param name="convertFromUnityToOpenXR">
|
||||
/// Specify whether the parameters
|
||||
/// <paramref name="meshPosition"/> and <paramref name="meshOrientation"/> have to be converted for OpenXR.
|
||||
/// </param>
|
||||
public static bool SetProjectedPassthroughMeshTransform(int passthroughID, ProjectedPassthroughSpaceType spaceType, Vector3 meshPosition, Quaternion meshOrientation, Vector3 meshScale, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 trackingSpaceMeshPosition = meshPosition;
|
||||
Quaternion trackingSpaceMeshRotation = meshOrientation;
|
||||
TrackingSpaceOrigin currentTrackingSpaceOrigin = TrackingSpaceOrigin.Instance;
|
||||
|
||||
if (currentTrackingSpaceOrigin != null && trackingToWorldSpace) //Apply origin correction to the mesh pose
|
||||
{
|
||||
Matrix4x4 trackingSpaceOriginTRS = Matrix4x4.TRS(currentTrackingSpaceOrigin.transform.position, currentTrackingSpaceOrigin.transform.rotation, Vector3.one);
|
||||
Matrix4x4 worldSpaceLayerPoseTRS = Matrix4x4.TRS(meshPosition, meshOrientation, Vector3.one);
|
||||
|
||||
Matrix4x4 trackingSpaceLayerPoseTRS = trackingSpaceOriginTRS.inverse * worldSpaceLayerPoseTRS;
|
||||
|
||||
trackingSpaceMeshPosition = trackingSpaceLayerPoseTRS.GetColumn(3); //4th Column of TRS Matrix is the position
|
||||
trackingSpaceMeshRotation = Quaternion.LookRotation(trackingSpaceLayerPoseTRS.GetColumn(2), trackingSpaceLayerPoseTRS.GetColumn(1));
|
||||
}
|
||||
|
||||
XrPosef meshXrPose;
|
||||
meshXrPose.position = OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR);
|
||||
meshXrPose.orientation = OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR);
|
||||
|
||||
XrVector3f meshXrScale = OpenXRHelper.ToOpenXRVector(meshScale, false);
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||
{
|
||||
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||
MeshTransformInfo.pose = meshXrPose;
|
||||
MeshTransformInfo.scale = meshXrScale;
|
||||
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||
passthrough2Layer[passthroughID] = layer;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransform(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType), meshXrPose, meshXrScale);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying layer type and composition depth of a passthrough layer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the layer type and composition depth of the passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="layerType">
|
||||
/// Specify whether the passthrough is an overlay or underlay. See <see cref="LayerType"/> for details.
|
||||
/// </param>
|
||||
/// <param name="compositionDepth">
|
||||
/// Specify the composition depth relative to other composition layers if present.
|
||||
/// 0 by default.
|
||||
/// </param>
|
||||
public static bool SetPassthroughLayerType(int passthroughID, LayerType layerType, uint compositionDepth = 0)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2IsUnderLay.ContainsKey(passthroughID))
|
||||
{
|
||||
passthrough2IsUnderLay[passthroughID] = layerType == LayerType.Underlay ? true : false;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_SetLayerType(passthroughID, layerType, compositionDepth);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying the space of a projected passthrough layer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the space of the projected passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="spaceType">
|
||||
/// Specify the type of space the projected passthrough is in. See <see cref="ProjectedPassthroughSpaceType"/> for details.
|
||||
/// </param>
|
||||
public static bool SetProjectedPassthroughSpaceType(int passthroughID, ProjectedPassthroughSpaceType spaceType)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||
{
|
||||
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||
MeshTransformInfo.baseSpace = passthroughFeature.GetXrSpaceFromSpaceType(spaceType);
|
||||
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||
passthrough2Layer[passthroughID] = layer;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformSpace(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying the mesh position of a projected passthrough layer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the mesh position of the projected passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="meshPosition">
|
||||
/// Position of the mesh.
|
||||
/// </param>
|
||||
/// <param name="trackingToWorldSpace">
|
||||
/// Specify whether or not the position of the mesh transform have to be converted from tracking space to world space.
|
||||
/// </param>
|
||||
/// <param name="convertFromUnityToOpenXR">
|
||||
/// Specify whether the parameter
|
||||
/// <paramref name="meshPosition"/> have to be converted for OpenXR.
|
||||
/// </param>
|
||||
public static bool SetProjectedPassthroughMeshPosition(int passthroughID, Vector3 meshPosition, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 trackingSpaceMeshPosition = meshPosition;
|
||||
TrackingSpaceOrigin currentTrackingSpaceOrigin = TrackingSpaceOrigin.Instance;
|
||||
|
||||
if (currentTrackingSpaceOrigin != null && trackingToWorldSpace) //Apply origin correction to the mesh pose
|
||||
{
|
||||
Matrix4x4 trackingSpaceOriginTRS = Matrix4x4.TRS(currentTrackingSpaceOrigin.transform.position, Quaternion.identity, Vector3.one);
|
||||
Matrix4x4 worldSpaceLayerPoseTRS = Matrix4x4.TRS(meshPosition, Quaternion.identity, Vector3.one);
|
||||
|
||||
Matrix4x4 trackingSpaceLayerPoseTRS = trackingSpaceOriginTRS.inverse * worldSpaceLayerPoseTRS;
|
||||
|
||||
trackingSpaceMeshPosition = trackingSpaceLayerPoseTRS.GetColumn(3); //4th Column of TRS Matrix is the position
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||
{
|
||||
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||
XrPosef meshXrPose = MeshTransformInfo.pose;
|
||||
meshXrPose.position = OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR); ;
|
||||
MeshTransformInfo.pose = meshXrPose;
|
||||
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||
passthrough2Layer[passthroughID] = layer;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformPosition(passthroughID, OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying the mesh orientation of a projected passthrough layer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the mesh orientation of the projected passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="meshOrientation">
|
||||
/// Orientation of the mesh.
|
||||
/// </param>
|
||||
/// <param name="trackingToWorldSpace">
|
||||
/// Specify whether or not the rotation of the mesh transform have to be converted from tracking space to world space.
|
||||
/// </param>
|
||||
/// <param name="convertFromUnityToOpenXR">
|
||||
/// Specify whether the parameter
|
||||
/// <paramref name="meshOrientation"/> have to be converted for OpenXR.
|
||||
/// </param>
|
||||
public static bool SetProjectedPassthroughMeshOrientation(int passthroughID, Quaternion meshOrientation, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Quaternion trackingSpaceMeshRotation = meshOrientation;
|
||||
TrackingSpaceOrigin currentTrackingSpaceOrigin = TrackingSpaceOrigin.Instance;
|
||||
|
||||
if (currentTrackingSpaceOrigin != null && trackingToWorldSpace) //Apply origin correction to the mesh pose
|
||||
{
|
||||
Matrix4x4 trackingSpaceOriginTRS = Matrix4x4.TRS(Vector3.zero, currentTrackingSpaceOrigin.transform.rotation, Vector3.one);
|
||||
Matrix4x4 worldSpaceLayerPoseTRS = Matrix4x4.TRS(Vector3.zero, meshOrientation, Vector3.one);
|
||||
|
||||
Matrix4x4 trackingSpaceLayerPoseTRS = trackingSpaceOriginTRS.inverse * worldSpaceLayerPoseTRS;
|
||||
|
||||
trackingSpaceMeshRotation = Quaternion.LookRotation(trackingSpaceLayerPoseTRS.GetColumn(2), trackingSpaceLayerPoseTRS.GetColumn(1));
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||
{
|
||||
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||
XrPosef meshXrPose = MeshTransformInfo.pose;
|
||||
meshXrPose.orientation = OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR);
|
||||
MeshTransformInfo.pose = meshXrPose;
|
||||
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||
passthrough2Layer[passthroughID] = layer;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformOrientation(passthroughID, OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For modifying the mesh scale of a passthrough layer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True for successfully modifying the mesh data of the projected passthrough, vice versa.
|
||||
/// </returns>
|
||||
/// <param name="passthroughID">
|
||||
/// The ID of the target passthrough.
|
||||
/// </param>
|
||||
/// <param name="meshScale">
|
||||
/// Scale of the mesh.
|
||||
/// </param>
|
||||
public static bool SetProjectedPassthroughScale(int passthroughID, Vector3 meshScale)
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return false;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
if (passthrough2meshTransformInfoPtr.ContainsKey(passthroughID))
|
||||
{
|
||||
XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthroughID];
|
||||
MeshTransformInfo.scale = OpenXRHelper.ToOpenXRVector(meshScale, false);
|
||||
passthrough2meshTransform[passthroughID] = MeshTransformInfo;
|
||||
Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthroughID], false);
|
||||
XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthroughID];
|
||||
layer.next = passthrough2meshTransformInfoPtr[passthroughID];
|
||||
passthrough2Layer[passthroughID] = layer;
|
||||
SubmitLayer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if UNITY_ANDROID
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformScale(passthroughID, OpenXRHelper.ToOpenXRVector(meshScale, false));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To get the list of IDs of active passthrough layers.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The a copy of the list of IDs of active passthrough layers.
|
||||
/// </returns>
|
||||
public static List<int> GetCurrentPassthroughLayerIDs()
|
||||
{
|
||||
if (!checkPassthroughFeatureInstance())
|
||||
{
|
||||
ERROR("HTC_Passthrough feature instance not found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return passthroughFeature.PassthroughIDList;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84f10e5139d365a4dafe943a2d80829b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using AOT;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
public class CompositionLayerRenderThreadSyncObject
|
||||
{
|
||||
private static IntPtr GetFunctionPointerForDelegate(Delegate del)
|
||||
{
|
||||
#if UNITY_EDITOR && UNITY_ANDROID
|
||||
return IntPtr.Zero;
|
||||
#elif UNITY_ANDROID
|
||||
return Marshal.GetFunctionPointerForDelegate(del);
|
||||
#else
|
||||
return IntPtr.Zero;
|
||||
#endif
|
||||
}
|
||||
|
||||
public delegate void CompositionLayerRenderEventDelegate(int eventID);
|
||||
private static readonly CompositionLayerRenderEventDelegate handle = new CompositionLayerRenderEventDelegate(RunSyncObjectInRenderThread);
|
||||
private static readonly IntPtr handlePtr = GetFunctionPointerForDelegate(handle);
|
||||
|
||||
public delegate void TaskQueueDelagate(PreAllocatedQueue taskQueue);
|
||||
|
||||
private static List<CompositionLayerRenderThreadSyncObject> taskList = new List<CompositionLayerRenderThreadSyncObject>();
|
||||
|
||||
private readonly PreAllocatedQueue queue = new PreAllocatedQueue();
|
||||
public PreAllocatedQueue Queue { get { return queue; } }
|
||||
|
||||
private readonly TaskQueueDelagate receiver;
|
||||
private readonly int taskID;
|
||||
|
||||
public CompositionLayerRenderThreadSyncObject(TaskQueueDelagate taskQueueDelegate)
|
||||
{
|
||||
receiver = taskQueueDelegate;
|
||||
if (receiver == null)
|
||||
throw new ArgumentNullException("receiver should not be null");
|
||||
|
||||
taskList.Add(this);
|
||||
taskID = taskList.IndexOf(this);
|
||||
}
|
||||
|
||||
~CompositionLayerRenderThreadSyncObject()
|
||||
{
|
||||
try { taskList.RemoveAt(taskID); } finally { }
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(CompositionLayerRenderEventDelegate))]
|
||||
private static void RunSyncObjectInRenderThread(int taskID)
|
||||
{
|
||||
taskList[taskID].ReceiveEvent();
|
||||
}
|
||||
|
||||
// Run in GameThread
|
||||
public void IssueEvent()
|
||||
{
|
||||
#if UNITY_EDITOR && UNITY_ANDROID
|
||||
if (Application.isEditor)
|
||||
{
|
||||
receiver(queue);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Let the render thread run the RunSyncObjectInRenderThread(id)
|
||||
#if UNITY_ANDROID
|
||||
GL.IssuePluginEvent(handlePtr, taskID);
|
||||
#else
|
||||
receiver(queue);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ReceiveEvent()
|
||||
{
|
||||
receiver(queue);
|
||||
}
|
||||
}
|
||||
|
||||
public class Task
|
||||
{
|
||||
public bool isFree = true;
|
||||
}
|
||||
|
||||
public class TaskPool
|
||||
{
|
||||
private readonly List<Task> pool = new List<Task>(2) { };
|
||||
private int index = 0;
|
||||
|
||||
public TaskPool() { }
|
||||
|
||||
private int Next(int value)
|
||||
{
|
||||
if (++value >= pool.Count)
|
||||
value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
public T Obtain<T>() where T : Task, new()
|
||||
{
|
||||
int c = pool.Count;
|
||||
int i = index;
|
||||
for (int j = 0; j < c; i++, j++)
|
||||
{
|
||||
if (i >= c)
|
||||
i = 0;
|
||||
if (pool[i].isFree)
|
||||
{
|
||||
//Debug.LogError("Obtain idx=" + i);
|
||||
index = i;
|
||||
return (T)pool[i];
|
||||
}
|
||||
}
|
||||
index = Next(i);
|
||||
var newItem = new T()
|
||||
{
|
||||
isFree = true
|
||||
};
|
||||
pool.Insert(index, newItem);
|
||||
//Debug.LogError("Obtain new one. Pool.Count=" + pool.Count);
|
||||
return newItem;
|
||||
}
|
||||
|
||||
public void Lock(Task msg)
|
||||
{
|
||||
msg.isFree = false;
|
||||
}
|
||||
|
||||
public void Release(Task msg)
|
||||
{
|
||||
msg.isFree = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class PreAllocatedQueue : TaskPool
|
||||
{
|
||||
private readonly List<Task> list = new List<Task>(2) { null, null };
|
||||
private int queueBegin = 0;
|
||||
private int queueEnd = 0;
|
||||
|
||||
public PreAllocatedQueue() : base() { }
|
||||
|
||||
private int Next(int value)
|
||||
{
|
||||
if (++value >= list.Count)
|
||||
value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Enqueue(Task msg)
|
||||
{
|
||||
Lock(msg);
|
||||
queueEnd = Next(queueEnd);
|
||||
|
||||
if (queueEnd == queueBegin)
|
||||
{
|
||||
list.Insert(queueEnd, msg);
|
||||
queueBegin++;
|
||||
}
|
||||
else
|
||||
{
|
||||
list[queueEnd] = msg;
|
||||
}
|
||||
}
|
||||
|
||||
public Task Dequeue()
|
||||
{
|
||||
queueBegin = Next(queueBegin);
|
||||
return list[queueBegin];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5edefaf3751462141bc4fa0c0eed4c08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,293 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
[RequireComponent(typeof(Canvas))]
|
||||
public class CompositionLayerUICanvas : MonoBehaviour
|
||||
{
|
||||
private Canvas sourceCanvas;
|
||||
private RectTransform sourceCanvasRectTransform;
|
||||
private Graphic[] graphicComponents;
|
||||
|
||||
private Camera canvasRenderCamera;
|
||||
private RenderTexture canvasRenderTexture;
|
||||
|
||||
private GameObject canvasCompositionLayerGO;
|
||||
private CompositionLayer canvasCompositionLayer;
|
||||
|
||||
[SerializeField]
|
||||
public uint maxRenderTextureSize = 1024;
|
||||
|
||||
[SerializeField]
|
||||
public CompositionLayer.LayerType layerType = CompositionLayer.LayerType.Underlay;
|
||||
|
||||
[SerializeField]
|
||||
public CompositionLayer.Visibility layerVisibility = CompositionLayer.Visibility.Both;
|
||||
|
||||
[SerializeField]
|
||||
public Color cameraBGColor = Color.clear;
|
||||
|
||||
[SerializeField]
|
||||
public List<GameObject> backgroundGO = new List<GameObject>();
|
||||
|
||||
[SerializeField]
|
||||
public bool enableAlphaBlendingCorrection = false;
|
||||
|
||||
[SerializeField]
|
||||
public uint compositionDepth = 0;
|
||||
|
||||
[SerializeField]
|
||||
private uint renderPriority = 0;
|
||||
public uint GetRenderPriority() { return renderPriority; }
|
||||
public void SetRenderPriority(uint newRenderPriority)
|
||||
{
|
||||
renderPriority = newRenderPriority;
|
||||
canvasCompositionLayer.SetRenderPriority(renderPriority);
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
public GameObject trackingOrigin = null;
|
||||
|
||||
private CompositionLayer.LayerType previousLayerType;
|
||||
private CompositionLayer.Visibility previousLayerVisibility;
|
||||
private uint previousCompositionDepth;
|
||||
private GameObject previousTrackingOrigin;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
sourceCanvas = GetComponent<Canvas>();
|
||||
sourceCanvasRectTransform = sourceCanvas.GetComponent<RectTransform>();
|
||||
|
||||
UpdateUIElementBlendMode();
|
||||
|
||||
//Calulate Aspect Ratio of the Canvas
|
||||
float canvasRectWidth = sourceCanvasRectTransform.rect.width;
|
||||
float canvasRectHeight = sourceCanvasRectTransform.rect.height;
|
||||
|
||||
float canvasAspectRatio_X = 1, canvasAspectRatio_Y = 1;
|
||||
|
||||
if (canvasRectWidth > canvasRectHeight)
|
||||
{
|
||||
canvasAspectRatio_X = canvasRectWidth / canvasRectHeight;
|
||||
}
|
||||
else if (canvasRectWidth < canvasRectHeight)
|
||||
{
|
||||
canvasAspectRatio_Y = canvasRectHeight / canvasRectWidth;
|
||||
}
|
||||
|
||||
//Create Render Texture
|
||||
int renderTextureWidth = Mathf.CeilToInt(maxRenderTextureSize * canvasAspectRatio_X);
|
||||
int renderTextureHeight = Mathf.CeilToInt(maxRenderTextureSize * canvasAspectRatio_Y);
|
||||
|
||||
canvasRenderTexture = new RenderTexture(renderTextureWidth, renderTextureHeight, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default);
|
||||
canvasRenderTexture.useMipMap = false;
|
||||
canvasRenderTexture.filterMode = FilterMode.Bilinear;
|
||||
canvasRenderTexture.autoGenerateMips = false;
|
||||
|
||||
canvasRenderTexture.Create();
|
||||
|
||||
//Create Canvas Rendering Camera
|
||||
GameObject canvasRenderCameraGO = new GameObject(name + "_CanvasRenderCamera");
|
||||
canvasRenderCameraGO.transform.SetParent(transform, false);
|
||||
|
||||
canvasRenderCamera = canvasRenderCameraGO.AddComponent<Camera>();
|
||||
canvasRenderCamera.stereoTargetEye = StereoTargetEyeMask.None;
|
||||
canvasRenderCamera.transform.position = transform.position - transform.forward; //1m away from canvas
|
||||
canvasRenderCamera.orthographic = true;
|
||||
canvasRenderCamera.enabled = false;
|
||||
canvasRenderCamera.targetTexture = canvasRenderTexture;
|
||||
canvasRenderCamera.cullingMask = 1 << gameObject.layer;
|
||||
canvasRenderCamera.clearFlags = CameraClearFlags.SolidColor;
|
||||
canvasRenderCamera.backgroundColor = cameraBGColor;
|
||||
|
||||
float widthWithScale = canvasRectWidth * sourceCanvasRectTransform.localScale.x;
|
||||
float heightWithScale = canvasRectHeight * sourceCanvasRectTransform.localScale.y;
|
||||
|
||||
canvasRenderCamera.orthographicSize = 0.5f * heightWithScale;
|
||||
|
||||
canvasRenderCamera.nearClipPlane = 0.99f;
|
||||
canvasRenderCamera.farClipPlane = 1.01f;
|
||||
|
||||
//Create Composition Layer Component
|
||||
canvasCompositionLayerGO = new GameObject(name + "_CanvasCompositionLayer");
|
||||
canvasCompositionLayerGO.transform.SetParent(transform, false);
|
||||
canvasCompositionLayerGO.transform.localPosition = Vector3.zero;
|
||||
canvasCompositionLayerGO.transform.localRotation = Quaternion.identity;
|
||||
canvasCompositionLayerGO.transform.localScale = Vector3.one;
|
||||
|
||||
canvasCompositionLayer = canvasCompositionLayerGO.AddComponent<CompositionLayer>();
|
||||
canvasCompositionLayer.isDynamicLayer = true;
|
||||
canvasCompositionLayer.texture = canvasRenderTexture;
|
||||
canvasCompositionLayer.layerShape = CompositionLayer.LayerShape.Quad;
|
||||
canvasCompositionLayer.layerType = previousLayerType = layerType;
|
||||
canvasCompositionLayer.layerVisibility = previousLayerVisibility = layerVisibility;
|
||||
canvasCompositionLayer.SetQuadLayerHeight(heightWithScale);
|
||||
canvasCompositionLayer.SetQuadLayerWidth(widthWithScale);
|
||||
canvasCompositionLayer.compositionDepth = previousCompositionDepth = compositionDepth;
|
||||
canvasCompositionLayer.SetRenderPriority(renderPriority);
|
||||
canvasCompositionLayer.trackingOrigin = previousTrackingOrigin = trackingOrigin;
|
||||
if (enableAlphaBlendingCorrection && layerType == CompositionLayer.LayerType.Underlay)
|
||||
{
|
||||
canvasCompositionLayer.ChangeBlitShadermode(CompositionLayer.BlitShaderMode.LINEAR_TO_SRGB_ALPHA, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
canvasCompositionLayer.ChangeBlitShadermode(CompositionLayer.BlitShaderMode.LINEAR_TO_SRGB_ALPHA, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
canvasRenderCamera.Render();
|
||||
|
||||
if (layerType != previousLayerType)
|
||||
{
|
||||
canvasCompositionLayer.layerType = previousLayerType = layerType;
|
||||
|
||||
if (enableAlphaBlendingCorrection && layerType == CompositionLayer.LayerType.Underlay)
|
||||
{
|
||||
canvasCompositionLayer.ChangeBlitShadermode(CompositionLayer.BlitShaderMode.LINEAR_TO_SRGB_ALPHA, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
canvasCompositionLayer.ChangeBlitShadermode(CompositionLayer.BlitShaderMode.LINEAR_TO_SRGB_ALPHA, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (layerVisibility != previousLayerVisibility)
|
||||
{
|
||||
canvasCompositionLayer.layerVisibility = previousLayerVisibility = layerVisibility;
|
||||
}
|
||||
|
||||
if (compositionDepth != previousCompositionDepth)
|
||||
{
|
||||
canvasCompositionLayer.compositionDepth = previousCompositionDepth = compositionDepth;
|
||||
}
|
||||
|
||||
if (trackingOrigin != previousTrackingOrigin)
|
||||
{
|
||||
canvasCompositionLayer.trackingOrigin = previousTrackingOrigin = trackingOrigin;
|
||||
}
|
||||
|
||||
if (canvasRenderCamera.backgroundColor != cameraBGColor)
|
||||
{
|
||||
canvasRenderCamera.backgroundColor = cameraBGColor;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (canvasRenderTexture != null && canvasRenderTexture.IsCreated())
|
||||
{
|
||||
canvasRenderTexture.Release();
|
||||
Destroy(canvasRenderTexture);
|
||||
}
|
||||
if (canvasCompositionLayerGO)
|
||||
{
|
||||
Destroy(canvasCompositionLayerGO);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (canvasRenderCamera)
|
||||
{
|
||||
canvasRenderCamera.enabled = true;
|
||||
}
|
||||
|
||||
if (canvasCompositionLayerGO)
|
||||
{
|
||||
canvasCompositionLayerGO.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (canvasRenderCamera)
|
||||
{
|
||||
canvasRenderCamera.enabled = false;
|
||||
}
|
||||
|
||||
if (canvasCompositionLayerGO)
|
||||
{
|
||||
canvasCompositionLayerGO.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReplaceUIMaterials()
|
||||
{
|
||||
sourceCanvas = GetComponent<Canvas>();
|
||||
graphicComponents = sourceCanvas.GetComponentsInChildren<Graphic>();
|
||||
|
||||
Material underlayCanvasUIMat = new Material(Shader.Find("VIVE/OpenXR/CompositionLayerUICanvas/MultiLayerCanvasUI"));
|
||||
|
||||
foreach (Graphic graphicComponent in graphicComponents)
|
||||
{
|
||||
if (backgroundGO != null && backgroundGO.Contains(graphicComponent.gameObject))
|
||||
{
|
||||
graphicComponent.material = new Material(Shader.Find("VIVE/OpenXR/CompositionLayerUICanvas/MultiLayerCanvasUI")); //Seperate material instance for background
|
||||
}
|
||||
else
|
||||
{
|
||||
graphicComponent.material = underlayCanvasUIMat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateUIElementBlendMode()
|
||||
{
|
||||
sourceCanvas = GetComponent<Canvas>();
|
||||
graphicComponents = sourceCanvas.GetComponentsInChildren<Graphic>();
|
||||
|
||||
foreach (Graphic graphicComponent in graphicComponents)
|
||||
{
|
||||
if (backgroundGO != null && backgroundGO.Contains(graphicComponent.gameObject))
|
||||
{
|
||||
SetUIShaderBlendMode(graphicComponent.material, UIShaderBlendMode.Background);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetUIShaderBlendMode(graphicComponent.material, UIShaderBlendMode.Others);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUIShaderBlendMode(Material canvasUIMaterial, UIShaderBlendMode blendMode = UIShaderBlendMode.Others)
|
||||
{
|
||||
switch (blendMode)
|
||||
{
|
||||
case UIShaderBlendMode.Background: //Discard camera background color and alpha values
|
||||
canvasUIMaterial.SetInt("_SrcColBlendMode", (int)BlendMode.One);
|
||||
canvasUIMaterial.SetInt("_DstColBlendMode", (int)BlendMode.Zero);
|
||||
canvasUIMaterial.SetInt("_SrcAlpBlendMode", (int)BlendMode.One);
|
||||
canvasUIMaterial.SetInt("_DstAlpBlendMode", (int)BlendMode.Zero);
|
||||
|
||||
break;
|
||||
|
||||
case UIShaderBlendMode.Others: //Nornmal transparency blending
|
||||
default:
|
||||
canvasUIMaterial.SetInt("_SrcColBlendMode", (int)BlendMode.SrcAlpha);
|
||||
canvasUIMaterial.SetInt("_DstColBlendMode", (int)BlendMode.OneMinusSrcAlpha);
|
||||
canvasUIMaterial.SetInt("_SrcAlpBlendMode", (int)BlendMode.One);
|
||||
canvasUIMaterial.SetInt("_DstAlpBlendMode", (int)BlendMode.OneMinusSrcAlpha);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public enum UIShaderBlendMode
|
||||
{
|
||||
Background,
|
||||
Others,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a6dae782d369ff4d8daf5ef7ea96a6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.htc.upm.vive.openxr/Runtime/Editor.meta
Normal file
8
Packages/com.htc.upm.vive.openxr/Runtime/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6509bdf37b3b364eb80cb0df68435a3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e0cbfbe15682c542acc5675d4503f72
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VIVE.OpenXR.CompositionLayer;
|
||||
|
||||
namespace VIVE.OpenXR.Editor.CompositionLayer
|
||||
{
|
||||
[CustomEditor(typeof(ViveCompositionLayerExtraSettings))]
|
||||
internal class ViveCompositionLayerEditorExtraSettings : UnityEditor.Editor
|
||||
{
|
||||
//private SerializedProperty SettingsEditorEnableSharpening;
|
||||
|
||||
static string PropertyName_SharpeningEnable = "SettingsEditorEnableSharpening";
|
||||
static GUIContent Label_SharpeningEnable = new GUIContent("Enable Sharpening", "Enable Sharpening.");
|
||||
SerializedProperty Property_SharpeningEnable;
|
||||
|
||||
static string PropertyName_SharpeningLevel = "SettingsEditorSharpeningLevel";
|
||||
static GUIContent Label_SharpeningLevel = new GUIContent("Sharpening Level", "Select Sharpening Level.");
|
||||
SerializedProperty Property_SharpeningLevel;
|
||||
|
||||
static string PropertyName_SharpeningMode = "SettingsEditorSharpeningMode";
|
||||
static GUIContent Label_SharpeningMode = new GUIContent("Sharpening Mode", "Select Sharpening Mode.");
|
||||
SerializedProperty Property_SharpeningMode;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Property_SharpeningEnable = serializedObject.FindProperty(PropertyName_SharpeningEnable);
|
||||
Property_SharpeningMode = serializedObject.FindProperty(PropertyName_SharpeningMode);
|
||||
Property_SharpeningLevel = serializedObject.FindProperty(PropertyName_SharpeningLevel);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.PropertyField(Property_SharpeningEnable, new GUIContent(Label_SharpeningEnable));
|
||||
EditorGUILayout.PropertyField(Property_SharpeningMode, new GUIContent(Label_SharpeningMode));
|
||||
EditorGUILayout.PropertyField(Property_SharpeningLevel, new GUIContent(Label_SharpeningLevel));
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3dfbc6bb6d75454db700d2326157424
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 050772d662d04514ca3bb28fbe82ecd7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
|
||||
using VIVE.OpenXR.FrameSynchronization;
|
||||
|
||||
namespace VIVE.OpenXR.Editor.FrameSynchronization
|
||||
{
|
||||
[CustomEditor(typeof(ViveFrameSynchronization))]
|
||||
public class ViveFrameSynchronizationEditor : UnityEditor.Editor
|
||||
{
|
||||
SerializedProperty m_SynchronizationMode;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_SynchronizationMode = serializedObject.FindProperty("m_SynchronizationMode");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.PropertyField(m_SynchronizationMode);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d25b2e9fff2d6724b865e0fbd609da9d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8bd17374612cce468393aa1acc9fa89
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
using VIVE.OpenXR.Interaction;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
|
||||
namespace VIVE.OpenXR.Editor.Interaction
|
||||
{
|
||||
[CustomEditor(typeof(ViveInteractions))]
|
||||
public class ViveInteractionsEditor : UnityEditor.Editor
|
||||
{
|
||||
SerializedProperty m_ViveHandInteraction, m_ViveWristTracker, m_ViveXRTracker;
|
||||
#if UNITY_ANDROID
|
||||
SerializedProperty m_KHRHandInteraction;
|
||||
#endif
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_ViveHandInteraction = serializedObject.FindProperty("m_ViveHandInteraction");
|
||||
m_ViveWristTracker = serializedObject.FindProperty("m_ViveWristTracker");
|
||||
m_ViveXRTracker = serializedObject.FindProperty("m_ViveXRTracker");
|
||||
#if UNITY_ANDROID
|
||||
m_KHRHandInteraction = serializedObject.FindProperty("m_KHRHandInteraction");
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
#region GUI
|
||||
GUIStyle boxStyleInfo = new GUIStyle(EditorStyles.helpBox);
|
||||
boxStyleInfo.fontSize = 12;
|
||||
boxStyleInfo.wordWrap = true;
|
||||
|
||||
GUIStyle boxStyleWarning = new GUIStyle(EditorStyles.helpBox);
|
||||
boxStyleWarning.fontSize = 12;
|
||||
boxStyleWarning.fontStyle = FontStyle.Bold;
|
||||
boxStyleInfo.wordWrap = true;
|
||||
|
||||
// ViveHandInteraction
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label(
|
||||
"The VIVE Hand Interaction feature enables hand selection and squeezing functions of XR_HTC_hand_interaction extension.\n" +
|
||||
"Please note that enabling this feature impacts runtime performance.",
|
||||
boxStyleInfo);
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.PropertyField(m_ViveHandInteraction);
|
||||
|
||||
// ViveWristTracker
|
||||
GUILayout.Space(20);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label(
|
||||
"The VIVE Wrist Tracker feature enables wrist tracker pose and button functions of XR_HTC_vive_wrist_tracker_interaction extension.\n" +
|
||||
"Please note that enabling this feature impacts runtime performance.",
|
||||
boxStyleInfo);
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.PropertyField(m_ViveWristTracker);
|
||||
|
||||
// ViveXrTracker
|
||||
GUILayout.Space(20);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label(
|
||||
"The VIVE XR Tracker feature enables ultimate tracker pose and button functions.\n" +
|
||||
"WARNING:\n" +
|
||||
"Please be aware that enabling this feature significantly affects runtime performance.",
|
||||
boxStyleWarning);
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.PropertyField(m_ViveXRTracker);
|
||||
/*
|
||||
#if UNITY_ANDROID
|
||||
// ViveHandInteractionExt
|
||||
GUILayout.Space(20);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label(
|
||||
"The KHR Hand Interaction feature enables hand functions of XR_EXT_hand_interaction extension.\n" +
|
||||
"Please note that enabling this feature impacts runtime performance.",
|
||||
boxStyleInfo);
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.PropertyField(m_KHRHandInteraction);
|
||||
#endif
|
||||
*/
|
||||
#endregion
|
||||
|
||||
ViveInteractions myScript = target as ViveInteractions;
|
||||
if (myScript.enabled)
|
||||
{
|
||||
bool viveHandInteraction = myScript.UseViveHandInteraction();
|
||||
bool viveWristTracker = myScript.UseViveWristTracker();
|
||||
bool viveXrTracker = myScript.UseViveXrTracker();
|
||||
//bool khrHandInteraction = myScript.UseKhrHandInteraction();
|
||||
|
||||
OpenXRSettings settings = null;
|
||||
#if UNITY_ANDROID
|
||||
settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
|
||||
#elif UNITY_STANDALONE
|
||||
settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Standalone);
|
||||
#endif
|
||||
if (settings != null)
|
||||
{
|
||||
bool addPathEnumeration = false;
|
||||
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
if (feature is Hand.ViveHandInteraction) { feature.enabled = viveHandInteraction; }
|
||||
if (feature is Tracker.ViveWristTracker) { feature.enabled = viveWristTracker; }
|
||||
if (feature is Tracker.ViveXRTracker)
|
||||
{
|
||||
feature.enabled = viveXrTracker;
|
||||
addPathEnumeration = viveXrTracker;
|
||||
}
|
||||
//if (feature is Hand.ViveHandInteractionExt) { feature.enabled = khrHandInteraction; }
|
||||
}
|
||||
|
||||
foreach (var feature in settings.GetFeatures<OpenXRFeature>())
|
||||
{
|
||||
if (addPathEnumeration && feature is VivePathEnumeration) { feature.enabled = true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/*public class ViveInteractionsBuildHook : OpenXRFeatureBuildHooks
|
||||
{
|
||||
public override int callbackOrder => 1;
|
||||
public override Type featureType => typeof(VIVEFocus3Feature);
|
||||
protected override void OnPostGenerateGradleAndroidProjectExt(string path)
|
||||
{
|
||||
}
|
||||
protected override void OnPostprocessBuildExt(BuildReport report)
|
||||
{
|
||||
}
|
||||
protected override void OnPreprocessBuildExt(BuildReport report)
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
|
||||
if (settings != null)
|
||||
{
|
||||
foreach (var feature in settings.GetFeatures<OpenXRFeature>())
|
||||
{
|
||||
if (feature is ViveInteractions && feature.enabled)
|
||||
{
|
||||
bool viveHandInteraction= ((ViveInteractions)feature).UseViveHandInteraction();
|
||||
bool viveWristTracker = ((ViveInteractions)feature).UseViveWristTracker();
|
||||
bool viveXrTracker = ((ViveInteractions)feature).UseViveXrTracker();
|
||||
bool khrHandInteraction = ((ViveInteractions)feature).UseKhrHandInteraction();
|
||||
Debug.LogFormat($"ViveInteractionsBuildHook() viveHandInteraction: {viveHandInteraction}, viveWristTracker: {viveWristTracker}, viveXrTracker: {viveXrTracker}, khrHandInteraction: {khrHandInteraction}");
|
||||
|
||||
EnableInteraction(viveHandInteraction, viveWristTracker, viveXrTracker, khrHandInteraction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnableInteraction(
|
||||
bool viveHandInteraction = false,
|
||||
bool viveWristTracker = false,
|
||||
bool viveXrTracker = false,
|
||||
bool khrHandInteraction = false)
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
|
||||
if (settings == null) { return; }
|
||||
|
||||
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
|
||||
{
|
||||
if (feature is Hand.ViveHandInteraction) { feature.enabled = viveHandInteraction; Debug.LogFormat($"EnableInteraction() ViveHandInteraction: {feature.enabled}"); }
|
||||
if (feature is Tracker.ViveWristTracker) { feature.enabled = viveWristTracker; Debug.LogFormat($"EnableInteraction() ViveWristTracker: {feature.enabled}"); }
|
||||
if (feature is Tracker.ViveXRTracker) { feature.enabled = viveXrTracker; Debug.LogFormat($"EnableInteraction() ViveXRTracker: {feature.enabled}"); }
|
||||
if (feature is Hand.ViveHandInteractionExt) { feature.enabled = khrHandInteraction; Debug.LogFormat($"EnableInteraction() ViveHandInteractionExt: {feature.enabled}"); }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7e32703a3206194580e534565abcf91
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
|
||||
namespace VIVE.OpenXR.Editor
|
||||
{
|
||||
[CustomEditor(typeof(VIVERig))]
|
||||
public class VIVERigEditor : UnityEditor.Editor
|
||||
{
|
||||
SerializedProperty m_TrackingOrigin, m_CameraOffset, m_CameraHeight, m_ActionAsset;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_TrackingOrigin = serializedObject.FindProperty("m_TrackingOrigin");
|
||||
m_CameraOffset = serializedObject.FindProperty("m_CameraOffset");
|
||||
m_CameraHeight = serializedObject.FindProperty("m_CameraHeight");
|
||||
m_ActionAsset = serializedObject.FindProperty("m_ActionAsset");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
VIVERig myScript = target as VIVERig;
|
||||
|
||||
EditorGUILayout.PropertyField(m_TrackingOrigin);
|
||||
EditorGUILayout.PropertyField(m_CameraOffset);
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"Set the height of camera when the Tracking Origin is Device.",
|
||||
MessageType.Info);
|
||||
EditorGUILayout.PropertyField(m_CameraHeight);
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
EditorGUILayout.PropertyField(m_ActionAsset);
|
||||
#endif
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
if (UnityEngine.GUI.changed)
|
||||
EditorUtility.SetDirty((VIVERig)target);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4766014dc7f94c8468710cc3fd265f90
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.htc.upm.vive.openxr/Runtime/Features.meta
Normal file
8
Packages/com.htc.upm.vive.openxr/Runtime/Features.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 159d937d6a1834e4caf20cc3de582e49
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 673b5df0bff21a84c8b23a4f3c6a6268
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 046b5fd65fa996041a970e1fd193d213
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,180 @@
|
||||
# XR_HTC_anchor XR_HTC_anchor_persistence
|
||||
## Name String
|
||||
XR_htc_anchor XR_HTC_anchor_persistence
|
||||
## Revision
|
||||
1
|
||||
## Overview
|
||||
|
||||
This document provides an overview of how to use the AnchorManager to manage anchors in an OpenXR application, specifically using the XR_HTC_anchor and XR_HTC_anchor_persistence extensions.
|
||||
Introduction
|
||||
|
||||
Anchors in OpenXR allow applications to track specific points in space over time. The XR_HTC_anchor extension provides the basic functionality for creating and managing anchors, while the XR_HTC_anchor_persistence extension allows anchors to be persisted across sessions. The AnchorManager class simplifies the use of these extensions by providing high-level methods for common operations.
|
||||
Checking Extension Support
|
||||
|
||||
Before using any anchor-related functions, it's important to check if the extensions are supported on the current system.
|
||||
|
||||
```csharp
|
||||
|
||||
bool isAnchorSupported = AnchorManager.IsSupported();
|
||||
bool isPersistedAnchorSupported = AnchorManager.IsPersistedAnchorSupported();
|
||||
|
||||
```
|
||||
|
||||
## Creating and Managing Anchors
|
||||
### Creating an Anchor
|
||||
|
||||
To create a new anchor, use the CreateAnchor method. This method requires a Pose representing the anchor's position and orientation relative to the tracking space, and a name for the anchor.
|
||||
|
||||
```csharp
|
||||
|
||||
Pose anchorPose = new Pose(new Vector3(0, 0, 0), Quaternion.identity);
|
||||
AnchorManager.Anchor newAnchor = AnchorManager.CreateAnchor(anchorPose, "MyAnchor");
|
||||
|
||||
```
|
||||
|
||||
### Getting an Anchor's Name
|
||||
|
||||
To retrieve the name of an existing anchor, use the GetSpatialAnchorName method.
|
||||
|
||||
```csharp
|
||||
|
||||
string anchorName;
|
||||
bool success = AnchorManager.GetSpatialAnchorName(newAnchor, out anchorName);
|
||||
if (success) {
|
||||
Debug.Log("Anchor name: " + anchorName);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Tracking Space and Pose
|
||||
|
||||
To get the current tracking space, use the GetTrackingSpace method. To retrieve the pose of an anchor relative to the current tracking space, use the GetTrackingSpacePose method.
|
||||
|
||||
```csharp
|
||||
|
||||
XrSpace trackingSpace = AnchorManager.GetTrackingSpace();
|
||||
Pose anchorPose;
|
||||
bool poseValid = AnchorManager.GetTrackingSpacePose(newAnchor, out anchorPose);
|
||||
if (poseValid) {
|
||||
Debug.Log("Anchor pose: " + anchorPose.position + ", " + anchorPose.rotation);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Persisting Anchors
|
||||
### Creating a Persisted Anchor Collection
|
||||
|
||||
To enable anchor persistence, create a persisted anchor collection using the CreatePersistedAnchorCollection method.
|
||||
|
||||
```csharp
|
||||
|
||||
Task createCollectionTask = AnchorManager.CreatePersistedAnchorCollection();
|
||||
createCollectionTask.Wait();
|
||||
|
||||
```
|
||||
|
||||
### Persisting an Anchor
|
||||
|
||||
To persist an anchor, use the PersistAnchor method with the anchor and a unique name for the persisted anchor.
|
||||
|
||||
```csharp
|
||||
|
||||
string persistedAnchorName = "MyPersistedAnchor";
|
||||
XrResult result = AnchorManager.PersistAnchor(newAnchor, persistedAnchorName);
|
||||
if (result == XrResult.XR_SUCCESS) {
|
||||
Debug.Log("Anchor persisted successfully.");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Unpersisting an Anchor
|
||||
|
||||
To remove a persisted anchor, use the UnpersistAnchor method with the name of the persisted anchor.
|
||||
|
||||
```csharp
|
||||
|
||||
XrResult result = AnchorManager.UnpersistAnchor(persistedAnchorName);
|
||||
if (result == XrResult.XR_SUCCESS) {
|
||||
Debug.Log("Anchor unpersisted successfully.");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Enumerating Persisted Anchors
|
||||
|
||||
To get a list of all persisted anchors, use the EnumeratePersistedAnchorNames method.
|
||||
|
||||
```csharp
|
||||
|
||||
string[] persistedAnchorNames;
|
||||
XrResult result = AnchorManager.EnumeratePersistedAnchorNames(out persistedAnchorNames);
|
||||
if (result == XrResult.XR_SUCCESS) {
|
||||
foreach (var name in persistedAnchorNames) {
|
||||
Debug.Log("Persisted anchor: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Creating an Anchor from a Persisted Anchor
|
||||
|
||||
To create an anchor from a persisted anchor, use the CreateSpatialAnchorFromPersistedAnchor method.
|
||||
|
||||
```csharp
|
||||
|
||||
AnchorManager.Anchor trackableAnchor;
|
||||
XrResult result = AnchorManager.CreateSpatialAnchorFromPersistedAnchor(persistedAnchorName, "NewAnchor", out trackableAnchor);
|
||||
if (result == XrResult.XR_SUCCESS) {
|
||||
Debug.Log("Anchor created from persisted anchor.");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Exporting and Importing Persisted Anchors
|
||||
### Exporting a Persisted Anchor
|
||||
|
||||
To export a persisted anchor to a buffer, use the ExportPersistedAnchor method.
|
||||
|
||||
```csharp
|
||||
|
||||
Task<(XrResult, string, byte[])> exportTask = AnchorManager.ExportPersistedAnchor(persistedAnchorName);
|
||||
exportTask.Wait();
|
||||
var (exportResult, exportName, buffer) = exportTask.Result;
|
||||
if (exportResult == XrResult.XR_SUCCESS) {
|
||||
// Save buffer to a file or use as needed
|
||||
File.WriteAllBytes("anchor.pa", buffer);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Importing a Persisted Anchor
|
||||
|
||||
To import a persisted anchor from a buffer, use the ImportPersistedAnchor method.
|
||||
|
||||
```csharp
|
||||
|
||||
byte[] buffer = File.ReadAllBytes("anchor.pa");
|
||||
Task<XrResult> importTask = AnchorManager.ImportPersistedAnchor(buffer);
|
||||
importTask.Wait();
|
||||
if (importTask.Result == XrResult.XR_SUCCESS) {
|
||||
Debug.Log("Anchor imported successfully.");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Clearing Persisted Anchors
|
||||
|
||||
To clear all persisted anchors, use the ClearPersistedAnchors method.
|
||||
|
||||
```csharp
|
||||
|
||||
XrResult result = AnchorManager.ClearPersistedAnchors();
|
||||
if (result == XrResult.XR_SUCCESS) {
|
||||
Debug.Log("All persisted anchors cleared.");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
The AnchorManager class simplifies the management of anchors in OpenXR applications. By using the methods provided, you can easily create, persist, and manage anchors, ensuring that spatial data can be maintained across sessions. This document covers the basic operations; for more advanced usage, refer to the OpenXR specification and the implementation details of the AnchorManager class.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69ae1c3151561af42ba226f0e563ebc6
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5cbfbcf56aaffa4fab38659c00c3903
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,675 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
// Remove FAKE_DATA if editor or windows is supported.
|
||||
#if UNITY_EDITOR
|
||||
#define FAKE_DATA
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.Feature
|
||||
{
|
||||
using XrPersistedAnchorCollectionHTC = System.IntPtr;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Anchor (Beta)",
|
||||
Desc = "VIVE's implementaion of the XR_HTC_anchor.",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class ViveAnchor : OpenXRFeature
|
||||
{
|
||||
public const string kOpenxrExtensionString = "XR_HTC_anchor XR_EXT_future XR_HTC_anchor_persistence";
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.htcanchor";
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable the persisted anchor feature. Set it only valid in feature settings.
|
||||
/// </summary>
|
||||
public bool enablePersistedAnchor = true;
|
||||
private XrInstance m_XrInstance = 0;
|
||||
private XrSession session = 0;
|
||||
private XrSystemId m_XrSystemId = 0;
|
||||
private bool IsInited = false;
|
||||
private bool IsPAInited = false;
|
||||
private bool useFakeData = false;
|
||||
|
||||
#region struct, enum, const of this extensions
|
||||
|
||||
/// <summary>
|
||||
/// An application can inspect whether the system is capable of anchor functionality by
|
||||
/// chaining an XrSystemAnchorPropertiesHTC structure to the XrSystemProperties when calling
|
||||
/// xrGetSystemProperties.The runtime must return XR_ERROR_FEATURE_UNSUPPORTED if
|
||||
/// XrSystemAnchorPropertiesHTC::supportsAnchor was XR_FALSE.
|
||||
/// supportsAnchor indicates if current system is capable of anchor functionality.
|
||||
/// </summary>
|
||||
public struct XrSystemAnchorPropertiesHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrBool32 supportsAnchor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// name is a null-terminated UTF-8 string whose length is less than or equal to XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrSpatialAnchorNameHTC
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
|
||||
public byte[] name;
|
||||
|
||||
public XrSpatialAnchorNameHTC(string anchorName)
|
||||
{
|
||||
name = new byte[256];
|
||||
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(anchorName);
|
||||
Array.Copy(utf8Bytes, name, Math.Min(utf8Bytes.Length, 255));
|
||||
name[255] = 0;
|
||||
}
|
||||
|
||||
public XrSpatialAnchorNameHTC(XrSpatialAnchorNameHTC anchorName)
|
||||
{
|
||||
name = new byte[256];
|
||||
Array.Copy(anchorName.name, name, 256);
|
||||
name[255] = 0;
|
||||
}
|
||||
|
||||
public override readonly string ToString() {
|
||||
if (name == null)
|
||||
return string.Empty;
|
||||
return System.Text.Encoding.UTF8.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
}
|
||||
|
||||
public struct XrSpatialAnchorCreateInfoHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrSpace space;
|
||||
public XrPosef poseInSpace;
|
||||
public XrSpatialAnchorNameHTC name;
|
||||
}
|
||||
|
||||
public struct XrPersistedAnchorCollectionAcquireInfoHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
}
|
||||
|
||||
public struct XrPersistedAnchorCollectionAcquireCompletionHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrResult futureResult;
|
||||
public System.IntPtr persistedAnchorCollection;
|
||||
}
|
||||
|
||||
public struct XrSpatialAnchorPersistInfoHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrSpace anchor;
|
||||
public XrSpatialAnchorNameHTC persistedAnchorName;
|
||||
}
|
||||
|
||||
public struct XrSpatialAnchorFromPersistedAnchorCreateInfoHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public System.IntPtr persistedAnchorCollection;
|
||||
public XrSpatialAnchorNameHTC persistedAnchorName;
|
||||
public XrSpatialAnchorNameHTC spatialAnchorName;
|
||||
}
|
||||
|
||||
public struct XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrResult futureResult;
|
||||
public XrSpace anchor;
|
||||
}
|
||||
|
||||
public struct XrPersistedAnchorPropertiesGetInfoHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public uint maxPersistedAnchorCount;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region delegates and delegate instances
|
||||
public delegate XrResult DelegateXrCreateSpatialAnchorHTC(XrSession session, ref XrSpatialAnchorCreateInfoHTC createInfo, ref XrSpace anchor);
|
||||
public delegate XrResult DelegateXrGetSpatialAnchorNameHTC(XrSpace anchor, ref XrSpatialAnchorNameHTC name);
|
||||
public delegate XrResult DelegateXrAcquirePersistedAnchorCollectionAsyncHTC(XrSession session, ref XrPersistedAnchorCollectionAcquireInfoHTC acquireInfo, out IntPtr future);
|
||||
public delegate XrResult DelegateXrAcquirePersistedAnchorCollectionCompleteHTC(IntPtr future, out XrPersistedAnchorCollectionAcquireCompletionHTC completion);
|
||||
public delegate XrResult DelegateXrReleasePersistedAnchorCollectionHTC(IntPtr persistedAnchorCollection);
|
||||
public delegate XrResult DelegateXrPersistSpatialAnchorAsyncHTC(XrPersistedAnchorCollectionHTC persistedAnchorCollection, ref XrSpatialAnchorPersistInfoHTC persistInfo, out IntPtr future);
|
||||
public delegate XrResult DelegateXrPersistSpatialAnchorCompleteHTC(IntPtr future, out FutureWrapper.XrFutureCompletionEXT completion);
|
||||
public delegate XrResult DelegateXrUnpersistSpatialAnchorHTC(IntPtr persistedAnchorCollection, ref XrSpatialAnchorNameHTC persistedAnchorName);
|
||||
public delegate XrResult DelegateXrEnumeratePersistedAnchorNamesHTC( IntPtr persistedAnchorCollection, uint persistedAnchorNameCapacityInput, ref uint persistedAnchorNameCountOutput, [Out] XrSpatialAnchorNameHTC[] persistedAnchorNames);
|
||||
public delegate XrResult DelegateXrCreateSpatialAnchorFromPersistedAnchorAsyncHTC(XrSession session, ref XrSpatialAnchorFromPersistedAnchorCreateInfoHTC spatialAnchorCreateInfo, out IntPtr future);
|
||||
public delegate XrResult DelegateXrCreateSpatialAnchorFromPersistedAnchorCompleteHTC(IntPtr future, out XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC completion);
|
||||
public delegate XrResult DelegateXrClearPersistedAnchorsHTC(IntPtr persistedAnchorCollection);
|
||||
public delegate XrResult DelegateXrGetPersistedAnchorPropertiesHTC(IntPtr persistedAnchorCollection, ref XrPersistedAnchorPropertiesGetInfoHTC getInfo);
|
||||
public delegate XrResult DelegateXrExportPersistedAnchorHTC(IntPtr persistedAnchorCollection, ref XrSpatialAnchorNameHTC persistedAnchorName, uint dataCapacityInput, ref uint dataCountOutput, [Out] byte[] data);
|
||||
public delegate XrResult DelegateXrImportPersistedAnchorHTC(IntPtr persistedAnchorCollection, uint dataCount, [In] byte[] data);
|
||||
public delegate XrResult DelegateXrGetPersistedAnchorNameFromBufferHTC(IntPtr persistedAnchorCollection, uint bufferCount, byte[] buffer, ref XrSpatialAnchorNameHTC name);
|
||||
|
||||
DelegateXrCreateSpatialAnchorHTC XrCreateSpatialAnchorHTC;
|
||||
DelegateXrGetSpatialAnchorNameHTC XrGetSpatialAnchorNameHTC;
|
||||
DelegateXrAcquirePersistedAnchorCollectionAsyncHTC XrAcquirePersistedAnchorCollectionAsyncHTC;
|
||||
DelegateXrAcquirePersistedAnchorCollectionCompleteHTC XrAcquirePersistedAnchorCollectionCompleteHTC;
|
||||
DelegateXrReleasePersistedAnchorCollectionHTC XrReleasePersistedAnchorCollectionHTC;
|
||||
DelegateXrPersistSpatialAnchorAsyncHTC XrPersistSpatialAnchorAsyncHTC;
|
||||
DelegateXrPersistSpatialAnchorCompleteHTC XrPersistSpatialAnchorCompleteHTC;
|
||||
DelegateXrUnpersistSpatialAnchorHTC XrUnpersistSpatialAnchorHTC;
|
||||
DelegateXrEnumeratePersistedAnchorNamesHTC XrEnumeratePersistedAnchorNamesHTC;
|
||||
DelegateXrCreateSpatialAnchorFromPersistedAnchorAsyncHTC XrCreateSpatialAnchorFromPersistedAnchorAsyncHTC;
|
||||
DelegateXrCreateSpatialAnchorFromPersistedAnchorCompleteHTC XrCreateSpatialAnchorFromPersistedAnchorCompleteHTC;
|
||||
DelegateXrClearPersistedAnchorsHTC XrClearPersistedAnchorsHTC;
|
||||
DelegateXrGetPersistedAnchorPropertiesHTC XrGetPersistedAnchorPropertiesHTC;
|
||||
DelegateXrExportPersistedAnchorHTC XrExportPersistedAnchorHTC;
|
||||
DelegateXrImportPersistedAnchorHTC XrImportPersistedAnchorHTC;
|
||||
DelegateXrGetPersistedAnchorNameFromBufferHTC XrGetPersistedAnchorNameFromBufferHTC;
|
||||
|
||||
#endregion delegates and delegate instances
|
||||
|
||||
#region override functions
|
||||
|
||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
// For LocateSpace, need WaitFrame's predictedDisplayTime.
|
||||
ViveInterceptors.Instance.AddRequiredFunction("xrWaitFrame");
|
||||
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
#if FAKE_DATA
|
||||
Debug.LogError("ViveAnchor OnInstanceCreate() Use FakeData");
|
||||
useFakeData = true;
|
||||
#endif
|
||||
IsInited = false;
|
||||
bool ret = true;
|
||||
ret &= CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
||||
ret &= SpaceWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
Debug.LogError("ViveAnchor OnInstanceCreate() failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Debug.Log("VIVEAnchor OnInstanceCreate() ");
|
||||
if (!OpenXRRuntime.IsExtensionEnabled("XR_HTC_anchor") && !useFakeData)
|
||||
{
|
||||
Debug.LogWarning("ViveAnchor OnInstanceCreate() XR_HTC_anchor is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
IsInited = GetXrFunctionDelegates(xrInstance);
|
||||
|
||||
if (!IsInited)
|
||||
{
|
||||
Debug.LogError("ViveAnchor OnInstanceCreate() failed to get function delegates.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_XrInstance = xrInstance;
|
||||
|
||||
bool hasFuture = FutureWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
||||
// No error log because future will print.
|
||||
#if FAKE_DATA
|
||||
hasFuture = true;
|
||||
#endif
|
||||
IsPAInited = false;
|
||||
bool hasPersistedAnchor = false;
|
||||
do
|
||||
{
|
||||
if (!hasFuture)
|
||||
{
|
||||
Debug.LogWarning("ViveAnchor OnInstanceCreate() XR_HTC_anchor_persistence is NOT enabled because no XR_EXT_future.");
|
||||
hasPersistedAnchor = false;
|
||||
break;
|
||||
}
|
||||
|
||||
hasPersistedAnchor = enablePersistedAnchor && OpenXRRuntime.IsExtensionEnabled("XR_HTC_anchor_persistence");
|
||||
#if FAKE_DATA
|
||||
hasPersistedAnchor = enablePersistedAnchor;
|
||||
#endif
|
||||
} while(false);
|
||||
|
||||
//Debug.Log("OnInstanceCreate() " + m_XrInstance);
|
||||
if (hasPersistedAnchor)
|
||||
IsPAInited = GetXrFunctionDelegatesPersistance(xrInstance);
|
||||
if (!IsPAInited)
|
||||
Debug.LogWarning("ViveAnchor OnInstanceCreate() XR_HTC_anchor_persistence is NOT enabled.");
|
||||
|
||||
return IsInited;
|
||||
}
|
||||
|
||||
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||
{
|
||||
m_XrInstance = 0;
|
||||
|
||||
IsInited = false;
|
||||
IsPAInited = false;
|
||||
|
||||
CommonWrapper.Instance.OnInstanceDestroy();
|
||||
SpaceWrapper.Instance.OnInstanceDestroy();
|
||||
FutureWrapper.Instance.OnInstanceDestroy();
|
||||
Debug.Log("ViveAnchor: OnInstanceDestroy()");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionCreate(ulong xrSession)
|
||||
{
|
||||
//Debug.Log("ViveAnchor OnSessionCreate() ");
|
||||
session = xrSession;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionDestroy(ulong xrSession)
|
||||
{
|
||||
//Debug.Log("ViveAnchor OnSessionDestroy() ");
|
||||
session = 0;
|
||||
}
|
||||
|
||||
// XXX Every millisecond the AppSpace switched from one space to another space. I don't know what is going on.
|
||||
//private ulong appSpace;
|
||||
//protected override void OnAppSpaceChange(ulong space)
|
||||
//{
|
||||
// //Debug.Log($"VIVEAnchor OnAppSpaceChange({appSpace} -> {space})");
|
||||
// appSpace = space;
|
||||
//}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSystemChange(ulong xrSystem)
|
||||
{
|
||||
m_XrSystemId = xrSystem;
|
||||
//Debug.Log("ViveAnchor OnSystemChange() " + m_XrSystemId);
|
||||
}
|
||||
|
||||
#endregion override functions
|
||||
|
||||
private bool GetXrFunctionDelegates(XrInstance inst)
|
||||
{
|
||||
Debug.Log("ViveAnchor GetXrFunctionDelegates() ");
|
||||
|
||||
bool ret = true;
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrCreateSpatialAnchorHTC", out XrCreateSpatialAnchorHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrGetSpatialAnchorNameHTC", out XrGetSpatialAnchorNameHTC);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool GetXrFunctionDelegatesPersistance(XrInstance inst)
|
||||
{
|
||||
Debug.Log("ViveAnchor GetXrFunctionDelegatesPersistance() ");
|
||||
bool ret = true;
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrAcquirePersistedAnchorCollectionAsyncHTC", out XrAcquirePersistedAnchorCollectionAsyncHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrAcquirePersistedAnchorCollectionCompleteHTC", out XrAcquirePersistedAnchorCollectionCompleteHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrReleasePersistedAnchorCollectionHTC", out XrReleasePersistedAnchorCollectionHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrPersistSpatialAnchorAsyncHTC", out XrPersistSpatialAnchorAsyncHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrPersistSpatialAnchorCompleteHTC", out XrPersistSpatialAnchorCompleteHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrUnpersistSpatialAnchorHTC", out XrUnpersistSpatialAnchorHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrEnumeratePersistedAnchorNamesHTC", out XrEnumeratePersistedAnchorNamesHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrCreateSpatialAnchorFromPersistedAnchorAsyncHTC", out XrCreateSpatialAnchorFromPersistedAnchorAsyncHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrCreateSpatialAnchorFromPersistedAnchorCompleteHTC", out XrCreateSpatialAnchorFromPersistedAnchorCompleteHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrClearPersistedAnchorsHTC", out XrClearPersistedAnchorsHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrGetPersistedAnchorPropertiesHTC", out XrGetPersistedAnchorPropertiesHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrExportPersistedAnchorHTC", out XrExportPersistedAnchorHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrImportPersistedAnchorHTC", out XrImportPersistedAnchorHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, inst, "xrGetPersistedAnchorNameFromBufferHTC", out XrGetPersistedAnchorNameFromBufferHTC);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#region functions of extension
|
||||
/// <summary>
|
||||
/// Helper function to get this feature's properties.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||
/// </summary>
|
||||
/// <param name="anchorProperties">Output parameter to hold anchor properties.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult GetProperties(out XrSystemAnchorPropertiesHTC anchorProperties)
|
||||
{
|
||||
anchorProperties = new XrSystemAnchorPropertiesHTC();
|
||||
anchorProperties.type = XrStructureType.XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC;
|
||||
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
{
|
||||
anchorProperties.type = XrStructureType.XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC;
|
||||
anchorProperties.supportsAnchor = true;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return CommonWrapper.Instance.GetProperties(m_XrInstance, m_XrSystemId, ref anchorProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The CreateSpatialAnchor function creates a spatial anchor with specified base space and pose in the space.
|
||||
/// The anchor is represented by an XrSpace and its pose can be tracked via xrLocateSpace.
|
||||
/// Once the anchor is no longer needed, call xrDestroySpace to erase the anchor.
|
||||
/// </summary>
|
||||
/// <param name="createInfo">Information required to create the spatial anchor.</param>
|
||||
/// <param name="anchor">Output parameter to hold the created anchor.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult CreateSpatialAnchor(XrSpatialAnchorCreateInfoHTC createInfo, out XrSpace anchor)
|
||||
{
|
||||
anchor = default;
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
if (session == 0)
|
||||
return XrResult.XR_ERROR_SESSION_LOST;
|
||||
|
||||
var ret = XrCreateSpatialAnchorHTC(session, ref createInfo, ref anchor);
|
||||
//Debug.Log("ViveAnchor CreateSpatialAnchor() r=" + ret + ", a=" + anchor + ", bs=" + createInfo.space +
|
||||
// ", pos=(" + createInfo.poseInSpace.position.x + "," + createInfo.poseInSpace.position.y + "," + createInfo.poseInSpace.position.z +
|
||||
// "), rot=(" + createInfo.poseInSpace.orientation.x + "," + createInfo.poseInSpace.orientation.y + "," + createInfo.poseInSpace.orientation.z + "," + createInfo.poseInSpace.orientation.w +
|
||||
// "), n=" + createInfo.name.name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The GetSpatialAnchorName function retrieves the name of the spatial anchor.
|
||||
/// </summary>
|
||||
/// <param name="anchor">The XrSpace representing the anchor.</param>
|
||||
/// <param name="name">Output parameter to hold the name of the anchor.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult GetSpatialAnchorName(XrSpace anchor, out XrSpatialAnchorNameHTC name)
|
||||
{
|
||||
name = new XrSpatialAnchorNameHTC();
|
||||
if (!IsInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
return XrGetSpatialAnchorNameHTC(anchor, ref name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the extension is supported and enabled, return true.
|
||||
/// </summary>
|
||||
/// <returns>True if persisted anchor extension is supported, false otherwise.</returns>
|
||||
public bool IsPersistedAnchorSupported()
|
||||
{
|
||||
return IsPAInited;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a persisted anchor collection. This collection can be used to persist spatial anchors across sessions.
|
||||
/// Many persisted anchor APIs need a persisted anchor collection to operate.
|
||||
/// </summary>
|
||||
/// <param name="future">Output the async future handle. Check the future to get the PersitedAnchorCollection handle.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult AcquirePersistedAnchorCollectionAsync(out IntPtr future)
|
||||
{
|
||||
future = IntPtr.Zero;
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
if (session == 0)
|
||||
return XrResult.XR_ERROR_SESSION_LOST;
|
||||
|
||||
XrPersistedAnchorCollectionAcquireInfoHTC acquireInfo = new XrPersistedAnchorCollectionAcquireInfoHTC
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_PERSISTED_ANCHOR_COLLECTION_ACQUIRE_INFO_HTC,
|
||||
next = IntPtr.Zero,
|
||||
};
|
||||
|
||||
return XrAcquirePersistedAnchorCollectionAsyncHTC(session, ref acquireInfo, out future);
|
||||
}
|
||||
|
||||
public XrResult AcquirePersistedAnchorCollectionComplete(IntPtr future, out XrPersistedAnchorCollectionAcquireCompletionHTC completion)
|
||||
{
|
||||
completion = new XrPersistedAnchorCollectionAcquireCompletionHTC();
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
return XrAcquirePersistedAnchorCollectionCompleteHTC(future, out completion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the persisted anchor collection.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to be destroyed.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult ReleasePersistedAnchorCollection(IntPtr persistedAnchorCollection)
|
||||
{
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
return XrReleasePersistedAnchorCollectionHTC(persistedAnchorCollection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persists a spatial anchor with the given name. The name should be unique.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
|
||||
/// <param name="anchor">The spatial anchor to be persisted.</param>
|
||||
/// <param name="name">The name of the persisted anchor.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult PersistSpatialAnchorAsync(IntPtr persistedAnchorCollection, XrSpace anchor, XrSpatialAnchorNameHTC name, out IntPtr future)
|
||||
{
|
||||
future = IntPtr.Zero;
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
XrSpatialAnchorPersistInfoHTC persistInfo = new XrSpatialAnchorPersistInfoHTC
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_HTC,
|
||||
anchor = anchor,
|
||||
persistedAnchorName = name
|
||||
};
|
||||
return XrPersistSpatialAnchorAsyncHTC(persistedAnchorCollection, ref persistInfo, out future);
|
||||
}
|
||||
|
||||
public XrResult PersistSpatialAnchorComplete(IntPtr future, out FutureWrapper.XrFutureCompletionEXT completion)
|
||||
{
|
||||
completion = new FutureWrapper.XrFutureCompletionEXT() {
|
||||
type = XrStructureType.XR_TYPE_FUTURE_COMPLETION_EXT,
|
||||
next = IntPtr.Zero,
|
||||
futureResult = XrResult.XR_SUCCESS
|
||||
};
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
return XrPersistSpatialAnchorCompleteHTC(future, out completion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpersists the anchor with the given name.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
|
||||
/// <param name="name">The name of the anchor to be unpersisted.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult UnpersistSpatialAnchor(IntPtr persistedAnchorCollection, XrSpatialAnchorNameHTC name)
|
||||
{
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
return XrUnpersistSpatialAnchorHTC(persistedAnchorCollection, ref name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates all persisted anchor names.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
|
||||
/// <param name="persistedAnchorNameCapacityInput">The capacity of the input buffer.</param>
|
||||
/// <param name="persistedAnchorNameCountOutput">Output parameter to hold the count of persisted anchor names.</param>
|
||||
/// <param name="persistedAnchorNames">Output parameter to hold the names of persisted anchors.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult EnumeratePersistedAnchorNames(IntPtr persistedAnchorCollection, uint persistedAnchorNameCapacityInput,
|
||||
ref uint persistedAnchorNameCountOutput, ref XrSpatialAnchorNameHTC[] persistedAnchorNames)
|
||||
{
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
return XrEnumeratePersistedAnchorNamesHTC(persistedAnchorCollection, persistedAnchorNameCapacityInput, ref persistedAnchorNameCountOutput, persistedAnchorNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spatial anchor from a persisted anchor.
|
||||
/// </summary>
|
||||
/// <param name="spatialAnchorCreateInfo">Information required to create the spatial anchor from persisted anchor.</param>
|
||||
/// <param name="anchor">Output parameter to hold the created spatial anchor.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult CreateSpatialAnchorFromPersistedAnchorAsync(XrSpatialAnchorFromPersistedAnchorCreateInfoHTC spatialAnchorCreateInfo, out IntPtr future)
|
||||
{
|
||||
future = IntPtr.Zero;
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
if (session == 0)
|
||||
return XrResult.XR_ERROR_SESSION_LOST;
|
||||
return XrCreateSpatialAnchorFromPersistedAnchorAsyncHTC(session, ref spatialAnchorCreateInfo, out future);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the future is ready, call this function to get the result.
|
||||
/// </summary>
|
||||
/// <param name="future"></param>
|
||||
/// <param name="completion"></param>
|
||||
/// <returns></returns>
|
||||
public XrResult CreateSpatialAnchorFromPersistedAnchorComplete(IntPtr future, out XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC completion)
|
||||
{
|
||||
completion = new XrSpatialAnchorFromPersistedAnchorCreateCompletionHTC()
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_COMPLETION_HTC,
|
||||
next = IntPtr.Zero,
|
||||
futureResult = XrResult.XR_SUCCESS,
|
||||
anchor = 0
|
||||
};
|
||||
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
return XrCreateSpatialAnchorFromPersistedAnchorCompleteHTC(future, out completion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all persisted anchors.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult ClearPersistedAnchors(IntPtr persistedAnchorCollection)
|
||||
{
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
return XrClearPersistedAnchorsHTC(persistedAnchorCollection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the properties of the persisted anchor.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
|
||||
/// <param name="getInfo">Output parameter to hold the properties of the persisted anchor.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult GetPersistedAnchorProperties(IntPtr persistedAnchorCollection, out XrPersistedAnchorPropertiesGetInfoHTC getInfo)
|
||||
{
|
||||
getInfo = new XrPersistedAnchorPropertiesGetInfoHTC
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_PERSISTED_ANCHOR_PROPERTIES_GET_INFO_HTC
|
||||
};
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
return XrGetPersistedAnchorPropertiesHTC(persistedAnchorCollection, ref getInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports the persisted anchor to a buffer. The buffer can be used to import the anchor later or save to a file.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
|
||||
/// <param name="persistedAnchorName">The name of the persisted anchor to be exported.</param>
|
||||
/// <param name="data">Output parameter to hold the buffer containing the exported anchor.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult ExportPersistedAnchor(IntPtr persistedAnchorCollection, XrSpatialAnchorNameHTC persistedAnchorName, out byte[] data)
|
||||
{
|
||||
data = null;
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
uint dataCountOutput = 0;
|
||||
uint dataCapacityInput = 0;
|
||||
XrResult ret = XrExportPersistedAnchorHTC(persistedAnchorCollection, ref persistedAnchorName, dataCapacityInput, ref dataCountOutput, null);
|
||||
if (ret != XrResult.XR_SUCCESS)
|
||||
{
|
||||
Debug.LogError("ExportPersistedAnchor failed to get data size. ret=" + ret);
|
||||
data = null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dataCapacityInput = dataCountOutput;
|
||||
data = new byte[dataCountOutput];
|
||||
ret = XrExportPersistedAnchorHTC(persistedAnchorCollection, ref persistedAnchorName, dataCapacityInput, ref dataCountOutput, data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the persisted anchor from a buffer. The buffer should be created by ExportPersistedAnchor.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection">The persisted anchor collection to operate.</param>
|
||||
/// <param name="data">The buffer containing the persisted anchor data.</param>
|
||||
/// <returns>XrResult indicating success or failure.</returns>
|
||||
public XrResult ImportPersistedAnchor(IntPtr persistedAnchorCollection, byte[] data)
|
||||
{
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
return XrImportPersistedAnchorHTC(persistedAnchorCollection, (uint)data.Length, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the persisted anchor from a buffer. The buffer should be created by ExportPersistedAnchor.
|
||||
/// </summary>
|
||||
/// <param name="persistedAnchorCollection"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public XrResult GetPersistedAnchorNameFromBuffer(IntPtr persistedAnchorCollection, byte[] buffer, out XrSpatialAnchorNameHTC name)
|
||||
{
|
||||
name = new XrSpatialAnchorNameHTC();
|
||||
if (!IsPAInited)
|
||||
return XrResult.XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||
|
||||
if (buffer == null)
|
||||
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||
|
||||
return XrGetPersistedAnchorNameFromBufferHTC(persistedAnchorCollection, (uint)buffer.Length, buffer, ref name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region tools for user
|
||||
|
||||
/// <summary>
|
||||
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XrSpace GetTrackingSpace()
|
||||
{
|
||||
var s = GetCurrentAppSpace();
|
||||
//Debug.Log("ViveAnchor GetTrackingSpace() s=" + s);
|
||||
return s;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c17aa731a6f4fb54bb9a2c28df667e5e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4126580e0f3096546977cff4f5203eb1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1850b50609fc1db458be9f8f11c62852
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
# 12.7. XR_KHR_composition_layer_cylinder
|
||||
## Name String
|
||||
XR_KHR_composition_layer_cylinder
|
||||
## Revision
|
||||
4
|
||||
## New Object Types
|
||||
## New Enum Constants
|
||||
[XrStructureType](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XrStructureType) enumeration is extended with:
|
||||
- XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR
|
||||
## New Enums
|
||||
## New Structures
|
||||
- [XrCompositionLayerCylinderKHR ](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XrCompositionLayerCylinderKHR)
|
||||
## New Functions
|
||||
|
||||
## VIVE Plugin
|
||||
|
||||
Enable "VIVE XR Composition Layer" in "Project Settings > XR Plugin-in Management > OpenXR > Android Tab > OpenXR Feature Groups" in order to use the Composition Layer feature provided by the VIVE plugin.
|
||||
|
||||
The plugin provides scripts and resources for setting up Composition Layers quickly and easily.
|
||||
There are two scripts which can be attached to **GameObjects**:
|
||||
- CompositionLayer: For setting up a basic Composition Layer. Located at: *\<VIVE OpenXR path\>/Runtime/CompositionLayer/Scripts/CompositionLayer.cs*
|
||||
- CompositionLayerUICanvas: For setting up a Composition Layer which renders a Unity UI Canvas. Located at: *\<VIVE OpenXR path\>/Runtime/CompositionLayer/Scripts/CompositionLayerUICanvas.cs*
|
||||
|
||||
To see how the two scripts above can be used, refer to the samples located at: *\<VIVE OpenXR sample path\>/CompositionLayer/Scenes*
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbab5ef182d5d34438925945775e2443
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f29d9246fa53ed5469160eb0ec165fb8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,570 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using AOT;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Composition Layer",
|
||||
Desc = "Enable this feature to use the Composition Layer.",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenxrExtensionStrings,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class ViveCompositionLayer : OpenXRFeature
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayer";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
/// <summary>
|
||||
/// Enable auto fallback or Not.
|
||||
/// </summary>
|
||||
public bool enableAutoFallback = false;
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.compositionlayer";
|
||||
|
||||
public const string kOpenxrExtensionStrings = "";
|
||||
|
||||
#region OpenXR Life Cycle
|
||||
private bool m_XrInstanceCreated = false;
|
||||
/// <summary>
|
||||
/// The XR instance is created or not.
|
||||
/// </summary>
|
||||
public bool XrInstanceCreated
|
||||
{
|
||||
get { return m_XrInstanceCreated; }
|
||||
}
|
||||
private XrInstance m_XrInstance = 0;
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
foreach (string kOpenxrExtensionString in kOpenxrExtensionStrings.Split(' '))
|
||||
{
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||
{
|
||||
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
m_XrInstanceCreated = true;
|
||||
m_XrInstance = xrInstance;
|
||||
DEBUG("OnInstanceCreate() " + m_XrInstance);
|
||||
|
||||
return GetXrFunctionDelegates(m_XrInstance);
|
||||
}
|
||||
|
||||
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||
{
|
||||
m_XrInstanceCreated = false;
|
||||
DEBUG("OnInstanceDestroy() " + m_XrInstance);
|
||||
}
|
||||
|
||||
private XrSystemId m_XrSystemId = 0;
|
||||
protected override void OnSystemChange(ulong xrSystem)
|
||||
{
|
||||
m_XrSystemId = xrSystem;
|
||||
DEBUG("OnSystemChange() " + m_XrSystemId);
|
||||
}
|
||||
|
||||
private bool m_XrSessionCreated = false;
|
||||
/// <summary>
|
||||
/// The XR session is created or not.
|
||||
/// </summary>
|
||||
public bool XrSessionCreated
|
||||
{
|
||||
get { return m_XrSessionCreated; }
|
||||
}
|
||||
private XrSession m_XrSession = 0;
|
||||
protected override void OnSessionCreate(ulong xrSession)
|
||||
{
|
||||
m_XrSession = xrSession;
|
||||
m_XrSessionCreated = true;
|
||||
DEBUG("OnSessionCreate() " + m_XrSession);
|
||||
}
|
||||
|
||||
private bool m_XrSessionEnding = false;
|
||||
/// <summary>
|
||||
/// The XR session is ending or not.
|
||||
/// </summary>
|
||||
public bool XrSessionEnding
|
||||
{
|
||||
get { return m_XrSessionEnding; }
|
||||
}
|
||||
|
||||
private XrSpace m_WorldLockSpaceOriginOnHead = 0, m_WorldLockSpaceOriginOnFloor = 0, m_HeadLockSpace = 0;
|
||||
/// <summary>
|
||||
/// The XrSpace of world lock space origin on head.
|
||||
/// </summary>
|
||||
public XrSpace WorldLockSpaceOriginOnHead
|
||||
{
|
||||
get { return m_WorldLockSpaceOriginOnHead; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The XrSpace of world lock space origin on floor.
|
||||
/// </summary>
|
||||
public XrSpace WorldLockSpaceOriginOnFloor
|
||||
{
|
||||
get { return m_WorldLockSpaceOriginOnFloor; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The XrSpace of head lock space.
|
||||
/// </summary>
|
||||
public XrSpace HeadLockSpace
|
||||
{
|
||||
get { return m_HeadLockSpace; }
|
||||
}
|
||||
|
||||
protected override void OnSessionBegin(ulong xrSession)
|
||||
{
|
||||
m_XrSessionEnding = false;
|
||||
DEBUG("OnSessionBegin() " + m_XrSession);
|
||||
|
||||
// Enumerate supported reference space types and create the XrSpace.
|
||||
XrReferenceSpaceType[] spaces = new XrReferenceSpaceType[Enum.GetNames(typeof(XrReferenceSpaceType)).Count()];
|
||||
UInt32 spaceCountOutput;
|
||||
if (EnumerateReferenceSpaces(
|
||||
spaceCapacityInput: 0,
|
||||
spaceCountOutput: out spaceCountOutput,
|
||||
spaces: out spaces[0]) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("spaceCountOutput: " + spaceCountOutput);
|
||||
|
||||
Array.Resize(ref spaces, (int)spaceCountOutput);
|
||||
if (EnumerateReferenceSpaces(
|
||||
spaceCapacityInput: spaceCountOutput,
|
||||
spaceCountOutput: out spaceCountOutput,
|
||||
spaces: out spaces[0]) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL))
|
||||
{
|
||||
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoWorldLock;
|
||||
referenceSpaceCreateInfoWorldLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||
referenceSpaceCreateInfoWorldLock.next = IntPtr.Zero;
|
||||
referenceSpaceCreateInfoWorldLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL;
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
|
||||
|
||||
if (CreateReferenceSpace(
|
||||
createInfo: ref referenceSpaceCreateInfoWorldLock,
|
||||
space: out m_WorldLockSpaceOriginOnHead) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("CreateReferenceSpace: " + m_WorldLockSpaceOriginOnHead);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace for world lock layers on head failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace no space type for world lock on head layers.");
|
||||
}
|
||||
|
||||
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE))
|
||||
{
|
||||
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoWorldLock;
|
||||
referenceSpaceCreateInfoWorldLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||
referenceSpaceCreateInfoWorldLock.next = IntPtr.Zero;
|
||||
referenceSpaceCreateInfoWorldLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE;
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
|
||||
|
||||
if (CreateReferenceSpace(
|
||||
createInfo: ref referenceSpaceCreateInfoWorldLock,
|
||||
space: out m_WorldLockSpaceOriginOnFloor) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("CreateReferenceSpace: " + m_WorldLockSpaceOriginOnFloor);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace for world lock layers on floor failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace no space type for world lock on floor layers.");
|
||||
}
|
||||
|
||||
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_VIEW))
|
||||
{
|
||||
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoHeadLock;
|
||||
referenceSpaceCreateInfoHeadLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||
referenceSpaceCreateInfoHeadLock.next = IntPtr.Zero;
|
||||
referenceSpaceCreateInfoHeadLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_VIEW;
|
||||
referenceSpaceCreateInfoHeadLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
|
||||
referenceSpaceCreateInfoHeadLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
|
||||
|
||||
if (CreateReferenceSpace(
|
||||
createInfo: ref referenceSpaceCreateInfoHeadLock,
|
||||
space: out m_HeadLockSpace) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("CreateReferenceSpace: " + m_HeadLockSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace for head lock layers failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace no space type for head lock layers.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("EnumerateReferenceSpaces(" + spaceCountOutput + ") failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("EnumerateReferenceSpaces(0) failed.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSessionEnd(ulong xrSession)
|
||||
{
|
||||
m_XrSessionEnding = true;
|
||||
DEBUG("OnSessionEnd() " + m_XrSession);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnSessionDestroy(ulong xrSession)
|
||||
{
|
||||
m_XrSessionCreated = false;
|
||||
DEBUG("OnSessionDestroy() " + xrSession);
|
||||
|
||||
if (m_HeadLockSpace != 0)
|
||||
{
|
||||
DestroySpace(m_HeadLockSpace);
|
||||
m_HeadLockSpace = 0;
|
||||
}
|
||||
if (m_WorldLockSpaceOriginOnFloor != 0)
|
||||
{
|
||||
DestroySpace(m_WorldLockSpaceOriginOnFloor);
|
||||
m_WorldLockSpaceOriginOnFloor = 0;
|
||||
}
|
||||
if (m_WorldLockSpaceOriginOnHead != 0)
|
||||
{
|
||||
DestroySpace(m_WorldLockSpaceOriginOnHead);
|
||||
m_WorldLockSpaceOriginOnHead = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current XR Session state.
|
||||
/// </summary>
|
||||
public XrSessionState XrSessionCurrentState
|
||||
{
|
||||
get { return m_XrSessionNewState; }
|
||||
}
|
||||
private XrSessionState m_XrSessionNewState = XrSessionState.XR_SESSION_STATE_UNKNOWN;
|
||||
private XrSessionState m_XrSessionOldState = XrSessionState.XR_SESSION_STATE_UNKNOWN;
|
||||
protected override void OnSessionStateChange(int oldState, int newState)
|
||||
{
|
||||
DEBUG("OnSessionStateChange() oldState: " + oldState + " newState:" + newState);
|
||||
|
||||
if (Enum.IsDefined(typeof(XrSessionState), oldState))
|
||||
{
|
||||
m_XrSessionOldState = (XrSessionState)oldState;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG("OnSessionStateChange() oldState undefined");
|
||||
}
|
||||
|
||||
if (Enum.IsDefined(typeof(XrSessionState), newState))
|
||||
{
|
||||
m_XrSessionNewState = (XrSessionState)newState;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG("OnSessionStateChange() newState undefined");
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OpenXR function delegates
|
||||
/// xrGetInstanceProcAddr
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||
|
||||
/// xrGetSystemProperties
|
||||
OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties;
|
||||
/// <summary>
|
||||
/// Helper function to get this feature' properties.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||
/// </summary>
|
||||
public XrResult GetSystemProperties(ref XrSystemProperties properties)
|
||||
{
|
||||
if (m_XrInstanceCreated)
|
||||
{
|
||||
return xrGetSystemProperties(m_XrInstance, m_XrSystemId, ref properties);
|
||||
}
|
||||
|
||||
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||
}
|
||||
|
||||
/// xrEnumerateReferenceSpaces
|
||||
OpenXRHelper.xrEnumerateReferenceSpacesDelegate xrEnumerateReferenceSpaces;
|
||||
/// <summary>
|
||||
/// Enumerate available reference spaces
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrEnumerateReferenceSpaces.html">xrEnumerateReferenceSpaces</see>
|
||||
/// </summary>
|
||||
public XrResult EnumerateReferenceSpaces(UInt32 spaceCapacityInput, out UInt32 spaceCountOutput, out XrReferenceSpaceType spaces)
|
||||
{
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
spaceCountOutput = 0;
|
||||
spaces = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT;
|
||||
return XrResult.XR_ERROR_SESSION_NOT_RUNNING;
|
||||
}
|
||||
|
||||
return xrEnumerateReferenceSpaces(m_XrSession, spaceCapacityInput, out spaceCountOutput, out spaces);
|
||||
}
|
||||
|
||||
/// xrCreateReferenceSpace
|
||||
OpenXRHelper.xrCreateReferenceSpaceDelegate xrCreateReferenceSpace;
|
||||
/// <summary>
|
||||
/// Creates a reference space
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrCreateReferenceSpace.html">xrCreateReferenceSpace</see>
|
||||
/// </summary>
|
||||
public XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||
{
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
space = 0;
|
||||
return XrResult.XR_ERROR_SESSION_NOT_RUNNING;
|
||||
}
|
||||
|
||||
return xrCreateReferenceSpace(m_XrSession, ref createInfo, out space);
|
||||
}
|
||||
|
||||
/// xrDestroySpace
|
||||
OpenXRHelper.xrDestroySpaceDelegate xrDestroySpace;
|
||||
/// <summary>
|
||||
/// Destroys an XrSpace
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrDestroySpace.html">xrDestroySpace</see>
|
||||
/// </summary>
|
||||
public XrResult DestroySpace(XrSpace space)
|
||||
{
|
||||
if (space != 0)
|
||||
{
|
||||
return xrDestroySpace(space);
|
||||
}
|
||||
return XrResult.XR_ERROR_REFERENCE_SPACE_UNSUPPORTED;
|
||||
}
|
||||
|
||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||
{
|
||||
/// xrGetInstanceProcAddr
|
||||
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrGetInstanceProcAddr.");
|
||||
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
||||
xrGetInstanceProcAddr,
|
||||
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrGetInstanceProcAddr");
|
||||
return false;
|
||||
}
|
||||
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
/// xrGetSystemProperties
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrGetSystemProperties", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrGetSystemProperties.");
|
||||
xrGetSystemProperties = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrGetSystemPropertiesDelegate)) as OpenXRHelper.xrGetSystemPropertiesDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrGetSystemProperties");
|
||||
return false;
|
||||
}
|
||||
/// xrEnumerateReferenceSpaces
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrEnumerateReferenceSpaces", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrEnumerateReferenceSpaces.");
|
||||
xrEnumerateReferenceSpaces = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrEnumerateReferenceSpacesDelegate)) as OpenXRHelper.xrEnumerateReferenceSpacesDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrEnumerateReferenceSpaces");
|
||||
return false;
|
||||
}
|
||||
/// xrCreateReferenceSpace
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrCreateReferenceSpace", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrCreateReferenceSpace.");
|
||||
xrCreateReferenceSpace = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrCreateReferenceSpaceDelegate)) as OpenXRHelper.xrCreateReferenceSpaceDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrCreateReferenceSpace");
|
||||
return false;
|
||||
}
|
||||
/// xrDestroySpace
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrDestroySpace", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrDestroySpace.");
|
||||
xrDestroySpace = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrDestroySpaceDelegate)) as OpenXRHelper.xrDestroySpaceDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrDestroySpace");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CompositionLayer_GetFuncAddrs(xrInstance, xrGetInstanceProcAddr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
DEBUG("Get function pointers in native.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CompositionLayer_GetFuncAddrs");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Wrapper Functions
|
||||
private const string ExtLib = "viveopenxr";
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetExternalSurfaceObj")]
|
||||
public static extern IntPtr VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj();
|
||||
public IntPtr Compositionlayer_GetExternalSurfaceObj()
|
||||
{
|
||||
return VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj();
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetExternalSurfaceObj2")]
|
||||
public static extern IntPtr VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj2(int layerID);
|
||||
public IntPtr Compositionlayer_GetExternalSurfaceObj2(int layerID)
|
||||
{
|
||||
return VIVEOpenXR_Compositionlayer_GetExternalSurfaceObj2(layerID);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_Init")]
|
||||
public static extern int VIVEOpenXR_CompositionLayer_Init(XrSession session, uint textureWidth, uint textureHeight, GraphicsAPI graphicsAPI, bool isDynamic, bool isProtected, out uint imageCount, bool isExternal);
|
||||
/// <summary>
|
||||
/// Init composion layer.
|
||||
/// </summary>
|
||||
public int CompositionLayer_Init(uint textureWidth, uint textureHeight, GraphicsAPI graphicsAPI, bool isDynamic, bool isProtected, out uint imageCount, bool isExternal = false)
|
||||
{
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
ERROR("Xr Session not found");
|
||||
imageCount = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_CompositionLayer_Init(m_XrSession, textureWidth, textureHeight, graphicsAPI, isDynamic, isProtected, out imageCount, isExternal);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetTexture")]
|
||||
public static extern IntPtr VIVEOpenXR_CompositionLayer_GetTexture(int layerID, out uint imageIndex);
|
||||
/// <summary>
|
||||
/// Get composition layer texture.
|
||||
/// </summary>
|
||||
public IntPtr CompositionLayer_GetTexture(int layerID, out uint imageIndex)
|
||||
{
|
||||
return VIVEOpenXR_CompositionLayer_GetTexture(layerID, out imageIndex);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_ReleaseTexture")]
|
||||
public static extern bool VIVEOpenXR_CompositionLayer_ReleaseTexture(int layerID);
|
||||
/// <summary>
|
||||
/// release composition layer texture.
|
||||
/// </summary>
|
||||
public bool CompositionLayer_ReleaseTexture(int layerID)
|
||||
{
|
||||
return VIVEOpenXR_CompositionLayer_ReleaseTexture(layerID);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_Destroy")]
|
||||
public static extern bool VIVEOpenXR_CompositionLayer_Destroy(int layerID);
|
||||
/// <summary>
|
||||
/// destroy composition layer.
|
||||
/// </summary>
|
||||
public bool CompositionLayer_Destroy(int layerID)
|
||||
{
|
||||
return VIVEOpenXR_CompositionLayer_Destroy(layerID);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerQuad")]
|
||||
public static extern void VIVEOpenXR_Submit_CompositionLayerQuad(XrCompositionLayerQuad quad, LayerType layerType, uint compositionDepth, int layerID);
|
||||
/// <summary>
|
||||
/// submit compostion layer of type quad.
|
||||
/// </summary>
|
||||
public void Submit_CompositionLayerQuad(XrCompositionLayerQuad quad, LayerType layerType, uint compositionDepth, int layerID)
|
||||
{
|
||||
VIVEOpenXR_Submit_CompositionLayerQuad(quad, layerType, compositionDepth, layerID);
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hook native functions
|
||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
DEBUG("CompositionLayer_GetInstanceProcAddr");
|
||||
return CompositionLayer_GetInstanceProcAddr(func);
|
||||
}
|
||||
|
||||
[DllImport(ExtLib, EntryPoint = "compositionlayer_intercept_xrGetInstanceProcAddr")]
|
||||
private static extern IntPtr CompositionLayer_GetInstanceProcAddr(IntPtr func);
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "compositionlayer_GetFuncAddrs")]
|
||||
public static extern XrResult VIVEOpenXR_CompositionLayer_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr);
|
||||
/// <summary>
|
||||
/// get function address of composition layer.
|
||||
/// </summary>
|
||||
public XrResult CompositionLayer_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr)
|
||||
{
|
||||
return VIVEOpenXR_CompositionLayer_GetFuncAddrs(xrInstance, xrGetInstanceProcAddrFuncPtr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b06faf09f93c35b4b83640fd9908b630
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using AOT;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Color Scale Bias)",
|
||||
Desc = "Enable this feature to enable the Composition Layer Color Scale Bias Extension",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenXRColorScaleBiasExtensionString,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class ViveCompositionLayerColorScaleBias : OpenXRFeature
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayer.ColorScaleBias";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.compositionlayer.colorscalebias";
|
||||
|
||||
private const string kOpenXRColorScaleBiasExtensionString = "XR_KHR_composition_layer_color_scale_bias";
|
||||
|
||||
private bool m_ColorScaleBiasExtensionEnabled = true;
|
||||
/// <summary>
|
||||
/// The extension of "XR_KHR_composition_layer_color_scale_bias" is enabled or not.
|
||||
/// </summary>
|
||||
public bool ColorScaleBiasExtensionEnabled
|
||||
{
|
||||
get { return m_ColorScaleBiasExtensionEnabled; }
|
||||
}
|
||||
|
||||
|
||||
#region OpenXR Life Cycle
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenXRColorScaleBiasExtensionString))
|
||||
{
|
||||
WARNING("OnInstanceCreate() " + kOpenXRColorScaleBiasExtensionString + " is NOT enabled.");
|
||||
|
||||
m_ColorScaleBiasExtensionEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Wrapper Functions
|
||||
private const string ExtLib = "viveopenxr";
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerColorBias")]
|
||||
public static extern void VIVEOpenXR_Submit_CompositionLayerColorBias(XrCompositionLayerColorScaleBiasKHR colorBias, int layerID);
|
||||
/// <summary>
|
||||
/// Submit Compostion Layer Color Bias Settings to specific layer ID.
|
||||
/// </summary>
|
||||
public void Submit_CompositionLayerColorBias(XrCompositionLayerColorScaleBiasKHR colorBias, int layerID)
|
||||
{
|
||||
if (!ColorScaleBiasExtensionEnabled)
|
||||
{
|
||||
ERROR("Submit_CompositionLayerColorBias: " + kOpenXRColorScaleBiasExtensionString + " is not enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
VIVEOpenXR_Submit_CompositionLayerColorBias(colorBias, layerID);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5621f5c5156e7648a0c3a1fa86497a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using AOT;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Cylinder)",
|
||||
Desc = "Enable this feature to enable the Composition Layer Cylinder Extension",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenXRCylinderExtensionString,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class ViveCompositionLayerCylinder : OpenXRFeature
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayer.Cylinder";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.compositionlayer.cylinder";
|
||||
|
||||
private const string kOpenXRCylinderExtensionString = "XR_KHR_composition_layer_cylinder";
|
||||
|
||||
private bool m_CylinderExtensionEnabled = true;
|
||||
/// <summary>
|
||||
/// The extension "XR_KHR_composition_layer_cylinder" is enabled or not.
|
||||
/// </summary>
|
||||
public bool CylinderExtensionEnabled
|
||||
{
|
||||
get { return m_CylinderExtensionEnabled; }
|
||||
}
|
||||
|
||||
|
||||
#region OpenXR Life Cycle
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenXRCylinderExtensionString))
|
||||
{
|
||||
WARNING("OnInstanceCreate() " + kOpenXRCylinderExtensionString + " is NOT enabled.");
|
||||
|
||||
m_CylinderExtensionEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Wrapper Functions
|
||||
private const string ExtLib = "viveopenxr";
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerCylinder")]
|
||||
public static extern void VIVEOpenXR_Submit_CompositionLayerCylinder(XrCompositionLayerCylinderKHR cylinder, LayerType layerType, uint compositionDepth, int layerID);
|
||||
/// <summary>
|
||||
/// submit compostion layer of type cylinder.
|
||||
/// </summary>
|
||||
public void Submit_CompositionLayerCylinder(XrCompositionLayerCylinderKHR cylinder, LayerType layerType, uint compositionDepth, int layerID)
|
||||
{
|
||||
if (!CylinderExtensionEnabled)
|
||||
{
|
||||
ERROR("Submit_CompositionLayerCylinder: " + kOpenXRCylinderExtensionString + " is NOT enabled.");
|
||||
}
|
||||
|
||||
VIVEOpenXR_Submit_CompositionLayerCylinder(cylinder, layerType, compositionDepth, layerID);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abd96d069ecb9b243a69661e0bffdfab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using AOT;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Equirect)",
|
||||
Desc = "Enable this feature to enable the Composition Layer Equirect Extension",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenXRCylinderExtensionString,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class ViveCompositionLayerEquirect : OpenXRFeature
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayer.Equirect";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.compositionlayer.equirect";
|
||||
|
||||
private const string kOpenXRCylinderExtensionString = "XR_KHR_composition_layer_equirect XR_KHR_composition_layer_equirect2";
|
||||
|
||||
private bool m_EquirectExtensionEnabled = true;
|
||||
/// <summary>
|
||||
/// The extension "XR_KHR_composition_layer_equirect" is enabled or not.
|
||||
/// </summary>
|
||||
public bool EquirectExtensionEnabled
|
||||
{
|
||||
get { return m_EquirectExtensionEnabled; }
|
||||
}
|
||||
|
||||
private bool m_Equirect2ExtensionEnabled = true;
|
||||
/// <summary>
|
||||
/// The extension "XR_KHR_composition_layer_equirect2" is enabled or not.
|
||||
/// </summary>
|
||||
public bool Equirect2ExtensionEnabled
|
||||
{
|
||||
get { return m_Equirect2ExtensionEnabled; }
|
||||
}
|
||||
|
||||
|
||||
#region OpenXR Life Cycle
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
if (!OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect"))
|
||||
{
|
||||
WARNING("OnInstanceCreate() " + "XR_KHR_composition_layer_equirect" + " is NOT enabled.");
|
||||
|
||||
m_EquirectExtensionEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect2"))
|
||||
{
|
||||
WARNING("OnInstanceCreate() " + "XR_KHR_composition_layer_equirect2" + " is NOT enabled.");
|
||||
|
||||
m_Equirect2ExtensionEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Wrapper Functions
|
||||
private const string ExtLib = "viveopenxr";
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerEquirect")]
|
||||
public static extern void VIVEOpenXR_Submit_CompositionLayerEquirect(XrCompositionLayerEquirectKHR equirect, LayerType layerType, uint compositionDepth, int layerID);
|
||||
/// <summary>
|
||||
/// submit compostion layer of type equirect.
|
||||
/// </summary>
|
||||
public void Submit_CompositionLayerEquirect(XrCompositionLayerEquirectKHR equirect, LayerType layerType, uint compositionDepth, int layerID)
|
||||
{
|
||||
if (!EquirectExtensionEnabled)
|
||||
{
|
||||
ERROR("Submit_CompositionLayerEquirect: " + "XR_KHR_composition_layer_equirect" + " is NOT enabled.");
|
||||
}
|
||||
|
||||
VIVEOpenXR_Submit_CompositionLayerEquirect(equirect, layerType, compositionDepth, layerID);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "submit_CompositionLayerEquirect2")]
|
||||
public static extern void VIVEOpenXR_Submit_CompositionLayerEquirect2(XrCompositionLayerEquirect2KHR equirect2, LayerType layerType, uint compositionDepth, int layerID);
|
||||
/// <summary>
|
||||
/// submit compostion layer of type equirect2.
|
||||
/// </summary>
|
||||
public void Submit_CompositionLayerEquirect2(XrCompositionLayerEquirect2KHR equirect2, LayerType layerType, uint compositionDepth, int layerID)
|
||||
{
|
||||
if (!Equirect2ExtensionEnabled)
|
||||
{
|
||||
ERROR("Submit_CompositionLayerEquirect2: " + "XR_KHR_composition_layer_equirect2" + " is NOT enabled.");
|
||||
}
|
||||
|
||||
VIVEOpenXR_Submit_CompositionLayerEquirect2(equirect2, layerType, compositionDepth, layerID);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec826264ff4d75d4081f2ca472a3e083
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,203 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Extra Settings) (Beta)",
|
||||
Desc = "Enable this feature to use the Composition Layer Extra Settings.",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenxrExtensionStrings,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class ViveCompositionLayerExtraSettings : OpenXRFeature
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayer.ExtraSettings";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
/// <summary>
|
||||
/// Settings Editor Enable Sharpening or Not.
|
||||
/// </summary>
|
||||
public bool SettingsEditorEnableSharpening = false;
|
||||
|
||||
/// <summary>
|
||||
/// Support Sharpening or Not.
|
||||
/// </summary>
|
||||
public bool supportSharpening = false;
|
||||
|
||||
/// <summary>
|
||||
/// Settings Editor Sharpening Mode
|
||||
/// </summary>
|
||||
public XrSharpeningModeHTC SettingsEditorSharpeningMode = XrSharpeningModeHTC.FAST;
|
||||
|
||||
/// <summary>
|
||||
/// Settings Editor Sharpening Levell
|
||||
/// </summary>
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float SettingsEditorSharpeningLevel = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.compositionlayer.extrasettings";
|
||||
|
||||
/// <summary>
|
||||
/// OpenXR specification.
|
||||
/// </summary>
|
||||
public const string kOpenxrExtensionStrings = "XR_HTC_composition_layer_extra_settings";
|
||||
|
||||
#region OpenXR Life Cycle
|
||||
private bool m_XrInstanceCreated = false;
|
||||
/// <summary>
|
||||
/// The XR instance is created or not.
|
||||
/// </summary>
|
||||
public bool XrInstanceCreated
|
||||
{
|
||||
get { return m_XrInstanceCreated; }
|
||||
}
|
||||
private XrInstance m_XrInstance = 0;
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
foreach (string kOpenxrExtensionString in kOpenxrExtensionStrings.Split(' '))
|
||||
{
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||
{
|
||||
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
m_XrInstanceCreated = true;
|
||||
m_XrInstance = xrInstance;
|
||||
DEBUG("OnInstanceCreate() " + m_XrInstance);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||
{
|
||||
m_XrInstanceCreated = false;
|
||||
DEBUG("OnInstanceDestroy() " + m_XrInstance);
|
||||
}
|
||||
|
||||
private XrSystemId m_XrSystemId = 0;
|
||||
protected override void OnSystemChange(ulong xrSystem)
|
||||
{
|
||||
m_XrSystemId = xrSystem;
|
||||
DEBUG("OnSystemChange() " + m_XrSystemId);
|
||||
}
|
||||
|
||||
private bool m_XrSessionCreated = false;
|
||||
/// <summary>
|
||||
/// The XR session is created or not.
|
||||
/// </summary>
|
||||
public bool XrSessionCreated
|
||||
{
|
||||
get { return m_XrSessionCreated; }
|
||||
}
|
||||
private XrSession m_XrSession = 0;
|
||||
protected override void OnSessionCreate(ulong xrSession)
|
||||
{
|
||||
m_XrSession = xrSession;
|
||||
m_XrSessionCreated = true;
|
||||
DEBUG("OnSessionCreate() " + m_XrSession);
|
||||
}
|
||||
|
||||
private bool m_XrSessionEnding = false;
|
||||
/// <summary>
|
||||
/// The XR session is ending or not.
|
||||
/// </summary>
|
||||
public bool XrSessionEnding
|
||||
{
|
||||
get { return m_XrSessionEnding; }
|
||||
}
|
||||
|
||||
protected override void OnSessionBegin(ulong xrSession)
|
||||
{
|
||||
m_XrSessionEnding = false;
|
||||
DEBUG("OnSessionBegin() " + m_XrSession);
|
||||
|
||||
//enable Sharpening
|
||||
if (OpenXRRuntime.IsExtensionEnabled("XR_HTC_composition_layer_extra_settings"))
|
||||
{
|
||||
ViveCompositionLayer_UpdateSystemProperties(m_XrInstance, m_XrSystemId);
|
||||
supportSharpening = ViveCompositionLayer_IsSupportSharpening();
|
||||
if (supportSharpening && SettingsEditorEnableSharpening)
|
||||
{
|
||||
EnableSharpening(SettingsEditorSharpeningMode, SettingsEditorSharpeningLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSessionEnd(ulong xrSession)
|
||||
{
|
||||
m_XrSessionEnding = true;
|
||||
DEBUG("OnSessionEnd() " + m_XrSession);
|
||||
}
|
||||
|
||||
protected override void OnSessionDestroy(ulong xrSession)
|
||||
{
|
||||
m_XrSessionCreated = false;
|
||||
DEBUG("OnSessionDestroy() " + xrSession);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wrapper Functions
|
||||
private const string ExtLib = "viveopenxr";
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_UpdateSystemProperties")]
|
||||
private static extern int VIVEOpenXR_ViveCompositionLayer_UpdateSystemProperties(XrInstance instance, XrSystemId system_id);
|
||||
private int ViveCompositionLayer_UpdateSystemProperties(XrInstance instance, XrSystemId system_id)
|
||||
{
|
||||
return VIVEOpenXR_ViveCompositionLayer_UpdateSystemProperties(instance, system_id);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_IsSupportSharpening")]
|
||||
private static extern bool VIVEOpenXR_ViveCompositionLayer_IsSupportSharpening();
|
||||
private bool ViveCompositionLayer_IsSupportSharpening()
|
||||
{
|
||||
return VIVEOpenXR_ViveCompositionLayer_IsSupportSharpening();
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_enableSharpening")]
|
||||
private static extern int VIVEOpenXR_ViveCompositionLayer_enableSharpening(XrSharpeningModeHTC sharpeningMode, float sharpeningLevel);
|
||||
/// <summary>
|
||||
/// Enable the sharpening setting applying to the projection layer.
|
||||
/// </summary>
|
||||
/// <param name="sharpeningMode">The sharpening mode in <see cref="XrSharpeningModeHTC"/>.</param>
|
||||
/// <param name="sharpeningLevel">The sharpening level in float [0, 1].</param>
|
||||
/// <returns>True for success.</returns>
|
||||
public bool EnableSharpening(XrSharpeningModeHTC sharpeningMode, float sharpeningLevel)
|
||||
{
|
||||
return (VIVEOpenXR_ViveCompositionLayer_enableSharpening(sharpeningMode, sharpeningLevel) == 0);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "viveCompositionLayer_disableSharpening")]
|
||||
private static extern int VIVEOpenXR_ViveCompositionLayer_DisableSharpening();
|
||||
/// <summary>
|
||||
/// Disable the sharpening setting on the projection layer.
|
||||
/// </summary>
|
||||
/// <returns>True for success</returns>
|
||||
public bool DisableSharpening()
|
||||
{
|
||||
return (VIVEOpenXR_ViveCompositionLayer_DisableSharpening() == 0);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f26de592e4135874baf6e64cc94183be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,583 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer
|
||||
{
|
||||
public struct XrSwapchain : IEquatable<ulong>
|
||||
{
|
||||
private readonly ulong value;
|
||||
|
||||
public XrSwapchain(ulong u)
|
||||
{
|
||||
value = u;
|
||||
}
|
||||
|
||||
public static implicit operator ulong(XrSwapchain xrBool)
|
||||
{
|
||||
return xrBool.value;
|
||||
}
|
||||
public static implicit operator XrSwapchain(ulong u)
|
||||
{
|
||||
return new XrSwapchain(u);
|
||||
}
|
||||
|
||||
public bool Equals(XrSwapchain other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
public bool Equals(ulong other)
|
||||
{
|
||||
return value == other;
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is XrSwapchain && Equals((XrSwapchain)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public static bool operator ==(XrSwapchain a, XrSwapchain b) { return a.Equals(b); }
|
||||
public static bool operator !=(XrSwapchain a, XrSwapchain b) { return !a.Equals(b); }
|
||||
public static bool operator >=(XrSwapchain a, XrSwapchain b) { return a.value >= b.value; }
|
||||
public static bool operator <=(XrSwapchain a, XrSwapchain b) { return a.value <= b.value; }
|
||||
public static bool operator >(XrSwapchain a, XrSwapchain b) { return a.value > b.value; }
|
||||
public static bool operator <(XrSwapchain a, XrSwapchain b) { return a.value < b.value; }
|
||||
public static XrSwapchain operator +(XrSwapchain a, XrSwapchain b) { return a.value + b.value; }
|
||||
public static XrSwapchain operator -(XrSwapchain a, XrSwapchain b) { return a.value - b.value; }
|
||||
public static XrSwapchain operator *(XrSwapchain a, XrSwapchain b) { return a.value * b.value; }
|
||||
public static XrSwapchain operator /(XrSwapchain a, XrSwapchain b)
|
||||
{
|
||||
if (b.value == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
return a.value / b.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct XrSwapchainCreateFlags : IEquatable<UInt64>
|
||||
{
|
||||
private readonly UInt64 value;
|
||||
|
||||
public XrSwapchainCreateFlags(UInt64 u)
|
||||
{
|
||||
value = u;
|
||||
}
|
||||
|
||||
public static implicit operator UInt64(XrSwapchainCreateFlags xrBool)
|
||||
{
|
||||
return xrBool.value;
|
||||
}
|
||||
public static implicit operator XrSwapchainCreateFlags(UInt64 u)
|
||||
{
|
||||
return new XrSwapchainCreateFlags(u);
|
||||
}
|
||||
|
||||
public bool Equals(XrSwapchainCreateFlags other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
public bool Equals(UInt64 other)
|
||||
{
|
||||
return value == other;
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is XrSwapchainCreateFlags && Equals((XrSwapchainCreateFlags)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public static bool operator ==(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.Equals(b); }
|
||||
public static bool operator !=(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return !a.Equals(b); }
|
||||
public static bool operator >=(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.value >= b.value; }
|
||||
public static bool operator <=(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.value <= b.value; }
|
||||
public static bool operator >(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.value > b.value; }
|
||||
public static bool operator <(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.value < b.value; }
|
||||
public static XrSwapchainCreateFlags operator +(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.value + b.value; }
|
||||
public static XrSwapchainCreateFlags operator -(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.value - b.value; }
|
||||
public static XrSwapchainCreateFlags operator *(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b) { return a.value * b.value; }
|
||||
public static XrSwapchainCreateFlags operator /(XrSwapchainCreateFlags a, XrSwapchainCreateFlags b)
|
||||
{
|
||||
if (b.value == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
return a.value / b.value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct XrSwapchainUsageFlags : IEquatable<UInt64>
|
||||
{
|
||||
private readonly UInt64 value;
|
||||
|
||||
public XrSwapchainUsageFlags(UInt64 u)
|
||||
{
|
||||
value = u;
|
||||
}
|
||||
|
||||
public static implicit operator UInt64(XrSwapchainUsageFlags xrBool)
|
||||
{
|
||||
return xrBool.value;
|
||||
}
|
||||
public static implicit operator XrSwapchainUsageFlags(UInt64 u)
|
||||
{
|
||||
return new XrSwapchainUsageFlags(u);
|
||||
}
|
||||
|
||||
public bool Equals(XrSwapchainUsageFlags other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
public bool Equals(UInt64 other)
|
||||
{
|
||||
return value == other;
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is XrSwapchainUsageFlags && Equals((XrSwapchainUsageFlags)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public static bool operator ==(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.Equals(b); }
|
||||
public static bool operator !=(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return !a.Equals(b); }
|
||||
public static bool operator >=(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.value >= b.value; }
|
||||
public static bool operator <=(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.value <= b.value; }
|
||||
public static bool operator >(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.value > b.value; }
|
||||
public static bool operator <(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.value < b.value; }
|
||||
public static XrSwapchainUsageFlags operator +(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.value + b.value; }
|
||||
public static XrSwapchainUsageFlags operator -(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.value - b.value; }
|
||||
public static XrSwapchainUsageFlags operator *(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b) { return a.value * b.value; }
|
||||
public static XrSwapchainUsageFlags operator /(XrSwapchainUsageFlags a, XrSwapchainUsageFlags b)
|
||||
{
|
||||
if (b.value == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
return a.value / b.value;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerQuad
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrCompositionLayerFlags layerFlags;
|
||||
public XrSpace space;
|
||||
public XrEyeVisibility eyeVisibility;
|
||||
public XrSwapchainSubImage subImage;
|
||||
public XrPosef pose;
|
||||
public XrExtent2Df size;
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerCylinderKHR
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrCompositionLayerFlags layerFlags;
|
||||
public XrSpace space;
|
||||
public XrEyeVisibility eyeVisibility;
|
||||
public XrSwapchainSubImage subImage;
|
||||
public XrPosef pose;
|
||||
public float radius;
|
||||
public float centralAngle;
|
||||
public float aspectRatio;
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerEquirectKHR
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrCompositionLayerFlags layerFlags;
|
||||
public XrSpace space;
|
||||
public XrEyeVisibility eyeVisibility;
|
||||
public XrSwapchainSubImage subImage;
|
||||
public XrPosef pose;
|
||||
public float radius;
|
||||
public XrVector2f scale;
|
||||
public XrVector2f bias;
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerEquirect2KHR
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrCompositionLayerFlags layerFlags;
|
||||
public XrSpace space;
|
||||
public XrEyeVisibility eyeVisibility;
|
||||
public XrSwapchainSubImage subImage;
|
||||
public XrPosef pose;
|
||||
public float radius;
|
||||
public float centralHorizontalAngle;
|
||||
public float upperVerticalAngle;
|
||||
public float lowerVerticalAngle;
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrSwapchainSubImage
|
||||
{
|
||||
public XrSwapchain swapchain;
|
||||
public XrRect2Di imageRect;
|
||||
public uint imageArrayIndex;
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerColorScaleBiasKHR
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrColor4f colorScale;
|
||||
public XrColor4f colorBias;
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerSharpeningSettingHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrSharpeningModeHTC mode;
|
||||
public float sharpeningLevel;
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerSuperSamplingSettingHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrSuperSamplingModeHTC mode;
|
||||
}
|
||||
public enum XrSharpeningModeHTC
|
||||
{
|
||||
FAST = 0,
|
||||
NORMAL = 1,
|
||||
QUALITY = 2,
|
||||
AUTOMATIC = 3,
|
||||
}
|
||||
public enum XrSuperSamplingModeHTC
|
||||
{
|
||||
FAST = 0,
|
||||
NORMAL = 1,
|
||||
QUALITY = 2,
|
||||
AUTOMATIC = 3,
|
||||
}
|
||||
|
||||
public enum GraphicsAPI
|
||||
{
|
||||
GLES3 = 1,
|
||||
Vulkan = 2
|
||||
}
|
||||
public enum LayerType
|
||||
{
|
||||
///<summary> Overlays are composition layers rendered after the projection layer </summary>
|
||||
Overlay = 1,
|
||||
///<summary> Underlays are composition layers rendered before the projection layer </summary>
|
||||
Underlay = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An application can create an <see cref="XrPassthroughHTC">XrPassthroughHTC</see> handle by calling <see cref="ViveCompositionLayerHelper.xrCreatePassthroughHTC">xrCreatePassthroughHTC</see>. The returned passthrough handle can be subsequently used in API calls.
|
||||
/// </summary>
|
||||
public struct XrPassthroughHTC : IEquatable<UInt64>
|
||||
{
|
||||
private readonly UInt64 value;
|
||||
|
||||
public XrPassthroughHTC(UInt64 u)
|
||||
{
|
||||
value = u;
|
||||
}
|
||||
|
||||
public static implicit operator UInt64(XrPassthroughHTC equatable)
|
||||
{
|
||||
return equatable.value;
|
||||
}
|
||||
public static implicit operator XrPassthroughHTC(UInt64 u)
|
||||
{
|
||||
return new XrPassthroughHTC(u);
|
||||
}
|
||||
|
||||
public bool Equals(XrPassthroughHTC other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
public bool Equals(UInt64 other)
|
||||
{
|
||||
return value == other;
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is XrPassthroughHTC && Equals((XrPassthroughHTC)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public static bool operator ==(XrPassthroughHTC a, XrPassthroughHTC b) { return a.Equals(b); }
|
||||
public static bool operator !=(XrPassthroughHTC a, XrPassthroughHTC b) { return !a.Equals(b); }
|
||||
public static bool operator >=(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value >= b.value; }
|
||||
public static bool operator <=(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value <= b.value; }
|
||||
public static bool operator >(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value > b.value; }
|
||||
public static bool operator <(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value < b.value; }
|
||||
public static XrPassthroughHTC operator +(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value + b.value; }
|
||||
public static XrPassthroughHTC operator -(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value - b.value; }
|
||||
public static XrPassthroughHTC operator *(XrPassthroughHTC a, XrPassthroughHTC b) { return a.value * b.value; }
|
||||
public static XrPassthroughHTC operator /(XrPassthroughHTC a, XrPassthroughHTC b)
|
||||
{
|
||||
if (b.value == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
return a.value / b.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The XrPassthroughFormHTC enumeration identifies the form of the passthrough, presenting the passthrough fill the full screen or project onto a specified mesh.
|
||||
/// </summary>
|
||||
public enum XrPassthroughFormHTC
|
||||
{
|
||||
/// <summary>
|
||||
/// Presents the passthrough with full of the entire screen..
|
||||
/// </summary>
|
||||
XR_PASSTHROUGH_FORM_PLANAR_HTC = 0,
|
||||
/// <summary>
|
||||
/// Presents the passthrough projecting onto a custom mesh.
|
||||
/// </summary>
|
||||
XR_PASSTHROUGH_FORM_PROJECTED_HTC = 1,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The XrPassthroughCreateInfoHTC structure describes the information to create an <see cref="XrPassthroughCreateInfoHTC">XrPassthroughCreateInfoHTC</see> handle.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrPassthroughCreateInfoHTC
|
||||
{
|
||||
/// <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. No such structures are defined in core OpenXR or this extension.
|
||||
/// </summary>
|
||||
public IntPtr next;
|
||||
/// <summary>
|
||||
/// The form specifies the form of passthrough.
|
||||
/// </summary>
|
||||
public XrPassthroughFormHTC form;
|
||||
|
||||
/// <param name="in_type">The <see cref="XrStructureType">XrStructureType</see> of this structure.</param>
|
||||
/// <param name="in_next">NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.</param>
|
||||
/// <param name="in_facialTrackingType">An XrFacialTrackingTypeHTC which describes which type of facial tracking should be used for this handle.</param>
|
||||
public XrPassthroughCreateInfoHTC(XrStructureType in_type, IntPtr in_next, XrPassthroughFormHTC in_form)
|
||||
{
|
||||
type = in_type;
|
||||
next = in_next;
|
||||
form = in_form;
|
||||
}
|
||||
};
|
||||
/// <summary>
|
||||
/// The application can specify the XrPassthroughColorHTC to adjust the alpha value of the passthrough. The range is between 0.0f and 1.0f, 1.0f means opaque.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrPassthroughColorHTC
|
||||
{
|
||||
/// <summary>
|
||||
/// The XrStructureType of this structure.
|
||||
/// </summary>
|
||||
public XrStructureType type;
|
||||
/// <summary>
|
||||
/// Next is NULL or a pointer to the next structure in a structure chain, such as XrPassthroughMeshTransformInfoHTC.
|
||||
/// </summary>
|
||||
public IntPtr next;
|
||||
/// <summary>
|
||||
/// The alpha value of the passthrough in the range [0, 1].
|
||||
/// </summary>
|
||||
public float alpha;
|
||||
public XrPassthroughColorHTC(XrStructureType in_type, IntPtr in_next, float in_alpha)
|
||||
{
|
||||
type = in_type;
|
||||
next = in_next;
|
||||
alpha = in_alpha;
|
||||
}
|
||||
};
|
||||
/// <summary>
|
||||
/// A pointer to XrCompositionLayerPassthroughHTC may be submitted in xrEndFrame as a pointer to the base structure XrCompositionLayerBaseHeader, in the desired layer order, to request the runtime to composite a passthrough layer into the final frame output.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrCompositionLayerPassthroughHTC
|
||||
{
|
||||
/// <summary>
|
||||
/// The XrStructureType of this structure.
|
||||
/// </summary>
|
||||
public XrStructureType type;
|
||||
/// <summary>
|
||||
/// Next is NULL or a pointer to the next structure in a structure chain, such as XrPassthroughMeshTransformInfoHTC.
|
||||
/// </summary>
|
||||
public IntPtr next;
|
||||
/// <summary>
|
||||
/// A bitmask of XrCompositionLayerFlagBits describing flags to apply to the layer.
|
||||
/// </summary>
|
||||
public XrCompositionLayerFlags layerFlags;
|
||||
/// <summary>
|
||||
/// The XrSpace that specifies the layer’s space - must be XR_NULL_HANDLE.
|
||||
/// </summary>
|
||||
public XrSpace space;
|
||||
/// <summary>
|
||||
/// The XrPassthroughHTC previously created by xrCreatePassthroughHTC.
|
||||
/// </summary>
|
||||
public XrPassthroughHTC passthrough;
|
||||
/// <summary>
|
||||
/// The XrPassthroughColorHTC describing the color information with the alpha value of the passthrough layer.
|
||||
/// </summary>
|
||||
public XrPassthroughColorHTC color;
|
||||
public XrCompositionLayerPassthroughHTC(XrStructureType in_type, IntPtr in_next, XrCompositionLayerFlags in_layerFlags,
|
||||
XrSpace in_space, XrPassthroughHTC in_passthrough, XrPassthroughColorHTC in_color)
|
||||
{
|
||||
type = in_type;
|
||||
next = in_next;
|
||||
layerFlags = in_layerFlags;
|
||||
space = in_space;
|
||||
passthrough = in_passthrough;
|
||||
color = in_color;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The XrPassthroughMeshTransformInfoHTC structure describes the mesh and transformation.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct XrPassthroughMeshTransformInfoHTC
|
||||
{
|
||||
/// <summary>
|
||||
/// The XrStructureType of this structure.
|
||||
/// </summary>
|
||||
public XrStructureType type;
|
||||
/// <summary>
|
||||
/// Next is NULL or a pointer to the next structure in a structure chain.
|
||||
/// </summary>
|
||||
public IntPtr next;
|
||||
/// <summary>
|
||||
/// The count of vertices array in the mesh.
|
||||
/// </summary>
|
||||
public UInt32 vertexCount;
|
||||
/// <summary>
|
||||
/// An array of XrVector3f. The size of the array must be equal to vertexCount.
|
||||
/// </summary>
|
||||
public XrVector3f[] vertices;
|
||||
/// <summary>
|
||||
/// The count of indices array in the mesh.
|
||||
/// </summary>
|
||||
public UInt32 indexCount;
|
||||
/// <summary>
|
||||
/// An array of triangle indices. The size of the array must be equal to indexCount.
|
||||
/// </summary>
|
||||
public UInt32[] indices;
|
||||
/// <summary>
|
||||
/// The XrSpace that defines the projected passthrough's base space for transformations.
|
||||
/// </summary>
|
||||
public XrSpace baseSpace;
|
||||
/// <summary>
|
||||
/// The XrTime that defines the time at which the transform is applied.
|
||||
/// </summary>
|
||||
public XrTime time;
|
||||
/// <summary>
|
||||
/// The XrPosef that defines the pose of the mesh
|
||||
/// </summary>
|
||||
public XrPosef pose;
|
||||
/// <summary>
|
||||
/// The XrVector3f that defines the scale of the mesh
|
||||
/// </summary>
|
||||
public XrVector3f scale;
|
||||
public XrPassthroughMeshTransformInfoHTC(XrStructureType in_type, IntPtr in_next, UInt32 in_vertexCount,
|
||||
XrVector3f[] in_vertices, UInt32 in_indexCount, UInt32[] in_indices, XrSpace in_baseSpace, XrTime in_time,
|
||||
XrPosef in_pose, XrVector3f in_scale)
|
||||
{
|
||||
type = in_type;
|
||||
next = in_next;
|
||||
vertexCount = in_vertexCount;
|
||||
vertices = in_vertices;
|
||||
indexCount = in_indexCount;
|
||||
indices = in_indices;
|
||||
baseSpace = in_baseSpace;
|
||||
time = in_time;
|
||||
pose = in_pose;
|
||||
scale = in_scale;
|
||||
}
|
||||
};
|
||||
|
||||
public static class ViveCompositionLayerHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// The delegate function of <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreatePassthroughHTC">xrCreatePassthroughHTC</see>.
|
||||
/// </summary>
|
||||
/// <param name="session">An <see cref="XrSession">XrSession</see> in which the passthrough will be active.</param>
|
||||
/// <param name="createInfo">createInfo is a pointer to an <see cref="XrPassthroughCreateInfoHTC">XrPassthroughCreateInfoHTC</see> structure containing information about how to create the passthrough.</param>
|
||||
/// <param name="passthrough">passthrough is a pointer to a handle in which the created <see cref="XrPassthroughHTC">XrPassthroughHTC</see> is returned.</param>
|
||||
/// <returns>XR_SUCCESS for success.</returns>
|
||||
public delegate XrResult xrCreatePassthroughHTCDelegate(
|
||||
XrSession session,
|
||||
XrPassthroughCreateInfoHTC createInfo,
|
||||
out XrPassthroughHTC passthrough);
|
||||
|
||||
/// <summary>
|
||||
/// The delegate function of <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyPassthroughHTC">xrDestroyFacialTrackerHTC</see>.
|
||||
/// </summary>
|
||||
/// <param name="passthrough">passthrough is the <see cref="XrPassthroughHTC">XrPassthroughHTC</see> to be destroyed..</param>
|
||||
/// <returns>XR_SUCCESS for success.</returns>
|
||||
public delegate XrResult xrDestroyPassthroughHTCDelegate(
|
||||
XrPassthroughHTC passthrough);
|
||||
// Flag bits for XrCompositionLayerFlags
|
||||
public static XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001;
|
||||
public static XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002;
|
||||
public static XrCompositionLayerFlags XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT = 0x00000004;
|
||||
|
||||
// Flag bits for XrSwapchainCreateFlags
|
||||
public static XrSwapchainCreateFlags XR_SWAPCHAIN_CREATE_PROTECTED_CONTENT_BIT = 0x00000001;
|
||||
public static XrSwapchainCreateFlags XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT = 0x00000002;
|
||||
|
||||
// Flag bits for XrSwapchainUsageFlags
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT = 0x00000001;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000002;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT = 0x00000004;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_TRANSFER_SRC_BIT = 0x00000008;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT = 0x00000010;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_SAMPLED_BIT = 0x00000020;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT = 0x00000040;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND = 0x00000080;
|
||||
public static XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_KHR = 0x00000080;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dfb147f98026e44786fda53bb2aeb18
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,943 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using AOT;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
using UnityEngine.XR;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.CompositionLayer.Passthrough
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Composition Layer (Passthrough) (Deprecated)",
|
||||
Desc = "Enable this feature to use the HTC Passthrough feature.",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenxrExtensionStrings,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android ,BuildTargetGroup.Standalone},
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
[Obsolete("This class is deprecated. Please use VivePassthrough instead.")]
|
||||
public class ViveCompositionLayerPassthrough : OpenXRFeature
|
||||
{
|
||||
const string LOG_TAG = "VIVE.OpenXR.ViveCompositionLayerPassthrough";
|
||||
static void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); }
|
||||
static void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); }
|
||||
static void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); }
|
||||
|
||||
private List<int> passthroughIDList = new List<int>();
|
||||
/// <summary>
|
||||
/// The List of passthrough ID.
|
||||
/// </summary>
|
||||
public List<int> PassthroughIDList { get{ return new List<int>(passthroughIDList); } }
|
||||
|
||||
private List<XRInputSubsystem> inputSubsystems = new List<XRInputSubsystem>();
|
||||
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.openxr.feature.compositionlayer.passthrough";
|
||||
|
||||
/// <summary>
|
||||
/// The extension string.
|
||||
/// </summary>
|
||||
public const string kOpenxrExtensionStrings = "XR_HTC_passthrough";
|
||||
|
||||
private bool m_HTCPassthroughExtensionEnabled = true;
|
||||
/// <summary>
|
||||
/// The HTC Passthrough extension is enabled or not.
|
||||
/// </summary>
|
||||
public bool HTCPassthroughExtensionEnabled
|
||||
{
|
||||
get { return m_HTCPassthroughExtensionEnabled; }
|
||||
}
|
||||
|
||||
#region OpenXR Life Cycle
|
||||
private bool m_XrInstanceCreated = false;
|
||||
/// <summary>
|
||||
/// The XR instance is created or not.
|
||||
/// </summary>
|
||||
public bool XrInstanceCreated
|
||||
{
|
||||
get { return m_XrInstanceCreated; }
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
private static IntPtr xrGetInstanceProcAddr_prev;
|
||||
private static IntPtr XrEndFrame_prev;
|
||||
private static IntPtr XrWaitFrame_prev;
|
||||
private static List<IntPtr> layerListOrigin = new List<IntPtr>();
|
||||
private static List<IntPtr> layerListModified = new List<IntPtr>();
|
||||
private static IntPtr layersModified = Marshal.AllocHGlobal((int)(Marshal.SizeOf(typeof(IntPtr)) * 30)); //Preallocate a layer buffer with sufficient size and reuse it for each frame.
|
||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
UnityEngine.Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
|
||||
xrGetInstanceProcAddr_prev = func;
|
||||
return Marshal.GetFunctionPointerForDelegate(Intercept_xrGetInstanceProcAddr);
|
||||
}
|
||||
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
|
||||
private static XrResult InterceptXrEndFrame_xrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (xrGetInstanceProcAddr_prev == null || xrGetInstanceProcAddr_prev == IntPtr.Zero)
|
||||
{
|
||||
UnityEngine.Debug.LogError("xrGetInstanceProcAddr_prev is null");
|
||||
function = IntPtr.Zero;
|
||||
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
// Get delegate of old xrGetInstanceProcAddr.
|
||||
var xrGetProc = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(xrGetInstanceProcAddr_prev);
|
||||
XrResult result = xrGetProc(instance, name, out function);
|
||||
if (name == "xrEndFrame")
|
||||
{
|
||||
XrEndFrame_prev = function;
|
||||
m_intercept_xrEndFrame = intercepted_xrEndFrame;
|
||||
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrEndFrame); ;
|
||||
UnityEngine.Debug.Log("Getting xrEndFrame func");
|
||||
}
|
||||
if (name == "xrWaitFrame")
|
||||
{
|
||||
XrWaitFrame_prev = function;
|
||||
m_intercept_xrWaitFrame = intercepted_xrWaitFrame;
|
||||
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame); ;
|
||||
UnityEngine.Debug.Log("Getting xrWaitFrame func");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(OpenXRHelper.xrEndFrameDelegate))]
|
||||
private static XrResult intercepted_xrEndFrame(XrSession session, ref XrFrameEndInfo frameEndInfo)
|
||||
{
|
||||
XrResult res;
|
||||
// Get delegate of prev xrEndFrame.
|
||||
var xrEndFrame = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrEndFrameDelegate>(XrEndFrame_prev);
|
||||
|
||||
layerListOrigin.Clear();
|
||||
uint layerCount = frameEndInfo.layerCount;
|
||||
IntPtr layers = frameEndInfo.layers;
|
||||
for (int i = 0; i < layerCount; i++)
|
||||
{
|
||||
IntPtr ptr = Marshal.ReadIntPtr(layers, i * Marshal.SizeOf(typeof(IntPtr)));
|
||||
XrCompositionLayerBaseHeader header = (XrCompositionLayerBaseHeader)Marshal.PtrToStructure(ptr, typeof(XrCompositionLayerBaseHeader));
|
||||
layerListOrigin.Add(ptr);
|
||||
}
|
||||
List<IntPtr> layerListNew;
|
||||
if (layerListModified.Count != 0)
|
||||
{
|
||||
layerListNew = new List<IntPtr>(layerListModified);
|
||||
}
|
||||
else
|
||||
{
|
||||
layerListNew = new List<IntPtr>(layerListOrigin);
|
||||
}
|
||||
for (int i = 0; i < layerListNew.Count; i++)
|
||||
{
|
||||
Marshal.WriteIntPtr(layersModified, i * Marshal.SizeOf(typeof(IntPtr)), layerListNew[i]);
|
||||
}
|
||||
frameEndInfo.layers = layersModified;
|
||||
frameEndInfo.layerCount = (uint)layerListNew.Count;
|
||||
|
||||
res = xrEndFrame(session, ref frameEndInfo);
|
||||
return res;
|
||||
}
|
||||
private static XrFrameWaitInfo m_frameWaitInfo;
|
||||
private static XrFrameState m_frameState;
|
||||
[MonoPInvokeCallback(typeof(OpenXRHelper.xrWaitFrameDelegate))]
|
||||
private static int intercepted_xrWaitFrame(ulong session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
||||
{
|
||||
var xrWaitFrame = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrWaitFrameDelegate>(XrWaitFrame_prev);
|
||||
int res = xrWaitFrame(session, ref frameWaitInfo, ref frameState);
|
||||
m_frameWaitInfo = frameWaitInfo;
|
||||
m_frameState = frameState;
|
||||
return res;
|
||||
}
|
||||
public void GetOriginEndFrameLayerList(out List<IntPtr> layers)
|
||||
{
|
||||
layers = new List<IntPtr>(layerListOrigin);
|
||||
}
|
||||
|
||||
public void SubmitLayers(List<IntPtr> layers)
|
||||
{
|
||||
layerListModified = new List<IntPtr>(layers);
|
||||
//UnityEngine.Debug.Log("####Update submit end " + layerListModified.Count);
|
||||
}
|
||||
public XrFrameState GetFrameState()
|
||||
{
|
||||
return m_frameState;
|
||||
}
|
||||
#endif
|
||||
|
||||
private XrInstance m_XrInstance = 0;
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
foreach (string kOpenxrExtensionString in kOpenxrExtensionStrings.Split(' '))
|
||||
{
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||
{
|
||||
WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||
|
||||
m_HTCPassthroughExtensionEnabled = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_XrInstanceCreated = true;
|
||||
m_XrInstance = xrInstance;
|
||||
DEBUG("OnInstanceCreate() " + m_XrInstance);
|
||||
|
||||
return GetXrFunctionDelegates(m_XrInstance);
|
||||
}
|
||||
|
||||
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||
{
|
||||
m_XrInstanceCreated = false;
|
||||
DEBUG("OnInstanceDestroy() " + m_XrInstance);
|
||||
}
|
||||
|
||||
private XrSystemId m_XrSystemId = 0;
|
||||
protected override void OnSystemChange(ulong xrSystem)
|
||||
{
|
||||
m_XrSystemId = xrSystem;
|
||||
DEBUG("OnSystemChange() " + m_XrSystemId);
|
||||
}
|
||||
|
||||
private bool m_XrSessionCreated = false;
|
||||
/// <summary>
|
||||
/// The XR session is created or not.
|
||||
/// </summary>
|
||||
public bool XrSessionCreated
|
||||
{
|
||||
get { return m_XrSessionCreated; }
|
||||
}
|
||||
private XrSession m_XrSession = 0;
|
||||
protected override void OnSessionCreate(ulong xrSession)
|
||||
{
|
||||
m_XrSession = xrSession;
|
||||
m_XrSessionCreated = true;
|
||||
DEBUG("OnSessionCreate() " + m_XrSession);
|
||||
}
|
||||
|
||||
private bool m_XrSessionEnding = false;
|
||||
/// <summary>
|
||||
/// The XR session is ending or not.
|
||||
/// </summary>
|
||||
public bool XrSessionEnding
|
||||
{
|
||||
get { return m_XrSessionEnding; }
|
||||
}
|
||||
|
||||
private XrSpace m_WorldLockSpaceOriginOnHead = 0, m_WorldLockSpaceOriginOnFloor = 0, m_HeadLockSpace = 0;
|
||||
private XrSpace WorldLockSpaceOriginOnHead
|
||||
{
|
||||
get { return m_WorldLockSpaceOriginOnHead; }
|
||||
}
|
||||
private XrSpace WorldLockSpaceOriginOnFloor
|
||||
{
|
||||
get { return m_WorldLockSpaceOriginOnFloor; }
|
||||
}
|
||||
private XrSpace HeadLockSpace
|
||||
{
|
||||
get { return m_HeadLockSpace; }
|
||||
}
|
||||
|
||||
protected override void OnSessionBegin(ulong xrSession)
|
||||
{
|
||||
m_XrSessionEnding = false;
|
||||
DEBUG("OnSessionBegin() " + m_XrSession);
|
||||
|
||||
// Enumerate supported reference space types and create the XrSpace.
|
||||
XrReferenceSpaceType[] spaces = new XrReferenceSpaceType[Enum.GetNames(typeof(XrReferenceSpaceType)).Count()];
|
||||
UInt32 spaceCountOutput;
|
||||
if (EnumerateReferenceSpaces(
|
||||
spaceCapacityInput: 0,
|
||||
spaceCountOutput: out spaceCountOutput,
|
||||
spaces: out spaces[0]) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("spaceCountOutput: " + spaceCountOutput);
|
||||
|
||||
Array.Resize(ref spaces, (int)spaceCountOutput);
|
||||
if (EnumerateReferenceSpaces(
|
||||
spaceCapacityInput: spaceCountOutput,
|
||||
spaceCountOutput: out spaceCountOutput,
|
||||
spaces: out spaces[0]) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL))
|
||||
{
|
||||
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoWorldLock;
|
||||
referenceSpaceCreateInfoWorldLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||
referenceSpaceCreateInfoWorldLock.next = IntPtr.Zero;
|
||||
referenceSpaceCreateInfoWorldLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_LOCAL;
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
|
||||
|
||||
if (CreateReferenceSpace(
|
||||
createInfo: ref referenceSpaceCreateInfoWorldLock,
|
||||
space: out m_WorldLockSpaceOriginOnHead) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("CreateReferenceSpace: " + m_WorldLockSpaceOriginOnHead);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace for world lock layers on head failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace no space type for world lock on head layers.");
|
||||
}
|
||||
|
||||
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE))
|
||||
{
|
||||
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoWorldLock;
|
||||
referenceSpaceCreateInfoWorldLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||
referenceSpaceCreateInfoWorldLock.next = IntPtr.Zero;
|
||||
referenceSpaceCreateInfoWorldLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_STAGE;
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
|
||||
referenceSpaceCreateInfoWorldLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
|
||||
|
||||
if (CreateReferenceSpace(
|
||||
createInfo: ref referenceSpaceCreateInfoWorldLock,
|
||||
space: out m_WorldLockSpaceOriginOnFloor) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("CreateReferenceSpace: " + m_WorldLockSpaceOriginOnFloor);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace for world lock layers on floor failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace no space type for world lock on floor layers.");
|
||||
}
|
||||
|
||||
if (spaces.Contains(XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_VIEW))
|
||||
{
|
||||
XrReferenceSpaceCreateInfo referenceSpaceCreateInfoHeadLock;
|
||||
referenceSpaceCreateInfoHeadLock.type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
||||
referenceSpaceCreateInfoHeadLock.next = IntPtr.Zero;
|
||||
referenceSpaceCreateInfoHeadLock.referenceSpaceType = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_VIEW;
|
||||
referenceSpaceCreateInfoHeadLock.poseInReferenceSpace.orientation = new XrQuaternionf(0, 0, 0, 1);
|
||||
referenceSpaceCreateInfoHeadLock.poseInReferenceSpace.position = new XrVector3f(0, 0, 0);
|
||||
|
||||
if (CreateReferenceSpace(
|
||||
createInfo: ref referenceSpaceCreateInfoHeadLock,
|
||||
space: out m_HeadLockSpace) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
//DEBUG("CreateReferenceSpace: " + m_HeadLockSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace for head lock layers failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("CreateReferenceSpace no space type for head lock layers.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("EnumerateReferenceSpaces(" + spaceCountOutput + ") failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("EnumerateReferenceSpaces(0) failed.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSessionEnd(ulong xrSession)
|
||||
{
|
||||
m_XrSessionEnding = true;
|
||||
DEBUG("OnSessionEnd() " + m_XrSession);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The delegate of Passthrough Session Destroy.
|
||||
/// </summary>
|
||||
public delegate void OnPassthroughSessionDestroyDelegate(int passthroughID);
|
||||
private Dictionary<int, OnPassthroughSessionDestroyDelegate> OnPassthroughSessionDestroyHandlerDictionary = new Dictionary<int, OnPassthroughSessionDestroyDelegate>();
|
||||
protected override void OnSessionDestroy(ulong xrSession)
|
||||
{
|
||||
m_XrSessionCreated = false;
|
||||
DEBUG("OnSessionDestroy() " + xrSession);
|
||||
#if UNITY_ANDROID
|
||||
//Notify that all passthrough layers should be destroyed
|
||||
List<int> currentPassthroughIDs = PassthroughIDList;
|
||||
foreach (int passthroughID in currentPassthroughIDs)
|
||||
{
|
||||
OnPassthroughSessionDestroyDelegate OnPassthroughSessionDestroyHandler = OnPassthroughSessionDestroyHandlerDictionary[passthroughID];
|
||||
|
||||
OnPassthroughSessionDestroyHandler?.Invoke(passthroughID);
|
||||
}
|
||||
#endif
|
||||
#if UNITY_STANDALONE
|
||||
//Notify that all passthrough layers should be destroyed
|
||||
List<XrPassthroughHTC> currentPassthroughs = PassthroughList;
|
||||
foreach (XrPassthroughHTC passthrough in currentPassthroughs)
|
||||
{
|
||||
DestroyPassthroughHTC(passthrough);
|
||||
}
|
||||
#endif
|
||||
if (m_HeadLockSpace != 0)
|
||||
{
|
||||
DestroySpace(m_HeadLockSpace);
|
||||
m_HeadLockSpace = 0;
|
||||
}
|
||||
if (m_WorldLockSpaceOriginOnFloor != 0)
|
||||
{
|
||||
DestroySpace(m_WorldLockSpaceOriginOnFloor);
|
||||
m_WorldLockSpaceOriginOnFloor = 0;
|
||||
}
|
||||
if (m_WorldLockSpaceOriginOnHead != 0)
|
||||
{
|
||||
DestroySpace(m_WorldLockSpaceOriginOnHead);
|
||||
m_WorldLockSpaceOriginOnHead = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current XR session state.
|
||||
/// </summary>
|
||||
public XrSessionState XrSessionCurrentState
|
||||
{
|
||||
get { return m_XrSessionNewState; }
|
||||
}
|
||||
private XrSessionState m_XrSessionNewState = XrSessionState.XR_SESSION_STATE_UNKNOWN;
|
||||
private XrSessionState m_XrSessionOldState = XrSessionState.XR_SESSION_STATE_UNKNOWN;
|
||||
protected override void OnSessionStateChange(int oldState, int newState)
|
||||
{
|
||||
DEBUG("OnSessionStateChange() oldState: " + oldState + " newState:" + newState);
|
||||
|
||||
if (Enum.IsDefined(typeof(XrSessionState), oldState))
|
||||
{
|
||||
m_XrSessionOldState = (XrSessionState)oldState;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG("OnSessionStateChange() oldState undefined");
|
||||
}
|
||||
|
||||
if (Enum.IsDefined(typeof(XrSessionState), newState))
|
||||
{
|
||||
m_XrSessionNewState = (XrSessionState)newState;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG("OnSessionStateChange() newState undefined");
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OpenXR function delegates
|
||||
/// xrGetInstanceProcAddr
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||
#if UNITY_STANDALONE
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate Intercept_xrGetInstanceProcAddr =
|
||||
new OpenXRHelper.xrGetInstanceProcAddrDelegate(InterceptXrEndFrame_xrGetInstanceProcAddr);
|
||||
#endif
|
||||
private static OpenXRHelper.xrEndFrameDelegate m_intercept_xrEndFrame;
|
||||
private static OpenXRHelper.xrWaitFrameDelegate m_intercept_xrWaitFrame;
|
||||
/// xrGetSystemProperties
|
||||
OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties;
|
||||
/// <summary>
|
||||
/// Helper function to get this feature' properties.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||
/// </summary>
|
||||
public XrResult GetSystemProperties(ref XrSystemProperties properties)
|
||||
{
|
||||
if (m_XrInstanceCreated)
|
||||
{
|
||||
return xrGetSystemProperties(m_XrInstance, m_XrSystemId, ref properties);
|
||||
}
|
||||
|
||||
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||
}
|
||||
|
||||
/// xrEnumerateReferenceSpaces
|
||||
OpenXRHelper.xrEnumerateReferenceSpacesDelegate xrEnumerateReferenceSpaces;
|
||||
public XrResult EnumerateReferenceSpaces(UInt32 spaceCapacityInput, out UInt32 spaceCountOutput, out XrReferenceSpaceType spaces)
|
||||
{
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
spaceCountOutput = 0;
|
||||
spaces = XrReferenceSpaceType.XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT;
|
||||
return XrResult.XR_ERROR_SESSION_NOT_RUNNING;
|
||||
}
|
||||
|
||||
return xrEnumerateReferenceSpaces(m_XrSession, spaceCapacityInput, out spaceCountOutput, out spaces);
|
||||
}
|
||||
|
||||
/// xrCreateReferenceSpace
|
||||
OpenXRHelper.xrCreateReferenceSpaceDelegate xrCreateReferenceSpace;
|
||||
/// <summary>
|
||||
/// Creates a reference space
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrCreateReferenceSpace.html">xrCreateReferenceSpace</see>
|
||||
/// </summary>
|
||||
public XrResult CreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out XrSpace space)
|
||||
{
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
space = 0;
|
||||
return XrResult.XR_ERROR_SESSION_NOT_RUNNING;
|
||||
}
|
||||
|
||||
return xrCreateReferenceSpace(m_XrSession, ref createInfo, out space);
|
||||
}
|
||||
|
||||
/// xrDestroySpace
|
||||
OpenXRHelper.xrDestroySpaceDelegate xrDestroySpace;
|
||||
private XrResult DestroySpace(XrSpace space)
|
||||
{
|
||||
if (space != 0)
|
||||
{
|
||||
return xrDestroySpace(space);
|
||||
}
|
||||
return XrResult.XR_ERROR_REFERENCE_SPACE_UNSUPPORTED;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
private List<XrPassthroughHTC> passthroughList = new List<XrPassthroughHTC>();
|
||||
public List<XrPassthroughHTC> PassthroughList { get { return new List<XrPassthroughHTC>(passthroughList); } }
|
||||
ViveCompositionLayerHelper.xrCreatePassthroughHTCDelegate xrCreatePassthroughHTC;
|
||||
public XrResult CreatePassthroughHTC(XrPassthroughCreateInfoHTC createInfo, out XrPassthroughHTC passthrough)
|
||||
{
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
ERROR("CreatePassthroughHTC() XR_ERROR_SESSION_LOST.");
|
||||
passthrough = 0;
|
||||
return XrResult.XR_ERROR_SESSION_LOST;
|
||||
}
|
||||
if (!m_XrInstanceCreated)
|
||||
{
|
||||
ERROR("CreatePassthroughHTC() XR_ERROR_INSTANCE_LOST.");
|
||||
passthrough = 0;
|
||||
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||
}
|
||||
|
||||
XrResult res = xrCreatePassthroughHTC(m_XrSession, createInfo, out passthrough);
|
||||
if (res == XrResult.XR_SUCCESS)
|
||||
{
|
||||
passthroughList.Add(passthrough);
|
||||
passthroughIDList.Add(((int)(ulong)passthrough));
|
||||
}
|
||||
else
|
||||
ERROR("CreatePassthroughHTC() "+res);
|
||||
return res;
|
||||
}
|
||||
|
||||
ViveCompositionLayerHelper.xrDestroyPassthroughHTCDelegate xrDestroyPassthroughHTC;
|
||||
public XrResult DestroyPassthroughHTC(XrPassthroughHTC passthrough)
|
||||
{
|
||||
DEBUG("Entry");
|
||||
XrResult res = xrDestroyPassthroughHTC(passthrough);
|
||||
if (res == XrResult.XR_SUCCESS)
|
||||
{
|
||||
passthroughList.Remove(passthrough);
|
||||
passthroughIDList.Remove(((int)(ulong)passthrough));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/// <summary>
|
||||
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XrSpace GetTrackingSpace()
|
||||
{
|
||||
var s = GetCurrentAppSpace();
|
||||
Debug.Log("VivePassthrough GetTrackingSpace() s=" + s);
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||
{
|
||||
/// xrGetInstanceProcAddr
|
||||
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrGetInstanceProcAddr.");
|
||||
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
||||
xrGetInstanceProcAddr,
|
||||
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrGetInstanceProcAddr");
|
||||
return false;
|
||||
}
|
||||
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
/// xrGetSystemProperties
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrGetSystemProperties", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrGetSystemProperties.");
|
||||
xrGetSystemProperties = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrGetSystemPropertiesDelegate)) as OpenXRHelper.xrGetSystemPropertiesDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrGetSystemProperties");
|
||||
return false;
|
||||
}
|
||||
/// xrEnumerateReferenceSpaces
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrEnumerateReferenceSpaces", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrEnumerateReferenceSpaces.");
|
||||
xrEnumerateReferenceSpaces = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrEnumerateReferenceSpacesDelegate)) as OpenXRHelper.xrEnumerateReferenceSpacesDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrEnumerateReferenceSpaces");
|
||||
return false;
|
||||
}
|
||||
/// xrCreateReferenceSpace
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrCreateReferenceSpace", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrCreateReferenceSpace.");
|
||||
xrCreateReferenceSpace = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrCreateReferenceSpaceDelegate)) as OpenXRHelper.xrCreateReferenceSpaceDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrCreateReferenceSpace");
|
||||
return false;
|
||||
}
|
||||
/// xrDestroySpace
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrDestroySpace", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrDestroySpace.");
|
||||
xrDestroySpace = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(OpenXRHelper.xrDestroySpaceDelegate)) as OpenXRHelper.xrDestroySpaceDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrDestroySpace");
|
||||
return false;
|
||||
}
|
||||
#if UNITY_STANDALONE
|
||||
/// xrCreatePassthroughHTC
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrCreatePassthroughHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrCreatePassthroughHTC.");
|
||||
xrCreatePassthroughHTC = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(ViveCompositionLayerHelper.xrCreatePassthroughHTCDelegate)) as ViveCompositionLayerHelper.xrCreatePassthroughHTCDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrCreatePassthroughHTC");
|
||||
return false;
|
||||
}
|
||||
/// xrCreatePassthroughHTC
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrDestroyPassthroughHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
DEBUG("Get function pointer of xrDestroyPassthroughHTC.");
|
||||
xrDestroyPassthroughHTC = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(ViveCompositionLayerHelper.xrDestroyPassthroughHTCDelegate)) as ViveCompositionLayerHelper.xrDestroyPassthroughHTCDelegate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("xrDestroyPassthroughHTC");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if UNITY_ANDROID
|
||||
if (HTCPassthrough_GetFuncAddrs(xrInstance, xrGetInstanceProcAddr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
DEBUG("Get function pointers in native.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("HTCPassthrough_GetFuncAddrs");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Wrapper Functions
|
||||
private const string ExtLib = "viveopenxr";
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_CreatePassthrough")]
|
||||
private static extern int VIVEOpenXR_HTCPassthrough_CreatePassthrough(XrSession session, LayerType layerType, PassthroughLayerForm layerForm, uint compositionDepth = 0);
|
||||
/// <summary>
|
||||
/// Create Passthrough.
|
||||
/// </summary>
|
||||
public int HTCPassthrough_CreatePassthrough(LayerType layerType, PassthroughLayerForm layerForm, OnPassthroughSessionDestroyDelegate onDestroyPassthroughHandler, uint compositionDepth = 0)
|
||||
{
|
||||
if (!m_XrSessionCreated || m_XrSession == 0)
|
||||
{
|
||||
ERROR("Xr Session not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_CreatePassthrough: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int passthroughID = VIVEOpenXR_HTCPassthrough_CreatePassthrough(m_XrSession, layerType, layerForm, compositionDepth);
|
||||
|
||||
if (passthroughID != 0)
|
||||
{
|
||||
passthroughIDList.Add(passthroughID);
|
||||
OnPassthroughSessionDestroyHandlerDictionary.Add(passthroughID, onDestroyPassthroughHandler);
|
||||
}
|
||||
|
||||
return passthroughID;
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetAlpha")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetAlpha(int passthroughID, float alpha);
|
||||
/// <summary>
|
||||
/// Set Passthough Alpha.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetAlpha(int passthroughID, float alpha)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetAlpha: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetAlpha(passthroughID, alpha);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetLayerType")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetLayerType(int passthroughID, LayerType layerType, uint compositionDepth = 0);
|
||||
/// <summary>
|
||||
/// Set Passthough Layer Type.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetLayerType(int passthroughID, LayerType layerType, uint compositionDepth = 0)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetLayerType: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetLayerType(passthroughID, layerType, compositionDepth);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMesh")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMesh(int passthroughID, uint vertexCount, [In, Out] XrVector3f[] vertexBuffer, uint indexCount, [In, Out] uint[] indexBuffer);
|
||||
/// <summary>
|
||||
/// Set Passthough Mesh.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetMesh(int passthroughID, uint vertexCount, [In, Out] XrVector3f[] vertexBuffer, uint indexCount, [In, Out] uint[] indexBuffer)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetMesh: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetMesh(passthroughID, vertexCount, vertexBuffer, indexCount, indexBuffer);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransform")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransform(int passthroughID, XrSpace meshSpace, XrPosef meshPose, XrVector3f meshScale);
|
||||
/// <summary>
|
||||
/// Set Passthough Mesh Transform.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetMeshTransform(int passthroughID, XrSpace meshSpace, XrPosef meshPose, XrVector3f meshScale)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetMeshTransform: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetMeshTransform(passthroughID, meshSpace, meshPose, meshScale);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformSpace")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformSpace(int passthroughID, XrSpace meshSpace);
|
||||
/// <summary>
|
||||
/// Set Passthough Mesh Transform Space.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetMeshTransformSpace(int passthroughID, XrSpace meshSpace)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetMeshTransformSpace: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetMeshTransformSpace(passthroughID, meshSpace);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformPosition")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformPosition(int passthroughID, XrVector3f meshPosition);
|
||||
/// <summary>
|
||||
/// Set Passthough Mesh Transform Position.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetMeshTransformPosition(int passthroughID, XrVector3f meshPosition)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetMeshTransformPosition: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetMeshTransformPosition(passthroughID, meshPosition);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformOrientation")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformOrientation(int passthroughID, XrQuaternionf meshOrientation);
|
||||
/// <summary>
|
||||
/// Set Passthough Mesh Transform orientation.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetMeshTransformOrientation(int passthroughID, XrQuaternionf meshOrientation)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetMeshTransformOrientation: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetMeshTransformOrientation(passthroughID, meshOrientation);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_SetMeshTransformScale")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_SetMeshTransformScale(int passthroughID, XrVector3f meshScale);
|
||||
/// <summary>
|
||||
/// Set Passthough Mesh Transform scale.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_SetMeshTransformScale(int passthroughID, XrVector3f meshScale)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_SetMeshTransformScale: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_SetMeshTransformScale(passthroughID, meshScale);
|
||||
}
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_DestroyPassthrough")]
|
||||
private static extern bool VIVEOpenXR_HTCPassthrough_DestroyPassthrough(int passthroughID);
|
||||
/// <summary>
|
||||
/// Destroy Passthough.
|
||||
/// </summary>
|
||||
public bool HTCPassthrough_DestroyPassthrough(int passthroughID)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("HTCPassthrough_DestroyPassthrough: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool destroyed = VIVEOpenXR_HTCPassthrough_DestroyPassthrough(passthroughID);
|
||||
|
||||
if (destroyed)
|
||||
{
|
||||
passthroughIDList.Remove(passthroughID);
|
||||
OnPassthroughSessionDestroyHandlerDictionary.Remove(passthroughID);
|
||||
}
|
||||
|
||||
return destroyed;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hook native functions
|
||||
|
||||
[DllImportAttribute(ExtLib, EntryPoint = "htcpassthrough_GetFuncAddrs")]
|
||||
private static extern XrResult VIVEOpenXR_HTCPassthrough_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr);
|
||||
private XrResult HTCPassthrough_GetFuncAddrs(XrInstance xrInstance, IntPtr xrGetInstanceProcAddrFuncPtr)
|
||||
{
|
||||
if (!HTCPassthroughExtensionEnabled)
|
||||
{
|
||||
ERROR("VIVEOpenXR_HTCPassthrough_GetFuncAddrs: " + kOpenxrExtensionStrings + " is NOT enabled.");
|
||||
return XrResult.XR_ERROR_FEATURE_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return VIVEOpenXR_HTCPassthrough_GetFuncAddrs(xrInstance, xrGetInstanceProcAddrFuncPtr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Funcs
|
||||
/// <summary>
|
||||
/// Helper function to get XrSpace from space type.
|
||||
/// </summary>
|
||||
public XrSpace GetXrSpaceFromSpaceType(ProjectedPassthroughSpaceType spaceType)
|
||||
{
|
||||
XrSpace meshSpace = 0;
|
||||
switch (spaceType)
|
||||
{
|
||||
case ProjectedPassthroughSpaceType.Headlock:
|
||||
meshSpace = HeadLockSpace;
|
||||
break;
|
||||
case ProjectedPassthroughSpaceType.Worldlock:
|
||||
default:
|
||||
XRInputSubsystem subsystem = null;
|
||||
SubsystemManager.GetInstances(inputSubsystems);
|
||||
if (inputSubsystems.Count > 0)
|
||||
{
|
||||
subsystem = inputSubsystems[0];
|
||||
}
|
||||
|
||||
if (subsystem != null)
|
||||
{
|
||||
TrackingOriginModeFlags trackingOriginMode = subsystem.GetTrackingOriginMode();
|
||||
|
||||
switch (trackingOriginMode)
|
||||
{
|
||||
default:
|
||||
case TrackingOriginModeFlags.Floor:
|
||||
meshSpace = WorldLockSpaceOriginOnFloor;
|
||||
break;
|
||||
case TrackingOriginModeFlags.Device:
|
||||
meshSpace = WorldLockSpaceOriginOnHead;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
meshSpace = WorldLockSpaceOriginOnFloor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return meshSpace;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e78888b9743d564482402fe284c9126
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user