summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 00:21:55 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 00:21:55 +0100
commitb97ac5d495ce82e6f1cf051c3f3a29ff38c91982 (patch)
tree7b16b311fb9fea94d4d160cc4cdd634f13033c25
parentSkybox image: decode PNG ourselves to avoid symbol interposition (diff)
downloadhl2h-b97ac5d495ce82e6f1cf051c3f3a29ff38c91982.tar.gz
hl2h-b97ac5d495ce82e6f1cf051c3f3a29ff38c91982.zip
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.
Diffstat (limited to '')
-rw-r--r--CONTEXT.md16
-rw-r--r--src/gui.cpp29
-rw-r--r--src/world.cpp43
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.
144 `x`, `y`, `category` and an `expanded` bitmask (card index order). Every 144 `x`, `y`, `category` and an `expanded` bitmask (card index order). Every
145 feature now persists an `active` and `bind` pair, including movement, melee 145 feature now persists an `active` and `bind` pair, including movement, melee
146 and spread. 146 and spread.
147- All card contents are clipped to the scrolling content rect, including
148 checkboxes and the expand chevron; only clipped fills/text were handled
149 before, so cards scrolled out of view leaked header widgets outside the
150 window.
147- **Colour control (`CK_COLOR`)**: row shows a live swatch; clicking the row 151- **Colour control (`CK_COLOR`)**: row shows a live swatch; clicking the row
148 expands an inline picker (saturation/value square + hue bar + hex/RGB 152 expands an inline picker (saturation/value square + hue bar + hex/RGB
149 preview) instead of three RGB sliders, growing the card in place so it 153 preview) instead of three RGB sliders, growing the card in place so it
@@ -377,9 +381,15 @@ vocabulary / decisions.
377 `IVTFTexture::ImageData(frame,face,mip)` (39). `IVTFTexture`'s virtual 381 `IVTFTexture::ImageData(frame,face,mip)` (39). `IVTFTexture`'s virtual
378 destructor occupies two slots, so Width=15, Height=16, RowSizeInBytes=19. 382 destructor occupies two slots, so Width=15, Height=16, RowSizeInBytes=19.
379- The texture is assigned with `IMaterialVar::SetTextureValue` to 383- The texture is assigned with `IMaterialVar::SetTextureValue` to
380 `$basetexture`, plus `$hdrbasetexture` / `$hdrcompressedtexture` when those 384 `$basetexture` and `$hdrbasetexture` on every material in the `SkyBox
381 vars exist, on every material in the `SkyBox textures` group or named 385 textures` group or named `skybox/*`. The same image maps onto all six faces.
382 `skybox/*`. The same image is mapped onto all six faces. 386- HDR sky materials use the `sky` shader, whose texture binding happens in
387 `SHADER_INIT`: it samples `$hdrcompressedtexture` (DXT5) when defined and
388 otherwise `$hdrbasetexture`. Putting an RGBA8 texture into the DXT5 slot
389 renders black, so `$hdrcompressedtexture` (+ its `0/1/2` variants) is
390 `SetUndefined()` and `$hdrbasetexture` is pointed at our texture instead.
391 The shader state is then rebuilt with `IMaterial::Refresh()` (slot 37),
392 otherwise the old bindings stay cached.
383- Original textures are recorded per material/var (texture pointer plus the 393- Original textures are recorded per material/var (texture pointer plus the
384 string as a fallback) and restored when the feature is disabled. Records 394 string as a fallback) and restored when the feature is disabled. Records
385 survive level changes so a reused sky material can still be restored; the 395 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() {
1672// Draw 1672// Draw
1673// --------------------------------------------------------------------------- 1673// ---------------------------------------------------------------------------
1674 1674
1675static void draw_checkbox(int x, int y, bool on) { 1675static void draw_checkbox(int x, int y, bool on,
1676 const Rect *clip = nullptr) {
1676 Rect box{x, y, S(15.0f), S(15.0f)}; 1677 Rect box{x, y, S(15.0f), S(15.0f)};
1677 fill(box, on ? C_ACCENT : Col{30, 34, 42, 255}); 1678 fill(box, on ? C_ACCENT : Col{30, 34, 42, 255}, clip);
1678 outline(box, C_BORDER); 1679 outline(box, C_BORDER, clip);
1679 if (on) { 1680 if (on) {
1680 line(x + S(3.0f), y + S(8.0f), x + S(7.0f), y + S(12.0f), C_BG); 1681 line(x + S(3.0f), y + S(8.0f), x + S(7.0f), y + S(12.0f), C_BG);
1681 line(x + S(7.0f), y + S(12.0f), x + S(13.0f), y + S(4.0f), C_BG); 1682 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) {
1719 Rect head{cr.x, cr.y, cr.w, hh}; 1720 Rect head{cr.x, cr.y, cr.w, hh};
1720 fill(head, C_HEADER, &clip); 1721 fill(head, C_HEADER, &clip);
1721 1722
1722 // expand arrow 1723 // expand arrow (clipped like the rest of the card; header rows of cards
1724 // scrolled out of view must not leak into the window chrome)
1723 int ax = cr.x + S(10.0f), ay = cr.y + (hh - S(8.0f)) / 2; 1725 int ax = cr.x + S(10.0f), ay = cr.y + (hh - S(8.0f)) / 2;
1724 if (c.expanded) { 1726 if (ay >= clip.y && ay + S(8.0f) <= clip.y + clip.h) {
1725 line(ax, ay, ax + S(4.0f), ay + S(5.0f), C_DIM); 1727 if (c.expanded) {
1726 line(ax + S(4.0f), ay + S(5.0f), ax + S(8.0f), ay, C_DIM); 1728 line(ax, ay, ax + S(4.0f), ay + S(5.0f), C_DIM);
1727 } else { 1729 line(ax + S(4.0f), ay + S(5.0f), ax + S(8.0f), ay, C_DIM);
1728 line(ax, ay, ax + S(5.0f), ay + S(4.0f), C_DIM); 1730 } else {
1729 line(ax + S(5.0f), ay + S(4.0f), ax, ay + S(8.0f), C_DIM); 1731 line(ax, ay, ax + S(5.0f), ay + S(4.0f), C_DIM);
1732 line(ax + S(5.0f), ay + S(4.0f), ax, ay + S(8.0f), C_DIM);
1733 }
1730 } 1734 }
1731 1735
1732 // title (wraps instead of truncating) 1736 // title (wraps instead of truncating)
@@ -1741,7 +1745,7 @@ static void draw_card(const Rect &clip, int index, int y) {
1741 if (c.feat) { 1745 if (c.feat) {
1742 bool active = c.feat->active; 1746 bool active = c.feat->active;
1743 int active_y = cr.y + (hh - S(18.0f)) / 2; 1747 int active_y = cr.y + (hh - S(18.0f)) / 2;
1744 draw_checkbox(cr.x + cr.w - S(170.0f), active_y, active); 1748 draw_checkbox(cr.x + cr.w - S(170.0f), active_y, active, &clip);
1745 text_in(clip, cr.x + cr.w - S(148.0f), 1749 text_in(clip, cr.x + cr.w - S(148.0f),
1746 active_y + (S(18.0f) - g_body_h) / 2, active ? "On" : "Off", 1750 active_y + (S(18.0f) - g_body_h) / 2, active ? "On" : "Off",
1747 g_font_body, g_body_h, 0, active ? C_GOOD : C_DIM); 1751 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) {
1798 maxw, C_TEXT, S(2.0f)); 1802 maxw, C_TEXT, S(2.0f));
1799 if (ct.kind == CK_TOGGLE) { 1803 if (ct.kind == CK_TOGGLE) {
1800 bool on = ctrl_val(ct) != 0; 1804 bool on = ctrl_val(ct) != 0;
1801 draw_checkbox(cr.x + cr.w - S(31.0f), by + (rh - S(15.0f)) / 2, on); 1805 draw_checkbox(cr.x + cr.w - S(31.0f), by + (rh - S(15.0f)) / 2, on,
1806 &clip);
1802 text_in(clip, cr.x + cr.w - S(58.0f), by + (rh - g_body_h) / 2, 1807 text_in(clip, cr.x + cr.w - S(58.0f), by + (rh - g_body_h) / 2,
1803 on ? "On" : "Off", g_font_body, g_body_h, 0, 1808 on ? "On" : "Off", g_font_body, g_body_h, 0,
1804 on ? C_GOOD : C_DIM); 1809 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:
587 return; 587 return;
588 int pitch = vfunc<PitchFn>(vtf, 19)(vtf, 0); // RowSizeInBytes(0) 588 int pitch = vfunc<PitchFn>(vtf, 19)(vtf, 0); // RowSizeInBytes(0)
589 unsigned char *dst = vfunc<DataFn>(vtf, 39)(vtf, 0, 0, 0); // ImageData 589 unsigned char *dst = vfunc<DataFn>(vtf, 39)(vtf, 0, 0, 0); // ImageData
590 static bool logged_regen = false;
591 if (!logged_regen) {
592 logged_regen = true;
593 logf("skybox: regenerator %dx%d pitch=%d dst=%p first=%02x%02x%02x%02x",
594 w, h, pitch, dst, g_sky_pixels[0], g_sky_pixels[1],
595 g_sky_pixels[2], g_sky_pixels[3]);
596 }
590 if (!dst || pitch <= 0) 597 if (!dst || pitch <= 0)
591 return; 598 return;
592 int rows = h < g_sky_px_h ? h : g_sky_px_h; 599 int rows = h < g_sky_px_h ? h : g_sky_px_h;
@@ -675,24 +682,44 @@ int apply_sky_texture() {
675 using SetTexFn = void (*)(void *, void *); 682 using SetTexFn = void (*)(void *, void *);
676 auto find_var = vfunc<FindVarFn>(mat, 11); // IMaterial::FindVar 683 auto find_var = vfunc<FindVarFn>(mat, 11); // IMaterial::FindVar
677 684
678 static const char *vars[] = {"$basetexture", "$hdrbasetexture", 685 using GetTexFn = void *(*)(void *);
679 "$hdrcompressedtexture", nullptr}; 686 using IsDefFn = bool (*)(void *);
680 for (int vi = 0; vars[vi]; vi++) { 687 using UndefFn = void (*)(void *);
688 static const char *tex_vars[] = {"$basetexture", "$hdrbasetexture",
689 nullptr};
690 for (int vi = 0; tex_vars[vi]; vi++) {
681 bool found = false; 691 bool found = false;
682 void *var = find_var(mat, vars[vi], &found, false); 692 void *var = find_var(mat, tex_vars[vi], &found, false);
683 if (!found || !var) 693 if (!found || !var)
684 continue; 694 continue;
685 using GetTexFn = void *(*)(void *);
686 auto vget = vfunc<GetStrFn>(var, 6); // GetStringValue 695 auto vget = vfunc<GetStrFn>(var, 6); // GetStringValue
687 auto vgettex = vfunc<GetTexFn>(var, 0); // GetTextureValue 696 auto vgettex = vfunc<GetTexFn>(var, 0); // GetTextureValue
688 auto vset = vfunc<SetTexFn>(var, 14); // SetTextureValue 697 auto vset = vfunc<SetTexFn>(var, 14); // SetTextureValue
689 const char *orig = vget(var); 698 record_orig(name, tex_vars[vi], vget(var), vgettex(var));
690 void *orig_tex = vgettex(var);
691 record_orig(name, vars[vi], orig, orig_tex);
692 vset(var, g_sky_tex); 699 vset(var, g_sky_tex);
693 if (vi == 0) 700 if (vi == 0)
694 hits++; 701 hits++;
695 } 702 }
703 // The HDR sky shader samples $hdrcompressedtexture (DXT5) when defined,
704 // which cannot hold our RGBA texture. Undefine it so the shader falls back
705 // to $hdrbasetexture, which now points at our image.
706 static const char *undef_vars[] = {
707 "$hdrcompressedtexture", "$hdrcompressedtexture0",
708 "$hdrcompressedtexture1", "$hdrcompressedtexture2", nullptr};
709 for (int vi = 0; undef_vars[vi]; vi++) {
710 bool found = false;
711 void *var = find_var(mat, undef_vars[vi], &found, false);
712 if (!found || !var)
713 continue;
714 if (!vfunc<IsDefFn>(var, 17)(var))
715 continue;
716 record_orig(name, undef_vars[vi], vfunc<GetStrFn>(var, 6)(var),
717 vfunc<GetTexFn>(var, 0)(var));
718 vfunc<UndefFn>(var, 18)(var); // SetUndefined
719 }
720 // The sky shader binds its texture in SHADER_INIT, so changing the vars is
721 // not enough: rebuild the material's shader state.
722 vfunc<void (*)(void *)>(mat, 37)(mat); // IMaterial::Refresh
696 } 723 }
697 return hits; 724 return hits;
698} 725}