1 /* Copyright (C) 2017 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"
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"
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
CPatchRData(CPatch * patch,CSimulation2 * simulation)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)
69 {
70 ENSURE(patch);
71 Build();
72 }
73
74 ///////////////////////////////////////////////////////////////////
75 // CPatchRData destructor
~CPatchRData()76 CPatchRData::~CPatchRData()
77 {
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);
88 }
89
90 /**
91 * Represents a blend for a single tile, texture and shape.
92 */
93 struct STileBlend
94 {
95 CTerrainTextureEntry* m_Texture;
96 int m_Priority;
97 u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
98
99 struct DecreasingPriority
100 {
operator ()STileBlend::DecreasingPriority101 bool operator()(const STileBlend& a, const STileBlend& b) const
102 {
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;
110 }
111 };
112
113 struct CurrentTile
114 {
operator ()STileBlend::CurrentTile115 bool operator()(const STileBlend& a) const
116 {
117 return (a.m_TileMask & (1 << 8)) != 0;
118 }
119 };
120 };
121
122 /**
123 * Represents the ordered collection of blends drawn on a particular tile.
124 */
125 struct STileBlendStack
126 {
127 u8 i, j;
128 std::vector<STileBlend> blends; // back of vector is lowest-priority texture
129 };
130
131 /**
132 * Represents a batched collection of blends using the same texture.
133 */
134 struct SBlendLayer
135 {
136 struct Tile
137 {
138 u8 i, j;
139 u8 shape;
140 };
141
142 CTerrainTextureEntry* m_Texture;
143 std::vector<Tile> m_Tiles;
144 };
145
BuildBlends()146 void CPatchRData::BuildBlends()
147 {
148 PROFILE3("build blends");
149
150 m_BlendSplats.clear();
151
152 std::vector<SBlendVertex> blendVertices;
153 std::vector<u16> blendIndices;
154
155 CTerrain* terrain = m_Patch->m_Parent;
156
157 std::vector<STileBlendStack> blendStacks;
158 blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
159
160 // For each tile in patch ..
161 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
162 {
163 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
164 {
165 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
166 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
167
168 std::vector<STileBlend> blends;
169 blends.reserve(9);
170
171 // Compute a blend for every tile in the 3x3 square around this tile
172 for (size_t n = 0; n < 9; ++n)
173 {
174 ssize_t ox = gx + BlendOffsets[n][1];
175 ssize_t oz = gz + BlendOffsets[n][0];
176
177 CMiniPatch* nmp = terrain->GetTile(ox, oz);
178 if (!nmp)
179 continue;
180
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);
186 }
187
188 // Sort the blends, highest priority first
189 std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
190
191 STileBlendStack blendStack;
192 blendStack.i = i;
193 blendStack.j = j;
194
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)
197 {
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]);
202 }
203
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());
210
211 blendStacks.push_back(blendStack);
212 }
213 }
214
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.)
219
220 std::vector<SBlendLayer> blendLayers;
221
222 while (true)
223 {
224 if (!blendLayers.empty())
225 {
226 // Try to grab as many tiles as possible that match our current layer,
227 // from off the blend stacks of all the tiles
228
229 CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
230
231 for (size_t k = 0; k < blendStacks.size(); ++k)
232 {
233 if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
234 {
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();
238 }
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)
241 }
242 }
243
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;
249
250 for (size_t k = 0; k < blendStacks.size(); ++k)
251 {
252 if (blendStacks[k].blends.size() > bestStackSize)
253 {
254 bestStackSize = blendStacks[k].blends.size();
255 bestTex = blendStacks[k].blends.back().m_Texture;
256 }
257 }
258
259 // If all our stacks were empty, we're done
260 if (bestStackSize == 0)
261 break;
262
263 // Otherwise add the new layer, then loop back and start filling it in
264
265 SBlendLayer layer;
266 layer.m_Texture = bestTex;
267 blendLayers.push_back(layer);
268 }
269
270 // Now build outgoing splats
271 m_BlendSplats.resize(blendLayers.size());
272
273 for (size_t k = 0; k < blendLayers.size(); ++k)
274 {
275 SSplat& splat = m_BlendSplats[k];
276 splat.m_IndexStart = blendIndices.size();
277 splat.m_Texture = blendLayers[k].m_Texture;
278
279 for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
280 {
281 SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
282 AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
283 }
284
285 splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
286 }
287
288 // Release existing vertex buffer chunks
289 if (m_VBBlends)
290 {
291 g_VBMan.Release(m_VBBlends);
292 m_VBBlends = 0;
293 }
294
295 if (m_VBBlendIndices)
296 {
297 g_VBMan.Release(m_VBBlendIndices);
298 m_VBBlendIndices = 0;
299 }
300
301 if (blendVertices.size())
302 {
303 // Construct vertex buffer
304
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]);
307
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;
311
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]);
314 }
315 }
316
AddBlend(std::vector<SBlendVertex> & blendVertices,std::vector<u16> & blendIndices,u16 i,u16 j,u8 shape,CTerrainTextureEntry * texture)317 void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
318 u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
319 {
320 CTerrain* terrain = m_Patch->m_Parent;
321
322 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
323 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
324
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;
329
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);
333
334 // now actually render the blend tile (if we need one)
335 if (alphamap == -1)
336 return;
337
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;
342
343 if (alphamapflags & BLENDMAP_FLIPU)
344 std::swap(u0, u1);
345
346 if (alphamapflags & BLENDMAP_FLIPV)
347 std::swap(v0, v1);
348
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;
356
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;
366
367 SBlendVertex dst;
368
369 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
370 CVector3D normal;
371
372 bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
373
374 size_t index = blendVertices.size();
375
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);
383
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);
391
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);
399
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);
407
408 bool dir = terrain->GetTriangulationDir(gx, gz);
409 if (dir)
410 {
411 blendIndices.push_back(index+0);
412 blendIndices.push_back(index+1);
413 blendIndices.push_back(index+3);
414
415 blendIndices.push_back(index+1);
416 blendIndices.push_back(index+2);
417 blendIndices.push_back(index+3);
418 }
419 else
420 {
421 blendIndices.push_back(index+0);
422 blendIndices.push_back(index+1);
423 blendIndices.push_back(index+2);
424
425 blendIndices.push_back(index+2);
426 blendIndices.push_back(index+3);
427 blendIndices.push_back(index+0);
428 }
429 }
430
BuildIndices()431 void CPatchRData::BuildIndices()
432 {
433 PROFILE3("build indices");
434
435 CTerrain* terrain = m_Patch->m_Parent;
436
437 ssize_t px = m_Patch->m_X * PATCH_SIZE;
438 ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
439
440 // must have allocated some vertices before trying to build corresponding indices
441 ENSURE(m_VBBase);
442
443 // number of vertices in each direction in each patch
444 ssize_t vsize=PATCH_SIZE+1;
445
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);
448
449 std::vector<unsigned short> indices;
450 indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
451
452 // release existing splats
453 m_Splats.clear();
454
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);
464 }
465 }
466 }
467
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;
472
473 for (size_t i=0;i<m_Splats.size();i++) {
474 CTerrainTextureEntry* tex=textures[i];
475
476 SSplat& splat=m_Splats[i];
477 splat.m_Texture=tex;
478 splat.m_IndexStart=indices.size();
479
480 for (ssize_t j = 0; j < PATCH_SIZE; j++)
481 {
482 for (ssize_t i = 0; i < PATCH_SIZE; i++)
483 {
484 if (texgrid[j][i] == tex)
485 {
486 bool dir = terrain->GetTriangulationDir(px+i, pz+j);
487 if (dir)
488 {
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));
492
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));
496 }
497 else
498 {
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));
502
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));
506 }
507 }
508 }
509 }
510 splat.m_IndexCount=indices.size()-splat.m_IndexStart;
511 }
512
513 // Release existing vertex buffer chunk
514 if (m_VBBaseIndices)
515 {
516 g_VBMan.Release(m_VBBaseIndices);
517 m_VBBaseIndices = 0;
518 }
519
520 ENSURE(indices.size());
521
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]);
525 }
526
527
BuildVertices()528 void CPatchRData::BuildVertices()
529 {
530 PROFILE3("build vertices");
531
532 // create both vertices and lighting colors
533
534 // number of vertices in each direction in each patch
535 ssize_t vsize=PATCH_SIZE+1;
536
537 std::vector<SBaseVertex> vertices;
538 vertices.resize(vsize*vsize);
539
540 // get index of this patch
541 ssize_t px=m_Patch->m_X;
542 ssize_t pz=m_Patch->m_Z;
543
544 CTerrain* terrain=m_Patch->m_Parent;
545 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
546
547 bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
548
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;
555
556 // calculate vertex data
557 terrain->CalcPosition(ix,iz,vertices[v].m_Position);
558
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);
564
565 vertices[v].m_Normal = normal;
566
567 vertices[v].m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
568 }
569 }
570
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);
574
575 m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase, &vertices[0]);
576 }
577
BuildSide(std::vector<SSideVertex> & vertices,CPatchSideFlags side)578 void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
579 {
580 ssize_t vsize = PATCH_SIZE + 1;
581 CTerrain* terrain = m_Patch->m_Parent;
582 CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
583
584 for (ssize_t k = 0; k < vsize; k++)
585 {
586 ssize_t gx = m_Patch->m_X * PATCH_SIZE;
587 ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
588 switch (side)
589 {
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;
594 }
595
596 CVector3D pos;
597 terrain->CalcPosition(gx, gz, pos);
598
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);
604
605 SSideVertex v0, v1;
606 v0.m_Position = pos;
607 v1.m_Position = pos;
608 v1.m_Position.Y = 0;
609
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())
613 {
614 vertices.push_back(vertices.back());
615 vertices.push_back(v1);
616 }
617
618 // Now add the new triangles
619 vertices.push_back(v1);
620 vertices.push_back(v0);
621 }
622 }
623
BuildSides()624 void CPatchRData::BuildSides()
625 {
626 PROFILE3("build sides");
627
628 std::vector<SSideVertex> sideVertices;
629
630 int sideFlags = m_Patch->GetSideFlags();
631
632 // If no sides are enabled, we don't need to do anything
633 if (!sideFlags)
634 return;
635
636 // For each side, generate a tristrip by adding a vertex at ground/water
637 // level and a vertex underneath at height 0.
638
639 if (sideFlags & CPATCH_SIDE_NEGX)
640 BuildSide(sideVertices, CPATCH_SIDE_NEGX);
641
642 if (sideFlags & CPATCH_SIDE_POSX)
643 BuildSide(sideVertices, CPATCH_SIDE_POSX);
644
645 if (sideFlags & CPATCH_SIDE_NEGZ)
646 BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
647
648 if (sideFlags & CPATCH_SIDE_POSZ)
649 BuildSide(sideVertices, CPATCH_SIDE_POSZ);
650
651 if (sideVertices.empty())
652 return;
653
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]);
657 }
658
Build()659 void CPatchRData::Build()
660 {
661 BuildVertices();
662 BuildSides();
663 BuildIndices();
664 BuildBlends();
665 BuildWater();
666 }
667
Update(CSimulation2 * simulation)668 void CPatchRData::Update(CSimulation2* simulation)
669 {
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();
680
681 m_UpdateFlags=0;
682 }
683 }
684
685 // Types used for glMultiDrawElements batching:
686
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.)
690
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 > >
694
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>
PooledMapGet(M & m,const typename M::key_type & k,Allocators::DynamicArena & arena)698 typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
699 {
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;
703 }
704
705 // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
706 template<typename M>
PooledPairGet(M & m,const typename M::key_type & k,Allocators::DynamicArena & arena)707 typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
708 {
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;
713 }
714
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;
717
718 // Group batches by index buffer
719 typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
720
721 // Group batches by vertex buffer
722 typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches;
723
724 // Group batches by texture
725 typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches;
726
RenderBases(const std::vector<CPatchRData * > & patches,const CShaderDefines & context,ShadowMap * shadow,bool isDummyShader,const CShaderProgramPtr & dummy)727 void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
728 ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
729 {
730 Allocators::DynamicArena arena(1 * MiB);
731
732 TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(arena)));
733
734 PROFILE_START("compute batches");
735
736 // Collect all the patches' base splats into their appropriate batches
737 for (size_t i = 0; i < patches.size(); ++i)
738 {
739 CPatchRData* patch = patches[i];
740 for (size_t j = 0; j < patch->m_Splats.size(); ++j)
741 {
742 SSplat& splat = patch->m_Splats[j];
743
744 BatchElements& batch = PooledPairGet(
745 PooledMapGet(
746 PooledMapGet(batches, splat.m_Texture, arena),
747 patch->m_VBBase->m_Owner, arena
748 ),
749 patch->m_VBBaseIndices->m_Owner, arena
750 );
751
752 batch.first.push_back(splat.m_IndexCount);
753
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));
756 }
757 }
758
759 PROFILE_END("compute batches");
760
761 // Render each batch
762 for (TextureBatches::iterator itt = batches.begin(); itt != batches.end(); ++itt)
763 {
764 int numPasses = 1;
765
766 CShaderTechniquePtr techBase;
767
768 if (!isDummyShader)
769 {
770 if (itt->first->GetMaterial().GetShaderEffect().length() == 0)
771 {
772 LOGERROR("Terrain renderer failed to load shader effect.\n");
773 continue;
774 }
775
776 techBase = g_Renderer.GetShaderManager().LoadEffect(itt->first->GetMaterial().GetShaderEffect(),
777 context, itt->first->GetMaterial().GetShaderDefines(0));
778
779 numPasses = techBase->GetNumPasses();
780 }
781
782 for (int pass = 0; pass < numPasses; ++pass)
783 {
784 if (!isDummyShader)
785 {
786 techBase->BeginPass(pass);
787 TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
788 }
789
790 const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
791
792 if (itt->first->GetMaterial().GetSamplers().size() != 0)
793 {
794 const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers();
795 size_t samplersNum = samplers.size();
796
797 for (size_t s = 0; s < samplersNum; ++s)
798 {
799 const CMaterial::TextureSampler& samp = samplers[s];
800 shader->BindTexture(samp.Name, samp.Sampler);
801 }
802
803 itt->first->GetMaterial().GetStaticUniforms().BindUniforms(shader);
804
805 #if !CONFIG2_GLES
806 if (isDummyShader)
807 {
808 glMatrixMode(GL_TEXTURE);
809 glLoadMatrixf(itt->first->GetTextureMatrix());
810 glMatrixMode(GL_MODELVIEW);
811 }
812 else
813 #endif
814 {
815 float c = itt->first->GetTextureMatrix()[0];
816 float ms = itt->first->GetTextureMatrix()[8];
817 shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
818 }
819 }
820 else
821 {
822 shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
823 }
824
825 for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
826 {
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]);
833
834 shader->AssertPointersBound();
835
836 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
837 {
838 it->first->Bind();
839
840 BatchElements& batch = it->second;
841
842 if (!g_Renderer.m_SkipSubmit)
843 {
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]);
849 }
850
851 g_Renderer.m_Stats.m_DrawCalls++;
852 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
853 }
854 }
855
856 if (!isDummyShader)
857 techBase->EndPass();
858 }
859 }
860
861 #if !CONFIG2_GLES
862 if (isDummyShader)
863 {
864 glMatrixMode(GL_TEXTURE);
865 glLoadIdentity();
866 glMatrixMode(GL_MODELVIEW);
867 }
868 #endif
869
870 CVertexBuffer::Unbind();
871 }
872
873 /**
874 * Helper structure for RenderBlends.
875 */
876 struct SBlendBatch
877 {
SBlendBatchSBlendBatch878 SBlendBatch(Allocators::DynamicArena& arena) :
879 m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
880 {
881 }
882
883 CTerrainTextureEntry* m_Texture;
884 VertexBufferBatches m_Batches;
885 };
886
887 /**
888 * Helper structure for RenderBlends.
889 */
890 struct SBlendStackItem
891 {
SBlendStackItemSBlendStackItem892 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))
895 {
896 }
897
898 typedef std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Allocators::DynamicArena > > SplatStack;
899 CVertexBuffer::VBChunk* vertices;
900 CVertexBuffer::VBChunk* indices;
901 SplatStack splats;
902 };
903
RenderBlends(const std::vector<CPatchRData * > & patches,const CShaderDefines & context,ShadowMap * shadow,bool isDummyShader,const CShaderProgramPtr & dummy)904 void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
905 ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
906 {
907 Allocators::DynamicArena arena(1 * MiB);
908
909 typedef std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Allocators::DynamicArena > > BatchesStack;
910 BatchesStack batches((BatchesStack::allocator_type(arena)));
911
912 CShaderDefines contextBlend = context;
913 contextBlend.Add(str_BLEND, str_1);
914
915 PROFILE_START("compute batches");
916
917 // Reserve an arbitrary size that's probably big enough in most cases,
918 // to avoid heavy reallocations
919 batches.reserve(256);
920
921 typedef std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Allocators::DynamicArena > > BlendStacks;
922 BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
923 blendStacks.reserve(patches.size());
924
925 // Extract all the blend splats from each patch
926 for (size_t i = 0; i < patches.size(); ++i)
927 {
928 CPatchRData* patch = patches[i];
929 if (!patch->m_BlendSplats.empty())
930 {
931
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());
935 }
936 }
937
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)
944 {
945 if (!batches.empty())
946 {
947 CTerrainTextureEntry* tex = batches.back().m_Texture;
948
949 for (size_t k = 0; k < blendStacks.size(); ++k)
950 {
951 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
952 if (!splats.empty() && splats.back().m_Texture == tex)
953 {
954 CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
955 CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
956
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);
959
960 u8* indexBase = indices->m_Owner->GetBindAddress();
961 batch.second.push_back(indexBase + sizeof(u16)*(indices->m_Index + splats.back().m_IndexStart));
962
963 splats.pop_back();
964 }
965 }
966 }
967
968 CTerrainTextureEntry* bestTex = NULL;
969 size_t bestStackSize = 0;
970
971 for (size_t k = 0; k < blendStacks.size(); ++k)
972 {
973 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
974 if (splats.size() > bestStackSize)
975 {
976 bestStackSize = splats.size();
977 bestTex = splats.back().m_Texture;
978 }
979 }
980
981 if (bestStackSize == 0)
982 break;
983
984 SBlendBatch layer(arena);
985 layer.m_Texture = bestTex;
986 batches.push_back(layer);
987 }
988
989 PROFILE_END("compute batches");
990
991 CVertexBuffer* lastVB = NULL;
992
993 for (BatchesStack::iterator itt = batches.begin(); itt != batches.end(); ++itt)
994 {
995 if (itt->m_Texture->GetMaterial().GetSamplers().size() == 0)
996 continue;
997
998 int numPasses = 1;
999 CShaderTechniquePtr techBase;
1000
1001 if (!isDummyShader)
1002 {
1003 techBase = g_Renderer.GetShaderManager().LoadEffect(itt->m_Texture->GetMaterial().GetShaderEffect(), contextBlend, itt->m_Texture->GetMaterial().GetShaderDefines(0));
1004
1005 numPasses = techBase->GetNumPasses();
1006 }
1007
1008 CShaderProgramPtr previousShader;
1009 for (int pass = 0; pass < numPasses; ++pass)
1010 {
1011 if (!isDummyShader)
1012 {
1013 techBase->BeginPass(pass);
1014 TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
1015
1016 glEnable(GL_BLEND);
1017 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1018 }
1019
1020 const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
1021
1022 if (itt->m_Texture)
1023 {
1024 const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
1025 size_t samplersNum = samplers.size();
1026
1027 for (size_t s = 0; s < samplersNum; ++s)
1028 {
1029 const CMaterial::TextureSampler& samp = samplers[s];
1030 shader->BindTexture(samp.Name, samp.Sampler);
1031 }
1032
1033 shader->BindTexture(str_blendTex, itt->m_Texture->m_TerrainAlpha->second.m_hCompositeAlphaMap);
1034
1035 itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(shader);
1036
1037 #if !CONFIG2_GLES
1038 if (isDummyShader)
1039 {
1040 pglClientActiveTextureARB(GL_TEXTURE0);
1041 glMatrixMode(GL_TEXTURE);
1042 glLoadMatrixf(itt->m_Texture->GetTextureMatrix());
1043 glMatrixMode(GL_MODELVIEW);
1044 }
1045 else
1046 #endif
1047 {
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);
1051 }
1052 }
1053 else
1054 {
1055 shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
1056 }
1057
1058 for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
1059 {
1060 // Rebind the VB only if it changed since the last batch
1061 if (itv->first != lastVB || shader != previousShader)
1062 {
1063 lastVB = itv->first;
1064 previousShader = shader;
1065 GLsizei stride = sizeof(SBlendVertex);
1066 SBlendVertex *base = (SBlendVertex *)itv->first->Bind();
1067
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]);
1073 }
1074
1075 shader->AssertPointersBound();
1076
1077 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1078 {
1079 it->first->Bind();
1080
1081 BatchElements& batch = it->second;
1082
1083 if (!g_Renderer.m_SkipSubmit)
1084 {
1085 for (size_t i = 0; i < batch.first.size(); ++i)
1086 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1087 }
1088
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;
1092 }
1093 }
1094
1095 if (!isDummyShader)
1096 {
1097 glDisable(GL_BLEND);
1098 techBase->EndPass();
1099 }
1100 }
1101 }
1102
1103 #if !CONFIG2_GLES
1104 if (isDummyShader)
1105 {
1106 pglClientActiveTextureARB(GL_TEXTURE0);
1107 glMatrixMode(GL_TEXTURE);
1108 glLoadIdentity();
1109 glMatrixMode(GL_MODELVIEW);
1110 }
1111 #endif
1112
1113 CVertexBuffer::Unbind();
1114 }
1115
RenderStreams(const std::vector<CPatchRData * > & patches,const CShaderProgramPtr & shader,int streamflags)1116 void CPatchRData::RenderStreams(const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader, int streamflags)
1117 {
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;
1120
1121 // Group batches by index buffer
1122 typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
1123
1124 // Group batches by vertex buffer
1125 typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
1126
1127 VertexBufferBatches batches;
1128
1129 PROFILE_START("compute batches");
1130
1131 // Collect all the patches into their appropriate batches
1132 for (size_t i = 0; i < patches.size(); ++i)
1133 {
1134 CPatchRData* patch = patches[i];
1135 BatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
1136
1137 batch.first.push_back(patch->m_VBBaseIndices->m_Count);
1138
1139 u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
1140 batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index));
1141 }
1142
1143 PROFILE_END("compute batches");
1144
1145 ENSURE(!(streamflags & ~(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0|STREAM_POSTOUV1)));
1146
1147 // Render each batch
1148 for (VertexBufferBatches::iterator itv = batches.begin(); itv != batches.end(); ++itv)
1149 {
1150 GLsizei stride = sizeof(SBaseVertex);
1151 SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
1152
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);
1160
1161 shader->AssertPointersBound();
1162
1163 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1164 {
1165 it->first->Bind();
1166
1167 BatchElements& batch = it->second;
1168
1169 if (!g_Renderer.m_SkipSubmit)
1170 {
1171 for (size_t i = 0; i < batch.first.size(); ++i)
1172 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1173 }
1174
1175 g_Renderer.m_Stats.m_DrawCalls++;
1176 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1177 }
1178 }
1179
1180 CVertexBuffer::Unbind();
1181 }
1182
RenderOutline()1183 void CPatchRData::RenderOutline()
1184 {
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;
1188
1189 CVector3D pos;
1190 std::vector<CVector3D> line;
1191
1192 ssize_t i, j;
1193
1194 for (i = 0, j = 0; i <= PATCH_SIZE; ++i)
1195 {
1196 terrain->CalcPosition(gx + i, gz + j, pos);
1197 line.push_back(pos);
1198 }
1199 for (i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1200 {
1201 terrain->CalcPosition(gx + i, gz + j, pos);
1202 line.push_back(pos);
1203 }
1204 for (i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1205 {
1206 terrain->CalcPosition(gx + i, gz + j, pos);
1207 line.push_back(pos);
1208 }
1209 for (i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1210 {
1211 terrain->CalcPosition(gx + i, gz + j, pos);
1212 line.push_back(pos);
1213 }
1214
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
1221 }
1222
RenderSides(CShaderProgramPtr & shader)1223 void CPatchRData::RenderSides(CShaderProgramPtr& shader)
1224 {
1225 ENSURE(m_UpdateFlags==0);
1226
1227 if (!m_VBSides)
1228 return;
1229
1230 SSideVertex *base = (SSideVertex *)m_VBSides->m_Owner->Bind();
1231
1232 // setup data pointers
1233 GLsizei stride = sizeof(SSideVertex);
1234 shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
1235
1236 shader->AssertPointersBound();
1237
1238 if (!g_Renderer.m_SkipSubmit)
1239 glDrawArrays(GL_TRIANGLE_STRIP, m_VBSides->m_Index, (GLsizei)m_VBSides->m_Count);
1240
1241 // bump stats
1242 g_Renderer.m_Stats.m_DrawCalls++;
1243 g_Renderer.m_Stats.m_TerrainTris += m_VBSides->m_Count - 2;
1244
1245 CVertexBuffer::Unbind();
1246 }
1247
RenderPriorities(CTextRenderer & textRenderer)1248 void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
1249 {
1250 CTerrain* terrain = m_Patch->m_Parent;
1251 CCamera* camera = g_Game->GetView()->GetCamera();
1252
1253 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1254 {
1255 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1256 {
1257 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1258 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1259
1260 CVector3D pos;
1261 terrain->CalcPosition(gx, gz, pos);
1262
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;
1266
1267 float x, y;
1268 camera->GetScreenCoordinates(pos, x, y);
1269
1270 textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1271 }
1272 }
1273 }
1274
1275 //
1276 // Water build and rendering
1277 //
1278
1279 // Build vertex buffer for water vertices over our patch
BuildWater()1280 void CPatchRData::BuildWater()
1281 {
1282 PROFILE3("build water");
1283
1284 // Number of vertices in each direction in each patch
1285 ENSURE(PATCH_SIZE % water_cell_size == 0);
1286
1287 if (m_VBWater)
1288 {
1289 g_VBMan.Release(m_VBWater);
1290 m_VBWater = nullptr;
1291 }
1292 if (m_VBWaterIndices)
1293 {
1294 g_VBMan.Release(m_VBWaterIndices);
1295 m_VBWaterIndices = nullptr;
1296 }
1297 if (m_VBWaterShore)
1298 {
1299 g_VBMan.Release(m_VBWaterShore);
1300 m_VBWaterShore = nullptr;
1301 }
1302 if (m_VBWaterIndicesShore)
1303 {
1304 g_VBMan.Release(m_VBWaterIndicesShore);
1305 m_VBWaterIndicesShore = nullptr;
1306 }
1307 m_WaterBounds.SetEmpty();
1308
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;
1314
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));
1320
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));
1326
1327 WaterManager* WaterMgr = g_Renderer.GetWaterManager();
1328
1329 CPatch* patch = m_Patch;
1330 CTerrain* terrain = patch->m_Parent;
1331
1332 ssize_t mapSize = terrain->GetVerticesPerSide();
1333
1334 // Top-left coordinates of our patch.
1335 ssize_t px = m_Patch->m_X * PATCH_SIZE;
1336 ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
1337
1338 // To whoever implements different water heights, this is a TODO: water height)
1339 float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
1340
1341 // The 4 points making a water tile.
1342 int moves[4][2] = {
1343 {0,0},
1344 {water_cell_size,0},
1345 {0,water_cell_size},
1346 {water_cell_size,water_cell_size}
1347 };
1348 // Where to look for when checking for water for shore tiles.
1349 int check[10][2] = {
1350 {0, 0},
1351 {water_cell_size, 0},
1352 {water_cell_size*2, 0},
1353 {0, water_cell_size},
1354 {0, water_cell_size*2},
1355 {water_cell_size, water_cell_size},
1356 {water_cell_size*2, water_cell_size*2},
1357 {-water_cell_size, 0},
1358 {0, -water_cell_size},
1359 {-water_cell_size, -water_cell_size}
1360 };
1361
1362 // build vertices, uv, and shader varying
1363 for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
1364 {
1365 for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
1366 {
1367 // Check that this tile is close to water
1368 bool nearWater = false;
1369 for (size_t test = 0; test < 10; ++test)
1370 if (terrain->GetVertexGroundLevel(x + px + check[test][0], z + pz + check[test][1]) < waterHeight)
1371 nearWater = true;
1372 if (!nearWater)
1373 continue;
1374
1375 // This is actually lying and I should call CcmpTerrain
1376 /*if (!terrain->IsOnMap(x+x1, z+z1)
1377 && !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
1378 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
1379 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
1380 continue;*/
1381
1382 for (int i = 0; i < 4; ++i)
1383 {
1384 if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1385 continue;
1386
1387 ssize_t xx = x + px + moves[i][0];
1388 ssize_t zz = z + pz + moves[i][1];
1389
1390 SWaterVertex vertex;
1391 terrain->CalcPosition(xx,zz, vertex.m_Position);
1392 float depth = waterHeight - vertex.m_Position.Y;
1393
1394 vertex.m_Position.Y = waterHeight;
1395
1396 m_WaterBounds += vertex.m_Position;
1397
1398 vertex.m_WaterData = CVector2D(WaterMgr->m_WindStrength[xx + zz*mapSize], depth);
1399
1400 water_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data.size();
1401 water_vertex_data.push_back(vertex);
1402 }
1403 water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1404 water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
1405 water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1406 water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1407 water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
1408 water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1409
1410 // Check id this tile is partly over land.
1411 // If so add a square over the terrain. This is necessary to render waves that go on shore.
1412 if (terrain->GetVertexGroundLevel(x+px, z+pz) < waterHeight
1413 && terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz) < waterHeight
1414 && terrain->GetVertexGroundLevel(x+px, z+pz+water_cell_size) < waterHeight
1415 && terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz+water_cell_size) < waterHeight)
1416 continue;
1417
1418 for (int i = 0; i < 4; ++i)
1419 {
1420 if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1421 continue;
1422 ssize_t xx = x + px + moves[i][0];
1423 ssize_t zz = x + pz + moves[i][1];
1424
1425 SWaterVertex vertex;
1426 terrain->CalcPosition(xx,zz, vertex.m_Position);
1427
1428 vertex.m_Position.Y += 0.02f;
1429 m_WaterBounds += vertex.m_Position;
1430
1431 vertex.m_WaterData = CVector2D(0.0f, -5.0f);
1432
1433 water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data_shore.size();
1434 water_vertex_data_shore.push_back(vertex);
1435 }
1436 if (terrain->GetTriangulationDir(x + px, z + pz))
1437 {
1438 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1439 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1440 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1441 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1442 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1443 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1444 }
1445 else
1446 {
1447 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1448 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1449 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1450 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1451 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1452 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1453 }
1454 }
1455 }
1456
1457 // No vertex buffers if no data generated
1458 if (!water_indices.empty())
1459 {
1460 m_VBWater = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
1461 m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater, &water_vertex_data[0]);
1462
1463 m_VBWaterIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
1464 m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices, &water_indices[0]);
1465 }
1466
1467 if (!water_indices_shore.empty())
1468 {
1469 m_VBWaterShore = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data_shore.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
1470 m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore, &water_vertex_data_shore[0]);
1471
1472 // Construct indices buffer
1473 m_VBWaterIndicesShore = g_VBMan.Allocate(sizeof(GLushort), water_indices_shore.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
1474 m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore, &water_indices_shore[0]);
1475 }
1476 }
1477
RenderWater(CShaderProgramPtr & shader,bool onlyShore,bool fixedPipeline)1478 void CPatchRData::RenderWater(CShaderProgramPtr& shader, bool onlyShore, bool fixedPipeline)
1479 {
1480 ASSERT(m_UpdateFlags==0);
1481
1482 if (g_Renderer.m_SkipSubmit || (!m_VBWater && !m_VBWaterShore))
1483 return;
1484
1485 #if !CONFIG2_GLES
1486 if (g_Renderer.m_WaterRenderMode == WIREFRAME)
1487 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1488 #endif
1489
1490 if (m_VBWater != 0x0 && !onlyShore)
1491 {
1492 SWaterVertex *base=(SWaterVertex *)m_VBWater->m_Owner->Bind();
1493
1494 // setup data pointers
1495 GLsizei stride = sizeof(SWaterVertex);
1496 shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWater->m_Index].m_Position);
1497 if (!fixedPipeline)
1498 shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWater->m_Index].m_WaterData);
1499
1500 shader->AssertPointersBound();
1501
1502 u8* indexBase = m_VBWaterIndices->m_Owner->Bind();
1503 glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndices->m_Count,
1504 GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndices->m_Index));
1505
1506 g_Renderer.m_Stats.m_DrawCalls++;
1507 g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
1508 }
1509
1510 if (m_VBWaterShore != 0x0 &&
1511 g_Renderer.GetWaterManager()->m_WaterEffects &&
1512 g_Renderer.GetWaterManager()->m_WaterFancyEffects)
1513 {
1514 SWaterVertex *base=(SWaterVertex *)m_VBWaterShore->m_Owner->Bind();
1515
1516 GLsizei stride = sizeof(SWaterVertex);
1517 shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWaterShore->m_Index].m_Position);
1518 if (!fixedPipeline)
1519 shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWaterShore->m_Index].m_WaterData);
1520
1521 shader->AssertPointersBound();
1522
1523 u8* indexBase = m_VBWaterIndicesShore->m_Owner->Bind();
1524 glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndicesShore->m_Count,
1525 GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndicesShore->m_Index));
1526
1527 g_Renderer.m_Stats.m_DrawCalls++;
1528 g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;
1529 }
1530
1531 CVertexBuffer::Unbind();
1532
1533 #if !CONFIG2_GLES
1534 if (g_Renderer.m_WaterRenderMode == WIREFRAME)
1535 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1536 #endif
1537 }
1538