summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/world.cpp59
1 files changed, 32 insertions, 27 deletions
diff --git a/src/world.cpp b/src/world.cpp
index 03b30a8..092df77 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -387,7 +387,7 @@ int paeth(int a, int b, int c) {
387} 387}
388 388
389bool load_png_rgba(const char *path, std::vector<unsigned char> &pixels, 389bool load_png_rgba(const char *path, std::vector<unsigned char> &pixels,
390 int &out_w, int &out_h, bool hdr_compensation) { 390 int &out_w, int &out_h) {
391 FILE *f = fopen(path, "rb"); 391 FILE *f = fopen(path, "rb");
392 if (!f) { 392 if (!f) {
393 logf("skybox: cannot open %s", path); 393 logf("skybox: cannot open %s", path);
@@ -610,24 +610,6 @@ bool load_png_rgba(const char *path, std::vector<unsigned char> &pixels,
610 } 610 }
611 } 611 }
612 612
613 // HDR sky materials use the "sky" shader. It samples the compressed HDR
614 // texture as rgb *= alpha, multiplies by 8, then converts linear->sRGB for
615 // display. Cancel the 8x with alpha = 1/8 and feed linear values so the
616 // final conversion restores the original sRGB image.
617 if (hdr_compensation) {
618 for (size_t i = 0; i + 3 < pixels.size(); i += 4) {
619 float rgb[3];
620 for (int c = 0; c < 3; c++) {
621 float v = (float)pixels[i + (size_t)c] / 255.0f;
622 rgb[c] = (v <= 0.04045f) ? v / 12.92f
623 : powf((v + 0.055f) / 1.055f, 2.4f);
624 pixels[i + (size_t)c] = (unsigned char)(rgb[c] * 255.0f + 0.5f);
625 }
626 pixels[i + 3] = 32; // 1/8 * 255
627 }
628 logf("skybox: HDR sky shader, using linear colours with 1/8 alpha");
629 }
630
631 out_w = (int)w; 613 out_w = (int)w;
632 out_h = (int)h; 614 out_h = (int)h;
633 return true; 615 return true;
@@ -692,6 +674,7 @@ SkyRegenerator g_sky_regenerator;
692void *g_sky_tex = nullptr; 674void *g_sky_tex = nullptr;
693bool g_sky_tex_tried = false; 675bool g_sky_tex_tried = false;
694bool g_sky_applied = false; 676bool g_sky_applied = false;
677bool g_sky_compensated = false;
695bool g_sky_reload = false; 678bool g_sky_reload = false;
696double g_sky_retry_at = 0.0; 679double g_sky_retry_at = 0.0;
697bool g_sky_fail_logged = false; 680bool g_sky_fail_logged = false;
@@ -729,6 +712,8 @@ void record_orig(const char *material, const char *var, const char *value,
729 {material, var, value ? value : "", texture}); 712 {material, var, value ? value : "", texture});
730} 713}
731 714
715bool sky_uses_hdr_shader();
716
732int apply_sky_texture() { 717int apply_sky_texture() {
733 void *ms = material_system(); 718 void *ms = material_system();
734 if (!ms || !g_sky_tex) 719 if (!ms || !g_sky_tex)
@@ -741,6 +726,29 @@ int apply_sky_texture() {
741 int total = count(ms); 726 int total = count(ms);
742 if (total <= 0 || total > 200000) 727 if (total <= 0 || total > 200000)
743 return 0; 728 return 0;
729 // HDR sky materials use the "sky"/Sky_HDR_DX9 shader, which treats the
730 // compressed texture as rgb *= alpha, scales by 8, then converts
731 // linear->sRGB. Cancel the 8x with alpha = 1/8 and feed linear values so
732 // the final conversion restores the original sRGB image. Decide this here,
733 // where the materials are definitely loaded, and re-upload if needed.
734 bool hdr = sky_uses_hdr_shader();
735 if (hdr && !g_sky_compensated && !g_sky_pixels.empty()) {
736 for (size_t i = 0; i + 3 < g_sky_pixels.size(); i += 4) {
737 for (int c = 0; c < 3; c++) {
738 float v = (float)g_sky_pixels[i + (size_t)c] / 255.0f;
739 float lin = (v <= 0.04045f) ? v / 12.92f
740 : powf((v + 0.055f) / 1.055f, 2.4f);
741 g_sky_pixels[i + (size_t)c] = (unsigned char)(lin * 255.0f + 0.5f);
742 }
743 g_sky_pixels[i + 3] = 32; // 1/8
744 }
745 using SetRegenFn = void (*)(void *, void *);
746 using DownloadFn = void (*)(void *, void *, int);
747 vfunc<SetRegenFn>(g_sky_tex, 12)(g_sky_tex, &g_sky_regenerator);
748 vfunc<DownloadFn>(g_sky_tex, 13)(g_sky_tex, nullptr, 0);
749 g_sky_compensated = true;
750 logf("skybox: HDR sky shader, using linear colours with 1/8 alpha");
751 }
744 unsigned short inv = invalid(ms); 752 unsigned short inv = invalid(ms);
745 int seen = 0, hits = 0; 753 int seen = 0, hits = 0;
746 for (unsigned short h = first(ms); h != inv; h = next(ms, h)) { 754 for (unsigned short h = first(ms); h != inv; h = next(ms, h)) {
@@ -892,8 +900,7 @@ void load_sky_texture() {
892 std::vector<unsigned char> pixels; 900 std::vector<unsigned char> pixels;
893 int w = 0, h = 0; 901 int w = 0, h = 0;
894 const char *path = config_file_cstr("skybox.png"); 902 const char *path = config_file_cstr("skybox.png");
895 bool hdr = sky_uses_hdr_shader(); 903 if (!load_png_rgba(path, pixels, w, h)) {
896 if (!load_png_rgba(path, pixels, w, h, hdr)) {
897 if (!g_sky_fail_logged) { 904 if (!g_sky_fail_logged) {
898 g_sky_fail_logged = true; 905 g_sky_fail_logged = true;
899 toastf(TOAST_ERROR, "Skybox image missing or unreadable: %s", path); 906 toastf(TOAST_ERROR, "Skybox image missing or unreadable: %s", path);
@@ -908,12 +915,9 @@ void load_sky_texture() {
908 // is called on Download() and again on device restore. 915 // is called on Download() and again on device restore.
909 using CreateProcFn = void *(*)(void *, const char *, const char *, int, int, 916 using CreateProcFn = void *(*)(void *, const char *, const char *, int, int,
910 int, int); 917 int, int);
911 // 0x40 = TEXTUREFLAGS_SRGB: the LDR sky path decodes sRGB from the texture 918 g_sky_tex = vfunc<CreateProcFn>(ms, 81)(
912 // flags, the HDR path forces sRGB reads off and does its own conversion. 919 ms, "hl2hake/skybox", GROUP_SKYBOX, w, h, 0 /* RGBA8888 */,
913 int tex_flags = 0x10C /* CLAMPS|CLAMPT|NOMIP */ | (hdr ? 0 : 0x40); 920 0x10C /* CLAMPS|CLAMPT|NOMIP */);
914 g_sky_tex = vfunc<CreateProcFn>(ms, 81)(ms, "hl2hake/skybox",
915 GROUP_SKYBOX, w, h, 0 /*RGBA8888*/,
916 tex_flags);
917 if (!g_sky_tex) { 921 if (!g_sky_tex) {
918 logf("skybox: CreateProceduralTexture failed for %dx%d", w, h); 922 logf("skybox: CreateProceduralTexture failed for %dx%d", w, h);
919 return; 923 return;
@@ -922,6 +926,7 @@ void load_sky_texture() {
922 using DownloadFn = void (*)(void *, void *, int); 926 using DownloadFn = void (*)(void *, void *, int);
923 vfunc<SetRegenFn>(g_sky_tex, 12)(g_sky_tex, &g_sky_regenerator); 927 vfunc<SetRegenFn>(g_sky_tex, 12)(g_sky_tex, &g_sky_regenerator);
924 vfunc<DownloadFn>(g_sky_tex, 13)(g_sky_tex, nullptr, 0); 928 vfunc<DownloadFn>(g_sky_tex, 13)(g_sky_tex, nullptr, 0);
929 g_sky_compensated = false;
925 g_sky_fail_logged = false; 930 g_sky_fail_logged = false;
926 logf("skybox: image %s (%dx%d) uploaded", path, w, h); 931 logf("skybox: image %s (%dx%d) uploaded", path, w, h);
927} 932}