From bba493ee7ca050ef6a5abc33d39acac6dda9324e Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Mon, 15 Jun 2026 21:25:54 +0300 Subject: [PATCH] Fixing lights and shadows --- resources/config2/lights_dorm.json | 172 +++++++++--------- resources/config2/lights_uni_interior.json | 52 ++++-- resources/dialogue/cutscenes005.json | 2 +- resources/shaders/night_fog.vertex | 2 + resources/shaders/night_fog_desktop.fragment | 9 +- resources/shaders/night_fog_shadow.vertex | 2 + .../shaders/night_fog_shadow_desktop.fragment | 9 +- .../shaders/night_fog_shadow_web.fragment | 9 +- resources/shaders/night_fog_skinning.vertex | 3 + .../shaders/night_fog_skinning_shadow.vertex | 3 + resources/shaders/night_fog_web.fragment | 9 +- src/Character.cpp | 63 ++++++- src/Character.h | 24 ++- src/Game.cpp | 1 + src/Location.cpp | 47 ++++- src/Location.h | 5 + src/render/Renderer.cpp | 20 ++ src/render/Renderer.h | 2 + 18 files changed, 308 insertions(+), 126 deletions(-) diff --git a/resources/config2/lights_dorm.json b/resources/config2/lights_dorm.json index 86867b1..83ab377 100644 --- a/resources/config2/lights_dorm.json +++ b/resources/config2/lights_dorm.json @@ -1,82 +1,92 @@ { - "lights": [ - { - "id" : "main_hall", - "positionX": -3.5951, - "positionY": 6.0, - "positionZ": 0.280929, - "directionX": 0.0, - "directionY": -1.0, - "directionZ": 0.0, - "colorR": 4.0, - "colorG": 3.2, - "colorB": 2.4 - }, - { - "id" : "player_room", - "autoLight": true, - "autoLightDistance": 6.0, - "positionX": 4.92215, - "positionY": 4.0, - "positionZ": -14.62, - "directionX": 0.0, - "directionY": -1.0, - "directionZ": 0.0, - "colorR": 2.0, - "colorG": 1.6, - "colorB": 1.2 - }, - { - "id" : "laundry", - "autoLight": true, - "autoLightDistance": 6.0, - "positionX": 4.65392, - "positionY": 4.0, - "positionZ": -20.0, - "directionX": 0.0, - "directionY": -1.0, - "directionZ": 0.0, - "colorR": 2.0, - "colorG": 1.6, - "colorB": 1.2 - }, - { - "id" : "kitchen", - "autoLight": true, - "autoLightDistance": 6.0, - "positionX": -5.21995, - "positionY": 4.0, - "positionZ": -20, - "directionX": 0.0, - "directionY": -1.0, - "directionZ": 0.0, - "colorR": 2.0, - "colorG": 1.6, - "colorB": 1.2 - }, - { - "id" : "streetlight1", - "positionX": -14.5, - "positionY": 4.0, - "positionZ": -14, - "directionX": 0.0, - "directionY": -1.0, - "directionZ": 0.0, - "colorR": 2.0, - "colorG": 1.6, - "colorB": 1.2 - }, - { - "id" : "streetlight2", - "positionX": -14.5, - "positionY": 4.0, - "positionZ": 14.0, - "directionX": 0.0, - "directionY": -1.0, - "directionZ": 0.0, - "colorR": 2.0, - "colorG": 1.6, - "colorB": 1.2 - } - ] -} + "lights": [ + { + "id": "main_hall", + "positionX": -3.1, + "positionY": 6.0, + "positionZ": 0.0, + "directionX": 0.0, + "directionY": -1.0, + "directionZ": 0.0, + "colorR": 4.0, + "colorG": 3.2, + "colorB": 2.4 + }, + { + "id": "player_room", + "autoLight": true, + "autoLightLimitX": 3.75, + "autoLightLimitY": 2.75, + "limitX": 3.75, + "limitY": 2.75, + "positionX": 4.95, + "positionY": 3.0, + "positionZ": -14.25, + "directionX": 0.0, + "directionY": -1.0, + "directionZ": 0.0, + "colorR": 2.0, + "colorG": 1.6, + "colorB": 1.2 + }, + { + "id": "laundry", + "autoLight": true, + "autoLightLimitX": 3.75, + "autoLightLimitY": 2.75, + "limitX": 3.75, + "limitY": 2.75, + "positionX": 4.95, + "positionY": 4.0, + "positionZ": -19.95, + "directionX": 0.0, + "directionY": -1.0, + "directionZ": 0.0, + "colorR": 2.0, + "colorG": 1.6, + "colorB": 1.2 + }, + { + "id": "kitchen", + "autoLight": true, + "autoLightDistance": 6.0, + "autoLightLimitX": 3.75, + "autoLightLimitY": 2.75, + "limitX": 3.75, + "limitY": 2.75, + "positionX": -4.95, + "positionY": 4.0, + "positionZ": -19.95, + "directionX": 0.0, + "directionY": -1.0, + "directionZ": 0.0, + "colorR": 2.0, + "colorG": 1.6, + "colorB": 1.2 + }, + { + "id": "streetlight1", + "positionX": -14.5, + "positionY": 4.0, + "positionZ": -14, + "directionX": 0.0, + "directionY": -1.0, + "directionZ": 0.0, + "colorR": 2.0, + "colorG": 1.6, + "colorB": 1.2 + }, + { + "id": "streetlight2", + "positionX": -14.5, + "positionY": 4.0, + "positionZ": 14.0, + "directionX": 0.0, + "directionY": -1.0, + "directionZ": 0.0, + "colorR": 2.0, + "colorG": 1.6, + "colorB": 1.2 + } + ] +} \ No newline at end of file diff --git a/resources/config2/lights_uni_interior.json b/resources/config2/lights_uni_interior.json index e97142a..e252352 100644 --- a/resources/config2/lights_uni_interior.json +++ b/resources/config2/lights_uni_interior.json @@ -2,9 +2,9 @@ "lights": [ { "id": "lamp_library", - "positionX": 3.76222, + "positionX": 3.750, "positionY": 5.0, - "positionZ": 4.16035, + "positionZ": 4.000, "directionX": 0.0, "directionY": -1.0, "directionZ": 0.0, @@ -12,13 +12,16 @@ "colorG": 3.2, "colorB": 2.4, "autoLight": true, - "autoLightDistance": 6.0 + "autoLightLimitX": 2.15, + "autoLightLimitY": 3.9, + "limitX": 2.15, + "limitY": 3.9 }, { "id": "lamp_teacher_room", - "positionX": -4.15666, + "positionX": -3.750, "positionY": 5.0, - "positionZ": 4.27091, + "positionZ": 4.000, "directionX": 0.0, "directionY": -1.0, "directionZ": 0.0, @@ -26,13 +29,16 @@ "colorG": 3.2, "colorB": 2.4, "autoLight": true, - "autoLightDistance": 6.0 + "autoLightLimitX": 2.15, + "autoLightLimitY": 3.9, + "limitX": 2.15, + "limitY": 3.9 }, { "id": "lamp_student_room", - "positionX": 3.60541, + "positionX": 3.750, "positionY": 5.0, - "positionZ": -4.36552, + "positionZ": -4.000, "directionX": 0.0, "directionY": -1.0, "directionZ": 0.0, @@ -40,13 +46,16 @@ "colorG": 3.2, "colorB": 2.4, "autoLight": true, - "autoLightDistance": 6.0 + "autoLightLimitX": 2.15, + "autoLightLimitY": 3.9, + "limitX": 2.15, + "limitY": 3.9 }, { "id": "lamp_s1_room", - "positionX": -3.90337, + "positionX": -3.750, "positionY": 5.0, - "positionZ": -4.00563, + "positionZ": -4.000, "directionX": 0.0, "directionY": -1.0, "directionZ": 0.0, @@ -54,13 +63,16 @@ "colorG": 3.2, "colorB": 2.4, "autoLight": true, - "autoLightDistance": 6.0 + "autoLightLimitX": 2.15, + "autoLightLimitY": 3.9, + "limitX": 2.15, + "limitY": 3.9 }, { "id": "lamp_s0_room", - "positionX": -3.90337, + "positionX": -3.750, "positionY": 5.0, - "positionZ": -12.485, + "positionZ": -12.0, "directionX": 0.0, "directionY": -1.0, "directionZ": 0.0, @@ -68,13 +80,16 @@ "colorG": 3.2, "colorB": 2.4, "autoLight": true, - "autoLightDistance": 6.0 + "autoLightLimitX": 2.15, + "autoLightLimitY": 3.9, + "limitX": 2.15, + "limitY": 3.9 }, { "id": "lamp_hall_room", "positionX": 0.0, "positionY": 5.0, - "positionZ": 11.7949, + "positionZ": 12.0, "directionX": 0.0, "directionY": -1.0, "directionZ": 0.0, @@ -82,7 +97,10 @@ "colorG": 3.2, "colorB": 2.4, "autoLight": true, - "autoLightDistance": 6.0 + "autoLightLimitX": 5.8, + "autoLightLimitY": 3.9, + "limitX": 5.8, + "limitY": 3.9 } ] } \ No newline at end of file diff --git a/resources/dialogue/cutscenes005.json b/resources/dialogue/cutscenes005.json index d0f0d17..63438f3 100644 --- a/resources/dialogue/cutscenes005.json +++ b/resources/dialogue/cutscenes005.json @@ -419,7 +419,7 @@ "fadeInMs": 500, "endFadeOutMs": 0, "endFadeInMs": 500, - "onFadeInCallback": "", + "onFadeInCallback": "on_sleep_cutscene", "imageSegments": [ { "path": "resources/black.png", diff --git a/resources/shaders/night_fog.vertex b/resources/shaders/night_fog.vertex index f6890f0..8f6e4e3 100644 --- a/resources/shaders/night_fog.vertex +++ b/resources/shaders/night_fog.vertex @@ -6,6 +6,7 @@ varying vec2 texCoord; varying float fogDistance; varying vec3 fragViewPos; varying vec3 fragNormal; +varying vec2 fragWorldXZ; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; @@ -19,4 +20,5 @@ void main() texCoord = vTexCoord; fragViewPos = eyePos.xyz; fragNormal = mat3(ModelViewMatrix) * vNormal; + fragWorldXZ = vPosition.xz; } diff --git a/resources/shaders/night_fog_desktop.fragment b/resources/shaders/night_fog_desktop.fragment index 401ffd2..a94b165 100644 --- a/resources/shaders/night_fog_desktop.fragment +++ b/resources/shaders/night_fog_desktop.fragment @@ -4,6 +4,8 @@ uniform int uPointLightCount; uniform vec3 uPointLightPos[4]; uniform vec3 uPointLightDir[4]; uniform vec3 uPointLightColor[4]; +uniform vec2 uPointLightWorldXZ[4]; +uniform vec2 uPointLightLimitXZ[4]; uniform vec3 uAmbientColor; uniform vec3 uFogColor; @@ -11,6 +13,7 @@ varying vec2 texCoord; varying float fogDistance; varying vec3 fragViewPos; varying vec3 fragNormal; +varying vec2 fragWorldXZ; // cos(45 deg) = half-angle for a 90-degree full-angle spotlight const float SPOT_COS_OUTER = 0.7071; @@ -44,7 +47,11 @@ void main() float atten = 1.0 / (1.0 + 0.35 * dist + 0.15 * dist * dist); float diff = hasNormal ? max(dot(N, lightDir), 0.0) : 1.0; - lighting += uPointLightColor[i] * diff * atten * spotAtten; + vec2 worldDelta = abs(fragWorldXZ - uPointLightWorldXZ[i]); + float rectX = 1.0 - smoothstep(uPointLightLimitXZ[i].x - 0.5, uPointLightLimitXZ[i].x + 0.5, worldDelta.x); + float rectY = 1.0 - smoothstep(uPointLightLimitXZ[i].y - 0.5, uPointLightLimitXZ[i].y + 0.5, worldDelta.y); + + lighting += uPointLightColor[i] * diff * atten * spotAtten * rectX * rectY; } color.rgb *= lighting; diff --git a/resources/shaders/night_fog_shadow.vertex b/resources/shaders/night_fog_shadow.vertex index dc70ac9..8452efa 100644 --- a/resources/shaders/night_fog_shadow.vertex +++ b/resources/shaders/night_fog_shadow.vertex @@ -7,6 +7,7 @@ varying vec4 fragPosLightSpace; varying vec3 fragNormal; varying float fogDistance; varying vec3 fragViewPos; +varying vec2 fragWorldXZ; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; @@ -22,4 +23,5 @@ void main() fragViewPos = eyePos.xyz; fragNormal = mat3(ModelViewMatrix) * vNormal; fragPosLightSpace = uLightFromCamera * eyePos; + fragWorldXZ = vPosition.xz; } diff --git a/resources/shaders/night_fog_shadow_desktop.fragment b/resources/shaders/night_fog_shadow_desktop.fragment index de5a361..337cb38 100644 --- a/resources/shaders/night_fog_shadow_desktop.fragment +++ b/resources/shaders/night_fog_shadow_desktop.fragment @@ -6,6 +6,8 @@ uniform int uPointLightCount; uniform vec3 uPointLightPos[4]; uniform vec3 uPointLightDir[4]; uniform vec3 uPointLightColor[4]; +uniform vec2 uPointLightWorldXZ[4]; +uniform vec2 uPointLightLimitXZ[4]; uniform vec3 uAmbientColor; uniform vec3 uFogColor; @@ -14,6 +16,7 @@ varying vec4 fragPosLightSpace; varying vec3 fragNormal; varying float fogDistance; varying vec3 fragViewPos; +varying vec2 fragWorldXZ; const float SPOT_COS_OUTER = 0.7071; const float SPOT_COS_INNER = 0.82; @@ -83,7 +86,11 @@ void main() float atten = 1.0 / (1.0 + 0.35 * dist + 0.15 * dist * dist); float diff = hasNormal ? max(dot(N, lightDir), 0.0) : 1.0; - lighting += uPointLightColor[i] * diff * atten * spotAtten * (1.0 - shadow); + vec2 worldDelta = abs(fragWorldXZ - uPointLightWorldXZ[i]); + float rectX = 1.0 - smoothstep(uPointLightLimitXZ[i].x - 0.5, uPointLightLimitXZ[i].x + 0.5, worldDelta.x); + float rectY = 1.0 - smoothstep(uPointLightLimitXZ[i].y - 0.5, uPointLightLimitXZ[i].y + 0.5, worldDelta.y); + + lighting += uPointLightColor[i] * diff * atten * spotAtten * rectX * rectY * (1.0 - shadow); } color.rgb *= lighting; diff --git a/resources/shaders/night_fog_shadow_web.fragment b/resources/shaders/night_fog_shadow_web.fragment index 2339ef8..7e7a4b1 100644 --- a/resources/shaders/night_fog_shadow_web.fragment +++ b/resources/shaders/night_fog_shadow_web.fragment @@ -8,6 +8,8 @@ uniform int uPointLightCount; uniform vec3 uPointLightPos[4]; uniform vec3 uPointLightDir[4]; uniform vec3 uPointLightColor[4]; +uniform vec2 uPointLightWorldXZ[4]; +uniform vec2 uPointLightLimitXZ[4]; uniform vec3 uAmbientColor; uniform vec3 uFogColor; @@ -16,6 +18,7 @@ varying vec4 fragPosLightSpace; varying vec3 fragNormal; varying float fogDistance; varying vec3 fragViewPos; +varying vec2 fragWorldXZ; const float SPOT_COS_OUTER = 0.7071; const float SPOT_COS_INNER = 0.82; @@ -85,7 +88,11 @@ void main() float atten = 1.0 / (1.0 + 0.35 * dist + 0.15 * dist * dist); float diff = hasNormal ? max(dot(N, lightDir), 0.0) : 1.0; - lighting += uPointLightColor[i] * diff * atten * spotAtten * (1.0 - shadow); + vec2 worldDelta = abs(fragWorldXZ - uPointLightWorldXZ[i]); + float rectX = 1.0 - smoothstep(uPointLightLimitXZ[i].x - 0.5, uPointLightLimitXZ[i].x + 0.5, worldDelta.x); + float rectY = 1.0 - smoothstep(uPointLightLimitXZ[i].y - 0.5, uPointLightLimitXZ[i].y + 0.5, worldDelta.y); + + lighting += uPointLightColor[i] * diff * atten * spotAtten * rectX * rectY * (1.0 - shadow); } color.rgb *= lighting; diff --git a/resources/shaders/night_fog_skinning.vertex b/resources/shaders/night_fog_skinning.vertex index 4d22f5f..228c5a1 100644 --- a/resources/shaders/night_fog_skinning.vertex +++ b/resources/shaders/night_fog_skinning.vertex @@ -10,9 +10,11 @@ varying vec2 texCoord; varying float fogDistance; varying vec3 fragViewPos; varying vec3 fragNormal; +varying vec2 fragWorldXZ; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; +uniform mat4 uViewInverse; uniform mat4 uBoneMatrices[58]; uniform vec3 uPlayerEyePos; @@ -65,4 +67,5 @@ void main() texCoord = vTexCoord; fragViewPos = eyePos.xyz; fragNormal = mat3(ModelViewMatrix) * skinnedNormal; + fragWorldXZ = (uViewInverse * eyePos).xz; } diff --git a/resources/shaders/night_fog_skinning_shadow.vertex b/resources/shaders/night_fog_skinning_shadow.vertex index 8c546b0..19049a6 100644 --- a/resources/shaders/night_fog_skinning_shadow.vertex +++ b/resources/shaders/night_fog_skinning_shadow.vertex @@ -11,10 +11,12 @@ varying vec4 fragPosLightSpace; varying vec3 fragNormal; varying float fogDistance; varying vec3 fragViewPos; +varying vec2 fragWorldXZ; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; uniform mat4 uLightFromCamera; +uniform mat4 uViewInverse; uniform mat4 uBoneMatrices[58]; uniform vec3 uPlayerEyePos; @@ -68,4 +70,5 @@ void main() fragViewPos = eyePos.xyz; fragNormal = mat3(ModelViewMatrix) * skinnedNormal; fragPosLightSpace = uLightFromCamera * eyePos; + fragWorldXZ = (uViewInverse * eyePos).xz; } diff --git a/resources/shaders/night_fog_web.fragment b/resources/shaders/night_fog_web.fragment index 1f59223..c5d2deb 100644 --- a/resources/shaders/night_fog_web.fragment +++ b/resources/shaders/night_fog_web.fragment @@ -6,6 +6,8 @@ uniform int uPointLightCount; uniform vec3 uPointLightPos[4]; uniform vec3 uPointLightDir[4]; uniform vec3 uPointLightColor[4]; +uniform vec2 uPointLightWorldXZ[4]; +uniform vec2 uPointLightLimitXZ[4]; uniform vec3 uAmbientColor; uniform vec3 uFogColor; @@ -13,6 +15,7 @@ varying vec2 texCoord; varying float fogDistance; varying vec3 fragViewPos; varying vec3 fragNormal; +varying vec2 fragWorldXZ; // cos(45 deg) = half-angle for a 90-degree full-angle spotlight const float SPOT_COS_OUTER = 0.7071; @@ -46,7 +49,11 @@ void main() float atten = 1.0 / (1.0 + 0.35 * dist + 0.15 * dist * dist); float diff = hasNormal ? max(dot(N, lightDir), 0.0) : 1.0; - lighting += uPointLightColor[i] * diff * atten * spotAtten; + vec2 worldDelta = abs(fragWorldXZ - uPointLightWorldXZ[i]); + float rectX = 1.0 - smoothstep(uPointLightLimitXZ[i].x - 0.5, uPointLightLimitXZ[i].x + 0.5, worldDelta.x); + float rectY = 1.0 - smoothstep(uPointLightLimitXZ[i].y - 0.5, uPointLightLimitXZ[i].y + 0.5, worldDelta.y); + + lighting += uPointLightColor[i] * diff * atten * spotAtten * rectX * rectY; } color.rgb *= lighting; diff --git a/src/Character.cpp b/src/Character.cpp index ad79a3b..64f7eb0 100644 --- a/src/Character.cpp +++ b/src/Character.cpp @@ -903,7 +903,13 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri // ==================== Night pass with point lights ==================== -void Character::drawNight(Renderer& renderer, const float* pointLightPosEye, const float* pointLightDirEye, const float* pointLightColors, int pointLightCount, const float* ambientColor, const float* fogColor) { +void Character::drawNight(Renderer& renderer, + const float* pointLightPosEye, const float* pointLightDirEye, + const float* pointLightColors, int pointLightCount, + const float* pointLightWorldXZ, const float* pointLightLimitXZ, + const Eigen::Matrix4f& cameraViewInverse, + const float* ambientColor, const float* fogColor) +{ if (!enabled) return; if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) { hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false); @@ -915,13 +921,19 @@ void Character::drawNight(Renderer& renderer, const float* pointLightPosEye, con //if (!isPlayer && hp <= 0) return; if (useGpuSkinning) { - drawNightGpuSkinning(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount, ambientColor, fogColor); + drawNightGpuSkinning(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount, + pointLightWorldXZ, pointLightLimitXZ, cameraViewInverse, ambientColor, fogColor); } else { - drawNightCpu(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount, ambientColor, fogColor); + drawNightCpu(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount, + pointLightWorldXZ, pointLightLimitXZ, cameraViewInverse, ambientColor, fogColor); } } -void Character::drawNightGpuSkinning(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, const float* ambientColor, const float* fogColor) { +void Character::drawNightGpuSkinning(Renderer& renderer, + const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, + const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse, + const float* ambientColor, const float* fogColor) +{ AnimationState drawState = resolveActiveState(); auto it = animations.find(drawState); if (it == animations.end()) return; @@ -942,7 +954,10 @@ void Character::drawNightGpuSkinning(Renderer& renderer, const float* plPosEye, renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye); renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye); renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors); + renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ); + renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ); } + renderer.RenderUniformMatrix4fv("uViewInverse", false, cameraViewInverse.data()); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), @@ -979,6 +994,8 @@ void Character::drawNightGpuSkinning(Renderer& renderer, const float* plPosEye, renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye); renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye); renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors); + renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ); + renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ); } drawAttachedWeapon(renderer); renderer.shaderManager.PopShader(); @@ -987,7 +1004,11 @@ void Character::drawNightGpuSkinning(Renderer& renderer, const float* plPosEye, renderer.shaderManager.PopShader(); } -void Character::drawNightCpu(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, const float* ambientColor, const float* fogColor) { +void Character::drawNightCpu(Renderer& renderer, + const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, + const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse, + const float* ambientColor, const float* fogColor) +{ AnimationState drawState = resolveActiveState(); auto it = animations.find(drawState); if (it == animations.end()) return; @@ -1005,6 +1026,8 @@ void Character::drawNightCpu(Renderer& renderer, const float* plPosEye, const fl renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye); renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye); renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors); + renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ); + renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ); } renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, @@ -1042,7 +1065,10 @@ void Character::drawNightCpu(Renderer& renderer, const float* plPosEye, const fl void Character::drawNightWithShadow(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, - const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor) + const float* plWorldXZ, const float* plLimitXZ, + const Eigen::Matrix4f& cameraViewInverse, + const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, + const float* ambientColor, const float* fogColor) { if (!enabled) return; if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) { @@ -1053,15 +1079,22 @@ void Character::drawNightWithShadow(Renderer& renderer, //if (!isPlayer && hp <= 0) return; if (useGpuSkinning) { - drawNightGpuSkinningWithShadow(renderer, plPosEye, plDirEye, plColors, plCount, lightFromCamera, shadowMapTex, ambientColor, fogColor); + drawNightGpuSkinningWithShadow(renderer, plPosEye, plDirEye, plColors, plCount, + plWorldXZ, plLimitXZ, cameraViewInverse, + lightFromCamera, shadowMapTex, ambientColor, fogColor); } else { - drawNightCpuWithShadow(renderer, plPosEye, plDirEye, plColors, plCount, lightFromCamera, shadowMapTex, ambientColor, fogColor); + drawNightCpuWithShadow(renderer, plPosEye, plDirEye, plColors, plCount, + plWorldXZ, plLimitXZ, cameraViewInverse, + lightFromCamera, shadowMapTex, ambientColor, fogColor); } } void Character::drawNightGpuSkinningWithShadow(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, - const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor) + const float* plWorldXZ, const float* plLimitXZ, + const Eigen::Matrix4f& cameraViewInverse, + const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, + const float* ambientColor, const float* fogColor) { AnimationState drawState = resolveActiveState(); auto it = animations.find(drawState); @@ -1088,6 +1121,9 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer, renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye); renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors); } + renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ); + renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ); + renderer.RenderUniformMatrix4fv("uViewInverse", false, cameraViewInverse.data()); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, shadowMapTex); @@ -1133,6 +1169,8 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer, renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye); renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors); } + renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ); + renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ); drawAttachedWeapon(renderer); renderer.shaderManager.PopShader(); @@ -1142,7 +1180,10 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer, void Character::drawNightCpuWithShadow(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, - const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor) + const float* plWorldXZ, const float* plLimitXZ, + const Eigen::Matrix4f& cameraViewInverse, + const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, + const float* ambientColor, const float* fogColor) { AnimationState drawState = resolveActiveState(); auto it = animations.find(drawState); @@ -1166,6 +1207,8 @@ void Character::drawNightCpuWithShadow(Renderer& renderer, renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye); renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors); } + renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", plCount, plWorldXZ); + renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", plCount, plLimitXZ); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, shadowMapTex); diff --git a/src/Character.h b/src/Character.h index 20fa145..dbf1d16 100644 --- a/src/Character.h +++ b/src/Character.h @@ -51,10 +51,18 @@ public: void draw(Renderer& renderer); void drawShadowDepth(Renderer& renderer); void drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera); - void drawNight(Renderer& renderer, const float* pointLightPosEye, const float* pointLightDirEye, const float* pointLightColors, int pointLightCount, const float* ambientColor, const float* fogColor); + void drawNight(Renderer& renderer, + const float* pointLightPosEye, const float* pointLightDirEye, + const float* pointLightColors, int pointLightCount, + const float* pointLightWorldXZ, const float* pointLightLimitXZ, + const Eigen::Matrix4f& cameraViewInverse, + const float* ambientColor, const float* fogColor); void drawNightWithShadow(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, - const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor); + const float* plWorldXZ, const float* plLimitXZ, + const Eigen::Matrix4f& cameraViewInverse, + const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, + const float* ambientColor, const float* fogColor); // Character-to-character collision (XZ-plane). Used by Location to keep // player/NPCs from walking through each other. @@ -217,14 +225,22 @@ private: void drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera); void drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera); // Night: draw with spot-light shaders - void drawNightGpuSkinning(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, const float* ambientColor, const float* fogColor); - void drawNightCpu(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, const float* ambientColor, const float* fogColor); + void drawNightGpuSkinning(Renderer& renderer, + const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, + const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse, + const float* ambientColor, const float* fogColor); + void drawNightCpu(Renderer& renderer, + const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, + const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse, + const float* ambientColor, const float* fogColor); // Night: draw with spot-light + shadow shaders void drawNightGpuSkinningWithShadow(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, + const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor); void drawNightCpuWithShadow(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount, + const float* plWorldXZ, const float* plLimitXZ, const Eigen::Matrix4f& cameraViewInverse, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor); }; diff --git a/src/Game.cpp b/src/Game.cpp index 210c08a..578f1a1 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -14,6 +14,7 @@ #include #endif + #ifdef EMSCRIPTEN #include #endif diff --git a/src/Location.cpp b/src/Location.cpp index 7b77848..2575d2b 100644 --- a/src/Location.cpp +++ b/src/Location.cpp @@ -318,6 +318,10 @@ namespace ZL pl.id = item.value("id", ""); pl.autoLight = item.value("autoLight", false); pl.autoLightDistance = item.value("autoLightDistance", -1.0f); + pl.limitX = item.value("limitX", 99999.f); + pl.limitY = item.value("limitY", 99999.f); + pl.autoLightLimitX = item.value("autoLightLimitX", -1.0f); + pl.autoLightLimitY = item.value("autoLightLimitY", -1.0f); pl.position = Eigen::Vector3f( item.value("positionX", 0.0f), item.value("positionY", 0.0f), @@ -1005,10 +1009,18 @@ namespace ZL for (int i = 0; i < static_cast(pointLights.size()); ++i) { const PointLight& pl = pointLights[i]; bool active = !pl.autoLight; - if (!active && i == nearestLightIdx) { - // autoLight: also check distance threshold if set - const float dist = player ? (pl.position - player->position).norm() : 0.0f; - active = (pl.autoLightDistance <= 0.0f || dist <= pl.autoLightDistance); + if (!active && player) { + if (pl.autoLightLimitX > 0.0f && pl.autoLightLimitY > 0.0f) { + // Rectangular activation zone — independent of nearest-light heuristic + const float dx = std::abs(player->position.x() - pl.position.x()); + const float dz = std::abs(player->position.z() - pl.position.z()); + active = (dx <= pl.autoLightLimitX && dz <= pl.autoLightLimitY); + } else { + // Legacy: only the nearest autoLight activates, with optional distance cap + const float dist = (pl.position - player->position).norm(); + active = (i == nearestLightIdx) && + (pl.autoLightDistance <= 0.0f || dist <= pl.autoLightDistance); + } } if (active) { activeLightIndices.push_back(i); @@ -1079,6 +1091,8 @@ namespace ZL float plPosEye[MAX_POINT_LIGHTS * 3] = {}; float plDirEye[MAX_POINT_LIGHTS * 3] = {}; float plColors[MAX_POINT_LIGHTS * 3] = {}; + float plWorldXZ[MAX_POINT_LIGHTS * 2] = {}; + float plLimitXZ[MAX_POINT_LIGHTS * 2] = {}; const Eigen::Matrix3f camRot = cameraViewMatrix.block<3, 3>(0, 0); const int lightCount = static_cast(activeLightIndices.size()); for (int j = 0; j < lightCount; ++j) { @@ -1095,12 +1109,19 @@ namespace ZL plColors[j * 3 + 0] = pl.color.x(); plColors[j * 3 + 1] = pl.color.y(); plColors[j * 3 + 2] = pl.color.z(); + plWorldXZ[j * 2 + 0] = pl.position.x(); + plWorldXZ[j * 2 + 1] = pl.position.z(); + plLimitXZ[j * 2 + 0] = pl.limitX; + plLimitXZ[j * 2 + 1] = pl.limitY; } + // Compute camera view inverse (needed for shadow lookup and skinning world-pos recovery) + const Eigen::Matrix4f cameraViewInverse = cameraViewMatrix.inverse(); + // Compute light-from-camera matrix for shadow lookup Eigen::Matrix4f lightFromCamera = Eigen::Matrix4f::Identity(); if (hasShadows) { - lightFromCamera = shadowMap->getLightSpaceMatrix() * cameraViewMatrix.inverse(); + lightFromCamera = shadowMap->getLightSpaceMatrix() * cameraViewInverse; renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data()); // Pass first active light's eye-space direction as uLightDir for shadow bias renderer.RenderUniform3fv("uLightDir", plDirEye); @@ -1111,6 +1132,8 @@ namespace ZL renderer.RenderUniform3fvArray("uPointLightPos[0]", lightCount, plPosEye); renderer.RenderUniform3fvArray("uPointLightDir[0]", lightCount, plDirEye); renderer.RenderUniform3fvArray("uPointLightColor[0]", lightCount, plColors); + renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", lightCount, plWorldXZ); + renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", lightCount, plLimitXZ); } renderer.RenderUniform1f("uAlpha", 1.0f); @@ -1132,11 +1155,17 @@ namespace ZL for (auto& tz : teleportZones) tz.prepareForDraw(cameraViewMatrix); if (hasShadows) { - if (player) player->drawNightWithShadow(renderer, plPosEye, plDirEye, plColors, lightCount, lightFromCamera, shadowMap->getDepthTexture(), ambientColor, fogColor); - for (auto& npc : npcs) npc->drawNightWithShadow(renderer, plPosEye, plDirEye, plColors, lightCount, lightFromCamera, shadowMap->getDepthTexture(), ambientColor, fogColor); + if (player) player->drawNightWithShadow(renderer, plPosEye, plDirEye, plColors, lightCount, + plWorldXZ, plLimitXZ, cameraViewInverse, + lightFromCamera, shadowMap->getDepthTexture(), ambientColor, fogColor); + for (auto& npc : npcs) npc->drawNightWithShadow(renderer, plPosEye, plDirEye, plColors, lightCount, + plWorldXZ, plLimitXZ, cameraViewInverse, + lightFromCamera, shadowMap->getDepthTexture(), ambientColor, fogColor); } else { - if (player) player->drawNight(renderer, plPosEye, plDirEye, plColors, lightCount, ambientColor, fogColor); - for (auto& npc : npcs) npc->drawNight(renderer, plPosEye, plDirEye, plColors, lightCount, ambientColor, fogColor); + if (player) player->drawNight(renderer, plPosEye, plDirEye, plColors, lightCount, + plWorldXZ, plLimitXZ, cameraViewInverse, ambientColor, fogColor); + for (auto& npc : npcs) npc->drawNight(renderer, plPosEye, plDirEye, plColors, lightCount, + plWorldXZ, plLimitXZ, cameraViewInverse, ambientColor, fogColor); } for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height); diff --git a/src/Location.h b/src/Location.h index 65c048a..a197801 100644 --- a/src/Location.h +++ b/src/Location.h @@ -28,6 +28,11 @@ namespace ZL Eigen::Vector3f color; // RGB, values > 1.0 increase intensity bool autoLight = false; // if true, only activates when nearest light to player float autoLightDistance = -1.0f; // max distance for autoLight activation; <=0 means unlimited + // Rectangular limits — keep light confined to a room-sized AABB centred on the light + float limitX = 99999.f; // shader fragment limit: half-width in world X + float limitY = 99999.f; // shader fragment limit: half-width in world Z + float autoLightLimitX = -1.0f; // activation rect half-width X; <0 = use legacy nearest-light logic + float autoLightLimitY = -1.0f; // activation rect half-width Z }; struct TriggerZone diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1c91da0..af7aead 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -813,6 +813,26 @@ namespace ZL { } } + void Renderer::RenderUniform2fv(const std::string& uniformName, const float* value) + { + auto shader = shaderManager.GetCurrentShader(); + auto uniform = shader->uniformList.find(uniformName); + if (uniform != shader->uniformList.end()) + { + glUniform2fv(uniform->second, 1, value); + } + } + + void Renderer::RenderUniform2fvArray(const std::string& uniformName, int count, const float* value) + { + auto shader = shaderManager.GetCurrentShader(); + auto uniform = shader->uniformList.find(uniformName); + if (uniform != shader->uniformList.end()) + { + glUniform2fv(uniform->second, count, value); + } + } + void Renderer::RenderUniform3fv(const std::string& uniformName, const float* value) { auto shader = shaderManager.GetCurrentShader(); diff --git a/src/render/Renderer.h b/src/render/Renderer.h index 35d7941..8988db9 100644 --- a/src/render/Renderer.h +++ b/src/render/Renderer.h @@ -134,6 +134,8 @@ namespace ZL { void RenderUniformMatrix4fv(const std::string& uniformName, bool transpose, const float* value); void RenderUniformMatrix4fvArray(const std::string& uniformName, int count, bool transpose, const float* value); void RenderUniform1i(const std::string& uniformName, const int value); + void RenderUniform2fv(const std::string& uniformName, const float* value); + void RenderUniform2fvArray(const std::string& uniformName, int count, const float* value); void RenderUniform3fv(const std::string& uniformName, const float* value); void RenderUniform3fvArray(const std::string& uniformName, int count, const float* value); void RenderUniform4fv(const std::string& uniformName, const float* value);