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.

265 lines
7.1 KiB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using UnityEngine;
  6. using UnityEngine.SceneManagement;
  7. using UnityEngine.UI;
  8. public class OVRSceneLoader : MonoBehaviour
  9. {
  10. public const string externalStoragePath = "/sdcard/Android/data";
  11. public const string sceneLoadDataName = "SceneLoadData.txt";
  12. public const string resourceBundleName = "asset_resources";
  13. public float sceneCheckIntervalSeconds = 1f;
  14. public float logCloseTime = 5.0f;
  15. public Canvas mainCanvas;
  16. public Text logTextBox;
  17. private AsyncOperation loadSceneOperation;
  18. private string formattedLogText;
  19. private float closeLogTimer;
  20. private bool closeLogDialogue;
  21. private bool canvasPosUpdated;
  22. private struct SceneInfo
  23. {
  24. public List<string> scenes;
  25. public long version;
  26. public SceneInfo(List<string> sceneList, long currentSceneEpochVersion)
  27. {
  28. scenes = sceneList;
  29. version = currentSceneEpochVersion;
  30. }
  31. }
  32. private string scenePath = "";
  33. private string sceneLoadDataPath = "";
  34. private List<AssetBundle> loadedAssetBundles = new List<AssetBundle>();
  35. private SceneInfo currentSceneInfo;
  36. private void Awake()
  37. {
  38. // Make it presist across scene to continue checking for changes
  39. DontDestroyOnLoad(this.gameObject);
  40. }
  41. void Start()
  42. {
  43. string applicationPath = Path.Combine(externalStoragePath, Application.identifier);
  44. scenePath = Path.Combine(applicationPath, "cache/scenes");
  45. sceneLoadDataPath = Path.Combine(scenePath, sceneLoadDataName);
  46. closeLogDialogue = false;
  47. StartCoroutine(DelayCanvasPosUpdate());
  48. currentSceneInfo = GetSceneInfo();
  49. // Check valid scene info has been fetched, and load the scenes
  50. if (currentSceneInfo.version != 0 && !string.IsNullOrEmpty(currentSceneInfo.scenes[0]))
  51. {
  52. LoadScene(currentSceneInfo);
  53. }
  54. }
  55. private void LoadScene(SceneInfo sceneInfo)
  56. {
  57. AssetBundle mainSceneBundle = null;
  58. Debug.Log("[OVRSceneLoader] Loading main scene: " + sceneInfo.scenes[0] + " with version " + sceneInfo.version.ToString());
  59. logTextBox.text += "Target Scene: " + sceneInfo.scenes[0] + "\n";
  60. logTextBox.text += "Version: " + sceneInfo.version.ToString() + "\n";
  61. // Load main scene and dependent additive scenes (if any)
  62. Debug.Log("[OVRSceneLoader] Loading scene bundle files.");
  63. // Fetch all files under scene cache path, excluding unnecessary files such as scene metadata file
  64. string[] bundles = Directory.GetFiles(scenePath, "*_*");
  65. logTextBox.text += "Loading " + bundles.Length + " bundle(s) . . . ";
  66. string mainSceneBundleFileName = "scene_" + sceneInfo.scenes[0].ToLower();
  67. try
  68. {
  69. foreach (string b in bundles)
  70. {
  71. var assetBundle = AssetBundle.LoadFromFile(b);
  72. if (assetBundle != null)
  73. {
  74. Debug.Log("[OVRSceneLoader] Loading file bundle: " + assetBundle.name == null ? "null" : assetBundle.name);
  75. loadedAssetBundles.Add(assetBundle);
  76. }
  77. else
  78. {
  79. Debug.LogError("[OVRSceneLoader] Loading file bundle failed");
  80. }
  81. if (assetBundle.name == mainSceneBundleFileName)
  82. {
  83. mainSceneBundle = assetBundle;
  84. }
  85. if (assetBundle.name == resourceBundleName)
  86. {
  87. OVRResources.SetResourceBundle(assetBundle);
  88. }
  89. }
  90. }
  91. catch(Exception e)
  92. {
  93. logTextBox.text += "<color=red>" + e.Message + "</color>";
  94. return;
  95. }
  96. logTextBox.text += "<color=green>DONE\n</color>";
  97. if (mainSceneBundle != null)
  98. {
  99. logTextBox.text += "Loading Scene: {0:P0}\n";
  100. formattedLogText = logTextBox.text;
  101. string[] scenePaths = mainSceneBundle.GetAllScenePaths();
  102. string sceneName = Path.GetFileNameWithoutExtension(scenePaths[0]);
  103. loadSceneOperation = SceneManager.LoadSceneAsync(sceneName);
  104. loadSceneOperation.completed += LoadSceneOperation_completed;
  105. }
  106. else
  107. {
  108. logTextBox.text += "<color=red>Failed to get main scene bundle.\n</color>";
  109. }
  110. }
  111. private void LoadSceneOperation_completed(AsyncOperation obj)
  112. {
  113. StartCoroutine(onCheckSceneCoroutine());
  114. StartCoroutine(DelayCanvasPosUpdate());
  115. closeLogTimer = 0;
  116. closeLogDialogue = true;
  117. logTextBox.text += "Log closing in {0} seconds.\n";
  118. formattedLogText = logTextBox.text;
  119. }
  120. public void Update()
  121. {
  122. // Display scene load percentage
  123. if (loadSceneOperation != null)
  124. {
  125. if (!loadSceneOperation.isDone)
  126. {
  127. logTextBox.text = string.Format(formattedLogText, loadSceneOperation.progress + 0.1f);
  128. if (loadSceneOperation.progress >= 0.9f)
  129. {
  130. logTextBox.text = formattedLogText.Replace("{0:P0}", "<color=green>DONE</color>");
  131. logTextBox.text += "Transitioning to new scene.\nLoad times will vary depending on scene complexity.\n";
  132. }
  133. }
  134. }
  135. UpdateCanvasPosition();
  136. // Wait a certain time before closing the log dialogue after the scene has transitioned
  137. if (closeLogDialogue)
  138. {
  139. if (closeLogTimer < logCloseTime)
  140. {
  141. closeLogTimer += Time.deltaTime;
  142. logTextBox.text = string.Format(formattedLogText, (int)(logCloseTime - closeLogTimer));
  143. }
  144. else
  145. {
  146. mainCanvas.gameObject.SetActive(false);
  147. closeLogDialogue = false;
  148. }
  149. }
  150. }
  151. private void UpdateCanvasPosition()
  152. {
  153. // Update canvas camera reference and position if the main camera has changed
  154. if (mainCanvas.worldCamera != Camera.main)
  155. {
  156. mainCanvas.worldCamera = Camera.main;
  157. if (Camera.main != null)
  158. {
  159. Vector3 newPosition = Camera.main.transform.position + Camera.main.transform.forward * 0.3f;
  160. gameObject.transform.position = newPosition;
  161. gameObject.transform.rotation = Camera.main.transform.rotation;
  162. }
  163. }
  164. }
  165. private SceneInfo GetSceneInfo()
  166. {
  167. SceneInfo sceneInfo = new SceneInfo();
  168. try
  169. {
  170. StreamReader reader = new StreamReader(sceneLoadDataPath);
  171. sceneInfo.version = System.Convert.ToInt64(reader.ReadLine());
  172. List<string> sceneList = new List<string>();
  173. while (!reader.EndOfStream)
  174. {
  175. sceneList.Add(reader.ReadLine());
  176. }
  177. sceneInfo.scenes = sceneList;
  178. }
  179. catch
  180. {
  181. logTextBox.text += "<color=red>Failed to get scene info data.\n</color>";
  182. }
  183. return sceneInfo;
  184. }
  185. // Update canvas position after a slight delay to get accurate headset position after scene transitions
  186. IEnumerator DelayCanvasPosUpdate()
  187. {
  188. yield return new WaitForSeconds(0.1f);
  189. UpdateCanvasPosition();
  190. }
  191. IEnumerator onCheckSceneCoroutine()
  192. {
  193. SceneInfo newSceneInfo;
  194. while (true)
  195. {
  196. newSceneInfo = GetSceneInfo();
  197. if (newSceneInfo.version != currentSceneInfo.version)
  198. {
  199. Debug.Log("[OVRSceneLoader] Scene change detected.");
  200. // Unload all asset bundles
  201. foreach (var b in loadedAssetBundles)
  202. {
  203. if (b != null)
  204. {
  205. b.Unload(true);
  206. }
  207. }
  208. loadedAssetBundles.Clear();
  209. // Unload all scenes in the hierarchy including main scene and
  210. // its dependent additive scenes.
  211. int activeScenes = SceneManager.sceneCount;
  212. for (int i = 0; i < activeScenes; i++)
  213. {
  214. SceneManager.UnloadSceneAsync(SceneManager.GetSceneAt(i));
  215. }
  216. DestroyAllGameObjects();
  217. SceneManager.LoadSceneAsync("OVRTransitionScene");
  218. break;
  219. }
  220. yield return new WaitForSeconds(sceneCheckIntervalSeconds);
  221. }
  222. }
  223. void DestroyAllGameObjects()
  224. {
  225. foreach (GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[])
  226. {
  227. Destroy(go);
  228. }
  229. }
  230. }