diff options
| -rw-r--r-- | src/world.cpp | 59 |
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 | ||
| 389 | bool load_png_rgba(const char *path, std::vector<unsigned char> &pixels, | 389 | bool 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; | |||
| 692 | void *g_sky_tex = nullptr; | 674 | void *g_sky_tex = nullptr; |
| 693 | bool g_sky_tex_tried = false; | 675 | bool g_sky_tex_tried = false; |
| 694 | bool g_sky_applied = false; | 676 | bool g_sky_applied = false; |
| 677 | bool g_sky_compensated = false; | ||
| 695 | bool g_sky_reload = false; | 678 | bool g_sky_reload = false; |
| 696 | double g_sky_retry_at = 0.0; | 679 | double g_sky_retry_at = 0.0; |
| 697 | bool g_sky_fail_logged = false; | 680 | bool 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 | ||
| 715 | bool sky_uses_hdr_shader(); | ||
| 716 | |||
| 732 | int apply_sky_texture() { | 717 | int 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 | } |
