/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "ShaderProgram.h"
#include "graphics/ShaderDefines.h"
#include "graphics/TextureManager.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/Matrix3D.h"
#include "maths/Vector3D.h"
#include "ps/CLogger.h"
#include "renderer/Renderer.h"
#if !CONFIG2_GLES
/**
* CShaderProgramFFP allows rendering code to use the shader-based API
* even if the 'shader' is actually implemented with the fixed-function
* pipeline instead of anything programmable.
*
* Currently we just hard-code a number of FFP programs as subclasses of this.
* If we have lots, it might be nicer to abstract out the common functionality
* and load these from text files or something.
*/
class CShaderProgramFFP : public CShaderProgram
{
public:
CShaderProgramFFP(int streamflags) :
CShaderProgram(streamflags)
{
}
~CShaderProgramFFP()
{
}
virtual void Reload()
{
m_IsValid = true;
}
int GetUniformIndex(CStrIntern id)
{
std::map::iterator it = m_UniformIndexes.find(id);
if (it == m_UniformIndexes.end())
return -1;
return it->second;
}
virtual Binding GetTextureBinding(uniform_id_t id)
{
int index = GetUniformIndex(CStrIntern(id));
if (index == -1)
return Binding();
else
return Binding((int)GL_TEXTURE_2D, index);
}
virtual void BindTexture(texture_id_t id, Handle tex)
{
int index = GetUniformIndex(CStrIntern(id));
if (index != -1)
ogl_tex_bind(tex, index);
}
virtual void BindTexture(texture_id_t id, GLuint tex)
{
int index = GetUniformIndex(CStrIntern(id));
if (index != -1)
{
pglActiveTextureARB((int)(GL_TEXTURE0+index));
glBindTexture(GL_TEXTURE_2D, tex);
}
}
virtual void BindTexture(Binding id, Handle tex)
{
int index = id.second;
if (index != -1)
ogl_tex_bind(tex, index);
}
virtual Binding GetUniformBinding(uniform_id_t id)
{
return Binding(-1, GetUniformIndex(id));
}
virtual void Uniform(Binding UNUSED(id), float UNUSED(v0), float UNUSED(v1), float UNUSED(v2), float UNUSED(v3))
{
}
virtual void Uniform(Binding UNUSED(id), const CMatrix3D& UNUSED(v))
{
}
virtual void Uniform(Binding UNUSED(id), size_t UNUSED(count), const CMatrix3D* UNUSED(v))
{
}
protected:
std::map m_UniformIndexes;
void SetUniformIndex(const char* id, int value)
{
m_UniformIndexes[CStrIntern(id)] = value;
}
};
//////////////////////////////////////////////////////////////////////////
/**
* A shader that does nothing but provide a shader-compatible interface to
* fixed-function features, for compatibility with existing fixed-function
* code that isn't fully ported to the shader API.
*/
class CShaderProgramFFP_Dummy : public CShaderProgramFFP
{
public:
CShaderProgramFFP_Dummy() :
CShaderProgramFFP(0)
{
// Texture units, for when this shader is used with terrain:
SetUniformIndex("baseTex", 0);
}
virtual void Bind()
{
BindClientStates();
}
virtual void Unbind()
{
UnbindClientStates();
}
};
//////////////////////////////////////////////////////////////////////////
class CShaderProgramFFP_OverlayLine : public CShaderProgramFFP
{
// Uniforms
enum
{
ID_losTransform,
ID_objectColor
};
bool m_IgnoreLos;
bool m_UseObjectColor;
public:
CShaderProgramFFP_OverlayLine(const CShaderDefines& defines) :
CShaderProgramFFP(0) // will be set manually in initializer below
{
SetUniformIndex("losTransform", ID_losTransform);
SetUniformIndex("objectColor", ID_objectColor);
// Texture units:
SetUniformIndex("baseTex", 0);
SetUniformIndex("maskTex", 1);
SetUniformIndex("losTex", 2);
m_IgnoreLos = (defines.GetInt("IGNORE_LOS") != 0);
m_UseObjectColor = (defines.GetInt("USE_OBJECTCOLOR") != 0);
m_StreamFlags = STREAM_POS | STREAM_UV0 | STREAM_UV1;
if (!m_UseObjectColor)
m_StreamFlags |= STREAM_COLOR;
}
bool IsIgnoreLos()
{
return m_IgnoreLos;
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_losTransform)
{
pglActiveTextureARB(GL_TEXTURE2);
GLfloat texgenS1[4] = { v0, 0, 0, v1 };
GLfloat texgenT1[4] = { 0, 0, v0, v1 };
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);
}
else if (id.second == ID_objectColor)
{
float c[] = { v0, v1, v2, v3 };
pglActiveTextureARB(GL_TEXTURE1);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, c);
}
else
{
debug_warn(L"Invalid id");
}
}
virtual void Uniform(Binding UNUSED(id), const CMatrix3D& UNUSED(v))
{
debug_warn(L"Not implemented");
}
virtual void Bind()
{
// RGB channels:
// Unit 0: Sample base texture
// Unit 1: Sample mask texture; interpolate with [objectColor or vertexColor] and base, depending on USE_OBJECTCOLOR
// Unit 2: (Load LOS texture; multiply) if not #IGNORE_LOS, pass through otherwise
// Alpha channel:
// Unit 0: Sample base texture
// Unit 1: Multiply by objectColor
// Unit 2: Pass through
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
// Sample base texture RGB
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
// Sample base texture Alpha
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// -----------------------------------------------------------------------------
pglActiveTextureARB(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
// RGB: interpolate component-wise between [objectColor or vertexColor] and base using mask:
// a0 * a2 + a1 * (1 - a2)
// Overridden implementation of Uniform() sets GL_TEXTURE_ENV_COLOR to objectColor, which
// is referenced as GL_CONSTANT (see spec)
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, m_UseObjectColor ? GL_CONSTANT : GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR);
// ALPHA: Multiply base alpha with [objectColor or vertexColor] alpha
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, m_UseObjectColor ? GL_CONSTANT : GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
// -----------------------------------------------------------------------------
pglActiveTextureARB(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
bool ignoreLos = IsIgnoreLos();
if (ignoreLos)
{
// RGB pass through
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
}
else
{
// Multiply RGB result up till now with LoS texture alpha channel
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
// Overridden implementation of Uniform() sets GL_OBJECT_PLANE values
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
}
// alpha pass through
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
BindClientStates();
}
virtual void Unbind()
{
UnbindClientStates();
pglActiveTextureARB(GL_TEXTURE2);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
pglActiveTextureARB(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
}
};
//////////////////////////////////////////////////////////////////////////
class CShaderProgramFFP_GuiText : public CShaderProgramFFP
{
// Uniforms
enum
{
ID_transform,
ID_colorMul
};
public:
CShaderProgramFFP_GuiText() :
CShaderProgramFFP(STREAM_POS | STREAM_UV0)
{
SetUniformIndex("transform", ID_transform);
SetUniformIndex("colorMul", ID_colorMul);
// Texture units:
SetUniformIndex("tex", 0);
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_colorMul)
glColor4f(v0, v1, v2, v3);
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.second == ID_transform)
glLoadMatrixf(&v._11);
}
virtual void Bind()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
BindClientStates();
}
virtual void Unbind()
{
UnbindClientStates();
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
};
//////////////////////////////////////////////////////////////////////////
class CShaderProgramFFP_Gui_Base : public CShaderProgramFFP
{
protected:
// Uniforms
enum
{
ID_transform,
ID_color
};
public:
CShaderProgramFFP_Gui_Base(int streamflags) :
CShaderProgramFFP(streamflags)
{
SetUniformIndex("transform", ID_transform);
SetUniformIndex("color", ID_color);
// Texture units:
SetUniformIndex("tex", 0);
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.second == ID_transform)
glLoadMatrixf(&v._11);
}
virtual void Bind()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
BindClientStates();
}
virtual void Unbind()
{
UnbindClientStates();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
};
class CShaderProgramFFP_GuiMinimap : public CShaderProgramFFP
{
protected:
CShaderDefines m_Defines;
// Uniforms
enum
{
ID_transform,
ID_textureTransform,
ID_color,
ID_pointSize,
};
public:
CShaderProgramFFP_GuiMinimap(const CShaderDefines& defines) :
CShaderProgramFFP(0) // We set the streamflags later, during initialization.
{
m_Defines = defines;
SetUniformIndex("transform", ID_transform);
SetUniformIndex("textureTransform", ID_textureTransform);
SetUniformIndex("color", ID_color);
SetUniformIndex("pointSize", ID_pointSize);
if (m_Defines.GetInt("MINIMAP_BASE") || m_Defines.GetInt("MINIMAP_LOS"))
{
SetUniformIndex("baseTex", 0);
m_StreamFlags = STREAM_POS | STREAM_UV0;
}
else if (m_Defines.GetInt("MINIMAP_POINT"))
m_StreamFlags = STREAM_POS | STREAM_COLOR;
else
m_StreamFlags = STREAM_POS;
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.second == ID_textureTransform)
{
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(&v._11);
}
else if (id.second == ID_transform)
{
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&v._11);
}
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_color)
glColor4f(v0, v1, v2, v3);
else if (id.second == ID_pointSize)
glPointSize(v0);
}
virtual void Bind()
{
// Setup matrix environment
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
BindClientStates();
// Setup texture environment
if (m_Defines.GetInt("MINIMAP_BASE"))
{
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else if (m_Defines.GetInt("MINIMAP_LOS"))
{
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);
glColor3f(0.0f, 0.0f, 0.0f);
}
else if (m_Defines.GetInt("MINIMAP_POINT"))
{
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
}
else if (m_Defines.GetInt("MINIMAP_LINE"))
{
// JoshuaJB 13.7.2014: This doesn't seem to do anything on my drivers.
glEnable(GL_LINE_SMOOTH);
}
}
virtual void Unbind()
{
// Reset texture environment
if (m_Defines.GetInt("MINIMAP_POINT"))
{
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
else if (m_Defines.GetInt("MINIMAP_LINE"))
{
glDisable(GL_LINE_SMOOTH);
}
UnbindClientStates();
// Clear matrix stack
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
};
class CShaderProgramFFP_GuiBasic : public CShaderProgramFFP_Gui_Base
{
public:
CShaderProgramFFP_GuiBasic() :
CShaderProgramFFP_Gui_Base(STREAM_POS | STREAM_UV0)
{
}
virtual void Bind()
{
CShaderProgramFFP_Gui_Base::Bind();
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
virtual void Unbind()
{
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
CShaderProgramFFP_Gui_Base::Unbind();
}
};
class CShaderProgramFFP_GuiAdd : public CShaderProgramFFP_Gui_Base
{
public:
using CShaderProgramFFP_Gui_Base::Uniform;
CShaderProgramFFP_GuiAdd() :
CShaderProgramFFP_Gui_Base(STREAM_POS | STREAM_UV0)
{
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_color)
glColor4f(v0, v1, v2, v3);
}
virtual void Bind()
{
CShaderProgramFFP_Gui_Base::Bind();
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
}
virtual void Unbind()
{
glColor4f(1.f, 1.f, 1.f, 1.f);
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
CShaderProgramFFP_Gui_Base::Unbind();
}
};
class CShaderProgramFFP_GuiGrayscale : public CShaderProgramFFP_Gui_Base
{
public:
CShaderProgramFFP_GuiGrayscale() :
CShaderProgramFFP_Gui_Base(STREAM_POS | STREAM_UV0)
{
}
virtual void Bind()
{
CShaderProgramFFP_Gui_Base::Bind();
/*
For the main conversion, use GL_DOT3_RGB, which is defined as
L = 4 * ((Arg0r - 0.5) * (Arg1r - 0.5)+
(Arg0g - 0.5) * (Arg1g - 0.5)+
(Arg0b - 0.5) * (Arg1b - 0.5))
where each of the RGB components is given the value 'L'.
Use the magical luminance formula
L = 0.3R + 0.59G + 0.11B
to calculate the greyscale value.
But to work around the annoying "Arg0-0.5", we need to calculate
Arg0+0.5. But we also need to scale it into the range 0.5-1.0, else
Arg0>0.5 will be clamped to 1.0. So use GL_INTERPOLATE, which outputs:
A0 * A2 + A1 * (1 - A2)
and set A2 = 0.5, A1 = 1.0, and A0 = texture (i.e. interpolating halfway
between the texture and {1,1,1}) giving
A0/2 + 0.5
and use that as Arg0.
So L = 4*(A0/2 * (Arg1-.5))
= 2 (Rx+Gy+Bz) (where Arg1 = {x+0.5, y+0.5, z+0.5})
= 2x R + 2y G + 2z B
= 0.3R + 0.59G + 0.11B
so e.g. 2y = 0.59 = 2(Arg1g-0.5) => Arg1g = 0.59/2+0.5
which fortunately doesn't get clamped.
So, just implement that:
*/
static const float GreyscaleDotColor[4] = {
0.3f / 2.f + 0.5f,
0.59f / 2.f + 0.5f,
0.11f / 2.f + 0.5f,
1.0f
};
static const float GreyscaleInterpColor0[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const float GreyscaleInterpColor1[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, GreyscaleInterpColor0);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
glColor4fv(GreyscaleInterpColor1);
pglActiveTextureARB(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
// GL_DOT3_RGB requires GL_(EXT|ARB)_texture_env_dot3.
// We currently don't bother implementing a fallback because it's
// only lacking on Riva-class HW, but at least want the rest of the
// game to run there without errors. Therefore, squelch the
// OpenGL error that's raised if they aren't actually present.
// Note: higher-level code checks for this extension, but
// allows users the choice of continuing even if not present.
ogl_SquelchError(GL_INVALID_ENUM);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, GreyscaleDotColor);
// To activate the second texture unit, we have to have some kind
// of texture bound into it, but we don't actually use the texture data,
// so bind a dummy texture
g_Renderer.GetTextureManager().GetErrorTexture()->Bind(1);
}
virtual void Unbind()
{
glColor4f(1.f, 1.f, 1.f, 1.f);
pglActiveTextureARB(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
CShaderProgramFFP_Gui_Base::Unbind();
}
};
class CShaderProgramFFP_GuiSolid : public CShaderProgramFFP_Gui_Base
{
public:
using CShaderProgramFFP_Gui_Base::Uniform;
CShaderProgramFFP_GuiSolid() :
CShaderProgramFFP_Gui_Base(STREAM_POS)
{
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_color)
glColor4f(v0, v1, v2, v3);
}
virtual void Bind()
{
CShaderProgramFFP_Gui_Base::Bind();
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
}
};
//////////////////////////////////////////////////////////////////////////
/**
* Common functionality for model rendering in the fixed renderpath.
*/
class CShaderProgramFFP_Model_Base : public CShaderProgramFFP
{
protected:
// Uniforms
enum
{
ID_transform,
ID_objectColor,
ID_playerColor,
ID_losTransform
};
bool m_IgnoreLos;
public:
CShaderProgramFFP_Model_Base(const CShaderDefines& defines, int streamflags)
: CShaderProgramFFP(streamflags)
{
SetUniformIndex("transform", ID_transform);
SetUniformIndex("losTransform", ID_losTransform);
if (defines.GetInt("USE_OBJECTCOLOR"))
SetUniformIndex("objectColor", ID_objectColor);
if (defines.GetInt("USE_PLAYERCOLOR"))
SetUniformIndex("playerColor", ID_playerColor);
m_IgnoreLos = (defines.GetInt("IGNORE_LOS") != 0);
// Texture units:
SetUniformIndex("baseTex", 0);
SetUniformIndex("losTex", 3);
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.second == ID_transform)
glLoadMatrixf(&v._11);
}
virtual void Uniform(Binding id, float v0, float v1, float UNUSED(v2), float UNUSED(v3))
{
if (id.second == ID_losTransform)
{
pglActiveTextureARB(GL_TEXTURE3);
GLfloat texgenS1[4] = { v0, 0, 0, v1 };
GLfloat texgenT1[4] = { 0, 0, v0, v1 };
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);
}
}
virtual void Bind()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// -----------------------------------------------------------------------------
pglActiveTextureARB(GL_TEXTURE3);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
if (m_IgnoreLos)
{
// RGB pass through
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
}
else
{
// Multiply RGB result up till now with LoS texture alpha channel
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
// Overridden implementation of Uniform() sets GL_OBJECT_PLANE values
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
}
// alpha pass through
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// -----------------------------------------------------------------------------
BindClientStates();
}
virtual void Unbind()
{
UnbindClientStates();
pglActiveTextureARB(GL_TEXTURE3);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
pglActiveTextureARB(GL_TEXTURE0);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
};
/**
* Basic non-recolored diffuse-textured model rendering.
*/
class CShaderProgramFFP_Model : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_Model(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_COLOR | STREAM_UV0)
{
}
virtual void Bind()
{
// Set up texture environment for base pass - modulate texture and vertex color
pglActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Copy alpha channel from texture
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
// The vertex color is scaled by 0.5 to permit overbrightness without clamping.
// We therefore need to scale by 2.0 after the modulation, and before any
// further clamping, to get the right color.
float scale2[] = { 2.0f, 2.0f, 2.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale2);
CShaderProgramFFP_Model_Base::Bind();
}
virtual void Unbind()
{
CShaderProgramFFP_Model_Base::Unbind();
pglActiveTextureARB(GL_TEXTURE0);
// Revert the scaling to default
float scale1[] = { 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale1);
}
};
/**
* Player-coloring diffuse-textured model rendering.
*/
class CShaderProgramFFP_ModelColor : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_ModelColor(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_COLOR | STREAM_UV0)
{
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_objectColor || id.second == ID_playerColor)
{
// (Player color and object color are mutually exclusive)
float color[] = { v0, v1, v2, v3 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
}
}
virtual void Bind()
{
// Player color uses a single pass with three texture environments
// Note: This uses ARB_texture_env_crossbar (which is checked in GameSetup),
// and it requires MAX_TEXTURE_IMAGE_UNITS >= 3 (which only excludes GF2MX/GF4MX)
//
// We calculate: Result = Color*Texture*(PlayerColor*(1-Texture.a) + 1.0*Texture.a)
// Algebra gives us:
// Result = (1 - ((1 - PlayerColor) * (1 - Texture.a)))*Texture*Color
// TexEnv #0
pglActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
// TexEnv #1
pglActiveTextureARB(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
// TexEnv #2
pglActiveTextureARB(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
// Scale colors at the end of all the computation (see CShaderProgramFFP_Model::Bind)
float scale[] = { 2.0f, 2.0f, 2.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale);
// Need to bind some kind of texture to enable the texture units.
// Unit 0 has baseTex, but the others need a texture.
g_Renderer.GetTextureManager().GetErrorTexture()->Bind(1);
g_Renderer.GetTextureManager().GetErrorTexture()->Bind(2);
CShaderProgramFFP_Model_Base::Bind();
}
virtual void Unbind()
{
CShaderProgramFFP_Model_Base::Unbind();
pglActiveTextureARB(GL_TEXTURE2);
glDisable(GL_TEXTURE_2D);
float scale[] = { 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale);
pglActiveTextureARB(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0);
}
};
/**
* Optionally-player-colored untextured model rendering.
*/
class CShaderProgramFFP_ModelSolid : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_ModelSolid(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS)
{
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_playerColor)
{
float color[] = { v0, v1, v2, v3 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
}
}
virtual void Bind()
{
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
CShaderProgramFFP_Model_Base::Bind();
}
};
/**
* Plain unlit texture model rendering, for e.g. alpha-blended shadow casters.
*/
class CShaderProgramFFP_ModelSolidTex : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_ModelSolidTex(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_UV0)
{
}
virtual void Bind()
{
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
CShaderProgramFFP_Model_Base::Bind();
}
};
//////////////////////////////////////////////////////////////////////////
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& id, const CShaderDefines& defines)
{
if (id == "dummy")
return new CShaderProgramFFP_Dummy();
if (id == "overlayline")
return new CShaderProgramFFP_OverlayLine(defines);
if (id == "gui_text")
return new CShaderProgramFFP_GuiText();
if (id == "gui_basic")
return new CShaderProgramFFP_GuiBasic();
if (id == "gui_add")
return new CShaderProgramFFP_GuiAdd();
if (id == "gui_grayscale")
return new CShaderProgramFFP_GuiGrayscale();
if (id == "gui_solid")
return new CShaderProgramFFP_GuiSolid();
if (id == "minimap")
return new CShaderProgramFFP_GuiMinimap(defines);
if (id == "solid")
return new CShaderProgramFFP_GuiSolid(); // works for non-GUI objects too
if (id == "model")
return new CShaderProgramFFP_Model(defines);
if (id == "model_color")
return new CShaderProgramFFP_ModelColor(defines);
if (id == "model_solid")
return new CShaderProgramFFP_ModelSolid(defines);
if (id == "model_solid_tex")
return new CShaderProgramFFP_ModelSolidTex(defines);
LOGERROR("CShaderProgram::ConstructFFP: '%s': Invalid id", id.c_str());
debug_warn(L"CShaderProgram::ConstructFFP: Invalid id");
return NULL;
}
#else // CONFIG2_GLES
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& UNUSED(id), const CShaderDefines& UNUSED(defines))
{
debug_warn(L"CShaderProgram::ConstructFFP: FFP not supported on this device");
return NULL;
}
#endif // CONFIG2_GLES