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"
37 #include "ps/GameSetup/Config.h"
38 #include "ps/Profile.h"
39 #include "ps/Pyrogenesis.h"
40 #include "ps/VideoMode.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"
55 const ssize_t BlendOffsets
[9][2] = {
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}
85 return g_Renderer
.GetVertexInputLayout(attributes
);
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
);
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
);
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
);
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
);
158 Renderer::Backend::IVertexInputLayout
* CPatchRData::GetWaterSurfaceVertexInputLayout(
159 const bool bindWaterData
)
161 const uint32_t stride
= sizeof(SWaterVertex
);
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
);
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
);
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
)
213 CPatchRData::~CPatchRData() = default;
216 * Represents a blend for a single tile, texture and shape.
220 CTerrainTextureEntry
* m_Texture
;
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
)
230 if (a
.m_Priority
< b
.m_Priority
)
232 if (a
.m_Texture
&& b
.m_Texture
)
233 return a
.m_Texture
->GetTag() > b
.m_Texture
->GetTag();
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
253 std::vector
<STileBlend
> blends
; // back of vector is lowest-priority texture
257 * Represents a batched collection of blends using the same texture.
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
;
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
;
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
);
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
;
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
;
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
;
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)
390 // Otherwise add the new layer, then loop back and start filling it in
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
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
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)
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
)
470 if (alphamapflags
& BLENDMAP_FLIPV
)
474 if (alphamapflags
& BLENDMAP_ROTATE90
)
476 else if (alphamapflags
& BLENDMAP_ROTATE180
)
478 else if (alphamapflags
& BLENDMAP_ROTATE270
)
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
;
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
);
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);
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
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
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();
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
];
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
);
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
));
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
;
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
);
675 terrain
->CalcNormal(ix
, iz
, normal
);
676 vertices
[v
].m_Normal
= normal
;
680 // upload to vertex buffer
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
;
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;
711 terrain
->CalcPosition(gx
, gz
, pos
);
713 // Clamp the height to the water level
714 float waterHeight
= 0.f
;
716 waterHeight
= cmpWaterManager
->GetExactWaterLevel(pos
.X
, pos
.Z
);
717 pos
.Y
= std::max(pos
.Y
, waterHeight
);
726 vertices
.emplace_back(v1
);
727 vertices
.emplace_back(v0
);
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
);
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
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())
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()
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
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)
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
))
831 // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
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
))
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");
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");
884 BatchElements
& batch
= PooledPairGet(
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");
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
);
951 deviceCommandContext
->SetTexture(
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.
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
;
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");
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.)
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
);
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)
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
)
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())
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
);
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
;
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())
1319 deviceCommandContext
->SetVertexInputLayout(vertexInputLayout
);
1321 CVertexBuffer
* lastVB
= nullptr;
1322 for (CPatchRData
* patch
: patches
)
1324 ENSURE(patch
->m_UpdateFlags
== 0);
1325 if (!patch
->m_VBSides
)
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
);
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
;
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
;
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);
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
)
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.
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] = {
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
)
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))
1461 for (int i
= 0; i
< 4; ++i
)
1463 if (water_index_map
[z
+moves
[i
][1]][x
+moves
[i
][0]] != 0xFFFF)
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
)
1497 for (int i
= 0; i
< 4; ++i
)
1499 if (water_shore_index_map
[z
+moves
[i
][1]][x
+moves
[i
][0]] != 0xFFFF)
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]]);
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);
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
)
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;