diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-03 23:58:59 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-03 23:58:59 +0100 |
| commit | 9c4455af43da34127864b09ad2a3d3e996c9dce1 (patch) | |
| tree | eb3754763db7c00fe664b900f9a560057bcb9955 /src/gui.cpp | |
| parent | World: add skybox changer (diff) | |
| download | hl2h-9c4455af43da34127864b09ad2a3d3e996c9dce1.tar.gz hl2h-9c4455af43da34127864b09ad2a3d3e996c9dce1.zip | |
World: replace skybox changer with a PNG skybox image
sv_skyname did not take effect at runtime in this build, so the Skybox card now
replaces the skybox materials' textures with <config dir>/skybox.png.
The PNG is decoded through libpng16's simplified API (dlopen'ed, so no new link
dependency) and uploaded with IMaterialSystem::CreateTextureFromBits as an
RGBA8888 texture. IMaterialVar::SetTextureValue assigns it to $basetexture and
the HDR variants for every material in the SkyBox textures group (all six
faces get the same image).
Original textures are recorded per material/var (texture pointer plus string
fallback) and restored when the feature is off. Records survive level changes
so a reused sky material is still restorable; the override is re-applied to a
new map automatically. The card shows the image path and has a Reload image
button; a missing/unreadable PNG raises an error toast.
Diffstat (limited to '')
| -rw-r--r-- | src/gui.cpp | 77 |
1 files changed, 16 insertions, 61 deletions
diff --git a/src/gui.cpp b/src/gui.cpp index 5fb8fa7..eb53dff 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -183,11 +183,10 @@ enum CtrlKind { | |||
| 183 | CK_SECTION, | 183 | CK_SECTION, |
| 184 | CK_INFO, | 184 | CK_INFO, |
| 185 | CK_BUTTON, | 185 | CK_BUTTON, |
| 186 | CK_COLOR, | 186 | CK_COLOR |
| 187 | CK_CHOICE | ||
| 188 | }; | 187 | }; |
| 189 | 188 | ||
| 190 | enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET }; | 189 | enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET, ACT_SKY_RELOAD }; |
| 191 | 190 | ||
| 192 | struct Ctrl { | 191 | struct Ctrl { |
| 193 | int kind; | 192 | int kind; |
| @@ -196,8 +195,6 @@ struct Ctrl { | |||
| 196 | bool *bval; // CK_TOGGLE | 195 | bool *bval; // CK_TOGGLE |
| 197 | int *ival; // CK_SLIDER | 196 | int *ival; // CK_SLIDER |
| 198 | int *rgb; // CK_COLOR: three ints, 0..255 | 197 | int *rgb; // CK_COLOR: three ints, 0..255 |
| 199 | const char *const *opts; // CK_CHOICE: option labels | ||
| 200 | int nopts; | ||
| 201 | int min, max; | 198 | int min, max; |
| 202 | int action; // CK_BUTTON | 199 | int action; // CK_BUTTON |
| 203 | bool zero_unlimited; // slider: value 0 displays as "All" | 200 | bool zero_unlimited; // slider: value 0 displays as "All" |
| @@ -256,8 +253,6 @@ static void add_ctrl(int card, int kind, const char *label, const char *desc, | |||
| 256 | c.action = action; | 253 | c.action = action; |
| 257 | c.zero_unlimited = false; | 254 | c.zero_unlimited = false; |
| 258 | c.rgb = nullptr; | 255 | c.rgb = nullptr; |
| 259 | c.opts = nullptr; | ||
| 260 | c.nopts = 0; | ||
| 261 | c.hue = 0.0f; | 256 | c.hue = 0.0f; |
| 262 | c.sat = 0.0f; | 257 | c.sat = 0.0f; |
| 263 | c.val = 1.0f; | 258 | c.val = 1.0f; |
| @@ -287,19 +282,11 @@ static void add_color(int card, const char *label, const char *desc, | |||
| 287 | add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); | 282 | add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); |
| 288 | g_ctrls[i].rgb = rgb; | 283 | g_ctrls[i].rgb = rgb; |
| 289 | } | 284 | } |
| 290 | static void add_choice(int card, const char *label, const char *desc, | ||
| 291 | const char *const *opts, int nopts, int *value) { | ||
| 292 | int i = g_nctrl; | ||
| 293 | add_ctrl(card, CK_CHOICE, label, desc, 0, nopts > 0 ? nopts - 1 : 0, | ||
| 294 | nullptr, value, ACT_NONE); | ||
| 295 | g_ctrls[i].opts = opts; | ||
| 296 | g_ctrls[i].nopts = nopts; | ||
| 297 | } | ||
| 298 | 285 | ||
| 299 | static int ctrl_val(const Ctrl &c) { | 286 | static int ctrl_val(const Ctrl &c) { |
| 300 | if (c.kind == CK_TOGGLE && c.bval) | 287 | if (c.kind == CK_TOGGLE && c.bval) |
| 301 | return *c.bval ? 1 : 0; | 288 | return *c.bval ? 1 : 0; |
| 302 | if ((c.kind == CK_SLIDER || c.kind == CK_CHOICE) && c.ival) | 289 | if (c.kind == CK_SLIDER && c.ival) |
| 303 | return *c.ival; | 290 | return *c.ival; |
| 304 | return 0; | 291 | return 0; |
| 305 | } | 292 | } |
| @@ -307,7 +294,7 @@ static void ctrl_set(Ctrl &c, int v) { | |||
| 307 | if (c.kind == CK_TOGGLE && c.bval) { | 294 | if (c.kind == CK_TOGGLE && c.bval) { |
| 308 | *c.bval = v != 0; | 295 | *c.bval = v != 0; |
| 309 | config_save(); | 296 | config_save(); |
| 310 | } else if ((c.kind == CK_SLIDER || c.kind == CK_CHOICE) && c.ival) { | 297 | } else if (c.kind == CK_SLIDER && c.ival) { |
| 311 | if (v < c.min) v = c.min; | 298 | if (v < c.min) v = c.min; |
| 312 | if (v > c.max) v = c.max; | 299 | if (v > c.max) v = c.max; |
| 313 | if (*c.ival != v) { | 300 | if (*c.ival != v) { |
| @@ -433,12 +420,17 @@ static void build_cards() { | |||
| 433 | add_toggle(fog, "Remove fog", | 420 | add_toggle(fog, "Remove fog", |
| 434 | "Disable fog entirely instead of recolouring it.", &g_fog_disable); | 421 | "Disable fog entirely instead of recolouring it.", &g_fog_disable); |
| 435 | 422 | ||
| 423 | static char sky_label[512]; | ||
| 424 | snprintf(sky_label, sizeof(sky_label), "Image: %s", | ||
| 425 | config_file_cstr("skybox.png")); | ||
| 436 | int sky = add_card(2, &g_skybox, "Skybox", | 426 | int sky = add_card(2, &g_skybox, "Skybox", |
| 437 | "Swap the skybox at runtime through sv_skyname. The " | 427 | "Replace the map skybox with a PNG image from the " |
| 438 | "map's own sky comes back on level change.", | 428 | "config directory (all six faces).", |
| 439 | true); | 429 | true); |
| 440 | add_choice(sky, "Sky", "Pick a skybox from the HL2 set.", skybox_options(), | 430 | add_info(sky, sky_label); |
| 441 | skybox_option_count(), &g_skybox_choice); | 431 | add_info(sky, "Edit skybox.png, then Reload image. Off restores the map sky."); |
| 432 | add_button(sky, "Reload image", "Re-read the PNG and apply it again.", | ||
| 433 | ACT_SKY_RELOAD); | ||
| 442 | 434 | ||
| 443 | int spread = add_card(3, &g_spread, "Spread control", | 435 | int spread = add_card(3, &g_spread, "Spread control", |
| 444 | "Choose a server shot seed that tightens the cone.", | 436 | "Choose a server shot seed that tightens the cone.", |
| @@ -855,8 +847,6 @@ static int header_h(const Card &c) { | |||
| 855 | static int label_max_w(const Ctrl &c) { | 847 | static int label_max_w(const Ctrl &c) { |
| 856 | if (c.kind == CK_SLIDER) | 848 | if (c.kind == CK_SLIDER) |
| 857 | return S(214.0f); | 849 | return S(214.0f); |
| 858 | if (c.kind == CK_CHOICE) | ||
| 859 | return card_content_w() - S(16.0f) - S(250.0f); | ||
| 860 | return card_content_w() - S(16.0f) - S(110.0f); | 850 | return card_content_w() - S(16.0f) - S(110.0f); |
| 861 | } | 851 | } |
| 862 | 852 | ||
| @@ -1431,25 +1421,6 @@ static void handle_mouse_click() { | |||
| 1431 | } | 1421 | } |
| 1432 | return; | 1422 | return; |
| 1433 | } | 1423 | } |
| 1434 | if (ct.kind == CK_CHOICE) { | ||
| 1435 | int base = label_row_h(ct); | ||
| 1436 | Rect box{cr.x + cr.w - S(238.0f), | ||
| 1437 | by + (base - S(24.0f)) / 2, S(222.0f), S(24.0f)}; | ||
| 1438 | if (box.has(mx, my)) { | ||
| 1439 | int v = ctrl_val(ct); | ||
| 1440 | if (mx < box.x + S(36.0f)) | ||
| 1441 | v--; | ||
| 1442 | else if (mx >= box.x + box.w - S(36.0f)) | ||
| 1443 | v++; | ||
| 1444 | if (v < 0) v = 0; | ||
| 1445 | if (ct.nopts > 0 && v >= ct.nopts) v = ct.nopts - 1; | ||
| 1446 | if (v != ctrl_val(ct)) { | ||
| 1447 | *ct.ival = v; | ||
| 1448 | config_save(); | ||
| 1449 | } | ||
| 1450 | } | ||
| 1451 | return; | ||
| 1452 | } | ||
| 1453 | if (ct.kind == CK_BUTTON) { | 1424 | if (ct.kind == CK_BUTTON) { |
| 1454 | int bw = button_w(ct.label); | 1425 | int bw = button_w(ct.label); |
| 1455 | Rect b{cr.x + cr.w - S(16.0f) - bw, | 1426 | Rect b{cr.x + cr.w - S(16.0f) - bw, |
| @@ -1469,6 +1440,9 @@ static void handle_mouse_click() { | |||
| 1469 | g_reset_armed = true; | 1440 | g_reset_armed = true; |
| 1470 | g_reset_armed_at = now; | 1441 | g_reset_armed_at = now; |
| 1471 | } | 1442 | } |
| 1443 | } else if (ct.action == ACT_SKY_RELOAD) { | ||
| 1444 | skybox_reload_image(); | ||
| 1445 | toastf(TOAST_INFO, "Reloading skybox.png"); | ||
| 1472 | } | 1446 | } |
| 1473 | return; | 1447 | return; |
| 1474 | } | 1448 | } |
| @@ -1840,25 +1814,6 @@ static void draw_card(const Rect &clip, int index, int y) { | |||
| 1840 | outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip); | 1814 | outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip); |
| 1841 | if (picker_open_for(ct)) | 1815 | if (picker_open_for(ct)) |
| 1842 | draw_picker(clip, cr, ct, by); | 1816 | draw_picker(clip, cr, ct, by); |
| 1843 | } else if (ct.kind == CK_CHOICE) { | ||
| 1844 | int v = ctrl_val(ct); | ||
| 1845 | const char *name = (ct.opts && v >= 0 && v < ct.nopts) | ||
| 1846 | ? ct.opts[v] | ||
| 1847 | : "?"; | ||
| 1848 | Rect box{cr.x + cr.w - S(238.0f), | ||
| 1849 | by + (base_h - S(24.0f)) / 2, S(222.0f), S(24.0f)}; | ||
| 1850 | fill(box, Col{45, 50, 60, 255}, &clip); | ||
| 1851 | outline(box, C_BORDER, &clip); | ||
| 1852 | int ty = box.y + (box.h - g_body_h) / 2; | ||
| 1853 | text_in(clip, box.x + S(8.0f), ty, "<", g_font_body, g_body_h, 0, | ||
| 1854 | C_DIM); | ||
| 1855 | text_in(clip, box.x + box.w - S(16.0f), ty, ">", g_font_body, g_body_h, | ||
| 1856 | 0, C_DIM); | ||
| 1857 | int w = text_w(name, g_body_h); | ||
| 1858 | int tx = box.x + (box.w - w) / 2; | ||
| 1859 | if (tx < box.x + S(24.0f)) | ||
| 1860 | tx = box.x + S(24.0f); | ||
| 1861 | text_in(clip, tx, ty, name, g_font_body, g_body_h, 0, C_TEXT); | ||
| 1862 | } | 1817 | } |
| 1863 | } | 1818 | } |
| 1864 | by += rh; | 1819 | by += rh; |
