Merge 'remotes/trunk'
[0ad.git] / source / renderer / PatchRData.cpp
blob0f18b72b39c0f62eda51c15092e9e404595e44d8
1 /* Copyright (C) 2013 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include <set>
21 #include <algorithm>
22 #include <numeric>
24 #include "graphics/GameView.h"
25 #include "graphics/LightEnv.h"
26 #include "graphics/LOSTexture.h"
27 #include "graphics/Patch.h"
28 #include "graphics/ShaderManager.h"
29 #include "graphics/Terrain.h"
30 #include "graphics/TextRenderer.h"
31 #include "lib/alignment.h"
32 #include "lib/allocators/arena.h"
33 #include "maths/MathUtil.h"
34 #include "ps/CLogger.h"
35 #include "ps/Game.h"
36 #include "ps/Profile.h"
37 #include "ps/Pyrogenesis.h"
38 #include "ps/World.h"
39 #include "ps/GameSetup/Config.h"
40 #include "renderer/AlphaMapCalculator.h"
41 #include "renderer/PatchRData.h"
42 #include "renderer/TerrainRenderer.h"
43 #include "renderer/Renderer.h"
44 #include "renderer/WaterManager.h"
45 #include "simulation2/Simulation2.h"
46 #include "simulation2/components/ICmpWaterManager.h"
48 const ssize_t BlendOffsets[9][2] = {
49 { 0, -1 },
50 { -1, -1 },
51 { -1, 0 },
52 { -1, 1 },
53 { 0, 1 },
54 { 1, 1 },
55 { 1, 0 },
56 { 1, -1 },
57 { 0, 0 }
60 ///////////////////////////////////////////////////////////////////
61 // CPatchRData constructor
62 CPatchRData::CPatchRData(CPatch* patch, CSimulation2* simulation) :
63 m_Patch(patch), m_VBSides(0),
64 m_VBBase(0), m_VBBaseIndices(0),
65 m_VBBlends(0), m_VBBlendIndices(0),
66 m_VBWater(0), m_VBWaterIndices(0),
67 m_VBWaterShore(0), m_VBWaterIndicesShore(0),
68 m_Simulation(simulation)
70 ENSURE(patch);
71 Build();
74 ///////////////////////////////////////////////////////////////////
75 // CPatchRData destructor
76 CPatchRData::~CPatchRData()
78 // release vertex buffer chunks
79 if (m_VBSides) g_VBMan.Release(m_VBSides);
80 if (m_VBBase) g_VBMan.Release(m_VBBase);
81 if (m_VBBaseIndices) g_VBMan.Release(m_VBBaseIndices);
82 if (m_VBBlends) g_VBMan.Release(m_VBBlends);
83 if (m_VBBlendIndices) g_VBMan.Release(m_VBBlendIndices);
84 if (m_VBWater) g_VBMan.Release(m_VBWater);
85 if (m_VBWaterIndices) g_VBMan.Release(m_VBWaterIndices);
86 if (m_VBWaterShore) g_VBMan.Release(m_VBWaterShore);
87 if (m_VBWaterIndicesShore) g_VBMan.Release(m_VBWaterIndicesShore);
90 /**
91 * Represents a blend for a single tile, texture and shape.
93 struct STileBlend
95 CTerrainTextureEntry* m_Texture;
96 int m_Priority;
97 u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
99 struct DecreasingPriority
101 bool operator()(const STileBlend& a, const STileBlend& b) const
103 if (a.m_Priority > b.m_Priority)
104 return true;
105 if (a.m_Priority < b.m_Priority)
106 return false;
107 if (a.m_Texture && b.m_Texture)
108 return a.m_Texture->GetTag() > b.m_Texture->GetTag();
109 return false;
113 struct CurrentTile
115 bool operator()(const STileBlend& a) const
117 return (a.m_TileMask & (1 << 8)) != 0;
123 * Represents the ordered collection of blends drawn on a particular tile.
125 struct STileBlendStack
127 u8 i, j;
128 std::vector<STileBlend> blends; // back of vector is lowest-priority texture
132 * Represents a batched collection of blends using the same texture.
134 struct SBlendLayer
136 struct Tile
138 u8 i, j;
139 u8 shape;
142 CTerrainTextureEntry* m_Texture;
143 std::vector<Tile> m_Tiles;
146 void CPatchRData::BuildBlends()
148 PROFILE3("build blends");
150 m_BlendSplats.clear();
152 std::vector<SBlendVertex> blendVertices;
153 std::vector<u16> blendIndices;
155 CTerrain* terrain = m_Patch->m_Parent;
157 std::vector<STileBlendStack> blendStacks;
158 blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
160 // For each tile in patch ..
161 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
163 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
165 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
166 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
168 std::vector<STileBlend> blends;
169 blends.reserve(9);
171 // Compute a blend for every tile in the 3x3 square around this tile
172 for (size_t n = 0; n < 9; ++n)
174 ssize_t ox = gx + BlendOffsets[n][1];
175 ssize_t oz = gz + BlendOffsets[n][0];
177 CMiniPatch* nmp = terrain->GetTile(ox, oz);
178 if (!nmp)
179 continue;
181 STileBlend blend;
182 blend.m_Texture = nmp->GetTextureEntry();
183 blend.m_Priority = nmp->GetPriority();
184 blend.m_TileMask = 1 << n;
185 blends.push_back(blend);
188 // Sort the blends, highest priority first
189 std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
191 STileBlendStack blendStack;
192 blendStack.i = i;
193 blendStack.j = j;
195 // Put the blends into the tile's stack, merging any adjacent blends with the same texture
196 for (size_t k = 0; k < blends.size(); ++k)
198 if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
199 blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
200 else
201 blendStack.blends.push_back(blends[k]);
204 // Remove blends that are after (i.e. lower priority than) the current tile
205 // (including the current tile), since we don't want to render them on top of
206 // the tile's base texture
207 blendStack.blends.erase(
208 std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
209 blendStack.blends.end());
211 blendStacks.push_back(blendStack);
215 // Given the blend stack per tile, we want to batch together as many blends as possible.
216 // Group them into a series of layers (each of which has a single texture):
217 // (This is effectively a topological sort / linearisation of the partial order induced
218 // by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
220 std::vector<SBlendLayer> blendLayers;
222 while (true)
224 if (!blendLayers.empty())
226 // Try to grab as many tiles as possible that match our current layer,
227 // from off the blend stacks of all the tiles
229 CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
231 for (size_t k = 0; k < blendStacks.size(); ++k)
233 if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
235 SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, (u8)blendStacks[k].blends.back().m_TileMask };
236 blendLayers.back().m_Tiles.push_back(t);
237 blendStacks[k].blends.pop_back();
239 // (We've already merged adjacent entries of the same texture in each stack,
240 // so we don't need to bother looping to check the next entry in this stack again)
244 // We've grabbed as many tiles as possible; now we need to start a new layer.
245 // The new layer's texture could come from the back of any non-empty stack;
246 // choose the longest stack as a heuristic to reduce the number of layers
247 CTerrainTextureEntry* bestTex = NULL;
248 size_t bestStackSize = 0;
250 for (size_t k = 0; k < blendStacks.size(); ++k)
252 if (blendStacks[k].blends.size() > bestStackSize)
254 bestStackSize = blendStacks[k].blends.size();
255 bestTex = blendStacks[k].blends.back().m_Texture;
259 // If all our stacks were empty, we're done
260 if (bestStackSize == 0)
261 break;
263 // Otherwise add the new layer, then loop back and start filling it in
265 SBlendLayer layer;
266 layer.m_Texture = bestTex;
267 blendLayers.push_back(layer);
270 // Now build outgoing splats
271 m_BlendSplats.resize(blendLayers.size());
273 for (size_t k = 0; k < blendLayers.size(); ++k)
275 SSplat& splat = m_BlendSplats[k];
276 splat.m_IndexStart = blendIndices.size();
277 splat.m_Texture = blendLayers[k].m_Texture;
279 for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
281 SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
282 AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
285 splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
288 // Release existing vertex buffer chunks
289 if (m_VBBlends)
291 g_VBMan.Release(m_VBBlends);
292 m_VBBlends = 0;
295 if (m_VBBlendIndices)
297 g_VBMan.Release(m_VBBlendIndices);
298 m_VBBlendIndices = 0;
301 if (blendVertices.size())
303 // Construct vertex buffer
305 m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), blendVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
306 m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends, &blendVertices[0]);
308 // Update the indices to include the base offset of the vertex data
309 for (size_t k = 0; k < blendIndices.size(); ++k)
310 blendIndices[k] += m_VBBlends->m_Index;
312 m_VBBlendIndices = g_VBMan.Allocate(sizeof(u16), blendIndices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
313 m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices, &blendIndices[0]);
317 void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
318 u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
320 CTerrain* terrain = m_Patch->m_Parent;
322 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
323 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
325 // uses the current neighbour texture
326 BlendShape8 shape8;
327 for (size_t m = 0; m < 8; ++m)
328 shape8[m] = (shape & (1 << m)) ? 0 : 1;
330 // calculate the required alphamap and the required rotation of the alphamap from blendshape
331 unsigned int alphamapflags;
332 int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
334 // now actually render the blend tile (if we need one)
335 if (alphamap == -1)
336 return;
338 float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0;
339 float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1;
340 float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0;
341 float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1;
343 if (alphamapflags & BLENDMAP_FLIPU)
344 std::swap(u0, u1);
346 if (alphamapflags & BLENDMAP_FLIPV)
347 std::swap(v0, v1);
349 int base = 0;
350 if (alphamapflags & BLENDMAP_ROTATE90)
351 base = 1;
352 else if (alphamapflags & BLENDMAP_ROTATE180)
353 base = 2;
354 else if (alphamapflags & BLENDMAP_ROTATE270)
355 base = 3;
357 SBlendVertex vtx[4];
358 vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
359 vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
360 vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
361 vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
362 vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
363 vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
364 vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
365 vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
367 SBlendVertex dst;
369 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
370 CVector3D normal;
372 bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
374 size_t index = blendVertices.size();
376 terrain->CalcPosition(gx, gz, dst.m_Position);
377 terrain->CalcNormal(gx, gz, normal);
378 dst.m_Normal = normal;
379 dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
380 dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
381 dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
382 blendVertices.push_back(dst);
384 terrain->CalcPosition(gx + 1, gz, dst.m_Position);
385 terrain->CalcNormal(gx + 1, gz, normal);
386 dst.m_Normal = normal;
387 dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
388 dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
389 dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
390 blendVertices.push_back(dst);
392 terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
393 terrain->CalcNormal(gx + 1, gz + 1, normal);
394 dst.m_Normal = normal;
395 dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
396 dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
397 dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
398 blendVertices.push_back(dst);
400 terrain->CalcPosition(gx, gz + 1, dst.m_Position);
401 terrain->CalcNormal(gx, gz + 1, normal);
402 dst.m_Normal = normal;
403 dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
404 dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
405 dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
406 blendVertices.push_back(dst);
408 bool dir = terrain->GetTriangulationDir(gx, gz);
409 if (dir)
411 blendIndices.push_back(index+0);
412 blendIndices.push_back(index+1);
413 blendIndices.push_back(index+3);
415 blendIndices.push_back(index+1);
416 blendIndices.push_back(index+2);
417 blendIndices.push_back(index+3);
419 else
421 blendIndices.push_back(index+0);
422 blendIndices.push_back(index+1);
423 blendIndices.push_back(index+2);
425 blendIndices.push_back(index+2);
426 blendIndices.push_back(index+3);
427 blendIndices.push_back(index+0);
431 void CPatchRData::BuildIndices()
433 PROFILE3("build indices");
435 CTerrain* terrain = m_Patch->m_Parent;
437 ssize_t px = m_Patch->m_X * PATCH_SIZE;
438 ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
440 // must have allocated some vertices before trying to build corresponding indices
441 ENSURE(m_VBBase);
443 // number of vertices in each direction in each patch
444 ssize_t vsize=PATCH_SIZE+1;
446 // PATCH_SIZE must be 2^8-2 or less to not overflow u16 indices buffer. Thankfully this is always true.
447 ENSURE(vsize*vsize < 65536);
449 std::vector<unsigned short> indices;
450 indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
452 // release existing splats
453 m_Splats.clear();
455 // build grid of textures on this patch
456 std::vector<CTerrainTextureEntry*> textures;
457 CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
458 for (ssize_t j=0;j<PATCH_SIZE;j++) {
459 for (ssize_t i=0;i<PATCH_SIZE;i++) {
460 CTerrainTextureEntry* tex=m_Patch->m_MiniPatches[j][i].GetTextureEntry();
461 texgrid[j][i]=tex;
462 if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
463 textures.push_back(tex);
468 // now build base splats from interior textures
469 m_Splats.resize(textures.size());
470 // build indices for base splats
471 size_t base=m_VBBase->m_Index;
473 for (size_t i=0;i<m_Splats.size();i++) {
474 CTerrainTextureEntry* tex=textures[i];
476 SSplat& splat=m_Splats[i];
477 splat.m_Texture=tex;
478 splat.m_IndexStart=indices.size();
480 for (ssize_t j = 0; j < PATCH_SIZE; j++)
482 for (ssize_t i = 0; i < PATCH_SIZE; i++)
484 if (texgrid[j][i] == tex)
486 bool dir = terrain->GetTriangulationDir(px+i, pz+j);
487 if (dir)
489 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
490 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
491 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
493 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
494 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
495 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
497 else
499 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
500 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
501 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
503 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
504 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
505 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
510 splat.m_IndexCount=indices.size()-splat.m_IndexStart;
513 // Release existing vertex buffer chunk
514 if (m_VBBaseIndices)
516 g_VBMan.Release(m_VBBaseIndices);
517 m_VBBaseIndices = 0;
520 ENSURE(indices.size());
522 // Construct vertex buffer
523 m_VBBaseIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
524 m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices, &indices[0]);
528 void CPatchRData::BuildVertices()
530 PROFILE3("build vertices");
532 // create both vertices and lighting colors
534 // number of vertices in each direction in each patch
535 ssize_t vsize=PATCH_SIZE+1;
537 std::vector<SBaseVertex> vertices;
538 vertices.resize(vsize*vsize);
540 // get index of this patch
541 ssize_t px=m_Patch->m_X;
542 ssize_t pz=m_Patch->m_Z;
544 CTerrain* terrain=m_Patch->m_Parent;
545 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
547 bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
549 // build vertices
550 for (ssize_t j=0;j<vsize;j++) {
551 for (ssize_t i=0;i<vsize;i++) {
552 ssize_t ix=px*PATCH_SIZE+i;
553 ssize_t iz=pz*PATCH_SIZE+j;
554 ssize_t v=(j*vsize)+i;
556 // calculate vertex data
557 terrain->CalcPosition(ix,iz,vertices[v].m_Position);
559 // Calculate diffuse lighting for this vertex
560 // Ambient is added by the lighting pass (since ambient is the same
561 // for all vertices, it need not be stored in the vertex structure)
562 CVector3D normal;
563 terrain->CalcNormal(ix,iz,normal);
565 vertices[v].m_Normal = normal;
567 vertices[v].m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
571 // upload to vertex buffer
572 if (!m_VBBase)
573 m_VBBase = g_VBMan.Allocate(sizeof(SBaseVertex), vsize * vsize, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
575 m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase, &vertices[0]);
578 void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
580 ssize_t vsize = PATCH_SIZE + 1;
581 CTerrain* terrain = m_Patch->m_Parent;
582 CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
584 for (ssize_t k = 0; k < vsize; k++)
586 ssize_t gx = m_Patch->m_X * PATCH_SIZE;
587 ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
588 switch (side)
590 case CPATCH_SIDE_NEGX: gz += k; break;
591 case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
592 case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
593 case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
596 CVector3D pos;
597 terrain->CalcPosition(gx, gz, pos);
599 // Clamp the height to the water level
600 float waterHeight = 0.f;
601 if (cmpWaterManager)
602 waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
603 pos.Y = std::max(pos.Y, waterHeight);
605 SSideVertex v0, v1;
606 v0.m_Position = pos;
607 v1.m_Position = pos;
608 v1.m_Position.Y = 0;
610 // If this is the start of this tristrip, but we've already got a partial
611 // tristrip, add a couple of degenerate triangles to join the strips properly
612 if (k == 0 && !vertices.empty())
614 vertices.push_back(vertices.back());
615 vertices.push_back(v1);
618 // Now add the new triangles
619 vertices.push_back(v1);
620 vertices.push_back(v0);
624 void CPatchRData::BuildSides()
626 PROFILE3("build sides");
628 std::vector<SSideVertex> sideVertices;
630 int sideFlags = m_Patch->GetSideFlags();
632 // If no sides are enabled, we don't need to do anything
633 if (!sideFlags)
634 return;
636 // For each side, generate a tristrip by adding a vertex at ground/water
637 // level and a vertex underneath at height 0.
639 if (sideFlags & CPATCH_SIDE_NEGX)
640 BuildSide(sideVertices, CPATCH_SIDE_NEGX);
642 if (sideFlags & CPATCH_SIDE_POSX)
643 BuildSide(sideVertices, CPATCH_SIDE_POSX);
645 if (sideFlags & CPATCH_SIDE_NEGZ)
646 BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
648 if (sideFlags & CPATCH_SIDE_POSZ)
649 BuildSide(sideVertices, CPATCH_SIDE_POSZ);
651 if (sideVertices.empty())
652 return;
654 if (!m_VBSides)
655 m_VBSides = g_VBMan.Allocate(sizeof(SSideVertex), sideVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
656 m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides, &sideVertices[0]);
659 void CPatchRData::Build()
661 BuildVertices();
662 BuildSides();
663 BuildIndices();
664 BuildBlends();
665 BuildWater();
668 void CPatchRData::Update(CSimulation2* simulation)
670 m_Simulation = simulation;
671 if (m_UpdateFlags!=0) {
672 // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
673 // than everything; it's complicated slightly because the blends are dependent
674 // on both vertex and index data
675 BuildVertices();
676 BuildSides();
677 BuildIndices();
678 BuildBlends();
679 BuildWater();
681 m_UpdateFlags=0;
685 // Types used for glMultiDrawElements batching:
687 // To minimise the cost of memory allocations, everything used for computing
688 // batches uses a arena allocator. (All allocations are short-lived so we can
689 // just throw away the whole arena at the end of each frame.)
691 // std::map types with appropriate arena allocators and default comparison operator
692 #define POOLED_BATCH_MAP(Key, Value) \
693 std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Allocators::DynamicArena > >
695 // Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
696 // use the default constructor in that case)
697 template<typename M>
698 typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
700 return m.insert(std::make_pair(k,
701 typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(arena))
702 )).first->second;
705 // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
706 template<typename M>
707 typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
709 return m.insert(std::make_pair(k, std::make_pair(
710 typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(arena)),
711 typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(arena))
712 ))).first->second;
715 // Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
716 typedef std::pair<std::vector<GLint, ProxyAllocator<GLint, Allocators::DynamicArena > >, std::vector<void*, ProxyAllocator<void*, Allocators::DynamicArena > > > BatchElements;
718 // Group batches by index buffer
719 typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
721 // Group batches by vertex buffer
722 typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches;
724 // Group batches by texture
725 typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches;
727 void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
728 ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
730 Allocators::DynamicArena arena(1 * MiB);
732 TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(arena)));
734 PROFILE_START("compute batches");
736 // Collect all the patches' base splats into their appropriate batches
737 for (size_t i = 0; i < patches.size(); ++i)
739 CPatchRData* patch = patches[i];
740 for (size_t j = 0; j < patch->m_Splats.size(); ++j)
742 SSplat& splat = patch->m_Splats[j];
744 BatchElements& batch = PooledPairGet(
745 PooledMapGet(
746 PooledMapGet(batches, splat.m_Texture, arena),
747 patch->m_VBBase->m_Owner, arena
749 patch->m_VBBaseIndices->m_Owner, arena
752 batch.first.push_back(splat.m_IndexCount);
754 u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
755 batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart));
759 PROFILE_END("compute batches");
761 // Render each batch
762 for (TextureBatches::iterator itt = batches.begin(); itt != batches.end(); ++itt)
764 int numPasses = 1;
766 CShaderTechniquePtr techBase;
768 if (!isDummyShader)
770 if (itt->first->GetMaterial().GetShaderEffect().length() == 0)
772 LOGERROR("Terrain renderer failed to load shader effect.\n");
773 continue;
776 techBase = g_Renderer.GetShaderManager().LoadEffect(itt->first->GetMaterial().GetShaderEffect(),
777 context, itt->first->GetMaterial().GetShaderDefines(0));
779 numPasses = techBase->GetNumPasses();
782 for (int pass = 0; pass < numPasses; ++pass)
784 if (!isDummyShader)
786 techBase->BeginPass(pass);
787 TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
790 const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
792 if (itt->first->GetMaterial().GetSamplers().size() != 0)
794 const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers();
795 size_t samplersNum = samplers.size();
797 for (size_t s = 0; s < samplersNum; ++s)
799 const CMaterial::TextureSampler& samp = samplers[s];
800 shader->BindTexture(samp.Name, samp.Sampler);
803 itt->first->GetMaterial().GetStaticUniforms().BindUniforms(shader);
805 #if !CONFIG2_GLES
806 if (isDummyShader)
808 glMatrixMode(GL_TEXTURE);
809 glLoadMatrixf(itt->first->GetTextureMatrix());
810 glMatrixMode(GL_MODELVIEW);
812 else
813 #endif
815 float c = itt->first->GetTextureMatrix()[0];
816 float ms = itt->first->GetTextureMatrix()[8];
817 shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
820 else
822 shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
825 for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
827 GLsizei stride = sizeof(SBaseVertex);
828 SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
829 shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
830 shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
831 shader->NormalPointer(GL_FLOAT, stride, &base->m_Normal[0]);
832 shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position[0]);
834 shader->AssertPointersBound();
836 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
838 it->first->Bind();
840 BatchElements& batch = it->second;
842 if (!g_Renderer.m_SkipSubmit)
844 // Don't use glMultiDrawElements here since it doesn't have a significant
845 // performance impact and it suffers from various driver bugs (e.g. it breaks
846 // in Mesa 7.10 swrast with index VBOs)
847 for (size_t i = 0; i < batch.first.size(); ++i)
848 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
851 g_Renderer.m_Stats.m_DrawCalls++;
852 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
856 if (!isDummyShader)
857 techBase->EndPass();
861 #if !CONFIG2_GLES
862 if (isDummyShader)
864 glMatrixMode(GL_TEXTURE);
865 glLoadIdentity();
866 glMatrixMode(GL_MODELVIEW);
868 #endif
870 CVertexBuffer::Unbind();
874 * Helper structure for RenderBlends.
876 struct SBlendBatch
878 SBlendBatch(Allocators::DynamicArena& arena) :
879 m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
883 CTerrainTextureEntry* m_Texture;
884 VertexBufferBatches m_Batches;
888 * Helper structure for RenderBlends.
890 struct SBlendStackItem
892 SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
893 const std::vector<CPatchRData::SSplat>& s, Allocators::DynamicArena& arena) :
894 vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
898 typedef std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Allocators::DynamicArena > > SplatStack;
899 CVertexBuffer::VBChunk* vertices;
900 CVertexBuffer::VBChunk* indices;
901 SplatStack splats;
904 void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
905 ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
907 Allocators::DynamicArena arena(1 * MiB);
909 typedef std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Allocators::DynamicArena > > BatchesStack;
910 BatchesStack batches((BatchesStack::allocator_type(arena)));
912 CShaderDefines contextBlend = context;
913 contextBlend.Add(str_BLEND, str_1);
915 PROFILE_START("compute batches");
917 // Reserve an arbitrary size that's probably big enough in most cases,
918 // to avoid heavy reallocations
919 batches.reserve(256);
921 typedef std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Allocators::DynamicArena > > BlendStacks;
922 BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
923 blendStacks.reserve(patches.size());
925 // Extract all the blend splats from each patch
926 for (size_t i = 0; i < patches.size(); ++i)
928 CPatchRData* patch = patches[i];
929 if (!patch->m_BlendSplats.empty())
932 blendStacks.push_back(SBlendStackItem(patch->m_VBBlends, patch->m_VBBlendIndices, patch->m_BlendSplats, arena));
933 // Reverse the splats so the first to be rendered is at the back of the list
934 std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
938 // Rearrange the collection of splats to be grouped by texture, preserving
939 // order of splats within each patch:
940 // (This is exactly the same algorithm used in CPatchRData::BuildBlends,
941 // but applied to patch-sized splats rather than to tile-sized splats;
942 // see that function for comments on the algorithm.)
943 while (true)
945 if (!batches.empty())
947 CTerrainTextureEntry* tex = batches.back().m_Texture;
949 for (size_t k = 0; k < blendStacks.size(); ++k)
951 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
952 if (!splats.empty() && splats.back().m_Texture == tex)
954 CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
955 CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
957 BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, arena), indices->m_Owner, arena);
958 batch.first.push_back(splats.back().m_IndexCount);
960 u8* indexBase = indices->m_Owner->GetBindAddress();
961 batch.second.push_back(indexBase + sizeof(u16)*(indices->m_Index + splats.back().m_IndexStart));
963 splats.pop_back();
968 CTerrainTextureEntry* bestTex = NULL;
969 size_t bestStackSize = 0;
971 for (size_t k = 0; k < blendStacks.size(); ++k)
973 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
974 if (splats.size() > bestStackSize)
976 bestStackSize = splats.size();
977 bestTex = splats.back().m_Texture;
981 if (bestStackSize == 0)
982 break;
984 SBlendBatch layer(arena);
985 layer.m_Texture = bestTex;
986 batches.push_back(layer);
989 PROFILE_END("compute batches");
991 CVertexBuffer* lastVB = NULL;
993 for (BatchesStack::iterator itt = batches.begin(); itt != batches.end(); ++itt)
995 if (itt->m_Texture->GetMaterial().GetSamplers().size() == 0)
996 continue;
998 int numPasses = 1;
999 CShaderTechniquePtr techBase;
1001 if (!isDummyShader)
1003 techBase = g_Renderer.GetShaderManager().LoadEffect(itt->m_Texture->GetMaterial().GetShaderEffect(), contextBlend, itt->m_Texture->GetMaterial().GetShaderDefines(0));
1005 numPasses = techBase->GetNumPasses();
1008 CShaderProgramPtr previousShader;
1009 for (int pass = 0; pass < numPasses; ++pass)
1011 if (!isDummyShader)
1013 techBase->BeginPass(pass);
1014 TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
1016 glEnable(GL_BLEND);
1017 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1020 const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
1022 if (itt->m_Texture)
1024 const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
1025 size_t samplersNum = samplers.size();
1027 for (size_t s = 0; s < samplersNum; ++s)
1029 const CMaterial::TextureSampler& samp = samplers[s];
1030 shader->BindTexture(samp.Name, samp.Sampler);
1033 shader->BindTexture(str_blendTex, itt->m_Texture->m_TerrainAlpha->second.m_hCompositeAlphaMap);
1035 itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(shader);
1037 #if !CONFIG2_GLES
1038 if (isDummyShader)
1040 pglClientActiveTextureARB(GL_TEXTURE0);
1041 glMatrixMode(GL_TEXTURE);
1042 glLoadMatrixf(itt->m_Texture->GetTextureMatrix());
1043 glMatrixMode(GL_MODELVIEW);
1045 else
1046 #endif
1048 float c = itt->m_Texture->GetTextureMatrix()[0];
1049 float ms = itt->m_Texture->GetTextureMatrix()[8];
1050 shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
1053 else
1055 shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
1058 for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
1060 // Rebind the VB only if it changed since the last batch
1061 if (itv->first != lastVB || shader != previousShader)
1063 lastVB = itv->first;
1064 previousShader = shader;
1065 GLsizei stride = sizeof(SBlendVertex);
1066 SBlendVertex *base = (SBlendVertex *)itv->first->Bind();
1068 shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
1069 shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
1070 shader->NormalPointer(GL_FLOAT, stride, &base->m_Normal[0]);
1071 shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position[0]);
1072 shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, &base->m_AlphaUVs[0]);
1075 shader->AssertPointersBound();
1077 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1079 it->first->Bind();
1081 BatchElements& batch = it->second;
1083 if (!g_Renderer.m_SkipSubmit)
1085 for (size_t i = 0; i < batch.first.size(); ++i)
1086 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1089 g_Renderer.m_Stats.m_DrawCalls++;
1090 g_Renderer.m_Stats.m_BlendSplats++;
1091 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1095 if (!isDummyShader)
1097 glDisable(GL_BLEND);
1098 techBase->EndPass();
1103 #if !CONFIG2_GLES
1104 if (isDummyShader)
1106 pglClientActiveTextureARB(GL_TEXTURE0);
1107 glMatrixMode(GL_TEXTURE);
1108 glLoadIdentity();
1109 glMatrixMode(GL_MODELVIEW);
1111 #endif
1113 CVertexBuffer::Unbind();
1116 void CPatchRData::RenderStreams(const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader, int streamflags)
1118 // Each batch has a list of index counts, and a list of pointers-to-first-indexes
1119 typedef std::pair<std::vector<GLint>, std::vector<void*> > BatchElements;
1121 // Group batches by index buffer
1122 typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
1124 // Group batches by vertex buffer
1125 typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
1127 VertexBufferBatches batches;
1129 PROFILE_START("compute batches");
1131 // Collect all the patches into their appropriate batches
1132 for (size_t i = 0; i < patches.size(); ++i)
1134 CPatchRData* patch = patches[i];
1135 BatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
1137 batch.first.push_back(patch->m_VBBaseIndices->m_Count);
1139 u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
1140 batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index));
1143 PROFILE_END("compute batches");
1145 ENSURE(!(streamflags & ~(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0|STREAM_POSTOUV1)));
1147 // Render each batch
1148 for (VertexBufferBatches::iterator itv = batches.begin(); itv != batches.end(); ++itv)
1150 GLsizei stride = sizeof(SBaseVertex);
1151 SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
1153 shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
1154 if (streamflags & STREAM_POSTOUV0)
1155 shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position);
1156 if (streamflags & STREAM_POSTOUV1)
1157 shader->TexCoordPointer(GL_TEXTURE1, 3, GL_FLOAT, stride, &base->m_Position);
1158 if (streamflags & STREAM_COLOR)
1159 shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
1161 shader->AssertPointersBound();
1163 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1165 it->first->Bind();
1167 BatchElements& batch = it->second;
1169 if (!g_Renderer.m_SkipSubmit)
1171 for (size_t i = 0; i < batch.first.size(); ++i)
1172 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1175 g_Renderer.m_Stats.m_DrawCalls++;
1176 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1180 CVertexBuffer::Unbind();
1183 void CPatchRData::RenderOutline()
1185 CTerrain* terrain = m_Patch->m_Parent;
1186 ssize_t gx = m_Patch->m_X * PATCH_SIZE;
1187 ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
1189 CVector3D pos;
1190 std::vector<CVector3D> line;
1192 ssize_t i, j;
1194 for (i = 0, j = 0; i <= PATCH_SIZE; ++i)
1196 terrain->CalcPosition(gx + i, gz + j, pos);
1197 line.push_back(pos);
1199 for (i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1201 terrain->CalcPosition(gx + i, gz + j, pos);
1202 line.push_back(pos);
1204 for (i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1206 terrain->CalcPosition(gx + i, gz + j, pos);
1207 line.push_back(pos);
1209 for (i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1211 terrain->CalcPosition(gx + i, gz + j, pos);
1212 line.push_back(pos);
1215 #if CONFIG2_GLES
1216 #warning TODO: implement CPatchRData::RenderOutlines for GLES
1217 #else
1218 glVertexPointer(3, GL_FLOAT, sizeof(CVector3D), &line[0]);
1219 glDrawArrays(GL_LINE_STRIP, 0, line.size());
1220 #endif
1223 void CPatchRData::RenderSides(CShaderProgramPtr& shader)
1225 ENSURE(m_UpdateFlags==0);
1227 if (!m_VBSides)
1228 return;
1230 SSideVertex *base = (SSideVertex *)m_VBSides->m_Owner->Bind();
1232 // setup data pointers
1233 GLsizei stride = sizeof(SSideVertex);
1234 shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
1236 shader->AssertPointersBound();
1238 if (!g_Renderer.m_SkipSubmit)
1239 glDrawArrays(GL_TRIANGLE_STRIP, m_VBSides->m_Index, (GLsizei)m_VBSides->m_Count);
1241 // bump stats
1242 g_Renderer.m_Stats.m_DrawCalls++;
1243 g_Renderer.m_Stats.m_TerrainTris += m_VBSides->m_Count - 2;
1245 CVertexBuffer::Unbind();
1248 void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
1250 CTerrain* terrain = m_Patch->m_Parent;
1251 CCamera* camera = g_Game->GetView()->GetCamera();
1253 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1255 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1257 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1258 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1260 CVector3D pos;
1261 terrain->CalcPosition(gx, gz, pos);
1263 // Move a bit towards the center of the tile
1264 pos.X += TERRAIN_TILE_SIZE/4.f;
1265 pos.Z += TERRAIN_TILE_SIZE/4.f;
1267 float x, y;
1268 camera->GetScreenCoordinates(pos, x, y);
1270 textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1276 // Water build and rendering
1279 // Build vertex buffer for water vertices over our patch
1280 void CPatchRData::BuildWater()
1282 PROFILE3("build water");
1284 // number of vertices in each direction in each patch
1285 ENSURE((PATCH_SIZE % water_cell_size) == 0);
1287 if (m_VBWater)
1289 g_VBMan.Release(m_VBWater);
1290 m_VBWater = 0;
1292 if (m_VBWaterIndices)
1294 g_VBMan.Release(m_VBWaterIndices);
1295 m_VBWaterIndices = 0;
1297 if (m_VBWaterShore)
1299 g_VBMan.Release(m_VBWaterShore);
1300 m_VBWaterShore = 0;
1302 if (m_VBWaterIndicesShore)
1304 g_VBMan.Release(m_VBWaterIndicesShore);
1305 m_VBWaterIndicesShore = 0;
1307 m_WaterBounds.SetEmpty();
1309 // We need to use this to access the water manager or we may not have the
1310 // actual values but some compiled-in defaults
1311 CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
1312 if (!cmpWaterManager)
1313 return;
1315 // Build data for water
1316 std::vector<SWaterVertex> water_vertex_data;
1317 std::vector<GLushort> water_indices;
1318 u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1319 memset(water_index_map, 0xFF, sizeof(water_index_map));
1321 // Build data for shore
1322 std::vector<SWaterVertex> water_vertex_data_shore;
1323 std::vector<GLushort> water_indices_shore;
1324 u16 water_shore_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1325 memset(water_shore_index_map, 0xFF, sizeof(water_shore_index_map));
1327 WaterManager* WaterMgr = g_Renderer.GetWaterManager();
1329 CPatch* patch = m_Patch;
1330 CTerrain* terrain = patch->m_Parent;
1332 ssize_t mapSize = (size_t)terrain->GetVerticesPerSide();
1334 //Top-left coordinates of our patch.
1335 ssize_t x1 = m_Patch->m_X*PATCH_SIZE;
1336 ssize_t z1 = m_Patch->m_Z*PATCH_SIZE;
1338 // to whoever implements different water heights, this is a TODO: water height)
1339 float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
1341 // The 4 points making a water tile.
1342 int moves[4][2] = { {0,0}, {water_cell_size,0}, {0,water_cell_size}, {water_cell_size,water_cell_size} };
1343 // Where to look for when checking for water for shore tiles.
1344 int check[10][2] = { {0,0},{water_cell_size,0},{water_cell_size*2,0},{0,water_cell_size},{0,water_cell_size*2},{water_cell_size,water_cell_size},{water_cell_size*2,water_cell_size*2}, {-water_cell_size,0}, {0,-water_cell_size}, {-water_cell_size,-water_cell_size} };
1346 // build vertices, uv, and shader varying
1347 for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
1349 for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
1352 // Check that this tile is close to water
1353 bool nearWat = false;
1354 for (size_t test = 0; test < 10; ++test)
1355 if (terrain->GetVertexGroundLevel(x+x1+check[test][0], z+z1+check[test][1]) < waterHeight)
1356 nearWat = true;
1357 if (!nearWat)
1358 continue;
1360 // This is actually lying and I should call CcmpTerrain
1361 /*if (!terrain->IsOnMap(x+x1, z+z1)
1362 && !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
1363 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
1364 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
1365 continue;*/
1367 for (int i = 0; i < 4; ++i)
1369 if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1370 continue;
1372 ssize_t zz = z+z1+moves[i][1];
1373 ssize_t xx = x+x1+moves[i][0];
1375 SWaterVertex vertex;
1376 terrain->CalcPosition(xx,zz, vertex.m_Position);
1377 float depth = waterHeight - vertex.m_Position.Y;
1379 vertex.m_Position.Y = waterHeight;
1381 m_WaterBounds += vertex.m_Position;
1383 vertex.m_WaterData = CVector2D(WaterMgr->m_WindStrength[xx + zz*mapSize], depth);
1385 water_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data.size();
1386 water_vertex_data.push_back(vertex);
1388 water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1389 water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
1390 water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1391 water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1392 water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
1393 water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1395 // Check id this tile is partly over land.
1396 // If so add a square over the terrain. This is necessary to render waves that go on shore.
1397 if (terrain->GetVertexGroundLevel(x+x1, z+z1) < waterHeight
1398 && terrain->GetVertexGroundLevel(x+x1 + water_cell_size, z+z1) < waterHeight
1399 && terrain->GetVertexGroundLevel(x+x1, z+z1+water_cell_size) < waterHeight
1400 && terrain->GetVertexGroundLevel(x+x1 + water_cell_size, z+z1+water_cell_size) < waterHeight)
1401 continue;
1403 for (int i = 0; i < 4; ++i)
1405 if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1406 continue;
1407 ssize_t zz = z+z1+moves[i][1];
1408 ssize_t xx = x+x1+moves[i][0];
1410 SWaterVertex vertex;
1411 terrain->CalcPosition(xx,zz, vertex.m_Position);
1413 vertex.m_Position.Y += 0.02f;
1414 m_WaterBounds += vertex.m_Position;
1416 vertex.m_WaterData = CVector2D(0.0f, -5.0f);
1418 water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data_shore.size();
1419 water_vertex_data_shore.push_back(vertex);
1421 if (terrain->GetTriangulationDir(x+x1,z+z1))
1423 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1424 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1425 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1426 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1427 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1428 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1430 else
1432 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1433 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1434 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1435 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1436 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1437 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1442 // no vertex buffers if no data generated
1443 if (water_indices.size() != 0)
1445 m_VBWater = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
1446 m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater, &water_vertex_data[0]);
1448 m_VBWaterIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
1449 m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices, &water_indices[0]);
1452 if (water_indices_shore.size() != 0)
1454 m_VBWaterShore = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data_shore.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
1455 m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore, &water_vertex_data_shore[0]);
1457 // Construct indices buffer
1458 m_VBWaterIndicesShore = g_VBMan.Allocate(sizeof(GLushort), water_indices_shore.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
1459 m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore, &water_indices_shore[0]);
1462 void CPatchRData::RenderWater(CShaderProgramPtr& shader, bool onlyShore, bool fixedPipeline)
1464 ASSERT(m_UpdateFlags==0);
1466 if (g_Renderer.m_SkipSubmit || (!m_VBWater && !m_VBWaterShore))
1467 return;
1469 if (m_VBWater != 0x0 && !onlyShore)
1471 SWaterVertex *base=(SWaterVertex *)m_VBWater->m_Owner->Bind();
1473 // setup data pointers
1474 GLsizei stride = sizeof(SWaterVertex);
1475 shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWater->m_Index].m_Position);
1476 if (!fixedPipeline)
1477 shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWater->m_Index].m_WaterData);
1479 shader->AssertPointersBound();
1481 u8* indexBase = m_VBWaterIndices->m_Owner->Bind();
1482 glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndices->m_Count,
1483 GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndices->m_Index));
1485 g_Renderer.m_Stats.m_DrawCalls++;
1486 g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
1488 if (m_VBWaterShore != 0x0 && g_Renderer.GetWaterManager()->m_WaterFancyEffects && !g_Renderer.GetWaterManager()->m_WaterUgly)
1490 SWaterVertex *base=(SWaterVertex *)m_VBWaterShore->m_Owner->Bind();
1492 GLsizei stride = sizeof(SWaterVertex);
1493 shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWaterShore->m_Index].m_Position);
1494 if (!fixedPipeline)
1495 shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWaterShore->m_Index].m_WaterData);
1497 shader->AssertPointersBound();
1499 u8* indexBase = m_VBWaterIndicesShore->m_Owner->Bind();
1500 glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndicesShore->m_Count,
1501 GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndicesShore->m_Index));
1503 g_Renderer.m_Stats.m_DrawCalls++;
1504 g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;
1507 CVertexBuffer::Unbind();