# Add new renderer mode based on GL_ARB_fragment_program.

Change lighting model for new maps to allow better overbrightness.
Cache player colours instead of loading from scripts every time the
renderer wants them.

This was SVN commit r9123.
This commit is contained in:
Ykkrosh 2011-03-26 20:17:21 +00:00
parent 8b11b73226
commit d295dacb9b
70 changed files with 2546 additions and 141 deletions

Binary file not shown.

View File

@ -0,0 +1,64 @@
!!ARBfp1.0
#ifdef USE_FP_SHADOW
OPTION ARB_fragment_program_shadow;
#endif
#ifdef LIGHTING_MODEL_old
#define CLAMP_LIGHTING
#endif
#ifdef CLAMP_LIGHTING // for compat with old scenarios that expect clamped lighting
#define MAD_MAYBE_SAT MAD_SAT
#else
#define MAD_MAYBE_SAT MAD
#endif
#ifdef USE_OBJECTCOLOR
PARAM objectColor = program.local[0];
#endif
PARAM ambient = program.local[1];
TEMP tex;
TEMP temp;
TEMP diffuse;
TEMP color;
TEX tex, fragment.texcoord[0], texture[0], 2D;
#ifdef USE_TRANSPARENT
MOV result.color.a, tex;
#endif
// Apply player-coloring based on texture alpha
#ifdef USE_OBJECTCOLOR
LRP temp.rgb, objectColor, 1.0, tex.a;
MUL color.rgb, tex, temp;
#else
MOV color.rgb, tex;
#endif
// Compute color = texture * (ambient + diffuse*shadow)
// (diffuse is 2*fragment.color due to clamp-avoidance in the vertex program)
#ifdef USE_SHADOW
#ifdef USE_FP_SHADOW
TEX temp, fragment.texcoord[1], texture[1], SHADOW2D;
#else
TEX tex, fragment.texcoord[1], texture[1], 2D;
MOV_SAT temp.z, fragment.texcoord[1].z;
SGE temp, tex.x, temp.z;
#endif
MUL diffuse.rgb, fragment.color, 2.0;
MAD_MAYBE_SAT temp.rgb, diffuse, temp, ambient;
MUL color.rgb, color, temp;
#else
MAD_MAYBE_SAT temp.rgb, fragment.color, 2.0, ambient;
MUL color.rgb, color, temp;
#endif
// Multiply everything by the LOS texture
TEX tex.a, fragment.texcoord[2], texture[2], 2D;
MUL color.rgb, color, tex.a;
MOV result.color.rgb, color;
END

View File

@ -0,0 +1,54 @@
!!ARBvp1.0
PARAM sunDir = program.local[0];
PARAM sunColor = program.local[1];
PARAM losTransform = program.local[2];
PARAM shadowTransform[4] = { program.local[3..6] };
TEMP lighting;
//// Compute position and normal:
#ifdef USE_INSTANCING
PARAM instancingTransform[4] = { program.local[7..10] };
TEMP position;
TEMP normal;
DP4 position.x, instancingTransform[0], vertex.position;
DP4 position.y, instancingTransform[1], vertex.position;
DP4 position.z, instancingTransform[2], vertex.position;
MOV position.w, 1.0;
DP3 normal.x, instancingTransform[0], vertex.normal;
DP3 normal.y, instancingTransform[1], vertex.normal;
DP3 normal.z, instancingTransform[2], vertex.normal;
#else
ATTRIB position = vertex.position;
ATTRIB normal = vertex.normal;
#endif
DP4 result.position.x, state.matrix.mvp.row[0], position;
DP4 result.position.y, state.matrix.mvp.row[1], position;
DP4 result.position.z, state.matrix.mvp.row[2], position;
DP4 result.position.w, state.matrix.mvp.row[3], position;
//// Compute lighting:
// Diffuse factor
DP3 lighting, normal, -sunDir;
MAX lighting, 0.0, lighting;
// Scale diffuse to allow overbrightness (since result.color will be clamped to [0, 1])
MUL lighting, lighting, 0.5;
// Apply light colour
MUL result.color, lighting, sunColor;
//// Texture coordinates:
MOV result.texcoord[0], vertex.texcoord[0];
#ifdef USE_SHADOW
DP4 result.texcoord[1].x, shadowTransform[0], position;
DP4 result.texcoord[1].y, shadowTransform[1], position;
DP4 result.texcoord[1].z, shadowTransform[2], position;
DP4 result.texcoord[1].w, shadowTransform[3], position;
#endif
MAD result.texcoord[2], position.xzzz, losTransform.x, losTransform.y;
END

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<vertex file="model_common.vp">
<uniform name="sunDir" loc="0" type="vec3"/>
<uniform name="sunColor" loc="1" type="vec3"/>
<uniform name="losTransform" loc="2" type="vec2"/>
<uniform name="shadowTransform" loc="3" type="mat4"/>
<stream name="pos"/>
<stream name="normal"/>
<stream name="uv0"/>
</vertex>
<fragment file="model_common.fp">
<uniform name="baseTex" loc="0" type="sampler2D"/>
<uniform name="shadowTex" loc="1" type="sampler2DShadow"/>
<uniform name="losTex" loc="2" type="sampler2D"/>
<uniform name="objectColor" loc="0" type="vec3"/>
<uniform name="ambient" loc="1" type="vec3"/>
</fragment>
</program>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<define name="USE_INSTANCING" value="1"/>
<vertex file="model_common.vp">
<uniform name="sunDir" loc="0" type="vec3"/>
<uniform name="sunColor" loc="1" type="vec3"/>
<uniform name="losTransform" loc="2" type="vec2"/>
<uniform name="shadowTransform" loc="3" type="mat4"/>
<uniform name="instancingTransform" loc="7" type="mat4"/>
<stream name="pos"/>
<stream name="normal"/>
<stream name="uv0"/>
</vertex>
<fragment file="model_common.fp">
<uniform name="baseTex" loc="0" type="sampler2D"/>
<uniform name="shadowTex" loc="1" type="sampler2DShadow"/>
<uniform name="losTex" loc="2" type="sampler2D"/>
<uniform name="objectColor" loc="0" type="vec3"/>
<uniform name="ambient" loc="1" type="vec3"/>
</fragment>
</program>

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<element name="program">
<attribute name="type">
<choice>
<value>arb</value>
<value>glsl</value>
</choice>
</attribute>
<zeroOrMore>
<element name="define">
<attribute name="name"><text/></attribute>
<attribute name="value"><text/></attribute>
</element>
</zeroOrMore>
<element name="vertex">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<choice>
<ref name="uniformContent"/>
<element name="attrib">
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
</element>
<element name="stream">
<attribute name="name">
<choice>
<value>pos</value>
<value>normal</value>
<value>color</value>
<value>uv0</value>
<value>uv1</value>
<value>uv2</value>
<value>uv3</value>
</choice>
</attribute>
</element>
</choice>
</zeroOrMore>
</element>
<element name="fragment">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<ref name="uniformContent"/>
</zeroOrMore>
</element>
</element>
</start>
<define name="uniformContent">
<element name="uniform">
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
<attribute name="type">
<choice>
<value>float</value>
<value>vec2</value>
<value>vec3</value>
<value>vec4</value>
<value>mat2</value>
<value>mat3</value>
<value>mat4</value>
<value>sampler2D</value>
<value>sampler2DShadow</value>
<value>samplerCube</value>
</choice>
</attribute>
</element>
</define>
</grammar>

View File

@ -0,0 +1,3 @@
!!ARBfp1.0
MOV result.color, fragment.color;
END

View File

@ -0,0 +1,20 @@
!!ARBvp1.0
#ifdef USE_INSTANCING
PARAM instancingTransform[4] = { program.local[0..3] };
TEMP position;
DP4 position.x, instancingTransform[0], vertex.position;
DP4 position.y, instancingTransform[1], vertex.position;
DP4 position.z, instancingTransform[2], vertex.position;
MOV position.w, 1.0;
#else
ATTRIB position = vertex.position;
#endif
DP4 result.position.x, state.matrix.mvp.row[0], position;
DP4 result.position.y, state.matrix.mvp.row[1], position;
DP4 result.position.z, state.matrix.mvp.row[2], position;
DP4 result.position.w, state.matrix.mvp.row[3], position;
MOV result.color, vertex.color;
END

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<vertex file="solid.vp">
<stream name="pos"/>
</vertex>
<fragment file="solid.fp"/>
</program>

View File

@ -0,0 +1,3 @@
!!ARBfp1.0
MOV result.color, program.local[0];
END

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<define name="USE_INSTANCING" value="1"/>
<vertex file="solid.vp">
<uniform name="instancingTransform" loc="0" type="mat4"/>
<stream name="pos"/>
</vertex>
<fragment file="solid.fp"/>
</program>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<vertex file="solid.vp">
<stream name="pos"/>
</vertex>
<fragment file="solid_color.fp">
<uniform name="playerColor" loc="0" type="vec3"/>
</fragment>
</program>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<define name="USE_INSTANCING" value="1"/>
<vertex file="solid.vp">
<uniform name="instancingTransform" loc="0" type="mat4"/>
<stream name="pos"/>
</vertex>
<fragment file="solid_color.fp">
<uniform name="playerColor" loc="0" type="vec3"/>
</fragment>
</program>

View File

@ -0,0 +1,3 @@
!!ARBfp1.0
TEX result.color, fragment.texcoord[0], texture[0], 2D;
END

View File

@ -0,0 +1,18 @@
!!ARBvp1.0
#ifdef USE_INSTANCING
PARAM instancingTransform[4] = { program.local[0..3] };
TEMP position;
DP4 position.x, instancingTransform[0], vertex.position;
DP4 position.y, instancingTransform[1], vertex.position;
DP4 position.z, instancingTransform[2], vertex.position;
MOV position.w, 1.0;
#else
ATTRIB position = vertex.position;
#endif
DP4 result.position.x, state.matrix.mvp.row[0], position;
DP4 result.position.y, state.matrix.mvp.row[1], position;
DP4 result.position.z, state.matrix.mvp.row[2], position;
DP4 result.position.w, state.matrix.mvp.row[3], position;
MOV result.texcoord[0], vertex.texcoord[0];
END

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<vertex file="solid_tex.vp">
<stream name="pos"/>
<stream name="uv0"/>
</vertex>
<fragment file="solid_tex.fp">
<uniform name="baseTex" loc="0" type="sampler2D"/>
</fragment>
</program>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<vertex file="terrain_common.vp">
<uniform name="sunColor" loc="0" type="vec3"/>
<uniform name="losTransform" loc="1" type="vec2"/>
<uniform name="shadowTransform" loc="2" type="mat4"/>
</vertex>
<fragment file="terrain_common.fp">
<uniform name="baseTex" loc="0" type="sampler2D"/>
<uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
<uniform name="losTex" loc="3" type="sampler2D"/>
<uniform name="ambient" loc="0" type="vec3"/>
</fragment>
</program>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<define name="BLEND" value="1"/>
<vertex file="terrain_common.vp">
<uniform name="sunColor" loc="0" type="vec3"/>
<uniform name="losTransform" loc="1" type="vec2"/>
<uniform name="shadowTransform" loc="2" type="mat4"/>
</vertex>
<fragment file="terrain_common.fp">
<uniform name="baseTex" loc="0" type="sampler2D"/>
<uniform name="blendTex" loc="1" type="sampler2D"/>
<uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
<uniform name="losTex" loc="3" type="sampler2D"/>
<uniform name="ambient" loc="0" type="vec3"/>
</fragment>
</program>

View File

@ -0,0 +1,62 @@
!!ARBfp1.0
#ifdef USE_FP_SHADOW
OPTION ARB_fragment_program_shadow;
#endif
#ifdef LIGHTING_MODEL_old
#define CLAMP_LIGHTING
#endif
#ifdef CLAMP_LIGHTING // for compat with old scenarios that expect clamped lighting
#define MAD_MAYBE_SAT MAD_SAT
#else
#define MAD_MAYBE_SAT MAD
#endif
PARAM ambient = program.local[0];
TEMP tex;
TEMP temp;
TEMP diffuse;
TEMP color;
#ifdef BLEND
// Use alpha from blend texture
// TODO: maybe we should invert the texture instead of doing SUB here?
TEX tex.a, fragment.texcoord[1], texture[1], 2D;
SUB result.color.a, 1.0, tex.a;
#endif
// Load diffuse colour
TEX color, fragment.texcoord[0], texture[0], 2D;
#ifdef DECAL
// Use alpha from main texture
MOV result.color.a, color;
#endif
// Compute color = texture * (ambient + diffuse*shadow)
// (diffuse is 2*fragment.color due to clamp-avoidance in the vertex program)
#ifdef USE_SHADOW
#ifdef USE_FP_SHADOW
TEX temp, fragment.texcoord[2], texture[2], SHADOW2D;
#else
TEX tex, fragment.texcoord[2], texture[2], 2D;
MOV_SAT temp.z, fragment.texcoord[2].z;
SGE temp, tex.x, temp.z;
#endif
MUL diffuse.rgb, fragment.color, 2.0;
MAD_MAYBE_SAT temp.rgb, diffuse, temp, ambient;
MUL color.rgb, color, temp;
#else
MAD_MAYBE_SAT temp.rgb, fragment.color, 2.0, ambient;
MUL color.rgb, color, temp;
#endif
// Multiply everything by the LOS texture
TEX tex.a, fragment.texcoord[3], texture[3], 2D;
MUL color.rgb, color, tex.a;
MOV result.color.rgb, color;
END

View File

@ -0,0 +1,42 @@
!!ARBvp1.0
PARAM sunColor = program.local[0];
PARAM losTransform = program.local[1];
PARAM shadowTransform[4] = { program.local[2..5] };
TEMP lighting;
//// Compute position and normal:
ATTRIB position = vertex.position;
DP4 result.position.x, state.matrix.mvp.row[0], position;
DP4 result.position.y, state.matrix.mvp.row[1], position;
DP4 result.position.z, state.matrix.mvp.row[2], position;
DP4 result.position.w, state.matrix.mvp.row[3], position;
//// Compute lighting:
// Diffuse factor is precomputed in vertex attribute
// Scale diffuse to allow overbrightness (since result.color will be clamped to [0, 1])
MUL lighting, vertex.color, 0.5;
// Apply light colour
MUL result.color, lighting, sunColor;
//// Texture coordinates:
MOV result.texcoord[0], vertex.texcoord[0];
#ifdef BLEND
MOV result.texcoord[1], vertex.texcoord[1];
#endif
#ifdef USE_SHADOW
DP4 result.texcoord[2].x, shadowTransform[0], position;
DP4 result.texcoord[2].y, shadowTransform[1], position;
DP4 result.texcoord[2].z, shadowTransform[2], position;
DP4 result.texcoord[2].w, shadowTransform[3], position;
#endif
MAD result.texcoord[3], position.xzzz, losTransform.x, losTransform.y;
END

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="arb">
<define name="DECAL" value="1"/>
<vertex file="terrain_common.vp">
<uniform name="sunColor" loc="0" type="vec3"/>
<uniform name="losTransform" loc="1" type="vec2"/>
<uniform name="shadowTransform" loc="2" type="mat4"/>
</vertex>
<fragment file="terrain_common.fp">
<uniform name="baseTex" loc="0" type="sampler2D"/>
<uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
<uniform name="losTex" loc="3" type="sampler2D"/>
<uniform name="ambient" loc="0" type="vec3"/>
</fragment>
</program>

View File

@ -442,7 +442,7 @@ void CGameView::RegisterInit()
}
void CGameView::Render()
void CGameView::BeginFrame()
{
if (m->LockCullCamera == false)
{
@ -462,6 +462,11 @@ void CGameView::Render()
CheckLightEnv();
m->Game->CachePlayerColours();
}
void CGameView::Render()
{
g_Renderer.RenderScene(this);
}
@ -548,6 +553,9 @@ void CGameView::CheckLightEnv()
if (m->CachedLightEnv == g_LightEnv)
return;
if (m->CachedLightEnv.GetLightingModel() != g_LightEnv.GetLightingModel())
g_Renderer.MakeShadersDirty();
m->CachedLightEnv = g_LightEnv;
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();

View File

@ -78,7 +78,7 @@ public:
// *presentation*
void Update(float DeltaTime);
// Render: Render the World
void BeginFrame();
void Render();
InReaction HandleEvent(const SDL_Event_* ev);

View File

@ -77,6 +77,17 @@ void CLOSTexture::BindTexture(int unit)
g_Renderer.BindTexture(unit, m_Texture);
}
GLuint CLOSTexture::GetTexture()
{
if (m_Dirty)
{
RecomputeTexture(0);
m_Dirty = false;
}
return m_Texture;
}
const float* CLOSTexture::GetTextureMatrix()
{
debug_assert(!m_Dirty);

View File

@ -47,6 +47,13 @@ public:
*/
void BindTexture(int unit);
/**
* Recomputes the LOS texture if necessary, and returns the texture handle.
* Also potentially switches the current active texture unit, and enables texturing on it.
* The texture is in 8-bit ALPHA format.
*/
GLuint GetTexture();
/**
* Returns a matrix to map (x,y,z) world coordinates onto (u,v) LOS texture
* coordinates, in the form expected by glLoadMatrixf.

View File

@ -30,6 +30,7 @@ CLightEnv::CLightEnv()
: m_Elevation(DEGTORAD(45)),
m_Rotation(DEGTORAD(315)),
m_TerrainShadowTransparency(0.0),
m_LightingModel("standard"),
m_SunColor(1.5, 1.5, 1.5),
m_TerrainAmbientColor(0x50/255.f, 0x60/255.f, 0x85/255.f),
m_UnitsAmbientColor(0x80/255.f, 0x80/255.f, 0x80/255.f)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -22,7 +22,8 @@
#ifndef INCLUDED_LIGHTENV
#define INCLUDED_LIGHTENV
#include "Color.h"
#include "graphics/Color.h"
#include "maths/MathUtil.h"
#include "maths/Vector3D.h"
class CMapWriter;
@ -56,11 +57,18 @@ private:
* m_TerrainShadowTransparency: Fraction of diffuse light that reaches shadowed terrain.
* A value of 0.0 means shadowed polygons get only ambient light, while a value of 1.0
* means shadows don't have any effect at all.
* TODO: probably delete this, since it's never used and always set to 0.0.
*/
float m_TerrainShadowTransparency;
CVector3D m_SunDir;
/**
* A string that shaders use to determine what lighting model to implement.
* Current recognised values are "old" and "standard".
*/
std::string m_LightingModel;
public:
RGBColor m_SunColor;
RGBColor m_TerrainAmbientColor;
@ -73,12 +81,15 @@ public:
float GetRotation() const { return m_Rotation; }
const CVector3D& GetSunDir() const { return m_SunDir; }
float GetTerrainShadowTransparency() const { return m_TerrainShadowTransparency; }
const std::string& GetLightingModel() const { return m_LightingModel; }
void SetElevation(float f);
void SetRotation(float f);
void SetTerrainShadowTransparency(float f);
void SetLightingModel(const std::string& model) { m_LightingModel = model; }
/**
* EvaluateTerrain: Calculate brightness of a point of the terrain with the given normal
* vector.
@ -130,12 +141,37 @@ public:
color = CVector3D(0,0,0);
}
/**
* Compute the diffuse sun lighting.
* If @p includeSunColor is set, the return value includes the sun color.
* (If sun overbrightness is enabled, this might result in clamping).
* Otherwise it returns a factor that the sun color should be multiplied by.
*/
SColor4ub EvaluateDiffuse(const CVector3D& normal, bool includeSunColor) const
{
float dot = -normal.Dot(m_SunDir);
if (dot <= 0)
return SColor4ub(0, 0, 0, 255);
if (includeSunColor)
{
return ConvertRGBColorTo4ub(m_SunColor * dot);
}
else
{
int c = clamp((int)(dot * 255), 0, 255);
return SColor4ub(c, c, c, 255);
}
}
// Comparison operators
bool operator==(const CLightEnv& o) const
{
return m_Elevation == o.m_Elevation &&
m_Rotation == o.m_Rotation &&
m_TerrainShadowTransparency == o.m_TerrainShadowTransparency &&
m_LightingModel == o.m_LightingModel &&
m_SunColor == o.m_SunColor &&
m_TerrainAmbientColor == o.m_TerrainAmbientColor &&
m_UnitsAmbientColor == o.m_UnitsAmbientColor;

View File

@ -51,6 +51,9 @@ CMapReader::CMapReader()
: xml_reader(0), m_PatchesPerSide(0)
{
cur_terrain_tex = 0; // important - resets generator state
// Maps that don't override the default probably want the old lighting model
m_LightEnv.SetLightingModel("old");
}
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
@ -538,6 +541,7 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
{
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(lightingmodel);
EL(skyset);
EL(suncolour);
EL(sunelevation);
@ -566,7 +570,11 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
XMBAttributeList attrs = element.GetAttributes();
if (element_name == el_skyset)
if (element_name == el_lightingmodel)
{
m_MapReader.m_LightEnv.SetLightingModel(element.GetText());
}
else if (element_name == el_skyset)
{
if (m_MapReader.pSkyMan)
m_MapReader.pSkyMan->SetSkySet(element.GetText().FromUTF8());
@ -1213,6 +1221,8 @@ int CMapReader::ParseEnvironment()
return 0;
}
m_LightEnv.SetLightingModel("standard");
std::wstring skySet;
GET_ENVIRONMENT_PROPERTY(envObj.get(), SkySet, skySet)
pSkyMan->SetSkySet(skySet);

View File

@ -189,6 +189,7 @@ void CMapWriter::WriteXML(const VfsPath& filename,
{
XML_Element("Environment");
XML_Setting("LightingModel", pLightEnv->GetLightingModel());
XML_Setting("SkySet", pSkyMan->GetSkySet());
{
XML_Element("SunColour");

View File

@ -87,9 +87,6 @@ SMaterialColor CMaterial::GetObjectColor()
if (m_UseTextureColor)
return m_TextureColor;
debug_assert(m_UsePlayerColor);
// this should never be called unless IsPlayer returned true
return GetPlayerColor();
}

View File

@ -19,29 +19,12 @@
#define INCLUDED_MATERIAL
#include "ps/CStr.h"
#include "ps/Overlay.h"
#include "simulation2/helpers/Player.h"
// FIXME: This material system is almost entirely unused and probably broken
struct CColor;
struct SMaterialColor
{
public:
float r;
float g;
float b;
float a;
SMaterialColor() : r(0.0f), g(0.0f), b(0.0f), a(1.0f) {}
SMaterialColor(float _r, float _g, float _b, float _a)
{
r = _r;
g = _g;
b = _b;
a = _a;
}
};
typedef CColor SMaterialColor;
class CMaterial
{

View File

@ -0,0 +1,210 @@
/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ShaderManager.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/XML/Xeromyces.h"
#include "ps/XML/XMLWriter.h"
#include "renderer/Renderer.h"
TIMER_ADD_CLIENT(tc_ShaderValidation);
CShaderManager::CShaderManager()
{
#if USE_SHADER_XML_VALIDATION
{
TIMER_ACCRUE(tc_ShaderValidation);
CVFSFile grammar;
if (grammar.Load(g_VFS, L"shaders/program.rng") != PSRETURN_OK)
LOGERROR(L"Failed to read grammar shaders/program.rng");
else
{
if (!m_Validator.LoadGrammar(grammar.GetAsString()))
LOGERROR(L"Failed to load grammar shaders/program.rng");
}
}
#endif
// Allow hotloading of textures
RegisterFileReloadFunc(ReloadChangedFileCB, this);
}
CShaderManager::~CShaderManager()
{
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
}
CShaderProgramPtr CShaderManager::LoadProgram(const char* name, const std::map<CStr, CStr>& defines)
{
CacheKey key = { name, defines };
std::map<CacheKey, CShaderProgramPtr>::iterator it = m_Cache.find(key);
if (it != m_Cache.end())
return it->second;
CShaderProgramPtr program;
if (NewProgram(name, defines, program) != PSRETURN_OK)
{
LOGERROR(L"Failed to load shader '%hs'", name);
program = CShaderProgramPtr();
}
m_Cache[key] = program;
return program;
}
bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& baseDefines, CShaderProgramPtr& program)
{
VfsPath xmlFilename = L"shaders/" + wstring_from_utf8(name) + L".xml";
CXeromyces XeroFile;
PSRETURN ret = XeroFile.Load(g_VFS, xmlFilename);
if (ret != PSRETURN_OK)
return false;
#if USE_SHADER_XML_VALIDATION
{
TIMER_ACCRUE(tc_ShaderValidation);
// Serialize the XMB data and pass it to the validator
XML_Start();
XML_SetPrettyPrint(false);
XML_WriteXMB(XeroFile);
bool ok = m_Validator.ValidateEncoded(wstring_from_utf8(name), XML_GetOutput());
if (!ok)
return false;
}
#endif
// Define all the elements and attributes used in the XML file
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(vertex);
EL(fragment);
EL(define);
EL(uniform);
EL(attrib);
EL(stream);
AT(type);
AT(file);
AT(name);
AT(value);
AT(loc);
#undef AT
#undef EL
XMBElement Root = XeroFile.GetRoot();
bool isGLSL = (Root.GetAttributes().GetNamedItem(at_type) == "glsl");
VfsPath vertexFile;
VfsPath fragmentFile;
std::map<CStr, CStr> defines = baseDefines;
std::map<CStr, int> vertexUniforms;
std::map<CStr, int> fragmentUniforms;
int streamFlags = 0;
XERO_ITER_EL(Root, Child)
{
if (Child.GetNodeName() == el_define)
{
defines[Child.GetAttributes().GetNamedItem(at_name)] = Child.GetAttributes().GetNamedItem(at_value);
}
else if (Child.GetNodeName() == el_vertex)
{
vertexFile = L"shaders/" + Child.GetAttributes().GetNamedItem(at_file).FromUTF8();
XERO_ITER_EL(Child, Param)
{
if (Param.GetNodeName() == el_uniform)
{
vertexUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
}
else if (Param.GetNodeName() == el_stream)
{
CStr StreamName = Param.GetAttributes().GetNamedItem(at_name);
if (StreamName == "pos")
streamFlags |= STREAM_POS;
else if (StreamName == "normal")
streamFlags |= STREAM_NORMAL;
else if (StreamName == "color")
streamFlags |= STREAM_COLOR;
else if (StreamName == "uv0")
streamFlags |= STREAM_UV0;
else if (StreamName == "uv1")
streamFlags |= STREAM_UV1;
else if (StreamName == "uv2")
streamFlags |= STREAM_UV2;
else if (StreamName == "uv3")
streamFlags |= STREAM_UV3;
}
else if (Param.GetNodeName() == el_attrib)
{
// TODO: add support for vertex attributes
}
}
}
else if (Child.GetNodeName() == el_fragment)
{
fragmentFile = L"shaders/" + Child.GetAttributes().GetNamedItem(at_file).FromUTF8();
XERO_ITER_EL(Child, Param)
{
if (Param.GetNodeName() == el_uniform)
fragmentUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
}
}
}
// TODO: add GLSL support
debug_assert(!isGLSL);
program = CShaderProgramPtr(CShaderProgram::ConstructARB(vertexFile, fragmentFile, defines, vertexUniforms, fragmentUniforms, streamFlags));
program->Reload();
// m_HotloadFiles[xmlFilename].insert(program); // TODO: should reload somehow when the XML changes
m_HotloadFiles[vertexFile].insert(program);
m_HotloadFiles[fragmentFile].insert(program);
return PSRETURN_OK;
}
/*static*/ LibError CShaderManager::ReloadChangedFileCB(void* param, const VfsPath& path)
{
return static_cast<CShaderManager*>(param)->ReloadChangedFile(path);
}
LibError CShaderManager::ReloadChangedFile(const VfsPath& path)
{
// Find all shaders using this file
HotloadFilesMap::iterator files = m_HotloadFiles.find(path);
if (files != m_HotloadFiles.end())
{
// Reload all shaders using this file
for (std::set<boost::weak_ptr<CShaderProgram> >::iterator it = files->second.begin(); it != files->second.end(); ++it)
{
if (shared_ptr<CShaderProgram> program = it->lock())
program->Reload();
}
}
return INFO::OK;
}

View File

@ -0,0 +1,79 @@
/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SHADERMANAGER
#define INCLUDED_SHADERMANAGER
#define USE_SHADER_XML_VALIDATION 1
#include <boost/unordered_map.hpp>
#include <boost/weak_ptr.hpp>
#include "graphics/ShaderProgram.h"
#if USE_SHADER_XML_VALIDATION
# include "ps/XML/RelaxNG.h"
#endif
/**
* Shader manager: loads and caches shader programs.
*/
class CShaderManager
{
public:
CShaderManager();
~CShaderManager();
/**
* Load a shader program.
* @param name name of shader XML specification (file is loaded from shaders/${name}.xml)
* @param defines key/value set of preprocessor definitions
* @return loaded program, or null pointer on error
*/
CShaderProgramPtr LoadProgram(const char* name, const std::map<CStr, CStr>& defines);
private:
bool NewProgram(const char* name, const std::map<CStr, CStr>& defines, CShaderProgramPtr& program);
static LibError ReloadChangedFileCB(void* param, const VfsPath& path);
LibError ReloadChangedFile(const VfsPath& path);
struct CacheKey
{
std::string name;
std::map<CStr, CStr> defines;
bool operator<(const CacheKey& k) const
{
if (name < k.name) return true;
if (k.name < name) return false;
return defines < k.defines;
}
};
std::map<CacheKey, CShaderProgramPtr> m_Cache;
// Store the set of shaders that need to be reloaded when the given file is modified
typedef boost::unordered_map<VfsPath, std::set<boost::weak_ptr<CShaderProgram> > > HotloadFilesMap;
HotloadFilesMap m_HotloadFiles;
#if USE_SHADER_XML_VALIDATION
RelaxNGValidator m_Validator;
#endif
};
#endif // INCLUDED_SHADERMANAGER

View File

@ -0,0 +1,313 @@
/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ShaderProgram.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/Matrix3D.h"
#include "maths/Vector3D.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Overlay.h"
#include "ps/Preprocessor.h"
class CShaderProgramARB : public CShaderProgram
{
public:
CShaderProgramARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const std::map<CStr, int>& vertexIndexes, const std::map<CStr, int>& fragmentIndexes,
int streamflags) :
CShaderProgram(streamflags),
m_VertexFile(vertexFile), m_FragmentFile(fragmentFile),
m_Defines(defines),
m_VertexIndexes(vertexIndexes), m_FragmentIndexes(fragmentIndexes)
{
pglGenProgramsARB(1, &m_VertexProgram);
pglGenProgramsARB(1, &m_FragmentProgram);
}
~CShaderProgramARB()
{
Unload();
pglDeleteProgramsARB(1, &m_VertexProgram);
pglDeleteProgramsARB(1, &m_FragmentProgram);
}
CStr Preprocess(CPreprocessor& preprocessor, const CStr& input)
{
size_t len = 0;
char* output = preprocessor.Parse(input.c_str(), input.size(), len);
if (!output)
{
LOGERROR(L"Shader preprocessing failed");
return "";
}
CStr ret(output, len);
// Free output if it's not inside the source string
if (!(output >= input.c_str() && output < input.c_str() + input.size()))
free(output);
return ret;
}
bool Compile(GLuint target, const char* targetName, GLuint program, const VfsPath& file, const CStr& code)
{
ogl_WarnIfError();
pglBindProgramARB(target, program);
ogl_WarnIfError();
pglProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)code.length(), code.c_str());
if (ogl_SquelchError(GL_INVALID_OPERATION))
{
GLint errPos = 0;
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errPos);
int errLine = std::count(code.begin(), code.begin() + errPos + 1, '\n') + 1;
char* errStr = (char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
LOGERROR(L"Failed to compile %hs program '%ls' (line %d):\n%hs", targetName, file.string().c_str(), errLine, errStr);
return false;
}
pglBindProgramARB(target, 0);
ogl_WarnIfError();
return true;
}
virtual void Reload()
{
Unload();
CVFSFile vertexFile;
if (vertexFile.Load(g_VFS, m_VertexFile) != PSRETURN_OK)
return;
CVFSFile fragmentFile;
if (fragmentFile.Load(g_VFS, m_FragmentFile) != PSRETURN_OK)
return;
CPreprocessor preprocessor;
for (std::map<CStr, CStr>::iterator it = m_Defines.begin(); it != m_Defines.end(); ++it)
preprocessor.Define(it->first.c_str(), it->second.c_str());
CStr vertexCode = Preprocess(preprocessor, vertexFile.GetAsString());
CStr fragmentCode = Preprocess(preprocessor, fragmentFile.GetAsString());
// printf(">>>\n%s<<<\n", vertexCode.c_str());
// printf(">>>\n%s<<<\n", fragmentCode.c_str());
if (!Compile(GL_VERTEX_PROGRAM_ARB, "vertex", m_VertexProgram, m_VertexFile, vertexCode))
return;
if (!Compile(GL_FRAGMENT_PROGRAM_ARB, "fragment", m_FragmentProgram, m_FragmentFile, fragmentCode))
return;
m_IsValid = true;
}
void Unload()
{
m_IsValid = false;
}
virtual void Bind()
{
glEnable(GL_VERTEX_PROGRAM_ARB);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
pglBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_VertexProgram);
pglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_FragmentProgram);
}
virtual void Unbind()
{
glDisable(GL_VERTEX_PROGRAM_ARB);
glDisable(GL_FRAGMENT_PROGRAM_ARB);
pglBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);
pglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
// TODO: should unbind textures, probably
}
int GetUniformVertexIndex(uniform_id_t id)
{
std::map<CStr, int>::iterator it = m_VertexIndexes.find(id);
if (it == m_VertexIndexes.end())
return -1;
return it->second;
}
int GetUniformFragmentIndex(uniform_id_t id)
{
std::map<CStr, int>::iterator it = m_FragmentIndexes.find(id);
if (it == m_FragmentIndexes.end())
return -1;
return it->second;
}
virtual bool HasTexture(texture_id_t id)
{
if (GetUniformFragmentIndex(id) != -1)
return true;
return false;
}
virtual void BindTexture(texture_id_t id, Handle tex)
{
int index = GetUniformFragmentIndex(id);
if (index != -1)
ogl_tex_bind(tex, index);
}
virtual void BindTexture(texture_id_t id, GLuint tex)
{
int index = GetUniformFragmentIndex(id);
if (index != -1)
{
pglActiveTextureARB((int)(GL_TEXTURE0+index));
glBindTexture(GL_TEXTURE_2D, tex);
}
}
virtual Binding GetUniformBinding(uniform_id_t id)
{
return Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id));
}
virtual void Uniform(uniform_id_t id, int v)
{
Uniform(Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id)), (float)v, (float)v, (float)v, (float)v);
}
virtual void Uniform(Binding id, int v)
{
Uniform(id, (float)v, (float)v, (float)v, (float)v);
}
virtual void Uniform(uniform_id_t id, float v)
{
Uniform(Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id)), v, v, v, v);
}
virtual void Uniform(Binding id, float v)
{
Uniform(id, v, v, v, v);
}
virtual void Uniform(uniform_id_t id, float v0, float v1, float v2, float v3)
{
Uniform(Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id)), v0, v1, v2, v3);
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.vertex != -1)
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex, v0, v1, v2, v3);
if (id.fragment != -1)
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment, v0, v1, v2, v3);
}
virtual void Uniform(uniform_id_t id, const CVector3D& v)
{
Uniform(Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id)), v.X, v.Y, v.Z, 0.0f);
}
virtual void Uniform(Binding id, const CVector3D& v)
{
Uniform(id, v.X, v.Y, v.Z, 0.0f);
}
virtual void Uniform(uniform_id_t id, const CColor& v)
{
Uniform(Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id)), v.r, v.g, v.b, v.a);
}
virtual void Uniform(Binding id, const CColor& v)
{
Uniform(id, v.r, v.g, v.b, v.a);
}
virtual void Uniform(uniform_id_t id, const CMatrix3D& v)
{
Uniform(Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id)), v);
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.vertex != -1)
{
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+0, v._11, v._12, v._13, v._14);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+1, v._21, v._22, v._23, v._24);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+2, v._31, v._32, v._33, v._34);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+3, v._41, v._42, v._43, v._44);
}
if (id.fragment != -1)
{
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+0, v._11, v._12, v._13, v._14);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+1, v._21, v._22, v._23, v._24);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+2, v._31, v._32, v._33, v._34);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+3, v._41, v._42, v._43, v._44);
}
}
private:
VfsPath m_VertexFile;
VfsPath m_FragmentFile;
std::map<CStr, CStr> m_Defines;
GLuint m_VertexProgram;
GLuint m_FragmentProgram;
std::map<CStr, int> m_VertexIndexes;
std::map<CStr, int> m_FragmentIndexes;
};
CShaderProgram::CShaderProgram(int streamflags)
: m_IsValid(false), m_StreamFlags(streamflags)
{
}
/*static*/ CShaderProgram* CShaderProgram::ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const std::map<CStr, int>& vertexIndexes, const std::map<CStr, int>& fragmentIndexes,
int streamflags)
{
return new CShaderProgramARB(vertexFile, fragmentFile, defines, vertexIndexes, fragmentIndexes, streamflags);
}
bool CShaderProgram::IsValid() const
{
return m_IsValid;
}
int CShaderProgram::GetStreamFlags() const
{
return m_StreamFlags;
}

View File

@ -0,0 +1,140 @@
/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SHADERPROGRAM
#define INCLUDED_SHADERPROGRAM
#include "lib/ogl.h"
#include "lib/file/vfs/vfs_path.h"
#include "lib/res/handle.h"
#include "ps/CStr.h"
class CColor;
class CMatrix3D;
class CVector3D;
/**
* A compiled vertex+fragment shader program.
* The implementation may use GL_ARB_{vertex,fragment}_program (assembly syntax)
* or GL_ARB_{vertex,fragment}_shader (GLSL); the difference is hidden from the caller.
*
* Texture/uniform IDs are typically strings, corresponding to the names defined
* in the shader .xml file. Alternatively (and more efficiently, if used extremely
* frequently), call GetUniformBinding and pass its return value as the ID.
* Setting uniforms that the shader .xml doesn't support is harmless.
*/
class CShaderProgram
{
NONCOPYABLE(CShaderProgram);
public:
/**
* Construct based on ARB vertex/fragment program files.
*/
static CShaderProgram* ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const std::map<CStr, int>& vertexIndexes, const std::map<CStr, int>& fragmentIndexes,
int streamflags);
typedef const char* attrib_id_t;
typedef const char* texture_id_t;
typedef const char* uniform_id_t;
/**
* Represents a uniform attribute binding.
*/
struct Binding
{
friend class CShaderProgramARB;
private:
Binding(int v, int f) : vertex(v), fragment(f) { }
i16 vertex;
i16 fragment;
public:
Binding() : vertex(-1), fragment(-1) { }
/**
* Returns whether this uniform attribute is active in the shader.
* If not then there's no point calling Uniform() to set its value.
*/
bool Active() { return vertex != -1 || fragment != -1; }
};
virtual ~CShaderProgram() { }
virtual void Reload() = 0;
/**
* Returns whether this shader was successfully loaded.
*/
bool IsValid() const;
/**
* Binds the shader into the GL context. Call this before calling Uniform()
* or trying to render with it.
*/
virtual void Bind() = 0;
/**
* Unbinds the shader from the GL context. Call this after rendering with it.
*/
virtual void Unbind() = 0;
/**
* Returns bitset of STREAM_* value, indicating what vertex data streams the
* vertex shader needs.
*/
int GetStreamFlags() const;
// TODO: implement vertex attributes
GLuint GetAttribIndex(attrib_id_t id);
/**
* Returns whether the shader needs the texture with the given name.
*/
virtual bool HasTexture(texture_id_t id) = 0;
virtual void BindTexture(texture_id_t id, Handle tex) = 0;
virtual void BindTexture(texture_id_t id, GLuint tex) = 0;
virtual Binding GetUniformBinding(uniform_id_t id) = 0;
virtual void Uniform(uniform_id_t id, int v) = 0;
virtual void Uniform(uniform_id_t id, float v) = 0;
virtual void Uniform(uniform_id_t id, float v0, float v1, float v2, float v3) = 0;
virtual void Uniform(uniform_id_t id, const CVector3D& v) = 0;
virtual void Uniform(uniform_id_t id, const CColor& v) = 0;
virtual void Uniform(uniform_id_t id, const CMatrix3D& v) = 0;
virtual void Uniform(Binding id, int v) = 0;
virtual void Uniform(Binding id, float v) = 0;
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3) = 0;
virtual void Uniform(Binding id, const CVector3D& v) = 0;
virtual void Uniform(Binding id, const CColor& v) = 0;
virtual void Uniform(Binding id, const CMatrix3D& v) = 0;
protected:
CShaderProgram(int streamflags);
bool m_IsValid;
int m_StreamFlags;
};
typedef shared_ptr<CShaderProgram> CShaderProgramPtr;
#endif // INCLUDED_SHADERPROGRAM

View File

@ -0,0 +1,134 @@
/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ShaderTechnique.h"
CShaderPass::CShaderPass(const CShaderProgramPtr& shader) :
m_Shader(shader),
m_HasAlpha(false), m_HasBlend(false), m_HasColorMask(false), m_HasDepthMask(false)
{
}
void CShaderPass::Bind()
{
m_Shader->Bind();
if (m_HasAlpha)
{
glEnable(GL_ALPHA_TEST);
glAlphaFunc(m_AlphaFunc, m_AlphaRef);
}
if (m_HasBlend)
{
glEnable(GL_BLEND);
glBlendFunc(m_BlendSrc, m_BlendDst);
}
if (m_HasColorMask)
glColorMask(m_ColorMaskR, m_ColorMaskG, m_ColorMaskB, m_ColorMaskA);
if (m_HasDepthMask)
glDepthMask(m_DepthMask);
}
void CShaderPass::Unbind()
{
m_Shader->Unbind();
if (m_HasAlpha)
{
glDisable(GL_ALPHA_TEST);
}
if (m_HasBlend)
{
glDisable(GL_BLEND);
}
if (m_HasColorMask)
glColorMask(1, 1, 1, 1);
if (m_HasDepthMask)
glDepthMask(1);
}
void CShaderPass::AlphaFunc(GLenum func, GLclampf ref)
{
m_HasAlpha = true;
m_AlphaFunc = func;
m_AlphaRef = ref;
}
void CShaderPass::BlendFunc(GLenum src, GLenum dst)
{
m_HasBlend = true;
m_BlendSrc = src;
m_BlendDst = dst;
}
void CShaderPass::ColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a)
{
m_HasColorMask = true;
m_ColorMaskR = r;
m_ColorMaskG = g;
m_ColorMaskB = b;
m_ColorMaskA = a;
}
void CShaderPass::DepthMask(GLboolean mask)
{
m_HasDepthMask = true;
m_DepthMask = mask;
}
CShaderTechnique::CShaderTechnique(const CShaderPass& pass)
{
m_Passes.push_back(pass);
}
void CShaderTechnique::AddPass(const CShaderPass& pass)
{
m_Passes.push_back(pass);
}
int CShaderTechnique::GetNumPasses()
{
return m_Passes.size();
}
void CShaderTechnique::BeginPass(int pass)
{
debug_assert(0 <= pass && pass < (int)m_Passes.size());
m_Passes[pass].Bind();
}
void CShaderTechnique::EndPass(int pass)
{
debug_assert(0 <= pass && pass < (int)m_Passes.size());
m_Passes[pass].Unbind();
}
CShaderProgramPtr CShaderTechnique::GetShader(int pass)
{
debug_assert(0 <= pass && pass < (int)m_Passes.size());
return m_Passes[pass].GetShader();
}

View File

@ -0,0 +1,91 @@
/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SHADERTECHNIQUE
#define INCLUDED_SHADERTECHNIQUE
#include "graphics/ShaderProgram.h"
/**
* Implements a render pass consisting of various GL state changes and a shader.
*/
class CShaderPass
{
public:
CShaderPass(const CShaderProgramPtr& shader);
// Add various bits of GL state to the pass:
void AlphaFunc(GLenum func, GLclampf ref);
void BlendFunc(GLenum src, GLenum dst);
void ColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a);
void DepthMask(GLboolean mask);
/**
* Set all the GL state that was previously specified on this pass.
*/
void Bind();
/**
* Reset the GL state to the default.
*/
void Unbind();
CShaderProgramPtr GetShader() { return m_Shader; }
private:
CShaderProgramPtr m_Shader;
bool m_HasAlpha;
GLenum m_AlphaFunc;
GLclampf m_AlphaRef;
bool m_HasBlend;
GLenum m_BlendSrc;
GLenum m_BlendDst;
bool m_HasColorMask;
GLboolean m_ColorMaskR;
GLboolean m_ColorMaskG;
GLboolean m_ColorMaskB;
GLboolean m_ColorMaskA;
bool m_HasDepthMask;
GLboolean m_DepthMask;
};
/**
* Implements a render technique consisting of a sequence of passes.
* In theory these should probably be loaded from an XML file or something,
* but currently you have to construct them manually.
*/
class CShaderTechnique
{
public:
CShaderTechnique(const CShaderPass& pass);
void AddPass(const CShaderPass& pass);
int GetNumPasses();
void BeginPass(int pass);
void EndPass(int pass);
CShaderProgramPtr GetShader(int pass);
private:
std::vector<CShaderPass> m_Passes;
};
#endif // INCLUDED_SHADERTECHNIQUE

View File

@ -512,6 +512,11 @@ CTexture::~CTexture()
}
void CTexture::Bind(size_t unit)
{
ogl_tex_bind(GetHandle(), unit);
}
Handle CTexture::GetHandle()
{
// TODO: TryLoad might call ogl_tex_upload which enables GL_TEXTURE_2D
// on texture unit 0, regardless of 'unit', which callers might
@ -519,7 +524,7 @@ void CTexture::Bind(size_t unit)
TryLoad();
ogl_tex_bind(m_Handle, unit);
return m_Handle;
}
bool CTexture::TryLoad()

View File

@ -225,6 +225,11 @@ public:
*/
void Bind(size_t unit = 0);
/**
* Returns a ogl_tex handle, for later binding. See comments from Bind().
*/
Handle GetHandle();
/**
* Attempt to load the texture data quickly, as with Bind().
* Returns whether the texture data is currently loaded.

View File

@ -114,7 +114,25 @@ FUNC(void, glGetFramebufferAttachmentParameterivEXT, (GLenum target, GLenum atta
FUNC(void, glGenerateMipmapEXT, (GLenum target))
// GL_ARB_vertex_program, GL_ARB_fragment_program
FUNC(void, glProgramStringARB, (GLenum target, GLenum format, GLsizei len, const GLvoid *string))
FUNC(void, glBindProgramARB, (GLenum target, GLuint program))
FUNC(void, glDeleteProgramsARB, (GLsizei n, const GLuint *programs))
FUNC(void, glGenProgramsARB, (GLsizei n, GLuint *programs))
FUNC(void, glProgramEnvParameter4dARB, (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w))
FUNC(void, glProgramEnvParameter4dvARB, (GLenum target, GLuint index, const GLdouble *params))
FUNC(void, glProgramEnvParameter4fARB, (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w))
FUNC(void, glProgramEnvParameter4fvARB, (GLenum target, GLuint index, const GLfloat *params))
FUNC(void, glProgramLocalParameter4dARB, (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w))
FUNC(void, glProgramLocalParameter4dvARB, (GLenum target, GLuint index, const GLdouble *params))
FUNC(void, glProgramLocalParameter4fARB, (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w))
FUNC(void, glProgramLocalParameter4fvARB, (GLenum target, GLuint index, const GLfloat *params))
FUNC(void, glGetProgramEnvParameterdvARB, (GLenum target, GLuint index, GLdouble *params))
FUNC(void, glGetProgramEnvParameterfvARB, (GLenum target, GLuint index, GLfloat *params))
FUNC(void, glGetProgramLocalParameterdvARB, (GLenum target, GLuint index, GLdouble *params))
FUNC(void, glGetProgramLocalParameterfvARB, (GLenum target, GLuint index, GLfloat *params))
FUNC(void, glGetProgramivARB, (GLenum target, GLenum pname, GLint *params))
FUNC(void, glGetProgramStringARB, (GLenum target, GLenum pname, GLvoid *string))
FUNC(GLboolean, glIsProgramARB, (GLuint program))
// GL_ARB_shader_objects
// (NOTE: Many of these have "Object" in their ARB names, but "Program" or "Shader" in their core names.

View File

@ -279,14 +279,35 @@ void CGame::Interpolate(float frameLength)
m_TurnManager->Interpolate(frameLength);
}
static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
CColor CGame::GetPlayerColour(int player) const
void CGame::CachePlayerColours()
{
m_PlayerColours.clear();
CmpPtr<ICmpPlayerManager> cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
if (cmpPlayerManager.null())
return BrokenColor;
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(player));
if (cmpPlayer.null())
return BrokenColor;
return cmpPlayer->GetColour();
return;
int numPlayers = cmpPlayerManager->GetNumPlayers();
m_PlayerColours.resize(numPlayers);
for (int i = 0; i < numPlayers; ++i)
{
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(i));
if (cmpPlayer.null())
m_PlayerColours[i] = BrokenColor;
else
m_PlayerColours[i] = cmpPlayer->GetColour();
}
}
CColor CGame::GetPlayerColour(int player) const
{
if (player < 0 || player >= (int)m_PlayerColours.size())
return BrokenColor;
return m_PlayerColours[player];
}

View File

@ -92,6 +92,14 @@ public:
int GetPlayerID();
void SetPlayerID(int playerID);
/**
* Retrieving player colours from scripts is slow, so this updates an
* internal cache of all players' colours.
* Call this just before rendering, so it will always have the latest
* colours.
*/
void CachePlayerColours();
CColor GetPlayerColour(int player) const;
/**
@ -155,6 +163,8 @@ private:
void RegisterInit(const CScriptValRooted& attribs);
IReplayLogger* m_ReplayLogger;
CScriptValRooted m_RegisteredAttribs;
std::vector<CColor> m_PlayerColours;
};
extern CGame *g_Game;

View File

@ -199,15 +199,17 @@ void Render()
GUI<CColor>::ParseString(skystring.FromUTF8(), skycol);
g_Renderer.SetClearColor(skycol.AsSColor4ub());
// prepare before starting the renderer frame
if (g_Game && g_Game->IsGameStarted())
g_Game->GetView()->BeginFrame();
// start new frame
g_Renderer.BeginFrame();
ogl_WarnIfError();
if (g_Game && g_Game->IsGameStarted())
{
g_Game->GetView()->Render();
}
ogl_WarnIfError();

View File

@ -58,6 +58,13 @@ bool RelaxNGValidator::LoadGrammar(const std::string& grammar)
}
bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document)
{
std::string docutf8 = "<?xml version='1.0' encoding='utf-8'?>" + utf8_from_wstring(document);
return ValidateEncoded(filename, docutf8);
}
bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document)
{
TIMER_ACCRUE(xml_validation);
@ -67,9 +74,7 @@ bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring
return false;
}
std::string docutf8 = "<?xml version='1.0' encoding='utf-8'?>" + utf8_from_wstring(document);
xmlDocPtr doc = xmlReadMemory(docutf8.c_str(), (int)docutf8.size(), utf8_from_wstring(filename).c_str(), NULL, XML_PARSE_NONET);
xmlDocPtr doc = xmlReadMemory(document.c_str(), (int)document.size(), utf8_from_wstring(filename).c_str(), NULL, XML_PARSE_NONET);
if (doc == NULL)
{
LOGERROR(L"RelaxNGValidator: Failed to parse document");

View File

@ -31,6 +31,8 @@ public:
bool Validate(const std::wstring& filename, const std::wstring& document);
bool ValidateEncoded(const std::wstring& filename, const std::string& document);
private:
xmlRelaxNGPtr m_Schema;
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -21,6 +21,7 @@
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/XML/Xeromyces.h"
#include "lib/utf8.h"
#include "lib/sysdep/cpu.h"
#include "maths/Fixed.h"
@ -111,6 +112,22 @@ const CStr& XMLWriter_File::GetOutput()
}
void XMLWriter_File::XMB(const XMBFile& file)
{
ElementXMB(file, file.GetRoot());
}
void XMLWriter_File::ElementXMB(const XMBFile& file, XMBElement el)
{
XMLWriter_Element writer(*this, file.GetElementString(el.GetNodeName()).c_str());
XERO_ITER_ATTR(el, attr)
writer.Attribute(file.GetAttributeString(attr.Name).c_str(), attr.Value);
XERO_ITER_EL(el, child)
ElementXMB(file, child);
}
void XMLWriter_File::Comment(const char* text)
{
ElementStart(NULL, "!-- ");

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -91,6 +91,8 @@ end of XMLWriter.cpp.
// Add a 'setting': <name>value</name>
#define XML_Setting(name, value) xml_element_.Setting(name, value)
#define XML_WriteXMB(xero) xml_file_.XMB(xero)
// Create a VFS file from the XML data.
// Returns true on success, false (and logs an error) on failure.
#define XML_StoreVFS(vfs, pathname) xml_file_.StoreVFS(vfs, pathname)
@ -103,6 +105,8 @@ end of XMLWriter.cpp.
#include "ps/CStr.h"
#include "lib/file/vfs/vfs.h"
class XMBElement;
class XMBFile;
class XMLWriter_Element;
class XMLWriter_File
@ -114,6 +118,8 @@ public:
void Comment(const char* text);
void XMB(const XMBFile& file);
bool StoreVFS(const PIVFS& vfs, const VfsPath& pathname);
const CStr& GetOutput();
@ -121,6 +127,8 @@ private:
friend class XMLWriter_Element;
void ElementXMB(const XMBFile& file, XMBElement el);
void ElementStart(XMLWriter_Element* element, const char* name);
void ElementText(const char* text, bool cdata);
template <typename T> void ElementAttribute(const char* name, const T& value, bool newelement);

View File

@ -20,6 +20,7 @@
#include "DecalRData.h"
#include "graphics/Decal.h"
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/Terrain.h"
#include "graphics/TextureManager.h"
@ -41,6 +42,10 @@ CDecalRData::CDecalRData(CModelDecal* decal)
m_Position.elems = 3;
m_Array.AddAttribute(&m_Position);
m_DiffuseColor.type = GL_UNSIGNED_BYTE;
m_DiffuseColor.elems = 4;
m_Array.AddAttribute(&m_DiffuseColor);
m_UV.type = GL_FLOAT;
m_UV.elems = 2;
m_Array.AddAttribute(&m_UV);
@ -81,9 +86,12 @@ void CDecalRData::Render()
u8* indexBase = m_IndexArray.Bind();
// TODO: make the shading color available to shader-based rendering
// (which uses the color array) too
glColor3fv(m_Decal->GetShadingColor().FloatArray());
glVertexPointer(3, GL_FLOAT, stride, base + m_Position.offset);
glColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_DiffuseColor.offset);
glTexCoordPointer(2, GL_FLOAT, stride, base + m_UV.offset);
if (!g_Renderer.m_SkipSubmit)
@ -118,8 +126,11 @@ void CDecalRData::BuildArrays()
m_Array.SetNumVertices((i1-i0+1)*(j1-j0+1));
m_Array.Layout();
VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
VertexArrayIterator<SColor4ub> DiffuseColor = m_DiffuseColor.GetIterator<SColor4ub>();
VertexArrayIterator<float[2]> UV = m_UV.GetIterator<float[2]>();
bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
for (ssize_t j = j0; j <= j1; ++j)
{
for (ssize_t i = i0; i <= i1; ++i)
@ -133,6 +144,11 @@ void CDecalRData::BuildArrays()
*Position = pos;
++Position;
CVector3D normal;
m_Decal->m_Terrain->CalcNormal(i, j, normal);
*DiffuseColor = g_Renderer.GetLightEnv().EvaluateDiffuse(normal, includeSunColor);
++DiffuseColor;
// Map from world space back into decal texture space
CVector3D inv = m_Decal->GetInvTransform().Transform(pos);
(*UV)[0] = 0.5f + (inv.X - decal.m_OffsetX) / decal.m_SizeX;

View File

@ -40,6 +40,7 @@ private:
VertexArray m_Array;
VertexArray::Attribute m_Position;
VertexArray::Attribute m_DiffuseColor;
VertexArray::Attribute m_UV;
CModelDecal* m_Decal;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -26,8 +26,6 @@
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
#include "ps/CLogger.h"
#include "graphics/Color.h"
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
@ -81,9 +79,6 @@ struct HWLModel
struct HWLightingModelRendererInternals
{
/// Currently used RenderModifier
RenderModifierPtr modifier;
/// Previously prepared modeldef
HWLModelDef* hwlmodeldef;
@ -331,4 +326,71 @@ void HWLightingModelRenderer::RenderModel(int streamflags, CModel* model, void*
g_Renderer.m_Stats.m_ModelTris += numFaces;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// ShaderModelRenderer implementation
ShaderModelRenderer::ShaderModelRenderer() :
HWLightingModelRenderer(false)
{
}
void ShaderModelRenderer::BeginPass(int streamflags, const CMatrix3D* UNUSED(texturematrix))
{
debug_assert(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0)));
if (streamflags & STREAM_POS)
glEnableClientState(GL_VERTEX_ARRAY);
if (streamflags & STREAM_NORMAL)
glEnableClientState(GL_NORMAL_ARRAY);
if (streamflags & STREAM_UV0)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
void ShaderModelRenderer::EndPass(int streamflags)
{
if (streamflags & STREAM_POS)
glDisableClientState(GL_VERTEX_ARRAY);
if (streamflags & STREAM_NORMAL)
glDisableClientState(GL_NORMAL_ARRAY);
if (streamflags & STREAM_UV0)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
CVertexBuffer::Unbind();
}
void ShaderModelRenderer::RenderModel(int streamflags, CModel* model, void* data)
{
CModelDefPtr mdldef = model->GetModelDef();
HWLModel* hwlmodel = (HWLModel*)data;
u8* base = hwlmodel->m_Array.Bind();
GLsizei stride = (GLsizei)hwlmodel->m_Array.GetStride();
u8* indexBase = m->hwlmodeldef->m_IndexArray.Bind();
if (streamflags & STREAM_POS)
glVertexPointer(3, GL_FLOAT, stride, base + hwlmodel->m_Position.offset);
if (streamflags & STREAM_NORMAL)
glNormalPointer(GL_FLOAT, stride, base + hwlmodel->m_Normal.offset);
if (streamflags & STREAM_UV0)
glTexCoordPointer(2, GL_FLOAT, stride, base + hwlmodel->m_UV.offset);
// render the lot
size_t numFaces = mdldef->GetNumFaces();
if (!g_Renderer.m_SkipSubmit)
{
pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1,
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_ModelTris += numFaces;
}

View File

@ -68,9 +68,24 @@ public:
*/
static bool IsAvailable();
private:
protected:
HWLightingModelRendererInternals* m;
};
/**
* Render animated models using a ShaderRenderModifier.
* This just passes through the vertex data directly; the modifier is responsible
* for setting any shader uniforms etc.
*/
class ShaderModelRenderer : public HWLightingModelRenderer
{
public:
ShaderModelRenderer();
void BeginPass(int streamflags, const CMatrix3D* texturematrix);
void EndPass(int streamflags);
void RenderModel(int streamflags, CModel* model, void* data);
};
#endif // INCLUDED_HWLIGHTINGMODELRENDERER

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -289,4 +289,78 @@ void InstancingModelRenderer::RenderModel(int streamflags, CModel* model, void*
g_Renderer.m_Stats.m_ModelTris += numFaces;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// ShaderInstancingModelRenderer implementation
ShaderInstancingModelRenderer::ShaderInstancingModelRenderer() :
InstancingModelRenderer(false)
{
}
void ShaderInstancingModelRenderer::BeginPass(int streamflags, const CMatrix3D* UNUSED(texturematrix))
{
debug_assert(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0)));
if (streamflags & STREAM_POS)
glEnableClientState(GL_VERTEX_ARRAY);
if (streamflags & STREAM_NORMAL)
glEnableClientState(GL_NORMAL_ARRAY);
if (streamflags & STREAM_UV0)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
void ShaderInstancingModelRenderer::EndPass(int streamflags)
{
if (streamflags & STREAM_POS)
glDisableClientState(GL_VERTEX_ARRAY);
if (streamflags & STREAM_NORMAL)
glDisableClientState(GL_NORMAL_ARRAY);
if (streamflags & STREAM_UV0)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
CVertexBuffer::Unbind();
}
void ShaderInstancingModelRenderer::PrepareModelDef(int streamflags, const CModelDefPtr& def)
{
m->imodeldef = (IModelDef*)def->GetRenderData(m);
debug_assert(m->imodeldef);
u8* base = m->imodeldef->m_Array.Bind();
GLsizei stride = (GLsizei)m->imodeldef->m_Array.GetStride();
m->imodeldefIndexBase = m->imodeldef->m_IndexArray.Bind();
if (streamflags & STREAM_POS)
glVertexPointer(3, GL_FLOAT, stride, base + m->imodeldef->m_Position.offset);
if (streamflags & STREAM_NORMAL)
glNormalPointer(GL_FLOAT, stride, base + m->imodeldef->m_Normal.offset);
if (streamflags & STREAM_UV0)
glTexCoordPointer(2, GL_FLOAT, stride, base + m->imodeldef->m_UV.offset);
}
void ShaderInstancingModelRenderer::RenderModel(int UNUSED(streamflags), CModel* model, void* UNUSED(data))
{
CModelDefPtr mdldef = model->GetModelDef();
// render the lot
size_t numFaces = mdldef->GetNumFaces();
if (!g_Renderer.m_SkipSubmit)
{
pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1,
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_ModelTris += numFaces;
}

View File

@ -68,9 +68,24 @@ public:
*/
static bool IsAvailable();
private:
protected:
InstancingModelRendererInternals* m;
};
/**
* Render non-animated (but potentially moving) models using a ShaderRenderModifier.
* This just passes through the vertex data directly; the modifier is responsible
* for setting any shader uniforms etc (including the instancing transform).
*/
class ShaderInstancingModelRenderer : public InstancingModelRenderer
{
public:
ShaderInstancingModelRenderer();
void BeginPass(int streamflags, const CMatrix3D* texturematrix);
void EndPass(int streamflags);
void PrepareModelDef(int streamflags, const CModelDefPtr& def);
void RenderModel(int streamflags, CModel* model, void* data);
};
#endif // INCLUDED_INSTANCINGMODELRENDERER

View File

@ -358,28 +358,41 @@ void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector
SBlendVertex dst;
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
CVector3D normal;
bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
size_t index = blendVertices.size();
CalculateUV(dst.m_UVs, gx, gz);
terrain->CalcPosition(gx, gz, dst.m_Position);
terrain->CalcNormal(gx, gz, normal);
dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
blendVertices.push_back(dst);
CalculateUV(dst.m_UVs, gx + 1, gz);
terrain->CalcPosition(gx + 1, gz, dst.m_Position);
terrain->CalcNormal(gx + 1, gz, normal);
dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
blendVertices.push_back(dst);
CalculateUV(dst.m_UVs, gx + 1, gz + 1);
terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
terrain->CalcNormal(gx + 1, gz + 1, normal);
dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
blendVertices.push_back(dst);
CalculateUV(dst.m_UVs, gx, gz + 1);
terrain->CalcPosition(gx, gz + 1, dst.m_Position);
terrain->CalcNormal(gx, gz + 1, normal);
dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
blendVertices.push_back(dst);
@ -516,6 +529,8 @@ void CPatchRData::BuildVertices()
CTerrain* terrain=m_Patch->m_Parent;
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
// build vertices
for (ssize_t j=0;j<vsize;j++) {
for (ssize_t i=0;i<vsize;i++) {
@ -533,9 +548,7 @@ void CPatchRData::BuildVertices()
CVector3D normal;
terrain->CalcNormal(ix,iz,normal);
RGBColor diffuse;
lightEnv.EvaluateDirect(normal, diffuse);
vertices[v].m_DiffuseColor = ConvertRGBColorTo4ub(diffuse);
vertices[v].m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
}
}
@ -836,6 +849,8 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]);

View File

@ -82,12 +82,12 @@ private:
struct SBlendVertex {
// vertex position
CVector3D m_Position;
// diffuse color from sunlight
SColor4ub m_DiffuseColor;
// vertex uvs for base texture
float m_UVs[2];
// vertex uvs for alpha texture
float m_AlphaUVs[2];
// add some padding
u32 m_Padding[1];
};
cassert(sizeof(SBlendVertex) == 32);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -24,10 +24,13 @@
#include "lib/ogl.h"
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
#include "maths/Matrix3D.h"
#include "ps/Game.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/Model.h"
#include "graphics/TextureManager.h"
@ -35,7 +38,7 @@
#include "renderer/Renderer.h"
#include "renderer/ShadowMap.h"
#include <boost/algorithm/string.hpp>
///////////////////////////////////////////////////////////////////////////////////////////////
// RenderModifier implementation
@ -299,3 +302,82 @@ void SolidColorRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& UNU
void SolidColorRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
// ShaderRenderModifier implementation
ShaderRenderModifier::ShaderRenderModifier(const CShaderTechnique& technique) :
m_Technique(technique)
{
}
ShaderRenderModifier::~ShaderRenderModifier()
{
}
int ShaderRenderModifier::BeginPass(int pass)
{
m_Technique.BeginPass(pass);
CShaderProgramPtr shader = m_Technique.GetShader(pass);
if (GetShadowMap() && shader->HasTexture("shadowTex"))
{
shader->BindTexture("shadowTex", GetShadowMap()->GetTexture());
shader->Uniform("shadowTransform", GetShadowMap()->GetTextureMatrix());
}
if (GetLightEnv())
{
shader->Uniform("ambient", GetLightEnv()->m_UnitsAmbientColor);
shader->Uniform("sunDir", GetLightEnv()->GetSunDir());
shader->Uniform("sunColor", GetLightEnv()->m_SunColor);
}
if (shader->HasTexture("losTex"))
{
CLOSTexture& los = g_Game->GetView()->GetLOSTexture();
shader->BindTexture("losTex", los.GetTexture());
// Don't bother sending the whole matrix, we just need two floats (scale and translation)
shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
}
m_BindingInstancingTransform = shader->GetUniformBinding("instancingTransform");
m_BindingShadingColor = shader->GetUniformBinding("shadingColor");
m_BindingObjectColor = shader->GetUniformBinding("objectColor");
m_BindingPlayerColor = shader->GetUniformBinding("playerColor");
return shader->GetStreamFlags();
}
bool ShaderRenderModifier::EndPass(int pass)
{
m_Technique.EndPass(pass);
return (pass >= m_Technique.GetNumPasses()-1);
}
void ShaderRenderModifier::PrepareTexture(int pass, CTexturePtr& texture)
{
CShaderProgramPtr shader = m_Technique.GetShader(pass);
shader->BindTexture("baseTex", texture->GetHandle());
}
void ShaderRenderModifier::PrepareModel(int pass, CModel* model)
{
CShaderProgramPtr shader = m_Technique.GetShader(pass);
if (m_BindingInstancingTransform.Active())
shader->Uniform(m_BindingInstancingTransform, model->GetTransform());
if (m_BindingShadingColor.Active())
shader->Uniform(m_BindingShadingColor, model->GetShadingColor());
if (m_BindingObjectColor.Active())
shader->Uniform(m_BindingObjectColor, model->GetMaterial().GetObjectColor());
if (m_BindingPlayerColor.Active())
shader->Uniform(m_BindingPlayerColor, model->GetMaterial().GetPlayerColor());
}

View File

@ -25,6 +25,7 @@
#define INCLUDED_RENDERMODIFIERS
#include "ModelRenderer.h"
#include "graphics/ShaderTechnique.h"
#include "graphics/Texture.h"
class CLightEnv;
@ -230,4 +231,28 @@ public:
void PrepareModel(int pass, CModel* model);
};
/**
* A RenderModifier that can be used with any CShaderTechnique.
* Uniforms and textures get set appropriately.
*/
class ShaderRenderModifier : public LitRenderModifier
{
public:
ShaderRenderModifier(const CShaderTechnique& technique);
~ShaderRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
private:
CShaderTechnique m_Technique;
CShaderProgram::Binding m_BindingInstancingTransform;
CShaderProgram::Binding m_BindingShadingColor;
CShaderProgram::Binding m_BindingObjectColor;
CShaderProgram::Binding m_BindingPlayerColor;
};
#endif // INCLUDED_RENDERMODIFIERS

View File

@ -42,15 +42,17 @@
#include "ps/Loader.h"
#include "ps/ProfileViewer.h"
#include "graphics/Camera.h"
#include "graphics/Texture.h"
#include "graphics/TextureManager.h"
#include "graphics/DefaultEmitter.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/Terrain.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "graphics/GameView.h"
#include "graphics/ParticleEngine.h"
#include "graphics/DefaultEmitter.h"
#include "graphics/ShaderManager.h"
#include "graphics/ShaderTechnique.h"
#include "graphics/Terrain.h"
#include "graphics/Texture.h"
#include "graphics/TextureManager.h"
#include "renderer/FixedFunctionModelRenderer.h"
#include "renderer/HWLightingModelRenderer.h"
#include "renderer/InstancingModelRenderer.h"
@ -222,9 +224,15 @@ public:
/// true if CRenderer::Open has been called
bool IsOpen;
/// true if shaders need to be reloaded
bool ShadersDirty;
/// Table to display renderer stats in-game via profile system
CRendererStatsTable profileTable;
/// Shader manager
CShaderManager shaderManager;
/// Water manager
WaterManager waterManager;
@ -266,10 +274,18 @@ public:
ModelRenderer* pal_TranspHWLit[NumVertexTypes];
ModelRenderer* pal_TranspSortAll;
ModelRenderer* pal_NormalShader;
ModelRenderer* pal_NormalInstancingShader;
ModelRenderer* pal_PlayerShader;
ModelRenderer* pal_PlayerInstancingShader;
ModelRenderer* pal_TranspShader;
ModelVertexRendererPtr VertexFF[NumVertexTypes];
ModelVertexRendererPtr VertexHWLit[NumVertexTypes];
ModelVertexRendererPtr VertexInstancing[NumVertexTypes];
ModelVertexRendererPtr VertexPolygonSort;
ModelVertexRendererPtr VertexRendererShader;
ModelVertexRendererPtr VertexInstancingShader;
// generic RenderModifiers that are supposed to be used directly
RenderModifierPtr ModWireframe;
@ -280,7 +296,13 @@ public:
// RenderModifiers that are selected from the palette below
RenderModifierPtr ModNormal;
RenderModifierPtr ModNormalInstancing;
RenderModifierPtr ModPlayer;
RenderModifierPtr ModPlayerInstancing;
RenderModifierPtr ModSolid;
RenderModifierPtr ModSolidInstancing;
RenderModifierPtr ModSolidPlayer;
RenderModifierPtr ModSolidPlayerInstancing;
RenderModifierPtr ModTransparent;
// Palette of available RenderModifiers
@ -290,11 +312,23 @@ public:
LitRenderModifierPtr ModPlayerLit;
RenderModifierPtr ModTransparentUnlit;
LitRenderModifierPtr ModTransparentLit;
RenderModifierPtr ModShaderSolidColor;
RenderModifierPtr ModShaderSolidColorInstancing;
RenderModifierPtr ModShaderSolidPlayerColor;
RenderModifierPtr ModShaderSolidPlayerColorInstancing;
RenderModifierPtr ModShaderSolidTex;
LitRenderModifierPtr ModShaderNormal;
LitRenderModifierPtr ModShaderNormalInstancing;
LitRenderModifierPtr ModShaderPlayer;
LitRenderModifierPtr ModShaderPlayerInstancing;
LitRenderModifierPtr ModShaderTransparent;
RenderModifierPtr ModShaderTransparentShadow;
} Model;
CRendererInternals()
: IsOpen(false), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false)
: IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false)
{
terrainRenderer = new TerrainRenderer();
shadow = new ShadowMap();
@ -312,6 +346,12 @@ public:
}
Model.pal_TranspSortAll = 0;
Model.pal_NormalShader = 0;
Model.pal_NormalInstancingShader = 0;
Model.pal_PlayerShader = 0;
Model.pal_PlayerInstancingShader = 0;
Model.pal_TranspShader = 0;
Model.Normal = 0;
Model.NormalInstancing = 0;
Model.Player = 0;
@ -360,15 +400,18 @@ public:
/**
* Renders all non-transparent models with the given modifiers.
*/
void CallModelRenderers(const RenderModifierPtr& modNormal, const RenderModifierPtr& modPlayer, int flags)
void CallModelRenderers(
const RenderModifierPtr& modNormal, const RenderModifierPtr& modNormalInstancing,
const RenderModifierPtr& modPlayer, const RenderModifierPtr& modPlayerInstancing,
int flags)
{
Model.Normal->Render(modNormal, flags);
if (Model.Normal != Model.NormalInstancing)
Model.NormalInstancing->Render(modNormal, flags);
Model.NormalInstancing->Render(modNormalInstancing, flags);
Model.Player->Render(modPlayer, flags);
if (Model.Player != Model.PlayerInstancing)
Model.PlayerInstancing->Render(modPlayer, flags);
Model.PlayerInstancing->Render(modPlayerInstancing, flags);
}
};
@ -403,13 +446,15 @@ CRenderer::CRenderer()
m_Options.m_FancyWater = false;
m_Options.m_Shadows = false;
m_Options.m_ShadowAlphaFix = true;
m_Options.m_ARBProgramShadow = true;
m_ShadowZBias = 0.02f;
m_ShadowMapSize = 0;
m_LightEnv = NULL;
m_hCompositeAlphaMap = 0;
AddLocalProperty(L"shadows", &m_Options.m_Shadows, false);
AddLocalProperty(L"fancyWater", &m_Options.m_FancyWater, false);
AddLocalProperty(L"horizonHeight", &m->skyManager.m_HorizonHeight, false);
AddLocalProperty(L"waterMurkiness", &m->waterManager.m_Murkiness, false);
@ -442,6 +487,12 @@ CRenderer::~CRenderer()
}
delete m->Model.pal_TranspSortAll;
delete m->Model.pal_NormalShader;
delete m->Model.pal_NormalInstancingShader;
delete m->Model.pal_PlayerShader;
delete m->Model.pal_PlayerInstancingShader;
delete m->Model.pal_TranspShader;
// general
delete m_VertexShader;
m_VertexShader = 0;
@ -461,6 +512,8 @@ void CRenderer::EnumCaps()
{
// assume support for nothing
m_Caps.m_VBO = false;
m_Caps.m_ARBProgram = false;
m_Caps.m_ARBProgramShadow = false;
m_Caps.m_VertexShader = false;
m_Caps.m_FragmentShader = false;
m_Caps.m_Shadows = false;
@ -474,12 +527,19 @@ void CRenderer::EnumCaps()
}
}
if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL))
{
m_Caps.m_ARBProgram = true;
if (ogl_HaveExtension("GL_ARB_fragment_program_shadow"))
m_Caps.m_ARBProgramShadow = true;
}
if (0 == ogl_HaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", NULL))
{
if (ogl_HaveExtension("GL_ARB_vertex_shader"))
m_Caps.m_VertexShader=true;
m_Caps.m_VertexShader = true;
if (ogl_HaveExtension("GL_ARB_fragment_shader"))
m_Caps.m_FragmentShader=true;
m_Caps.m_FragmentShader = true;
}
if (ogl_max_tex_units >= 3)
@ -507,6 +567,74 @@ void CRenderer::EnumCaps()
}
}
void CRenderer::ReloadShaders()
{
typedef std::map<CStr, CStr> Defines;
Defines defNull;
Defines defBasic;
if (m_Options.m_Shadows)
{
defBasic["USE_SHADOW"] = "1";
if (m_Caps.m_ARBProgramShadow && m_Options.m_ARBProgramShadow)
defBasic["USE_FP_SHADOW"] = "1";
}
if (m_LightEnv)
defBasic["LIGHTING_MODEL_" + m_LightEnv->GetLightingModel()] = "1";
Defines defColored = defBasic;
defColored["USE_OBJECTCOLOR"] = "1";
Defines defTransparent = defBasic;
defTransparent["USE_TRANSPARENT"] = "1";
// TODO: it'd be nicer to load this technique from an XML file or something
CShaderPass passTransparent0(m->shaderManager.LoadProgram("solid_tex", defNull));
passTransparent0.AlphaFunc(GL_GREATER, 0.975f);
passTransparent0.ColorMask(0, 0, 0, 0);
CShaderPass passTransparent1(m->shaderManager.LoadProgram("model_common", defTransparent));
passTransparent1.AlphaFunc(GL_GREATER, 0.0f);
passTransparent1.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
passTransparent1.DepthMask(0);
CShaderTechnique techTransparent(passTransparent0);
techTransparent.AddPass(passTransparent1);
CShaderPass passTransparentShadow(m->shaderManager.LoadProgram("solid_tex", defBasic));
passTransparentShadow.AlphaFunc(GL_GREATER, 0.4f);
CShaderTechnique techTransparentShadow(passTransparentShadow);
m->Model.ModShaderSolidColor = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"solid", defNull))));
m->Model.ModShaderSolidColorInstancing = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"solid_instancing", defNull))));
m->Model.ModShaderSolidPlayerColor = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"solid_player", defNull))));
m->Model.ModShaderSolidPlayerColorInstancing = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"solid_player_instancing", defNull))));
m->Model.ModShaderSolidTex = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"solid_tex", defNull))));
m->Model.ModShaderNormal = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common", defBasic))));
m->Model.ModShaderNormalInstancing = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common_instancing", defBasic))));
m->Model.ModShaderPlayer = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common", defColored))));
m->Model.ModShaderPlayerInstancing = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common_instancing", defColored))));
m->Model.ModShaderTransparent = LitRenderModifierPtr(new ShaderRenderModifier(
techTransparent));
m->Model.ModShaderTransparentShadow = LitRenderModifierPtr(new ShaderRenderModifier(
techTransparentShadow));
m->ShadersDirty = false;
}
bool CRenderer::Open(int width, int height)
{
@ -524,6 +652,7 @@ bool CRenderer::Open(int width, int height)
m_VertexShader = 0;
}
// model rendering
m->Model.VertexFF[AmbientDiffuse] = ModelVertexRendererPtr(new FixedFunctionModelRenderer(false));
m->Model.VertexFF[OnlyDiffuse] = ModelVertexRendererPtr(new FixedFunctionModelRenderer(true));
@ -538,6 +667,8 @@ bool CRenderer::Open(int width, int height)
m->Model.VertexInstancing[OnlyDiffuse] = ModelVertexRendererPtr(new InstancingModelRenderer(true));
}
m->Model.VertexPolygonSort = ModelVertexRendererPtr(new PolygonSortModelRenderer);
m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelRenderer);
m->Model.VertexInstancingShader = ModelVertexRendererPtr(new ShaderInstancingModelRenderer);
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
{
@ -559,6 +690,12 @@ bool CRenderer::Open(int width, int height)
m->Model.pal_TranspSortAll = new SortModelRenderer(m->Model.VertexPolygonSort);
m->Model.pal_NormalShader = new BatchModelRenderer(m->Model.VertexRendererShader);
m->Model.pal_NormalInstancingShader = new BatchModelRenderer(m->Model.VertexInstancingShader);
m->Model.pal_PlayerShader = new BatchModelRenderer(m->Model.VertexRendererShader);
m->Model.pal_PlayerInstancingShader = new BatchModelRenderer(m->Model.VertexInstancingShader);
m->Model.pal_TranspShader = new SortModelRenderer(m->Model.VertexRendererShader);
m->Model.ModWireframe = RenderModifierPtr(new WireframeRenderModifier);
m->Model.ModPlain = RenderModifierPtr(new PlainRenderModifier);
m->Model.ModPlainLit = LitRenderModifierPtr(new PlainLitRenderModifier);
@ -628,6 +765,7 @@ void CRenderer::SetOptionBool(enum Option opt,bool value)
break;
case OPT_SHADOWS:
m_Options.m_Shadows=value;
ReloadShaders();
break;
case OPT_FANCYWATER:
m_Options.m_FancyWater=value;
@ -688,8 +826,8 @@ void CRenderer::SetRenderPath(RenderPath rp)
// Renderer has been opened, so validate the selected renderpath
if (rp == RP_DEFAULT)
{
if (m->CanUseRenderPathVertexShader())
rp = RP_VERTEXSHADER;
if (m_Caps.m_ARBProgram)
rp = RP_SHADER;
else
rp = RP_FIXED;
}
@ -703,7 +841,20 @@ void CRenderer::SetRenderPath(RenderPath rp)
}
}
if (rp == RP_SHADER)
{
if (!m_Caps.m_ARBProgram)
{
LOGWARNING(L"Falling back to fixed function\n");
rp = RP_FIXED;
}
}
m_Options.m_RenderPath = rp;
// We might need to regenerate some render data after changing path
if (g_Game)
g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR);
}
@ -713,6 +864,7 @@ CStr CRenderer::GetRenderPathName(RenderPath rp)
case RP_DEFAULT: return "default";
case RP_FIXED: return "fixed";
case RP_VERTEXSHADER: return "vertexshader";
case RP_SHADER: return "shader";
default: return "(invalid)";
}
}
@ -723,6 +875,8 @@ CRenderer::RenderPath CRenderer::GetRenderPathByName(const CStr& name)
return RP_FIXED;
if (name == "vertexshader")
return RP_VERTEXSHADER;
if (name == "shader")
return RP_SHADER;
if (name == "default")
return RP_DEFAULT;
@ -758,12 +912,54 @@ void CRenderer::BeginFrame()
{
PROFILE("begin frame");
if (m_VertexShader)
m_VertexShader->BeginFrame();
// zero out all the per-frame stats
m_Stats.Reset();
// handle the new shader-based approach
if (m_Options.m_RenderPath == RP_SHADER)
{
if (m->ShadersDirty)
ReloadShaders();
m->Model.ModShaderNormal->SetShadowMap(m->shadow);
m->Model.ModShaderNormal->SetLightEnv(m_LightEnv);
m->Model.ModShaderNormalInstancing->SetShadowMap(m->shadow);
m->Model.ModShaderNormalInstancing->SetLightEnv(m_LightEnv);
m->Model.ModShaderPlayer->SetShadowMap(m->shadow);
m->Model.ModShaderPlayer->SetLightEnv(m_LightEnv);
m->Model.ModShaderPlayerInstancing->SetShadowMap(m->shadow);
m->Model.ModShaderPlayerInstancing->SetLightEnv(m_LightEnv);
m->Model.ModShaderTransparent->SetShadowMap(m->shadow);
m->Model.ModShaderTransparent->SetLightEnv(m_LightEnv);
m->Model.ModNormal = m->Model.ModShaderNormal;
m->Model.ModNormalInstancing = m->Model.ModShaderNormalInstancing;
m->Model.ModPlayer = m->Model.ModShaderPlayer;
m->Model.ModPlayerInstancing = m->Model.ModShaderPlayerInstancing;
m->Model.ModSolid = m->Model.ModShaderSolidColor;
m->Model.ModSolidInstancing = m->Model.ModShaderSolidColorInstancing;
m->Model.ModSolidPlayer = m->Model.ModShaderSolidPlayerColor;
m->Model.ModSolidPlayerInstancing = m->Model.ModShaderSolidPlayerColorInstancing;
m->Model.ModTransparent = m->Model.ModShaderTransparent;
m->Model.Normal = m->Model.pal_NormalShader;
m->Model.NormalInstancing = m->Model.pal_NormalInstancingShader;
m->Model.Player = m->Model.pal_PlayerShader;
m->Model.PlayerInstancing = m->Model.pal_PlayerInstancingShader;
m->Model.Transp = m->Model.pal_TranspShader;
return;
}
if (m_VertexShader)
m_VertexShader->BeginFrame();
// choose model renderers for this frame
int vertexType;
@ -771,10 +967,12 @@ void CRenderer::BeginFrame()
{
vertexType = OnlyDiffuse;
m->Model.ModNormal = m->Model.ModPlainLit;
m->Model.ModNormalInstancing = m->Model.ModPlainLit;
m->Model.ModPlainLit->SetShadowMap(m->shadow);
m->Model.ModPlainLit->SetLightEnv(m_LightEnv);
m->Model.ModPlayer = m->Model.ModPlayerLit;
m->Model.ModPlayerInstancing = m->Model.ModPlayerLit;
m->Model.ModPlayerLit->SetShadowMap(m->shadow);
m->Model.ModPlayerLit->SetLightEnv(m_LightEnv);
@ -786,7 +984,9 @@ void CRenderer::BeginFrame()
{
vertexType = AmbientDiffuse;
m->Model.ModNormal = m->Model.ModPlain;
m->Model.ModNormalInstancing = m->Model.ModPlain;
m->Model.ModPlayer = m->Model.ModPlayerUnlit;
m->Model.ModPlayerInstancing = m->Model.ModPlayerUnlit;
m->Model.ModTransparent = m->Model.ModTransparentUnlit;
}
@ -815,6 +1015,11 @@ void CRenderer::BeginFrame()
m->Model.Player = m->Model.pal_PlayerFF[vertexType];
}
m->Model.ModSolid = m->Model.ModSolidColor;
m->Model.ModSolidInstancing = m->Model.ModSolidColor;
m->Model.ModSolidPlayer = m->Model.ModSolidPlayerColor;
m->Model.ModSolidPlayerInstancing = m->Model.ModSolidPlayerColor;
if (m_SortAllTransparent)
m->Model.Transp = m->Model.pal_TranspSortAll;
else if (m_Options.m_RenderPath == RP_VERTEXSHADER)
@ -835,7 +1040,7 @@ void CRenderer::SetClearColor(SColor4ub color)
void CRenderer::RenderShadowMap()
{
PROFILE( "render shadow map" );
PROFILE("render shadow map");
m->shadow->BeginRender();
@ -843,10 +1048,19 @@ void CRenderer::RenderShadowMap()
glColor3f(shadowTransp, shadowTransp, shadowTransp);
// Figure out transparent rendering strategy
RenderModifierPtr transparentShadows = m->Model.ModTransparentShadow;
RenderModifierPtr transparentShadows;
if (GetRenderPath() == RP_SHADER)
{
transparentShadows = m->Model.ModShaderTransparentShadow;
}
else
{
if (m->shadow->GetUseDepthTexture())
transparentShadows = m->Model.ModTransparentDepthShadow;
else
transparentShadows = m->Model.ModTransparentShadow;
}
if (m->shadow->GetUseDepthTexture())
transparentShadows = m->Model.ModTransparentDepthShadow;
// Render all closed models (i.e. models where rendering back faces will produce
// the correct result)
@ -865,7 +1079,8 @@ void CRenderer::RenderShadowMap()
{
PROFILE("render models");
m->CallModelRenderers(m->Model.ModSolidColor, m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS);
m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing,
m->Model.ModSolid, m->Model.ModSolidInstancing, MODELFLAG_CASTSHADOWS);
}
{
@ -882,88 +1097,108 @@ void CRenderer::RenderShadowMap()
void CRenderer::RenderPatches()
{
PROFILE( "render patches" );
PROFILE("render patches");
// switch on wireframe if we need it
if (m_TerrainRenderMode==WIREFRAME) {
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
if (m_TerrainRenderMode == WIREFRAME)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
// render all the patches, including blend pass
m->terrainRenderer->RenderTerrain((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0);
if (GetRenderPath() == RP_SHADER)
m->terrainRenderer->RenderTerrainShader((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0);
else
m->terrainRenderer->RenderTerrain((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0);
if (m_TerrainRenderMode==WIREFRAME) {
if (m_TerrainRenderMode == WIREFRAME)
{
// switch wireframe off again
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
} else if (m_TerrainRenderMode==EDGED_FACES) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
else if (m_TerrainRenderMode == EDGED_FACES)
{
// edged faces: need to make a second pass over the data:
// first switch on wireframe
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
// setup some renderstate ..
glDepthMask(0);
ogl_tex_bind(0, 0);
glColor4f(1,1,1,0.35f);
glDisable(GL_TEXTURE_2D);
glColor3f(0.5f, 0.5f, 1.0f);
glLineWidth(2.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// render tiles edges
m->terrainRenderer->RenderPatches();
// set color for outline
glColor3f(0,0,1);
glColor3f(0, 0, 1);
glLineWidth(4.0f);
// render outline of each patch
m->terrainRenderer->RenderOutlines();
// .. and restore the renderstates
glDisable(GL_BLEND);
glDepthMask(1);
// restore fill mode, and we're done
glLineWidth(1.0f);
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
void CRenderer::RenderModels()
{
PROFILE( "render models ");
PROFILE("render models");
// switch on wireframe if we need it
if (m_ModelRenderMode==WIREFRAME) {
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
if (m_ModelRenderMode == WIREFRAME)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
m->CallModelRenderers(m->Model.ModNormal, m->Model.ModPlayer, 0);
m->CallModelRenderers(m->Model.ModNormal, m->Model.ModNormalInstancing,
m->Model.ModPlayer, m->Model.ModPlayerInstancing, 0);
if (m_ModelRenderMode==WIREFRAME) {
// switch wireframe off again
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
} else if (m_ModelRenderMode==EDGED_FACES) {
m->CallModelRenderers(m->Model.ModWireframe, m->Model.ModWireframe, 0);
if (m_ModelRenderMode == WIREFRAME)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
else if (m_ModelRenderMode == EDGED_FACES)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 0.0f);
m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing,
m->Model.ModSolid, m->Model.ModSolidInstancing, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
void CRenderer::RenderTransparentModels()
{
PROFILE( "render transparent models ");
PROFILE("render transparent models");
// switch on wireframe if we need it
if (m_ModelRenderMode==WIREFRAME) {
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
if (m_ModelRenderMode == WIREFRAME)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
m->Model.Transp->Render(m->Model.ModTransparent, 0);
if (m_ModelRenderMode==WIREFRAME) {
if (m_ModelRenderMode == WIREFRAME)
{
// switch wireframe off again
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
} else if (m_ModelRenderMode==EDGED_FACES) {
m->Model.Transp->Render(m->Model.ModWireframe, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
else if (m_ModelRenderMode == EDGED_FACES)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_TEXTURE_2D);
glColor3f(1.0f, 0.0f, 0.0f);
m->Model.Transp->Render(m->Model.ModSolid, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
@ -1229,13 +1464,24 @@ void CRenderer::RenderSilhouettes()
{
PROFILE("render model occluders");
m->CallModelRenderers(m->Model.ModSolidColor, m->Model.ModSolidColor, MODELFLAG_SILHOUETTE_OCCLUDER);
m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing,
m->Model.ModSolid, m->Model.ModSolidInstancing, MODELFLAG_SILHOUETTE_OCCLUDER);
}
{
PROFILE("render transparent occluders");
// Reuse the depth shadow modifier to get alpha-tested rendering
m->Model.Transp->Render(m->Model.ModTransparentDepthShadow, MODELFLAG_SILHOUETTE_OCCLUDER);
if (GetRenderPath() == RP_SHADER)
{
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.4f);
m->Model.Transp->Render(m->Model.ModShaderSolidTex, MODELFLAG_SILHOUETTE_OCCLUDER);
glDisable(GL_ALPHA_TEST);
}
else
{
// Reuse the depth shadow modifier to get alpha-tested rendering
m->Model.Transp->Render(m->Model.ModTransparentDepthShadow, MODELFLAG_SILHOUETTE_OCCLUDER);
}
}
glDepthFunc(GL_GEQUAL);
@ -1269,7 +1515,8 @@ void CRenderer::RenderSilhouettes()
{
PROFILE("render models");
m->CallModelRenderers(m->Model.ModSolidPlayerColor, m->Model.ModSolidPlayerColor, MODELFLAG_SILHOUETTE_DISPLAY);
m->CallModelRenderers(m->Model.ModSolidPlayer, m->Model.ModSolidPlayerInstancing,
m->Model.ModSolidPlayer, m->Model.ModSolidPlayerInstancing, MODELFLAG_SILHOUETTE_DISPLAY);
// (This won't render transparent objects with SILHOUETTE_DISPLAY - will
// we have any units that need that?)
}
@ -1738,6 +1985,11 @@ LibError CRenderer::ReloadChangedFileCB(void* param, const VfsPath& path)
return INFO::OK;
}
void CRenderer::MakeShadersDirty()
{
m->ShadersDirty = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Scripting Interface
@ -1801,6 +2053,19 @@ void CRenderer::JSI_SetDepthTextureBits(JSContext* ctx, jsval newval)
m->shadow->SetDepthTextureBits(depthTextureBits);
}
jsval CRenderer::JSI_GetShadows(JSContext*)
{
return ToJSVal(m_Options.m_Shadows);
}
void CRenderer::JSI_SetShadows(JSContext* ctx, jsval newval)
{
if (!ToPrimitive(ctx, newval, m_Options.m_Shadows))
return;
ReloadShaders();
}
jsval CRenderer::JSI_GetShadowAlphaFix(JSContext*)
{
return ToJSVal(m_Options.m_ShadowAlphaFix);
@ -1836,6 +2101,7 @@ void CRenderer::ScriptingInit()
AddProperty(L"shadowZBias", &CRenderer::m_ShadowZBias);
AddProperty(L"shadowMapSize", &CRenderer::m_ShadowMapSize);
AddProperty(L"disableCopyShadow", &CRenderer::m_DisableCopyShadow);
AddProperty(L"shadows", &CRenderer::JSI_GetShadows, &CRenderer::JSI_SetShadows);
AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits);
AddProperty(L"shadowAlphaFix", &CRenderer::JSI_GetShadowAlphaFix, &CRenderer::JSI_SetShadowAlphaFix);
AddProperty(L"skipSubmit", &CRenderer::m_SkipSubmit);
@ -1849,3 +2115,8 @@ CTextureManager& CRenderer::GetTextureManager()
{
return m->textureManager;
}
CShaderManager& CRenderer::GetShaderManager()
{
return m->shaderManager;
}

View File

@ -25,6 +25,7 @@
#include "graphics/Camera.h"
#include "graphics/SColor.h"
#include "graphics/ShaderProgram.h"
#include "lib/ogl.h"
#include "lib/res/handle.h"
#include "ps/Singleton.h"
@ -42,6 +43,7 @@ class RenderPathVertexShader;
class WaterManager;
class SkyManager;
class CTextureManager;
class CShaderManager;
// rendering modes
enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES };
@ -60,24 +62,6 @@ enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES };
#define STREAM_POSTOUV3 (1 << 10)
#define STREAM_TEXGENTOUV1 (1 << 11)
//////////////////////////////////////////////////////////////////////////////////////////
// SVertex3D: simple 3D vertex declaration
struct SVertex3D
{
float m_Position[3];
float m_TexCoords[2];
unsigned int m_Color;
};
//////////////////////////////////////////////////////////////////////////////////////////
// SVertex2D: simple 2D vertex declaration
struct SVertex2D
{
float m_Position[2];
float m_TexCoords[2];
unsigned int m_Color;
};
// access to sole renderer object
#define g_Renderer CRenderer::GetSingleton()
@ -110,7 +94,10 @@ public:
RP_FIXED,
// Use (GL 2.0) vertex shaders for T&L when possible.
RP_VERTEXSHADER
RP_VERTEXSHADER,
// Use new ARB/GLSL system
RP_SHADER
};
// stats class - per frame counts of number of draw calls, poly counts etc
@ -147,10 +134,13 @@ public:
float m_LodBias;
RenderPath m_RenderPath;
bool m_ShadowAlphaFix;
bool m_ARBProgramShadow;
} m_Options;
struct Caps {
bool m_VBO;
bool m_ARBProgram;
bool m_ARBProgramShadow;
bool m_VertexShader;
bool m_FragmentShader;
bool m_Shadows;
@ -193,6 +183,9 @@ public:
// set color used to clear screen in BeginFrame()
void SetClearColor(SColor4ub color);
// trigger a reload of shaders (when parameters they depend on have changed)
void MakeShadersDirty();
/**
* Set up the camera used for rendering the next scene; this includes
* setting OpenGL state like viewport, projection and modelview matrices.
@ -277,6 +270,8 @@ public:
CTextureManager& GetTextureManager();
CShaderManager& GetShaderManager();
/**
* SetFastPlayerColor: Tell the renderer which path to take for
* player colored models. Both paths should provide the same visual
@ -310,7 +305,9 @@ protected:
friend class SortModelRenderer;
friend class RenderPathVertexShader;
friend class HWLightingModelRenderer;
friend class ShaderModelRenderer;
friend class InstancingModelRenderer;
friend class ShaderInstancingModelRenderer;
friend class TerrainRenderer;
// scripting
@ -324,6 +321,8 @@ protected:
void JSI_SetUseDepthTexture(JSContext* ctx, jsval newval);
jsval JSI_GetDepthTextureBits(JSContext*);
void JSI_SetDepthTextureBits(JSContext* ctx, jsval newval);
jsval JSI_GetShadows(JSContext*);
void JSI_SetShadows(JSContext* ctx, jsval newval);
jsval JSI_GetShadowAlphaFix(JSContext*);
void JSI_SetShadowAlphaFix(JSContext* ctx, jsval newval);
jsval JSI_GetSky(JSContext*);
@ -362,6 +361,8 @@ protected:
// enable oblique frustum clipping with the given clip plane
void SetObliqueFrustumClipping(const CVector4D& clipPlane, int sign);
void ReloadShaders();
// hotloading
static LibError ReloadChangedFileCB(void* param, const VfsPath& path);

View File

@ -30,6 +30,7 @@
#include "graphics/Terrain.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/ShaderManager.h"
#include "maths/MathUtil.h"
@ -225,6 +226,9 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
// no need to write to the depth buffer a second time
glDepthMask(0);
// The decal color array contains lighting data, which we don't want in this non-shader mode
glDisableClientState(GL_COLOR_ARRAY);
// render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(m->visiblePatches);
@ -497,6 +501,134 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
}
///////////////////////////////////////////////////////////////////
/**
* Set up all the uniforms for a shader pass.
*/
void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow)
{
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
if (shadow)
{
shader->BindTexture("shadowTex", shadow->GetTexture());
shader->Uniform("shadowTransform", shadow->GetTextureMatrix());
}
CLOSTexture& los = g_Game->GetView()->GetLOSTexture();
shader->BindTexture("losTex", los.GetTexture());
shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
shader->Uniform("ambient", lightEnv.m_TerrainAmbientColor);
shader->Uniform("sunColor", lightEnv.m_SunColor);
}
void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
{
debug_assert(m->phase == Phase_Render);
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
typedef std::map<CStr, CStr> Defines;
Defines defBasic;
if (shadow)
{
defBasic["USE_SHADOW"] = "1";
if (g_Renderer.m_Caps.m_ARBProgramShadow && g_Renderer.m_Options.m_ARBProgramShadow)
defBasic["USE_FP_SHADOW"] = "1";
}
defBasic["LIGHTING_MODEL_" + g_Renderer.GetLightEnv().GetLightingModel()] = "1";
CShaderProgramPtr shaderBase(shaderManager.LoadProgram("terrain_base", defBasic));
CShaderProgramPtr shaderBlend(shaderManager.LoadProgram("terrain_blend", defBasic));
CShaderProgramPtr shaderDecal(shaderManager.LoadProgram("terrain_decal", defBasic));
// render the solid black sides of the map first
g_Renderer.BindTexture(0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(0, 0, 0);
PROFILE_START("render terrain sides");
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
m->visiblePatches[i]->RenderSides();
PROFILE_END("render terrain sides");
// switch on required client states
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours
shaderBase->Bind();
PrepareShader(shaderBase, shadow);
PROFILE_START("render terrain base");
CPatchRData::RenderBases(m->visiblePatches);
PROFILE_END("render terrain base");
shaderBase->Unbind();
// render blends
shaderBlend->Bind();
PrepareShader(shaderBlend, shadow);
// switch on the composite alpha map texture
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
// switch on second uv set
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// switch on blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// no need to write to the depth buffer a second time
glDepthMask(0);
// render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(m->visiblePatches);
PROFILE_END("render terrain blends");
// Disable second texcoord array
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
shaderBlend->Unbind();
// Render terrain decals
shaderDecal->Bind();
PrepareShader(shaderDecal, shadow);
g_Renderer.BindTexture(1, 0);
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
PROFILE_START("render terrain decals");
for (size_t i = 0; i < m->visibleDecals.size(); ++i)
m->visibleDecals[i]->Render();
PROFILE_END("render terrain decals");
shaderDecal->Unbind();
// restore OpenGL state
g_Renderer.BindTexture(1, 0);
g_Renderer.BindTexture(2, 0);
g_Renderer.BindTexture(3, 0);
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
glDepthMask(1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches()

View File

@ -82,6 +82,12 @@ public:
*/
void RenderTerrain(ShadowMap* shadow);
/**
* Render textured terrain, as with RenderTerrain, but using shaders
* instead of multitexturing.
*/
void RenderTerrainShader(ShadowMap* shadow);
/**
* RenderPatches: Render all patches un-textured as polygons.
*
@ -114,6 +120,8 @@ public:
private:
TerrainRendererInternals* m;
void PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow);
};
#endif // INCLUDED_TERRAINRENDERER

View File

@ -35,6 +35,11 @@ public:
m_Script.CallVoid("AddPlayer", (int)ent);
}
virtual int32_t GetNumPlayers()
{
return m_Script.Call<int32_t>("GetNumPlayers");
}
virtual entity_id_t GetPlayerByID(int32_t id)
{
return m_Script.Call<entity_id_t>("GetPlayerByID", (int)id);

View File

@ -28,6 +28,8 @@ class ICmpPlayerManager : public IComponent
public:
virtual void AddPlayer(entity_id_t ent) = 0;
virtual int32_t GetNumPlayers() = 0;
virtual entity_id_t GetPlayerByID(int32_t id) = 0;
DECLARE_INTERFACE_TYPE(PlayerManager)

View File

@ -311,6 +311,9 @@ enum
ID_Screenshot,
ID_JavaScript,
ID_CameraReset,
ID_RenderPathFixed,
ID_RenderPathVertexShader,
ID_RenderPathShader,
ID_Toolbar // must be last in the list
};
@ -334,6 +337,9 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
EVT_MENU(ID_Screenshot, ScenarioEditor::OnScreenshot)
EVT_MENU(ID_JavaScript, ScenarioEditor::OnJavaScript)
EVT_MENU(ID_CameraReset, ScenarioEditor::OnCameraReset)
EVT_MENU(ID_RenderPathFixed, ScenarioEditor::OnRenderPath)
EVT_MENU(ID_RenderPathVertexShader, ScenarioEditor::OnRenderPath)
EVT_MENU(ID_RenderPathShader, ScenarioEditor::OnRenderPath)
EVT_IDLE(ScenarioEditor::OnIdle)
END_EVENT_TABLE()
@ -480,6 +486,12 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
menuMisc->Append(ID_Screenshot, _("&Screenshot"));
menuMisc->Append(ID_JavaScript, _("&JS console"));
menuMisc->Append(ID_CameraReset, _("&Reset camera"));
wxMenu *menuRP = new wxMenu;
menuMisc->AppendSubMenu(menuRP, _("Render &path"));
menuRP->Append(ID_RenderPathFixed, _("&Fixed function"));
menuRP->Append(ID_RenderPathVertexShader, _("&Vertex shader (old)"));
menuRP->Append(ID_RenderPathShader, _("&Shader (new)"));
}
m_FileHistory.Load(*wxConfigBase::Get());
@ -771,6 +783,22 @@ void ScenarioEditor::OnCameraReset(wxCommandEvent& WXUNUSED(event))
POST_MESSAGE(CameraReset, ());
}
void ScenarioEditor::OnRenderPath(wxCommandEvent& event)
{
switch (event.GetId())
{
case ID_RenderPathFixed:
POST_MESSAGE(SetViewParamS, (eRenderView::GAME, L"renderpath", L"fixed"));
break;
case ID_RenderPathVertexShader:
POST_MESSAGE(SetViewParamS, (eRenderView::GAME, L"renderpath", L"vertexshader"));
break;
case ID_RenderPathShader:
POST_MESSAGE(SetViewParamS, (eRenderView::GAME, L"renderpath", L"shader"));
break;
}
}
//////////////////////////////////////////////////////////////////////////
Position::Position(const wxPoint& pt)

View File

@ -51,6 +51,7 @@ public:
void OnMediaPlayer(wxCommandEvent& event);
void OnJavaScript(wxCommandEvent& event);
void OnCameraReset(wxCommandEvent& event);
void OnRenderPath(wxCommandEvent& event);
void OpenFile(const wxString& name);

View File

@ -227,6 +227,8 @@ EnvironmentSidebar::EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow*
sunSizer->Add(new VariableSliderBox(this, _("Sun elevation"), g_EnvironmentSettings.sunelevation, -M_PIf/2, M_PIf/2), wxSizerFlags().Expand());
sunSizer->Add(new VariableSliderBox(this, _("Sun overbrightness"), g_EnvironmentSettings.sunoverbrightness, 1.0f, 3.0f), wxSizerFlags().Expand());
sunSizer->Add(m_LightingModelList = new VariableListBox(this, _("Light model"), g_EnvironmentSettings.lightingmodel), wxSizerFlags().Expand());
m_MainSizer->Add(new LightControl(this, wxSize(150, 150), g_EnvironmentSettings));
m_MainSizer->Add(m_SkyList = new VariableListBox(this, _("Sky set"), g_EnvironmentSettings.skyset));
m_MainSizer->Add(new VariableColourBox(this, _("Sun colour"), g_EnvironmentSettings.suncolour));
@ -248,6 +250,13 @@ void EnvironmentSidebar::OnFirstDisplay()
qry_env.Post();
g_EnvironmentSettings = qry_env.settings;
std::vector<std::wstring> lightingModels;
lightingModels.push_back(L"old");
lightingModels.push_back(L"standard");
m_LightingModelList->SetChoices(lightingModels);
g_EnvironmentSettings.NotifyObservers();
// TODO: reupdate everything when loading a new map...
}

View File

@ -30,6 +30,7 @@ protected:
virtual void OnFirstDisplay();
private:
VariableListBox* m_LightingModelList;
VariableListBox* m_SkyList;
ObservableScopedConnection m_Conn;
};

View File

@ -62,6 +62,8 @@ sEnvironmentSettings GetSettings()
s.sunrotation = sunrotation;
s.sunelevation = g_LightEnv.GetElevation();
s.lightingmodel = CStr(g_LightEnv.GetLightingModel()).FromUTF8();
s.skyset = g_Renderer.GetSkyManager()->GetSkySet();
// RGBColor (CVector3D) colours
@ -103,6 +105,8 @@ void SetSettings(const sEnvironmentSettings& s)
g_LightEnv.SetRotation(s.sunrotation);
g_LightEnv.SetElevation(s.sunelevation);
g_LightEnv.SetLightingModel(CStrW(*s.lightingmodel).ToUTF8());
CStrW skySet = *s.skyset;
if (skySet.length() == 0)
skySet = L"default";

View File

@ -345,6 +345,10 @@ struct sEnvironmentSettings
// (struct Colour stores as normal u8, 0..255)
Shareable<float> sunoverbrightness; // range 1..3
// support different lighting models ("old" for the version compatible with old scenarios,
// "standard" for the new normal model that supports much brighter lighting)
Shareable<std::wstring> lightingmodel;
Shareable<std::wstring> skyset;
Shareable<Colour> suncolour;

View File

@ -260,6 +260,10 @@ void ViewGame::SetParam(const std::wstring& name, const std::wstring& value)
if (!cmpPathfinder.null())
cmpPathfinder->SetDebugOverlay(!value.empty());
}
else if (name == L"renderpath")
{
g_Renderer.SetRenderPath(g_Renderer.GetRenderPathByName(CStrW(value).ToUTF8()));
}
}
CCamera& ViewGame::GetCamera()