2020-02-17 11:22:12 +08:00
using UnityEditor ;
2018-06-22 18:48:14 +08:00
using UnityEditor.UI ;
using UnityEngine ;
2018-11-28 18:55:56 +08:00
using System.Collections.Generic ;
2020-09-02 01:39:05 +08:00
using System.Linq ;
2020-08-28 13:38:13 +08:00
using UnityEditorInternal ;
using UnityEngine.UI ;
2018-06-22 18:48:14 +08:00
namespace Coffee.UIExtensions
{
2020-02-12 20:38:06 +08:00
[CustomEditor(typeof(UIParticle))]
[CanEditMultipleObjects]
2020-08-20 03:42:16 +08:00
internal class UIParticleEditor : GraphicEditor
2020-02-12 20:38:06 +08:00
{
//################################
// Constant or Static Members.
//################################
2020-08-31 08:28:38 +08:00
private static readonly GUIContent s_ContentRenderingOrder = new GUIContent ( "Rendering Order" ) ;
private static readonly GUIContent s_ContentRefresh = new GUIContent ( "Refresh" ) ;
2020-09-01 12:50:54 +08:00
private static readonly GUIContent s_ContentFix = new GUIContent ( "Fix" ) ;
2020-10-04 22:26:53 +08:00
private static readonly GUIContent s_ContentMaterial = new GUIContent ( "Material" ) ;
private static readonly GUIContent s_ContentTrailMaterial = new GUIContent ( "Trail Material" ) ;
private static readonly GUIContent s_Content3D = new GUIContent ( "3D" ) ;
private static readonly GUIContent s_ContentScale = new GUIContent ( "Scale" ) ;
2020-08-20 03:42:16 +08:00
2020-10-28 21:19:15 +08:00
private SerializedProperty _spMaskable ;
2020-08-28 13:38:13 +08:00
private SerializedProperty _spScale ;
2020-08-20 03:42:16 +08:00
private SerializedProperty _spIgnoreCanvasScaler ;
private SerializedProperty _spAnimatableProperties ;
2020-11-20 12:11:08 +08:00
private SerializedProperty _spShrinkByMaterial ;
2020-08-28 13:38:13 +08:00
private ReorderableList _ro ;
2020-10-04 22:26:53 +08:00
private bool _xyzMode ;
2020-11-20 14:10:30 +08:00
private bool _showMaterials ;
2020-08-20 03:42:16 +08:00
private static readonly List < string > s_MaskablePropertyNames = new List < string >
2020-02-12 20:38:06 +08:00
{
"_Stencil" ,
"_StencilComp" ,
"_StencilOp" ,
"_StencilWriteMask" ,
"_StencilReadMask" ,
"_ColorMask" ,
} ;
2020-08-20 03:42:16 +08:00
2020-02-12 20:38:06 +08:00
//################################
// Public/Protected Members.
//################################
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected override void OnEnable ( )
{
base . OnEnable ( ) ;
2020-10-28 21:19:15 +08:00
_spMaskable = serializedObject . FindProperty ( "m_Maskable" ) ;
2020-10-04 22:26:53 +08:00
_spScale = serializedObject . FindProperty ( "m_Scale3D" ) ;
2020-08-20 03:42:16 +08:00
_spIgnoreCanvasScaler = serializedObject . FindProperty ( "m_IgnoreCanvasScaler" ) ;
2020-02-12 20:38:06 +08:00
_spAnimatableProperties = serializedObject . FindProperty ( "m_AnimatableProperties" ) ;
2020-11-20 12:11:08 +08:00
_spShrinkByMaterial = serializedObject . FindProperty ( "m_ShrinkByMaterial" ) ;
2020-11-20 14:10:30 +08:00
_showMaterials = EditorPrefs . GetBool ( "Coffee.UIExtensions.UIParticleEditor._showMaterials" , true ) ;
2020-08-28 13:38:13 +08:00
var sp = serializedObject . FindProperty ( "m_Particles" ) ;
2020-10-04 22:26:53 +08:00
_ro = new ReorderableList ( sp . serializedObject , sp , true , true , true , true ) ;
_ro . elementHeight = EditorGUIUtility . singleLineHeight * 3 + 4 ;
2020-11-20 14:10:30 +08:00
_ro . elementHeightCallback = _ = > _showMaterials
? 3 * ( EditorGUIUtility . singleLineHeight + 2 )
: EditorGUIUtility . singleLineHeight + 2 ;
2020-08-28 13:38:13 +08:00
_ro . drawElementCallback = ( rect , index , active , focused ) = >
{
2020-10-04 22:26:53 +08:00
EditorGUI . BeginDisabledGroup ( sp . hasMultipleDifferentValues ) ;
2020-08-28 13:38:13 +08:00
rect . y + = 1 ;
rect . height = EditorGUIUtility . singleLineHeight ;
2020-10-04 22:26:53 +08:00
var p = sp . GetArrayElementAtIndex ( index ) ;
EditorGUI . ObjectField ( rect , p , GUIContent . none ) ;
2020-11-20 14:10:30 +08:00
if ( ! _showMaterials ) return ;
2020-10-04 22:26:53 +08:00
rect . x + = 15 ;
rect . width - = 15 ;
var ps = p . objectReferenceValue as ParticleSystem ;
var materials = ps
? new SerializedObject ( ps . GetComponent < ParticleSystemRenderer > ( ) ) . FindProperty ( "m_Materials" )
: null ;
rect . y + = rect . height + 1 ;
MaterialField ( rect , s_ContentMaterial , materials , 0 ) ;
rect . y + = rect . height + 1 ;
MaterialField ( rect , s_ContentTrailMaterial , materials , 1 ) ;
EditorGUI . EndDisabledGroup ( ) ;
if ( materials ! = null )
{
materials . serializedObject . ApplyModifiedProperties ( ) ;
}
2020-08-28 13:38:13 +08:00
} ;
_ro . drawHeaderCallback + = rect = >
{
2020-11-20 14:10:30 +08:00
#if !UNITY_2019_3_OR_NEWER
rect . y - = 1 ;
# endif
2020-08-31 08:28:38 +08:00
EditorGUI . LabelField ( new Rect ( rect . x , rect . y , 150 , rect . height ) , s_ContentRenderingOrder ) ;
2020-08-28 13:38:13 +08:00
2020-11-20 14:10:30 +08:00
var content = EditorGUIUtility . IconContent ( _showMaterials ? "VisibilityOn" : "VisibilityOff" ) ;
_showMaterials = GUI . Toggle ( new Rect ( rect . width - 55 , rect . y , 24 , 20 ) , _showMaterials , content , EditorStyles . label ) ;
2020-10-04 22:26:53 +08:00
2020-11-20 14:10:30 +08:00
if ( GUI . Button ( new Rect ( rect . width - 35 , rect . y , 60 , rect . height ) , s_ContentRefresh , EditorStyles . miniButton ) )
2020-08-28 13:38:13 +08:00
{
2020-08-31 08:28:38 +08:00
foreach ( UIParticle t in targets )
{
t . RefreshParticles ( ) ;
}
2020-08-28 13:38:13 +08:00
}
} ;
2020-02-12 20:38:06 +08:00
}
2020-10-04 22:26:53 +08:00
private static void MaterialField ( Rect rect , GUIContent label , SerializedProperty sp , int index )
{
if ( sp = = null | | sp . arraySize < = index )
{
EditorGUI . BeginDisabledGroup ( true ) ;
EditorGUI . ObjectField ( rect , label , null , typeof ( Material ) , true ) ;
EditorGUI . EndDisabledGroup ( ) ;
}
else
{
EditorGUI . PropertyField ( rect , sp . GetArrayElementAtIndex ( index ) , label ) ;
}
}
2020-02-12 20:38:06 +08:00
/// <summary>
/// Implement this function to make a custom inspector.
/// </summary>
public override void OnInspectorGUI ( )
{
2020-08-20 03:42:16 +08:00
var current = target as UIParticle ;
if ( current = = null ) return ;
2020-02-12 20:38:06 +08:00
serializedObject . Update ( ) ;
2020-10-28 21:19:15 +08:00
// Maskable
EditorGUILayout . PropertyField ( _spMaskable ) ;
2020-08-28 13:38:13 +08:00
// IgnoreCanvasScaler
2020-09-01 12:50:54 +08:00
using ( var ccs = new EditorGUI . ChangeCheckScope ( ) )
{
EditorGUILayout . PropertyField ( _spIgnoreCanvasScaler ) ;
if ( ccs . changed )
{
foreach ( UIParticle p in targets )
{
p . ignoreCanvasScaler = _spIgnoreCanvasScaler . boolValue ;
}
}
}
2020-02-12 20:38:06 +08:00
2020-08-28 13:38:13 +08:00
// Scale
2020-10-04 22:26:53 +08:00
_xyzMode = DrawFloatOrVector3Field ( _spScale , _xyzMode ) ;
2020-08-28 13:38:13 +08:00
2020-02-12 20:38:06 +08:00
// AnimatableProperties
2020-09-02 01:39:05 +08:00
var mats = current . particles
. Where ( x = > x )
. Select ( x = > x . GetComponent < ParticleSystemRenderer > ( ) . sharedMaterial )
. Where ( x = > x )
. ToArray ( ) ;
2020-10-04 22:26:53 +08:00
// Animated properties
EditorGUI . BeginChangeCheck ( ) ;
2020-09-02 01:39:05 +08:00
AnimatedPropertiesEditor . DrawAnimatableProperties ( _spAnimatableProperties , mats ) ;
2020-10-04 22:26:53 +08:00
if ( EditorGUI . EndChangeCheck ( ) )
{
foreach ( UIParticle t in targets )
t . SetMaterialDirty ( ) ;
}
2020-02-12 20:38:06 +08:00
2020-11-20 12:11:08 +08:00
// ShrinkByMaterial
EditorGUILayout . PropertyField ( _spShrinkByMaterial ) ;
2020-10-04 22:26:53 +08:00
// Target ParticleSystems.
2020-08-28 13:38:13 +08:00
_ro . DoLayoutList ( ) ;
2020-02-12 20:38:06 +08:00
2020-09-01 12:50:54 +08:00
serializedObject . ApplyModifiedProperties ( ) ;
2020-08-20 03:42:16 +08:00
// Does the shader support UI masks?
2020-08-28 13:38:13 +08:00
if ( current . maskable & & current . GetComponentInParent < Mask > ( ) )
2020-02-12 20:38:06 +08:00
{
2020-08-28 13:38:13 +08:00
foreach ( var mat in current . materials )
2020-02-12 20:38:06 +08:00
{
2020-08-31 08:28:38 +08:00
if ( ! mat | | ! mat . shader ) continue ;
2020-08-28 13:38:13 +08:00
var shader = mat . shader ;
foreach ( var propName in s_MaskablePropertyNames )
{
if ( mat . HasProperty ( propName ) ) continue ;
2020-08-20 03:42:16 +08:00
2020-08-28 13:38:13 +08:00
EditorGUILayout . HelpBox ( string . Format ( "Shader '{0}' doesn't have '{1}' property. This graphic cannot be masked." , shader . name , propName ) , MessageType . Warning ) ;
break ;
}
2020-02-12 20:38:06 +08:00
}
2020-08-12 01:10:22 +08:00
}
2020-02-12 20:38:06 +08:00
2020-09-01 12:50:54 +08:00
// Does the shader support UI masks?
2020-09-02 01:39:05 +08:00
if ( FixButton ( current . m_IsTrail , "This UIParticle component should be removed. The UIParticle for trails is no longer needed." ) )
2020-09-01 12:50:54 +08:00
{
DestroyUIParticle ( current ) ;
return ;
}
2022-06-10 15:12:15 +08:00
// #203: When using linear color space, the particle colors are not output correctly.
// To fix, set 'Apply Active Color Space' in renderer module to false.
var allPsRenderers = targets . OfType < UIParticle > ( )
. SelectMany ( x = > x . particles )
. Where ( x = > x )
. Select ( x = > x . GetComponent < ParticleSystemRenderer > ( ) )
. ToArray ( ) ;
if ( 0 < allPsRenderers . Length )
{
var so = new SerializedObject ( allPsRenderers ) ;
var sp = so . FindProperty ( "m_ApplyActiveColorSpace" ) ; //.boolValue = false;
if ( FixButton ( sp . boolValue | | sp . hasMultipleDifferentValues , "When using linear color space, the particle colors are not output correctly.\nTo fix, set 'Apply Active Color Space' in renderer module to false." ) )
{
sp . boolValue = false ;
so . ApplyModifiedProperties ( ) ;
}
}
2020-09-01 12:50:54 +08:00
}
void DestroyUIParticle ( UIParticle p , bool ignoreCurrent = false )
{
if ( ! p | | ignoreCurrent & & target = = p ) return ;
var cr = p . canvasRenderer ;
DestroyImmediate ( p ) ;
DestroyImmediate ( cr ) ;
2020-09-01 13:27:06 +08:00
2022-02-16 00:47:45 +08:00
#if UNITY_2021_2_OR_NEWER
var stage = UnityEditor . SceneManagement . PrefabStageUtility . GetCurrentPrefabStage ( ) ;
#elif UNITY_2018_3_OR_NEWER
2020-09-01 15:03:06 +08:00
var stage = UnityEditor . Experimental . SceneManagement . PrefabStageUtility . GetCurrentPrefabStage ( ) ;
2022-02-16 00:47:45 +08:00
# endif
#if UNITY_2018_3_OR_NEWER
2020-09-01 13:27:06 +08:00
if ( stage ! = null & & stage . scene . isLoaded )
{
2021-08-02 13:45:22 +08:00
#if UNITY_2020_1_OR_NEWER
2021-08-02 14:09:21 +08:00
string prefabAssetPath = stage . assetPath ;
2021-08-02 13:45:22 +08:00
# else
2021-08-02 14:09:21 +08:00
string prefabAssetPath = stage . prefabAssetPath ;
2021-08-02 13:45:22 +08:00
# endif
2021-08-02 14:09:21 +08:00
PrefabUtility . SaveAsPrefabAsset ( stage . prefabContentsRoot , prefabAssetPath ) ;
2020-09-01 13:27:06 +08:00
}
2020-09-01 15:03:06 +08:00
# endif
2020-09-01 12:50:54 +08:00
}
bool FixButton ( bool show , string text )
{
if ( ! show ) return false ;
using ( new EditorGUILayout . HorizontalScope ( GUILayout . ExpandWidth ( true ) ) )
{
EditorGUILayout . HelpBox ( text , MessageType . Warning , true ) ;
using ( new EditorGUILayout . VerticalScope ( ) )
{
return GUILayout . Button ( s_ContentFix , GUILayout . Width ( 30 ) ) ;
}
}
2020-02-12 20:38:06 +08:00
}
2020-10-04 22:26:53 +08:00
private static bool DrawFloatOrVector3Field ( SerializedProperty sp , bool showXyz )
{
var x = sp . FindPropertyRelative ( "x" ) ;
var y = sp . FindPropertyRelative ( "y" ) ;
var z = sp . FindPropertyRelative ( "z" ) ;
showXyz | = ! Mathf . Approximately ( x . floatValue , y . floatValue ) | |
! Mathf . Approximately ( y . floatValue , z . floatValue ) | |
y . hasMultipleDifferentValues | |
z . hasMultipleDifferentValues ;
EditorGUILayout . BeginHorizontal ( ) ;
if ( showXyz )
{
2021-02-19 08:57:02 +08:00
EditorGUI . BeginChangeCheck ( ) ;
2020-10-04 22:26:53 +08:00
EditorGUILayout . PropertyField ( sp ) ;
2021-02-19 08:57:02 +08:00
if ( EditorGUI . EndChangeCheck ( ) )
{
x . floatValue = Mathf . Max ( 0.001f , x . floatValue ) ;
y . floatValue = Mathf . Max ( 0.001f , y . floatValue ) ;
z . floatValue = Mathf . Max ( 0.001f , z . floatValue ) ;
}
2020-10-04 22:26:53 +08:00
}
else
{
EditorGUI . BeginChangeCheck ( ) ;
EditorGUILayout . PropertyField ( x , s_ContentScale ) ;
if ( EditorGUI . EndChangeCheck ( ) )
2021-02-19 08:57:02 +08:00
{
x . floatValue = Mathf . Max ( 0.001f , x . floatValue ) ;
y . floatValue = Mathf . Max ( 0.001f , x . floatValue ) ;
z . floatValue = Mathf . Max ( 0.001f , x . floatValue ) ;
}
2020-10-04 22:26:53 +08:00
}
EditorGUI . BeginChangeCheck ( ) ;
showXyz = GUILayout . Toggle ( showXyz , s_Content3D , EditorStyles . miniButton , GUILayout . Width ( 30 ) ) ;
if ( EditorGUI . EndChangeCheck ( ) & & ! showXyz )
z . floatValue = y . floatValue = x . floatValue ;
EditorGUILayout . EndHorizontal ( ) ;
return showXyz ;
}
2020-02-12 20:38:06 +08:00
}
}