572 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			572 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| /*
 | ||
|  * 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);
 | ||
| 
 | ||
|   }
 | ||
| 
 | ||
| }
 |