|
|
- /************************************************************************************
-
- Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
-
- See SampleFramework license.txt for license terms. Unless required by applicable law
- or agreed to in writing, the sample code is provided “AS IS” WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the license for specific
- language governing permissions and limitations under the license.
-
- ************************************************************************************/
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
- using System;
-
- namespace OculusSampleFramework
- {
-
- /// <summary>
- /// The rendering methods swappable via radio buttons
- /// </summary>
- public enum EUiDisplayType
- {
- EUDT_WorldGeoQuad,
- EUDT_OverlayQuad,
- EUDT_None,
- EUDT_MaxDislayTypes
- }
-
- /// <summary>
- /// Usage: demonstrate how to use overlay layers for a paneled UI system
- /// On Mobile, we support both Cylinder layer and Quad layer
- /// Press any button: it will cycle [world geometry Quad]->[overlay layer Quad]->[world geometry cylinder]->[overlay layer cylinder]
- /// On PC, only Quad layer is supported
- /// Press any button: it will cycle [world geometry Quad]->[overlay layer Quad]
- ///
- /// You should be able to observe sharper and less aliased image when switch from world geometry to overlay layer.
- ///
- /// </summary>
- public class OVROverlaySample : MonoBehaviour
- {
-
- bool inMenu;
-
- /// <summary>
- /// The string identifiers for DebugUI radio buttons
- /// </summary>
- const string ovrOverlayID = "OVROverlayID";
- const string applicationID = "ApplicationID";
- const string noneID = "NoneID";
-
- /// <summary>
- /// Toggle references
- /// </summary>
- Toggle applicationRadioButton;
- Toggle noneRadioButton;
-
- [Header("App vs Compositor Comparison Settings")]
- /// <summary>
- /// The main camera used to calculate reprojected OVROverlay quad
- /// </summary>
- public GameObject mainCamera;
-
- /// <summary>
- /// The camera used to render UI panels
- /// </summary>
- public GameObject uiCamera;
-
- /// <summary>
- /// The parents of grouped UI panels
- /// </summary>
- public GameObject uiGeoParent;
- public GameObject worldspaceGeoParent;
-
- /// <summary>
- /// The OVROverlay component to pass the uiCamera rendered RT to
- /// </summary>
- public OVROverlay cameraRenderOverlay;
-
- /// <summary>
- /// The OVROverlay component displaying which rendering mode is active
- /// </summary>
- public OVROverlay renderingLabelOverlay;
-
- /// <summary>
- /// The quad textures to indicate the active rendering method
- /// </summary>
- public Texture applicationLabelTexture;
- public Texture compositorLabelTexture;
-
- /// <summary>
- /// The resources & settings needed for the level loading simulation demo
- /// </summary>
- [Header("Level Loading Sim Settings")]
- public GameObject prefabForLevelLoadSim;
- public OVROverlay cubemapOverlay;
- public OVROverlay loadingTextQuadOverlay;
- public float distanceFromCamToLoadText;
- public float cubeSpawnRadius;
- public float heightBetweenItems;
- public int numObjectsPerLevel;
- public int numLevels;
- public int numLoopsTrigger = 500000000;
- List<GameObject> spawnedCubes = new List<GameObject>();
-
- #region MonoBehaviour handler
-
- void Start()
- {
- DebugUIBuilder.instance.AddLabel("OVROverlay Sample");
- DebugUIBuilder.instance.AddDivider();
- DebugUIBuilder.instance.AddLabel("Level Loading Example");
- DebugUIBuilder.instance.AddButton("Simulate Level Load", TriggerLoad);
- DebugUIBuilder.instance.AddButton("Destroy Cubes", TriggerUnload);
- DebugUIBuilder.instance.AddDivider();
- DebugUIBuilder.instance.AddLabel("OVROverlay vs. Application Render Comparison");
- DebugUIBuilder.instance.AddRadio("OVROverlay", "group", delegate (Toggle t) { RadioPressed(ovrOverlayID, "group", t); }).GetComponentInChildren<Toggle>();
- applicationRadioButton = DebugUIBuilder.instance.AddRadio("Application", "group", delegate (Toggle t) { RadioPressed(applicationID, "group", t); }).GetComponentInChildren<Toggle>();
- noneRadioButton = DebugUIBuilder.instance.AddRadio("None", "group", delegate (Toggle t) { RadioPressed(noneID, "group", t); }).GetComponentInChildren<Toggle>();
-
- DebugUIBuilder.instance.Show();
-
- // Start with Overlay Quad
- CameraAndRenderTargetSetup();
- cameraRenderOverlay.enabled = true;
- cameraRenderOverlay.currentOverlayShape = OVROverlay.OverlayShape.Quad;
- spawnedCubes.Capacity = numObjectsPerLevel * numLevels;
- }
-
- void Update()
- {
- // Switch ui display types
- if (OVRInput.GetDown(OVRInput.Button.Two) || OVRInput.GetDown(OVRInput.Button.Start))
- {
- if (inMenu) DebugUIBuilder.instance.Hide();
- else DebugUIBuilder.instance.Show();
- inMenu = !inMenu;
- }
-
- // Trigger loading simulator via keyboard
- if (Input.GetKeyDown(KeyCode.A))
- {
- TriggerLoad();
- }
- }
- #endregion
-
- #region Private Functions
-
- /// <summary>
- /// Usage: Activate the world geometry and deactivate OVROverlay display
- /// </summary>
- void ActivateWorldGeo()
- {
- worldspaceGeoParent.SetActive(true);
- uiGeoParent.SetActive(false);
- uiCamera.SetActive(false);
- cameraRenderOverlay.enabled = false;
- renderingLabelOverlay.enabled = true;
- renderingLabelOverlay.textures[0] = applicationLabelTexture;
- Debug.Log("Switched to ActivateWorldGeo");
- }
-
- /// <summary>
- /// Usage: Activate OVROverlay display and deactivate the world geometry
- /// </summary>
- void ActivateOVROverlay()
- {
- worldspaceGeoParent.SetActive(false);
- uiCamera.SetActive(true);
- cameraRenderOverlay.enabled = true;
- uiGeoParent.SetActive(true);
- renderingLabelOverlay.enabled = true;
- renderingLabelOverlay.textures[0] = compositorLabelTexture;
- Debug.Log("Switched to ActivateOVROVerlay");
- }
-
- /// <summary>
- /// Usage: Deactivate both world geometry and OVROverlay display
- /// </summary>
- void ActivateNone()
- {
- worldspaceGeoParent.SetActive(false);
- uiCamera.SetActive(false);
- cameraRenderOverlay.enabled = false;
- uiGeoParent.SetActive(false);
- renderingLabelOverlay.enabled = false;
- Debug.Log("Switched to ActivateNone");
- }
-
-
- /// <summary>
- /// This function is to simulate a level load event in Unity
- /// The idea is to enable a cubemap overlay right before any action that will stall the main thread
- /// This cubemap overlay can be combined with other OVROverlay objects, such as animated textures to indicate "Loading..."
- /// </summary>
- void TriggerLoad()
- {
- StartCoroutine(WaitforOVROverlay());
- }
-
- IEnumerator WaitforOVROverlay()
- {
- Transform camTransform = mainCamera.transform;
- Transform uiTextOverlayTrasnform = loadingTextQuadOverlay.transform;
- Vector3 newPos = camTransform.position + camTransform.forward * distanceFromCamToLoadText;
- newPos.y = camTransform.position.y;
- uiTextOverlayTrasnform.position = newPos;
- cubemapOverlay.enabled = true;
- loadingTextQuadOverlay.enabled = true;
- noneRadioButton.isOn = true;
- yield return new WaitForSeconds(0.1f);
- ClearObjects();
- SimulateLevelLoad();
- cubemapOverlay.enabled = false;
- loadingTextQuadOverlay.enabled = false;
- yield return null;
- }
-
-
- /// <summary>
- /// Usage: Destroy all loaded resources and switch back to world geometry rendering mode.
- /// </summary>
- void TriggerUnload()
- {
- ClearObjects();
- applicationRadioButton.isOn = true;
- }
-
- /// <summary>
- /// Usage: Recreate UI render target according overlay type and overlay size
- /// </summary>
- void CameraAndRenderTargetSetup()
- {
- float overlayWidth = cameraRenderOverlay.transform.localScale.x;
- float overlayHeight = cameraRenderOverlay.transform.localScale.y;
- float overlayRadius = cameraRenderOverlay.transform.localScale.z;
-
- #if UNITY_ANDROID
- // Gear VR display panel resolution
- float hmdPanelResWidth = 2560;
- float hmdPanelResHeight = 1440;
- #else
- // Rift display panel resolution
- float hmdPanelResWidth = 2160;
- float hmdPanelResHeight = 1200;
- #endif
-
- float singleEyeScreenPhysicalResX = hmdPanelResWidth * 0.5f;
- float singleEyeScreenPhysicalResY = hmdPanelResHeight;
-
- // Calculate RT Height
- // screenSizeYInWorld : how much world unity the full screen can cover at overlayQuad's location vertically
- // pixelDensityY: pixels / world unit ( meter )
-
- float halfFovY = mainCamera.GetComponent<Camera>().fieldOfView / 2;
- float screenSizeYInWorld = 2 * overlayRadius * Mathf.Tan(Mathf.Deg2Rad * halfFovY);
- float pixelDensityYPerWorldUnit = singleEyeScreenPhysicalResY / screenSizeYInWorld;
- float renderTargetHeight = pixelDensityYPerWorldUnit * overlayWidth;
-
- // Calculate RT width
- float renderTargetWidth = 0.0f;
-
- // screenSizeXInWorld : how much world unity the full screen can cover at overlayQuad's location horizontally
- // pixelDensityY: pixels / world unit ( meter )
-
- float screenSizeXInWorld = screenSizeYInWorld * mainCamera.GetComponent<Camera>().aspect;
- float pixelDensityXPerWorldUnit = singleEyeScreenPhysicalResX / screenSizeXInWorld;
- renderTargetWidth = pixelDensityXPerWorldUnit * overlayWidth;
-
- // Compute the orthographic size for the camera
- float orthographicSize = overlayHeight / 2.0f;
- float orthoCameraAspect = overlayWidth / overlayHeight;
- uiCamera.GetComponent<Camera>().orthographicSize = orthographicSize;
- uiCamera.GetComponent<Camera>().aspect = orthoCameraAspect;
-
- if (uiCamera.GetComponent<Camera>().targetTexture != null)
- uiCamera.GetComponent<Camera>().targetTexture.Release();
-
- RenderTexture overlayRT = new RenderTexture(
- (int)renderTargetWidth * 2,
- (int)renderTargetHeight * 2,
- 0,
- RenderTextureFormat.ARGB32,
- RenderTextureReadWrite.sRGB);
- Debug.Log("Created RT of resolution w: " + renderTargetWidth + " and h: " + renderTargetHeight);
-
- overlayRT.hideFlags = HideFlags.DontSave;
- overlayRT.useMipMap = true;
- overlayRT.filterMode = FilterMode.Trilinear;
- overlayRT.anisoLevel = 4;
- #if UNITY_5_5_OR_NEWER
- overlayRT.autoGenerateMips = true;
- #else
- overlayRT.generateMips = true;
- #endif
- uiCamera.GetComponent<Camera>().targetTexture = overlayRT;
-
- cameraRenderOverlay.textures[0] = overlayRT;
- }
-
-
- /// <summary>
- /// Usage: block main thread with an empty for loop and generate a bunch of cubes around the player.
- /// </summary>
- void SimulateLevelLoad()
- {
- int numToPrint = 0;
- for (int p = 0; p < numLoopsTrigger; p++)
- {
- numToPrint++;
- }
- Debug.Log("Finished " + numToPrint + " Loops");
- Vector3 playerPos = mainCamera.transform.position;
- playerPos.y = 0.5f;
- // Generate a bunch of blocks, "blocking" the mainthread ;)
- for (int j = 0; j < numLevels; j++)
- {
- for (var i = 0; i < numObjectsPerLevel; i++)
- {
- var angle = i * Mathf.PI * 2 / numObjectsPerLevel;
- float stagger = (i % 2 == 0) ? 1.5f : 1.0f;
- var pos = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * cubeSpawnRadius * stagger;
- pos.y = j * heightBetweenItems;
- var newInst = Instantiate(prefabForLevelLoadSim, pos + playerPos, Quaternion.identity);
- var newObjTransform = newInst.transform;
- newObjTransform.LookAt(playerPos);
- Vector3 newAngle = newObjTransform.rotation.eulerAngles;
- newAngle.x = 0.0f;
- newObjTransform.rotation = Quaternion.Euler(newAngle);
- spawnedCubes.Add(newInst);
- }
- }
- }
-
-
- /// <summary>
- /// Usage: destroy all created cubes and garbage collect.
- /// </summary>
- void ClearObjects()
- {
- for (int i = 0; i < spawnedCubes.Count; i++)
- {
- DestroyImmediate(spawnedCubes[i]);
- }
- spawnedCubes.Clear();
- GC.Collect();
- }
- #endregion
-
- #region Debug UI Handlers
-
- /// <summary>
- /// Usage: radio button handler.
- /// </summary>
- public void RadioPressed(string radioLabel, string group, Toggle t)
- {
- if (string.Compare(radioLabel, ovrOverlayID) == 0)
- {
- ActivateOVROverlay();
- }
- else if (string.Compare(radioLabel, applicationID) == 0)
- {
- ActivateWorldGeo();
- }
- else if (string.Compare(radioLabel, noneID) == 0)
- {
- ActivateNone();
- }
- }
- #endregion
- }
- }
|