Initial Commit

This commit is contained in:
2025-07-04 20:33:06 +03:00
commit 8f09347ae0
9219 changed files with 2447903 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
/*
* 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.
*/
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Oculus.VR.Scripts.Editor")]
[assembly: InternalsVisibleTo("Oculus.VR.Editor")]
[assembly: InternalsVisibleTo("com.meta.photo3d")]
[assembly: InternalsVisibleTo("com.meta.photo3d.generator")]
[assembly: InternalsVisibleTo("Oculus.Interaction.OVR.Editor")]
[assembly: InternalsVisibleTo("Meta.XR.BuildingBlocks")]
[assembly: InternalsVisibleTo("Meta.XR.BuildingBlocks.Editor")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07a8397add4f58d4dbfa3505a0517451
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ef6e9504db776f346a4b58e188012a8e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
/*
* 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.
*/
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Meta.XR.BuildingBlocks.Editor")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3be90808049d02438e1636838abb82c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,45 @@
/*
* 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.
*/
using System;
using UnityEngine;
namespace Meta.XR.BuildingBlocks
{
[DisallowMultipleComponent]
public class BuildingBlock : MonoBehaviour
{
[SerializeField, OVRReadOnly] internal string blockId;
public string BlockId => blockId;
[SerializeField, HideInInspector] internal string instanceId = Guid.NewGuid().ToString();
public string InstanceId => instanceId;
[SerializeField, OVRReadOnly] internal int version = 1;
public int Version => version;
private void Start()
{
OVRTelemetry.Start(OVRTelemetryConstants.BB.MarkerId.RunBlock)
.AddBlockInfo(this)
.Send();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b023183069df224080314a560e6e1ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 6557dba7c7035a3448c0583098600c6c, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
{
"name": "Meta.XR.BuildingBlocks",
"rootNamespace": "",
"references": [
"Oculus.VR"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d0574574baafe9d4aa78d8df4a5e3e5e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
/*
* 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.
*/
using System;
using UnityEngine;
namespace Meta.XR.BuildingBlocks
{
public class PassthroughProjectionSurfaceBuildingBlock : MonoBehaviour
{
public MeshFilter projectionObject;
// Start is called before the first frame update
private void Start()
{
var ptLayers = FindObjectsOfType<OVRPassthroughLayer>();
var foundLayer = false;
foreach (var ptLayer in ptLayers)
{
if (!ptLayer.GetComponent<BuildingBlock>())
{
continue;
}
foundLayer = true;
ptLayer.AddSurfaceGeometry(projectionObject.gameObject, true);
}
if (foundLayer)
{
// The MeshRenderer component renders the quad as a blue outline
// we only use this when Passthrough isn't visible
var quadOutline = projectionObject.GetComponent<MeshRenderer>();
quadOutline.enabled = false;
}
else
{
throw new InvalidOperationException("A BB with the passthrough overlay layer was not found");
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c153b9a5da6ec24bb71a81fc631f600
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,33 @@
/*
* 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.
*/
namespace Meta.XR.BuildingBlocks
{
internal static class Telemetry
{
public static OVRTelemetryMarker AddBlockInfo(this OVRTelemetryMarker marker, BuildingBlock block)
{
return marker.AddAnnotation(OVRTelemetryConstants.BB.AnnotationType.BlockId, block.BlockId)
.AddAnnotation(OVRTelemetryConstants.BB.AnnotationType.InstanceId, block.InstanceId)
.AddAnnotation(OVRTelemetryConstants.BB.AnnotationType.BlockName, block.gameObject.name)
.AddAnnotation(OVRTelemetryConstants.BB.AnnotationType.Version, block.Version.ToString());
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d93bbab64ed50ea45b9bc8df3d334c0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1f92828e69374384b8cb197653871a6e
folderAsset: yes
timeCreated: 1502989983
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
/*
* 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.
*/
public class OVRCameraComposition
{
// deprecated since SDK 1.49
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 70818bad1fe6859439b190a61dfb6eb8
timeCreated: 1503089686
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,120 @@
/*
* 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 UNITY_ANDROID && !UNITY_EDITOR
#define OVR_ANDROID_MRC
#endif
using UnityEngine;
using System.Collections;
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
public abstract class OVRComposition
{
public bool cameraInTrackingSpace = false;
public OVRCameraRig cameraRig = null;
protected OVRComposition(GameObject parentObject, Camera mainCamera,
OVRMixedRealityCaptureConfiguration configuration)
{
RefreshCameraRig(parentObject, mainCamera);
}
public abstract OVRManager.CompositionMethod CompositionMethod();
public abstract void Update(GameObject gameObject, Camera mainCamera,
OVRMixedRealityCaptureConfiguration configuration, OVRManager.TrackingOrigin trackingOrigin);
public abstract void Cleanup();
public virtual void RecenterPose()
{
}
protected bool usingLastAttachedNodePose = false;
// Sometimes the attach node pose is not readable (lose tracking, low battery, etc.) Use the last pose instead when it happens
protected OVRPose lastAttachedNodePose = new OVRPose();
public void RefreshCameraRig(GameObject parentObject, Camera mainCamera)
{
OVRCameraRig cameraRig = mainCamera.GetComponentInParent<OVRCameraRig>();
if (cameraRig == null)
{
cameraRig = parentObject.GetComponent<OVRCameraRig>();
}
cameraInTrackingSpace = (cameraRig != null && cameraRig.trackingSpace != null);
this.cameraRig = cameraRig;
Debug.Log(cameraRig == null ? "[OVRComposition] CameraRig not found" : "[OVRComposition] CameraRig found");
}
public OVRPose ComputeCameraWorldSpacePose(OVRPlugin.CameraExtrinsics extrinsics, Camera mainCamera)
{
OVRPose trackingSpacePose = ComputeCameraTrackingSpacePose(extrinsics);
OVRPose worldSpacePose = OVRExtensions.ToWorldSpacePose(trackingSpacePose, mainCamera);
return worldSpacePose;
}
public OVRPose ComputeCameraTrackingSpacePose(OVRPlugin.CameraExtrinsics extrinsics)
{
OVRPose trackingSpacePose = new OVRPose();
OVRPose cameraTrackingSpacePose = extrinsics.RelativePose.ToOVRPose();
#if OVR_ANDROID_MRC
OVRPose stageToLocalPose =
OVRPlugin.GetTrackingTransformRelativePose(OVRPlugin.TrackingOrigin.Stage).ToOVRPose();
cameraTrackingSpacePose = stageToLocalPose * cameraTrackingSpacePose;
#endif
trackingSpacePose = cameraTrackingSpacePose;
if (extrinsics.AttachedToNode != OVRPlugin.Node.None && OVRPlugin.GetNodePresent(extrinsics.AttachedToNode))
{
if (usingLastAttachedNodePose)
{
Debug.Log("The camera attached node get tracked");
usingLastAttachedNodePose = false;
}
OVRPose attachedNodePose =
OVRPlugin.GetNodePose(extrinsics.AttachedToNode, OVRPlugin.Step.Render).ToOVRPose();
lastAttachedNodePose = attachedNodePose;
trackingSpacePose = attachedNodePose * trackingSpacePose;
}
else
{
if (extrinsics.AttachedToNode != OVRPlugin.Node.None)
{
if (!usingLastAttachedNodePose)
{
Debug.LogWarning("The camera attached node could not be tracked, using the last pose");
usingLastAttachedNodePose = true;
}
trackingSpacePose = lastAttachedNodePose * trackingSpacePose;
}
}
return trackingSpacePose;
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 829a382f3380d4b46ad9670463232a0b
timeCreated: 1502990005
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,141 @@
/*
* 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.
*/
using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
internal class OVRCompositionUtil
{
public static void SafeDestroy(GameObject obj)
{
if (Application.isPlaying)
{
GameObject.Destroy(obj);
}
else
{
GameObject.DestroyImmediate(obj);
}
}
public static void SafeDestroy(ref GameObject obj)
{
if (obj != null)
{
SafeDestroy(obj);
obj = null;
}
}
[System.Obsolete("GetWorldPosition should be invoked with an explicit camera parameter")]
public static Vector3 GetWorldPosition(Vector3 trackingSpacePosition)
{
return GetWorldPosition(Camera.main, trackingSpacePosition);
}
public static Vector3 GetWorldPosition(Camera camera, Vector3 trackingSpacePosition)
{
OVRPose tsPose;
tsPose.position = trackingSpacePosition;
tsPose.orientation = Quaternion.identity;
OVRPose wsPose = OVRExtensions.ToWorldSpacePose(tsPose, camera);
Vector3 pos = wsPose.position;
return pos;
}
public static float GetMaximumBoundaryDistance(Camera camera, OVRBoundary.BoundaryType boundaryType)
{
if (!OVRManager.boundary.GetConfigured())
{
return float.MaxValue;
}
Vector3[] geometry = OVRManager.boundary.GetGeometry(boundaryType);
if (geometry.Length == 0)
{
return float.MaxValue;
}
float maxDistance = -float.MaxValue;
foreach (Vector3 v in geometry)
{
Vector3 pos = GetWorldPosition(camera, v);
float distance = Vector3.Dot(camera.transform.forward, pos);
if (maxDistance < distance)
{
maxDistance = distance;
}
}
return maxDistance;
}
public static Mesh BuildBoundaryMesh(OVRBoundary.BoundaryType boundaryType, float topY, float bottomY)
{
if (!OVRManager.boundary.GetConfigured())
{
return null;
}
List<Vector3> geometry = new List<Vector3>(OVRManager.boundary.GetGeometry(boundaryType));
if (geometry.Count == 0)
{
return null;
}
geometry.Add(geometry[0]);
int numPoints = geometry.Count;
Vector3[] vertices = new Vector3[numPoints * 2];
Vector2[] uvs = new Vector2[numPoints * 2];
for (int i = 0; i < numPoints; ++i)
{
Vector3 v = geometry[i];
vertices[i] = new Vector3(v.x, bottomY, v.z);
vertices[i + numPoints] = new Vector3(v.x, topY, v.z);
uvs[i] = new Vector2((float)i / (numPoints - 1), 0.0f);
uvs[i + numPoints] = new Vector2(uvs[i].x, 1.0f);
}
int[] triangles = new int[(numPoints - 1) * 2 * 3];
for (int i = 0; i < numPoints - 1; ++i)
{
// the geometry is built clockwised. only the back faces should be rendered in the camera frame mask
triangles[i * 6 + 0] = i;
triangles[i * 6 + 1] = i + numPoints;
triangles[i * 6 + 2] = i + 1 + numPoints;
triangles[i * 6 + 3] = i;
triangles[i * 6 + 4] = i + 1 + numPoints;
triangles[i * 6 + 5] = i + 1;
}
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.uv = uvs;
mesh.triangles = triangles;
return mesh;
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 43bf91d46b2eb874a842be95aee2cc9a
timeCreated: 1502992822
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
/*
* 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.
*/
public class OVRDirectComposition
{
// deprecated since SDK 1.49
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8e9d1c62d6c68c7429ce265558cfd2b2
timeCreated: 1502990248
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,632 @@
/*
* 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 UNITY_ANDROID && !UNITY_EDITOR
#define OVR_ANDROID_MRC
#endif
using System;
using UnityEngine;
using System.Collections.Generic;
using System.Threading;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
#if USING_URP
using UnityEngine.Rendering.Universal;
#endif
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
public class OVRExternalComposition : OVRComposition
{
private GameObject previousMainCameraObject = null;
public GameObject foregroundCameraGameObject = null;
public Camera foregroundCamera = null;
public GameObject backgroundCameraGameObject = null;
public Camera backgroundCamera = null;
#if OVR_ANDROID_MRC
private bool skipFrame = false;
private float fpsThreshold = 80.0f;
private bool isFrameSkipped = true;
private bool renderCombinedFrame = false;
private AudioListener audioListener;
private OVRMRAudioFilter audioFilter;
private RenderTexture[] mrcRenderTextureArray = new RenderTexture[2];
private int frameIndex;
private int lastMrcEncodeFrameSyncId;
// when rendererSupportsCameraRect is false, mrcRenderTextureArray would only store the background frame (regular width)
private RenderTexture[] mrcForegroundRenderTextureArray = new RenderTexture[2];
// this is used for moving MRC camera where we would need to be able to synchronize the camera position from the game with that on the client for composition
private double[] cameraPoseTimeArray = new double[2];
#endif
public override OVRManager.CompositionMethod CompositionMethod()
{
return OVRManager.CompositionMethod.External;
}
public OVRExternalComposition(GameObject parentObject, Camera mainCamera,
OVRMixedRealityCaptureConfiguration configuration)
: base(parentObject, mainCamera, configuration)
{
#if OVR_ANDROID_MRC
renderCombinedFrame = false;
int frameWidth;
int frameHeight;
OVRPlugin.Media.GetMrcFrameSize(out frameWidth, out frameHeight);
Debug.LogFormat("[OVRExternalComposition] Create render texture {0}, {1}",
renderCombinedFrame ? frameWidth : frameWidth / 2, frameHeight);
for (int i = 0; i < 2; ++i)
{
mrcRenderTextureArray[i] = new RenderTexture(renderCombinedFrame ? frameWidth : frameWidth / 2, frameHeight,
24, RenderTextureFormat.ARGB32);
mrcRenderTextureArray[i].Create();
cameraPoseTimeArray[i] = 0.0;
}
skipFrame = OVRManager.display.displayFrequency > fpsThreshold;
OVRManager.DisplayRefreshRateChanged += DisplayRefreshRateChanged;
frameIndex = 0;
lastMrcEncodeFrameSyncId = -1;
if (!renderCombinedFrame)
{
Debug.LogFormat("[OVRExternalComposition] Create extra render textures for foreground");
for (int i = 0; i < 2; ++i)
{
mrcForegroundRenderTextureArray[i] =
new RenderTexture(frameWidth / 2, frameHeight, 24, RenderTextureFormat.ARGB32);
mrcForegroundRenderTextureArray[i].Create();
}
}
#endif
RefreshCameraObjects(parentObject, mainCamera, configuration);
}
private void RefreshCameraObjects(GameObject parentObject, Camera mainCamera,
OVRMixedRealityCaptureConfiguration configuration)
{
if (mainCamera.gameObject != previousMainCameraObject)
{
Debug.LogFormat("[OVRExternalComposition] Camera refreshed. Rebind camera to {0}",
mainCamera.gameObject.name);
OVRCompositionUtil.SafeDestroy(ref backgroundCameraGameObject);
backgroundCamera = null;
OVRCompositionUtil.SafeDestroy(ref foregroundCameraGameObject);
foregroundCamera = null;
RefreshCameraRig(parentObject, mainCamera);
Debug.Assert(backgroundCameraGameObject == null);
if (configuration.instantiateMixedRealityCameraGameObject != null)
{
backgroundCameraGameObject =
configuration.instantiateMixedRealityCameraGameObject(mainCamera.gameObject,
OVRManager.MrcCameraType.Background);
}
else
{
backgroundCameraGameObject = Object.Instantiate(mainCamera.gameObject);
}
backgroundCameraGameObject.name = "OculusMRC_BackgroundCamera";
backgroundCameraGameObject.transform.parent =
cameraInTrackingSpace ? cameraRig.trackingSpace : parentObject.transform;
if (backgroundCameraGameObject.GetComponent<AudioListener>())
{
Object.Destroy(backgroundCameraGameObject.GetComponent<AudioListener>());
}
if (backgroundCameraGameObject.GetComponent<OVRManager>())
{
Object.Destroy(backgroundCameraGameObject.GetComponent<OVRManager>());
}
backgroundCamera = backgroundCameraGameObject.GetComponent<Camera>();
backgroundCamera.tag = "Untagged";
#if USING_MRC_COMPATIBLE_URP_VERSION
var backgroundCamData = backgroundCamera.GetUniversalAdditionalCameraData();
if (backgroundCamData != null)
{
backgroundCamData.allowXRRendering = false;
}
#elif USING_URP
Debug.LogError("Using URP with MRC is only supported with URP version 10.0.0 or higher. Consider using Unity 2020 or higher.");
#else
backgroundCamera.stereoTargetEye = StereoTargetEyeMask.None;
#endif
backgroundCamera.depth = 99990.0f;
backgroundCamera.rect = new Rect(0.0f, 0.0f, 0.5f, 1.0f);
backgroundCamera.cullingMask = (backgroundCamera.cullingMask & ~configuration.extraHiddenLayers) |
configuration.extraVisibleLayers;
#if OVR_ANDROID_MRC
backgroundCamera.targetTexture = mrcRenderTextureArray[0];
if (!renderCombinedFrame)
{
backgroundCamera.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
}
#endif
Debug.Assert(foregroundCameraGameObject == null);
if (configuration.instantiateMixedRealityCameraGameObject != null)
{
foregroundCameraGameObject =
configuration.instantiateMixedRealityCameraGameObject(mainCamera.gameObject,
OVRManager.MrcCameraType.Foreground);
}
else
{
foregroundCameraGameObject = Object.Instantiate(mainCamera.gameObject);
}
foregroundCameraGameObject.name = "OculusMRC_ForgroundCamera";
foregroundCameraGameObject.transform.parent =
cameraInTrackingSpace ? cameraRig.trackingSpace : parentObject.transform;
if (foregroundCameraGameObject.GetComponent<AudioListener>())
{
Object.Destroy(foregroundCameraGameObject.GetComponent<AudioListener>());
}
if (foregroundCameraGameObject.GetComponent<OVRManager>())
{
Object.Destroy(foregroundCameraGameObject.GetComponent<OVRManager>());
}
foregroundCamera = foregroundCameraGameObject.GetComponent<Camera>();
foregroundCamera.tag = "Untagged";
#if USING_MRC_COMPATIBLE_URP_VERSION
var foregroundCamData = foregroundCamera.GetUniversalAdditionalCameraData();
if (foregroundCamData != null)
{
foregroundCamData.allowXRRendering = false;
}
#elif USING_URP
Debug.LogError("Using URP with MRC is only supported with URP version 10.0.0 or higher. Consider using Unity 2020 or higher.");
#else
foregroundCamera.stereoTargetEye = StereoTargetEyeMask.None;
#endif
foregroundCamera.depth =
backgroundCamera.depth + 1.0f; // enforce the forground be rendered after the background
foregroundCamera.rect = new Rect(0.5f, 0.0f, 0.5f, 1.0f);
foregroundCamera.clearFlags = CameraClearFlags.Color;
#if OVR_ANDROID_MRC
foregroundCamera.backgroundColor = configuration.externalCompositionBackdropColorQuest;
#else
foregroundCamera.backgroundColor = configuration.externalCompositionBackdropColorRift;
#endif
foregroundCamera.cullingMask = (foregroundCamera.cullingMask & ~configuration.extraHiddenLayers) |
configuration.extraVisibleLayers;
#if OVR_ANDROID_MRC
if (renderCombinedFrame)
{
foregroundCamera.targetTexture = mrcRenderTextureArray[0];
}
else
{
foregroundCamera.targetTexture = mrcForegroundRenderTextureArray[0];
foregroundCamera.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
}
#endif
// [Debug] Uncommenting the following code will put a cube on the external camera location for visualization
//GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
//cube.transform.parent = foregroundCameraGameObject.transform;
//cube.transform.localPosition = Vector3.zero;
//cube.transform.localRotation = Quaternion.identity;
//cube.transform.localScale = Vector3.one * 0.1f;
previousMainCameraObject = mainCamera.gameObject;
}
}
#if OVR_ANDROID_MRC
private void RefreshAudioFilter(Camera mainCamera)
{
if (audioListener == null || !audioListener.enabled || !audioListener.gameObject.activeInHierarchy)
{
CleanupAudioFilter();
// first try cameraRig
AudioListener tmpAudioListener = cameraRig != null && cameraRig.centerEyeAnchor.gameObject.activeInHierarchy
? cameraRig.centerEyeAnchor.GetComponent<AudioListener>()
: null;
// second try mainCamera
if (tmpAudioListener == null || !tmpAudioListener.enabled)
{
tmpAudioListener = mainCamera != null && mainCamera.gameObject.activeInHierarchy
? mainCamera.GetComponent<AudioListener>()
: null;
}
// third try Camera.main (expensive)
if (tmpAudioListener == null || !tmpAudioListener.enabled)
{
mainCamera = Camera.main;
tmpAudioListener = mainCamera != null && mainCamera.gameObject.activeInHierarchy
? mainCamera.GetComponent<AudioListener>()
: null;
}
// fourth, search for all AudioListeners (very expensive)
if (tmpAudioListener == null || !tmpAudioListener.enabled)
{
Object[] allListeners = Object.FindObjectsOfType<AudioListener>();
foreach (var l in allListeners)
{
AudioListener al = l as AudioListener;
if (al != null && al.enabled && al.gameObject.activeInHierarchy)
{
tmpAudioListener = al;
break;
}
}
}
if (tmpAudioListener == null || !tmpAudioListener.enabled)
{
Debug.LogWarning("[OVRExternalComposition] No AudioListener in scene");
}
else
{
Debug.LogFormat("[OVRExternalComposition] AudioListener found, obj {0}",
tmpAudioListener.gameObject.name);
audioListener = tmpAudioListener;
audioFilter = audioListener.gameObject.AddComponent<OVRMRAudioFilter>();
audioFilter.composition = this;
Debug.LogFormat("OVRMRAudioFilter added");
}
}
}
private float[] cachedAudioDataArray = null;
private int CastMrcFrame(int castTextureIndex)
{
int audioFrames;
int audioChannels;
GetAndResetAudioData(ref cachedAudioDataArray, out audioFrames, out audioChannels);
int syncId = -1;
//Debug.Log("EncodeFrameThreadObject EncodeMrcFrame");
bool ret = false;
if (OVRPlugin.Media.GetMrcInputVideoBufferType() == OVRPlugin.Media.InputVideoBufferType.TextureHandle)
{
ret = OVRPlugin.Media.EncodeMrcFrame(mrcRenderTextureArray[castTextureIndex].GetNativeTexturePtr(),
renderCombinedFrame
? System.IntPtr.Zero
: mrcForegroundRenderTextureArray[castTextureIndex].GetNativeTexturePtr(),
cachedAudioDataArray, audioFrames, audioChannels, AudioSettings.dspTime,
cameraPoseTimeArray[castTextureIndex], ref syncId);
}
else
{
ret = OVRPlugin.Media.EncodeMrcFrame(mrcRenderTextureArray[castTextureIndex], cachedAudioDataArray,
audioFrames, audioChannels, AudioSettings.dspTime, cameraPoseTimeArray[castTextureIndex], ref syncId);
}
if (!ret)
{
Debug.LogWarning("EncodeMrcFrame failed. Likely caused by OBS plugin disconnection");
return -1;
}
return syncId;
}
private void SetCameraTargetTexture(int drawTextureIndex)
{
if (renderCombinedFrame)
{
RenderTexture texture = mrcRenderTextureArray[drawTextureIndex];
if (backgroundCamera.targetTexture != texture)
{
backgroundCamera.targetTexture = texture;
}
if (foregroundCamera.targetTexture != texture)
{
foregroundCamera.targetTexture = texture;
}
}
else
{
RenderTexture bgTexture = mrcRenderTextureArray[drawTextureIndex];
RenderTexture fgTexture = mrcForegroundRenderTextureArray[drawTextureIndex];
if (backgroundCamera.targetTexture != bgTexture)
{
backgroundCamera.targetTexture = bgTexture;
}
if (foregroundCamera.targetTexture != fgTexture)
{
foregroundCamera.targetTexture = fgTexture;
}
}
}
#endif
public override void Update(GameObject gameObject, Camera mainCamera,
OVRMixedRealityCaptureConfiguration configuration, OVRManager.TrackingOrigin trackingOrigin)
{
#if OVR_ANDROID_MRC
if (skipFrame && OVRPlugin.Media.IsCastingToRemoteClient())
{
isFrameSkipped = !isFrameSkipped;
if (isFrameSkipped)
{
return;
}
}
#endif
RefreshCameraObjects(gameObject, mainCamera, configuration);
// the HandNodePoseStateLatency doesn't apply to the external composition. Always enforce it to 0.0
OVRPlugin.SetHandNodePoseStateLatency(0.0);
// For third-person camera to use for calculating camera position with different anchors
OVRPose stageToLocalPose =
OVRPlugin.GetTrackingTransformRelativePose(OVRPlugin.TrackingOrigin.Stage).ToOVRPose();
OVRPose localToStagePose = stageToLocalPose.Inverse();
OVRPose head = localToStagePose * OVRPlugin.GetNodePose(OVRPlugin.Node.Head, OVRPlugin.Step.Render).ToOVRPose();
OVRPose leftC = localToStagePose *
OVRPlugin.GetNodePose(OVRPlugin.Node.HandLeft, OVRPlugin.Step.Render).ToOVRPose();
OVRPose rightC = localToStagePose *
OVRPlugin.GetNodePose(OVRPlugin.Node.HandRight, OVRPlugin.Step.Render).ToOVRPose();
OVRPlugin.Media.SetMrcHeadsetControllerPose(head.ToPosef(), leftC.ToPosef(), rightC.ToPosef());
#if OVR_ANDROID_MRC
RefreshAudioFilter(mainCamera);
int drawTextureIndex = (frameIndex / 2) % 2;
int castTextureIndex = 1 - drawTextureIndex;
backgroundCamera.enabled = (frameIndex % 2) == 0;
foregroundCamera.enabled = (frameIndex % 2) == 1;
if (frameIndex % 2 == 0)
{
if (lastMrcEncodeFrameSyncId != -1)
{
OVRPlugin.Media.SyncMrcFrame(lastMrcEncodeFrameSyncId);
lastMrcEncodeFrameSyncId = -1;
}
lastMrcEncodeFrameSyncId = CastMrcFrame(castTextureIndex);
SetCameraTargetTexture(drawTextureIndex);
}
++frameIndex;
#endif
backgroundCamera.clearFlags = mainCamera.clearFlags;
backgroundCamera.backgroundColor = mainCamera.backgroundColor;
if (configuration.dynamicCullingMask)
{
backgroundCamera.cullingMask = (mainCamera.cullingMask & ~configuration.extraHiddenLayers) |
configuration.extraVisibleLayers;
}
backgroundCamera.nearClipPlane = mainCamera.nearClipPlane;
backgroundCamera.farClipPlane = mainCamera.farClipPlane;
if (configuration.dynamicCullingMask)
{
foregroundCamera.cullingMask = (mainCamera.cullingMask & ~configuration.extraHiddenLayers) |
configuration.extraVisibleLayers;
}
foregroundCamera.nearClipPlane = mainCamera.nearClipPlane;
foregroundCamera.farClipPlane = mainCamera.farClipPlane;
if (OVRMixedReality.useFakeExternalCamera || OVRPlugin.GetExternalCameraCount() == 0)
{
OVRPose worldSpacePose = new OVRPose();
OVRPose trackingSpacePose = new OVRPose();
trackingSpacePose.position = trackingOrigin == OVRManager.TrackingOrigin.EyeLevel
? OVRMixedReality.fakeCameraEyeLevelPosition
: OVRMixedReality.fakeCameraFloorLevelPosition;
trackingSpacePose.orientation = OVRMixedReality.fakeCameraRotation;
worldSpacePose = OVRExtensions.ToWorldSpacePose(trackingSpacePose, mainCamera);
backgroundCamera.fieldOfView = OVRMixedReality.fakeCameraFov;
backgroundCamera.aspect = OVRMixedReality.fakeCameraAspect;
foregroundCamera.fieldOfView = OVRMixedReality.fakeCameraFov;
foregroundCamera.aspect = OVRMixedReality.fakeCameraAspect;
if (cameraInTrackingSpace)
{
backgroundCamera.transform.FromOVRPose(trackingSpacePose, true);
foregroundCamera.transform.FromOVRPose(trackingSpacePose, true);
}
else
{
backgroundCamera.transform.FromOVRPose(worldSpacePose);
foregroundCamera.transform.FromOVRPose(worldSpacePose);
}
}
else
{
OVRPlugin.CameraExtrinsics extrinsics;
OVRPlugin.CameraIntrinsics intrinsics;
// So far, only support 1 camera for MR and always use camera index 0
if (OVRPlugin.GetMixedRealityCameraInfo(0, out extrinsics, out intrinsics))
{
float fovY = Mathf.Atan(intrinsics.FOVPort.UpTan) * Mathf.Rad2Deg * 2;
float aspect = intrinsics.FOVPort.LeftTan / intrinsics.FOVPort.UpTan;
backgroundCamera.fieldOfView = fovY;
backgroundCamera.aspect = aspect;
foregroundCamera.fieldOfView = fovY;
foregroundCamera.aspect = intrinsics.FOVPort.LeftTan / intrinsics.FOVPort.UpTan;
if (cameraInTrackingSpace)
{
OVRPose trackingSpacePose = ComputeCameraTrackingSpacePose(extrinsics);
backgroundCamera.transform.FromOVRPose(trackingSpacePose, true);
foregroundCamera.transform.FromOVRPose(trackingSpacePose, true);
}
else
{
OVRPose worldSpacePose = ComputeCameraWorldSpacePose(extrinsics, mainCamera);
backgroundCamera.transform.FromOVRPose(worldSpacePose);
foregroundCamera.transform.FromOVRPose(worldSpacePose);
}
#if OVR_ANDROID_MRC
cameraPoseTimeArray[drawTextureIndex] = extrinsics.LastChangedTimeSeconds;
#endif
}
else
{
Debug.LogError("Failed to get external camera information");
return;
}
}
Vector3 headToExternalCameraVec = mainCamera.transform.position - foregroundCamera.transform.position;
float clipDistance = Vector3.Dot(headToExternalCameraVec, foregroundCamera.transform.forward);
foregroundCamera.farClipPlane = Mathf.Max(foregroundCamera.nearClipPlane + 0.001f, clipDistance);
}
#if OVR_ANDROID_MRC
private void CleanupAudioFilter()
{
if (audioFilter)
{
audioFilter.composition = null;
Object.Destroy(audioFilter);
Debug.LogFormat("OVRMRAudioFilter destroyed");
audioFilter = null;
}
}
#endif
public override void Cleanup()
{
OVRCompositionUtil.SafeDestroy(ref backgroundCameraGameObject);
backgroundCamera = null;
OVRCompositionUtil.SafeDestroy(ref foregroundCameraGameObject);
foregroundCamera = null;
Debug.Log("ExternalComposition deactivated");
#if OVR_ANDROID_MRC
if (lastMrcEncodeFrameSyncId != -1)
{
OVRPlugin.Media.SyncMrcFrame(lastMrcEncodeFrameSyncId);
lastMrcEncodeFrameSyncId = -1;
}
CleanupAudioFilter();
for (int i = 0; i < 2; ++i)
{
mrcRenderTextureArray[i].Release();
mrcRenderTextureArray[i] = null;
if (!renderCombinedFrame)
{
mrcForegroundRenderTextureArray[i].Release();
mrcForegroundRenderTextureArray[i] = null;
}
}
OVRManager.DisplayRefreshRateChanged -= DisplayRefreshRateChanged;
frameIndex = 0;
#endif
}
private readonly object audioDataLock = new object();
private List<float> cachedAudioData = new List<float>(16384);
private int cachedChannels = 0;
public void CacheAudioData(float[] data, int channels)
{
lock (audioDataLock)
{
if (channels != cachedChannels)
{
cachedAudioData.Clear();
}
cachedChannels = channels;
cachedAudioData.AddRange(data);
//Debug.LogFormat("[CacheAudioData] dspTime {0} indata {1} channels {2} accu_len {3}", AudioSettings.dspTime, data.Length, channels, cachedAudioData.Count);
}
}
public void GetAndResetAudioData(ref float[] audioData, out int audioFrames, out int channels)
{
lock (audioDataLock)
{
//Debug.LogFormat("[GetAndResetAudioData] dspTime {0} accu_len {1}", AudioSettings.dspTime, cachedAudioData.Count);
if (audioData == null || audioData.Length < cachedAudioData.Count)
{
audioData = new float[cachedAudioData.Capacity];
}
cachedAudioData.CopyTo(audioData);
audioFrames = cachedAudioData.Count;
channels = cachedChannels;
cachedAudioData.Clear();
}
}
#if OVR_ANDROID_MRC
private void DisplayRefreshRateChanged(float fromRefreshRate, float toRefreshRate)
{
skipFrame = toRefreshRate > fpsThreshold;
}
#endif
}
#if OVR_ANDROID_MRC
public class OVRMRAudioFilter : MonoBehaviour
{
private bool running = false;
public OVRExternalComposition composition;
void Start()
{
running = true;
}
void OnAudioFilterRead(float[] data, int channels)
{
if (!running)
return;
if (composition != null)
{
composition.CacheAudioData(data, channels);
}
}
}
#endif
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2c109ff55176f71418ec2c06d1b5d28e
timeCreated: 1502990231
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
/*
* 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.
*/
public class OVRSandwichComposition
{
// deprecated since SDK 1.41
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c02efcdd3fb2aa4e9c641b0c2a54b9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e87d4bbdfc8d17445b4a41760b401026
folderAsset: yes
timeCreated: 1510282190
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
/*
* 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.
*/
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("EditModeTests")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: da51b98f15f03d446acef754ccab494d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
/*
* 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 OVR_UNITY_ASSET_STORE
#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 System.Diagnostics;
using UnityEngine;
using UnityEditor;
// TODO: rename to MetaXRSimulatorDownloader after UPM migration
public class MetaXRSimulatorEnabler : MonoBehaviour
{
#if !USING_META_XR_SIMULATOR
const string kDownloadSimulator = "Oculus/Download Meta XR Simulator";
[MenuItem(kDownloadSimulator, false, 10000)]
private static void DownloadSimulator()
{
if (EditorUtility.DisplayDialog("Meta XR Simulator", "Download Meta XR Simulator from Oculus server as an UPM tarball, which can be installed through Package Manager.", "Download", "Cancel"))
{
string downloadUrl = "https://npm.developer.oculus.com/-/web/detail/com.meta.xr.simulator";
UnityEngine.Debug.LogFormat("Open Meta XR Simulator URL: {0}", downloadUrl);
Application.OpenURL(downloadUrl);
}
}
#endif
}
#endif // #if OVR_UNITY_ASSET_STORE
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6431dd3b7073564ca2280d089483532
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,206 @@
/*
* 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.
*/
using System.Collections.Generic;
using System;
using System.ComponentModel;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Assertions;
using Component = UnityEngine.Component;
/// <summary>
/// Custom Editor for <see cref="OVRCustomFace">
/// </summary>
/// <remarks>
/// Custom Editor for <see cref="OVRCustomFace"> that supports:
/// - attempting to find an <see cref="OVRFaceExpressions"/>in the parent hierarchy to match to if none is chosen
/// - supporting string matching to attempt to automatically match <see cref="OVRFaceExpressions.FaceExpression"/> to blend shapes on the shared mesh
/// The string matching algorithm will tokenize the blend shape names and the <see cref="OVRFaceExpressions.FaceExpression"/> names and for each
/// blend shape find the <see cref="OVRFaceExpressions.FaceExpression"/> with the most total characters in matching tokens.
/// To match tokens it currently uses case invariant matching.
/// The tokenization is based on some common string seperation characters and seperation by camel case.
/// </remarks>
[CustomEditor(typeof(OVRCustomFace))]
public class OVRCustomFaceEditor : Editor
{
private SerializedProperty _expressionsProp;
private SerializedProperty _mappings;
private SerializedProperty _strengthMultiplier;
private SerializedProperty _allowDuplicateMapping;
private bool _showBlendshapes = true;
protected virtual void OnEnable()
{
_expressionsProp = serializedObject.FindProperty(nameof(OVRCustomFace._faceExpressions));
_mappings = serializedObject.FindProperty(nameof(OVRCustomFace._mappings));
_strengthMultiplier = serializedObject.FindProperty(nameof(OVRCustomFace._blendShapeStrengthMultiplier));
_allowDuplicateMapping = serializedObject.FindProperty(nameof(OVRCustomFace._allowDuplicateMapping));
}
public override void OnInspectorGUI()
{
var face = (OVRCustomFace)target;
serializedObject.Update();
if (_expressionsProp.objectReferenceValue == null)
{
_expressionsProp.objectReferenceValue = face.SearchFaceExpressions();
}
if (!IsFaceExpressionsConfigured(face))
{
if (OVREditorUIElements.RenderWarningWithButton(
"OVRFaceExpressions is required.", "Configure OVRFaceExpressions"))
{
FixFaceExpressions(face);
}
}
EditorGUILayout.PropertyField(_expressionsProp, new GUIContent(nameof(OVRFaceExpressions)));
EditorGUILayout.PropertyField(_strengthMultiplier, new GUIContent("Blend Shape Strength Multiplier"));
//need to pass out some property to find the component from
SkinnedMeshRenderer renderer = GetSkinnedMeshRenderer(_expressionsProp);
if (renderer == null || renderer.sharedMesh == null)
{
if (_mappings.arraySize > 0)
{
_mappings.ClearArray();
}
serializedObject.ApplyModifiedProperties();
return;
}
if (_mappings.arraySize != renderer.sharedMesh.blendShapeCount)
{
_mappings.ClearArray();
_mappings.arraySize = renderer.sharedMesh.blendShapeCount;
for (int i = 0; i < renderer.sharedMesh.blendShapeCount; ++i)
{
_mappings.GetArrayElementAtIndex(i).intValue = (int)OVRFaceExpressions.FaceExpression.Invalid;
}
}
EditorGUILayout.Space();
var enumValues = Enum.GetNames(typeof(OVRCustomFace.RetargetingType));
face.retargetingType = (OVRCustomFace.RetargetingType)
EditorGUILayout.Popup("Custom face structure", (int)face.retargetingType, enumValues);
if (face.retargetingType == OVRCustomFace.RetargetingType.OculusFace
|| face.retargetingType == OVRCustomFace.RetargetingType.Custom
)
{
_showBlendshapes = EditorGUILayout.BeginFoldoutHeaderGroup(_showBlendshapes, "Blendshapes");
if (_showBlendshapes)
{
if (GUILayout.Button("Auto Generate Mapping"))
{
face.AutoMapBlendshapes();
Refresh(face);
}
if (GUILayout.Button("Clear Mapping"))
{
face.ClearBlendshapes();
Refresh(face);
}
EditorGUILayout.Space();
for (int i = 0; i < renderer.sharedMesh.blendShapeCount; ++i)
{
EditorGUILayout.PropertyField(_mappings.GetArrayElementAtIndex(i),
new GUIContent(renderer.sharedMesh.GetBlendShapeName(i)));
}
}
}
EditorGUILayout.EndFoldoutHeaderGroup();
EditorGUILayout.PropertyField(_allowDuplicateMapping,
new GUIContent("Allow duplicate mapping"));
serializedObject.ApplyModifiedProperties();
static void Refresh(OVRCustomFace face)
{
EditorUtility.SetDirty(face);
EditorSceneManager.MarkSceneDirty(face.gameObject.scene);
}
}
internal static bool IsFaceExpressionsConfigured(OVRFace face)
{
return face._faceExpressions != null;
}
internal static void FixFaceExpressions(OVRFace face)
{
Undo.IncrementCurrentGroup();
var gameObject = face.gameObject;
var faceExpressions = face.SearchFaceExpressions();
if (!faceExpressions)
{
faceExpressions = gameObject.AddComponent<OVRFaceExpressions>();
Undo.RegisterCreatedObjectUndo(faceExpressions, "Create OVRFaceExpressions component");
}
Undo.RecordObject(face, "Linked OVRFaceExpression");
face._faceExpressions = faceExpressions;
EditorUtility.SetDirty(face);
EditorSceneManager.MarkSceneDirty(gameObject.scene);
Undo.SetCurrentGroupName("Configure OVRFaceExpressions for OVRCustomFace");
}
private static SkinnedMeshRenderer GetSkinnedMeshRenderer(SerializedProperty property)
{
GameObject targetObject = GetGameObject(property);
if (!targetObject)
return null;
return targetObject.GetComponent<SkinnedMeshRenderer>();
}
private static GameObject GetGameObject(SerializedProperty property)
{
Component targetComponent = property.serializedObject.targetObject as Component;
if (targetComponent && targetComponent.gameObject)
{
return targetComponent.gameObject;
}
return null;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8f1a97fd76f665479039628101a634f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,285 @@
/*
* 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.
*/
using System;
using System.ComponentModel;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
[CustomEditor(typeof(OVRCustomSkeleton))]
public class OVRCustomSkeletonEditor : OVRSkeletonEditor
{
public override void OnInspectorGUI()
{
var skeleton = (OVRCustomSkeleton)target;
base.OnInspectorGUI();
DrawBonesMapping(skeleton);
}
private static void DrawBonesMapping(OVRCustomSkeleton skeleton)
{
if (GUILayout.Button($"Auto Map Bones"))
{
skeleton.AutoMapBones(skeleton.retargetingType);
EditorUtility.SetDirty(skeleton);
EditorSceneManager.MarkSceneDirty(skeleton.gameObject.scene);
}
EditorGUILayout.LabelField("Bones", EditorStyles.boldLabel);
var start = skeleton.GetCurrentStartBoneId();
var end = skeleton.GetCurrentEndBoneId();
if (skeleton.IsValidBone(start) && skeleton.IsValidBone(end))
{
for (var i = (int)start; i < (int)end; ++i)
{
var boneName = OVRSkeleton.BoneLabelFromBoneId(skeleton.GetSkeletonType(), (OVRSkeleton.BoneId)i);
EditorGUI.BeginChangeCheck();
var val =
EditorGUILayout.ObjectField(boneName, skeleton.CustomBones[i], typeof(Transform), true);
if (EditorGUI.EndChangeCheck())
{
skeleton.CustomBones[i] = (Transform)val;
EditorUtility.SetDirty(skeleton);
}
}
}
}
}
/// <summary>
/// Extensions class for the editor methods of <see cref="OVRCustomSkeleton"/>.
/// </summary>
public static class OVRCustomSkeletonEditorExtensions
{
/// <summary>
/// This method tries to retarget the skeleton structure present in the current <see cref="GameObject"/> to the one supported by the body tracking system.
/// </summary>
/// <param name="customSkeleton" cref="OVRCustomSkeleton">The custom skeleton to run this method on</param>
/// <param name="type" cref="OVRCustomSkeleton.RetargetingType">The skeleton structure to auto map from</param>
public static void AutoMapBones(this OVRCustomSkeleton customSkeleton, OVRCustomSkeleton.RetargetingType type)
{
try
{
switch (type)
{
case OVRCustomSkeleton.RetargetingType.OculusSkeleton:
customSkeleton.AutoMapBonesFromOculusSkeleton();
break;
default:
throw new InvalidEnumArgumentException($"Invalid {nameof(OVRCustomSkeleton.RetargetingType)}");
}
}
catch (Exception e)
{
EditorUtility.DisplayDialog($"Auto Map Bones Error", e.Message, "Ok");
}
}
public static void TryAutoMapBonesByName(this OVRCustomSkeleton customSkeleton)
{
customSkeleton.AutoMapBonesFromOculusSkeleton();
}
internal static void AutoMapBonesFromOculusSkeleton(this OVRCustomSkeleton customSkeleton)
{
var start = customSkeleton.GetCurrentStartBoneId();
var end = customSkeleton.GetCurrentEndBoneId();
var skeletonType = customSkeleton.GetSkeletonType();
if (customSkeleton.IsValidBone(start) && customSkeleton.IsValidBone(end))
{
for (var bi = (int)start; bi < (int)end; ++bi)
{
string fbxBoneName = FbxBoneNameFromBoneId(skeletonType, (OVRSkeleton.BoneId)bi);
Transform t = customSkeleton.transform.FindChildRecursive(fbxBoneName);
if (t == null && skeletonType == OVRSkeleton.SkeletonType.Body)
{
var legacyBoneName = fbxBoneName
.Replace("Little", "Pinky")
.Replace("Metacarpal", "Meta");
t = customSkeleton.transform.FindChildRecursive(legacyBoneName);
}
if (t != null)
{
customSkeleton.CustomBones[bi] = t;
}
}
}
}
internal static bool ClearBonesMapping(this OVRCustomSkeleton skeleton)
{
var start = skeleton.GetCurrentStartBoneId();
var end = skeleton.GetCurrentEndBoneId();
var cleared = false;
if (!skeleton.IsValidBone(start) || !skeleton.IsValidBone(end))
{
return false;
}
for (var i = (int)start; i < (int)end; ++i)
{
if (skeleton.CustomBones[i] != null)
{
skeleton.CustomBones[i] = null;
cleared = true;
}
}
return cleared;
}
private static string FbxBoneNameFromBoneId(OVRSkeleton.SkeletonType skeletonType, OVRSkeleton.BoneId bi)
{
if (skeletonType == OVRSkeleton.SkeletonType.Body)
{
return FBXBodyBoneNames[(int)bi];
}
else
{
if (bi >= OVRSkeleton.BoneId.Hand_ThumbTip && bi <= OVRSkeleton.BoneId.Hand_PinkyTip)
{
return FBXHandSidePrefix[(int)skeletonType] +
FBXHandFingerNames[(int)bi - (int)OVRSkeleton.BoneId.Hand_ThumbTip] +
"_finger_tip_marker";
}
else
{
return FBXHandBonePrefix + FBXHandSidePrefix[(int)skeletonType] + FBXHandBoneNames[(int)bi];
}
}
}
private static readonly string[] FBXBodyBoneNames =
{
"Root",
"Hips",
"SpineLower",
"SpineMiddle",
"SpineUpper",
"Chest",
"Neck",
"Head",
"LeftShoulder",
"LeftScapula",
"LeftArmUpper",
"LeftArmLower",
"LeftHandWristTwist",
"RightShoulder",
"RightScapula",
"RightArmUpper",
"RightArmLower",
"RightHandWristTwist",
"LeftHandPalm",
"LeftHandWrist",
"LeftHandThumbMetacarpal",
"LeftHandThumbProximal",
"LeftHandThumbDistal",
"LeftHandThumbTip",
"LeftHandIndexMetacarpal",
"LeftHandIndexProximal",
"LeftHandIndexIntermediate",
"LeftHandIndexDistal",
"LeftHandIndexTip",
"LeftHandMiddleMetacarpal",
"LeftHandMiddleProximal",
"LeftHandMiddleIntermediate",
"LeftHandMiddleDistal",
"LeftHandMiddleTip",
"LeftHandRingMetacarpal",
"LeftHandRingProximal",
"LeftHandRingIntermediate",
"LeftHandRingDistal",
"LeftHandRingTip",
"LeftHandLittleMetacarpal",
"LeftHandLittleProximal",
"LeftHandLittleIntermediate",
"LeftHandLittleDistal",
"LeftHandLittleTip",
"RightHandPalm",
"RightHandWrist",
"RightHandThumbMetacarpal",
"RightHandThumbProximal",
"RightHandThumbDistal",
"RightHandThumbTip",
"RightHandIndexMetacarpal",
"RightHandIndexProximal",
"RightHandIndexIntermediate",
"RightHandIndexDistal",
"RightHandIndexTip",
"RightHandMiddleMetacarpal",
"RightHandMiddleProximal",
"RightHandMiddleIntermediate",
"RightHandMiddleDistal",
"RightHandMiddleTip",
"RightHandRingMetacarpal",
"RightHandRingProximal",
"RightHandRingIntermediate",
"RightHandRingDistal",
"RightHandRingTip",
"RightHandLittleMetacarpal",
"RightHandLittleProximal",
"RightHandLittleIntermediate",
"RightHandLittleDistal",
"RightHandLittleTip"
};
private static readonly string[] FBXHandSidePrefix = { "l_", "r_" };
private const string FBXHandBonePrefix = "b_";
private static readonly string[] FBXHandBoneNames =
{
"wrist",
"forearm_stub",
"thumb0",
"thumb1",
"thumb2",
"thumb3",
"index1",
"index2",
"index3",
"middle1",
"middle2",
"middle3",
"ring1",
"ring2",
"ring3",
"pinky0",
"pinky1",
"pinky2",
"pinky3"
};
private static readonly string[] FBXHandFingerNames =
{
"thumb",
"index",
"middle",
"ring",
"pinky"
};
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 48b4582957a398741abd6d10bcb62042
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,59 @@
/*
* 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.
*/
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
internal class OVREditorUIElements
{
internal static bool RenderWarningWithButton(string labelString, string buttonString)
{
Assert.IsNotNull(labelString);
Assert.IsNotNull(buttonString);
bool isButtonClicked;
GUIContent Warning = EditorGUIUtility.IconContent("console.warnicon@2x");
var alignByCenter = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter };
using (var z = new EditorGUILayout.VerticalScope("HelpBox"))
{
EditorGUI.LabelField(z.rect, Warning, EditorStyles.helpBox);
GUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
var horizontalSpace =
EditorGUIUtility.standardVerticalSpacing * 3 +
EditorGUIUtility.singleLineHeight * 2 + 5;
GUILayout.BeginHorizontal();
GUILayout.Space(horizontalSpace);
GUILayout.Label(labelString, alignByCenter);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Space(horizontalSpace);
isButtonClicked = GUILayout.Button(buttonString);
GUILayout.EndHorizontal();
GUILayout.Space(5);
GUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
}
return isButtonClicked;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2975e40846d349a688592b73d09cb809
timeCreated: 1678595602
@@ -0,0 +1,280 @@
/*
* 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.
*/
using UnityEngine;
using UnityEditor;
using System.Diagnostics;
public static class OVREditorUtil
{
private static GUIContent tooltipLink = new GUIContent("[?]");
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupBoolField(Object target, string name, ref bool member, ref bool modified,
string docLink = "")
{
SetupBoolField(target, new GUIContent(name), ref member, ref modified, docLink);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupBoolField(Object target, GUIContent name, ref bool member, ref bool modified,
string docLink = "")
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
bool value = EditorGUILayout.Toggle(name, member);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
if (!string.IsNullOrEmpty(docLink))
{
DisplayDocLink(docLink);
}
EditorGUILayout.EndHorizontal();
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupIntField(Object target, string name, ref int member, ref bool modified)
{
SetupIntField(target, new GUIContent(name), ref member, ref modified);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupIntField(Object target, GUIContent name, ref int member, ref bool modified)
{
EditorGUI.BeginChangeCheck();
int value = EditorGUILayout.IntField(name, member);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupFloatField(Object target, string name, ref float member, ref bool modified)
{
SetupFloatField(target, new GUIContent(name), ref member, ref modified);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupFloatField(Object target, GUIContent name, ref float member, ref bool modified)
{
EditorGUI.BeginChangeCheck();
float value = EditorGUILayout.FloatField(name, member);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupDoubleField(Object target, string name, ref double member, ref bool modified)
{
SetupDoubleField(target, new GUIContent(name), ref member, ref modified);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupDoubleField(Object target, GUIContent name, ref double member, ref bool modified)
{
EditorGUI.BeginChangeCheck();
double value = EditorGUILayout.DoubleField(name, member);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupColorField(Object target, string name, ref Color member, ref bool modified)
{
SetupColorField(target, new GUIContent(name), ref member, ref modified);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupColorField(Object target, GUIContent name, ref Color member, ref bool modified)
{
EditorGUI.BeginChangeCheck();
Color value = EditorGUILayout.ColorField(name, member);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupLayerMaskField(Object target, string name, ref LayerMask layerMask,
string[] layerMaskOptions, ref bool modified)
{
SetupLayerMaskField(target, new GUIContent(name), ref layerMask, layerMaskOptions, ref modified);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupLayerMaskField(Object target, GUIContent name, ref LayerMask layerMask,
string[] layerMaskOptions, ref bool modified)
{
EditorGUI.BeginChangeCheck();
int value = EditorGUILayout.MaskField(name, layerMask, layerMaskOptions);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
layerMask = value;
}
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupEnumField<T>(Object target, string name, ref T member, ref bool modified,
string docLink = "") where T : struct
{
SetupEnumField(target, new GUIContent(name), ref member, ref modified, docLink);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupEnumField<T>(Object target, GUIContent name, ref T member, ref bool modified,
string docLink = "") where T : struct
{
GUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
T value = (T)(object)EditorGUILayout.EnumPopup(name, member as System.Enum);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
if (!string.IsNullOrEmpty(docLink))
{
DisplayDocLink(docLink);
}
GUILayout.EndHorizontal();
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupInputField(Object target, string name, ref string member, ref bool modified,
string docLink = "")
{
SetupInputField(target, new GUIContent(name), ref member, ref modified, docLink);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupInputField(Object target, GUIContent name, ref string member, ref bool modified,
string docLink = "")
{
GUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
string value = EditorGUILayout.TextField(name, member);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
if (!string.IsNullOrEmpty(docLink))
{
DisplayDocLink(docLink);
}
GUILayout.EndHorizontal();
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupTexture2DField(Object target, string name, ref Texture2D member, ref bool modified)
{
SetupTexture2DField(target, new GUIContent(name), ref member, ref modified);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupTexture2DField(Object target, GUIContent name, ref Texture2D member, ref bool modified,
string docLink = "")
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
Texture2D value = (Texture2D)EditorGUILayout.ObjectField(name, member, typeof(Texture2D), false);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
member = value;
modified = true;
}
if (!string.IsNullOrEmpty(docLink))
{
DisplayDocLink(docLink);
}
EditorGUILayout.EndHorizontal();
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupPopupField(Object target, string name, ref int selectedIndex, GUIContent[] options,
ref bool modified)
{
SetupPopupField(target, new GUIContent(name), ref selectedIndex, options, ref modified);
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void SetupPopupField(Object target, GUIContent name, ref int selectedIndex, GUIContent[] options,
ref bool modified)
{
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.Popup(name, selectedIndex, options);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Changed " + name.text);
selectedIndex = value;
modified = true;
}
}
[Conditional("UNITY_EDITOR_WIN"), Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_ANDROID")]
public static void DisplayDocLink(string docLink)
{
#if UNITY_2021_1_OR_NEWER
if (EditorGUILayout.LinkButton(tooltipLink))
{
Application.OpenURL(docLink);
}
#else
if (GUILayout.Button(tooltipLink, GUILayout.ExpandWidth(false)))
{
Application.OpenURL(docLink);
}
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 00e66be22bd6053489650de094c5efa8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,262 @@
/*
* 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.
*/
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(OVRManager))]
public class OVRManagerEditor : Editor
{
private SerializedProperty _requestBodyTrackingPermissionOnStartup;
private SerializedProperty _requestFaceTrackingPermissionOnStartup;
private SerializedProperty _requestEyeTrackingPermissionOnStartup;
private SerializedProperty _requestScenePermissionOnStartup;
private bool _expandPermissionsRequest;
void OnEnable()
{
_requestBodyTrackingPermissionOnStartup =
serializedObject.FindProperty(nameof(OVRManager.requestBodyTrackingPermissionOnStartup));
_requestFaceTrackingPermissionOnStartup =
serializedObject.FindProperty(nameof(OVRManager.requestFaceTrackingPermissionOnStartup));
_requestEyeTrackingPermissionOnStartup =
serializedObject.FindProperty(nameof(OVRManager.requestEyeTrackingPermissionOnStartup));
_requestScenePermissionOnStartup =
serializedObject.FindProperty(nameof(OVRManager.requestScenePermissionOnStartup));
}
public override void OnInspectorGUI()
{
serializedObject.ApplyModifiedProperties();
OVRRuntimeSettings runtimeSettings = OVRRuntimeSettings.GetRuntimeSettings();
OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
#if UNITY_ANDROID
OVRProjectConfigEditor.DrawTargetDeviceInspector(projectConfig);
EditorGUILayout.Space();
#endif
DrawDefaultInspector();
bool modified = false;
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
OVRManager manager = (OVRManager)target;
EditorGUILayout.Space();
EditorGUILayout.LabelField("Display", EditorStyles.boldLabel);
OVRManager.ColorSpace colorGamut = runtimeSettings.colorSpace;
OVREditorUtil.SetupEnumField(target, new GUIContent("Color Gamut",
"The target color gamut when displayed on the HMD"), ref colorGamut, ref modified,
"https://developer.oculus.com/documentation/unity/unity-color-space/");
manager.colorGamut = colorGamut;
if (modified)
{
runtimeSettings.colorSpace = colorGamut;
OVRRuntimeSettings.CommitRuntimeSettings(runtimeSettings);
}
#endif
EditorGUILayout.Space();
OVRProjectConfigEditor.DrawProjectConfigInspector(projectConfig);
#if UNITY_ANDROID
EditorGUILayout.Space();
EditorGUILayout.LabelField("Mixed Reality Capture for Quest", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
OVREditorUtil.SetupEnumField(target, "ActivationMode", ref manager.mrcActivationMode, ref modified);
EditorGUI.indentLevel--;
#endif
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
manager.expandMixedRealityCapturePropertySheet =
EditorGUILayout.BeginFoldoutHeaderGroup(manager.expandMixedRealityCapturePropertySheet,
"Mixed Reality Capture");
OVREditorUtil.DisplayDocLink("https://developer.oculus.com/documentation/unity/unity-mrc/");
EditorGUILayout.EndHorizontal();
if (manager.expandMixedRealityCapturePropertySheet)
{
string[] layerMaskOptions = new string[32];
for (int i = 0; i < 32; ++i)
{
layerMaskOptions[i] = LayerMask.LayerToName(i);
if (layerMaskOptions[i].Length == 0)
{
layerMaskOptions[i] = "<Layer " + i.ToString() + ">";
}
}
EditorGUI.indentLevel++;
OVREditorUtil.SetupBoolField(target, "Enable MixedRealityCapture", ref manager.enableMixedReality,
ref modified);
OVREditorUtil.SetupEnumField(target, "Composition Method", ref manager.compositionMethod, ref modified);
OVREditorUtil.SetupLayerMaskField(target, "Extra Hidden Layers", ref manager.extraHiddenLayers,
layerMaskOptions, ref modified);
OVREditorUtil.SetupLayerMaskField(target, "Extra Visible Layers", ref manager.extraVisibleLayers,
layerMaskOptions, ref modified);
OVREditorUtil.SetupBoolField(target, "Dynamic Culling Mask", ref manager.dynamicCullingMask, ref modified);
// CompositionMethod.External is the only composition method that is available.
// All other deprecated composition methods should fallback to the path below.
{
// CompositionMethod.External
EditorGUILayout.Space();
EditorGUILayout.LabelField("External Composition", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
OVREditorUtil.SetupColorField(target, "Backdrop Color (Target, Rift)",
ref manager.externalCompositionBackdropColorRift, ref modified);
OVREditorUtil.SetupColorField(target, "Backdrop Color (Target, Quest)",
ref manager.externalCompositionBackdropColorQuest, ref modified);
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndFoldoutHeaderGroup();
#endif
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
// Multimodal hands and controllers section
#if UNITY_ANDROID
bool launchMultimodalHandsControllersOnStartup =
projectConfig.multimodalHandsControllersSupport != OVRProjectConfig.MultimodalHandsControllersSupport.Disabled;
EditorGUI.BeginDisabledGroup(!launchMultimodalHandsControllersOnStartup);
GUIContent enableConcurrentHandsAndControllersOnStartup = new GUIContent("Launch concurrent hands and controllers mode on startup",
"Launches concurrent hands and controllers on startup for the scene. Concurrent Hands and Controllers Capability must be enabled in the project settings.");
#else
GUIContent enableConcurrentHandsAndControllersOnStartup = new GUIContent("Enable concurrent hands and controllers mode on startup",
"Launches concurrent hands and controllers on startup for the scene.");
#endif
EditorGUILayout.Space();
EditorGUILayout.LabelField("Concurrent hands and controllers", EditorStyles.boldLabel);
#if UNITY_ANDROID
if (!launchMultimodalHandsControllersOnStartup)
{
EditorGUILayout.LabelField(
"Requires Concurrent Hands and Controllers Capability to be enabled in the General section of the Quest features.",
EditorStyles.wordWrappedLabel);
}
#endif
OVREditorUtil.SetupBoolField(target, enableConcurrentHandsAndControllersOnStartup,
ref manager.launchMultimodalHandsControllersOnStartup,
ref modified);
#if UNITY_ANDROID
EditorGUI.EndDisabledGroup();
#endif
#endif
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
// Insight Passthrough section
#if UNITY_ANDROID
bool passthroughCapabilityEnabled =
projectConfig.insightPassthroughSupport != OVRProjectConfig.FeatureSupport.None;
EditorGUI.BeginDisabledGroup(!passthroughCapabilityEnabled);
GUIContent enablePassthroughContent = new GUIContent("Enable Passthrough",
"Enables passthrough functionality for the scene. Can be toggled at runtime. Passthrough Capability must be enabled in the project settings.");
#else
GUIContent enablePassthroughContent = new GUIContent("Enable Passthrough",
"Enables passthrough functionality for the scene. Can be toggled at runtime.");
#endif
EditorGUILayout.Space();
EditorGUILayout.LabelField("Insight Passthrough", EditorStyles.boldLabel);
#if UNITY_ANDROID
if (!passthroughCapabilityEnabled)
{
EditorGUILayout.LabelField(
"Requires Passthrough Capability to be enabled in the General section of the Quest features.",
EditorStyles.wordWrappedLabel);
}
#endif
OVREditorUtil.SetupBoolField(target, enablePassthroughContent, ref manager.isInsightPassthroughEnabled,
ref modified);
#if UNITY_ANDROID
EditorGUI.EndDisabledGroup();
#endif
#endif
#region PermissionRequests
EditorGUILayout.Space();
_expandPermissionsRequest =
EditorGUILayout.BeginFoldoutHeaderGroup(_expandPermissionsRequest, "Permission Requests On Startup");
if (_expandPermissionsRequest)
{
void AddPermissionGroup(bool featureEnabled, string permissionName, SerializedProperty property)
{
using (new EditorGUI.DisabledScope(!featureEnabled))
{
if (!featureEnabled)
{
EditorGUILayout.LabelField(
$"Requires {permissionName} Capability to be enabled in the Quest features section.",
EditorStyles.wordWrappedLabel);
}
var label = new GUIContent(permissionName,
$"Requests {permissionName} permission on start up. {permissionName} Capability must be enabled in the project settings.");
EditorGUILayout.PropertyField(property, label);
}
}
AddPermissionGroup(projectConfig.bodyTrackingSupport != OVRProjectConfig.FeatureSupport.None,
"Body Tracking", _requestBodyTrackingPermissionOnStartup);
AddPermissionGroup(projectConfig.faceTrackingSupport != OVRProjectConfig.FeatureSupport.None,
"Face Tracking", _requestFaceTrackingPermissionOnStartup);
AddPermissionGroup(projectConfig.eyeTrackingSupport != OVRProjectConfig.FeatureSupport.None, "Eye Tracking",
_requestEyeTrackingPermissionOnStartup);
AddPermissionGroup(projectConfig.sceneSupport != OVRProjectConfig.FeatureSupport.None, "Scene",
_requestScenePermissionOnStartup);
}
EditorGUILayout.EndFoldoutHeaderGroup();
#endregion
if (modified)
{
EditorUtility.SetDirty(target);
}
serializedObject.ApplyModifiedProperties();
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
#if !OCULUS_XR_3_3_0_OR_NEWER || UNITY_2020
if (manager.enableDynamicResolution && !PlayerSettings.GetUseDefaultGraphicsAPIs(BuildTarget.Android))
{
UnityEngine.Rendering.GraphicsDeviceType[] apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android);
if (apis.Length >= 1 && apis[0] == UnityEngine.Rendering.GraphicsDeviceType.Vulkan)
{
Debug.LogError("Vulkan Dynamic Resolution is not supported on your current build version. Ensure you are on Unity 2021+ with Oculus XR plugin v3.3.0+");
manager.enableDynamicResolution = false;
}
}
#endif
#endif
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9b07d18088099f94fa00fc15e64b2b17
timeCreated: 1502747851
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,216 @@
/*
* 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.
*/
using System;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
internal static class OVRMovementTool
{
private const string k_SetupCharacterForBodyTrackingMovementToolsMenuStr =
"GameObject/Movement/Setup Character for Body Tracking/";
private const string oculusSkeletonFormat = "Format: Oculus Skeleton";
[MenuItem(k_SetupCharacterForBodyTrackingMovementToolsMenuStr + oculusSkeletonFormat, true)]
private static bool ValidateSetupCharacterForOculusSkeletonBodyTracking()
{
return Selection.activeGameObject != null;
}
[MenuItem(k_SetupCharacterForBodyTrackingMovementToolsMenuStr + oculusSkeletonFormat)]
private static void SetupCharacterForOculusSkeletonBodyTracking()
{
SetUpCharacterForBodyTracking(OVRCustomSkeleton.RetargetingType.OculusSkeleton);
}
private static void SetUpCharacterForBodyTracking(OVRCustomSkeleton.RetargetingType retargetingType)
{
Undo.IncrementCurrentGroup();
var gameObject = Selection.activeGameObject;
var body = gameObject.GetComponentInParent<OVRBody>();
if (!body)
{
body = gameObject.AddComponent<OVRBody>();
Undo.RegisterCreatedObjectUndo(body, "Create OVRBody component");
}
var skeleton = gameObject.GetComponent<OVRCustomSkeleton>();
if (!skeleton)
{
skeleton = gameObject.AddComponent<OVRCustomSkeleton>();
Undo.RegisterCreatedObjectUndo(skeleton, "Create OVRCustomSkeleton component");
}
Undo.RegisterFullObjectHierarchyUndo(skeleton, "Auto-map OVRCustomSkeleton bones");
skeleton.SetSkeletonType(OVRSkeleton.SkeletonType.Body);
skeleton.retargetingType = retargetingType;
skeleton.AutoMapBones(retargetingType);
EditorUtility.SetDirty(skeleton);
EditorSceneManager.MarkSceneDirty(skeleton.gameObject.scene);
var projectConfig = OVRProjectConfig.CachedProjectConfig;
projectConfig.bodyTrackingSupport = OVRProjectConfig.FeatureSupport.Supported;
OVRProjectConfig.CommitProjectConfig(projectConfig);
var ovrManager = OVRProjectSetupUtils.FindComponentInScene<OVRManager>();
if (ovrManager != null)
{
ovrManager.requestBodyTrackingPermissionOnStartup = true;
EditorUtility.SetDirty(ovrManager);
}
Undo.SetCurrentGroupName($"Setup Character for {retargetingType} Body Tracking");
}
private const string unityHumanoidFormat = "Format: Unity Humanoid";
[MenuItem(k_SetupCharacterForBodyTrackingMovementToolsMenuStr + unityHumanoidFormat, true)]
private static bool ValidateSetupCharacterForUnityHumanoidBodyTracking()
{
return Selection.activeGameObject != null;
}
[MenuItem(k_SetupCharacterForBodyTrackingMovementToolsMenuStr + unityHumanoidFormat)]
private static void SetupCharacterForUnityHumanoidBodyTracking()
{
try
{
OVRUnityHumanoidSkeletonRetargeter
.ValidateGameObjectForUnityHumanoidRetargeting(Selection.activeGameObject);
}
catch (InvalidOperationException e)
{
EditorUtility.DisplayDialog("Character Setup Error", e.Message, "Ok");
return;
}
SetUpCharacterForUnityHumanoidRetargeting();
}
private static void SetUpCharacterForUnityHumanoidRetargeting()
{
Undo.IncrementCurrentGroup();
var gameObject = Selection.activeGameObject;
var body = gameObject.GetComponent<OVRBody>();
if (!body)
{
body = gameObject.AddComponent<OVRBody>();
Undo.RegisterCreatedObjectUndo(body, "Create OVRBody component");
}
var skeleton = gameObject.GetComponent<OVRUnityHumanoidSkeletonRetargeter>();
if (!skeleton)
{
skeleton = gameObject.AddComponent<OVRUnityHumanoidSkeletonRetargeter>();
Undo.RegisterCreatedObjectUndo(skeleton, "Create OVRCustomSkeleton component");
}
Undo.RegisterFullObjectHierarchyUndo(skeleton, "Auto-map OVRCustomSkeleton bones");
EditorUtility.SetDirty(skeleton);
EditorSceneManager.MarkSceneDirty(skeleton.gameObject.scene);
Undo.SetCurrentGroupName($"Setup Character for Unity Humanoid Retargeting");
}
private const string k_SetupCharacterForFaceTrackingMovementToolsMenuStr =
"GameObject/Movement/Setup Character for Face Tracking/";
private const string oculusFaceFormat = "Format: Oculus Face";
[MenuItem(k_SetupCharacterForFaceTrackingMovementToolsMenuStr + oculusFaceFormat, true)]
private static bool ValidateSetupCharacterForOculusFaceTracking()
{
return Selection.activeGameObject != null;
}
[MenuItem(k_SetupCharacterForFaceTrackingMovementToolsMenuStr + oculusFaceFormat)]
private static void SetupCharacterForOculusFaceTracking()
{
try
{
ValidateGameObjectFaceRetargeting(Selection.activeGameObject);
}
catch (InvalidOperationException e)
{
EditorUtility.DisplayDialog("Character Setup Error", e.Message, "Ok");
return;
}
SetUpCharacterForFaceTracking(OVRCustomFace.RetargetingType.OculusFace);
}
public static void ValidateGameObjectFaceRetargeting(GameObject go)
{
var renderer = go.GetComponent<SkinnedMeshRenderer>();
if (renderer == null || renderer.sharedMesh == null || renderer.sharedMesh.blendShapeCount == 0)
{
throw new InvalidOperationException(
$"Retargeting to Oculus Face requires an {nameof(SkinnedMeshRenderer)} component with a mesh that contains blendshapes.");
}
}
private static void SetUpCharacterForFaceTracking(OVRCustomFace.RetargetingType retargetingType)
{
Undo.IncrementCurrentGroup();
var gameObject = Selection.activeGameObject;
var faceExpressions = gameObject.GetComponentInParent<OVRFaceExpressions>();
if (!faceExpressions)
{
faceExpressions = gameObject.AddComponent<OVRFaceExpressions>();
Undo.RegisterCreatedObjectUndo(faceExpressions, "Create OVRFaceExpressions component");
}
var face = gameObject.GetComponent<OVRCustomFace>();
if (!face)
{
face = gameObject.AddComponent<OVRCustomFace>();
face._faceExpressions = faceExpressions;
Undo.RegisterCreatedObjectUndo(face, "Create OVRCustomFace component");
}
Undo.RegisterFullObjectHierarchyUndo(face, "Auto-map OVRCustomFace blendshapes");
face.retargetingType = retargetingType;
face.AutoMapBlendshapes();
EditorUtility.SetDirty(face);
EditorSceneManager.MarkSceneDirty(face.gameObject.scene);
var projectConfig = OVRProjectConfig.CachedProjectConfig;
projectConfig.faceTrackingSupport = OVRProjectConfig.FeatureSupport.Supported;
OVRProjectConfig.CommitProjectConfig(projectConfig);
var ovrManager = OVRProjectSetupUtils.FindComponentInScene<OVRManager>();
if (ovrManager != null)
{
ovrManager.requestFaceTrackingPermissionOnStartup = true;
EditorUtility.SetDirty(ovrManager);
}
Undo.SetCurrentGroupName($"Setup Character for {retargetingType} Face Tracking");
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 285832bbc0585ef40ba90095a03c9449
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,141 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities 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.
************************************************************************************/
Shader "Unlit/OVROverlayDestRectEditor"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_PaddingAndSize("Padding And Size", Vector) = (4, 4, 128, 128)
_SrcRect("SrcRect", Vector) = (0,0,1,1)
_DestRect ("DestRect", Vector) = (0,0,1,1)
_DragColor ("DragColor", Color) = (1,0,0,1)
_BackgroundColor("Background Color", Color) = (0.278, 0.278, 0.278, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 dragLeftRight : TEXCOORD1;
float4 dragTopBottom : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _PaddingAndSize;
float4 _SrcRect;
float4 _DestRect;
float4 _DragColor;
float4 _BackgroundColor;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Add padding
o.uv = (o.uv - 0.5) * (_PaddingAndSize.xy + _PaddingAndSize.zw) / _PaddingAndSize.zw + 0.5;
// left
o.dragLeftRight.x = _DestRect.x;
o.dragLeftRight.y = _DestRect.y + _DestRect.w * 0.5;
// right
o.dragLeftRight.z = _DestRect.x + _DestRect.z;
o.dragLeftRight.w = _DestRect.y + _DestRect.w * 0.5;
// top
o.dragTopBottom.x = _DestRect.x + _DestRect.z * 0.5;
o.dragTopBottom.y = _DestRect.y;
// bottom
o.dragTopBottom.z = _DestRect.x + _DestRect.z * 0.5;
o.dragTopBottom.w = _DestRect.y + _DestRect.w;
return o;
}
float onDrag(const float2 uv, const float2 xy)
{
const float2 handleSize = (_PaddingAndSize.xy / 2.0 + 1.0) / _PaddingAndSize.zw;
const float2 offset = abs(uv - xy);
return offset.x <= handleSize.x && offset.y <= handleSize.y;
}
float onLine(const float2 uv, const float4 rect)
{
return
(abs(uv.x - rect.x) < (1 / _PaddingAndSize.z) && uv.y >= rect.y && uv.y <= rect.y + rect.w) ||
(abs(uv.x - rect.x - rect.z) < (1 / _PaddingAndSize.z) && uv.y >= rect.y && uv.y <= rect.y + rect.w) ||
(abs(uv.y - rect.y) < (1 / _PaddingAndSize.w) && uv.x >= rect.x && uv.x <= rect.x + rect.z) ||
(abs(uv.y - rect.y - rect.w) < (1 / _PaddingAndSize.w) && uv.x >= rect.x && uv.x <= rect.x + rect.z);
}
float checkerboard(const float2 uv)
{
const float2 xy = floor(uv * (_PaddingAndSize.xy + _PaddingAndSize.zw) / 8 - _PaddingAndSize.xy / 8);
return xy.x + xy.y - 2.0 * floor((xy.x + xy.y) / 2.0);
}
fixed4 frag (v2f i) : SV_Target
{
const float2 uv = (i.uv - _DestRect.xy) / _DestRect.zw;
const float2 texcoord = uv * _SrcRect.zw + _SrcRect.xy;
// sample the texture
fixed4 col = tex2D(_MainTex, texcoord);
if (uv.x < 0 || uv.x > 1 || uv.y < 0 || uv.y > 1)
{
col.a = 0;
}
col.rgb = lerp(0.41 - 0.13 * checkerboard(i.uv), col.rgb, col.a);
if (i.uv.x < 0 || i.uv.x > 1 || i.uv.y < 0 || i.uv.y > 1)
{
col = _BackgroundColor;
}
// now draw clipping objects
const float drag = onLine(i.uv, _DestRect) ||
onDrag(uv, i.dragLeftRight.xy) ||
onDrag(uv, i.dragLeftRight.zw) ||
onDrag(uv, i.dragTopBottom.xy) ||
onDrag(uv, i.dragTopBottom.zw);
return lerp(col, _DragColor, drag);
}
ENDCG
}
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7c52c9bacdbb59f4a973dd1849d03106
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fde3aeb28643f6c48a48f926ac7207e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,132 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities 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.
************************************************************************************/
Shader "Unlit/OVROverlaySrcRectEditor"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_PaddingAndSize("Padding And Size", Vector) = (4, 4, 128, 128)
_SrcRect ("SrcRect", Vector) = (0,0,1,1)
_DragColor ("DragColor", Color) = (1, 0, 0, 1)
_BackgroundColor("Background Color", Color) = (0.278, 0.278, 0.278, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 dragLeftRight : TEXCOORD1;
float4 dragTopBottom : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _PaddingAndSize;
float4 _SrcRect;
float4 _DragColor;
float4 _BackgroundColor;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Add padding
o.uv = (o.uv - 0.5) * (_PaddingAndSize.xy + _PaddingAndSize.zw) / _PaddingAndSize.zw + 0.5;
// left
o.dragLeftRight.x = _SrcRect.x;
o.dragLeftRight.y = _SrcRect.y + _SrcRect.w * 0.5;
// right
o.dragLeftRight.z = _SrcRect.x + _SrcRect.z;
o.dragLeftRight.w = _SrcRect.y + _SrcRect.w * 0.5;
// top
o.dragTopBottom.x = _SrcRect.x + _SrcRect.z * 0.5;
o.dragTopBottom.y = _SrcRect.y;
// bottom
o.dragTopBottom.z = _SrcRect.x + _SrcRect.z * 0.5;
o.dragTopBottom.w = _SrcRect.y + _SrcRect.w;
return o;
}
float onDrag(const float2 uv, const float2 xy)
{
const float2 handleSize = (_PaddingAndSize.xy / 2.0 + 1.0) / _PaddingAndSize.zw;
const float2 offset = abs(uv - xy);
return offset.x <= handleSize.x && offset.y <= handleSize.y;
}
float onLine(const float2 uv, const float4 rect)
{
return
(abs(uv.x - rect.x) < (1 / _PaddingAndSize.z) && uv.y >= rect.y && uv.y <= rect.y + rect.w) ||
(abs(uv.x - rect.x - rect.z) < (1 / _PaddingAndSize.z) && uv.y >= rect.y && uv.y <= rect.y + rect.w) ||
(abs(uv.y - rect.y) < (1 / _PaddingAndSize.w) && uv.x >= rect.x && uv.x <= rect.x + rect.z) ||
(abs(uv.y - rect.y - rect.w) < (1 / _PaddingAndSize.w) && uv.x >= rect.x && uv.x <= rect.x + rect.z);
}
float checkerboard(const float2 uv)
{
const float2 xy = floor(uv * (_PaddingAndSize.xy + _PaddingAndSize.zw) / 8 - _PaddingAndSize.xy / 8);
return xy.x + xy.y - 2.0 * floor((xy.x + xy.y) / 2.0);
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = lerp(0.41 - 0.13 * checkerboard(i.uv), col.rgb, col.a);
if (i.uv.x < 0 || i.uv.x > 1 || i.uv.y < 0 || i.uv.y > 1)
{
col = _BackgroundColor;
}
const float2 uv = i.uv;
// now draw clipping objects
const float drag = onLine(uv, _SrcRect) ||
onDrag(uv, i.dragLeftRight.xy) ||
onDrag(uv, i.dragLeftRight.zw) ||
onDrag(uv, i.dragTopBottom.xy) ||
onDrag(uv, i.dragTopBottom.zw);
return lerp(col, _DragColor, drag);
}
ENDCG
}
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 589b36d0aa66c7349bcff8750b670434
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,324 @@
/*
* 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.
*/
using System;
using UnityEditor;
using UnityEngine;
using ColorMapEditorType = OVRPassthroughLayer.ColorMapEditorType;
[CustomPropertyDrawer(typeof(OVRPassthroughLayer.SerializedSurfaceGeometry))]
class SerializedSurfaceGeometryPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// Find the SerializedProperties by name
var meshFilterProperty =
property.FindPropertyRelative(nameof(OVRPassthroughLayer.SerializedSurfaceGeometry.meshFilter));
var updateTransformProperty =
property.FindPropertyRelative(nameof(OVRPassthroughLayer.SerializedSurfaceGeometry.updateTransform));
var propertyHeight = position.height / 2;
using (new EditorGUI.PropertyScope(position, label, property))
{
var surfaceGeometryPropertyPosition = new Rect(position.x, position.y, position.width, propertyHeight);
EditorGUI.PropertyField(surfaceGeometryPropertyPosition, meshFilterProperty,
new GUIContent("Surface Geometry", "The GameObject from which to generate surface geometry."));
var heightOffset = EditorGUI.GetPropertyHeight(updateTransformProperty) +
EditorGUIUtility.standardVerticalSpacing;
var updateTransformPosition =
new Rect(position.x, position.y + heightOffset, position.width, propertyHeight);
EditorGUI.PropertyField(updateTransformPosition, updateTransformProperty, new GUIContent("Update Transform",
"When enabled, updates the mesh's transform every frame. Use this if the GameObject is dynamic."));
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return base.GetPropertyHeight(property, label) * 2.2f;
}
}
[CustomEditor(typeof(OVRPassthroughLayer))]
public class OVRPassthroughLayerEditor : Editor
{
private readonly static string[] _colorMapNames =
{
"None",
"Color Adjustment",
"Grayscale",
"Grayscale to Color",
"Color LUT",
"Blended Color LUTs",
"Custom"
};
private readonly static string[] _selectableColorMapNames =
{
_colorMapNames[0],
_colorMapNames[1],
_colorMapNames[2],
_colorMapNames[3],
_colorMapNames[4],
_colorMapNames[5]
};
private ColorMapEditorType[] _colorMapTypes =
{
ColorMapEditorType.None,
ColorMapEditorType.ColorAdjustment,
ColorMapEditorType.Grayscale,
ColorMapEditorType.GrayscaleToColor,
ColorMapEditorType.ColorLut,
ColorMapEditorType.InterpolatedColorLut,
ColorMapEditorType.Custom
};
private SerializedProperty _projectionSurfaces;
private SerializedProperty _propProjectionSurfaceType;
private SerializedProperty _propOverlayType;
private SerializedProperty _propCompositionDepth;
private SerializedProperty _propTextureOpacity;
private SerializedProperty _propEdgeRenderingEnabled;
private SerializedProperty _propEdgeColor;
private SerializedProperty _propColorMapEditorContrast;
private SerializedProperty _propColorMapEditorBrightness;
private SerializedProperty _propColorMapEditorPosterize;
private SerializedProperty _propColorMapEditorGradient;
private SerializedProperty _propColorMapEditorSaturation;
private SerializedProperty _propColorLutSourceTexture;
private SerializedProperty _propColorLutTargetTexture;
private SerializedProperty _propLutWeight;
private SerializedProperty _propFlipLutY;
void OnEnable()
{
_projectionSurfaces = serializedObject.FindProperty(nameof(OVRPassthroughLayer.serializedSurfaceGeometry));
_propProjectionSurfaceType = serializedObject.FindProperty(nameof(OVRPassthroughLayer.projectionSurfaceType));
_propOverlayType = serializedObject.FindProperty(nameof(OVRPassthroughLayer.overlayType));
_propCompositionDepth = serializedObject.FindProperty(nameof(OVRPassthroughLayer.compositionDepth));
_propTextureOpacity = serializedObject.FindProperty(nameof(OVRPassthroughLayer.textureOpacity_));
_propEdgeRenderingEnabled = serializedObject.FindProperty(nameof(OVRPassthroughLayer.edgeRenderingEnabled_));
_propEdgeColor = serializedObject.FindProperty(nameof(OVRPassthroughLayer.edgeColor_));
_propColorMapEditorContrast = serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorContrast));
_propColorMapEditorBrightness =
serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorBrightness));
_propColorMapEditorPosterize =
serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorPosterize));
_propColorMapEditorSaturation =
serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorSaturation));
_propColorMapEditorGradient = serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorGradient));
_propColorLutSourceTexture = serializedObject.FindProperty(nameof(OVRPassthroughLayer._colorLutSourceTexture));
_propColorLutTargetTexture = serializedObject.FindProperty(nameof(OVRPassthroughLayer._colorLutTargetTexture));
_propLutWeight = serializedObject.FindProperty(nameof(OVRPassthroughLayer._lutWeight));
_propFlipLutY = serializedObject.FindProperty(nameof(OVRPassthroughLayer._flipLutY));
}
public override void OnInspectorGUI()
{
OVRPassthroughLayer layer = (OVRPassthroughLayer)target;
serializedObject.Update();
EditorGUILayout.PropertyField(_propProjectionSurfaceType,
new GUIContent("Projection Surface", "The type of projection surface for this Passthrough layer"));
if (layer.projectionSurfaceType == OVRPassthroughLayer.ProjectionSurfaceType.UserDefined)
{
EditorGUILayout.PropertyField(_projectionSurfaces, new GUIContent("Projection Surfaces"));
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Compositing", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(_propOverlayType,
new GUIContent("Placement", "Whether this overlay should layer behind the scene or in front of it"));
EditorGUILayout.PropertyField(_propCompositionDepth,
new GUIContent("Composition Depth",
"Depth value used to sort layers in the scene, smaller value appears in front"));
EditorGUILayout.Space();
EditorGUILayout.LabelField("Style", EditorStyles.boldLabel);
EditorGUILayout.Slider(_propTextureOpacity, 0, 1f, new GUIContent("Opacity"));
EditorGUILayout.Space();
EditorGUILayout.PropertyField(_propEdgeRenderingEnabled,
new GUIContent("Edge Rendering", "Highlight salient edges in the camera images in a specific color"));
EditorGUILayout.PropertyField(_propEdgeColor, new GUIContent("Edge Color"));
if (serializedObject.ApplyModifiedProperties())
{
layer.SetStyleDirty();
}
layer.textureOpacity = _propTextureOpacity.floatValue;
layer.edgeRenderingEnabled = _propEdgeRenderingEnabled.boolValue;
layer.edgeColor = _propEdgeColor.colorValue;
EditorGUILayout.Space();
// Custom popup for color map type to control order, names, and visibility of types
int colorMapTypeIndex = Array.IndexOf(_colorMapTypes, layer.colorMapEditorType);
if (colorMapTypeIndex == -1)
{
Debug.LogWarning("Invalid color map type encountered");
colorMapTypeIndex = 0;
}
// Dropdown list contains "Custom" only if it is currently selected.
string[] colorMapNames = layer.colorMapEditorType == ColorMapEditorType.Custom
? _colorMapNames
: _selectableColorMapNames;
GUIContent[] colorMapLabels = new GUIContent[colorMapNames.Length];
for (int i = 0; i < colorMapNames.Length; i++)
colorMapLabels[i] = new GUIContent(colorMapNames[i]);
bool modified = false;
OVREditorUtil.SetupPopupField(target,
new GUIContent("Color Control", "The type of color controls applied to this layer"), ref colorMapTypeIndex,
colorMapLabels,
ref modified);
layer.colorMapEditorType = _colorMapTypes[colorMapTypeIndex];
if (layer.colorMapEditorType == ColorMapEditorType.Grayscale
|| layer.colorMapEditorType == ColorMapEditorType.GrayscaleToColor
|| layer.colorMapEditorType == ColorMapEditorType.ColorAdjustment)
{
EditorGUILayout.PropertyField(_propColorMapEditorContrast, new GUIContent("Contrast"));
EditorGUILayout.PropertyField(_propColorMapEditorBrightness, new GUIContent("Brightness"));
}
if (layer.colorMapEditorType == ColorMapEditorType.Grayscale
|| layer.colorMapEditorType == ColorMapEditorType.GrayscaleToColor)
{
EditorGUILayout.PropertyField(_propColorMapEditorPosterize, new GUIContent("Posterize"));
}
if (layer.colorMapEditorType == ColorMapEditorType.ColorAdjustment)
{
EditorGUILayout.PropertyField(_propColorMapEditorSaturation, new GUIContent("Saturation"));
}
if (layer.colorMapEditorType == ColorMapEditorType.GrayscaleToColor)
{
EditorGUILayout.PropertyField(_propColorMapEditorGradient, new GUIContent("Colorize"));
}
if (layer.colorMapEditorType == ColorMapEditorType.ColorLut
|| layer.colorMapEditorType == ColorMapEditorType.InterpolatedColorLut)
{
var sourceLutLabel = layer.colorMapEditorType == ColorMapEditorType.ColorLut
? "LUT"
: "Source LUT";
EditorGUILayout.PropertyField(_propColorLutSourceTexture, new GUIContent(sourceLutLabel));
PerformLutTextureCheck((Texture2D)_propColorLutSourceTexture.objectReferenceValue);
if (layer.colorMapEditorType == ColorMapEditorType.InterpolatedColorLut)
{
EditorGUILayout.PropertyField(_propColorLutTargetTexture, new GUIContent("Target LUT"));
PerformLutTextureCheck((Texture2D)_propColorLutTargetTexture.objectReferenceValue);
}
var flipLutYTooltip = "Flip LUT textures along the vertical axis on load. This is needed for LUT " +
"images which have color (0, 0, 0) in the top-left corner. Some color grading systems, " +
"e.g. Unity post-processing, have color (0, 0, 0) in the bottom-left corner, " +
"in which case flipping is not needed.";
EditorGUILayout.PropertyField(_propFlipLutY, new GUIContent("Flip Vertically", flipLutYTooltip));
var weightTooltip = layer.colorMapEditorType == ColorMapEditorType.ColorLut
? "Blend between the original colors and the specified LUT. A value of 0 leaves the colors unchanged, a value of 1 fully applies the LUT."
: "Blend between the source and the target LUT. A value of 0 fully applies the source LUT and a value of 1 fully applies the target LUT.";
EditorGUILayout.PropertyField(_propLutWeight, new GUIContent("Blend", weightTooltip));
}
serializedObject.ApplyModifiedProperties();
}
private void PerformLutTextureCheck(Texture2D texture)
{
if (texture != null)
{
if (!OVRPassthroughColorLut.IsTextureSupported(texture, out var message))
{
EditorGUILayout.HelpBox(message, MessageType.Error);
}
CheckLutImportSettings(texture);
}
}
private void CheckLutImportSettings(Texture lut)
{
if (lut != null)
{
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(lut)) as TextureImporter;
// Fails when using an internal texture as you can't change import settings on
// builtin resources, thus the check for null
if (importer != null)
{
bool isReadable = importer.isReadable == true;
bool isUncompressed = importer.textureCompression == TextureImporterCompression.Uncompressed;
bool valid = isReadable && isUncompressed;
if (!valid)
{
string warningMessage = ""
+ (isReadable ? "" : "Texture is not readable. ")
+ (isUncompressed ? "" : "Texture is compressed.");
DrawFixMeBox(warningMessage, () => SetLutImportSettings(importer));
}
}
}
}
private void SetLutImportSettings(TextureImporter importer)
{
importer.isReadable = true;
importer.textureCompression = TextureImporterCompression.Uncompressed;
importer.SaveAndReimport();
AssetDatabase.Refresh();
}
private void DrawFixMeBox(string text, Action action)
{
EditorGUILayout.HelpBox(text, MessageType.Warning);
GUILayout.Space(-32);
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (GUILayout.Button("Fix", GUILayout.Width(60)))
action();
GUILayout.Space(8);
}
GUILayout.Space(11);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f49479bb47dde564680a2a5bdf5a6dfe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
/*
* 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 UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using Assets.OVR.Scripts;
public class OVRProfilerDeprecated : EditorWindow
{
[MenuItem("Oculus/Tools/(Deprecated) OVR Profiler", false, 200000)]
static void Init()
{
Debug.LogWarning("OVR Profiler has been replaced by OVR Performance Lint Tool");
// Get existing open window or if none, make a new one:
EditorWindow.GetWindow(typeof(OVRLint));
OVRPlugin.SendEvent("perf_lint", "activated");
OVRLint.RunCheck();
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad582af7c1f87bf4b99ef951b26ec465
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,399 @@
/*
* 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.
*/
using System;
using Oculus.VR.Editor;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(OVRProjectConfig))]
public class OVRProjectConfigEditor : Editor
{
override public void OnInspectorGUI()
{
OVRProjectConfig projectConfig = (OVRProjectConfig)target;
DrawTargetDeviceInspector(projectConfig);
EditorGUILayout.Space();
DrawProjectConfigInspector(projectConfig);
}
public static void DrawTargetDeviceInspector(OVRProjectConfig projectConfig)
{
// Target Devices
EditorGUILayout.LabelField("Target Devices", EditorStyles.boldLabel);
bool useOculusXRSettings = false;
#if PRIORITIZE_OCULUS_XR_SETTINGS
EditorGUILayout.LabelField("Configure Target Devices in Oculus XR Plugin Settings:", GUILayout.Width(320));
if (GUILayout.Button("Open Settings"))
SettingsService.OpenProjectSettings("Project/XR Plug-in Management/Oculus");
#if !USING_QUEST_PRO_COMPATIBLE_OCULUS_XR_PLUGIN_VERSION
++EditorGUI.indentLevel;
EditorGUILayout.LabelField("Note: The currently installed Oculus XR Plugin version does not support "
+ "configuring Quest Pro as a target device. Please install a compatible version:"
+ "\n2.2.0-preview.1 or newer\n3.2.1 or newer", EditorStyles.wordWrappedMiniLabel);
--EditorGUI.indentLevel;
#endif
useOculusXRSettings = true;
#endif
if (!useOculusXRSettings)
{
bool hasModified = false;
foreach (OVRProjectConfig.DeviceType deviceType in System.Enum.GetValues(
typeof(OVRProjectConfig.DeviceType)))
{
bool oldSupportsDevice = projectConfig.targetDeviceTypes.Contains(deviceType);
bool newSupportsDevice = oldSupportsDevice;
if (deviceType == OVRProjectConfig.DeviceType.Quest)
{
continue;
}
OVREditorUtil.SetupBoolField(projectConfig, ObjectNames.NicifyVariableName(deviceType.ToString()),
ref newSupportsDevice, ref hasModified);
if (newSupportsDevice && !oldSupportsDevice)
{
projectConfig.targetDeviceTypes.Add(deviceType);
}
else if (oldSupportsDevice && !newSupportsDevice)
{
projectConfig.targetDeviceTypes.Remove(deviceType);
}
}
if (hasModified)
{
OVRProjectConfig.CommitProjectConfig(projectConfig);
}
}
}
enum eProjectConfigTab
{
General = 0,
BuildSettings,
Security,
Experimental,
}
static eProjectConfigTab selectedTab = 0;
static string[] projectConfigTabStrs = null;
public static void DrawProjectConfigInspector(OVRProjectConfig projectConfig)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Quest Features", EditorStyles.boldLabel);
if (EditorUserBuildSettings.activeBuildTarget != UnityEditor.BuildTarget.Android)
{
EditorGUILayout.LabelField(
$"Your current platform is \"{EditorUserBuildSettings.activeBuildTarget}\". These settings only apply if your active platform is \"Android\".",
EditorStyles.wordWrappedMiniLabel);
}
if (projectConfigTabStrs == null)
{
projectConfigTabStrs = Enum.GetNames(typeof(eProjectConfigTab));
for (int i = 0; i < projectConfigTabStrs.Length; ++i)
projectConfigTabStrs[i] = ObjectNames.NicifyVariableName(projectConfigTabStrs[i]);
}
selectedTab =
(eProjectConfigTab)GUILayout.SelectionGrid((int)selectedTab, projectConfigTabStrs, 3, GUI.skin.button);
EditorGUILayout.Space(5);
bool hasModified = false;
switch (selectedTab)
{
case eProjectConfigTab.General:
// Show overlay support option
using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.Toggle(new GUIContent("Focus Aware (Required)",
"If checked, the new overlay will be displayed when the user presses the home button. The game will not be paused, but will now receive InputFocusLost and InputFocusAcquired events."),
true);
}
// Hand Tracking Support
OVREditorUtil.SetupEnumField(projectConfig, "Hand Tracking Support",
ref projectConfig.handTrackingSupport, ref hasModified);
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Hand Tracking Frequency",
"Note that a higher tracking frequency will reserve some performance headroom from the application's budget."),
ref projectConfig.handTrackingFrequency, ref hasModified,
"https://developer.oculus.com/documentation/unity/unity-handtracking/#enable-hand-tracking");
OVREditorUtil.SetupEnumField(projectConfig, "Hand Tracking Version",
ref projectConfig.handTrackingVersion, ref hasModified);
// Concurrent hands and controllers support
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Concurrent Hands/Controllers Support",
"Allows the application to use concurrent hands and controllers functionality. This option must be enabled at build time."),
ref projectConfig.multimodalHandsControllersSupport, ref hasModified);
// Enable Render Model Support
bool renderModelSupportAvailable = OVRPluginInfo.IsOVRPluginOpenXRActivated();
EditorGUI.BeginDisabledGroup(!renderModelSupportAvailable);
if (!renderModelSupportAvailable)
{
projectConfig.renderModelSupport = OVRProjectConfig.RenderModelSupport.Disabled;
}
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Render Model Support",
"If enabled, the application will be able to load render models from the runtime."),
ref projectConfig.renderModelSupport, ref hasModified);
if (hasModified && projectConfig.renderModelSupport == OVRProjectConfig.RenderModelSupport.Disabled)
{
if (projectConfig.trackedKeyboardSupport != OVRProjectConfig.TrackedKeyboardSupport.None)
{
Debug.LogWarning("Tracked Keyboard support disabled. Requires Render Model Support");
projectConfig.trackedKeyboardSupport = OVRProjectConfig.TrackedKeyboardSupport.None;
}
if (projectConfig.virtualKeyboardSupport != OVRProjectConfig.FeatureSupport.None)
{
Debug.LogWarning("Virtual Keyboard support disabled. Requires Render Model Support");
projectConfig.virtualKeyboardSupport = OVRProjectConfig.FeatureSupport.None;
}
}
EditorGUI.EndDisabledGroup();
// System Keyboard Support
OVREditorUtil.SetupBoolField(projectConfig, new GUIContent("Requires System Keyboard",
"If checked, the Oculus System keyboard will be enabled for Unity input fields and any calls to open/close the Unity TouchScreenKeyboard."),
ref projectConfig.requiresSystemKeyboard, ref hasModified);
// Tracked Keyboard Support
bool trackedKeyboardSupportAvailable = OVRPluginInfo.IsOVRPluginOpenXRActivated();
using (new EditorGUI.DisabledGroupScope(!trackedKeyboardSupportAvailable))
{
if (!trackedKeyboardSupportAvailable)
{
projectConfig.trackedKeyboardSupport = OVRProjectConfig.TrackedKeyboardSupport.None;
}
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Tracked Keyboard Support",
"Show user's physical keyboard in correct position in VR."),
ref projectConfig.trackedKeyboardSupport, ref hasModified);
}
// Virtual Keyboard Support
bool virtualKeyboardSupportAvailable = OVRPluginInfo.IsOVRPluginOpenXRActivated();
using (new EditorGUI.DisabledGroupScope(!virtualKeyboardSupportAvailable))
{
if (!virtualKeyboardSupportAvailable)
{
projectConfig.virtualKeyboardSupport = OVRProjectConfig.FeatureSupport.None;
}
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Virtual Keyboard Support",
"Provides a consistent typing experience across Meta Quest VR applications."),
ref projectConfig.virtualKeyboardSupport, ref hasModified);
if (projectConfig.requiresSystemKeyboard)
{
EditorGUILayout.HelpBox(
"Using the System Keyboard with Virtual Keyboard is not recommended.",
MessageType.Warning);
}
}
// Anchor Support - linked to Shared Spatial Anchors and Scene
var anchorSupportRequired = projectConfig.sharedAnchorSupport != OVRProjectConfig.FeatureSupport.None;
var anchorSupportTooltip = "Anchor Support is required for Shared Spatial Anchor Support.";
anchorSupportRequired = anchorSupportRequired ||
projectConfig.sceneSupport != OVRProjectConfig.FeatureSupport.None;
anchorSupportTooltip =
"Anchor Support is required for Shared Spatial Anchor Support and/or Scene Support.";
using (new EditorGUI.DisabledScope(anchorSupportRequired))
{
var tooltip = anchorSupportRequired ? anchorSupportTooltip : "";
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Anchor Support", tooltip),
ref projectConfig.anchorSupport, ref hasModified);
}
OVREditorUtil.SetupEnumField(projectConfig,
new GUIContent("Shared Spatial Anchor Support",
"Enables support for sharing spatial anchors with other users. This requires Anchor Support to be enabled."),
ref projectConfig.sharedAnchorSupport, ref hasModified);
if (projectConfig.sharedAnchorSupport != OVRProjectConfig.FeatureSupport.None &&
projectConfig.anchorSupport != OVRProjectConfig.AnchorSupport.Enabled)
{
projectConfig.anchorSupport = OVRProjectConfig.AnchorSupport.Enabled;
hasModified = true;
}
// Scene Support
var sceneTooltip =
"Enable support for scene understanding. This requires Anchor Support to be enabled.";
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Scene Support", sceneTooltip),
ref projectConfig.sceneSupport, ref hasModified);
// enable anchor support if scene requires it
if (projectConfig.sceneSupport != OVRProjectConfig.FeatureSupport.None &&
projectConfig.anchorSupport != OVRProjectConfig.AnchorSupport.Enabled)
{
projectConfig.anchorSupport = OVRProjectConfig.AnchorSupport.Enabled;
hasModified = true;
}
// Body Tracking Support
OVREditorUtil.SetupEnumField(projectConfig, "Body Tracking Support",
ref projectConfig.bodyTrackingSupport, ref hasModified);
// Face Tracking Support
OVREditorUtil.SetupEnumField(projectConfig, "Face Tracking Support",
ref projectConfig.faceTrackingSupport, ref hasModified);
// Eye Tracking Support
OVREditorUtil.SetupEnumField(projectConfig, "Eye Tracking Support",
ref projectConfig.eyeTrackingSupport, ref hasModified);
if (hasModified && projectConfig.trackedKeyboardSupport != OVRProjectConfig.TrackedKeyboardSupport.None)
{
projectConfig.renderModelSupport = OVRProjectConfig.RenderModelSupport.Enabled;
}
if (hasModified && projectConfig.virtualKeyboardSupport != OVRProjectConfig.FeatureSupport.None)
{
projectConfig.renderModelSupport = OVRProjectConfig.RenderModelSupport.Enabled;
}
if (!OVRPluginInfo.IsOVRPluginOpenXRActivated())
{
EditorGUILayout.HelpBox(
"The OpenXR backend must be enabled in the Oculus menu to use the Render Model and Tracked Keyboard features.",
MessageType.Info);
}
if (projectConfig.trackedKeyboardSupport != OVRProjectConfig.TrackedKeyboardSupport.None &&
projectConfig.renderModelSupport == OVRProjectConfig.RenderModelSupport.Disabled)
{
EditorGUILayout.HelpBox(
"Render model support is required to load keyboard models from the runtime.",
MessageType.Error);
}
if (projectConfig.virtualKeyboardSupport != OVRProjectConfig.FeatureSupport.None &&
projectConfig.renderModelSupport == OVRProjectConfig.RenderModelSupport.Disabled)
{
EditorGUILayout.HelpBox(
"Render model support is required to load virtual keyboard models from the runtime.",
MessageType.Error);
}
// System Splash Screen
bool splashScreenTextureModified = false;
OVREditorUtil.SetupTexture2DField(projectConfig, new GUIContent("System Splash Screen",
"If set, the Splash Screen will be presented by the Operating System as a high quality composition layer at launch time."),
ref projectConfig.systemSplashScreen, ref splashScreenTextureModified,
"https://developer.oculus.com/documentation/unity/unity-splash-screen/");
if (splashScreenTextureModified)
{
projectConfig.systemSplashScreen = OVRSystemSplashScreenEditor.ProcessTexture(projectConfig.systemSplashScreen);
hasModified = true;
}
// System Splash Screen: "Mono", "Stereo"
OVREditorUtil.SetupEnumField(
projectConfig,
new GUIContent("System Splash Screen Type", "\"Mono\": Texture will be rendered to both eyes.\n\"Stereo\": Texture will be split and rendered to each eye."),
ref projectConfig.systemSplashScreenType,
ref hasModified
);
if (projectConfig.systemSplashScreenType ==
OVRProjectConfig.SystemSplashScreenType.Stereo)
{
EditorGUILayout.HelpBox(
"For stereoscopic splash screen, the image needs to be double-wide with left-to-right texture pair.",
MessageType.Info);
}
// Allow optional 3-dof head-tracking
OVREditorUtil.SetupBoolField(projectConfig, new GUIContent("Allow Optional 3DoF Head Tracking",
"If checked, application can work in both 6DoF and 3DoF modes. It's highly recommended to keep it unchecked unless your project strongly needs the 3DoF head tracking."),
ref projectConfig.allowOptional3DofHeadTracking, ref hasModified);
// Passthrough support
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Passthrough Support",
"Allows the application to use passthrough functionality. This option must be enabled at build time, otherwise initializing passthrough and creating passthrough layers in application scenes will fail."),
ref projectConfig._insightPassthroughSupport, ref hasModified);
// Processor favor (cpu/gpu level trading)
OVREditorUtil.SetupEnumField(projectConfig, new GUIContent("Processor Favor",
"If selected, will increase the frequency of one processor at the expense of decreasing the frequency of the other on supported devices"),
ref projectConfig._processorFavor, ref hasModified);
break;
case eProjectConfigTab.BuildSettings:
OVREditorUtil.SetupBoolField(projectConfig, new GUIContent("Skip Unneeded Shaders",
"If checked, prevent building shaders that are not used by default to reduce time spent when building."),
ref projectConfig.skipUnneededShaders, ref hasModified,
"https://developer.oculus.com/documentation/unity/unity-strip-shaders/");
break;
case eProjectConfigTab.Security:
OVREditorUtil.SetupBoolField(projectConfig, "Disable Backups", ref projectConfig.disableBackups,
ref hasModified,
"https://developer.android.com/guide/topics/data/autobackup#EnablingAutoBackup");
OVREditorUtil.SetupBoolField(projectConfig, "Enable NSC Configuration",
ref projectConfig.enableNSCConfig, ref hasModified,
"https://developer.android.com/training/articles/security-config");
EditorGUI.BeginDisabledGroup(!projectConfig.enableNSCConfig);
++EditorGUI.indentLevel;
OVREditorUtil.SetupInputField(projectConfig, "Custom Security XML Path",
ref projectConfig.securityXmlPath, ref hasModified);
--EditorGUI.indentLevel;
EditorGUI.EndDisabledGroup();
break;
case eProjectConfigTab.Experimental:
// Experimental Features Enabled
OVREditorUtil.SetupBoolField(projectConfig, new GUIContent("Experimental Features Enabled",
"If checked, this application can use experimental features. Note that such features are for developer use only. This option must be disabled when submitting to the Oculus Store."),
ref projectConfig.experimentalFeaturesEnabled, ref hasModified);
break;
}
EditorGUILayout.EndVertical();
// apply any pending changes to project config
if (hasModified)
{
OVRProjectConfig.CommitProjectConfig(projectConfig);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 950d95332920b814ea41df294856f96a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,99 @@
/*
* 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.
*/
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
[InitializeOnLoad]
internal static class OVRProjectSetupMovementSDKConfigurationTasks
{
private const OVRProjectSetup.TaskGroup Group = OVRProjectSetup.TaskGroup.Features;
static OVRProjectSetupMovementSDKConfigurationTasks()
{
CheckBodyTrackingTasks();
CheckFaceTrackingTasks();
}
private static void CheckBodyTrackingTasks()
{
OVRProjectSetup.AddTask(
level: OVRProjectSetup.TaskLevel.Required,
group: Group,
isDone: buildTargetGroup => FindMisconfiguredOVRSkeletonInstances().Count == 0,
message: "When using OVRSkeleton components it's required to have OVRBody data provider next to it",
fix: buildTargetGroup =>
{
var skeletons = FindMisconfiguredOVRSkeletonInstances();
foreach (var skeleton in skeletons)
{
OVRSkeletonEditor.FixOVRBodyConfiguration(skeleton);
}
},
fixMessage: $"Create OVRBody components where they are required"
);
}
private static void CheckFaceTrackingTasks()
{
OVRProjectSetup.AddTask(
level: OVRProjectSetup.TaskLevel.Required,
group: Group,
isDone: buildTargetGroup => FindMisconfiguredOVRCustomFaceInstances().Count == 0,
message:
"When using OVRCustomFace components it's required to have OVRFaceExpressions data provider next to it",
fix: buildTargetGroup =>
{
var faces = FindMisconfiguredOVRCustomFaceInstances();
foreach (var face in faces)
{
OVRCustomFaceEditor.FixFaceExpressions(face);
}
},
fixMessage: $"Crete OVRFaceExpressions components where they are required"
);
}
private static List<OVRSkeleton> FindMisconfiguredOVRSkeletonInstances() => FindComponentsInScene<OVRSkeleton>()
.FindAll(s => !OVRSkeletonEditor.IsSkeletonProperlyConfigured(s))
.ToList();
private static List<OVRCustomFace> FindMisconfiguredOVRCustomFaceInstances() =>
FindComponentsInScene<OVRCustomFace>()
.FindAll(s => !OVRCustomFaceEditor.IsFaceExpressionsConfigured(s))
.ToList();
private static List<T> FindComponentsInScene<T>() where T : MonoBehaviour
{
List<T> results = new List<T>();
var scene = SceneManager.GetActiveScene();
var rootGameObjects = scene.GetRootGameObjects();
foreach (var root in rootGameObjects)
{
results.AddRange(root.GetComponentsInChildren<T>());
}
return results;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e810009fa55c47e4817f0f1ee23eb0c5
timeCreated: 1678653329
@@ -0,0 +1,65 @@
/*
* 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.
*/
using UnityEditor;
[CustomEditor(typeof(OVRSceneManager))]
internal class OVRSceneManagerEditor : Editor
{
private SerializedProperty _planePrefab;
private SerializedProperty _volumePrefab;
private SerializedProperty _prefabOverrides;
private SerializedProperty _verboseLogging;
private SerializedProperty _maxSceneAnchorUpdatesPerFrame;
private SerializedProperty _initialAnchorParent;
private SerializedProperty _activeRoomsOnly;
private bool _showAdvanced;
private void OnEnable()
{
_planePrefab = serializedObject.FindProperty(nameof(OVRSceneManager.PlanePrefab));
_volumePrefab = serializedObject.FindProperty(nameof(OVRSceneManager.VolumePrefab));
_prefabOverrides = serializedObject.FindProperty(nameof(OVRSceneManager.PrefabOverrides));
_verboseLogging = serializedObject.FindProperty(nameof(OVRSceneManager.VerboseLogging));
_maxSceneAnchorUpdatesPerFrame =
serializedObject.FindProperty(nameof(OVRSceneManager.MaxSceneAnchorUpdatesPerFrame));
_activeRoomsOnly = serializedObject.FindProperty(nameof(OVRSceneManager.ActiveRoomsOnly));
_initialAnchorParent = serializedObject.FindProperty(nameof(OVRSceneManager._initialAnchorParent));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_planePrefab);
EditorGUILayout.PropertyField(_volumePrefab);
EditorGUILayout.PropertyField(_prefabOverrides);
EditorGUILayout.PropertyField(_activeRoomsOnly);
_showAdvanced = EditorGUILayout.Foldout(_showAdvanced, "Advanced");
if (_showAdvanced)
{
EditorGUILayout.PropertyField(_verboseLogging);
EditorGUILayout.PropertyField(_maxSceneAnchorUpdatesPerFrame);
EditorGUILayout.PropertyField(_initialAnchorParent);
}
serializedObject.ApplyModifiedProperties();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0ac8f8c52ff042318f6a46ff461838a4
timeCreated: 1663870763
@@ -0,0 +1,65 @@
/*
* 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.
*/
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
[CustomEditor(typeof(OVRSkeleton))]
public class OVRSkeletonEditor : Editor
{
public override void OnInspectorGUI()
{
var skeleton = (OVRSkeleton)target;
if (skeleton.GetSkeletonType() == OVRSkeleton.SkeletonType.None)
{
EditorGUILayout.HelpBox("Please select a SkeletonType.", MessageType.Warning);
}
if (!IsSkeletonProperlyConfigured(skeleton))
{
if (OVREditorUIElements.RenderWarningWithButton(
"OVRBody is required.", "Add OVRBody component"))
{
FixOVRBodyConfiguration(skeleton);
}
}
DrawDefaultInspector();
}
internal static bool IsSkeletonProperlyConfigured(OVRSkeleton skeleton)
{
return !OVRSkeleton.IsBodySkeleton(skeleton.GetSkeletonType()) ||
skeleton.SearchSkeletonDataProvider() != null;
}
internal static void FixOVRBodyConfiguration(OVRSkeleton skeleton)
{
var gameObject = skeleton.gameObject;
Undo.IncrementCurrentGroup();
var body = gameObject.AddComponent<OVRBody>();
Undo.RegisterCreatedObjectUndo(body, "Add OVRBody component");
EditorUtility.SetDirty(body);
EditorSceneManager.MarkSceneDirty(gameObject.scene);
Undo.SetCurrentGroupName("Add OVRBody component");
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8695749c9d2c4b408eba12edb958ce84
timeCreated: 1678577145
@@ -0,0 +1,34 @@
/*
* 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.
*/
using UnityEditor;
[CustomEditor(typeof(OVRUnityHumanoidSkeletonRetargeter))]
public class OVRUnityHumanoidSkeletonRetargeterEditor : OVRSkeletonEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
serializedObject.ApplyModifiedProperties();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 91cc641bbb7c4eadbfa894cc59e2a3ff
timeCreated: 1678593640
@@ -0,0 +1,75 @@
{
"name": "Oculus.VR.Scripts.Editor",
"rootNamespace": "",
"references": [
"Oculus.VR",
"Oculus.VR.Editor",
"Unity.XR.Oculus",
"Unity.XR.OpenXR",
"Unity.XR.OpenXR.Editor",
"Unity.XR.Management",
"Unity.XR.Management.Editor"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.xr.management",
"expression": "",
"define": "USING_XR_MANAGEMENT"
},
{
"name": "com.unity.xr.oculus",
"expression": "",
"define": "USING_XR_SDK_OCULUS"
},
{
"name": "com.unity.xr.openxr",
"expression": "",
"define": "USING_XR_SDK_OPENXR"
},
{
"name": "com.unity.xr.oculus",
"expression": "1.7.0",
"define": "__OVERRIDED__PRIORITIZE_OCULUS_XR_SETTINGS"
},
{
"name": "com.unity.xr.oculus",
"expression": "[2.2-pre,3.0-pre)",
"define": "USING_QUEST_PRO_COMPATIBLE_OCULUS_XR_PLUGIN_VERSION"
},
{
"name": "com.unity.xr.oculus",
"expression": "3.2.1-pre",
"define": "USING_QUEST_PRO_COMPATIBLE_OCULUS_XR_PLUGIN_VERSION"
},
{
"name": "Unity",
"expression": "",
"define": "OVR_UNITY_ASSET_STORE"
},
{
"name": "com.unity.xr.oculus",
"expression": "3.2.1-pre",
"define": "OCULUS_XR_DEPTH_SUBMISSION"
},
{
"name": "com.unity.xr.oculus",
"expression": "3.3.0",
"define": "OCULUS_XR_3_3_0_OR_NEWER"
},
{
"name": "com.meta.xr.simulator",
"expression": "",
"define": "USING_META_XR_SIMULATOR"
}
],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7305c54a43f3814439df347c7519653e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e218d62590057045aa69bd80ea3dd65
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ca9218ce934bc274998d7b9899c9bb31
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
/*
* 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.
*/
using UnityEditor;
namespace Meta.XR.Samples.Editor
{
[CustomEditor(typeof(SampleMetadata))]
public class SampleMetadataInspector : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49ebc9bf791c3b048b0403dd7d9d0eb9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4b8818c8082f44088ff51bdf21b38903
timeCreated: 1669366532
@@ -0,0 +1,349 @@
/*
* 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.
*/
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using static OVRPlugin;
/// <summary>
/// Represents an anchor.
/// </summary>
/// <remarks>
/// Scenes anchors are uniquely identified with their <see cref="Uuid"/>.
/// <para>You may dispose of an anchor by calling their <see cref="Dispose"/> method.</para>
/// </remarks>
public readonly struct OVRAnchor : IEquatable<OVRAnchor>, IDisposable
{
#region Static
public static readonly OVRAnchor Null = new OVRAnchor(0, Guid.Empty);
internal static OVRPlugin.SpaceQueryInfo GetQueryInfo(SpaceComponentType type,
OVRSpace.StorageLocation location, int maxResults, double timeout) => new OVRSpaceQuery.Options
{
QueryType = OVRPlugin.SpaceQueryType.Action,
ActionType = OVRPlugin.SpaceQueryActionType.Load,
ComponentFilter = type,
Location = location,
Timeout = timeout,
MaxResults = maxResults,
}.ToQueryInfo();
internal static OVRPlugin.SpaceQueryInfo GetQueryInfo(IEnumerable<Guid> uuids,
OVRSpace.StorageLocation location, double timeout) => new OVRSpaceQuery.Options
{
QueryType = OVRPlugin.SpaceQueryType.Action,
ActionType = OVRPlugin.SpaceQueryActionType.Load,
UuidFilter = uuids,
Location = location,
Timeout = timeout,
MaxResults = OVRSpaceQuery.Options.MaxUuidCount,
}.ToQueryInfo();
internal static OVRTask<bool> FetchAnchorsAsync(SpaceComponentType type, IList<OVRAnchor> anchors,
OVRSpace.StorageLocation location = OVRSpace.StorageLocation.Local,
int maxResults = OVRSpaceQuery.Options.MaxUuidCount, double timeout = 0.0)
=> FetchAnchors(anchors, GetQueryInfo(type, location, maxResults, timeout));
/// <summary>
/// Asynchronous method that fetches anchors with a specific component.
/// </summary>
/// <typeparam name="T">The type of component the fetched anchor must have.</typeparam>
/// <param name="anchors">IList that will get cleared and populated with the requested anchors.</param>s
/// <param name="location">Storage location to query</param>
/// <param name="maxResults">The maximum number of results the query can return</param>
/// <param name="timeout">Timeout in seconds for the query. Zero indicates the query does not timeout.</param>
/// <remarks>Dispose of the returned <see cref="OVRTask{T}"/> if you don't use the results</remarks>
/// <returns>An <see cref="OVRTask{T}"/> that will eventually let you test if the fetch was successful or not.
/// If the result is true, then the <see cref="anchors"/> parameter has been populated with the requested anchors.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="anchors"/> is `null`.</exception>
public static OVRTask<bool> FetchAnchorsAsync<T>(IList<OVRAnchor> anchors,
OVRSpace.StorageLocation location = OVRSpace.StorageLocation.Local,
int maxResults = OVRSpaceQuery.Options.MaxUuidCount, double timeout = 0.0)
where T : struct, IOVRAnchorComponent<T>
{
if (anchors == null)
{
throw new ArgumentNullException(nameof(anchors));
}
return FetchAnchorsAsync(default(T).Type, anchors, location, maxResults, timeout);
}
/// <summary>
/// Asynchronous method that fetches anchors with specifics uuids.
/// </summary>
/// <param name="uuids">Enumerable of uuids that anchors fetched must verify</param>
/// <param name="anchors">IList that will get cleared and populated with the requested anchors.</param>s
/// <param name="location">Storage location to query</param>
/// <param name="timeout">Timeout in seconds for the query. Zero indicates the query does not timeout.</param>
/// <remarks>Dispose of the returned <see cref="OVRTask{T}"/> if you don't use the results</remarks>
/// <returns>An <see cref="OVRTask{T}"/> that will eventually let you test if the fetch was successful or not.
/// If the result is true, then the <see cref="anchors"/> parameter has been populated with the requested anchors.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="uuids"/> is `null`.</exception>
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="anchors"/> is `null`.</exception>
public static OVRTask<bool> FetchAnchorsAsync(IEnumerable<Guid> uuids, IList<OVRAnchor> anchors,
OVRSpace.StorageLocation location = OVRSpace.StorageLocation.Local, double timeout = 0.0)
{
if (uuids == null)
{
throw new ArgumentNullException(nameof(uuids));
}
if (anchors == null)
{
throw new ArgumentNullException(nameof(anchors));
}
return FetchAnchors(anchors, GetQueryInfo(uuids, location, timeout));
}
private static OVRTask<bool> FetchAnchors(IList<OVRAnchor> anchors, OVRPlugin.SpaceQueryInfo queryInfo)
{
if (anchors == null)
{
throw new ArgumentNullException(nameof(anchors));
}
anchors.Clear();
if (!OVRPlugin.QuerySpaces(queryInfo, out var requestId))
{
return OVRTask.FromResult(false);
}
var task = OVRTask.FromRequest<bool>(requestId);
task.SetInternalData(anchors);
return task;
}
internal static void OnSpaceQueryCompleteData(OVRDeserialize.SpaceQueryCompleteData data)
{
var requestId = data.RequestId;
var task = OVRTask.GetExisting<bool>(requestId);
if (!task.IsPending)
{
return;
}
if (!task.TryGetInternalData<IList<OVRAnchor>>(out var anchors) || anchors == null)
{
task.SetResult(false);
return;
}
if (!OVRPlugin.RetrieveSpaceQueryResults(requestId, out var rawResults, Allocator.Temp))
{
task.SetResult(false);
return;
}
foreach (var result in rawResults)
{
var anchor = new OVRAnchor(result.space, result.uuid);
anchors.Add(anchor);
}
rawResults.Dispose();
task.SetResult(true);
}
/// <summary>
/// Creates a new spatial anchor.
/// </summary>
/// <remarks>
/// Spatial anchor creation is asynchronous. This method initiates a request to create a spatial anchor at
/// <paramref name="trackingSpacePose"/>. The returned <see cref="OVRTask{TResult}"/> can be awaited or used to
/// track the completion of the request.
///
/// If spatial anchor creation fails, the resulting <see cref="OVRAnchor"/> will be <see cref="OVRAnchor.Null"/>.
/// </remarks>
/// <param name="trackingSpacePose">The pose, in tracking space, at which you wish to create the spatial anchor.</param>
/// <returns>A task which can be used to track completion of the request.</returns>
public static OVRTask<OVRAnchor> CreateSpatialAnchorAsync(Pose trackingSpacePose)
=> CreateSpatialAnchor(new SpatialAnchorCreateInfo
{
BaseTracking = GetTrackingOriginType(),
PoseInSpace = new Posef
{
Orientation = trackingSpacePose.rotation.ToFlippedZQuatf(),
Position = trackingSpacePose.position.ToFlippedZVector3f(),
},
Time = GetTimeInSeconds(),
}, out var requestId)
? OVRTask.FromRequest<OVRAnchor>(requestId)
: OVRTask.FromResult(Null);
/// <summary>
/// Creates a new spatial anchor.
/// </summary>
/// <remarks>
/// Spatial anchor creation is asynchronous. This method initiates a request to create a spatial anchor at
/// <paramref name="transform"/>. The returned <see cref="OVRTask{TResult}"/> can be awaited or used to
/// track the completion of the request.
///
/// If spatial anchor creation fails, the resulting <see cref="OVRAnchor"/> will be <see cref="OVRAnchor.Null"/>.
/// </remarks>
/// <param name="transform">The transform at which you wish to create the spatial anchor.</param>
/// <param name="centerEyeCamera">The `Camera` associated with the Meta Quest's center eye.</param>
/// <returns>A task which can be used to track completion of the request.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="transform"/> is `null`.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="centerEyeCamera"/> is `null`.</exception>
public static OVRTask<OVRAnchor> CreateSpatialAnchorAsync(Transform transform, Camera centerEyeCamera)
{
if (transform == null)
throw new ArgumentNullException(nameof(transform));
if (centerEyeCamera == null)
throw new ArgumentNullException(nameof(centerEyeCamera));
var pose = transform.ToTrackingSpacePose(centerEyeCamera);
return CreateSpatialAnchorAsync(new Pose
{
position = pose.position,
rotation = pose.orientation,
});
}
#endregion
internal ulong Handle { get; }
/// <summary>
/// Unique Identifier representing the anchor.
/// </summary>
public Guid Uuid { get; }
internal OVRAnchor(ulong handle, Guid uuid)
{
Handle = handle;
Uuid = uuid;
}
/// <summary>
/// Gets the anchor's component of a specific type.
/// </summary>
/// <typeparam name="T">The type of the component.</typeparam>
/// <returns>The requested component.</returns>
/// <remarks>Make sure the anchor supports the specified type of component using <see cref="SupportsComponent{T}"/></remarks>
/// <exception cref="InvalidOperationException">Thrown if the anchor doesn't support the specified type of component.</exception>
/// <seealso cref="TryGetComponent{T}"/>
/// <seealso cref="SupportsComponent{T}"/>
public T GetComponent<T>() where T : struct, IOVRAnchorComponent<T>
{
if (!TryGetComponent<T>(out var component))
{
throw new InvalidOperationException($"Anchor {Uuid} does not have component {typeof(T).Name}");
}
return component;
}
/// <summary>
/// Tries to get the anchor's component of a specific type.
/// </summary>
/// <param name="component">The requested component, as an <c>out</c> parameter.</param>
/// <typeparam name="T">The type of the component.</typeparam>
/// <returns>Whether or not the request succeeded. It may fail if the anchor doesn't support this type of component.</returns>
/// <seealso cref="GetComponent{T}"/>
public bool TryGetComponent<T>(out T component) where T : struct, IOVRAnchorComponent<T>
{
component = default(T);
var result = OVRPlugin.GetSpaceComponentStatusInternal(Handle, component.Type, out _, out _);
if (!result.IsSuccess())
{
return false;
}
component = component.FromAnchor(this);
return true;
}
/// <summary>
/// Tests whether or not the anchor supports a specific type of component.
/// </summary>
/// <remarks>
/// For performance reasons, we use xrGetSpaceComponentStatusFB, which can
/// result in an error in the logs when the component is not available.
///
/// This error does not have impact on the control flow. The alternative method,
/// <seealso cref="GetSupportedComponents(List{SpaceComponentType})"/> avoids
/// this error reporting, but does have performance constraints.
/// </remarks>
/// <typeparam name="T">The type of the component.</typeparam>
/// <returns>Whether or not the specified type of component is supported.</returns>
public bool SupportsComponent<T>() where T : struct, IOVRAnchorComponent<T>
{
var component = default(T);
var result = OVRPlugin.GetSpaceComponentStatusInternal(Handle, component.Type, out _, out _);
return result.IsSuccess();
}
/// <summary>
/// Get all the supported components of an anchor.
/// </summary>
/// <remarks>
/// For performance reasons, this method reuses data structures to
/// avoid allocations, and is therefore not considered thread safe.
///
/// Do not use in background threads, including async functions
/// started with <seealso cref="System.Threading.Tasks.Task.Run(Action)"/>
/// </remarks>
/// <param name="components">The list that will be cleared, then populated with
/// the supported components.</param>
/// <returns>Whether or not the request succeeded.</returns>
public bool GetSupportedComponents(List<SpaceComponentType> components)
{
components.Clear();
if (OVRPlugin.EnumerateSpaceSupportedComponents(Handle,
out var componentCount, SupportedComponentsArray))
{
for (var i = 0; i < componentCount; i++)
components.Add(SupportedComponentsArray[i]);
return true;
}
return false;
}
private static readonly SpaceComponentType[] SupportedComponentsArray = new SpaceComponentType[64];
public bool Equals(OVRAnchor other) => Handle.Equals(other.Handle) && Uuid.Equals(other.Uuid);
public override bool Equals(object obj) => obj is OVRAnchor other && Equals(other);
public static bool operator ==(OVRAnchor lhs, OVRAnchor rhs) => lhs.Equals(rhs);
public static bool operator !=(OVRAnchor lhs, OVRAnchor rhs) => !lhs.Equals(rhs);
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + Uuid.GetHashCode());
public override string ToString() => Uuid.ToString();
/// <summary>
/// Disposes of an anchor.
/// </summary>
/// <remarks>
/// Calling this method will destroy the anchor so that it won't be managed by internal systems until
/// the next time it is fetched again.
/// </remarks>
public void Dispose() => OVRPlugin.DestroySpace(Handle);
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5f2967bab93d4b57ab8d2b9799ca6de9
timeCreated: 1669366542
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 49d8ce21824e4f38a4ce11967992e63c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
/*
* 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.
*/
/// <summary>
/// Interface shared by all components from an <see cref="OVRAnchor"/>.
/// </summary>
/// <typeparam name="T">The actual implementation Type of the interface.</typeparam>
/// <seealso cref="OVRAnchor.FetchAnchorsAsync{T}"/>
/// <seealso cref="OVRAnchor.TryGetComponent{T}"/>
/// <seealso cref="OVRAnchor.SupportsComponent{T}"/>
public interface IOVRAnchorComponent<T>
{
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull { get; }
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled { get; }
/// <summary>
/// Sets the enabled status of this component.
/// </summary>
/// <remarks>
/// A component must be enabled to access its data.
/// </remarks>
/// <param name="enabled">The desired state of the component.</param>
/// <param name="timeout">The timeout, in seconds, for the operation. Use zero to indicate an infinite timeout.</param>
/// <returns>Returns an <see cref="OVRTask{T}" /> whose result indicates the result of the operation.</returns>
public OVRTask<bool> SetEnabledAsync(bool enable, double timeout = 0);
internal OVRPlugin.SpaceComponentType Type { get; }
internal ulong Handle { get; }
internal T FromAnchor(OVRAnchor anchor);
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4858e79b20d841adb5e0d5236a35dd1f
timeCreated: 1669391737
@@ -0,0 +1,755 @@
/*
* 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.
*/
using System;
using static OVRPlugin;
public readonly partial struct OVRLocatable : IOVRAnchorComponent<OVRLocatable>, IEquatable<OVRLocatable>
{
SpaceComponentType IOVRAnchorComponent<OVRLocatable>.Type => Type;
ulong IOVRAnchorComponent<OVRLocatable>.Handle => Handle;
OVRLocatable IOVRAnchorComponent<OVRLocatable>.FromAnchor(OVRAnchor anchor) => new OVRLocatable(anchor);
/// <summary>
/// A null representation of an OVRLocatable.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRLocatable Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
/// <summary>
/// Sets the enabled status of this component.
/// </summary>
/// <remarks>
/// A component must be enabled to access its data.
/// </remarks>
/// <param name="enabled">The desired state of the component.</param>
/// <param name="timeout">The timeout, in seconds, for the operation. Use zero to indicate an infinite timeout.</param>
/// <returns>Returns an <see cref="OVRTask{T}" /> whose result indicates the result of the operation.</returns>
public OVRTask<bool> SetEnabledAsync(bool enabled, double timeout = 0) => SetSpaceComponentStatus(Handle, Type, enabled, timeout, out var requestId)
? OVRTask.FromRequest<bool>(requestId)
: OVRTask.FromResult(false);
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRLocatable other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRLocatable lhs, OVRLocatable rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRLocatable lhs, OVRLocatable rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRLocatable and <see cref="Equals(OVRLocatable)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRLocatable other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.Locatable";
internal SpaceComponentType Type => SpaceComponentType.Locatable;
internal ulong Handle { get; }
private OVRLocatable(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRStorable : IOVRAnchorComponent<OVRStorable>, IEquatable<OVRStorable>
{
SpaceComponentType IOVRAnchorComponent<OVRStorable>.Type => Type;
ulong IOVRAnchorComponent<OVRStorable>.Handle => Handle;
OVRStorable IOVRAnchorComponent<OVRStorable>.FromAnchor(OVRAnchor anchor) => new OVRStorable(anchor);
/// <summary>
/// A null representation of an OVRStorable.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRStorable Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
/// <summary>
/// Sets the enabled status of this component.
/// </summary>
/// <remarks>
/// A component must be enabled to access its data.
/// </remarks>
/// <param name="enabled">The desired state of the component.</param>
/// <param name="timeout">The timeout, in seconds, for the operation. Use zero to indicate an infinite timeout.</param>
/// <returns>Returns an <see cref="OVRTask{T}" /> whose result indicates the result of the operation.</returns>
public OVRTask<bool> SetEnabledAsync(bool enabled, double timeout = 0) => SetSpaceComponentStatus(Handle, Type, enabled, timeout, out var requestId)
? OVRTask.FromRequest<bool>(requestId)
: OVRTask.FromResult(false);
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRStorable other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRStorable lhs, OVRStorable rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRStorable lhs, OVRStorable rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRStorable and <see cref="Equals(OVRStorable)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRStorable other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.Storable";
internal SpaceComponentType Type => SpaceComponentType.Storable;
internal ulong Handle { get; }
private OVRStorable(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRSharable : IOVRAnchorComponent<OVRSharable>, IEquatable<OVRSharable>
{
SpaceComponentType IOVRAnchorComponent<OVRSharable>.Type => Type;
ulong IOVRAnchorComponent<OVRSharable>.Handle => Handle;
OVRSharable IOVRAnchorComponent<OVRSharable>.FromAnchor(OVRAnchor anchor) => new OVRSharable(anchor);
/// <summary>
/// A null representation of an OVRSharable.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRSharable Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
/// <summary>
/// Sets the enabled status of this component.
/// </summary>
/// <remarks>
/// A component must be enabled to access its data.
/// </remarks>
/// <param name="enabled">The desired state of the component.</param>
/// <param name="timeout">The timeout, in seconds, for the operation. Use zero to indicate an infinite timeout.</param>
/// <returns>Returns an <see cref="OVRTask{T}" /> whose result indicates the result of the operation.</returns>
public OVRTask<bool> SetEnabledAsync(bool enabled, double timeout = 0) => SetSpaceComponentStatus(Handle, Type, enabled, timeout, out var requestId)
? OVRTask.FromRequest<bool>(requestId)
: OVRTask.FromResult(false);
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRSharable other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRSharable lhs, OVRSharable rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRSharable lhs, OVRSharable rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRSharable and <see cref="Equals(OVRSharable)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRSharable other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.Sharable";
internal SpaceComponentType Type => SpaceComponentType.Sharable;
internal ulong Handle { get; }
private OVRSharable(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRBounded2D : IOVRAnchorComponent<OVRBounded2D>, IEquatable<OVRBounded2D>
{
SpaceComponentType IOVRAnchorComponent<OVRBounded2D>.Type => Type;
ulong IOVRAnchorComponent<OVRBounded2D>.Handle => Handle;
OVRBounded2D IOVRAnchorComponent<OVRBounded2D>.FromAnchor(OVRAnchor anchor) => new OVRBounded2D(anchor);
/// <summary>
/// A null representation of an OVRBounded2D.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRBounded2D Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
OVRTask<bool> IOVRAnchorComponent<OVRBounded2D>.SetEnabledAsync(bool enabled, double timeout)
=> throw new NotSupportedException("The Bounded2D component cannot be enabled or disabled.");
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRBounded2D other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRBounded2D lhs, OVRBounded2D rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRBounded2D lhs, OVRBounded2D rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRBounded2D and <see cref="Equals(OVRBounded2D)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRBounded2D other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.Bounded2D";
internal SpaceComponentType Type => SpaceComponentType.Bounded2D;
internal ulong Handle { get; }
private OVRBounded2D(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRBounded3D : IOVRAnchorComponent<OVRBounded3D>, IEquatable<OVRBounded3D>
{
SpaceComponentType IOVRAnchorComponent<OVRBounded3D>.Type => Type;
ulong IOVRAnchorComponent<OVRBounded3D>.Handle => Handle;
OVRBounded3D IOVRAnchorComponent<OVRBounded3D>.FromAnchor(OVRAnchor anchor) => new OVRBounded3D(anchor);
/// <summary>
/// A null representation of an OVRBounded3D.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRBounded3D Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
OVRTask<bool> IOVRAnchorComponent<OVRBounded3D>.SetEnabledAsync(bool enabled, double timeout)
=> throw new NotSupportedException("The Bounded3D component cannot be enabled or disabled.");
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRBounded3D other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRBounded3D lhs, OVRBounded3D rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRBounded3D lhs, OVRBounded3D rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRBounded3D and <see cref="Equals(OVRBounded3D)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRBounded3D other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.Bounded3D";
internal SpaceComponentType Type => SpaceComponentType.Bounded3D;
internal ulong Handle { get; }
private OVRBounded3D(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRSemanticLabels : IOVRAnchorComponent<OVRSemanticLabels>, IEquatable<OVRSemanticLabels>
{
SpaceComponentType IOVRAnchorComponent<OVRSemanticLabels>.Type => Type;
ulong IOVRAnchorComponent<OVRSemanticLabels>.Handle => Handle;
OVRSemanticLabels IOVRAnchorComponent<OVRSemanticLabels>.FromAnchor(OVRAnchor anchor) => new OVRSemanticLabels(anchor);
/// <summary>
/// A null representation of an OVRSemanticLabels.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRSemanticLabels Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
OVRTask<bool> IOVRAnchorComponent<OVRSemanticLabels>.SetEnabledAsync(bool enabled, double timeout)
=> throw new NotSupportedException("The SemanticLabels component cannot be enabled or disabled.");
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRSemanticLabels other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRSemanticLabels lhs, OVRSemanticLabels rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRSemanticLabels lhs, OVRSemanticLabels rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRSemanticLabels and <see cref="Equals(OVRSemanticLabels)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRSemanticLabels other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.SemanticLabels";
internal SpaceComponentType Type => SpaceComponentType.SemanticLabels;
internal ulong Handle { get; }
private OVRSemanticLabels(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRRoomLayout : IOVRAnchorComponent<OVRRoomLayout>, IEquatable<OVRRoomLayout>
{
SpaceComponentType IOVRAnchorComponent<OVRRoomLayout>.Type => Type;
ulong IOVRAnchorComponent<OVRRoomLayout>.Handle => Handle;
OVRRoomLayout IOVRAnchorComponent<OVRRoomLayout>.FromAnchor(OVRAnchor anchor) => new OVRRoomLayout(anchor);
/// <summary>
/// A null representation of an OVRRoomLayout.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRRoomLayout Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
OVRTask<bool> IOVRAnchorComponent<OVRRoomLayout>.SetEnabledAsync(bool enabled, double timeout)
=> throw new NotSupportedException("The RoomLayout component cannot be enabled or disabled.");
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRRoomLayout other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRRoomLayout lhs, OVRRoomLayout rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRRoomLayout lhs, OVRRoomLayout rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRRoomLayout and <see cref="Equals(OVRRoomLayout)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRRoomLayout other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.RoomLayout";
internal SpaceComponentType Type => SpaceComponentType.RoomLayout;
internal ulong Handle { get; }
private OVRRoomLayout(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRAnchorContainer : IOVRAnchorComponent<OVRAnchorContainer>, IEquatable<OVRAnchorContainer>
{
SpaceComponentType IOVRAnchorComponent<OVRAnchorContainer>.Type => Type;
ulong IOVRAnchorComponent<OVRAnchorContainer>.Handle => Handle;
OVRAnchorContainer IOVRAnchorComponent<OVRAnchorContainer>.FromAnchor(OVRAnchor anchor) => new OVRAnchorContainer(anchor);
/// <summary>
/// A null representation of an OVRAnchorContainer.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRAnchorContainer Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
OVRTask<bool> IOVRAnchorComponent<OVRAnchorContainer>.SetEnabledAsync(bool enabled, double timeout)
=> throw new NotSupportedException("The AnchorContainer component cannot be enabled or disabled.");
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRAnchorContainer other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRAnchorContainer lhs, OVRAnchorContainer rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRAnchorContainer lhs, OVRAnchorContainer rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRAnchorContainer and <see cref="Equals(OVRAnchorContainer)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRAnchorContainer other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.AnchorContainer";
internal SpaceComponentType Type => SpaceComponentType.SpaceContainer;
internal ulong Handle { get; }
private OVRAnchorContainer(OVRAnchor anchor) => Handle = anchor.Handle;
}
public readonly partial struct OVRTriangleMesh : IOVRAnchorComponent<OVRTriangleMesh>, IEquatable<OVRTriangleMesh>
{
SpaceComponentType IOVRAnchorComponent<OVRTriangleMesh>.Type => Type;
ulong IOVRAnchorComponent<OVRTriangleMesh>.Handle => Handle;
OVRTriangleMesh IOVRAnchorComponent<OVRTriangleMesh>.FromAnchor(OVRAnchor anchor) => new OVRTriangleMesh(anchor);
/// <summary>
/// A null representation of an OVRTriangleMesh.
/// </summary>
/// <remarks>
/// Use this to compare with another component to determine whether it is null.
/// </remarks>
public static readonly OVRTriangleMesh Null = default;
/// <summary>
/// Whether this object represents a valid anchor component.
/// </summary>
public bool IsNull => Handle == 0;
/// <summary>
/// True if this component is enabled and no change to its enabled status is pending.
/// </summary>
public bool IsEnabled => !IsNull && GetSpaceComponentStatus(Handle, Type, out var enabled, out var pending) && enabled && !pending;
OVRTask<bool> IOVRAnchorComponent<OVRTriangleMesh>.SetEnabledAsync(bool enabled, double timeout)
=> throw new NotSupportedException("The TriangleMesh component cannot be enabled or disabled.");
/// <summary>
/// Compares this component for equality with <paramref name="other" />.
/// </summary>
/// <param name="other">The other component to compare with.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public bool Equals(OVRTriangleMesh other) => Handle == other.Handle;
/// <summary>
/// Compares two components for equality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if both components belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator ==(OVRTriangleMesh lhs, OVRTriangleMesh rhs) => lhs.Equals(rhs);
/// <summary>
/// Compares two components for inequality.
/// </summary>
/// <param name="lhs">The component to compare with <paramref name="rhs" />.</param>
/// <param name="rhs">The component to compare with <paramref name="lhs" />.</param>
/// <returns>True if the components do not belong to the same <see cref="OVRAnchor" />, otherwise false.</returns>
public static bool operator !=(OVRTriangleMesh lhs, OVRTriangleMesh rhs) => !lhs.Equals(rhs);
/// <summary>
/// Compares this component for equality with <paramref name="obj" />.
/// </summary>
/// <param name="obj">The `object` to compare with.</param>
/// <returns>True if <paramref name="obj" /> is an OVRTriangleMesh and <see cref="Equals(OVRTriangleMesh)" /> is true, otherwise false.</returns>
public override bool Equals(object obj) => obj is OVRTriangleMesh other && Equals(other);
/// <summary>
/// Gets a hashcode suitable for use in a Dictionary or HashSet.
/// </summary>
/// <returns>A hashcode for this component.</returns>
public override int GetHashCode() => unchecked(Handle.GetHashCode() * 486187739 + ((int)Type).GetHashCode());
/// <summary>
/// Gets a string representation of this component.
/// </summary>
/// <returns>A string representation of this component.</returns>
public override string ToString() => $"{Handle}.TriangleMesh";
internal SpaceComponentType Type => SpaceComponentType.TriangleMesh;
internal ulong Handle { get; }
private OVRTriangleMesh(OVRAnchor anchor) => Handle = anchor.Handle;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3d7c9331dd825041bbd89858f165463
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,55 @@
/*
* 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.
*/
using System;
using System.Collections.Generic;
/// <summary>
/// Represents a container for other <see cref="OVRAnchor"/>s.
/// </summary>
/// <remarks>
/// This component can be accessed from an <see cref="OVRAnchor"/> that supports it by calling
/// <see cref="OVRAnchor.GetComponent{T}"/> from the anchor.s
/// </remarks>
/// <seealso cref="Uuids"/>
/// <seealso cref="FetchChildrenAsync"/>
public readonly partial struct OVRAnchorContainer : IOVRAnchorComponent<OVRAnchorContainer>,
IEquatable<OVRAnchorContainer>
{
/// <summary>
/// Uuids of the anchors contained by this Anchor Container.
/// </summary>
/// <seealso cref="OVRAnchor.FetchAnchorsAsync"/>
/// <exception cref="InvalidOperationException">If it fails to retrieve the Uuids, which could happen if the component is not supported or enabled.</exception>
public Guid[] Uuids => OVRPlugin.GetSpaceContainer(Handle, out var containerUuids)
? containerUuids
: throw new InvalidOperationException("Could not get Uuids");
/// <summary>
/// Asynchronous method that fetches anchors contained by this Anchor Container.
/// </summary>
/// <param name="anchors">IList that will get cleared and populated with the requested anchors.</param>
/// <remarks>Dispose of the returned <see cref="OVRTask{bool}"/> if you don't use the results</remarks>
/// <returns>An <see cref="OVRTask{bool}"/> that will eventually let you test if the fetch was successful or not.
/// If the result is true, then the <see cref="anchors"/> parameter has been populated with the requested anchors.</returns>
/// <exception cref="InvalidOperationException">If it fails to retrieve the Uuids</exception>
/// <exception cref="ArgumentNullException">If parameter anchors is null</exception>
public OVRTask<bool> FetchChildrenAsync(List<OVRAnchor> anchors) => OVRAnchor.FetchAnchorsAsync(Uuids, anchors);
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 808c13beb83f4e819eb369c205ccd05c
timeCreated: 1669392046
@@ -0,0 +1,104 @@
/*
* 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.
*/
using System;
using Unity.Collections;
using UnityEngine;
/// <summary>
/// Represents a plane described by its <see cref="Rect"/> and boundary points.
/// </summary>
/// <remarks>
/// This component can be accessed from an <see cref="OVRAnchor"/> that supports it by calling
/// <see cref="OVRAnchor.GetComponent{T}"/> from the anchor.
/// </remarks>
/// <seealso cref="BoundingBox"/>
/// <seealso cref="TryGetBoundaryPointsCount"/>
/// <seealso cref="TryGetBoundaryPoints"/>
public readonly partial struct OVRBounded2D : IOVRAnchorComponent<OVRBounded2D>, IEquatable<OVRBounded2D>
{
/// <summary>
/// Bounding Box
/// </summary>
/// <returns>
/// <see cref="Rect"/> representing the 2D Bounding Box of the Anchor this component is attached to.
/// </returns>
/// <exception cref="InvalidOperationException">If it fails to retrieve the Bounding Box.</exception>
public Rect BoundingBox => OVRPlugin.GetSpaceBoundingBox2D(Handle, out var rectf)
? ConvertRect(rectf)
: throw new InvalidOperationException("Could not get BoundingBox");
private Rect ConvertRect(OVRPlugin.Rectf openXrRect)
{
// OpenXR Rects describe a rectangle by its position (or offset) and its size (or extents)
// https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrRect2Df.html
// Unity Rects describe a rectangle by its position and its size (or extents)
// https://docs.unity3d.com/ScriptReference/Rect.html
var extents = openXrRect.Size.FromSizef();
// OpenXR uses a right-handed coordinate system
// Unity uses left-handed coordinate system
// There is a design assumption that plane's normal should coincide with +z
// We therefore need to rotate the plane 180° around +y axis
// We therefore need to flip the x axis.
var offset = openXrRect.Pos.FromFlippedXVector2f();
// When flipping one axis, position doesn't point to a min corner any more
offset.x -= extents.x;
return new Rect(offset, extents);
}
/// <summary>
/// Retrieves the number of boundary points contained in an Anchor with an enabled Bounded2D component.
/// </summary>
/// <param name="count">The number of boundary points contained in the Bounded2D component of the Anchor, as an <c>out</c> parameter.</param>
/// <returns><c>true</c> if it successfully retrieves the count, <c>false</c> otherwise.</returns>
/// <remarks>This is the first part of the two-calls idiom for retrieving boundary points. <see cref="TryGetBoundaryPoints"/> to actually get those points.</remarks>
/// <seealso cref="TryGetBoundaryPoints"/>
public bool TryGetBoundaryPointsCount(out int count) =>
OVRPlugin.GetSpaceBoundary2DCount(Handle, out count);
/// <summary>
/// Retrieves the boundary points contained in an Anchor with an enabled Bounded2D component.
/// </summary>
/// <param name="positions">The array that will get populated with the boundary points contained in the Bounded2D component of the Anchor.</param>
/// <returns><c>true</c> if it successfully populates the <paramref name="positions"/> array with the boundary points.</returns>
/// <exception cref="ArgumentException">Thrown when <paramref name="positions"/> has not been created.</exception>
/// <remarks>This is the second part of the two-calls idiom for retrieving boundary points.
/// It is expected for the <paramref name="positions"/> to be created and with enough capacity to contain the boundary points.</remarks>
/// <seealso cref="TryGetBoundaryPointsCount"/>
public bool TryGetBoundaryPoints(NativeArray<Vector2> positions)
{
if (!positions.IsCreated) throw new ArgumentException("NativeArray is not created", nameof(positions));
if (!OVRPlugin.GetSpaceBoundary2D(Handle, positions, out var count)) return false;
var low = 0;
var high = count - 1;
for (; low <= high; low++, high--)
{
var swapTemporaryPositionHigh = positions[high];
var swapTemporaryPositionLow = positions[low];
positions[low] = new Vector2(-swapTemporaryPositionHigh.x, swapTemporaryPositionHigh.y);
positions[high] = new Vector2(-swapTemporaryPositionLow.x, swapTemporaryPositionLow.y);
}
return true;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 955209a5d89b4116b1fe76931b971c18
timeCreated: 1669713526
@@ -0,0 +1,68 @@
/*
* 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.
*/
using System;
using UnityEngine;
/// <summary>
/// Represents a volume described by its <see cref="Bounds"/>.
/// </summary>
/// <remarks>
/// This component can be accessed from an <see cref="OVRAnchor"/> that supports it by calling
/// <see cref="OVRAnchor.GetComponent{T}"/> from the anchor.
/// </remarks>
/// <seealso cref="BoundingBox"/>
public readonly partial struct OVRBounded3D : IOVRAnchorComponent<OVRBounded3D>, IEquatable<OVRBounded3D>
{
/// <summary>
/// Bounding Box
/// </summary>
/// <returns>
/// <see cref="Bounds"/> representing the 3D Bounding Box of the Anchor this component is attached to.
/// </returns>
/// <exception cref="InvalidOperationException">If it fails to retrieve the Bounding Box.</exception>
public Bounds BoundingBox => OVRPlugin.GetSpaceBoundingBox3D(Handle, out var boundsf)
? ConvertBounds(boundsf)
: throw new InvalidOperationException("Could not get BoundingBox");
private Bounds ConvertBounds(OVRPlugin.Boundsf openXrBounds)
{
// OpenXR Bounds describe a volume by its position (or offset) and its size (or extents)
// https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrRect3DfFB.html
// Unity Bounds describe a volume by its center and its size (or extents)
// https://docs.unity3d.com/ScriptReference/Bounds.html
var extents = openXrBounds.Size.FromSize3f();
// OpenXR uses a right-handed coordinate system
// Unity uses left-handed coordinate system
// We therefore need to flip the z axis to convert from OpenXR to Unity
// And then, because of the z-axis positive normal, rotate 180 around +y
// This ends up being equivalent to flipping x axis
var offset = openXrBounds.Pos.FromFlippedXVector3f();
// When flipping one axis, position doesn't point to a min corner any more
offset.x -= extents.x;
// And add half of the extents to find the center
var halfExtents = extents * 0.5f;
var center = offset + halfExtents;
// Bounds constructor takes the center and the full size (not half extents)
return new Bounds(center, extents);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e084c5263424e6aaa12b7c6536407ad
timeCreated: 1669714557
@@ -0,0 +1,212 @@
/*
* 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.
*/
using System;
using UnityEngine;
/// <summary>
/// Represents the Pose of the anchor. Enabling it will localize the anchor.
/// </summary>
/// <remarks>
/// This component can be accessed from an <see cref="OVRAnchor"/> that supports it by calling
/// <see cref="OVRAnchor.GetComponent{T}"/> from the anchor.
/// This component needs to be enabled before requesting its Pose. See <see cref="IsEnabled"/> and
/// <see cref="SetEnabledAsync"/>
/// </remarks>
/// <seealso cref="TrackingSpacePose"/>
/// <seealso cref="TryGetSceneAnchorPose"/>
/// <seealso cref="TryGetSpatialAnchorPose"/>
public readonly partial struct OVRLocatable : IOVRAnchorComponent<OVRLocatable>, IEquatable<OVRLocatable>
{
/// <summary>
/// Tracking space position and rotation of the anchor
/// </summary>
/// <remarks>
/// Position and rotation are both nullable <see cref="Vector3"/> and <see cref="Quaternion"/>
/// and might be null independently if one of them or both are invalid.
/// </remarks>
/// <seealso cref="Position"/>
/// <seealso cref="IsPositionTracked"/>
/// <seealso cref="ComputeWorldPosition"/>
/// <seealso cref="Rotation"/>
/// <seealso cref="IsRotationTracked"/>
/// <seealso cref="ComputeWorldRotation"/>
public readonly struct TrackingSpacePose
{
/// <summary>
/// Position in tracking space of the anchor
/// </summary>
/// <remarks>
/// Null if and when the position is invalid
/// </remarks>
/// <seealso cref="Rotation"/>
/// <seealso cref="ComputeWorldPosition"/>
/// <seealso cref="ComputeWorldRotation"/>
public Vector3? Position { get; }
/// <summary>
/// Rotation in tracking space of the Anchor
/// </summary>
/// <remarks>
/// Null if and when the rotation is invalid
/// </remarks>
/// <seealso cref="Position"/>
/// <seealso cref="ComputeWorldPosition"/>
/// <seealso cref="ComputeWorldRotation"/>
public Quaternion? Rotation { get; }
/// <summary>
/// Indicates whether or not the position is currently tracked
/// </summary>
public bool IsPositionTracked => _flags.IsPositionTracked();
/// <summary>
/// Indicates whether or not the rotation is currently tracked
/// </summary>
public bool IsRotationTracked => _flags.IsOrientationTracked();
private readonly OVRPlugin.SpaceLocationFlags _flags;
internal TrackingSpacePose(Vector3 position, Quaternion rotation, OVRPlugin.SpaceLocationFlags flags)
{
_flags = flags;
Position = _flags.IsPositionValid() ? position : default(Vector3?);
Rotation = _flags.IsOrientationValid() ? rotation : default(Quaternion?);
}
/// <summary>
/// Computes the world space position of the anchor
/// </summary>
/// <param name="camera">A <see cref="Camera"/> component that will be use to compute the transform to world space</param>
/// <returns>
/// The nullable <see cref="Vector3"/> position in world space which may be
/// null if and when <see cref="Position"/> is invalid or head pose is invalid.
/// </returns>
/// <seealso cref="Position"/>
/// <seealso cref="Rotation"/>
/// <seealso cref="ComputeWorldRotation"/>
/// <exception cref="ArgumentNullException">If <paramref name="camera"/> is null</exception>
public Vector3? ComputeWorldPosition(Camera camera)
{
if (camera == null) throw new ArgumentNullException(nameof(camera));
if (!Position.HasValue) return null;
var headPose = OVRPose.identity;
if (!OVRNodeStateProperties.GetNodeStatePropertyVector3(UnityEngine.XR.XRNode.Head,
NodeStatePropertyType.Position, OVRPlugin.Node.Head, OVRPlugin.Step.Render, out headPose.position))
return null;
if (!OVRNodeStateProperties.GetNodeStatePropertyQuaternion(UnityEngine.XR.XRNode.Head,
NodeStatePropertyType.Orientation, OVRPlugin.Node.Head, OVRPlugin.Step.Render,
out headPose.orientation))
return null;
headPose = headPose.Inverse();
var headTrackingPosition = headPose.position + headPose.orientation * Position.Value;
return camera.transform.localToWorldMatrix.MultiplyPoint(headTrackingPosition);
}
/// <summary>
/// Computes the world space rotation of the anchor
/// </summary>
/// <param name="camera">A <see cref="Camera"/> component that will be use to compute the transform to world space</param>
/// <returns>
/// The nullable <see cref="Quaternion"/> rotation in world space which may be
/// null if and when <see cref="Rotation"/> is invalid or if head rotation is invalid.
/// </returns>
/// <seealso cref="Position"/>
/// <seealso cref="Rotation"/>
/// <seealso cref="ComputeWorldPosition"/>
/// <exception cref="ArgumentNullException">If <paramref name="camera"/> is null</exception>
public Quaternion? ComputeWorldRotation(Camera camera)
{
if (camera == null) throw new ArgumentNullException(nameof(camera));
if (!Rotation.HasValue) return null;
if (!OVRNodeStateProperties.GetNodeStatePropertyQuaternion(UnityEngine.XR.XRNode.Head,
NodeStatePropertyType.Orientation, OVRPlugin.Node.Head, OVRPlugin.Step.Render,
out var headPoseRotation))
return null;
headPoseRotation = Quaternion.Inverse(headPoseRotation);
var headTrackingOrientation = headPoseRotation * Rotation.Value;
return camera.transform.rotation * headTrackingOrientation;
}
}
/// <summary>
/// Tries to get the <see cref="TrackingSpacePose"/> representing the position and rotation of this anchor, treated as a scene anchor, in tracking space.
/// </summary>
/// <param name="pose">The out <see cref="TrackingSpacePose"/> which will get filled in.</param>
/// <returns>
/// True if the request was successful, False otherwise.
/// </returns>
/// <remarks>
/// <para>Although the request may succeed and provide a valid <see cref="TrackingSpacePose"/>, actual Position and Rotation provided
/// may not be valid and/or tracked, see <see cref="TrackingSpacePose"/> for more information on how to use its data.</para>
/// <para>Scene anchors follow a different transform from the raw OpenXR data than spatial anchors'.</para>
/// </remarks>
public bool TryGetSceneAnchorPose(out TrackingSpacePose pose)
{
if (!OVRPlugin.TryLocateSpace(Handle, OVRPlugin.GetTrackingOriginType(), out var posef, out var locationFlags))
{
pose = default;
return false;
}
// Transform from OpenXR Right-handed coordinate system
// to Unity Left-handed coordinate system with additional 180 rotation around +y
var position = posef.Position.FromFlippedZVector3f();
var rotation = new Quaternion(-posef.Orientation.z, posef.Orientation.w, -posef.Orientation.x,
posef.Orientation.y);
pose = new TrackingSpacePose(position, rotation, locationFlags);
return true;
}
/// <summary>
/// Tries to get the <see cref="TrackingSpacePose"/> representing the position and rotation of this anchor, treated as a spatial anchor, in tracking space.
/// </summary>
/// <param name="pose">The out <see cref="TrackingSpacePose"/> which will get filled in.</param>
/// <returns>
/// True if the request was successful, False otherwise.
/// </returns>
/// <remarks>
/// <para>Although the request may succeed and provide a valid <see cref="TrackingSpacePose"/>, actual position and rotation provided
/// may not be valid and/or tracked, see <see cref="TrackingSpacePose"/> for more information on how to use its data.</para>
/// <para>Spatial anchors follow a different transform from the raw OpenXR data than scene anchors'.</para>
/// </remarks>
public bool TryGetSpatialAnchorPose(out TrackingSpacePose pose)
{
if (!OVRPlugin.TryLocateSpace(Handle, OVRPlugin.GetTrackingOriginType(), out var posef, out var locationFlags))
{
pose = default;
return false;
}
// Transform from OpenXR Right-handed coordinate system
// to Unity Left-handed coordinate system
var position = posef.Position.FromFlippedZVector3f();
var rotation = posef.Orientation.FromFlippedZQuatf();
pose = new TrackingSpacePose(position, rotation, locationFlags);
return true;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2896867f1aef412690defa16271f3dcc
timeCreated: 1669392029
@@ -0,0 +1,84 @@
/*
* 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.
*/
using System;
using System.Collections.Generic;
/// <summary>
/// Represents a room described by its floor, ceiling and walls <see cref="OVRAnchor"/>s.
/// </summary>
/// <remarks>
/// This component can be accessed from an <see cref="OVRAnchor"/> that supports it by calling
/// <see cref="OVRAnchor.GetComponent{T}"/> from the anchor.
/// </remarks>
/// <seealso cref="FetchLayoutAnchorsAsync"/>
/// <seealso cref="TryGetRoomLayout"/>
public readonly partial struct OVRRoomLayout : IOVRAnchorComponent<OVRRoomLayout>, IEquatable<OVRRoomLayout>
{
/// <summary>
/// Asynchronous method that fetches anchors contained in the Room Layout.
/// </summary>
/// <param name="anchors">List that will get cleared and populated with the requested anchors.</param>
/// <remarks>Dispose of the returned task if you don't use the results</remarks>
/// <returns>A task that will eventually let you test if the fetch was successful or not.
/// If the result is true, then the <see cref="anchors"/> parameter has been populated with the requested anchors.</returns>
/// <exception cref="InvalidOperationException">If it fails to retrieve the Room Layout</exception>
/// <exception cref="ArgumentNullException">If parameter anchors is null</exception>
public OVRTask<bool> FetchLayoutAnchorsAsync(List<OVRAnchor> anchors)
{
if (!OVRPlugin.GetSpaceRoomLayout(Handle, out var roomLayout))
{
throw new InvalidOperationException("Could not get Room Layout");
}
using (new OVRObjectPool.ListScope<Guid>(out var list))
{
list.Add(roomLayout.floorUuid);
list.Add(roomLayout.ceilingUuid);
list.AddRange(roomLayout.wallUuids);
return OVRAnchor.FetchAnchorsAsync(list, anchors);
}
}
/// <summary>
/// Tries to get the Ceiling, Floor and Walls unique identifiers. These can then be used to Fetch their anchors.
/// </summary>
/// <param name="ceiling">Out <see cref="Guid"/> representing the ceiling of the room.</param>
/// <param name="floor">Out <see cref="Guid"/> representing the floor of the room.</param>
/// <param name="walls">Out array of <see cref="Guid"/>s representing the walls of the room.</param>
/// <returns>
/// <see cref="bool"/> true if the request succeeds and false if it fails.
/// </returns>
public bool TryGetRoomLayout(out Guid ceiling, out Guid floor, out Guid[] walls)
{
ceiling = Guid.Empty;
floor = Guid.Empty;
walls = null;
if (!OVRPlugin.GetSpaceRoomLayout(Handle, out var roomLayout))
{
return false;
}
ceiling = roomLayout.ceilingUuid;
floor = roomLayout.floorUuid;
walls = roomLayout.wallUuids;
return true;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d22c4dde2fa64be99f6c39b1c57ff87b
timeCreated: 1669715079
@@ -0,0 +1,53 @@
/*
* 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.
*/
using System;
/// <summary>
/// Descriptive labels of the <see cref="OVRAnchor"/>, as comma separated strings.
/// </summary>
/// <remarks>
/// This component can be accessed from an <see cref="OVRAnchor"/> that supports it by calling
/// <see cref="OVRAnchor.GetComponent{T}"/> from the anchor.
/// </remarks>
/// <seealso cref="Labels"/>
public readonly partial struct OVRSemanticLabels : IOVRAnchorComponent<OVRSemanticLabels>, IEquatable<OVRSemanticLabels>
{
// Features
/// <summary>
/// Semantic Labels
/// </summary>
/// <returns>
/// <para>Comma-separated values in one <see cref="string"/></para>
/// </returns>
/// <exception cref="Exception">If it fails to get the semantic labels</exception>
public string Labels
{
get
{
if (!OVRPlugin.GetSpaceSemanticLabels(Handle, out var labels))
{
throw new Exception("Could not Get Semantic Labels");
}
return OVRSemanticClassification.ValidateAndUpgradeLabels(labels);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: efe24f81961b460eb43781a3f72460ed
timeCreated: 1669712605
@@ -0,0 +1,222 @@
/*
* 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.
*/
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
using static OVRPlugin;
/// <summary>
/// Represents the Triangle Mesh component of an <see cref="OVRAnchor"/>.
/// </summary>
/// <remarks>
/// This component can be accessed from an <see cref="OVRAnchor"/> that supports it by calling
/// <see cref="OVRAnchor.GetComponent{T}"/> from the anchor.
/// </remarks>
/// <seealso cref="TryGetCounts"/>
/// <seealso cref="TryGetMeshRawUntransformed"/>
/// <seealso cref="TryGetMesh"/>
public readonly partial struct OVRTriangleMesh
{
/// <summary>
/// Gets the number of vertices and triangles in the mesh.
/// </summary>
/// <remarks>
/// Use this method to get the required sizes of the vertex and triangle index buffers. The length of the `indices`
/// array passed to <see cref="TryGetMesh"/> and <seealso cref="TryGetMeshRawUntransformed"/> should be three times
/// <paramref name="triangleCount"/>.
///
/// This method is thread-safe.
/// </remarks>
/// <param name="vertexCount">The number of vertices in the mesh.</param>
/// <param name="triangleCount">The number of triangles in the mesh. There are three times as many indices.</param>
/// <returns>True if the counts were retrieved; otherwise, false.</returns>
public bool TryGetCounts(out int vertexCount, out int triangleCount)
=> GetSpaceTriangleMeshCounts(Handle, out vertexCount, out triangleCount);
/// <summary>
/// Gets the raw, untransformed triangle mesh.
/// </summary>
/// <remarks>
/// ## Thread safety
/// This method is thread-safe.
///
/// ## Memory ownership
/// The caller owns the memory of the input arrays and is responsible for allocating them to the appropriate size
/// before passing them to this method. Use <see cref="TryGetCounts"/> to determine the required size of each array.
/// Note that <paramref name="indices"/> should be three times the number of triangles (`triangleCount`) indicated
/// by <see cref="TryGetCounts"/>.
///
/// ## Coordinate space
/// The mesh data provided by this method must be transformed into the appropriate coordinate space before being
/// used with a `UnityEngine.Mesh`. Use <see cref="TryGetMesh"/> or <see cref="ScheduleGetMeshJob"/> to get mesh
/// data in the correct coordinate space. This method is typically for advanced use cases where you want to perform
/// the conversion at a later time, or combine it with your own jobs.
///
/// The <paramref name="positions"/> are provided in the right-handed coordinate
/// space defined by OpenXR, with X to the right, Y up, and Z backward. <paramref name="indices"/> is an array of
/// index triplets which define the triangles in counter-clockwise order.
///
/// To convert to the coordinate space used by a Scene anchor in Unity's coordinate system, you must
/// - Negate each vertex's X coordinate
/// - Reverse the triangle winding by swapping each index triplet (a, b, c) => (a, c, b)
/// </remarks>
/// <param name="positions">The vertex positions of the mesh.</param>
/// <param name="indices">The triangle indices of the mesh.</param>
/// <returns>True if the mesh data was retrieved; otherwise, false.</returns>
public bool TryGetMeshRawUntransformed(NativeArray<Vector3> positions, NativeArray<int> indices)
=> GetSpaceTriangleMesh(Handle, positions, indices);
/// <summary>
/// Gets the triangle mesh.
/// </summary>
/// <remarks>
/// The caller owns the memory of the input arrays and is responsible for allocating them to the appropriate size
/// before passing them to this method. Use <see cref="TryGetCounts"/> to determine the required size of each array.
/// Note that <paramref name="indices"/> should be three times the number of triangles (`triangleCount`) indicated
/// by <see cref="TryGetCounts"/>.
///
/// This method is thread-safe.
/// </remarks>
/// <param name="positions">The vertex positions of the mesh.</param>
/// <param name="indices">The triangle indices of the mesh.</param>
/// <returns>True if the mesh data was retrieved; otherwise, false.</returns>
public bool TryGetMesh(NativeArray<Vector3> positions, NativeArray<int> indices)
{
if (!TryGetMeshRawUntransformed(positions, indices)) return false;
for (var i = 0; i < positions.Length; i++)
{
var p = positions[i];
// Necessary due to the coordinate space difference between OpenXR (right-handed) and Unity (left-handed)
positions[i] = new Vector3(-p.x, p.y, p.z);
}
var triangles = indices.Reinterpret<Triangle>(
expectedTypeSize: sizeof(int));
for (var i = 0; i < triangles.Length; i++)
{
var triangle = triangles[i];
triangles[i] = new Triangle
{
A = triangle.A,
B = triangle.C,
C = triangle.B
};
}
return true;
}
/// <summary>
/// Schedules a job to get an anchor's triangle mesh.
/// </summary>
/// <remarks>
/// This schedules jobs with the Unity Job system to retrieve the mesh data and then perform the necessary
/// conversion to Unity's coordinate space (see <see cref="TryGetMeshRawUntransformed"/>).
///
/// The caller owns the memory of the input arrays and is responsible for allocating them to the appropriate size
/// before passing them to this method. Use <see cref="TryGetCounts"/> to determine the required size of each array.
/// Note that <paramref name="indices"/> should be three times the number of triangles (`triangleCount`) indicated
/// by <see cref="TryGetCounts"/>.
///
/// If the triangle mesh cannot be retrieved, all <paramref name="indices"/> will be set to zero. Use this to check
/// for success after the job completes. For example, if the first three indices are zero, then the mesh is not
/// valid.
/// </remarks>
/// <param name="positions">The vertex positions of the triangle mesh.</param>
/// <param name="indices">The triangle indices of the triangle mesh.</param>
/// <param name="dependencies">(Optional) A job on which the new jobs will depend.</param>
/// <returns>Returns the handle associated with the new job.</returns>
public JobHandle ScheduleGetMeshJob(NativeArray<Vector3> positions, NativeArray<int> indices,
JobHandle dependencies = default)
{
var getMeshJob = new GetMeshJob
{
Positions = positions,
Indices = indices,
Space = Handle
}.Schedule(dependencies);
var triangles =
indices.Reinterpret<Triangle>(expectedTypeSize: sizeof(int));
return JobHandle.CombineDependencies(
new NegateXJob
{
Positions = positions
}.Schedule(positions.Length, 32, getMeshJob),
new FlipTriangleWindingJob
{
Triangles = triangles
}.Schedule(triangles.Length, 32, getMeshJob));
}
private struct GetMeshJob : IJob
{
public ulong Space;
public NativeArray<Vector3> Positions;
public NativeArray<int> Indices;
public unsafe void Execute()
{
if (!GetSpaceTriangleMesh(Space, Positions, Indices))
{
UnsafeUtility.MemSet(Indices.GetUnsafePtr(), 0, Indices.Length * sizeof(int));
}
}
}
private struct Triangle
{
public int A, B, C;
}
private struct FlipTriangleWindingJob : IJobParallelFor
{
public NativeArray<Triangle> Triangles;
public void Execute(int index)
{
var triangle = Triangles[index];
Triangles[index] = new Triangle
{
A = triangle.A,
B = triangle.C,
C = triangle.B
};
}
}
// Necessary due to the coordinate space difference between OpenXR (right-handed) and Unity (left-handed)
private struct NegateXJob : IJobParallelFor
{
public NativeArray<Vector3> Positions;
public void Execute(int index)
{
var p = Positions[index];
Positions[index] = new Vector3(-p.x, p.y, p.z);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0e2d445935c74f9484188ad82fbfd067
timeCreated: 1674000939
+196
View File
@@ -0,0 +1,196 @@
/*
* 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.
*/
using System;
using UnityEngine;
/// <summary>
/// Manages data related to body tracking.
/// </summary>
/// <remarks>
/// Typically, you would use this in conjunction with an <see cref="OVRSkeleton"/> and/or
/// <see cref="OVRSkeletonRenderer"/>.
/// </remarks>
[HelpURL("https://developer.oculus.com/reference/unity/latest/class_o_v_r_body")]
public class OVRBody : MonoBehaviour,
OVRSkeleton.IOVRSkeletonDataProvider,
OVRSkeletonRenderer.IOVRSkeletonRendererDataProvider
{
private OVRPlugin.BodyState _bodyState;
private OVRPlugin.Quatf[] _boneRotations;
private OVRPlugin.Vector3f[] _boneTranslations;
private bool _dataChangedSinceLastQuery;
private bool _hasData;
private const OVRPermissionsRequester.Permission BodyTrackingPermission =
OVRPermissionsRequester.Permission.BodyTracking;
private Action<string> _onPermissionGranted;
private static int _trackingInstanceCount;
/// <summary>
/// The raw <see cref="BodyState"/> data used to populate the <see cref="OVRSkeleton"/>.
/// </summary>
public OVRPlugin.BodyState? BodyState => _hasData ? _bodyState : default(OVRPlugin.BodyState?);
private void Awake()
{
_onPermissionGranted = OnPermissionGranted;
}
private void OnEnable()
{
_dataChangedSinceLastQuery = false;
_hasData = false;
_trackingInstanceCount++;
if (!StartBodyTracking())
{
enabled = false;
return;
}
if (OVRPlugin.nativeXrApi == OVRPlugin.XrApi.OpenXR)
{
GetBodyState(OVRPlugin.Step.Render);
}
else
{
enabled = false;
Debug.LogWarning($"[{nameof(OVRBody)}] Body tracking is only supported by OpenXR and is unavailable.");
}
}
private void OnPermissionGranted(string permissionId)
{
if (permissionId == OVRPermissionsRequester.GetPermissionId(BodyTrackingPermission))
{
OVRPermissionsRequester.PermissionGranted -= _onPermissionGranted;
enabled = true;
}
}
private bool StartBodyTracking()
{
if (!OVRPermissionsRequester.IsPermissionGranted(BodyTrackingPermission))
{
OVRPermissionsRequester.PermissionGranted -= _onPermissionGranted;
OVRPermissionsRequester.PermissionGranted += _onPermissionGranted;
return false;
}
if (!OVRPlugin.StartBodyTracking())
{
Debug.LogWarning($"[{nameof(OVRBody)}] Failed to start body tracking.");
return false;
}
return true;
}
private void OnDisable()
{
if (--_trackingInstanceCount == 0)
{
OVRPlugin.StopBodyTracking();
}
}
private void OnDestroy()
{
OVRPermissionsRequester.PermissionGranted -= _onPermissionGranted;
}
private void Update() => GetBodyState(OVRPlugin.Step.Render);
private void GetBodyState(OVRPlugin.Step step)
{
if (OVRPlugin.GetBodyState(step, ref _bodyState))
{
_hasData = true;
_dataChangedSinceLastQuery = true;
}
else
{
_hasData = false;
}
}
OVRSkeleton.SkeletonType OVRSkeleton.IOVRSkeletonDataProvider.GetSkeletonType() => OVRSkeleton.SkeletonType.Body;
OVRSkeleton.SkeletonPoseData OVRSkeleton.IOVRSkeletonDataProvider.GetSkeletonPoseData()
{
if (!_hasData)
return default;
if (_dataChangedSinceLastQuery)
{
// Make sure arrays have been allocated
Array.Resize(ref _boneRotations, _bodyState.JointLocations.Length);
Array.Resize(ref _boneTranslations, _bodyState.JointLocations.Length);
// Copy joint poses into bone arrays
for (var i = 0; i < _bodyState.JointLocations.Length; i++)
{
var jointLocation = _bodyState.JointLocations[i];
if (jointLocation.OrientationValid)
{
_boneRotations[i] = jointLocation.Pose.Orientation;
}
if (jointLocation.PositionValid)
{
_boneTranslations[i] = jointLocation.Pose.Position;
}
}
_dataChangedSinceLastQuery = false;
}
return new OVRSkeleton.SkeletonPoseData
{
IsDataValid = true,
IsDataHighConfidence = _bodyState.Confidence > .5f,
RootPose = _bodyState.JointLocations[(int)OVRPlugin.BoneId.Body_Root].Pose,
RootScale = 1.0f,
BoneRotations = _boneRotations,
BoneTranslations = _boneTranslations,
SkeletonChangedCount = (int)_bodyState.SkeletonChangedCount,
};
}
OVRSkeletonRenderer.SkeletonRendererData
OVRSkeletonRenderer.IOVRSkeletonRendererDataProvider.GetSkeletonRendererData() => _hasData
? new OVRSkeletonRenderer.SkeletonRendererData
{
RootScale = 1.0f,
IsDataValid = true,
IsDataHighConfidence = true,
ShouldUseSystemGestureMaterial = false,
}
: default;
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 274835e4e2feae04b87f0d000555f8a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -90
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+269
View File
@@ -0,0 +1,269 @@
/*
* 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 System;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
#if !USING_XR_SDK && !REQUIRES_XR_SDK
using Boundary = UnityEngine.Experimental.XR.Boundary;
#endif
/// <summary>
/// Provides access to the Oculus boundary system.
/// </summary>
[HelpURL("https://developer.oculus.com/reference/unity/latest/class_o_v_r_boundary")]
public class OVRBoundary
{
/// <summary>
/// Specifies a tracked node that can be queried through the boundary system.
/// </summary>
public enum Node
{
// Tracks the left hand node.
HandLeft = OVRPlugin.Node.HandLeft,
// Tracks the right hand node.
HandRight = OVRPlugin.Node.HandRight,
// Tracks the head node.
Head = OVRPlugin.Node.Head,
}
/// <summary>
/// Specifies a boundary type surface.
/// </summary>
public enum BoundaryType
{
// Outer boundary that closely matches the user's configured walls.
[System.Obsolete("Deprecated. This enum value will not be supported in OpenXR", false)]
OuterBoundary = OVRPlugin.BoundaryType.OuterBoundary,
// Smaller convex area inset within the outer boundary.
PlayArea = OVRPlugin.BoundaryType.PlayArea,
}
/// <summary>
/// Provides test results of boundary system queries.
/// </summary>
[System.Obsolete("Deprecated. This struct will not be supported in OpenXR", false)]
public struct BoundaryTestResult
{
// Returns true if the queried test would violate and/or trigger the tested boundary types.
public bool IsTriggering;
// Returns the distance between the queried test object and the closest tested boundary type.
public float ClosestDistance;
// Returns the closest point to the queried test object.
public Vector3 ClosestPoint;
// Returns the normal of the closest point to the queried test object.
public Vector3 ClosestPointNormal;
}
/// <summary>
/// Returns true if the boundary system is currently configured with valid boundary data.
/// </summary>
public bool GetConfigured()
{
if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus)
return OVRPlugin.GetBoundaryConfigured();
else
{
#if !USING_XR_SDK && !REQUIRES_XR_SDK
return Boundary.configured;
#else
return false;
#endif
}
}
/// <summary>
/// Returns the results of testing a tracked node against the specified boundary type.
/// All points are returned in local tracking space shared by tracked nodes and accessible through OVRCameraRig's trackingSpace anchor.
/// </summary>
[System.Obsolete("Deprecated. This function will not be supported in OpenXR", false)]
public OVRBoundary.BoundaryTestResult TestNode(OVRBoundary.Node node, OVRBoundary.BoundaryType boundaryType)
{
OVRPlugin.BoundaryTestResult ovrpRes =
OVRPlugin.TestBoundaryNode((OVRPlugin.Node)node, (OVRPlugin.BoundaryType)boundaryType);
OVRBoundary.BoundaryTestResult res = new OVRBoundary.BoundaryTestResult()
{
IsTriggering = (ovrpRes.IsTriggering == OVRPlugin.Bool.True),
ClosestDistance = ovrpRes.ClosestDistance,
ClosestPoint = ovrpRes.ClosestPoint.FromFlippedZVector3f(),
ClosestPointNormal = ovrpRes.ClosestPointNormal.FromFlippedZVector3f(),
};
return res;
}
/// <summary>
/// Returns the results of testing a 3d point against the specified boundary type.
/// The test point is expected in local tracking space.
/// All points are returned in local tracking space shared by tracked nodes and accessible through OVRCameraRig's trackingSpace anchor.
/// </summary>
[System.Obsolete("Deprecated. This function will not be supported in OpenXR", false)]
public OVRBoundary.BoundaryTestResult TestPoint(Vector3 point, OVRBoundary.BoundaryType boundaryType)
{
OVRPlugin.BoundaryTestResult ovrpRes =
OVRPlugin.TestBoundaryPoint(point.ToFlippedZVector3f(), (OVRPlugin.BoundaryType)boundaryType);
OVRBoundary.BoundaryTestResult res = new OVRBoundary.BoundaryTestResult()
{
IsTriggering = (ovrpRes.IsTriggering == OVRPlugin.Bool.True),
ClosestDistance = ovrpRes.ClosestDistance,
ClosestPoint = ovrpRes.ClosestPoint.FromFlippedZVector3f(),
ClosestPointNormal = ovrpRes.ClosestPointNormal.FromFlippedZVector3f(),
};
return res;
}
private static int cachedVector3fSize = Marshal.SizeOf(typeof(OVRPlugin.Vector3f));
private static OVRNativeBuffer cachedGeometryNativeBuffer = new OVRNativeBuffer(0);
private static float[] cachedGeometryManagedBuffer = new float[0];
private List<Vector3> cachedGeometryList = new List<Vector3>();
/// <summary>
/// Returns an array of 3d points (in clockwise order) that define the specified boundary type.
/// All points are returned in local tracking space shared by tracked nodes and accessible through OVRCameraRig's trackingSpace anchor.
/// </summary>
public Vector3[] GetGeometry(OVRBoundary.BoundaryType boundaryType)
{
if (OVRManager.loadedXRDevice != OVRManager.XRDevice.Oculus)
{
#if !USING_XR_SDK && !REQUIRES_XR_SDK
if (Boundary.TryGetGeometry(cachedGeometryList,
(boundaryType == BoundaryType.PlayArea) ? Boundary.Type.PlayArea : Boundary.Type.TrackedArea))
{
Vector3[] arr = cachedGeometryList.ToArray();
return arr;
}
#endif
Debug.LogError("This functionality is not supported in your current version of Unity.");
return null;
}
int pointsCount = 0;
if (OVRPlugin.GetBoundaryGeometry2((OVRPlugin.BoundaryType)boundaryType, IntPtr.Zero, ref pointsCount))
{
if (pointsCount > 0)
{
int requiredNativeBufferCapacity = pointsCount * cachedVector3fSize;
if (cachedGeometryNativeBuffer.GetCapacity() < requiredNativeBufferCapacity)
cachedGeometryNativeBuffer.Reset(requiredNativeBufferCapacity);
int requiredManagedBufferCapacity = pointsCount * 3;
if (cachedGeometryManagedBuffer.Length < requiredManagedBufferCapacity)
cachedGeometryManagedBuffer = new float[requiredManagedBufferCapacity];
if (OVRPlugin.GetBoundaryGeometry2((OVRPlugin.BoundaryType)boundaryType,
cachedGeometryNativeBuffer.GetPointer(), ref pointsCount))
{
Marshal.Copy(cachedGeometryNativeBuffer.GetPointer(), cachedGeometryManagedBuffer, 0,
requiredManagedBufferCapacity);
Vector3[] points = new Vector3[pointsCount];
for (int i = 0; i < pointsCount; i++)
{
points[i] = new OVRPlugin.Vector3f()
{
x = cachedGeometryManagedBuffer[3 * i + 0],
y = cachedGeometryManagedBuffer[3 * i + 1],
z = cachedGeometryManagedBuffer[3 * i + 2],
}.FromFlippedZVector3f();
}
return points;
}
}
}
return new Vector3[0];
}
/// <summary>
/// Returns a vector that indicates the spatial dimensions of the specified boundary type. (x = width, y = height, z = depth)
/// </summary>
public Vector3 GetDimensions(OVRBoundary.BoundaryType boundaryType)
{
if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus)
return OVRPlugin.GetBoundaryDimensions((OVRPlugin.BoundaryType)boundaryType).FromVector3f();
else
{
#if !USING_XR_SDK && !REQUIRES_XR_SDK
Vector3 dimensions;
if (Boundary.TryGetDimensions(out dimensions,
(boundaryType == BoundaryType.PlayArea) ? Boundary.Type.PlayArea : Boundary.Type.TrackedArea))
return dimensions;
#endif
return Vector3.zero;
}
}
/// <summary>
/// Returns true if the boundary system is currently visible.
/// </summary>
[System.Obsolete("Deprecated. This function will not be supported in OpenXR", false)]
public bool GetVisible()
{
if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus)
return OVRPlugin.GetBoundaryVisible();
else
{
#if !USING_XR_SDK && !REQUIRES_XR_SDK
return Boundary.visible;
#else
return false;
#endif
}
}
/// <summary>
/// Requests that the boundary system visibility be set to the specified value.
/// The actual visibility can be overridden by the system (i.e., proximity trigger) or by the user (boundary system disabled)
/// </summary>
[System.Obsolete("Deprecated. This function will not be supported in OpenXR", false)]
public void SetVisible(bool value)
{
if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus)
OVRPlugin.SetBoundaryVisible(value);
else
{
#if !USING_XR_SDK && !REQUIRES_XR_SDK
Boundary.visible = value;
#endif
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 332b8e08854932543ba356eec601c0ef
timeCreated: 1470352252
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+719
View File
@@ -0,0 +1,719 @@
/*
* 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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
using Node = UnityEngine.XR.XRNode;
/// <summary>
/// A head-tracked stereoscopic virtual reality camera rig.
/// </summary>
[ExecuteInEditMode]
[HelpURL("https://developer.oculus.com/reference/unity/latest/class_o_v_r_camera_rig")]
public class OVRCameraRig : MonoBehaviour
{
/// <summary>
/// The left eye camera.
/// </summary>
public Camera leftEyeCamera
{
get { return (usePerEyeCameras) ? _leftEyeCamera : _centerEyeCamera; }
}
/// <summary>
/// The right eye camera.
/// </summary>
public Camera rightEyeCamera
{
get { return (usePerEyeCameras) ? _rightEyeCamera : _centerEyeCamera; }
}
/// <summary>
/// Provides a root transform for all anchors in tracking space.
/// </summary>
public Transform trackingSpace { get; private set; }
/// <summary>
/// Always coincides with the pose of the left eye.
/// </summary>
public Transform leftEyeAnchor { get; private set; }
/// <summary>
/// Always coincides with average of the left and right eye poses.
/// </summary>
public Transform centerEyeAnchor { get; private set; }
/// <summary>
/// Always coincides with the pose of the right eye.
/// </summary>
public Transform rightEyeAnchor { get; private set; }
/// <summary>
/// Always coincides with the pose of the left active input device.
/// </summary>
public Transform leftHandAnchor { get; private set; }
/// <summary>
/// Always coincides with the pose of the right active input device.
/// </summary>
public Transform rightHandAnchor { get; private set; }
/// <summary>
/// Coincides with the pose of the left input device if it is detached.
/// </summary>
public Transform leftHandAnchorDetached { get; private set; }
/// <summary>
/// Coincides with the pose of the left input device if it is detached.
/// </summary>
public Transform rightHandAnchorDetached { get; private set; }
/// <summary>
/// Coincides with the pose of the left controller when it is in a hand
/// </summary>
public Transform leftControllerInHandAnchor { get; private set; }
/// <summary>
/// Coincides with the pose of the left hand when it is on a controller
/// </summary>
public Transform leftHandOnControllerAnchor { get; private set; }
/// <summary>
/// Coincides with the pose of the right controller when it is in a hand
/// </summary>
public Transform rightControllerInHandAnchor { get; private set; }
/// <summary>
/// Coincides with the pose of the right hand when it is on a controller
/// </summary>
public Transform rightHandOnControllerAnchor { get; private set; }
/// <summary>
/// Anchors controller pose to fix offset issues for the left hand.
/// </summary>
public Transform leftControllerAnchor { get; private set; }
/// <summary>
/// Anchors controller pose to fix offset issues for the right hand.
/// </summary>
public Transform rightControllerAnchor { get; private set; }
/// <summary>
/// Always coincides with the pose of the sensor.
/// </summary>
public Transform trackerAnchor { get; private set; }
/// <summary>
/// Occurs when the eye pose anchors have been set.
/// </summary>
public event System.Action<OVRCameraRig> UpdatedAnchors;
/// <summary>
/// Occurs when the <see cref="trackingSpace"/>'s transform changes.
/// </summary>
/// <remarks>
/// This event provides a single argument: the `Transform` of the tracking space. This is the same as the
/// <see cref="trackingSpace"/> property.
/// </remarks>
public event Action<Transform> TrackingSpaceChanged;
/// <summary>
/// If true, separate cameras will be used for the left and right eyes.
/// </summary>
public bool usePerEyeCameras = false;
/// <summary>
/// If true, all tracked anchors are updated in FixedUpdate instead of Update to favor physics fidelity.
/// \note: This will cause visible judder unless you tick exactly once per frame using a custom physics
/// update, because you'll be sampling the position at different times into each frame.
/// </summary>
public bool useFixedUpdateForTracking = false;
/// <summary>
/// If true, the cameras on the eyeAnchors will be disabled.
/// \note: The main camera of the game will be used to provide VR rendering. And the tracking space anchors will still be updated to provide reference poses.
/// </summary>
public bool disableEyeAnchorCameras = false;
protected bool _skipUpdate = false;
protected readonly string trackingSpaceName = "TrackingSpace";
protected readonly string trackerAnchorName = "TrackerAnchor";
protected readonly string leftEyeAnchorName = "LeftEyeAnchor";
protected readonly string centerEyeAnchorName = "CenterEyeAnchor";
protected readonly string rightEyeAnchorName = "RightEyeAnchor";
protected readonly string leftHandAnchorName = "LeftHandAnchor";
protected readonly string rightHandAnchorName = "RightHandAnchor";
protected readonly string leftControllerAnchorName = "LeftControllerAnchor";
protected readonly string rightControllerAnchorName = "RightControllerAnchor";
protected readonly string leftHandAnchorDetachedName = "LeftHandAnchorDetached";
protected readonly string rightHandAnchorDetachedName = "RightHandAnchorDetached";
protected readonly string leftControllerInHandAnchorName = "LeftControllerInHandAnchor";
protected readonly string leftHandOnControllerAnchorName = "LeftHandOnControllerAnchor";
protected readonly string rightControllerInHandAnchorName = "RightControllerInHandAnchor";
protected readonly string rightHandOnControllerAnchorName = "RightHandOnControllerAnchor";
protected Camera _centerEyeCamera;
protected Camera _leftEyeCamera;
protected Camera _rightEyeCamera;
private Matrix4x4 _previousTrackingSpaceTransform;
#region Unity Messages
protected virtual void Awake()
{
_skipUpdate = true;
EnsureGameObjectIntegrity();
}
protected virtual void Start()
{
UpdateAnchors(true, true);
Application.onBeforeRender += OnBeforeRenderCallback;
}
protected virtual void FixedUpdate()
{
if (useFixedUpdateForTracking)
UpdateAnchors(true, true);
}
protected virtual void Update()
{
_skipUpdate = false;
if (!useFixedUpdateForTracking)
UpdateAnchors(true, true);
#if DEVELOPMENT_BUILD || UNITY_EDITOR
CheckForAnchorsInParent();
#endif
}
protected virtual void OnDestroy()
{
Application.onBeforeRender -= OnBeforeRenderCallback;
}
#endregion
protected virtual void UpdateAnchors(bool updateEyeAnchors, bool updateHandAnchors)
{
if (!OVRManager.OVRManagerinitialized)
return;
EnsureGameObjectIntegrity();
if (!Application.isPlaying)
return;
if (_skipUpdate)
{
centerEyeAnchor.FromOVRPose(OVRPose.identity, true);
leftEyeAnchor.FromOVRPose(OVRPose.identity, true);
rightEyeAnchor.FromOVRPose(OVRPose.identity, true);
return;
}
bool monoscopic = OVRManager.instance.monoscopic;
bool hmdPresent = OVRNodeStateProperties.IsHmdPresent();
OVRPose tracker = OVRManager.tracker.GetPose();
trackerAnchor.localRotation = tracker.orientation;
Quaternion emulatedRotation = Quaternion.Euler(-OVRManager.instance.headPoseRelativeOffsetRotation.x,
-OVRManager.instance.headPoseRelativeOffsetRotation.y,
OVRManager.instance.headPoseRelativeOffsetRotation.z);
//Note: in the below code, when using UnityEngine's API, we only update anchor transforms if we have a new, fresh value this frame.
//If we don't, it could mean that tracking is lost, etc. so the pose should not change in the virtual world.
//This can be thought of as similar to calling InputTracking GetLocalPosition and Rotation, but only for doing so when the pose is valid.
//If false is returned for any of these calls, then a new pose is not valid and thus should not be updated.
if (updateEyeAnchors)
{
if (hmdPresent)
{
Vector3 centerEyePosition = Vector3.zero;
Quaternion centerEyeRotation = Quaternion.identity;
if (OVRNodeStateProperties.GetNodeStatePropertyVector3(Node.CenterEye, NodeStatePropertyType.Position,
OVRPlugin.Node.EyeCenter, OVRPlugin.Step.Render, out centerEyePosition))
centerEyeAnchor.localPosition = centerEyePosition;
if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(Node.CenterEye,
NodeStatePropertyType.Orientation, OVRPlugin.Node.EyeCenter, OVRPlugin.Step.Render,
out centerEyeRotation))
centerEyeAnchor.localRotation = centerEyeRotation;
}
else
{
centerEyeAnchor.localRotation = emulatedRotation;
centerEyeAnchor.localPosition = OVRManager.instance.headPoseRelativeOffsetTranslation;
}
if (!hmdPresent || monoscopic)
{
leftEyeAnchor.localPosition = centerEyeAnchor.localPosition;
rightEyeAnchor.localPosition = centerEyeAnchor.localPosition;
leftEyeAnchor.localRotation = centerEyeAnchor.localRotation;
rightEyeAnchor.localRotation = centerEyeAnchor.localRotation;
}
else
{
Vector3 leftEyePosition = Vector3.zero;
Vector3 rightEyePosition = Vector3.zero;
Quaternion leftEyeRotation = Quaternion.identity;
Quaternion rightEyeRotation = Quaternion.identity;
if (OVRNodeStateProperties.GetNodeStatePropertyVector3(Node.LeftEye, NodeStatePropertyType.Position,
OVRPlugin.Node.EyeLeft, OVRPlugin.Step.Render, out leftEyePosition))
leftEyeAnchor.localPosition = leftEyePosition;
if (OVRNodeStateProperties.GetNodeStatePropertyVector3(Node.RightEye, NodeStatePropertyType.Position,
OVRPlugin.Node.EyeRight, OVRPlugin.Step.Render, out rightEyePosition))
rightEyeAnchor.localPosition = rightEyePosition;
if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(Node.LeftEye,
NodeStatePropertyType.Orientation, OVRPlugin.Node.EyeLeft, OVRPlugin.Step.Render,
out leftEyeRotation))
leftEyeAnchor.localRotation = leftEyeRotation;
if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(Node.RightEye,
NodeStatePropertyType.Orientation, OVRPlugin.Node.EyeRight, OVRPlugin.Step.Render,
out rightEyeRotation))
rightEyeAnchor.localRotation = rightEyeRotation;
}
}
if (updateHandAnchors)
{
//Need this for controller offset because if we're on OpenVR, we want to set the local poses as specified by Unity, but if we're not, OVRInput local position is the right anchor
if (OVRManager.loadedXRDevice == OVRManager.XRDevice.OpenVR)
{
Vector3 leftPos = Vector3.zero;
Vector3 rightPos = Vector3.zero;
Quaternion leftQuat = Quaternion.identity;
Quaternion rightQuat = Quaternion.identity;
if (OVRNodeStateProperties.GetNodeStatePropertyVector3(Node.LeftHand, NodeStatePropertyType.Position,
OVRPlugin.Node.HandLeft, OVRPlugin.Step.Render, out leftPos))
leftHandAnchor.localPosition = leftPos;
if (OVRNodeStateProperties.GetNodeStatePropertyVector3(Node.RightHand, NodeStatePropertyType.Position,
OVRPlugin.Node.HandRight, OVRPlugin.Step.Render, out rightPos))
rightHandAnchor.localPosition = rightPos;
if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(Node.LeftHand,
NodeStatePropertyType.Orientation, OVRPlugin.Node.HandLeft, OVRPlugin.Step.Render,
out leftQuat))
leftHandAnchor.localRotation = leftQuat;
if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(Node.RightHand,
NodeStatePropertyType.Orientation, OVRPlugin.Node.HandRight, OVRPlugin.Step.Render,
out rightQuat))
rightHandAnchor.localRotation = rightQuat;
}
else
{
OVRInput.Controller leftActiveController =
OVRInput.GetActiveControllerForHand(OVRInput.Handedness.LeftHanded);
OVRInput.Controller rightActiveController =
OVRInput.GetActiveControllerForHand(OVRInput.Handedness.RightHanded);
// it's possible for niether hands or controllers to be active, in that case
// manually calculate which one we care about giving hands priority.
if (leftActiveController == OVRInput.Controller.None)
{
if (OVRInput.GetControllerPositionValid(OVRInput.Controller.LHand))
{
leftActiveController = OVRInput.Controller.LHand;
}
else if (OVRInput.GetControllerPositionValid(OVRInput.Controller.LTouch))
{
leftActiveController = OVRInput.Controller.LTouch;
}
}
if (rightActiveController == OVRInput.Controller.None)
{
if (OVRInput.GetControllerPositionValid(OVRInput.Controller.RHand))
{
rightActiveController = OVRInput.Controller.RHand;
}
else if (OVRInput.GetControllerPositionValid(OVRInput.Controller.RTouch))
{
rightActiveController = OVRInput.Controller.RTouch;
}
}
leftHandAnchor.localPosition = OVRInput.GetLocalControllerPosition(leftActiveController);
rightHandAnchor.localPosition = OVRInput.GetLocalControllerPosition(rightActiveController);
leftHandAnchor.localRotation = OVRInput.GetLocalControllerRotation(leftActiveController);
rightHandAnchor.localRotation = OVRInput.GetLocalControllerRotation(rightActiveController);
// left
OVRInput.ControllerInHandState controllerInHandState =
OVRInput.GetControllerIsInHandState(OVRInput.Hand.HandLeft);
if (controllerInHandState == OVRInput.ControllerInHandState.ControllerNotInHand)
{
leftHandAnchorDetached.localPosition =
OVRInput.GetLocalControllerPosition(OVRInput.Controller.LTouch);
leftHandAnchorDetached.localRotation =
OVRInput.GetLocalControllerRotation(OVRInput.Controller.LTouch);
leftHandOnControllerAnchor.localPosition = Vector3.zero;
leftHandOnControllerAnchor.localRotation = Quaternion.identity;
}
else if (controllerInHandState == OVRInput.ControllerInHandState.ControllerInHand)
{
Vector3 leftRelativeHandPosition =
trackingSpace.TransformPoint(OVRInput.GetLocalControllerPosition(OVRInput.Controller.LHand));
leftHandOnControllerAnchor.localPosition =
leftHandAnchor.InverseTransformPoint(leftRelativeHandPosition);
leftHandOnControllerAnchor.localRotation = Quaternion.Inverse(leftHandAnchor.localRotation) *
OVRInput.GetLocalControllerRotation(OVRInput.Controller
.LHand);
leftHandAnchorDetached.localPosition = Vector3.zero;
leftHandAnchorDetached.localRotation = Quaternion.identity;
}
else
{
leftHandAnchorDetached.localPosition = Vector3.zero;
leftHandAnchorDetached.localRotation = Quaternion.identity;
leftHandOnControllerAnchor.localPosition = Vector3.zero;
leftHandOnControllerAnchor.localRotation = Quaternion.identity;
}
// right
controllerInHandState = OVRInput.GetControllerIsInHandState(OVRInput.Hand.HandRight);
if (controllerInHandState == OVRInput.ControllerInHandState.ControllerNotInHand)
{
rightHandAnchorDetached.localPosition =
OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch);
rightHandAnchorDetached.localRotation =
OVRInput.GetLocalControllerRotation(OVRInput.Controller.RTouch);
rightHandOnControllerAnchor.localPosition = Vector3.zero;
rightHandOnControllerAnchor.localRotation = Quaternion.identity;
}
else if (controllerInHandState == OVRInput.ControllerInHandState.ControllerInHand)
{
Vector3 rightRelativeHandPosition =
trackingSpace.TransformPoint(OVRInput.GetLocalControllerPosition(OVRInput.Controller.RHand));
rightHandOnControllerAnchor.localPosition =
rightHandAnchor.InverseTransformPoint(rightRelativeHandPosition);
rightHandOnControllerAnchor.localRotation = Quaternion.Inverse(rightHandAnchor.localRotation) *
OVRInput.GetLocalControllerRotation(OVRInput.Controller
.RHand);
rightHandAnchorDetached.localPosition = Vector3.zero;
rightHandAnchorDetached.localRotation = Quaternion.identity;
}
else
{
rightHandAnchorDetached.localPosition = Vector3.zero;
rightHandAnchorDetached.localRotation = Quaternion.identity;
rightHandOnControllerAnchor.localPosition = Vector3.zero;
rightHandOnControllerAnchor.localRotation = Quaternion.identity;
}
}
trackerAnchor.localPosition = tracker.position;
OVRPose leftOffsetPose = OVRPose.identity;
OVRPose rightOffsetPose = OVRPose.identity;
if (OVRManager.loadedXRDevice == OVRManager.XRDevice.OpenVR)
{
leftOffsetPose = OVRManager.GetOpenVRControllerOffset(Node.LeftHand);
rightOffsetPose = OVRManager.GetOpenVRControllerOffset(Node.RightHand);
//Sets poses of left and right nodes, local to the tracking space.
OVRManager.SetOpenVRLocalPose(trackingSpace.InverseTransformPoint(leftControllerAnchor.position),
trackingSpace.InverseTransformPoint(rightControllerAnchor.position),
Quaternion.Inverse(trackingSpace.rotation) * leftControllerAnchor.rotation,
Quaternion.Inverse(trackingSpace.rotation) * rightControllerAnchor.rotation);
}
rightControllerAnchor.localPosition = rightOffsetPose.position;
rightControllerAnchor.localRotation = rightOffsetPose.orientation;
leftControllerAnchor.localPosition = leftOffsetPose.position;
leftControllerAnchor.localRotation = leftOffsetPose.orientation;
}
#if USING_XR_SDK
#if UNITY_2020_3_OR_NEWER
if (OVRManager.instance.LateLatching)
{
XRDisplaySubsystem displaySubsystem = OVRManager.GetCurrentDisplaySubsystem();
if (displaySubsystem != null)
{
displaySubsystem.MarkTransformLateLatched(centerEyeAnchor.transform,
XRDisplaySubsystem.LateLatchNode.Head);
displaySubsystem.MarkTransformLateLatched(leftHandAnchor, XRDisplaySubsystem.LateLatchNode.LeftHand);
displaySubsystem.MarkTransformLateLatched(rightHandAnchor, XRDisplaySubsystem.LateLatchNode.RightHand);
}
}
#endif
#endif
RaiseUpdatedAnchorsEvent();
CheckForTrackingSpaceChangesAndRaiseEvent();
}
protected virtual void OnBeforeRenderCallback()
{
if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus) //Restrict late-update to only Oculus devices
{
bool controllersNeedUpdate = OVRManager.instance.LateControllerUpdate;
#if USING_XR_SDK
//For the XR SDK, we need to late update head pose, not just the controllers, because the functionality
//is no longer built-in to the Engine. Under legacy, late camera update is done by default. In the XR SDK, you must use
//Tracked Pose Driver to get this by default, which we do not use. So, we have to manually late update camera poses.
UpdateAnchors(true, controllersNeedUpdate);
#else
if (controllersNeedUpdate)
UpdateAnchors(false, true);
#endif
}
}
/// <summary>
/// Checks for changes to <see cref="trackingSpace"/>'s transform and, if it has changed, raises the
/// <see cref="TrackingSpaceChanged"/> event.
/// </summary>
/// <remarks>
/// This method compares the <see cref="trackingSpace"/> transform's `localToWorldMatrix` matrix against its value
/// during the last invocation of this method.
///
/// If <see cref="trackingSpace"/> is `null`, this method has no effect.
/// </remarks>
protected virtual void CheckForTrackingSpaceChangesAndRaiseEvent()
{
if (trackingSpace == null) return;
var currentLocalToWorld = trackingSpace.localToWorldMatrix;
var shouldRaiseEvent = TrackingSpaceChanged != null &&
!_previousTrackingSpaceTransform.Equals(currentLocalToWorld);
_previousTrackingSpaceTransform = currentLocalToWorld;
if (shouldRaiseEvent)
{
TrackingSpaceChanged(trackingSpace);
}
}
protected virtual void RaiseUpdatedAnchorsEvent()
{
if (UpdatedAnchors != null)
{
UpdatedAnchors(this);
}
}
public virtual void EnsureGameObjectIntegrity()
{
bool monoscopic = OVRManager.instance != null ? OVRManager.instance.monoscopic : false;
if (trackingSpace == null)
{
trackingSpace = ConfigureAnchor(null, trackingSpaceName);
_previousTrackingSpaceTransform = trackingSpace.localToWorldMatrix;
}
if (leftEyeAnchor == null)
leftEyeAnchor = ConfigureAnchor(trackingSpace, leftEyeAnchorName);
if (centerEyeAnchor == null)
centerEyeAnchor = ConfigureAnchor(trackingSpace, centerEyeAnchorName);
if (rightEyeAnchor == null)
rightEyeAnchor = ConfigureAnchor(trackingSpace, rightEyeAnchorName);
if (leftHandAnchor == null)
leftHandAnchor = ConfigureAnchor(trackingSpace, leftHandAnchorName);
if (rightHandAnchor == null)
rightHandAnchor = ConfigureAnchor(trackingSpace, rightHandAnchorName);
if (leftHandAnchorDetached == null)
leftHandAnchorDetached = ConfigureAnchor(trackingSpace, leftHandAnchorDetachedName);
if (rightHandAnchorDetached == null)
rightHandAnchorDetached = ConfigureAnchor(trackingSpace, rightHandAnchorDetachedName);
if (leftControllerInHandAnchor == null)
leftControllerInHandAnchor = ConfigureAnchor(leftHandAnchor, leftControllerInHandAnchorName);
if (leftHandOnControllerAnchor == null)
leftHandOnControllerAnchor = ConfigureAnchor(leftControllerInHandAnchor, leftHandOnControllerAnchorName);
if (rightControllerInHandAnchor == null)
rightControllerInHandAnchor = ConfigureAnchor(rightHandAnchor, rightControllerInHandAnchorName);
if (rightHandOnControllerAnchor == null)
rightHandOnControllerAnchor = ConfigureAnchor(rightControllerInHandAnchor, rightHandOnControllerAnchorName);
if (trackerAnchor == null)
trackerAnchor = ConfigureAnchor(trackingSpace, trackerAnchorName);
if (leftControllerAnchor == null)
leftControllerAnchor = ConfigureAnchor(leftHandAnchor, leftControllerAnchorName);
if (rightControllerAnchor == null)
rightControllerAnchor = ConfigureAnchor(rightHandAnchor, rightControllerAnchorName);
if (_centerEyeCamera == null || _leftEyeCamera == null || _rightEyeCamera == null)
{
_centerEyeCamera = centerEyeAnchor.GetComponent<Camera>();
_leftEyeCamera = leftEyeAnchor.GetComponent<Camera>();
_rightEyeCamera = rightEyeAnchor.GetComponent<Camera>();
if (_centerEyeCamera == null)
{
_centerEyeCamera = centerEyeAnchor.gameObject.AddComponent<Camera>();
_centerEyeCamera.tag = "MainCamera";
}
if (_leftEyeCamera == null)
{
_leftEyeCamera = leftEyeAnchor.gameObject.AddComponent<Camera>();
_leftEyeCamera.tag = "MainCamera";
}
if (_rightEyeCamera == null)
{
_rightEyeCamera = rightEyeAnchor.gameObject.AddComponent<Camera>();
_rightEyeCamera.tag = "MainCamera";
}
_centerEyeCamera.stereoTargetEye = StereoTargetEyeMask.Both;
_leftEyeCamera.stereoTargetEye = StereoTargetEyeMask.Left;
_rightEyeCamera.stereoTargetEye = StereoTargetEyeMask.Right;
}
if (monoscopic && !OVRPlugin.EyeTextureArrayEnabled)
{
// Output to left eye only when in monoscopic mode
if (_centerEyeCamera.stereoTargetEye != StereoTargetEyeMask.Left)
{
_centerEyeCamera.stereoTargetEye = StereoTargetEyeMask.Left;
}
}
else
{
if (_centerEyeCamera.stereoTargetEye != StereoTargetEyeMask.Both)
{
_centerEyeCamera.stereoTargetEye = StereoTargetEyeMask.Both;
}
}
if (disableEyeAnchorCameras)
{
_centerEyeCamera.enabled = false;
_leftEyeCamera.enabled = false;
_rightEyeCamera.enabled = false;
}
else
{
// disable the right eye camera when in monoscopic mode
if (_centerEyeCamera.enabled == usePerEyeCameras ||
_leftEyeCamera.enabled == !usePerEyeCameras ||
_rightEyeCamera.enabled == !(usePerEyeCameras && (!monoscopic || OVRPlugin.EyeTextureArrayEnabled)))
{
_skipUpdate = true;
}
_centerEyeCamera.enabled = !usePerEyeCameras;
_leftEyeCamera.enabled = usePerEyeCameras;
_rightEyeCamera.enabled = (usePerEyeCameras && (!monoscopic || OVRPlugin.EyeTextureArrayEnabled));
}
}
protected virtual Transform ConfigureAnchor(Transform root, string name)
{
Transform anchor = (root != null) ? root.Find(name) : null;
if (anchor == null)
{
anchor = transform.Find(name);
}
if (anchor == null)
{
anchor = new GameObject(name).transform;
}
anchor.name = name;
anchor.parent = (root != null) ? root : transform;
anchor.localScale = Vector3.one;
anchor.localPosition = Vector3.zero;
anchor.localRotation = Quaternion.identity;
return anchor;
}
public virtual Matrix4x4 ComputeTrackReferenceMatrix()
{
if (centerEyeAnchor == null)
{
Debug.LogError("centerEyeAnchor is required");
return Matrix4x4.identity;
}
// The ideal approach would be using UnityEngine.VR.VRNode.TrackingReference, then we would not have to depend on the OVRCameraRig. Unfortunately, it is not available in Unity 5.4.3
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;
OVRPose invHeadPose = headPose.Inverse();
Matrix4x4 invHeadMatrix = Matrix4x4.TRS(invHeadPose.position, invHeadPose.orientation, Vector3.one);
Matrix4x4 ret = centerEyeAnchor.localToWorldMatrix * invHeadMatrix;
return ret;
}
private void CheckForAnchorsInParent()
{
void Check<T>(Transform node) where T : MonoBehaviour
{
var anchor = node.GetComponent<T>();
if (anchor && anchor.enabled)
{
anchor.enabled = false;
Debug.LogError(
$"The {typeof(T).Name} '{anchor.name}' is a parent of the {nameof(OVRCameraRig)} '{name}', which is not allowed. An {typeof(T).Name} may not be the parent of an {nameof(OVRCameraRig)} because the {nameof(OVRCameraRig)} defines the tracking space for the anchor, and its transform is relative to the {nameof(OVRCameraRig)}.");
}
}
var parent = transform.parent;
while (parent)
{
Check<OVRSpatialAnchor>(parent);
Check<OVRSceneAnchor>(parent);
parent = parent.parent;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: df9f338034892c44ebb62d97894772f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -50
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+824
View File
@@ -0,0 +1,824 @@
/*
* 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;
/// <summary>
/// Miscellaneous extension methods that any script can use.
/// </summary>
public static partial class OVRExtensions
{
/// <summary>
/// Converts the given world-space transform to an OVRPose in tracking space.
/// </summary>
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;
}
/// <summary>
/// Converts the given pose from tracking-space to world-space.
/// </summary>
[Obsolete("ToWorldSpacePose should be invoked with an explicit mainCamera parameter")]
public static OVRPose ToWorldSpacePose(this OVRPose trackingSpacePose)
{
return ToWorldSpacePose(trackingSpacePose, Camera.main);
}
/// <summary>
/// Converts the given pose from tracking-space to world-space.
/// </summary>
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
};
}
/// <summary>
/// Converts the given pose from tracking-space to head-space.
/// </summary>
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;
}
/// <summary>
/// Converts the given world-space transform to an OVRPose in head space.
/// </summary>
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
};
}
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;
}
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;
}
}
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)
};
}
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
}
};
}
public static Color FromColorf(this OVRPlugin.Colorf c)
{
return new Color() { r = c.r, g = c.g, b = c.b, a = c.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 };
}
public static Vector2 FromSizef(this OVRPlugin.Sizef v)
{
return new Vector2() { x = v.w, y = v.h };
}
public static OVRPlugin.Sizef ToSizef(this Vector2 v)
{
return new OVRPlugin.Sizef() { w = v.x, h = v.y };
}
public static Vector2 FromVector2f(this OVRPlugin.Vector2f v)
{
return new Vector2() { x = v.x, y = v.y };
}
public static Vector2 FromFlippedXVector2f(this OVRPlugin.Vector2f v)
{
return new Vector2() { x = -v.x, y = v.y };
}
public static OVRPlugin.Vector2f ToVector2f(this Vector2 v)
{
return new OVRPlugin.Vector2f() { x = v.x, y = v.y };
}
public static Vector3 FromSize3f(this OVRPlugin.Size3f v)
{
return new Vector3() { x = v.w, y = v.h, z = v.d };
}
public static OVRPlugin.Size3f ToSize3f(this Vector3 v)
{
return new OVRPlugin.Size3f() { w = v.x, h = v.y, d = v.z };
}
public static Vector3 FromVector3f(this OVRPlugin.Vector3f v)
{
return new Vector3() { x = v.x, y = v.y, z = v.z };
}
public static Vector3 FromFlippedXVector3f(this OVRPlugin.Vector3f v)
{
return new Vector3() { x = -v.x, y = v.y, z = v.z };
}
public static Vector3 FromFlippedZVector3f(this OVRPlugin.Vector3f v)
{
return new Vector3() { x = v.x, y = v.y, z = -v.z };
}
public static OVRPlugin.Vector3f ToVector3f(this Vector3 v)
{
return new OVRPlugin.Vector3f() { x = v.x, y = v.y, z = v.z };
}
public static OVRPlugin.Vector3f ToFlippedXVector3f(this Vector3 v)
{
return new OVRPlugin.Vector3f() { x = -v.x, y = v.y, z = v.z };
}
public static OVRPlugin.Vector3f ToFlippedZVector3f(this Vector3 v)
{
return new OVRPlugin.Vector3f() { x = v.x, y = v.y, z = -v.z };
}
public static Vector4 FromVector4f(this OVRPlugin.Vector4f v)
{
return new Vector4() { x = v.x, y = v.y, z = v.z, w = v.w };
}
public static OVRPlugin.Vector4f ToVector4f(this Vector4 v)
{
return new OVRPlugin.Vector4f() { x = v.x, y = v.y, z = v.z, w = v.w };
}
public static Quaternion FromQuatf(this OVRPlugin.Quatf q)
{
return new Quaternion() { x = q.x, y = q.y, z = q.z, w = q.w };
}
public static Quaternion FromFlippedXQuatf(this OVRPlugin.Quatf q)
{
return new Quaternion() { x = q.x, y = -q.y, z = -q.z, w = q.w };
}
public static Quaternion FromFlippedZQuatf(this OVRPlugin.Quatf q)
{
return new Quaternion() { x = -q.x, y = -q.y, z = q.z, w = q.w };
}
public static OVRPlugin.Quatf ToQuatf(this Quaternion q)
{
return new OVRPlugin.Quatf() { x = q.x, y = q.y, z = q.z, w = q.w };
}
public static OVRPlugin.Quatf ToFlippedXQuatf(this Quaternion q)
{
return new OVRPlugin.Quatf() { x = q.x, y = -q.y, z = -q.z, w = q.w };
}
public static OVRPlugin.Quatf ToFlippedZQuatf(this Quaternion q)
{
return new OVRPlugin.Quatf() { x = -q.x, y = -q.y, z = q.z, w = q.w };
}
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;
}
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;
}
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;
}
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<NodeState> nodeStateList = new List<NodeState>();
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;
}
}
/// <summary>
/// An affine transformation built from a Unity position and orientation.
/// </summary>
[System.Serializable]
public struct OVRPose
{
/// <summary>
/// A pose with no translation or rotation.
/// </summary>
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);
}
/// <summary>
/// The position.
/// </summary>
public Vector3 position;
/// <summary>
/// The orientation.
/// </summary>
public Quaternion orientation;
/// <summary>
/// Multiplies two poses.
/// </summary>
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;
}
/// <summary>
/// Computes the inverse of the given pose.
/// </summary>
public OVRPose Inverse()
{
OVRPose ret;
ret.orientation = Quaternion.Inverse(orientation);
ret.position = ret.orientation * -position;
return ret;
}
/// <summary>
/// Converts the pose from left- to right-handed or vice-versa.
/// </summary>
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;
}
}
/// <summary>
/// Encapsulates an 8-byte-aligned of unmanaged memory.
/// </summary>
public class OVRNativeBuffer : IDisposable
{
private bool disposed = false;
private int m_numBytes = 0;
private IntPtr m_ptr = IntPtr.Zero;
/// <summary>
/// Creates a buffer of the specified size.
/// </summary>
public OVRNativeBuffer(int numBytes)
{
Reallocate(numBytes);
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the <see cref="OVRNativeBuffer"/> is
/// reclaimed by garbage collection.
/// </summary>
~OVRNativeBuffer()
{
Dispose(false);
}
/// <summary>
/// Reallocates the buffer with the specified new size.
/// </summary>
public void Reset(int numBytes)
{
Reallocate(numBytes);
}
/// <summary>
/// The current number of bytes in the buffer.
/// </summary>
public int GetCapacity()
{
return m_numBytes;
}
/// <summary>
/// A pointer to the unmanaged memory in the buffer, starting at the given offset in bytes.
/// </summary>
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);
}
/// <summary>
/// Releases all resource used by the <see cref="OVRNativeBuffer"/> object.
/// </summary>
/// <remarks>Call <see cref="Dispose"/> when you are finished using the <see cref="OVRNativeBuffer"/>. The <see cref="Dispose"/>
/// method leaves the <see cref="OVRNativeBuffer"/> in an unusable state. After calling <see cref="Dispose"/>, you must
/// release all references to the <see cref="OVRNativeBuffer"/> so the garbage collector can reclaim the memory that
/// the <see cref="OVRNativeBuffer"/> was occupying.</remarks>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
// dispose managed resources
}
// dispose unmanaged resources
Release();
disposed = true;
}
private void Reallocate(int numBytes)
{
Release();
if (numBytes > 0)
{
m_ptr = Marshal.AllocHGlobal(numBytes);
m_numBytes = numBytes;
}
else
{
m_ptr = IntPtr.Zero;
m_numBytes = 0;
}
}
private void Release()
{
if (m_ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(m_ptr);
m_ptr = IntPtr.Zero;
m_numBytes = 0;
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 176f8d665b1d78048b1e87956698df6b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,158 @@
/*
* 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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using Quaternion = UnityEngine.Quaternion;
using Vector3 = UnityEngine.Vector3;
using System.Linq;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
/// <summary>
/// Simple script for running the ControllerDrivenHandPosesSample
/// </summary>
[DisallowMultipleComponent]
[HelpURL("https://developer.oculus.com/reference/unity/latest/class_o_v_r_controller_driven_hand_poses_sample")]
public class OVRControllerDrivenHandPosesSample : MonoBehaviour
{
[SerializeField]
private Button buttonOff;
[SerializeField]
private Button buttonConforming;
[SerializeField]
private Button buttonNatural;
[SerializeField]
private LineRenderer leftLinePointer;
[SerializeField]
private LineRenderer rightLinePointer;
public OVRCameraRig cameraRig;
// Unity event functions
void Awake()
{
switch (OVRManager.instance.controllerDrivenHandPosesType)
{
case OVRManager.ControllerDrivenHandPosesType.None:
SetControllerDrivenHandPosesTypeToNone();
break;
case OVRManager.ControllerDrivenHandPosesType.ConformingToController:
SetControllerDrivenHandPosesTypeToControllerConforming();
break;
case OVRManager.ControllerDrivenHandPosesType.Natural:
SetControllerDrivenHandPosesTypeToNatural();
break;
}
}
void OnDestroy()
{
}
void OnEnable()
{
}
void OnDisable()
{
}
private void Update()
{
UpdateLineRenderer();
}
private void UpdateLineRenderer()
{
leftLinePointer.enabled = false;
rightLinePointer.enabled = false;
UpdateLineRendererForHand(false);
UpdateLineRendererForHand(true);
}
private void UpdateLineRendererForHand(bool isLeft)
{
Transform inputTransform = null;
if (isLeft)
{
if (OVRInput.IsControllerConnected(OVRInput.Controller.LTouch))
{
inputTransform = cameraRig.leftControllerAnchor;
}
}
else
{
if (OVRInput.IsControllerConnected(OVRInput.Controller.RTouch))
{
inputTransform = cameraRig.rightControllerAnchor;
}
}
if (inputTransform == null)
{
return;
}
var inputPosition = inputTransform.position;
LineRenderer linePointer = (isLeft) ? leftLinePointer : rightLinePointer;
var ray = new Ray(inputPosition, inputTransform.rotation * Vector3.forward);
linePointer.enabled = true;
linePointer.SetPosition(0, inputTransform.position + ray.direction * 0.05f);
linePointer.SetPosition(1, inputPosition + ray.direction * 2.5f);
}
public void SetControllerDrivenHandPosesTypeToNone()
{
OVRManager.instance.controllerDrivenHandPosesType = OVRManager.ControllerDrivenHandPosesType.None;
buttonOff.interactable = false;
buttonConforming.interactable = true;
buttonNatural.interactable = true;
}
public void SetControllerDrivenHandPosesTypeToControllerConforming()
{
OVRManager.instance.controllerDrivenHandPosesType = OVRManager.ControllerDrivenHandPosesType.ConformingToController;
buttonOff.interactable = true;
buttonConforming.interactable = false;
buttonNatural.interactable = true;
}
public void SetControllerDrivenHandPosesTypeToNatural()
{
OVRManager.instance.controllerDrivenHandPosesType = OVRManager.ControllerDrivenHandPosesType.Natural;
buttonOff.interactable = true;
buttonConforming.interactable = true;
buttonNatural.interactable = false;
}
}

Some files were not shown because too many files have changed in this diff Show More