123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- using System;
- using UnityEditor;
- using UnityEngine;
- using UnityEngine.Assertions;
- namespace SoftMasking.Editor {
- [CustomEditor(typeof(SoftMask))]
- [CanEditMultipleObjects]
- public class SoftMaskEditor : UnityEditor.Editor {
- SerializedProperty _source;
- SerializedProperty _separateMask;
- SerializedProperty _sprite;
- SerializedProperty _spriteBorderMode;
- SerializedProperty _spritePixelsPerUnitMultiplier;
- SerializedProperty _texture;
- SerializedProperty _textureUVRect;
- SerializedProperty _channelWeights;
- SerializedProperty _raycastThreshold;
- SerializedProperty _invertMask;
- SerializedProperty _invertOutsides;
- bool _customWeightsExpanded;
-
- static class Labels {
- public static readonly GUIContent Source = new GUIContent("Source",
- "Specifies from where the mask gets its image.");
- public static readonly GUIContent Sprite = new GUIContent("Sprite",
- "Sprite that should be used as the mask image.");
- public static readonly GUIContent SpriteBorderMode = new GUIContent("Sprite Border Mode",
- "Specifies how sprite borders should be processed. Corresponds to Unity UI Image " +
- "types. The Sliced and Tiled modes are available only for sprites having borders.");
- public static readonly GUIContent SpritePixelsPerUnitMultiplier = new GUIContent("Pixels Per Unit Multiplier",
- "Specifies a multiplier that is applied to Sprite's Pixels Per Unit property.");
- public static readonly GUIContent Texture = new GUIContent("Texture",
- "Texture that should be used as the mask image.");
- public static readonly GUIContent TextureUVRect = new GUIContent("Texture UV Rect",
- "Specifies a normalized rectangle in UV-space defining an image part that " +
- "should be used as the mask image.");
- public static readonly GUIContent SeparateMask = new GUIContent("Separate Mask",
- "A Rect Transform that defines the bounds of the mask in the scene. If not set " +
- "bounds of Rect Transform attached to this object is used. Use of a separate Rect " +
- "Transform allows to move or resize mask without affecting children.");
- public static readonly GUIContent RaycastThreshold = new GUIContent("Raycast Threshold",
- "Specifies the minimum value that the mask should have at any given point to " +
- "pass a pointer input event to children. 0 makes the entire mask rectangle pass " +
- "events. 1 passes events only in points where masked objects are fully opaque.");
- public static readonly GUIContent MaskChannel = new GUIContent("Mask Channel",
- "Specifies which image channel(s) should be used as the mask source.");
- public static readonly GUIContent ChannelWeights = new GUIContent("Channel Weights",
- "Specifies how much value each of image channels adds to the resulted mask value. " +
- "The resulted mask value is calculated as the sum of products of image channel " +
- "values and the corresponding fields");
- public static readonly GUIContent R = new GUIContent("R");
- public static readonly GUIContent G = new GUIContent("G");
- public static readonly GUIContent B = new GUIContent("B");
- public static readonly GUIContent A = new GUIContent("A");
- public static readonly GUIContent InvertMask = new GUIContent("Invert Mask",
- "If set, the mask inside the mask rectangle will be inverted.");
- public static readonly GUIContent InvertOutsides = new GUIContent("Invert Outsides",
- "If set, the mask outside the mask rectangle will be inverted. By default everything " +
- "outside the mask is transparent, so when this flag is set, the outsides of the mask " +
- "are opaque.");
- public static readonly string UnsupportedShaders =
- "Some of children's shaders aren't supported. Mask won't work on these elements. " +
- "See the documentation for more details about how to add Soft Mask support to " +
- "custom shaders.";
- public static readonly string NestedMasks =
- "The mask may work not as expected because a child or parent Soft Mask exists. " +
- "Soft Mask doesn't support nesting. You can work around this limitation by nesting " +
- "a Soft Mask into a Unity standard Mask or Rect Mask 2D or vice versa.";
- public static readonly string TightPackedSprite =
- "Soft Mask doesn't support tight packed sprites. Disable packing for the mask sprite " +
- "or use Rectangle pack mode.";
- public static readonly string AlphaSplitSprite =
- "Soft Mask doesn't support sprites with an alpha split texture. Disable compression of " +
- "the sprite texture or use another compression type.";
- public static readonly string UnsupportedImageType =
- "Soft Mask doesn't support this image type. The supported image types are Simple, Sliced " +
- "and Tiled.";
- public static readonly string UnreadableTexture =
- "Soft Mask with Raycast Threshold greater than zero can't be used with a CPU-unreadable " +
- "texture. You can make the texture readable in the Texture Import Settings.";
- public static readonly string UnreadableRenderTexture =
- "Soft Mask with Raycast Threshold greater than zero can't be used with Render Textures " +
- "as they're unreadable for CPU. Only a Texture 2D may be used with the raycast filtering " +
- "capability.";
- }
- public void OnEnable() {
- _source = serializedObject.FindProperty("_source");
- _separateMask = serializedObject.FindProperty("_separateMask");
- _sprite = serializedObject.FindProperty("_sprite");
- _spriteBorderMode = serializedObject.FindProperty("_spriteBorderMode");
- _spritePixelsPerUnitMultiplier = serializedObject.FindProperty("_spritePixelsPerUnitMultiplier");
- _texture = serializedObject.FindProperty("_texture");
- _textureUVRect = serializedObject.FindProperty("_textureUVRect");
- _channelWeights = serializedObject.FindProperty("_channelWeights");
- _raycastThreshold = serializedObject.FindProperty("_raycastThreshold");
- _invertMask = serializedObject.FindProperty("_invertMask");
- _invertOutsides = serializedObject.FindProperty("_invertOutsides");
- Assert.IsNotNull(_source);
- Assert.IsNotNull(_separateMask);
- Assert.IsNotNull(_sprite);
- Assert.IsNotNull(_spriteBorderMode);
- Assert.IsNotNull(_spritePixelsPerUnitMultiplier);
- Assert.IsNotNull(_texture);
- Assert.IsNotNull(_textureUVRect);
- Assert.IsNotNull(_channelWeights);
- Assert.IsNotNull(_raycastThreshold);
- Assert.IsNotNull(_invertMask);
- Assert.IsNotNull(_invertOutsides);
- _customWeightsExpanded = CustomEditors.CustomChannelShouldBeExpandedFor(_channelWeights.colorValue);
- }
- public override void OnInspectorGUI() {
- serializedObject.Update();
- EditorGUILayout.PropertyField(_source, Labels.Source);
- CustomEditors.WithIndent(() => {
- switch ((SoftMask.MaskSource)_source.enumValueIndex) {
- case SoftMask.MaskSource.Graphic:
- break;
- case SoftMask.MaskSource.Sprite:
- EditorGUILayout.PropertyField(_sprite, Labels.Sprite);
- EditorGUILayout.PropertyField(_spriteBorderMode, Labels.SpriteBorderMode);
- if ((SoftMask.BorderMode)_spriteBorderMode.enumValueIndex != SoftMask.BorderMode.Simple)
- EditorGUILayout.PropertyField(_spritePixelsPerUnitMultiplier, Labels.SpritePixelsPerUnitMultiplier);
- break;
- case SoftMask.MaskSource.Texture:
- EditorGUILayout.PropertyField(_texture, Labels.Texture);
- EditorGUILayout.PropertyField(_textureUVRect, Labels.TextureUVRect);
- break;
- }
- });
- EditorGUILayout.PropertyField(_separateMask, Labels.SeparateMask);
- EditorGUILayout.Slider(_raycastThreshold, 0, 1, Labels.RaycastThreshold);
- EditorGUILayout.PropertyField(_invertMask, Labels.InvertMask);
- EditorGUILayout.PropertyField(_invertOutsides, Labels.InvertOutsides);
- var newExpanded = CustomEditors.ChannelWeights(Labels.MaskChannel, _channelWeights, _customWeightsExpanded);
- if (newExpanded != _customWeightsExpanded) {
- _customWeightsExpanded = newExpanded;
- Repaint();
- }
- ShowErrorsIfAny();
- serializedObject.ApplyModifiedProperties();
- }
- void ShowErrorsIfAny() {
- var errors = CollectErrors();
- ShowErrorIfPresent(errors, SoftMask.Errors.UnsupportedShaders, Labels.UnsupportedShaders, MessageType.Warning);
- ShowErrorIfPresent(errors, SoftMask.Errors.NestedMasks, Labels.NestedMasks, MessageType.Warning);
- ShowErrorIfPresent(errors, SoftMask.Errors.TightPackedSprite, Labels.TightPackedSprite, MessageType.Error);
- ShowErrorIfPresent(errors, SoftMask.Errors.AlphaSplitSprite, Labels.AlphaSplitSprite, MessageType.Error);
- ShowErrorIfPresent(errors, SoftMask.Errors.UnsupportedImageType, Labels.UnsupportedImageType, MessageType.Error);
- ShowErrorIfPresent(errors, SoftMask.Errors.UnreadableTexture, Labels.UnreadableTexture, MessageType.Error);
- ShowErrorIfPresent(errors, SoftMask.Errors.UnreadableRenderTexture, Labels.UnreadableRenderTexture, MessageType.Error);
- }
- SoftMask.Errors CollectErrors() {
- var result = SoftMask.Errors.NoError;
- foreach (var t in targets)
- result |= ((SoftMask)t).PollErrors();
- return result;
- }
- static void ShowErrorIfPresent(
- SoftMask.Errors actualErrors,
- SoftMask.Errors expectedError,
- string errorMessage,
- MessageType errorMessageType) {
- if ((actualErrors & expectedError) != 0)
- EditorGUILayout.HelpBox(errorMessage, errorMessageType);
- }
- public static class CustomEditors {
- public static bool ChannelWeights(GUIContent label, SerializedProperty weightsProp, bool customWeightsExpanded) {
- var rect = GUILayoutUtility.GetRect(GUIContent.none, KnownChannelStyle);
- if (customWeightsExpanded)
- rect.max = GUILayoutUtility.GetRect(GUIContent.none, CustomWeightsStyle).max;
- return ChannelWeights(rect, label, weightsProp, customWeightsExpanded);
- }
- public static void WithIndent(Action f) {
- ++EditorGUI.indentLevel;
- try {
- f();
- } finally {
- --EditorGUI.indentLevel;
- }
- }
- static GUIStyle KnownChannelStyle { get { return EditorStyles.popup; } }
- static GUIStyle CustomWeightsStyle { get { return EditorStyles.textField; } }
- static bool ChannelWeights(Rect rect, GUIContent label, SerializedProperty weightsProp, bool customWeightsExpanded) {
- var knownChannel =
- customWeightsExpanded
- ? KnownMaskChannel.Custom
- : KnownChannel(weightsProp.colorValue);
- label = EditorGUI.BeginProperty(rect, label, weightsProp);
- EditorGUI.BeginChangeCheck();
- knownChannel = (KnownMaskChannel)EditorGUI.EnumPopup(KnownChannelSubrect(rect), label, knownChannel);
- var weights = Weights(knownChannel, weightsProp.colorValue);
- if (customWeightsExpanded)
- WithIndent(() => {
- weights = ColorField(CustomWeightsSubrect(rect), Labels.ChannelWeights, weights);
- });
- if (EditorGUI.EndChangeCheck())
- weightsProp.colorValue = weights;
- if (Event.current.type != EventType.Layout)
- customWeightsExpanded = CustomChannelShouldBeExpandedFor(knownChannel);
- EditorGUI.EndProperty();
- return customWeightsExpanded;
- }
- static Rect KnownChannelSubrect(Rect rect) {
- var result = rect;
- result.height = HeightOf(KnownChannelStyle);
- return result;
- }
- static Rect CustomWeightsSubrect(Rect rect) {
- var result = rect;
- result.y += HeightOf(KnownChannelStyle) + Mathf.Max(KnownChannelStyle.margin.bottom, CustomWeightsStyle.margin.top);
- result.height = HeightOf(CustomWeightsStyle);
- return result;
- }
- static float HeightOf(GUIStyle style) { return style.CalcSize(GUIContent.none).y; }
- static Color ColorField(Rect rect, GUIContent label, Color color) {
- rect = EditorGUI.PrefixLabel(rect, label);
- color.r = ColorComponentField(HorizontalSlice(rect, 0, 4, 2), Labels.R, color.r);
- color.g = ColorComponentField(HorizontalSlice(rect, 1, 4, 2), Labels.G, color.g);
- color.b = ColorComponentField(HorizontalSlice(rect, 2, 4, 2), Labels.B, color.b);
- color.a = ColorComponentField(HorizontalSlice(rect, 3, 4, 2), Labels.A, color.a);
- return color;
- }
- static float ColorComponentField(Rect rect, GUIContent label, float value) {
- return WithZeroIndent(() => {
- var labelWidth = EditorStyles.label.CalcSize(label).x + 1;
- EditorGUI.LabelField(new Rect(rect) { width = labelWidth }, label);
- rect.width -= labelWidth;
- rect.x += labelWidth;
- return EditorGUI.FloatField(rect, value);
- });
- }
- static Rect HorizontalSlice(Rect whole, int part, int partCount, int spacing) {
- var result = new Rect(whole);
- result.width -= (partCount - 1) * spacing;
- result.width /= partCount;
- result.x += part * (result.width + spacing);
- return result;
- }
- static T WithZeroIndent<T>(Func<T> f) {
- var prev = EditorGUI.indentLevel;
- EditorGUI.indentLevel = 0;
- try {
- return f();
- } finally {
- EditorGUI.indentLevel = prev;
- }
- }
- enum KnownMaskChannel { Alpha, Red, Green, Blue, Gray, Custom }
- static KnownMaskChannel KnownChannel(Color weights) {
- if (weights == MaskChannel.alpha) return KnownMaskChannel.Alpha;
- else if (weights == MaskChannel.red) return KnownMaskChannel.Red;
- else if (weights == MaskChannel.green) return KnownMaskChannel.Green;
- else if (weights == MaskChannel.blue) return KnownMaskChannel.Blue;
- else if (weights == MaskChannel.gray) return KnownMaskChannel.Gray;
- else return KnownMaskChannel.Custom;
- }
- static Color Weights(KnownMaskChannel known, Color custom) {
- switch (known) {
- case KnownMaskChannel.Alpha: return MaskChannel.alpha;
- case KnownMaskChannel.Red: return MaskChannel.red;
- case KnownMaskChannel.Green: return MaskChannel.green;
- case KnownMaskChannel.Blue: return MaskChannel.blue;
- case KnownMaskChannel.Gray: return MaskChannel.gray;
- default: return custom;
- }
- }
-
- public static bool CustomChannelShouldBeExpandedFor(Color weights) {
- return CustomChannelShouldBeExpandedFor(KnownChannel(weights));
- }
- static bool CustomChannelShouldBeExpandedFor(KnownMaskChannel channel) {
- return channel == KnownMaskChannel.Custom;
- }
- }
- }
- }
-
|