diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-03 23:32:16 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-03 23:32:16 +0100 |
| commit | edba65625dd5823705619a2e682a99d6ad91ae8b (patch) | |
| tree | cae03d46267469d1076937ad22c7b2aec63b068e /src/world.cpp | |
| parent | GUI: add inline colour picker control (diff) | |
| download | hl2h-edba65625dd5823705619a2e682a99d6ad91ae8b.tar.gz hl2h-edba65625dd5823705619a2e682a99d6ad91ae8b.zip | |
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].
Diffstat (limited to 'src/world.cpp')
| -rw-r--r-- | src/world.cpp | 120 |
1 files changed, 107 insertions, 13 deletions
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 { | |||
| 48 | bool valid; | 48 | bool valid; |
| 49 | int world_r, world_g, world_b; | 49 | int world_r, world_g, world_b; |
| 50 | int sky_r, sky_g, sky_b; | 50 | int sky_r, sky_g, sky_b; |
| 51 | int prop_r, prop_g, prop_b; | ||
| 51 | }; | 52 | }; |
| 52 | Tint g_applied{false, 255, 255, 255, 255, 255, 255}; | 53 | Tint g_applied{false, 255, 255, 255, 255, 255, 255, 255, 255, 255}; |
| 54 | |||
| 55 | // Static props and prop_physics share their model materials; every stock HL2 | ||
| 56 | // map prop lives under models/props*, which keeps NPC/player/weapon materials | ||
| 57 | // out of the prop pass. | ||
| 58 | bool is_prop_material(const char *name) { | ||
| 59 | return name && strncmp(name, "models/props", 12) == 0; | ||
| 60 | } | ||
| 53 | 61 | ||
| 54 | struct MatCalls { | 62 | struct MatCalls { |
| 55 | using FirstFn = unsigned short (*)(void *); | 63 | using FirstFn = unsigned short (*)(void *); |
| @@ -145,7 +153,7 @@ void *material_system() { | |||
| 145 | // Push one colour pair over all materials. Groups that are off get white so a | 153 | // Push one colour pair over all materials. Groups that are off get white so a |
| 146 | // previously tinted world is restored. | 154 | // previously tinted world is restored. |
| 147 | bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, | 155 | bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, |
| 148 | int sky_b) { | 156 | int sky_b, int prop_r, int prop_g, int prop_b) { |
| 149 | void *ms = material_system(); | 157 | void *ms = material_system(); |
| 150 | if (!ms) | 158 | if (!ms) |
| 151 | return false; | 159 | return false; |
| @@ -166,9 +174,10 @@ bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, | |||
| 166 | 174 | ||
| 167 | float wr = world_r / 255.0f, wg = world_g / 255.0f, wb = world_b / 255.0f; | 175 | float wr = world_r / 255.0f, wg = world_g / 255.0f, wb = world_b / 255.0f; |
| 168 | float sr = sky_r / 255.0f, sg = sky_g / 255.0f, sb = sky_b / 255.0f; | 176 | float sr = sky_r / 255.0f, sg = sky_g / 255.0f, sb = sky_b / 255.0f; |
| 177 | float pr = prop_r / 255.0f, pg = prop_g / 255.0f, pb = prop_b / 255.0f; | ||
| 169 | 178 | ||
| 170 | unsigned short inv = invalid(ms); | 179 | unsigned short inv = invalid(ms); |
| 171 | int seen = 0, world_hits = 0, sky_hits = 0; | 180 | int seen = 0, world_hits = 0, sky_hits = 0, prop_hits = 0; |
| 172 | for (unsigned short h = first(ms); h != inv; h = next(ms, h)) { | 181 | for (unsigned short h = first(ms); h != inv; h = next(ms, h)) { |
| 173 | if (++seen > total + 16) { | 182 | if (++seen > total + 16) { |
| 174 | logf("tint: material walk runaway (%d > %d); stopped", seen, total); | 183 | 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, | |||
| 199 | } else if (is_sky) { | 208 | } else if (is_sky) { |
| 200 | mod_fn(mat, sr, sg, sb); | 209 | mod_fn(mat, sr, sg, sb); |
| 201 | sky_hits++; | 210 | sky_hits++; |
| 211 | } else if (is_prop_material(name)) { | ||
| 212 | mod_fn(mat, pr, pg, pb); | ||
| 213 | prop_hits++; | ||
| 202 | } | 214 | } |
| 203 | } | 215 | } |
| 204 | logf("tint: applied world=%d(%d,%d,%d) sky=%d(%d,%d,%d) of %d materials", | 216 | logf("tint: applied world=%d(%d,%d,%d) sky=%d(%d,%d,%d) props=%d(%d,%d,%d) " |
| 217 | "of %d materials", | ||
| 205 | world_hits, world_r, world_g, world_b, sky_hits, sky_r, sky_g, sky_b, | 218 | world_hits, world_r, world_g, world_b, sky_hits, sky_r, sky_g, sky_b, |
| 206 | total); | 219 | prop_hits, prop_r, prop_g, prop_b, total); |
| 207 | return true; | 220 | return true; |
| 208 | } | 221 | } |
| 209 | 222 | ||
| @@ -215,6 +228,7 @@ void world_tint_update() { | |||
| 215 | bool on = feature_active(FEAT_WORLD_TINT); | 228 | bool on = feature_active(FEAT_WORLD_TINT); |
| 216 | bool world_on = on && g_tint_world_on; | 229 | bool world_on = on && g_tint_world_on; |
| 217 | bool sky_on = on && g_tint_sky_on; | 230 | bool sky_on = on && g_tint_sky_on; |
| 231 | bool prop_on = on && g_tint_prop_on; | ||
| 218 | 232 | ||
| 219 | int wr = world_on ? g_tint_world_rgb[0] : 255; | 233 | int wr = world_on ? g_tint_world_rgb[0] : 255; |
| 220 | int wg = world_on ? g_tint_world_rgb[1] : 255; | 234 | int wg = world_on ? g_tint_world_rgb[1] : 255; |
| @@ -222,27 +236,107 @@ void world_tint_update() { | |||
| 222 | int sr = sky_on ? g_tint_sky_rgb[0] : 255; | 236 | int sr = sky_on ? g_tint_sky_rgb[0] : 255; |
| 223 | int sg = sky_on ? g_tint_sky_rgb[1] : 255; | 237 | int sg = sky_on ? g_tint_sky_rgb[1] : 255; |
| 224 | int sb = sky_on ? g_tint_sky_rgb[2] : 255; | 238 | int sb = sky_on ? g_tint_sky_rgb[2] : 255; |
| 239 | int pr = prop_on ? g_tint_prop_rgb[0] : 255; | ||
| 240 | int pg = prop_on ? g_tint_prop_rgb[1] : 255; | ||
| 241 | int pb = prop_on ? g_tint_prop_rgb[2] : 255; | ||
| 225 | 242 | ||
| 226 | bool white = wr == 255 && wg == 255 && wb == 255 && sr == 255 && | 243 | bool white = wr == 255 && wg == 255 && wb == 255 && sr == 255 && |
| 227 | sg == 255 && sb == 255; | 244 | sg == 255 && sb == 255 && pr == 255 && pg == 255 && pb == 255; |
| 228 | if (g_applied.valid && g_applied.world_r == wr && g_applied.world_g == wg && | 245 | if (g_applied.valid && g_applied.world_r == wr && g_applied.world_g == wg && |
| 229 | g_applied.world_b == wb && g_applied.sky_r == sr && | 246 | g_applied.world_b == wb && g_applied.sky_r == sr && |
| 230 | g_applied.sky_g == sg && g_applied.sky_b == sb) | 247 | g_applied.sky_g == sg && g_applied.sky_b == sb && |
| 248 | g_applied.prop_r == pr && g_applied.prop_g == pg && | ||
| 249 | g_applied.prop_b == pb) | ||
| 231 | return; | 250 | return; |
| 232 | // Nothing was ever tinted and nothing is requested: no need to walk the | 251 | // Nothing was ever tinted and nothing is requested: no need to walk the |
| 233 | // material list just to write white over white. | 252 | // material list just to write white over white. |
| 234 | if (!g_applied.valid && white) { | 253 | if (!g_applied.valid && white) { |
| 235 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 254 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 236 | return; | 255 | return; |
| 237 | } | 256 | } |
| 238 | if (g_matsys_failed && !world_on && !sky_on) { | 257 | if (g_matsys_failed && !world_on && !sky_on && !prop_on) { |
| 239 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 258 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 240 | return; | 259 | return; |
| 241 | } | 260 | } |
| 242 | if (!apply_tint(wr, wg, wb, sr, sg, sb)) { | 261 | if (!apply_tint(wr, wg, wb, sr, sg, sb, pr, pg, pb)) { |
| 243 | if (g_matsys_failed) | 262 | if (g_matsys_failed) |
| 244 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 263 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 245 | return; | 264 | return; |
| 246 | } | 265 | } |
| 247 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 266 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 267 | } | ||
| 268 | |||
| 269 | // --------------------------------------------------------------------------- | ||
| 270 | // Fog override (engine console commands; no render hooks) | ||
| 271 | // --------------------------------------------------------------------------- | ||
| 272 | |||
| 273 | namespace { | ||
| 274 | |||
| 275 | struct FogState { | ||
| 276 | bool valid; | ||
| 277 | bool on; | ||
| 278 | bool disable; | ||
| 279 | int r, g, b; | ||
| 280 | int start, end; | ||
| 281 | bool skybox; | ||
| 282 | }; | ||
| 283 | FogState g_fog_applied{false, false, false, 0, 0, 0, 0, 0, false}; | ||
| 284 | |||
| 285 | } // namespace | ||
| 286 | |||
| 287 | void fog_invalidate() { g_fog_applied.valid = false; } | ||
| 288 | |||
| 289 | void fog_update() { | ||
| 290 | bool on = feature_active(FEAT_FOG); | ||
| 291 | bool disable = on && g_fog_disable; | ||
| 292 | int r = g_fog_rgb[0], g = g_fog_rgb[1], b = g_fog_rgb[2]; | ||
| 293 | int start = g_fog_start; | ||
| 294 | int end = g_fog_end; | ||
| 295 | if (end <= start) | ||
| 296 | end = start + 1; | ||
| 297 | bool skybox = on && g_fog_skybox; | ||
| 298 | |||
| 299 | if (g_fog_applied.valid && g_fog_applied.on == on && | ||
| 300 | g_fog_applied.disable == disable && g_fog_applied.r == r && | ||
| 301 | g_fog_applied.g == g && g_fog_applied.b == b && | ||
| 302 | g_fog_applied.start == start && g_fog_applied.end == end && | ||
| 303 | g_fog_applied.skybox == skybox) | ||
| 304 | return; | ||
| 305 | |||
| 306 | if (!on) { | ||
| 307 | if (g_fog_applied.valid && g_fog_applied.on) | ||
| 308 | console_cmd("fog_override 0"); | ||
| 309 | g_fog_applied = {true, false, false, 0, 0, 0, 0, 0, false}; | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | |||
| 313 | console_cmd("fog_override 1"); | ||
| 314 | char cmd[128]; | ||
| 315 | if (disable) { | ||
| 316 | console_cmd("fog_enable 0"); | ||
| 317 | if (skybox) | ||
| 318 | console_cmd("fog_enableskybox 0"); | ||
| 319 | } else { | ||
| 320 | console_cmd("fog_enable 1"); | ||
| 321 | snprintf(cmd, sizeof(cmd), "fog_color %d %d %d", r, g, b); | ||
| 322 | console_cmd(cmd); | ||
| 323 | snprintf(cmd, sizeof(cmd), "fog_start %d", start); | ||
| 324 | console_cmd(cmd); | ||
| 325 | snprintf(cmd, sizeof(cmd), "fog_end %d", end); | ||
| 326 | console_cmd(cmd); | ||
| 327 | if (skybox) { | ||
| 328 | console_cmd("fog_enableskybox 1"); | ||
| 329 | snprintf(cmd, sizeof(cmd), "fog_colorskybox %d %d %d", r, g, b); | ||
| 330 | console_cmd(cmd); | ||
| 331 | snprintf(cmd, sizeof(cmd), "fog_startskybox %d", start); | ||
| 332 | console_cmd(cmd); | ||
| 333 | snprintf(cmd, sizeof(cmd), "fog_endskybox %d", end); | ||
| 334 | console_cmd(cmd); | ||
| 335 | } else { | ||
| 336 | console_cmd("fog_enableskybox 0"); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | g_fog_applied = {true, on, disable, r, g, b, start, end, skybox}; | ||
| 340 | logf("fog: %s colour=(%d,%d,%d) start=%d end=%d skybox=%d", disable ? "off" | ||
| 341 | : "on", r, g, b, start, end, (int)skybox); | ||
| 248 | } | 342 | } |
