From e55551cc9f2acadc5eefc7269551854aa0e1ab3d Mon Sep 17 00:00:00 2001 From: Jucan Andrei Daniel Date: Fri, 23 Feb 2024 15:17:05 +0200 Subject: [PATCH 1/2] Implement Color Changing for light vobs --- Assets/GothicVR/Scripts/Creator/VobCreator.cs | 3 ++ .../GothicVR/Scripts/Creator/WorldCreator.cs | 1 + .../Manager/StationaryLightsManager.cs | 37 +++++++++++++++++++ .../GothicVR/Scripts/Vob/StationaryLight.cs | 22 +++++++++-- .../GothicVR/Shaders/StationaryLighting.hlsl | 3 +- 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/Assets/GothicVR/Scripts/Creator/VobCreator.cs b/Assets/GothicVR/Scripts/Creator/VobCreator.cs index 5b8ac6c4b..7e4560e13 100644 --- a/Assets/GothicVR/Scripts/Creator/VobCreator.cs +++ b/Assets/GothicVR/Scripts/Creator/VobCreator.cs @@ -513,6 +513,9 @@ private static GameObject CreateLight(ZenKit.Vobs.Light vob) SetPosAndRot(vobObj, vob.Position, vob.Rotation); StationaryLight lightComp = vobObj.AddComponent(); + lightComp.ColorAnimationList = vob.ColorAnimationList.Select(color => + new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f)).ToList(); + lightComp.ColorAnimationFps = vob.ColorAnimationFps; lightComp.Color = new Color(vob.Color.R / 255f, vob.Color.G / 255f, vob.Color.B / 255f, vob.Color.A / 255f); lightComp.Type = vob.LightType == ZenKit.Vobs.LightType.Point ? UnityEngine.LightType.Point diff --git a/Assets/GothicVR/Scripts/Creator/WorldCreator.cs b/Assets/GothicVR/Scripts/Creator/WorldCreator.cs index 0fd4a0d72..9523ab73b 100644 --- a/Assets/GothicVR/Scripts/Creator/WorldCreator.cs +++ b/Assets/GothicVR/Scripts/Creator/WorldCreator.cs @@ -74,6 +74,7 @@ public static async Task CreateAsync(string worldName) SkyManager.I.InitSky(); StationaryLight.InitStationaryLights(); + StationaryLightsManager.I.StartCoroutineChangeColors(); if (FeatureFlags.I.showBarrier) { diff --git a/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs b/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs index e4836ca10..dfaf91ac0 100644 --- a/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs +++ b/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections; using System.Collections.Generic; using GVR.Util; using UnityEngine; @@ -25,6 +27,41 @@ private void LateUpdate() Profiler.EndSample(); } } + + public void StartCoroutineChangeColors() + { + foreach (var light in StationaryLight.Lights) + { + StartCoroutine(ChangeColor(light)); + } + } + + private IEnumerator ChangeColor(StationaryLight light) + { + yield return new WaitForSeconds(1 / light.ColorAnimationFps); + while (true) + { + yield return new WaitForSeconds(1 / light.ColorAnimationFps); + if (light.ColorAnimationList.Count == 0) continue; + light.CurrentColorIndex++; + if (light.CurrentColorIndex >= light.ColorAnimationList.Count) + light.CurrentColorIndex = 0; + + light.Color = light.ColorAnimationList[light.CurrentColorIndex]; + UpdateShaderArray(light); + } + } + + private void UpdateShaderArray(StationaryLight light) + { + if (StationaryLight.LightColors == null || StationaryLight.LightColors.Length == 0) + return; + var colorIndices = Shader.GetGlobalFloatArray(StationaryLight.GlobalStationaryLightColorIndicesShaderId); + colorIndices[light.Index] = Array.IndexOf(StationaryLight.LightColors, + StationaryLight.Lights[light.Index].Color.linear); + Shader.SetGlobalFloatArray(StationaryLight.GlobalStationaryLightColorIndicesShaderId, colorIndices); + } + public static void AddLightOnRenderer(StationaryLight light, MeshRenderer renderer) { if (!LightsPerRenderer.ContainsKey(renderer)) diff --git a/Assets/GothicVR/Scripts/Vob/StationaryLight.cs b/Assets/GothicVR/Scripts/Vob/StationaryLight.cs index c19623489..fb876a42b 100644 --- a/Assets/GothicVR/Scripts/Vob/StationaryLight.cs +++ b/Assets/GothicVR/Scripts/Vob/StationaryLight.cs @@ -1,6 +1,7 @@ using GVR.Manager; using System; using System.Collections.Generic; +using System.Linq; using GVR.Extensions; using UnityEngine; using UnityEngine.Profiling; @@ -114,6 +115,7 @@ public float SpotAngle public static readonly List Lights = new List(); public static readonly int GlobalStationaryLightPositionsAndAttenuationShaderId = Shader.PropertyToID("_GlobalStationaryLightPositionsAndAttenuation"); + public static readonly int GlobalStationaryLightColorIndicesShaderId = Shader.PropertyToID("_GlobalStationaryLightColorIndices"); public static readonly int GlobalStationaryLightColorsShaderId = Shader.PropertyToID("_GlobalStationaryLightColors"); public static readonly int StationaryLightIndicesShaderId = Shader.PropertyToID("_StationaryLightIndices"); public static readonly int StationaryLightIndices2ShaderId = Shader.PropertyToID("_StationaryLightIndices2"); @@ -124,6 +126,10 @@ public float SpotAngle private List _affectedRenderers = new List(); private Light _unityLight; private static readonly List<(Vector3 Position, float Range)> ThreadSafeLightData = new(); + internal int CurrentColorIndex; + internal float ColorAnimationFps; // change color every 1/ColorAnimationFps seconds + internal List ColorAnimationList = new(); + internal static Vector4[] LightColors; private void OnDrawGizmosSelected() { @@ -175,21 +181,29 @@ public static void InitStationaryLights() // e.g. if we disabled Vob loading within FeatureFlags. if (Lights.IsEmpty()) return; - + List _lightColorsList = new List(); + foreach (var light in Lights) + { + _lightColorsList.Add(light.Color.linear); + _lightColorsList.AddRange(light.ColorAnimationList.Select(t => t.linear).Select(dummy => (Vector4)dummy)); + } Vector4[] _lightPositionsAndAttenuation = new Vector4[Lights.Count]; - Vector4[] _lightColors = new Vector4[Lights.Count]; + float[] _lightColorIndices = new float[Lights.Count]; + LightColors = _lightColorsList.Distinct().ToArray(); for (int i = 0; i < Lights.Count; i++) { Lights[i].Index = i; Lights[i].GatherRenderers(); _lightPositionsAndAttenuation[i] = new Vector4(Lights[i].transform.position.x, Lights[i].transform.position.y, Lights[i].transform.position.z, 1f / (Lights[i].Range * Lights[i].Range)); - _lightColors[i] = Lights[i].Color.linear; + int index = Array.IndexOf(LightColors, Lights[i].Color.linear); + _lightColorIndices[i] = index; Lights[i].gameObject.SetActive(false); Lights[i].gameObject.SetActive(true); } Shader.SetGlobalVectorArray(GlobalStationaryLightPositionsAndAttenuationShaderId, _lightPositionsAndAttenuation); - Shader.SetGlobalVectorArray(GlobalStationaryLightColorsShaderId, _lightColors); + Shader.SetGlobalFloatArray(GlobalStationaryLightColorIndicesShaderId, _lightColorIndices); + Shader.SetGlobalVectorArray(GlobalStationaryLightColorsShaderId, LightColors); ThreadSafeLightData.Clear(); // Clear the thread safe data as it is no longer needed. } diff --git a/Assets/GothicVR/Shaders/StationaryLighting.hlsl b/Assets/GothicVR/Shaders/StationaryLighting.hlsl index 52d7b88d1..d1a6237f5 100644 --- a/Assets/GothicVR/Shaders/StationaryLighting.hlsl +++ b/Assets/GothicVR/Shaders/StationaryLighting.hlsl @@ -5,6 +5,7 @@ #define MAX_AFFECTING_STATIONARY_LIGHTS 16 float4 _GlobalStationaryLightPositionsAndAttenuation[MAX_TOTAL_STATIONARY_LIGHTS]; +float _GlobalStationaryLightColorIndices[MAX_TOTAL_STATIONARY_LIGHTS]; real3 _GlobalStationaryLightColors[MAX_TOTAL_STATIONARY_LIGHTS]; half3 AdditionalStationaryDiffuse(uint lightIndex, real3 worldPos, real3 normal) @@ -15,7 +16,7 @@ half3 AdditionalStationaryDiffuse(uint lightIndex, real3 worldPos, real3 normal) half3 lightDirection = half3(lightVector * rsqrt(distanceSqr)); float diffuseDot = saturate(dot(lightDirection, normal)); - return _GlobalStationaryLightColors[lightIndex] * CustomDistanceAttenuation(distanceSqr, lightPosAndAttenuation.w) * diffuseDot * _PointLightIntensity; + return _GlobalStationaryLightColors[_GlobalStationaryLightColorIndices[lightIndex]] * CustomDistanceAttenuation(distanceSqr, lightPosAndAttenuation.w) * diffuseDot * _PointLightIntensity; } #endif \ No newline at end of file From 162fddd4964d357b5b9892de1c017763484eb59d Mon Sep 17 00:00:00 2001 From: Jucan Andrei Daniel Date: Sat, 24 Feb 2024 00:40:34 +0200 Subject: [PATCH 2/2] Move color switch coroutine from StationaryLightsManager.cs to StationaryLight.cs and keep Shader array update in StationaryLightsManager.cs --- .../GothicVR/Scripts/Creator/WorldCreator.cs | 2 +- .../Manager/StationaryLightsManager.cs | 50 +++++++++---------- .../GothicVR/Scripts/Vob/StationaryLight.cs | 20 +++++++- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/Assets/GothicVR/Scripts/Creator/WorldCreator.cs b/Assets/GothicVR/Scripts/Creator/WorldCreator.cs index 9523ab73b..b8b773e37 100644 --- a/Assets/GothicVR/Scripts/Creator/WorldCreator.cs +++ b/Assets/GothicVR/Scripts/Creator/WorldCreator.cs @@ -74,7 +74,7 @@ public static async Task CreateAsync(string worldName) SkyManager.I.InitSky(); StationaryLight.InitStationaryLights(); - StationaryLightsManager.I.StartCoroutineChangeColors(); + StationaryLightsManager.I.InitLightColorChange(); if (FeatureFlags.I.showBarrier) { diff --git a/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs b/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs index dfaf91ac0..19b65ec56 100644 --- a/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs +++ b/Assets/GothicVR/Scripts/Manager/StationaryLightsManager.cs @@ -23,45 +23,38 @@ private void LateUpdate() { UpdateRenderer(renderer); } + DirtiedMeshes.Clear(); Profiler.EndSample(); } } - public void StartCoroutineChangeColors() + public void InitLightColorChange() { - foreach (var light in StationaryLight.Lights) - { - StartCoroutine(ChangeColor(light)); - } + StartCoroutine(UpdateLightColors()); } + - private IEnumerator ChangeColor(StationaryLight light) + private IEnumerator UpdateLightColors() { - yield return new WaitForSeconds(1 / light.ColorAnimationFps); - while (true) + while(true) { - yield return new WaitForSeconds(1 / light.ColorAnimationFps); - if (light.ColorAnimationList.Count == 0) continue; - light.CurrentColorIndex++; - if (light.CurrentColorIndex >= light.ColorAnimationList.Count) - light.CurrentColorIndex = 0; + var colorIndices = + Shader.GetGlobalFloatArray(StationaryLight.GlobalStationaryLightColorIndicesShaderId); + var highestTimeInterval = 0f; + foreach (var light in StationaryLight.Lights) + { + colorIndices[light.Index] = Array.IndexOf(StationaryLight.LightColors, + StationaryLight.Lights[light.Index].Color.linear); + if (highestTimeInterval < light.ColorAnimationFps) + highestTimeInterval = light.ColorAnimationFps; + } - light.Color = light.ColorAnimationList[light.CurrentColorIndex]; - UpdateShaderArray(light); + Shader.SetGlobalFloatArray(StationaryLight.GlobalStationaryLightColorIndicesShaderId, colorIndices); + yield return new WaitForSeconds(1 / highestTimeInterval); } } - private void UpdateShaderArray(StationaryLight light) - { - if (StationaryLight.LightColors == null || StationaryLight.LightColors.Length == 0) - return; - var colorIndices = Shader.GetGlobalFloatArray(StationaryLight.GlobalStationaryLightColorIndicesShaderId); - colorIndices[light.Index] = Array.IndexOf(StationaryLight.LightColors, - StationaryLight.Lights[light.Index].Color.linear); - Shader.SetGlobalFloatArray(StationaryLight.GlobalStationaryLightColorIndicesShaderId, colorIndices); - } - public static void AddLightOnRenderer(StationaryLight light, MeshRenderer renderer) { if (!LightsPerRenderer.ContainsKey(renderer)) @@ -104,12 +97,14 @@ private void UpdateRenderer(MeshRenderer renderer) { indicesMatrix[i / 4, i % 4] = LightsPerRenderer[renderer][i].Index; } + for (int i = 0; i < NonAllocMaterials.Count; i++) { if (NonAllocMaterials[i]) { NonAllocMaterials[i].SetMatrix(StationaryLight.StationaryLightIndicesShaderId, indicesMatrix); - NonAllocMaterials[i].SetInt(StationaryLight.StationaryLightCountShaderId, LightsPerRenderer[renderer].Count); + NonAllocMaterials[i].SetInt(StationaryLight.StationaryLightCountShaderId, + LightsPerRenderer[renderer].Count); } } @@ -119,6 +114,7 @@ private void UpdateRenderer(MeshRenderer renderer) { indicesMatrix[i / 4, i % 4] = LightsPerRenderer[renderer][i + 16].Index; } + for (int i = 0; i < NonAllocMaterials.Count; i++) { if (NonAllocMaterials[i]) @@ -129,4 +125,4 @@ private void UpdateRenderer(MeshRenderer renderer) } } } -} +} \ No newline at end of file diff --git a/Assets/GothicVR/Scripts/Vob/StationaryLight.cs b/Assets/GothicVR/Scripts/Vob/StationaryLight.cs index fb876a42b..9e43e4cd0 100644 --- a/Assets/GothicVR/Scripts/Vob/StationaryLight.cs +++ b/Assets/GothicVR/Scripts/Vob/StationaryLight.cs @@ -1,5 +1,6 @@ using GVR.Manager; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using GVR.Extensions; @@ -129,7 +130,7 @@ public float SpotAngle internal int CurrentColorIndex; internal float ColorAnimationFps; // change color every 1/ColorAnimationFps seconds internal List ColorAnimationList = new(); - internal static Vector4[] LightColors; + public static Vector4[] LightColors; private void OnDrawGizmosSelected() { @@ -157,6 +158,7 @@ private void OnDestroy() private void OnEnable() { Profiler.BeginSample("Stationary light enabled"); + StartCoroutine(ChangeColor(this)); for (int i = 0; i < _affectedRenderers.Count; i++) { StationaryLightsManager.AddLightOnRenderer(this, _affectedRenderers[i]); @@ -167,6 +169,7 @@ private void OnEnable() private void OnDisable() { Profiler.BeginSample("Stationary light disable"); + StopCoroutine(ChangeColor(this)); for (int i = 0; i < _affectedRenderers.Count; i++) { StationaryLightsManager.RemoveLightOnRenderer(this, _affectedRenderers[i]); @@ -206,6 +209,21 @@ public static void InitStationaryLights() Shader.SetGlobalVectorArray(GlobalStationaryLightColorsShaderId, LightColors); ThreadSafeLightData.Clear(); // Clear the thread safe data as it is no longer needed. } + + private static IEnumerator ChangeColor(StationaryLight light) + { + yield return new WaitForSeconds(1 / light.ColorAnimationFps); + while (true) + { + yield return new WaitForSeconds(1 / light.ColorAnimationFps); + if (light.ColorAnimationList.Count == 0) continue; + light.CurrentColorIndex++; + if (light.CurrentColorIndex >= light.ColorAnimationList.Count) + light.CurrentColorIndex = 0; + + light.Color = light.ColorAnimationList[light.CurrentColorIndex]; + } + } public static int CountLightsInBounds(Bounds bounds) {