From bd39eb57cc6044cbfb3dc6f4820bd3b50b859fe9 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 10 Aug 2021 11:49:02 +0200 Subject: [PATCH] Added entity lighting support, by forwarding the shade vector and light color from the engine to Unity. The lighting calculations are replicated in the Quake shader. This is currently per-pixel shading (as opposed to the more authentic gouraud shading) and it's only applied to alias models at the moment. Brush models will have to be figured out properly. --- Assets/Scripts/Data/QExtensions.cs | 10 ++++++++++ Assets/Scripts/Game/Entity.cs | 9 +++++++++ Assets/Scripts/Modules/GameModule.Interop.cs | 15 ++++++++++++++- Assets/Scripts/Modules/GameModule.cs | 5 +++++ Assets/Shaders/Quake.shader | 3 +++ Assets/Shaders/QuakeForwardPass.hlsl | 15 +++++++++------ Assets/Shaders/QuakeInput.hlsl | 6 ++++++ engine/Quake/client.h | 1 + engine/Quake/r_alias.c | 2 ++ engine/UniQuake/game_uniquake.c | 6 ++++++ 10 files changed, 65 insertions(+), 7 deletions(-) diff --git a/Assets/Scripts/Data/QExtensions.cs b/Assets/Scripts/Data/QExtensions.cs index a59fb6e..75959b1 100644 --- a/Assets/Scripts/Data/QExtensions.cs +++ b/Assets/Scripts/Data/QExtensions.cs @@ -55,6 +55,11 @@ public static class QExtensions { return new Vector3(origin.y, origin.z, -origin.x); } + + public static Vector3 ToUnityDirection(this QVec3 dir) + { + return new Vector3(dir.y, dir.z, -dir.x); + } public static Quaternion ToUnityRotation(this QVec3 angles) { @@ -71,4 +76,9 @@ public static class QExtensions (byte)((color >> 16) & 0xFF), (byte)((color >> 24) & 0xFF)); } + + public static Color ToColor(this QVec3 vec) + { + return new Color(vec.x, vec.y, vec.z); + } } diff --git a/Assets/Scripts/Game/Entity.cs b/Assets/Scripts/Game/Entity.cs index 08d6550..e8ebbc2 100644 --- a/Assets/Scripts/Game/Entity.cs +++ b/Assets/Scripts/Game/Entity.cs @@ -159,6 +159,15 @@ public class Entity } } + public void SetLighting(Vector3 shadeVector, Color lightColor) + { + if (material == null) + return; + + material.SetVector("_ShadeVector", shadeVector); + material.SetVector("_LightColor", lightColor); + } + private void AssignMeshRenderer() { material = visualStyle.CreateEntityMaterial(true); diff --git a/Assets/Scripts/Modules/GameModule.Interop.cs b/Assets/Scripts/Modules/GameModule.Interop.cs index 0b1d369..46bc6db 100644 --- a/Assets/Scripts/Modules/GameModule.Interop.cs +++ b/Assets/Scripts/Modules/GameModule.Interop.cs @@ -17,6 +17,7 @@ public partial class GameModule : CallbackHandler RemoveEntity = CreateCallback(Callback_GameRemoveEntity), UpdateEntityAnimation = CreateCallback(Callback_GameUpdateEntityAnimation), SetEntitySkin = CreateCallback(Callback_GameSetEntitySkin), + SetEntityLighting = CreateCallback(Callback_GameSetEntityLighting), RunParticleEffect = CreateCallback(Callback_RunParticleEffect), CreateParticleEffect = CreateCallback(Callback_CreateParticleEffect), @@ -37,6 +38,7 @@ public partial class GameModule : CallbackHandler public IntPtr RemoveEntity; public IntPtr UpdateEntityAnimation; public IntPtr SetEntitySkin; + public IntPtr SetEntityLighting; public IntPtr RunParticleEffect; public IntPtr CreateParticleEffect; @@ -98,6 +100,17 @@ public partial class GameModule : CallbackHandler Profiler.EndSample(); } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void GameSetEntityLightingCallback(IntPtr context, int entityNum, ref QVec3 shadeVector, ref QVec3 lightColor); + + [MonoPInvokeCallback(typeof(GameSetEntityLightingCallback))] + private static void Callback_GameSetEntityLighting(IntPtr context, int entityNum, ref QVec3 shadeVector, ref QVec3 lightColor) + { + Profiler.BeginSample("GameSetEntityLighting"); + GetSelf(context).SetEntityLighting(entityNum, shadeVector.ToUnityDirection(), lightColor.ToColor()); + Profiler.EndSample(); + } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void RunParticleEffectCallback(IntPtr context, ref QVec3 origin, ref QVec3 direction, uint colorMin, uint colorMax, int count); @@ -105,7 +118,7 @@ public partial class GameModule : CallbackHandler private static void Callback_RunParticleEffect(IntPtr context, ref QVec3 origin, ref QVec3 direction, uint colorMin, uint colorMax, int count) { Profiler.BeginSample("RunParticleEffect"); - GetSelf(context).RunParticleEffect(origin.ToUnityPosition(), direction.ToUnityPosition(), colorMin.ToColor(), colorMax.ToColor(), count); + GetSelf(context).RunParticleEffect(origin.ToUnityPosition(), direction.ToUnityDirection(), colorMin.ToColor(), colorMax.ToColor(), count); Profiler.EndSample(); } diff --git a/Assets/Scripts/Modules/GameModule.cs b/Assets/Scripts/Modules/GameModule.cs index 03f7dd7..cc43157 100644 --- a/Assets/Scripts/Modules/GameModule.cs +++ b/Assets/Scripts/Modules/GameModule.cs @@ -67,6 +67,11 @@ public partial class GameModule { uq.GameState.GetEntity(entityNum).SetSkin(skinNum); } + + private void SetEntityLighting(int entityNum, Vector3 shadeVector, Color lightColor) + { + uq.GameState.GetEntity(entityNum).SetLighting(shadeVector, lightColor); + } private void RunParticleEffect(Vector3 position, Vector3 direction, Color colorMin, Color colorMax, int count) { diff --git a/Assets/Shaders/Quake.shader b/Assets/Shaders/Quake.shader index d6b420f..f053aef 100644 --- a/Assets/Shaders/Quake.shader +++ b/Assets/Shaders/Quake.shader @@ -12,6 +12,9 @@ Shader "UniQuake/Quake" [HDR] _EmissionColor("Emission Color", Color) = (0,0,0) [NoScaleOffset]_EmissionMap("Emission Map", 2D) = "white" {} + + [HideInInspector] _ShadeVector("Shade Vector", Vector) = (0, 0, 0) + [HideInInspector] _LightColor("Light Color", Color) = (1, 1, 1, 1) // Blending state [HideInInspector] _Surface("__surface", Float) = 0.0 diff --git a/Assets/Shaders/QuakeForwardPass.hlsl b/Assets/Shaders/QuakeForwardPass.hlsl index 724fdf9..690b240 100644 --- a/Assets/Shaders/QuakeForwardPass.hlsl +++ b/Assets/Shaders/QuakeForwardPass.hlsl @@ -30,7 +30,7 @@ struct Varyings float3 posWS : TEXCOORD2; // xyz: posWS - float3 normal : TEXCOORD3; + float3 normal : TEXCOORD3; float3 viewDir : TEXCOORD4; half4 fogFactorAndVertexLight : TEXCOORD6; // x: fogFactor, yzw: vertex light @@ -129,11 +129,14 @@ half4 LitPassFragmentSimple(Varyings input) : SV_Target half4 lightmapColor = SAMPLE_TEXTURE2D(_LightMap, sampler_LightMap, input.lightmapUV); half3 finalColor = diffuse * lightmapColor.rgb * 2.0f; #else - // Specular and smoothness are some bogus values just to make models not appear completely black on one side - half3 finalColor = UniversalFragmentBlinnPhong(inputData, diffuse, half4(0.2, 0.2, 0.2, 1), 0.5, 0.0, alpha).rgb; - // Light light = GetMainLight(); - // half3 diffuseColor = LightingLambert(light.color, light.direction, inputData.normalWS); - // half3 finalColor = diffuse * diffuseColor; + // This reproduces anorm_dots within a reasonable degree of tolerance + half shade = dot(_ShadeVector.xyz, inputData.normalWS); + if (shade < 0.0) + shade = 1.0 + shade * (13.0 / 44.0); + else + shade = 1.0 + shade; + + half3 finalColor = diffuse * shade * _LightColor.rgb; #endif #ifdef _EMISSION diff --git a/Assets/Shaders/QuakeInput.hlsl b/Assets/Shaders/QuakeInput.hlsl index f3658ba..a01d3b2 100644 --- a/Assets/Shaders/QuakeInput.hlsl +++ b/Assets/Shaders/QuakeInput.hlsl @@ -11,6 +11,8 @@ CBUFFER_START(UnityPerMaterial) half4 _EmissionColor; half _Cutoff; half _Surface; + half4 _ShadeVector; + half4 _LightColor; CBUFFER_END #ifdef UNITY_DOTS_INSTANCING_ENABLED @@ -19,12 +21,16 @@ CBUFFER_END UNITY_DOTS_INSTANCED_PROP(float4, _EmissionColor) UNITY_DOTS_INSTANCED_PROP(float , _Cutoff) UNITY_DOTS_INSTANCED_PROP(float , _Surface) + UNITY_DOTS_INSTANCED_PROP(half4, _ShadeVector) + UNITY_DOTS_INSTANCED_PROP(half4, _LightColor) UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata) #define _BaseColor UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata__BaseColor) #define _EmissionColor UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata__EmissionColor) #define _Cutoff UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Cutoff) #define _Surface UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Surface) + #define _ShadeVector UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(half4 , Metadata__ShadeVector) + #define _LightColor UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(half4 , Metadata__LightColor) #endif SAMPLER(point_repeat_sampler); diff --git a/engine/Quake/client.h b/engine/Quake/client.h index 529d1aa..f652cbf 100644 --- a/engine/Quake/client.h +++ b/engine/Quake/client.h @@ -377,6 +377,7 @@ void UQ_Game_SetEntityTransform(int entityNum, vec3_t origin, vec3_t angles); void UQ_Game_RemoveEntity(int entityNum); void UQ_Game_UpdateEntityAnimation(int entityNum, int pose1, int pose2, float blend); void UQ_Game_SetEntitySkin(int entityNum, int skinNum); +void UQ_Game_SetEntityLighting(int entityNum, vec3_t shadeVector, vec3_t lightColor); #endif /* _CLIENT_H_ */ diff --git a/engine/Quake/r_alias.c b/engine/Quake/r_alias.c index e4bfe6e..065c4ac 100644 --- a/engine/Quake/r_alias.c +++ b/engine/Quake/r_alias.c @@ -619,6 +619,8 @@ void R_SetupAliasLighting (entity_t *e) shadedots = r_avertexnormal_dots[quantizedangle]; VectorScale (lightcolor, 1.0f / 200.0f, lightcolor); + + UQ_Game_SetEntityLighting(e->num, shadevector, lightcolor); } /* diff --git a/engine/UniQuake/game_uniquake.c b/engine/UniQuake/game_uniquake.c index 1f90d67..1840ef5 100644 --- a/engine/UniQuake/game_uniquake.c +++ b/engine/UniQuake/game_uniquake.c @@ -9,6 +9,7 @@ typedef struct unity_gamecalls_s void(*RemoveEntity)(void *context, int entityNum); void(*UpdateEntityAnimation)(void *context, int entityNum, int pose1, int pose2, float blend); void(*SetEntitySkin)(void *context, int entityNum, int skinNum); + void(*SetEntityLighting)(void *context, int entityNum, vec3_t shadeVector, vec3_t lightColor); void(*RunParticleEffect)(void *context, vec3_t origin, vec3_t direction, unsigned int colorMin, unsigned int colorMax, int count); void(*CreateParticleEffect)(void *context, int type, vec3_t origin, unsigned int colorMin, unsigned int colorMax); @@ -49,6 +50,11 @@ void UQ_Game_SetEntitySkin(int entityNum, int skinNum) unity_gamecalls->SetEntitySkin(unity_context, entityNum, skinNum); } +void UQ_Game_SetEntityLighting(int entityNum, vec3_t shadeVector, vec3_t lightColor) +{ + unity_gamecalls->SetEntityLighting(unity_context, entityNum, shadeVector, lightColor); +} + void UQ_Game_RunParticleEffect(vec3_t origin, vec3_t direction, int color, int count) { unsigned int colorMin, colorMax;