2022-01-12 10:06:03 +03:00
#if ENABLE_ANIMATION_COLLECTION & & ENABLE_ANIMATION_BURST
#define ENABLE_ANIMATION_PERFORMANCE
#endif
using UnityEngine ;
using UnityEditorInternal ;
using UnityEngine.U2D.Animation ;
using UnityEditor.IMGUI.Controls ;
using UnityEngine.U2D ;
using UnityEngine.U2D.Common ;
namespace UnityEditor.U2D.Animation
{
[CustomEditor(typeof(SpriteSkin))]
[CanEditMultipleObjects]
class SpriteSkinEditor : Editor
{
private static class Contents
{
public static readonly GUIContent listHeaderLabel = new GUIContent ( "Bones" , "GameObject Transform to represent the Bones defined by the Sprite that is currently used for deformation." ) ;
public static readonly GUIContent rootBoneLabel = new GUIContent ( "Root Bone" , "GameObject Transform to represent the Root Bone." ) ;
public static readonly string spriteNotFound = L10n . Tr ( "Sprite not found in SpriteRenderer" ) ;
public static readonly string spriteHasNoSkinningInformation = L10n . Tr ( "Sprite has no Bind Poses" ) ;
public static readonly string spriteHasNoWeights = L10n . Tr ( "Sprite has no weights" ) ;
public static readonly string rootTransformNotFound = L10n . Tr ( "Root Bone not set" ) ;
public static readonly string rootTransformNotFoundInArray = L10n . Tr ( "Bone list doesn't contain a reference to the Root Bone" ) ;
public static readonly string invalidTransformArray = L10n . Tr ( "Bone list is invalid" ) ;
public static readonly string transformArrayContainsNull = L10n . Tr ( "Bone list contains unassigned references" ) ;
public static readonly string invalidTransformArrayLength = L10n . Tr ( "The number of Sprite's Bind Poses and the number of Transforms should match" ) ;
public static readonly GUIContent useManager = new GUIContent ( "Enable batching" , "When enabled, SpriteSkin deformation will be done in batch to improve performance." ) ;
public static readonly GUIContent alwaysUpdate = new GUIContent ( "Always Update" , "Executes deformation of SpriteSkin even when the associated SpriteRenderer has been culled and is not visible." ) ;
public static readonly string experimental = L10n . Tr ( "Experimental" ) ;
}
private static Color s_BoundingBoxHandleColor = new Color ( 255 , 255 , 255 , 150 ) / 255 ;
private SerializedProperty m_RootBoneProperty ;
private SerializedProperty m_BoneTransformsProperty ;
private SerializedProperty m_AlwaysUpdateProperty ;
private SpriteSkin m_SpriteSkin ;
private ReorderableList m_ReorderableList ;
private Sprite m_CurrentSprite ;
private BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle ( ) ;
private bool m_NeedsRebind = false ;
#if ENABLE_ANIMATION_PERFORMANCE
private SerializedProperty m_UseBatching ;
private bool m_ExperimentalFold ;
#endif
private bool m_BoneFold = true ;
private void OnEnable ( )
{
m_SpriteSkin = ( SpriteSkin ) target ;
m_SpriteSkin . OnEditorEnable ( ) ;
2022-01-12 10:39:15 +03:00
2022-01-12 10:06:03 +03:00
m_RootBoneProperty = serializedObject . FindProperty ( "m_RootBone" ) ;
#if ENABLE_ANIMATION_PERFORMANCE
m_UseBatching = serializedObject . FindProperty ( "m_UseBatching" ) ;
#endif
m_BoneTransformsProperty = serializedObject . FindProperty ( "m_BoneTransforms" ) ;
m_AlwaysUpdateProperty = serializedObject . FindProperty ( "m_AlwaysUpdate" ) ;
2022-01-12 10:39:15 +03:00
2022-01-12 10:06:03 +03:00
m_CurrentSprite = m_SpriteSkin . spriteRenderer . sprite ;
m_BoundsHandle . axes = BoxBoundsHandle . Axes . X | BoxBoundsHandle . Axes . Y ;
m_BoundsHandle . SetColor ( s_BoundingBoxHandleColor ) ;
SetupReorderableList ( ) ;
Undo . undoRedoPerformed + = UndoRedoPerformed ;
}
private void OnDestroy ( )
{
Undo . undoRedoPerformed - = UndoRedoPerformed ;
}
private void UndoRedoPerformed ( )
{
m_CurrentSprite = m_SpriteSkin . spriteRenderer . sprite ;
}
private void SetupReorderableList ( )
{
m_ReorderableList = new ReorderableList ( serializedObject , m_BoneTransformsProperty , false , true , false , false ) ;
m_ReorderableList . headerHeight = 1.0f ;
m_ReorderableList . elementHeightCallback = ( int index ) = >
{
return EditorGUIUtility . singleLineHeight + 6 ;
} ;
m_ReorderableList . drawElementCallback = ( Rect rect , int index , bool isactive , bool isfocused ) = >
{
var content = GUIContent . none ;
if ( m_CurrentSprite ! = null )
{
var bones = m_CurrentSprite . GetBones ( ) ;
if ( index < bones . Length )
content = new GUIContent ( bones [ index ] . name ) ;
}
rect . y + = 2f ;
rect . height = EditorGUIUtility . singleLineHeight ;
SerializedProperty element = m_BoneTransformsProperty . GetArrayElementAtIndex ( index ) ;
EditorGUI . PropertyField ( rect , element , content ) ;
} ;
}
private void InitializeBoneTransformArray ( )
{
if ( m_CurrentSprite )
{
var elementCount = m_BoneTransformsProperty . arraySize ;
var bindPoses = m_CurrentSprite . GetBindPoses ( ) ;
if ( elementCount ! = bindPoses . Length )
{
m_BoneTransformsProperty . arraySize = bindPoses . Length ;
for ( int i = elementCount ; i < m_BoneTransformsProperty . arraySize ; + + i )
m_BoneTransformsProperty . GetArrayElementAtIndex ( i ) . objectReferenceValue = null ;
m_NeedsRebind = true ;
}
}
}
public override void OnInspectorGUI ( )
{
serializedObject . Update ( ) ;
EditorGUILayout . PropertyField ( m_AlwaysUpdateProperty , Contents . alwaysUpdate ) ;
var sprite = m_SpriteSkin . spriteRenderer . sprite ;
var spriteChanged = m_CurrentSprite ! = sprite ;
if ( m_ReorderableList = = null | | spriteChanged )
{
m_CurrentSprite = sprite ;
InitializeBoneTransformArray ( ) ;
SetupReorderableList ( ) ;
}
EditorGUI . BeginChangeCheck ( ) ;
EditorGUILayout . PropertyField ( m_RootBoneProperty , Contents . rootBoneLabel ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
m_NeedsRebind = true ;
}
m_BoneFold = EditorGUILayout . Foldout ( m_BoneFold , Contents . listHeaderLabel , true ) ;
if ( m_BoneFold )
{
EditorGUILayout . Space ( ) ;
if ( ! serializedObject . isEditingMultipleObjects )
{
EditorGUI . BeginDisabledGroup ( m_SpriteSkin . rootBone = = null ) ;
m_ReorderableList . DoLayoutList ( ) ;
EditorGUI . EndDisabledGroup ( ) ;
}
}
EditorGUILayout . BeginHorizontal ( ) ;
GUILayout . FlexibleSpace ( ) ;
EditorGUI . BeginDisabledGroup ( ! EnableCreateBones ( ) ) ;
DoGenerateBonesButton ( ) ;
EditorGUI . EndDisabledGroup ( ) ;
EditorGUI . BeginDisabledGroup ( ! EnableSetBindPose ( ) ) ;
DoResetBindPoseButton ( ) ;
EditorGUI . EndDisabledGroup ( ) ;
GUILayout . FlexibleSpace ( ) ;
EditorGUILayout . EndHorizontal ( ) ;
#if ENABLE_ANIMATION_PERFORMANCE
m_ExperimentalFold = EditorGUILayout . Foldout ( m_ExperimentalFold , Contents . experimental , true ) ;
if ( m_ExperimentalFold )
{
EditorGUI . indentLevel + + ;
EditorGUI . BeginChangeCheck ( ) ;
EditorGUILayout . PropertyField ( m_UseBatching , Contents . useManager ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
foreach ( var obj in targets )
{
( ( SpriteSkin ) obj ) . UseBatching ( m_UseBatching . boolValue ) ;
}
}
EditorGUI . indentLevel - - ;
}
#endif
serializedObject . ApplyModifiedProperties ( ) ;
if ( m_NeedsRebind )
Rebind ( ) ;
if ( spriteChanged & & ! m_SpriteSkin . ignoreNextSpriteChange )
{
ResetBounds ( Undo . GetCurrentGroupName ( ) ) ;
m_SpriteSkin . ignoreNextSpriteChange = false ;
}
DoValidationWarnings ( ) ;
}
private void Rebind ( )
{
foreach ( var t in targets )
{
var spriteSkin = t as SpriteSkin ;
if ( spriteSkin . spriteRenderer . sprite = = null | | spriteSkin . rootBone = = null )
continue ;
spriteSkin . Rebind ( ) ;
ResetBoundsIfNeeded ( spriteSkin ) ;
}
m_NeedsRebind = false ;
}
private void ResetBounds ( string undoName = "Reset Bounds" )
{
foreach ( var t in targets )
{
var spriteSkin = t as SpriteSkin ;
if ( ! spriteSkin . isValid )
continue ;
Undo . RegisterCompleteObjectUndo ( spriteSkin , undoName ) ;
spriteSkin . CalculateBounds ( ) ;
EditorUtility . SetDirty ( spriteSkin ) ;
}
}
private void ResetBoundsIfNeeded ( SpriteSkin spriteSkin )
{
if ( spriteSkin . isValid & & spriteSkin . bounds = = new Bounds ( ) )
spriteSkin . CalculateBounds ( ) ;
}
private bool EnableCreateBones ( )
{
foreach ( var t in targets )
{
var spriteSkin = t as SpriteSkin ;
var sprite = spriteSkin . spriteRenderer . sprite ;
if ( sprite ! = null & & spriteSkin . rootBone = = null )
return true ;
}
return false ;
}
private bool EnableSetBindPose ( )
{
return IsAnyTargetValid ( ) ;
}
private bool IsAnyTargetValid ( )
{
foreach ( var t in targets )
{
var spriteSkin = t as SpriteSkin ;
if ( spriteSkin . isValid )
return true ;
}
return false ;
}
private void DoGenerateBonesButton ( )
{
if ( GUILayout . Button ( "Create Bones" , GUILayout . MaxWidth ( 125f ) ) )
{
foreach ( var t in targets )
{
var spriteSkin = t as SpriteSkin ;
var sprite = spriteSkin . spriteRenderer . sprite ;
if ( sprite = = null | | spriteSkin . rootBone ! = null )
continue ;
Undo . RegisterCompleteObjectUndo ( spriteSkin , "Create Bones" ) ;
spriteSkin . CreateBoneHierarchy ( ) ;
foreach ( var transform in spriteSkin . boneTransforms )
Undo . RegisterCreatedObjectUndo ( transform . gameObject , "Create Bones" ) ;
ResetBoundsIfNeeded ( spriteSkin ) ;
EditorUtility . SetDirty ( spriteSkin ) ;
}
BoneGizmo . instance . boneGizmoController . OnSelectionChanged ( ) ;
}
}
private void DoResetBindPoseButton ( )
{
if ( GUILayout . Button ( "Reset Bind Pose" , GUILayout . MaxWidth ( 125f ) ) )
{
foreach ( var t in targets )
{
var spriteSkin = t as SpriteSkin ;
if ( ! spriteSkin . isValid )
continue ;
Undo . RecordObjects ( spriteSkin . boneTransforms , "Reset Bind Pose" ) ;
spriteSkin . ResetBindPose ( ) ;
}
}
}
private void DoValidationWarnings ( )
{
EditorGUILayout . Space ( ) ;
bool preAppendObjectName = targets . Length > 1 ;
foreach ( var t in targets )
{
var spriteSkin = t as SpriteSkin ;
var validationResult = spriteSkin . Validate ( ) ;
if ( validationResult = = SpriteSkinValidationResult . Ready )
continue ;
var text = "" ;
switch ( validationResult )
{
case SpriteSkinValidationResult . SpriteNotFound :
text = Contents . spriteNotFound ;
break ;
case SpriteSkinValidationResult . SpriteHasNoSkinningInformation :
text = Contents . spriteHasNoSkinningInformation ;
break ;
case SpriteSkinValidationResult . SpriteHasNoWeights :
text = Contents . spriteHasNoWeights ;
break ;
case SpriteSkinValidationResult . RootTransformNotFound :
text = Contents . rootTransformNotFound ;
break ;
case SpriteSkinValidationResult . RootNotFoundInTransformArray :
text = Contents . rootTransformNotFoundInArray ;
break ;
case SpriteSkinValidationResult . InvalidTransformArray :
text = Contents . invalidTransformArray ;
break ;
case SpriteSkinValidationResult . InvalidTransformArrayLength :
text = Contents . invalidTransformArrayLength ;
break ;
case SpriteSkinValidationResult . TransformArrayContainsNull :
text = Contents . transformArrayContainsNull ;
break ;
}
if ( preAppendObjectName )
text = spriteSkin . name + ": " + text ;
EditorGUILayout . HelpBox ( text , MessageType . Warning ) ;
}
}
}
}