version 2.0.0
This commit is contained in:
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,347 @@
|
||||
// "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."
|
||||
|
||||
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,584 @@
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Planar, onDestroyPassthroughSessionHandler, compositionDepth);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler, compositionDepth);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
passthroughID = passthroughFeature.HTCPassthrough_CreatePassthrough(layerType, PassthroughLayerForm.Projected, onDestroyPassthroughSessionHandler);
|
||||
|
||||
if (passthroughID == 0)
|
||||
{
|
||||
ERROR("Failed to create projected pasthrough");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPassthroughAlpha(passthroughID, alpha);
|
||||
}
|
||||
|
||||
return passthroughID;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
return passthroughFeature.HTCPassthrough_DestroyPassthrough(passthroughID);
|
||||
}
|
||||
|
||||
/// <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 (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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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];
|
||||
}
|
||||
|
||||
//Note: Ignore Clock-Wise definition of index buffer for now as passthrough extension does not have back-face culling
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetMesh(passthroughID, (uint)vertexBuffer.Length, vertexBufferXrVector, (uint)indexBuffer.Length, indexBufferUint); ;
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransform(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType), meshXrPose, meshXrScale);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetLayerType(passthroughID, layerType, compositionDepth);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformSpace(passthroughID, passthroughFeature.GetXrSpaceFromSpaceType(spaceType));
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformPosition(passthroughID, OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR));
|
||||
}
|
||||
|
||||
/// <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));
|
||||
}
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformOrientation(passthroughID, OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR));
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
return passthroughFeature.HTCPassthrough_SetMeshTransformScale(passthroughID, OpenXRHelper.ToOpenXRVector(meshScale, false));
|
||||
}
|
||||
|
||||
/// <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,185 @@
|
||||
// "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."
|
||||
|
||||
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,301 @@
|
||||
// "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."
|
||||
|
||||
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:
|
||||
Reference in New Issue
Block a user