From b97ac5d495ce82e6f1cf051c3f3a29ff38c91982 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 00:21:55 +0100 Subject: Skybox image: fix HDR sky binding; clip checkbox/arrow drawing The HDR sky materials use the "sky" shader with $hdrcompressedtexture (DXT5). Assigning an RGBA8 procedural texture there sampled as compressed data and rendered black. Point $hdrbasetexture at the image, undefine $hdrcompressedtexture (and its 0/1/2 variants) so the shader falls back, and call IMaterial::Refresh (37) because the sky shader binds textures in SHADER_INIT, which is otherwise cached. GUI: draw_checkbox and the card expand chevron ignored the content clip, so cards scrolled out of view leaked header widgets outside the window. Log the first regenerator call (size/pitch/first pixel) for diagnosis. --- CONTEXT.md | 16 +++++++++++++--- src/gui.cpp | 29 +++++++++++++++++------------ src/world.cpp | 43 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 5bd0436..b07e407 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -144,6 +144,10 @@ vocabulary / decisions. `x`, `y`, `category` and an `expanded` bitmask (card index order). Every feature now persists an `active` and `bind` pair, including movement, melee and spread. +- All card contents are clipped to the scrolling content rect, including + checkboxes and the expand chevron; only clipped fills/text were handled + before, so cards scrolled out of view leaked header widgets outside the + window. - **Colour control (`CK_COLOR`)**: row shows a live swatch; clicking the row expands an inline picker (saturation/value square + hue bar + hex/RGB preview) instead of three RGB sliders, growing the card in place so it @@ -377,9 +381,15 @@ vocabulary / decisions. `IVTFTexture::ImageData(frame,face,mip)` (39). `IVTFTexture`'s virtual destructor occupies two slots, so Width=15, Height=16, RowSizeInBytes=19. - The texture is assigned with `IMaterialVar::SetTextureValue` to - `$basetexture`, plus `$hdrbasetexture` / `$hdrcompressedtexture` when those - vars exist, on every material in the `SkyBox textures` group or named - `skybox/*`. The same image is mapped onto all six faces. + `$basetexture` and `$hdrbasetexture` on every material in the `SkyBox + textures` group or named `skybox/*`. The same image maps onto all six faces. +- HDR sky materials use the `sky` shader, whose texture binding happens in + `SHADER_INIT`: it samples `$hdrcompressedtexture` (DXT5) when defined and + otherwise `$hdrbasetexture`. Putting an RGBA8 texture into the DXT5 slot + renders black, so `$hdrcompressedtexture` (+ its `0/1/2` variants) is + `SetUndefined()` and `$hdrbasetexture` is pointed at our texture instead. + The shader state is then rebuilt with `IMaterial::Refresh()` (slot 37), + otherwise the old bindings stay cached. - Original textures are recorded per material/var (texture pointer plus the string as a fallback) and restored when the feature is disabled. Records survive level changes so a reused sky material can still be restored; the diff --git a/src/gui.cpp b/src/gui.cpp index eb53dff..ebc1f62 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -1672,10 +1672,11 @@ void gui_handle_input() { // Draw // --------------------------------------------------------------------------- -static void draw_checkbox(int x, int y, bool on) { +static void draw_checkbox(int x, int y, bool on, + const Rect *clip = nullptr) { Rect box{x, y, S(15.0f), S(15.0f)}; - fill(box, on ? C_ACCENT : Col{30, 34, 42, 255}); - outline(box, C_BORDER); + fill(box, on ? C_ACCENT : Col{30, 34, 42, 255}, clip); + outline(box, C_BORDER, clip); if (on) { line(x + S(3.0f), y + S(8.0f), x + S(7.0f), y + S(12.0f), C_BG); line(x + S(7.0f), y + S(12.0f), x + S(13.0f), y + S(4.0f), C_BG); @@ -1719,14 +1720,17 @@ static void draw_card(const Rect &clip, int index, int y) { Rect head{cr.x, cr.y, cr.w, hh}; fill(head, C_HEADER, &clip); - // expand arrow + // expand arrow (clipped like the rest of the card; header rows of cards + // scrolled out of view must not leak into the window chrome) int ax = cr.x + S(10.0f), ay = cr.y + (hh - S(8.0f)) / 2; - if (c.expanded) { - line(ax, ay, ax + S(4.0f), ay + S(5.0f), C_DIM); - line(ax + S(4.0f), ay + S(5.0f), ax + S(8.0f), ay, C_DIM); - } else { - line(ax, ay, ax + S(5.0f), ay + S(4.0f), C_DIM); - line(ax + S(5.0f), ay + S(4.0f), ax, ay + S(8.0f), C_DIM); + if (ay >= clip.y && ay + S(8.0f) <= clip.y + clip.h) { + if (c.expanded) { + line(ax, ay, ax + S(4.0f), ay + S(5.0f), C_DIM); + line(ax + S(4.0f), ay + S(5.0f), ax + S(8.0f), ay, C_DIM); + } else { + line(ax, ay, ax + S(5.0f), ay + S(4.0f), C_DIM); + line(ax + S(5.0f), ay + S(4.0f), ax, ay + S(8.0f), C_DIM); + } } // title (wraps instead of truncating) @@ -1741,7 +1745,7 @@ static void draw_card(const Rect &clip, int index, int y) { if (c.feat) { bool active = c.feat->active; int active_y = cr.y + (hh - S(18.0f)) / 2; - draw_checkbox(cr.x + cr.w - S(170.0f), active_y, active); + draw_checkbox(cr.x + cr.w - S(170.0f), active_y, active, &clip); text_in(clip, cr.x + cr.w - S(148.0f), active_y + (S(18.0f) - g_body_h) / 2, active ? "On" : "Off", g_font_body, g_body_h, 0, active ? C_GOOD : C_DIM); @@ -1798,7 +1802,8 @@ static void draw_card(const Rect &clip, int index, int y) { maxw, C_TEXT, S(2.0f)); if (ct.kind == CK_TOGGLE) { bool on = ctrl_val(ct) != 0; - draw_checkbox(cr.x + cr.w - S(31.0f), by + (rh - S(15.0f)) / 2, on); + draw_checkbox(cr.x + cr.w - S(31.0f), by + (rh - S(15.0f)) / 2, on, + &clip); text_in(clip, cr.x + cr.w - S(58.0f), by + (rh - g_body_h) / 2, on ? "On" : "Off", g_font_body, g_body_h, 0, on ? C_GOOD : C_DIM); diff --git a/src/world.cpp b/src/world.cpp index 621e5f7..aaee058 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -587,6 +587,13 @@ public: return; int pitch = vfunc(vtf, 19)(vtf, 0); // RowSizeInBytes(0) unsigned char *dst = vfunc(vtf, 39)(vtf, 0, 0, 0); // ImageData + static bool logged_regen = false; + if (!logged_regen) { + logged_regen = true; + logf("skybox: regenerator %dx%d pitch=%d dst=%p first=%02x%02x%02x%02x", + w, h, pitch, dst, g_sky_pixels[0], g_sky_pixels[1], + g_sky_pixels[2], g_sky_pixels[3]); + } if (!dst || pitch <= 0) return; int rows = h < g_sky_px_h ? h : g_sky_px_h; @@ -675,24 +682,44 @@ int apply_sky_texture() { using SetTexFn = void (*)(void *, void *); auto find_var = vfunc(mat, 11); // IMaterial::FindVar - static const char *vars[] = {"$basetexture", "$hdrbasetexture", - "$hdrcompressedtexture", nullptr}; - for (int vi = 0; vars[vi]; vi++) { + using GetTexFn = void *(*)(void *); + using IsDefFn = bool (*)(void *); + using UndefFn = void (*)(void *); + static const char *tex_vars[] = {"$basetexture", "$hdrbasetexture", + nullptr}; + for (int vi = 0; tex_vars[vi]; vi++) { bool found = false; - void *var = find_var(mat, vars[vi], &found, false); + void *var = find_var(mat, tex_vars[vi], &found, false); if (!found || !var) continue; - using GetTexFn = void *(*)(void *); auto vget = vfunc(var, 6); // GetStringValue auto vgettex = vfunc(var, 0); // GetTextureValue auto vset = vfunc(var, 14); // SetTextureValue - const char *orig = vget(var); - void *orig_tex = vgettex(var); - record_orig(name, vars[vi], orig, orig_tex); + record_orig(name, tex_vars[vi], vget(var), vgettex(var)); vset(var, g_sky_tex); if (vi == 0) hits++; } + // The HDR sky shader samples $hdrcompressedtexture (DXT5) when defined, + // which cannot hold our RGBA texture. Undefine it so the shader falls back + // to $hdrbasetexture, which now points at our image. + static const char *undef_vars[] = { + "$hdrcompressedtexture", "$hdrcompressedtexture0", + "$hdrcompressedtexture1", "$hdrcompressedtexture2", nullptr}; + for (int vi = 0; undef_vars[vi]; vi++) { + bool found = false; + void *var = find_var(mat, undef_vars[vi], &found, false); + if (!found || !var) + continue; + if (!vfunc(var, 17)(var)) + continue; + record_orig(name, undef_vars[vi], vfunc(var, 6)(var), + vfunc(var, 0)(var)); + vfunc(var, 18)(var); // SetUndefined + } + // The sky shader binds its texture in SHADER_INIT, so changing the vars is + // not enough: rebuild the material's shader state. + vfunc(mat, 37)(mat); // IMaterial::Refresh } return hits; } -- cgit v1.2.3