/************************************************************************************ Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. Licensed under the Oculus Utilities SDK License Version 1.31 (the "License"); you may not use the Utilities SDK except in compliance with the License, which is provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. You may obtain a copy of the License at https://developer.oculus.com/licenses/utilities-1.31 Unless required by applicable law or agreed to in writing, the Utilities SDK distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ************************************************************************************/ using UnityEngine; using System.Collections; using System.IO; /// /// Helper script for capture cubemap and save it into PNG or JPG file /// /// /// How it works: /// 1) This script can be attached to a existing game object, you can also use prefab Assets\OVR\Prefabs\OVRCubemapCaptureProbe /// There are 2 ways to trigger a capture if you attached this script to a game object. /// * Automatic capturing: if [autoTriggerAfterLaunch] is true, a automatic capturing will be triggered after [autoTriggerDelay] seconds. /// * Keyboard trigger: press key [triggeredByKey], a capturing will be triggered. /// 2) If you like to trigger the screen capture in your code logic, just call static function [OVRCubemapCapture.TriggerCubemapCapture] with proper input arguments. /// public class OVRCubemapCapture : MonoBehaviour { /// /// Enable the automatic screenshot trigger, which will capture a cubemap after autoTriggerDelay (seconds) /// public bool autoTriggerAfterLaunch = true; public float autoTriggerDelay = 1.0f; private float autoTriggerElapse = 0.0f; /// /// Trigger cubemap screenshot if user pressed key triggeredByKey /// public KeyCode triggeredByKey = KeyCode.F8; /// /// The complete file path for saving the cubemap screenshot, including the filename and extension /// if pathName is blank, screenshots will be saved into %USERPROFILE%\Documents\OVR_ScreenShot360 /// public string pathName; /// /// The cube face resolution /// public int cubemapSize = 2048; // Update is called once per frame void Update() { // Trigger after autoTriggerDelay if (autoTriggerAfterLaunch) { autoTriggerElapse += Time.deltaTime; if (autoTriggerElapse >= autoTriggerDelay) { autoTriggerAfterLaunch = false; TriggerCubemapCapture(transform.position, cubemapSize, pathName); } } // Trigger by press triggeredByKey if ( Input.GetKeyDown( triggeredByKey ) ) { TriggerCubemapCapture(transform.position, cubemapSize, pathName); } } /// /// Generate unity cubemap at specific location and save into JPG/PNG /// /// /// Default save folder: your app's persistentDataPath /// Default file name: using current time OVR_hh_mm_ss.png /// Note1: this will take a few seconds to finish /// Note2: if you only want to specify path not filename, please end [pathName] with "/" /// public static void TriggerCubemapCapture(Vector3 capturePos, int cubemapSize = 2048, string pathName = null) { GameObject ownerObj = new GameObject("CubemapCamera", typeof(Camera)); ownerObj.hideFlags = HideFlags.HideAndDontSave; ownerObj.transform.position = capturePos; ownerObj.transform.rotation = Quaternion.identity; Camera camComponent = ownerObj.GetComponent(); camComponent.farClipPlane = 10000.0f; camComponent.enabled = false; Cubemap cubemap = new Cubemap(cubemapSize, TextureFormat.RGB24, false); RenderIntoCubemap(camComponent, cubemap); SaveCubemapCapture(cubemap, pathName); DestroyImmediate(cubemap); DestroyImmediate(ownerObj); } public static void RenderIntoCubemap(Camera ownerCamera, Cubemap outCubemap) { int width = (int)outCubemap.width; int height = (int)outCubemap.height; CubemapFace[] faces = new CubemapFace[] { CubemapFace.PositiveX, CubemapFace.NegativeX, CubemapFace.PositiveY, CubemapFace.NegativeY, CubemapFace.PositiveZ, CubemapFace.NegativeZ }; 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) }; // Backup states RenderTexture backupRenderTex = RenderTexture.active; float backupFieldOfView = ownerCamera.fieldOfView; float backupAspect = ownerCamera.aspect; Quaternion backupRot = ownerCamera.transform.rotation; //RenderTexture backupRT = ownerCamera.targetTexture; // Enable 8X MSAA RenderTexture faceTexture = new RenderTexture(width, height, 24); faceTexture.antiAliasing = 8; faceTexture.dimension = UnityEngine.Rendering.TextureDimension.Tex2D; faceTexture.hideFlags = HideFlags.HideAndDontSave; // For intermediate saving Texture2D swapTex = new Texture2D(width, height, TextureFormat.RGB24, false); swapTex.hideFlags = HideFlags.HideAndDontSave; // Capture 6 Directions ownerCamera.targetTexture = faceTexture; ownerCamera.fieldOfView = 90; ownerCamera.aspect = 1.0f; Color[] mirroredPixels = new Color[swapTex.height * swapTex.width]; for (int i = 0; i < faces.Length; i++) { ownerCamera.transform.eulerAngles = faceAngles[i]; ownerCamera.Render(); RenderTexture.active = faceTexture; swapTex.ReadPixels(new Rect(0, 0, width, height), 0, 0); // Mirror vertically to meet the standard of unity cubemap Color[] OrignalPixels = swapTex.GetPixels(); for (int y1 = 0; y1 < height; y1++) { for (int x1 = 0; x1 < width; x1++) { mirroredPixels[y1 * width + x1] = OrignalPixels[((height - 1 - y1) * width) + x1]; } }; outCubemap.SetPixels(mirroredPixels, faces[i]); } outCubemap.SmoothEdges(); // Restore states RenderTexture.active = backupRenderTex; ownerCamera.fieldOfView = backupFieldOfView; ownerCamera.aspect = backupAspect; ownerCamera.transform.rotation = backupRot; ownerCamera.targetTexture = backupRenderTex; DestroyImmediate(swapTex); DestroyImmediate(faceTexture); } /// /// Save unity cubemap into NPOT 6x1 cubemap/texture atlas in the following format PX NX PY NY PZ NZ /// /// /// Supported format: PNG/JPG /// Default file name: using current time OVR_hh_mm_ss.png /// public static bool SaveCubemapCapture(Cubemap cubemap, string pathName = null) { string fileName; string dirName; int width = cubemap.width; int height = cubemap.height; int x = 0; int y = 0; bool saveToPNG = true; if (string.IsNullOrEmpty(pathName)) { dirName = Application.persistentDataPath + "/OVR_ScreenShot360/"; fileName = null; } else { dirName = Path.GetDirectoryName(pathName); fileName = Path.GetFileName(pathName); if (dirName[dirName.Length - 1] != '/' || dirName[dirName.Length - 1] != '\\') dirName += "/"; } if (string.IsNullOrEmpty(fileName)) fileName = "OVR_" + System.DateTime.Now.ToString("hh_mm_ss") + ".png"; string extName = Path.GetExtension(fileName); if (extName == ".png") { saveToPNG = true; } else if (extName == ".jpg") { saveToPNG = false; } else { Debug.LogError("Unsupported file format" + extName); return false; } // Validate path try { System.IO.Directory.CreateDirectory(dirName); } catch (System.Exception e) { Debug.LogError("Failed to create path " + dirName + " since " + e.ToString()); return false; } // Create the new texture Texture2D tex = new Texture2D(width * 6, height, TextureFormat.RGB24, false); if (tex == null) { Debug.LogError("[OVRScreenshotWizard] Failed creating the texture!"); return false; } // Merge all the cubemap faces into the texture // Reference cubemap format: http://docs.unity3d.com/Manual/class-Cubemap.html CubemapFace[] faces = new CubemapFace[] { CubemapFace.PositiveX, CubemapFace.NegativeX, CubemapFace.PositiveY, CubemapFace.NegativeY, CubemapFace.PositiveZ, CubemapFace.NegativeZ }; for (int i = 0; i < faces.Length; i++) { // get the pixels from the cubemap Color[] srcPixels = null; Color[] pixels = cubemap.GetPixels(faces[i]); // if desired, flip them as they are ordered left to right, bottom to top srcPixels = new Color[pixels.Length]; for (int y1 = 0; y1 < height; y1++) { for (int x1 = 0; x1 < width; x1++) { srcPixels[y1 * width + x1] = pixels[((height - 1 - y1) * width) + x1]; } } // Copy them to the dest texture tex.SetPixels(x, y, width, height, srcPixels); x += width; } try { // Encode the texture and save it to disk byte[] bytes = saveToPNG ? tex.EncodeToPNG() : tex.EncodeToJPG(); System.IO.File.WriteAllBytes(dirName + fileName, bytes); Debug.Log("Cubemap file created " + dirName + fileName); } catch (System.Exception e) { Debug.LogError("Failed to save cubemap file since " + e.ToString()); return false; } DestroyImmediate(tex); return true; } }