diff options
Diffstat (limited to 'indra/newview/llreflectionmap.cpp')
-rw-r--r-- | indra/newview/llreflectionmap.cpp | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp new file mode 100644 index 0000000000..ec54fa1165 --- /dev/null +++ b/indra/newview/llreflectionmap.cpp @@ -0,0 +1,368 @@ +/** + * @file llreflectionmap.cpp + * @brief LLReflectionMap 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 "llreflectionmap.h" +#include "pipeline.h" +#include "llviewerwindow.h" +#include "llviewerregion.h" +#include "llworld.h" +#include "llshadermgr.h" + +extern F32SecondsImplicit gFrameTimeSeconds; + +extern U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center); + +LLReflectionMap::LLReflectionMap() +{ +} + +LLReflectionMap::~LLReflectionMap() +{ + if (mOcclusionQuery) + { + glDeleteQueries(1, &mOcclusionQuery); + } +} + +void LLReflectionMap::update(U32 resolution, U32 face) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + mLastUpdateTime = gFrameTimeSeconds; + llassert(mCubeArray.notNull()); + llassert(mCubeIndex != -1); + //llassert(LLPipeline::sRenderDeferred); + + // make sure we don't walk off the edge of the render target + while (resolution > gPipeline.mRT->deferredScreen.getWidth() || + resolution > gPipeline.mRT->deferredScreen.getHeight()) + { + resolution /= 2; + } + gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, getNearClip(), getIsDynamic()); +} + +void LLReflectionMap::autoAdjustOrigin() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + + if (mGroup && !mComplete) + { + const LLVector4a* bounds = mGroup->getBounds(); + auto* node = mGroup->getOctreeNode(); + + if (mGroup->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_VOLUME) + { + mPriority = 0; + // cast a ray towards 8 corners of bounding box + // nudge origin towards center of empty space + + if (!node) + { + return; + } + + mOrigin = bounds[0]; + + LLVector4a size = bounds[1]; + + LLVector4a corners[] = + { + { 1, 1, 1 }, + { -1, 1, 1 }, + { 1, -1, 1 }, + { -1, -1, 1 }, + { 1, 1, -1 }, + { -1, 1, -1 }, + { 1, -1, -1 }, + { -1, -1, -1 } + }; + + for (int i = 0; i < 8; ++i) + { + corners[i].mul(size); + corners[i].add(bounds[0]); + } + + LLVector4a extents[2]; + extents[0].setAdd(bounds[0], bounds[1]); + extents[1].setSub(bounds[0], bounds[1]); + + bool hit = false; + for (int i = 0; i < 8; ++i) + { + int face = -1; + LLVector4a intersection; + LLDrawable* drawable = mGroup->lineSegmentIntersect(bounds[0], corners[i], false, false, true, true, &face, &intersection); + if (drawable != nullptr) + { + hit = true; + update_min_max(extents[0], extents[1], intersection); + } + else + { + update_min_max(extents[0], extents[1], corners[i]); + } + } + + if (hit) + { + mOrigin.setAdd(extents[0], extents[1]); + mOrigin.mul(0.5f); + } + + // make sure origin isn't under ground + F32* fp = mOrigin.getF32ptr(); + LLVector3 origin(fp); + F32 height = LLWorld::instance().resolveLandHeightAgent(origin) + 2.f; + fp[2] = llmax(fp[2], height); + + // make sure radius encompasses all objects + LLSimdScalar r2 = 0.0; + for (int i = 0; i < 8; ++i) + { + LLVector4a v; + v.setSub(corners[i], mOrigin); + + LLSimdScalar d = v.dot3(v); + + if (d > r2) + { + r2 = d; + } + } + + mRadius = llmax(sqrtf(r2.getF32()), 8.f); + + // make sure near clip doesn't poke through ground + fp[2] = llmax(fp[2], height+mRadius*0.5f); + + } + } + else if (mViewerObject) + { + mPriority = 1; + mOrigin.load3(mViewerObject->getPositionAgent().mV); + mRadius = mViewerObject->getScale().mV[0]*0.5f; + } +} + +bool LLReflectionMap::intersects(LLReflectionMap* other) +{ + LLVector4a delta; + delta.setSub(other->mOrigin, mOrigin); + + F32 dist = delta.dot3(delta).getF32(); + + F32 r2 = mRadius + other->mRadius; + + r2 *= r2; + + return dist < r2; +} + +extern LLControlGroup gSavedSettings; + +F32 LLReflectionMap::getAmbiance() +{ + F32 ret = 0.f; + if (mViewerObject && mViewerObject->getVolume()) + { + ret = ((LLVOVolume*)mViewerObject)->getReflectionProbeAmbiance(); + } + + return ret; +} + +F32 LLReflectionMap::getNearClip() +{ + const F32 MINIMUM_NEAR_CLIP = 0.1f; + + F32 ret = 0.f; + + if (mViewerObject && mViewerObject->getVolume()) + { + ret = ((LLVOVolume*)mViewerObject)->getReflectionProbeNearClip(); + } + else if (mGroup) + { + ret = mRadius * 0.5f; // default to half radius for automatic object probes + } + else + { + ret = 1.f; // default to 1m for automatic terrain probes + } + + return llmax(ret, MINIMUM_NEAR_CLIP); +} + +bool LLReflectionMap::getIsDynamic() +{ + if (gSavedSettings.getS32("RenderReflectionProbeDetail") > (S32) LLReflectionMapManager::DetailLevel::STATIC_ONLY && + mViewerObject && + mViewerObject->getVolume()) + { + return ((LLVOVolume*)mViewerObject)->getReflectionProbeIsDynamic(); + } + + return false; +} + +bool LLReflectionMap::getBox(LLMatrix4& box) +{ + if (mViewerObject) + { + LLVolume* volume = mViewerObject->getVolume(); + if (volume) + { + LLVOVolume* vobjp = (LLVOVolume*)mViewerObject; + + if (vobjp->getReflectionProbeIsBox()) + { + glh::matrix4f mv(gGLModelView); + glh::matrix4f scale; + LLVector3 s = vobjp->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f)); + mRadius = s.magVec(); + scale.set_scale(glh::vec3f(s.mV)); + if (vobjp->mDrawable != nullptr) + { + // object to agent space (no scale) + glh::matrix4f rm((F32*)vobjp->mDrawable->getWorldMatrix().mMatrix); + + // construct object to camera space (with scale) + mv = mv * rm * scale; + + // inverse is camera space to object unit cube + mv = mv.inverse(); + + box = LLMatrix4(mv.m); + + return true; + } + } + } + } + + return false; +} + +bool LLReflectionMap::isActive() +{ + return mCubeIndex != -1; +} + +bool LLReflectionMap::isRelevant() +{ + static LLCachedControl<S32> RenderReflectionProbeLevel(gSavedSettings, "RenderReflectionProbeLevel", 3); + + if (mViewerObject && RenderReflectionProbeLevel > 0) + { // not an automatic probe + return true; + } + + if (RenderReflectionProbeLevel == 3) + { // all automatics are relevant + return true; + } + + if (RenderReflectionProbeLevel == 2) + { // terrain and water only, ignore probes that have a group + return !mGroup; + } + + // no automatic probes, yes manual probes + return mViewerObject != nullptr; +} + + +void LLReflectionMap::doOcclusion(const LLVector4a& eye) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; + if (LLGLSLShader::sProfileEnabled) + { + return; + } + +#if 1 + // super sloppy, but we're doing an occlusion cull against a bounding cube of + // a bounding sphere, pad radius so we assume if the eye is within + // the bounding sphere of the bounding cube, the node is not culled + F32 dist = mRadius * F_SQRT3 + 1.f; + + LLVector4a o; + o.setSub(mOrigin, eye); + + bool do_query = false; + + if (o.getLength3().getF32() < dist) + { // eye is inside radius, don't attempt to occlude + mOccluded = false; + return; + } + + if (mOcclusionQuery == 0) + { // no query was previously issued, allocate one and issue + LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("rmdo - glGenQueries"); + glGenQueries(1, &mOcclusionQuery); + do_query = true; + } + else + { // query was previously issued, check it and only issue a new query + // if previous query is available + LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("rmdo - glGetQueryObject"); + GLuint result = 0; + glGetQueryObjectuiv(mOcclusionQuery, GL_QUERY_RESULT_AVAILABLE, &result); + + if (result > 0) + { + do_query = true; + glGetQueryObjectuiv(mOcclusionQuery, GL_QUERY_RESULT, &result); + mOccluded = result == 0; + mOcclusionPendingFrames = 0; + } + else + { + mOcclusionPendingFrames++; + } + } + + if (do_query) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("rmdo - push query"); + glBeginQuery(GL_ANY_SAMPLES_PASSED, mOcclusionQuery); + + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + + shader->uniform3fv(LLShaderMgr::BOX_CENTER, 1, mOrigin.getF32ptr()); + shader->uniform3f(LLShaderMgr::BOX_SIZE, mRadius, mRadius, mRadius); + + gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(LLViewerCamera::getInstance(), mOrigin)); + + glEndQuery(GL_ANY_SAMPLES_PASSED); + } +#endif +} |