OVRCubemapCapture.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.IO;
  4. /// <summary>
  5. /// Helper script for capture cubemap and save it into PNG or JPG file
  6. /// </summary>
  7. /// <description>
  8. /// How it works:
  9. /// 1) This script can be attached to a existing game object, you can also use prefab Assets\OVR\Prefabs\OVRCubemapCaptureProbe
  10. /// There are 2 ways to trigger a capture if you attached this script to a game object.
  11. /// * Automatic capturing: if [autoTriggerAfterLaunch] is true, a automatic capturing will be triggered after [autoTriggerDelay] seconds.
  12. /// * Keyboard trigger: press key [triggeredByKey], a capturing will be triggered.
  13. /// 2) If you like to trigger the screen capture in your code logic, just call static function [OVRCubemapCapture.TriggerCubemapCapture] with proper input arguments.
  14. /// </description>
  15. public class OVRCubemapCapture : MonoBehaviour
  16. {
  17. /// <summary>
  18. /// Enable the automatic screenshot trigger, which will capture a cubemap after autoTriggerDelay (seconds)
  19. /// </summary>
  20. public bool autoTriggerAfterLaunch = true;
  21. public float autoTriggerDelay = 1.0f;
  22. private float autoTriggerElapse = 0.0f;
  23. /// <summary>
  24. /// Trigger cubemap screenshot if user pressed key triggeredByKey
  25. /// </summary>
  26. public KeyCode triggeredByKey = KeyCode.F8;
  27. /// <summary>
  28. /// The complete file path for saving the cubemap screenshot, including the filename and extension
  29. /// if pathName is blank, screenshots will be saved into %USERPROFILE%\Documents\OVR_ScreenShot360
  30. /// </summary>
  31. public string pathName;
  32. /// <summary>
  33. /// The cube face resolution
  34. /// </summary>
  35. public int cubemapSize = 2048;
  36. // Update is called once per frame
  37. void Update()
  38. {
  39. // Trigger after autoTriggerDelay
  40. if (autoTriggerAfterLaunch)
  41. {
  42. autoTriggerElapse += Time.deltaTime;
  43. if (autoTriggerElapse >= autoTriggerDelay)
  44. {
  45. autoTriggerAfterLaunch = false;
  46. TriggerCubemapCapture(transform.position, cubemapSize, pathName);
  47. }
  48. }
  49. // Trigger by press triggeredByKey
  50. if ( Input.GetKeyDown( triggeredByKey ) )
  51. {
  52. TriggerCubemapCapture(transform.position, cubemapSize, pathName);
  53. }
  54. }
  55. /// <summary>
  56. /// Generate unity cubemap at specific location and save into JPG/PNG
  57. /// </summary>
  58. /// <description>
  59. /// Default save folder: your app's persistentDataPath
  60. /// Default file name: using current time OVR_hh_mm_ss.png
  61. /// Note1: this will take a few seconds to finish
  62. /// Note2: if you only want to specify path not filename, please end [pathName] with "/"
  63. /// </description>
  64. public static void TriggerCubemapCapture(Vector3 capturePos, int cubemapSize = 2048, string pathName = null)
  65. {
  66. GameObject ownerObj = new GameObject("CubemapCamera", typeof(Camera));
  67. ownerObj.hideFlags = HideFlags.HideAndDontSave;
  68. ownerObj.transform.position = capturePos;
  69. ownerObj.transform.rotation = Quaternion.identity;
  70. Camera camComponent = ownerObj.GetComponent<Camera>();
  71. camComponent.farClipPlane = 10000.0f;
  72. camComponent.enabled = false;
  73. Cubemap cubemap = new Cubemap(cubemapSize, TextureFormat.RGB24, false);
  74. RenderIntoCubemap(camComponent, cubemap);
  75. SaveCubemapCapture(cubemap, pathName);
  76. DestroyImmediate(cubemap);
  77. DestroyImmediate(ownerObj);
  78. }
  79. public static void RenderIntoCubemap(Camera ownerCamera, Cubemap outCubemap)
  80. {
  81. int width = (int)outCubemap.width;
  82. int height = (int)outCubemap.height;
  83. CubemapFace[] faces = new CubemapFace[] { CubemapFace.PositiveX, CubemapFace.NegativeX, CubemapFace.PositiveY, CubemapFace.NegativeY, CubemapFace.PositiveZ, CubemapFace.NegativeZ };
  84. Vector3[] faceAngles = new Vector3[] { new Vector3(0.0f, 90.0f, 0.0f), new Vector3(0.0f, -90.0f, 0.0f), new Vector3(-90.0f, 0.0f, 0.0f), new Vector3(90.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 180.0f, 0.0f) };
  85. // Backup states
  86. RenderTexture backupRenderTex = RenderTexture.active;
  87. float backupFieldOfView = ownerCamera.fieldOfView;
  88. float backupAspect = ownerCamera.aspect;
  89. Quaternion backupRot = ownerCamera.transform.rotation;
  90. //RenderTexture backupRT = ownerCamera.targetTexture;
  91. // Enable 8X MSAA
  92. RenderTexture faceTexture = new RenderTexture(width, height, 24);
  93. faceTexture.antiAliasing = 8;
  94. faceTexture.dimension = UnityEngine.Rendering.TextureDimension.Tex2D;
  95. faceTexture.hideFlags = HideFlags.HideAndDontSave;
  96. // For intermediate saving
  97. Texture2D swapTex = new Texture2D(width, height, TextureFormat.RGB24, false);
  98. swapTex.hideFlags = HideFlags.HideAndDontSave;
  99. // Capture 6 Directions
  100. ownerCamera.targetTexture = faceTexture;
  101. ownerCamera.fieldOfView = 90;
  102. ownerCamera.aspect = 1.0f;
  103. Color[] mirroredPixels = new Color[swapTex.height * swapTex.width];
  104. for (int i = 0; i < faces.Length; i++)
  105. {
  106. ownerCamera.transform.eulerAngles = faceAngles[i];
  107. ownerCamera.Render();
  108. RenderTexture.active = faceTexture;
  109. swapTex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
  110. // Mirror vertically to meet the standard of unity cubemap
  111. Color[] OrignalPixels = swapTex.GetPixels();
  112. for (int y1 = 0; y1 < height; y1++)
  113. {
  114. for (int x1 = 0; x1 < width; x1++)
  115. {
  116. mirroredPixels[y1 * width + x1] = OrignalPixels[((height - 1 - y1) * width) + x1];
  117. }
  118. };
  119. outCubemap.SetPixels(mirroredPixels, faces[i]);
  120. }
  121. outCubemap.SmoothEdges();
  122. // Restore states
  123. RenderTexture.active = backupRenderTex;
  124. ownerCamera.fieldOfView = backupFieldOfView;
  125. ownerCamera.aspect = backupAspect;
  126. ownerCamera.transform.rotation = backupRot;
  127. ownerCamera.targetTexture = backupRenderTex;
  128. DestroyImmediate(swapTex);
  129. DestroyImmediate(faceTexture);
  130. }
  131. /// <summary>
  132. /// Save unity cubemap into NPOT 6x1 cubemap/texture atlas in the following format PX NX PY NY PZ NZ
  133. /// </summary>
  134. /// <description>
  135. /// Supported format: PNG/JPG
  136. /// Default file name: using current time OVR_hh_mm_ss.png
  137. /// </description>
  138. public static bool SaveCubemapCapture(Cubemap cubemap, string pathName = null)
  139. {
  140. string fileName;
  141. string dirName;
  142. int width = cubemap.width;
  143. int height = cubemap.height;
  144. int x = 0;
  145. int y = 0;
  146. bool saveToPNG = true;
  147. if (string.IsNullOrEmpty(pathName))
  148. {
  149. dirName = Application.persistentDataPath + "/OVR_ScreenShot360/";
  150. fileName = null;
  151. }
  152. else
  153. {
  154. dirName = Path.GetDirectoryName(pathName);
  155. fileName = Path.GetFileName(pathName);
  156. if (dirName[dirName.Length - 1] != '/' || dirName[dirName.Length - 1] != '\\')
  157. dirName += "/";
  158. }
  159. if (string.IsNullOrEmpty(fileName))
  160. fileName = "OVR_" + System.DateTime.Now.ToString("hh_mm_ss") + ".png";
  161. string extName = Path.GetExtension(fileName);
  162. if (extName == ".png")
  163. {
  164. saveToPNG = true;
  165. }
  166. else if (extName == ".jpg")
  167. {
  168. saveToPNG = false;
  169. }
  170. else
  171. {
  172. Debug.LogError("Unsupported file format" + extName);
  173. return false;
  174. }
  175. // Validate path
  176. try
  177. {
  178. System.IO.Directory.CreateDirectory(dirName);
  179. }
  180. catch (System.Exception e)
  181. {
  182. Debug.LogError("Failed to create path " + dirName + " since " + e.ToString());
  183. return false;
  184. }
  185. // Create the new texture
  186. Texture2D tex = new Texture2D(width * 6, height, TextureFormat.RGB24, false);
  187. if (tex == null)
  188. {
  189. Debug.LogError("[OVRScreenshotWizard] Failed creating the texture!");
  190. return false;
  191. }
  192. // Merge all the cubemap faces into the texture
  193. // Reference cubemap format: http://docs.unity3d.com/Manual/class-Cubemap.html
  194. CubemapFace[] faces = new CubemapFace[] { CubemapFace.PositiveX, CubemapFace.NegativeX, CubemapFace.PositiveY, CubemapFace.NegativeY, CubemapFace.PositiveZ, CubemapFace.NegativeZ };
  195. for (int i = 0; i < faces.Length; i++)
  196. {
  197. // get the pixels from the cubemap
  198. Color[] srcPixels = null;
  199. Color[] pixels = cubemap.GetPixels(faces[i]);
  200. // if desired, flip them as they are ordered left to right, bottom to top
  201. srcPixels = new Color[pixels.Length];
  202. for (int y1 = 0; y1 < height; y1++)
  203. {
  204. for (int x1 = 0; x1 < width; x1++)
  205. {
  206. srcPixels[y1 * width + x1] = pixels[((height - 1 - y1) * width) + x1];
  207. }
  208. }
  209. // Copy them to the dest texture
  210. tex.SetPixels(x, y, width, height, srcPixels);
  211. x += width;
  212. }
  213. try
  214. {
  215. // Encode the texture and save it to disk
  216. byte[] bytes = saveToPNG ? tex.EncodeToPNG() : tex.EncodeToJPG();
  217. System.IO.File.WriteAllBytes(dirName + fileName, bytes);
  218. Debug.Log("Cubemap file created " + dirName + fileName);
  219. }
  220. catch (System.Exception e)
  221. {
  222. Debug.LogError("Failed to save cubemap file since " + e.ToString());
  223. return false;
  224. }
  225. DestroyImmediate(tex);
  226. return true;
  227. }
  228. }