diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index 10e03e143c..89238a4bbe 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -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/" diff --git a/binaries/data/mods/public/art/materials/basic_trans.xml b/binaries/data/mods/public/art/materials/basic_trans.xml index 136f4e7d08..3cc6db6f1f 100644 --- a/binaries/data/mods/public/art/materials/basic_trans.xml +++ b/binaries/data/mods/public/art/materials/basic_trans.xml @@ -1,5 +1,6 @@ - - - \ No newline at end of file + + + + + + diff --git a/binaries/data/mods/public/art/materials/default.xml b/binaries/data/mods/public/art/materials/default.xml new file mode 100644 index 0000000000..2337fa18e9 --- /dev/null +++ b/binaries/data/mods/public/art/materials/default.xml @@ -0,0 +1,4 @@ + + + + diff --git a/binaries/data/mods/public/art/materials/objectcolor.xml b/binaries/data/mods/public/art/materials/objectcolor.xml index df13fef316..92ebf138c0 100644 --- a/binaries/data/mods/public/art/materials/objectcolor.xml +++ b/binaries/data/mods/public/art/materials/objectcolor.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + + + diff --git a/binaries/data/mods/public/art/materials/player_trans.xml b/binaries/data/mods/public/art/materials/player_trans.xml index e1d8574139..ca076afbd7 100644 --- a/binaries/data/mods/public/art/materials/player_trans.xml +++ b/binaries/data/mods/public/art/materials/player_trans.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + + + diff --git a/binaries/data/mods/public/shaders/effects/model.xml b/binaries/data/mods/public/shaders/effects/model.xml new file mode 100644 index 0000000000..d06f6dd25c --- /dev/null +++ b/binaries/data/mods/public/shaders/effects/model.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/binaries/data/mods/public/shaders/effects/model_alphablend.xml b/binaries/data/mods/public/shaders/effects/model_alphablend.xml new file mode 100644 index 0000000000..9387586706 --- /dev/null +++ b/binaries/data/mods/public/shaders/effects/model_alphablend.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/binaries/data/mods/public/shaders/effects/model_normal.xml b/binaries/data/mods/public/shaders/effects/model_normal.xml deleted file mode 100644 index 0d6c4bae21..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_normal.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_normal_instancing.xml b/binaries/data/mods/public/shaders/effects/model_normal_instancing.xml deleted file mode 100644 index 19e9156cf3..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_normal_instancing.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_solid.xml b/binaries/data/mods/public/shaders/effects/model_solid.xml deleted file mode 100644 index 134abf20fb..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_solid.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_solid_instancing.xml b/binaries/data/mods/public/shaders/effects/model_solid_instancing.xml deleted file mode 100644 index 64ef2983ab..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_solid_instancing.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_solid_player.xml b/binaries/data/mods/public/shaders/effects/model_solid_player.xml deleted file mode 100644 index f597194da5..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_solid_player.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_solid_player_instancing.xml b/binaries/data/mods/public/shaders/effects/model_solid_player_instancing.xml deleted file mode 100644 index 9a0e7a8a8a..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_solid_player_instancing.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_transparent.xml b/binaries/data/mods/public/shaders/effects/model_transparent.xml deleted file mode 100644 index 86091dcdd7..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_transparent.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_transparent_blend.xml b/binaries/data/mods/public/shaders/effects/model_transparent_blend.xml deleted file mode 100644 index 0710e6c13d..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_transparent_blend.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_transparent_opaque.xml b/binaries/data/mods/public/shaders/effects/model_transparent_opaque.xml deleted file mode 100644 index f36dbd271e..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_transparent_opaque.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/effects/model_transparent_shadow.xml b/binaries/data/mods/public/shaders/effects/model_transparent_shadow.xml deleted file mode 100644 index d037f3f346..0000000000 --- a/binaries/data/mods/public/shaders/effects/model_transparent_shadow.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/binaries/data/mods/public/shaders/glsl/model_common.fs b/binaries/data/mods/public/shaders/glsl/model_common.fs index 826017d12f..1003d0f3f8 100644 --- a/binaries/data/mods/public/shaders/glsl/model_common.fs +++ b/binaries/data/mods/public/shaders/glsl/model_common.fs @@ -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; diff --git a/binaries/data/mods/public/shaders/glsl/terrain_common.vs b/binaries/data/mods/public/shaders/glsl/terrain_common.vs index 38463cd34a..8a0a570ebc 100644 --- a/binaries/data/mods/public/shaders/glsl/terrain_common.vs +++ b/binaries/data/mods/public/shaders/glsl/terrain_common.vs @@ -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; diff --git a/binaries/data/mods/public/shaders/model_common.fp b/binaries/data/mods/public/shaders/model_common.fp index f8b72688da..d7cdc4a6ba 100644 --- a/binaries/data/mods/public/shaders/model_common.fp +++ b/binaries/data/mods/public/shaders/model_common.fp @@ -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) diff --git a/binaries/data/mods/public/shaders/model_common_arb.xml b/binaries/data/mods/public/shaders/model_common_arb.xml index a2a86e3c56..e0fef3510b 100644 --- a/binaries/data/mods/public/shaders/model_common_arb.xml +++ b/binaries/data/mods/public/shaders/model_common_arb.xml @@ -6,6 +6,7 @@ + @@ -16,7 +17,8 @@ - + + diff --git a/binaries/data/mods/public/shaders/program.rng b/binaries/data/mods/public/shaders/program.rng index 10f4bd8beb..c8e59d167f 100644 --- a/binaries/data/mods/public/shaders/program.rng +++ b/binaries/data/mods/public/shaders/program.rng @@ -21,6 +21,7 @@ + @@ -47,6 +48,7 @@ + @@ -83,6 +85,7 @@ + @@ -104,6 +107,7 @@ + pos @@ -118,4 +122,10 @@ + + + + + + diff --git a/binaries/data/mods/public/shaders/solid.xml b/binaries/data/mods/public/shaders/solid.xml index 53d668e50d..8e4bd98398 100644 --- a/binaries/data/mods/public/shaders/solid.xml +++ b/binaries/data/mods/public/shaders/solid.xml @@ -2,6 +2,7 @@ + diff --git a/binaries/data/mods/public/shaders/solid_player.xml b/binaries/data/mods/public/shaders/solid_player.xml index d2d772410d..a196f4d768 100644 --- a/binaries/data/mods/public/shaders/solid_player.xml +++ b/binaries/data/mods/public/shaders/solid_player.xml @@ -2,6 +2,7 @@ + diff --git a/binaries/data/mods/public/shaders/solid_tex.xml b/binaries/data/mods/public/shaders/solid_tex.xml index c62b3be0d5..4cb11a12b8 100644 --- a/binaries/data/mods/public/shaders/solid_tex.xml +++ b/binaries/data/mods/public/shaders/solid_tex.xml @@ -2,6 +2,7 @@ + diff --git a/source/graphics/LightEnv.cpp b/source/graphics/LightEnv.cpp index 1ce3f7049f..07234e3013 100644 --- a/source/graphics/LightEnv.cpp +++ b/source/graphics/LightEnv.cpp @@ -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(); } diff --git a/source/graphics/LightEnv.h b/source/graphics/LightEnv.h index 4f8e304745..272fa7bf90 100644 --- a/source/graphics/LightEnv.h +++ b/source/graphics/LightEnv.h @@ -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 diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 0262f2f90a..93b33c8962 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -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) diff --git a/source/graphics/Material.cpp b/source/graphics/Material.cpp index 3ad1646625..c8e87dfd44 100644 --- a/source/graphics/Material.cpp +++ b/source/graphics/Material.cpp @@ -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); } diff --git a/source/graphics/Material.h b/source/graphics/Material.h index 225cb973c1..85a7c14325 100644 --- a/source/graphics/Material.h +++ b/source/graphics/Material.h @@ -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 diff --git a/source/graphics/MaterialManager.cpp b/source/graphics/MaterialManager.cpp index 21b7c0f70b..a78614c774 100644 --- a/source/graphics/MaterialManager.cpp +++ b/source/graphics/MaterialManager.cpp @@ -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::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::iterator iter = m_Materials.find(pathname); - if(iter != m_Materials.end()) - { - if((*iter).second) - return *(*iter).second; - } + std::map::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; } diff --git a/source/graphics/MaterialManager.h b/source/graphics/MaterialManager.h index 0f063ad154..14fbfeb783 100644 --- a/source/graphics/MaterialManager.h +++ b/source/graphics/MaterialManager.h @@ -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 -#include "ps/Singleton.h" #include "Material.h" -#define g_MaterialManager CMaterialManager::GetSingleton() - -class CMaterialManager : public Singleton +class CMaterialManager { public: - CMaterialManager(); - ~CMaterialManager(); + CMaterial LoadMaterial(const VfsPath& pathname); - CMaterial& LoadMaterial(const VfsPath& pathname); private: - std::map m_Materials; + std::map m_Materials; }; -#endif +#endif // INCLUDED_MATERIALMANAGER diff --git a/source/graphics/Model.cpp b/source/graphics/Model.cpp index 728deea55d..af9ce38e58 100644 --- a/source/graphics/Model.cpp +++ b/source/graphics/Model.cpp @@ -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::iterator it = m_Props.begin(); it != m_Props.end(); ++it) it->m_Model->SetPlayerID(id); } diff --git a/source/graphics/Model.h b/source/graphics/Model.h index 35fdffd471..31c6d3ed2c 100644 --- a/source/graphics/Model.h +++ b/source/graphics/Model.h @@ -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 diff --git a/source/graphics/ObjectBase.cpp b/source/graphics/ObjectBase.cpp index 9e9f9d5e01..e866a319e2 100644 --- a/source/graphics/ObjectBase.cpp +++ b/source/graphics/ObjectBase.cpp @@ -257,6 +257,9 @@ bool CObjectBase::Load(const VfsPath& pathname) } } + if (m_Material.empty()) + m_Material = VfsPath("art/materials/default.xml"); + return true; } diff --git a/source/graphics/ObjectEntry.cpp b/source/graphics/ObjectEntry.cpp index 069ea31b16..7095adc9ed 100644 --- a/source/graphics/ObjectEntry.cpp +++ b/source/graphics/ObjectEntry.cpp @@ -118,15 +118,15 @@ bool CObjectEntry::BuildVariation(const std::vector >& 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(); diff --git a/source/graphics/ShaderDefines.cpp b/source/graphics/ShaderDefines.cpp new file mode 100644 index 0000000000..52c87f8476 --- /dev/null +++ b/source/graphics/ShaderDefines.cpp @@ -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 . + */ + +#include "precompiled.h" + +#include "ShaderDefines.h" + +#include "ps/ThreadUtil.h" + +#include + +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 > 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())) == items.items.end()); + + shared_ptr 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::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 CShaderDefines::GetMap() const +{ + std::map 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; +} diff --git a/source/graphics/ShaderDefines.h b/source/graphics/ShaderDefines.h new file mode 100644 index 0000000000..925f6d4926 --- /dev/null +++ b/source/graphics/ShaderDefines.h @@ -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 . + */ + +#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 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 Item; + + // Sorted by name; no duplicated names + std::vector 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 diff --git a/source/graphics/ShaderManager.cpp b/source/graphics/ShaderManager.cpp index de062902cf..35ea693fb8 100644 --- a/source/graphics/ShaderManager.cpp +++ b/source/graphics/ShaderManager.cpp @@ -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& defines) +CShaderProgramPtr CShaderManager::LoadProgram(const char* name, const CShaderDefines& defines) { CacheKey key = { name, defines }; std::map::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& 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& 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& ba #undef AT #undef EL + CPreprocessor preprocessor; + std::map baseDefinesMap = baseDefines.GetMap(); + for (std::map::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 defines = baseDefines; + CShaderDefines defines = baseDefines; std::map vertexUniforms; std::map fragmentUniforms; std::map vertexAttribs; @@ -173,7 +211,7 @@ bool CShaderManager::NewProgram(const char* name, const std::map& 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& 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& 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& 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::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& 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::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& 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& 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& 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 baseDefinesMap = baseDefines.GetMap(); + for (std::map::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& 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& 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 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& 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& bas } // Load the shader program after we've read all the possibly-relevant 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& bas return true; } +size_t CShaderManager::GetNumEffectsLoaded() +{ + return m_EffectCache.size(); +} + /*static*/ Status CShaderManager::ReloadChangedFileCB(void* param, const VfsPath& path) { return static_cast(param)->ReloadChangedFile(path); diff --git a/source/graphics/ShaderManager.h b/source/graphics/ShaderManager.h index 948223eb50..e7b04dac49 100644 --- a/source/graphics/ShaderManager.h +++ b/source/graphics/ShaderManager.h @@ -23,6 +23,7 @@ #include #include +#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& defines = (std::map())); + 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& defines = (std::map())); + 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 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 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 m_EffectCache; - EffectContext m_EffectCacheContext; + struct EffectCacheKeyHash + { + size_t operator()(const EffectCacheKey& key) const; + }; + typedef boost::unordered_map EffectCacheMap; + EffectCacheMap m_EffectCache; // Store the set of shaders that need to be reloaded when the given file is modified typedef boost::unordered_map > > HotloadFilesMap; @@ -110,8 +122,8 @@ private: RelaxNGValidator m_Validator; #endif - bool NewProgram(const char* name, const std::map& defines, CShaderProgramPtr& program); - bool NewEffect(const char* name, const std::map& 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); diff --git a/source/graphics/ShaderProgram.cpp b/source/graphics/ShaderProgram.cpp index 531bfd9229..621304a92e 100644 --- a/source/graphics/ShaderProgram.cpp +++ b/source/graphics/ShaderProgram.cpp @@ -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& defines, + const CShaderDefines& defines, const std::map& vertexIndexes, const std::map& 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::iterator it = m_Defines.begin(); it != m_Defines.end(); ++it) - preprocessor.Define(it->first.c_str(), it->second.c_str()); + std::map definesMap = m_Defines.GetMap(); + for (std::map::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 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& defines, + const CShaderDefines& defines, const std::map& 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::iterator it = m_Defines.begin(); it != m_Defines.end(); ++it) - preprocessor.Define(it->first.c_str(), it->second.c_str()); + std::map definesMap = m_Defines.GetMap(); + for (std::map::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 >::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 >::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 m_Defines; + CShaderDefines m_Defines; std::map m_VertexAttribs; GLhandleARB m_Program; @@ -584,7 +616,7 @@ private: std::map > 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& UNUSED(defines), + const CShaderDefines& UNUSED(defines), const std::map& UNUSED(vertexIndexes), const std::map& 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& defines, + const CShaderDefines& defines, const std::map& vertexIndexes, const std::map& 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& defines, + const CShaderDefines& defines, const std::map& vertexAttribs, int streamflags) { diff --git a/source/graphics/ShaderProgram.h b/source/graphics/ShaderProgram.h index aae4f7a28e..c24dcb523b 100644 --- a/source/graphics/ShaderProgram.h +++ b/source/graphics/ShaderProgram.h @@ -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& defines, + const CShaderDefines& defines, const std::map& vertexIndexes, const std::map& 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& defines, + const CShaderDefines& defines, const std::map& vertexAttribs, int streamflags); /** * Construct an instance of a pre-defined fixed-function pipeline setup. */ - static CShaderProgram* ConstructFFP(const std::string& id, const std::map& 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; diff --git a/source/graphics/ShaderProgramFFP.cpp b/source/graphics/ShaderProgramFFP.cpp index e502c9ab78..a57ffcc5b2 100644 --- a/source/graphics/ShaderProgramFFP.cpp +++ b/source/graphics/ShaderProgramFFP.cpp @@ -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& 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& 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& 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; diff --git a/source/graphics/ShaderTechnique.cpp b/source/graphics/ShaderTechnique.cpp index d8a8c2f572..736b188d33 100644 --- a/source/graphics/ShaderTechnique.cpp +++ b/source/graphics/ShaderTechnique.cpp @@ -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(); -} \ No newline at end of file +} diff --git a/source/graphics/ShaderTechnique.h b/source/graphics/ShaderTechnique.h index 72d8132e20..6763b298df 100644 --- a/source/graphics/ShaderTechnique.h +++ b/source/graphics/ShaderTechnique.h @@ -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 m_Passes; + + bool m_SortByDistance; }; typedef shared_ptr CShaderTechniquePtr; diff --git a/source/graphics/tests/test_ShaderManager.h b/source/graphics/tests/test_ShaderManager.h new file mode 100644 index 0000000000..5ca0600117 --- /dev/null +++ b/source/graphics/tests/test_ShaderManager.h @@ -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 . + */ + +#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); + } +}; diff --git a/source/ps/GameSetup/Config.cpp b/source/ps/GameSetup/Config.cpp index 7da49844ef..342dfbc80b 100644 --- a/source/ps/GameSetup/Config.cpp +++ b/source/ps/GameSetup/Config.cpp @@ -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(); diff --git a/source/ps/GameSetup/Config.h b/source/ps/GameSetup/Config.h index e56c986c69..c9746d1071 100644 --- a/source/ps/GameSetup/Config.h +++ b/source/ps/GameSetup/Config.h @@ -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; diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index a58c82efd8..2a09d020f4 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -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"); diff --git a/source/renderer/DecalRData.cpp b/source/renderer/DecalRData.cpp index 1ba583784b..c01930e31a 100644 --- a/source/renderer/DecalRData.cpp +++ b/source/renderer/DecalRData.cpp @@ -138,7 +138,8 @@ void CDecalRData::BuildArrays() VertexArrayIterator DiffuseColor = m_DiffuseColor.GetIterator(); VertexArrayIterator UV = m_UV.GetIterator(); - 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 diff --git a/source/renderer/FixedFunctionModelRenderer.cpp b/source/renderer/FixedFunctionModelRenderer.cpp deleted file mode 100644 index a575709842..0000000000 --- a/source/renderer/FixedFunctionModelRenderer.cpp +++ /dev/null @@ -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 . - */ - -/* - * 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 UVit = m_UV.GetIterator(); - - 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 Position = ffmodel->m_Position.GetIterator(); - VertexArrayIterator Normal = VertexArrayIterator(m->normals, 16); - - ModelRenderer::BuildPositionAndNormals(model, Position, Normal); - - VertexArrayIterator Color = ffmodel->m_Color.GetIterator(); - - 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 diff --git a/source/renderer/FixedFunctionModelRenderer.h b/source/renderer/FixedFunctionModelRenderer.h deleted file mode 100644 index 919b9e5533..0000000000 --- a/source/renderer/FixedFunctionModelRenderer.h +++ /dev/null @@ -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 . - */ - -/* - * 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 diff --git a/source/renderer/HWLightingModelRenderer.cpp b/source/renderer/HWLightingModelRenderer.cpp index e33fc86f4a..c1fc61d5be 100644 --- a/source/renderer/HWLightingModelRenderer.cpp +++ b/source/renderer/HWLightingModelRenderer.cpp @@ -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(data); + + if (!m->cpuLighting && (updateflags & RENDERDATA_UPDATE_VERTICES)) { // build vertices VertexArrayIterator Position = shadermodel->m_Position.GetIterator(); @@ -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 Position = shadermodel->m_Position.GetIterator(); + VertexArrayIterator Normal = VertexArrayIterator(m->normals, 16); + + ModelRenderer::BuildPositionAndNormals(model, Position, Normal); + + VertexArrayIterator Color = shadermodel->m_Color.GetIterator(); + + 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(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 diff --git a/source/renderer/HWLightingModelRenderer.h b/source/renderer/HWLightingModelRenderer.h index 987ee41542..cd89f45c6c 100644 --- a/source/renderer/HWLightingModelRenderer.h +++ b/source/renderer/HWLightingModelRenderer.h @@ -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; diff --git a/source/renderer/InstancingModelRenderer.cpp b/source/renderer/InstancingModelRenderer.cpp index 3a681ae79f..d7ab9b4027 100644 --- a/source/renderer/InstancingModelRenderer.cpp +++ b/source/renderer/InstancingModelRenderer.cpp @@ -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(); diff --git a/source/renderer/InstancingModelRenderer.h b/source/renderer/InstancingModelRenderer.h index 35c654b1a2..2cef2b68ed 100644 --- a/source/renderer/InstancingModelRenderer.h +++ b/source/renderer/InstancingModelRenderer.h @@ -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; diff --git a/source/renderer/ModelRenderer.cpp b/source/renderer/ModelRenderer.cpp index de582b3099..94c9f6e6c3 100644 --- a/source/renderer/ModelRenderer.cpp +++ b/source/renderer/ModelRenderer.cpp @@ -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 . */ -/* - * 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 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 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 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(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, 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& bucketItems = materialBuckets[key]; + bucketItems.push_back(model); + } + } - for(; bmrdata; bmrdata = bmrdata->m_Next) + std::vector sortByDistItems; + + std::vector 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 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 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(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); + } +} diff --git a/source/renderer/ModelRenderer.h b/source/renderer/ModelRenderer.h index 692546b993..e27a0c6bed 100644 --- a/source/renderer/ModelRenderer.h +++ b/source/renderer/ModelRenderer.h @@ -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 LitRenderModifierPtr; class ModelVertexRenderer; typedef shared_ptr ModelVertexRendererPtr; +class ModelRenderer; +typedef shared_ptr 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 diff --git a/source/renderer/ModelVertexRenderer.h b/source/renderer/ModelVertexRenderer.h index 2cbb011448..f8c424c36f 100644 --- a/source/renderer/ModelVertexRenderer.h +++ b/source/renderer/ModelVertexRenderer.h @@ -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; }; diff --git a/source/renderer/OverlayRenderer.cpp b/source/renderer/OverlayRenderer.cpp index 938574cc5b..b6f000ec97 100644 --- a/source/renderer/OverlayRenderer.cpp +++ b/source/renderer/OverlayRenderer.cpp @@ -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 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)); // ---------------------------------------------------------------------------------------- diff --git a/source/renderer/ParticleRenderer.cpp b/source/renderer/ParticleRenderer.cpp index 88db5f96e7..964aa2c4a4 100644 --- a/source/renderer/ParticleRenderer.cpp +++ b/source/renderer/ParticleRenderer.cpp @@ -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()); } } diff --git a/source/renderer/ParticleRenderer.h b/source/renderer/ParticleRenderer.h index 7beeeddb65..ab56086fa8 100644 --- a/source/renderer/ParticleRenderer.h +++ b/source/renderer/ParticleRenderer.h @@ -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. diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index c47a7f7eb5..65450f8edb 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -361,34 +361,34 @@ void CPatchRData::AddBlend(std::vector& 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;jCalcNormal(ix,iz,normal); - vertices[v].m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor); + vertices[v].m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal); } } diff --git a/source/renderer/PlayerRenderer.cpp b/source/renderer/PlayerRenderer.cpp deleted file mode 100644 index e68722456b..0000000000 --- a/source/renderer/PlayerRenderer.cpp +++ /dev/null @@ -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 . - */ - -/* - * 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 diff --git a/source/renderer/PlayerRenderer.h b/source/renderer/PlayerRenderer.h deleted file mode 100644 index c3e47f352f..0000000000 --- a/source/renderer/PlayerRenderer.h +++ /dev/null @@ -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 . - */ - -/* - * 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 diff --git a/source/renderer/RenderModifiers.cpp b/source/renderer/RenderModifiers.cpp index 3e1bf8a05d..ef976d3008 100644 --- a/source/renderer/RenderModifiers.cpp +++ b/source/renderer/RenderModifiers.cpp @@ -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 -/////////////////////////////////////////////////////////////////////////////////////////////// -// 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())); } diff --git a/source/renderer/RenderModifiers.h b/source/renderer/RenderModifiers.h index 0b41dec015..76008cecc5 100644 --- a/source/renderer/RenderModifiers.h +++ b/source/renderer/RenderModifiers.h @@ -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 diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 277298c7f3..a17f7290cb 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -46,6 +46,7 @@ #include "graphics/Camera.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" +#include "graphics/MaterialManager.h" #include "graphics/Model.h" #include "graphics/ModelDef.h" #include "graphics/ParticleManager.h" @@ -53,19 +54,16 @@ #include "graphics/Terrain.h" #include "graphics/Texture.h" #include "graphics/TextureManager.h" -#include "renderer/FixedFunctionModelRenderer.h" #include "renderer/HWLightingModelRenderer.h" #include "renderer/InstancingModelRenderer.h" #include "renderer/ModelRenderer.h" #include "renderer/OverlayRenderer.h" #include "renderer/ParticleRenderer.h" -#include "renderer/PlayerRenderer.h" #include "renderer/RenderModifiers.h" #include "renderer/ShadowMap.h" #include "renderer/SkyManager.h" #include "renderer/TerrainOverlay.h" #include "renderer/TerrainRenderer.h" -#include "renderer/TransparencyRenderer.h" #include "renderer/VertexBufferManager.h" #include "renderer/WaterManager.h" @@ -111,6 +109,7 @@ private: Row_Particles, Row_VBReserved, Row_VBAllocated, + Row_ShadersLoaded, // Must be last to count number of rows NumberRows @@ -206,6 +205,12 @@ CStr CRendererStatsTable::GetCellText(size_t row, size_t col) sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_VBMan.GetBytesAllocated()); return buf; + case Row_ShadersLoaded: + if (col == 0) + return "shader effects loaded"; + sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_Renderer.GetShaderManager().GetNumEffectsLoaded()); + return buf; + default: return "???"; } @@ -250,7 +255,7 @@ public: CTextureManager textureManager; /// Terrain renderer - TerrainRenderer* terrainRenderer; + TerrainRenderer terrainRenderer; /// Overlay renderer OverlayRenderer overlayRenderer; @@ -261,106 +266,48 @@ public: /// Particle renderer ParticleRenderer particleRenderer; + /// Material manager + CMaterialManager materialManager; + /// Shadow map - ShadowMap* shadow; + ShadowMap shadow; /// Various model renderers - struct Models { - // The following model renderers are aliases for the appropriate real_* - // model renderers (depending on hardware availability and current settings) - // and must be used for actual model submission and rendering - ModelRenderer* Normal; - ModelRenderer* NormalInstancing; - ModelRenderer* Player; - ModelRenderer* PlayerInstancing; - ModelRenderer* Transp; + struct Models + { + // NOTE: The current renderer design (with ModelRenderer, ModelVertexRenderer, + // RenderModifier, etc) is mostly a relic of an older design that implemented + // the different materials and rendering modes through extensive subclassing + // and hooking objects together in various combinations. + // The new design uses the CShaderManager API to abstract away the details + // of rendering, and uses a data-driven approach to materials, so there are + // now a small number of generic subclasses instead of many specialised subclasses, + // but most of the old infrastructure hasn't been refactored out yet and leads to + // some unwanted complexity. - // "Palette" of available ModelRenderers. Do not use these directly for - // rendering and submission; use the aliases above instead. - ModelRenderer* pal_NormalFF; - ModelRenderer* pal_PlayerFF; - ModelRenderer* pal_TranspFF; - ModelRenderer* pal_TranspSortAll; + // Submitted models are split on two axes: + // - Normal vs Transp[arent] - alpha-blended models are stored in a separate + // list so we can draw them above/below the alpha-blended water plane correctly + // - Instancing vs [not instancing] - with hardware lighting we don't need to + // duplicate mesh data per model instance (except for skinned models), + // so non-skinned models get different ModelVertexRenderers - ModelRenderer* pal_NormalShader; - ModelRenderer* pal_NormalInstancingShader; - ModelRenderer* pal_PlayerShader; - ModelRenderer* pal_PlayerInstancingShader; - ModelRenderer* pal_TranspShader; + ModelRendererPtr Normal; + ModelRendererPtr NormalInstancing; + ModelRendererPtr Transp; + ModelRendererPtr TranspInstancing; - ModelVertexRendererPtr VertexFF; - ModelVertexRendererPtr VertexPolygonSort; ModelVertexRendererPtr VertexRendererShader; ModelVertexRendererPtr VertexInstancingShader; - // generic RenderModifiers that are supposed to be used directly - RenderModifierPtr ModSolidColor; - RenderModifierPtr ModSolidPlayerColor; - RenderModifierPtr ModTransparentDepthShadow; - - // RenderModifiers that are selected from the palette below - RenderModifierPtr ModNormal; - RenderModifierPtr ModNormalInstancing; - RenderModifierPtr ModPlayer; - RenderModifierPtr ModPlayerInstancing; - RenderModifierPtr ModSolid; - RenderModifierPtr ModSolidInstancing; - RenderModifierPtr ModSolidPlayer; - RenderModifierPtr ModSolidPlayerInstancing; - RenderModifierPtr ModTransparent; - RenderModifierPtr ModTransparentOpaque; - RenderModifierPtr ModTransparentBlend; - - // Palette of available RenderModifiers - RenderModifierPtr ModPlainUnlit; - RenderModifierPtr ModPlayerUnlit; - RenderModifierPtr ModTransparentUnlit; - RenderModifierPtr ModTransparentOpaqueUnlit; - RenderModifierPtr ModTransparentBlendUnlit; - - RenderModifierPtr ModShaderSolidColor; - RenderModifierPtr ModShaderSolidColorInstancing; - RenderModifierPtr ModShaderSolidPlayerColor; - RenderModifierPtr ModShaderSolidPlayerColorInstancing; - LitRenderModifierPtr ModShaderNormal; - LitRenderModifierPtr ModShaderNormalInstancing; - LitRenderModifierPtr ModShaderPlayer; - LitRenderModifierPtr ModShaderPlayerInstancing; - LitRenderModifierPtr ModShaderTransparent; - LitRenderModifierPtr ModShaderTransparentOpaque; - LitRenderModifierPtr ModShaderTransparentBlend; - RenderModifierPtr ModShaderTransparentShadow; + LitRenderModifierPtr ModShader; } Model; + CShaderDefines globalContext; CRendererInternals() : IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false) { - terrainRenderer = new TerrainRenderer(); - shadow = new ShadowMap(); - - Model.pal_NormalFF = 0; - Model.pal_PlayerFF = 0; - Model.pal_TranspFF = 0; - Model.pal_TranspSortAll = 0; - - Model.pal_NormalShader = 0; - Model.pal_NormalInstancingShader = 0; - Model.pal_PlayerShader = 0; - Model.pal_PlayerInstancingShader = 0; - Model.pal_TranspShader = 0; - - Model.Normal = 0; - Model.NormalInstancing = 0; - Model.Player = 0; - Model.PlayerInstancing = 0; - Model.Transp = 0; - } - - ~CRendererInternals() - { - delete shadow; - delete terrainRenderer; } /** @@ -388,34 +335,49 @@ public: } /** - * Renders all non-transparent models with the given modifiers. + * Renders all non-alpha-blended models with the given context. */ - void CallModelRenderers( - const RenderModifierPtr& modNormal, const RenderModifierPtr& modNormalInstancing, - const RenderModifierPtr& modPlayer, const RenderModifierPtr& modPlayerInstancing, - int flags) + void CallModelRenderers(const CShaderDefines& context, int flags) { - Model.Normal->Render(modNormal, flags); - if (Model.Normal != Model.NormalInstancing) - Model.NormalInstancing->Render(modNormalInstancing, flags); + CShaderDefines contextInstancing = context; + contextInstancing.Add("USE_INSTANCING", "1"); - Model.Player->Render(modPlayer, flags); - if (Model.Player != Model.PlayerInstancing) - Model.PlayerInstancing->Render(modPlayerInstancing, flags); + Model.Normal->Render(Model.ModShader, context, flags); + if (Model.NormalInstancing) + Model.NormalInstancing->Render(Model.ModShader, contextInstancing, flags); } /** - * Filters all non-transparent models with the given modifiers. + * Renders all alpha-blended models with the given context. + */ + void CallTranspModelRenderers(const CShaderDefines& context, int flags) + { + CShaderDefines contextInstancing = context; + contextInstancing.Add("USE_INSTANCING", "1"); + + Model.Transp->Render(Model.ModShader, context, flags); + if (Model.TranspInstancing) + Model.TranspInstancing->Render(Model.ModShader, contextInstancing, flags); + } + + /** + * Filters all non-alpha-blended models. */ void FilterModels(CModelFilter& filter, int passed, int flags = 0) { Model.Normal->Filter(filter, passed, flags); - if (Model.Normal != Model.NormalInstancing) + if (Model.NormalInstancing) Model.NormalInstancing->Filter(filter, passed, flags); + } - Model.Player->Filter(filter, passed, flags); - if (Model.Player != Model.PlayerInstancing) - Model.PlayerInstancing->Filter(filter, passed, flags); + /** + * Filters all alpha-blended models. + */ + void FilterTranspModels(CModelFilter& filter, int passed, int flags = 0) + { + Model.Transp->Filter(filter, passed, flags); + if (Model.TranspInstancing) + Model.TranspInstancing->Filter(filter, passed, flags); } }; @@ -435,11 +397,8 @@ CRenderer::CRenderer() m_ModelRenderMode=SOLID; m_ClearColor[0]=m_ClearColor[1]=m_ClearColor[2]=m_ClearColor[3]=0; - m_SortAllTransparent = false; m_DisplayFrustum = false; - m_DisableCopyShadow = false; m_DisplayTerrainPriorities = false; - m_FastPlayerColor = true; m_SkipSubmit = false; m_Options.m_NoVBO = false; @@ -488,18 +447,6 @@ CRenderer::~CRenderer() { UnregisterFileReloadFunc(ReloadChangedFileCB, this); - // model rendering - delete m->Model.pal_NormalFF; - delete m->Model.pal_PlayerFF; - delete m->Model.pal_TranspFF; - delete m->Model.pal_TranspSortAll; - - delete m->Model.pal_NormalShader; - delete m->Model.pal_NormalInstancingShader; - delete m->Model.pal_PlayerShader; - delete m->Model.pal_PlayerInstancingShader; - delete m->Model.pal_TranspShader; - // we no longer UnloadAlphaMaps / UnloadWaterTextures here - // that is the responsibility of the module that asked for // them to be loaded (i.e. CGameView). @@ -556,55 +503,52 @@ void CRenderer::ReloadShaders() { ENSURE(m->IsOpen); - typedef std::map Defines; + m->globalContext = CShaderDefines(); - Defines defBasic; - if (m_Options.m_Shadows) + if (m_Caps.m_Shadows && m_Options.m_Shadows) { - defBasic["USE_SHADOW"] = "1"; + m->globalContext.Add("USE_SHADOW", "1"); if (m_Caps.m_ARBProgramShadow && m_Options.m_ARBProgramShadow) - defBasic["USE_FP_SHADOW"] = "1"; + m->globalContext.Add("USE_FP_SHADOW", "1"); if (m_Options.m_ShadowPCF) - defBasic["USE_SHADOW_PCF"] = "1"; + m->globalContext.Add("USE_SHADOW_PCF", "1"); #if !CONFIG2_GLES - defBasic["USE_SHADOW_SAMPLER"] = "1"; + m->globalContext.Add("USE_SHADOW_SAMPLER", "1"); #endif } if (m_LightEnv) - defBasic["LIGHTING_MODEL_" + m_LightEnv->GetLightingModel()] = "1"; + m->globalContext.Add(("LIGHTING_MODEL_" + m_LightEnv->GetLightingModel()).c_str(), "1"); - Defines defColored = defBasic; - defColored["USE_OBJECTCOLOR"] = "1"; + if (GetRenderPath() == RP_SHADER && m_Caps.m_ARBProgram) + m->globalContext.Add("SYS_HAS_ARB", "1"); - m->Model.ModShaderSolidColor = - RenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_solid"))); - m->Model.ModShaderSolidColorInstancing = - RenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_solid_instancing"))); + if (GetRenderPath() == RP_SHADER && m_Caps.m_VertexShader && m_Caps.m_FragmentShader) + m->globalContext.Add("SYS_HAS_GLSL", "1"); - m->Model.ModShaderSolidPlayerColor = - RenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_solid_player"))); - m->Model.ModShaderSolidPlayerColorInstancing = - RenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_solid_player_instancing"))); + if (m_Options.m_PreferGLSL) + m->globalContext.Add("SYS_PREFER_GLSL", "1"); - m->Model.ModShaderNormal = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal", defBasic))); - m->Model.ModShaderNormalInstancing = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal_instancing", defBasic))); + m->Model.ModShader = LitRenderModifierPtr(new ShaderRenderModifier()); - m->Model.ModShaderPlayer = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal", defColored))); - m->Model.ModShaderPlayerInstancing = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal_instancing", defColored))); + bool cpuLighting = (GetRenderPath() == RP_FIXED); + m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelVertexRenderer(cpuLighting)); + m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer); - m->Model.ModShaderTransparent = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_transparent", defBasic))); - m->Model.ModShaderTransparentOpaque = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_transparent_opaque", defBasic))); - m->Model.ModShaderTransparentBlend = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_transparent_blend", defBasic))); - m->Model.ModShaderTransparentShadow = - LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_transparent_shadow", defBasic))); + m->Model.Normal = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader)); + m->Model.Transp = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader)); + + // Use instancing renderers in shader mode + if (GetRenderPath() == RP_SHADER) + { + m->Model.NormalInstancing = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader)); + m->Model.TranspInstancing = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader)); + } + else + { + m->Model.NormalInstancing.reset(); + m->Model.TranspInstancing.reset(); + } m->ShadersDirty = false; } @@ -617,39 +561,6 @@ bool CRenderer::Open(int width, int height) // on card capabilities. EnumCaps(); - // model rendering -#if !CONFIG2_GLES - m->Model.VertexFF = ModelVertexRendererPtr(new FixedFunctionModelRenderer); - m->Model.VertexPolygonSort = ModelVertexRendererPtr(new PolygonSortModelRenderer); -#endif - m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelRenderer); - m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer); - -#if !CONFIG2_GLES - m->Model.pal_NormalFF = new BatchModelRenderer(m->Model.VertexFF); - m->Model.pal_PlayerFF = new BatchModelRenderer(m->Model.VertexFF); - m->Model.pal_TranspFF = new SortModelRenderer(m->Model.VertexFF); - - m->Model.pal_TranspSortAll = new SortModelRenderer(m->Model.VertexPolygonSort); -#endif - - m->Model.pal_NormalShader = new BatchModelRenderer(m->Model.VertexRendererShader); - m->Model.pal_NormalInstancingShader = new BatchModelRenderer(m->Model.VertexInstancingShader); - m->Model.pal_PlayerShader = new BatchModelRenderer(m->Model.VertexRendererShader); - m->Model.pal_PlayerInstancingShader = new BatchModelRenderer(m->Model.VertexInstancingShader); - m->Model.pal_TranspShader = new SortModelRenderer(m->Model.VertexRendererShader); - -#if !CONFIG2_GLES - m->Model.ModPlainUnlit = RenderModifierPtr(new PlainRenderModifier); - SetFastPlayerColor(true); - m->Model.ModSolidColor = RenderModifierPtr(new SolidColorRenderModifier); - m->Model.ModSolidPlayerColor = RenderModifierPtr(new SolidPlayerColorRender); - m->Model.ModTransparentUnlit = RenderModifierPtr(new TransparentRenderModifier); - m->Model.ModTransparentOpaqueUnlit = RenderModifierPtr(new TransparentOpaqueRenderModifier); - m->Model.ModTransparentBlendUnlit = RenderModifierPtr(new TransparentBlendRenderModifier); - m->Model.ModTransparentDepthShadow = RenderModifierPtr(new TransparentDepthShadowModifier); -#endif - // Dimensions m_Width = width; m_Height = height; @@ -683,7 +594,7 @@ bool CRenderer::Open(int width, int height) void CRenderer::Resize(int width,int height) { // need to recreate the shadow map object to resize the shadow texture - m->shadow->RecreateTexture(); + m->shadow.RecreateTexture(); m_Width = width; m_Height = height; @@ -706,6 +617,7 @@ void CRenderer::SetOptionBool(enum Option opt,bool value) break; case OPT_SHADOWPCF: m_Options.m_ShadowPCF=value; + MakeShadersDirty(); break; default: debug_warn(L"CRenderer::SetOptionBool: unknown option"); @@ -734,19 +646,6 @@ bool CRenderer::GetOptionBool(enum Option opt) const return false; } -void CRenderer::SetOptionFloat(enum Option opt, float val) -{ - switch(opt) - { - case OPT_LODBIAS: - m_Options.m_LodBias = val; - break; - default: - debug_warn(L"CRenderer::SetOptionFloat: unknown option"); - break; - } -} - ////////////////////////////////////////////////////////////////////////////////////////// // SetRenderPath: Select the preferred render path. // This may only be called before Open(), because the layout of vertex arrays and other @@ -780,6 +679,8 @@ void CRenderer::SetRenderPath(RenderPath rp) m_Options.m_RenderPath = rp; + MakeShadersDirty(); + // We might need to regenerate some render data after changing path if (g_Game) g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR); @@ -810,29 +711,6 @@ CRenderer::RenderPath CRenderer::GetRenderPathByName(const CStr& name) } -////////////////////////////////////////////////////////////////////////////////////////// -// SetFastPlayerColor -void CRenderer::SetFastPlayerColor(bool fast) -{ -#if !CONFIG2_GLES - m_FastPlayerColor = fast; - - if (m_FastPlayerColor) - { - if (!FastPlayerColorRender::IsAvailable()) - { - LOGWARNING(L"Falling back to slower player color rendering."); - m_FastPlayerColor = false; - } - } - - if (m_FastPlayerColor) - m->Model.ModPlayerUnlit = RenderModifierPtr(new FastPlayerColorRender); - else - m->Model.ModPlayerUnlit = RenderModifierPtr(new SlowPlayerColorRender); -#endif -} - ////////////////////////////////////////////////////////////////////////////////////////// // BeginFrame: signal frame start void CRenderer::BeginFrame() @@ -844,78 +722,11 @@ void CRenderer::BeginFrame() // choose model renderers for this frame - if (m_Options.m_RenderPath == RP_SHADER) - { - if (m->ShadersDirty) - ReloadShaders(); + if (m->ShadersDirty) + ReloadShaders(); - m->Model.ModShaderNormal->SetShadowMap(m->shadow); - m->Model.ModShaderNormal->SetLightEnv(m_LightEnv); - - m->Model.ModShaderNormalInstancing->SetShadowMap(m->shadow); - m->Model.ModShaderNormalInstancing->SetLightEnv(m_LightEnv); - - m->Model.ModShaderPlayer->SetShadowMap(m->shadow); - m->Model.ModShaderPlayer->SetLightEnv(m_LightEnv); - - m->Model.ModShaderPlayerInstancing->SetShadowMap(m->shadow); - m->Model.ModShaderPlayerInstancing->SetLightEnv(m_LightEnv); - - m->Model.ModShaderTransparent->SetShadowMap(m->shadow); - m->Model.ModShaderTransparent->SetLightEnv(m_LightEnv); - - m->Model.ModShaderTransparentOpaque->SetShadowMap(m->shadow); - m->Model.ModShaderTransparentOpaque->SetLightEnv(m_LightEnv); - - m->Model.ModShaderTransparentBlend->SetShadowMap(m->shadow); - m->Model.ModShaderTransparentBlend->SetLightEnv(m_LightEnv); - - m->Model.ModNormal = m->Model.ModShaderNormal; - m->Model.ModNormalInstancing = m->Model.ModShaderNormalInstancing; - m->Model.ModPlayer = m->Model.ModShaderPlayer; - m->Model.ModPlayerInstancing = m->Model.ModShaderPlayerInstancing; - m->Model.ModSolid = m->Model.ModShaderSolidColor; - m->Model.ModSolidInstancing = m->Model.ModShaderSolidColorInstancing; - m->Model.ModSolidPlayer = m->Model.ModShaderSolidPlayerColor; - m->Model.ModSolidPlayerInstancing = m->Model.ModShaderSolidPlayerColorInstancing; - m->Model.ModTransparent = m->Model.ModShaderTransparent; - m->Model.ModTransparentOpaque = m->Model.ModShaderTransparentOpaque; - m->Model.ModTransparentBlend = m->Model.ModShaderTransparentBlend; - - m->Model.Normal = m->Model.pal_NormalShader; - m->Model.NormalInstancing = m->Model.pal_NormalInstancingShader; - - m->Model.Player = m->Model.pal_PlayerShader; - m->Model.PlayerInstancing = m->Model.pal_PlayerInstancingShader; - - m->Model.Transp = m->Model.pal_TranspShader; - } - else - { - m->Model.ModNormal = m->Model.ModPlainUnlit; - m->Model.ModNormalInstancing = m->Model.ModPlainUnlit; - m->Model.ModPlayer = m->Model.ModPlayerUnlit; - m->Model.ModPlayerInstancing = m->Model.ModPlayerUnlit; - m->Model.ModTransparent = m->Model.ModTransparentUnlit; - m->Model.ModTransparentOpaque = m->Model.ModTransparentOpaqueUnlit; - m->Model.ModTransparentBlend = m->Model.ModTransparentBlendUnlit; - - m->Model.NormalInstancing = m->Model.pal_NormalFF; - m->Model.Normal = m->Model.pal_NormalFF; - - m->Model.PlayerInstancing = m->Model.pal_PlayerFF; - m->Model.Player = m->Model.pal_PlayerFF; - - m->Model.ModSolid = m->Model.ModSolidColor; - m->Model.ModSolidInstancing = m->Model.ModSolidColor; - m->Model.ModSolidPlayer = m->Model.ModSolidPlayerColor; - m->Model.ModSolidPlayerInstancing = m->Model.ModSolidPlayerColor; - - if (m_SortAllTransparent) - m->Model.Transp = m->Model.pal_TranspSortAll; - else - m->Model.Transp = m->Model.pal_TranspFF; - } + m->Model.ModShader->SetShadowMap(&m->shadow); + m->Model.ModShader->SetLightEnv(m_LightEnv); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -928,55 +739,46 @@ void CRenderer::SetClearColor(SColor4ub color) m_ClearColor[3] = float(color.A) / 255.0f; } -void CRenderer::RenderShadowMap() +void CRenderer::RenderShadowMap(const CShaderDefines& context) { PROFILE3_GPU("shadow map"); - m->shadow->BeginRender(); - -#if CONFIG2_GLES -#warning TODO: implement shadow transparency for GLES -#else - float shadowTransp = m_LightEnv->GetTerrainShadowTransparency(); - glColor3f(shadowTransp, shadowTransp, shadowTransp); -#endif + m->shadow.BeginRender(); { PROFILE("render patches"); - m->terrainRenderer->RenderPatches(); + m->terrainRenderer.RenderPatches(); } + CShaderDefines contextCast = context; + contextCast.Add("MODE_SHADOWCAST", "1"); + { PROFILE("render models"); - m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing, - m->Model.ModSolid, m->Model.ModSolidInstancing, MODELFLAG_CASTSHADOWS); + m->CallModelRenderers(contextCast, MODELFLAG_CASTSHADOWS); } { PROFILE("render transparent models"); // disable face-culling for two-sided models glDisable(GL_CULL_FACE); - m->Model.Transp->Render(m->Model.ModShaderTransparentShadow, MODELFLAG_CASTSHADOWS); + m->CallTranspModelRenderers(contextCast, MODELFLAG_CASTSHADOWS); glEnable(GL_CULL_FACE); } -#if !CONFIG2_GLES - glColor3f(1.0, 1.0, 1.0); -#endif - - m->shadow->EndRender(); + m->shadow.EndRender(); m->SetOpenGLCamera(m_ViewCamera); } -void CRenderer::RenderPatches(const CFrustum* frustum) +void CRenderer::RenderPatches(const CShaderDefines& context, const CFrustum* frustum) { PROFILE3_GPU("patches"); bool filtered = false; if (frustum) { - if (!m->terrainRenderer->CullPatches(frustum)) + if (!m->terrainRenderer.CullPatches(frustum)) return; filtered = true; @@ -994,9 +796,9 @@ void CRenderer::RenderPatches(const CFrustum* frustum) // render all the patches, including blend pass if (GetRenderPath() == RP_SHADER) - m->terrainRenderer->RenderTerrainShader((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0, filtered); + m->terrainRenderer.RenderTerrainShader(context, (m_Caps.m_Shadows && m_Options.m_Shadows) ? &m->shadow : 0, filtered); else - m->terrainRenderer->RenderTerrain(filtered); + m->terrainRenderer.RenderTerrain(filtered); #if !CONFIG2_GLES @@ -1018,14 +820,14 @@ void CRenderer::RenderPatches(const CFrustum* frustum) glLineWidth(2.0f); // render tiles edges - m->terrainRenderer->RenderPatches(filtered); + m->terrainRenderer.RenderPatches(filtered); // set color for outline glColor3f(0, 0, 1); glLineWidth(4.0f); // render outline of each patch - m->terrainRenderer->RenderOutlines(filtered); + m->terrainRenderer.RenderOutlines(filtered); // .. and restore the renderstates glLineWidth(1.0f); @@ -1048,7 +850,7 @@ private: const CFrustum& m_Frustum; }; -void CRenderer::RenderModels(const CFrustum* frustum) +void CRenderer::RenderModels(const CShaderDefines& context, const CFrustum* frustum) { PROFILE3_GPU("models"); @@ -1067,8 +869,7 @@ void CRenderer::RenderModels(const CFrustum* frustum) } #endif - m->CallModelRenderers(m->Model.ModNormal, m->Model.ModNormalInstancing, - m->Model.ModPlayer, m->Model.ModPlayerInstancing, flags); + m->CallModelRenderers(context, flags); #if !CONFIG2_GLES if (m_ModelRenderMode == WIREFRAME) @@ -1077,19 +878,20 @@ void CRenderer::RenderModels(const CFrustum* frustum) } else if (m_ModelRenderMode == EDGED_FACES) { + CShaderDefines contextWireframe = context; + contextWireframe.Add("MODE_WIREFRAME", "1"); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_TEXTURE_2D); - glColor3f(1.0f, 1.0f, 0.0f); - m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing, - m->Model.ModSolid, m->Model.ModSolidInstancing, flags); + m->CallModelRenderers(contextWireframe, flags); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } #endif } -void CRenderer::RenderTransparentModels(ETransparentMode transparentMode, const CFrustum* frustum) +void CRenderer::RenderTransparentModels(const CShaderDefines& context, ETransparentMode transparentMode, const CFrustum* frustum) { PROFILE3_GPU("transparent models"); @@ -1098,7 +900,7 @@ void CRenderer::RenderTransparentModels(ETransparentMode transparentMode, const { flags = MODELFLAG_FILTERED; CModelCuller culler(*frustum); - m->Model.Transp->Filter(culler, flags); + m->FilterTranspModels(culler, flags); } #if !CONFIG2_GLES @@ -1113,12 +915,17 @@ void CRenderer::RenderTransparentModels(ETransparentMode transparentMode, const if (flags) glDisable(GL_CULL_FACE); - if (transparentMode == TRANSPARENT_OPAQUE) - m->Model.Transp->Render(m->Model.ModTransparentOpaque, flags); - else if (transparentMode == TRANSPARENT_BLEND) - m->Model.Transp->Render(m->Model.ModTransparentBlend, flags); - else - m->Model.Transp->Render(m->Model.ModTransparent, flags); + CShaderDefines contextOpaque = context; + contextOpaque.Add("ALPHABLEND_PASS_OPAQUE", "1"); + + CShaderDefines contextBlend = context; + contextBlend.Add("ALPHABLEND_PASS_BLEND", "1"); + + if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_OPAQUE) + m->CallTranspModelRenderers(contextOpaque, flags); + + if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_BLEND) + m->CallTranspModelRenderers(contextBlend, flags); if (flags) glEnable(GL_CULL_FACE); @@ -1131,11 +938,13 @@ void CRenderer::RenderTransparentModels(ETransparentMode transparentMode, const } else if (m_ModelRenderMode == EDGED_FACES) { + CShaderDefines contextWireframe = contextOpaque; + contextWireframe.Add("MODE_WIREFRAME", "1"); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_TEXTURE_2D); - glColor3f(1.0f, 0.0f, 0.0f); - m->Model.Transp->Render(m->Model.ModSolid, flags); + m->CallTranspModelRenderers(contextWireframe, flags); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } @@ -1183,7 +992,7 @@ void CRenderer::SetObliqueFrustumClipping(const CVector4D& worldPlane) /////////////////////////////////////////////////////////////////////////////////////////////////// // RenderReflections: render the water reflections to the reflection texture -SScreenRect CRenderer::RenderReflections(const CBoundingBoxAligned& scissor) +SScreenRect CRenderer::RenderReflections(const CShaderDefines& context, const CBoundingBoxAligned& scissor) { PROFILE3_GPU("water reflections"); @@ -1239,11 +1048,11 @@ SScreenRect CRenderer::RenderReflections(const CBoundingBoxAligned& scissor) // Render sky, terrain and models m->skyManager.RenderSky(); ogl_WarnIfError(); - RenderPatches(&m_ViewCamera.GetFrustum()); + RenderPatches(context, &m_ViewCamera.GetFrustum()); ogl_WarnIfError(); - RenderModels(&m_ViewCamera.GetFrustum()); + RenderModels(context, &m_ViewCamera.GetFrustum()); ogl_WarnIfError(); - RenderTransparentModels(TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum()); + RenderTransparentModels(context, TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum()); ogl_WarnIfError(); glFrontFace(GL_CCW); @@ -1270,7 +1079,7 @@ SScreenRect CRenderer::RenderReflections(const CBoundingBoxAligned& scissor) /////////////////////////////////////////////////////////////////////////////////////////////////// // RenderRefractions: render the water refractions to the refraction texture -SScreenRect CRenderer::RenderRefractions(const CBoundingBoxAligned &scissor) +SScreenRect CRenderer::RenderRefractions(const CShaderDefines& context, const CBoundingBoxAligned &scissor) { PROFILE3_GPU("water refractions"); @@ -1319,11 +1128,11 @@ SScreenRect CRenderer::RenderRefractions(const CBoundingBoxAligned &scissor) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Render terrain and models - RenderPatches(&m_ViewCamera.GetFrustum()); + RenderPatches(context, &m_ViewCamera.GetFrustum()); ogl_WarnIfError(); - RenderModels(&m_ViewCamera.GetFrustum()); + RenderModels(context, &m_ViewCamera.GetFrustum()); ogl_WarnIfError(); - RenderTransparentModels(TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum()); + RenderTransparentModels(context, TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum()); ogl_WarnIfError(); glDisable(GL_SCISSOR_TEST); @@ -1346,10 +1155,16 @@ SScreenRect CRenderer::RenderRefractions(const CBoundingBoxAligned &scissor) } -void CRenderer::RenderSilhouettes() +void CRenderer::RenderSilhouettes(const CShaderDefines& context) { PROFILE3_GPU("silhouettes"); + CShaderDefines contextOccluder = context; + contextOccluder.Add("MODE_SILHOUETTEOCCLUDER", "1"); + + CShaderDefines contextDisplay = context; + contextDisplay.Add("MODE_SILHOUETTEDISPLAY", "1"); + // Render silhouettes of units hidden behind terrain or occluders. // To avoid breaking the standard rendering of alpha-blended objects, this // has to be done in a separate pass. @@ -1377,27 +1192,18 @@ void CRenderer::RenderSilhouettes() // protrude into the ground, only occlude with the back faces of the // terrain (so silhouettes will still display when behind hills) glCullFace(GL_FRONT); - m->terrainRenderer->RenderPatches(); + m->terrainRenderer.RenderPatches(); glCullFace(GL_BACK); } { PROFILE("render model occluders"); - m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing, - m->Model.ModSolid, m->Model.ModSolidInstancing, MODELFLAG_SILHOUETTE_OCCLUDER); + m->CallModelRenderers(contextOccluder, MODELFLAG_SILHOUETTE_OCCLUDER); } { PROFILE("render transparent occluders"); - if (GetRenderPath() == RP_SHADER) - { - m->Model.Transp->Render(m->Model.ModShaderTransparentShadow, MODELFLAG_SILHOUETTE_OCCLUDER); - } - else - { - // Reuse the depth shadow modifier to get alpha-tested rendering - m->Model.Transp->Render(m->Model.ModTransparentDepthShadow, MODELFLAG_SILHOUETTE_OCCLUDER); - } + m->CallTranspModelRenderers(contextOccluder, MODELFLAG_SILHOUETTE_OCCLUDER); } glDepthFunc(GL_GEQUAL); @@ -1431,8 +1237,7 @@ void CRenderer::RenderSilhouettes() { PROFILE("render models"); - m->CallModelRenderers(m->Model.ModSolidPlayer, m->Model.ModSolidPlayerInstancing, - m->Model.ModSolidPlayer, m->Model.ModSolidPlayerInstancing, MODELFLAG_SILHOUETTE_DISPLAY); + m->CallModelRenderers(contextDisplay, MODELFLAG_SILHOUETTE_DISPLAY); // (This won't render transparent objects with SILHOUETTE_DISPLAY - will // we have any units that need that?) } @@ -1493,6 +1298,8 @@ void CRenderer::RenderSubmissions() { PROFILE3("render submissions"); + CShaderDefines context = m->globalContext; + ogl_WarnIfError(); // Set the camera @@ -1502,23 +1309,22 @@ void CRenderer::RenderSubmissions() { PROFILE3("prepare models"); m->Model.Normal->PrepareModels(); - m->Model.Player->PrepareModels(); - if (m->Model.Normal != m->Model.NormalInstancing) - m->Model.NormalInstancing->PrepareModels(); - if (m->Model.Player != m->Model.PlayerInstancing) - m->Model.PlayerInstancing->PrepareModels(); m->Model.Transp->PrepareModels(); + if (m->Model.NormalInstancing) + m->Model.NormalInstancing->PrepareModels(); + if (m->Model.TranspInstancing) + m->Model.TranspInstancing->PrepareModels(); } - m->terrainRenderer->PrepareForRendering(); + m->terrainRenderer.PrepareForRendering(); m->overlayRenderer.PrepareForRendering(); - m->particleRenderer.PrepareForRendering(); + m->particleRenderer.PrepareForRendering(context); if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER) { - RenderShadowMap(); + RenderShadowMap(context); } { @@ -1532,11 +1338,11 @@ void CRenderer::RenderSubmissions() CBoundingBoxAligned waterScissor; if (m_WaterManager->m_RenderWater) { - waterScissor = m->terrainRenderer->ScissorWater(m_ViewCamera.GetViewProjection()); + waterScissor = m->terrainRenderer.ScissorWater(m_ViewCamera.GetViewProjection()); if (waterScissor.GetVolume() > 0 && m_WaterManager->WillRenderFancyWater()) { - SScreenRect reflectionScissor = RenderReflections(waterScissor); - SScreenRect refractionScissor = RenderRefractions(waterScissor); + SScreenRect reflectionScissor = RenderReflections(context, waterScissor); + SScreenRect refractionScissor = RenderRefractions(context, waterScissor); PROFILE3_GPU("water scissor"); SScreenRect dirty; @@ -1556,15 +1362,9 @@ void CRenderer::RenderSubmissions() } // render submitted patches and models - RenderPatches(); + RenderPatches(context); ogl_WarnIfError(); - if (g_Game) - { -// g_Game->GetWorld()->GetTerritoryManager()->RenderTerritories(); // TODO: implement in new sim system - ogl_WarnIfError(); - } - // render debug-related terrain overlays TerrainOverlay::RenderOverlays(); ogl_WarnIfError(); @@ -1573,27 +1373,27 @@ void CRenderer::RenderSubmissions() m->overlayRenderer.RenderOverlaysBeforeWater(); ogl_WarnIfError(); - RenderModels(); + RenderModels(context); ogl_WarnIfError(); // render water if (m_WaterManager->m_RenderWater && g_Game && waterScissor.GetVolume() > 0) { // render transparent stuff, but only the solid parts that can occlude block water - RenderTransparentModels(TRANSPARENT_OPAQUE); + RenderTransparentModels(context, TRANSPARENT_OPAQUE); ogl_WarnIfError(); - m->terrainRenderer->RenderWater(); + m->terrainRenderer.RenderWater(); ogl_WarnIfError(); // render transparent stuff again, but only the blended parts that overlap water - RenderTransparentModels(TRANSPARENT_BLEND); + RenderTransparentModels(context, TRANSPARENT_BLEND); ogl_WarnIfError(); } else { // render transparent stuff, so it can overlap models/terrain - RenderTransparentModels(TRANSPARENT); + RenderTransparentModels(context, TRANSPARENT); ogl_WarnIfError(); } @@ -1605,7 +1405,7 @@ void CRenderer::RenderSubmissions() RenderParticles(); ogl_WarnIfError(); - RenderSilhouettes(); + RenderSilhouettes(context); #if !CONFIG2_GLES // Clean up texture blend mode so particles and other things render OK @@ -1617,8 +1417,8 @@ void CRenderer::RenderSubmissions() if (m_DisplayFrustum) { DisplayFrustum(); - m->shadow->RenderDebugBounds(); - m->shadow->RenderDebugTexture(); + m->shadow.RenderDebugBounds(); + m->shadow.RenderDebugTexture(); ogl_WarnIfError(); } @@ -1634,18 +1434,17 @@ void CRenderer::EndFrame() PROFILE3("end frame"); // empty lists - m->terrainRenderer->EndFrame(); + m->terrainRenderer.EndFrame(); m->overlayRenderer.EndFrame(); m->particleRenderer.EndFrame(); // Finish model renderers m->Model.Normal->EndFrame(); - m->Model.Player->EndFrame(); - if (m->Model.Normal != m->Model.NormalInstancing) - m->Model.NormalInstancing->EndFrame(); - if (m->Model.Player != m->Model.PlayerInstancing) - m->Model.PlayerInstancing->EndFrame(); m->Model.Transp->EndFrame(); + if (m->Model.NormalInstancing) + m->Model.NormalInstancing->EndFrame(); + if (m->Model.TranspInstancing) + m->Model.TranspInstancing->EndFrame(); ogl_tex_bind(0, 0); @@ -1695,7 +1494,7 @@ void CRenderer::RenderTextOverlays() PROFILE3_GPU("text overlays"); if (m_DisplayTerrainPriorities) - m->terrainRenderer->RenderPriorities(); + m->terrainRenderer.RenderPriorities(); ogl_WarnIfError(); } @@ -1710,7 +1509,7 @@ void CRenderer::SetSceneCamera(const CCamera& viewCamera, const CCamera& cullCam m_CullCamera = cullCamera; if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER) - m->shadow->SetupFrame(m_CullCamera, m_LightEnv->GetSunDir()); + m->shadow.SetupFrame(m_CullCamera, m_LightEnv->GetSunDir()); } @@ -1721,7 +1520,7 @@ void CRenderer::SetViewport(const SViewPort &vp) void CRenderer::Submit(CPatch* patch) { - m->terrainRenderer->Submit(patch); + m->terrainRenderer.Submit(patch); } void CRenderer::Submit(SOverlayLine* overlay) @@ -1741,7 +1540,7 @@ void CRenderer::Submit(SOverlaySprite* overlay) void CRenderer::Submit(CModelDecal* decal) { - m->terrainRenderer->Submit(decal); + m->terrainRenderer.Submit(decal); } void CRenderer::Submit(CParticleEmitter* emitter) @@ -1751,9 +1550,9 @@ void CRenderer::Submit(CParticleEmitter* emitter) void CRenderer::SubmitNonRecursive(CModel* model) { - if (model->GetFlags() & MODELFLAG_CASTSHADOWS) { -// PROFILE( "updating shadow bounds" ); - m->shadow->AddShadowedBound(model->GetWorldBounds()); + if (model->GetFlags() & MODELFLAG_CASTSHADOWS) + { + m->shadow.AddShadowedBound(model->GetWorldBounds()); } // Tricky: The call to GetWorldBounds() above can invalidate the position @@ -1764,20 +1563,16 @@ void CRenderer::SubmitNonRecursive(CModel* model) if (model->GetModelDef()->GetNumBones() == 0) canUseInstancing = true; - if (model->GetMaterial().IsPlayer()) + if (model->GetMaterial().UsesAlphaBlending()) { - if (canUseInstancing) - m->Model.PlayerInstancing->Submit(model); + if (canUseInstancing && m->Model.TranspInstancing) + m->Model.TranspInstancing->Submit(model); else - m->Model.Player->Submit(model); - } - else if (model->GetMaterial().UsesAlpha()) - { - m->Model.Transp->Submit(model); + m->Model.Transp->Submit(model); } else { - if (canUseInstancing) + if (canUseInstancing && m->Model.NormalInstancing) m->Model.NormalInstancing->Submit(model); else m->Model.Normal->Submit(model); @@ -1987,21 +1782,6 @@ void CRenderer::MakeShadersDirty() /////////////////////////////////////////////////////////////////////////////////////////////////// // Scripting Interface -jsval CRenderer::JSI_GetFastPlayerColor(JSContext*) -{ - return ToJSVal(m_FastPlayerColor); -} - -void CRenderer::JSI_SetFastPlayerColor(JSContext* ctx, jsval newval) -{ - bool fast; - - if (!ToPrimitive(ctx, newval, fast)) - return; - - SetFastPlayerColor(fast); -} - jsval CRenderer::JSI_GetRenderPath(JSContext*) { return ToJSVal(GetRenderPathName(m_Options.m_RenderPath)); @@ -2019,7 +1799,7 @@ void CRenderer::JSI_SetRenderPath(JSContext* ctx, jsval newval) jsval CRenderer::JSI_GetDepthTextureBits(JSContext*) { - return ToJSVal(m->shadow->GetDepthTextureBits()); + return ToJSVal(m->shadow.GetDepthTextureBits()); } void CRenderer::JSI_SetDepthTextureBits(JSContext* ctx, jsval newval) @@ -2029,7 +1809,7 @@ void CRenderer::JSI_SetDepthTextureBits(JSContext* ctx, jsval newval) if (!ToPrimitive(ctx, newval, depthTextureBits)) return; - m->shadow->SetDepthTextureBits(depthTextureBits); + m->shadow.SetDepthTextureBits(depthTextureBits); } jsval CRenderer::JSI_GetShadows(JSContext*) @@ -2039,9 +1819,7 @@ jsval CRenderer::JSI_GetShadows(JSContext*) void CRenderer::JSI_SetShadows(JSContext* ctx, jsval newval) { - if (!ToPrimitive(ctx, newval, m_Options.m_Shadows)) - return; - + ToPrimitive(ctx, newval, m_Options.m_Shadows); ReloadShaders(); } @@ -2055,7 +1833,7 @@ void CRenderer::JSI_SetShadowAlphaFix(JSContext* ctx, jsval newval) if (!ToPrimitive(ctx, newval, m_Options.m_ShadowAlphaFix)) return; - m->shadow->RecreateTexture(); + m->shadow.RecreateTexture(); } jsval CRenderer::JSI_GetShadowPCF(JSContext*) @@ -2065,9 +1843,7 @@ jsval CRenderer::JSI_GetShadowPCF(JSContext*) void CRenderer::JSI_SetShadowPCF(JSContext* ctx, jsval newval) { - if (!ToPrimitive(ctx, newval, m_Options.m_ShadowPCF)) - return; - + ToPrimitive(ctx, newval, m_Options.m_ShadowPCF); ReloadShaders(); } @@ -2078,8 +1854,8 @@ jsval CRenderer::JSI_GetPreferGLSL(JSContext*) void CRenderer::JSI_SetPreferGLSL(JSContext* ctx, jsval newval) { - if (!ToPrimitive(ctx, newval, m_Options.m_PreferGLSL)) - return; + ToPrimitive(ctx, newval, m_Options.m_PreferGLSL); + ReloadShaders(); } jsval CRenderer::JSI_GetSky(JSContext*) @@ -2096,13 +1872,10 @@ void CRenderer::JSI_SetSky(JSContext* ctx, jsval newval) void CRenderer::ScriptingInit() { - AddProperty(L"fastPlayerColor", &CRenderer::JSI_GetFastPlayerColor, &CRenderer::JSI_SetFastPlayerColor); AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath); - AddProperty(L"sortAllTransparent", &CRenderer::m_SortAllTransparent); AddProperty(L"displayFrustum", &CRenderer::m_DisplayFrustum); AddProperty(L"shadowZBias", &CRenderer::m_ShadowZBias); AddProperty(L"shadowMapSize", &CRenderer::m_ShadowMapSize); - AddProperty(L"disableCopyShadow", &CRenderer::m_DisableCopyShadow); AddProperty(L"shadows", &CRenderer::JSI_GetShadows, &CRenderer::JSI_SetShadows); AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits); AddProperty(L"shadowAlphaFix", &CRenderer::JSI_GetShadowAlphaFix, &CRenderer::JSI_SetShadowAlphaFix); @@ -2129,3 +1902,8 @@ CParticleManager& CRenderer::GetParticleManager() { return m->particleManager; } + +CMaterialManager& CRenderer::GetMaterialManager() +{ + return m->materialManager; +} diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h index 337bc9f5a2..e7a33f1ef0 100644 --- a/source/renderer/Renderer.h +++ b/source/renderer/Renderer.h @@ -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. */ diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index afc1ff3be6..1e41ba49cf 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -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 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 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); diff --git a/source/renderer/TerrainRenderer.h b/source/renderer/TerrainRenderer.h index 48bb53e873..c3df37c9e5 100644 --- a/source/renderer/TerrainRenderer.h +++ b/source/renderer/TerrainRenderer.h @@ -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. diff --git a/source/renderer/TransparencyRenderer.cpp b/source/renderer/TransparencyRenderer.cpp deleted file mode 100644 index 2db6a23175..0000000000 --- a/source/renderer/TransparencyRenderer.cpp +++ /dev/null @@ -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 . - */ - -/* - * ModelRenderer implementation that sorts models and/or polygons based - * on distance from viewer, for transparency rendering. - */ - -#include "precompiled.h" - -#include -#include - -#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 UVit = m_UV.GetIterator(); - - 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 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 IndexSorter; - - CModelDefPtr mdef = m_Model->GetModelDef(); - size_t numFaces = mdef->GetNumFaces(); - const SModelFace* faces = mdef->GetFaces(); - - if (IndexSorter.size() < numFaces) - IndexSorter.resize(numFaces); - - VertexArrayIterator Position = m_Position.GetIterator(); - 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 Position = psmdl->m_Position.GetIterator(); - VertexArrayIterator Normal = VertexArrayIterator(m->normals, 16); - - ModelRenderer::BuildPositionAndNormals(model, Position, Normal); - - VertexArrayIterator Color = psmdl->m_Color.GetIterator(); - - 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 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::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::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::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 diff --git a/source/renderer/TransparencyRenderer.h b/source/renderer/TransparencyRenderer.h deleted file mode 100644 index 21dd2c5828..0000000000 --- a/source/renderer/TransparencyRenderer.h +++ /dev/null @@ -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 . - */ - -/* - * 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 diff --git a/source/test_setup.cpp b/source/test_setup.cpp index fd1be36179..027403f968 100644 --- a/source/test_setup.cpp +++ b/source/test_setup.cpp @@ -78,6 +78,8 @@ class MiscSetup : public CxxTest::GlobalFixture setlocale(LC_CTYPE, "UTF-8"); #endif + ThreadUtil::SetMainThread(); + g_Profiler2.Initialise(); return true;