736 lines
22 KiB
C++
736 lines
22 KiB
C++
/**
|
|
* @file lldrawpoolwater.cpp
|
|
* @brief LLDrawPoolWater class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, 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 "llfeaturemanager.h"
|
|
#include "lldrawpoolwater.h"
|
|
|
|
#include "llviewercontrol.h"
|
|
#include "lldir.h"
|
|
#include "llerror.h"
|
|
#include "m3math.h"
|
|
#include "llrender.h"
|
|
|
|
#include "llagent.h" // for gAgent for getRegion for getWaterHeight
|
|
#include "llcubemap.h"
|
|
#include "lldrawable.h"
|
|
#include "llface.h"
|
|
#include "llsky.h"
|
|
#include "llviewertexturelist.h"
|
|
#include "llviewerregion.h"
|
|
#include "llvosky.h"
|
|
#include "llvowater.h"
|
|
#include "llworld.h"
|
|
#include "pipeline.h"
|
|
#include "llviewershadermgr.h"
|
|
#include "llenvironment.h"
|
|
#include "llsettingssky.h"
|
|
#include "llsettingswater.h"
|
|
|
|
BOOL deferred_render = FALSE;
|
|
|
|
BOOL LLDrawPoolWater::sSkipScreenCopy = FALSE;
|
|
BOOL LLDrawPoolWater::sNeedsReflectionUpdate = TRUE;
|
|
BOOL LLDrawPoolWater::sNeedsDistortionUpdate = TRUE;
|
|
F32 LLDrawPoolWater::sWaterFogEnd = 0.f;
|
|
|
|
LLDrawPoolWater::LLDrawPoolWater() : LLFacePool(POOL_WATER)
|
|
{
|
|
// <FS:Zi> Render speedup for water parameters
|
|
gSavedSettings.getControl("RenderWaterMipNormal")->getCommitSignal()->connect(boost::bind(&LLDrawPoolWater::onRenderWaterMipNormalChanged, this));
|
|
onRenderWaterMipNormalChanged();
|
|
// </FS:Zi>
|
|
}
|
|
|
|
LLDrawPoolWater::~LLDrawPoolWater()
|
|
{
|
|
}
|
|
|
|
void LLDrawPoolWater::setTransparentTextures(const LLUUID& transparentTextureId, const LLUUID& nextTransparentTextureId)
|
|
{
|
|
LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater();
|
|
mWaterImagep[0] = LLViewerTextureManager::getFetchedTexture(!transparentTextureId.isNull() ? transparentTextureId : pwater->GetDefaultTransparentTextureAssetId());
|
|
mWaterImagep[1] = LLViewerTextureManager::getFetchedTexture(!nextTransparentTextureId.isNull() ? nextTransparentTextureId : (!transparentTextureId.isNull() ? transparentTextureId : pwater->GetDefaultTransparentTextureAssetId()));
|
|
mWaterImagep[0]->addTextureStats(1024.f*1024.f);
|
|
mWaterImagep[1]->addTextureStats(1024.f*1024.f);
|
|
}
|
|
|
|
void LLDrawPoolWater::setOpaqueTexture(const LLUUID& opaqueTextureId)
|
|
{
|
|
LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater();
|
|
mOpaqueWaterImagep = LLViewerTextureManager::getFetchedTexture(opaqueTextureId);
|
|
mOpaqueWaterImagep->addTextureStats(1024.f*1024.f);
|
|
}
|
|
|
|
void LLDrawPoolWater::setNormalMaps(const LLUUID& normalMapId, const LLUUID& nextNormalMapId)
|
|
{
|
|
LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater();
|
|
mWaterNormp[0] = LLViewerTextureManager::getFetchedTexture(!normalMapId.isNull() ? normalMapId : pwater->GetDefaultWaterNormalAssetId());
|
|
mWaterNormp[1] = LLViewerTextureManager::getFetchedTexture(!nextNormalMapId.isNull() ? nextNormalMapId : (!normalMapId.isNull() ? normalMapId : pwater->GetDefaultWaterNormalAssetId()));
|
|
mWaterNormp[0]->addTextureStats(1024.f*1024.f);
|
|
mWaterNormp[1]->addTextureStats(1024.f*1024.f);
|
|
}
|
|
|
|
//static
|
|
void LLDrawPoolWater::restoreGL()
|
|
{
|
|
/*LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater();
|
|
if (pwater)
|
|
{
|
|
setTransparentTextures(pwater->getTransparentTextureID(), pwater->getNextTransparentTextureID());
|
|
setOpaqueTexture(pwater->GetDefaultOpaqueTextureAssetId());
|
|
setNormalMaps(pwater->getNormalMapID(), pwater->getNextNormalMapID());
|
|
}*/
|
|
}
|
|
|
|
void LLDrawPoolWater::prerender()
|
|
{
|
|
mShaderLevel = (gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps) ? LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_WATER) : 0;
|
|
}
|
|
|
|
S32 LLDrawPoolWater::getNumPasses()
|
|
{
|
|
if (LLViewerCamera::getInstance()->getOrigin().mV[2] < 1024.f)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void LLDrawPoolWater::beginPostDeferredPass(S32 pass)
|
|
{
|
|
beginRenderPass(pass);
|
|
deferred_render = TRUE;
|
|
}
|
|
|
|
void LLDrawPoolWater::endPostDeferredPass(S32 pass)
|
|
{
|
|
endRenderPass(pass);
|
|
deferred_render = FALSE;
|
|
}
|
|
|
|
//===============================
|
|
//DEFERRED IMPLEMENTATION
|
|
//===============================
|
|
void LLDrawPoolWater::renderDeferred(S32 pass)
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_WATER);
|
|
|
|
if (!LLPipeline::sRenderTransparentWater)
|
|
{
|
|
// Will render opaque water without use of ALM
|
|
render(pass);
|
|
return;
|
|
}
|
|
|
|
deferred_render = TRUE;
|
|
renderWater();
|
|
deferred_render = FALSE;
|
|
}
|
|
|
|
//=========================================
|
|
|
|
void LLDrawPoolWater::render(S32 pass)
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_WATER);
|
|
if (mDrawFace.empty() || LLDrawable::getCurrentFrame() <= 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//do a quick 'n dirty depth sort
|
|
for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
|
|
iter != mDrawFace.end(); iter++)
|
|
{
|
|
LLFace* facep = *iter;
|
|
facep->mDistance = -facep->mCenterLocal.mV[2];
|
|
}
|
|
|
|
std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater());
|
|
|
|
// See if we are rendering water as opaque or not
|
|
if (!LLPipeline::sRenderTransparentWater)
|
|
{
|
|
// render water for low end hardware
|
|
renderOpaqueLegacyWater();
|
|
return;
|
|
}
|
|
|
|
LLGLEnable blend(GL_BLEND);
|
|
|
|
if ((mShaderLevel > 0) && !sSkipScreenCopy)
|
|
{
|
|
renderWater();
|
|
return;
|
|
}
|
|
|
|
LLVOSky *voskyp = gSky.mVOSkyp;
|
|
|
|
stop_glerror();
|
|
|
|
if (!gGLManager.mHasMultitexture)
|
|
{
|
|
// Ack! No multitexture! Bail!
|
|
return;
|
|
}
|
|
|
|
LLFace* refl_face = voskyp->getReflFace();
|
|
|
|
gPipeline.disableLights();
|
|
|
|
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
|
|
|
|
LLGLDisable cullFace(GL_CULL_FACE);
|
|
|
|
// Set up second pass first
|
|
gGL.getTexUnit(1)->activate();
|
|
gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(1)->bind(mWaterImagep[0]) ;
|
|
|
|
gGL.getTexUnit(2)->activate();
|
|
gGL.getTexUnit(2)->enable(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(2)->bind(mWaterImagep[1]) ;
|
|
|
|
LLVector3 camera_up = LLViewerCamera::getInstance()->getUpAxis();
|
|
F32 up_dot = camera_up * LLVector3::z_axis;
|
|
|
|
LLColor4 water_color;
|
|
if (LLViewerCamera::getInstance()->cameraUnderWater())
|
|
{
|
|
water_color.setVec(1.f, 1.f, 1.f, 0.4f);
|
|
}
|
|
else
|
|
{
|
|
water_color.setVec(1.f, 1.f, 1.f, 0.5f*(1.f + up_dot));
|
|
}
|
|
|
|
gGL.diffuseColor4fv(water_color.mV);
|
|
|
|
// Automatically generate texture coords for detail map
|
|
glEnable(GL_TEXTURE_GEN_S); //texture unit 1
|
|
glEnable(GL_TEXTURE_GEN_T); //texture unit 1
|
|
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
|
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
|
|
|
// Slowly move over time.
|
|
F32 offset = fmod(gFrameTimeSeconds*2.f, 100.f);
|
|
F32 tp0[4] = {16.f/256.f, 0.0f, 0.0f, offset*0.01f};
|
|
F32 tp1[4] = {0.0f, 16.f/256.f, 0.0f, offset*0.01f};
|
|
glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0);
|
|
glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1);
|
|
|
|
gGL.getTexUnit(0)->activate();
|
|
|
|
glClearStencil(1);
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
LLGLEnable gls_stencil(GL_STENCIL_TEST);
|
|
glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
|
|
glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
|
|
|
|
for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
|
|
iter != mDrawFace.end(); iter++)
|
|
{
|
|
LLFace *face = *iter;
|
|
if (voskyp->isReflFace(face))
|
|
{
|
|
continue;
|
|
}
|
|
gGL.getTexUnit(0)->bind(face->getTexture());
|
|
face->renderIndexed();
|
|
}
|
|
|
|
// Now, disable texture coord generation on texture state 1
|
|
gGL.getTexUnit(1)->activate();
|
|
gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(1)->disable();
|
|
|
|
glDisable(GL_TEXTURE_GEN_S); //texture unit 1
|
|
glDisable(GL_TEXTURE_GEN_T); //texture unit 1
|
|
|
|
gGL.getTexUnit(2)->activate();
|
|
gGL.getTexUnit(2)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(2)->disable();
|
|
|
|
glDisable(GL_TEXTURE_GEN_S); //texture unit 1
|
|
glDisable(GL_TEXTURE_GEN_T); //texture unit 1
|
|
|
|
// Disable texture coordinate and color arrays
|
|
gGL.getTexUnit(0)->activate();
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
stop_glerror();
|
|
|
|
if (gSky.mVOSkyp->getCubeMap())
|
|
{
|
|
gSky.mVOSkyp->getCubeMap()->enable(0);
|
|
gSky.mVOSkyp->getCubeMap()->bind();
|
|
|
|
gGL.matrixMode(LLRender::MM_TEXTURE);
|
|
gGL.loadIdentity();
|
|
LLMatrix4 camera_mat = LLViewerCamera::getInstance()->getModelview();
|
|
LLMatrix4 camera_rot(camera_mat.getMat3());
|
|
camera_rot.invert();
|
|
|
|
gGL.loadMatrix((F32 *)camera_rot.mMatrix);
|
|
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
LLOverrideFaceColor overrid(this, 1.f, 1.f, 1.f, 0.5f*up_dot);
|
|
|
|
for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
|
|
iter != mDrawFace.end(); iter++)
|
|
{
|
|
LLFace *face = *iter;
|
|
if (voskyp->isReflFace(face))
|
|
{
|
|
//refl_face = face;
|
|
continue;
|
|
}
|
|
|
|
if (face->getGeomCount() > 0)
|
|
{
|
|
face->renderIndexed();
|
|
}
|
|
}
|
|
|
|
gSky.mVOSkyp->getCubeMap()->disable();
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
|
|
gGL.matrixMode(LLRender::MM_TEXTURE);
|
|
gGL.loadIdentity();
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
|
|
}
|
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
if (refl_face)
|
|
{
|
|
glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF);
|
|
renderReflection(refl_face);
|
|
}
|
|
}
|
|
|
|
// for low end hardware
|
|
void LLDrawPoolWater::renderOpaqueLegacyWater()
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
|
|
LLVOSky *voskyp = gSky.mVOSkyp;
|
|
|
|
if (voskyp == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLGLSLShader* shader = NULL;
|
|
if (LLPipeline::sUnderWaterRender)
|
|
{
|
|
shader = &gObjectSimpleNonIndexedTexGenWaterProgram;
|
|
}
|
|
else
|
|
{
|
|
shader = &gObjectSimpleNonIndexedTexGenProgram;
|
|
}
|
|
|
|
shader->bind();
|
|
|
|
stop_glerror();
|
|
|
|
// Depth sorting and write to depth buffer
|
|
// since this is opaque, we should see nothing
|
|
// behind the water. No blending because
|
|
// of no transparency. And no face culling so
|
|
// that the underside of the water is also opaque.
|
|
LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
|
|
LLGLDisable no_cull(GL_CULL_FACE);
|
|
LLGLDisable no_blend(GL_BLEND);
|
|
|
|
gPipeline.disableLights();
|
|
|
|
// Activate the texture binding and bind one
|
|
// texture since all images will have the same texture
|
|
gGL.getTexUnit(0)->activate();
|
|
gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(0)->bind(mOpaqueWaterImagep);
|
|
|
|
// Automatically generate texture coords for water texture
|
|
if (!shader)
|
|
{
|
|
glEnable(GL_TEXTURE_GEN_S); //texture unit 0
|
|
glEnable(GL_TEXTURE_GEN_T); //texture unit 0
|
|
glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
|
glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
|
}
|
|
|
|
// Use the fact that we know all water faces are the same size
|
|
// to save some computation
|
|
|
|
// Slowly move texture coordinates over time so the watter appears
|
|
// to be moving.
|
|
F32 movement_period_secs = 50.f;
|
|
|
|
F32 offset = fmod(gFrameTimeSeconds, movement_period_secs);
|
|
|
|
if (movement_period_secs != 0)
|
|
{
|
|
offset /= movement_period_secs;
|
|
}
|
|
else
|
|
{
|
|
offset = 0;
|
|
}
|
|
|
|
F32 tp0[4] = { 16.f / 256.f, 0.0f, 0.0f, offset };
|
|
F32 tp1[4] = { 0.0f, 16.f / 256.f, 0.0f, offset };
|
|
|
|
if (!shader)
|
|
{
|
|
glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0);
|
|
glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1);
|
|
}
|
|
else
|
|
{
|
|
shader->uniform4fv(LLShaderMgr::OBJECT_PLANE_S, 1, tp0);
|
|
shader->uniform4fv(LLShaderMgr::OBJECT_PLANE_T, 1, tp1);
|
|
}
|
|
|
|
gGL.diffuseColor3f(1.f, 1.f, 1.f);
|
|
|
|
for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
|
|
iter != mDrawFace.end(); iter++)
|
|
{
|
|
LLFace *face = *iter;
|
|
if (voskyp->isReflFace(face))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
face->renderIndexed();
|
|
}
|
|
|
|
stop_glerror();
|
|
|
|
if (!shader)
|
|
{
|
|
// Reset the settings back to expected values
|
|
glDisable(GL_TEXTURE_GEN_S); //texture unit 0
|
|
glDisable(GL_TEXTURE_GEN_T); //texture unit 0
|
|
}
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
}
|
|
|
|
|
|
void LLDrawPoolWater::renderReflection(LLFace* face)
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
|
|
LLVOSky *voskyp = gSky.mVOSkyp;
|
|
|
|
if (!voskyp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!face->getGeomCount())
|
|
{
|
|
return;
|
|
}
|
|
|
|
S8 dr = voskyp->getDrawRefl();
|
|
if (dr < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLGLSNoFog noFog;
|
|
|
|
gGL.getTexUnit(0)->bind((dr == 0) ? voskyp->getSunTex() : voskyp->getMoonTex());
|
|
|
|
LLOverrideFaceColor override(this, LLColor4(face->getFaceColor().mV));
|
|
face->renderIndexed();
|
|
}
|
|
|
|
void LLDrawPoolWater::renderWater()
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
|
|
if (!deferred_render)
|
|
{
|
|
gGL.setColorMask(true, true);
|
|
}
|
|
|
|
LLGLDisable blend(GL_BLEND);
|
|
|
|
LLColor3 light_diffuse(0, 0, 0);
|
|
F32 light_exp = 0.0f;
|
|
|
|
LLEnvironment & environment = LLEnvironment::instance();
|
|
LLSettingsWater::ptr_t pwater = environment.getCurrentWater();
|
|
LLSettingsSky::ptr_t psky = environment.getCurrentSky();
|
|
LLVector3 light_dir = environment.getLightDirection();
|
|
bool sun_up = environment.getIsSunUp();
|
|
bool moon_up = environment.getIsMoonUp();
|
|
// <FS:Zi> Render speedup for water parameters
|
|
//bool has_normal_mips = gSavedSettings.getBOOL("RenderWaterMipNormal");
|
|
bool has_normal_mips = mRenderWaterMipNormal;
|
|
bool underwater = LLViewerCamera::getInstance()->cameraUnderWater();
|
|
|
|
if (sun_up)
|
|
{
|
|
light_diffuse += psky->getSunlightColor();
|
|
}
|
|
// moonlight is several orders of magnitude less bright than sunlight,
|
|
// so only use this color when the moon alone is showing
|
|
else if (moon_up)
|
|
{
|
|
light_diffuse += psky->getMoonlightColor();
|
|
}
|
|
|
|
// Apply magic numbers translating light direction into intensities
|
|
light_dir.normalize();
|
|
F32 ground_proj_sq = light_dir.mV[0] * light_dir.mV[0] + light_dir.mV[1] * light_dir.mV[1];
|
|
light_exp = llmax(32.f, 256.f * powf(ground_proj_sq, 16.0f));
|
|
if (0.f < light_diffuse.normalize()) // Normalizing a color? Puzzling...
|
|
{
|
|
light_diffuse *= (1.5f + (6.f * ground_proj_sq));
|
|
}
|
|
|
|
// set up normal maps filtering
|
|
for (auto norm_map : mWaterNormp)
|
|
{
|
|
if (norm_map) norm_map->setFilteringOption(has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT);
|
|
}
|
|
|
|
LLColor4 specular(sun_up ? psky->getSunlightColor() : psky->getMoonlightColor());
|
|
F32 phase_time = (F32) LLFrameTimer::getElapsedSeconds() * 0.5f;
|
|
bool edge = false;
|
|
LLGLSLShader *shader = nullptr;
|
|
do // twice through, once with normal shader bound & once with edge shader bound
|
|
{
|
|
// select shader
|
|
if (underwater && LLPipeline::sWaterReflections)
|
|
{
|
|
shader = deferred_render ? &gDeferredUnderWaterProgram : &gUnderWaterProgram;
|
|
}
|
|
else
|
|
{
|
|
if (edge && !deferred_render)
|
|
{
|
|
shader = &gWaterEdgeProgram;
|
|
}
|
|
else
|
|
{
|
|
shader = deferred_render ? &gDeferredWaterProgram : &gWaterProgram;
|
|
}
|
|
}
|
|
shader->bind();
|
|
|
|
// bind textures for water rendering
|
|
S32 reftex = shader->enableTexture(LLShaderMgr::WATER_REFTEX);
|
|
if (reftex > -1)
|
|
{
|
|
gGL.getTexUnit(reftex)->activate();
|
|
gGL.getTexUnit(reftex)->bind(&gPipeline.mWaterRef);
|
|
gGL.getTexUnit(0)->activate();
|
|
}
|
|
|
|
// bind normal map
|
|
S32 bumpTex = shader->enableTexture(LLViewerShaderMgr::BUMP_MAP);
|
|
S32 bumpTex2 = shader->enableTexture(LLViewerShaderMgr::BUMP_MAP2);
|
|
|
|
LLViewerTexture *tex_a = mWaterNormp[0];
|
|
LLViewerTexture *tex_b = mWaterNormp[1];
|
|
|
|
F32 blend_factor = pwater->getBlendFactor();
|
|
|
|
gGL.getTexUnit(bumpTex)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(bumpTex2)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
if (tex_a && (!tex_b || (tex_a == tex_b)))
|
|
{
|
|
gGL.getTexUnit(bumpTex)->bind(tex_a);
|
|
blend_factor = 0; // only one tex provided, no blending
|
|
}
|
|
else if (tex_b && !tex_a)
|
|
{
|
|
gGL.getTexUnit(bumpTex)->bind(tex_b);
|
|
blend_factor = 0; // only one tex provided, no blending
|
|
}
|
|
else if (tex_b != tex_a)
|
|
{
|
|
gGL.getTexUnit(bumpTex)->bind(tex_a);
|
|
gGL.getTexUnit(bumpTex2)->bind(tex_b);
|
|
}
|
|
|
|
// bind reflection texture from RenderTarget
|
|
S32 screentex = shader->enableTexture(LLShaderMgr::WATER_SCREENTEX);
|
|
F32 screenRes[] = {1.f / gGLViewport[2], 1.f / gGLViewport[3]};
|
|
|
|
S32 diffTex = shader->enableTexture(LLShaderMgr::DIFFUSE_MAP);
|
|
|
|
// set uniforms for shader
|
|
if (deferred_render)
|
|
{
|
|
if (shader->getUniformLocation(LLShaderMgr::DEFERRED_NORM_MATRIX) >= 0)
|
|
{
|
|
glh::matrix4f norm_mat = get_current_modelview().inverse().transpose();
|
|
shader->uniformMatrix4fv(LLShaderMgr::DEFERRED_NORM_MATRIX, 1, FALSE, norm_mat.m);
|
|
}
|
|
}
|
|
|
|
shader->uniform2fv(LLShaderMgr::DEFERRED_SCREEN_RES, 1, screenRes);
|
|
shader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor);
|
|
|
|
LLColor4 fog_color(pwater->getWaterFogColor(), 0.0f);
|
|
F32 fog_density = pwater->getModifiedWaterFogDensity(underwater);
|
|
|
|
if (screentex > -1)
|
|
{
|
|
shader->uniform1f(LLShaderMgr::WATER_FOGDENSITY, fog_density);
|
|
gGL.getTexUnit(screentex)->bind(&gPipeline.mWaterDis);
|
|
}
|
|
|
|
if (mShaderLevel == 1)
|
|
{
|
|
fog_color.mV[VW] = log(fog_density) / log(2);
|
|
}
|
|
|
|
F32 water_height = environment.getWaterHeight();
|
|
F32 camera_height = LLViewerCamera::getInstance()->getOrigin().mV[2];
|
|
shader->uniform1f(LLShaderMgr::WATER_WATERHEIGHT, camera_height - water_height);
|
|
shader->uniform1f(LLShaderMgr::WATER_TIME, phase_time);
|
|
shader->uniform3fv(LLShaderMgr::WATER_EYEVEC, 1, LLViewerCamera::getInstance()->getOrigin().mV);
|
|
|
|
shader->uniform4fv(LLShaderMgr::SPECULAR_COLOR, 1, specular.mV);
|
|
shader->uniform4fv(LLShaderMgr::WATER_FOGCOLOR, 1, fog_color.mV);
|
|
|
|
shader->uniform3fv(LLShaderMgr::WATER_SPECULAR, 1, light_diffuse.mV);
|
|
shader->uniform1f(LLShaderMgr::WATER_SPECULAR_EXP, light_exp);
|
|
if (LLEnvironment::instance().isCloudScrollPaused())
|
|
{
|
|
static const std::array<F32, 2> zerowave {{0.0f, 0.0f}};
|
|
|
|
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR1, 1, zerowave.data());
|
|
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR2, 1, zerowave.data());
|
|
}
|
|
else
|
|
{
|
|
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR1, 1, pwater->getWave1Dir().mV);
|
|
shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR2, 1, pwater->getWave2Dir().mV);
|
|
}
|
|
shader->uniform3fv(LLShaderMgr::WATER_LIGHT_DIR, 1, light_dir.mV);
|
|
|
|
shader->uniform3fv(LLShaderMgr::WATER_NORM_SCALE, 1, pwater->getNormalScale().mV);
|
|
shader->uniform1f(LLShaderMgr::WATER_FRESNEL_SCALE, pwater->getFresnelScale());
|
|
shader->uniform1f(LLShaderMgr::WATER_FRESNEL_OFFSET, pwater->getFresnelOffset());
|
|
shader->uniform1f(LLShaderMgr::WATER_BLUR_MULTIPLIER, pwater->getBlurMultiplier());
|
|
|
|
F32 sunAngle = llmax(0.f, light_dir.mV[1]);
|
|
F32 scaledAngle = 1.f - sunAngle;
|
|
|
|
shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up ? 1 : 0);
|
|
shader->uniform1f(LLShaderMgr::WATER_SUN_ANGLE, sunAngle);
|
|
shader->uniform1f(LLShaderMgr::WATER_SCALED_ANGLE, scaledAngle);
|
|
shader->uniform1f(LLShaderMgr::WATER_SUN_ANGLE2, 0.1f + 0.2f * sunAngle);
|
|
shader->uniform1i(LLShaderMgr::WATER_EDGE_FACTOR, edge ? 1 : 0);
|
|
|
|
LLVector4 rotated_light_direction = LLEnvironment::instance().getRotatedLightNorm();
|
|
shader->uniform4fv(LLViewerShaderMgr::LIGHTNORM, 1, rotated_light_direction.mV);
|
|
shader->uniform3fv(LLShaderMgr::WL_CAMPOSLOCAL, 1, LLViewerCamera::getInstance()->getOrigin().mV);
|
|
|
|
if (LLViewerCamera::getInstance()->cameraUnderWater())
|
|
{
|
|
shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleBelow());
|
|
}
|
|
else
|
|
{
|
|
shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleAbove());
|
|
}
|
|
|
|
LLGLDisable cullface(GL_CULL_FACE);
|
|
|
|
LLVOWater *water = nullptr;
|
|
for (LLFace *const &face : mDrawFace)
|
|
{
|
|
if (!face) continue;
|
|
water = static_cast<LLVOWater *>(face->getViewerObject());
|
|
if (!water) continue;
|
|
|
|
gGL.getTexUnit(diffTex)->bind(face->getTexture());
|
|
|
|
if (edge == (bool) water->getIsEdgePatch())
|
|
{
|
|
face->renderIndexed();
|
|
|
|
// Note non-void water being drawn, updates required
|
|
if (!edge) // SL-16461 remove !LLPipeline::sUseOcclusion check
|
|
{
|
|
sNeedsReflectionUpdate = TRUE;
|
|
sNeedsDistortionUpdate = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
shader->disableTexture(LLShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
|
|
shader->disableTexture(LLShaderMgr::WATER_SCREENTEX);
|
|
shader->disableTexture(LLShaderMgr::BUMP_MAP);
|
|
shader->disableTexture(LLShaderMgr::DIFFUSE_MAP);
|
|
shader->disableTexture(LLShaderMgr::WATER_REFTEX);
|
|
shader->disableTexture(LLShaderMgr::WATER_SCREENDEPTH);
|
|
|
|
// clean up
|
|
shader->unbind();
|
|
gGL.getTexUnit(bumpTex)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(bumpTex2)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
edge = !edge;
|
|
} while (!edge);
|
|
|
|
gGL.getTexUnit(0)->activate();
|
|
gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
|
|
if (!deferred_render)
|
|
{
|
|
gGL.setColorMask(true, false);
|
|
}
|
|
}
|
|
|
|
LLViewerTexture *LLDrawPoolWater::getDebugTexture()
|
|
{
|
|
return LLViewerFetchedTexture::sSmokeImagep;
|
|
}
|
|
|
|
LLColor3 LLDrawPoolWater::getDebugColor() const
|
|
{
|
|
return LLColor3(0.f, 1.f, 1.f);
|
|
}
|
|
|
|
// <FS:Zi> Render speedup for water parameters
|
|
void LLDrawPoolWater::onRenderWaterMipNormalChanged()
|
|
{
|
|
mRenderWaterMipNormal = (bool)gSavedSettings.getBOOL("RenderWaterMipNormal");
|
|
}
|
|
// </FS:Zi>
|