Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PatchRData.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include <set>
21 #include <algorithm>
22 #include <numeric>
23 
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"
41 #include "renderer/PatchRData.h"
43 #include "renderer/Renderer.h"
44 #include "renderer/WaterManager.h"
47 
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 }
58 };
59 
60 ///////////////////////////////////////////////////////////////////
61 // CPatchRData constructor
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_Simulation(simulation)
68 {
69  ENSURE(patch);
70  Build();
71 }
72 
73 ///////////////////////////////////////////////////////////////////
74 // CPatchRData destructor
76 {
77  // release vertex buffer chunks
85 }
86 
87 /**
88  * Represents a blend for a single tile, texture and shape.
89  */
90 struct STileBlend
91 {
94  u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
95 
97  {
98  bool operator()(const STileBlend& a, const STileBlend& b) const
99  {
100  if (a.m_Priority > b.m_Priority)
101  return true;
102  if (a.m_Priority < b.m_Priority)
103  return false;
104  if (a.m_Texture && b.m_Texture)
105  return a.m_Texture->GetTag() > b.m_Texture->GetTag();
106  return false;
107  }
108  };
109 
110  struct CurrentTile
111  {
112  bool operator()(const STileBlend& a) const
113  {
114  return (a.m_TileMask & (1 << 8)) != 0;
115  }
116  };
117 };
118 
119 /**
120  * Represents the ordered collection of blends drawn on a particular tile.
121  */
123 {
124  u8 i, j;
125  std::vector<STileBlend> blends; // back of vector is lowest-priority texture
126 };
127 
128 /**
129  * Represents a batched collection of blends using the same texture.
130  */
132 {
133  struct Tile
134  {
135  u8 i, j;
137  };
138 
140  std::vector<Tile> m_Tiles;
141 };
142 
144 {
145  PROFILE3("build blends");
146 
147  m_BlendSplats.clear();
148 
149  std::vector<SBlendVertex> blendVertices;
150  std::vector<u16> blendIndices;
151 
152  CTerrain* terrain = m_Patch->m_Parent;
153 
154  std::vector<STileBlendStack> blendStacks;
155  blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
156 
157  // For each tile in patch ..
158  for (ssize_t j = 0; j < PATCH_SIZE; ++j)
159  {
160  for (ssize_t i = 0; i < PATCH_SIZE; ++i)
161  {
162  ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
163  ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
164 
165  std::vector<STileBlend> blends;
166  blends.reserve(9);
167 
168  // Compute a blend for every tile in the 3x3 square around this tile
169  for (size_t n = 0; n < 9; ++n)
170  {
171  ssize_t ox = gx + BlendOffsets[n][1];
172  ssize_t oz = gz + BlendOffsets[n][0];
173 
174  CMiniPatch* nmp = terrain->GetTile(ox, oz);
175  if (!nmp)
176  continue;
177 
178  STileBlend blend;
179  blend.m_Texture = nmp->GetTextureEntry();
180  blend.m_Priority = nmp->GetPriority();
181  blend.m_TileMask = 1 << n;
182  blends.push_back(blend);
183  }
184 
185  // Sort the blends, highest priority first
186  std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
187 
188  STileBlendStack blendStack;
189  blendStack.i = i;
190  blendStack.j = j;
191 
192  // Put the blends into the tile's stack, merging any adjacent blends with the same texture
193  for (size_t k = 0; k < blends.size(); ++k)
194  {
195  if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
196  blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
197  else
198  blendStack.blends.push_back(blends[k]);
199  }
200 
201  // Remove blends that are after (i.e. lower priority than) the current tile
202  // (including the current tile), since we don't want to render them on top of
203  // the tile's base texture
204  blendStack.blends.erase(
205  std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
206  blendStack.blends.end());
207 
208  blendStacks.push_back(blendStack);
209  }
210  }
211 
212  // Given the blend stack per tile, we want to batch together as many blends as possible.
213  // Group them into a series of layers (each of which has a single texture):
214  // (This is effectively a topological sort / linearisation of the partial order induced
215  // by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
216 
217  std::vector<SBlendLayer> blendLayers;
218 
219  while (true)
220  {
221  if (!blendLayers.empty())
222  {
223  // Try to grab as many tiles as possible that match our current layer,
224  // from off the blend stacks of all the tiles
225 
226  CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
227 
228  for (size_t k = 0; k < blendStacks.size(); ++k)
229  {
230  if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
231  {
232  SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, (u8)blendStacks[k].blends.back().m_TileMask };
233  blendLayers.back().m_Tiles.push_back(t);
234  blendStacks[k].blends.pop_back();
235  }
236  // (We've already merged adjacent entries of the same texture in each stack,
237  // so we don't need to bother looping to check the next entry in this stack again)
238  }
239  }
240 
241  // We've grabbed as many tiles as possible; now we need to start a new layer.
242  // The new layer's texture could come from the back of any non-empty stack;
243  // choose the longest stack as a heuristic to reduce the number of layers
244  CTerrainTextureEntry* bestTex = NULL;
245  size_t bestStackSize = 0;
246 
247  for (size_t k = 0; k < blendStacks.size(); ++k)
248  {
249  if (blendStacks[k].blends.size() > bestStackSize)
250  {
251  bestStackSize = blendStacks[k].blends.size();
252  bestTex = blendStacks[k].blends.back().m_Texture;
253  }
254  }
255 
256  // If all our stacks were empty, we're done
257  if (bestStackSize == 0)
258  break;
259 
260  // Otherwise add the new layer, then loop back and start filling it in
261 
262  SBlendLayer layer;
263  layer.m_Texture = bestTex;
264  blendLayers.push_back(layer);
265  }
266 
267  // Now build outgoing splats
268  m_BlendSplats.resize(blendLayers.size());
269 
270  for (size_t k = 0; k < blendLayers.size(); ++k)
271  {
272  SSplat& splat = m_BlendSplats[k];
273  splat.m_IndexStart = blendIndices.size();
274  splat.m_Texture = blendLayers[k].m_Texture;
275 
276  for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
277  {
278  SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
279  AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
280  }
281 
282  splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
283  }
284 
285  // Release existing vertex buffer chunks
286  if (m_VBBlends)
287  {
289  m_VBBlends = 0;
290  }
291 
292  if (m_VBBlendIndices)
293  {
295  m_VBBlendIndices = 0;
296  }
297 
298  if (blendVertices.size())
299  {
300  // Construct vertex buffer
301 
302  m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), blendVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
303  m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends, &blendVertices[0]);
304 
305  // Update the indices to include the base offset of the vertex data
306  for (size_t k = 0; k < blendIndices.size(); ++k)
307  blendIndices[k] += m_VBBlends->m_Index;
308 
309  m_VBBlendIndices = g_VBMan.Allocate(sizeof(u16), blendIndices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
311  }
312 }
313 
314 void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
315  u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
316 {
317  CTerrain* terrain = m_Patch->m_Parent;
318 
319  ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
320  ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
321 
322  // uses the current neighbour texture
323  BlendShape8 shape8;
324  for (size_t m = 0; m < 8; ++m)
325  shape8[m] = (shape & (1 << m)) ? 0 : 1;
326 
327  // calculate the required alphamap and the required rotation of the alphamap from blendshape
328  unsigned int alphamapflags;
329  int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
330 
331  // now actually render the blend tile (if we need one)
332  if (alphamap == -1)
333  return;
334 
335  float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0;
336  float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1;
337  float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0;
338  float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1;
339 
340  if (alphamapflags & BLENDMAP_FLIPU)
341  std::swap(u0, u1);
342 
343  if (alphamapflags & BLENDMAP_FLIPV)
344  std::swap(v0, v1);
345 
346  int base = 0;
347  if (alphamapflags & BLENDMAP_ROTATE90)
348  base = 1;
349  else if (alphamapflags & BLENDMAP_ROTATE180)
350  base = 2;
351  else if (alphamapflags & BLENDMAP_ROTATE270)
352  base = 3;
353 
354  SBlendVertex vtx[4];
355  vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
356  vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
357  vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
358  vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
359  vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
360  vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
361  vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
362  vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
363 
364  SBlendVertex dst;
365 
366  const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
367  CVector3D normal;
368 
369  bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
370 
371  size_t index = blendVertices.size();
372 
373  terrain->CalcPosition(gx, gz, dst.m_Position);
374  terrain->CalcNormal(gx, gz, normal);
375  dst.m_Normal = normal;
376  dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
377  dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
378  dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
379  blendVertices.push_back(dst);
380 
381  terrain->CalcPosition(gx + 1, gz, dst.m_Position);
382  terrain->CalcNormal(gx + 1, gz, normal);
383  dst.m_Normal = normal;
384  dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
385  dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
386  dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
387  blendVertices.push_back(dst);
388 
389  terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
390  terrain->CalcNormal(gx + 1, gz + 1, normal);
391  dst.m_Normal = normal;
392  dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
393  dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
394  dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
395  blendVertices.push_back(dst);
396 
397  terrain->CalcPosition(gx, gz + 1, dst.m_Position);
398  terrain->CalcNormal(gx, gz + 1, normal);
399  dst.m_Normal = normal;
400  dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
401  dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
402  dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
403  blendVertices.push_back(dst);
404 
405  bool dir = terrain->GetTriangulationDir(gx, gz);
406  if (dir)
407  {
408  blendIndices.push_back(index+0);
409  blendIndices.push_back(index+1);
410  blendIndices.push_back(index+3);
411 
412  blendIndices.push_back(index+1);
413  blendIndices.push_back(index+2);
414  blendIndices.push_back(index+3);
415  }
416  else
417  {
418  blendIndices.push_back(index+0);
419  blendIndices.push_back(index+1);
420  blendIndices.push_back(index+2);
421 
422  blendIndices.push_back(index+2);
423  blendIndices.push_back(index+3);
424  blendIndices.push_back(index+0);
425  }
426 }
427 
429 {
430  PROFILE3("build indices");
431 
432  CTerrain* terrain = m_Patch->m_Parent;
433 
434  ssize_t px = m_Patch->m_X * PATCH_SIZE;
435  ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
436 
437  // must have allocated some vertices before trying to build corresponding indices
438  ENSURE(m_VBBase);
439 
440  // number of vertices in each direction in each patch
441  ssize_t vsize=PATCH_SIZE+1;
442 
443  std::vector<unsigned short> indices;
444  indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
445 
446  // release existing splats
447  m_Splats.clear();
448 
449  // build grid of textures on this patch
450  std::vector<CTerrainTextureEntry*> textures;
452  for (ssize_t j=0;j<PATCH_SIZE;j++) {
453  for (ssize_t i=0;i<PATCH_SIZE;i++) {
455  texgrid[j][i]=tex;
456  if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
457  textures.push_back(tex);
458  }
459  }
460  }
461 
462  // now build base splats from interior textures
463  m_Splats.resize(textures.size());
464  // build indices for base splats
465  size_t base=m_VBBase->m_Index;
466  ENSURE(base + vsize*vsize < 65536); // mustn't overflow u16 indexes
467  for (size_t i=0;i<m_Splats.size();i++) {
468  CTerrainTextureEntry* tex=textures[i];
469 
470  SSplat& splat=m_Splats[i];
471  splat.m_Texture=tex;
472  splat.m_IndexStart=indices.size();
473 
474  for (ssize_t j = 0; j < PATCH_SIZE; j++)
475  {
476  for (ssize_t i = 0; i < PATCH_SIZE; i++)
477  {
478  if (texgrid[j][i] == tex)
479  {
480  bool dir = terrain->GetTriangulationDir(px+i, pz+j);
481  if (dir)
482  {
483  indices.push_back(u16(((j+0)*vsize+(i+0))+base));
484  indices.push_back(u16(((j+0)*vsize+(i+1))+base));
485  indices.push_back(u16(((j+1)*vsize+(i+0))+base));
486 
487  indices.push_back(u16(((j+0)*vsize+(i+1))+base));
488  indices.push_back(u16(((j+1)*vsize+(i+1))+base));
489  indices.push_back(u16(((j+1)*vsize+(i+0))+base));
490  }
491  else
492  {
493  indices.push_back(u16(((j+0)*vsize+(i+0))+base));
494  indices.push_back(u16(((j+0)*vsize+(i+1))+base));
495  indices.push_back(u16(((j+1)*vsize+(i+1))+base));
496 
497  indices.push_back(u16(((j+1)*vsize+(i+1))+base));
498  indices.push_back(u16(((j+1)*vsize+(i+0))+base));
499  indices.push_back(u16(((j+0)*vsize+(i+0))+base));
500  }
501  }
502  }
503  }
504  splat.m_IndexCount=indices.size()-splat.m_IndexStart;
505  }
506 
507  // Release existing vertex buffer chunk
508  if (m_VBBaseIndices)
509  {
511  m_VBBaseIndices = 0;
512  }
513 
514  ENSURE(indices.size());
515 
516  // Construct vertex buffer
517  m_VBBaseIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
518  m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices, &indices[0]);
519 }
520 
521 
523 {
524  PROFILE3("build vertices");
525 
526  // create both vertices and lighting colors
527 
528  // number of vertices in each direction in each patch
529  ssize_t vsize=PATCH_SIZE+1;
530 
531  std::vector<SBaseVertex> vertices;
532  vertices.resize(vsize*vsize);
533 
534  // get index of this patch
535  ssize_t px=m_Patch->m_X;
536  ssize_t pz=m_Patch->m_Z;
537 
538  CTerrain* terrain=m_Patch->m_Parent;
539  const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
540 
541  bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
542 
543  // build vertices
544  for (ssize_t j=0;j<vsize;j++) {
545  for (ssize_t i=0;i<vsize;i++) {
546  ssize_t ix=px*PATCH_SIZE+i;
547  ssize_t iz=pz*PATCH_SIZE+j;
548  ssize_t v=(j*vsize)+i;
549 
550  // calculate vertex data
551  terrain->CalcPosition(ix,iz,vertices[v].m_Position);
552 
553  // Calculate diffuse lighting for this vertex
554  // Ambient is added by the lighting pass (since ambient is the same
555  // for all vertices, it need not be stored in the vertex structure)
556  CVector3D normal;
557  terrain->CalcNormal(ix,iz,normal);
558 
559  vertices[v].m_Normal = normal;
560 
561  vertices[v].m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
562  }
563  }
564 
565  // upload to vertex buffer
566  if (!m_VBBase)
567  m_VBBase = g_VBMan.Allocate(sizeof(SBaseVertex), vsize * vsize, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
568 
569  m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase, &vertices[0]);
570 }
571 
572 void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
573 {
574  ssize_t vsize = PATCH_SIZE + 1;
575  CTerrain* terrain = m_Patch->m_Parent;
577 
578  for (ssize_t k = 0; k < vsize; k++)
579  {
580  ssize_t gx = m_Patch->m_X * PATCH_SIZE;
581  ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
582  switch (side)
583  {
584  case CPATCH_SIDE_NEGX: gz += k; break;
585  case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
586  case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
587  case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
588  }
589 
590  CVector3D pos;
591  terrain->CalcPosition(gx, gz, pos);
592 
593  // Clamp the height to the water level
594  float waterHeight = 0.f;
595  if (cmpWaterManager)
596  waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
597  pos.Y = std::max(pos.Y, waterHeight);
598 
599  SSideVertex v0, v1;
600  v0.m_Position = pos;
601  v1.m_Position = pos;
602  v1.m_Position.Y = 0;
603 
604  // If this is the start of this tristrip, but we've already got a partial
605  // tristrip, add a couple of degenerate triangles to join the strips properly
606  if (k == 0 && !vertices.empty())
607  {
608  vertices.push_back(vertices.back());
609  vertices.push_back(v1);
610  }
611 
612  // Now add the new triangles
613  vertices.push_back(v1);
614  vertices.push_back(v0);
615  }
616 }
617 
619 {
620  PROFILE3("build sides");
621 
622  std::vector<SSideVertex> sideVertices;
623 
624  int sideFlags = m_Patch->GetSideFlags();
625 
626  // If no sides are enabled, we don't need to do anything
627  if (!sideFlags)
628  return;
629 
630  // For each side, generate a tristrip by adding a vertex at ground/water
631  // level and a vertex underneath at height 0.
632 
633  if (sideFlags & CPATCH_SIDE_NEGX)
634  BuildSide(sideVertices, CPATCH_SIDE_NEGX);
635 
636  if (sideFlags & CPATCH_SIDE_POSX)
637  BuildSide(sideVertices, CPATCH_SIDE_POSX);
638 
639  if (sideFlags & CPATCH_SIDE_NEGZ)
640  BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
641 
642  if (sideFlags & CPATCH_SIDE_POSZ)
643  BuildSide(sideVertices, CPATCH_SIDE_POSZ);
644 
645  if (sideVertices.empty())
646  return;
647 
648  if (!m_VBSides)
649  m_VBSides = g_VBMan.Allocate(sizeof(SSideVertex), sideVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
650  m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides, &sideVertices[0]);
651 }
652 
654 {
655  BuildVertices();
656  BuildSides();
657  BuildIndices();
658  BuildBlends();
659  BuildWater();
660 }
661 
663 {
664  m_Simulation = simulation;
665  if (m_UpdateFlags!=0) {
666  // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
667  // than everything; it's complicated slightly because the blends are dependent
668  // on both vertex and index data
669  BuildVertices();
670  BuildSides();
671  BuildIndices();
672  BuildBlends();
673  BuildWater();
674 
675  m_UpdateFlags=0;
676  }
677 }
678 
679 // Types used for glMultiDrawElements batching:
680 
681 // To minimise the cost of memory allocations, everything used for computing
682 // batches uses a arena allocator. (All allocations are short-lived so we can
683 // just throw away the whole arena at the end of each frame.)
684 
685 // std::map types with appropriate arena allocators and default comparison operator
686 #define POOLED_BATCH_MAP(Key, Value) \
687  std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Allocators::DynamicArena > >
688 
689 // Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
690 // use the default constructor in that case)
691 template<typename M>
692 typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
693 {
694  return m.insert(std::make_pair(k,
695  typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(arena))
696  )).first->second;
697 }
698 
699 // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
700 template<typename M>
701 typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
702 {
703  return m.insert(std::make_pair(k, std::make_pair(
704  typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(arena)),
705  typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(arena))
706  ))).first->second;
707 }
708 
709 // Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
710 typedef std::pair<std::vector<GLint, ProxyAllocator<GLint, Allocators::DynamicArena > >, std::vector<void*, ProxyAllocator<void*, Allocators::DynamicArena > > > BatchElements;
711 
712 // Group batches by index buffer
713 typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
714 
715 // Group batches by vertex buffer
716 typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches;
717 
718 // Group batches by texture
719 typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches;
720 
721 void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
722  ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
723 {
724  Allocators::DynamicArena arena(1 * MiB);
725 
726  TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(arena)));
727 
728  PROFILE_START("compute batches");
729 
730  // Collect all the patches' base splats into their appropriate batches
731  for (size_t i = 0; i < patches.size(); ++i)
732  {
733  CPatchRData* patch = patches[i];
734  for (size_t j = 0; j < patch->m_Splats.size(); ++j)
735  {
736  SSplat& splat = patch->m_Splats[j];
737 
738  BatchElements& batch = PooledPairGet(
739  PooledMapGet(
740  PooledMapGet(batches, splat.m_Texture, arena),
741  patch->m_VBBase->m_Owner, arena
742  ),
743  patch->m_VBBaseIndices->m_Owner, arena
744  );
745 
746  batch.first.push_back(splat.m_IndexCount);
747 
748  u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
749  batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart));
750  }
751  }
752 
753  PROFILE_END("compute batches");
754 
755  // Render each batch
756  for (TextureBatches::iterator itt = batches.begin(); itt != batches.end(); ++itt)
757  {
758  int numPasses = 1;
759 
760  CShaderTechniquePtr techBase;
761 
762  if (!isDummyShader)
763  {
764  if (itt->first->GetMaterial().GetShaderEffect().length() == 0)
765  {
766  LOGERROR(L"Terrain renderer failed to load shader effect.\n");
767  continue;
768  }
769 
770  techBase = g_Renderer.GetShaderManager().LoadEffect(itt->first->GetMaterial().GetShaderEffect(),
771  context, itt->first->GetMaterial().GetShaderDefines(0));
772 
773  numPasses = techBase->GetNumPasses();
774  }
775 
776  for (int pass = 0; pass < numPasses; ++pass)
777  {
778  if (!isDummyShader)
779  {
780  techBase->BeginPass(pass);
781  TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
782  }
783 
784  const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
785 
786  if (itt->first->GetMaterial().GetSamplers().size() != 0)
787  {
788  const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers();
789  size_t samplersNum = samplers.size();
790 
791  for (size_t s = 0; s < samplersNum; ++s)
792  {
793  const CMaterial::TextureSampler& samp = samplers[s];
794  shader->BindTexture(samp.Name, samp.Sampler);
795  }
796 
797  itt->first->GetMaterial().GetStaticUniforms().BindUniforms(shader);
798 
799 #if !CONFIG2_GLES
800  if (isDummyShader)
801  {
802  glMatrixMode(GL_TEXTURE);
803  glLoadMatrixf(itt->first->GetTextureMatrix());
804  glMatrixMode(GL_MODELVIEW);
805  }
806  else
807 #endif
808  {
809  float c = itt->first->GetTextureMatrix()[0];
810  float ms = itt->first->GetTextureMatrix()[8];
811  shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
812  }
813  }
814  else
815  {
816  shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
817  }
818 
819  for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
820  {
821  GLsizei stride = sizeof(SBaseVertex);
822  SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
823  shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
824  shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
825  shader->NormalPointer(GL_FLOAT, stride, &base->m_Normal[0]);
826  shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position[0]);
827 
828  shader->AssertPointersBound();
829 
830  for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
831  {
832  it->first->Bind();
833 
834  BatchElements& batch = it->second;
835 
836  if (!g_Renderer.m_SkipSubmit)
837  {
838  // Don't use glMultiDrawElements here since it doesn't have a significant
839  // performance impact and it suffers from various driver bugs (e.g. it breaks
840  // in Mesa 7.10 swrast with index VBOs)
841  for (size_t i = 0; i < batch.first.size(); ++i)
842  glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
843  }
844 
845  g_Renderer.m_Stats.m_DrawCalls++;
846  g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
847  }
848  }
849 
850  if (!isDummyShader)
851  techBase->EndPass();
852  }
853  }
854 
855 #if !CONFIG2_GLES
856  if (isDummyShader)
857  {
858  glMatrixMode(GL_TEXTURE);
859  glLoadIdentity();
860  glMatrixMode(GL_MODELVIEW);
861  }
862 #endif
863 
865 }
866 
867 /**
868  * Helper structure for RenderBlends.
869  */
871 {
873  m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
874  {
875  }
876 
878  VertexBufferBatches m_Batches;
879 };
880 
881 /**
882  * Helper structure for RenderBlends.
883  */
885 {
887  const std::vector<CPatchRData::SSplat>& s, Allocators::DynamicArena& arena) :
888  vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
889  {
890  }
891 
892  typedef std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Allocators::DynamicArena > > SplatStack;
896 };
897 
898 void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
899  ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
900 {
901  Allocators::DynamicArena arena(1 * MiB);
902 
903  typedef std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Allocators::DynamicArena > > BatchesStack;
904  BatchesStack batches((BatchesStack::allocator_type(arena)));
905 
906  CShaderDefines contextBlend = context;
907  contextBlend.Add(str_BLEND, str_1);
908 
909  PROFILE_START("compute batches");
910 
911  // Reserve an arbitrary size that's probably big enough in most cases,
912  // to avoid heavy reallocations
913  batches.reserve(256);
914 
915  typedef std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Allocators::DynamicArena > > BlendStacks;
916  BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
917  blendStacks.reserve(patches.size());
918 
919  // Extract all the blend splats from each patch
920  for (size_t i = 0; i < patches.size(); ++i)
921  {
922  CPatchRData* patch = patches[i];
923  if (!patch->m_BlendSplats.empty())
924  {
925 
926  blendStacks.push_back(SBlendStackItem(patch->m_VBBlends, patch->m_VBBlendIndices, patch->m_BlendSplats, arena));
927  // Reverse the splats so the first to be rendered is at the back of the list
928  std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
929  }
930  }
931 
932  // Rearrange the collection of splats to be grouped by texture, preserving
933  // order of splats within each patch:
934  // (This is exactly the same algorithm used in CPatchRData::BuildBlends,
935  // but applied to patch-sized splats rather than to tile-sized splats;
936  // see that function for comments on the algorithm.)
937  while (true)
938  {
939  if (!batches.empty())
940  {
941  CTerrainTextureEntry* tex = batches.back().m_Texture;
942 
943  for (size_t k = 0; k < blendStacks.size(); ++k)
944  {
945  SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
946  if (!splats.empty() && splats.back().m_Texture == tex)
947  {
948  CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
949  CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
950 
951  BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, arena), indices->m_Owner, arena);
952  batch.first.push_back(splats.back().m_IndexCount);
953 
954  u8* indexBase = indices->m_Owner->GetBindAddress();
955  batch.second.push_back(indexBase + sizeof(u16)*(indices->m_Index + splats.back().m_IndexStart));
956 
957  splats.pop_back();
958  }
959  }
960  }
961 
962  CTerrainTextureEntry* bestTex = NULL;
963  size_t bestStackSize = 0;
964 
965  for (size_t k = 0; k < blendStacks.size(); ++k)
966  {
967  SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
968  if (splats.size() > bestStackSize)
969  {
970  bestStackSize = splats.size();
971  bestTex = splats.back().m_Texture;
972  }
973  }
974 
975  if (bestStackSize == 0)
976  break;
977 
978  SBlendBatch layer(arena);
979  layer.m_Texture = bestTex;
980  batches.push_back(layer);
981  }
982 
983  PROFILE_END("compute batches");
984 
985  CVertexBuffer* lastVB = NULL;
986 
987  for (BatchesStack::iterator itt = batches.begin(); itt != batches.end(); ++itt)
988  {
989  if (itt->m_Texture->GetMaterial().GetSamplers().size() == 0)
990  continue;
991 
992  int numPasses = 1;
993  CShaderTechniquePtr techBase;
994 
995  if (!isDummyShader)
996  {
997  techBase = g_Renderer.GetShaderManager().LoadEffect(itt->m_Texture->GetMaterial().GetShaderEffect(), contextBlend, itt->m_Texture->GetMaterial().GetShaderDefines(0));
998 
999  numPasses = techBase->GetNumPasses();
1000  }
1001 
1002  CShaderProgramPtr previousShader;
1003  for (int pass = 0; pass < numPasses; ++pass)
1004  {
1005  if (!isDummyShader)
1006  {
1007  techBase->BeginPass(pass);
1008  TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
1009 
1010  glEnable(GL_BLEND);
1011  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1012  }
1013 
1014  const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
1015 
1016  if (itt->m_Texture)
1017  {
1018  const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
1019  size_t samplersNum = samplers.size();
1020 
1021  for (size_t s = 0; s < samplersNum; ++s)
1022  {
1023  const CMaterial::TextureSampler& samp = samplers[s];
1024  shader->BindTexture(samp.Name, samp.Sampler);
1025  }
1026 
1027  shader->BindTexture(str_blendTex, itt->m_Texture->m_TerrainAlpha->second.m_hCompositeAlphaMap);
1028 
1029  itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(shader);
1030 
1031 #if !CONFIG2_GLES
1032  if (isDummyShader)
1033  {
1034  pglClientActiveTextureARB(GL_TEXTURE0);
1035  glMatrixMode(GL_TEXTURE);
1036  glLoadMatrixf(itt->m_Texture->GetTextureMatrix());
1037  glMatrixMode(GL_MODELVIEW);
1038  }
1039  else
1040 #endif
1041  {
1042  float c = itt->m_Texture->GetTextureMatrix()[0];
1043  float ms = itt->m_Texture->GetTextureMatrix()[8];
1044  shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
1045  }
1046  }
1047  else
1048  {
1049  shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
1050  }
1051 
1052  for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
1053  {
1054  // Rebind the VB only if it changed since the last batch
1055  if (itv->first != lastVB || shader != previousShader)
1056  {
1057  lastVB = itv->first;
1058  previousShader = shader;
1059  GLsizei stride = sizeof(SBlendVertex);
1060  SBlendVertex *base = (SBlendVertex *)itv->first->Bind();
1061 
1062  shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
1063  shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
1064  shader->NormalPointer(GL_FLOAT, stride, &base->m_Normal[0]);
1065  shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position[0]);
1066  shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, &base->m_AlphaUVs[0]);
1067  }
1068 
1069  shader->AssertPointersBound();
1070 
1071  for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1072  {
1073  it->first->Bind();
1074 
1075  BatchElements& batch = it->second;
1076 
1077  if (!g_Renderer.m_SkipSubmit)
1078  {
1079  for (size_t i = 0; i < batch.first.size(); ++i)
1080  glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1081  }
1082 
1083  g_Renderer.m_Stats.m_DrawCalls++;
1084  g_Renderer.m_Stats.m_BlendSplats++;
1085  g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1086  }
1087  }
1088 
1089  if (!isDummyShader)
1090  {
1091  glDisable(GL_BLEND);
1092  techBase->EndPass();
1093  }
1094  }
1095  }
1096 
1097 #if !CONFIG2_GLES
1098  if (isDummyShader)
1099  {
1100  pglClientActiveTextureARB(GL_TEXTURE0);
1101  glMatrixMode(GL_TEXTURE);
1102  glLoadIdentity();
1103  glMatrixMode(GL_MODELVIEW);
1104  }
1105 #endif
1106 
1108 }
1109 
1110 void CPatchRData::RenderStreams(const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader, int streamflags)
1111 {
1112  // Each batch has a list of index counts, and a list of pointers-to-first-indexes
1113  typedef std::pair<std::vector<GLint>, std::vector<void*> > BatchElements;
1114 
1115  // Group batches by index buffer
1116  typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
1117 
1118  // Group batches by vertex buffer
1119  typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
1120 
1121  VertexBufferBatches batches;
1122 
1123  PROFILE_START("compute batches");
1124 
1125  // Collect all the patches into their appropriate batches
1126  for (size_t i = 0; i < patches.size(); ++i)
1127  {
1128  CPatchRData* patch = patches[i];
1129  BatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
1130 
1131  batch.first.push_back(patch->m_VBBaseIndices->m_Count);
1132 
1133  u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
1134  batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index));
1135  }
1136 
1137  PROFILE_END("compute batches");
1138 
1140 
1141  // Render each batch
1142  for (VertexBufferBatches::iterator itv = batches.begin(); itv != batches.end(); ++itv)
1143  {
1144  GLsizei stride = sizeof(SBaseVertex);
1145  SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
1146 
1147  shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
1148  if (streamflags & STREAM_POSTOUV0)
1149  shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position);
1150  if (streamflags & STREAM_POSTOUV1)
1151  shader->TexCoordPointer(GL_TEXTURE1, 3, GL_FLOAT, stride, &base->m_Position);
1152  if (streamflags & STREAM_COLOR)
1153  shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
1154 
1155  shader->AssertPointersBound();
1156 
1157  for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1158  {
1159  it->first->Bind();
1160 
1161  BatchElements& batch = it->second;
1162 
1163  if (!g_Renderer.m_SkipSubmit)
1164  {
1165  for (size_t i = 0; i < batch.first.size(); ++i)
1166  glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1167  }
1168 
1169  g_Renderer.m_Stats.m_DrawCalls++;
1170  g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1171  }
1172  }
1173 
1175 }
1176 
1178 {
1179  CTerrain* terrain = m_Patch->m_Parent;
1180  ssize_t gx = m_Patch->m_X * PATCH_SIZE;
1181  ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
1182 
1183  CVector3D pos;
1184  std::vector<CVector3D> line;
1185 
1186  ssize_t i, j;
1187 
1188  for (i = 0, j = 0; i <= PATCH_SIZE; ++i)
1189  {
1190  terrain->CalcPosition(gx + i, gz + j, pos);
1191  line.push_back(pos);
1192  }
1193  for (i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1194  {
1195  terrain->CalcPosition(gx + i, gz + j, pos);
1196  line.push_back(pos);
1197  }
1198  for (i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1199  {
1200  terrain->CalcPosition(gx + i, gz + j, pos);
1201  line.push_back(pos);
1202  }
1203  for (i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1204  {
1205  terrain->CalcPosition(gx + i, gz + j, pos);
1206  line.push_back(pos);
1207  }
1208 
1209 #if CONFIG2_GLES
1210 #warning TODO: implement CPatchRData::RenderOutlines for GLES
1211 #else
1212  glVertexPointer(3, GL_FLOAT, sizeof(CVector3D), &line[0]);
1213  glDrawArrays(GL_LINE_STRIP, 0, line.size());
1214 #endif
1215 }
1216 
1218 {
1219  ENSURE(m_UpdateFlags==0);
1220 
1221  if (!m_VBSides)
1222  return;
1223 
1224  SSideVertex *base = (SSideVertex *)m_VBSides->m_Owner->Bind();
1225 
1226  // setup data pointers
1227  GLsizei stride = sizeof(SSideVertex);
1228  shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
1229 
1230  shader->AssertPointersBound();
1231 
1232  if (!g_Renderer.m_SkipSubmit)
1233  glDrawArrays(GL_TRIANGLE_STRIP, m_VBSides->m_Index, (GLsizei)m_VBSides->m_Count);
1234 
1235  // bump stats
1236  g_Renderer.m_Stats.m_DrawCalls++;
1237  g_Renderer.m_Stats.m_TerrainTris += m_VBSides->m_Count - 2;
1238 
1240 }
1241 
1243 {
1244  CTerrain* terrain = m_Patch->m_Parent;
1245  CCamera* camera = g_Game->GetView()->GetCamera();
1246 
1247  for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1248  {
1249  for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1250  {
1251  ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1252  ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1253 
1254  CVector3D pos;
1255  terrain->CalcPosition(gx, gz, pos);
1256 
1257  // Move a bit towards the center of the tile
1258  pos.X += TERRAIN_TILE_SIZE/4.f;
1259  pos.Z += TERRAIN_TILE_SIZE/4.f;
1260 
1261  float x, y;
1262  camera->GetScreenCoordinates(pos, x, y);
1263 
1264  textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1265  }
1266  }
1267 }
1268 
1269 //
1270 // Water build and rendering
1271 //
1272 
1273 // Build vertex buffer for water vertices over our patch
1275 {
1276  PROFILE3("build water");
1277 
1278  // number of vertices in each direction in each patch
1279  ENSURE((PATCH_SIZE % water_cell_size) == 0);
1280 
1281  if (m_VBWater)
1282  {
1284  m_VBWater = 0;
1285  }
1286  if (m_VBWaterIndices)
1287  {
1289  m_VBWaterIndices = 0;
1290  }
1292 
1293  // We need to use this to access the water manager or we may not have the
1294  // actual values but some compiled-in defaults
1296  if (!cmpWaterManager)
1297  return;
1298 
1299  // Build data for water
1300  std::vector<SWaterVertex> water_vertex_data;
1301  std::vector<GLushort> water_indices;
1302  u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1303  memset(water_index_map, 0xFF, sizeof(water_index_map));
1304 
1305  // TODO: This is not (yet) exported via the ICmp interface so... we stick to these values which can be compiled in defaults
1306  WaterManager* WaterMgr = g_Renderer.GetWaterManager();
1307 
1308  if (WaterMgr->m_NeedInfoUpdate)
1309  {
1310  WaterMgr->m_NeedInfoUpdate = false;
1312  }
1313  CPatch* patch = m_Patch;
1314  CTerrain* terrain = patch->m_Parent;
1315 
1316  ssize_t mapSize = (size_t)terrain->GetVerticesPerSide();
1317 
1318  ssize_t x1 = m_Patch->m_X*PATCH_SIZE;
1319  ssize_t z1 = m_Patch->m_Z*PATCH_SIZE;
1320 
1321  // build vertices, uv, and shader varying
1322  for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
1323  {
1324  for (ssize_t x = 0; x <= PATCH_SIZE; x += water_cell_size)
1325  {
1326  // Check that the edge at x is partially underwater
1327  float startTerrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
1328  float startWaterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
1329  if (startTerrainHeight[0] >= startWaterHeight[0] && startTerrainHeight[1] >= startWaterHeight[1])
1330  continue;
1331 
1332  // Move x back one cell (unless at start of patch), then scan rightwards
1333  bool belowWater = true;
1334  ssize_t stripStart;
1335  for (stripStart = x = std::max(x-water_cell_size, (ssize_t)0); x <= PATCH_SIZE; x += water_cell_size)
1336  {
1337  // If this edge is not underwater, and neither is the previous edge
1338  // (i.e. belowWater == false), then stop this strip since we've reached
1339  // a cell that's entirely above water
1340  float terrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
1341  float waterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
1342  if (terrainHeight[0] >= waterHeight[0] && terrainHeight[1] >= waterHeight[1])
1343  {
1344  if (!belowWater)
1345  break;
1346  belowWater = false;
1347  }
1348  else
1349  belowWater = true;
1350 
1351  // Edge (x,z)-(x,z+1) is at least partially underwater, so extend the water plane strip across it
1352 
1353  // Compute vertex data for the 2 points on the edge
1354  for (int j = 0; j < 2; j++)
1355  {
1356  // Check if we already computed this vertex from an earlier strip
1357  if (water_index_map[z+j*water_cell_size][x] != 0xFFFF)
1358  continue;
1359 
1360  SWaterVertex vertex;
1361 
1362  terrain->CalcPosition(x+x1, z+z1 + j*water_cell_size, vertex.m_Position);
1363  float depth = waterHeight[j] - vertex.m_Position.Y;
1364  vertex.m_Position.Y = waterHeight[j];
1365  m_WaterBounds += vertex.m_Position;
1366 
1367  // NB: Usually this factor is view dependent, but for performance reasons
1368  // we do not take it into account with basic non-shader based water.
1369  // Average constant Fresnel effect for non-fancy water
1370  float alpha = clamp(depth / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterMaxAlpha);
1371 
1372  // Split the depth data across 24 bits, so the fancy-water shader can reconstruct
1373  // the depth value while the simple-water can just use the precomputed alpha
1374  float depthInt = floor(depth);
1375  float depthFrac = depth - depthInt;
1376  vertex.m_DepthData = SColor4ub(
1377  u8(clamp(depthInt, 0.0f, 255.0f)),
1378  u8(clamp(-depthInt, 0.0f, 255.0f)),
1379  u8(clamp(depthFrac*255.0f, 0.0f, 255.0f)),
1380  u8(clamp(alpha*255.0f, 0.0f, 255.0f)));
1381 
1382  int tx = x+x1;
1383  int ty = z+z1 + j*water_cell_size;
1384 
1385  vertex.m_WaterData = CVector4D(WaterMgr->m_WaveX[tx + ty*mapSize],
1386  WaterMgr->m_WaveZ[tx + ty*mapSize],
1387  WaterMgr->m_DistanceToShore[tx + ty*mapSize],
1388  WaterMgr->m_FoamFactor[tx + ty*mapSize]);
1389 
1390  water_index_map[z+j*water_cell_size][x] = water_vertex_data.size();
1391  water_vertex_data.push_back(vertex);
1392  }
1393 
1394  // If this was not the first x in the strip, then add a quad
1395  // using the computed vertex data
1396 
1397  if (x <= stripStart)
1398  continue;
1399 
1400  water_indices.push_back(water_index_map[z + water_cell_size][x - water_cell_size]);
1401  water_indices.push_back(water_index_map[z][x - water_cell_size]);
1402  water_indices.push_back(water_index_map[z][x]);
1403  water_indices.push_back(water_index_map[z + water_cell_size][x]);
1404  }
1405  }
1406  }
1407 
1408  // no vertex buffers if no data generated
1409  if (water_indices.size() == 0)
1410  return;
1411 
1412  // allocate vertex buffer
1413  m_VBWater = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
1414  m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater, &water_vertex_data[0]);
1415 
1416  // Construct indices buffer
1417  m_VBWaterIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
1419 }
1420 
1422 {
1423  ASSERT(m_UpdateFlags==0);
1424 
1425  if (!m_VBWater)
1426  return;
1427 
1429 
1430  // setup data pointers
1431  GLsizei stride = sizeof(SWaterVertex);
1432  shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base[m_VBWater->m_Index].m_DepthData);
1433  shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWater->m_Index].m_Position);
1434  shader->TexCoordPointer(GL_TEXTURE0, 4, GL_FLOAT, stride, &base[m_VBWater->m_Index].m_WaterData);
1435 
1436  shader->AssertPointersBound();
1437 
1438  // render
1439  if (!g_Renderer.m_SkipSubmit)
1440  {
1441  u8* indexBase = m_VBWaterIndices->m_Owner->Bind();
1442 #if CONFIG2_GLES
1443 #warning TODO: fix CPatchRData::RenderWater for GLES (avoid GL_QUADS)
1444 #else
1445  glDrawElements(GL_QUADS, (GLsizei) m_VBWaterIndices->m_Count,
1446  GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndices->m_Index));
1447 #endif
1448  }
1449 
1450  // bump stats
1451  g_Renderer.m_Stats.m_DrawCalls++;
1452  g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 2;
1453 
1455 }
#define BLENDMAP_ROTATE90
#define BLENDMAP_FLIPU
static const ssize_t water_cell_size
Definition: PatchRData.h:171
void BuildWater()
void Add(CStrIntern name, CStrIntern value)
Add a name and associated value to the map of defines.
#define u8
Definition: types.h:39
size_t m_Index
Start index of this chunk in owner.
Definition: VertexBuffer.h:52
CTerrainTextureEntry * GetTextureEntry()
Definition: MiniPatch.h:42
friend struct SBlendStackItem
Definition: PatchRData.h:63
void PrintfAt(float x, float y, const wchar_t *fmt,...)
Print formatted text at (x,y) under the current transform.
bool m_NeedInfoUpdate
Definition: WaterManager.h:88
#define BLENDMAP_FLIPV
void RenderWater(CShaderProgramPtr &shader)
std::vector< CPatchRData::SSplat, ProxyAllocator< CPatchRData::SSplat, Allocators::DynamicArena > > SplatStack
Definition: PatchRData.cpp:892
CBoundingBoxAligned m_WaterBounds
Definition: PatchRData.h:154
float m_WaterMaxAlpha
Definition: WaterManager.h:92
VertexBufferBatches m_Batches
Definition: PatchRData.cpp:878
const ssize_t TERRAIN_TILE_SIZE
metres [world space units] per tile in x and z
Definition: Terrain.h:40
#define LOGERROR
Definition: CLogger.h:35
void BuildBlends()
Definition: PatchRData.cpp:143
CTerrainTextureEntry * m_Texture
Definition: PatchRData.cpp:139
int GetPriority()
Definition: MiniPatch.h:43
void BuildVertices()
Definition: PatchRData.cpp:522
CVertexBufferManager g_VBMan
CTerrainTextureEntry * m_Texture
Definition: PatchRData.cpp:92
Represents the ordered collection of blends drawn on a particular tile.
Definition: PatchRData.cpp:122
static void swap(UniqueRange &p1, UniqueRange &p2)
Definition: unique_range.h:176
SColor4ub EvaluateTerrainDiffuseScaled(const CVector3D &normal) const
Compute the diffuse sun lighting color on terrain, for rendering with CPU lighting.
Definition: LightEnv.h:117
void CalcPosition(ssize_t i, ssize_t j, CVector3D &pos) const
Definition: Terrain.cpp:118
ssize_t GetVerticesPerSide() const
Definition: Terrain.h:65
bool GetTriangulationDir(ssize_t i, ssize_t j) const
Definition: Terrain.cpp:427
std::vector< STileBlend > blends
Definition: PatchRData.cpp:125
M::mapped_type & PooledMapGet(M &m, const typename M::key_type &k, Allocators::DynamicArena &arena)
Definition: PatchRData.cpp:692
bool operator()(const STileBlend &a, const STileBlend &b) const
Definition: PatchRData.cpp:98
shared_ptr< CShaderTechnique > CShaderTechniquePtr
Represents a batched collection of blends using the same texture.
Definition: PatchRData.cpp:131
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:310
CPatchRData(CPatch *patch, CSimulation2 *simulation)
Definition: PatchRData.cpp:62
Class ShadowMap: Maintain the shadow map texture and perform necessary OpenGL setup, including matrix calculations.
Definition: ShadowMap.h:39
#define PROFILE_END(name)
Definition: Profile.h:198
Represents a blend for a single tile, texture and shape.
Definition: PatchRData.cpp:90
u8 * Bind()
Bind to this buffer; return pointer to address required as parameter to glVertexPointer ( + etc) call...
CVertexBuffer::VBChunk * m_VBWaterIndices
Definition: PatchRData.h:160
VBChunk: describes a portion of this vertex buffer.
Definition: VertexBuffer.h:47
void Build()
Definition: PatchRData.cpp:653
CVertexBuffer::VBChunk * m_VBBlends
Definition: PatchRData.h:142
CPatchSideFlags
Definition: Patch.h:38
void AddBlend(std::vector< SBlendVertex > &blendVertices, std::vector< u16 > &blendIndices, u16 i, u16 j, u8 shape, CTerrainTextureEntry *texture)
Definition: PatchRData.cpp:314
float * m_WaveX
Definition: WaterManager.h:56
std::pair< std::vector< GLint, ProxyAllocator< GLint, Allocators::DynamicArena > >, std::vector< void *, ProxyAllocator< void *, Allocators::DynamicArena > > > BatchElements
Definition: PatchRData.cpp:710
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton &#39;system&#39; components.
Definition: Entity.h:44
Public API for simulation system.
Definition: Simulation2.h:46
CVertexBuffer: encapsulation of ARB_vertex_buffer_object, also supplying some additional functionalit...
Definition: VertexBuffer.h:40
u8 * GetBindAddress()
Get the address that Bind() will return, without actually binding.
#define g_Renderer
Definition: Renderer.h:61
float * m_FoamFactor
Definition: WaterManager.h:59
void CalcNormal(ssize_t i, ssize_t j, CVector3D &normal) const
Definition: Terrain.cpp:144
CVertexBuffer::VBChunk * indices
Definition: PatchRData.cpp:894
void BuildSide(std::vector< SSideVertex > &vertices, CPatchSideFlags side)
Definition: PatchRData.cpp:572
float m_WaterAlphaOffset
Definition: WaterManager.h:94
u16 m_TileMask
Definition: PatchRData.cpp:94
std::vector< SSplat > m_BlendSplats
Definition: PatchRData.h:151
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
#define POOLED_BATCH_MAP(Key, Value)
Definition: PatchRData.cpp:686
M::mapped_type & PooledPairGet(M &m, const typename M::key_type &k, Allocators::DynamicArena &arena)
Definition: PatchRData.cpp:701
CVertexBuffer::VBChunk * Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target)
Try to allocate a vertex buffer of the given size and type.
int m_Priority
Definition: PatchRData.cpp:93
Helper structure for RenderBlends.
Definition: PatchRData.cpp:870
std::vector< Tile > m_Tiles
Definition: PatchRData.cpp:140
float X
Definition: Vector3D.h:31
void Update(CSimulation2 *simulation)
Definition: PatchRData.cpp:662
float Y
Definition: Vector3D.h:31
int m_X
Definition: Patch.h:68
CMiniPatch * GetTile(ssize_t i, ssize_t j) const
Definition: Terrain.cpp:298
static const size_t MiB
Definition: alignment.h:72
Definition: Camera.h:39
int Priority
Definition: MiniPatch.h:40
virtual float GetExactWaterLevel(float x, float z)=0
Get the current water level at the given point.
CCamera * GetCamera()
Definition: GameView.cpp:390
SBlendBatch(Allocators::DynamicArena &arena)
Definition: PatchRData.cpp:872
Definition: Patch.h:48
#define PROFILE_START(name)
Definition: Profile.h:197
CGame * g_Game
Globally accessible pointer to the CGame object.
Definition: Game.cpp:56
void UpdateChunkVertices(VBChunk *chunk, void *data)
Update vertex data for given chunk. Transfers the provided data to the actual OpenGL vertex buffer...
void GetScreenCoordinates(const CVector3D &world, float &x, float &y) const
Definition: Camera.cpp:192
CTerrainTextureEntry * m_Texture
Definition: PatchRData.h:69
void RenderSides(CShaderProgramPtr &shader)
const ssize_t BlendOffsets[9][2]
Definition: PatchRData.cpp:48
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
size_t m_Count
Number of vertices used by chunk.
Definition: VertexBuffer.h:54
intptr_t ssize_t
Definition: wposix_types.h:82
static void RenderBlends(const std::vector< CPatchRData * > &patches, const CShaderDefines &context, ShadowMap *shadow, bool isDummyShader=false, const CShaderProgramPtr &dummy=CShaderProgramPtr())
Definition: PatchRData.cpp:898
Represents a mapping of name strings to value strings, for use with #if and #ifdef and similar condit...
int Calculate(BlendShape8 shape, unsigned int &flags)
int GetSideFlags()
Definition: Patch.cpp:78
#define u16
Definition: types.h:40
void CreateSuperfancyInfo(CSimulation2 *simulation)
CreateSuperfancyInfo: creates textures and wave vertices for superfancy water.
void BuildIndices()
Definition: PatchRData.cpp:428
CGameView * GetView()
Get the pointer to the game view object.
Definition: Game.h:128
void Release(CVertexBuffer::VBChunk *chunk)
Returns the given chunk to its owning buffer.
CVertexBuffer::VBChunk * vertices
Definition: PatchRData.cpp:893
SBlendStackItem(CVertexBuffer::VBChunk *v, CVertexBuffer::VBChunk *i, const std::vector< CPatchRData::SSplat > &s, Allocators::DynamicArena &arena)
Definition: PatchRData.cpp:886
CVertexBuffer::VBChunk * m_VBBase
Definition: PatchRData.h:136
float * m_WaveZ
Definition: WaterManager.h:57
CTerrainTextureEntry * m_Texture
Definition: PatchRData.cpp:877
static void PrepareShader(const CShaderProgramPtr &shader, ShadowMap *shadow)
Set up all the uniforms for a shader pass.
#define PROFILE3(name)
Definition: Profile.h:201
CTerrain * m_Parent
Definition: Patch.h:70
int m_Z
Definition: Patch.h:68
CVertexBuffer::VBChunk * m_VBBlendIndices
Definition: PatchRData.h:145
std::vector< SSplat > m_Splats
Definition: PatchRData.h:148
void RenderPriorities(CTextRenderer &textRenderer)
CVertexBuffer * m_Owner
Owning (parent) vertex buffer.
Definition: VertexBuffer.h:50
float GetVertexGroundLevel(ssize_t i, ssize_t j) const
Definition: Terrain.cpp:308
CVertexBuffer::VBChunk * m_VBSides
Definition: PatchRData.h:133
void RenderOutline()
CSimulation2 * m_Simulation
Definition: PatchRData.h:162
std::vector< TextureSampler > SamplersVector
Definition: Material.h:40
CMiniPatch m_MiniPatches[PATCH_SIZE][PATCH_SIZE]
Definition: Patch.h:66
allocator design parameters:
Definition: arena.h:93
#define BLENDMAP_ROTATE270
void BuildSides()
Definition: PatchRData.cpp:618
float Z
Definition: Vector3D.h:31
CVertexBuffer::VBChunk * m_VBWater
Definition: PatchRData.h:157
SColor4ub EvaluateTerrainDiffuseFactor(const CVector3D &normal) const
Compute the diffuse sun lighting factor on terrain, for rendering with shader lighting.
Definition: LightEnv.h:128
SplatStack splats
Definition: PatchRData.cpp:895
#define BLENDMAP_ROTATE180
static void Unbind()
Unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it...
CVertexBuffer::VBChunk * m_VBBaseIndices
Definition: PatchRData.h:139
Class CLightEnv: description of a lighting environment - contains all the necessary parameters for re...
Definition: LightEnv.h:36
float m_WaterFullDepth
Definition: WaterManager.h:93
float * m_DistanceToShore
Definition: WaterManager.h:58
static void RenderStreams(const std::vector< CPatchRData * > &patches, const CShaderProgramPtr &shader, int streamflags)
Helper structure for RenderBlends.
Definition: PatchRData.cpp:884
shared_ptr< CShaderProgram > CShaderProgramPtr
const ssize_t PATCH_SIZE
Definition: Patch.h:34
CTerrainTextureManager::TerrainAlphaMap::iterator m_TerrainAlpha
CPatch * m_Patch
Definition: PatchRData.h:130
T clamp(T value, T min, T max)
Definition: MathUtil.h:32
bool operator()(const STileBlend &a) const
Definition: PatchRData.cpp:112
Class WaterManager: Maintain water settings and textures.
Definition: WaterManager.h:49