diff --git a/binaries/data/mods/public/maps/scenarios/_default.xml b/binaries/data/mods/public/maps/scenarios/_default.xml
index b5f6afb3a0..47a883a9fb 100644
--- a/binaries/data/mods/public/maps/scenarios/_default.xml
+++ b/binaries/data/mods/public/maps/scenarios/_default.xml
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:11974496de6fda5d5c21bad61de1515be486120e4154b31d3bd12e4769f6fb98
-size 1371
+oid sha256:51dcf7313ce4101c47a26ed7979fa07defe737082e6aa04bcd48734598c8dc9d
+size 1428
diff --git a/binaries/data/mods/public/shaders/model_common.fp b/binaries/data/mods/public/shaders/model_common.fp
new file mode 100644
index 0000000000..ea6142f7d1
--- /dev/null
+++ b/binaries/data/mods/public/shaders/model_common.fp
@@ -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
diff --git a/binaries/data/mods/public/shaders/model_common.vp b/binaries/data/mods/public/shaders/model_common.vp
new file mode 100644
index 0000000000..797f5d1286
--- /dev/null
+++ b/binaries/data/mods/public/shaders/model_common.vp
@@ -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
diff --git a/binaries/data/mods/public/shaders/model_common.xml b/binaries/data/mods/public/shaders/model_common.xml
new file mode 100644
index 0000000000..69bee47d8e
--- /dev/null
+++ b/binaries/data/mods/public/shaders/model_common.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/model_common_instancing.xml b/binaries/data/mods/public/shaders/model_common_instancing.xml
new file mode 100644
index 0000000000..4702f02a45
--- /dev/null
+++ b/binaries/data/mods/public/shaders/model_common_instancing.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/program.rng b/binaries/data/mods/public/shaders/program.rng
new file mode 100644
index 0000000000..06efdd0ac4
--- /dev/null
+++ b/binaries/data/mods/public/shaders/program.rng
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+ arb
+ glsl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ pos
+ normal
+ color
+ uv0
+ uv1
+ uv2
+ uv3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ float
+ vec2
+ vec3
+ vec4
+ mat2
+ mat3
+ mat4
+ sampler2D
+ sampler2DShadow
+ samplerCube
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/solid.fp b/binaries/data/mods/public/shaders/solid.fp
new file mode 100644
index 0000000000..4c67baec5e
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid.fp
@@ -0,0 +1,3 @@
+!!ARBfp1.0
+MOV result.color, fragment.color;
+END
diff --git a/binaries/data/mods/public/shaders/solid.vp b/binaries/data/mods/public/shaders/solid.vp
new file mode 100644
index 0000000000..365d5d30af
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid.vp
@@ -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
diff --git a/binaries/data/mods/public/shaders/solid.xml b/binaries/data/mods/public/shaders/solid.xml
new file mode 100644
index 0000000000..53d668e50d
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/solid_color.fp b/binaries/data/mods/public/shaders/solid_color.fp
new file mode 100644
index 0000000000..14c43060d8
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid_color.fp
@@ -0,0 +1,3 @@
+!!ARBfp1.0
+MOV result.color, program.local[0];
+END
diff --git a/binaries/data/mods/public/shaders/solid_instancing.xml b/binaries/data/mods/public/shaders/solid_instancing.xml
new file mode 100644
index 0000000000..53f2f20bec
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid_instancing.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/solid_player.xml b/binaries/data/mods/public/shaders/solid_player.xml
new file mode 100644
index 0000000000..d2d772410d
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid_player.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/solid_player_instancing.xml b/binaries/data/mods/public/shaders/solid_player_instancing.xml
new file mode 100644
index 0000000000..64e50dd073
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid_player_instancing.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/solid_tex.fp b/binaries/data/mods/public/shaders/solid_tex.fp
new file mode 100644
index 0000000000..cc315b0280
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid_tex.fp
@@ -0,0 +1,3 @@
+!!ARBfp1.0
+TEX result.color, fragment.texcoord[0], texture[0], 2D;
+END
diff --git a/binaries/data/mods/public/shaders/solid_tex.vp b/binaries/data/mods/public/shaders/solid_tex.vp
new file mode 100644
index 0000000000..d252869620
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid_tex.vp
@@ -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
diff --git a/binaries/data/mods/public/shaders/solid_tex.xml b/binaries/data/mods/public/shaders/solid_tex.xml
new file mode 100644
index 0000000000..c62b3be0d5
--- /dev/null
+++ b/binaries/data/mods/public/shaders/solid_tex.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/terrain_base.xml b/binaries/data/mods/public/shaders/terrain_base.xml
new file mode 100644
index 0000000000..6d4caf1453
--- /dev/null
+++ b/binaries/data/mods/public/shaders/terrain_base.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/terrain_blend.xml b/binaries/data/mods/public/shaders/terrain_blend.xml
new file mode 100644
index 0000000000..f8e7d06b90
--- /dev/null
+++ b/binaries/data/mods/public/shaders/terrain_blend.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/terrain_common.fp b/binaries/data/mods/public/shaders/terrain_common.fp
new file mode 100644
index 0000000000..0b1232493e
--- /dev/null
+++ b/binaries/data/mods/public/shaders/terrain_common.fp
@@ -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
diff --git a/binaries/data/mods/public/shaders/terrain_common.vp b/binaries/data/mods/public/shaders/terrain_common.vp
new file mode 100644
index 0000000000..5117637af9
--- /dev/null
+++ b/binaries/data/mods/public/shaders/terrain_common.vp
@@ -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
diff --git a/binaries/data/mods/public/shaders/terrain_decal.xml b/binaries/data/mods/public/shaders/terrain_decal.xml
new file mode 100644
index 0000000000..ed234d5d18
--- /dev/null
+++ b/binaries/data/mods/public/shaders/terrain_decal.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp
index f1f2f6b596..4a9ff19fe4 100644
--- a/source/graphics/GameView.cpp
+++ b/source/graphics/GameView.cpp
@@ -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();
diff --git a/source/graphics/GameView.h b/source/graphics/GameView.h
index ba87e1cfbd..b70af3b8a6 100644
--- a/source/graphics/GameView.h
+++ b/source/graphics/GameView.h
@@ -78,7 +78,7 @@ public:
// *presentation*
void Update(float DeltaTime);
- // Render: Render the World
+ void BeginFrame();
void Render();
InReaction HandleEvent(const SDL_Event_* ev);
diff --git a/source/graphics/LOSTexture.cpp b/source/graphics/LOSTexture.cpp
index 8e44a00bdd..6d8a2ca54c 100644
--- a/source/graphics/LOSTexture.cpp
+++ b/source/graphics/LOSTexture.cpp
@@ -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);
diff --git a/source/graphics/LOSTexture.h b/source/graphics/LOSTexture.h
index 833f86e6a8..6a6f147dba 100644
--- a/source/graphics/LOSTexture.h
+++ b/source/graphics/LOSTexture.h
@@ -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.
diff --git a/source/graphics/LightEnv.cpp b/source/graphics/LightEnv.cpp
index 00e9a71cc1..1ce3f7049f 100644
--- a/source/graphics/LightEnv.cpp
+++ b/source/graphics/LightEnv.cpp
@@ -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)
diff --git a/source/graphics/LightEnv.h b/source/graphics/LightEnv.h
index dd23ed3709..4f8e304745 100644
--- a/source/graphics/LightEnv.h
+++ b/source/graphics/LightEnv.h
@@ -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;
diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp
index 40119fe39c..a8b7678f26 100644
--- a/source/graphics/MapReader.cpp
+++ b/source/graphics/MapReader.cpp
@@ -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);
diff --git a/source/graphics/MapWriter.cpp b/source/graphics/MapWriter.cpp
index 8f70ed69d1..3303a85964 100644
--- a/source/graphics/MapWriter.cpp
+++ b/source/graphics/MapWriter.cpp
@@ -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");
diff --git a/source/graphics/Material.cpp b/source/graphics/Material.cpp
index e30b3d55c2..271163605f 100644
--- a/source/graphics/Material.cpp
+++ b/source/graphics/Material.cpp
@@ -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();
}
diff --git a/source/graphics/Material.h b/source/graphics/Material.h
index 986debb0b0..adf5926a90 100644
--- a/source/graphics/Material.h
+++ b/source/graphics/Material.h
@@ -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
{
diff --git a/source/graphics/ShaderManager.cpp b/source/graphics/ShaderManager.cpp
new file mode 100644
index 0000000000..fb4c46d640
--- /dev/null
+++ b/source/graphics/ShaderManager.cpp
@@ -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 .
+ */
+
+#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& defines)
+{
+ CacheKey key = { name, defines };
+ std::map::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& 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 defines = baseDefines;
+ std::map vertexUniforms;
+ std::map 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(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 >::iterator it = files->second.begin(); it != files->second.end(); ++it)
+ {
+ if (shared_ptr program = it->lock())
+ program->Reload();
+ }
+ }
+
+ return INFO::OK;
+}
diff --git a/source/graphics/ShaderManager.h b/source/graphics/ShaderManager.h
new file mode 100644
index 0000000000..70e7cf3a01
--- /dev/null
+++ b/source/graphics/ShaderManager.h
@@ -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 .
+ */
+
+#ifndef INCLUDED_SHADERMANAGER
+#define INCLUDED_SHADERMANAGER
+
+#define USE_SHADER_XML_VALIDATION 1
+
+#include
+#include
+
+#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& defines);
+
+private:
+ bool NewProgram(const char* name, const std::map& defines, CShaderProgramPtr& program);
+
+ static LibError ReloadChangedFileCB(void* param, const VfsPath& path);
+ LibError ReloadChangedFile(const VfsPath& path);
+
+ struct CacheKey
+ {
+ std::string name;
+ std::map 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 m_Cache;
+
+ // Store the set of shaders that need to be reloaded when the given file is modified
+ typedef boost::unordered_map > > HotloadFilesMap;
+ HotloadFilesMap m_HotloadFiles;
+
+#if USE_SHADER_XML_VALIDATION
+ RelaxNGValidator m_Validator;
+#endif
+};
+
+#endif // INCLUDED_SHADERMANAGER
diff --git a/source/graphics/ShaderProgram.cpp b/source/graphics/ShaderProgram.cpp
new file mode 100644
index 0000000000..2ba69d8712
--- /dev/null
+++ b/source/graphics/ShaderProgram.cpp
@@ -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 .
+ */
+
+#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& defines,
+ const std::map& vertexIndexes, const std::map& 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::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::iterator it = m_VertexIndexes.find(id);
+ if (it == m_VertexIndexes.end())
+ return -1;
+ return it->second;
+ }
+
+ int GetUniformFragmentIndex(uniform_id_t id)
+ {
+ std::map::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 m_Defines;
+
+ GLuint m_VertexProgram;
+ GLuint m_FragmentProgram;
+
+ std::map m_VertexIndexes;
+ std::map 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& defines,
+ const std::map& vertexIndexes, const std::map& 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;
+}
diff --git a/source/graphics/ShaderProgram.h b/source/graphics/ShaderProgram.h
new file mode 100644
index 0000000000..5857bf9fef
--- /dev/null
+++ b/source/graphics/ShaderProgram.h
@@ -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 .
+ */
+
+#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& defines,
+ const std::map& vertexIndexes, const std::map& 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 CShaderProgramPtr;
+
+#endif // INCLUDED_SHADERPROGRAM
diff --git a/source/graphics/ShaderTechnique.cpp b/source/graphics/ShaderTechnique.cpp
new file mode 100644
index 0000000000..683036c157
--- /dev/null
+++ b/source/graphics/ShaderTechnique.cpp
@@ -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 .
+ */
+
+#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();
+
+}
diff --git a/source/graphics/ShaderTechnique.h b/source/graphics/ShaderTechnique.h
new file mode 100644
index 0000000000..2176f988c0
--- /dev/null
+++ b/source/graphics/ShaderTechnique.h
@@ -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 .
+ */
+
+#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 m_Passes;
+};
+
+#endif // INCLUDED_SHADERTECHNIQUE
diff --git a/source/graphics/TextureManager.cpp b/source/graphics/TextureManager.cpp
index b03e65cd1c..bd3a1010a3 100644
--- a/source/graphics/TextureManager.cpp
+++ b/source/graphics/TextureManager.cpp
@@ -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()
diff --git a/source/graphics/TextureManager.h b/source/graphics/TextureManager.h
index 2f8651a18e..897aaf94ec 100644
--- a/source/graphics/TextureManager.h
+++ b/source/graphics/TextureManager.h
@@ -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.
diff --git a/source/lib/glext_funcs.h b/source/lib/glext_funcs.h
index 30097bdfb0..9e37cdb164 100644
--- a/source/lib/glext_funcs.h
+++ b/source/lib/glext_funcs.h
@@ -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.
diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp
index 769522ca83..6f5d5a9051 100644
--- a/source/ps/Game.cpp
+++ b/source/ps/Game.cpp
@@ -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 cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
if (cmpPlayerManager.null())
- return BrokenColor;
- CmpPtr 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 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];
}
diff --git a/source/ps/Game.h b/source/ps/Game.h
index 082787b0a4..d8897a12e7 100644
--- a/source/ps/Game.h
+++ b/source/ps/Game.h
@@ -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 m_PlayerColours;
};
extern CGame *g_Game;
diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp
index 75e436a17b..c948bf5e6a 100644
--- a/source/ps/GameSetup/GameSetup.cpp
+++ b/source/ps/GameSetup/GameSetup.cpp
@@ -199,15 +199,17 @@ void Render()
GUI::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();
diff --git a/source/ps/XML/RelaxNG.cpp b/source/ps/XML/RelaxNG.cpp
index 5173b75662..2088ea336e 100644
--- a/source/ps/XML/RelaxNG.cpp
+++ b/source/ps/XML/RelaxNG.cpp
@@ -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 = "" + 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 = "" + 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");
diff --git a/source/ps/XML/RelaxNG.h b/source/ps/XML/RelaxNG.h
index b33b34b4e2..18758192b2 100644
--- a/source/ps/XML/RelaxNG.h
+++ b/source/ps/XML/RelaxNG.h
@@ -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;
};
diff --git a/source/ps/XML/XMLWriter.cpp b/source/ps/XML/XMLWriter.cpp
index 0947a50a8b..8182b7f608 100644
--- a/source/ps/XML/XMLWriter.cpp
+++ b/source/ps/XML/XMLWriter.cpp
@@ -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, "!-- ");
diff --git a/source/ps/XML/XMLWriter.h b/source/ps/XML/XMLWriter.h
index 55ad4530fe..b07f9a58b7 100644
--- a/source/ps/XML/XMLWriter.h
+++ b/source/ps/XML/XMLWriter.h
@@ -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': value
#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 void ElementAttribute(const char* name, const T& value, bool newelement);
diff --git a/source/renderer/DecalRData.cpp b/source/renderer/DecalRData.cpp
index dde2d1066f..16a34b45eb 100644
--- a/source/renderer/DecalRData.cpp
+++ b/source/renderer/DecalRData.cpp
@@ -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 Position = m_Position.GetIterator();
+ VertexArrayIterator DiffuseColor = m_DiffuseColor.GetIterator();
VertexArrayIterator UV = m_UV.GetIterator();
+ 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;
diff --git a/source/renderer/DecalRData.h b/source/renderer/DecalRData.h
index e0dd13cb9f..347509c4af 100644
--- a/source/renderer/DecalRData.h
+++ b/source/renderer/DecalRData.h
@@ -40,6 +40,7 @@ private:
VertexArray m_Array;
VertexArray::Attribute m_Position;
+ VertexArray::Attribute m_DiffuseColor;
VertexArray::Attribute m_UV;
CModelDecal* m_Decal;
diff --git a/source/renderer/HWLightingModelRenderer.cpp b/source/renderer/HWLightingModelRenderer.cpp
index 953f1013a1..fc26945ab8 100644
--- a/source/renderer/HWLightingModelRenderer.cpp
+++ b/source/renderer/HWLightingModelRenderer.cpp
@@ -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;
+}
diff --git a/source/renderer/HWLightingModelRenderer.h b/source/renderer/HWLightingModelRenderer.h
index c313e36417..84e1d19dee 100644
--- a/source/renderer/HWLightingModelRenderer.h
+++ b/source/renderer/HWLightingModelRenderer.h
@@ -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
diff --git a/source/renderer/InstancingModelRenderer.cpp b/source/renderer/InstancingModelRenderer.cpp
index cdacc7102c..bb40cd15af 100644
--- a/source/renderer/InstancingModelRenderer.cpp
+++ b/source/renderer/InstancingModelRenderer.cpp
@@ -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;
+
+}
diff --git a/source/renderer/InstancingModelRenderer.h b/source/renderer/InstancingModelRenderer.h
index bc850b153d..34e540a079 100644
--- a/source/renderer/InstancingModelRenderer.h
+++ b/source/renderer/InstancingModelRenderer.h
@@ -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
diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp
index fe65a8a712..b7e56144e1 100644
--- a/source/renderer/PatchRData.cpp
+++ b/source/renderer/PatchRData.cpp
@@ -358,28 +358,41 @@ void CPatchRData::AddBlend(std::vector& 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;jCalcNormal(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& 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]);
diff --git a/source/renderer/PatchRData.h b/source/renderer/PatchRData.h
index da0eece5c1..7b8e381024 100644
--- a/source/renderer/PatchRData.h
+++ b/source/renderer/PatchRData.h
@@ -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);
diff --git a/source/renderer/RenderModifiers.cpp b/source/renderer/RenderModifiers.cpp
index 4d541e9457..f4df4033fe 100644
--- a/source/renderer/RenderModifiers.cpp
+++ b/source/renderer/RenderModifiers.cpp
@@ -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
///////////////////////////////////////////////////////////////////////////////////////////////
// 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());
+}
diff --git a/source/renderer/RenderModifiers.h b/source/renderer/RenderModifiers.h
index 1e94294c37..fe5fa5014e 100644
--- a/source/renderer/RenderModifiers.h
+++ b/source/renderer/RenderModifiers.h
@@ -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
diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp
index 43033b87e7..013d474bd7 100644
--- a/source/renderer/Renderer.cpp
+++ b/source/renderer/Renderer.cpp
@@ -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 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;
+}
diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h
index 847b0abfe4..7699b2c985 100644
--- a/source/renderer/Renderer.h
+++ b/source/renderer/Renderer.h
@@ -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);
diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp
index 3816313806..239d7349bb 100644
--- a/source/renderer/TerrainRenderer.cpp
+++ b/source/renderer/TerrainRenderer.cpp
@@ -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 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()
diff --git a/source/renderer/TerrainRenderer.h b/source/renderer/TerrainRenderer.h
index 9db03087fe..6d6592dc3c 100644
--- a/source/renderer/TerrainRenderer.h
+++ b/source/renderer/TerrainRenderer.h
@@ -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
diff --git a/source/simulation2/components/ICmpPlayerManager.cpp b/source/simulation2/components/ICmpPlayerManager.cpp
index f13156e481..944cbe8ebc 100644
--- a/source/simulation2/components/ICmpPlayerManager.cpp
+++ b/source/simulation2/components/ICmpPlayerManager.cpp
@@ -35,6 +35,11 @@ public:
m_Script.CallVoid("AddPlayer", (int)ent);
}
+ virtual int32_t GetNumPlayers()
+ {
+ return m_Script.Call("GetNumPlayers");
+ }
+
virtual entity_id_t GetPlayerByID(int32_t id)
{
return m_Script.Call("GetPlayerByID", (int)id);
diff --git a/source/simulation2/components/ICmpPlayerManager.h b/source/simulation2/components/ICmpPlayerManager.h
index 7fa778ac1f..207ad8a68e 100644
--- a/source/simulation2/components/ICmpPlayerManager.h
+++ b/source/simulation2/components/ICmpPlayerManager.h
@@ -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)
diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp
index 97c2d0cac0..0b27cf88ed 100644
--- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp
+++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp
@@ -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)
diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h
index 21a9aefc76..42950f5c1b 100644
--- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h
+++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h
@@ -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);
diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
index 22c3750c89..a975fd6df3 100644
--- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
+++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
@@ -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 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...
}
diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h
index 738214e60a..9aed4a9e1a 100644
--- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h
+++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h
@@ -30,6 +30,7 @@ protected:
virtual void OnFirstDisplay();
private:
+ VariableListBox* m_LightingModelList;
VariableListBox* m_SkyList;
ObservableScopedConnection m_Conn;
};
diff --git a/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
index 1bc5b973fd..496426b113 100644
--- a/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
+++ b/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
@@ -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";
diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h
index fbf2c6f81d..4d293bc697 100644
--- a/source/tools/atlas/GameInterface/Messages.h
+++ b/source/tools/atlas/GameInterface/Messages.h
@@ -345,6 +345,10 @@ struct sEnvironmentSettings
// (struct Colour stores as normal u8, 0..255)
Shareable 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 lightingmodel;
+
Shareable skyset;
Shareable suncolour;
diff --git a/source/tools/atlas/GameInterface/View.cpp b/source/tools/atlas/GameInterface/View.cpp
index 7508312ddc..a033e686ec 100644
--- a/source/tools/atlas/GameInterface/View.cpp
+++ b/source/tools/atlas/GameInterface/View.cpp
@@ -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()