Merge 'remotes/trunk'
[0ad.git] / source / renderer / PatchRData.cpp
blob064b6173e7090c3bddee9d01e1b09528e16e847f
1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "renderer/PatchRData.h"
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"
51 #include <algorithm>
52 #include <numeric>
53 #include <set>
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 }
67 // static
68 Renderer::Backend::IVertexInputLayout* CPatchRData::GetBaseVertexInputLayout()
70 const uint32_t stride = sizeof(SBaseVertex);
71 const std::array<Renderer::Backend::SVertexAttributeFormat, 3> attributes{{
72 {Renderer::Backend::VertexAttributeStream::POSITION,
73 Renderer::Backend::Format::R32G32B32_SFLOAT,
74 offsetof(SBaseVertex, m_Position), stride,
75 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
76 {Renderer::Backend::VertexAttributeStream::NORMAL,
77 Renderer::Backend::Format::R32G32B32_SFLOAT,
78 offsetof(SBaseVertex, m_Normal), stride,
79 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
80 {Renderer::Backend::VertexAttributeStream::UV0,
81 Renderer::Backend::Format::R32G32B32_SFLOAT,
82 offsetof(SBaseVertex, m_Position), stride,
83 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
84 }};
85 return g_Renderer.GetVertexInputLayout(attributes);
88 // static
89 Renderer::Backend::IVertexInputLayout* CPatchRData::GetBlendVertexInputLayout()
91 const uint32_t stride = sizeof(SBlendVertex);
92 const std::array<Renderer::Backend::SVertexAttributeFormat, 4> attributes{{
93 {Renderer::Backend::VertexAttributeStream::POSITION,
94 Renderer::Backend::Format::R32G32B32_SFLOAT,
95 offsetof(SBlendVertex, m_Position), stride,
96 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
97 {Renderer::Backend::VertexAttributeStream::NORMAL,
98 Renderer::Backend::Format::R32G32B32_SFLOAT,
99 offsetof(SBlendVertex, m_Normal), stride,
100 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
101 {Renderer::Backend::VertexAttributeStream::UV0,
102 Renderer::Backend::Format::R32G32B32_SFLOAT,
103 offsetof(SBlendVertex, m_Position), stride,
104 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
105 {Renderer::Backend::VertexAttributeStream::UV1,
106 Renderer::Backend::Format::R32G32_SFLOAT,
107 offsetof(SBlendVertex, m_AlphaUVs), stride,
108 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
110 return g_Renderer.GetVertexInputLayout(attributes);
113 // static
114 Renderer::Backend::IVertexInputLayout* CPatchRData::GetStreamVertexInputLayout(
115 const bool bindPositionAsTexCoord)
117 const uint32_t stride = sizeof(SBaseVertex);
118 if (bindPositionAsTexCoord)
120 const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
121 {Renderer::Backend::VertexAttributeStream::POSITION,
122 Renderer::Backend::Format::R32G32B32_SFLOAT,
123 offsetof(SBaseVertex, m_Position), stride,
124 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
125 {Renderer::Backend::VertexAttributeStream::UV0,
126 Renderer::Backend::Format::R32G32B32_SFLOAT,
127 offsetof(SBaseVertex, m_Position), stride,
128 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
130 return g_Renderer.GetVertexInputLayout(attributes);
132 else
134 const std::array<Renderer::Backend::SVertexAttributeFormat, 1> attributes{{
135 {Renderer::Backend::VertexAttributeStream::POSITION,
136 Renderer::Backend::Format::R32G32B32_SFLOAT,
137 offsetof(SBaseVertex, m_Position), stride,
138 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
140 return g_Renderer.GetVertexInputLayout(attributes);
144 // static
145 Renderer::Backend::IVertexInputLayout* CPatchRData::GetSideVertexInputLayout()
147 const uint32_t stride = sizeof(SSideVertex);
148 const std::array<Renderer::Backend::SVertexAttributeFormat, 1> attributes{{
149 {Renderer::Backend::VertexAttributeStream::POSITION,
150 Renderer::Backend::Format::R32G32B32_SFLOAT,
151 offsetof(SSideVertex, m_Position), stride,
152 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
154 return g_Renderer.GetVertexInputLayout(attributes);
157 // static
158 Renderer::Backend::IVertexInputLayout* CPatchRData::GetWaterSurfaceVertexInputLayout(
159 const bool bindWaterData)
161 const uint32_t stride = sizeof(SWaterVertex);
162 if (bindWaterData)
164 const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
165 {Renderer::Backend::VertexAttributeStream::POSITION,
166 Renderer::Backend::Format::R32G32B32_SFLOAT,
167 offsetof(SWaterVertex, m_Position), stride,
168 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
169 // UV1 will be used only in case of bindWaterData.
170 {Renderer::Backend::VertexAttributeStream::UV1,
171 Renderer::Backend::Format::R32G32_SFLOAT,
172 offsetof(SWaterVertex, m_WaterData), stride,
173 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
175 return g_Renderer.GetVertexInputLayout(attributes);
177 else
179 const std::array<Renderer::Backend::SVertexAttributeFormat, 1> attributes{{
180 {Renderer::Backend::VertexAttributeStream::POSITION,
181 Renderer::Backend::Format::R32G32B32_SFLOAT,
182 offsetof(SWaterVertex, m_Position), stride,
183 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
185 return g_Renderer.GetVertexInputLayout(attributes);
189 // static
190 Renderer::Backend::IVertexInputLayout* CPatchRData::GetWaterShoreVertexInputLayout()
192 const uint32_t stride = sizeof(SWaterVertex);
193 const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
194 {Renderer::Backend::VertexAttributeStream::POSITION,
195 Renderer::Backend::Format::R32G32B32_SFLOAT,
196 offsetof(SWaterVertex, m_Position), stride,
197 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
198 {Renderer::Backend::VertexAttributeStream::UV1,
199 Renderer::Backend::Format::R32G32_SFLOAT,
200 offsetof(SWaterVertex, m_WaterData), stride,
201 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
203 return g_Renderer.GetVertexInputLayout(attributes);
206 CPatchRData::CPatchRData(CPatch* patch, CSimulation2* simulation) :
207 m_Patch(patch), m_Simulation(simulation)
209 ENSURE(patch);
210 Build();
213 CPatchRData::~CPatchRData() = default;
216 * Represents a blend for a single tile, texture and shape.
218 struct STileBlend
220 CTerrainTextureEntry* m_Texture;
221 int m_Priority;
222 u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
224 struct DecreasingPriority
226 bool operator()(const STileBlend& a, const STileBlend& b) const
228 if (a.m_Priority > b.m_Priority)
229 return true;
230 if (a.m_Priority < b.m_Priority)
231 return false;
232 if (a.m_Texture && b.m_Texture)
233 return a.m_Texture->GetTag() > b.m_Texture->GetTag();
234 return false;
238 struct CurrentTile
240 bool operator()(const STileBlend& a) const
242 return (a.m_TileMask & (1 << 8)) != 0;
248 * Represents the ordered collection of blends drawn on a particular tile.
250 struct STileBlendStack
252 u8 i, j;
253 std::vector<STileBlend> blends; // back of vector is lowest-priority texture
257 * Represents a batched collection of blends using the same texture.
259 struct SBlendLayer
261 struct Tile
263 u8 i, j;
264 u8 shape;
267 CTerrainTextureEntry* m_Texture;
268 std::vector<Tile> m_Tiles;
271 void CPatchRData::BuildBlends()
273 PROFILE3("build blends");
275 m_BlendSplats.clear();
277 std::vector<SBlendVertex> blendVertices;
278 std::vector<u16> blendIndices;
280 CTerrain* terrain = m_Patch->m_Parent;
282 std::vector<STileBlendStack> blendStacks;
283 blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
285 std::vector<STileBlend> blends;
286 blends.reserve(9);
288 // For each tile in patch ..
289 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
291 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
293 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
294 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
296 blends.clear();
298 // Compute a blend for every tile in the 3x3 square around this tile
299 for (size_t n = 0; n < 9; ++n)
301 ssize_t ox = gx + BlendOffsets[n][1];
302 ssize_t oz = gz + BlendOffsets[n][0];
304 CMiniPatch* nmp = terrain->GetTile(ox, oz);
305 if (!nmp)
306 continue;
308 STileBlend blend;
309 blend.m_Texture = nmp->GetTextureEntry();
310 blend.m_Priority = nmp->GetPriority();
311 blend.m_TileMask = 1 << n;
312 blends.push_back(blend);
315 // Sort the blends, highest priority first
316 std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
318 STileBlendStack blendStack;
319 blendStack.i = i;
320 blendStack.j = j;
322 // Put the blends into the tile's stack, merging any adjacent blends with the same texture
323 for (size_t k = 0; k < blends.size(); ++k)
325 if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
326 blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
327 else
328 blendStack.blends.push_back(blends[k]);
331 // Remove blends that are after (i.e. lower priority than) the current tile
332 // (including the current tile), since we don't want to render them on top of
333 // the tile's base texture
334 blendStack.blends.erase(
335 std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
336 blendStack.blends.end());
338 blendStacks.push_back(blendStack);
342 // Given the blend stack per tile, we want to batch together as many blends as possible.
343 // Group them into a series of layers (each of which has a single texture):
344 // (This is effectively a topological sort / linearisation of the partial order induced
345 // by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
347 std::vector<SBlendLayer> blendLayers;
349 while (true)
351 if (!blendLayers.empty())
353 // Try to grab as many tiles as possible that match our current layer,
354 // from off the blend stacks of all the tiles
356 CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
358 for (size_t k = 0; k < blendStacks.size(); ++k)
360 if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
362 SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, (u8)blendStacks[k].blends.back().m_TileMask };
363 blendLayers.back().m_Tiles.push_back(t);
364 blendStacks[k].blends.pop_back();
366 // (We've already merged adjacent entries of the same texture in each stack,
367 // so we don't need to bother looping to check the next entry in this stack again)
371 // We've grabbed as many tiles as possible; now we need to start a new layer.
372 // The new layer's texture could come from the back of any non-empty stack;
373 // choose the longest stack as a heuristic to reduce the number of layers
374 CTerrainTextureEntry* bestTex = NULL;
375 size_t bestStackSize = 0;
377 for (size_t k = 0; k < blendStacks.size(); ++k)
379 if (blendStacks[k].blends.size() > bestStackSize)
381 bestStackSize = blendStacks[k].blends.size();
382 bestTex = blendStacks[k].blends.back().m_Texture;
386 // If all our stacks were empty, we're done
387 if (bestStackSize == 0)
388 break;
390 // Otherwise add the new layer, then loop back and start filling it in
392 SBlendLayer layer;
393 layer.m_Texture = bestTex;
394 blendLayers.push_back(layer);
397 // Now build outgoing splats
398 m_BlendSplats.resize(blendLayers.size());
400 for (size_t k = 0; k < blendLayers.size(); ++k)
402 SSplat& splat = m_BlendSplats[k];
403 splat.m_IndexStart = blendIndices.size();
404 splat.m_Texture = blendLayers[k].m_Texture;
406 for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
408 SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
409 AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
412 splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
415 // Release existing vertex buffer chunks
416 m_VBBlends.Reset();
417 m_VBBlendIndices.Reset();
419 if (blendVertices.size())
421 // Construct vertex buffer
423 m_VBBlends = g_VBMan.AllocateChunk(
424 sizeof(SBlendVertex), blendVertices.size(),
425 Renderer::Backend::IBuffer::Type::VERTEX, false,
426 nullptr, CVertexBufferManager::Group::TERRAIN);
427 m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends.Get(), &blendVertices[0]);
429 // Update the indices to include the base offset of the vertex data
430 for (size_t k = 0; k < blendIndices.size(); ++k)
431 blendIndices[k] += static_cast<u16>(m_VBBlends->m_Index);
433 m_VBBlendIndices = g_VBMan.AllocateChunk(
434 sizeof(u16), blendIndices.size(),
435 Renderer::Backend::IBuffer::Type::INDEX, false,
436 nullptr, CVertexBufferManager::Group::TERRAIN);
437 m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices.Get(), &blendIndices[0]);
441 void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
442 u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
444 CTerrain* terrain = m_Patch->m_Parent;
446 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
447 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
449 // uses the current neighbour texture
450 BlendShape8 shape8;
451 for (size_t m = 0; m < 8; ++m)
452 shape8[m] = (shape & (1 << m)) ? 0 : 1;
454 // calculate the required alphamap and the required rotation of the alphamap from blendshape
455 unsigned int alphamapflags;
456 int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
458 // now actually render the blend tile (if we need one)
459 if (alphamap == -1)
460 return;
462 float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0;
463 float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1;
464 float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0;
465 float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1;
467 if (alphamapflags & BLENDMAP_FLIPU)
468 std::swap(u0, u1);
470 if (alphamapflags & BLENDMAP_FLIPV)
471 std::swap(v0, v1);
473 int base = 0;
474 if (alphamapflags & BLENDMAP_ROTATE90)
475 base = 1;
476 else if (alphamapflags & BLENDMAP_ROTATE180)
477 base = 2;
478 else if (alphamapflags & BLENDMAP_ROTATE270)
479 base = 3;
481 SBlendVertex vtx[4];
482 vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
483 vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
484 vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
485 vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
486 vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
487 vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
488 vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
489 vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
491 SBlendVertex dst;
493 CVector3D normal;
495 u16 index = static_cast<u16>(blendVertices.size());
497 terrain->CalcPosition(gx, gz, dst.m_Position);
498 terrain->CalcNormal(gx, gz, normal);
499 dst.m_Normal = normal;
500 dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
501 dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
502 blendVertices.push_back(dst);
504 terrain->CalcPosition(gx + 1, gz, dst.m_Position);
505 terrain->CalcNormal(gx + 1, gz, normal);
506 dst.m_Normal = normal;
507 dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
508 dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
509 blendVertices.push_back(dst);
511 terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
512 terrain->CalcNormal(gx + 1, gz + 1, normal);
513 dst.m_Normal = normal;
514 dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
515 dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
516 blendVertices.push_back(dst);
518 terrain->CalcPosition(gx, gz + 1, dst.m_Position);
519 terrain->CalcNormal(gx, gz + 1, normal);
520 dst.m_Normal = normal;
521 dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
522 dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
523 blendVertices.push_back(dst);
525 bool dir = terrain->GetTriangulationDir(gx, gz);
526 if (dir)
528 blendIndices.push_back(index+0);
529 blendIndices.push_back(index+1);
530 blendIndices.push_back(index+3);
532 blendIndices.push_back(index+1);
533 blendIndices.push_back(index+2);
534 blendIndices.push_back(index+3);
536 else
538 blendIndices.push_back(index+0);
539 blendIndices.push_back(index+1);
540 blendIndices.push_back(index+2);
542 blendIndices.push_back(index+2);
543 blendIndices.push_back(index+3);
544 blendIndices.push_back(index+0);
548 void CPatchRData::BuildIndices()
550 PROFILE3("build indices");
552 CTerrain* terrain = m_Patch->m_Parent;
554 ssize_t px = m_Patch->m_X * PATCH_SIZE;
555 ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
557 // must have allocated some vertices before trying to build corresponding indices
558 ENSURE(m_VBBase);
560 // number of vertices in each direction in each patch
561 ssize_t vsize=PATCH_SIZE+1;
563 // PATCH_SIZE must be 2^8-2 or less to not overflow u16 indices buffer. Thankfully this is always true.
564 ENSURE(vsize*vsize < 65536);
566 std::vector<unsigned short> indices;
567 indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
569 // release existing splats
570 m_Splats.clear();
572 // build grid of textures on this patch
573 std::vector<CTerrainTextureEntry*> textures;
574 CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
575 for (ssize_t j=0;j<PATCH_SIZE;j++) {
576 for (ssize_t i=0;i<PATCH_SIZE;i++) {
577 CTerrainTextureEntry* tex=m_Patch->m_MiniPatches[j][i].GetTextureEntry();
578 texgrid[j][i]=tex;
579 if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
580 textures.push_back(tex);
585 // now build base splats from interior textures
586 m_Splats.resize(textures.size());
587 // build indices for base splats
588 size_t base=m_VBBase->m_Index;
590 for (size_t k = 0; k < m_Splats.size(); ++k)
592 CTerrainTextureEntry* tex = textures[k];
594 SSplat& splat=m_Splats[k];
595 splat.m_Texture=tex;
596 splat.m_IndexStart=indices.size();
598 for (ssize_t j = 0; j < PATCH_SIZE; j++)
600 for (ssize_t i = 0; i < PATCH_SIZE; i++)
602 if (texgrid[j][i] == tex)
604 bool dir = terrain->GetTriangulationDir(px+i, pz+j);
605 if (dir)
607 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
608 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
609 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
611 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
612 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
613 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
615 else
617 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
618 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
619 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
621 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
622 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
623 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
628 splat.m_IndexCount=indices.size()-splat.m_IndexStart;
631 // Release existing vertex buffer chunk
632 m_VBBaseIndices.Reset();
634 ENSURE(indices.size());
636 // Construct vertex buffer
637 m_VBBaseIndices = g_VBMan.AllocateChunk(
638 sizeof(u16), indices.size(),
639 Renderer::Backend::IBuffer::Type::INDEX, false, nullptr, CVertexBufferManager::Group::TERRAIN);
640 m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices.Get(), &indices[0]);
644 void CPatchRData::BuildVertices()
646 PROFILE3("build vertices");
648 // create both vertices and lighting colors
650 // number of vertices in each direction in each patch
651 ssize_t vsize = PATCH_SIZE + 1;
653 std::vector<SBaseVertex> vertices;
654 vertices.resize(vsize * vsize);
656 // get index of this patch
657 ssize_t px = m_Patch->m_X;
658 ssize_t pz = m_Patch->m_Z;
660 CTerrain* terrain = m_Patch->m_Parent;
662 // build vertices
663 for (ssize_t j = 0; j < vsize; ++j)
665 for (ssize_t i = 0; i < vsize; ++i)
667 ssize_t ix = px * PATCH_SIZE + i;
668 ssize_t iz = pz * PATCH_SIZE + j;
669 ssize_t v = j * vsize + i;
671 // calculate vertex data
672 terrain->CalcPosition(ix, iz, vertices[v].m_Position);
674 CVector3D normal;
675 terrain->CalcNormal(ix, iz, normal);
676 vertices[v].m_Normal = normal;
680 // upload to vertex buffer
681 if (!m_VBBase)
683 m_VBBase = g_VBMan.AllocateChunk(
684 sizeof(SBaseVertex), vsize * vsize,
685 Renderer::Backend::IBuffer::Type::VERTEX, false,
686 nullptr, CVertexBufferManager::Group::TERRAIN);
689 m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase.Get(), &vertices[0]);
692 void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
694 ssize_t vsize = PATCH_SIZE + 1;
695 CTerrain* terrain = m_Patch->m_Parent;
696 CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
698 for (ssize_t k = 0; k < vsize; k++)
700 ssize_t gx = m_Patch->m_X * PATCH_SIZE;
701 ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
702 switch (side)
704 case CPATCH_SIDE_NEGX: gz += k; break;
705 case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
706 case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
707 case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
710 CVector3D pos;
711 terrain->CalcPosition(gx, gz, pos);
713 // Clamp the height to the water level
714 float waterHeight = 0.f;
715 if (cmpWaterManager)
716 waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
717 pos.Y = std::max(pos.Y, waterHeight);
719 SSideVertex v0, v1;
720 v0.m_Position = pos;
721 v1.m_Position = pos;
722 v1.m_Position.Y = 0;
724 if (k == 0)
726 vertices.emplace_back(v1);
727 vertices.emplace_back(v0);
729 if (k > 0)
731 const size_t lastIndex = vertices.size() - 1;
732 vertices.emplace_back(v1);
733 vertices.emplace_back(vertices[lastIndex]);
734 vertices.emplace_back(v0);
735 vertices.emplace_back(v1);
736 if (k + 1 < vsize)
738 vertices.emplace_back(v1);
739 vertices.emplace_back(v0);
745 void CPatchRData::BuildSides()
747 PROFILE3("build sides");
749 std::vector<SSideVertex> sideVertices;
751 int sideFlags = m_Patch->GetSideFlags();
753 // If no sides are enabled, we don't need to do anything
754 if (!sideFlags)
755 return;
757 // For each side, generate a tristrip by adding a vertex at ground/water
758 // level and a vertex underneath at height 0.
760 if (sideFlags & CPATCH_SIDE_NEGX)
761 BuildSide(sideVertices, CPATCH_SIDE_NEGX);
763 if (sideFlags & CPATCH_SIDE_POSX)
764 BuildSide(sideVertices, CPATCH_SIDE_POSX);
766 if (sideFlags & CPATCH_SIDE_NEGZ)
767 BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
769 if (sideFlags & CPATCH_SIDE_POSZ)
770 BuildSide(sideVertices, CPATCH_SIDE_POSZ);
772 if (sideVertices.empty())
773 return;
775 if (!m_VBSides)
777 m_VBSides = g_VBMan.AllocateChunk(
778 sizeof(SSideVertex), sideVertices.size(),
779 Renderer::Backend::IBuffer::Type::VERTEX, false,
780 nullptr, CVertexBufferManager::Group::DEFAULT);
782 m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides.Get(), &sideVertices[0]);
785 void CPatchRData::Build()
787 BuildVertices();
788 BuildSides();
789 BuildIndices();
790 BuildBlends();
791 BuildWater();
794 void CPatchRData::Update(CSimulation2* simulation)
796 m_Simulation = simulation;
797 if (m_UpdateFlags!=0) {
798 // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
799 // than everything; it's complicated slightly because the blends are dependent
800 // on both vertex and index data
801 BuildVertices();
802 BuildSides();
803 BuildIndices();
804 BuildBlends();
805 BuildWater();
807 m_UpdateFlags=0;
811 // To minimise the cost of memory allocations, everything used for computing
812 // batches uses a arena allocator. (All allocations are short-lived so we can
813 // just throw away the whole arena at the end of each frame.)
815 using Arena = Allocators::DynamicArena<1 * MiB>;
817 // std::map types with appropriate arena allocators and default comparison operator
818 template<class Key, class Value>
819 using PooledBatchMap = std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Arena>>;
821 // Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
822 // use the default constructor in that case)
823 template<typename M>
824 typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Arena& arena)
826 return m.insert(std::make_pair(k,
827 typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(arena))
828 )).first->second;
831 // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
832 template<typename M>
833 typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Arena& arena)
835 return m.insert(std::make_pair(k, std::make_pair(
836 typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(arena)),
837 typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(arena))
838 ))).first->second;
841 // Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
842 using BatchElements = std::pair<std::vector<u32, ProxyAllocator<u32, Arena>>, std::vector<u32, ProxyAllocator<u32, Arena>>>;
844 // Group batches by index buffer
845 using IndexBufferBatches = PooledBatchMap<CVertexBuffer*, BatchElements>;
847 // Group batches by vertex buffer
848 using VertexBufferBatches = PooledBatchMap<CVertexBuffer*, IndexBufferBatches>;
850 // Group batches by texture
851 using TextureBatches = PooledBatchMap<CTerrainTextureEntry*, VertexBufferBatches>;
853 // Group batches by shaders.
854 using ShaderTechniqueBatches = PooledBatchMap<std::pair<CStrIntern, CShaderDefines>, TextureBatches>;
856 void CPatchRData::RenderBases(
857 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
858 Renderer::Backend::IVertexInputLayout* vertexInputLayout,
859 const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
861 PROFILE3("render terrain bases");
862 GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain bases");
864 Arena arena;
866 ShaderTechniqueBatches batches(ShaderTechniqueBatches::key_compare(), (ShaderTechniqueBatches::allocator_type(arena)));
868 PROFILE_START("compute batches");
870 // Collect all the patches' base splats into their appropriate batches
871 for (size_t i = 0; i < patches.size(); ++i)
873 CPatchRData* patch = patches[i];
874 for (size_t j = 0; j < patch->m_Splats.size(); ++j)
876 SSplat& splat = patch->m_Splats[j];
877 const CMaterial& material = splat.m_Texture->GetMaterial();
878 if (material.GetShaderEffect().empty())
880 LOGERROR("Terrain renderer failed to load shader effect.\n");
881 continue;
884 BatchElements& batch = PooledPairGet(
885 PooledMapGet(
886 PooledMapGet(
887 PooledMapGet(batches, std::make_pair(material.GetShaderEffect(), material.GetShaderDefines()), arena),
888 splat.m_Texture, arena
890 patch->m_VBBase->m_Owner, arena
892 patch->m_VBBaseIndices->m_Owner, arena
895 batch.first.push_back(splat.m_IndexCount);
897 batch.second.push_back(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart);
901 PROFILE_END("compute batches");
903 // Render each batch
904 for (ShaderTechniqueBatches::iterator itTech = batches.begin(); itTech != batches.end(); ++itTech)
906 CShaderDefines defines = context;
907 defines.SetMany(itTech->first.second);
908 CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
909 itTech->first.first, defines);
911 const int numPasses = techBase->GetNumPasses();
912 for (int pass = 0; pass < numPasses; ++pass)
914 deviceCommandContext->SetGraphicsPipelineState(
915 techBase->GetGraphicsPipelineState(pass));
916 deviceCommandContext->BeginPass();
917 Renderer::Backend::IShaderProgram* shader = techBase->GetShader(pass);
918 TerrainRenderer::PrepareShader(deviceCommandContext, shader, shadow);
920 const int32_t baseTexBindingSlot =
921 shader->GetBindingSlot(str_baseTex);
922 const int32_t textureTransformBindingSlot =
923 shader->GetBindingSlot(str_textureTransform);
925 TextureBatches& textureBatches = itTech->second;
926 for (TextureBatches::iterator itt = textureBatches.begin(); itt != textureBatches.end(); ++itt)
928 if (!itt->first->GetMaterial().GetSamplers().empty())
930 const CMaterial::SamplersVector& samplers =
931 itt->first->GetMaterial().GetSamplers();
932 for(const CMaterial::TextureSampler& samp : samplers)
933 samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
934 for(const CMaterial::TextureSampler& samp : samplers)
936 deviceCommandContext->SetTexture(
937 shader->GetBindingSlot(samp.Name),
938 samp.Sampler->GetBackendTexture());
941 itt->first->GetMaterial().GetStaticUniforms().BindUniforms(
942 deviceCommandContext, shader);
944 float c = itt->first->GetTextureMatrix()[0];
945 float ms = itt->first->GetTextureMatrix()[8];
946 deviceCommandContext->SetUniform(
947 textureTransformBindingSlot, c, ms);
949 else
951 deviceCommandContext->SetTexture(
952 baseTexBindingSlot,
953 g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
956 for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
958 ENSURE(!itv->first->GetBuffer()->IsDynamic());
960 deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
962 deviceCommandContext->SetVertexBuffer(0, itv->first->GetBuffer(), 0);
964 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
966 ENSURE(!it->first->GetBuffer()->IsDynamic());
967 deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
969 BatchElements& batch = it->second;
971 for (size_t i = 0; i < batch.first.size(); ++i)
972 deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
974 g_Renderer.m_Stats.m_DrawCalls++;
975 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
979 deviceCommandContext->EndPass();
985 * Helper structure for RenderBlends.
987 struct SBlendBatch
989 SBlendBatch(Arena& arena) :
990 m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
994 CTerrainTextureEntry* m_Texture;
995 CShaderTechniquePtr m_ShaderTech;
996 VertexBufferBatches m_Batches;
1000 * Helper structure for RenderBlends.
1002 struct SBlendStackItem
1004 SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
1005 const std::vector<CPatchRData::SSplat>& s, Arena& arena) :
1006 vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
1010 using SplatStack = std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Arena>>;
1011 CVertexBuffer::VBChunk* vertices;
1012 CVertexBuffer::VBChunk* indices;
1013 SplatStack splats;
1016 void CPatchRData::RenderBlends(
1017 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1018 Renderer::Backend::IVertexInputLayout* vertexInputLayout,
1019 const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
1021 PROFILE3("render terrain blends");
1022 GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain blends");
1024 Arena arena;
1026 using BatchesStack = std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Arena>>;
1027 BatchesStack batches((BatchesStack::allocator_type(arena)));
1029 CShaderDefines contextBlend = context;
1030 contextBlend.Add(str_BLEND, str_1);
1032 PROFILE_START("compute batches");
1034 // Reserve an arbitrary size that's probably big enough in most cases,
1035 // to avoid heavy reallocations
1036 batches.reserve(256);
1038 using BlendStacks = std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Arena>>;
1039 BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
1040 blendStacks.reserve(patches.size());
1042 // Extract all the blend splats from each patch
1043 for (size_t i = 0; i < patches.size(); ++i)
1045 CPatchRData* patch = patches[i];
1046 if (!patch->m_BlendSplats.empty())
1049 blendStacks.push_back(SBlendStackItem(patch->m_VBBlends.Get(), patch->m_VBBlendIndices.Get(), patch->m_BlendSplats, arena));
1050 // Reverse the splats so the first to be rendered is at the back of the list
1051 std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
1055 // Rearrange the collection of splats to be grouped by texture, preserving
1056 // order of splats within each patch:
1057 // (This is exactly the same algorithm used in CPatchRData::BuildBlends,
1058 // but applied to patch-sized splats rather than to tile-sized splats;
1059 // see that function for comments on the algorithm.)
1060 while (true)
1062 if (!batches.empty())
1064 CTerrainTextureEntry* tex = batches.back().m_Texture;
1066 for (size_t k = 0; k < blendStacks.size(); ++k)
1068 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
1069 if (!splats.empty() && splats.back().m_Texture == tex)
1071 CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
1072 CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
1074 BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, arena), indices->m_Owner, arena);
1075 batch.first.push_back(splats.back().m_IndexCount);
1077 batch.second.push_back(indices->m_Index + splats.back().m_IndexStart);
1079 splats.pop_back();
1084 CTerrainTextureEntry* bestTex = NULL;
1085 size_t bestStackSize = 0;
1087 for (size_t k = 0; k < blendStacks.size(); ++k)
1089 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
1090 if (splats.size() > bestStackSize)
1092 bestStackSize = splats.size();
1093 bestTex = splats.back().m_Texture;
1097 if (bestStackSize == 0)
1098 break;
1100 SBlendBatch layer(arena);
1101 layer.m_Texture = bestTex;
1102 if (!bestTex->GetMaterial().GetSamplers().empty())
1104 CShaderDefines defines = contextBlend;
1105 defines.SetMany(bestTex->GetMaterial().GetShaderDefines());
1106 // TODO: move enabling blend to XML.
1107 const CStrIntern shaderEffect = bestTex->GetMaterial().GetShaderEffect();
1108 if (shaderEffect != str_terrain_base)
1109 ONCE(LOGWARNING("Shader effect '%s' doesn't support semi-transparent terrain rendering.", shaderEffect.c_str()));
1110 layer.m_ShaderTech = g_Renderer.GetShaderManager().LoadEffect(
1111 shaderEffect == str_terrain_base ? str_terrain_blend : shaderEffect, defines);
1113 batches.push_back(layer);
1116 PROFILE_END("compute batches");
1118 CVertexBuffer* lastVB = nullptr;
1119 Renderer::Backend::IShaderProgram* previousShader = nullptr;
1120 for (BatchesStack::iterator itTechBegin = batches.begin(), itTechEnd = batches.begin(); itTechBegin != batches.end(); itTechBegin = itTechEnd)
1122 while (itTechEnd != batches.end() && itTechEnd->m_ShaderTech == itTechBegin->m_ShaderTech)
1123 ++itTechEnd;
1125 const CShaderTechniquePtr& techBase = itTechBegin->m_ShaderTech;
1126 const int numPasses = techBase->GetNumPasses();
1127 for (int pass = 0; pass < numPasses; ++pass)
1129 deviceCommandContext->SetGraphicsPipelineState(
1130 techBase->GetGraphicsPipelineState(pass));
1131 deviceCommandContext->BeginPass();
1133 Renderer::Backend::IShaderProgram* shader = techBase->GetShader(pass);
1134 TerrainRenderer::PrepareShader(deviceCommandContext, shader, shadow);
1136 Renderer::Backend::ITexture* lastBlendTex = nullptr;
1138 const int32_t baseTexBindingSlot =
1139 shader->GetBindingSlot(str_baseTex);
1140 const int32_t blendTexBindingSlot =
1141 shader->GetBindingSlot(str_blendTex);
1142 const int32_t textureTransformBindingSlot =
1143 shader->GetBindingSlot(str_textureTransform);
1145 for (BatchesStack::iterator itt = itTechBegin; itt != itTechEnd; ++itt)
1147 if (itt->m_Texture->GetMaterial().GetSamplers().empty())
1148 continue;
1150 if (itt->m_Texture)
1152 const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
1153 for (const CMaterial::TextureSampler& samp : samplers)
1154 samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
1155 for (const CMaterial::TextureSampler& samp : samplers)
1157 deviceCommandContext->SetTexture(
1158 shader->GetBindingSlot(samp.Name),
1159 samp.Sampler->GetBackendTexture());
1162 Renderer::Backend::ITexture* currentBlendTex = itt->m_Texture->m_TerrainAlpha->second.m_CompositeAlphaMap.get();
1163 if (currentBlendTex != lastBlendTex)
1165 deviceCommandContext->SetTexture(
1166 blendTexBindingSlot, currentBlendTex);
1167 lastBlendTex = currentBlendTex;
1170 itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(deviceCommandContext, shader);
1172 float c = itt->m_Texture->GetTextureMatrix()[0];
1173 float ms = itt->m_Texture->GetTextureMatrix()[8];
1174 deviceCommandContext->SetUniform(
1175 textureTransformBindingSlot, c, ms);
1177 else
1179 deviceCommandContext->SetTexture(
1180 baseTexBindingSlot, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
1183 for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
1185 // Rebind the VB only if it changed since the last batch
1186 if (itv->first != lastVB || shader != previousShader)
1188 lastVB = itv->first;
1189 previousShader = shader;
1191 ENSURE(!itv->first->GetBuffer()->IsDynamic());
1193 deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1195 deviceCommandContext->SetVertexBuffer(0, itv->first->GetBuffer(), 0);
1198 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1200 ENSURE(!it->first->GetBuffer()->IsDynamic());
1201 deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
1203 BatchElements& batch = it->second;
1205 for (size_t i = 0; i < batch.first.size(); ++i)
1206 deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
1208 g_Renderer.m_Stats.m_DrawCalls++;
1209 g_Renderer.m_Stats.m_BlendSplats++;
1210 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1214 deviceCommandContext->EndPass();
1219 void CPatchRData::RenderStreams(
1220 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1221 Renderer::Backend::IVertexInputLayout* vertexInputLayout,
1222 const std::vector<CPatchRData*>& patches)
1224 PROFILE3("render terrain streams");
1226 // Each batch has a list of index counts, and a list of pointers-to-first-indexes
1227 using StreamBatchElements = std::pair<std::vector<u32>, std::vector<u32>>;
1229 // Group batches by index buffer
1230 using StreamIndexBufferBatches = std::map<CVertexBuffer*, StreamBatchElements>;
1232 // Group batches by vertex buffer
1233 using StreamVertexBufferBatches = std::map<CVertexBuffer*, StreamIndexBufferBatches>;
1235 StreamVertexBufferBatches batches;
1237 PROFILE_START("compute batches");
1239 // Collect all the patches into their appropriate batches
1240 for (const CPatchRData* patch : patches)
1242 StreamBatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
1244 batch.first.push_back(patch->m_VBBaseIndices->m_Count);
1246 batch.second.push_back(patch->m_VBBaseIndices->m_Index);
1249 PROFILE_END("compute batches");
1251 deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1253 // Render each batch
1254 for (const std::pair<CVertexBuffer* const, StreamIndexBufferBatches>& streamBatch : batches)
1256 ENSURE(!streamBatch.first->GetBuffer()->IsDynamic());
1258 deviceCommandContext->SetVertexBuffer(0, streamBatch.first->GetBuffer(), 0);
1260 for (const std::pair<CVertexBuffer* const, StreamBatchElements>& batchIndexBuffer : streamBatch.second)
1262 ENSURE(!batchIndexBuffer.first->GetBuffer()->IsDynamic());
1263 deviceCommandContext->SetIndexBuffer(batchIndexBuffer.first->GetBuffer());
1265 const StreamBatchElements& batch = batchIndexBuffer.second;
1267 for (size_t i = 0; i < batch.first.size(); ++i)
1268 deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
1270 g_Renderer.m_Stats.m_DrawCalls++;
1271 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1276 void CPatchRData::RenderOutline()
1278 CTerrain* terrain = m_Patch->m_Parent;
1279 ssize_t gx = m_Patch->m_X * PATCH_SIZE;
1280 ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
1282 CVector3D pos;
1283 std::vector<CVector3D> line;
1284 for (ssize_t i = 0, j = 0; i <= PATCH_SIZE; ++i)
1286 terrain->CalcPosition(gx + i, gz + j, pos);
1287 line.push_back(pos);
1289 for (ssize_t i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1291 terrain->CalcPosition(gx + i, gz + j, pos);
1292 line.push_back(pos);
1294 for (ssize_t i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1296 terrain->CalcPosition(gx + i, gz + j, pos);
1297 line.push_back(pos);
1299 for (ssize_t i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1301 terrain->CalcPosition(gx + i, gz + j, pos);
1302 line.push_back(pos);
1305 g_Renderer.GetDebugRenderer().DrawLine(line, CColor(0.0f, 0.0f, 1.0f, 1.0f), 0.1f);
1308 void CPatchRData::RenderSides(
1309 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1310 Renderer::Backend::IVertexInputLayout* vertexInputLayout,
1311 const std::vector<CPatchRData*>& patches)
1313 PROFILE3("render terrain sides");
1314 GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain sides");
1316 if (patches.empty())
1317 return;
1319 deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1321 CVertexBuffer* lastVB = nullptr;
1322 for (CPatchRData* patch : patches)
1324 ENSURE(patch->m_UpdateFlags == 0);
1325 if (!patch->m_VBSides)
1326 continue;
1327 if (lastVB != patch->m_VBSides->m_Owner)
1329 lastVB = patch->m_VBSides->m_Owner;
1330 ENSURE(!lastVB->GetBuffer()->IsDynamic());
1331 deviceCommandContext->SetVertexBuffer(0, patch->m_VBSides->m_Owner->GetBuffer(), 0);
1334 deviceCommandContext->Draw(patch->m_VBSides->m_Index, patch->m_VBSides->m_Count);
1336 // bump stats
1337 g_Renderer.m_Stats.m_DrawCalls++;
1338 g_Renderer.m_Stats.m_TerrainTris += patch->m_VBSides->m_Count / 3;
1342 void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
1344 CTerrain* terrain = m_Patch->m_Parent;
1345 const CCamera& camera = *(g_Game->GetView()->GetCamera());
1347 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1349 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1351 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1352 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1354 CVector3D pos;
1355 terrain->CalcPosition(gx, gz, pos);
1357 // Move a bit towards the center of the tile
1358 pos.X += TERRAIN_TILE_SIZE/4.f;
1359 pos.Z += TERRAIN_TILE_SIZE/4.f;
1361 float x, y;
1362 camera.GetScreenCoordinates(pos, x, y);
1364 textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1370 // Water build and rendering
1373 // Build vertex buffer for water vertices over our patch
1374 void CPatchRData::BuildWater()
1376 PROFILE3("build water");
1378 // Number of vertices in each direction in each patch
1379 ENSURE(PATCH_SIZE % water_cell_size == 0);
1381 m_VBWater.Reset();
1382 m_VBWaterIndices.Reset();
1383 m_VBWaterShore.Reset();
1384 m_VBWaterIndicesShore.Reset();
1386 m_WaterBounds.SetEmpty();
1388 // We need to use this to access the water manager or we may not have the
1389 // actual values but some compiled-in defaults
1390 CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
1391 if (!cmpWaterManager)
1392 return;
1394 // Build data for water
1395 std::vector<SWaterVertex> water_vertex_data;
1396 std::vector<u16> water_indices;
1397 u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1398 memset(water_index_map, 0xFF, sizeof(water_index_map));
1400 // Build data for shore
1401 std::vector<SWaterVertex> water_vertex_data_shore;
1402 std::vector<u16> water_indices_shore;
1403 u16 water_shore_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1404 memset(water_shore_index_map, 0xFF, sizeof(water_shore_index_map));
1406 const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
1408 CPatch* patch = m_Patch;
1409 CTerrain* terrain = patch->m_Parent;
1411 ssize_t mapSize = terrain->GetVerticesPerSide();
1413 // Top-left coordinates of our patch.
1414 ssize_t px = m_Patch->m_X * PATCH_SIZE;
1415 ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
1417 // To whoever implements different water heights, this is a TODO: water height)
1418 float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
1420 // The 4 points making a water tile.
1421 int moves[4][2] = {
1422 {0, 0},
1423 {water_cell_size, 0},
1424 {0, water_cell_size},
1425 {water_cell_size, water_cell_size}
1427 // Where to look for when checking for water for shore tiles.
1428 int check[10][2] = {
1429 {0, 0},
1430 {water_cell_size, 0},
1431 {water_cell_size*2, 0},
1432 {0, water_cell_size},
1433 {0, water_cell_size*2},
1434 {water_cell_size, water_cell_size},
1435 {water_cell_size*2, water_cell_size*2},
1436 {-water_cell_size, 0},
1437 {0, -water_cell_size},
1438 {-water_cell_size, -water_cell_size}
1441 // build vertices, uv, and shader varying
1442 for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
1444 for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
1446 // Check that this tile is close to water
1447 bool nearWater = false;
1448 for (size_t test = 0; test < 10; ++test)
1449 if (terrain->GetVertexGroundLevel(x + px + check[test][0], z + pz + check[test][1]) < waterHeight)
1450 nearWater = true;
1451 if (!nearWater)
1452 continue;
1454 // This is actually lying and I should call CcmpTerrain
1455 /*if (!terrain->IsOnMap(x+x1, z+z1)
1456 && !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
1457 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
1458 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
1459 continue;*/
1461 for (int i = 0; i < 4; ++i)
1463 if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1464 continue;
1466 ssize_t xx = x + px + moves[i][0];
1467 ssize_t zz = z + pz + moves[i][1];
1469 SWaterVertex vertex;
1470 terrain->CalcPosition(xx,zz, vertex.m_Position);
1471 float depth = waterHeight - vertex.m_Position.Y;
1473 vertex.m_Position.Y = waterHeight;
1475 m_WaterBounds += vertex.m_Position;
1477 vertex.m_WaterData = CVector2D(waterManager.m_WindStrength[xx + zz*mapSize], depth);
1479 water_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data.size());
1480 water_vertex_data.push_back(vertex);
1482 water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1483 water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
1484 water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1485 water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1486 water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
1487 water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1489 // Check id this tile is partly over land.
1490 // If so add a square over the terrain. This is necessary to render waves that go on shore.
1491 if (terrain->GetVertexGroundLevel(x+px, z+pz) < waterHeight &&
1492 terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz) < waterHeight &&
1493 terrain->GetVertexGroundLevel(x+px, z+pz+water_cell_size) < waterHeight &&
1494 terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz+water_cell_size) < waterHeight)
1495 continue;
1497 for (int i = 0; i < 4; ++i)
1499 if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1500 continue;
1501 ssize_t xx = x + px + moves[i][0];
1502 ssize_t zz = z + pz + moves[i][1];
1504 SWaterVertex vertex;
1505 terrain->CalcPosition(xx,zz, vertex.m_Position);
1507 vertex.m_Position.Y += 0.02f;
1508 m_WaterBounds += vertex.m_Position;
1510 vertex.m_WaterData = CVector2D(0.0f, -5.0f);
1512 water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data_shore.size());
1513 water_vertex_data_shore.push_back(vertex);
1515 if (terrain->GetTriangulationDir(x + px, z + pz))
1517 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1518 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1519 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1520 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1521 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1522 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1524 else
1526 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1527 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1528 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1529 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1530 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1531 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1536 // No vertex buffers if no data generated
1537 if (!water_indices.empty())
1539 m_VBWater = g_VBMan.AllocateChunk(
1540 sizeof(SWaterVertex), water_vertex_data.size(),
1541 Renderer::Backend::IBuffer::Type::VERTEX, false,
1542 nullptr, CVertexBufferManager::Group::WATER);
1543 m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater.Get(), &water_vertex_data[0]);
1545 m_VBWaterIndices = g_VBMan.AllocateChunk(
1546 sizeof(u16), water_indices.size(),
1547 Renderer::Backend::IBuffer::Type::INDEX, false,
1548 nullptr, CVertexBufferManager::Group::WATER);
1549 m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices.Get(), &water_indices[0]);
1552 if (!water_indices_shore.empty())
1554 m_VBWaterShore = g_VBMan.AllocateChunk(
1555 sizeof(SWaterVertex), water_vertex_data_shore.size(),
1556 Renderer::Backend::IBuffer::Type::VERTEX, false,
1557 nullptr, CVertexBufferManager::Group::WATER);
1558 m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore.Get(), &water_vertex_data_shore[0]);
1560 // Construct indices buffer
1561 m_VBWaterIndicesShore = g_VBMan.AllocateChunk(
1562 sizeof(u16), water_indices_shore.size(),
1563 Renderer::Backend::IBuffer::Type::INDEX, false,
1564 nullptr, CVertexBufferManager::Group::WATER);
1565 m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore.Get(), &water_indices_shore[0]);
1569 void CPatchRData::RenderWaterSurface(
1570 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1571 Renderer::Backend::IVertexInputLayout* vertexInputLayout)
1573 ASSERT(m_UpdateFlags == 0);
1575 if (!m_VBWater)
1576 return;
1578 ENSURE(!m_VBWater->m_Owner->GetBuffer()->IsDynamic());
1579 ENSURE(!m_VBWaterIndices->m_Owner->GetBuffer()->IsDynamic());
1581 const uint32_t stride = sizeof(SWaterVertex);
1582 const uint32_t firstVertexOffset = m_VBWater->m_Index * stride;
1584 deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1586 deviceCommandContext->SetVertexBuffer(
1587 0, m_VBWater->m_Owner->GetBuffer(), firstVertexOffset);
1588 deviceCommandContext->SetIndexBuffer(m_VBWaterIndices->m_Owner->GetBuffer());
1590 deviceCommandContext->DrawIndexed(m_VBWaterIndices->m_Index, m_VBWaterIndices->m_Count, 0);
1592 g_Renderer.m_Stats.m_DrawCalls++;
1593 g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
1596 void CPatchRData::RenderWaterShore(
1597 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1598 Renderer::Backend::IVertexInputLayout* vertexInputLayout)
1600 ASSERT(m_UpdateFlags == 0);
1602 if (!m_VBWaterShore)
1603 return;
1605 ENSURE(!m_VBWaterShore->m_Owner->GetBuffer()->IsDynamic());
1606 ENSURE(!m_VBWaterIndicesShore->m_Owner->GetBuffer()->IsDynamic());
1608 const uint32_t stride = sizeof(SWaterVertex);
1609 const uint32_t firstVertexOffset = m_VBWaterShore->m_Index * stride;
1611 deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1613 deviceCommandContext->SetVertexBuffer(
1614 0, m_VBWaterShore->m_Owner->GetBuffer(), firstVertexOffset);
1615 deviceCommandContext->SetIndexBuffer(m_VBWaterIndicesShore->m_Owner->GetBuffer());
1617 deviceCommandContext->DrawIndexed(m_VBWaterIndicesShore->m_Index, m_VBWaterIndicesShore->m_Count, 0);
1619 g_Renderer.m_Stats.m_DrawCalls++;
1620 g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;