// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" // NEL3D #include "nel/3d/u_driver.h" #include "nel/3d/u_scene.h" #include "nel/3d/u_camera.h" //3D #include "nel/3d/driver_user.h" #include "nel/3d/texture_bloom.h" #include "nel/3d/texture_user.h" #include "nel/3d/bloom_effect.h" using namespace NLMISC; using namespace NL3D; using namespace std; namespace NL3D { // vertex program used to blur texture static const char *TextureOffset = "!!VP1.0 \n\ MOV o[COL0].x, c[8].x; \n\ MOV o[COL0].y, c[8].y; \n\ MOV o[COL0].z, c[8].z; \n\ MOV o[COL0].w, c[8].w; \n\ MOV o[HPOS].x, v[OPOS].x; \n\ MOV o[HPOS].y, v[OPOS].y; \n\ MOV o[HPOS].z, v[OPOS].z; \n\ MOV o[HPOS].w, c[9].w; \n\ ADD o[TEX0], v[TEX0], c[10]; \n\ ADD o[TEX1], v[TEX0], c[11]; \n\ ADD o[TEX2], v[TEX0], c[12]; \n\ ADD o[TEX3], v[TEX0], c[13]; \n\ END \n"; static NLMISC::CSmartPtr TextureOffsetVertexProgram; //----------------------------------------------------------------------------------------------------------- CBloomEffect::CBloomEffect() { if (!TextureOffsetVertexProgram) { TextureOffsetVertexProgram = new CVertexProgram(TextureOffset); } _Driver = NULL; _Scene = NULL; _SquareBloom = true; _DensityBloom = 128; _Init = false; _InitBloomEffect = false; } //----------------------------------------------------------------------------------------------------------- CBloomEffect::~CBloomEffect() { if(_Init) { if(!_DisplayInitMat.empty()) { _DisplayInitMat.getObjectPtr()->setTexture(0, NULL); if (_Driver) _Driver->deleteMaterial(_DisplayInitMat); } _InitText = NULL; if(!_DisplayBlurMat.empty()) { _DisplayBlurMat.getObjectPtr()->setTexture(0, NULL); if (_Driver) _Driver->deleteMaterial(_DisplayBlurMat); } if(!_DisplaySquareBlurMat.empty()) { _DisplaySquareBlurMat.getObjectPtr()->setTexture(0, NULL); _DisplaySquareBlurMat.getObjectPtr()->setTexture(1, NULL); if (_Driver) _Driver->deleteMaterial(_DisplaySquareBlurMat); } if(!_BlurMat.empty()) { _BlurMat.getObjectPtr()->setTexture(0, NULL); _BlurMat.getObjectPtr()->setTexture(1, NULL); _BlurMat.getObjectPtr()->setTexture(2, NULL); _BlurMat.getObjectPtr()->setTexture(3, NULL); if (_Driver) _Driver->deleteMaterial(_BlurMat); } _BlurHorizontalTex = NULL; _BlurFinalTex = NULL; } } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::init(bool initBloomEffect) { _InitBloomEffect = initBloomEffect; if(((CDriverUser *)_Driver)->getDriver()->supportBloomEffect()) init(); } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::init() { _WndWidth = _Driver->getWindowWidth(); _WndHeight = _Driver->getWindowHeight(); _BlurWidth = 256; _BlurHeight = 256; // initialize textures _InitText = NULL; _BlurHorizontalTex = NULL; _BlurFinalTex = NULL; if(_InitBloomEffect) { initTexture(_InitText, false, _WndWidth, _WndHeight); } initTexture(_BlurFinalTex, true, _BlurWidth, _BlurHeight); initTexture(_BlurHorizontalTex, true, _BlurWidth, _BlurHeight); // initialize blur material _BlurMat = _Driver->createMaterial(); CMaterial * matObject = _BlurMat.getObjectPtr(); _BlurMat.initUnlit(); _BlurMat.setColor(CRGBA::White); _BlurMat.setBlend (false); _BlurMat.setAlphaTest (false); matObject->setBlendFunc (CMaterial::one, CMaterial::zero); matObject->setZWrite(false); matObject->setZFunc(CMaterial::always); matObject->setDoubleSided(true); // initialize stages of fixed pipeline CRGBA constantCol1(85, 85, 85, 85); CRGBA constantCol2(43, 43, 43, 43); // stage 0 matObject->texConstantColor(0, constantCol1); matObject->texEnvOpRGB(0, CMaterial::Modulate); matObject->texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor); matObject->texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor); // stage 1 matObject->texConstantColor(1, constantCol2); matObject->texEnvOpRGB(1, CMaterial::Mad); matObject->texEnvArg0RGB(1, CMaterial::Texture, CMaterial::SrcColor); matObject->texEnvArg1RGB(1, CMaterial::Constant, CMaterial::SrcColor); matObject->texEnvArg2RGB(1, CMaterial::Previous, CMaterial::SrcColor); // stage 2 matObject->texConstantColor(2, constantCol1); matObject->texEnvOpRGB(2, CMaterial::Mad); matObject->texEnvArg0RGB(2, CMaterial::Texture, CMaterial::SrcColor); matObject->texEnvArg1RGB(2, CMaterial::Constant, CMaterial::SrcColor); matObject->texEnvArg2RGB(2, CMaterial::Previous, CMaterial::SrcColor); // stage 3 matObject->texConstantColor(3, constantCol2); matObject->texEnvOpRGB(3, CMaterial::Mad); matObject->texEnvArg0RGB(3, CMaterial::Texture, CMaterial::SrcColor); matObject->texEnvArg1RGB(3, CMaterial::Constant, CMaterial::SrcColor); matObject->texEnvArg2RGB(3, CMaterial::Previous, CMaterial::SrcColor); // initialize display materials if(_InitBloomEffect) { _DisplayInitMat = _Driver->createMaterial(); CMaterial * matObjectInit = _DisplayInitMat.getObjectPtr(); _DisplayInitMat.initUnlit(); _DisplayInitMat.setColor(CRGBA::White); _DisplayInitMat.setBlend (false); _DisplayInitMat.setAlphaTest (false); matObjectInit->setBlendFunc (CMaterial::one, CMaterial::zero); matObjectInit->setZWrite(false); matObjectInit->setZFunc(CMaterial::always); matObjectInit->setDoubleSided(true); matObjectInit->setTexture(0, _InitText); } // initialize linear blur material _DisplayBlurMat = _Driver->createMaterial(); CMaterial * matObjectFinal = _DisplayBlurMat.getObjectPtr(); _DisplayBlurMat.initUnlit(); _DisplayBlurMat.setColor(CRGBA::White); matObjectFinal->setBlend(true); matObjectFinal->setBlendFunc(CMaterial::one, CMaterial::invsrccolor); matObjectFinal->setZWrite(false); matObjectFinal->setZFunc(CMaterial::always); matObjectFinal->setDoubleSided(true); matObjectFinal->setTexture(0, _BlurFinalTex); matObjectFinal->texEnvOpRGB(0, CMaterial::Modulate); matObjectFinal->texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor); matObjectFinal->texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor); // initialize square blur material _DisplaySquareBlurMat = _Driver->createMaterial(); matObjectFinal = _DisplaySquareBlurMat.getObjectPtr(); _DisplaySquareBlurMat.initUnlit(); _DisplaySquareBlurMat.setColor(CRGBA::White); matObjectFinal->setBlend(true); matObjectFinal->setBlendFunc(CMaterial::one, CMaterial::invsrccolor); matObjectFinal->setZWrite(false); matObjectFinal->setZFunc(CMaterial::always); matObjectFinal->setDoubleSided(true); matObjectFinal->setTexture(0, _BlurFinalTex); matObjectFinal->texEnvOpRGB(0, CMaterial::Modulate); matObjectFinal->texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor); matObjectFinal->texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor); matObjectFinal->setTexture(1, _BlurFinalTex); matObjectFinal->texEnvOpRGB(1, CMaterial::Modulate); matObjectFinal->texEnvArg0RGB(1, CMaterial::Texture, CMaterial::SrcColor); matObjectFinal->texEnvArg1RGB(1, CMaterial::Previous, CMaterial::SrcColor); // initialize quads _DisplayQuad.V0 = CVector(0.f, 0.f, 0.5f); _DisplayQuad.V1 = CVector(1.f, 0.f, 0.5f); _DisplayQuad.V2 = CVector(1.f, 1.f, 0.5f); _DisplayQuad.V3 = CVector(0.f, 1.f, 0.5f); _BlurQuad.V0 = CVector(-1.f, -1.f, 0.5f); _BlurQuad.V1 = CVector(1.f, -1.f, 0.5f); _BlurQuad.V2 = CVector(1.f, 1.f, 0.5f); _BlurQuad.V3 = CVector(-1.f, 1.f, 0.5f); _Init = true; } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::initTexture(CSmartPtr & tex, bool isMode2D, uint32 width, uint32 height) { NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver(); tex = new CTextureBloom(); tex->setReleasable(false); tex->resize(width, height); tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); tex->setWrapS(ITexture::Clamp); tex->setWrapT(ITexture::Clamp); ((CTextureBloom *)tex.getPtr())->mode2D(isMode2D); if(tex->TextureDrvShare==NULL || tex->TextureDrvShare->DrvTexture.getPtr()==NULL) { tex->setRenderTarget(true); drvInternal->setupTexture(*tex); } } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::initBloom() // clientcfg { if(!((CDriverUser *)_Driver)->getDriver()->supportBloomEffect()) return; // don't activate bloom when PolygonMode is different from Filled if (_Driver->getPolygonMode() != UDriver::Filled) return; if(_Driver->getWindowWidth()==0 || _Driver->getWindowHeight()==0) return; if(!_Init) init(); _OriginalRenderTarget = static_cast(_Driver)->getDriver()->getRenderTarget(); // if window resize, reinitialize textures if(_WndWidth!=_Driver->getWindowWidth() || _WndHeight!=_Driver->getWindowHeight()) { _WndWidth = _Driver->getWindowWidth(); _WndHeight = _Driver->getWindowHeight(); if(_InitBloomEffect) { // release old SmartPtr _DisplayInitMat.getObjectPtr()->setTexture(0, NULL); _InitText = NULL; initTexture(_InitText, false, _WndWidth, _WndHeight); _DisplayInitMat.getObjectPtr()->setTexture(0, _InitText); } bool reinitBlurTextures = false; if(_WndWidth<_BlurWidth || _WndHeight<_BlurHeight) { _BlurWidth = raiseToNextPowerOf2(_WndWidth)/2; _BlurHeight = raiseToNextPowerOf2(_WndHeight)/2; reinitBlurTextures = true; } if(_WndWidth>256 && _BlurWidth!=256) { _BlurWidth = 256; reinitBlurTextures = true; } if(_WndHeight>256 && _BlurHeight!=256) { _BlurHeight = 256; reinitBlurTextures = true; } if(reinitBlurTextures) { // release old SmartPtr _DisplayBlurMat.getObjectPtr()->setTexture(0, NULL); _DisplaySquareBlurMat.getObjectPtr()->setTexture(0, NULL); _DisplaySquareBlurMat.getObjectPtr()->setTexture(1, NULL); _BlurMat.getObjectPtr()->setTexture(0, NULL); _BlurMat.getObjectPtr()->setTexture(1, NULL); _BlurMat.getObjectPtr()->setTexture(2, NULL); _BlurMat.getObjectPtr()->setTexture(3, NULL); _BlurHorizontalTex = NULL; _BlurFinalTex = NULL; initTexture(_BlurFinalTex, true, _BlurWidth, _BlurHeight); initTexture(_BlurHorizontalTex, true, _BlurWidth, _BlurHeight); _DisplayBlurMat.getObjectPtr()->setTexture(0, _BlurFinalTex); _DisplaySquareBlurMat.getObjectPtr()->setTexture(0, _BlurFinalTex); _DisplaySquareBlurMat.getObjectPtr()->setTexture(1, _BlurFinalTex); } } if (!_OriginalRenderTarget) { NL3D::CTextureUser txt = (_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser()); if(!(static_cast(_Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight))) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); return; } } } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::endBloom() // clientcfg { if(!_Driver->supportBloomEffect() || !_Init) return; // don't activate bloom when PolygonMode is different from Filled if (_Driver->getPolygonMode() != UDriver::Filled) return; if(_Driver->getWindowWidth()==0 || _Driver->getWindowHeight()==0) return; CTextureUser txt1 = _OriginalRenderTarget ? CTextureUser(_OriginalRenderTarget) : ((_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser())); CTextureUser txt2(_BlurFinalTex); CRect rect1(0, 0, _WndWidth, _WndHeight); CRect rect2(0, 0, _BlurWidth, _BlurHeight); // stretch rect ((CDriverUser *) _Driver)->stretchRect(_Scene, txt1 , rect1, txt2, rect2); // horizontal blur pass doBlur(true); // vertical blur pass doBlur(false); // apply blur with a blend operation applyBlur(); } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::applyBlur() { NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver(); /*if (_OriginalRenderTarget) { CTextureUser txt(_OriginalRenderTarget); if(!(static_cast(_Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight))) { nlwarning("setRenderTarget return false with original render target for bloom effect\n"); return; } } // in opengl, display in init texture else if(_InitBloomEffect) { CTextureUser txt(_InitText); if(!(static_cast(_Driver)->setRenderTarget(txt, 0, 0, _WndWidth, _WndHeight))) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); return; } }*/ CTextureUser txtApply = _OriginalRenderTarget ? CTextureUser(_OriginalRenderTarget) : ((_InitBloomEffect) ? (CTextureUser(_InitText)) : (CTextureUser())); if(!(static_cast(_Driver)->setRenderTarget(txtApply, 0, 0, _WndWidth, _WndHeight))) { nlwarning("setRenderTarget return false with initial texture for bloom effect\n"); return; } // display blur texture // initialize blur texture coordinates if(_InitBloomEffect) { _BlurQuad.Uv0 = CUV(0.f, 0.f); _BlurQuad.Uv1 = CUV(1.f, 0.f); _BlurQuad.Uv2 = CUV(1.f, 1.f); _BlurQuad.Uv3 = CUV(0.f, 1.f); } else { _BlurQuad.Uv0 = CUV(0.f, 1.f); _BlurQuad.Uv1 = CUV(1.f, 1.f); _BlurQuad.Uv2 = CUV(1.f, 0.f); _BlurQuad.Uv3 = CUV(0.f, 0.f); } // initialize vertex program drvInternal->activeVertexProgram(TextureOffsetVertexProgram); drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f); drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f); // initialize blur material UMaterial displayBlurMat; if(_SquareBloom) { displayBlurMat = _DisplaySquareBlurMat; } else { displayBlurMat = _DisplayBlurMat; } CMaterial * matObjectFinal = displayBlurMat.getObjectPtr(); uint8 d = _DensityBloom; CRGBA constCoeff(d, d, d, d); matObjectFinal->texConstantColor(0, constCoeff); // display quad UCamera pCam = _Scene->getCam(); _Driver->setMatrixMode2D11(); _Driver->drawQuad(_BlurQuad, displayBlurMat); _Driver->setMatrixMode3D(pCam); // disable vertex program drvInternal->activeVertexProgram(NULL); } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::endInterfacesDisplayBloom() // clientcfg { // Render from render target to screen if necessary. // Don't do this when the blend was done to the screen or when rendering to a user provided rendertarget. if ((_OriginalRenderTarget.getPtr() == NULL) && _InitBloomEffect) { if(!_Driver->supportBloomEffect() || !_Init) return; // don't activate bloom when PolygonMode is different from Filled if (_Driver->getPolygonMode() != UDriver::Filled) return; if(_Driver->getWindowWidth()==0 || _Driver->getWindowHeight()==0) return; NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver(); CTextureUser txtNull; ((CDriverUser *)_Driver)->setRenderTarget(txtNull, 0, 0, 0, 0); // initialize texture coordinates float newU = drvInternal->isTextureRectangle(_InitText) ? (float)_WndWidth : 1.f; float newV = drvInternal->isTextureRectangle(_InitText) ? (float)_WndHeight : 1.f; _DisplayQuad.Uv0 = CUV(0.f, 0.f); _DisplayQuad.Uv1 = CUV(newU, 0.f); _DisplayQuad.Uv2 = CUV(newU, newV); _DisplayQuad.Uv3 = CUV(0.f, newV); // init material texture // CMaterial * matObjectInit = _DisplayInitMat.getObjectPtr(); // display UCamera pCam = _Scene->getCam(); _Driver->setMatrixMode2D11(); _Driver->drawQuad(_DisplayQuad, _DisplayInitMat); _Driver->setMatrixMode3D(pCam); } _OriginalRenderTarget = NULL; } //----------------------------------------------------------------------------------------------------------- void CBloomEffect::doBlur(bool horizontalBlur) { CVector2f blurVec; ITexture * startTexture; ITexture * endTexture; // set displayed texture and render target texture of the pass if(horizontalBlur) { blurVec = CVector2f(1.f, 0.f); startTexture = _BlurFinalTex; endTexture = _BlurHorizontalTex; } else { blurVec = CVector2f(0.f, 1.f); startTexture = _BlurHorizontalTex; endTexture = _BlurFinalTex; } NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver(); CTextureUser txt(endTexture); // initialize render target if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _BlurWidth, _BlurHeight)) { nlwarning("setRenderTarget return false with blur texture for bloom effect\n"); return; } // initialize vertex program drvInternal->activeVertexProgram(TextureOffsetVertexProgram); drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f); drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f); // set several decal constants in order to obtain in the render target texture a mix of color // of a texel and its neighbored texels on the axe of the pass. float decalL, decal2L, decalR, decal2R; if(_InitBloomEffect) { decalL = -0.5f; decal2L = -1.5f; decalR = 0.5f; decal2R = 1.5f; } else { decalL = 0.f; decal2L = -1.f; decalR = 1.f; decal2R = 2.f; } drvInternal->setUniform2f(IDriver::VertexProgram, 10, (decalR/(float)_BlurWidth)*blurVec.x, (decalR/(float)_BlurHeight)*blurVec.y); drvInternal->setUniform2f(IDriver::VertexProgram, 11, (decal2R/(float)_BlurWidth)*blurVec.x, (decal2R/(float)_BlurHeight)*blurVec.y); drvInternal->setUniform2f(IDriver::VertexProgram, 12, (decalL/(float)_BlurWidth)*blurVec.x, (decalL/(float)_BlurHeight)*blurVec.y); drvInternal->setUniform2f(IDriver::VertexProgram, 13, (decal2L/(float)_BlurWidth)*blurVec.x, (decal2L/(float)_BlurHeight)*blurVec.y); // initialize material textures CMaterial * matObject = _BlurMat.getObjectPtr(); matObject->setTexture(0, startTexture); matObject->setTexture(1, startTexture); matObject->setTexture(2, startTexture); matObject->setTexture(3, startTexture); // initialize quad _BlurQuad.Uv0 = CUV(0.0f, 0.0f); _BlurQuad.Uv1 = CUV(1.f, 0.0f); _BlurQuad.Uv2 = CUV(1.f, 1.f); _BlurQuad.Uv3 = CUV(0.0f, 1.f); // display UCamera pCam = _Scene->getCam(); _Driver->setMatrixMode2D11(); _Driver->drawQuad(_BlurQuad, _BlurMat); // disable render target and vertex program drvInternal->activeVertexProgram(NULL); CTextureUser cu; ((CDriverUser *)_Driver)->setRenderTarget(cu, 0, 0, 0, 0); _Driver->setMatrixMode3D(pCam); } }; // NL3D