diff options
Diffstat (limited to 'indra/llrender')
-rw-r--r-- | indra/llrender/llpostprocess.cpp | 1190 |
1 files changed, 595 insertions, 595 deletions
diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp index c51f85ab71..4c36185b08 100644 --- a/indra/llrender/llpostprocess.cpp +++ b/indra/llrender/llpostprocess.cpp @@ -1,595 +1,595 @@ -/**
- * @file llpostprocess.cpp
- * @brief LLPostProcess class implementation
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llpostprocess.h"
-#include "llglslshader.h"
-#include "llsdserialize.h"
-#include "llrender.h"
-
-static LLStaticHashedString sRenderTexture("RenderTexture");
-static LLStaticHashedString sBrightness("brightness");
-static LLStaticHashedString sContrast("contrast");
-static LLStaticHashedString sContrastBase("contrastBase");
-static LLStaticHashedString sSaturation("saturation");
-static LLStaticHashedString sLumWeights("lumWeights");
-static LLStaticHashedString sNoiseTexture("NoiseTexture");
-static LLStaticHashedString sBrightMult("brightMult");
-static LLStaticHashedString sNoiseStrength("noiseStrength");
-static LLStaticHashedString sExtractLow("extractLow");
-static LLStaticHashedString sExtractHigh("extractHigh");
-static LLStaticHashedString sBloomStrength("bloomStrength");
-static LLStaticHashedString sTexelSize("texelSize");
-static LLStaticHashedString sBlurDirection("blurDirection");
-static LLStaticHashedString sBlurWidth("blurWidth");
-
-LLPostProcess * gPostProcess = NULL;
-
-
-static const unsigned int NOISE_SIZE = 512;
-
-/// CALCULATING LUMINANCE (Using NTSC lum weights)
-/// http://en.wikipedia.org/wiki/Luma_%28video%29
-static const float LUMINANCE_R = 0.299f;
-static const float LUMINANCE_G = 0.587f;
-static const float LUMINANCE_B = 0.114f;
-
-static const char * const XML_FILENAME = "postprocesseffects.xml";
-
-LLPostProcess::LLPostProcess(void) :
- initialized(false),
- mAllEffects(LLSD::emptyMap()),
- screenW(1), screenH(1)
-{
- mSceneRenderTexture = NULL ;
- mNoiseTexture = NULL ;
- mTempBloomTexture = NULL ;
-
- noiseTextureScale = 1.0f;
-
- /* 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, mAllEffects, LLSDSerialize::SIZE_UNLIMITED);
- }
-
- if (!mAllEffects.has("default"))
- {
- LLSD & defaultEffect = (mAllEffects["default"] = LLSD::emptyMap());
-
- defaultEffect["enable_night_vision"] = LLSD::Boolean(false);
- defaultEffect["enable_bloom"] = LLSD::Boolean(false);
- defaultEffect["enable_color_filter"] = LLSD::Boolean(false);
-
- /// NVG Defaults
- defaultEffect["brightness_multiplier"] = 3.0;
- defaultEffect["noise_size"] = 25.0;
- defaultEffect["noise_strength"] = 0.4;
-
- // TODO BTest potentially add this to tweaks?
- noiseTextureScale = 1.0f;
-
- /// Bloom Defaults
- defaultEffect["extract_low"] = 0.95;
- defaultEffect["extract_high"] = 1.0;
- defaultEffect["bloom_width"] = 2.25;
- defaultEffect["bloom_strength"] = 1.5;
-
- /// Color Filter Defaults
- defaultEffect["brightness"] = 1.0;
- defaultEffect["contrast"] = 1.0;
- defaultEffect["saturation"] = 1.0;
-
- LLSD& contrastBase = (defaultEffect["contrast_base"] = LLSD::emptyArray());
- contrastBase.append(1.0);
- contrastBase.append(1.0);
- contrastBase.append(1.0);
- contrastBase.append(0.5);
- }
-
- setSelectedEffect("default");
- */
-}
-
-LLPostProcess::~LLPostProcess(void)
-{
- invalidate() ;
-}
-
-// static
-void LLPostProcess::initClass(void)
-{
- //this will cause system to crash at second time login
- //if first time login fails due to network connection --- bao
- //***llassert_always(gPostProcess == NULL);
- //replaced by the following line:
- if(gPostProcess)
- return ;
-
-
- gPostProcess = new LLPostProcess();
-}
-
-// static
-void LLPostProcess::cleanupClass()
-{
- delete gPostProcess;
- gPostProcess = NULL;
-}
-
-void LLPostProcess::setSelectedEffect(std::string const & effectName)
-{
- mSelectedEffectName = effectName;
- static_cast<LLSD &>(tweaks) = mAllEffects[effectName];
-}
-
-void LLPostProcess::saveEffect(std::string const & effectName)
-{
- /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.
- mAllEffects[effectName] = tweaks;
-
- 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(mAllEffects, effectsXML);
- */
-}
-void LLPostProcess::invalidate()
-{
- mSceneRenderTexture = NULL ;
- mNoiseTexture = NULL ;
- mTempBloomTexture = NULL ;
- initialized = FALSE ;
-}
-
-void LLPostProcess::apply(unsigned int width, unsigned int height)
-{
- if (!initialized || width != screenW || height != screenH){
- initialize(width, height);
- }
- if (shadersEnabled()){
- doEffects();
- }
-}
-
-void LLPostProcess::initialize(unsigned int width, unsigned int height)
-{
- screenW = width;
- screenH = height;
- createTexture(mSceneRenderTexture, screenW, screenH);
- initialized = true;
-
- checkError();
- createNightVisionShader();
- createBloomShader();
- createColorFilterShader();
- checkError();
-}
-
-inline bool LLPostProcess::shadersEnabled(void)
-{
- return (tweaks.useColorFilter().asBoolean() ||
- tweaks.useNightVisionShader().asBoolean() ||
- tweaks.useBloomShader().asBoolean() );
-
-}
-
-void LLPostProcess::applyShaders(void)
-{
- if (tweaks.useColorFilter()){
- applyColorFilterShader();
- checkError();
- }
- if (tweaks.useNightVisionShader()){
- /// If any of the above shaders have been called update the frame buffer;
- if (tweaks.useColorFilter())
- {
- U32 tex = mSceneRenderTexture->getTexName() ;
- copyFrameBuffer(tex, screenW, screenH);
- }
- applyNightVisionShader();
- checkError();
- }
- if (tweaks.useBloomShader()){
- /// If any of the above shaders have been called update the frame buffer;
- if (tweaks.useColorFilter().asBoolean() || tweaks.useNightVisionShader().asBoolean())
- {
- U32 tex = mSceneRenderTexture->getTexName() ;
- copyFrameBuffer(tex, screenW, screenH);
- }
- applyBloomShader();
- checkError();
- }
-}
-
-void LLPostProcess::applyColorFilterShader(void)
-{
- /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.
- gPostColorFilterProgram.bind();
-
- gGL.getTexUnit(0)->activate();
- gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE);
-
- gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture);
-
- getShaderUniforms(colorFilterUniforms, gPostColorFilterProgram.mProgramObject);
- glUniform1iARB(colorFilterUniforms["RenderTexture"], 0);
- glUniform1fARB(colorFilterUniforms["brightness"], tweaks.getBrightness());
- glUniform1fARB(colorFilterUniforms["contrast"], tweaks.getContrast());
- float baseI = (tweaks.getContrastBaseR() + tweaks.getContrastBaseG() + tweaks.getContrastBaseB()) / 3.0f;
- baseI = tweaks.getContrastBaseIntensity() / ((baseI < 0.001f) ? 0.001f : baseI);
- float baseR = tweaks.getContrastBaseR() * baseI;
- float baseG = tweaks.getContrastBaseG() * baseI;
- float baseB = tweaks.getContrastBaseB() * baseI;
- glUniform3fARB(colorFilterUniforms["contrastBase"], baseR, baseG, baseB);
- glUniform1fARB(colorFilterUniforms["saturation"], tweaks.getSaturation());
- glUniform3fARB(colorFilterUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B);
-
- LLGLEnable blend(GL_BLEND);
- gGL.setSceneBlendType(LLRender::BT_REPLACE);
- LLGLDepthTest depth(GL_FALSE);
-
- /// Draw a screen space quad
- drawOrthoQuad(screenW, screenH, QUAD_NORMAL);
- gPostColorFilterProgram.unbind();
- */
-}
-
-void LLPostProcess::createColorFilterShader(void)
-{
- /// Define uniform names
- colorFilterUniforms[sRenderTexture] = 0;
- colorFilterUniforms[sBrightness] = 0;
- colorFilterUniforms[sContrast] = 0;
- colorFilterUniforms[sContrastBase] = 0;
- colorFilterUniforms[sSaturation] = 0;
- colorFilterUniforms[sLumWeights] = 0;
-}
-
-void LLPostProcess::applyNightVisionShader(void)
-{
- /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.
- gPostNightVisionProgram.bind();
-
- gGL.getTexUnit(0)->activate();
- gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE);
-
- getShaderUniforms(nightVisionUniforms, gPostNightVisionProgram.mProgramObject);
- gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture);
- glUniform1iARB(nightVisionUniforms["RenderTexture"], 0);
-
- gGL.getTexUnit(1)->activate();
- gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE);
-
- gGL.getTexUnit(1)->bindManual(LLTexUnit::TT_TEXTURE, noiseTexture);
- glUniform1iARB(nightVisionUniforms["NoiseTexture"], 1);
-
-
- glUniform1fARB(nightVisionUniforms["brightMult"], tweaks.getBrightMult());
- glUniform1fARB(nightVisionUniforms["noiseStrength"], tweaks.getNoiseStrength());
- noiseTextureScale = 0.01f + ((101.f - tweaks.getNoiseSize()) / 100.f);
- noiseTextureScale *= (screenH / NOISE_SIZE);
-
-
- glUniform3fARB(nightVisionUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B);
-
- LLGLEnable blend(GL_BLEND);
- gGL.setSceneBlendType(LLRender::BT_REPLACE);
- LLGLDepthTest depth(GL_FALSE);
-
- /// Draw a screen space quad
- drawOrthoQuad(screenW, screenH, QUAD_NOISE);
- gPostNightVisionProgram.unbind();
- gGL.getTexUnit(0)->activate();
- */
-}
-
-void LLPostProcess::createNightVisionShader(void)
-{
- /// Define uniform names
- nightVisionUniforms[sRenderTexture] = 0;
- nightVisionUniforms[sNoiseTexture] = 0;
- nightVisionUniforms[sBrightMult] = 0;
- nightVisionUniforms[sNoiseStrength] = 0;
- nightVisionUniforms[sLumWeights] = 0;
-
- createNoiseTexture(mNoiseTexture);
-}
-
-void LLPostProcess::applyBloomShader(void)
-{
-
-}
-
-void LLPostProcess::createBloomShader(void)
-{
- createTexture(mTempBloomTexture, unsigned(screenW * 0.5), unsigned(screenH * 0.5));
-
- /// Create Bloom Extract Shader
- bloomExtractUniforms[sRenderTexture] = 0;
- bloomExtractUniforms[sExtractLow] = 0;
- bloomExtractUniforms[sExtractHigh] = 0;
- bloomExtractUniforms[sLumWeights] = 0;
-
- /// Create Bloom Blur Shader
- bloomBlurUniforms[sRenderTexture] = 0;
- bloomBlurUniforms[sBloomStrength] = 0;
- bloomBlurUniforms[sTexelSize] = 0;
- bloomBlurUniforms[sBlurDirection] = 0;
- bloomBlurUniforms[sBlurWidth] = 0;
-}
-
-void LLPostProcess::getShaderUniforms(glslUniforms & uniforms, GLhandleARB & prog)
-{
- /// Find uniform locations and insert into map
- glslUniforms::iterator i;
- for (i = uniforms.begin(); i != uniforms.end(); ++i){
- i->second = glGetUniformLocationARB(prog, i->first.String().c_str());
- }
-}
-
-void LLPostProcess::doEffects(void)
-{
- /// Save GL State
- glPushAttrib(GL_ALL_ATTRIB_BITS);
- glPushClientAttrib(GL_ALL_ATTRIB_BITS);
-
- /// Copy the screen buffer to the render texture
- {
- U32 tex = mSceneRenderTexture->getTexName() ;
- copyFrameBuffer(tex, screenW, screenH);
- }
-
- /// Clear the frame buffer.
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-
- /// Change to an orthogonal view
- viewOrthogonal(screenW, screenH);
-
- checkError();
- applyShaders();
-
- LLGLSLShader::bindNoShader();
- checkError();
-
- /// Change to a perspective view
- viewPerspective();
-
- /// Reset GL State
- glPopClientAttrib();
- glPopAttrib();
- checkError();
-}
-
-void LLPostProcess::copyFrameBuffer(U32 & texture, unsigned int width, unsigned int height)
-{
- gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture);
- glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 0, 0, width, height, 0);
-}
-
-void LLPostProcess::drawOrthoQuad(unsigned int width, unsigned int height, QuadType type)
-{
-#if 0
- float noiseX = 0.f;
- float noiseY = 0.f;
- float screenRatio = 1.0f;
-
- if (type == QUAD_NOISE){
- noiseX = ((float) rand() / (float) RAND_MAX);
- noiseY = ((float) rand() / (float) RAND_MAX);
- screenRatio = (float) width / (float) height;
- }
-
-
- glBegin(GL_QUADS);
- if (type != QUAD_BLOOM_EXTRACT){
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height);
- } else {
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height * 2.0f);
- }
- if (type == QUAD_NOISE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
- noiseX,
- noiseTextureScale + noiseY);
- } else if (type == QUAD_BLOOM_COMBINE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, (GLfloat) height * 0.5f);
- }
- glVertex2f(0.f, (GLfloat) screenH - height);
-
- if (type != QUAD_BLOOM_EXTRACT){
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f);
- } else {
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f);
- }
- if (type == QUAD_NOISE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
- noiseX,
- noiseY);
- } else if (type == QUAD_BLOOM_COMBINE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, 0.f);
- }
- glVertex2f(0.f, (GLfloat) height + (screenH - height));
-
-
- if (type != QUAD_BLOOM_EXTRACT){
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, 0.f);
- } else {
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, 0.f);
- }
- if (type == QUAD_NOISE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
- screenRatio * noiseTextureScale + noiseX,
- noiseY);
- } else if (type == QUAD_BLOOM_COMBINE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, 0.f);
- }
- glVertex2f((GLfloat) width, (GLfloat) height + (screenH - height));
-
-
- if (type != QUAD_BLOOM_EXTRACT){
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, (GLfloat) height);
- } else {
- glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, (GLfloat) height * 2.0f);
- }
- if (type == QUAD_NOISE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
- screenRatio * noiseTextureScale + noiseX,
- noiseTextureScale + noiseY);
- } else if (type == QUAD_BLOOM_COMBINE){
- glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, (GLfloat) height * 0.5f);
- }
- glVertex2f((GLfloat) width, (GLfloat) screenH - height);
- glEnd();
-#endif
-}
-
-void LLPostProcess::viewOrthogonal(unsigned int width, unsigned int height)
-{
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.pushMatrix();
- gGL.loadIdentity();
- gGL.ortho( 0.f, (GLdouble) width , (GLdouble) height , 0.f, -1.f, 1.f );
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.pushMatrix();
- gGL.loadIdentity();
-}
-
-void LLPostProcess::viewPerspective(void)
-{
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.popMatrix();
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.popMatrix();
-}
-
-void LLPostProcess::changeOrthogonal(unsigned int width, unsigned int height)
-{
- viewPerspective();
- viewOrthogonal(width, height);
-}
-
-void LLPostProcess::createTexture(LLPointer<LLImageGL>& texture, unsigned int width, unsigned int height)
-{
- std::vector<GLubyte> data(width * height * 4, 0) ;
-
- texture = new LLImageGL(FALSE) ;
- if(texture->createGLTexture())
- {
- gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture->getTexName());
- glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, width, height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
- gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
- gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
- }
-}
-
-void LLPostProcess::createNoiseTexture(LLPointer<LLImageGL>& texture)
-{
- 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);
- }
- }
-
- texture = new LLImageGL(FALSE) ;
- if(texture->createGLTexture())
- {
- gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, texture->getTexName());
- LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]);
- gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
- gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP);
- }
-}
-
-bool LLPostProcess::checkError(void)
-{
- GLenum glErr;
- bool retCode = false;
-
- glErr = glGetError();
- while (glErr != GL_NO_ERROR)
- {
- // shaderErrorLog << (const char *) gluErrorString(glErr) << std::endl;
- char const * err_str_raw = (const char *) gluErrorString(glErr);
-
- if(err_str_raw == NULL)
- {
- std::ostringstream err_builder;
- err_builder << "unknown error number " << glErr;
- mShaderErrorString = err_builder.str();
- }
- else
- {
- mShaderErrorString = err_str_raw;
- }
-
- retCode = true;
- glErr = glGetError();
- }
- return retCode;
-}
-
-void LLPostProcess::checkShaderError(GLhandleARB shader)
-{
- GLint infologLength = 0;
- GLint charsWritten = 0;
- GLchar *infoLog;
-
- checkError(); // Check for OpenGL errors
-
- glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);
-
- checkError(); // Check for OpenGL errors
-
- if (infologLength > 0)
- {
- infoLog = (GLchar *)malloc(infologLength);
- if (infoLog == NULL)
- {
- /// Could not allocate infolog buffer
- return;
- }
- glGetInfoLogARB(shader, infologLength, &charsWritten, infoLog);
- // shaderErrorLog << (char *) infoLog << std::endl;
- mShaderErrorString = (char *) infoLog;
- free(infoLog);
- }
- checkError(); // Check for OpenGL errors
-}
+/** + * @file llpostprocess.cpp + * @brief LLPostProcess class implementation + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llpostprocess.h" +#include "llglslshader.h" +#include "llsdserialize.h" +#include "llrender.h" + +static LLStaticHashedString sRenderTexture("RenderTexture"); +static LLStaticHashedString sBrightness("brightness"); +static LLStaticHashedString sContrast("contrast"); +static LLStaticHashedString sContrastBase("contrastBase"); +static LLStaticHashedString sSaturation("saturation"); +static LLStaticHashedString sLumWeights("lumWeights"); +static LLStaticHashedString sNoiseTexture("NoiseTexture"); +static LLStaticHashedString sBrightMult("brightMult"); +static LLStaticHashedString sNoiseStrength("noiseStrength"); +static LLStaticHashedString sExtractLow("extractLow"); +static LLStaticHashedString sExtractHigh("extractHigh"); +static LLStaticHashedString sBloomStrength("bloomStrength"); +static LLStaticHashedString sTexelSize("texelSize"); +static LLStaticHashedString sBlurDirection("blurDirection"); +static LLStaticHashedString sBlurWidth("blurWidth"); + +LLPostProcess * gPostProcess = NULL; + + +static const unsigned int NOISE_SIZE = 512; + +/// CALCULATING LUMINANCE (Using NTSC lum weights) +/// http://en.wikipedia.org/wiki/Luma_%28video%29 +static const float LUMINANCE_R = 0.299f; +static const float LUMINANCE_G = 0.587f; +static const float LUMINANCE_B = 0.114f; + +static const char * const XML_FILENAME = "postprocesseffects.xml"; + +LLPostProcess::LLPostProcess(void) : + initialized(false), + mAllEffects(LLSD::emptyMap()), + screenW(1), screenH(1) +{ + mSceneRenderTexture = NULL ; + mNoiseTexture = NULL ; + mTempBloomTexture = NULL ; + + noiseTextureScale = 1.0f; + + /* 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, mAllEffects, LLSDSerialize::SIZE_UNLIMITED); + } + + if (!mAllEffects.has("default")) + { + LLSD & defaultEffect = (mAllEffects["default"] = LLSD::emptyMap()); + + defaultEffect["enable_night_vision"] = LLSD::Boolean(false); + defaultEffect["enable_bloom"] = LLSD::Boolean(false); + defaultEffect["enable_color_filter"] = LLSD::Boolean(false); + + /// NVG Defaults + defaultEffect["brightness_multiplier"] = 3.0; + defaultEffect["noise_size"] = 25.0; + defaultEffect["noise_strength"] = 0.4; + + // TODO BTest potentially add this to tweaks? + noiseTextureScale = 1.0f; + + /// Bloom Defaults + defaultEffect["extract_low"] = 0.95; + defaultEffect["extract_high"] = 1.0; + defaultEffect["bloom_width"] = 2.25; + defaultEffect["bloom_strength"] = 1.5; + + /// Color Filter Defaults + defaultEffect["brightness"] = 1.0; + defaultEffect["contrast"] = 1.0; + defaultEffect["saturation"] = 1.0; + + LLSD& contrastBase = (defaultEffect["contrast_base"] = LLSD::emptyArray()); + contrastBase.append(1.0); + contrastBase.append(1.0); + contrastBase.append(1.0); + contrastBase.append(0.5); + } + + setSelectedEffect("default"); + */ +} + +LLPostProcess::~LLPostProcess(void) +{ + invalidate() ; +} + +// static +void LLPostProcess::initClass(void) +{ + //this will cause system to crash at second time login + //if first time login fails due to network connection --- bao + //***llassert_always(gPostProcess == NULL); + //replaced by the following line: + if(gPostProcess) + return ; + + + gPostProcess = new LLPostProcess(); +} + +// static +void LLPostProcess::cleanupClass() +{ + delete gPostProcess; + gPostProcess = NULL; +} + +void LLPostProcess::setSelectedEffect(std::string const & effectName) +{ + mSelectedEffectName = effectName; + static_cast<LLSD &>(tweaks) = mAllEffects[effectName]; +} + +void LLPostProcess::saveEffect(std::string const & effectName) +{ + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender. + mAllEffects[effectName] = tweaks; + + 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(mAllEffects, effectsXML); + */ +} +void LLPostProcess::invalidate() +{ + mSceneRenderTexture = NULL ; + mNoiseTexture = NULL ; + mTempBloomTexture = NULL ; + initialized = FALSE ; +} + +void LLPostProcess::apply(unsigned int width, unsigned int height) +{ + if (!initialized || width != screenW || height != screenH){ + initialize(width, height); + } + if (shadersEnabled()){ + doEffects(); + } +} + +void LLPostProcess::initialize(unsigned int width, unsigned int height) +{ + screenW = width; + screenH = height; + createTexture(mSceneRenderTexture, screenW, screenH); + initialized = true; + + checkError(); + createNightVisionShader(); + createBloomShader(); + createColorFilterShader(); + checkError(); +} + +inline bool LLPostProcess::shadersEnabled(void) +{ + return (tweaks.useColorFilter().asBoolean() || + tweaks.useNightVisionShader().asBoolean() || + tweaks.useBloomShader().asBoolean() ); + +} + +void LLPostProcess::applyShaders(void) +{ + if (tweaks.useColorFilter()){ + applyColorFilterShader(); + checkError(); + } + if (tweaks.useNightVisionShader()){ + /// If any of the above shaders have been called update the frame buffer; + if (tweaks.useColorFilter()) + { + U32 tex = mSceneRenderTexture->getTexName() ; + copyFrameBuffer(tex, screenW, screenH); + } + applyNightVisionShader(); + checkError(); + } + if (tweaks.useBloomShader()){ + /// If any of the above shaders have been called update the frame buffer; + if (tweaks.useColorFilter().asBoolean() || tweaks.useNightVisionShader().asBoolean()) + { + U32 tex = mSceneRenderTexture->getTexName() ; + copyFrameBuffer(tex, screenW, screenH); + } + applyBloomShader(); + checkError(); + } +} + +void LLPostProcess::applyColorFilterShader(void) +{ + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender. + gPostColorFilterProgram.bind(); + + gGL.getTexUnit(0)->activate(); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE); + + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture); + + getShaderUniforms(colorFilterUniforms, gPostColorFilterProgram.mProgramObject); + glUniform1iARB(colorFilterUniforms["RenderTexture"], 0); + glUniform1fARB(colorFilterUniforms["brightness"], tweaks.getBrightness()); + glUniform1fARB(colorFilterUniforms["contrast"], tweaks.getContrast()); + float baseI = (tweaks.getContrastBaseR() + tweaks.getContrastBaseG() + tweaks.getContrastBaseB()) / 3.0f; + baseI = tweaks.getContrastBaseIntensity() / ((baseI < 0.001f) ? 0.001f : baseI); + float baseR = tweaks.getContrastBaseR() * baseI; + float baseG = tweaks.getContrastBaseG() * baseI; + float baseB = tweaks.getContrastBaseB() * baseI; + glUniform3fARB(colorFilterUniforms["contrastBase"], baseR, baseG, baseB); + glUniform1fARB(colorFilterUniforms["saturation"], tweaks.getSaturation()); + glUniform3fARB(colorFilterUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B); + + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + LLGLDepthTest depth(GL_FALSE); + + /// Draw a screen space quad + drawOrthoQuad(screenW, screenH, QUAD_NORMAL); + gPostColorFilterProgram.unbind(); + */ +} + +void LLPostProcess::createColorFilterShader(void) +{ + /// Define uniform names + colorFilterUniforms[sRenderTexture] = 0; + colorFilterUniforms[sBrightness] = 0; + colorFilterUniforms[sContrast] = 0; + colorFilterUniforms[sContrastBase] = 0; + colorFilterUniforms[sSaturation] = 0; + colorFilterUniforms[sLumWeights] = 0; +} + +void LLPostProcess::applyNightVisionShader(void) +{ + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender. + gPostNightVisionProgram.bind(); + + gGL.getTexUnit(0)->activate(); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE); + + getShaderUniforms(nightVisionUniforms, gPostNightVisionProgram.mProgramObject); + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture); + glUniform1iARB(nightVisionUniforms["RenderTexture"], 0); + + gGL.getTexUnit(1)->activate(); + gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE); + + gGL.getTexUnit(1)->bindManual(LLTexUnit::TT_TEXTURE, noiseTexture); + glUniform1iARB(nightVisionUniforms["NoiseTexture"], 1); + + + glUniform1fARB(nightVisionUniforms["brightMult"], tweaks.getBrightMult()); + glUniform1fARB(nightVisionUniforms["noiseStrength"], tweaks.getNoiseStrength()); + noiseTextureScale = 0.01f + ((101.f - tweaks.getNoiseSize()) / 100.f); + noiseTextureScale *= (screenH / NOISE_SIZE); + + + glUniform3fARB(nightVisionUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B); + + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + LLGLDepthTest depth(GL_FALSE); + + /// Draw a screen space quad + drawOrthoQuad(screenW, screenH, QUAD_NOISE); + gPostNightVisionProgram.unbind(); + gGL.getTexUnit(0)->activate(); + */ +} + +void LLPostProcess::createNightVisionShader(void) +{ + /// Define uniform names + nightVisionUniforms[sRenderTexture] = 0; + nightVisionUniforms[sNoiseTexture] = 0; + nightVisionUniforms[sBrightMult] = 0; + nightVisionUniforms[sNoiseStrength] = 0; + nightVisionUniforms[sLumWeights] = 0; + + createNoiseTexture(mNoiseTexture); +} + +void LLPostProcess::applyBloomShader(void) +{ + +} + +void LLPostProcess::createBloomShader(void) +{ + createTexture(mTempBloomTexture, unsigned(screenW * 0.5), unsigned(screenH * 0.5)); + + /// Create Bloom Extract Shader + bloomExtractUniforms[sRenderTexture] = 0; + bloomExtractUniforms[sExtractLow] = 0; + bloomExtractUniforms[sExtractHigh] = 0; + bloomExtractUniforms[sLumWeights] = 0; + + /// Create Bloom Blur Shader + bloomBlurUniforms[sRenderTexture] = 0; + bloomBlurUniforms[sBloomStrength] = 0; + bloomBlurUniforms[sTexelSize] = 0; + bloomBlurUniforms[sBlurDirection] = 0; + bloomBlurUniforms[sBlurWidth] = 0; +} + +void LLPostProcess::getShaderUniforms(glslUniforms & uniforms, GLhandleARB & prog) +{ + /// Find uniform locations and insert into map + glslUniforms::iterator i; + for (i = uniforms.begin(); i != uniforms.end(); ++i){ + i->second = glGetUniformLocationARB(prog, i->first.String().c_str()); + } +} + +void LLPostProcess::doEffects(void) +{ + /// Save GL State + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_ALL_ATTRIB_BITS); + + /// Copy the screen buffer to the render texture + { + U32 tex = mSceneRenderTexture->getTexName() ; + copyFrameBuffer(tex, screenW, screenH); + } + + /// Clear the frame buffer. + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + /// Change to an orthogonal view + viewOrthogonal(screenW, screenH); + + checkError(); + applyShaders(); + + LLGLSLShader::bindNoShader(); + checkError(); + + /// Change to a perspective view + viewPerspective(); + + /// Reset GL State + glPopClientAttrib(); + glPopAttrib(); + checkError(); +} + +void LLPostProcess::copyFrameBuffer(U32 & texture, unsigned int width, unsigned int height) +{ + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture); + glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 0, 0, width, height, 0); +} + +void LLPostProcess::drawOrthoQuad(unsigned int width, unsigned int height, QuadType type) +{ +#if 0 + float noiseX = 0.f; + float noiseY = 0.f; + float screenRatio = 1.0f; + + if (type == QUAD_NOISE){ + noiseX = ((float) rand() / (float) RAND_MAX); + noiseY = ((float) rand() / (float) RAND_MAX); + screenRatio = (float) width / (float) height; + } + + + glBegin(GL_QUADS); + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height * 2.0f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + noiseX, + noiseTextureScale + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, (GLfloat) height * 0.5f); + } + glVertex2f(0.f, (GLfloat) screenH - height); + + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + noiseX, + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, 0.f); + } + glVertex2f(0.f, (GLfloat) height + (screenH - height)); + + + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, 0.f); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, 0.f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + screenRatio * noiseTextureScale + noiseX, + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, 0.f); + } + glVertex2f((GLfloat) width, (GLfloat) height + (screenH - height)); + + + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, (GLfloat) height); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, (GLfloat) height * 2.0f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + screenRatio * noiseTextureScale + noiseX, + noiseTextureScale + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, (GLfloat) height * 0.5f); + } + glVertex2f((GLfloat) width, (GLfloat) screenH - height); + glEnd(); +#endif +} + +void LLPostProcess::viewOrthogonal(unsigned int width, unsigned int height) +{ + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho( 0.f, (GLdouble) width , (GLdouble) height , 0.f, -1.f, 1.f ); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); +} + +void LLPostProcess::viewPerspective(void) +{ + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); +} + +void LLPostProcess::changeOrthogonal(unsigned int width, unsigned int height) +{ + viewPerspective(); + viewOrthogonal(width, height); +} + +void LLPostProcess::createTexture(LLPointer<LLImageGL>& texture, unsigned int width, unsigned int height) +{ + std::vector<GLubyte> data(width * height * 4, 0) ; + + texture = new LLImageGL(FALSE) ; + if(texture->createGLTexture()) + { + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture->getTexName()); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + } +} + +void LLPostProcess::createNoiseTexture(LLPointer<LLImageGL>& texture) +{ + 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); + } + } + + texture = new LLImageGL(FALSE) ; + if(texture->createGLTexture()) + { + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, texture->getTexName()); + LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP); + } +} + +bool LLPostProcess::checkError(void) +{ + GLenum glErr; + bool retCode = false; + + glErr = glGetError(); + while (glErr != GL_NO_ERROR) + { + // shaderErrorLog << (const char *) gluErrorString(glErr) << std::endl; + char const * err_str_raw = (const char *) gluErrorString(glErr); + + if(err_str_raw == NULL) + { + std::ostringstream err_builder; + err_builder << "unknown error number " << glErr; + mShaderErrorString = err_builder.str(); + } + else + { + mShaderErrorString = err_str_raw; + } + + retCode = true; + glErr = glGetError(); + } + return retCode; +} + +void LLPostProcess::checkShaderError(GLhandleARB shader) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + GLchar *infoLog; + + checkError(); // Check for OpenGL errors + + glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength); + + checkError(); // Check for OpenGL errors + + if (infologLength > 0) + { + infoLog = (GLchar *)malloc(infologLength); + if (infoLog == NULL) + { + /// Could not allocate infolog buffer + return; + } + glGetInfoLogARB(shader, infologLength, &charsWritten, infoLog); + // shaderErrorLog << (char *) infoLog << std::endl; + mShaderErrorString = (char *) infoLog; + free(infoLog); + } + checkError(); // Check for OpenGL errors +} |