using System.Collections.Generic; using UnityEngine; using System.IO; using UnityEngine.Experimental.Rendering; namespace UnityEditor.Rendering { internal class TextureCombiner { static Texture2D _midGrey; /// /// Returns a 1 by 1 mid grey (0.5, 0.5, 0.5, 1) Texture. /// public static Texture2D midGrey { get { if (_midGrey == null) _midGrey = TextureFromColor(Color.grey); return _midGrey; } } private static Dictionary singleColorTextures = new Dictionary(); /// /// Returns a 1 by 1 Texture that is the color that you pass in. /// /// The color that Unity uses to create the Texture. /// public static Texture2D TextureFromColor(Color color) { if (color == Color.white) return Texture2D.whiteTexture; if (color == Color.black) return Texture2D.blackTexture; bool makeTexture = !singleColorTextures.ContainsKey(color); if (!makeTexture) makeTexture = (singleColorTextures[color] == null); if (makeTexture) { Texture2D tex = new Texture2D(1, 1, GraphicsFormat.R8G8B8A8_UNorm, TextureCreationFlags.None); tex.SetPixel(0, 0, color); tex.Apply(); singleColorTextures[color] = tex; } return singleColorTextures[color]; } /// /// Returns the Texture assigned to the property "propertyName" of "srcMaterial". /// If no matching property is found, or no Texture is assigned, returns a 1 by 1 Texture of "fallback" color. /// /// The Material to get the Texture from. /// The name of the Texture property. /// The fallback color that Unity uses to create a Texture if it could not find the Texture property on the Material. /// public static Texture GetTextureSafe(Material srcMaterial, string propertyName, Color fallback) { return GetTextureSafe(srcMaterial, propertyName, TextureFromColor(fallback)); } /// /// Returns the Texture assigned to the property "propertyName" of "srcMaterial". /// If no matching property is found, or no Texture is assigned, returns the "fallback" Texture. /// /// The Material to get the Texture from. /// The name of the Texture property. /// The fallback color that Unity uses to create a Texture if it could not find the Texture property on the Material. /// public static Texture GetTextureSafe(Material srcMaterial, string propertyName, Texture fallback) { if (!srcMaterial.HasProperty(propertyName)) return fallback; Texture tex = srcMaterial.GetTexture(propertyName); if (tex == null) return fallback; else return tex; } /// /// Specifies whether the Texture has an alpha channel or not. Returns true if it does and false otherwise. /// /// The Texture for this function to check. /// public static bool TextureHasAlpha(Texture2D tex) { if (tex == null) return false; return GraphicsFormatUtility.HasAlphaChannel(tex.graphicsFormat); } private Texture m_rSource; private Texture m_gSource; private Texture m_bSource; private Texture m_aSource; // Chanels are : r=0, g=1, b=2, a=3, greyscale from rgb = 4 // If negative, the chanel is inverted private int m_rChanel; private int m_gChanel; private int m_bChanel; private int m_aChanel; // Chanels remaping private Vector4[] m_remapings = { new Vector4(0f, 1f, 0f, 0f), new Vector4(0f, 1f, 0f, 0f), new Vector4(0f, 1f, 0f, 0f), new Vector4(0f, 1f, 0f, 0f) }; private bool m_bilinearFilter; private Dictionary m_RawTextures; /// /// Creates a TextureCombiner object. /// /// Source Texture for the RED output. /// Channel index to use for the RED output. /// Source Texture for the GREEN output. /// Channel index to use for the GREEN output. /// Source Texture for the BLUE output. /// Channel index to use for the BLUE output. /// Source Texture for the ALPHA output. /// Channel index to use for the ALPHA output. /// Use bilinear filtering when combining (default = true). public TextureCombiner(Texture rSource, int rChanel, Texture gSource, int gChanel, Texture bSource, int bChanel, Texture aSource, int aChanel, bool bilinearFilter = true) { m_rSource = rSource; m_gSource = gSource; m_bSource = bSource; m_aSource = aSource; m_rChanel = rChanel; m_gChanel = gChanel; m_bChanel = bChanel; m_aChanel = aChanel; m_bilinearFilter = bilinearFilter; } /// /// Set the remapping of a specific color channel. /// /// Target color channel (Red:0, Green:1, Blue:2, Alpha:3). /// Minimum input value mapped to 0 in output. /// Maximum input value mapped to 1 in output. public void SetRemapping(int channel, float min, float max) { if (channel > 3 || channel < 0) return; m_remapings[channel].x = min; m_remapings[channel].y = max; } /// /// Process the TextureCombiner. /// Unity creates the Texture Asset at the "savePath", and returns the Texture object. /// /// The path to save the Texture Asset to, relative to the Project folder. /// public Texture2D Combine(string savePath) { int xMin = int.MaxValue; int yMin = int.MaxValue; if (m_rSource.width > 4 && m_rSource.width < xMin) xMin = m_rSource.width; if (m_gSource.width > 4 && m_gSource.width < xMin) xMin = m_gSource.width; if (m_bSource.width > 4 && m_bSource.width < xMin) xMin = m_bSource.width; if (m_aSource.width > 4 && m_aSource.width < xMin) xMin = m_aSource.width; if (xMin == int.MaxValue) xMin = 4; if (m_rSource.height > 4 && m_rSource.height < yMin) yMin = m_rSource.height; if (m_gSource.height > 4 && m_gSource.height < yMin) yMin = m_gSource.height; if (m_bSource.height > 4 && m_bSource.height < yMin) yMin = m_bSource.height; if (m_aSource.height > 4 && m_aSource.height < yMin) yMin = m_aSource.height; if (yMin == int.MaxValue) yMin = 4; Texture2D combined = new Texture2D(xMin, yMin, GraphicsFormat.R32G32B32A32_SFloat, TextureCreationFlags.MipChain); combined.hideFlags = HideFlags.DontUnloadUnusedAsset; Material combinerMaterial = new Material(Shader.Find("Hidden/SRP_Core/TextureCombiner")); combinerMaterial.hideFlags = HideFlags.DontUnloadUnusedAsset; combinerMaterial.SetTexture("_RSource", GetRawTexture(m_rSource)); combinerMaterial.SetTexture("_GSource", GetRawTexture(m_gSource)); combinerMaterial.SetTexture("_BSource", GetRawTexture(m_bSource)); combinerMaterial.SetTexture("_ASource", GetRawTexture(m_aSource)); combinerMaterial.SetFloat("_RChannel", m_rChanel); combinerMaterial.SetFloat("_GChannel", m_gChanel); combinerMaterial.SetFloat("_BChannel", m_bChanel); combinerMaterial.SetFloat("_AChannel", m_aChanel); combinerMaterial.SetVector("_RRemap", m_remapings[0]); combinerMaterial.SetVector("_GRemap", m_remapings[1]); combinerMaterial.SetVector("_BRemap", m_remapings[2]); combinerMaterial.SetVector("_ARemap", m_remapings[3]); RenderTexture combinedRT = new RenderTexture(xMin, yMin, 0, GraphicsFormat.R32G32B32A32_SFloat); Graphics.Blit(Texture2D.whiteTexture, combinedRT, combinerMaterial); // Readback the render texture RenderTexture previousActive = RenderTexture.active; RenderTexture.active = combinedRT; combined.ReadPixels(new Rect(0, 0, xMin, yMin), 0, 0, false); combined.Apply(); RenderTexture.active = previousActive; byte[] bytes = new byte[0]; if (savePath.EndsWith("png")) bytes = ImageConversion.EncodeToPNG(combined); if (savePath.EndsWith("exr")) bytes = ImageConversion.EncodeToEXR(combined); if (savePath.EndsWith("jpg")) bytes = ImageConversion.EncodeToJPG(combined); string systemPath = Path.Combine(Application.dataPath.Remove(Application.dataPath.Length - 6), savePath); File.WriteAllBytes(systemPath, bytes); Object.DestroyImmediate(combined); AssetDatabase.ImportAsset(savePath); TextureImporter combinedImporter = (TextureImporter)AssetImporter.GetAtPath(savePath); combinedImporter.sRGBTexture = false; combinedImporter.SaveAndReimport(); if (savePath.EndsWith("exr")) { // The options for the platform string are: "Standalone", "iPhone", "Android", "WebGL", "Windows Store Apps", "PSP2", "PS4", "XboxOne", "Nintendo 3DS", "WiiU", "tvOS". combinedImporter.SetPlatformTextureSettings(new TextureImporterPlatformSettings() { name = "Standalone", format = TextureImporterFormat.DXT5, overridden = true }); } combined = AssetDatabase.LoadAssetAtPath(savePath); //cleanup "raw" textures foreach (KeyValuePair prop in m_RawTextures) { if (prop.Key != prop.Value && AssetDatabase.Contains(prop.Value)) AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(prop.Value)); } Object.DestroyImmediate(combinerMaterial); m_RawTextures.Clear(); return combined; } private Texture GetRawTexture(Texture original, bool sRGBFallback = false) { if (m_RawTextures == null) m_RawTextures = new Dictionary(); if (!m_RawTextures.ContainsKey(original)) { string path = AssetDatabase.GetAssetPath(original); string rawPath = "Assets/raw_" + Path.GetFileName(path); bool isBuiltinResource = path.Contains("unity_builtin"); if (!isBuiltinResource && AssetDatabase.Contains(original) && AssetDatabase.CopyAsset(path, rawPath)) { AssetDatabase.ImportAsset(rawPath); TextureImporter rawImporter = (TextureImporter)AssetImporter.GetAtPath(rawPath); rawImporter.textureType = TextureImporterType.Default; rawImporter.mipmapEnabled = false; rawImporter.isReadable = true; rawImporter.filterMode = m_bilinearFilter ? FilterMode.Bilinear : FilterMode.Point; rawImporter.npotScale = TextureImporterNPOTScale.None; rawImporter.wrapMode = TextureWrapMode.Clamp; Texture2D originalTex2D = original as Texture2D; rawImporter.sRGBTexture = (originalTex2D == null) ? sRGBFallback : (AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(original)) as TextureImporter).sRGBTexture; rawImporter.maxTextureSize = 8192; rawImporter.textureCompression = TextureImporterCompression.Uncompressed; rawImporter.SaveAndReimport(); m_RawTextures.Add(original, AssetDatabase.LoadAssetAtPath(rawPath)); } else m_RawTextures.Add(original, original); } return m_RawTextures[original]; } } }