summaryrefslogtreecommitdiffstats
path: root/src/world.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/world.cpp45
1 files changed, 42 insertions, 3 deletions
diff --git a/src/world.cpp b/src/world.cpp
index ea186b4..a0576ab 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -633,6 +633,10 @@ bool load_png_rgba(const char *path, std::vector<unsigned char> &pixels,
633} 633}
634 634
635std::vector<unsigned char> g_sky_pixels; 635std::vector<unsigned char> g_sky_pixels;
636// Pristine copy of the decoded PNG. HDR compensation is applied to
637// g_sky_pixels in place, so without this the linearised/darkened bytes bled
638// into the next map when it used the LDR sky shader.
639std::vector<unsigned char> g_sky_pixels_src;
636int g_sky_px_w = 0, g_sky_px_h = 0; 640int g_sky_px_w = 0, g_sky_px_h = 0;
637 641
638// ITextureRegenerator has two virtuals: RegenerateTextureBits then Release. 642// ITextureRegenerator has two virtuals: RegenerateTextureBits then Release.
@@ -717,6 +721,28 @@ bool is_sky_material(const char *name, const char *group) {
717 strncmp(name, "skybox/", 7) == 0; 721 strncmp(name, "skybox/", 7) == 0;
718} 722}
719 723
724// ITexture reference counting (material system header order, matching the
725// slots already used here: SetTextureRegenerator = 12, Download = 13):
726// 10 = IncrementReferenceCount, 11 = DecrementReferenceCount
727// The engine owns the original textures; holding a reference keeps them valid
728// between "capture" and "restore" instead of leaving a dangling pointer.
729static void texture_addref(void *tex) {
730 if (!tex)
731 return;
732 using Fn = void (*)(void *);
733 vfunc<Fn>(tex, 10)(tex);
734}
735
736void release_sky_originals() {
737 for (auto &r : g_sky_orig) {
738 if (!r.texture)
739 continue;
740 using Fn = void (*)(void *);
741 vfunc<Fn>(r.texture, 11)(r.texture);
742 }
743 g_sky_orig.clear();
744}
745
720void record_orig(const char *material, const char *var, const char *value, 746void record_orig(const char *material, const char *var, const char *value,
721 void *texture) { 747 void *texture) {
722 if (!material || !var) 748 if (!material || !var)
@@ -725,6 +751,7 @@ void record_orig(const char *material, const char *var, const char *value,
725 if (r.material == material && r.var == var) 751 if (r.material == material && r.var == var)
726 return; 752 return;
727 } 753 }
754 texture_addref(texture);
728 g_sky_orig.push_back( 755 g_sky_orig.push_back(
729 {material, var, value ? value : "", texture}); 756 {material, var, value ? value : "", texture});
730} 757}
@@ -750,6 +777,10 @@ int apply_sky_texture() {
750 // where the materials are definitely loaded, and re-upload if needed. 777 // where the materials are definitely loaded, and re-upload if needed.
751 bool hdr = sky_uses_hdr_shader(); 778 bool hdr = sky_uses_hdr_shader();
752 if (hdr && !g_sky_compensated && !g_sky_pixels.empty()) { 779 if (hdr && !g_sky_compensated && !g_sky_pixels.empty()) {
780 // Always start from the untouched decode so repeated HDR passes cannot
781 // compound the gamma conversion.
782 if (!g_sky_pixels_src.empty())
783 g_sky_pixels = g_sky_pixels_src;
753 for (size_t i = 0; i + 3 < g_sky_pixels.size(); i += 4) { 784 for (size_t i = 0; i + 3 < g_sky_pixels.size(); i += 4) {
754 for (int c = 0; c < 3; c++) { 785 for (int c = 0; c < 3; c++) {
755 float v = (float)g_sky_pixels[i + (size_t)c] / 255.0f; 786 float v = (float)g_sky_pixels[i + (size_t)c] / 255.0f;
@@ -822,7 +853,7 @@ void restore_sky_texture() {
822 } 853 }
823 void *ms = material_system(); 854 void *ms = material_system();
824 if (!ms) { 855 if (!ms) {
825 g_sky_orig.clear(); 856 release_sky_originals();
826 g_sky_applied = false; 857 g_sky_applied = false;
827 return; 858 return;
828 } 859 }
@@ -833,7 +864,7 @@ void restore_sky_texture() {
833 auto count = vfunc<MatCalls::CountFn>(ms, MATSYS_GetNumMaterials); 864 auto count = vfunc<MatCalls::CountFn>(ms, MATSYS_GetNumMaterials);
834 int total = count(ms); 865 int total = count(ms);
835 if (total <= 0 || total > 200000) { 866 if (total <= 0 || total > 200000) {
836 g_sky_orig.clear(); 867 release_sky_originals();
837 g_sky_applied = false; 868 g_sky_applied = false;
838 return; 869 return;
839 } 870 }
@@ -866,7 +897,7 @@ void restore_sky_texture() {
866 vfunc<SetStrFn>(var, 5)(var, r.value.c_str()); 897 vfunc<SetStrFn>(var, 5)(var, r.value.c_str());
867 } 898 }
868 } 899 }
869 g_sky_orig.clear(); 900 release_sky_originals();
870 g_sky_applied = false; 901 g_sky_applied = false;
871} 902}
872 903
@@ -925,6 +956,7 @@ void load_sky_texture() {
925 return; 956 return;
926 } 957 }
927 g_sky_pixels.swap(pixels); 958 g_sky_pixels.swap(pixels);
959 g_sky_pixels_src = g_sky_pixels;
928 g_sky_px_w = w; 960 g_sky_px_w = w;
929 g_sky_px_h = h; 961 g_sky_px_h = h;
930 962
@@ -960,6 +992,13 @@ void skybox_invalidate() {
960 992
961void skybox_reload_image() { 993void skybox_reload_image() {
962 restore_sky_texture(); 994 restore_sky_texture();
995 // Detach the regenerator before dropping the handle: the old texture may
996 // outlive it and must not call back into a regenerator whose pixels were
997 // replaced.
998 if (g_sky_tex) {
999 using SetRegenFn = void (*)(void *, void *);
1000 vfunc<SetRegenFn>(g_sky_tex, 12)(g_sky_tex, nullptr);
1001 }
963 g_sky_tex = nullptr; 1002 g_sky_tex = nullptr;
964 g_sky_tex_tried = false; 1003 g_sky_tex_tried = false;
965 g_sky_reload = true; 1004 g_sky_reload = true;