203 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			203 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Linq; | ||
|  | using UnityEditor; | ||
|  | using UnityEngine; | ||
|  | using UnityObject = UnityEngine.Object; | ||
|  | 
 | ||
|  | namespace Unity.Mathematics.Editor | ||
|  | { | ||
|  |     [CustomPropertyDrawer(typeof(PostNormalizeAttribute))] | ||
|  |     class PostNormalizedVectorDrawer : PrimitiveVectorDrawer | ||
|  |     { | ||
|  |         static class Content | ||
|  |         { | ||
|  |             public static readonly string tooltip = | ||
|  |                 L10n.Tr("Values you enter will be post-normalized. You will see the normalized result if you change selection and view the values again."); | ||
|  |         } | ||
|  | 
 | ||
|  |         class VectorPropertyGUIData | ||
|  |         { | ||
|  |             const int k_MaxElements = 4; | ||
|  | 
 | ||
|  |             public readonly bool Valid; | ||
|  | 
 | ||
|  |             // parent property | ||
|  |             readonly SerializedProperty m_VectorProperty; | ||
|  |             // relative paths of element child properties | ||
|  |             readonly IReadOnlyList<string> m_ElementPaths; | ||
|  |             // the number of element child properties | ||
|  |             readonly int m_NumElements; | ||
|  |             // per child property; value is null if there are multiple different values | ||
|  |             readonly double?[] m_PreNormalizedValues; | ||
|  |             // per target; used to revert actual values for each object after displaying pre-normalized values | ||
|  |             readonly Dictionary<SerializedProperty, double4> m_PostNormalizedValues = new Dictionary<SerializedProperty, double4>(); | ||
|  | 
 | ||
|  |             public VectorPropertyGUIData(SerializedProperty property) | ||
|  |             { | ||
|  |                 m_VectorProperty = property; | ||
|  |                 var parentPath = m_VectorProperty.propertyPath; | ||
|  |                 var i = 0; | ||
|  |                 var elementPaths = new List<string>(k_MaxElements); | ||
|  |                 var iterator = m_VectorProperty.Copy(); | ||
|  |                 while (iterator.Next(true) && iterator.propertyPath.StartsWith(parentPath)) | ||
|  |                 { | ||
|  |                     if (i >= k_MaxElements || iterator.propertyType != SerializedPropertyType.Float) | ||
|  |                         return; | ||
|  |                     elementPaths.Add(iterator.propertyPath.Substring(parentPath.Length + 1)); | ||
|  |                     i++; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 Valid = true; | ||
|  |                 m_NumElements = elementPaths.Count; | ||
|  |                 m_ElementPaths = elementPaths; | ||
|  |                 m_PreNormalizedValues = elementPaths.Select(p => (double?)null).ToArray(); | ||
|  | 
 | ||
|  |                 UpdatePreNormalizedValues(); | ||
|  |                 UpdatePostNormalizedValues(); | ||
|  |             } | ||
|  | 
 | ||
|  |             void UpdatePostNormalizedValues() | ||
|  |             { | ||
|  |                 m_PostNormalizedValues.Clear(); | ||
|  |                 foreach (var target in m_VectorProperty.serializedObject.targetObjects) | ||
|  |                 { | ||
|  |                     var postNormalizedValue = new double4(); | ||
|  |                     var parentProperty = new SerializedObject(target).FindProperty(m_VectorProperty.propertyPath); | ||
|  |                     for (var i = 0; i < m_NumElements; ++i) | ||
|  |                         postNormalizedValue[i] = parentProperty.FindPropertyRelative(m_ElementPaths[i]).doubleValue; | ||
|  |                     m_PostNormalizedValues[parentProperty] = postNormalizedValue; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public void UpdatePreNormalizedValues() | ||
|  |             { | ||
|  |                 for (var i = 0; i < m_NumElements; ++i) | ||
|  |                 { | ||
|  |                     var p = m_VectorProperty.FindPropertyRelative(m_ElementPaths[i]); | ||
|  |                     m_PreNormalizedValues[i] = p.hasMultipleDifferentValues ? (double?)null : p.doubleValue; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public void ApplyPreNormalizedValues() | ||
|  |             { | ||
|  |                 m_VectorProperty.serializedObject.ApplyModifiedProperties(); | ||
|  |                 for (var i = 0; i < m_NumElements; ++i) | ||
|  |                 { | ||
|  |                     if (m_PreNormalizedValues[i] != null) | ||
|  |                         m_VectorProperty.FindPropertyRelative(m_ElementPaths[i]).doubleValue = m_PreNormalizedValues[i].Value; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public void UnapplyPreNormalizedValues() | ||
|  |             { | ||
|  |                 foreach (var target in m_PostNormalizedValues) | ||
|  |                 { | ||
|  |                     target.Key.serializedObject.Update(); | ||
|  |                     for (var i = 0; i < m_NumElements; ++i) | ||
|  |                     { | ||
|  |                         target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue = target.Value[i]; | ||
|  |                         target.Key.serializedObject.ApplyModifiedProperties(); | ||
|  |                     } | ||
|  |                 } | ||
|  |                 m_VectorProperty.serializedObject.Update(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void PostNormalize(Func<double4, double4> normalize) | ||
|  |             { | ||
|  |                 m_VectorProperty.serializedObject.ApplyModifiedProperties(); | ||
|  |                 foreach (var target in m_PostNormalizedValues) | ||
|  |                 { | ||
|  |                     target.Key.serializedObject.Update(); | ||
|  |                     var postNormalizedValue = new double4(); | ||
|  |                     for (var i = 0; i < m_NumElements; ++i) | ||
|  |                         postNormalizedValue[i] = target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue; | ||
|  |                     postNormalizedValue = normalize(normalize(postNormalizedValue)); | ||
|  |                     for (var i = 0; i < m_NumElements; ++i) | ||
|  |                         target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue = postNormalizedValue[i]; | ||
|  |                     target.Key.serializedObject.ApplyModifiedProperties(); | ||
|  |                 } | ||
|  |                 UpdatePostNormalizedValues(); | ||
|  |                 m_VectorProperty.serializedObject.Update(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void RebuildIfDirty() | ||
|  |             { | ||
|  |                 foreach (var target in m_PostNormalizedValues) | ||
|  |                 { | ||
|  |                     target.Key.serializedObject.Update(); | ||
|  |                     for (var i = 0; i < m_NumElements; ++i) | ||
|  |                     { | ||
|  |                         var serialized = target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue; | ||
|  |                         if (target.Value[i] != serialized) | ||
|  |                         { | ||
|  |                             UpdatePreNormalizedValues(); | ||
|  |                             UpdatePostNormalizedValues(); | ||
|  |                             return; | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         Dictionary<string, VectorPropertyGUIData> m_GUIDataPerPropertyPath = new Dictionary<string, VectorPropertyGUIData>(); | ||
|  | 
 | ||
|  |         protected virtual SerializedProperty GetVectorProperty(SerializedProperty property) | ||
|  |         { | ||
|  |             return property; | ||
|  |         } | ||
|  | 
 | ||
|  |         protected virtual double4 Normalize(double4 value) | ||
|  |         { | ||
|  |             return math.normalizesafe(value); | ||
|  |         } | ||
|  | 
 | ||
|  |         VectorPropertyGUIData GetGUIData(SerializedProperty property) | ||
|  |         { | ||
|  |             VectorPropertyGUIData guiData; | ||
|  |             if (!m_GUIDataPerPropertyPath.TryGetValue(property.propertyPath, out guiData)) | ||
|  |             { | ||
|  |                 guiData = new VectorPropertyGUIData(GetVectorProperty(property)); | ||
|  |                 m_GUIDataPerPropertyPath[property.propertyPath] = guiData; | ||
|  |             } | ||
|  |             return guiData; | ||
|  |         } | ||
|  | 
 | ||
|  |         public override float GetPropertyHeight(SerializedProperty property, GUIContent label) | ||
|  |         { | ||
|  |             return GetGUIData(property).Valid ? base.GetPropertyHeight(property, label) : EditorGUIUtility.singleLineHeight; | ||
|  |         } | ||
|  | 
 | ||
|  |         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | ||
|  |         { | ||
|  |             var guiData = GetGUIData(property); | ||
|  |             if (!guiData.Valid) | ||
|  |             { | ||
|  |                 EditorGUI.HelpBox( | ||
|  |                     EditorGUI.PrefixLabel(position, label), | ||
|  |                     L10n.Tr($"{typeof(PostNormalizeAttribute).Name} only works with decimal vector types."), | ||
|  |                     MessageType.None | ||
|  |                 ); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (string.IsNullOrEmpty(label.tooltip)) | ||
|  |                 label.tooltip = Content.tooltip; | ||
|  | 
 | ||
|  |             guiData.RebuildIfDirty(); | ||
|  |             guiData.ApplyPreNormalizedValues(); | ||
|  |             EditorGUI.BeginChangeCheck(); | ||
|  |             base.OnGUI(position, property, label); | ||
|  |             if (EditorGUI.EndChangeCheck()) | ||
|  |             { | ||
|  |                 guiData.UpdatePreNormalizedValues(); | ||
|  |                 guiData.PostNormalize(Normalize); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 guiData.UnapplyPreNormalizedValues(); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |