#if UNITY_EDITOR using UnityEngine; using UnityEditor; using UnityEditor.IMGUI.Controls; namespace VLB { public static class EditorExtensions { public static class SD { public static GameObject NewBeam() { return new GameObject("Volumetric Light Beam SD", typeof(VolumetricLightBeamSD)); } public static GameObject NewBeam2D() { var gao = new GameObject("Volumetric Light Beam SD (2D)", typeof(VolumetricLightBeamSD)); gao.GetComponent().dimensions = Dimensions.Dim2D; return gao; } public static GameObject NewBeamAndDust() { return new GameObject("Volumetric Light Beam SD + Dust", typeof(VolumetricLightBeamSD), typeof(VolumetricDustParticles)); } public static GameObject NewSpotLightAndBeam() { var light = Utils.NewWithComponent("Spotlight and Beam SD"); light.type = LightType.Spot; var gao = light.gameObject; gao.AddComponent(); return gao; } } public static class HD { public static GameObject NewBeam() { return new GameObject("Volumetric Light Beam HD", typeof(VolumetricLightBeamHD)); } public static GameObject NewBeam2D() { return new GameObject("Volumetric Light Beam HD (2D)", typeof(VolumetricLightBeamHD2D)); } public static GameObject NewBeamAndDust() { return new GameObject("Volumetric Light Beam HD + Dust", typeof(VolumetricLightBeamHD), typeof(VolumetricDustParticles)); } public static GameObject NewSpotLightAndBeam() { var light = Utils.NewWithComponent("Spotlight and Beam HD"); light.type = LightType.Spot; var gao = light.gameObject; gao.AddComponent().scalable = false; return gao; } } public static void OnNewGameObjectCreated(GameObject gao, MenuCommand menuCommand) { Debug.Assert(gao != null); Debug.Assert(menuCommand != null); OnNewGameObjectCreated(gao, menuCommand.context as GameObject); } public static void OnNewGameObjectCreated(GameObject gao, GameObject parentGao) { Debug.Assert(gao != null); if (parentGao == null) { #if UNITY_2021_2_OR_NEWER var currentPrefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); #else var currentPrefabStage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); #endif if (currentPrefabStage != null) { // The user is in prefab mode without any GAO selected in the hierarchy. Get the current prefab root as parent. parentGao = currentPrefabStage.prefabContentsRoot; } } GameObjectUtility.SetParentAndAlign(gao, parentGao); // Ensure it gets reparented if this was a context click (otherwise does nothing) Undo.RegisterCreatedObjectUndo(gao, "Create " + gao.name); // Register the creation in the undo system Selection.activeObject = gao; } public static bool CanAddComponentFromEditor(VolumetricLightBeamAbstractBase self) where TComp : Component { return !Application.isPlaying && self != null && self.GetComponent() == null; } public static void AddComponentFromEditor(VolumetricLightBeamAbstractBase self) where TComp : Component { if (CanAddComponentFromEditor(self)) { /*var comp =*/ Undo.AddComponent(self.gameObject); } } /// /// Add a EditorGUILayout.ToggleLeft which properly handles multi-object editing /// public static void ToggleLeft(this SerializedProperty prop, GUIContent label, params GUILayoutOption[] options) { ToggleLeft(prop, label, prop.boolValue, options); } public static void ToggleLeft(this SerializedProperty prop, GUIContent label, bool forcedValue, params GUILayoutOption[] options) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = prop.hasMultipleDifferentValues; var newValue = EditorGUILayout.ToggleLeft(label, forcedValue, options); EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) prop.boolValue = newValue; } public static bool HasAtLeastOneValue(this SerializedProperty prop, bool value) { return (prop.boolValue == value) || prop.hasMultipleDifferentValues; } public static bool HasAtLeastOnePositiveValue(this SerializedProperty prop) { return (prop.floatValue >= 0.0f) || prop.hasMultipleDifferentValues; } /// /// Create a EditorGUILayout.Slider which properly handles multi-object editing /// We apply the 'convIn' conversion to the SerializedProperty value before exposing it as a Slider. /// We apply the 'convOut' conversion to the Slider value to get the SerializedProperty back. /// /// The value the slider shows. /// Label in front of the slider. /// The value at the left end of the slider. /// The value at the right end of the slider. /// Conversion applied on the SerializedProperty to get the Slider value /// Conversion applied on the Slider value to get the SerializedProperty public static bool FloatSlider( this SerializedProperty prop, GUIContent label, float leftValue, float rightValue, System.Func convIn, System.Func convOut, params GUILayoutOption[] options) { var floatValue = convIn(prop.floatValue); EditorGUI.BeginChangeCheck(); { EditorGUI.showMixedValue = prop.hasMultipleDifferentValues; { floatValue = EditorGUILayout.Slider(label, floatValue, leftValue, rightValue, options); } EditorGUI.showMixedValue = false; } if (EditorGUI.EndChangeCheck()) { prop.floatValue = convOut(floatValue); return true; } return false; } public static bool FloatSlider( this SerializedProperty prop, GUIContent label, float leftValue, float rightValue, params GUILayoutOption[] options) { var floatValue = prop.floatValue; EditorGUI.BeginChangeCheck(); { EditorGUI.showMixedValue = prop.hasMultipleDifferentValues; { floatValue = EditorGUILayout.Slider(label, floatValue, leftValue, rightValue, options); } EditorGUI.showMixedValue = false; } if (EditorGUI.EndChangeCheck()) { prop.floatValue = floatValue; return true; } return false; } /* public static void ToggleFromLight(this SerializedProperty prop) { ToggleLeft( prop, new GUIContent("From Spot", "Get the value from the Light Spot"), GUILayout.MaxWidth(80.0f)); } */ public static void ToggleUseGlobalNoise(this SerializedProperty prop) { ToggleLeft( prop, new GUIContent("Global", "Get the value from the Global 3D Noise"), GUILayout.MaxWidth(55.0f)); } public static void CustomEnum(this SerializedProperty prop, GUIContent content, string[] descriptions = null) { if(descriptions == null) descriptions = System.Enum.GetNames(typeof(EnumType)); Debug.Assert(System.Enum.GetNames(typeof(EnumType)).Length == descriptions.Length, string.Format("Enum '{0}' and the description array don't have the same size", typeof(EnumType))); int enumValueIndex = prop.enumValueIndex; EditorGUI.BeginChangeCheck(); { EditorGUI.showMixedValue = prop.hasMultipleDifferentValues; { #if UNITY_2018_1_OR_NEWER enumValueIndex = EditorGUILayout.Popup(content, enumValueIndex, descriptions); #else enumValueIndex = EditorGUILayout.Popup(content.text, enumValueIndex, descriptions); #endif } EditorGUI.showMixedValue = false; } if (EditorGUI.EndChangeCheck()) prop.enumValueIndex = enumValueIndex; } public static void CustomMask(this SerializedProperty prop, GUIContent content, string[] descriptions = null) { if (descriptions == null) descriptions = System.Enum.GetNames(typeof(EnumType)); Debug.Assert(System.Enum.GetNames(typeof(EnumType)).Length == descriptions.Length, string.Format("Enum '{0}' and the description array don't have the same size", typeof(EnumType))); int intValue = prop.intValue; EditorGUI.BeginChangeCheck(); { EditorGUI.showMixedValue = prop.hasMultipleDifferentValues; { intValue = EditorGUILayout.MaskField(content, intValue, descriptions); } EditorGUI.showMixedValue = false; } if (EditorGUI.EndChangeCheck()) prop.intValue = intValue; } public static void DrawLineSeparator() { DrawLineSeparator(Color.grey, 1, 10); } static void DrawLineSeparator(Color color, int thickness = 2, int padding = 10) { Rect r = EditorGUILayout.GetControlRect(GUILayout.Height(padding + thickness)); r.x = 0; r.width = EditorGUIUtility.currentViewWidth; r.y += padding / 2; r.height = thickness; EditorGUI.DrawRect(r, color); } public static bool GlobalToggleButton(ref bool boolean, GUIContent content, string saveString, float maxWidth = 999.0f) { EditorGUI.BeginChangeCheck(); boolean = GUILayout.Toggle(boolean, content, EditorStyles.miniButton, GUILayout.MaxWidth(maxWidth)); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetBool(saveString, boolean); SceneView.RepaintAll(); return true; } return false; } public abstract class EditorGUIWidth : System.IDisposable { protected abstract void ApplyWidth(float width); public EditorGUIWidth(float width) { ApplyWidth(width); } public void Dispose() { ApplyWidth(0.0f); } } public class LabelWidth : EditorGUIWidth { public LabelWidth(float width) : base(width) { } protected override void ApplyWidth(float width) { EditorGUIUtility.labelWidth = width; } } public class FieldWidth : EditorGUIWidth { public FieldWidth(float width) : base(width) { } protected override void ApplyWidth(float width) { EditorGUIUtility.fieldWidth = width; } } public class ShowMixedValue : System.IDisposable { public ShowMixedValue(bool? value) { m_PrevValue = EditorGUI.showMixedValue; EditorGUI.showMixedValue = value ?? false; } public ShowMixedValue(SerializedProperty prop) : this(prop?.hasMultipleDifferentValues) { } public void Dispose() { EditorGUI.showMixedValue = m_PrevValue; } bool m_PrevValue = false; } private static ArcHandle ms_ArcHandle = null; // HANDLES public static ArcHandle DrawHandleRadius(float radius) { if (ms_ArcHandle == null) ms_ArcHandle = new ArcHandle(); ms_ArcHandle.SetColorWithRadiusHandle(Color.white, 0f); ms_ArcHandle.angle = 360f; ms_ArcHandle.angleHandleSizeFunction = null; ms_ArcHandle.angleHandleColor = Color.clear; ms_ArcHandle.radius = radius; ms_ArcHandle.DrawHandle(); return ms_ArcHandle; } public static ArcHandle DrawHandleSpotAngle(float angle, float radius) { if (ms_ArcHandle == null) ms_ArcHandle = new ArcHandle(); ms_ArcHandle.angleHandleSizeFunction = ArcHandle.DefaultAngleHandleSizeFunction; ms_ArcHandle.SetColorWithRadiusHandle(Handles.color, Handles.color.maxColorComponent < 0.5f ? 0.25f : 0.1f); ms_ArcHandle.angleHandleColor = Handles.color; ms_ArcHandle.radius = radius; ms_ArcHandle.angle = angle; ms_ArcHandle.DrawHandle(); return ms_ArcHandle; } } } #endif