using System; using System.Collections.Generic; using System.Linq; using UnityEngine.Experimental.Rendering; namespace UnityEngine.Rendering { using UnityObject = UnityEngine.Object; /// /// Set of utility functions for the Core Scriptable Render Pipeline Library /// public static class CoreUtils { /// /// List of look at matrices for cubemap faces. /// Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/bb204881(v=vs.85).aspx /// static public readonly Vector3[] lookAtList = { new Vector3(1.0f, 0.0f, 0.0f), new Vector3(-1.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.0f, -1.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 0.0f, -1.0f), }; /// /// List of up vectors for cubemap faces. /// Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/bb204881(v=vs.85).aspx /// static public readonly Vector3[] upVectorList = { new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.0f, 0.0f, -1.0f), new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), }; /// Edit Menu priority 1 public const int editMenuPriority1 = 320; /// Edit Menu priority 2 public const int editMenuPriority2 = 331; /// Edit Menu priority 3 public const int editMenuPriority3 = 342; /// Edit Menu priority 4 public const int editMenuPriority4 = 353; /// Asset Create Menu priority 1 public const int assetCreateMenuPriority1 = 230; /// Asset Create Menu priority 2 public const int assetCreateMenuPriority2 = 241; /// Asset Create Menu priority 3 public const int assetCreateMenuPriority3 = 300; /// Game Object Menu priority public const int gameObjectMenuPriority = 10; static Cubemap m_BlackCubeTexture; /// /// Black cubemap texture. /// public static Cubemap blackCubeTexture { get { if (m_BlackCubeTexture == null) { m_BlackCubeTexture = new Cubemap(1, TextureFormat.ARGB32, false); for (int i = 0; i < 6; ++i) m_BlackCubeTexture.SetPixel((CubemapFace)i, 0, 0, Color.black); m_BlackCubeTexture.Apply(); } return m_BlackCubeTexture; } } static Cubemap m_MagentaCubeTexture; /// /// Magenta cubemap texture. /// public static Cubemap magentaCubeTexture { get { if (m_MagentaCubeTexture == null) { m_MagentaCubeTexture = new Cubemap(1, TextureFormat.ARGB32, false); for (int i = 0; i < 6; ++i) m_MagentaCubeTexture.SetPixel((CubemapFace)i, 0, 0, Color.magenta); m_MagentaCubeTexture.Apply(); } return m_MagentaCubeTexture; } } static CubemapArray m_MagentaCubeTextureArray; /// /// Black cubemap array texture. /// public static CubemapArray magentaCubeTextureArray { get { if (m_MagentaCubeTextureArray == null) { m_MagentaCubeTextureArray = new CubemapArray(1, 1, TextureFormat.RGBAFloat, false); for (int i = 0; i < 6; ++i) { Color[] colors = { Color.magenta }; m_MagentaCubeTextureArray.SetPixels(colors, (CubemapFace)i, 0); } m_MagentaCubeTextureArray.Apply(); } return m_MagentaCubeTextureArray; } } static Cubemap m_WhiteCubeTexture; /// /// White cubemap texture. /// public static Cubemap whiteCubeTexture { get { if (m_WhiteCubeTexture == null) { m_WhiteCubeTexture = new Cubemap(1, TextureFormat.ARGB32, false); for (int i = 0; i < 6; ++i) m_WhiteCubeTexture.SetPixel((CubemapFace)i, 0, 0, Color.white); m_WhiteCubeTexture.Apply(); } return m_WhiteCubeTexture; } } static RenderTexture m_EmptyUAV; /// /// Empty 1x1 texture usable as a dummy UAV. /// public static RenderTexture emptyUAV { get { if (m_EmptyUAV == null) { m_EmptyUAV = new RenderTexture(1, 1, 0); m_EmptyUAV.enableRandomWrite = true; m_EmptyUAV.Create(); } return m_EmptyUAV; } } static Texture3D m_BlackVolumeTexture; /// /// Black 3D texture. /// public static Texture3D blackVolumeTexture { get { if (m_BlackVolumeTexture == null) { Color[] colors = { Color.black }; m_BlackVolumeTexture = new Texture3D(1, 1, 1, TextureFormat.ARGB32, false); m_BlackVolumeTexture.SetPixels(colors, 0); m_BlackVolumeTexture.Apply(); } return m_BlackVolumeTexture; } } /// /// Clear the currently bound render texture. /// /// CommandBuffer used for rendering commands. /// Specify how the render texture should be cleared. /// Specify with which color the render texture should be cleared. public static void ClearRenderTarget(CommandBuffer cmd, ClearFlag clearFlag, Color clearColor) { if (clearFlag != ClearFlag.None) cmd.ClearRenderTarget((clearFlag & ClearFlag.Depth) != 0, (clearFlag & ClearFlag.Color) != 0, clearColor); } // We use -1 as a default value because when doing SPI for XR, it will bind the full texture array by default (and has no effect on 2D textures) // Unfortunately, for cubemaps, passing -1 does not work for faces other than the first one, so we fall back to 0 in this case. private static int FixupDepthSlice(int depthSlice, RTHandle buffer) { if (depthSlice == -1 && buffer.rt.dimension == TextureDimension.Cube) depthSlice = 0; return depthSlice; } private static int FixupDepthSlice(int depthSlice, CubemapFace cubemapFace) { if (depthSlice == -1 && cubemapFace != CubemapFace.Unknown) depthSlice = 0; return depthSlice; } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier of the render texture. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier buffer, ClearFlag clearFlag, Color clearColor, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { depthSlice = FixupDepthSlice(depthSlice, cubemapFace); cmd.SetRenderTarget(buffer, miplevel, cubemapFace, depthSlice); ClearRenderTarget(cmd, clearFlag, clearColor); } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier of the render texture. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier buffer, ClearFlag clearFlag = ClearFlag.None, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { SetRenderTarget(cmd, buffer, clearFlag, Color.clear, miplevel, cubemapFace, depthSlice); } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier of the color render texture. /// RenderTargetIdentifier of the depth render texture. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorBuffer, RenderTargetIdentifier depthBuffer, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { SetRenderTarget(cmd, colorBuffer, depthBuffer, ClearFlag.None, Color.clear, miplevel, cubemapFace, depthSlice); } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier of the color render texture. /// RenderTargetIdentifier of the depth render texture. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorBuffer, RenderTargetIdentifier depthBuffer, ClearFlag clearFlag, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { SetRenderTarget(cmd, colorBuffer, depthBuffer, clearFlag, Color.clear, miplevel, cubemapFace, depthSlice); } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier of the color render texture. /// RenderTargetIdentifier of the depth render texture. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorBuffer, RenderTargetIdentifier depthBuffer, ClearFlag clearFlag, Color clearColor, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { depthSlice = FixupDepthSlice(depthSlice, cubemapFace); cmd.SetRenderTarget(colorBuffer, depthBuffer, miplevel, cubemapFace, depthSlice); ClearRenderTarget(cmd, clearFlag, clearColor); } /// /// Set the current multiple render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier array of the color render textures. /// RenderTargetIdentifier of the depth render texture. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier[] colorBuffers, RenderTargetIdentifier depthBuffer) { SetRenderTarget(cmd, colorBuffers, depthBuffer, ClearFlag.None, Color.clear); } /// /// Set the current multiple render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier array of the color render textures. /// RenderTargetIdentifier of the depth render texture. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier[] colorBuffers, RenderTargetIdentifier depthBuffer, ClearFlag clearFlag = ClearFlag.None) { SetRenderTarget(cmd, colorBuffers, depthBuffer, clearFlag, Color.clear); } /// /// Set the current multiple render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier array of the color render textures. /// RenderTargetIdentifier of the depth render texture. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier[] colorBuffers, RenderTargetIdentifier depthBuffer, ClearFlag clearFlag, Color clearColor) { cmd.SetRenderTarget(colorBuffers, depthBuffer, 0, CubemapFace.Unknown, -1); ClearRenderTarget(cmd, clearFlag, clearColor); } // Explicit load and store actions /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// Color buffer RenderTargetIdentifier. /// Load action. /// Store action. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier buffer, RenderBufferLoadAction loadAction, RenderBufferStoreAction storeAction, ClearFlag clearFlag, Color clearColor) { cmd.SetRenderTarget(buffer, loadAction, storeAction); ClearRenderTarget(cmd, clearFlag, clearColor); } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// Color buffer RenderTargetIdentifier. /// Load action. /// Store action. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier buffer, RenderBufferLoadAction loadAction, RenderBufferStoreAction storeAction, ClearFlag clearFlag) { SetRenderTarget(cmd, buffer, loadAction, storeAction, clearFlag, Color.clear); } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// Color buffer RenderTargetIdentifier. /// Color buffer load action. /// Color buffer store action. /// Depth buffer RenderTargetIdentifier. /// Depth buffer load action. /// Depth buffer store action. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorBuffer, RenderBufferLoadAction colorLoadAction, RenderBufferStoreAction colorStoreAction, RenderTargetIdentifier depthBuffer, RenderBufferLoadAction depthLoadAction, RenderBufferStoreAction depthStoreAction, ClearFlag clearFlag, Color clearColor) { cmd.SetRenderTarget(colorBuffer, colorLoadAction, colorStoreAction, depthBuffer, depthLoadAction, depthStoreAction); ClearRenderTarget(cmd, clearFlag, clearColor); } /// /// Set the current render texture. /// /// CommandBuffer used for rendering commands. /// Color buffer RenderTargetIdentifier. /// Color buffer load action. /// Color buffer store action. /// Depth buffer RenderTargetIdentifier. /// Depth buffer load action. /// Depth buffer store action. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorBuffer, RenderBufferLoadAction colorLoadAction, RenderBufferStoreAction colorStoreAction, RenderTargetIdentifier depthBuffer, RenderBufferLoadAction depthLoadAction, RenderBufferStoreAction depthStoreAction, ClearFlag clearFlag) { SetRenderTarget(cmd, colorBuffer, colorLoadAction, colorStoreAction, depthBuffer, depthLoadAction, depthStoreAction, clearFlag, Color.clear); } private static void SetViewportAndClear(CommandBuffer cmd, RTHandle buffer, ClearFlag clearFlag, Color clearColor) { // Clearing a partial viewport currently does not go through the hardware clear. // Instead it goes through a quad rendered with a specific shader. // When enabling wireframe mode in the scene view, unfortunately it overrides this shader thus breaking every clears. // That's why in the editor we don't set the viewport before clearing (it's set to full screen by the previous SetRenderTarget) but AFTER so that we benefit from un-bugged hardware clear. // We consider that the small loss in performance is acceptable in the editor. // A refactor of wireframe is needed before we can fix this properly (with not doing anything!) #if !UNITY_EDITOR SetViewport(cmd, buffer); #endif CoreUtils.ClearRenderTarget(cmd, clearFlag, clearColor); #if UNITY_EDITOR SetViewport(cmd, buffer); #endif } // This set of RenderTarget management methods is supposed to be used when rendering RTHandle render texture. // This will automatically set the viewport based on the RTHandle System reference size and the RTHandle scaling info. /// /// Setup the current render texture using an RTHandle /// /// CommandBuffer used for rendering commands /// Color buffer RTHandle /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RTHandle buffer, ClearFlag clearFlag, Color clearColor, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { depthSlice = FixupDepthSlice(depthSlice, buffer); cmd.SetRenderTarget(buffer, miplevel, cubemapFace, depthSlice); SetViewportAndClear(cmd, buffer, clearFlag, clearColor); } /// /// Setup the current render texture using an RTHandle /// /// CommandBuffer used for rendering commands /// Color buffer RTHandle /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RTHandle buffer, ClearFlag clearFlag = ClearFlag.None, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) => SetRenderTarget(cmd, buffer, clearFlag, Color.clear, miplevel, cubemapFace, depthSlice); /// /// Setup the current render texture using an RTHandle /// /// CommandBuffer used for rendering commands /// Color buffer RTHandle /// Depth buffer RTHandle /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RTHandle colorBuffer, RTHandle depthBuffer, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { int cw = colorBuffer.rt.width; int ch = colorBuffer.rt.height; int dw = depthBuffer.rt.width; int dh = depthBuffer.rt.height; Debug.Assert(cw == dw && ch == dh); SetRenderTarget(cmd, colorBuffer, depthBuffer, ClearFlag.None, Color.clear, miplevel, cubemapFace, depthSlice); } /// /// Setup the current render texture using an RTHandle /// /// CommandBuffer used for rendering commands /// Color buffer RTHandle /// Depth buffer RTHandle /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RTHandle colorBuffer, RTHandle depthBuffer, ClearFlag clearFlag, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { int cw = colorBuffer.rt.width; int ch = colorBuffer.rt.height; int dw = depthBuffer.rt.width; int dh = depthBuffer.rt.height; Debug.Assert(cw == dw && ch == dh); SetRenderTarget(cmd, colorBuffer, depthBuffer, clearFlag, Color.clear, miplevel, cubemapFace, depthSlice); } /// /// Setup the current render texture using an RTHandle /// /// CommandBuffer used for rendering commands /// Color buffer RTHandle /// Depth buffer RTHandle /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. /// Mip level that should be bound as a render texture if applicable. /// Cubemap face that should be bound as a render texture if applicable. /// Depth slice that should be bound as a render texture if applicable. public static void SetRenderTarget(CommandBuffer cmd, RTHandle colorBuffer, RTHandle depthBuffer, ClearFlag clearFlag, Color clearColor, int miplevel = 0, CubemapFace cubemapFace = CubemapFace.Unknown, int depthSlice = -1) { int cw = colorBuffer.rt.width; int ch = colorBuffer.rt.height; int dw = depthBuffer.rt.width; int dh = depthBuffer.rt.height; Debug.Assert(cw == dw && ch == dh); CoreUtils.SetRenderTarget(cmd, colorBuffer.rt, depthBuffer.rt, miplevel, cubemapFace, depthSlice); SetViewportAndClear(cmd, colorBuffer, clearFlag, clearColor); } /// /// Set the current multiple render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier array of the color render textures. /// Depth Buffer RTHandle. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier[] colorBuffers, RTHandle depthBuffer) { CoreUtils.SetRenderTarget(cmd, colorBuffers, depthBuffer.rt, ClearFlag.None, Color.clear); SetViewport(cmd, depthBuffer); } /// /// Set the current multiple render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier array of the color render textures. /// Depth Buffer RTHandle. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier[] colorBuffers, RTHandle depthBuffer, ClearFlag clearFlag = ClearFlag.None) { CoreUtils.SetRenderTarget(cmd, colorBuffers, depthBuffer.rt); // Don't clear here, viewport needs to be set before we do. SetViewportAndClear(cmd, depthBuffer, clearFlag, Color.clear); } /// /// Set the current multiple render texture. /// /// CommandBuffer used for rendering commands. /// RenderTargetIdentifier array of the color render textures. /// Depth Buffer RTHandle. /// If not set to ClearFlag.None, specifies how to clear the render target after setup. /// If applicable, color with which to clear the render texture after setup. public static void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier[] colorBuffers, RTHandle depthBuffer, ClearFlag clearFlag, Color clearColor) { cmd.SetRenderTarget(colorBuffers, depthBuffer, 0, CubemapFace.Unknown, -1); SetViewportAndClear(cmd, depthBuffer, clearFlag, clearColor); } // Scaling viewport is done for auto-scaling render targets. // In the context of SRP, every auto-scaled RT is scaled against the maximum RTHandles reference size (that can only grow). // When we render using a camera whose viewport is smaller than the RTHandles reference size (and thus smaller than the RT actual size), we need to set it explicitly (otherwise, native code will set the viewport at the size of the RT) // For auto-scaled RTs (like for example a half-resolution RT), we need to scale this viewport accordingly. // For non scaled RTs we just do nothing, the native code will set the viewport at the size of the RT anyway. /// /// Setup the viewport to the size of the provided RTHandle. /// /// CommandBuffer used for rendering commands. /// RTHandle from which to compute the proper viewport. public static void SetViewport(CommandBuffer cmd, RTHandle target) { if (target.useScaling) { Vector2Int scaledViewportSize = target.GetScaledSize(target.rtHandleProperties.currentViewportSize); cmd.SetViewport(new Rect(0.0f, 0.0f, scaledViewportSize.x, scaledViewportSize.y)); } } /// /// Generate a name based on render texture parameters. /// /// With of the texture. /// Height of the texture. /// Depth of the texture. /// Format of the render texture. /// Base name of the texture. /// True if the texture has mip maps. /// True if the texture is multisampled. /// Number of MSAA samples. /// Generated names bassed on the provided parameters. public static string GetRenderTargetAutoName(int width, int height, int depth, RenderTextureFormat format, string name, bool mips = false, bool enableMSAA = false, MSAASamples msaaSamples = MSAASamples.None) => GetRenderTargetAutoName(width, height, depth, format.ToString(), name, mips, enableMSAA, msaaSamples); /// /// Generate a name based on render texture parameters. /// /// With of the texture. /// Height of the texture. /// Depth of the texture. /// Graphics format of the render texture. /// Base name of the texture. /// True if the texture has mip maps. /// True if the texture is multisampled. /// Number of MSAA samples. /// Generated names bassed on the provided parameters. public static string GetRenderTargetAutoName(int width, int height, int depth, GraphicsFormat format, string name, bool mips = false, bool enableMSAA = false, MSAASamples msaaSamples = MSAASamples.None) => GetRenderTargetAutoName(width, height, depth, format.ToString(), name, mips, enableMSAA, msaaSamples); static string GetRenderTargetAutoName(int width, int height, int depth, string format, string name, bool mips = false, bool enableMSAA = false, MSAASamples msaaSamples = MSAASamples.None) { string result = string.Format("{0}_{1}x{2}", name, width, height); if (depth > 1) result = string.Format("{0}x{1}", result, depth); if (mips) result = string.Format("{0}_{1}", result, "Mips"); result = string.Format("{0}_{1}", result, format); if (enableMSAA) result = string.Format("{0}_{1}", result, msaaSamples.ToString()); return result; } /// /// Generate a name based on texture parameters. /// /// With of the texture. /// Height of the texture. /// Format of the texture. /// Dimension of the texture. /// Base name of the texture. /// True if the texture has mip maps. /// Depth of the texture. /// Generated names based on the provided parameters. public static string GetTextureAutoName(int width, int height, TextureFormat format, TextureDimension dim = TextureDimension.None, string name = "", bool mips = false, int depth = 0) => GetTextureAutoName(width, height, format.ToString(), dim, name, mips, depth); /// /// Generate a name based on texture parameters. /// /// With of the texture. /// Height of the texture. /// Graphics format of the texture. /// Dimension of the texture. /// Base name of the texture. /// True if the texture has mip maps. /// Depth of the texture. /// Generated names based on the provided parameters. public static string GetTextureAutoName(int width, int height, GraphicsFormat format, TextureDimension dim = TextureDimension.None, string name = "", bool mips = false, int depth = 0) => GetTextureAutoName(width, height, format.ToString(), dim, name, mips, depth); static string GetTextureAutoName(int width, int height, string format, TextureDimension dim = TextureDimension.None, string name = "", bool mips = false, int depth = 0) { string temp; if (depth == 0) temp = string.Format("{0}x{1}{2}_{3}", width, height, mips ? "_Mips" : "", format); else temp = string.Format("{0}x{1}x{2}{3}_{4}", width, height, depth, mips ? "_Mips" : "", format); temp = String.Format("{0}_{1}_{2}", name == "" ? "Texture" : name, (dim == TextureDimension.None) ? "" : dim.ToString(), temp); return temp; } /// /// Clear a cubemap render texture. /// /// CommandBuffer used for rendering commands. /// Cubemap render texture that needs to be cleared. /// Color used for clearing. /// Set to true to clear the mip maps of the render texture. public static void ClearCubemap(CommandBuffer cmd, RenderTexture renderTexture, Color clearColor, bool clearMips = false) { int mipCount = 1; if (renderTexture.useMipMap && clearMips) { mipCount = (int)Mathf.Log((float)renderTexture.width, 2.0f) + 1; } for (int i = 0; i < 6; ++i) { for (int mip = 0; mip < mipCount; ++mip) { SetRenderTarget(cmd, new RenderTargetIdentifier(renderTexture), ClearFlag.Color, clearColor, mip, (CubemapFace)i); } } } /// /// Draws a full screen triangle. /// /// CommandBuffer used for rendering commands. /// Material used on the full screen triangle. /// Optional material property block for the provided material. /// Index of the material pass. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, MaterialPropertyBlock properties = null, int shaderPassId = 0) { commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } /// /// Draws a full screen triangle. /// /// CommandBuffer used for rendering commands. /// Material used on the full screen triangle. /// RenderTargetIdentifier of the color buffer that needs to be set before drawing the full screen triangle. /// Optional material property block for the provided material. /// Index of the material pass. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, RenderTargetIdentifier colorBuffer, MaterialPropertyBlock properties = null, int shaderPassId = 0) { commandBuffer.SetRenderTarget(colorBuffer); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } /// /// Draws a full screen triangle. /// /// CommandBuffer used for rendering commands. /// Material used on the full screen triangle. /// RenderTargetIdentifier of the color buffer that needs to be set before drawing the full screen triangle. /// RenderTargetIdentifier of the depth buffer that needs to be set before drawing the full screen triangle. /// Optional material property block for the provided material. /// Index of the material pass. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, RenderTargetIdentifier colorBuffer, RenderTargetIdentifier depthStencilBuffer, MaterialPropertyBlock properties = null, int shaderPassId = 0) { commandBuffer.SetRenderTarget(colorBuffer, depthStencilBuffer, 0, CubemapFace.Unknown, -1); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } /// /// Draws a full screen triangle. /// /// CommandBuffer used for rendering commands. /// Material used on the full screen triangle. /// RenderTargetIdentifier array of the color buffers that needs to be set before drawing the full screen triangle. /// RenderTargetIdentifier of the depth buffer that needs to be set before drawing the full screen triangle. /// Optional material property block for the provided material. /// Index of the material pass. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, RenderTargetIdentifier[] colorBuffers, RenderTargetIdentifier depthStencilBuffer, MaterialPropertyBlock properties = null, int shaderPassId = 0) { commandBuffer.SetRenderTarget(colorBuffers, depthStencilBuffer, 0, CubemapFace.Unknown, -1); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } // Important: the first RenderTarget must be created with 0 depth bits! /// /// Draws a full screen triangle. /// /// CommandBuffer used for rendering commands. /// Material used on the full screen triangle. /// RenderTargetIdentifier array of the color buffers that needs to be set before drawing the full screen triangle. /// Optional material property block for the provided material. /// Index of the material pass. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, RenderTargetIdentifier[] colorBuffers, MaterialPropertyBlock properties = null, int shaderPassId = 0) { // It is currently not possible to have MRT without also setting a depth target. // To work around this deficiency of the CommandBuffer.SetRenderTarget() API, // we pass the first color target as the depth target. If it has 0 depth bits, // no depth target ends up being bound. DrawFullScreen(commandBuffer, material, colorBuffers, colorBuffers[0], properties, shaderPassId); } // Color space utilities /// /// Converts the provided sRGB color to the current active color space. /// /// Input color. /// Linear color if the active color space is ColorSpace.Linear, the original input otherwise. public static Color ConvertSRGBToActiveColorSpace(Color color) { return (QualitySettings.activeColorSpace == ColorSpace.Linear) ? color.linear : color; } /// /// Converts the provided linear color to the current active color space. /// /// Input color. /// sRGB color if the active color space is ColorSpace.Gamma, the original input otherwise. public static Color ConvertLinearToActiveColorSpace(Color color) { return (QualitySettings.activeColorSpace == ColorSpace.Linear) ? color : color.gamma; } /// /// Creates a Material with the provided shader path. /// hideFlags will be set to HideFlags.HideAndDontSave. /// /// Path of the shader used for the material. /// A new Material instance using the shader found at the provided path. public static Material CreateEngineMaterial(string shaderPath) { Shader shader = Shader.Find(shaderPath); if (shader == null) { Debug.LogError("Cannot create required material because shader " + shaderPath + " could not be found"); return null; } var mat = new Material(shader) { hideFlags = HideFlags.HideAndDontSave }; return mat; } /// /// Creates a Material with the provided shader. /// hideFlags will be set to HideFlags.HideAndDontSave. /// /// Shader used for the material. /// A new Material instance using the provided shader. public static Material CreateEngineMaterial(Shader shader) { if (shader == null) { Debug.LogError("Cannot create required material because shader is null"); return null; } var mat = new Material(shader) { hideFlags = HideFlags.HideAndDontSave }; return mat; } /// /// Bitfield flag test. /// /// Type of the enum flag. /// Bitfield to test the flag against. /// Flag to be tested against the provided mask. /// True if the flag is present in the mask. public static bool HasFlag(T mask, T flag) where T : IConvertible { return (mask.ToUInt32(null) & flag.ToUInt32(null)) != 0; } /// /// Swaps two values. /// /// Type of the values /// First value. /// Second value. public static void Swap(ref T a, ref T b) { var tmp = a; a = b; b = tmp; } /// /// Set a global keyword using a CommandBuffer /// /// CommandBuffer on which to set the global keyword. /// Keyword to be set. /// Value of the keyword to be set. public static void SetKeyword(CommandBuffer cmd, string keyword, bool state) { if (state) cmd.EnableShaderKeyword(keyword); else cmd.DisableShaderKeyword(keyword); } // Caution: such a call should not be use interlaced with command buffer command, as it is immediate /// /// Set a keyword immediatly on a Material. /// /// Material on which to set the keyword. /// Keyword to set on the material. /// Value of the keyword to set on the material. public static void SetKeyword(Material material, string keyword, bool state) { if (state) material.EnableKeyword(keyword); else material.DisableKeyword(keyword); } /// /// Destroys a UnityObject safely. /// /// Object to be destroyed. public static void Destroy(UnityObject obj) { if (obj != null) { #if UNITY_EDITOR if (Application.isPlaying) UnityObject.Destroy(obj); else UnityObject.DestroyImmediate(obj); #else UnityObject.Destroy(obj); #endif } } static IEnumerable m_AssemblyTypes; /// /// Returns all assembly types. /// /// The list of all assembly types of the current domain. public static IEnumerable GetAllAssemblyTypes() { if (m_AssemblyTypes == null) { m_AssemblyTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(t => { // Ugly hack to handle mis-versioned dlls var innerTypes = new Type[0]; try { innerTypes = t.GetTypes(); } catch {} return innerTypes; }); } return m_AssemblyTypes; } /// /// Returns a list of types that inherit from the provided type. /// /// Parent Type /// A list of types that inherit from the provided type. public static IEnumerable GetAllTypesDerivedFrom() { #if UNITY_EDITOR && UNITY_2019_2_OR_NEWER return UnityEditor.TypeCache.GetTypesDerivedFrom(); #else return GetAllAssemblyTypes().Where(t => t.IsSubclassOf(typeof(T))); #endif } /// /// Safely release a Compute Buffer. /// /// Compute Buffer that needs to be released. public static void SafeRelease(ComputeBuffer buffer) { if (buffer != null) buffer.Release(); } /// /// Creates a cube mesh. /// /// Minimum corner coordinates in local space. /// Maximum corner coordinates in local space. /// A new instance of a cube Mesh. public static Mesh CreateCubeMesh(Vector3 min, Vector3 max) { Mesh mesh = new Mesh(); Vector3[] vertices = new Vector3[8]; vertices[0] = new Vector3(min.x, min.y, min.z); vertices[1] = new Vector3(max.x, min.y, min.z); vertices[2] = new Vector3(max.x, max.y, min.z); vertices[3] = new Vector3(min.x, max.y, min.z); vertices[4] = new Vector3(min.x, min.y, max.z); vertices[5] = new Vector3(max.x, min.y, max.z); vertices[6] = new Vector3(max.x, max.y, max.z); vertices[7] = new Vector3(min.x, max.y, max.z); mesh.vertices = vertices; int[] triangles = new int[36]; triangles[0] = 0; triangles[1] = 2; triangles[2] = 1; triangles[3] = 0; triangles[4] = 3; triangles[5] = 2; triangles[6] = 1; triangles[7] = 6; triangles[8] = 5; triangles[9] = 1; triangles[10] = 2; triangles[11] = 6; triangles[12] = 5; triangles[13] = 7; triangles[14] = 4; triangles[15] = 5; triangles[16] = 6; triangles[17] = 7; triangles[18] = 4; triangles[19] = 3; triangles[20] = 0; triangles[21] = 4; triangles[22] = 7; triangles[23] = 3; triangles[24] = 3; triangles[25] = 6; triangles[26] = 2; triangles[27] = 3; triangles[28] = 7; triangles[29] = 6; triangles[30] = 4; triangles[31] = 1; triangles[32] = 5; triangles[33] = 4; triangles[34] = 0; triangles[35] = 1; mesh.triangles = triangles; return mesh; } /// /// Returns true if "Post Processes" are enabled for the view associated with the given camera. /// /// Input camera. /// True if "Post Processes" are enabled for the view associated with the given camera. public static bool ArePostProcessesEnabled(Camera camera) { bool enabled = true; #if UNITY_EDITOR if (camera.cameraType == CameraType.SceneView) { enabled = false; // Determine whether the "Post Processes" checkbox is checked for the current view. for (int i = 0; i < UnityEditor.SceneView.sceneViews.Count; i++) { var sv = UnityEditor.SceneView.sceneViews[i] as UnityEditor.SceneView; // Post-processing is disabled in scene view if either showImageEffects is disabled or we are // rendering in wireframe mode. if (sv.camera == camera && (sv.sceneViewState.showImageEffects && sv.cameraMode.drawMode != UnityEditor.DrawCameraMode.Wireframe)) { enabled = true; break; } } } #endif return enabled; } /// /// Returns true if "Animated Materials" are enabled for the view associated with the given camera. /// /// Input camera. /// True if "Animated Materials" are enabled for the view associated with the given camera. public static bool AreAnimatedMaterialsEnabled(Camera camera) { bool animateMaterials = true; #if UNITY_EDITOR animateMaterials = Application.isPlaying; // For Game and VR views; Reflection views pass the parent camera if (camera.cameraType == CameraType.SceneView) { animateMaterials = false; // Determine whether the "Animated Materials" checkbox is checked for the current view. for (int i = 0; i < UnityEditor.SceneView.sceneViews.Count; i++) // Using a foreach on an ArrayList generates garbage ... { var sv = UnityEditor.SceneView.sceneViews[i] as UnityEditor.SceneView; if (sv.camera == camera && sv.sceneViewState.showMaterialUpdate) { animateMaterials = true; break; } } } else if (camera.cameraType == CameraType.Preview) { // Enable for previews so the shader graph main preview works with time parameters. animateMaterials = true; } else if (camera.cameraType == CameraType.Reflection) { // Reflection cameras should be handled outside this function. // Debug.Assert(false, "Unexpected View type."); } // IMHO, a better solution would be: // A window invokes a camera render. The camera knows which window called it, so it can query its properies // (such as animated materials). This camera provides the space-time position. It should also be able // to access the rendering settings somehow. Using this information, it is then able to construct the // primary view with information about camera-relative rendering, LOD, time, rendering passes/features // enabled, etc. We then render this view. It can have multiple sub-views (shadows, reflections). // They inherit all the properties of the primary view, but also have the ability to override them // (e.g. primary cam pos and time are retained, matrices are modified, SSS and tessellation are disabled). // These views can then have multiple sub-views (probably not practical for games), // which simply amounts to a recursive call, and then the story repeats itself. // // TLDR: we need to know the caller and its status/properties to make decisions. #endif return animateMaterials; } /// /// Returns true if "Scene Lighting" is enabled for the view associated with the given camera. /// /// Input camera. /// True if "Scene Lighting" is enabled for the view associated with the given camera. public static bool IsSceneLightingDisabled(Camera camera) { bool disabled = false; #if UNITY_EDITOR if (camera.cameraType == CameraType.SceneView) { // Determine whether the "No Scene Lighting" checkbox is checked for the current view. for (int i = 0; i < UnityEditor.SceneView.sceneViews.Count; i++) { var sv = UnityEditor.SceneView.sceneViews[i] as UnityEditor.SceneView; if (sv.camera == camera && !sv.sceneLighting) { disabled = true; break; } } } #endif return disabled; } #if UNITY_EDITOR static Func> materialEditors; static CoreUtils() { //quicker than standard reflection as it is compiled System.Reflection.FieldInfo field = typeof(UnityEditor.MaterialEditor).GetField("s_MaterialEditors", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); var fieldExpression = System.Linq.Expressions.Expression.Field(null, field); var lambda = System.Linq.Expressions.Expression.Lambda>>(fieldExpression); materialEditors = lambda.Compile(); } #endif /// /// Returns true if "Fog" is enabled for the view associated with the given camera. /// /// Input camera. /// True if "Fog" is enabled for the view associated with the given camera. public static bool IsSceneViewFogEnabled(Camera camera) { bool fogEnable = true; #if UNITY_EDITOR if (camera.cameraType == CameraType.SceneView) { fogEnable = false; // Determine whether the "Animated Materials" checkbox is checked for the current view. for (int i = 0; i < UnityEditor.SceneView.sceneViews.Count; i++) { var sv = UnityEditor.SceneView.sceneViews[i] as UnityEditor.SceneView; if (sv.camera == camera && sv.sceneViewState.showFog) { fogEnable = true; break; } } } #endif return fogEnable; } } }