diff --git a/code/def_files/data/effects/deferred-f.sdr b/code/def_files/data/effects/deferred-f.sdr index 748432a009b..2622a8a92f3 100644 --- a/code/def_files/data/effects/deferred-f.sdr +++ b/code/def_files/data/effects/deferred-f.sdr @@ -10,7 +10,7 @@ uniform sampler2D ColorBuffer; uniform sampler2D NormalBuffer; uniform sampler2D PositionBuffer; uniform sampler2D SpecBuffer; -uniform sampler2DArray shadow_map; +uniform sampler2DArrayShadow shadow_map; #ifdef ENV_MAP uniform samplerCube sEnvmap; @@ -18,22 +18,23 @@ uniform samplerCube sIrrmap; #endif layout (std140) uniform globalDeferredData { - mat4 shadow_mv_matrix; - mat4 shadow_proj_matrix[4]; - mat4 inv_view_matrix; - float veryneardist; - float neardist; - float middist; - float fardist; - float invScreenWidth; float invScreenHeight; - float nearPlane; }; +layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; + vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; +}; + layout (std140) uniform matrixData { mat4 modelViewMatrix; mat4 projMatrix; @@ -259,14 +260,12 @@ void main() if (enable_shadows) { vec4 fragShadowPos = shadow_mv_matrix * inv_view_matrix * vec4(position, 1.0); - vec4 fragShadowUV[4]; - fragShadowUV[0] = transformToShadowMap(shadow_proj_matrix[0], 0, fragShadowPos); - fragShadowUV[1] = transformToShadowMap(shadow_proj_matrix[1], 1, fragShadowPos); - fragShadowUV[2] = transformToShadowMap(shadow_proj_matrix[2], 2, fragShadowPos); - fragShadowUV[3] = transformToShadowMap(shadow_proj_matrix[3], 3, fragShadowPos); - - attenuation *= getShadowValue(shadow_map, -position.z, fragShadowPos.z, fragShadowUV, fardist, middist, - neardist, veryneardist); + vec4 fragShadowUV[NUM_SHADOW_CASCADES]; + for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { + fragShadowUV[i] = transformToShadowMap(shadow_proj_matrix[i], i, fragShadowPos); + } + + attenuation *= getShadowValue(shadow_map, -position.z, fragShadowUV, cascade_distances, smoothness_factors, cascade_offset, cascade_count); } vec3 halfVec = normalize(lightDir + eyeDir); diff --git a/code/def_files/data/effects/main-f.sdr b/code/def_files/data/effects/main-f.sdr index 8a36e96d8e1..e5ed5de9c51 100644 --- a/code/def_files/data/effects/main-f.sdr +++ b/code/def_files/data/effects/main-f.sdr @@ -31,8 +31,6 @@ layout (std140) uniform modelData { mat4 viewMatrix; mat4 projMatrix; mat4 textureMatrix; - mat4 shadow_mv_matrix; - mat4 shadow_proj_matrix[4]; vec4 color; @@ -50,7 +48,6 @@ layout (std140) uniform modelData { int n_lights; float defaultGloss; - //EXCLUSIVELY used for non-deferred rendering vec3 ambientFactor; int desaturate; @@ -66,7 +63,6 @@ layout (std140) uniform modelData { int effect_num; int sBasemapIndex; - vec4 fogColor; vec3 base_color; @@ -80,11 +76,6 @@ layout (std140) uniform modelData { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - int sGlowmapIndex; int sSpecmapIndex; int sNormalmapIndex; @@ -95,6 +86,16 @@ layout (std140) uniform modelData { int flags; }; +layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; + vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; +}; + in VertexOutput { mat3 tangentMatrix; @@ -107,7 +108,7 @@ in VertexOutput { vec4 texCoord; #prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS - vec4 shadowUV[4]; + vec4 shadowUV[NUM_SHADOW_CASCADES]; vec4 shadowPos; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS } vertIn; @@ -132,17 +133,15 @@ uniform sampler2D sFramebuffer; uniform sampler2DArray sMiscmap; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_MISC #prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS -uniform sampler2DArray shadow_map; +uniform sampler2DArrayShadow shadow_map; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS out vec4 fragOut0; -#ifndef MODEL_SDR_FLAG_SHADOW_MAP out vec4 fragOut1; out vec4 fragOut2; out vec4 fragOut3; out vec4 fragOut4; -#endif vec3 FresnelLazarovEnv(vec3 specColor, vec3 view, vec3 normal, float gloss) { @@ -203,12 +202,6 @@ vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial, void main() { - #ifdef MODEL_SDR_FLAG_SHADOW_MAP - // need depth and depth squared for variance shadow maps - fragOut0 = vec4(vertIn.position.z, vertIn.position.z * vertIn.position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0); - - return; - #else vec3 eyeDir = vec3(normalize(-vertIn.position).xyz); vec2 texCoord = vertIn.texCoord.xy; @@ -337,7 +330,7 @@ void main() #prereplace IF_FLAG MODEL_SDR_FLAG_LIGHT float shadow = 1.0; #prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS - shadow = getShadowValue(shadow_map, -vertIn.position.z, vertIn.shadowPos.z, vertIn.shadowUV, fardist, middist, neardist, veryneardist); + shadow = getShadowValue(shadow_map, -vertIn.position.z, vertIn.shadowUV, cascade_distances, smoothness_factors, cascade_offset, cascade_count); #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS baseColor.rgb = CalculateLighting(normal, baseColor.rgb, specColor.rgb, glossData, fresnelFactor, shadow, aoFactors.x); #prereplace ELSE_FLAG //MODEL_SDR_FLAG_LIGHT @@ -435,6 +428,4 @@ void main() fragOut3 = vec4(specColor.rgb, fresnelFactor); fragOut4 = emissiveColor; #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DEFERRED - - #endif } \ No newline at end of file diff --git a/code/def_files/data/effects/main-g.sdr b/code/def_files/data/effects/main-g.sdr index 6904e113ed3..49cee6b0581 100644 --- a/code/def_files/data/effects/main-g.sdr +++ b/code/def_files/data/effects/main-g.sdr @@ -7,15 +7,6 @@ layout (triangles) in; // For every triangle line we generate 2 triangles which can be done with 4 vertices so in total we will need 12 vertices layout (triangle_strip, max_vertices = 12) out; -#elif defined(MODEL_SDR_FLAG_SHADOW_MAP) -layout (triangles) in; -layout (triangle_strip, max_vertices = 3) out; -#endif - -#ifdef MODEL_SDR_FLAG_SHADOW_MAP - #ifdef GL_ARB_gpu_shader5 -layout(invocations = 4) in; - #endif #endif #define MAX_LIGHTS 8 @@ -37,8 +28,6 @@ layout (std140) uniform modelData { mat4 viewMatrix; mat4 projMatrix; mat4 textureMatrix; - mat4 shadow_mv_matrix; - mat4 shadow_proj_matrix[4]; vec4 color; @@ -84,11 +73,6 @@ layout (std140) uniform modelData { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - int sGlowmapIndex; int sSpecmapIndex; @@ -108,19 +92,12 @@ in VertexOutput { float fogDist; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_FOG -#ifdef MODEL_SDR_FLAG_SHADOW_MAP - #if !defined(GL_ARB_gpu_shader5) - float instance; - #endif - float clipModel; -#else vec4 position; -#endif vec3 normal; vec4 texCoord; #prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS - vec4 shadowUV[4]; + vec4 shadowUV[NUM_SHADOW_CASCADES]; vec4 shadowPos; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS } vertIn[]; @@ -137,62 +114,12 @@ out VertexOutput { vec4 texCoord; #prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS - vec4 shadowUV[4]; + vec4 shadowUV[NUM_SHADOW_CASCADES]; vec4 shadowPos; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS } vertOut; -#ifdef MODEL_SDR_FLAG_SHADOW_MAP - void main(void) - { - #ifdef GL_ARB_gpu_shader5 - int instanceID = gl_InvocationID; - #else - int instanceID = int(vertIn[0].instance); - #endif - for(int vert = 0; vert < gl_in.length(); vert++) - { - #prereplace IF_FLAG MODEL_SDR_FLAG_TRANSFORM - if (vertIn[vert].clipModel > 0.9) { - // If the model was clipped in the vertex shader then we do not apply the shadow projection matrix here - gl_Position = gl_in[vert].gl_Position; - } else { - gl_Position = shadow_proj_matrix[instanceID] * gl_in[vert].gl_Position; - } - #prereplace ELSE_FLAG //MODEL_SDR_FLAG_TRANSFORM - gl_Position = shadow_proj_matrix[instanceID] * gl_in[vert].gl_Position; - #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TRANSFORM - - vertOut.position = gl_in[vert].gl_Position; - vertOut.normal = vertIn[vert].normal; - vertOut.texCoord = vertIn[vert].texCoord; - - gl_Layer = instanceID; - - #prereplace IF_FLAG MODEL_SDR_FLAG_NORMAL - vertOut.tangentMatrix = vertIn[vert].tangentMatrix; - #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_NORMAL - - #prereplace IF_FLAG MODEL_SDR_FLAG_FOG - vertOut.fogDist = vertIn[vert].fogDist; - #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG - - #prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS - vertOut.shadowUV[0] = vertIn[vert].shadowUV[0]; - vertOut.shadowUV[1] = vertIn[vert].shadowUV[1]; - vertOut.shadowUV[2] = vertIn[vert].shadowUV[2]; - vertOut.shadowUV[3] = vertIn[vert].shadowUV[3]; - vertOut.shadowPos = vertIn[vert].shadowPos; - #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS - - if (use_clip_plane) { - gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0]; - } - EmitVertex(); - } - EndPrimitive(); - } -#elif defined(MODEL_SDR_FLAG_THICK_OUTLINES) +#ifdef MODEL_SDR_FLAG_THICK_OUTLINES const vec2 pixelOffsetDir[4] = vec2[]( vec2(0.0, 1.0), vec2(1.0, 1.0), @@ -232,10 +159,9 @@ out VertexOutput { #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG #prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS - vertOut.shadowUV[0] = vertIn[vert].shadowUV[0]; - vertOut.shadowUV[1] = vertIn[vert].shadowUV[1]; - vertOut.shadowUV[2] = vertIn[vert].shadowUV[2]; - vertOut.shadowUV[3] = vertIn[vert].shadowUV[3]; + for (int s = 0; s < NUM_SHADOW_CASCADES; s++) { + vertOut.shadowUV[s] = vertIn[vert].shadowUV[s]; + } vertOut.shadowPos = vertIn[vert].shadowPos; #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS diff --git a/code/def_files/data/effects/main-v.sdr b/code/def_files/data/effects/main-v.sdr index c3aa9ad74c1..ee59cec19f8 100644 --- a/code/def_files/data/effects/main-v.sdr +++ b/code/def_files/data/effects/main-v.sdr @@ -32,8 +32,6 @@ layout (std140) uniform modelData { mat4 viewMatrix; mat4 projMatrix; mat4 textureMatrix; - mat4 shadow_mv_matrix; - mat4 shadow_proj_matrix[4]; vec4 color; @@ -79,11 +77,6 @@ layout (std140) uniform modelData { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - int sGlowmapIndex; int sSpecmapIndex; @@ -96,6 +89,16 @@ layout (std140) uniform modelData { int flags; }; +layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; + vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; +}; + #prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM uniform samplerBuffer transform_tex; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM @@ -107,21 +110,12 @@ out VertexOutput { float fogDist; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_FOG -#ifdef MODEL_SDR_FLAG_SHADOW_MAP - #if !defined(GL_ARB_gpu_shader5) - float instance; - #endif - // This flag is needed to let the geometry shader know that it doesn't need to apply the - // shadow projection to gl_Position - float clipModel; -#else vec4 position; -#endif vec3 normal; vec4 texCoord; #prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS - vec4 shadowUV[4]; + vec4 shadowUV[NUM_SHADOW_CASCADES]; vec4 shadowPos; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS } vertOut; @@ -162,25 +156,13 @@ void main() // Transform the normal into eye space and normalize the result. normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal); position = modelViewMatrix * orient * vertex; - #ifdef MODEL_SDR_FLAG_SHADOW_MAP - gl_Position = position; - #if !defined(GL_ARB_gpu_shader5) - #ifdef APPLE - vertOut.instance = float(gl_InstanceIDARB); - #else - vertOut.instance = float(gl_InstanceID); - #endif - #endif - #else gl_Position = projMatrix * position; - #endif #prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS vec4 shadowPos = shadow_mv_matrix * modelMatrix * orient * vertPosition; - vertOut.shadowPos = shadow_mv_matrix * modelMatrix * orient * vertPosition; - vertOut.shadowUV[0] = transformToShadowMap(shadow_proj_matrix[0], 0, shadowPos); - vertOut.shadowUV[1] = transformToShadowMap(shadow_proj_matrix[1], 1, shadowPos); - vertOut.shadowUV[2] = transformToShadowMap(shadow_proj_matrix[2], 2, shadowPos); - vertOut.shadowUV[3] = transformToShadowMap(shadow_proj_matrix[3], 3, shadowPos); + vertOut.shadowPos = shadowPos; + for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { + vertOut.shadowUV[i] = transformToShadowMap(shadow_proj_matrix[i], i, shadowPos); + } #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS // Setup stuff for normal maps and envmaps @@ -197,9 +179,6 @@ void main() // Clip this model by moving all vertices outside the clip volume gl_Position = vec4(vec3(-2.0), 1.0); } - #ifdef MODEL_SDR_FLAG_SHADOW_MAP - vertOut.clipModel = clipModel ? 1.0 : 0.0; - #endif #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TRANSFORM if(use_clip_plane) { @@ -208,12 +187,7 @@ void main() gl_ClipDistance[0] = 1.0; } - #ifndef MODEL_SDR_FLAG_SHADOW_MAP - vertOut.position = position; - vertOut.normal = normal; - vertOut.texCoord = texCoord; - #else - vertOut.normal = normal; - vertOut.texCoord = texCoord; - #endif + vertOut.position = position; + vertOut.normal = normal; + vertOut.texCoord = texCoord; } diff --git a/code/def_files/data/effects/shadow_map-f.sdr b/code/def_files/data/effects/shadow_map-f.sdr new file mode 100644 index 00000000000..fff405831fd --- /dev/null +++ b/code/def_files/data/effects/shadow_map-f.sdr @@ -0,0 +1,5 @@ +//? #version 150 + +void main() +{ +} diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr new file mode 100644 index 00000000000..0de37ea486c --- /dev/null +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -0,0 +1,51 @@ +//? #version 150 +#extension GL_ARB_gpu_shader5: enable + +layout (triangles) in; +layout (triangle_strip, max_vertices = 3) out; +#ifdef GL_ARB_gpu_shader5 +layout(invocations = NUM_SHADOW_CASCADES) in; +#endif + +layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; + vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; +}; + +in VertexOutput { +#if !defined(GL_ARB_gpu_shader5) + float instance; +#endif + float clipModel; + vec3 normal; +} vertIn[]; + +void main(void) +{ +#ifdef GL_ARB_gpu_shader5 + if (gl_InvocationID >= cascade_count) return; + int cascade_id = cascade_offset + gl_InvocationID; +#else + int cascade_id = cascade_offset + int(vertIn[0].instance); +#endif + + for(int vert = 0; vert < gl_in.length(); vert++) + { + if (vertIn[vert].clipModel > 0.9) { + gl_Position = gl_in[vert].gl_Position; + } else { + gl_Position = shadow_proj_matrix[cascade_id] * gl_in[vert].gl_Position; + } + + gl_Layer = cascade_id; + gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0]; + + EmitVertex(); + } + EndPrimitive(); +} diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr new file mode 100644 index 00000000000..3a804f0046d --- /dev/null +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -0,0 +1,101 @@ +//? #version 150 +#extension GL_ARB_gpu_shader5: enable + +#ifndef GEOMETRY_FALLBACK +#extension GL_ARB_shader_viewport_layer_array : enable +#endif + +in vec4 vertPosition; +in vec4 vertTexCoord; +in vec3 vertNormal; +in vec4 vertTangent; +in float vertModelID; + +layout (std140) uniform shadowMapData { + mat4 modelViewMatrix; + mat4 modelMatrix; + vec4 clip_equation; + bool use_clip_plane; + int buffer_matrix_offset; +}; + +layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; + vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; +}; + +uniform samplerBuffer transform_tex; + +#ifdef GEOMETRY_FALLBACK +out VertexOutput { +#if !defined(GL_ARB_gpu_shader5) + float instance; +#endif + float clipModel; + vec3 normal; +} vertOut; +#endif + +#define TEXELS_PER_MATRIX 4 + +void getModelTransform(inout mat4 transform, out bool invisible, int id, int matrix_offset) +{ + transform[0] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX); + transform[1] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX + 1); + transform[2] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX + 2); + transform[3] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX + 3); + invisible = transform[3].w >= 0.9; + transform[3].w = 1.0; +} + +void main() +{ + vec4 position; + vec3 normal; + mat4 orient = mat4(1.0); + bool clipModel = false; + + getModelTransform(orient, clipModel, int(vertModelID), buffer_matrix_offset); + + vec4 vertex = vertPosition; + + normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal); + position = modelViewMatrix * orient * vertex; + +#if !defined(GL_ARB_gpu_shader5) + #ifdef APPLE + vertOut.instance = float(gl_InstanceIDARB); + #else + vertOut.instance = float(gl_InstanceID); + #endif +#endif + + if (clipModel) { + gl_Position = vec4(vec3(-2.0), 1.0); + } + else { +#ifndef GEOMETRY_FALLBACK + int cascade_id = cascade_offset + gl_InstanceID; + position = shadow_proj_matrix[cascade_id] * position; +#endif + gl_Position = position; + } + + if (use_clip_plane) { + gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex); + } else { + gl_ClipDistance[0] = 1.0; + } + +#ifdef GEOMETRY_FALLBACK + vertOut.normal = normal; + vertOut.clipModel = clipModel ? 1.0 : 0.0; +#else + gl_Layer = cascade_offset + gl_InstanceID; +#endif +} diff --git a/code/def_files/data/effects/shadows.sdr b/code/def_files/data/effects/shadows.sdr index a765482f30c..a9437d87976 100644 --- a/code/def_files/data/effects/shadows.sdr +++ b/code/def_files/data/effects/shadows.sdr @@ -1,36 +1,26 @@ +#define SMOOTH_PCSS 1 -const float VARIANCE_SHADOW_SCALE = 1000000.0; -const float VARIANCE_SHADOW_SCALE_INV = 1.0/VARIANCE_SHADOW_SCALE; +#if SMOOTH_PCSS +#define SAMPLES_PCSS 16 +#else +#define SAMPLES_PCSS 1 +#endif -vec2 sampleShadowMap(sampler2DArray shadow_map, vec2 uv, vec2 offset_uv, int cascade, float shadowMapSizeInv) -{ - return texture(shadow_map, vec3(uv + offset_uv * shadowMapSizeInv, float(cascade))).xy; -} - -float computeShadowFactor(float shadowDepth, vec2 moments, float bias) -{ - float shadow = 1.0; - if((moments.x - bias) > shadowDepth) - { - // variance shadow mapping using Chebychev's Formula - float variance = moments.y * VARIANCE_SHADOW_SCALE - moments.x * moments.x; - float mD = moments.x - bias - shadowDepth; - shadow = variance / (variance + mD * mD); - shadow = clamp(shadow, 0.0, 1.0); - } - return shadow; -} - -float sampleNoPCF(sampler2DArray shadow_map, float shadowDepth, int cascade, vec4 shadowUV[4]) +vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos) { - return computeShadowFactor(shadowDepth, sampleShadowMap(shadow_map, shadowUV[cascade].xy, vec2(0.0, 0.0), cascade, 1.0/1024.0), 0.05); + vec4 shadow_proj; + shadow_proj = shadow_proj_matrix * pos; + shadow_proj += 1.0; + shadow_proj *= 0.5; + shadow_proj.w = shadow_proj.z; + shadow_proj.z = float(i); + return shadow_proj; } -float samplePoissonPCF(sampler2DArray shadow_map, float shadowDepth, int cascade, vec4 shadowUV[4], bool cockpit_shadow_bias) +float samplePoissonPCSS(sampler2DArrayShadow shadow_map, vec4 shadowUV, float maxUVOffset) { - if(cascade > 3 || cascade < 0) return 1.0; - - vec2 poissonDisc[16] = vec2[]( +#if SMOOTH_PCSS + vec2 poissonDisc[16] = vec2[]( vec2(-0.76275, -0.3432573), vec2(-0.5226235, -0.8277544), vec2(-0.3780261, 0.01528688), @@ -48,65 +38,43 @@ float samplePoissonPCF(sampler2DArray shadow_map, float shadowDepth, int cascade vec2(0.81772, -0.02475523), vec2(0.6890262, 0.5191521) ); +#else + vec2 poissonDisc[1] = vec2[]( + vec2(0.0,0.0) + ); +#endif - float maxUVOffset[4]; - maxUVOffset[0] = 1.0/300.0; - maxUVOffset[1] = 1.0/250.0; - maxUVOffset[2] = 1.0/200.0; - maxUVOffset[3] = 1.0/200.0; - - // default external shadows - // bias is a bit larger to only pick up larger geometry - // favors light over shadow to avoid flickering/spotty shadows - // compute Chebyshev per-sample to avoid halos from moment-averaging at occluder edges float visibility = 0.0f; - for (int i=0; i<16; i++) { - vec2 shadow_sample = sampleShadowMap(shadow_map, shadowUV[cascade].xy, poissonDisc[i], cascade, maxUVOffset[cascade]); - visibility += computeShadowFactor(shadowDepth, shadow_sample, cockpit_shadow_bias ? 0.01f : 0.1f); + for (int i=0; i 3 || cascade < 0) return 1.0; + int cascade = cascade_offset + cascade_count; + for (int i = 0; i < min(cascade_count, NUM_SHADOW_CASCADES); i++) { + cascade -= int(step(depth, cascade_distances[(cascade_offset + i) / 4][(cascade_offset + i) % 4])); + } + if (cascade >= cascade_offset + cascade_count || cascade < cascade_offset) return 1.0; - bool cockpit_shadow_bias; - if (fardist < 50.0f) { - // internal cockpit shadows - cockpit_shadow_bias = true; - } else { - // default external shadows - cockpit_shadow_bias = false; - } + int cascade_v = cascade / 4; + int cascade_i = cascade % 4; - float dist_threshold = (cascade_start_dist[cascade+1] - cascade_start_dist[cascade])*0.2; - if(cascade_start_dist[cascade+1] - dist_threshold > depth) - return samplePoissonPCF(shadow_map, shadowDepth, cascade, shadowUV, cockpit_shadow_bias); - return mix(samplePoissonPCF(shadow_map, shadowDepth, cascade, shadowUV, cockpit_shadow_bias), samplePoissonPCF(shadow_map, shadowDepth, cascade+1, shadowUV, cockpit_shadow_bias), - smoothstep(cascade_start_dist[cascade+1] - dist_threshold, cascade_start_dist[cascade+1], depth)); -} + float cascade_start = (cascade > cascade_offset) ? cascade_distances[(cascade - 1) / 4][(cascade - 1) % 4] : 0.0; + float cascade_end = cascade_distances[cascade_v][cascade_i]; + float dist_threshold = (cascade_end - cascade_start) * 0.2; -vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos) -{ - vec4 shadow_proj; - shadow_proj = shadow_proj_matrix * pos; - shadow_proj += 1.0; - shadow_proj *= 0.5; - shadow_proj.w = shadow_proj.z; - shadow_proj.z = float(i); - return shadow_proj; + if (cascade_end - dist_threshold > depth || cascade >= cascade_offset + cascade_count - 1) + return samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade_v][cascade_i]); + + return mix( + samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade_v][cascade_i]), + samplePoissonPCSS(shadow_map, shadowUV[cascade + 1], smoothness_factors[(cascade + 1) / 4][(cascade + 1) % 4]), + smoothstep(cascade_end - dist_threshold, cascade_end, depth)); } diff --git a/code/graphics/2d.cpp b/code/graphics/2d.cpp index 189227e8912..b055b2449de 100644 --- a/code/graphics/2d.cpp +++ b/code/graphics/2d.cpp @@ -82,6 +82,7 @@ gr_capability_def gr_capabilities[] = { gr_capability_def {gr_capability::CAPABILITY_BPTC, "BPTC Texture Compression"}, //This one had a different parse string already! GR_CAPABILITY_ENTRY(LARGE_SHADER), GR_CAPABILITY_ENTRY(INSTANCED_RENDERING), + GR_CAPABILITY_ENTRY(FAST_SHADOWS), }; const size_t gr_capabilities_num = sizeof(gr_capabilities) / sizeof(gr_capabilities[0]); diff --git a/code/graphics/2d.h b/code/graphics/2d.h index 53dbc1980e2..527ffcd5095 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -214,6 +214,8 @@ enum shader_type { SDR_TYPE_IRRADIANCE_MAP_GEN, + SDR_TYPE_SHADOW_MAP_GEN, + NUM_SHADER_TYPES }; @@ -241,6 +243,8 @@ enum shader_type { #define SDR_FLAG_ENV_MAP (1 << 0) +#define SDR_FLAG_SHADOW_FALLBACK (1 << 0) + enum class uniform_block_type { Lights = 0, @@ -252,6 +256,8 @@ enum class uniform_block_type { Matrices = 6, MovieData = 7, GenericData = 8, + ShadowMapData = 9, + ShadowCascadeParams = 10, NUM_BLOCK_TYPES }; @@ -339,7 +345,8 @@ enum class gr_capability { CAPABILITY_PERSISTENT_BUFFER_MAPPING, CAPABILITY_BPTC, CAPABILITY_LARGE_SHADER, - CAPABILITY_INSTANCED_RENDERING + CAPABILITY_INSTANCED_RENDERING, + CAPABILITY_FAST_SHADOWS, }; struct gr_capability_def { @@ -833,7 +840,7 @@ typedef struct screen { std::function gf_update_texture; std::function gf_get_bitmap_from_texture; - std::function gf_shadow_map_start; + std::function gf_shadow_map_start; std::function gf_shadow_map_end; std::function gf_start_decal_pass; @@ -843,6 +850,9 @@ typedef struct screen { std::function< void(model_material* material_info, indexed_vertex_source* vert_source, vertex_buffer* bufferp, size_t texi)> gf_render_model; + std::function + gf_render_shadow_draw; std::function&& graphicsOps) mprintf(( "\n" )); mprintf(("Extensions: \n")); mprintf((" Geo shader support : %s\n", GLAD_GL_ARB_gpu_shader5 ? NOX("YES") : NOX("NO"))); + mprintf((" Layered viewport support : %s\n", GLAD_GL_ARB_shader_viewport_layer_array ? NOX("YES") : NOX("NO"))); mprintf((" S3TC texture support : %s\n", GLAD_GL_EXT_texture_compression_s3tc ? NOX("YES") : NOX("NO"))); mprintf((" BPTC texture support : %s\n", GLAD_GL_ARB_texture_compression_bptc ? NOX("YES") : NOX("NO"))); @@ -1544,6 +1546,7 @@ bool gr_opengl_is_capable(gr_capability capability) case gr_capability::CAPABILITY_DEFERRED_LIGHTING: return !Cmdline_no_fbo && light_deferred_enabled(); case gr_capability::CAPABILITY_SHADOWS: + return !Cmdline_no_geo_sdr_effects || (GLAD_GL_ARB_vertex_attrib_binding && GLAD_GL_ARB_shader_viewport_layer_array && GL_ARB_gpu_shader5); case gr_capability::CAPABILITY_THICK_OUTLINE: return !Cmdline_no_geo_sdr_effects; case gr_capability::CAPABILITY_BATCHED_SUBMODELS: @@ -1560,6 +1563,8 @@ bool gr_opengl_is_capable(gr_capability capability) return !Cmdline_no_large_shaders; case gr_capability::CAPABILITY_INSTANCED_RENDERING: return GLAD_GL_ARB_vertex_attrib_binding; + case gr_capability::CAPABILITY_FAST_SHADOWS: + return GLAD_GL_ARB_vertex_attrib_binding && GLAD_GL_ARB_shader_viewport_layer_array && GL_ARB_gpu_shader5; } diff --git a/code/graphics/opengl/gropengldeferred.cpp b/code/graphics/opengl/gropengldeferred.cpp index 52f733b3bdc..1fd22204de8 100644 --- a/code/graphics/opengl/gropengldeferred.cpp +++ b/code/graphics/opengl/gropengldeferred.cpp @@ -11,6 +11,7 @@ #include "graphics/2d.h" #include "graphics/light.h" #include "graphics/matrix.h" +#include "graphics/shadows.h" #include "graphics/util/UniformAligner.h" #include "graphics/util/UniformBuffer.h" #include "graphics/util/uniform_structs.h" @@ -20,6 +21,7 @@ #include "mission/missionparse.h" #include "nebula/neb.h" #include "nebula/volumetrics.h" +#include "mod_table/mod_table.h" #include "render/3d.h" #include "tracing/tracing.h" #ifdef USE_OPENGL_ES @@ -250,7 +252,7 @@ void gr_opengl_deferred_lighting_finish() GL_state.Texture.Enable(2, GL_TEXTURE_2D, Scene_position_texture); GL_state.Texture.Enable(3, GL_TEXTURE_2D, Scene_specular_texture); if (Shadow_quality != ShadowQuality::Disabled) { - GL_state.Texture.Enable(4, GL_TEXTURE_2D_ARRAY, Shadow_map_texture); + GL_state.Texture.Enable(4, GL_TEXTURE_2D_ARRAY, Shadow_map_depth_texture); } if (ENVMAP > 0) { @@ -313,17 +315,10 @@ void gr_opengl_deferred_lighting_finish() auto header = light_uniform_aligner.getHeader(); if (Shadow_quality != ShadowQuality::Disabled) { - // Avoid this overhead when we are not going to use these values - header->shadow_mv_matrix = Shadow_view_matrix_light; - for (size_t i = 0; i < MAX_SHADOW_CASCADES; ++i) { - header->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; - } - header->veryneardist = Shadow_cascade_distances[0]; - header->neardist = Shadow_cascade_distances[1]; - header->middist = Shadow_cascade_distances[2]; - header->fardist = Shadow_cascade_distances[3]; - vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render); + int offset = (Lighting_mode == lighting_mode::COCKPIT) ? 0 : Num_cockpit_shadow_cascades; + int count = (Lighting_mode == lighting_mode::COCKPIT) ? Num_cockpit_shadow_cascades : Num_shadow_cascades; + shadow_cascade_params_bind(offset, count); } header->invScreenWidth = 1.0f / gr_screen.max_w; diff --git a/code/graphics/opengl/gropengldraw.h b/code/graphics/opengl/gropengldraw.h index d60f857a657..f85da1b1ca9 100644 --- a/code/graphics/opengl/gropengldraw.h +++ b/code/graphics/opengl/gropengldraw.h @@ -43,7 +43,7 @@ void gr_opengl_update_distortion(); void gr_opengl_sphere(material *material_def, float rad); -void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient, vec3d* eye_pos); +void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient, vec3d* eye_pos, bool first_pass); void gr_opengl_shadow_map_end(); void gr_opengl_render_shield_impact(shield_material* material_info, diff --git a/code/graphics/opengl/gropenglpostprocessing.cpp b/code/graphics/opengl/gropenglpostprocessing.cpp index ea9b5c4fa80..1982cedd743 100644 --- a/code/graphics/opengl/gropenglpostprocessing.cpp +++ b/code/graphics/opengl/gropenglpostprocessing.cpp @@ -630,9 +630,8 @@ void gr_opengl_post_process_end() // GL_state.Texture.SetTarget(GL_TEXTURE_2D); GL_state.Texture.SetTarget(GL_TEXTURE_2D_ARRAY); // GL_state.Texture.Enable(Shadow_map_depth_texture); - extern GLuint Shadow_map_texture; extern GLuint Post_shadow_texture_id; - GL_state.Texture.Enable(Shadow_map_texture); + GL_state.Texture.Enable(Shadow_map_depth_texture); glUniform1iARB( opengl_shader_get_uniform("shadow_map"), 0); glUniform1iARB( opengl_shader_get_uniform("index"), 0); //opengl_draw_textured_quad(-1.0f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, Scene_texture_u_scale, Scene_texture_u_scale); diff --git a/code/graphics/opengl/gropenglshader.cpp b/code/graphics/opengl/gropenglshader.cpp index 6baeb10bb6d..2c848c989db 100644 --- a/code/graphics/opengl/gropenglshader.cpp +++ b/code/graphics/opengl/gropenglshader.cpp @@ -71,6 +71,7 @@ struct opengl_uniform_block_binding { opengl_uniform_block_binding GL_uniform_blocks[] = { {uniform_block_type::Lights, "lightData"}, {uniform_block_type::ModelData, "modelData"}, + {uniform_block_type::ShadowMapData, "shadowMapData"}, {uniform_block_type::NanoVGData, "NanoVGUniformData"}, {uniform_block_type::DecalInfo, "decalInfoData"}, {uniform_block_type::DecalGlobals, "decalGlobalData"}, @@ -78,6 +79,7 @@ opengl_uniform_block_binding GL_uniform_blocks[] = { {uniform_block_type::Matrices, "matrixData"}, {uniform_block_type::MovieData, "movieData"}, {uniform_block_type::GenericData, "genericData"}, + {uniform_block_type::ShadowCascadeParams, "shadowCascadeParams"}, }; /** @@ -178,6 +180,10 @@ static opengl_shader_type_t GL_shader_types[] = { { SDR_TYPE_IRRADIANCE_MAP_GEN, "post-v.sdr", "irrmap-f.sdr", nullptr, { opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Irradiance Map Generation", false }, + + { SDR_TYPE_SHADOW_MAP_GEN, "shadow_map-v.sdr", "shadow_map-f.sdr", "shadow_map-g.sdr", + { opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::NORMAL, opengl_vert_attrib::TANGENT, opengl_vert_attrib::MODEL_ID }, "Shadow Map Generation", false }, + }; // clang-format on @@ -215,7 +221,10 @@ static opengl_shader_variant_t GL_shader_variants[] = { {SDR_TYPE_COPY_WORLD, false, SDR_FLAG_COPY_FROM_ARRAY, "COPY_ARRAY", {}, "Expects to copy from an array texture"}, - {SDR_TYPE_POST_PROCESS_TONEMAPPING, false, SDR_FLAG_TONEMAPPING_LINEAR_OUT, "LINEAR_OUT", {}, "Will make the tonemapper output in linear color space and not in sRGB"} + {SDR_TYPE_POST_PROCESS_TONEMAPPING, false, SDR_FLAG_TONEMAPPING_LINEAR_OUT, "LINEAR_OUT", {}, "Will make the tonemapper output in linear color space and not in sRGB"}, + + {SDR_TYPE_SHADOW_MAP_GEN, true, SDR_FLAG_SHADOW_FALLBACK, "GEOMETRY_FALLBACK", {}, "Will make the shadow map generation shader fall back on geometry shaders if multi viewports are not avilable"} + }; static const int GL_num_shader_variants = sizeof(GL_shader_variants) / sizeof(opengl_shader_variant_t); @@ -509,8 +518,8 @@ static SCP_string handle_predefines(const char* filename, const SCP_string& orig SCP_stringstream output; SCP_unordered_map defines; - //In any shader, define GLOBAL_FAR_Z output << "#define GLOBAL_FAR_Z " << std::fixed << std::setprecision(2) << Max_draw_distance << std::defaultfloat << '\n'; + output << "#define NUM_SHADOW_CASCADES " << (Num_shadow_cascades + Num_cockpit_shadow_cascades) << '\n'; const char* PREDEFINE_STRING = "#predefine"; const char* PREREPLACE_STRING = "#prereplace"; diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 9ed9f6d6a8c..d412894ecb6 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -32,6 +32,7 @@ #include "graphics/matrix.h" #include "graphics/shadows.h" #include "graphics/util/uniform_structs.h" +#include "mod_table/mod_table.h" #include "lighting/lighting.h" #include "math/vecmat.h" #include "options/Option.h" @@ -63,7 +64,6 @@ size_t GL_vertex_data_in = 0; GLint GL_max_elements_vertices = 4096; GLint GL_max_elements_indices = 4096; -GLuint Shadow_map_texture = 0; GLuint Shadow_map_depth_texture = 0; GLuint shadow_fbo = 0; int Shadow_texture_size = 0; @@ -392,6 +392,11 @@ void gr_opengl_bind_uniform_buffer(uniform_block_type bind_point, size_t offset, glBindBufferRange(GL_UNIFORM_BUFFER, static_cast(bind_point), buffer_handle, static_cast(offset), static_cast(size)); + + // glBindBufferRange also modifies the generic GL_UNIFORM_BUFFER binding point. + // Sync the state tracker to prevent stale-state skips in subsequent + // opengl_bind_buffer_object calls (which rely on BindUniformBuffer's cache). + GL_state.Array.BindUniformBuffer(buffer_handle); } gr_buffer_handle opengl_create_texture_buffer_object() @@ -451,9 +456,9 @@ void opengl_destroy_all_buffers() GL_vertex_buffers_in_use = 0; } -static bool opengl_init_shadow_framebuffer(int size, GLenum color_format) +static bool opengl_init_shadow_framebuffer(int size) { - mprintf(("Trying to create %dx%d %d-bit shadow framebuffer\n", size, size, color_format == GL_RGBA32F ? 32 : 16)); + mprintf(("Trying to create %dx%d 24-bit shadow framebuffer\n", size, size)); glGenFramebuffers(1, &shadow_fbo); GL_state.BindFrameBuffer(shadow_fbo); @@ -469,25 +474,11 @@ static bool opengl_init_shadow_framebuffer(int size, GLenum color_format) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32, size, size, 4, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, size, size, Num_shadow_cascades + Num_cockpit_shadow_cascades, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, Shadow_map_depth_texture, 0); - glGenTextures(1, &Shadow_map_texture); - - GL_state.Texture.SetActiveUnit(0); - GL_state.Texture.SetTarget(GL_TEXTURE_2D_ARRAY); - GL_state.Texture.Enable(Shadow_map_texture); - - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, color_format, size, size, 4, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); - - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, Shadow_map_texture, 0); - auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GL_state.BindFrameBuffer(0); @@ -499,11 +490,9 @@ static bool opengl_init_shadow_framebuffer(int size, GLenum color_format) } // Clean up resources - glDeleteTextures(1, &Shadow_map_texture); glDeleteTextures(1, &Shadow_map_depth_texture); glDeleteFramebuffers(1, &shadow_fbo); - Shadow_map_texture = 0; Shadow_map_depth_texture = 0; shadow_fbo = 0; @@ -551,11 +540,13 @@ void opengl_tnl_init() break; } - if (!opengl_init_shadow_framebuffer(size, GL_RGBA32F)) { - if (!opengl_init_shadow_framebuffer(size, GL_RGBA16F)) { - mprintf(("Failed to create either 32 or 16-bit color shadow framebuffer. Disabling shadow support.\n")); - Shadow_quality = ShadowQuality::Disabled; - } + if (!opengl_init_shadow_framebuffer(size)) { + mprintf(("Failed to create either shadow framebuffer. Disabling shadow support.\n")); + Shadow_quality = ShadowQuality::Disabled; + } + + if (Shadow_quality != ShadowQuality::Disabled) { + shadow_cascade_params_init(); } } @@ -571,16 +562,13 @@ void opengl_tnl_shutdown() gr_opengl_deferred_shutdown(); + shadow_cascade_params_shutdown(); + if ( Shadow_map_depth_texture ) { glDeleteTextures(1, &Shadow_map_depth_texture); Shadow_map_depth_texture = 0; } - if ( Shadow_map_texture ) { - glDeleteTextures(1, &Shadow_map_texture); - Shadow_map_texture = 0; - } - opengl_destroy_all_buffers(); } @@ -603,33 +591,22 @@ void opengl_render_model_program(model_material* material_info, indexed_vertex_s opengl_buffer_get_id(GL_ARRAY_BUFFER, vert_source->Vbuffer_handle), opengl_buffer_get_id(GL_ELEMENT_ARRAY_BUFFER, vert_source->Ibuffer_handle)); - // If GL_ARB_gpu_shader5 is supprted then the instancing is handled by the geometry shader - if (!GLAD_GL_ARB_gpu_shader5 && Rendering_to_shadow_map) { - glDrawElementsInstancedBaseVertex(GL_TRIANGLES, - (GLsizei)datap->n_verts, - element_type, - ibuffer + datap->index_offset, - 4, - (GLint) (vert_source->Base_vertex_offset + bufferp->vertex_num_offset)); + if (Cmdline_drawelements) { + glDrawElementsBaseVertex(GL_TRIANGLES, + (GLsizei) datap->n_verts, + element_type, + ibuffer + datap->index_offset, + (GLint) (vert_source->Base_vertex_offset + bufferp->vertex_num_offset)); } else { - if (Cmdline_drawelements) { - glDrawElementsBaseVertex(GL_TRIANGLES, - (GLsizei) datap->n_verts, - element_type, - ibuffer + datap->index_offset, - (GLint) (vert_source->Base_vertex_offset + bufferp->vertex_num_offset)); - } else { - glDrawRangeElementsBaseVertex(GL_TRIANGLES, - datap->i_first, - datap->i_last, - (GLsizei) datap->n_verts, - element_type, - ibuffer + datap->index_offset, - (GLint) (vert_source->Base_vertex_offset + bufferp->vertex_num_offset)); - } + glDrawRangeElementsBaseVertex(GL_TRIANGLES, + datap->i_first, + datap->i_last, + (GLsizei) datap->n_verts, + element_type, + ibuffer + datap->index_offset, + (GLint) (vert_source->Base_vertex_offset + bufferp->vertex_num_offset)); } - GL_state.Texture.SetShaderMode(GL_FALSE); } @@ -646,6 +623,65 @@ void gr_opengl_render_model(model_material* material_info, indexed_vertex_source GL_CHECK_FOR_ERRORS("end of render_buffer()"); } +void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset, size_t ubo_size, + vertex_buffer* buffer, indexed_vertex_source* vert_src, size_t texi) +{ + auto* datap = &buffer->tex_buf[texi]; + if (datap->n_verts == 0) { + return; + } + + int shader_handle = gr_opengl_maybe_create_shader(SDR_TYPE_SHADOW_MAP_GEN, gr_is_capable(gr_capability::CAPABILITY_FAST_SHADOWS) ? 0 : SDR_FLAG_SHADOW_FALLBACK); + opengl_shader_set_current(shader_handle); + + GL_state.Texture.SetShaderMode(GL_TRUE); + + gr_bind_uniform_buffer(uniform_block_type::ShadowMapData, ubo_offset, ubo_size, ubo_handle); + + // Bind the transform texture buffer so the vertex shader can read model transforms + Current_shader->program->Uniforms.setTextureUniform("transform_tex", 10); + GL_state.Texture.Enable(10, GL_TEXTURE_BUFFER, opengl_get_transform_buffer_texture()); + + GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE); + gr_zbuffer_set(ZBUFFER_TYPE_FULL); + gr_set_cull(1); + gr_zbias(-1024); + gr_set_fill_mode(GR_FILL_MODE_SOLID); + GL_state.ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + GL_state.FrontFaceValue(gr_screen.rendering_to_texture != -1 ? GL_CCW : GL_CW); + + // Enable clip distance; actual clipping is gated by the use_clip_plane uniform in the shader + GL_state.ClipDistance(0, true); + + opengl_bind_vertex_layout(buffer->layout, + opengl_buffer_get_id(GL_ARRAY_BUFFER, vert_src->Vbuffer_handle), + opengl_buffer_get_id(GL_ELEMENT_ARRAY_BUFFER, vert_src->Ibuffer_handle)); + + auto ibuffer = reinterpret_cast(vert_src->Index_offset); + GLenum element_type = (datap->flags & VB_FLAG_LARGE_INDEX) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + auto base_vertex = static_cast(vert_src->Base_vertex_offset + buffer->vertex_num_offset); + + //Funnily enough, both the modern shadow rendering (using shader_viewport_layer_array), and the super-old fallback without shader5 use the same instanced draw call + if (gr_is_capable(gr_capability::CAPABILITY_FAST_SHADOWS) || !GLAD_GL_ARB_gpu_shader5) { + glDrawElementsInstancedBaseVertex(GL_TRIANGLES, + (GLsizei)datap->n_verts, + element_type, + ibuffer + datap->index_offset, + Shadow_cascade_count, + base_vertex); + } + else { + glDrawElementsBaseVertex(GL_TRIANGLES, + (GLsizei)datap->n_verts, + element_type, + ibuffer + datap->index_offset, + base_vertex); + } + + GL_state.Texture.SetShaderMode(GL_FALSE); +} + extern GLuint Framebuffer_fallback_texture_id; extern GLuint Scene_depth_texture; extern GLuint Scene_position_texture; @@ -682,28 +718,28 @@ bool Glowpoint_override_save; extern bool gr_htl_projection_matrix_set; -void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient, vec3d* eye_pos) +void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient, vec3d* eye_pos, bool first_pass) { if (Shadow_quality == ShadowQuality::Disabled) return; - GL_state.PushFramebufferState(); - GL_state.BindFrameBuffer(shadow_fbo); + if (first_pass) { + GL_state.PushFramebufferState(); + GL_state.BindFrameBuffer(shadow_fbo); - //glDrawBuffer(GL_COLOR_ATTACHMENT0); - GLenum buffers[] = { GL_COLOR_ATTACHMENT0}; - glDrawBuffers(1, buffers); + glDrawBuffers(0, nullptr); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_CLAMP); + glEnable(GL_DEPTH_CLAMP); - Rendering_to_shadow_map = true; - Glowpoint_override_save = Glowpoint_override; - Glowpoint_override = true; + Glowpoint_override_save = Glowpoint_override; + Glowpoint_override = true; - gr_htl_projection_matrix_set = true; + gr_htl_projection_matrix_set = true; + } else { + gr_end_view_matrix(); + } gr_set_view_matrix(eye_pos, light_orient); @@ -714,14 +750,10 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light void gr_opengl_shadow_map_end() { - if(!Rendering_to_shadow_map) - return; + gr_end_view_matrix(); glDisable(GL_DEPTH_CLAMP); - gr_end_view_matrix(); - Rendering_to_shadow_map = false; - gr_zbuffer_set(ZBUFFER_TYPE_FULL); GL_state.PopFramebufferState(); @@ -955,7 +987,7 @@ void opengl_tnl_set_model_material(model_material *material_info) } if (material_info->is_shadow_receiving()) { - GL_state.Texture.Enable(8, GL_TEXTURE_2D_ARRAY, Shadow_map_texture); + GL_state.Texture.Enable(8, GL_TEXTURE_2D_ARRAY, Shadow_map_depth_texture); } if (material_info->get_animated_effect() >= 0) { diff --git a/code/graphics/opengl/gropengltnl.h b/code/graphics/opengl/gropengltnl.h index ea83618da35..a3be9c51bd8 100644 --- a/code/graphics/opengl/gropengltnl.h +++ b/code/graphics/opengl/gropengltnl.h @@ -33,7 +33,7 @@ extern float shadow_neardist; extern float shadow_middist; extern float shadow_fardist; -extern GLuint Shadow_map_texture; +extern GLuint Shadow_map_depth_texture; struct opengl_vertex_bind { vertex_format_data::vertex_format format; @@ -63,6 +63,9 @@ GLuint opengl_buffer_get_id(GLenum expected_type, gr_buffer_handle buffer_handle void gr_opengl_update_transform_buffer(void* data, size_t size); +void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset, size_t ubo_size, + vertex_buffer* buffer, indexed_vertex_source* vert_src, size_t texi); + void opengl_tnl_init(); void opengl_tnl_shutdown(); diff --git a/code/graphics/render_queue.h b/code/graphics/render_queue.h new file mode 100644 index 00000000000..9a464367f64 --- /dev/null +++ b/code/graphics/render_queue.h @@ -0,0 +1,155 @@ +#pragma once + +#include "globalincs/pstypes.h" +#include "graphics/2d.h" +#include "graphics/util/UniformBuffer.h" +#include "lighting/lighting.h" +#include "math/vecmat.h" + +class model_batch_buffer +{ + SCP_vector Submodel_matrices; + void* Mem_alloc; + size_t Mem_alloc_size; + + size_t Current_offset; + + void allocate_memory(); +public: + model_batch_buffer() : Mem_alloc(nullptr), Mem_alloc_size(0), Current_offset(0) {}; + + void reset(); + + size_t get_buffer_offset() const; + void set_num_models(int n_models); + void set_model_transform(const matrix4 &transform, int model_id); + + void submit_buffer_data(); + + void add_matrix(const matrix4 &mat); +}; + +template +class render_queue { +protected: + SCP_vector _elements; + SCP_vector _keys; + transform_stack _transforms; + scene_lights _lights; + graphics::util::UniformBuffer _dataBuffer; + vec3d _scale; + inline static model_batch_buffer _batchBuffer; // shared across instances so we don't need to do the malloc again + bool _initialized = false; + +public: + render_queue() + { + _scale.xyz.x = 1.0f; + _scale.xyz.y = 1.0f; + _scale.xyz.z = 1.0f; + } + + void reset() + { + _elements.clear(); + _keys.clear(); + _transforms.clear(); + _scale.xyz.x = 1.0f; + _scale.xyz.y = 1.0f; + _scale.xyz.z = 1.0f; + _initialized = false; + _batchBuffer.reset(); + } + + void push_transform(const vec3d* pos, const matrix* orient) + { + _transforms.push(pos, orient); + } + + void pop_transform() + { + _transforms.pop(); + } + + const matrix4& get_transform() const + { + return _transforms.get_transform(); + } + + void clear_transforms() + { + _transforms.clear(); + } + + void set_scale(const vec3d* s) + { + if (s == nullptr) { + _scale.xyz.x = 1.0f; + _scale.xyz.y = 1.0f; + _scale.xyz.z = 1.0f; + } else { + _scale = *s; + } + } + + void start_model_batch(int n_models) + { + _batchBuffer.set_num_models(n_models); + } + + void add_submodel_to_batch(int model_num) + { + matrix4 transform = _transforms.get_transform(); + + vm_vec_scale(&transform.vec.rvec, _scale.xyz.x); + vm_vec_scale(&transform.vec.uvec, _scale.xyz.y); + vm_vec_scale(&transform.vec.fvec, _scale.xyz.z); + + transform.a1d[15] = 0.0f; + + _batchBuffer.set_model_transform(transform, model_num); + } + + void init_render(bool sort = true) + { + if (sort) { + self().sort_draws(); + } + + _batchBuffer.submit_buffer_data(); + + self().build_uniform_buffer(); + + _initialized = true; + } + + void render_all() + { + Assertion(_initialized, "init_render must be called before any render_all call!"); + + _lights.resetLightState(); + + for (const int& _key : _keys) { + self().render_buffer(_elements[_key]); + } + + gr_alpha_mask_set(0, 1.0f); + } + + void push_element(DrawEntryT&& entry) + { + _elements.push_back(std::move(entry)); + _keys.push_back(static_cast(_elements.size() - 1)); + } + + void sort_draws() + { + const auto& d = self(); + std::sort(_keys.begin(), _keys.end(), + [&d](int a, int b) { return d.sort_draw_pair(a, b); }); + } + +private: + Derived& self() { return static_cast(*this); } + const Derived& self() const { return static_cast(*this); } +}; diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 3e86d2c709b..aba8424a337 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -8,7 +8,11 @@ */ #include "graphics/shadows.h" + +#include "graphics/2d.h" +#include "graphics/uniforms.h" #include "asteroid/asteroid.h" +#include "camera/camera.h" #include "cmdline/cmdline.h" #include "debris/debris.h" #include "graphics/matrix.h" @@ -17,18 +21,23 @@ #include "mod_table/mod_table.h" #include "model/model.h" #include "model/modelrender.h" +#include "object/objectdock.h" #include "options/Option.h" +#include "prop/prop.h" +#include "ship/ship.h" +#include "ship/shipfx.h" #include "render/3d.h" #include "tracing/tracing.h" +#include "util/uniform_structs.h" extern vec3d check_offsets[8]; matrix4 Shadow_view_matrix_light; matrix4 Shadow_view_matrix_render; -matrix4 Shadow_proj_matrix[MAX_SHADOW_CASCADES]; -float Shadow_cascade_distances[MAX_SHADOW_CASCADES]; +SCP_vector Shadow_proj_matrix; +SCP_vector Shadow_cascade_distances; -light_frustum_info Shadow_frustums[MAX_SHADOW_CASCADES]; +static SCP_vector Shadow_frustums; ShadowQuality Shadow_quality = ShadowQuality::Disabled; @@ -76,7 +85,7 @@ auto ShadowQualityOption = options::OptionBuilder("Graphics.Shado .parser(parse_shadow_quality_func) .finish(); -bool shadows_obj_in_frustum(object *objp, matrix *light_orient, vec3d *min, vec3d *max) +bool shadows_obj_in_frustum(const object *objp, const matrix *light_orient, const vec3d *min, const vec3d *max) { vec3d pos, pos_rot; @@ -372,7 +381,7 @@ void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *li vm_vec_add(&far_bottom_right, &up_scale, &right_scale); vm_vec_add2(&far_bottom_right, &forward_scale_far); - + vec3d frustum_pts[8]; // bring frustum points into light space @@ -424,7 +433,7 @@ void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *li shadows_construct_light_proj(shadow_data); } -matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float aspect, float veryneardist, float neardist, float middist, float fardist) +matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t cockpit_fov, float aspect, const SCP_vector& cascade_distances) { if(Static_light.empty()) return vmd_identity_matrix; @@ -437,22 +446,33 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float vm_vec_copy_normalize(&light_dir, &lp.vec); vm_vector_2_matrix_norm(&light_matrix, &light_dir, &eye_orient->vec.uvec, nullptr); - shadows_construct_light_frustum(&Shadow_frustums[0], &light_matrix, eye_orient, eye_pos, fov, aspect, 0.0f, veryneardist); - shadows_construct_light_frustum(&Shadow_frustums[1], &light_matrix, eye_orient, eye_pos, fov, aspect, veryneardist - (veryneardist - 0.0f)* 0.2f, neardist); - shadows_construct_light_frustum(&Shadow_frustums[2], &light_matrix, eye_orient, eye_pos, fov, aspect, neardist - (neardist - veryneardist) * 0.2f, middist); - shadows_construct_light_frustum(&Shadow_frustums[3], &light_matrix, eye_orient, eye_pos, fov, aspect, middist - (middist - neardist) * 0.2f, fardist); - - Shadow_cascade_distances[0] = veryneardist; - Shadow_cascade_distances[1] = neardist; - Shadow_cascade_distances[2] = middist; - Shadow_cascade_distances[3] = fardist; + const int num_cascades = Num_shadow_cascades + Num_cockpit_shadow_cascades; - Shadow_proj_matrix[0] = Shadow_frustums[0].proj_matrix; - Shadow_proj_matrix[1] = Shadow_frustums[1].proj_matrix; - Shadow_proj_matrix[2] = Shadow_frustums[2].proj_matrix; - Shadow_proj_matrix[3] = Shadow_frustums[3].proj_matrix; + Shadow_frustums.resize(num_cascades); + Shadow_cascade_distances.resize(num_cascades); + Shadow_proj_matrix.resize(num_cascades); - gr_shadow_map_start(&Shadow_view_matrix_light, &light_matrix, eye_pos); + bool render_cockpit_cascades = ship_render_player_has_closeup_visuals(); + + for (int i = 0; i < num_cascades; i++) { + float z_near; + if (i == 0 || (!render_cockpit_cascades && i == Num_cockpit_shadow_cascades)) { + z_near = 0.0f; + } else { + z_near = cascade_distances[i - 1] - (cascade_distances[i - 1] - (i >= 2 ? cascade_distances[i - 2] : 0.0f)) * 0.2f; + } + float z_far = cascade_distances[i]; + + shadows_construct_light_frustum(&Shadow_frustums[i], &light_matrix, eye_orient, nullptr, i < Num_cockpit_shadow_cascades ? cockpit_fov : fov, aspect, z_near, z_far); + Shadow_cascade_distances[i] = cascade_distances[i]; + Shadow_proj_matrix[i] = Shadow_frustums[i].proj_matrix; + } + + gr_shadow_map_start(&Shadow_view_matrix_light, &light_matrix, eye_pos, true); + if (render_cockpit_cascades) + shadow_cascade_params_bind(0, num_cascades); + else + shadow_cascade_params_bind(Num_cockpit_shadow_cascades, Num_shadow_cascades); return light_matrix; } @@ -462,7 +482,140 @@ void shadows_end_render() gr_shadow_map_end(); } -void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) +static bool shadow_obj_clip_plane(const object* objp, shadow_render_list::clip_plane_info* clip) +{ + if (objp->type != OBJ_SHIP) + return false; + + ship* shipp = &Ships[objp->instance]; + + if (shipp->is_arriving(ship::warpstage::BOTH, true)) { + model_render_params dummy; + if (shipp->warpin_effect->warpShipClip(&dummy)) { + clip->normal = dummy.get_clip_plane_normal(); + clip->position = dummy.get_clip_plane_pos(); + return true; + } + } + if (shipp->flags[Ship::Ship_Flags::Depart_warp]) { + model_render_params dummy; + if (shipp->warpout_effect->warpShipClip(&dummy)) { + clip->normal = dummy.get_clip_plane_normal(); + clip->position = dummy.get_clip_plane_pos(); + return true; + } + } + + dock_function_info dfi; + dock_evaluate_all_docked_objects(const_cast(objp), &dfi, + [](object* docked, dock_function_info* info) { + if (docked->type != OBJ_SHIP) return; + auto* dship = &Ships[docked->instance]; + if (dship->is_arriving(ship::warpstage::BOTH, true) || dship->flags[Ship::Ship_Flags::Depart_warp]) { + info->maintained_variables.bool_value = true; + info->maintained_variables.objp_value = docked; + } + }); + + if (dfi.maintained_variables.bool_value) { + auto* dship = &Ships[dfi.maintained_variables.objp_value->instance]; + WarpEffect* warp_effect = dship->is_arriving(ship::warpstage::BOTH, true) + ? dship->warpin_effect : dship->warpout_effect; + model_render_params dummy; + if (warp_effect->warpShipClip(&dummy)) { + clip->normal = dummy.get_clip_plane_normal(); + clip->position = dummy.get_clip_plane_pos(); + return true; + } + } + + return false; +} + +static void render_viewer_shadow(object* objp, const matrix* light_matrix, + const vec3d* cam_offset, const matrix* rot_offset) +{ + if (objp == nullptr || objp->type != OBJ_SHIP || objp->instance < 0) + return; + + ship* shipp = &Ships[objp->instance]; + ship_info* sip = &Ship_info[shipp->ship_info_index]; + + vec3d eye_pos_local; + matrix eye_orient; + object_get_eye(&eye_pos_local, &eye_orient, objp, true, true, false); + if (cam_offset != nullptr) { + vec3d offset_local; + vm_vec_unrotate(&offset_local, cam_offset, &eye_orient); + (void)offset_local; + } + if (rot_offset != nullptr) { + eye_orient = *rot_offset * eye_orient; + } + + vec3d eye_offset; + vm_vec_copy_scale(&eye_offset, &eye_pos_local, -1.0f); + if (!Disable_cockpit_sway) + eye_offset += sip->cockpit_sway_val * objp->phys_info.acceleration; + + vec3d view_pos_local; + vm_vec_rotate(&view_pos_local, &eye_pos_local, &objp->orient); + + if (Show_ship_casts_shadow) { + matrix4 dummy_view; + bool casts_shadow_on_cockpit = ship_render_player_ship_casts_shadow_on_cockpit(); + + gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, false); + if (casts_shadow_on_cockpit) + shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades + Num_shadow_cascades); + else + shadow_cascade_params_bind(Num_cockpit_shadow_cascades, Num_shadow_cascades); + + model_clear_instance(sip->model_num); + polymodel_instance* pmi = nullptr; + if (shipp->model_instance_num >= 0) { + pmi = model_get_instance(shipp->model_instance_num); + } + auto pm = model_get(sip->model_num); + + shadow_render_list viewer_list; + shadow_render_list::add_model_draws(&viewer_list, pm, pmi, OBJ_INDEX(objp), + &eye_offset, &objp->orient, nullptr, 0, &view_pos_local); + viewer_list.init_render(false); + viewer_list.render_all(); + } + + const bool renderCockpitModel = (Viewer_mode != VM_TOPDOWN) && sip->cockpit_model_num >= 0 && !Disable_cockpits; + + if (renderCockpitModel && !Shadow_disable_overrides.disable_cockpit) { + matrix4 dummy_view; + gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, false); + shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades); + + vec3d cockpit_offset = sip->cockpit_offset; + vm_vec_unrotate(&cockpit_offset, &cockpit_offset, &objp->orient); + if (!Disable_cockpit_sway) + cockpit_offset += sip->cockpit_sway_val * objp->phys_info.acceleration; + + model_clear_instance(sip->cockpit_model_num); + polymodel_instance* cockpit_pmi = nullptr; + if (shipp->cockpit_model_instance >= 0) + cockpit_pmi = model_get_instance(shipp->cockpit_model_instance); + auto cockpit_pm = model_get(sip->cockpit_model_num); + + vec3d cockpit_view_local = view_pos_local; + vm_vec_sub2(&cockpit_view_local, &sip->cockpit_offset); + + shadow_render_list cockpit_list; + shadow_render_list::add_model_draws(&cockpit_list, cockpit_pm, cockpit_pmi, OBJ_INDEX(objp), + &cockpit_offset, &objp->orient, nullptr, 0, &cockpit_view_local); + cockpit_list.init_render(false); + cockpit_list.render_all(); + } +} + +void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, + const vec3d* cam_offset, const matrix* rot_offset, const fov_t* fov_override) { if (gr_screen.mode == GR_STUB) { return; @@ -479,81 +632,136 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) return; } - //shadows_debug_show_frustum(&Player_obj->orient, &Player_obj->pos, fov, gr_screen.clip_aspect, Min_draw_distance, 3000.0f); - Shadow_view_matrix_render = gr_view_matrix; gr_end_proj_matrix(); gr_end_view_matrix(); - // the default cascade distances are a result of some arbitrary tuning to give a good balance of quality and banding. - // maybe we could use a more programmatic algorithim? - matrix light_matrix = shadows_start_render(eye_orient, eye_pos, fov, gr_screen.clip_aspect, std::get<0>(Shadow_distances), std::get<1>(Shadow_distances), std::get<2>(Shadow_distances), std::get<3>(Shadow_distances)); + fov_t cockpit_fov; + if (fov_override) + cockpit_fov = *fov_override; + else if (Sexp_fov > 0.0f) + cockpit_fov = Sexp_fov; + else + cockpit_fov = COCKPIT_ZOOM_DEFAULT; + cockpit_fov = cockpit_fov * PROJ_FOV_FACTOR; + + matrix light_matrix = shadows_start_render(eye_orient, eye_pos, fov, cockpit_fov, gr_screen.clip_aspect, Shadow_distances); + + shadow_render_list shadow_list; - model_draw_list scene; object *objp = Objects; for ( int i = 0; i <= Highest_object_index; i++, objp++ ) { - bool cull = true; + if ( objp->flags[Object::Object_Flags::Should_be_dead] ) + continue; - for ( int j = 0; j < MAX_SHADOW_CASCADES; ++j ) { - if ( shadows_obj_in_frustum(objp, &light_matrix, &Shadow_frustums[j].min, &Shadow_frustums[j].max) ) { + bool cull = true; + for (const auto& Shadow_frustum : Shadow_frustums) { + if ( shadows_obj_in_frustum(objp, &light_matrix, &Shadow_frustum.min, &Shadow_frustum.max) ) { cull = false; break; } } + if ( cull ) continue; - if ( cull ) { - continue; - } + switch (objp->type) { + case OBJ_SHIP: { + if (objp == Viewer_obj) { + continue; + } - switch(objp->type) - { - case OBJ_RAW_POF: - case OBJ_PROP: - case OBJ_SHIP: - { - obj_queue_render(objp, &scene); + ship* shipp = &Ships[objp->instance]; + + if (shipp->large_ship_blowup_index >= 0) { + shipfx_shadow_render_blowup(&shadow_list, shipp); + continue; + } + + model_clear_instance(Ship_info[shipp->ship_info_index].model_num); + + shadow_render_list::clip_plane_info clip; + bool has_clip = shadow_obj_clip_plane(objp, &clip); + + auto pm = model_get(Ship_info[shipp->ship_info_index].model_num); + polymodel_instance* pmi = nullptr; + if (shipp->model_instance_num >= 0) { + pmi = model_get_instance(shipp->model_instance_num); } + + vec3d eye_rel, view_pos_local; + vm_vec_sub(&eye_rel, eye_pos, &objp->pos); + vm_vec_rotate(&view_pos_local, &eye_rel, &objp->orient); + + shadow_render_list::add_model_draws(&shadow_list, pm, pmi, OBJ_INDEX(objp), &objp->pos, &objp->orient, has_clip ? &clip : nullptr, -1, &view_pos_local); break; - case OBJ_ASTEROID: - { - model_render_params render_info; - - render_info.set_object_number(OBJ_INDEX(objp)); - render_info.set_flags(MR_IS_ASTEROID | MR_NO_TEXTURING | MR_NO_LIGHTING); - - model_clear_instance( Asteroid_info[Asteroids[objp->instance].asteroid_type].subtypes[Asteroids[objp->instance].asteroid_subtype].model_number); - model_render_queue(&render_info, &scene, Asteroid_info[Asteroids[objp->instance].asteroid_type].subtypes[Asteroids[objp->instance].asteroid_subtype].model_number, &objp->orient, &objp->pos); + } + + case OBJ_RAW_POF: + case OBJ_PROP: { + int model_num = object_get_model_num(objp); + auto pm = model_get(model_num); + model_clear_instance(model_num); + + polymodel_instance* pmi = nullptr; + int instance_num = object_get_model_instance_num(objp); + if (instance_num >= 0) { + pmi = model_get_instance(instance_num); } + + vec3d eye_rel, view_pos_local; + vm_vec_sub(&eye_rel, eye_pos, &objp->pos); + vm_vec_rotate(&view_pos_local, &eye_rel, &objp->orient); + + shadow_render_list::add_model_draws(&shadow_list, pm, pmi, OBJ_INDEX(objp), &objp->pos, &objp->orient, nullptr, -1, &view_pos_local); break; + } - case OBJ_DEBRIS: - { - debris *db; - db = &Debris[objp->instance]; + case OBJ_ASTEROID: { + int num = objp->instance; + auto* ast = &Asteroids[num]; + int model_num = Asteroid_info[ast->asteroid_type].subtypes[ast->asteroid_subtype].model_number; + model_clear_instance(model_num); + auto pm = model_get(model_num); - if ( !(db->flags[Debris_Flags::Used])){ - continue; - } - - auto pm = model_get(db->model_num); - auto pmi = db->model_instance_num < 0 ? nullptr : model_get_instance(db->model_instance_num); + vec3d eye_rel, view_pos_local; + vm_vec_sub(&eye_rel, eye_pos, &objp->pos); + vm_vec_rotate(&view_pos_local, &eye_rel, &objp->orient); - objp = &Objects[db->objnum]; + shadow_render_list::add_model_draws(&shadow_list, pm, nullptr, OBJ_INDEX(objp), &objp->pos, &objp->orient, nullptr, -1, &view_pos_local); + break; + } - model_render_params render_info; + case OBJ_DEBRIS: { + debris* db = &Debris[objp->instance]; + if ( !(db->flags[Debris_Flags::Used]) ) continue; - render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING); + auto pm = model_get(db->model_num); + object* debris_obj = &Objects[db->objnum]; - submodel_render_queue(&render_info, &scene, pm, pmi, db->submodel_num, &objp->orient, &objp->pos); + polymodel_instance* pmi = nullptr; + if (db->model_instance_num >= 0) { + pmi = model_get_instance(db->model_instance_num); } - break; + + vec3d eye_rel, view_pos_local; + vm_vec_sub(&eye_rel, eye_pos, &debris_obj->pos); + vm_vec_rotate(&view_pos_local, &eye_rel, &debris_obj->orient); + + shadow_render_list::add_model_draws(&shadow_list, pm, pmi, db->objnum, &debris_obj->pos, &debris_obj->orient, nullptr, -1, &view_pos_local); + break; + } + + default: + break; } } - scene.init_render(); - scene.render_all(ZBUFFER_TYPE_FULL); + shadow_list.init_render(false); + shadow_list.render_all(); + + render_viewer_shadow(Viewer_obj, &light_matrix, cam_offset, rot_offset); + shadows_end_render(); @@ -585,4 +793,315 @@ void shadow_end_frame() { Shadow_override = shadow_override_backup; last_override = false; } +} + +static gr_buffer_handle Shadow_cascade_params_buffer; +static size_t Shadow_cascade_params_buffer_size = 0; + +static std::pair compute_cascade_params_size(int num_cascades) { + size_t padding_required = num_cascades % 4; + if (padding_required != 0) + padding_required = 4 - padding_required; + + return {sizeof(graphics::shadow_cascade_static_data) + + sizeof(matrix4) * num_cascades + + sizeof(float) * (num_cascades + padding_required) + + sizeof(float) * (num_cascades + padding_required), + padding_required}; +} + +void shadow_cascade_params_init() { + Shadow_cascade_params_buffer_size = compute_cascade_params_size(Num_shadow_cascades + Num_cockpit_shadow_cascades).first; + Shadow_cascade_params_buffer = gr_create_buffer(BufferType::Uniform, BufferUsageHint::Dynamic); + + SCP_vector zero_data(Shadow_cascade_params_buffer_size, 0); + gr_update_buffer_data(Shadow_cascade_params_buffer, Shadow_cascade_params_buffer_size, zero_data.data()); +} + +void shadow_cascade_params_shutdown() { + if (Shadow_cascade_params_buffer.isValid()) { + gr_delete_buffer(Shadow_cascade_params_buffer); + Shadow_cascade_params_buffer = gr_buffer_handle(); + } +} + +int Shadow_cascade_count = 0; +void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { + if (!Shadow_cascade_params_buffer.isValid()) { + return; + } + + const int num_cascades = Num_shadow_cascades + Num_cockpit_shadow_cascades; + const auto [required_size, padding] = compute_cascade_params_size(num_cascades); + + Assertion(required_size <= Shadow_cascade_params_buffer_size, "The shadow cascade parameter buffer grew in size!"); + + SCP_vector buffer(required_size, 0); + size_t offset = 0; + + auto& static_data = *reinterpret_cast(buffer.data()); + + static_data.cascade_offset = cascade_offset; + static_data.cascade_count = cascade_count; + static_data.shadow_mv_matrix = Shadow_view_matrix_light; + + Shadow_cascade_count = cascade_count; + + offset += sizeof(graphics::shadow_cascade_static_data); + + for (int i = 0; i < num_cascades; i++) { + auto& proj_matrix = *reinterpret_cast(buffer.data() + offset); + proj_matrix = Shadow_proj_matrix[i]; + offset += sizeof(matrix4); + } + + for (int i = 0; i < num_cascades; i++) { + auto& cascade_distance = *reinterpret_cast(buffer.data() + offset); + cascade_distance = Shadow_cascade_distances[i]; + offset += sizeof(float); + } + offset += sizeof(float) * padding; + + for (int i = 0; i < num_cascades; i++) { + auto& smoothness_factor = *reinterpret_cast(buffer.data() + offset); + smoothness_factor = Shadow_smoothness_factor[i]; + offset += sizeof(float); + } + offset += sizeof(float) * padding; + + gr_update_buffer_data_offset(Shadow_cascade_params_buffer, 0, required_size, buffer.data()); + gr_bind_uniform_buffer(uniform_block_type::ShadowCascadeParams, 0, required_size, Shadow_cascade_params_buffer); +} + +shadow_render_list::shadow_render_list() { + reset(); +} + +void shadow_render_list::add_draw(const indexed_vertex_source* vert_src, + vertex_buffer* buffer, + size_t texi, + const matrix4& model_matrix, + const vec3d& scale, + const clip_plane_info* clip) +{ + shadow_batch_entry entry; + + entry.model_matrix = model_matrix; + entry.scale = scale; + entry.transform_buffer_offset = _batchBuffer.get_buffer_offset(); + + entry.flags = 0; + entry.vert_src = vert_src; + entry.buffer = buffer; + entry.texi = texi; + + entry.has_clip_plane = (clip != nullptr); + if (clip != nullptr) { + entry.clip_equation.xyzw.x = clip->normal.xyz.x; + entry.clip_equation.xyzw.y = clip->normal.xyz.y; + entry.clip_equation.xyzw.z = clip->normal.xyz.z; + entry.clip_equation.xyzw.w = -vm_vec_dot(&clip->normal, &clip->position); + } else { + entry.clip_equation.xyzw.x = 0.0f; + entry.clip_equation.xyzw.y = 0.0f; + entry.clip_equation.xyzw.z = 0.0f; + entry.clip_equation.xyzw.w = 0.0f; + } + + push_element(std::move(entry)); +} + +bool shadow_render_list::sort_draw_pair(int a, int b) const +{ + auto* draw_call_a = &_elements[a]; + auto* draw_call_b = &_elements[b]; + + if (draw_call_a->flags != draw_call_b->flags) { + return draw_call_a->flags < draw_call_b->flags; + } + + return a < b; +} + +void shadow_render_list::build_uniform_buffer() +{ + GR_DEBUG_SCOPE("Build shadow uniform buffer"); + + _dataBuffer = gr_get_uniform_buffer(uniform_block_type::ShadowMapData, _keys.size()); + + for (auto render_index : _keys) { + auto& queued_draw = _elements[render_index]; + + _lights.resetLightState(); + + auto element = _dataBuffer.aligner().addTypedElement(); + graphics::uniforms::convert_shadow_material(element, + queued_draw.model_matrix, + queued_draw.scale, + queued_draw.transform_buffer_offset); + + element->clip_equation = queued_draw.clip_equation; + element->use_clip_plane = queued_draw.has_clip_plane ? 1 : 0; + + queued_draw.uniform_buffer_offset = _dataBuffer.getCurrentAlignerOffset(); + } + + _dataBuffer.submitData(); +} + +void shadow_render_list::render_buffer(const shadow_batch_entry& entry) +{ + GR_DEBUG_SCOPE("Render shadow buffer"); + + auto* datap = &entry.buffer->tex_buf[entry.texi]; + if (datap->n_verts == 0) { + return; + } + + gr_bind_uniform_buffer(uniform_block_type::ShadowMapData, entry.uniform_buffer_offset, + sizeof(graphics::shadow_uniform_data), _dataBuffer.bufferHandle()); + + gr_render_shadow_draw(_dataBuffer.bufferHandle(), + entry.uniform_buffer_offset, + sizeof(graphics::shadow_uniform_data), + entry.buffer, + const_cast(entry.vert_src), + entry.texi); +} + +void shadow_render_list::add_model_draws(shadow_render_list* list, + polymodel* pm, + polymodel_instance* pmi, + int obj_num, + const vec3d* pos, const matrix* orient, + const clip_plane_info* clip, + int detail_level_lock, + const vec3d* view_pos) +{ + int detail_level; + if (detail_level_lock >= 0) { + detail_level = detail_level_lock; + } else { + float depth = model_render_determine_depth(obj_num, pm->id, orient, pos, -1); + detail_level = model_render_determine_detail(depth, pm->id, -1); + } + int detail_root = pm->detail[detail_level]; + + bool render_root_geometry = true; + if (view_pos != nullptr) { + if (!model_render_check_detail_box(view_pos, pm, detail_root, 0)) { + render_root_geometry = false; + } + } + + list->clear_transforms(); + list->push_transform(pos, orient); + + //TODO This might be incorrect for some models + const vec3d scale_identity = SCALE_IDENTITY_VECTOR; + + matrix4 identity_4; + vm_matrix4_set_identity(&identity_4); + + { + list->start_model_batch(pm->n_models); + + auto& detail_buffer = pm->detail_buffers[detail_level]; + for (size_t j = 0; j < detail_buffer.tex_buf.size(); j++) { + if (detail_buffer.tex_buf[j].n_verts == 0) { + continue; + } + + int tmap_num = detail_buffer.tex_buf[j].texture; + + if (pm->maps[tmap_num].is_transparent) { + continue; + } + + int base_tex = pm->maps[tmap_num].textures[TM_BASE_TYPE].GetTexture(); + + bool skip = false; + if (pmi != nullptr && pmi->texture_replace != nullptr) { + int replace = (*pmi->texture_replace)[tmap_num * TM_NUM_TYPES + TM_BASE_TYPE]; + if (replace == REPLACE_WITH_INVISIBLE) { + skip = true; + } else if (replace < 0 && base_tex < 0) { + skip = true; + } else if (replace >= 0) { + base_tex = 0; + } + } else if (base_tex < 0) { + skip = true; + } + + if (skip) { + continue; + } + + list->add_draw(&pm->vert_source, &detail_buffer, j, identity_4, scale_identity, clip); + } + } + + int i = pm->submodel[detail_root].first_child; + + while (i >= 0) { + if (!pm->submodel[i].flags[Model::Submodel_flags::Is_thruster]) { + render_submodel_children(list, pm, pmi, i, clip, view_pos); + } + + i = pm->submodel[i].next_sibling; + } + + if (render_root_geometry) + list->add_submodel_to_batch(detail_root); + + list->pop_transform(); +} + +void shadow_render_list::render_submodel_children(shadow_render_list* list, + polymodel* pm, + polymodel_instance* pmi, + int mn, + const clip_plane_info* clip, + const vec3d* view_pos) +{ + bsp_info* sm = &pm->submodel[mn]; + submodel_instance* smi = nullptr; + + if (pmi != nullptr) { + smi = &pmi->submodel[mn]; + if (smi->blown_off) { + return; + } + } + + if (view_pos != nullptr) { + if (!model_render_check_detail_box(view_pos, pm, mn, 0)) { + return; + } + } + + matrix submodel_orient = vmd_identity_matrix; + vec3d submodel_offset = sm->offset; + + if (smi != nullptr) { + submodel_orient = smi->canonical_orient; + vm_vec_add2(&submodel_offset, &smi->canonical_offset); + } + + list->push_transform(&submodel_offset, &submodel_orient); + + list->add_submodel_to_batch(mn); + + // Recurse into children + int i = sm->first_child; + while (i >= 0) { + if (!pm->submodel[i].flags[Model::Submodel_flags::Is_thruster]) { + render_submodel_children(list, pm, pmi, i, clip, view_pos); + } + + i = pm->submodel[i].next_sibling; + } + + list->pop_transform(); } \ No newline at end of file diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index 17e1d3dee7a..ccf754180e1 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -10,12 +10,11 @@ #ifndef _SHADOWS_H #define _SHADOWS_H +#include "graphics/render_queue.h" #include "globalincs/pstypes.h" #include "object/object.h" #include "render/3d.h" -#define MAX_SHADOW_CASCADES 4 - struct light_frustum_info { matrix4 proj_matrix; @@ -36,14 +35,20 @@ extern matrix4 Shadow_view_matrix_render; extern bool Shadow_quality_uses_mod_option; -extern matrix4 Shadow_proj_matrix[MAX_SHADOW_CASCADES]; -extern float Shadow_cascade_distances[MAX_SHADOW_CASCADES]; +extern SCP_vector Shadow_proj_matrix; +extern SCP_vector Shadow_cascade_distances; +extern int Shadow_cascade_count; void shadows_construct_light_frustum(vec3d *min_out, vec3d *max_out, vec3d light_vec, matrix *orient, vec3d *pos, fov_t fov, float aspect, float z_near, float z_far); bool shadows_obj_in_frustum(object *objp, vec3d *min, vec3d *max, matrix *light_orient); -void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos); +void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, + const vec3d* cam_offset = nullptr, const matrix* rot_offset = nullptr, const fov_t* fov_override = nullptr); + +void shadow_cascade_params_init(); +void shadow_cascade_params_shutdown(); +void shadow_cascade_params_bind(int cascade_offset, int cascade_count); -matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float aspect, float veryneardist, float neardist, float middist, float fardist); +matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t cockpit_fov, float aspect, const SCP_vector& cascade_distances); void shadows_end_render(); /** @@ -58,4 +63,59 @@ bool shadow_maybe_start_frame(const bool& override = false); */ void shadow_end_frame(); +struct shadow_batch_entry { + size_t uniform_buffer_offset = 0; + size_t transform_buffer_offset = 0; + bool has_clip_plane; + vec4 clip_equation; + matrix4 model_matrix; + vec3d scale; + int flags; + const indexed_vertex_source* vert_src; + vertex_buffer* buffer; + size_t texi; +}; + +class shadow_render_list : public render_queue { + friend class render_queue; +public: + struct clip_plane_info { + vec3d normal; + vec3d position; + }; + + shadow_render_list(); + ~shadow_render_list() = default; + + void add_draw(const indexed_vertex_source* vert_src, + vertex_buffer* buffer, + size_t texi, + const matrix4& model_matrix, + const vec3d& scale, + const clip_plane_info* clip); + + static void add_model_draws(shadow_render_list* list, + polymodel* pm, + polymodel_instance* pmi, + int obj_num, + const vec3d* pos, const matrix* orient, + const clip_plane_info* clip, + int detail_level_lock = -1, + const vec3d* view_pos = nullptr); + +private: + void build_uniform_buffer(); + void render_buffer(const shadow_batch_entry& entry); + bool sort_draw_pair(int a, int b) const; + + void sort_draws() {} + + static void render_submodel_children(shadow_render_list* list, + polymodel* pm, + polymodel_instance* pmi, + int mn, + const clip_plane_info* clip, + const vec3d* view_pos); +}; + #endif diff --git a/code/graphics/uniforms.cpp b/code/graphics/uniforms.cpp index 1b2e586144f..15ddb93a7dc 100644 --- a/code/graphics/uniforms.cpp +++ b/code/graphics/uniforms.cpp @@ -7,6 +7,7 @@ #include "globalincs/systemvars.h" #include "shadows.h" #include "nebula/neb.h" +#include "mod_table/mod_table.h" #define MODEL_SDR_FLAG_MODE_CPP #include "def_files/data/effects/model_shader_flags.h" @@ -165,24 +166,7 @@ void convert_model_material(model_uniform_data* data_out, data_out->sMiscmapIndex = bm_get_array_index(material.get_texture_map(TM_MISC_TYPE)); } - if (material.is_shadow_receiving()) { - data_out->shadow_mv_matrix = Shadow_view_matrix_light; - for (size_t i = 0; i < MAX_SHADOW_CASCADES; ++i) { - data_out->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; - } - - data_out->veryneardist = Shadow_cascade_distances[0]; - data_out->neardist = Shadow_cascade_distances[1]; - data_out->middist = Shadow_cascade_distances[2]; - data_out->fardist = Shadow_cascade_distances[3]; - } - - if (shader_flags & MODEL_SDR_FLAG_SHADOW_MAP) { - for (size_t i = 0; i < MAX_SHADOW_CASCADES; ++i) { - data_out->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; - } - } if (material.is_batched()) { data_out->buffer_matrix_offset = (int) transform_buffer_offset; @@ -240,5 +224,21 @@ void convert_model_material(model_uniform_data* data_out, } } +void convert_shadow_material(shadow_uniform_data* data_out, + const matrix4& model_transform, + const vec3d& scale, + size_t transform_buffer_offset) +{ + matrix4 scaled_matrix = model_transform; + + scale_matrix(scaled_matrix, scale); + + data_out->modelMatrix = scaled_matrix; + + vm_matrix4_x_matrix4(&data_out->modelViewMatrix, &gr_view_matrix, &scaled_matrix); + + data_out->buffer_matrix_offset = (int) transform_buffer_offset; +} + } } diff --git a/code/graphics/uniforms.h b/code/graphics/uniforms.h index 675c2a47e41..68109d06f57 100644 --- a/code/graphics/uniforms.h +++ b/code/graphics/uniforms.h @@ -18,5 +18,10 @@ void convert_model_material(model_uniform_data* data_out, const vec3d& scale, size_t transform_buffer_offset); +void convert_shadow_material(shadow_uniform_data* data_out, + const matrix4& model_transform, + const vec3d& scale, + size_t transform_buffer_offset); + } } diff --git a/code/graphics/util/UniformBufferManager.cpp b/code/graphics/util/UniformBufferManager.cpp index 5201a1129a4..652b1da5cda 100644 --- a/code/graphics/util/UniformBufferManager.cpp +++ b/code/graphics/util/UniformBufferManager.cpp @@ -14,6 +14,8 @@ size_t getElementSize(uniform_block_type type) return sizeof(graphics::deferred_light_data); case uniform_block_type::ModelData: return sizeof(graphics::model_uniform_data); + case uniform_block_type::ShadowMapData: + return sizeof(graphics::shadow_uniform_data); case uniform_block_type::NanoVGData: return sizeof(graphics::nanovg_draw_data); case uniform_block_type::DecalInfo: @@ -22,6 +24,8 @@ size_t getElementSize(uniform_block_type type) return sizeof(graphics::matrix_uniforms); case uniform_block_type::MovieData: return sizeof(graphics::movie_uniforms); + case uniform_block_type::ShadowCascadeParams: + return 0; case uniform_block_type::NUM_BLOCK_TYPES: default: UNREACHABLE("Invalid block type encountered!"); @@ -37,10 +41,12 @@ size_t getHeaderSize(uniform_block_type type) case uniform_block_type::DecalInfo: return sizeof(graphics::decal_globals); case uniform_block_type::ModelData: + case uniform_block_type::ShadowMapData: case uniform_block_type::NanoVGData: case uniform_block_type::Matrices: case uniform_block_type::MovieData: case uniform_block_type::GenericData: + case uniform_block_type::ShadowCascadeParams: return 0; case uniform_block_type::NUM_BLOCK_TYPES: default: diff --git a/code/graphics/util/uniform_structs.h b/code/graphics/util/uniform_structs.h index 454db36d1a0..c55c8194e90 100644 --- a/code/graphics/util/uniform_structs.h +++ b/code/graphics/util/uniform_structs.h @@ -18,16 +18,8 @@ namespace graphics { */ struct deferred_global_data { - matrix4 shadow_mv_matrix; - matrix4 shadow_proj_matrix[4]; - matrix4 inv_view_matrix; - float veryneardist; - float neardist; - float middist; - float fardist; - float invScreenWidth; float invScreenHeight; float nearPlane; @@ -79,8 +71,6 @@ struct model_uniform_data { matrix4 viewMatrix; matrix4 projMatrix; matrix4 textureMatrix; - matrix4 shadow_mv_matrix; - matrix4 shadow_proj_matrix[4]; vec4 color; @@ -110,7 +100,7 @@ struct model_uniform_data { int gammaSpec; int envGloss; int effect_num; - int sBasemapIndex; // moved up here to track alignment + int sBasemapIndex; vec4 fogColor; @@ -125,11 +115,6 @@ struct model_uniform_data { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - int sGlowmapIndex; int sSpecmapIndex; int sNormalmapIndex; @@ -144,6 +129,25 @@ struct model_uniform_data { const size_t model_uniform_data_size = sizeof(model_uniform_data); const float mud_align = model_uniform_data_size / 16.0f; + +struct shadow_uniform_data { + matrix4 modelViewMatrix; + matrix4 modelMatrix; + vec4 clip_equation; + int use_clip_plane; + int buffer_matrix_offset; + float pad[2]; +}; + +const size_t shadow_uniform_data_size = sizeof(shadow_uniform_data); + +struct shadow_cascade_static_data { + int cascade_offset; + int cascade_count; + float pad[2]; + matrix4 shadow_mv_matrix; +}; + enum class NanoVGShaderType: int32_t { FillGradient = 0, FillImage = 1, Simple = 2, Image = 3 }; diff --git a/code/graphics/vulkan/vulkan_stubs.cpp b/code/graphics/vulkan/vulkan_stubs.cpp index a757ed553ef..6d4f3d0230a 100644 --- a/code/graphics/vulkan/vulkan_stubs.cpp +++ b/code/graphics/vulkan/vulkan_stubs.cpp @@ -127,7 +127,7 @@ bool stub_bm_data(int /*n*/, bitmap* /*bm*/) { return true; } int stub_maybe_create_shader(shader_type /*shader_t*/, unsigned int /*flags*/) { return -1; } -void stub_shadow_map_start(matrix4* /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/) {} +void stub_shadow_map_start(matrix4* /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/, bool /*first_pass*/) {} void stub_shadow_map_end() {} @@ -154,6 +154,13 @@ void stub_render_model(model_material* /*material_info*/, vertex_buffer* /*bufferp*/, size_t /*texi*/) { + +} + +void stub_render_shadow_draw(gr_buffer_handle /*ubo_handle*/, size_t /*ubo_offset*/, size_t /*ubo_size*/, + vertex_buffer* /*buffer*/, indexed_vertex_source* /*vert_src*/, size_t /*texi*/) +{ + } void stub_render_primitives(material* /*material_info*/, @@ -352,6 +359,7 @@ void init_stub_pointers() gr_screen.gf_get_bitmap_from_texture = stub_get_bitmap_from_texture; gr_screen.gf_render_model = stub_render_model; + gr_screen.gf_render_shadow_draw = stub_render_shadow_draw; gr_screen.gf_render_primitives = stub_render_primitives; gr_screen.gf_render_primitives_particle = stub_render_primitives_particle; gr_screen.gf_render_primitives_distortion = stub_render_primitives_distortion; diff --git a/code/menuui/techmenu.cpp b/code/menuui/techmenu.cpp index 4a64d32acef..b18a219aa83 100644 --- a/code/menuui/techmenu.cpp +++ b/code/menuui/techmenu.cpp @@ -614,7 +614,7 @@ void techroom_ships_render(float frametime) auto pm = model_get(Techroom_modelnum); - shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos.xyz.z + pm->rad, -closeup_pos.xyz.z + pm->rad + 200.0f, -closeup_pos.xyz.z + pm->rad + 2000.0f, -closeup_pos.xyz.z + pm->rad + 10000.0f); + shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, Proj_fov, gr_screen.clip_aspect, {-closeup_pos.xyz.z + pm->rad, -closeup_pos.xyz.z + pm->rad + 200.0f, -closeup_pos.xyz.z + pm->rad + 2000.0f, -closeup_pos.xyz.z + pm->rad + 10000.0f}); render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | MR_AUTOCENTER); model_render_immediate(&render_info, Techroom_modelnum, model_instance, &Techroom_ship_orient, &vmd_zero_vector); diff --git a/code/missionui/missionbrief.cpp b/code/missionui/missionbrief.cpp index b1112c5c87d..6a885ef52d6 100644 --- a/code/missionui/missionbrief.cpp +++ b/code/missionui/missionbrief.cpp @@ -1097,7 +1097,7 @@ void brief_render_closeup(int ship_class, float frametime) auto pm = model_get(Closeup_icon->modelnum); gr_reset_clip(); - shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -Closeup_cam_pos.xyz.z + pm->rad, -Closeup_cam_pos.xyz.z + pm->rad + 200.0f, -Closeup_cam_pos.xyz.z + pm->rad + 2000.0f, -Closeup_cam_pos.xyz.z + pm->rad + 10000.0f); + shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, Proj_fov, gr_screen.clip_aspect, {-Closeup_cam_pos.xyz.z + pm->rad, -Closeup_cam_pos.xyz.z + pm->rad + 200.0f, -Closeup_cam_pos.xyz.z + pm->rad + 2000.0f, -Closeup_cam_pos.xyz.z + pm->rad + 10000.0f}); render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | MR_AUTOCENTER); model_render_immediate(&render_info, Closeup_icon->modelnum, Closeup_icon->model_instance_num, &Closeup_orient, &Closeup_pos); diff --git a/code/missionui/missionscreencommon.cpp b/code/missionui/missionscreencommon.cpp index 291b9dfcff9..adbca853b82 100644 --- a/code/missionui/missionscreencommon.cpp +++ b/code/missionui/missionscreencommon.cpp @@ -1845,9 +1845,9 @@ void draw_model_rotating(model_render_params *render_info, int ship_class, int m shadow_render_info.set_flags(flags | MR_NO_TEXTURING | MR_NO_LIGHTING); if ( flags & MR_IS_MISSILE ) { - shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 20.0f, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 1000.0f); + shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, Proj_fov, gr_screen.clip_aspect, {-closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 20.0f, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 1000.0f}); } else { - shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 2000.0f, -closeup_pos->xyz.z + pm->rad + 10000.0f); + shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, Proj_fov, gr_screen.clip_aspect, {-closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 2000.0f, -closeup_pos->xyz.z + pm->rad + 10000.0f}); } model_render_immediate(&shadow_render_info, model_id, model_instance, &model_orient, &vmd_zero_vector); @@ -1937,9 +1937,9 @@ void draw_model_rotating(model_render_params *render_info, int ship_class, int m if(shadow_maybe_start_frame(shadow_disable_override)) { if ( flags & MR_IS_MISSILE ) { - shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 20.0f, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 1000.0f); + shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, Proj_fov, gr_screen.clip_aspect, {-closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 20.0f, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 1000.0f}); } else { - shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 2000.0f, -closeup_pos->xyz.z + pm->rad + 10000.0f); + shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, Proj_fov, gr_screen.clip_aspect, {-closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 2000.0f, -closeup_pos->xyz.z + pm->rad + 10000.0f}); } model_render_params shadow_render_info; diff --git a/code/missionui/missionweaponchoice.cpp b/code/missionui/missionweaponchoice.cpp index 14d2260edd3..b652a42536c 100644 --- a/code/missionui/missionweaponchoice.cpp +++ b/code/missionui/missionweaponchoice.cpp @@ -844,11 +844,12 @@ void draw_3d_overhead_view(int model_num, shadows_start_render(&vmd_identity_matrix, &Eye_position, Proj_fov, + Proj_fov, gr_screen.clip_aspect, - -sip->closeup_pos.xyz.z + pm->rad, + {-sip->closeup_pos.xyz.z + pm->rad, -sip->closeup_pos.xyz.z + pm->rad + 200.0f, -sip->closeup_pos.xyz.z + pm->rad + 2000.0f, - -sip->closeup_pos.xyz.z + pm->rad + 10000.0f); + -sip->closeup_pos.xyz.z + pm->rad + 10000.0f}); render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | MR_AUTOCENTER); diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index b5fd84f2c6d..efef07e63f6 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -127,8 +127,11 @@ bool Neb_affects_beams; bool Neb_affects_weapons; bool Neb_affects_particles; bool Neb_affects_fireballs; -std::tuple Shadow_distances; -std::tuple Shadow_distances_cockpit; +SCP_vector Shadow_distances; +static SCP_vector Shadow_distances_cockpit; +SCP_vector Shadow_smoothness_factor; +int Num_shadow_cascades = 4; +int Num_cockpit_shadow_cascades = 4; bool Show_ship_casts_shadow; bool Cockpit_shares_coordinate_space; bool Show_ship_only_if_cockpits_enabled; @@ -886,23 +889,46 @@ void parse_mod_table(const char *filename) } if (optional_string("$Shadow Cascade Distances:")) { - float dis[4]; - stuff_float_list(dis, 4); - if ((dis[0] >= 0) && (dis[1] > dis[0]) && (dis[2] > dis[1]) && (dis[3] > dis[2])) { - Shadow_distances = std::make_tuple((dis[0]), (dis[1]), (dis[2]), (dis[3])); + SCP_vector dis; + stuff_float_list(dis); + bool valid = !dis.empty() && dis[0] >= 0.0f; + for (size_t i = 1; valid && i < dis.size(); i++) { + valid = dis[i] > dis[i - 1]; + } + if (valid) { + Shadow_distances = std::move(dis); + Num_shadow_cascades = static_cast(Shadow_distances.size()); } else { - error_display(0, "$Shadow Cascade Distances are %f, %f, %f, %f. One or more are < 0, and/or values are not increasing. Assuming default distances.", dis[0], dis[1], dis[2], dis[3]); + error_display(0, "$Shadow Cascade Distances: values must be non-negative and strictly increasing. Assuming default distances."); } } if (optional_string("$Shadow Cascade Distances Cockpit:")) { - float dis[4]; - stuff_float_list(dis, 4); - if ((dis[0] >= 0) && (dis[1] > dis[0]) && (dis[2] > dis[1]) && (dis[3] > dis[2])) { - Shadow_distances_cockpit = std::make_tuple((dis[0]), (dis[1]), (dis[2]), (dis[3])); + SCP_vector dis; + stuff_float_list(dis); + bool valid = !dis.empty() && dis[0] >= 0.0f; + for (size_t i = 1; valid && i < dis.size(); i++) { + valid = dis[i] > dis[i - 1]; + } + if (valid) { + Shadow_distances_cockpit = std::move(dis); + Num_cockpit_shadow_cascades = static_cast(Shadow_distances_cockpit.size()); + } else { + error_display(0, "$Shadow Cascade Distances Cockpit: values must be non-negative and strictly increasing. Assuming default distances."); } - else { - error_display(0, "$Shadow Cascade Distances Cockpit are %f, %f, %f, %f. One or more are < 0, and/or values are not increasing. Assuming default distances.", dis[0], dis[1], dis[2], dis[3]); + } + + if (optional_string("$Shadow Smoothness Factor:")) { + SCP_vector smoothness; + stuff_float_list(smoothness); + bool valid = !smoothness.empty(); + for (size_t i = 0; valid && i < smoothness.size(); i++) { + valid = smoothness[i] > 0.0f; + } + if (valid) { + Shadow_smoothness_factor = std::move(smoothness); + } else { + error_display(0, "$Shadow Smoothness Factor: all values must be > 0. Using defaults."); } } @@ -1704,6 +1730,32 @@ void mod_table_init() mprintf(( "Game Settings Table: Disabling in-game options system because the commandline override was detected!.\n")); } + + //Validate and process shadow settings. This has to happen here rather than in mod_table_post_process as to run before graphics init. + { + //Validate that we have the correct number of shadow smoothness factors + if (Num_shadow_cascades + Num_cockpit_shadow_cascades != static_cast(Shadow_smoothness_factor.size())) { + Warning(LOCATION, "$Shadow Smoothness Factor: number of values (currently %d) must match number of total cascades (%d cockpit cascades + %d main scene cascades = %d total cascades).", static_cast(Shadow_smoothness_factor.size()), Num_cockpit_shadow_cascades, Num_shadow_cascades, Num_cockpit_shadow_cascades + Num_shadow_cascades); + int current_last = static_cast(Shadow_smoothness_factor.size()); + Shadow_smoothness_factor.resize(Num_shadow_cascades + Num_cockpit_shadow_cascades); + for (int i = current_last; i < Num_shadow_cascades + Num_cockpit_shadow_cascades; ++i) + Shadow_smoothness_factor[i] = 1.f / 300.f; + } + + //If we guarantee that no cockpit / local show ship shadows are ever rendered, we can remove the cascades and save some VRAM + if (Shadow_disable_overrides.disable_cockpit) { + Shadow_smoothness_factor.erase(Shadow_smoothness_factor.begin(), Shadow_smoothness_factor.begin() + Num_cockpit_shadow_cascades); + Num_cockpit_shadow_cascades = 0; + Shadow_distances_cockpit.clear(); + } + + //Insert the normal distances after the shadow distances as to keep them in ascending order + Shadow_distances_cockpit.insert(Shadow_distances_cockpit.end(), Shadow_distances.begin(), Shadow_distances.end()); + std::swap(Shadow_distances_cockpit, Shadow_distances); + + //Stale data + Shadow_distances_cockpit.clear(); + } } // game_settings.tbl is parsed before graphics are actually initialized, so we can't calculate the resolution at that time @@ -1831,8 +1883,11 @@ void mod_table_reset() Neb_affects_weapons = false; Neb_affects_particles = false; Neb_affects_fireballs = false; - Shadow_distances = std::make_tuple(200.0f, 600.0f, 2500.0f, 8000.0f); // Default values tuned by Swifty and added here by wookieejedi - Shadow_distances_cockpit = std::make_tuple(0.25f, 0.75f, 1.5f, 3.0f); // Default values tuned by wookieejedi and added here by Lafiel + Shadow_distances = {200.0f, 600.0f, 2500.0f, 8000.0f}; + Shadow_distances_cockpit = {0.25f, 0.75f, 1.5f, 3.0f}; + Shadow_smoothness_factor = {1.0f/300.0f, 1.0f/250.0f, 1.0f/200.0f, 1.0f/200.0f, 1.0f/300.0f, 1.0f/250.0f, 1.0f/200.0f, 1.0f/200.0f}; + Num_shadow_cascades = static_cast(Shadow_distances.size()); + Num_cockpit_shadow_cascades = static_cast(Shadow_distances_cockpit.size()); Show_ship_casts_shadow = false; Cockpit_shares_coordinate_space = false; Show_ship_only_if_cockpits_enabled = false; diff --git a/code/mod_table/mod_table.h b/code/mod_table/mod_table.h index 022d6e34fc3..d74e35b0395 100644 --- a/code/mod_table/mod_table.h +++ b/code/mod_table/mod_table.h @@ -147,8 +147,10 @@ extern bool Neb_affects_beams; extern bool Neb_affects_weapons; extern bool Neb_affects_particles; extern bool Neb_affects_fireballs; -extern std::tuple Shadow_distances; -extern std::tuple Shadow_distances_cockpit; +extern SCP_vector Shadow_distances; +extern SCP_vector Shadow_smoothness_factor; +extern int Num_shadow_cascades; +extern int Num_cockpit_shadow_cascades; extern bool Show_ship_casts_shadow; extern bool Cockpit_shares_coordinate_space; extern bool Show_ship_only_if_cockpits_enabled; diff --git a/code/model/modelread.cpp b/code/model/modelread.cpp index c5f908f7c71..cd472b1f316 100644 --- a/code/model/modelread.cpp +++ b/code/model/modelread.cpp @@ -3213,7 +3213,7 @@ void model_load_texture(polymodel *pm, int i, const char *file) // See if we need to compile a new shader for this material if (Shadow_quality != ShadowQuality::Disabled) - gr_maybe_create_shader(SDR_TYPE_MODEL, MODEL_SDR_FLAG_SHADOW_MAP); + gr_maybe_create_shader(SDR_TYPE_SHADOW_MAP_GEN, gr_is_capable(gr_capability::CAPABILITY_FAST_SHADOWS) ? 0 : SDR_FLAG_SHADOW_FALLBACK); gr_maybe_create_shader(SDR_TYPE_MODEL, 0); diff --git a/code/model/modelrender.cpp b/code/model/modelrender.cpp index 681aac11e93..f1ef2b8d643 100644 --- a/code/model/modelrender.cpp +++ b/code/model/modelrender.cpp @@ -156,8 +156,6 @@ void model_clear_cached_ui_render_instances() Ui_render_instance_cache_last_processed_framecount = -1; } -model_batch_buffer TransformBufferHandler; - model_render_params::model_render_params() : Model_flags(MR_NORMAL), Debug_flags(0), @@ -503,59 +501,16 @@ void model_batch_buffer::submit_buffer_data() gr_update_transform_buffer(Mem_alloc, Mem_alloc_size); } -model_draw_list::model_draw_list(): -Transformations() +model_draw_list::model_draw_list() { reset(); } -void model_draw_list::reset() -{ - Render_elements.clear(); - Render_keys.clear(); - - Transformations.clear(); - - Current_scale.xyz.x = 1.0f; - Current_scale.xyz.y = 1.0f; - Current_scale.xyz.z = 1.0f; - - Render_initialized = false; -} - -void model_draw_list::sort_draws() -{ - std::sort(Render_keys.begin(), Render_keys.end(), - [this](const int a, const int b) { return model_draw_list::sort_draw_pair(this, a, b); }); -} - -void model_draw_list::start_model_batch(int n_models) -{ - TransformBufferHandler.set_num_models(n_models); -} - -void model_draw_list::add_submodel_to_batch(int model_num) -{ - matrix4 transform; - - transform = Transformations.get_transform(); - - // set scale - vm_vec_scale(&transform.vec.rvec, Current_scale.xyz.x); - vm_vec_scale(&transform.vec.uvec, Current_scale.xyz.y); - vm_vec_scale(&transform.vec.fvec, Current_scale.xyz.z); - - // set visibility - transform.a1d[15] = 0.0f; - - TransformBufferHandler.set_model_transform(transform, model_num); -} - void model_draw_list::add_arc(const vec3d *v1, const vec3d *v2, const SCP_vector *persistent_arc_points, const color *primary, const color *secondary, float arc_width, ubyte segment_depth) { arc_effect new_arc; - new_arc.transform = Transformations.get_transform(); + new_arc.transform = get_transform(); new_arc.v1 = *v1; new_arc.v2 = *v2; new_arc.primary = *primary; @@ -569,9 +524,9 @@ void model_draw_list::add_arc(const vec3d *v1, const vec3d *v2, const SCP_vector void model_draw_list::set_light_filter(const vec3d *pos, float rad) { - Scene_light_handler.setLightFilter(pos, rad); + _lights.setLightFilter(pos, rad); - Current_lights_set = Scene_light_handler.bufferLights(); + Current_lights_set = _lights.bufferLights(); } void model_draw_list::add_buffer_draw(const model_material *render_material, const indexed_vertex_source *vert_src, const vertex_buffer *buffer, size_t texi, uint tmap_flags) @@ -579,17 +534,11 @@ void model_draw_list::add_buffer_draw(const model_material *render_material, con queued_buffer_draw draw_data; draw_data.render_material = *render_material; - if (Rendering_to_shadow_map) { - draw_data.render_material.set_shadow_casting(true); - } else { - // If the zbuffer type is FULL then this buffer may be drawn in the deferred lighting part otherwise we need to - // make sure that the deferred flag is disabled or else some parts of the rendered colors go missing - // TODO: This should really be handled somewhere else. This feels like a crude hack... + { auto possibly_deferred = draw_data.render_material.get_depth_mode() == ZBUFFER_TYPE_FULL && gr_is_capable(gr_capability::CAPABILITY_DEFERRED_LIGHTING) && light_deferred_enabled(); if (possibly_deferred) { - // Fog is handled differently in deferred shader situations draw_data.render_material.set_fog(); } @@ -605,12 +554,12 @@ void model_draw_list::add_buffer_draw(const model_material *render_material, con draw_data.scale.xyz.y = 1.0f; draw_data.scale.xyz.z = 1.0f; - draw_data.transform_buffer_offset = TransformBufferHandler.get_buffer_offset(); + draw_data.transform_buffer_offset = _batchBuffer.get_buffer_offset(); draw_data.render_material.set_batching(true); } else { - draw_data.transform = Transformations.get_transform(); - draw_data.scale = Current_scale; + draw_data.transform = get_transform(); + draw_data.scale = _scale; draw_data.transform_buffer_offset = INVALID_SIZE; draw_data.render_material.set_batching(false); } @@ -623,8 +572,7 @@ void model_draw_list::add_buffer_draw(const model_material *render_material, con draw_data.flags = tmap_flags; draw_data.lights = Current_lights_set; - Render_elements.push_back(draw_data); - Render_keys.push_back((int) (Render_elements.size() - 1)); + push_element(std::move(draw_data)); } void model_draw_list::render_buffer(const queued_buffer_draw &render_elements) @@ -641,14 +589,13 @@ void model_draw_list::render_buffer(const queued_buffer_draw &render_elements) vec3d model_draw_list::get_view_position() const { matrix basis_world; - matrix4 transform_mat = Transformations.get_transform(); + matrix4 transform_mat = _transforms.get_transform(); matrix orient; vec3d pos; vm_matrix4_get_orientation(&orient, &transform_mat); vm_matrix4_get_offset(&pos, &transform_mat); - // get the world basis of our current local space. vm_matrix_x_matrix(&basis_world, &Object_matrix, &orient); vec3d eye_pos_local; @@ -660,52 +607,15 @@ vec3d model_draw_list::get_view_position() const return return_val; } -void model_draw_list::push_transform(const vec3d *pos, const matrix *orient) -{ - Transformations.push(pos, orient); -} - -void model_draw_list::pop_transform() -{ - Transformations.pop(); -} - -void model_draw_list::set_scale(const vec3d *scale) -{ - if ( scale == NULL ) { - Current_scale.xyz.x = 1.0f; - Current_scale.xyz.y = 1.0f; - Current_scale.xyz.z = 1.0f; - return; - } - - Current_scale = *scale; -} - void model_draw_list::init() { reset(); for (auto& l : Lights) { if ( l.type == Light_Type::Directional || !Deferred_lighting ) { - Scene_light_handler.addLight(&l); + _lights.addLight(&l); } } - - TransformBufferHandler.reset(); -} - -void model_draw_list::init_render(bool sort) -{ - if ( sort ) { - sort_draws(); - } - - TransformBufferHandler.submit_buffer_data(); - - build_uniform_buffer(); - - Render_initialized = true; } void model_draw_list::render_all(gr_zbuffer_type depth_mode) @@ -713,15 +623,13 @@ void model_draw_list::render_all(gr_zbuffer_type depth_mode) GR_DEBUG_SCOPE("Render draw list"); TRACE_SCOPE(tracing::SubmitDraws); - Assertion(Render_initialized, "init_render must be called before any render_all call!"); - - Scene_light_handler.resetLightState(); + Assertion(_initialized, "init_render must be called before any render_all call!"); - for ( size_t i = 0; i < Render_keys.size(); ++i ) { - int render_index = Render_keys[i]; + _lights.resetLightState(); - if ( depth_mode == ZBUFFER_TYPE_DEFAULT || Render_elements[render_index].render_material.get_depth_mode() == depth_mode ) { - render_buffer(Render_elements[render_index]); + for (int render_index : _keys) { + if ( depth_mode == ZBUFFER_TYPE_DEFAULT || _elements[render_index].render_material.get_depth_mode() == depth_mode ) { + render_buffer(_elements[render_index]); } } @@ -752,7 +660,7 @@ void model_draw_list::add_insignia(const model_render_params *params, const poly { insignia_draw_data new_insignia; - new_insignia.transform = Transformations.get_transform(); + new_insignia.transform = get_transform(); new_insignia.pm = pm; new_insignia.detail_level = detail_level; new_insignia.bitmap_num = bitmap_num; @@ -800,7 +708,7 @@ void model_draw_list::add_outline(const vertex* vert_array, int n_verts, const c draw_info.vert_array = vert_array; draw_info.n_verts = n_verts; draw_info.clr = *clr; - draw_info.transform = Transformations.get_transform(); + draw_info.transform = get_transform(); Outlines.push_back(draw_info); } @@ -829,10 +737,10 @@ void model_draw_list::render_outline(const outline_draw &outline_info) g3_done_instance(true); } -bool model_draw_list::sort_draw_pair(const model_draw_list* target, const int a, const int b) +bool model_draw_list::sort_draw_pair(int a, int b) const { - auto draw_call_a = &target->Render_elements[a]; - auto draw_call_b = &target->Render_elements[b]; + auto draw_call_a = &_elements[a]; + auto draw_call_b = &_elements[b]; if ( draw_call_a->sdr_flags != draw_call_b->sdr_flags ) { return draw_call_a->sdr_flags < draw_call_b->sdr_flags; @@ -885,17 +793,15 @@ void model_draw_list::build_uniform_buffer() { TRACE_SCOPE(tracing::BuildModelUniforms); - _dataBuffer = gr_get_uniform_buffer(uniform_block_type::ModelData, Render_keys.size()); + _dataBuffer = gr_get_uniform_buffer(uniform_block_type::ModelData, _keys.size()); - for (auto render_index : Render_keys) { - auto& queued_draw = Render_elements[render_index]; + for (auto render_index : _keys) { + auto& queued_draw = _elements[render_index]; - // Set lighting here so that it can be captured by the uniform conversion below if ( queued_draw.render_material.is_lit() ) { - Scene_light_handler.setLights(&queued_draw.lights); + _lights.setLights(&queued_draw.lights); } else { - - Scene_light_handler.resetLightState(); + _lights.resetLightState(); } auto element = _dataBuffer.aligner().addTypedElement(); @@ -1263,20 +1169,6 @@ void model_render_buffers(model_draw_list* scene, model_material *rendering_mate } } else { alpha = forced_alpha; - - //Check for invisible or transparent textures so they don't show up in the shadow maps - Valathil - if ( Rendering_to_shadow_map ) { - if ( (replacement_textures != nullptr) && ((*replacement_textures)[rt_begin_index + TM_BASE_TYPE] >= 0) ) { - tex_replace[TM_BASE_TYPE] = texture_info((*replacement_textures)[rt_begin_index + TM_BASE_TYPE]); - texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], elapsed_time); - } else { - texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], elapsed_time); - } - - if ( texture_maps[TM_BASE_TYPE] < 0 ) { - continue; - } - } } if ( (texture_maps[TM_BASE_TYPE] == -1) && !no_texturing && !(debug_flags & MR_DEBUG_NO_DIFFUSE) ) { @@ -1703,11 +1595,7 @@ void submodel_render_queue(const model_render_params *render_info, model_draw_li rendering_material.set_fog(); } - if(Rendering_to_shadow_map) { - rendering_material.set_depth_bias(-1024); - } else { - rendering_material.set_depth_bias(0); - } + rendering_material.set_depth_bias(0); vec3d view_pos = scene->get_view_position(); @@ -2119,10 +2007,6 @@ void model_render_glow_points(const polymodel *pm, const polymodel_instance *pmi { Assert(pmi == nullptr || pm->id == pmi->model_num); - if ( Rendering_to_shadow_map ) { - return; - } - int i, j; int cull = gr_set_cull(0); @@ -2245,10 +2129,6 @@ void model_queue_render_thrusters(const model_render_params *interp, const polym vertex p; bool do_render = false; - if ( Rendering_to_shadow_map ) { - return; - } - if ( pm == NULL ) { Int3(); return; @@ -2988,11 +2868,7 @@ void model_render_queue(const model_render_params* interp, model_draw_list* scen rendering_material.set_color_mask(true, true, true, true); } - if ( Rendering_to_shadow_map ) { - rendering_material.set_depth_bias(-1024); - } else { - rendering_material.set_depth_bias(0); - } + rendering_material.set_depth_bias(0); if ( !(model_flags & MR_NO_BATCH) && pm->flags & PM_FLAG_BATCHED && !(is_outlines_only || is_outlines_only_htl) ) { diff --git a/code/model/modelrender.h b/code/model/modelrender.h index 8586373639f..260b318e971 100644 --- a/code/model/modelrender.h +++ b/code/model/modelrender.h @@ -11,6 +11,7 @@ #define _MODELRENDER_H #include "graphics/material.h" +#include "graphics/render_queue.h" #include "lighting/lighting.h" #include "math/vecmat.h" #include "model/model.h" @@ -21,8 +22,6 @@ extern SCP_vector Lights; extern int Num_lights; -extern bool Rendering_to_shadow_map; - extern matrix Object_matrix; extern vec3d Object_position; @@ -249,57 +248,25 @@ struct outline_draw color clr; }; -class model_batch_buffer -{ - SCP_vector Submodel_matrices; - void* Mem_alloc; - size_t Mem_alloc_size; - - size_t Current_offset; - - void allocate_memory(); -public: - model_batch_buffer() : Mem_alloc(NULL), Mem_alloc_size(0), Current_offset(0) {}; - - void reset(); - - size_t get_buffer_offset() const; - void set_num_models(int n_models); - void set_model_transform(const matrix4 &transform, int model_id); - void submit_buffer_data(); - void add_matrix(const matrix4 &mat); -}; - -class model_draw_list -{ - vec3d Current_scale; - transform_stack Transformations; +class model_draw_list : public render_queue { + friend class render_queue; - scene_lights Scene_light_handler; light_indexing_info Current_lights_set; void render_arc(const arc_effect &arc); static void render_insignia(const insignia_draw_data &insignia_info); void render_outline(const outline_draw &outline_info); - void render_buffer(const queued_buffer_draw &render_elements); - - SCP_vector Render_elements; - SCP_vector Render_keys; SCP_vector Arcs; SCP_vector Insignias; SCP_vector Outlines; - graphics::util::UniformBuffer _dataBuffer; - - bool Render_initialized = false; //!< A flag for checking if init_render has been called before a render_all call - - static bool sort_draw_pair(const model_draw_list* target, const int a, const int b); - void sort_draws(); - void build_uniform_buffer(); + void render_buffer(const queued_buffer_draw &render_elements); + bool sort_draw_pair(int a, int b) const; + public: model_draw_list(); ~model_draw_list(); @@ -309,15 +276,9 @@ class model_draw_list void init(); - void add_submodel_to_batch(int model_num); - void start_model_batch(int n_models); - void add_buffer_draw(const model_material *render_material, const indexed_vertex_source *vert_src, const vertex_buffer *buffer, size_t texi, uint tmap_flags); - + vec3d get_view_position() const; - void push_transform(const vec3d* pos, const matrix* orient); - void pop_transform(); - void set_scale(const vec3d *scale = NULL); void add_arc(const vec3d *v1, const vec3d *v2, const SCP_vector *persistent_arc_points, const color *primary, const color *secondary, float arc_width, ubyte segment_depth); void render_arcs(); @@ -330,9 +291,7 @@ class model_draw_list void set_light_filter(const vec3d *pos, float rad); - void init_render(bool sort = true); void render_all(gr_zbuffer_type depth_mode = ZBUFFER_TYPE_DEFAULT); - void reset(); }; void model_render_only_glowpoint_lights(const model_render_params* interp, int model_num, int model_instance_num, const matrix* orient, const vec3d* pos); @@ -349,6 +308,8 @@ bool model_render_check_detail_box(const vec3d* view_pos, const polymodel* pm, i void model_render_arc(const vec3d* v1, const vec3d* v2, const SCP_vector *persistent_arc_points, const color* primary, const color* secondary, float arc_width, ubyte depth_limit); void model_render_insignias(const insignia_draw_data* insignia); void model_render_set_wireframe_color(const color* clr); +float model_render_determine_depth(int obj_num, int model_num, const matrix* orient, const vec3d* pos, int detail_level_locked); +int model_render_determine_detail(float depth, int model_num, int detail_level_locked); bool render_tech_model(tech_render_type model_type, int x1, int y1, int x2, int y2, float zoom, bool lighting, int class_idx, const matrix* orient, const SCP_string& pof_filename = "", float closeup_zoom = 0, const vec3d* closeup_pos = &vmd_zero_vector, const SCP_string& tcolor = "", const SCP_vector& destroyed_subsystems = SCP_vector()); size_t model_hash_subsystem_name_list_for_cache(const SCP_vector& subsystem_names); diff --git a/code/object/objectsort.cpp b/code/object/objectsort.cpp index fde3af712a5..7629f6fe3ef 100644 --- a/code/object/objectsort.cpp +++ b/code/object/objectsort.cpp @@ -17,7 +17,9 @@ #include "cmdline/cmdline.h" #include "debris/debris.h" #include "graphics/light.h" +#include "graphics/shadows.h" #include "jumpnode/jumpnode.h" +#include "mod_table/mod_table.h" #include "mission/missionparse.h" #include "model/modelrender.h" #include "nebula/neb.h" @@ -368,6 +370,10 @@ void obj_render_queue_all() scene.init_render(); + if (Shadow_quality != ShadowQuality::Disabled) { + shadow_cascade_params_bind(Num_cockpit_shadow_cascades, Num_shadow_cascades); + } + scene.render_all(ZBUFFER_TYPE_FULL); gr_zbuffer_set(ZBUFFER_TYPE_READ); gr_zbias(0); diff --git a/code/prop/prop.cpp b/code/prop/prop.cpp index 52e3872745b..426f092e2fb 100644 --- a/code/prop/prop.cpp +++ b/code/prop/prop.cpp @@ -739,10 +739,6 @@ void prop_render(object* obj, model_draw_list* scene) render_flags |= MR_NO_LIGHTING; } - if (Rendering_to_shadow_map) { - render_flags = MR_NO_TEXTURING | MR_NO_LIGHTING; - } - if (propp->flags[Prop::Prop_Flags::Glowmaps_disabled]) { render_flags |= MR_NO_GLOWMAPS; } diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 17dff658e29..3cd9cae6819 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8476,8 +8476,46 @@ static void ship_find_warping_ship_helper(object *objp, dock_function_info *info } } -//WMC - used for FTL and maneuvering thrusters -extern bool Rendering_to_shadow_map; +static bool ship_render_player_renderShipModel(const ship_info* sip) { + return sip->flags[Ship::Info_Flags::Show_ship_model] + && (!Show_ship_only_if_cockpits_enabled || Cockpit_active) + && (!Viewer_mode || (Viewer_mode & VM_PADLOCK_ANY) || (Viewer_mode & VM_OTHER_SHIP) || (Viewer_mode & VM_TRACK) || !(Viewer_mode & VM_EXTERNAL)); +} + +bool ship_render_player_ship_casts_shadow_on_cockpit() { + if (Viewer_obj == nullptr) + return false; + + if (Shadow_disable_overrides.disable_cockpit) + return false; + + ship* shipp = &Ships[Viewer_obj->instance]; + ship_info* sip = &Ship_info[shipp->ship_info_index]; + + const bool hasCockpitModel = sip->cockpit_model_num >= 0; + const bool renderShipModel = ship_render_player_renderShipModel(sip); + + //If we aren't sure whether cockpits and external models can share the same worldspace, + //we need to pre-render the external ship hull without shadows / deferred and give the cockpit precedence, + //unless this ship has no cockpit at all + const bool prerenderShipModel = hasCockpitModel && !Cockpit_shares_coordinate_space; + return renderShipModel && !prerenderShipModel; +} + +bool ship_render_player_has_closeup_visuals() { + if (Viewer_obj == nullptr) + return false; + + ship* shipp = &Ships[Viewer_obj->instance]; + ship_info* sip = &Ship_info[shipp->ship_info_index]; + + const bool hasCockpitModel = sip->cockpit_model_num >= 0; + + const bool renderCockpitModel = (Viewer_mode != VM_TOPDOWN) && hasCockpitModel && !Disable_cockpits; + const bool renderShipModel = ship_render_player_renderShipModel(sip); + + return renderCockpitModel || renderShipModel; +} void ship_render_player_ship(object* objp, const vec3d* cam_offset, const matrix* rot_offset, const fov_t* fov_override) { ship* shipp = &Ships[objp->instance]; @@ -8487,10 +8525,7 @@ void ship_render_player_ship(object* objp, const vec3d* cam_offset, const matrix const bool hasCockpitModel = sip->cockpit_model_num >= 0; const bool renderCockpitModel = (Viewer_mode != VM_TOPDOWN) && hasCockpitModel && !Disable_cockpits; - const bool renderShipModel = ( - sip->flags[Ship::Info_Flags::Show_ship_model]) - && (!Show_ship_only_if_cockpits_enabled || Cockpit_active) - && (!Viewer_mode || (Viewer_mode & VM_PADLOCK_ANY) || (Viewer_mode & VM_OTHER_SHIP) || (Viewer_mode & VM_TRACK) || !(Viewer_mode & VM_EXTERNAL)); + const bool renderShipModel = ship_render_player_renderShipModel(sip); Cockpit_active = renderCockpitModel; //Nothing to do @@ -8574,46 +8609,20 @@ void ship_render_player_ship(object* objp, const vec3d* cam_offset, const matrix gr_post_process_save_zbuffer(); - //Deal with shadow if we have to - if (shadow_maybe_start_frame(Shadow_disable_overrides.disable_cockpit)) { - gr_reset_clip(); - Shadow_override = false; - - shadows_start_render(&eye_orient, &leaning_position, Proj_fov, gr_screen.clip_aspect, - std::get<0>(Shadow_distances_cockpit), - std::get<1>(Shadow_distances_cockpit), - std::get<2>(Shadow_distances_cockpit), - std::get<3>(Shadow_distances_cockpit)); - - if (deferredRenderShipModel) { - model_render_params shadow_render_info; - shadow_render_info.set_detail_level_lock(0); - //If we just want to recieve, we still have to write to the color buffer but not to the zbuffer, otherwise shadow recieving breaks - shadow_render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | (Show_ship_casts_shadow ? 0 : MR_NO_ZBUFFER)); - shadow_render_info.set_object_number(OBJ_INDEX(objp)); - model_render_immediate(&shadow_render_info, sip->model_num, shipp->model_instance_num, &objp->orient, &eye_offset, MODEL_RENDER_OPAQUE); - } - if (renderCockpitModel) { - model_render_params shadow_render_info; - shadow_render_info.set_detail_level_lock(0); - shadow_render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING); - shadow_render_info.set_object_number(OBJ_INDEX(objp)); - vec3d offset = sip->cockpit_offset; - vm_vec_unrotate(&offset, &offset, &objp->orient); - if (!Disable_cockpit_sway) - offset += sip->cockpit_sway_val * objp->phys_info.acceleration; - model_render_immediate(&shadow_render_info, sip->cockpit_model_num, shipp->cockpit_model_instance, &objp->orient, &offset, MODEL_RENDER_OPAQUE); - } - - shadows_end_render(); - gr_clear_states(); - } - gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance_cockpit, Max_draw_distance); gr_set_view_matrix(&leaning_position, &eye_orient); Shadow_view_matrix_render = gr_view_matrix; + matrix4 shadow_view_light_backup = Shadow_view_matrix_light; + if (shadow_maybe_start_frame(Shadow_disable_overrides.disable_cockpit)) { + Shadow_override = false; + Shadow_view_matrix_light.a1d[12] = 0; + Shadow_view_matrix_light.a1d[13] = 0; + Shadow_view_matrix_light.a1d[14] = 0; + shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades); + } + if (light_deferredcockpit_enabled()) { gr_deferred_lighting_begin(true); @@ -8700,7 +8709,8 @@ void ship_render_player_ship(object* objp, const vec3d* cam_offset, const matrix leaning_position = leaning_backup; Proj_fov = fov_backup; - //Restore the Shadow_override + Shadow_view_matrix_light = shadow_view_light_backup; + shadow_end_frame(); gr_post_process_restore_zbuffer(); @@ -21767,8 +21777,6 @@ void ship_render_batch_thrusters(object *obj) ship *shipp = &Ships[num]; ship_info *sip = &Ship_info[Ships[num].ship_info_index]; - if ( Rendering_to_shadow_map ) return; - for (size_t i = 0; i < shipp->rcs_activity.size(); i++) { const auto mtp = &sip->rcs_thrusters[i]; @@ -21955,10 +21963,6 @@ void ship_render_weapon_models(model_render_params *ship_render_info, model_draw int ship_render_get_insignia(object* obj, ship* shipp) { - if ( Rendering_to_shadow_map ) { - return -1; - } - if ( Game_mode & GM_MULTIPLAYER ) { // if its any player's object int np_index = multi_find_player_by_object( obj ); @@ -21991,7 +21995,7 @@ int ship_render_get_insignia(object* obj, ship* shipp) void ship_render_set_animated_effect(model_render_params *render_info, ship *shipp, uint64_t * /*render_flags*/) { - if ( !shipp->shader_effect_timestamp.isValid() || Rendering_to_shadow_map ) { + if ( !shipp->shader_effect_timestamp.isValid() ) { return; } @@ -22027,7 +22031,7 @@ void ship_render(object* obj, model_draw_list* scene) ship_info *sip = &Ship_info[Ships[num].ship_info_index]; ship *warp_shipp = NULL; bool is_first_stage_arrival = false; - bool show_thrusters = (!shipp->flags[Ship_Flags::No_thrusters]) && !Rendering_to_shadow_map; + bool show_thrusters = (!shipp->flags[Ship_Flags::No_thrusters]); dock_function_info dfi; MONITOR_INC( NumShipsRend, 1 ); @@ -22065,7 +22069,7 @@ void ship_render(object* obj, model_draw_list* scene) } model_render_params render_info; - if ( obj == Viewer_obj && !Rendering_to_shadow_map ) { + if ( obj == Viewer_obj ) { if (!(Viewer_mode & VM_TOPDOWN)) { render_info.set_object_number(OBJ_INDEX(obj)); @@ -22082,7 +22086,7 @@ void ship_render(object* obj, model_draw_list* scene) model_instance_clear_arcs(pm, pmi); // Only render electrical arcs if within 500m of the eye (for a 10m piece) - if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f && !Rendering_to_shadow_map ) { + if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f ) { for (auto &arc: shipp->electrical_arcs) { if (arc.timestamp.isValid()) { model_instance_add_arc(pm, pmi, -1, &arc.endpoint_1, &arc.endpoint_2, arc.persistent_arc_points.get(), arc.type, &arc.primary_color_1, &arc.primary_color_2, &arc.secondary_color, arc.width, arc.segment_depth); @@ -22155,10 +22159,6 @@ void ship_render(object* obj, model_draw_list* scene) render_flags |= MR_NO_LIGHTING; } - if ( Rendering_to_shadow_map ) { - render_flags = MR_NO_TEXTURING | MR_NO_LIGHTING; - } - if (shipp->flags[Ship_Flags::Glowmaps_disabled]) { render_flags |= MR_NO_GLOWMAPS; } @@ -22228,7 +22228,7 @@ void ship_render(object* obj, model_draw_list* scene) model_render_queue(&render_info, scene, sip->model_num, &obj->orient, &obj->pos); } - if (shipp->shield_hits && !Rendering_to_shadow_map) { + if (shipp->shield_hits) { create_shield_explosion_all(obj); shipp->shield_hits = 0; } diff --git a/code/ship/ship.h b/code/ship/ship.h index e3d7e6663eb..b951ab131bb 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1695,6 +1695,8 @@ extern void change_ship_type(int n, int ship_type, int by_sexp = 0); extern void ship_process_pre( object * objp, float frametime ); extern void ship_process_post( object * objp, float frametime ); extern void ship_render( object * obj, model_draw_list * scene ); +extern bool ship_render_player_ship_casts_shadow_on_cockpit(); +extern bool ship_render_player_has_closeup_visuals(); extern void ship_render_player_ship( object * objp, const vec3d* offset = nullptr, const matrix* rot_offset = nullptr, const fov_t* fov_override = nullptr); extern void ship_delete( object * objp ); extern int ship_check_collision_fast( object * obj, object * other_obj, vec3d * hitpos ); diff --git a/code/ship/shipfx.cpp b/code/ship/shipfx.cpp index ef5b30531a0..f22e8f68c4f 100644 --- a/code/ship/shipfx.cpp +++ b/code/ship/shipfx.cpp @@ -19,6 +19,7 @@ #include "fireball/fireballs.h" #include "gamesequence/gamesequence.h" #include "gamesnd/gamesnd.h" +#include "graphics/shadows.h" #include "hud/hudmessage.h" #include "io/timer.h" #include "lighting/lighting.h" @@ -1556,10 +1557,6 @@ void shipfx_queue_render_ship_halves_and_debris(model_draw_list *scene, clip_shi // set up render flags uint64_t render_flags = MR_NORMAL; - if ( Rendering_to_shadow_map ) { - render_flags |= MR_NO_TEXTURING | MR_NO_LIGHTING; - } - if (shipp->flags[Ship::Ship_Flags::Glowmaps_disabled]) { render_flags |= MR_NO_GLOWMAPS; } @@ -2045,6 +2042,39 @@ void shipfx_large_blowup_queue_render(model_draw_list *scene, ship* shipp) } } +void shipfx_shadow_render_blowup(shadow_render_list* shadow_list, ship* shipp) +{ + Assert(shipp->large_ship_blowup_index > -1); + Assert(shipp->large_ship_blowup_index < (int)Split_ships.size()); + + split_ship* the_split_ship = &Split_ships[shipp->large_ship_blowup_index]; + Assert(the_split_ship->used); + + auto pmi = model_get_instance(shipp->model_instance_num); + auto pm = model_get(pmi->model_num); + + for (int half_idx = 0; half_idx < 2; half_idx++) { + clip_ship* half = (half_idx == 0) ? &the_split_ship->front_ship : &the_split_ship->back_ship; + if (half->length_left <= 0) continue; + + vec3d clip_plane_norm, orig_ship_world_center, model_clip_plane_pt; + vm_vec_unrotate(&clip_plane_norm, &half->clip_plane_norm, &half->orient); + vm_vec_unrotate(&orig_ship_world_center, &half->model_center_disp_to_orig_center, &half->orient); + vm_vec_add2(&orig_ship_world_center, &half->local_pivot); + + vec3d temp; + vm_vec_make(&temp, 0.0f, 0.0f, half->cur_clip_plane_pt); + vm_vec_unrotate(&model_clip_plane_pt, &temp, &half->orient); + vm_vec_add2(&model_clip_plane_pt, &orig_ship_world_center); + + shadow_render_list::clip_plane_info clip; + clip.normal = clip_plane_norm; + clip.position = model_clip_plane_pt; + + shadow_render_list::add_model_draws(shadow_list, pm, pmi, shipp->objnum, &half->local_pivot, &half->orient, &clip); + } +} + // ================== DO THE ELECTRIC ARCING STUFF ===================== // Creates any new ones, moves old ones. diff --git a/code/ship/shipfx.h b/code/ship/shipfx.h index 06db056443b..c5f2f43d79e 100644 --- a/code/ship/shipfx.h +++ b/code/ship/shipfx.h @@ -401,4 +401,8 @@ class WE_Hyperspace : public WarpEffect }; +class shadow_render_list; + +void shipfx_shadow_render_blowup(shadow_render_list* shadow_list, ship* shipp); + #endif diff --git a/code/source_groups.cmake b/code/source_groups.cmake index d1483a75dbc..9630776d723 100644 --- a/code/source_groups.cmake +++ b/code/source_groups.cmake @@ -266,6 +266,9 @@ add_file_folder("Default files\\\\data\\\\effects" def_files/data/effects/post-v.sdr def_files/data/effects/rocketui-f.sdr def_files/data/effects/rocketui-v.sdr + def_files/data/effects/shadow_map-f.sdr + def_files/data/effects/shadow_map-g.sdr + def_files/data/effects/shadow_map-v.sdr def_files/data/effects/shadows.sdr def_files/data/effects/shield-impact-v.sdr def_files/data/effects/shield-impact-f.sdr @@ -469,6 +472,7 @@ add_file_folder("Graphics" graphics/post_processing.h graphics/render.cpp graphics/render.h + graphics/render_queue.h graphics/shadows.cpp graphics/shadows.h graphics/tmapper.h diff --git a/freespace2/freespace.cpp b/freespace2/freespace.cpp index cf9d354e695..0803fb32098 100644 --- a/freespace2/freespace.cpp +++ b/freespace2/freespace.cpp @@ -3551,7 +3551,7 @@ void game_render_frame( camid cid, const vec3d* offset, const matrix* rot_offset stars_draw(1,1,1,0,0); } - shadows_render_all(Proj_fov, &Eye_matrix, &Eye_position); + shadows_render_all(Proj_fov, &Eye_matrix, &Eye_position, offset, rot_offset, fov_override); obj_render_queue_all(); // render all ships with shader effects on them diff --git a/lib/opengl/gl/glad/include/glad/glad.h b/lib/opengl/gl/glad/include/glad/glad.h index 49c1e57e088..300da24fe01 100644 --- a/lib/opengl/gl/glad/include/glad/glad.h +++ b/lib/opengl/gl/glad/include/glad/glad.h @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.36 on Mon May 25 23:56:14 2026. + OpenGL loader generated by glad 0.1.36 on Thu Jun 11 03:03:22 2026. Language/Generator: C/C++ Specification: gl @@ -13,6 +13,7 @@ GL_ARB_get_program_binary, GL_ARB_gpu_shader5, GL_ARB_shader_texture_lod, + GL_ARB_shader_viewport_layer_array, GL_ARB_texture_compression_bptc, GL_ARB_texture_storage, GL_ARB_timer_query, @@ -26,9 +27,9 @@ Reproducible: False Commandline: - --profile="core" --api="gl=4.3" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_buffer_storage,GL_ARB_debug_output,GL_ARB_draw_buffers_blend,GL_ARB_get_program_binary,GL_ARB_gpu_shader5,GL_ARB_shader_texture_lod,GL_ARB_texture_compression_bptc,GL_ARB_texture_storage,GL_ARB_timer_query,GL_ARB_vertex_attrib_binding,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_KHR_debug" + --profile="core" --api="gl=4.3" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_buffer_storage,GL_ARB_debug_output,GL_ARB_draw_buffers_blend,GL_ARB_get_program_binary,GL_ARB_gpu_shader5,GL_ARB_shader_texture_lod,GL_ARB_shader_viewport_layer_array,GL_ARB_texture_compression_bptc,GL_ARB_texture_storage,GL_ARB_timer_query,GL_ARB_vertex_attrib_binding,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_KHR_debug" Online: - https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D4.3&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_debug_output&extensions=GL_ARB_draw_buffers_blend&extensions=GL_ARB_get_program_binary&extensions=GL_ARB_gpu_shader5&extensions=GL_ARB_shader_texture_lod&extensions=GL_ARB_texture_compression_bptc&extensions=GL_ARB_texture_storage&extensions=GL_ARB_timer_query&extensions=GL_ARB_vertex_attrib_binding&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug + https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D4.3&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_debug_output&extensions=GL_ARB_draw_buffers_blend&extensions=GL_ARB_get_program_binary&extensions=GL_ARB_gpu_shader5&extensions=GL_ARB_shader_texture_lod&extensions=GL_ARB_shader_viewport_layer_array&extensions=GL_ARB_texture_compression_bptc&extensions=GL_ARB_texture_storage&extensions=GL_ARB_timer_query&extensions=GL_ARB_vertex_attrib_binding&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug */ @@ -3333,6 +3334,10 @@ GLAPI int GLAD_GL_ARB_gpu_shader5; #define GL_ARB_shader_texture_lod 1 GLAPI int GLAD_GL_ARB_shader_texture_lod; #endif +#ifndef GL_ARB_shader_viewport_layer_array +#define GL_ARB_shader_viewport_layer_array 1 +GLAPI int GLAD_GL_ARB_shader_viewport_layer_array; +#endif #ifndef GL_ARB_texture_compression_bptc #define GL_ARB_texture_compression_bptc 1 GLAPI int GLAD_GL_ARB_texture_compression_bptc; diff --git a/lib/opengl/gl/glad/src/glad.c b/lib/opengl/gl/glad/src/glad.c index c07bac7bf1e..0986c04d66d 100644 --- a/lib/opengl/gl/glad/src/glad.c +++ b/lib/opengl/gl/glad/src/glad.c @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.36 on Mon May 25 23:56:14 2026. + OpenGL loader generated by glad 0.1.36 on Thu Jun 11 03:03:22 2026. Language/Generator: C/C++ Specification: gl @@ -13,6 +13,7 @@ GL_ARB_get_program_binary, GL_ARB_gpu_shader5, GL_ARB_shader_texture_lod, + GL_ARB_shader_viewport_layer_array, GL_ARB_texture_compression_bptc, GL_ARB_texture_storage, GL_ARB_timer_query, @@ -26,9 +27,9 @@ Reproducible: False Commandline: - --profile="core" --api="gl=4.3" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_buffer_storage,GL_ARB_debug_output,GL_ARB_draw_buffers_blend,GL_ARB_get_program_binary,GL_ARB_gpu_shader5,GL_ARB_shader_texture_lod,GL_ARB_texture_compression_bptc,GL_ARB_texture_storage,GL_ARB_timer_query,GL_ARB_vertex_attrib_binding,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_KHR_debug" + --profile="core" --api="gl=4.3" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_buffer_storage,GL_ARB_debug_output,GL_ARB_draw_buffers_blend,GL_ARB_get_program_binary,GL_ARB_gpu_shader5,GL_ARB_shader_texture_lod,GL_ARB_shader_viewport_layer_array,GL_ARB_texture_compression_bptc,GL_ARB_texture_storage,GL_ARB_timer_query,GL_ARB_vertex_attrib_binding,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_KHR_debug" Online: - https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D4.3&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_debug_output&extensions=GL_ARB_draw_buffers_blend&extensions=GL_ARB_get_program_binary&extensions=GL_ARB_gpu_shader5&extensions=GL_ARB_shader_texture_lod&extensions=GL_ARB_texture_compression_bptc&extensions=GL_ARB_texture_storage&extensions=GL_ARB_timer_query&extensions=GL_ARB_vertex_attrib_binding&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug + https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D4.3&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_debug_output&extensions=GL_ARB_draw_buffers_blend&extensions=GL_ARB_get_program_binary&extensions=GL_ARB_gpu_shader5&extensions=GL_ARB_shader_texture_lod&extensions=GL_ARB_shader_viewport_layer_array&extensions=GL_ARB_texture_compression_bptc&extensions=GL_ARB_texture_storage&extensions=GL_ARB_timer_query&extensions=GL_ARB_vertex_attrib_binding&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug */ #include @@ -721,6 +722,7 @@ int GLAD_GL_ARB_draw_buffers_blend = 0; int GLAD_GL_ARB_get_program_binary = 0; int GLAD_GL_ARB_gpu_shader5 = 0; int GLAD_GL_ARB_shader_texture_lod = 0; +int GLAD_GL_ARB_shader_viewport_layer_array = 0; int GLAD_GL_ARB_texture_compression_bptc = 0; int GLAD_GL_ARB_texture_storage = 0; int GLAD_GL_ARB_timer_query = 0; @@ -1442,6 +1444,7 @@ static int find_extensionsGL(void) { GLAD_GL_ARB_get_program_binary = has_ext("GL_ARB_get_program_binary"); GLAD_GL_ARB_gpu_shader5 = has_ext("GL_ARB_gpu_shader5"); GLAD_GL_ARB_shader_texture_lod = has_ext("GL_ARB_shader_texture_lod"); + GLAD_GL_ARB_shader_viewport_layer_array = has_ext("GL_ARB_shader_viewport_layer_array"); GLAD_GL_ARB_texture_compression_bptc = has_ext("GL_ARB_texture_compression_bptc"); GLAD_GL_ARB_texture_storage = has_ext("GL_ARB_texture_storage"); GLAD_GL_ARB_timer_query = has_ext("GL_ARB_timer_query");