using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; //----------------------------------------------------------------------------- // Copyright 2012-2022 RenderHeads Ltd. All rights reserved. //----------------------------------------------------------------------------- namespace RenderHeads.Media.AVProMovieCapture.Editor { public class EditorScreenshot : MonoBehaviour { internal enum ImageFormat { PNG, JPG, TGA, EXR, } internal enum ExrPrecision { Half, Float, } internal enum ExrCompression { None, ZIP, RLE, PIZ, } [Serializable] internal class Options { [SerializeField, Range(1, 100)] internal int jpgQuality = 75; [SerializeField] internal ExrPrecision exrPrecision; [SerializeField] internal ExrCompression exrCompression; internal Texture2D.EXRFlags GetExrFlags() { Texture2D.EXRFlags result = Texture2D.EXRFlags.None; if (exrPrecision == ExrPrecision.Float) result |= Texture2D.EXRFlags.OutputAsFloat; if (exrCompression == ExrCompression.ZIP) result |= Texture2D.EXRFlags.CompressZIP; else if (exrCompression == ExrCompression.RLE) result |= Texture2D.EXRFlags.CompressRLE; else if (exrCompression == ExrCompression.PIZ) result |= Texture2D.EXRFlags.CompressPIZ; return result; } } private const string SceneCameraName = "SceneCamera"; internal static RenderTexture GetSceneViewTexture() { RenderTexture result = null; Camera[] cameras = FindAllCameras(); if (cameras != null) { Camera camera = FindCameraByName(cameras.Length, cameras, SceneCameraName); if (camera != null) { if (camera.targetTexture != null) { // Note we have to force a render camera.Render(); result = camera.targetTexture; } } } return result; } private static RenderTexture GetSceneViewTexture2() { RenderTexture result = null; RenderTexture[] rts = Resources.FindObjectsOfTypeAll(); foreach (RenderTexture rt in rts) { if (rt.name == "SceneView RT") { result = rt; break; } } return result; } internal static void SceneViewToFile(string fileNamePrefix, string folderPath, ImageFormat format, Options options) { RenderTexture cameraTexture = GetSceneViewTexture(); if (cameraTexture != null) { Texture2D texture = GetReadableTexture(cameraTexture, format == ImageFormat.EXR); if (texture != null) { string filePath = EditorScreenshot.GenerateFilename(fileNamePrefix, format, texture.width, texture.height); filePath = GenerateFilePath(folderPath, filePath); TextureToFile(texture, filePath, format, options); if (Application.isPlaying) { Destroy(texture); } else { DestroyImmediate(texture); } } } else { Debug.LogError("SceneView texture isn't available, make sure the view is visible"); } } internal static Texture2D GetReadableTexture(RenderTexture texture, bool supportHDR) { var oldRT = RenderTexture.active; TextureFormat format = TextureFormat.RGBA32; if (supportHDR) { format = TextureFormat.RGBAFloat; } Texture2D destTex = new Texture2D(texture.width, texture.height, format, false, supportHDR); RenderTexture.active = texture; destTex.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); destTex.Apply(); RenderTexture.active = oldRT; return destTex; } internal static bool SupportsTGA() { #if UNITY_2018_3_OR_NEWER return true; #else return false; #endif } internal static bool SupportsGameViewJPGTGAEXR() { #if UNITY_2017_3_OR_NEWER return Application.isPlaying; #else return false; #endif } internal static bool SupportsGameViewEXR() { #if UNITY_2019_1_OR_NEWER return Application.isPlaying; #else return false; #endif } internal static void TextureToFile(Texture2D texture, string filePath, ImageFormat format, Options options) { byte[] data = null; #if UNITY_2017_1_OR_NEWER switch (format) { case ImageFormat.PNG: data = ImageConversion.EncodeToPNG(texture); break; case ImageFormat.JPG: data = ImageConversion.EncodeToJPG(texture, options.jpgQuality); break; case ImageFormat.TGA: #if UNITY_2018_3_OR_NEWER data = ImageConversion.EncodeToTGA(texture); #endif break; case ImageFormat.EXR: data = ImageConversion.EncodeToEXR(texture, options.GetExrFlags()); break; } #else switch (format) { case ImageFormat.PNG: data = texture.EncodeToPNG(); break; case ImageFormat.JPG: data = texture.EncodeToJPG(options.jpgQuality); break; case ImageFormat.EXR: data = texture.EncodeToEXR(options.GetExrFlags()); break; } #endif if (data != null) { System.IO.File.WriteAllBytes(filePath, data); OnFileWritten(filePath); } } internal static void GameViewToPNG(string filePath, int superSize = 1) { #if UNITY_2017_1_OR_NEWER ScreenCapture.CaptureScreenshot(filePath, superSize); #else Application.CaptureScreenshot(filePath, superSize); #endif // The screenshot will not be generated until the frame has finished (at least in Application.CaptureScreenshot()) if (!Application.isPlaying) { UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); } OnFileWritten(filePath); } internal static void OnFileWritten(string filePath) { Debug.Log("[AVProMovieCapture] File written: " + filePath); CaptureBase.LastFileSaved = filePath; } internal static void RenderTextureToFile(string filePath, ImageFormat format, Options options, RenderTexture rt) { Texture2D texture = GetReadableTexture(rt, format == ImageFormat.EXR); if (texture != null) { TextureToFile(texture, filePath, format, options); if (Application.isPlaying) { Destroy(texture); } else { DestroyImmediate(texture); } } } internal static void GameViewToFile(string filePath, ImageFormat format, Options options, int superSize = 1) { // Coroutines aren't supported in editor mode, so we fake it using a GameObject with EditorCoroutine component GameObject go = new GameObject("temp-screenshot"); go.hideFlags = HideFlags.HideAndDontSave; EditorCoroutine co = go.AddComponent(); co.RunCoroutine(EditorScreenshot.GameViewToFileCoroutine(filePath, format, options, go, superSize)); } internal static IEnumerator GameViewToFileCoroutine(string filePath, ImageFormat format, Options options, GameObject go, int superSize = 1) { yield return new WaitForEndOfFrame(); Texture2D texture = null; #if UNITY_2017_3_OR_NEWER if (format != ImageFormat.EXR) { texture = ScreenCapture.CaptureScreenshotAsTexture(superSize); } else { // For EXR we want floating point textures which CaptureScreenshotAsTexture() doesn't provide RenderTextureFormat rtFormat = (options.exrPrecision == ExrPrecision.Float) ? RenderTextureFormat.ARGBFloat : RenderTextureFormat.ARGBHalf; RenderTexture rt = new RenderTexture(Screen.width * superSize, Screen.height * superSize, 24, rtFormat); rt.Create(); #if UNITY_2019_1_OR_NEWER ScreenCapture.CaptureScreenshotIntoRenderTexture(rt); #endif texture = GetReadableTexture(rt, true); Destroy(rt); } #endif if (texture != null) { TextureToFile(texture, filePath, format, options); if (Application.isPlaying) { Destroy(texture); Destroy(go); } else { DestroyImmediate(texture); DestroyImmediate(go); } } } internal static Camera[] FindAllCameras() { return Resources.FindObjectsOfTypeAll(); } static Camera FindCameraByName(int cameraCount, Camera[] cameras, string name) { Camera result = null; for (int i = 0; i < cameraCount; i++) { Camera c = cameras[i]; if (c.name == name) { result = c; break; } } return result; } internal static string GetExtension(ImageFormat format) { switch (format) { case ImageFormat.PNG: return "png"; case ImageFormat.JPG: return "jpg"; case ImageFormat.TGA: return "tga"; case ImageFormat.EXR: return "exr"; } throw new Exception("Unknown image format"); } internal static Vector2 GetGameViewSize() { Vector2 result = Vector2.zero; string[] res = UnityStats.screenRes.Split('x'); if (res.Length == 2) { result.x = int.Parse(res[0]); result.y = int.Parse(res[1]); } return result; } internal static string GenerateFilename(string filenamePrefix, ImageFormat format, int width, int height) { string filenameExtension = GetExtension(format); string dateTime = DateTime.Now.ToString("yyyyMMdd-HHmmss"); string filename = string.Format("{0}-{1}-{2}x{3}.{4}", filenamePrefix, dateTime, width, height, filenameExtension); return filename; } internal static string GenerateFilePath(string folderPath, string fileName) { if (!System.IO.Directory.Exists(folderPath)) { System.IO.Directory.CreateDirectory(folderPath); } return System.IO.Path.Combine(folderPath, fileName); } } }