Files
2026-04-16 04:58:10 +09:00

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;
}
}
}