version 2.0.0

This commit is contained in:
srl87
2023-09-14 18:17:47 +08:00
parent 13e9d00b37
commit ca21423a06
953 changed files with 125887 additions and 21229 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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 HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System;
using System.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;
}
}
}
}
}

View File

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

View File

@@ -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
}
}

View File

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

View File

@@ -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 HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using AOT;
using 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];
}
}
}

View File

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

View File

@@ -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 HTCs SDK license agreement terms and
// conditions signed by you and all SDK and API requirements,
// specifications, and documentation provided by HTC to You."
using System.Collections;
using System.Collections.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,
}
}
}

View File

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