Line data Source code
1 : /* Copyright (C) 2022 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 "renderer/PatchRData.h"
21 :
22 : #include "graphics/GameView.h"
23 : #include "graphics/LightEnv.h"
24 : #include "graphics/LOSTexture.h"
25 : #include "graphics/Patch.h"
26 : #include "graphics/ShaderManager.h"
27 : #include "graphics/Terrain.h"
28 : #include "graphics/TerrainTextureEntry.h"
29 : #include "graphics/TextRenderer.h"
30 : #include "graphics/TextureManager.h"
31 : #include "lib/allocators/DynamicArena.h"
32 : #include "lib/allocators/STLAllocators.h"
33 : #include "maths/MathUtil.h"
34 : #include "ps/CLogger.h"
35 : #include "ps/CStrInternStatic.h"
36 : #include "ps/Game.h"
37 : #include "ps/GameSetup/Config.h"
38 : #include "ps/Profile.h"
39 : #include "ps/Pyrogenesis.h"
40 : #include "ps/VideoMode.h"
41 : #include "ps/World.h"
42 : #include "renderer/AlphaMapCalculator.h"
43 : #include "renderer/DebugRenderer.h"
44 : #include "renderer/Renderer.h"
45 : #include "renderer/SceneRenderer.h"
46 : #include "renderer/TerrainRenderer.h"
47 : #include "renderer/WaterManager.h"
48 : #include "simulation2/components/ICmpWaterManager.h"
49 : #include "simulation2/Simulation2.h"
50 :
51 : #include <algorithm>
52 : #include <numeric>
53 : #include <set>
54 :
55 : const ssize_t BlendOffsets[9][2] = {
56 : { 0, -1 },
57 : { -1, -1 },
58 : { -1, 0 },
59 : { -1, 1 },
60 : { 0, 1 },
61 : { 1, 1 },
62 : { 1, 0 },
63 : { 1, -1 },
64 : { 0, 0 }
65 : };
66 :
67 0 : CPatchRData::CPatchRData(CPatch* patch, CSimulation2* simulation) :
68 0 : m_Patch(patch), m_Simulation(simulation)
69 : {
70 0 : ENSURE(patch);
71 0 : Build();
72 0 : }
73 :
74 : CPatchRData::~CPatchRData() = default;
75 :
76 : /**
77 : * Represents a blend for a single tile, texture and shape.
78 : */
79 : struct STileBlend
80 : {
81 : CTerrainTextureEntry* m_Texture;
82 : int m_Priority;
83 : u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
84 :
85 : struct DecreasingPriority
86 : {
87 0 : bool operator()(const STileBlend& a, const STileBlend& b) const
88 : {
89 0 : if (a.m_Priority > b.m_Priority)
90 0 : return true;
91 0 : if (a.m_Priority < b.m_Priority)
92 0 : return false;
93 0 : if (a.m_Texture && b.m_Texture)
94 0 : return a.m_Texture->GetTag() > b.m_Texture->GetTag();
95 : return false;
96 : }
97 : };
98 :
99 : struct CurrentTile
100 : {
101 0 : bool operator()(const STileBlend& a) const
102 : {
103 0 : return (a.m_TileMask & (1 << 8)) != 0;
104 : }
105 : };
106 : };
107 :
108 : /**
109 : * Represents the ordered collection of blends drawn on a particular tile.
110 : */
111 0 : struct STileBlendStack
112 : {
113 : u8 i, j;
114 : std::vector<STileBlend> blends; // back of vector is lowest-priority texture
115 : };
116 :
117 : /**
118 : * Represents a batched collection of blends using the same texture.
119 : */
120 0 : struct SBlendLayer
121 : {
122 : struct Tile
123 : {
124 : u8 i, j;
125 : u8 shape;
126 : };
127 :
128 : CTerrainTextureEntry* m_Texture;
129 : std::vector<Tile> m_Tiles;
130 : };
131 :
132 0 : void CPatchRData::BuildBlends()
133 : {
134 0 : PROFILE3("build blends");
135 :
136 0 : m_BlendSplats.clear();
137 :
138 0 : std::vector<SBlendVertex> blendVertices;
139 0 : std::vector<u16> blendIndices;
140 :
141 0 : CTerrain* terrain = m_Patch->m_Parent;
142 :
143 0 : std::vector<STileBlendStack> blendStacks;
144 0 : blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
145 :
146 0 : std::vector<STileBlend> blends;
147 0 : blends.reserve(9);
148 :
149 : // For each tile in patch ..
150 0 : for (ssize_t j = 0; j < PATCH_SIZE; ++j)
151 : {
152 0 : for (ssize_t i = 0; i < PATCH_SIZE; ++i)
153 : {
154 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
155 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
156 :
157 0 : blends.clear();
158 :
159 : // Compute a blend for every tile in the 3x3 square around this tile
160 0 : for (size_t n = 0; n < 9; ++n)
161 : {
162 0 : ssize_t ox = gx + BlendOffsets[n][1];
163 0 : ssize_t oz = gz + BlendOffsets[n][0];
164 :
165 0 : CMiniPatch* nmp = terrain->GetTile(ox, oz);
166 0 : if (!nmp)
167 0 : continue;
168 :
169 0 : STileBlend blend;
170 0 : blend.m_Texture = nmp->GetTextureEntry();
171 0 : blend.m_Priority = nmp->GetPriority();
172 0 : blend.m_TileMask = 1 << n;
173 0 : blends.push_back(blend);
174 : }
175 :
176 : // Sort the blends, highest priority first
177 0 : std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
178 :
179 0 : STileBlendStack blendStack;
180 0 : blendStack.i = i;
181 0 : blendStack.j = j;
182 :
183 : // Put the blends into the tile's stack, merging any adjacent blends with the same texture
184 0 : for (size_t k = 0; k < blends.size(); ++k)
185 : {
186 0 : if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
187 0 : blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
188 : else
189 0 : blendStack.blends.push_back(blends[k]);
190 : }
191 :
192 : // Remove blends that are after (i.e. lower priority than) the current tile
193 : // (including the current tile), since we don't want to render them on top of
194 : // the tile's base texture
195 0 : blendStack.blends.erase(
196 0 : std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
197 0 : blendStack.blends.end());
198 :
199 0 : blendStacks.push_back(blendStack);
200 : }
201 : }
202 :
203 : // Given the blend stack per tile, we want to batch together as many blends as possible.
204 : // Group them into a series of layers (each of which has a single texture):
205 : // (This is effectively a topological sort / linearisation of the partial order induced
206 : // by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
207 :
208 0 : std::vector<SBlendLayer> blendLayers;
209 :
210 0 : while (true)
211 : {
212 0 : if (!blendLayers.empty())
213 : {
214 : // Try to grab as many tiles as possible that match our current layer,
215 : // from off the blend stacks of all the tiles
216 :
217 0 : CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
218 :
219 0 : for (size_t k = 0; k < blendStacks.size(); ++k)
220 : {
221 0 : if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
222 : {
223 0 : SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, (u8)blendStacks[k].blends.back().m_TileMask };
224 0 : blendLayers.back().m_Tiles.push_back(t);
225 0 : blendStacks[k].blends.pop_back();
226 : }
227 : // (We've already merged adjacent entries of the same texture in each stack,
228 : // so we don't need to bother looping to check the next entry in this stack again)
229 : }
230 : }
231 :
232 : // We've grabbed as many tiles as possible; now we need to start a new layer.
233 : // The new layer's texture could come from the back of any non-empty stack;
234 : // choose the longest stack as a heuristic to reduce the number of layers
235 : CTerrainTextureEntry* bestTex = NULL;
236 : size_t bestStackSize = 0;
237 :
238 0 : for (size_t k = 0; k < blendStacks.size(); ++k)
239 : {
240 0 : if (blendStacks[k].blends.size() > bestStackSize)
241 : {
242 0 : bestStackSize = blendStacks[k].blends.size();
243 0 : bestTex = blendStacks[k].blends.back().m_Texture;
244 : }
245 : }
246 :
247 : // If all our stacks were empty, we're done
248 0 : if (bestStackSize == 0)
249 : break;
250 :
251 : // Otherwise add the new layer, then loop back and start filling it in
252 :
253 0 : SBlendLayer layer;
254 0 : layer.m_Texture = bestTex;
255 0 : blendLayers.push_back(layer);
256 0 : }
257 :
258 : // Now build outgoing splats
259 0 : m_BlendSplats.resize(blendLayers.size());
260 :
261 0 : for (size_t k = 0; k < blendLayers.size(); ++k)
262 : {
263 0 : SSplat& splat = m_BlendSplats[k];
264 0 : splat.m_IndexStart = blendIndices.size();
265 0 : splat.m_Texture = blendLayers[k].m_Texture;
266 :
267 0 : for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
268 : {
269 0 : SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
270 0 : AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
271 : }
272 :
273 0 : splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
274 : }
275 :
276 : // Release existing vertex buffer chunks
277 0 : m_VBBlends.Reset();
278 0 : m_VBBlendIndices.Reset();
279 :
280 0 : if (blendVertices.size())
281 : {
282 : // Construct vertex buffer
283 :
284 0 : m_VBBlends = g_VBMan.AllocateChunk(
285 : sizeof(SBlendVertex), blendVertices.size(),
286 : Renderer::Backend::GL::CBuffer::Type::VERTEX, false,
287 0 : nullptr, CVertexBufferManager::Group::TERRAIN);
288 0 : m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends.Get(), &blendVertices[0]);
289 :
290 : // Update the indices to include the base offset of the vertex data
291 0 : for (size_t k = 0; k < blendIndices.size(); ++k)
292 0 : blendIndices[k] += static_cast<u16>(m_VBBlends->m_Index);
293 :
294 0 : m_VBBlendIndices = g_VBMan.AllocateChunk(
295 : sizeof(u16), blendIndices.size(),
296 : Renderer::Backend::GL::CBuffer::Type::INDEX, false,
297 0 : nullptr, CVertexBufferManager::Group::TERRAIN);
298 0 : m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices.Get(), &blendIndices[0]);
299 : }
300 0 : }
301 :
302 0 : void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
303 : u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
304 : {
305 0 : CTerrain* terrain = m_Patch->m_Parent;
306 :
307 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
308 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
309 :
310 : // uses the current neighbour texture
311 0 : BlendShape8 shape8;
312 0 : for (size_t m = 0; m < 8; ++m)
313 0 : shape8[m] = (shape & (1 << m)) ? 0 : 1;
314 :
315 : // calculate the required alphamap and the required rotation of the alphamap from blendshape
316 0 : unsigned int alphamapflags;
317 0 : int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
318 :
319 : // now actually render the blend tile (if we need one)
320 0 : if (alphamap == -1)
321 0 : return;
322 :
323 0 : float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0;
324 0 : float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1;
325 0 : float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0;
326 0 : float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1;
327 :
328 0 : if (alphamapflags & BLENDMAP_FLIPU)
329 0 : std::swap(u0, u1);
330 :
331 0 : if (alphamapflags & BLENDMAP_FLIPV)
332 0 : std::swap(v0, v1);
333 :
334 0 : int base = 0;
335 0 : if (alphamapflags & BLENDMAP_ROTATE90)
336 : base = 1;
337 0 : else if (alphamapflags & BLENDMAP_ROTATE180)
338 : base = 2;
339 0 : else if (alphamapflags & BLENDMAP_ROTATE270)
340 0 : base = 3;
341 :
342 0 : SBlendVertex vtx[4];
343 0 : vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
344 0 : vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
345 0 : vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
346 0 : vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
347 0 : vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
348 0 : vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
349 0 : vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
350 0 : vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
351 :
352 0 : SBlendVertex dst;
353 :
354 0 : CVector3D normal;
355 :
356 0 : u16 index = static_cast<u16>(blendVertices.size());
357 :
358 0 : terrain->CalcPosition(gx, gz, dst.m_Position);
359 0 : terrain->CalcNormal(gx, gz, normal);
360 0 : dst.m_Normal = normal;
361 0 : dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
362 0 : dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
363 0 : blendVertices.push_back(dst);
364 :
365 0 : terrain->CalcPosition(gx + 1, gz, dst.m_Position);
366 0 : terrain->CalcNormal(gx + 1, gz, normal);
367 0 : dst.m_Normal = normal;
368 0 : dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
369 0 : dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
370 0 : blendVertices.push_back(dst);
371 :
372 0 : terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
373 0 : terrain->CalcNormal(gx + 1, gz + 1, normal);
374 0 : dst.m_Normal = normal;
375 0 : dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
376 0 : dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
377 0 : blendVertices.push_back(dst);
378 :
379 0 : terrain->CalcPosition(gx, gz + 1, dst.m_Position);
380 0 : terrain->CalcNormal(gx, gz + 1, normal);
381 0 : dst.m_Normal = normal;
382 0 : dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
383 0 : dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
384 0 : blendVertices.push_back(dst);
385 :
386 0 : bool dir = terrain->GetTriangulationDir(gx, gz);
387 0 : if (dir)
388 : {
389 0 : blendIndices.push_back(index+0);
390 0 : blendIndices.push_back(index+1);
391 0 : blendIndices.push_back(index+3);
392 :
393 0 : blendIndices.push_back(index+1);
394 0 : blendIndices.push_back(index+2);
395 0 : blendIndices.push_back(index+3);
396 : }
397 : else
398 : {
399 0 : blendIndices.push_back(index+0);
400 0 : blendIndices.push_back(index+1);
401 0 : blendIndices.push_back(index+2);
402 :
403 0 : blendIndices.push_back(index+2);
404 0 : blendIndices.push_back(index+3);
405 0 : blendIndices.push_back(index+0);
406 : }
407 : }
408 :
409 0 : void CPatchRData::BuildIndices()
410 : {
411 0 : PROFILE3("build indices");
412 :
413 0 : CTerrain* terrain = m_Patch->m_Parent;
414 :
415 0 : ssize_t px = m_Patch->m_X * PATCH_SIZE;
416 0 : ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
417 :
418 : // must have allocated some vertices before trying to build corresponding indices
419 0 : ENSURE(m_VBBase);
420 :
421 : // number of vertices in each direction in each patch
422 0 : ssize_t vsize=PATCH_SIZE+1;
423 :
424 : // PATCH_SIZE must be 2^8-2 or less to not overflow u16 indices buffer. Thankfully this is always true.
425 0 : ENSURE(vsize*vsize < 65536);
426 :
427 0 : std::vector<unsigned short> indices;
428 0 : indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
429 :
430 : // release existing splats
431 0 : m_Splats.clear();
432 :
433 : // build grid of textures on this patch
434 0 : std::vector<CTerrainTextureEntry*> textures;
435 0 : CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
436 0 : for (ssize_t j=0;j<PATCH_SIZE;j++) {
437 0 : for (ssize_t i=0;i<PATCH_SIZE;i++) {
438 0 : CTerrainTextureEntry* tex=m_Patch->m_MiniPatches[j][i].GetTextureEntry();
439 0 : texgrid[j][i]=tex;
440 0 : if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
441 0 : textures.push_back(tex);
442 : }
443 : }
444 : }
445 :
446 : // now build base splats from interior textures
447 0 : m_Splats.resize(textures.size());
448 : // build indices for base splats
449 0 : size_t base=m_VBBase->m_Index;
450 :
451 0 : for (size_t k = 0; k < m_Splats.size(); ++k)
452 : {
453 0 : CTerrainTextureEntry* tex = textures[k];
454 :
455 0 : SSplat& splat=m_Splats[k];
456 0 : splat.m_Texture=tex;
457 0 : splat.m_IndexStart=indices.size();
458 :
459 0 : for (ssize_t j = 0; j < PATCH_SIZE; j++)
460 : {
461 0 : for (ssize_t i = 0; i < PATCH_SIZE; i++)
462 : {
463 0 : if (texgrid[j][i] == tex)
464 : {
465 0 : bool dir = terrain->GetTriangulationDir(px+i, pz+j);
466 0 : if (dir)
467 : {
468 0 : indices.push_back(u16(((j+0)*vsize+(i+0))+base));
469 0 : indices.push_back(u16(((j+0)*vsize+(i+1))+base));
470 0 : indices.push_back(u16(((j+1)*vsize+(i+0))+base));
471 :
472 0 : indices.push_back(u16(((j+0)*vsize+(i+1))+base));
473 0 : indices.push_back(u16(((j+1)*vsize+(i+1))+base));
474 0 : indices.push_back(u16(((j+1)*vsize+(i+0))+base));
475 : }
476 : else
477 : {
478 0 : indices.push_back(u16(((j+0)*vsize+(i+0))+base));
479 0 : indices.push_back(u16(((j+0)*vsize+(i+1))+base));
480 0 : indices.push_back(u16(((j+1)*vsize+(i+1))+base));
481 :
482 0 : indices.push_back(u16(((j+1)*vsize+(i+1))+base));
483 0 : indices.push_back(u16(((j+1)*vsize+(i+0))+base));
484 0 : indices.push_back(u16(((j+0)*vsize+(i+0))+base));
485 : }
486 : }
487 : }
488 : }
489 0 : splat.m_IndexCount=indices.size()-splat.m_IndexStart;
490 : }
491 :
492 : // Release existing vertex buffer chunk
493 0 : m_VBBaseIndices.Reset();
494 :
495 0 : ENSURE(indices.size());
496 :
497 : // Construct vertex buffer
498 0 : m_VBBaseIndices = g_VBMan.AllocateChunk(
499 : sizeof(u16), indices.size(),
500 0 : Renderer::Backend::GL::CBuffer::Type::INDEX, false, nullptr, CVertexBufferManager::Group::TERRAIN);
501 0 : m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices.Get(), &indices[0]);
502 0 : }
503 :
504 :
505 0 : void CPatchRData::BuildVertices()
506 : {
507 0 : PROFILE3("build vertices");
508 :
509 : // create both vertices and lighting colors
510 :
511 : // number of vertices in each direction in each patch
512 0 : ssize_t vsize = PATCH_SIZE + 1;
513 :
514 0 : std::vector<SBaseVertex> vertices;
515 0 : vertices.resize(vsize * vsize);
516 :
517 : // get index of this patch
518 0 : ssize_t px = m_Patch->m_X;
519 0 : ssize_t pz = m_Patch->m_Z;
520 :
521 0 : CTerrain* terrain = m_Patch->m_Parent;
522 :
523 : // build vertices
524 0 : for (ssize_t j = 0; j < vsize; ++j)
525 : {
526 0 : for (ssize_t i = 0; i < vsize; ++i)
527 : {
528 0 : ssize_t ix = px * PATCH_SIZE + i;
529 0 : ssize_t iz = pz * PATCH_SIZE + j;
530 0 : ssize_t v = j * vsize + i;
531 :
532 : // calculate vertex data
533 0 : terrain->CalcPosition(ix, iz, vertices[v].m_Position);
534 :
535 0 : CVector3D normal;
536 0 : terrain->CalcNormal(ix, iz, normal);
537 0 : vertices[v].m_Normal = normal;
538 : }
539 : }
540 :
541 : // upload to vertex buffer
542 0 : if (!m_VBBase)
543 : {
544 0 : m_VBBase = g_VBMan.AllocateChunk(
545 : sizeof(SBaseVertex), vsize * vsize,
546 : Renderer::Backend::GL::CBuffer::Type::VERTEX, false,
547 0 : nullptr, CVertexBufferManager::Group::TERRAIN);
548 : }
549 :
550 0 : m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase.Get(), &vertices[0]);
551 0 : }
552 :
553 0 : void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
554 : {
555 0 : ssize_t vsize = PATCH_SIZE + 1;
556 0 : CTerrain* terrain = m_Patch->m_Parent;
557 0 : CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
558 :
559 0 : for (ssize_t k = 0; k < vsize; k++)
560 : {
561 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE;
562 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
563 0 : switch (side)
564 : {
565 0 : case CPATCH_SIDE_NEGX: gz += k; break;
566 0 : case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
567 0 : case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
568 0 : case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
569 : }
570 :
571 0 : CVector3D pos;
572 0 : terrain->CalcPosition(gx, gz, pos);
573 :
574 : // Clamp the height to the water level
575 0 : float waterHeight = 0.f;
576 0 : if (cmpWaterManager)
577 0 : waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
578 0 : pos.Y = std::max(pos.Y, waterHeight);
579 :
580 0 : SSideVertex v0, v1;
581 0 : v0.m_Position = pos;
582 0 : v1.m_Position = pos;
583 0 : v1.m_Position.Y = 0;
584 :
585 0 : if (k == 0)
586 : {
587 0 : vertices.emplace_back(v1);
588 0 : vertices.emplace_back(v0);
589 : }
590 0 : if (k > 0)
591 : {
592 0 : const size_t lastIndex = vertices.size() - 1;
593 0 : vertices.emplace_back(v1);
594 0 : vertices.emplace_back(vertices[lastIndex]);
595 0 : vertices.emplace_back(v0);
596 0 : vertices.emplace_back(v1);
597 0 : if (k + 1 < vsize)
598 : {
599 0 : vertices.emplace_back(v1);
600 0 : vertices.emplace_back(v0);
601 : }
602 : }
603 : }
604 0 : }
605 :
606 0 : void CPatchRData::BuildSides()
607 : {
608 0 : PROFILE3("build sides");
609 :
610 0 : std::vector<SSideVertex> sideVertices;
611 :
612 0 : int sideFlags = m_Patch->GetSideFlags();
613 :
614 : // If no sides are enabled, we don't need to do anything
615 0 : if (!sideFlags)
616 0 : return;
617 :
618 : // For each side, generate a tristrip by adding a vertex at ground/water
619 : // level and a vertex underneath at height 0.
620 :
621 0 : if (sideFlags & CPATCH_SIDE_NEGX)
622 0 : BuildSide(sideVertices, CPATCH_SIDE_NEGX);
623 :
624 0 : if (sideFlags & CPATCH_SIDE_POSX)
625 0 : BuildSide(sideVertices, CPATCH_SIDE_POSX);
626 :
627 0 : if (sideFlags & CPATCH_SIDE_NEGZ)
628 0 : BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
629 :
630 0 : if (sideFlags & CPATCH_SIDE_POSZ)
631 0 : BuildSide(sideVertices, CPATCH_SIDE_POSZ);
632 :
633 0 : if (sideVertices.empty())
634 : return;
635 :
636 0 : if (!m_VBSides)
637 : {
638 0 : m_VBSides = g_VBMan.AllocateChunk(
639 : sizeof(SSideVertex), sideVertices.size(),
640 : Renderer::Backend::GL::CBuffer::Type::VERTEX, false,
641 0 : nullptr, CVertexBufferManager::Group::DEFAULT);
642 : }
643 0 : m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides.Get(), &sideVertices[0]);
644 : }
645 :
646 0 : void CPatchRData::Build()
647 : {
648 0 : BuildVertices();
649 0 : BuildSides();
650 0 : BuildIndices();
651 0 : BuildBlends();
652 0 : BuildWater();
653 0 : }
654 :
655 0 : void CPatchRData::Update(CSimulation2* simulation)
656 : {
657 0 : m_Simulation = simulation;
658 0 : if (m_UpdateFlags!=0) {
659 : // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
660 : // than everything; it's complicated slightly because the blends are dependent
661 : // on both vertex and index data
662 0 : BuildVertices();
663 0 : BuildSides();
664 0 : BuildIndices();
665 0 : BuildBlends();
666 0 : BuildWater();
667 :
668 0 : m_UpdateFlags=0;
669 : }
670 0 : }
671 :
672 : // To minimise the cost of memory allocations, everything used for computing
673 : // batches uses a arena allocator. (All allocations are short-lived so we can
674 : // just throw away the whole arena at the end of each frame.)
675 :
676 : using Arena = Allocators::DynamicArena<1 * MiB>;
677 :
678 : // std::map types with appropriate arena allocators and default comparison operator
679 : template<class Key, class Value>
680 : using PooledBatchMap = std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Arena>>;
681 :
682 : // Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
683 : // use the default constructor in that case)
684 : template<typename M>
685 0 : typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Arena& arena)
686 : {
687 0 : return m.insert(std::make_pair(k,
688 : typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(arena))
689 0 : )).first->second;
690 : }
691 0 :
692 : // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
693 0 : template<typename M>
694 : typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Arena& arena)
695 0 : {
696 : return m.insert(std::make_pair(k, std::make_pair(
697 0 : typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(arena)),
698 : typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(arena))
699 0 : ))).first->second;
700 : }
701 0 :
702 : // Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
703 0 : using BatchElements = std::pair<std::vector<GLint, ProxyAllocator<GLint, Arena>>, std::vector<u32, ProxyAllocator<u32, Arena>>>;
704 :
705 0 : // Group batches by index buffer
706 : using IndexBufferBatches = PooledBatchMap<CVertexBuffer*, BatchElements>;
707 0 :
708 : // Group batches by vertex buffer
709 : using VertexBufferBatches = PooledBatchMap<CVertexBuffer*, IndexBufferBatches>;
710 :
711 : // Group batches by texture
712 0 : using TextureBatches = PooledBatchMap<CTerrainTextureEntry*, VertexBufferBatches>;
713 :
714 0 : // Group batches by shaders.
715 : using ShaderTechniqueBatches = PooledBatchMap<CShaderTechniquePtr, TextureBatches>;
716 :
717 0 : void CPatchRData::RenderBases(
718 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
719 : const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
720 : {
721 : PROFILE3("render terrain bases");
722 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain bases");
723 :
724 : Arena arena;
725 :
726 : ShaderTechniqueBatches batches(ShaderTechniqueBatches::key_compare(), (ShaderTechniqueBatches::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 0 : {
736 : SSplat& splat = patch->m_Splats[j];
737 : const CMaterial& material = splat.m_Texture->GetMaterial();
738 : if (material.GetShaderEffect().empty())
739 0 : {
740 0 : LOGERROR("Terrain renderer failed to load shader effect.\n");
741 : continue;
742 0 : }
743 : CShaderDefines defines = context;
744 0 : defines.SetMany(material.GetShaderDefines(0));
745 : CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
746 0 : material.GetShaderEffect(), defines);
747 :
748 : BatchElements& batch = PooledPairGet(
749 0 : PooledMapGet(
750 : PooledMapGet(
751 0 : PooledMapGet(batches, techBase, arena),
752 0 : splat.m_Texture, arena
753 : ),
754 0 : patch->m_VBBase->m_Owner, arena
755 0 : ),
756 0 : patch->m_VBBaseIndices->m_Owner, arena
757 : );
758 0 :
759 0 : batch.first.push_back(splat.m_IndexCount);
760 :
761 0 : batch.second.push_back(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart);
762 0 : }
763 0 : }
764 0 :
765 : PROFILE_END("compute batches");
766 0 :
767 : // Render each batch
768 : for (ShaderTechniqueBatches::iterator itTech = batches.begin(); itTech != batches.end(); ++itTech)
769 0 : {
770 0 : const CShaderTechniquePtr& techBase = itTech->first;
771 0 : const int numPasses = techBase->GetNumPasses();
772 0 : for (int pass = 0; pass < numPasses; ++pass)
773 0 : {
774 0 : techBase->BeginPass(pass);
775 0 : deviceCommandContext->SetGraphicsPipelineState(
776 : techBase->GetGraphicsPipelineStateDesc(pass));
777 0 : const CShaderProgramPtr& shader = techBase->GetShader(pass);
778 : TerrainRenderer::PrepareShader(shader, shadow);
779 0 :
780 : TextureBatches& textureBatches = itTech->second;
781 : for (TextureBatches::iterator itt = textureBatches.begin(); itt != textureBatches.end(); ++itt)
782 : {
783 0 : if (!itt->first->GetMaterial().GetSamplers().empty())
784 : {
785 : const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers();
786 0 : for(const CMaterial::TextureSampler& samp : samplers)
787 : samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
788 0 : for(const CMaterial::TextureSampler& samp : samplers)
789 0 : shader->BindTexture(samp.Name, samp.Sampler->GetBackendTexture());
790 0 :
791 : itt->first->GetMaterial().GetStaticUniforms().BindUniforms(shader);
792 0 :
793 0 : float c = itt->first->GetTextureMatrix()[0];
794 : float ms = itt->first->GetTextureMatrix()[8];
795 0 : shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
796 0 : }
797 : else
798 0 : {
799 0 : shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
800 : }
801 0 :
802 : for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
803 : {
804 0 : GLsizei stride = sizeof(SBaseVertex);
805 0 : SBaseVertex *base = (SBaseVertex *)itv->first->Bind(deviceCommandContext);
806 0 : shader->VertexPointer(
807 0 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position[0]);
808 : shader->NormalPointer(
809 0 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Normal[0]);
810 : shader->TexCoordPointer(
811 0 : GL_TEXTURE0, Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position[0]);
812 0 :
813 0 : shader->AssertPointersBound();
814 :
815 : for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
816 : {
817 0 : it->first->UploadIfNeeded(deviceCommandContext);
818 : deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
819 :
820 0 : BatchElements& batch = it->second;
821 :
822 0 : for (size_t i = 0; i < batch.first.size(); ++i)
823 0 : deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
824 0 :
825 0 : g_Renderer.m_Stats.m_DrawCalls++;
826 0 : g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
827 0 : }
828 0 : }
829 0 : }
830 : techBase->EndPass();
831 0 : }
832 : }
833 0 :
834 : CVertexBuffer::Unbind(deviceCommandContext);
835 0 : }
836 0 :
837 : /**
838 : * Helper structure for RenderBlends.
839 : */
840 0 : struct SBlendBatch
841 0 : {
842 : SBlendBatch(Arena& arena) :
843 0 : m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
844 0 : {
845 : }
846 :
847 : CTerrainTextureEntry* m_Texture;
848 0 : CShaderTechniquePtr m_ShaderTech;
849 : VertexBufferBatches m_Batches;
850 : };
851 :
852 0 : /**
853 0 : * Helper structure for RenderBlends.
854 : */
855 : struct SBlendStackItem
856 : {
857 : SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
858 : const std::vector<CPatchRData::SSplat>& s, Arena& arena) :
859 : vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
860 0 : {
861 0 : }
862 :
863 : using SplatStack = std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Arena>>;
864 : CVertexBuffer::VBChunk* vertices;
865 : CVertexBuffer::VBChunk* indices;
866 : SplatStack splats;
867 : };
868 :
869 : void CPatchRData::RenderBlends(
870 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
871 : const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
872 : {
873 0 : PROFILE3("render terrain blends");
874 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain blends");
875 :
876 : Arena arena;
877 0 :
878 : using BatchesStack = std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Arena>>;
879 : BatchesStack batches((BatchesStack::allocator_type(arena)));
880 :
881 : CShaderDefines contextBlend = context;
882 : contextBlend.Add(str_BLEND, str_1);
883 :
884 : PROFILE_START("compute batches");
885 :
886 : // Reserve an arbitrary size that's probably big enough in most cases,
887 0 : // to avoid heavy reallocations
888 : batches.reserve(256);
889 :
890 : using BlendStacks = std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Arena>>;
891 0 : BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
892 0 : blendStacks.reserve(patches.size());
893 :
894 0 : // Extract all the blend splats from each patch
895 : for (size_t i = 0; i < patches.size(); ++i)
896 0 : {
897 0 : CPatchRData* patch = patches[i];
898 : if (!patch->m_BlendSplats.empty())
899 0 : {
900 0 :
901 : blendStacks.push_back(SBlendStackItem(patch->m_VBBlends.Get(), patch->m_VBBlendIndices.Get(), patch->m_BlendSplats, arena));
902 0 : // Reverse the splats so the first to be rendered is at the back of the list
903 : std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
904 : }
905 : }
906 0 :
907 : // Rearrange the collection of splats to be grouped by texture, preserving
908 0 : // order of splats within each patch:
909 0 : // (This is exactly the same algorithm used in CPatchRData::BuildBlends,
910 0 : // but applied to patch-sized splats rather than to tile-sized splats;
911 : // see that function for comments on the algorithm.)
912 : while (true)
913 0 : {
914 : if (!batches.empty())
915 0 : {
916 0 : CTerrainTextureEntry* tex = batches.back().m_Texture;
917 :
918 : for (size_t k = 0; k < blendStacks.size(); ++k)
919 0 : {
920 : SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
921 0 : if (!splats.empty() && splats.back().m_Texture == tex)
922 : {
923 : CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
924 : CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
925 :
926 : BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, arena), indices->m_Owner, arena);
927 : batch.first.push_back(splats.back().m_IndexCount);
928 :
929 : batch.second.push_back(indices->m_Index + splats.back().m_IndexStart);
930 0 :
931 : splats.pop_back();
932 0 : }
933 : }
934 0 : }
935 :
936 0 : CTerrainTextureEntry* bestTex = NULL;
937 : size_t bestStackSize = 0;
938 0 :
939 0 : for (size_t k = 0; k < blendStacks.size(); ++k)
940 : {
941 0 : SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
942 0 : if (splats.size() > bestStackSize)
943 : {
944 0 : bestStackSize = splats.size();
945 0 : bestTex = splats.back().m_Texture;
946 : }
947 0 : }
948 :
949 0 : if (bestStackSize == 0)
950 : break;
951 :
952 : SBlendBatch layer(arena);
953 : layer.m_Texture = bestTex;
954 : if (!bestTex->GetMaterial().GetSamplers().empty())
955 : {
956 : CShaderDefines defines = contextBlend;
957 0 : defines.SetMany(bestTex->GetMaterial().GetShaderDefines(0));
958 : layer.m_ShaderTech = g_Renderer.GetShaderManager().LoadEffect(
959 0 : bestTex->GetMaterial().GetShaderEffect(), defines);
960 0 : }
961 : batches.push_back(layer);
962 0 : }
963 0 :
964 : PROFILE_END("compute batches");
965 :
966 : CVertexBuffer* lastVB = nullptr;
967 0 : CShaderProgramPtr previousShader;
968 : for (BatchesStack::iterator itTechBegin = batches.begin(), itTechEnd = batches.begin(); itTechBegin != batches.end(); itTechBegin = itTechEnd)
969 : {
970 0 : while (itTechEnd != batches.end() && itTechEnd->m_ShaderTech == itTechBegin->m_ShaderTech)
971 0 : ++itTechEnd;
972 0 :
973 : const CShaderTechniquePtr& techBase = itTechBegin->m_ShaderTech;
974 0 : const int numPasses = techBase->GetNumPasses();
975 0 : for (int pass = 0; pass < numPasses; ++pass)
976 0 : {
977 0 : Renderer::Backend::GraphicsPipelineStateDesc pipelineStateDesc =
978 : techBase->GetGraphicsPipelineStateDesc(pass);
979 0 : pipelineStateDesc.blendState.enabled = true;
980 0 : pipelineStateDesc.blendState.srcColorBlendFactor = pipelineStateDesc.blendState.srcAlphaBlendFactor =
981 : Renderer::Backend::BlendFactor::SRC_ALPHA;
982 0 : pipelineStateDesc.blendState.dstColorBlendFactor = pipelineStateDesc.blendState.dstAlphaBlendFactor =
983 : Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
984 0 : pipelineStateDesc.blendState.colorBlendOp = pipelineStateDesc.blendState.alphaBlendOp =
985 0 : Renderer::Backend::BlendOp::ADD;
986 0 : techBase->BeginPass(pass);
987 : deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
988 0 :
989 0 : const CShaderProgramPtr& shader = techBase->GetShader(pass);
990 : TerrainRenderer::PrepareShader(shader, shadow);
991 0 :
992 0 : Renderer::Backend::GL::CTexture* lastBlendTex = nullptr;
993 0 :
994 : for (BatchesStack::iterator itt = itTechBegin; itt != itTechEnd; ++itt)
995 0 : {
996 0 : if (itt->m_Texture->GetMaterial().GetSamplers().empty())
997 0 : continue;
998 0 :
999 : if (itt->m_Texture)
1000 0 : {
1001 : const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
1002 0 : for (const CMaterial::TextureSampler& samp : samplers)
1003 : samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
1004 0 : for (const CMaterial::TextureSampler& samp : samplers)
1005 0 : shader->BindTexture(samp.Name, samp.Sampler->GetBackendTexture());
1006 :
1007 0 : Renderer::Backend::GL::CTexture* currentBlendTex = itt->m_Texture->m_TerrainAlpha->second.m_CompositeAlphaMap.get();
1008 0 : if (currentBlendTex != lastBlendTex)
1009 : {
1010 : shader->BindTexture(str_blendTex, currentBlendTex);
1011 : lastBlendTex = currentBlendTex;
1012 0 : }
1013 :
1014 0 : itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(shader);
1015 :
1016 : float c = itt->m_Texture->GetTextureMatrix()[0];
1017 : float ms = itt->m_Texture->GetTextureMatrix()[8];
1018 : shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
1019 : }
1020 0 : else
1021 0 : {
1022 0 : shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
1023 0 : }
1024 :
1025 0 : for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
1026 0 : {
1027 : // Rebind the VB only if it changed since the last batch
1028 0 : if (itv->first != lastVB || shader != previousShader)
1029 : {
1030 : lastVB = itv->first;
1031 : previousShader = shader;
1032 0 : GLsizei stride = sizeof(SBlendVertex);
1033 : SBlendVertex *base = (SBlendVertex *)itv->first->Bind(deviceCommandContext);
1034 0 :
1035 0 : shader->VertexPointer(
1036 0 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position[0]);
1037 : shader->NormalPointer(
1038 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Normal[0]);
1039 : shader->TexCoordPointer(
1040 : GL_TEXTURE0, Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position[0]);
1041 : shader->TexCoordPointer(
1042 : GL_TEXTURE1, Renderer::Backend::Format::R32G32_SFLOAT, stride, &base->m_AlphaUVs[0]);
1043 0 : }
1044 :
1045 : shader->AssertPointersBound();
1046 0 :
1047 : for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1048 0 : {
1049 0 : it->first->UploadIfNeeded(deviceCommandContext);
1050 0 : deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
1051 0 :
1052 : BatchElements& batch = it->second;
1053 0 :
1054 0 : for (size_t i = 0; i < batch.first.size(); ++i)
1055 0 : deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
1056 0 :
1057 0 : g_Renderer.m_Stats.m_DrawCalls++;
1058 0 : g_Renderer.m_Stats.m_BlendSplats++;
1059 0 : g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1060 0 : }
1061 : }
1062 : }
1063 0 : techBase->EndPass();
1064 : }
1065 0 : }
1066 :
1067 0 : CVertexBuffer::Unbind(deviceCommandContext);
1068 0 : }
1069 :
1070 : void CPatchRData::RenderStreams(
1071 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
1072 0 : const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader,
1073 0 : const bool bindPositionAsTexCoord)
1074 : {
1075 0 : PROFILE3("render terrain streams");
1076 0 :
1077 0 : // Each batch has a list of index counts, and a list of pointers-to-first-indexes
1078 : using StreamBatchElements = std::pair<std::vector<GLint>, std::vector<u32>>;
1079 :
1080 : // Group batches by index buffer
1081 0 : using StreamIndexBufferBatches = std::map<CVertexBuffer*, StreamBatchElements>;
1082 :
1083 : // Group batches by vertex buffer
1084 : using StreamVertexBufferBatches = std::map<CVertexBuffer*, StreamIndexBufferBatches>;
1085 0 :
1086 0 : StreamVertexBufferBatches batches;
1087 :
1088 0 : PROFILE_START("compute batches");
1089 :
1090 : // Collect all the patches into their appropriate batches
1091 : for (const CPatchRData* patch : patches)
1092 : {
1093 0 : StreamBatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
1094 :
1095 : batch.first.push_back(patch->m_VBBaseIndices->m_Count);
1096 0 :
1097 : batch.second.push_back(patch->m_VBBaseIndices->m_Index);
1098 : }
1099 0 :
1100 : PROFILE_END("compute batches");
1101 :
1102 0 : // Render each batch
1103 : for (const std::pair<CVertexBuffer* const, StreamIndexBufferBatches>& streamBatch : batches)
1104 0 : {
1105 : GLsizei stride = sizeof(SBaseVertex);
1106 0 : SBaseVertex *base = (SBaseVertex *)streamBatch.first->Bind(deviceCommandContext);
1107 :
1108 : shader->VertexPointer(
1109 0 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position);
1110 : if (bindPositionAsTexCoord)
1111 0 : {
1112 : shader->TexCoordPointer(
1113 0 : GL_TEXTURE0, Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position);
1114 : }
1115 0 :
1116 : shader->AssertPointersBound();
1117 :
1118 0 : for (const std::pair<CVertexBuffer* const, StreamBatchElements>& batchIndexBuffer : streamBatch.second)
1119 : {
1120 : batchIndexBuffer.first->UploadIfNeeded(deviceCommandContext);
1121 0 : deviceCommandContext->SetIndexBuffer(batchIndexBuffer.first->GetBuffer());
1122 :
1123 0 : const StreamBatchElements& batch = batchIndexBuffer.second;
1124 0 :
1125 : for (size_t i = 0; i < batch.first.size(); ++i)
1126 0 : deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
1127 0 :
1128 0 : g_Renderer.m_Stats.m_DrawCalls++;
1129 : g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1130 0 : }
1131 0 : }
1132 :
1133 : CVertexBuffer::Unbind(deviceCommandContext);
1134 0 : }
1135 :
1136 0 : void CPatchRData::RenderOutline()
1137 : {
1138 0 : CTerrain* terrain = m_Patch->m_Parent;
1139 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE;
1140 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
1141 :
1142 : CVector3D pos;
1143 0 : std::vector<CVector3D> line;
1144 0 : for (ssize_t i = 0, j = 0; i <= PATCH_SIZE; ++i)
1145 : {
1146 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1147 0 : line.push_back(pos);
1148 : }
1149 : for (ssize_t i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1150 : {
1151 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1152 0 : line.push_back(pos);
1153 : }
1154 0 : for (ssize_t i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1155 : {
1156 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1157 0 : line.push_back(pos);
1158 0 : }
1159 : for (ssize_t i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1160 0 : {
1161 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1162 0 : line.push_back(pos);
1163 : }
1164 0 :
1165 0 : g_Renderer.GetDebugRenderer().DrawLine(line, CColor(0.0f, 0.0f, 1.0f, 1.0f), 0.1f);
1166 : }
1167 0 :
1168 : void CPatchRData::RenderSides(
1169 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
1170 0 : const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader)
1171 : {
1172 0 : PROFILE3("render terrain sides");
1173 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain sides");
1174 0 :
1175 0 : CVertexBuffer* lastVB = nullptr;
1176 : for (CPatchRData* patch : patches)
1177 0 : {
1178 : ENSURE(patch->m_UpdateFlags == 0);
1179 0 : if (!patch->m_VBSides)
1180 0 : continue;
1181 : if (lastVB != patch->m_VBSides->m_Owner)
1182 : {
1183 0 : lastVB = patch->m_VBSides->m_Owner;
1184 0 : SSideVertex *base = (SSideVertex*)patch->m_VBSides->m_Owner->Bind(deviceCommandContext);
1185 :
1186 0 : // setup data pointers
1187 : GLsizei stride = sizeof(SSideVertex);
1188 : shader->VertexPointer(
1189 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position);
1190 0 : }
1191 0 :
1192 : shader->AssertPointersBound();
1193 0 :
1194 0 : deviceCommandContext->Draw(patch->m_VBSides->m_Index, (GLsizei)patch->m_VBSides->m_Count);
1195 :
1196 0 : // bump stats
1197 0 : g_Renderer.m_Stats.m_DrawCalls++;
1198 : g_Renderer.m_Stats.m_TerrainTris += patch->m_VBSides->m_Count / 3;
1199 0 : }
1200 :
1201 0 : CVertexBuffer::Unbind(deviceCommandContext);
1202 0 : }
1203 :
1204 : void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
1205 0 : {
1206 0 : CTerrain* terrain = m_Patch->m_Parent;
1207 0 : const CCamera& camera = *(g_Game->GetView()->GetCamera());
1208 :
1209 : for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1210 0 : {
1211 : for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1212 0 : {
1213 : ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1214 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1215 0 :
1216 0 : CVector3D pos;
1217 : terrain->CalcPosition(gx, gz, pos);
1218 :
1219 0 : // Move a bit towards the center of the tile
1220 0 : pos.X += TERRAIN_TILE_SIZE/4.f;
1221 : pos.Z += TERRAIN_TILE_SIZE/4.f;
1222 0 :
1223 : float x, y;
1224 0 : camera.GetScreenCoordinates(pos, x, y);
1225 0 :
1226 : textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1227 0 : }
1228 : }
1229 0 : }
1230 :
1231 0 : //
1232 0 : // Water build and rendering
1233 : //
1234 0 :
1235 0 : // Build vertex buffer for water vertices over our patch
1236 : void CPatchRData::BuildWater()
1237 : {
1238 0 : PROFILE3("build water");
1239 0 :
1240 : // Number of vertices in each direction in each patch
1241 0 : ENSURE(PATCH_SIZE % water_cell_size == 0);
1242 0 :
1243 : m_VBWater.Reset();
1244 0 : m_VBWaterIndices.Reset();
1245 : m_VBWaterShore.Reset();
1246 : m_VBWaterIndicesShore.Reset();
1247 0 :
1248 : m_WaterBounds.SetEmpty();
1249 :
1250 : // We need to use this to access the water manager or we may not have the
1251 : // actual values but some compiled-in defaults
1252 : CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
1253 : if (!cmpWaterManager)
1254 0 : return;
1255 :
1256 0 : // Build data for water
1257 : std::vector<SWaterVertex> water_vertex_data;
1258 : std::vector<GLushort> water_indices;
1259 0 : u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1260 : memset(water_index_map, 0xFF, sizeof(water_index_map));
1261 0 :
1262 0 : // Build data for shore
1263 0 : std::vector<SWaterVertex> water_vertex_data_shore;
1264 0 : std::vector<GLushort> water_indices_shore;
1265 : u16 water_shore_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1266 0 : memset(water_shore_index_map, 0xFF, sizeof(water_shore_index_map));
1267 :
1268 : const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
1269 :
1270 0 : CPatch* patch = m_Patch;
1271 0 : CTerrain* terrain = patch->m_Parent;
1272 0 :
1273 : ssize_t mapSize = terrain->GetVerticesPerSide();
1274 :
1275 0 : // Top-left coordinates of our patch.
1276 0 : ssize_t px = m_Patch->m_X * PATCH_SIZE;
1277 0 : ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
1278 0 :
1279 : // To whoever implements different water heights, this is a TODO: water height)
1280 : float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
1281 0 :
1282 0 : // The 4 points making a water tile.
1283 0 : int moves[4][2] = {
1284 0 : {0, 0},
1285 : {water_cell_size, 0},
1286 0 : {0, water_cell_size},
1287 : {water_cell_size, water_cell_size}
1288 0 : };
1289 0 : // Where to look for when checking for water for shore tiles.
1290 : int check[10][2] = {
1291 0 : {0, 0},
1292 : {water_cell_size, 0},
1293 : {water_cell_size*2, 0},
1294 0 : {0, water_cell_size},
1295 0 : {0, water_cell_size*2},
1296 : {water_cell_size, water_cell_size},
1297 : {water_cell_size*2, water_cell_size*2},
1298 0 : {-water_cell_size, 0},
1299 : {0, -water_cell_size},
1300 : {-water_cell_size, -water_cell_size}
1301 0 : };
1302 :
1303 : // build vertices, uv, and shader varying
1304 : for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
1305 : {
1306 : for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
1307 : {
1308 0 : // Check that this tile is close to water
1309 : bool nearWater = false;
1310 : for (size_t test = 0; test < 10; ++test)
1311 : if (terrain->GetVertexGroundLevel(x + px + check[test][0], z + pz + check[test][1]) < waterHeight)
1312 : nearWater = true;
1313 : if (!nearWater)
1314 : continue;
1315 :
1316 : // This is actually lying and I should call CcmpTerrain
1317 : /*if (!terrain->IsOnMap(x+x1, z+z1)
1318 : && !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
1319 : && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
1320 : && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
1321 : continue;*/
1322 0 :
1323 : for (int i = 0; i < 4; ++i)
1324 0 : {
1325 : if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1326 : continue;
1327 :
1328 0 : ssize_t xx = x + px + moves[i][0];
1329 0 : ssize_t zz = z + pz + moves[i][1];
1330 0 :
1331 0 : SWaterVertex vertex;
1332 : terrain->CalcPosition(xx,zz, vertex.m_Position);
1333 : float depth = waterHeight - vertex.m_Position.Y;
1334 :
1335 : vertex.m_Position.Y = waterHeight;
1336 :
1337 : m_WaterBounds += vertex.m_Position;
1338 :
1339 : vertex.m_WaterData = CVector2D(waterManager.m_WindStrength[xx + zz*mapSize], depth);
1340 :
1341 0 : water_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data.size());
1342 : water_vertex_data.push_back(vertex);
1343 0 : }
1344 0 : water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1345 : water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
1346 0 : water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1347 0 : water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1348 : water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
1349 0 : water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1350 0 :
1351 0 : // Check id this tile is partly over land.
1352 : // If so add a square over the terrain. This is necessary to render waves that go on shore.
1353 0 : if (terrain->GetVertexGroundLevel(x+px, z+pz) < waterHeight &&
1354 : terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz) < waterHeight &&
1355 0 : terrain->GetVertexGroundLevel(x+px, z+pz+water_cell_size) < waterHeight &&
1356 : terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz+water_cell_size) < waterHeight)
1357 0 : continue;
1358 :
1359 0 : for (int i = 0; i < 4; ++i)
1360 0 : {
1361 : if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1362 0 : continue;
1363 0 : ssize_t xx = x + px + moves[i][0];
1364 0 : ssize_t zz = z + pz + moves[i][1];
1365 0 :
1366 0 : SWaterVertex vertex;
1367 0 : terrain->CalcPosition(xx,zz, vertex.m_Position);
1368 :
1369 : vertex.m_Position.Y += 0.02f;
1370 : m_WaterBounds += vertex.m_Position;
1371 0 :
1372 0 : vertex.m_WaterData = CVector2D(0.0f, -5.0f);
1373 0 :
1374 0 : water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data_shore.size());
1375 : water_vertex_data_shore.push_back(vertex);
1376 : }
1377 0 : if (terrain->GetTriangulationDir(x + px, z + pz))
1378 : {
1379 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1380 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1381 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1382 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1383 : water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1384 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1385 0 : }
1386 : else
1387 0 : {
1388 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1389 : water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1390 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1391 : water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1392 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1393 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1394 : }
1395 0 : }
1396 : }
1397 0 :
1398 0 : // No vertex buffers if no data generated
1399 0 : if (!water_indices.empty())
1400 0 : {
1401 0 : m_VBWater = g_VBMan.AllocateChunk(
1402 0 : sizeof(SWaterVertex), water_vertex_data.size(),
1403 : Renderer::Backend::GL::CBuffer::Type::VERTEX, false,
1404 : nullptr, CVertexBufferManager::Group::WATER);
1405 : m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater.Get(), &water_vertex_data[0]);
1406 0 :
1407 0 : m_VBWaterIndices = g_VBMan.AllocateChunk(
1408 0 : sizeof(GLushort), water_indices.size(),
1409 0 : Renderer::Backend::GL::CBuffer::Type::INDEX, false,
1410 0 : nullptr, CVertexBufferManager::Group::WATER);
1411 0 : m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices.Get(), &water_indices[0]);
1412 : }
1413 :
1414 : if (!water_indices_shore.empty())
1415 : {
1416 : m_VBWaterShore = g_VBMan.AllocateChunk(
1417 0 : sizeof(SWaterVertex), water_vertex_data_shore.size(),
1418 : Renderer::Backend::GL::CBuffer::Type::VERTEX, false,
1419 0 : nullptr, CVertexBufferManager::Group::WATER);
1420 : m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore.Get(), &water_vertex_data_shore[0]);
1421 :
1422 0 : // Construct indices buffer
1423 0 : m_VBWaterIndicesShore = g_VBMan.AllocateChunk(
1424 : sizeof(GLushort), water_indices_shore.size(),
1425 0 : Renderer::Backend::GL::CBuffer::Type::INDEX, false,
1426 : nullptr, CVertexBufferManager::Group::WATER);
1427 : m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore.Get(), &water_indices_shore[0]);
1428 0 : }
1429 0 : }
1430 :
1431 : void CPatchRData::RenderWaterSurface(
1432 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
1433 : const CShaderProgramPtr& shader, const bool bindWaterData)
1434 0 : {
1435 : ASSERT(m_UpdateFlags == 0);
1436 :
1437 0 : if (!m_VBWater)
1438 0 : return;
1439 :
1440 : m_VBWaterIndices->m_Owner->UploadIfNeeded(deviceCommandContext);
1441 0 :
1442 : SWaterVertex* base = reinterpret_cast<SWaterVertex*>(m_VBWater->m_Owner->Bind(deviceCommandContext));
1443 :
1444 0 : // Setup data pointers.
1445 0 : const GLsizei stride = sizeof(SWaterVertex);
1446 : shader->VertexPointer(
1447 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base[m_VBWater->m_Index].m_Position);
1448 : if (bindWaterData)
1449 0 : {
1450 : shader->VertexAttribPointer(
1451 : str_a_waterInfo, Renderer::Backend::Format::R32G32_SFLOAT, false, stride,
1452 : &base[m_VBWater->m_Index].m_WaterData);
1453 0 : }
1454 :
1455 0 : shader->AssertPointersBound();
1456 :
1457 : deviceCommandContext->SetIndexBuffer(m_VBWaterIndices->m_Owner->GetBuffer());
1458 0 : deviceCommandContext->DrawIndexed(m_VBWaterIndices->m_Index, m_VBWaterIndices->m_Count, 0);
1459 :
1460 0 : g_Renderer.m_Stats.m_DrawCalls++;
1461 : g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
1462 :
1463 0 : CVertexBuffer::Unbind(deviceCommandContext);
1464 0 : }
1465 0 :
1466 0 : void CPatchRData::RenderWaterShore(
1467 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
1468 0 : const CShaderProgramPtr& shader)
1469 : {
1470 0 : ASSERT(m_UpdateFlags == 0);
1471 :
1472 : if (!m_VBWaterShore)
1473 0 : return;
1474 :
1475 0 : m_VBWaterIndicesShore->m_Owner->UploadIfNeeded(deviceCommandContext);
1476 0 :
1477 : SWaterVertex* base = reinterpret_cast<SWaterVertex*>(m_VBWaterShore->m_Owner->Bind(deviceCommandContext));
1478 0 :
1479 0 : const GLsizei stride = sizeof(SWaterVertex);
1480 : shader->VertexPointer(
1481 0 : Renderer::Backend::Format::R32G32B32_SFLOAT, stride,
1482 : &base[m_VBWaterShore->m_Index].m_Position);
1483 : shader->VertexAttribPointer(
1484 0 : str_a_waterInfo, Renderer::Backend::Format::R32G32_SFLOAT, false, stride,
1485 : &base[m_VBWaterShore->m_Index].m_WaterData);
1486 :
1487 : shader->AssertPointersBound();
1488 0 :
1489 : deviceCommandContext->SetIndexBuffer(m_VBWaterIndicesShore->m_Owner->GetBuffer());
1490 0 : deviceCommandContext->DrawIndexed(m_VBWaterIndicesShore->m_Index, m_VBWaterIndicesShore->m_Count, 0);
1491 :
1492 : g_Renderer.m_Stats.m_DrawCalls++;
1493 0 : g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;
1494 :
1495 0 : CVertexBuffer::Unbind(deviceCommandContext);
1496 : }
|