714 lines
22 KiB
C++
714 lines
22 KiB
C++
/**
|
|
* @file llpostprocess.cpp
|
|
* @brief LLPostProcess class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2007-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 "linden_common.h"
|
|
|
|
#include "llpostprocess.h"
|
|
|
|
#include "lldir.h"
|
|
#include "llgl.h"
|
|
#include "llglslshader.h"
|
|
#include "llrender.h"
|
|
#include "llsdserialize.h"
|
|
#include "llsdutil.h"
|
|
#include "llsdutil_math.h"
|
|
#include "llvertexbuffer.h"
|
|
#include "llfasttimer.h"
|
|
#include "llmatrix4a.h"
|
|
|
|
extern LLGLSLShader gPostColorFilterProgram;
|
|
extern LLGLSLShader gPostNightVisionProgram;
|
|
extern LLGLSLShader gPostGaussianBlurProgram;
|
|
extern LLGLSLShader gPostPosterizeProgram;
|
|
extern LLGLSLShader gPostMotionBlurProgram;
|
|
extern LLGLSLShader gPostVignetteProgram;
|
|
|
|
static LLStaticHashedString sGamma("gamma");
|
|
static LLStaticHashedString sBrightness("brightness");
|
|
static LLStaticHashedString sContrast("contrast");
|
|
static LLStaticHashedString sContrastBase("contrastBase");
|
|
static LLStaticHashedString sSaturation("saturation");
|
|
static LLStaticHashedString sBrightMult("brightMult");
|
|
static LLStaticHashedString sNoiseStrength("noiseStrength");
|
|
static LLStaticHashedString sLayerCount("layerCount");
|
|
|
|
static LLStaticHashedString sVignetteStrength("vignette_strength");
|
|
static LLStaticHashedString sVignettRadius("vignette_radius");
|
|
static LLStaticHashedString sVignetteDarkness("vignette_darkness");
|
|
static LLStaticHashedString sVignetteDesaturation("vignette_desaturation");
|
|
static LLStaticHashedString sVignetteChromaticAberration("vignette_chromatic_aberration");
|
|
static LLStaticHashedString sScreenRes("screen_res");
|
|
|
|
static LLStaticHashedString sHorizontalPass("horizontalPass");
|
|
|
|
static LLStaticHashedString sPrevProj("prev_proj");
|
|
static LLStaticHashedString sInvProj("inv_proj");
|
|
static LLStaticHashedString sBlurStrength("blur_strength");
|
|
|
|
static const unsigned int NOISE_SIZE = 512;
|
|
|
|
static const char * const XML_FILENAME = "postprocesseffects.xml";
|
|
|
|
class LLPostProcessShader : public IPostProcessShader, public LLRefCount
|
|
{
|
|
public:
|
|
LLPostProcessShader(const std::string& enable_name, LLGLSLShader& shader, bool enabled = false) :
|
|
mShader(shader), mEnabled(enable_name,enabled)
|
|
{
|
|
addSetting(mEnabled);
|
|
}
|
|
/*virtual*/ bool isEnabled() const {return mShader.mProgramObject && mEnabled;}
|
|
/*virtual*/ void bindShader() {mShader.bind();}
|
|
/*virtual*/ void unbindShader() {mShader.unbind();}
|
|
/*virtual*/ LLGLSLShader& getShader() {return mShader;}
|
|
|
|
/*virtual*/ LLSD getDefaults(); //See IPostProcessShader::getDefaults
|
|
/*virtual*/ void loadSettings(const LLSD& settings); //See IPostProcessShader::loadSettings
|
|
/*virtual*/ void addSetting(IShaderSettingBase& setting) { mSettings.push_back(&setting); }
|
|
protected:
|
|
template<typename T>
|
|
struct LLShaderSetting : public IShaderSettingBase
|
|
{
|
|
LLShaderSetting(const std::string& name, T def) :
|
|
mValue(def), mDefault(def), mSettingName(name) {}
|
|
operator T() const { return mValue; }
|
|
T get() const { return mValue; }
|
|
/*virtual*/ const std::string& getName() const { return mSettingName; } //See LLShaderSettingBase::getName
|
|
/*virtual*/ LLSD getDefaultValue() const { return mDefault; } //See LLShaderSettingBase::getDefaultValue
|
|
/*virtual*/ void setValue(const LLSD& value) { mValue = value; } //See LLShaderSettingBase::setValue
|
|
private:
|
|
const std::string mSettingName; //LLSD key names as found in postprocesseffects.xml. eg 'contrast_base'
|
|
T mValue; //The member variable mentioned above.
|
|
T mDefault; //Set via ctor. Value is inserted into the defaults LLSD list if absent from postprocesseffects.xml
|
|
};
|
|
private:
|
|
std::vector<IShaderSettingBase*> mSettings; //Contains a list of all the 'settings' this shader uses. Manually add via push_back in ctor.
|
|
LLGLSLShader& mShader;
|
|
LLShaderSetting<bool> mEnabled;
|
|
};
|
|
|
|
//helpers
|
|
class LLPostProcessSinglePassColorShader : public LLPostProcessShader
|
|
{
|
|
public:
|
|
LLPostProcessSinglePassColorShader(const std::string& enable_name, LLGLSLShader& shader, bool enabled = false) :
|
|
LLPostProcessShader(enable_name, shader, enabled) {}
|
|
/*virtual*/ S32 getColorChannel() const {return 0;}
|
|
/*virtual*/ S32 getDepthChannel() const {return -1;}
|
|
/*virtual*/ bool draw(U32 pass) {return pass == 1;}
|
|
/*virtual*/ void postDraw() {}
|
|
};
|
|
|
|
template<> LLSD LLPostProcessShader::LLShaderSetting<LLVector4>::getDefaultValue() const
|
|
{
|
|
return mDefault.getValue();
|
|
}
|
|
template<> void LLPostProcessShader::LLShaderSetting<LLVector4>::setValue(const LLSD& value)
|
|
{
|
|
mValue = ll_vector4_from_sd(value);
|
|
}
|
|
|
|
LLSD LLPostProcessShader::getDefaults()
|
|
{
|
|
LLSD defaults;
|
|
for(std::vector<IShaderSettingBase*>::iterator it=mSettings.begin();it!=mSettings.end();++it)
|
|
{
|
|
defaults[(*it)->getName()]=(*it)->getDefaultValue();
|
|
}
|
|
return defaults;
|
|
}
|
|
void LLPostProcessShader::loadSettings(const LLSD& settings)
|
|
{
|
|
for(std::vector<IShaderSettingBase*>::iterator it=mSettings.begin();it!=mSettings.end();++it)
|
|
{
|
|
LLSD value = settings[(*it)->getName()];
|
|
(*it)->setValue(value);
|
|
}
|
|
}
|
|
|
|
class LLColorFilterShader : public LLPostProcessSinglePassColorShader
|
|
{
|
|
private:
|
|
LLShaderSetting<F32> mGamma, mBrightness, mContrast, mSaturation;
|
|
LLShaderSetting<LLVector4> mContrastBase;
|
|
public:
|
|
LLColorFilterShader() :
|
|
LLPostProcessSinglePassColorShader("enable_color_filter",gPostColorFilterProgram),
|
|
mGamma("gamma",1.f),
|
|
mBrightness("brightness",1.f),
|
|
mContrast("contrast",1.f),
|
|
mSaturation("saturation",1.f),
|
|
mContrastBase("contrast_base",LLVector4(1.f,1.f,1.f,0.5f))
|
|
{
|
|
addSetting(mGamma);
|
|
addSetting(mBrightness);
|
|
addSetting(mContrast);
|
|
addSetting(mSaturation);
|
|
addSetting(mContrastBase);
|
|
}
|
|
|
|
/*virtual*/ QuadType preDraw()
|
|
{
|
|
getShader().uniform1f(sGamma, mGamma);
|
|
getShader().uniform1f(sBrightness, mBrightness);
|
|
getShader().uniform1f(sContrast, mContrast);
|
|
float baseI = (mContrastBase.get()[VX] + mContrastBase.get()[VY] + mContrastBase.get()[VZ]) / 3.0f;
|
|
baseI = mContrastBase.get()[VW] / llmax(baseI,0.001f);
|
|
float baseR = mContrastBase.get()[VX] * baseI;
|
|
float baseG = mContrastBase.get()[VY] * baseI;
|
|
float baseB = mContrastBase.get()[VZ] * baseI;
|
|
getShader().uniform3fv(sContrastBase, 1, LLVector3(baseR, baseG, baseB).mV);
|
|
getShader().uniform1f(sSaturation, mSaturation);
|
|
|
|
return QUAD_NORMAL;
|
|
}
|
|
};
|
|
|
|
class LLNightVisionShader : public LLPostProcessSinglePassColorShader
|
|
{
|
|
private:
|
|
LLShaderSetting<F32> mBrightnessMult, mNoiseStrength;
|
|
public:
|
|
LLNightVisionShader() :
|
|
LLPostProcessSinglePassColorShader("enable_night_vision",gPostNightVisionProgram),
|
|
mBrightnessMult("brightness_multiplier",3.f),
|
|
mNoiseStrength("noise_strength",.4f)
|
|
{
|
|
addSetting(mBrightnessMult);
|
|
addSetting(mNoiseStrength);
|
|
}
|
|
/*virtual*/ QuadType preDraw()
|
|
{
|
|
LLPostProcess::getInstance()->bindNoise(1);
|
|
|
|
getShader().uniform1f(sBrightMult, mBrightnessMult);
|
|
getShader().uniform1f(sNoiseStrength, mNoiseStrength);
|
|
|
|
return QUAD_NOISE;
|
|
}
|
|
};
|
|
|
|
class LLPosterizeShader : public LLPostProcessSinglePassColorShader
|
|
{
|
|
private:
|
|
LLShaderSetting<S32> mNumLayers;
|
|
public:
|
|
LLPosterizeShader() : LLPostProcessSinglePassColorShader("enable_posterize",gPostPosterizeProgram),
|
|
mNumLayers("posterize_layers",2)
|
|
{
|
|
addSetting(mNumLayers);
|
|
}
|
|
/*virtual*/ QuadType preDraw()
|
|
{
|
|
getShader().uniform1i(sLayerCount, mNumLayers);
|
|
return QUAD_NORMAL;
|
|
}
|
|
};
|
|
|
|
class LLVignetteShader : public LLPostProcessSinglePassColorShader
|
|
{
|
|
private:
|
|
LLShaderSetting<F32> mStrength, mRadius, mDarkness, mDesaturation, mChromaticAberration;
|
|
public:
|
|
LLVignetteShader() : LLPostProcessSinglePassColorShader("enable_vignette",gPostVignetteProgram),
|
|
mStrength("vignette_strength",.85f),
|
|
mRadius("vignette_radius",.7f),
|
|
mDarkness("vignette_darkness",1.f),
|
|
mDesaturation("vignette_desaturation",1.5f),
|
|
mChromaticAberration("vignette_chromatic_aberration",.05f)
|
|
{
|
|
addSetting(mStrength);
|
|
addSetting(mRadius);
|
|
addSetting(mDarkness);
|
|
addSetting(mDesaturation);
|
|
addSetting(mChromaticAberration);
|
|
}
|
|
/*virtual*/ QuadType preDraw()
|
|
{
|
|
LLVector2 screen_rect = LLPostProcess::getInstance()->getDimensions();
|
|
|
|
getShader().uniform1f(sVignetteStrength, mStrength);
|
|
getShader().uniform1f(sVignettRadius, mRadius);
|
|
getShader().uniform1f(sVignetteDarkness, mDarkness);
|
|
getShader().uniform1f(sVignetteDesaturation, mDesaturation);
|
|
getShader().uniform1f(sVignetteChromaticAberration, mChromaticAberration);
|
|
getShader().uniform2fv(sScreenRes, 1, screen_rect.mV);
|
|
return QUAD_NORMAL;
|
|
}
|
|
};
|
|
|
|
class LLGaussBlurShader : public LLPostProcessShader
|
|
{
|
|
private:
|
|
LLShaderSetting<S32> mNumPasses;
|
|
GLint mPassLoc;
|
|
public:
|
|
LLGaussBlurShader() : LLPostProcessShader("enable_gauss_blur",gPostGaussianBlurProgram),
|
|
mNumPasses("gauss_blur_passes",2),
|
|
mPassLoc(0)
|
|
{
|
|
addSetting(mNumPasses);
|
|
}
|
|
/*virtual*/ bool isEnabled() const { return LLPostProcessShader::isEnabled() && mNumPasses.get(); }
|
|
/*virtual*/ S32 getColorChannel() const { return 0; }
|
|
/*virtual*/ S32 getDepthChannel() const { return -1; }
|
|
/*virtual*/ QuadType preDraw()
|
|
{
|
|
mPassLoc = getShader().getUniformLocation(sHorizontalPass);
|
|
return QUAD_NORMAL;
|
|
}
|
|
/*virtual*/ bool draw(U32 pass)
|
|
{
|
|
if((S32)pass > mNumPasses*2)
|
|
return false;
|
|
glUniform1iARB(mPassLoc, (pass-1) % 2);
|
|
return true;
|
|
}
|
|
/*virtual*/ void postDraw() {}
|
|
};
|
|
|
|
class LLMotionShader : public LLPostProcessShader
|
|
{
|
|
private:
|
|
LLShaderSetting<S32> mStrength;
|
|
public:
|
|
LLMotionShader() : LLPostProcessShader("enable_motionblur",gPostMotionBlurProgram),
|
|
mStrength("blur_strength",1)
|
|
{
|
|
addSetting(mStrength);
|
|
}
|
|
/*virtual*/ bool isEnabled() const { return LLPostProcessShader::isEnabled() && llabs(gGLModelView.getF32ptr()[0] - gGLPreviousModelView.getF32ptr()[0]) > .0000001; }
|
|
/*virtual*/ S32 getColorChannel() const { return 0; }
|
|
/*virtual*/ S32 getDepthChannel() const { return 1; }
|
|
/*virtual*/ QuadType preDraw()
|
|
{
|
|
glh::matrix4f inv_proj(gGLModelView.getF32ptr());
|
|
inv_proj.mult_left(gGLProjection.getF32ptr());
|
|
inv_proj = inv_proj.inverse();
|
|
glh::matrix4f prev_proj(gGLPreviousModelView.getF32ptr());
|
|
prev_proj.mult_left(gGLProjection.getF32ptr());
|
|
|
|
LLVector2 screen_rect = LLPostProcess::getInstance()->getDimensions();
|
|
|
|
getShader().uniformMatrix4fv(sPrevProj, 1, GL_FALSE, prev_proj.m);
|
|
getShader().uniformMatrix4fv(sInvProj, 1, GL_FALSE, inv_proj.m);
|
|
getShader().uniform2fv(sScreenRes, 1, screen_rect.mV);
|
|
getShader().uniform1i(sBlurStrength, mStrength);
|
|
|
|
return QUAD_NORMAL;
|
|
}
|
|
/*virtual*/ bool draw(U32 pass)
|
|
{
|
|
return pass == 1;
|
|
}
|
|
/*virtual*/ void postDraw() {}
|
|
};
|
|
|
|
LLPostProcess::LLPostProcess(void) :
|
|
mVBO(NULL),
|
|
mDepthTexture(0),
|
|
mNoiseTexture(0),
|
|
mScreenWidth(0),
|
|
mScreenHeight(0),
|
|
mNoiseTextureScale(0.f),
|
|
mSelectedEffectInfo(LLSD::emptyMap()),
|
|
mAllEffectInfo(LLSD::emptyMap())
|
|
{
|
|
mShaders.push_back(new LLMotionShader());
|
|
mShaders.push_back(new LLVignetteShader());
|
|
mShaders.push_back(new LLColorFilterShader());
|
|
mShaders.push_back(new LLNightVisionShader());
|
|
mShaders.push_back(new LLGaussBlurShader());
|
|
mShaders.push_back(new LLPosterizeShader());
|
|
|
|
/* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.*/
|
|
std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME));
|
|
LL_DEBUGS2("AppInit", "Shaders") << "Loading PostProcess Effects settings from " << pathName << LL_ENDL;
|
|
|
|
llifstream effectsXML(pathName);
|
|
|
|
if (effectsXML)
|
|
{
|
|
LLPointer<LLSDParser> parser = new LLSDXMLParser();
|
|
|
|
parser->parse(effectsXML, mAllEffectInfo, LLSDSerialize::SIZE_UNLIMITED);
|
|
}
|
|
|
|
if (!mAllEffectInfo.has("default"))
|
|
mAllEffectInfo["default"] = LLSD::emptyMap();
|
|
|
|
LLSD& defaults = mAllEffectInfo["default"];
|
|
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
LLSD shader_defaults = (*it)->getDefaults();
|
|
for (LLSD::map_const_iterator it2 = defaults.beginMap();it2 != defaults.endMap();++it2)
|
|
{
|
|
if(!defaults.has(it2->first))
|
|
defaults[it2->first]=it2->second;
|
|
}
|
|
}
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
(*it)->loadSettings(defaults);
|
|
}
|
|
setSelectedEffect("default");
|
|
}
|
|
|
|
LLPostProcess::~LLPostProcess(void)
|
|
{
|
|
destroyGL() ;
|
|
}
|
|
|
|
void LLPostProcess::initialize(unsigned int width, unsigned int height)
|
|
{
|
|
destroyGL();
|
|
mScreenWidth = width;
|
|
mScreenHeight = height;
|
|
|
|
createScreenTextures();
|
|
createNoiseTexture();
|
|
|
|
//Setup our VBO.
|
|
{
|
|
mVBO = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1,GL_STREAM_DRAW_ARB);
|
|
mVBO->allocateBuffer(4,0,TRUE);
|
|
|
|
LLStrider<LLVector3> v;
|
|
LLStrider<LLVector2> uv1;
|
|
LLStrider<LLVector2> uv2;
|
|
|
|
mVBO->getVertexStrider(v);
|
|
mVBO->getTexCoord0Strider(uv1);
|
|
mVBO->getTexCoord1Strider(uv2);
|
|
|
|
v[0] = LLVector3( uv2[0] = uv1[0] = LLVector2(0, 0) );
|
|
v[1] = LLVector3( uv2[1] = uv1[1] = LLVector2(0, mScreenHeight) );
|
|
v[2] = LLVector3( uv2[2] = uv1[2] = LLVector2(mScreenWidth, 0) );
|
|
v[3] = LLVector3( uv2[3] = uv1[3] = LLVector2(mScreenWidth, mScreenHeight) );
|
|
|
|
mVBO->flush();
|
|
}
|
|
stop_glerror();
|
|
}
|
|
|
|
void LLPostProcess::createScreenTextures()
|
|
{
|
|
const LLTexUnit::eTextureType type = LLTexUnit::TT_RECT_TEXTURE;
|
|
|
|
mRenderTarget[0].allocate(mScreenWidth,mScreenHeight,GL_RGBA,FALSE,FALSE,type,FALSE);
|
|
if(mRenderTarget[0].getFBO())//Only need pingpong between two rendertargets if FBOs are supported.
|
|
mRenderTarget[1].allocate(mScreenWidth,mScreenHeight,GL_RGBA,FALSE,FALSE,type,FALSE);
|
|
stop_glerror();
|
|
|
|
if(mDepthTexture)
|
|
{
|
|
LLImageGL::deleteTextures(1, &mDepthTexture);
|
|
mDepthTexture = 0;
|
|
}
|
|
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
if((*it)->getDepthChannel()>=0)
|
|
{
|
|
LLImageGL::generateTextures(1, &mDepthTexture);
|
|
gGL.getTexUnit(0)->bindManual(type, mDepthTexture);
|
|
LLImageGL::setManualImage(LLTexUnit::getInternalType(type), 0, GL_DEPTH_COMPONENT24, mScreenWidth, mScreenHeight, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false);
|
|
stop_glerror();
|
|
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
|
|
gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
|
|
stop_glerror();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLPostProcess::createNoiseTexture()
|
|
{
|
|
std::vector<GLubyte> buffer(NOISE_SIZE * NOISE_SIZE);
|
|
for (unsigned int i = 0; i < NOISE_SIZE; i++){
|
|
for (unsigned int k = 0; k < NOISE_SIZE; k++){
|
|
buffer[(i * NOISE_SIZE) + k] = (GLubyte)((double) rand() / ((double) RAND_MAX + 1.f) * 255.f);
|
|
}
|
|
}
|
|
|
|
if(mNoiseTexture)
|
|
{
|
|
LLImageGL::deleteTextures(1, &mNoiseTexture);
|
|
mNoiseTexture = 0;
|
|
}
|
|
|
|
LLImageGL::generateTextures(1, &mNoiseTexture);
|
|
stop_glerror();
|
|
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mNoiseTexture);
|
|
stop_glerror();
|
|
|
|
LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE8, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0], false);
|
|
|
|
stop_glerror();
|
|
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
|
|
gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP);
|
|
stop_glerror();
|
|
}
|
|
|
|
void LLPostProcess::destroyGL()
|
|
{
|
|
mRenderTarget[0].release();
|
|
mRenderTarget[1].release();
|
|
if(mDepthTexture)
|
|
LLImageGL::deleteTextures(1, &mDepthTexture);
|
|
mDepthTexture=0;
|
|
if(mNoiseTexture)
|
|
LLImageGL::deleteTextures(1, &mNoiseTexture);
|
|
mNoiseTexture=0 ;
|
|
mVBO = NULL ;
|
|
}
|
|
|
|
/*static*/void LLPostProcess::cleanupClass()
|
|
{
|
|
if(instanceExists())
|
|
getInstance()->destroyGL() ;
|
|
}
|
|
|
|
void LLPostProcess::copyFrameBuffer()
|
|
{
|
|
mRenderTarget[!!mRenderTarget[0].getFBO()].bindTexture(0,0);
|
|
glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,0,0,mScreenWidth, mScreenHeight);
|
|
stop_glerror();
|
|
|
|
if(mDepthTexture)
|
|
{
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
if((*it)->isEnabled() && (*it)->getDepthChannel()>=0)
|
|
{
|
|
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, mDepthTexture);
|
|
glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,0,0,mScreenWidth, mScreenHeight);
|
|
stop_glerror();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void LLPostProcess::bindNoise(U32 channel)
|
|
{
|
|
gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE,mNoiseTexture);
|
|
}
|
|
|
|
void LLPostProcess::renderEffects(unsigned int width, unsigned int height)
|
|
{
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
if((*it)->isEnabled())
|
|
{
|
|
if (mVBO.isNull() || width != mScreenWidth || height != mScreenHeight)
|
|
{
|
|
initialize(width, height);
|
|
}
|
|
doEffects();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLPostProcess::doEffects(void)
|
|
{
|
|
LLVertexBuffer::unbind();
|
|
|
|
mNoiseTextureScale = (1.f - (mSelectedEffectInfo["noise_size"].asFloat() - 1.f) *(9.f/990.f)) / (float)NOISE_SIZE;
|
|
|
|
/// Copy the screen buffer to the render texture
|
|
copyFrameBuffer();
|
|
stop_glerror();
|
|
|
|
//Disable depth. Set blendmode to replace.
|
|
LLGLDepthTest depth(GL_FALSE,GL_FALSE);
|
|
LLGLEnable blend(GL_BLEND);
|
|
gGL.setSceneBlendType(LLRender::BT_REPLACE);
|
|
|
|
/// Change to an orthogonal view
|
|
gGL.matrixMode(LLRender::MM_PROJECTION);
|
|
gGL.pushMatrix();
|
|
gGL.loadIdentity();
|
|
gGL.ortho( 0.f, (GLdouble) mScreenWidth, 0.f, (GLdouble) mScreenHeight, -1.f, 1.f );
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
gGL.pushMatrix();
|
|
gGL.loadIdentity();
|
|
|
|
applyShaders();
|
|
|
|
LLGLSLShader::bindNoShader();
|
|
|
|
/// Change to a perspective view
|
|
gGL.flush();
|
|
gGL.matrixMode( LLRender::MM_PROJECTION );
|
|
gGL.popMatrix();
|
|
gGL.matrixMode( LLRender::MM_MODELVIEW );
|
|
gGL.popMatrix();
|
|
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA); //Restore blendstate. Alpha is ASSUMED for hud/ui render, etc.
|
|
|
|
gGL.getTexUnit(1)->disable();
|
|
}
|
|
|
|
void LLPostProcess::applyShaders(void)
|
|
{
|
|
bool primary_rendertarget = 1;
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
if((*it)->isEnabled())
|
|
{
|
|
S32 color_channel = (*it)->getColorChannel();
|
|
S32 depth_channel = (*it)->getDepthChannel();
|
|
if(depth_channel >= 0)
|
|
gGL.getTexUnit(depth_channel)->bindManual(LLTexUnit::TT_RECT_TEXTURE, mDepthTexture);
|
|
U32 pass = 1;
|
|
|
|
(*it)->bindShader();
|
|
QuadType quad = (*it)->preDraw();
|
|
while((*it)->draw(pass++))
|
|
{
|
|
LLRenderTarget& write_target = mRenderTarget[!primary_rendertarget];
|
|
LLRenderTarget& read_target = mRenderTarget[mRenderTarget[0].getFBO() ? primary_rendertarget : !primary_rendertarget];
|
|
write_target.bindTarget();
|
|
|
|
if(color_channel >= 0)
|
|
read_target.bindTexture(0,color_channel);
|
|
|
|
drawOrthoQuad(quad);
|
|
|
|
if(color_channel >= 0 && !mRenderTarget[0].getFBO())
|
|
gGL.getTexUnit(color_channel)->unbind(read_target.getUsage());
|
|
|
|
write_target.flush();
|
|
if(mRenderTarget[0].getFBO())
|
|
primary_rendertarget = !primary_rendertarget;
|
|
}
|
|
(*it)->postDraw();
|
|
(*it)->unbindShader();
|
|
}
|
|
}
|
|
//Only need to copy to framebuffer if FBOs are supported, else we've already been drawing to the framebuffer to begin with.
|
|
if(mRenderTarget[0].getFBO())
|
|
{
|
|
//copyContentsToFramebuffer also binds the main framebuffer.
|
|
LLRenderTarget::copyContentsToFramebuffer(mRenderTarget[primary_rendertarget],0,0,mScreenWidth,mScreenHeight,0,0,mScreenWidth,mScreenHeight,GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
}
|
|
stop_glerror();
|
|
}
|
|
|
|
void LLPostProcess::drawOrthoQuad(QuadType type)
|
|
{
|
|
if(type == QUAD_NOISE)
|
|
{
|
|
//This could also be done through uniforms.
|
|
LLStrider<LLVector2> uv2;
|
|
mVBO->getTexCoord1Strider(uv2);
|
|
|
|
float offs[2] = {
|
|
llround(((float) rand() / (float) RAND_MAX) * (float)NOISE_SIZE)/float(NOISE_SIZE),
|
|
llround(((float) rand() / (float) RAND_MAX) * (float)NOISE_SIZE)/float(NOISE_SIZE) };
|
|
float scale[2] = {
|
|
(float)mScreenWidth * mNoiseTextureScale,
|
|
(float)mScreenHeight * mNoiseTextureScale };
|
|
|
|
uv2[0] = LLVector2(offs[0],offs[1]);
|
|
uv2[1] = LLVector2(offs[0],offs[1]+scale[1]);
|
|
uv2[2] = LLVector2(offs[0]+scale[0],offs[1]);
|
|
uv2[3] = LLVector2(uv2[2].mV[0],uv2[1].mV[1]);
|
|
mVBO->flush();
|
|
}
|
|
|
|
U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | (type == QUAD_NOISE ? LLVertexBuffer::MAP_TEXCOORD1 : 0);
|
|
mVBO->setBuffer(mask);
|
|
mVBO->drawArrays(LLRender::TRIANGLE_STRIP, 0, 4);
|
|
}
|
|
|
|
void LLPostProcess::setSelectedEffect(std::string const & effectName)
|
|
{
|
|
mSelectedEffectName = effectName;
|
|
mSelectedEffectInfo = mAllEffectInfo[effectName];
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
(*it)->loadSettings(mSelectedEffectInfo);
|
|
}
|
|
}
|
|
|
|
void LLPostProcess::setSelectedEffectValue(std::string const & setting, LLSD value)
|
|
{
|
|
char buf[256];
|
|
S32 elem=0;
|
|
if(sscanf(setting.c_str(),"%255[^[][%d]", buf, &elem) == 2)
|
|
{
|
|
mSelectedEffectInfo[static_cast<const char*>(buf)][elem] = value;
|
|
}
|
|
else
|
|
{
|
|
mSelectedEffectInfo[setting] = value;
|
|
}
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
(*it)->loadSettings(mSelectedEffectInfo);
|
|
}
|
|
}
|
|
|
|
void LLPostProcess::resetSelectedEffect()
|
|
{
|
|
if(!llsd_equals(mAllEffectInfo[mSelectedEffectName], mSelectedEffectInfo))
|
|
{
|
|
mSelectedEffectInfo = mAllEffectInfo[mSelectedEffectName];
|
|
for(std::list<LLPointer<LLPostProcessShader> >::iterator it=mShaders.begin();it!=mShaders.end();++it)
|
|
{
|
|
(*it)->loadSettings(mSelectedEffectInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLPostProcess::saveEffectAs(std::string const & effectName)
|
|
{
|
|
mAllEffectInfo[effectName] = mSelectedEffectInfo;
|
|
|
|
std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME));
|
|
//llinfos << "Saving PostProcess Effects settings to " << pathName << llendl;
|
|
|
|
llofstream effectsXML(pathName);
|
|
|
|
LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
|
|
|
|
formatter->format(mAllEffectInfo, effectsXML);
|
|
}
|
|
|
|
|
|
|