/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /************************************************************************************ * Filename : ONSPPropagationMaterialEditor.cs * Content : Propagation material editor class Attach to geometry to define material properties ***********************************************************************************/ using UnityEditor; using UnityEngine; using Spectrum = ONSPPropagationMaterial.Spectrum; using Point = ONSPPropagationMaterial.Point; [CustomEditor(typeof(ONSPPropagationMaterial))] internal sealed class ONSPPropagationMaterialEditor : Editor{ private enum AxisScale{ Lin, Log, Sqr } private sealed class SpectrumDrawer{ private const float cutoff = 20000f; private static readonly Texture2D texture = EditorGUIUtility.whiteTexture; private static readonly GUIStyle textStyle = new GUIStyle{ alignment = TextAnchor.MiddleLeft, clipping = TextClipping.Overflow, fontSize = 8, fontStyle = FontStyle.Bold, wordWrap = false, normal = new GUIStyleState{ textColor = Color.grey }, focused = new GUIStyleState{ textColor = Color.grey } }; private static int focus; private readonly string label; private readonly AxisScale scale; private readonly Spectrum spectrum; private bool dragInitiated; private bool isDragging; private bool displaySpectrum; private bool displayPoints; internal bool IsFocus{ get{ return focus == spectrum.GetHashCode(); } } internal SpectrumDrawer(string label, Spectrum spectrum, AxisScale scale){ this.label = label; this.spectrum = spectrum; this.scale = scale; } internal void Draw(Event e){ displaySpectrum = EditorGUILayout.Foldout(displaySpectrum, label); if(displaySpectrum){ EditorGUI.indentLevel++; DrawSpectrum(e); displayPoints = EditorGUILayout.Foldout(displayPoints, "Points"); if(displayPoints){ EditorGUI.indentLevel++; DrawPoints(); EditorGUI.indentLevel--; } EditorGUI.indentLevel--; } } internal void LoadFoldoutState(){ displaySpectrum = EditorPrefs.GetBool(label + "Spectrum", true); displayPoints = EditorPrefs.GetBool(label + "Points", false); } internal void SaveFoldoutState(){ EditorPrefs.SetBool(label + "Spectrum", displaySpectrum); EditorPrefs.SetBool(label + "Points", displayPoints); } private void DrawSpectrum(Event e){ float height = 10 * EditorGUIUtility.singleLineHeight; Rect r = EditorGUILayout.GetControlRect(true, height); r.width -= rightMargin; DrawDataTicks(r); r = AudioCurveRendering.BeginCurveFrame(r); AudioCurveRendering.DrawFilledCurve(r, EvaluateCurve, AudioCurveRendering.kAudioOrange); DrawFrequencyTicks(r); HandleEvent(r, e); if(IsFocus) DrawSelected(r); AudioCurveRendering.EndCurveFrame(); } private void DrawPoints(){ var points = spectrum.points; int lines = points.Count > 0 ? points.Count + 2 : 1; float height = EditorGUIUtility.singleLineHeight * lines; Rect r1 = EditorGUILayout.GetControlRect(true, height); r1.width -= rightMargin; r1.height = EditorGUIUtility.singleLineHeight; { int oldCount = points.Count; int newCount = EditorGUI.DelayedIntField(r1, "Size", oldCount); r1.y += r1.height; if(newCount < points.Count){ points.RemoveRange(newCount, oldCount - newCount); Undo.SetCurrentGroupName("Points Removed"); GUI.changed = true; } else if(newCount > oldCount){ if(newCount > points.Capacity) points.Capacity = newCount; for(int i = oldCount; i < newCount; i++) points.Add(new Point(125 * (1 << i))); Undo.SetCurrentGroupName("Points Added"); GUI.changed = true; } } if(points.Count > 0){ Rect r2 = new Rect(r1.xMax + 9, r1.y + r1.height * 1.125f, 24, r1.height * .75f); r1.width /= 2; EditorGUI.LabelField(r1, "Frequency"); r1.x += r1.width; EditorGUI.LabelField(r1, "Data"); r1.x -= r1.width; r1.y += r1.height; for(int i = 0; i < points.Count; i++){ points[i].frequency = EditorGUI.FloatField(r1, points[i].frequency); points[i].frequency = Mathf.Clamp(points[i].frequency, 0f, cutoff); r1.x += r1.width; points[i].data = EditorGUI.FloatField(r1, points[i].data); points[i].data = Mathf.Clamp01(points[i].data); r1.x -= r1.width; r1.y += r1.height; if(GUI.Button(r2, "–")){ RemovePointAt(i); break; } r2.y += r1.height; } } } private void DrawDataTicks(Rect r){ const int ticks = 10; Rect label = new Rect(r.xMax + 9, r.y - r.height / (2 * ticks), 24, r.height / ticks); Rect tick = new Rect(r.xMax + 2, r.y - 1, 4.5f, 2); for(int i = 0; i <= ticks; i++){ float value = MapData(1 - (float)i / ticks, false); EditorGUI.DrawRect(tick, textStyle.normal.textColor); GUI.Label(label, value.ToString("0.000"), textStyle); tick.y += label.height; label.y += label.height; } } private void DrawFrequencyTicks(Rect r){ Rect tick = new Rect(r.x, r.y, 1, r.height); Rect label = new Rect(r.x, r.yMax - 1.5f * EditorGUIUtility.singleLineHeight, 32, EditorGUIUtility.singleLineHeight); for(int i = 1; i < 30; i++){ float frequency; if(MapFrequencyTick(i, out frequency)){ tick.x = MapFrequency(frequency) * r.width; tick.height = label.y - r.y; tick.width = 2; EditorGUI.DrawRect(tick, textStyle.normal.textColor); tick.y = label.yMax; tick.height = r.yMax - label.yMax; EditorGUI.DrawRect(tick, textStyle.normal.textColor); label.x = tick.x - 2; GUI.Label(label, FrequencyToString(frequency), textStyle); tick.y = r.y; tick.height = r.height; tick.width = 1; } else{ tick.x = MapFrequency(frequency) * r.width; EditorGUI.DrawRect(tick, textStyle.normal.textColor); } } } private void DrawSelected(Rect r){ if(spectrum.points.Count > spectrum.selection){ const float radius = 12; Vector2 position = MapPointPosition(r, spectrum.points[spectrum.selection]); Vector2 size = new Vector2(radius, radius); r = new Rect(position - size / 2, size); #if UNITY_5 GUI.DrawTexture(r, texture, ScaleMode.StretchToFill, false, 0); GUI.DrawTexture(r, texture, ScaleMode.StretchToFill, false, 0); #else GUI.DrawTexture(r, texture, ScaleMode.StretchToFill, false, 0, Color.white, 0, radius); GUI.DrawTexture(r, texture, ScaleMode.StretchToFill, false, 0, Color.black, 2, radius); #endif } } private void HandleEvent(Rect r, Event e){ Vector2 position = e.mousePosition; switch(e.type){ case EventType.MouseDown: if(r.Contains(position)){ if(e.clickCount == 2){ spectrum.selection = spectrum.points.Count; spectrum.points.Add(MapMouseEvent(r, position)); Undo.SetCurrentGroupName("Point Added"); GUI.changed = true; } else{ int selection = spectrum.selection; float minDistance = float.MaxValue; for(int i = 0; i < spectrum.points.Count; i++){ float distance = Vector2.Distance(MapPointPosition(r, spectrum.points[i]), position); if(distance < minDistance){ selection = i; minDistance = distance; } } if(selection != spectrum.selection){ spectrum.selection = selection; Undo.SetCurrentGroupName("Point Selected"); GUI.changed = true; } } focus = spectrum.GetHashCode(); dragInitiated = true; } else{ isDragging = false; focus = 0; } e.Use(); break; case EventType.MouseDrag: if(dragInitiated){ dragInitiated = false; isDragging = true; } if(isDragging && spectrum.selection < spectrum.points.Count){ spectrum.points[spectrum.selection] = MapMouseEvent(r, position); e.Use(); } break; case EventType.Ignore: case EventType.MouseUp: dragInitiated = false; if(isDragging){ isDragging = false; Undo.SetCurrentGroupName("Point Moved"); GUI.changed = true; e.Use(); } break; case EventType.KeyDown: switch(e.keyCode){ case KeyCode.Delete: case KeyCode.Backspace: if(spectrum.selection < spectrum.points.Count){ RemovePointAt(spectrum.selection); e.Use(); } break; } break; } } private void RemovePointAt(int index){ spectrum.points.RemoveAt(index); if(spectrum.selection == index) spectrum.selection = spectrum.points.Count; Undo.SetCurrentGroupName("Point Removed"); GUI.changed = true; } private float EvaluateCurve(float f){ return 2 * MapData(spectrum[MapFrequency(f, false)]) - 1; } private Vector2 MapPointPosition(Rect r, Point point){ return new Vector2{ x = r.x + r.width * MapFrequency(point.frequency), y = r.y + r.height * (1 - MapData(point.data)) }; } private Point MapMouseEvent(Rect r, Vector2 v){ return new Point{ frequency = v.x < r.xMin ? 0 : v.x > r.xMax ? cutoff : MapFrequency((v.x - r.x) / r.width, false), data = v.y < r.yMin ? 1 : v.y > r.yMax ? 0 : MapData(1 - (v.y - r.y) / r.height, false) }; } private float MapData(float f, bool forward = true){ switch(scale){ case AxisScale.Log: return forward ? f < 1e-3f ? 0 : 1 + Mathf.Log10(f) / 3 : Mathf.Pow(10, -3 * (1 - f)); case AxisScale.Sqr: return forward ? Mathf.Sqrt(f) : f * f; default: case AxisScale.Lin: return f; } } public static bool MapFrequencyTick(int i, out float frequency){ int power = i / 9 + 1; int multiplier = i % 9 + 1; frequency = multiplier * Mathf.Pow(10, power); return multiplier == 1; } public static float MapFrequency(float f, bool forward = true){ return forward ? f < 10 ? 0 : Mathf.Log(f / 10, cutoff / 10) : 10 * Mathf.Pow(cutoff / 10, f); } private static string FrequencyToString(float frequency){ if(frequency < 1000) return string.Format("{0:F0} Hz", frequency); else return string.Format("{0:F0} kHz", frequency * .001f); } } private const float rightMargin = 36; private SpectrumDrawer absorption, scattering, transmission; private void OnEnable(){ GetSpectra(out absorption, out scattering, out transmission); absorption.LoadFoldoutState(); scattering.LoadFoldoutState(); transmission.LoadFoldoutState(); } private void OnDisable(){ absorption.SaveFoldoutState(); scattering.SaveFoldoutState(); transmission.SaveFoldoutState(); } public override void OnInspectorGUI(){ ONSPPropagationMaterial material = target as ONSPPropagationMaterial; EditorGUI.BeginChangeCheck(); Rect r = EditorGUILayout.GetControlRect(); r.width -= rightMargin; ONSPPropagationMaterial.Preset newPreset = (ONSPPropagationMaterial.Preset)EditorGUI.EnumPopup(r, "Preset", material.preset); Event e = Event.current; EventType type = e.type; absorption.Draw(e); scattering.Draw(e); transmission.Draw(e); if(EditorGUI.EndChangeCheck()){ string groupName = Undo.GetCurrentGroupName(); Undo.RegisterCompleteObjectUndo(material, groupName); if(groupName == "Point Added") Undo.CollapseUndoOperations(Undo.GetCurrentGroup() - 1); if(material.preset != newPreset) material.preset = newPreset; else material.preset = ONSPPropagationMaterial.Preset.Custom; if(Application.isPlaying) material.UploadMaterial(); } } private void GetSpectra(out SpectrumDrawer absorption, out SpectrumDrawer scattering, out SpectrumDrawer transmission){ ONSPPropagationMaterial material = target as ONSPPropagationMaterial; absorption = new SpectrumDrawer("Absorption", material.absorption, AxisScale.Sqr); scattering = new SpectrumDrawer("Scattering", material.scattering, AxisScale.Lin); transmission = new SpectrumDrawer("Transmission", material.transmission, AxisScale.Sqr); } }