// Copyright HTC Corporation All Rights Reserved. using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.XR.OpenXR; using System.Threading.Tasks; using VIVE.OpenXR; namespace VIVE.OpenXR.Passthrough { public static class PassthroughAPI { #region LOG const string LOG_TAG = "VIVE.OpenXR.Passthrough.PassthroughAPI"; static void DEBUG(string msg) { Debug.LogFormat("{0} {1}", LOG_TAG, msg); } static void WARNING(string msg) { Debug.LogWarningFormat("{0} {1}", LOG_TAG, msg); } static void ERROR(string msg) { Debug.LogErrorFormat("{0} {1}", LOG_TAG, msg); } #endregion private static VivePassthrough passthroughFeature = null; private static bool AssertFeature() { if (passthroughFeature == null) { passthroughFeature = OpenXRSettings.Instance.GetFeature(); } if (passthroughFeature) { return true; } return false; } #if UNITY_STANDALONE private static Dictionary passthrough2Layer = new Dictionary(); private static Dictionary passthrough2LayerPtr = new Dictionary(); private static Dictionary passthrough2IsUnderLay= new Dictionary(); private static Dictionary passthrough2meshTransform = new Dictionary(); private static Dictionary passthrough2meshTransformInfoPtr = new Dictionary(); #endif #region Public APIs /// /// Creates a fullscreen passthrough. /// Passthroughs will be destroyed automatically when the current is destroyed. /// /// The created /// The specifies whether the passthrough is an overlay or underlay. /// A delegate will be invoked when the current OpenXR Session is going to be destroyed. /// The alpha value of the passthrough layer within the range [0, 1] where 1 (Opaque) is default. /// The composition depth relative to other composition layers if present where 0 is default. /// XR_SUCCESS for success. public static XrResult CreatePlanarPassthrough(out XrPassthroughHTC passthrough, CompositionLayer.LayerType layerType, VivePassthrough.OnPassthroughSessionDestroyDelegate onDestroyPassthroughSessionHandler = null, float alpha = 1f, uint compositionDepth = 0) { passthrough = 0; XrResult res = XrResult.XR_ERROR_RUNTIME_FAILURE; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return res; } XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC( XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe. XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PLANAR_HTC ); #if UNITY_ANDROID res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough, layerType, compositionDepth, onDestroyPassthroughSessionHandler); DEBUG("CreatePlanarPassthrough() CreatePassthroughHTC result: " + res + ", passthrough: " + passthrough); #endif #if UNITY_STANDALONE res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough); if(res == XrResult.XR_SUCCESS) { XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC( in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC, in_next: IntPtr.Zero, in_alpha: alpha); XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC( in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC, in_next: IntPtr.Zero, in_layerFlags: (UInt64)XrCompositionLayerFlagBits.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT, in_space: 0, in_passthrough: passthrough, in_color: passthroughColor); passthrough2Layer.Add(passthrough, compositionLayerPassthrough); IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC))); passthrough2LayerPtr.Add(passthrough, layerPtr); if (layerType == CompositionLayer.LayerType.Underlay) passthrough2IsUnderLay.Add(passthrough, true); if (layerType == CompositionLayer.LayerType.Overlay) passthrough2IsUnderLay.Add(passthrough, false); } #endif if (res == XrResult.XR_SUCCESS) { SetPassthroughAlpha(passthrough, alpha); } return res; } /// /// Creates 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 is destroyed. /// /// The created /// The specifies whether the passthrough is an overlay or underlay. /// Positions of the vertices in the mesh. /// List of triangles represented by indices into the . /// The projected passthrough's /// Position of the mesh. /// Orientation of the mesh. /// Scale of the mesh. /// A delegate will be invoked when the current OpenXR Session is going to be destroyed. /// The alpha value of the passthrough layer within the range [0, 1] where 1 (Opaque) is default. /// The composition depth relative to other composition layers if present where 0 is default. /// Specify whether or not the position and rotation of the mesh transform have to be converted from tracking space to world space. /// Specify whether or not the parameters , , and have to be converted to OpenXR coordinate. /// XR_SUCCESS for success. public static XrResult CreateProjectedPassthrough(out XrPassthroughHTC passthrough, CompositionLayer.LayerType layerType, [In, Out] Vector3[] vertexBuffer, [In, Out] int[] indexBuffer, //For Mesh ProjectedPassthroughSpaceType spaceType, Vector3 meshPosition, Quaternion meshOrientation, Vector3 meshScale, //For Mesh Transform VivePassthrough.OnPassthroughSessionDestroyDelegate onDestroyPassthroughSessionHandler = null, float alpha = 1f, uint compositionDepth = 0, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true) { passthrough = 0; XrResult res = XrResult.XR_ERROR_RUNTIME_FAILURE; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return res; } if (vertexBuffer.Length < 3 || indexBuffer.Length % 3 != 0) //Must have at least 3 vertices and complete triangles { ERROR("Mesh data invalid."); return res; } XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC( XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe. XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC ); #if UNITY_STANDALONE res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough); if (res == XrResult.XR_SUCCESS) { XrPassthroughMeshTransformInfoHTC PassthroughMeshTransformInfo = new XrPassthroughMeshTransformInfoHTC( in_type: XrStructureType.XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC, in_next: IntPtr.Zero, in_vertexCount: 0, in_vertices: new XrVector3f[0], in_indexCount: 0, in_indices: new UInt32[0], in_baseSpace: XR_HTC_passthrough.Interop.GetTrackingSpace(), in_time: XR_HTC_passthrough.Interop.GetFrameState().predictedDisplayTime, in_pose: new XrPosef(), in_scale: new XrVector3f() ); IntPtr meshTransformInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC))); Marshal.StructureToPtr(PassthroughMeshTransformInfo, meshTransformInfoPtr, false); XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC( in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC, in_next: IntPtr.Zero, in_alpha: alpha); XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC( in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC, in_next: meshTransformInfoPtr, in_layerFlags: (UInt64)XrCompositionLayerFlagBits.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT, in_space: 0, in_passthrough: passthrough, in_color: passthroughColor); passthrough2meshTransform.Add(passthrough, PassthroughMeshTransformInfo); passthrough2meshTransformInfoPtr.Add(passthrough, meshTransformInfoPtr); passthrough2Layer.Add(passthrough, compositionLayerPassthrough); IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC))); passthrough2LayerPtr.Add(passthrough, layerPtr); if (layerType == CompositionLayer.LayerType.Underlay) passthrough2IsUnderLay.Add(passthrough, true); if (layerType == CompositionLayer.LayerType.Overlay) passthrough2IsUnderLay.Add(passthrough, false); } #endif #if UNITY_ANDROID res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough, layerType, compositionDepth, onDestroyPassthroughSessionHandler); DEBUG("CreateProjectedPassthrough() CreatePassthroughHTC result: " + res + ", passthrough: " + passthrough); #endif if (res == XrResult.XR_SUCCESS) { SetPassthroughAlpha(passthrough, alpha); SetProjectedPassthroughMesh(passthrough, vertexBuffer, indexBuffer, convertFromUnityToOpenXR); SetProjectedPassthroughMeshTransform(passthrough, spaceType, meshPosition, meshOrientation, meshScale, trackingToWorldSpace, convertFromUnityToOpenXR); } return res; } /// /// Creates 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 is destroyed. /// /// The created /// The specifies whether the passthrough is an overlay or underlay. /// A delegate will be invoked when the current OpenXR Session is going to be destroyed. /// The alpha value of the passthrough layer within the range [0, 1] where 1 (Opaque) is default. /// The composition depth relative to other composition layers if present where 0 is default. /// XR_SUCCESS for success. public static XrResult CreateProjectedPassthrough(out XrPassthroughHTC passthrough, CompositionLayer.LayerType layerType, VivePassthrough.OnPassthroughSessionDestroyDelegate onDestroyPassthroughSessionHandler = null, float alpha = 1f, uint compositionDepth = 0) { passthrough = 0; XrResult res = XrResult.XR_ERROR_RUNTIME_FAILURE; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return res; } XrPassthroughCreateInfoHTC createInfo = new XrPassthroughCreateInfoHTC( XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, new IntPtr(6), //Enter IntPtr(0) for backward compatibility (using createPassthrough to enable the passthrough feature), or enter IntPtr(6) to enable the passthrough feature based on the layer submitted to endframe. XrPassthroughFormHTC.XR_PASSTHROUGH_FORM_PROJECTED_HTC ); #if UNITY_STANDALONE res = XR_HTC_passthrough.xrCreatePassthroughHTC(createInfo, out passthrough); if (res == XrResult.XR_SUCCESS) { XrPassthroughMeshTransformInfoHTC PassthroughMeshTransformInfo = new XrPassthroughMeshTransformInfoHTC( in_type: XrStructureType.XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC, in_next: IntPtr.Zero, in_vertexCount: 0, in_vertices: new XrVector3f[0], in_indexCount: 0, in_indices: new UInt32[0], in_baseSpace: XR_HTC_passthrough.Interop.GetTrackingSpace(), in_time: XR_HTC_passthrough.Interop.GetFrameState().predictedDisplayTime, in_pose: new XrPosef(), in_scale: new XrVector3f() ); IntPtr meshTransformInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrPassthroughMeshTransformInfoHTC))); Marshal.StructureToPtr(PassthroughMeshTransformInfo, meshTransformInfoPtr, false); XrPassthroughColorHTC passthroughColor = new XrPassthroughColorHTC( in_type: XrStructureType.XR_TYPE_PASSTHROUGH_COLOR_HTC, in_next: IntPtr.Zero, in_alpha: alpha); XrCompositionLayerPassthroughHTC compositionLayerPassthrough = new XrCompositionLayerPassthroughHTC( in_type: XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC, in_next: meshTransformInfoPtr, in_layerFlags: (UInt64)XrCompositionLayerFlagBits.XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT, in_space: 0, in_passthrough: passthrough, in_color: passthroughColor); passthrough2meshTransform.Add(passthrough, PassthroughMeshTransformInfo); passthrough2meshTransformInfoPtr.Add(passthrough, meshTransformInfoPtr); passthrough2Layer.Add(passthrough, compositionLayerPassthrough); IntPtr layerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XrCompositionLayerPassthroughHTC))); passthrough2LayerPtr.Add(passthrough, layerPtr); if (layerType == CompositionLayer.LayerType.Underlay) passthrough2IsUnderLay.Add(passthrough, true); if (layerType == CompositionLayer.LayerType.Overlay) passthrough2IsUnderLay.Add(passthrough, false); } #endif #if UNITY_ANDROID res = passthroughFeature.CreatePassthroughHTC(createInfo, out passthrough, layerType, onDestroyPassthroughSessionHandler); DEBUG("CreateProjectedPassthrough() CreatePassthroughHTC result: " + res + ", passthrough: " + passthrough); #endif if (res == XrResult.XR_SUCCESS) { SetPassthroughAlpha(passthrough, alpha); } return res; } #if UNITY_STANDALONE private static async void SubmitLayer() { await Task.Run(() => { int layerListCount = 0; while(layerListCount == 0) { System.Threading.Thread.Sleep(1); XR_HTC_passthrough.Interop.GetOriginEndFrameLayerList(out List layerList);//GetOriginEndFrameLayers layerListCount = layerList.Count; foreach (var passthrough in passthrough2IsUnderLay.Keys) { //Get and submit layer list if (layerListCount != 0) { Marshal.StructureToPtr(passthrough2Layer[passthrough], passthrough2LayerPtr[passthrough], false); if (passthrough2IsUnderLay[passthrough]) layerList.Insert(0, passthrough2LayerPtr[passthrough]); else layerList.Insert(1, passthrough2LayerPtr[passthrough]); } } if(layerListCount != 0) XR_HTC_passthrough.Interop.SubmitLayers(layerList); } }); } #endif /// /// To Destroying a passthrough. /// You should call this function when the delegate is invoked. /// /// The created /// XR_SUCCESS for success. public static XrResult DestroyPassthrough(XrPassthroughHTC passthrough) { XrResult res = XrResult.XR_ERROR_RUNTIME_FAILURE; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return res; } if (!passthroughFeature.PassthroughList.Contains(passthrough)) { ERROR("Passthrough to be destroyed not found"); return res; } #if UNITY_STANDALONE XrPassthroughHTC pt = passthrough2Layer[passthrough].passthrough; XR_HTC_passthrough.xrDestroyPassthroughHTC(pt); passthrough2IsUnderLay.Remove(passthrough); SubmitLayer(); passthrough2Layer.Remove(pt); if(passthrough2LayerPtr.ContainsKey(passthrough)) Marshal.FreeHGlobal(passthrough2LayerPtr[passthrough]); passthrough2LayerPtr.Remove(passthrough); if(passthrough2meshTransformInfoPtr.ContainsKey(passthrough)) Marshal.FreeHGlobal(passthrough2meshTransformInfoPtr[passthrough]); passthrough2meshTransformInfoPtr.Remove(passthrough); passthrough2meshTransform.Remove(passthrough); res = XrResult.XR_SUCCESS; #elif UNITY_ANDROID res = passthroughFeature.DestroyPassthroughHTC(passthrough); DEBUG("DestroyPassthrough() DestroyPassthroughHTC result: " + res + ", passthrough: " + passthrough); #endif return res; } /// /// Modifies the opacity of a specific passthrough layer. /// Can be used for both Planar and Projected passthroughs. /// /// The created /// The alpha value of the passthrough layer within the range [0, 1] where 1 (Opaque) is default. /// /// 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. /// Default is true. /// /// XR_SUCCESS for success. public static bool SetPassthroughAlpha(XrPassthroughHTC passthrough, float alpha, bool autoClamp = true) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } #if UNITY_ANDROID if (autoClamp) { ret = passthroughFeature.SetAlpha(passthrough, Mathf.Clamp01(alpha)); } else { if (alpha < 0f || alpha > 1f) { ERROR("SetPassthroughAlpha: Alpha out of range"); return ret; } ret = passthroughFeature.SetAlpha(passthrough, alpha); } DEBUG("SetPassthroughAlpha() SetAlpha result: " + ret + ", passthrough: " + passthrough); #endif #if UNITY_STANDALONE if (passthrough2Layer.ContainsKey(passthrough)) { XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthrough]; layer.color.alpha = alpha; passthrough2Layer[passthrough] = layer; SubmitLayer(); ret = true; } else ret = false; #endif return ret; } /// /// Modifies the mesh data of a projected passthrough layer. /// /// The created /// Positions of the vertices in the mesh. /// List of triangles represented by indices into the . /// Specify whether or not the parameters , , and have to be converted to OpenXR coordinate. /// XR_SUCCESS for success. public static bool SetProjectedPassthroughMesh(XrPassthroughHTC passthrough, [In, Out] Vector3[] vertexBuffer, [In, Out] int[] indexBuffer, bool convertFromUnityToOpenXR = true) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } if (vertexBuffer.Length < 3 || indexBuffer.Length % 3 != 0) //Must have at least 3 vertices and complete triangles { ERROR("Mesh data invalid."); return ret; } XrVector3f[] vertexBufferXrVector = new XrVector3f[vertexBuffer.Length]; for (int i = 0; i < vertexBuffer.Length; i++) { vertexBufferXrVector[i] = OpenXRHelper.ToOpenXRVector(vertexBuffer[i], convertFromUnityToOpenXR); } uint[] indexBufferUint = new uint[indexBuffer.Length]; for (int i = 0; i < indexBuffer.Length; i++) { indexBufferUint[i] = (uint)indexBuffer[i]; } #if UNITY_STANDALONE if (passthrough2meshTransformInfoPtr.ContainsKey(passthrough)) { XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthrough]; MeshTransformInfo.vertexCount = (uint)vertexBuffer.Length; MeshTransformInfo.vertices = vertexBufferXrVector; MeshTransformInfo.indexCount = (uint)indexBuffer.Length; MeshTransformInfo.indices = indexBufferUint; passthrough2meshTransform[passthrough] = MeshTransformInfo; Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthrough], false); XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthrough]; layer.next = passthrough2meshTransformInfoPtr[passthrough]; passthrough2Layer[passthrough] = layer; SubmitLayer(); ret = true; } else ret = false; #endif //Note: Ignore Clock-Wise definition of index buffer for now as passthrough extension does not have back-face culling #if UNITY_ANDROID ret = passthroughFeature.SetMesh(passthrough, (uint)vertexBuffer.Length, vertexBufferXrVector, (uint)indexBuffer.Length, indexBufferUint); ; DEBUG("SetProjectedPassthroughMesh() SetMesh result: " + ret + ", passthrough: " + passthrough); #endif return ret; } /// /// Modifies the mesh transform of a projected passthrough layer. /// /// The created /// The projected passthrough's /// Position of the mesh. /// Orientation of the mesh. /// Scale of the mesh. /// Specify whether or not the position and rotation of the mesh transform have to be converted from tracking space to world space. /// Specify whether or not the parameters , , and have to be converted to OpenXR coordinate. /// XR_SUCCESS for success. public static bool SetProjectedPassthroughMeshTransform(XrPassthroughHTC passthrough, ProjectedPassthroughSpaceType spaceType, Vector3 meshPosition, Quaternion meshOrientation, Vector3 meshScale, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } Vector3 trackingSpaceMeshPosition = meshPosition; Quaternion trackingSpaceMeshRotation = meshOrientation; TrackingSpaceOrigin currentTrackingSpaceOrigin = TrackingSpaceOrigin.Instance; if (currentTrackingSpaceOrigin != null && trackingToWorldSpace) //Apply origin correction to the mesh pose { Matrix4x4 trackingSpaceOriginTRS = Matrix4x4.TRS(currentTrackingSpaceOrigin.transform.position, currentTrackingSpaceOrigin.transform.rotation, Vector3.one); Matrix4x4 worldSpaceLayerPoseTRS = Matrix4x4.TRS(meshPosition, meshOrientation, Vector3.one); Matrix4x4 trackingSpaceLayerPoseTRS = trackingSpaceOriginTRS.inverse * worldSpaceLayerPoseTRS; trackingSpaceMeshPosition = trackingSpaceLayerPoseTRS.GetColumn(3); //4th Column of TRS Matrix is the position trackingSpaceMeshRotation = Quaternion.LookRotation(trackingSpaceLayerPoseTRS.GetColumn(2), trackingSpaceLayerPoseTRS.GetColumn(1)); } XrPosef meshXrPose; meshXrPose.position = OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR); meshXrPose.orientation = OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR); XrVector3f meshXrScale = OpenXRHelper.ToOpenXRVector(meshScale, false); #if UNITY_STANDALONE if (passthrough2meshTransformInfoPtr.ContainsKey(passthrough)) { XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthrough]; MeshTransformInfo.pose = meshXrPose; MeshTransformInfo.scale = meshXrScale; passthrough2meshTransform[passthrough] = MeshTransformInfo; Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthrough], false); XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthrough]; layer.next = passthrough2meshTransformInfoPtr[passthrough]; passthrough2Layer[passthrough] = layer; SubmitLayer(); ret = true; } else ret = false; #endif #if UNITY_ANDROID ret = passthroughFeature.SetMeshTransform(passthrough, passthroughFeature.GetXrSpaceFromSpaceType(spaceType), meshXrPose, meshXrScale); DEBUG("SetProjectedPassthroughMeshTransform() SetMeshTransform result: " + ret + ", passthrough: " + passthrough); #endif return ret; } /// /// Modifies layer type and composition depth of a passthrough layer. /// /// The created /// The specifies whether the passthrough is an overlay or underlay. /// The composition depth relative to other composition layers if present where 0 is default. /// XR_SUCCESS for success. public static bool SetPassthroughLayerType(XrPassthroughHTC passthrough, CompositionLayer.LayerType layerType, uint compositionDepth = 0) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } #if UNITY_STANDALONE if (passthrough2IsUnderLay.ContainsKey(passthrough)) { passthrough2IsUnderLay[passthrough] = layerType == CompositionLayer.LayerType.Underlay ? true : false; SubmitLayer(); ret = true; } else ret = false; #endif #if UNITY_ANDROID ret = passthroughFeature.SetLayerType(passthrough, layerType, compositionDepth); DEBUG("SetPassthroughLayerType() SetLayerType result: " + ret + ", passthrough: " + passthrough); #endif return ret; } /// /// Modifies the space of a projected passthrough layer. /// /// The created /// The projected passthrough's /// XR_SUCCESS for success. public static bool SetProjectedPassthroughSpaceType(XrPassthroughHTC passthrough, ProjectedPassthroughSpaceType spaceType) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } #if UNITY_STANDALONE if (passthrough2meshTransformInfoPtr.ContainsKey(passthrough)) { XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthrough]; MeshTransformInfo.baseSpace = passthroughFeature.GetXrSpaceFromSpaceType(spaceType); passthrough2meshTransform[passthrough] = MeshTransformInfo; Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthrough], false); XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthrough]; layer.next = passthrough2meshTransformInfoPtr[passthrough]; passthrough2Layer[passthrough] = layer; SubmitLayer(); ret = true; } else ret = false; #endif #if UNITY_ANDROID ret = passthroughFeature.SetMeshTransformSpace(passthrough, passthroughFeature.GetXrSpaceFromSpaceType(spaceType)); DEBUG("SetProjectedPassthroughSpaceType() SetMeshTransformSpace result: " + ret + ", passthrough: " + passthrough); #endif return ret; } /// /// Modifies the mesh position of a projected passthrough layer. /// /// The created /// Position of the mesh. /// Specify whether or not the position and rotation of the mesh transform have to be converted from tracking space to world space. /// Specify whether or not the parameters , , and have to be converted to OpenXR coordinate. /// XR_SUCCESS for success. public static bool SetProjectedPassthroughMeshPosition(XrPassthroughHTC passthrough, Vector3 meshPosition, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } Vector3 trackingSpaceMeshPosition = meshPosition; TrackingSpaceOrigin currentTrackingSpaceOrigin = TrackingSpaceOrigin.Instance; if (currentTrackingSpaceOrigin != null && trackingToWorldSpace) //Apply origin correction to the mesh pose { Matrix4x4 trackingSpaceOriginTRS = Matrix4x4.TRS(currentTrackingSpaceOrigin.transform.position, Quaternion.identity, Vector3.one); Matrix4x4 worldSpaceLayerPoseTRS = Matrix4x4.TRS(meshPosition, Quaternion.identity, Vector3.one); Matrix4x4 trackingSpaceLayerPoseTRS = trackingSpaceOriginTRS.inverse * worldSpaceLayerPoseTRS; trackingSpaceMeshPosition = trackingSpaceLayerPoseTRS.GetColumn(3); //4th Column of TRS Matrix is the position } #if UNITY_STANDALONE if (passthrough2meshTransformInfoPtr.ContainsKey(passthrough)) { XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthrough]; XrPosef meshXrPose = MeshTransformInfo.pose; meshXrPose.position = OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR); ; MeshTransformInfo.pose = meshXrPose; passthrough2meshTransform[passthrough] = MeshTransformInfo; Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthrough], false); XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthrough]; layer.next = passthrough2meshTransformInfoPtr[passthrough]; passthrough2Layer[passthrough] = layer; SubmitLayer(); ret = true; } else ret = false; #endif #if UNITY_ANDROID ret = passthroughFeature.SetMeshTransformPosition(passthrough, OpenXRHelper.ToOpenXRVector(trackingSpaceMeshPosition, convertFromUnityToOpenXR)); DEBUG("SetProjectedPassthroughMeshPosition() SetMeshTransformPosition result: " + ret + ", passthrough: " + passthrough); #endif return ret; } /// /// Modifies the mesh orientation of a projected passthrough layer. /// /// The created /// Orientation of the mesh. /// Specify whether or not the position and rotation of the mesh transform have to be converted from tracking space to world space. /// Specify whether or not the parameters , , and have to be converted to OpenXR coordinate. /// XR_SUCCESS for success. public static bool SetProjectedPassthroughMeshOrientation(XrPassthroughHTC passthrough, Quaternion meshOrientation, bool trackingToWorldSpace = true, bool convertFromUnityToOpenXR = true) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } Quaternion trackingSpaceMeshRotation = meshOrientation; TrackingSpaceOrigin currentTrackingSpaceOrigin = TrackingSpaceOrigin.Instance; if (currentTrackingSpaceOrigin != null && trackingToWorldSpace) //Apply origin correction to the mesh pose { Matrix4x4 trackingSpaceOriginTRS = Matrix4x4.TRS(Vector3.zero, currentTrackingSpaceOrigin.transform.rotation, Vector3.one); Matrix4x4 worldSpaceLayerPoseTRS = Matrix4x4.TRS(Vector3.zero, meshOrientation, Vector3.one); Matrix4x4 trackingSpaceLayerPoseTRS = trackingSpaceOriginTRS.inverse * worldSpaceLayerPoseTRS; trackingSpaceMeshRotation = Quaternion.LookRotation(trackingSpaceLayerPoseTRS.GetColumn(2), trackingSpaceLayerPoseTRS.GetColumn(1)); } #if UNITY_STANDALONE if (passthrough2meshTransformInfoPtr.ContainsKey(passthrough)) { XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthrough]; XrPosef meshXrPose = MeshTransformInfo.pose; meshXrPose.orientation = OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR); MeshTransformInfo.pose = meshXrPose; passthrough2meshTransform[passthrough] = MeshTransformInfo; Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthrough], false); XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthrough]; layer.next = passthrough2meshTransformInfoPtr[passthrough]; passthrough2Layer[passthrough] = layer; SubmitLayer(); ret = true; } else ret = false; #endif #if UNITY_ANDROID ret = passthroughFeature.SetMeshTransformOrientation(passthrough, OpenXRHelper.ToOpenXRQuaternion(trackingSpaceMeshRotation, convertFromUnityToOpenXR)); DEBUG("SetProjectedPassthroughMeshOrientation() SetMeshTransformOrientation result: " + ret + ", passthrough: " + passthrough); #endif return ret; } /// /// Modifies the mesh scale of a passthrough layer. /// /// The created /// Scale of the mesh. /// XR_SUCCESS for success. public static bool SetProjectedPassthroughScale(XrPassthroughHTC passthrough, Vector3 meshScale) { bool ret = false; if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return ret; } #if UNITY_STANDALONE if (passthrough2meshTransformInfoPtr.ContainsKey(passthrough)) { XrPassthroughMeshTransformInfoHTC MeshTransformInfo = passthrough2meshTransform[passthrough]; MeshTransformInfo.scale = OpenXRHelper.ToOpenXRVector(meshScale, false); passthrough2meshTransform[passthrough] = MeshTransformInfo; Marshal.StructureToPtr(MeshTransformInfo, passthrough2meshTransformInfoPtr[passthrough], false); XrCompositionLayerPassthroughHTC layer = passthrough2Layer[passthrough]; layer.next = passthrough2meshTransformInfoPtr[passthrough]; passthrough2Layer[passthrough] = layer; SubmitLayer(); ret = true; } else ret = false; #endif #if UNITY_ANDROID ret = passthroughFeature.SetMeshTransformScale(passthrough, OpenXRHelper.ToOpenXRVector(meshScale, false)); DEBUG("SetProjectedPassthroughScale() SetMeshTransformScale result: " + ret + ", passthrough: " + passthrough); #endif return ret; } /// /// To get the list of IDs of active passthrough layers. /// /// /// The a copy of the list of IDs of active passthrough layers. /// public static List GetCurrentPassthroughLayerIDs() { if (!AssertFeature()) { ERROR("HTC_Passthrough feature instance not found."); return null; } return passthroughFeature.PassthroughList; } #endregion } }