From d3922aa879ae28d3f74c0534ba7916e515b34146 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Tue, 9 Jun 2026 21:01:01 +0900 Subject: [PATCH 01/31] Extract shadows into own shader and render pass # Conflicts: # code/def_files/data/effects/main-g.sdr --- code/def_files/data/effects/main-f.sdr | 10 - code/def_files/data/effects/main-g.sdr | 68 +--- code/def_files/data/effects/main-v.sdr | 34 +- code/def_files/data/effects/shadow_map-f.sdr | 15 + code/def_files/data/effects/shadow_map-g.sdr | 60 +++ code/def_files/data/effects/shadow_map-v.sdr | 75 ++++ code/graphics/2d.h | 12 + code/graphics/grstub.cpp | 7 + code/graphics/material.cpp | 4 +- code/graphics/opengl/gropengl.cpp | 1 + code/graphics/opengl/gropenglshader.cpp | 12 + code/graphics/opengl/gropengltnl.cpp | 52 ++- code/graphics/opengl/gropengltnl.h | 3 + code/graphics/shadow_render_list.cpp | 382 +++++++++++++++++++ code/graphics/shadow_render_list.h | 93 +++++ code/graphics/shadows.cpp | 167 +++++--- code/graphics/uniforms.cpp | 6 - code/graphics/util/UniformBufferManager.cpp | 3 + code/graphics/util/uniform_structs.h | 12 + code/graphics/vulkan/vulkan_stubs.cpp | 8 + code/model/modelread.cpp | 2 +- code/model/modelrender.cpp | 4 +- code/ship/shipfx.cpp | 34 ++ code/ship/shipfx.h | 4 + code/source_groups.cmake | 5 + 25 files changed, 907 insertions(+), 166 deletions(-) create mode 100644 code/def_files/data/effects/shadow_map-f.sdr create mode 100644 code/def_files/data/effects/shadow_map-g.sdr create mode 100644 code/def_files/data/effects/shadow_map-v.sdr create mode 100644 code/graphics/shadow_render_list.cpp create mode 100644 code/graphics/shadow_render_list.h diff --git a/code/def_files/data/effects/main-f.sdr b/code/def_files/data/effects/main-f.sdr index 8a36e96d8e1..a9051e9f6ca 100644 --- a/code/def_files/data/effects/main-f.sdr +++ b/code/def_files/data/effects/main-f.sdr @@ -137,12 +137,10 @@ uniform sampler2DArray shadow_map; 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 +201,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; @@ -435,6 +427,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..5006c744335 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 @@ -108,14 +99,7 @@ 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; @@ -142,57 +126,7 @@ out VertexOutput { #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), diff --git a/code/def_files/data/effects/main-v.sdr b/code/def_files/data/effects/main-v.sdr index c3aa9ad74c1..e48ad7d5a89 100644 --- a/code/def_files/data/effects/main-v.sdr +++ b/code/def_files/data/effects/main-v.sdr @@ -107,16 +107,7 @@ 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; @@ -162,18 +153,7 @@ 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; @@ -197,9 +177,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 +185,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..0be768d8368 --- /dev/null +++ b/code/def_files/data/effects/shadow_map-f.sdr @@ -0,0 +1,15 @@ +//? #version 150 + +#include "shadows.sdr" + +in VertexOutputGeo { + vec4 position; + vec3 normal; +} vertIn; + +out vec4 fragOut0; + +void main() +{ + fragOut0 = vec4(vertIn.position.z, vertIn.position.z * vertIn.position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0); +} 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..acf14a50add --- /dev/null +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -0,0 +1,60 @@ +//? #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 = 4) in; +#endif + +layout (std140) uniform shadowMapData { + mat4 modelViewMatrix; + mat4 modelMatrix; + mat4 shadow_proj_matrix[4]; + vec4 clip_equation; + bool use_clip_plane; + int buffer_matrix_offset; +}; + +in VertexOutput { +#if !defined(GL_ARB_gpu_shader5) + float instance; +#endif + float clipModel; + vec3 normal; +} vertIn[]; + +out VertexOutputGeo { + vec4 position; + vec3 normal; +} vertOut; + +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++) + { + if (vertIn[vert].clipModel > 0.9) { + gl_Position = gl_in[vert].gl_Position; + } else { + gl_Position = shadow_proj_matrix[instanceID] * gl_in[vert].gl_Position; + } + + if(gl_Position.z < -1.0) + gl_Position.z = -1.0; + + vertOut.position = gl_in[vert].gl_Position; + vertOut.normal = vertIn[vert].normal; + + gl_Layer = instanceID; + 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..4f82e585287 --- /dev/null +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -0,0 +1,75 @@ +//? #version 150 +#extension GL_ARB_gpu_shader5: enable + +in vec4 vertPosition; +in vec4 vertTexCoord; +in vec3 vertNormal; +in vec4 vertTangent; +in float vertModelID; + +layout (std140) uniform shadowMapData { + mat4 modelViewMatrix; + mat4 modelMatrix; + mat4 shadow_proj_matrix[4]; + vec4 clip_equation; + bool use_clip_plane; + int buffer_matrix_offset; +}; + +uniform samplerBuffer transform_tex; + +out VertexOutput { +#if !defined(GL_ARB_gpu_shader5) + float instance; +#endif + float clipModel; + vec3 normal; +} vertOut; + +#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; + gl_Position = position; +#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); + } + + if (use_clip_plane) { + gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex); + } else { + gl_ClipDistance[0] = 1.0; + } + + vertOut.clipModel = clipModel ? 1.0 : 0.0; + vertOut.normal = normal; +} diff --git a/code/graphics/2d.h b/code/graphics/2d.h index 53dbc1980e2..09ea862e5fe 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 }; @@ -252,6 +254,7 @@ enum class uniform_block_type { Matrices = 6, MovieData = 7, GenericData = 8, + ShadowMapData = 9, NUM_BLOCK_TYPES }; @@ -843,6 +846,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::functiontex_buf[texi]; + if (datap->n_verts == 0) { + return; + } + + int shader_handle = gr_opengl_maybe_create_shader(SDR_TYPE_SHADOW_MAP_GEN, 0); + 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_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + 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; + GLint base_vertex = (GLint)(vert_src->Base_vertex_offset + buffer->vertex_num_offset); + + 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; @@ -694,6 +742,7 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light GLenum buffers[] = { GL_COLOR_ATTACHMENT0}; glDrawBuffers(1, buffers); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -774,6 +823,7 @@ void opengl_tnl_set_material(material* material_info, bool set_base_map, bool se GL_state.ClipDistance(0, false); } else { Assertion(Current_shader != nullptr && (Current_shader->shader == SDR_TYPE_MODEL + || Current_shader->shader == SDR_TYPE_SHADOW_MAP_GEN || Current_shader->shader == SDR_TYPE_DEFAULT_MATERIAL), "Clip planes are not supported by this shader!"); @@ -837,7 +887,7 @@ void opengl_tnl_set_model_material(model_material *material_info) gr_set_center_alpha(material_info->get_center_alpha()); - Assert( Current_shader->shader == SDR_TYPE_MODEL ); + Assert( Current_shader->shader == SDR_TYPE_MODEL || Current_shader->shader == SDR_TYPE_SHADOW_MAP_GEN ); GL_state.Texture.SetShaderMode(GL_TRUE); diff --git a/code/graphics/opengl/gropengltnl.h b/code/graphics/opengl/gropengltnl.h index ea83618da35..72bd5c024e6 100644 --- a/code/graphics/opengl/gropengltnl.h +++ b/code/graphics/opengl/gropengltnl.h @@ -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/shadow_render_list.cpp b/code/graphics/shadow_render_list.cpp new file mode 100644 index 00000000000..5ea2a919362 --- /dev/null +++ b/code/graphics/shadow_render_list.cpp @@ -0,0 +1,382 @@ +#include "graphics/shadow_render_list.h" + +#include "graphics/grinternal.h" +#include "graphics/matrix.h" +#include "graphics/shadows.h" +#include "model/model.h" +#include "model/modelrender.h" +#include "render/3d.h" + +extern float model_render_determine_depth(int obj_num, int model_num, const matrix* orient, const vec3d* pos, int detail_level_locked); +extern int model_render_determine_detail(float depth, int model_num, int detail_level_locked); +extern model_batch_buffer TransformBufferHandler; + +namespace { + +// Exact replica of scale_matrix from code/graphics/uniforms.cpp lines 15-27 +void scale_matrix(matrix4& mat, const vec3d& scale) +{ + mat.a2d[0][0] *= scale.xyz.x; + mat.a2d[0][1] *= scale.xyz.x; + mat.a2d[0][2] *= scale.xyz.x; + + mat.a2d[1][0] *= scale.xyz.y; + mat.a2d[1][1] *= scale.xyz.y; + mat.a2d[1][2] *= scale.xyz.y; + + mat.a2d[2][0] *= scale.xyz.z; + mat.a2d[2][1] *= scale.xyz.z; + mat.a2d[2][2] *= scale.xyz.z; +} + +// Exact mirror of graphics::uniforms::convert_model_material (lines 33-189), +// with fields not present in shadow_uniform_data removed. +// Line references in comments map to the original function in code/graphics/uniforms.cpp. +void convert_shadow_material(graphics::shadow_uniform_data* data_out, + const matrix4& model_transform, + const vec3d& scale, + size_t transform_buffer_offset) +{ + // Line 43: matrix4 scaled_matrix = model_transform; + matrix4 scaled_matrix = model_transform; + // Line 44: scale_matrix(scaled_matrix, scale); + scale_matrix(scaled_matrix, scale); + + // Line 46: data_out->modelMatrix = scaled_matrix; + data_out->modelMatrix = scaled_matrix; + // Lines 47-48: (viewMatrix removed) vm_matrix4_x_matrix4(&data_out->modelViewMatrix, &gr_view_matrix, &scaled_matrix); + vm_matrix4_x_matrix4(&data_out->modelViewMatrix, &gr_view_matrix, &scaled_matrix); + + // Lines 49-50: projMatrix, textureMatrix — not in shadow_uniform_data + + // Lines 52-66: color, vpwidth/height, effect, anim_timer, flags — not in shadow_uniform_data + + // Lines 68-73: texture indices — not in shadow_uniform_data + + // Lines 75-86: clip_equation, use_clip_plane — managed in build_uniform_buffer from batch_entry + + // Lines 88-110: lighting — not in shadow_uniform_data + + // Line 111: defaultGloss — not in shadow_uniform_data + + // Lines 113-166: texture map indices, blend, gammaSpec, alphaGloss — not in shadow_uniform_data + + // Lines 168-179: shadow_proj_matrix — filled in build_uniform_buffer (applied globally) + + // Line 181-182: (no is_batched guard) data_out->buffer_matrix_offset = (int) transform_buffer_offset; + data_out->buffer_matrix_offset = (int) transform_buffer_offset; + + // Lines 185+: team colors — not in shadow_uniform_data +} + +} // namespace + +shadow_render_list::shadow_render_list() +{ +} + +shadow_render_list::~shadow_render_list() +{ +} + +void shadow_render_list::reset() +{ + _render_elements.clear(); + _render_keys.clear(); +} + +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, + size_t transform_buffer_offset, + const clip_plane_info* clip) +{ + batch_entry entry; + + // Mirror of add_buffer_draw lines 610-614 (non-batched path) + entry.model_matrix = model_matrix; + entry.scale = scale; + entry.transform_buffer_offset = transform_buffer_offset; + + // Mirror of add_buffer_draw lines 616-622 + 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; + } + + // Mirror of add_buffer_draw lines 624-625 + _render_elements.push_back(entry); + _render_keys.push_back((int)(_render_elements.size() - 1)); +} + +// Mirror of model_draw_list::start_model_batch line 532-535 +void shadow_render_list::start_model_batch(int n_models) +{ + TransformBufferHandler.set_num_models(n_models); +} + +// Mirror of model_draw_list::add_submodel_to_batch lines 537-551 +void shadow_render_list::add_submodel_to_batch(int model_num) +{ + matrix4 transform; + + transform = _transform_stack.get_transform(); + + // set scale — mirror of lines 544-546 + const vec3d scale_identity = SCALE_IDENTITY_VECTOR; + vm_vec_scale(&transform.vec.rvec, scale_identity.xyz.x); + vm_vec_scale(&transform.vec.uvec, scale_identity.xyz.y); + vm_vec_scale(&transform.vec.fvec, scale_identity.xyz.z); + + // set visibility — mirror of line 549 + transform.a1d[15] = 0.0f; + + TransformBufferHandler.set_model_transform(transform, model_num); +} + +// Mirror of model_draw_list::sort_draw_pair — simplified since all draws use the same shader +bool shadow_render_list::sort_draw_pair(const shadow_render_list* target, const int a, const int b) +{ + auto* draw_call_a = &target->_render_elements[a]; + auto* draw_call_b = &target->_render_elements[b]; + + if (draw_call_a->flags != draw_call_b->flags) { + return draw_call_a->flags < draw_call_b->flags; + } + + return a < b; +} + +// Mirror of model_draw_list::sort_draws lines 526-530 — no-op allowed deviation +void shadow_render_list::sort_draws() +{ + // All shadow draws use the same shader — sorting is a no-op +} + +// Mirror of model_draw_list::build_uniform_buffer lines 881-911 +void shadow_render_list::build_uniform_buffer() +{ + GR_DEBUG_SCOPE("Build shadow uniform buffer"); + + _dataBuffer = gr_get_uniform_buffer(uniform_block_type::ShadowMapData, _render_keys.size()); + + for (auto render_index : _render_keys) { + auto& queued_draw = _render_elements[render_index]; + + // Mirror of lines 892-897: lighting setup (SIMPLIFIED - shadow materials are always unlit) + _scene_light_handler.resetLightState(); + + auto element = _dataBuffer.aligner().addTypedElement(); + // Mirror of lines 900-904 + convert_shadow_material(element, + queued_draw.model_matrix, + queued_draw.scale, + queued_draw.transform_buffer_offset); + + // Mirror of lines 168-179: shadow_proj_matrix + for (size_t i = 0; i < MAX_SHADOW_CASCADES; i++) { + element->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; + } + + // Mirror of lines 75-86: clip_equation, use_clip_plane + element->clip_equation = queued_draw.clip_equation; + element->use_clip_plane = queued_draw.has_clip_plane ? 1 : 0; + + // Mirror of line 905 + queued_draw.uniform_buffer_offset = _dataBuffer.getCurrentAlignerOffset(); + } + + // Mirror of line 910 + _dataBuffer.submitData(); +} + +// Mirror of model_draw_list::init_render lines 696-707 +void shadow_render_list::init_render(bool sort) +{ + if (sort) { + sort_draws(); + } + + // Mirror of line 702: TransformBufferHandler.submit_buffer_data(); + TransformBufferHandler.submit_buffer_data(); + + build_uniform_buffer(); + + _render_initialized = true; +} + +// Mirror of model_draw_list::render_buffer lines 628-637 +void shadow_render_list::render_buffer(size_t render_index) +{ + GR_DEBUG_SCOPE("Render shadow buffer"); + + auto& render_elements = _render_elements[render_index]; + + auto* datap = &render_elements.buffer->tex_buf[render_elements.texi]; + if (datap->n_verts == 0) { + return; + } + + gr_bind_uniform_buffer(uniform_block_type::ShadowMapData, render_elements.uniform_buffer_offset, + sizeof(graphics::shadow_uniform_data), _dataBuffer.bufferHandle()); + + gr_render_shadow_draw(_dataBuffer.bufferHandle(), + render_elements.uniform_buffer_offset, + sizeof(graphics::shadow_uniform_data), + render_elements.buffer, + const_cast(render_elements.vert_src), + render_elements.texi); +} + +// Mirror of model_draw_list::render_all lines 709-727 +void shadow_render_list::render_all() +{ + GR_DEBUG_SCOPE("Render shadow draw list"); + + Assertion(_render_initialized, "init_render must be called before any render_all call!"); + + _scene_light_handler.resetLightState(); + + for (size_t i = 0; i < _render_keys.size(); ++i) { + int render_index = _render_keys[i]; + + render_buffer(render_index); + } + + gr_alpha_mask_set(0, 1.0f); +} + +void shadow_render_list::push_transform(const vec3d* pos, const matrix* orient) +{ + _transform_stack.push(pos, orient); +} + +void shadow_render_list::pop_transform() +{ + _transform_stack.pop(); +} + +const matrix4& shadow_render_list::get_current_transform() const +{ + return _transform_stack.get_transform(); +} + +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) +{ + float depth = model_render_determine_depth(obj_num, pm->id, orient, pos, -1); + int detail_level = model_render_determine_detail(depth, pm->id, -1); + int detail_root = pm->detail[detail_level]; + + list->_transform_stack.clear(); + list->push_transform(pos, orient); + + const vec3d scale_identity = SCALE_IDENTITY_VECTOR; + + // Mirror of add_buffer_draw line 600: vm_matrix4_set_identity(&draw_data.transform); + matrix4 identity_4; + vm_matrix4_set_identity(&identity_4); + + // Mirror of model_render_queue lines 3001-3004: batched detail buffer + { + list->start_model_batch(pm->n_models); + size_t batch_offset = TransformBufferHandler.get_buffer_offset(); + + 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; + int base_tex = pm->maps[tmap_num].textures[TM_BASE_TYPE].GetTexture(); + + if (base_tex < 0) { + continue; + } + + list->add_draw(&pm->vert_source, &detail_buffer, j, identity_4, scale_identity, batch_offset, clip); + } + } + + // Mirror of model_render_queue lines 3006-3019: walk children + 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); + } + + i = pm->submodel[i].next_sibling; + } + + // Mirror of model_render_queue lines 3026-3038 hull render: + // model_render_buffers with mn=detail_model_num calls add_submodel_to_batch + // and returns (no draw calls since batching is active). + 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) +{ + bsp_info* sm = &pm->submodel[mn]; + submodel_instance* smi = nullptr; + + if (pmi != nullptr) { + smi = &pmi->submodel[mn]; + if (smi->blown_off) { + return; + } + } + + matrix submodel_orient = vmd_identity_matrix; + vec3d submodel_offset = sm->offset; + + const vec3d scale_identity = SCALE_IDENTITY_VECTOR; + + if (smi != nullptr) { + submodel_orient = smi->canonical_orient; + vm_vec_add2(&submodel_offset, &smi->canonical_offset); + } + + list->push_transform(&submodel_offset, &submodel_orient); + + // Mirror of model_render_children_buffers: add this submodel to the transform batch + 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); + } + + i = pm->submodel[i].next_sibling; + } + + list->pop_transform(); +} diff --git a/code/graphics/shadow_render_list.h b/code/graphics/shadow_render_list.h new file mode 100644 index 00000000000..d460ad9c368 --- /dev/null +++ b/code/graphics/shadow_render_list.h @@ -0,0 +1,93 @@ +#pragma once + +#include "globalincs/pstypes.h" +#include "graphics/util/uniform_structs.h" +#include "graphics/2d.h" +#include "graphics/util/UniformBuffer.h" +#include "lighting/lighting.h" +#include "matrix.h" + +class polymodel; +class polymodel_instance; + +class shadow_render_list { +public: + struct clip_plane_info { + vec3d normal; + vec3d position; + }; + + shadow_render_list(); + ~shadow_render_list(); + + void reset(); + + void add_draw(const indexed_vertex_source* vert_src, + vertex_buffer* buffer, + size_t texi, + const matrix4& model_matrix, + const vec3d& scale, + size_t transform_buffer_offset, + const clip_plane_info* clip); + + void push_transform(const vec3d* pos, const matrix* orient); + void pop_transform(); + const matrix4& get_current_transform() const; + + void start_model_batch(int n_models); + void add_submodel_to_batch(int model_num); + + void init_render(bool sort); + void render_all(); + void build_uniform_buffer(); + void render_buffer(size_t render_index); + + // Walk all submodels of a polymodel and add shadow draws, using the transform_stack + // for correct per-submodel world transforms. + 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); + +private: + struct batch_key { + const indexed_vertex_source* vert_src; + vertex_buffer* buffer; + size_t texi; + + bool operator<(const batch_key& other) const; + }; + + struct batch_entry { + size_t uniform_buffer_offset; + bool has_clip_plane; + vec4 clip_equation; + matrix4 model_matrix; + vec3d scale; + size_t transform_buffer_offset; + int flags; + const indexed_vertex_source* vert_src; + vertex_buffer* buffer; + size_t texi; + }; + + static void render_submodel_children(shadow_render_list* list, + polymodel* pm, + polymodel_instance* pmi, + int mn, + const clip_plane_info* clip); + + static bool sort_draw_pair(const shadow_render_list* target, const int a, const int b); + void sort_draws(); + + SCP_vector _render_elements; + SCP_vector _render_keys; + + graphics::util::UniformBuffer _dataBuffer; + transform_stack _transform_stack; + scene_lights _scene_light_handler; + + bool _render_initialized = false; +}; diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 3e86d2c709b..615fda080f3 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -12,12 +12,17 @@ #include "cmdline/cmdline.h" #include "debris/debris.h" #include "graphics/matrix.h" +#include "graphics/shadow_render_list.h" #include "lighting/lighting.h" #include "math/vecmat.h" #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" @@ -462,6 +467,56 @@ void shadows_end_render() gr_shadow_map_end(); } +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; +} + void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) { if (gr_screen.mode == GR_STUB) { @@ -479,81 +534,105 @@ 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)); + 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)); - model_draw_list scene; + shadow_render_list shadow_list; 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; + bool cull = true; 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) ) { cull = false; break; } } + if ( cull ) continue; - if ( cull ) { - continue; - } + switch (objp->type) { + case OBJ_SHIP: { + ship* shipp = &Ships[objp->instance]; - switch(objp->type) - { - case OBJ_RAW_POF: - case OBJ_PROP: - case OBJ_SHIP: - { - obj_queue_render(objp, &scene); + if (shipp->large_ship_blowup_index >= 0) { + shipfx_shadow_render_blowup(&shadow_list, shipp); + continue; } - 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); + + 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); } + + shadow_render_list::add_model_draws(&shadow_list, pm, pmi, OBJ_INDEX(objp), &objp->pos, &objp->orient, has_clip ? &clip : nullptr); break; + } + + 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); + } - case OBJ_DEBRIS: - { - debris *db; - db = &Debris[objp->instance]; + shadow_render_list::add_model_draws(&shadow_list, pm, pmi, OBJ_INDEX(objp), &objp->pos, &objp->orient, nullptr); + break; + } - 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); + 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); - objp = &Objects[db->objnum]; + shadow_render_list::add_model_draws(&shadow_list, pm, nullptr, OBJ_INDEX(objp), &objp->pos, &objp->orient, nullptr); + 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; + + shadow_render_list::add_model_draws(&shadow_list, pm, pmi, db->objnum, &debris_obj->pos, &debris_obj->orient, nullptr); + break; + } + + default: + break; } } - scene.init_render(); - scene.render_all(ZBUFFER_TYPE_FULL); + shadow_list.init_render(true); + shadow_list.render_all(); shadows_end_render(); diff --git a/code/graphics/uniforms.cpp b/code/graphics/uniforms.cpp index 1b2e586144f..a6bd5872f7a 100644 --- a/code/graphics/uniforms.cpp +++ b/code/graphics/uniforms.cpp @@ -178,12 +178,6 @@ void convert_model_material(model_uniform_data* data_out, 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; } diff --git a/code/graphics/util/UniformBufferManager.cpp b/code/graphics/util/UniformBufferManager.cpp index 5201a1129a4..f021a43dfd2 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: @@ -37,6 +39,7 @@ 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: diff --git a/code/graphics/util/uniform_structs.h b/code/graphics/util/uniform_structs.h index 454db36d1a0..bd30a007010 100644 --- a/code/graphics/util/uniform_structs.h +++ b/code/graphics/util/uniform_structs.h @@ -144,6 +144,18 @@ 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; // light view matrix (same for all draws in a pass) + matrix4 modelMatrix; // model-to-world (for clip plane) + matrix4 shadow_proj_matrix[4]; // cascade projection matrices + vec4 clip_equation; // xyz = normal, w = -dot(normal, point) + int use_clip_plane; // 1 if clip plane is active + int buffer_matrix_offset; // index into transform_tex + float pad[2]; // alignment to 16 bytes +}; + +const size_t shadow_uniform_data_size = sizeof(shadow_uniform_data); + 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..1dd5c05ec74 100644 --- a/code/graphics/vulkan/vulkan_stubs.cpp +++ b/code/graphics/vulkan/vulkan_stubs.cpp @@ -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/model/modelread.cpp b/code/model/modelread.cpp index c5f908f7c71..7c979c8c26f 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, 0); gr_maybe_create_shader(SDR_TYPE_MODEL, 0); diff --git a/code/model/modelrender.cpp b/code/model/modelrender.cpp index 681aac11e93..a31ce647ba0 100644 --- a/code/model/modelrender.cpp +++ b/code/model/modelrender.cpp @@ -579,9 +579,7 @@ 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... diff --git a/code/ship/shipfx.cpp b/code/ship/shipfx.cpp index ef5b30531a0..bc2cdbb4994 100644 --- a/code/ship/shipfx.cpp +++ b/code/ship/shipfx.cpp @@ -36,6 +36,7 @@ #include "prop/prop.h" #include "render/3d.h" // needed for View_position, which is used when playing a 3D sound #include "render/batching.h" +#include "graphics/shadow_render_list.h" #include "scripting/hook_api.h" #include "ship/ship.h" #include "ship/shiphit.h" @@ -2045,6 +2046,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..1ac0cf159a2 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 @@ -471,6 +474,8 @@ add_file_folder("Graphics" graphics/render.h graphics/shadows.cpp graphics/shadows.h + graphics/shadow_render_list.cpp + graphics/shadow_render_list.h graphics/tmapper.h graphics/uniforms.cpp graphics/uniforms.h From 4957b109eb1d0ece747a722ef3d3ebf6e514a495 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Tue, 9 Jun 2026 21:50:59 +0900 Subject: [PATCH 02/31] Consolidate render queue code --- code/graphics/render_queue.h | 155 ++++++++++++++++++++++ code/graphics/shadow_render_list.cpp | 191 ++++----------------------- code/graphics/shadow_render_list.h | 76 +++-------- code/model/modelrender.cpp | 142 ++++---------------- code/model/modelrender.h | 53 +------- code/source_groups.cmake | 1 + 6 files changed, 234 insertions(+), 384 deletions(-) create mode 100644 code/graphics/render_queue.h 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/shadow_render_list.cpp b/code/graphics/shadow_render_list.cpp index 5ea2a919362..4842c714ba0 100644 --- a/code/graphics/shadow_render_list.cpp +++ b/code/graphics/shadow_render_list.cpp @@ -1,19 +1,16 @@ #include "graphics/shadow_render_list.h" -#include "graphics/grinternal.h" +#include "graphics/util/uniform_structs.h" #include "graphics/matrix.h" #include "graphics/shadows.h" #include "model/model.h" #include "model/modelrender.h" -#include "render/3d.h" extern float model_render_determine_depth(int obj_num, int model_num, const matrix* orient, const vec3d* pos, int detail_level_locked); extern int model_render_determine_detail(float depth, int model_num, int detail_level_locked); -extern model_batch_buffer TransformBufferHandler; namespace { -// Exact replica of scale_matrix from code/graphics/uniforms.cpp lines 15-27 void scale_matrix(matrix4& mat, const vec3d& scale) { mat.a2d[0][0] *= scale.xyz.x; @@ -29,78 +26,37 @@ void scale_matrix(matrix4& mat, const vec3d& scale) mat.a2d[2][2] *= scale.xyz.z; } -// Exact mirror of graphics::uniforms::convert_model_material (lines 33-189), -// with fields not present in shadow_uniform_data removed. -// Line references in comments map to the original function in code/graphics/uniforms.cpp. void convert_shadow_material(graphics::shadow_uniform_data* data_out, const matrix4& model_transform, const vec3d& scale, size_t transform_buffer_offset) { - // Line 43: matrix4 scaled_matrix = model_transform; matrix4 scaled_matrix = model_transform; - // Line 44: scale_matrix(scaled_matrix, scale); + scale_matrix(scaled_matrix, scale); - // Line 46: data_out->modelMatrix = scaled_matrix; data_out->modelMatrix = scaled_matrix; - // Lines 47-48: (viewMatrix removed) vm_matrix4_x_matrix4(&data_out->modelViewMatrix, &gr_view_matrix, &scaled_matrix); - vm_matrix4_x_matrix4(&data_out->modelViewMatrix, &gr_view_matrix, &scaled_matrix); - - // Lines 49-50: projMatrix, textureMatrix — not in shadow_uniform_data - - // Lines 52-66: color, vpwidth/height, effect, anim_timer, flags — not in shadow_uniform_data - - // Lines 68-73: texture indices — not in shadow_uniform_data - - // Lines 75-86: clip_equation, use_clip_plane — managed in build_uniform_buffer from batch_entry - - // Lines 88-110: lighting — not in shadow_uniform_data - - // Line 111: defaultGloss — not in shadow_uniform_data - // Lines 113-166: texture map indices, blend, gammaSpec, alphaGloss — not in shadow_uniform_data - - // Lines 168-179: shadow_proj_matrix — filled in build_uniform_buffer (applied globally) + vm_matrix4_x_matrix4(&data_out->modelViewMatrix, &gr_view_matrix, &scaled_matrix); - // Line 181-182: (no is_batched guard) data_out->buffer_matrix_offset = (int) transform_buffer_offset; data_out->buffer_matrix_offset = (int) transform_buffer_offset; - - // Lines 185+: team colors — not in shadow_uniform_data } } // namespace -shadow_render_list::shadow_render_list() -{ -} - -shadow_render_list::~shadow_render_list() -{ -} - -void shadow_render_list::reset() -{ - _render_elements.clear(); - _render_keys.clear(); -} - 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, - size_t transform_buffer_offset, const clip_plane_info* clip) { - batch_entry entry; + shadow_batch_entry entry; - // Mirror of add_buffer_draw lines 610-614 (non-batched path) entry.model_matrix = model_matrix; entry.scale = scale; - entry.transform_buffer_offset = transform_buffer_offset; + entry.transform_buffer_offset = _batchBuffer.get_buffer_offset(); - // Mirror of add_buffer_draw lines 616-622 entry.flags = 0; entry.vert_src = vert_src; entry.buffer = buffer; @@ -119,41 +75,13 @@ void shadow_render_list::add_draw(const indexed_vertex_source* vert_src, entry.clip_equation.xyzw.w = 0.0f; } - // Mirror of add_buffer_draw lines 624-625 - _render_elements.push_back(entry); - _render_keys.push_back((int)(_render_elements.size() - 1)); -} - -// Mirror of model_draw_list::start_model_batch line 532-535 -void shadow_render_list::start_model_batch(int n_models) -{ - TransformBufferHandler.set_num_models(n_models); -} - -// Mirror of model_draw_list::add_submodel_to_batch lines 537-551 -void shadow_render_list::add_submodel_to_batch(int model_num) -{ - matrix4 transform; - - transform = _transform_stack.get_transform(); - - // set scale — mirror of lines 544-546 - const vec3d scale_identity = SCALE_IDENTITY_VECTOR; - vm_vec_scale(&transform.vec.rvec, scale_identity.xyz.x); - vm_vec_scale(&transform.vec.uvec, scale_identity.xyz.y); - vm_vec_scale(&transform.vec.fvec, scale_identity.xyz.z); - - // set visibility — mirror of line 549 - transform.a1d[15] = 0.0f; - - TransformBufferHandler.set_model_transform(transform, model_num); + push_element(std::move(entry)); } -// Mirror of model_draw_list::sort_draw_pair — simplified since all draws use the same shader -bool shadow_render_list::sort_draw_pair(const shadow_render_list* target, const int a, const int b) +bool shadow_render_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->flags != draw_call_b->flags) { return draw_call_a->flags < draw_call_b->flags; @@ -162,118 +90,54 @@ bool shadow_render_list::sort_draw_pair(const shadow_render_list* target, const return a < b; } -// Mirror of model_draw_list::sort_draws lines 526-530 — no-op allowed deviation -void shadow_render_list::sort_draws() -{ - // All shadow draws use the same shader — sorting is a no-op -} - -// Mirror of model_draw_list::build_uniform_buffer lines 881-911 void shadow_render_list::build_uniform_buffer() { GR_DEBUG_SCOPE("Build shadow uniform buffer"); - _dataBuffer = gr_get_uniform_buffer(uniform_block_type::ShadowMapData, _render_keys.size()); + _dataBuffer = gr_get_uniform_buffer(uniform_block_type::ShadowMapData, _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]; - // Mirror of lines 892-897: lighting setup (SIMPLIFIED - shadow materials are always unlit) - _scene_light_handler.resetLightState(); + _lights.resetLightState(); auto element = _dataBuffer.aligner().addTypedElement(); - // Mirror of lines 900-904 convert_shadow_material(element, queued_draw.model_matrix, queued_draw.scale, queued_draw.transform_buffer_offset); - // Mirror of lines 168-179: shadow_proj_matrix for (size_t i = 0; i < MAX_SHADOW_CASCADES; i++) { element->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; } - // Mirror of lines 75-86: clip_equation, use_clip_plane element->clip_equation = queued_draw.clip_equation; element->use_clip_plane = queued_draw.has_clip_plane ? 1 : 0; - // Mirror of line 905 queued_draw.uniform_buffer_offset = _dataBuffer.getCurrentAlignerOffset(); } - // Mirror of line 910 _dataBuffer.submitData(); } -// Mirror of model_draw_list::init_render lines 696-707 -void shadow_render_list::init_render(bool sort) -{ - if (sort) { - sort_draws(); - } - - // Mirror of line 702: TransformBufferHandler.submit_buffer_data(); - TransformBufferHandler.submit_buffer_data(); - - build_uniform_buffer(); - - _render_initialized = true; -} - -// Mirror of model_draw_list::render_buffer lines 628-637 -void shadow_render_list::render_buffer(size_t render_index) +void shadow_render_list::render_buffer(const shadow_batch_entry& entry) { GR_DEBUG_SCOPE("Render shadow buffer"); - auto& render_elements = _render_elements[render_index]; - - auto* datap = &render_elements.buffer->tex_buf[render_elements.texi]; + auto* datap = &entry.buffer->tex_buf[entry.texi]; if (datap->n_verts == 0) { return; } - gr_bind_uniform_buffer(uniform_block_type::ShadowMapData, render_elements.uniform_buffer_offset, + 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(), - render_elements.uniform_buffer_offset, + entry.uniform_buffer_offset, sizeof(graphics::shadow_uniform_data), - render_elements.buffer, - const_cast(render_elements.vert_src), - render_elements.texi); -} - -// Mirror of model_draw_list::render_all lines 709-727 -void shadow_render_list::render_all() -{ - GR_DEBUG_SCOPE("Render shadow draw list"); - - Assertion(_render_initialized, "init_render must be called before any render_all call!"); - - _scene_light_handler.resetLightState(); - - for (size_t i = 0; i < _render_keys.size(); ++i) { - int render_index = _render_keys[i]; - - render_buffer(render_index); - } - - gr_alpha_mask_set(0, 1.0f); -} - -void shadow_render_list::push_transform(const vec3d* pos, const matrix* orient) -{ - _transform_stack.push(pos, orient); -} - -void shadow_render_list::pop_transform() -{ - _transform_stack.pop(); -} - -const matrix4& shadow_render_list::get_current_transform() const -{ - return _transform_stack.get_transform(); + entry.buffer, + const_cast(entry.vert_src), + entry.texi); } void shadow_render_list::add_model_draws(shadow_render_list* list, @@ -287,19 +151,17 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, int detail_level = model_render_determine_detail(depth, pm->id, -1); int detail_root = pm->detail[detail_level]; - list->_transform_stack.clear(); + list->clear_transforms(); list->push_transform(pos, orient); + //TODO This might be incorrect for some models const vec3d scale_identity = SCALE_IDENTITY_VECTOR; - // Mirror of add_buffer_draw line 600: vm_matrix4_set_identity(&draw_data.transform); matrix4 identity_4; vm_matrix4_set_identity(&identity_4); - // Mirror of model_render_queue lines 3001-3004: batched detail buffer { list->start_model_batch(pm->n_models); - size_t batch_offset = TransformBufferHandler.get_buffer_offset(); auto& detail_buffer = pm->detail_buffers[detail_level]; for (size_t j = 0; j < detail_buffer.tex_buf.size(); j++) { @@ -314,11 +176,10 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, continue; } - list->add_draw(&pm->vert_source, &detail_buffer, j, identity_4, scale_identity, batch_offset, clip); + list->add_draw(&pm->vert_source, &detail_buffer, j, identity_4, scale_identity, clip); } } - // Mirror of model_render_queue lines 3006-3019: walk children int i = pm->submodel[detail_root].first_child; while (i >= 0) { @@ -329,9 +190,6 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, i = pm->submodel[i].next_sibling; } - // Mirror of model_render_queue lines 3026-3038 hull render: - // model_render_buffers with mn=detail_model_num calls add_submodel_to_batch - // and returns (no draw calls since batching is active). list->add_submodel_to_batch(detail_root); list->pop_transform(); @@ -356,8 +214,6 @@ void shadow_render_list::render_submodel_children(shadow_render_list* list, matrix submodel_orient = vmd_identity_matrix; vec3d submodel_offset = sm->offset; - const vec3d scale_identity = SCALE_IDENTITY_VECTOR; - if (smi != nullptr) { submodel_orient = smi->canonical_orient; vm_vec_add2(&submodel_offset, &smi->canonical_offset); @@ -365,7 +221,6 @@ void shadow_render_list::render_submodel_children(shadow_render_list* list, list->push_transform(&submodel_offset, &submodel_orient); - // Mirror of model_render_children_buffers: add this submodel to the transform batch list->add_submodel_to_batch(mn); // Recurse into children diff --git a/code/graphics/shadow_render_list.h b/code/graphics/shadow_render_list.h index d460ad9c368..d65e7044257 100644 --- a/code/graphics/shadow_render_list.h +++ b/code/graphics/shadow_render_list.h @@ -1,49 +1,44 @@ #pragma once #include "globalincs/pstypes.h" -#include "graphics/util/uniform_structs.h" +#include "graphics/render_queue.h" #include "graphics/2d.h" -#include "graphics/util/UniformBuffer.h" -#include "lighting/lighting.h" #include "matrix.h" class polymodel; class polymodel_instance; -class shadow_render_list { +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(); - - void reset(); + shadow_render_list() = default; + ~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, - size_t transform_buffer_offset, const clip_plane_info* clip); - void push_transform(const vec3d* pos, const matrix* orient); - void pop_transform(); - const matrix4& get_current_transform() const; - - void start_model_batch(int n_models); - void add_submodel_to_batch(int model_num); - - void init_render(bool sort); - void render_all(); - void build_uniform_buffer(); - void render_buffer(size_t render_index); - - // Walk all submodels of a polymodel and add shadow draws, using the transform_stack - // for correct per-submodel world transforms. static void add_model_draws(shadow_render_list* list, polymodel* pm, polymodel_instance* pmi, @@ -52,42 +47,15 @@ class shadow_render_list { const clip_plane_info* clip); private: - struct batch_key { - const indexed_vertex_source* vert_src; - vertex_buffer* buffer; - size_t texi; - - bool operator<(const batch_key& other) const; - }; + void build_uniform_buffer(); + void render_buffer(const shadow_batch_entry& entry); + bool sort_draw_pair(int a, int b) const; - struct batch_entry { - size_t uniform_buffer_offset; - bool has_clip_plane; - vec4 clip_equation; - matrix4 model_matrix; - vec3d scale; - size_t transform_buffer_offset; - int flags; - const indexed_vertex_source* vert_src; - vertex_buffer* buffer; - size_t texi; - }; + void sort_draws() {} static void render_submodel_children(shadow_render_list* list, polymodel* pm, polymodel_instance* pmi, int mn, const clip_plane_info* clip); - - static bool sort_draw_pair(const shadow_render_list* target, const int a, const int b); - void sort_draws(); - - SCP_vector _render_elements; - SCP_vector _render_keys; - - graphics::util::UniformBuffer _dataBuffer; - transform_stack _transform_stack; - scene_lights _scene_light_handler; - - bool _render_initialized = false; }; diff --git a/code/model/modelrender.cpp b/code/model/modelrender.cpp index a31ce647ba0..54e9ee4c3fa 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) @@ -580,14 +535,10 @@ void model_draw_list::add_buffer_draw(const model_material *render_material, con draw_data.render_material = *render_material; { - // 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(); } @@ -603,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); } @@ -621,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) @@ -639,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; @@ -658,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) @@ -711,15 +623,15 @@ 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!"); + Assertion(_initialized, "init_render must be called before any render_all call!"); - Scene_light_handler.resetLightState(); + _lights.resetLightState(); - for ( size_t i = 0; i < Render_keys.size(); ++i ) { - int render_index = Render_keys[i]; + for ( size_t i = 0; i < _keys.size(); ++i ) { + int render_index = _keys[i]; - if ( depth_mode == ZBUFFER_TYPE_DEFAULT || Render_elements[render_index].render_material.get_depth_mode() == depth_mode ) { - render_buffer(Render_elements[render_index]); + if ( depth_mode == ZBUFFER_TYPE_DEFAULT || _elements[render_index].render_material.get_depth_mode() == depth_mode ) { + render_buffer(_elements[render_index]); } } @@ -750,7 +662,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; @@ -798,7 +710,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); } @@ -827,10 +739,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; @@ -883,17 +795,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(); diff --git a/code/model/modelrender.h b/code/model/modelrender.h index 8586373639f..014ed1f5a94 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" @@ -249,57 +250,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(); +class model_draw_list : public render_queue { + friend class render_queue; - void add_matrix(const matrix4 &mat); -}; - -class model_draw_list -{ - vec3d Current_scale; - transform_stack Transformations; - - 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 +278,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 +293,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); diff --git a/code/source_groups.cmake b/code/source_groups.cmake index 1ac0cf159a2..2347654fd45 100644 --- a/code/source_groups.cmake +++ b/code/source_groups.cmake @@ -472,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/shadow_render_list.cpp From 0de6a88aee2478658a1d56f2ae2c56b83518c44e Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Tue, 9 Jun 2026 23:38:42 +0900 Subject: [PATCH 03/31] More cleanup --- code/graphics/shadow_render_list.cpp | 237 --------------------------- code/graphics/shadow_render_list.h | 61 ------- code/graphics/shadows.cpp | 196 +++++++++++++++++++++- code/graphics/shadows.h | 53 ++++++ code/graphics/uniforms.cpp | 16 ++ code/graphics/uniforms.h | 5 + code/model/modelrender.h | 2 + code/ship/shipfx.cpp | 2 +- code/source_groups.cmake | 2 - 9 files changed, 272 insertions(+), 302 deletions(-) delete mode 100644 code/graphics/shadow_render_list.cpp delete mode 100644 code/graphics/shadow_render_list.h diff --git a/code/graphics/shadow_render_list.cpp b/code/graphics/shadow_render_list.cpp deleted file mode 100644 index 4842c714ba0..00000000000 --- a/code/graphics/shadow_render_list.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "graphics/shadow_render_list.h" - -#include "graphics/util/uniform_structs.h" -#include "graphics/matrix.h" -#include "graphics/shadows.h" -#include "model/model.h" -#include "model/modelrender.h" - -extern float model_render_determine_depth(int obj_num, int model_num, const matrix* orient, const vec3d* pos, int detail_level_locked); -extern int model_render_determine_detail(float depth, int model_num, int detail_level_locked); - -namespace { - -void scale_matrix(matrix4& mat, const vec3d& scale) -{ - mat.a2d[0][0] *= scale.xyz.x; - mat.a2d[0][1] *= scale.xyz.x; - mat.a2d[0][2] *= scale.xyz.x; - - mat.a2d[1][0] *= scale.xyz.y; - mat.a2d[1][1] *= scale.xyz.y; - mat.a2d[1][2] *= scale.xyz.y; - - mat.a2d[2][0] *= scale.xyz.z; - mat.a2d[2][1] *= scale.xyz.z; - mat.a2d[2][2] *= scale.xyz.z; -} - -void convert_shadow_material(graphics::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; -} - -} // namespace - -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(); - convert_shadow_material(element, - queued_draw.model_matrix, - queued_draw.scale, - queued_draw.transform_buffer_offset); - - for (size_t i = 0; i < MAX_SHADOW_CASCADES; i++) { - element->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; - } - - 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) -{ - float depth = model_render_determine_depth(obj_num, pm->id, orient, pos, -1); - int detail_level = model_render_determine_detail(depth, pm->id, -1); - int detail_root = pm->detail[detail_level]; - - 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; - int base_tex = pm->maps[tmap_num].textures[TM_BASE_TYPE].GetTexture(); - - if (base_tex < 0) { - 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); - } - - i = pm->submodel[i].next_sibling; - } - - 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) -{ - bsp_info* sm = &pm->submodel[mn]; - submodel_instance* smi = nullptr; - - if (pmi != nullptr) { - smi = &pmi->submodel[mn]; - if (smi->blown_off) { - 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); - } - - i = pm->submodel[i].next_sibling; - } - - list->pop_transform(); -} diff --git a/code/graphics/shadow_render_list.h b/code/graphics/shadow_render_list.h deleted file mode 100644 index d65e7044257..00000000000 --- a/code/graphics/shadow_render_list.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "globalincs/pstypes.h" -#include "graphics/render_queue.h" -#include "graphics/2d.h" -#include "matrix.h" - -class polymodel; -class polymodel_instance; - -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() = default; - ~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); - -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); -}; diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 615fda080f3..0ddb1dc0e54 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -8,11 +8,12 @@ */ #include "graphics/shadows.h" + +#include "graphics/uniforms.h" #include "asteroid/asteroid.h" #include "cmdline/cmdline.h" #include "debris/debris.h" #include "graphics/matrix.h" -#include "graphics/shadow_render_list.h" #include "lighting/lighting.h" #include "math/vecmat.h" #include "mod_table/mod_table.h" @@ -25,6 +26,7 @@ #include "ship/shipfx.h" #include "render/3d.h" #include "tracing/tracing.h" +#include "util/uniform_structs.h" extern vec3d check_offsets[8]; @@ -664,4 +666,196 @@ void shadow_end_frame() { Shadow_override = shadow_override_backup; last_override = false; } +} + +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); + + for (size_t i = 0; i < MAX_SHADOW_CASCADES; i++) { + element->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; + } + + 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) +{ + float depth = model_render_determine_depth(obj_num, pm->id, orient, pos, -1); + int detail_level = model_render_determine_detail(depth, pm->id, -1); + int detail_root = pm->detail[detail_level]; + + 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; + int base_tex = pm->maps[tmap_num].textures[TM_BASE_TYPE].GetTexture(); + + if (base_tex < 0) { + 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); + } + + i = pm->submodel[i].next_sibling; + } + + 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) +{ + bsp_info* sm = &pm->submodel[mn]; + submodel_instance* smi = nullptr; + + if (pmi != nullptr) { + smi = &pmi->submodel[mn]; + if (smi->blown_off) { + 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); + } + + 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..a96c50d8af2 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -10,6 +10,7 @@ #ifndef _SHADOWS_H #define _SHADOWS_H +#include "graphics/render_queue.h" #include "globalincs/pstypes.h" #include "object/object.h" #include "render/3d.h" @@ -58,4 +59,56 @@ 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() = default; + ~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); + +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); +}; + #endif diff --git a/code/graphics/uniforms.cpp b/code/graphics/uniforms.cpp index a6bd5872f7a..cdce2d7a69f 100644 --- a/code/graphics/uniforms.cpp +++ b/code/graphics/uniforms.cpp @@ -234,5 +234,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/model/modelrender.h b/code/model/modelrender.h index 014ed1f5a94..43098f7ab36 100644 --- a/code/model/modelrender.h +++ b/code/model/modelrender.h @@ -310,6 +310,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/ship/shipfx.cpp b/code/ship/shipfx.cpp index bc2cdbb4994..415badf31ea 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" @@ -36,7 +37,6 @@ #include "prop/prop.h" #include "render/3d.h" // needed for View_position, which is used when playing a 3D sound #include "render/batching.h" -#include "graphics/shadow_render_list.h" #include "scripting/hook_api.h" #include "ship/ship.h" #include "ship/shiphit.h" diff --git a/code/source_groups.cmake b/code/source_groups.cmake index 2347654fd45..9630776d723 100644 --- a/code/source_groups.cmake +++ b/code/source_groups.cmake @@ -475,8 +475,6 @@ add_file_folder("Graphics" graphics/render_queue.h graphics/shadows.cpp graphics/shadows.h - graphics/shadow_render_list.cpp - graphics/shadow_render_list.h graphics/tmapper.h graphics/uniforms.cpp graphics/uniforms.h From f317900c56482b4f9f09bfd6551c03ad2195843d Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Thu, 11 Jun 2026 08:35:47 +0900 Subject: [PATCH 04/31] Fix missing clear of shadow buffer inter frame --- code/graphics/shadows.cpp | 7 ++++++- code/graphics/shadows.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 0ddb1dc0e54..7ba2d789cf9 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -546,6 +546,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) std::get<2>(Shadow_distances), std::get<3>(Shadow_distances)); shadow_render_list shadow_list; + object *objp = Objects; for ( int i = 0; i <= Highest_object_index; i++, objp++ ) { @@ -633,7 +634,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) } } - shadow_list.init_render(true); + shadow_list.init_render(false); shadow_list.render_all(); shadows_end_render(); @@ -668,6 +669,10 @@ void shadow_end_frame() { } } +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, diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index a96c50d8af2..c9f3c86b8aa 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -80,7 +80,7 @@ class shadow_render_list : public render_queue Date: Thu, 11 Jun 2026 11:54:10 +0900 Subject: [PATCH 05/31] Request capability for viewport layer rendering --- code/graphics/2d.cpp | 1 + code/graphics/2d.h | 3 ++- code/graphics/opengl/gropengl.cpp | 2 ++ lib/opengl/gl/glad/include/glad/glad.h | 11 ++++++++--- lib/opengl/gl/glad/src/glad.c | 9 ++++++--- 5 files changed, 19 insertions(+), 7 deletions(-) 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 09ea862e5fe..8c4c5604ec5 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -342,7 +342,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 { diff --git a/code/graphics/opengl/gropengl.cpp b/code/graphics/opengl/gropengl.cpp index 4fa4f8eb05d..3e6a943c0b2 100644 --- a/code/graphics/opengl/gropengl.cpp +++ b/code/graphics/opengl/gropengl.cpp @@ -1561,6 +1561,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; } 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"); From cbf0934b8fbf8f2851449204bdbef68a48951cca Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Thu, 11 Jun 2026 16:40:14 +0900 Subject: [PATCH 06/31] instanced shadows --- code/def_files/data/effects/shadow_map-f.sdr | 11 +++++++++ code/def_files/data/effects/shadow_map-v.sdr | 24 ++++++++++++++++++-- code/graphics/2d.h | 2 ++ code/graphics/opengl/gropengl.cpp | 3 ++- code/graphics/opengl/gropenglshader.cpp | 12 ++++------ code/graphics/opengl/gropengltnl.cpp | 22 +++++++++++++----- 6 files changed, 57 insertions(+), 17 deletions(-) diff --git a/code/def_files/data/effects/shadow_map-f.sdr b/code/def_files/data/effects/shadow_map-f.sdr index 0be768d8368..c2a0c6b0298 100644 --- a/code/def_files/data/effects/shadow_map-f.sdr +++ b/code/def_files/data/effects/shadow_map-f.sdr @@ -2,14 +2,25 @@ #include "shadows.sdr" +#ifdef GEOMETRY_FALLBACK in VertexOutputGeo { vec4 position; vec3 normal; } vertIn; +#else +in VertexOutput { + vec3 normal; +} vertIn; +#endif + out vec4 fragOut0; void main() { +#ifdef GEOMETRY_FALLBACK fragOut0 = vec4(vertIn.position.z, vertIn.position.z * vertIn.position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0); +#else + fragOut0 = vec4(gl_Position.z, gl_Position.z * gl_Position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0); +#endif } diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index 4f82e585287..f43d177bc95 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -1,6 +1,10 @@ //? #version 150 #extension GL_ARB_gpu_shader5: enable +#ifndef GEOMETRY_FALLBACK +#extension GL_ARB_shader_viewport_layer_array : require +#endif + in vec4 vertPosition; in vec4 vertTexCoord; in vec3 vertNormal; @@ -22,7 +26,9 @@ out VertexOutput { #if !defined(GL_ARB_gpu_shader5) float instance; #endif +#ifdef GEOMETRY_FALLBACK float clipModel; +#endif vec3 normal; } vertOut; @@ -51,7 +57,6 @@ void main() normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal); position = modelViewMatrix * orient * vertex; - gl_Position = position; #if !defined(GL_ARB_gpu_shader5) #ifdef APPLE vertOut.instance = float(gl_InstanceIDARB); @@ -63,6 +68,15 @@ void main() if (clipModel) { gl_Position = vec4(vec3(-2.0), 1.0); } +#ifndef GEOMETRY_FALLBACK + else { + position = shadow_proj_matrix[gl_InstanceID] * position + if(position.z < -1.0) + position.z = -1.0; + } +#endif + + gl_Position = position; if (use_clip_plane) { gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex); @@ -70,6 +84,12 @@ void main() gl_ClipDistance[0] = 1.0; } - vertOut.clipModel = clipModel ? 1.0 : 0.0; + vertOut.normal = normal; + +#ifdef GEOMETRY_FALLBACK + vertOut.clipModel = clipModel ? 1.0 : 0.0; +#else + gl_Layer = gl_InstanceID; +#endif } diff --git a/code/graphics/2d.h b/code/graphics/2d.h index 8c4c5604ec5..817e7aca5a8 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -243,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, diff --git a/code/graphics/opengl/gropengl.cpp b/code/graphics/opengl/gropengl.cpp index 3e6a943c0b2..19a38e1a826 100644 --- a/code/graphics/opengl/gropengl.cpp +++ b/code/graphics/opengl/gropengl.cpp @@ -1545,6 +1545,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: @@ -1562,7 +1563,7 @@ bool gr_opengl_is_capable(gr_capability capability) 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; + return GLAD_GL_ARB_vertex_attrib_binding && GLAD_GL_ARB_shader_viewport_layer_array && GL_ARB_gpu_shader5; } diff --git a/code/graphics/opengl/gropenglshader.cpp b/code/graphics/opengl/gropenglshader.cpp index ac8ff391983..5170c2d15a7 100644 --- a/code/graphics/opengl/gropenglshader.cpp +++ b/code/graphics/opengl/gropenglshader.cpp @@ -220,7 +220,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); @@ -836,13 +839,6 @@ void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_sha break; } } - - // TODO: This is an ugly hardcoded override. The shadow map generation shader always needs its - // geometry shader for cascade instancing. Ideally this should use a proper variant system - // (e.g. a variant with flag=0 that always matches for this shader type). - if (!use_geo_sdr && sdr == SDR_TYPE_SHADOW_MAP_GEN) { - use_geo_sdr = true; - } } auto vert_content = diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index bd5e73d6352..6e5c80932a6 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -654,7 +654,7 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset return; } - int shader_handle = gr_opengl_maybe_create_shader(SDR_TYPE_SHADOW_MAP_GEN, 0); + 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); @@ -685,11 +685,21 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset GLenum element_type = (datap->flags & VB_FLAG_LARGE_INDEX) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; GLint base_vertex = (GLint)(vert_src->Base_vertex_offset + buffer->vertex_num_offset); - glDrawElementsBaseVertex(GL_TRIANGLES, - (GLsizei)datap->n_verts, - element_type, - ibuffer + datap->index_offset, - base_vertex); + if (gr_is_capable(gr_capability::CAPABILITY_FAST_SHADOWS)) { + glDrawElementsInstancedBaseVertex(GL_TRIANGLES, + (GLsizei)datap->n_verts, + element_type, + ibuffer + datap->index_offset, + 4, + base_vertex); + } + else { + glDrawElementsBaseVertex(GL_TRIANGLES, + (GLsizei)datap->n_verts, + element_type, + ibuffer + datap->index_offset, + base_vertex); + } GL_state.Texture.SetShaderMode(GL_FALSE); } From 97355db18de0984766a870af59647d8270be2f5e Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Thu, 11 Jun 2026 18:30:59 +0900 Subject: [PATCH 07/31] Fix issues --- code/def_files/data/effects/shadow_map-f.sdr | 11 ++--------- code/def_files/data/effects/shadow_map-v.sdr | 16 ++++++++++------ code/graphics/opengl/gropengl.cpp | 1 + 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/code/def_files/data/effects/shadow_map-f.sdr b/code/def_files/data/effects/shadow_map-f.sdr index c2a0c6b0298..d918d6dc0e9 100644 --- a/code/def_files/data/effects/shadow_map-f.sdr +++ b/code/def_files/data/effects/shadow_map-f.sdr @@ -4,23 +4,16 @@ #ifdef GEOMETRY_FALLBACK in VertexOutputGeo { - vec4 position; - vec3 normal; -} vertIn; #else in VertexOutput { +#endif + vec4 position; vec3 normal; } vertIn; -#endif - out vec4 fragOut0; void main() { -#ifdef GEOMETRY_FALLBACK fragOut0 = vec4(vertIn.position.z, vertIn.position.z * vertIn.position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0); -#else - fragOut0 = vec4(gl_Position.z, gl_Position.z * gl_Position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0); -#endif } diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index f43d177bc95..aede2f42c4f 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -28,6 +28,8 @@ out VertexOutput { #endif #ifdef GEOMETRY_FALLBACK float clipModel; +#else + vec4 position; #endif vec3 normal; } vertOut; @@ -57,6 +59,10 @@ void main() normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal); position = modelViewMatrix * orient * vertex; +#ifndef GEOMETRY_FALLBACK + vertOut.position = position; +#endif + #if !defined(GL_ARB_gpu_shader5) #ifdef APPLE vertOut.instance = float(gl_InstanceIDARB); @@ -68,15 +74,14 @@ void main() if (clipModel) { gl_Position = vec4(vec3(-2.0), 1.0); } + else { #ifndef GEOMETRY_FALLBACK - else { - position = shadow_proj_matrix[gl_InstanceID] * position + position = shadow_proj_matrix[gl_InstanceID] * position; if(position.z < -1.0) position.z = -1.0; - } #endif - - gl_Position = position; + gl_Position = position; + } if (use_clip_plane) { gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex); @@ -84,7 +89,6 @@ void main() gl_ClipDistance[0] = 1.0; } - vertOut.normal = normal; #ifdef GEOMETRY_FALLBACK diff --git a/code/graphics/opengl/gropengl.cpp b/code/graphics/opengl/gropengl.cpp index 19a38e1a826..439bd368732 100644 --- a/code/graphics/opengl/gropengl.cpp +++ b/code/graphics/opengl/gropengl.cpp @@ -1385,6 +1385,7 @@ bool gr_opengl_init(std::unique_ptr&& 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"))); From d2f2d48427d80e19a99e8e03bee8a717e89e56c8 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Thu, 11 Jun 2026 20:32:01 +0900 Subject: [PATCH 08/31] Switch to PCF+PCSS and proper textures for it --- code/def_files/data/effects/deferred-f.sdr | 4 +- code/def_files/data/effects/main-f.sdr | 4 +- code/def_files/data/effects/shadow_map-f.sdr | 14 ---- code/def_files/data/effects/shadow_map-g.sdr | 10 +-- code/def_files/data/effects/shadow_map-v.sdr | 14 +--- code/def_files/data/effects/shadows.sdr | 75 +++++-------------- code/graphics/opengl/gropengldeferred.cpp | 2 +- .../opengl/gropenglpostprocessing.cpp | 3 +- code/graphics/opengl/gropengltnl.cpp | 39 ++-------- code/graphics/opengl/gropengltnl.h | 2 +- 10 files changed, 38 insertions(+), 129 deletions(-) diff --git a/code/def_files/data/effects/deferred-f.sdr b/code/def_files/data/effects/deferred-f.sdr index 748432a009b..c30f9dc87b1 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; @@ -265,7 +265,7 @@ void main() 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, + attenuation *= getShadowValueNew(shadow_map, -position.z, fragShadowUV, fardist, middist, neardist, veryneardist); } diff --git a/code/def_files/data/effects/main-f.sdr b/code/def_files/data/effects/main-f.sdr index a9051e9f6ca..39f5daf01dc 100644 --- a/code/def_files/data/effects/main-f.sdr +++ b/code/def_files/data/effects/main-f.sdr @@ -132,7 +132,7 @@ 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; @@ -329,7 +329,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 = getShadowValueNew(shadow_map, -vertIn.position.z, vertIn.shadowUV, fardist, middist, neardist, veryneardist); #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 diff --git a/code/def_files/data/effects/shadow_map-f.sdr b/code/def_files/data/effects/shadow_map-f.sdr index d918d6dc0e9..fff405831fd 100644 --- a/code/def_files/data/effects/shadow_map-f.sdr +++ b/code/def_files/data/effects/shadow_map-f.sdr @@ -1,19 +1,5 @@ //? #version 150 -#include "shadows.sdr" - -#ifdef GEOMETRY_FALLBACK -in VertexOutputGeo { -#else -in VertexOutput { -#endif - vec4 position; - vec3 normal; -} vertIn; - -out vec4 fragOut0; - void main() { - fragOut0 = vec4(vertIn.position.z, vertIn.position.z * vertIn.position.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0); } diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr index acf14a50add..8bb56ba619e 100644 --- a/code/def_files/data/effects/shadow_map-g.sdr +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -24,11 +24,6 @@ in VertexOutput { vec3 normal; } vertIn[]; -out VertexOutputGeo { - vec4 position; - vec3 normal; -} vertOut; - void main(void) { #ifdef GL_ARB_gpu_shader5 @@ -46,10 +41,7 @@ void main(void) } if(gl_Position.z < -1.0) - gl_Position.z = -1.0; - - vertOut.position = gl_in[vert].gl_Position; - vertOut.normal = vertIn[vert].normal; + gl_Position.z = -1.0; gl_Layer = instanceID; gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0]; diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index aede2f42c4f..a779cea7fc1 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -22,17 +22,15 @@ layout (std140) uniform shadowMapData { uniform samplerBuffer transform_tex; +#ifdef GEOMETRY_FALLBACK out VertexOutput { #if !defined(GL_ARB_gpu_shader5) float instance; #endif -#ifdef GEOMETRY_FALLBACK float clipModel; -#else - vec4 position; -#endif vec3 normal; } vertOut; +#endif #define TEXELS_PER_MATRIX 4 @@ -59,9 +57,6 @@ void main() normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal); position = modelViewMatrix * orient * vertex; -#ifndef GEOMETRY_FALLBACK - vertOut.position = position; -#endif #if !defined(GL_ARB_gpu_shader5) #ifdef APPLE @@ -78,7 +73,7 @@ void main() #ifndef GEOMETRY_FALLBACK position = shadow_proj_matrix[gl_InstanceID] * position; if(position.z < -1.0) - position.z = -1.0; + position.z = -1.0; #endif gl_Position = position; } @@ -89,9 +84,8 @@ void main() gl_ClipDistance[0] = 1.0; } - vertOut.normal = normal; - #ifdef GEOMETRY_FALLBACK + vertOut.normal = normal; vertOut.clipModel = clipModel ? 1.0 : 0.0; #else gl_Layer = gl_InstanceID; diff --git a/code/def_files/data/effects/shadows.sdr b/code/def_files/data/effects/shadows.sdr index a765482f30c..aa882f1daed 100644 --- a/code/def_files/data/effects/shadows.sdr +++ b/code/def_files/data/effects/shadows.sdr @@ -1,36 +1,19 @@ +#define SAMPLES_PCSS 1 -const float VARIANCE_SHADOW_SCALE = 1000000.0; -const float VARIANCE_SHADOW_SCALE_INV = 1.0/VARIANCE_SHADOW_SCALE; - -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, int cascade, vec4 shadowUV) { - if(cascade > 3 || cascade < 0) return 1.0; - - vec2 poissonDisc[16] = vec2[]( + vec2 poissonDisc[16] = vec2[]( vec2(-0.76275, -0.3432573), vec2(-0.5226235, -0.8277544), vec2(-0.3780261, 0.01528688), @@ -60,14 +43,15 @@ float samplePoissonPCF(sampler2DArray shadow_map, float shadowDepth, int cascade // 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; - bool cockpit_shadow_bias; - if (fardist < 50.0f) { - // internal cockpit shadows - cockpit_shadow_bias = true; - } else { - // default external shadows - cockpit_shadow_bias = false; - } - - 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)); + return samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade]); } -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; -} diff --git a/code/graphics/opengl/gropengldeferred.cpp b/code/graphics/opengl/gropengldeferred.cpp index 52f733b3bdc..c23a7e325e2 100644 --- a/code/graphics/opengl/gropengldeferred.cpp +++ b/code/graphics/opengl/gropengldeferred.cpp @@ -250,7 +250,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) { 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/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 6e5c80932a6..831d34c8b97 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -63,7 +63,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; @@ -469,25 +468,12 @@ 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); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_LEQUAL); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, size, size, 4, 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 +485,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; @@ -576,11 +560,6 @@ void opengl_tnl_shutdown() Shadow_map_depth_texture = 0; } - if ( Shadow_map_texture ) { - glDeleteTextures(1, &Shadow_map_texture); - Shadow_map_texture = 0; - } - opengl_destroy_all_buffers(); } @@ -670,7 +649,7 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset gr_set_cull(1); gr_zbias(-1024); gr_set_fill_mode(GR_FILL_MODE_SOLID); - GL_state.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + GL_state.ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); GL_state.FrontFaceValue(gr_screen.rendering_to_texture != -1 ? GL_CCW : GL_CW); @@ -749,12 +728,10 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light GL_state.BindFrameBuffer(shadow_fbo); //glDrawBuffer(GL_COLOR_ATTACHMENT0); - GLenum buffers[] = { GL_COLOR_ATTACHMENT0}; - glDrawBuffers(1, buffers); + GLenum buffers[] = {}; + glDrawBuffers(0, buffers); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - 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); @@ -1015,7 +992,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 72bd5c024e6..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; From f12519d5bfe845d54ea8b67b90d78311d01a4694 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Thu, 11 Jun 2026 20:58:00 +0900 Subject: [PATCH 09/31] Smooth PCSS toggle --- code/def_files/data/effects/shadows.sdr | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/code/def_files/data/effects/shadows.sdr b/code/def_files/data/effects/shadows.sdr index aa882f1daed..789d7ad70e0 100644 --- a/code/def_files/data/effects/shadows.sdr +++ b/code/def_files/data/effects/shadows.sdr @@ -1,4 +1,10 @@ +#define SMOOTH_PCSS 1 + +#if SMOOTH_PCSS +#define SAMPLES_PCSS 16 +#else #define SAMPLES_PCSS 1 +#endif vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos) { @@ -13,6 +19,7 @@ vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos) float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shadowUV) { +#if SMOOTH_PCSS vec2 poissonDisc[16] = vec2[]( vec2(-0.76275, -0.3432573), vec2(-0.5226235, -0.8277544), @@ -31,6 +38,11 @@ float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shado 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; @@ -68,6 +80,13 @@ float getShadowValueNew(sampler2DArrayShadow shadow_map, float depth, vec4 shado cascade_start_dist[4] = fardist; if(cascade > 3 || cascade < 0) return 1.0; - return samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade]); + float dist_threshold = (cascade_start_dist[cascade + 1] - cascade_start_dist[cascade]) * 0.2; + if(cascade_start_dist[cascade + 1] - dist_threshold > depth) + return samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade]); + + return mix( + samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade]), + samplePoissonPCSS(shadow_map, cascade + 1, shadowUV[cascade + 1]), + smoothstep(cascade_start_dist[cascade + 1] - dist_threshold, cascade_start_dist[cascade + 1], depth)); } From 933bb5946c67cefebd9a0995b74d10ab2de803de Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Thu, 11 Jun 2026 21:33:05 +0900 Subject: [PATCH 10/31] Fix for windows --- code/graphics/opengl/gropengltnl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 831d34c8b97..0b03ca0c139 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -728,8 +728,7 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light GL_state.BindFrameBuffer(shadow_fbo); //glDrawBuffer(GL_COLOR_ATTACHMENT0); - GLenum buffers[] = {}; - glDrawBuffers(0, buffers); + glDrawBuffers(0, nullptr); glClear(GL_DEPTH_BUFFER_BIT); From 38227a0dfebb60d5e37cc6d6dbbd958b476b40bb Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Thu, 11 Jun 2026 21:52:59 +0900 Subject: [PATCH 11/31] Fix OGL error --- code/graphics/opengl/gropengltnl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 0b03ca0c139..8d926602349 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -469,7 +469,6 @@ static bool opengl_init_shadow_framebuffer(int size, GLenum color_format) 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); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_LEQUAL); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, size, size, 4, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, Shadow_map_depth_texture, 0); From 1377af0065a3a1ebd2f3f304a6cdd9993e6ac3a3 Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:50:03 +0900 Subject: [PATCH 12/31] Cleanup of old flag --- code/graphics/grinternal.cpp | 1 - code/graphics/grinternal.h | 1 - code/graphics/opengl/gropengltnl.cpp | 49 +++++++++------------------- code/graphics/shadows.cpp | 18 +++++++++- code/model/modelrender.cpp | 34 ++----------------- code/model/modelrender.h | 2 -- code/prop/prop.cpp | 4 --- code/ship/ship.cpp | 23 +++---------- code/ship/shipfx.cpp | 4 --- 9 files changed, 39 insertions(+), 97 deletions(-) diff --git a/code/graphics/grinternal.cpp b/code/graphics/grinternal.cpp index 5d5d7c82b2f..7ce870dc91a 100644 --- a/code/graphics/grinternal.cpp +++ b/code/graphics/grinternal.cpp @@ -1,6 +1,5 @@ #include "grinternal.h" -bool Rendering_to_shadow_map = false; bool Scene_framebuffer_in_frame = false; namespace graphics { diff --git a/code/graphics/grinternal.h b/code/graphics/grinternal.h index 044f163bd29..01091c2fd0b 100644 --- a/code/graphics/grinternal.h +++ b/code/graphics/grinternal.h @@ -44,7 +44,6 @@ extern color_gun *Gr_current_red, *Gr_current_green, *Gr_current_blue, *Gr_curre extern float Gr_gamma; -extern bool Rendering_to_shadow_map; extern bool Scene_framebuffer_in_frame; #define TCACHE_TYPE_AABITMAP 0 // HUD bitmap. All Alpha. diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 8d926602349..24744b66042 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -581,33 +581,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); } @@ -663,7 +652,8 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset GLenum element_type = (datap->flags & VB_FLAG_LARGE_INDEX) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; GLint base_vertex = (GLint)(vert_src->Base_vertex_offset + buffer->vertex_num_offset); - if (gr_is_capable(gr_capability::CAPABILITY_FAST_SHADOWS)) { + //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, @@ -731,9 +721,6 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light glClear(GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_CLAMP); - - Rendering_to_shadow_map = true; Glowpoint_override_save = Glowpoint_override; Glowpoint_override = true; @@ -748,13 +735,7 @@ 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; - - glDisable(GL_DEPTH_CLAMP); - gr_end_view_matrix(); - Rendering_to_shadow_map = false; gr_zbuffer_set(ZBUFFER_TYPE_FULL); GL_state.PopFramebufferState(); diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 7ba2d789cf9..ef15f78457b 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -801,7 +801,23 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, int tmap_num = detail_buffer.tex_buf[j].texture; int base_tex = pm->maps[tmap_num].textures[TM_BASE_TYPE].GetTexture(); - if (base_tex < 0) { + // Replacement textures may make a polygon invisible; skip it for shadow casting + 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) { + // Valid replacement, polygon should cast shadows; suppress the base_tex check below + base_tex = 0; + } + } else if (base_tex < 0) { + skip = true; + } + + if (skip) { continue; } diff --git a/code/model/modelrender.cpp b/code/model/modelrender.cpp index 54e9ee4c3fa..7d4774c372e 100644 --- a/code/model/modelrender.cpp +++ b/code/model/modelrender.cpp @@ -1171,20 +1171,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) ) { @@ -1611,11 +1597,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(); @@ -2027,10 +2009,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); @@ -2153,10 +2131,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; @@ -2896,11 +2870,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 43098f7ab36..260b318e971 100644 --- a/code/model/modelrender.h +++ b/code/model/modelrender.h @@ -22,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; 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..ac60eeb37f5 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8476,9 +8476,6 @@ 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; - 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]; ship_info* sip = &Ship_info[shipp->ship_info_index]; @@ -21767,8 +21764,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 +21950,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 +21982,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 +22018,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 +22056,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 +22073,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 +22146,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 +22215,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/shipfx.cpp b/code/ship/shipfx.cpp index 415badf31ea..f22e8f68c4f 100644 --- a/code/ship/shipfx.cpp +++ b/code/ship/shipfx.cpp @@ -1557,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; } From efec0d0ddd59bc0428338fd125e994ca06c40946 Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Fri, 12 Jun 2026 14:50:45 +0900 Subject: [PATCH 13/31] Expose shadow smoothness --- code/def_files/data/effects/deferred-f.sdr | 4 +++- code/def_files/data/effects/main-f.sdr | 4 +++- code/def_files/data/effects/shadows.sdr | 16 +++++----------- code/graphics/opengl/gropengldeferred.cpp | 6 ++++++ code/graphics/uniforms.cpp | 6 ++++++ code/graphics/util/uniform_structs.h | 4 ++++ code/mod_table/mod_table.cpp | 21 +++++++++++++++++++++ code/mod_table/mod_table.h | 1 + 8 files changed, 49 insertions(+), 13 deletions(-) diff --git a/code/def_files/data/effects/deferred-f.sdr b/code/def_files/data/effects/deferred-f.sdr index c30f9dc87b1..53d21e39ba5 100644 --- a/code/def_files/data/effects/deferred-f.sdr +++ b/code/def_files/data/effects/deferred-f.sdr @@ -28,6 +28,8 @@ layout (std140) uniform globalDeferredData { float middist; float fardist; + vec4 maxUVOffset; + float invScreenWidth; float invScreenHeight; @@ -266,7 +268,7 @@ void main() fragShadowUV[3] = transformToShadowMap(shadow_proj_matrix[3], 3, fragShadowPos); attenuation *= getShadowValueNew(shadow_map, -position.z, fragShadowUV, fardist, middist, - neardist, veryneardist); + neardist, veryneardist, maxUVOffset); } 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 39f5daf01dc..4532e492dfe 100644 --- a/code/def_files/data/effects/main-f.sdr +++ b/code/def_files/data/effects/main-f.sdr @@ -85,6 +85,8 @@ layout (std140) uniform modelData { float middist; float fardist; + vec4 maxUVOffset; + int sGlowmapIndex; int sSpecmapIndex; int sNormalmapIndex; @@ -329,7 +331,7 @@ void main() #prereplace IF_FLAG MODEL_SDR_FLAG_LIGHT float shadow = 1.0; #prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS - shadow = getShadowValueNew(shadow_map, -vertIn.position.z, vertIn.shadowUV, fardist, middist, neardist, veryneardist); + shadow = getShadowValueNew(shadow_map, -vertIn.position.z, vertIn.shadowUV, fardist, middist, neardist, veryneardist, maxUVOffset); #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 diff --git a/code/def_files/data/effects/shadows.sdr b/code/def_files/data/effects/shadows.sdr index 789d7ad70e0..96688b727f2 100644 --- a/code/def_files/data/effects/shadows.sdr +++ b/code/def_files/data/effects/shadows.sdr @@ -17,7 +17,7 @@ vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos) return shadow_proj; } -float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shadowUV) +float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shadowUV, vec4 maxUVOffset) { #if SMOOTH_PCSS vec2 poissonDisc[16] = vec2[]( @@ -44,12 +44,6 @@ float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shado ); #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 @@ -64,7 +58,7 @@ float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shado } float getShadowValueNew(sampler2DArrayShadow shadow_map, float depth, vec4 shadowUV[4], float fardist, - float middist, float neardist, float veryneardist) + float middist, float neardist, float veryneardist, vec4 maxUVOffset) { // Valathil's Shadows int cascade = 4; @@ -82,11 +76,11 @@ float getShadowValueNew(sampler2DArrayShadow shadow_map, float depth, vec4 shado float dist_threshold = (cascade_start_dist[cascade + 1] - cascade_start_dist[cascade]) * 0.2; if(cascade_start_dist[cascade + 1] - dist_threshold > depth) - return samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade]); + return samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade], maxUVOffset); return mix( - samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade]), - samplePoissonPCSS(shadow_map, cascade + 1, shadowUV[cascade + 1]), + samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade], maxUVOffset), + samplePoissonPCSS(shadow_map, cascade + 1, shadowUV[cascade + 1], maxUVOffset), smoothstep(cascade_start_dist[cascade + 1] - dist_threshold, cascade_start_dist[cascade + 1], depth)); } diff --git a/code/graphics/opengl/gropengldeferred.cpp b/code/graphics/opengl/gropengldeferred.cpp index c23a7e325e2..de43d31dea7 100644 --- a/code/graphics/opengl/gropengldeferred.cpp +++ b/code/graphics/opengl/gropengldeferred.cpp @@ -20,6 +20,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 @@ -323,6 +324,11 @@ void gr_opengl_deferred_lighting_finish() header->middist = Shadow_cascade_distances[2]; header->fardist = Shadow_cascade_distances[3]; + header->maxUVOffset.xyzw.x = Shadow_smoothness_factor[0]; + header->maxUVOffset.xyzw.y = Shadow_smoothness_factor[1]; + header->maxUVOffset.xyzw.z = Shadow_smoothness_factor[2]; + header->maxUVOffset.xyzw.w = Shadow_smoothness_factor[3]; + vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render); } diff --git a/code/graphics/uniforms.cpp b/code/graphics/uniforms.cpp index cdce2d7a69f..45095c8c5eb 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" @@ -176,6 +177,11 @@ void convert_model_material(model_uniform_data* data_out, data_out->neardist = Shadow_cascade_distances[1]; data_out->middist = Shadow_cascade_distances[2]; data_out->fardist = Shadow_cascade_distances[3]; + + data_out->maxUVOffset.xyzw.x = Shadow_smoothness_factor[0]; + data_out->maxUVOffset.xyzw.y = Shadow_smoothness_factor[1]; + data_out->maxUVOffset.xyzw.z = Shadow_smoothness_factor[2]; + data_out->maxUVOffset.xyzw.w = Shadow_smoothness_factor[3]; } if (material.is_batched()) { diff --git a/code/graphics/util/uniform_structs.h b/code/graphics/util/uniform_structs.h index bd30a007010..7d435486a70 100644 --- a/code/graphics/util/uniform_structs.h +++ b/code/graphics/util/uniform_structs.h @@ -28,6 +28,8 @@ struct deferred_global_data { float middist; float fardist; + vec4 maxUVOffset; + float invScreenWidth; float invScreenHeight; float nearPlane; @@ -130,6 +132,8 @@ struct model_uniform_data { float middist; float fardist; + vec4 maxUVOffset; + int sGlowmapIndex; int sSpecmapIndex; int sNormalmapIndex; diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index b5fd84f2c6d..933dd119845 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -129,6 +129,7 @@ bool Neb_affects_particles; bool Neb_affects_fireballs; std::tuple Shadow_distances; std::tuple Shadow_distances_cockpit; +float Shadow_smoothness_factor[4] = {1.0f/300.0f, 1.0f/250.0f, 1.0f/200.0f, 1.0f/200.0f}; bool Show_ship_casts_shadow; bool Cockpit_shares_coordinate_space; bool Show_ship_only_if_cockpits_enabled; @@ -906,6 +907,22 @@ void parse_mod_table(const char *filename) } } + if (optional_string("$Shadow Smoothness Factor:")) { + float smoothness[4]; + stuff_float_list(smoothness, 4); + if (smoothness[0] > 0.0f && smoothness[1] > 0.0f && smoothness[2] > 0.0f && smoothness[3] > 0.0f) { + Shadow_smoothness_factor[0] = smoothness[0]; + Shadow_smoothness_factor[1] = smoothness[1]; + Shadow_smoothness_factor[2] = smoothness[2]; + Shadow_smoothness_factor[3] = smoothness[3]; + mprintf(("Game Settings Table: Shadow smoothness factor set to %f, %f, %f, %f\n", + smoothness[0], smoothness[1], smoothness[2], smoothness[3])); + } else { + error_display(0, "$Shadow Smoothness Factor values are %f, %f, %f, %f. All values must be > 0. Using defaults.", + smoothness[0], smoothness[1], smoothness[2], smoothness[3]); + } + } + if (optional_string("$Shadow Disable Techroom:")) { stuff_boolean(&Shadow_disable_overrides.disable_techroom); } @@ -1833,6 +1850,10 @@ void mod_table_reset() 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_smoothness_factor[0] = 1.0f/300.0f; + Shadow_smoothness_factor[1] = 1.0f/250.0f; + Shadow_smoothness_factor[2] = 1.0f/200.0f; + Shadow_smoothness_factor[3] = 1.0f/200.0f; 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..998c751cffc 100644 --- a/code/mod_table/mod_table.h +++ b/code/mod_table/mod_table.h @@ -148,6 +148,7 @@ extern bool Neb_affects_weapons; extern bool Neb_affects_particles; extern bool Neb_affects_fireballs; extern std::tuple Shadow_distances; +extern float Shadow_smoothness_factor[4]; extern std::tuple Shadow_distances_cockpit; extern bool Show_ship_casts_shadow; extern bool Cockpit_shares_coordinate_space; From d2c2e77116b7947995e607e039f1c3e02ca2d86b Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Sat, 13 Jun 2026 09:05:20 +0900 Subject: [PATCH 14/31] Fix missing uniform --- code/def_files/data/effects/main-g.sdr | 2 ++ code/def_files/data/effects/main-v.sdr | 2 ++ 2 files changed, 4 insertions(+) diff --git a/code/def_files/data/effects/main-g.sdr b/code/def_files/data/effects/main-g.sdr index 5006c744335..48e652f17a8 100644 --- a/code/def_files/data/effects/main-g.sdr +++ b/code/def_files/data/effects/main-g.sdr @@ -80,6 +80,8 @@ layout (std140) uniform modelData { float middist; float fardist; + vec4 maxUVOffset; + int sGlowmapIndex; int sSpecmapIndex; diff --git a/code/def_files/data/effects/main-v.sdr b/code/def_files/data/effects/main-v.sdr index e48ad7d5a89..3b9b070b103 100644 --- a/code/def_files/data/effects/main-v.sdr +++ b/code/def_files/data/effects/main-v.sdr @@ -84,6 +84,8 @@ layout (std140) uniform modelData { float middist; float fardist; + vec4 maxUVOffset; + int sGlowmapIndex; int sSpecmapIndex; From 62ba1bbe0bd41ff0330551f2ce39b7c834023b7a Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Sat, 13 Jun 2026 22:40:35 +0900 Subject: [PATCH 15/31] Make number of shadow cascades dynamic --- code/def_files/data/effects/deferred-f.sdr | 32 +++--- code/def_files/data/effects/main-f.sdr | 22 ++-- code/def_files/data/effects/main-g.sdr | 20 +--- code/def_files/data/effects/main-v.sdr | 27 ++--- code/def_files/data/effects/shadow_map-g.sdr | 14 +-- code/def_files/data/effects/shadow_map-v.sdr | 8 +- code/def_files/data/effects/shadows.sdr | 48 ++++---- code/graphics/2d.h | 1 + code/graphics/opengl/gropengldeferred.cpp | 17 +-- code/graphics/opengl/gropenglshader.cpp | 3 +- code/graphics/opengl/gropengltnl.cpp | 12 +- code/graphics/shadows.cpp | 114 +++++++++++++++---- code/graphics/shadows.h | 12 +- code/graphics/uniforms.cpp | 16 --- code/graphics/util/UniformBufferManager.cpp | 3 + code/graphics/util/uniform_structs.h | 38 ++----- code/menuui/techmenu.cpp | 2 +- code/missionui/missionbrief.cpp | 2 +- code/missionui/missionscreencommon.cpp | 8 +- code/missionui/missionweaponchoice.cpp | 4 +- code/mod_table/mod_table.cpp | 72 ++++++------ code/mod_table/mod_table.h | 8 +- code/ship/ship.cpp | 5 +- 23 files changed, 250 insertions(+), 238 deletions(-) diff --git a/code/def_files/data/effects/deferred-f.sdr b/code/def_files/data/effects/deferred-f.sdr index 53d21e39ba5..364f4fd8860 100644 --- a/code/def_files/data/effects/deferred-f.sdr +++ b/code/def_files/data/effects/deferred-f.sdr @@ -18,24 +18,20 @@ 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; - - vec4 maxUVOffset; - float invScreenWidth; float invScreenHeight; - float nearPlane; }; +layout (std140) uniform shadowCascadeParams { + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + float cascade_distances[NUM_SHADOW_CASCADES]; + float smoothness_factors[NUM_SHADOW_CASCADES]; +}; + layout (std140) uniform matrixData { mat4 modelViewMatrix; mat4 projMatrix; @@ -261,14 +257,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 *= getShadowValueNew(shadow_map, -position.z, fragShadowUV, fardist, middist, - neardist, veryneardist, maxUVOffset); + 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); } 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 4532e492dfe..1d89a160ccd 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,13 +76,6 @@ layout (std140) uniform modelData { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - - vec4 maxUVOffset; - int sGlowmapIndex; int sSpecmapIndex; int sNormalmapIndex; @@ -97,6 +86,13 @@ layout (std140) uniform modelData { int flags; }; +layout (std140) uniform shadowCascadeParams { + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + float cascade_distances[NUM_SHADOW_CASCADES]; + float smoothness_factors[NUM_SHADOW_CASCADES]; +}; + in VertexOutput { mat3 tangentMatrix; @@ -109,7 +105,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; @@ -331,7 +327,7 @@ void main() #prereplace IF_FLAG MODEL_SDR_FLAG_LIGHT float shadow = 1.0; #prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS - shadow = getShadowValueNew(shadow_map, -vertIn.position.z, vertIn.shadowUV, fardist, middist, neardist, veryneardist, maxUVOffset); + shadow = getShadowValue(shadow_map, -vertIn.position.z, vertIn.shadowUV, cascade_distances, smoothness_factors); #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 diff --git a/code/def_files/data/effects/main-g.sdr b/code/def_files/data/effects/main-g.sdr index 48e652f17a8..49cee6b0581 100644 --- a/code/def_files/data/effects/main-g.sdr +++ b/code/def_files/data/effects/main-g.sdr @@ -28,8 +28,6 @@ layout (std140) uniform modelData { mat4 viewMatrix; mat4 projMatrix; mat4 textureMatrix; - mat4 shadow_mv_matrix; - mat4 shadow_proj_matrix[4]; vec4 color; @@ -75,13 +73,6 @@ layout (std140) uniform modelData { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - - vec4 maxUVOffset; - int sGlowmapIndex; int sSpecmapIndex; @@ -106,7 +97,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[]; @@ -123,7 +114,7 @@ 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; @@ -168,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 3b9b070b103..aa21fd0e1cf 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,13 +77,6 @@ layout (std140) uniform modelData { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - - vec4 maxUVOffset; - int sGlowmapIndex; int sSpecmapIndex; @@ -98,6 +89,13 @@ layout (std140) uniform modelData { int flags; }; +layout (std140) uniform shadowCascadeParams { + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + float cascade_distances[NUM_SHADOW_CASCADES]; + float smoothness_factors[NUM_SHADOW_CASCADES]; +}; + #prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM uniform samplerBuffer transform_tex; #prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM @@ -114,7 +112,7 @@ 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; @@ -158,11 +156,10 @@ void main() gl_Position = projMatrix * position; #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 diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr index 8bb56ba619e..5bc14163eed 100644 --- a/code/def_files/data/effects/shadow_map-g.sdr +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -4,16 +4,14 @@ layout (triangles) in; layout (triangle_strip, max_vertices = 3) out; #ifdef GL_ARB_gpu_shader5 -layout(invocations = 4) in; +layout(invocations = NUM_SHADOW_CASCADES) in; #endif -layout (std140) uniform shadowMapData { - mat4 modelViewMatrix; - mat4 modelMatrix; - mat4 shadow_proj_matrix[4]; - vec4 clip_equation; - bool use_clip_plane; - int buffer_matrix_offset; +layout (std140) uniform shadowCascadeParams { + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + float cascade_distances[NUM_SHADOW_CASCADES]; + float smoothness_factors[NUM_SHADOW_CASCADES]; }; in VertexOutput { diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index a779cea7fc1..e518defc4a0 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -14,12 +14,18 @@ in float vertModelID; layout (std140) uniform shadowMapData { mat4 modelViewMatrix; mat4 modelMatrix; - mat4 shadow_proj_matrix[4]; vec4 clip_equation; bool use_clip_plane; int buffer_matrix_offset; }; +layout (std140) uniform shadowCascadeParams { + mat4 shadow_mv_matrix; + mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; + float cascade_distances[NUM_SHADOW_CASCADES]; + float smoothness_factors[NUM_SHADOW_CASCADES]; +}; + uniform samplerBuffer transform_tex; #ifdef GEOMETRY_FALLBACK diff --git a/code/def_files/data/effects/shadows.sdr b/code/def_files/data/effects/shadows.sdr index 96688b727f2..3429fe41d6f 100644 --- a/code/def_files/data/effects/shadows.sdr +++ b/code/def_files/data/effects/shadows.sdr @@ -17,7 +17,7 @@ vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos) return shadow_proj; } -float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shadowUV, vec4 maxUVOffset) +float samplePoissonPCSS(sampler2DArrayShadow shadow_map, vec4 shadowUV, float maxUVOffset) { #if SMOOTH_PCSS vec2 poissonDisc[16] = vec2[]( @@ -44,43 +44,33 @@ float samplePoissonPCSS(sampler2DArrayShadow shadow_map, int cascade, vec4 shado ); #endif - // 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 3 || cascade < 0) return 1.0; + int cascade = NUM_SHADOW_CASCADES; + for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { + cascade -= int(step(depth, cascade_distances[i])); + } + if (cascade >= NUM_SHADOW_CASCADES || cascade < 0) return 1.0; - float dist_threshold = (cascade_start_dist[cascade + 1] - cascade_start_dist[cascade]) * 0.2; - if(cascade_start_dist[cascade + 1] - dist_threshold > depth) - return samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade], maxUVOffset); + float cascade_start = (cascade > 0) ? cascade_distances[cascade - 1] : 0.0; + float cascade_end = cascade_distances[cascade]; + float dist_threshold = (cascade_end - cascade_start) * 0.2; - return mix( - samplePoissonPCSS(shadow_map, cascade, shadowUV[cascade], maxUVOffset), - samplePoissonPCSS(shadow_map, cascade + 1, shadowUV[cascade + 1], maxUVOffset), - smoothstep(cascade_start_dist[cascade + 1] - dist_threshold, cascade_start_dist[cascade + 1], depth)); -} + if (cascade_end - dist_threshold > depth || cascade >= NUM_SHADOW_CASCADES - 1) + return samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade]); + return mix( + samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade]), + samplePoissonPCSS(shadow_map, shadowUV[cascade + 1], smoothness_factors[cascade + 1]), + smoothstep(cascade_end - dist_threshold, cascade_end, depth)); +} diff --git a/code/graphics/2d.h b/code/graphics/2d.h index 817e7aca5a8..cd0d9c92be2 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -257,6 +257,7 @@ enum class uniform_block_type { MovieData = 7, GenericData = 8, ShadowMapData = 9, + ShadowCascadeParams = 10, NUM_BLOCK_TYPES }; diff --git a/code/graphics/opengl/gropengldeferred.cpp b/code/graphics/opengl/gropengldeferred.cpp index de43d31dea7..5ba064a1df4 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" @@ -314,22 +315,8 @@ 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]; - - header->maxUVOffset.xyzw.x = Shadow_smoothness_factor[0]; - header->maxUVOffset.xyzw.y = Shadow_smoothness_factor[1]; - header->maxUVOffset.xyzw.z = Shadow_smoothness_factor[2]; - header->maxUVOffset.xyzw.w = Shadow_smoothness_factor[3]; - vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render); + shadow_cascade_params_bind(); } header->invScreenWidth = 1.0f / gr_screen.max_w; diff --git a/code/graphics/opengl/gropenglshader.cpp b/code/graphics/opengl/gropenglshader.cpp index 5170c2d15a7..976c5c48a42 100644 --- a/code/graphics/opengl/gropenglshader.cpp +++ b/code/graphics/opengl/gropenglshader.cpp @@ -79,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"}, }; /** @@ -517,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 << '\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 24744b66042..0c809c10c37 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" @@ -469,7 +470,7 @@ static bool opengl_init_shadow_framebuffer(int size, GLenum color_format) 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); 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, 4, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, size, size, Num_shadow_cascades, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, Shadow_map_depth_texture, 0); @@ -540,6 +541,10 @@ void opengl_tnl_init() Shadow_quality = ShadowQuality::Disabled; } } + + if (Shadow_quality != ShadowQuality::Disabled) { + shadow_cascade_params_init(); + } } gr_opengl_deferred_init(); @@ -554,6 +559,8 @@ 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; @@ -652,13 +659,12 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset GLenum element_type = (datap->flags & VB_FLAG_LARGE_INDEX) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; GLint base_vertex = (GLint)(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, - 4, + Num_shadow_cascades, base_vertex); } else { diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index ef15f78457b..cb85ae65637 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -9,6 +9,7 @@ #include "graphics/shadows.h" +#include "graphics/2d.h" #include "graphics/uniforms.h" #include "asteroid/asteroid.h" #include "cmdline/cmdline.h" @@ -32,10 +33,10 @@ 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; @@ -431,7 +432,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, float aspect, const SCP_vector& cascade_distances) { if(Static_light.empty()) return vmd_identity_matrix; @@ -444,23 +445,30 @@ 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 = static_cast(cascade_distances.size()); + + Shadow_frustums.resize(num_cascades); + Shadow_cascade_distances.resize(num_cascades); + Shadow_proj_matrix.resize(num_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; + for (int i = 0; i < num_cascades; i++) { + float z_near; + if (i == 0) { + 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, eye_pos, 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); + shadow_cascade_params_bind(); + return light_matrix; } @@ -542,8 +550,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) gr_end_view_matrix(); 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)); + Shadow_distances); shadow_render_list shadow_list; @@ -554,7 +561,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) continue; bool cull = true; - for ( int j = 0; j < MAX_SHADOW_CASCADES; ++j ) { + for ( size_t j = 0; j < Shadow_frustums.size(); ++j ) { if ( shadows_obj_in_frustum(objp, &light_matrix, &Shadow_frustums[j].min, &Shadow_frustums[j].max) ) { cull = false; break; @@ -669,6 +676,69 @@ void shadow_end_frame() { } } +static gr_buffer_handle Shadow_cascade_params_buffer; +static size_t Shadow_cascade_params_buffer_size = 0; + +static size_t compute_cascade_params_size(int num_cascades) { + return sizeof(matrix4) + + sizeof(matrix4) * num_cascades + + 16 * num_cascades + + 16 * num_cascades; +} + +void shadow_cascade_params_init() { + Shadow_cascade_params_buffer_size = compute_cascade_params_size(Num_shadow_cascades); + 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(); + } +} + +void shadow_cascade_params_bind() { + if (!Shadow_cascade_params_buffer.isValid()) { + return; + } + + const int num_cascades = static_cast(Shadow_proj_matrix.size()); + const size_t required_size = compute_cascade_params_size(num_cascades); + + if (required_size > Shadow_cascade_params_buffer_size) { + Shadow_cascade_params_buffer_size = required_size; + } + + SCP_vector buffer(required_size, 0); + uint8_t* ptr = buffer.data(); + size_t offset = 0; + + memcpy(ptr + offset, &Shadow_view_matrix_light, sizeof(matrix4)); + offset += sizeof(matrix4); + + for (int i = 0; i < num_cascades; i++) { + memcpy(ptr + offset, &Shadow_proj_matrix[i], sizeof(matrix4)); + offset += sizeof(matrix4); + } + + for (int i = 0; i < num_cascades; i++) { + memcpy(ptr + offset, &Shadow_cascade_distances[i], sizeof(float)); + offset += 16; + } + + for (size_t i = 0; i < Shadow_smoothness_factor.size() && i < static_cast(num_cascades); i++) { + memcpy(ptr + offset, &Shadow_smoothness_factor[i], sizeof(float)); + offset += 16; + } + + gr_update_buffer_data(Shadow_cascade_params_buffer, 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(); } @@ -736,10 +806,6 @@ void shadow_render_list::build_uniform_buffer() queued_draw.scale, queued_draw.transform_buffer_offset); - for (size_t i = 0; i < MAX_SHADOW_CASCADES; i++) { - element->shadow_proj_matrix[i] = Shadow_proj_matrix[i]; - } - element->clip_equation = queued_draw.clip_equation; element->use_clip_plane = queued_draw.has_clip_plane ? 1 : 0; diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index c9f3c86b8aa..df7d0e34fa9 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -15,8 +15,6 @@ #include "object/object.h" #include "render/3d.h" -#define MAX_SHADOW_CASCADES 4 - struct light_frustum_info { matrix4 proj_matrix; @@ -37,14 +35,18 @@ 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; 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); -matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float aspect, float veryneardist, float neardist, float middist, float fardist); +void shadow_cascade_params_init(); +void shadow_cascade_params_shutdown(); +void shadow_cascade_params_bind(); + +matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float aspect, const SCP_vector& cascade_distances); void shadows_end_render(); /** diff --git a/code/graphics/uniforms.cpp b/code/graphics/uniforms.cpp index 45095c8c5eb..15ddb93a7dc 100644 --- a/code/graphics/uniforms.cpp +++ b/code/graphics/uniforms.cpp @@ -166,23 +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]; - - data_out->maxUVOffset.xyzw.x = Shadow_smoothness_factor[0]; - data_out->maxUVOffset.xyzw.y = Shadow_smoothness_factor[1]; - data_out->maxUVOffset.xyzw.z = Shadow_smoothness_factor[2]; - data_out->maxUVOffset.xyzw.w = Shadow_smoothness_factor[3]; - } if (material.is_batched()) { data_out->buffer_matrix_offset = (int) transform_buffer_offset; diff --git a/code/graphics/util/UniformBufferManager.cpp b/code/graphics/util/UniformBufferManager.cpp index f021a43dfd2..652b1da5cda 100644 --- a/code/graphics/util/UniformBufferManager.cpp +++ b/code/graphics/util/UniformBufferManager.cpp @@ -24,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!"); @@ -44,6 +46,7 @@ size_t getHeaderSize(uniform_block_type type) 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 7d435486a70..72999e2d094 100644 --- a/code/graphics/util/uniform_structs.h +++ b/code/graphics/util/uniform_structs.h @@ -18,18 +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; - - vec4 maxUVOffset; - float invScreenWidth; float invScreenHeight; float nearPlane; @@ -81,8 +71,6 @@ struct model_uniform_data { matrix4 viewMatrix; matrix4 projMatrix; matrix4 textureMatrix; - matrix4 shadow_mv_matrix; - matrix4 shadow_proj_matrix[4]; vec4 color; @@ -112,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; @@ -127,13 +115,6 @@ struct model_uniform_data { float znear; float zfar; - float veryneardist; - float neardist; - float middist; - float fardist; - - vec4 maxUVOffset; - int sGlowmapIndex; int sSpecmapIndex; int sNormalmapIndex; @@ -148,14 +129,17 @@ 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_cascade_params_header { + matrix4 shadow_mv_matrix; +}; + struct shadow_uniform_data { - matrix4 modelViewMatrix; // light view matrix (same for all draws in a pass) - matrix4 modelMatrix; // model-to-world (for clip plane) - matrix4 shadow_proj_matrix[4]; // cascade projection matrices - vec4 clip_equation; // xyz = normal, w = -dot(normal, point) - int use_clip_plane; // 1 if clip plane is active - int buffer_matrix_offset; // index into transform_tex - float pad[2]; // alignment to 16 bytes + 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); diff --git a/code/menuui/techmenu.cpp b/code/menuui/techmenu.cpp index 4a64d32acef..1005b39a6cd 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, 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..5f6424872cd 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, 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..323a4879cc5 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, 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, 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, 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, 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..dcb918ff952 100644 --- a/code/missionui/missionweaponchoice.cpp +++ b/code/missionui/missionweaponchoice.cpp @@ -845,10 +845,10 @@ void draw_3d_overhead_view(int model_num, &Eye_position, 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 933dd119845..f92f0672ff1 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -127,9 +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; -float Shadow_smoothness_factor[4] = {1.0f/300.0f, 1.0f/250.0f, 1.0f/200.0f, 1.0f/200.0f}; +SCP_vector Shadow_distances; +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; @@ -887,39 +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])); - } - 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]); + 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."); } } if (optional_string("$Shadow Smoothness Factor:")) { - float smoothness[4]; - stuff_float_list(smoothness, 4); - if (smoothness[0] > 0.0f && smoothness[1] > 0.0f && smoothness[2] > 0.0f && smoothness[3] > 0.0f) { - Shadow_smoothness_factor[0] = smoothness[0]; - Shadow_smoothness_factor[1] = smoothness[1]; - Shadow_smoothness_factor[2] = smoothness[2]; - Shadow_smoothness_factor[3] = smoothness[3]; - mprintf(("Game Settings Table: Shadow smoothness factor set to %f, %f, %f, %f\n", - smoothness[0], smoothness[1], smoothness[2], smoothness[3])); + 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 values are %f, %f, %f, %f. All values must be > 0. Using defaults.", - smoothness[0], smoothness[1], smoothness[2], smoothness[3]); + error_display(0, "$Shadow Smoothness Factor: all values must be > 0. Using defaults."); } } @@ -1848,12 +1857,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_smoothness_factor[0] = 1.0f/300.0f; - Shadow_smoothness_factor[1] = 1.0f/250.0f; - Shadow_smoothness_factor[2] = 1.0f/200.0f; - Shadow_smoothness_factor[3] = 1.0f/200.0f; + 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}; + 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 998c751cffc..5031de3d4fb 100644 --- a/code/mod_table/mod_table.h +++ b/code/mod_table/mod_table.h @@ -147,9 +147,11 @@ 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 float Shadow_smoothness_factor[4]; -extern std::tuple Shadow_distances_cockpit; +extern SCP_vector Shadow_distances; +extern SCP_vector Shadow_smoothness_factor; +extern SCP_vector Shadow_distances_cockpit; +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/ship/ship.cpp b/code/ship/ship.cpp index ac60eeb37f5..e5d1894b02b 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8577,10 +8577,7 @@ void ship_render_player_ship(object* objp, const vec3d* cam_offset, const matrix 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)); + Shadow_distances_cockpit); if (deferredRenderShipModel) { model_render_params shadow_render_info; From 615390c1702b6f3609b33873adbf0f5e95f51e2a Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Mon, 15 Jun 2026 21:56:21 +0900 Subject: [PATCH 16/31] Progress on cockpit shadow integration --- code/def_files/data/effects/shadow_map-g.sdr | 12 +- code/def_files/data/effects/shadow_map-v.sdr | 6 +- code/graphics/2d.h | 2 +- code/graphics/grstub.cpp | 2 +- code/graphics/matrix.cpp | 2 +- code/graphics/opengl/gropengldraw.h | 2 +- code/graphics/opengl/gropenglshader.cpp | 2 +- code/graphics/opengl/gropengltnl.cpp | 46 ++++-- code/graphics/shadows.cpp | 152 ++++++++++++++++++- code/graphics/shadows.h | 6 +- code/graphics/util/uniform_structs.h | 3 - code/graphics/vulkan/vulkan_stubs.cpp | 2 +- code/mod_table/mod_table.cpp | 33 +++- code/mod_table/mod_table.h | 1 - code/ship/ship.cpp | 67 ++++---- code/ship/ship.h | 1 + freespace2/freespace.cpp | 2 +- 17 files changed, 268 insertions(+), 73 deletions(-) diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr index 5bc14163eed..483e93cbe01 100644 --- a/code/def_files/data/effects/shadow_map-g.sdr +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -14,6 +14,9 @@ layout (std140) uniform shadowCascadeParams { float smoothness_factors[NUM_SHADOW_CASCADES]; }; +uniform int cascade_offset; +uniform int cascade_count; + in VertexOutput { #if !defined(GL_ARB_gpu_shader5) float instance; @@ -25,9 +28,10 @@ in VertexOutput { void main(void) { #ifdef GL_ARB_gpu_shader5 - int instanceID = gl_InvocationID; + if (gl_InvocationID >= cascade_count) return; + int cascade_id = cascade_offset + gl_InvocationID; #else - int instanceID = int(vertIn[0].instance); + int cascade_id = cascade_offset + int(vertIn[0].instance); #endif for(int vert = 0; vert < gl_in.length(); vert++) @@ -35,13 +39,13 @@ void main(void) if (vertIn[vert].clipModel > 0.9) { gl_Position = gl_in[vert].gl_Position; } else { - gl_Position = shadow_proj_matrix[instanceID] * gl_in[vert].gl_Position; + gl_Position = shadow_proj_matrix[cascade_id] * gl_in[vert].gl_Position; } if(gl_Position.z < -1.0) gl_Position.z = -1.0; - gl_Layer = instanceID; + gl_Layer = cascade_id; gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0]; EmitVertex(); diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index e518defc4a0..32d6c162fd2 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -27,6 +27,7 @@ layout (std140) uniform shadowCascadeParams { }; uniform samplerBuffer transform_tex; +uniform int cascade_offset; #ifdef GEOMETRY_FALLBACK out VertexOutput { @@ -77,7 +78,8 @@ void main() } else { #ifndef GEOMETRY_FALLBACK - position = shadow_proj_matrix[gl_InstanceID] * position; + int cascade_id = cascade_offset + gl_InstanceID; + position = shadow_proj_matrix[cascade_id] * position; if(position.z < -1.0) position.z = -1.0; #endif @@ -94,6 +96,6 @@ void main() vertOut.normal = normal; vertOut.clipModel = clipModel ? 1.0 : 0.0; #else - gl_Layer = gl_InstanceID; + gl_Layer = cascade_offset + gl_InstanceID; #endif } diff --git a/code/graphics/2d.h b/code/graphics/2d.h index cd0d9c92be2..69a388875b9 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -840,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; diff --git a/code/graphics/grstub.cpp b/code/graphics/grstub.cpp index 77fcbc02610..b5ce243123a 100644 --- a/code/graphics/grstub.cpp +++ b/code/graphics/grstub.cpp @@ -299,7 +299,7 @@ int gr_stub_maybe_create_shader(shader_type /*shader_t*/, unsigned int /*flags return -1; } -void gr_stub_shadow_map_start(matrix4 * /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/) +void gr_stub_shadow_map_start(matrix4 * /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/, bool /*render_cockpit_cascades*/, bool /*render_scene_cascades*/, bool /*first_pass*/) { } diff --git a/code/graphics/matrix.cpp b/code/graphics/matrix.cpp index c7681159191..9f89cb0d763 100644 --- a/code/graphics/matrix.cpp +++ b/code/graphics/matrix.cpp @@ -30,7 +30,7 @@ static bool matrix_uniform_up_to_date = false; static bool gr_ortho_override_active = false; static float gr_ortho_override_distance = 0.0f; -static matrix4 create_view_matrix(const vec3d* pos, const matrix* orient) +matrix4 create_view_matrix(const vec3d* pos, const matrix* orient) { vec3d scaled_pos; vec3d inv_pos; diff --git a/code/graphics/opengl/gropengldraw.h b/code/graphics/opengl/gropengldraw.h index d60f857a657..ce02d0e1bd0 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 render_cockpit_cascades, bool render_scene_cascades, 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/gropenglshader.cpp b/code/graphics/opengl/gropenglshader.cpp index 976c5c48a42..2c848c989db 100644 --- a/code/graphics/opengl/gropenglshader.cpp +++ b/code/graphics/opengl/gropenglshader.cpp @@ -519,7 +519,7 @@ static SCP_string handle_predefines(const char* filename, const SCP_string& orig SCP_unordered_map defines; output << "#define GLOBAL_FAR_Z " << std::fixed << std::setprecision(2) << Max_draw_distance << std::defaultfloat << '\n'; - output << "#define NUM_SHADOW_CASCADES " << Num_shadow_cascades << '\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 0c809c10c37..1f82404a752 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -470,7 +470,7 @@ static bool opengl_init_shadow_framebuffer(int size, GLenum color_format) 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); 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, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + 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); @@ -620,6 +620,9 @@ void gr_opengl_render_model(model_material* material_info, indexed_vertex_source GL_CHECK_FOR_ERRORS("end of render_buffer()"); } +static int Shadow_cascade_offset = 0; +static int Shadow_cascade_count = 0; + 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) { @@ -639,6 +642,9 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset Current_shader->program->Uniforms.setTextureUniform("transform_tex", 10); GL_state.Texture.Enable(10, GL_TEXTURE_BUFFER, opengl_get_transform_buffer_texture()); + Current_shader->program->Uniforms.setTextureUniform("cascade_offset", Shadow_cascade_offset); + Current_shader->program->Uniforms.setTextureUniform("cascade_count", Shadow_cascade_count); + GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE); gr_zbuffer_set(ZBUFFER_TYPE_FULL); gr_set_cull(1); @@ -664,7 +670,7 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset (GLsizei)datap->n_verts, element_type, ibuffer + datap->index_offset, - Num_shadow_cascades, + Shadow_cascade_count, base_vertex); } else { @@ -714,23 +720,41 @@ 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 render_cockpit_cascades, bool render_scene_cascades, 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); + + glDrawBuffers(0, nullptr); - //glDrawBuffer(GL_COLOR_ATTACHMENT0); - glDrawBuffers(0, nullptr); + glClear(GL_DEPTH_BUFFER_BIT); - glClear(GL_DEPTH_BUFFER_BIT); + Glowpoint_override_save = Glowpoint_override; + Glowpoint_override = true; - Glowpoint_override_save = Glowpoint_override; - Glowpoint_override = true; + gr_htl_projection_matrix_set = true; + } else { + gr_end_view_matrix(); + } - gr_htl_projection_matrix_set = true; + if (render_cockpit_cascades && render_scene_cascades) { + Shadow_cascade_offset = 0; + Shadow_cascade_count = Num_cockpit_shadow_cascades + Num_shadow_cascades; + } else if (render_cockpit_cascades) { + Shadow_cascade_offset = 0; + Shadow_cascade_count = Num_cockpit_shadow_cascades; + } else if (render_scene_cascades) { + Shadow_cascade_offset = Num_cockpit_shadow_cascades; + Shadow_cascade_count = Num_shadow_cascades; + } else { + Shadow_cascade_offset = 0; + Shadow_cascade_count = 0; + } gr_set_view_matrix(eye_pos, light_orient); diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index cb85ae65637..2717ca5f6af 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -12,6 +12,7 @@ #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" @@ -253,7 +254,7 @@ void shadows_debug_show_frustum(matrix* orient, vec3d *pos, float fov, float asp g3_render_line_3d(true, &far_bottom_left, &far_top_left); } -void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *light_matrix, matrix *orient, vec3d * /*pos*/, fov_t fov, float aspect, float z_near, float z_far) +void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *light_matrix, matrix *orient, vec3d *pos, fov_t fov, float aspect, float z_near, float z_far) { // find the widths and heights of the near plane and far plane to determine the points of this frustum float near_l, near_r, near_u, near_d; @@ -381,6 +382,18 @@ 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); + //TODO: account for leaning_position for more precise cockpit cascade coverage + if (pos != nullptr) { + vm_vec_add2(&near_bottom_left, pos); + vm_vec_add2(&near_bottom_right, pos); + vm_vec_add2(&near_top_right, pos); + vm_vec_add2(&near_top_left, pos); + vm_vec_add2(&far_top_left, pos); + vm_vec_add2(&far_top_right, pos); + vm_vec_add2(&far_bottom_right, pos); + vm_vec_add2(&far_bottom_left, pos); + } + vec3d frustum_pts[8]; // bring frustum points into light space @@ -460,12 +473,12 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float } float z_far = cascade_distances[i]; - shadows_construct_light_frustum(&Shadow_frustums[i], &light_matrix, eye_orient, eye_pos, fov, aspect, z_near, z_far); + shadows_construct_light_frustum(&Shadow_frustums[i], &light_matrix, eye_orient, nullptr, 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); + gr_shadow_map_start(&Shadow_view_matrix_light, &light_matrix, eye_pos, true, true, true); shadow_cascade_params_bind(); @@ -527,7 +540,92 @@ static bool shadow_obj_clip_plane(const object* objp, shadow_render_list::clip_p return false; } -void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) +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]; + + const bool hasCockpitModel = sip->cockpit_model_num >= 0; + + 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 renderCockpitModel = (Viewer_mode != VM_TOPDOWN) && hasCockpitModel && !Disable_cockpits; + const bool prerenderShipModel = renderShipModel && hasCockpitModel && !Cockpit_shares_coordinate_space; + const bool deferredRenderShipModel = renderShipModel && !prerenderShipModel; + + if (!deferredRenderShipModel && !renderCockpitModel) + return; + + 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; + + if (deferredRenderShipModel) { + bool all_cascades = ship_render_player_ship_casts_shadow(); + + matrix4 dummy_view; + gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, all_cascades, true, false); + shadow_cascade_params_bind(); + + 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); + viewer_list.init_render(false); + viewer_list.render_all(); + } + + if (renderCockpitModel && !Shadow_disable_overrides.disable_cockpit) { + matrix4 dummy_view; + gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, true, false, false); + shadow_cascade_params_bind(); + + 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); + + 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_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; @@ -552,6 +650,28 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) matrix light_matrix = shadows_start_render(eye_orient, eye_pos, fov, gr_screen.clip_aspect, 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; + + for (int i = 0; i < Num_cockpit_shadow_cascades && i < static_cast(Shadow_frustums.size()); i++) { + float z_near; + if (i == 0) { + z_near = 0.0f; + } else { + z_near = Shadow_distances[i - 1] - (Shadow_distances[i - 1] - (i >= 2 ? Shadow_distances[i - 2] : 0.0f)) * 0.2f; + } + float z_far = Shadow_distances[i]; + + shadows_construct_light_frustum(&Shadow_frustums[i], &light_matrix, eye_orient, nullptr, cockpit_fov, gr_screen.clip_aspect, z_near, z_far); + Shadow_proj_matrix[i] = Shadow_frustums[i].proj_matrix; + } + shadow_cascade_params_bind(); + shadow_render_list shadow_list; object *objp = Objects; @@ -571,6 +691,10 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) switch (objp->type) { case OBJ_SHIP: { + if (objp == Viewer_obj) { + continue; + } + ship* shipp = &Ships[objp->instance]; if (shipp->large_ship_blowup_index >= 0) { @@ -644,6 +768,8 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) shadow_list.init_render(false); shadow_list.render_all(); + render_viewer_shadow(Viewer_obj, &light_matrix, cam_offset, rot_offset); + shadows_end_render(); gr_zbias(0); @@ -840,10 +966,16 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, polymodel_instance* pmi, int obj_num, const vec3d* pos, const matrix* orient, - const clip_plane_info* clip) + const clip_plane_info* clip, + int detail_level_lock) { - float depth = model_render_determine_depth(obj_num, pm->id, orient, pos, -1); - int detail_level = model_render_determine_detail(depth, pm->id, -1); + 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]; list->clear_transforms(); @@ -865,9 +997,13 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, } 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(); - // Replacement textures may make a polygon invisible; skip it for shadow casting bool skip = false; if (pmi != nullptr && pmi->texture_replace != nullptr) { int replace = (*pmi->texture_replace)[tmap_num * TM_NUM_TYPES + TM_BASE_TYPE]; diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index df7d0e34fa9..680c10d5a7f 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -40,7 +40,8 @@ extern SCP_vector Shadow_cascade_distances; 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(); @@ -97,7 +98,8 @@ class shadow_render_list : public render_queue Shadow_distances; -SCP_vector Shadow_distances_cockpit; +static SCP_vector Shadow_distances_cockpit; SCP_vector Shadow_smoothness_factor; int Num_shadow_cascades = 4; int Num_cockpit_shadow_cascades = 4; @@ -1723,6 +1723,35 @@ void mod_table_init() // parse any modular tables parse_modular_table("*-mod.tbm", parse_mod_table); + //Validate and process shadow settings + { + //Since we're now merging cockpit shadow cascades into the main scene, we need to make sure that the cockpit shadow cascade distances are smaller than the main scene shadow cascade distances. + for (size_t i = 0; i < Shadow_distances_cockpit.size(); ++i) { + if (Shadow_distances_cockpit[i] >= Shadow_distances.front()) { + error_display(0, "Shadow Cascade Distances Cockpit: Values %d and onwards are larger than the smallest main-scene shadow cascade value. Dropping these cascades...", static_cast(i) ); + Shadow_distances_cockpit.resize(i); + Num_cockpit_shadow_cascades = static_cast(i); + break; + } + } + + //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())) { + error_display(0, "$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 = 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; + } + + //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(); + } + // if we have the troubleshoot commandline flag to override ingame options then disable them right after all // parsing so we can be sure it doesn't affect anything past this point during engine init. if (Cmdline_no_ingame_options && Using_in_game_options) { @@ -1859,7 +1888,7 @@ void mod_table_reset() Neb_affects_fireballs = false; 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}; + 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; diff --git a/code/mod_table/mod_table.h b/code/mod_table/mod_table.h index 5031de3d4fb..d74e35b0395 100644 --- a/code/mod_table/mod_table.h +++ b/code/mod_table/mod_table.h @@ -149,7 +149,6 @@ extern bool Neb_affects_particles; extern bool Neb_affects_fireballs; extern SCP_vector Shadow_distances; extern SCP_vector Shadow_smoothness_factor; -extern SCP_vector Shadow_distances_cockpit; extern int Num_shadow_cascades; extern int Num_cockpit_shadow_cascades; extern bool Show_ship_casts_shadow; diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index e5d1894b02b..8af36208fc8 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8476,6 +8476,29 @@ static void ship_find_warping_ship_helper(object *objp, dock_function_info *info } } +bool ship_render_player_ship_casts_shadow() { + if (Viewer_obj == nullptr) + return false; + + if (Shadow_disable_overrides.disable_cockpit || !Show_ship_casts_shadow) + 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 = 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)); + + //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; +} + 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]; ship_info* sip = &Ship_info[shipp->ship_info_index]; @@ -8571,43 +8594,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, - 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(); + } + if (light_deferredcockpit_enabled()) { gr_deferred_lighting_begin(true); @@ -8694,7 +8694,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(); diff --git a/code/ship/ship.h b/code/ship/ship.h index e3d7e6663eb..836da5e0bd2 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1695,6 +1695,7 @@ 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(); 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/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 From b545e4bb79dd57f67b105e6f0880471e90b27a98 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Mon, 15 Jun 2026 23:10:26 +0900 Subject: [PATCH 17/31] Wrangle Cockpit and show-ship shadows into correct behaviour --- code/graphics/shadows.cpp | 61 +++++++++++++++++++++++++++++++-------- code/graphics/shadows.h | 6 ++-- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 2717ca5f6af..a71f235906b 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -578,6 +578,9 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, 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 (deferredRenderShipModel) { bool all_cascades = ship_render_player_ship_casts_shadow(); @@ -594,7 +597,7 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, shadow_render_list viewer_list; shadow_render_list::add_model_draws(&viewer_list, pm, pmi, OBJ_INDEX(objp), - &eye_offset, &objp->orient, nullptr, 0); + &eye_offset, &objp->orient, nullptr, 0, &view_pos_local); viewer_list.init_render(false); viewer_list.render_all(); } @@ -615,9 +618,12 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, 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_offset, &objp->orient, nullptr, 0, &cockpit_view_local); cockpit_list.init_render(false); cockpit_list.render_all(); } @@ -713,7 +719,11 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, pmi = model_get_instance(shipp->model_instance_num); } - shadow_render_list::add_model_draws(&shadow_list, pm, pmi, OBJ_INDEX(objp), &objp->pos, &objp->orient, has_clip ? &clip : nullptr); + 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; } @@ -729,7 +739,11 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, pmi = model_get_instance(instance_num); } - shadow_render_list::add_model_draws(&shadow_list, pm, pmi, OBJ_INDEX(objp), &objp->pos, &objp->orient, nullptr); + 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; } @@ -740,7 +754,11 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, model_clear_instance(model_num); auto pm = model_get(model_num); - shadow_render_list::add_model_draws(&shadow_list, pm, nullptr, OBJ_INDEX(objp), &objp->pos, &objp->orient, nullptr); + 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, nullptr, OBJ_INDEX(objp), &objp->pos, &objp->orient, nullptr, -1, &view_pos_local); break; } @@ -756,7 +774,11 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, pmi = model_get_instance(db->model_instance_num); } - shadow_render_list::add_model_draws(&shadow_list, pm, pmi, db->objnum, &debris_obj->pos, &debris_obj->orient, nullptr); + 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; } @@ -967,7 +989,8 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, int obj_num, const vec3d* pos, const matrix* orient, const clip_plane_info* clip, - int detail_level_lock) + int detail_level_lock, + const vec3d* view_pos) { int detail_level; if (detail_level_lock >= 0) { @@ -978,6 +1001,13 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, } 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); @@ -1012,7 +1042,6 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, } else if (replace < 0 && base_tex < 0) { skip = true; } else if (replace >= 0) { - // Valid replacement, polygon should cast shadows; suppress the base_tex check below base_tex = 0; } } else if (base_tex < 0) { @@ -1031,13 +1060,14 @@ void shadow_render_list::add_model_draws(shadow_render_list* list, while (i >= 0) { if (!pm->submodel[i].flags[Model::Submodel_flags::Is_thruster]) { - render_submodel_children(list, pm, pmi, i, clip); + render_submodel_children(list, pm, pmi, i, clip, view_pos); } i = pm->submodel[i].next_sibling; } - list->add_submodel_to_batch(detail_root); + if (render_root_geometry) + list->add_submodel_to_batch(detail_root); list->pop_transform(); } @@ -1046,7 +1076,8 @@ void shadow_render_list::render_submodel_children(shadow_render_list* list, polymodel* pm, polymodel_instance* pmi, int mn, - const clip_plane_info* clip) + const clip_plane_info* clip, + const vec3d* view_pos) { bsp_info* sm = &pm->submodel[mn]; submodel_instance* smi = nullptr; @@ -1058,6 +1089,12 @@ void shadow_render_list::render_submodel_children(shadow_render_list* list, } } + 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; @@ -1074,7 +1111,7 @@ void shadow_render_list::render_submodel_children(shadow_render_list* list, 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); + render_submodel_children(list, pm, pmi, i, clip, view_pos); } i = pm->submodel[i].next_sibling; diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index 680c10d5a7f..a89d45ef6c5 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -99,7 +99,8 @@ class shadow_render_list : public render_queue Date: Mon, 15 Jun 2026 23:36:41 +0900 Subject: [PATCH 18/31] Cleanup --- code/graphics/shadows.cpp | 24 ++++++------------------ code/ship/ship.cpp | 2 +- code/ship/ship.h | 2 +- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index a71f235906b..a9caf3ae807 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -549,18 +549,6 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, ship* shipp = &Ships[objp->instance]; ship_info* sip = &Ship_info[shipp->ship_info_index]; - const bool hasCockpitModel = sip->cockpit_model_num >= 0; - - 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 renderCockpitModel = (Viewer_mode != VM_TOPDOWN) && hasCockpitModel && !Disable_cockpits; - const bool prerenderShipModel = renderShipModel && hasCockpitModel && !Cockpit_shares_coordinate_space; - const bool deferredRenderShipModel = renderShipModel && !prerenderShipModel; - - if (!deferredRenderShipModel && !renderCockpitModel) - return; - vec3d eye_pos_local; matrix eye_orient; object_get_eye(&eye_pos_local, &eye_orient, objp, true, true, false); @@ -581,11 +569,10 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, vec3d view_pos_local; vm_vec_rotate(&view_pos_local, &eye_pos_local, &objp->orient); - if (deferredRenderShipModel) { - bool all_cascades = ship_render_player_ship_casts_shadow(); - + //The player ship always casts shadows into the main scene, but only on the cockpit cascades when properly configured in the tables + { matrix4 dummy_view; - gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, all_cascades, true, false); + gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, ship_render_player_ship_casts_shadow_on_cockpit(), true, false); shadow_cascade_params_bind(); model_clear_instance(sip->model_num); @@ -597,11 +584,13 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, 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); + &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, true, false, false); @@ -627,7 +616,6 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, cockpit_list.init_render(false); cockpit_list.render_all(); } - } void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 8af36208fc8..c151e34b661 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8476,7 +8476,7 @@ static void ship_find_warping_ship_helper(object *objp, dock_function_info *info } } -bool ship_render_player_ship_casts_shadow() { +bool ship_render_player_ship_casts_shadow_on_cockpit() { if (Viewer_obj == nullptr) return false; diff --git a/code/ship/ship.h b/code/ship/ship.h index 836da5e0bd2..3e0fd54cd9b 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1695,7 +1695,7 @@ 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(); +extern bool ship_render_player_ship_casts_shadow_on_cockpit(); 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 ); From e3962f8d3c9a0b39a6b9d0c258367dddb4a7589c Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Mon, 15 Jun 2026 23:41:24 +0900 Subject: [PATCH 19/31] make player ship shadow table options more intuitive --- code/graphics/shadows.cpp | 3 +-- code/ship/ship.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index a9caf3ae807..b6a56bab277 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -569,8 +569,7 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, vec3d view_pos_local; vm_vec_rotate(&view_pos_local, &eye_pos_local, &objp->orient); - //The player ship always casts shadows into the main scene, but only on the cockpit cascades when properly configured in the tables - { + if (Show_ship_casts_shadow) { matrix4 dummy_view; gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, ship_render_player_ship_casts_shadow_on_cockpit(), true, false); shadow_cascade_params_bind(); diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index c151e34b661..4408a6598e3 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8480,7 +8480,7 @@ bool ship_render_player_ship_casts_shadow_on_cockpit() { if (Viewer_obj == nullptr) return false; - if (Shadow_disable_overrides.disable_cockpit || !Show_ship_casts_shadow) + if (Shadow_disable_overrides.disable_cockpit) return false; ship* shipp = &Ships[Viewer_obj->instance]; From 9ba01456f3087f4b833e0bd90dc20226ddb89d2c Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:26:07 +0900 Subject: [PATCH 20/31] Cleanup --- code/graphics/shadows.cpp | 30 ++++++++----------------- code/graphics/shadows.h | 2 +- code/menuui/techmenu.cpp | 2 +- code/missionui/missionbrief.cpp | 2 +- code/missionui/missionscreencommon.cpp | 8 +++---- code/missionui/missionweaponchoice.cpp | 1 + code/ship/ship.cpp | 31 +++++++++++++++++++------- code/ship/ship.h | 1 + 8 files changed, 41 insertions(+), 36 deletions(-) diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index b6a56bab277..538916e8f57 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -445,7 +445,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, const SCP_vector& cascade_distances) +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; @@ -464,21 +464,23 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float Shadow_cascade_distances.resize(num_cascades); Shadow_proj_matrix.resize(num_cascades); + bool render_cockpit_cascades = ship_render_player_has_closeup_visuals(); + for (int i = 0; i < num_cascades; i++) { float z_near; - if (i == 0) { + 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, fov, aspect, z_near, z_far); - Shadow_cascade_distances[i] = 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] = !render_cockpit_cascades && i <= Num_cockpit_shadow_cascades ? 0.f : 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, true, true); + gr_shadow_map_start(&Shadow_view_matrix_light, &light_matrix, eye_pos, render_cockpit_cascades, true, true); shadow_cascade_params_bind(); @@ -640,9 +642,6 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, gr_end_proj_matrix(); gr_end_view_matrix(); - matrix light_matrix = shadows_start_render(eye_orient, eye_pos, fov, gr_screen.clip_aspect, - Shadow_distances); - fov_t cockpit_fov; if (fov_override) cockpit_fov = *fov_override; @@ -651,19 +650,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, else cockpit_fov = COCKPIT_ZOOM_DEFAULT; - for (int i = 0; i < Num_cockpit_shadow_cascades && i < static_cast(Shadow_frustums.size()); i++) { - float z_near; - if (i == 0) { - z_near = 0.0f; - } else { - z_near = Shadow_distances[i - 1] - (Shadow_distances[i - 1] - (i >= 2 ? Shadow_distances[i - 2] : 0.0f)) * 0.2f; - } - float z_far = Shadow_distances[i]; - - shadows_construct_light_frustum(&Shadow_frustums[i], &light_matrix, eye_orient, nullptr, cockpit_fov, gr_screen.clip_aspect, z_near, z_far); - Shadow_proj_matrix[i] = Shadow_frustums[i].proj_matrix; - } - shadow_cascade_params_bind(); + matrix light_matrix = shadows_start_render(eye_orient, eye_pos, fov, cockpit_fov, gr_screen.clip_aspect, Shadow_distances); shadow_render_list shadow_list; @@ -779,6 +766,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, render_viewer_shadow(Viewer_obj, &light_matrix, cam_offset, rot_offset); + shadows_end_render(); gr_zbias(0); diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index a89d45ef6c5..3c6b4f88fe4 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -47,7 +47,7 @@ void shadow_cascade_params_init(); void shadow_cascade_params_shutdown(); void shadow_cascade_params_bind(); -matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, float aspect, const SCP_vector& cascade_distances); +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(); /** diff --git a/code/menuui/techmenu.cpp b/code/menuui/techmenu.cpp index 1005b39a6cd..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 5f6424872cd..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 323a4879cc5..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 dcb918ff952..b652a42536c 100644 --- a/code/missionui/missionweaponchoice.cpp +++ b/code/missionui/missionweaponchoice.cpp @@ -844,6 +844,7 @@ 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 + 200.0f, diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 4408a6598e3..ae5297a98ba 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8476,6 +8476,12 @@ static void ship_find_warping_ship_helper(object *objp, dock_function_info *info } } +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; @@ -8487,10 +8493,7 @@ bool ship_render_player_ship_casts_shadow_on_cockpit() { ship_info* sip = &Ship_info[shipp->ship_info_index]; const bool hasCockpitModel = sip->cockpit_model_num >= 0; - - 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); //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, @@ -8499,6 +8502,21 @@ bool ship_render_player_ship_casts_shadow_on_cockpit() { 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]; ship_info* sip = &Ship_info[shipp->ship_info_index]; @@ -8507,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 diff --git a/code/ship/ship.h b/code/ship/ship.h index 3e0fd54cd9b..b951ab131bb 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1696,6 +1696,7 @@ 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 ); From d56417b639e13c7dd47bba36442b17c6602338eb Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:57:54 +0900 Subject: [PATCH 21/31] Only read from correct set of shadow maps, also stretch first main-scene map to 0 --- code/def_files/data/effects/deferred-f.sdr | 5 +++- code/def_files/data/effects/main-f.sdr | 5 +++- code/def_files/data/effects/main-v.sdr | 3 ++ code/def_files/data/effects/shadow_map-g.sdr | 6 ++-- code/def_files/data/effects/shadow_map-v.sdr | 4 ++- code/def_files/data/effects/shadows.sdr | 15 +++++----- code/graphics/opengl/gropengldeferred.cpp | 4 ++- code/graphics/opengl/gropengltnl.cpp | 3 -- code/graphics/shadows.cpp | 30 ++++++++++++++------ code/graphics/shadows.h | 2 +- code/object/objectsort.cpp | 6 ++++ code/ship/ship.cpp | 2 +- 12 files changed, 57 insertions(+), 28 deletions(-) diff --git a/code/def_files/data/effects/deferred-f.sdr b/code/def_files/data/effects/deferred-f.sdr index 364f4fd8860..aa9b29737a9 100644 --- a/code/def_files/data/effects/deferred-f.sdr +++ b/code/def_files/data/effects/deferred-f.sdr @@ -26,6 +26,9 @@ layout (std140) uniform globalDeferredData { }; layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES]; @@ -262,7 +265,7 @@ void main() fragShadowUV[i] = transformToShadowMap(shadow_proj_matrix[i], i, fragShadowPos); } - attenuation *= getShadowValue(shadow_map, -position.z, fragShadowUV, cascade_distances, smoothness_factors); + 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 1d89a160ccd..673f533c398 100644 --- a/code/def_files/data/effects/main-f.sdr +++ b/code/def_files/data/effects/main-f.sdr @@ -87,6 +87,9 @@ layout (std140) uniform modelData { }; layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES]; @@ -327,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.shadowUV, cascade_distances, smoothness_factors); + 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 diff --git a/code/def_files/data/effects/main-v.sdr b/code/def_files/data/effects/main-v.sdr index aa21fd0e1cf..098dcdf4418 100644 --- a/code/def_files/data/effects/main-v.sdr +++ b/code/def_files/data/effects/main-v.sdr @@ -90,6 +90,9 @@ layout (std140) uniform modelData { }; layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES]; diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr index 483e93cbe01..a75c731a258 100644 --- a/code/def_files/data/effects/shadow_map-g.sdr +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -8,15 +8,15 @@ layout(invocations = NUM_SHADOW_CASCADES) in; #endif layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES]; float smoothness_factors[NUM_SHADOW_CASCADES]; }; -uniform int cascade_offset; -uniform int cascade_count; - in VertexOutput { #if !defined(GL_ARB_gpu_shader5) float instance; diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index 32d6c162fd2..30d3f21813e 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -20,6 +20,9 @@ layout (std140) uniform shadowMapData { }; layout (std140) uniform shadowCascadeParams { + int cascade_offset; + int cascade_count; + float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES]; @@ -27,7 +30,6 @@ layout (std140) uniform shadowCascadeParams { }; uniform samplerBuffer transform_tex; -uniform int cascade_offset; #ifdef GEOMETRY_FALLBACK out VertexOutput { diff --git a/code/def_files/data/effects/shadows.sdr b/code/def_files/data/effects/shadows.sdr index 3429fe41d6f..49ff206d42b 100644 --- a/code/def_files/data/effects/shadows.sdr +++ b/code/def_files/data/effects/shadows.sdr @@ -54,19 +54,20 @@ float samplePoissonPCSS(sampler2DArrayShadow shadow_map, vec4 shadowUV, float ma } float getShadowValue(sampler2DArrayShadow shadow_map, float depth, vec4 shadowUV[NUM_SHADOW_CASCADES], - float cascade_distances[NUM_SHADOW_CASCADES], float smoothness_factors[NUM_SHADOW_CASCADES]) + float cascade_distances[NUM_SHADOW_CASCADES], float smoothness_factors[NUM_SHADOW_CASCADES], + int cascade_offset, int cascade_count) { - int cascade = NUM_SHADOW_CASCADES; - for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - cascade -= int(step(depth, cascade_distances[i])); + 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])); } - if (cascade >= NUM_SHADOW_CASCADES || cascade < 0) return 1.0; + if (cascade >= cascade_offset + cascade_count || cascade < cascade_offset) return 1.0; - float cascade_start = (cascade > 0) ? cascade_distances[cascade - 1] : 0.0; + float cascade_start = (cascade > cascade_offset) ? cascade_distances[cascade - 1] : 0.0; float cascade_end = cascade_distances[cascade]; float dist_threshold = (cascade_end - cascade_start) * 0.2; - if (cascade_end - dist_threshold > depth || cascade >= NUM_SHADOW_CASCADES - 1) + if (cascade_end - dist_threshold > depth || cascade >= cascade_offset + cascade_count - 1) return samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade]); return mix( diff --git a/code/graphics/opengl/gropengldeferred.cpp b/code/graphics/opengl/gropengldeferred.cpp index 5ba064a1df4..1fd22204de8 100644 --- a/code/graphics/opengl/gropengldeferred.cpp +++ b/code/graphics/opengl/gropengldeferred.cpp @@ -316,7 +316,9 @@ void gr_opengl_deferred_lighting_finish() auto header = light_uniform_aligner.getHeader(); if (Shadow_quality != ShadowQuality::Disabled) { vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render); - shadow_cascade_params_bind(); + 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/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 1f82404a752..94992c997e8 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -642,9 +642,6 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset Current_shader->program->Uniforms.setTextureUniform("transform_tex", 10); GL_state.Texture.Enable(10, GL_TEXTURE_BUFFER, opengl_get_transform_buffer_texture()); - Current_shader->program->Uniforms.setTextureUniform("cascade_offset", Shadow_cascade_offset); - Current_shader->program->Uniforms.setTextureUniform("cascade_count", Shadow_cascade_count); - GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE); gr_zbuffer_set(ZBUFFER_TYPE_FULL); gr_set_cull(1); diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 538916e8f57..cc44844c5d6 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -458,7 +458,7 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t vm_vec_copy_normalize(&light_dir, &lp.vec); vm_vector_2_matrix_norm(&light_matrix, &light_dir, &eye_orient->vec.uvec, nullptr); - const int num_cascades = static_cast(cascade_distances.size()); + const int num_cascades = Num_shadow_cascades + Num_cockpit_shadow_cascades; Shadow_frustums.resize(num_cascades); Shadow_cascade_distances.resize(num_cascades); @@ -468,7 +468,7 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t for (int i = 0; i < num_cascades; i++) { float z_near; - if (i == 0 || (!render_cockpit_cascades && i <= Num_cockpit_shadow_cascades)) { + 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; @@ -476,13 +476,13 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t 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] = !render_cockpit_cascades && i <= Num_cockpit_shadow_cascades ? 0.f : cascade_distances[i]; + 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, render_cockpit_cascades, true, true); - shadow_cascade_params_bind(); + shadow_cascade_params_bind(0, num_cascades); return light_matrix; } @@ -573,8 +573,13 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, if (Show_ship_casts_shadow) { matrix4 dummy_view; - gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, ship_render_player_ship_casts_shadow_on_cockpit(), true, false); - shadow_cascade_params_bind(); + bool casts_shadow_on_cockpit = ship_render_player_ship_casts_shadow_on_cockpit(); + + gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, casts_shadow_on_cockpit, true, 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; @@ -595,7 +600,7 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, if (renderCockpitModel && !Shadow_disable_overrides.disable_cockpit) { matrix4 dummy_view; gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, true, false, false); - shadow_cascade_params_bind(); + shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades); vec3d cockpit_offset = sip->cockpit_offset; vm_vec_unrotate(&cockpit_offset, &cockpit_offset, &objp->orient); @@ -803,7 +808,8 @@ static gr_buffer_handle Shadow_cascade_params_buffer; static size_t Shadow_cascade_params_buffer_size = 0; static size_t compute_cascade_params_size(int num_cascades) { - return sizeof(matrix4) + return 16 // cascade_offset, cascade_count, padding + + sizeof(matrix4) + sizeof(matrix4) * num_cascades + 16 * num_cascades + 16 * num_cascades; @@ -824,7 +830,7 @@ void shadow_cascade_params_shutdown() { } } -void shadow_cascade_params_bind() { +void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { if (!Shadow_cascade_params_buffer.isValid()) { return; } @@ -840,6 +846,12 @@ void shadow_cascade_params_bind() { uint8_t* ptr = buffer.data(); size_t offset = 0; + memcpy(ptr + offset, &cascade_offset, sizeof(int)); + offset += sizeof(int); + memcpy(ptr + offset, &cascade_count, sizeof(int)); + offset += sizeof(int); + offset += 8; // padding to align mat4 to 16 bytes + memcpy(ptr + offset, &Shadow_view_matrix_light, sizeof(matrix4)); offset += sizeof(matrix4); diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index 3c6b4f88fe4..1702e2ebdef 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -45,7 +45,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, void shadow_cascade_params_init(); void shadow_cascade_params_shutdown(); -void shadow_cascade_params_bind(); +void shadow_cascade_params_bind(int cascade_offset, int cascade_count); 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(); 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/ship/ship.cpp b/code/ship/ship.cpp index ae5297a98ba..3cd9cae6819 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -8620,7 +8620,7 @@ void ship_render_player_ship(object* objp, const vec3d* cam_offset, const matrix 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(); + shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades); } if (light_deferredcockpit_enabled()) { From e4d3d07c8a35b019c2f368e92f86d59620746fa7 Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:59:06 +0900 Subject: [PATCH 22/31] Remove now non-necessary validation --- code/mod_table/mod_table.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index a1d26cb93ea..708ed03dfd3 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -1725,16 +1725,6 @@ void mod_table_init() //Validate and process shadow settings { - //Since we're now merging cockpit shadow cascades into the main scene, we need to make sure that the cockpit shadow cascade distances are smaller than the main scene shadow cascade distances. - for (size_t i = 0; i < Shadow_distances_cockpit.size(); ++i) { - if (Shadow_distances_cockpit[i] >= Shadow_distances.front()) { - error_display(0, "Shadow Cascade Distances Cockpit: Values %d and onwards are larger than the smallest main-scene shadow cascade value. Dropping these cascades...", static_cast(i) ); - Shadow_distances_cockpit.resize(i); - Num_cockpit_shadow_cascades = static_cast(i); - break; - } - } - //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())) { error_display(0, "$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); From b67bea090ad8b1ed2a98723d69e38ad47b516d42 Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:31:01 +0900 Subject: [PATCH 23/31] Make shadow cascade buffer more typesafe and less wasteful --- code/def_files/data/effects/deferred-f.sdr | 4 +- code/def_files/data/effects/main-f.sdr | 4 +- code/def_files/data/effects/main-v.sdr | 4 +- code/def_files/data/effects/shadow_map-g.sdr | 4 +- code/def_files/data/effects/shadow_map-v.sdr | 4 +- code/def_files/data/effects/shadows.sdr | 17 ++++--- code/graphics/shadows.cpp | 49 +++++++++++--------- code/graphics/util/uniform_structs.h | 7 +++ 8 files changed, 55 insertions(+), 38 deletions(-) diff --git a/code/def_files/data/effects/deferred-f.sdr b/code/def_files/data/effects/deferred-f.sdr index aa9b29737a9..3b87f289387 100644 --- a/code/def_files/data/effects/deferred-f.sdr +++ b/code/def_files/data/effects/deferred-f.sdr @@ -31,8 +31,8 @@ layout (std140) uniform shadowCascadeParams { float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; - float cascade_distances[NUM_SHADOW_CASCADES]; - float smoothness_factors[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 { diff --git a/code/def_files/data/effects/main-f.sdr b/code/def_files/data/effects/main-f.sdr index 673f533c398..50904982e34 100644 --- a/code/def_files/data/effects/main-f.sdr +++ b/code/def_files/data/effects/main-f.sdr @@ -92,8 +92,8 @@ layout (std140) uniform shadowCascadeParams { float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; - float cascade_distances[NUM_SHADOW_CASCADES]; - float smoothness_factors[NUM_SHADOW_CASCADES]; + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; + vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; }; in VertexOutput { diff --git a/code/def_files/data/effects/main-v.sdr b/code/def_files/data/effects/main-v.sdr index 098dcdf4418..950e7df2e5e 100644 --- a/code/def_files/data/effects/main-v.sdr +++ b/code/def_files/data/effects/main-v.sdr @@ -95,8 +95,8 @@ layout (std140) uniform shadowCascadeParams { float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; - float cascade_distances[NUM_SHADOW_CASCADES]; - float smoothness_factors[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 diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr index a75c731a258..99e7d77b407 100644 --- a/code/def_files/data/effects/shadow_map-g.sdr +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -13,8 +13,8 @@ layout (std140) uniform shadowCascadeParams { float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; - float cascade_distances[NUM_SHADOW_CASCADES]; - float smoothness_factors[NUM_SHADOW_CASCADES]; + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; + vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; }; in VertexOutput { diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index 30d3f21813e..4a958a7bd25 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -25,8 +25,8 @@ layout (std140) uniform shadowCascadeParams { float pad[2]; mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; - float cascade_distances[NUM_SHADOW_CASCADES]; - float smoothness_factors[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; diff --git a/code/def_files/data/effects/shadows.sdr b/code/def_files/data/effects/shadows.sdr index 49ff206d42b..a9437d87976 100644 --- a/code/def_files/data/effects/shadows.sdr +++ b/code/def_files/data/effects/shadows.sdr @@ -54,24 +54,27 @@ float samplePoissonPCSS(sampler2DArrayShadow shadow_map, vec4 shadowUV, float ma } float getShadowValue(sampler2DArrayShadow shadow_map, float depth, vec4 shadowUV[NUM_SHADOW_CASCADES], - float cascade_distances[NUM_SHADOW_CASCADES], float smoothness_factors[NUM_SHADOW_CASCADES], + vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4], vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4], int cascade_offset, int cascade_count) { 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])); + 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; - float cascade_start = (cascade > cascade_offset) ? cascade_distances[cascade - 1] : 0.0; - float cascade_end = cascade_distances[cascade]; + int cascade_v = cascade / 4; + int cascade_i = cascade % 4; + + 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; if (cascade_end - dist_threshold > depth || cascade >= cascade_offset + cascade_count - 1) - return samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade]); + return samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade_v][cascade_i]); return mix( - samplePoissonPCSS(shadow_map, shadowUV[cascade], smoothness_factors[cascade]), - samplePoissonPCSS(shadow_map, shadowUV[cascade + 1], smoothness_factors[cascade + 1]), + 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/shadows.cpp b/code/graphics/shadows.cpp index cc44844c5d6..4261da62ffe 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -807,16 +807,20 @@ void shadow_end_frame() { static gr_buffer_handle Shadow_cascade_params_buffer; static size_t Shadow_cascade_params_buffer_size = 0; -static size_t compute_cascade_params_size(int num_cascades) { - return 16 // cascade_offset, cascade_count, padding - + sizeof(matrix4) +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 - + 16 * num_cascades - + 16 * 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); + Shadow_cascade_params_buffer_size = compute_cascade_params_size(Num_shadow_cascades).first; Shadow_cascade_params_buffer = gr_create_buffer(BufferType::Uniform, BufferUsageHint::Dynamic); SCP_vector zero_data(Shadow_cascade_params_buffer_size, 0); @@ -836,39 +840,42 @@ void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { } const int num_cascades = static_cast(Shadow_proj_matrix.size()); - const size_t required_size = compute_cascade_params_size(num_cascades); + const auto [required_size, padding] = compute_cascade_params_size(num_cascades); if (required_size > Shadow_cascade_params_buffer_size) { Shadow_cascade_params_buffer_size = required_size; } SCP_vector buffer(required_size, 0); - uint8_t* ptr = buffer.data(); size_t offset = 0; - memcpy(ptr + offset, &cascade_offset, sizeof(int)); - offset += sizeof(int); - memcpy(ptr + offset, &cascade_count, sizeof(int)); - offset += sizeof(int); - offset += 8; // padding to align mat4 to 16 bytes + 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; - memcpy(ptr + offset, &Shadow_view_matrix_light, sizeof(matrix4)); - offset += sizeof(matrix4); + offset += sizeof(graphics::shadow_cascade_static_data); for (int i = 0; i < num_cascades; i++) { - memcpy(ptr + offset, &Shadow_proj_matrix[i], sizeof(matrix4)); + 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++) { - memcpy(ptr + offset, &Shadow_cascade_distances[i], sizeof(float)); - offset += 16; + auto& cascade_distance = *reinterpret_cast(buffer.data() + offset); + cascade_distance = Shadow_cascade_distances[i]; + offset += sizeof(float); } + offset += sizeof(float) * padding; - for (size_t i = 0; i < Shadow_smoothness_factor.size() && i < static_cast(num_cascades); i++) { - memcpy(ptr + offset, &Shadow_smoothness_factor[i], sizeof(float)); - offset += 16; + 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(Shadow_cascade_params_buffer, required_size, buffer.data()); gr_bind_uniform_buffer(uniform_block_type::ShadowCascadeParams, 0, required_size, Shadow_cascade_params_buffer); diff --git a/code/graphics/util/uniform_structs.h b/code/graphics/util/uniform_structs.h index 82275143c8c..c55c8194e90 100644 --- a/code/graphics/util/uniform_structs.h +++ b/code/graphics/util/uniform_structs.h @@ -141,6 +141,13 @@ struct shadow_uniform_data { 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 }; From a2795988317cfe3643c1a93585f63cbde20bf431 Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:22:04 +0900 Subject: [PATCH 24/31] Don't allocate cockpit cascades if not used --- code/mod_table/mod_table.cpp | 45 +++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index 708ed03dfd3..5179bc88c07 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -1723,25 +1723,6 @@ void mod_table_init() // parse any modular tables parse_modular_table("*-mod.tbm", parse_mod_table); - //Validate and process shadow settings - { - //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())) { - error_display(0, "$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 = 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; - } - - //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(); - } - // if we have the troubleshoot commandline flag to override ingame options then disable them right after all // parsing so we can be sure it doesn't affect anything past this point during engine init. if (Cmdline_no_ingame_options && Using_in_game_options) { @@ -1768,6 +1749,32 @@ void mod_table_post_process() mprintf(("Game Settings Table: Show-subtitle adjusted resolution is (%d, %d)\n", Show_subtitle_screen_adjusted_res[0], Show_subtitle_screen_adjusted_res[1])); // we don't need to calculate adjusted resolution for hud-set-coords because that function doesn't do screen scaling + + //Validate and process shadow settings + { + //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 = 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(); + } } bool mod_supports_version(int major, int minor, int build) From d93b05941200055dd0eefff3157b44a222b2dbb4 Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:51:11 +0900 Subject: [PATCH 25/31] Cleanup --- code/graphics/matrix.cpp | 2 +- code/graphics/opengl/gropengltnl.cpp | 30 ++++++++++++---------------- code/graphics/shadows.cpp | 22 ++++---------------- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/code/graphics/matrix.cpp b/code/graphics/matrix.cpp index 9f89cb0d763..c7681159191 100644 --- a/code/graphics/matrix.cpp +++ b/code/graphics/matrix.cpp @@ -30,7 +30,7 @@ static bool matrix_uniform_up_to_date = false; static bool gr_ortho_override_active = false; static float gr_ortho_override_distance = 0.0f; -matrix4 create_view_matrix(const vec3d* pos, const matrix* orient) +static matrix4 create_view_matrix(const vec3d* pos, const matrix* orient) { vec3d scaled_pos; vec3d inv_pos; diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 94992c997e8..2c5f67d78b8 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -451,9 +451,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); @@ -535,11 +535,9 @@ 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) { @@ -662,6 +660,7 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset GLenum element_type = (datap->flags & VB_FLAG_LARGE_INDEX) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; GLint base_vertex = (GLint)(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, @@ -739,20 +738,18 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light gr_end_view_matrix(); } - if (render_cockpit_cascades && render_scene_cascades) { - Shadow_cascade_offset = 0; - Shadow_cascade_count = Num_cockpit_shadow_cascades + Num_shadow_cascades; - } else if (render_cockpit_cascades) { + if (render_cockpit_cascades) { Shadow_cascade_offset = 0; Shadow_cascade_count = Num_cockpit_shadow_cascades; - } else if (render_scene_cascades) { + } + else { Shadow_cascade_offset = Num_cockpit_shadow_cascades; - Shadow_cascade_count = Num_shadow_cascades; - } else { - Shadow_cascade_offset = 0; Shadow_cascade_count = 0; } + if (render_scene_cascades) + Shadow_cascade_count += Num_shadow_cascades; + gr_set_view_matrix(eye_pos, light_orient); *shadow_view_matrix = gr_view_matrix; @@ -816,7 +813,6 @@ void opengl_tnl_set_material(material* material_info, bool set_base_map, bool se GL_state.ClipDistance(0, false); } else { Assertion(Current_shader != nullptr && (Current_shader->shader == SDR_TYPE_MODEL - || Current_shader->shader == SDR_TYPE_SHADOW_MAP_GEN || Current_shader->shader == SDR_TYPE_DEFAULT_MATERIAL), "Clip planes are not supported by this shader!"); @@ -880,7 +876,7 @@ void opengl_tnl_set_model_material(model_material *material_info) gr_set_center_alpha(material_info->get_center_alpha()); - Assert( Current_shader->shader == SDR_TYPE_MODEL || Current_shader->shader == SDR_TYPE_SHADOW_MAP_GEN ); + Assert( Current_shader->shader == SDR_TYPE_MODEL ); GL_state.Texture.SetShaderMode(GL_TRUE); diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 4261da62ffe..bb19adb6f1c 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -254,7 +254,7 @@ void shadows_debug_show_frustum(matrix* orient, vec3d *pos, float fov, float asp g3_render_line_3d(true, &far_bottom_left, &far_top_left); } -void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *light_matrix, matrix *orient, vec3d *pos, fov_t fov, float aspect, float z_near, float z_far) +void shadows_construct_light_frustum(light_frustum_info *shadow_data, matrix *light_matrix, matrix *orient, vec3d * /*pos*/, fov_t fov, float aspect, float z_near, float z_far) { // find the widths and heights of the near plane and far plane to determine the points of this frustum float near_l, near_r, near_u, near_d; @@ -381,18 +381,6 @@ 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); - - //TODO: account for leaning_position for more precise cockpit cascade coverage - if (pos != nullptr) { - vm_vec_add2(&near_bottom_left, pos); - vm_vec_add2(&near_bottom_right, pos); - vm_vec_add2(&near_top_right, pos); - vm_vec_add2(&near_top_left, pos); - vm_vec_add2(&far_top_left, pos); - vm_vec_add2(&far_top_right, pos); - vm_vec_add2(&far_bottom_right, pos); - vm_vec_add2(&far_bottom_left, pos); - } vec3d frustum_pts[8]; @@ -820,7 +808,7 @@ static std::pair compute_cascade_params_size(int num_cascades) { } void shadow_cascade_params_init() { - Shadow_cascade_params_buffer_size = compute_cascade_params_size(Num_shadow_cascades).first; + 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); @@ -839,12 +827,10 @@ void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { return; } - const int num_cascades = static_cast(Shadow_proj_matrix.size()); + const int num_cascades = Num_shadow_cascades + Num_cockpit_shadow_cascades; const auto [required_size, padding] = compute_cascade_params_size(num_cascades); - if (required_size > Shadow_cascade_params_buffer_size) { - Shadow_cascade_params_buffer_size = required_size; - } + Assertion(required_size == Shadow_cascade_params_buffer_size, "The shadow cascade parameter buffer changed size!"); SCP_vector buffer(required_size, 0); size_t offset = 0; From 5604ffd14addc12a9f9faa15853ce37c55bfff47 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 21:43:36 +0900 Subject: [PATCH 26/31] Final fixes --- code/def_files/data/effects/deferred-f.sdr | 2 +- code/def_files/data/effects/main-f.sdr | 2 +- code/def_files/data/effects/main-v.sdr | 2 +- code/def_files/data/effects/shadow_map-g.sdr | 2 +- code/def_files/data/effects/shadow_map-v.sdr | 2 +- code/graphics/2d.h | 2 +- code/graphics/grstub.cpp | 2 +- code/graphics/opengl/gropengldraw.h | 2 +- code/graphics/opengl/gropengltnl.cpp | 23 ++++------- code/graphics/shadows.cpp | 19 ++++++---- code/graphics/vulkan/vulkan_stubs.cpp | 2 +- code/mod_table/mod_table.cpp | 40 ++++++++++---------- 12 files changed, 48 insertions(+), 52 deletions(-) diff --git a/code/def_files/data/effects/deferred-f.sdr b/code/def_files/data/effects/deferred-f.sdr index 3b87f289387..2622a8a92f3 100644 --- a/code/def_files/data/effects/deferred-f.sdr +++ b/code/def_files/data/effects/deferred-f.sdr @@ -28,7 +28,7 @@ layout (std140) uniform globalDeferredData { layout (std140) uniform shadowCascadeParams { int cascade_offset; int cascade_count; - float pad[2]; + mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; diff --git a/code/def_files/data/effects/main-f.sdr b/code/def_files/data/effects/main-f.sdr index 50904982e34..e5ed5de9c51 100644 --- a/code/def_files/data/effects/main-f.sdr +++ b/code/def_files/data/effects/main-f.sdr @@ -89,7 +89,7 @@ layout (std140) uniform modelData { layout (std140) uniform shadowCascadeParams { int cascade_offset; int cascade_count; - float pad[2]; + mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; diff --git a/code/def_files/data/effects/main-v.sdr b/code/def_files/data/effects/main-v.sdr index 950e7df2e5e..ee59cec19f8 100644 --- a/code/def_files/data/effects/main-v.sdr +++ b/code/def_files/data/effects/main-v.sdr @@ -92,7 +92,7 @@ layout (std140) uniform modelData { layout (std140) uniform shadowCascadeParams { int cascade_offset; int cascade_count; - float pad[2]; + mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr index 99e7d77b407..d7c8bf8a998 100644 --- a/code/def_files/data/effects/shadow_map-g.sdr +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -10,7 +10,7 @@ layout(invocations = NUM_SHADOW_CASCADES) in; layout (std140) uniform shadowCascadeParams { int cascade_offset; int cascade_count; - float pad[2]; + mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index 4a958a7bd25..e445e13830c 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -22,7 +22,7 @@ layout (std140) uniform shadowMapData { layout (std140) uniform shadowCascadeParams { int cascade_offset; int cascade_count; - float pad[2]; + mat4 shadow_mv_matrix; mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES]; vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4]; diff --git a/code/graphics/2d.h b/code/graphics/2d.h index 69a388875b9..527ffcd5095 100644 --- a/code/graphics/2d.h +++ b/code/graphics/2d.h @@ -840,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; diff --git a/code/graphics/grstub.cpp b/code/graphics/grstub.cpp index b5ce243123a..3803c668b8e 100644 --- a/code/graphics/grstub.cpp +++ b/code/graphics/grstub.cpp @@ -299,7 +299,7 @@ int gr_stub_maybe_create_shader(shader_type /*shader_t*/, unsigned int /*flags return -1; } -void gr_stub_shadow_map_start(matrix4 * /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/, bool /*render_cockpit_cascades*/, bool /*render_scene_cascades*/, bool /*first_pass*/) +void gr_stub_shadow_map_start(matrix4 * /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/, bool /*first_pass*/) { } diff --git a/code/graphics/opengl/gropengldraw.h b/code/graphics/opengl/gropengldraw.h index ce02d0e1bd0..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, bool render_cockpit_cascades, bool render_scene_cascades, bool first_pass); +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/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 2c5f67d78b8..11edeb8ee6f 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -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() @@ -618,8 +623,7 @@ void gr_opengl_render_model(model_material* material_info, indexed_vertex_source GL_CHECK_FOR_ERRORS("end of render_buffer()"); } -static int Shadow_cascade_offset = 0; -static int Shadow_cascade_count = 0; +int Shadow_cascade_count = 0; 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) @@ -716,8 +720,7 @@ 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, - bool render_cockpit_cascades, bool render_scene_cascades, bool first_pass) +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; @@ -738,18 +741,6 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light gr_end_view_matrix(); } - if (render_cockpit_cascades) { - Shadow_cascade_offset = 0; - Shadow_cascade_count = Num_cockpit_shadow_cascades; - } - else { - Shadow_cascade_offset = Num_cockpit_shadow_cascades; - Shadow_cascade_count = 0; - } - - if (render_scene_cascades) - Shadow_cascade_count += Num_shadow_cascades; - gr_set_view_matrix(eye_pos, light_orient); *shadow_view_matrix = gr_view_matrix; diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index bb19adb6f1c..08a22d5c4b8 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -468,9 +468,11 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t Shadow_proj_matrix[i] = Shadow_frustums[i].proj_matrix; } - gr_shadow_map_start(&Shadow_view_matrix_light, &light_matrix, eye_pos, render_cockpit_cascades, true, true); - - shadow_cascade_params_bind(0, num_cascades); + 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; } @@ -563,7 +565,7 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, 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, casts_shadow_on_cockpit, true, false); + 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 @@ -587,7 +589,7 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix, if (renderCockpitModel && !Shadow_disable_overrides.disable_cockpit) { matrix4 dummy_view; - gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, true, false, false); + 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; @@ -822,6 +824,7 @@ void shadow_cascade_params_shutdown() { } } +extern int Shadow_cascade_count; void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { if (!Shadow_cascade_params_buffer.isValid()) { return; @@ -830,7 +833,7 @@ void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { 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 changed size!"); + 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; @@ -841,6 +844,8 @@ void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { 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++) { @@ -863,7 +868,7 @@ void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { } offset += sizeof(float) * padding; - gr_update_buffer_data(Shadow_cascade_params_buffer, required_size, buffer.data()); + 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); } diff --git a/code/graphics/vulkan/vulkan_stubs.cpp b/code/graphics/vulkan/vulkan_stubs.cpp index 3b445c48109..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*/, bool /*render_cockpit_cascades*/, bool /*render_scene_cascades*/, bool /*first_pass*/) {} +void stub_shadow_map_start(matrix4* /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/, bool /*first_pass*/) {} void stub_shadow_map_end() {} diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index 5179bc88c07..79d139b6b2e 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -1730,27 +1730,8 @@ void mod_table_init() mprintf(( "Game Settings Table: Disabling in-game options system because the commandline override was detected!.\n")); } -} - -// game_settings.tbl is parsed before graphics are actually initialized, so we can't calculate the resolution at that time -void mod_table_post_process() -{ - // use the same widescreen code as in adjust_base_res() - // This calculates an adjusted resolution if the aspect ratio of the base resolution doesn't exactly match that of the current resolution. - // The base resolution specified in game_settings.tbl does not need to be 1024x768 or even 4:3. - float aspect_quotient_subtitle = ((float)gr_screen.center_w / (float)gr_screen.center_h) / ((float)Show_subtitle_screen_base_res[0] / (float)Show_subtitle_screen_base_res[1]); - if (aspect_quotient_subtitle >= 1.0) { - Show_subtitle_screen_adjusted_res[0] = (int)(Show_subtitle_screen_base_res[0] * aspect_quotient_subtitle); - Show_subtitle_screen_adjusted_res[1] = Show_subtitle_screen_base_res[1]; - } else { - Show_subtitle_screen_adjusted_res[0] = Show_subtitle_screen_base_res[0]; - Show_subtitle_screen_adjusted_res[1] = (int)(Show_subtitle_screen_base_res[1] / aspect_quotient_subtitle); - } - mprintf(("Game Settings Table: Show-subtitle adjusted resolution is (%d, %d)\n", Show_subtitle_screen_adjusted_res[0], Show_subtitle_screen_adjusted_res[1])); - - // we don't need to calculate adjusted resolution for hud-set-coords because that function doesn't do screen scaling - //Validate and process shadow settings + //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())) { @@ -1777,6 +1758,25 @@ void mod_table_post_process() } } +// game_settings.tbl is parsed before graphics are actually initialized, so we can't calculate the resolution at that time +void mod_table_post_process() +{ + // use the same widescreen code as in adjust_base_res() + // This calculates an adjusted resolution if the aspect ratio of the base resolution doesn't exactly match that of the current resolution. + // The base resolution specified in game_settings.tbl does not need to be 1024x768 or even 4:3. + float aspect_quotient_subtitle = ((float)gr_screen.center_w / (float)gr_screen.center_h) / ((float)Show_subtitle_screen_base_res[0] / (float)Show_subtitle_screen_base_res[1]); + if (aspect_quotient_subtitle >= 1.0) { + Show_subtitle_screen_adjusted_res[0] = (int)(Show_subtitle_screen_base_res[0] * aspect_quotient_subtitle); + Show_subtitle_screen_adjusted_res[1] = Show_subtitle_screen_base_res[1]; + } else { + Show_subtitle_screen_adjusted_res[0] = Show_subtitle_screen_base_res[0]; + Show_subtitle_screen_adjusted_res[1] = (int)(Show_subtitle_screen_base_res[1] / aspect_quotient_subtitle); + } + mprintf(("Game Settings Table: Show-subtitle adjusted resolution is (%d, %d)\n", Show_subtitle_screen_adjusted_res[0], Show_subtitle_screen_adjusted_res[1])); + + // we don't need to calculate adjusted resolution for hud-set-coords because that function doesn't do screen scaling +} + bool mod_supports_version(int major, int minor, int build) { return Targeted_version >= gameversion::version(major, minor, build, 0); From 3245bf5eca986990df4079e9d19033744374de47 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 22:43:40 +0900 Subject: [PATCH 27/31] Fix Warnings --- code/graphics/opengl/gropengltnl.cpp | 2 -- code/graphics/shadows.cpp | 2 +- code/graphics/shadows.h | 1 + code/mod_table/mod_table.cpp | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 11edeb8ee6f..30ce03e932c 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -623,8 +623,6 @@ void gr_opengl_render_model(model_material* material_info, indexed_vertex_source GL_CHECK_FOR_ERRORS("end of render_buffer()"); } -int Shadow_cascade_count = 0; - 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) { diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 08a22d5c4b8..bbec5ad1b92 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -824,7 +824,7 @@ void shadow_cascade_params_shutdown() { } } -extern int Shadow_cascade_count; +int Shadow_cascade_count = 0; void shadow_cascade_params_bind(int cascade_offset, int cascade_count) { if (!Shadow_cascade_params_buffer.isValid()) { return; diff --git a/code/graphics/shadows.h b/code/graphics/shadows.h index 1702e2ebdef..ccf754180e1 100644 --- a/code/graphics/shadows.h +++ b/code/graphics/shadows.h @@ -37,6 +37,7 @@ extern bool Shadow_quality_uses_mod_option; 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); diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index 79d139b6b2e..efef07e63f6 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -1736,7 +1736,7 @@ void mod_table_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 = Shadow_smoothness_factor.size(); + 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; From 58dd80dd3d0456fdab2369b345b300ca556ee9ca Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Tue, 16 Jun 2026 23:07:28 +0900 Subject: [PATCH 28/31] clang tidy --- code/graphics/opengl/gropengltnl.cpp | 2 +- code/graphics/shadows.cpp | 6 +++--- code/model/modelrender.cpp | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index 30ce03e932c..daf59cee90a 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -660,7 +660,7 @@ void gr_opengl_render_shadow_draw(gr_buffer_handle ubo_handle, size_t ubo_offset auto ibuffer = reinterpret_cast(vert_src->Index_offset); GLenum element_type = (datap->flags & VB_FLAG_LARGE_INDEX) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; - GLint base_vertex = (GLint)(vert_src->Base_vertex_offset + buffer->vertex_num_offset); + 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) { diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index bbec5ad1b92..b129970675b 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -85,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; @@ -656,8 +656,8 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, continue; bool cull = true; - for ( size_t j = 0; j < Shadow_frustums.size(); ++j ) { - if ( shadows_obj_in_frustum(objp, &light_matrix, &Shadow_frustums[j].min, &Shadow_frustums[j].max) ) { + for (const auto& Shadow_frustum : Shadow_frustums) { + if ( shadows_obj_in_frustum(objp, &light_matrix, &Shadow_frustum.min, &Shadow_frustum.max) ) { cull = false; break; } diff --git a/code/model/modelrender.cpp b/code/model/modelrender.cpp index 7d4774c372e..f1ef2b8d643 100644 --- a/code/model/modelrender.cpp +++ b/code/model/modelrender.cpp @@ -627,9 +627,7 @@ void model_draw_list::render_all(gr_zbuffer_type depth_mode) _lights.resetLightState(); - for ( size_t i = 0; i < _keys.size(); ++i ) { - int render_index = _keys[i]; - + 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]); } From b0b7243f5bd887927104a4499246427815ab71c7 Mon Sep 17 00:00:00 2001 From: BMagnu <6238428+BMagnu@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:32:13 +0900 Subject: [PATCH 29/31] Fix missing fallback for shader precaching --- code/def_files/data/effects/shadow_map-v.sdr | 2 +- code/model/modelread.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index e445e13830c..b7c06137e4a 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -2,7 +2,7 @@ #extension GL_ARB_gpu_shader5: enable #ifndef GEOMETRY_FALLBACK -#extension GL_ARB_shader_viewport_layer_array : require +#extension GL_ARB_shader_viewport_layer_array : enable #endif in vec4 vertPosition; diff --git a/code/model/modelread.cpp b/code/model/modelread.cpp index 7c979c8c26f..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_SHADOW_MAP_GEN, 0); + 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); From 6441910c7c4cd4cc41c8957c6018ff2deda047f1 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:42:50 +0900 Subject: [PATCH 30/31] That weird FOV multiplier that always ruins your day --- code/graphics/shadows.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index b129970675b..aba8424a337 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -644,6 +644,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos, 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); From 00abd57a1c548bda6ffae080142fb1d42afd9e76 Mon Sep 17 00:00:00 2001 From: Birk Magnussen <6238428+BMagnu@users.noreply.github.com> Date: Wed, 17 Jun 2026 22:56:11 +0900 Subject: [PATCH 31/31] Avoid shadow geometry artifacts by using hardware depth clamping --- code/def_files/data/effects/shadow_map-g.sdr | 3 --- code/def_files/data/effects/shadow_map-v.sdr | 2 -- code/graphics/opengl/gropengltnl.cpp | 4 ++++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/code/def_files/data/effects/shadow_map-g.sdr b/code/def_files/data/effects/shadow_map-g.sdr index d7c8bf8a998..0de37ea486c 100644 --- a/code/def_files/data/effects/shadow_map-g.sdr +++ b/code/def_files/data/effects/shadow_map-g.sdr @@ -42,9 +42,6 @@ void main(void) gl_Position = shadow_proj_matrix[cascade_id] * gl_in[vert].gl_Position; } - if(gl_Position.z < -1.0) - gl_Position.z = -1.0; - gl_Layer = cascade_id; gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0]; diff --git a/code/def_files/data/effects/shadow_map-v.sdr b/code/def_files/data/effects/shadow_map-v.sdr index b7c06137e4a..3a804f0046d 100644 --- a/code/def_files/data/effects/shadow_map-v.sdr +++ b/code/def_files/data/effects/shadow_map-v.sdr @@ -82,8 +82,6 @@ void main() #ifndef GEOMETRY_FALLBACK int cascade_id = cascade_offset + gl_InstanceID; position = shadow_proj_matrix[cascade_id] * position; - if(position.z < -1.0) - position.z = -1.0; #endif gl_Position = position; } diff --git a/code/graphics/opengl/gropengltnl.cpp b/code/graphics/opengl/gropengltnl.cpp index daf59cee90a..d412894ecb6 100644 --- a/code/graphics/opengl/gropengltnl.cpp +++ b/code/graphics/opengl/gropengltnl.cpp @@ -731,6 +731,8 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light glClear(GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_CLAMP); + Glowpoint_override_save = Glowpoint_override; Glowpoint_override = true; @@ -750,6 +752,8 @@ void gr_opengl_shadow_map_end() { gr_end_view_matrix(); + glDisable(GL_DEPTH_CLAMP); + gr_zbuffer_set(ZBUFFER_TYPE_FULL); GL_state.PopFramebufferState();