phoenix-firestorm/indra/newview/llreflectionmap.cpp

275 lines
8.0 KiB
C++

/**
* @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"
extern F32SecondsImplicit gFrameTimeSeconds;
LLReflectionMap::LLReflectionMap()
{
}
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)
{
const LLVector4a* bounds = mGroup->getBounds();
auto* node = mGroup->getOctreeNode();
if (mGroup->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_TERRAIN)
{
// for terrain, make probes float a couple meters above the highest point in the surface patch
mOrigin = bounds[0];
mOrigin.getF32ptr()[2] += bounds[1].getF32ptr()[2] + 3.f;
// update radius to encompass bounding box
LLVector4a d;
d.setAdd(bounds[0], bounds[1]);
d.sub(mOrigin);
mRadius = d.getLength3().getF32();
mPriority = 1;
}
else if (mGroup->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_VOLUME)
{
mPriority = 1;
// cast a ray towards 8 corners of bounding box
// nudge origin towards center of empty space
if (!node)
{
return;
}
if (node->isLeaf() || node->getChildCount() > 1 || node->getData().size() > 0)
{ // use center of object bounding box for leaf nodes or nodes with multiple child nodes
mOrigin = bounds[0];
LLVector4a start;
LLVector4a end;
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], true, false, 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 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);
}
else
{
// user placed probe
mPriority = 2;
// use center of octree node volume for nodes that are just branches without data
mOrigin = node->getCenter();
// update radius to encompass entire octree node volume
mRadius = node->getSize().getLength3().getF32();
//mOrigin = bounds[0];
//mRadius = bounds[1].getLength3().getF32();
}
}
}
else if (mViewerObject)
{
mPriority = 64;
mOrigin.load3(mViewerObject->getPositionAgent().mV);
mRadius = mViewerObject->getScale().mV[0]*0.5f;
}
}
bool LLReflectionMap::intersects(LLReflectionMap* other)
{
// TODO: incorporate getBox
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();
}
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)
{
glh::matrix4f rm((F32*)vobjp->mDrawable->getWorldMatrix().mMatrix);
glh::matrix4f rt((F32*)vobjp->getRelativeXform().mMatrix);
mv = mv * rm * scale; // *rt;
mv = mv.inverse();
box = LLMatrix4(mv.m);
return true;
}
}
}
}
return false;
}