Move GUI renderer towards using shader infrastructure.

Remove support for unused GUI sprite effects.
Use vertex arrays for bounding box rendering.

This was SVN commit r11039.
This commit is contained in:
Ykkrosh 2012-02-08 20:43:38 +00:00
parent bbded2db91
commit 1f5b8f1c9a
12 changed files with 396 additions and 457 deletions

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -19,11 +19,13 @@
#include "ShaderProgram.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 "ps/Overlay.h"
#include "renderer/Renderer.h"
/**
* CShaderProgramFFP allows rendering code to use the shader-based API
@ -93,6 +95,14 @@ public:
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))
{
}
protected:
std::map<CStr, int> m_UniformIndexes;
};
@ -267,8 +277,6 @@ class CShaderProgramFFP_GuiText : public CShaderProgramFFP
ID_colorMul
};
bool m_IgnoreLos;
public:
CShaderProgramFFP_GuiText() :
CShaderProgramFFP(STREAM_POS | STREAM_UV0)
@ -283,17 +291,13 @@ public:
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()
@ -323,12 +327,287 @@ public:
//////////////////////////////////////////////////////////////////////////
class CShaderProgramFFP_Gui_Base : public CShaderProgramFFP
{
protected:
// Uniforms
enum
{
ID_transform,
ID_color
};
public:
CShaderProgramFFP_Gui_Base(int streamflags) :
CShaderProgramFFP(streamflags)
{
m_UniformIndexes["transform"] = ID_transform;
m_UniformIndexes["color"] = ID_color;
// Texture units:
m_UniformIndexes["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();
}
virtual void Unbind()
{
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:
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:
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);
}
};
//////////////////////////////////////////////////////////////////////////
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& id, const std::map<CStr, CStr>& defines)
{
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();
LOGERROR(L"CShaderProgram::ConstructFFP: Invalid id '%hs'", id.c_str());
return NULL;

View file

@ -1573,8 +1573,6 @@ void CGUI::Xeromyces_ReadImage(XMBElement Element, CXeromyces* pFile, CGUISprite
image.m_WrapMode = GL_MIRRORED_REPEAT;
else if (attr_value == L"clamp_to_edge")
image.m_WrapMode = GL_CLAMP_TO_EDGE;
else if (attr_value == L"clamp_to_border")
image.m_WrapMode = GL_CLAMP_TO_BORDER;
else
LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str());
}
@ -1656,28 +1654,18 @@ void CGUI::Xeromyces_ReadEffects(XMBElement Element, CXeromyces* pFile, SGUIImag
CStr attr_name (pFile->GetAttributeString(attr.Name));
CStrW attr_value (attr.Value.FromUTF8());
#define COLOR(xml, mem, alpha) \
if (attr_name == xml) \
{ \
CColor color; \
if (!GUI<int>::ParseColor(attr_value, color, alpha)) \
LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); \
else effects.m_##mem = color; \
} \
if (attr_name == "add_color")
{
CColor color;
if (!GUI<int>::ParseColor(attr_value, color, 0.f))
LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str());
else effects.m_AddColor = color;
}
else if (attr_name == "grayscale")
{
effects.m_Greyscale = true;
}
else
#define BOOL(xml, mem) \
if (attr_name == xml) \
{ \
effects.m_##mem = true; \
} \
else
COLOR("add_color", AddColor, 0.f)
COLOR("multiply_color", MultiplyColor, 255.f)
BOOL("grayscale", Greyscale)
{
debug_warn(L"Invalid data - DTD shouldn't allow this");
}

View file

@ -66,7 +66,6 @@ struct SGUIImageEffects
{
SGUIImageEffects() : m_Greyscale(false) {}
CColor m_AddColor;
CColor m_MultiplyColor;
bool m_Greyscale;
};
@ -116,7 +115,7 @@ struct SGUIImage
bool m_RoundCoordinates;
/**
* Texture wrapping mode (GL_REPEAT, GL_CLAMP_TO_BORDER, etc)
* Texture wrapping mode (GL_REPEAT, GL_CLAMP_TO_EDGE, etc)
*/
GLint m_WrapMode;

View file

@ -19,6 +19,7 @@
#include "GUIRenderer.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextureManager.h"
#include "gui/GUIutil.h"
#include "lib/ogl.h"
@ -33,24 +34,10 @@
using namespace GUIRenderer;
void DrawCalls::clear()
{
for (iterator it = begin(); it != end(); ++it)
{
delete it->m_Effects;
}
std::vector<SDrawCall>::clear();
}
DrawCalls::DrawCalls()
{
}
DrawCalls::~DrawCalls()
{
clear();
}
// DrawCalls needs to be copyable, so it can be used in other copyable types.
// But actually copying data is hard, since we'd need to avoid losing track of
// who owns various pointers, so instead we just return an empty list.
@ -69,293 +56,6 @@ const DrawCalls& DrawCalls::operator=(const DrawCalls&)
}
// Implementations of graphical effects:
const GLint TexScale1[3] = { 1, 1, 1 };
const GLint TexScale2[3] = { 2, 2, 2 };
const GLint TexScale4[3] = { 4, 4, 4 };
class Effect_AddColor : public IGLState
{
// Uses GL_COMBINE and GL_ADD/GL_SUBTRACT/GL_ADD_SIGNED, to allow
// addition/subtraction of colors.
public:
Effect_AddColor(CColor c)
{
// If everything's in [0,1], use GL_ADD
#define RANGE(lo,hi) c.r >= lo && c.r <= hi && c.g >= lo && c.g <= hi && c.b >= lo && c.b <= hi && c.a >= lo && c.a <= hi
if (RANGE(0.f, 1.f))
{
m_Color = c;
m_Method = ADD_NORMAL;
}
// If it's in [-1, 0] use GL_SUBTRACT
else if (RANGE(-1.f, 0.f))
{
m_Color = CColor(-c.r, -c.g, -c.b, -c.a);
m_Method = ADD_SUBTRACT;
}
// If it's in [-0.5, 0.5] use GL_ADD_SIGNED
else if (RANGE(-0.5f, 0.5f))
{
m_Color = CColor(c.r+0.5f, c.g+0.5f, c.b+0.5f, c.a+0.5f);
m_Method = ADD_SIGNED;
}
// Otherwise, complain.
else
{
LOGWARNING(L"add_color effect has some components above 127 and some below -127 - colours will be clamped");
m_Color = CColor(c.r+0.5f, c.g+0.5f, c.b+0.5f, c.a+0.5f);
m_Method = ADD_SIGNED;
}
}
~Effect_AddColor() {}
void Set(const CTexturePtr& tex)
{
glEnable(GL_TEXTURE_2D);
glColor4fv(m_Color.FloatArray());
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
if (m_Method == ADD_NORMAL)
{
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
}
else
if (m_Method == ADD_SUBTRACT)
{
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_SUBTRACT);
}
else // if (m_Method == ADD_SIGNED)
{
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD_SIGNED);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD_SIGNED);
}
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);
tex->Bind();
}
void Unset()
{
glColor4f(1.f, 1.f, 1.f, 1.f);
}
private:
CColor m_Color;
enum { ADD_NORMAL, ADD_SUBTRACT, ADD_SIGNED, ADD_SIGNED_DOUBLED } m_Method;
};
class Effect_MultiplyColor : public IGLState
{
// Uses GL_MODULATE to do the multiplication; but since all colours are
// clamped to the range [0,1], it uses GL_RGB_SCALE to allow images to be
// multiplied by [0,4]. Alpha is assumed to always be [0,1].
public:
Effect_MultiplyColor(CColor c)
{
if (c.r <= 1.f && c.g <= 1.f && c.b <= 1.f)
{
m_Color = c;
m_Scale = 1;
}
else if (c.r <= 2.f && c.g <= 2.f && c.b <= 2.f)
{
m_Color = CColor(c.r/2.f, c.g/2.f, c.b/2.f, c.a);
m_Scale = 2;
}
else
{
if (c.r <= 4.f && c.g <= 4.f && c.b <= 4.f)
;
else
// Oops - trying to multiply by >4
LOGWARNING(L"multiply_color effect has a component >1020 - colours will be clamped");
m_Color = CColor(c.r/4.f, c.g/4.f, c.b/4.f, c.a);
m_Scale = 4;
}
}
~Effect_MultiplyColor() {}
void Set(const CTexturePtr& tex)
{
glEnable(GL_TEXTURE_2D);
glColor4fv(m_Color.FloatArray());
if (m_Scale == 1)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
else
{
// Duplicate the effect of GL_MODULATE, but using GL_COMBINE
// so that GL_RGB_SCALE will work.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, 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_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);
if (m_Scale == 2)
glTexEnviv(GL_TEXTURE_ENV, GL_RGB_SCALE, TexScale2);
else if (m_Scale == 4)
glTexEnviv(GL_TEXTURE_ENV, GL_RGB_SCALE, TexScale4);
}
tex->Bind();
}
void Unset()
{
if (m_Scale != 1)
glTexEnviv(GL_TEXTURE_ENV, GL_RGB_SCALE, TexScale1);
glColor4f(1.f, 1.f, 1.f, 1.f);
}
private:
CColor m_Color;
int m_Scale;
};
#define X(n) (n##f/2.0f + 0.5f)
const float GreyscaleDotColor[4] = { X(0.3), X(0.59), X(0.11), 1.0f };
#undef X
const float GreyscaleInterpColor0[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
const float GreyscaleInterpColor1[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
class Effect_Greyscale : public IGLState
{
public:
~Effect_Greyscale() {}
void Set(const CTexturePtr& tex)
{
/*
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:
*/
// TODO: Render all greyscale objects at the same time, to reduce
// the number of times the following code is called - it looks like
// a rather worrying amount of work for rendering a single button...
// Texture unit 0:
tex->Bind(0);
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);
// Texture unit 1:
tex->Bind(1);
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);
}
void Unset()
{
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0);
glColor4f(1.f, 1.f, 1.f, 1.f);
}
};
// Functions to perform drawing-related actions:
void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, const CStr& SpriteName, const CRect &Size, int CellID, std::map<CStr, CGUISprite> &Sprites)
{
// This is called only when something has changed (like the size of the
@ -485,37 +185,34 @@ void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, const CStr& SpriteName,
Call.m_BackColor = cit->m_BackColor;
Call.m_BorderColor = cit->m_Border ? cit->m_BorderColor : CColor();
Call.m_DeltaZ = cit->m_DeltaZ;
if (cit->m_Effects)
if (!Call.m_HasTexture)
{
Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect("shader:fixed:gui_solid");
}
else if (cit->m_Effects)
{
if (cit->m_Effects->m_AddColor != CColor())
{
Call.m_Effects = new Effect_AddColor(cit->m_Effects->m_AddColor);
Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect("shader:fixed:gui_add");
Call.m_ShaderColorParameter = cit->m_Effects->m_AddColor;
// Always enable blending if something's being subtracted from
// the alpha channel
if (cit->m_Effects->m_AddColor.a < 0.f)
Call.m_EnableBlending = true;
}
else if (cit->m_Effects->m_MultiplyColor != CColor())
{
Call.m_Effects = new Effect_MultiplyColor(cit->m_Effects->m_MultiplyColor);
// Always enable blending if the alpha channel is being multiplied
if (cit->m_Effects->m_AddColor.a != 1.f)
Call.m_EnableBlending = true;
}
else if (cit->m_Effects->m_Greyscale)
{
Call.m_Effects = new Effect_Greyscale;
Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect("shader:fixed:gui_grayscale");
}
else /* Slight confusion - why no effects? */
{
Call.m_Effects = NULL;
Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect("shader:fixed:gui_basic");
}
}
else
{
Call.m_Effects = NULL;
Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect("shader:fixed:gui_basic");
}
Calls.push_back(Call);
@ -598,33 +295,31 @@ void GUIRenderer::Draw(DrawCalls &Calls, float Z)
{
// Called every frame, to draw the object (based on cached calculations)
glPushMatrix();
// TODO: batching by shader/texture/etc would be nice
CMatrix3D matrix = GetDefaultGuiMatrix();
matrix.Translate(0, 0, Z);
glLoadMatrixf(&matrix._11);
glDisable(GL_BLEND);
// Set LOD bias so mipmapped textures are prettier
#if CONFIG2_GLES
#warning TODO: implement GUI LOD bias for GLES
#else
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.f);
#endif
// Iterate through each DrawCall, and execute whatever drawing code is being called
for (DrawCalls::const_iterator cit = Calls.begin(); cit != Calls.end(); ++cit)
{
if (cit->m_HasTexture)
{
// TODO: Handle the GL state in a nicer way
cit->m_Shader->BeginPass(0);
CShaderProgramPtr shader = cit->m_Shader->GetShader(0);
shader->Uniform("transform", matrix);
shader->Uniform("color", cit->m_ShaderColorParameter);
shader->BindTexture("tex", cit->m_Texture);
if (cit->m_Effects)
cit->m_Effects->Set(cit->m_Texture);
else
{
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
cit->m_Texture->Bind();
}
if (cit->m_EnableBlending || cit->m_Texture->HasAlpha()) // (shouldn't call HasAlpha before Bind)
if (cit->m_EnableBlending || cit->m_Texture->HasAlpha()) // (shouldn't call HasAlpha before BindTexture)
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
@ -648,25 +343,27 @@ void GUIRenderer::Draw(DrawCalls &Calls, float Z)
glBegin(GL_QUADS);
glTexCoord2f(TexCoords.left, TexCoords.bottom);
glVertex3f(Verts.left, Verts.bottom, cit->m_DeltaZ);
glVertex3f(Verts.left, Verts.bottom, Z + cit->m_DeltaZ);
glTexCoord2f(TexCoords.right, TexCoords.bottom);
glVertex3f(Verts.right, Verts.bottom, cit->m_DeltaZ);
glVertex3f(Verts.right, Verts.bottom, Z + cit->m_DeltaZ);
glTexCoord2f(TexCoords.right, TexCoords.top);
glVertex3f(Verts.right, Verts.top, cit->m_DeltaZ);
glVertex3f(Verts.right, Verts.top, Z + cit->m_DeltaZ);
glTexCoord2f(TexCoords.left, TexCoords.top);
glVertex3f(Verts.left, Verts.top, cit->m_DeltaZ);
glVertex3f(Verts.left, Verts.top, Z + cit->m_DeltaZ);
glEnd();
if (cit->m_Effects)
cit->m_Effects->Unset();
cit->m_Shader->EndPass(0);
}
else
{
glDisable(GL_TEXTURE_2D);
cit->m_Shader->BeginPass(0);
CShaderProgramPtr shader = cit->m_Shader->GetShader(0);
shader->Uniform("transform", matrix);
shader->Uniform("color", cit->m_BackColor);
if (cit->m_EnableBlending)
{
@ -674,8 +371,6 @@ void GUIRenderer::Draw(DrawCalls &Calls, float Z)
glEnable(GL_BLEND);
}
glColor4fv(cit->m_BackColor.FloatArray());
// Ensure the quad has the correct winding order
CRect Verts = cit->m_Vertices;
if (Verts.right < Verts.left)
@ -684,29 +379,32 @@ void GUIRenderer::Draw(DrawCalls &Calls, float Z)
std::swap(Verts.bottom, Verts.top);
glBegin(GL_QUADS);
glVertex3f(Verts.left, Verts.bottom, cit->m_DeltaZ);
glVertex3f(Verts.right, Verts.bottom, cit->m_DeltaZ);
glVertex3f(Verts.right, Verts.top, cit->m_DeltaZ);
glVertex3f(Verts.left, Verts.top, cit->m_DeltaZ);
glVertex3f(Verts.left, Verts.bottom, Z + cit->m_DeltaZ);
glVertex3f(Verts.right, Verts.bottom, Z + cit->m_DeltaZ);
glVertex3f(Verts.right, Verts.top, Z + cit->m_DeltaZ);
glVertex3f(Verts.left, Verts.top, Z + cit->m_DeltaZ);
glEnd();
if (cit->m_BorderColor != CColor())
{
glColor4fv(cit->m_BorderColor.FloatArray());
shader->Uniform("color", cit->m_BorderColor);
glBegin(GL_LINE_LOOP);
glVertex3f(Verts.left + 0.5f, Verts.top + 0.5f, cit->m_DeltaZ);
glVertex3f(Verts.right - 0.5f, Verts.top + 0.5f, cit->m_DeltaZ);
glVertex3f(Verts.right - 0.5f, Verts.bottom - 0.5f, cit->m_DeltaZ);
glVertex3f(Verts.left + 0.5f, Verts.bottom - 0.5f, cit->m_DeltaZ);
glVertex3f(Verts.left + 0.5f, Verts.top + 0.5f, Z + cit->m_DeltaZ);
glVertex3f(Verts.right - 0.5f, Verts.top + 0.5f, Z + cit->m_DeltaZ);
glVertex3f(Verts.right - 0.5f, Verts.bottom - 0.5f, Z + cit->m_DeltaZ);
glVertex3f(Verts.left + 0.5f, Verts.bottom - 0.5f, Z + cit->m_DeltaZ);
glEnd();
}
cit->m_Shader->EndPass(0);
}
glDisable(GL_BLEND);
}
#if CONFIG2_GLES
#warning TODO: implement GUI LOD bias for GLES
#else
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.f);
glPopMatrix();
#endif
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,6 +18,7 @@
#ifndef GUIRenderer_h
#define GUIRenderer_h
#include "graphics/ShaderTechnique.h"
#include "graphics/Texture.h"
#include "lib/res/handle.h"
#include "ps/Overlay.h"
@ -39,7 +40,7 @@ namespace GUIRenderer
struct SDrawCall
{
SDrawCall(const SGUIImage* image) : m_Image(image), m_Effects(NULL) {}
SDrawCall(const SGUIImage* image) : m_Image(image) {}
CRect ComputeTexCoords() const;
const SGUIImage* m_Image;
@ -52,7 +53,8 @@ namespace GUIRenderer
bool m_EnableBlending;
IGLState* m_Effects;
CShaderTechniquePtr m_Shader;
CColor m_ShaderColorParameter;
CRect m_Vertices;
float m_DeltaZ;
@ -64,9 +66,7 @@ namespace GUIRenderer
class DrawCalls : public std::vector<SDrawCall>
{
public:
void clear();
DrawCalls();
~DrawCalls();
// Copy/assignment results in an empty list, not an actual copy
DrawCalls(const DrawCalls&);
const DrawCalls& operator=(const DrawCalls&);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -25,7 +25,7 @@ GUI utilities
#include "maths/Matrix3D.h"
#include "ps/Parser.h"
extern int g_yres;
extern int g_xres, g_yres;
#include "ps/CLogger.h"
@ -256,6 +256,11 @@ CMatrix3D GetDefaultGuiMatrix()
m.SetIdentity();
m.Scale(1.0f, -1.f, 1.0f);
m.Translate(0.0f, (float)g_yres, -1000.0f);
CMatrix3D proj;
proj.SetOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
m = proj * m;
return m;
}

View file

@ -55,7 +55,7 @@ class CMatrix3D;
template <typename T>
bool __ParseString(const CStrW& Value, T &tOutput);
// Matrix with (0,0) in top-left of screen
// Model-view-projection matrix with (0,0) in top-left of screen
CMatrix3D GetDefaultGuiMatrix();
//--------------------------------------------------------

View file

@ -299,6 +299,10 @@ void CMiniMap::Draw()
RebuildTerrainTexture();
}
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
CMatrix3D matrix = GetDefaultGuiMatrix();
glLoadMatrixf(&matrix._11);
@ -421,6 +425,10 @@ void CMiniMap::Draw()
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
// Reset everything back to normal

View file

@ -237,35 +237,35 @@ void CBoundingBoxAligned::Expand(float amount)
// Render the bounding box
void CBoundingBoxAligned::Render() const
{
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[0].Z);
glTexCoord2f(1, 0); glVertex3f(m_Data[1].X, m_Data[0].Y, m_Data[0].Z);
glTexCoord2f(1, 1); glVertex3f(m_Data[1].X, m_Data[1].Y, m_Data[0].Z);
glTexCoord2f(0, 1); glVertex3f(m_Data[0].X, m_Data[1].Y, m_Data[0].Z);
std::vector<float> data;
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[0].Z);
glTexCoord2f(1, 0); glVertex3f(m_Data[0].X, m_Data[1].Y, m_Data[0].Z);
glTexCoord2f(1, 1); glVertex3f(m_Data[0].X, m_Data[1].Y, m_Data[1].Z);
glTexCoord2f(0, 1); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[1].Z);
#define ADD_FACE(x, y, z) \
ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \
ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z);
#define ADD_PT(u_, v_, x, y, z) \
STMT(int u = u_; int v = v_; \
data.push_back(u); \
data.push_back(v); \
data.push_back(m_Data[x].X); \
data.push_back(m_Data[y].Y); \
data.push_back(m_Data[z].Z); \
)
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[1].Z);
glTexCoord2f(1, 0); glVertex3f(m_Data[1].X, m_Data[0].Y, m_Data[1].Z);
glTexCoord2f(1, 1); glVertex3f(m_Data[1].X, m_Data[0].Y, m_Data[0].Z);
glTexCoord2f(0, 1); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[0].Z);
ADD_FACE(u, v, 0);
ADD_FACE(0, u, v);
ADD_FACE(u, 0, 1-v);
ADD_FACE(u, 1-v, 1);
ADD_FACE(1, u, 1-v);
ADD_FACE(u, 1, v);
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[1].Y, m_Data[1].Z);
glTexCoord2f(1, 0); glVertex3f(m_Data[1].X, m_Data[1].Y, m_Data[1].Z);
glTexCoord2f(1, 1); glVertex3f(m_Data[1].X, m_Data[0].Y, m_Data[1].Z);
glTexCoord2f(0, 1); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[1].Z);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), &data[0]);
glVertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]);
glTexCoord2f(0, 0); glVertex3f(m_Data[1].X, m_Data[0].Y, m_Data[1].Z);
glTexCoord2f(1, 0); glVertex3f(m_Data[1].X, m_Data[1].Y, m_Data[1].Z);
glTexCoord2f(1, 1); glVertex3f(m_Data[1].X, m_Data[1].Y, m_Data[0].Z);
glTexCoord2f(0, 1); glVertex3f(m_Data[1].X, m_Data[0].Y, m_Data[0].Z);
glDrawArrays(GL_TRIANGLES, 0, 6*6);
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[1].Y, m_Data[0].Z);
glTexCoord2f(1, 0); glVertex3f(m_Data[1].X, m_Data[1].Y, m_Data[0].Z);
glTexCoord2f(1, 1); glVertex3f(m_Data[1].X, m_Data[1].Y, m_Data[1].Z);
glTexCoord2f(0, 1); glVertex3f(m_Data[0].X, m_Data[1].Y, m_Data[1].Z);
glEnd();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

View file

@ -301,36 +301,4 @@ void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const
}
ENSURE(prev == &result);
}
///////////////////////////////////////////////////////////////////////////////
// Dump the faces to OpenGL
void CBrush::Render() const
{
size_t firstInFace = no_vertex;
for(size_t i = 0; i < m_Faces.size(); ++i)
{
if (firstInFace == no_vertex)
{
glBegin(GL_POLYGON);
firstInFace = m_Faces[i];
continue;
}
const CVector3D& vertex = m_Vertices[m_Faces[i]];
glVertex3fv(&vertex.X);
if (firstInFace == m_Faces[i])
{
glEnd();
firstInFace = no_vertex;
}
}
ENSURE(firstInFace == no_vertex);
}
}

View file

@ -75,14 +75,6 @@ public:
*/
void Intersect(const CFrustum& frustum, CBrush& result) const;
/**
* Render: Renders the brush as OpenGL polygons.
*
* @note the winding of the brush faces is undefined (i.e. it is undefined which
* sides of the faces are the front faces)
*/
void Render() const;
private:
static const size_t no_vertex = ~0u;

View file

@ -138,6 +138,8 @@ void ParticleRenderer::RenderParticles(bool solidColor)
emitter->RenderArray();
}
CVertexBuffer::Unbind();
pglBlendEquationEXT(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);