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.

381 lines
13 KiB

  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Custom inspector display for SteamVR_Skybox
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using UnityEditor;
  8. using System.Text;
  9. using System.Collections.Generic;
  10. using Valve.VR;
  11. using System.IO;
  12. [CustomEditor(typeof(SteamVR_Skybox)), CanEditMultipleObjects]
  13. public class SteamVR_SkyboxEditor : Editor
  14. {
  15. private const string nameFormat = "{0}/{1}-{2}.png";
  16. private const string helpText = "Take snapshot will use the current " +
  17. "position and rotation to capture six directional screenshots to use as this " +
  18. "skybox's textures. Note: This skybox is only used to override what shows up " +
  19. "in the compositor (e.g. when loading levels). Add a Camera component to this " +
  20. "object to override default settings like which layers to render. Additionally, " +
  21. "by specifying your own targetTexture, you can control the size of the textures " +
  22. "and other properties like antialiasing. Don't forget to disable the camera.\n\n" +
  23. "For stereo screenshots, a panorama is render for each eye using the specified " +
  24. "ipd (in millimeters) broken up into segments cellSize pixels square to optimize " +
  25. "generation.\n(32x32 takes about 10 seconds depending on scene complexity, 16x16 " +
  26. "takes around a minute, while will 8x8 take several minutes.)\n\nTo test, hit " +
  27. "play then pause - this will activate the skybox settings, and then drop you to " +
  28. "the compositor where the skybox is rendered.";
  29. public override void OnInspectorGUI()
  30. {
  31. DrawDefaultInspector();
  32. EditorGUILayout.HelpBox(helpText, MessageType.Info);
  33. if (GUILayout.Button("Take snapshot"))
  34. {
  35. var directions = new Quaternion[] {
  36. Quaternion.LookRotation(Vector3.forward),
  37. Quaternion.LookRotation(Vector3.back),
  38. Quaternion.LookRotation(Vector3.left),
  39. Quaternion.LookRotation(Vector3.right),
  40. Quaternion.LookRotation(Vector3.up, Vector3.back),
  41. Quaternion.LookRotation(Vector3.down, Vector3.forward)
  42. };
  43. Camera tempCamera = null;
  44. foreach (SteamVR_Skybox target in targets)
  45. {
  46. var targetScene = target.gameObject.scene;
  47. var sceneName = Path.GetFileNameWithoutExtension(targetScene.path);
  48. var scenePath = Path.GetDirectoryName(targetScene.path);
  49. var assetPath = scenePath + "/" + sceneName;
  50. if (!AssetDatabase.IsValidFolder(assetPath))
  51. {
  52. var guid = AssetDatabase.CreateFolder(scenePath, sceneName);
  53. assetPath = AssetDatabase.GUIDToAssetPath(guid);
  54. }
  55. var camera = target.GetComponent<Camera>();
  56. if (camera == null)
  57. {
  58. if (tempCamera == null)
  59. tempCamera = new GameObject().AddComponent<Camera>();
  60. camera = tempCamera;
  61. }
  62. var targetTexture = camera.targetTexture;
  63. if (camera.targetTexture == null)
  64. {
  65. targetTexture = new RenderTexture(1024, 1024, 24);
  66. targetTexture.antiAliasing = 8;
  67. camera.targetTexture = targetTexture;
  68. }
  69. var oldPosition = target.transform.localPosition;
  70. var oldRotation = target.transform.localRotation;
  71. var baseRotation = target.transform.rotation;
  72. var t = camera.transform;
  73. t.position = target.transform.position;
  74. camera.orthographic = false;
  75. camera.fieldOfView = 90;
  76. for (int i = 0; i < directions.Length; i++)
  77. {
  78. t.rotation = baseRotation * directions[i];
  79. camera.Render();
  80. // Copy to texture and save to disk.
  81. RenderTexture.active = targetTexture;
  82. var texture = new Texture2D(targetTexture.width, targetTexture.height, TextureFormat.ARGB32, false);
  83. texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0);
  84. texture.Apply();
  85. RenderTexture.active = null;
  86. var assetName = string.Format(nameFormat, assetPath, target.name, i);
  87. System.IO.File.WriteAllBytes(assetName, texture.EncodeToPNG());
  88. }
  89. if (camera != tempCamera)
  90. {
  91. target.transform.localPosition = oldPosition;
  92. target.transform.localRotation = oldRotation;
  93. }
  94. }
  95. if (tempCamera != null)
  96. {
  97. Object.DestroyImmediate(tempCamera.gameObject);
  98. }
  99. // Now that everything has be written out, reload the associated assets and assign them.
  100. AssetDatabase.Refresh();
  101. foreach (SteamVR_Skybox target in targets)
  102. {
  103. var targetScene = target.gameObject.scene;
  104. var sceneName = Path.GetFileNameWithoutExtension(targetScene.path);
  105. var scenePath = Path.GetDirectoryName(targetScene.path);
  106. var assetPath = scenePath + "/" + sceneName;
  107. for (int i = 0; i < directions.Length; i++)
  108. {
  109. var assetName = string.Format(nameFormat, assetPath, target.name, i);
  110. var importer = AssetImporter.GetAtPath(assetName) as TextureImporter;
  111. #if (UNITY_5_4 || UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
  112. importer.textureFormat = TextureImporterFormat.RGB24;
  113. #else
  114. importer.textureCompression = TextureImporterCompression.Uncompressed;
  115. #endif
  116. importer.wrapMode = TextureWrapMode.Clamp;
  117. importer.mipmapEnabled = false;
  118. importer.SaveAndReimport();
  119. var texture = AssetDatabase.LoadAssetAtPath<Texture>(assetName);
  120. target.SetTextureByIndex(i, texture);
  121. }
  122. }
  123. }
  124. else if (GUILayout.Button("Take stereo snapshot"))
  125. {
  126. const int width = 4096;
  127. const int height = width / 2;
  128. const int halfHeight = height / 2;
  129. var textures = new Texture2D[] {
  130. new Texture2D(width, height, TextureFormat.ARGB32, false),
  131. new Texture2D(width, height, TextureFormat.ARGB32, false) };
  132. var timer = new System.Diagnostics.Stopwatch();
  133. Camera tempCamera = null;
  134. foreach (SteamVR_Skybox target in targets)
  135. {
  136. timer.Start();
  137. var targetScene = target.gameObject.scene;
  138. var sceneName = Path.GetFileNameWithoutExtension(targetScene.path);
  139. var scenePath = Path.GetDirectoryName(targetScene.path);
  140. var assetPath = scenePath + "/" + sceneName;
  141. if (!AssetDatabase.IsValidFolder(assetPath))
  142. {
  143. var guid = AssetDatabase.CreateFolder(scenePath, sceneName);
  144. assetPath = AssetDatabase.GUIDToAssetPath(guid);
  145. }
  146. var camera = target.GetComponent<Camera>();
  147. if (camera == null)
  148. {
  149. if (tempCamera == null)
  150. tempCamera = new GameObject().AddComponent<Camera>();
  151. camera = tempCamera;
  152. }
  153. var fx = camera.gameObject.AddComponent<SteamVR_SphericalProjection>();
  154. var oldTargetTexture = camera.targetTexture;
  155. var oldOrthographic = camera.orthographic;
  156. var oldFieldOfView = camera.fieldOfView;
  157. var oldAspect = camera.aspect;
  158. var oldPosition = target.transform.localPosition;
  159. var oldRotation = target.transform.localRotation;
  160. var basePosition = target.transform.position;
  161. var baseRotation = target.transform.rotation;
  162. var transform = camera.transform;
  163. int cellSize = int.Parse(target.StereoCellSize.ToString().Substring(1));
  164. float ipd = target.StereoIpdMm / 1000.0f;
  165. int vTotal = halfHeight / cellSize;
  166. float dv = 90.0f / vTotal; // vertical degrees per segment
  167. float dvHalf = dv / 2.0f;
  168. var targetTexture = new RenderTexture(cellSize, cellSize, 24);
  169. targetTexture.wrapMode = TextureWrapMode.Clamp;
  170. targetTexture.antiAliasing = 8;
  171. camera.fieldOfView = dv;
  172. camera.orthographic = false;
  173. camera.targetTexture = targetTexture;
  174. // Render sections of a sphere using a rectilinear projection
  175. // and resample using a sphereical projection into a single panorama
  176. // texture per eye. We break into sections in order to keep the eye
  177. // separation similar around the sphere. Rendering alternates between
  178. // top and bottom sections, sweeping horizontally around the sphere,
  179. // alternating left and right eyes.
  180. for (int v = 0; v < vTotal; v++)
  181. {
  182. var pitch = 90.0f - (v * dv) - dvHalf;
  183. var uTotal = width / targetTexture.width;
  184. var du = 360.0f / uTotal; // horizontal degrees per segment
  185. var duHalf = du / 2.0f;
  186. var vTarget = v * halfHeight / vTotal;
  187. for (int i = 0; i < 2; i++) // top, bottom
  188. {
  189. if (i == 1)
  190. {
  191. pitch = -pitch;
  192. vTarget = height - vTarget - cellSize;
  193. }
  194. for (int u = 0; u < uTotal; u++)
  195. {
  196. var yaw = -180.0f + (u * du) + duHalf;
  197. var uTarget = u * width / uTotal;
  198. var xOffset = -ipd / 2 * Mathf.Cos(pitch * Mathf.Deg2Rad);
  199. for (int j = 0; j < 2; j++) // left, right
  200. {
  201. var texture = textures[j];
  202. if (j == 1)
  203. {
  204. xOffset = -xOffset;
  205. }
  206. var offset = baseRotation * Quaternion.Euler(0, yaw, 0) * new Vector3(xOffset, 0, 0);
  207. transform.position = basePosition + offset;
  208. var direction = Quaternion.Euler(pitch, yaw, 0.0f);
  209. transform.rotation = baseRotation * direction;
  210. // vector pointing to center of this section
  211. var N = direction * Vector3.forward;
  212. // horizontal span of this section in degrees
  213. var phi0 = yaw - (du / 2);
  214. var phi1 = phi0 + du;
  215. // vertical span of this section in degrees
  216. var theta0 = pitch + (dv / 2);
  217. var theta1 = theta0 - dv;
  218. var midPhi = (phi0 + phi1) / 2;
  219. var baseTheta = Mathf.Abs(theta0) < Mathf.Abs(theta1) ? theta0 : theta1;
  220. // vectors pointing to corners of image closes to the equator
  221. var V00 = Quaternion.Euler(baseTheta, phi0, 0.0f) * Vector3.forward;
  222. var V01 = Quaternion.Euler(baseTheta, phi1, 0.0f) * Vector3.forward;
  223. // vectors pointing to top and bottom midsection of image
  224. var V0M = Quaternion.Euler(theta0, midPhi, 0.0f) * Vector3.forward;
  225. var V1M = Quaternion.Euler(theta1, midPhi, 0.0f) * Vector3.forward;
  226. // intersection points for each of the above
  227. var P00 = V00 / Vector3.Dot(V00, N);
  228. var P01 = V01 / Vector3.Dot(V01, N);
  229. var P0M = V0M / Vector3.Dot(V0M, N);
  230. var P1M = V1M / Vector3.Dot(V1M, N);
  231. // calculate basis vectors for plane
  232. var P00_P01 = P01 - P00;
  233. var P0M_P1M = P1M - P0M;
  234. var uMag = P00_P01.magnitude;
  235. var vMag = P0M_P1M.magnitude;
  236. var uScale = 1.0f / uMag;
  237. var vScale = 1.0f / vMag;
  238. var uAxis = P00_P01 * uScale;
  239. var vAxis = P0M_P1M * vScale;
  240. // update material constant buffer
  241. fx.Set(N, phi0, phi1, theta0, theta1,
  242. uAxis, P00, uScale,
  243. vAxis, P0M, vScale);
  244. camera.aspect = uMag / vMag;
  245. camera.Render();
  246. RenderTexture.active = targetTexture;
  247. texture.ReadPixels(new Rect(0, 0, targetTexture.width, targetTexture.height), uTarget, vTarget);
  248. RenderTexture.active = null;
  249. }
  250. }
  251. }
  252. }
  253. // Save textures to disk.
  254. for (int i = 0; i < 2; i++)
  255. {
  256. var texture = textures[i];
  257. texture.Apply();
  258. var assetName = string.Format(nameFormat, assetPath, target.name, i);
  259. File.WriteAllBytes(assetName, texture.EncodeToPNG());
  260. }
  261. // Cleanup.
  262. if (camera != tempCamera)
  263. {
  264. camera.targetTexture = oldTargetTexture;
  265. camera.orthographic = oldOrthographic;
  266. camera.fieldOfView = oldFieldOfView;
  267. camera.aspect = oldAspect;
  268. target.transform.localPosition = oldPosition;
  269. target.transform.localRotation = oldRotation;
  270. }
  271. else
  272. {
  273. tempCamera.targetTexture = null;
  274. }
  275. DestroyImmediate(targetTexture);
  276. DestroyImmediate(fx);
  277. timer.Stop();
  278. Debug.Log(string.Format("Screenshot took {0} seconds.", timer.Elapsed));
  279. }
  280. if (tempCamera != null)
  281. {
  282. DestroyImmediate(tempCamera.gameObject);
  283. }
  284. DestroyImmediate(textures[0]);
  285. DestroyImmediate(textures[1]);
  286. // Now that everything has be written out, reload the associated assets and assign them.
  287. AssetDatabase.Refresh();
  288. foreach (SteamVR_Skybox target in targets)
  289. {
  290. var targetScene = target.gameObject.scene;
  291. var sceneName = Path.GetFileNameWithoutExtension(targetScene.path);
  292. var scenePath = Path.GetDirectoryName(targetScene.path);
  293. var assetPath = scenePath + "/" + sceneName;
  294. for (int i = 0; i < 2; i++)
  295. {
  296. var assetName = string.Format(nameFormat, assetPath, target.name, i);
  297. var importer = AssetImporter.GetAtPath(assetName) as TextureImporter;
  298. importer.mipmapEnabled = false;
  299. importer.wrapMode = TextureWrapMode.Repeat;
  300. #if (UNITY_5_4 || UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
  301. importer.SetPlatformTextureSettings("Standalone", width, TextureImporterFormat.RGB24);
  302. #else
  303. var settings = importer.GetPlatformTextureSettings("Standalone");
  304. settings.textureCompression = TextureImporterCompression.Uncompressed;
  305. settings.maxTextureSize = width;
  306. importer.SetPlatformTextureSettings(settings);
  307. #endif
  308. importer.SaveAndReimport();
  309. var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetName);
  310. target.SetTextureByIndex(i, texture);
  311. }
  312. }
  313. }
  314. }
  315. }