diff options
Diffstat (limited to 'indra/newview/lldrawpoolalpha.cpp')
-rw-r--r-- | indra/newview/lldrawpoolalpha.cpp | 1870 |
1 files changed, 935 insertions, 935 deletions
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 7e4c1287ff..7093560644 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -1,935 +1,935 @@ -/**
- * @file lldrawpoolalpha.cpp
- * @brief LLDrawPoolAlpha 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 "lldrawpoolalpha.h"
-
-#include "llglheaders.h"
-#include "llviewercontrol.h"
-#include "llcriticaldamp.h"
-#include "llfasttimer.h"
-#include "llrender.h"
-
-#include "llcubemap.h"
-#include "llsky.h"
-#include "lldrawable.h"
-#include "llface.h"
-#include "llviewercamera.h"
-#include "llviewertexturelist.h" // For debugging
-#include "llviewerobjectlist.h" // For debugging
-#include "llviewerwindow.h"
-#include "pipeline.h"
-#include "llviewershadermgr.h"
-#include "llviewerregion.h"
-#include "lldrawpoolwater.h"
-#include "llspatialpartition.h"
-#include "llglcommonfunc.h"
-#include "llvoavatar.h"
-
-#include "llenvironment.h"
-
-bool LLDrawPoolAlpha::sShowDebugAlpha = false;
-
-#define current_shader (LLGLSLShader::sCurBoundShaderPtr)
-
-LLVector4 LLDrawPoolAlpha::sWaterPlane;
-
-// minimum alpha before discarding a fragment
-static const F32 MINIMUM_ALPHA = 0.004f; // ~ 1/255
-
-// minimum alpha before discarding a fragment when rendering impostors
-static const F32 MINIMUM_IMPOSTOR_ALPHA = 0.1f;
-
-LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) :
- LLRenderPass(type), target_shader(NULL),
- mColorSFactor(LLRender::BF_UNDEF), mColorDFactor(LLRender::BF_UNDEF),
- mAlphaSFactor(LLRender::BF_UNDEF), mAlphaDFactor(LLRender::BF_UNDEF)
-{
-
-}
-
-LLDrawPoolAlpha::~LLDrawPoolAlpha()
-{
-}
-
-
-void LLDrawPoolAlpha::prerender()
-{
- mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT);
-
- // TODO: is this even necessay? These are probably set to never discard
- LLViewerFetchedTexture::sFlatNormalImagep->addTextureStats(1024.f*1024.f);
- LLViewerFetchedTexture::sWhiteImagep->addTextureStats(1024.f * 1024.f);
-}
-
-S32 LLDrawPoolAlpha::getNumPostDeferredPasses()
-{
- return 1;
-}
-
-// set some common parameters on the given shader to prepare for alpha rendering
-static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool deferredEnvironment, F32 water_sign)
-{
- static LLCachedControl<F32> displayGamma(gSavedSettings, "RenderDeferredDisplayGamma");
- F32 gamma = displayGamma;
-
- static LLStaticHashedString waterSign("waterSign");
-
- // Does this deferred shader need environment uniforms set such as sun_dir, etc. ?
- // NOTE: We don't actually need a gbuffer since we are doing forward rendering (for transparency) post deferred rendering
- // TODO: bindDeferredShader() probably should have the updating of the environment uniforms factored out into updateShaderEnvironmentUniforms()
- // i.e. shaders\class1\deferred\alphaF.glsl
- if (deferredEnvironment)
- {
- shader->mCanBindFast = false;
- }
-
- shader->bind();
- shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f / 2.2f));
-
- if (LLPipeline::sRenderingHUDs)
- { // for HUD attachments, only the pre-water pass is executed and we never want to clip anything
- LLVector4 near_clip(0, 0, -1, 0);
- shader->uniform1f(waterSign, 1.f);
- shader->uniform4fv(LLShaderMgr::WATER_WATERPLANE, 1, near_clip.mV);
- }
- else
- {
- shader->uniform1f(waterSign, water_sign);
- shader->uniform4fv(LLShaderMgr::WATER_WATERPLANE, 1, LLDrawPoolAlpha::sWaterPlane.mV);
- }
-
- if (LLPipeline::sImpostorRender)
- {
- shader->setMinimumAlpha(MINIMUM_IMPOSTOR_ALPHA);
- }
- else
- {
- shader->setMinimumAlpha(MINIMUM_ALPHA);
- }
- if (textureGamma)
- {
- shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
- }
-
- //also prepare rigged variant
- if (shader->mRiggedVariant && shader->mRiggedVariant != shader)
- {
- prepare_alpha_shader(shader->mRiggedVariant, textureGamma, deferredEnvironment, water_sign);
- }
-}
-
-extern bool gCubeSnapshot;
-
-void LLDrawPoolAlpha::renderPostDeferred(S32 pass)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
-
- if (LLPipeline::isWaterClip() && getType() == LLDrawPool::POOL_ALPHA_PRE_WATER)
- { // don't render alpha objects on the other side of the water plane if water is opaque
- return;
- }
-
- F32 water_sign = 1.f;
-
- if (getType() == LLDrawPool::POOL_ALPHA_PRE_WATER)
- {
- water_sign = -1.f;
- }
-
- if (LLPipeline::sUnderWaterRender)
- {
- water_sign *= -1.f;
- }
-
- // prepare shaders
- llassert(LLPipeline::sRenderDeferred);
-
- emissive_shader = &gDeferredEmissiveProgram;
- prepare_alpha_shader(emissive_shader, true, false, water_sign);
-
- pbr_emissive_shader = &gPBRGlowProgram;
- prepare_alpha_shader(pbr_emissive_shader, true, false, water_sign);
-
-
- fullbright_shader =
- (LLPipeline::sImpostorRender) ? &gDeferredFullbrightAlphaMaskProgram :
- (LLPipeline::sRenderingHUDs) ? &gHUDFullbrightAlphaMaskAlphaProgram :
- &gDeferredFullbrightAlphaMaskAlphaProgram;
- prepare_alpha_shader(fullbright_shader, true, true, water_sign);
-
- simple_shader =
- (LLPipeline::sImpostorRender) ? &gDeferredAlphaImpostorProgram :
- (LLPipeline::sRenderingHUDs) ? &gHUDAlphaProgram :
- &gDeferredAlphaProgram;
-
- prepare_alpha_shader(simple_shader, false, true, water_sign); //prime simple shader (loads shadow relevant uniforms)
-
- LLGLSLShader* materialShader = gDeferredMaterialProgram;
- for (int i = 0; i < LLMaterial::SHADER_COUNT*2; ++i)
- {
- prepare_alpha_shader(&materialShader[i], false, true, water_sign);
- }
-
- pbr_shader =
- (LLPipeline::sRenderingHUDs) ? &gHUDPBRAlphaProgram :
- &gDeferredPBRAlphaProgram;
-
- prepare_alpha_shader(pbr_shader, false, true, water_sign);
-
- // explicitly unbind here so render loop doesn't make assumptions about the last shader
- // already being setup for rendering
- LLGLSLShader::unbind();
-
- if (!LLPipeline::sRenderingHUDs)
- {
- // first pass, render rigged objects only and render to depth buffer
- forwardRender(true);
- }
-
- // second pass, regular forward alpha rendering
- forwardRender();
-
- // final pass, render to depth for depth of field effects
- if (!LLPipeline::sImpostorRender && gSavedSettings.getBOOL("RenderDepthOfField") && !gCubeSnapshot && !LLPipeline::sRenderingHUDs && getType() == LLDrawPool::POOL_ALPHA_POST_WATER)
- {
- //update depth buffer sampler
- simple_shader = fullbright_shader = &gDeferredFullbrightAlphaMaskProgram;
-
- simple_shader->bind();
- simple_shader->setMinimumAlpha(0.33f);
-
- // mask off color buffer writes as we're only writing to depth buffer
- gGL.setColorMask(false, false);
-
- // If the face is more than 90% transparent, then don't update the Depth buffer for Dof
- // We don't want the nearly invisible objects to cause of DoF effects
- renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2,
- true); // <--- discard mostly transparent faces
-
- gGL.setColorMask(true, false);
- }
-}
-
-void LLDrawPoolAlpha::forwardRender(bool rigged)
-{
- gPipeline.enableLightsDynamic();
-
- LLGLSPipelineAlpha gls_pipeline_alpha;
-
- //enable writing to alpha for emissive effects
- gGL.setColorMask(true, true);
-
- bool write_depth = rigged ||
- LLDrawPoolWater::sSkipScreenCopy
- // we want depth written so that rendered alpha will
- // contribute to the alpha mask used for impostors
- || LLPipeline::sImpostorRenderAlphaDepthPass
- || getType() == LLDrawPoolAlpha::POOL_ALPHA_PRE_WATER; // needed for accurate water fog
-
-
- LLGLDepthTest depth(GL_TRUE, write_depth ? GL_TRUE : GL_FALSE);
-
- mColorSFactor = LLRender::BF_SOURCE_ALPHA; // } regular alpha blend
- mColorDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // }
- mAlphaSFactor = LLRender::BF_ZERO; // } glow suppression
- mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // }
- gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
-
- // If the face is more than 90% transparent, then don't update the Depth buffer for Dof
- // We don't want the nearly invisible objects to cause of DoF effects
- renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, false, rigged);
-
- gGL.setColorMask(true, false);
-
- if (!rigged)
- { //render "highlight alpha" on final non-rigged pass
- // NOTE -- hacky call here protected by !rigged instead of alongside "forwardRender"
- // so renderDebugAlpha is executed while gls_pipeline_alpha and depth GL state
- // variables above are still in scope
- renderDebugAlpha();
- }
-}
-
-void LLDrawPoolAlpha::renderDebugAlpha()
-{
- if (sShowDebugAlpha)
- {
- gHighlightProgram.bind();
- gGL.diffuseColor4f(1, 0, 0, 1);
- gGL.getTexUnit(0)->bindFast(LLViewerFetchedTexture::getSmokeImage());
-
-
- renderAlphaHighlight();
-
- pushUntexturedBatches(LLRenderPass::PASS_ALPHA_MASK);
- pushUntexturedBatches(LLRenderPass::PASS_ALPHA_INVISIBLE);
-
- // Material alpha mask
- gGL.diffuseColor4f(0, 0, 1, 1);
- pushUntexturedBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK);
- pushUntexturedBatches(LLRenderPass::PASS_NORMMAP_MASK);
- pushUntexturedBatches(LLRenderPass::PASS_SPECMAP_MASK);
- pushUntexturedBatches(LLRenderPass::PASS_NORMSPEC_MASK);
- pushUntexturedBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK);
- pushUntexturedBatches(LLRenderPass::PASS_GLTF_PBR_ALPHA_MASK);
-
- gGL.diffuseColor4f(0, 1, 0, 1);
- pushUntexturedBatches(LLRenderPass::PASS_INVISIBLE);
-
- gHighlightProgram.mRiggedVariant->bind();
- gGL.diffuseColor4f(1, 0, 0, 1);
-
- pushRiggedBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, false);
- pushRiggedBatches(LLRenderPass::PASS_ALPHA_INVISIBLE_RIGGED, false);
-
- // Material alpha mask
- gGL.diffuseColor4f(0, 0, 1, 1);
- pushRiggedBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK_RIGGED, false);
- pushRiggedBatches(LLRenderPass::PASS_NORMMAP_MASK_RIGGED, false);
- pushRiggedBatches(LLRenderPass::PASS_SPECMAP_MASK_RIGGED, false);
- pushRiggedBatches(LLRenderPass::PASS_NORMSPEC_MASK_RIGGED, false);
- pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, false);
- pushRiggedBatches(LLRenderPass::PASS_GLTF_PBR_ALPHA_MASK_RIGGED, false);
-
- gGL.diffuseColor4f(0, 1, 0, 1);
- pushRiggedBatches(LLRenderPass::PASS_INVISIBLE_RIGGED, false);
- LLGLSLShader::sCurBoundShaderPtr->unbind();
- }
-}
-
-void LLDrawPoolAlpha::renderAlphaHighlight()
-{
- for (int pass = 0; pass < 2; ++pass)
- { //two passes, one rigged and one not
- LLVOAvatar* lastAvatar = nullptr;
- U64 lastMeshId = 0;
-
- LLCullResult::sg_iterator begin = pass == 0 ? gPipeline.beginAlphaGroups() : gPipeline.beginRiggedAlphaGroups();
- LLCullResult::sg_iterator end = pass == 0 ? gPipeline.endAlphaGroups() : gPipeline.endRiggedAlphaGroups();
-
- for (LLCullResult::sg_iterator i = begin; i != end; ++i)
- {
- LLSpatialGroup* group = *i;
- if (group->getSpatialPartition()->mRenderByGroup &&
- !group->isDead())
- {
- LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA+pass]; // <-- hacky + pass to use PASS_ALPHA_RIGGED on second pass
-
- for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)
- {
- LLDrawInfo& params = **k;
-
- bool rigged = (params.mAvatar != nullptr);
- gHighlightProgram.bind(rigged);
- gGL.diffuseColor4f(1, 0, 0, 1);
-
- if (rigged)
- {
- if (lastAvatar != params.mAvatar ||
- lastMeshId != params.mSkinInfo->mHash)
- {
- if (!uploadMatrixPalette(params))
- {
- continue;
- }
- lastAvatar = params.mAvatar;
- lastMeshId = params.mSkinInfo->mHash;
- }
- }
-
- LLRenderPass::applyModelMatrix(params);
- params.mVertexBuffer->setBuffer();
- params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset);
- }
- }
- }
- }
-
- // make sure static version of highlight shader is bound before returning
- gHighlightProgram.bind();
-}
-
-inline bool IsFullbright(LLDrawInfo& params)
-{
- return params.mFullbright;
-}
-
-inline bool IsMaterial(LLDrawInfo& params)
-{
- return params.mMaterial != nullptr;
-}
-
-inline bool IsEmissive(LLDrawInfo& params)
-{
- return params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE);
-}
-
-inline void Draw(LLDrawInfo* draw, U32 mask)
-{
- draw->mVertexBuffer->setBuffer();
- LLRenderPass::applyModelMatrix(*draw);
- draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
-}
-
-bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material)
-{
- bool tex_setup = false;
-
- if (draw->mGLTFMaterial)
- {
- if (draw->mTextureMatrix)
- {
- tex_setup = true;
- gGL.getTexUnit(0)->activate();
- gGL.matrixMode(LLRender::MM_TEXTURE);
- gGL.loadMatrix((GLfloat*)draw->mTextureMatrix->mMatrix);
- gPipeline.mTextureMatrixOps++;
- }
- }
- else
- {
- if (!LLPipeline::sRenderingHUDs && use_material && current_shader)
- {
- if (draw->mNormalMap)
- {
- current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap);
- }
-
- if (draw->mSpecularMap)
- {
- current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap);
- }
- }
- else if (current_shader == simple_shader || current_shader == simple_shader->mRiggedVariant)
- {
- current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
- current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep);
- }
- if (draw->mTextureList.size() > 1)
- {
- for (U32 i = 0; i < draw->mTextureList.size(); ++i)
- {
- if (draw->mTextureList[i].notNull())
- {
- gGL.getTexUnit(i)->bindFast(draw->mTextureList[i]);
- }
- }
- }
- else
- { //not batching textures or batch has only 1 texture -- might need a texture matrix
- if (draw->mTexture.notNull())
- {
- if (use_material)
- {
- current_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, draw->mTexture);
- }
- else
- {
- gGL.getTexUnit(0)->bindFast(draw->mTexture);
- }
-
- if (draw->mTextureMatrix)
- {
- tex_setup = true;
- gGL.getTexUnit(0)->activate();
- gGL.matrixMode(LLRender::MM_TEXTURE);
- gGL.loadMatrix((GLfloat*)draw->mTextureMatrix->mMatrix);
- gPipeline.mTextureMatrixOps++;
- }
- }
- else
- {
- gGL.getTexUnit(0)->unbindFast(LLTexUnit::TT_TEXTURE);
- }
- }
- }
-
- return tex_setup;
-}
-
-void LLDrawPoolAlpha::RestoreTexSetup(bool tex_setup)
-{
- if (tex_setup)
- {
- gGL.getTexUnit(0)->activate();
- gGL.matrixMode(LLRender::MM_TEXTURE);
- gGL.loadIdentity();
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- }
-}
-
-void LLDrawPoolAlpha::drawEmissive(LLDrawInfo* draw)
-{
- LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f);
- draw->mVertexBuffer->setBuffer();
- draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
-}
-
-
-void LLDrawPoolAlpha::renderEmissives(std::vector<LLDrawInfo*>& emissives)
-{
- emissive_shader->bind();
- emissive_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f);
-
- for (LLDrawInfo* draw : emissives)
- {
- bool tex_setup = TexSetup(draw, false);
- drawEmissive(draw);
- RestoreTexSetup(tex_setup);
- }
-}
-
-void LLDrawPoolAlpha::renderPbrEmissives(std::vector<LLDrawInfo*>& emissives)
-{
- pbr_emissive_shader->bind();
-
- for (LLDrawInfo* draw : emissives)
- {
- llassert(draw->mGLTFMaterial);
- LLGLDisable cull_face(draw->mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0);
- draw->mGLTFMaterial->bind(draw->mTexture);
- draw->mVertexBuffer->setBuffer();
- draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
- }
-}
-
-void LLDrawPoolAlpha::renderRiggedEmissives(std::vector<LLDrawInfo*>& emissives)
-{
- LLGLDepthTest depth(GL_TRUE, GL_FALSE); //disable depth writes since "emissive" is additive so sorting doesn't matter
- LLGLSLShader* shader = emissive_shader->mRiggedVariant;
- shader->bind();
- shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f);
-
- LLVOAvatar* lastAvatar = nullptr;
- U64 lastMeshId = 0;
-
- for (LLDrawInfo* draw : emissives)
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("Emissives");
-
- bool tex_setup = TexSetup(draw, false);
- if (lastAvatar != draw->mAvatar || lastMeshId != draw->mSkinInfo->mHash)
- {
- if (!uploadMatrixPalette(*draw))
- { // failed to upload matrix palette, skip rendering
- continue;
- }
- lastAvatar = draw->mAvatar;
- lastMeshId = draw->mSkinInfo->mHash;
- }
- drawEmissive(draw);
- RestoreTexSetup(tex_setup);
- }
-}
-
-void LLDrawPoolAlpha::renderRiggedPbrEmissives(std::vector<LLDrawInfo*>& emissives)
-{
- LLGLDepthTest depth(GL_TRUE, GL_FALSE); //disable depth writes since "emissive" is additive so sorting doesn't matter
- pbr_emissive_shader->bind(true);
-
- LLVOAvatar* lastAvatar = nullptr;
- U64 lastMeshId = 0;
-
- for (LLDrawInfo* draw : emissives)
- {
- if (lastAvatar != draw->mAvatar || lastMeshId != draw->mSkinInfo->mHash)
- {
- if (!uploadMatrixPalette(*draw))
- { // failed to upload matrix palette, skip rendering
- continue;
- }
- lastAvatar = draw->mAvatar;
- lastMeshId = draw->mSkinInfo->mHash;
- }
-
- LLGLDisable cull_face(draw->mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0);
- draw->mGLTFMaterial->bind(draw->mTexture);
- draw->mVertexBuffer->setBuffer();
- draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
- }
-}
-
-void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
- bool initialized_lighting = false;
- bool light_enabled = true;
-
- LLVOAvatar* lastAvatar = nullptr;
- U64 lastMeshId = 0;
- LLGLSLShader* lastAvatarShader = nullptr;
-
- LLCullResult::sg_iterator begin;
- LLCullResult::sg_iterator end;
-
- if (rigged)
- {
- begin = gPipeline.beginRiggedAlphaGroups();
- end = gPipeline.endRiggedAlphaGroups();
- }
- else
- {
- begin = gPipeline.beginAlphaGroups();
- end = gPipeline.endAlphaGroups();
- }
-
- LLEnvironment& env = LLEnvironment::instance();
- F32 water_height = env.getWaterHeight();
-
- bool above_water = getType() == LLDrawPool::POOL_ALPHA_POST_WATER;
- if (LLPipeline::sUnderWaterRender)
- {
- above_water = !above_water;
- }
-
-
- for (LLCullResult::sg_iterator i = begin; i != end; ++i)
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("renderAlpha - group");
- LLSpatialGroup* group = *i;
- llassert(group);
- llassert(group->getSpatialPartition());
-
- if (group->getSpatialPartition()->mRenderByGroup &&
- !group->isDead())
- {
-
- LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge();
- const LLVector4a* ext = bridge ? bridge->getSpatialExtents() : group->getExtents();
-
- if (!LLPipeline::sRenderingHUDs) // ignore above/below water for HUD render
- {
- if (above_water)
- { // reject any spatial groups that have no part above water
- if (ext[1].getF32ptr()[2] < water_height)
- {
- continue;
- }
- }
- else
- { // reject any spatial groups that he no part below water
- if (ext[0].getF32ptr()[2] > water_height)
- {
- continue;
- }
- }
- }
-
- static std::vector<LLDrawInfo*> emissives;
- static std::vector<LLDrawInfo*> rigged_emissives;
- static std::vector<LLDrawInfo*> pbr_emissives;
- static std::vector<LLDrawInfo*> pbr_rigged_emissives;
-
- emissives.resize(0);
- rigged_emissives.resize(0);
- pbr_emissives.resize(0);
- pbr_rigged_emissives.resize(0);
-
- bool is_particle_or_hud_particle = group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_PARTICLE
- || group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_HUD_PARTICLE;
-
- bool disable_cull = is_particle_or_hud_particle;
- LLGLDisable cull(disable_cull ? GL_CULL_FACE : 0);
-
- LLSpatialGroup::drawmap_elem_t& draw_info = rigged ? group->mDrawMap[LLRenderPass::PASS_ALPHA_RIGGED] : group->mDrawMap[LLRenderPass::PASS_ALPHA];
-
- for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)
- {
- LLDrawInfo& params = **k;
- if ((bool)params.mAvatar != rigged)
- {
- continue;
- }
-
- LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("ra - push batch");
-
- LLRenderPass::applyModelMatrix(params);
-
- LLMaterial* mat = NULL;
- LLGLTFMaterial *gltf_mat = params.mGLTFMaterial;
-
- LLGLDisable cull_face(gltf_mat && gltf_mat->mDoubleSided ? GL_CULL_FACE : 0);
-
- if (gltf_mat && gltf_mat->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_BLEND)
- {
- target_shader = pbr_shader;
- if (params.mAvatar != nullptr)
- {
- target_shader = target_shader->mRiggedVariant;
- }
-
- // shader must be bound before LLGLTFMaterial::bind
- if (current_shader != target_shader)
- {
- gPipeline.bindDeferredShaderFast(*target_shader);
- }
-
- params.mGLTFMaterial->bind(params.mTexture);
- }
- else
- {
- mat = LLPipeline::sRenderingHUDs ? nullptr : params.mMaterial;
-
- if (params.mFullbright)
- {
- // Turn off lighting if it hasn't already been so.
- if (light_enabled || !initialized_lighting)
- {
- initialized_lighting = true;
- target_shader = fullbright_shader;
-
- light_enabled = false;
- }
- }
- // Turn on lighting if it isn't already.
- else if (!light_enabled || !initialized_lighting)
- {
- initialized_lighting = true;
- target_shader = simple_shader;
- light_enabled = true;
- }
-
- if (LLPipeline::sRenderingHUDs)
- {
- target_shader = fullbright_shader;
- }
- else if (mat)
- {
- U32 mask = params.mShaderMask;
-
- llassert(mask < LLMaterial::SHADER_COUNT);
- target_shader = &(gDeferredMaterialProgram[mask]);
- }
- else if (!params.mFullbright)
- {
- target_shader = simple_shader;
- }
- else
- {
- target_shader = fullbright_shader;
- }
-
- if (params.mAvatar != nullptr)
- {
- llassert(target_shader->mRiggedVariant != nullptr);
- target_shader = target_shader->mRiggedVariant;
- }
-
- if (current_shader != target_shader)
- {// If we need shaders, and we're not ALREADY using the proper shader, then bind it
- // (this way we won't rebind shaders unnecessarily).
- gPipeline.bindDeferredShaderFast(*target_shader);
-
- if (params.mFullbright)
- { // make sure the bind the exposure map for fullbright shaders so they can cancel out exposure
- S32 channel = target_shader->enableTexture(LLShaderMgr::EXPOSURE_MAP);
- if (channel > -1)
- {
- gGL.getTexUnit(channel)->bind(&gPipeline.mExposureMap);
- }
- }
- }
-
- LLVector4 spec_color(1, 1, 1, 1);
- F32 env_intensity = 0.0f;
- F32 brightness = 1.0f;
-
- // We have a material. Supply the appropriate data here.
- if (mat)
- {
- spec_color = params.mSpecColor;
- env_intensity = params.mEnvIntensity;
- brightness = params.mFullbright ? 1.f : 0.f;
- }
-
- if (current_shader)
- {
- current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, spec_color.mV[0], spec_color.mV[1], spec_color.mV[2], spec_color.mV[3]);
- current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env_intensity);
- current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, brightness);
- }
- }
-
- if (params.mAvatar != nullptr)
- {
- if (lastAvatar != params.mAvatar ||
- lastMeshId != params.mSkinInfo->mHash ||
- lastAvatarShader != LLGLSLShader::sCurBoundShaderPtr)
- {
- if (!uploadMatrixPalette(params))
- {
- continue;
- }
- lastAvatar = params.mAvatar;
- lastMeshId = params.mSkinInfo->mHash;
- lastAvatarShader = LLGLSLShader::sCurBoundShaderPtr;
- }
- }
-
- bool tex_setup = TexSetup(¶ms, (mat != nullptr));
-
- {
- gGL.blendFunc((LLRender::eBlendFactor) params.mBlendFuncSrc, (LLRender::eBlendFactor) params.mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
-
- bool reset_minimum_alpha = false;
- if (!LLPipeline::sImpostorRender &&
- params.mBlendFuncDst != LLRender::BF_SOURCE_ALPHA &&
- params.mBlendFuncSrc != LLRender::BF_SOURCE_ALPHA)
- { // this draw call has a custom blend function that may require rendering of "invisible" fragments
- current_shader->setMinimumAlpha(0.f);
- reset_minimum_alpha = true;
- }
-
- params.mVertexBuffer->setBuffer();
- params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset);
-
- if (reset_minimum_alpha)
- {
- current_shader->setMinimumAlpha(MINIMUM_ALPHA);
- }
- }
-
- // If this alpha mesh has glow, then draw it a second time to add the destination-alpha (=glow). Interleaving these state-changing calls is expensive, but glow must be drawn Z-sorted with alpha.
- if (getType() != LLDrawPool::POOL_ALPHA_PRE_WATER &&
- params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE))
- {
- if (params.mAvatar != nullptr)
- {
- if (params.mGLTFMaterial.isNull())
- {
- rigged_emissives.push_back(¶ms);
- }
- else
- {
- pbr_rigged_emissives.push_back(¶ms);
- }
- }
- else
- {
- if (params.mGLTFMaterial.isNull())
- {
- emissives.push_back(¶ms);
- }
- else
- {
- pbr_emissives.push_back(¶ms);
- }
- }
- }
-
- if (tex_setup)
- {
- gGL.getTexUnit(0)->activate();
- gGL.matrixMode(LLRender::MM_TEXTURE);
- gGL.loadIdentity();
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- }
- }
-
- // render emissive faces into alpha channel for bloom effects
- if (!depth_only)
- {
- gPipeline.enableLightsDynamic();
-
- // install glow-accumulating blend mode
- // don't touch color, add to alpha (glow)
- gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, LLRender::BF_ONE, LLRender::BF_ONE);
-
- bool rebind = false;
- LLGLSLShader* lastShader = current_shader;
- if (!emissives.empty())
- {
- light_enabled = true;
- renderEmissives(emissives);
- rebind = true;
- }
-
- if (!pbr_emissives.empty())
- {
- light_enabled = true;
- renderPbrEmissives(pbr_emissives);
- rebind = true;
- }
-
- if (!rigged_emissives.empty())
- {
- light_enabled = true;
- renderRiggedEmissives(rigged_emissives);
- rebind = true;
- }
-
- if (!pbr_rigged_emissives.empty())
- {
- light_enabled = true;
- renderRiggedPbrEmissives(pbr_rigged_emissives);
- rebind = true;
- }
-
- // restore our alpha blend mode
- gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
-
- if (lastShader && rebind)
- {
- lastShader->bind();
- }
- }
- }
- }
-
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
-
- LLVertexBuffer::unbind();
-
- if (!light_enabled)
- {
- gPipeline.enableLightsDynamic();
- }
-}
-
-bool LLDrawPoolAlpha::uploadMatrixPalette(const LLDrawInfo& params)
-{
- if (params.mAvatar.isNull())
- {
- return false;
- }
- const LLVOAvatar::MatrixPaletteCache& mpc = params.mAvatar.get()->updateSkinInfoMatrixPalette(params.mSkinInfo);
- U32 count = mpc.mMatrixPalette.size();
-
- if (count == 0)
- {
- //skin info not loaded yet, don't render
- return false;
- }
-
- LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
- count,
- false,
- (GLfloat*)&(mpc.mGLMp[0]));
-
- return true;
-}
+/** + * @file lldrawpoolalpha.cpp + * @brief LLDrawPoolAlpha 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 "lldrawpoolalpha.h" + +#include "llglheaders.h" +#include "llviewercontrol.h" +#include "llcriticaldamp.h" +#include "llfasttimer.h" +#include "llrender.h" + +#include "llcubemap.h" +#include "llsky.h" +#include "lldrawable.h" +#include "llface.h" +#include "llviewercamera.h" +#include "llviewertexturelist.h" // For debugging +#include "llviewerobjectlist.h" // For debugging +#include "llviewerwindow.h" +#include "pipeline.h" +#include "llviewershadermgr.h" +#include "llviewerregion.h" +#include "lldrawpoolwater.h" +#include "llspatialpartition.h" +#include "llglcommonfunc.h" +#include "llvoavatar.h" + +#include "llenvironment.h" + +bool LLDrawPoolAlpha::sShowDebugAlpha = false; + +#define current_shader (LLGLSLShader::sCurBoundShaderPtr) + +LLVector4 LLDrawPoolAlpha::sWaterPlane; + +// minimum alpha before discarding a fragment +static const F32 MINIMUM_ALPHA = 0.004f; // ~ 1/255 + +// minimum alpha before discarding a fragment when rendering impostors +static const F32 MINIMUM_IMPOSTOR_ALPHA = 0.1f; + +LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) : + LLRenderPass(type), target_shader(NULL), + mColorSFactor(LLRender::BF_UNDEF), mColorDFactor(LLRender::BF_UNDEF), + mAlphaSFactor(LLRender::BF_UNDEF), mAlphaDFactor(LLRender::BF_UNDEF) +{ + +} + +LLDrawPoolAlpha::~LLDrawPoolAlpha() +{ +} + + +void LLDrawPoolAlpha::prerender() +{ + mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT); + + // TODO: is this even necessay? These are probably set to never discard + LLViewerFetchedTexture::sFlatNormalImagep->addTextureStats(1024.f*1024.f); + LLViewerFetchedTexture::sWhiteImagep->addTextureStats(1024.f * 1024.f); +} + +S32 LLDrawPoolAlpha::getNumPostDeferredPasses() +{ + return 1; +} + +// set some common parameters on the given shader to prepare for alpha rendering +static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool deferredEnvironment, F32 water_sign) +{ + static LLCachedControl<F32> displayGamma(gSavedSettings, "RenderDeferredDisplayGamma"); + F32 gamma = displayGamma; + + static LLStaticHashedString waterSign("waterSign"); + + // Does this deferred shader need environment uniforms set such as sun_dir, etc. ? + // NOTE: We don't actually need a gbuffer since we are doing forward rendering (for transparency) post deferred rendering + // TODO: bindDeferredShader() probably should have the updating of the environment uniforms factored out into updateShaderEnvironmentUniforms() + // i.e. shaders\class1\deferred\alphaF.glsl + if (deferredEnvironment) + { + shader->mCanBindFast = false; + } + + shader->bind(); + shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f / 2.2f)); + + if (LLPipeline::sRenderingHUDs) + { // for HUD attachments, only the pre-water pass is executed and we never want to clip anything + LLVector4 near_clip(0, 0, -1, 0); + shader->uniform1f(waterSign, 1.f); + shader->uniform4fv(LLShaderMgr::WATER_WATERPLANE, 1, near_clip.mV); + } + else + { + shader->uniform1f(waterSign, water_sign); + shader->uniform4fv(LLShaderMgr::WATER_WATERPLANE, 1, LLDrawPoolAlpha::sWaterPlane.mV); + } + + if (LLPipeline::sImpostorRender) + { + shader->setMinimumAlpha(MINIMUM_IMPOSTOR_ALPHA); + } + else + { + shader->setMinimumAlpha(MINIMUM_ALPHA); + } + if (textureGamma) + { + shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f); + } + + //also prepare rigged variant + if (shader->mRiggedVariant && shader->mRiggedVariant != shader) + { + prepare_alpha_shader(shader->mRiggedVariant, textureGamma, deferredEnvironment, water_sign); + } +} + +extern bool gCubeSnapshot; + +void LLDrawPoolAlpha::renderPostDeferred(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + + if (LLPipeline::isWaterClip() && getType() == LLDrawPool::POOL_ALPHA_PRE_WATER) + { // don't render alpha objects on the other side of the water plane if water is opaque + return; + } + + F32 water_sign = 1.f; + + if (getType() == LLDrawPool::POOL_ALPHA_PRE_WATER) + { + water_sign = -1.f; + } + + if (LLPipeline::sUnderWaterRender) + { + water_sign *= -1.f; + } + + // prepare shaders + llassert(LLPipeline::sRenderDeferred); + + emissive_shader = &gDeferredEmissiveProgram; + prepare_alpha_shader(emissive_shader, true, false, water_sign); + + pbr_emissive_shader = &gPBRGlowProgram; + prepare_alpha_shader(pbr_emissive_shader, true, false, water_sign); + + + fullbright_shader = + (LLPipeline::sImpostorRender) ? &gDeferredFullbrightAlphaMaskProgram : + (LLPipeline::sRenderingHUDs) ? &gHUDFullbrightAlphaMaskAlphaProgram : + &gDeferredFullbrightAlphaMaskAlphaProgram; + prepare_alpha_shader(fullbright_shader, true, true, water_sign); + + simple_shader = + (LLPipeline::sImpostorRender) ? &gDeferredAlphaImpostorProgram : + (LLPipeline::sRenderingHUDs) ? &gHUDAlphaProgram : + &gDeferredAlphaProgram; + + prepare_alpha_shader(simple_shader, false, true, water_sign); //prime simple shader (loads shadow relevant uniforms) + + LLGLSLShader* materialShader = gDeferredMaterialProgram; + for (int i = 0; i < LLMaterial::SHADER_COUNT*2; ++i) + { + prepare_alpha_shader(&materialShader[i], false, true, water_sign); + } + + pbr_shader = + (LLPipeline::sRenderingHUDs) ? &gHUDPBRAlphaProgram : + &gDeferredPBRAlphaProgram; + + prepare_alpha_shader(pbr_shader, false, true, water_sign); + + // explicitly unbind here so render loop doesn't make assumptions about the last shader + // already being setup for rendering + LLGLSLShader::unbind(); + + if (!LLPipeline::sRenderingHUDs) + { + // first pass, render rigged objects only and render to depth buffer + forwardRender(true); + } + + // second pass, regular forward alpha rendering + forwardRender(); + + // final pass, render to depth for depth of field effects + if (!LLPipeline::sImpostorRender && gSavedSettings.getBOOL("RenderDepthOfField") && !gCubeSnapshot && !LLPipeline::sRenderingHUDs && getType() == LLDrawPool::POOL_ALPHA_POST_WATER) + { + //update depth buffer sampler + simple_shader = fullbright_shader = &gDeferredFullbrightAlphaMaskProgram; + + simple_shader->bind(); + simple_shader->setMinimumAlpha(0.33f); + + // mask off color buffer writes as we're only writing to depth buffer + gGL.setColorMask(false, false); + + // If the face is more than 90% transparent, then don't update the Depth buffer for Dof + // We don't want the nearly invisible objects to cause of DoF effects + renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, + true); // <--- discard mostly transparent faces + + gGL.setColorMask(true, false); + } +} + +void LLDrawPoolAlpha::forwardRender(bool rigged) +{ + gPipeline.enableLightsDynamic(); + + LLGLSPipelineAlpha gls_pipeline_alpha; + + //enable writing to alpha for emissive effects + gGL.setColorMask(true, true); + + bool write_depth = rigged || + LLDrawPoolWater::sSkipScreenCopy + // we want depth written so that rendered alpha will + // contribute to the alpha mask used for impostors + || LLPipeline::sImpostorRenderAlphaDepthPass + || getType() == LLDrawPoolAlpha::POOL_ALPHA_PRE_WATER; // needed for accurate water fog + + + LLGLDepthTest depth(GL_TRUE, write_depth ? GL_TRUE : GL_FALSE); + + mColorSFactor = LLRender::BF_SOURCE_ALPHA; // } regular alpha blend + mColorDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // } + mAlphaSFactor = LLRender::BF_ZERO; // } glow suppression + mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // } + gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); + + // If the face is more than 90% transparent, then don't update the Depth buffer for Dof + // We don't want the nearly invisible objects to cause of DoF effects + renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, false, rigged); + + gGL.setColorMask(true, false); + + if (!rigged) + { //render "highlight alpha" on final non-rigged pass + // NOTE -- hacky call here protected by !rigged instead of alongside "forwardRender" + // so renderDebugAlpha is executed while gls_pipeline_alpha and depth GL state + // variables above are still in scope + renderDebugAlpha(); + } +} + +void LLDrawPoolAlpha::renderDebugAlpha() +{ + if (sShowDebugAlpha) + { + gHighlightProgram.bind(); + gGL.diffuseColor4f(1, 0, 0, 1); + gGL.getTexUnit(0)->bindFast(LLViewerFetchedTexture::getSmokeImage()); + + + renderAlphaHighlight(); + + pushUntexturedBatches(LLRenderPass::PASS_ALPHA_MASK); + pushUntexturedBatches(LLRenderPass::PASS_ALPHA_INVISIBLE); + + // Material alpha mask + gGL.diffuseColor4f(0, 0, 1, 1); + pushUntexturedBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK); + pushUntexturedBatches(LLRenderPass::PASS_NORMMAP_MASK); + pushUntexturedBatches(LLRenderPass::PASS_SPECMAP_MASK); + pushUntexturedBatches(LLRenderPass::PASS_NORMSPEC_MASK); + pushUntexturedBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK); + pushUntexturedBatches(LLRenderPass::PASS_GLTF_PBR_ALPHA_MASK); + + gGL.diffuseColor4f(0, 1, 0, 1); + pushUntexturedBatches(LLRenderPass::PASS_INVISIBLE); + + gHighlightProgram.mRiggedVariant->bind(); + gGL.diffuseColor4f(1, 0, 0, 1); + + pushRiggedBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, false); + pushRiggedBatches(LLRenderPass::PASS_ALPHA_INVISIBLE_RIGGED, false); + + // Material alpha mask + gGL.diffuseColor4f(0, 0, 1, 1); + pushRiggedBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK_RIGGED, false); + pushRiggedBatches(LLRenderPass::PASS_NORMMAP_MASK_RIGGED, false); + pushRiggedBatches(LLRenderPass::PASS_SPECMAP_MASK_RIGGED, false); + pushRiggedBatches(LLRenderPass::PASS_NORMSPEC_MASK_RIGGED, false); + pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, false); + pushRiggedBatches(LLRenderPass::PASS_GLTF_PBR_ALPHA_MASK_RIGGED, false); + + gGL.diffuseColor4f(0, 1, 0, 1); + pushRiggedBatches(LLRenderPass::PASS_INVISIBLE_RIGGED, false); + LLGLSLShader::sCurBoundShaderPtr->unbind(); + } +} + +void LLDrawPoolAlpha::renderAlphaHighlight() +{ + for (int pass = 0; pass < 2; ++pass) + { //two passes, one rigged and one not + LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + + LLCullResult::sg_iterator begin = pass == 0 ? gPipeline.beginAlphaGroups() : gPipeline.beginRiggedAlphaGroups(); + LLCullResult::sg_iterator end = pass == 0 ? gPipeline.endAlphaGroups() : gPipeline.endRiggedAlphaGroups(); + + for (LLCullResult::sg_iterator i = begin; i != end; ++i) + { + LLSpatialGroup* group = *i; + if (group->getSpatialPartition()->mRenderByGroup && + !group->isDead()) + { + LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA+pass]; // <-- hacky + pass to use PASS_ALPHA_RIGGED on second pass + + for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) + { + LLDrawInfo& params = **k; + + bool rigged = (params.mAvatar != nullptr); + gHighlightProgram.bind(rigged); + gGL.diffuseColor4f(1, 0, 0, 1); + + if (rigged) + { + if (lastAvatar != params.mAvatar || + lastMeshId != params.mSkinInfo->mHash) + { + if (!uploadMatrixPalette(params)) + { + continue; + } + lastAvatar = params.mAvatar; + lastMeshId = params.mSkinInfo->mHash; + } + } + + LLRenderPass::applyModelMatrix(params); + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); + } + } + } + } + + // make sure static version of highlight shader is bound before returning + gHighlightProgram.bind(); +} + +inline bool IsFullbright(LLDrawInfo& params) +{ + return params.mFullbright; +} + +inline bool IsMaterial(LLDrawInfo& params) +{ + return params.mMaterial != nullptr; +} + +inline bool IsEmissive(LLDrawInfo& params) +{ + return params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE); +} + +inline void Draw(LLDrawInfo* draw, U32 mask) +{ + draw->mVertexBuffer->setBuffer(); + LLRenderPass::applyModelMatrix(*draw); + draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); +} + +bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material) +{ + bool tex_setup = false; + + if (draw->mGLTFMaterial) + { + if (draw->mTextureMatrix) + { + tex_setup = true; + gGL.getTexUnit(0)->activate(); + gGL.matrixMode(LLRender::MM_TEXTURE); + gGL.loadMatrix((GLfloat*)draw->mTextureMatrix->mMatrix); + gPipeline.mTextureMatrixOps++; + } + } + else + { + if (!LLPipeline::sRenderingHUDs && use_material && current_shader) + { + if (draw->mNormalMap) + { + current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap); + } + + if (draw->mSpecularMap) + { + current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap); + } + } + else if (current_shader == simple_shader || current_shader == simple_shader->mRiggedVariant) + { + current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep); + current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep); + } + if (draw->mTextureList.size() > 1) + { + for (U32 i = 0; i < draw->mTextureList.size(); ++i) + { + if (draw->mTextureList[i].notNull()) + { + gGL.getTexUnit(i)->bindFast(draw->mTextureList[i]); + } + } + } + else + { //not batching textures or batch has only 1 texture -- might need a texture matrix + if (draw->mTexture.notNull()) + { + if (use_material) + { + current_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, draw->mTexture); + } + else + { + gGL.getTexUnit(0)->bindFast(draw->mTexture); + } + + if (draw->mTextureMatrix) + { + tex_setup = true; + gGL.getTexUnit(0)->activate(); + gGL.matrixMode(LLRender::MM_TEXTURE); + gGL.loadMatrix((GLfloat*)draw->mTextureMatrix->mMatrix); + gPipeline.mTextureMatrixOps++; + } + } + else + { + gGL.getTexUnit(0)->unbindFast(LLTexUnit::TT_TEXTURE); + } + } + } + + return tex_setup; +} + +void LLDrawPoolAlpha::RestoreTexSetup(bool tex_setup) +{ + if (tex_setup) + { + gGL.getTexUnit(0)->activate(); + gGL.matrixMode(LLRender::MM_TEXTURE); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + } +} + +void LLDrawPoolAlpha::drawEmissive(LLDrawInfo* draw) +{ + LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f); + draw->mVertexBuffer->setBuffer(); + draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); +} + + +void LLDrawPoolAlpha::renderEmissives(std::vector<LLDrawInfo*>& emissives) +{ + emissive_shader->bind(); + emissive_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f); + + for (LLDrawInfo* draw : emissives) + { + bool tex_setup = TexSetup(draw, false); + drawEmissive(draw); + RestoreTexSetup(tex_setup); + } +} + +void LLDrawPoolAlpha::renderPbrEmissives(std::vector<LLDrawInfo*>& emissives) +{ + pbr_emissive_shader->bind(); + + for (LLDrawInfo* draw : emissives) + { + llassert(draw->mGLTFMaterial); + LLGLDisable cull_face(draw->mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0); + draw->mGLTFMaterial->bind(draw->mTexture); + draw->mVertexBuffer->setBuffer(); + draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); + } +} + +void LLDrawPoolAlpha::renderRiggedEmissives(std::vector<LLDrawInfo*>& emissives) +{ + LLGLDepthTest depth(GL_TRUE, GL_FALSE); //disable depth writes since "emissive" is additive so sorting doesn't matter + LLGLSLShader* shader = emissive_shader->mRiggedVariant; + shader->bind(); + shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f); + + LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + + for (LLDrawInfo* draw : emissives) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("Emissives"); + + bool tex_setup = TexSetup(draw, false); + if (lastAvatar != draw->mAvatar || lastMeshId != draw->mSkinInfo->mHash) + { + if (!uploadMatrixPalette(*draw)) + { // failed to upload matrix palette, skip rendering + continue; + } + lastAvatar = draw->mAvatar; + lastMeshId = draw->mSkinInfo->mHash; + } + drawEmissive(draw); + RestoreTexSetup(tex_setup); + } +} + +void LLDrawPoolAlpha::renderRiggedPbrEmissives(std::vector<LLDrawInfo*>& emissives) +{ + LLGLDepthTest depth(GL_TRUE, GL_FALSE); //disable depth writes since "emissive" is additive so sorting doesn't matter + pbr_emissive_shader->bind(true); + + LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + + for (LLDrawInfo* draw : emissives) + { + if (lastAvatar != draw->mAvatar || lastMeshId != draw->mSkinInfo->mHash) + { + if (!uploadMatrixPalette(*draw)) + { // failed to upload matrix palette, skip rendering + continue; + } + lastAvatar = draw->mAvatar; + lastMeshId = draw->mSkinInfo->mHash; + } + + LLGLDisable cull_face(draw->mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0); + draw->mGLTFMaterial->bind(draw->mTexture); + draw->mVertexBuffer->setBuffer(); + draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); + } +} + +void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + bool initialized_lighting = false; + bool light_enabled = true; + + LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + LLGLSLShader* lastAvatarShader = nullptr; + + LLCullResult::sg_iterator begin; + LLCullResult::sg_iterator end; + + if (rigged) + { + begin = gPipeline.beginRiggedAlphaGroups(); + end = gPipeline.endRiggedAlphaGroups(); + } + else + { + begin = gPipeline.beginAlphaGroups(); + end = gPipeline.endAlphaGroups(); + } + + LLEnvironment& env = LLEnvironment::instance(); + F32 water_height = env.getWaterHeight(); + + bool above_water = getType() == LLDrawPool::POOL_ALPHA_POST_WATER; + if (LLPipeline::sUnderWaterRender) + { + above_water = !above_water; + } + + + for (LLCullResult::sg_iterator i = begin; i != end; ++i) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("renderAlpha - group"); + LLSpatialGroup* group = *i; + llassert(group); + llassert(group->getSpatialPartition()); + + if (group->getSpatialPartition()->mRenderByGroup && + !group->isDead()) + { + + LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge(); + const LLVector4a* ext = bridge ? bridge->getSpatialExtents() : group->getExtents(); + + if (!LLPipeline::sRenderingHUDs) // ignore above/below water for HUD render + { + if (above_water) + { // reject any spatial groups that have no part above water + if (ext[1].getF32ptr()[2] < water_height) + { + continue; + } + } + else + { // reject any spatial groups that he no part below water + if (ext[0].getF32ptr()[2] > water_height) + { + continue; + } + } + } + + static std::vector<LLDrawInfo*> emissives; + static std::vector<LLDrawInfo*> rigged_emissives; + static std::vector<LLDrawInfo*> pbr_emissives; + static std::vector<LLDrawInfo*> pbr_rigged_emissives; + + emissives.resize(0); + rigged_emissives.resize(0); + pbr_emissives.resize(0); + pbr_rigged_emissives.resize(0); + + bool is_particle_or_hud_particle = group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_PARTICLE + || group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_HUD_PARTICLE; + + bool disable_cull = is_particle_or_hud_particle; + LLGLDisable cull(disable_cull ? GL_CULL_FACE : 0); + + LLSpatialGroup::drawmap_elem_t& draw_info = rigged ? group->mDrawMap[LLRenderPass::PASS_ALPHA_RIGGED] : group->mDrawMap[LLRenderPass::PASS_ALPHA]; + + for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) + { + LLDrawInfo& params = **k; + if ((bool)params.mAvatar != rigged) + { + continue; + } + + LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("ra - push batch"); + + LLRenderPass::applyModelMatrix(params); + + LLMaterial* mat = NULL; + LLGLTFMaterial *gltf_mat = params.mGLTFMaterial; + + LLGLDisable cull_face(gltf_mat && gltf_mat->mDoubleSided ? GL_CULL_FACE : 0); + + if (gltf_mat && gltf_mat->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_BLEND) + { + target_shader = pbr_shader; + if (params.mAvatar != nullptr) + { + target_shader = target_shader->mRiggedVariant; + } + + // shader must be bound before LLGLTFMaterial::bind + if (current_shader != target_shader) + { + gPipeline.bindDeferredShaderFast(*target_shader); + } + + params.mGLTFMaterial->bind(params.mTexture); + } + else + { + mat = LLPipeline::sRenderingHUDs ? nullptr : params.mMaterial; + + if (params.mFullbright) + { + // Turn off lighting if it hasn't already been so. + if (light_enabled || !initialized_lighting) + { + initialized_lighting = true; + target_shader = fullbright_shader; + + light_enabled = false; + } + } + // Turn on lighting if it isn't already. + else if (!light_enabled || !initialized_lighting) + { + initialized_lighting = true; + target_shader = simple_shader; + light_enabled = true; + } + + if (LLPipeline::sRenderingHUDs) + { + target_shader = fullbright_shader; + } + else if (mat) + { + U32 mask = params.mShaderMask; + + llassert(mask < LLMaterial::SHADER_COUNT); + target_shader = &(gDeferredMaterialProgram[mask]); + } + else if (!params.mFullbright) + { + target_shader = simple_shader; + } + else + { + target_shader = fullbright_shader; + } + + if (params.mAvatar != nullptr) + { + llassert(target_shader->mRiggedVariant != nullptr); + target_shader = target_shader->mRiggedVariant; + } + + if (current_shader != target_shader) + {// If we need shaders, and we're not ALREADY using the proper shader, then bind it + // (this way we won't rebind shaders unnecessarily). + gPipeline.bindDeferredShaderFast(*target_shader); + + if (params.mFullbright) + { // make sure the bind the exposure map for fullbright shaders so they can cancel out exposure + S32 channel = target_shader->enableTexture(LLShaderMgr::EXPOSURE_MAP); + if (channel > -1) + { + gGL.getTexUnit(channel)->bind(&gPipeline.mExposureMap); + } + } + } + + LLVector4 spec_color(1, 1, 1, 1); + F32 env_intensity = 0.0f; + F32 brightness = 1.0f; + + // We have a material. Supply the appropriate data here. + if (mat) + { + spec_color = params.mSpecColor; + env_intensity = params.mEnvIntensity; + brightness = params.mFullbright ? 1.f : 0.f; + } + + if (current_shader) + { + current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, spec_color.mV[0], spec_color.mV[1], spec_color.mV[2], spec_color.mV[3]); + current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env_intensity); + current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, brightness); + } + } + + if (params.mAvatar != nullptr) + { + if (lastAvatar != params.mAvatar || + lastMeshId != params.mSkinInfo->mHash || + lastAvatarShader != LLGLSLShader::sCurBoundShaderPtr) + { + if (!uploadMatrixPalette(params)) + { + continue; + } + lastAvatar = params.mAvatar; + lastMeshId = params.mSkinInfo->mHash; + lastAvatarShader = LLGLSLShader::sCurBoundShaderPtr; + } + } + + bool tex_setup = TexSetup(¶ms, (mat != nullptr)); + + { + gGL.blendFunc((LLRender::eBlendFactor) params.mBlendFuncSrc, (LLRender::eBlendFactor) params.mBlendFuncDst, mAlphaSFactor, mAlphaDFactor); + + bool reset_minimum_alpha = false; + if (!LLPipeline::sImpostorRender && + params.mBlendFuncDst != LLRender::BF_SOURCE_ALPHA && + params.mBlendFuncSrc != LLRender::BF_SOURCE_ALPHA) + { // this draw call has a custom blend function that may require rendering of "invisible" fragments + current_shader->setMinimumAlpha(0.f); + reset_minimum_alpha = true; + } + + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); + + if (reset_minimum_alpha) + { + current_shader->setMinimumAlpha(MINIMUM_ALPHA); + } + } + + // If this alpha mesh has glow, then draw it a second time to add the destination-alpha (=glow). Interleaving these state-changing calls is expensive, but glow must be drawn Z-sorted with alpha. + if (getType() != LLDrawPool::POOL_ALPHA_PRE_WATER && + params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE)) + { + if (params.mAvatar != nullptr) + { + if (params.mGLTFMaterial.isNull()) + { + rigged_emissives.push_back(¶ms); + } + else + { + pbr_rigged_emissives.push_back(¶ms); + } + } + else + { + if (params.mGLTFMaterial.isNull()) + { + emissives.push_back(¶ms); + } + else + { + pbr_emissives.push_back(¶ms); + } + } + } + + if (tex_setup) + { + gGL.getTexUnit(0)->activate(); + gGL.matrixMode(LLRender::MM_TEXTURE); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + } + } + + // render emissive faces into alpha channel for bloom effects + if (!depth_only) + { + gPipeline.enableLightsDynamic(); + + // install glow-accumulating blend mode + // don't touch color, add to alpha (glow) + gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, LLRender::BF_ONE, LLRender::BF_ONE); + + bool rebind = false; + LLGLSLShader* lastShader = current_shader; + if (!emissives.empty()) + { + light_enabled = true; + renderEmissives(emissives); + rebind = true; + } + + if (!pbr_emissives.empty()) + { + light_enabled = true; + renderPbrEmissives(pbr_emissives); + rebind = true; + } + + if (!rigged_emissives.empty()) + { + light_enabled = true; + renderRiggedEmissives(rigged_emissives); + rebind = true; + } + + if (!pbr_rigged_emissives.empty()) + { + light_enabled = true; + renderRiggedPbrEmissives(pbr_rigged_emissives); + rebind = true; + } + + // restore our alpha blend mode + gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); + + if (lastShader && rebind) + { + lastShader->bind(); + } + } + } + } + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLVertexBuffer::unbind(); + + if (!light_enabled) + { + gPipeline.enableLightsDynamic(); + } +} + +bool LLDrawPoolAlpha::uploadMatrixPalette(const LLDrawInfo& params) +{ + if (params.mAvatar.isNull()) + { + return false; + } + const LLVOAvatar::MatrixPaletteCache& mpc = params.mAvatar.get()->updateSkinInfoMatrixPalette(params.mSkinInfo); + U32 count = mpc.mMatrixPalette.size(); + + if (count == 0) + { + //skin info not loaded yet, don't render + return false; + } + + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX, + count, + false, + (GLfloat*)&(mpc.mGLMp[0])); + + return true; +} |