summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTEXT.md9
-rw-r--r--src/world.cpp66
2 files changed, 67 insertions, 8 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index a490403..669c1fb 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -364,8 +364,13 @@ vocabulary / decisions.
364 the INI). Not `sv_skyname`: that did not take effect at runtime in this 364 the INI). Not `sv_skyname`: that did not take effect at runtime in this
365 build, so the skybox card now works on materials instead. 365 build, so the skybox card now works on materials instead.
366- PNG is decoded with libpng16's simplified API (`dlopen`ed, no link 366- PNG is decoded with libpng16's simplified API (`dlopen`ed, no link
367 dependency); the RGBA pixels become an `ITexture` via 367 dependency). This build's `IMaterialSystem` interface ends at
368 `IMaterialSystem::CreateTextureFromBits` (RGBA8888, one mip). 368 `GetDisplayDeviceName` (the slots after it belong to `CMaterialSystem`
369 itself), so there is **no `CreateTextureFromBits`**. The pixels are uploaded
370 through `CreateProceduralTexture` (vtable 81) + an `ITextureRegenerator`
371 (`SetTextureRegenerator` 12, `Download` 13), which fills mip 0 through
372 `IVTFTexture::ImageData(frame,face,mip)` (39). `IVTFTexture`'s virtual
373 destructor occupies two slots, so Width=15, Height=16, RowSizeInBytes=19.
369- The texture is assigned with `IMaterialVar::SetTextureValue` to 374- The texture is assigned with `IMaterialVar::SetTextureValue` to
370 `$basetexture`, plus `$hdrbasetexture` / `$hdrcompressedtexture` when those 375 `$basetexture`, plus `$hdrbasetexture` / `$hdrcompressedtexture` when those
371 vars exist, on every material in the `SkyBox textures` group or named 376 vars exist, on every material in the `SkyBox textures` group or named
diff --git a/src/world.cpp b/src/world.cpp
index cf85795..391ab1a 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -409,6 +409,49 @@ bool load_png_rgba(const char *path, std::vector<unsigned char> &pixels,
409 return true; 409 return true;
410} 410}
411 411
412std::vector<unsigned char> g_sky_pixels;
413int g_sky_px_w = 0, g_sky_px_h = 0;
414
415// ITextureRegenerator has two virtuals: RegenerateTextureBits then Release.
416// This build has no IMaterialSystem::CreateTextureFromBits (its interface stops
417// at GetDisplayDeviceName), so the pixels are uploaded through a procedural
418// texture + regenerator instead.
419class SkyRegenerator {
420public:
421 virtual void RegenerateTextureBits(void *texture, void *vtf, void *rect) {
422 (void)texture;
423 (void)rect;
424 if (!vtf || g_sky_pixels.empty())
425 return;
426 using IntFn = int (*)(void *);
427 using PitchFn = int (*)(void *, int);
428 using DataFn = unsigned char *(*)(void *, int, int, int);
429 // IVTFTexture vtable: dtor takes 2 slots, then Init, so Width=15,
430 // Height=16, RowSizeInBytes=19, ImageData(frame,face,mip)=39.
431 int w = vfunc<IntFn>(vtf, 15)(vtf); // IVTFTexture::Width
432 int h = vfunc<IntFn>(vtf, 16)(vtf); // IVTFTexture::Height
433 if (w <= 0 || h <= 0)
434 return;
435 int pitch = vfunc<PitchFn>(vtf, 19)(vtf, 0); // RowSizeInBytes(0)
436 unsigned char *dst = vfunc<DataFn>(vtf, 39)(vtf, 0, 0, 0); // ImageData
437 if (!dst || pitch <= 0)
438 return;
439 int rows = h < g_sky_px_h ? h : g_sky_px_h;
440 int src_stride = g_sky_px_w * 4;
441 int copy = w * 4;
442 if (copy > src_stride)
443 copy = src_stride;
444 for (int y = 0; y < rows; y++) {
445 const unsigned char *src =
446 g_sky_pixels.data() + (size_t)y * (size_t)src_stride;
447 memcpy(dst + (size_t)y * (size_t)pitch, src, (size_t)copy);
448 }
449 }
450 virtual void Release() {}
451};
452
453SkyRegenerator g_sky_regenerator;
454
412void *g_sky_tex = nullptr; 455void *g_sky_tex = nullptr;
413bool g_sky_tex_tried = false; 456bool g_sky_tex_tried = false;
414bool g_sky_applied = false; 457bool g_sky_applied = false;
@@ -573,16 +616,27 @@ void load_sky_texture() {
573 } 616 }
574 return; 617 return;
575 } 618 }
576 using CreateTexFn = void *(*)(void *, int, int, int, int, int, 619 g_sky_pixels.swap(pixels);
577 unsigned char *); 620 g_sky_px_w = w;
578 g_sky_tex = vfunc<CreateTexFn>(ms, 134)(ms, w, h, 1, 0 /* RGBA8888 */, 621 g_sky_px_h = h;
579 (int)pixels.size(), pixels.data()); 622
623 // The procedural texture keeps its own copy through the regenerator, which
624 // is called on Download() and again on device restore.
625 using CreateProcFn = void *(*)(void *, const char *, const char *, int, int,
626 int, int);
627 g_sky_tex = vfunc<CreateProcFn>(ms, 81)(
628 ms, "hl2hake/skybox", GROUP_SKYBOX, w, h, 0 /* RGBA8888 */,
629 0x10C /* CLAMPS|CLAMPT|NOMIP */);
580 if (!g_sky_tex) { 630 if (!g_sky_tex) {
581 logf("skybox: CreateTextureFromBits failed for %dx%d", w, h); 631 logf("skybox: CreateProceduralTexture failed for %dx%d", w, h);
582 return; 632 return;
583 } 633 }
634 using SetRegenFn = void (*)(void *, void *);
635 using DownloadFn = void (*)(void *, void *, int);
636 vfunc<SetRegenFn>(g_sky_tex, 12)(g_sky_tex, &g_sky_regenerator);
637 vfunc<DownloadFn>(g_sky_tex, 13)(g_sky_tex, nullptr, 0);
584 g_sky_fail_logged = false; 638 g_sky_fail_logged = false;
585 logf("skybox: image %s (%dx%d) loaded", path, w, h); 639 logf("skybox: image %s (%dx%d) uploaded", path, w, h);
586} 640}
587 641
588} // namespace 642} // namespace