Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

374 lines
14 KiB

  1. /************************************************************************************
  2. Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
  3. See SampleFramework license.txt for license terms. Unless required by applicable law
  4. or agreed to in writing, the sample code is provided AS IS WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied. See the license for specific
  6. language governing permissions and limitations under the license.
  7. ************************************************************************************/
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using UnityEngine;
  11. using UnityEngine.UI;
  12. using System;
  13. namespace OculusSampleFramework
  14. {
  15. /// <summary>
  16. /// The rendering methods swappable via radio buttons
  17. /// </summary>
  18. public enum EUiDisplayType
  19. {
  20. EUDT_WorldGeoQuad,
  21. EUDT_OverlayQuad,
  22. EUDT_None,
  23. EUDT_MaxDislayTypes
  24. }
  25. /// <summary>
  26. /// Usage: demonstrate how to use overlay layers for a paneled UI system
  27. /// On Mobile, we support both Cylinder layer and Quad layer
  28. /// Press any button: it will cycle [world geometry Quad]->[overlay layer Quad]->[world geometry cylinder]->[overlay layer cylinder]
  29. /// On PC, only Quad layer is supported
  30. /// Press any button: it will cycle [world geometry Quad]->[overlay layer Quad]
  31. ///
  32. /// You should be able to observe sharper and less aliased image when switch from world geometry to overlay layer.
  33. ///
  34. /// </summary>
  35. public class OVROverlaySample : MonoBehaviour
  36. {
  37. bool inMenu;
  38. /// <summary>
  39. /// The string identifiers for DebugUI radio buttons
  40. /// </summary>
  41. const string ovrOverlayID = "OVROverlayID";
  42. const string applicationID = "ApplicationID";
  43. const string noneID = "NoneID";
  44. /// <summary>
  45. /// Toggle references
  46. /// </summary>
  47. Toggle applicationRadioButton;
  48. Toggle noneRadioButton;
  49. [Header("App vs Compositor Comparison Settings")]
  50. /// <summary>
  51. /// The main camera used to calculate reprojected OVROverlay quad
  52. /// </summary>
  53. public GameObject mainCamera;
  54. /// <summary>
  55. /// The camera used to render UI panels
  56. /// </summary>
  57. public GameObject uiCamera;
  58. /// <summary>
  59. /// The parents of grouped UI panels
  60. /// </summary>
  61. public GameObject uiGeoParent;
  62. public GameObject worldspaceGeoParent;
  63. /// <summary>
  64. /// The OVROverlay component to pass the uiCamera rendered RT to
  65. /// </summary>
  66. public OVROverlay cameraRenderOverlay;
  67. /// <summary>
  68. /// The OVROverlay component displaying which rendering mode is active
  69. /// </summary>
  70. public OVROverlay renderingLabelOverlay;
  71. /// <summary>
  72. /// The quad textures to indicate the active rendering method
  73. /// </summary>
  74. public Texture applicationLabelTexture;
  75. public Texture compositorLabelTexture;
  76. /// <summary>
  77. /// The resources & settings needed for the level loading simulation demo
  78. /// </summary>
  79. [Header("Level Loading Sim Settings")]
  80. public GameObject prefabForLevelLoadSim;
  81. public OVROverlay cubemapOverlay;
  82. public OVROverlay loadingTextQuadOverlay;
  83. public float distanceFromCamToLoadText;
  84. public float cubeSpawnRadius;
  85. public float heightBetweenItems;
  86. public int numObjectsPerLevel;
  87. public int numLevels;
  88. public int numLoopsTrigger = 500000000;
  89. List<GameObject> spawnedCubes = new List<GameObject>();
  90. #region MonoBehaviour handler
  91. void Start()
  92. {
  93. DebugUIBuilder.instance.AddLabel("OVROverlay Sample");
  94. DebugUIBuilder.instance.AddDivider();
  95. DebugUIBuilder.instance.AddLabel("Level Loading Example");
  96. DebugUIBuilder.instance.AddButton("Simulate Level Load", TriggerLoad);
  97. DebugUIBuilder.instance.AddButton("Destroy Cubes", TriggerUnload);
  98. DebugUIBuilder.instance.AddDivider();
  99. DebugUIBuilder.instance.AddLabel("OVROverlay vs. Application Render Comparison");
  100. DebugUIBuilder.instance.AddRadio("OVROverlay", "group", delegate (Toggle t) { RadioPressed(ovrOverlayID, "group", t); }).GetComponentInChildren<Toggle>();
  101. applicationRadioButton = DebugUIBuilder.instance.AddRadio("Application", "group", delegate (Toggle t) { RadioPressed(applicationID, "group", t); }).GetComponentInChildren<Toggle>();
  102. noneRadioButton = DebugUIBuilder.instance.AddRadio("None", "group", delegate (Toggle t) { RadioPressed(noneID, "group", t); }).GetComponentInChildren<Toggle>();
  103. DebugUIBuilder.instance.Show();
  104. // Start with Overlay Quad
  105. CameraAndRenderTargetSetup();
  106. cameraRenderOverlay.enabled = true;
  107. cameraRenderOverlay.currentOverlayShape = OVROverlay.OverlayShape.Quad;
  108. spawnedCubes.Capacity = numObjectsPerLevel * numLevels;
  109. }
  110. void Update()
  111. {
  112. // Switch ui display types
  113. if (OVRInput.GetDown(OVRInput.Button.Two) || OVRInput.GetDown(OVRInput.Button.Start))
  114. {
  115. if (inMenu) DebugUIBuilder.instance.Hide();
  116. else DebugUIBuilder.instance.Show();
  117. inMenu = !inMenu;
  118. }
  119. // Trigger loading simulator via keyboard
  120. if (Input.GetKeyDown(KeyCode.A))
  121. {
  122. TriggerLoad();
  123. }
  124. }
  125. #endregion
  126. #region Private Functions
  127. /// <summary>
  128. /// Usage: Activate the world geometry and deactivate OVROverlay display
  129. /// </summary>
  130. void ActivateWorldGeo()
  131. {
  132. worldspaceGeoParent.SetActive(true);
  133. uiGeoParent.SetActive(false);
  134. uiCamera.SetActive(false);
  135. cameraRenderOverlay.enabled = false;
  136. renderingLabelOverlay.enabled = true;
  137. renderingLabelOverlay.textures[0] = applicationLabelTexture;
  138. Debug.Log("Switched to ActivateWorldGeo");
  139. }
  140. /// <summary>
  141. /// Usage: Activate OVROverlay display and deactivate the world geometry
  142. /// </summary>
  143. void ActivateOVROverlay()
  144. {
  145. worldspaceGeoParent.SetActive(false);
  146. uiCamera.SetActive(true);
  147. cameraRenderOverlay.enabled = true;
  148. uiGeoParent.SetActive(true);
  149. renderingLabelOverlay.enabled = true;
  150. renderingLabelOverlay.textures[0] = compositorLabelTexture;
  151. Debug.Log("Switched to ActivateOVROVerlay");
  152. }
  153. /// <summary>
  154. /// Usage: Deactivate both world geometry and OVROverlay display
  155. /// </summary>
  156. void ActivateNone()
  157. {
  158. worldspaceGeoParent.SetActive(false);
  159. uiCamera.SetActive(false);
  160. cameraRenderOverlay.enabled = false;
  161. uiGeoParent.SetActive(false);
  162. renderingLabelOverlay.enabled = false;
  163. Debug.Log("Switched to ActivateNone");
  164. }
  165. /// <summary>
  166. /// This function is to simulate a level load event in Unity
  167. /// The idea is to enable a cubemap overlay right before any action that will stall the main thread
  168. /// This cubemap overlay can be combined with other OVROverlay objects, such as animated textures to indicate "Loading..."
  169. /// </summary>
  170. void TriggerLoad()
  171. {
  172. StartCoroutine(WaitforOVROverlay());
  173. }
  174. IEnumerator WaitforOVROverlay()
  175. {
  176. Transform camTransform = mainCamera.transform;
  177. Transform uiTextOverlayTrasnform = loadingTextQuadOverlay.transform;
  178. Vector3 newPos = camTransform.position + camTransform.forward * distanceFromCamToLoadText;
  179. newPos.y = camTransform.position.y;
  180. uiTextOverlayTrasnform.position = newPos;
  181. cubemapOverlay.enabled = true;
  182. loadingTextQuadOverlay.enabled = true;
  183. noneRadioButton.isOn = true;
  184. yield return new WaitForSeconds(0.1f);
  185. ClearObjects();
  186. SimulateLevelLoad();
  187. cubemapOverlay.enabled = false;
  188. loadingTextQuadOverlay.enabled = false;
  189. yield return null;
  190. }
  191. /// <summary>
  192. /// Usage: Destroy all loaded resources and switch back to world geometry rendering mode.
  193. /// </summary>
  194. void TriggerUnload()
  195. {
  196. ClearObjects();
  197. applicationRadioButton.isOn = true;
  198. }
  199. /// <summary>
  200. /// Usage: Recreate UI render target according overlay type and overlay size
  201. /// </summary>
  202. void CameraAndRenderTargetSetup()
  203. {
  204. float overlayWidth = cameraRenderOverlay.transform.localScale.x;
  205. float overlayHeight = cameraRenderOverlay.transform.localScale.y;
  206. float overlayRadius = cameraRenderOverlay.transform.localScale.z;
  207. #if UNITY_ANDROID
  208. // Gear VR display panel resolution
  209. float hmdPanelResWidth = 2560;
  210. float hmdPanelResHeight = 1440;
  211. #else
  212. // Rift display panel resolution
  213. float hmdPanelResWidth = 2160;
  214. float hmdPanelResHeight = 1200;
  215. #endif
  216. float singleEyeScreenPhysicalResX = hmdPanelResWidth * 0.5f;
  217. float singleEyeScreenPhysicalResY = hmdPanelResHeight;
  218. // Calculate RT Height
  219. // screenSizeYInWorld : how much world unity the full screen can cover at overlayQuad's location vertically
  220. // pixelDensityY: pixels / world unit ( meter )
  221. float halfFovY = mainCamera.GetComponent<Camera>().fieldOfView / 2;
  222. float screenSizeYInWorld = 2 * overlayRadius * Mathf.Tan(Mathf.Deg2Rad * halfFovY);
  223. float pixelDensityYPerWorldUnit = singleEyeScreenPhysicalResY / screenSizeYInWorld;
  224. float renderTargetHeight = pixelDensityYPerWorldUnit * overlayWidth;
  225. // Calculate RT width
  226. float renderTargetWidth = 0.0f;
  227. // screenSizeXInWorld : how much world unity the full screen can cover at overlayQuad's location horizontally
  228. // pixelDensityY: pixels / world unit ( meter )
  229. float screenSizeXInWorld = screenSizeYInWorld * mainCamera.GetComponent<Camera>().aspect;
  230. float pixelDensityXPerWorldUnit = singleEyeScreenPhysicalResX / screenSizeXInWorld;
  231. renderTargetWidth = pixelDensityXPerWorldUnit * overlayWidth;
  232. // Compute the orthographic size for the camera
  233. float orthographicSize = overlayHeight / 2.0f;
  234. float orthoCameraAspect = overlayWidth / overlayHeight;
  235. uiCamera.GetComponent<Camera>().orthographicSize = orthographicSize;
  236. uiCamera.GetComponent<Camera>().aspect = orthoCameraAspect;
  237. if (uiCamera.GetComponent<Camera>().targetTexture != null)
  238. uiCamera.GetComponent<Camera>().targetTexture.Release();
  239. RenderTexture overlayRT = new RenderTexture(
  240. (int)renderTargetWidth * 2,
  241. (int)renderTargetHeight * 2,
  242. 0,
  243. RenderTextureFormat.ARGB32,
  244. RenderTextureReadWrite.sRGB);
  245. Debug.Log("Created RT of resolution w: " + renderTargetWidth + " and h: " + renderTargetHeight);
  246. overlayRT.hideFlags = HideFlags.DontSave;
  247. overlayRT.useMipMap = true;
  248. overlayRT.filterMode = FilterMode.Trilinear;
  249. overlayRT.anisoLevel = 4;
  250. #if UNITY_5_5_OR_NEWER
  251. overlayRT.autoGenerateMips = true;
  252. #else
  253. overlayRT.generateMips = true;
  254. #endif
  255. uiCamera.GetComponent<Camera>().targetTexture = overlayRT;
  256. cameraRenderOverlay.textures[0] = overlayRT;
  257. }
  258. /// <summary>
  259. /// Usage: block main thread with an empty for loop and generate a bunch of cubes around the player.
  260. /// </summary>
  261. void SimulateLevelLoad()
  262. {
  263. int numToPrint = 0;
  264. for (int p = 0; p < numLoopsTrigger; p++)
  265. {
  266. numToPrint++;
  267. }
  268. Debug.Log("Finished " + numToPrint + " Loops");
  269. Vector3 playerPos = mainCamera.transform.position;
  270. playerPos.y = 0.5f;
  271. // Generate a bunch of blocks, "blocking" the mainthread ;)
  272. for (int j = 0; j < numLevels; j++)
  273. {
  274. for (var i = 0; i < numObjectsPerLevel; i++)
  275. {
  276. var angle = i * Mathf.PI * 2 / numObjectsPerLevel;
  277. float stagger = (i % 2 == 0) ? 1.5f : 1.0f;
  278. var pos = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * cubeSpawnRadius * stagger;
  279. pos.y = j * heightBetweenItems;
  280. var newInst = Instantiate(prefabForLevelLoadSim, pos + playerPos, Quaternion.identity);
  281. var newObjTransform = newInst.transform;
  282. newObjTransform.LookAt(playerPos);
  283. Vector3 newAngle = newObjTransform.rotation.eulerAngles;
  284. newAngle.x = 0.0f;
  285. newObjTransform.rotation = Quaternion.Euler(newAngle);
  286. spawnedCubes.Add(newInst);
  287. }
  288. }
  289. }
  290. /// <summary>
  291. /// Usage: destroy all created cubes and garbage collect.
  292. /// </summary>
  293. void ClearObjects()
  294. {
  295. for (int i = 0; i < spawnedCubes.Count; i++)
  296. {
  297. DestroyImmediate(spawnedCubes[i]);
  298. }
  299. spawnedCubes.Clear();
  300. GC.Collect();
  301. }
  302. #endregion
  303. #region Debug UI Handlers
  304. /// <summary>
  305. /// Usage: radio button handler.
  306. /// </summary>
  307. public void RadioPressed(string radioLabel, string group, Toggle t)
  308. {
  309. if (string.Compare(radioLabel, ovrOverlayID) == 0)
  310. {
  311. ActivateOVROverlay();
  312. }
  313. else if (string.Compare(radioLabel, applicationID) == 0)
  314. {
  315. ActivateWorldGeo();
  316. }
  317. else if (string.Compare(radioLabel, noneID) == 0)
  318. {
  319. ActivateNone();
  320. }
  321. }
  322. #endregion
  323. }
  324. }