From 7b90c82428eb9e74fc6bda73195d2fa786c86166 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 22 Oct 2024 16:20:47 +0300 Subject: [PATCH 01/79] Add command to set/inspect custom fog. --- src/client/client.h | 2 ++ src/client/view.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/client/client.h b/src/client/client.h index dcb85d518..ef9c18474 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -312,6 +312,8 @@ typedef struct { unsigned hit_marker_time; int hit_marker_count; + + player_fog_t custom_fog; } client_state_t; extern client_state_t cl; diff --git a/src/client/view.c b/src/client/view.c index b4d8436e3..53d6be2d4 100644 --- a/src/client/view.c +++ b/src/client/view.c @@ -421,6 +421,10 @@ void V_RenderView(void) Vector4Clear(cl.refdef.screen_blend); Vector4Clear(cl.refdef.damage_blend); } + if (cl.custom_fog.density) { + cl.refdef.fog = cl.custom_fog; + cl.refdef.heightfog = (player_heightfog_t){ 0 }; + } cl.refdef.num_entities = r_numentities; cl.refdef.entities = r_entities; @@ -456,11 +460,78 @@ static void V_Viewpos_f(void) Com_Printf("%s : %.f\n", vtos(cl.refdef.vieworg), cl.refdef.viewangles[YAW]); } +/* +============= +V_Fog_f +============= +*/ +static void dump_fog(const player_fog_t *fog) +{ + Com_Printf("(%.3f %.3f %.3f) %f %f\n", + fog->color[0], fog->color[1], fog->color[2], + fog->density, fog->sky_factor); +} + +static void dump_heightfog(const player_heightfog_t *fog) +{ + Com_Printf("Start : (%.3f %.3f %.3f) %.f\n", + fog->start.color[0], fog->start.color[1], fog->start.color[2], fog->start.dist); + Com_Printf("End : (%.3f %.3f %.3f) %.f\n", + fog->end.color[0], fog->end.color[1], fog->end.color[2], fog->end.dist); + Com_Printf("Density: %f\n", fog->density); + Com_Printf("Falloff: %f\n", fog->falloff); +} + +static void V_Fog_f(void) +{ + int argc = Cmd_Argc(); + float args[5]; + + if (argc == 1) { + if (cl.custom_fog.density) { + Com_Printf("User set global fog:\n"); + dump_fog(&cl.custom_fog); + return; + } + if (!cl.frame.ps.fog.density && !cl.frame.ps.heightfog.density) { + Com_Printf("No fog.\n"); + return; + } + if (cl.frame.ps.fog.density) { + Com_Printf("Global fog:\n"); + dump_fog(&cl.frame.ps.fog); + } + if (cl.frame.ps.heightfog.density) { + Com_Printf("Height fog:\n"); + dump_heightfog(&cl.frame.ps.heightfog); + } + return; + } + + if (argc < 5) { + Com_Printf("Usage: %s [sky_factor]\n", Cmd_Argv(0)); + return; + } + + for (int i = 0; i < 5; i++) + args[i] = Q_clipf(Q_atof(Cmd_Argv(i + 1)), 0, 1); + + cl.custom_fog.color[0] = args[0]; + cl.custom_fog.color[1] = args[1]; + cl.custom_fog.color[2] = args[2]; + cl.custom_fog.density = args[3]; + cl.custom_fog.sky_factor = args[4]; + + cl.refdef.fog = cl.custom_fog; + cl.refdef.heightfog = (player_heightfog_t){ 0 }; +} + static const cmdreg_t v_cmds[] = { { "gun_next", V_Gun_Next_f }, { "gun_prev", V_Gun_Prev_f }, { "gun_model", V_Gun_Model_f }, { "viewpos", V_Viewpos_f }, + { "fog", V_Fog_f }, { NULL } }; From 2dbf94e86317ffc6267eb2443c6ceed0b3a1b1f9 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 22 Oct 2024 16:21:08 +0300 Subject: [PATCH 02/79] Don't add heatbeam particles if paused. --- src/client/tent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/tent.c b/src/client/tent.c index 0724d7035..17128a6db 100644 --- a/src/client/tent.c +++ b/src/client/tent.c @@ -914,7 +914,7 @@ static void CL_AddPlayerBeams(void) vectoangles2(dist, angles); // if it's the heatbeam, draw the particle effect - if (cl_mod_heatbeam && b->model == cl_mod_heatbeam) + if (cl_mod_heatbeam && b->model == cl_mod_heatbeam && !sv_paused->integer) CL_Heatbeam(org, dist); framenum = 1; From 9e99f370a5b264b33c933d3f104192ce2f9252e0 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 23 Oct 2024 00:59:22 +0300 Subject: [PATCH 03/79] Interpolate BFG lightramp in extended mode. --- src/client/entities.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/client/entities.c b/src/client/entities.c index e399bf346..1ff83517b 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -978,11 +978,14 @@ static void CL_AddPacketEntities(void) } else if (effects & EF_FLIES) { CL_FlyEffect(cent, ent.origin); } else if (effects & EF_BFG) { + static const uint16_t bfg_lightramp[6] = {300, 400, 600, 300, 150, 75}; if (effects & EF_ANIM_ALLFAST) { CL_BfgParticles(&ent); i = 200; + } else if (cl.csr.extended) { + i = bfg_lightramp[Q_clip(ent.oldframe, 0, 5)] * ent.backlerp + + bfg_lightramp[Q_clip(ent.frame, 0, 5)] * cl.lerpfrac; } else { - static const uint16_t bfg_lightramp[6] = {300, 400, 600, 300, 150, 75}; i = bfg_lightramp[Q_clip(s1->frame, 0, 5)]; } V_AddLight(ent.origin, i, 0, 1, 0); From c0d5845a72f8c2089c70f4d9a73d76b96a3cc73c Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 23 Oct 2024 01:01:31 +0300 Subject: [PATCH 04/79] Improve RandomDir() uniformity. --- src/client/newfx.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/client/newfx.c b/src/client/newfx.c index 70650217d..0ec470d62 100644 --- a/src/client/newfx.c +++ b/src/client/newfx.c @@ -338,12 +338,21 @@ void CL_TrackerTrail(centity_t *ent, const vec3_t end) VectorCopy(move, ent->lerp_origin); } +// Marsaglia 1972 rejection method static void RandomDir(vec3_t dir) { - dir[0] = crand(); - dir[1] = crand(); - dir[2] = crand(); - VectorNormalize(dir); + float x, y, s, a; + + do { + x = crand(); + y = crand(); + s = x * x + y * y; + } while (s > 1); + + a = 2 * sqrtf(1 - s); + dir[0] = x * a; + dir[1] = y * a; + dir[2] = -1 + 2 * s; } void CL_Tracker_Shell(const centity_t *ent, const vec3_t origin) From 4712833bf644fdd795e5e27d462d8cd86a076ebf Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 23 Oct 2024 01:07:34 +0300 Subject: [PATCH 05/79] Improve tracker shell effect. Make entity black. Add more particles and make particles larger depending on radius. --- inc/refresh/refresh.h | 7 +++++-- src/client/client.h | 4 ++++ src/client/effects.c | 2 ++ src/client/entities.c | 9 +++++++++ src/client/newfx.c | 11 ++++++++--- src/refresh/mesh.c | 8 +++++--- src/refresh/tess.c | 2 +- 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/inc/refresh/refresh.h b/inc/refresh/refresh.h index 87980fae5..58a69e6b7 100644 --- a/inc/refresh/refresh.h +++ b/inc/refresh/refresh.h @@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define POWERSUIT_SCALE 4.0f #define WEAPONSHELL_SCALE 0.5f +#define RF_TRACKER BIT_ULL(32) + #define RF_SHELL_MASK (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | \ RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM | RF_SHELL_LITE_GREEN) @@ -60,9 +62,9 @@ typedef struct entity_s { float alpha; // ignore if RF_TRANSLUCENT isn't set color_t rgba; - qhandle_t skin; // NULL for inline skin - int flags; + uint64_t flags; + qhandle_t skin; // NULL for inline skin float scale; } entity_t; @@ -76,6 +78,7 @@ typedef struct { typedef struct { vec3_t origin; int color; // -1 => use rgba + float scale; float alpha; color_t rgba; } particle_t; diff --git a/src/client/client.h b/src/client/client.h index ef9c18474..85962feed 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -745,6 +745,9 @@ bool CL_SeekDemoMessage(void); EF_TRACKERTRAIL | EF_TRACKER | EF_GREENGIB | EF_IONRIPPER | \ EF_BLUEHYPERBLASTER | EF_PLASMA) +#define IS_TRACKER(effects) \ + (((effects) & (EF_TRACKERTRAIL | EF_TRACKER)) == EF_TRACKERTRAIL) + void CL_DeltaFrame(void); void CL_AddEntities(void); void CL_CalcViewValues(void); @@ -846,6 +849,7 @@ typedef struct cparticle_s { vec3_t vel; vec3_t accel; int color; // -1 => use rgba + float scale; float alpha; float alphavel; color_t rgba; diff --git a/src/client/effects.c b/src/client/effects.c index 06a2a3e45..39e22f260 100644 --- a/src/client/effects.c +++ b/src/client/effects.c @@ -897,6 +897,7 @@ cparticle_t *CL_AllocParticle(void) p->next = active_particles; active_particles = p; + p->scale = 1.0f; return p; } @@ -1758,6 +1759,7 @@ void CL_AddParticles(void) part->rgba = p->rgba; part->color = p->color; part->alpha = min(alpha, 1.0f); + part->scale = p->scale; if (p->alphavel == INSTANT_PARTICLE) { p->alphavel = 0.0f; diff --git a/src/client/entities.c b/src/client/entities.c index 1ff83517b..376dc3ebd 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -812,6 +812,9 @@ static void CL_AddPacketEntities(void) ent.scale = s1->scale; + if (IS_TRACKER(effects)) + ent.flags |= RF_TRACKER; + // add to refresh list V_AddEntity(&ent); @@ -862,6 +865,9 @@ static void CL_AddPacketEntities(void) ent.alpha = s1->alpha; } + if (IS_TRACKER(effects)) + ent.flags |= RF_TRACKER; + // duplicate for linked models if (s1->modelindex2) { if (s1->modelindex2 == MODELINDEX_PLAYER) { @@ -899,6 +905,9 @@ static void CL_AddPacketEntities(void) ent.alpha = s1->alpha; } + if (IS_TRACKER(effects)) + ent.flags |= RF_TRACKER; + if (s1->modelindex3) { ent.model = cl.model_draw[s1->modelindex3]; V_AddEntity(&ent); diff --git a/src/client/newfx.c b/src/client/newfx.c index 0ec470d62..d8eade9b7 100644 --- a/src/client/newfx.c +++ b/src/client/newfx.c @@ -358,20 +358,24 @@ static void RandomDir(vec3_t dir) void CL_Tracker_Shell(const centity_t *ent, const vec3_t origin) { vec3_t org, dir, mid; - int i; + int i, count; cparticle_t *p; - float radius; + float radius, scale; if (cl.csr.extended) { VectorAvg(ent->mins, ent->maxs, mid); VectorAdd(origin, mid, org); radius = ent->radius; + scale = Q_clipf(ent->radius / 40.0f, 1, 2); + count = 300 * scale; } else { VectorCopy(origin, org); radius = 40.0f; + scale = 1.0f; + count = 300; } - for (i = 0; i < 300; i++) { + for (i = 0; i < count; i++) { p = CL_AllocParticle(); if (!p) return; @@ -382,6 +386,7 @@ void CL_Tracker_Shell(const centity_t *ent, const vec3_t origin) p->alpha = 1.0f; p->alphavel = INSTANT_PARTICLE; p->color = 0; + p->scale = scale; RandomDir(dir); VectorMA(org, radius, dir, p->org); diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index 29fb16bab..263a75d4c 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -60,7 +60,7 @@ static void setup_dotshading(void) if (!gl_dotshading->integer) return; - if (glr.ent->flags & RF_SHELL_MASK) + if (glr.ent->flags & (RF_SHELL_MASK | RF_TRACKER)) return; if (drawshadow == SHADOW_ONLY) @@ -356,7 +356,7 @@ static void setup_frame_scale(const model_t *model) static void setup_color(void) { - int flags = glr.ent->flags; + uint64_t flags = glr.ent->flags; float f, m; int i; @@ -382,6 +382,8 @@ static void setup_color(void) VectorSet(color, 1, 1, 1); } else if ((flags & RF_IR_VISIBLE) && (glr.fd.rdflags & RDF_IRGOGGLES)) { VectorSet(color, 1, 0, 0); + } else if (flags & RF_TRACKER) { + VectorClear(color); } else { GL_LightPoint(origin, color); @@ -418,7 +420,7 @@ static void setup_celshading(void) { float value = Cvar_ClampValue(gl_celshading, 0, 10); - if (value == 0 || (glr.ent->flags & (RF_TRANSLUCENT | RF_SHELL_MASK)) || !qglPolygonMode || !qglLineWidth) + if (value == 0 || (glr.ent->flags & (RF_TRANSLUCENT | RF_SHELL_MASK | RF_TRACKER)) || !qglPolygonMode || !qglLineWidth) celscale = 0; else celscale = 1.0f - Distance(origin, glr.fd.vieworg) / 700.0f; diff --git a/src/refresh/tess.c b/src/refresh/tess.c index 8fe3f08d2..def297b0b 100644 --- a/src/refresh/tess.c +++ b/src/refresh/tess.c @@ -97,7 +97,7 @@ void GL_DrawParticles(void) scale = 1.0f; if (dist > 20) scale += dist * 0.004f; - scale *= gl_partscale->value; + scale *= gl_partscale->value * p->scale; scale2 = scale * PARTICLE_SCALE; VectorMA(p->origin, scale2, glr.viewaxis[1], dst_vert); From 700de333c7616d572156678bec0c781677189cf0 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 24 Oct 2024 19:41:52 +0300 Subject: [PATCH 06/79] Avoid NaNs in heightfog shader. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes small ‘dead pixel’ artifacts visible in dark areas. --- src/refresh/shader.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 1e0ae1b26..b3ebe6a17 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -350,6 +350,8 @@ static void write_height_fog(sizebuf_t *buf) { GLSL({ float dir_z = normalize(v_world_pos - u_vieworg).z; + float s = sign(dir_z); + dir_z += 0.00001 * (1.0 - s * s); float eye = u_vieworg.z - u_heightfog_start.w; float pos = v_world_pos.z - u_heightfog_start.w; float density = (exp(-u_heightfog_falloff * eye) - From 4b4bb948efaac24ffb52b2f4679d4a3f9b74e2af Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 25 Oct 2024 02:07:18 +0300 Subject: [PATCH 07/79] Reduce indentation. --- src/refresh/shader.c | 146 +++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index b3ebe6a17..782a1101a 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -322,26 +322,26 @@ static void write_vertex_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(out vec3 v_world_pos;) GLSF("void main() {\n"); - if (bits & GLS_CLASSIC_SKY) { - GLSL(v_dir = (m_sky[1] * a_pos).xyz;) - } else if (bits & GLS_DEFAULT_SKY) { - GLSL(v_dir = (m_sky[0] * a_pos).xyz;) - } else if (bits & GLS_SCROLL_ENABLE) { - GLSL(v_tc = a_tc + u_scroll;) - } else { - GLSL(v_tc = a_tc;) - } + if (bits & GLS_CLASSIC_SKY) { + GLSL(v_dir = (m_sky[1] * a_pos).xyz;) + } else if (bits & GLS_DEFAULT_SKY) { + GLSL(v_dir = (m_sky[0] * a_pos).xyz;) + } else if (bits & GLS_SCROLL_ENABLE) { + GLSL(v_tc = a_tc + u_scroll;) + } else { + GLSL(v_tc = a_tc;) + } - if (bits & GLS_LIGHTMAP_ENABLE) - GLSL(v_lmtc = a_lmtc;) + if (bits & GLS_LIGHTMAP_ENABLE) + GLSL(v_lmtc = a_lmtc;) - if (!(bits & GLS_TEXTURE_REPLACE)) - GLSL(v_color = a_color;) + if (!(bits & GLS_TEXTURE_REPLACE)) + GLSL(v_color = a_color;) - if (bits & GLS_FOG_HEIGHT) - GLSL(v_world_pos = (m_model * a_pos).xyz;) + if (bits & GLS_FOG_HEIGHT) + GLSL(v_world_pos = (m_model * a_pos).xyz;) - GLSL(gl_Position = m_vp * a_pos;) + GLSL(gl_Position = m_vp * a_pos;) GLSF("}\n"); } @@ -407,78 +407,78 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(in vec3 v_world_pos;) GLSF("void main() {\n"); - if (bits & GLS_CLASSIC_SKY) { - GLSL( - float len = length(v_dir); - vec2 dir = v_dir.xy * (3.0 / len); - vec2 tc1 = dir + vec2(u_time * 0.0625); - vec2 tc2 = dir + vec2(u_time * 0.1250); - vec4 solid = texture(u_texture1, tc1); - vec4 alpha = texture(u_texture2, tc2); - vec4 diffuse = vec4((solid.rgb - alpha.rgb * 0.25) * 0.65, 1.0); - ) - } else if (bits & GLS_DEFAULT_SKY) { - GLSL(vec4 diffuse = texture(u_texture, v_dir);) - } else { - GLSL(vec2 tc = v_tc;) + if (bits & GLS_CLASSIC_SKY) { + GLSL( + float len = length(v_dir); + vec2 dir = v_dir.xy * (3.0 / len); + vec2 tc1 = dir + vec2(u_time * 0.0625); + vec2 tc2 = dir + vec2(u_time * 0.1250); + vec4 solid = texture(u_texture1, tc1); + vec4 alpha = texture(u_texture2, tc2); + vec4 diffuse = vec4((solid.rgb - alpha.rgb * 0.25) * 0.65, 1.0); + ) + } else if (bits & GLS_DEFAULT_SKY) { + GLSL(vec4 diffuse = texture(u_texture, v_dir);) + } else { + GLSL(vec2 tc = v_tc;) - if (bits & GLS_WARP_ENABLE) - GLSL(tc += w_amp * sin(tc.ts * w_phase + u_time);) + if (bits & GLS_WARP_ENABLE) + GLSL(tc += w_amp * sin(tc.ts * w_phase + u_time);) - GLSL(vec4 diffuse = texture(u_texture, tc);) - } - - if (bits & GLS_ALPHATEST_ENABLE) - GLSL(if (diffuse.a <= 0.666) discard;) + GLSL(vec4 diffuse = texture(u_texture, tc);) + } - if (bits & GLS_LIGHTMAP_ENABLE) { - GLSL(vec4 lightmap = texture(u_lightmap, v_lmtc);) + if (bits & GLS_ALPHATEST_ENABLE) + GLSL(if (diffuse.a <= 0.666) discard;) - if (bits & GLS_GLOWMAP_ENABLE) { - GLSL(vec4 glowmap = texture(u_glowmap, tc);) - GLSL(lightmap.rgb = mix(lightmap.rgb, vec3(1.0), glowmap.a);) - } + if (bits & GLS_LIGHTMAP_ENABLE) { + GLSL(vec4 lightmap = texture(u_lightmap, v_lmtc);) - GLSL(diffuse.rgb *= (lightmap.rgb + u_add) * u_modulate;) + if (bits & GLS_GLOWMAP_ENABLE) { + GLSL(vec4 glowmap = texture(u_glowmap, tc);) + GLSL(lightmap.rgb = mix(lightmap.rgb, vec3(1.0), glowmap.a);) } - if (bits & GLS_INTENSITY_ENABLE) - GLSL(diffuse.rgb *= u_intensity;) + GLSL(diffuse.rgb *= (lightmap.rgb + u_add) * u_modulate;) + } - if (bits & GLS_DEFAULT_FLARE) - GLSL( - diffuse.rgb *= (diffuse.r + diffuse.g + diffuse.b) / 3.0; - diffuse.rgb *= v_color.a; - ) + if (bits & GLS_INTENSITY_ENABLE) + GLSL(diffuse.rgb *= u_intensity;) - if (!(bits & GLS_TEXTURE_REPLACE)) - GLSL(diffuse *= v_color;) + if (bits & GLS_DEFAULT_FLARE) + GLSL( + diffuse.rgb *= (diffuse.r + diffuse.g + diffuse.b) / 3.0; + diffuse.rgb *= v_color.a; + ) - if (!(bits & GLS_LIGHTMAP_ENABLE) && (bits & GLS_GLOWMAP_ENABLE)) { - GLSL(vec4 glowmap = texture(u_glowmap, tc);) - if (bits & GLS_INTENSITY_ENABLE) - GLSL(diffuse.rgb += glowmap.rgb * u_intensity2;) - else - GLSL(diffuse.rgb += glowmap.rgb;) - } + if (!(bits & GLS_TEXTURE_REPLACE)) + GLSL(diffuse *= v_color;) - if (bits & (GLS_FOG_GLOBAL | GLS_FOG_HEIGHT)) - GLSL(float frag_depth = gl_FragCoord.z / gl_FragCoord.w;) + if (!(bits & GLS_LIGHTMAP_ENABLE) && (bits & GLS_GLOWMAP_ENABLE)) { + GLSL(vec4 glowmap = texture(u_glowmap, tc);) + if (bits & GLS_INTENSITY_ENABLE) + GLSL(diffuse.rgb += glowmap.rgb * u_intensity2;) + else + GLSL(diffuse.rgb += glowmap.rgb;) + } - if (bits & GLS_FOG_GLOBAL) - GLSL({ - float d = u_fog_color.a * frag_depth; - float fog = 1.0f - exp(-(d * d)); - diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, fog); - }) + if (bits & (GLS_FOG_GLOBAL | GLS_FOG_HEIGHT)) + GLSL(float frag_depth = gl_FragCoord.z / gl_FragCoord.w;) - if (bits & GLS_FOG_HEIGHT) - write_height_fog(buf); + if (bits & GLS_FOG_GLOBAL) + GLSL({ + float d = u_fog_color.a * frag_depth; + float fog = 1.0f - exp(-(d * d)); + diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, fog); + }) + + if (bits & GLS_FOG_HEIGHT) + write_height_fog(buf); - if (bits & GLS_FOG_SKY) - GLSL(diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, u_fog_sky_factor);) + if (bits & GLS_FOG_SKY) + GLSL(diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, u_fog_sky_factor);) - GLSL(o_color = diffuse;) + GLSL(o_color = diffuse;) GLSF("}\n"); } From e4c84f178e7359900f00af59f17ea94bddf202c7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 25 Oct 2024 22:32:58 +0300 Subject: [PATCH 08/79] Add re-release bloom effect support. Based on code from: Jonathan Suggestions by: @res2k Closes #362. --- doc/client.asciidoc | 12 +++ src/refresh/gl.h | 62 +++++++---- src/refresh/main.c | 114 ++++++++++++++++----- src/refresh/mesh.c | 6 ++ src/refresh/qgl.c | 10 ++ src/refresh/qgl.h | 4 + src/refresh/shader.c | 232 ++++++++++++++++++++++++++++++++++++++++-- src/refresh/state.c | 4 +- src/refresh/tess.c | 10 +- src/refresh/texture.c | 107 +++++++++++-------- 10 files changed, 463 insertions(+), 98 deletions(-) diff --git a/doc/client.asciidoc b/doc/client.asciidoc index 94e37f7c8..03a38437a 100644 --- a/doc/client.asciidoc +++ b/doc/client.asciidoc @@ -691,6 +691,18 @@ gl_fog:: Enable re-release fog effect. Only effective when using GLSL backend. Default value is 1 (enabled). +gl_bloom:: + Enable re-release bloom effect. Only effective when using GLSL backend. + Default value is 0 (disabled). Higher values specify number of blurring + filter iterations (typical value is 1). + +gl_bloom_sigma:: + Specifies strength parameter of gaussian blur filter used for the bloom + effect. Default value is 4. + +NOTE: Bloom only works with glowmaps. Enabling it with non-remastered content +(or glowmaps disabled) is a waste of resources. + gl_flarespeed:: Specifies flare fading effect speed. Default value is 8. Set this to 0 to disable fading. diff --git a/src/refresh/gl.h b/src/refresh/gl.h index 6753bde65..863106269 100644 --- a/src/refresh/gl.h +++ b/src/refresh/gl.h @@ -53,7 +53,29 @@ typedef GLuint glIndex_t; #define TAB_SIN(x) gl_static.sintab[(x) & 255] #define TAB_COS(x) gl_static.sintab[((x) + 64) & 255] -#define NUM_AUTO_TEXTURES 9 +// auto textures +#define NUM_AUTO_TEXTURES 13 +#define AUTO_TEX(n) gl_static.texnums[n] + +#define TEXNUM_DEFAULT AUTO_TEX(0) +#define TEXNUM_SCRAP AUTO_TEX(1) +#define TEXNUM_PARTICLE AUTO_TEX(2) +#define TEXNUM_BEAM AUTO_TEX(3) +#define TEXNUM_WHITE AUTO_TEX(4) +#define TEXNUM_BLACK AUTO_TEX(5) +#define TEXNUM_RAW AUTO_TEX(6) +#define TEXNUM_CUBEMAP_DEFAULT AUTO_TEX(7) +#define TEXNUM_CUBEMAP_BLACK AUTO_TEX(8) +#define TEXNUM_PP_SCENE AUTO_TEX(9) +#define TEXNUM_PP_BLOOM AUTO_TEX(10) +#define TEXNUM_PP_BLUR_0 AUTO_TEX(11) +#define TEXNUM_PP_BLUR_1 AUTO_TEX(12) + +// framebuffers +#define FBO_COUNT 3 +#define FBO_SCENE gl_static.framebuffers[0] +#define FBO_BLUR_0 gl_static.framebuffers[1] +#define FBO_BLUR_1 gl_static.framebuffers[2] typedef struct { GLuint query; @@ -76,9 +98,8 @@ typedef struct { size_t buffer_size; vec_t size; } world; - GLuint warp_texture; - GLuint warp_renderbuffer; - GLuint warp_framebuffer; + GLuint renderbuffer; + GLuint framebuffers[FBO_COUNT]; GLuint uniform_buffer; #if USE_MD5 GLuint skeleton_buffer; @@ -92,6 +113,7 @@ typedef struct { GLenum samples_passed; GLbitfield stencil_buffer_bit; float entity_modulate; + float bloom_sigma; uint32_t inverse_intensity_33; uint32_t inverse_intensity_66; uint32_t inverse_intensity_100; @@ -131,6 +153,7 @@ typedef struct { int framebuffer_width; int framebuffer_height; bool framebuffer_ok; + bool framebuffer_bound; } glRefdef_t; typedef enum { @@ -226,6 +249,7 @@ extern cvar_t *gl_md5_use; extern cvar_t *gl_md5_distance; #endif extern cvar_t *gl_damageblend_frac; +extern cvar_t *gl_bloom; // development variables extern cvar_t *gl_znear; @@ -512,6 +536,13 @@ typedef enum { GLS_FOG_HEIGHT = BIT(27), GLS_FOG_SKY = BIT(28), + GLS_BLOOM_GENERATE = BIT(29), + GLS_BLOOM_BLUR = BIT(30), + GLS_BLOOM_SHELL = BIT(31), + GLS_BLOOM_OUTPUT = GLS_BLOOM_GENERATE | GLS_BLOOM_BLUR, + GLS_BLOOM_DOWNSCALE = GLS_BLOOM_BLUR | GLS_BLOOM_SHELL, + GLS_BLOOM_MASK = GLS_BLOOM_OUTPUT, + GLS_BLEND_MASK = GLS_BLEND_BLEND | GLS_BLEND_ADD | GLS_BLEND_MODULATE, GLS_COMMON_MASK = GLS_DEPTHMASK_FALSE | GLS_DEPTHTEST_DISABLE | GLS_CULL_DISABLE | GLS_BLEND_MASK, GLS_SKY_MASK = GLS_CLASSIC_SKY | GLS_DEFAULT_SKY, @@ -520,8 +551,9 @@ typedef enum { GLS_MESH_MASK = GLS_MESH_ANY | GLS_MESH_LERP | GLS_MESH_SHELL | GLS_MESH_SHADE, GLS_SHADER_MASK = GLS_ALPHATEST_ENABLE | GLS_TEXTURE_REPLACE | GLS_SCROLL_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_WARP_ENABLE | GLS_INTENSITY_ENABLE | GLS_GLOWMAP_ENABLE | - GLS_SKY_MASK | GLS_DEFAULT_FLARE | GLS_MESH_MASK | GLS_FOG_MASK, - GLS_UNIFORM_MASK = GLS_WARP_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_INTENSITY_ENABLE | GLS_SKY_MASK | GLS_FOG_MASK, + GLS_SKY_MASK | GLS_DEFAULT_FLARE | GLS_MESH_MASK | GLS_FOG_MASK | GLS_BLOOM_MASK | GLS_BLOOM_SHELL, + GLS_UNIFORM_MASK = GLS_WARP_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_INTENSITY_ENABLE | + GLS_SKY_MASK | GLS_FOG_MASK | GLS_BLOOM_MASK, GLS_SCROLL_MASK = GLS_SCROLL_ENABLE | GLS_SCROLL_X | GLS_SCROLL_Y | GLS_SCROLL_FLIP | GLS_SCROLL_SLOW, } glStateBits_t; @@ -559,7 +591,7 @@ typedef enum { VA_EFFECT, VA_NULLMODEL, VA_OCCLUDE, - VA_WATERWARP, + VA_POSTPROCESS, VA_MESH_SHADE, VA_MESH_FLAT, VA_2D, @@ -798,10 +830,11 @@ void GL_DrawOutlines(GLsizei count, GLenum type, const void *indices); void GL_Ortho(GLfloat xmin, GLfloat xmax, GLfloat ymin, GLfloat ymax, GLfloat znear, GLfloat zfar); void GL_Frustum(GLfloat fov_x, GLfloat fov_y, GLfloat reflect_x); void GL_Setup2D(void); -void GL_Setup3D(bool waterwarp); +void GL_Setup3D(void); void GL_ClearState(void); void GL_InitState(void); void GL_ShutdownState(void); +void GL_UpdateBlurParams(void); /* * gl_draw.c @@ -831,23 +864,12 @@ void GL_Blend(void); * */ -// auto textures -#define TEXNUM_DEFAULT gl_static.texnums[0] -#define TEXNUM_SCRAP gl_static.texnums[1] -#define TEXNUM_PARTICLE gl_static.texnums[2] -#define TEXNUM_BEAM gl_static.texnums[3] -#define TEXNUM_WHITE gl_static.texnums[4] -#define TEXNUM_BLACK gl_static.texnums[5] -#define TEXNUM_RAW gl_static.texnums[6] -#define TEXNUM_CUBEMAP_DEFAULT gl_static.texnums[7] -#define TEXNUM_CUBEMAP_BLACK gl_static.texnums[8] - void Scrap_Upload(void); void GL_InitImages(void); void GL_ShutdownImages(void); -bool GL_InitWarpTexture(void); +bool GL_InitFramebuffers(void); extern cvar_t *gl_intensity; diff --git a/src/refresh/main.c b/src/refresh/main.c index 84a3ace5d..fbbc7ca74 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -62,6 +62,7 @@ cvar_t *gl_md5_distance; cvar_t *gl_damageblend_frac; cvar_t *gl_waterwarp; cvar_t *gl_fog; +cvar_t *gl_bloom; cvar_t *gl_swapinterval; // development variables @@ -72,6 +73,7 @@ cvar_t *gl_drawsky; cvar_t *gl_showtris; cvar_t *gl_showorigins; cvar_t *gl_showtearing; +cvar_t *gl_showbloom; #if USE_DEBUG cvar_t *gl_showstats; cvar_t *gl_showscrap; @@ -623,33 +625,71 @@ bool GL_ShowErrors(const char *func) return true; } -static void GL_WaterWarp(void) +static void GL_PostProcess(glStateBits_t bits, int x, int y, int w, int h) { - float x0, x1, y0, y1; - - GL_ForceTexture(TMU_TEXTURE, gl_static.warp_texture); - GL_BindArrays(VA_WATERWARP); + GL_BindArrays(VA_POSTPROCESS); GL_StateBits(GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_FALSE | - GLS_CULL_DISABLE | GLS_TEXTURE_REPLACE | GLS_WARP_ENABLE); + GLS_CULL_DISABLE | GLS_TEXTURE_REPLACE | bits); GL_ArrayBits(GLA_VERTEX | GLA_TC); - GL_LoadUniforms(); - - x0 = glr.fd.x; - x1 = glr.fd.x + glr.fd.width; + gl_backend->load_uniforms(); - y0 = glr.fd.y; - y1 = glr.fd.y + glr.fd.height; - - Vector4Set(tess.vertices, x0, y0, 0, 1); - Vector4Set(tess.vertices + 4, x0, y1, 0, 0); - Vector4Set(tess.vertices + 8, x1, y0, 1, 1); - Vector4Set(tess.vertices + 12, x1, y1, 1, 0); + Vector4Set(tess.vertices, x, y, 0, 1); + Vector4Set(tess.vertices + 4, x, y + h, 0, 0); + Vector4Set(tess.vertices + 8, x + w, y, 1, 1); + Vector4Set(tess.vertices + 12, x + w, y + h, 1, 0); GL_LockArrays(4); qglDrawArrays(GL_TRIANGLE_STRIP, 0, 4); GL_UnlockArrays(); } +static void GL_DrawBloom(bool waterwarp) +{ + int iterations = Cvar_ClampInteger(gl_bloom, 1, 8) * 2; + int w = glr.fd.width / 4; + int h = glr.fd.height / 4; + + qglViewport(0, 0, w, h); + GL_Ortho(0, w, h, 0, -1, 1); + + // downscale + gls.u_block.fog_color[0] = 1.0f / w; + gls.u_block.fog_color[1] = 1.0f / h; + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_BLOOM); + qglBindFramebuffer(GL_FRAMEBUFFER, FBO_BLUR_0); + GL_PostProcess(GLS_BLOOM_DOWNSCALE, 0, 0, w, h); + + // blur X/Y + for (int i = 0; i < iterations; i++) { + int j = i & 1; + + gls.u_block.fog_color[0] = 1.0f / w; + gls.u_block.fog_color[1] = 1.0f / h; + gls.u_block.fog_color[j] = 0; + + GL_ForceTexture(TMU_TEXTURE, j ? TEXNUM_PP_BLUR_1 : TEXNUM_PP_BLUR_0); + qglBindFramebuffer(GL_FRAMEBUFFER, j ? FBO_BLUR_0 : FBO_BLUR_1); + GL_PostProcess(GLS_BLOOM_BLUR, 0, 0, w, h); + } + + GL_Setup2D(); + + glStateBits_t bits = GLS_BLOOM_OUTPUT; + if (q_unlikely(gl_showbloom->integer)) { + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_BLUR_0); + bits = GLS_DEFAULT; + } else { + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_SCENE); + GL_ForceTexture(TMU_LIGHTMAP, TEXNUM_PP_BLUR_0); + if (waterwarp) + bits |= GLS_WARP_ENABLE; + } + + // upscale & add + qglBindFramebuffer(GL_FRAMEBUFFER, 0); + GL_PostProcess(bits, glr.fd.x, glr.fd.y, glr.fd.width, glr.fd.height); +} + void R_RenderFrame(const refdef_t *fd) { GL_Flush2D(); @@ -683,20 +723,32 @@ void R_RenderFrame(const refdef_t *fd) } bool waterwarp = (glr.fd.rdflags & RDF_UNDERWATER) && gl_static.use_shaders && gl_waterwarp->integer; + bool bloom = !(glr.fd.rdflags & RDF_NOWORLDMODEL) && gl_static.use_shaders && gl_bloom->integer; - if (waterwarp) { - if (glr.fd.width != glr.framebuffer_width || glr.fd.height != glr.framebuffer_height) { - glr.framebuffer_ok = GL_InitWarpTexture(); + if (waterwarp || bloom || gl_bloom->modified) { + if (glr.fd.width != glr.framebuffer_width || glr.fd.height != glr.framebuffer_height || gl_bloom->modified) { + glr.framebuffer_ok = GL_InitFramebuffers(); glr.framebuffer_width = glr.fd.width; glr.framebuffer_height = glr.fd.height; + gl_bloom->modified = false; } - waterwarp = glr.framebuffer_ok; + if (!glr.framebuffer_ok) + waterwarp = bloom = false; } - if (waterwarp) - qglBindFramebuffer(GL_FRAMEBUFFER, gl_static.warp_framebuffer); + if (waterwarp || bloom) { + qglBindFramebuffer(GL_FRAMEBUFFER, FBO_SCENE); + glr.framebuffer_bound = true; - GL_Setup3D(waterwarp); + if (gl_clear->integer) { + GLenum buffers[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + qglDrawBuffers(bloom + 1, buffers); + qglClear(GL_COLOR_BUFFER_BIT); + qglDrawBuffers(1, buffers); + } + } + + GL_Setup3D(); GL_SetupFrustum(); @@ -722,14 +774,20 @@ void R_RenderFrame(const refdef_t *fd) GL_DrawDebugObjects(); - if (waterwarp) + if (glr.framebuffer_bound) { qglBindFramebuffer(GL_FRAMEBUFFER, 0); + glr.framebuffer_bound = false; + } // go back into 2D mode GL_Setup2D(); - if (waterwarp) - GL_WaterWarp(); + if (bloom) { + GL_DrawBloom(waterwarp); + } else if (waterwarp) { + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_SCENE); + GL_PostProcess(GLS_WARP_ENABLE, glr.fd.x, glr.fd.y, glr.fd.width, glr.fd.height); + } if (gl_polyblend->integer) GL_Blend(); @@ -914,6 +972,7 @@ static void GL_Register(void) gl_damageblend_frac = Cvar_Get("gl_damageblend_frac", "0.2", 0); gl_waterwarp = Cvar_Get("gl_waterwarp", "0", 0); gl_fog = Cvar_Get("gl_fog", "1", 0); + gl_bloom = Cvar_Get("gl_bloom", "0", 0); gl_swapinterval = Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_swapinterval->changed = gl_swapinterval_changed; @@ -926,6 +985,7 @@ static void GL_Register(void) gl_showtris = Cvar_Get("gl_showtris", "0", CVAR_CHEAT); gl_showorigins = Cvar_Get("gl_showorigins", "0", CVAR_CHEAT); gl_showtearing = Cvar_Get("gl_showtearing", "0", CVAR_CHEAT); + gl_showbloom = Cvar_Get("gl_showbloom", "0", CVAR_CHEAT); #if USE_DEBUG gl_showstats = Cvar_Get("gl_showstats", "0", 0); gl_showscrap = Cvar_Get("gl_showscrap", "0", 0); diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index 263a75d4c..81c0cb530 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -688,6 +688,12 @@ static void draw_alias_mesh(const uint16_t *indices, int num_indices, if (skin->texnum2) state |= GLS_GLOWMAP_ENABLE; + if (glr.framebuffer_bound && gl_bloom->integer) { + state |= GLS_BLOOM_GENERATE; + if (glr.ent->flags & RF_SHELL_MASK) + state |= GLS_BLOOM_SHELL; + } + GL_StateBits(state); GL_BindTexture(TMU_TEXTURE, skin->texnum); diff --git a/src/refresh/qgl.c b/src/refresh/qgl.c index 42ba5acf1..c5f3f9401 100644 --- a/src/refresh/qgl.c +++ b/src/refresh/qgl.c @@ -244,6 +244,7 @@ static const glsection_t sections[] = { QGL_FN(CreateShader), QGL_FN(DeleteProgram), QGL_FN(DeleteShader), + QGL_FN(DrawBuffers), QGL_FN(DisableVertexAttribArray), QGL_FN(EnableVertexAttribArray), QGL_FN(GetProgramInfoLog), @@ -301,6 +302,15 @@ static const glsection_t sections[] = { } }, + // GL 3.0, not ES + { + .ver_gl = QGL_VER(3, 0), + .functions = (const glfunction_t []) { + QGL_FN(BindFragDataLocation), + { NULL } + } + }, + // GL 3.1 // ES 3.2 { diff --git a/src/refresh/qgl.h b/src/refresh/qgl.h index b66e11547..24a6ea895 100644 --- a/src/refresh/qgl.h +++ b/src/refresh/qgl.h @@ -123,6 +123,7 @@ QGLAPI GLuint (APIENTRYP qglCreateProgram)(void); QGLAPI GLuint (APIENTRYP qglCreateShader)(GLenum type); QGLAPI void (APIENTRYP qglDeleteProgram)(GLuint program); QGLAPI void (APIENTRYP qglDeleteShader)(GLuint shader); +QGLAPI void (APIENTRYP qglDrawBuffers)(GLsizei n, const GLenum *bufs); QGLAPI void (APIENTRYP qglDisableVertexAttribArray)(GLuint index); QGLAPI void (APIENTRYP qglEnableVertexAttribArray)(GLuint index); QGLAPI void (APIENTRYP qglGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); @@ -158,6 +159,9 @@ QGLAPI const GLubyte *(APIENTRYP qglGetStringi)(GLenum name, GLuint index); QGLAPI void (APIENTRYP qglRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); QGLAPI void (APIENTRYP qglVertexAttribIPointer)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +// GL 3.0, not ES +QGLAPI void (APIENTRYP qglBindFragDataLocation)(GLuint program, GLuint colorNumber, const GLchar *name); + // GL 3.1 QGLAPI void (APIENTRYP qglGetActiveUniformBlockiv)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); QGLAPI GLuint (APIENTRYP qglGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName); diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 782a1101a..cf55890b0 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -23,6 +23,25 @@ with this program; if not, write to the Free Software Foundation, Inc., #define GLSL(x) SZ_Write(buf, CONST_STR_LEN(#x "\n")); #define GLSF(x) SZ_Write(buf, CONST_STR_LEN(x)) +#define GLSP(...) shader_printf(buf, __VA_ARGS__) + +static cvar_t *gl_bloom_sigma; + +q_printf(2, 3) +static void shader_printf(sizebuf_t *buf, const char *fmt, ...) +{ + va_list ap; + size_t len; + + Q_assert(buf->cursize <= buf->maxsize); + + va_start(ap, fmt); + len = Q_vsnprintf((char *)buf->data + buf->cursize, buf->maxsize - buf->cursize, fmt, ap); + va_end(ap); + + Q_assert(len <= buf->maxsize - buf->cursize); + buf->cursize += len; +} static void write_header(sizebuf_t *buf, glStateBits_t bits) { @@ -345,8 +364,92 @@ static void write_vertex_shader(sizebuf_t *buf, glStateBits_t bits) GLSF("}\n"); } +#define MAX_SIGMA 25 +#define MAX_RADIUS 50 + +// https://lisyarus.github.io/blog/posts/blur-coefficients-generator.html +static void write_gaussian_blur(sizebuf_t *buf) +{ + float sigma = gl_static.bloom_sigma; + int radius = min(sigma * 2 + 0.5f, MAX_RADIUS); + int samples = radius + 1; + int raw_samples = (radius * 2) + 1; + float offsets[MAX_RADIUS + 1]; + float weights[(MAX_RADIUS * 2) + 1]; + + // should not really happen + if (radius < 1) { + GLSL(vec4 blur(sampler2D src, vec2 tc, vec2 dir) { return texture(src, tc); }) + return; + } + + float sum = 0; + for (int i = -radius, j = 0; i <= radius; i++, j++) { + float w = expf(-(i * i) / (sigma * sigma)); + weights[j] = w; + sum += w; + } + + for (int i = 0; i < raw_samples; i++) + weights[i] /= sum; + + for (int i = -radius, j = 0; i <= radius; i += 2, j++) { + if (i == radius) { + offsets[j] = i; + weights[j] = weights[i + radius]; + } else { + float w0 = weights[i + radius + 0]; + float w1 = weights[i + radius + 1]; + float w = w0 + w1; + + if (w > 0) + offsets[j] = i + w1 / w; + else + offsets[j] = i; + + weights[j] = w; + } + } + + GLSP("#define BLUR_SAMPLES %d\n", samples); + + GLSF("const float blur_offsets[BLUR_SAMPLES] = float[BLUR_SAMPLES](\n"); + for (int i = 0; i < samples - 1; i++) + GLSP("%f, ", offsets[i]); + GLSP("%f);\n", offsets[samples - 1]); + + GLSF("const float blur_weights[BLUR_SAMPLES] = float[BLUR_SAMPLES](\n"); + for (int i = 0; i < samples - 1; i++) + GLSP("%f, ", weights[i]); + GLSP("%f);\n", weights[samples - 1]); + + GLSL( + vec4 blur(sampler2D src, vec2 tc, vec2 dir) { + vec4 result = vec4(0.0); + for (int i = 0; i < BLUR_SAMPLES; i++) + result += texture(src, tc + dir * blur_offsets[i]) * blur_weights[i]; + return result; + } + ) +} + +static void write_box_blur(sizebuf_t *buf) +{ + GLSL( + vec4 blur(sampler2D src, vec2 tc, vec2 dir) { + vec4 result = vec4(0.0); + const float o = 0.25; + result += texture(src, tc + vec2(-o, -o) * dir) * 0.25; + result += texture(src, tc + vec2(-o, o) * dir) * 0.25; + result += texture(src, tc + vec2( o, -o) * dir) * 0.25; + result += texture(src, tc + vec2( o, o) * dir) * 0.25; + return result; + } + ) +} + // XXX: this is very broken. but that's how it is in re-release. -static void write_height_fog(sizebuf_t *buf) +static void write_height_fog(sizebuf_t *buf, glStateBits_t bits) { GLSL({ float dir_z = normalize(v_world_pos - u_vieworg).z; @@ -361,7 +464,12 @@ static void write_height_fog(sizebuf_t *buf) vec3 fog_color = mix(u_heightfog_start.rgb, u_heightfog_end.rgb, fraction) * extinction; float fog = (1.0 - exp(-(u_heightfog_density * frag_depth))) * extinction; diffuse.rgb = mix(diffuse.rgb, fog_color.rgb, fog); - }) + ) + + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + GLSL(bloom.rgb *= 1.0 - fog;) + + GLSL(}) } static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) @@ -383,6 +491,8 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(uniform samplerCube u_texture;) } else { GLSL(uniform sampler2D u_texture;) + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_OUTPUT) + GLSL(uniform sampler2D u_bloom;) } if (bits & GLS_SKY_MASK) @@ -401,11 +511,25 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (!(bits & GLS_TEXTURE_REPLACE)) GLSL(in vec4 v_color;) + if (gl_config.ver_es) + GLSL(layout(location = 0)) GLSL(out vec4 o_color;) + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + if (gl_config.ver_es) + GLSL(layout(location = 1)) + GLSL(out vec4 o_bloom;) + } if (bits & GLS_FOG_HEIGHT) GLSL(in vec3 v_world_pos;) + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_BLUR) { + if (bits & GLS_BLOOM_SHELL) + write_box_blur(buf); + else + write_gaussian_blur(buf); + } + GLSF("void main() {\n"); if (bits & GLS_CLASSIC_SKY) { GLSL( @@ -425,18 +549,30 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (bits & GLS_WARP_ENABLE) GLSL(tc += w_amp * sin(tc.ts * w_phase + u_time);) - GLSL(vec4 diffuse = texture(u_texture, tc);) + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_BLUR) + GLSL(vec4 diffuse = blur(u_texture, tc, u_fog_color.xy);) + else + GLSL(vec4 diffuse = texture(u_texture, tc);) } if (bits & GLS_ALPHATEST_ENABLE) GLSL(if (diffuse.a <= 0.666) discard;) + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + GLSL(vec4 bloom = vec4(0.0);) + if (bits & GLS_LIGHTMAP_ENABLE) { GLSL(vec4 lightmap = texture(u_lightmap, v_lmtc);) if (bits & GLS_GLOWMAP_ENABLE) { GLSL(vec4 glowmap = texture(u_glowmap, tc);) GLSL(lightmap.rgb = mix(lightmap.rgb, vec3(1.0), glowmap.a);) + + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + GLSL(bloom.rgb = diffuse.rgb * glowmap.a;) + if (bits & GLS_INTENSITY_ENABLE) + GLSL(bloom.rgb *= u_intensity;) + } } GLSL(diffuse.rgb *= (lightmap.rgb + u_add) * u_modulate;) @@ -460,24 +596,49 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(diffuse.rgb += glowmap.rgb * u_intensity2;) else GLSL(diffuse.rgb += glowmap.rgb;) + + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + GLSL(bloom.rgb = glowmap.rgb;) + if (bits & GLS_INTENSITY_ENABLE) + GLSL(bloom.rgb *= u_intensity2;) + } + } + + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + if (bits & GLS_BLOOM_SHELL) + GLSL(bloom = diffuse;) + else + GLSL(bloom.a = diffuse.a;) } if (bits & (GLS_FOG_GLOBAL | GLS_FOG_HEIGHT)) GLSL(float frag_depth = gl_FragCoord.z / gl_FragCoord.w;) - if (bits & GLS_FOG_GLOBAL) + if (bits & GLS_FOG_GLOBAL) { GLSL({ float d = u_fog_color.a * frag_depth; - float fog = 1.0f - exp(-(d * d)); + float fog = 1.0 - exp(-(d * d)); diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, fog); - }) + ) + + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + GLSL(bloom.rgb *= 1.0 - fog;) + + GLSL(}) + } if (bits & GLS_FOG_HEIGHT) - write_height_fog(buf); + write_height_fog(buf, bits); if (bits & GLS_FOG_SKY) GLSL(diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, u_fog_sky_factor);) + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_OUTPUT) + GLSL(diffuse.rgb += texture(u_bloom, tc).rgb;) + + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + GLSL(o_bloom = bloom;) + GLSL(o_color = diffuse;) GLSF("}\n"); } @@ -565,6 +726,11 @@ static GLuint create_and_use_program(glStateBits_t bits) qglBindAttribLocation(program, VERT_ATTR_COLOR, "a_color"); } + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE && !gl_config.ver_es) { + qglBindFragDataLocation(program, 0, "o_color"); + qglBindFragDataLocation(program, 1, "o_bloom"); + } + qglLinkProgram(program); qglDeleteShader(shader_v); @@ -624,6 +790,8 @@ static GLuint create_and_use_program(glStateBits_t bits) qglUniform1i(qglGetUniformLocation(program, "u_texture2"), TMU_LIGHTMAP); } else { qglUniform1i(qglGetUniformLocation(program, "u_texture"), TMU_TEXTURE); + if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_OUTPUT) + qglUniform1i(qglGetUniformLocation(program, "u_bloom"), TMU_LIGHTMAP); } if (bits & GLS_LIGHTMAP_ENABLE) qglUniform1i(qglGetUniformLocation(program, "u_lightmap"), TMU_LIGHTMAP); @@ -659,6 +827,14 @@ static void shader_state_bits(glStateBits_t bits) GL_ScrollPos(gls.u_block.scroll, bits); gls.u_block_dirty = true; } + + if (diff & GLS_BLOOM_MASK && glr.framebuffer_bound) { + int n = (bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE ? 2 : 1; + qglDrawBuffers(n, (const GLenum []) { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1 + }); + } } static void shader_array_bits(glArrayBits_t bits) @@ -802,8 +978,47 @@ static void shader_clear_state(void) shader_use_program(GLS_DEFAULT); } +void GL_UpdateBlurParams(void) +{ + if (!gl_static.programs) + return; + if (!gl_bloom->integer) + return; + + float sigma = Cvar_ClampValue(gl_bloom_sigma, 1, MAX_SIGMA) * glr.fd.height / 1080; + + if (gl_static.bloom_sigma == sigma) + return; + + Com_DDPrintf("%s: %.1f\n", __func__, sigma); + gl_static.bloom_sigma = sigma; + + bool changed = false; + uint32_t map_size = HashMap_Size(gl_static.programs); + for (int i = 0; i < map_size; i++) { + glStateBits_t *bits = HashMap_GetKey(glStateBits_t, gl_static.programs, i); + if ((*bits & (GLS_BLOOM_MASK | GLS_BLOOM_SHELL)) == GLS_BLOOM_BLUR) { + GLuint *prog = HashMap_GetValue(GLuint, gl_static.programs, i); + qglDeleteProgram(*prog); + *prog = create_and_use_program(*bits); + changed = true; + } + } + + if (changed) + shader_use_program(gls.state_bits & GLS_SHADER_MASK); +} + +static void gl_bloom_sigma_changed(cvar_t *self) +{ + GL_UpdateBlurParams(); +} + static void shader_init(void) { + gl_bloom_sigma = Cvar_Get("gl_bloom_sigma", "4", 0); + gl_bloom_sigma->changed = gl_bloom_sigma_changed; + gl_static.programs = HashMap_TagCreate(glStateBits_t, GLuint, HashInt32, NULL, TAG_RENDERER); qglGenBuffers(1, &gl_static.uniform_buffer); @@ -831,6 +1046,9 @@ static void shader_shutdown(void) shader_disable_state(); qglUseProgram(0); + gl_static.bloom_sigma = 0; + gl_bloom_sigma->changed = NULL; + if (gl_static.programs) { uint32_t map_size = HashMap_Size(gl_static.programs); for (int i = 0; i < map_size; i++) { diff --git a/src/refresh/state.c b/src/refresh/state.c index ab66c5203..747f5ef57 100644 --- a/src/refresh/state.c +++ b/src/refresh/state.c @@ -297,9 +297,9 @@ static void GL_RotateForViewer(void) GL_ForceMatrix(matrix); } -void GL_Setup3D(bool waterwarp) +void GL_Setup3D(void) { - if (waterwarp) + if (glr.framebuffer_bound) qglViewport(0, 0, glr.fd.width, glr.fd.height); else qglViewport(glr.fd.x, r_config.height - (glr.fd.y + glr.fd.height), diff --git a/src/refresh/tess.c b/src/refresh/tess.c index def297b0b..70be78058 100644 --- a/src/refresh/tess.c +++ b/src/refresh/tess.c @@ -138,6 +138,7 @@ static void GL_FlushBeamSegments(void) if (!tess.numindices) return; + glStateBits_t state = GLS_BLEND_BLEND | GLS_DEPTHMASK_FALSE | glr.fog_bits; glArrayBits_t array = GLA_VERTEX | GLA_COLOR; GLuint texnum = TEXNUM_BEAM; @@ -146,8 +147,11 @@ static void GL_FlushBeamSegments(void) else array |= GLA_TC; + if (glr.framebuffer_bound && gl_bloom->integer) + state |= GLS_BLOOM_GENERATE | GLS_BLOOM_SHELL; + GL_BindTexture(TMU_TEXTURE, texnum); - GL_StateBits(GLS_BLEND_BLEND | GLS_DEPTHMASK_FALSE | glr.fog_bits); + GL_StateBits(state); GL_ArrayBits(array); GL_DrawIndexed(SHOWTRIS_FX); @@ -517,7 +521,7 @@ static const glVaDesc_t arraydescs[VA_TOTAL][VERT_ATTR_COUNT] = { [VA_OCCLUDE] = { [VERT_ATTR_POS] = ATTR_FLOAT(3, 3, 0), }, - [VA_WATERWARP] = { + [VA_POSTPROCESS] = { [VERT_ATTR_POS] = ATTR_FLOAT(2, 4, 0), [VERT_ATTR_TC] = ATTR_FLOAT(2, 4, 2), }, @@ -663,6 +667,8 @@ void GL_Flush3D(void) if (tess.texnum[TMU_GLOWMAP]) state |= GLS_GLOWMAP_ENABLE; } + if (glr.framebuffer_bound && gl_bloom->integer) + state |= GLS_BLOOM_GENERATE; if (!(state & GLS_TEXTURE_REPLACE)) array |= GLA_COLOR; diff --git a/src/refresh/texture.c b/src/refresh/texture.c index 58b8824cc..9de68c16e 100644 --- a/src/refresh/texture.c +++ b/src/refresh/texture.c @@ -436,6 +436,20 @@ static bool GL_MakePowerOfTwo(int *width, int *height) return false; } +static void GL_ClampTextureSize(int *width, int *height) +{ + while (*width > gl_config.max_texture_size || + *height > gl_config.max_texture_size) { + *width >>= 1; + *height >>= 1; + } + + if (*width < 1) + *width = 1; + if (*height < 1) + *height = 1; +} + /* =============== GL_Upload32 @@ -467,17 +481,7 @@ static void GL_Upload32(byte *data, int width, int height, int baselevel, imaget } // don't ever bother with >256 textures - while (scaled_width > gl_config.max_texture_size || - scaled_height > gl_config.max_texture_size) { - scaled_width >>= 1; - scaled_height >>= 1; - } - - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - + GL_ClampTextureSize(&scaled_width, &scaled_height); upload_width = scaled_width; upload_height = scaled_height; @@ -1145,25 +1149,40 @@ static void GL_InitCubemaps(void) sky->texnum = TEXNUM_CUBEMAP_DEFAULT; } -bool GL_InitWarpTexture(void) +static void GL_InitPostProcTexture(int w, int h) { - GL_ClearErrors(); - - GL_ForceTexture(TMU_TEXTURE, gl_static.warp_texture); - qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glr.fd.width, glr.fd.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +bool GL_InitFramebuffers(void) +{ + GL_ClearErrors(); + + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_SCENE); + GL_InitPostProcTexture(glr.fd.width, glr.fd.height); + + int w = glr.fd.width; + int h = glr.fd.height; + + if (!gl_bloom->integer) + w = h = 0; + + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_BLOOM); + GL_InitPostProcTexture(w, h); - qglBindFramebuffer(GL_FRAMEBUFFER, gl_static.warp_framebuffer); - qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_static.warp_texture, 0); + qglBindFramebuffer(GL_FRAMEBUFFER, FBO_SCENE); + qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, TEXNUM_PP_SCENE, 0); + qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gl_bloom->integer ? TEXNUM_PP_BLOOM : GL_NONE, 0); - qglBindRenderbuffer(GL_RENDERBUFFER, gl_static.warp_renderbuffer); + qglBindRenderbuffer(GL_RENDERBUFFER, gl_static.renderbuffer); qglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, glr.fd.width, glr.fd.height); qglBindRenderbuffer(GL_RENDERBUFFER, 0); - qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_static.warp_renderbuffer); + qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_static.renderbuffer); GLenum status = qglCheckFramebufferStatus(GL_FRAMEBUFFER); qglBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -1176,23 +1195,25 @@ bool GL_InitWarpTexture(void) return false; } - return true; -} + GL_UpdateBlurParams(); -static void GL_DeleteWarpTexture(void) -{ - if (gl_static.warp_framebuffer) { - qglDeleteFramebuffers(1, &gl_static.warp_framebuffer); - gl_static.warp_framebuffer = 0; - } - if (gl_static.warp_renderbuffer) { - qglDeleteRenderbuffers(1, &gl_static.warp_renderbuffer); - gl_static.warp_renderbuffer = 0; - } - if (gl_static.warp_texture) { - qglDeleteTextures(1, &gl_static.warp_texture); - gl_static.warp_texture = 0; - } + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_BLUR_0); + GL_InitPostProcTexture(w / 4, h / 4); + + GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_BLUR_1); + GL_InitPostProcTexture(w / 4, h / 4); + + qglBindFramebuffer(GL_FRAMEBUFFER, FBO_BLUR_0); + qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_bloom->integer ? TEXNUM_PP_BLUR_0 : GL_NONE, 0); + + qglBindFramebuffer(GL_FRAMEBUFFER, FBO_BLUR_1); + qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_bloom->integer ? TEXNUM_PP_BLUR_1 : GL_NONE, 0); + + qglBindFramebuffer(GL_FRAMEBUFFER, 0); + + GL_ShowErrors(__func__); + + return true; } static void gl_partshape_changed(cvar_t *self) @@ -1269,9 +1290,8 @@ void GL_InitImages(void) qglGenTextures(LM_MAX_LIGHTMAPS, lm.texnums); if (gl_static.use_shaders) { - qglGenTextures(1, &gl_static.warp_texture); - qglGenRenderbuffers(1, &gl_static.warp_renderbuffer); - qglGenFramebuffers(1, &gl_static.warp_framebuffer); + qglGenRenderbuffers(1, &gl_static.renderbuffer); + qglGenFramebuffers(FBO_COUNT, gl_static.framebuffers); } Scrap_Init(); @@ -1312,7 +1332,14 @@ void GL_ShutdownImages(void) memset(gl_static.texnums, 0, sizeof(gl_static.texnums)); memset(lm.texnums, 0, sizeof(lm.texnums)); - GL_DeleteWarpTexture(); + // delete framebuffers + if (gl_static.use_shaders) { + qglDeleteFramebuffers(FBO_COUNT, gl_static.framebuffers); + memset(gl_static.framebuffers, 0, sizeof(gl_static.framebuffers)); + + qglDeleteRenderbuffers(1, &gl_static.renderbuffer); + gl_static.renderbuffer = 0; + } #if USE_DEBUG r_charset = 0; From a3e8809004b46a78ed0a7db104715f36a9509695 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 29 Oct 2024 19:47:02 +0300 Subject: [PATCH 09/79] Delete program if it fails to compile/link. Using incomplete program may crash the GPU. --- src/refresh/shader.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index cf55890b0..4af6fb640 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -691,14 +691,14 @@ static GLuint create_and_use_program(glStateBits_t bits) write_vertex_shader(&sb, bits); GLuint shader_v = create_shader(GL_VERTEX_SHADER, &sb); if (!shader_v) - return program; + goto fail; SZ_Clear(&sb); write_fragment_shader(&sb, bits); GLuint shader_f = create_shader(GL_FRAGMENT_SHADER, &sb); if (!shader_f) { qglDeleteShader(shader_v); - return program; + goto fail; } qglAttachShader(program, shader_v); @@ -748,20 +748,20 @@ static GLuint create_and_use_program(glStateBits_t bits) Com_Printf("%s", buffer); Com_EPrintf("Error linking program\n"); - return program; + goto fail; } GLuint index = qglGetUniformBlockIndex(program, "u_block"); if (index == GL_INVALID_INDEX) { Com_EPrintf("Uniform block not found\n"); - return program; + goto fail; } GLint size = 0; qglGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); if (size != sizeof(gls.u_block)) { Com_EPrintf("Uniform block size mismatch: %d != %zu\n", size, sizeof(gls.u_block)); - return program; + goto fail; } qglUniformBlockBinding(program, index, UBO_UNIFORMS); @@ -771,7 +771,7 @@ static GLuint create_and_use_program(glStateBits_t bits) index = qglGetUniformBlockIndex(program, "Skeleton"); if (index == GL_INVALID_INDEX) { Com_EPrintf("Skeleton block not found\n"); - return program; + goto fail; } qglUniformBlockBinding(program, index, UBO_SKELETON); } @@ -799,6 +799,10 @@ static GLuint create_and_use_program(glStateBits_t bits) qglUniform1i(qglGetUniformLocation(program, "u_glowmap"), TMU_GLOWMAP); return program; + +fail: + qglDeleteProgram(program); + return 0; } static void shader_use_program(glStateBits_t key) From 1aca530fe902682e7771833d85189ffc1a353ab5 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 29 Oct 2024 20:13:01 +0300 Subject: [PATCH 10/79] Clean up uniforms binding code. --- src/refresh/gl.h | 5 +++ src/refresh/mesh.c | 5 --- src/refresh/shader.c | 73 ++++++++++++++++++++++++++------------------ 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/refresh/gl.h b/src/refresh/gl.h index 863106269..d56772a59 100644 --- a/src/refresh/gl.h +++ b/src/refresh/gl.h @@ -373,6 +373,11 @@ typedef struct { #define MD5_MAX_FRAMES 1024 /* Joint */ +typedef struct { + vec4_t pos; + vec4_t axis[3]; +} glJoint_t; + typedef struct { vec3_t pos; float scale; diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index 81c0cb530..58d13b59f 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -861,11 +861,6 @@ static void draw_skeleton_mesh(const md5_model_t *model, const md5_mesh_t *mesh, model->skins, model->num_skins); } -typedef struct { - vec4_t pos; - vec4_t axis[3]; -} glJoint_t; - static void draw_alias_skeleton(const md5_model_t *model) { const md5_joint_t *skel = temp_skeleton; diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 4af6fb640..8690a5f2c 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -65,7 +65,7 @@ static void write_header(sizebuf_t *buf, glStateBits_t bits) static void write_block(sizebuf_t *buf, glStateBits_t bits) { - GLSF("layout(std140) uniform u_block {\n"); + GLSF("layout(std140) uniform Uniforms {\n"); GLSL(mat4 m_vp;); GLSL(mat4 m_model;); @@ -676,6 +676,35 @@ static GLuint create_shader(GLenum type, const sizebuf_t *buf) return shader; } +static bool bind_uniform_block(GLuint program, const char *name, size_t cpu_size, GLuint binding) +{ + GLuint index = qglGetUniformBlockIndex(program, name); + if (index == GL_INVALID_INDEX) { + Com_EPrintf("%s block not found\n", name); + return false; + } + + GLint gpu_size = 0; + qglGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_DATA_SIZE, &gpu_size); + if (gpu_size != cpu_size) { + Com_EPrintf("%s block size mismatch: %d != %zu\n", name, gpu_size, cpu_size); + return false; + } + + qglUniformBlockBinding(program, index, binding); + return true; +} + +static void bind_texture_unit(GLuint program, const char *name, GLuint tmu) +{ + GLint loc = qglGetUniformLocation(program, name); + if (loc == -1) { + Com_EPrintf("Texture %s not found\n", name); + return; + } + qglUniform1i(loc, tmu); +} + static GLuint create_and_use_program(glStateBits_t bits) { char buffer[MAX_SHADER_CHARS]; @@ -751,52 +780,38 @@ static GLuint create_and_use_program(glStateBits_t bits) goto fail; } - GLuint index = qglGetUniformBlockIndex(program, "u_block"); - if (index == GL_INVALID_INDEX) { - Com_EPrintf("Uniform block not found\n"); - goto fail; - } - - GLint size = 0; - qglGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - if (size != sizeof(gls.u_block)) { - Com_EPrintf("Uniform block size mismatch: %d != %zu\n", size, sizeof(gls.u_block)); + if (!bind_uniform_block(program, "Uniforms", sizeof(gls.u_block), UBO_UNIFORMS)) goto fail; - } - - qglUniformBlockBinding(program, index, UBO_UNIFORMS); #if USE_MD5 - if (bits & GLS_MESH_MD5) { - index = qglGetUniformBlockIndex(program, "Skeleton"); - if (index == GL_INVALID_INDEX) { - Com_EPrintf("Skeleton block not found\n"); + if (bits & GLS_MESH_MD5) + if (!bind_uniform_block(program, "Skeleton", sizeof(glJoint_t) * MD5_MAX_JOINTS, UBO_SKELETON)) goto fail; - } - qglUniformBlockBinding(program, index, UBO_SKELETON); - } #endif qglUseProgram(program); #if USE_MD5 if (bits & GLS_MESH_MD5 && !(gl_config.caps & QGL_CAP_SHADER_STORAGE)) { - qglUniform1i(qglGetUniformLocation(program, "u_weights"), TMU_SKEL_WEIGHTS); - qglUniform1i(qglGetUniformLocation(program, "u_jointnums"), TMU_SKEL_JOINTNUMS); + bind_texture_unit(program, "u_weights", TMU_SKEL_WEIGHTS); + bind_texture_unit(program, "u_jointnums", TMU_SKEL_JOINTNUMS); } #endif + if (bits & GLS_CLASSIC_SKY) { - qglUniform1i(qglGetUniformLocation(program, "u_texture1"), TMU_TEXTURE); - qglUniform1i(qglGetUniformLocation(program, "u_texture2"), TMU_LIGHTMAP); + bind_texture_unit(program, "u_texture1", TMU_TEXTURE); + bind_texture_unit(program, "u_texture2", TMU_LIGHTMAP); } else { - qglUniform1i(qglGetUniformLocation(program, "u_texture"), TMU_TEXTURE); + bind_texture_unit(program, "u_texture", TMU_TEXTURE); if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_OUTPUT) - qglUniform1i(qglGetUniformLocation(program, "u_bloom"), TMU_LIGHTMAP); + bind_texture_unit(program, "u_bloom", TMU_LIGHTMAP); } + if (bits & GLS_LIGHTMAP_ENABLE) - qglUniform1i(qglGetUniformLocation(program, "u_lightmap"), TMU_LIGHTMAP); + bind_texture_unit(program, "u_lightmap", TMU_LIGHTMAP); + if (bits & GLS_GLOWMAP_ENABLE) - qglUniform1i(qglGetUniformLocation(program, "u_glowmap"), TMU_GLOWMAP); + bind_texture_unit(program, "u_glowmap", TMU_GLOWMAP); return program; From efc53a1fd4ae16f4bcec25f1a7d2a0c00db86980 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 29 Oct 2024 22:54:56 +0300 Subject: [PATCH 11/79] Fix uniforms block size mismatch. ANGLE doesn't like it, but Nvidia/Mesa are fine with it. --- src/refresh/shader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 8690a5f2c..b081ea37d 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -106,7 +106,7 @@ static void write_block(sizebuf_t *buf, glStateBits_t bits) float u_heightfog_density; float u_heightfog_falloff; vec2 pad_4; - vec3 u_vieworg; + vec4 u_vieworg; ) GLSF("};\n"); } @@ -452,7 +452,7 @@ static void write_box_blur(sizebuf_t *buf) static void write_height_fog(sizebuf_t *buf, glStateBits_t bits) { GLSL({ - float dir_z = normalize(v_world_pos - u_vieworg).z; + float dir_z = normalize(v_world_pos - u_vieworg.xyz).z; float s = sign(dir_z); dir_z += 0.00001 * (1.0 - s * s); float eye = u_vieworg.z - u_heightfog_start.w; From 05aa7ecb14c30915e8737554dd7cc6250fcbf4a7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 30 Oct 2024 20:23:00 +0300 Subject: [PATCH 12/79] Add GL_BindBufferBase() wrapper. --- src/refresh/gl.h | 7 +++++++ src/refresh/shader.c | 6 ++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/refresh/gl.h b/src/refresh/gl.h index d56772a59..beb6f1f67 100644 --- a/src/refresh/gl.h +++ b/src/refresh/gl.h @@ -798,6 +798,13 @@ static inline void GL_BindBuffer(GLenum target, GLuint buffer) } } +static inline void GL_BindBufferBase(GLenum target, GLuint index, GLuint buffer) +{ + glBufferBinding_t i = GL_BindingForTarget(target); + qglBindBufferBase(target, index, buffer); + gls.currentbuffer[i] = buffer; +} + static inline void GL_ClearDepth(GLfloat d) { if (qglClearDepthf) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index b081ea37d..217a08ec1 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -1041,15 +1041,13 @@ static void shader_init(void) gl_static.programs = HashMap_TagCreate(glStateBits_t, GLuint, HashInt32, NULL, TAG_RENDERER); qglGenBuffers(1, &gl_static.uniform_buffer); - GL_BindBuffer(GL_UNIFORM_BUFFER, gl_static.uniform_buffer); - qglBindBufferBase(GL_UNIFORM_BUFFER, UBO_UNIFORMS, gl_static.uniform_buffer); + GL_BindBufferBase(GL_UNIFORM_BUFFER, UBO_UNIFORMS, gl_static.uniform_buffer); qglBufferData(GL_UNIFORM_BUFFER, sizeof(gls.u_block), NULL, GL_DYNAMIC_DRAW); #if USE_MD5 if (gl_config.caps & QGL_CAP_SKELETON_MASK) { qglGenBuffers(1, &gl_static.skeleton_buffer); - GL_BindBuffer(GL_UNIFORM_BUFFER, gl_static.skeleton_buffer); - qglBindBufferBase(GL_UNIFORM_BUFFER, UBO_SKELETON, gl_static.skeleton_buffer); + GL_BindBufferBase(GL_UNIFORM_BUFFER, UBO_SKELETON, gl_static.skeleton_buffer); if ((gl_config.caps & QGL_CAP_SKELETON_MASK) == QGL_CAP_BUFFER_TEXTURE) qglGenTextures(2, gl_static.skeleton_tex); From e64752907ecbc62d151511e982e3f645f272ff92 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 30 Oct 2024 20:24:24 +0300 Subject: [PATCH 13/79] Remove unused argument. --- src/refresh/mesh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index 58d13b59f..fb07e7ad5 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -815,7 +815,7 @@ static void lerp_alias_skeleton(const md5_model_t *model) #pragma GCC reset_options #endif -static void bind_skel_arrays(const md5_mesh_t *mesh, const md5_joint_t *skel) +static void bind_skel_arrays(const md5_mesh_t *mesh) { if (gl_config.caps & QGL_CAP_SHADER_STORAGE) { qglBindBufferRange(GL_SHADER_STORAGE_BUFFER, SSBO_WEIGHTS, buffer, @@ -848,7 +848,7 @@ static void bind_skel_arrays(const md5_mesh_t *mesh, const md5_joint_t *skel) static void draw_skeleton_mesh(const md5_model_t *model, const md5_mesh_t *mesh, const md5_joint_t *skel) { if (buffer) - bind_skel_arrays(mesh, skel); + bind_skel_arrays(mesh); else if (glr.ent->flags & RF_SHELL_MASK) tess_shell_skel(mesh, skel); else if (dotshading) From cb7e15512184b858947cbfbddfc8b30f3b9d19b2 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 31 Oct 2024 03:30:15 +0300 Subject: [PATCH 14/79] Make glStateBits_t 64-bit. --- src/refresh/gl.h | 118 ++++++++++++++++++++++--------------------- src/refresh/main.c | 4 +- src/refresh/shader.c | 45 ++++++++--------- 3 files changed, 85 insertions(+), 82 deletions(-) diff --git a/src/refresh/gl.h b/src/refresh/gl.h index beb6f1f67..61891cf1d 100644 --- a/src/refresh/gl.h +++ b/src/refresh/gl.h @@ -50,6 +50,8 @@ typedef GLushort glIndex_t; typedef GLuint glIndex_t; #endif +typedef uint64_t glStateBits_t; + #define TAB_SIN(x) gl_static.sintab[(x) & 255] #define TAB_COS(x) gl_static.sintab[((x) + 64) & 255] @@ -149,7 +151,7 @@ typedef struct { lightpoint_t lightpoint; int num_beams; int num_flares; - int fog_bits, fog_bits_sky; + glStateBits_t fog_bits, fog_bits_sky; int framebuffer_width; int framebuffer_height; bool framebuffer_ok; @@ -505,62 +507,64 @@ void GL_LoadWorld(const char *name); * gl_state.c * */ -typedef enum { - GLS_DEFAULT = 0, - GLS_DEPTHMASK_FALSE = BIT(0), - GLS_DEPTHTEST_DISABLE = BIT(1), - GLS_CULL_DISABLE = BIT(2), - GLS_BLEND_BLEND = BIT(3), - GLS_BLEND_ADD = BIT(4), - GLS_BLEND_MODULATE = BIT(5), - - GLS_ALPHATEST_ENABLE = BIT(6), - GLS_TEXTURE_REPLACE = BIT(7), - GLS_SCROLL_ENABLE = BIT(8), - GLS_LIGHTMAP_ENABLE = BIT(9), - GLS_WARP_ENABLE = BIT(10), - GLS_INTENSITY_ENABLE = BIT(11), - GLS_GLOWMAP_ENABLE = BIT(12), - GLS_CLASSIC_SKY = BIT(13), - GLS_DEFAULT_SKY = BIT(14), - GLS_DEFAULT_FLARE = BIT(15), - - GLS_MESH_MD2 = BIT(16), - GLS_MESH_MD5 = BIT(17), - GLS_MESH_LERP = BIT(18), - GLS_MESH_SHELL = BIT(19), - GLS_MESH_SHADE = BIT(20), - - GLS_SHADE_SMOOTH = BIT(21), - GLS_SCROLL_X = BIT(22), - GLS_SCROLL_Y = BIT(23), - GLS_SCROLL_FLIP = BIT(24), - GLS_SCROLL_SLOW = BIT(25), - - GLS_FOG_GLOBAL = BIT(26), - GLS_FOG_HEIGHT = BIT(27), - GLS_FOG_SKY = BIT(28), - - GLS_BLOOM_GENERATE = BIT(29), - GLS_BLOOM_BLUR = BIT(30), - GLS_BLOOM_SHELL = BIT(31), - GLS_BLOOM_OUTPUT = GLS_BLOOM_GENERATE | GLS_BLOOM_BLUR, - GLS_BLOOM_DOWNSCALE = GLS_BLOOM_BLUR | GLS_BLOOM_SHELL, - GLS_BLOOM_MASK = GLS_BLOOM_OUTPUT, - - GLS_BLEND_MASK = GLS_BLEND_BLEND | GLS_BLEND_ADD | GLS_BLEND_MODULATE, - GLS_COMMON_MASK = GLS_DEPTHMASK_FALSE | GLS_DEPTHTEST_DISABLE | GLS_CULL_DISABLE | GLS_BLEND_MASK, - GLS_SKY_MASK = GLS_CLASSIC_SKY | GLS_DEFAULT_SKY, - GLS_FOG_MASK = GLS_FOG_GLOBAL | GLS_FOG_HEIGHT | GLS_FOG_SKY, - GLS_MESH_ANY = GLS_MESH_MD2 | GLS_MESH_MD5, - GLS_MESH_MASK = GLS_MESH_ANY | GLS_MESH_LERP | GLS_MESH_SHELL | GLS_MESH_SHADE, - GLS_SHADER_MASK = GLS_ALPHATEST_ENABLE | GLS_TEXTURE_REPLACE | GLS_SCROLL_ENABLE | - GLS_LIGHTMAP_ENABLE | GLS_WARP_ENABLE | GLS_INTENSITY_ENABLE | GLS_GLOWMAP_ENABLE | - GLS_SKY_MASK | GLS_DEFAULT_FLARE | GLS_MESH_MASK | GLS_FOG_MASK | GLS_BLOOM_MASK | GLS_BLOOM_SHELL, - GLS_UNIFORM_MASK = GLS_WARP_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_INTENSITY_ENABLE | - GLS_SKY_MASK | GLS_FOG_MASK | GLS_BLOOM_MASK, - GLS_SCROLL_MASK = GLS_SCROLL_ENABLE | GLS_SCROLL_X | GLS_SCROLL_Y | GLS_SCROLL_FLIP | GLS_SCROLL_SLOW, -} glStateBits_t; +#define GLS_DEFAULT 0ULL + +#define GLS_DEPTHMASK_FALSE BIT_ULL(0) +#define GLS_DEPTHTEST_DISABLE BIT_ULL(1) +#define GLS_CULL_DISABLE BIT_ULL(2) +#define GLS_BLEND_BLEND BIT_ULL(3) +#define GLS_BLEND_ADD BIT_ULL(4) +#define GLS_BLEND_MODULATE BIT_ULL(5) + +#define GLS_ALPHATEST_ENABLE BIT_ULL(6) +#define GLS_TEXTURE_REPLACE BIT_ULL(7) +#define GLS_SCROLL_ENABLE BIT_ULL(8) +#define GLS_LIGHTMAP_ENABLE BIT_ULL(9) +#define GLS_WARP_ENABLE BIT_ULL(10) +#define GLS_INTENSITY_ENABLE BIT_ULL(11) +#define GLS_GLOWMAP_ENABLE BIT_ULL(12) +#define GLS_CLASSIC_SKY BIT_ULL(13) +#define GLS_DEFAULT_SKY BIT_ULL(14) +#define GLS_DEFAULT_FLARE BIT_ULL(15) + +#define GLS_MESH_MD2 BIT_ULL(16) +#define GLS_MESH_MD5 BIT_ULL(17) +#define GLS_MESH_LERP BIT_ULL(18) +#define GLS_MESH_SHELL BIT_ULL(19) +#define GLS_MESH_SHADE BIT_ULL(20) + +#define GLS_SHADE_SMOOTH BIT_ULL(21) +#define GLS_SCROLL_X BIT_ULL(22) +#define GLS_SCROLL_Y BIT_ULL(23) +#define GLS_SCROLL_FLIP BIT_ULL(24) +#define GLS_SCROLL_SLOW BIT_ULL(25) + +#define GLS_FOG_GLOBAL BIT_ULL(26) +#define GLS_FOG_HEIGHT BIT_ULL(27) +#define GLS_FOG_SKY BIT_ULL(28) + +#define GLS_BLOOM_GENERATE BIT_ULL(29) +#define GLS_BLOOM_OUTPUT BIT_ULL(30) +#define GLS_BLOOM_SHELL BIT_ULL(31) + +#define GLS_BLUR_GAUSS BIT_ULL(32) +#define GLS_BLUR_BOX BIT_ULL(33) + +#define GLS_BLEND_MASK (GLS_BLEND_BLEND | GLS_BLEND_ADD | GLS_BLEND_MODULATE) +#define GLS_COMMON_MASK (GLS_DEPTHMASK_FALSE | GLS_DEPTHTEST_DISABLE | GLS_CULL_DISABLE | GLS_BLEND_MASK) +#define GLS_SKY_MASK (GLS_CLASSIC_SKY | GLS_DEFAULT_SKY) +#define GLS_FOG_MASK (GLS_FOG_GLOBAL | GLS_FOG_HEIGHT | GLS_FOG_SKY) +#define GLS_MESH_ANY (GLS_MESH_MD2 | GLS_MESH_MD5) +#define GLS_MESH_MASK (GLS_MESH_ANY | GLS_MESH_LERP | GLS_MESH_SHELL | GLS_MESH_SHADE) +#define GLS_BLOOM_MASK (GLS_BLOOM_GENERATE | GLS_BLOOM_OUTPUT | GLS_BLOOM_SHELL) +#define GLS_BLUR_MASK (GLS_BLUR_GAUSS | GLS_BLUR_BOX) +#define GLS_SHADER_MASK (GLS_ALPHATEST_ENABLE | GLS_TEXTURE_REPLACE | GLS_SCROLL_ENABLE | \ + GLS_LIGHTMAP_ENABLE | GLS_WARP_ENABLE | GLS_INTENSITY_ENABLE | \ + GLS_GLOWMAP_ENABLE | GLS_SKY_MASK | GLS_DEFAULT_FLARE | GLS_MESH_MASK | \ + GLS_FOG_MASK | GLS_BLOOM_MASK | GLS_BLUR_MASK) +#define GLS_UNIFORM_MASK (GLS_WARP_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_INTENSITY_ENABLE | \ + GLS_SKY_MASK | GLS_FOG_MASK | GLS_BLUR_MASK) +#define GLS_SCROLL_MASK (GLS_SCROLL_ENABLE | GLS_SCROLL_X | GLS_SCROLL_Y | GLS_SCROLL_FLIP | GLS_SCROLL_SLOW) typedef enum { VERT_ATTR_POS, diff --git a/src/refresh/main.c b/src/refresh/main.c index fbbc7ca74..8d57bb81b 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -657,7 +657,7 @@ static void GL_DrawBloom(bool waterwarp) gls.u_block.fog_color[1] = 1.0f / h; GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_BLOOM); qglBindFramebuffer(GL_FRAMEBUFFER, FBO_BLUR_0); - GL_PostProcess(GLS_BLOOM_DOWNSCALE, 0, 0, w, h); + GL_PostProcess(GLS_BLUR_BOX, 0, 0, w, h); // blur X/Y for (int i = 0; i < iterations; i++) { @@ -669,7 +669,7 @@ static void GL_DrawBloom(bool waterwarp) GL_ForceTexture(TMU_TEXTURE, j ? TEXNUM_PP_BLUR_1 : TEXNUM_PP_BLUR_0); qglBindFramebuffer(GL_FRAMEBUFFER, j ? FBO_BLUR_0 : FBO_BLUR_1); - GL_PostProcess(GLS_BLOOM_BLUR, 0, 0, w, h); + GL_PostProcess(GLS_BLUR_GAUSS, 0, 0, w, h); } GL_Setup2D(); diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 217a08ec1..0ad67e423 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -466,7 +466,7 @@ static void write_height_fog(sizebuf_t *buf, glStateBits_t bits) diffuse.rgb = mix(diffuse.rgb, fog_color.rgb, fog); ) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + if (bits & GLS_BLOOM_GENERATE) GLSL(bloom.rgb *= 1.0 - fog;) GLSL(}) @@ -491,7 +491,7 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(uniform samplerCube u_texture;) } else { GLSL(uniform sampler2D u_texture;) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_OUTPUT) + if (bits & GLS_BLOOM_OUTPUT) GLSL(uniform sampler2D u_bloom;) } @@ -514,7 +514,8 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (gl_config.ver_es) GLSL(layout(location = 0)) GLSL(out vec4 o_color;) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + + if (bits & GLS_BLOOM_GENERATE) { if (gl_config.ver_es) GLSL(layout(location = 1)) GLSL(out vec4 o_bloom;) @@ -523,12 +524,10 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (bits & GLS_FOG_HEIGHT) GLSL(in vec3 v_world_pos;) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_BLUR) { - if (bits & GLS_BLOOM_SHELL) - write_box_blur(buf); - else - write_gaussian_blur(buf); - } + if (bits & GLS_BLUR_GAUSS) + write_gaussian_blur(buf); + else if (bits & GLS_BLUR_BOX) + write_box_blur(buf); GLSF("void main() {\n"); if (bits & GLS_CLASSIC_SKY) { @@ -549,7 +548,7 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (bits & GLS_WARP_ENABLE) GLSL(tc += w_amp * sin(tc.ts * w_phase + u_time);) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_BLUR) + if (bits & GLS_BLUR_MASK) GLSL(vec4 diffuse = blur(u_texture, tc, u_fog_color.xy);) else GLSL(vec4 diffuse = texture(u_texture, tc);) @@ -558,7 +557,7 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (bits & GLS_ALPHATEST_ENABLE) GLSL(if (diffuse.a <= 0.666) discard;) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + if (bits & GLS_BLOOM_GENERATE) GLSL(vec4 bloom = vec4(0.0);) if (bits & GLS_LIGHTMAP_ENABLE) { @@ -568,7 +567,7 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(vec4 glowmap = texture(u_glowmap, tc);) GLSL(lightmap.rgb = mix(lightmap.rgb, vec3(1.0), glowmap.a);) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + if (bits & GLS_BLOOM_GENERATE) { GLSL(bloom.rgb = diffuse.rgb * glowmap.a;) if (bits & GLS_INTENSITY_ENABLE) GLSL(bloom.rgb *= u_intensity;) @@ -597,14 +596,14 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) else GLSL(diffuse.rgb += glowmap.rgb;) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + if (bits & GLS_BLOOM_GENERATE) { GLSL(bloom.rgb = glowmap.rgb;) if (bits & GLS_INTENSITY_ENABLE) GLSL(bloom.rgb *= u_intensity2;) } } - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) { + if (bits & GLS_BLOOM_GENERATE) { if (bits & GLS_BLOOM_SHELL) GLSL(bloom = diffuse;) else @@ -621,7 +620,7 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, fog); ) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + if (bits & GLS_BLOOM_GENERATE) GLSL(bloom.rgb *= 1.0 - fog;) GLSL(}) @@ -633,10 +632,10 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (bits & GLS_FOG_SKY) GLSL(diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, u_fog_sky_factor);) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_OUTPUT) + if (bits & GLS_BLOOM_OUTPUT) GLSL(diffuse.rgb += texture(u_bloom, tc).rgb;) - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE) + if (bits & GLS_BLOOM_GENERATE) GLSL(o_bloom = bloom;) GLSL(o_color = diffuse;) @@ -755,7 +754,7 @@ static GLuint create_and_use_program(glStateBits_t bits) qglBindAttribLocation(program, VERT_ATTR_COLOR, "a_color"); } - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE && !gl_config.ver_es) { + if (bits & GLS_BLOOM_GENERATE && !gl_config.ver_es) { qglBindFragDataLocation(program, 0, "o_color"); qglBindFragDataLocation(program, 1, "o_bloom"); } @@ -803,7 +802,7 @@ static GLuint create_and_use_program(glStateBits_t bits) bind_texture_unit(program, "u_texture2", TMU_LIGHTMAP); } else { bind_texture_unit(program, "u_texture", TMU_TEXTURE); - if ((bits & GLS_BLOOM_MASK) == GLS_BLOOM_OUTPUT) + if (bits & GLS_BLOOM_OUTPUT) bind_texture_unit(program, "u_bloom", TMU_LIGHTMAP); } @@ -847,8 +846,8 @@ static void shader_state_bits(glStateBits_t bits) gls.u_block_dirty = true; } - if (diff & GLS_BLOOM_MASK && glr.framebuffer_bound) { - int n = (bits & GLS_BLOOM_MASK) == GLS_BLOOM_GENERATE ? 2 : 1; + if (diff & GLS_BLOOM_GENERATE && glr.framebuffer_bound) { + int n = (bits & GLS_BLOOM_GENERATE) ? 2 : 1; qglDrawBuffers(n, (const GLenum []) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 @@ -1016,7 +1015,7 @@ void GL_UpdateBlurParams(void) uint32_t map_size = HashMap_Size(gl_static.programs); for (int i = 0; i < map_size; i++) { glStateBits_t *bits = HashMap_GetKey(glStateBits_t, gl_static.programs, i); - if ((*bits & (GLS_BLOOM_MASK | GLS_BLOOM_SHELL)) == GLS_BLOOM_BLUR) { + if (*bits & GLS_BLUR_GAUSS) { GLuint *prog = HashMap_GetValue(GLuint, gl_static.programs, i); qglDeleteProgram(*prog); *prog = create_and_use_program(*bits); @@ -1038,7 +1037,7 @@ static void shader_init(void) gl_bloom_sigma = Cvar_Get("gl_bloom_sigma", "4", 0); gl_bloom_sigma->changed = gl_bloom_sigma_changed; - gl_static.programs = HashMap_TagCreate(glStateBits_t, GLuint, HashInt32, NULL, TAG_RENDERER); + gl_static.programs = HashMap_TagCreate(glStateBits_t, GLuint, HashInt64, NULL, TAG_RENDERER); qglGenBuffers(1, &gl_static.uniform_buffer); GL_BindBufferBase(GL_UNIFORM_BUFFER, UBO_UNIFORMS, gl_static.uniform_buffer); From c96366a4935f4a100b6625a099f3186bff76541b Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 31 Oct 2024 20:52:55 +0300 Subject: [PATCH 15/79] =?UTF-8?q?Print=20shader=20source=20with=20?= =?UTF-8?q?=E2=80=98developer=203=E2=80=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/refresh/shader.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 0ad67e423..27679228b 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -653,6 +653,9 @@ static GLuint create_shader(GLenum type, const sizebuf_t *buf) return 0; } + Com_DDDPrintf("Compiling %s shader (%d bytes):\n%.*s\n", + type == GL_VERTEX_SHADER ? "vertex" : "fragment", size, size, data); + qglShaderSource(shader, 1, &data, &size); qglCompileShader(shader); GLint status = 0; From 0229402539542176c2a716c8f51be6558c09b5bb Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 3 Nov 2024 11:15:41 +0300 Subject: [PATCH 16/79] Add color shell to weapon model too. --- src/client/entities.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/client/entities.c b/src/client/entities.c index 376dc3ebd..9226e28a0 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -1084,8 +1084,13 @@ static int shell_effect_hack(void) flags |= RF_SHELL_DOUBLE; if (ent->current.effects & EF_HALF_DAMAGE) flags |= RF_SHELL_HALF_DAM; - if (ent->current.morefx & EFX_DUALFIRE) - flags |= RF_SHELL_LITE_GREEN; + + if (cl.csr.extended) { + if (ent->current.morefx & EFX_DUALFIRE) + flags |= RF_SHELL_LITE_GREEN; + if (ent->current.effects & EF_COLOR_SHELL) + flags |= ent->current.renderfx & RF_SHELL_MASK; + } return flags; } From 2fe584ce4f81ef155d467a45605e0ebdb2a0e16f Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 3 Nov 2024 11:23:17 +0300 Subject: [PATCH 17/79] Improve visibility of typewritten strings. --- src/client/screen.c | 22 ++++++++++++++++------ src/refresh/draw.c | 8 +++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/client/screen.c b/src/client/screen.c index 1a5a895b3..9d48697f9 100644 --- a/src/client/screen.c +++ b/src/client/screen.c @@ -160,21 +160,28 @@ void SCR_DrawStringMulti(int x, int y, int flags, size_t maxlen, { char *p; size_t len; + int last_x = x; + int last_y = y; while (*s && maxlen) { p = strchr(s, '\n'); if (!p) { - SCR_DrawStringEx(x, y, flags, maxlen, s, font); + last_x = SCR_DrawStringEx(x, y, flags, maxlen, s, font); + last_y = y; break; } len = min(p - s, maxlen); - SCR_DrawStringEx(x, y, flags, len, s, font); + last_x = SCR_DrawStringEx(x, y, flags, len, s, font); + last_y = y; maxlen -= len; y += CHAR_HEIGHT; s = p + 1; } + + if (flags & UI_DRAWCURSOR && com_localTime & BIT(8)) + R_DrawChar(last_x, last_y, flags, 11, font); } @@ -446,7 +453,7 @@ void SCR_CenterPrint(const char *str, bool typewrite) static void SCR_DrawCenterString(void) { centerprint_t *cp; - int y; + int y, flags; float alpha; size_t maxlen; @@ -470,13 +477,16 @@ static void SCR_DrawCenterString(void) R_SetAlpha(alpha * scr_alpha->value); y = scr.hud_height / 4 - cp->lines * CHAR_HEIGHT / 2; + flags = UI_CENTER; - if (cp->typewrite) + if (cp->typewrite) { maxlen = scr_printspeed->value * 0.001f * (cls.realtime - cp->start); - else + flags |= UI_DROPSHADOW | UI_DRAWCURSOR; + } else { maxlen = MAX_STRING_CHARS; + } - SCR_DrawStringMulti(scr.hud_width / 2, y, UI_CENTER, + SCR_DrawStringMulti(scr.hud_width / 2, y, flags, maxlen, cp->string, scr.font_pic); R_SetAlpha(scr_alpha->value); diff --git a/src/refresh/draw.c b/src/refresh/draw.c index 95c90e159..66623723a 100644 --- a/src/refresh/draw.c +++ b/src/refresh/draw.c @@ -361,7 +361,7 @@ static inline void draw_char(int x, int y, int flags, int c, const image_t *imag s = (c & 15) * 0.0625f; t = (c >> 4) * 0.0625f; - if (gl_fontshadow->integer > 0 && c != 0x83) { + if (flags & UI_DROPSHADOW && c != 0x83) { uint32_t black = draw.colors[0].u32 & U32_ALPHA; GL_StretchPic(x + 1, y + 1, CHAR_WIDTH, CHAR_HEIGHT, s, t, @@ -378,6 +378,9 @@ static inline void draw_char(int x, int y, int flags, int c, const image_t *imag void R_DrawChar(int x, int y, int flags, int c, qhandle_t font) { + if (gl_fontshadow->integer > 0) + flags |= UI_DROPSHADOW; + draw_char(x, y, flags, c & 255, IMG_ForHandle(font)); } @@ -385,6 +388,9 @@ int R_DrawString(int x, int y, int flags, size_t maxlen, const char *s, qhandle_ { const image_t *image = IMG_ForHandle(font); + if (gl_fontshadow->integer > 0) + flags |= UI_DROPSHADOW; + while (maxlen-- && *s) { byte c = *s++; draw_char(x, y, flags, c, image); From 78fe88d749f2b2303c5af799db70594b9221add9 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 4 Nov 2024 19:43:09 +0300 Subject: [PATCH 18/79] Update mgu6m2 checksum. --- src/common/bsp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index 3bc3a688f..50cd8783b 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -186,6 +186,7 @@ static void BSP_PrintStats(const bsp_t *bsp) #endif Com_Printf("\n"); } + Com_Printf("Checksum : %#x\n", bsp->checksum); Com_Printf("------------------\n"); } @@ -1083,7 +1084,7 @@ byte *BSP_ClusterVis(const bsp_t *bsp, byte *mask, int cluster, int vis) Q_SetBit(mask, 939); Q_SetBit(mask, 947); } - } else if (bsp->checksum == 0x2b2ccdd1) { + } else if (bsp->checksum == 0x1ebe8001) { // mgu6m2, waterfall Q_SetBit(mask, 213); Q_SetBit(mask, 214); From 981a3084b881975f55a2eec94a2f322620c06fc4 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 4 Nov 2024 20:36:01 +0300 Subject: [PATCH 19/79] Don't OR contents in CM_ClipEntity(). It turns out fire_lead() water detection logic has been broken all this time due to this change. Revert to original Q2 behavior. --- src/common/cmodel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/cmodel.c b/src/common/cmodel.c index d3ea14200..7cdf47294 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -850,7 +850,7 @@ void CM_ClipEntity(trace_t *dst, const trace_t *src, struct edict_s *ent) VectorCopy(src->endpos, dst->endpos); dst->plane = src->plane; dst->surface = src->surface; - dst->contents |= src->contents; + dst->contents = src->contents; dst->ent = ent; } } From 93d383b597c2f30ebb3fb7bd0b9ce6c217cc9d1e Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 5 Nov 2024 18:54:55 +0300 Subject: [PATCH 20/79] Fix BFG laser sparks color. --- src/game/g_weapon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c index 3e78e7a14..c8e99d4de 100644 --- a/src/game/g_weapon.c +++ b/src/game/g_weapon.c @@ -805,7 +805,7 @@ void bfg_think(edict_t *self) gi.WriteByte(4); gi.WritePosition(tr.endpos); gi.WriteDir(tr.plane.normal); - gi.WriteByte(self->s.skinnum); + gi.WriteByte(0xd0); gi.multicast(tr.endpos, MULTICAST_PVS); break; } From eba1fe4a7719aa83dcf193ab020785ea818fb485 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 5 Nov 2024 20:39:07 +0300 Subject: [PATCH 21/79] Fix PSX skies. --- src/refresh/surf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/refresh/surf.c b/src/refresh/surf.c index 648b5a733..2c3830f98 100644 --- a/src/refresh/surf.c +++ b/src/refresh/surf.c @@ -1046,7 +1046,7 @@ void GL_LoadWorld(const char *name) if (info->c.flags & SURF_SKY) { if (!gl_static.use_cubemaps) { info->image = R_NOTEXTURE; - } else if (Q_stricmpn(info->name, CONST_STR_LEN("n64/env/sky")) == 0) { + } else if (Q_stristr(info->name, "env/sky")) { Q_concat(buffer, sizeof(buffer), "textures/", info->name, ".tga"); info->image = IMG_Find(buffer, IT_SKY, IF_REPEAT | IF_CLASSIC_SKY); } else if (Q_stricmpn(info->name, CONST_STR_LEN("sky/")) == 0) { From e1d4a993dd2b5de572a2d9f13a908165e38d6e7d Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 5 Nov 2024 20:58:57 +0300 Subject: [PATCH 22/79] Draw PSX flare as default flare. --- src/client/precache.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client/precache.c b/src/client/precache.c index 4bd64794e..a7adc32ed 100644 --- a/src/client/precache.c +++ b/src/client/precache.c @@ -328,8 +328,12 @@ static qhandle_t CL_RegisterImage(const char *s) // if it's in a subdir and has an extension, it's either a sprite or a skin // allow /some/pic.pcx escape syntax if (cl.csr.extended && *s != '/' && *s != '\\' && *COM_FileExtension(s)) { + if (!FS_pathcmpn(s, CONST_STR_LEN("sprites/psx_flare"))) + return R_RegisterImage(s, IT_SPRITE, IF_DEFAULT_FLARE); + if (!FS_pathcmpn(s, CONST_STR_LEN("sprites/"))) return R_RegisterSprite(s); + if (strchr(s, '/')) return R_RegisterSkin(s); } From 9c29f6e2f136c45a2723268962a6d49e93de9ca6 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 5 Nov 2024 22:00:34 +0300 Subject: [PATCH 23/79] Better smoothing of short steps. --- src/client/entities.c | 3 --- src/client/predict.c | 10 ++++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/client/entities.c b/src/client/entities.c index 9226e28a0..76fde71e6 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -1374,9 +1374,6 @@ void CL_CalcViewValues(void) VectorMA(cl.predicted_origin, backlerp, cl.prediction_error, cl.refdef.vieworg); // smooth out stair climbing - if (cl.predicted_step < 127 * 0.125f) { - delta <<= 1; // small steps - } if (delta < 100) { cl.refdef.vieworg[2] -= cl.predicted_step * (100 - delta) * 0.01f; } diff --git a/src/client/predict.c b/src/client/predict.c index 2fd887ea8..96522653a 100644 --- a/src/client/predict.c +++ b/src/client/predict.c @@ -261,8 +261,14 @@ void CL_PredictMovement(void) if (pm.s.pm_type != PM_SPECTATOR && (pm.s.pm_flags & PMF_ON_GROUND)) { oldz = cl.predicted_origins[cl.predicted_step_frame & CMD_MASK][2]; step = pm.s.origin[2] - oldz; - if (step > 63 && step < 160) { - cl.predicted_step = step * 0.125f; + if (step >= 63 && step < 160) { + // check for stepping up before a previous step is completed + unsigned delta = cls.realtime - cl.predicted_step_time; + float prev_step = 0; + if (delta < 100) + prev_step = cl.predicted_step * (100 - delta) * 0.01f; + + cl.predicted_step = min(prev_step + step * 0.125f, 32); cl.predicted_step_time = cls.realtime; cl.predicted_step_frame = frame + 1; // don't double step } From c1416b839f073e9febc186f6e743e206bce1de7a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 6 Nov 2024 19:17:31 +0300 Subject: [PATCH 24/79] Re-add debug graph support. Can still be useful for some things. Also include packet size mode from R1Q2. --- inc/client/client.h | 2 + src/client/client.h | 2 + src/client/demo.c | 2 +- src/client/main.c | 4 ++ src/client/null.c | 4 ++ src/client/parse.c | 12 +++-- src/client/screen.c | 126 ++++++++++++++++++++++++++++++++++++++++++++ src/server/game.c | 6 +-- 8 files changed, 149 insertions(+), 9 deletions(-) diff --git a/inc/client/client.h b/inc/client/client.h index 71f1854a3..2566a732b 100644 --- a/inc/client/client.h +++ b/inc/client/client.h @@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc., void CL_PreInit(void); +void SCR_DebugGraph(float value, int color); + #if USE_CLIENT #define MAX_LOCAL_SERVERS 16 diff --git a/src/client/client.h b/src/client/client.h index 85962feed..767054fc1 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -198,6 +198,7 @@ typedef struct { server_frame_t frames[UPDATE_BACKUP]; unsigned frameflags; + int suppress_count; server_frame_t frame; // received from server server_frame_t oldframe; @@ -1007,6 +1008,7 @@ void SCR_ModeChanged(void); void SCR_LagSample(void); void SCR_LagClear(void); void SCR_SetCrosshairColor(void); +void SCR_AddNetgraph(void); float SCR_FadeAlpha(unsigned startTime, unsigned visTime, unsigned fadeTime); int SCR_DrawStringEx(int x, int y, int flags, size_t maxlen, const char *s, qhandle_t font); diff --git a/src/client/demo.c b/src/client/demo.c index 317b07334..57d72573a 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -166,7 +166,7 @@ static void emit_delta_frame(const server_frame_t *from, const server_frame_t *t MSG_WriteLong(tonum); MSG_WriteLong(fromnum); // what we are delta'ing from if (cls.serverProtocol != PROTOCOL_VERSION_OLD) - MSG_WriteByte(0); // rate dropped packets + MSG_WriteByte(cl.suppress_count); // rate dropped packets // send over the areabits MSG_WriteByte(to->areabytes); diff --git a/src/client/main.c b/src/client/main.c index 5f4b6e88a..224799b16 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1483,8 +1483,12 @@ static void CL_PacketEvent(void) cls.errorReceived = false; // don't drop #endif + cl.suppress_count = 0; + CL_ParseServerMessage(); + SCR_AddNetgraph(); + SCR_LagSample(); // if recording demo, write the message out diff --git a/src/client/null.c b/src/client/null.c index 81c2f1705..db3f319a0 100644 --- a/src/client/null.c +++ b/src/client/null.c @@ -33,3 +33,7 @@ void CL_PreInit(void) for (int i = 0; i < q_countof(nullcmds); i++) Cmd_AddCommand(nullcmds[i], NULL); } + +void SCR_DebugGraph(float value, int color) +{ +} diff --git a/src/client/parse.c b/src/client/parse.c index 216c86d9e..cf0342d4c 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -230,9 +230,15 @@ static void CL_ParseFrame(int extrabits) // CLIENTDROP is implied, don't draw both suppressed &= ~FF_CLIENTDROP; } + if (suppressed & FF_SUPPRESSED) { + cl.suppress_count = 1; + } cl.frameflags |= suppressed; - } else if (suppressed) { - cl.frameflags |= FF_SUPPRESSED; + } else { + if (suppressed) { + cl.frameflags |= FF_SUPPRESSED; + } + cl.suppress_count = suppressed; } extraflags = (extrabits << 4) | (bits >> SUPPRESSCOUNT_BITS); } else { @@ -245,7 +251,7 @@ static void CL_ParseFrame(int extrabits) // BIG HACK to let old demos continue to work if (cls.serverProtocol != PROTOCOL_VERSION_OLD) { - suppressed = MSG_ReadByte(); + cl.suppress_count = suppressed = MSG_ReadByte(); if (suppressed) { cl.frameflags |= FF_SUPPRESSED; } diff --git a/src/client/screen.c b/src/client/screen.c index 9d48697f9..0f89a5392 100644 --- a/src/client/screen.c +++ b/src/client/screen.c @@ -67,6 +67,13 @@ static cvar_t *scr_showpmove; #endif static cvar_t *scr_showturtle; +static cvar_t *scr_netgraph; +static cvar_t *scr_timegraph; +static cvar_t *scr_debuggraph; +static cvar_t *scr_graphheight; +static cvar_t *scr_graphscale; +static cvar_t *scr_graphshift; + static cvar_t *scr_draw2d; static cvar_t *scr_lag_x; static cvar_t *scr_lag_y; @@ -290,6 +297,112 @@ BAR GRAPHS =============================================================================== */ +/* +============== +SCR_AddNetgraph + +A new packet was just parsed +============== +*/ +void SCR_AddNetgraph(void) +{ + int i, color; + unsigned ping; + + // if using the debuggraph for something else, don't + // add the net lines + if (scr_debuggraph->integer || scr_timegraph->integer) + return; + + for (i = 0; i < cls.netchan.dropped; i++) + SCR_DebugGraph(30, 0x40); + + for (i = 0; i < cl.suppress_count; i++) + SCR_DebugGraph(30, 0xdf); + + if (scr_netgraph->integer > 1) { + ping = msg_read.cursize; + if (ping < 200) + color = 61; + else if (ping < 500) + color = 59; + else if (ping < 800) + color = 57; + else if (ping < 1200) + color = 224; + else + color = 242; + ping /= 40; + } else { + // see what the latency was on this packet + i = cls.netchan.incoming_acknowledged & CMD_MASK; + ping = (cls.realtime - cl.history[i].sent) / 30; + color = 0xd0; + } + + SCR_DebugGraph(min(ping, 30), color); +} + +#define GRAPH_SAMPLES 4096 +#define GRAPH_MASK (GRAPH_SAMPLES - 1) + +static struct { + float values[GRAPH_SAMPLES]; + byte colors[GRAPH_SAMPLES]; + unsigned current; +} graph; + +/* +============== +SCR_DebugGraph +============== +*/ +void SCR_DebugGraph(float value, int color) +{ + graph.values[graph.current & GRAPH_MASK] = value; + graph.colors[graph.current & GRAPH_MASK] = color; + graph.current++; +} + +/* +============== +SCR_DrawDebugGraph +============== +*/ +static void SCR_DrawDebugGraph(void) +{ + int a, y, w, i, h, height; + float v, scale, shift; + + scale = scr_graphscale->value; + shift = scr_graphshift->value; + height = scr_graphheight->integer; + if (height < 1) + return; + + w = scr.hud_width; + y = scr.hud_height; + + for (a = 0; a < w; a++) { + i = (graph.current - 1 - a) & GRAPH_MASK; + v = graph.values[i] * scale + shift; + + if (v < 0) + v += height * (1 + (int)(-v / height)); + + h = (int)v % height; + R_DrawFill8(w - 1 - a, y - h, 1, h, graph.colors[i]); + } +} + +/* +=============================================================================== + +DEMO BAR + +=============================================================================== +*/ + static void draw_progress_bar(float progress, bool paused, int framenum) { char buffer[16]; @@ -1331,6 +1444,13 @@ void SCR_Init(void) scr_crosshair = Cvar_Get("crosshair", "0", CVAR_ARCHIVE); scr_crosshair->changed = scr_crosshair_changed; + scr_netgraph = Cvar_Get("netgraph", "0", 0); + scr_timegraph = Cvar_Get("timegraph", "0", 0); + scr_debuggraph = Cvar_Get("debuggraph", "0", 0); + scr_graphheight = Cvar_Get("graphheight", "32", 0); + scr_graphscale = Cvar_Get("graphscale", "1", 0); + scr_graphshift = Cvar_Get("graphshift", "0", 0); + scr_chathud = Cvar_Get("scr_chathud", "0", 0); scr_chathud_lines = Cvar_Get("scr_chathud_lines", "4", 0); scr_chathud_time = Cvar_Get("scr_chathud_time", "0", 0); @@ -2169,6 +2289,12 @@ static void SCR_Draw2D(void) R_ClearColor(); R_SetAlpha(Cvar_ClampValue(scr_alpha, 0, 1)); + if (scr_timegraph->integer) + SCR_DebugGraph(cls.frametime * 300, 0xdc); + + if (scr_debuggraph->integer || scr_timegraph->integer || scr_netgraph->integer) + SCR_DrawDebugGraph(); + SCR_DrawStats(); SCR_DrawLayout(); diff --git a/src/server/game.c b/src/server/game.c index 98bf90cee..5ae558898 100644 --- a/src/server/game.c +++ b/src/server/game.c @@ -767,10 +767,6 @@ static void PF_FreeTags(unsigned tag) Z_FreeTags(tag + TAG_MAX); } -static void PF_DebugGraph(float value, int color) -{ -} - static int PF_LoadFile(const char *path, void **buffer, unsigned flags, unsigned tag) { if (tag > UINT16_MAX - TAG_MAX) { @@ -839,7 +835,7 @@ static const game_import_t game_import = { .args = Cmd_RawArgs, .AddCommandString = PF_AddCommandString, - .DebugGraph = PF_DebugGraph, + .DebugGraph = SCR_DebugGraph, .SetAreaPortalState = PF_SetAreaPortalState, .AreasConnected = PF_AreasConnected, }; From 3dbc62928a07e0b08c0ed6ae78122d9210805d5c Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 6 Nov 2024 20:41:48 +0300 Subject: [PATCH 25/79] Update libcurl to 8.11.0. --- subprojects/libcurl.wrap | 8 ++++---- subprojects/packagefiles/curl/meson.build | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/subprojects/libcurl.wrap b/subprojects/libcurl.wrap index ce2622169..852d11df4 100644 --- a/subprojects/libcurl.wrap +++ b/subprojects/libcurl.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = curl-8.10.1 -source_url = https://curl.se/download/curl-8.10.1.tar.xz -source_filename = curl-8.10.1.tar.xz -source_hash = 73a4b0e99596a09fa5924a4fb7e4b995a85fda0d18a2c02ab9cf134bebce04ee +directory = curl-8.11.0 +source_url = https://curl.se/download/curl-8.11.0.tar.xz +source_filename = curl-8.11.0.tar.xz +source_hash = db59cf0d671ca6e7f5c2c5ec177084a33a79e04c97e71cf183a5cdea235054eb patch_directory = curl [provide] diff --git a/subprojects/packagefiles/curl/meson.build b/subprojects/packagefiles/curl/meson.build index 919fd9c99..1ae8457de 100644 --- a/subprojects/packagefiles/curl/meson.build +++ b/subprojects/packagefiles/curl/meson.build @@ -1,4 +1,4 @@ -project('libcurl', 'c', version: '8.10.1', license: 'bsd') +project('libcurl', 'c', version: '8.11.0', license: 'bsd') src = [ 'lib/vauth/cleartext.c', @@ -39,6 +39,7 @@ src = [ 'lib/vssh/libssh.c', 'lib/vssh/libssh2.c', + 'lib/vssh/curl_path.c', 'lib/vssh/wolfssh.c', 'lib/altsvc.c', @@ -69,7 +70,6 @@ src = [ 'lib/curl_memrchr.c', 'lib/curl_multibyte.c', 'lib/curl_ntlm_core.c', - 'lib/curl_path.c', 'lib/curl_range.c', 'lib/curl_rtmp.c', 'lib/curl_sasl.c', From 745cd746c7b3206a3c91aecbd4199fa0a8f4cb58 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 7 Nov 2024 12:46:20 +0300 Subject: [PATCH 26/79] =?UTF-8?q?Turn=20=E2=80=98PF=5Fcprintf=20to=20a=20n?= =?UTF-8?q?on-client=E2=80=99=20error=20into=20warning.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No reason for this to be an error when other similar conditions are warnings. --- src/server/game.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/game.c b/src/server/game.c index 5ae558898..606f0deba 100644 --- a/src/server/game.c +++ b/src/server/game.c @@ -252,7 +252,8 @@ static void PF_cprintf(edict_t *ent, int level, const char *fmt, ...) clientNum = NUM_FOR_EDICT(ent) - 1; if (clientNum < 0 || clientNum >= sv_maxclients->integer) { - Com_Error(ERR_DROP, "%s to a non-client %d", __func__, clientNum); + Com_WPrintf("%s to a non-client %d\n", __func__, clientNum); + return; } client = svs.client_pool + clientNum; From 289fb23db75f3fbef1e57f86d27fd77d9e7d5958 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 8 Nov 2024 13:13:07 +0300 Subject: [PATCH 27/79] Don't put shadows on alpha surfaces. --- src/refresh/world.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/refresh/world.c b/src/refresh/world.c index 77618b830..a3dec3ee7 100644 --- a/src/refresh/world.c +++ b/src/refresh/world.c @@ -167,7 +167,7 @@ static bool GL_LightPoint_(const vec3_t start, vec3_t color) end[2] = start[2] - 8192; // get base lightpoint from world - BSP_LightPoint(&glr.lightpoint, start, end, bsp->nodes, gl_static.nolm_mask); + BSP_LightPoint(&glr.lightpoint, start, end, bsp->nodes, gl_static.nolm_mask | SURF_TRANS_MASK); // trace to other BSP models for (i = 0, ent = glr.fd.entities; i < glr.fd.num_entities; i++, ent++) { @@ -201,7 +201,7 @@ static bool GL_LightPoint_(const vec3_t start, vec3_t color) } BSP_TransformedLightPoint(&pt, start, end, model->headnode, - gl_static.nolm_mask, ent->origin, angles); + gl_static.nolm_mask | SURF_TRANS_MASK, ent->origin, angles); if (pt.fraction < glr.lightpoint.fraction) glr.lightpoint = pt; From 5813712639136120717f8b486946d2756d7094c6 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 8 Nov 2024 14:14:23 +0300 Subject: [PATCH 28/79] Use separate cvar for skybox texture filter. Skybox textures are typically very low-res, and having them nearest filtered can be undesirable even if the rest of textures are. --- doc/client.asciidoc | 3 +++ src/refresh/texture.c | 21 ++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/doc/client.asciidoc b/doc/client.asciidoc index 03a38437a..b6191fa20 100644 --- a/doc/client.asciidoc +++ b/doc/client.asciidoc @@ -661,6 +661,9 @@ gl_bilerp_pics:: - 1 — enabled for large pictures that don't fit into the scrap - 2 — enabled for all pictures, including the scrap texture itself +gl_bilerp_skies:: + Enables bilinear filtering of skybox textures. Default value is 1 (enabled). + gl_upscale_pcx:: Enables upscaling of PCX images using HQ2x and HQ4x filters. This improves rendering quality when screen scaling is used. Default value is 0. diff --git a/src/refresh/texture.c b/src/refresh/texture.c index 9de68c16e..66d800332 100644 --- a/src/refresh/texture.c +++ b/src/refresh/texture.c @@ -38,6 +38,7 @@ static cvar_t *gl_downsample_skins; static cvar_t *gl_gamma_scale_pics; static cvar_t *gl_bilerp_chars; static cvar_t *gl_bilerp_pics; +static cvar_t *gl_bilerp_skies; static cvar_t *gl_upscale_pcx; static cvar_t *gl_texturemode; static cvar_t *gl_texturebits; @@ -161,6 +162,12 @@ static void gl_bilerp_pics_changed(cvar_t *self) GL_InitRawTexture(); } +static void gl_bilerp_skies_changed(cvar_t *self) +{ + // change all the existing sky texture objects + update_image_params(BIT(IT_SKY)); +} + static void gl_texturebits_changed(cvar_t *self) { if (!(gl_config.caps & QGL_CAP_TEXTURE_BITS)) { @@ -620,9 +627,6 @@ static void GL_SetFilterAndRepeat(imagetype_t type, imageflags_t flags) if (type == IT_WALL || type == IT_SKIN) { qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } else if (type == IT_SKY) { - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { bool nearest; @@ -635,6 +639,8 @@ static void GL_SetFilterAndRepeat(imagetype_t type, imageflags_t flags) nearest = (gl_bilerp_pics->integer == 0 || gl_bilerp_pics->integer == 1); else nearest = (gl_bilerp_pics->integer == 0); + } else if (type == IT_SKY) { + nearest = (gl_bilerp_skies->integer == 0); } else { nearest = false; } @@ -693,8 +699,10 @@ static const byte gl_env_ofs[6][14] = { static void GL_SetCubemapFilterAndRepeat(void) { - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, gl_filter_max); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, gl_filter_max); + GLenum filter = gl_bilerp_skies->integer ? GL_LINEAR : GL_NEAREST; + + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filter); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filter); qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -1232,6 +1240,8 @@ void GL_InitImages(void) gl_bilerp_chars->changed = gl_bilerp_chars_changed; gl_bilerp_pics = Cvar_Get("gl_bilerp_pics", "1", 0); gl_bilerp_pics->changed = gl_bilerp_pics_changed; + gl_bilerp_skies = Cvar_Get("gl_bilerp_skies", "1", 0); + gl_bilerp_skies->changed = gl_bilerp_skies_changed; gl_texturemode = Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE); gl_texturemode->changed = gl_texturemode_changed; gl_texturemode->generator = gl_texturemode_g; @@ -1319,6 +1329,7 @@ void GL_ShutdownImages(void) { gl_bilerp_chars->changed = NULL; gl_bilerp_pics->changed = NULL; + gl_bilerp_skies->changed = NULL; gl_texturemode->changed = NULL; gl_texturemode->generator = NULL; gl_anisotropy->changed = NULL; From 02c1c734b7411cd2463325452478393544e19a5a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 10 Nov 2024 11:57:53 +0300 Subject: [PATCH 29/79] Remove unneeded image type bit. --- src/refresh/texture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/refresh/texture.c b/src/refresh/texture.c index 66d800332..7b734a081 100644 --- a/src/refresh/texture.c +++ b/src/refresh/texture.c @@ -122,7 +122,7 @@ static void gl_texturemode_changed(cvar_t *self) } // change all the existing mipmap texture objects - update_image_params(BIT(IT_WALL) | BIT(IT_SKIN) | BIT(IT_SKY)); + update_image_params(BIT(IT_WALL) | BIT(IT_SKIN)); } static void gl_texturemode_g(genctx_t *ctx) From 4a6841b6cdfb1258f344da9e95e22d5b8819cbce Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 10 Nov 2024 11:59:59 +0300 Subject: [PATCH 30/79] Reset flare visibility if entity disappeared. Prevents previously visible flares from popping up behind the wall. Use 2.5 sec timeout so that flashing flares still work. --- src/refresh/main.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/refresh/main.c b/src/refresh/main.c index 8d57bb81b..7eac9029b 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -450,10 +450,16 @@ static void GL_OccludeFlares(void) } if (q) { - if (q->pending) - continue; - if (com_eventTime - q->timestamp <= 33) - continue; + // reset visibility if entity disappeared + if (com_eventTime - q->timestamp >= 2500) { + q->pending = q->visible = false; + q->frac = 0; + } else { + if (q->pending) + continue; + if (com_eventTime - q->timestamp <= 33) + continue; + } } else { glquery_t new = { 0 }; uint32_t map_size = HashMap_Size(gl_static.queries); From 66f07771f8f788120d1014f01ea6811a11080f5b Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 12 Nov 2024 19:45:01 +0300 Subject: [PATCH 31/79] Scale flares up with distance for occlusion. --- src/refresh/main.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/refresh/main.c b/src/refresh/main.c index 7eac9029b..bd50ad3e5 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -427,6 +427,8 @@ static void GL_OccludeFlares(void) const entity_t *e; glquery_t *q; int i, j; + vec3_t dir, org; + float scale, dist; bool set = false; if (!glr.num_flares) @@ -480,14 +482,19 @@ static void GL_OccludeFlares(void) set = true; } + VectorSubtract(e->origin, glr.fd.vieworg, dir); + dist = DotProduct(dir, glr.viewaxis[0]); + + scale = 2.5f; + if (dist > 20) + scale += dist * 0.004f; + if (bsp && BSP_PointLeaf(bsp->nodes, e->origin)->contents == CONTENTS_SOLID) { - vec3_t dir, org; - VectorSubtract(glr.fd.vieworg, e->origin, dir); VectorNormalize(dir); - VectorMA(e->origin, 5.0f, dir, org); - make_flare_quad(org, 2.5f); + VectorMA(e->origin, -5.0f, dir, org); + make_flare_quad(org, scale); } else - make_flare_quad(e->origin, 2.5f); + make_flare_quad(e->origin, scale); GL_LockArrays(4); qglBeginQuery(gl_static.samples_passed, q->query); From 28163e3bf741838433e0115ee91ccff67d799bd2 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 13 Nov 2024 12:39:16 +0300 Subject: [PATCH 32/79] Fix BFG lightramp lerp with variable FPS. Regression since 9e99f370. --- src/client/entities.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/entities.c b/src/client/entities.c index 76fde71e6..0fc183131 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -993,7 +993,7 @@ static void CL_AddPacketEntities(void) i = 200; } else if (cl.csr.extended) { i = bfg_lightramp[Q_clip(ent.oldframe, 0, 5)] * ent.backlerp + - bfg_lightramp[Q_clip(ent.frame, 0, 5)] * cl.lerpfrac; + bfg_lightramp[Q_clip(ent.frame, 0, 5)] * (1.0f - ent.backlerp); } else { i = bfg_lightramp[Q_clip(s1->frame, 0, 5)]; } From b57e6e3ec27a2e172ea3defc2f6a98319c9eb8a4 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 13 Nov 2024 17:31:26 +0300 Subject: [PATCH 33/79] Add N64 electric sparks effect. Partially based on: https://github.com/Paril/q2pro/commit/fd255ef16b965cc9e839c5d7bf0c45daa241aafd --- inc/shared/shared.h | 1 + src/client/tent.c | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/inc/shared/shared.h b/inc/shared/shared.h index 676cb40e5..1fbb2fff4 100644 --- a/inc/shared/shared.h +++ b/inc/shared/shared.h @@ -1265,6 +1265,7 @@ enum { SPLASH_SLIME, SPLASH_LAVA, SPLASH_BLOOD, + SPLASH_ELECTRIC_N64, // KEX }; // sound channels diff --git a/src/client/tent.c b/src/client/tent.c index 17128a6db..126d36ca2 100644 --- a/src/client/tent.c +++ b/src/client/tent.c @@ -1285,11 +1285,17 @@ void CL_ParseTEnt(void) break; case TE_SPLASH: // bullet hitting water - if (te.color < 0 || te.color > 6) - r = 0x00; - else - r = splash_color[te.color]; - CL_ParticleEffect(te.pos1, te.dir, r, te.count); + if (cl.csr.extended && te.color == SPLASH_ELECTRIC_N64) { + CL_ParticleEffect(te.pos1, te.dir, 0x6c, te.count / 2); + CL_ParticleEffect(te.pos1, te.dir, 0xb0, (te.count + 1) / 2); + te.color = SPLASH_SPARKS; + } else { + if (te.color >= q_countof(splash_color)) + r = 0x00; + else + r = splash_color[te.color]; + CL_ParticleEffect(te.pos1, te.dir, r, te.count); + } if (te.color == SPLASH_SPARKS) { r = Q_rand() & 3; From 53f6611cdfede003e3ea99984f26eab5c65310c2 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 17 Nov 2024 16:55:04 +0300 Subject: [PATCH 34/79] Only print 24-bit PCX warning in developer mode. --- inc/common/common.h | 4 ++++ src/refresh/images.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/inc/common/common.h b/inc/common/common.h index cb4d9c268..0cf98a78c 100644 --- a/inc/common/common.h +++ b/inc/common/common.h @@ -136,11 +136,15 @@ void Sys_Printf(const char *fmt, ...) q_printf(1, 2); #define Com_DDDDPrintf(...) \ do { if (developer && developer->integer > 3) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) +#define Com_DWPrintf(...) \ + do { if (developer && developer->integer > 0) \ + Com_LPrintf(PRINT_WARNING, __VA_ARGS__); } while (0) #else #define Com_DPrintf(...) ((void)0) #define Com_DDPrintf(...) ((void)0) #define Com_DDDPrintf(...) ((void)0) #define Com_DDDDPrintf(...) ((void)0) +#define Com_DWPrintf(...) ((void)0 #endif #if USE_TESTS diff --git a/src/refresh/images.c b/src/refresh/images.c index 86849fe31..90986c36a 100644 --- a/src/refresh/images.c +++ b/src/refresh/images.c @@ -309,7 +309,7 @@ static int load_pcx(const byte *rawdata, size_t rawlen, IMG_FreePixels(pixels); } else { - Com_WPrintf("%s is a 24-bit PCX file. This is not portable.\n", image->name); + Com_DWPrintf("%s is a 24-bit PCX file. This is not portable.\n", image->name); *pic = pixels; image->flags |= IF_OPAQUE; } From e4bbc21f12295e4684724fd33121d753cbf8b889 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 18 Nov 2024 20:50:56 +0300 Subject: [PATCH 35/79] Fix broken maps that set skyrotate but not skyaxis. --- src/refresh/sky.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/refresh/sky.c b/src/refresh/sky.c index b90236b9d..d603d9531 100644 --- a/src/refresh/sky.c +++ b/src/refresh/sky.c @@ -450,14 +450,20 @@ void R_SetSky(const char *name, float rotate, bool autorotate, const vec3_t axis return; } + Com_DDPrintf("%s: %s %.1f %d (%.1f %.1f %.1f)\n", __func__, + name, rotate, autorotate, axis[0], axis[1], axis[2]); + + if (VectorEmpty(axis)) + rotate = 0; + if (!rotate) + autorotate = false; + skyrotate = rotate; skyautorotate = autorotate; VectorNormalize2(axis, skyaxis); SetupRotationMatrix(skymatrix, skyaxis, skyrotate); if (gl_static.use_cubemaps) TransposeAxis(skymatrix); - if (!skyrotate) - skyautorotate = false; // try to load cubemap image first if (gl_static.use_cubemaps) { From 58f44eb0f5d0db963d02bf391c75557a4de7ef63 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 18 Nov 2024 20:52:21 +0300 Subject: [PATCH 36/79] Only call GL_InitFramebuffers() if using shaders. --- src/refresh/main.c | 27 ++++++++++++++++----------- src/refresh/texture.c | 2 ++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/refresh/main.c b/src/refresh/main.c index bd50ad3e5..ae10800c2 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -735,18 +735,23 @@ void R_RenderFrame(const refdef_t *fd) lm.dirty = false; } - bool waterwarp = (glr.fd.rdflags & RDF_UNDERWATER) && gl_static.use_shaders && gl_waterwarp->integer; - bool bloom = !(glr.fd.rdflags & RDF_NOWORLDMODEL) && gl_static.use_shaders && gl_bloom->integer; - - if (waterwarp || bloom || gl_bloom->modified) { - if (glr.fd.width != glr.framebuffer_width || glr.fd.height != glr.framebuffer_height || gl_bloom->modified) { - glr.framebuffer_ok = GL_InitFramebuffers(); - glr.framebuffer_width = glr.fd.width; - glr.framebuffer_height = glr.fd.height; - gl_bloom->modified = false; + bool waterwarp = false; + bool bloom = false; + + if (gl_static.use_shaders) { + waterwarp = (glr.fd.rdflags & RDF_UNDERWATER) && gl_waterwarp->integer; + bloom = !(glr.fd.rdflags & RDF_NOWORLDMODEL) && gl_bloom->integer; + + if (waterwarp || bloom || gl_bloom->modified) { + if (glr.fd.width != glr.framebuffer_width || glr.fd.height != glr.framebuffer_height || gl_bloom->modified) { + glr.framebuffer_ok = GL_InitFramebuffers(); + glr.framebuffer_width = glr.fd.width; + glr.framebuffer_height = glr.fd.height; + gl_bloom->modified = false; + } + if (!glr.framebuffer_ok) + waterwarp = bloom = false; } - if (!glr.framebuffer_ok) - waterwarp = bloom = false; } if (waterwarp || bloom) { diff --git a/src/refresh/texture.c b/src/refresh/texture.c index 7b734a081..72e82cbb6 100644 --- a/src/refresh/texture.c +++ b/src/refresh/texture.c @@ -1168,6 +1168,8 @@ static void GL_InitPostProcTexture(int w, int h) bool GL_InitFramebuffers(void) { + Q_assert(gl_static.use_shaders); + GL_ClearErrors(); GL_ForceTexture(TMU_TEXTURE, TEXNUM_PP_SCENE); From cf26bcf49005be477f0e87fa8f1e5e8579997882 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 18 Nov 2024 22:18:16 +0300 Subject: [PATCH 37/79] Allow arbitrary length image extensions. Don't just assume 3 chars. Allows some broken maps to work. --- src/refresh/images.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/refresh/images.c b/src/refresh/images.c index 90986c36a..fef5274d3 100644 --- a/src/refresh/images.c +++ b/src/refresh/images.c @@ -1919,21 +1919,23 @@ static image_t *find_or_load_image(const char *name, size_t len, image_t *image; byte *pic; unsigned hash; + size_t baselen; imageformat_t fmt; int ret; Q_assert(len < MAX_QPATH); + baselen = COM_FileExtension(name) - name; // must have an extension and at least 1 char of base name - if (len <= 4 || name[len - 4] != '.') { + if (baselen < 1 || name[baselen] != '.') { ret = Q_ERR_INVALID_PATH; goto fail; } - hash = FS_HashPathLen(name, len - 4, RIMAGES_HASH); + hash = FS_HashPathLen(name, baselen, RIMAGES_HASH); // look for it - if ((image = lookup_image(name, type, hash, len - 4)) != NULL) { + if ((image = lookup_image(name, type, hash, baselen)) != NULL) { image->registration_sequence = r_registration_sequence; if (image->upload_width && image->upload_height) { image->flags |= flags & IF_PERMANENT; @@ -1951,7 +1953,7 @@ static image_t *find_or_load_image(const char *name, size_t len, // fill in some basic info memcpy(image->name, name, len + 1); - image->baselen = len - 4; + image->baselen = baselen; image->type = type; image->flags = flags; image->registration_sequence = r_registration_sequence; From 34d5b4d7aabbbee16277ffd299dcd04f49289665 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 18 Nov 2024 23:54:59 +0300 Subject: [PATCH 38/79] Only print game API warnings in developer mode. --- src/server/game.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/server/game.c b/src/server/game.c index 606f0deba..cb17bb3aa 100644 --- a/src/server/game.c +++ b/src/server/game.c @@ -101,13 +101,13 @@ static void PF_Unicast(edict_t *ent, qboolean reliable) clientNum = NUM_FOR_EDICT(ent) - 1; if (clientNum < 0 || clientNum >= sv_maxclients->integer) { - Com_WPrintf("%s to a non-client %d\n", __func__, clientNum); + Com_DWPrintf("%s to a non-client %d\n", __func__, clientNum); goto clear; } client = svs.client_pool + clientNum; if (client->state <= cs_zombie) { - Com_WPrintf("%s to a free/zombie client %d\n", __func__, clientNum); + Com_DWPrintf("%s to a free/zombie client %d\n", __func__, clientNum); goto clear; } @@ -162,7 +162,7 @@ static void PF_bprintf(int level, const char *fmt, ...) va_end(argptr); if (len >= sizeof(string)) { - Com_WPrintf("%s: overflow\n", __func__); + Com_DWPrintf("%s: overflow\n", __func__); return; } @@ -241,7 +241,7 @@ static void PF_cprintf(edict_t *ent, int level, const char *fmt, ...) va_end(argptr); if (len >= sizeof(msg)) { - Com_WPrintf("%s: overflow\n", __func__); + Com_DWPrintf("%s: overflow\n", __func__); return; } @@ -252,13 +252,13 @@ static void PF_cprintf(edict_t *ent, int level, const char *fmt, ...) clientNum = NUM_FOR_EDICT(ent) - 1; if (clientNum < 0 || clientNum >= sv_maxclients->integer) { - Com_WPrintf("%s to a non-client %d\n", __func__, clientNum); + Com_DWPrintf("%s to a non-client %d\n", __func__, clientNum); return; } client = svs.client_pool + clientNum; if (client->state <= cs_zombie) { - Com_WPrintf("%s to a free/zombie client %d\n", __func__, clientNum); + Com_DWPrintf("%s to a free/zombie client %d\n", __func__, clientNum); return; } @@ -296,7 +296,7 @@ static void PF_centerprintf(edict_t *ent, const char *fmt, ...) n = NUM_FOR_EDICT(ent); if (n < 1 || n > sv_maxclients->integer) { - Com_WPrintf("%s to a non-client %d\n", __func__, n - 1); + Com_DWPrintf("%s to a non-client %d\n", __func__, n - 1); return; } @@ -305,7 +305,7 @@ static void PF_centerprintf(edict_t *ent, const char *fmt, ...) va_end(argptr); if (len >= sizeof(msg)) { - Com_WPrintf("%s: overflow\n", __func__); + Com_DWPrintf("%s: overflow\n", __func__); return; } @@ -375,7 +375,7 @@ static void PF_configstring(int index, const char *val) Com_Error(ERR_DROP, "%s: bad index: %d", __func__, index); if (sv.state == ss_dead) { - Com_WPrintf("%s: not yet initialized\n", __func__); + Com_DWPrintf("%s: not yet initialized\n", __func__); return; } @@ -394,7 +394,7 @@ static void PF_configstring(int index, const char *val) // print a warning and truncate everything else maxlen = Com_ConfigstringSize(&svs.csr, index); if (len >= maxlen) { - Com_WPrintf( + Com_DWPrintf( "%s: index %d overflowed: %zu > %zu\n", __func__, index, len, maxlen - 1); len = maxlen - 1; From da602cf8c5fb3b28efa9606e47737f7273a87044 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 19 Nov 2024 01:21:45 +0300 Subject: [PATCH 39/79] Mask vwep index with 255 in extended mode. More data may be packed after it in the future. --- src/client/entities.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/entities.c b/src/client/entities.c index 0fc183131..0df4c2bfd 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -874,6 +874,8 @@ static void CL_AddPacketEntities(void) // custom weapon ci = &cl.clientinfo[s1->skinnum & 0xff]; i = (s1->skinnum >> 8); // 0 is default weapon model + if (cl.csr.extended) + i &= 0xff; if (i < 0 || i > cl.numWeaponModels - 1) i = 0; ent.model = ci->weaponmodel[i]; From ade82fda54caa92103d7542cad77f948e6f3201c Mon Sep 17 00:00:00 2001 From: Karolis Stasaitis Date: Tue, 19 Nov 2024 00:09:04 +0100 Subject: [PATCH 40/79] Fix misrendered window icon --- src/unix/video/sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unix/video/sdl.c b/src/unix/video/sdl.c index 954120ebf..d27c4bda6 100644 --- a/src/unix/video/sdl.c +++ b/src/unix/video/sdl.c @@ -303,8 +303,8 @@ static bool init(void) SDL_SetWindowMinimumSize(sdl.window, 320, 240); - SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(q2icon_bits, q2icon_width, q2icon_height, - 1, q2icon_width / 8, 0, 0, 0, 0); + SDL_Surface *icon = SDL_CreateRGBSurfaceWithFormatFrom(q2icon_bits, q2icon_width, q2icon_height, + 1, q2icon_width / 8, SDL_PIXELFORMAT_INDEX1LSB); if (icon) { SDL_Color colors[2] = { { 255, 255, 255 }, From 0b790d74f2620cd0b7f0b528eb9684b28725b119 Mon Sep 17 00:00:00 2001 From: Karolis Stasaitis Date: Tue, 19 Nov 2024 23:38:17 +0100 Subject: [PATCH 41/79] Add missing parens for Com_DWPrintf Ran into this when trying to make a release build from ade82fda --- inc/common/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/common/common.h b/inc/common/common.h index 0cf98a78c..d30d7e2dd 100644 --- a/inc/common/common.h +++ b/inc/common/common.h @@ -144,7 +144,7 @@ void Sys_Printf(const char *fmt, ...) q_printf(1, 2); #define Com_DDPrintf(...) ((void)0) #define Com_DDDPrintf(...) ((void)0) #define Com_DDDDPrintf(...) ((void)0) -#define Com_DWPrintf(...) ((void)0 +#define Com_DWPrintf(...) ((void)0) #endif #if USE_TESTS From 40ec20db11a06939077d1903b4b7c9809b5262ee Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 20 Nov 2024 22:44:46 +0300 Subject: [PATCH 42/79] Add clipmask parameter to new pmove trace function. Allows ladders to work more reliably in some re-release maps. --- inc/shared/game.h | 2 +- inc/shared/shared.h | 3 ++- src/client/predict.c | 4 ++-- src/common/pmove/new.c | 2 ++ src/common/pmove/old.c | 2 ++ src/common/pmove/template.c | 19 ++++++++++--------- src/game/p_client.c | 21 +++++++++++++++------ src/server/mvd/game.c | 2 +- 8 files changed, 35 insertions(+), 20 deletions(-) diff --git a/inc/shared/game.h b/inc/shared/game.h index b8390e0fb..3c18090de 100644 --- a/inc/shared/game.h +++ b/inc/shared/game.h @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // #define GAME_API_VERSION_OLD 3 // game uses gclient_old_t -#define GAME_API_VERSION_NEW 3301 // game uses gclient_new_t +#define GAME_API_VERSION_NEW 3302 // game uses gclient_new_t #if USE_NEW_GAME_API #define GAME_API_VERSION GAME_API_VERSION_NEW diff --git a/inc/shared/shared.h b/inc/shared/shared.h index 1fbb2fff4..7ee252965 100644 --- a/inc/shared/shared.h +++ b/inc/shared/shared.h @@ -1016,11 +1016,12 @@ typedef struct { vec3_t mins, maxs; // bounding box size struct edict_s *groundentity; + cplane_t groundplane; int watertype; int waterlevel; // callbacks to test the world - trace_t (* q_gameabi trace)(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end); + trace_t (* q_gameabi trace)(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int contentmask); int (*pointcontents)(const vec3_t point); } pmove_new_t; #endif diff --git a/src/client/predict.c b/src/client/predict.c index 96522653a..c295e7b61 100644 --- a/src/client/predict.c +++ b/src/client/predict.c @@ -129,10 +129,10 @@ void CL_Trace(trace_t *tr, const vec3_t start, const vec3_t end, const vec3_t mi static int pm_clipmask; -static trace_t q_gameabi CL_PMTrace(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end) +static trace_t q_gameabi CL_PMTrace(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int contentmask) { trace_t t; - CL_Trace(&t, start, end, mins, maxs, pm_clipmask); + CL_Trace(&t, start, end, mins, maxs, (cl.csr.extended && contentmask) ? contentmask : pm_clipmask); return t; } diff --git a/src/common/pmove/new.c b/src/common/pmove/new.c index a47f3f4c2..58b2ba75f 100644 --- a/src/common/pmove/new.c +++ b/src/common/pmove/new.c @@ -24,4 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PMOVE_FUNC PmoveNew #define PMOVE_TIME_SHIFT pmp->time_shift #define PMOVE_C2S(x) SignExtend(COORD2SHORT(x), pmp->coord_bits) +#define PMOVE_TRACE(start, mins, maxs, end) pm->trace(start, mins, maxs, end, 0) +#define PMOVE_TRACE_MASK(start, mins, maxs, end, mask) pm->trace(start, mins, maxs, end, mask) #include "template.c" diff --git a/src/common/pmove/old.c b/src/common/pmove/old.c index 6060e7ef1..75cc92589 100644 --- a/src/common/pmove/old.c +++ b/src/common/pmove/old.c @@ -24,4 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PMOVE_FUNC PmoveOld #define PMOVE_TIME_SHIFT 3 #define PMOVE_C2S(x) COORD2SHORT(x) +#define PMOVE_TRACE(start, mins, maxs, end) pm->trace(start, mins, maxs, end) +#define PMOVE_TRACE_MASK(start, mins, maxs, end, mask) pm->trace(start, mins, maxs, end) #include "template.c" diff --git a/src/common/pmove/template.c b/src/common/pmove/template.c index 1f785135b..c4726e32a 100644 --- a/src/common/pmove/template.c +++ b/src/common/pmove/template.c @@ -30,7 +30,6 @@ typedef struct { float frametime; csurface_t *groundsurface; - cplane_t groundplane; int groundcontents; int previous_origin[3]; @@ -117,7 +116,7 @@ static void PM_StepSlideMove_(void) for (i = 0; i < 3; i++) end[i] = pml.origin[i] + time_left * pml.velocity[i]; - trace = pm->trace(pml.origin, pm->mins, pm->maxs, end); + trace = PMOVE_TRACE(pml.origin, pm->mins, pm->maxs, end); if (trace.allsolid) { // entity is trapped in another solid @@ -218,7 +217,7 @@ static void PM_StepSlideMove(void) VectorCopy(start_o, up); up[2] += STEPSIZE; - trace = pm->trace(up, pm->mins, pm->maxs, up); + trace = PMOVE_TRACE(up, pm->mins, pm->maxs, up); if (trace.allsolid) return; // can't step up @@ -231,7 +230,7 @@ static void PM_StepSlideMove(void) // push down the final amount VectorCopy(pml.origin, down); down[2] -= STEPSIZE; - trace = pm->trace(pml.origin, pm->mins, pm->maxs, down); + trace = PMOVE_TRACE(pml.origin, pm->mins, pm->maxs, down); if (!trace.allsolid) VectorCopy(trace.endpos, pml.origin); @@ -568,8 +567,10 @@ static void PM_CategorizePosition(void) pm->s.pm_flags &= ~PMF_ON_GROUND; pm->groundentity = NULL; } else { - trace = pm->trace(pml.origin, pm->mins, pm->maxs, point); - pml.groundplane = trace.plane; + trace = PMOVE_TRACE(pml.origin, pm->mins, pm->maxs, point); +#ifdef PMOVE_NEW + pm->groundplane = trace.plane; +#endif pml.groundsurface = trace.surface; pml.groundcontents = trace.contents; @@ -715,7 +716,7 @@ static void PM_CheckSpecialMovement(void) VectorNormalize(flatforward); VectorMA(pml.origin, 1, flatforward, spot); - trace = pm->trace(pml.origin, pm->mins, pm->maxs, spot); + trace = PMOVE_TRACE_MASK(pml.origin, pm->mins, pm->maxs, spot, CONTENTS_LADDER); if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER)) pml.ladder = true; @@ -855,7 +856,7 @@ static void PM_CheckDuck(void) if (pm->s.pm_flags & PMF_DUCKED) { // try to stand up pm->maxs[2] = 32; - trace = pm->trace(pml.origin, pm->mins, pm->maxs, pml.origin); + trace = PMOVE_TRACE(pml.origin, pm->mins, pm->maxs, pml.origin); if (!trace.allsolid) pm->s.pm_flags &= ~PMF_DUCKED; } @@ -904,7 +905,7 @@ static bool PM_GoodPosition(void) for (i = 0; i < 3; i++) origin[i] = end[i] = pm->s.origin[i] * 0.125f; - trace = pm->trace(origin, pm->mins, pm->maxs, end); + trace = PMOVE_TRACE(origin, pm->mins, pm->maxs, end); return !trace.allsolid; } diff --git a/src/game/p_client.c b/src/game/p_client.c index b55a4e95c..50f6d79fb 100644 --- a/src/game/p_client.c +++ b/src/game/p_client.c @@ -1488,15 +1488,20 @@ void ClientDisconnect(edict_t *ent) //============================================================== edict_t *pm_passent; +int pm_clipmask; // pmove doesn't need to know about passent and contentmask +#if USE_NEW_GAME_API +trace_t q_gameabi PM_trace(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int contentmask) +{ + return gi.trace(start, mins, maxs, end, pm_passent, (game.csr.extended && contentmask) ? contentmask : pm_clipmask); +} +#else trace_t q_gameabi PM_trace(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end) { - if (pm_passent->health > 0) - return gi.trace(start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID); - else - return gi.trace(start, mins, maxs, end, pm_passent, MASK_DEADSOLID); + return gi.trace(start, mins, maxs, end, pm_passent, pm_clipmask); } +#endif /* ============== @@ -1525,8 +1530,6 @@ void ClientThink(edict_t *ent, usercmd_t *ucmd) return; } - pm_passent = ent; - if (ent->client->chase_target) { client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); @@ -1547,6 +1550,12 @@ void ClientThink(edict_t *ent, usercmd_t *ucmd) else client->ps.pmove.pm_type = PM_NORMAL; + pm_passent = ent; + if (ent->health > 0) + pm_clipmask = MASK_PLAYERSOLID; + else + pm_clipmask = MASK_DEADSOLID; + client->ps.pmove.gravity = sv_gravity->value; pm.s = client->ps.pmove; diff --git a/src/server/mvd/game.c b/src/server/mvd/game.c index 509c4a6ab..c50915e71 100644 --- a/src/server/mvd/game.c +++ b/src/server/mvd/game.c @@ -2080,7 +2080,7 @@ static mvd_player_t *MVD_HitPlayer(mvd_client_t *client) return target; } -static trace_t q_gameabi MVD_Trace(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end) +static trace_t q_gameabi MVD_Trace(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int contentmask) { trace_t trace; From cd3f69e66b83931c2c34841099784fb0e91d7dad Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 21 Nov 2024 13:57:02 +0300 Subject: [PATCH 43/79] Update comments. --- inc/shared/game.h | 4 ++-- src/server/server.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/inc/shared/game.h b/inc/shared/game.h index 3c18090de..8f65205a5 100644 --- a/inc/shared/game.h +++ b/inc/shared/game.h @@ -24,8 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., // game.h -- game dll information visible to server // -#define GAME_API_VERSION_OLD 3 // game uses gclient_old_t -#define GAME_API_VERSION_NEW 3302 // game uses gclient_new_t +#define GAME_API_VERSION_OLD 3 // game uses gclient_old_t, pmove_old_t +#define GAME_API_VERSION_NEW 3302 // game uses gclient_new_t, pmove_new_t #if USE_NEW_GAME_API #define GAME_API_VERSION GAME_API_VERSION_NEW diff --git a/src/server/server.h b/src/server/server.h index 819c420fb..0520560ac 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -93,7 +93,7 @@ with this program; if not, write to the Free Software Foundation, Inc., GMF_IPV6_ADDRESS_AWARE | GMF_ALLOW_INDEX_OVERFLOW | \ GMF_PROTOCOL_EXTENSIONS) -// flag indicating if game uses gclient_new_t or gclient_old_t. +// flag indicating if game uses new versions of gclient_t and pmove_t. // doesn't enable protocol extensions by itself. #define IS_NEW_GAME_API (ge->apiversion == GAME_API_VERSION_NEW) From 2872f781197cf032c65c0711e1372b644e9b6476 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 22 Nov 2024 19:02:39 +0300 Subject: [PATCH 44/79] Merge brush contents into solid leaf contents. Fixes annoying issue with CONTENTS_LADDER tracing not working as expected, because solid leafs only have CONTENTS_SOLID bit. To avoid regressions, only do this in extended protocol mode. --- inc/common/bsp.h | 2 +- inc/common/cmodel.h | 13 ++++++++----- src/client/entities.c | 2 +- src/client/locs.c | 4 ++-- src/client/predict.c | 13 ++++++------- src/common/bsp.c | 13 +++++++++++++ src/common/bsp_template.c | 4 ++-- src/common/cmodel.c | 39 ++++++++++++++++++++++++--------------- src/refresh/main.c | 2 +- src/refresh/world.c | 7 ++++--- src/server/mvd/game.c | 7 ++++--- src/server/world.c | 15 +++++++++------ 12 files changed, 75 insertions(+), 46 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index c0cbb6684..ae30a218a 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -158,7 +158,7 @@ typedef struct { struct mnode_s *parent; /* <====== */ - int contents; + int contents[2]; // 0 - original, 1 - merged int cluster; int area; int numleafbrushes; diff --git a/inc/common/cmodel.h b/inc/common/cmodel.h index f97433b53..c237c4737 100644 --- a/inc/common/cmodel.h +++ b/inc/common/cmodel.h @@ -54,25 +54,28 @@ const mleaf_t *CM_LeafNum(const cm_t *cm, int number); const mnode_t *CM_HeadnodeForBox(const vec3_t mins, const vec3_t maxs); // returns an ORed contents mask -static inline int CM_PointContents(const vec3_t p, const mnode_t *headnode) +static inline int CM_PointContents(const vec3_t p, const mnode_t *headnode, bool extended) { if (!headnode) return 0; // map not loaded - return BSP_PointLeaf(headnode, p)->contents; + return BSP_PointLeaf(headnode, p)->contents[extended]; } int CM_TransformedPointContents(const vec3_t p, const mnode_t *headnode, - const vec3_t origin, const vec3_t angles); + const vec3_t origin, const vec3_t angles, + bool extended); void CM_BoxTrace(trace_t *trace, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, - const mnode_t *headnode, int brushmask); + const mnode_t *headnode, int brushmask, + bool extended); void CM_TransformedBoxTrace(trace_t *trace, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, const mnode_t *headnode, int brushmask, - const vec3_t origin, const vec3_t angles); + const vec3_t origin, const vec3_t angles, + bool extended); void CM_ClipEntity(trace_t *dst, const trace_t *src, struct edict_s *ent); // call with topnode set to the headnode, returns with topnode diff --git a/src/client/entities.c b/src/client/entities.c index 0df4c2bfd..ac8b02c79 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -1275,7 +1275,7 @@ static void CL_SetupThirdPersionView(void) VectorMA(cl.refdef.vieworg, -range * rscale, cl.v_right, cl.refdef.vieworg); CM_BoxTrace(&trace, cl.playerEntityOrigin, cl.refdef.vieworg, - mins, maxs, cl.bsp->nodes, MASK_SOLID); + mins, maxs, cl.bsp->nodes, MASK_SOLID, cl.csr.extended); if (trace.fraction != 1.0f) { VectorCopy(trace.endpos, cl.refdef.vieworg); } diff --git a/src/client/locs.c b/src/client/locs.c index c5f1c9e97..bfeba459e 100644 --- a/src/client/locs.c +++ b/src/client/locs.c @@ -151,7 +151,7 @@ static location_t *LOC_FindClosest(const vec3_t pos) if (loc_trace->integer) { CM_BoxTrace(&trace, pos, loc->origin, vec3_origin, vec3_origin, - cl.bsp->nodes, MASK_SOLID); + cl.bsp->nodes, MASK_SOLID, cl.csr.extended); if (trace.fraction != 1.0f) { continue; } @@ -239,7 +239,7 @@ static size_t LOC_There_m(char *buffer, size_t size) VectorMA(cl.playerEntityOrigin, 8192, cl.v_forward, pos); CM_BoxTrace(&trace, cl.playerEntityOrigin, pos, vec3_origin, - vec3_origin, cl.bsp->nodes, MASK_SOLID); + vec3_origin, cl.bsp->nodes, MASK_SOLID, cl.csr.extended); loc = LOC_FindClosest(trace.endpos); } diff --git a/src/client/predict.c b/src/client/predict.c index c295e7b61..e3757fab6 100644 --- a/src/client/predict.c +++ b/src/client/predict.c @@ -104,7 +104,8 @@ static void CL_ClipMoveToEntities(trace_t *tr, const vec3_t start, const vec3_t CM_TransformedBoxTrace(&trace, start, end, mins, maxs, headnode, contentmask, - ent->current.origin, ent->current.angles); + ent->current.origin, ent->current.angles, + cl.csr.extended); CM_ClipEntity(tr, &trace, (struct edict_s *)ent); } @@ -118,7 +119,7 @@ CL_Trace void CL_Trace(trace_t *tr, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, int contentmask) { // check against world - CM_BoxTrace(tr, start, end, mins, maxs, cl.bsp->nodes, contentmask); + CM_BoxTrace(tr, start, end, mins, maxs, cl.bsp->nodes, contentmask, cl.csr.extended); tr->ent = (struct edict_s *)cl_entities; if (tr->fraction == 0) return; // blocked by the world @@ -142,7 +143,7 @@ static int CL_PointContents(const vec3_t point) const mmodel_t *cmodel; int i, contents; - contents = CM_PointContents(point, cl.bsp->nodes); + contents = CM_PointContents(point, cl.bsp->nodes, cl.csr.extended); for (i = 0; i < cl.numSolidEntities; i++) { ent = cl.solidEntities[i]; @@ -154,10 +155,8 @@ static int CL_PointContents(const vec3_t point) if (!cmodel) continue; - contents |= CM_TransformedPointContents( - point, cmodel->headnode, - ent->current.origin, - ent->current.angles); + contents |= CM_TransformedPointContents(point, cmodel->headnode, ent->current.origin, + ent->current.angles, cl.csr.extended); } return contents; diff --git a/src/common/bsp.c b/src/common/bsp.c index 50cd8783b..37fac4721 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -723,6 +723,17 @@ static size_t BSP_ParseExtensionHeader(bsp_t *bsp, lump_t *out, const byte *buf, #endif +// remaster needs ORed contents from all brushes for solid leafs +static void BSP_MergeLeafContents(bsp_t *bsp) +{ + mleaf_t *leaf; + int i, j; + + for (i = 1, leaf = bsp->leafs + i; i < bsp->numleafs; i++, leaf++) + for (j = 0; j < leaf->numleafbrushes; j++) + leaf->contents[1] |= leaf->firstleafbrush[j]->contents; +} + /* ================== BSP_Load @@ -863,6 +874,8 @@ int BSP_Load(const char *name, bsp_t **bsp_p) } #endif + BSP_MergeLeafContents(bsp); + Hunk_End(&bsp->hunk); List_Append(&bsp_cache, &bsp->entry); diff --git a/src/common/bsp_template.c b/src/common/bsp_template.c index 9554f7233..6e0a68716 100644 --- a/src/common/bsp_template.c +++ b/src/common/bsp_template.c @@ -455,7 +455,7 @@ BSP_LOAD(Leafs) for (int i = 0; i < count; i++, out++) { out->plane = NULL; - out->contents = BSP_Long(); + out->contents[0] = out->contents[1] = BSP_Long(); uint32_t cluster = BSP_ExtLong(); if (cluster == BSP_ExtNull) { @@ -496,7 +496,7 @@ BSP_LOAD(Leafs) out->numleafbrushes = numleafbrushes; } - BSP_ENSURE(bsp->leafs[0].contents == CONTENTS_SOLID, "Map leaf 0 is not CONTENTS_SOLID"); + BSP_ENSURE(bsp->leafs[0].contents[0] == CONTENTS_SOLID, "Map leaf 0 is not CONTENTS_SOLID"); return Q_ERR_SUCCESS; } diff --git a/src/common/cmodel.c b/src/common/cmodel.c index 7cdf47294..aa5cf9d3a 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -270,7 +270,7 @@ static void CM_InitBoxHull(void) box_brush.firstbrushside = &box_brushsides[0]; box_brush.contents = CONTENTS_MONSTER; - box_leaf.contents = CONTENTS_MONSTER; + box_leaf.contents[0] = box_leaf.contents[1] = CONTENTS_MONSTER; box_leaf.firstleafbrush = &box_leafbrush; box_leaf.numleafbrushes = 1; @@ -395,7 +395,8 @@ rotating entities ================== */ int CM_TransformedPointContents(const vec3_t p, const mnode_t *headnode, - const vec3_t origin, const vec3_t angles) + const vec3_t origin, const vec3_t angles, + bool extended) { vec3_t p_l; vec3_t axis[3]; @@ -412,7 +413,7 @@ int CM_TransformedPointContents(const vec3_t p, const mnode_t *headnode, RotatePoint(p_l, axis); } - return BSP_PointLeaf(headnode, p_l)->contents; + return BSP_PointLeaf(headnode, p_l)->contents[extended]; } /* @@ -433,6 +434,7 @@ static vec3_t trace_extents; static trace_t *trace_trace; static int trace_contents; static bool trace_ispoint; // optimized case +static bool trace_extended; // remaster fixes /* ================ @@ -513,7 +515,7 @@ static void CM_ClipBoxToBrush(const vec3_t p1, const vec3_t p2, trace_t *trace, trace->startsolid = true; if (!getout) { trace->allsolid = true; - if (!map_allsolid_bug->integer) { + if (trace_extended || !map_allsolid_bug->integer) { // original Q2 didn't set these trace->fraction = 0; trace->contents = brush->contents; @@ -582,7 +584,7 @@ static void CM_TraceToLeaf(const mleaf_t *leaf) int k; mbrush_t *b, **leafbrush; - if (!(leaf->contents & trace_contents)) + if (!(leaf->contents[trace_extended] & trace_contents)) return; // trace line against all brushes in the leaf leafbrush = leaf->firstleafbrush; @@ -610,7 +612,7 @@ static void CM_TestInLeaf(const mleaf_t *leaf) int k; mbrush_t *b, **leafbrush; - if (!(leaf->contents & trace_contents)) + if (!(leaf->contents[trace_extended] & trace_contents)) return; // trace line against all brushes in the leaf leafbrush = leaf->firstleafbrush; @@ -727,7 +729,8 @@ CM_BoxTrace void CM_BoxTrace(trace_t *trace, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, - const mnode_t *headnode, int brushmask) + const mnode_t *headnode, int brushmask, + bool extended) { const vec_t *bounds[2] = { mins, maxs }; int i, j; @@ -744,6 +747,7 @@ void CM_BoxTrace(trace_t *trace, return; trace_contents = brushmask; + trace_extended = extended; VectorCopy(start, trace_start); VectorCopy(end, trace_end); for (i = 0; i < 8; i++) @@ -809,7 +813,8 @@ void CM_TransformedBoxTrace(trace_t *trace, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, const mnode_t *headnode, int brushmask, - const vec3_t origin, const vec3_t angles) + const vec3_t origin, const vec3_t angles, + bool extended) { vec3_t start_l, end_l; vec3_t axis[3]; @@ -828,15 +833,19 @@ void CM_TransformedBoxTrace(trace_t *trace, } // sweep the box through the model - CM_BoxTrace(trace, start_l, end_l, mins, maxs, headnode, brushmask); + CM_BoxTrace(trace, start_l, end_l, mins, maxs, headnode, brushmask, extended); - // rotate plane normal into the worlds frame of reference - if (rotated && trace->fraction != 1.0f) { - TransposeAxis(axis); - RotatePoint(trace->plane.normal, axis); - } + if (trace->fraction != 1.0f) { + // rotate plane normal into the worlds frame of reference + if (rotated) { + TransposeAxis(axis); + RotatePoint(trace->plane.normal, axis); + } - // FIXME: offset plane distance? + // offset plane distance + if (extended) + trace->plane.dist += DotProduct(trace->plane.normal, origin); + } LerpVector(start, end, trace->fraction, trace->endpos); } diff --git a/src/refresh/main.c b/src/refresh/main.c index ae10800c2..ae5a97084 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -489,7 +489,7 @@ static void GL_OccludeFlares(void) if (dist > 20) scale += dist * 0.004f; - if (bsp && BSP_PointLeaf(bsp->nodes, e->origin)->contents == CONTENTS_SOLID) { + if (bsp && BSP_PointLeaf(bsp->nodes, e->origin)->contents[0] & CONTENTS_SOLID) { VectorNormalize(dir); VectorMA(e->origin, -5.0f, dir, org); make_flare_quad(org, scale); diff --git a/src/refresh/world.c b/src/refresh/world.c index a3dec3ee7..28711ad3b 100644 --- a/src/refresh/world.c +++ b/src/refresh/world.c @@ -339,12 +339,12 @@ static void GL_MarkLeaves(void) leaf = BSP_PointLeaf(bsp->nodes, glr.fd.vieworg); cluster1 = cluster2 = leaf->cluster; VectorCopy(glr.fd.vieworg, tmp); - if (!leaf->contents) + if (!leaf->contents[0]) tmp[2] -= 16; else tmp[2] += 16; leaf = BSP_PointLeaf(bsp->nodes, tmp); - if (!(leaf->contents & CONTENTS_SOLID)) + if (!(leaf->contents[0] & CONTENTS_SOLID)) cluster2 = leaf->cluster; if (cluster1 == glr.viewcluster1 && cluster2 == glr.viewcluster2) @@ -511,7 +511,8 @@ static inline bool GL_ClipNode(const mnode_t *node, int *clipflags) static inline void GL_DrawLeaf(const mleaf_t *leaf) { - if (leaf->contents == CONTENTS_SOLID) + // FIXME: use `&' here? + if (leaf->contents[0] == CONTENTS_SOLID) return; // solid leaf if (glr.fd.areabits && !Q_IsBitSet(glr.fd.areabits, leaf->area)) diff --git a/src/server/mvd/game.c b/src/server/mvd/game.c index c50915e71..62d5d1bf0 100644 --- a/src/server/mvd/game.c +++ b/src/server/mvd/game.c @@ -665,7 +665,7 @@ static void MVD_UpdateClient(mvd_client_t *client) if (mvd->cm.cache) { vec3_t vieworg; VectorMA(client->ps.viewoffset, 0.125f, client->ps.pmove.origin, vieworg); - contents = CM_PointContents(vieworg, mvd->cm.cache->nodes); + contents = CM_PointContents(vieworg, mvd->cm.cache->nodes, mvd->csr->extended); } if (contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) @@ -2048,7 +2048,7 @@ static mvd_player_t *MVD_HitPlayer(mvd_client_t *client) if (mvd->cm.cache) { CM_BoxTrace(&trace, start, end, vec3_origin, vec3_origin, - mvd->cm.cache->nodes, CONTENTS_SOLID); + mvd->cm.cache->nodes, CONTENTS_SOLID, mvd->csr->extended); fraction = trace.fraction; } else { fraction = 1; @@ -2069,7 +2069,8 @@ static mvd_player_t *MVD_HitPlayer(mvd_client_t *client) CM_TransformedBoxTrace(&trace, start, end, vec3_origin, vec3_origin, CM_HeadnodeForBox(ent->mins, ent->maxs), - CONTENTS_MONSTER, ent->s.origin, vec3_origin); + CONTENTS_MONSTER, ent->s.origin, vec3_origin, + mvd->csr->extended); if (trace.fraction < fraction) { fraction = trace.fraction; diff --git a/src/server/world.c b/src/server/world.c index 2c97c9f55..3964dd815 100644 --- a/src/server/world.c +++ b/src/server/world.c @@ -477,7 +477,7 @@ int SV_PointContents(const vec3_t p) int contents; // get base contents from world - contents = CM_PointContents(p, SV_WorldNodes()); + contents = CM_PointContents(p, SV_WorldNodes(), svs.csr.extended); // or in contents from all the other entities num = SV_AreaEdicts(p, p, touch, q_countof(touch), AREA_SOLID); @@ -487,7 +487,8 @@ int SV_PointContents(const vec3_t p) // might intersect, so do an exact clip contents |= CM_TransformedPointContents(p, SV_HullForEntity(hit, false), - hit->s.origin, hit->s.angles); + hit->s.origin, hit->s.angles, + svs.csr.extended); } return contents; @@ -554,7 +555,8 @@ static void SV_ClipMoveToEntities(trace_t *tr, // might intersect, so do an exact clip CM_TransformedBoxTrace(&trace, start, end, mins, maxs, SV_HullForEntity(touch, false), contentmask, - touch->s.origin, touch->s.angles); + touch->s.origin, touch->s.angles, + svs.csr.extended); CM_ClipEntity(tr, &trace, touch); } @@ -580,7 +582,7 @@ trace_t q_gameabi SV_Trace(const vec3_t start, const vec3_t mins, maxs = vec3_origin; // clip to world - CM_BoxTrace(&trace, start, end, mins, maxs, SV_WorldNodes(), contentmask); + CM_BoxTrace(&trace, start, end, mins, maxs, SV_WorldNodes(), contentmask, svs.csr.extended); trace.ent = ge->edicts; if (trace.fraction == 0) return trace; // blocked by the world @@ -610,11 +612,12 @@ trace_t q_gameabi SV_Clip(const vec3_t start, const vec3_t mins, maxs = vec3_origin; if (clip == ge->edicts) - CM_BoxTrace(&trace, start, end, mins, maxs, SV_WorldNodes(), contentmask); + CM_BoxTrace(&trace, start, end, mins, maxs, SV_WorldNodes(), contentmask, svs.csr.extended); else CM_TransformedBoxTrace(&trace, start, end, mins, maxs, SV_HullForEntity(clip, true), contentmask, - clip->s.origin, clip->s.angles); + clip->s.origin, clip->s.angles, + svs.csr.extended); trace.ent = clip; return trace; } From d7069a57f6879dba7327aecbf1bed5e8e774a645 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 22 Nov 2024 20:39:38 +0300 Subject: [PATCH 45/79] =?UTF-8?q?Remove=20ill-defined=20=E2=80=98map=5Fall?= =?UTF-8?q?solid=5Fbug=E2=80=99=20cvar.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It could cause prediction misses if desynced between client/server. --- src/common/cmodel.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/common/cmodel.c b/src/common/cmodel.c index aa5cf9d3a..5f4303b69 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -37,7 +37,6 @@ static unsigned floodvalid; static unsigned checkcount; static cvar_t *map_noareas; -static cvar_t *map_allsolid_bug; static cvar_t *map_override_path; static void FloodAreaConnections(const cm_t *cm); @@ -515,7 +514,7 @@ static void CM_ClipBoxToBrush(const vec3_t p1, const vec3_t p2, trace_t *trace, trace->startsolid = true; if (!getout) { trace->allsolid = true; - if (trace_extended || !map_allsolid_bug->integer) { + if (trace_extended) { // original Q2 didn't set these trace->fraction = 0; trace->contents = brush->contents; @@ -1129,6 +1128,5 @@ void CM_Init(void) CM_InitBoxHull(); map_noareas = Cvar_Get("map_noareas", "0", 0); - map_allsolid_bug = Cvar_Get("map_allsolid_bug", "1", 0); map_override_path = Cvar_Get("map_override_path", "", 0); } From 6cff2a60dce7150610f62027d2af31bd18fb9092 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 22 Nov 2024 20:44:53 +0300 Subject: [PATCH 46/79] Reduce size of mnode_t/mleaf_t. --- inc/common/bsp.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index ae30a218a..564f3f64a 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -117,21 +117,21 @@ typedef struct mface_s { typedef struct mnode_s { /* ======> */ cplane_t *plane; // never NULL to differentiate from leafs + struct mnode_s *parent; + #if USE_REF vec3_t mins; vec3_t maxs; - unsigned visframe; #endif - struct mnode_s *parent; /* <====== */ - struct mnode_s *children[2]; - #if USE_REF int numfaces; mface_t *firstface; #endif + + struct mnode_s *children[2]; } mnode_t; typedef struct { @@ -149,13 +149,13 @@ typedef struct { typedef struct { /* ======> */ cplane_t *plane; // always NULL to differentiate from nodes + struct mnode_s *parent; + #if USE_REF vec3_t mins; vec3_t maxs; - unsigned visframe; #endif - struct mnode_s *parent; /* <====== */ int contents[2]; // 0 - original, 1 - merged From ca6b7ff0ad9044f83569bcd66035cb5f7038cead Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 22 Nov 2024 23:46:05 +0300 Subject: [PATCH 47/79] =?UTF-8?q?Print=20missing=20models=20with=20?= =?UTF-8?q?=E2=80=98developer=202=E2=80=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- inc/common/common.h | 2 ++ src/refresh/images.c | 4 +--- src/refresh/models.c | 27 +++++++++++++++++++-------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/inc/common/common.h b/inc/common/common.h index d30d7e2dd..8b3025965 100644 --- a/inc/common/common.h +++ b/inc/common/common.h @@ -124,6 +124,7 @@ void Sys_Printf(const char *fmt, ...) q_printf(1, 2); #endif #if USE_DEBUG +#define COM_DEVELOPER (developer->integer) #define Com_DPrintf(...) \ do { if (developer && developer->integer > 0) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) @@ -140,6 +141,7 @@ void Sys_Printf(const char *fmt, ...) q_printf(1, 2); do { if (developer && developer->integer > 0) \ Com_LPrintf(PRINT_WARNING, __VA_ARGS__); } while (0) #else +#define COM_DEVELOPER 0 #define Com_DPrintf(...) ((void)0) #define Com_DDPrintf(...) ((void)0) #define Com_DDDPrintf(...) ((void)0) diff --git a/src/refresh/images.c b/src/refresh/images.c index fef5274d3..e09a2891c 100644 --- a/src/refresh/images.c +++ b/src/refresh/images.c @@ -1802,10 +1802,8 @@ static void print_error(const char *name, imageflags_t flags, int err) // ugly hack for console code if (strcmp(name, "pics/conchars.pcx")) level = PRINT_WARNING; -#if USE_DEBUG - } else if (developer->integer >= 2) { + } else if (COM_DEVELOPER >= 2) { level = PRINT_DEVELOPER; -#endif } else { return; } diff --git a/src/refresh/models.c b/src/refresh/models.c index df05fecbd..764d4bfe0 100644 --- a/src/refresh/models.c +++ b/src/refresh/models.c @@ -727,9 +727,24 @@ static int MOD_LoadMD3(model_t *model, const void *rawdata, size_t length) static void MOD_PrintError(const char *path, int err) { - Com_EPrintf("Couldn't load %s: %s\n", Com_MakePrintable(path), - err == Q_ERR_INVALID_FORMAT ? - Com_GetLastError() : Q_ErrorString(err)); + print_type_t level = PRINT_ERROR; + const char *msg; + + switch (err) { + case Q_ERR_INVALID_FORMAT: + msg = Com_GetLastError(); + break; + case Q_ERR(ENOENT): + if (COM_DEVELOPER < 2) + return; + level = PRINT_DEVELOPER; + // fall through + default: + msg = Q_ErrorString(err); + break; + } + + Com_LPrintf(level, "Couldn't load %s: %s\n", Com_MakePrintable(path), msg); } #if USE_MD5 @@ -1530,12 +1545,8 @@ qhandle_t R_RegisterModel(const char *name) } ret = FS_LoadFile(normalized, (void **)&rawdata); - if (!rawdata) { - // don't spam about missing models - if (ret == Q_ERR(ENOENT)) - return 0; + if (!rawdata) goto fail1; - } if (ret < 4) { ret = Q_ERR_FILE_TOO_SMALL; From 424ff2d822f75f007c74e4d6a120d3e90b50cf72 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 22 Nov 2024 23:48:59 +0300 Subject: [PATCH 48/79] Build some CI targets in release mode. To catch errors like 0b790d74. --- .github/workflows/build.yml | 2 ++ src/client/input.c | 6 ++---- src/client/main.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9eec5fed9..e0815fab1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ env: -Dwrap_mode=nofallback MESON_ARGS_MACOS: >- + -Dbuildtype=release -Dicmp-errors=disabled -Dpkg_config_path=/opt/homebrew/opt/openal-soft/lib/pkgconfig -Dwayland=disabled @@ -37,6 +38,7 @@ env: -Dx11=disabled MESON_ARGS_FREEBSD: >- + -Dbuildtype=release -Dicmp-errors=disabled -Dwindows-crash-dumps=disabled -Dwrap_mode=nofallback diff --git a/src/client/input.c b/src/client/input.c index fb5586615..496082432 100644 --- a/src/client/input.c +++ b/src/client/input.c @@ -956,10 +956,8 @@ CL_SendBatchedCmd */ static void CL_SendBatchedCmd(void) { - int i, j, seq, bits q_unused; - int numCmds, numDups; - int totalCmds, totalMsec; - int cursize q_unused; + int i, j, seq, numCmds, numDups; + q_unused int totalCmds, totalMsec, cursize, bits; usercmd_t *cmd, *oldcmd; client_history_t *history, *oldest; byte *patch; diff --git a/src/client/main.c b/src/client/main.c index 224799b16..f4c1ab3f3 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1746,7 +1746,7 @@ void CL_LoadFilterList(string_entry_t **list, const char *name, const char *comm { string_entry_t *entry, *next; char *raw, *data, *p; - int len, count, line; + int len, count q_unused, line; // free previous entries for (entry = *list; entry; entry = next) { From 18b7a54946d71ebf134cd939f90bd74e62bf6f12 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 23 Nov 2024 14:10:25 +0300 Subject: [PATCH 49/79] Cast zlib malloc size to size_t before multiply. --- src/common/files.c | 2 +- src/server/main.c | 2 +- src/server/mvd/client.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/files.c b/src/common/files.c index 8b6c467cd..9c2ef74e0 100644 --- a/src/common/files.c +++ b/src/common/files.c @@ -973,7 +973,7 @@ static int check_header_coherency(FILE *fp, packfile_t *entry) static voidpf FS_zalloc(voidpf opaque, uInt items, uInt size) { - return FS_Malloc(items * size); + return FS_Malloc((size_t)items * size); } static void FS_zfree(voidpf opaque, voidpf address) diff --git a/src/server/main.c b/src/server/main.c index 07bcd225b..02e9f45b6 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -2152,7 +2152,7 @@ static void sv_hostname_changed(cvar_t *self) #if USE_ZLIB voidpf SV_zalloc(voidpf opaque, uInt items, uInt size) { - return SV_Malloc(items * size); + return SV_Malloc((size_t)items * size); } void SV_zfree(voidpf opaque, voidpf address) diff --git a/src/server/mvd/client.c b/src/server/mvd/client.c index 327a1db3d..7dd80bed6 100644 --- a/src/server/mvd/client.c +++ b/src/server/mvd/client.c @@ -1100,7 +1100,7 @@ static void send_stream_stop(gtv_t *gtv) #if USE_ZLIB static voidpf gtv_zalloc(voidpf opaque, uInt items, uInt size) { - return MVD_Malloc(items * size); + return MVD_Malloc((size_t)items * size); } static void gtv_zfree(voidpf opaque, voidpf address) From 0f4f368a112f55ca82aac41d75b78afa70c7824a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 23 Nov 2024 17:46:09 +0300 Subject: [PATCH 50/79] Support scaling sprites too. --- src/refresh/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/refresh/main.c b/src/refresh/main.c index ae5a97084..b92de5890 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -331,6 +331,7 @@ static void GL_DrawSpriteModel(const model_t *model) const mspriteframe_t *frame = &model->spriteframes[e->frame % model->numframes]; const image_t *image = frame->image; const float alpha = (e->flags & RF_TRANSLUCENT) ? e->alpha : 1.0f; + const float scale = e->scale ? e->scale : 1.0f; glStateBits_t bits = GLS_DEPTHMASK_FALSE | glr.fog_bits; vec3_t up, down, left, right; @@ -353,10 +354,10 @@ static void GL_DrawSpriteModel(const model_t *model) GL_ArrayBits(GLA_VERTEX | GLA_TC); GL_Color(1, 1, 1, alpha); - VectorScale(glr.viewaxis[1], frame->origin_x, left); - VectorScale(glr.viewaxis[1], frame->origin_x - frame->width, right); - VectorScale(glr.viewaxis[2], -frame->origin_y, down); - VectorScale(glr.viewaxis[2], frame->height - frame->origin_y, up); + VectorScale(glr.viewaxis[1], frame->origin_x * scale, left); + VectorScale(glr.viewaxis[1], (frame->origin_x - frame->width) * scale, right); + VectorScale(glr.viewaxis[2], -frame->origin_y * scale, down); + VectorScale(glr.viewaxis[2], (frame->height - frame->origin_y) * scale, up); VectorAdd3(e->origin, down, left, tess.vertices); VectorAdd3(e->origin, up, left, tess.vertices + 5); From 65c853d8bf37d466551ef318eec1aed9e93977c1 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 24 Nov 2024 21:16:06 +0300 Subject: [PATCH 51/79] Interpolate blend in extended mode. --- src/client/entities.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/client/entities.c b/src/client/entities.c index ac8b02c79..b3fe6aaab 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -1409,9 +1409,16 @@ void CL_CalcViewValues(void) LerpAngles(ops->viewangles, ps->viewangles, lerp, cl.refdef.viewangles); } - // don't interpolate blend color - Vector4Copy(ps->blend, cl.refdef.screen_blend); - Vector4Copy(ps->damage_blend, cl.refdef.damage_blend); + // interpolate blend + if (cl.csr.extended && ops->blend[3]) + lerp_values(ops->blend, ps->blend, lerp, cl.refdef.screen_blend, 4); + else + Vector4Copy(ps->blend, cl.refdef.screen_blend); + + if (cl.csr.extended && ops->damage_blend[3]) + lerp_values(ops->damage_blend, ps->damage_blend, lerp, cl.refdef.damage_blend, 4); + else + Vector4Copy(ps->damage_blend, cl.refdef.damage_blend); // interpolate fog if (cl.psFlags & MSG_PS_MOREBITS) { From 4d2ca1db92a0eddc9f60f9345fe9b285e0ad69fa Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 28 Nov 2024 00:43:24 +0300 Subject: [PATCH 52/79] Remove unused defines. --- src/refresh/tess.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/refresh/tess.c b/src/refresh/tess.c index 70be78058..3a49fb569 100644 --- a/src/refresh/tess.c +++ b/src/refresh/tess.c @@ -20,9 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., tesselator_t tess; -#define FACE_HASH_BITS 8 -#define FACE_HASH_SIZE (1 << FACE_HASH_BITS) -#define FACE_HASH_MASK (FACE_HASH_SIZE - 1) +#define FACE_HASH_SIZE 256 static mface_t *faces_head[FACE_HASH_SIZE]; static mface_t **faces_next[FACE_HASH_SIZE]; From 6e6c06847125b02de7197cd961c17f18ecae5e12 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 29 Nov 2024 20:51:14 +0300 Subject: [PATCH 53/79] Add BSPX header presence flag. --- inc/common/bsp.h | 3 ++- src/common/bsp.c | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 564f3f64a..0962a2acb 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -299,7 +299,8 @@ typedef struct { bool lm_decoupled; #endif - bool extended; + bool extended; // QBSP extended format + bool has_bspx; // has BSPX header char name[1]; } bsp_t; diff --git a/src/common/bsp.c b/src/common/bsp.c index 37fac4721..2bc0d5bc7 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -152,8 +152,6 @@ static list_t bsp_cache; static void BSP_PrintStats(const bsp_t *bsp) { - bool extended = bsp->extended; - for (int i = 0; i < q_countof(bsp_stats); i++) Com_Printf("%8d : %s\n", *(int *)((byte *)bsp + bsp_stats[i].ofs), bsp_stats[i].name); @@ -169,15 +167,15 @@ static void BSP_PrintStats(const bsp_t *bsp) "%8u : lightgrid leafs\n" "%8u : lightgrid samples\n", grid->numstyles, grid->numnodes, grid->numleafs, grid->numsamples); - extended = true; } - extended |= bsp->lm_decoupled; #endif - if (extended) { + if (bsp->extended || bsp->has_bspx) { Com_Printf("Features :"); if (bsp->extended) Com_Printf(" QBSP"); + if (bsp->has_bspx) + Com_Printf(" BSPX"); #if USE_REF if (bsp->lm_decoupled) Com_Printf(" DECOUPLED_LM"); @@ -718,6 +716,8 @@ static size_t BSP_ParseExtensionHeader(bsp_t *bsp, lump_t *out, const byte *buf, } } + bsp->has_bspx = true; + return extrasize; } From 4be14331927cd73180c8dbd3ee2ae6a2966c537a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 28 Nov 2024 01:51:19 +0300 Subject: [PATCH 54/79] Ignore SURF_NODRAW bit in vanilla maps. Commit 0dbe1bba disabled rendering of NODRAW surfaces in attempt to fix re-release maps, but this in turn broke vanilla maps which have NODRAW bit randomly set on visible surfaces. Only account for NODRAW if map has BSPX header. Originally reported in Paril/q2pro#132. --- src/client/download.c | 2 +- src/refresh/surf.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/client/download.c b/src/client/download.c index dc5c99d4f..d268dab16 100644 --- a/src/client/download.c +++ b/src/client/download.c @@ -823,7 +823,7 @@ void CL_RequestNextDownload(void) if (allow_download_textures->integer) { for (i = 0; i < cl.bsp->numtexinfo; i++) { - if (cl.bsp->texinfo[i].c.flags & SURF_NODRAW) + if (cl.bsp->texinfo[i].c.flags & SURF_NODRAW && cl.bsp->has_bspx) continue; len = Q_concat(fn, sizeof(fn), "textures/", cl.bsp->texinfo[i].name, ".wal"); check_file_len(fn, len, DL_OTHER); diff --git a/src/refresh/surf.c b/src/refresh/surf.c index 2c3830f98..20c3aabaf 100644 --- a/src/refresh/surf.c +++ b/src/refresh/surf.c @@ -1055,7 +1055,7 @@ void GL_LoadWorld(const char *name) } else { info->image = R_SKYTEXTURE; } - } else if (info->c.flags & SURF_NODRAW) { + } else if (info->c.flags & SURF_NODRAW && bsp->has_bspx) { info->image = R_NOTEXTURE; } else { imageflags_t flags = (info->c.flags & SURF_WARP) ? IF_TURBULENT : IF_NONE; @@ -1079,8 +1079,14 @@ void GL_LoadWorld(const char *name) continue; surf->drawflags &= ~SURF_NODRAW; } - if (surf->drawflags & SURF_NODRAW) - continue; + + // ignore NODRAW bit in vanilla maps for compatibility + if (surf->drawflags & SURF_NODRAW) { + if (bsp->has_bspx) + continue; + surf->drawflags &= ~SURF_NODRAW; + } + if (surf->drawflags & SURF_N64_UV) n64surfs++; From 2695c6c0a0cfa2e3a5e1ed8210eac00ea2e9f5e1 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 28 Nov 2024 01:52:04 +0300 Subject: [PATCH 55/79] Allow lightmapped SURF_FLOWING faces. These are allowed to have lightmaps in vanilla Q2. --- inc/common/bsp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 0962a2acb..6c8992cf3 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -76,7 +76,7 @@ typedef struct { #define SURF_COLOR_MASK (SURF_TRANS_MASK | SURF_WARP) #define SURF_NOLM_MASK_REMASTER (SURF_SKY | SURF_NODRAW) -#define SURF_NOLM_MASK_DEFAULT (SURF_COLOR_MASK | SURF_FLOWING | SURF_NOLM_MASK_REMASTER) +#define SURF_NOLM_MASK_DEFAULT (SURF_COLOR_MASK | SURF_NOLM_MASK_REMASTER) #define DSURF_PLANEBACK 1 From b3ba4886eb5ef4a589c582036122c1183c9637d4 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 28 Nov 2024 01:52:57 +0300 Subject: [PATCH 56/79] =?UTF-8?q?Show=20surface=20flags=20for=20=E2=80=98c?= =?UTF-8?q?l=5Fsurface=E2=80=99=20macro.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/main.c b/src/client/main.c index f4c1ab3f3..484fa4151 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -2297,7 +2297,7 @@ static size_t CL_Surface_m(char *buffer, size_t size) VectorMA(cl.refdef.vieworg, 8192, cl.v_forward, end); CL_Trace(&trace, cl.refdef.vieworg, end, vec3_origin, vec3_origin, MASK_SOLID | MASK_WATER); - return Q_strlcpy(buffer, trace.surface->name, size); + return Q_snprintf(buffer, size, "%s %#x", trace.surface->name, trace.surface->flags); } /* From 945ee658df63013baf2381d0ec6849f7ddf868e7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 29 Nov 2024 00:49:18 +0300 Subject: [PATCH 57/79] Remove fake sky faces if using cubemaps. Some vanilla maps use fake sky faces for lighting and backface culling. These faces should be invisible, and must be removed if using cubemaps. Originally reported in Paril/q2pro#132. --- src/refresh/surf.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/refresh/surf.c b/src/refresh/surf.c index 20c3aabaf..17606666a 100644 --- a/src/refresh/surf.c +++ b/src/refresh/surf.c @@ -995,6 +995,69 @@ void GL_FreeWorld(void) memset(&gl_static.world, 0, sizeof(gl_static.world)); } +static const mnode_t *find_face_node(const bsp_t *bsp, const mface_t *face) +{ + const mnode_t *node; + int i, left, right; + + left = 0; + right = bsp->numnodes - 1; + while (left <= right) { + i = (left + right) / 2; + node = &bsp->nodes[i]; + if (node->firstface + node->numfaces <= face) + left = i + 1; + else if (node->firstface > face) + right = i - 1; + else + return node; + } + + return NULL; +} + +static void remove_fake_sky_faces(const bsp_t *bsp) +{ + const mleaf_t *leaf; + const mnode_t *node; + int i, j, k, count = 0; + mface_t *face; + + // find CONTENTS_MIST leafs + for (i = 1, leaf = bsp->leafs + i; i < bsp->numleafs; i++, leaf++) { + if (!(leaf->contents[0] & CONTENTS_MIST)) + continue; + + // remove sky faces in this leaf + for (j = 0; j < leaf->numleaffaces; j++) { + face = leaf->firstleafface[j]; + if (!(face->drawflags & SURF_SKY)) + continue; + + face->drawflags = SURF_NODRAW; + count++; + + // find node this face is on + node = find_face_node(bsp, face); + if (!node) { + Com_DPrintf("Sky face node not found\n"); + continue; + } + + // remove other sky faces on this node + for (k = 0, face = node->firstface; k < node->numfaces; k++, face++) { + if (face->drawflags & SURF_SKY) { + face->drawflags = SURF_NODRAW; + count++; + } + } + } + } + + if (count) + Com_DPrintf("Removed %d fake sky faces\n", count); +} + void GL_LoadWorld(const char *name) { char buffer[MAX_QPATH]; @@ -1093,6 +1156,10 @@ void GL_LoadWorld(const char *name) size += surf->numsurfedges * VERTEX_SIZE * sizeof(vec_t); } + // remove fake sky faces in vanilla maps + if (!bsp->has_bspx && gl_static.use_cubemaps) + remove_fake_sky_faces(bsp); + // try VBO first, then allocate on heap if (create_surface_vbo(size)) { Com_DPrintf("%s: %zu bytes of vertex data as VBO\n", __func__, size); From b55563ec80e2a3c1f73bdfb445114f2d2daa3bb9 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 29 Nov 2024 19:34:54 +0300 Subject: [PATCH 58/79] Calc vertex buffer size after removing sky faces. --- src/refresh/surf.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/refresh/surf.c b/src/refresh/surf.c index 17606666a..eb79ca7d6 100644 --- a/src/refresh/surf.c +++ b/src/refresh/surf.c @@ -895,8 +895,6 @@ static void upload_world_surfaces(void) currvert = 0; lastvert = 0; for (i = 0, surf = bsp->faces; i < bsp->numfaces; i++, surf++) { - if (surf->drawflags & SURF_SKY && !gl_static.use_cubemaps) - continue; if (surf->drawflags & SURF_NODRAW) continue; @@ -1127,8 +1125,7 @@ void GL_LoadWorld(const char *name) } } - // calculate vertex buffer size in bytes - size = 0; + // setup drawflags, etc for (i = n64surfs = 0, surf = bsp->faces; i < bsp->numfaces; i++, surf++) { // hack surface flags into drawflags for faster access surf->drawflags |= surf->texinfo->c.flags & ~DSURF_PLANEBACK; @@ -1136,10 +1133,12 @@ void GL_LoadWorld(const char *name) // clear statebits from previous load surf->statebits = GLS_DEFAULT; - // don't count sky surfaces + // don't count sky surfaces unless using cubemaps if (surf->drawflags & SURF_SKY) { - if (!gl_static.use_cubemaps) + if (!gl_static.use_cubemaps) { + surf->drawflags |= SURF_NODRAW; // simplify other code continue; + } surf->drawflags &= ~SURF_NODRAW; } @@ -1152,14 +1151,17 @@ void GL_LoadWorld(const char *name) if (surf->drawflags & SURF_N64_UV) n64surfs++; - - size += surf->numsurfedges * VERTEX_SIZE * sizeof(vec_t); } // remove fake sky faces in vanilla maps if (!bsp->has_bspx && gl_static.use_cubemaps) remove_fake_sky_faces(bsp); + // calculate vertex buffer size in bytes + for (i = size = 0, surf = bsp->faces; i < bsp->numfaces; i++, surf++) + if (!(surf->drawflags & SURF_NODRAW)) + size += surf->numsurfedges * VERTEX_SIZE * sizeof(vec_t); + // try VBO first, then allocate on heap if (create_surface_vbo(size)) { Com_DPrintf("%s: %zu bytes of vertex data as VBO\n", __func__, size); From a1b3d4989dae410a42702e61c9c02788352bd65b Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 30 Nov 2024 01:10:36 +0300 Subject: [PATCH 59/79] Consider more flags for N64 detection. --- src/refresh/surf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/refresh/surf.c b/src/refresh/surf.c index eb79ca7d6..f0d4ace28 100644 --- a/src/refresh/surf.c +++ b/src/refresh/surf.c @@ -1149,7 +1149,7 @@ void GL_LoadWorld(const char *name) surf->drawflags &= ~SURF_NODRAW; } - if (surf->drawflags & SURF_N64_UV) + if (surf->drawflags & (SURF_N64_UV | SURF_N64_SCROLL_X | SURF_N64_SCROLL_Y)) n64surfs++; } From c219c2568fa9fe372b9dbbb7df2294a2180bd637 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 10 Dec 2024 18:38:35 +0300 Subject: [PATCH 60/79] Align code for readability. --- src/refresh/models.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/refresh/models.c b/src/refresh/models.c index 764d4bfe0..cc5048455 100644 --- a/src/refresh/models.c +++ b/src/refresh/models.c @@ -260,26 +260,26 @@ static int MOD_LoadSP2(model_t *model, const void *rawdata, size_t length) static const char *MOD_ValidateMD2(const dmd2header_t *header, size_t length) { - ENSURE(header->num_tris <= TESS_MAX_INDICES / 3, "Too many tris"); - ENSURE(header->num_st <= INT_MAX / sizeof(dmd2stvert_t), "Too many st"); - ENSURE(header->num_xyz <= MD2_MAX_VERTS, "Too many xyz"); - ENSURE(header->num_frames <= MD2_MAX_FRAMES, "Too many frames"); - ENSURE(header->num_skins <= MD2_MAX_SKINS, "Too many skins"); + ENSURE(header->num_tris <= TESS_MAX_INDICES / 3, "Too many tris"); + ENSURE(header->num_st <= INT_MAX / sizeof(dmd2stvert_t), "Too many st"); + ENSURE(header->num_xyz <= MD2_MAX_VERTS, "Too many xyz"); + ENSURE(header->num_frames <= MD2_MAX_FRAMES, "Too many frames"); + ENSURE(header->num_skins <= MD2_MAX_SKINS, "Too many skins"); ENSURE(header->framesize >= sizeof(dmd2frame_t) + (header->num_xyz - 1) * sizeof(dmd2trivertx_t), "Too small frame size"); ENSURE(header->framesize <= MD2_MAX_FRAMESIZE, "Too big frame size"); - ENSURE((uint64_t)header->ofs_tris + header->num_tris * sizeof(dmd2triangle_t) <= length, "Bad tris offset"); - ENSURE((uint64_t)header->ofs_st + header->num_st * sizeof(dmd2stvert_t) <= length, "Bad st offset"); - ENSURE((uint64_t)header->ofs_frames + header->num_frames * header->framesize <= length, "Bad frames offset"); - ENSURE((uint64_t)header->ofs_skins + MD2_MAX_SKINNAME * header->num_skins <= length, "Bad skins offset"); + ENSURE((uint64_t)header->ofs_tris + header->num_tris * sizeof(dmd2triangle_t) <= length, "Bad tris offset"); + ENSURE((uint64_t)header->ofs_st + header->num_st * sizeof(dmd2stvert_t) <= length, "Bad st offset"); + ENSURE((uint64_t)header->ofs_frames + header->num_frames * header->framesize <= length, "Bad frames offset"); + ENSURE((uint64_t)header->ofs_skins + header->num_skins * MD2_MAX_SKINNAME <= length, "Bad skins offset"); - ENSURE(!(header->ofs_tris % q_alignof(dmd2triangle_t)), "Odd tris offset"); - ENSURE(!(header->ofs_st % q_alignof(dmd2stvert_t)), "Odd st offset"); - ENSURE(!(header->ofs_frames % q_alignof(dmd2frame_t)), "Odd frames offset"); - ENSURE(!(header->framesize % q_alignof(dmd2frame_t)), "Odd frame size"); + ENSURE(!(header->ofs_tris % q_alignof(dmd2triangle_t)), "Odd tris offset"); + ENSURE(!(header->ofs_st % q_alignof(dmd2stvert_t)), "Odd st offset"); + ENSURE(!(header->ofs_frames % q_alignof(dmd2frame_t)), "Odd frames offset"); + ENSURE(!(header->framesize % q_alignof(dmd2frame_t)), "Odd frame size"); - ENSURE(header->skinwidth >= 1 && header->skinwidth <= MAX_TEXTURE_SIZE, "Bad skin width"); + ENSURE(header->skinwidth >= 1 && header->skinwidth <= MAX_TEXTURE_SIZE, "Bad skin width"); ENSURE(header->skinheight >= 1 && header->skinheight <= MAX_TEXTURE_SIZE, "Bad skin height"); return NULL; } From 70277e76dde224eb24c944c337b538274cf8540a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 10 Dec 2024 18:42:17 +0300 Subject: [PATCH 61/79] Ignore prediction misses of 1/8 units or less. Prevents tiny oscillations when standing on top of another entity in some cases. Follow-up fix to d0190749. --- src/client/predict.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/predict.c b/src/client/predict.c index e3757fab6..1e896b31d 100644 --- a/src/client/predict.c +++ b/src/client/predict.c @@ -51,7 +51,7 @@ void CL_CheckPredictionError(void) // save the prediction error for interpolation len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]); - if (len < 1 || len > 640) { + if (len <= 1 || len > 640) { // > 80 world units is a teleport or something VectorClear(cl.prediction_error); return; From 2c0b89905d2a1d0c840e7e6cef834e9cf18d531a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 19 Dec 2024 00:02:13 +0300 Subject: [PATCH 62/79] Fix race condition when retrieving thread return value. Return value is not used anywhere currently. --- inc/system/pthread.h | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/inc/system/pthread.h b/inc/system/pthread.h index bb6fc18e7..5547414e7 100644 --- a/inc/system/pthread.h +++ b/inc/system/pthread.h @@ -44,11 +44,11 @@ typedef struct { void *(*func)(void *); void *arg, *ret; HANDLE handle; -} pthread_t; +} *pthread_t; static unsigned __stdcall thread_func(void *arg) { - pthread_t *t = arg; + pthread_t t = arg; t->ret = t->func(t->arg); return 0; } @@ -56,18 +56,27 @@ static unsigned __stdcall thread_func(void *arg) static inline int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { - thread->func = start_routine; - thread->arg = arg; - thread->handle = (HANDLE)_beginthreadex(NULL, 0, thread_func, thread, 0, NULL); - return thread->handle ? 0 : EAGAIN; + pthread_t t = calloc(1, sizeof(*t)); + if (!t) + return EAGAIN; + t->func = start_routine; + t->arg = arg; + t->handle = (HANDLE)_beginthreadex(NULL, 0, thread_func, t, 0, NULL); + if (!t->handle) { + free(t); + return EAGAIN; + } + *thread = t; + return 0; } static inline int pthread_join(pthread_t thread, void **retval) { - int ret = WaitForSingleObject(thread.handle, INFINITE); - CloseHandle(thread.handle); + int ret = WaitForSingleObject(thread->handle, INFINITE); + CloseHandle(thread->handle); if (retval) - *retval = thread.ret; + *retval = thread->ret; + free(thread); return ret ? EINVAL : 0; } From d07f8c97267e692fb7290a30f47a4ee139a3d0b6 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 21 Dec 2024 19:35:19 +0300 Subject: [PATCH 63/79] Rename CHAR_WIDTH to CONCHAR_WIDTH. Prevents conflict with limits.h when compiling in C23 mode. Also rename CHAR_HEIGHT. --- inc/client/client.h | 4 +-- src/client/console.c | 48 ++++++++++++++-------------- src/client/screen.c | 62 ++++++++++++++++++------------------ src/client/ui/demos.c | 24 +++++++------- src/client/ui/menu.c | 52 +++++++++++++++--------------- src/client/ui/playerconfig.c | 4 +-- src/client/ui/servers.c | 56 ++++++++++++++++---------------- src/client/ui/ui.c | 8 ++--- src/client/ui/ui.h | 8 ++--- src/common/field.c | 2 +- src/refresh/draw.c | 8 ++--- src/server/mvd/game.c | 2 +- 12 files changed, 139 insertions(+), 139 deletions(-) diff --git a/inc/client/client.h b/inc/client/client.h index 2566a732b..8e22b98a3 100644 --- a/inc/client/client.h +++ b/inc/client/client.h @@ -22,8 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/net/net.h" #include "common/utils.h" -#define CHAR_WIDTH 8 -#define CHAR_HEIGHT 8 +#define CONCHAR_WIDTH 8 +#define CONCHAR_HEIGHT 8 // only begin attenuating sound volumes when outside the FULLVOLUME range #define SOUND_FULLVOLUME 80 diff --git a/src/client/console.c b/src/client/console.c index b019a5fb1..c72143ec2 100644 --- a/src/client/console.c +++ b/src/client/console.c @@ -372,7 +372,7 @@ void Con_CheckResize(void) con.vidWidth = Q_rint(r_config.width * con.scale); con.vidHeight = Q_rint(r_config.height * con.scale); - con.linewidth = Q_clip(con.vidWidth / CHAR_WIDTH - 2, 0, CON_LINEWIDTH); + con.linewidth = Q_clip(con.vidWidth / CONCHAR_WIDTH - 2, 0, CON_LINEWIDTH); con.prompt.inputLine.visibleChars = con.linewidth; con.prompt.widthInChars = con.linewidth; con.chatPrompt.inputLine.visibleChars = con.linewidth; @@ -693,7 +693,7 @@ static int Con_DrawLine(int v, int row, float alpha, bool notify) const consoleLine_t *line = &con.text[row & CON_TOTALLINES_MASK]; const char *s = line->text; int flags = 0; - int x = CHAR_WIDTH; + int x = CONCHAR_WIDTH; int w = con.linewidth; if (notify) { @@ -724,7 +724,7 @@ static int Con_DrawLine(int v, int row, float alpha, bool notify) return R_DrawString(x, v, flags, w, s, con.charsetImage); } -#define CON_PRESTEP (CHAR_HEIGHT * 3 + CHAR_HEIGHT / 4) +#define CON_PRESTEP (CONCHAR_HEIGHT * 3 + CONCHAR_HEIGHT / 4) /* ================ @@ -775,7 +775,7 @@ static void Con_DrawNotify(void) Con_DrawLine(v, i, alpha, true); - v += CHAR_HEIGHT; + v += CONCHAR_HEIGHT; } R_ClearColor(); @@ -789,10 +789,10 @@ static void Con_DrawNotify(void) skip = 5; } - R_DrawString(CHAR_WIDTH, v, 0, MAX_STRING_CHARS, text, + R_DrawString(CONCHAR_WIDTH, v, 0, MAX_STRING_CHARS, text, con.charsetImage); con.chatPrompt.inputLine.visibleChars = con.linewidth - skip + 1; - IF_Draw(&con.chatPrompt.inputLine, skip * CHAR_WIDTH, v, + IF_Draw(&con.chatPrompt.inputLine, skip * CONCHAR_WIDTH, v, UI_DRAWCURSOR, con.charsetImage); } } @@ -836,16 +836,16 @@ static void Con_DrawSolidConsole(void) // draw the text y = vislines - CON_PRESTEP; - rows = y / CHAR_HEIGHT + 1; // rows of text to draw + rows = y / CONCHAR_HEIGHT + 1; // rows of text to draw // draw arrows to show the buffer is backscrolled if (con.display != con.current) { R_SetColor(U32_RED); for (i = 1; i < con.linewidth / 2; i += 4) { - R_DrawChar(i * CHAR_WIDTH, y, 0, '^', con.charsetImage); + R_DrawChar(i * CONCHAR_WIDTH, y, 0, '^', con.charsetImage); } - y -= CHAR_HEIGHT; + y -= CONCHAR_HEIGHT; rows--; } @@ -864,7 +864,7 @@ static void Con_DrawSolidConsole(void) widths[i] = x; } - y -= CHAR_HEIGHT; + y -= CONCHAR_HEIGHT; row--; } @@ -913,8 +913,8 @@ static void Con_DrawSolidConsole(void) Q_strlcat(buffer, suf, sizeof(buffer)); // draw it - y = vislines - CON_PRESTEP + CHAR_HEIGHT * 2; - R_DrawString(CHAR_WIDTH, y, 0, con.linewidth, buffer, con.charsetImage); + y = vislines - CON_PRESTEP + CONCHAR_HEIGHT * 2; + R_DrawString(CONCHAR_WIDTH, y, 0, con.linewidth, buffer, con.charsetImage); } else if (cls.state == ca_loading) { // draw loading state switch (con.loadstate) { @@ -942,35 +942,35 @@ static void Con_DrawSolidConsole(void) Q_snprintf(buffer, sizeof(buffer), "Loading %s...", text); // draw it - y = vislines - CON_PRESTEP + CHAR_HEIGHT * 2; - R_DrawString(CHAR_WIDTH, y, 0, con.linewidth, buffer, con.charsetImage); + y = vislines - CON_PRESTEP + CONCHAR_HEIGHT * 2; + R_DrawString(CONCHAR_WIDTH, y, 0, con.linewidth, buffer, con.charsetImage); } } // draw the input prompt, user text, and cursor if desired x = 0; if (cls.key_dest & KEY_CONSOLE) { - y = vislines - CON_PRESTEP + CHAR_HEIGHT; + y = vislines - CON_PRESTEP + CONCHAR_HEIGHT; // draw command prompt i = con.mode == CON_REMOTE ? '#' : 17; R_SetColor(U32_YELLOW); - R_DrawChar(CHAR_WIDTH, y, 0, i, con.charsetImage); + R_DrawChar(CONCHAR_WIDTH, y, 0, i, con.charsetImage); R_ClearColor(); // draw input line - x = IF_Draw(&con.prompt.inputLine, 2 * CHAR_WIDTH, y, + x = IF_Draw(&con.prompt.inputLine, 2 * CONCHAR_WIDTH, y, UI_DRAWCURSOR, con.charsetImage); } #define APP_VERSION APPLICATION " " VERSION -#define VER_WIDTH ((int)(sizeof(APP_VERSION) + 1) * CHAR_WIDTH) +#define VER_WIDTH ((int)(sizeof(APP_VERSION) + 1) * CONCHAR_WIDTH) - y = vislines - CON_PRESTEP + CHAR_HEIGHT; + y = vislines - CON_PRESTEP + CONCHAR_HEIGHT; row = 0; // shift version upwards to prevent overdraw if (x > con.vidWidth - VER_WIDTH) { - y -= CHAR_HEIGHT; + y -= CONCHAR_HEIGHT; row++; } @@ -978,16 +978,16 @@ static void Con_DrawSolidConsole(void) // draw clock if (con_clock->integer) { - x = Com_Time_m(buffer, sizeof(buffer)) * CHAR_WIDTH; - if (widths[row] + x + CHAR_WIDTH <= con.vidWidth) { - R_DrawString(con.vidWidth - CHAR_WIDTH - x, y - CHAR_HEIGHT, + x = Com_Time_m(buffer, sizeof(buffer)) * CONCHAR_WIDTH; + if (widths[row] + x + CONCHAR_WIDTH <= con.vidWidth) { + R_DrawString(con.vidWidth - CONCHAR_WIDTH - x, y - CONCHAR_HEIGHT, UI_RIGHT, MAX_STRING_CHARS, buffer, con.charsetImage); } } // draw version if (!row || widths[0] + VER_WIDTH <= con.vidWidth) { - SCR_DrawStringEx(con.vidWidth - CHAR_WIDTH, y, UI_RIGHT, + SCR_DrawStringEx(con.vidWidth - CONCHAR_WIDTH, y, UI_RIGHT, MAX_STRING_CHARS, APP_VERSION, con.charsetImage); } diff --git a/src/client/screen.c b/src/client/screen.c index 0f89a5392..9534e2472 100644 --- a/src/client/screen.c +++ b/src/client/screen.c @@ -148,9 +148,9 @@ int SCR_DrawStringEx(int x, int y, int flags, size_t maxlen, } if ((flags & UI_CENTER) == UI_CENTER) { - x -= len * CHAR_WIDTH / 2; + x -= len * CONCHAR_WIDTH / 2; } else if (flags & UI_RIGHT) { - x -= len * CHAR_WIDTH; + x -= len * CONCHAR_WIDTH; } return R_DrawString(x, y, flags, maxlen, s, font); @@ -183,7 +183,7 @@ void SCR_DrawStringMulti(int x, int y, int flags, size_t maxlen, last_y = y; maxlen -= len; - y += CHAR_HEIGHT; + y += CONCHAR_HEIGHT; s = p + 1; } @@ -410,7 +410,7 @@ static void draw_progress_bar(float progress, bool paused, int framenum) size_t len; w = Q_rint(scr.hud_width * progress); - h = Q_rint(CHAR_HEIGHT / scr.hud_scale); + h = Q_rint(CONCHAR_HEIGHT / scr.hud_scale); scr.hud_height -= h; @@ -423,7 +423,7 @@ static void draw_progress_bar(float progress, bool paused, int framenum) h = Q_rint(scr.hud_height * scr.hud_scale); len = Q_scnprintf(buffer, sizeof(buffer), "%.f%%", progress * 100); - x = (w - len * CHAR_WIDTH) / 2; + x = (w - len * CONCHAR_WIDTH) / 2; R_DrawString(x, h, 0, MAX_STRING_CHARS, buffer, scr.font_pic); if (scr_demobar->integer > 1) { @@ -589,7 +589,7 @@ static void SCR_DrawCenterString(void) R_SetAlpha(alpha * scr_alpha->value); - y = scr.hud_height / 4 - cp->lines * CHAR_HEIGHT / 2; + y = scr.hud_height / 4 - cp->lines * CONCHAR_HEIGHT / 2; flags = UI_CENTER; if (cp->typewrite) { @@ -933,7 +933,7 @@ static void SCR_DrawObjects(void) x += scr.hud_width + 1; } if (y < 0) { - y += scr.hud_height - CHAR_HEIGHT + 1; + y += scr.hud_height - CONCHAR_HEIGHT + 1; } if (!(obj->flags & UI_IGNORECOLOR)) { R_SetColor(obj->color.u32); @@ -1016,10 +1016,10 @@ static void SCR_DrawChatHUD(void) } if (y < 0) { - y += scr.hud_height - CHAR_HEIGHT + 1; - step = -CHAR_HEIGHT; + y += scr.hud_height - CONCHAR_HEIGHT + 1; + step = -CONCHAR_HEIGHT; } else { - step = CHAR_HEIGHT; + step = CONCHAR_HEIGHT; } lines = scr_chathud_lines->integer; @@ -1063,13 +1063,13 @@ static void SCR_DrawTurtle(void) if (!cl.frameflags) return; - x = CHAR_WIDTH; - y = scr.hud_height - 11 * CHAR_HEIGHT; + x = CONCHAR_WIDTH; + y = scr.hud_height - 11 * CONCHAR_HEIGHT; #define DF(f) \ if (cl.frameflags & FF_##f) { \ SCR_DrawString(x, y, UI_ALTCOLOR, #f); \ - y += CHAR_HEIGHT; \ + y += CONCHAR_HEIGHT; \ } if (scr_showturtle->integer > 1) { @@ -1103,8 +1103,8 @@ static void SCR_DrawDebugStats(void) if (j > MAX_STATS) j = MAX_STATS; - x = CHAR_WIDTH; - y = (scr.hud_height - j * CHAR_HEIGHT) / 2; + x = CONCHAR_WIDTH; + y = (scr.hud_height - j * CONCHAR_HEIGHT) / 2; for (i = 0; i < j; i++) { Q_snprintf(buffer, sizeof(buffer), "%2d: %d", i, cl.frame.ps.stats[i]); if (cl.oldframe.ps.stats[i] != cl.frame.ps.stats[i]) { @@ -1112,7 +1112,7 @@ static void SCR_DrawDebugStats(void) } R_DrawString(x, y, 0, MAX_STRING_CHARS, buffer, scr.font_pic); R_ClearColor(); - y += CHAR_HEIGHT; + y += CONCHAR_HEIGHT; } } @@ -1132,21 +1132,21 @@ static void SCR_DrawDebugPmove(void) if (!scr_showpmove->integer) return; - x = CHAR_WIDTH; - y = (scr.hud_height - 2 * CHAR_HEIGHT) / 2; + x = CONCHAR_WIDTH; + y = (scr.hud_height - 2 * CONCHAR_HEIGHT) / 2; i = cl.frame.ps.pmove.pm_type; if (i > PM_FREEZE) i = PM_FREEZE; R_DrawString(x, y, 0, MAX_STRING_CHARS, types[i], scr.font_pic); - y += CHAR_HEIGHT; + y += CONCHAR_HEIGHT; j = cl.frame.ps.pmove.pm_flags; for (i = 0; i < 8; i++) { if (j & (1 << i)) { x = R_DrawString(x, y, 0, MAX_STRING_CHARS, flags[i], scr.font_pic); - x += CHAR_WIDTH; + x += CONCHAR_WIDTH; } } } @@ -1697,10 +1697,10 @@ static void SCR_DrawInventory(void) x += 24; HUD_DrawString(x, y, "hotkey ### item"); - y += CHAR_HEIGHT; + y += CONCHAR_HEIGHT; HUD_DrawString(x, y, "------ --- ----"); - y += CHAR_HEIGHT; + y += CONCHAR_HEIGHT; for (i = top; i < num && i < top + DISPLAY_ITEMS; i++) { item = index[i]; @@ -1716,11 +1716,11 @@ static void SCR_DrawInventory(void) } else { // draw a blinky cursor by the selected item HUD_DrawString(x, y, string); if ((cls.realtime >> 8) & 1) { - R_DrawChar(x - CHAR_WIDTH, y, 0, 15, scr.font_pic); + R_DrawChar(x - CONCHAR_WIDTH, y, 0, 15, scr.font_pic); } } - y += CHAR_HEIGHT; + y += CONCHAR_HEIGHT; } } @@ -1783,7 +1783,7 @@ static void SCR_DrawHealthBar(int x, int y, int value) int bar_width = scr.hud_width / 3; float percent = (value - 1) / 254.0f; int w = bar_width * percent + 0.5f; - int h = CHAR_HEIGHT / 2; + int h = CONCHAR_HEIGHT / 2; x -= bar_width / 2; R_DrawFill8(x, y, w, h, 240); @@ -1907,13 +1907,13 @@ static void SCR_ExecuteLayoutString(const char *s) time = Q_atoi(token); HUD_DrawAltString(x + 32, y, ci->name); - HUD_DrawString(x + 32, y + CHAR_HEIGHT, "Score: "); + HUD_DrawString(x + 32, y + CONCHAR_HEIGHT, "Score: "); Q_snprintf(buffer, sizeof(buffer), "%i", score); - HUD_DrawAltString(x + 32 + 7 * CHAR_WIDTH, y + CHAR_HEIGHT, buffer); + HUD_DrawAltString(x + 32 + 7 * CONCHAR_WIDTH, y + CONCHAR_HEIGHT, buffer); Q_snprintf(buffer, sizeof(buffer), "Ping: %i", ping); - HUD_DrawString(x + 32, y + 2 * CHAR_HEIGHT, buffer); + HUD_DrawString(x + 32, y + 2 * CONCHAR_HEIGHT, buffer); Q_snprintf(buffer, sizeof(buffer), "Time: %i", time); - HUD_DrawString(x + 32, y + 3 * CHAR_HEIGHT, buffer); + HUD_DrawString(x + 32, y + 3 * CONCHAR_HEIGHT, buffer); if (!ci->icon) { ci = &cl.baseclientinfo; @@ -2145,8 +2145,8 @@ static void SCR_ExecuteLayoutString(const char *s) } HUD_DrawCenterString(x + 320 / 2, y, cl.configstrings[index]); - SCR_DrawHealthBar(x + 320 / 2, y + CHAR_HEIGHT + 4, value & 0xff); - SCR_DrawHealthBar(x + 320 / 2, y + CHAR_HEIGHT + 12, (value >> 8) & 0xff); + SCR_DrawHealthBar(x + 320 / 2, y + CONCHAR_HEIGHT + 4, value & 0xff); + SCR_DrawHealthBar(x + 320 / 2, y + CONCHAR_HEIGHT + 12, (value >> 8) & 0xff); continue; } } diff --git a/src/client/ui/demos.c b/src/client/ui/demos.c index 0d26d0b7b..49c4b45d6 100644 --- a/src/client/ui/demos.c +++ b/src/client/ui/demos.c @@ -539,26 +539,26 @@ static void Size(menuFrameWork_t *self) int w1, w2; m_demos.list.generic.x = 0; - m_demos.list.generic.y = CHAR_HEIGHT; + m_demos.list.generic.y = CONCHAR_HEIGHT; m_demos.list.generic.width = 0; - m_demos.list.generic.height = uis.height - CHAR_HEIGHT * 2 - 1; + m_demos.list.generic.height = uis.height - CONCHAR_HEIGHT * 2 - 1; w1 = 17 + m_demos.widest_map + m_demos.widest_pov; - w2 = uis.width - w1 * CHAR_WIDTH - MLIST_PADDING * 4 - MLIST_SCROLLBAR_WIDTH; - if (w2 > 8 * CHAR_WIDTH) { + w2 = uis.width - w1 * CONCHAR_WIDTH - MLIST_PADDING * 4 - MLIST_SCROLLBAR_WIDTH; + if (w2 > 8 * CONCHAR_WIDTH) { // everything fits m_demos.list.columns[0].width = w2; - m_demos.list.columns[1].width = 12 * CHAR_WIDTH + MLIST_PADDING; - m_demos.list.columns[2].width = 5 * CHAR_WIDTH + MLIST_PADDING; - m_demos.list.columns[3].width = m_demos.widest_map * CHAR_WIDTH + MLIST_PADDING; - m_demos.list.columns[4].width = m_demos.widest_pov * CHAR_WIDTH + MLIST_PADDING; + m_demos.list.columns[1].width = 12 * CONCHAR_WIDTH + MLIST_PADDING; + m_demos.list.columns[2].width = 5 * CONCHAR_WIDTH + MLIST_PADDING; + m_demos.list.columns[3].width = m_demos.widest_map * CONCHAR_WIDTH + MLIST_PADDING; + m_demos.list.columns[4].width = m_demos.widest_pov * CONCHAR_WIDTH + MLIST_PADDING; m_demos.list.numcolumns = COL_MAX; } else { // map and pov don't fit - w2 = uis.width - 17 * CHAR_WIDTH - MLIST_PADDING * 2 - MLIST_SCROLLBAR_WIDTH; + w2 = uis.width - 17 * CONCHAR_WIDTH - MLIST_PADDING * 2 - MLIST_SCROLLBAR_WIDTH; m_demos.list.columns[0].width = w2; - m_demos.list.columns[1].width = 12 * CHAR_WIDTH + MLIST_PADDING; - m_demos.list.columns[2].width = 5 * CHAR_WIDTH + MLIST_PADDING; + m_demos.list.columns[1].width = 12 * CONCHAR_WIDTH + MLIST_PADDING; + m_demos.list.columns[2].width = 5 * CONCHAR_WIDTH + MLIST_PADDING; m_demos.list.columns[3].width = 0; m_demos.list.columns[4].width = 0; m_demos.list.numcolumns = COL_MAX - 2; @@ -579,7 +579,7 @@ static void Draw(menuFrameWork_t *self) { Menu_Draw(self); if (uis.width >= 640) { - UI_DrawString(uis.width, uis.height - CHAR_HEIGHT, + UI_DrawString(uis.width, uis.height - CONCHAR_HEIGHT, UI_RIGHT, m_demos.status); } } diff --git a/src/client/ui/menu.c b/src/client/ui/menu.c index ef34cc16a..9c140a011 100644 --- a/src/client/ui/menu.c +++ b/src/client/ui/menu.c @@ -72,7 +72,7 @@ static void Action_Draw(menuAction_t *a) } else { flags |= UI_ALTCOLOR; if ((uis.realtime >> 8) & 1) { - UI_DrawChar(a->generic.x - strlen(a->generic.name) * CHAR_WIDTH / 2 - CHAR_WIDTH, a->generic.y, flags, 13); + UI_DrawChar(a->generic.x - strlen(a->generic.name) * CONCHAR_WIDTH / 2 - CONCHAR_WIDTH, a->generic.y, flags, 13); } } } @@ -208,7 +208,7 @@ static void Keybind_Init(menuKeybind_t *k) len = 3; } - k->generic.rect.width += (RCOLUMN_OFFSET - LCOLUMN_OFFSET) + len * CHAR_WIDTH; + k->generic.rect.width += (RCOLUMN_OFFSET - LCOLUMN_OFFSET) + len * CONCHAR_WIDTH; } /* @@ -390,7 +390,7 @@ Field_Init */ static void Field_Init(menuField_t *f) { - int w = f->width * CHAR_WIDTH; + int w = f->width * CONCHAR_WIDTH; f->generic.uiFlags &= ~(UI_LEFT | UI_RIGHT); @@ -404,7 +404,7 @@ static void Field_Init(menuField_t *f) f->generic.rect.x = f->generic.x - w / 2; f->generic.rect.y = f->generic.y; f->generic.rect.width = w; - f->generic.rect.height = CHAR_HEIGHT; + f->generic.rect.height = CONCHAR_HEIGHT; } } @@ -429,13 +429,13 @@ static void Field_Draw(menuField_t *f) f->generic.uiFlags | UI_RIGHT | UI_ALTCOLOR, f->generic.name); R_DrawFill32(f->generic.x + RCOLUMN_OFFSET, f->generic.y - 1, - f->field.visibleChars * CHAR_WIDTH, CHAR_HEIGHT + 2, color); + f->field.visibleChars * CONCHAR_WIDTH, CONCHAR_HEIGHT + 2, color); IF_Draw(&f->field, f->generic.x + RCOLUMN_OFFSET, f->generic.y, flags, uis.fontHandle); } else { R_DrawFill32(f->generic.rect.x, f->generic.rect.y - 1, - f->generic.rect.width, CHAR_HEIGHT + 2, color); + f->generic.rect.width, CONCHAR_HEIGHT + 2, color); IF_Draw(&f->field, f->generic.rect.x, f->generic.rect.y, flags, uis.fontHandle); @@ -560,7 +560,7 @@ void SpinControl_Init(menuSpinControl_t *s) } s->generic.rect.width += (RCOLUMN_OFFSET - LCOLUMN_OFFSET) + - maxLength * CHAR_WIDTH; + maxLength * CONCHAR_WIDTH; } /* @@ -1304,7 +1304,7 @@ static void MenuList_DrawString(int x, int y, int flags, rc.left = x; rc.right = x + column->width - 1; rc.top = y + 1; - rc.bottom = y + CHAR_HEIGHT + 1; + rc.bottom = y + CONCHAR_HEIGHT + 1; if ((column->uiFlags & UI_CENTER) == UI_CENTER) { x += column->width / 2 - 1; @@ -1518,14 +1518,14 @@ static void Slider_Free(menuSlider_t *s) static void Slider_Init(menuSlider_t *s) { - int len = strlen(s->generic.name) * CHAR_WIDTH; + int len = strlen(s->generic.name) * CONCHAR_WIDTH; s->generic.rect.x = s->generic.x + LCOLUMN_OFFSET - len; s->generic.rect.y = s->generic.y; s->generic.rect.width = (RCOLUMN_OFFSET - LCOLUMN_OFFSET) + - len + (SLIDER_RANGE + 2) * CHAR_WIDTH; - s->generic.rect.height = CHAR_HEIGHT; + len + (SLIDER_RANGE + 2) * CONCHAR_WIDTH; + s->generic.rect.height = CONCHAR_HEIGHT; } static menuSound_t Slider_Click(menuSlider_t *s) @@ -1536,31 +1536,31 @@ static menuSound_t Slider_Click(menuSlider_t *s) pos = Q_clipf((s->curvalue - s->minvalue) / (s->maxvalue - s->minvalue), 0, 1); - x = CHAR_WIDTH + (SLIDER_RANGE - 1) * CHAR_WIDTH * pos; + x = CONCHAR_WIDTH + (SLIDER_RANGE - 1) * CONCHAR_WIDTH * pos; // click left of thumb rect.x = s->generic.x + RCOLUMN_OFFSET; rect.y = s->generic.y; rect.width = x; - rect.height = CHAR_HEIGHT; + rect.height = CONCHAR_HEIGHT; if (UI_CursorInRect(&rect)) return Slider_DoSlide(s, -1); // click on thumb rect.x = s->generic.x + RCOLUMN_OFFSET + x; rect.y = s->generic.y; - rect.width = CHAR_WIDTH; - rect.height = CHAR_HEIGHT; + rect.width = CONCHAR_WIDTH; + rect.height = CONCHAR_HEIGHT; if (UI_CursorInRect(&rect)) { uis.mouseTracker = &s->generic; return QMS_SILENT; } // click right of thumb - rect.x = s->generic.x + RCOLUMN_OFFSET + x + CHAR_WIDTH; + rect.x = s->generic.x + RCOLUMN_OFFSET + x + CONCHAR_WIDTH; rect.y = s->generic.y; - rect.width = (SLIDER_RANGE + 1) * CHAR_WIDTH - x; - rect.height = CHAR_HEIGHT; + rect.width = (SLIDER_RANGE + 1) * CONCHAR_WIDTH - x; + rect.height = CONCHAR_HEIGHT; if (UI_CursorInRect(&rect)) return Slider_DoSlide(s, 1); @@ -1575,7 +1575,7 @@ static menuSound_t Slider_MouseMove(menuSlider_t *s) if (uis.mouseTracker != &s->generic) return QMS_NOTHANDLED; - pos = (uis.mouseCoords[0] - (s->generic.x + RCOLUMN_OFFSET + CHAR_WIDTH)) * (1.0f / (SLIDER_RANGE * CHAR_WIDTH)); + pos = (uis.mouseCoords[0] - (s->generic.x + RCOLUMN_OFFSET + CONCHAR_WIDTH)) * (1.0f / (SLIDER_RANGE * CONCHAR_WIDTH)); value = Q_clipf(pos, 0, 1) * (s->maxvalue - s->minvalue); steps = Q_rint(value / s->step); @@ -1648,13 +1648,13 @@ static void Slider_Draw(menuSlider_t *s) UI_DrawChar(s->generic.x + RCOLUMN_OFFSET, s->generic.y, flags | UI_LEFT, 128); for (i = 0; i < SLIDER_RANGE; i++) - UI_DrawChar(RCOLUMN_OFFSET + s->generic.x + i * CHAR_WIDTH + CHAR_WIDTH, s->generic.y, flags | UI_LEFT, 129); + UI_DrawChar(RCOLUMN_OFFSET + s->generic.x + i * CONCHAR_WIDTH + CONCHAR_WIDTH, s->generic.y, flags | UI_LEFT, 129); - UI_DrawChar(RCOLUMN_OFFSET + s->generic.x + i * CHAR_WIDTH + CHAR_WIDTH, s->generic.y, flags | UI_LEFT, 130); + UI_DrawChar(RCOLUMN_OFFSET + s->generic.x + i * CONCHAR_WIDTH + CONCHAR_WIDTH, s->generic.y, flags | UI_LEFT, 130); pos = Q_clipf((s->curvalue - s->minvalue) / (s->maxvalue - s->minvalue), 0, 1); - UI_DrawChar(CHAR_WIDTH + RCOLUMN_OFFSET + s->generic.x + (SLIDER_RANGE - 1) * CHAR_WIDTH * pos, s->generic.y, flags | UI_LEFT, 131); + UI_DrawChar(CONCHAR_WIDTH + RCOLUMN_OFFSET + s->generic.x + (SLIDER_RANGE - 1) * CONCHAR_WIDTH * pos, s->generic.y, flags | UI_LEFT, 131); } /* @@ -2082,7 +2082,7 @@ menuSound_t Menu_AdjustCursor(menuFrameWork_t *m, int dir) static void Menu_DrawStatus(menuFrameWork_t *menu) { - int linewidth = uis.width / CHAR_WIDTH; + int linewidth = uis.width / CONCHAR_WIDTH; int x, y, l, count; char *txt, *p; int lens[8]; @@ -2116,11 +2116,11 @@ static void Menu_DrawStatus(menuFrameWork_t *menu) lens[count++] = x; - R_DrawFill8(0, menu->y2 - count * CHAR_HEIGHT, uis.width, count * CHAR_HEIGHT, 4); + R_DrawFill8(0, menu->y2 - count * CONCHAR_HEIGHT, uis.width, count * CONCHAR_HEIGHT, 4); for (l = 0; l < count; l++) { - x = (uis.width - lens[l] * CHAR_WIDTH) / 2; - y = menu->y2 - (count - l) * CHAR_HEIGHT; + x = (uis.width - lens[l] * CONCHAR_WIDTH) / 2; + y = menu->y2 - (count - l) * CONCHAR_HEIGHT; R_DrawString(x, y, 0, lens[l], ptrs[l], uis.fontHandle); } } diff --git a/src/client/ui/playerconfig.c b/src/client/ui/playerconfig.c index a5867c141..bda423565 100644 --- a/src/client/ui/playerconfig.c +++ b/src/client/ui/playerconfig.c @@ -150,7 +150,7 @@ static void Size(menuFrameWork_t *self) m_player.refdef.width, m_player.refdef.height); if (uis.width < 800 && uis.width >= 640) { - x -= CHAR_WIDTH * 10; + x -= CONCHAR_WIDTH * 10; } if (m_player.menu.banner) { @@ -161,7 +161,7 @@ static void Size(menuFrameWork_t *self) } if (uis.width < 640) { - x -= CHAR_WIDTH * 10; + x -= CONCHAR_WIDTH * 10; m_player.hand.generic.name = "hand"; } else { m_player.hand.generic.name = "handedness"; diff --git a/src/client/ui/servers.c b/src/client/ui/servers.c index 0be614446..20fd8d1f7 100644 --- a/src/client/ui/servers.c +++ b/src/client/ui/servers.c @@ -899,52 +899,52 @@ static void SizeCompact(void) // server list // m_servers.list.generic.x = 0; - m_servers.list.generic.y = CHAR_HEIGHT; - m_servers.list.generic.height = uis.height / 2 - CHAR_HEIGHT; + m_servers.list.generic.y = CONCHAR_HEIGHT; + m_servers.list.generic.height = uis.height / 2 - CONCHAR_HEIGHT; - m_servers.list.columns[0].width = w - 10 * CHAR_WIDTH - MLIST_PADDING * 2; + m_servers.list.columns[0].width = w - 10 * CONCHAR_WIDTH - MLIST_PADDING * 2; m_servers.list.columns[1].width = 0; m_servers.list.columns[2].width = 0; - m_servers.list.columns[3].width = 7 * CHAR_WIDTH + MLIST_PADDING; - m_servers.list.columns[4].width = 3 * CHAR_WIDTH + MLIST_PADDING; + m_servers.list.columns[3].width = 7 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.list.columns[4].width = 3 * CONCHAR_WIDTH + MLIST_PADDING; // // player list // m_servers.players.generic.x = 0; m_servers.players.generic.y = uis.height / 2 + 1; - m_servers.players.generic.height = (uis.height + 1) / 2 - CHAR_HEIGHT - 2; + m_servers.players.generic.height = (uis.height + 1) / 2 - CONCHAR_HEIGHT - 2; - m_servers.players.columns[0].width = 3 * CHAR_WIDTH + MLIST_PADDING; - m_servers.players.columns[1].width = 3 * CHAR_WIDTH + MLIST_PADDING; - m_servers.players.columns[2].width = w - 6 * CHAR_WIDTH - MLIST_PADDING * 2; + m_servers.players.columns[0].width = 3 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.players.columns[1].width = 3 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.players.columns[2].width = w - 6 * CONCHAR_WIDTH - MLIST_PADDING * 2; m_servers.players.mlFlags |= MLF_SCROLLBAR; } static void SizeFull(void) { - int w = uis.width - MLIST_SCROLLBAR_WIDTH - 21 * CHAR_WIDTH - MLIST_PADDING * 3; + int w = uis.width - MLIST_SCROLLBAR_WIDTH - 21 * CONCHAR_WIDTH - MLIST_PADDING * 3; // // server list // m_servers.list.generic.x = 0; - m_servers.list.generic.y = CHAR_HEIGHT; - m_servers.list.generic.height = uis.height / 2 - CHAR_HEIGHT; + m_servers.list.generic.y = CONCHAR_HEIGHT; + m_servers.list.generic.height = uis.height / 2 - CONCHAR_HEIGHT; - m_servers.list.columns[0].width = w - 26 * CHAR_WIDTH - MLIST_PADDING * 4; - m_servers.list.columns[1].width = 8 * CHAR_WIDTH + MLIST_PADDING; - m_servers.list.columns[2].width = 8 * CHAR_WIDTH + MLIST_PADDING; - m_servers.list.columns[3].width = 7 * CHAR_WIDTH + MLIST_PADDING; - m_servers.list.columns[4].width = 3 * CHAR_WIDTH + MLIST_PADDING; + m_servers.list.columns[0].width = w - 26 * CONCHAR_WIDTH - MLIST_PADDING * 4; + m_servers.list.columns[1].width = 8 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.list.columns[2].width = 8 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.list.columns[3].width = 7 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.list.columns[4].width = 3 * CONCHAR_WIDTH + MLIST_PADDING; // // server info // m_servers.info.generic.x = 0; m_servers.info.generic.y = uis.height / 2 + 1; - m_servers.info.generic.height = (uis.height + 1) / 2 - CHAR_HEIGHT - 2; + m_servers.info.generic.height = (uis.height + 1) / 2 - CONCHAR_HEIGHT - 2; m_servers.info.columns[0].width = w / 3; m_servers.info.columns[1].width = w - w / 3; @@ -953,12 +953,12 @@ static void SizeFull(void) // player list // m_servers.players.generic.x = w + MLIST_SCROLLBAR_WIDTH; - m_servers.players.generic.y = CHAR_HEIGHT; - m_servers.players.generic.height = uis.height - CHAR_HEIGHT * 2 - 1; + m_servers.players.generic.y = CONCHAR_HEIGHT; + m_servers.players.generic.height = uis.height - CONCHAR_HEIGHT * 2 - 1; - m_servers.players.columns[0].width = 3 * CHAR_WIDTH + MLIST_PADDING; - m_servers.players.columns[1].width = 3 * CHAR_WIDTH + MLIST_PADDING; - m_servers.players.columns[2].width = 15 * CHAR_WIDTH + MLIST_PADDING; + m_servers.players.columns[0].width = 3 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.players.columns[1].width = 3 * CONCHAR_WIDTH + MLIST_PADDING; + m_servers.players.columns[2].width = 15 * CONCHAR_WIDTH + MLIST_PADDING; m_servers.players.mlFlags &= ~MLF_SCROLLBAR; } @@ -1017,22 +1017,22 @@ static void DrawStatus(void) else w = uis.width; - R_DrawFill8(0, uis.height - CHAR_HEIGHT, w, CHAR_HEIGHT, 4); - R_DrawFill8(w, uis.height - CHAR_HEIGHT, uis.width - w, CHAR_HEIGHT, 0); + R_DrawFill8(0, uis.height - CONCHAR_HEIGHT, w, CONCHAR_HEIGHT, 4); + R_DrawFill8(w, uis.height - CONCHAR_HEIGHT, uis.width - w, CONCHAR_HEIGHT, 0); if (m_servers.status_c) - UI_DrawString(uis.width / 2, uis.height - CHAR_HEIGHT, UI_CENTER, m_servers.status_c); + UI_DrawString(uis.width / 2, uis.height - CONCHAR_HEIGHT, UI_CENTER, m_servers.status_c); if (uis.width < 800) return; if (m_servers.list.numItems) - UI_DrawString(uis.width, uis.height - CHAR_HEIGHT, UI_RIGHT, m_servers.status_r); + UI_DrawString(uis.width, uis.height - CONCHAR_HEIGHT, UI_RIGHT, m_servers.status_r); if (m_servers.list.numItems && m_servers.list.curvalue >= 0) { serverslot_t *slot = m_servers.list.items[m_servers.list.curvalue]; if (slot->status > SLOT_PENDING) { - UI_DrawString(0, uis.height - CHAR_HEIGHT, UI_LEFT, slot->hostname); + UI_DrawString(0, uis.height - CONCHAR_HEIGHT, UI_LEFT, slot->hostname); } } } diff --git a/src/client/ui/ui.c b/src/client/ui/ui.c index b80b1bee9..307be2d0e 100644 --- a/src/client/ui/ui.c +++ b/src/client/ui/ui.c @@ -305,9 +305,9 @@ bool UI_CursorInRect(const vrect_t *rect) void UI_DrawString(int x, int y, int flags, const char *string) { if ((flags & UI_CENTER) == UI_CENTER) { - x -= strlen(string) * CHAR_WIDTH / 2; + x -= strlen(string) * CONCHAR_WIDTH / 2; } else if (flags & UI_RIGHT) { - x -= strlen(string) * CHAR_WIDTH; + x -= strlen(string) * CONCHAR_WIDTH; } R_DrawString(x, y, flags, MAX_STRING_CHARS, string, uis.fontHandle); @@ -320,8 +320,8 @@ void UI_DrawChar(int x, int y, int flags, int ch) void UI_StringDimensions(vrect_t *rc, int flags, const char *string) { - rc->height = CHAR_HEIGHT; - rc->width = CHAR_WIDTH * strlen(string); + rc->height = CONCHAR_HEIGHT; + rc->width = CONCHAR_WIDTH * strlen(string); if ((flags & UI_CENTER) == UI_CENTER) { rc->x -= rc->width / 2; diff --git a/src/client/ui/ui.h b/src/client/ui/ui.h index c3be79296..81bdc61d8 100644 --- a/src/client/ui/ui.h +++ b/src/client/ui/ui.h @@ -75,12 +75,12 @@ typedef enum { QMS_BEEP } menuSound_t; -#define RCOLUMN_OFFSET (CHAR_WIDTH * 2) +#define RCOLUMN_OFFSET (CONCHAR_WIDTH * 2) #define LCOLUMN_OFFSET -RCOLUMN_OFFSET #define GENERIC_SPACING(x) ((x) + (x) / 4) -#define MENU_SPACING GENERIC_SPACING(CHAR_HEIGHT) +#define MENU_SPACING GENERIC_SPACING(CONCHAR_HEIGHT) #define DOUBLE_CLICK_DELAY 300 @@ -169,9 +169,9 @@ typedef struct { #define MAX_COLUMNS 8 -#define MLIST_SPACING GENERIC_SPACING(CHAR_HEIGHT) +#define MLIST_SPACING GENERIC_SPACING(CONCHAR_HEIGHT) #define MLIST_BORDER_WIDTH 1 -#define MLIST_SCROLLBAR_WIDTH GENERIC_SPACING(CHAR_WIDTH) +#define MLIST_SCROLLBAR_WIDTH GENERIC_SPACING(CONCHAR_WIDTH) #define MLIST_PRESTEP 3 #define MLIST_PADDING (MLIST_PRESTEP*2) diff --git a/src/common/field.c b/src/common/field.c index 8e1ac8ecf..3cb8fadbd 100644 --- a/src/common/field.c +++ b/src/common/field.c @@ -278,7 +278,7 @@ int IF_Draw(const inputField_t *field, int x, int y, int flags, qhandle_t font) // draw blinking cursor if (flags & UI_DRAWCURSOR && com_localTime & BIT(8)) { - R_DrawChar(x + cursorPos * CHAR_WIDTH, y, flags, + R_DrawChar(x + cursorPos * CONCHAR_WIDTH, y, flags, Key_GetOverstrikeMode() ? 11 : '_', font); } diff --git a/src/refresh/draw.c b/src/refresh/draw.c index 66623723a..a07ba1e2c 100644 --- a/src/refresh/draw.c +++ b/src/refresh/draw.c @@ -364,15 +364,15 @@ static inline void draw_char(int x, int y, int flags, int c, const image_t *imag if (flags & UI_DROPSHADOW && c != 0x83) { uint32_t black = draw.colors[0].u32 & U32_ALPHA; - GL_StretchPic(x + 1, y + 1, CHAR_WIDTH, CHAR_HEIGHT, s, t, + GL_StretchPic(x + 1, y + 1, CONCHAR_WIDTH, CONCHAR_HEIGHT, s, t, s + 0.0625f, t + 0.0625f, black, image); if (gl_fontshadow->integer > 1) - GL_StretchPic(x + 2, y + 2, CHAR_WIDTH, CHAR_HEIGHT, s, t, + GL_StretchPic(x + 2, y + 2, CONCHAR_WIDTH, CONCHAR_HEIGHT, s, t, s + 0.0625f, t + 0.0625f, black, image); } - GL_StretchPic(x, y, CHAR_WIDTH, CHAR_HEIGHT, s, t, + GL_StretchPic(x, y, CONCHAR_WIDTH, CONCHAR_HEIGHT, s, t, s + 0.0625f, t + 0.0625f, draw.colors[c >> 7].u32, image); } @@ -394,7 +394,7 @@ int R_DrawString(int x, int y, int flags, size_t maxlen, const char *s, qhandle_ while (maxlen-- && *s) { byte c = *s++; draw_char(x, y, flags, c, image); - x += CHAR_WIDTH; + x += CONCHAR_WIDTH; } return x; diff --git a/src/server/mvd/game.c b/src/server/mvd/game.c index 62d5d1bf0..d15846309 100644 --- a/src/server/mvd/game.c +++ b/src/server/mvd/game.c @@ -53,7 +53,7 @@ LAYOUTS // clients per screen page #define PAGE_CLIENTS 16 -#define VER_OFS (272 - (int)(sizeof(VERSION) - 1) * CHAR_WIDTH) +#define VER_OFS (272 - (int)(sizeof(VERSION) - 1) * CONCHAR_WIDTH) static void MVD_LayoutClients(mvd_client_t *client) { From 318bf104a789a0fef163c9b98118e1f09bc8422d Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 23 Dec 2024 21:05:01 +0300 Subject: [PATCH 64/79] Better protect against multiple nextserver commands. Users could kill the server by sending multiple nextserver commands with crafted spawncount. --- src/server/server.h | 1 + src/server/user.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server/server.h b/src/server/server.h index 0520560ac..bd2ffbdcf 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -152,6 +152,7 @@ typedef struct { typedef struct { server_state_t state; // precache commands are only valid during load int spawncount; // random number generated each server spawn + bool nextserver_pending; #if USE_FPS int framerate; diff --git a/src/server/user.c b/src/server/user.c index 6e3986207..63a37c8ab 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -775,7 +775,10 @@ static void SV_NextServer_f(void) if (Q_atoi(Cmd_Argv(1)) != sv.spawncount) return; // leftover from last server - sv.spawncount ^= 1; // make sure another doesn't sneak in + if (sv.nextserver_pending) + return; + + sv.nextserver_pending = true; // make sure another doesn't sneak in const char *v = Cvar_VariableString("nextserver"); if (*v) { From 29a72c5111b346121bef28bc33ab02931fec294c Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 23 Dec 2024 23:55:40 +0300 Subject: [PATCH 65/79] Change debug level comparisons to >= for readability. --- inc/common/common.h | 10 ++++---- src/client/client.h | 4 ++-- src/client/main.c | 12 +++++----- src/client/parse.c | 52 +++++++++++++++++++++--------------------- src/server/entities.c | 2 +- src/server/mvd/parse.c | 28 +++++++++++------------ src/server/send.c | 18 +++++++-------- src/server/server.h | 2 +- 8 files changed, 64 insertions(+), 64 deletions(-) diff --git a/inc/common/common.h b/inc/common/common.h index 8b3025965..fe93497f5 100644 --- a/inc/common/common.h +++ b/inc/common/common.h @@ -126,19 +126,19 @@ void Sys_Printf(const char *fmt, ...) q_printf(1, 2); #if USE_DEBUG #define COM_DEVELOPER (developer->integer) #define Com_DPrintf(...) \ - do { if (developer && developer->integer > 0) \ + do { if (developer && developer->integer >= 1) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) #define Com_DDPrintf(...) \ - do { if (developer && developer->integer > 1) \ + do { if (developer && developer->integer >= 2) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) #define Com_DDDPrintf(...) \ - do { if (developer && developer->integer > 2) \ + do { if (developer && developer->integer >= 3) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) #define Com_DDDDPrintf(...) \ - do { if (developer && developer->integer > 3) \ + do { if (developer && developer->integer >= 4) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) #define Com_DWPrintf(...) \ - do { if (developer && developer->integer > 0) \ + do { if (developer && developer->integer >= 1) \ Com_LPrintf(PRINT_WARNING, __VA_ARGS__); } while (0) #else #define COM_DEVELOPER 0 diff --git a/src/client/client.h b/src/client/client.h index 767054fc1..c7731139c 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -544,10 +544,10 @@ extern cvar_t *cl_nolerp; #if USE_DEBUG #define SHOWNET(level, ...) \ - do { if (cl_shownet->integer > level) \ + do { if (cl_shownet->integer >= level) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) #define SHOWCLAMP(level, ...) \ - do { if (cl_showclamp->integer > level) \ + do { if (cl_showclamp->integer >= level) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) #define SHOWMISS(...) \ do { if (cl_showmiss->integer) \ diff --git a/src/client/main.c b/src/client/main.c index 484fa4151..e7db10b74 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -2924,35 +2924,35 @@ static void CL_SetClientTime(void) prevtime = cl.servertime - CL_FRAMETIME; if (cl.time > cl.servertime) { - SHOWCLAMP(1, "high clamp %i\n", cl.time - cl.servertime); + SHOWCLAMP(2, "high clamp %i\n", cl.time - cl.servertime); cl.time = cl.servertime; cl.lerpfrac = 1.0f; } else if (cl.time < prevtime) { - SHOWCLAMP(1, "low clamp %i\n", prevtime - cl.time); + SHOWCLAMP(2, "low clamp %i\n", prevtime - cl.time); cl.time = prevtime; cl.lerpfrac = 0; } else { cl.lerpfrac = (cl.time - prevtime) * CL_1_FRAMETIME; } - SHOWCLAMP(2, "time %d %d, lerpfrac %.3f\n", + SHOWCLAMP(3, "time %d %d, lerpfrac %.3f\n", cl.time, cl.servertime, cl.lerpfrac); #if USE_FPS prevtime = cl.keyservertime - BASE_FRAMETIME; if (cl.keytime > cl.keyservertime) { - SHOWCLAMP(1, "high keyclamp %i\n", cl.keytime - cl.keyservertime); + SHOWCLAMP(2, "high keyclamp %i\n", cl.keytime - cl.keyservertime); cl.keytime = cl.keyservertime; cl.keylerpfrac = 1.0f; } else if (cl.keytime < prevtime) { - SHOWCLAMP(1, "low keyclamp %i\n", prevtime - cl.keytime); + SHOWCLAMP(2, "low keyclamp %i\n", prevtime - cl.keytime); cl.keytime = prevtime; cl.keylerpfrac = 0; } else { cl.keylerpfrac = (cl.keytime - prevtime) * BASE_1_FRAMETIME; } - SHOWCLAMP(2, "keytime %d %d keylerpfrac %.3f\n", + SHOWCLAMP(3, "keytime %d %d keylerpfrac %.3f\n", cl.keytime, cl.keyservertime, cl.keylerpfrac); #endif } diff --git a/src/client/parse.c b/src/client/parse.c index cf0342d4c..c6ee77f07 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -45,7 +45,7 @@ static void CL_ParseDeltaEntity(server_frame_t *frame, frame->numEntities++; #if USE_DEBUG - if (cl_shownet->integer > 2 && bits) { + if (cl_shownet->integer >= 3 && bits) { MSG_ShowDeltaEntityBits(bits); Com_LPrintf(PRINT_DEVELOPER, "\n"); } @@ -112,7 +112,7 @@ static void CL_ParsePacketEntities(const server_frame_t *oldframe, server_frame_ while (oldnum < newnum) { // one or more entities from the old packet are unchanged - SHOWNET(3, " unchanged:%i\n", oldnum); + SHOWNET(4, " unchanged:%i\n", oldnum); CL_ParseDeltaEntity(frame, oldnum, oldstate, 0); oldindex++; @@ -128,7 +128,7 @@ static void CL_ParsePacketEntities(const server_frame_t *oldframe, server_frame_ if (bits & U_REMOVE) { // the entity present in oldframe is not in the current frame - SHOWNET(2, "%3u:remove:%i\n", readcount, newnum); + SHOWNET(3, "%3u:remove:%i\n", readcount, newnum); if (oldnum != newnum) { Com_DPrintf("U_REMOVE: oldnum != newnum\n"); } @@ -150,10 +150,10 @@ static void CL_ParsePacketEntities(const server_frame_t *oldframe, server_frame_ if (oldnum == newnum) { // delta from previous state - SHOWNET(2, "%3u:delta:%i ", readcount, newnum); + SHOWNET(3, "%3u:delta:%i ", readcount, newnum); CL_ParseDeltaEntity(frame, newnum, oldstate, bits); if (!bits) { - SHOWNET(2, "\n"); + SHOWNET(3, "\n"); } oldindex++; @@ -170,10 +170,10 @@ static void CL_ParsePacketEntities(const server_frame_t *oldframe, server_frame_ if (oldnum > newnum) { // delta from baseline - SHOWNET(2, "%3u:baseline:%i ", readcount, newnum); + SHOWNET(3, "%3u:baseline:%i ", readcount, newnum); CL_ParseDeltaEntity(frame, newnum, &cl.baselines[newnum], bits); if (!bits) { - SHOWNET(2, "\n"); + SHOWNET(3, "\n"); } continue; } @@ -182,7 +182,7 @@ static void CL_ParsePacketEntities(const server_frame_t *oldframe, server_frame_ // any remaining entities in the old frame are copied over while (oldnum != MAX_EDICTS) { // one or more entities from the old packet are unchanged - SHOWNET(3, " unchanged:%i\n", oldnum); + SHOWNET(4, " unchanged:%i\n", oldnum); CL_ParseDeltaEntity(frame, oldnum, oldstate, 0); oldindex++; @@ -316,7 +316,7 @@ static void CL_ParseFrame(int extrabits) frame.areabytes = 0; } - SHOWNET(2, "%3u:playerinfo\n", msg_read.readcount); + SHOWNET(3, "%3u:playerinfo\n", msg_read.readcount); if (cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT) { if (MSG_ReadByte() != svc_playerinfo) { @@ -332,7 +332,7 @@ static void CL_ParseFrame(int extrabits) if (cls.serverProtocol > PROTOCOL_VERSION_DEFAULT) { MSG_ParseDeltaPlayerstate_Enhanced(from, &frame.ps, bits, extraflags, cl.psFlags); #if USE_DEBUG - if (cl_shownet->integer > 2 && (bits || extraflags)) { + if (cl_shownet->integer >= 3 && (bits || extraflags)) { Com_LPrintf(PRINT_DEVELOPER, " "); MSG_ShowDeltaPlayerstateBits_Enhanced(bits, extraflags); Com_LPrintf(PRINT_DEVELOPER, "\n"); @@ -358,7 +358,7 @@ static void CL_ParseFrame(int extrabits) } else { MSG_ParseDeltaPlayerstate_Default(from, &frame.ps, bits, cl.psFlags); #if USE_DEBUG - if (cl_shownet->integer > 2 && bits) { + if (cl_shownet->integer >= 3 && bits) { Com_LPrintf(PRINT_DEVELOPER, " "); MSG_ShowDeltaPlayerstateBits_Default(bits); Com_LPrintf(PRINT_DEVELOPER, "\n"); @@ -367,7 +367,7 @@ static void CL_ParseFrame(int extrabits) frame.clientNum = cl.clientNum; } - SHOWNET(2, "%3u:packetentities\n", msg_read.readcount); + SHOWNET(3, "%3u:packetentities\n", msg_read.readcount); // parse packetentities if (cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT) { @@ -382,7 +382,7 @@ static void CL_ParseFrame(int extrabits) cl.frames[currentframe & UPDATE_MASK] = frame; #if USE_DEBUG - if (cl_shownet->integer > 2) { + if (cl_shownet->integer >= 3) { int seq = cls.netchan.incoming_acknowledged & CMD_MASK; int rtt = cls.demo.playback ? 0 : cls.realtime - cl.history[seq].sent; Com_LPrintf(PRINT_DEVELOPER, "%3u:frame:%d delta:%d rtt:%d\n", @@ -443,7 +443,7 @@ static void CL_ParseConfigstring(int index) maxlen = Com_ConfigstringSize(&cl.csr, index); len = MSG_ReadString(s, maxlen); - SHOWNET(2, " %d \"%s\"\n", index, Com_MakePrintable(s)); + SHOWNET(3, " %d \"%s\"\n", index, Com_MakePrintable(s)); if (len >= maxlen) { Com_WPrintf( @@ -473,7 +473,7 @@ static void CL_ParseBaseline(int index, uint64_t bits) } #if USE_DEBUG - if (cl_shownet->integer > 2) { + if (cl_shownet->integer >= 3) { Com_LPrintf(PRINT_DEVELOPER, " baseline:%i ", index); MSG_ShowDeltaEntityBits(bits); Com_LPrintf(PRINT_DEVELOPER, "\n"); @@ -957,7 +957,7 @@ static void CL_ParseStartSoundPacket(void) if (flags & SND_POS) CL_ReadPos(snd.pos); - SHOWNET(2, " %s\n", cl.configstrings[cl.csr.sounds + snd.index]); + SHOWNET(3, " %s\n", cl.configstrings[cl.csr.sounds + snd.index]); } static void CL_ParseReconnect(void) @@ -1046,7 +1046,7 @@ static void CL_ParsePrint(void) level = MSG_ReadByte(); MSG_ReadString(s, sizeof(s)); - SHOWNET(2, " %i \"%s\"\n", level, Com_MakePrintable(s)); + SHOWNET(3, " %i \"%s\"\n", level, Com_MakePrintable(s)); if (level != PRINT_CHAT) { if (cl.csr.extended && (level == PRINT_TYPEWRITER || level == PRINT_CENTER)) @@ -1107,7 +1107,7 @@ static void CL_ParseCenterPrint(void) char s[MAX_STRING_CHARS]; MSG_ReadString(s, sizeof(s)); - SHOWNET(2, " \"%s\"\n", Com_MakePrintable(s)); + SHOWNET(3, " \"%s\"\n", Com_MakePrintable(s)); SCR_CenterPrint(s, false); if (!cls.demo.playback && cl.serverstate != ss_broadcast) { @@ -1121,14 +1121,14 @@ static void CL_ParseStuffText(void) char s[MAX_STRING_CHARS]; MSG_ReadString(s, sizeof(s)); - SHOWNET(2, " \"%s\"\n", Com_MakePrintable(s)); + SHOWNET(3, " \"%s\"\n", Com_MakePrintable(s)); Cbuf_AddText(&cl_cmdbuf, s); } static void CL_ParseLayout(void) { MSG_ReadString(cl.layout, sizeof(cl.layout)); - SHOWNET(2, " \"%s\"\n", Com_MakePrintable(cl.layout)); + SHOWNET(3, " \"%s\"\n", Com_MakePrintable(cl.layout)); } static void CL_ParseInventory(void) @@ -1270,7 +1270,7 @@ void CL_ParseServerMessage(void) #if USE_DEBUG if (cl_shownet->integer == 1) { Com_LPrintf(PRINT_DEVELOPER, "%u ", msg_read.cursize); - } else if (cl_shownet->integer > 1) { + } else if (cl_shownet->integer >= 2) { Com_LPrintf(PRINT_DEVELOPER, "------------------\n"); } #endif @@ -1283,7 +1283,7 @@ void CL_ParseServerMessage(void) while (1) { readcount = msg_read.readcount; if (readcount == msg_read.cursize) { - SHOWNET(1, "%3u:END OF MESSAGE\n", readcount); + SHOWNET(2, "%3u:END OF MESSAGE\n", readcount); break; } @@ -1294,7 +1294,7 @@ void CL_ParseServerMessage(void) extrabits = cmd >> SVCMD_BITS; cmd &= SVCMD_MASK; - SHOWNET(1, "%3u:%s\n", msg_read.readcount - 1, MSG_ServerCommandString(cmd)); + SHOWNET(2, "%3u:%s\n", msg_read.readcount - 1, MSG_ServerCommandString(cmd)); // other commands switch (cmd) { @@ -1444,7 +1444,7 @@ bool CL_SeekDemoMessage(void) #if USE_DEBUG if (cl_shownet->integer == 1) { Com_LPrintf(PRINT_DEVELOPER, "%u ", msg_read.cursize); - } else if (cl_shownet->integer > 1) { + } else if (cl_shownet->integer >= 2) { Com_LPrintf(PRINT_DEVELOPER, "------------------\n"); } #endif @@ -1456,12 +1456,12 @@ bool CL_SeekDemoMessage(void) // while (1) { if (msg_read.readcount == msg_read.cursize) { - SHOWNET(1, "%3u:END OF MESSAGE\n", msg_read.readcount); + SHOWNET(2, "%3u:END OF MESSAGE\n", msg_read.readcount); break; } cmd = MSG_ReadByte(); - SHOWNET(1, "%3u:%s\n", msg_read.readcount - 1, MSG_ServerCommandString(cmd)); + SHOWNET(2, "%3u:%s\n", msg_read.readcount - 1, MSG_ServerCommandString(cmd)); // other commands switch (cmd) { diff --git a/src/server/entities.c b/src/server/entities.c index b8984f811..497bc10c3 100644 --- a/src/server/entities.c +++ b/src/server/entities.c @@ -49,7 +49,7 @@ static bool SV_TruncPacketEntities(client_t *client, const client_frame_t *from, if (!sv_trunc_packet_entities->integer || client->netchan.type) return false; - SV_DPrintf(0, "Truncating frame %d at %u bytes for %s\n", + SV_DPrintf(1, "Truncating frame %d at %u bytes for %s\n", client->framenum, msg_write.cursize, client->name); if (!from) diff --git a/src/server/mvd/parse.c b/src/server/mvd/parse.c index 576bf811b..6e63114d6 100644 --- a/src/server/mvd/parse.c +++ b/src/server/mvd/parse.c @@ -27,7 +27,7 @@ static bool match_ended_hack; #if USE_DEBUG #define SHOWNET(level, ...) \ - do { if (mvd_shownet->integer > level) \ + do { if (mvd_shownet->integer >= level) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) static const char *MVD_ServerCommandString(int cmd) @@ -377,7 +377,7 @@ static void MVD_ParseUnicast(mvd_t *mvd, bool reliable, int extrabits) while (msg_read.readcount < last) { cmd = MSG_ReadByte(); - SHOWNET(1, "%3u:%s\n", msg_read.readcount - 1, MSG_ServerCommandString(cmd)); + SHOWNET(2, "%3u:%s\n", msg_read.readcount - 1, MSG_ServerCommandString(cmd)); switch (cmd) { case svc_layout: @@ -393,7 +393,7 @@ static void MVD_ParseUnicast(mvd_t *mvd, bool reliable, int extrabits) MVD_UnicastStuff(mvd, reliable, player); break; default: - SHOWNET(1, "%3u:SKIPPING UNICAST\n", msg_read.readcount - 1); + SHOWNET(2, "%3u:SKIPPING UNICAST\n", msg_read.readcount - 1); // send remaining data and return data = msg_read.data + msg_read.readcount - 1; length = last - msg_read.readcount + 1; @@ -404,7 +404,7 @@ static void MVD_ParseUnicast(mvd_t *mvd, bool reliable, int extrabits) } } - SHOWNET(1, "%3u:END OF UNICAST\n", msg_read.readcount); + SHOWNET(2, "%3u:END OF UNICAST\n", msg_read.readcount); if (msg_read.readcount > last) { MVD_Destroyf(mvd, "%s: read past end of unicast", __func__); @@ -671,7 +671,7 @@ static void MVD_ParsePacketEntities(mvd_t *mvd) ent = &mvd->edicts[number]; #if USE_DEBUG - if (mvd_shownet->integer > 2) { + if (mvd_shownet->integer >= 3) { Com_LPrintf(PRINT_DEVELOPER, "%3u:%s:%d ", readcount, ent->inuse ? "delta" : "baseline", number); MSG_ShowDeltaEntityBits(bits); @@ -691,7 +691,7 @@ static void MVD_ParsePacketEntities(mvd_t *mvd) // shuffle current origin to old if removed if (bits & U_REMOVE) { - SHOWNET(2, "%3u:remove:%d\n", readcount, number); + SHOWNET(3, "%3u:remove:%d\n", readcount, number); if (!(ent->s.renderfx & RF_BEAM)) { VectorCopy(ent->s.origin, ent->s.old_origin); } @@ -745,7 +745,7 @@ static void MVD_ParsePacketPlayers(mvd_t *mvd) } #if USE_DEBUG - if (mvd_shownet->integer > 2) { + if (mvd_shownet->integer >= 3) { Com_LPrintf(PRINT_DEVELOPER, "%3u:%s:%d ", readcount, player->inuse ? "delta" : "baseline", number); MSG_ShowDeltaPlayerstateBits_Packet(bits); @@ -756,7 +756,7 @@ static void MVD_ParsePacketPlayers(mvd_t *mvd) MSG_ParseDeltaPlayerstate_Packet(&player->ps, bits, mvd->psFlags); if (bits & PPS_REMOVE) { - SHOWNET(2, "%3u:remove:%d\n", readcount, number); + SHOWNET(3, "%3u:remove:%d\n", readcount, number); player->inuse = false; continue; } @@ -784,11 +784,11 @@ static void MVD_ParseFrame(mvd_t *mvd) if (!mvd->demoseeking) CM_SetPortalStates(&mvd->cm, data, length); - SHOWNET(1, "%3u:playerinfo\n", msg_read.readcount); + SHOWNET(2, "%3u:playerinfo\n", msg_read.readcount); MVD_ParsePacketPlayers(mvd); - SHOWNET(1, "%3u:packetentities\n", msg_read.readcount); + SHOWNET(2, "%3u:packetentities\n", msg_read.readcount); MVD_ParsePacketEntities(mvd); - SHOWNET(1, "%3u:frame:%u\n", msg_read.readcount, mvd->framenum); + SHOWNET(2, "%3u:frame:%u\n", msg_read.readcount, mvd->framenum); MVD_PlayerToEntityStates(mvd); // update clients now so that effects datagram that @@ -1069,7 +1069,7 @@ bool MVD_ParseMessage(mvd_t *mvd) #if USE_DEBUG if (mvd_shownet->integer == 1) { Com_LPrintf(PRINT_DEVELOPER, "%u ", msg_read.cursize); - } else if (mvd_shownet->integer > 1) { + } else if (mvd_shownet->integer >= 2) { Com_LPrintf(PRINT_DEVELOPER, "------------------\n"); } #endif @@ -1083,7 +1083,7 @@ bool MVD_ParseMessage(mvd_t *mvd) MVD_Destroyf(mvd, "Read past end of message"); } if (msg_read.readcount == msg_read.cursize) { - SHOWNET(1, "%3u:END OF MESSAGE\n", msg_read.readcount); + SHOWNET(2, "%3u:END OF MESSAGE\n", msg_read.readcount); break; } @@ -1091,7 +1091,7 @@ bool MVD_ParseMessage(mvd_t *mvd) extrabits = cmd >> SVCMD_BITS; cmd &= SVCMD_MASK; - SHOWNET(1, "%3u:%s\n", msg_read.readcount - 1, MVD_ServerCommandString(cmd)); + SHOWNET(2, "%3u:%s\n", msg_read.readcount - 1, MVD_ServerCommandString(cmd)); switch (cmd) { case mvd_serverdata: diff --git a/src/server/send.c b/src/server/send.c index 98e7eee77..fb51434ee 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -75,7 +75,7 @@ static bool SV_RateDrop(client_t *client) #endif if (total > client->rate) { - SV_DPrintf(0, "Frame %d suppressed for %s (total = %zu)\n", + SV_DPrintf(1, "Frame %d suppressed for %s (total = %zu)\n", client->framenum, client->name, total); client->frameflags |= FF_SUPPRESSED; client->suppress_count++; @@ -404,11 +404,11 @@ void SV_ClientAddMessage(client_t *client, int flags) if ((flags & MSG_COMPRESS) && (len = compress_message(client)) && len < msg_write.cursize) { client->AddMessage(client, get_compressed_data(), len, flags & MSG_RELIABLE); - SV_DPrintf(0, "Compressed %sreliable message to %s: %u into %d\n", + SV_DPrintf(1, "Compressed %sreliable message to %s: %u into %d\n", (flags & MSG_RELIABLE) ? "" : "un", client->name, msg_write.cursize, len); } else { client->AddMessage(client, msg_write.data, msg_write.cursize, flags & MSG_RELIABLE); - SV_DPrintf(1, "Added %sreliable message to %s: %u bytes\n", + SV_DPrintf(2, "Added %sreliable message to %s: %u bytes\n", (flags & MSG_RELIABLE) ? "" : "un", client->name, msg_write.cursize); } @@ -540,7 +540,7 @@ static void emit_snd(const client_t *client, const message_packet_t *msg) // check if position needs to be explicitly sent if (!(flags & SND_POS) && !check_entity(client, entnum)) { - SV_DPrintf(1, "Forcing position on entity %d for %s\n", + SV_DPrintf(2, "Forcing position on entity %d for %s\n", entnum, client->name); flags |= SND_POS; // entity is not present in frame } @@ -627,7 +627,7 @@ static void write_reliables_old(client_t *client, unsigned maxsize) int count; if (client->netchan.reliable_length) { - SV_DPrintf(1, "%s to %s: unacked\n", __func__, client->name); + SV_DPrintf(2, "%s to %s: unacked\n", __func__, client->name); return; // there is still outgoing reliable message pending } @@ -644,7 +644,7 @@ static void write_reliables_old(client_t *client, unsigned maxsize) break; } - SV_DPrintf(1, "%s to %s: writing msg %d: %d bytes\n", + SV_DPrintf(2, "%s to %s: writing msg %d: %d bytes\n", __func__, client->name, count, msg->cursize); SZ_Write(&client->netchan.message, msg->data, msg->cursize); @@ -733,7 +733,7 @@ static void write_datagram_old(client_t *client) // send over all the relevant entity_state_t // and the player_state_t if (!client->WriteFrame(client, maxsize)) { - SV_DPrintf(0, "Frame %d overflowed for %s\n", client->framenum, client->name); + SV_DPrintf(1, "Frame %d overflowed for %s\n", client->framenum, client->name); SZ_Clear(&msg_write); } @@ -999,7 +999,7 @@ void SV_SendAsyncPackets(void) // make sure all fragments are transmitted first if (netchan->fragment_pending) { cursize = Netchan_TransmitNextFragment(netchan); - SV_DPrintf(1, "%s: frag: %d\n", client->name, cursize); + SV_DPrintf(2, "%s: frag: %d\n", client->name, cursize); goto calctime; } @@ -1027,7 +1027,7 @@ void SV_SendAsyncPackets(void) if (netchan->message.cursize || netchan->reliable_ack_pending || netchan->reliable_length || retransmit) { cursize = Netchan_Transmit(netchan, 0, NULL, 1); - SV_DPrintf(1, "%s: send: %d\n", client->name, cursize); + SV_DPrintf(2, "%s: send: %d\n", client->name, cursize); calctime: SV_CalcSendTime(client, cursize); } diff --git a/src/server/server.h b/src/server/server.h index bd2ffbdcf..a09d82036 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -60,7 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_DEBUG #define SV_DPrintf(level,...) \ - do { if (sv_debug && sv_debug->integer > level) \ + do { if (sv_debug && sv_debug->integer >= level) \ Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__); } while (0) #else #define SV_DPrintf(...) From 58f200f1922778e3eaf2969a60711d3a11f0bc7b Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 24 Dec 2024 20:31:02 +0300 Subject: [PATCH 66/79] Add shadow alpha fading with distance. Also fix wrong projected point being used for shadow culling. --- doc/client.asciidoc | 6 ++++++ inc/common/bsp.h | 1 + src/client/ui/q2pro.menu | 2 +- src/common/bsp.c | 4 ++++ src/refresh/mesh.c | 33 ++++++++++++++++++++------------- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/doc/client.asciidoc b/doc/client.asciidoc index b6191fa20..babc9c923 100644 --- a/doc/client.asciidoc +++ b/doc/client.asciidoc @@ -743,6 +743,12 @@ gl_dotshading:: truly 3D-ish by simulating diffuse lighting from a fake light source. Default value is 1 (enabled). +gl_shadows:: + Enables drawing stencil shadows underneath 3D models. Default value is 0. + - 0 — disabled + - 1 — enabled, constant alpha + - 2 — enabled, alpha fade with distance + gl_saturation:: Enables grayscaling of world textures. 1 keeps original colors, 0 converts textures to grayscale format (this may save some video memory and speed up diff --git a/inc/common/bsp.h b/inc/common/bsp.h index 6c8992cf3..46e6fe242 100644 --- a/inc/common/bsp.h +++ b/inc/common/bsp.h @@ -319,6 +319,7 @@ typedef struct { cplane_t plane; float s, t; float fraction; + vec3_t pos; } lightpoint_t; void BSP_LightPoint(lightpoint_t *point, const vec3_t start, const vec3_t end, const mnode_t *headnode, int nolm_mask); diff --git a/src/client/ui/q2pro.menu b/src/client/ui/q2pro.menu index 6c3512428..b29f167ab 100644 --- a/src/client/ui/q2pro.menu +++ b/src/client/ui/q2pro.menu @@ -133,7 +133,7 @@ begin effects values "dynamic lighting" gl_dynamic "no" "yes" "only switchable" values "entity cel-shading" gl_celshading no 1x 2x 3x toggle "entity glowing" cl_noglow ~ - toggle "ground shadows" gl_shadows + values "ground shadows" gl_shadows no yes fade toggle "screen blending" gl_polyblend toggle "screen warping" gl_waterwarp toggle "grenade explosions" cl_disable_explosions ~0 diff --git a/src/common/bsp.c b/src/common/bsp.c index 2bc0d5bc7..27c3d8d97 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -981,6 +981,8 @@ void BSP_LightPoint(lightpoint_t *point, const vec3_t start, const vec3_t end, c light_mask = nolm_mask; BSP_RecursiveLightPoint(headnode, 0, 1, start, end); + + LerpVector(start, end, light_point->fraction, light_point->pos); } void BSP_TransformedLightPoint(lightpoint_t *point, const vec3_t start, const vec3_t end, @@ -1017,6 +1019,8 @@ void BSP_TransformedLightPoint(lightpoint_t *point, const vec3_t start, const ve // offset plane distance point->plane.dist += DotProduct(point->plane.normal, origin); + + LerpVector(start, end, light_point->fraction, light_point->pos); } #endif diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index fb07e7ad5..4c960da1c 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -41,6 +41,7 @@ static vec3_t shadedir; static bool dotshading; static float celscale; +static float shadowalpha; static drawshadow_t drawshadow; static mat4_t m_shadow_view; @@ -461,8 +462,7 @@ static void draw_celshading(const uint16_t *indices, int num_indices) static drawshadow_t cull_shadow(const model_t *model) { const cplane_t *plane; - float radius, d, w; - vec3_t point; + float radius, w; if (!gl_shadows->integer) return SHADOW_NO; @@ -483,19 +483,26 @@ static drawshadow_t cull_shadow(const model_t *model) if (w < 0.5f) return SHADOW_NO; // too steep - if (!gl_cull_models->integer) - return SHADOW_YES; + radius = (model->frames[newframenum].radius * frontlerp + model->frames[oldframenum].radius * backlerp) * glr.entscale; - // project on plane - d = PlaneDiffFast(origin, plane); - VectorMA(origin, -d, plane->normal, point); + shadowalpha = 0.5f; - radius = max(model->frames[newframenum].radius, model->frames[oldframenum].radius) / w; + // check if faded out + if (gl_shadows->integer >= 2) { + float dist = origin[2] - glr.lightpoint.pos[2] - radius; + if (dist > radius * 4.0f) + return SHADOW_NO; + if (dist > 0) + shadowalpha = 0.5f - dist / (radius * 8.0f); + } - for (int i = 0; i < 4; i++) { - if (PlaneDiff(point, &glr.frustumPlanes[i]) < -radius) { - c.spheresCulled++; - return SHADOW_NO; // culled out + if (gl_cull_models->integer) { + float min_d = -radius / w; + for (int i = 0; i < 4; i++) { + if (PlaneDiff(glr.lightpoint.pos, &glr.frustumPlanes[i]) < min_d) { + c.spheresCulled++; + return SHADOW_NO; // culled out + } } } @@ -573,7 +580,7 @@ static void draw_shadow(const uint16_t *indices, int num_indices) if (gls.currentva) GL_ArrayBits(GLA_VERTEX); - uniform_mesh_color(0, 0, 0, color[3] * 0.5f); + uniform_mesh_color(0, 0, 0, color[3] * shadowalpha); GL_LoadUniforms(); qglEnable(GL_POLYGON_OFFSET_FILL); From 583440452a95658ee092c149bf366026e5cf7d26 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 25 Dec 2024 18:49:46 +0300 Subject: [PATCH 67/79] Count culled shadows separately. --- src/refresh/draw.c | 3 ++- src/refresh/gl.h | 1 + src/refresh/mesh.c | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/refresh/draw.c b/src/refresh/draw.c index a07ba1e2c..3ba10792e 100644 --- a/src/refresh/draw.c +++ b/src/refresh/draw.c @@ -421,7 +421,7 @@ void Draw_Stats(void) int x = 10, y = 10; R_SetScale(1.0f / get_auto_scale()); - R_DrawFill8(8, 8, 25*8, 23*10+2, 4); + R_DrawFill8(8, 8, 25*8, 24*10+2, 4); Draw_Stringf(x, y, "Nodes visible : %i", glr.nodes_visible); y += 10; Draw_Stringf(x, y, "Nodes culled : %i", c.nodesCulled); y += 10; @@ -432,6 +432,7 @@ void Draw_Stats(void) Draw_Stringf(x, y, "Boxes culled : %i", c.boxesCulled); y += 10; Draw_Stringf(x, y, "Spheres culled : %i", c.spheresCulled); y += 10; Draw_Stringf(x, y, "RtBoxes culled : %i", c.rotatedBoxesCulled); y += 10; + Draw_Stringf(x, y, "Shadows culled : %i", c.shadowsCulled); y += 10; Draw_Stringf(x, y, "Tris drawn : %i", c.trisDrawn); y += 10; Draw_Stringf(x, y, "Tex switches : %i", c.texSwitches); y += 10; Draw_Stringf(x, y, "Tex uploads : %i", c.texUploads); y += 10; diff --git a/src/refresh/gl.h b/src/refresh/gl.h index 61891cf1d..ad9588c1e 100644 --- a/src/refresh/gl.h +++ b/src/refresh/gl.h @@ -217,6 +217,7 @@ typedef struct { int boxesCulled; int spheresCulled; int rotatedBoxesCulled; + int shadowsCulled; int batchesDrawn2D; int uniformUploads; int vertexArrayBinds; diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index 4c960da1c..ffd54c446 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -500,7 +500,7 @@ static drawshadow_t cull_shadow(const model_t *model) float min_d = -radius / w; for (int i = 0; i < 4; i++) { if (PlaneDiff(glr.lightpoint.pos, &glr.frustumPlanes[i]) < min_d) { - c.spheresCulled++; + c.shadowsCulled++; return SHADOW_NO; // culled out } } @@ -649,6 +649,8 @@ static void draw_alias_mesh(const uint16_t *indices, int num_indices, glStateBits_t state; const image_t *skin; + c.trisDrawn += num_indices / 3; + // if the model was culled, just draw the shadow if (drawshadow == SHADOW_ONLY) { GL_LockArrays(num_verts); @@ -719,7 +721,6 @@ static void draw_alias_mesh(const uint16_t *indices, int num_indices, GL_LockArrays(num_verts); qglDrawElements(GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, indices); - c.trisDrawn += num_indices / 3; draw_celshading(indices, num_indices); From 73e40adc97c688b2e92f416db608768b6ee2b4a0 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 26 Dec 2024 19:27:19 +0300 Subject: [PATCH 68/79] Const-ify server state pointers in client_t. --- src/server/server.h | 16 ++++++++-------- src/server/user.c | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/server/server.h b/src/server/server.h index a09d82036..888f5a36e 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -353,14 +353,14 @@ typedef struct client_s { entity_packed_t *entities; // [num_entities] // server state pointers (hack for MVD channels implementation) - configstring_t *configstrings; - const cs_remap_t *csr; - char *gamedir, *mapname; - const game_export_t *ge; - cm_t *cm; - int infonum; // slot number visible to client - int spawncount; - int maxclients; + const configstring_t *configstrings; + const cs_remap_t *csr; + const char *gamedir, *mapname; + const game_export_t *ge; + const cm_t *cm; + int infonum; // slot number visible to client + int spawncount; + int maxclients; // netchan type dependent methods void (*AddMessage)(struct client_s *, const byte *, size_t, bool); diff --git a/src/server/user.c b/src/server/user.c index 63a37c8ab..f8138fbb9 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -112,9 +112,9 @@ static void maybe_flush_msg(size_t size) static void write_configstrings(void) { - int i; - char *string; - size_t length; + int i; + const char *string; + size_t length; // write a packet full of data for (i = 0; i < sv_client->csr->end; i++) { @@ -144,7 +144,7 @@ static void write_baseline(const entity_packed_t *base) static void write_baselines(void) { int i, j; - entity_packed_t *base; + const entity_packed_t *base; // write a packet full of data for (i = 0; i < SV_BASELINES_CHUNKS; i++) { @@ -169,9 +169,9 @@ static void write_baselines(void) static void write_configstring_stream(void) { - int i; - char *string; - size_t length; + int i; + const char *string; + size_t length; MSG_WriteByte(svc_configstringstream); @@ -202,7 +202,7 @@ static void write_configstring_stream(void) static void write_baseline_stream(void) { int i, j; - entity_packed_t *base; + const entity_packed_t *base; MSG_WriteByte(svc_baselinestream); @@ -232,10 +232,10 @@ static void write_baseline_stream(void) static void write_gamestate(void) { - entity_packed_t *base; + const entity_packed_t *base; int i, j; size_t length; - char *string; + const char *string; MSG_WriteByte(svc_gamestate); From d9613b7f66a2f1d06c01c8fbd3995f66671d8049 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 26 Dec 2024 19:28:26 +0300 Subject: [PATCH 69/79] Assert input field buffer is nul-terminated. --- src/common/field.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/field.c b/src/common/field.c index 3cb8fadbd..94a879742 100644 --- a/src/common/field.c +++ b/src/common/field.c @@ -93,6 +93,7 @@ bool IF_KeyEvent(inputField_t *field, int key) while (field->text[pos] > 32) { pos++; } + Q_assert(pos < sizeof(field->text)); memmove(field->text + field->cursorPos, field->text + pos, sizeof(field->text) - pos); return true; From 5bbb02a323ac94600966fd04e6d9a4c1122212b6 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 27 Dec 2024 17:49:06 +0300 Subject: [PATCH 70/79] Update libcurl to 8.11.1. --- subprojects/libcurl.wrap | 8 ++++---- subprojects/packagefiles/curl/meson.build | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/subprojects/libcurl.wrap b/subprojects/libcurl.wrap index 852d11df4..d7172a4f2 100644 --- a/subprojects/libcurl.wrap +++ b/subprojects/libcurl.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = curl-8.11.0 -source_url = https://curl.se/download/curl-8.11.0.tar.xz -source_filename = curl-8.11.0.tar.xz -source_hash = db59cf0d671ca6e7f5c2c5ec177084a33a79e04c97e71cf183a5cdea235054eb +directory = curl-8.11.1 +source_url = https://curl.se/download/curl-8.11.1.tar.xz +source_filename = curl-8.11.1.tar.xz +source_hash = c7ca7db48b0909743eaef34250da02c19bc61d4f1dcedd6603f109409536ab56 patch_directory = curl [provide] diff --git a/subprojects/packagefiles/curl/meson.build b/subprojects/packagefiles/curl/meson.build index 1ae8457de..d3c54c17e 100644 --- a/subprojects/packagefiles/curl/meson.build +++ b/subprojects/packagefiles/curl/meson.build @@ -1,4 +1,4 @@ -project('libcurl', 'c', version: '8.11.0', license: 'bsd') +project('libcurl', 'c', version: '8.11.1', license: 'bsd') src = [ 'lib/vauth/cleartext.c', From 2b846d0273f8223e14a002025ca8c47072593b90 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 27 Dec 2024 18:46:14 +0300 Subject: [PATCH 71/79] Update libjpeg-turbo to 3.1.0. --- subprojects/libjpeg-turbo.wrap | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/subprojects/libjpeg-turbo.wrap b/subprojects/libjpeg-turbo.wrap index f9eda5fb3..f8dd1641f 100644 --- a/subprojects/libjpeg-turbo.wrap +++ b/subprojects/libjpeg-turbo.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = libjpeg-turbo-3.0.4 -source_url = https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.0.4/libjpeg-turbo-3.0.4.tar.gz -source_filename = libjpeg-turbo-3.0.4.tar.gz -source_hash = 99130559e7d62e8d695f2c0eaeef912c5828d5b84a0537dcb24c9678c9d5b76b -patch_filename = libjpeg-turbo_3.0.4-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/libjpeg-turbo_3.0.4-2/get_patch -patch_hash = f12d14c6ff4ae83eddc1a2e8cfd96a9b7a40b6bdc8ff345ca8094abf4c3da928 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/libjpeg-turbo_3.0.4-2/libjpeg-turbo-3.0.4.tar.gz -wrapdb_version = 3.0.4-2 +directory = libjpeg-turbo-3.1.0 +source_url = https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.0/libjpeg-turbo-3.1.0.tar.gz +source_filename = libjpeg-turbo-3.1.0.tar.gz +source_hash = 9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 +patch_filename = libjpeg-turbo_3.1.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/libjpeg-turbo_3.1.0-1/get_patch +patch_hash = 970435d4d90cbcf3b372f597dd5770e1fc834d9608fb1ed787c07e421929aede +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/libjpeg-turbo_3.1.0-1/libjpeg-turbo-3.1.0.tar.gz +wrapdb_version = 3.1.0-1 [provide] dependency_names = libjpeg, libturbojpeg From f7df6d5599fd839ab2ccd028ebbaa4bf5d08f8c4 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 29 Dec 2024 00:14:13 +0300 Subject: [PATCH 72/79] More strictly check stat index bounds. Don't allow referencing stats >= 32 without protocol extensions. --- src/client/ascii.c | 6 +++--- src/client/client.h | 1 + src/client/demo.c | 1 + src/client/main.c | 1 + src/client/parse.c | 2 ++ src/client/screen.c | 14 +++++++------- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/client/ascii.c b/src/client/ascii.c index 8070ac700..bce26c7a8 100644 --- a/src/client/ascii.c +++ b/src/client/ascii.c @@ -233,7 +233,7 @@ static void TH_DrawLayoutString(char *dst, const char *s) width = Q_atoi(token); token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; @@ -244,7 +244,7 @@ static void TH_DrawLayoutString(char *dst, const char *s) if (!strcmp(token, "stat_string")) { token = COM_Parse(&s); index = Q_atoi(token); - if (index < 0 || index >= MAX_STATS) { + if (index < 0 || index >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid string index", __func__); } index = cl.frame.ps.stats[index]; @@ -273,7 +273,7 @@ static void TH_DrawLayoutString(char *dst, const char *s) if (!strcmp(token, "if")) { token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; diff --git a/src/client/client.h b/src/client/client.h index c7731139c..0d49187d5 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -264,6 +264,7 @@ typedef struct { char gamedir[MAX_QPATH]; int clientNum; // never changed during gameplay, set by serverdata packet int maxclients; + int max_stats; pmoveParams_t pmp; #if USE_FPS diff --git a/src/client/demo.c b/src/client/demo.c index 57d72573a..e04907452 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -756,6 +756,7 @@ static void CL_PlayDemo_f(void) Q_strlcpy(cls.servername, COM_SkipPath(name), sizeof(cls.servername)); cls.serverAddress.type = NA_LOOPBACK; cl.csr = cs_remap_old; + cl.max_stats = MAX_STATS_OLD; Con_Popup(true); SCR_UpdateScreen(); diff --git a/src/client/main.c b/src/client/main.c index e7db10b74..154ef074e 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1394,6 +1394,7 @@ static void CL_ConnectionlessPacket(void) cls.connect_count = 0; Q_strlcpy(cl.mapname, mapname, sizeof(cl.mapname)); // for levelshot screen cl.csr = cs_remap_old; + cl.max_stats = MAX_STATS_OLD; return; } diff --git a/src/client/parse.c b/src/client/parse.c index c6ee77f07..f5a18313d 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -705,6 +705,8 @@ static void CL_ParseServerData(void) } } + cl.max_stats = (cl.psFlags & MSG_PS_EXTENSIONS_2) ? MAX_STATS_NEW : MAX_STATS_OLD; + // use full extended flags unless writing backward compatible demo cls.demo.esFlags = cl.csr.extended ? CL_ES_EXTENDED_MASK_2 : 0; cls.demo.psFlags = cl.csr.extended ? CL_PS_EXTENDED_MASK_2 : 0; diff --git a/src/client/screen.c b/src/client/screen.c index 9534e2472..b45ce355e 100644 --- a/src/client/screen.c +++ b/src/client/screen.c @@ -1100,8 +1100,8 @@ static void SCR_DrawDebugStats(void) if (j <= 0) return; - if (j > MAX_STATS) - j = MAX_STATS; + if (j > cl.max_stats) + j = cl.max_stats; x = CONCHAR_WIDTH; y = (scr.hud_height - j * CONCHAR_HEIGHT) / 2; @@ -1854,7 +1854,7 @@ static void SCR_ExecuteLayoutString(const char *s) // draw a pic from a stat number token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; @@ -1969,7 +1969,7 @@ static void SCR_ExecuteLayoutString(const char *s) width = Q_atoi(token); token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; @@ -2039,7 +2039,7 @@ static void SCR_ExecuteLayoutString(const char *s) char *cmd = token + 5; token = COM_Parse(&s); index = Q_atoi(token); - if (index < 0 || index >= MAX_STATS) { + if (index < 0 || index >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } index = cl.frame.ps.stats[index]; @@ -2101,7 +2101,7 @@ static void SCR_ExecuteLayoutString(const char *s) if (!strcmp(token, "if")) { token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; @@ -2133,7 +2133,7 @@ static void SCR_ExecuteLayoutString(const char *s) if (!strcmp(token, "health_bars")) { token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; From f059908d77f33c0f7ddfc3d6e8936df9e875f998 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 30 Dec 2024 19:02:51 +0300 Subject: [PATCH 73/79] Tweak BSP feature detection logic a bit. Lightmapped liquids are compiler specific feature independent from DECOUPLED_LM. Make them depend on BSPX header instead. Also restrict bmodel skies to BSPX maps only, not N64. --- src/refresh/surf.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/refresh/surf.c b/src/refresh/surf.c index f0d4ace28..3e488fcff 100644 --- a/src/refresh/surf.c +++ b/src/refresh/surf.c @@ -554,8 +554,8 @@ static uint32_t color_for_surface(const mface_t *surf) static bool enable_intensity_for_surface(const mface_t *surf) { - // enable for any surface with a lightmap in DECOUPLED_LM maps - if (surf->lightmap && gl_static.world.cache->lm_decoupled) + // enable for any surface with a lightmap in BSPX maps + if (surf->lightmap && gl_static.world.cache->has_bspx) return true; // enable for non-transparent, non-warped surfaces @@ -1172,15 +1172,13 @@ void GL_LoadWorld(const char *name) gl_static.world.buffer_size = size; gl_static.nolm_mask = SURF_NOLM_MASK_DEFAULT; - gl_static.use_bmodel_skies = false; + gl_static.use_bmodel_skies = gl_static.use_cubemaps && bsp->has_bspx; - // only supported in DECOUPLED_LM maps because vanilla maps have broken + // only supported in BSPX and N64 maps because vanilla maps have broken // lightofs for liquids/alphas. legacy renderer doesn't support lightmapped // liquids too. - if ((bsp->lm_decoupled || n64surfs > 100) && gl_static.use_shaders) { + if ((bsp->has_bspx || n64surfs > 100) && gl_static.use_shaders) gl_static.nolm_mask = SURF_NOLM_MASK_REMASTER; - gl_static.use_bmodel_skies = gl_static.use_cubemaps; - } glr.fd.lightstyles = &(lightstyle_t){ 1 }; From 8b4750c3bdf87dba964f63ba8615f89752556fe7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 30 Dec 2024 20:09:11 +0300 Subject: [PATCH 74/79] Skip cinematic demos if running coop server. Hack to prevent the server from being killed if mod uses demos as cinematics. Serving demos to multiple clients has never worked in vanilla Q2 anyway. --- src/server/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/init.c b/src/server/init.c index b74dc92f9..63f58f328 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -269,8 +269,9 @@ static bool parse_and_check_server(mapcmd_t *cmd, const char *server, bool nexts ret = SCR_CheckForCinematic(expanded); break; + // demos are handled specially, because they are played locally on the client case ss_demo: - if (!sv_cinematics->integer && nextserver) + if (nextserver && (!sv_cinematics->integer || (sv.state && sv_maxclients->integer > 1))) return false; // skip it if (Q_concat(expanded, sizeof(expanded), "demos/", cmd->server) >= sizeof(expanded)) break; From affbf1a2556bda0b0dfcf9405daf8a13e7724311 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 30 Dec 2024 20:20:15 +0300 Subject: [PATCH 75/79] Skip cinematic existence check if running dedicated server. --- src/server/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/init.c b/src/server/init.c index 63f58f328..9dfc8421f 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -266,7 +266,7 @@ static bool parse_and_check_server(mapcmd_t *cmd, const char *server, bool nexts if (!sv_cinematics->integer && nextserver) return false; // skip it if (Q_concat(expanded, sizeof(expanded), "video/", cmd->server) < sizeof(expanded)) - ret = SCR_CheckForCinematic(expanded); + ret = COM_DEDICATED ? Q_ERR_SUCCESS : SCR_CheckForCinematic(expanded); break; // demos are handled specially, because they are played locally on the client From a5b89271f8fec2ce30859577efba6a40a83f00c4 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 2 Jan 2025 00:52:59 +0300 Subject: [PATCH 76/79] Move CRC to common code, expose CRC_Block(). --- inc/common/crc.h | 25 +++++++++++++++++++++++++ meson.build | 2 +- src/client/client.h | 5 ----- src/client/input.c | 1 + src/{client => common}/crc.c | 9 +++++++-- 5 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 inc/common/crc.h rename src/{client => common}/crc.c (98%) diff --git a/inc/common/crc.h b/inc/common/crc.h new file mode 100644 index 000000000..453796277 --- /dev/null +++ b/inc/common/crc.h @@ -0,0 +1,25 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +uint16_t CRC_Block(const byte *start, size_t count); + +#if USE_CLIENT +byte COM_BlockSequenceCRCByte(const byte *base, size_t length, int sequence); +#endif diff --git a/meson.build b/meson.build index 74d33ffbb..c6825c146 100644 --- a/meson.build +++ b/meson.build @@ -13,6 +13,7 @@ common_src = [ 'src/common/cmd.c', 'src/common/cmodel.c', 'src/common/common.c', + 'src/common/crc.c', 'src/common/cvar.c', 'src/common/error.c', 'src/common/field.c', @@ -37,7 +38,6 @@ common_src = [ client_src = [ 'src/client/ascii.c', 'src/client/console.c', - 'src/client/crc.c', 'src/client/demo.c', 'src/client/download.c', 'src/client/effects.c', diff --git a/src/client/client.h b/src/client/client.h index 0d49187d5..fe6248433 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -1090,8 +1090,3 @@ void CL_GTV_Shutdown(void); #define CL_GTV_Init() (void)0 #define CL_GTV_Shutdown() (void)0 #endif - -// -// crc.c -// -byte COM_BlockSequenceCRCByte(const byte *base, size_t length, int sequence); diff --git a/src/client/input.c b/src/client/input.c index 496082432..8a7f21351 100644 --- a/src/client/input.c +++ b/src/client/input.c @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // cl.input.c -- builds an intended movement command to send to the server #include "client.h" +#include "common/crc.h" static cvar_t *cl_nodelta; static cvar_t *cl_maxpackets; diff --git a/src/client/crc.c b/src/common/crc.c similarity index 98% rename from src/client/crc.c rename to src/common/crc.c index b661f0a08..68aec0c27 100644 --- a/src/client/crc.c +++ b/src/common/crc.c @@ -17,7 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., */ /* crc.c */ -#include "client.h" +#include "shared/shared.h" +#include "common/crc.h" // this is a 16 bit, non-reflected CRC using the polynomial 0x1021 // and the initial and final xor values shown below... in other words, the @@ -61,7 +62,7 @@ static const uint16_t crctable[256] = { 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; -static uint16_t CRC_Block(const byte *start, size_t count) +uint16_t CRC_Block(const byte *start, size_t count) { uint16_t crc = CRC_INIT_VALUE; @@ -71,6 +72,8 @@ static uint16_t CRC_Block(const byte *start, size_t count) return crc; } +#if USE_CLIENT + static const byte chktbl[1024] = { 0x84, 0x47, 0x51, 0xc1, 0x93, 0x22, 0x21, 0x24, 0x2f, 0x66, 0x60, 0x4d, 0xb0, 0x7c, 0xda, 0x88, 0x54, 0x15, 0x2b, 0xc6, 0x6c, 0x89, 0xc5, 0x9d, 0x48, 0xee, 0xe6, 0x8a, 0xb5, 0xf4, @@ -178,3 +181,5 @@ byte COM_BlockSequenceCRCByte(const byte *base, size_t length, int sequence) return crc; } + +#endif // USE_CLIENT From 5b2d9f29aa9fb07cfe2b4ba9ee628a0153e759c2 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 2 Jan 2025 00:55:32 +0300 Subject: [PATCH 77/79] Support hashed format for entity string overrides. For compatibility with YQ2 mapfixes. --- inc/common/cmodel.h | 2 +- src/common/cmodel.c | 81 ++++++++++++++++++++++++++++----------------- src/server/init.c | 2 +- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/inc/common/cmodel.h b/inc/common/cmodel.h index c237c4737..739ada533 100644 --- a/inc/common/cmodel.h +++ b/inc/common/cmodel.h @@ -40,7 +40,7 @@ void CM_Init(void); void CM_FreeMap(cm_t *cm); int CM_LoadMap(cm_t *cm, const char *name); -void CM_LoadOverrides(cm_t *cm, char *server, size_t server_size); +void CM_LoadOverride(cm_t *cm, char *server, size_t server_size); const mnode_t *CM_NodeNum(const cm_t *cm, int number); const mleaf_t *CM_LeafNum(const cm_t *cm, int number); diff --git a/src/common/cmodel.c b/src/common/cmodel.c index 5f4303b69..36d89cdbf 100644 --- a/src/common/cmodel.c +++ b/src/common/cmodel.c @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/cmd.h" #include "common/cmodel.h" #include "common/common.h" +#include "common/crc.h" #include "common/cvar.h" #include "common/files.h" #include "common/math.h" @@ -50,20 +51,43 @@ enum { OVERRIDE_ALL = MASK(3) }; -static void load_entstring_override(cm_t *cm, const char *server) +static void load_entstring_override(cm_t *cm) { - char buffer[MAX_QPATH], *data = NULL; - int ret; + char buffer[MAX_QPATH], name[MAX_QPATH], *data = NULL; + const bsp_t *bsp = cm->cache; + const char *path = map_override_path->string; + int ret, crc = 0; + + if (!*path) + return; + + if (!Com_ParseMapName(name, bsp->name, sizeof(name))) + return; - if (Q_snprintf(buffer, sizeof(buffer), "%s/%s.ent", map_override_path->string, server) >= sizeof(buffer)) { + // last byte is excluded from CRC (why?) + if (bsp->numentitychars > 0) + crc = CRC_Block((const byte *)bsp->entitystring, bsp->numentitychars - 1); + + // load entity string from `@.ent' + if (Q_snprintf(buffer, sizeof(buffer), "%s/%s@%04x.ent", path, name, crc) >= sizeof(buffer)) { ret = Q_ERR(ENAMETOOLONG); goto fail; } - ret = FS_LoadFileEx(buffer, (void **)&data, 0, TAG_CMODEL); - if (!data) { - if (ret == Q_ERR(ENOENT)) - return; + + // fall back to no hash + if (ret == Q_ERR(ENOENT)) { + Q_snprintf(buffer, sizeof(buffer), "%s/%s.ent", path, name); + ret = FS_LoadFileEx(buffer, (void **)&data, 0, TAG_CMODEL); + } + if (ret == Q_ERR(ENOENT)) + return; + + if (ret < 0) + goto fail; + + if (ret < 2) { + ret = Q_ERR_FILE_TOO_SMALL; goto fail; } @@ -77,7 +101,18 @@ static void load_entstring_override(cm_t *cm, const char *server) Com_EPrintf("Couldn't load entity string from %s: %s\n", buffer, Q_ErrorString(ret)); } -static void load_binary_override(cm_t *cm, char *server, size_t server_size) +/* +================== +CM_LoadOverride + +Load R1Q2-style binary override file. + +Must be called before CM_LoadMap(). +May modify server buffer if name override is in effect. +May allocate enstring, must be freed with CM_FreeMap(). +================== +*/ +void CM_LoadOverride(cm_t *cm, char *server, size_t server_size) { sizebuf_t sz; char buffer[MAX_QPATH]; @@ -85,6 +120,9 @@ static void load_binary_override(cm_t *cm, char *server, size_t server_size) int ret, bits, len; char *buf, name_buf[MAX_QPATH]; + if (!*map_override_path->string) + return; + if (Q_snprintf(buffer, sizeof(buffer), "%s/%s.bsp.override", map_override_path->string, server) >= sizeof(buffer)) { ret = Q_ERR(ENAMETOOLONG); goto fail; @@ -141,28 +179,6 @@ static void load_binary_override(cm_t *cm, char *server, size_t server_size) FS_FreeFile(data); } -/* -================== -CM_LoadOverrides - -Ugly hack to override entstring and other parameters. - -Must be called before CM_LoadMap. -May modify server buffer if name override is in effect. -May allocate enstring, must be freed with CM_FreeMap(). -================== -*/ -void CM_LoadOverrides(cm_t *cm, char *server, size_t server_size) -{ - if (!*map_override_path->string) - return; - - load_binary_override(cm, server, server_size); - - if (!(cm->override_bits & OVERRIDE_ENTS)) - load_entstring_override(cm, server); -} - /* ================== CM_FreeMap @@ -196,6 +212,9 @@ int CM_LoadMap(cm_t *cm, const char *name) if (!cm->cache) return ret; + if (!(cm->override_bits & OVERRIDE_ENTS)) + load_entstring_override(cm); + if (!(cm->override_bits & OVERRIDE_CSUM)) cm->checksum = cm->cache->checksum; diff --git a/src/server/init.c b/src/server/init.c index 9dfc8421f..4150460b7 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -286,7 +286,7 @@ static bool parse_and_check_server(mapcmd_t *cmd, const char *server, bool nexts break; default: - CM_LoadOverrides(&cmd->cm, cmd->server, sizeof(cmd->server)); // may override server! + CM_LoadOverride(&cmd->cm, cmd->server, sizeof(cmd->server)); // may override server! if (Q_concat(expanded, sizeof(expanded), "maps/", cmd->server, ".bsp") < sizeof(expanded)) ret = CM_LoadMap(&cmd->cm, expanded); if (ret < 0) From 4839a2b260d9ba0082b790dddab5dd4f177f32d6 Mon Sep 17 00:00:00 2001 From: Dino <8dino2@gmail.com> Date: Wed, 15 Jan 2025 08:38:49 -0500 Subject: [PATCH 78/79] Fixed a few warnings and one error --- src/client/screen.c | 4 ++-- src/refresh/main.c | 2 +- src/server/init.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/screen.c b/src/client/screen.c index 0c8a4c35c..ec2b005ca 100644 --- a/src/client/screen.c +++ b/src/client/screen.c @@ -2554,9 +2554,9 @@ static void SCR_DrawGhudElement(ghud_element_t *element, float alpha_base, color x -= (length * CHAR_WIDTH); if ((uiflags & UI_MIDDLE) == UI_MIDDLE) - y -= (length * CHAR_HEIGHT * 0.5); + y -= (length * CONCHAR_HEIGHT * 0.5); else if (uiflags & UI_BOTTOM) - y -= (length * CHAR_HEIGHT); + y -= (length * CONCHAR_HEIGHT); uiflags &= ~(UI_LEFT | UI_RIGHT | UI_TOP | UI_BOTTOM); diff --git a/src/refresh/main.c b/src/refresh/main.c index c0cc0ed7a..287bf9b9f 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -670,7 +670,7 @@ static qboolean ALLOC_BoxPoints(int num_boxes) } else if (num_boxes != drawbox_total) { - if (box_points[0] == NULL || box_colors[0] == NULL) + if (box_points == NULL || box_colors == NULL) return false; if_null_1 = box_points; diff --git a/src/server/init.c b/src/server/init.c index fe730db43..818145ee3 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -289,7 +289,7 @@ static bool parse_and_check_server(mapcmd_t *cmd, const char *server, bool nexts default: if (sv_load_ent->integer) - CM_LoadOverrides(&cmd->cm, cmd->server, sizeof(cmd->server)); // may override server! + CM_LoadOverride(&cmd->cm, cmd->server, sizeof(cmd->server)); // may override server! if (Q_concat(expanded, sizeof(expanded), "maps/", cmd->server, ".bsp") < sizeof(expanded)) ret = CM_LoadMap(&cmd->cm, expanded); if (ret < 0) From e812b0ae0a66119bd8583d1c035a4f08e2ec5e80 Mon Sep 17 00:00:00 2001 From: Dino <8dino2@gmail.com> Date: Thu, 18 Dec 2025 12:45:41 -0500 Subject: [PATCH 79/79] Fixed CON_WIDTH error --- src/client/screen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/screen.c b/src/client/screen.c index ec2b005ca..2d8751d44 100644 --- a/src/client/screen.c +++ b/src/client/screen.c @@ -2549,9 +2549,9 @@ static void SCR_DrawGhudElement(ghud_element_t *element, float alpha_base, color int length = strlen(element->text); int uiflags = element->size[0] | (element->size[1] << 16); if ((uiflags & UI_CENTER) == UI_CENTER) - x -= (length * CHAR_WIDTH * 0.5); + x -= (length * CONCHAR_WIDTH * 0.5); else if (uiflags & UI_RIGHT) - x -= (length * CHAR_WIDTH); + x -= (length * CONCHAR_WIDTH); if ((uiflags & UI_MIDDLE) == UI_MIDDLE) y -= (length * CONCHAR_HEIGHT * 0.5);