From edba65625dd5823705619a2e682a99d6ad91ae8b Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 3 Nov 2026 23:32:16 +0100 Subject: World: add prop tint and fog override - World tint now also colours prop materials (models/props*) with their own colour, covering static props and prop_physics while leaving NPC, player and weapon materials untouched. - New Fog card: fog_override colour/start/end via ClientCmd, optional skybox fog, and a Remove fog mode. Restores the map fog (fog_override 0) when disabled and re-applies after level changes. - Config sections [worldtint] prop/colour keys and [fog]. --- src/world.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 107 insertions(+), 13 deletions(-) (limited to 'src/world.cpp') diff --git a/src/world.cpp b/src/world.cpp index eeffb99..2333829 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -48,8 +48,16 @@ struct Tint { bool valid; int world_r, world_g, world_b; int sky_r, sky_g, sky_b; + int prop_r, prop_g, prop_b; }; -Tint g_applied{false, 255, 255, 255, 255, 255, 255}; +Tint g_applied{false, 255, 255, 255, 255, 255, 255, 255, 255, 255}; + +// Static props and prop_physics share their model materials; every stock HL2 +// map prop lives under models/props*, which keeps NPC/player/weapon materials +// out of the prop pass. +bool is_prop_material(const char *name) { + return name && strncmp(name, "models/props", 12) == 0; +} struct MatCalls { using FirstFn = unsigned short (*)(void *); @@ -145,7 +153,7 @@ void *material_system() { // Push one colour pair over all materials. Groups that are off get white so a // previously tinted world is restored. bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, - int sky_b) { + int sky_b, int prop_r, int prop_g, int prop_b) { void *ms = material_system(); if (!ms) return false; @@ -166,9 +174,10 @@ bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, float wr = world_r / 255.0f, wg = world_g / 255.0f, wb = world_b / 255.0f; float sr = sky_r / 255.0f, sg = sky_g / 255.0f, sb = sky_b / 255.0f; + float pr = prop_r / 255.0f, pg = prop_g / 255.0f, pb = prop_b / 255.0f; unsigned short inv = invalid(ms); - int seen = 0, world_hits = 0, sky_hits = 0; + int seen = 0, world_hits = 0, sky_hits = 0, prop_hits = 0; for (unsigned short h = first(ms); h != inv; h = next(ms, h)) { if (++seen > total + 16) { logf("tint: material walk runaway (%d > %d); stopped", seen, total); @@ -199,11 +208,15 @@ bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, } else if (is_sky) { mod_fn(mat, sr, sg, sb); sky_hits++; + } else if (is_prop_material(name)) { + mod_fn(mat, pr, pg, pb); + prop_hits++; } } - logf("tint: applied world=%d(%d,%d,%d) sky=%d(%d,%d,%d) of %d materials", + logf("tint: applied world=%d(%d,%d,%d) sky=%d(%d,%d,%d) props=%d(%d,%d,%d) " + "of %d materials", world_hits, world_r, world_g, world_b, sky_hits, sky_r, sky_g, sky_b, - total); + prop_hits, prop_r, prop_g, prop_b, total); return true; } @@ -215,6 +228,7 @@ void world_tint_update() { bool on = feature_active(FEAT_WORLD_TINT); bool world_on = on && g_tint_world_on; bool sky_on = on && g_tint_sky_on; + bool prop_on = on && g_tint_prop_on; int wr = world_on ? g_tint_world_rgb[0] : 255; int wg = world_on ? g_tint_world_rgb[1] : 255; @@ -222,27 +236,107 @@ void world_tint_update() { int sr = sky_on ? g_tint_sky_rgb[0] : 255; int sg = sky_on ? g_tint_sky_rgb[1] : 255; int sb = sky_on ? g_tint_sky_rgb[2] : 255; + int pr = prop_on ? g_tint_prop_rgb[0] : 255; + int pg = prop_on ? g_tint_prop_rgb[1] : 255; + int pb = prop_on ? g_tint_prop_rgb[2] : 255; bool white = wr == 255 && wg == 255 && wb == 255 && sr == 255 && - sg == 255 && sb == 255; + sg == 255 && sb == 255 && pr == 255 && pg == 255 && pb == 255; if (g_applied.valid && g_applied.world_r == wr && g_applied.world_g == wg && g_applied.world_b == wb && g_applied.sky_r == sr && - g_applied.sky_g == sg && g_applied.sky_b == sb) + g_applied.sky_g == sg && g_applied.sky_b == sb && + g_applied.prop_r == pr && g_applied.prop_g == pg && + g_applied.prop_b == pb) return; // Nothing was ever tinted and nothing is requested: no need to walk the // material list just to write white over white. if (!g_applied.valid && white) { - g_applied = {true, wr, wg, wb, sr, sg, sb}; + g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; return; } - if (g_matsys_failed && !world_on && !sky_on) { - g_applied = {true, wr, wg, wb, sr, sg, sb}; + if (g_matsys_failed && !world_on && !sky_on && !prop_on) { + g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; return; } - if (!apply_tint(wr, wg, wb, sr, sg, sb)) { + if (!apply_tint(wr, wg, wb, sr, sg, sb, pr, pg, pb)) { if (g_matsys_failed) - g_applied = {true, wr, wg, wb, sr, sg, sb}; + g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; return; } - g_applied = {true, wr, wg, wb, sr, sg, sb}; + g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; +} + +// --------------------------------------------------------------------------- +// Fog override (engine console commands; no render hooks) +// --------------------------------------------------------------------------- + +namespace { + +struct FogState { + bool valid; + bool on; + bool disable; + int r, g, b; + int start, end; + bool skybox; +}; +FogState g_fog_applied{false, false, false, 0, 0, 0, 0, 0, false}; + +} // namespace + +void fog_invalidate() { g_fog_applied.valid = false; } + +void fog_update() { + bool on = feature_active(FEAT_FOG); + bool disable = on && g_fog_disable; + int r = g_fog_rgb[0], g = g_fog_rgb[1], b = g_fog_rgb[2]; + int start = g_fog_start; + int end = g_fog_end; + if (end <= start) + end = start + 1; + bool skybox = on && g_fog_skybox; + + if (g_fog_applied.valid && g_fog_applied.on == on && + g_fog_applied.disable == disable && g_fog_applied.r == r && + g_fog_applied.g == g && g_fog_applied.b == b && + g_fog_applied.start == start && g_fog_applied.end == end && + g_fog_applied.skybox == skybox) + return; + + if (!on) { + if (g_fog_applied.valid && g_fog_applied.on) + console_cmd("fog_override 0"); + g_fog_applied = {true, false, false, 0, 0, 0, 0, 0, false}; + return; + } + + console_cmd("fog_override 1"); + char cmd[128]; + if (disable) { + console_cmd("fog_enable 0"); + if (skybox) + console_cmd("fog_enableskybox 0"); + } else { + console_cmd("fog_enable 1"); + snprintf(cmd, sizeof(cmd), "fog_color %d %d %d", r, g, b); + console_cmd(cmd); + snprintf(cmd, sizeof(cmd), "fog_start %d", start); + console_cmd(cmd); + snprintf(cmd, sizeof(cmd), "fog_end %d", end); + console_cmd(cmd); + if (skybox) { + console_cmd("fog_enableskybox 1"); + snprintf(cmd, sizeof(cmd), "fog_colorskybox %d %d %d", r, g, b); + console_cmd(cmd); + snprintf(cmd, sizeof(cmd), "fog_startskybox %d", start); + console_cmd(cmd); + snprintf(cmd, sizeof(cmd), "fog_endskybox %d", end); + console_cmd(cmd); + } else { + console_cmd("fog_enableskybox 0"); + } + } + g_fog_applied = {true, on, disable, r, g, b, start, end, skybox}; + logf("fog: %s colour=(%d,%d,%d) start=%d end=%d skybox=%d", disable ? "off" + : "on", r, g, b, start, end, (int)skybox); } -- cgit v1.2.3