Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_common.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_common.vp (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_common.vp (revision 25285) @@ -1,97 +1,98 @@ !!ARBvp1.0 PARAM cameraPos = program.local[0]; PARAM sunDir = program.local[1]; PARAM sunColor = program.local[2]; PARAM losTransform = program.local[3]; PARAM shadowTransform[4] = { program.local[4..7] }; #if USE_INSTANCING PARAM instancingTransform[4] = { program.local[8..11] }; #endif +PARAM transform[4] = { program.local[16..19] }; #if USE_FP_SHADOW && USE_SHADOW_PCF PARAM shadowScale = program.local[12]; #endif TEMP temp; TEMP lighting; OUTPUT v_tex = result.texcoord[0]; OUTPUT v_shadow = result.texcoord[1]; OUTPUT v_los = result.texcoord[2]; #if USE_SPECULAR OUTPUT v_normal = result.texcoord[3]; OUTPUT v_half = result.texcoord[4]; #endif - + //// Compute position and normal: #if USE_INSTANCING TEMP position; TEMP normal; DP4 position.x, instancingTransform[0], vertex.position; DP4 position.y, instancingTransform[1], vertex.position; DP4 position.z, instancingTransform[2], vertex.position; MOV position.w, 1.0; DP3 normal.x, instancingTransform[0], vertex.normal; DP3 normal.y, instancingTransform[1], vertex.normal; DP3 normal.z, instancingTransform[2], vertex.normal; #else ATTRIB position = vertex.position; ATTRIB normal = vertex.normal; #endif -DP4 result.position.x, state.matrix.mvp.row[0], position; -DP4 result.position.y, state.matrix.mvp.row[1], position; -DP4 result.position.z, state.matrix.mvp.row[2], position; -DP4 result.position.w, state.matrix.mvp.row[3], position; +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; //// Compute lighting: // Diffuse factor DP3 lighting, normal, -sunDir; MAX lighting, 0.0, lighting; // Scale diffuse to allow overbrightness (since result.color will be clamped to [0, 1]) MUL lighting, lighting, 0.5; // Apply light color MUL result.color, lighting, sunColor; #if USE_SPECULAR // eyeVec = normalize(cameraPos - position); TEMP eyeVec; SUB eyeVec.xyz, cameraPos, position; DP3 eyeVec.w, eyeVec, eyeVec; RSQ eyeVec.w, eyeVec.w; MUL eyeVec.xyz, eyeVec, eyeVec.w; // v_half = normalize(-sunDir + eyeVec); TEMP half; SUB half.xyz, eyeVec, sunDir; DP3 half.w, half, half; RSQ half.w, half.w; MUL v_half.xyz, half, half.w; MOV v_normal, normal; #endif //// Texture coordinates: MOV v_tex, vertex.texcoord[0]; #if USE_SHADOW #if USE_FP_SHADOW && USE_SHADOW_PCF TEMP shadowtc; DP4 shadowtc.x, shadowTransform[0], position; DP4 shadowtc.y, shadowTransform[1], position; MUL v_shadow.xy, shadowtc, shadowScale; #else DP4 v_shadow.x, shadowTransform[0], position; DP4 v_shadow.y, shadowTransform[1], position; #endif DP4 v_shadow.z, shadowTransform[2], position; DP4 v_shadow.w, shadowTransform[3], position; #endif MAD v_los, position.xzzz, losTransform.x, losTransform.y; END Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_common.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_common.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_common.xml (revision 25285) @@ -1,33 +1,34 @@ + Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.xml (revision 25285) @@ -1,11 +1,12 @@ + Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_player.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_player.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_player.xml (revision 25285) @@ -1,13 +1,14 @@ + Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_tex.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_tex.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_tex.xml (revision 25285) @@ -1,14 +1,15 @@ + Index: ps/trunk/binaries/data/mods/public/shaders/arb/overlay_solid.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/overlay_solid.vp (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/overlay_solid.vp (revision 25285) @@ -1,17 +1,18 @@ !!ARBvp1.0 PARAM transform[4] = { program.local[0..3] }; +PARAM instancingTransform[4] = { program.local[4..7] }; TEMP position; -DP4 position.x, transform[0], vertex.position; -DP4 position.y, transform[1], vertex.position; -DP4 position.z, transform[2], vertex.position; +DP4 position.x, instancingTransform[0], vertex.position; +DP4 position.y, instancingTransform[1], vertex.position; +DP4 position.z, instancingTransform[2], vertex.position; MOV position.w, 1.0; -DP4 result.position.x, state.matrix.mvp.row[0], position; -DP4 result.position.y, state.matrix.mvp.row[1], position; -DP4 result.position.z, state.matrix.mvp.row[2], position; -DP4 result.position.w, state.matrix.mvp.row[3], position; +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; END Index: ps/trunk/binaries/data/mods/public/shaders/arb/overlay_solid.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/overlay_solid.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/overlay_solid.xml (revision 25285) @@ -1,13 +1,14 @@ + Index: ps/trunk/binaries/data/mods/public/shaders/arb/particle.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/particle.vp (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/particle.vp (revision 25285) @@ -1,25 +1,29 @@ !!ARBvp1.0 ATTRIB uv = vertex.texcoord[0]; ATTRIB offset = vertex.texcoord[1]; -PARAM axis1 = state.matrix.modelview.row[0]; -PARAM axis2 = state.matrix.modelview.row[1]; -PARAM losTransform = program.local[0]; +PARAM transform[4] = { program.local[0..3] }; +PARAM modelViewMatrix[4] = { program.local[4..7] }; +PARAM losTransform = program.local[8]; +TEMP axis1; +MOV axis1, modelViewMatrix[0]; +TEMP axis2; +MOV axis2, modelViewMatrix[1]; TEMP position; MAD position.xyz, axis1, offset.x, vertex.position; MAD position.xyz, axis1, offset.y, position; MAD position.xyz, axis2, offset.x, position; MAD position.xyz, axis2, -offset.y, position; MOV position.w, vertex.position.w; -DP4 result.position.x, state.matrix.mvp.row[0], position; -DP4 result.position.y, state.matrix.mvp.row[1], position; -DP4 result.position.z, state.matrix.mvp.row[2], position; -DP4 result.position.w, state.matrix.mvp.row[3], position; +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; MOV result.texcoord[0], uv; MOV result.color, vertex.color; MAD result.texcoord[1], vertex.position.xzzz, losTransform.x, losTransform.y; END Index: ps/trunk/binaries/data/mods/public/shaders/arb/particle.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/particle.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/particle.xml (revision 25285) @@ -1,18 +1,20 @@ - + + + Index: ps/trunk/binaries/data/mods/public/shaders/arb/particle_solid.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/particle_solid.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/particle_solid.xml (revision 25285) @@ -1,12 +1,14 @@ + + Index: ps/trunk/binaries/data/mods/public/shaders/arb/solid.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/solid.vp (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/solid.vp (revision 25285) @@ -1,20 +1,22 @@ !!ARBvp1.0 #ifdef USE_INSTANCING PARAM instancingTransform[4] = { program.local[0..3] }; TEMP position; DP4 position.x, instancingTransform[0], vertex.position; DP4 position.y, instancingTransform[1], vertex.position; DP4 position.z, instancingTransform[2], vertex.position; MOV position.w, 1.0; #else ATTRIB position = vertex.position; #endif -DP4 result.position.x, state.matrix.mvp.row[0], position; -DP4 result.position.y, state.matrix.mvp.row[1], position; -DP4 result.position.z, state.matrix.mvp.row[2], position; -DP4 result.position.w, state.matrix.mvp.row[3], position; +PARAM transform[4] = { program.local[4..7] }; + +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; MOV result.color, vertex.color; END Index: ps/trunk/binaries/data/mods/public/shaders/arb/solid_tex.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/solid_tex.vp (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/solid_tex.vp (revision 25285) @@ -1,18 +1,20 @@ !!ARBvp1.0 #ifdef USE_INSTANCING PARAM instancingTransform[4] = { program.local[0..3] }; TEMP position; DP4 position.x, instancingTransform[0], vertex.position; DP4 position.y, instancingTransform[1], vertex.position; DP4 position.z, instancingTransform[2], vertex.position; MOV position.w, 1.0; #else ATTRIB position = vertex.position; #endif -DP4 result.position.x, state.matrix.mvp.row[0], position; -DP4 result.position.y, state.matrix.mvp.row[1], position; -DP4 result.position.z, state.matrix.mvp.row[2], position; -DP4 result.position.w, state.matrix.mvp.row[3], position; +PARAM transform[4] = { program.local[4..7] }; + +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; MOV result.texcoord[0], vertex.texcoord[0]; END Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_base.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_base.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_base.xml (revision 25285) @@ -1,27 +1,28 @@ + Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_blend.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_blend.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_blend.xml (revision 25285) @@ -1,29 +1,30 @@ + Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_common.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_common.vp (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_common.vp (revision 25285) @@ -1,68 +1,69 @@ !!ARBvp1.0 PARAM sunColor = program.local[0]; PARAM textureTransform = program.local[1]; PARAM losTransform = program.local[2]; PARAM shadowTransform[4] = { program.local[3..6] }; PARAM sunDir = program.local[8]; +PARAM transform[4] = { program.local[9..12] }; #if USE_FP_SHADOW && USE_SHADOW_PCF PARAM shadowScale = program.local[7]; #endif TEMP lighting; //// Compute position and normal: ATTRIB position = vertex.position; -DP4 result.position.x, state.matrix.mvp.row[0], position; -DP4 result.position.y, state.matrix.mvp.row[1], position; -DP4 result.position.z, state.matrix.mvp.row[2], position; -DP4 result.position.w, state.matrix.mvp.row[3], position; +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; //// Compute lighting: // Diffuse factor is precomputed in vertex attribute // Scale diffuse to allow overbrightness (since result.color will be clamped to [0, 1]) // DP3 lighting, -sunDir, vertex.normal; MAX lighting, 0.0, lighting; // DP3_SAT isn't available here. MUL lighting, lighting, 0.5; // Apply light color MUL result.color, lighting, sunColor; //// Texture coordinates: #if DECAL MOV result.texcoord[0], vertex.texcoord[0]; #else // Compute texcoords from position and terrain-texture-dependent transform. // textureTransform is stored as [c, -s, s, 0], // and we want texcoord = (x*c + z*-s, x*-s + z*-c, 0, 1) DP3 result.texcoord[0].x, textureTransform.xyww, position.xzww; DP3 result.texcoord[0].y, -textureTransform.zxww, position.xzww; MOV result.texcoord[0].z, 0; MOV result.texcoord[0].w, 1; #endif #if BLEND MOV result.texcoord[1], vertex.texcoord[1]; #endif #if USE_SHADOW #if USE_FP_SHADOW && USE_SHADOW_PCF TEMP shadowtc; DP4 shadowtc.x, shadowTransform[0], position; DP4 shadowtc.y, shadowTransform[1], position; MUL result.texcoord[2].xy, shadowtc, shadowScale; #else DP4 result.texcoord[2].x, shadowTransform[0], position; DP4 result.texcoord[2].y, shadowTransform[1], position; #endif DP4 result.texcoord[2].z, shadowTransform[2], position; DP4 result.texcoord[2].w, shadowTransform[3], position; #endif MAD result.texcoord[3], position.xzzz, losTransform.x, losTransform.y; END Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_decal.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_decal.xml (revision 25284) +++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_decal.xml (revision 25285) @@ -1,28 +1,29 @@ + Index: ps/trunk/source/renderer/OverlayRenderer.cpp =================================================================== --- ps/trunk/source/renderer/OverlayRenderer.cpp (revision 25284) +++ ps/trunk/source/renderer/OverlayRenderer.cpp (revision 25285) @@ -1,793 +1,794 @@ /* Copyright (C) 2021 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 "OverlayRenderer.h" #include "graphics/Camera.h" #include "graphics/LOSTexture.h" #include "graphics/Overlay.h" #include "graphics/Terrain.h" #include "graphics/TextureManager.h" #include "lib/hash.h" #include "lib/ogl.h" #include "maths/MathUtil.h" #include "maths/Quaternion.h" #include "ps/Game.h" #include "ps/Profile.h" #include "renderer/Renderer.h" #include "renderer/TexturedLineRData.h" #include "renderer/VertexArray.h" #include "renderer/VertexBuffer.h" #include "renderer/VertexBufferManager.h" #include "simulation2/components/ICmpWaterManager.h" #include "simulation2/Simulation2.h" #include "simulation2/system/SimContext.h" #include namespace { CShaderProgramPtr GetOverlayLineShader(const CShaderDefines& defines) { const char* shaderName; if (g_RenderingOptions.GetPreferGLSL()) shaderName = "glsl/overlayline"; else shaderName = "arb/overlayline"; return g_Renderer.GetShaderManager().LoadProgram(shaderName, defines); } } // anonymous namespace /** * Key used to group quads into batches for more efficient rendering. Currently groups by the combination * of the main texture and the texture mask, to minimize texture swapping during rendering. */ struct QuadBatchKey { QuadBatchKey (const CTexturePtr& texture, const CTexturePtr& textureMask) : m_Texture(texture), m_TextureMask(textureMask) { } bool operator==(const QuadBatchKey& other) const { return (m_Texture == other.m_Texture && m_TextureMask == other.m_TextureMask); } CTexturePtr m_Texture; CTexturePtr m_TextureMask; }; struct QuadBatchHash { std::size_t operator()(const QuadBatchKey& d) const { size_t seed = 0; hash_combine(seed, d.m_Texture); hash_combine(seed, d.m_TextureMask); return seed; } }; /** * Holds information about a single quad rendering batch. */ class QuadBatchData : public CRenderData { public: QuadBatchData() : m_IndicesBase(0), m_NumRenderQuads(0) { } /// Holds the quad overlay structures requested to be rendered in this batch. Must be cleared /// after each frame. std::vector m_Quads; /// Start index of this batch into the dedicated quad indices VertexArray (see OverlayInternals). size_t m_IndicesBase; /// Amount of quads to actually render in this batch. Potentially (although unlikely to be) /// different from m_Quads.size() due to restrictions on the total amount of quads that can be /// rendered. Must be reset after each frame. size_t m_NumRenderQuads; }; struct OverlayRendererInternals { using QuadBatchMap = std::unordered_map; OverlayRendererInternals(); ~OverlayRendererInternals(){ } std::vector lines; std::vector texlines; std::vector sprites; std::vector quads; std::vector spheres; QuadBatchMap quadBatchMap; // Dedicated vertex/index buffers for rendering all quads (to within the limits set by // MAX_QUAD_OVERLAYS). VertexArray quadVertices; VertexArray::Attribute quadAttributePos; VertexArray::Attribute quadAttributeColor; VertexArray::Attribute quadAttributeUV; VertexIndexArray quadIndices; /// Maximum amount of quad overlays we support for rendering. This limit is set to be able to /// render all quads from a single dedicated VB without having to reallocate it, which is much /// faster in the typical case of rendering only a handful of quads. When modifying this value, /// you must take care for the new amount of quads to fit in a single VBO (which is not likely /// to be a problem). static const size_t MAX_QUAD_OVERLAYS = 1024; // Sets of commonly-(re)used shader defines. CShaderDefines defsOverlayLineNormal; CShaderDefines defsOverlayLineAlwaysVisible; CShaderDefines defsQuadOverlay; // Geometry for a unit sphere std::vector sphereVertexes; std::vector sphereIndexes; void GenerateSphere(); /// Performs one-time setup. Called from CRenderer::Open, after graphics capabilities have /// been detected. Note that no VBOs must be created before this is called, since the shader /// path and graphics capabilities are not guaranteed to be stable before this point. void Initialize(); }; const float OverlayRenderer::OVERLAY_VOFFSET = 0.2f; OverlayRendererInternals::OverlayRendererInternals() : quadVertices(GL_DYNAMIC_DRAW), quadIndices(GL_STATIC_DRAW) { quadAttributePos.elems = 3; quadAttributePos.type = GL_FLOAT; quadVertices.AddAttribute(&quadAttributePos); quadAttributeColor.elems = 4; quadAttributeColor.type = GL_FLOAT; quadVertices.AddAttribute(&quadAttributeColor); quadAttributeUV.elems = 2; quadAttributeUV.type = GL_SHORT; // don't use GL_UNSIGNED_SHORT here, TexCoordPointer won't accept it quadVertices.AddAttribute(&quadAttributeUV); // Note that we're reusing the textured overlay line shader for the quad overlay rendering. This // is because their code is almost identical; the only difference is that for the quad overlays // we want to use a vertex color stream as opposed to an objectColor uniform. To this end, the // shader has been set up to switch between the two behaviours based on the USE_OBJECTCOLOR define. defsOverlayLineNormal.Add(str_USE_OBJECTCOLOR, str_1); defsOverlayLineAlwaysVisible.Add(str_USE_OBJECTCOLOR, str_1); defsOverlayLineAlwaysVisible.Add(str_IGNORE_LOS, str_1); } void OverlayRendererInternals::Initialize() { // Perform any initialization after graphics capabilities have been detected. Notably, // only at this point can we safely allocate VBOs (in contrast to e.g. in the constructor), // because their creation depends on the shader path, which is not reliably set before this point. quadVertices.SetNumVertices(MAX_QUAD_OVERLAYS * 4); quadVertices.Layout(); // allocate backing store quadIndices.SetNumVertices(MAX_QUAD_OVERLAYS * 6); quadIndices.Layout(); // allocate backing store // Since the quads in the vertex array are independent and always consist of exactly 4 vertices per quad, the // indices are always the same; we can therefore fill in all the indices once and pretty much forget about // them. We then also no longer need its backing store, since we never change any indices afterwards. VertexArrayIterator index = quadIndices.GetIterator(); for (u16 i = 0; i < static_cast(MAX_QUAD_OVERLAYS); ++i) { *index++ = i * 4 + 0; *index++ = i * 4 + 1; *index++ = i * 4 + 2; *index++ = i * 4 + 2; *index++ = i * 4 + 3; *index++ = i * 4 + 0; } quadIndices.Upload(); quadIndices.FreeBackingStore(); } OverlayRenderer::OverlayRenderer() { m = new OverlayRendererInternals(); } OverlayRenderer::~OverlayRenderer() { delete m; } void OverlayRenderer::Initialize() { m->Initialize(); } void OverlayRenderer::Submit(SOverlayLine* line) { ENSURE(line->m_Coords.size() % 3 == 0); m->lines.push_back(line); } void OverlayRenderer::Submit(SOverlayTexturedLine* line) { // Simplify the rest of the code by guaranteeing non-empty lines if (line->m_Coords.empty()) return; m->texlines.push_back(line); } void OverlayRenderer::Submit(SOverlaySprite* overlay) { m->sprites.push_back(overlay); } void OverlayRenderer::Submit(SOverlayQuad* overlay) { m->quads.push_back(overlay); } void OverlayRenderer::Submit(SOverlaySphere* overlay) { m->spheres.push_back(overlay); } void OverlayRenderer::EndFrame() { m->lines.clear(); m->texlines.clear(); m->sprites.clear(); m->quads.clear(); m->spheres.clear(); // this should leave the capacity unchanged, which is okay since it // won't be very large or very variable // Empty the batch rendering data structures, but keep their key mappings around for the next frames for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); ++it) { QuadBatchData& quadBatchData = (it->second); quadBatchData.m_Quads.clear(); quadBatchData.m_NumRenderQuads = 0; quadBatchData.m_IndicesBase = 0; } } void OverlayRenderer::PrepareForRendering() { PROFILE3("prepare overlays"); // This is where we should do something like sort the overlays by // color/sprite/etc for more efficient rendering for (size_t i = 0; i < m->texlines.size(); ++i) { SOverlayTexturedLine* line = m->texlines[i]; if (!line->m_RenderData) { line->m_RenderData = std::make_shared(); line->m_RenderData->Update(*line); // We assume the overlay line will get replaced by the caller // if terrain changes, so we don't need to detect that here and // call Update again. Also we assume the caller won't change // any of the parameters after first submitting the line. } } // Group quad overlays by their texture/mask combination for efficient rendering // TODO: consider doing this directly in Submit() for (size_t i = 0; i < m->quads.size(); ++i) { SOverlayQuad* const quad = m->quads[i]; QuadBatchKey textures(quad->m_Texture, quad->m_TextureMask); QuadBatchData& batchRenderData = m->quadBatchMap[textures]; // will create entry if it doesn't already exist // add overlay to list of quads batchRenderData.m_Quads.push_back(quad); } const CVector3D vOffset(0, OverlayRenderer::OVERLAY_VOFFSET, 0); // Write quad overlay vertices/indices to VA backing store VertexArrayIterator vertexPos = m->quadAttributePos.GetIterator(); VertexArrayIterator vertexColor = m->quadAttributeColor.GetIterator(); VertexArrayIterator vertexUV = m->quadAttributeUV.GetIterator(); size_t indicesIdx = 0; size_t totalNumQuads = 0; for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); ++it) { QuadBatchData& batchRenderData = (it->second); batchRenderData.m_NumRenderQuads = 0; if (batchRenderData.m_Quads.empty()) continue; // Remember the current index into the (entire) indices array as our base offset for this batch batchRenderData.m_IndicesBase = indicesIdx; // points to the index where each iteration's vertices will be appended for (size_t i = 0; i < batchRenderData.m_Quads.size() && totalNumQuads < OverlayRendererInternals::MAX_QUAD_OVERLAYS; i++) { const SOverlayQuad* quad = batchRenderData.m_Quads[i]; // TODO: this is kind of ugly, the iterator should use a type that can have quad->m_Color assigned // to it directly const CVector4D quadColor(quad->m_Color.r, quad->m_Color.g, quad->m_Color.b, quad->m_Color.a); *vertexPos++ = quad->m_Corners[0] + vOffset; *vertexPos++ = quad->m_Corners[1] + vOffset; *vertexPos++ = quad->m_Corners[2] + vOffset; *vertexPos++ = quad->m_Corners[3] + vOffset; (*vertexUV)[0] = 0; (*vertexUV)[1] = 0; ++vertexUV; (*vertexUV)[0] = 0; (*vertexUV)[1] = 1; ++vertexUV; (*vertexUV)[0] = 1; (*vertexUV)[1] = 1; ++vertexUV; (*vertexUV)[0] = 1; (*vertexUV)[1] = 0; ++vertexUV; *vertexColor++ = quadColor; *vertexColor++ = quadColor; *vertexColor++ = quadColor; *vertexColor++ = quadColor; indicesIdx += 6; totalNumQuads++; batchRenderData.m_NumRenderQuads++; } } m->quadVertices.Upload(); // don't free the backing store! we'll overwrite it on the next frame to save a reallocation. m->quadVertices.PrepareForRendering(); } void OverlayRenderer::RenderOverlaysBeforeWater() { PROFILE3_GPU("overlays (before)"); #if CONFIG2_GLES #warning TODO: implement OverlayRenderer::RenderOverlaysBeforeWater for GLES #else if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); pglActiveTextureARB(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); // Ignore z so that we draw behind terrain (but don't disable GL_DEPTH_TEST // since we still want to write to the z buffer) glDepthFunc(GL_ALWAYS); for (size_t i = 0; i < m->lines.size(); ++i) { SOverlayLine* line = m->lines[i]; if (line->m_Coords.empty()) continue; ENSURE(line->m_Coords.size() % 3 == 0); glColor4fv(line->m_Color.FloatArray()); glLineWidth((float)line->m_Thickness); glInterleavedArrays(GL_V3F, sizeof(float)*3, &line->m_Coords[0]); glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)line->m_Coords.size()/3); } glDisableClientState(GL_VERTEX_ARRAY); glLineWidth(1.f); glDepthFunc(GL_LEQUAL); glDisable(GL_BLEND); if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif } void OverlayRenderer::RenderOverlaysAfterWater() { PROFILE3_GPU("overlays (after)"); RenderTexturedOverlayLines(); RenderQuadOverlays(); RenderSphereOverlays(); } void OverlayRenderer::RenderTexturedOverlayLines() { #if CONFIG2_GLES #warning TODO: implement OverlayRenderer::RenderTexturedOverlayLines for GLES return; #endif if (m->texlines.empty()) return; ogl_WarnIfError(); pglActiveTextureARB(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glDepthMask(0); CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); CShaderProgramPtr shaderTexLineNormal = GetOverlayLineShader(m->defsOverlayLineNormal); CShaderProgramPtr shaderTexLineAlwaysVisible = GetOverlayLineShader(m->defsOverlayLineAlwaysVisible); // ---------------------------------------------------------------------------------------- if (shaderTexLineNormal) { shaderTexLineNormal->Bind(); shaderTexLineNormal->BindTexture(str_losTex, los.GetTexture()); shaderTexLineNormal->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); shaderTexLineNormal->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); // batch render only the non-always-visible overlay lines using the normal shader RenderTexturedOverlayLines(shaderTexLineNormal, false); shaderTexLineNormal->Unbind(); } // ---------------------------------------------------------------------------------------- if (shaderTexLineAlwaysVisible) { shaderTexLineAlwaysVisible->Bind(); // TODO: losTex and losTransform are unused in the always visible shader; see if these can be safely omitted shaderTexLineAlwaysVisible->BindTexture(str_losTex, los.GetTexture()); shaderTexLineAlwaysVisible->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); shaderTexLineAlwaysVisible->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); // batch render only the always-visible overlay lines using the LoS-ignored shader RenderTexturedOverlayLines(shaderTexLineAlwaysVisible, true); shaderTexLineAlwaysVisible->Unbind(); } // ---------------------------------------------------------------------------------------- // TODO: the shaders should probably be responsible for unbinding their textures g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(0, 0); CVertexBuffer::Unbind(); glDepthMask(1); glDisable(GL_BLEND); } void OverlayRenderer::RenderTexturedOverlayLines(CShaderProgramPtr shader, bool alwaysVisible) { #if !CONFIG2_GLES if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); #endif for (size_t i = 0; i < m->texlines.size(); ++i) { SOverlayTexturedLine* line = m->texlines[i]; // render only those lines matching the requested alwaysVisible status if (!line->m_RenderData || line->m_AlwaysVisible != alwaysVisible) continue; ENSURE(line->m_RenderData); line->m_RenderData->Render(*line, shader); } #if !CONFIG2_GLES if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif } void OverlayRenderer::RenderQuadOverlays() { #if CONFIG2_GLES #warning TODO: implement OverlayRenderer::RenderQuadOverlays for GLES return; #endif if (m->quadBatchMap.empty()) return; CShaderProgramPtr shader = GetOverlayLineShader(m->defsQuadOverlay); if (!shader) return; #if !CONFIG2_GLES if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); #endif pglActiveTextureARB(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glDepthMask(0); CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); shader->Bind(); shader->BindTexture(str_losTex, los.GetTexture()); shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); // Base offsets (in bytes) of the two backing stores relative to their owner VBO u8* indexBase = m->quadIndices.Bind(); u8* vertexBase = m->quadVertices.Bind(); GLsizei indexStride = m->quadIndices.GetStride(); GLsizei vertexStride = m->quadVertices.GetStride(); for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); ++it) { QuadBatchData& batchRenderData = it->second; const size_t batchNumQuads = batchRenderData.m_NumRenderQuads; // Careful; some drivers don't like drawing calls with 0 stuff to draw. if (batchNumQuads == 0) continue; const QuadBatchKey& maskPair = it->first; shader->BindTexture(str_baseTex, maskPair.m_Texture->GetHandle()); shader->BindTexture(str_maskTex, maskPair.m_TextureMask->GetHandle()); int streamflags = shader->GetStreamFlags(); if (streamflags & STREAM_POS) shader->VertexPointer(m->quadAttributePos.elems, m->quadAttributePos.type, vertexStride, vertexBase + m->quadAttributePos.offset); if (streamflags & STREAM_UV0) shader->TexCoordPointer(GL_TEXTURE0, m->quadAttributeUV.elems, m->quadAttributeUV.type, vertexStride, vertexBase + m->quadAttributeUV.offset); if (streamflags & STREAM_UV1) shader->TexCoordPointer(GL_TEXTURE1, m->quadAttributeUV.elems, m->quadAttributeUV.type, vertexStride, vertexBase + m->quadAttributeUV.offset); if (streamflags & STREAM_COLOR) shader->ColorPointer(m->quadAttributeColor.elems, m->quadAttributeColor.type, vertexStride, vertexBase + m->quadAttributeColor.offset); shader->AssertPointersBound(); glDrawElements(GL_TRIANGLES, (GLsizei)(batchNumQuads * 6), GL_UNSIGNED_SHORT, indexBase + indexStride * batchRenderData.m_IndicesBase); g_Renderer.GetStats().m_DrawCalls++; g_Renderer.GetStats().m_OverlayTris += batchNumQuads*2; } shader->Unbind(); // TODO: the shader should probably be responsible for unbinding its textures g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(0, 0); CVertexBuffer::Unbind(); glDepthMask(1); glDisable(GL_BLEND); #if !CONFIG2_GLES if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif } void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera) { PROFILE3_GPU("overlays (fg)"); #if CONFIG2_GLES #warning TODO: implement OverlayRenderer::RenderForegroundOverlays for GLES #else if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); pglActiveTextureARB(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); CVector3D right = -viewCamera.GetOrientation().GetLeft(); CVector3D up = viewCamera.GetOrientation().GetUp(); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_foreground_overlay); tech->BeginPass(); CShaderProgramPtr shader = tech->GetShader(); shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); float uvs[8] = { 0,1, 1,1, 1,0, 0,0 }; shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, sizeof(float)*2, &uvs[0]); for (size_t i = 0; i < m->sprites.size(); ++i) { SOverlaySprite* sprite = m->sprites[i]; if (!i || sprite->m_Texture != m->sprites[i - 1]->m_Texture) shader->BindTexture(str_baseTex, sprite->m_Texture); shader->Uniform(str_colorMul, sprite->m_Color); CVector3D pos[4] = { sprite->m_Position + right*sprite->m_X0 + up*sprite->m_Y0, sprite->m_Position + right*sprite->m_X1 + up*sprite->m_Y0, sprite->m_Position + right*sprite->m_X1 + up*sprite->m_Y1, sprite->m_Position + right*sprite->m_X0 + up*sprite->m_Y1 }; shader->VertexPointer(3, GL_FLOAT, sizeof(float)*3, &pos[0].X); glDrawArrays(GL_QUADS, 0, (GLsizei)4); g_Renderer.GetStats().m_DrawCalls++; g_Renderer.GetStats().m_OverlayTris += 2; } tech->EndPass(); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); if (g_Renderer.GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif } static void TessellateSphereFace(const CVector3D& a, u16 ai, const CVector3D& b, u16 bi, const CVector3D& c, u16 ci, std::vector& vertexes, std::vector& indexes, int level) { if (level == 0) { indexes.push_back(ai); indexes.push_back(bi); indexes.push_back(ci); } else { CVector3D d = (a + b).Normalized(); CVector3D e = (b + c).Normalized(); CVector3D f = (c + a).Normalized(); int di = vertexes.size() / 3; vertexes.push_back(d.X); vertexes.push_back(d.Y); vertexes.push_back(d.Z); int ei = vertexes.size() / 3; vertexes.push_back(e.X); vertexes.push_back(e.Y); vertexes.push_back(e.Z); int fi = vertexes.size() / 3; vertexes.push_back(f.X); vertexes.push_back(f.Y); vertexes.push_back(f.Z); TessellateSphereFace(a,ai, d,di, f,fi, vertexes, indexes, level-1); TessellateSphereFace(d,di, b,bi, e,ei, vertexes, indexes, level-1); TessellateSphereFace(f,fi, e,ei, c,ci, vertexes, indexes, level-1); TessellateSphereFace(d,di, e,ei, f,fi, vertexes, indexes, level-1); } } static void TessellateSphere(std::vector& vertexes, std::vector& indexes, int level) { /* Start with a tetrahedron, then tessellate */ float s = sqrtf(0.5f); #define VERT(a,b,c) vertexes.push_back(a); vertexes.push_back(b); vertexes.push_back(c); VERT(-s, 0, -s); VERT( s, 0, -s); VERT( s, 0, s); VERT(-s, 0, s); VERT( 0, -1, 0); VERT( 0, 1, 0); #define FACE(a,b,c) \ TessellateSphereFace( \ CVector3D(vertexes[a*3], vertexes[a*3+1], vertexes[a*3+2]), a, \ CVector3D(vertexes[b*3], vertexes[b*3+1], vertexes[b*3+2]), b, \ CVector3D(vertexes[c*3], vertexes[c*3+1], vertexes[c*3+2]), c, \ vertexes, indexes, level); FACE(0,4,1); FACE(1,4,2); FACE(2,4,3); FACE(3,4,0); FACE(1,5,0); FACE(2,5,1); FACE(3,5,2); FACE(0,5,3); #undef FACE #undef VERT } void OverlayRendererInternals::GenerateSphere() { if (sphereVertexes.empty()) TessellateSphere(sphereVertexes, sphereIndexes, 3); } void OverlayRenderer::RenderSphereOverlays() { PROFILE3_GPU("overlays (spheres)"); #if CONFIG2_GLES #warning TODO: implement OverlayRenderer::RenderSphereOverlays for GLES #else if (m->spheres.empty()) return; glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glDepthMask(0); glEnableClientState(GL_VERTEX_ARRAY); CShaderProgramPtr shader; CShaderTechniquePtr tech; tech = g_Renderer.GetShaderManager().LoadEffect(str_overlay_solid); tech->BeginPass(); shader = tech->GetShader(); m->GenerateSphere(); shader->VertexPointer(3, GL_FLOAT, 0, &m->sphereVertexes[0]); for (size_t i = 0; i < m->spheres.size(); ++i) { SOverlaySphere* sphere = m->spheres[i]; CMatrix3D transform; transform.SetIdentity(); transform.Scale(sphere->m_Radius, sphere->m_Radius, sphere->m_Radius); transform.Translate(sphere->m_Center); - shader->Uniform(str_transform, transform); + shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); + shader->Uniform(str_instancingTransform, transform); shader->Uniform(str_color, sphere->m_Color); glDrawElements(GL_TRIANGLES, m->sphereIndexes.size(), GL_UNSIGNED_SHORT, &m->sphereIndexes[0]); g_Renderer.GetStats().m_DrawCalls++; g_Renderer.GetStats().m_OverlayTris = m->sphereIndexes.size()/3; } tech->EndPass(); glDisableClientState(GL_VERTEX_ARRAY); glDepthMask(1); glDisable(GL_BLEND); #endif }