Files
SingularityViewer/indra/newview/lldrawpoolbump.cpp
2010-04-02 02:48:44 -03:00

1305 lines
33 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 "llimagegl.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 "llviewerimagelist.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()
{
llassert( LLStandardBumpmap::sStandardBumpmapCount == 0 );
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_file[2048] = ""; /* Flawfinder: ignore */
fields_read = fscanf( /* Flawfinder: ignore */
file, "\n%2047s %2047s", label, bump_file);
if( EOF == fields_read )
{
break;
}
if( fields_read != 2 )
{
llwarns << "Bad LLStandardBumpmap entry" << llendl;
return;
}
// llinfos << "Loading bumpmap: " << bump_file << " from viewerart" << llendl;
gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label;
gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage =
gImageList.getImageFromFile(bump_file,
TRUE,
FALSE,
0,
0);
gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setLoadedCallback(LLBumpImageList::onSourceStandardLoaded, 0, TRUE, FALSE, NULL );
LLStandardBumpmap::sStandardBumpmapCount++;
}
fclose( file );
}
// static
void LLStandardBumpmap::destroyGL()
{
for( U32 i = 0; i < LLStandardBumpmap::sStandardBumpmapCount; i++ )
{
gStandardBumpmapList[i].mLabel.assign("");
gStandardBumpmapList[i].mImage = NULL;
}
sStandardBumpmapCount = 0;
}
////////////////////////////////////////////////////////////////
LLDrawPoolBump::LLDrawPoolBump()
: LLRenderPass(LLDrawPool::POOL_BUMP)
{
mShiny = FALSE;
}
void LLDrawPoolBump::prerender()
{
mVertexShaderLevel = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT);
}
// static
S32 LLDrawPoolBump::numBumpPasses()
{
if (gSavedSettings.getBOOL("RenderObjectBump"))
{
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)
{
LLImageGL* bump = NULL;
U8 bump_code = params.mBump;
LLViewerImage* tex = params.mTexture;
switch( bump_code )
{
case BE_NO_BUMP:
bump = NULL;
break;
case BE_BRIGHTNESS:
case BE_DARKNESS:
if( tex )
{
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::shutdown()
{
mBrightnessEntries.clear();
mDarknessEntries.clear();
LLStandardBumpmap::shutdown();
}
void LLBumpImageList::destroyGL()
{
mBrightnessEntries.clear();
mDarknessEntries.clear();
LLStandardBumpmap::destroyGL();
}
void LLBumpImageList::restoreGL()
{
// Images will be recreated as they are needed.
LLStandardBumpmap::restoreGL();
}
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;
LLViewerImage* 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++;
LLImageGL* image = curiter->second;
if( image )
{
BOOL destroy = TRUE;
if( image->getHasGLTexture())
{
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++;
LLImageGL* image = curiter->second;
if( image )
{
BOOL destroy = TRUE;
if( image->getHasGLTexture())
{
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.
LLImageGL* LLBumpImageList::getBrightnessDarknessImage(LLViewerImage* src_image, U8 bump_code )
{
llassert( (bump_code == BE_BRIGHTNESS) || (bump_code == BE_DARKNESS) );
LLImageGL* bump = NULL;
const F32 BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD = 1000;
if( src_image->mMaxVirtualSize > BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD )
{
bump_image_map_t* entries_list = NULL;
void (*callback_func)( BOOL success, LLViewerImage *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);
//------------------------------
bump = new LLImageGL( raw, TRUE);
//immediately assign bump to a global smart pointer in case some local smart pointer
//accidently releases it.
(*entries_list)[src_image->getID()] = bump;
//------------------------------
bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
// Note: this may create an LLImageGL immediately
src_image->setLoadedCallback( callback_func, 0, TRUE, FALSE, new LLUUID(src_image->getID()) );
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, LLViewerImage *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, LLViewerImage *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, LLViewerImage* 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(0, 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[(rY*resX+i)*src_cmp+src_cmp-1]-cH);
LLVector3 down = LLVector3(0, norm_scale, (F32) src_data[(lY*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, LLViewerImage *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
//accidently releases it.
LLPointer<LLImageGL> bump = new LLImageGL( TRUE);
if (!LLPipeline::sRenderDeferred)
{
bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
bump->createGLTexture(0, dst_image);
}
else
{
LLPointer<LLImageRaw> nrm_image = new LLImageRaw(src->getWidth(), src->getHeight(), 4);
generateNormalMapFromAlpha(src, 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.get());
//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);
}
}