From 6bc33fe8bde44fb7077ed4d653d005b7a2dc43db Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Tue, 3 Apr 2012 18:44:46 +0000 Subject: [PATCH] Update renderer design to be more flexible and data-driven based on material and shader definitions. Support conditional expressions in shader effect XML files. Consolidate fixed-function model rendering into the shader system. Remove lots of now-obsolete renderer code. Move shader defines from std::map to new class with interned data, for performance. Move texture from model into material. Alleviate singletonitis. Remove obsolete lodbias setting. Remove unused terrain shadow transparency. This was SVN commit r11423. --- binaries/data/config/default.cfg | 5 - .../mods/public/art/materials/basic_trans.xml | 11 +- .../mods/public/art/materials/default.xml | 4 + .../mods/public/art/materials/objectcolor.xml | 10 +- .../public/art/materials/player_trans.xml | 10 +- .../mods/public/shaders/effects/model.xml | 67 ++ .../shaders/effects/model_alphablend.xml | 126 +++ .../public/shaders/effects/model_normal.xml | 14 - .../effects/model_normal_instancing.xml | 14 - .../public/shaders/effects/model_solid.xml | 14 - .../effects/model_solid_instancing.xml | 16 - .../shaders/effects/model_solid_player.xml | 14 - .../effects/model_solid_player_instancing.xml | 16 - .../shaders/effects/model_transparent.xml | 34 - .../effects/model_transparent_blend.xml | 25 - .../effects/model_transparent_opaque.xml | 22 - .../effects/model_transparent_shadow.xml | 18 - .../mods/public/shaders/glsl/model_common.fs | 15 +- .../public/shaders/glsl/terrain_common.vs | 4 +- .../data/mods/public/shaders/model_common.fp | 11 +- .../mods/public/shaders/model_common_arb.xml | 4 +- binaries/data/mods/public/shaders/program.rng | 10 + binaries/data/mods/public/shaders/solid.xml | 1 + .../data/mods/public/shaders/solid_player.xml | 1 + .../data/mods/public/shaders/solid_tex.xml | 1 + source/graphics/LightEnv.cpp | 16 +- source/graphics/LightEnv.h | 82 +- source/graphics/MapReader.cpp | 5 - source/graphics/Material.cpp | 59 +- source/graphics/Material.h | 55 +- source/graphics/MaterialManager.cpp | 111 +-- source/graphics/MaterialManager.h | 15 +- source/graphics/Model.cpp | 6 - source/graphics/Model.h | 8 +- source/graphics/ObjectBase.cpp | 3 + source/graphics/ObjectEntry.cpp | 6 +- source/graphics/ShaderDefines.cpp | 159 ++++ source/graphics/ShaderDefines.h | 110 +++ source/graphics/ShaderManager.cpp | 177 +++- source/graphics/ShaderManager.h | 60 +- source/graphics/ShaderProgram.cpp | 92 +- source/graphics/ShaderProgram.h | 39 +- source/graphics/ShaderProgramFFP.cpp | 308 ++++++- source/graphics/ShaderTechnique.cpp | 23 +- source/graphics/ShaderTechnique.h | 23 +- source/graphics/tests/test_ShaderManager.h | 89 ++ source/ps/GameSetup/Config.cpp | 7 - source/ps/GameSetup/Config.h | 2 - source/ps/GameSetup/GameSetup.cpp | 7 - source/renderer/DecalRData.cpp | 5 +- .../renderer/FixedFunctionModelRenderer.cpp | 288 ------ source/renderer/FixedFunctionModelRenderer.h | 58 -- source/renderer/HWLightingModelRenderer.cpp | 133 ++- source/renderer/HWLightingModelRenderer.h | 22 +- source/renderer/InstancingModelRenderer.cpp | 19 +- source/renderer/InstancingModelRenderer.h | 13 +- source/renderer/ModelRenderer.cpp | 609 +++++++------ source/renderer/ModelRenderer.h | 55 +- source/renderer/ModelVertexRenderer.h | 37 +- source/renderer/OverlayRenderer.cpp | 8 +- source/renderer/ParticleRenderer.cpp | 6 +- source/renderer/ParticleRenderer.h | 2 +- source/renderer/PatchRData.cpp | 14 +- source/renderer/PlayerRenderer.cpp | 294 ------ source/renderer/PlayerRenderer.h | 100 --- source/renderer/RenderModifiers.cpp | 148 +--- source/renderer/RenderModifiers.h | 88 +- source/renderer/Renderer.cpp | 648 +++++--------- source/renderer/Renderer.h | 56 +- source/renderer/TerrainRenderer.cpp | 61 +- source/renderer/TerrainRenderer.h | 2 +- source/renderer/TransparencyRenderer.cpp | 838 ------------------ source/renderer/TransparencyRenderer.h | 172 ---- source/test_setup.cpp | 2 + 74 files changed, 2100 insertions(+), 3507 deletions(-) create mode 100644 binaries/data/mods/public/art/materials/default.xml create mode 100644 binaries/data/mods/public/shaders/effects/model.xml create mode 100644 binaries/data/mods/public/shaders/effects/model_alphablend.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_normal.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_normal_instancing.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_solid.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_solid_instancing.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_solid_player.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_solid_player_instancing.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_transparent.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_transparent_blend.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_transparent_opaque.xml delete mode 100644 binaries/data/mods/public/shaders/effects/model_transparent_shadow.xml create mode 100644 source/graphics/ShaderDefines.cpp create mode 100644 source/graphics/ShaderDefines.h create mode 100644 source/graphics/tests/test_ShaderManager.h delete mode 100644 source/renderer/FixedFunctionModelRenderer.cpp delete mode 100644 source/renderer/FixedFunctionModelRenderer.h delete mode 100644 source/renderer/PlayerRenderer.cpp delete mode 100644 source/renderer/PlayerRenderer.h delete mode 100644 source/renderer/TransparencyRenderer.cpp delete mode 100644 source/renderer/TransparencyRenderer.h 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;