Update renderer design to be more flexible and data-driven based on material and shader definitions.

Support conditional expressions in shader effect XML files.
Consolidate fixed-function model rendering into the shader system.
Remove lots of now-obsolete renderer code.
Move shader defines from std::map to new class with interned data, for
performance.
Move texture from model into material.
Alleviate singletonitis.
Remove obsolete lodbias setting.
Remove unused terrain shadow transparency.

This was SVN commit r11423.
This commit is contained in:
Ykkrosh 2012-04-03 18:44:46 +00:00
parent a56169ff8c
commit 6bc33fe8bd
74 changed files with 2100 additions and 3507 deletions

View File

@ -58,11 +58,6 @@ renderpath = default
; Prefer GLSL shaders over ARB shaders (not recommended)
preferglsl = false
; Adjusts how OpenGL calculates mipmap level of detail. 0.0f is the default (blurry) value.
; Lower values sharpen/extend, and higher values blur/decrease. Clamped at -3.0 to 3.0.
; -1.0 to -1.5 recommended for good results.
lodbias = 0
; Opt-in online user reporting system
userreport.url = "http://feedback.wildfiregames.com/report/upload/v1/"

View File

@ -1,5 +1,6 @@
<Material>
<Alpha
usage="blend"
/>
</Material>
<?xml version="1.0" encoding="utf-8"?>
<material>
<alpha_blending/>
<shader effect="model_alphablend"/>
<define name="USE_TRANSPARENT" value="1"/>
</material>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<material>
<shader effect="model"/>
</material>

View File

@ -1,5 +1,5 @@
<Material>
<Alpha
usage="objectcolor"
/>
</Material>
<?xml version="1.0" encoding="utf-8"?>
<material>
<shader effect="model"/>
<define name="USE_OBJECTCOLOR" value="1"/>
</material>

View File

@ -1,5 +1,5 @@
<Material>
<Alpha
usage="playercolor"
/>
</Material>
<?xml version="1.0" encoding="utf-8"?>
<material>
<shader effect="model"/>
<define name="USE_PLAYERCOLOR" value="1"/>
</material>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/>
<require shaders="arb"/>
<pass shader="solid"/>
</technique>
<technique>
<require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/>
<require shaders="glsl"/>
<pass shader="glsl/model_solid"/>
</technique>
<technique>
<require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/>
<require shaders="fixed"/>
<define name="USE_PLAYERCOLOR" value="0"/>
<define name="USE_OBJECTCOLOR" value="0"/>
<pass shader="fixed:model_solid"/>
</technique>
<technique>
<require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/>
<require shaders="arb"/>
<pass shader="solid_player"/>
</technique>
<technique>
<require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/>
<require shaders="glsl"/>
<pass shader="glsl/model_solid_player"/>
</technique>
<technique>
<require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/>
<require shaders="fixed"/>
<define name="USE_PLAYERCOLOR" value="1"/>
<define name="USE_OBJECTCOLOR" value="0"/>
<pass shader="fixed:model_solid"/>
</technique>
<technique>
<require shaders="arb"/>
<pass shader="model_common_arb"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_common"/>
</technique>
<technique>
<require context="USE_PLAYERCOLOR || USE_OBJECTCOLOR"/>
<require shaders="fixed"/>
<pass shader="fixed:model_color"/>
</technique>
<technique>
<require shaders="fixed"/>
<pass shader="fixed:model"/>
</technique>
</effect>

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/>
<require shaders="arb"/>
<pass shader="solid_tex">
<alpha func="greater" ref="0.4"/>
</pass>
</technique>
<technique>
<require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/>
<require shaders="glsl"/>
<pass shader="glsl/model_solid_tex">
<define name="REQUIRE_ALPHA_GREATER" value="0.4"/>
</pass>
</technique>
<technique>
<require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/>
<require shaders="fixed"/>
<define name="USE_PLAYERCOLOR" value="0"/>
<define name="USE_OBJECTCOLOR" value="0"/>
<pass shader="fixed:model_solid_tex">
<alpha func="greater" ref="0.4"/>
</pass>
</technique>
<!--
CRenderer::RenderSilhouettes skips alpha-blended models for
MODE_SILHOUETTEDISPLAY, so do a dummy non-blended behaviour here to
make it obviously buggy if CRenderer is changed to render these models.
-->
<technique>
<require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/>
<require shaders="arb"/>
<pass shader="solid_player"/>
</technique>
<technique>
<require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/>
<require shaders="glsl"/>
<pass shader="glsl/model_solid_player"/>
</technique>
<technique>
<require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/>
<require shaders="fixed"/>
<define name="USE_PLAYERCOLOR" value="1"/>
<define name="USE_OBJECTCOLOR" value="0"/>
<pass shader="fixed:model_solid"/>
</technique>
<technique>
<require context="ALPHABLEND_PASS_OPAQUE"/>
<require shaders="arb"/>
<pass shader="model_common_arb">
<define name="USE_TRANSPARENT" value="1"/>
<alpha func="greater" ref="0.9375"/>
<!-- <blend src="src_alpha" dst="one_minus_src_alpha"/>-->
</pass>
</technique>
<technique>
<require context="ALPHABLEND_PASS_OPAQUE"/>
<require shaders="glsl"/>
<pass shader="glsl/model_common">
<define name="USE_TRANSPARENT" value="1"/>
<define name="REQUIRE_ALPHA_GREATER" value="0.9375"/>
<!-- <blend src="src_alpha" dst="one_minus_src_alpha"/>-->
</pass>
</technique>
<technique>
<require context="ALPHABLEND_PASS_OPAQUE"/>
<require shaders="fixed"/>
<pass shader="fixed:model">
<alpha func="greater" ref="0.9375"/>
<!-- <blend src="src_alpha" dst="one_minus_src_alpha"/>-->
</pass>
</technique>
<technique>
<require context="ALPHABLEND_PASS_BLEND"/>
<require shaders="arb"/>
<sort_by_distance/>
<pass shader="model_common_arb">
<define name="USE_TRANSPARENT" value="1"/>
<alpha func="greater" ref="0.0"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
<depth func="less" mask="false"/>
</pass>
</technique>
<technique>
<require context="ALPHABLEND_PASS_BLEND"/>
<require shaders="glsl"/>
<sort_by_distance/>
<pass shader="glsl/model_common">
<define name="USE_TRANSPARENT" value="1"/>
<define name="REQUIRE_ALPHA_GREATER" value="0.0"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
<depth func="less" mask="false"/>
</pass>
</technique>
<technique>
<require context="ALPHABLEND_PASS_BLEND"/>
<require shaders="fixed"/>
<sort_by_distance/>
<pass shader="fixed:model">
<define name="USE_TRANSPARENT" value="1"/>
<alpha func="greater" ref="0.0"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
<depth func="less" mask="false"/>
</pass>
</technique>
</effect>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="model_common_arb"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_common"/>
</technique>
</effect>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="model_common_instancing_arb"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_common_instancing"/>
</technique>
</effect>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="solid"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_solid"/>
</technique>
</effect>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="solid_instancing"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_solid">
<define name="USE_INSTANCING" value="1"/>
</pass>
</technique>
</effect>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="solid_player"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_solid_player"/>
</technique>
</effect>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="solid_player_instancing"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_solid_player">
<define name="USE_INSTANCING" value="1"/>
</pass>
</technique>
</effect>

View File

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="model_common_arb">
<define name="USE_TRANSPARENT" value="1"/>
<alpha func="greater" ref="0.9375"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
</pass>
<pass shader="model_common_arb">
<define name="USE_TRANSPARENT" value="1"/>
<alpha func="greater" ref="0.0"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
<depth func="less" mask="false"/>
</pass>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_common">
<define name="USE_TRANSPARENT" value="1"/>
<define name="REQUIRE_ALPHA_GREATER" value="0.9375"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
</pass>
<pass shader="glsl/model_common">
<define name="USE_TRANSPARENT" value="1"/>
<define name="REQUIRE_ALPHA_GREATER" value="0.0"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
<depth func="less" mask="false"/>
</pass>
</technique>
</effect>

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="model_common_arb">
<define name="USE_TRANSPARENT" value="1"/>
<alpha func="greater" ref="0.0"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
<depth func="less" mask="false"/>
</pass>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_common">
<define name="USE_TRANSPARENT" value="1"/>
<define name="REQUIRE_ALPHA_GREATER" value="0.0"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
<depth func="less" mask="false"/>
</pass>
</technique>
</effect>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="model_common_arb">
<define name="USE_TRANSPARENT" value="1"/>
<alpha func="greater" ref="0.9375"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
</pass>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_common">
<define name="USE_TRANSPARENT" value="1"/>
<define name="REQUIRE_ALPHA_GREATER" value="0.9375"/>
<blend src="src_alpha" dst="one_minus_src_alpha"/>
</pass>
</technique>
</effect>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="solid_tex">
<alpha func="greater" ref="0.4"/>
</pass>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="glsl/model_solid_tex">
<define name="REQUIRE_ALPHA_GREATER" value="0.4"/>
</pass>
</technique>
</effect>

View File

@ -11,7 +11,14 @@ uniform sampler2D losTex;
#endif
#endif
uniform vec3 objectColor;
#ifdef USE_OBJECTCOLOR
uniform vec3 objectColor;
#else
#ifdef USE_PLAYERCOLOR
uniform vec3 playerColor;
#endif
#endif
uniform vec3 shadingColor;
uniform vec3 ambient;
uniform vec4 shadowOffsets1;
@ -72,9 +79,13 @@ void main()
vec3 color = tex.rgb;
// Apply player-coloring based on texture alpha
// Apply-coloring based on texture alpha
#ifdef USE_OBJECTCOLOR
color *= mix(objectColor, vec3(1.0, 1.0, 1.0), tex.a);
#else
#ifdef USE_PLAYERCOLOR
color *= mix(playerColor, vec3(1.0, 1.0, 1.0), tex.a);
#endif
#endif
color *= v_lighting * get_shadow() + ambient;

View File

@ -30,10 +30,12 @@ void main()
float c = textureTransform.x;
float s = -textureTransform.y;
v_tex = vec2(a_vertex.x * c + a_vertex.z * -s, a_vertex.x * -s + a_vertex.z * -c);
#ifdef GL_ES
// XXX: Ugly hack to hide some precision issues in GLES
v_tex = mod(v_tex, vec2(9.0, 9.0));
#endif
#endif
#ifdef BLEND
v_blend = a_uv1;

View File

@ -15,6 +15,10 @@
#ifdef USE_OBJECTCOLOR
PARAM objectColor = program.local[0];
#else
#ifdef USE_PLAYERCOLOR
PARAM playerColor = program.local[0];
#endif
#endif
PARAM shadingColor = program.local[1];
@ -36,13 +40,18 @@ TEX tex, fragment.texcoord[0], texture[0], 2D;
MOV result.color.a, tex;
#endif
// Apply player-coloring based on texture alpha
// Apply coloring based on texture alpha
#ifdef USE_OBJECTCOLOR
LRP temp.rgb, objectColor, 1.0, tex.a;
MUL color.rgb, tex, temp;
#else
#ifdef USE_PLAYERCOLOR
LRP temp.rgb, playerColor, 1.0, tex.a;
MUL color.rgb, tex, temp;
#else
MOV color.rgb, tex;
#endif
#endif
// Compute color = texture * (ambient + diffuse*shadow)
// (diffuse is 2*fragment.color due to clamp-avoidance in the vertex program)

View File

@ -6,6 +6,7 @@
<uniform name="sunColor" loc="1" type="vec3"/>
<uniform name="losTransform" loc="2" type="vec2"/>
<uniform name="shadowTransform" loc="3" type="mat4"/>
<uniform if="USE_INSTANCING" name="instancingTransform" loc="7" type="mat4"/>
<stream name="pos"/>
<stream name="normal"/>
<stream name="uv0"/>
@ -16,7 +17,8 @@
<uniform name="shadowTex" loc="1" type="sampler2DShadow"/>
<uniform name="losTex" loc="2" type="sampler2D"/>
<uniform name="objectColor" loc="0" type="vec3"/>
<uniform if="USE_OBJECTCOLOR" name="objectColor" loc="0" type="vec3"/>
<uniform if="USE_PLAYERCOLOR" name="playerColor" loc="0" type="vec3"/>
<uniform name="shadingColor" loc="1" type="vec3"/>
<uniform name="ambient" loc="2" type="vec3"/>
<uniform name="shadowOffsets1" loc="3" type="vec4"/>

View File

@ -21,6 +21,7 @@
<choice>
<ref name="uniformContent"/>
<element name="attrib">
<ref name="conditional"/>
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
</element>
@ -47,6 +48,7 @@
<zeroOrMore>
<choice>
<element name="attrib">
<ref name="conditional"/>
<attribute name="name"><text/></attribute>
<attribute name="semantics">
<choice>
@ -83,6 +85,7 @@
<define name="uniformContent">
<element name="uniform">
<ref name="conditional"/>
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
<attribute name="type">
@ -104,6 +107,7 @@
<define name="streamContent">
<element name="stream">
<ref name="conditional"/>
<attribute name="name">
<choice>
<value>pos</value>
@ -118,4 +122,10 @@
</element>
</define>
<define name="conditional">
<optional>
<attribute name="if"><text/></attribute>
</optional>
</define>
</grammar>

View File

@ -2,6 +2,7 @@
<program type="arb">
<vertex file="solid.vp">
<uniform if="USE_INSTANCING" name="instancingTransform" loc="0" type="mat4"/>
<stream name="pos"/>
</vertex>

View File

@ -2,6 +2,7 @@
<program type="arb">
<vertex file="solid.vp">
<uniform if="USE_INSTANCING" name="instancingTransform" loc="0" type="mat4"/>
<stream name="pos"/>
</vertex>

View File

@ -2,6 +2,7 @@
<program type="arb">
<vertex file="solid_tex.vp">
<uniform if="USE_INSTANCING" name="instancingTransform" loc="0" type="mat4"/>
<stream name="pos"/>
<stream name="uv0"/>
</vertex>

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -29,7 +29,6 @@
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),
@ -50,16 +49,11 @@ void CLightEnv::SetRotation(float f)
CalculateSunDirection();
}
void CLightEnv::SetTerrainShadowTransparency(float f)
{
m_TerrainShadowTransparency = f;
}
void CLightEnv::CalculateSunDirection()
{
m_SunDir.Y=-float(sin(m_Elevation));
float scale=1+m_SunDir.Y;
m_SunDir.X=scale*float(sin(m_Rotation));
m_SunDir.Z=scale*float(cos(m_Rotation));
m_SunDir.Y = -sinf(m_Elevation);
float scale = 1 + m_SunDir.Y;
m_SunDir.X = scale * sinf(m_Rotation);
m_SunDir.Z = scale * cosf(m_Rotation);
m_SunDir.Normalize();
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -40,13 +40,13 @@ friend class CMapReader;
friend class CXMLReader;
private:
/**
* m_Elevation: Height of sun above the horizon, in radians.
* Height of sun above the horizon, in radians.
* For example, an elevation of M_PI/2 means the sun is straight up.
*/
float m_Elevation;
/**
* m_Rotation: Direction of sun on the compass, in radians.
* Direction of sun on the compass, in radians.
* For example, a rotation of zero means the sun is in the direction (0,0,-1)
* and a rotation of M_PI/2 means the sun is in the direction (1,0,0) (not taking
* elevation into account).
@ -54,13 +54,9 @@ private:
float m_Rotation;
/**
* 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.
* Vector corresponding to m_Elevation and m_Rotation.
* Updated by CalculateSunDirection.
*/
float m_TerrainShadowTransparency;
CVector3D m_SunDir;
/**
@ -80,89 +76,54 @@ public:
float GetElevation() const { return m_Elevation; }
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.
* Calculate brightness of a point of a unit with the given normal vector,
* for rendering with CPU lighting.
* The resulting color contains both ambient and diffuse light.
* To cope with sun overbrightness, the color is scaled by 0.5.
*
* @param normal normal vector (must have length 1)
* @param color resulting color
*/
void EvaluateTerrain(const CVector3D& normal, RGBColor& color) const
RGBColor EvaluateUnitScaled(const CVector3D& normal) const
{
float dot = -normal.Dot(m_SunDir);
color = m_TerrainAmbientColor;
RGBColor color = m_UnitsAmbientColor;
if (dot > 0)
color += m_SunColor * dot;
return color * 0.5f;
}
/**
* EvaluateUnit: Calculate brightness of a point of a unit with the given normal
* vector.
* The resulting color contains both ambient and diffuse light.
* Compute the diffuse sun lighting color on terrain, for rendering with CPU lighting.
* To cope with sun overbrightness, the color is scaled by 0.5.
*
* @param normal normal vector (must have length 1)
* @param color resulting color
*/
void EvaluateUnit(const CVector3D& normal, RGBColor& color) const
SColor4ub EvaluateTerrainDiffuseScaled(const CVector3D& normal) const
{
float dot = -normal.Dot(m_SunDir);
color = m_UnitsAmbientColor;
if (dot > 0)
color += m_SunColor * dot;
return ConvertRGBColorTo4ub(m_SunColor * dot * 0.5f);
}
/**
* EvaluateDirect: Like EvaluateTerrain and EvaluateUnit, but return only the direct
* sunlight term without ambient.
* Compute the diffuse sun lighting factor on terrain, for rendering with shader lighting.
*
* @param normal normal vector (must have length 1)
* @param color resulting color
*/
void EvaluateDirect(const CVector3D& normal, RGBColor& color) const
SColor4ub EvaluateTerrainDiffuseFactor(const CVector3D& normal) const
{
float dot = -normal.Dot(m_SunDir);
if (dot > 0)
color = m_SunColor * dot;
else
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);
}
int c = clamp((int)(dot * 255), 0, 255);
return SColor4ub(c, c, c, 255);
}
// Comparison operators
@ -170,7 +131,6 @@ public:
{
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 &&
@ -186,4 +146,4 @@ private:
void CalculateSunDirection();
};
#endif
#endif // INCLUDED_LIGHTENV

View File

@ -560,7 +560,6 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
EL(sunrotation);
EL(terrainambientcolour);
EL(unitsambientcolour);
EL(terrainshadowtransparency);
EL(water);
EL(waterbody);
EL(type);
@ -620,10 +619,6 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
attrs.GetNamedItem(at_g).ToFloat(),
attrs.GetNamedItem(at_b).ToFloat());
}
else if (element_name == el_terrainshadowtransparency)
{
m_MapReader.m_LightEnv.SetTerrainShadowTransparency(element.GetText().ToFloat());
}
else if (element_name == el_water)
{
XERO_ITER_EL(element, waterbody)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -19,66 +19,31 @@
#include "Material.h"
#include "lib/ogl.h"
#include "ps/Game.h"
#include "ps/Overlay.h" // for CColor
CMaterial NullMaterial;
static SMaterialColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
CMaterial::CMaterial() :
m_Alpha(false),
m_AlphaBlending(false),
m_PlayerID(INVALID_PLAYER),
m_TextureColor(BrokenColor),
m_UsePlayerColor(false),
m_UseTextureColor(false)
m_ObjectColor(BrokenColor)
{
}
SMaterialColor CMaterial::GetObjectColor()
void CMaterial::SetObjectColor(const CColor& colour)
{
if (m_UseTextureColor)
return m_TextureColor;
return GetPlayerColor();
m_ObjectColor = colour;
}
SMaterialColor CMaterial::GetPlayerColor()
void CMaterial::SetDiffuseTexture(const CTexturePtr& texture)
{
if (m_PlayerID == -1)
return BrokenColor;
CColor c(g_Game->GetPlayerColour(m_PlayerID));
return SMaterialColor(c.r, c.g, c.b, c.a);
m_DiffuseTexture = texture;
}
void CMaterial::SetPlayerID(player_id_t id)
void CMaterial::SetShaderEffect(const CStr& effect)
{
m_PlayerID = id;
m_ShaderEffect = CStrIntern(effect);
}
void CMaterial::SetUsePlayerColor(bool use)
void CMaterial::AddShaderDefine(const char* key, const char* value)
{
m_UsePlayerColor = use;
}
void CMaterial::SetUseTextureColor(bool use)
{
m_UseTextureColor = use;
}
void CMaterial::SetTextureColor(const CColor& colour)
{
m_TextureColor = SMaterialColor(colour.r, colour.g, colour.b, colour.a);
}
void CMaterial::SetTexture(const CStr& texture)
{
m_Texture = texture;
}
void CMaterial::SetUsesAlpha(bool flag)
{
m_Alpha = flag;
m_ShaderDefines.Add(key, value);
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,54 +18,47 @@
#ifndef INCLUDED_MATERIAL
#define INCLUDED_MATERIAL
#include "graphics/ShaderDefines.h"
#include "graphics/Texture.h"
#include "ps/CStr.h"
#include "ps/CStrIntern.h"
#include "ps/Overlay.h"
#include "simulation2/helpers/Player.h"
// FIXME: This material system is almost entirely unused and probably broken
typedef CColor SMaterialColor;
class CMaterial
{
public:
CMaterial();
const CStr& GetTexture() { return m_Texture; }
// Whether this material's shaders use alpha blending, in which case
// models using this material need to be rendered in a special order
// relative to the alpha-blended water plane
void SetUsesAlphaBlending(bool flag) { m_AlphaBlending = flag; }
bool UsesAlphaBlending() { return m_AlphaBlending; }
bool UsesAlpha() { return m_Alpha; }
// Color used for "objectColor" in shaders when USE_OBJECTCOLOR is set,
// to allow e.g. variations in horse colorings
void SetObjectColor(const CColor &colour);
CColor GetObjectColor() { return m_ObjectColor; }
// Determines whether or not the model goes into the PlayerRenderer
bool IsPlayer() { return (m_UseTextureColor || m_UsePlayerColor); }
void SetDiffuseTexture(const CTexturePtr& texture);
const CTexturePtr& GetDiffuseTexture() const { return m_DiffuseTexture; }
// Get the player colour or texture colour to be applied to this object
SMaterialColor GetObjectColor();
// Get the player colour
SMaterialColor GetPlayerColor();
void SetShaderEffect(const CStr& effect);
CStrIntern GetShaderEffect() const { return m_ShaderEffect; }
void SetPlayerID(player_id_t id);
void SetTextureColor(const CColor &colour);
void SetUsePlayerColor(bool use);
void SetUseTextureColor(bool use);
void SetTexture(const CStr& texture);
void SetUsesAlpha(bool flag);
void AddShaderDefine(const char* key, const char* value);
const CShaderDefines& GetShaderDefines() const { return m_ShaderDefines; }
private:
// Path to the materials texture
CStr m_Texture;
CTexturePtr m_DiffuseTexture;
CStrIntern m_ShaderEffect;
CShaderDefines m_ShaderDefines;
// Alpha required flag
bool m_Alpha;
bool m_AlphaBlending;
player_id_t m_PlayerID;
SMaterialColor m_TextureColor; // used as an alternative to the per-player colour
bool m_UsePlayerColor;
bool m_UseTextureColor;
CColor m_ObjectColor;
};
extern CMaterial NullMaterial;
#endif

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -21,94 +21,53 @@
#include "ps/XML/Xeromyces.h"
#include "MaterialManager.h"
static bool ParseUsage(CStr temp)
CMaterial CMaterialManager::LoadMaterial(const VfsPath& pathname)
{
temp = temp.LowerCase().Trim(PS_TRIM_BOTH);
if(temp == "blend" ||
temp == "true" ||
temp == "yes" ||
temp.ToInt() > 0)
return true;
if (pathname.empty())
return CMaterial();
return false;
}
CMaterialManager::CMaterialManager()
{
}
CMaterialManager::~CMaterialManager()
{
std::map<VfsPath, CMaterial*>::iterator iter;
for(iter = m_Materials.begin(); iter != m_Materials.end(); iter++)
delete (*iter).second;
m_Materials.clear();
}
CMaterial& CMaterialManager::LoadMaterial(const VfsPath& pathname)
{
if(pathname.empty())
return NullMaterial;
std::map<VfsPath, CMaterial*>::iterator iter = m_Materials.find(pathname);
if(iter != m_Materials.end())
{
if((*iter).second)
return *(*iter).second;
}
std::map<VfsPath, CMaterial>::iterator iter = m_Materials.find(pathname);
if (iter != m_Materials.end())
return iter->second;
CXeromyces xeroFile;
if(xeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
return NullMaterial;
if (xeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
return CMaterial();
#define EL(x) int el_##x = xeroFile.GetElementID(#x)
#define AT(x) int at_##x = xeroFile.GetAttributeID(#x)
EL(texture);
EL(alpha);
AT(usage);
EL(alpha_blending);
EL(define);
EL(shader);
AT(effect);
AT(name);
AT(value);
#undef AT
#undef EL
CMaterial *material = NULL;
try
{
XMBElement root = xeroFile.GetRoot();
XMBElementList childNodes = root.GetChildNodes();
material = new CMaterial();
CMaterial material;
for(int i = 0; i < childNodes.Count; i++)
XMBElement root = xeroFile.GetRoot();
XMBElementList childNodes = root.GetChildNodes();
XERO_ITER_EL(root, node)
{
int token = node.GetNodeName();
XMBAttributeList attrs = node.GetAttributes();
if (token == el_alpha_blending)
{
XMBElement node = childNodes.Item(i);
int token = node.GetNodeName();
XMBAttributeList attrs = node.GetAttributes();
CStr temp;
if(token == el_texture)
{
CStr value(node.GetText());
material->SetTexture(value);
}
else if(token == el_alpha)
{
temp = CStr(attrs.GetNamedItem(at_usage));
// Determine whether the alpha is used for basic transparency or player color
if (temp == "playercolor")
material->SetUsePlayerColor(true);
else if (temp == "objectcolor")
material->SetUseTextureColor(true);
else
material->SetUsesAlpha(ParseUsage(temp));
}
material.SetUsesAlphaBlending(true);
}
else if (token == el_shader)
{
material.SetShaderEffect(attrs.GetNamedItem(at_effect));
}
else if (token == el_define)
{
material.AddShaderDefine(attrs.GetNamedItem(at_name).c_str(), attrs.GetNamedItem(at_value).c_str());
}
m_Materials[pathname] = material;
}
catch(...)
{
SAFE_DELETE(material);
throw;
}
return *material;
m_Materials[pathname] = material;
return material;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -19,20 +19,15 @@
#define INCLUDED_MATERIALMANAGER
#include <map>
#include "ps/Singleton.h"
#include "Material.h"
#define g_MaterialManager CMaterialManager::GetSingleton()
class CMaterialManager : public Singleton<CMaterialManager>
class CMaterialManager
{
public:
CMaterialManager();
~CMaterialManager();
CMaterial LoadMaterial(const VfsPath& pathname);
CMaterial& LoadMaterial(const VfsPath& pathname);
private:
std::map<VfsPath, CMaterial*> m_Materials;
std::map<VfsPath, CMaterial> m_Materials;
};
#endif
#endif // INCLUDED_MATERIALMANAGER

View File

@ -65,8 +65,6 @@ void CModel::ReleaseData()
m_Props.clear();
m_pModelDef = CModelDefPtr();
m_Texture.reset();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -560,7 +558,6 @@ CModelAbstract* CModel::Clone() const
CModel* clone = new CModel(m_SkeletonAnimManager);
clone->m_ObjectBounds = m_ObjectBounds;
clone->InitModel(m_pModelDef);
clone->SetTexture(m_Texture);
clone->SetMaterial(m_Material);
clone->SetAnimation(m_Anim);
clone->SetFlags(m_Flags);
@ -606,9 +603,6 @@ void CModel::SetPlayerID(player_id_t id)
{
CModelAbstract::SetPlayerID(id);
if (id != INVALID_PLAYER)
m_Material.SetPlayerID(id);
for (std::vector<Prop>::iterator it = m_Props.begin(); it != m_Props.end(); ++it)
it->m_Model->SetPlayerID(id);
}

View File

@ -90,18 +90,14 @@ public:
void UpdateTo(float time);
// get the model's geometry data
CModelDefPtr GetModelDef() { return m_pModelDef; }
const CModelDefPtr& GetModelDef() { return m_pModelDef; }
// set the model's texture
void SetTexture(const CTexturePtr& tex) { m_Texture=tex; }
// set the model's material
void SetMaterial(const CMaterial &material);
// set the model's player ID, recursively through props
void SetPlayerID(player_id_t id);
// set the models mod color
virtual void SetShadingColor(const CColor& colour);
// get the model's texture
CTexturePtr& GetTexture() { return m_Texture; }
// get the model's material
CMaterial& GetMaterial() { return m_Material; }
@ -259,8 +255,6 @@ private:
// object flags
int m_Flags;
// texture used by model
CTexturePtr m_Texture;
// model's material
CMaterial m_Material;
// pointer to the model's raw 3d data

View File

@ -257,6 +257,9 @@ bool CObjectBase::Load(const VfsPath& pathname)
}
}
if (m_Material.empty())
m_Material = VfsPath("art/materials/default.xml");
return true;
}

View File

@ -118,15 +118,15 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
CModel* model = new CModel(objectManager.GetSkeletonAnimManager());
delete m_Model;
m_Model = model;
model->SetMaterial(g_MaterialManager.LoadMaterial(m_Base->m_Material));
model->GetMaterial().SetTextureColor(m_Color);
model->SetMaterial(g_Renderer.GetMaterialManager().LoadMaterial(m_Base->m_Material));
model->GetMaterial().SetObjectColor(m_Color);
model->InitModel(modeldef);
CTextureProperties textureProps(m_TextureName);
textureProps.SetWrap(GL_CLAMP_TO_EDGE);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch(); // if we've loaded this model we're probably going to render it soon, so prefetch its texture
model->SetTexture(texture);
model->GetMaterial().SetDiffuseTexture(texture);
// calculate initial object space bounds, based on vertex positions
model->CalcStaticObjectBounds();

View File

@ -0,0 +1,159 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ShaderDefines.h"
#include "ps/ThreadUtil.h"
#include <boost/unordered_map.hpp>
size_t hash_value(const CShaderDefines::SItems& items)
{
return items.hash;
}
bool operator==(const CShaderDefines::SItems& a, const CShaderDefines::SItems& b)
{
return a.items == b.items;
}
struct ItemNameCmp
{
typedef CShaderDefines::SItems::Item first_argument_type;
typedef CShaderDefines::SItems::Item second_argument_type;
bool operator()(const CShaderDefines::SItems::Item& a, const CShaderDefines::SItems::Item& b) const
{
return a.first < b.first;
}
};
struct ItemNameGeq
{
bool operator()(const CShaderDefines::SItems::Item& a, const CShaderDefines::SItems::Item& b) const
{
return !(b.first < a.first);
}
};
typedef boost::unordered_map<CShaderDefines::SItems, shared_ptr<CShaderDefines::SItems> > InternedItems_t;
static InternedItems_t g_InternedItems;
CShaderDefines::SItems* CShaderDefines::GetInterned(const SItems& items)
{
ENSURE(ThreadUtil::IsMainThread()); // g_InternedItems is not thread-safe
InternedItems_t::iterator it = g_InternedItems.find(items);
if (it != g_InternedItems.end())
return it->second.get();
// Sanity test: the items list is meant to be sorted by name.
// This is a reasonable place to verify that, since this will be called once per distinct SItems.
ENSURE(std::adjacent_find(items.items.begin(), items.items.end(), std::binary_negate<ItemNameCmp>(ItemNameCmp())) == items.items.end());
shared_ptr<SItems> ptr(new SItems(items));
g_InternedItems.insert(std::make_pair(items, ptr));
return ptr.get();
}
CShaderDefines::CShaderDefines()
{
SItems items;
items.RecalcHash();
m_Items = GetInterned(items);
}
void CShaderDefines::Add(const char* name, const char* value)
{
SItems items = *m_Items;
SItems::Item addedItem = std::make_pair(CStrIntern(name), CStrIntern(value));
// Add the new item in a way that preserves the sortedness and uniqueness of item names
for (std::vector<SItems::Item>::iterator it = items.items.begin(); ; ++it)
{
if (it == items.items.end() || addedItem.first < it->first)
{
items.items.insert(it, addedItem);
break;
}
else if (addedItem.first == it->first)
{
it->second = addedItem.second;
break;
}
}
items.RecalcHash();
m_Items = GetInterned(items);
}
void CShaderDefines::Add(const CShaderDefines& defines)
{
SItems items;
// set_union merges the two sorted lists into a new sorted list;
// if two items are equivalent (i.e. equal names, possibly different values)
// then the one from the first list is kept
std::set_union(
defines.m_Items->items.begin(), defines.m_Items->items.end(),
m_Items->items.begin(), m_Items->items.end(),
std::inserter(items.items, items.items.begin()),
ItemNameCmp());
items.RecalcHash();
m_Items = GetInterned(items);
}
std::map<CStr, CStr> CShaderDefines::GetMap() const
{
std::map<CStr, CStr> ret;
for (size_t i = 0; i < m_Items->items.size(); ++i)
ret[m_Items->items[i].first.string()] = m_Items->items[i].second.string();
return ret;
}
int CShaderDefines::GetInt(const char* name) const
{
CStrIntern nameIntern(name);
for (size_t i = 0; i < m_Items->items.size(); ++i)
{
if (m_Items->items[i].first == nameIntern)
{
int ret;
std::stringstream str(m_Items->items[i].second.c_str());
str >> ret;
return ret;
}
}
return 0;
}
size_t CShaderDefines::GetHash() const
{
return m_Items->hash;
}
void CShaderDefines::SItems::RecalcHash()
{
size_t h = 0;
for (size_t i = 0; i < items.size(); ++i)
{
boost::hash_combine(h, items[i].first.GetHash());
boost::hash_combine(h, items[i].second.GetHash());
}
hash = h;
}

View File

@ -0,0 +1,110 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SHADERDEFINES
#define INCLUDED_SHADERDEFINES
#include "ps/CStr.h"
#include "ps/CStrIntern.h"
/**
* Represents a mapping of name to value strings, for use with
* \#if and \#ifdef and similar conditionals in shaders.
*
* Stored as interned vectors of string-pairs, to support high performance
* comparison operators.
*
* Not thread-safe - must only be used from the main thread.
*/
class CShaderDefines
{
public:
/**
* Create an empty map of defines.
*/
CShaderDefines();
/**
* Add a name and associated value to the map of defines.
* If the name is already defined, its value will be replaced.
*/
void Add(const char* name, const char* value);
/**
* Add all the names and values from another set of defines.
* If any name is already defined in this object, its value will be replaced.
*/
void Add(const CShaderDefines& defines);
/**
* Return the value for the given name as an integer, or 0 if not defined.
*/
int GetInt(const char* name) const;
/**
* Return a copy of the current name/value mapping.
*/
std::map<CStr, CStr> GetMap() const;
/**
* Return a hash of the current mapping.
*/
size_t GetHash() const;
/**
* Compare with some arbitrary total order.
* The order may be different each time the application is run
* (it is based on interned memory addresses).
*/
bool operator<(const CShaderDefines& b) const
{
return m_Items < b.m_Items;
}
/**
* Equality comparison.
*/
bool operator==(const CShaderDefines& b) const
{
return m_Items == b.m_Items;
}
struct SItems
{
// Name/value pair
typedef std::pair<CStrIntern, CStrIntern> Item;
// Sorted by name; no duplicated names
std::vector<Item> items;
size_t hash;
void RecalcHash();
};
private:
SItems* m_Items; // interned value
/**
* Returns a pointer to an SItems equal to @p items.
* The pointer will be valid forever, and the same pointer will be returned
* for any subsequent requests for an equal items list.
*/
static SItems* GetInterned(const SItems& items);
};
#endif // INCLUDED_SHADERDEFINES

View File

@ -23,7 +23,9 @@
#include "lib/timer.h"
#include "lib/utf8.h"
#include "ps/CLogger.h"
#include "ps/CStrIntern.h"
#include "ps/Filesystem.h"
#include "ps/Preprocessor.h"
#include "ps/Profile.h"
#include "ps/XML/Xeromyces.h"
#include "ps/XML/XMLWriter.h"
@ -64,7 +66,7 @@ CShaderManager::~CShaderManager()
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
}
CShaderProgramPtr CShaderManager::LoadProgram(const char* name, const std::map<CStr, CStr>& defines)
CShaderProgramPtr CShaderManager::LoadProgram(const char* name, const CShaderDefines& defines)
{
CacheKey key = { name, defines };
std::map<CacheKey, CShaderProgramPtr>::iterator it = m_ProgramCache.find(key);
@ -105,7 +107,37 @@ static GLenum ParseAttribSemantics(const CStr& str)
return 0;
}
bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& baseDefines, CShaderProgramPtr& program)
static bool CheckPreprocessorConditional(CPreprocessor& preprocessor, const CStr& expr)
{
// Construct a dummy program so we can trigger the preprocessor's expression
// code without modifying its public API.
// Be careful that the API buggily returns a statically allocated pointer
// (which we will try to free()) if the input just causes it to append a single
// sequence of newlines to the output; the "\n" after the "#endif" is enough
// to avoid this case.
CStr input = "#if ";
input += expr;
input += "\n1\n#endif\n";
size_t len = 0;
char* output = preprocessor.Parse(input.c_str(), input.size(), len);
if (!output)
{
LOGERROR(L"Failed to parse conditional expression '%hs'", expr.c_str());
return false;
}
bool ret = (memchr(output, '1', len) != NULL);
// 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 CShaderManager::NewProgram(const char* name, const CShaderDefines& baseDefines, CShaderProgramPtr& program)
{
PROFILE2("loading shader");
PROFILE2_ATTR("name: %s", name);
@ -150,6 +182,7 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
EL(uniform);
EL(vertex);
AT(file);
AT(if);
AT(loc);
AT(name);
AT(semantics);
@ -158,12 +191,17 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
#undef AT
#undef EL
CPreprocessor preprocessor;
std::map<CStr, CStr> baseDefinesMap = baseDefines.GetMap();
for (std::map<CStr, CStr>::const_iterator it = baseDefinesMap.begin(); it != baseDefinesMap.end(); ++it)
preprocessor.Define(it->first.c_str(), it->first.length(), it->second.c_str(), it->second.length());
XMBElement Root = XeroFile.GetRoot();
bool isGLSL = (Root.GetAttributes().GetNamedItem(at_type) == "glsl");
VfsPath vertexFile;
VfsPath fragmentFile;
std::map<CStr, CStr> defines = baseDefines;
CShaderDefines defines = baseDefines;
std::map<CStr, int> vertexUniforms;
std::map<CStr, int> fragmentUniforms;
std::map<CStr, int> vertexAttribs;
@ -173,7 +211,7 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
{
if (Child.GetNodeName() == el_define)
{
defines[Child.GetAttributes().GetNamedItem(at_name)] = Child.GetAttributes().GetNamedItem(at_value);
defines.Add(Child.GetAttributes().GetNamedItem(at_name).c_str(), Child.GetAttributes().GetNamedItem(at_value).c_str());
}
else if (Child.GetNodeName() == el_vertex)
{
@ -181,13 +219,19 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
XERO_ITER_EL(Child, Param)
{
XMBAttributeList Attrs = Param.GetAttributes();
CStr cond = Attrs.GetNamedItem(at_if);
if (!cond.empty() && !CheckPreprocessorConditional(preprocessor, cond))
continue;
if (Param.GetNodeName() == el_uniform)
{
vertexUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
vertexUniforms[Attrs.GetNamedItem(at_name)] = Attrs.GetNamedItem(at_loc).ToInt();
}
else if (Param.GetNodeName() == el_stream)
{
CStr StreamName = Param.GetAttributes().GetNamedItem(at_name);
CStr StreamName = Attrs.GetNamedItem(at_name);
if (StreamName == "pos")
streamFlags |= STREAM_POS;
else if (StreamName == "normal")
@ -205,8 +249,8 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
}
else if (Param.GetNodeName() == el_attrib)
{
int attribLoc = ParseAttribSemantics(Param.GetAttributes().GetNamedItem(at_semantics));
vertexAttribs[Param.GetAttributes().GetNamedItem(at_name)] = attribLoc;
int attribLoc = ParseAttribSemantics(Attrs.GetNamedItem(at_semantics));
vertexAttribs[Attrs.GetNamedItem(at_name)] = attribLoc;
}
}
}
@ -216,9 +260,15 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
XERO_ITER_EL(Child, Param)
{
XMBAttributeList Attrs = Param.GetAttributes();
CStr cond = Attrs.GetNamedItem(at_if);
if (!cond.empty() && !CheckPreprocessorConditional(preprocessor, cond))
continue;
if (Param.GetNodeName() == el_uniform)
{
fragmentUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
fragmentUniforms[Attrs.GetNamedItem(at_name)] = Attrs.GetNamedItem(at_loc).ToInt();
}
}
}
@ -296,40 +346,43 @@ static GLenum ParseBlendFunc(const CStr& str)
return GL_ZERO;
}
CShaderManager::EffectContext CShaderManager::GetEffectContextAndVerifyCache()
size_t CShaderManager::EffectCacheKeyHash::operator()(const EffectCacheKey& key) const
{
EffectContext cx;
cx.hasARB = g_Renderer.GetCapabilities().m_ARBProgram;
cx.hasGLSL = (g_Renderer.GetCapabilities().m_VertexShader && g_Renderer.GetCapabilities().m_FragmentShader);
cx.preferGLSL = g_Renderer.m_Options.m_PreferGLSL;
// If the context changed since last time, reload every effect
if (!(cx == m_EffectCacheContext))
{
m_EffectCacheContext = cx;
for (std::map<CacheKey, CShaderTechniquePtr>::iterator it = m_EffectCache.begin(); it != m_EffectCache.end(); ++it)
{
it->second->Reset();
NewEffect(it->first.name.c_str(), it->first.defines, cx, it->second);
}
}
return cx;
size_t hash = 0;
boost::hash_combine(hash, key.name.GetHash());
boost::hash_combine(hash, key.defines1.GetHash());
boost::hash_combine(hash, key.defines2.GetHash());
return hash;
}
CShaderTechniquePtr CShaderManager::LoadEffect(const char* name, const std::map<CStr, CStr>& defines)
bool CShaderManager::EffectCacheKey::operator==(const EffectCacheKey& b) const
{
EffectContext cx = GetEffectContextAndVerifyCache();
return (name == b.name && defines1 == b.defines1 && defines2 == b.defines2);
}
CacheKey key = { name, defines };
std::map<CacheKey, CShaderTechniquePtr>::iterator it = m_EffectCache.find(key);
CShaderTechniquePtr CShaderManager::LoadEffect(const char* name)
{
return LoadEffect(CStrIntern(name), CShaderDefines(), CShaderDefines());
}
CShaderTechniquePtr CShaderManager::LoadEffect(CStrIntern name, const CShaderDefines& defines1, const CShaderDefines& defines2)
{
// Return the cached effect, if there is one
EffectCacheKey key = { name, defines1, defines2 };
EffectCacheMap::iterator it = m_EffectCache.find(key);
if (it != m_EffectCache.end())
return it->second;
// First time we've seen this key, so construct a new effect:
// Merge the two sets of defines, so NewEffect doesn't have to care about the split
CShaderDefines defines(defines1);
defines.Add(defines2);
CShaderTechniquePtr tech(new CShaderTechnique());
if (!NewEffect(name, defines, cx, tech))
if (!NewEffect(name.c_str(), defines, tech))
{
LOGERROR(L"Failed to load effect '%hs'", name);
LOGERROR(L"Failed to load effect '%hs'", name.c_str());
tech = CShaderTechniquePtr();
}
@ -337,7 +390,7 @@ CShaderTechniquePtr CShaderManager::LoadEffect(const char* name, const std::map<
return tech;
}
bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& baseDefines, const EffectContext& cx, CShaderTechniquePtr& tech)
bool CShaderManager::NewEffect(const char* name, const CShaderDefines& baseDefines, CShaderTechniquePtr& tech)
{
PROFILE2("loading effect");
PROFILE2_ATTR("name: %s", name);
@ -370,6 +423,8 @@ bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& bas
EL(depth);
EL(pass);
EL(require);
EL(sort_by_distance);
AT(context);
AT(dst);
AT(func);
AT(ref);
@ -382,6 +437,17 @@ bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& bas
#undef AT
#undef EL
// Read some defines that influence how we pick techniques
bool hasARB = (baseDefines.GetInt("SYS_HAS_ARB") != 0);
bool hasGLSL = (baseDefines.GetInt("SYS_HAS_GLSL") != 0);
bool preferGLSL = (baseDefines.GetInt("SYS_PREFER_GLSL") != 0);
// Prepare the preprocessor for conditional tests
CPreprocessor preprocessor;
std::map<CStr, CStr> baseDefinesMap = baseDefines.GetMap();
for (std::map<CStr, CStr>::const_iterator it = baseDefinesMap.begin(); it != baseDefinesMap.end(); ++it)
preprocessor.Define(it->first.c_str(), it->first.length(), it->second.c_str(), it->second.length());
XMBElement Root = XeroFile.GetRoot();
// Find all the techniques that we can use, and their preference
@ -394,30 +460,38 @@ bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& bas
bool isUsable = true;
XERO_ITER_EL(Technique, Child)
{
XMBAttributeList Attrs = Child.GetAttributes();
if (Child.GetNodeName() == el_require)
{
if (Child.GetAttributes().GetNamedItem(at_shaders) == "fixed")
if (Attrs.GetNamedItem(at_shaders) == "fixed")
{
// FFP not supported by OpenGL ES
#if CONFIG2_GLES
isUsable = false;
#endif
}
else if (Child.GetAttributes().GetNamedItem(at_shaders) == "arb")
else if (Attrs.GetNamedItem(at_shaders) == "arb")
{
if (!cx.hasARB)
if (!hasARB)
isUsable = false;
}
else if (Child.GetAttributes().GetNamedItem(at_shaders) == "glsl")
else if (Attrs.GetNamedItem(at_shaders) == "glsl")
{
if (!cx.hasGLSL)
if (!hasGLSL)
isUsable = false;
if (cx.preferGLSL)
if (preferGLSL)
preference += 100;
else
preference -= 100;
}
else if (!Attrs.GetNamedItem(at_context).empty())
{
CStr cond = Attrs.GetNamedItem(at_context);
if (!CheckPreprocessorConditional(preprocessor, cond))
isUsable = false;
}
}
}
@ -434,11 +508,21 @@ bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& bas
// Sort by preference, tie-break on order of specification
std::stable_sort(usableTechs.begin(), usableTechs.end(), revcompare2nd());
CShaderDefines techDefines = baseDefines;
XERO_ITER_EL(usableTechs[0].first, Child)
{
if (Child.GetNodeName() == el_pass)
if (Child.GetNodeName() == el_define)
{
std::map<CStr, CStr> defines = baseDefines;
techDefines.Add(Child.GetAttributes().GetNamedItem(at_name).c_str(), Child.GetAttributes().GetNamedItem(at_value).c_str());
}
else if (Child.GetNodeName() == el_sort_by_distance)
{
tech->SetSortByDistance(true);
}
else if (Child.GetNodeName() == el_pass)
{
CShaderDefines passDefines = techDefines;
CShaderPass pass;
@ -446,7 +530,7 @@ bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& bas
{
if (Element.GetNodeName() == el_define)
{
defines[Element.GetAttributes().GetNamedItem(at_name)] = Element.GetAttributes().GetNamedItem(at_value);
passDefines.Add(Element.GetAttributes().GetNamedItem(at_name).c_str(), Element.GetAttributes().GetNamedItem(at_value).c_str());
}
else if (Element.GetNodeName() == el_alpha)
{
@ -471,7 +555,7 @@ bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& bas
}
// Load the shader program after we've read all the possibly-relevant <define>s
pass.SetShader(LoadProgram(Child.GetAttributes().GetNamedItem(at_shader).c_str(), defines));
pass.SetShader(LoadProgram(Child.GetAttributes().GetNamedItem(at_shader).c_str(), passDefines));
tech->AddPass(pass);
}
@ -480,6 +564,11 @@ bool CShaderManager::NewEffect(const char* name, const std::map<CStr, CStr>& bas
return true;
}
size_t CShaderManager::GetNumEffectsLoaded()
{
return m_EffectCache.size();
}
/*static*/ Status CShaderManager::ReloadChangedFileCB(void* param, const VfsPath& path)
{
return static_cast<CShaderManager*>(param)->ReloadChangedFile(path);

View File

@ -23,6 +23,7 @@
#include <boost/unordered_map.hpp>
#include <boost/weak_ptr.hpp>
#include "graphics/ShaderDefines.h"
#include "graphics/ShaderProgram.h"
#include "graphics/ShaderTechnique.h"
@ -47,23 +48,33 @@ public:
* @param defines key/value set of preprocessor definitions
* @return loaded program, or null pointer on error
*/
CShaderProgramPtr LoadProgram(const char* name, const std::map<CStr, CStr>& defines = (std::map<CStr, CStr>()));
CShaderProgramPtr LoadProgram(const char* name, const CShaderDefines& defines);
/**
* Load a shader effect.
* Effects can be implemented via many techniques; this returns the best usable technique.
* @param name name of effect XML specification (file is loaded from shaders/effects/${name}.xml)
* @param defines key/value set of preprocessor definitions
* @param defines1,defines2 key/value set of preprocessor definitions; defines2 has higher precedence
* @return loaded technique, or empty technique on error
*/
CShaderTechniquePtr LoadEffect(const char* name, const std::map<CStr, CStr>& defines = (std::map<CStr, CStr>()));
CShaderTechniquePtr LoadEffect(CStrIntern name, const CShaderDefines& defines1, const CShaderDefines& defines2);
/**
* Load a shader effect, with no defines.
*/
CShaderTechniquePtr LoadEffect(const char* name);
/**
* Returns the number of shader effects that are currently loaded.
*/
size_t GetNumEffectsLoaded();
private:
struct CacheKey
{
std::string name;
std::map<CStr, CStr> defines;
CShaderDefines defines;
bool operator<(const CacheKey& k) const
{
@ -76,31 +87,32 @@ private:
// A CShaderProgram contains expensive GL state, so we ought to cache it.
// The compiled state depends solely on the filename and list of defines,
// so we store that in CacheKey.
// TODO: is this cache useful when we already have an effect cache?
std::map<CacheKey, CShaderProgramPtr> m_ProgramCache;
// An effect isn't too expensive but it may be loaded many times per frame,
// so we ought to cache it anyway.
// For each effect we pick a technique at load time, dependent on various
// settings (e.g. GL shader extensions, or user's chosen graphics quality)
// which rarely change. We'll store that collection of settings in
// EffectContext and reload the effect cache whenever it changes.
struct EffectContext
/**
* Key for effect cache lookups.
* This stores two separate CShaderDefines because the renderer typically
* has one set from the rendering context and one set from the material;
* by handling both separately here, we avoid the cost of having to merge
* the two sets into a single one before doing the cache lookup.
*/
struct EffectCacheKey
{
bool hasARB;
bool hasGLSL;
bool preferGLSL;
CStrIntern name;
CShaderDefines defines1;
CShaderDefines defines2;
bool operator==(const EffectContext& b) const
{
return hasARB == b.hasARB && hasGLSL == b.hasGLSL && preferGLSL == b.preferGLSL;
}
bool operator==(const EffectCacheKey& b) const;
};
EffectContext GetEffectContextAndVerifyCache();
std::map<CacheKey, CShaderTechniquePtr> m_EffectCache;
EffectContext m_EffectCacheContext;
struct EffectCacheKeyHash
{
size_t operator()(const EffectCacheKey& key) const;
};
typedef boost::unordered_map<EffectCacheKey, CShaderTechniquePtr, EffectCacheKeyHash> EffectCacheMap;
EffectCacheMap m_EffectCache;
// Store the set of shaders that need to be reloaded when the given file is modified
typedef boost::unordered_map<VfsPath, std::set<boost::weak_ptr<CShaderProgram> > > HotloadFilesMap;
@ -110,8 +122,8 @@ private:
RelaxNGValidator m_Validator;
#endif
bool NewProgram(const char* name, const std::map<CStr, CStr>& defines, CShaderProgramPtr& program);
bool NewEffect(const char* name, const std::map<CStr, CStr>& defines, const EffectContext& cx, CShaderTechniquePtr& tech);
bool NewProgram(const char* name, const CShaderDefines& defines, CShaderProgramPtr& program);
bool NewEffect(const char* name, const CShaderDefines& defines, CShaderTechniquePtr& tech);
static Status ReloadChangedFileCB(void* param, const VfsPath& path);
Status ReloadChangedFile(const VfsPath& path);

View File

@ -19,6 +19,7 @@
#include "ShaderProgram.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextureManager.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/Matrix3D.h"
@ -34,7 +35,7 @@ class CShaderProgramARB : public CShaderProgram
{
public:
CShaderProgramARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const CShaderDefines& defines,
const std::map<CStr, int>& vertexIndexes, const std::map<CStr, int>& fragmentIndexes,
int streamflags) :
CShaderProgram(streamflags),
@ -68,7 +69,7 @@ public:
{
GLint errPos = 0;
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errPos);
int errLine = std::count(code.begin(), code.begin() + errPos + 1, '\n') + 1;
int errLine = std::count(code.begin(), code.begin() + std::min((int)code.length(), 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;
@ -94,8 +95,9 @@ public:
return;
CPreprocessor preprocessor;
for (std::map<CStr, CStr>::iterator it = m_Defines.begin(); it != m_Defines.end(); ++it)
preprocessor.Define(it->first.c_str(), it->second.c_str());
std::map<CStr, CStr> definesMap = m_Defines.GetMap();
for (std::map<CStr, CStr>::const_iterator it = definesMap.begin(); it != definesMap.end(); ++it)
preprocessor.Define(it->first.c_str(), it->first.length(), it->second.c_str(), it->second.length());
CStr vertexCode = Preprocess(preprocessor, vertexFile.GetAsString());
CStr fragmentCode = Preprocess(preprocessor, fragmentFile.GetAsString());
@ -155,18 +157,25 @@ public:
return it->second;
}
virtual bool HasTexture(texture_id_t id)
virtual Binding GetTextureBinding(texture_id_t id)
{
if (GetUniformFragmentIndex(id) != -1)
return true;
return false;
int index = GetUniformFragmentIndex(id);
if (index == -1)
return Binding();
else
return Binding((int)GL_TEXTURE_2D, index);
}
virtual void BindTexture(texture_id_t id, Handle tex)
{
int index = GetUniformFragmentIndex(id);
if (index != -1)
ogl_tex_bind(tex, index);
{
GLuint h;
ogl_tex_get_texture_id(tex, &h);
pglActiveTextureARB(GL_TEXTURE0+index);
glBindTexture(GL_TEXTURE_2D, h);
}
}
virtual void BindTexture(texture_id_t id, GLuint tex)
@ -174,14 +183,16 @@ public:
int index = GetUniformFragmentIndex(id);
if (index != -1)
{
pglActiveTextureARB((int)(GL_TEXTURE0+index));
pglActiveTextureARB(GL_TEXTURE0+index);
glBindTexture(GL_TEXTURE_2D, tex);
}
}
virtual int GetTextureUnit(texture_id_t id)
virtual void BindTexture(Binding id, Handle tex)
{
return GetUniformFragmentIndex(id);
int index = id.second;
if (index != -1)
ogl_tex_bind(tex, index);
}
virtual Binding GetUniformBinding(uniform_id_t id)
@ -220,7 +231,7 @@ public:
private:
VfsPath m_VertexFile;
VfsPath m_FragmentFile;
std::map<CStr, CStr> m_Defines;
CShaderDefines m_Defines;
GLuint m_VertexProgram;
GLuint m_FragmentProgram;
@ -231,11 +242,16 @@ private:
#endif // #if !CONFIG2_GLES
//////////////////////////////////////////////////////////////////////////
TIMER_ADD_CLIENT(tc_ShaderGLSLCompile);
TIMER_ADD_CLIENT(tc_ShaderGLSLLink);
class CShaderProgramGLSL : public CShaderProgram
{
public:
CShaderProgramGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const CShaderDefines& defines,
const std::map<CStr, int>& vertexAttribs,
int streamflags) :
CShaderProgram(streamflags),
@ -258,6 +274,8 @@ public:
bool Compile(GLhandleARB shader, const VfsPath& file, const CStr& code)
{
TIMER_ACCRUE(tc_ShaderGLSLCompile);
ogl_WarnIfError();
const char* code_string = code.c_str();
@ -297,6 +315,8 @@ public:
bool Link()
{
TIMER_ACCRUE(tc_ShaderGLSLLink);
ENSURE(!m_Program);
m_Program = pglCreateProgramObjectARB();
@ -403,8 +423,16 @@ public:
return;
CPreprocessor preprocessor;
for (std::map<CStr, CStr>::iterator it = m_Defines.begin(); it != m_Defines.end(); ++it)
preprocessor.Define(it->first.c_str(), it->second.c_str());
std::map<CStr, CStr> definesMap = m_Defines.GetMap();
for (std::map<CStr, CStr>::const_iterator it = definesMap.begin(); it != definesMap.end(); ++it)
preprocessor.Define(it->first.c_str(), it->first.length(), it->second.c_str(), it->second.length());
#if CONFIG2_GLES
// GLES defines the macro "GL_ES" in its GLSL preprocessor,
// but since we run our own preprocessor first, we need to explicitly
// define it here
preprocessor.Define("GL_ES", "1");
#endif
CStr vertexCode = Preprocess(preprocessor, vertexFile.GetAsString());
CStr fragmentCode = Preprocess(preprocessor, fragmentFile.GetAsString());
@ -467,11 +495,13 @@ public:
return it->second;
}
virtual bool HasTexture(texture_id_t id)
virtual Binding GetTextureBinding(texture_id_t id)
{
if (GetUniformLocation(id) != -1)
return true;
return false;
std::map<CStr, std::pair<GLenum, int> >::iterator it = m_Samplers.find(id);
if (it == m_Samplers.end())
return Binding();
else
return Binding((int)it->second.first, it->second.second);
}
virtual void BindTexture(texture_id_t id, Handle tex)
@ -496,13 +526,15 @@ public:
glBindTexture(it->second.first, tex);
}
virtual int GetTextureUnit(texture_id_t id)
virtual void BindTexture(Binding id, Handle tex)
{
std::map<CStr, std::pair<GLenum, int> >::iterator it = m_Samplers.find(id);
if (it == m_Samplers.end())
return -1;
if (id.second == -1)
return;
return it->second.second;
GLuint h;
ogl_tex_get_texture_id(tex, &h);
pglActiveTextureARB(GL_TEXTURE0 + id.second);
glBindTexture(id.first, h);
}
virtual Binding GetUniformBinding(uniform_id_t id)
@ -572,7 +604,7 @@ public:
private:
VfsPath m_VertexFile;
VfsPath m_FragmentFile;
std::map<CStr, CStr> m_Defines;
CShaderDefines m_Defines;
std::map<CStr, int> m_VertexAttribs;
GLhandleARB m_Program;
@ -584,7 +616,7 @@ private:
std::map<CStr, std::pair<GLenum, int> > m_Samplers; // texture target & unit chosen for each uniform sampler
};
//////////////////////////////////////////////////////////////////////////
CShaderProgram::CShaderProgram(int streamflags)
: m_IsValid(false), m_StreamFlags(streamflags), m_ValidStreams(0)
@ -593,7 +625,7 @@ CShaderProgram::CShaderProgram(int streamflags)
#if CONFIG2_GLES
/*static*/ CShaderProgram* CShaderProgram::ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& UNUSED(defines),
const CShaderDefines& UNUSED(defines),
const std::map<CStr, int>& UNUSED(vertexIndexes), const std::map<CStr, int>& UNUSED(fragmentIndexes),
int UNUSED(streamflags))
{
@ -603,7 +635,7 @@ CShaderProgram::CShaderProgram(int streamflags)
}
#else
/*static*/ CShaderProgram* CShaderProgram::ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const CShaderDefines& defines,
const std::map<CStr, int>& vertexIndexes, const std::map<CStr, int>& fragmentIndexes,
int streamflags)
{
@ -612,7 +644,7 @@ CShaderProgram::CShaderProgram(int streamflags)
#endif
/*static*/ CShaderProgram* CShaderProgram::ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const CShaderDefines& defines,
const std::map<CStr, int>& vertexAttribs,
int streamflags)
{

View File

@ -30,6 +30,7 @@ struct CColor;
class CMatrix3D;
class CVector3D;
class CPreprocessor;
class CShaderDefines;
// Vertex data stream flags
enum
@ -49,12 +50,13 @@ enum
/**
* 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.
* The implementation may use GL_ARB_{vertex,fragment}_program (ARB assembly syntax)
* or GL_ARB_{vertex,fragment}_shader (GLSL), or may use hard-coded fixed-function
* multitexturing setup code; 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.
* Texture/uniform IDs are typically strings, corresponding to the names defined in
* the shader .xml file. Alternatively (and more efficiently, if used very frequently),
* call GetTextureBinding/GetUniformBinding and pass its return value as the ID.
* Setting uniforms that the shader .xml doesn't support is harmless.
*/
class CShaderProgram
@ -66,7 +68,7 @@ public:
* Construct based on ARB vertex/fragment program files.
*/
static CShaderProgram* ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const CShaderDefines& defines,
const std::map<CStr, int>& vertexIndexes, const std::map<CStr, int>& fragmentIndexes,
int streamflags);
@ -74,24 +76,26 @@ public:
* Construct based on GLSL vertex/fragment shader files.
*/
static CShaderProgram* ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const CShaderDefines& defines,
const std::map<CStr, int>& vertexAttribs,
int streamflags);
/**
* Construct an instance of a pre-defined fixed-function pipeline setup.
*/
static CShaderProgram* ConstructFFP(const std::string& id, const std::map<CStr, CStr>& defines);
static CShaderProgram* ConstructFFP(const std::string& id, const CShaderDefines& defines);
typedef const char* attrib_id_t;
typedef const char* texture_id_t;
typedef const char* uniform_id_t;
/**
* Represents a uniform attribute binding.
* ARB shaders store vertex location in 'first', fragment location in 'second'.
* GLSL shaders store uniform location in 'first', data type in 'second'.
* FFP shaders store -1 in 'first', index in 'second'.
* Represents a uniform attribute or texture binding.
* For uniforms:
* - ARB shaders store vertex location in 'first', fragment location in 'second'.
* - GLSL shaders store uniform location in 'first', data type in 'second'.
* - FFP shaders store -1 in 'first', index in 'second'.
* For textures, all store texture target (e.g. GL_TEXTURE_2D) in 'first', texture unit in 'second'.
* Non-existent bindings must store -1 in both.
*/
struct Binding
@ -139,18 +143,15 @@ public:
// 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 Binding GetTextureBinding(texture_id_t id) = 0;
// Variants of texture binding:
void BindTexture(texture_id_t id, CTexturePtr tex);
virtual void BindTexture(texture_id_t id, Handle tex) = 0;
virtual void BindTexture(texture_id_t id, GLuint tex) = 0;
virtual void BindTexture(Binding id, Handle tex) = 0;
virtual int GetTextureUnit(texture_id_t) = 0;
virtual Binding GetUniformBinding(uniform_id_t id) = 0;

View File

@ -19,6 +19,7 @@
#include "ShaderProgram.h"
#include "graphics/ShaderDefines.h"
#include "graphics/TextureManager.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/Matrix3D.h"
@ -63,11 +64,13 @@ public:
return it->second;
}
virtual bool HasTexture(texture_id_t id)
virtual Binding GetTextureBinding(uniform_id_t id)
{
if (GetUniformIndex(id) != -1)
return true;
return false;
int index = GetUniformIndex(id);
if (index == -1)
return Binding();
else
return Binding((int)GL_TEXTURE_2D, index);
}
virtual void BindTexture(texture_id_t id, Handle tex)
@ -87,9 +90,11 @@ public:
}
}
virtual int GetTextureUnit(texture_id_t id)
virtual void BindTexture(Binding id, Handle tex)
{
return GetUniformIndex(id);
int index = id.second;
if (index != -1)
ogl_tex_bind(tex, index);
}
virtual Binding GetUniformBinding(uniform_id_t id)
@ -151,7 +156,7 @@ class CShaderProgramFFP_OverlayLine : public CShaderProgramFFP
bool m_IgnoreLos;
public:
CShaderProgramFFP_OverlayLine(const std::map<CStr, CStr>& defines) :
CShaderProgramFFP_OverlayLine(const CShaderDefines& defines) :
CShaderProgramFFP(STREAM_POS | STREAM_UV0 | STREAM_UV1)
{
m_UniformIndexes["losTransform"] = ID_losTransform;
@ -162,7 +167,7 @@ public:
m_UniformIndexes["maskTex"] = 1;
m_UniformIndexes["losTex"] = 2;
m_IgnoreLos = (defines.find(CStr("IGNORE_LOS")) != defines.end());
m_IgnoreLos = (defines.GetInt("IGNORE_LOS") != 0);
}
bool IsIgnoreLos()
@ -635,7 +640,282 @@ public:
//////////////////////////////////////////////////////////////////////////
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& id, const std::map<CStr, CStr>& defines)
/**
* Common functionality for model rendering in the fixed renderpath.
*/
class CShaderProgramFFP_Model_Base : public CShaderProgramFFP
{
protected:
// Uniforms
enum
{
ID_transform,
ID_objectColor,
ID_playerColor
};
public:
CShaderProgramFFP_Model_Base(const CShaderDefines& defines, int streamflags)
: CShaderProgramFFP(streamflags)
{
m_UniformIndexes["transform"] = ID_transform;
if (defines.GetInt("USE_OBJECTCOLOR"))
m_UniformIndexes["objectColor"] = ID_objectColor;
if (defines.GetInt("USE_PLAYERCOLOR"))
m_UniformIndexes["playerColor"] = ID_playerColor;
// Texture units:
m_UniformIndexes["baseTex"] = 0;
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.second == ID_transform)
glLoadMatrixf(&v._11);
}
virtual void Bind()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
BindClientStates();
}
virtual void Unbind()
{
UnbindClientStates();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
};
/**
* Basic non-recolored diffuse-textured model rendering.
*/
class CShaderProgramFFP_Model : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_Model(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_COLOR | STREAM_UV0)
{
}
virtual void Bind()
{
// Set up texture environment for base pass - modulate texture and vertex color
pglActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Copy alpha channel from texture
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
// The vertex color is scaled by 0.5 to permit overbrightness without clamping.
// We therefore need to scale by 2.0 after the modulation, and before any
// further clamping, to get the right color.
float scale2[] = { 2.0f, 2.0f, 2.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale2);
CShaderProgramFFP_Model_Base::Bind();
}
virtual void Unbind()
{
CShaderProgramFFP_Model_Base::Unbind();
pglActiveTextureARB(GL_TEXTURE0);
// Revert the scaling to default
float scale1[] = { 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale1);
}
};
/**
* Player-coloring diffuse-textured model rendering.
*/
class CShaderProgramFFP_ModelColor : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_ModelColor(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_COLOR | STREAM_UV0)
{
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_objectColor || id.second == ID_playerColor)
{
// (Player color and object color are mutually exclusive)
float color[] = { v0, v1, v2, v3 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
}
}
virtual void Bind()
{
// Player color uses a single pass with three texture environments
// Note: This uses ARB_texture_env_crossbar (which is checked in GameSetup),
// and it requires MAX_TEXTURE_IMAGE_UNITS >= 3 (which only excludes GF2MX/GF4MX)
//
// We calculate: Result = Color*Texture*(PlayerColor*(1-Texture.a) + 1.0*Texture.a)
// Algebra gives us:
// Result = (1 - ((1 - PlayerColor) * (1 - Texture.a)))*Texture*Color
// TexEnv #0
pglActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
// TexEnv #1
pglActiveTextureARB(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
// TexEnv #2
pglActiveTextureARB(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
// Scale colors at the end of all the computation (see CShaderProgramFFP_Model::Bind)
float scale[] = { 2.0f, 2.0f, 2.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale);
// Need to bind some kind of texture to enable the texture units.
// Unit 0 has baseTex, but the others need a texture.
g_Renderer.GetTextureManager().GetErrorTexture()->Bind(1);
g_Renderer.GetTextureManager().GetErrorTexture()->Bind(2);
CShaderProgramFFP_Model_Base::Bind();
}
virtual void Unbind()
{
CShaderProgramFFP_Model_Base::Unbind();
pglActiveTextureARB(GL_TEXTURE2);
glDisable(GL_TEXTURE_2D);
float scale[] = { 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale);
pglActiveTextureARB(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0);
}
};
/**
* Optionally-player-colored untextured model rendering.
*/
class CShaderProgramFFP_ModelSolid : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_ModelSolid(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS)
{
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.second == ID_playerColor)
{
float color[] = { v0, v1, v2, v3 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
}
}
virtual void Bind()
{
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
CShaderProgramFFP_Model_Base::Bind();
}
};
/**
* Plain unlit texture model rendering, for e.g. alpha-blended shadow casters.
*/
class CShaderProgramFFP_ModelSolidTex : public CShaderProgramFFP_Model_Base
{
public:
CShaderProgramFFP_ModelSolidTex(const CShaderDefines& defines)
: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_UV0)
{
}
virtual void Bind()
{
pglActiveTextureARB(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
CShaderProgramFFP_Model_Base::Bind();
}
};
//////////////////////////////////////////////////////////////////////////
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& id, const CShaderDefines& defines)
{
if (id == "dummy")
return new CShaderProgramFFP_Dummy();
@ -653,6 +933,14 @@ public:
return new CShaderProgramFFP_GuiSolid();
if (id == "solid")
return new CShaderProgramFFP_GuiSolid(); // works for non-GUI objects too
if (id == "model")
return new CShaderProgramFFP_Model(defines);
if (id == "model_color")
return new CShaderProgramFFP_ModelColor(defines);
if (id == "model_solid")
return new CShaderProgramFFP_ModelSolid(defines);
if (id == "model_solid_tex")
return new CShaderProgramFFP_ModelSolidTex(defines);
LOGERROR(L"CShaderProgram::ConstructFFP: '%hs': Invalid id", id.c_str());
debug_warn(L"CShaderProgram::ConstructFFP: Invalid id");
@ -661,7 +949,7 @@ public:
#else // CONFIG2_GLES
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& UNUSED(id), const std::map<CStr, CStr>& UNUSED(defines))
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& UNUSED(id), const CShaderDefines& UNUSED(defines))
{
debug_warn(L"CShaderProgram::ConstructFFP: FFP not supported on this device");
return NULL;

View File

@ -113,20 +113,16 @@ void CShaderPass::DepthFunc(GLenum func)
CShaderTechnique::CShaderTechnique()
: m_SortByDistance(false)
{
}
CShaderTechnique::CShaderTechnique(const CShaderPass& pass)
{
m_Passes.push_back(pass);
}
void CShaderTechnique::AddPass(const CShaderPass& pass)
{
m_Passes.push_back(pass);
}
int CShaderTechnique::GetNumPasses()
int CShaderTechnique::GetNumPasses() const
{
return m_Passes.size();
}
@ -143,13 +139,24 @@ void CShaderTechnique::EndPass(int pass)
m_Passes[pass].Unbind();
}
CShaderProgramPtr CShaderTechnique::GetShader(int pass)
const CShaderProgramPtr& CShaderTechnique::GetShader(int pass) const
{
ENSURE(0 <= pass && pass < (int)m_Passes.size());
return m_Passes[pass].GetShader();
}
bool CShaderTechnique::GetSortByDistance() const
{
return m_SortByDistance;
}
void CShaderTechnique::SetSortByDistance(bool enable)
{
m_SortByDistance = enable;
}
void CShaderTechnique::Reset()
{
m_SortByDistance = false;
m_Passes.clear();
}
}

View File

@ -21,7 +21,8 @@
#include "graphics/ShaderProgram.h"
/**
* Implements a render pass consisting of various GL state changes and a shader.
* Implements a render pass consisting of various GL state changes and a shader,
* used by CShaderTechnique.
*/
class CShaderPass
{
@ -50,7 +51,7 @@ public:
*/
void Unbind();
CShaderProgramPtr GetShader() { return m_Shader; }
const CShaderProgramPtr& GetShader() const { return m_Shader; }
private:
CShaderProgramPtr m_Shader;
@ -78,26 +79,34 @@ private:
/**
* 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.
* CShaderManager loads these from shader effect XML files.
*/
class CShaderTechnique
{
public:
CShaderTechnique();
CShaderTechnique(const CShaderPass& pass);
void AddPass(const CShaderPass& pass);
int GetNumPasses();
int GetNumPasses() const;
void BeginPass(int pass = 0);
void EndPass(int pass = 0);
CShaderProgramPtr GetShader(int pass = 0);
const CShaderProgramPtr& GetShader(int pass = 0) const;
/**
* Whether this technique uses alpha blending that requires objects to be
* drawn from furthest to nearest.
*/
bool GetSortByDistance() const;
void SetSortByDistance(bool enable);
void Reset();
private:
std::vector<CShaderPass> m_Passes;
bool m_SortByDistance;
};
typedef shared_ptr<CShaderTechnique> CShaderTechniquePtr;

View File

@ -0,0 +1,89 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lib/self_test.h"
#include "graphics/ShaderManager.h"
class TestShaderManager : public CxxTest::TestSuite
{
public:
void test_defines()
{
CShaderDefines defines1;
CShaderDefines defines2;
CShaderDefines defines3;
TS_ASSERT(defines1 == defines2);
TS_ASSERT(defines1.GetHash() == defines2.GetHash());
TS_ASSERT(!(defines1 < defines2 || defines2 < defines1));
defines1.Add("FOO", "1");
TS_ASSERT_EQUALS(defines1.GetInt("FOO"), 1);
TS_ASSERT_EQUALS(defines2.GetInt("FOO"), 0);
TS_ASSERT(!(defines1 == defines2));
TS_ASSERT(!(defines1.GetHash() == defines2.GetHash()));
TS_ASSERT(defines1 < defines2 || defines2 < defines1);
defines2.Add("FOO", "2");
TS_ASSERT_EQUALS(defines1.GetInt("FOO"), 1);
TS_ASSERT_EQUALS(defines2.GetInt("FOO"), 2);
TS_ASSERT(!(defines1 == defines2));
TS_ASSERT(!(defines1.GetHash() == defines2.GetHash()));
defines3 = defines2;
defines2.Add("FOO", "1"); // override old value
TS_ASSERT_EQUALS(defines1.GetInt("FOO"), 1);
TS_ASSERT_EQUALS(defines2.GetInt("FOO"), 1);
TS_ASSERT_EQUALS(defines3.GetInt("FOO"), 2);
TS_ASSERT(defines1 == defines2);
TS_ASSERT(defines1.GetHash() == defines2.GetHash());
}
void test_defines_order()
{
CShaderDefines defines1;
defines1.Add("A", "1");
defines1.Add("B", "1");
defines1.Add("C", "1");
defines1.Add("D", "1");
CShaderDefines defines2;
defines2.Add("C", "2");
defines2.Add("C", "1");
defines2.Add("B", "2");
defines2.Add("B", "1");
defines2.Add("D", "2");
defines2.Add("D", "1");
defines2.Add("A", "2");
defines2.Add("A", "1");
TS_ASSERT(defines1 == defines2);
defines2.Add(defines1);
TS_ASSERT(defines1 == defines2);
CShaderDefines defines3;
defines3.Add(defines1);
TS_ASSERT(defines1 == defines3);
}
};

View File

@ -39,10 +39,8 @@ bool g_Shadows = false;
bool g_ShadowPCF = false;
bool g_FancyWater = false;
float g_LodBias = 0.0f;
float g_Gamma = 1.0f;
bool g_EntGraph = false;
CStr g_RenderPath = "default";
int g_xres, g_yres;
@ -80,8 +78,6 @@ static void LoadGlobals()
CFG_GET_USER_VAL("fancywater", Bool, g_FancyWater);
CFG_GET_USER_VAL("renderpath", String, g_RenderPath);
CFG_GET_USER_VAL("lodbias", Float, g_LodBias);
float gain = -1.0f;
CFG_GET_USER_VAL("sound.mastergain", Float, gain);
if(gain >= 0.0f)
@ -115,9 +111,6 @@ static void ProcessCommandLineArgs(const CmdLineArgs& args)
}
}
if (args.Has("entgraph"))
g_EntGraph = true;
if (args.Has("g"))
{
g_Gamma = args.Get("g").ToFloat();

View File

@ -51,9 +51,7 @@ extern bool g_FancyWater;
// flag to switch on shadow PCF
extern bool g_ShadowPCF;
extern float g_LodBias;
extern float g_Gamma;
extern bool g_EntGraph;
// name of configured render path (depending on OpenGL extensions, this may not be
// the render path that is actually in use right now)
extern CStr g_RenderPath;

View File

@ -581,7 +581,6 @@ static void InitRenderer()
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS,g_Shadows);
g_Renderer.SetOptionBool(CRenderer::OPT_FANCYWATER,g_FancyWater);
g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));
g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, g_LodBias);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCF, g_ShadowPCF);
// create terrain related stuff
@ -659,12 +658,6 @@ void Shutdown(int UNUSED(flags))
in_reset_handlers();
// destroy actor related stuff
TIMER_BEGIN(L"shutdown actor stuff");
delete &g_MaterialManager;
TIMER_END(L"shutdown actor stuff");
// destroy terrain related stuff
TIMER_BEGIN(L"shutdown TexMan");
delete &g_TexMan;
TIMER_END(L"shutdown TexMan");

View File

@ -138,7 +138,8 @@ void CDecalRData::BuildArrays()
VertexArrayIterator<SColor4ub> DiffuseColor = m_DiffuseColor.GetIterator<SColor4ub>();
VertexArrayIterator<float[2]> UV = m_UV.GetIterator<float[2]>();
bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
for (ssize_t j = j0; j <= j1; ++j)
{
@ -155,7 +156,7 @@ void CDecalRData::BuildArrays()
CVector3D normal;
m_Decal->m_Terrain->CalcNormal(i, j, normal);
*DiffuseColor = g_Renderer.GetLightEnv().EvaluateDiffuse(normal, includeSunColor);
*DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
++DiffuseColor;
// Map from world space back into decal texture space

View File

@ -1,288 +0,0 @@
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Implementation of FixedFunctionModelRenderer
*/
#include "precompiled.h"
#include "lib/bits.h"
#include "lib/ogl.h"
#include "lib/sysdep/rtl.h"
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
#include "ps/CLogger.h"
#include "graphics/SColor.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "renderer/FixedFunctionModelRenderer.h"
#include "renderer/Renderer.h"
#include "renderer/RenderModifiers.h"
#include "renderer/VertexArray.h"
#if !CONFIG2_GLES
///////////////////////////////////////////////////////////////////////////////////////////////
// FixedFunctionModelRenderer implementation
struct FFModelDef : public CModelDefRPrivate
{
/// Indices are the same for all models, so share them
VertexIndexArray m_IndexArray;
/// Static per-CModelDef vertex array
VertexArray m_Array;
/// UV coordinates are stored in the static array
VertexArray::Attribute m_UV;
FFModelDef(const CModelDefPtr& mdef);
};
FFModelDef::FFModelDef(const CModelDefPtr& mdef)
: m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
{
size_t numVertices = mdef->GetNumVertices();
m_UV.type = GL_FLOAT;
m_UV.elems = 2;
m_Array.AddAttribute(&m_UV);
m_Array.SetNumVertices(numVertices);
m_Array.Layout();
VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>();
ModelRenderer::BuildUV(mdef, UVit);
m_Array.Upload();
m_Array.FreeBackingStore();
m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3);
m_IndexArray.Layout();
ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
m_IndexArray.Upload();
m_IndexArray.FreeBackingStore();
}
struct FFModel
{
/// Dynamic per-CModel vertex array
VertexArray m_Array;
/// Position and lighting are recalculated on CPU every frame
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Color;
FFModel() : m_Array(GL_DYNAMIC_DRAW) { }
};
struct FixedFunctionModelRendererInternals
{
/**
* Scratch space for normal vector calculation.
* Space is reserved so we don't have to do frequent reallocations.
* Allocated with rtl_AllocateAligned(normalsNumVertices*16, 16) for SSE writes.
*/
char* normals;
size_t normalsNumVertices;
/// Previously prepared modeldef
FFModelDef* ffmodeldef;
};
// Construction and Destruction
FixedFunctionModelRenderer::FixedFunctionModelRenderer()
{
m = new FixedFunctionModelRendererInternals;
m->ffmodeldef = 0;
m->normals = 0;
m->normalsNumVertices = 0;
}
FixedFunctionModelRenderer::~FixedFunctionModelRenderer()
{
rtl_FreeAligned(m->normals);
delete m;
}
// Build model data (and modeldef data if necessary)
void* FixedFunctionModelRenderer::CreateModelData(CModel* model)
{
CModelDefPtr mdef = model->GetModelDef();
FFModelDef* ffmodeldef = (FFModelDef*)mdef->GetRenderData(m);
if (!ffmodeldef)
{
ffmodeldef = new FFModelDef(mdef);
mdef->SetRenderData(m, ffmodeldef);
}
// Build the per-model data
FFModel* ffmodel = new FFModel;
// Positions must be 16-byte aligned for SSE writes.
// We can pack the color after the position; it will be corrupted by
// BuildPositionAndNormals, but that's okay since we'll recompute the
// colors afterwards.
ffmodel->m_Color.type = GL_UNSIGNED_BYTE;
ffmodel->m_Color.elems = 4;
ffmodel->m_Array.AddAttribute(&ffmodel->m_Color);
ffmodel->m_Position.type = GL_FLOAT;
ffmodel->m_Position.elems = 3;
ffmodel->m_Array.AddAttribute(&ffmodel->m_Position);
ffmodel->m_Array.SetNumVertices(mdef->GetNumVertices());
ffmodel->m_Array.Layout();
// Verify alignment
ENSURE(ffmodel->m_Position.offset % 16 == 0);
ENSURE(ffmodel->m_Array.GetStride() % 16 == 0);
return ffmodel;
}
// Fill in and upload dynamic vertex array
void FixedFunctionModelRenderer::UpdateModelData(CModel* model, void* data, int updateflags)
{
FFModel* ffmodel = (FFModel*)data;
if (updateflags & (RENDERDATA_UPDATE_VERTICES|RENDERDATA_UPDATE_COLOR))
{
CModelDefPtr mdef = model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
// build vertices
// allocate working space for computing normals
if (numVertices > m->normalsNumVertices)
{
rtl_FreeAligned(m->normals);
size_t newSize = round_up_to_pow2(numVertices);
m->normals = (char*)rtl_AllocateAligned(newSize*16, 16);
m->normalsNumVertices = newSize;
}
VertexArrayIterator<CVector3D> Position = ffmodel->m_Position.GetIterator<CVector3D>();
VertexArrayIterator<CVector3D> Normal = VertexArrayIterator<CVector3D>(m->normals, 16);
ModelRenderer::BuildPositionAndNormals(model, Position, Normal);
VertexArrayIterator<SColor4ub> Color = ffmodel->m_Color.GetIterator<SColor4ub>();
ModelRenderer::BuildColor4ub(model, Normal, Color);
// upload everything to vertex buffer
ffmodel->m_Array.Upload();
}
}
// Cleanup per-model data.
// Note that per-CModelDef data is deleted by the CModelDef itself.
void FixedFunctionModelRenderer::DestroyModelData(CModel* UNUSED(model), void* data)
{
FFModel* ffmodel = (FFModel*)data;
delete ffmodel;
}
// Setup one rendering pass
void FixedFunctionModelRenderer::BeginPass(int streamflags)
{
ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_UV0|STREAM_COLOR)));
glEnableClientState(GL_VERTEX_ARRAY);
if (streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (streamflags & STREAM_COLOR) glEnableClientState(GL_COLOR_ARRAY);
}
// Cleanup one rendering pass
void FixedFunctionModelRenderer::EndPass(int streamflags)
{
if (streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (streamflags & STREAM_COLOR) glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
CVertexBuffer::Unbind();
}
// Prepare UV coordinates for this modeldef
void FixedFunctionModelRenderer::PrepareModelDef(CShaderProgramPtr& UNUSED(shader), int streamflags, const CModelDefPtr& def)
{
m->ffmodeldef = (FFModelDef*)def->GetRenderData(m);
ENSURE(m->ffmodeldef);
if (streamflags & STREAM_UV0)
{
u8* base = m->ffmodeldef->m_Array.Bind();
GLsizei stride = (GLsizei)m->ffmodeldef->m_Array.GetStride();
glTexCoordPointer(2, GL_FLOAT, stride, base + m->ffmodeldef->m_UV.offset);
}
}
// Render one model
void FixedFunctionModelRenderer::RenderModel(CShaderProgramPtr& UNUSED(shader), int streamflags, CModel* model, void* data)
{
CModelDefPtr mdldef = model->GetModelDef();
FFModel* ffmodel = (FFModel*)data;
u8* base = ffmodel->m_Array.Bind();
GLsizei stride = (GLsizei)ffmodel->m_Array.GetStride();
u8* indexBase = m->ffmodeldef->m_IndexArray.Bind();
glVertexPointer(3, GL_FLOAT, stride, base + ffmodel->m_Position.offset);
if (streamflags & STREAM_COLOR)
glColorPointer(3, ffmodel->m_Color.type, stride, base + ffmodel->m_Color.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;
}
#endif // !CONFIG2_GLES

View File

@ -1,58 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* ModelVertexRenderer that uses only fixed function pipeline to render
* animated models.
*/
#ifndef INCLUDED_FIXEDFUNCTIONMODELRENDERER
#define INCLUDED_FIXEDFUNCTIONMODELRENDERER
#include "renderer/ModelVertexRenderer.h"
#if !CONFIG2_GLES
struct FixedFunctionModelRendererInternals;
/**
* Class FixedFunctionModelRenderer: Render animated models using only
* OpenGL fixed function.
*/
class FixedFunctionModelRenderer : public ModelVertexRenderer
{
public:
FixedFunctionModelRenderer();
~FixedFunctionModelRenderer();
// Implementations
void* CreateModelData(CModel* model);
void UpdateModelData(CModel* model, void* data, int updateflags);
void DestroyModelData(CModel* model, void* data);
void BeginPass(int streamflags);
void EndPass(int streamflags);
void PrepareModelDef(CShaderProgramPtr& shader, int streamflags, const CModelDefPtr& def);
void RenderModel(CShaderProgramPtr& shader, int streamflags, CModel* model, void* data);
private:
FixedFunctionModelRendererInternals* m;
};
#endif // !CONFIG2_GLES
#endif // INCLUDED_FIXEDFUNCTIONMODELRENDERER

View File

@ -17,9 +17,10 @@
#include "precompiled.h"
#include "lib/bits.h"
#include "lib/ogl.h"
#include "lib/sysdep/rtl.h"
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
#include "graphics/Color.h"
#include "graphics/LightEnv.h"
@ -74,41 +75,58 @@ ShaderModelDef::ShaderModelDef(const CModelDefPtr& mdef)
}
struct ShaderModel
struct ShaderModel : public CModelRData
{
/// Dynamic per-CModel vertex array
VertexArray m_Array;
/// Position and normals are recalculated on CPU every frame
/// Position and normals/lighting are recalculated on CPU every frame
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Normal;
VertexArray::Attribute m_Normal; // valid iff cpuLighting == false
VertexArray::Attribute m_Color; // valid iff cpuLighting == true
ShaderModel() : m_Array(GL_DYNAMIC_DRAW) { }
ShaderModel(const void* key) : CModelRData(key), m_Array(GL_DYNAMIC_DRAW) { }
};
struct ShaderModelRendererInternals
{
bool cpuLighting;
/**
* Scratch space for normal vector calculation.
* Only used if cpuLighting == true.
* Space is reserved so we don't have to do frequent reallocations.
* Allocated with rtl_AllocateAligned(normalsNumVertices*16, 16) for SSE writes.
*/
char* normals;
size_t normalsNumVertices;
/// Previously prepared modeldef
ShaderModelDef* shadermodeldef;
};
// Construction and Destruction
ShaderModelRenderer::ShaderModelRenderer()
ShaderModelVertexRenderer::ShaderModelVertexRenderer(bool cpuLighting)
{
m = new ShaderModelRendererInternals;
m->shadermodeldef = 0;
m->cpuLighting = cpuLighting;
m->normals = NULL;
m->normalsNumVertices = 0;
m->shadermodeldef = NULL;
}
ShaderModelRenderer::~ShaderModelRenderer()
ShaderModelVertexRenderer::~ShaderModelVertexRenderer()
{
rtl_FreeAligned(m->normals);
delete m;
}
// Build model data (and modeldef data if necessary)
void* ShaderModelRenderer::CreateModelData(CModel* model)
CModelRData* ShaderModelVertexRenderer::CreateModelData(const void* key, CModel* model)
{
CModelDefPtr mdef = model->GetModelDef();
ShaderModelDef* shadermodeldef = (ShaderModelDef*)mdef->GetRenderData(m);
@ -120,24 +138,43 @@ void* ShaderModelRenderer::CreateModelData(CModel* model)
}
// Build the per-model data
ShaderModel* shadermodel = new ShaderModel;
ShaderModel* shadermodel = new ShaderModel(key);
// Positions and normals must be 16-byte aligned for SSE writes.
if (m->cpuLighting)
{
// Positions must be 16-byte aligned for SSE writes.
// We can pack the color after the position; it will be corrupted by
// BuildPositionAndNormals, but that's okay since we'll recompute the
// colors afterwards.
shadermodel->m_Position.type = GL_FLOAT;
shadermodel->m_Position.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Position);
shadermodel->m_Color.type = GL_UNSIGNED_BYTE;
shadermodel->m_Color.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Color);
shadermodel->m_Normal.type = GL_FLOAT;
shadermodel->m_Normal.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Normal);
shadermodel->m_Position.type = GL_FLOAT;
shadermodel->m_Position.elems = 3;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Position);
}
else
{
// Positions and normals must be 16-byte aligned for SSE writes.
shadermodel->m_Position.type = GL_FLOAT;
shadermodel->m_Position.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Position);
shadermodel->m_Normal.type = GL_FLOAT;
shadermodel->m_Normal.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Normal);
}
shadermodel->m_Array.SetNumVertices(mdef->GetNumVertices());
shadermodel->m_Array.Layout();
// Verify alignment
ENSURE(shadermodel->m_Position.offset % 16 == 0);
ENSURE(shadermodel->m_Normal.offset % 16 == 0);
if (!m->cpuLighting)
ENSURE(shadermodel->m_Normal.offset % 16 == 0);
ENSURE(shadermodel->m_Array.GetStride() % 16 == 0);
return shadermodel;
@ -145,11 +182,11 @@ void* ShaderModelRenderer::CreateModelData(CModel* model)
// Fill in and upload dynamic vertex array
void ShaderModelRenderer::UpdateModelData(CModel* model, void* data, int updateflags)
void ShaderModelVertexRenderer::UpdateModelData(CModel* model, CModelRData* data, int updateflags)
{
ShaderModel* shadermodel = (ShaderModel*)data;
if (updateflags & RENDERDATA_UPDATE_VERTICES)
ShaderModel* shadermodel = static_cast<ShaderModel*>(data);
if (!m->cpuLighting && (updateflags & RENDERDATA_UPDATE_VERTICES))
{
// build vertices
VertexArrayIterator<CVector3D> Position = shadermodel->m_Position.GetIterator<CVector3D>();
@ -160,36 +197,57 @@ void ShaderModelRenderer::UpdateModelData(CModel* model, void* data, int updatef
// upload everything to vertex buffer
shadermodel->m_Array.Upload();
}
}
if (m->cpuLighting && (updateflags & (RENDERDATA_UPDATE_VERTICES|RENDERDATA_UPDATE_COLOR)))
{
CModelDefPtr mdef = model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
// Cleanup per-model data.
// Note that per-CModelDef data is deleted by the CModelDef itself.
void ShaderModelRenderer::DestroyModelData(CModel* UNUSED(model), void* data)
{
ShaderModel* shadermodel = (ShaderModel*)data;
// allocate working space for computing normals
if (numVertices > m->normalsNumVertices)
{
rtl_FreeAligned(m->normals);
delete shadermodel;
size_t newSize = round_up_to_pow2(numVertices);
m->normals = (char*)rtl_AllocateAligned(newSize*16, 16);
m->normalsNumVertices = newSize;
}
VertexArrayIterator<CVector3D> Position = shadermodel->m_Position.GetIterator<CVector3D>();
VertexArrayIterator<CVector3D> Normal = VertexArrayIterator<CVector3D>(m->normals, 16);
ModelRenderer::BuildPositionAndNormals(model, Position, Normal);
VertexArrayIterator<SColor4ub> Color = shadermodel->m_Color.GetIterator<SColor4ub>();
ModelRenderer::BuildColor4ub(model, Normal, Color);
// upload everything to vertex buffer
shadermodel->m_Array.Upload();
}
}
// Setup one rendering pass
void ShaderModelRenderer::BeginPass(int streamflags)
void ShaderModelVertexRenderer::BeginPass(int streamflags)
{
ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0)));
if (m->cpuLighting)
ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_COLOR)));
else
ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_NORMAL)));
}
// Cleanup one rendering pass
void ShaderModelRenderer::EndPass(int UNUSED(streamflags))
void ShaderModelVertexRenderer::EndPass(int UNUSED(streamflags))
{
CVertexBuffer::Unbind();
}
// Prepare UV coordinates for this modeldef
void ShaderModelRenderer::PrepareModelDef(CShaderProgramPtr& shader, int streamflags, const CModelDefPtr& def)
void ShaderModelVertexRenderer::PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def)
{
m->shadermodeldef = (ShaderModelDef*)def->GetRenderData(m);
m->shadermodeldef = (ShaderModelDef*)def.GetRenderData(m);
ENSURE(m->shadermodeldef);
@ -204,10 +262,10 @@ void ShaderModelRenderer::PrepareModelDef(CShaderProgramPtr& shader, int streamf
// Render one model
void ShaderModelRenderer::RenderModel(CShaderProgramPtr& shader, int streamflags, CModel* model, void* data)
void ShaderModelVertexRenderer::RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data)
{
CModelDefPtr mdldef = model->GetModelDef();
ShaderModel* shadermodel = (ShaderModel*)data;
ShaderModel* shadermodel = static_cast<ShaderModel*>(data);
u8* base = shadermodel->m_Array.Bind();
GLsizei stride = (GLsizei)shadermodel->m_Array.GetStride();
@ -220,6 +278,9 @@ void ShaderModelRenderer::RenderModel(CShaderProgramPtr& shader, int streamflags
if (streamflags & STREAM_NORMAL)
shader->NormalPointer(GL_FLOAT, stride, base + shadermodel->m_Normal.offset);
if (streamflags & STREAM_COLOR)
shader->ColorPointer(3, GL_UNSIGNED_BYTE, stride, base + shadermodel->m_Color.offset);
shader->AssertPointersBound();
// render the lot

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -29,27 +29,23 @@ struct ShaderModelRendererInternals;
/**
* Render animated models using a ShaderRenderModifier.
* This just passes through the vertex data directly; the modifier is responsible
* This computes and binds per-vertex data; the modifier is responsible
* for setting any shader uniforms etc.
*/
class ShaderModelRenderer : public ModelVertexRenderer
class ShaderModelVertexRenderer : public ModelVertexRenderer
{
public:
/**
* HWLightingModelRenderer: Constructor.
*/
ShaderModelRenderer();
~ShaderModelRenderer();
ShaderModelVertexRenderer(bool cpuLighting);
~ShaderModelVertexRenderer();
// Implementations
void* CreateModelData(CModel* model);
void UpdateModelData(CModel* model, void* data, int updateflags);
void DestroyModelData(CModel* model, void* data);
CModelRData* CreateModelData(const void* key, CModel* model);
void UpdateModelData(CModel* model, CModelRData* data, int updateflags);
void BeginPass(int streamflags);
void EndPass(int streamflags);
void PrepareModelDef(CShaderProgramPtr& shader, int streamflags, const CModelDefPtr& def);
void RenderModel(CShaderProgramPtr& shader, int streamflags, CModel* model, void* data);
void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def);
void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data);
protected:
ShaderModelRendererInternals* m;

View File

@ -54,7 +54,6 @@ struct IModelDef : public CModelDefRPrivate
/// Indices are the same for all models, so share them
VertexIndexArray m_IndexArray;
IModelDef(const CModelDefPtr& mdef);
};
@ -121,7 +120,7 @@ InstancingModelRenderer::~InstancingModelRenderer()
// Build modeldef data if necessary - we have no per-CModel data
void* InstancingModelRenderer::CreateModelData(CModel* model)
CModelRData* InstancingModelRenderer::CreateModelData(const void* key, CModel* model)
{
CModelDefPtr mdef = model->GetModelDef();
IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
@ -134,22 +133,16 @@ void* InstancingModelRenderer::CreateModelData(CModel* model)
mdef->SetRenderData(m, imodeldef);
}
return NULL;
return new CModelRData(key);
}
void InstancingModelRenderer::UpdateModelData(CModel* UNUSED(model), void* UNUSED(data), int UNUSED(updateflags))
void InstancingModelRenderer::UpdateModelData(CModel* UNUSED(model), CModelRData* UNUSED(data), int UNUSED(updateflags))
{
// We have no per-CModel data
}
void InstancingModelRenderer::DestroyModelData(CModel* UNUSED(model), void* UNUSED(data))
{
// We have no per-CModel data, and per-CModelDef data is deleted by the CModelDef
}
// Setup one rendering pass.
void InstancingModelRenderer::BeginPass(int streamflags)
{
@ -164,9 +157,9 @@ void InstancingModelRenderer::EndPass(int UNUSED(streamflags))
// Prepare UV coordinates for this modeldef
void InstancingModelRenderer::PrepareModelDef(CShaderProgramPtr& shader, int streamflags, const CModelDefPtr& def)
void InstancingModelRenderer::PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def)
{
m->imodeldef = (IModelDef*)def->GetRenderData(m);
m->imodeldef = (IModelDef*)def.GetRenderData(m);
ENSURE(m->imodeldef);
@ -189,7 +182,7 @@ void InstancingModelRenderer::PrepareModelDef(CShaderProgramPtr& shader, int str
// Render one model
void InstancingModelRenderer::RenderModel(CShaderProgramPtr& UNUSED(shader), int UNUSED(streamflags), CModel* model, void* UNUSED(data))
void InstancingModelRenderer::RenderModel(const CShaderProgramPtr& UNUSED(shader), int UNUSED(streamflags), CModel* model, CModelRData* UNUSED(data))
{
CModelDefPtr mdldef = model->GetModelDef();

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -29,7 +29,7 @@ struct InstancingModelRendererInternals;
/**
* Render non-animated (but potentially moving) models using a ShaderRenderModifier.
* This just passes through the vertex data directly; the modifier is responsible
* This computes and binds per-vertex data; the modifier is responsible
* for setting any shader uniforms etc (including the instancing transform).
*/
class InstancingModelRenderer : public ModelVertexRenderer
@ -39,14 +39,13 @@ public:
~InstancingModelRenderer();
// Implementations
void* CreateModelData(CModel* model);
void UpdateModelData(CModel* model, void* data, int updateflags);
void DestroyModelData(CModel* model, void* data);
CModelRData* CreateModelData(const void* key, CModel* model);
void UpdateModelData(CModel* model, CModelRData* data, int updateflags);
void BeginPass(int streamflags);
void EndPass(int streamflags);
void PrepareModelDef(CShaderProgramPtr& shader, int streamflags, const CModelDefPtr& def);
void RenderModel(CShaderProgramPtr& shader, int streamflags, CModel* model, void* data);
void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def);
void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data);
protected:
InstancingModelRendererInternals* m;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -15,10 +15,6 @@
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Implementation of ModelRenderer and BatchModelRenderer
*/
#include "precompiled.h"
#include "lib/ogl.h"
@ -32,6 +28,7 @@
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextureManager.h"
#include "renderer/ModelRenderer.h"
@ -89,7 +86,6 @@ void ModelRenderer::BuildPositionAndNormals(
if (model->IsSkinned())
{
// boned model - calculate skinned vertex positions/normals
PROFILE( "skinning bones" );
// Avoid the noisy warnings that occur inside SkinPoint/SkinNormal in
// some broken situations
@ -137,11 +133,10 @@ void ModelRenderer::BuildColor4ub(
size_t numVertices = mdef->GetNumVertices();
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
CColor shadingColor = model->GetShadingColor();
RGBColor tempcolor;
for (size_t j=0; j<numVertices; j++)
{
lightEnv.EvaluateUnit(Normal[j], tempcolor);
RGBColor tempcolor = lightEnv.EvaluateUnitScaled(Normal[j]);
tempcolor.X *= shadingColor.r;
tempcolor.Y *= shadingColor.g;
tempcolor.Z *= shadingColor.b;
@ -185,326 +180,438 @@ void ModelRenderer::BuildIndices(
///////////////////////////////////////////////////////////////////////////////////////////////
// BatchModelRenderer implementation
/// See BatchModelRendererInternals::phase
enum BMRPhase {
/// Currently allow calls to Submit and PrepareModels
BMRSubmit,
/// Allow calls to rendering and EndFrame
BMRRender
};
// ShaderModelRenderer implementation
/**
* Struct BMRModelData: Per-CModel render data used by the BatchModelRenderer.
*/
struct BMRModelData : public CModelRData
{
BMRModelData(BatchModelRendererInternals* bmri, CModel* model)
: CModelRData(bmri, model), m_BMRI(bmri), m_Data(0), m_Next(0) { }
virtual ~BMRModelData();
/// Back-link to "our" modelrenderer
BatchModelRendererInternals* m_BMRI;
/// Private data created by derived class' CreateModelData
void* m_Data;
/// Next model in the per-ModelDefTracker-slot linked list.
BMRModelData* m_Next;
};
/**
* Class BMRModelDefTracker: Per-CModelDef data used by the BatchModelRenderer.
*
* Note that classes that derive from BatchModelRenderer should use
* their own per-CModelDef data if necessary.
*/
struct BMRModelDefTracker : public CModelDefRPrivate
{
BMRModelDefTracker(const CModelDefPtr& mdef)
: m_ModelDef(mdef), m_Next(0), m_Slots(0) { }
/// Back-link to the CModelDef object
boost::weak_ptr<CModelDef> m_ModelDef;
/// Pointer to the next ModelDefTracker that has submitted models.
BMRModelDefTracker* m_Next;
/// Number of slots used in m_ModelSlots
size_t m_Slots;
/// Each slot contains a linked list of model data objects, up to m_Slots-1
// At the end of the frame, m_Slots is reset to 0, but m_ModelSlots stays
// the same size (we assume the same number of slots is going to be used
// next frame)
std::vector<BMRModelData*> m_ModelSlots;
};
/**
* Struct BatchModelRendererInternals: Internal data of the BatchModelRenderer
* Internal data of the ShaderModelRenderer.
*
* Separated into the source file to increase implementation hiding (and to
* avoid some causes of recompiles).
*/
struct BatchModelRendererInternals
struct ShaderModelRendererInternals
{
BatchModelRendererInternals(BatchModelRenderer* r) : m_Renderer(r) { }
ShaderModelRendererInternals(ShaderModelRenderer* r) : m_Renderer(r) { }
/// Back-link to "our" renderer
BatchModelRenderer* m_Renderer;
ShaderModelRenderer* m_Renderer;
/// ModelVertexRenderer used for vertex transformations
ModelVertexRendererPtr vertexRenderer;
/// Track the current "phase" of the frame (only for debugging purposes)
BMRPhase phase;
/// Linked list of ModelDefTrackers that have submitted models
BMRModelDefTracker* submissions;
/// Helper functions
void ThunkDestroyModelData(CModel* model, void* data)
{
vertexRenderer->DestroyModelData(model, data);
}
void RenderAllModels(const RenderModifierPtr& modifier, int filterflags, int pass, int streamflags);
void FilterAllModels(CModelFilter& filter, int passed, int filterflags);
/// List of submitted models for rendering in this frame
std::vector<CModel*> submissions;
};
BMRModelData::~BMRModelData()
{
m_BMRI->ThunkDestroyModelData(GetModel(), m_Data);
}
// Construction/Destruction
BatchModelRenderer::BatchModelRenderer(ModelVertexRendererPtr vertexrenderer)
ShaderModelRenderer::ShaderModelRenderer(ModelVertexRendererPtr vertexrenderer)
{
m = new BatchModelRendererInternals(this);
m = new ShaderModelRendererInternals(this);
m->vertexRenderer = vertexrenderer;
m->phase = BMRSubmit;
m->submissions = 0;
}
BatchModelRenderer::~BatchModelRenderer()
ShaderModelRenderer::~ShaderModelRenderer()
{
delete m;
}
// Submit one model.
void BatchModelRenderer::Submit(CModel* model)
void ShaderModelRenderer::Submit(CModel* model)
{
ENSURE(m->phase == BMRSubmit);
ogl_WarnIfError();
CModelDefPtr mdef = model->GetModelDef();
BMRModelDefTracker* mdeftracker = (BMRModelDefTracker*)mdef->GetRenderData(m);
CModelRData* rdata = (CModelRData*)model->GetRenderData();
BMRModelData* bmrdata = 0;
// Ensure model def data and model data exist
if (!mdeftracker)
// Ensure model data is valid
const void* key = m->vertexRenderer.get();
if (!rdata || rdata->GetKey() != key)
{
mdeftracker = new BMRModelDefTracker(mdef);
mdef->SetRenderData(m, mdeftracker);
}
if (rdata && rdata->GetKey() == m)
{
bmrdata = (BMRModelData*)rdata;
}
else
{
bmrdata = new BMRModelData(m, model);
bmrdata->m_Data = m->vertexRenderer->CreateModelData(model);
rdata = bmrdata;
model->SetRenderData(bmrdata);
rdata = m->vertexRenderer->CreateModelData(key, model);
model->SetRenderData(rdata);
model->SetDirty(~0u);
}
// Add the model def tracker to the submission list if necessary
if (!mdeftracker->m_Slots)
{
mdeftracker->m_Next = m->submissions;
m->submissions = mdeftracker;
}
// Add the bmrdata to the modeldef list
CTexturePtr tex = model->GetTexture();
size_t idx;
for(idx = 0; idx < mdeftracker->m_Slots; ++idx)
{
BMRModelData* in = mdeftracker->m_ModelSlots[idx];
if (in->GetModel()->GetTexture() == tex)
break;
}
if (idx >= mdeftracker->m_Slots)
{
++mdeftracker->m_Slots;
if (mdeftracker->m_Slots > mdeftracker->m_ModelSlots.size())
{
mdeftracker->m_ModelSlots.push_back(0);
ENSURE(mdeftracker->m_ModelSlots.size() == mdeftracker->m_Slots);
}
mdeftracker->m_ModelSlots[idx] = 0;
}
bmrdata->m_Next = mdeftracker->m_ModelSlots[idx];
mdeftracker->m_ModelSlots[idx] = bmrdata;
ogl_WarnIfError();
m->submissions.push_back(model);
}
// Call update for all submitted models and enter the rendering phase
void BatchModelRenderer::PrepareModels()
void ShaderModelRenderer::PrepareModels()
{
ENSURE(m->phase == BMRSubmit);
for(BMRModelDefTracker* mdeftracker = m->submissions; mdeftracker; mdeftracker = mdeftracker->m_Next)
for (size_t i = 0; i < m->submissions.size(); ++i)
{
for(size_t idx = 0; idx < mdeftracker->m_Slots; ++idx)
{
for(BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx]; bmrdata; bmrdata = bmrdata->m_Next)
{
CModel* model = bmrdata->GetModel();
CModel* model = m->submissions[i];
ENSURE(model->GetRenderData() == bmrdata);
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
m->vertexRenderer->UpdateModelData(
model, bmrdata->m_Data,
bmrdata->m_UpdateFlags);
bmrdata->m_UpdateFlags = 0;
}
}
m->vertexRenderer->UpdateModelData(model, rdata, rdata->m_UpdateFlags);
rdata->m_UpdateFlags = 0;
}
m->phase = BMRRender;
}
// Clear the submissions list
void BatchModelRenderer::EndFrame()
void ShaderModelRenderer::EndFrame()
{
static size_t mostslots = 1;
m->submissions.clear();
}
for(BMRModelDefTracker* mdeftracker = m->submissions; mdeftracker; mdeftracker = mdeftracker->m_Next)
// Helper structs for ShaderModelRenderer::Render():
struct SMRSortByDistItem
{
size_t techIdx;
CModel* model;
float dist;
};
struct SMRBatchModel
{
bool operator()(CModel* a, CModel* b)
{
if (mdeftracker->m_Slots > mostslots)
{
mostslots = mdeftracker->m_Slots;
//debug_printf(L"BatchModelRenderer: SubmissionSlots maximum: %u\n", mostslots);
}
mdeftracker->m_Slots = 0;
if (a->GetModelDef() < b->GetModelDef())
return true;
if (b->GetModelDef() < a->GetModelDef())
return false;
if (a->GetMaterial().GetDiffuseTexture() < b->GetMaterial().GetDiffuseTexture())
return true;
if (b->GetMaterial().GetDiffuseTexture() < a->GetMaterial().GetDiffuseTexture())
return false;
return false;
}
m->submissions = 0;
};
m->phase = BMRSubmit;
}
// Return whether we models have been submitted this frame
bool BatchModelRenderer::HaveSubmissions()
struct SMRCompareSortByDistItem
{
return m->submissions != 0;
}
bool operator()(const SMRSortByDistItem& a, const SMRSortByDistItem& b)
{
// Prefer items with greater distance, so we draw back-to-front
return (a.dist > b.dist);
// (Distances will almost always be distinct, so we don't need to bother
// tie-breaking on modeldef/texture/etc)
}
};
// Render models, outer loop for multi-passing
void BatchModelRenderer::Render(const RenderModifierPtr& modifier, int flags)
struct SMRMaterialBucketKey
{
ENSURE(m->phase == BMRRender);
SMRMaterialBucketKey(CStrIntern effect, const CShaderDefines& defines)
: effect(effect), defines(defines) { }
if (!HaveSubmissions())
CStrIntern effect;
const CShaderDefines& defines;
bool operator==(const SMRMaterialBucketKey& b) const
{
return (effect == b.effect && defines == b.defines);
}
private:
SMRMaterialBucketKey& operator=(const SMRMaterialBucketKey&);
};
struct SMRMaterialBucketKeyHash
{
size_t operator()(const SMRMaterialBucketKey& key) const
{
size_t hash = 0;
boost::hash_combine(hash, key.effect.GetHash());
boost::hash_combine(hash, key.defines.GetHash());
return hash;
}
};
struct SMRTechBucket
{
CShaderTechniquePtr tech;
CModel** models;
size_t numModels;
// Model list is stored as pointers, not as a std::vector,
// so that sorting lists of this struct is fast
};
struct SMRCompareTechBucket
{
bool operator()(const SMRTechBucket& a, const SMRTechBucket& b)
{
return a.tech < b.tech;
}
};
void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int flags)
{
if (m->submissions.empty())
return;
int pass = 0;
CMatrix3D worldToCam;
g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam);
/*
* Rendering approach:
*
* m->submissions contains the list of CModels to render.
*
* The data we need to render a model is:
* - CShaderTechnique
* - CTexture
* - CModelDef (mesh data)
* - CModel (model instance data)
*
* For efficient rendering, we need to batch the draw calls to minimise state changes.
* (Texture changes are assumed to be cheaper than binding new mesh data,
* and shader changes are assumed to be most expensive.)
* First, group all models that share a technique to render them together.
* Within those groups, sub-group by CModelDef.
* Within those sub-groups, sub-sub-group by CTexture.
*
* Alpha-blended models have to be sorted by distance from camera,
* then we can batch as long as the order is preserved.
* Non-alpha-blended models can be arbitrarily reordered to maximise batching.
*
* For each model, the CShaderTechnique is derived from:
* - The current global 'context' defines
* - The CModel's material's defines
* - The CModel's material's shader effect name
*
* There are a smallish number of materials, and a smaller number of techniques.
*
* To minimise technique lookups, we first group models by material,
* in 'materialBuckets' (a hash table).
*
* For each material bucket we then look up the appropriate shader technique.
* If the technique requires sort-by-distance, the model is added to the
* 'sortByDistItems' list with its computed distance.
* Otherwise, the bucket's list of models is sorted by modeldef+texture,
* then the technique and model list is added to 'techBuckets'.
*
* 'techBuckets' is then sorted by technique, to improve batching when multiple
* materials map onto the same technique.
*
* (Note that this isn't perfect batching: we don't sort across models in
* multiple buckets that share a technique. In practice that shouldn't reduce
* batching much (we rarely have one mesh used with multiple materials),
* and it saves on copying and lets us sort smaller lists.)
*
* Extra tech buckets are added for the sorted-by-distance models without reordering.
* Finally we render by looping over each tech bucket, then looping over the model
* list in each, rebinding the GL state whenever it changes.
*/
typedef boost::unordered_map<SMRMaterialBucketKey, std::vector<CModel*>, SMRMaterialBucketKeyHash> MaterialBuckets_t;
MaterialBuckets_t materialBuckets;
do
{
int streamflags = modifier->BeginPass(pass);
PROFILE3("bucketing by material");
m->vertexRenderer->BeginPass(streamflags);
m->RenderAllModels(modifier, flags, pass, streamflags);
m->vertexRenderer->EndPass(streamflags);
} while(!modifier->EndPass(pass++));
}
// Render one frame worth of models
void BatchModelRendererInternals::RenderAllModels(
const RenderModifierPtr& modifier, int filterflags,
int pass, int streamflags)
{
CShaderProgramPtr shader = modifier->GetShader(pass);
for(BMRModelDefTracker* mdeftracker = submissions; mdeftracker; mdeftracker = mdeftracker->m_Next)
{
vertexRenderer->PrepareModelDef(shader, streamflags, mdeftracker->m_ModelDef.lock());
for(size_t idx = 0; idx < mdeftracker->m_Slots; ++idx)
for (size_t i = 0; i < m->submissions.size(); ++i)
{
BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx];
CModel* model = m->submissions[i];
modifier->PrepareTexture(pass, bmrdata->GetModel()->GetTexture());
SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), model->GetMaterial().GetShaderDefines());
std::vector<CModel*>& bucketItems = materialBuckets[key];
bucketItems.push_back(model);
}
}
for(; bmrdata; bmrdata = bmrdata->m_Next)
std::vector<SMRSortByDistItem> sortByDistItems;
std::vector<CShaderTechniquePtr> sortByDistTechs;
// indexed by sortByDistItems[i].techIdx
// (which stores indexes instead of CShaderTechniquePtr directly
// to avoid the shared_ptr copy cost when sorting; maybe it'd be better
// if we just stored raw CShaderTechnique* and assumed the shader manager
// will keep it alive long enough)
std::vector<SMRTechBucket> techBuckets;
{
PROFILE3("processing material buckets");
for (MaterialBuckets_t::iterator it = materialBuckets.begin(); it != materialBuckets.end(); ++it)
{
CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(it->first.effect, context, it->first.defines);
if (tech->GetSortByDistance())
{
CModel* model = bmrdata->GetModel();
// Add the tech into a vector so we can index it
// (There might be duplicates in this list, but that doesn't really matter)
if (sortByDistTechs.empty() || sortByDistTechs.back() != tech)
sortByDistTechs.push_back(tech);
size_t techIdx = sortByDistTechs.size()-1;
ENSURE(bmrdata->GetKey() == this);
// Add each model into sortByDistItems
for (size_t i = 0; i < it->second.size(); ++i)
{
SMRSortByDistItem itemWithDist;
itemWithDist.techIdx = techIdx;
if (filterflags && !(model->GetFlags() & filterflags))
continue;
CModel* model = it->second[i];
itemWithDist.model = model;
modifier->PrepareModel(pass, model);
vertexRenderer->RenderModel(shader, streamflags, model, bmrdata->m_Data);
CVector3D modelpos = model->GetTransform().GetTranslation();
itemWithDist.dist = worldToCam.Transform(modelpos).Z;
sortByDistItems.push_back(itemWithDist);
}
}
else
{
// Sort model list by modeldef+texture, for batching
std::sort(it->second.begin(), it->second.end(), SMRBatchModel());
// Add a tech bucket pointing at this model list
SMRTechBucket techBucket = { tech, &it->second[0], it->second.size() };
techBuckets.push_back(techBucket);
}
}
}
}
void BatchModelRenderer::Filter(CModelFilter& filter, int passed, int flags)
{
if (!HaveSubmissions())
return;
m->FilterAllModels(filter, passed, flags);
}
// Recompute filter flags
void BatchModelRendererInternals::FilterAllModels(CModelFilter& filter, int passed, int filterflags)
{
for(BMRModelDefTracker* mdeftracker = submissions; mdeftracker; mdeftracker = mdeftracker->m_Next)
{
for(size_t idx = 0; idx < mdeftracker->m_Slots; ++idx)
{
BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx];
for(; bmrdata; bmrdata = bmrdata->m_Next)
{
CModel* model = bmrdata->GetModel();
if (filterflags && !(model->GetFlags() & filterflags))
continue;
PROFILE3("sorting tech buckets");
// Sort by technique, for better batching
std::sort(techBuckets.begin(), techBuckets.end(), SMRCompareTechBucket());
}
if (filter.Filter(model))
model->SetFlags(model->GetFlags() | passed);
else
model->SetFlags(model->GetFlags() & ~passed);
// List of models corresponding to sortByDistItems[i].model
// (This exists primarily because techBuckets wants a CModel**;
// we could avoid the cost of copying into this list by adding
// a stride length into techBuckets and not requiring contiguous CModel*s)
std::vector<CModel*> sortByDistModels;
if (!sortByDistItems.empty())
{
{
PROFILE3("sorting items by dist");
std::sort(sortByDistItems.begin(), sortByDistItems.end(), SMRCompareSortByDistItem());
}
{
PROFILE3("batching dist-sorted items");
sortByDistModels.reserve(sortByDistItems.size());
// Find runs of distance-sorted models that share a technique,
// and create a new tech bucket for each run
size_t start = 0; // start of current run
size_t currentTechIdx = sortByDistItems[start].techIdx;
for (size_t end = 0; end < sortByDistItems.size(); ++end)
{
sortByDistModels.push_back(sortByDistItems[end].model);
size_t techIdx = sortByDistItems[end].techIdx;
if (techIdx != currentTechIdx)
{
// Start of a new run - push the old run into a new tech bucket
SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], end-start };
techBuckets.push_back(techBucket);
start = end;
currentTechIdx = techIdx;
}
}
// Add the tech bucket for the final run
SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], sortByDistItems.size()-start };
techBuckets.push_back(techBucket);
}
}
{
PROFILE3("rendering bucketed submissions");
size_t idxTechStart = 0;
while (idxTechStart < techBuckets.size())
{
CShaderTechniquePtr currentTech = techBuckets[idxTechStart].tech;
// Find runs [idxTechStart, idxTechEnd) in techBuckets of the same technique
size_t idxTechEnd;
for (idxTechEnd = idxTechStart + 1; idxTechEnd < techBuckets.size(); ++idxTechEnd)
{
if (techBuckets[idxTechEnd].tech != currentTech)
break;
}
// For each of the technique's passes, render all the models in this run
for (int pass = 0; pass < currentTech->GetNumPasses(); ++pass)
{
currentTech->BeginPass(pass);
const CShaderProgramPtr& shader = currentTech->GetShader(pass);
int streamflags = shader->GetStreamFlags();
modifier->BeginPass(shader);
m->vertexRenderer->BeginPass(streamflags);
CTexture* currentTex = NULL;
CModelDef* currentModeldef = NULL;
// (Texture needs to be rebound after binding a new shader, so we
// can't move currentTex outside of this loop to reduce state changes)
for (size_t idx = idxTechStart; idx < idxTechEnd; ++idx)
{
CModel** models = techBuckets[idx].models;
size_t numModels = techBuckets[idx].numModels;
for (size_t i = 0; i < numModels; ++i)
{
CModel* model = models[i];
if (flags && !(model->GetFlags() & flags))
continue;
// Bind texture when it changes
CTexture* newTex = model->GetMaterial().GetDiffuseTexture().get();
if (newTex != currentTex)
{
currentTex = newTex;
modifier->PrepareTexture(shader, *currentTex);
}
// Bind modeldef when it changes
CModelDef* newModeldef = model->GetModelDef().get();
if (newModeldef != currentModeldef)
{
currentModeldef = newModeldef;
m->vertexRenderer->PrepareModelDef(shader, streamflags, *currentModeldef);
}
modifier->PrepareModel(shader, model);
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
m->vertexRenderer->RenderModel(shader, streamflags, model, rdata);
}
}
m->vertexRenderer->EndPass(streamflags);
currentTech->EndPass(pass);
}
idxTechStart = idxTechEnd;
}
}
}
void ShaderModelRenderer::Filter(CModelFilter& filter, int passed, int flags)
{
for (size_t i = 0; i < m->submissions.size(); ++i)
{
CModel* model = m->submissions[i];
if (flags && !(model->GetFlags() & flags))
continue;
if (filter.Filter(model))
model->SetFlags(model->GetFlags() | passed);
else
model->SetFlags(model->GetFlags() & ~passed);
}
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -40,7 +40,11 @@ typedef shared_ptr<LitRenderModifier> LitRenderModifierPtr;
class ModelVertexRenderer;
typedef shared_ptr<ModelVertexRenderer> ModelVertexRendererPtr;
class ModelRenderer;
typedef shared_ptr<ModelRenderer> ModelRendererPtr;
class CModel;
class CShaderDefines;
class CModelFilter
{
@ -66,7 +70,7 @@ public:
class CModelRData : public CRenderData
{
public:
CModelRData(const void* key, CModel* model) : m_Key(key), m_Model(model) { }
CModelRData(const void* key) : m_Key(key) { }
/**
* GetKey: Retrieve the key that can be used to identify the
@ -76,20 +80,9 @@ public:
*/
const void* GetKey() const { return m_Key; }
/**
* GetModel: Retrieve the model that this render data object
* belongs to.
*
* @return The model pointer that was passed to the constructor.
*/
CModel* GetModel() const { return m_Model; }
private:
/// The key for model renderer identification
const void* m_Key;
/// The model this object was created for
CModel* m_Model;
};
@ -156,13 +149,6 @@ public:
*/
virtual void EndFrame() = 0;
/**
* HaveSubmissions: Return whether any models have been submitted this frame.
*
* @return true if models have been submitted, false otherwise.
*/
virtual bool HaveSubmissions() = 0;
/**
* Render: Render submitted models, using the given RenderModifier to setup
* the fragment stage.
@ -180,7 +166,7 @@ public:
* If flags is non-zero, only models that contain flags in their
* CModel::GetFlags() are rendered.
*/
virtual void Render(const RenderModifierPtr& modifier, int flags) = 0;
virtual void Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int flags) = 0;
/**
* Filter: Filter submitted models, setting the passed flags on any models
@ -270,35 +256,32 @@ public:
};
struct BatchModelRendererInternals;
struct ShaderModelRendererInternals;
/**
* Class BatchModelRenderer: Model renderer that sorts submitted models
* by CModelDef and texture for batching, and uses a ModelVertexRenderer
* (e.g. FixedFunctionModelRenderer) to manage model vertices.
*
* @note Deriving from this class is highly discouraged. Specialize
* using ModelVertexRendererPtr and RenderModifier instead.
* Implementation of ModelRenderer that loads the appropriate shaders for
* rendering each model, and that batches by shader (and by mesh and texture).
*
* Note that the term "Shader" is somewhat misleading, as this handled
* fixed-function rendering using the same API as real GLSL/ARB shaders.
*/
class BatchModelRenderer : public ModelRenderer
class ShaderModelRenderer : public ModelRenderer
{
friend struct BatchModelRendererInternals;
friend struct ShaderModelRendererInternals;
public:
BatchModelRenderer(ModelVertexRendererPtr vertexrender);
virtual ~BatchModelRenderer();
ShaderModelRenderer(ModelVertexRendererPtr vertexrender);
virtual ~ShaderModelRenderer();
// Batching implementations
virtual void Submit(CModel* model);
virtual void PrepareModels();
virtual void EndFrame();
virtual bool HaveSubmissions();
virtual void Render(const RenderModifierPtr& modifier, int flags);
virtual void Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int flags);
virtual void Filter(CModelFilter& filter, int passed, int flags);
private:
BatchModelRendererInternals* m;
ShaderModelRendererInternals* m;
};
#endif // INCLUDED_MODELRENDERER

View File

@ -27,6 +27,7 @@
#include "graphics/ShaderProgram.h"
class CModel;
class CModelRData;
/**
* Class ModelVertexRenderer: Normal ModelRenderer implementations delegate
@ -47,19 +48,19 @@ public:
* CreateModelData: Create internal data for one model.
*
* ModelRenderer implementations must call this once for every
* model that will later be rendered.
* model that will later be rendered, with @p key set to a value
* that's unique to that ModelRenderer.
*
* ModelVertexRenderer implementations should use this function to
* create per-CModel and per-CModelDef data like vertex arrays.
*
* @param key An opaque pointer to pass to the CModelRData constructor
* @param model The model.
*
* @return An opaque pointer that will be passed to other
* ModelVertexRenderer functions whenever the CModel is passed again.
* Note that returning 0 is allowed and does not indicate an error
* condition.
* @return A new CModelRData that will be passed into other
* ModelVertexRenderer functions whenever the same CModel is used again.
*/
virtual void* CreateModelData(CModel* model) = 0;
virtual CModelRData* CreateModelData(const void* key, CModel* model) = 0;
/**
@ -81,25 +82,7 @@ public:
* the frame. The value is the same as the value of the model's
* CRenderData::m_UpdateFlags.
*/
virtual void UpdateModelData(CModel* model, void* data, int updateflags) = 0;
/**
* DestroyModelData: Release all per-model data that has been allocated
* by CreateModelData or UpdateModelData.
*
* ModelRenderer implementations must ensure that this function is
* called exactly once for every call to CreateModelData. This can be
* achieved by deriving from CModelRData and calling DestroyModelData
* in the derived class' destructor.
*
* ModelVertexRenderer implementations need not track the CModel
* instances for which per-model data has been created.
*
* @param model The model.
* @param data Private data as returned by CreateModelData.
*/
virtual void DestroyModelData(CModel* model, void* data) = 0;
virtual void UpdateModelData(CModel* model, CModelRData* data, int updateflags) = 0;
/**
@ -148,7 +131,7 @@ public:
* BeginPass.
* @param def The model definition.
*/
virtual void PrepareModelDef(CShaderProgramPtr& shader, int streamflags, const CModelDefPtr& def) = 0;
virtual void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def) = 0;
/**
@ -170,7 +153,7 @@ public:
* that use the same CModelDef object and the same texture must
* succeed.
*/
virtual void RenderModel(CShaderProgramPtr& shader, int streamflags, CModel* model, void* data) = 0;
virtual void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data) = 0;
};

View File

@ -201,6 +201,8 @@ void OverlayRenderer::RenderOverlaysAfterWater()
return;
#endif
ogl_WarnIfError();
if (!m->texlines.empty())
{
glEnable(GL_TEXTURE_2D);
@ -213,13 +215,13 @@ void OverlayRenderer::RenderOverlaysAfterWater()
else
shaderName = "fixed:overlayline";
std::map<CStr, CStr> defAlwaysVisible;
defAlwaysVisible.insert(std::make_pair(CStr("IGNORE_LOS"), CStr("1")));
CShaderDefines defAlwaysVisible;
defAlwaysVisible.Add("IGNORE_LOS", "1");
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram(shaderName));
CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram(shaderName, CShaderDefines()));
CShaderProgramPtr shaderTexLineAlwaysVisible(shaderManager.LoadProgram(shaderName, defAlwaysVisible));
// ----------------------------------------------------------------------------------------

View File

@ -74,7 +74,7 @@ struct SortEmitterDistance
CMatrix3D worldToCam;
};
void ParticleRenderer::PrepareForRendering()
void ParticleRenderer::PrepareForRendering(const CShaderDefines& context)
{
PROFILE3("prepare particles");
@ -86,8 +86,8 @@ void ParticleRenderer::PrepareForRendering()
// RenderParticles will never be called so it's safe to leave the shaders as null
if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
{
m->shader = g_Renderer.GetShaderManager().LoadEffect("particle");
m->shaderSolid = g_Renderer.GetShaderManager().LoadEffect("particle_solid");
m->shader = g_Renderer.GetShaderManager().LoadEffect(CStrIntern("particle"), context, CShaderDefines());
m->shaderSolid = g_Renderer.GetShaderManager().LoadEffect(CStrIntern("particle_solid"), context, CShaderDefines());
}
}

View File

@ -43,7 +43,7 @@ public:
* Must be called after all Submit calls for a frame, and before
* any rendering calls.
*/
void PrepareForRendering();
void PrepareForRendering(const CShaderDefines& context);
/**
* Reset the list of submitted overlays.

View File

@ -361,34 +361,34 @@ void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
CVector3D normal;
bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
size_t index = blendVertices.size();
terrain->CalcPosition(gx, gz, dst.m_Position);
terrain->CalcNormal(gx, gz, normal);
dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
blendVertices.push_back(dst);
terrain->CalcPosition(gx + 1, gz, dst.m_Position);
terrain->CalcNormal(gx + 1, gz, normal);
dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
blendVertices.push_back(dst);
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_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
blendVertices.push_back(dst);
terrain->CalcPosition(gx, gz + 1, dst.m_Position);
terrain->CalcNormal(gx, gz + 1, normal);
dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
blendVertices.push_back(dst);
@ -529,7 +529,7 @@ void CPatchRData::BuildVertices()
CTerrain* terrain=m_Patch->m_Parent;
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
// build vertices
for (ssize_t j=0;j<vsize;j++) {
@ -547,7 +547,7 @@ void CPatchRData::BuildVertices()
CVector3D normal;
terrain->CalcNormal(ix,iz,normal);
vertices[v].m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
vertices[v].m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
}
}

View File

@ -1,294 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Implementation of player colour RenderModifiers.
*/
#include "precompiled.h"
#include "renderer/Renderer.h"
#include "renderer/PlayerRenderer.h"
#include "renderer/ShadowMap.h"
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/TextureManager.h"
#if !CONFIG2_GLES
///////////////////////////////////////////////////////////////////////////////////////////////////
// FastPlayerColorRender
FastPlayerColorRender::FastPlayerColorRender()
{
ENSURE(ogl_max_tex_units >= 3);
}
FastPlayerColorRender::~FastPlayerColorRender()
{
}
bool FastPlayerColorRender::IsAvailable()
{
return (ogl_max_tex_units >= 3);
}
int FastPlayerColorRender::BeginPass(int pass)
{
ENSURE(pass == 0);
// Fast player color uses a single pass with three texture environments
// Note: This uses ARB_texture_env_crossbar (which is checked in GameSetup)
//
// We calculate: Result = Color*Texture*(PlayerColor*(1-Texture.a) + 1.0*Texture.a)
// Algebra gives us:
// Result = (1 - ((1 - PlayerColor) * (1 - Texture.a)))*Texture*Color
// TexEnv #0
pglActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE_MINUS_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// TexEnv #1
pglActiveTextureARB(GL_TEXTURE0+1);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// TexEnv #2
pglActiveTextureARB(GL_TEXTURE0+2);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
pglActiveTextureARB(GL_TEXTURE0);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool FastPlayerColorRender::EndPass(int UNUSED(pass))
{
// Restore state
pglActiveTextureARB(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE2);
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0);
return true;
}
void FastPlayerColorRender::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(2);
texture->Bind(1);
texture->Bind(0);
}
void FastPlayerColorRender::PrepareModel(int UNUSED(pass), CModel* model)
{
// Get the player color
SMaterialColor colour = model->GetMaterial().GetObjectColor();
float* color = &colour.r; // because it's stored RGBA
// Set the texture environment color the player color
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// SlowPlayerColorRender
SlowPlayerColorRender::SlowPlayerColorRender()
{
}
SlowPlayerColorRender::~SlowPlayerColorRender()
{
}
int SlowPlayerColorRender::BeginPass(int pass)
{
// We calculate: Result = (Color*Texture)*Texture.a + (Color*Texture*PlayerColor)*(1-Texture.a)
// Modulation is done via texture environments, the final interpolation is done via blending
if (pass == 0)
{
// TexEnv #0
pglActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// Don't care about alpha; set it to something harmless
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Render it!
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
else
{
// TexEnv #0
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// Alpha = Opacity of non-player colored layer
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// TexEnv #1
pglActiveTextureARB(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// Pass alpha unchanged
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
pglActiveTextureARB(GL_TEXTURE0);
// Setup blending
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_LESS, 1.0);
glDepthMask(0);
// Render it!
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
}
bool SlowPlayerColorRender::EndPass(int pass)
{
if (pass == 0)
return false; // need two passes
// Restore state
pglActiveTextureARB(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0);
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glDepthMask(1);
return true;
}
void SlowPlayerColorRender::PrepareTexture(int pass, CTexturePtr& texture)
{
if (pass == 1)
texture->Bind(1);
texture->Bind(0);
}
void SlowPlayerColorRender::PrepareModel(int pass, CModel* model)
{
if (pass == 1)
{
// Get the player color
SMaterialColor colour = model->GetMaterial().GetObjectColor();
float* color = &colour.r; // because it's stored RGBA
// Set the texture environment color the player color
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// SolidPlayerColorRender
SolidPlayerColorRender::SolidPlayerColorRender()
{
}
SolidPlayerColorRender::~SolidPlayerColorRender()
{
}
int SolidPlayerColorRender::BeginPass(int UNUSED(pass))
{
ogl_tex_bind(0, 0);
return STREAM_POS;
}
bool SolidPlayerColorRender::EndPass(int UNUSED(pass))
{
return true;
}
void SolidPlayerColorRender::PrepareTexture(int UNUSED(pass), CTexturePtr& UNUSED(texture))
{
}
void SolidPlayerColorRender::PrepareModel(int UNUSED(pass), CModel* model)
{
// Get the player color
SMaterialColor colour = model->GetMaterial().GetPlayerColor();
// Send the player color
glColor3f(colour.r, colour.g, colour.b);
}
#endif // !CONFIG2_GLES

View File

@ -1,100 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* RenderModifier for player color rendering, to be used with e.g.
* FixedFunctionModelRenderer
*/
#ifndef INCLUDED_PLAYERRENDERER
#define INCLUDED_PLAYERRENDERER
#include "RenderModifiers.h"
#if !CONFIG2_GLES
/**
* Class FastPlayerColorRender: Render models fully textured and lit
* plus player color in a single pass using multi-texturing (at least 3 TMUs
* required).
*/
class FastPlayerColorRender : public RenderModifier
{
public:
FastPlayerColorRender();
~FastPlayerColorRender();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
/**
* IsAvailable: Determines whether this RenderModifier can be used
* given the OpenGL implementation specific limits.
*
* @note Do not attempt to construct a FastPlayerColorRender object
* when IsAvailable returns false.
*
* @return true if the OpenGL implementation can support this
* RenderModifier.
*/
static bool IsAvailable();
};
/**
* Class SlowPlayerColorRender: Render models fully textured and lit
* plus player color using multi-pass.
*
* It has the same visual result as FastPlayerColorRender (except for
* potential precision issues due to the multi-passing).
*/
class SlowPlayerColorRender : public RenderModifier
{
public:
SlowPlayerColorRender();
~SlowPlayerColorRender();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
/**
* Render all models using their player color without lighting.
*/
class SolidPlayerColorRender : public RenderModifier
{
public:
SolidPlayerColorRender();
~SolidPlayerColorRender();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
#endif // !CONFIG2_GLES
#endif

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -40,18 +40,6 @@
#include <boost/algorithm/string.hpp>
///////////////////////////////////////////////////////////////////////////////////////////////
// RenderModifier implementation
void RenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
}
CShaderProgramPtr RenderModifier::GetShader(int UNUSED(pass))
{
return CShaderProgramPtr();
}
///////////////////////////////////////////////////////////////////////////////////////////////
// LitRenderModifier implementation
@ -76,116 +64,18 @@ void LitRenderModifier::SetLightEnv(const CLightEnv* lightenv)
m_LightEnv = lightenv;
}
#if !CONFIG2_GLES
///////////////////////////////////////////////////////////////////////////////////////////////
// PlainRenderModifier implementation
PlainRenderModifier::PlainRenderModifier()
{
}
PlainRenderModifier::~PlainRenderModifier()
{
}
int PlainRenderModifier::BeginPass(int pass)
{
ENSURE(pass == 0);
// set up texture environment for base pass - modulate texture and primary color
pglActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
// pass one through as alpha; transparent textures handled specially by TransparencyRenderer
// (gl_constant means the colour comes from the gl_texture_env_color)
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
float color[] = { 1.0, 1.0, 1.0, 1.0 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool PlainRenderModifier::EndPass(int UNUSED(pass))
{
// We didn't modify blend state or higher texenvs, so we don't have
// to reset OpenGL state here.
return true;
}
void PlainRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(0);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// SolidColorRenderModifier implementation
SolidColorRenderModifier::SolidColorRenderModifier()
{
}
SolidColorRenderModifier::~SolidColorRenderModifier()
{
}
int SolidColorRenderModifier::BeginPass(int UNUSED(pass))
{
ogl_tex_bind(0, 0);
return STREAM_POS;
}
bool SolidColorRenderModifier::EndPass(int UNUSED(pass))
{
return true;
}
void SolidColorRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& UNUSED(texture))
{
}
void SolidColorRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
}
#endif // !CONFIG2_GLES
///////////////////////////////////////////////////////////////////////////////////////////////
// ShaderRenderModifier implementation
ShaderRenderModifier::ShaderRenderModifier(const CShaderTechniquePtr& technique) :
m_Technique(technique)
ShaderRenderModifier::ShaderRenderModifier()
{
}
ShaderRenderModifier::~ShaderRenderModifier()
void ShaderRenderModifier::BeginPass(const CShaderProgramPtr& shader)
{
}
int ShaderRenderModifier::BeginPass(int pass)
{
m_Technique->BeginPass(pass);
CShaderProgramPtr shader = m_Technique->GetShader(pass);
shader->Uniform("transform", g_Renderer.GetViewCamera().GetViewProjection());
if (GetShadowMap() && shader->HasTexture("shadowTex"))
if (GetShadowMap() && shader->GetTextureBinding("shadowTex").Active())
{
shader->BindTexture("shadowTex", GetShadowMap()->GetTexture());
shader->Uniform("shadowTransform", GetShadowMap()->GetTextureMatrix());
@ -202,7 +92,7 @@ int ShaderRenderModifier::BeginPass(int pass)
shader->Uniform("sunColor", GetLightEnv()->m_SunColor);
}
if (shader->HasTexture("losTex"))
if (shader->GetTextureBinding("losTex").Active())
{
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
shader->BindTexture("losTex", los.GetTexture());
@ -214,33 +104,17 @@ int ShaderRenderModifier::BeginPass(int pass)
m_BindingShadingColor = shader->GetUniformBinding("shadingColor");
m_BindingObjectColor = shader->GetUniformBinding("objectColor");
m_BindingPlayerColor = shader->GetUniformBinding("playerColor");
return shader->GetStreamFlags();
m_BindingBaseTex = shader->GetTextureBinding("baseTex");
}
bool ShaderRenderModifier::EndPass(int pass)
void ShaderRenderModifier::PrepareTexture(const CShaderProgramPtr& shader, CTexture& texture)
{
m_Technique->EndPass(pass);
return (pass >= m_Technique->GetNumPasses()-1);
if (m_BindingBaseTex.Active())
shader->BindTexture(m_BindingBaseTex, texture.GetHandle());
}
CShaderProgramPtr ShaderRenderModifier::GetShader(int pass)
void ShaderRenderModifier::PrepareModel(const CShaderProgramPtr& shader, CModel* model)
{
return m_Technique->GetShader(pass);
}
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());
@ -251,5 +125,5 @@ void ShaderRenderModifier::PrepareModel(int pass, CModel* model)
shader->Uniform(m_BindingObjectColor, model->GetMaterial().GetObjectColor());
if (m_BindingPlayerColor.Active())
shader->Uniform(m_BindingPlayerColor, model->GetMaterial().GetPlayerColor());
shader->Uniform(m_BindingPlayerColor, g_Game->GetPlayerColour(model->GetPlayerID()));
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -19,6 +19,10 @@
* RenderModifiers can affect the fragment stage behaviour of some
* ModelRenderers. This file defines some common RenderModifiers in
* addition to the base class.
*
* TODO: See comment in CRendererInternals::Models - we no longer use multiple
* subclasses of RenderModifier, so most of the stuff here is unnecessary
* abstraction which should probably be cleaned up.
*/
#ifndef INCLUDED_RENDERMODIFIERS
@ -56,26 +60,7 @@ public:
* @return The streamflags that indicate which vertex components
* are required by the fragment stages (see STREAM_XYZ constants).
*/
virtual int BeginPass(int pass) = 0;
/**
* EndPass: Cleanup OpenGL state after the given pass. This function
* may indicate that additional passes are needed.
*
* Must be implemented by derived classes.
*
* @param pass The current pass number (pass == 0 is the first pass)
*
* @return true if rendering is complete, false if an additional pass
* is needed. If false is returned, BeginPass is then called with an
* increased pass number.
*/
virtual bool EndPass(int pass) = 0;
/**
* Return the shader for the given pass, or a null pointer if none.
*/
virtual CShaderProgramPtr GetShader(int pass);
virtual void BeginPass(const CShaderProgramPtr& shader) = 0;
/**
* PrepareTexture: Called before rendering models that use the given
@ -86,7 +71,7 @@ public:
* @param pass The current pass number (pass == 0 is the first pass)
* @param texture The texture used by subsequent models
*/
virtual void PrepareTexture(int pass, CTexturePtr& texture) = 0;
virtual void PrepareTexture(const CShaderProgramPtr& shader, CTexture& texture) = 0;
/**
* PrepareModel: Called before rendering the given model.
@ -96,7 +81,7 @@ public:
* @param pass The current pass number (pass == 0 is the first pass)
* @param model The model that is about to be rendered.
*/
virtual void PrepareModel(int pass, CModel* model);
virtual void PrepareModel(const CShaderProgramPtr& shader, CModel* model) = 0;
};
@ -138,70 +123,25 @@ private:
const CLightEnv* m_LightEnv;
};
#if !CONFIG2_GLES
/**
* Class PlainRenderModifier: RenderModifier that simply uses opaque textures
* modulated by primary color. It is used for normal, no-frills models.
*/
class PlainRenderModifier : public RenderModifier
{
public:
PlainRenderModifier();
~PlainRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
};
/**
* Class SolidColorRenderModifier: Render all models using the same
* solid color without lighting.
*
* You have to specify the color using a glColor*() calls before rendering.
*/
class SolidColorRenderModifier : public RenderModifier
{
public:
SolidColorRenderModifier();
~SolidColorRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
#endif // !CONFIG2_GLES
/**
* A RenderModifier that can be used with any CShaderTechnique.
* Uniforms and textures get set appropriately.
* A RenderModifier that sets uniforms and textures appropriately for rendering models.
*/
class ShaderRenderModifier : public LitRenderModifier
{
public:
ShaderRenderModifier(const CShaderTechniquePtr& technique);
~ShaderRenderModifier();
ShaderRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
CShaderProgramPtr GetShader(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
void BeginPass(const CShaderProgramPtr& shader);
void PrepareTexture(const CShaderProgramPtr& shader, CTexture& texture);
void PrepareModel(const CShaderProgramPtr& shader, CModel* model);
private:
CShaderTechniquePtr m_Technique;
CShaderProgram::Binding m_BindingInstancingTransform;
CShaderProgram::Binding m_BindingShadingColor;
CShaderProgram::Binding m_BindingObjectColor;
CShaderProgram::Binding m_BindingPlayerColor;
CShaderProgram::Binding m_BindingBaseTex;
};
#endif // INCLUDED_RENDERMODIFIERS

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -37,6 +37,7 @@ class CPatch;
class CMaterial;
class CModel;
class CLightEnv;
class CShaderDefines;
class RenderPathVertexShader;
class WaterManager;
@ -44,6 +45,7 @@ class SkyManager;
class CTextureManager;
class CShaderManager;
class CParticleManager;
class CMaterialManager;
// rendering modes
enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES };
@ -75,7 +77,6 @@ public:
OPT_NOVBO,
OPT_SHADOWS,
OPT_FANCYWATER,
OPT_LODBIAS,
OPT_SHADOWPCF
};
@ -116,7 +117,6 @@ public:
bool m_NoVBO;
bool m_Shadows;
bool m_FancyWater;
float m_LodBias;
RenderPath m_RenderPath;
bool m_ShadowAlphaFix;
bool m_ARBProgramShadow;
@ -147,7 +147,6 @@ public:
// set/get boolean renderer option
void SetOptionBool(enum Option opt,bool value);
bool GetOptionBool(enum Option opt) const;
void SetOptionFloat(enum Option opt, float val);
void SetRenderPath(RenderPath rp);
RenderPath GetRenderPath() const { return m_Options.m_RenderPath; }
static CStr GetRenderPathName(RenderPath rp);
@ -265,16 +264,7 @@ public:
CParticleManager& GetParticleManager();
/**
* SetFastPlayerColor: Tell the renderer which path to take for
* player colored models. Both paths should provide the same visual
* quality, however the slow path runs on older hardware using multi-pass.
*
* @param fast true if the fast path should be used from now on. If fast
* is true but the OpenGL implementation does not support it, a warning
* is printed and the slow path is used instead.
*/
void SetFastPlayerColor(bool fast);
CMaterialManager& GetMaterialManager();
/**
* GetCapabilities: Return which OpenGL capabilities are available and enabled.
@ -283,8 +273,6 @@ public:
*/
const Caps& GetCapabilities() const { return m_Caps; }
bool GetDisableCopyShadow() const { return m_DisableCopyShadow; }
static void ScriptingInit();
protected:
@ -298,7 +286,7 @@ protected:
friend class SortModelRenderer;
friend class RenderPathVertexShader;
friend class HWLightingModelRenderer;
friend class ShaderModelRenderer;
friend class ShaderModelVertexRenderer;
friend class InstancingModelRenderer;
friend class ShaderInstancingModelRenderer;
friend class TerrainRenderer;
@ -306,8 +294,6 @@ protected:
// scripting
// TODO: Perhaps we could have a version of AddLocalProperty for function-driven
// properties? Then we could hide these function in the private implementation class.
jsval JSI_GetFastPlayerColor(JSContext*);
void JSI_SetFastPlayerColor(JSContext* ctx, jsval newval);
jsval JSI_GetRenderPath(JSContext*);
void JSI_SetRenderPath(JSContext* ctx, jsval newval);
jsval JSI_GetDepthTextureBits(JSContext*);
@ -337,22 +323,22 @@ protected:
void RenderSubmissions();
// patch rendering stuff
void RenderPatches(const CFrustum* frustum = 0);
void RenderPatches(const CShaderDefines& context, const CFrustum* frustum = 0);
// model rendering stuff
void RenderModels(const CFrustum* frustum = 0);
void RenderTransparentModels(ETransparentMode transparentMode, const CFrustum* frustum = 0);
void RenderModels(const CShaderDefines& context, const CFrustum* frustum = 0);
void RenderTransparentModels(const CShaderDefines& context, ETransparentMode transparentMode, const CFrustum* frustum = 0);
void RenderSilhouettes();
void RenderSilhouettes(const CShaderDefines& context);
void RenderParticles();
// shadow rendering stuff
void RenderShadowMap();
void RenderShadowMap(const CShaderDefines& context);
// render water reflection and refraction textures
SScreenRect RenderReflections(const CBoundingBoxAligned& scissor);
SScreenRect RenderRefractions(const CBoundingBoxAligned& scissor);
SScreenRect RenderReflections(const CShaderDefines& context, const CBoundingBoxAligned& scissor);
SScreenRect RenderRefractions(const CShaderDefines& context, const CBoundingBoxAligned& scissor);
// debugging
void DisplayFrustum();
@ -411,9 +397,6 @@ protected:
// per-frame renderer stats
Stats m_Stats;
/// If false, use a multipass fallback for player colors.
bool m_FastPlayerColor;
/**
* m_WaterManager: the WaterManager object used for water textures and settings
* (e.g. water color, water height)
@ -425,15 +408,6 @@ protected:
*/
SkyManager* m_SkyManager;
/**
* m_SortAllTransparent: If true, all transparent models are
* rendered using the TransparencyRenderer which performs sorting.
*
* Otherwise, transparent models are rendered using the faster
* batching renderer when possible.
*/
bool m_SortAllTransparent;
/**
* m_DisplayFrustum: Render the cull frustum and other data that may be interesting
* to evaluate culling and shadow map calculations
@ -442,12 +416,6 @@ protected:
*/
bool m_DisplayFrustum;
/**
* m_DisableCopyShadow: For debugging purpose:
* Disable copying of shadow data into the shadow texture (when EXT_fbo is not available)
*/
bool m_DisableCopyShadow;
/**
* Enable rendering of terrain tile priority text overlay, for debugging.
*/

View File

@ -195,7 +195,7 @@ void TerrainRenderer::RenderTerrain(bool filtered)
if (visiblePatches.empty() && visibleDecals.empty())
return;
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy");
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
// render the solid black sides of the map first
@ -250,7 +250,7 @@ void TerrainRenderer::RenderTerrain(bool filtered)
// switch on blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// no need to write to the depth buffer a second time
glDepthMask(0);
@ -295,13 +295,18 @@ void TerrainRenderer::RenderTerrain(bool filtered)
pglClientActiveTextureARB(GL_TEXTURE0);
glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours
glBlendFunc(GL_DST_COLOR, GL_ZERO);
// The vertex color is scaled by 0.5 to permit overbrightness without clamping.
// We therefore need to draw clamp((texture*lighting)*2.0), where 'texture'
// is what previous passes drew onto the framebuffer, and 'lighting' is the
// color computed by this pass.
// We can do that with blending by getting it to draw dst*src + src*dst:
glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
// GL_TEXTURE_ENV_COLOR requires four floats, so we shouldn't use the RGBColor directly
// Scale the ambient color by 0.5 to match the vertex diffuse colors
float terrainAmbientColor[4] = {
lightEnv.m_TerrainAmbientColor.X,
lightEnv.m_TerrainAmbientColor.Y,
lightEnv.m_TerrainAmbientColor.Z,
lightEnv.m_TerrainAmbientColor.X * 0.5f,
lightEnv.m_TerrainAmbientColor.Y * 0.5f,
lightEnv.m_TerrainAmbientColor.Z * 0.5f,
1.f
};
@ -370,6 +375,7 @@ void TerrainRenderer::RenderTerrain(bool filtered)
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
glDepthMask(1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
@ -412,7 +418,7 @@ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap*
shader->BindTexture("blendTex", g_Renderer.m_hCompositeAlphaMap);
}
void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow, bool filtered)
void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, ShadowMap* shadow, bool filtered)
{
ENSURE(m->phase == Phase_Render);
@ -423,25 +429,9 @@ void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow, bool filtered)
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
typedef std::map<CStr, CStr> Defines;
Defines defBasic;
if (shadow)
{
defBasic["USE_SHADOW"] = "1";
if (g_Renderer.m_Caps.m_ARBProgramShadow && g_Renderer.m_Options.m_ARBProgramShadow)
defBasic["USE_FP_SHADOW"] = "1";
if (g_Renderer.m_Options.m_ShadowPCF)
defBasic["USE_SHADOW_PCF"] = "1";
#if !CONFIG2_GLES
defBasic["USE_SHADOW_SAMPLER"] = "1";
#endif
}
defBasic["LIGHTING_MODEL_" + g_Renderer.GetLightEnv().GetLightingModel()] = "1";
CShaderTechniquePtr techBase(shaderManager.LoadEffect("terrain_base", defBasic));
CShaderTechniquePtr techBlend(shaderManager.LoadEffect("terrain_blend", defBasic));
CShaderTechniquePtr techDecal(shaderManager.LoadEffect("terrain_decal", defBasic));
CShaderTechniquePtr techBase(shaderManager.LoadEffect(CStrIntern("terrain_base"), context, CShaderDefines()));
CShaderTechniquePtr techBlend(shaderManager.LoadEffect(CStrIntern("terrain_blend"), context, CShaderDefines()));
CShaderTechniquePtr techDecal(shaderManager.LoadEffect(CStrIntern("terrain_decal"), context, CShaderDefines()));
// render the solid black sides of the map first
CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect("gui_solid");
@ -521,7 +511,7 @@ void TerrainRenderer::RenderPatches(bool filtered)
#if CONFIG2_GLES
#warning TODO: implement TerrainRenderer::RenderPatches for GLES
#else
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy");
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
glEnableClientState(GL_VERTEX_ARRAY);
@ -614,8 +604,7 @@ bool TerrainRenderer::RenderFancyWater()
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterShader)
{
std::map<CStr, CStr> defNull;
m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("water_high", defNull);
m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("water_high", CShaderDefines());
if (!m->fancyWaterShader)
{
LOGERROR(L"Failed to load water shader. Falling back to non-fancy water.\n");
@ -628,7 +617,7 @@ bool TerrainRenderer::RenderFancyWater()
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
@ -645,11 +634,6 @@ bool TerrainRenderer::RenderFancyWater()
float ty = -fmod(time, 34.0)/34.0;
float repeatPeriod = WaterMgr->m_RepeatPeriod;
// Set the proper LOD bias
#if !CONFIG2_GLES
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
#endif
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
@ -762,10 +746,7 @@ void TerrainRenderer::RenderSimpleWater()
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy");
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
glEnableClientState(GL_VERTEX_ARRAY);

View File

@ -96,7 +96,7 @@ public:
* @param shadow A prepared shadow map, in case rendering with shadows is enabled.
* @param filtered If true then only render objects that passed CullPatches.
*/
void RenderTerrainShader(ShadowMap* shadow, bool filtered = false);
void RenderTerrainShader(const CShaderDefines& context, ShadowMap* shadow, bool filtered = false);
/**
* RenderPatches: Render all patches un-textured as polygons.

View File

@ -1,838 +0,0 @@
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* ModelRenderer implementation that sorts models and/or polygons based
* on distance from viewer, for transparency rendering.
*/
#include "precompiled.h"
#include <algorithm>
#include <vector>
#include "lib/bits.h"
#include "lib/ogl.h"
#include "lib/sysdep/rtl.h"
#include "maths/MathUtil.h"
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "graphics/TextureManager.h"
#include "ps/Profile.h"
#include "renderer/Renderer.h"
#include "renderer/ShadowMap.h"
#include "renderer/TransparencyRenderer.h"
#include "renderer/VertexArray.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PolygonSortModelRenderer implementation
#if !CONFIG2_GLES
/**
* Struct PSModelDef: Per-CModelDef data for the polygon sort vertex renderer
*/
struct PSModelDef : public CModelDefRPrivate
{
PSModelDef(const CModelDefPtr& mdef);
/// Static vertex array
VertexArray m_Array;
/// UV is static
VertexArray::Attribute m_UV;
};
PSModelDef::PSModelDef(const CModelDefPtr& mdef)
: m_Array(GL_STATIC_DRAW)
{
m_UV.type = GL_FLOAT;
m_UV.elems = 2;
m_Array.AddAttribute(&m_UV);
m_Array.SetNumVertices(mdef->GetNumVertices());
m_Array.Layout();
VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>();
ModelRenderer::BuildUV(mdef, UVit);
m_Array.Upload();
m_Array.FreeBackingStore();
}
/**
* Struct PSModel: Per-CModel data for the polygon sorting renderer
*/
struct PSModel
{
PSModel(CModel* model);
~PSModel();
/**
* BackToFrontIndexSort: Sort polygons by distance to camera for
* transparency rendering and fill the indices array appropriately.
*
* @param worldToCam World to camera coordinate space transform
*
* @return Square of the estimated distance to the nearest triangle.
*/
float BackToFrontIndexSort(const CMatrix3D& worldToCam);
/// Back-link to the model
CModel* m_Model;
/// Dynamic per-CModel vertex array
VertexArray m_Array;
/// Position and lighting are recalculated on CPU every frame
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Color;
/// Indices array (sorted on CPU based on distance to camera)
u16* m_Indices;
};
PSModel::PSModel(CModel* model)
: m_Model(model), m_Array(GL_DYNAMIC_DRAW)
{
CModelDefPtr mdef = m_Model->GetModelDef();
// Positions and normals must be 16-byte aligned for SSE writes.
// We can pack the color after the position; it will be corrupted by
// BuildPositionAndNormals, but that's okay since we'll recompute the
// colors afterwards.
m_Color.type = GL_UNSIGNED_BYTE;
m_Color.elems = 4;
m_Array.AddAttribute(&m_Color);
m_Position.type = GL_FLOAT;
m_Position.elems = 3;
m_Array.AddAttribute(&m_Position);
m_Array.SetNumVertices(mdef->GetNumVertices());
m_Array.Layout();
// Verify alignment
ENSURE(m_Position.offset % 16 == 0);
ENSURE(m_Array.GetStride() % 16 == 0);
m_Indices = new u16[mdef->GetNumFaces()*3];
}
PSModel::~PSModel()
{
delete[] m_Indices;
}
typedef std::pair<int,float> IntFloatPair;
struct SortFacesByDist {
bool operator()(const IntFloatPair& lhs,const IntFloatPair& rhs) {
return lhs.second>rhs.second ? true : false;
}
};
float PSModel::BackToFrontIndexSort(const CMatrix3D& worldToCam)
{
static std::vector<IntFloatPair> IndexSorter;
CModelDefPtr mdef = m_Model->GetModelDef();
size_t numFaces = mdef->GetNumFaces();
const SModelFace* faces = mdef->GetFaces();
if (IndexSorter.size() < numFaces)
IndexSorter.resize(numFaces);
VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
CVector3D tmpvtx;
for(size_t i = 0; i < numFaces; ++i)
{
tmpvtx = Position[faces[i].m_Verts[0]];
tmpvtx += Position[faces[i].m_Verts[1]];
tmpvtx += Position[faces[i].m_Verts[2]];
tmpvtx *= 1.0f/3.0f;
tmpvtx = worldToCam.Transform(tmpvtx);
float distsqrd = SQR(tmpvtx.X)+SQR(tmpvtx.Y)+SQR(tmpvtx.Z);
IndexSorter[i].first = (int)i;
IndexSorter[i].second = distsqrd;
}
std::sort(IndexSorter.begin(),IndexSorter.begin()+numFaces,SortFacesByDist());
// now build index list
size_t idxidx = 0;
for (size_t i = 0; i < numFaces; ++i) {
const SModelFace& face = faces[IndexSorter[i].first];
m_Indices[idxidx++] = (u16)(face.m_Verts[0]);
m_Indices[idxidx++] = (u16)(face.m_Verts[1]);
m_Indices[idxidx++] = (u16)(face.m_Verts[2]);
}
return IndexSorter[0].second;
}
/**
* Struct PolygonSortModelRendererInternals: Internal data structure of
* PolygonSortModelRenderer
*/
struct PolygonSortModelRendererInternals
{
/**
* Scratch space for normal vector calculation.
* Space is reserved so we don't have to do frequent reallocations.
* Allocated with rtl_AllocateAligned(normalsNumVertices*16, 16) for SSE writes.
*/
char* normals;
size_t normalsNumVertices;
};
// Construction / Destruction
PolygonSortModelRenderer::PolygonSortModelRenderer()
{
m = new PolygonSortModelRendererInternals;
m->normals = 0;
m->normalsNumVertices = 0;
}
PolygonSortModelRenderer::~PolygonSortModelRenderer()
{
rtl_FreeAligned(m->normals);
delete m;
}
// Create per-CModel data for the model (and per-CModelDef data if necessary)
void* PolygonSortModelRenderer::CreateModelData(CModel* model)
{
CModelDefPtr mdef = model->GetModelDef();
PSModelDef* psmdef = (PSModelDef*)mdef->GetRenderData(m);
if (!psmdef)
{
psmdef = new PSModelDef(mdef);
mdef->SetRenderData(m, psmdef);
}
return new PSModel(model);
}
// Updated transforms
void PolygonSortModelRenderer::UpdateModelData(CModel* model, void* data, int updateflags)
{
PSModel* psmdl = (PSModel*)data;
if (updateflags & (RENDERDATA_UPDATE_VERTICES|RENDERDATA_UPDATE_COLOR))
{
CModelDefPtr mdef = model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
// build vertices
// allocate working space for computing normals
if (numVertices > m->normalsNumVertices)
{
rtl_FreeAligned(m->normals);
size_t newSize = round_up_to_pow2(numVertices);
m->normals = (char*)rtl_AllocateAligned(newSize*16, 16);
m->normalsNumVertices = newSize;
}
VertexArrayIterator<CVector3D> Position = psmdl->m_Position.GetIterator<CVector3D>();
VertexArrayIterator<CVector3D> Normal = VertexArrayIterator<CVector3D>(m->normals, 16);
ModelRenderer::BuildPositionAndNormals(model, Position, Normal);
VertexArrayIterator<SColor4ub> Color = psmdl->m_Color.GetIterator<SColor4ub>();
ModelRenderer::BuildColor4ub(model, Normal, Color);
// upload everything to vertex buffer
psmdl->m_Array.Upload();
}
// resort model indices from back to front, according to the view camera position - and store
// the returned sqrd distance to the centre of the nearest triangle
// Use the view camera instead of the cull camera because:
// a) polygon sorting implicitly uses the view camera (and changing that would be costly)
// b) using the cull camera is likely not interesting from a debugging POV
PROFILE_START( "sorting transparent" );
CMatrix3D worldToCam;
g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam);
psmdl->BackToFrontIndexSort(worldToCam);
PROFILE_END( "sorting transparent" );
}
// Cleanup per-CModel data
void PolygonSortModelRenderer::DestroyModelData(CModel* UNUSED(model), void* data)
{
PSModel* psmdl = (PSModel*)data;
delete psmdl;
}
// Prepare for one rendering pass
void PolygonSortModelRenderer::BeginPass(int streamflags)
{
ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_COLOR|STREAM_UV0)));
glEnableClientState(GL_VERTEX_ARRAY);
if (streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (streamflags & STREAM_COLOR) glEnableClientState(GL_COLOR_ARRAY);
}
// Cleanup rendering
void PolygonSortModelRenderer::EndPass(int streamflags)
{
if (streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (streamflags & STREAM_COLOR) glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
CVertexBuffer::Unbind();
}
// Prepare for rendering models using this CModelDef
void PolygonSortModelRenderer::PrepareModelDef(CShaderProgramPtr& UNUSED(shader), int streamflags, const CModelDefPtr& def)
{
if (streamflags & STREAM_UV0)
{
PSModelDef* psmdef = (PSModelDef*)def->GetRenderData(m);
ENSURE(psmdef);
u8* base = psmdef->m_Array.Bind();
GLsizei stride = (GLsizei)psmdef->m_Array.GetStride();
glTexCoordPointer(2, GL_FLOAT, stride, base + psmdef->m_UV.offset);
}
}
// Render one model
void PolygonSortModelRenderer::RenderModel(CShaderProgramPtr& UNUSED(shader), int streamflags, CModel* model, void* data)
{
CModelDefPtr mdef = model->GetModelDef();
PSModel* psmdl = (PSModel*)data;
// Setup per-CModel arrays
u8* base = psmdl->m_Array.Bind();
GLsizei stride = (GLsizei)psmdl->m_Array.GetStride();
glVertexPointer(3, GL_FLOAT, stride, base + psmdl->m_Position.offset);
if (streamflags & STREAM_COLOR)
glColorPointer(3, psmdl->m_Color.type, stride, base + psmdl->m_Color.offset);
// render the lot
size_t numFaces = mdef->GetNumFaces();
if (!g_Renderer.m_SkipSubmit) {
pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdef->GetNumVertices()-1,
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, psmdl->m_Indices);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_ModelTris += numFaces;
}
#endif // !CONFIG2_GLES
///////////////////////////////////////////////////////////////////////////////////////////////////
// SortModelRenderer implementation
/**
* Struct SModel: Per-CModel data for the model-sorting renderer
*/
struct SModel : public CModelRData
{
SModel(SortModelRendererInternals* tri, CModel* model);
~SModel();
// Back-link to the Model renderer
SortModelRendererInternals* m_SMRI;
// Private data of the ModelVertexRenderer
void* m_Data;
// Distance to camera (for sorting)
float m_Distance;
};
/**
* Struct SortModelRendererInternals: Internal data structure of SortModelRenderer
*/
struct SortModelRendererInternals
{
/// Vertex renderer used for transform and lighting
ModelVertexRendererPtr vertexRenderer;
/// List of submitted models.
std::vector<SModel*> models;
};
SModel::SModel(SortModelRendererInternals* smri, CModel* model)
: CModelRData(smri, model), m_SMRI(smri)
{
m_Data = m_SMRI->vertexRenderer->CreateModelData(model);
m_Distance = 0;
}
SModel::~SModel()
{
m_SMRI->vertexRenderer->DestroyModelData(GetModel(), m_Data);
}
// Construction / Destruction
SortModelRenderer::SortModelRenderer(ModelVertexRendererPtr vertexRenderer)
{
m = new SortModelRendererInternals;
m->vertexRenderer = vertexRenderer;
}
SortModelRenderer::~SortModelRenderer()
{
delete m;
}
// Submit a model: Create, but don't fill in, our own Model and ModelDef structures
void SortModelRenderer::Submit(CModel* model)
{
CModelRData* rdata = (CModelRData*)model->GetRenderData();
SModel* smdl;
if (rdata && rdata->GetKey() == m)
{
smdl = (SModel*)rdata;
}
else
{
smdl = new SModel(m, model);
rdata = smdl;
model->SetRenderData(rdata);
model->SetDirty(~0u);
}
m->models.push_back(smdl);
}
// Transform and sort all models
struct SortModelsByDist {
bool operator()(SModel* lhs, SModel* rhs) {
// Sort by distance, and break ties by comparing pointers
return lhs->m_Distance > rhs->m_Distance ? true
: lhs->m_Distance < rhs->m_Distance ? false
: (lhs > rhs);
}
};
void SortModelRenderer::PrepareModels()
{
CMatrix3D worldToCam;
if (m->models.size() == 0)
return;
g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam);
for(std::vector<SModel*>::iterator it = m->models.begin(); it != m->models.end(); ++it)
{
SModel* smdl = *it;
CModel* model = smdl->GetModel();
ENSURE(model->GetRenderData() == smdl);
m->vertexRenderer->UpdateModelData(model, smdl->m_Data, smdl->m_UpdateFlags);
smdl->m_UpdateFlags = 0;
CVector3D modelpos = model->GetTransform().GetTranslation();
modelpos = worldToCam.Transform(modelpos);
smdl->m_Distance = modelpos.Z;
}
PROFILE_START( "sorting transparent" );
std::sort(m->models.begin(), m->models.end(), SortModelsByDist());
PROFILE_END( "sorting transparent" );
}
// Cleanup per-frame model list
void SortModelRenderer::EndFrame()
{
m->models.clear();
}
// Return whether models have been submitted this frame
bool SortModelRenderer::HaveSubmissions()
{
return m->models.size() != 0;
}
// Render submitted models (filtered by flags) using the given modifier
void SortModelRenderer::Render(const RenderModifierPtr& modifier, int flags)
{
int pass = 0;
if (m->models.size() == 0)
return;
do
{
int streamflags = modifier->BeginPass(pass);
CModelDefPtr lastmdef;
CTexturePtr lasttex;
CShaderProgramPtr shader = modifier->GetShader(pass);
m->vertexRenderer->BeginPass(streamflags);
for(std::vector<SModel*>::iterator it = m->models.begin(); it != m->models.end(); ++it)
{
SModel* smdl = *it;
CModel* mdl = smdl->GetModel();
if (flags && !(mdl->GetFlags() & flags))
continue;
ENSURE(smdl->GetKey() == m);
CModelDefPtr mdef = mdl->GetModelDef();
CTexturePtr tex = mdl->GetTexture();
// Prepare per-CModelDef data if changed
if (mdef != lastmdef)
{
m->vertexRenderer->PrepareModelDef(shader, streamflags, mdef);
lastmdef = mdef;
}
// Prepare necessary RenderModifier stuff
if (tex != lasttex)
{
modifier->PrepareTexture(pass, tex);
lasttex = tex;
}
modifier->PrepareModel(pass, mdl);
// Render the model
m->vertexRenderer->RenderModel(shader, streamflags, mdl, smdl->m_Data);
}
m->vertexRenderer->EndPass(streamflags);
} while(!modifier->EndPass(pass++));
}
void SortModelRenderer::Filter(CModelFilter& filter, int passed, int flags)
{
for (std::vector<SModel*>::iterator it = m->models.begin(); it != m->models.end(); ++it)
{
SModel* smdl = *it;
CModel* mdl = smdl->GetModel();
if (flags && !(mdl->GetFlags() & flags))
continue;
if (filter.Filter(mdl))
mdl->SetFlags(mdl->GetFlags() | passed);
else
mdl->SetFlags(mdl->GetFlags() & ~passed);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentRenderModifier implementation
#if !CONFIG2_GLES
TransparentRenderModifier::TransparentRenderModifier()
{
}
TransparentRenderModifier::~TransparentRenderModifier()
{
}
int TransparentRenderModifier::BeginPass(int pass)
{
// First pass: opaque areas only.
// Second pass: blended areas.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// just pass through texture's alpha
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_ALPHA_TEST);
if (pass == 0)
{
glAlphaFunc(GL_GREATER, 0.9375f);
}
else
{
glAlphaFunc(GL_GREATER, 0.0f);
glDepthFunc(GL_LESS);
glDepthMask(0);
}
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool TransparentRenderModifier::EndPass(int pass)
{
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
if (pass == 0)
return false;
glDepthFunc(GL_LEQUAL);
glDepthMask(1);
return true;
}
void TransparentRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(0);
}
void TransparentRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
// No per-model setup necessary
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentOpaqueRenderModifier implementation
TransparentOpaqueRenderModifier::TransparentOpaqueRenderModifier()
{
}
TransparentOpaqueRenderModifier::~TransparentOpaqueRenderModifier()
{
}
int TransparentOpaqueRenderModifier::BeginPass(int pass)
{
ENSURE(pass == 0);
// Put down opaque parts of the model only.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// just pass through texture's alpha
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.9375f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool TransparentOpaqueRenderModifier::EndPass(int UNUSED(pass))
{
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
return true;
}
void TransparentOpaqueRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(0);
}
void TransparentOpaqueRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
// No per-model setup necessary
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentBlendRenderModifier implementation
TransparentBlendRenderModifier::TransparentBlendRenderModifier()
{
}
TransparentBlendRenderModifier::~TransparentBlendRenderModifier()
{
}
int TransparentBlendRenderModifier::BeginPass(int pass)
{
ENSURE(pass == 0);
// Put down blended parts of the model only.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// just pass through texture's alpha
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER,0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS);
glDepthMask(0);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool TransparentBlendRenderModifier::EndPass(int UNUSED(pass))
{
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glDepthMask(1);
return true;
}
void TransparentBlendRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(0);
}
void TransparentBlendRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
// No per-model setup necessary
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentDepthShadowModifier implementation
TransparentDepthShadowModifier::TransparentDepthShadowModifier()
{
}
TransparentDepthShadowModifier::~TransparentDepthShadowModifier()
{
}
int TransparentDepthShadowModifier::BeginPass(int pass)
{
ENSURE(pass == 0);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.4f);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
return STREAM_POS|STREAM_UV0;
}
bool TransparentDepthShadowModifier::EndPass(int UNUSED(pass))
{
glDisable(GL_ALPHA_TEST);
return true;
}
void TransparentDepthShadowModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(0);
}
void TransparentDepthShadowModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
// No per-model setup necessary
}
#endif // !CONFIG2_GLES

View File

@ -1,172 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* ModelRenderer implementation that sorts models and/or polygons based
* on distance from viewer, for transparency rendering.
*/
#ifndef INCLUDED_TRANSPARENCYRENDERER
#define INCLUDED_TRANSPARENCYRENDERER
#include "renderer/ModelRenderer.h"
#include "renderer/ModelVertexRenderer.h"
#include "renderer/RenderModifiers.h"
struct PolygonSortModelRendererInternals;
#if !CONFIG2_GLES
/**
* Class PolygonSortModelRenderer: Render animated models using only
* OpenGL fixed function, sorting polygons from back to front.
*
* This ModelVertexRenderer should only be used with SortModelRenderer.
* However, SortModelRenderer can be used with other ModelVertexRenderers
* than this one.
*/
class PolygonSortModelRenderer : public ModelVertexRenderer
{
public:
PolygonSortModelRenderer();
~PolygonSortModelRenderer();
// Implementations
void* CreateModelData(CModel* model);
void UpdateModelData(CModel* model, void* data, int updateflags);
void DestroyModelData(CModel* model, void* data);
void BeginPass(int streamflags);
void EndPass(int streamflags);
void PrepareModelDef(CShaderProgramPtr& shader, int streamflags, const CModelDefPtr& def);
void RenderModel(CShaderProgramPtr& shader, int streamflags, CModel* model, void* data);
private:
PolygonSortModelRendererInternals* m;
};
#endif
struct SortModelRendererInternals;
/**
* Class SortModelRenderer: Render models back-to-front from the
* camera's point of view.
*
* This is less efficient than batched model renderers, but
* necessary for transparent models.
*
* TransparencyRenderer can be used with any ModelVertexRenderer.
*
* Use this renderer together with TransparentRenderModifier and
* TransparentShadowRenderModifier to achieve transparency.
*/
class SortModelRenderer : public ModelRenderer
{
public:
SortModelRenderer(ModelVertexRendererPtr vertexrenderer);
~SortModelRenderer();
// Transparency renderer implementation
void Submit(CModel* model);
void PrepareModels();
void EndFrame();
bool HaveSubmissions();
void Render(const RenderModifierPtr& modifier, int flags);
void Filter(CModelFilter& filter, int passed, int flags);
private:
SortModelRendererInternals* m;
};
/**
* Class TransparentRenderModifier: Modifier for transparent models,
* including alpha blending and lighting.
*/
class TransparentRenderModifier : public RenderModifier
{
public:
TransparentRenderModifier();
~TransparentRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
#if !CONFIG2_GLES
/**
* Class TransparentOpaqueRenderModifier: Modifier for transparent models,
* including alpha blending and lighting, Opaque pass only.
*/
class TransparentOpaqueRenderModifier : public RenderModifier
{
public:
TransparentOpaqueRenderModifier();
~TransparentOpaqueRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
/**
* Class TransparentBlendRenderModifier: Modifier for transparent models,
* including alpha blending and lighting. Blend pass only.
*/
class TransparentBlendRenderModifier : public RenderModifier
{
public:
TransparentBlendRenderModifier();
~TransparentBlendRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
/**
* Class TransparentDepthShadowModifier: Use to render shadow data for
* transparent models into a depth texture: Writes into the depth buffer,
* color data is undefined.
*/
class TransparentDepthShadowModifier : public RenderModifier
{
public:
TransparentDepthShadowModifier();
~TransparentDepthShadowModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
#endif // !CONFIG2_GLES
#endif

View File

@ -78,6 +78,8 @@ class MiscSetup : public CxxTest::GlobalFixture
setlocale(LC_CTYPE, "UTF-8");
#endif
ThreadUtil::SetMainThread();
g_Profiler2.Initialise();
return true;