A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEditor;
5using UnityEngine;
6using UnityObject = UnityEngine.Object;
7
8namespace Unity.Mathematics.Editor
9{
10 [CustomPropertyDrawer(typeof(PostNormalizeAttribute))]
11 class PostNormalizedVectorDrawer : PrimitiveVectorDrawer
12 {
13 static class Content
14 {
15 public static readonly string tooltip =
16 L10n.Tr("Values you enter will be post-normalized. You will see the normalized result if you change selection and view the values again.");
17 }
18
19 class VectorPropertyGUIData
20 {
21 const int k_MaxElements = 4;
22
23 public readonly bool Valid;
24
25 // parent property
26 readonly SerializedProperty m_VectorProperty;
27 // relative paths of element child properties
28 readonly IReadOnlyList<string> m_ElementPaths;
29 // the number of element child properties
30 readonly int m_NumElements;
31 // per child property; value is null if there are multiple different values
32 readonly double?[] m_PreNormalizedValues;
33 // per target; used to revert actual values for each object after displaying pre-normalized values
34 readonly Dictionary<SerializedProperty, double4> m_PostNormalizedValues = new Dictionary<SerializedProperty, double4>();
35
36 public VectorPropertyGUIData(SerializedProperty property)
37 {
38 m_VectorProperty = property;
39 var parentPath = m_VectorProperty.propertyPath;
40 var i = 0;
41 var elementPaths = new List<string>(k_MaxElements);
42 var iterator = m_VectorProperty.Copy();
43 while (iterator.Next(true) && iterator.propertyPath.StartsWith(parentPath))
44 {
45 if (i >= k_MaxElements || iterator.propertyType != SerializedPropertyType.Float)
46 return;
47 elementPaths.Add(iterator.propertyPath.Substring(parentPath.Length + 1));
48 i++;
49 }
50
51 Valid = true;
52 m_NumElements = elementPaths.Count;
53 m_ElementPaths = elementPaths;
54 m_PreNormalizedValues = elementPaths.Select(p => (double?)null).ToArray();
55
56 UpdatePreNormalizedValues();
57 UpdatePostNormalizedValues();
58 }
59
60 void UpdatePostNormalizedValues()
61 {
62 m_PostNormalizedValues.Clear();
63 foreach (var target in m_VectorProperty.serializedObject.targetObjects)
64 {
65 var postNormalizedValue = new double4();
66 var parentProperty = new SerializedObject(target).FindProperty(m_VectorProperty.propertyPath);
67 for (var i = 0; i < m_NumElements; ++i)
68 postNormalizedValue[i] = parentProperty.FindPropertyRelative(m_ElementPaths[i]).doubleValue;
69 m_PostNormalizedValues[parentProperty] = postNormalizedValue;
70 }
71 }
72
73 public void UpdatePreNormalizedValues()
74 {
75 for (var i = 0; i < m_NumElements; ++i)
76 {
77 var p = m_VectorProperty.FindPropertyRelative(m_ElementPaths[i]);
78 m_PreNormalizedValues[i] = p.hasMultipleDifferentValues ? (double?)null : p.doubleValue;
79 }
80 }
81
82 public void ApplyPreNormalizedValues()
83 {
84 m_VectorProperty.serializedObject.ApplyModifiedProperties();
85 for (var i = 0; i < m_NumElements; ++i)
86 {
87 if (m_PreNormalizedValues[i] != null)
88 m_VectorProperty.FindPropertyRelative(m_ElementPaths[i]).doubleValue = m_PreNormalizedValues[i].Value;
89 }
90 }
91
92 public void UnapplyPreNormalizedValues()
93 {
94 foreach (var target in m_PostNormalizedValues)
95 {
96 target.Key.serializedObject.Update();
97 for (var i = 0; i < m_NumElements; ++i)
98 {
99 target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue = target.Value[i];
100 target.Key.serializedObject.ApplyModifiedProperties();
101 }
102 }
103 m_VectorProperty.serializedObject.Update();
104 }
105
106 public void PostNormalize(Func<double4, double4> normalize)
107 {
108 m_VectorProperty.serializedObject.ApplyModifiedProperties();
109 foreach (var target in m_PostNormalizedValues)
110 {
111 target.Key.serializedObject.Update();
112 var postNormalizedValue = new double4();
113 for (var i = 0; i < m_NumElements; ++i)
114 postNormalizedValue[i] = target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue;
115 postNormalizedValue = normalize(normalize(postNormalizedValue));
116 for (var i = 0; i < m_NumElements; ++i)
117 target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue = postNormalizedValue[i];
118 target.Key.serializedObject.ApplyModifiedProperties();
119 }
120 UpdatePostNormalizedValues();
121 m_VectorProperty.serializedObject.Update();
122 }
123
124 public void RebuildIfDirty()
125 {
126 foreach (var target in m_PostNormalizedValues)
127 {
128 target.Key.serializedObject.Update();
129 for (var i = 0; i < m_NumElements; ++i)
130 {
131 var serialized = target.Key.FindPropertyRelative(m_ElementPaths[i]).doubleValue;
132 if (target.Value[i] != serialized)
133 {
134 UpdatePreNormalizedValues();
135 UpdatePostNormalizedValues();
136 return;
137 }
138 }
139 }
140 }
141 }
142
143 Dictionary<string, VectorPropertyGUIData> m_GUIDataPerPropertyPath = new Dictionary<string, VectorPropertyGUIData>();
144
145 protected virtual SerializedProperty GetVectorProperty(SerializedProperty property)
146 {
147 return property;
148 }
149
150 protected virtual double4 Normalize(double4 value)
151 {
152 return math.normalizesafe(value);
153 }
154
155 VectorPropertyGUIData GetGUIData(SerializedProperty property)
156 {
157 VectorPropertyGUIData guiData;
158 if (!m_GUIDataPerPropertyPath.TryGetValue(property.propertyPath, out guiData))
159 {
160 guiData = new VectorPropertyGUIData(GetVectorProperty(property));
161 m_GUIDataPerPropertyPath[property.propertyPath] = guiData;
162 }
163 return guiData;
164 }
165
166 public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
167 {
168 return GetGUIData(property).Valid ? base.GetPropertyHeight(property, label) : EditorGUIUtility.singleLineHeight;
169 }
170
171 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
172 {
173 var guiData = GetGUIData(property);
174 if (!guiData.Valid)
175 {
176 EditorGUI.HelpBox(
177 EditorGUI.PrefixLabel(position, label),
178 L10n.Tr($"{typeof(PostNormalizeAttribute).Name} only works with decimal vector types."),
179 MessageType.None
180 );
181 return;
182 }
183
184 if (string.IsNullOrEmpty(label.tooltip))
185 label.tooltip = Content.tooltip;
186
187 guiData.RebuildIfDirty();
188 guiData.ApplyPreNormalizedValues();
189 EditorGUI.BeginChangeCheck();
190 base.OnGUI(position, property, label);
191 if (EditorGUI.EndChangeCheck())
192 {
193 guiData.UpdatePreNormalizedValues();
194 guiData.PostNormalize(Normalize);
195 }
196 else
197 {
198 guiData.UnapplyPreNormalizedValues();
199 }
200 }
201 }
202}