/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #if USING_XR_MANAGEMENT && (USING_XR_SDK_OCULUS || USING_XR_SDK_OPENXR) #define USING_XR_SDK #endif #if UNITY_2020_1_OR_NEWER #define REQUIRES_XR_SDK #endif using UnityEngine; using System; using System.Collections.Generic; using System.Runtime.InteropServices; #if USING_XR_SDK using UnityEngine.XR; using UnityEngine.Experimental.XR; #endif using InputTracking = UnityEngine.XR.InputTracking; using Node = UnityEngine.XR.XRNode; using NodeState = UnityEngine.XR.XRNodeState; using Device = UnityEngine.XR.XRDevice; /// /// Miscellaneous extension methods that any script can use. /// /// /// This class encapulsates several extension methods that help convert between the Quest's coordinate system and /// Unity's coorindate system. Use these methods to convert to and from the Quest's local tracking space and Unity's /// world space, for example. /// /// This class also contains several methods to facilitate native interop, as the native plugin uses a different /// coordinate system than Unity (for example, ). These methods are used by the /// Meta XR Core SDK, but typically should not necessary in application code. /// public static partial class OVRExtensions { /// /// Converts the given world-space to an in tracking space. /// /// /// The "tracking space" refers to the local coordinate system of the Quest device. This method uses a camera's /// world-space transform to calculate the transform between the Quest's tracking space and Unity's world space, /// then applies it to the to compute that transform in the device's tracking space. /// /// A transform that will be converted to a tracking space pose. /// The camera whose transform is driven by the HMD. This is often the main camera. /// Returns the pose of the in tracking space. public static OVRPose ToTrackingSpacePose(this Transform transform, Camera camera) { // Initializing to identity, but for all Oculus headsets, down below the pose will be initialized to the runtime's pose value, so identity will never be returned. OVRPose headPose = OVRPose.identity; Vector3 pos; Quaternion rot; if (OVRNodeStateProperties.GetNodeStatePropertyVector3(Node.Head, NodeStatePropertyType.Position, OVRPlugin.Node.Head, OVRPlugin.Step.Render, out pos)) headPose.position = pos; if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(Node.Head, NodeStatePropertyType.Orientation, OVRPlugin.Node.Head, OVRPlugin.Step.Render, out rot)) headPose.orientation = rot; var ret = headPose * transform.ToHeadSpacePose(camera); return ret; } /// /// Converts the given pose from tracking-space to world-space. /// /// /// \deprecated This method is obsolete. Use instead. /// /// The "tracking space" refers to the local coordinate system of the Quest device. This method uses the main /// camera's world-space transform to calculate the transform between the Quest's tracking space and Unity's world /// space, then applies it to the to compute that pose in Unity world space. /// /// The pose, in tracking-space, to convert to world space. /// The in world space. [Obsolete("ToWorldSpacePose should be invoked with an explicit mainCamera parameter")] public static OVRPose ToWorldSpacePose(this OVRPose trackingSpacePose) { return ToWorldSpacePose(trackingSpacePose, Camera.main); } /// /// Converts the given pose from tracking-space to world-space. /// /// /// The "tracking space" refers to the local coordinate system of the Quest device. This method uses /// 's world-space transform to calculate the transform between the Quest's tracking /// space and Unity's world space, then applies it to the to compute that pose /// in Unity world space. /// /// This is the inverse operation of . /// /// The pose to convert to world-space. /// The camera whose transform is driven by the HMD. This is often the main camera. /// in world space. public static OVRPose ToWorldSpacePose(this OVRPose trackingSpacePose, Camera mainCamera) { // Transform from tracking-Space to head-Space OVRPose poseInHeadSpace = trackingSpacePose.ToHeadSpacePose(); // Transform from head space to world space var cameraTransform = mainCamera.transform.localToWorldMatrix; var headSpaceTransform = Matrix4x4.TRS( poseInHeadSpace.position, poseInHeadSpace.orientation, Vector3.one); var worldSpaceTransform = cameraTransform * headSpaceTransform; return new OVRPose { position = worldSpaceTransform.GetColumn(3), orientation = worldSpaceTransform.rotation }; } /// /// Converts the given pose from tracking-space to head-space. /// /// /// The "tracking space" refers to the local coordinate system of the Quest device. This method converts a pose /// in tracking space to a pose relative to the . /// /// The pose, in tracking space, that should be converted to head space. /// in head space. public static OVRPose ToHeadSpacePose(this OVRPose trackingSpacePose) { OVRPose headPose = OVRPose.identity; Vector3 pos; Quaternion rot; if (OVRNodeStateProperties.GetNodeStatePropertyVector3(UnityEngine.XR.XRNode.Head, NodeStatePropertyType.Position, OVRPlugin.Node.Head, OVRPlugin.Step.Render, out pos)) headPose.position = pos; if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(UnityEngine.XR.XRNode.Head, NodeStatePropertyType.Orientation, OVRPlugin.Node.Head, OVRPlugin.Step.Render, out rot)) headPose.orientation = rot; OVRPose poseInHeadSpace = headPose.Inverse() * trackingSpacePose; return poseInHeadSpace; } /// /// Converts the given world-space transform to an OVRPose in head space. /// /// /// The "head space" refers to the local coordinate system of the Quest HMD. This method converts the given /// to an equivalent pose relative to the user's head. /// /// The transform to convert to head space. /// The camera whose transform is driven by the Quest HMD. This is often the main camera. /// converted to a head relative pose. public static OVRPose ToHeadSpacePose(this Transform transform, Camera camera) { // The Quaternion used in OVRPose is not 1-1 mapped with the Transform[0:3,0:3] (which is Scale * Rotation), need to calculate rotation and position separately. Quaternion rotation = Quaternion.Inverse(camera.transform.rotation) * transform.rotation; var position = (camera.transform.worldToLocalMatrix * transform.localToWorldMatrix).GetColumn(3); return new OVRPose { orientation = rotation, position = position }; } /// /// Converts the given transform to an . /// /// The transform to convert to a pose. /// If true, uses the local position and transform of . Otherwise, uses /// the world space position and rotation. /// as a . public static OVRPose ToOVRPose(this Transform t, bool isLocal = false) { OVRPose pose; pose.orientation = (isLocal) ? t.localRotation : t.rotation; pose.position = (isLocal) ? t.localPosition : t.position; return pose; } /// /// Sets a transform to the given . /// /// The transform whose position and rotation will be set. /// The to convert to . /// If true, 's local position and transform are set. Otherwise, sets /// the world position and rotation. public static void FromOVRPose(this Transform t, OVRPose pose, bool isLocal = false) { if (isLocal) { t.localRotation = pose.orientation; t.localPosition = pose.position; } else { t.rotation = pose.orientation; t.position = pose.position; } } /// /// Converts an to an . /// /// /// is typically used for native interop with native plugins, and uses a different /// coordinate system (right-handed as opposed to Unity's left-handed coordinate system). /// /// This method converts the right-handed to a left-handed /// compatible with Unity's coordinate system. /// /// This method is for advanced usage. /// /// A right-handed pose acquired from a native function call. /// converted to an . public static OVRPose ToOVRPose(this OVRPlugin.Posef p) { return new OVRPose() { position = new Vector3(p.Position.x, p.Position.y, -p.Position.z), orientation = new Quaternion(-p.Orientation.x, -p.Orientation.y, p.Orientation.z, p.Orientation.w) }; } /// /// Converts an to an . /// /// /// is typically used for native interop with native plugins. This method converts /// that native format into a format that is more compatible with Unity. /// /// This method is for advanced usage. /// /// The to convert to . /// converted to an . public static OVRTracker.Frustum ToFrustum(this OVRPlugin.Frustumf f) { return new OVRTracker.Frustum() { nearZ = f.zNear, farZ = f.zFar, fov = new Vector2() { x = Mathf.Rad2Deg * f.fovX, y = Mathf.Rad2Deg * f.fovY } }; } /// /// Converts a to a UnityEngine `Color`. /// /// /// This method is used to convert color data between the native format and a /// UnityEngine [Color](https://docs.unity3d.com/ScriptReference/Color.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A color in native color format. /// as a UnityEngine `Color`. public static Color FromColorf(this OVRPlugin.Colorf c) { return new Color() { r = c.r, g = c.g, b = c.b, a = c.a }; } /// /// Converts a UnityEngine `Color` to a . /// /// /// This method is used to convert color data between the native format and a /// UnityEngine [Color](https://docs.unity3d.com/ScriptReference/Color.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A color in Unity's `Color` format. /// as a . public static OVRPlugin.Colorf ToColorf(this Color c) { return new OVRPlugin.Colorf() { r = c.r, g = c.g, b = c.b, a = c.a }; } /// /// Converts a to a UnityEngine `Vector2`. /// /// /// This method is used to convert size data between the native format and a /// UnityEngine [Vector2](https://docs.unity3d.com/ScriptReference/Vector2.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A size in OVRPlugin's native format. /// as a UnityEngine `Vector2`. public static Vector2 FromSizef(this OVRPlugin.Sizef v) { return new Vector2() { x = v.w, y = v.h }; } /// /// Converts a `Vector2` to a . /// /// /// This method is used to convert size data between the native format and a /// UnityEngine [Vector2](https://docs.unity3d.com/ScriptReference/Vector2.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A size as a `Vector2`. /// as an OVRPlugin compatible . public static OVRPlugin.Sizef ToSizef(this Vector2 v) { return new OVRPlugin.Sizef() { w = v.x, h = v.y }; } /// /// Converts a to a UnityEngine `Vector2`. /// /// /// This method is used to convert vector data between the native format and a /// UnityEngine [Vector2](https://docs.unity3d.com/ScriptReference/Vector2.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A vector in OVRPlugin's native format. /// as a UnityEngine `Vector2`. public static Vector2 FromVector2f(this OVRPlugin.Vector2f v) { return new Vector2() { x = v.x, y = v.y }; } /// /// Converts a to a UnityEngine `Vector2`. /// /// /// This method is used to convert vector data between the native format and a /// UnityEngine [Vector2](https://docs.unity3d.com/ScriptReference/Vector2.html). /// /// This method negates the X component of the . /// /// This method is for advanced usage and is not typically used by application code. /// /// A vector in OVRPlugin's native format. /// as a UnityEngine `Vector2`. public static Vector2 FromFlippedXVector2f(this OVRPlugin.Vector2f v) { return new Vector2() { x = -v.x, y = v.y }; } /// /// Converts a `Vector2` to a . /// /// /// This method is used to convert vector data between the native format and a /// UnityEngine [Vector2](https://docs.unity3d.com/ScriptReference/Vector2.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A UnityEngine `Vector2`. /// as a . public static OVRPlugin.Vector2f ToVector2f(this Vector2 v) { return new OVRPlugin.Vector2f() { x = v.x, y = v.y }; } /// /// Converts size information between the native plugin and Unity format. /// /// /// This method is used to convert size data between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A size as a . /// as a UnityEngine `Vector3`. public static Vector3 FromSize3f(this OVRPlugin.Size3f v) { return new Vector3() { x = v.w, y = v.h, z = v.d }; } /// /// Converts size information between the native plugin and Unity format. /// /// /// This method is used to convert size data between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A size as a UnityEngine `Vector3`. /// as a . public static OVRPlugin.Size3f ToSize3f(this Vector3 v) { return new OVRPlugin.Size3f() { w = v.x, h = v.y, d = v.z }; } /// /// Converts a 3d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 3d vector between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A 3d vector as an . /// as a UnityEngine `Vector3`. public static Vector3 FromVector3f(this OVRPlugin.Vector3f v) { return new Vector3() { x = v.x, y = v.y, z = v.z }; } /// /// Converts a 3d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 3d vector between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method negates the X component of . /// /// This method is for advanced usage and is not typically used by application code. /// /// A 3d vector as an . /// as a UnityEngine `Vector3`. public static Vector3 FromFlippedXVector3f(this OVRPlugin.Vector3f v) { return new Vector3() { x = -v.x, y = v.y, z = v.z }; } /// /// Converts a 3d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 3d vector between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method negates the Z component of . /// /// This method is for advanced usage and is not typically used by application code. /// /// A 3d vector as an . /// as a UnityEngine `Vector3`. public static Vector3 FromFlippedZVector3f(this OVRPlugin.Vector3f v) { return new Vector3() { x = v.x, y = v.y, z = -v.z }; } /// /// Converts a 3d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 3d vector between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A 3d vector as UnityEngine `Vector3`. /// as an . /// public static OVRPlugin.Vector3f ToVector3f(this Vector3 v) { return new OVRPlugin.Vector3f() { x = v.x, y = v.y, z = v.z }; } /// /// Converts a 3d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 3d vector between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method negates the X component of . /// /// This method is for advanced usage and is not typically used by application code. /// /// A 3d vector as UnityEngine `Vector3`. /// as an . /// public static OVRPlugin.Vector3f ToFlippedXVector3f(this Vector3 v) { return new OVRPlugin.Vector3f() { x = -v.x, y = v.y, z = v.z }; } /// /// Converts a 3d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 3d vector between the native format and a /// UnityEngine [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html). /// /// This method negates the Z component of . /// /// This method is for advanced usage and is not typically used by application code. /// /// A 3d vector as UnityEngine `Vector3`. /// as an . /// public static OVRPlugin.Vector3f ToFlippedZVector3f(this Vector3 v) { return new OVRPlugin.Vector3f() { x = v.x, y = v.y, z = -v.z }; } /// /// Converts a 4d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 4d vector between the native format and a /// UnityEngine [Vector4](https://docs.unity3d.com/ScriptReference/Vector4.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A 4d vector as an . /// as a UnityEngine `Vector4`. /// public static Vector4 FromVector4f(this OVRPlugin.Vector4f v) { return new Vector4() { x = v.x, y = v.y, z = v.z, w = v.w }; } /// /// Converts a 4d vector between the native plugin and Unity format. /// /// /// This method is used to convert a 4d vector between the native format and a /// UnityEngine [Vector4](https://docs.unity3d.com/ScriptReference/Vector4.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A 4d vector as UnityEngine `Vector4`. /// as an . /// public static OVRPlugin.Vector4f ToVector4f(this Vector4 v) { return new OVRPlugin.Vector4f() { x = v.x, y = v.y, z = v.z, w = v.w }; } /// /// Converts a quaternion between the native plugin and Unity format. /// /// /// This method is used to convert a quaternion between the native format and a /// UnityEngine [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A quaternion as an . /// as a UnityEngine `Quaternion`. /// public static Quaternion FromQuatf(this OVRPlugin.Quatf q) { return new Quaternion() { x = q.x, y = q.y, z = q.z, w = q.w }; } /// /// Converts a quaternion between the native plugin and Unity format. /// /// /// This method is used to convert a quaternion between the native format and a /// UnityEngine [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html). /// /// This method negates the Y and Z components of the quaternion, which has the effect of flipping the rotation /// about the X axis. /// /// This method is for advanced usage and is not typically used by application code. /// /// A quaternion as an . /// as a UnityEngine `Quaternion`. /// public static Quaternion FromFlippedXQuatf(this OVRPlugin.Quatf q) { return new Quaternion() { x = q.x, y = -q.y, z = -q.z, w = q.w }; } /// /// Converts a quaternion between the native plugin and Unity format. /// /// /// This method is used to convert a quaternion between the native format and a /// UnityEngine [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html). /// /// This method negates the X and Y components of the quaternion, which has the effect of flipping the rotation /// about the Z axis. /// /// This method is for advanced usage and is not typically used by application code. /// /// A quaternion as an . /// as a UnityEngine `Quaternion`. /// public static Quaternion FromFlippedZQuatf(this OVRPlugin.Quatf q) { return new Quaternion() { x = -q.x, y = -q.y, z = q.z, w = q.w }; } /// /// Converts a quaternion between the native plugin and Unity format. /// /// /// This method is used to convert a quaternion between the native format and a /// UnityEngine [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html). /// /// This method is for advanced usage and is not typically used by application code. /// /// A quaternion as a UnityEngine `Quaternion`. /// as an . /// public static OVRPlugin.Quatf ToQuatf(this Quaternion q) { return new OVRPlugin.Quatf() { x = q.x, y = q.y, z = q.z, w = q.w }; } /// /// Converts a quaternion between the native plugin and Unity format. /// /// /// This method is used to convert a quaternion between the native format and a /// UnityEngine [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html). /// /// This method negates the Y and Z components of the quaternion, which has the effect of flipping the rotation /// about the X axis. /// /// This method is for advanced usage and is not typically used by application code. /// /// A quaternion as a UnityEngine `Quaternion`. /// as an . /// public static OVRPlugin.Quatf ToFlippedXQuatf(this Quaternion q) { return new OVRPlugin.Quatf() { x = q.x, y = -q.y, z = -q.z, w = q.w }; } /// /// Converts a quaternion between the native plugin and Unity format. /// /// /// This method is used to convert a quaternion between the native format and a /// UnityEngine [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html). /// /// This method negates the X and Y components of the quaternion, which has the effect of flipping the rotation /// about the Z axis. /// /// This method is for advanced usage and is not typically used by application code. /// /// A quaternion as a UnityEngine `Quaternion`. /// as an . /// public static OVRPlugin.Quatf ToFlippedZQuatf(this Quaternion q) { return new OVRPlugin.Quatf() { x = -q.x, y = -q.y, z = q.z, w = q.w }; } /// /// Converts a matrix between the native plugin and Unity format. /// /// /// This method is used to convert a matrix between the native format and a /// UnityEngine [Matrix4x4](https://docs.unity3d.com/ScriptReference/Matrix4x4.html). /// /// This method negates the Z axis of the rotation and translation, which converts between the native right-handed /// coordinate system and Unity's left-handed coordinate system. /// /// This method is for advanced usage and is not typically used by application code. /// /// The `Matrix4x4` to convert. /// Returns as an . public static OVR.OpenVR.HmdMatrix34_t ConvertToHMDMatrix34(this Matrix4x4 m) { OVR.OpenVR.HmdMatrix34_t pose = new OVR.OpenVR.HmdMatrix34_t(); pose.m0 = m[0, 0]; pose.m1 = m[0, 1]; pose.m2 = -m[0, 2]; pose.m3 = m[0, 3]; pose.m4 = m[1, 0]; pose.m5 = m[1, 1]; pose.m6 = -m[1, 2]; pose.m7 = m[1, 3]; pose.m8 = -m[2, 0]; pose.m9 = -m[2, 1]; pose.m10 = m[2, 2]; pose.m11 = -m[2, 3]; return pose; } /// /// Recursively searches for a child transform with the given name. /// /// /// Note that only children of are considered. itself is not /// included in the search. /// /// A child transform is considered a match if any part of its name contains . /// /// The transform at which to begin the search. /// The name of the transform to find. /// Returns the first child of with , or `null`, if no /// such child exists. public static Transform FindChildRecursive(this Transform parent, string name) { for (int i = 0; i < parent.childCount; i++) { var child = parent.GetChild(i); if (child.name.Contains(name)) return child; var result = child.FindChildRecursive(name); if (result != null) return result; } return null; } /// /// Compares two s for equality. /// /// The to compare with . /// The to compare with . /// Returns `true` if is equal to . public static bool Equals(this Gradient gradient, Gradient otherGradient) { if (gradient.colorKeys.Length != otherGradient.colorKeys.Length || gradient.alphaKeys.Length != otherGradient.alphaKeys.Length) return false; for (int i = 0; i < gradient.colorKeys.Length; i++) { GradientColorKey key = gradient.colorKeys[i]; GradientColorKey otherKey = otherGradient.colorKeys[i]; if (key.color != otherKey.color || key.time != otherKey.time) return false; } for (int i = 0; i < gradient.alphaKeys.Length; i++) { GradientAlphaKey key = gradient.alphaKeys[i]; GradientAlphaKey otherKey = otherGradient.alphaKeys[i]; if (key.alpha != otherKey.alpha || key.time != otherKey.time) return false; } return true; } /// /// Copies one gradiant into another. /// /// The that will be copied into. /// The to copy into . public static void CopyFrom(this Gradient gradient, Gradient otherGradient) { GradientColorKey[] colorKeys = new GradientColorKey[otherGradient.colorKeys.Length]; for (int i = 0; i < colorKeys.Length; i++) { Color col = otherGradient.colorKeys[i].color; colorKeys[i].color = new Color(col.r, col.g, col.b, col.a); colorKeys[i].time = otherGradient.colorKeys[i].time; } GradientAlphaKey[] alphaKeys = new GradientAlphaKey[otherGradient.alphaKeys.Length]; for (int i = 0; i < alphaKeys.Length; i++) { alphaKeys[i].alpha = otherGradient.alphaKeys[i].alpha; alphaKeys[i].time = otherGradient.alphaKeys[i].time; } gradient.SetKeys(colorKeys, alphaKeys); } } //4 types of node state properties that can be queried with UnityEngine.XR public enum NodeStatePropertyType { [System.Obsolete("Deprecated. Acceleration is not supported in OpenXR", false)] Acceleration, [System.Obsolete("Deprecated. Acceleration is not supported in OpenXR", false)] AngularAcceleration, Velocity, AngularVelocity, Position, Orientation } public static class OVRNodeStateProperties { private static List nodeStateList = new List(); public static bool IsHmdPresent() { if (OVRManager.OVRManagerinitialized && OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) return OVRPlugin.hmdPresent; #if USING_XR_SDK XRDisplaySubsystem currentDisplaySubsystem = OVRManager.GetCurrentDisplaySubsystem(); if (currentDisplaySubsystem != null) { //In 2019.3, this should be changed to currentDisplaySubsystem.isConnected, but this is a fine placeholder for now. return currentDisplaySubsystem.running; } return false; #elif REQUIRES_XR_SDK return false; #else return Device.isPresent; #endif } public static bool GetNodeStatePropertyVector3(Node nodeType, NodeStatePropertyType propertyType, OVRPlugin.Node ovrpNodeType, OVRPlugin.Step stepType, out Vector3 retVec) { retVec = Vector3.zero; switch (propertyType) { #pragma warning disable CS0618 // Type or member is obsolete case NodeStatePropertyType.Acceleration: if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) { retVec = OVRPlugin.GetNodeAcceleration(ovrpNodeType, stepType).FromFlippedZVector3f(); return true; } if (GetUnityXRNodeStateVector3(nodeType, NodeStatePropertyType.Acceleration, out retVec)) return true; break; case NodeStatePropertyType.AngularAcceleration: if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) { retVec = OVRPlugin.GetNodeAngularAcceleration(ovrpNodeType, stepType).FromFlippedZVector3f(); return true; } if (GetUnityXRNodeStateVector3(nodeType, NodeStatePropertyType.AngularAcceleration, out retVec)) return true; break; #pragma warning restore CS0618 // Type or member is obsolete case NodeStatePropertyType.Velocity: if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) { retVec = OVRPlugin.GetNodeVelocity(ovrpNodeType, stepType).FromFlippedZVector3f(); return true; } if (GetUnityXRNodeStateVector3(nodeType, NodeStatePropertyType.Velocity, out retVec)) return true; break; case NodeStatePropertyType.AngularVelocity: if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) { retVec = OVRPlugin.GetNodeAngularVelocity(ovrpNodeType, stepType).FromFlippedZVector3f(); return true; } if (GetUnityXRNodeStateVector3(nodeType, NodeStatePropertyType.AngularVelocity, out retVec)) return true; break; case NodeStatePropertyType.Position: if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) { retVec = OVRPlugin.GetNodePose(ovrpNodeType, stepType).ToOVRPose().position; return true; } if (GetUnityXRNodeStateVector3(nodeType, NodeStatePropertyType.Position, out retVec)) return true; break; } return false; } public static bool GetNodeStatePropertyQuaternion(Node nodeType, NodeStatePropertyType propertyType, OVRPlugin.Node ovrpNodeType, OVRPlugin.Step stepType, out Quaternion retQuat) { retQuat = Quaternion.identity; switch (propertyType) { case NodeStatePropertyType.Orientation: if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) { retQuat = OVRPlugin.GetNodePose(ovrpNodeType, stepType).ToOVRPose().orientation; return true; } if (GetUnityXRNodeStateQuaternion(nodeType, NodeStatePropertyType.Orientation, out retQuat)) return true; break; } return false; } private static bool ValidateProperty(Node nodeType, ref NodeState requestedNodeState) { InputTracking.GetNodeStates(nodeStateList); if (nodeStateList.Count == 0) return false; bool nodeStateFound = false; requestedNodeState = nodeStateList[0]; for (int i = 0; i < nodeStateList.Count; i++) { if (nodeStateList[i].nodeType == nodeType) { requestedNodeState = nodeStateList[i]; nodeStateFound = true; break; } } return nodeStateFound; } private static bool GetUnityXRNodeStateVector3(Node nodeType, NodeStatePropertyType propertyType, out Vector3 retVec) { retVec = Vector3.zero; NodeState requestedNodeState = default(NodeState); if (!ValidateProperty(nodeType, ref requestedNodeState)) return false; #pragma warning disable CS0618 // Type or member is obsolete if (propertyType == NodeStatePropertyType.Acceleration) { if (requestedNodeState.TryGetAcceleration(out retVec)) { return true; } } else if (propertyType == NodeStatePropertyType.AngularAcceleration) { if (requestedNodeState.TryGetAngularAcceleration(out retVec)) { return true; } } #pragma warning restore CS0618 // Type or member is obsolete else if (propertyType == NodeStatePropertyType.Velocity) { if (requestedNodeState.TryGetVelocity(out retVec)) { return true; } } else if (propertyType == NodeStatePropertyType.AngularVelocity) { if (requestedNodeState.TryGetAngularVelocity(out retVec)) { return true; } } else if (propertyType == NodeStatePropertyType.Position) { if (requestedNodeState.TryGetPosition(out retVec)) { return true; } } return false; } private static bool GetUnityXRNodeStateQuaternion(Node nodeType, NodeStatePropertyType propertyType, out Quaternion retQuat) { retQuat = Quaternion.identity; NodeState requestedNodeState = default(NodeState); if (!ValidateProperty(nodeType, ref requestedNodeState)) return false; if (propertyType == NodeStatePropertyType.Orientation) { if (requestedNodeState.TryGetRotation(out retQuat)) { return true; } } return false; } } /// /// OVRPose stores a position and orientation in the Unity coordinate system. Use to convert the pose to /// , which stores a pose in the XR coordinate system, for native XR API calls. /// [System.Serializable] public struct OVRPose { /// /// A pose with no translation or rotation. /// public static OVRPose identity { get { return new OVRPose() { position = Vector3.zero, orientation = Quaternion.identity }; } } public override bool Equals(System.Object obj) { return obj is OVRPose && this == (OVRPose)obj; } public override int GetHashCode() { return position.GetHashCode() ^ orientation.GetHashCode(); } public static bool operator ==(OVRPose x, OVRPose y) { return x.position == y.position && x.orientation == y.orientation; } public static bool operator !=(OVRPose x, OVRPose y) { return !(x == y); } /// /// The position. /// public Vector3 position; /// /// The orientation. /// public Quaternion orientation; /// /// Multiplies two poses. /// public static OVRPose operator *(OVRPose lhs, OVRPose rhs) { var ret = new OVRPose(); ret.position = lhs.position + lhs.orientation * rhs.position; ret.orientation = lhs.orientation * rhs.orientation; return ret; } /// /// Computes the inverse of the given pose. /// public OVRPose Inverse() { OVRPose ret; ret.orientation = Quaternion.Inverse(orientation); ret.position = ret.orientation * -position; return ret; } /// /// Converts the pose from left- to right-handed or vice-versa. /// public OVRPose flipZ() { var ret = this; ret.position.z = -ret.position.z; ret.orientation.z = -ret.orientation.z; ret.orientation.w = -ret.orientation.w; return ret; } // Warning: this function is not a strict reverse of OVRPlugin.Posef.ToOVRPose(), even after flipZ() public OVRPlugin.Posef ToPosef_Legacy() { return new OVRPlugin.Posef() { Position = position.ToVector3f(), Orientation = orientation.ToQuatf() }; } public OVRPlugin.Posef ToPosef() { OVRPlugin.Posef result = new OVRPlugin.Posef(); result.Position.x = position.x; result.Position.y = position.y; result.Position.z = -position.z; result.Orientation.x = -orientation.x; result.Orientation.y = -orientation.y; result.Orientation.z = orientation.z; result.Orientation.w = orientation.w; return result; } public OVRPose Rotate180AlongX() { var ret = this; ret.orientation *= Quaternion.Euler(180, 0, 0); return ret; } } /// /// Encapsulates an 8-byte-aligned buffer of unmanaged memory. /// public class OVRNativeBuffer : IDisposable { protected bool disposed = false; protected int m_numBytes = 0; protected IntPtr m_ptr = IntPtr.Zero; /// /// Creates a buffer of the specified size. /// public OVRNativeBuffer(int numBytes) { Reallocate(numBytes); } /// /// Releases unmanaged resources and performs other cleanup operations before the is /// reclaimed by garbage collection. /// ~OVRNativeBuffer() { Dispose(false); } /// /// Reallocates the buffer with the specified new size. /// public void Reset(int numBytes) { Reallocate(numBytes); } /// /// The current number of bytes in the buffer. /// public int GetCapacity() { return m_numBytes; } /// /// A pointer to the unmanaged memory in the buffer, starting at the given offset in bytes. /// public IntPtr GetPointer(int byteOffset = 0) { if (byteOffset < 0 || byteOffset >= m_numBytes) return IntPtr.Zero; return (byteOffset == 0) ? m_ptr : new IntPtr(m_ptr.ToInt64() + byteOffset); } /// /// Releases all resource used by the object. /// /// Call when you are finished using the . The /// method leaves the in an unusable state. After calling , you must /// release all references to the so the garbage collector can reclaim the memory that /// the was occupying. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool disposing) { if (disposed) return; if (disposing) { // dispose managed resources } // dispose unmanaged resources Release(); disposed = true; } protected void Reallocate(int numBytes) { Release(); if (numBytes > 0) { m_ptr = Marshal.AllocHGlobal(numBytes); m_numBytes = numBytes; } else { m_ptr = IntPtr.Zero; m_numBytes = 0; } } protected void Release() { if (m_ptr != IntPtr.Zero) { Marshal.FreeHGlobal(m_ptr); m_ptr = IntPtr.Zero; m_numBytes = 0; } } }