summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRunitaiLinden <davep@lindenlab.com>2024-03-08 12:01:20 -0600
committerGitHub <noreply@github.com>2024-03-08 12:01:20 -0600
commitc1bde75768e1374d4f094936d52ed29f6f5d3cba (patch)
treeeddbe1b79490e27e63e05fa36b4175bea1e02fc9
parentcca461647f4569fb57e35679dd86a863f5d52702 (diff)
HDRI Local Preview (#953)
* #926 WIP - HDRI import prototype v0 * #926 WIP -- add OpenEXR to autobuild.xml * #926 WIP -- Add OpenEXR cmake * #926 WIP -- Attempt at using OpenEXR autobuild package and don't hard code .exr file to load * #926 Unmangle autobuild.xml and get dll's in the right place (thanks, Caladbolg!) * implement mac shared libs plumbing for OpenEXR for secondlife/viewer#926 * Fix Xcode/clang compile error regarding new[]/delete[] mismatch * #926 HDRI Preview finishing touches. - Full ACES when HDRI is enabled - Fix for probes getting stuck paused - Add exposure and rotation controls --------- Co-authored-by: Brad Linden <brad@lindenlab.com>
-rw-r--r--autobuild.xml56
-rw-r--r--indra/cmake/Copy3rdPartyLibs.cmake12
-rw-r--r--indra/cmake/OpenEXR.cmake18
-rw-r--r--indra/llrender/llshadermgr.cpp2
-rw-r--r--indra/newview/CMakeLists.txt9
-rw-r--r--indra/newview/app_settings/settings.xml33
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyF.glsl23
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyV.glsl8
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl4
-rw-r--r--indra/newview/lldrawpoolwlsky.cpp38
-rw-r--r--indra/newview/llenvironment.cpp8
-rw-r--r--indra/newview/llfilepicker.cpp7
-rw-r--r--indra/newview/llfilepicker.h1
-rw-r--r--indra/newview/llreflectionmapmanager.cpp131
-rw-r--r--indra/newview/llreflectionmapmanager.h4
-rw-r--r--indra/newview/llviewermenu.cpp14
-rw-r--r--indra/newview/llviewershadermgr.cpp23
-rw-r--r--indra/newview/llviewershadermgr.h1
-rw-r--r--indra/newview/llvoavatar.cpp3
-rw-r--r--indra/newview/pipeline.cpp4
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml6
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml9
-rwxr-xr-xindra/newview/viewer_manifest.py14
24 files changed, 415 insertions, 16 deletions
diff --git a/autobuild.xml b/autobuild.xml
index 11b2783bd7..f0038f6532 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2198,6 +2198,62 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>description</key>
<string>OpenAL Soft is a software implementation of the OpenAL 3D audio API.</string>
</map>
+ <key>openexr</key>
+ <map>
+ <key>canonical_repo</key>
+ <string>https://github.com/secondlife/3p-openexr</string>
+ <key>copyright</key>
+ <string>Copyright (c) Contributors to the OpenEXR Project. All rights reserved.</string>
+ <key>description</key>
+ <string>OpenEXR provides the specification and reference implementation of the EXR file format, the professional-grade image storage format of the motion picture industry.</string>
+ <key>license</key>
+ <string>OpenEXR</string>
+ <key>license_file</key>
+ <string>LICENSES/openexr.txt</string>
+ <key>name</key>
+ <string>openexr</string>
+ <key>platforms</key>
+ <map>
+ <key>darwin64</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>hash</key>
+ <string>158cbe79bef4ecafb870052bbaca541e07107228</string>
+ <key>hash_algorithm</key>
+ <string>sha1</string>
+ <key>url</key>
+ <string>https://github.com/secondlife/3p-openexr/releases/download/v1.8/openexr-3.2.2-darwin64-6501c65.tar.zst</string>
+ </map>
+ <key>name</key>
+ <string>darwin64</string>
+ </map>
+ <key>windows64</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>hash</key>
+ <string>924f6ddf6669af023d1f3832cb79b50b913ae0ca</string>
+ <key>hash_algorithm</key>
+ <string>sha1</string>
+ <key>url</key>
+ <string>https://github.com/secondlife/3p-openexr/releases/download/v1.8/openexr-3.2.2-windows64-6501c65.tar.zst</string>
+ </map>
+ <key>name</key>
+ <string>windows64</string>
+ </map>
+ </map>
+ <key>source_type</key>
+ <string>git</string>
+ <key>vcs_branch</key>
+ <string>debug_autobuild</string>
+ <key>vcs_revision</key>
+ <string>5cd1075295c17b5f7085e83d5c16b13c7ecb2eb1</string>
+ <key>vcs_url</key>
+ <string>https://github.com/secondlife/3p-openexr</string>
+ <key>version</key>
+ <string>3.2.2</string>
+ </map>
<key>openjpeg</key>
<map>
<key>platforms</key>
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index 9f79c13a97..415641f65f 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -60,6 +60,12 @@ if(WINDOWS)
nghttp2.dll
libhunspell.dll
uriparser.dll
+ Iex-3_2.dll
+ IlmThread-3_2.dll
+ Imath-3_1.dll
+ OpenEXR-3_2.dll
+ OpenEXRCore-3_2.dll
+ OpenEXRUtil-3_2.dll
)
# ICU4C (same filenames for 32 and 64 bit builds)
@@ -184,6 +190,12 @@ elseif(DARWIN)
liburiparser.dylib
liburiparser.1.dylib
liburiparser.1.0.27.dylib
+ libIex-3_2.dylib
+ libIlmThread-3_2.dylib
+ libImath-3_1.dylib
+ libOpenEXR-3_2.dylib
+ libOpenEXRCore-3_2.dylib
+ libOpenEXRUtil-3_2.dylib
)
if (TARGET ll::fmodstudio)
diff --git a/indra/cmake/OpenEXR.cmake b/indra/cmake/OpenEXR.cmake
new file mode 100644
index 0000000000..ee21fac541
--- /dev/null
+++ b/indra/cmake/OpenEXR.cmake
@@ -0,0 +1,18 @@
+# -*- cmake -*-
+
+include(Prebuilt)
+
+include_guard()
+add_library( ll::openexr INTERFACE IMPORTED )
+
+if(USE_CONAN )
+ target_link_libraries( ll::openexr INTERFACE CONAN_PKG::openexr )
+ return()
+endif()
+
+use_prebuilt_binary(openexr)
+
+target_link_libraries( ll::openexr INTERFACE Iex-3_2 IlmThread-3_2 Imath-3_1 OpenEXR-3_2 OpenEXRCore-3_2 OpenEXRUtil-3_2)
+
+target_include_directories( ll::openexr SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/OpenEXR ${LIBS_PREBUILT_DIR}/include/Imath)
+
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 5384133220..6cfe065355 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -618,7 +618,7 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev
extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_SKIP_ATMOS 0.0 \n"); // atmo kill
extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_ATMOS 0.34\n"); // bit 0
extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_PBR 0.67\n"); // bit 1
- extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_MIRROR 1.0\n"); // bit 2
+ extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_HDRI 1.0\n"); // bit 2
extra_code_text[extra_code_count++] = strdup("#define GET_GBUFFER_FLAG(flag) (abs(norm.w-flag)< 0.1)\n");
if (defines)
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 6b15e847a3..8494ba5b49 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -34,6 +34,7 @@ include(LLWindow)
include(NDOF)
include(NVAPI)
include(OPENAL)
+include(OpenEXR)
include(OpenGL)
include(OpenSSL)
include(PNG)
@@ -72,7 +73,6 @@ if (NOT HAVOK_TPV)
endif()
endif (NOT HAVOK_TPV)
-
set(viewer_SOURCE_FILES
groupchatlistener.cpp
llaccountingcostmanager.cpp
@@ -1744,6 +1744,12 @@ if (WINDOWS)
media_plugin_cef
media_plugin_libvlc
media_plugin_example
+ ${SHARED_LIB_STAGING_DIR}/Iex-3_2.dll
+ ${SHARED_LIB_STAGING_DIR}/IlmThread-3_2.dll
+ ${SHARED_LIB_STAGING_DIR}/Imath-3_1.dll
+ ${SHARED_LIB_STAGING_DIR}/OpenEXR-3_2.dll
+ ${SHARED_LIB_STAGING_DIR}/OpenEXRCore-3_2.dll
+ ${SHARED_LIB_STAGING_DIR}/OpenEXRUtil-3_2.dll
)
if (ADDRESS_SIZE EQUAL 64)
@@ -1937,6 +1943,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
ll::bugsplat
ll::tracy
ll::icu4c
+ ll::openexr
)
if( TARGET ll::intel_memops )
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9d4f4cf911..8420f32db8 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9274,6 +9274,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>RenderDesaturateIrradiance</key>
+ <map>
+ <key>Comment</key>
+ <string>Desaturate irradiance to remove blue tint</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>RenderDebugAlphaMask</key>
<map>
<key>Comment</key>
@@ -9340,6 +9351,28 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>RenderHDRIExposure</key>
+ <map>
+ <key>Comment</key>
+ <string>Exposure adjustment of HDRI when previewing an HDRI. Units are EV. Sane values would be -10 to 10.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>RenderHDRIRotation</key>
+ <map>
+ <key>Comment</key>
+ <string>Rotation (in degrees) of environment when previewing an HDRI.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
<key>RenderMaxOpenGLVersion</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
index 3443785e1a..d89377326e 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
@@ -97,6 +97,7 @@ vec3 toneMapACES_Hill(vec3 color)
uniform float exposure;
uniform float gamma;
+uniform float aces_mix;
vec3 toneMap(vec3 color)
{
@@ -106,7 +107,7 @@ vec3 toneMap(vec3 color)
color *= exposure * exp_scale;
// mix ACES and Linear here as a compromise to avoid over-darkening legacy content
- color = mix(toneMapACES_Hill(color), color, 0.3);
+ color = mix(toneMapACES_Hill(color), color, aces_mix);
#endif
return color;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
index 9d9ba49d82..cc5280d929 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
@@ -27,6 +27,13 @@
in vec3 vary_HazeColor;
in float vary_LightNormPosDot;
+#ifdef HAS_HDRI
+in vec3 vary_position;
+uniform float sky_hdr_scale;
+uniform mat3 env_mat;
+uniform sampler2D environmentMap;
+#endif
+
uniform sampler2D rainbow_map;
uniform sampler2D halo_map;
@@ -37,6 +44,9 @@ uniform float ice_level;
out vec4 frag_data[4];
vec3 srgb_to_linear(vec3 c);
+vec3 linear_to_srgb(vec3 c);
+
+#define PI 3.14159265
/////////////////////////////////////////////////////////////////////////
// The fragment shader for the sky
@@ -71,6 +81,14 @@ vec3 halo22(float d)
void main()
{
+#ifdef HAS_HDRI
+ vec3 pos = normalize(vary_position);
+ pos = env_mat * pos;
+ vec2 texCoord = vec2(atan(pos.z, pos.x) + PI, acos(pos.y)) / vec2(2.0 * PI, PI);
+ vec3 color = textureLod(environmentMap, texCoord.xy, 0).rgb * sky_hdr_scale;
+ color = min(color, vec3(8192*8192*16));
+#else
+
// Potential Fill-rate optimization. Add cloud calculation
// back in and output alpha of 0 (so that alpha culling kills
// the fragment) if the sky wouldn't show up because the clouds
@@ -86,9 +104,12 @@ void main()
color.rgb *= 2.;
color.rgb = clamp(color.rgb, vec3(0), vec3(5));
+#endif
+
frag_data[0] = vec4(0);
frag_data[1] = vec4(0);
- frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_SKIP_ATMOS); //1.0 in norm.w masks off fog
+ frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_SKIP_ATMOS);
frag_data[3] = vec4(color.rgb, 1.0);
+
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
index 17ce2dee5b..bbe9a5a838 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
@@ -35,6 +35,10 @@ in vec3 position;
out vec3 vary_HazeColor;
out float vary_LightNormPosDot;
+#ifdef HAS_HDRI
+out vec3 vary_position;
+#endif
+
// Inputs
uniform vec3 camPosLocal;
@@ -72,6 +76,10 @@ void main()
// Get relative position
vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0);
+#ifdef HAS_HDRI
+ vary_position = rel_pos;
+#endif
+
// Adj position vector to clamp altitude
if (rel_pos.y > 0.)
{
diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
index 2f90249169..5cc7ea698a 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
@@ -189,6 +189,10 @@ void main()
vec3 v = -normalize(pos.xyz);
color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten);
}
+ else if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_HDRI))
+ {
+ color = texture(emissiveRect, tc).rgb;
+ }
else if (!GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_ATMOS))
{
//should only be true of WL sky, just port over base color value
diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp
index b14235f25c..a9cc138549 100644
--- a/indra/newview/lldrawpoolwlsky.cpp
+++ b/indra/newview/lldrawpoolwlsky.cpp
@@ -44,6 +44,7 @@
#include "llsky.h"
#include "llvowlsky.h"
#include "llsettingsvo.h"
+#include "llviewercontrol.h"
extern BOOL gCubeSnapshot;
@@ -127,6 +128,8 @@ void LLDrawPoolWLSky::renderDome(const LLVector3& camPosLocal, F32 camHeightLoca
gGL.popMatrix();
}
+extern LLPointer<LLImageGL> gEXRImage;
+
void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 camHeightLocal) const
{
if (!gSky.mVOSkyp)
@@ -138,9 +141,33 @@ void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 ca
if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY))
{
+ if (gEXRImage.notNull())
+ {
+ sky_shader = &gEnvironmentMapProgram;
+ sky_shader->bind();
+ S32 idx = sky_shader->enableTexture(LLShaderMgr::ENVIRONMENT_MAP);
+ if (idx > -1)
+ {
+ gGL.getTexUnit(idx)->bind(gEXRImage);
+ }
+
+ static LLCachedControl<F32> hdri_exposure(gSavedSettings, "RenderHDRIExposure", 0.0f);
+ static LLCachedControl<F32> hdri_rotation(gSavedSettings, "RenderHDRIRotation", 0.f);
+
+ LLMatrix3 rot;
+ rot.setRot(0.f, hdri_rotation*DEG_TO_RAD, 0.f);
+
+ sky_shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, powf(2.f, hdri_exposure));
+ sky_shader->uniformMatrix3fv(LLShaderMgr::DEFERRED_ENV_MAT, 1, GL_FALSE, (F32*) rot.mMatrix);
+ }
+ else
+ {
+ sky_shader->bind();
+ }
+
LLGLSPipelineDepthTestSkyBox sky(true, true);
- sky_shader->bind();
+
sky_shader->uniform1i(LLShaderMgr::CUBE_SNAPSHOT, gCubeSnapshot ? 1 : 0);
@@ -180,7 +207,7 @@ void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 ca
void LLDrawPoolWLSky::renderStarsDeferred(const LLVector3& camPosLocal) const
{
- if (!gSky.mVOSkyp)
+ if (!gSky.mVOSkyp || gEXRImage.notNull())
{
return;
}
@@ -251,6 +278,11 @@ void LLDrawPoolWLSky::renderStarsDeferred(const LLVector3& camPosLocal) const
void LLDrawPoolWLSky::renderSkyCloudsDeferred(const LLVector3& camPosLocal, F32 camHeightLocal, LLGLSLShader* cloudshader) const
{
+ if (gEXRImage.notNull())
+ {
+ return;
+ }
+
if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS) && gSky.mVOSkyp && gSky.mVOSkyp->getCloudNoiseTex())
{
LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky();
@@ -310,7 +342,7 @@ void LLDrawPoolWLSky::renderSkyCloudsDeferred(const LLVector3& camPosLocal, F32
void LLDrawPoolWLSky::renderHeavenlyBodies()
{
- if (!gSky.mVOSkyp) return;
+ if (!gSky.mVOSkyp || gEXRImage.notNull()) return;
LLGLSPipelineBlendSkyBox gls_skybox(true, true); // SL-14113 we need moon to write to depth to clip stars behind
diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp
index affea3f69c..0b535e15b0 100644
--- a/indra/newview/llenvironment.cpp
+++ b/indra/newview/llenvironment.cpp
@@ -1770,8 +1770,10 @@ void LLEnvironment::updateGLVariablesForSettings(LLShaderUniforms* uniforms, con
case LLSD::TypeArray:
{
LLVector4 vect4(value);
+ // always identify as a radiance pass if desaturating irradiance is disabled
+ static LLCachedControl<bool> desaturate_irradiance(gSavedSettings, "RenderDesaturateIrradiance", true);
- if (gCubeSnapshot && !gPipeline.mReflectionMapManager.isRadiancePass())
+ if (desaturate_irradiance && gCubeSnapshot && !gPipeline.mReflectionMapManager.isRadiancePass())
{ // maximize and remove tinting if this is an irradiance map render pass and the parameter feeds into the sky background color
auto max_vec = [](LLVector4 col)
{
@@ -2966,7 +2968,7 @@ void LLEnvironment::DayTransition::animate()
// pause probe updates and reset reflection maps on sky change
- gPipeline.mReflectionMapManager.pause();
+ gPipeline.mReflectionMapManager.pause(mTransitionTime);
gPipeline.mReflectionMapManager.reset();
mSky = mStartSky->buildClone();
@@ -3569,7 +3571,7 @@ namespace
mInjectedSky->setSource(target_sky);
// clear reflection probes and pause updates during sky change
- gPipeline.mReflectionMapManager.pause();
+ gPipeline.mReflectionMapManager.pause(transition);
gPipeline.mReflectionMapManager.reset();
mBlenderSky = std::make_shared<LLSettingsBlenderTimeDelta>(target_sky, start_sky, psky, transition);
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 4ad136e13a..b82172c506 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -61,6 +61,7 @@ LLFilePicker LLFilePicker::sInstance;
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
+#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"
#define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
#define SCRIPT_FILTER L"Script files (*.lsl)\0*.lsl\0"
#define DICTIONARY_FILTER L"Dictionary files (*.dic; *.xcu)\0*.dic;*.xcu\0"
@@ -228,6 +229,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
IMAGE_FILTER \
L"\0";
break;
+ case FFLOAD_HDRI:
+ mOFN.lpstrFilter = HDRI_FILTER \
+ L"\0";
+ break;
case FFLOAD_SCRIPT:
mOFN.lpstrFilter = SCRIPT_FILTER \
L"\0";
@@ -663,6 +668,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
allowedv->push_back("gltf");
allowedv->push_back("glb");
break;
+ case FFLOAD_HDRI:
+ allowedv->push_back("exr");
case FFLOAD_COLLADA:
allowedv->push_back("dae");
break;
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index 38daff9937..891c0c0482 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -89,6 +89,7 @@ public:
FFLOAD_EXE = 14, // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin
FFLOAD_MATERIAL = 15,
FFLOAD_MATERIAL_TEXTURE = 16,
+ FFLOAD_HDRI = 17,
};
enum ESaveFilter
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index ce389a5cad..f9c5421866 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -38,6 +38,126 @@
#include "llviewercontrol.h"
#include "llenvironment.h"
#include "llstartup.h"
+#include "llviewermenufile.h"
+#include "llnotificationsutil.h"
+
+
+// load an OpenEXR image from a file
+#define IMATH_HALF_NO_LOOKUP_TABLE 1
+#include <ImfInputFile.h>
+#include <ImfArray.h>
+#include <ImfHeader.h>
+#include <ImfFrameBuffer.h>
+#include <iostream>
+
+LLPointer<LLImageGL> gEXRImage;
+
+void load_exr(const std::string& filename)
+{
+ // reset reflection maps when previewing a new HDRI
+ gPipeline.mReflectionMapManager.reset();
+ gPipeline.mReflectionMapManager.initReflectionMaps();
+
+ try {
+ Imf::InputFile file(filename.c_str());
+ Imath::Box2i dw = file.header().dataWindow();
+ int width = dw.max.x - dw.min.x + 1;
+ int height = dw.max.y - dw.min.y + 1;
+
+ Imf::Array2D<Imath::half> rPixels;
+ Imf::Array2D<Imath::half> gPixels;
+ Imf::Array2D<Imath::half> bPixels;
+
+ rPixels.resizeErase(height, width);
+ gPixels.resizeErase(height, width);
+ bPixels.resizeErase(height, width);
+
+ Imf::FrameBuffer frameBuffer;
+
+ frameBuffer.insert("R", // name
+ Imf::Slice(Imf::HALF, // type
+ (char*)(&rPixels[0][0] - // base
+ dw.min.x -
+ dw.min.y * width),
+ sizeof(rPixels[0][0]) * 1, // xStride
+ sizeof(rPixels[0][0]) * width, // yStride
+ 1, 1, // x/y sampling
+ 0.0)); // fillValue
+
+ frameBuffer.insert("G", // name
+ Imf::Slice(Imf::HALF, // type
+ (char*)(&gPixels[0][0] - // base
+ dw.min.x -
+ dw.min.y * width),
+ sizeof(gPixels[0][0]) * 1, // xStride
+ sizeof(gPixels[0][0]) * width, // yStride
+ 1, 1, // x/y sampling
+ 0.0)); // fillValue
+
+ frameBuffer.insert("B", // name
+ Imf::Slice(Imf::HALF, // type
+ (char*)(&bPixels[0][0] - // base
+ dw.min.x -
+ dw.min.y * width),
+ sizeof(bPixels[0][0]) * 1, // xStride
+ sizeof(bPixels[0][0]) * width, // yStride
+ 1, 1, // x/y sampling
+ FLT_MAX)); // fillValue
+
+ file.setFrameBuffer(frameBuffer);
+ file.readPixels(dw.min.y, dw.max.y);
+
+ U32 texName = 0;
+ LLImageGL::generateTextures(1, &texName);
+
+ gEXRImage = new LLImageGL(texName, 4, GL_TEXTURE_2D, GL_RGB16F, GL_RGB16F, GL_FLOAT, LLTexUnit::TAM_CLAMP);
+ gEXRImage->setHasMipMaps(TRUE);
+ gEXRImage->setUseMipMaps(TRUE);
+ gEXRImage->setFilteringOption(LLTexUnit::TFO_TRILINEAR);
+
+ gGL.getTexUnit(0)->bind(gEXRImage);
+
+ std::vector<F32> data(width * height * 3);
+ for (int i = 0; i < width * height; ++i)
+ {
+ data[i * 3 + 0] = rPixels[i / width][i % width];
+ data[i * 3 + 1] = gPixels[i / width][i % width];
+ data[i * 3 + 2] = bPixels[i / width][i % width];
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data.data());
+
+ glGenerateMipmap(GL_TEXTURE_2D);
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ }
+ catch (const std::exception& e) {
+ LLSD notif_args;
+ notif_args["WHAT"] = filename;
+ notif_args["REASON"] = e.what();
+ LLNotificationsUtil::add("CannotLoad", notif_args);
+ return;
+ }
+}
+
+void hdri_preview()
+{
+ LLFilePickerReplyThread::startPicker(
+ [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter)
+ {
+ if (LLAppViewer::instance()->quitRequested())
+ {
+ return;
+ }
+ if (filenames.size() > 0)
+ {
+ load_exr(filenames[0]);
+ }
+ },
+ LLFilePicker::FFLOAD_HDRI,
+ true);
+}
extern BOOL gCubeSnapshot;
extern BOOL gTeleportDisplay;
@@ -133,6 +253,11 @@ void LLReflectionMapManager::update()
return;
}
+ if (mPaused && gFrameTimeSeconds > mResumeTime)
+ {
+ resume();
+ }
+
initReflectionMaps();
if (!mRenderTarget.isComplete())
@@ -831,9 +956,10 @@ void LLReflectionMapManager::reset()
mReset = true;
}
-void LLReflectionMapManager::pause()
+void LLReflectionMapManager::pause(F32 duration)
{
mPaused = true;
+ mResumeTime = gFrameTimeSeconds + duration;
}
void LLReflectionMapManager::resume()
@@ -1283,6 +1409,8 @@ void LLReflectionMapManager::initReflectionMaps()
if (mTexture.isNull() || mReflectionProbeCount != count || mReset)
{
+ gEXRImage = nullptr;
+
mReset = false;
mReflectionProbeCount = count;
mProbeResolution = nhpo2(llclamp(gSavedSettings.getU32("RenderReflectionProbeResolution"), (U32)64, (U32)512));
@@ -1340,7 +1468,6 @@ void LLReflectionMapManager::initReflectionMaps()
mDefaultProbe->mComplete = default_complete;
touch_default_probe(mDefaultProbe);
-
}
if (mVertexBuffer.isNull())
diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h
index 0fee99eefc..5c0651bc24 100644
--- a/indra/newview/llreflectionmapmanager.h
+++ b/indra/newview/llreflectionmapmanager.h
@@ -87,7 +87,8 @@ public:
void reset();
// pause all updates other than the default probe
- void pause();
+ // duration - number of seconds to pause (default 10)
+ void pause(F32 duration = 10.f);
// unpause (see pause)
void resume();
@@ -208,5 +209,6 @@ private:
// if true, only update the default probe
bool mPaused = false;
+ F32 mResumeTime = 0.f;
};
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index d5e4de03a9..c50ae2e153 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -7890,6 +7890,19 @@ class LLAdvancedClickRenderBenchmark: public view_listener_t
}
};
+void hdri_preview();
+
+class LLAdvancedClickHDRIPreview: public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
+ LLFloaterReg::showInstance("env_adjust_snapshot");
+ hdri_preview();
+ return true;
+ }
+};
+
// these are used in the gl menus to set control values that require shader recompilation
class LLToggleShaderControl : public view_listener_t
{
@@ -9529,6 +9542,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickRenderShadowOption(), "Advanced.ClickRenderShadowOption");
view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile");
view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark");
+ view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 354cc79036..dc20f035c9 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -201,6 +201,7 @@ LLGLSLShader gLuminanceProgram;
LLGLSLShader gFXAAProgram;
LLGLSLShader gDeferredPostNoDoFProgram;
LLGLSLShader gDeferredWLSkyProgram;
+LLGLSLShader gEnvironmentMapProgram;
LLGLSLShader gDeferredWLCloudProgram;
LLGLSLShader gDeferredWLSunProgram;
LLGLSLShader gDeferredWLMoonProgram;
@@ -315,6 +316,7 @@ void LLViewerShaderMgr::finalizeShaderList()
mShaderList.push_back(&gDeferredEmissiveProgram);
mShaderList.push_back(&gDeferredAvatarEyesProgram);
mShaderList.push_back(&gDeferredAvatarAlphaProgram);
+ mShaderList.push_back(&gEnvironmentMapProgram);
mShaderList.push_back(&gDeferredWLSkyProgram);
mShaderList.push_back(&gDeferredWLCloudProgram);
mShaderList.push_back(&gDeferredWLMoonProgram);
@@ -987,6 +989,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gNoPostGammaCorrectProgram.unload();
gLegacyPostGammaCorrectProgram.unload();
gFXAAProgram.unload();
+ gEnvironmentMapProgram.unload();
gDeferredWLSkyProgram.unload();
gDeferredWLCloudProgram.unload();
gDeferredWLSunProgram.unload();
@@ -2268,6 +2271,26 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
llassert(success);
}
+ if (success)
+ {
+ gEnvironmentMapProgram.mName = "Environment Map Program";
+ gEnvironmentMapProgram.mShaderFiles.clear();
+ gEnvironmentMapProgram.mFeatures.calculatesAtmospherics = true;
+ gEnvironmentMapProgram.mFeatures.hasAtmospherics = true;
+ gEnvironmentMapProgram.mFeatures.hasGamma = true;
+ gEnvironmentMapProgram.mFeatures.hasSrgb = true;
+
+ gEnvironmentMapProgram.clearPermutations();
+ gEnvironmentMapProgram.addPermutation("HAS_HDRI", "1");
+ gEnvironmentMapProgram.mShaderFiles.push_back(make_pair("deferred/skyV.glsl", GL_VERTEX_SHADER));
+ gEnvironmentMapProgram.mShaderFiles.push_back(make_pair("deferred/skyF.glsl", GL_FRAGMENT_SHADER));
+ gEnvironmentMapProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+ gEnvironmentMapProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+
+ success = gEnvironmentMapProgram.createShader(NULL, NULL);
+ llassert(success);
+ }
+
if (success)
{
gDeferredWLSkyProgram.mName = "Deferred Windlight Sky Shader";
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 2502be6bb1..c51f583ebc 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -266,6 +266,7 @@ extern LLGLSLShader gHUDFullbrightAlphaMaskAlphaProgram;
extern LLGLSLShader gDeferredEmissiveProgram;
extern LLGLSLShader gDeferredAvatarEyesProgram;
extern LLGLSLShader gDeferredAvatarAlphaProgram;
+extern LLGLSLShader gEnvironmentMapProgram;
extern LLGLSLShader gDeferredWLSkyProgram;
extern LLGLSLShader gDeferredWLCloudProgram;
extern LLGLSLShader gDeferredWLSunProgram;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index fee00eb6f4..35e45c6cd9 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -5233,9 +5233,6 @@ U32 LLVOAvatar::renderRigid()
return 0;
}
- bool should_alpha_mask = shouldAlphaMask();
- LLGLState test(GL_ALPHA_TEST, should_alpha_mask);
-
if (isTextureVisible(TEX_EYES_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar())
{
LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT);
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 94ec5c0817..7809129743 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -6869,6 +6869,8 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool
}
}
+extern LLPointer<LLImageGL> gEXRImage;
+
void LLPipeline::gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst) {
dst->bindTarget();
// gamma correct lighting
@@ -6905,8 +6907,10 @@ void LLPipeline::gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst) {
F32 e = llclamp(exposure(), 0.5f, 4.f);
static LLStaticHashedString s_exposure("exposure");
+ static LLStaticHashedString aces_mix("aces_mix");
shader.uniform1f(s_exposure, e);
+ shader.uniform1f(aces_mix, gEXRImage.notNull() ? 0.f : 0.3f);
mScreenTriangleVB->setBuffer();
mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 4719b091ab..591b5537c7 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2824,6 +2824,12 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClickRenderBenchmark" />
</menu_item_call>
+ <menu_item_call
+ label="HDRI Preview"
+ name="HDRI Preview">
+ <menu_item_call.on_click
+ function="Advanced.ClickHDRIPreview" />
+ </menu_item_call>
</menu>
<menu
create_jump_keys="true"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index df9f53686e..1a6cadf43e 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -9260,6 +9260,15 @@ Unable to upload texture.
</notification>
<notification
+ icon="alertmodal.tga"
+ name="CannotLoad"
+ type="alertmodal">
+ Unable to load [WHAT].
+ [REASON]
+ <tag>fail</tag>
+ </notification>
+
+ <notification
icon="alertmodal.tga"
name="CannotUploadMaterial"
type="alertmodal">
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index c7f32d0da9..70121ecc64 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -587,6 +587,14 @@ class Windows_x86_64_Manifest(ViewerManifest):
self.path("libcrypto-1_1-x64.dll")
self.path("libssl-1_1-x64.dll")
+ # OpenEXR
+ self.path("Iex-3_2.dll")
+ self.path("IlmThread-3_2.dll")
+ self.path("Imath-3_1.dll")
+ self.path("OpenEXR-3_2.dll")
+ self.path("OpenEXRCore-3_2.dll")
+ self.path("OpenEXRUtil-3_2.dll")
+
# HTTP/2
self.path("nghttp2.dll")
@@ -934,6 +942,12 @@ class Darwin_x86_64_Manifest(ViewerManifest):
with self.prefix(src=relpkgdir, dst=""):
self.path("libndofdev.dylib")
self.path("libhunspell-*.dylib")
+ self.path("libIex-3_2.dylib")
+ self.path("libIlmThread-3_2.dylib")
+ self.path("libImath-3_1.dylib")
+ self.path("libOpenEXR-3_2.dylib")
+ self.path("libOpenEXRCore-3_2.dylib")
+ self.path("libOpenEXRUtil-3_2.dylib")
with self.prefix(src_dst="cursors_mac"):
self.path("*.tif")