备份CatanBuilding瘦身独立工程

This commit is contained in:
JSD\13999
2026-05-26 16:15:54 +08:00
commit 2d0e6a61b7
12001 changed files with 2431925 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
{
"name": "Coffee.SoftMaskForUGUI",
"rootNamespace": "",
"references": [
"GUID:15fc0a57446b3144c949da3e2b9737a9"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8ef573751495a4f88822a98a88954a0b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fd8b4f97015bf4bb3936f3cf874c89a3
folderAsset: yes
timeCreated: 1539820783
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
{
"name": "Coffee.SoftMaskForUGUI.Editor",
"references": [
"Coffee.SoftMaskForUGUI"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e8f1dfda17cbd45ad95e761ca8c9b120
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
#if UNITY_2018_3_OR_NEWER
using UnityEditor.Experimental.SceneManagement;
#endif
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace Coffee.UISoftMask
{
internal static class EditorUtils
{
internal static void MarkPrefabDirty()
{
#if UNITY_2018_3_OR_NEWER
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
if (prefabStage == null) return;
EditorSceneManager.MarkSceneDirty(prefabStage.scene);
#endif
}
/// <summary>
/// Verify whether it can be converted to the specified component.
/// </summary>
internal static bool CanConvertTo<T>(Object context) where T : MonoBehaviour
{
return context && context.GetType() != typeof(T);
}
/// <summary>
/// Convert to the specified component.
/// </summary>
internal static void ConvertTo<T>(Object context) where T : MonoBehaviour
{
var target = context as MonoBehaviour;
var so = new SerializedObject(target);
so.Update();
var oldEnable = target.enabled;
target.enabled = false;
// Find MonoScript of the specified component.
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
{
if (script.GetClass() != typeof(T))
continue;
// Set 'm_Script' to convert.
so.FindProperty("m_Script").objectReferenceValue = script;
so.ApplyModifiedProperties();
break;
}
(so.targetObject as MonoBehaviour).enabled = oldEnable;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8829eb4004797463480fafa9a1112833
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
#if !UNITY_2019_1_OR_NEWER
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
namespace Coffee.UISoftMask
{
public static class ImportSampleMenu
{
private const string jsonGuid = "c43fd233e88b347cdabc530c23ffe30a";
[MenuItem("Assets/Samples/UISoftMask/Import Demo")]
private static void ImportDemo()
{
ImportSample(jsonGuid, "Demo");
}
[MenuItem("Assets/Samples/UISoftMask/Import TextMeshPro Support")]
private static void ImportTextMeshProSupport()
{
ImportSample(jsonGuid, "TextMeshProSupport");
}
private static void ImportSample(string jsonGuid, string sampleName)
{
var jsonPath = AssetDatabase.GUIDToAssetPath(jsonGuid);
var packageRoot = Path.GetDirectoryName(jsonPath).Replace('\\', '/');
var json = File.ReadAllText(jsonPath);
var version = Regex.Match(json, "\"version\"\\s*:\\s*\"([^\"]+)\"").Groups[1].Value;
var displayName = Regex.Match(json, "\"displayName\"\\s*:\\s*\"([^\"]+)\"").Groups[1].Value;
var src = string.Format("{0}/Samples~/{1}", packageRoot, sampleName);
var srcAlt = string.Format("{0}/Samples/{1}", packageRoot, sampleName);
var dst = string.Format("Assets/Samples/{0}/{1}/{2}", displayName, version, sampleName);
var previousPath = GetPreviousSamplePath(displayName, sampleName);
// Remove the previous sample directory.
if (!string.IsNullOrEmpty(previousPath))
{
var msg = "A different version of the sample is already imported at\n\n"
+ previousPath
+ "\n\nIt will be deleted when you update. Are you sure you want to continue?";
if (!EditorUtility.DisplayDialog("Sample Importer", msg, "OK", "Cancel"))
return;
FileUtil.DeleteFileOrDirectory(previousPath);
var metaFile = previousPath + ".meta";
if (File.Exists(metaFile))
FileUtil.DeleteFileOrDirectory(metaFile);
}
if (!Directory.Exists(dst))
FileUtil.DeleteFileOrDirectory(dst);
var dstDir = Path.GetDirectoryName(dst);
if (!Directory.Exists(dstDir))
Directory.CreateDirectory(dstDir);
if (Directory.Exists(src))
FileUtil.CopyFileOrDirectory(src, dst);
else if (Directory.Exists(srcAlt))
FileUtil.CopyFileOrDirectory(srcAlt, dst);
else
throw new DirectoryNotFoundException(src);
AssetDatabase.Refresh(ImportAssetOptions.ImportRecursive);
}
private static string GetPreviousSamplePath(string displayName, string sampleName)
{
var sampleRoot = string.Format("Assets/Samples/{0}", displayName);
var sampleRootInfo = new DirectoryInfo(sampleRoot);
if (!sampleRootInfo.Exists) return null;
return sampleRootInfo.GetDirectories()
.Select(versionDir => Path.Combine(versionDir.ToString(), sampleName))
.FirstOrDefault(Directory.Exists);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aae59e0ee9bec4e71a10bd5a08f4fba4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System.Linq;
namespace Coffee.UISoftMask
{
/// <summary>
/// SoftMask editor.
/// </summary>
[CustomEditor(typeof(SoftMask))]
[CanEditMultipleObjects]
public class SoftMaskEditor : Editor
{
private const int k_PreviewSize = 128;
private const string k_PrefsPreview = "SoftMaskEditor_Preview";
private static readonly List<Graphic> s_Graphics = new List<Graphic>();
private static bool s_Preview;
private void OnEnable()
{
s_Preview = EditorPrefs.GetBool(k_PrefsPreview, false);
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var current = target as SoftMask;
current.GetComponentsInChildren<Graphic>(true, s_Graphics);
var fixTargets = s_Graphics
.Where(x => x.gameObject != current.gameObject)
.Where(x => !x.GetComponent<SoftMaskable>() && (!x.GetComponent<Mask>() || x.GetComponent<Mask>().showMaskGraphic))
.ToList();
if (0 < fixTargets.Count)
{
GUILayout.BeginHorizontal();
EditorGUILayout.HelpBox("There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.", MessageType.Warning);
GUILayout.BeginVertical();
if (GUILayout.Button("Fix"))
{
foreach (var p in fixTargets)
{
p.gameObject.AddComponent<SoftMaskable>();
}
EditorUtils.MarkPrefabDirty();
}
if (GUILayout.Button("Ping"))
{
EditorGUIUtility.PingObject(fixTargets[0]);
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
var currentImage = current.graphic as Image;
if (currentImage && IsMaskUI(currentImage.sprite))
{
GUILayout.BeginHorizontal();
EditorGUILayout.HelpBox("SoftMask does not recommend to use 'UIMask' sprite as a source image.\n(It contains only small alpha pixels.)\nDo you want to use 'UISprite' instead?", MessageType.Warning);
GUILayout.BeginVertical();
if (GUILayout.Button("Fix"))
{
currentImage.sprite = AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/UISprite.psd");
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
// Preview buffer.
GUILayout.BeginVertical(EditorStyles.helpBox);
if (s_Preview != (s_Preview = EditorGUILayout.ToggleLeft("Preview Soft Mask Buffer", s_Preview)))
{
EditorPrefs.SetBool(k_PrefsPreview, s_Preview);
}
if (s_Preview)
{
var tex = current.softMaskBuffer;
var width = tex.width * k_PreviewSize / tex.height;
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(width, k_PreviewSize), tex, null, ScaleMode.ScaleToFit);
Repaint();
}
GUILayout.EndVertical();
}
private static bool IsMaskUI(Object obj)
{
return obj
&& obj.name == "UIMask"
&& AssetDatabase.GetAssetPath(obj) == "Resources/unity_builtin_extra";
}
//%%%% Context menu for editor %%%%
[MenuItem("CONTEXT/Mask/Convert To SoftMask", true)]
private static bool _ConvertToSoftMask(MenuCommand command)
{
return EditorUtils.CanConvertTo<SoftMask>(command.context);
}
[MenuItem("CONTEXT/Mask/Convert To SoftMask", false)]
private static void ConvertToSoftMask(MenuCommand command)
{
EditorUtils.ConvertTo<SoftMask>(command.context);
}
[MenuItem("CONTEXT/Mask/Convert To Mask", true)]
private static bool _ConvertToMask(MenuCommand command)
{
return EditorUtils.CanConvertTo<Mask>(command.context);
}
[MenuItem("CONTEXT/Mask/Convert To Mask", false)]
private static void ConvertToMask(MenuCommand command)
{
EditorUtils.ConvertTo<Mask>(command.context);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c2615ef08e99d4d898049fb9da8626c6
timeCreated: 1539820794
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,138 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using MaskIntr = UnityEngine.SpriteMaskInteraction;
namespace Coffee.UISoftMask
{
internal enum MaskInteraction : int
{
VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6),
VisibleOutsideMask = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6),
Custom = -1,
}
/// <summary>
/// SoftMaskable editor.
/// </summary>
[CustomEditor(typeof(SoftMaskable))]
[CanEditMultipleObjects]
public class SoftMaskableEditor : Editor
{
private static readonly List<Mask> s_TmpMasks = new List<Mask>();
private static GUIContent s_MaskWarning;
private SerializedProperty _spMaskInteraction;
private bool _custom;
private MaskInteraction maskInteraction
{
get
{
var value = _spMaskInteraction.intValue;
return _custom
? MaskInteraction.Custom
: System.Enum.IsDefined(typeof(MaskInteraction), value)
? (MaskInteraction) value
: MaskInteraction.Custom;
}
set
{
_custom = (value == MaskInteraction.Custom);
if (!_custom)
{
_spMaskInteraction.intValue = (int) value;
}
}
}
private void OnEnable()
{
_spMaskInteraction = serializedObject.FindProperty("m_MaskInteraction");
_custom = (maskInteraction == MaskInteraction.Custom);
if (s_MaskWarning == null)
{
s_MaskWarning = new GUIContent(EditorGUIUtility.FindTexture("console.warnicon.sml"), "This is not a SoftMask component.");
}
}
private void DrawMaskInteractions()
{
var softMaskable = target as SoftMaskable;
if (softMaskable == null) return;
softMaskable.GetComponentsInParent<Mask>(true, s_TmpMasks);
s_TmpMasks.RemoveAll(x => !x.enabled);
s_TmpMasks.Reverse();
maskInteraction = (MaskInteraction) EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
if (!_custom) return;
var l = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 45;
using (var ccs = new EditorGUI.ChangeCheckScope())
{
var intr0 = DrawMaskInteraction(0);
var intr1 = DrawMaskInteraction(1);
var intr2 = DrawMaskInteraction(2);
var intr3 = DrawMaskInteraction(3);
if (ccs.changed)
{
_spMaskInteraction.intValue = (intr0 << 0) + (intr1 << 2) + (intr2 << 4) + (intr3 << 6);
}
}
EditorGUIUtility.labelWidth = l;
}
private int DrawMaskInteraction(int layer)
{
var mask = layer < s_TmpMasks.Count ? s_TmpMasks[layer] : null;
var intr = (MaskIntr) ((_spMaskInteraction.intValue >> layer * 2) & 0x3);
if (!mask)
{
return (int) intr;
}
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(mask is SoftMask ? GUIContent.none : s_MaskWarning, GUILayout.Width(16));
GUILayout.Space(-5);
EditorGUILayout.ObjectField("Mask " + layer, mask, typeof(Mask), false);
GUILayout.Space(-15);
intr = (MaskIntr) EditorGUILayout.EnumPopup(intr);
GUILayout.EndHorizontal();
return (int) intr;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
DrawMaskInteractions();
serializedObject.ApplyModifiedProperties();
var current = target as SoftMaskable;
if (current == null) return;
var mask = current.softMask;
if (mask) return;
GUILayout.BeginHorizontal();
EditorGUILayout.HelpBox("This is unnecessary SoftMaskable.\nCan't find any SoftMask components above.", MessageType.Warning);
if (GUILayout.Button("Remove", GUILayout.Height(40)))
{
DestroyImmediate(current);
EditorUtils.MarkPrefabDirty();
}
GUILayout.EndHorizontal();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e2f5fc58cb78640d9abbb950e92109b6
timeCreated: 1539820794
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Coffee.UISoftMask
{
internal static class GraphicConnectorExtension
{
public static void SetVerticesDirtyEx(this Graphic graphic)
{
GraphicConnector.FindConnector(graphic).SetVerticesDirty(graphic);
}
public static void SetMaterialDirtyEx(this Graphic graphic)
{
GraphicConnector.FindConnector(graphic).SetMaterialDirty(graphic);
}
public static T GetComponentInParentEx<T>(this Component component, bool includeInactive = false) where T : MonoBehaviour
{
if (!component) return null;
var trans = component.transform;
while (trans)
{
var c = trans.GetComponent<T>();
if (c && (includeInactive || c.isActiveAndEnabled)) return c;
trans = trans.parent;
}
return null;
}
}
public class GraphicConnector
{
private static readonly List<GraphicConnector> s_Connectors = new List<GraphicConnector>();
private static readonly Dictionary<Type, GraphicConnector> s_ConnectorMap = new Dictionary<Type, GraphicConnector>();
private static readonly GraphicConnector s_EmptyConnector = new GraphicConnector();
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
#endif
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Init()
{
AddConnector(new GraphicConnector());
}
protected static void AddConnector(GraphicConnector connector)
{
s_Connectors.Add(connector);
s_Connectors.Sort((x, y) => y.priority - x.priority);
}
public static GraphicConnector FindConnector(Graphic graphic)
{
if (!graphic) return s_EmptyConnector;
var type = graphic.GetType();
GraphicConnector connector = null;
if (s_ConnectorMap.TryGetValue(type, out connector)) return connector;
foreach (var c in s_Connectors)
{
if (!c.IsValid(graphic)) continue;
s_ConnectorMap.Add(type, c);
return c;
}
return s_EmptyConnector;
}
/// <summary>
/// Connector priority.
/// </summary>
protected virtual int priority
{
get { return -1; }
}
/// <summary>
/// The connector is valid for the component.
/// </summary>
protected virtual bool IsValid(Graphic graphic)
{
return true;
}
public virtual void SetVerticesDirty(Graphic graphic)
{
if (graphic)
graphic.SetVerticesDirty();
}
public virtual void SetMaterialDirty(Graphic graphic)
{
if (graphic)
graphic.SetMaterialDirty();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e702140c28f4425fac896f9394a31b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
using System.Collections.Generic;
using System;
using UnityEngine;
namespace Coffee.UISoftMask
{
internal class MaterialEntry
{
public Material material;
public int referenceCount;
public void Release()
{
if (material)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEngine.Object.DestroyImmediate(material, false);
else
#endif
UnityEngine.Object.Destroy(material);
}
material = null;
}
}
internal static class MaterialCache
{
static readonly Dictionary<Hash128, MaterialEntry> s_MaterialMap = new Dictionary<Hash128, MaterialEntry>();
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
private static void ClearCache()
{
foreach (var entry in s_MaterialMap.Values)
{
entry.Release();
}
s_MaterialMap.Clear();
}
#endif
public static Material Register(Material material, Hash128 hash, Action<Material> onModify)
{
if (!hash.isValid) return null;
MaterialEntry entry;
if (!s_MaterialMap.TryGetValue(hash, out entry))
{
entry = new MaterialEntry()
{
material = new Material(material)
{
hideFlags = HideFlags.HideAndDontSave,
},
};
onModify(entry.material);
s_MaterialMap.Add(hash, entry);
}
entry.referenceCount++;
//Debug.LogFormat("Register: {0}, {1} (Total: {2})", hash, entry.referenceCount, materialMap.Count);
return entry.material;
}
public static void Unregister(Hash128 hash)
{
MaterialEntry entry;
if (!hash.isValid || !s_MaterialMap.TryGetValue(hash, out entry)) return;
//Debug.LogFormat("Unregister: {0}, {1}", hash, entry.referenceCount -1);
if (--entry.referenceCount > 0) return;
entry.Release();
s_MaterialMap.Remove(hash);
//Debug.LogFormat("Unregister: Release Emtry: {0}, {1} (Total: {2})", hash, entry.referenceCount, materialMap.Count);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be6c8de8d4ec241fdbfad99aca2497d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,788 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace Coffee.UISoftMask
{
/// <summary>
/// Soft mask.
/// Use instead of Mask for smooth masking.
/// </summary>
public class SoftMask : Mask, IMeshModifier
{
/// <summary>
/// Down sampling rate.
/// </summary>
public enum DownSamplingRate
{
None = 0,
x1 = 1,
x2 = 2,
x4 = 4,
x8 = 8,
}
private static readonly List<SoftMask>[] s_TmpSoftMasks = new List<SoftMask>[]
{
new List<SoftMask>(),
new List<SoftMask>(),
new List<SoftMask>(),
new List<SoftMask>(),
};
private static readonly Color[] s_ClearColors = new Color[]
{
new Color(0, 0, 0, 0),
new Color(1, 0, 0, 0),
new Color(1, 1, 0, 0),
new Color(1, 1, 1, 0),
};
private static bool s_UVStartsAtTop;
private static bool s_IsMetal;
private static Shader s_SoftMaskShader;
private static Texture2D s_ReadTexture;
private static readonly List<SoftMask> s_ActiveSoftMasks = new List<SoftMask>();
private static readonly List<SoftMask> s_TempRelatables = new List<SoftMask>();
private static readonly Dictionary<int, Matrix4x4> s_PreviousViewProjectionMatrices = new Dictionary<int, Matrix4x4>();
private static readonly Dictionary<int, Matrix4x4> s_NowViewProjectionMatrices = new Dictionary<int, Matrix4x4>();
private static int s_StencilCompId;
private static int s_ColorMaskId;
private static int s_MainTexId;
private static int s_SoftnessId;
private static int s_Alpha;
private static int s_PreviousWidth;
private static int s_PreviousHeight;
private MaterialPropertyBlock _mpb;
private CommandBuffer _cb;
private Material _material;
private RenderTexture _softMaskBuffer;
private int _stencilDepth;
private Mesh _mesh;
private SoftMask _parent;
internal readonly List<SoftMask> _children = new List<SoftMask>();
private bool _hasChanged = false;
private bool _hasStencilStateChanged = false;
[FormerlySerializedAs("m_DesamplingRate")] [SerializeField, Tooltip("The down sampling rate for soft mask buffer.")]
private DownSamplingRate m_DownSamplingRate = DownSamplingRate.x1;
[SerializeField, Range(0, 1), Tooltip("The value used by the soft mask to select the area of influence defined over the soft mask's graphic.")]
private float m_Softness = 1;
[SerializeField, Range(0f, 1f), Tooltip("The transparency of the whole masked graphic.")]
private float m_Alpha = 1;
[Header("Advanced Options")] [SerializeField, Tooltip("Should the soft mask ignore parent soft masks?")]
private bool m_IgnoreParent = false;
[SerializeField, Tooltip("Is the soft mask a part of parent soft mask?")]
private bool m_PartOfParent = false;
[SerializeField, Tooltip("Self graphic will not be drawn to soft mask buffer.")]
private bool m_IgnoreSelfGraphic;
[SerializeField, Tooltip("Self graphic will not be written to stencil buffer.")]
private bool m_IgnoreSelfStencil;
/// <summary>
/// The down sampling rate for soft mask buffer.
/// </summary>
public DownSamplingRate downSamplingRate
{
get { return m_DownSamplingRate; }
set
{
if (m_DownSamplingRate == value) return;
m_DownSamplingRate = value;
hasChanged = true;
}
}
/// <summary>
/// The value used by the soft mask to select the area of influence defined over the soft mask's graphic.
/// </summary>
public float softness
{
get { return m_Softness; }
set
{
value = Mathf.Clamp01(value);
if (Mathf.Approximately(m_Softness, value)) return;
m_Softness = value;
hasChanged = true;
}
}
/// <summary>
/// The transparency of the whole masked graphic.
/// </summary>
public float alpha
{
get { return m_Alpha; }
set
{
value = Mathf.Clamp01(value);
if (Mathf.Approximately(m_Alpha, value)) return;
m_Alpha = value;
hasChanged = true;
}
}
/// <summary>
/// Should the soft mask ignore parent soft masks?
/// </summary>
/// <value>If set to true the soft mask will ignore any parent soft mask settings.</value>
public bool ignoreParent
{
get { return m_IgnoreParent; }
set
{
if (m_IgnoreParent == value) return;
m_IgnoreParent = value;
hasChanged = true;
OnTransformParentChanged();
}
}
/// <summary>
/// Is the soft mask a part of parent soft mask?
/// </summary>
public bool partOfParent
{
get { return m_PartOfParent; }
set
{
if (m_PartOfParent == value) return;
m_PartOfParent = value;
hasChanged = true;
OnTransformParentChanged();
}
}
/// <summary>
/// The soft mask buffer.
/// </summary>
public RenderTexture softMaskBuffer
{
get
{
if (_parent)
{
ReleaseRt(ref _softMaskBuffer);
return _parent.softMaskBuffer;
}
// Check the size of soft mask buffer.
int w, h;
GetDownSamplingSize(m_DownSamplingRate, out w, out h);
if (_softMaskBuffer && (_softMaskBuffer.width != w || _softMaskBuffer.height != h))
{
ReleaseRt(ref _softMaskBuffer);
}
if (!_softMaskBuffer)
{
_softMaskBuffer = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, 1, RenderTextureMemoryless.Depth);
hasChanged = true;
_hasStencilStateChanged = true;
}
return _softMaskBuffer;
}
}
public bool hasChanged
{
get { return _parent ? _parent.hasChanged : _hasChanged; }
private set
{
if (_parent)
{
_parent.hasChanged = value;
}
_hasChanged = value;
}
}
public SoftMask parent
{
get { return _parent; }
}
public bool ignoreSelfGraphic
{
get { return m_IgnoreSelfGraphic; }
set
{
if (m_IgnoreSelfGraphic == value) return;
m_IgnoreSelfGraphic = value;
hasChanged = true;
graphic.SetVerticesDirtyEx();
}
}
public bool ignoreSelfStencil
{
get { return m_IgnoreSelfStencil; }
set
{
if (m_IgnoreSelfStencil == value) return;
m_IgnoreSelfStencil = value;
hasChanged = true;
graphic.SetVerticesDirtyEx();
graphic.SetMaterialDirtyEx();
}
}
Material material
{
get
{
return _material
? _material
: _material =
new Material(s_SoftMaskShader
? s_SoftMaskShader
: s_SoftMaskShader = Resources.Load<Shader>("SoftMask")) {hideFlags = HideFlags.HideAndDontSave};
}
}
Mesh mesh
{
get { return _mesh ? _mesh : _mesh = new Mesh() {hideFlags = HideFlags.HideAndDontSave}; }
}
/// <summary>
/// Perform material modification in this function.
/// </summary>
/// <returns>Modified material.</returns>
/// <param name="baseMaterial">Configured Material.</param>
public override Material GetModifiedMaterial(Material baseMaterial)
{
hasChanged = true;
if (ignoreSelfStencil) return baseMaterial;
var result = base.GetModifiedMaterial(baseMaterial);
if (m_IgnoreParent && result != baseMaterial)
{
result.SetInt(s_StencilCompId, (int) CompareFunction.Always);
}
return result;
}
/// <summary>
/// Call used to modify mesh.
/// </summary>
void IMeshModifier.ModifyMesh(Mesh mesh)
{
hasChanged = true;
_mesh = mesh;
}
/// <summary>
/// Call used to modify mesh.
/// </summary>
void IMeshModifier.ModifyMesh(VertexHelper verts)
{
if (isActiveAndEnabled)
{
if (ignoreSelfGraphic)
{
verts.Clear();
verts.FillMesh(mesh);
}
else if (ignoreSelfStencil)
{
verts.FillMesh(mesh);
verts.Clear();
}
else
{
verts.FillMesh(mesh);
}
}
hasChanged = true;
}
/// <summary>
/// Given a point and a camera is the raycast valid.
/// </summary>
/// <returns>Valid.</returns>
/// <param name="sp">Screen position.</param>
/// <param name="eventCamera">Raycast camera.</param>
/// <param name="g">Target graphic.</param>
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera, Graphic g, int[] interactions)
{
if (!isActiveAndEnabled || (g == graphic && !g.raycastTarget)) return true;
int x = (int) ((softMaskBuffer.width - 1) * Mathf.Clamp01(sp.x / Screen.width));
int y = s_UVStartsAtTop && !s_IsMetal
? (int) ((softMaskBuffer.height - 1) * (1 - Mathf.Clamp01(sp.y / Screen.height)))
: (int) ((softMaskBuffer.height - 1) * Mathf.Clamp01(sp.y / Screen.height));
return 0.5f < GetPixelValue(x, y, interactions);
}
public override bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
return true;
}
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected override void OnEnable()
{
hasChanged = true;
// Register.
if (s_ActiveSoftMasks.Count == 0)
{
Canvas.willRenderCanvases += UpdateMaskTextures;
if (s_StencilCompId == 0)
{
s_UVStartsAtTop = SystemInfo.graphicsUVStartsAtTop;
s_IsMetal = SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal;
s_StencilCompId = Shader.PropertyToID("_StencilComp");
s_ColorMaskId = Shader.PropertyToID("_ColorMask");
s_MainTexId = Shader.PropertyToID("_MainTex");
s_SoftnessId = Shader.PropertyToID("_Softness");
s_Alpha = Shader.PropertyToID("_Alpha");
}
}
s_ActiveSoftMasks.Add(this);
// Reset the parent-child relation.
GetComponentsInChildren<SoftMask>(false, s_TempRelatables);
for (int i = s_TempRelatables.Count - 1; 0 <= i; i--)
{
s_TempRelatables[i].OnTransformParentChanged();
}
s_TempRelatables.Clear();
// Create objects.
_mpb = new MaterialPropertyBlock();
_cb = new CommandBuffer();
graphic.SetVerticesDirtyEx();
base.OnEnable();
_hasStencilStateChanged = false;
}
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
protected override void OnDisable()
{
// Unregister.
s_ActiveSoftMasks.Remove(this);
if (s_ActiveSoftMasks.Count == 0)
{
Canvas.willRenderCanvases -= UpdateMaskTextures;
}
// Reset the parent-child relation.
for (int i = _children.Count - 1; 0 <= i; i--)
{
_children[i].SetParent(_parent);
}
_children.Clear();
SetParent(null);
// Destroy objects.
_mpb.Clear();
_mpb = null;
_cb.Release();
_cb = null;
ReleaseObject(_mesh);
_mesh = null;
ReleaseObject(_material);
_material = null;
ReleaseRt(ref _softMaskBuffer);
base.OnDisable();
_hasStencilStateChanged = false;
}
/// <summary>
/// This function is called when the parent property of the transform of the GameObject has changed.
/// </summary>
protected override void OnTransformParentChanged()
{
hasChanged = true;
SoftMask newParent = null;
if (isActiveAndEnabled && !m_IgnoreParent)
{
var parentTransform = transform.parent;
while (parentTransform && (!newParent || !newParent.enabled))
{
newParent = parentTransform.GetComponent<SoftMask>();
parentTransform = parentTransform.parent;
}
}
SetParent(newParent);
hasChanged = true;
}
protected override void OnRectTransformDimensionsChange()
{
hasChanged = true;
}
#if UNITY_EDITOR
/// <summary>
/// This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).
/// </summary>
protected override void OnValidate()
{
graphic.SetVerticesDirtyEx();
graphic.SetMaterialDirtyEx();
OnTransformParentChanged();
base.OnValidate();
_hasStencilStateChanged = false;
}
#endif
/// <summary>
/// Update all soft mask textures.
/// </summary>
private static void UpdateMaskTextures()
{
Profiler.BeginSample("UpdateMaskTextures");
foreach (var sm in s_ActiveSoftMasks)
{
if (!sm || sm._hasChanged)
continue;
var canvas = sm.graphic.canvas;
if (!canvas)
continue;
if (canvas.renderMode == RenderMode.WorldSpace)
{
var cam = canvas.worldCamera;
if (!cam)
continue;
Profiler.BeginSample("Check view projection matrix changed (world space)");
var nowVP = cam.projectionMatrix * cam.worldToCameraMatrix;
var previousVP = default(Matrix4x4);
var id = cam.GetInstanceID();
s_PreviousViewProjectionMatrices.TryGetValue(id, out previousVP);
s_NowViewProjectionMatrices[id] = nowVP;
if (previousVP != nowVP)
{
sm.hasChanged = true;
}
Profiler.EndSample();
}
var rt = sm.rectTransform;
if (rt.hasChanged)
{
rt.hasChanged = false;
sm.hasChanged = true;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
{
sm.hasChanged = true;
}
#endif
}
Profiler.BeginSample("Update changed soft masks");
foreach (var sm in s_ActiveSoftMasks)
{
if (!sm || !sm._hasChanged)
continue;
sm._hasChanged = false;
if (sm._parent) continue;
sm.UpdateMaskTexture();
if (!sm._hasStencilStateChanged) continue;
sm._hasStencilStateChanged = false;
Profiler.BeginSample("Notify stencil state changed");
MaskUtilities.NotifyStencilStateChanged(sm);
Profiler.EndSample();
}
Profiler.EndSample();
Profiler.BeginSample("Update previous view projection matrices");
s_PreviousViewProjectionMatrices.Clear();
foreach (var kv in s_NowViewProjectionMatrices)
{
s_PreviousViewProjectionMatrices.Add(kv.Key, kv.Value);
}
s_NowViewProjectionMatrices.Clear();
Profiler.EndSample();
Profiler.EndSample();
#if UNITY_EDITOR
var w = s_PreviousWidth;
var h = s_PreviousHeight;
GetDownSamplingSize(DownSamplingRate.None, out s_PreviousWidth, out s_PreviousHeight);
if (w != s_PreviousWidth || h != s_PreviousHeight)
{
Canvas.ForceUpdateCanvases();
}
#endif
}
/// <summary>
/// Update the mask texture.
/// </summary>
private void UpdateMaskTexture()
{
if (!graphic || !graphic.canvas) return;
Profiler.BeginSample("UpdateMaskTexture");
_stencilDepth = MaskUtilities.GetStencilDepth(transform, MaskUtilities.FindRootSortOverrideCanvas(transform));
// Collect children soft masks.
Profiler.BeginSample("Collect children soft masks");
var depth = 0;
s_TmpSoftMasks[0].Add(this);
while (_stencilDepth + depth < 3)
{
var count = s_TmpSoftMasks[depth].Count;
for (var i = 0; i < count; i++)
{
var children = s_TmpSoftMasks[depth][i]._children;
var childCount = children.Count;
for (var j = 0; j < childCount; j++)
{
var child = children[j];
var childDepth = child.m_PartOfParent ? depth : depth + 1;
s_TmpSoftMasks[childDepth].Add(child);
}
}
depth++;
}
Profiler.EndSample();
// CommandBuffer.
Profiler.BeginSample("Initialize CommandBuffer");
_cb.Clear();
_cb.SetRenderTarget(softMaskBuffer);
_cb.ClearRenderTarget(false, true, s_ClearColors[_stencilDepth]);
Profiler.EndSample();
// Set view and projection matrices.
Profiler.BeginSample("Set view and projection matrices");
var c = graphic.canvas.rootCanvas;
var cam = c.worldCamera ?? Camera.main;
if (c && c.renderMode != RenderMode.ScreenSpaceOverlay && cam)
{
var p = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false);
_cb.SetViewProjectionMatrices(cam.worldToCameraMatrix, p);
}
else
{
var pos = c.transform.position;
var vm = Matrix4x4.TRS(new Vector3(-pos.x, -pos.y, -1000), Quaternion.identity, new Vector3(1, 1, -1f));
var pm = Matrix4x4.TRS(new Vector3(0, 0, -1), Quaternion.identity, new Vector3(1 / pos.x, 1 / pos.y, -2 / 10000f));
_cb.SetViewProjectionMatrices(vm, pm);
}
Profiler.EndSample();
// Draw soft masks.
Profiler.BeginSample("Draw Mesh");
for (var i = 0; i < s_TmpSoftMasks.Length; i++)
{
var count = s_TmpSoftMasks[i].Count;
for (var j = 0; j < count; j++)
{
var sm = s_TmpSoftMasks[i][j];
if (i != 0)
{
sm._stencilDepth = MaskUtilities.GetStencilDepth(sm.transform, MaskUtilities.FindRootSortOverrideCanvas(sm.transform));
}
// Set material property.
sm.material.SetInt(s_ColorMaskId, (int) 1 << (3 - _stencilDepth - i));
sm._mpb.SetTexture(s_MainTexId, sm.graphic.mainTexture);
sm._mpb.SetFloat(s_SoftnessId, sm.m_Softness);
sm._mpb.SetFloat(s_Alpha, sm.m_Alpha);
// Draw mesh.
_cb.DrawMesh(sm.mesh, sm.transform.localToWorldMatrix, sm.material, 0, 0, sm._mpb);
}
s_TmpSoftMasks[i].Clear();
}
Profiler.EndSample();
Graphics.ExecuteCommandBuffer(_cb);
Profiler.EndSample();
}
/// <summary>
/// Gets the size of the down sampling.
/// </summary>
private static void GetDownSamplingSize(DownSamplingRate rate, out int w, out int h)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
var res = UnityEditor.UnityStats.screenRes.Split('x');
w = Mathf.Max(64, int.Parse(res[0]));
h = Mathf.Max(64, int.Parse(res[1]));
}
else
#endif
if (Screen.fullScreenMode == FullScreenMode.Windowed)
{
w = Screen.width;
h = Screen.height;
}
else
{
w = Screen.currentResolution.width;
h = Screen.currentResolution.height;
}
if (rate == DownSamplingRate.None)
return;
var aspect = (float) w / h;
if (w < h)
{
h = Mathf.ClosestPowerOfTwo(h / (int) rate);
w = Mathf.CeilToInt(h * aspect);
}
else
{
w = Mathf.ClosestPowerOfTwo(w / (int) rate);
h = Mathf.CeilToInt(w / aspect);
}
}
/// <summary>
/// Release the specified obj.
/// </summary>
/// <param name="tmpRT">Object.</param>
private static void ReleaseRt(ref RenderTexture tmpRT)
{
if (!tmpRT) return;
tmpRT.Release();
RenderTexture.ReleaseTemporary(tmpRT);
tmpRT = null;
}
/// <summary>
/// Release the specified obj.
/// </summary>
/// <param name="obj">Object.</param>
private static void ReleaseObject(Object obj)
{
if (!obj) return;
#if UNITY_EDITOR
if (!Application.isPlaying)
DestroyImmediate(obj);
else
#endif
Destroy(obj);
}
/// <summary>
/// Set the parent of the soft mask.
/// </summary>
/// <param name="newParent">The parent soft mask to use.</param>
private void SetParent(SoftMask newParent)
{
if (_parent != newParent && this != newParent)
{
if (_parent && _parent._children.Contains(this))
{
_parent._children.Remove(this);
_parent._children.RemoveAll(x => x == null);
}
_parent = newParent;
}
if (_parent && !_parent._children.Contains(this))
{
_parent._children.Add(this);
}
}
/// <summary>
/// Gets the pixel value.
/// </summary>
private float GetPixelValue(int x, int y, int[] interactions)
{
if (!s_ReadTexture)
{
s_ReadTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
}
var currentRt = RenderTexture.active;
RenderTexture.active = softMaskBuffer;
s_ReadTexture.ReadPixels(new Rect(x, y, 1, 1), 0, 0);
s_ReadTexture.Apply(false, false);
RenderTexture.active = currentRt;
var colors = s_ReadTexture.GetRawTextureData();
for (int i = 0; i < 4; i++)
{
switch (interactions[(i + 3) % 4])
{
case 0:
colors[i] = 255;
break;
case 2:
colors[i] = (byte) (255 - colors[i]);
break;
}
}
switch (_stencilDepth)
{
case 0: return (colors[1] / 255f);
case 1: return (colors[1] / 255f) * (colors[2] / 255f);
case 2: return (colors[1] / 255f) * (colors[2] / 255f) * (colors[3] / 255f);
case 3: return (colors[1] / 255f) * (colors[2] / 255f) * (colors[3] / 255f) * (colors[0] / 255f);
default: return 0;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 385b7d1277b6c4007a84c065696e0f8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,352 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.UI;
using MaskIntr = UnityEngine.SpriteMaskInteraction;
namespace Coffee.UISoftMask
{
/// <summary>
/// Soft maskable.
/// Add this component to Graphic under SoftMask for smooth masking.
/// </summary>
#if UNITY_2018_3_OR_NEWER
[ExecuteAlways]
#else
[ExecuteInEditMode]
# endif
[RequireComponent(typeof(Graphic))]
public class SoftMaskable : MonoBehaviour, IMaterialModifier, ICanvasRaycastFilter
#if UNITY_EDITOR
, ISerializationCallbackReceiver
# endif
{
private const int kVisibleInside = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6);
private const int kVisibleOutside = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6);
private static readonly Hash128 k_InvalidHash = new Hash128();
private static int s_RenderScale;
private static int s_SoftMaskTexId;
private static int s_StencilCompId;
private static int s_MaskInteractionId;
private static int s_GameVPId;
private static int s_GameTVPId;
private static List<SoftMaskable> s_ActiveSoftMaskables;
private static int[] s_Interactions = new int[4];
[SerializeField, HideInInspector, System.Obsolete]
private bool m_Inverse;
[SerializeField, Tooltip("The interaction for each masks."), HideInInspector]
private int m_MaskInteraction = kVisibleInside;
[SerializeField, Tooltip("Use stencil to mask.")]
private bool m_UseStencil = true;
[SerializeField, Tooltip("Use soft-masked raycast target.\n\nNote: This option is expensive.")]
private bool m_RaycastFilter;
private Graphic _graphic;
private SoftMask _softMask;
private Hash128 _effectMaterialHash;
/// <summary>
/// The graphic will be visible only in areas where no mask is present.
/// </summary>
public bool inverse
{
get { return m_MaskInteraction == kVisibleOutside; }
set
{
var intValue = value ? kVisibleOutside : kVisibleInside;
if (m_MaskInteraction == intValue) return;
m_MaskInteraction = intValue;
graphic.SetMaterialDirtyEx();
}
}
/// <summary>
/// Use soft-masked raycast target. This option is expensive.
/// </summary>
public bool raycastFilter
{
get { return m_RaycastFilter; }
set { m_RaycastFilter = value; }
}
/// <summary>
/// Use stencil to mask.
/// </summary>
public bool useStencil
{
get { return m_UseStencil; }
set
{
if (m_UseStencil == value) return;
m_UseStencil = value;
graphic.SetMaterialDirtyEx();
}
}
/// <summary>
/// The graphic associated with the soft mask.
/// </summary>
public Graphic graphic
{
get { return _graphic ? _graphic : _graphic = GetComponent<Graphic>(); }
}
public SoftMask softMask
{
get { return _softMask ? _softMask : _softMask = this.GetComponentInParentEx<SoftMask>(); }
}
public Material modifiedMaterial { get; private set; }
/// <summary>
/// Perform material modification in this function.
/// </summary>
/// <returns>Modified material.</returns>
/// <param name="baseMaterial">Configured Material.</param>
Material IMaterialModifier.GetModifiedMaterial(Material baseMaterial)
{
_softMask = null;
modifiedMaterial = null;
// If this component is disabled, the material is returned as is.
// If the parents do not have a soft mask component, the material is returned as is.
if (!isActiveAndEnabled || !softMask)
{
MaterialCache.Unregister(_effectMaterialHash);
_effectMaterialHash = k_InvalidHash;
return baseMaterial;
}
// Generate soft maskable material.
var previousHash = _effectMaterialHash;
_effectMaterialHash = new Hash128(
(uint) baseMaterial.GetInstanceID(),
(uint) softMask.GetInstanceID(),
(uint) m_MaskInteraction,
(uint) (m_UseStencil ? 1 : 0)
);
// Generate soft maskable material.
modifiedMaterial = MaterialCache.Register(baseMaterial, _effectMaterialHash, mat =>
{
mat.shader = Shader.Find(string.Format("Hidden/{0} (SoftMaskable)", mat.shader.name));
mat.SetFloat(s_RenderScale, UniversalRenderPipeline.asset.renderScale);
mat.SetTexture(s_SoftMaskTexId, softMask.softMaskBuffer);
mat.SetInt(s_StencilCompId, m_UseStencil ? (int) CompareFunction.Equal : (int) CompareFunction.Always);
#if UNITY_EDITOR
mat.EnableKeyword("SOFTMASK_EDITOR");
UpdateMaterialForSceneView(mat);
#endif
var root = MaskUtilities.FindRootSortOverrideCanvas(transform);
var stencil = MaskUtilities.GetStencilDepth(transform, root);
mat.SetVector(s_MaskInteractionId, new Vector4(
1 <= stencil ? (m_MaskInteraction >> 0 & 0x3) : 0,
2 <= stencil ? (m_MaskInteraction >> 2 & 0x3) : 0,
3 <= stencil ? (m_MaskInteraction >> 4 & 0x3) : 0,
4 <= stencil ? (m_MaskInteraction >> 6 & 0x3) : 0
));
});
// Unregister the previous material.
MaterialCache.Unregister(previousHash);
return modifiedMaterial;
}
/// <summary>
/// Given a point and a camera is the raycast valid.
/// </summary>
/// <returns>Valid.</returns>
/// <param name="sp">Screen position.</param>
/// <param name="eventCamera">Raycast camera.</param>
bool ICanvasRaycastFilter.IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (!isActiveAndEnabled || !softMask)
return true;
if (!RectTransformUtility.RectangleContainsScreenPoint(transform as RectTransform, sp, eventCamera))
return false;
if (m_RaycastFilter)
{
var sm = _softMask;
for (var i = 0; i < 4; i++)
{
s_Interactions[i] = sm ? ((m_MaskInteraction >> i * 2) & 0x3) : 0;
sm = sm ? sm.parent : null;
}
return _softMask.IsRaycastLocationValid(sp, eventCamera, graphic, s_Interactions);
}
else
{
var sm = _softMask;
for (var i = 0; i < 4; i++)
{
if (!sm) break;
s_Interactions[i] = sm ? ((m_MaskInteraction >> i * 2) & 0x3) : 0;
var interaction = s_Interactions[i] == 1;
var rt = sm.transform as RectTransform;
var inRect = RectTransformUtility.RectangleContainsScreenPoint(rt, sp, eventCamera);
if (!sm.ignoreSelfGraphic && interaction != inRect) return false;
foreach (var child in sm._children)
{
if (!child) continue;
var childRt = child.transform as RectTransform;
var inRectChild = RectTransformUtility.RectangleContainsScreenPoint(childRt, sp, eventCamera);
if (!child.ignoreSelfGraphic && interaction != inRectChild) return false;
}
sm = sm ? sm.parent : null;
}
return true;
}
}
/// <summary>
/// Set the interaction for each mask.
/// </summary>
public void SetMaskInteraction(MaskIntr intr)
{
SetMaskInteraction(intr, intr, intr, intr);
}
/// <summary>
/// Set the interaction for each mask.
/// </summary>
public void SetMaskInteraction(MaskIntr layer0, MaskIntr layer1, MaskIntr layer2, MaskIntr layer3)
{
m_MaskInteraction = (int) layer0 + ((int) layer1 << 2) + ((int) layer2 << 4) + ((int) layer3 << 6);
graphic.SetMaterialDirtyEx();
}
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
private void OnEnable()
{
// Register.
if (s_ActiveSoftMaskables == null)
{
s_ActiveSoftMaskables = new List<SoftMaskable>();
s_RenderScale = Shader.PropertyToID("_RenderScale");
s_SoftMaskTexId = Shader.PropertyToID("_SoftMaskTex");
s_StencilCompId = Shader.PropertyToID("_StencilComp");
s_MaskInteractionId = Shader.PropertyToID("_MaskInteraction");
#if UNITY_EDITOR
s_GameVPId = Shader.PropertyToID("_GameVP");
s_GameTVPId = Shader.PropertyToID("_GameTVP");
#endif
}
s_ActiveSoftMaskables.Add(this);
graphic.SetMaterialDirtyEx();
_softMask = null;
}
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
private void OnDisable()
{
s_ActiveSoftMaskables.Remove(this);
graphic.SetMaterialDirtyEx();
_softMask = null;
MaterialCache.Unregister(_effectMaterialHash);
_effectMaterialHash = k_InvalidHash;
}
#if UNITY_EDITOR
private void UpdateMaterialForSceneView(Material mat)
{
if(!mat || !graphic || !graphic.canvas || !mat.shader || !mat.shader.name.EndsWith(" (SoftMaskable)")) return;
// Set view and projection matrices.
Profiler.BeginSample("Set view and projection matrices");
var c = graphic.canvas.rootCanvas;
var cam = c.worldCamera ?? Camera.main;
if (c && c.renderMode != RenderMode.ScreenSpaceOverlay && cam)
{
var p = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false);
var pv = p * cam.worldToCameraMatrix;
mat.SetMatrix(s_GameVPId, pv);
mat.SetMatrix(s_GameTVPId, pv);
}
else
{
var pos = c.transform.position;
var scale = c.transform.localScale.x;
var size = (c.transform as RectTransform).sizeDelta;
var gameVp = Matrix4x4.TRS(new Vector3(0, 0, 0.5f), Quaternion.identity, new Vector3(2 / size.x, 2 / size.y, 0.0005f * scale));
var gameTvp = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(1 / pos.x, 1 / pos.y, -2 / 2000f)) * Matrix4x4.Translate(-pos);
mat.SetMatrix(s_GameVPId, gameVp);
mat.SetMatrix(s_GameTVPId, gameTvp);
}
Profiler.EndSample();
}
private void LateUpdate()
{
UpdateMaterialForSceneView(modifiedMaterial);
}
/// <summary>
/// This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).
/// </summary>
private void OnValidate()
{
graphic.SetMaterialDirtyEx();
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
#pragma warning disable 0612
if (m_Inverse)
{
m_Inverse = false;
m_MaskInteraction = kVisibleOutside;
}
#pragma warning restore 0612
var current = this;
UnityEditor.EditorApplication.delayCall += () =>
{
if (!current) return;
if (!graphic) return;
if (graphic.name.Contains("TMP SubMeshUI")) return;
if (!graphic.material) return;
if (!graphic.material.shader) return;
if (graphic.material.shader.name != "Hidden/UI/Default (SoftMaskable)") return;
graphic.material = null;
graphic.SetMaterialDirtyEx();
};
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97bc2ebab6563400c95b036136d26ea6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: