/************************************************************************************ 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. ************************************************************************************/ #if UNITY_ANDROID && !UNITY_EDITOR #define OVR_ANDROID_MRC #endif using System.Collections; using System.Collections.Generic; using UnityEngine; public class OVRSpectatorModeDomeTest : MonoBehaviour { bool inited = false; public Camera defaultExternalCamera; OVRPlugin.Fovf defaultFov; public Transform SpectatorAnchor; public Transform Head; #if OVR_ANDROID_MRC private OVRPlugin.Media.PlatformCameraMode camMode = OVRPlugin.Media.PlatformCameraMode.Disabled; private bool readyToSwitch = false; private Transform SpectatorCamera; // Dome sphere representation private float distance = 0.8f; private float elevation = 0.0f; private float polar = 90.0f; private const float distance_near = 0.5f; private const float distance_far = 1.2f; private const float elevationLimit = 30.0f; #endif // Start is called before the first frame update void Awake() { #if OVR_ANDROID_MRC OVRPlugin.Media.SetPlatformInitialized(); SpectatorCamera = defaultExternalCamera.transform.parent; #endif } // Use this for initialization void Start () { #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC if (!defaultExternalCamera) { Debug.LogWarning("defaultExternalCamera undefined"); } #if !OVR_ANDROID_MRC // On Quest, we enable MRC automatically through the configuration if (!OVRManager.instance.enableMixedReality) { OVRManager.instance.enableMixedReality = true; } #endif #endif } void Initialize() { #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC if (inited) return; #if OVR_ANDROID_MRC if (!OVRPlugin.Media.GetInitialized()) return; #else if (!OVRPlugin.IsMixedRealityInitialized()) return; #endif OVRPlugin.ResetDefaultExternalCamera(); Debug.LogFormat("GetExternalCameraCount before adding manual external camera {0}", OVRPlugin.GetExternalCameraCount()); UpdateDefaultExternalCamera(); Debug.LogFormat("GetExternalCameraCount after adding manual external camera {0}", OVRPlugin.GetExternalCameraCount()); // obtain default FOV { OVRPlugin.CameraIntrinsics cameraIntrinsics; OVRPlugin.CameraExtrinsics cameraExtrinsics; OVRPlugin.GetMixedRealityCameraInfo(0, out cameraExtrinsics, out cameraIntrinsics); defaultFov = cameraIntrinsics.FOVPort; } inited = true; #if OVR_ANDROID_MRC readyToSwitch = true; #endif #endif } void UpdateDefaultExternalCamera() { #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC // always build a 1080p external camera const int cameraPixelWidth = 1920; const int cameraPixelHeight = 1080; const float cameraAspect = (float)cameraPixelWidth / cameraPixelHeight; string cameraName = "UnityExternalCamera"; OVRPlugin.CameraIntrinsics cameraIntrinsics = new OVRPlugin.CameraIntrinsics(); OVRPlugin.CameraExtrinsics cameraExtrinsics = new OVRPlugin.CameraExtrinsics(); // intrinsics cameraIntrinsics.IsValid = OVRPlugin.Bool.True; cameraIntrinsics.LastChangedTimeSeconds = Time.time; float vFov = defaultExternalCamera.fieldOfView * Mathf.Deg2Rad; float hFov = Mathf.Atan(Mathf.Tan(vFov * 0.5f) * cameraAspect) * 2.0f; OVRPlugin.Fovf fov = new OVRPlugin.Fovf(); fov.UpTan = fov.DownTan = Mathf.Tan(vFov * 0.5f); fov.LeftTan = fov.RightTan = Mathf.Tan(hFov * 0.5f); cameraIntrinsics.FOVPort = fov; cameraIntrinsics.VirtualNearPlaneDistanceMeters = defaultExternalCamera.nearClipPlane; cameraIntrinsics.VirtualFarPlaneDistanceMeters = defaultExternalCamera.farClipPlane; cameraIntrinsics.ImageSensorPixelResolution.w = cameraPixelWidth; cameraIntrinsics.ImageSensorPixelResolution.h = cameraPixelHeight; // extrinsics cameraExtrinsics.IsValid = OVRPlugin.Bool.True; cameraExtrinsics.LastChangedTimeSeconds = Time.time; cameraExtrinsics.CameraStatusData = OVRPlugin.CameraStatus.CameraStatus_Calibrated; cameraExtrinsics.AttachedToNode = OVRPlugin.Node.None; Camera mainCamera = Camera.main; OVRCameraRig cameraRig = mainCamera.GetComponentInParent(); if (cameraRig) { Transform trackingSpace = cameraRig.trackingSpace; OVRPose trackingSpacePose = trackingSpace.ToOVRPose(false); OVRPose cameraPose = defaultExternalCamera.transform.ToOVRPose(false); OVRPose relativePose = trackingSpacePose.Inverse() * cameraPose; #if OVR_ANDROID_MRC OVRPose stageToLocalPose = OVRPlugin.GetTrackingTransformRelativePose(OVRPlugin.TrackingOrigin.Stage).ToOVRPose(); OVRPose localToStagePose = stageToLocalPose.Inverse(); relativePose = localToStagePose * relativePose; #endif cameraExtrinsics.RelativePose = relativePose.ToPosef(); } else { cameraExtrinsics.RelativePose = OVRPlugin.Posef.identity; } if (!OVRPlugin.SetDefaultExternalCamera(cameraName, ref cameraIntrinsics, ref cameraExtrinsics)) { Debug.LogError("SetDefaultExternalCamera() failed"); } #endif } private void UpdateSpectatorCameraStatus() { #if OVR_ANDROID_MRC // Trigger to switch between 1st person and spectator mode during casting to phone if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger) || OVRInput.GetDown(OVRInput.Button.SecondaryIndexTrigger)) { camMode = OVRPlugin.Media.GetPlatformCameraMode(); if (camMode == OVRPlugin.Media.PlatformCameraMode.Disabled && readyToSwitch) { OVRPlugin.Media.SetMrcFrameImageFlipped(false); OVRPlugin.Media.SetPlatformCameraMode(OVRPlugin.Media.PlatformCameraMode.Initialized); StartCoroutine(TimerCoroutine()); } if (camMode == OVRPlugin.Media.PlatformCameraMode.Initialized && readyToSwitch) { OVRPlugin.Media.SetMrcFrameImageFlipped(true); OVRPlugin.Media.SetPlatformCameraMode(OVRPlugin.Media.PlatformCameraMode.Disabled); StartCoroutine(TimerCoroutine()); } } // Keep spectator camera on dome surface Vector2 axis = OVRInput.Get(OVRInput.Axis2D.SecondaryThumbstick); if (Mathf.Abs(axis.x) > 0.2f) { polar = polar - axis.x * 0.5f; } if (Mathf.Abs(axis.y) > 0.2f) { elevation = elevation + axis.y * 0.5f; if (elevation < -90.0f + elevationLimit) elevation = -90.0f + elevationLimit; if (elevation > 90.0f) elevation = 90.0f; } axis = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick); if (Mathf.Abs(axis.y) > 0.1f) { distance = axis.y * 0.05f + distance; if (distance > distance_far) distance = distance_far; if (distance < distance_near) distance = distance_near; } SpectatorCamera.position = SpectatorCameraDomePosition(SpectatorAnchor.position, distance, elevation, polar); SpectatorCamera.rotation = Quaternion.LookRotation(SpectatorCamera.position - SpectatorAnchor.position); Head.position = SpectatorAnchor.position; Head.rotation = SpectatorAnchor.rotation; #endif } Vector3 SpectatorCameraDomePosition(Vector3 spectatorAnchorPosition, float d, float e, float p) { float x = d * Mathf.Cos(Mathf.Deg2Rad * e) * Mathf.Cos(Mathf.Deg2Rad * p); float y = d * Mathf.Sin(Mathf.Deg2Rad * e); float z = d * Mathf.Cos(Mathf.Deg2Rad * e) * Mathf.Sin(Mathf.Deg2Rad * p); return new Vector3(x + spectatorAnchorPosition.x, y + spectatorAnchorPosition.y, z + spectatorAnchorPosition.z); } IEnumerator TimerCoroutine() { #if OVR_ANDROID_MRC readyToSwitch = false; #endif yield return new WaitForSeconds(2); #if OVR_ANDROID_MRC readyToSwitch = true; #endif } // Update is called once per frame void Update () { #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC if (!inited) { Initialize(); return; } if (!defaultExternalCamera) { return; } #if OVR_ANDROID_MRC if (!OVRPlugin.Media.GetInitialized()) { return; } #else if (!OVRPlugin.IsMixedRealityInitialized()) { return; } #endif UpdateSpectatorCameraStatus(); UpdateDefaultExternalCamera(); OVRPlugin.OverrideExternalCameraFov(0, false, new OVRPlugin.Fovf()); OVRPlugin.OverrideExternalCameraStaticPose(0, false, OVRPlugin.Posef.identity); #endif } void OnApplicationPause() { #if OVR_ANDROID_MRC OVRPlugin.Media.SetMrcFrameImageFlipped(true); OVRPlugin.Media.SetPlatformCameraMode(OVRPlugin.Media.PlatformCameraMode.Disabled); #endif } void OnApplicationQuit() { #if OVR_ANDROID_MRC OVRPlugin.Media.SetMrcFrameImageFlipped(true); OVRPlugin.Media.SetPlatformCameraMode(OVRPlugin.Media.PlatformCameraMode.Disabled); #endif } }