OVRCubemapCapture.cs 9.4 KB

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