diff --git a/source/graphics/Patch.cpp b/source/graphics/Patch.cpp index 1f6f19da88..38fbf90aee 100644 --- a/source/graphics/Patch.cpp +++ b/source/graphics/Patch.cpp @@ -68,4 +68,23 @@ void CPatch::CalcBounds() m_Bounds+=pos; } } + + // If this a side patch, the sides go down to height 0, so add them + // into the bounds + if (GetSideFlags()) + m_Bounds[0].Y = std::min(m_Bounds[0].Y, 0.f); +} + +int CPatch::GetSideFlags() +{ + int flags = 0; + if (m_X == 0) + flags |= CPATCH_SIDE_NEGX; + if (m_Z == 0) + flags |= CPATCH_SIDE_NEGZ; + if (m_X == m_Parent->GetPatchesPerSide()-1) + flags |= CPATCH_SIDE_POSX; + if (m_Z == m_Parent->GetPatchesPerSide()-1) + flags |= CPATCH_SIDE_POSZ; + return flags; } diff --git a/source/graphics/Patch.h b/source/graphics/Patch.h index e82b3c2c31..7c5cce07ac 100644 --- a/source/graphics/Patch.h +++ b/source/graphics/Patch.h @@ -33,17 +33,14 @@ class CTerrain; // PATCH_SIZE: number of tiles in each patch const ssize_t PATCH_SIZE = 16; -/// neighbor IDs for CPatch -enum CPatchNeighbors +// Flags for whether the patch should be drawn with a solid plane +// on each side +enum CPatchSideFlags { - CPATCH_NEIGHTBOR_LEFT_TOP = 0, - CPATCH_NEIGHTBOR_TOP = 1, - CPATCH_NEIGHTBOR_RIGHT_TOP = 2, - CPATCH_NEIGHTBOR_LEFT = 3, - CPATCH_NEIGHTBOR_RIGHT = 4, - CPATCH_NEIGHTBOR_LEFT_BOTTOM = 5, - CPATCH_NEIGHTBOR_BOTTOM = 6, - CPATCH_NEIGHTBOR_RIGHT_BOTTOM = 7 + CPATCH_SIDE_NEGX = (1 << 0), + CPATCH_SIDE_POSX = (1 << 1), + CPATCH_SIDE_NEGZ = (1 << 2), + CPATCH_SIDE_POSZ = (1 << 3), }; /////////////////////////////////////////////////////////////////////////////// @@ -75,6 +72,8 @@ public: // draw state... void setDrawState(bool value) { m_bWillBeDrawn = value; }; bool getDrawState() { return m_bWillBeDrawn; }; + + int GetSideFlags(); }; diff --git a/source/graphics/Terrain.cpp b/source/graphics/Terrain.cpp index 092042167a..16cda0c2f8 100644 --- a/source/graphics/Terrain.cpp +++ b/source/graphics/Terrain.cpp @@ -25,7 +25,6 @@ #include "lib/sysdep/cpu.h" #include "renderer/Renderer.h" -#include "renderer/WaterManager.h" #include "TerrainProperties.h" #include "TerrainTextureEntry.h" diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index d2c35548f6..a48415438f 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -35,8 +35,9 @@ #include "renderer/AlphaMapCalculator.h" #include "renderer/PatchRData.h" #include "renderer/Renderer.h" +#include "renderer/WaterManager.h" #include "simulation2/Simulation2.h" -#include "simulation2/components/ICmpRangeManager.h" +#include "simulation2/components/ICmpWaterManager.h" const ssize_t BlendOffsets[9][2] = { { 0, -1 }, @@ -52,7 +53,8 @@ const ssize_t BlendOffsets[9][2] = { /////////////////////////////////////////////////////////////////// // CPatchRData constructor -CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0) +CPatchRData::CPatchRData(CPatch* patch) : + m_Patch(patch), m_VBBase(0), m_VBSides(0), m_VBBlends(0), m_Vertices(0) { debug_assert(patch); Build(); @@ -66,6 +68,7 @@ CPatchRData::~CPatchRData() delete[] m_Vertices; // release vertex buffer chunks if (m_VBBase) g_VBMan.Release(m_VBBase); + if (m_VBSides) g_VBMan.Release(m_VBSides); if (m_VBBlends) g_VBMan.Release(m_VBBlends); } @@ -427,8 +430,6 @@ void CPatchRData::BuildVertices() { // create both vertices and lighting colors - CVector3D normal; - // number of vertices in each direction in each patch ssize_t vsize=PATCH_SIZE+1; @@ -459,6 +460,7 @@ void CPatchRData::BuildVertices() // Calculate diffuse lighting for this vertex // Ambient is added by the lighting pass (since ambient is the same // for all vertices, it need not be stored in the vertex structure) + CVector3D normal; terrain->CalcNormal(ix,iz,normal); RGBColor diffuse; @@ -474,9 +476,89 @@ void CPatchRData::BuildVertices() m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices); } +void CPatchRData::BuildSide(std::vector& vertices, CPatchSideFlags side) +{ + ssize_t vsize = PATCH_SIZE + 1; + CTerrain* terrain = m_Patch->m_Parent; + CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); + + for (ssize_t k = 0; k < vsize; k++) + { + ssize_t gx = m_Patch->m_X * PATCH_SIZE; + ssize_t gz = m_Patch->m_Z * PATCH_SIZE; + switch (side) + { + case CPATCH_SIDE_NEGX: gz += k; break; + case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break; + case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break; + case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break; + } + + CVector3D pos; + terrain->CalcPosition(gx, gz, pos); + + // Clamp the height to the water level + float waterHeight = 0.f; + if (!cmpWaterManager.null()) + waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z); + pos.Y = std::max(pos.Y, waterHeight); + + SSideVertex v0, v1; + v0.m_Position = pos; + v1.m_Position = pos; + v1.m_Position.Y = 0; + + // If this is the start of this tristrip, but we've already got a partial + // tristrip, and a couple of degenerate triangles to join the strips properly + if (k == 0 && !vertices.empty()) + { + vertices.push_back(vertices.back()); + vertices.push_back(v1); + } + + // Now add the new triangles + vertices.push_back(v1); + vertices.push_back(v0); + } +} + +void CPatchRData::BuildSides() +{ + std::vector sideVertices; + + int sideFlags = m_Patch->GetSideFlags(); + + // If no sides are enabled, we don't need to do anything + if (!sideFlags) + return; + + // For each side, generate a tristrip by adding a vertex at ground/water + // level and a vertex underneath at height 0. + + if (sideFlags & CPATCH_SIDE_NEGX) + BuildSide(sideVertices, CPATCH_SIDE_NEGX); + + if (sideFlags & CPATCH_SIDE_POSX) + BuildSide(sideVertices, CPATCH_SIDE_POSX); + + if (sideFlags & CPATCH_SIDE_NEGZ) + BuildSide(sideVertices, CPATCH_SIDE_NEGZ); + + if (sideFlags & CPATCH_SIDE_POSZ) + BuildSide(sideVertices, CPATCH_SIDE_POSZ); + + if (sideVertices.empty()) + return; + + if (!m_VBSides) + m_VBSides = g_VBMan.Allocate(sizeof(SSideVertex), sideVertices.size(), true); + m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides, &sideVertices[0]); +} + void CPatchRData::Build() { BuildVertices(); + BuildSides(); BuildIndices(); BuildBlends(); } @@ -488,6 +570,7 @@ void CPatchRData::Update() // than everything; it's complicated slightly because the blends are dependent // on both vertex and index data BuildVertices(); + BuildSides(); BuildIndices(); BuildBlends(); @@ -659,6 +742,29 @@ void CPatchRData::RenderOutline() glEnd(); } +void CPatchRData::RenderSides() +{ + debug_assert(m_UpdateFlags==0); + + if (!m_VBSides) + return; + + SSideVertex *base = (SSideVertex *)m_VBSides->m_Owner->Bind(); + + // setup data pointers + GLsizei stride = sizeof(SSideVertex); + glVertexPointer(3, GL_FLOAT, stride, &base->m_Position); + + if (!g_Renderer.m_SkipSubmit) + glDrawArrays(GL_TRIANGLE_STRIP, m_VBSides->m_Index, (GLsizei)m_VBSides->m_Count); + + // bump stats + g_Renderer.m_Stats.m_DrawCalls++; + g_Renderer.m_Stats.m_TerrainTris += m_VBSides->m_Count - 2; + + CVertexBuffer::Unbind(); +} + void CPatchRData::RenderPriorities() { CTerrain* terrain = m_Patch->m_Parent; diff --git a/source/renderer/PatchRData.h b/source/renderer/PatchRData.h index e822deb709..f23a73771c 100644 --- a/source/renderer/PatchRData.h +++ b/source/renderer/PatchRData.h @@ -41,6 +41,7 @@ public: void RenderBlends(); void RenderOutline(); void RenderStreams(int streamflags); + void RenderSides(); void RenderPriorities(); private: @@ -64,6 +65,11 @@ private: float m_UVs[2]; }; + struct SSideVertex { + // vertex position + CVector3D m_Position; + }; + struct SBlendVertex { // vertex position CVector3D m_Position; @@ -81,6 +87,9 @@ private: void BuildBlends(); void BuildIndices(); void BuildVertices(); + void BuildSides(); + + void BuildSide(std::vector& vertices, CPatchSideFlags side); // owner patch CPatch* m_Patch; @@ -88,6 +97,9 @@ private: // vertex buffer handle for base vertices CVertexBuffer::VBChunk* m_VBBase; + // vertex buffer handle for side vertices + CVertexBuffer::VBChunk* m_VBSides; + // vertex buffer handle for blend vertices CVertexBuffer::VBChunk* m_VBBlends; diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index 6cc87de701..5d15ecd37b 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -155,8 +155,17 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) { debug_assert(m->phase == Phase_Render); - // switch on required client states + // render the solid black sides of the map first + g_Renderer.BindTexture(0, 0); glEnableClientState(GL_VERTEX_ARRAY); + glColor3f(0, 0, 0); + for(size_t i = 0; i < m->visiblePatches.size(); ++i) + { + CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); + patchdata->RenderSides(); + } + + // switch on required client states glEnableClientState(GL_TEXTURE_COORD_ARRAY); // render everything fullbright diff --git a/source/simulation2/components/CCmpWaterManager.cpp b/source/simulation2/components/CCmpWaterManager.cpp index 32a4c6e9e4..9cc1ffce3e 100644 --- a/source/simulation2/components/CCmpWaterManager.cpp +++ b/source/simulation2/components/CCmpWaterManager.cpp @@ -20,6 +20,8 @@ #include "simulation2/system/Component.h" #include "ICmpWaterManager.h" +#include "graphics/RenderableObject.h" +#include "graphics/Terrain.h" #include "renderer/Renderer.h" #include "renderer/WaterManager.h" @@ -80,6 +82,9 @@ public: virtual void SetWaterLevel(entity_pos_t h) { m_WaterHeight = h; + + // Tell the terrain it'll need to recompute its cached render data + GetSimContext().GetTerrain().MakeDirty(RENDERDATA_UPDATE_VERTICES); } virtual entity_pos_t GetWaterLevel(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z))