summaryrefslogtreecommitdiff
path: root/indra/newview/lldrawpoolalpha.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lldrawpoolalpha.cpp')
-rw-r--r--indra/newview/lldrawpoolalpha.cpp1870
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(&params, (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(&params);
- }
- else
- {
- pbr_rigged_emissives.push_back(&params);
- }
- }
- else
- {
- if (params.mGLTFMaterial.isNull())
- {
- emissives.push_back(&params);
- }
- else
- {
- pbr_emissives.push_back(&params);
- }
- }
- }
-
- 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(&params, (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(&params);
+ }
+ else
+ {
+ pbr_rigged_emissives.push_back(&params);
+ }
+ }
+ else
+ {
+ if (params.mGLTFMaterial.isNull())
+ {
+ emissives.push_back(&params);
+ }
+ else
+ {
+ pbr_emissives.push_back(&params);
+ }
+ }
+ }
+
+ 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;
+}