Compare commits
2 Commits
e6bfe85e2e
...
e3542b59f7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3542b59f7 | ||
|
|
442d3a7945 |
82
resources/config2/lights_dorm.json
Normal file
82
resources/config2/lights_dorm.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
resources/config2/lights_uni_exterior.json
Normal file
17
resources/config2/lights_uni_exterior.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"lights": [
|
||||||
|
{
|
||||||
|
"id": "lamp_entrance",
|
||||||
|
"positionX": 6.6335,
|
||||||
|
"positionY": 2.9,
|
||||||
|
"positionZ": -9.3,
|
||||||
|
"directionX": 0.0,
|
||||||
|
"directionY": -1.0,
|
||||||
|
"directionZ": 0.0,
|
||||||
|
"colorR": 4.0,
|
||||||
|
"colorG": 3.2,
|
||||||
|
"colorB": 2.4,
|
||||||
|
"autoLight": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
resources/config2/lights_uni_interior.json
Normal file
17
resources/config2/lights_uni_interior.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"lights": [
|
||||||
|
{
|
||||||
|
"id": "lamp_library",
|
||||||
|
"positionX": 3.76222,
|
||||||
|
"positionY": 5.0,
|
||||||
|
"positionZ": 4.16035,
|
||||||
|
"directionX": 0.0,
|
||||||
|
"directionY": -1.0,
|
||||||
|
"directionZ": 0.0,
|
||||||
|
"colorR": 4.0,
|
||||||
|
"colorG": 3.2,
|
||||||
|
"colorB": 2.4,
|
||||||
|
"autoLight": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
22
resources/shaders/night_fog.vertex
Normal file
22
resources/shaders/night_fog.vertex
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
attribute vec3 vPosition;
|
||||||
|
attribute vec2 vTexCoord;
|
||||||
|
attribute vec3 vNormal;
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
|
||||||
|
uniform mat4 ProjectionModelViewMatrix;
|
||||||
|
uniform mat4 ModelViewMatrix;
|
||||||
|
uniform vec3 uPlayerEyePos;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 eyePos = ModelViewMatrix * vec4(vPosition.xyz, 1.0);
|
||||||
|
fogDistance = length(eyePos.xyz - uPlayerEyePos);
|
||||||
|
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
|
||||||
|
texCoord = vTexCoord;
|
||||||
|
fragViewPos = eyePos.xyz;
|
||||||
|
fragNormal = mat3(ModelViewMatrix) * vNormal;
|
||||||
|
}
|
||||||
56
resources/shaders/night_fog_desktop.fragment
Normal file
56
resources/shaders/night_fog_desktop.fragment
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
uniform sampler2D Texture;
|
||||||
|
uniform float uAlpha;
|
||||||
|
uniform int uPointLightCount;
|
||||||
|
uniform vec3 uPointLightPos[4];
|
||||||
|
uniform vec3 uPointLightDir[4];
|
||||||
|
uniform vec3 uPointLightColor[4];
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
|
||||||
|
// cos(45 deg) = half-angle for a 90-degree full-angle spotlight
|
||||||
|
const float SPOT_COS_OUTER = 0.7071;
|
||||||
|
const float SPOT_COS_INNER = 0.82; // slightly tighter inner cone for smooth edge
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = texture2D(Texture, texCoord);
|
||||||
|
|
||||||
|
if (color.a < 0.1)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
float ambient = 0.37;
|
||||||
|
vec3 lighting = vec3(ambient);
|
||||||
|
|
||||||
|
bool hasNormal = dot(fragNormal, fragNormal) > 0.001;
|
||||||
|
vec3 N = hasNormal ? normalize(fragNormal) : vec3(0.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (i >= uPointLightCount) break;
|
||||||
|
|
||||||
|
vec3 lightVec = uPointLightPos[i] - fragViewPos;
|
||||||
|
float dist = length(lightVec);
|
||||||
|
vec3 lightDir = normalize(lightVec);
|
||||||
|
|
||||||
|
// Spotlight cone: angle between the cone axis and the ray from light to fragment
|
||||||
|
float cosTheta = dot(-lightDir, normalize(uPointLightDir[i]));
|
||||||
|
float spotAtten = smoothstep(SPOT_COS_OUTER, SPOT_COS_INNER, cosTheta);
|
||||||
|
if (spotAtten <= 0.0) continue;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
color.rgb *= lighting;
|
||||||
|
|
||||||
|
vec3 fogColor = vec3(0.01, 0.01, 0.05);
|
||||||
|
float fogFactor = clamp((fogDistance - 15.0) / 8.0, 0.0, 1.0);
|
||||||
|
color.rgb = mix(color.rgb, fogColor, fogFactor);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color.rgb, color.a * uAlpha);
|
||||||
|
}
|
||||||
25
resources/shaders/night_fog_shadow.vertex
Normal file
25
resources/shaders/night_fog_shadow.vertex
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
attribute vec3 vPosition;
|
||||||
|
attribute vec2 vTexCoord;
|
||||||
|
attribute vec3 vNormal;
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying vec4 fragPosLightSpace;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
|
||||||
|
uniform mat4 ProjectionModelViewMatrix;
|
||||||
|
uniform mat4 ModelViewMatrix;
|
||||||
|
uniform mat4 uLightFromCamera;
|
||||||
|
uniform vec3 uPlayerEyePos;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 eyePos = ModelViewMatrix * vec4(vPosition, 1.0);
|
||||||
|
fogDistance = length(eyePos.xyz - uPlayerEyePos);
|
||||||
|
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
|
||||||
|
texCoord = vTexCoord;
|
||||||
|
fragViewPos = eyePos.xyz;
|
||||||
|
fragNormal = mat3(ModelViewMatrix) * vNormal;
|
||||||
|
fragPosLightSpace = uLightFromCamera * eyePos;
|
||||||
|
}
|
||||||
95
resources/shaders/night_fog_shadow_desktop.fragment
Normal file
95
resources/shaders/night_fog_shadow_desktop.fragment
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
uniform sampler2D Texture;
|
||||||
|
uniform sampler2D uShadowMap;
|
||||||
|
uniform vec3 uLightDir;
|
||||||
|
uniform float uAlpha;
|
||||||
|
uniform int uPointLightCount;
|
||||||
|
uniform vec3 uPointLightPos[4];
|
||||||
|
uniform vec3 uPointLightDir[4];
|
||||||
|
uniform vec3 uPointLightColor[4];
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying vec4 fragPosLightSpace;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
|
||||||
|
const float SPOT_COS_OUTER = 0.7071;
|
||||||
|
const float SPOT_COS_INNER = 0.82;
|
||||||
|
|
||||||
|
float computeShadow(vec4 lightSpacePos, vec3 normal)
|
||||||
|
{
|
||||||
|
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
||||||
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
|
|
||||||
|
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||||
|
projCoords.y < 0.0 || projCoords.y > 1.0 ||
|
||||||
|
projCoords.z > 1.0)
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentDepth = projCoords.z;
|
||||||
|
|
||||||
|
float bias = 0.0004;
|
||||||
|
if (dot(normal, normal) > 0.001)
|
||||||
|
{
|
||||||
|
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
|
||||||
|
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
|
||||||
|
}
|
||||||
|
|
||||||
|
float shadow = 0.0;
|
||||||
|
float texelSize = 1.0 / 2048.0;
|
||||||
|
for (int x = -1; x <= 1; x++)
|
||||||
|
{
|
||||||
|
for (int y = -1; y <= 1; y++)
|
||||||
|
{
|
||||||
|
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
|
||||||
|
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadow /= 9.0;
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = texture2D(Texture, texCoord);
|
||||||
|
|
||||||
|
if (color.a < 0.1)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
float ambient = 0.37;
|
||||||
|
vec3 lighting = vec3(ambient);
|
||||||
|
|
||||||
|
bool hasNormal = dot(fragNormal, fragNormal) > 0.001;
|
||||||
|
vec3 N = hasNormal ? normalize(fragNormal) : vec3(0.0);
|
||||||
|
|
||||||
|
float shadow = computeShadow(fragPosLightSpace, fragNormal);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (i >= uPointLightCount) break;
|
||||||
|
|
||||||
|
vec3 lightVec = uPointLightPos[i] - fragViewPos;
|
||||||
|
float dist = length(lightVec);
|
||||||
|
vec3 lightDir = normalize(lightVec);
|
||||||
|
|
||||||
|
float cosTheta = dot(-lightDir, normalize(uPointLightDir[i]));
|
||||||
|
float spotAtten = smoothstep(SPOT_COS_OUTER, SPOT_COS_INNER, cosTheta);
|
||||||
|
if (spotAtten <= 0.0) continue;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
color.rgb *= lighting;
|
||||||
|
|
||||||
|
vec3 fogColor = vec3(0.01, 0.01, 0.05);
|
||||||
|
float fogFactor = clamp((fogDistance - 15.0) / 8.0, 0.0, 1.0);
|
||||||
|
color.rgb = mix(color.rgb, fogColor, fogFactor);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color.rgb, color.a * uAlpha);
|
||||||
|
}
|
||||||
97
resources/shaders/night_fog_shadow_web.fragment
Normal file
97
resources/shaders/night_fog_shadow_web.fragment
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform sampler2D Texture;
|
||||||
|
uniform sampler2D uShadowMap;
|
||||||
|
uniform vec3 uLightDir;
|
||||||
|
uniform float uAlpha;
|
||||||
|
uniform int uPointLightCount;
|
||||||
|
uniform vec3 uPointLightPos[4];
|
||||||
|
uniform vec3 uPointLightDir[4];
|
||||||
|
uniform vec3 uPointLightColor[4];
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying vec4 fragPosLightSpace;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
|
||||||
|
const float SPOT_COS_OUTER = 0.7071;
|
||||||
|
const float SPOT_COS_INNER = 0.82;
|
||||||
|
|
||||||
|
float computeShadow(vec4 lightSpacePos, vec3 normal)
|
||||||
|
{
|
||||||
|
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
||||||
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
|
|
||||||
|
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||||
|
projCoords.y < 0.0 || projCoords.y > 1.0 ||
|
||||||
|
projCoords.z > 1.0)
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentDepth = projCoords.z;
|
||||||
|
|
||||||
|
float bias = 0.0004;
|
||||||
|
if (dot(normal, normal) > 0.001)
|
||||||
|
{
|
||||||
|
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
|
||||||
|
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
|
||||||
|
}
|
||||||
|
|
||||||
|
float shadow = 0.0;
|
||||||
|
float texelSize = 1.0 / 2048.0;
|
||||||
|
for (int x = -1; x <= 1; x++)
|
||||||
|
{
|
||||||
|
for (int y = -1; y <= 1; y++)
|
||||||
|
{
|
||||||
|
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
|
||||||
|
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadow /= 9.0;
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = texture2D(Texture, texCoord);
|
||||||
|
|
||||||
|
if (color.a < 0.1)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
float ambient = 0.37;
|
||||||
|
vec3 lighting = vec3(ambient);
|
||||||
|
|
||||||
|
bool hasNormal = dot(fragNormal, fragNormal) > 0.001;
|
||||||
|
vec3 N = hasNormal ? normalize(fragNormal) : vec3(0.0);
|
||||||
|
|
||||||
|
float shadow = computeShadow(fragPosLightSpace, fragNormal);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (i >= uPointLightCount) break;
|
||||||
|
|
||||||
|
vec3 lightVec = uPointLightPos[i] - fragViewPos;
|
||||||
|
float dist = length(lightVec);
|
||||||
|
vec3 lightDir = normalize(lightVec);
|
||||||
|
|
||||||
|
float cosTheta = dot(-lightDir, normalize(uPointLightDir[i]));
|
||||||
|
float spotAtten = smoothstep(SPOT_COS_OUTER, SPOT_COS_INNER, cosTheta);
|
||||||
|
if (spotAtten <= 0.0) continue;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
color.rgb *= lighting;
|
||||||
|
|
||||||
|
vec3 fogColor = vec3(0.01, 0.01, 0.05);
|
||||||
|
float fogFactor = clamp((fogDistance - 15.0) / 8.0, 0.0, 1.0);
|
||||||
|
color.rgb = mix(color.rgb, fogColor, fogFactor);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color.rgb, color.a * uAlpha);
|
||||||
|
}
|
||||||
68
resources/shaders/night_fog_skinning.vertex
Normal file
68
resources/shaders/night_fog_skinning.vertex
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
attribute vec3 vPosition;
|
||||||
|
attribute vec2 vTexCoord;
|
||||||
|
attribute vec3 vNormal;
|
||||||
|
attribute vec4 aBoneIndices0;
|
||||||
|
attribute vec2 aBoneIndices1;
|
||||||
|
attribute vec4 aBoneWeights0;
|
||||||
|
attribute vec2 aBoneWeights1;
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
|
||||||
|
uniform mat4 ProjectionModelViewMatrix;
|
||||||
|
uniform mat4 ModelViewMatrix;
|
||||||
|
uniform mat4 uBoneMatrices[64];
|
||||||
|
uniform vec3 uPlayerEyePos;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
|
||||||
|
vec4 originalPos = vec4(vPosition, 1.0);
|
||||||
|
float totalWeight = 0.0;
|
||||||
|
|
||||||
|
if (aBoneWeights0.x > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
|
||||||
|
totalWeight += aBoneWeights0.x;
|
||||||
|
}
|
||||||
|
if (aBoneWeights0.y > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
|
||||||
|
totalWeight += aBoneWeights0.y;
|
||||||
|
}
|
||||||
|
if (aBoneWeights0.z > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
|
||||||
|
totalWeight += aBoneWeights0.z;
|
||||||
|
}
|
||||||
|
if (aBoneWeights0.w > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
|
||||||
|
totalWeight += aBoneWeights0.w;
|
||||||
|
}
|
||||||
|
if (aBoneWeights1.x > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
|
||||||
|
totalWeight += aBoneWeights1.x;
|
||||||
|
}
|
||||||
|
if (aBoneWeights1.y > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
|
||||||
|
totalWeight += aBoneWeights1.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalWeight < 0.001) {
|
||||||
|
skinnedPos = originalPos;
|
||||||
|
skinnedNormal = vNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 eyePos = ModelViewMatrix * skinnedPos;
|
||||||
|
fogDistance = length(eyePos.xyz - uPlayerEyePos);
|
||||||
|
gl_Position = ProjectionModelViewMatrix * skinnedPos;
|
||||||
|
texCoord = vTexCoord;
|
||||||
|
fragViewPos = eyePos.xyz;
|
||||||
|
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
|
||||||
|
}
|
||||||
71
resources/shaders/night_fog_skinning_shadow.vertex
Normal file
71
resources/shaders/night_fog_skinning_shadow.vertex
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
attribute vec3 vPosition;
|
||||||
|
attribute vec2 vTexCoord;
|
||||||
|
attribute vec3 vNormal;
|
||||||
|
attribute vec4 aBoneIndices0;
|
||||||
|
attribute vec2 aBoneIndices1;
|
||||||
|
attribute vec4 aBoneWeights0;
|
||||||
|
attribute vec2 aBoneWeights1;
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying vec4 fragPosLightSpace;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
|
||||||
|
uniform mat4 ProjectionModelViewMatrix;
|
||||||
|
uniform mat4 ModelViewMatrix;
|
||||||
|
uniform mat4 uLightFromCamera;
|
||||||
|
uniform mat4 uBoneMatrices[64];
|
||||||
|
uniform vec3 uPlayerEyePos;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
|
||||||
|
vec4 originalPos = vec4(vPosition, 1.0);
|
||||||
|
float totalWeight = 0.0;
|
||||||
|
|
||||||
|
if (aBoneWeights0.x > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
|
||||||
|
totalWeight += aBoneWeights0.x;
|
||||||
|
}
|
||||||
|
if (aBoneWeights0.y > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
|
||||||
|
totalWeight += aBoneWeights0.y;
|
||||||
|
}
|
||||||
|
if (aBoneWeights0.z > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
|
||||||
|
totalWeight += aBoneWeights0.z;
|
||||||
|
}
|
||||||
|
if (aBoneWeights0.w > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
|
||||||
|
totalWeight += aBoneWeights0.w;
|
||||||
|
}
|
||||||
|
if (aBoneWeights1.x > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
|
||||||
|
totalWeight += aBoneWeights1.x;
|
||||||
|
}
|
||||||
|
if (aBoneWeights1.y > 0.0) {
|
||||||
|
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
|
||||||
|
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
|
||||||
|
totalWeight += aBoneWeights1.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalWeight < 0.001) {
|
||||||
|
skinnedPos = originalPos;
|
||||||
|
skinnedNormal = vNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 eyePos = ModelViewMatrix * skinnedPos;
|
||||||
|
fogDistance = length(eyePos.xyz - uPlayerEyePos);
|
||||||
|
gl_Position = ProjectionModelViewMatrix * skinnedPos;
|
||||||
|
texCoord = vTexCoord;
|
||||||
|
fragViewPos = eyePos.xyz;
|
||||||
|
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
|
||||||
|
fragPosLightSpace = uLightFromCamera * eyePos;
|
||||||
|
}
|
||||||
58
resources/shaders/night_fog_web.fragment
Normal file
58
resources/shaders/night_fog_web.fragment
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform sampler2D Texture;
|
||||||
|
uniform float uAlpha;
|
||||||
|
uniform int uPointLightCount;
|
||||||
|
uniform vec3 uPointLightPos[4];
|
||||||
|
uniform vec3 uPointLightDir[4];
|
||||||
|
uniform vec3 uPointLightColor[4];
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
varying float fogDistance;
|
||||||
|
varying vec3 fragViewPos;
|
||||||
|
varying vec3 fragNormal;
|
||||||
|
|
||||||
|
// cos(45 deg) = half-angle for a 90-degree full-angle spotlight
|
||||||
|
const float SPOT_COS_OUTER = 0.7071;
|
||||||
|
const float SPOT_COS_INNER = 0.82; // slightly tighter inner cone for smooth edge
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = texture2D(Texture, texCoord);
|
||||||
|
|
||||||
|
if (color.a < 0.1)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
float ambient = 0.37;
|
||||||
|
vec3 lighting = vec3(ambient);
|
||||||
|
|
||||||
|
bool hasNormal = dot(fragNormal, fragNormal) > 0.001;
|
||||||
|
vec3 N = hasNormal ? normalize(fragNormal) : vec3(0.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (i >= uPointLightCount) break;
|
||||||
|
|
||||||
|
vec3 lightVec = uPointLightPos[i] - fragViewPos;
|
||||||
|
float dist = length(lightVec);
|
||||||
|
vec3 lightDir = normalize(lightVec);
|
||||||
|
|
||||||
|
// Spotlight cone: angle between the cone axis and the ray from light to fragment
|
||||||
|
float cosTheta = dot(-lightDir, normalize(uPointLightDir[i]));
|
||||||
|
float spotAtten = smoothstep(SPOT_COS_OUTER, SPOT_COS_INNER, cosTheta);
|
||||||
|
if (spotAtten <= 0.0) continue;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
color.rgb *= lighting;
|
||||||
|
|
||||||
|
vec3 fogColor = vec3(0.01, 0.01, 0.05);
|
||||||
|
float fogFactor = clamp((fogDistance - 15.0) / 8.0, 0.0, 1.0);
|
||||||
|
color.rgb = mix(color.rgb, fogColor, fogFactor);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color.rgb, color.a * uAlpha);
|
||||||
|
}
|
||||||
@ -901,6 +901,295 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
|
|||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Night pass with point lights ====================
|
||||||
|
|
||||||
|
void Character::drawNight(Renderer& renderer, const float* pointLightPosEye, const float* pointLightDirEye, const float* pointLightColors, int pointLightCount) {
|
||||||
|
if (!enabled) return;
|
||||||
|
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||||
|
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
if (!isPlayer && hp <= 0) return;
|
||||||
|
|
||||||
|
if (useGpuSkinning) {
|
||||||
|
drawNightGpuSkinning(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount);
|
||||||
|
} else {
|
||||||
|
drawNightCpu(renderer, pointLightPosEye, pointLightDirEye, pointLightColors, pointLightCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Character::drawNightGpuSkinning(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount) {
|
||||||
|
AnimationState drawState = resolveActiveState();
|
||||||
|
auto it = animations.find(drawState);
|
||||||
|
if (it == animations.end()) return;
|
||||||
|
|
||||||
|
if (!prepareGpuSkinning()) return;
|
||||||
|
|
||||||
|
static const std::string nightSkinningShader = "night_fog_skinning";
|
||||||
|
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(nightSkinningShader);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
const float playerEyePosNight[3] = { 0.0f, 0.0f, -Environment::zoom };
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNight);
|
||||||
|
renderer.RenderUniform1i("uPointLightCount", plCount);
|
||||||
|
if (plCount > 0) {
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||||
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||||
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
|
renderer.ScaleMatrix(modelScale);
|
||||||
|
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
|
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||||
|
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
||||||
|
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
|
||||||
|
|
||||||
|
for (const auto& name : it->second.model.meshNamesOrdered) {
|
||||||
|
auto pit = it->second.gpuSkinningShaderData.perMesh.find(name);
|
||||||
|
if (pit == it->second.gpuSkinningShaderData.perMesh.end()) continue;
|
||||||
|
auto tit = meshTextures.find(name);
|
||||||
|
if (tit == meshTextures.end() || !tit->second) continue;
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tit->second->getTexID());
|
||||||
|
pit->second.RenderVBO(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader("night_fog");
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNight);
|
||||||
|
renderer.RenderUniform1i("uPointLightCount", plCount);
|
||||||
|
if (plCount > 0) {
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors);
|
||||||
|
}
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
renderer.PopProjectionMatrix();
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Character::drawNightCpu(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount) {
|
||||||
|
AnimationState drawState = resolveActiveState();
|
||||||
|
auto it = animations.find(drawState);
|
||||||
|
if (it == animations.end()) return;
|
||||||
|
|
||||||
|
static const std::string nightShader = "night_fog";
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(nightShader);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
const float playerEyePosNight[3] = { 0.0f, 0.0f, -Environment::zoom };
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNight);
|
||||||
|
renderer.RenderUniform1i("uPointLightCount", plCount);
|
||||||
|
if (plCount > 0) {
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||||
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||||
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
|
renderer.ScaleMatrix(modelScale);
|
||||||
|
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
|
auto& anim = it->second;
|
||||||
|
for (const auto& name : anim.model.meshNamesOrdered) {
|
||||||
|
auto mit = anim.model.meshes.find(name);
|
||||||
|
if (mit == anim.model.meshes.end()) continue;
|
||||||
|
auto tit = meshTextures.find(name);
|
||||||
|
if (tit == meshTextures.end() || !tit->second) continue;
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tit->second->getTexID());
|
||||||
|
modelMutable.AssignFrom(mit->second.mesh);
|
||||||
|
modelMutable.RefreshVBO();
|
||||||
|
renderer.DrawVertexRenderStruct(modelMutable);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNight);
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
|
||||||
|
renderer.PopProjectionMatrix();
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Night pass with spot-light + shadow ====================
|
||||||
|
|
||||||
|
void Character::drawNightWithShadow(Renderer& renderer,
|
||||||
|
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
|
||||||
|
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex)
|
||||||
|
{
|
||||||
|
if (!enabled) return;
|
||||||
|
if (hitSparkEmitter.isConfigured() && hitSparkEmitter.getActiveParticleCount() > 0) {
|
||||||
|
hitSparkEmitter.draw(renderer, 1.0f, Environment::width, Environment::height, false);
|
||||||
|
}
|
||||||
|
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
if (!isPlayer && hp <= 0) return;
|
||||||
|
|
||||||
|
if (useGpuSkinning) {
|
||||||
|
drawNightGpuSkinningWithShadow(renderer, plPosEye, plDirEye, plColors, plCount, lightFromCamera, shadowMapTex);
|
||||||
|
} else {
|
||||||
|
drawNightCpuWithShadow(renderer, plPosEye, plDirEye, plColors, plCount, lightFromCamera, shadowMapTex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Character::drawNightGpuSkinningWithShadow(Renderer& renderer,
|
||||||
|
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
|
||||||
|
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex)
|
||||||
|
{
|
||||||
|
AnimationState drawState = resolveActiveState();
|
||||||
|
auto it = animations.find(drawState);
|
||||||
|
if (it == animations.end()) return;
|
||||||
|
if (!prepareGpuSkinning()) return;
|
||||||
|
|
||||||
|
static const std::string nightShadowSkinningShader = "night_fog_skinning_shadow";
|
||||||
|
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(nightShadowSkinningShader);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.RenderUniform1i("uShadowMap", 1);
|
||||||
|
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||||
|
// First light's eye-space direction used for shadow bias
|
||||||
|
if (plCount > 0) renderer.RenderUniform3fv("uLightDir", plDirEye);
|
||||||
|
|
||||||
|
const float playerEyePosNightShadow[3] = { 0.0f, 0.0f, -Environment::zoom };
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNightShadow);
|
||||||
|
renderer.RenderUniform1i("uPointLightCount", plCount);
|
||||||
|
if (plCount > 0) {
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||||
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||||
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
|
renderer.ScaleMatrix(modelScale);
|
||||||
|
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
|
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||||
|
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
||||||
|
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
|
||||||
|
|
||||||
|
for (const auto& name : it->second.model.meshNamesOrdered) {
|
||||||
|
auto pit = it->second.gpuSkinningShaderData.perMesh.find(name);
|
||||||
|
if (pit == it->second.gpuSkinningShaderData.perMesh.end()) continue;
|
||||||
|
auto tit = meshTextures.find(name);
|
||||||
|
if (tit == meshTextures.end() || !tit->second) continue;
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tit->second->getTexID());
|
||||||
|
pit->second.RenderVBO(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
// Weapon uses the non-skinning night shadow shader
|
||||||
|
renderer.shaderManager.PushShader("night_fog_shadow");
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.RenderUniform1i("uShadowMap", 1);
|
||||||
|
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||||
|
if (plCount > 0) renderer.RenderUniform3fv("uLightDir", plDirEye);
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNightShadow);
|
||||||
|
renderer.RenderUniform1i("uPointLightCount", plCount);
|
||||||
|
if (plCount > 0) {
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors);
|
||||||
|
}
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
renderer.PopProjectionMatrix();
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Character::drawNightCpuWithShadow(Renderer& renderer,
|
||||||
|
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
|
||||||
|
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex)
|
||||||
|
{
|
||||||
|
AnimationState drawState = resolveActiveState();
|
||||||
|
auto it = animations.find(drawState);
|
||||||
|
if (it == animations.end()) return;
|
||||||
|
|
||||||
|
static const std::string nightShadowShader = "night_fog_shadow";
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(nightShadowShader);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.RenderUniform1i("uShadowMap", 1);
|
||||||
|
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||||
|
if (plCount > 0) renderer.RenderUniform3fv("uLightDir", plDirEye);
|
||||||
|
|
||||||
|
const float playerEyePosNightShadow[3] = { 0.0f, 0.0f, -Environment::zoom };
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNightShadow);
|
||||||
|
renderer.RenderUniform1i("uPointLightCount", plCount);
|
||||||
|
if (plCount > 0) {
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightPos[0]", plCount, plPosEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightDir[0]", plCount, plDirEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightColor[0]", plCount, plColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||||
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||||
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
|
renderer.ScaleMatrix(modelScale);
|
||||||
|
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
|
auto& anim = it->second;
|
||||||
|
for (const auto& name : anim.model.meshNamesOrdered) {
|
||||||
|
auto mit = anim.model.meshes.find(name);
|
||||||
|
if (mit == anim.model.meshes.end()) continue;
|
||||||
|
auto tit = meshTextures.find(name);
|
||||||
|
if (tit == meshTextures.end() || !tit->second) continue;
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tit->second->getTexID());
|
||||||
|
modelMutable.AssignFrom(mit->second.mesh);
|
||||||
|
modelMutable.RefreshVBO();
|
||||||
|
renderer.DrawVertexRenderStruct(modelMutable);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePosNightShadow);
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
|
||||||
|
renderer.PopProjectionMatrix();
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
}
|
||||||
|
|
||||||
void Character::setupHitSparks(std::shared_ptr<Texture> sparkTexture)
|
void Character::setupHitSparks(std::shared_ptr<Texture> sparkTexture)
|
||||||
{
|
{
|
||||||
hitSparkEmitter.setTexture(sparkTexture);
|
hitSparkEmitter.setTexture(sparkTexture);
|
||||||
|
|||||||
@ -51,6 +51,10 @@ public:
|
|||||||
void draw(Renderer& renderer);
|
void draw(Renderer& renderer);
|
||||||
void drawShadowDepth(Renderer& renderer);
|
void drawShadowDepth(Renderer& renderer);
|
||||||
void drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
|
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);
|
||||||
|
void drawNightWithShadow(Renderer& renderer,
|
||||||
|
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
|
||||||
|
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex);
|
||||||
|
|
||||||
// Character-to-character collision (XZ-plane). Used by Location to keep
|
// Character-to-character collision (XZ-plane). Used by Location to keep
|
||||||
// player/NPCs from walking through each other.
|
// player/NPCs from walking through each other.
|
||||||
@ -201,6 +205,16 @@ private:
|
|||||||
// Shadow: draw with shadow-aware shaders
|
// Shadow: draw with shadow-aware shaders
|
||||||
void drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
|
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);
|
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);
|
||||||
|
void drawNightCpu(Renderer& renderer, const float* plPosEye, const float* plDirEye, const float* plColors, int plCount);
|
||||||
|
// Night: draw with spot-light + shadow shaders
|
||||||
|
void drawNightGpuSkinningWithShadow(Renderer& renderer,
|
||||||
|
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
|
||||||
|
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex);
|
||||||
|
void drawNightCpuWithShadow(Renderer& renderer,
|
||||||
|
const float* plPosEye, const float* plDirEye, const float* plColors, int plCount,
|
||||||
|
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
|
|||||||
33
src/Game.cpp
33
src/Game.cpp
@ -159,6 +159,10 @@ namespace ZL
|
|||||||
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
|
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||||
std::cout << "Load resurces step 12x5" << std::endl;
|
std::cout << "Load resurces step 12x5" << std::endl;
|
||||||
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
|
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
|
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
|
||||||
@ -181,6 +185,10 @@ namespace ZL
|
|||||||
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
|
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||||
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||||
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -254,6 +262,7 @@ namespace ZL
|
|||||||
|
|
||||||
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports_uni_interior.json";
|
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports_uni_interior.json";
|
||||||
uniInteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_interior.json";
|
uniInteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_interior.json";
|
||||||
|
uniInteriorParams.lightsJsonPath = "resources/config2/lights_uni_interior.json";
|
||||||
uniInteriorParams.scriptPath = "resources/start_uni_interior.lua";
|
uniInteriorParams.scriptPath = "resources/start_uni_interior.lua";
|
||||||
uniInteriorParams.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_interior.json";
|
uniInteriorParams.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_interior.json";
|
||||||
uniInteriorParams.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
|
uniInteriorParams.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
|
||||||
@ -280,6 +289,7 @@ namespace ZL
|
|||||||
//uniExteriorParams.navigationJsonPaths = { "resources/navigation/uni_exterior2_all.json" };
|
//uniExteriorParams.navigationJsonPaths = { "resources/navigation/uni_exterior2_all.json" };
|
||||||
uniExteriorParams.teleportsJsonPath = "resources/config2/teleports_uni_exterior.json";
|
uniExteriorParams.teleportsJsonPath = "resources/config2/teleports_uni_exterior.json";
|
||||||
uniExteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_exterior.json";
|
uniExteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_exterior.json";
|
||||||
|
uniExteriorParams.lightsJsonPath = "resources/config2/lights_uni_exterior.json";
|
||||||
uniExteriorParams.scriptPath = "resources/start_uni_exterior.lua";
|
uniExteriorParams.scriptPath = "resources/start_uni_exterior.lua";
|
||||||
uniExteriorParams.playerPosition = Eigen::Vector3f(5, 0, -18.4);
|
uniExteriorParams.playerPosition = Eigen::Vector3f(5, 0, -18.4);
|
||||||
uniExteriorParams.npcsJsonPath = "resources/config2/npcs_uni_exterior.json";
|
uniExteriorParams.npcsJsonPath = "resources/config2/npcs_uni_exterior.json";
|
||||||
@ -300,6 +310,7 @@ namespace ZL
|
|||||||
params_dorm.npcsJsonPath = "resources/config2/npcs_dorm.json";
|
params_dorm.npcsJsonPath = "resources/config2/npcs_dorm.json";
|
||||||
params_dorm.dialoguesJsonPath = "resources/dialogue/dorm_dialogues.json";
|
params_dorm.dialoguesJsonPath = "resources/dialogue/dorm_dialogues.json";
|
||||||
|
|
||||||
|
/*
|
||||||
params_dorm.navigationJsonPaths = {
|
params_dorm.navigationJsonPaths = {
|
||||||
"resources/navigation/dorm3_bca.json",
|
"resources/navigation/dorm3_bca.json",
|
||||||
"resources/navigation/dorm3_ca.json",
|
"resources/navigation/dorm3_ca.json",
|
||||||
@ -307,7 +318,7 @@ namespace ZL
|
|||||||
"resources/navigation/dorm3_a.json",
|
"resources/navigation/dorm3_a.json",
|
||||||
"resources/navigation/dorm3_b.json",
|
"resources/navigation/dorm3_b.json",
|
||||||
"resources/navigation/dorm3_all_open.json",
|
"resources/navigation/dorm3_all_open.json",
|
||||||
};
|
};*/
|
||||||
/*
|
/*
|
||||||
params_dorm.navigationJsonPaths = {
|
params_dorm.navigationJsonPaths = {
|
||||||
"resources/navigation/dorm2_bca2.json",
|
"resources/navigation/dorm2_bca2.json",
|
||||||
@ -328,7 +339,7 @@ namespace ZL
|
|||||||
"resources/navigation/dorm2_b.txt",
|
"resources/navigation/dorm2_b.txt",
|
||||||
"resources/navigation/dorm2_all_open.txt",
|
"resources/navigation/dorm2_all_open.txt",
|
||||||
};*/
|
};*/
|
||||||
/*
|
|
||||||
params_dorm.navigationJsonPaths = {
|
params_dorm.navigationJsonPaths = {
|
||||||
"resources/navigation/dorm0_large.json",
|
"resources/navigation/dorm0_large.json",
|
||||||
"resources/navigation/dorm0_large.json",
|
"resources/navigation/dorm0_large.json",
|
||||||
@ -336,11 +347,12 @@ namespace ZL
|
|||||||
"resources/navigation/dorm0_large.json",
|
"resources/navigation/dorm0_large.json",
|
||||||
"resources/navigation/dorm0_large.json",
|
"resources/navigation/dorm0_large.json",
|
||||||
"resources/navigation/dorm0_large.json",
|
"resources/navigation/dorm0_large.json",
|
||||||
};*/
|
};
|
||||||
|
|
||||||
|
|
||||||
params_dorm.teleportsJsonPath = "resources/config2/teleports_dorm.json";
|
params_dorm.teleportsJsonPath = "resources/config2/teleports_dorm.json";
|
||||||
params_dorm.triggerZonesJsonPath = "resources/config2/trigger_zones_dorm.json";
|
params_dorm.triggerZonesJsonPath = "resources/config2/trigger_zones_dorm.json";
|
||||||
|
params_dorm.lightsJsonPath = "resources/config2/lights_dorm.json";
|
||||||
params_dorm.scriptPath = "resources/start_dorm.lua";
|
params_dorm.scriptPath = "resources/start_dorm.lua";
|
||||||
params_dorm.interactiveObjectsJsonPath = "resources/config2/interactive_objects_dorm.json";
|
params_dorm.interactiveObjectsJsonPath = "resources/config2/interactive_objects_dorm.json";
|
||||||
params_dorm.playerPosition = Eigen::Vector3f(6.76345, 0, -14.6022);
|
params_dorm.playerPosition = Eigen::Vector3f(6.76345, 0, -14.6022);
|
||||||
@ -520,13 +532,18 @@ namespace ZL
|
|||||||
{
|
{
|
||||||
if (currentLocation)
|
if (currentLocation)
|
||||||
{
|
{
|
||||||
// Sync global darklands flag so Location's raycast and draw functions see it.
|
// Sync global flags so Location's draw functions see them.
|
||||||
currentLocation->isDarklands = isDarklands;
|
currentLocation->isDarklands = isDarklands;
|
||||||
|
currentLocation->isNight = isNight;
|
||||||
|
|
||||||
if (isDarklands) {
|
if (isDarklands) {
|
||||||
currentLocation->drawGameDarklands();
|
currentLocation->drawGameDarklands();
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
else if (isNight) {
|
||||||
|
currentLocation->drawGameNight();
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
}
|
||||||
else if (currentLocation->shadowMap) {
|
else if (currentLocation->shadowMap) {
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
currentLocation->drawShadowDepthPass();
|
currentLocation->drawShadowDepthPass();
|
||||||
@ -727,6 +744,13 @@ namespace ZL
|
|||||||
|
|
||||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
|
case SDLK_9:
|
||||||
|
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
||||||
|
currentLocation->editor.selectInteractiveObject(9);
|
||||||
|
} else {
|
||||||
|
isNight = !isNight;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SDLK_0:
|
case SDLK_0:
|
||||||
case SDLK_1:
|
case SDLK_1:
|
||||||
case SDLK_2:
|
case SDLK_2:
|
||||||
@ -736,7 +760,6 @@ namespace ZL
|
|||||||
case SDLK_6:
|
case SDLK_6:
|
||||||
case SDLK_7:
|
case SDLK_7:
|
||||||
case SDLK_8:
|
case SDLK_8:
|
||||||
case SDLK_9:
|
|
||||||
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
||||||
currentLocation->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
|
currentLocation->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
|
||||||
} else if (event.key.keysym.sym == SDLK_0) {
|
} else if (event.key.keysym.sym == SDLK_0) {
|
||||||
|
|||||||
@ -52,6 +52,9 @@ namespace ZL {
|
|||||||
// Returns false if a transition is already in progress.
|
// Returns false if a transition is already in progress.
|
||||||
bool startDarklandsTransition();
|
bool startDarklandsTransition();
|
||||||
|
|
||||||
|
// Global night mode state — persists across location transitions.
|
||||||
|
bool isNight = false;
|
||||||
|
|
||||||
Inventory inventory;
|
Inventory inventory;
|
||||||
InteractiveObject* pickedUpObject = nullptr;
|
InteractiveObject* pickedUpObject = nullptr;
|
||||||
|
|
||||||
|
|||||||
268
src/Location.cpp
268
src/Location.cpp
@ -9,6 +9,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
|
#include <limits>
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
#include "Character.h"
|
#include "Character.h"
|
||||||
#include "external/nlohmann/json.hpp"
|
#include "external/nlohmann/json.hpp"
|
||||||
@ -117,6 +118,7 @@ namespace ZL
|
|||||||
|
|
||||||
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
|
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
|
||||||
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
|
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
|
||||||
|
loadPointLights(params.lightsJsonPath, CONST_ZIP_FILE);
|
||||||
|
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
|
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
|
||||||
@ -273,6 +275,64 @@ namespace ZL
|
|||||||
std::cout << "[TRIGGER] Loaded " << triggerZones.size() << " trigger zone(s) from " << jsonPath << std::endl;
|
std::cout << "[TRIGGER] Loaded " << triggerZones.size() << " trigger zone(s) from " << jsonPath << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Location::loadPointLights(const std::string& jsonPath, const char* zipFile)
|
||||||
|
{
|
||||||
|
if (jsonPath.empty()) return;
|
||||||
|
|
||||||
|
std::string content;
|
||||||
|
try {
|
||||||
|
if (!zipFile || zipFile[0] == '\0') {
|
||||||
|
content = readTextFile(jsonPath);
|
||||||
|
} else {
|
||||||
|
auto buf = readFileFromZIP(jsonPath, zipFile);
|
||||||
|
if (buf.empty()) {
|
||||||
|
std::cerr << "[LIGHTS] Failed to read " << jsonPath << " from zip" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
content.assign(buf.begin(), buf.end());
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "[LIGHTS] Failed to open " << jsonPath << ": " << e.what() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
json j;
|
||||||
|
try {
|
||||||
|
j = json::parse(content);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "[LIGHTS] JSON parse error in " << jsonPath << ": " << e.what() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!j.contains("lights") || !j["lights"].is_array()) return;
|
||||||
|
|
||||||
|
for (const auto& item : j["lights"]) {
|
||||||
|
PointLight pl;
|
||||||
|
pl.id = item.value("id", "");
|
||||||
|
pl.autoLight = item.value("autoLight", false);
|
||||||
|
pl.autoLightDistance = item.value("autoLightDistance", -1.0f);
|
||||||
|
pl.position = Eigen::Vector3f(
|
||||||
|
item.value("positionX", 0.0f),
|
||||||
|
item.value("positionY", 0.0f),
|
||||||
|
item.value("positionZ", 0.0f)
|
||||||
|
);
|
||||||
|
pl.direction = Eigen::Vector3f(
|
||||||
|
item.value("directionX", 0.0f),
|
||||||
|
item.value("directionY", -1.0f),
|
||||||
|
item.value("directionZ", 0.0f)
|
||||||
|
);
|
||||||
|
pl.color = Eigen::Vector3f(
|
||||||
|
item.value("colorR", 1.0f),
|
||||||
|
item.value("colorG", 1.0f),
|
||||||
|
item.value("colorB", 1.0f)
|
||||||
|
);
|
||||||
|
pointLights.push_back(std::move(pl));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[LIGHTS] Loaded " << pointLights.size() << " light(s) from " << jsonPath << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
void Location::updateTriggerZones(const Eigen::Vector3f& playerPos)
|
void Location::updateTriggerZones(const Eigen::Vector3f& playerPos)
|
||||||
{
|
{
|
||||||
for (auto& tz : triggerZones) {
|
for (auto& tz : triggerZones) {
|
||||||
@ -878,6 +938,214 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Location::drawNightShadowDepthPass(const PointLight& light)
|
||||||
|
{
|
||||||
|
if (!shadowMap) return;
|
||||||
|
static constexpr float kSpotFovY = static_cast<float>(M_PI) * 0.5f; // 90°
|
||||||
|
shadowMap->updateSpotlightMatrix(light.position, light.direction, kSpotFovY, 0.1f, 20.0f);
|
||||||
|
shadowMap->bind();
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader("shadow_depth");
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(kSpotFovY, 1.0f, 0.1f, 20.0f);
|
||||||
|
renderer.PushSpecialMatrix(shadowMap->getLightViewMatrix());
|
||||||
|
|
||||||
|
for (auto& [name, gameObj] : gameObjects) {
|
||||||
|
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||||
|
}
|
||||||
|
for (auto& intObj : interactiveObjects) {
|
||||||
|
if (intObj.isActive && intObj.loadedObject.texture) {
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.TranslateMatrix(intObj.position);
|
||||||
|
renderer.DrawVertexRenderStruct(intObj.loadedObject.mesh);
|
||||||
|
renderer.PopMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (player) player->drawShadowDepth(renderer);
|
||||||
|
for (auto& npc : npcs) npc->drawShadowDepth(renderer);
|
||||||
|
|
||||||
|
renderer.PopMatrix(); // light view
|
||||||
|
renderer.PopProjectionMatrix();
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
shadowMap->unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Location::drawGameNight()
|
||||||
|
{
|
||||||
|
// --- Determine which lights are active this frame ---
|
||||||
|
// autoLight=true lights only activate when they are the nearest light to the player.
|
||||||
|
// autoLight=false lights are always active.
|
||||||
|
int nearestLightIdx = -1;
|
||||||
|
if (player && !pointLights.empty()) {
|
||||||
|
float minDist = FLT_MAX;
|
||||||
|
for (int i = 0; i < static_cast<int>(pointLights.size()); ++i) {
|
||||||
|
const float d = (pointLights[i].position - player->position).norm();
|
||||||
|
if (d < minDist) { minDist = d; nearestLightIdx = i; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int MAX_POINT_LIGHTS = 4;
|
||||||
|
std::vector<int> activeLightIndices;
|
||||||
|
activeLightIndices.reserve(pointLights.size());
|
||||||
|
for (int i = 0; i < static_cast<int>(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) {
|
||||||
|
activeLightIndices.push_back(i);
|
||||||
|
if (static_cast<int>(activeLightIndices.size()) >= MAX_POINT_LIGHTS) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shadow caster = nearest active light to player
|
||||||
|
const PointLight* shadowLight = nullptr;
|
||||||
|
if (player && !activeLightIndices.empty()) {
|
||||||
|
float minDist = FLT_MAX;
|
||||||
|
for (int idx : activeLightIndices) {
|
||||||
|
const float d = (pointLights[idx].position - player->position).norm();
|
||||||
|
if (d < minDist) { minDist = d; shadowLight = &pointLights[idx]; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const bool hasShadows = (shadowMap != nullptr && shadowLight != nullptr);
|
||||||
|
|
||||||
|
if (hasShadows) {
|
||||||
|
drawNightShadowDepthPass(*shadowLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main render pass ---
|
||||||
|
glClearColor(0.01f, 0.01f, 0.05f, 1.0f);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
const std::string mainShader = hasShadows ? "night_fog_shadow" : "night_fog";
|
||||||
|
renderer.shaderManager.PushShader(mainShader);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
|
||||||
|
if (hasShadows) {
|
||||||
|
renderer.RenderUniform1i("uShadowMap", 1);
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthTexture());
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float playerEyePos[3] = { 0.0f, 0.0f, -Environment::zoom };
|
||||||
|
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePos);
|
||||||
|
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||||
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
renderer.PushMatrix();
|
||||||
|
|
||||||
|
renderer.LoadIdentity();
|
||||||
|
renderer.TranslateMatrix({ 0, 0, -1.0f * Environment::zoom });
|
||||||
|
|
||||||
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix());
|
||||||
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
|
const Eigen::Vector3f& camTarget = player ? player->position : Eigen::Vector3f::Zero();
|
||||||
|
renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() });
|
||||||
|
|
||||||
|
cameraViewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||||
|
|
||||||
|
// Transform active spot lights to eye space and build uniform arrays
|
||||||
|
float plPosEye[MAX_POINT_LIGHTS * 3] = {};
|
||||||
|
float plDirEye[MAX_POINT_LIGHTS * 3] = {};
|
||||||
|
float plColors[MAX_POINT_LIGHTS * 3] = {};
|
||||||
|
const Eigen::Matrix3f camRot = cameraViewMatrix.block<3, 3>(0, 0);
|
||||||
|
const int lightCount = static_cast<int>(activeLightIndices.size());
|
||||||
|
for (int j = 0; j < lightCount; ++j) {
|
||||||
|
const PointLight& pl = pointLights[activeLightIndices[j]];
|
||||||
|
const Eigen::Vector4f worldPos(pl.position.x(), pl.position.y(), pl.position.z(), 1.0f);
|
||||||
|
const Eigen::Vector4f eyePos4 = cameraViewMatrix * worldPos;
|
||||||
|
plPosEye[j * 3 + 0] = eyePos4.x();
|
||||||
|
plPosEye[j * 3 + 1] = eyePos4.y();
|
||||||
|
plPosEye[j * 3 + 2] = eyePos4.z();
|
||||||
|
const Eigen::Vector3f eyeDir = camRot * pl.direction;
|
||||||
|
plDirEye[j * 3 + 0] = eyeDir.x();
|
||||||
|
plDirEye[j * 3 + 1] = eyeDir.y();
|
||||||
|
plDirEye[j * 3 + 2] = eyeDir.z();
|
||||||
|
plColors[j * 3 + 0] = pl.color.x();
|
||||||
|
plColors[j * 3 + 1] = pl.color.y();
|
||||||
|
plColors[j * 3 + 2] = pl.color.z();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute light-from-camera matrix for shadow lookup
|
||||||
|
Eigen::Matrix4f lightFromCamera = Eigen::Matrix4f::Identity();
|
||||||
|
if (hasShadows) {
|
||||||
|
lightFromCamera = shadowMap->getLightSpaceMatrix() * cameraViewMatrix.inverse();
|
||||||
|
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||||
|
// Pass first active light's eye-space direction as uLightDir for shadow bias
|
||||||
|
renderer.RenderUniform3fv("uLightDir", plDirEye);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.RenderUniform1i("uPointLightCount", lightCount);
|
||||||
|
if (lightCount > 0) {
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightPos[0]", lightCount, plPosEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightDir[0]", lightCount, plDirEye);
|
||||||
|
renderer.RenderUniform3fvArray("uPointLightColor[0]", lightCount, plColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||||
|
for (auto& [name, gameObj] : gameObjects) {
|
||||||
|
if (!gameObj.texture) continue;
|
||||||
|
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& intObj : interactiveObjects) {
|
||||||
|
if (intObj.isActive) {
|
||||||
|
intObj.draw(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||||
|
if (player) player->prepareHitSparksForDraw(cameraViewMatrix);
|
||||||
|
for (auto& npc : npcs) npc->prepareHitSparksForDraw(cameraViewMatrix);
|
||||||
|
for (auto& tz : teleportZones) tz.prepareForDraw(cameraViewMatrix);
|
||||||
|
|
||||||
|
if (hasShadows) {
|
||||||
|
if (player) player->drawNightWithShadow(renderer, plPosEye, plDirEye, plColors, lightCount, lightFromCamera, shadowMap->getDepthTexture());
|
||||||
|
for (auto& npc : npcs) npc->drawNightWithShadow(renderer, plPosEye, plDirEye, plColors, lightCount, lightFromCamera, shadowMap->getDepthTexture());
|
||||||
|
} else {
|
||||||
|
if (player) player->drawNight(renderer, plPosEye, plDirEye, plColors, lightCount);
|
||||||
|
for (auto& npc : npcs) npc->drawNight(renderer, plPosEye, plDirEye, plColors, lightCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||||
|
|
||||||
|
if (editorMode == EditorMode::Navigation) {
|
||||||
|
editor.drawNavigation();
|
||||||
|
editor.drawPoints();
|
||||||
|
}
|
||||||
|
if (editorMode == EditorMode::InteractiveObjects) {
|
||||||
|
editor.drawInteractiveObjectBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PopMatrix();
|
||||||
|
renderer.PopProjectionMatrix();
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
if (npcNameText) {
|
||||||
|
Eigen::Matrix4f proj = MakePerspectiveMatrix(1.0f / 1.5f,
|
||||||
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
for (auto& npc : npcs) {
|
||||||
|
if (npc) npc->drawName(*npcNameText, cameraViewMatrix, proj);
|
||||||
|
}
|
||||||
|
if (player) player->drawHealthBar(renderer, cameraViewMatrix, proj);
|
||||||
|
for (auto& npc : npcs) {
|
||||||
|
if (npc) npc->drawHealthBar(renderer, cameraViewMatrix, proj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Location::setNavigationAreaAvailable(const std::string& areaName, bool available)
|
bool Location::setNavigationAreaAvailable(const std::string& areaName, bool available)
|
||||||
{
|
{
|
||||||
if (!navigation) return false;
|
if (!navigation) return false;
|
||||||
|
|||||||
@ -20,6 +20,16 @@
|
|||||||
namespace ZL
|
namespace ZL
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct PointLight
|
||||||
|
{
|
||||||
|
std::string id;
|
||||||
|
Eigen::Vector3f position; // world space
|
||||||
|
Eigen::Vector3f direction; // world space, unit vector the cone points toward
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
struct TriggerZone
|
struct TriggerZone
|
||||||
{
|
{
|
||||||
std::string id;
|
std::string id;
|
||||||
@ -40,6 +50,7 @@ namespace ZL
|
|||||||
std::string scriptPath;
|
std::string scriptPath;
|
||||||
std::string teleportsJsonPath;
|
std::string teleportsJsonPath;
|
||||||
std::string triggerZonesJsonPath;
|
std::string triggerZonesJsonPath;
|
||||||
|
std::string lightsJsonPath;
|
||||||
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
|
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,9 +94,12 @@ namespace ZL
|
|||||||
|
|
||||||
std::vector<TriggerZone> triggerZones;
|
std::vector<TriggerZone> triggerZones;
|
||||||
|
|
||||||
|
std::vector<PointLight> pointLights;
|
||||||
|
|
||||||
// Set by Game and kept in sync across location transitions.
|
// Set by Game and kept in sync across location transitions.
|
||||||
// Read by draw functions and raycast — do not write from Location code.
|
// Read by draw functions and raycast — do not write from Location code.
|
||||||
bool isDarklands = false;
|
bool isDarklands = false;
|
||||||
|
bool isNight = false;
|
||||||
|
|
||||||
// Called when the player successfully taps the ground and a floor walk target is set.
|
// Called when the player successfully taps the ground and a floor walk target is set.
|
||||||
// Used by the tutorial system to detect the "tap to walk" gesture.
|
// Used by the tutorial system to detect the "tap to walk" gesture.
|
||||||
@ -122,6 +136,8 @@ namespace ZL
|
|||||||
void drawShadowDepthPass();
|
void drawShadowDepthPass();
|
||||||
void drawGameWithShadows();
|
void drawGameWithShadows();
|
||||||
void drawGameDarklands();
|
void drawGameDarklands();
|
||||||
|
void drawGameNight();
|
||||||
|
void drawNightShadowDepthPass(const PointLight& light);
|
||||||
|
|
||||||
bool setNavigationAreaAvailable(const std::string& areaName, bool available);
|
bool setNavigationAreaAvailable(const std::string& areaName, bool available);
|
||||||
|
|
||||||
@ -147,6 +163,7 @@ namespace ZL
|
|||||||
void updateDynamicReplans(int64_t deltaMs);
|
void updateDynamicReplans(int64_t deltaMs);
|
||||||
void loadTeleportZones(const std::string& jsonPath, const char* zipFile);
|
void loadTeleportZones(const std::string& jsonPath, const char* zipFile);
|
||||||
void loadTriggerZones(const std::string& jsonPath, const char* zipFile);
|
void loadTriggerZones(const std::string& jsonPath, const char* zipFile);
|
||||||
|
void loadPointLights(const std::string& jsonPath, const char* zipFile);
|
||||||
void updateTriggerZones(const Eigen::Vector3f& playerPos);
|
void updateTriggerZones(const Eigen::Vector3f& playerPos);
|
||||||
void nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom);
|
void nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom);
|
||||||
int findNpcIndex(const Character* npc) const;
|
int findNpcIndex(const Character* npc) const;
|
||||||
|
|||||||
@ -825,6 +825,16 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::RenderUniform3fvArray(const std::string& uniformName, int count, const float* value)
|
||||||
|
{
|
||||||
|
auto shader = shaderManager.GetCurrentShader();
|
||||||
|
auto uniform = shader->uniformList.find(uniformName);
|
||||||
|
if (uniform != shader->uniformList.end())
|
||||||
|
{
|
||||||
|
glUniform3fv(uniform->second, count, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer::RenderUniform4fv(const std::string& uniformName, const float* value)
|
void Renderer::RenderUniform4fv(const std::string& uniformName, const float* value)
|
||||||
{
|
{
|
||||||
auto shader = shaderManager.GetCurrentShader();
|
auto shader = shaderManager.GetCurrentShader();
|
||||||
|
|||||||
@ -135,6 +135,7 @@ namespace ZL {
|
|||||||
void RenderUniformMatrix4fvArray(const std::string& uniformName, int count, 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 RenderUniform1i(const std::string& uniformName, const int value);
|
||||||
void RenderUniform3fv(const std::string& uniformName, 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);
|
void RenderUniform4fv(const std::string& uniformName, const float* value);
|
||||||
void RenderUniform1f(const std::string& uniformName, float value);
|
void RenderUniform1f(const std::string& uniformName, float value);
|
||||||
|
|
||||||
|
|||||||
@ -133,6 +133,29 @@ namespace ZL {
|
|||||||
lightSpaceMatrix = lightProjectionMatrix * lightViewMatrix;
|
lightSpaceMatrix = lightProjectionMatrix * lightViewMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Eigen::Matrix4f perspective(float fovY, float aspect, float zNear, float zFar) {
|
||||||
|
float t = std::tan(fovY * 0.5f);
|
||||||
|
Eigen::Matrix4f m = Eigen::Matrix4f::Zero();
|
||||||
|
m(0, 0) = 1.0f / (aspect * t);
|
||||||
|
m(1, 1) = 1.0f / t;
|
||||||
|
m(2, 2) = -(zFar + zNear) / (zFar - zNear);
|
||||||
|
m(2, 3) = -(2.0f * zFar * zNear) / (zFar - zNear);
|
||||||
|
m(3, 2) = -1.0f;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowMap::updateSpotlightMatrix(const Eigen::Vector3f& lightPos,
|
||||||
|
const Eigen::Vector3f& lightDir,
|
||||||
|
float fovY, float nearZ, float farZ) {
|
||||||
|
Eigen::Vector3f d = lightDir.normalized();
|
||||||
|
Eigen::Vector3f up(0.f, 1.f, 0.f);
|
||||||
|
if (std::abs(d.dot(up)) > 0.99f)
|
||||||
|
up = Eigen::Vector3f(1.f, 0.f, 0.f);
|
||||||
|
lightViewMatrix = lookAt(lightPos, lightPos + d, up);
|
||||||
|
lightProjectionMatrix = perspective(fovY, 1.0f, nearZ, farZ);
|
||||||
|
lightSpaceMatrix = lightProjectionMatrix * lightViewMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
void ShadowMap::bind() {
|
void ShadowMap::bind() {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
glViewport(0, 0, resolution, resolution);
|
glViewport(0, 0, resolution, resolution);
|
||||||
|
|||||||
@ -29,6 +29,9 @@ namespace ZL {
|
|||||||
|
|
||||||
void setLightDirection(const Eigen::Vector3f& dir);
|
void setLightDirection(const Eigen::Vector3f& dir);
|
||||||
void updateLightSpaceMatrix(const Eigen::Vector3f& sceneCenter);
|
void updateLightSpaceMatrix(const Eigen::Vector3f& sceneCenter);
|
||||||
|
void updateSpotlightMatrix(const Eigen::Vector3f& lightPos,
|
||||||
|
const Eigen::Vector3f& lightDir,
|
||||||
|
float fovY, float nearZ, float farZ);
|
||||||
|
|
||||||
void bind();
|
void bind();
|
||||||
void unbind();
|
void unbind();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user