diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/lldrawpoolbump.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/newview/lldrawpoolbump.cpp')
-rw-r--r-- | indra/newview/lldrawpoolbump.cpp | 2608 |
1 files changed, 1304 insertions, 1304 deletions
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 16efb36ba6..28a1077177 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -1,1304 +1,1304 @@ -/** - * @file lldrawpoolbump.cpp - * @brief LLDrawPoolBump class implementation - * - * $LicenseInfo:firstyear=2003&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 "lldrawpoolbump.h" - -#include "llstl.h" -#include "llviewercontrol.h" -#include "lldir.h" -#include "m3math.h" -#include "m4math.h" -#include "v4math.h" -#include "llglheaders.h" -#include "llrender.h" - -#include "llcubemap.h" -#include "lldrawable.h" -#include "llface.h" -#include "llsky.h" -#include "llstartup.h" -#include "lltextureentry.h" -#include "llviewercamera.h" -#include "llviewertexturelist.h" -#include "pipeline.h" -#include "llspatialpartition.h" -#include "llviewershadermgr.h" -#include "llmodel.h" - -//#include "llimagebmp.h" -//#include "../tools/imdebug/imdebug.h" - -// static -LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT]; -LL::WorkQueue::weak_t LLBumpImageList::sMainQueue; -LL::WorkQueue::weak_t LLBumpImageList::sTexUpdateQueue; -LLRenderTarget LLBumpImageList::sRenderTarget; - -// static -U32 LLStandardBumpmap::sStandardBumpmapCount = 0; - -// static -LLBumpImageList gBumpImageList; - -const S32 STD_BUMP_LATEST_FILE_VERSION = 1; - -const U32 VERTEX_MASK_SHINY = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR; -const U32 VERTEX_MASK_BUMP = LLVertexBuffer::MAP_VERTEX |LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1; - -U32 LLDrawPoolBump::sVertexMask = VERTEX_MASK_SHINY; - - -static LLGLSLShader* shader = NULL; -static S32 cube_channel = -1; -static S32 diffuse_channel = -1; -static S32 bump_channel = -1; -static bool shiny = false; - -// Enabled after changing LLViewerTexture::mNeedsCreateTexture to an -// LLAtomicBool; this should work just fine, now. HB -#define LL_BUMPLIST_MULTITHREADED 1 - - -// static -void LLStandardBumpmap::shutdown() -{ - LLStandardBumpmap::destroyGL(); -} - -// static -void LLStandardBumpmap::restoreGL() -{ - addstandard(); -} - -// static -void LLStandardBumpmap::addstandard() -{ - if(!gTextureList.isInitialized()) - { - //Note: loading pre-configuration sometimes triggers this call. - //But it is safe to return here because bump images will be reloaded during initialization later. - return ; - } - - if (LLStartUp::getStartupState() < STATE_SEED_CAP_GRANTED) - { - // Not ready, need caps for images - return; - } - - // can't assert; we destroyGL and restoreGL a lot during *first* startup, which populates this list already, THEN we explicitly init the list as part of *normal* startup. Sigh. So clear the list every time before we (re-)add the standard bumpmaps. - //llassert( LLStandardBumpmap::sStandardBumpmapCount == 0 ); - clear(); - LL_INFOS() << "Adding standard bumpmaps." << LL_ENDL; - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("None"); // BE_NO_BUMP - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Brightness"); // BE_BRIGHTNESS - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Darkness"); // BE_DARKNESS - - std::string file_name = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "std_bump.ini" ); - LLFILE* file = LLFile::fopen( file_name, "rt" ); /*Flawfinder: ignore*/ - if( !file ) - { - LL_WARNS() << "Could not open std_bump <" << file_name << ">" << LL_ENDL; - return; - } - - S32 file_version = 0; - - S32 fields_read = fscanf( file, "LLStandardBumpmap version %d", &file_version ); - if( fields_read != 1 ) - { - LL_WARNS() << "Bad LLStandardBumpmap header" << LL_ENDL; - return; - } - - if( file_version > STD_BUMP_LATEST_FILE_VERSION ) - { - LL_WARNS() << "LLStandardBumpmap has newer version (" << file_version << ") than viewer (" << STD_BUMP_LATEST_FILE_VERSION << ")" << LL_ENDL; - return; - } - - while( !feof(file) && (LLStandardBumpmap::sStandardBumpmapCount < (U32)TEM_BUMPMAP_COUNT) ) - { - // *NOTE: This buffer size is hard coded into scanf() below. - char label[2048] = ""; /* Flawfinder: ignore */ - char bump_image_id[2048] = ""; /* Flawfinder: ignore */ - fields_read = fscanf( /* Flawfinder: ignore */ - file, "\n%2047s %2047s", label, bump_image_id); - if( EOF == fields_read ) - { - break; - } - if( fields_read != 2 ) - { - LL_WARNS() << "Bad LLStandardBumpmap entry" << LL_ENDL; - return; - } - -// LL_INFOS() << "Loading bumpmap: " << bump_image_id << " from viewerart" << LL_ENDL; - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label; - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage = - LLViewerTextureManager::getFetchedTexture(LLUUID(bump_image_id)); - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setBoostLevel(LLGLTexture::LOCAL) ; - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setLoadedCallback(LLBumpImageList::onSourceStandardLoaded, 0, true, false, NULL, NULL ); - gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->forceToSaveRawImage(0, 30.f) ; - LLStandardBumpmap::sStandardBumpmapCount++; - } - - fclose( file ); -} - -// static -void LLStandardBumpmap::clear() -{ - LL_INFOS() << "Clearing standard bumpmaps." << LL_ENDL; - for( U32 i = 0; i < LLStandardBumpmap::sStandardBumpmapCount; i++ ) - { - gStandardBumpmapList[i].mLabel.assign(""); - gStandardBumpmapList[i].mImage = NULL; - } - sStandardBumpmapCount = 0; -} - -// static -void LLStandardBumpmap::destroyGL() -{ - clear(); -} - - - -//////////////////////////////////////////////////////////////// - -LLDrawPoolBump::LLDrawPoolBump() -: LLRenderPass(LLDrawPool::POOL_BUMP) -{ - shiny = false; -} - - -void LLDrawPoolBump::prerender() -{ - mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT); -} - -// static -S32 LLDrawPoolBump::numBumpPasses() -{ - return 1; -} - - -//static -void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel) -{ - LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; - if( cube_map && !LLPipeline::sReflectionProbesEnabled ) - { - if (shader ) - { - LLMatrix4 mat; - mat.initRows(LLVector4(gGLModelView+0), - LLVector4(gGLModelView+4), - LLVector4(gGLModelView+8), - LLVector4(gGLModelView+12)); - LLVector3 vec = LLVector3(gShinyOrigin) * mat; - LLVector4 vec4(vec, gShinyOrigin.mV[3]); - shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV); - if (shader_level > 1) - { - cube_map->setMatrix(1); - // Make sure that texture coord generation happens for tex unit 1, as that's the one we use for - // the cube map in the one pass shiny shaders - cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); - cube_map->enableTexture(cube_channel); - diffuse_channel = shader->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); - } - else - { - cube_map->setMatrix(0); - cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); - diffuse_channel = -1; - cube_map->enable(cube_channel); - } - gGL.getTexUnit(cube_channel)->bind(cube_map); - gGL.getTexUnit(0)->activate(); - } - else - { - cube_channel = 0; - diffuse_channel = -1; - gGL.getTexUnit(0)->disable(); - cube_map->enable(0); - cube_map->setMatrix(0); - gGL.getTexUnit(0)->bind(cube_map); - } - } -} - -//static -void LLDrawPoolBump::unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel) -{ - LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; - if( cube_map && !LLPipeline::sReflectionProbesEnabled) - { - if (shader_level > 1) - { - shader->disableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); - - if (LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0) - { - if (diffuse_channel != 0) - { - shader->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); - } - } - } - // Moved below shader->disableTexture call to avoid false alarms from auto-re-enable of textures on stage 0 - // MAINT-755 - cube_map->disable(); - cube_map->restoreMatrix(); - } -} - -void LLDrawPoolBump::beginFullbrightShiny() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY); - - sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0; - - // Second pass: environment map - shader = &gDeferredFullbrightShinyProgram; - if (LLPipeline::sRenderingHUDs) - { - shader = &gHUDFullbrightShinyProgram; - } - - if (mRigged) - { - llassert(shader->mRiggedVariant); - shader = shader->mRiggedVariant; - } - - // bind exposure map so fullbright shader can cancel out exposure - S32 channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP); - if (channel > -1) - { - gGL.getTexUnit(channel)->bind(&gPipeline.mExposureMap); - } - - LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; - - if (cube_map && !LLPipeline::sReflectionProbesEnabled) - { - // Make sure that texture coord generation happens for tex unit 1, as that's the one we use for - // the cube map in the one pass shiny shaders - gGL.getTexUnit(1)->disable(); - cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); - cube_map->enableTexture(cube_channel); - diffuse_channel = shader->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); - - gGL.getTexUnit(cube_channel)->bind(cube_map); - gGL.getTexUnit(0)->activate(); - } - - { - LLMatrix4 mat; - mat.initRows(LLVector4(gGLModelView+0), - LLVector4(gGLModelView+4), - LLVector4(gGLModelView+8), - LLVector4(gGLModelView+12)); - shader->bind(); - - LLVector3 vec = LLVector3(gShinyOrigin) * mat; - LLVector4 vec4(vec, gShinyOrigin.mV[3]); - shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV); - - if (LLPipeline::sReflectionProbesEnabled) - { - gPipeline.bindReflectionProbes(*shader); - } - else - { - gPipeline.setEnvMat(*shader); - } - } - - if (mShaderLevel > 1) - { //indexed texture rendering, channel 0 is always diffuse - diffuse_channel = 0; - } - - shiny = true; -} - -void LLDrawPoolBump::renderFullbrightShiny() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY); - - { - LLGLEnable blend_enable(GL_BLEND); - - if (mShaderLevel > 1) - { - if (mRigged) - { - LLRenderPass::pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY_RIGGED, true, true); - } - else - { - LLRenderPass::pushBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY, true, true); - } - } - else - { - if (mRigged) - { - LLRenderPass::pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY_RIGGED); - } - else - { - LLRenderPass::pushBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY); - } - } - } -} - -void LLDrawPoolBump::endFullbrightShiny() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY); - - LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; - if( cube_map && !LLPipeline::sReflectionProbesEnabled ) - { - cube_map->disable(); - if (shader->mFeatures.hasReflectionProbes) - { - gPipeline.unbindReflectionProbes(*shader); - } - shader->unbind(); - } - - diffuse_channel = -1; - cube_channel = 0; - shiny = false; -} - -void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, bool texture = true) -{ - LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[type]; - - for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) - { - LLDrawInfo& params = **k; - - applyModelMatrix(params); - - params.mVertexBuffer->setBuffer(); - params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); - } -} - - -// static -bool LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel) -{ - U8 bump_code = params.mBump; - - return bindBumpMap(bump_code, params.mTexture, channel); -} - -//static -bool LLDrawPoolBump::bindBumpMap(LLFace* face, S32 channel) -{ - const LLTextureEntry* te = face->getTextureEntry(); - if (te) - { - U8 bump_code = te->getBumpmap(); - return bindBumpMap(bump_code, face->getTexture(), channel); - } - - return false; -} - -//static -bool LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, S32 channel) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; - //Note: texture atlas does not support bump texture now. - LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(texture) ; - if(!tex) - { - //if the texture is not a fetched texture - return false; - } - - LLViewerTexture* bump = NULL; - - switch( bump_code ) - { - case BE_NO_BUMP: - break; - case BE_BRIGHTNESS: - case BE_DARKNESS: - bump = gBumpImageList.getBrightnessDarknessImage( tex, bump_code ); - break; - - default: - if( bump_code < LLStandardBumpmap::sStandardBumpmapCount ) - { - bump = gStandardBumpmapList[bump_code].mImage; - gBumpImageList.addTextureStats(bump_code, tex->getID(), tex->getMaxVirtualSize()); - } - break; - } - - if (bump) - { - if (channel == -2) - { - gGL.getTexUnit(1)->bindFast(bump); - gGL.getTexUnit(0)->bindFast(bump); - } - else - { - // NOTE: do not use bindFast here (see SL-16222) - gGL.getTexUnit(channel)->bind(bump); - } - - return true; - } - - return false; -} - -//static -void LLDrawPoolBump::beginBump() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP); - sVertexMask = VERTEX_MASK_BUMP; - // Optional second pass: emboss bump map - stop_glerror(); - - shader = &gObjectBumpProgram; - - if (mRigged) - { - llassert(shader->mRiggedVariant); - shader = shader->mRiggedVariant; - } - - shader->bind(); - - gGL.setSceneBlendType(LLRender::BT_MULT_X2); - stop_glerror(); -} - -//static -void LLDrawPoolBump::renderBump(U32 pass) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP); - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LEQUAL); - LLGLEnable blend(GL_BLEND); - gGL.diffuseColor4f(1,1,1,1); - /// Get rid of z-fighting with non-bump pass. - LLGLEnable polyOffset(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - pushBumpBatches(pass); -} - -//static -void LLDrawPoolBump::endBump(U32 pass) -{ - LLGLSLShader::unbind(); - - gGL.setSceneBlendType(LLRender::BT_ALPHA); -} - -S32 LLDrawPoolBump::getNumDeferredPasses() -{ - return 1; -} - -void LLDrawPoolBump::renderDeferred(S32 pass) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP); - - shiny = true; - for (int i = 0; i < 2; ++i) - { - bool rigged = i == 1; - gDeferredBumpProgram.bind(rigged); - diffuse_channel = LLGLSLShader::sCurBoundShaderPtr->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); - bump_channel = LLGLSLShader::sCurBoundShaderPtr->enableTexture(LLViewerShaderMgr::BUMP_MAP); - gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE); - gGL.getTexUnit(bump_channel)->unbind(LLTexUnit::TT_TEXTURE); - - U32 type = rigged ? LLRenderPass::PASS_BUMP_RIGGED : LLRenderPass::PASS_BUMP; - LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type); - LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type); - - LLVOAvatar* avatar = nullptr; - U64 skin = 0; - - for (LLCullResult::drawinfo_iterator i = begin; i != end; ) - { - LLDrawInfo& params = **i; - - LLCullResult::increment_iterator(i, end); - - LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(params.mAlphaMaskCutoff); - LLDrawPoolBump::bindBumpMap(params, bump_channel); - - if (rigged) - { - if (avatar != params.mAvatar || skin != params.mSkinInfo->mHash) - { - uploadMatrixPalette(params); - avatar = params.mAvatar; - skin = params.mSkinInfo->mHash; - } - pushBumpBatch(params, true, false); - } - else - { - pushBumpBatch(params, true, false); - } - } - - LLGLSLShader::sCurBoundShaderPtr->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); - LLGLSLShader::sCurBoundShaderPtr->disableTexture(LLViewerShaderMgr::BUMP_MAP); - LLGLSLShader::sCurBoundShaderPtr->unbind(); - gGL.getTexUnit(0)->activate(); - } - - shiny = false; -} - - -void LLDrawPoolBump::renderPostDeferred(S32 pass) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; - - S32 num_passes = LLPipeline::sRenderingHUDs ? 1 : 2; // skip rigged pass when rendering HUDs - - for (int i = 0; i < num_passes; ++i) - { // two passes -- static and rigged - mRigged = (i == 1); - - // render shiny - beginFullbrightShiny(); - renderFullbrightShiny(); - endFullbrightShiny(); - - //render bump - beginBump(); - renderBump(LLRenderPass::PASS_POST_BUMP); - endBump(); - } -} - - -//////////////////////////////////////////////////////////////// -// List of bump-maps created from other textures. - - -//const LLUUID TEST_BUMP_ID("3d33eaf2-459c-6f97-fd76-5fce3fc29447"); - -void LLBumpImageList::init() -{ - llassert( mBrightnessEntries.size() == 0 ); - llassert( mDarknessEntries.size() == 0 ); - - LLStandardBumpmap::restoreGL(); - sMainQueue = LL::WorkQueue::getInstance("mainloop"); - sTexUpdateQueue = LL::WorkQueue::getInstance("LLImageGL"); // Share work queue with tex loader. -} - -void LLBumpImageList::clear() -{ - LL_INFOS() << "Clearing dynamic bumpmaps." << LL_ENDL; - // these will be re-populated on-demand - mBrightnessEntries.clear(); - mDarknessEntries.clear(); - - sRenderTarget.release(); - - LLStandardBumpmap::clear(); -} - -void LLBumpImageList::shutdown() -{ - clear(); - LLStandardBumpmap::shutdown(); -} - -void LLBumpImageList::destroyGL() -{ - clear(); - LLStandardBumpmap::destroyGL(); -} - -void LLBumpImageList::restoreGL() -{ - if(!gTextureList.isInitialized()) - { - //safe to return here because bump images will be reloaded during initialization later. - return ; - } - - LLStandardBumpmap::restoreGL(); - // Images will be recreated as they are needed. -} - - -LLBumpImageList::~LLBumpImageList() -{ - // Shutdown should have already been called. - llassert( mBrightnessEntries.size() == 0 ); - llassert( mDarknessEntries.size() == 0 ); -} - - -// Note: Does nothing for entries in gStandardBumpmapList that are not actually standard bump images (e.g. none, brightness, and darkness) -void LLBumpImageList::addTextureStats(U8 bump, const LLUUID& base_image_id, F32 virtual_size) -{ - bump &= TEM_BUMP_MASK; - LLViewerFetchedTexture* bump_image = gStandardBumpmapList[bump].mImage; - if( bump_image ) - { - bump_image->addTextureStats(virtual_size); - } -} - - -void LLBumpImageList::updateImages() -{ - for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); ) - { - bump_image_map_t::iterator curiter = iter++; - LLViewerTexture* image = curiter->second; - if( image ) - { - bool destroy = true; - if( image->hasGLTexture()) - { - if( image->getBoundRecently() ) - { - destroy = false; - } - else - { - image->destroyGLTexture(); - } - } - - if( destroy ) - { - //LL_INFOS() << "*** Destroying bright " << (void*)image << LL_ENDL; - mBrightnessEntries.erase(curiter); // deletes the image thanks to reference counting - } - } - } - - for (bump_image_map_t::iterator iter = mDarknessEntries.begin(); iter != mDarknessEntries.end(); ) - { - bump_image_map_t::iterator curiter = iter++; - LLViewerTexture* image = curiter->second; - if( image ) - { - bool destroy = true; - if( image->hasGLTexture()) - { - if( image->getBoundRecently() ) - { - destroy = false; - } - else - { - image->destroyGLTexture(); - } - } - - if( destroy ) - { - //LL_INFOS() << "*** Destroying dark " << (void*)image << LL_ENDL;; - mDarknessEntries.erase(curiter); // deletes the image thanks to reference counting - } - } - } -} - - -// Note: the caller SHOULD NOT keep the pointer that this function returns. It may be updated as more data arrives. -LLViewerTexture* LLBumpImageList::getBrightnessDarknessImage(LLViewerFetchedTexture* src_image, U8 bump_code ) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; - llassert( (bump_code == BE_BRIGHTNESS) || (bump_code == BE_DARKNESS) ); - - LLViewerTexture* bump = NULL; - - bump_image_map_t* entries_list = NULL; - void (*callback_func)( bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata ) = NULL; - - switch( bump_code ) - { - case BE_BRIGHTNESS: - entries_list = &mBrightnessEntries; - callback_func = LLBumpImageList::onSourceBrightnessLoaded; - break; - case BE_DARKNESS: - entries_list = &mDarknessEntries; - callback_func = LLBumpImageList::onSourceDarknessLoaded; - break; - default: - llassert(0); - return NULL; - } - - bump_image_map_t::iterator iter = entries_list->find(src_image->getID()); - if (iter != entries_list->end() && iter->second.notNull()) - { - bump = iter->second; - } - else - { - (*entries_list)[src_image->getID()] = LLViewerTextureManager::getLocalTexture( true ); - bump = (*entries_list)[src_image->getID()]; // In case callback was called immediately and replaced the image - } - - if (!src_image->hasCallbacks()) - { //if image has no callbacks but resolutions don't match, trigger raw image loaded callback again - if (src_image->getWidth() != bump->getWidth() || - src_image->getHeight() != bump->getHeight())// || - //(LLPipeline::sRenderDeferred && bump->getComponents() != 4)) - { - src_image->setBoostLevel(LLGLTexture::BOOST_BUMP) ; - src_image->setLoadedCallback( callback_func, 0, true, false, new LLUUID(src_image->getID()), NULL ); - src_image->forceToSaveRawImage(0) ; - } - } - - return bump; -} - - -// static -void LLBumpImageList::onSourceBrightnessLoaded( bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata ) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; - LLUUID* source_asset_id = (LLUUID*)userdata; - LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_BRIGHTNESS ); - if( final ) - { - delete source_asset_id; - } -} - -// static -void LLBumpImageList::onSourceDarknessLoaded( bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata ) -{ - LLUUID* source_asset_id = (LLUUID*)userdata; - LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_DARKNESS ); - if( final ) - { - delete source_asset_id; - } -} - -void LLBumpImageList::onSourceStandardLoaded( bool success, LLViewerFetchedTexture* src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata) -{ - if (success && LLPipeline::sRenderDeferred) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; - LLPointer<LLImageRaw> nrm_image = new LLImageRaw(src->getWidth(), src->getHeight(), 4); - { - generateNormalMapFromAlpha(src, nrm_image); - } - src_vi->setExplicitFormat(GL_RGBA, GL_RGBA); - { - src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image); - } - } -} - -void LLBumpImageList::generateNormalMapFromAlpha(LLImageRaw* src, LLImageRaw* nrm_image) -{ - LLImageDataSharedLock lockIn(src); - LLImageDataLock lockOut(nrm_image); - - U8* nrm_data = nrm_image->getData(); - S32 resX = src->getWidth(); - S32 resY = src->getHeight(); - - const U8* src_data = src->getData(); - - S32 src_cmp = src->getComponents(); - - F32 norm_scale = gSavedSettings.getF32("RenderNormalMapScale"); - - U32 idx = 0; - //generate normal map from pseudo-heightfield - for (S32 j = 0; j < resY; ++j) - { - for (S32 i = 0; i < resX; ++i) - { - S32 rX = (i+1)%resX; - S32 rY = (j+1)%resY; - S32 lX = (i-1)%resX; - S32 lY = (j-1)%resY; - - if (lX < 0) - { - lX += resX; - } - if (lY < 0) - { - lY += resY; - } - - F32 cH = (F32) src_data[(j*resX+i)*src_cmp+src_cmp-1]; - - LLVector3 right = LLVector3(norm_scale, 0, (F32) src_data[(j*resX+rX)*src_cmp+src_cmp-1]-cH); - LLVector3 left = LLVector3(-norm_scale, 0, (F32) src_data[(j*resX+lX)*src_cmp+src_cmp-1]-cH); - LLVector3 up = LLVector3(0, -norm_scale, (F32) src_data[(lY*resX+i)*src_cmp+src_cmp-1]-cH); - LLVector3 down = LLVector3(0, norm_scale, (F32) src_data[(rY*resX+i)*src_cmp+src_cmp-1]-cH); - - LLVector3 norm = right%down + down%left + left%up + up%right; - - norm.normVec(); - - norm *= 0.5f; - norm += LLVector3(0.5f,0.5f,0.5f); - - idx = (j*resX+i)*4; - nrm_data[idx+0]= (U8) (norm.mV[0]*255); - nrm_data[idx+1]= (U8) (norm.mV[1]*255); - nrm_data[idx+2]= (U8) (norm.mV[2]*255); - nrm_data[idx+3]= src_data[(j*resX+i)*src_cmp+src_cmp-1]; - } - } -} - -// static -void LLBumpImageList::onSourceLoaded( bool success, LLViewerTexture *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump_code ) -{ - LL_PROFILE_ZONE_SCOPED; - - if( success ) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; - - LLImageDataSharedLock lock(src); - - bump_image_map_t& entries_list(bump_code == BE_BRIGHTNESS ? gBumpImageList.mBrightnessEntries : gBumpImageList.mDarknessEntries ); - bump_image_map_t::iterator iter = entries_list.find(source_asset_id); - - { - if (iter == entries_list.end() || - iter->second.isNull() || - iter->second->getWidth() != src->getWidth() || - iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution - { //make sure an entry exists for this image - entries_list[src_vi->getID()] = LLViewerTextureManager::getLocalTexture(true); - iter = entries_list.find(src_vi->getID()); - } - } - - if (iter->second->getWidth() != src->getWidth() || - iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution - { - LLPointer<LLImageRaw> dst_image = new LLImageRaw(src->getWidth(), src->getHeight(), 1); - U8* dst_data = dst_image->getData(); - S32 dst_data_size = dst_image->getDataSize(); - - const U8* src_data = src->getData(); - S32 src_data_size = src->getDataSize(); - - S32 src_components = src->getComponents(); - - // Convert to luminance and then scale and bias that to get ready for - // embossed bump mapping. (0-255 maps to 127-255) - - // Convert to fixed point so we don't have to worry about precision/clamping. - const S32 FIXED_PT = 8; - const S32 R_WEIGHT = S32(0.2995f * (1<<FIXED_PT)); - const S32 G_WEIGHT = S32(0.5875f * (1<<FIXED_PT)); - const S32 B_WEIGHT = S32(0.1145f * (1<<FIXED_PT)); - - S32 minimum = 255; - S32 maximum = 0; - - switch( src_components ) - { - case 1: - case 2: - { - if( src_data_size == dst_data_size * src_components ) - { - for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components ) - { - dst_data[i] = src_data[j]; - if( dst_data[i] < minimum ) - { - minimum = dst_data[i]; - } - if( dst_data[i] > maximum ) - { - maximum = dst_data[i]; - } - } - } - else - { - llassert(0); - dst_image->clear(); - } - } - break; - case 3: - case 4: - { - if( src_data_size == dst_data_size * src_components ) - { - for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components ) - { - // RGB to luminance - dst_data[i] = (R_WEIGHT * src_data[j] + G_WEIGHT * src_data[j+1] + B_WEIGHT * src_data[j+2]) >> FIXED_PT; - //llassert( dst_data[i] <= 255 );true because it's 8bit - if( dst_data[i] < minimum ) - { - minimum = dst_data[i]; - } - if( dst_data[i] > maximum ) - { - maximum = dst_data[i]; - } - } - } - else - { - llassert(0); - dst_image->clear(); - } - } - break; - default: - llassert(0); - dst_image->clear(); - break; - } - - if( maximum > minimum ) - { - U8 bias_and_scale_lut[256]; - F32 twice_one_over_range = 2.f / (maximum - minimum); - S32 i; - - const F32 ARTIFICIAL_SCALE = 2.f; // Advantage: exaggerates the effect in midrange. Disadvantage: clamps at the extremes. - if (BE_DARKNESS == bump_code) - { - for( i = minimum; i <= maximum; i++ ) - { - F32 minus_one_to_one = F32(maximum - i) * twice_one_over_range - 1.f; - bias_and_scale_lut[i] = llclampb(ll_round(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128)); - } - } - else - { - for( i = minimum; i <= maximum; i++ ) - { - F32 minus_one_to_one = F32(i - minimum) * twice_one_over_range - 1.f; - bias_and_scale_lut[i] = llclampb(ll_round(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128)); - } - } - - for( i = 0; i < dst_data_size; i++ ) - { - dst_data[i] = bias_and_scale_lut[dst_data[i]]; - } - } - - //--------------------------------------------------- - // immediately assign bump to a smart pointer in case some local smart pointer - // accidentally releases it. - LLPointer<LLViewerTexture> bump = iter->second; - - if (!LLPipeline::sRenderDeferred) - { - bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA); - -#if LL_BUMPLIST_MULTITHREADED - auto tex_queue = LLImageGLThread::sEnabledTextures ? sTexUpdateQueue.lock() : nullptr; - - if (tex_queue) - { //dispatch creation to background thread - LLImageRaw* dst_ptr = dst_image; - LLViewerTexture* bump_ptr = bump; - dst_ptr->ref(); - bump_ptr->ref(); - tex_queue->post( - [=]() - { - LL_PROFILE_ZONE_NAMED("bil - create texture"); - bump_ptr->createGLTexture(0, dst_ptr); - bump_ptr->unref(); - dst_ptr->unref(); - }); - - } - else -#endif - { - bump->createGLTexture(0, dst_image); - } - } - else - { //convert to normal map - LL_PROFILE_ZONE_NAMED("bil - create normal map"); - LLImageGL* img = bump->getGLTexture(); - LLImageRaw* dst_ptr = dst_image.get(); - LLGLTexture* bump_ptr = bump.get(); - - dst_ptr->ref(); - img->ref(); - bump_ptr->ref(); - auto create_func = [=]() - { - img->setUseMipMaps(true); - // upload dst_image to GPU (greyscale in red channel) - img->setExplicitFormat(GL_RED, GL_RED); - - bump_ptr->createGLTexture(0, dst_ptr); - dst_ptr->unref(); - }; - - auto generate_func = [=]() - { - // Allocate an empty RGBA texture at "tex_name" the same size as bump - // Note: bump will still point at GPU copy of dst_image - bump_ptr->setExplicitFormat(GL_RGBA, GL_RGBA); - LLGLuint tex_name; - img->createGLTexture(0, nullptr, false, 0, true, &tex_name); - - // point render target at empty buffer - sRenderTarget.setColorAttachment(img, tex_name); - - // generate normal map in empty texture - { - sRenderTarget.bindTarget(); - - LLGLDepthTest depth(GL_FALSE); - LLGLDisable cull(GL_CULL_FACE); - LLGLDisable blend(GL_BLEND); - gGL.setColorMask(true, true); - - gNormalMapGenProgram.bind(); - - static LLStaticHashedString sNormScale("norm_scale"); - static LLStaticHashedString sStepX("stepX"); - static LLStaticHashedString sStepY("stepY"); - - gNormalMapGenProgram.uniform1f(sNormScale, gSavedSettings.getF32("RenderNormalMapScale")); - gNormalMapGenProgram.uniform1f(sStepX, 1.f / bump_ptr->getWidth()); - gNormalMapGenProgram.uniform1f(sStepY, 1.f / bump_ptr->getHeight()); - - gGL.getTexUnit(0)->bind(bump_ptr); - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(0, 0); - gGL.vertex2f(0, 0); - - gGL.texCoord2f(0, 1); - gGL.vertex2f(0, 1); - - gGL.texCoord2f(1, 0); - gGL.vertex2f(1, 0); - - gGL.texCoord2f(1, 1); - gGL.vertex2f(1, 1); - - gGL.end(); - - gGL.flush(); - - gNormalMapGenProgram.unbind(); - - sRenderTarget.flush(); - sRenderTarget.releaseColorAttachment(); - } - - // point bump at normal map and free gpu copy of dst_image - img->syncTexName(tex_name); - - // generate mipmap - gGL.getTexUnit(0)->bind(img); - glGenerateMipmap(GL_TEXTURE_2D); - gGL.getTexUnit(0)->disable(); - - bump_ptr->unref(); - img->unref(); - }; - -#if LL_BUMPLIST_MULTITHREADED - auto main_queue = LLImageGLThread::sEnabledTextures ? sMainQueue.lock() : nullptr; - - if (main_queue) - { //dispatch texture upload to background thread, issue GPU commands to generate normal map on main thread - main_queue->postTo( - sTexUpdateQueue, - create_func, - generate_func); - } - else -#endif - { // immediate upload texture and generate normal map - create_func(); - generate_func(); - } - - - } - - iter->second = bump; // derefs (and deletes) old image - //--------------------------------------------------- - } - } -} - -void LLDrawPoolBump::pushBumpBatches(U32 type) -{ - LLVOAvatar* avatar = nullptr; - U64 skin = 0; - - if (mRigged) - { // nudge type enum and include skinweights for rigged pass - type += 1; - } - - LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type); - LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type); - - for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i) - { - LLDrawInfo& params = **i; - - if (LLDrawPoolBump::bindBumpMap(params)) - { - if (mRigged) - { - if (avatar != params.mAvatar || skin != params.mSkinInfo->mHash) - { - if (uploadMatrixPalette(params)) - { - avatar = params.mAvatar; - skin = params.mSkinInfo->mHash; - } - else - { - continue; - } - } - } - pushBumpBatch(params, false); - } - } -} - -void LLRenderPass::pushBumpBatch(LLDrawInfo& params, bool texture, bool batch_textures) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; - applyModelMatrix(params); - - bool tex_setup = false; - - if (batch_textures && params.mTextureList.size() > 1) - { - for (U32 i = 0; i < params.mTextureList.size(); ++i) - { - if (params.mTextureList[i].notNull()) - { - gGL.getTexUnit(i)->bindFast(params.mTextureList[i]); - } - } - } - else - { //not batching textures or batch has only 1 texture -- might need a texture matrix - if (params.mTextureMatrix) - { - if (shiny) - { - gGL.getTexUnit(0)->activate(); - gGL.matrixMode(LLRender::MM_TEXTURE); - } - else - { - gGL.getTexUnit(0)->activate(); - gGL.matrixMode(LLRender::MM_TEXTURE); - gGL.loadMatrix((GLfloat*) params.mTextureMatrix->mMatrix); - gPipeline.mTextureMatrixOps++; - } - - gGL.loadMatrix((GLfloat*) params.mTextureMatrix->mMatrix); - gPipeline.mTextureMatrixOps++; - - tex_setup = true; - } - - if (shiny && mShaderLevel > 1 && texture) - { - if (params.mTexture.notNull()) - { - gGL.getTexUnit(diffuse_channel)->bindFast(params.mTexture); - } - else - { - gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE); - } - } - } - - params.mVertexBuffer->setBuffer(); - params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); - - if (tex_setup) - { - if (shiny) - { - gGL.getTexUnit(0)->activate(); - } - else - { - gGL.getTexUnit(0)->activate(); - gGL.matrixMode(LLRender::MM_TEXTURE); - } - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - } -} - +/**
+ * @file lldrawpoolbump.cpp
+ * @brief LLDrawPoolBump class implementation
+ *
+ * $LicenseInfo:firstyear=2003&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 "lldrawpoolbump.h"
+
+#include "llstl.h"
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "v4math.h"
+#include "llglheaders.h"
+#include "llrender.h"
+
+#include "llcubemap.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llstartup.h"
+#include "lltextureentry.h"
+#include "llviewercamera.h"
+#include "llviewertexturelist.h"
+#include "pipeline.h"
+#include "llspatialpartition.h"
+#include "llviewershadermgr.h"
+#include "llmodel.h"
+
+//#include "llimagebmp.h"
+//#include "../tools/imdebug/imdebug.h"
+
+// static
+LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT];
+LL::WorkQueue::weak_t LLBumpImageList::sMainQueue;
+LL::WorkQueue::weak_t LLBumpImageList::sTexUpdateQueue;
+LLRenderTarget LLBumpImageList::sRenderTarget;
+
+// static
+U32 LLStandardBumpmap::sStandardBumpmapCount = 0;
+
+// static
+LLBumpImageList gBumpImageList;
+
+const S32 STD_BUMP_LATEST_FILE_VERSION = 1;
+
+const U32 VERTEX_MASK_SHINY = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR;
+const U32 VERTEX_MASK_BUMP = LLVertexBuffer::MAP_VERTEX |LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1;
+
+U32 LLDrawPoolBump::sVertexMask = VERTEX_MASK_SHINY;
+
+
+static LLGLSLShader* shader = NULL;
+static S32 cube_channel = -1;
+static S32 diffuse_channel = -1;
+static S32 bump_channel = -1;
+static bool shiny = false;
+
+// Enabled after changing LLViewerTexture::mNeedsCreateTexture to an
+// LLAtomicBool; this should work just fine, now. HB
+#define LL_BUMPLIST_MULTITHREADED 1
+
+
+// static
+void LLStandardBumpmap::shutdown()
+{
+ LLStandardBumpmap::destroyGL();
+}
+
+// static
+void LLStandardBumpmap::restoreGL()
+{
+ addstandard();
+}
+
+// static
+void LLStandardBumpmap::addstandard()
+{
+ if(!gTextureList.isInitialized())
+ {
+ //Note: loading pre-configuration sometimes triggers this call.
+ //But it is safe to return here because bump images will be reloaded during initialization later.
+ return ;
+ }
+
+ if (LLStartUp::getStartupState() < STATE_SEED_CAP_GRANTED)
+ {
+ // Not ready, need caps for images
+ return;
+ }
+
+ // can't assert; we destroyGL and restoreGL a lot during *first* startup, which populates this list already, THEN we explicitly init the list as part of *normal* startup. Sigh. So clear the list every time before we (re-)add the standard bumpmaps.
+ //llassert( LLStandardBumpmap::sStandardBumpmapCount == 0 );
+ clear();
+ LL_INFOS() << "Adding standard bumpmaps." << LL_ENDL;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("None"); // BE_NO_BUMP
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Brightness"); // BE_BRIGHTNESS
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Darkness"); // BE_DARKNESS
+
+ std::string file_name = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "std_bump.ini" );
+ LLFILE* file = LLFile::fopen( file_name, "rt" ); /*Flawfinder: ignore*/
+ if( !file )
+ {
+ LL_WARNS() << "Could not open std_bump <" << file_name << ">" << LL_ENDL;
+ return;
+ }
+
+ S32 file_version = 0;
+
+ S32 fields_read = fscanf( file, "LLStandardBumpmap version %d", &file_version );
+ if( fields_read != 1 )
+ {
+ LL_WARNS() << "Bad LLStandardBumpmap header" << LL_ENDL;
+ return;
+ }
+
+ if( file_version > STD_BUMP_LATEST_FILE_VERSION )
+ {
+ LL_WARNS() << "LLStandardBumpmap has newer version (" << file_version << ") than viewer (" << STD_BUMP_LATEST_FILE_VERSION << ")" << LL_ENDL;
+ return;
+ }
+
+ while( !feof(file) && (LLStandardBumpmap::sStandardBumpmapCount < (U32)TEM_BUMPMAP_COUNT) )
+ {
+ // *NOTE: This buffer size is hard coded into scanf() below.
+ char label[2048] = ""; /* Flawfinder: ignore */
+ char bump_image_id[2048] = ""; /* Flawfinder: ignore */
+ fields_read = fscanf( /* Flawfinder: ignore */
+ file, "\n%2047s %2047s", label, bump_image_id);
+ if( EOF == fields_read )
+ {
+ break;
+ }
+ if( fields_read != 2 )
+ {
+ LL_WARNS() << "Bad LLStandardBumpmap entry" << LL_ENDL;
+ return;
+ }
+
+// LL_INFOS() << "Loading bumpmap: " << bump_image_id << " from viewerart" << LL_ENDL;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage =
+ LLViewerTextureManager::getFetchedTexture(LLUUID(bump_image_id));
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setBoostLevel(LLGLTexture::LOCAL) ;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setLoadedCallback(LLBumpImageList::onSourceStandardLoaded, 0, true, false, NULL, NULL );
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->forceToSaveRawImage(0, 30.f) ;
+ LLStandardBumpmap::sStandardBumpmapCount++;
+ }
+
+ fclose( file );
+}
+
+// static
+void LLStandardBumpmap::clear()
+{
+ LL_INFOS() << "Clearing standard bumpmaps." << LL_ENDL;
+ for( U32 i = 0; i < LLStandardBumpmap::sStandardBumpmapCount; i++ )
+ {
+ gStandardBumpmapList[i].mLabel.assign("");
+ gStandardBumpmapList[i].mImage = NULL;
+ }
+ sStandardBumpmapCount = 0;
+}
+
+// static
+void LLStandardBumpmap::destroyGL()
+{
+ clear();
+}
+
+
+
+////////////////////////////////////////////////////////////////
+
+LLDrawPoolBump::LLDrawPoolBump()
+: LLRenderPass(LLDrawPool::POOL_BUMP)
+{
+ shiny = false;
+}
+
+
+void LLDrawPoolBump::prerender()
+{
+ mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT);
+}
+
+// static
+S32 LLDrawPoolBump::numBumpPasses()
+{
+ return 1;
+}
+
+
+//static
+void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel)
+{
+ LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
+ if( cube_map && !LLPipeline::sReflectionProbesEnabled )
+ {
+ if (shader )
+ {
+ LLMatrix4 mat;
+ mat.initRows(LLVector4(gGLModelView+0),
+ LLVector4(gGLModelView+4),
+ LLVector4(gGLModelView+8),
+ LLVector4(gGLModelView+12));
+ LLVector3 vec = LLVector3(gShinyOrigin) * mat;
+ LLVector4 vec4(vec, gShinyOrigin.mV[3]);
+ shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);
+ if (shader_level > 1)
+ {
+ cube_map->setMatrix(1);
+ // Make sure that texture coord generation happens for tex unit 1, as that's the one we use for
+ // the cube map in the one pass shiny shaders
+ cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
+ cube_map->enableTexture(cube_channel);
+ diffuse_channel = shader->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+ }
+ else
+ {
+ cube_map->setMatrix(0);
+ cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
+ diffuse_channel = -1;
+ cube_map->enable(cube_channel);
+ }
+ gGL.getTexUnit(cube_channel)->bind(cube_map);
+ gGL.getTexUnit(0)->activate();
+ }
+ else
+ {
+ cube_channel = 0;
+ diffuse_channel = -1;
+ gGL.getTexUnit(0)->disable();
+ cube_map->enable(0);
+ cube_map->setMatrix(0);
+ gGL.getTexUnit(0)->bind(cube_map);
+ }
+ }
+}
+
+//static
+void LLDrawPoolBump::unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel)
+{
+ LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
+ if( cube_map && !LLPipeline::sReflectionProbesEnabled)
+ {
+ if (shader_level > 1)
+ {
+ shader->disableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
+
+ if (LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0)
+ {
+ if (diffuse_channel != 0)
+ {
+ shader->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+ }
+ }
+ }
+ // Moved below shader->disableTexture call to avoid false alarms from auto-re-enable of textures on stage 0
+ // MAINT-755
+ cube_map->disable();
+ cube_map->restoreMatrix();
+ }
+}
+
+void LLDrawPoolBump::beginFullbrightShiny()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
+
+ sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0;
+
+ // Second pass: environment map
+ shader = &gDeferredFullbrightShinyProgram;
+ if (LLPipeline::sRenderingHUDs)
+ {
+ shader = &gHUDFullbrightShinyProgram;
+ }
+
+ if (mRigged)
+ {
+ llassert(shader->mRiggedVariant);
+ shader = shader->mRiggedVariant;
+ }
+
+ // bind exposure map so fullbright shader can cancel out exposure
+ S32 channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP);
+ if (channel > -1)
+ {
+ gGL.getTexUnit(channel)->bind(&gPipeline.mExposureMap);
+ }
+
+ LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
+
+ if (cube_map && !LLPipeline::sReflectionProbesEnabled)
+ {
+ // Make sure that texture coord generation happens for tex unit 1, as that's the one we use for
+ // the cube map in the one pass shiny shaders
+ gGL.getTexUnit(1)->disable();
+ cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
+ cube_map->enableTexture(cube_channel);
+ diffuse_channel = shader->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+
+ gGL.getTexUnit(cube_channel)->bind(cube_map);
+ gGL.getTexUnit(0)->activate();
+ }
+
+ {
+ LLMatrix4 mat;
+ mat.initRows(LLVector4(gGLModelView+0),
+ LLVector4(gGLModelView+4),
+ LLVector4(gGLModelView+8),
+ LLVector4(gGLModelView+12));
+ shader->bind();
+
+ LLVector3 vec = LLVector3(gShinyOrigin) * mat;
+ LLVector4 vec4(vec, gShinyOrigin.mV[3]);
+ shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);
+
+ if (LLPipeline::sReflectionProbesEnabled)
+ {
+ gPipeline.bindReflectionProbes(*shader);
+ }
+ else
+ {
+ gPipeline.setEnvMat(*shader);
+ }
+ }
+
+ if (mShaderLevel > 1)
+ { //indexed texture rendering, channel 0 is always diffuse
+ diffuse_channel = 0;
+ }
+
+ shiny = true;
+}
+
+void LLDrawPoolBump::renderFullbrightShiny()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
+
+ {
+ LLGLEnable blend_enable(GL_BLEND);
+
+ if (mShaderLevel > 1)
+ {
+ if (mRigged)
+ {
+ LLRenderPass::pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY_RIGGED, true, true);
+ }
+ else
+ {
+ LLRenderPass::pushBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY, true, true);
+ }
+ }
+ else
+ {
+ if (mRigged)
+ {
+ LLRenderPass::pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY_RIGGED);
+ }
+ else
+ {
+ LLRenderPass::pushBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY);
+ }
+ }
+ }
+}
+
+void LLDrawPoolBump::endFullbrightShiny()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
+
+ LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
+ if( cube_map && !LLPipeline::sReflectionProbesEnabled )
+ {
+ cube_map->disable();
+ if (shader->mFeatures.hasReflectionProbes)
+ {
+ gPipeline.unbindReflectionProbes(*shader);
+ }
+ shader->unbind();
+ }
+
+ diffuse_channel = -1;
+ cube_channel = 0;
+ shiny = false;
+}
+
+void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, bool texture = true)
+{
+ LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[type];
+
+ for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)
+ {
+ LLDrawInfo& params = **k;
+
+ applyModelMatrix(params);
+
+ params.mVertexBuffer->setBuffer();
+ params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset);
+ }
+}
+
+
+// static
+bool LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel)
+{
+ U8 bump_code = params.mBump;
+
+ return bindBumpMap(bump_code, params.mTexture, channel);
+}
+
+//static
+bool LLDrawPoolBump::bindBumpMap(LLFace* face, S32 channel)
+{
+ const LLTextureEntry* te = face->getTextureEntry();
+ if (te)
+ {
+ U8 bump_code = te->getBumpmap();
+ return bindBumpMap(bump_code, face->getTexture(), channel);
+ }
+
+ return false;
+}
+
+//static
+bool LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, S32 channel)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
+ //Note: texture atlas does not support bump texture now.
+ LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(texture) ;
+ if(!tex)
+ {
+ //if the texture is not a fetched texture
+ return false;
+ }
+
+ LLViewerTexture* bump = NULL;
+
+ switch( bump_code )
+ {
+ case BE_NO_BUMP:
+ break;
+ case BE_BRIGHTNESS:
+ case BE_DARKNESS:
+ bump = gBumpImageList.getBrightnessDarknessImage( tex, bump_code );
+ break;
+
+ default:
+ if( bump_code < LLStandardBumpmap::sStandardBumpmapCount )
+ {
+ bump = gStandardBumpmapList[bump_code].mImage;
+ gBumpImageList.addTextureStats(bump_code, tex->getID(), tex->getMaxVirtualSize());
+ }
+ break;
+ }
+
+ if (bump)
+ {
+ if (channel == -2)
+ {
+ gGL.getTexUnit(1)->bindFast(bump);
+ gGL.getTexUnit(0)->bindFast(bump);
+ }
+ else
+ {
+ // NOTE: do not use bindFast here (see SL-16222)
+ gGL.getTexUnit(channel)->bind(bump);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//static
+void LLDrawPoolBump::beginBump()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
+ sVertexMask = VERTEX_MASK_BUMP;
+ // Optional second pass: emboss bump map
+ stop_glerror();
+
+ shader = &gObjectBumpProgram;
+
+ if (mRigged)
+ {
+ llassert(shader->mRiggedVariant);
+ shader = shader->mRiggedVariant;
+ }
+
+ shader->bind();
+
+ gGL.setSceneBlendType(LLRender::BT_MULT_X2);
+ stop_glerror();
+}
+
+//static
+void LLDrawPoolBump::renderBump(U32 pass)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LEQUAL);
+ LLGLEnable blend(GL_BLEND);
+ gGL.diffuseColor4f(1,1,1,1);
+ /// Get rid of z-fighting with non-bump pass.
+ LLGLEnable polyOffset(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(-1.0f, -1.0f);
+ pushBumpBatches(pass);
+}
+
+//static
+void LLDrawPoolBump::endBump(U32 pass)
+{
+ LLGLSLShader::unbind();
+
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+}
+
+S32 LLDrawPoolBump::getNumDeferredPasses()
+{
+ return 1;
+}
+
+void LLDrawPoolBump::renderDeferred(S32 pass)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
+
+ shiny = true;
+ for (int i = 0; i < 2; ++i)
+ {
+ bool rigged = i == 1;
+ gDeferredBumpProgram.bind(rigged);
+ diffuse_channel = LLGLSLShader::sCurBoundShaderPtr->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+ bump_channel = LLGLSLShader::sCurBoundShaderPtr->enableTexture(LLViewerShaderMgr::BUMP_MAP);
+ gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(bump_channel)->unbind(LLTexUnit::TT_TEXTURE);
+
+ U32 type = rigged ? LLRenderPass::PASS_BUMP_RIGGED : LLRenderPass::PASS_BUMP;
+ LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
+ LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
+
+ LLVOAvatar* avatar = nullptr;
+ U64 skin = 0;
+
+ for (LLCullResult::drawinfo_iterator i = begin; i != end; )
+ {
+ LLDrawInfo& params = **i;
+
+ LLCullResult::increment_iterator(i, end);
+
+ LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(params.mAlphaMaskCutoff);
+ LLDrawPoolBump::bindBumpMap(params, bump_channel);
+
+ if (rigged)
+ {
+ if (avatar != params.mAvatar || skin != params.mSkinInfo->mHash)
+ {
+ uploadMatrixPalette(params);
+ avatar = params.mAvatar;
+ skin = params.mSkinInfo->mHash;
+ }
+ pushBumpBatch(params, true, false);
+ }
+ else
+ {
+ pushBumpBatch(params, true, false);
+ }
+ }
+
+ LLGLSLShader::sCurBoundShaderPtr->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+ LLGLSLShader::sCurBoundShaderPtr->disableTexture(LLViewerShaderMgr::BUMP_MAP);
+ LLGLSLShader::sCurBoundShaderPtr->unbind();
+ gGL.getTexUnit(0)->activate();
+ }
+
+ shiny = false;
+}
+
+
+void LLDrawPoolBump::renderPostDeferred(S32 pass)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
+
+ S32 num_passes = LLPipeline::sRenderingHUDs ? 1 : 2; // skip rigged pass when rendering HUDs
+
+ for (int i = 0; i < num_passes; ++i)
+ { // two passes -- static and rigged
+ mRigged = (i == 1);
+
+ // render shiny
+ beginFullbrightShiny();
+ renderFullbrightShiny();
+ endFullbrightShiny();
+
+ //render bump
+ beginBump();
+ renderBump(LLRenderPass::PASS_POST_BUMP);
+ endBump();
+ }
+}
+
+
+////////////////////////////////////////////////////////////////
+// List of bump-maps created from other textures.
+
+
+//const LLUUID TEST_BUMP_ID("3d33eaf2-459c-6f97-fd76-5fce3fc29447");
+
+void LLBumpImageList::init()
+{
+ llassert( mBrightnessEntries.size() == 0 );
+ llassert( mDarknessEntries.size() == 0 );
+
+ LLStandardBumpmap::restoreGL();
+ sMainQueue = LL::WorkQueue::getInstance("mainloop");
+ sTexUpdateQueue = LL::WorkQueue::getInstance("LLImageGL"); // Share work queue with tex loader.
+}
+
+void LLBumpImageList::clear()
+{
+ LL_INFOS() << "Clearing dynamic bumpmaps." << LL_ENDL;
+ // these will be re-populated on-demand
+ mBrightnessEntries.clear();
+ mDarknessEntries.clear();
+
+ sRenderTarget.release();
+
+ LLStandardBumpmap::clear();
+}
+
+void LLBumpImageList::shutdown()
+{
+ clear();
+ LLStandardBumpmap::shutdown();
+}
+
+void LLBumpImageList::destroyGL()
+{
+ clear();
+ LLStandardBumpmap::destroyGL();
+}
+
+void LLBumpImageList::restoreGL()
+{
+ if(!gTextureList.isInitialized())
+ {
+ //safe to return here because bump images will be reloaded during initialization later.
+ return ;
+ }
+
+ LLStandardBumpmap::restoreGL();
+ // Images will be recreated as they are needed.
+}
+
+
+LLBumpImageList::~LLBumpImageList()
+{
+ // Shutdown should have already been called.
+ llassert( mBrightnessEntries.size() == 0 );
+ llassert( mDarknessEntries.size() == 0 );
+}
+
+
+// Note: Does nothing for entries in gStandardBumpmapList that are not actually standard bump images (e.g. none, brightness, and darkness)
+void LLBumpImageList::addTextureStats(U8 bump, const LLUUID& base_image_id, F32 virtual_size)
+{
+ bump &= TEM_BUMP_MASK;
+ LLViewerFetchedTexture* bump_image = gStandardBumpmapList[bump].mImage;
+ if( bump_image )
+ {
+ bump_image->addTextureStats(virtual_size);
+ }
+}
+
+
+void LLBumpImageList::updateImages()
+{
+ for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); )
+ {
+ bump_image_map_t::iterator curiter = iter++;
+ LLViewerTexture* image = curiter->second;
+ if( image )
+ {
+ bool destroy = true;
+ if( image->hasGLTexture())
+ {
+ if( image->getBoundRecently() )
+ {
+ destroy = false;
+ }
+ else
+ {
+ image->destroyGLTexture();
+ }
+ }
+
+ if( destroy )
+ {
+ //LL_INFOS() << "*** Destroying bright " << (void*)image << LL_ENDL;
+ mBrightnessEntries.erase(curiter); // deletes the image thanks to reference counting
+ }
+ }
+ }
+
+ for (bump_image_map_t::iterator iter = mDarknessEntries.begin(); iter != mDarknessEntries.end(); )
+ {
+ bump_image_map_t::iterator curiter = iter++;
+ LLViewerTexture* image = curiter->second;
+ if( image )
+ {
+ bool destroy = true;
+ if( image->hasGLTexture())
+ {
+ if( image->getBoundRecently() )
+ {
+ destroy = false;
+ }
+ else
+ {
+ image->destroyGLTexture();
+ }
+ }
+
+ if( destroy )
+ {
+ //LL_INFOS() << "*** Destroying dark " << (void*)image << LL_ENDL;;
+ mDarknessEntries.erase(curiter); // deletes the image thanks to reference counting
+ }
+ }
+ }
+}
+
+
+// Note: the caller SHOULD NOT keep the pointer that this function returns. It may be updated as more data arrives.
+LLViewerTexture* LLBumpImageList::getBrightnessDarknessImage(LLViewerFetchedTexture* src_image, U8 bump_code )
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
+ llassert( (bump_code == BE_BRIGHTNESS) || (bump_code == BE_DARKNESS) );
+
+ LLViewerTexture* bump = NULL;
+
+ bump_image_map_t* entries_list = NULL;
+ void (*callback_func)( bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata ) = NULL;
+
+ switch( bump_code )
+ {
+ case BE_BRIGHTNESS:
+ entries_list = &mBrightnessEntries;
+ callback_func = LLBumpImageList::onSourceBrightnessLoaded;
+ break;
+ case BE_DARKNESS:
+ entries_list = &mDarknessEntries;
+ callback_func = LLBumpImageList::onSourceDarknessLoaded;
+ break;
+ default:
+ llassert(0);
+ return NULL;
+ }
+
+ bump_image_map_t::iterator iter = entries_list->find(src_image->getID());
+ if (iter != entries_list->end() && iter->second.notNull())
+ {
+ bump = iter->second;
+ }
+ else
+ {
+ (*entries_list)[src_image->getID()] = LLViewerTextureManager::getLocalTexture( true );
+ bump = (*entries_list)[src_image->getID()]; // In case callback was called immediately and replaced the image
+ }
+
+ if (!src_image->hasCallbacks())
+ { //if image has no callbacks but resolutions don't match, trigger raw image loaded callback again
+ if (src_image->getWidth() != bump->getWidth() ||
+ src_image->getHeight() != bump->getHeight())// ||
+ //(LLPipeline::sRenderDeferred && bump->getComponents() != 4))
+ {
+ src_image->setBoostLevel(LLGLTexture::BOOST_BUMP) ;
+ src_image->setLoadedCallback( callback_func, 0, true, false, new LLUUID(src_image->getID()), NULL );
+ src_image->forceToSaveRawImage(0) ;
+ }
+ }
+
+ return bump;
+}
+
+
+// static
+void LLBumpImageList::onSourceBrightnessLoaded( bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata )
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
+ LLUUID* source_asset_id = (LLUUID*)userdata;
+ LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_BRIGHTNESS );
+ if( final )
+ {
+ delete source_asset_id;
+ }
+}
+
+// static
+void LLBumpImageList::onSourceDarknessLoaded( bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata )
+{
+ LLUUID* source_asset_id = (LLUUID*)userdata;
+ LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_DARKNESS );
+ if( final )
+ {
+ delete source_asset_id;
+ }
+}
+
+void LLBumpImageList::onSourceStandardLoaded( bool success, LLViewerFetchedTexture* src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata)
+{
+ if (success && LLPipeline::sRenderDeferred)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
+ LLPointer<LLImageRaw> nrm_image = new LLImageRaw(src->getWidth(), src->getHeight(), 4);
+ {
+ generateNormalMapFromAlpha(src, nrm_image);
+ }
+ src_vi->setExplicitFormat(GL_RGBA, GL_RGBA);
+ {
+ src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image);
+ }
+ }
+}
+
+void LLBumpImageList::generateNormalMapFromAlpha(LLImageRaw* src, LLImageRaw* nrm_image)
+{
+ LLImageDataSharedLock lockIn(src);
+ LLImageDataLock lockOut(nrm_image);
+
+ U8* nrm_data = nrm_image->getData();
+ S32 resX = src->getWidth();
+ S32 resY = src->getHeight();
+
+ const U8* src_data = src->getData();
+
+ S32 src_cmp = src->getComponents();
+
+ F32 norm_scale = gSavedSettings.getF32("RenderNormalMapScale");
+
+ U32 idx = 0;
+ //generate normal map from pseudo-heightfield
+ for (S32 j = 0; j < resY; ++j)
+ {
+ for (S32 i = 0; i < resX; ++i)
+ {
+ S32 rX = (i+1)%resX;
+ S32 rY = (j+1)%resY;
+ S32 lX = (i-1)%resX;
+ S32 lY = (j-1)%resY;
+
+ if (lX < 0)
+ {
+ lX += resX;
+ }
+ if (lY < 0)
+ {
+ lY += resY;
+ }
+
+ F32 cH = (F32) src_data[(j*resX+i)*src_cmp+src_cmp-1];
+
+ LLVector3 right = LLVector3(norm_scale, 0, (F32) src_data[(j*resX+rX)*src_cmp+src_cmp-1]-cH);
+ LLVector3 left = LLVector3(-norm_scale, 0, (F32) src_data[(j*resX+lX)*src_cmp+src_cmp-1]-cH);
+ LLVector3 up = LLVector3(0, -norm_scale, (F32) src_data[(lY*resX+i)*src_cmp+src_cmp-1]-cH);
+ LLVector3 down = LLVector3(0, norm_scale, (F32) src_data[(rY*resX+i)*src_cmp+src_cmp-1]-cH);
+
+ LLVector3 norm = right%down + down%left + left%up + up%right;
+
+ norm.normVec();
+
+ norm *= 0.5f;
+ norm += LLVector3(0.5f,0.5f,0.5f);
+
+ idx = (j*resX+i)*4;
+ nrm_data[idx+0]= (U8) (norm.mV[0]*255);
+ nrm_data[idx+1]= (U8) (norm.mV[1]*255);
+ nrm_data[idx+2]= (U8) (norm.mV[2]*255);
+ nrm_data[idx+3]= src_data[(j*resX+i)*src_cmp+src_cmp-1];
+ }
+ }
+}
+
+// static
+void LLBumpImageList::onSourceLoaded( bool success, LLViewerTexture *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump_code )
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ if( success )
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
+
+ LLImageDataSharedLock lock(src);
+
+ bump_image_map_t& entries_list(bump_code == BE_BRIGHTNESS ? gBumpImageList.mBrightnessEntries : gBumpImageList.mDarknessEntries );
+ bump_image_map_t::iterator iter = entries_list.find(source_asset_id);
+
+ {
+ if (iter == entries_list.end() ||
+ iter->second.isNull() ||
+ iter->second->getWidth() != src->getWidth() ||
+ iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution
+ { //make sure an entry exists for this image
+ entries_list[src_vi->getID()] = LLViewerTextureManager::getLocalTexture(true);
+ iter = entries_list.find(src_vi->getID());
+ }
+ }
+
+ if (iter->second->getWidth() != src->getWidth() ||
+ iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution
+ {
+ LLPointer<LLImageRaw> dst_image = new LLImageRaw(src->getWidth(), src->getHeight(), 1);
+ U8* dst_data = dst_image->getData();
+ S32 dst_data_size = dst_image->getDataSize();
+
+ const U8* src_data = src->getData();
+ S32 src_data_size = src->getDataSize();
+
+ S32 src_components = src->getComponents();
+
+ // Convert to luminance and then scale and bias that to get ready for
+ // embossed bump mapping. (0-255 maps to 127-255)
+
+ // Convert to fixed point so we don't have to worry about precision/clamping.
+ const S32 FIXED_PT = 8;
+ const S32 R_WEIGHT = S32(0.2995f * (1<<FIXED_PT));
+ const S32 G_WEIGHT = S32(0.5875f * (1<<FIXED_PT));
+ const S32 B_WEIGHT = S32(0.1145f * (1<<FIXED_PT));
+
+ S32 minimum = 255;
+ S32 maximum = 0;
+
+ switch( src_components )
+ {
+ case 1:
+ case 2:
+ {
+ if( src_data_size == dst_data_size * src_components )
+ {
+ for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+ {
+ dst_data[i] = src_data[j];
+ if( dst_data[i] < minimum )
+ {
+ minimum = dst_data[i];
+ }
+ if( dst_data[i] > maximum )
+ {
+ maximum = dst_data[i];
+ }
+ }
+ }
+ else
+ {
+ llassert(0);
+ dst_image->clear();
+ }
+ }
+ break;
+ case 3:
+ case 4:
+ {
+ if( src_data_size == dst_data_size * src_components )
+ {
+ for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+ {
+ // RGB to luminance
+ dst_data[i] = (R_WEIGHT * src_data[j] + G_WEIGHT * src_data[j+1] + B_WEIGHT * src_data[j+2]) >> FIXED_PT;
+ //llassert( dst_data[i] <= 255 );true because it's 8bit
+ if( dst_data[i] < minimum )
+ {
+ minimum = dst_data[i];
+ }
+ if( dst_data[i] > maximum )
+ {
+ maximum = dst_data[i];
+ }
+ }
+ }
+ else
+ {
+ llassert(0);
+ dst_image->clear();
+ }
+ }
+ break;
+ default:
+ llassert(0);
+ dst_image->clear();
+ break;
+ }
+
+ if( maximum > minimum )
+ {
+ U8 bias_and_scale_lut[256];
+ F32 twice_one_over_range = 2.f / (maximum - minimum);
+ S32 i;
+
+ const F32 ARTIFICIAL_SCALE = 2.f; // Advantage: exaggerates the effect in midrange. Disadvantage: clamps at the extremes.
+ if (BE_DARKNESS == bump_code)
+ {
+ for( i = minimum; i <= maximum; i++ )
+ {
+ F32 minus_one_to_one = F32(maximum - i) * twice_one_over_range - 1.f;
+ bias_and_scale_lut[i] = llclampb(ll_round(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128));
+ }
+ }
+ else
+ {
+ for( i = minimum; i <= maximum; i++ )
+ {
+ F32 minus_one_to_one = F32(i - minimum) * twice_one_over_range - 1.f;
+ bias_and_scale_lut[i] = llclampb(ll_round(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128));
+ }
+ }
+
+ for( i = 0; i < dst_data_size; i++ )
+ {
+ dst_data[i] = bias_and_scale_lut[dst_data[i]];
+ }
+ }
+
+ //---------------------------------------------------
+ // immediately assign bump to a smart pointer in case some local smart pointer
+ // accidentally releases it.
+ LLPointer<LLViewerTexture> bump = iter->second;
+
+ if (!LLPipeline::sRenderDeferred)
+ {
+ bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
+
+#if LL_BUMPLIST_MULTITHREADED
+ auto tex_queue = LLImageGLThread::sEnabledTextures ? sTexUpdateQueue.lock() : nullptr;
+
+ if (tex_queue)
+ { //dispatch creation to background thread
+ LLImageRaw* dst_ptr = dst_image;
+ LLViewerTexture* bump_ptr = bump;
+ dst_ptr->ref();
+ bump_ptr->ref();
+ tex_queue->post(
+ [=]()
+ {
+ LL_PROFILE_ZONE_NAMED("bil - create texture");
+ bump_ptr->createGLTexture(0, dst_ptr);
+ bump_ptr->unref();
+ dst_ptr->unref();
+ });
+
+ }
+ else
+#endif
+ {
+ bump->createGLTexture(0, dst_image);
+ }
+ }
+ else
+ { //convert to normal map
+ LL_PROFILE_ZONE_NAMED("bil - create normal map");
+ LLImageGL* img = bump->getGLTexture();
+ LLImageRaw* dst_ptr = dst_image.get();
+ LLGLTexture* bump_ptr = bump.get();
+
+ dst_ptr->ref();
+ img->ref();
+ bump_ptr->ref();
+ auto create_func = [=]()
+ {
+ img->setUseMipMaps(true);
+ // upload dst_image to GPU (greyscale in red channel)
+ img->setExplicitFormat(GL_RED, GL_RED);
+
+ bump_ptr->createGLTexture(0, dst_ptr);
+ dst_ptr->unref();
+ };
+
+ auto generate_func = [=]()
+ {
+ // Allocate an empty RGBA texture at "tex_name" the same size as bump
+ // Note: bump will still point at GPU copy of dst_image
+ bump_ptr->setExplicitFormat(GL_RGBA, GL_RGBA);
+ LLGLuint tex_name;
+ img->createGLTexture(0, nullptr, false, 0, true, &tex_name);
+
+ // point render target at empty buffer
+ sRenderTarget.setColorAttachment(img, tex_name);
+
+ // generate normal map in empty texture
+ {
+ sRenderTarget.bindTarget();
+
+ LLGLDepthTest depth(GL_FALSE);
+ LLGLDisable cull(GL_CULL_FACE);
+ LLGLDisable blend(GL_BLEND);
+ gGL.setColorMask(true, true);
+
+ gNormalMapGenProgram.bind();
+
+ static LLStaticHashedString sNormScale("norm_scale");
+ static LLStaticHashedString sStepX("stepX");
+ static LLStaticHashedString sStepY("stepY");
+
+ gNormalMapGenProgram.uniform1f(sNormScale, gSavedSettings.getF32("RenderNormalMapScale"));
+ gNormalMapGenProgram.uniform1f(sStepX, 1.f / bump_ptr->getWidth());
+ gNormalMapGenProgram.uniform1f(sStepY, 1.f / bump_ptr->getHeight());
+
+ gGL.getTexUnit(0)->bind(bump_ptr);
+
+ gGL.begin(LLRender::TRIANGLE_STRIP);
+ gGL.texCoord2f(0, 0);
+ gGL.vertex2f(0, 0);
+
+ gGL.texCoord2f(0, 1);
+ gGL.vertex2f(0, 1);
+
+ gGL.texCoord2f(1, 0);
+ gGL.vertex2f(1, 0);
+
+ gGL.texCoord2f(1, 1);
+ gGL.vertex2f(1, 1);
+
+ gGL.end();
+
+ gGL.flush();
+
+ gNormalMapGenProgram.unbind();
+
+ sRenderTarget.flush();
+ sRenderTarget.releaseColorAttachment();
+ }
+
+ // point bump at normal map and free gpu copy of dst_image
+ img->syncTexName(tex_name);
+
+ // generate mipmap
+ gGL.getTexUnit(0)->bind(img);
+ glGenerateMipmap(GL_TEXTURE_2D);
+ gGL.getTexUnit(0)->disable();
+
+ bump_ptr->unref();
+ img->unref();
+ };
+
+#if LL_BUMPLIST_MULTITHREADED
+ auto main_queue = LLImageGLThread::sEnabledTextures ? sMainQueue.lock() : nullptr;
+
+ if (main_queue)
+ { //dispatch texture upload to background thread, issue GPU commands to generate normal map on main thread
+ main_queue->postTo(
+ sTexUpdateQueue,
+ create_func,
+ generate_func);
+ }
+ else
+#endif
+ { // immediate upload texture and generate normal map
+ create_func();
+ generate_func();
+ }
+
+
+ }
+
+ iter->second = bump; // derefs (and deletes) old image
+ //---------------------------------------------------
+ }
+ }
+}
+
+void LLDrawPoolBump::pushBumpBatches(U32 type)
+{
+ LLVOAvatar* avatar = nullptr;
+ U64 skin = 0;
+
+ if (mRigged)
+ { // nudge type enum and include skinweights for rigged pass
+ type += 1;
+ }
+
+ LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
+ LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
+
+ for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i)
+ {
+ LLDrawInfo& params = **i;
+
+ if (LLDrawPoolBump::bindBumpMap(params))
+ {
+ if (mRigged)
+ {
+ if (avatar != params.mAvatar || skin != params.mSkinInfo->mHash)
+ {
+ if (uploadMatrixPalette(params))
+ {
+ avatar = params.mAvatar;
+ skin = params.mSkinInfo->mHash;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ }
+ pushBumpBatch(params, false);
+ }
+ }
+}
+
+void LLRenderPass::pushBumpBatch(LLDrawInfo& params, bool texture, bool batch_textures)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
+ applyModelMatrix(params);
+
+ bool tex_setup = false;
+
+ if (batch_textures && params.mTextureList.size() > 1)
+ {
+ for (U32 i = 0; i < params.mTextureList.size(); ++i)
+ {
+ if (params.mTextureList[i].notNull())
+ {
+ gGL.getTexUnit(i)->bindFast(params.mTextureList[i]);
+ }
+ }
+ }
+ else
+ { //not batching textures or batch has only 1 texture -- might need a texture matrix
+ if (params.mTextureMatrix)
+ {
+ if (shiny)
+ {
+ gGL.getTexUnit(0)->activate();
+ gGL.matrixMode(LLRender::MM_TEXTURE);
+ }
+ else
+ {
+ gGL.getTexUnit(0)->activate();
+ gGL.matrixMode(LLRender::MM_TEXTURE);
+ gGL.loadMatrix((GLfloat*) params.mTextureMatrix->mMatrix);
+ gPipeline.mTextureMatrixOps++;
+ }
+
+ gGL.loadMatrix((GLfloat*) params.mTextureMatrix->mMatrix);
+ gPipeline.mTextureMatrixOps++;
+
+ tex_setup = true;
+ }
+
+ if (shiny && mShaderLevel > 1 && texture)
+ {
+ if (params.mTexture.notNull())
+ {
+ gGL.getTexUnit(diffuse_channel)->bindFast(params.mTexture);
+ }
+ else
+ {
+ gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE);
+ }
+ }
+ }
+
+ params.mVertexBuffer->setBuffer();
+ params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset);
+
+ if (tex_setup)
+ {
+ if (shiny)
+ {
+ gGL.getTexUnit(0)->activate();
+ }
+ else
+ {
+ gGL.getTexUnit(0)->activate();
+ gGL.matrixMode(LLRender::MM_TEXTURE);
+ }
+ gGL.loadIdentity();
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ }
+}
+
|