| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- using UnityEditor;
- using UnityEngine;
- using UnityEditorInternal;
- using System.Collections.Generic;
- namespace Cinemachine.Editor
- {
- [CustomEditor(typeof(NoiseSettings))]
- internal sealed class NoiseSettingsEditor : BaseEditor<NoiseSettings>
- {
- private const float vSpace = 2;
- private const float hSpace = 3;
- // Needed for accessing string names of fields
- #pragma warning disable 0649 // assigned but never used
- private NoiseSettings.TransformNoiseParams tpDef;
- private NoiseSettings.NoiseParams npDef;
- private static float mPreviewTime = 2;
- private static float mPreviewHeight = 5;
- private float mNoiseOffsetBase = 0;
- private float mNoiseOffset = 0;
- private bool mAnimatedPreview = false;
- GUIContent mAnimatedLabel = new GUIContent("Animated", "Animate the noise signal preview");
- private ReorderableList[] mPosChannels;
- private ReorderableList[] mRotChannels;
- private static GUIContent[] mPoslabels = new GUIContent[]
- {
- new GUIContent("Position X"),
- new GUIContent("Position Y"),
- new GUIContent("Position Z")
- };
- private static GUIContent[] mRotlabels = new GUIContent[]
- {
- new GUIContent("Rotation X"),
- new GUIContent("Rotation Y"),
- new GUIContent("Rotation Z")
- };
- private static bool[] mPosExpanded = new bool[3];
- private static bool[] mRotExpanded = new bool[3];
- private void OnEnable()
- {
- mNoiseOffsetBase = Time.realtimeSinceStartup;
- mNoiseOffset = 0;
- }
- /// <summary>Get the property names to exclude in the inspector.</summary>
- /// <param name="excluded">Add the names to this list</param>
- protected override void GetExcludedPropertiesInInspector(List<string> excluded)
- {
- base.GetExcludedPropertiesInInspector(excluded);
- excluded.Add(FieldPath(x => Target.PositionNoise));
- excluded.Add(FieldPath(x => Target.OrientationNoise));
- }
- public override void OnInspectorGUI()
- {
- if (mPosChannels == null)
- mPosChannels = SetupReorderableLists(
- serializedObject.FindProperty(() => Target.PositionNoise), mPoslabels);
- if (mRotChannels == null)
- mRotChannels = SetupReorderableLists(
- serializedObject.FindProperty(() => Target.OrientationNoise), mRotlabels);
- BeginInspector();
- Rect r = EditorGUILayout.GetControlRect();
- mPreviewTime = EditorGUI.Slider(r, "Preview Time", mPreviewTime, 0.01f, 10f);
- r = EditorGUILayout.GetControlRect();
- float labelWidth = GUI.skin.label.CalcSize(mAnimatedLabel).x + EditorGUIUtility.singleLineHeight;
- r.width -= labelWidth + hSpace;
- mPreviewHeight = EditorGUI.Slider(r, "Preview Height", mPreviewHeight, 1f, 10f);
- r.x += r.width + hSpace; r.width = labelWidth;
- mAnimatedPreview = EditorGUI.ToggleLeft(r, mAnimatedLabel, mAnimatedPreview);
- EditorGUILayout.Separator();
- r = EditorGUILayout.GetControlRect();
- EditorGUI.LabelField(r, "Position Noise - amplitudes are in Distance units", EditorStyles.boldLabel);
- r = EditorGUILayout.GetControlRect(true, mPreviewHeight * EditorGUIUtility.singleLineHeight);
- if (Event.current.type == EventType.Repaint)
- {
- mSampleCachePos.SnapshotSample(r.size, Target.PositionNoise, mNoiseOffset, mAnimatedPreview);
- mSampleCachePos.DrawSamplePreview(r, 7);
- }
- for (int i = 0; i < mPosChannels.Length; ++i)
- {
- r = EditorGUILayout.GetControlRect();
- mPosExpanded[i] = EditorGUI.Foldout(r, mPosExpanded[i], mPoslabels[i], true);
- if (mPosExpanded[i])
- {
- r = EditorGUILayout.GetControlRect(true, mPreviewHeight * EditorGUIUtility.singleLineHeight);
- if (Event.current.type == EventType.Repaint)
- mSampleCachePos.DrawSamplePreview(r, 1 << i);
- mPosChannels[i].DoLayoutList();
- }
- }
- EditorGUILayout.Separator();
- r = EditorGUILayout.GetControlRect();
- EditorGUI.LabelField(r, "Rotation Noise - amplitude units are degrees", EditorStyles.boldLabel);
- r = EditorGUILayout.GetControlRect(true, mPreviewHeight * EditorGUIUtility.singleLineHeight);
- if (Event.current.type == EventType.Repaint)
- {
- mSampleCacheRot.SnapshotSample(r.size, Target.OrientationNoise, mNoiseOffset, mAnimatedPreview);
- mSampleCacheRot.DrawSamplePreview(r, 7);
- }
- for (int i = 0; i < mPosChannels.Length; ++i)
- {
- r = EditorGUILayout.GetControlRect();
- mRotExpanded[i] = EditorGUI.Foldout(r, mRotExpanded[i], mRotlabels[i], true);
- if (mRotExpanded[i])
- {
- r = EditorGUILayout.GetControlRect(true, mPreviewHeight * EditorGUIUtility.singleLineHeight);
- if (Event.current.type == EventType.Repaint)
- mSampleCacheRot.DrawSamplePreview(r, 1 << i);
- mRotChannels[i].DoLayoutList();
- }
- }
- serializedObject.ApplyModifiedProperties();
- // Make it live!
- if (mAnimatedPreview && Event.current.type == EventType.Repaint)
- {
- mNoiseOffset += Time.realtimeSinceStartup - mNoiseOffsetBase;
- Repaint();
- }
- mNoiseOffsetBase = Time.realtimeSinceStartup;
- }
- class SampleCache
- {
- private List<Vector3> mSampleCurveX = new List<Vector3>();
- private List<Vector3> mSampleCurveY = new List<Vector3>();
- private List<Vector3> mSampleCurveZ = new List<Vector3>();
- private List<Vector3> mSampleNoise = new List<Vector3>();
- public void SnapshotSample(
- Vector2 areaSize, NoiseSettings.TransformNoiseParams[] signal, float noiseOffset, bool animated)
- {
- // These values give a smoother curve, more-or-less fitting in the window
- int numSamples = Mathf.RoundToInt(areaSize.x);
- if (animated)
- numSamples *= 2;
- const float signalScale = 0.75f;
- float maxVal = 0;
- for (int i = 0; i < signal.Length; ++i)
- {
- maxVal = Mathf.Max(maxVal, Mathf.Abs(signal[i].X.Amplitude * signalScale));
- maxVal = Mathf.Max(maxVal, Mathf.Abs(signal[i].Y.Amplitude * signalScale));
- maxVal = Mathf.Max(maxVal, Mathf.Abs(signal[i].Z.Amplitude * signalScale));
- }
- mSampleNoise.Clear();
- for (int i = 0; i < numSamples; ++i)
- {
- float t = (float)i / (numSamples - 1) * mPreviewTime + noiseOffset;
- Vector3 p = NoiseSettings.GetCombinedFilterResults(signal, t, Vector3.zero);
- mSampleNoise.Add(p);
- }
- mSampleCurveX.Clear();
- mSampleCurveY.Clear();
- mSampleCurveZ.Clear();
- float halfHeight = areaSize.y / 2;
- float yOffset = halfHeight;
- for (int i = 0; i < numSamples; ++i)
- {
- float t = (float)i / (numSamples - 1);
- Vector3 p = mSampleNoise[i];
- mSampleCurveX.Add(new Vector3(areaSize.x * t, halfHeight * Mathf.Clamp(-p.x / maxVal, -1, 1) + yOffset, 0));
- mSampleCurveY.Add(new Vector3(areaSize.x * t, halfHeight * Mathf.Clamp(-p.y / maxVal, -1, 1) + yOffset, 0));
- mSampleCurveZ.Add(new Vector3(areaSize.x * t, halfHeight * Mathf.Clamp(-p.z / maxVal, -1, 1) + yOffset, 0));
- }
- }
- public void DrawSamplePreview(Rect r, int channelMask)
- {
- EditorGUI.DrawRect(r, Color.black);
- var oldMatrix = Handles.matrix;
- Handles.matrix = Handles.matrix * Matrix4x4.Translate(r.position);
- if ((channelMask & 1) != 0)
- {
- Handles.color = new Color(1, 0.5f, 0, 0.8f);
- Handles.DrawPolyLine(mSampleCurveX.ToArray());
- }
- if ((channelMask & 2) != 0)
- {
- Handles.color = new Color(0, 1, 0, 0.8f);
- Handles.DrawPolyLine(mSampleCurveY.ToArray());
- }
- if ((channelMask & 4) != 0)
- {
- Handles.color = new Color(0, 0.5f, 1, 0.8f);
- Handles.DrawPolyLine(mSampleCurveZ.ToArray());
- }
- Handles.color = Color.black;
- Handles.DrawLine(new Vector3(1, 0, 0), new Vector3(r.width, 0, 0));
- Handles.DrawLine(new Vector3(0, r.height, 0), new Vector3(r.width, r.height, 0));
- Handles.matrix = oldMatrix;
- }
- }
- SampleCache mSampleCachePos = new SampleCache();
- SampleCache mSampleCacheRot = new SampleCache();
- private ReorderableList[] SetupReorderableLists(
- SerializedProperty property, GUIContent[] titles)
- {
- ReorderableList[] lists = new ReorderableList[3];
- for (int i = 0; i < 3; ++i)
- lists[i] = SetupReorderableList(property, i, new GUIContent("Components"));
- return lists;
- }
- private ReorderableList SetupReorderableList(
- SerializedProperty property, int channel, GUIContent title)
- {
- ChannelList list = new ChannelList(
- property.serializedObject, property, channel, title);
- list.drawHeaderCallback = (Rect rect) =>
- {
- GUIContent steadyLabel = new GUIContent("(non-random wave if checked)");
- float steadyLabelWidth = GUI.skin.label.CalcSize(steadyLabel).x;
- Rect r = rect;
- EditorGUI.LabelField(r, list.mTitle);
- r.x = rect.x + rect.width - steadyLabelWidth; r.width = steadyLabelWidth;
- EditorGUI.LabelField(r, steadyLabel);
- };
- list.drawElementCallback
- = (Rect rect, int index, bool isActive, bool isFocused) =>
- {
- SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
- switch (list.mChannel)
- {
- case 0: DrawNoiseChannel(rect, element.FindPropertyRelative(() => tpDef.X)); break;
- case 1: DrawNoiseChannel(rect, element.FindPropertyRelative(() => tpDef.Y)); break;
- case 2: DrawNoiseChannel(rect, element.FindPropertyRelative(() => tpDef.Z)); break;
- default: break;
- }
- };
- list.onAddCallback = (ReorderableList l) =>
- {
- var index = l.serializedProperty.arraySize;
- ++l.serializedProperty.arraySize;
- SerializedProperty p = l.serializedProperty.GetArrayElementAtIndex(index);
- ClearComponent(p.FindPropertyRelative(() => tpDef.X));
- ClearComponent(p.FindPropertyRelative(() => tpDef.Y));
- ClearComponent(p.FindPropertyRelative(() => tpDef.Z));
- };
- list.onRemoveCallback = (ReorderableList l) =>
- {
- // Can't just delete because the component arrays are connected
- SerializedProperty p = l.serializedProperty.GetArrayElementAtIndex(l.index);
- bool IsClear
- = (list.mChannel == 0 || IsClearComponent(p.FindPropertyRelative(() => tpDef.X)))
- && (list.mChannel == 1 || IsClearComponent(p.FindPropertyRelative(() => tpDef.Y)))
- && (list.mChannel == 2 || IsClearComponent(p.FindPropertyRelative(() => tpDef.Z)));
- if (IsClear)
- l.serializedProperty.DeleteArrayElementAtIndex(l.index);
- else switch (list.mChannel)
- {
- case 0: ClearComponent(p.FindPropertyRelative(() => tpDef.X)); break;
- case 1: ClearComponent(p.FindPropertyRelative(() => tpDef.Y)); break;
- case 2: ClearComponent(p.FindPropertyRelative(() => tpDef.Z)); break;
- default: break;
- }
- };
- return list;
- }
- class ChannelList : ReorderableList
- {
- public int mChannel;
- public GUIContent mTitle;
- public ChannelList(
- SerializedObject serializedObject,
- SerializedProperty elements,
- int channel, GUIContent title)
- : base(serializedObject, elements, true, true, true, true)
- {
- mChannel = channel;
- mTitle = title;
- }
- };
-
- private GUIContent steadyLabel;
- private GUIContent freqLabel;
- private float freqLabelWidth;
- private GUIContent ampLabel;
- private float ampLabelWidth;
- private void InitializeLabels(SerializedProperty property)
- {
- if (steadyLabel == null)
- {
- SerializedProperty p = property.FindPropertyRelative(() => npDef.Constant);
- steadyLabel = new GUIContent(p.displayName, p.tooltip) { text = " " };
- }
- if (freqLabel == null)
- {
- SerializedProperty p = property.FindPropertyRelative(() => npDef.Frequency);
- freqLabel = new GUIContent(p.displayName, p.tooltip);
- freqLabelWidth = GUI.skin.label.CalcSize(freqLabel).x;
- }
- if (ampLabel == null)
- {
- SerializedProperty p = property.FindPropertyRelative(() => npDef.Amplitude);
- ampLabel = new GUIContent(p.displayName, p.tooltip);
- ampLabelWidth = GUI.skin.label.CalcSize(ampLabel).x;
- }
- }
- private void DrawNoiseChannel(Rect rect, SerializedProperty property)
- {
- InitializeLabels(property);
- Rect r = rect;
- r.height -= vSpace;
- r.width -= EditorGUIUtility.singleLineHeight + hSpace;
- r.width /= 2;
- float oldLabelWidth = EditorGUIUtility.labelWidth;
- EditorGUIUtility.labelWidth = freqLabelWidth;
- EditorGUI.PropertyField(r, property.FindPropertyRelative(() => npDef.Frequency), freqLabel);
- r.x += r.width + hSpace;
- EditorGUIUtility.labelWidth = ampLabelWidth;
- EditorGUI.PropertyField(r, property.FindPropertyRelative(() => npDef.Amplitude), ampLabel);
- r.y -= 1;
- r.x += r.width + hSpace; r.width = EditorGUIUtility.singleLineHeight + hSpace;
- EditorGUIUtility.labelWidth = hSpace;
- EditorGUI.PropertyField(r, property.FindPropertyRelative(() => npDef.Constant), steadyLabel);
- EditorGUIUtility.labelWidth = oldLabelWidth;
- }
- // SerializedProperty is a NoiseSettings.NoiseParam
- void ClearComponent(SerializedProperty p)
- {
- p.FindPropertyRelative(() => npDef.Amplitude).floatValue = 0;
- p.FindPropertyRelative(() => npDef.Frequency).floatValue = 0;
- p.FindPropertyRelative(() => npDef.Constant).boolValue = false;
- }
- // SerializedProperty is a NoiseSettings.NoiseParam
- bool IsClearComponent(SerializedProperty p)
- {
- return p.FindPropertyRelative(() => npDef.Amplitude).floatValue == 0
- && p.FindPropertyRelative(() => npDef.Frequency).floatValue == 0;
- }
- }
- }
|