diff options
Diffstat (limited to 'indra/newview')
25 files changed, 980 insertions, 33 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d413c12de9..e0dc148266 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -516,6 +516,7 @@ set(viewer_SOURCE_FILES llrecentpeople.cpp llreflectionmap.cpp llreflectionmapmanager.cpp + llheroprobemanager.cpp llregioninfomodel.cpp llregionposition.cpp llremoteparcelrequest.cpp @@ -1151,6 +1152,7 @@ set(viewer_HEADER_FILES llrecentpeople.h llreflectionmap.h llreflectionmapmanager.h + llheroprobemanager.h llregioninfomodel.h llregionposition.h llremoteparcelrequest.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 21d8722f56..750125a1a8 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9197,6 +9197,17 @@ <real>0.00</real> </array> </map> + <key>RenderMirrors</key> + <map> + <key>Comment</key> + <string>Renders realtime mirrors.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>RenderScreenSpaceReflections</key> <map> <key>Comment</key> @@ -10401,6 +10412,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>RenderHeroProbeResolution</key> + <map> + <key>Comment</key> + <string>Resolution to render hero probes used for mirrors, water, etc.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>512</integer> + </map> <key>RenderReflectionProbeVolumes</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl index 9ecdf0bf77..cb6f34713c 100644 --- a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl @@ -38,6 +38,7 @@ in vec3 vary_dir; uniform float mipLevel; uniform int u_width; uniform float max_probe_lod; +uniform float probe_strength; // ============================================================================================================= @@ -163,5 +164,6 @@ void main() { vec3 N = normalize(vary_dir); frag_color = max(prefilterEnvMap(N), vec4(0)); + frag_color.a *= probe_strength; } // ============================================================================================================= diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl index 41821def8e..549ac50280 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl @@ -31,6 +31,7 @@ float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, uniform samplerCubeArray reflectionProbes; uniform samplerCubeArray irradianceProbes; + uniform sampler2D sceneMap; uniform int cube_snapshot; uniform float max_probe_lod; diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 4ef003e0cb..2483355205 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -32,6 +32,14 @@ uniform sampler2D specularRect; uniform sampler2D normalMap; uniform sampler2D emissiveRect; // PBR linear packed Occlusion, Roughness, Metal. See: pbropaqueF.glsl +uniform samplerCubeArray heroProbes; + +layout (std140) uniform HeroProbeData +{ + vec4 heroPosition[1]; + int heroProbeCount; +}; + const float M_PI = 3.14159265; #if defined(HAS_SUN_SHADOW) || defined(HAS_SSAO) @@ -109,8 +117,8 @@ vec3 pbrBaseLight(vec3 diffuseColor, vec3 additive, vec3 atten); -vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, - float perceptualRoughness, +vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, + float perceptualRoughness, float metallic, vec3 n, // normal vec3 v, // surface point to camera @@ -185,7 +193,7 @@ void main() if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR)) { - vec3 orm = texture(specularRect, tc).rgb; + vec3 orm = texture(specularRect, tc).rgb; float perceptualRoughness = orm.g; float metallic = orm.b; float ao = orm.r; @@ -205,10 +213,14 @@ 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); + vec3 refnormpersp = reflect(pos.xyz, norm.xyz); + color = textureLod(heroProbes, vec4(env_mat * refnormpersp, 0), (1.0 - gloss) * 11).xyz * specularColor; + if (do_atmospherics) { color = atmosFragLightingLinear(color, additive, atten); } + } else if (!GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_ATMOS)) { @@ -268,6 +280,9 @@ void main() // add radiance map applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz); + + color = textureLod(heroProbes, vec4(env_mat * refnormpersp, 0), (1.0 - spec.a) * 11).xyz * spec.rgb; + } color.rgb = mix(color.rgb, baseColor.rgb, baseColor.a); @@ -283,14 +298,11 @@ void main() color = atmosFragLightingLinear(color, additive, atten); } } - - #ifdef WATER_FOG vec4 fogged = applyWaterFogViewLinear(pos.xyz, vec4(color, bloom)); color = fogged.rgb; #endif - frag_color.rgb = max(color.rgb, vec3(0)); //output linear since local lights will be added to this shader's results frag_color.a = 0.0; } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ce5430104e..1b2858cdc2 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1518,6 +1518,7 @@ bool LLAppViewer::doFrame() LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df Snapshot"); pingMainloopTimeout("Main:Snapshot"); gPipeline.mReflectionMapManager.update(); + gPipeline.mHeroProbeManager.update(); LLFloaterSnapshot::update(); // take snapshots LLFloaterSimpleOutfitSnapshot::update(); gGLActive = FALSE; diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 970e8c8b2a..fc182fc2e2 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -286,6 +286,7 @@ public: ANIMATED_CHILD = 0x01000000, ACTIVE_CHILD = 0x02000000, FOR_UNLOAD = 0x04000000, //should be unload from memory + MIRROR = 0x08000000, // Used as a mirror, needs a hero probe position to be calculated. } EDrawableFlags; public: diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp new file mode 100644 index 0000000000..7c616593fc --- /dev/null +++ b/indra/newview/llheroprobemanager.cpp @@ -0,0 +1,574 @@ +/** + * @file LLHeroProbeManager.cpp + * @brief LLHeroProbeManager class implementation + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llheroprobemanager.h" +#include "llreflectionmapmanager.h" +#include "llviewercamera.h" +#include "llspatialpartition.h" +#include "llviewerregion.h" +#include "pipeline.h" +#include "llviewershadermgr.h" +#include "llviewercontrol.h" +#include "llenvironment.h" +#include "llstartup.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llviewerwindow.h" +#include "llviewerjoystick.h" +#include "llviewermediafocus.h" + +extern BOOL gCubeSnapshot; +extern BOOL gTeleportDisplay; + +// get the next highest power of two of v (or v if v is already a power of two) +//defined in llvertexbuffer.cpp +extern U32 nhpo2(U32 v); + +static void touch_default_probe(LLReflectionMap* probe) +{ + if (LLViewerCamera::getInstance()) + { + LLVector3 origin = LLViewerCamera::getInstance()->getOrigin(); + origin.mV[2] += 64.f; + + probe->mOrigin.load3(origin.mV); + } +} + +LLHeroProbeManager::LLHeroProbeManager() +{ +} + +// helper class to seed octree with probes +void LLHeroProbeManager::update() +{ + if (!LLPipeline::sReflectionProbesEnabled || gTeleportDisplay || LLStartUp::getStartupState() < STATE_PRECACHE) + { + return; + } + + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + llassert(!gCubeSnapshot); // assert a snapshot is not in progress + if (LLAppViewer::instance()->logoutRequestSent()) + { + return; + } + + initReflectionMaps(); + + if (!mRenderTarget.isComplete()) + { + U32 color_fmt = GL_RGB16F; + U32 targetRes = mProbeResolution * 4; // super sample + mRenderTarget.allocate(targetRes, targetRes, color_fmt, true); + } + + if (mMipChain.empty()) + { + U32 res = mProbeResolution; + U32 count = log2((F32)res) + 0.5f; + + mMipChain.resize(count); + for (int i = 0; i < count; ++i) + { + mMipChain[i].allocate(res, res, GL_RGB16F); + res /= 2; + } + } + + llassert(mProbes[0] == mDefaultProbe); + + LLVector4a probe_pos; + LLVector3 camera_pos = LLViewerCamera::instance().mOrigin; + if (mHeroVOList.size() > 0) + { + { + if (mNearestHero != nullptr && mNearestHero->mDrawable.notNull()) + { + LLVector3 hero_pos = mNearestHero->mDrawable->mXform.getWorldPosition(); + + LLVector4a hit_pos; + LLVector3 focus_point; + LLQuaternion camera_rot; + F32 angleInRadians = 180 * DEG_TO_RAD; + + LLMatrix4 rotationMatrix; + + LLVector3 translatedPoint; + LLVector3 rotatedTranslatedPoint; + LLVector3 rotatedPoint; + + switch (mNearestHero->mirrorPlacementMode()) { + case 0: + + hero_pos.mV[1] = camera_pos.mV[1]; + + rotationMatrix.rotate(angleInRadians, LLVector4(1, 0, 0, 0)); + + translatedPoint = camera_pos - hero_pos; + rotatedTranslatedPoint = translatedPoint * rotationMatrix; + rotatedPoint = rotatedTranslatedPoint + hero_pos; + + probe_pos.load3(rotatedPoint.mV); + break; + case 1: + + hero_pos.mV[2] = camera_pos.mV[2]; + + rotationMatrix.rotate(angleInRadians, LLVector4(0, 1, 0, 0)); + + translatedPoint = camera_pos - hero_pos; + rotatedTranslatedPoint = translatedPoint * rotationMatrix; + rotatedPoint = rotatedTranslatedPoint + hero_pos; + + probe_pos.load3(rotatedPoint.mV); + break; + case 2: + + hero_pos.mV[0] = camera_pos.mV[0]; + + rotationMatrix.rotate(angleInRadians, LLVector4(0, 0, 1, 0)); + + translatedPoint = camera_pos - hero_pos; + rotatedTranslatedPoint = translatedPoint * rotationMatrix; + rotatedPoint = rotatedTranslatedPoint + hero_pos; + + probe_pos.load3(rotatedPoint.mV); + break; + } + } + + mHeroProbeStrength = 1; + } + } + else + { + probe_pos.load3(camera_pos.mV); + } + + + static LLCachedControl<S32> sDetail(gSavedSettings, "RenderHeroReflectionProbeDetail", -1); + static LLCachedControl<S32> sLevel(gSavedSettings, "RenderHeroReflectionProbeLevel", 3); + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime"); + // Probe 0 is always our mirror probe. + mProbes[0]->mOrigin = probe_pos; + + bool radiance_pass = gPipeline.mReflectionMapManager.isRadiancePass(); + + gPipeline.mReflectionMapManager.mRadiancePass = true; + + for (U32 j = 0; j < mProbes.size(); j++) + { + for (U32 i = 0; i < 6; ++i) + { + updateProbeFace(mProbes[j], i); + } + } + + gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass; + } +} + +// Do the reflection map update render passes. +// For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated +// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate +// a simple mip chain (not convolution filter). +// At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe +// The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain. +// At the end of these passes, a radiance map is generated for this probe and placed into the radiance cube map array at the index for this probe. +// In effect this simulates single-bounce lighting. +void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face) +{ + // hacky hot-swap of camera specific render targets + gPipeline.mRT = &gPipeline.mAuxillaryRT; + + probe->update(mRenderTarget.getWidth(), face, true); + + gPipeline.mRT = &gPipeline.mMainRT; + + S32 sourceIdx = mReflectionProbeCount; + + + // Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes. + sourceIdx += 1; + + gGL.setColorMask(true, true); + LLGLDepthTest depth(GL_FALSE, GL_FALSE); + LLGLDisable cull(GL_CULL_FACE); + LLGLDisable blend(GL_BLEND); + + // downsample to placeholder map + { + gGL.matrixMode(gGL.MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.matrixMode(gGL.MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.flush(); + U32 res = mProbeResolution * 2; + + static LLStaticHashedString resScale("resScale"); + static LLStaticHashedString direction("direction"); + static LLStaticHashedString znear("znear"); + static LLStaticHashedString zfar("zfar"); + + LLRenderTarget* screen_rt = &gPipeline.mAuxillaryRT.screen; + + // perform a gaussian blur on the super sampled render before downsampling + { + gGaussianProgram.bind(); + gGaussianProgram.uniform1f(resScale, 1.f / (mProbeResolution * 2)); + S32 diffuseChannel = gGaussianProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE); + + // horizontal + gGaussianProgram.uniform2f(direction, 1.f, 0.f); + gGL.getTexUnit(diffuseChannel)->bind(screen_rt); + mRenderTarget.bindTarget(); + gPipeline.mScreenTriangleVB->setBuffer(); + gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + mRenderTarget.flush(); + + // vertical + gGaussianProgram.uniform2f(direction, 0.f, 1.f); + gGL.getTexUnit(diffuseChannel)->bind(&mRenderTarget); + screen_rt->bindTarget(); + gPipeline.mScreenTriangleVB->setBuffer(); + gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + screen_rt->flush(); + } + + + S32 mips = log2((F32)mProbeResolution) + 0.5f; + + gReflectionMipProgram.bind(); + S32 diffuseChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE); + + for (int i = 0; i < mMipChain.size(); ++i) + { + LL_PROFILE_GPU_ZONE("probe mip"); + mMipChain[i].bindTarget(); + if (i == 0) + { + gGL.getTexUnit(diffuseChannel)->bind(screen_rt); + } + else + { + gGL.getTexUnit(diffuseChannel)->bind(&(mMipChain[i - 1])); + } + + + gReflectionMipProgram.uniform1f(resScale, 1.f/(mProbeResolution*2)); + + gPipeline.mScreenTriangleVB->setBuffer(); + gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + + res /= 2; + + S32 mip = i - (mMipChain.size() - mips); + + if (mip >= 0) + { + LL_PROFILE_GPU_ZONE("probe mip copy"); + mTexture->bind(0); + //glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, probe->mCubeIndex * 6 + face, 0, 0, res, res); + glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, sourceIdx * 6 + face, 0, 0, res, res); + //if (i == 0) + //{ + //glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, probe->mCubeIndex * 6 + face, 0, 0, res, res); + //} + mTexture->unbind(); + } + mMipChain[i].flush(); + } + + gGL.popMatrix(); + gGL.matrixMode(gGL.MM_MODELVIEW); + gGL.popMatrix(); + + gGL.getTexUnit(diffuseChannel)->unbind(LLTexUnit::TT_TEXTURE); + gReflectionMipProgram.unbind(); + } + + if (face == 5) + { + mMipChain[0].bindTarget(); + static LLStaticHashedString sSourceIdx("sourceIdx"); + + { + //generate radiance map (even if this is not the irradiance map, we need the mip chain for the irradiance map) + gRadianceGenProgram.bind(); + mVertexBuffer->setBuffer(); + + S32 channel = gRadianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY); + mTexture->bind(channel); + gRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx); + gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD); + gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, mHeroProbeStrength); + + U32 res = mMipChain[0].getWidth(); + + for (int i = 0; i < mMipChain.size(); ++i) + { + LL_PROFILE_GPU_ZONE("probe radiance gen"); + static LLStaticHashedString sMipLevel("mipLevel"); + static LLStaticHashedString sRoughness("roughness"); + static LLStaticHashedString sWidth("u_width"); + + gRadianceGenProgram.uniform1f(sRoughness, (F32)i / (F32)(mMipChain.size() - 1)); + gRadianceGenProgram.uniform1f(sMipLevel, i); + gRadianceGenProgram.uniform1i(sWidth, mProbeResolution); + + for (int cf = 0; cf < 6; ++cf) + { // for each cube face + LLCoordFrame frame; + frame.lookAt(LLVector3(0, 0, 0), LLCubeMapArray::sClipToCubeLookVecs[cf], LLCubeMapArray::sClipToCubeUpVecs[cf]); + + F32 mat[16]; + frame.getOpenGLRotation(mat); + gGL.loadMatrix(mat); + + mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4); + + glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i, 0, 0, probe->mCubeIndex * 6 + cf, 0, 0, res, res); + } + + if (i != mMipChain.size() - 1) + { + res /= 2; + glViewport(0, 0, res, res); + } + } + + gRadianceGenProgram.unbind(); + } + + mMipChain[0].flush(); + } +} + +void LLHeroProbeManager::updateUniforms() +{ + if (!LLPipeline::sReflectionProbesEnabled) + { + return; + } + + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + + struct HeroProbeData + { + LLVector4 heroPosition[1]; + GLint heroProbeCount = 1; + }; + + HeroProbeData hpd; + + LLMatrix4a modelview; + modelview.loadu(gGLModelView); + LLVector4a oa; // scratch space for transformed origin + oa.set(0, 0, 0, 0); + hpd.heroProbeCount = 1; + modelview.affineTransform(mProbes[0]->mOrigin, oa); + hpd.heroPosition[0].set(oa.getF32ptr()); + + //copy rpd into uniform buffer object + if (mUBO == 0) + { + glGenBuffers(1, &mUBO); + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer"); + glBindBuffer(GL_UNIFORM_BUFFER, mUBO); + glBufferData(GL_UNIFORM_BUFFER, sizeof(HeroProbeData), &hpd, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + +#if 0 + if (!gCubeSnapshot) + { + for (auto& probe : mProbes) + { + LLViewerObject* vobj = probe->mViewerObject; + if (vobj) + { + F32 time = (F32)gFrameTimeSeconds - probe->mLastUpdateTime; + vobj->setDebugText(llformat("%d/%d/%d/%.1f - %.1f/%.1f", probe->mCubeIndex, probe->mProbeIndex, (U32) probe->mNeighbors.size(), probe->mMinDepth, probe->mMaxDepth, time), time > 1.f ? LLColor4::white : LLColor4::green); + } + } + } +#endif +} + +void LLHeroProbeManager::setUniforms() +{ + if (!LLPipeline::sReflectionProbesEnabled) + { + return; + } + + if (mUBO == 0) + { + updateUniforms(); + } + glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO); +} + +void LLHeroProbeManager::renderDebug() +{ + gDebugProgram.bind(); + + for (auto& probe : mProbes) + { + renderReflectionProbe(probe); + } + + gDebugProgram.unbind(); +} + +void LLHeroProbeManager::initReflectionMaps() +{ + U32 count = LL_MAX_REFLECTION_PROBE_COUNT; + + if (mTexture.isNull() || mReflectionProbeCount != count || mReset) + { + mReset = false; + mReflectionProbeCount = count; + mProbeResolution = nhpo2(1024); + mMaxProbeLOD = log2f(mProbeResolution) - 1.f; // number of mips - 1 + + mTexture = new LLCubeMapArray(); + + // store mReflectionProbeCount+2 cube maps, final two cube maps are used for render target and radiance map generation source) + mTexture->allocate(mProbeResolution, 3, mReflectionProbeCount + 2); + + mIrradianceMaps = new LLCubeMapArray(); + mIrradianceMaps->allocate(LL_IRRADIANCE_MAP_RESOLUTION, 3, mReflectionProbeCount, FALSE); + + if (mDefaultProbe.isNull()) + { + llassert(mProbes.empty()); // default probe MUST be the first probe created + mDefaultProbe = new LLReflectionMap(); + mProbes.push_back(mDefaultProbe); + } + + llassert(mProbes[0] == mDefaultProbe); + + // For hero probes, we treat this as the main mirror probe. + + mDefaultProbe->mCubeIndex = 0; + mDefaultProbe->mCubeArray = mTexture; + mDefaultProbe->mDistance = 12.f; + mDefaultProbe->mRadius = 4096.f; + mDefaultProbe->mProbeIndex = 0; + touch_default_probe(mDefaultProbe); + + mProbes.push_back(mDefaultProbe); + } + + if (mVertexBuffer.isNull()) + { + U32 mask = LLVertexBuffer::MAP_VERTEX; + LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask); + buff->allocateBuffer(4, 0); + + LLStrider<LLVector3> v; + + buff->getVertexStrider(v); + + v[0] = LLVector3(-1, -1, -1); + v[1] = LLVector3(1, -1, -1); + v[2] = LLVector3(-1, 1, -1); + v[3] = LLVector3(1, 1, -1); + + buff->unmapBuffer(); + + mVertexBuffer = buff; + } +} + +void LLHeroProbeManager::cleanup() +{ + mVertexBuffer = nullptr; + mRenderTarget.release(); + mHeroRenderTarget.release(); + + mMipChain.clear(); + + mTexture = nullptr; + + mProbes.clear(); + + mReflectionMaps.clear(); + + mDefaultProbe = nullptr; + mUpdatingProbe = nullptr; + + glDeleteBuffers(1, &mUBO); + mUBO = 0; + + mHeroVOList.clear(); + mNearestHero = nullptr; +} + +void LLHeroProbeManager::doOcclusion() +{ + LLVector4a eye; + eye.load3(LLViewerCamera::instance().getOrigin().mV); + + for (auto& probe : mProbes) + { + if (probe != nullptr && probe != mDefaultProbe) + { + probe->doOcclusion(eye); + } + } +} + +void LLHeroProbeManager::registerHeroDrawable(LLVOVolume* drawablep) +{ + mNearestHero = drawablep; + if (mHeroVOList.find(drawablep) == mHeroVOList.end()) + { + mHeroVOList.insert(drawablep); + LL_INFOS() << "Mirror drawable registered." << LL_ENDL; + } +} + +void LLHeroProbeManager::unregisterHeroDrawable(LLVOVolume* drawablep) +{ + if (mHeroVOList.find(drawablep) != mHeroVOList.end()) + { + mHeroVOList.erase(drawablep); + } +} diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h new file mode 100644 index 0000000000..b817b2396f --- /dev/null +++ b/indra/newview/llheroprobemanager.h @@ -0,0 +1,134 @@ +/** + * @file llheroprobemanager.h + * @brief LLHeroProbeManager class declaration + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "llreflectionmap.h" +#include "llrendertarget.h" +#include "llcubemaparray.h" +#include "llcubemap.h" +#include "lldrawable.h" + +class LLSpatialGroup; +class LLViewerObject; + +// number of reflection probes to keep in vram +#define LL_MAX_HERO_PROBE_COUNT 2 + +class alignas(16) LLHeroProbeManager +{ + LL_ALIGN_NEW +public: + enum class DetailLevel + { + STATIC_ONLY = 0, + STATIC_AND_DYNAMIC, + REALTIME = 2 + }; + + // allocate an environment map of the given resolution + LLHeroProbeManager(); + + // release any GL state + void cleanup(); + + // maintain reflection probes + void update(); + + // debug display, called from llspatialpartition if reflection + // probe debug display is active + void renderDebug(); + + // call once at startup to allocate cubemap arrays + void initReflectionMaps(); + + // perform occlusion culling on all active reflection probes + void doOcclusion(); + + void registerHeroDrawable(LLVOVolume* drawablep); + void unregisterHeroDrawable(LLVOVolume* drawablep); + +private: + friend class LLPipeline; + + // update UBO used for rendering (call only once per render pipe flush) + void updateUniforms(); + + // bind UBO used for rendering + void setUniforms(); + + // render target for cube snapshots + // used to generate mipmaps without doing a copy-to-texture + LLRenderTarget mRenderTarget; + + LLRenderTarget mHeroRenderTarget; + + std::vector<LLRenderTarget> mMipChain; + + // storage for reflection probe radiance maps (plus two scratch space cubemaps) + LLPointer<LLCubeMapArray> mTexture; + + // vertex buffer for pushing verts to filter shaders + LLPointer<LLVertexBuffer> mVertexBuffer; + + // storage for reflection probe irradiance maps + LLPointer<LLCubeMapArray> mIrradianceMaps; + + // update the specified face of the specified probe + void updateProbeFace(LLReflectionMap* probe, U32 face); + + // list of active reflection maps + std::vector<LLPointer<LLReflectionMap> > mProbes; + + // handle to UBO + U32 mUBO = 0; + + // list of maps being used for rendering + std::vector<LLReflectionMap*> mReflectionMaps; + + LLReflectionMap* mUpdatingProbe = nullptr; + + LLPointer<LLReflectionMap> mDefaultProbe; // default reflection probe to fall back to for pixels with no probe influences (should always be at cube index 0) + + // number of reflection probes to use for rendering + U32 mReflectionProbeCount; + + // resolution of reflection probes + U32 mProbeResolution = 1024; + + // maximum LoD of reflection probes (mip levels - 1) + F32 mMaxProbeLOD = 6.f; + + F32 mHeroProbeStrength = 1.f; + bool mIsInTransition = false; + + // if true, reset all probe render state on the next update (for teleports and sky changes) + bool mReset = false; + + std::set<LLVOVolume*> mHeroVOList; + LLVOVolume* mNearestHero; +}; + diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index df464bc7e5..aa96487907 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -1733,6 +1733,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild<LLUICtrl>("shinyOffsetV")->setValue(offset_y); getChild<LLUICtrl>("glossiness")->setValue(material->getSpecularLightExponent()); getChild<LLUICtrl>("environment")->setValue(material->getEnvironmentIntensity()); + getChild<LLUICtrl>("mirror")->setValue(material->getEnvironmentIntensity()); updateShinyControls(!material->getSpecularID().isNull(), true); } diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index d36662c11b..89c3c67286 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -143,6 +143,7 @@ protected: void sendTexGen(); // applies and sends bump map void sendShiny(U32 shininess); // applies and sends shininess void sendFullbright(); // applies and sends full bright + void sendGlow(); void alignTestureLayer(); @@ -232,7 +233,7 @@ protected: static void onCommitShiny( LLUICtrl* ctrl, void* userdata); static void onCommitAlphaMode( LLUICtrl* ctrl, void* userdata); static void onCommitFullbright( LLUICtrl* ctrl, void* userdata); - static void onCommitGlow( LLUICtrl* ctrl, void *userdata); + static void onCommitGlow( LLUICtrl* ctrl, void *userdata); static void onCommitPlanarAlign( LLUICtrl* ctrl, void* userdata); static void onCommitRepeatsPerMeter( LLUICtrl* ctrl, void* userinfo); diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index ed244f773c..74c02099f8 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -114,6 +114,11 @@ BOOL LLPanelVolume::postBuild() getChild<LLUICtrl>("FlexForceZ")->setValidateBeforeCommit(precommitValidate); } + // Mirror Parameters + { + childSetCommitCallback("Mirror Checkbox Ctrl", onCommitIsMirror, this); + } + // LIGHT Parameters { childSetCommitCallback("Light Checkbox Ctrl",onCommitIsLight,this); @@ -305,6 +310,10 @@ void LLPanelVolume::getState( ) getChildView("select_single")->setEnabled(true); } + BOOL is_mirror = volobjp && volobjp->isMirror(); + getChild<LLUICtrl>("Mirror Checkbox Ctrl")->setValue(is_mirror); + getChildView("Mirror Checkbox Ctrl")->setEnabled(editable && single_volume && volobjp); + // Light properties BOOL is_light = volobjp && volobjp->getIsLight(); getChild<LLUICtrl>("Light Checkbox Ctrl")->setValue(is_light); @@ -737,6 +746,21 @@ void LLPanelVolume::sendIsLight() LL_INFOS() << "update light sent" << LL_ENDL; } +void LLPanelVolume::sendIsMirror() +{ + LLViewerObject* objectp = mObject; + if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME)) + { + return; + } + LLVOVolume *volobjp = (LLVOVolume *)objectp; + + BOOL value = getChild<LLUICtrl>("Mirror Checkbox Ctrl")->getValue(); + volobjp->setIsMirror(value); + LL_INFOS() << "update mirror sent" << LL_ENDL; +} + + void LLPanelVolume::sendIsReflectionProbe() { LLViewerObject* objectp = mObject; @@ -1403,6 +1427,12 @@ void LLPanelVolume::onCommitIsLight( LLUICtrl* ctrl, void* userdata ) self->sendIsLight(); } +void LLPanelVolume::onCommitIsMirror( LLUICtrl* ctrl, void* userdata ) +{ + LLPanelVolume* self = (LLPanelVolume*) userdata; + self->sendIsMirror(); +} + // static void LLPanelVolume::setLightTextureID(const LLUUID &asset_id, const LLUUID &item_id, LLVOVolume* volobjp) { diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index 01b7ebb75c..a658351624 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -58,6 +58,7 @@ public: void sendIsLight(); + void sendIsMirror(); // when an object is becoming a refleciton probe, present a dialog asking for confirmation // otherwise, send the reflection probe update immediately void sendIsReflectionProbe(); @@ -71,6 +72,7 @@ public: static void onCommitIsLight( LLUICtrl* ctrl, void* userdata); static void onCommitLight( LLUICtrl* ctrl, void* userdata); + static void onCommitIsMirror( LLUICtrl* ctrl, void* userdata); static void onCommitIsReflectionProbe(LLUICtrl* ctrl, void* userdata); static void onCommitProbe(LLUICtrl* ctrl, void* userdata); void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata); diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp index a039c8072a..c2cb8f183a 100644 --- a/indra/newview/llreflectionmap.cpp +++ b/indra/newview/llreflectionmap.cpp @@ -49,7 +49,7 @@ LLReflectionMap::~LLReflectionMap() } } -void LLReflectionMap::update(U32 resolution, U32 face) +void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; mLastUpdateTime = gFrameTimeSeconds; @@ -63,7 +63,8 @@ void LLReflectionMap::update(U32 resolution, U32 face) { resolution /= 2; } - gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, getNearClip(), getIsDynamic()); + + gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, getNearClip(), getIsDynamic() || force_dynamic); } void LLReflectionMap::autoAdjustOrigin() diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h index 7ea0fe6187..a23bdc3a98 100644 --- a/indra/newview/llreflectionmap.h +++ b/indra/newview/llreflectionmap.h @@ -36,6 +36,15 @@ class alignas(16) LLReflectionMap : public LLRefCount { LL_ALIGN_NEW public: + + enum class ProbeType + { + ALL = 0, + RADIANCE, + IRRADIANCE, + REFLECTION + }; + // allocate an environment map of the given resolution LLReflectionMap(); @@ -43,7 +52,7 @@ public: // update this environment map // resolution - size of cube map to generate - void update(U32 resolution, U32 face); + void update(U32 resolution, U32 face, bool force_dynamic = false); // for volume partition probes, try to place this probe in the best spot void autoAdjustOrigin(); @@ -127,5 +136,7 @@ public: GLuint mOcclusionQuery = 0; bool mOccluded = false; U32 mOcclusionPendingFrames = 0; + + ProbeType mType; }; diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp index bb0bb04797..61143c61e8 100644 --- a/indra/newview/llreflectionmapmanager.cpp +++ b/indra/newview/llreflectionmapmanager.cpp @@ -297,8 +297,8 @@ void LLReflectionMapManager::update() } } - if (realtime && - closestDynamic == nullptr && + if (realtime && + closestDynamic == nullptr && probe->mCubeIndex != -1 && probe->getIsDynamic()) { @@ -313,7 +313,7 @@ void LLReflectionMapManager::update() // should do a full irradiance pass on "odd" frames and a radiance pass on "even" frames closestDynamic->autoAdjustOrigin(); - // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set + // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set // lighting values etc bool radiance_pass = isRadiancePass(); mRadiancePass = mRealtimeRadiancePass; @@ -553,7 +553,7 @@ void LLReflectionMapManager::doProbeUpdate() // Do the reflection map update render passes. // For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated -// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate +// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate // a simple mip chain (not convolution filter). // At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe // The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain. @@ -714,6 +714,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face) mTexture->bind(channel); gRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx); gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD); + gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, 1.f); U32 res = mMipChain[0].getWidth(); @@ -868,14 +869,14 @@ void LLReflectionMapManager::updateUniforms() // see class3/deferred/reflectionProbeF.glsl struct ReflectionProbeData { - // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of + // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of // the box probe - LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT]; + LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT]; // for sphere probes, origin (xyz) and radius (w) of refmaps in clip space - LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT]; + LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT]; - // extra parameters + // extra parameters // x - irradiance scale // y - radiance scale // z - fade in @@ -887,14 +888,14 @@ void LLReflectionMapManager::updateUniforms() // [i][1] - index into "refNeighbor" for probes that intersect this probe // [i][2] - number of probes that intersect this probe, or -1 for no neighbors // [i][3] - priority (probe type stored in sign bit - positive for spheres, negative for boxes) - GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4]; + GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4]; // list of neighbor indices - GLint refNeighbor[4096]; + GLint refNeighbor[4096]; GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth // numbrer of active refmaps - GLint refmapCount; + GLint refmapCount; }; mReflectionMaps.resize(mReflectionProbeCount); @@ -1111,7 +1112,7 @@ void LLReflectionMapManager::setUniforms() } if (mUBO == 0) - { + { updateUniforms(); } glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO); @@ -1292,8 +1293,8 @@ void LLReflectionMapManager::initReflectionMaps() } } -void LLReflectionMapManager::cleanup() -{ +void LLReflectionMapManager::cleanup() +{ mVertexBuffer = nullptr; mRenderTarget.release(); diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h index 5a3901cae9..4ef6c5fdff 100644 --- a/indra/newview/llreflectionmapmanager.h +++ b/indra/newview/llreflectionmapmanager.h @@ -43,21 +43,23 @@ class LLViewerObject; // reflection probe mininum scale #define LL_REFLECTION_PROBE_MINIMUM_SCALE 1.f; +void renderReflectionProbe(LLReflectionMap* probe); + class alignas(16) LLReflectionMapManager { LL_ALIGN_NEW public: - enum class DetailLevel + enum class DetailLevel { STATIC_ONLY = 0, STATIC_AND_DYNAMIC, REALTIME = 2 }; - // allocate an environment map of the given resolution + // allocate an environment map of the given resolution LLReflectionMapManager(); - // release any GL state + // release any GL state void cleanup(); // maintain reflection probes @@ -102,6 +104,7 @@ public: private: friend class LLPipeline; + friend class LLHeroProbeManager; // initialize mCubeFree array to default values void initCubeFree(); @@ -113,7 +116,7 @@ private: // returns -1 if allocation failed S32 allocateCubeIndex(); - // update the neighbors of the given probe + // update the neighbors of the given probe void updateNeighbors(LLReflectionMap* probe); // update UBO used for rendering (call only once per render pipe flush) diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 69e62ace97..dcdb66a6d4 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -314,7 +314,9 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mLastUpdateCached(FALSE), mCachedMuteListUpdateTime(0), mCachedOwnerInMuteList(false), - mRiggedAttachedWarned(false) + mRiggedAttachedWarned(false), +mIsMirror(false), +mMirrorPlacementMode(3) { if (!is_global) { @@ -1148,6 +1150,32 @@ U32 LLViewerObject::extractSpatialExtents(LLDataPackerBinaryBuffer *dp, LLVector return parent_id; } +void detectMirror(const std::string &str, bool &mirror, U8 &mode) +{ + + std::stringstream ss(str); + std::string word; + while (ss >> word) + { + if (word == "IsMirror") + { + mirror = true; + } + else if (word == "ReflectedX" && mirror) + { + mode = 0; + } + else if (word == "ReflectedY" && mirror) + { + mode = 1; + } + else if (word == "ReflectedZ" && mirror) + { + mode = 2; + } + } +} + U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, void **user_data, U32 block_num, @@ -1524,11 +1552,14 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, std::string temp_string; mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, temp_string, block_num ); + detectMirror(temp_string, mIsMirror, mMirrorPlacementMode); + LLColor4U coloru; mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num); - + // alpha was flipped so that it zero encoded better coloru.mV[3] = 255 - coloru.mV[3]; + mText->setColor(LLColor4(coloru)); mText->setString(temp_string); @@ -1908,6 +1939,9 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, { std::string temp_string; dp->unpackString(temp_string, "Text"); + + detectMirror(temp_string, mIsMirror, mMirrorPlacementMode); + LLColor4U coloru; dp->unpackBinaryDataFixed(coloru.mV, 4, "Color"); coloru.mV[3] = 255 - coloru.mV[3]; @@ -5307,7 +5341,6 @@ S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright) return retval; } - S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags) { // this might need work for media type @@ -6241,6 +6274,11 @@ LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 para new_block = new LLReflectionProbeParams(); break; } + case LLNetworkData::PARAMS_MIRROR: + { + new_block = new LLMirrorParams(); + break; + } default: { llassert(false); // invalid parameter type diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 898b21e1ae..c165f164a8 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -257,6 +257,8 @@ public: virtual BOOL isRiggedMesh() const { return FALSE; } virtual BOOL hasLightTexture() const { return FALSE; } virtual BOOL isReflectionProbe() const { return FALSE; } + virtual BOOL isMirror() const { return FALSE; } + virtual U8 mirrorPlacementMode() const { return 0; } // This method returns true if the object is over land owned by // the agent, one of its groups, or it encroaches and @@ -877,6 +879,9 @@ protected: F32 mPhysicsCost; F32 mLinksetPhysicsCost; + bool mIsMirror; + U8 mMirrorPlacementMode; + // If true, "shrink wrap" this volume in its spatial partition. See "shrinkWrap" bool mShouldShrinkWrap = false; diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 82b16d67bd..6e5d85bc88 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -2237,6 +2237,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() { gDeferredSoftenProgram.addPermutation("LOCAL_LIGHT_KILL", "1"); } + + gDeferredSoftenProgram.addPermutation("HERO_PROBES", "1"); if (gSavedSettings.getBOOL("RenderDeferredSSAO")) { //if using SSAO, take screen space light map into account as if shadows are enabled diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 34f55d3341..23580c4ec0 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -249,6 +249,9 @@ LLVOVolume::~LLVOVolume() mTextureAnimp = NULL; delete mVolumeImpl; mVolumeImpl = NULL; + + if (mIsMirror) + gPipeline.mHeroProbeManager.unregisterHeroDrawable(this); gMeshRepo.unregisterMesh(this); @@ -1044,7 +1047,12 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline) { updateReflectionProbePtr(); } - + + if (isMirror()) + { + gPipeline.mHeroProbeManager.registerHeroDrawable(this); + } + updateRadius(); bool force_update = true; // avoid non-alpha mDistance update being optimized away mDrawable->updateDistance(*LLViewerCamera::getInstance(), force_update); @@ -3367,6 +3375,48 @@ F32 LLVOVolume::getLightCutoff() const } } +bool LLVOVolume::setIsMirror(BOOL is_mirror) +{ + BOOL was_mirror = isMirror(); + if (is_mirror != was_mirror) + { + if (is_mirror) + { + setParameterEntryInUse(LLNetworkData::PARAMS_MIRROR, TRUE, true); + } + else + { + setParameterEntryInUse(LLNetworkData::PARAMS_MIRROR, FALSE, true); + } + } + + updateMirrorDrawable(); + + return was_mirror != is_mirror; +} + +void LLVOVolume::updateMirrorDrawable() +{ + if (isMirror()) + { + gPipeline.mHeroProbeManager.registerHeroDrawable(this); + } + else + { + gPipeline.mHeroProbeManager.unregisterHeroDrawable(this); + } +} + +BOOL LLVOVolume::isMirror() const +{ + return mIsMirror; +} + +U8 LLVOVolume::mirrorPlacementMode() const +{ + return mMirrorPlacementMode; +} + BOOL LLVOVolume::isReflectionProbe() const { return getParameterEntryInUse(LLNetworkData::PARAMS_REFLECTION_PROBE); @@ -4417,6 +4467,11 @@ void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_u } updateReflectionProbePtr(); + + if (isMirror()) + gPipeline.mHeroProbeManager.registerHeroDrawable(this); + else + gPipeline.mHeroProbeManager.unregisterHeroDrawable(this); } void LLVOVolume::updateReflectionProbePtr() diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index aadc1fbcf3..848f455e2a 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -294,6 +294,11 @@ public: F32 getLightRadius() const; F32 getLightFalloff(const F32 fudge_factor = 1.f) const; F32 getLightCutoff() const; + + // Mirrors + bool setIsMirror(BOOL is_mirror); + void updateMirrorDrawable(); + U8 mirrorPlacementMode() const override; // Reflection Probes bool setIsReflectionProbe(BOOL is_probe); @@ -307,6 +312,8 @@ public: F32 getReflectionProbeNearClip() const; bool getReflectionProbeIsBox() const; bool getReflectionProbeIsDynamic() const; + + BOOL isMirror() const override; // Flexible Objects U32 getVolumeInterfaceID() const; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 1620b1ff4c..af2a26f54d 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -642,6 +642,7 @@ void LLPipeline::cleanup() mCubeVB = NULL; mReflectionMapManager.cleanup(); + mHeroProbeManager.cleanup(); } //============================================================================ @@ -772,6 +773,7 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) { // hacky -- allocate auxillary buffer gCubeSnapshot = TRUE; mReflectionMapManager.initReflectionMaps(); + mHeroProbeManager.initReflectionMaps(); mRT = &mAuxillaryRT; U32 res = mReflectionMapManager.mProbeResolution * 4; //multiply by 4 because probes will be 16x super sampled allocateScreenBuffer(res, res, samples); @@ -2441,6 +2443,26 @@ void LLPipeline::doOcclusion(LLCamera& camera) gGL.setColorMask(true, true); } + + if (sReflectionProbesEnabled && sUseOcclusion > 1 && !LLPipeline::sShadowRender && !gCubeSnapshot) + { + gGL.setColorMask(false, false); + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + LLGLDisable cull(GL_CULL_FACE); + + gOcclusionCubeProgram.bind(); + + if (mCubeVB.isNull()) + { //cube VB will be used for issuing occlusion queries + mCubeVB = ll_create_cube_vb(LLVertexBuffer::MAP_VERTEX); + } + mCubeVB->setBuffer(); + + mHeroProbeManager.doOcclusion(); + gOcclusionCubeProgram.unbind(); + + gGL.setColorMask(true, true); + } if (LLPipeline::sUseOcclusion > 1 && (sCull->hasOcclusionGroups() || LLVOCachePartition::sNeedsOcclusionCheck)) @@ -3818,6 +3840,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera, bool do_occlusion) { //update reflection probe uniform mReflectionMapManager.updateUniforms(); + mHeroProbeManager.updateUniforms(); } U32 cur_type = 0; @@ -8485,6 +8508,14 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) mReflectionMapManager.mIrradianceMaps->bind(channel); bound = true; } + + channel = shader.enableTexture(LLShaderMgr::HERO_PROBE, LLTexUnit::TT_CUBE_MAP_ARRAY); + if (channel > -1 && mHeroProbeManager.mTexture.notNull()) + { + mHeroProbeManager.mTexture->bind(channel); + bound = true; + } + if (bound) { diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index fe92c69cbb..fc129f5912 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -39,6 +39,7 @@ #include "lldrawable.h" #include "llrendertarget.h" #include "llreflectionmapmanager.h" +#include "llheroprobemanager.h" #include <stack> @@ -455,6 +456,7 @@ public: void handleShadowDetailChanged(); LLReflectionMapManager mReflectionMapManager; + LLHeroProbeManager mHeroProbeManager; private: void unloadShaders(); diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index 9966fe0b56..510351b471 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -2410,7 +2410,15 @@ even though the user gets a free copy. name="object_horizontal" top_pad="10" width="278" /> - + <check_box + height="16" + label="Mirror" + layout="topleft" + left="10" + name="Mirror Checkbox Ctrl" + tool_tip="Causes object to be a mirror" + top_pad="8" + width="60" /> <check_box height="16" label="Light" |