Need to test: localassetbrowser preview related floaters hgfloatertexteditor maps media textures! Currently very hacky web browser alpha masks on avatars bumpmaps Are all sky components appearing? LLViewerDynamicTexture (texture baking, browser, animated textures, anim previews, etc) Snapshot related features Customize avatar vfs floater UI textures in general Texture priority issues
1330 lines
34 KiB
C++
1330 lines
34 KiB
C++
/**
|
|
* @file lldrawpoolbump.cpp
|
|
* @brief LLDrawPoolBump class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/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 "llagent.h"
|
|
#include "llcubemap.h"
|
|
#include "lldrawable.h"
|
|
#include "llface.h"
|
|
#include "llsky.h"
|
|
#include "lltextureentry.h"
|
|
#include "llviewercamera.h"
|
|
#include "llviewertexturelist.h"
|
|
#include "pipeline.h"
|
|
#include "llspatialpartition.h"
|
|
#include "llviewershadermgr.h"
|
|
|
|
//#include "llimagebmp.h"
|
|
//#include "../tools/imdebug/imdebug.h"
|
|
|
|
// static
|
|
LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT];
|
|
|
|
// 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
|
|
void LLStandardBumpmap::init()
|
|
{
|
|
LLStandardBumpmap::restoreGL();
|
|
}
|
|
|
|
// static
|
|
void LLStandardBumpmap::shutdown()
|
|
{
|
|
LLStandardBumpmap::destroyGL();
|
|
}
|
|
|
|
// static
|
|
void LLStandardBumpmap::restoreGL()
|
|
{
|
|
addstandard();
|
|
}
|
|
|
|
// static
|
|
void LLStandardBumpmap::addstandard()
|
|
{
|
|
// 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();
|
|
llinfos << "Adding standard bumpmaps." << llendl;
|
|
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 )
|
|
{
|
|
llwarns << "Could not open std_bump <" << file_name << ">" << llendl;
|
|
return;
|
|
}
|
|
|
|
S32 file_version = 0;
|
|
|
|
S32 fields_read = fscanf( file, "LLStandardBumpmap version %d", &file_version );
|
|
if( fields_read != 1 )
|
|
{
|
|
llwarns << "Bad LLStandardBumpmap header" << llendl;
|
|
return;
|
|
}
|
|
|
|
if( file_version > STD_BUMP_LATEST_FILE_VERSION )
|
|
{
|
|
llwarns << "LLStandardBumpmap has newer version (" << file_version << ") than viewer (" << STD_BUMP_LATEST_FILE_VERSION << ")" << llendl;
|
|
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 )
|
|
{
|
|
llwarns << "Bad LLStandardBumpmap entry" << llendl;
|
|
return;
|
|
}
|
|
if(strlen(bump_image_id) == (UUID_STR_LENGTH - 1) + 4 && !stricmp(&(bump_image_id[UUID_STR_LENGTH-1]),".j2c"))
|
|
bump_image_id[UUID_STR_LENGTH-1] = 0; // truncate to a valid uuid (hopefully)
|
|
// llinfos << "Loading bumpmap: " << bump_file << " from viewerart" << llendl;
|
|
gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label;
|
|
gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage =
|
|
LLViewerTextureManager::getFetchedTexture(LLUUID(bump_image_id),
|
|
TRUE,
|
|
LLViewerTexture::BOOST_NONE,
|
|
LLViewerTexture::LOD_TEXTURE,
|
|
0,
|
|
0);
|
|
gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setBoostLevel(LLViewerTexture::BOOST_BUMP) ;
|
|
gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setLoadedCallback(LLBumpImageList::onSourceStandardLoaded, 0, TRUE, FALSE, NULL, NULL );
|
|
LLStandardBumpmap::sStandardBumpmapCount++;
|
|
}
|
|
|
|
fclose( file );
|
|
}
|
|
|
|
// static
|
|
void LLStandardBumpmap::clear()
|
|
{
|
|
llinfos << "Clearing standard bumpmaps." << llendl;
|
|
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)
|
|
{
|
|
mShiny = FALSE;
|
|
}
|
|
|
|
|
|
void LLDrawPoolBump::prerender()
|
|
{
|
|
mVertexShaderLevel = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT);
|
|
}
|
|
|
|
// static
|
|
S32 LLDrawPoolBump::numBumpPasses()
|
|
{
|
|
static const LLCachedControl<bool> render_object_bump("RenderObjectBump",false);
|
|
if (render_object_bump)
|
|
{
|
|
if (mVertexShaderLevel > 1)
|
|
{
|
|
if (LLPipeline::sImpostorRender)
|
|
{
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
return 3;
|
|
}
|
|
}
|
|
else if (LLPipeline::sImpostorRender)
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
S32 LLDrawPoolBump::getNumPasses()
|
|
{
|
|
return numBumpPasses();
|
|
}
|
|
|
|
void LLDrawPoolBump::beginRenderPass(S32 pass)
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
|
|
switch( pass )
|
|
{
|
|
case 0:
|
|
beginShiny();
|
|
break;
|
|
case 1:
|
|
if (mVertexShaderLevel > 1)
|
|
{
|
|
beginFullbrightShiny();
|
|
}
|
|
else
|
|
{
|
|
beginBump();
|
|
}
|
|
break;
|
|
case 2:
|
|
beginBump();
|
|
break;
|
|
default:
|
|
llassert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::render(S32 pass)
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
|
|
|
|
if (!gPipeline.hasRenderType(LLDrawPool::POOL_SIMPLE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch( pass )
|
|
{
|
|
case 0:
|
|
renderShiny();
|
|
break;
|
|
case 1:
|
|
if (mVertexShaderLevel > 1)
|
|
{
|
|
renderFullbrightShiny();
|
|
}
|
|
else
|
|
{
|
|
renderBump();
|
|
}
|
|
break;
|
|
case 2:
|
|
renderBump();
|
|
break;
|
|
default:
|
|
llassert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::endRenderPass(S32 pass)
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
|
|
switch( pass )
|
|
{
|
|
case 0:
|
|
endShiny();
|
|
break;
|
|
case 1:
|
|
if (mVertexShaderLevel > 1)
|
|
{
|
|
endFullbrightShiny();
|
|
}
|
|
else
|
|
{
|
|
endBump();
|
|
}
|
|
break;
|
|
case 2:
|
|
endBump();
|
|
break;
|
|
default:
|
|
llassert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLDrawPoolBump::beginShiny(bool invisible)
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
|
|
if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))||
|
|
(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mShiny = TRUE;
|
|
sVertexMask = VERTEX_MASK_SHINY;
|
|
// Second pass: environment map
|
|
if (!invisible && mVertexShaderLevel > 1)
|
|
{
|
|
sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0;
|
|
}
|
|
|
|
if (LLPipeline::sUnderWaterRender)
|
|
{
|
|
shader = &gObjectShinyWaterProgram;
|
|
}
|
|
else
|
|
{
|
|
shader = &gObjectShinyProgram;
|
|
}
|
|
|
|
LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
|
|
if( cube_map )
|
|
{
|
|
if (!invisible && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0 )
|
|
{
|
|
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 (mVertexShaderLevel > 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);
|
|
cube_map->enableTextureCoords(1);
|
|
diffuse_channel = shader->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
|
|
}
|
|
else
|
|
{
|
|
cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
|
|
diffuse_channel = -1;
|
|
cube_map->setMatrix(0);
|
|
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);
|
|
|
|
gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_COLOR);
|
|
gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_VERT_ALPHA);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::renderShiny(bool invisible)
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
|
|
if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))||
|
|
(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( gSky.mVOSkyp->getCubeMap() )
|
|
{
|
|
LLGLEnable blend_enable(GL_BLEND);
|
|
if (!invisible && mVertexShaderLevel > 1)
|
|
{
|
|
LLRenderPass::renderTexture(LLRenderPass::PASS_SHINY, sVertexMask);
|
|
}
|
|
else if (!invisible)
|
|
{
|
|
renderGroups(LLRenderPass::PASS_SHINY, sVertexMask);
|
|
}
|
|
else // invisible
|
|
{
|
|
renderGroups(LLRenderPass::PASS_INVISI_SHINY, sVertexMask);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::endShiny(bool invisible)
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
|
|
if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))||
|
|
(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
|
|
if( cube_map )
|
|
{
|
|
cube_map->disable();
|
|
cube_map->restoreMatrix();
|
|
|
|
if (!invisible && mVertexShaderLevel > 1)
|
|
{
|
|
shader->disableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
|
|
|
|
if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0)
|
|
{
|
|
if (diffuse_channel != 0)
|
|
{
|
|
shader->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
|
|
}
|
|
}
|
|
shader->unbind();
|
|
}
|
|
}
|
|
gGL.getTexUnit(diffuse_channel)->disable();
|
|
gGL.getTexUnit(cube_channel)->disable();
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
|
|
|
|
diffuse_channel = -1;
|
|
cube_channel = 0;
|
|
mShiny = FALSE;
|
|
}
|
|
|
|
void LLDrawPoolBump::beginFullbrightShiny()
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0;
|
|
|
|
// Second pass: environment map
|
|
|
|
if (LLPipeline::sUnderWaterRender)
|
|
{
|
|
shader = &gObjectShinyWaterProgram;
|
|
}
|
|
else
|
|
{
|
|
shader = &gObjectFullbrightShinyProgram;
|
|
}
|
|
|
|
LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
|
|
if( cube_map )
|
|
{
|
|
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);
|
|
|
|
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
|
|
gGL.getTexUnit(1)->disable();
|
|
cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
|
|
cube_map->enableTexture(cube_channel);
|
|
cube_map->enableTextureCoords(1);
|
|
diffuse_channel = shader->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
|
|
|
|
gGL.getTexUnit(cube_channel)->bind(cube_map);
|
|
gGL.getTexUnit(0)->activate();
|
|
}
|
|
mShiny = TRUE;
|
|
}
|
|
|
|
void LLDrawPoolBump::renderFullbrightShiny()
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( gSky.mVOSkyp->getCubeMap() )
|
|
{
|
|
LLGLEnable blend_enable(GL_BLEND);
|
|
LLRenderPass::renderTexture(LLRenderPass::PASS_FULLBRIGHT_SHINY, sVertexMask);
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::endFullbrightShiny()
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
|
|
if( cube_map )
|
|
{
|
|
cube_map->disable();
|
|
cube_map->restoreMatrix();
|
|
|
|
if (diffuse_channel != 0)
|
|
{
|
|
shader->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
|
|
}
|
|
gGL.getTexUnit(0)->activate();
|
|
gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
|
|
|
|
shader->unbind();
|
|
gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
|
|
}
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
|
|
|
|
diffuse_channel = -1;
|
|
cube_channel = 0;
|
|
mShiny = FALSE;
|
|
}
|
|
|
|
void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, 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);
|
|
|
|
if (params.mGroup)
|
|
{
|
|
params.mGroup->rebuildMesh();
|
|
}
|
|
params.mVertexBuffer->setBuffer(mask);
|
|
params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset);
|
|
gPipeline.addTrianglesDrawn(params.mCount/3);
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
BOOL LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel)
|
|
{
|
|
LLViewerTexture* bump = NULL;
|
|
|
|
U8 bump_code = params.mBump;
|
|
|
|
//Note: texture atlas does not support bump texture now.
|
|
LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(params.mTexture) ;
|
|
if(!tex)
|
|
{
|
|
//if the texture is not a fetched texture
|
|
return FALSE;
|
|
}
|
|
|
|
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(), params.mVSize);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (bump)
|
|
{
|
|
if (channel == -2)
|
|
{
|
|
gGL.getTexUnit(1)->bind(bump);
|
|
gGL.getTexUnit(0)->bind(bump);
|
|
}
|
|
else
|
|
{
|
|
gGL.getTexUnit(channel)->bind(bump);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//static
|
|
void LLDrawPoolBump::beginBump()
|
|
{
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
|
|
{
|
|
return;
|
|
}
|
|
|
|
sVertexMask = VERTEX_MASK_BUMP;
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
|
|
// Optional second pass: emboss bump map
|
|
stop_glerror();
|
|
|
|
// TEXTURE UNIT 0
|
|
// Output.rgb = texture at texture coord 0
|
|
gGL.getTexUnit(0)->activate();
|
|
|
|
gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA);
|
|
gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA);
|
|
|
|
// TEXTURE UNIT 1
|
|
gGL.getTexUnit(1)->activate();
|
|
|
|
gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE);
|
|
|
|
gGL.getTexUnit(1)->setTextureColorBlend(LLTexUnit::TBO_ADD_SIGNED, LLTexUnit::TBS_PREV_COLOR, LLTexUnit::TBS_ONE_MINUS_TEX_ALPHA);
|
|
gGL.getTexUnit(1)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA);
|
|
|
|
// src = tex0 + (1 - tex1) - 0.5
|
|
// = (bump0/2 + 0.5) + (1 - (bump1/2 + 0.5)) - 0.5
|
|
// = (1 + bump0 - bump1) / 2
|
|
|
|
|
|
// Blend: src * dst + dst * src
|
|
// = 2 * src * dst
|
|
// = 2 * ((1 + bump0 - bump1) / 2) * dst [0 - 2 * dst]
|
|
// = (1 + bump0 - bump1) * dst.rgb
|
|
// = dst.rgb + dst.rgb * (bump0 - bump1)
|
|
gGL.setSceneBlendType(LLRender::BT_MULT_X2);
|
|
gGL.getTexUnit(0)->activate();
|
|
stop_glerror();
|
|
|
|
gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE);
|
|
}
|
|
|
|
//static
|
|
void LLDrawPoolBump::renderBump()
|
|
{
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP);
|
|
LLGLDisable fog(GL_FOG);
|
|
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LEQUAL);
|
|
LLGLEnable blend(GL_BLEND);
|
|
glColor4f(1,1,1,1);
|
|
/// Get rid of z-fighting with non-bump pass.
|
|
LLGLEnable polyOffset(GL_POLYGON_OFFSET_FILL);
|
|
glPolygonOffset(-1.0f, -1.0f);
|
|
renderBump(LLRenderPass::PASS_BUMP, sVertexMask);
|
|
}
|
|
|
|
//static
|
|
void LLDrawPoolBump::endBump()
|
|
{
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Disable texture unit 1
|
|
gGL.getTexUnit(1)->activate();
|
|
gGL.getTexUnit(1)->disable();
|
|
gGL.getTexUnit(1)->setTextureBlendType(LLTexUnit::TB_MULT);
|
|
|
|
// Disable texture unit 0
|
|
gGL.getTexUnit(0)->activate();
|
|
gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
|
|
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
}
|
|
|
|
void LLDrawPoolBump::beginDeferredPass(S32 pass)
|
|
{
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
|
|
{
|
|
return;
|
|
}
|
|
LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP);
|
|
mShiny = TRUE;
|
|
gDeferredBumpProgram.bind();
|
|
diffuse_channel = gDeferredBumpProgram.enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
|
|
bump_channel = gDeferredBumpProgram.enableTexture(LLViewerShaderMgr::BUMP_MAP);
|
|
gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(bump_channel)->unbind(LLTexUnit::TT_TEXTURE);
|
|
}
|
|
|
|
void LLDrawPoolBump::endDeferredPass(S32 pass)
|
|
{
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
|
|
{
|
|
return;
|
|
}
|
|
LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP);
|
|
mShiny = FALSE;
|
|
gDeferredBumpProgram.disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
|
|
gDeferredBumpProgram.disableTexture(LLViewerShaderMgr::BUMP_MAP);
|
|
gDeferredBumpProgram.unbind();
|
|
gGL.getTexUnit(0)->activate();
|
|
}
|
|
|
|
void LLDrawPoolBump::renderDeferred(S32 pass)
|
|
{
|
|
if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
|
|
{
|
|
return;
|
|
}
|
|
LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP);
|
|
|
|
U32 type = LLRenderPass::PASS_BUMP;
|
|
LLCullResult::drawinfo_list_t::iterator begin = gPipeline.beginRenderMap(type);
|
|
LLCullResult::drawinfo_list_t::iterator end = gPipeline.endRenderMap(type);
|
|
|
|
U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_BINORMAL | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR;
|
|
|
|
for (LLCullResult::drawinfo_list_t::iterator i = begin; i != end; ++i)
|
|
{
|
|
LLDrawInfo& params = **i;
|
|
|
|
LLDrawPoolBump::bindBumpMap(params, bump_channel);
|
|
pushBatch(params, mask, TRUE);
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::beginPostDeferredPass(S32 pass)
|
|
{
|
|
beginFullbrightShiny();
|
|
}
|
|
|
|
void LLDrawPoolBump::endPostDeferredPass(S32 pass)
|
|
{
|
|
endFullbrightShiny();
|
|
}
|
|
|
|
void LLDrawPoolBump::renderPostDeferred(S32 pass)
|
|
{
|
|
renderFullbrightShiny();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// List of one-component bump-maps created from other texures.
|
|
|
|
|
|
//const LLUUID TEST_BUMP_ID("3d33eaf2-459c-6f97-fd76-5fce3fc29447");
|
|
|
|
void LLBumpImageList::init()
|
|
{
|
|
llassert( mBrightnessEntries.size() == 0 );
|
|
llassert( mDarknessEntries.size() == 0 );
|
|
|
|
LLStandardBumpmap::init();
|
|
}
|
|
|
|
void LLBumpImageList::clear()
|
|
{
|
|
llinfos << "Clearing dynamic bumpmaps." << llendl;
|
|
// these will be re-populated on-demand
|
|
mBrightnessEntries.clear();
|
|
mDarknessEntries.clear();
|
|
|
|
LLStandardBumpmap::clear();
|
|
}
|
|
|
|
void LLBumpImageList::shutdown()
|
|
{
|
|
clear();
|
|
LLStandardBumpmap::shutdown();
|
|
}
|
|
|
|
void LLBumpImageList::destroyGL()
|
|
{
|
|
clear();
|
|
LLStandardBumpmap::destroyGL();
|
|
}
|
|
|
|
void LLBumpImageList::restoreGL()
|
|
{
|
|
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()
|
|
{
|
|
llpushcallstacks ;
|
|
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 )
|
|
{
|
|
//llinfos << "*** Destroying bright " << (void*)image << llendl;
|
|
mBrightnessEntries.erase(curiter); // deletes the image thanks to reference counting
|
|
}
|
|
}
|
|
}
|
|
llpushcallstacks ;
|
|
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 )
|
|
{
|
|
//llinfos << "*** Destroying dark " << (void*)image << llendl;;
|
|
mDarknessEntries.erase(curiter); // deletes the image thanks to reference counting
|
|
}
|
|
}
|
|
}
|
|
llpushcallstacks ;
|
|
}
|
|
|
|
|
|
// 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 )
|
|
{
|
|
llassert( (bump_code == BE_BRIGHTNESS) || (bump_code == BE_DARKNESS) );
|
|
|
|
LLViewerTexture* bump = NULL;
|
|
const F32 BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD = 1000;
|
|
if( src_image->getMaxVirtualSize() > BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD )
|
|
{
|
|
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())
|
|
{
|
|
bump = iter->second;
|
|
}
|
|
else
|
|
{
|
|
LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,1);
|
|
raw->clear(0x77, 0x77, 0x77, 0xFF);
|
|
|
|
(*entries_list)[src_image->getID()] = LLViewerTextureManager::getLocalTexture( raw.get(), TRUE);
|
|
(*entries_list)[src_image->getID()]->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
|
|
|
|
// Note: this may create an LLImageGL immediately
|
|
src_image->setBoostLevel(LLViewerTexture::BOOST_BUMP) ;
|
|
src_image->setLoadedCallback( callback_func, 0, TRUE, FALSE, new LLUUID(src_image->getID()), NULL );
|
|
bump = (*entries_list)[src_image->getID()]; // In case callback was called immediately and replaced the image
|
|
|
|
// bump_total++;
|
|
// llinfos << "*** Creating " << (void*)bump << " " << bump_total << llendl;
|
|
}
|
|
}
|
|
|
|
return bump;
|
|
}
|
|
|
|
|
|
// static
|
|
void LLBumpImageList::onSourceBrightnessLoaded( 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_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)
|
|
{
|
|
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)
|
|
{
|
|
U8* nrm_data = nrm_image->getData();
|
|
S32 resX = src->getWidth();
|
|
S32 resY = src->getHeight();
|
|
|
|
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 )
|
|
{
|
|
if( success )
|
|
{
|
|
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())
|
|
{
|
|
LLPointer<LLImageRaw> dst_image = new LLImageRaw(src->getWidth(), src->getHeight(), 1);
|
|
U8* dst_data = dst_image->getData();
|
|
S32 dst_data_size = dst_image->getDataSize();
|
|
|
|
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: exagerates 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(llround(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// BE_LIGHTNESS
|
|
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(llround(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 global smart pointer in case some local smart pointer
|
|
// accidentally releases it.
|
|
LLPointer<LLViewerTexture> bump = LLViewerTextureManager::getLocalTexture( TRUE );
|
|
|
|
if (!LLPipeline::sRenderDeferred)
|
|
{
|
|
bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
|
|
bump->createGLTexture(0, dst_image);
|
|
}
|
|
else
|
|
{
|
|
LLPointer<LLImageRaw> nrm_image = new LLImageRaw(dst_image->getWidth(), dst_image->getHeight(), 4);
|
|
generateNormalMapFromAlpha(dst_image, nrm_image);
|
|
bump->setExplicitFormat(GL_RGBA, GL_RGBA);
|
|
bump->createGLTexture(0, nrm_image);
|
|
}
|
|
|
|
|
|
iter->second = bump; // derefs (and deletes) old image
|
|
//---------------------------------------------------
|
|
}
|
|
else
|
|
{
|
|
// entry should have been added in LLBumpImageList::getImage().
|
|
|
|
// Not a legit assertion - the bump texture could have been flushed by the bump image manager
|
|
//llassert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::renderBump(U32 type, U32 mask)
|
|
{
|
|
LLCullResult::drawinfo_list_t::iterator begin = gPipeline.beginRenderMap(type);
|
|
LLCullResult::drawinfo_list_t::iterator end = gPipeline.endRenderMap(type);
|
|
|
|
for (LLCullResult::drawinfo_list_t::iterator i = begin; i != end; ++i)
|
|
{
|
|
LLDrawInfo& params = **i;
|
|
|
|
if (LLDrawPoolBump::bindBumpMap(params))
|
|
{
|
|
pushBatch(params, mask, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture)
|
|
{
|
|
applyModelMatrix(params);
|
|
|
|
if (params.mTextureMatrix)
|
|
{
|
|
if (mShiny)
|
|
{
|
|
gGL.getTexUnit(0)->activate();
|
|
glMatrixMode(GL_TEXTURE);
|
|
}
|
|
else
|
|
{
|
|
gGL.getTexUnit(1)->activate();
|
|
glMatrixMode(GL_TEXTURE);
|
|
glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix);
|
|
gPipeline.mTextureMatrixOps++;
|
|
gGL.getTexUnit(0)->activate();
|
|
}
|
|
|
|
glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix);
|
|
gPipeline.mTextureMatrixOps++;
|
|
}
|
|
|
|
if (mShiny && mVertexShaderLevel > 1 && texture)
|
|
{
|
|
if (params.mTexture.notNull())
|
|
{
|
|
gGL.getTexUnit(diffuse_channel)->bind(params.mTexture) ;
|
|
params.mTexture->addTextureStats(params.mVSize);
|
|
}
|
|
else
|
|
{
|
|
gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE);
|
|
}
|
|
}
|
|
|
|
if (params.mGroup)
|
|
{
|
|
params.mGroup->rebuildMesh();
|
|
}
|
|
params.mVertexBuffer->setBuffer(mask);
|
|
params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset);
|
|
gPipeline.addTrianglesDrawn(params.mCount/3);
|
|
if (params.mTextureMatrix)
|
|
{
|
|
if (mShiny)
|
|
{
|
|
gGL.getTexUnit(0)->activate();
|
|
}
|
|
else
|
|
{
|
|
gGL.getTexUnit(1)->activate();
|
|
glLoadIdentity();
|
|
gGL.getTexUnit(0)->activate();
|
|
}
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolInvisible::render(S32 pass)
|
|
{ //render invisiprims
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_INVISIBLE);
|
|
|
|
U32 invisi_mask = LLVertexBuffer::MAP_VERTEX;
|
|
glStencilMask(0);
|
|
gGL.setColorMask(false, false);
|
|
pushBatches(LLRenderPass::PASS_INVISIBLE, invisi_mask, FALSE);
|
|
gGL.setColorMask(true, false);
|
|
glStencilMask(0xFFFFFFFF);
|
|
|
|
if (gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY))
|
|
{
|
|
beginShiny(true);
|
|
renderShiny(true);
|
|
endShiny(true);
|
|
}
|
|
}
|
|
|
|
void LLDrawPoolInvisible::beginDeferredPass(S32 pass)
|
|
{
|
|
beginRenderPass(pass);
|
|
}
|
|
|
|
void LLDrawPoolInvisible::endDeferredPass( S32 pass )
|
|
{
|
|
endRenderPass(pass);
|
|
}
|
|
|
|
void LLDrawPoolInvisible::renderDeferred( S32 pass )
|
|
{ //render invisiprims; this doesn't work becaue it also blocks all the post-deferred stuff
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_INVISIBLE);
|
|
|
|
U32 invisi_mask = LLVertexBuffer::MAP_VERTEX;
|
|
glStencilMask(0);
|
|
glStencilOp(GL_ZERO, GL_KEEP, GL_REPLACE);
|
|
gGL.setColorMask(false, false);
|
|
pushBatches(LLRenderPass::PASS_INVISIBLE, invisi_mask, FALSE);
|
|
gGL.setColorMask(true, true);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
|
glStencilMask(0xFFFFFFFF);
|
|
|
|
if (gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY))
|
|
{
|
|
beginShiny(true);
|
|
renderShiny(true);
|
|
endShiny(true);
|
|
}
|
|
}
|