using Coffee.UISoftMaskInternal; using UnityEditor; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Rendering; #if URP_ENABLE using UnityEngine.Rendering.Universal; #endif namespace Coffee.UISoftMask { /// /// Utility class containing various functions and resources for soft masking. /// internal static class SoftMaskUtils { /// /// Object pool for CommandBuffer instances. /// public static readonly InternalObjectPool commandBufferPool = new InternalObjectPool( () => new CommandBuffer(), x => x != null, x => x.Clear()); /// /// Object pool for MaterialPropertyBlock instances. /// public static readonly InternalObjectPool materialPropertyBlockPool = new InternalObjectPool( () => new MaterialPropertyBlock(), x => x != null, x => x.Clear()); private static Material s_SoftMaskingMaterialAdd; private static Material s_SoftMaskingMaterialSub; private static readonly int s_MainTex = Shader.PropertyToID("_MainTex"); private static readonly int s_ColorMask = Shader.PropertyToID("_ColorMask"); private static readonly int s_BlendOp = Shader.PropertyToID("_BlendOp"); private static readonly int s_ThresholdMin = Shader.PropertyToID("_ThresholdMin"); private static readonly int s_ThresholdMax = Shader.PropertyToID("_ThresholdMax"); private static readonly int s_RenderScale = Shader.PropertyToID("_RenderScale"); private static readonly int s_DynamicResolutionScale = Shader.PropertyToID("_DynamicResolutionScale"); private static float s_CurrentRenderScale = 1; private static Vector2 s_CurrentDynamicResolutionScale = Vector2.one; #if UNITY_EDITOR [InitializeOnLoadMethod] #else [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] #endif private static void InitializeOnLoadMethod() { s_CurrentRenderScale = 1; Shader.SetGlobalFloat(s_RenderScale, s_CurrentRenderScale); #if URP_ENABLE UIExtraCallbacks.onBeforeCanvasRebuild += () => { // Discard variations lesser than 0.05f. var renderScale = 1f; if (GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset urpAsset && 0.05f < Mathf.Abs(urpAsset.renderScale - 1)) { renderScale = urpAsset.renderScale; } if (!Mathf.Approximately(s_CurrentRenderScale, renderScale)) { s_CurrentRenderScale = renderScale; Shader.SetGlobalFloat(s_RenderScale, s_CurrentRenderScale); } }; #endif UIExtraCallbacks.onBeforeCanvasRebuild += () => { var s = new Vector2(ScalableBufferManager.widthScaleFactor, ScalableBufferManager.heightScaleFactor); s.x = Mathf.Clamp(Mathf.CeilToInt(s.x / 0.05f) * 0.05f, 0.25f, 1.0f); s.y = Mathf.Clamp(Mathf.CeilToInt(s.y / 0.05f) * 0.05f, 0.25f, 1.0f); #if URP_ENABLE if (GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset urpAsset && Mathf.Abs(urpAsset.renderScale - 1) <= 0.05f) { s = Vector2.one; } #endif if (s_CurrentDynamicResolutionScale != s) { s_CurrentDynamicResolutionScale = s; Shader.SetGlobalVector(s_DynamicResolutionScale, s_CurrentDynamicResolutionScale); } }; #if UNITY_EDITOR if (!Misc.isBatchOrBuilding) { // Enable the 'SOFTMASK_EDITOR' keyword only when drawing in the scene view camera. RenderPipelineManager.beginCameraRendering += (_, c) => EnableSoftMaskEditor(true, c); RenderPipelineManager.endCameraRendering += (_, c) => EnableSoftMaskEditor(false, c); Camera.onPreRender += c => EnableSoftMaskEditor(true, c); Camera.onPostRender += c => EnableSoftMaskEditor(false, c); void EnableSoftMaskEditor(bool begin, Camera cam) { if (cam.cameraType == CameraType.SceneView) { if (begin) { Shader.EnableKeyword("SOFTMASK_EDITOR"); } else { Shader.DisableKeyword("SOFTMASK_EDITOR"); } } } } #endif } /// /// Applies properties to a MaterialPropertyBlock for soft masking. /// public static void ApplyMaterialPropertyBlock(MaterialPropertyBlock mpb, int depth, Texture texture, MinMax01 threshold, float alpha) { Profiler.BeginSample("(SM4UI)[SoftMaskUtils] ApplyMaterialPropertyBlock"); var colorMask = Vector4.zero; colorMask[depth] = alpha; mpb.SetVector(s_ColorMask, colorMask); mpb.SetTexture(s_MainTex, texture ? texture : null); mpb.SetFloat(s_ThresholdMin, threshold.min); mpb.SetFloat(s_ThresholdMax, threshold.max); Profiler.EndSample(); } /// /// Gets the soft masking material based on the masking method. /// public static Material GetSoftMaskingMaterial(MaskingShape.MaskingMethod method) { return method == MaskingShape.MaskingMethod.Additive ? GetSoftMaskingMaterial(ref s_SoftMaskingMaterialAdd, BlendOp.Add) : GetSoftMaskingMaterial(ref s_SoftMaskingMaterialSub, BlendOp.ReverseSubtract); } /// /// Gets or creates the soft masking material with a specific blend operation. /// private static Material GetSoftMaskingMaterial(ref Material mat, BlendOp op) { if (mat) return mat; mat = new Material(Shader.Find("Hidden/UI/SoftMask")) { hideFlags = HideFlags.DontSave | HideFlags.NotEditable }; mat.SetInt(s_BlendOp, (int)op); #if UNITY_EDITOR UISoftMaskProjectSettings.shaderRegistry.RegisterVariant(mat, "UI > Soft Mask"); #endif return mat; } /// /// Adds SoftMaskable components to all children of the specified MonoBehaviour. /// public static void AddSoftMaskableOnChildren(MonoBehaviour self, bool includeSelf) { if (!self || !self.isActiveAndEnabled) return; if (!UISoftMaskProjectSettings.addSoftMaskableAutomatically) return; self.AddComponentOnChildren(includeSelf); } } }