qzdoom/src/gl/renderer/gl_renderstate.cpp
2013-06-23 09:49:34 +02:00

352 lines
9.7 KiB
C++

/*
** gl_renderstate.cpp
** Render state maintenance
**
**---------------------------------------------------------------------------
** Copyright 2009 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be
** covered by the terms of the GNU Lesser General Public License as published
** by the Free Software Foundation; either version 2.1 of the License, or (at
** your option) any later version.
** 5. Full disclosure of the entire project's source code, except for third
** party libraries is mandatory. (NOTE: This clause is non-negotiable!)
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "gl/system/gl_system.h"
#include "gl/data/gl_data.h"
#include "gl/system/gl_cvars.h"
#include "gl/shaders/gl_shader.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/renderer/gl_colormap.h"
FRenderState gl_RenderState;
int FStateAttr::ChangeCounter;
CVAR(Bool, gl_direct_state_change, true, 0)
//==========================================================================
//
//
//
//==========================================================================
void FRenderState::Reset()
{
mTextureEnabled = true;
mBrightmapEnabled = mFogEnabled = mGlowEnabled = mLightEnabled = false;
ffTextureEnabled = ffFogEnabled = false;
mSpecialEffect = ffSpecialEffect = EFF_NONE;
mFogColor.d = ffFogColor.d = -1;
mFogDensity = ffFogDensity = 0;
mTextureMode = ffTextureMode = -1;
mSrcBlend = GL_SRC_ALPHA;
mDstBlend = GL_ONE_MINUS_SRC_ALPHA;
glSrcBlend = glDstBlend = -1;
glAlphaFunc = -1;
mAlphaFunc = GL_GEQUAL;
mAlphaThreshold = 0.5f;
mBlendEquation = GL_FUNC_ADD;
glBlendEquation = -1;
m2D = true;
}
//==========================================================================
//
// Set texture shader info
//
//==========================================================================
int FRenderState::SetupShader(bool cameratexture, int &shaderindex, int &cm, float warptime)
{
bool usecmshader;
int softwarewarp = 0;
if (shaderindex == 3)
{
// Brightmap should not be used.
if (!mBrightmapEnabled || cm >= CM_FIRSTSPECIALCOLORMAP)
{
shaderindex = 0;
}
}
if (gl.shadermodel == 4)
{
usecmshader = cm > CM_DEFAULT && cm < CM_MAXCOLORMAP && mTextureMode != TM_MASK;
}
else if (gl.shadermodel == 3)
{
usecmshader = (cameratexture || gl_colormap_shader) &&
cm > CM_DEFAULT && cm < CM_MAXCOLORMAP && mTextureMode != TM_MASK;
if (!gl_brightmap_shader && shaderindex == 3)
{
shaderindex = 0;
}
else if (!gl_warp_shader && shaderindex !=3)
{
if (shaderindex <= 2) softwarewarp = shaderindex;
shaderindex = 0;
}
}
else
{
usecmshader = cameratexture;
softwarewarp = shaderindex > 0 && shaderindex < 3? shaderindex : 0;
shaderindex = 0;
}
mEffectState = shaderindex;
mColormapState = usecmshader? cm : CM_DEFAULT;
if (usecmshader) cm = CM_DEFAULT;
mWarpTime = warptime;
return softwarewarp;
}
//==========================================================================
//
// Apply shader settings
//
//==========================================================================
bool FRenderState::ApplyShader()
{
bool useshaders = false;
FShader *activeShader = NULL;
if (mSpecialEffect > 0 && gl.shadermodel > 2)
{
activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect);
}
else
{
switch (gl.shadermodel)
{
case 2:
useshaders = (mTextureEnabled && mColormapState != CM_DEFAULT);
break;
case 3:
useshaders = (
mEffectState != 0 || // special shaders
(mFogEnabled && (gl_fogmode == 2 || gl_fog_shader) && gl_fogmode != 0) || // fog requires a shader
(mTextureEnabled && (mEffectState != 0 || mColormapState)) || // colormap
mGlowEnabled // glow requires a shader
);
break;
case 4:
useshaders = (!m2D || mEffectState != 0 || mColormapState); // all 3D rendering and 2D with texture effects.
break;
default:
break;
}
if (useshaders)
{
FShaderContainer *shd = GLRenderer->mShaderManager->Get(mTextureEnabled? mEffectState : 4);
if (shd != NULL)
{
activeShader = shd->Bind(mColormapState, mGlowEnabled, mWarpTime, mLightEnabled);
}
}
}
if (activeShader)
{
int fogset = 0;
if (mFogEnabled)
{
if ((mFogColor & 0xffffff) == 0)
{
fogset = gl_fogmode;
}
else
{
fogset = -gl_fogmode;
}
}
if (fogset != activeShader->currentfogenabled)
{
gl.Uniform1i(activeShader->fogenabled_index, (activeShader->currentfogenabled = fogset));
}
if (mTextureMode != activeShader->currenttexturemode)
{
gl.Uniform1i(activeShader->texturemode_index, (activeShader->currenttexturemode = mTextureMode));
}
if (activeShader->currentcamerapos.Update(&mCameraPos))
{
gl.Uniform3fv(activeShader->camerapos_index, 1, mCameraPos.vec);
}
/*if (mLightParms[0] != activeShader->currentlightfactor ||
mLightParms[1] != activeShader->currentlightdist ||
mFogDensity != activeShader->currentfogdensity)*/
{
const float LOG2E = 1.442692f; // = 1/log(2)
//activeShader->currentlightdist = mLightParms[1];
//activeShader->currentlightfactor = mLightParms[0];
//activeShader->currentfogdensity = mFogDensity;
// premultiply the density with as much as possible here to reduce shader
// execution time.
gl.VertexAttrib4f(VATTR_FOGPARAMS, mLightParms[0], mLightParms[1], mFogDensity * (-LOG2E / 64000.f), 0);
}
if (mFogColor != activeShader->currentfogcolor)
{
activeShader->currentfogcolor = mFogColor;
gl.Uniform4f (activeShader->fogcolor_index, mFogColor.r/255.f, mFogColor.g/255.f,
mFogColor.b/255.f, 0);
}
if (mGlowEnabled)
{
gl.Uniform4fv(activeShader->glowtopcolor_index, 1, mGlowTop.vec);
gl.Uniform4fv(activeShader->glowbottomcolor_index, 1, mGlowBottom.vec);
}
if (mLightEnabled)
{
gl.Uniform3iv(activeShader->lightrange_index, 1, mNumLights);
gl.Uniform4fv(activeShader->lights_index, mNumLights[2], mLightData);
}
if (glset.lightmode == 8)
{
gl.Uniform3fv(activeShader->dlightcolor_index, 1, mDynLight);
}
return true;
}
return false;
}
//==========================================================================
//
// Apply State
//
//==========================================================================
void FRenderState::Apply(bool forcenoshader)
{
if (!gl_direct_state_change)
{
if (mSrcBlend != glSrcBlend || mDstBlend != glDstBlend)
{
glSrcBlend = mSrcBlend;
glDstBlend = mDstBlend;
glBlendFunc(mSrcBlend, mDstBlend);
}
if (mAlphaFunc != glAlphaFunc || mAlphaThreshold != glAlphaThreshold)
{
glAlphaFunc = mAlphaFunc;
glAlphaThreshold = mAlphaThreshold;
::glBlendFunc(mAlphaFunc, mAlphaThreshold);
}
if (mAlphaTest != glAlphaTest)
{
glAlphaTest = mAlphaTest;
if (mAlphaTest) glEnable(GL_ALPHA_TEST);
else glDisable(GL_ALPHA_TEST);
}
if (mBlendEquation != glBlendEquation)
{
glBlendEquation = mBlendEquation;
gl.BlendEquation(mBlendEquation);
}
}
if (forcenoshader || !ApplyShader())
{
GLRenderer->mShaderManager->SetActiveShader(NULL);
if (mTextureMode != ffTextureMode)
{
gl.SetTextureMode((ffTextureMode = mTextureMode));
}
if (mTextureEnabled != ffTextureEnabled)
{
if ((ffTextureEnabled = mTextureEnabled)) gl.Enable(GL_TEXTURE_2D);
else gl.Disable(GL_TEXTURE_2D);
}
if (mFogEnabled != ffFogEnabled)
{
if ((ffFogEnabled = mFogEnabled))
{
gl.Enable(GL_FOG);
}
else gl.Disable(GL_FOG);
}
if (mFogEnabled)
{
if (ffFogColor != mFogColor)
{
ffFogColor = mFogColor;
GLfloat FogColor[4]={mFogColor.r/255.0f,mFogColor.g/255.0f,mFogColor.b/255.0f,0.0f};
gl.Fogfv(GL_FOG_COLOR, FogColor);
}
if (ffFogDensity != mFogDensity)
{
gl.Fogf(GL_FOG_DENSITY, mFogDensity/64000.f);
ffFogDensity=mFogDensity;
}
}
if (mSpecialEffect != ffSpecialEffect)
{
switch (ffSpecialEffect)
{
case EFF_SPHEREMAP:
gl.Disable(GL_TEXTURE_GEN_T);
gl.Disable(GL_TEXTURE_GEN_S);
default:
break;
}
switch (mSpecialEffect)
{
case EFF_SPHEREMAP:
// Use sphere mapping for this
gl.Enable(GL_TEXTURE_GEN_T);
gl.Enable(GL_TEXTURE_GEN_S);
gl.TexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP);
gl.TexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP);
break;
default:
break;
}
ffSpecialEffect = mSpecialEffect;
}
}
}