322 lines
12 KiB
C#
322 lines
12 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
using BadDog.Rendering.AreaLight;
|
|
|
|
namespace BadDog.Rendering.AreaLight.Samples
|
|
{
|
|
/// <summary>
|
|
/// AreaLight Test Panel for runtime testing and debugging
|
|
/// </summary>
|
|
public class AreaLightTestPanel : MonoBehaviour
|
|
{
|
|
[Header("UI Settings")]
|
|
[SerializeField] private Rect panelRect = new Rect(10, 10, 800, 500);
|
|
|
|
[Header("FPS Settings")]
|
|
[SerializeField] private float fpsUpdateInterval = 0.5f;
|
|
|
|
// FPS tracking
|
|
private float m_LastFpsUpdate;
|
|
private int m_FrameCount;
|
|
private float m_CurrentFps;
|
|
|
|
// GUI styles
|
|
private GUIStyle m_WindowStyle;
|
|
private GUIStyle m_LabelStyle;
|
|
private GUIStyle m_ToggleStyle;
|
|
private GUIStyle m_ButtonStyle;
|
|
private bool m_StylesInitialized = false;
|
|
|
|
// Shadow atlas resolution options
|
|
private static readonly int[] k_ShadowAtlasResolutions = { 256, 512, 1024, 2048, 4096, 8192 };
|
|
private static readonly string[] k_ShadowAtlasResolutionLabels = { "256", "512", "1024", "2048", "4096", "8192" };
|
|
private int m_ShadowAtlasResolutionIndex = 2; // Default 1024
|
|
|
|
private void Start()
|
|
{
|
|
InitializeFPSTracking();
|
|
SetTargetFrameRate();
|
|
InitializeShadowAtlasResolutionIndex();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
UpdateFPS();
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
InitializeStyles();
|
|
// Fixed window that cannot be dragged
|
|
panelRect = GUILayout.Window(95279529, panelRect, DrawTestPanel, "AreaLight Test Panel", m_WindowStyle, GUILayout.Width(panelRect.width), GUILayout.Height(panelRect.height));
|
|
}
|
|
|
|
private void InitializeFPSTracking()
|
|
{
|
|
m_LastFpsUpdate = Time.time;
|
|
m_FrameCount = 0;
|
|
m_CurrentFps = 0f;
|
|
}
|
|
|
|
private void SetTargetFrameRate()
|
|
{
|
|
#if UNITY_ANDROID || UNITY_IOS
|
|
// Set target frame rate to 60 for mobile platforms
|
|
Application.targetFrameRate = 60;
|
|
#endif
|
|
}
|
|
|
|
private void UpdateFPS()
|
|
{
|
|
m_FrameCount++;
|
|
float currentTime = Time.time;
|
|
|
|
if (currentTime - m_LastFpsUpdate >= fpsUpdateInterval)
|
|
{
|
|
m_CurrentFps = m_FrameCount / (currentTime - m_LastFpsUpdate);
|
|
m_FrameCount = 0;
|
|
m_LastFpsUpdate = currentTime;
|
|
}
|
|
}
|
|
|
|
private void InitializeStyles()
|
|
{
|
|
if (m_StylesInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_WindowStyle = new GUIStyle(GUI.skin.window) { fontSize = 38 };
|
|
m_LabelStyle = new GUIStyle(GUI.skin.label) { fontSize = 32, wordWrap = true };
|
|
m_ToggleStyle = new GUIStyle(GUI.skin.toggle) { fontSize = 32 };
|
|
m_ButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 28 };
|
|
|
|
m_StylesInitialized = true;
|
|
}
|
|
|
|
private void InitializeShadowAtlasResolutionIndex()
|
|
{
|
|
var areaLightInstance = GetActiveAreaLightInstance();
|
|
if (areaLightInstance != null)
|
|
{
|
|
var shadowSettings = GetShadowSettings(areaLightInstance);
|
|
if (shadowSettings != null)
|
|
{
|
|
int currentResolution = shadowSettings.shadowAtlasResolution;
|
|
for (int i = 0; i < k_ShadowAtlasResolutions.Length; i++)
|
|
{
|
|
if (k_ShadowAtlasResolutions[i] == currentResolution)
|
|
{
|
|
m_ShadowAtlasResolutionIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawTestPanel(int windowID)
|
|
{
|
|
GUILayout.BeginVertical();
|
|
GUILayout.Space(10);
|
|
|
|
DrawPerformanceSection();
|
|
DrawControlsSection();
|
|
|
|
GUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawPerformanceSection()
|
|
{
|
|
GUILayout.Label("=== Performance ===", m_LabelStyle);
|
|
|
|
Color originalColor = GUI.contentColor;
|
|
SetFPSColor();
|
|
GUILayout.Label($"FPS: {m_CurrentFps:F1}", m_LabelStyle);
|
|
GUI.contentColor = originalColor;
|
|
|
|
GUILayout.Space(10);
|
|
}
|
|
|
|
private void SetFPSColor()
|
|
{
|
|
if (m_CurrentFps >= 60f)
|
|
{
|
|
GUI.contentColor = Color.green;
|
|
}
|
|
else if (m_CurrentFps >= 30f)
|
|
{
|
|
GUI.contentColor = Color.yellow;
|
|
}
|
|
else
|
|
{
|
|
GUI.contentColor = Color.red;
|
|
}
|
|
}
|
|
|
|
private void DrawControlsSection()
|
|
{
|
|
GUILayout.Label("=== AreaLight Controls ===", m_LabelStyle);
|
|
|
|
// Get AreaLight instance from URP asset
|
|
var areaLightInstance = GetActiveAreaLightInstance();
|
|
if (areaLightInstance == null)
|
|
{
|
|
GUILayout.Label("AreaLight Feature not found", m_LabelStyle);
|
|
return;
|
|
}
|
|
|
|
var lightSettings = GetLightSettings(areaLightInstance);
|
|
var shadowSettings = GetShadowSettings(areaLightInstance);
|
|
|
|
if (lightSettings == null || shadowSettings == null)
|
|
{
|
|
GUILayout.Label("Failed to access AreaLight settings", m_LabelStyle);
|
|
return;
|
|
}
|
|
|
|
// Light Settings
|
|
GUILayout.Label("--- Light Settings ---", m_LabelStyle);
|
|
DrawIntSlider("Max Area Lights", ref lightSettings.maxAreaLights, 0, 8);
|
|
|
|
GUILayout.Space(10);
|
|
|
|
// Shadow Settings
|
|
GUILayout.Label("--- Shadow Settings ---", m_LabelStyle);
|
|
|
|
// Shadow Atlas Resolution (sync index first)
|
|
int currentResolution = shadowSettings.shadowAtlasResolution;
|
|
for (int i = 0; i < k_ShadowAtlasResolutions.Length; i++)
|
|
{
|
|
if (k_ShadowAtlasResolutions[i] == currentResolution)
|
|
{
|
|
m_ShadowAtlasResolutionIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GUILayout.Label("Shadow Atlas Resolution:", m_LabelStyle);
|
|
int newIndex = GUILayout.SelectionGrid(m_ShadowAtlasResolutionIndex, k_ShadowAtlasResolutionLabels, 6, m_ButtonStyle);
|
|
if (newIndex != m_ShadowAtlasResolutionIndex)
|
|
{
|
|
m_ShadowAtlasResolutionIndex = newIndex;
|
|
shadowSettings.shadowAtlasResolution = k_ShadowAtlasResolutions[m_ShadowAtlasResolutionIndex];
|
|
}
|
|
|
|
DrawIntSlider("Max Shadow Casting Lights", ref shadowSettings.maxShadowCastingLights, 0, 8);
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// Shadow Filter (enum dropdown simulation)
|
|
GUILayout.Label("Shadow Filter:", m_LabelStyle);
|
|
string[] filterOptions = { "Off", "PCF2x2", "Tent5x5", "Tent7x7", "PCSS" };
|
|
int currentFilterIndex = (int)shadowSettings.shadowFilter;
|
|
int newFilterIndex = GUILayout.SelectionGrid(currentFilterIndex, filterOptions, 5, m_ButtonStyle);
|
|
if (newFilterIndex != currentFilterIndex)
|
|
{
|
|
shadowSettings.shadowFilter = (AreaLightShadowSettings.BGAreaLightShadowMode)newFilterIndex;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// PCSS Settings (only show when PCSS is selected)
|
|
if (shadowSettings.shadowFilter == AreaLightShadowSettings.BGAreaLightShadowMode.PCSS)
|
|
{
|
|
GUILayout.Label("--- PCSS Settings ---", m_LabelStyle);
|
|
DrawSlider("PCSS Shadow Softness", ref shadowSettings.pcssShadowSoftness, 0f, 32f);
|
|
DrawIntSlider("PCSS Blocker Sample Count", ref shadowSettings.pcssBlockerSampleCount, 4, 32);
|
|
DrawIntSlider("PCSS Filter Sample Count", ref shadowSettings.pcssFilterSampleCount, 4, 32);
|
|
}
|
|
}
|
|
|
|
private void DrawSlider(string label, ref float value, float min, float max)
|
|
{
|
|
GUILayout.Label($"{label}: {value:F2}", m_LabelStyle);
|
|
// GUILayout.HorizontalSlider returns the current slider value
|
|
// Ensure slider has enough width to be interactive
|
|
value = GUILayout.HorizontalSlider(value, min, max, GUILayout.ExpandWidth(true), GUILayout.Height(20));
|
|
}
|
|
|
|
private void DrawIntSlider(string label, ref int value, int min, int max)
|
|
{
|
|
GUILayout.Label($"{label}: {value}", m_LabelStyle);
|
|
// Ensure slider has enough width to be interactive
|
|
float newValue = GUILayout.HorizontalSlider(value, min, max, GUILayout.ExpandWidth(true), GUILayout.Height(20));
|
|
int intValue = Mathf.RoundToInt(newValue);
|
|
// Clamp to ensure value stays within bounds
|
|
value = Mathf.Clamp(intValue, min, max);
|
|
}
|
|
|
|
private AreaLighting GetActiveAreaLightInstance()
|
|
{
|
|
var urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
|
|
if (urpAsset == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var scriptableRendererData = GetScriptableRendererData(urpAsset);
|
|
if (scriptableRendererData == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (scriptableRendererData.rendererFeatures != null)
|
|
{
|
|
foreach (var feature in scriptableRendererData.rendererFeatures)
|
|
{
|
|
if (feature is AreaLighting areaLightFeature && feature.isActive)
|
|
{
|
|
return areaLightFeature;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private ScriptableRendererData GetScriptableRendererData(UniversalRenderPipelineAsset urpAsset)
|
|
{
|
|
var property = typeof(UniversalRenderPipelineAsset).GetProperty("scriptableRendererData",
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
if (property != null)
|
|
{
|
|
return property.GetValue(urpAsset) as ScriptableRendererData;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private AreaLightSettings GetLightSettings(AreaLighting areaLighting)
|
|
{
|
|
var field = typeof(AreaLighting).GetField("m_LightSettings",
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
if (field != null)
|
|
{
|
|
return field.GetValue(areaLighting) as AreaLightSettings;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private AreaLightShadowSettings GetShadowSettings(AreaLighting areaLighting)
|
|
{
|
|
var field = typeof(AreaLighting).GetField("m_ShadowSettings",
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
if (field != null)
|
|
{
|
|
return field.GetValue(areaLighting) as AreaLightShadowSettings;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|