Line data Source code
1 : /* Copyright (C) 2023 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 : // static
68 6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetBaseVertexInputLayout()
69 : {
70 6 : const uint32_t stride = sizeof(SBaseVertex);
71 6 : 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 6 : return g_Renderer.GetVertexInputLayout(attributes);
86 : }
87 :
88 : // static
89 6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetBlendVertexInputLayout()
90 : {
91 6 : const uint32_t stride = sizeof(SBlendVertex);
92 6 : 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}
109 : }};
110 6 : return g_Renderer.GetVertexInputLayout(attributes);
111 : }
112 :
113 : // static
114 12 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetStreamVertexInputLayout(
115 : const bool bindPositionAsTexCoord)
116 : {
117 12 : const uint32_t stride = sizeof(SBaseVertex);
118 12 : if (bindPositionAsTexCoord)
119 : {
120 6 : 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}
129 : }};
130 6 : return g_Renderer.GetVertexInputLayout(attributes);
131 : }
132 : else
133 : {
134 6 : 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}
139 : }};
140 6 : return g_Renderer.GetVertexInputLayout(attributes);
141 : }
142 : }
143 :
144 : // static
145 6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetSideVertexInputLayout()
146 : {
147 6 : const uint32_t stride = sizeof(SSideVertex);
148 6 : 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}
153 : }};
154 6 : return g_Renderer.GetVertexInputLayout(attributes);
155 : }
156 :
157 : // static
158 12 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetWaterSurfaceVertexInputLayout(
159 : const bool bindWaterData)
160 : {
161 12 : const uint32_t stride = sizeof(SWaterVertex);
162 12 : if (bindWaterData)
163 : {
164 6 : 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}
174 : }};
175 6 : return g_Renderer.GetVertexInputLayout(attributes);
176 : }
177 : else
178 : {
179 6 : 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}
184 : }};
185 6 : return g_Renderer.GetVertexInputLayout(attributes);
186 : }
187 : }
188 :
189 : // static
190 6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetWaterShoreVertexInputLayout()
191 : {
192 6 : const uint32_t stride = sizeof(SWaterVertex);
193 6 : 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}
202 : }};
203 6 : return g_Renderer.GetVertexInputLayout(attributes);
204 : }
205 :
206 0 : CPatchRData::CPatchRData(CPatch* patch, CSimulation2* simulation) :
207 0 : m_Patch(patch), m_Simulation(simulation)
208 : {
209 0 : ENSURE(patch);
210 0 : Build();
211 0 : }
212 :
213 : CPatchRData::~CPatchRData() = default;
214 :
215 : /**
216 : * Represents a blend for a single tile, texture and shape.
217 : */
218 : struct STileBlend
219 : {
220 : CTerrainTextureEntry* m_Texture;
221 : int m_Priority;
222 : u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
223 :
224 : struct DecreasingPriority
225 : {
226 0 : bool operator()(const STileBlend& a, const STileBlend& b) const
227 : {
228 0 : if (a.m_Priority > b.m_Priority)
229 0 : return true;
230 0 : if (a.m_Priority < b.m_Priority)
231 0 : return false;
232 0 : if (a.m_Texture && b.m_Texture)
233 0 : return a.m_Texture->GetTag() > b.m_Texture->GetTag();
234 0 : return false;
235 : }
236 : };
237 :
238 : struct CurrentTile
239 : {
240 0 : bool operator()(const STileBlend& a) const
241 : {
242 0 : return (a.m_TileMask & (1 << 8)) != 0;
243 : }
244 : };
245 : };
246 :
247 : /**
248 : * Represents the ordered collection of blends drawn on a particular tile.
249 : */
250 0 : struct STileBlendStack
251 : {
252 : u8 i, j;
253 : std::vector<STileBlend> blends; // back of vector is lowest-priority texture
254 : };
255 :
256 : /**
257 : * Represents a batched collection of blends using the same texture.
258 : */
259 0 : struct SBlendLayer
260 : {
261 : struct Tile
262 : {
263 : u8 i, j;
264 : u8 shape;
265 : };
266 :
267 : CTerrainTextureEntry* m_Texture;
268 : std::vector<Tile> m_Tiles;
269 : };
270 :
271 0 : void CPatchRData::BuildBlends()
272 : {
273 0 : PROFILE3("build blends");
274 :
275 0 : m_BlendSplats.clear();
276 :
277 0 : std::vector<SBlendVertex> blendVertices;
278 0 : std::vector<u16> blendIndices;
279 :
280 0 : CTerrain* terrain = m_Patch->m_Parent;
281 :
282 0 : std::vector<STileBlendStack> blendStacks;
283 0 : blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
284 :
285 0 : std::vector<STileBlend> blends;
286 0 : blends.reserve(9);
287 :
288 : // For each tile in patch ..
289 0 : for (ssize_t j = 0; j < PATCH_SIZE; ++j)
290 : {
291 0 : for (ssize_t i = 0; i < PATCH_SIZE; ++i)
292 : {
293 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
294 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
295 :
296 0 : blends.clear();
297 :
298 : // Compute a blend for every tile in the 3x3 square around this tile
299 0 : for (size_t n = 0; n < 9; ++n)
300 : {
301 0 : ssize_t ox = gx + BlendOffsets[n][1];
302 0 : ssize_t oz = gz + BlendOffsets[n][0];
303 :
304 0 : CMiniPatch* nmp = terrain->GetTile(ox, oz);
305 0 : if (!nmp)
306 0 : continue;
307 :
308 : STileBlend blend;
309 0 : blend.m_Texture = nmp->GetTextureEntry();
310 0 : blend.m_Priority = nmp->GetPriority();
311 0 : blend.m_TileMask = 1 << n;
312 0 : blends.push_back(blend);
313 : }
314 :
315 : // Sort the blends, highest priority first
316 0 : std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
317 :
318 0 : STileBlendStack blendStack;
319 0 : blendStack.i = i;
320 0 : blendStack.j = j;
321 :
322 : // Put the blends into the tile's stack, merging any adjacent blends with the same texture
323 0 : for (size_t k = 0; k < blends.size(); ++k)
324 : {
325 0 : if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
326 0 : blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
327 : else
328 0 : blendStack.blends.push_back(blends[k]);
329 : }
330 :
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 0 : blendStack.blends.erase(
335 0 : std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
336 0 : blendStack.blends.end());
337 :
338 0 : blendStacks.push_back(blendStack);
339 : }
340 : }
341 :
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.)
346 :
347 0 : std::vector<SBlendLayer> blendLayers;
348 :
349 : while (true)
350 : {
351 0 : if (!blendLayers.empty())
352 : {
353 : // Try to grab as many tiles as possible that match our current layer,
354 : // from off the blend stacks of all the tiles
355 :
356 0 : CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
357 :
358 0 : for (size_t k = 0; k < blendStacks.size(); ++k)
359 : {
360 0 : if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
361 : {
362 0 : SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, (u8)blendStacks[k].blends.back().m_TileMask };
363 0 : blendLayers.back().m_Tiles.push_back(t);
364 0 : blendStacks[k].blends.pop_back();
365 : }
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)
368 : }
369 : }
370 :
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 0 : CTerrainTextureEntry* bestTex = NULL;
375 0 : size_t bestStackSize = 0;
376 :
377 0 : for (size_t k = 0; k < blendStacks.size(); ++k)
378 : {
379 0 : if (blendStacks[k].blends.size() > bestStackSize)
380 : {
381 0 : bestStackSize = blendStacks[k].blends.size();
382 0 : bestTex = blendStacks[k].blends.back().m_Texture;
383 : }
384 : }
385 :
386 : // If all our stacks were empty, we're done
387 0 : if (bestStackSize == 0)
388 0 : break;
389 :
390 : // Otherwise add the new layer, then loop back and start filling it in
391 :
392 0 : SBlendLayer layer;
393 0 : layer.m_Texture = bestTex;
394 0 : blendLayers.push_back(layer);
395 0 : }
396 :
397 : // Now build outgoing splats
398 0 : m_BlendSplats.resize(blendLayers.size());
399 :
400 0 : for (size_t k = 0; k < blendLayers.size(); ++k)
401 : {
402 0 : SSplat& splat = m_BlendSplats[k];
403 0 : splat.m_IndexStart = blendIndices.size();
404 0 : splat.m_Texture = blendLayers[k].m_Texture;
405 :
406 0 : for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
407 : {
408 0 : SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
409 0 : AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
410 : }
411 :
412 0 : splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
413 : }
414 :
415 : // Release existing vertex buffer chunks
416 0 : m_VBBlends.Reset();
417 0 : m_VBBlendIndices.Reset();
418 :
419 0 : if (blendVertices.size())
420 : {
421 : // Construct vertex buffer
422 :
423 0 : m_VBBlends = g_VBMan.AllocateChunk(
424 : sizeof(SBlendVertex), blendVertices.size(),
425 : Renderer::Backend::IBuffer::Type::VERTEX, false,
426 0 : nullptr, CVertexBufferManager::Group::TERRAIN);
427 0 : m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends.Get(), &blendVertices[0]);
428 :
429 : // Update the indices to include the base offset of the vertex data
430 0 : for (size_t k = 0; k < blendIndices.size(); ++k)
431 0 : blendIndices[k] += static_cast<u16>(m_VBBlends->m_Index);
432 :
433 0 : m_VBBlendIndices = g_VBMan.AllocateChunk(
434 : sizeof(u16), blendIndices.size(),
435 : Renderer::Backend::IBuffer::Type::INDEX, false,
436 0 : nullptr, CVertexBufferManager::Group::TERRAIN);
437 0 : m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices.Get(), &blendIndices[0]);
438 : }
439 0 : }
440 :
441 0 : void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
442 : u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
443 : {
444 0 : CTerrain* terrain = m_Patch->m_Parent;
445 :
446 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
447 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
448 :
449 : // uses the current neighbour texture
450 0 : BlendShape8 shape8;
451 0 : for (size_t m = 0; m < 8; ++m)
452 0 : shape8[m] = (shape & (1 << m)) ? 0 : 1;
453 :
454 : // calculate the required alphamap and the required rotation of the alphamap from blendshape
455 : unsigned int alphamapflags;
456 0 : int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
457 :
458 : // now actually render the blend tile (if we need one)
459 0 : if (alphamap == -1)
460 0 : return;
461 :
462 0 : float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0;
463 0 : float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1;
464 0 : float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0;
465 0 : float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1;
466 :
467 0 : if (alphamapflags & BLENDMAP_FLIPU)
468 0 : std::swap(u0, u1);
469 :
470 0 : if (alphamapflags & BLENDMAP_FLIPV)
471 0 : std::swap(v0, v1);
472 :
473 0 : int base = 0;
474 0 : if (alphamapflags & BLENDMAP_ROTATE90)
475 0 : base = 1;
476 0 : else if (alphamapflags & BLENDMAP_ROTATE180)
477 0 : base = 2;
478 0 : else if (alphamapflags & BLENDMAP_ROTATE270)
479 0 : base = 3;
480 :
481 0 : SBlendVertex vtx[4];
482 0 : vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
483 0 : vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
484 0 : vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
485 0 : vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
486 0 : vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
487 0 : vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
488 0 : vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
489 0 : vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
490 :
491 0 : SBlendVertex dst;
492 :
493 0 : CVector3D normal;
494 :
495 0 : u16 index = static_cast<u16>(blendVertices.size());
496 :
497 0 : terrain->CalcPosition(gx, gz, dst.m_Position);
498 0 : terrain->CalcNormal(gx, gz, normal);
499 0 : dst.m_Normal = normal;
500 0 : dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
501 0 : dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
502 0 : blendVertices.push_back(dst);
503 :
504 0 : terrain->CalcPosition(gx + 1, gz, dst.m_Position);
505 0 : terrain->CalcNormal(gx + 1, gz, normal);
506 0 : dst.m_Normal = normal;
507 0 : dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
508 0 : dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
509 0 : blendVertices.push_back(dst);
510 :
511 0 : terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
512 0 : terrain->CalcNormal(gx + 1, gz + 1, normal);
513 0 : dst.m_Normal = normal;
514 0 : dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
515 0 : dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
516 0 : blendVertices.push_back(dst);
517 :
518 0 : terrain->CalcPosition(gx, gz + 1, dst.m_Position);
519 0 : terrain->CalcNormal(gx, gz + 1, normal);
520 0 : dst.m_Normal = normal;
521 0 : dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
522 0 : dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
523 0 : blendVertices.push_back(dst);
524 :
525 0 : bool dir = terrain->GetTriangulationDir(gx, gz);
526 0 : if (dir)
527 : {
528 0 : blendIndices.push_back(index+0);
529 0 : blendIndices.push_back(index+1);
530 0 : blendIndices.push_back(index+3);
531 :
532 0 : blendIndices.push_back(index+1);
533 0 : blendIndices.push_back(index+2);
534 0 : blendIndices.push_back(index+3);
535 : }
536 : else
537 : {
538 0 : blendIndices.push_back(index+0);
539 0 : blendIndices.push_back(index+1);
540 0 : blendIndices.push_back(index+2);
541 :
542 0 : blendIndices.push_back(index+2);
543 0 : blendIndices.push_back(index+3);
544 0 : blendIndices.push_back(index+0);
545 : }
546 : }
547 :
548 0 : void CPatchRData::BuildIndices()
549 : {
550 0 : PROFILE3("build indices");
551 :
552 0 : CTerrain* terrain = m_Patch->m_Parent;
553 :
554 0 : ssize_t px = m_Patch->m_X * PATCH_SIZE;
555 0 : ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
556 :
557 : // must have allocated some vertices before trying to build corresponding indices
558 0 : ENSURE(m_VBBase);
559 :
560 : // number of vertices in each direction in each patch
561 0 : ssize_t vsize=PATCH_SIZE+1;
562 :
563 : // PATCH_SIZE must be 2^8-2 or less to not overflow u16 indices buffer. Thankfully this is always true.
564 0 : ENSURE(vsize*vsize < 65536);
565 :
566 0 : std::vector<unsigned short> indices;
567 0 : indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
568 :
569 : // release existing splats
570 0 : m_Splats.clear();
571 :
572 : // build grid of textures on this patch
573 0 : std::vector<CTerrainTextureEntry*> textures;
574 : CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
575 0 : for (ssize_t j=0;j<PATCH_SIZE;j++) {
576 0 : for (ssize_t i=0;i<PATCH_SIZE;i++) {
577 0 : CTerrainTextureEntry* tex=m_Patch->m_MiniPatches[j][i].GetTextureEntry();
578 0 : texgrid[j][i]=tex;
579 0 : if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
580 0 : textures.push_back(tex);
581 : }
582 : }
583 : }
584 :
585 : // now build base splats from interior textures
586 0 : m_Splats.resize(textures.size());
587 : // build indices for base splats
588 0 : size_t base=m_VBBase->m_Index;
589 :
590 0 : for (size_t k = 0; k < m_Splats.size(); ++k)
591 : {
592 0 : CTerrainTextureEntry* tex = textures[k];
593 :
594 0 : SSplat& splat=m_Splats[k];
595 0 : splat.m_Texture=tex;
596 0 : splat.m_IndexStart=indices.size();
597 :
598 0 : for (ssize_t j = 0; j < PATCH_SIZE; j++)
599 : {
600 0 : for (ssize_t i = 0; i < PATCH_SIZE; i++)
601 : {
602 0 : if (texgrid[j][i] == tex)
603 : {
604 0 : bool dir = terrain->GetTriangulationDir(px+i, pz+j);
605 0 : if (dir)
606 : {
607 0 : indices.push_back(u16(((j+0)*vsize+(i+0))+base));
608 0 : indices.push_back(u16(((j+0)*vsize+(i+1))+base));
609 0 : indices.push_back(u16(((j+1)*vsize+(i+0))+base));
610 :
611 0 : indices.push_back(u16(((j+0)*vsize+(i+1))+base));
612 0 : indices.push_back(u16(((j+1)*vsize+(i+1))+base));
613 0 : indices.push_back(u16(((j+1)*vsize+(i+0))+base));
614 : }
615 : else
616 : {
617 0 : indices.push_back(u16(((j+0)*vsize+(i+0))+base));
618 0 : indices.push_back(u16(((j+0)*vsize+(i+1))+base));
619 0 : indices.push_back(u16(((j+1)*vsize+(i+1))+base));
620 :
621 0 : indices.push_back(u16(((j+1)*vsize+(i+1))+base));
622 0 : indices.push_back(u16(((j+1)*vsize+(i+0))+base));
623 0 : indices.push_back(u16(((j+0)*vsize+(i+0))+base));
624 : }
625 : }
626 : }
627 : }
628 0 : splat.m_IndexCount=indices.size()-splat.m_IndexStart;
629 : }
630 :
631 : // Release existing vertex buffer chunk
632 0 : m_VBBaseIndices.Reset();
633 :
634 0 : ENSURE(indices.size());
635 :
636 : // Construct vertex buffer
637 0 : m_VBBaseIndices = g_VBMan.AllocateChunk(
638 : sizeof(u16), indices.size(),
639 0 : Renderer::Backend::IBuffer::Type::INDEX, false, nullptr, CVertexBufferManager::Group::TERRAIN);
640 0 : m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices.Get(), &indices[0]);
641 0 : }
642 :
643 :
644 0 : void CPatchRData::BuildVertices()
645 : {
646 0 : PROFILE3("build vertices");
647 :
648 : // create both vertices and lighting colors
649 :
650 : // number of vertices in each direction in each patch
651 0 : ssize_t vsize = PATCH_SIZE + 1;
652 :
653 0 : std::vector<SBaseVertex> vertices;
654 0 : vertices.resize(vsize * vsize);
655 :
656 : // get index of this patch
657 0 : ssize_t px = m_Patch->m_X;
658 0 : ssize_t pz = m_Patch->m_Z;
659 :
660 0 : CTerrain* terrain = m_Patch->m_Parent;
661 :
662 : // build vertices
663 0 : for (ssize_t j = 0; j < vsize; ++j)
664 : {
665 0 : for (ssize_t i = 0; i < vsize; ++i)
666 : {
667 0 : ssize_t ix = px * PATCH_SIZE + i;
668 0 : ssize_t iz = pz * PATCH_SIZE + j;
669 0 : ssize_t v = j * vsize + i;
670 :
671 : // calculate vertex data
672 0 : terrain->CalcPosition(ix, iz, vertices[v].m_Position);
673 :
674 0 : CVector3D normal;
675 0 : terrain->CalcNormal(ix, iz, normal);
676 0 : vertices[v].m_Normal = normal;
677 : }
678 : }
679 :
680 : // upload to vertex buffer
681 0 : if (!m_VBBase)
682 : {
683 0 : m_VBBase = g_VBMan.AllocateChunk(
684 0 : sizeof(SBaseVertex), vsize * vsize,
685 : Renderer::Backend::IBuffer::Type::VERTEX, false,
686 0 : nullptr, CVertexBufferManager::Group::TERRAIN);
687 : }
688 :
689 0 : m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase.Get(), &vertices[0]);
690 0 : }
691 :
692 0 : void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
693 : {
694 0 : ssize_t vsize = PATCH_SIZE + 1;
695 0 : CTerrain* terrain = m_Patch->m_Parent;
696 0 : CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
697 :
698 0 : for (ssize_t k = 0; k < vsize; k++)
699 : {
700 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE;
701 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
702 0 : switch (side)
703 : {
704 0 : case CPATCH_SIDE_NEGX: gz += k; break;
705 0 : case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
706 0 : case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
707 0 : case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
708 : }
709 :
710 0 : CVector3D pos;
711 0 : terrain->CalcPosition(gx, gz, pos);
712 :
713 : // Clamp the height to the water level
714 0 : float waterHeight = 0.f;
715 0 : if (cmpWaterManager)
716 0 : waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
717 0 : pos.Y = std::max(pos.Y, waterHeight);
718 :
719 0 : SSideVertex v0, v1;
720 0 : v0.m_Position = pos;
721 0 : v1.m_Position = pos;
722 0 : v1.m_Position.Y = 0;
723 :
724 0 : if (k == 0)
725 : {
726 0 : vertices.emplace_back(v1);
727 0 : vertices.emplace_back(v0);
728 : }
729 0 : if (k > 0)
730 : {
731 0 : const size_t lastIndex = vertices.size() - 1;
732 0 : vertices.emplace_back(v1);
733 0 : vertices.emplace_back(vertices[lastIndex]);
734 0 : vertices.emplace_back(v0);
735 0 : vertices.emplace_back(v1);
736 0 : if (k + 1 < vsize)
737 : {
738 0 : vertices.emplace_back(v1);
739 0 : vertices.emplace_back(v0);
740 : }
741 : }
742 : }
743 0 : }
744 :
745 0 : void CPatchRData::BuildSides()
746 : {
747 0 : PROFILE3("build sides");
748 :
749 0 : std::vector<SSideVertex> sideVertices;
750 :
751 0 : int sideFlags = m_Patch->GetSideFlags();
752 :
753 : // If no sides are enabled, we don't need to do anything
754 0 : if (!sideFlags)
755 0 : return;
756 :
757 : // For each side, generate a tristrip by adding a vertex at ground/water
758 : // level and a vertex underneath at height 0.
759 :
760 0 : if (sideFlags & CPATCH_SIDE_NEGX)
761 0 : BuildSide(sideVertices, CPATCH_SIDE_NEGX);
762 :
763 0 : if (sideFlags & CPATCH_SIDE_POSX)
764 0 : BuildSide(sideVertices, CPATCH_SIDE_POSX);
765 :
766 0 : if (sideFlags & CPATCH_SIDE_NEGZ)
767 0 : BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
768 :
769 0 : if (sideFlags & CPATCH_SIDE_POSZ)
770 0 : BuildSide(sideVertices, CPATCH_SIDE_POSZ);
771 :
772 0 : if (sideVertices.empty())
773 0 : return;
774 :
775 0 : if (!m_VBSides)
776 : {
777 0 : m_VBSides = g_VBMan.AllocateChunk(
778 : sizeof(SSideVertex), sideVertices.size(),
779 : Renderer::Backend::IBuffer::Type::VERTEX, false,
780 0 : nullptr, CVertexBufferManager::Group::DEFAULT);
781 : }
782 0 : m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides.Get(), &sideVertices[0]);
783 : }
784 :
785 0 : void CPatchRData::Build()
786 : {
787 0 : BuildVertices();
788 0 : BuildSides();
789 0 : BuildIndices();
790 0 : BuildBlends();
791 0 : BuildWater();
792 0 : }
793 :
794 0 : void CPatchRData::Update(CSimulation2* simulation)
795 : {
796 0 : m_Simulation = simulation;
797 0 : 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 0 : BuildVertices();
802 0 : BuildSides();
803 0 : BuildIndices();
804 0 : BuildBlends();
805 0 : BuildWater();
806 :
807 0 : m_UpdateFlags=0;
808 : }
809 0 : }
810 :
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.)
814 :
815 : using Arena = Allocators::DynamicArena<1 * MiB>;
816 :
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>>;
820 :
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 0 : typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Arena& arena)
825 : {
826 0 : 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 0 : )).first->second;
829 : }
830 :
831 : // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
832 : template<typename M>
833 0 : typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Arena& arena)
834 : {
835 0 : 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 0 : ))).first->second;
839 : }
840 :
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>>>;
843 :
844 : // Group batches by index buffer
845 : using IndexBufferBatches = PooledBatchMap<CVertexBuffer*, BatchElements>;
846 :
847 : // Group batches by vertex buffer
848 : using VertexBufferBatches = PooledBatchMap<CVertexBuffer*, IndexBufferBatches>;
849 :
850 : // Group batches by texture
851 : using TextureBatches = PooledBatchMap<CTerrainTextureEntry*, VertexBufferBatches>;
852 :
853 : // Group batches by shaders.
854 : using ShaderTechniqueBatches = PooledBatchMap<std::pair<CStrIntern, CShaderDefines>, TextureBatches>;
855 :
856 0 : void CPatchRData::RenderBases(
857 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
858 : Renderer::Backend::IVertexInputLayout* vertexInputLayout,
859 : const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
860 : {
861 0 : PROFILE3("render terrain bases");
862 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain bases");
863 :
864 0 : Arena arena;
865 :
866 0 : ShaderTechniqueBatches batches(ShaderTechniqueBatches::key_compare(), (ShaderTechniqueBatches::allocator_type(arena)));
867 :
868 0 : PROFILE_START("compute batches");
869 :
870 : // Collect all the patches' base splats into their appropriate batches
871 0 : for (size_t i = 0; i < patches.size(); ++i)
872 : {
873 0 : CPatchRData* patch = patches[i];
874 0 : for (size_t j = 0; j < patch->m_Splats.size(); ++j)
875 : {
876 0 : SSplat& splat = patch->m_Splats[j];
877 0 : const CMaterial& material = splat.m_Texture->GetMaterial();
878 0 : if (material.GetShaderEffect().empty())
879 : {
880 0 : LOGERROR("Terrain renderer failed to load shader effect.\n");
881 0 : continue;
882 : }
883 :
884 : BatchElements& batch = PooledPairGet(
885 : PooledMapGet(
886 : PooledMapGet(
887 0 : PooledMapGet(batches, std::make_pair(material.GetShaderEffect(), material.GetShaderDefines()), arena),
888 : splat.m_Texture, arena
889 0 : ),
890 0 : patch->m_VBBase->m_Owner, arena
891 0 : ),
892 0 : patch->m_VBBaseIndices->m_Owner, arena
893 0 : );
894 :
895 0 : batch.first.push_back(splat.m_IndexCount);
896 :
897 0 : batch.second.push_back(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart);
898 : }
899 : }
900 :
901 : PROFILE_END("compute batches");
902 :
903 : // Render each batch
904 0 : for (ShaderTechniqueBatches::iterator itTech = batches.begin(); itTech != batches.end(); ++itTech)
905 : {
906 0 : CShaderDefines defines = context;
907 0 : defines.SetMany(itTech->first.second);
908 0 : CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
909 0 : itTech->first.first, defines);
910 :
911 0 : const int numPasses = techBase->GetNumPasses();
912 0 : for (int pass = 0; pass < numPasses; ++pass)
913 : {
914 0 : deviceCommandContext->SetGraphicsPipelineState(
915 0 : techBase->GetGraphicsPipelineState(pass));
916 0 : deviceCommandContext->BeginPass();
917 0 : Renderer::Backend::IShaderProgram* shader = techBase->GetShader(pass);
918 0 : TerrainRenderer::PrepareShader(deviceCommandContext, shader, shadow);
919 :
920 : const int32_t baseTexBindingSlot =
921 0 : shader->GetBindingSlot(str_baseTex);
922 : const int32_t textureTransformBindingSlot =
923 0 : shader->GetBindingSlot(str_textureTransform);
924 :
925 0 : TextureBatches& textureBatches = itTech->second;
926 0 : for (TextureBatches::iterator itt = textureBatches.begin(); itt != textureBatches.end(); ++itt)
927 : {
928 0 : if (!itt->first->GetMaterial().GetSamplers().empty())
929 : {
930 : const CMaterial::SamplersVector& samplers =
931 0 : itt->first->GetMaterial().GetSamplers();
932 0 : for(const CMaterial::TextureSampler& samp : samplers)
933 0 : samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
934 0 : for(const CMaterial::TextureSampler& samp : samplers)
935 : {
936 0 : deviceCommandContext->SetTexture(
937 0 : shader->GetBindingSlot(samp.Name),
938 0 : samp.Sampler->GetBackendTexture());
939 : }
940 :
941 0 : itt->first->GetMaterial().GetStaticUniforms().BindUniforms(
942 : deviceCommandContext, shader);
943 :
944 0 : float c = itt->first->GetTextureMatrix()[0];
945 0 : float ms = itt->first->GetTextureMatrix()[8];
946 0 : deviceCommandContext->SetUniform(
947 0 : textureTransformBindingSlot, c, ms);
948 : }
949 : else
950 : {
951 0 : deviceCommandContext->SetTexture(
952 : baseTexBindingSlot,
953 0 : g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
954 : }
955 :
956 0 : for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
957 : {
958 0 : ENSURE(!itv->first->GetBuffer()->IsDynamic());
959 :
960 0 : deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
961 :
962 0 : deviceCommandContext->SetVertexBuffer(0, itv->first->GetBuffer(), 0);
963 :
964 0 : for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
965 : {
966 0 : ENSURE(!it->first->GetBuffer()->IsDynamic());
967 0 : deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
968 :
969 0 : BatchElements& batch = it->second;
970 :
971 0 : for (size_t i = 0; i < batch.first.size(); ++i)
972 0 : deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
973 :
974 0 : g_Renderer.m_Stats.m_DrawCalls++;
975 0 : g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
976 : }
977 : }
978 : }
979 0 : deviceCommandContext->EndPass();
980 : }
981 : }
982 0 : }
983 :
984 : /**
985 : * Helper structure for RenderBlends.
986 : */
987 0 : struct SBlendBatch
988 : {
989 0 : SBlendBatch(Arena& arena) :
990 0 : m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
991 : {
992 0 : }
993 :
994 : CTerrainTextureEntry* m_Texture;
995 : CShaderTechniquePtr m_ShaderTech;
996 : VertexBufferBatches m_Batches;
997 : };
998 :
999 : /**
1000 : * Helper structure for RenderBlends.
1001 : */
1002 0 : struct SBlendStackItem
1003 : {
1004 0 : SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
1005 0 : const std::vector<CPatchRData::SSplat>& s, Arena& arena) :
1006 0 : vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
1007 : {
1008 0 : }
1009 :
1010 : using SplatStack = std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Arena>>;
1011 : CVertexBuffer::VBChunk* vertices;
1012 : CVertexBuffer::VBChunk* indices;
1013 : SplatStack splats;
1014 : };
1015 :
1016 0 : void CPatchRData::RenderBlends(
1017 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1018 : Renderer::Backend::IVertexInputLayout* vertexInputLayout,
1019 : const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
1020 : {
1021 0 : PROFILE3("render terrain blends");
1022 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain blends");
1023 :
1024 0 : Arena arena;
1025 :
1026 : using BatchesStack = std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Arena>>;
1027 0 : BatchesStack batches((BatchesStack::allocator_type(arena)));
1028 :
1029 0 : CShaderDefines contextBlend = context;
1030 0 : contextBlend.Add(str_BLEND, str_1);
1031 :
1032 0 : PROFILE_START("compute batches");
1033 :
1034 : // Reserve an arbitrary size that's probably big enough in most cases,
1035 : // to avoid heavy reallocations
1036 0 : batches.reserve(256);
1037 :
1038 : using BlendStacks = std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Arena>>;
1039 0 : BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
1040 0 : blendStacks.reserve(patches.size());
1041 :
1042 : // Extract all the blend splats from each patch
1043 0 : for (size_t i = 0; i < patches.size(); ++i)
1044 : {
1045 0 : CPatchRData* patch = patches[i];
1046 0 : if (!patch->m_BlendSplats.empty())
1047 : {
1048 :
1049 0 : 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 0 : std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
1052 : }
1053 : }
1054 :
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)
1061 : {
1062 0 : if (!batches.empty())
1063 : {
1064 0 : CTerrainTextureEntry* tex = batches.back().m_Texture;
1065 :
1066 0 : for (size_t k = 0; k < blendStacks.size(); ++k)
1067 : {
1068 0 : SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
1069 0 : if (!splats.empty() && splats.back().m_Texture == tex)
1070 : {
1071 0 : CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
1072 0 : CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
1073 :
1074 0 : BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, arena), indices->m_Owner, arena);
1075 0 : batch.first.push_back(splats.back().m_IndexCount);
1076 :
1077 0 : batch.second.push_back(indices->m_Index + splats.back().m_IndexStart);
1078 :
1079 0 : splats.pop_back();
1080 : }
1081 : }
1082 : }
1083 :
1084 0 : CTerrainTextureEntry* bestTex = NULL;
1085 0 : size_t bestStackSize = 0;
1086 :
1087 0 : for (size_t k = 0; k < blendStacks.size(); ++k)
1088 : {
1089 0 : SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
1090 0 : if (splats.size() > bestStackSize)
1091 : {
1092 0 : bestStackSize = splats.size();
1093 0 : bestTex = splats.back().m_Texture;
1094 : }
1095 : }
1096 :
1097 0 : if (bestStackSize == 0)
1098 0 : break;
1099 :
1100 0 : SBlendBatch layer(arena);
1101 0 : layer.m_Texture = bestTex;
1102 0 : if (!bestTex->GetMaterial().GetSamplers().empty())
1103 : {
1104 0 : CShaderDefines defines = contextBlend;
1105 0 : defines.SetMany(bestTex->GetMaterial().GetShaderDefines());
1106 : // TODO: move enabling blend to XML.
1107 0 : const CStrIntern shaderEffect = bestTex->GetMaterial().GetShaderEffect();
1108 0 : if (shaderEffect != str_terrain_base)
1109 0 : ONCE(LOGWARNING("Shader effect '%s' doesn't support semi-transparent terrain rendering.", shaderEffect.c_str()));
1110 0 : layer.m_ShaderTech = g_Renderer.GetShaderManager().LoadEffect(
1111 0 : shaderEffect == str_terrain_base ? str_terrain_blend : shaderEffect, defines);
1112 : }
1113 0 : batches.push_back(layer);
1114 0 : }
1115 :
1116 : PROFILE_END("compute batches");
1117 :
1118 0 : CVertexBuffer* lastVB = nullptr;
1119 0 : Renderer::Backend::IShaderProgram* previousShader = nullptr;
1120 0 : for (BatchesStack::iterator itTechBegin = batches.begin(), itTechEnd = batches.begin(); itTechBegin != batches.end(); itTechBegin = itTechEnd)
1121 : {
1122 0 : while (itTechEnd != batches.end() && itTechEnd->m_ShaderTech == itTechBegin->m_ShaderTech)
1123 0 : ++itTechEnd;
1124 :
1125 0 : const CShaderTechniquePtr& techBase = itTechBegin->m_ShaderTech;
1126 0 : const int numPasses = techBase->GetNumPasses();
1127 0 : for (int pass = 0; pass < numPasses; ++pass)
1128 : {
1129 0 : deviceCommandContext->SetGraphicsPipelineState(
1130 0 : techBase->GetGraphicsPipelineState(pass));
1131 0 : deviceCommandContext->BeginPass();
1132 :
1133 0 : Renderer::Backend::IShaderProgram* shader = techBase->GetShader(pass);
1134 0 : TerrainRenderer::PrepareShader(deviceCommandContext, shader, shadow);
1135 :
1136 0 : Renderer::Backend::ITexture* lastBlendTex = nullptr;
1137 :
1138 : const int32_t baseTexBindingSlot =
1139 0 : shader->GetBindingSlot(str_baseTex);
1140 : const int32_t blendTexBindingSlot =
1141 0 : shader->GetBindingSlot(str_blendTex);
1142 : const int32_t textureTransformBindingSlot =
1143 0 : shader->GetBindingSlot(str_textureTransform);
1144 :
1145 0 : for (BatchesStack::iterator itt = itTechBegin; itt != itTechEnd; ++itt)
1146 : {
1147 0 : if (itt->m_Texture->GetMaterial().GetSamplers().empty())
1148 0 : continue;
1149 :
1150 0 : if (itt->m_Texture)
1151 : {
1152 0 : const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
1153 0 : for (const CMaterial::TextureSampler& samp : samplers)
1154 0 : samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
1155 0 : for (const CMaterial::TextureSampler& samp : samplers)
1156 : {
1157 0 : deviceCommandContext->SetTexture(
1158 0 : shader->GetBindingSlot(samp.Name),
1159 0 : samp.Sampler->GetBackendTexture());
1160 : }
1161 :
1162 0 : Renderer::Backend::ITexture* currentBlendTex = itt->m_Texture->m_TerrainAlpha->second.m_CompositeAlphaMap.get();
1163 0 : if (currentBlendTex != lastBlendTex)
1164 : {
1165 0 : deviceCommandContext->SetTexture(
1166 0 : blendTexBindingSlot, currentBlendTex);
1167 0 : lastBlendTex = currentBlendTex;
1168 : }
1169 :
1170 0 : itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(deviceCommandContext, shader);
1171 :
1172 0 : float c = itt->m_Texture->GetTextureMatrix()[0];
1173 0 : float ms = itt->m_Texture->GetTextureMatrix()[8];
1174 0 : deviceCommandContext->SetUniform(
1175 0 : textureTransformBindingSlot, c, ms);
1176 : }
1177 : else
1178 : {
1179 0 : deviceCommandContext->SetTexture(
1180 0 : baseTexBindingSlot, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
1181 : }
1182 :
1183 0 : for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
1184 : {
1185 : // Rebind the VB only if it changed since the last batch
1186 0 : if (itv->first != lastVB || shader != previousShader)
1187 : {
1188 0 : lastVB = itv->first;
1189 0 : previousShader = shader;
1190 :
1191 0 : ENSURE(!itv->first->GetBuffer()->IsDynamic());
1192 :
1193 0 : deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1194 :
1195 0 : deviceCommandContext->SetVertexBuffer(0, itv->first->GetBuffer(), 0);
1196 : }
1197 :
1198 0 : for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1199 : {
1200 0 : ENSURE(!it->first->GetBuffer()->IsDynamic());
1201 0 : deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
1202 :
1203 0 : BatchElements& batch = it->second;
1204 :
1205 0 : for (size_t i = 0; i < batch.first.size(); ++i)
1206 0 : deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
1207 :
1208 0 : g_Renderer.m_Stats.m_DrawCalls++;
1209 0 : g_Renderer.m_Stats.m_BlendSplats++;
1210 0 : g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1211 : }
1212 : }
1213 : }
1214 0 : deviceCommandContext->EndPass();
1215 : }
1216 : }
1217 0 : }
1218 :
1219 0 : void CPatchRData::RenderStreams(
1220 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1221 : Renderer::Backend::IVertexInputLayout* vertexInputLayout,
1222 : const std::vector<CPatchRData*>& patches)
1223 : {
1224 0 : PROFILE3("render terrain streams");
1225 :
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>>;
1228 :
1229 : // Group batches by index buffer
1230 : using StreamIndexBufferBatches = std::map<CVertexBuffer*, StreamBatchElements>;
1231 :
1232 : // Group batches by vertex buffer
1233 : using StreamVertexBufferBatches = std::map<CVertexBuffer*, StreamIndexBufferBatches>;
1234 :
1235 0 : StreamVertexBufferBatches batches;
1236 :
1237 0 : PROFILE_START("compute batches");
1238 :
1239 : // Collect all the patches into their appropriate batches
1240 0 : for (const CPatchRData* patch : patches)
1241 : {
1242 0 : StreamBatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
1243 :
1244 0 : batch.first.push_back(patch->m_VBBaseIndices->m_Count);
1245 :
1246 0 : batch.second.push_back(patch->m_VBBaseIndices->m_Index);
1247 : }
1248 :
1249 : PROFILE_END("compute batches");
1250 :
1251 0 : deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1252 :
1253 : // Render each batch
1254 0 : for (const std::pair<CVertexBuffer* const, StreamIndexBufferBatches>& streamBatch : batches)
1255 : {
1256 0 : ENSURE(!streamBatch.first->GetBuffer()->IsDynamic());
1257 :
1258 0 : deviceCommandContext->SetVertexBuffer(0, streamBatch.first->GetBuffer(), 0);
1259 :
1260 0 : for (const std::pair<CVertexBuffer* const, StreamBatchElements>& batchIndexBuffer : streamBatch.second)
1261 : {
1262 0 : ENSURE(!batchIndexBuffer.first->GetBuffer()->IsDynamic());
1263 0 : deviceCommandContext->SetIndexBuffer(batchIndexBuffer.first->GetBuffer());
1264 :
1265 0 : const StreamBatchElements& batch = batchIndexBuffer.second;
1266 :
1267 0 : for (size_t i = 0; i < batch.first.size(); ++i)
1268 0 : deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
1269 :
1270 0 : g_Renderer.m_Stats.m_DrawCalls++;
1271 0 : g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1272 : }
1273 : }
1274 0 : }
1275 :
1276 0 : void CPatchRData::RenderOutline()
1277 : {
1278 0 : CTerrain* terrain = m_Patch->m_Parent;
1279 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE;
1280 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
1281 :
1282 0 : CVector3D pos;
1283 0 : std::vector<CVector3D> line;
1284 0 : for (ssize_t i = 0, j = 0; i <= PATCH_SIZE; ++i)
1285 : {
1286 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1287 0 : line.push_back(pos);
1288 : }
1289 0 : for (ssize_t i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1290 : {
1291 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1292 0 : line.push_back(pos);
1293 : }
1294 0 : for (ssize_t i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1295 : {
1296 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1297 0 : line.push_back(pos);
1298 : }
1299 0 : for (ssize_t i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1300 : {
1301 0 : terrain->CalcPosition(gx + i, gz + j, pos);
1302 0 : line.push_back(pos);
1303 : }
1304 :
1305 0 : g_Renderer.GetDebugRenderer().DrawLine(line, CColor(0.0f, 0.0f, 1.0f, 1.0f), 0.1f);
1306 0 : }
1307 :
1308 0 : void CPatchRData::RenderSides(
1309 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1310 : Renderer::Backend::IVertexInputLayout* vertexInputLayout,
1311 : const std::vector<CPatchRData*>& patches)
1312 : {
1313 0 : PROFILE3("render terrain sides");
1314 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain sides");
1315 :
1316 0 : if (patches.empty())
1317 0 : return;
1318 :
1319 0 : deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1320 :
1321 0 : CVertexBuffer* lastVB = nullptr;
1322 0 : for (CPatchRData* patch : patches)
1323 : {
1324 0 : ENSURE(patch->m_UpdateFlags == 0);
1325 0 : if (!patch->m_VBSides)
1326 0 : continue;
1327 0 : if (lastVB != patch->m_VBSides->m_Owner)
1328 : {
1329 0 : lastVB = patch->m_VBSides->m_Owner;
1330 0 : ENSURE(!lastVB->GetBuffer()->IsDynamic());
1331 0 : deviceCommandContext->SetVertexBuffer(0, patch->m_VBSides->m_Owner->GetBuffer(), 0);
1332 : }
1333 :
1334 0 : deviceCommandContext->Draw(patch->m_VBSides->m_Index, patch->m_VBSides->m_Count);
1335 :
1336 : // bump stats
1337 0 : g_Renderer.m_Stats.m_DrawCalls++;
1338 0 : g_Renderer.m_Stats.m_TerrainTris += patch->m_VBSides->m_Count / 3;
1339 : }
1340 : }
1341 :
1342 0 : void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
1343 : {
1344 0 : CTerrain* terrain = m_Patch->m_Parent;
1345 0 : const CCamera& camera = *(g_Game->GetView()->GetCamera());
1346 :
1347 0 : for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1348 : {
1349 0 : for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1350 : {
1351 0 : ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1352 0 : ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1353 :
1354 0 : CVector3D pos;
1355 0 : terrain->CalcPosition(gx, gz, pos);
1356 :
1357 : // Move a bit towards the center of the tile
1358 0 : pos.X += TERRAIN_TILE_SIZE/4.f;
1359 0 : pos.Z += TERRAIN_TILE_SIZE/4.f;
1360 :
1361 : float x, y;
1362 0 : camera.GetScreenCoordinates(pos, x, y);
1363 :
1364 0 : textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1365 : }
1366 : }
1367 0 : }
1368 :
1369 : //
1370 : // Water build and rendering
1371 : //
1372 :
1373 : // Build vertex buffer for water vertices over our patch
1374 0 : void CPatchRData::BuildWater()
1375 : {
1376 0 : PROFILE3("build water");
1377 :
1378 : // Number of vertices in each direction in each patch
1379 : ENSURE(PATCH_SIZE % water_cell_size == 0);
1380 :
1381 0 : m_VBWater.Reset();
1382 0 : m_VBWaterIndices.Reset();
1383 0 : m_VBWaterShore.Reset();
1384 0 : m_VBWaterIndicesShore.Reset();
1385 :
1386 0 : m_WaterBounds.SetEmpty();
1387 :
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 0 : CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
1391 0 : if (!cmpWaterManager)
1392 0 : return;
1393 :
1394 : // Build data for water
1395 0 : std::vector<SWaterVertex> water_vertex_data;
1396 0 : std::vector<u16> water_indices;
1397 : u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1398 0 : memset(water_index_map, 0xFF, sizeof(water_index_map));
1399 :
1400 : // Build data for shore
1401 0 : std::vector<SWaterVertex> water_vertex_data_shore;
1402 0 : std::vector<u16> water_indices_shore;
1403 : u16 water_shore_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
1404 0 : memset(water_shore_index_map, 0xFF, sizeof(water_shore_index_map));
1405 :
1406 0 : const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
1407 :
1408 0 : CPatch* patch = m_Patch;
1409 0 : CTerrain* terrain = patch->m_Parent;
1410 :
1411 0 : ssize_t mapSize = terrain->GetVerticesPerSide();
1412 :
1413 : // Top-left coordinates of our patch.
1414 0 : ssize_t px = m_Patch->m_X * PATCH_SIZE;
1415 0 : ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
1416 :
1417 : // To whoever implements different water heights, this is a TODO: water height)
1418 0 : float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
1419 :
1420 : // The 4 points making a water tile.
1421 0 : int moves[4][2] = {
1422 : {0, 0},
1423 : {water_cell_size, 0},
1424 : {0, water_cell_size},
1425 : {water_cell_size, water_cell_size}
1426 : };
1427 : // Where to look for when checking for water for shore tiles.
1428 0 : 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}
1439 : };
1440 :
1441 : // build vertices, uv, and shader varying
1442 0 : for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
1443 : {
1444 0 : for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
1445 : {
1446 : // Check that this tile is close to water
1447 0 : bool nearWater = false;
1448 0 : for (size_t test = 0; test < 10; ++test)
1449 0 : if (terrain->GetVertexGroundLevel(x + px + check[test][0], z + pz + check[test][1]) < waterHeight)
1450 0 : nearWater = true;
1451 0 : if (!nearWater)
1452 0 : continue;
1453 :
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;*/
1460 :
1461 0 : for (int i = 0; i < 4; ++i)
1462 : {
1463 0 : if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1464 0 : continue;
1465 :
1466 0 : ssize_t xx = x + px + moves[i][0];
1467 0 : ssize_t zz = z + pz + moves[i][1];
1468 :
1469 0 : SWaterVertex vertex;
1470 0 : terrain->CalcPosition(xx,zz, vertex.m_Position);
1471 0 : float depth = waterHeight - vertex.m_Position.Y;
1472 :
1473 0 : vertex.m_Position.Y = waterHeight;
1474 :
1475 0 : m_WaterBounds += vertex.m_Position;
1476 :
1477 0 : vertex.m_WaterData = CVector2D(waterManager.m_WindStrength[xx + zz*mapSize], depth);
1478 :
1479 0 : water_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data.size());
1480 0 : water_vertex_data.push_back(vertex);
1481 : }
1482 0 : water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1483 0 : water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
1484 0 : water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1485 0 : water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1486 0 : water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
1487 0 : water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1488 :
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 0 : if (terrain->GetVertexGroundLevel(x+px, z+pz) < waterHeight &&
1492 0 : terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz) < waterHeight &&
1493 0 : terrain->GetVertexGroundLevel(x+px, z+pz+water_cell_size) < waterHeight &&
1494 0 : terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz+water_cell_size) < waterHeight)
1495 0 : continue;
1496 :
1497 0 : for (int i = 0; i < 4; ++i)
1498 : {
1499 0 : if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1500 0 : continue;
1501 0 : ssize_t xx = x + px + moves[i][0];
1502 0 : ssize_t zz = z + pz + moves[i][1];
1503 :
1504 0 : SWaterVertex vertex;
1505 0 : terrain->CalcPosition(xx,zz, vertex.m_Position);
1506 :
1507 0 : vertex.m_Position.Y += 0.02f;
1508 0 : m_WaterBounds += vertex.m_Position;
1509 :
1510 0 : vertex.m_WaterData = CVector2D(0.0f, -5.0f);
1511 :
1512 0 : water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data_shore.size());
1513 0 : water_vertex_data_shore.push_back(vertex);
1514 : }
1515 0 : if (terrain->GetTriangulationDir(x + px, z + pz))
1516 : {
1517 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1518 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1519 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1520 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1521 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1522 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1523 : }
1524 : else
1525 : {
1526 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1527 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1528 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1529 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1530 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1531 0 : water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1532 : }
1533 : }
1534 : }
1535 :
1536 : // No vertex buffers if no data generated
1537 0 : if (!water_indices.empty())
1538 : {
1539 0 : m_VBWater = g_VBMan.AllocateChunk(
1540 : sizeof(SWaterVertex), water_vertex_data.size(),
1541 : Renderer::Backend::IBuffer::Type::VERTEX, false,
1542 0 : nullptr, CVertexBufferManager::Group::WATER);
1543 0 : m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater.Get(), &water_vertex_data[0]);
1544 :
1545 0 : m_VBWaterIndices = g_VBMan.AllocateChunk(
1546 : sizeof(u16), water_indices.size(),
1547 : Renderer::Backend::IBuffer::Type::INDEX, false,
1548 0 : nullptr, CVertexBufferManager::Group::WATER);
1549 0 : m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices.Get(), &water_indices[0]);
1550 : }
1551 :
1552 0 : if (!water_indices_shore.empty())
1553 : {
1554 0 : m_VBWaterShore = g_VBMan.AllocateChunk(
1555 : sizeof(SWaterVertex), water_vertex_data_shore.size(),
1556 : Renderer::Backend::IBuffer::Type::VERTEX, false,
1557 0 : nullptr, CVertexBufferManager::Group::WATER);
1558 0 : m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore.Get(), &water_vertex_data_shore[0]);
1559 :
1560 : // Construct indices buffer
1561 0 : m_VBWaterIndicesShore = g_VBMan.AllocateChunk(
1562 : sizeof(u16), water_indices_shore.size(),
1563 : Renderer::Backend::IBuffer::Type::INDEX, false,
1564 0 : nullptr, CVertexBufferManager::Group::WATER);
1565 0 : m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore.Get(), &water_indices_shore[0]);
1566 : }
1567 : }
1568 :
1569 0 : void CPatchRData::RenderWaterSurface(
1570 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1571 : Renderer::Backend::IVertexInputLayout* vertexInputLayout)
1572 : {
1573 0 : ASSERT(m_UpdateFlags == 0);
1574 :
1575 0 : if (!m_VBWater)
1576 0 : return;
1577 :
1578 0 : ENSURE(!m_VBWater->m_Owner->GetBuffer()->IsDynamic());
1579 0 : ENSURE(!m_VBWaterIndices->m_Owner->GetBuffer()->IsDynamic());
1580 :
1581 0 : const uint32_t stride = sizeof(SWaterVertex);
1582 0 : const uint32_t firstVertexOffset = m_VBWater->m_Index * stride;
1583 :
1584 0 : deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1585 :
1586 0 : deviceCommandContext->SetVertexBuffer(
1587 0 : 0, m_VBWater->m_Owner->GetBuffer(), firstVertexOffset);
1588 0 : deviceCommandContext->SetIndexBuffer(m_VBWaterIndices->m_Owner->GetBuffer());
1589 :
1590 0 : deviceCommandContext->DrawIndexed(m_VBWaterIndices->m_Index, m_VBWaterIndices->m_Count, 0);
1591 :
1592 0 : g_Renderer.m_Stats.m_DrawCalls++;
1593 0 : g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
1594 : }
1595 :
1596 0 : void CPatchRData::RenderWaterShore(
1597 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
1598 : Renderer::Backend::IVertexInputLayout* vertexInputLayout)
1599 : {
1600 0 : ASSERT(m_UpdateFlags == 0);
1601 :
1602 0 : if (!m_VBWaterShore)
1603 0 : return;
1604 :
1605 0 : ENSURE(!m_VBWaterShore->m_Owner->GetBuffer()->IsDynamic());
1606 0 : ENSURE(!m_VBWaterIndicesShore->m_Owner->GetBuffer()->IsDynamic());
1607 :
1608 0 : const uint32_t stride = sizeof(SWaterVertex);
1609 0 : const uint32_t firstVertexOffset = m_VBWaterShore->m_Index * stride;
1610 :
1611 0 : deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
1612 :
1613 0 : deviceCommandContext->SetVertexBuffer(
1614 0 : 0, m_VBWaterShore->m_Owner->GetBuffer(), firstVertexOffset);
1615 0 : deviceCommandContext->SetIndexBuffer(m_VBWaterIndicesShore->m_Owner->GetBuffer());
1616 :
1617 0 : deviceCommandContext->DrawIndexed(m_VBWaterIndicesShore->m_Index, m_VBWaterIndicesShore->m_Count, 0);
1618 :
1619 0 : g_Renderer.m_Stats.m_DrawCalls++;
1620 0 : g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;
1621 3 : }
|