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__);
|
||||
}
|
||||
|
||||
// ==================== 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)
|
||||
{
|
||||
hitSparkEmitter.setTexture(sparkTexture);
|
||||
|
||||
@ -51,6 +51,10 @@ 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);
|
||||
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
|
||||
// player/NPCs from walking through each other.
|
||||
@ -201,6 +205,16 @@ private:
|
||||
// Shadow: draw with shadow-aware shaders
|
||||
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);
|
||||
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
|
||||
|
||||
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);
|
||||
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("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
|
||||
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("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("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
|
||||
|
||||
@ -254,6 +262,7 @@ namespace ZL
|
||||
|
||||
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports_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.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_interior.json";
|
||||
uniInteriorParams.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
|
||||
@ -280,6 +289,7 @@ namespace ZL
|
||||
//uniExteriorParams.navigationJsonPaths = { "resources/navigation/uni_exterior2_all.json" };
|
||||
uniExteriorParams.teleportsJsonPath = "resources/config2/teleports_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.playerPosition = Eigen::Vector3f(5, 0, -18.4);
|
||||
uniExteriorParams.npcsJsonPath = "resources/config2/npcs_uni_exterior.json";
|
||||
@ -300,6 +310,7 @@ namespace ZL
|
||||
params_dorm.npcsJsonPath = "resources/config2/npcs_dorm.json";
|
||||
params_dorm.dialoguesJsonPath = "resources/dialogue/dorm_dialogues.json";
|
||||
|
||||
/*
|
||||
params_dorm.navigationJsonPaths = {
|
||||
"resources/navigation/dorm3_bca.json",
|
||||
"resources/navigation/dorm3_ca.json",
|
||||
@ -307,7 +318,7 @@ namespace ZL
|
||||
"resources/navigation/dorm3_a.json",
|
||||
"resources/navigation/dorm3_b.json",
|
||||
"resources/navigation/dorm3_all_open.json",
|
||||
};
|
||||
};*/
|
||||
/*
|
||||
params_dorm.navigationJsonPaths = {
|
||||
"resources/navigation/dorm2_bca2.json",
|
||||
@ -328,7 +339,7 @@ namespace ZL
|
||||
"resources/navigation/dorm2_b.txt",
|
||||
"resources/navigation/dorm2_all_open.txt",
|
||||
};*/
|
||||
/*
|
||||
|
||||
params_dorm.navigationJsonPaths = {
|
||||
"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",
|
||||
};*/
|
||||
};
|
||||
|
||||
|
||||
params_dorm.teleportsJsonPath = "resources/config2/teleports_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.interactiveObjectsJsonPath = "resources/config2/interactive_objects_dorm.json";
|
||||
params_dorm.playerPosition = Eigen::Vector3f(6.76345, 0, -14.6022);
|
||||
@ -520,13 +532,18 @@ namespace ZL
|
||||
{
|
||||
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->isNight = isNight;
|
||||
|
||||
if (isDarklands) {
|
||||
currentLocation->drawGameDarklands();
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
}
|
||||
else if (isNight) {
|
||||
currentLocation->drawGameNight();
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
}
|
||||
else if (currentLocation->shadowMap) {
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
currentLocation->drawShadowDepthPass();
|
||||
@ -727,6 +744,13 @@ namespace ZL
|
||||
|
||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
||||
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_1:
|
||||
case SDLK_2:
|
||||
@ -736,7 +760,6 @@ namespace ZL
|
||||
case SDLK_6:
|
||||
case SDLK_7:
|
||||
case SDLK_8:
|
||||
case SDLK_9:
|
||||
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
||||
currentLocation->editor.selectInteractiveObject(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.
|
||||
bool startDarklandsTransition();
|
||||
|
||||
// Global night mode state — persists across location transitions.
|
||||
bool isNight = false;
|
||||
|
||||
Inventory inventory;
|
||||
InteractiveObject* pickedUpObject = nullptr;
|
||||
|
||||
|
||||
268
src/Location.cpp
268
src/Location.cpp
@ -9,6 +9,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <cfloat>
|
||||
#include <limits>
|
||||
#include "GameConstants.h"
|
||||
#include "Character.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
@ -117,6 +118,7 @@ namespace ZL
|
||||
|
||||
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
|
||||
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
|
||||
loadPointLights(params.lightsJsonPath, CONST_ZIP_FILE);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
// 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!navigation) return false;
|
||||
|
||||
@ -20,6 +20,16 @@
|
||||
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
|
||||
{
|
||||
std::string id;
|
||||
@ -40,6 +50,7 @@ namespace ZL
|
||||
std::string scriptPath;
|
||||
std::string teleportsJsonPath;
|
||||
std::string triggerZonesJsonPath;
|
||||
std::string lightsJsonPath;
|
||||
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
|
||||
};
|
||||
|
||||
@ -83,9 +94,12 @@ namespace ZL
|
||||
|
||||
std::vector<TriggerZone> triggerZones;
|
||||
|
||||
std::vector<PointLight> pointLights;
|
||||
|
||||
// Set by Game and kept in sync across location transitions.
|
||||
// Read by draw functions and raycast — do not write from Location code.
|
||||
bool isDarklands = false;
|
||||
bool isNight = false;
|
||||
|
||||
// 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.
|
||||
@ -122,6 +136,8 @@ namespace ZL
|
||||
void drawShadowDepthPass();
|
||||
void drawGameWithShadows();
|
||||
void drawGameDarklands();
|
||||
void drawGameNight();
|
||||
void drawNightShadowDepthPass(const PointLight& light);
|
||||
|
||||
bool setNavigationAreaAvailable(const std::string& areaName, bool available);
|
||||
|
||||
@ -147,6 +163,7 @@ namespace ZL
|
||||
void updateDynamicReplans(int64_t deltaMs);
|
||||
void loadTeleportZones(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 nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom);
|
||||
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)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
|
||||
@ -135,6 +135,7 @@ namespace ZL {
|
||||
void RenderUniformMatrix4fvArray(const std::string& uniformName, int count, bool transpose, const float* value);
|
||||
void RenderUniform1i(const std::string& uniformName, const int 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 RenderUniform1f(const std::string& uniformName, float value);
|
||||
|
||||
|
||||
@ -133,6 +133,29 @@ namespace ZL {
|
||||
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() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glViewport(0, 0, resolution, resolution);
|
||||
|
||||
@ -29,6 +29,9 @@ namespace ZL {
|
||||
|
||||
void setLightDirection(const Eigen::Vector3f& dir);
|
||||
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 unbind();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user