using UnityEngine; using System.IO; using System.Diagnostics; using System.Runtime.InteropServices; //----------------------------------------------------------------------------- // Copyright 2012-2022 RenderHeads Ltd. All rights reserved. //----------------------------------------------------------------------------- namespace RenderHeads.Media.AVProMovieCapture { public class Utils { public static string[] WindowsImageSequenceFormatNames = new string[] { "PNG (uncompressed)" }; public static string[] MacOSImageSequenceFormatNames = new string[] { "PNG", "JPEG", "TIFF", "HEIF" }; public static string[] IOSImageSequenceFormatNames = new string[] { "PNG", "JPEG", "TIFF", "HEIF" }; public static string[] AndroidImageSequenceFormatNames = new string[] { "PNG", "JPEG" }; public static string[] GetNativeImageSequenceFormatNames() { string[] result = null; #if UNITY_EDITOR #if UNITY_EDITOR_WIN result = WindowsImageSequenceFormatNames; #elif UNITY_EDITOR_OSX result = MacOSImageSequenceFormatNames; #endif #else #if UNITY_STANDALONE_WIN result = WindowsImageSequenceFormatNames; #elif UNITY_STANDALONE_OSX result = MacOSImageSequenceFormatNames; #elif UNITY_IOS result = IOSImageSequenceFormatNames; #elif UNITY_ANDROID result = AndroidImageSequenceFormatNames; #endif #endif if (result == null) { result = new string[0]; } return result; } public static bool HasAlphaChannel(RenderTextureFormat format) { bool result = false; switch (format) { case RenderTextureFormat.ARGB32: case RenderTextureFormat.BGRA32: case RenderTextureFormat.ARGB4444: case RenderTextureFormat.ARGB1555: case RenderTextureFormat.ARGB2101010: case RenderTextureFormat.ARGB64: case RenderTextureFormat.RGBAUShort: case RenderTextureFormat.ARGBInt: case RenderTextureFormat.ARGBFloat: case RenderTextureFormat.ARGBHalf: #if UNITY_2017_2_OR_NEWER case RenderTextureFormat.BGRA10101010_XR: #endif result = true; break; } return result; } public static RenderTextureFormat GetBestRenderTextureFormat(bool supportHDR, bool supportTransparency, bool favourSpeedOverQuality) { RenderTextureFormat result = RenderTextureFormat.Default; if (supportTransparency) { if (supportHDR) { // HDR Transparent result = RenderTextureFormat.DefaultHDR; if (favourSpeedOverQuality) { #if UNITY_2017_2_OR_NEWER if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.BGRA10101010_XR)) return RenderTextureFormat.BGRA10101010_XR; #endif if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)) return RenderTextureFormat.ARGBHalf; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)) return RenderTextureFormat.ARGBFloat; } else { if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)) return RenderTextureFormat.ARGBFloat; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)) return RenderTextureFormat.ARGBHalf; #if UNITY_2017_2_OR_NEWER if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.BGRA10101010_XR)) return RenderTextureFormat.BGRA10101010_XR; #endif if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB64)) return RenderTextureFormat.ARGB64; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB2101010)) return RenderTextureFormat.ARGB2101010; } } else { // SDR Transparent if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB32)) return RenderTextureFormat.ARGB32; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.BGRA32)) return RenderTextureFormat.BGRA32; } } else { if (supportHDR) { // HDR non-transparent result = RenderTextureFormat.DefaultHDR; /*if (favourSpeedOverQuality) { if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGB111110Float)) return RenderTextureFormat.RGB111110Float; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB2101010)) return RenderTextureFormat.ARGB2101010; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)) return RenderTextureFormat.ARGBHalf; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)) return RenderTextureFormat.ARGBFloat; } else { if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)) return RenderTextureFormat.ARGBFloat; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)) return RenderTextureFormat.ARGBHalf; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB2101010)) return RenderTextureFormat.ARGB2101010; if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGB111110Float)) return RenderTextureFormat.RGB111110Float; }*/ } } if (!SystemInfo.SupportsRenderTextureFormat(result)) { UnityEngine.Debug.LogError("[AVProMovieCapture] Couldn't find suitable texture format " + result + " for " + supportHDR + " " + supportTransparency + " " + favourSpeedOverQuality); } return result; } /// /// The "main" camera isn't necessarily the one the gets rendered last to screen, /// so we sort all cameras by depth and find the one with no render target /// public static Camera GetUltimateRenderCamera() { Camera result = Camera.main; { // Iterate all enabled cameras float highestDepth = float.MinValue; Camera[] enabledCameras = Camera.allCameras; for (int cameraIndex = 0; cameraIndex < enabledCameras.Length; cameraIndex++) { Camera camera = enabledCameras[cameraIndex]; // Ignore null cameras if (camera != null) { // Ignore cameras that are hidden or have a targetTexture bool isHidden = (camera.hideFlags & HideFlags.HideInHierarchy) == HideFlags.HideInHierarchy; if (!isHidden && camera.targetTexture == null) { // Ignore cameras that render nothing if (camera.pixelRect.width > 0f && camera.pixelHeight > 0f) { // Keep the one with highest depth // TODO: handle the case where camera depths are equal - which one is first then? if (camera.depth >= highestDepth) { highestDepth = camera.depth; result = camera; } } } } } } return result; } public static bool HasContributingCameras(Camera parentCamera) { bool result = true; // If the camera doesn't clear the target completely then it may have other contributing cameras if (parentCamera.rect == new Rect(0f, 0f, 1f, 1f)) { if (parentCamera.clearFlags == CameraClearFlags.Skybox || parentCamera.clearFlags == CameraClearFlags.Color) { result = false; } } return result; } /// /// Returns a list of cameras sorted in render order from first to last that contribute to the rendering to parentCamera /// public static Camera[] FindContributingCameras(Camera parentCamera) { System.Collections.Generic.List validCameras = new System.Collections.Generic.List(8); { // Iterate all enabled/disabled cameras (they may become enabled later on) Camera[] allCameras = (Camera[])Resources.FindObjectsOfTypeAll(typeof(Camera)); for (int cameraIndex = 0; cameraIndex < allCameras.Length; cameraIndex++) { Camera camera = allCameras[cameraIndex]; // Ignore null cameras and camera that is the parent if (camera != null && camera != parentCamera) { // Only allow cameras with depth less or equal to parent camera if (camera.depth <= parentCamera.depth) { // Ignore cameras that are hidden or have a targetTexture that doesn't match the parent bool isHidden = (camera.hideFlags & HideFlags.HideInHierarchy) == HideFlags.HideInHierarchy; if (!isHidden && camera.targetTexture == parentCamera.targetTexture) { // Ignore cameras that render nothing if (camera.pixelRect.width > 0 && camera.pixelHeight > 0) { validCameras.Add(camera); } } } } } } if (validCameras.Count > 1) { // Sort by depth (render order) // TODO: handle the case where camera depths are equal - which one is first then? validCameras.Sort(delegate (Camera a, Camera b) { if (a != b) // Pre .Net 4.6.2 Sort() can compare an elements with itself { if (a.depth < b.depth) { return -1; } else if (a.depth > b.depth) { return 1; } else if (a.depth == b.depth) { UnityEngine.Debug.LogWarning("[AVProMovieCapture] Cameras '" + a.name + "' and '" + b.name + "' have the same depth value - unable to determine render order: " + a.depth); } } return 0; }); // Starting from the last camera to render, find the first one that clears the screen completely for (int i = (validCameras.Count - 1); i >= 0; i--) { if (validCameras[i].rect == new Rect(0f, 0f, 1f, 1f)) { if (validCameras[i].clearFlags == CameraClearFlags.Skybox || validCameras[i].clearFlags == CameraClearFlags.Color) { // Remove all cameras before this validCameras.RemoveRange(0, i); break; } } } } return validCameras.ToArray(); } #if UNITY_EDITOR_OSX || (UNITY_STANDALONE_OSX && !UNITY_EDITOR) private static string PhotosScheme { get { return "photos://"; } } private static string FileScheme { get { return "file://"; } } #elif UNITY_IOS && !UNITY_EDITOR private static string PhotosScheme { get { return "photos-redirect://"; } } private static string FileScheme { get { return "shareddocuments://"; } } #endif private static string URLEscapePathByPercentEncoding(string path) { return path.Replace("%", "%25") // Escape percents first so escaped character's percents aren't themselves escaped .Replace(" ", "%20") .Replace("\"", "%22") .Replace("#", "%23") .Replace(";", "%3B") .Replace("<", "%3C") .Replace(">", "%3E") .Replace("?", "%3F") .Replace("[", "%5B") .Replace("\\", "%5C") .Replace("]", "%5D") .Replace("^", "%5E") .Replace("`", "%60") .Replace("{", "%7B") .Replace("|", "%7C") .Replace("}", "%7D"); } public static bool ShowInExplorer(string itemPath) { bool result = false; #if UNITY_EDITOR_OSX || (!UNITY_EDITOR && (UNITY_STANDALONE_OSX || UNITY_IOS)) string url; if (itemPath.StartsWith("avpmc-photolibrary://")) { // TODO: Find out how to open photos to a specific album/item url = PhotosScheme; } else { url = FileScheme + URLEscapePathByPercentEncoding(Directory.GetParent(itemPath).FullName); } UnityEngine.Debug.LogFormat("ShowInExplorer - opening URL: {0}", url); UnityEngine.Application.OpenURL(url); result = true; #else #if !UNITY_WEBPLAYER itemPath = Path.GetFullPath(itemPath.Replace(@"/", @"\")); // explorer doesn't like front slashes if (File.Exists(itemPath)) { #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN) Process.Start("explorer.exe", "/select," + itemPath); #else #endif result = true; } else if (Directory.Exists(itemPath)) { // NOTE: We use OpenURL() instead of the explorer process so that it opens explorer inside the folder UnityEngine.Application.OpenURL(itemPath); result = true; } #endif #endif // UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX return result; } public static bool OpenInDefaultApp(string itemPath) { bool result = false; #if UNITY_EDITOR_OSX || (!UNITY_EDITOR && (UNITY_STANDALONE_OSX || UNITY_IOS)) string url; if (itemPath.StartsWith("avpmc-photolibrary://")) { // TODO: Find out how to open photos to a specific album/item url = PhotosScheme; } else { url = FileScheme + URLEscapePathByPercentEncoding(itemPath); } UnityEngine.Debug.LogFormat("OpenInDefaultApp - opening URL: {0}", url); UnityEngine.Application.OpenURL(url); result = true; #else itemPath = Path.GetFullPath(itemPath.Replace(@"/", @"\")); if (File.Exists(itemPath)) { UnityEngine.Application.OpenURL(itemPath); result = true; } else if (Directory.Exists(itemPath)) { UnityEngine.Application.OpenURL(itemPath); result = true; } #endif return result; } public static long GetFileSize(string filename) { #if UNITY_WEBPLAYER return 0; #else System.IO.FileInfo fi = new System.IO.FileInfo(filename); return fi.Length; #endif } #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN) [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetDiskFreeSpaceEx(string lpDirectoryName, out ulong lpFreeBytesAvailable, out ulong lpTotalNumberOfBytes, out ulong lpTotalNumberOfFreeBytes); public static bool DriveFreeBytes(string folderName, out ulong freespace) { freespace = 0; if (string.IsNullOrEmpty(folderName)) { throw new System.ArgumentNullException("folderName"); } if (!folderName.EndsWith("\\")) { folderName += '\\'; } ulong free = 0, dummy1 = 0, dummy2 = 0; if (GetDiskFreeSpaceEx(folderName, out free, out dummy1, out dummy2)) { freespace = free; return true; } else { return false; } } #endif public static string GetImageFileExtension(ImageSequenceFormat format) { string result = string.Empty; switch (format) { case ImageSequenceFormat.PNG: result = "png"; break; case ImageSequenceFormat.JPEG: result = "jpg"; break; case ImageSequenceFormat.TIFF: result = "tiff"; break; case ImageSequenceFormat.HEIF: result = "heif"; break; } return result; } } }