342 lines
14 KiB
C#
342 lines
14 KiB
C#
#if UNITY_EDITOR
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
|
|
namespace VLB
|
|
{
|
|
public abstract class Editor_VolumetricLightBeamAbstractBase : Editor_Common
|
|
{
|
|
TargetList<VolumetricLightBeamAbstractBase> m_Targets;
|
|
|
|
protected override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
m_Targets = new TargetList<VolumetricLightBeamAbstractBase>(targets);
|
|
}
|
|
|
|
protected string GetBatchingReport()
|
|
{
|
|
if (m_Targets.Count > 1)
|
|
{
|
|
string reasons = "";
|
|
for (int i = 1; i < m_Targets.Count; ++i)
|
|
{
|
|
if (!BatchingHelper.CanBeBatched(m_Targets[0], m_Targets[i], ref reasons))
|
|
{
|
|
return "Selected beams can't be batched together:\n" + reasons;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
protected void AddComponentToTargets<T>() where T : MonoBehaviour
|
|
{
|
|
foreach (var target in m_Targets) EditorExtensions.AddComponentFromEditor<T>(target);
|
|
}
|
|
|
|
protected void DrawEditInSceneButton()
|
|
{
|
|
EditorGUI.BeginChangeCheck();
|
|
bool editInScene = EditorPrefs.GetBool(EditorStrings.Beam.PrefEditInScene, false);
|
|
editInScene = GUILayout.Toggle(editInScene, EditorStrings.Beam.ButtonEditInScene, EditorStyles.miniButton);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
EditorPrefs.SetBool(EditorStrings.Beam.PrefEditInScene, editInScene);
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
|
|
protected bool DrawFallOffSliderHandle(Transform beamTransf, Vector3 beamGlobalForward, Vector3 beamScale, float lineStartOffset, float lineAlpha, Handles.CapFunction capFunction, string recordName, System.Func<float> getValue, System.Action<float> setValue)
|
|
{
|
|
Debug.Assert(beamTransf != null);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
var sliderPos = beamTransf.position + getValue() * beamScale.z * beamGlobalForward;
|
|
sliderPos = Handles.Slider(sliderPos, beamGlobalForward, HandleUtility.GetHandleSize(sliderPos) * 0.1f, capFunction, 0.5f);
|
|
|
|
Handles.Label(sliderPos, string.Format("{0} {1:0.0}", recordName, getValue()), labelStyle);
|
|
|
|
var savedCol = Handles.color;
|
|
{
|
|
var col = savedCol;
|
|
col.a = lineAlpha;
|
|
Handles.color = col;
|
|
Handles.DrawLine(beamTransf.position + beamGlobalForward * lineStartOffset, sliderPos);
|
|
Handles.color = savedCol;
|
|
}
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
Undo.RecordObject(target, string.Format("Edit Beam '{0}'", recordName));
|
|
|
|
var sliderVec = (sliderPos - beamTransf.position);
|
|
var sliderLength = 0.0f;
|
|
if (Vector3.Dot(sliderVec, beamGlobalForward) > 0.0f)
|
|
sliderLength = sliderVec.magnitude;
|
|
|
|
sliderLength /= beamScale.z;
|
|
setValue(sliderLength);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected GUIStyle labelStyle
|
|
{
|
|
get
|
|
{
|
|
var style = new GUIStyle(GUI.skin.GetStyle("Label"));
|
|
style.normal.textColor = Handles.color;
|
|
style.alignment = TextAnchor.LowerCenter;
|
|
style.fixedWidth = 200.0f;
|
|
return style;
|
|
}
|
|
}
|
|
|
|
bool DrawRadiusHandle(Transform beamTransf, Vector3 beamGlobalForward, Vector3 beamScale, Vector3 pos, string recordName, System.Func<float> getValue, System.Action<float> setValue)
|
|
{
|
|
Debug.Assert(beamTransf != null);
|
|
|
|
var rot = Quaternion.LookRotation(beamTransf.up, beamGlobalForward);
|
|
|
|
UnityEditor.IMGUI.Controls.ArcHandle hdl = null;
|
|
|
|
float maxScaleXY = Mathf.Max(beamScale.x, beamScale.y);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
{
|
|
using (new Handles.DrawingScope(Matrix4x4.TRS(pos, rot * Quaternion.Euler(0.0f, 45.0f, 0.0f), Vector3.one)))
|
|
{
|
|
hdl = EditorExtensions.DrawHandleRadius(getValue() * maxScaleXY);
|
|
}
|
|
|
|
using (new Handles.DrawingScope(Matrix4x4.TRS(pos, rot, Vector3.one)))
|
|
{
|
|
Handles.Label(getValue() * maxScaleXY * Vector3.forward, string.Format("{0} {1:0.00}", recordName, getValue()), labelStyle);
|
|
}
|
|
}
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
Undo.RecordObject(target, string.Format("Edit Beam '{0}'", recordName));
|
|
setValue(hdl.radius / maxScaleXY);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DrawSpotAngleHandle(Transform beamTransf, Vector3 beamGlobalForward, Vector3 pos, float handleRadius, string recordName, System.Func<float> getValue, System.Action<float> setValue)
|
|
{
|
|
Debug.Assert(beamTransf != null);
|
|
|
|
var left = Vector3.Cross(beamGlobalForward, beamTransf.up); // show angle handle properly with 2d beams
|
|
var rot = Quaternion.LookRotation(beamGlobalForward, left);
|
|
|
|
UnityEditor.IMGUI.Controls.ArcHandle hdl = null;
|
|
EditorGUI.BeginChangeCheck();
|
|
{
|
|
using (new Handles.DrawingScope(Matrix4x4.TRS(pos, rot * Quaternion.Euler(0.0f, -getValue() / 2, 0.0f), Vector3.one)))
|
|
{
|
|
hdl = EditorExtensions.DrawHandleSpotAngle(getValue(), handleRadius);
|
|
}
|
|
|
|
using (new Handles.DrawingScope(Matrix4x4.TRS(pos, rot, Vector3.one)))
|
|
{
|
|
Handles.Label(Vector3.forward * handleRadius, string.Format("{0} {1:0.0}", recordName, getValue()), labelStyle);
|
|
}
|
|
}
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
Undo.RecordObject(target, string.Format("Edit Beam '{0}'", recordName));
|
|
setValue(hdl.angle);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static Vector3 GetBeamGlobalForward(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.beamGlobalForward;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.beamGlobalForward;
|
|
return Vector3.forward;
|
|
}
|
|
|
|
static Vector3 GetBeamLossyScaleIfScalable(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam.IsScalable())
|
|
return beam.GetLossyScale();
|
|
return Vector3.one;
|
|
}
|
|
|
|
static float GetBeamConeRadiusStart(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.coneRadiusStart;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.coneRadiusStart;
|
|
return 0.0f;
|
|
}
|
|
|
|
static void SetBeamConeRadiusStart(VolumetricLightBeamAbstractBase beam, float radius)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) sd.coneRadiusStart = radius;
|
|
if (beam is VolumetricLightBeamHD hd) hd.coneRadiusStart = radius;
|
|
}
|
|
|
|
static float GetBeamConeRadiusEnd(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.coneRadiusEnd;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.coneRadiusEnd;
|
|
return 1.0f;
|
|
}
|
|
|
|
static void SetBeamConeRadiusEnd(VolumetricLightBeamAbstractBase beam, float radius)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) sd.coneRadiusEnd = radius;
|
|
if (beam is VolumetricLightBeamHD hd) hd.coneRadiusEnd = radius;
|
|
}
|
|
|
|
static float GetBeamSpotAngle(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.spotAngle;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.spotAngle;
|
|
return 1.0f;
|
|
}
|
|
|
|
static void SetBeamSpotAngle(VolumetricLightBeamAbstractBase beam, float radius)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) sd.spotAngle = radius;
|
|
if (beam is VolumetricLightBeamHD hd) hd.spotAngle = radius;
|
|
}
|
|
|
|
static float GetBeamFallOffEnd(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.fallOffEnd;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.fallOffEnd;
|
|
return 1.0f;
|
|
}
|
|
|
|
static void SetBeamFallOffEnd(VolumetricLightBeamAbstractBase beam, float radius)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) sd.fallOffEnd = radius;
|
|
if (beam is VolumetricLightBeamHD hd) hd.fallOffEnd = radius;
|
|
}
|
|
|
|
static float GetBeamFallOffStart(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.fallOffStart;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.fallOffStart;
|
|
return 1.0f;
|
|
}
|
|
|
|
static bool GetBeamHasMeshSkewing(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.hasMeshSkewing;
|
|
return false;
|
|
}
|
|
|
|
static bool GetBeamUseSpotAngleFromLight(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.spotAngleFromLight;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.useSpotAngleFromAttachedLightSpot;
|
|
return false;
|
|
}
|
|
|
|
static bool GetBeamUseFallOffEndFromLight(VolumetricLightBeamAbstractBase beam)
|
|
{
|
|
if (beam is VolumetricLightBeamSD sd) return sd.fallOffEndFromLight;
|
|
if (beam is VolumetricLightBeamHD hd) return hd.useFallOffEndFromAttachedLightSpot;
|
|
return false;
|
|
}
|
|
|
|
protected virtual bool DrawEditInSceneHandles()
|
|
{
|
|
bool editInScene = EditorPrefs.GetBool(EditorStrings.Beam.PrefEditInScene, false);
|
|
if (!editInScene)
|
|
return false;
|
|
|
|
var beam = target as VolumetricLightBeamAbstractBase;
|
|
Debug.Assert(beam != null);
|
|
|
|
bool update = false;
|
|
|
|
VolumetricLightBeamSD.AttachedLightType lightType;
|
|
var hasSpotLight = beam.GetLightSpotAttachedSlow(out lightType) != null;
|
|
|
|
Handles.color = beam.ComputeColorAtDepth(0.0f).ComputeComplementaryColor(true);
|
|
{
|
|
update |= DrawRadiusHandle(beam.transform, GetBeamGlobalForward(beam), GetBeamLossyScaleIfScalable(beam), beam.transform.position, "Source Radius", () => GetBeamConeRadiusStart(beam), (float v) => SetBeamConeRadiusStart(beam, v));
|
|
|
|
if (!hasSpotLight || !GetBeamUseSpotAngleFromLight(beam))
|
|
{
|
|
update |= DrawSpotAngleHandle(beam.transform, GetBeamGlobalForward(beam), beam.transform.position, GetBeamFallOffEnd(beam) / 2, "Spot Angle", () => GetBeamSpotAngle(beam), (float v) => SetBeamSpotAngle(beam, v));
|
|
}
|
|
}
|
|
|
|
if (!GetBeamHasMeshSkewing(beam))
|
|
{
|
|
Handles.color = beam.ComputeColorAtDepth(1.0f).ComputeComplementaryColor(true);
|
|
{
|
|
if (!hasSpotLight || !GetBeamUseFallOffEndFromLight(beam))
|
|
{
|
|
update |= DrawFallOffSliderHandle(beam.transform, GetBeamGlobalForward(beam), GetBeamLossyScaleIfScalable(beam), GetBeamFallOffStart(beam), 0.5f, Handles.CubeHandleCap, "Range Limit", () => GetBeamFallOffEnd(beam), (float v) => SetBeamFallOffEnd(beam, v));
|
|
}
|
|
|
|
if (!hasSpotLight || !GetBeamUseSpotAngleFromLight(beam))
|
|
{
|
|
var fallOffEndPos = beam.transform.TransformPoint(GetBeamGlobalForward(beam) * GetBeamFallOffEnd(beam));
|
|
update |= DrawRadiusHandle(beam.transform, GetBeamGlobalForward(beam), GetBeamLossyScaleIfScalable(beam), fallOffEndPos, "End Radius", () => GetBeamConeRadiusEnd(beam), (float v) => SetBeamConeRadiusEnd(beam, v));
|
|
}
|
|
}
|
|
}
|
|
|
|
return update;
|
|
}
|
|
|
|
|
|
|
|
protected GUIStyle buttonAddComponentStyle { get => GUI.skin.button; }
|
|
|
|
protected void DrawButtonAddComponentDust()
|
|
{
|
|
bool showButtonDust = m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeamAbstractBase beam) => { return beam.GetComponent<VolumetricDustParticles>() == null; });
|
|
|
|
if (showButtonDust && GUILayout.Button(EditorData.Instance.contentAddDustParticles, buttonAddComponentStyle))
|
|
AddComponentToTargets<VolumetricDustParticles>();
|
|
}
|
|
|
|
protected void DrawButtonAddComponentTriggerZone()
|
|
{
|
|
bool showButtonTriggerZone = m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeamAbstractBase beam) => { return beam.GetComponent<TriggerZone>() == null; });
|
|
|
|
if (showButtonTriggerZone && GUILayout.Button(EditorData.Instance.contentAddTriggerZone, buttonAddComponentStyle))
|
|
AddComponentToTargets<TriggerZone>();
|
|
}
|
|
|
|
protected void DrawButtonAddComponentEffect()
|
|
{
|
|
bool showButtonEffect = m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeamAbstractBase beam) => { return beam.GetComponent<EffectAbstractBase>() == null; });
|
|
|
|
if (showButtonEffect && GUILayout.Button(EditorData.Instance.contentAddEffect, buttonAddComponentStyle))
|
|
{
|
|
var menu = new GenericMenu();
|
|
menu.AddItem(new GUIContent(EditorStrings.Beam.ButtonAddEffectFlicker), false, AddComponentToTargets<EffectFlicker>);
|
|
menu.AddItem(new GUIContent(EditorStrings.Beam.ButtonAddEffectPulse), false, AddComponentToTargets<EffectPulse>);
|
|
menu.ShowAsContext();
|
|
}
|
|
}
|
|
|
|
protected override void GetInfoTips(List<InfoTip> tips)
|
|
{
|
|
var gpuInstancingReport = GetBatchingReport();
|
|
if (!string.IsNullOrEmpty(gpuInstancingReport))
|
|
tips.Add(new InfoTip { type = MessageType.Warning, message = gpuInstancingReport });
|
|
|
|
base.GetInfoTips(tips);
|
|
}
|
|
}
|
|
}
|
|
#endif // UNITY_EDITOR
|
|
|