From 4632788c4bef265e58630473eed5559bf125a726 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 00:29:24 +0100 Subject: Skybox image: power-of-two resize; upload at FRAME_RENDER_START - The material system rejects textures whose dimensions are not multiples of 4 ("Image dimensions must be multiple of 4!"), so scale the decoded image to the next power of two (bilinear). 798x798 -> 1024x1024. The invalid VTF also crashed the renderer after the apply. - Move skybox_update() out of the VGUI paint hook into the FRAME_RENDER_START FrameStageNotify stage. ITexture::Download only regenerates bits when the material system considers the frame render-safe; during paint it returned early, the regenerator never ran (no pixels), and a later upload of the empty VTF crashed. - Log the regenerator entry so the log shows whether the upload path ran. --- src/world.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'src/world.cpp') diff --git a/src/world.cpp b/src/world.cpp index aaee058..1228c7a 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -557,6 +557,58 @@ bool load_png_rgba(const char *path, std::vector &pixels, } } + // The material system rejects textures whose dimensions are not multiples of + // 4 ("Image dimensions must be multiple of 4!" / "LoadTextureFromVTF: VTF + // dimensions do not match texture"). Scale to the next power of two; that is + // also what the old skybox code expects. + { + int tw = 1, th = 1; + while (tw < (int)w) + tw <<= 1; + while (th < (int)h) + th <<= 1; + if (tw > 4096) tw = 4096; + if (th > 4096) th = 4096; + if (tw != (int)w || th != (int)h) { + std::vector scaled((size_t)tw * (size_t)th * 4); + for (int y = 0; y < th; y++) { + float sy = ((float)y + 0.5f) * (float)h / (float)th - 0.5f; + int y0 = (int)sy; + float fy = sy - (float)y0; + if (y0 < 0) { y0 = 0; fy = 0.0f; } + int y1 = y0 + 1 < (int)h ? y0 + 1 : y0; + for (int x = 0; x < tw; x++) { + float sx = ((float)x + 0.5f) * (float)w / (float)tw - 0.5f; + int x0 = (int)sx; + float fx = sx - (float)x0; + if (x0 < 0) { x0 = 0; fx = 0.0f; } + int x1 = x0 + 1 < (int)w ? x0 + 1 : x0; + const unsigned char *p00 = pixels.data() + + ((size_t)y0 * (size_t)w + (size_t)x0) * 4; + const unsigned char *p10 = pixels.data() + + ((size_t)y0 * (size_t)w + (size_t)x1) * 4; + const unsigned char *p01 = pixels.data() + + ((size_t)y1 * (size_t)w + (size_t)x0) * 4; + const unsigned char *p11 = pixels.data() + + ((size_t)y1 * (size_t)w + (size_t)x1) * 4; + unsigned char *o = scaled.data() + + ((size_t)y * (size_t)tw + (size_t)x) * 4; + for (int c = 0; c < 4; c++) { + float v = (1.0f - fy) * + ((1.0f - fx) * p00[c] + fx * p10[c]) + + fy * ((1.0f - fx) * p01[c] + fx * p11[c]); + o[c] = (unsigned char)(v + 0.5f); + } + } + } + logf("skybox: resized %dx%d -> %dx%d for the material system", + (int)w, (int)h, tw, th); + pixels.swap(scaled); + w = (uint32_t)tw; + h = (uint32_t)th; + } + } + out_w = (int)w; out_h = (int)h; return true; @@ -574,6 +626,12 @@ public: virtual void RegenerateTextureBits(void *texture, void *vtf, void *rect) { (void)texture; (void)rect; + static bool entered = false; + if (!entered) { + entered = true; + logf("skybox: regenerator entered (vtf=%p, %zu pixels)", vtf, + g_sky_pixels.size()); + } if (!vtf || g_sky_pixels.empty()) return; using IntFn = int (*)(void *); -- cgit v1.2.3