1 /* Copyright (C) 2013 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"
24 #include "graphics/GameView.h"
25 #include "graphics/LightEnv.h"
26 #include "graphics/LOSTexture.h"
27 #include "graphics/Patch.h"
28 #include "graphics/ShaderManager.h"
29 #include "graphics/Terrain.h"
30 #include "graphics/TextRenderer.h"
31 #include "lib/alignment.h"
32 #include "lib/allocators/arena.h"
33 #include "maths/MathUtil.h"
34 #include "ps/CLogger.h"
36 #include "ps/Profile.h"
37 #include "ps/Pyrogenesis.h"
39 #include "ps/GameSetup/Config.h"
40 #include "renderer/AlphaMapCalculator.h"
41 #include "renderer/PatchRData.h"
42 #include "renderer/TerrainRenderer.h"
43 #include "renderer/Renderer.h"
44 #include "renderer/WaterManager.h"
45 #include "simulation2/Simulation2.h"
46 #include "simulation2/components/ICmpWaterManager.h"
48 const ssize_t BlendOffsets
[9][2] = {
60 ///////////////////////////////////////////////////////////////////
61 // CPatchRData constructor
62 CPatchRData::CPatchRData(CPatch
* patch
, CSimulation2
* simulation
) :
63 m_Patch(patch
), m_VBSides(0),
64 m_VBBase(0), m_VBBaseIndices(0),
65 m_VBBlends(0), m_VBBlendIndices(0),
66 m_VBWater(0), m_VBWaterIndices(0),
67 m_VBWaterShore(0), m_VBWaterIndicesShore(0),
68 m_Simulation(simulation
)
74 ///////////////////////////////////////////////////////////////////
75 // CPatchRData destructor
76 CPatchRData::~CPatchRData()
78 // release vertex buffer chunks
79 if (m_VBSides
) g_VBMan
.Release(m_VBSides
);
80 if (m_VBBase
) g_VBMan
.Release(m_VBBase
);
81 if (m_VBBaseIndices
) g_VBMan
.Release(m_VBBaseIndices
);
82 if (m_VBBlends
) g_VBMan
.Release(m_VBBlends
);
83 if (m_VBBlendIndices
) g_VBMan
.Release(m_VBBlendIndices
);
84 if (m_VBWater
) g_VBMan
.Release(m_VBWater
);
85 if (m_VBWaterIndices
) g_VBMan
.Release(m_VBWaterIndices
);
86 if (m_VBWaterShore
) g_VBMan
.Release(m_VBWaterShore
);
87 if (m_VBWaterIndicesShore
) g_VBMan
.Release(m_VBWaterIndicesShore
);
91 * Represents a blend for a single tile, texture and shape.
95 CTerrainTextureEntry
* m_Texture
;
97 u16 m_TileMask
; // bit n set if this blend contains neighbour tile BlendOffsets[n]
99 struct DecreasingPriority
101 bool operator()(const STileBlend
& a
, const STileBlend
& b
) const
103 if (a
.m_Priority
> b
.m_Priority
)
105 if (a
.m_Priority
< b
.m_Priority
)
107 if (a
.m_Texture
&& b
.m_Texture
)
108 return a
.m_Texture
->GetTag() > b
.m_Texture
->GetTag();
115 bool operator()(const STileBlend
& a
) const
117 return (a
.m_TileMask
& (1 << 8)) != 0;
123 * Represents the ordered collection of blends drawn on a particular tile.
125 struct STileBlendStack
128 std::vector
<STileBlend
> blends
; // back of vector is lowest-priority texture
132 * Represents a batched collection of blends using the same texture.
142 CTerrainTextureEntry
* m_Texture
;
143 std::vector
<Tile
> m_Tiles
;
146 void CPatchRData::BuildBlends()
148 PROFILE3("build blends");
150 m_BlendSplats
.clear();
152 std::vector
<SBlendVertex
> blendVertices
;
153 std::vector
<u16
> blendIndices
;
155 CTerrain
* terrain
= m_Patch
->m_Parent
;
157 std::vector
<STileBlendStack
> blendStacks
;
158 blendStacks
.reserve(PATCH_SIZE
*PATCH_SIZE
);
160 // For each tile in patch ..
161 for (ssize_t j
= 0; j
< PATCH_SIZE
; ++j
)
163 for (ssize_t i
= 0; i
< PATCH_SIZE
; ++i
)
165 ssize_t gx
= m_Patch
->m_X
* PATCH_SIZE
+ i
;
166 ssize_t gz
= m_Patch
->m_Z
* PATCH_SIZE
+ j
;
168 std::vector
<STileBlend
> blends
;
171 // Compute a blend for every tile in the 3x3 square around this tile
172 for (size_t n
= 0; n
< 9; ++n
)
174 ssize_t ox
= gx
+ BlendOffsets
[n
][1];
175 ssize_t oz
= gz
+ BlendOffsets
[n
][0];
177 CMiniPatch
* nmp
= terrain
->GetTile(ox
, oz
);
182 blend
.m_Texture
= nmp
->GetTextureEntry();
183 blend
.m_Priority
= nmp
->GetPriority();
184 blend
.m_TileMask
= 1 << n
;
185 blends
.push_back(blend
);
188 // Sort the blends, highest priority first
189 std::sort(blends
.begin(), blends
.end(), STileBlend::DecreasingPriority());
191 STileBlendStack blendStack
;
195 // Put the blends into the tile's stack, merging any adjacent blends with the same texture
196 for (size_t k
= 0; k
< blends
.size(); ++k
)
198 if (!blendStack
.blends
.empty() && blendStack
.blends
.back().m_Texture
== blends
[k
].m_Texture
)
199 blendStack
.blends
.back().m_TileMask
|= blends
[k
].m_TileMask
;
201 blendStack
.blends
.push_back(blends
[k
]);
204 // Remove blends that are after (i.e. lower priority than) the current tile
205 // (including the current tile), since we don't want to render them on top of
206 // the tile's base texture
207 blendStack
.blends
.erase(
208 std::find_if(blendStack
.blends
.begin(), blendStack
.blends
.end(), STileBlend::CurrentTile()),
209 blendStack
.blends
.end());
211 blendStacks
.push_back(blendStack
);
215 // Given the blend stack per tile, we want to batch together as many blends as possible.
216 // Group them into a series of layers (each of which has a single texture):
217 // (This is effectively a topological sort / linearisation of the partial order induced
218 // by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
220 std::vector
<SBlendLayer
> blendLayers
;
224 if (!blendLayers
.empty())
226 // Try to grab as many tiles as possible that match our current layer,
227 // from off the blend stacks of all the tiles
229 CTerrainTextureEntry
* tex
= blendLayers
.back().m_Texture
;
231 for (size_t k
= 0; k
< blendStacks
.size(); ++k
)
233 if (!blendStacks
[k
].blends
.empty() && blendStacks
[k
].blends
.back().m_Texture
== tex
)
235 SBlendLayer::Tile t
= { blendStacks
[k
].i
, blendStacks
[k
].j
, (u8
)blendStacks
[k
].blends
.back().m_TileMask
};
236 blendLayers
.back().m_Tiles
.push_back(t
);
237 blendStacks
[k
].blends
.pop_back();
239 // (We've already merged adjacent entries of the same texture in each stack,
240 // so we don't need to bother looping to check the next entry in this stack again)
244 // We've grabbed as many tiles as possible; now we need to start a new layer.
245 // The new layer's texture could come from the back of any non-empty stack;
246 // choose the longest stack as a heuristic to reduce the number of layers
247 CTerrainTextureEntry
* bestTex
= NULL
;
248 size_t bestStackSize
= 0;
250 for (size_t k
= 0; k
< blendStacks
.size(); ++k
)
252 if (blendStacks
[k
].blends
.size() > bestStackSize
)
254 bestStackSize
= blendStacks
[k
].blends
.size();
255 bestTex
= blendStacks
[k
].blends
.back().m_Texture
;
259 // If all our stacks were empty, we're done
260 if (bestStackSize
== 0)
263 // Otherwise add the new layer, then loop back and start filling it in
266 layer
.m_Texture
= bestTex
;
267 blendLayers
.push_back(layer
);
270 // Now build outgoing splats
271 m_BlendSplats
.resize(blendLayers
.size());
273 for (size_t k
= 0; k
< blendLayers
.size(); ++k
)
275 SSplat
& splat
= m_BlendSplats
[k
];
276 splat
.m_IndexStart
= blendIndices
.size();
277 splat
.m_Texture
= blendLayers
[k
].m_Texture
;
279 for (size_t t
= 0; t
< blendLayers
[k
].m_Tiles
.size(); ++t
)
281 SBlendLayer::Tile
& tile
= blendLayers
[k
].m_Tiles
[t
];
282 AddBlend(blendVertices
, blendIndices
, tile
.i
, tile
.j
, tile
.shape
, splat
.m_Texture
);
285 splat
.m_IndexCount
= blendIndices
.size() - splat
.m_IndexStart
;
288 // Release existing vertex buffer chunks
291 g_VBMan
.Release(m_VBBlends
);
295 if (m_VBBlendIndices
)
297 g_VBMan
.Release(m_VBBlendIndices
);
298 m_VBBlendIndices
= 0;
301 if (blendVertices
.size())
303 // Construct vertex buffer
305 m_VBBlends
= g_VBMan
.Allocate(sizeof(SBlendVertex
), blendVertices
.size(), GL_STATIC_DRAW
, GL_ARRAY_BUFFER
);
306 m_VBBlends
->m_Owner
->UpdateChunkVertices(m_VBBlends
, &blendVertices
[0]);
308 // Update the indices to include the base offset of the vertex data
309 for (size_t k
= 0; k
< blendIndices
.size(); ++k
)
310 blendIndices
[k
] += m_VBBlends
->m_Index
;
312 m_VBBlendIndices
= g_VBMan
.Allocate(sizeof(u16
), blendIndices
.size(), GL_STATIC_DRAW
, GL_ELEMENT_ARRAY_BUFFER
);
313 m_VBBlendIndices
->m_Owner
->UpdateChunkVertices(m_VBBlendIndices
, &blendIndices
[0]);
317 void CPatchRData::AddBlend(std::vector
<SBlendVertex
>& blendVertices
, std::vector
<u16
>& blendIndices
,
318 u16 i
, u16 j
, u8 shape
, CTerrainTextureEntry
* texture
)
320 CTerrain
* terrain
= m_Patch
->m_Parent
;
322 ssize_t gx
= m_Patch
->m_X
* PATCH_SIZE
+ i
;
323 ssize_t gz
= m_Patch
->m_Z
* PATCH_SIZE
+ j
;
325 // uses the current neighbour texture
327 for (size_t m
= 0; m
< 8; ++m
)
328 shape8
[m
] = (shape
& (1 << m
)) ? 0 : 1;
330 // calculate the required alphamap and the required rotation of the alphamap from blendshape
331 unsigned int alphamapflags
;
332 int alphamap
= CAlphaMapCalculator::Calculate(shape8
, alphamapflags
);
334 // now actually render the blend tile (if we need one)
338 float u0
= texture
->m_TerrainAlpha
->second
.m_AlphaMapCoords
[alphamap
].u0
;
339 float u1
= texture
->m_TerrainAlpha
->second
.m_AlphaMapCoords
[alphamap
].u1
;
340 float v0
= texture
->m_TerrainAlpha
->second
.m_AlphaMapCoords
[alphamap
].v0
;
341 float v1
= texture
->m_TerrainAlpha
->second
.m_AlphaMapCoords
[alphamap
].v1
;
343 if (alphamapflags
& BLENDMAP_FLIPU
)
346 if (alphamapflags
& BLENDMAP_FLIPV
)
350 if (alphamapflags
& BLENDMAP_ROTATE90
)
352 else if (alphamapflags
& BLENDMAP_ROTATE180
)
354 else if (alphamapflags
& BLENDMAP_ROTATE270
)
358 vtx
[(base
+ 0) % 4].m_AlphaUVs
[0] = u0
;
359 vtx
[(base
+ 0) % 4].m_AlphaUVs
[1] = v0
;
360 vtx
[(base
+ 1) % 4].m_AlphaUVs
[0] = u1
;
361 vtx
[(base
+ 1) % 4].m_AlphaUVs
[1] = v0
;
362 vtx
[(base
+ 2) % 4].m_AlphaUVs
[0] = u1
;
363 vtx
[(base
+ 2) % 4].m_AlphaUVs
[1] = v1
;
364 vtx
[(base
+ 3) % 4].m_AlphaUVs
[0] = u0
;
365 vtx
[(base
+ 3) % 4].m_AlphaUVs
[1] = v1
;
369 const CLightEnv
& lightEnv
= g_Renderer
.GetLightEnv();
372 bool cpuLighting
= (g_Renderer
.GetRenderPath() == CRenderer::RP_FIXED
);
374 size_t index
= blendVertices
.size();
376 terrain
->CalcPosition(gx
, gz
, dst
.m_Position
);
377 terrain
->CalcNormal(gx
, gz
, normal
);
378 dst
.m_Normal
= normal
;
379 dst
.m_DiffuseColor
= cpuLighting
? lightEnv
.EvaluateTerrainDiffuseScaled(normal
) : lightEnv
.EvaluateTerrainDiffuseFactor(normal
);
380 dst
.m_AlphaUVs
[0] = vtx
[0].m_AlphaUVs
[0];
381 dst
.m_AlphaUVs
[1] = vtx
[0].m_AlphaUVs
[1];
382 blendVertices
.push_back(dst
);
384 terrain
->CalcPosition(gx
+ 1, gz
, dst
.m_Position
);
385 terrain
->CalcNormal(gx
+ 1, gz
, normal
);
386 dst
.m_Normal
= normal
;
387 dst
.m_DiffuseColor
= cpuLighting
? lightEnv
.EvaluateTerrainDiffuseScaled(normal
) : lightEnv
.EvaluateTerrainDiffuseFactor(normal
);
388 dst
.m_AlphaUVs
[0] = vtx
[1].m_AlphaUVs
[0];
389 dst
.m_AlphaUVs
[1] = vtx
[1].m_AlphaUVs
[1];
390 blendVertices
.push_back(dst
);
392 terrain
->CalcPosition(gx
+ 1, gz
+ 1, dst
.m_Position
);
393 terrain
->CalcNormal(gx
+ 1, gz
+ 1, normal
);
394 dst
.m_Normal
= normal
;
395 dst
.m_DiffuseColor
= cpuLighting
? lightEnv
.EvaluateTerrainDiffuseScaled(normal
) : lightEnv
.EvaluateTerrainDiffuseFactor(normal
);
396 dst
.m_AlphaUVs
[0] = vtx
[2].m_AlphaUVs
[0];
397 dst
.m_AlphaUVs
[1] = vtx
[2].m_AlphaUVs
[1];
398 blendVertices
.push_back(dst
);
400 terrain
->CalcPosition(gx
, gz
+ 1, dst
.m_Position
);
401 terrain
->CalcNormal(gx
, gz
+ 1, normal
);
402 dst
.m_Normal
= normal
;
403 dst
.m_DiffuseColor
= cpuLighting
? lightEnv
.EvaluateTerrainDiffuseScaled(normal
) : lightEnv
.EvaluateTerrainDiffuseFactor(normal
);
404 dst
.m_AlphaUVs
[0] = vtx
[3].m_AlphaUVs
[0];
405 dst
.m_AlphaUVs
[1] = vtx
[3].m_AlphaUVs
[1];
406 blendVertices
.push_back(dst
);
408 bool dir
= terrain
->GetTriangulationDir(gx
, gz
);
411 blendIndices
.push_back(index
+0);
412 blendIndices
.push_back(index
+1);
413 blendIndices
.push_back(index
+3);
415 blendIndices
.push_back(index
+1);
416 blendIndices
.push_back(index
+2);
417 blendIndices
.push_back(index
+3);
421 blendIndices
.push_back(index
+0);
422 blendIndices
.push_back(index
+1);
423 blendIndices
.push_back(index
+2);
425 blendIndices
.push_back(index
+2);
426 blendIndices
.push_back(index
+3);
427 blendIndices
.push_back(index
+0);
431 void CPatchRData::BuildIndices()
433 PROFILE3("build indices");
435 CTerrain
* terrain
= m_Patch
->m_Parent
;
437 ssize_t px
= m_Patch
->m_X
* PATCH_SIZE
;
438 ssize_t pz
= m_Patch
->m_Z
* PATCH_SIZE
;
440 // must have allocated some vertices before trying to build corresponding indices
443 // number of vertices in each direction in each patch
444 ssize_t vsize
=PATCH_SIZE
+1;
446 // PATCH_SIZE must be 2^8-2 or less to not overflow u16 indices buffer. Thankfully this is always true.
447 ENSURE(vsize
*vsize
< 65536);
449 std::vector
<unsigned short> indices
;
450 indices
.reserve(PATCH_SIZE
* PATCH_SIZE
* 4);
452 // release existing splats
455 // build grid of textures on this patch
456 std::vector
<CTerrainTextureEntry
*> textures
;
457 CTerrainTextureEntry
* texgrid
[PATCH_SIZE
][PATCH_SIZE
];
458 for (ssize_t j
=0;j
<PATCH_SIZE
;j
++) {
459 for (ssize_t i
=0;i
<PATCH_SIZE
;i
++) {
460 CTerrainTextureEntry
* tex
=m_Patch
->m_MiniPatches
[j
][i
].GetTextureEntry();
462 if (std::find(textures
.begin(),textures
.end(),tex
)==textures
.end()) {
463 textures
.push_back(tex
);
468 // now build base splats from interior textures
469 m_Splats
.resize(textures
.size());
470 // build indices for base splats
471 size_t base
=m_VBBase
->m_Index
;
473 for (size_t i
=0;i
<m_Splats
.size();i
++) {
474 CTerrainTextureEntry
* tex
=textures
[i
];
476 SSplat
& splat
=m_Splats
[i
];
478 splat
.m_IndexStart
=indices
.size();
480 for (ssize_t j
= 0; j
< PATCH_SIZE
; j
++)
482 for (ssize_t i
= 0; i
< PATCH_SIZE
; i
++)
484 if (texgrid
[j
][i
] == tex
)
486 bool dir
= terrain
->GetTriangulationDir(px
+i
, pz
+j
);
489 indices
.push_back(u16(((j
+0)*vsize
+(i
+0))+base
));
490 indices
.push_back(u16(((j
+0)*vsize
+(i
+1))+base
));
491 indices
.push_back(u16(((j
+1)*vsize
+(i
+0))+base
));
493 indices
.push_back(u16(((j
+0)*vsize
+(i
+1))+base
));
494 indices
.push_back(u16(((j
+1)*vsize
+(i
+1))+base
));
495 indices
.push_back(u16(((j
+1)*vsize
+(i
+0))+base
));
499 indices
.push_back(u16(((j
+0)*vsize
+(i
+0))+base
));
500 indices
.push_back(u16(((j
+0)*vsize
+(i
+1))+base
));
501 indices
.push_back(u16(((j
+1)*vsize
+(i
+1))+base
));
503 indices
.push_back(u16(((j
+1)*vsize
+(i
+1))+base
));
504 indices
.push_back(u16(((j
+1)*vsize
+(i
+0))+base
));
505 indices
.push_back(u16(((j
+0)*vsize
+(i
+0))+base
));
510 splat
.m_IndexCount
=indices
.size()-splat
.m_IndexStart
;
513 // Release existing vertex buffer chunk
516 g_VBMan
.Release(m_VBBaseIndices
);
520 ENSURE(indices
.size());
522 // Construct vertex buffer
523 m_VBBaseIndices
= g_VBMan
.Allocate(sizeof(u16
), indices
.size(), GL_STATIC_DRAW
, GL_ELEMENT_ARRAY_BUFFER
);
524 m_VBBaseIndices
->m_Owner
->UpdateChunkVertices(m_VBBaseIndices
, &indices
[0]);
528 void CPatchRData::BuildVertices()
530 PROFILE3("build vertices");
532 // create both vertices and lighting colors
534 // number of vertices in each direction in each patch
535 ssize_t vsize
=PATCH_SIZE
+1;
537 std::vector
<SBaseVertex
> vertices
;
538 vertices
.resize(vsize
*vsize
);
540 // get index of this patch
541 ssize_t px
=m_Patch
->m_X
;
542 ssize_t pz
=m_Patch
->m_Z
;
544 CTerrain
* terrain
=m_Patch
->m_Parent
;
545 const CLightEnv
& lightEnv
= g_Renderer
.GetLightEnv();
547 bool cpuLighting
= (g_Renderer
.GetRenderPath() == CRenderer::RP_FIXED
);
550 for (ssize_t j
=0;j
<vsize
;j
++) {
551 for (ssize_t i
=0;i
<vsize
;i
++) {
552 ssize_t ix
=px
*PATCH_SIZE
+i
;
553 ssize_t iz
=pz
*PATCH_SIZE
+j
;
554 ssize_t v
=(j
*vsize
)+i
;
556 // calculate vertex data
557 terrain
->CalcPosition(ix
,iz
,vertices
[v
].m_Position
);
559 // Calculate diffuse lighting for this vertex
560 // Ambient is added by the lighting pass (since ambient is the same
561 // for all vertices, it need not be stored in the vertex structure)
563 terrain
->CalcNormal(ix
,iz
,normal
);
565 vertices
[v
].m_Normal
= normal
;
567 vertices
[v
].m_DiffuseColor
= cpuLighting
? lightEnv
.EvaluateTerrainDiffuseScaled(normal
) : lightEnv
.EvaluateTerrainDiffuseFactor(normal
);
571 // upload to vertex buffer
573 m_VBBase
= g_VBMan
.Allocate(sizeof(SBaseVertex
), vsize
* vsize
, GL_STATIC_DRAW
, GL_ARRAY_BUFFER
);
575 m_VBBase
->m_Owner
->UpdateChunkVertices(m_VBBase
, &vertices
[0]);
578 void CPatchRData::BuildSide(std::vector
<SSideVertex
>& vertices
, CPatchSideFlags side
)
580 ssize_t vsize
= PATCH_SIZE
+ 1;
581 CTerrain
* terrain
= m_Patch
->m_Parent
;
582 CmpPtr
<ICmpWaterManager
> cmpWaterManager(*m_Simulation
, SYSTEM_ENTITY
);
584 for (ssize_t k
= 0; k
< vsize
; k
++)
586 ssize_t gx
= m_Patch
->m_X
* PATCH_SIZE
;
587 ssize_t gz
= m_Patch
->m_Z
* PATCH_SIZE
;
590 case CPATCH_SIDE_NEGX
: gz
+= k
; break;
591 case CPATCH_SIDE_POSX
: gx
+= PATCH_SIZE
; gz
+= PATCH_SIZE
-k
; break;
592 case CPATCH_SIDE_NEGZ
: gx
+= PATCH_SIZE
-k
; break;
593 case CPATCH_SIDE_POSZ
: gz
+= PATCH_SIZE
; gx
+= k
; break;
597 terrain
->CalcPosition(gx
, gz
, pos
);
599 // Clamp the height to the water level
600 float waterHeight
= 0.f
;
602 waterHeight
= cmpWaterManager
->GetExactWaterLevel(pos
.X
, pos
.Z
);
603 pos
.Y
= std::max(pos
.Y
, waterHeight
);
610 // If this is the start of this tristrip, but we've already got a partial
611 // tristrip, add a couple of degenerate triangles to join the strips properly
612 if (k
== 0 && !vertices
.empty())
614 vertices
.push_back(vertices
.back());
615 vertices
.push_back(v1
);
618 // Now add the new triangles
619 vertices
.push_back(v1
);
620 vertices
.push_back(v0
);
624 void CPatchRData::BuildSides()
626 PROFILE3("build sides");
628 std::vector
<SSideVertex
> sideVertices
;
630 int sideFlags
= m_Patch
->GetSideFlags();
632 // If no sides are enabled, we don't need to do anything
636 // For each side, generate a tristrip by adding a vertex at ground/water
637 // level and a vertex underneath at height 0.
639 if (sideFlags
& CPATCH_SIDE_NEGX
)
640 BuildSide(sideVertices
, CPATCH_SIDE_NEGX
);
642 if (sideFlags
& CPATCH_SIDE_POSX
)
643 BuildSide(sideVertices
, CPATCH_SIDE_POSX
);
645 if (sideFlags
& CPATCH_SIDE_NEGZ
)
646 BuildSide(sideVertices
, CPATCH_SIDE_NEGZ
);
648 if (sideFlags
& CPATCH_SIDE_POSZ
)
649 BuildSide(sideVertices
, CPATCH_SIDE_POSZ
);
651 if (sideVertices
.empty())
655 m_VBSides
= g_VBMan
.Allocate(sizeof(SSideVertex
), sideVertices
.size(), GL_STATIC_DRAW
, GL_ARRAY_BUFFER
);
656 m_VBSides
->m_Owner
->UpdateChunkVertices(m_VBSides
, &sideVertices
[0]);
659 void CPatchRData::Build()
668 void CPatchRData::Update(CSimulation2
* simulation
)
670 m_Simulation
= simulation
;
671 if (m_UpdateFlags
!=0) {
672 // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
673 // than everything; it's complicated slightly because the blends are dependent
674 // on both vertex and index data
685 // Types used for glMultiDrawElements batching:
687 // To minimise the cost of memory allocations, everything used for computing
688 // batches uses a arena allocator. (All allocations are short-lived so we can
689 // just throw away the whole arena at the end of each frame.)
691 // std::map types with appropriate arena allocators and default comparison operator
692 #define POOLED_BATCH_MAP(Key, Value) \
693 std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Allocators::DynamicArena > >
695 // Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
696 // use the default constructor in that case)
698 typename
M::mapped_type
& PooledMapGet(M
& m
, const typename
M::key_type
& k
, Allocators::DynamicArena
& arena
)
700 return m
.insert(std::make_pair(k
,
701 typename
M::mapped_type(typename
M::mapped_type::key_compare(), typename
M::mapped_type::allocator_type(arena
))
705 // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
707 typename
M::mapped_type
& PooledPairGet(M
& m
, const typename
M::key_type
& k
, Allocators::DynamicArena
& arena
)
709 return m
.insert(std::make_pair(k
, std::make_pair(
710 typename
M::mapped_type::first_type(typename
M::mapped_type::first_type::allocator_type(arena
)),
711 typename
M::mapped_type::second_type(typename
M::mapped_type::second_type::allocator_type(arena
))
715 // Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
716 typedef std::pair
<std::vector
<GLint
, ProxyAllocator
<GLint
, Allocators::DynamicArena
> >, std::vector
<void*, ProxyAllocator
<void*, Allocators::DynamicArena
> > > BatchElements
;
718 // Group batches by index buffer
719 typedef POOLED_BATCH_MAP(CVertexBuffer
*, BatchElements
) IndexBufferBatches
;
721 // Group batches by vertex buffer
722 typedef POOLED_BATCH_MAP(CVertexBuffer
*, IndexBufferBatches
) VertexBufferBatches
;
724 // Group batches by texture
725 typedef POOLED_BATCH_MAP(CTerrainTextureEntry
*, VertexBufferBatches
) TextureBatches
;
727 void CPatchRData::RenderBases(const std::vector
<CPatchRData
*>& patches
, const CShaderDefines
& context
,
728 ShadowMap
* shadow
, bool isDummyShader
, const CShaderProgramPtr
& dummy
)
730 Allocators::DynamicArena
arena(1 * MiB
);
732 TextureBatches
batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(arena
)));
734 PROFILE_START("compute batches");
736 // Collect all the patches' base splats into their appropriate batches
737 for (size_t i
= 0; i
< patches
.size(); ++i
)
739 CPatchRData
* patch
= patches
[i
];
740 for (size_t j
= 0; j
< patch
->m_Splats
.size(); ++j
)
742 SSplat
& splat
= patch
->m_Splats
[j
];
744 BatchElements
& batch
= PooledPairGet(
746 PooledMapGet(batches
, splat
.m_Texture
, arena
),
747 patch
->m_VBBase
->m_Owner
, arena
749 patch
->m_VBBaseIndices
->m_Owner
, arena
752 batch
.first
.push_back(splat
.m_IndexCount
);
754 u8
* indexBase
= patch
->m_VBBaseIndices
->m_Owner
->GetBindAddress();
755 batch
.second
.push_back(indexBase
+ sizeof(u16
)*(patch
->m_VBBaseIndices
->m_Index
+ splat
.m_IndexStart
));
759 PROFILE_END("compute batches");
762 for (TextureBatches::iterator itt
= batches
.begin(); itt
!= batches
.end(); ++itt
)
766 CShaderTechniquePtr techBase
;
770 if (itt
->first
->GetMaterial().GetShaderEffect().length() == 0)
772 LOGERROR("Terrain renderer failed to load shader effect.\n");
776 techBase
= g_Renderer
.GetShaderManager().LoadEffect(itt
->first
->GetMaterial().GetShaderEffect(),
777 context
, itt
->first
->GetMaterial().GetShaderDefines(0));
779 numPasses
= techBase
->GetNumPasses();
782 for (int pass
= 0; pass
< numPasses
; ++pass
)
786 techBase
->BeginPass(pass
);
787 TerrainRenderer::PrepareShader(techBase
->GetShader(), shadow
);
790 const CShaderProgramPtr
& shader
= isDummyShader
? dummy
: techBase
->GetShader(pass
);
792 if (itt
->first
->GetMaterial().GetSamplers().size() != 0)
794 const CMaterial::SamplersVector
& samplers
= itt
->first
->GetMaterial().GetSamplers();
795 size_t samplersNum
= samplers
.size();
797 for (size_t s
= 0; s
< samplersNum
; ++s
)
799 const CMaterial::TextureSampler
& samp
= samplers
[s
];
800 shader
->BindTexture(samp
.Name
, samp
.Sampler
);
803 itt
->first
->GetMaterial().GetStaticUniforms().BindUniforms(shader
);
808 glMatrixMode(GL_TEXTURE
);
809 glLoadMatrixf(itt
->first
->GetTextureMatrix());
810 glMatrixMode(GL_MODELVIEW
);
815 float c
= itt
->first
->GetTextureMatrix()[0];
816 float ms
= itt
->first
->GetTextureMatrix()[8];
817 shader
->Uniform(str_textureTransform
, c
, ms
, -ms
, 0.f
);
822 shader
->BindTexture(str_baseTex
, g_Renderer
.GetTextureManager().GetErrorTexture());
825 for (VertexBufferBatches::iterator itv
= itt
->second
.begin(); itv
!= itt
->second
.end(); ++itv
)
827 GLsizei stride
= sizeof(SBaseVertex
);
828 SBaseVertex
*base
= (SBaseVertex
*)itv
->first
->Bind();
829 shader
->VertexPointer(3, GL_FLOAT
, stride
, &base
->m_Position
[0]);
830 shader
->ColorPointer(4, GL_UNSIGNED_BYTE
, stride
, &base
->m_DiffuseColor
);
831 shader
->NormalPointer(GL_FLOAT
, stride
, &base
->m_Normal
[0]);
832 shader
->TexCoordPointer(GL_TEXTURE0
, 3, GL_FLOAT
, stride
, &base
->m_Position
[0]);
834 shader
->AssertPointersBound();
836 for (IndexBufferBatches::iterator it
= itv
->second
.begin(); it
!= itv
->second
.end(); ++it
)
840 BatchElements
& batch
= it
->second
;
842 if (!g_Renderer
.m_SkipSubmit
)
844 // Don't use glMultiDrawElements here since it doesn't have a significant
845 // performance impact and it suffers from various driver bugs (e.g. it breaks
846 // in Mesa 7.10 swrast with index VBOs)
847 for (size_t i
= 0; i
< batch
.first
.size(); ++i
)
848 glDrawElements(GL_TRIANGLES
, batch
.first
[i
], GL_UNSIGNED_SHORT
, batch
.second
[i
]);
851 g_Renderer
.m_Stats
.m_DrawCalls
++;
852 g_Renderer
.m_Stats
.m_TerrainTris
+= std::accumulate(batch
.first
.begin(), batch
.first
.end(), 0) / 3;
864 glMatrixMode(GL_TEXTURE
);
866 glMatrixMode(GL_MODELVIEW
);
870 CVertexBuffer::Unbind();
874 * Helper structure for RenderBlends.
878 SBlendBatch(Allocators::DynamicArena
& arena
) :
879 m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena
))
883 CTerrainTextureEntry
* m_Texture
;
884 VertexBufferBatches m_Batches
;
888 * Helper structure for RenderBlends.
890 struct SBlendStackItem
892 SBlendStackItem(CVertexBuffer::VBChunk
* v
, CVertexBuffer::VBChunk
* i
,
893 const std::vector
<CPatchRData::SSplat
>& s
, Allocators::DynamicArena
& arena
) :
894 vertices(v
), indices(i
), splats(s
.begin(), s
.end(), SplatStack::allocator_type(arena
))
898 typedef std::vector
<CPatchRData::SSplat
, ProxyAllocator
<CPatchRData::SSplat
, Allocators::DynamicArena
> > SplatStack
;
899 CVertexBuffer::VBChunk
* vertices
;
900 CVertexBuffer::VBChunk
* indices
;
904 void CPatchRData::RenderBlends(const std::vector
<CPatchRData
*>& patches
, const CShaderDefines
& context
,
905 ShadowMap
* shadow
, bool isDummyShader
, const CShaderProgramPtr
& dummy
)
907 Allocators::DynamicArena
arena(1 * MiB
);
909 typedef std::vector
<SBlendBatch
, ProxyAllocator
<SBlendBatch
, Allocators::DynamicArena
> > BatchesStack
;
910 BatchesStack
batches((BatchesStack::allocator_type(arena
)));
912 CShaderDefines contextBlend
= context
;
913 contextBlend
.Add(str_BLEND
, str_1
);
915 PROFILE_START("compute batches");
917 // Reserve an arbitrary size that's probably big enough in most cases,
918 // to avoid heavy reallocations
919 batches
.reserve(256);
921 typedef std::vector
<SBlendStackItem
, ProxyAllocator
<SBlendStackItem
, Allocators::DynamicArena
> > BlendStacks
;
922 BlendStacks
blendStacks((BlendStacks::allocator_type(arena
)));
923 blendStacks
.reserve(patches
.size());
925 // Extract all the blend splats from each patch
926 for (size_t i
= 0; i
< patches
.size(); ++i
)
928 CPatchRData
* patch
= patches
[i
];
929 if (!patch
->m_BlendSplats
.empty())
932 blendStacks
.push_back(SBlendStackItem(patch
->m_VBBlends
, patch
->m_VBBlendIndices
, patch
->m_BlendSplats
, arena
));
933 // Reverse the splats so the first to be rendered is at the back of the list
934 std::reverse(blendStacks
.back().splats
.begin(), blendStacks
.back().splats
.end());
938 // Rearrange the collection of splats to be grouped by texture, preserving
939 // order of splats within each patch:
940 // (This is exactly the same algorithm used in CPatchRData::BuildBlends,
941 // but applied to patch-sized splats rather than to tile-sized splats;
942 // see that function for comments on the algorithm.)
945 if (!batches
.empty())
947 CTerrainTextureEntry
* tex
= batches
.back().m_Texture
;
949 for (size_t k
= 0; k
< blendStacks
.size(); ++k
)
951 SBlendStackItem::SplatStack
& splats
= blendStacks
[k
].splats
;
952 if (!splats
.empty() && splats
.back().m_Texture
== tex
)
954 CVertexBuffer::VBChunk
* vertices
= blendStacks
[k
].vertices
;
955 CVertexBuffer::VBChunk
* indices
= blendStacks
[k
].indices
;
957 BatchElements
& batch
= PooledPairGet(PooledMapGet(batches
.back().m_Batches
, vertices
->m_Owner
, arena
), indices
->m_Owner
, arena
);
958 batch
.first
.push_back(splats
.back().m_IndexCount
);
960 u8
* indexBase
= indices
->m_Owner
->GetBindAddress();
961 batch
.second
.push_back(indexBase
+ sizeof(u16
)*(indices
->m_Index
+ splats
.back().m_IndexStart
));
968 CTerrainTextureEntry
* bestTex
= NULL
;
969 size_t bestStackSize
= 0;
971 for (size_t k
= 0; k
< blendStacks
.size(); ++k
)
973 SBlendStackItem::SplatStack
& splats
= blendStacks
[k
].splats
;
974 if (splats
.size() > bestStackSize
)
976 bestStackSize
= splats
.size();
977 bestTex
= splats
.back().m_Texture
;
981 if (bestStackSize
== 0)
984 SBlendBatch
layer(arena
);
985 layer
.m_Texture
= bestTex
;
986 batches
.push_back(layer
);
989 PROFILE_END("compute batches");
991 CVertexBuffer
* lastVB
= NULL
;
993 for (BatchesStack::iterator itt
= batches
.begin(); itt
!= batches
.end(); ++itt
)
995 if (itt
->m_Texture
->GetMaterial().GetSamplers().size() == 0)
999 CShaderTechniquePtr techBase
;
1003 techBase
= g_Renderer
.GetShaderManager().LoadEffect(itt
->m_Texture
->GetMaterial().GetShaderEffect(), contextBlend
, itt
->m_Texture
->GetMaterial().GetShaderDefines(0));
1005 numPasses
= techBase
->GetNumPasses();
1008 CShaderProgramPtr previousShader
;
1009 for (int pass
= 0; pass
< numPasses
; ++pass
)
1013 techBase
->BeginPass(pass
);
1014 TerrainRenderer::PrepareShader(techBase
->GetShader(), shadow
);
1017 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1020 const CShaderProgramPtr
& shader
= isDummyShader
? dummy
: techBase
->GetShader(pass
);
1024 const CMaterial::SamplersVector
& samplers
= itt
->m_Texture
->GetMaterial().GetSamplers();
1025 size_t samplersNum
= samplers
.size();
1027 for (size_t s
= 0; s
< samplersNum
; ++s
)
1029 const CMaterial::TextureSampler
& samp
= samplers
[s
];
1030 shader
->BindTexture(samp
.Name
, samp
.Sampler
);
1033 shader
->BindTexture(str_blendTex
, itt
->m_Texture
->m_TerrainAlpha
->second
.m_hCompositeAlphaMap
);
1035 itt
->m_Texture
->GetMaterial().GetStaticUniforms().BindUniforms(shader
);
1040 pglClientActiveTextureARB(GL_TEXTURE0
);
1041 glMatrixMode(GL_TEXTURE
);
1042 glLoadMatrixf(itt
->m_Texture
->GetTextureMatrix());
1043 glMatrixMode(GL_MODELVIEW
);
1048 float c
= itt
->m_Texture
->GetTextureMatrix()[0];
1049 float ms
= itt
->m_Texture
->GetTextureMatrix()[8];
1050 shader
->Uniform(str_textureTransform
, c
, ms
, -ms
, 0.f
);
1055 shader
->BindTexture(str_baseTex
, g_Renderer
.GetTextureManager().GetErrorTexture());
1058 for (VertexBufferBatches::iterator itv
= itt
->m_Batches
.begin(); itv
!= itt
->m_Batches
.end(); ++itv
)
1060 // Rebind the VB only if it changed since the last batch
1061 if (itv
->first
!= lastVB
|| shader
!= previousShader
)
1063 lastVB
= itv
->first
;
1064 previousShader
= shader
;
1065 GLsizei stride
= sizeof(SBlendVertex
);
1066 SBlendVertex
*base
= (SBlendVertex
*)itv
->first
->Bind();
1068 shader
->VertexPointer(3, GL_FLOAT
, stride
, &base
->m_Position
[0]);
1069 shader
->ColorPointer(4, GL_UNSIGNED_BYTE
, stride
, &base
->m_DiffuseColor
);
1070 shader
->NormalPointer(GL_FLOAT
, stride
, &base
->m_Normal
[0]);
1071 shader
->TexCoordPointer(GL_TEXTURE0
, 3, GL_FLOAT
, stride
, &base
->m_Position
[0]);
1072 shader
->TexCoordPointer(GL_TEXTURE1
, 2, GL_FLOAT
, stride
, &base
->m_AlphaUVs
[0]);
1075 shader
->AssertPointersBound();
1077 for (IndexBufferBatches::iterator it
= itv
->second
.begin(); it
!= itv
->second
.end(); ++it
)
1081 BatchElements
& batch
= it
->second
;
1083 if (!g_Renderer
.m_SkipSubmit
)
1085 for (size_t i
= 0; i
< batch
.first
.size(); ++i
)
1086 glDrawElements(GL_TRIANGLES
, batch
.first
[i
], GL_UNSIGNED_SHORT
, batch
.second
[i
]);
1089 g_Renderer
.m_Stats
.m_DrawCalls
++;
1090 g_Renderer
.m_Stats
.m_BlendSplats
++;
1091 g_Renderer
.m_Stats
.m_TerrainTris
+= std::accumulate(batch
.first
.begin(), batch
.first
.end(), 0) / 3;
1097 glDisable(GL_BLEND
);
1098 techBase
->EndPass();
1106 pglClientActiveTextureARB(GL_TEXTURE0
);
1107 glMatrixMode(GL_TEXTURE
);
1109 glMatrixMode(GL_MODELVIEW
);
1113 CVertexBuffer::Unbind();
1116 void CPatchRData::RenderStreams(const std::vector
<CPatchRData
*>& patches
, const CShaderProgramPtr
& shader
, int streamflags
)
1118 // Each batch has a list of index counts, and a list of pointers-to-first-indexes
1119 typedef std::pair
<std::vector
<GLint
>, std::vector
<void*> > BatchElements
;
1121 // Group batches by index buffer
1122 typedef std::map
<CVertexBuffer
*, BatchElements
> IndexBufferBatches
;
1124 // Group batches by vertex buffer
1125 typedef std::map
<CVertexBuffer
*, IndexBufferBatches
> VertexBufferBatches
;
1127 VertexBufferBatches batches
;
1129 PROFILE_START("compute batches");
1131 // Collect all the patches into their appropriate batches
1132 for (size_t i
= 0; i
< patches
.size(); ++i
)
1134 CPatchRData
* patch
= patches
[i
];
1135 BatchElements
& batch
= batches
[patch
->m_VBBase
->m_Owner
][patch
->m_VBBaseIndices
->m_Owner
];
1137 batch
.first
.push_back(patch
->m_VBBaseIndices
->m_Count
);
1139 u8
* indexBase
= patch
->m_VBBaseIndices
->m_Owner
->GetBindAddress();
1140 batch
.second
.push_back(indexBase
+ sizeof(u16
)*(patch
->m_VBBaseIndices
->m_Index
));
1143 PROFILE_END("compute batches");
1145 ENSURE(!(streamflags
& ~(STREAM_POS
|STREAM_COLOR
|STREAM_POSTOUV0
|STREAM_POSTOUV1
)));
1147 // Render each batch
1148 for (VertexBufferBatches::iterator itv
= batches
.begin(); itv
!= batches
.end(); ++itv
)
1150 GLsizei stride
= sizeof(SBaseVertex
);
1151 SBaseVertex
*base
= (SBaseVertex
*)itv
->first
->Bind();
1153 shader
->VertexPointer(3, GL_FLOAT
, stride
, &base
->m_Position
);
1154 if (streamflags
& STREAM_POSTOUV0
)
1155 shader
->TexCoordPointer(GL_TEXTURE0
, 3, GL_FLOAT
, stride
, &base
->m_Position
);
1156 if (streamflags
& STREAM_POSTOUV1
)
1157 shader
->TexCoordPointer(GL_TEXTURE1
, 3, GL_FLOAT
, stride
, &base
->m_Position
);
1158 if (streamflags
& STREAM_COLOR
)
1159 shader
->ColorPointer(4, GL_UNSIGNED_BYTE
, stride
, &base
->m_DiffuseColor
);
1161 shader
->AssertPointersBound();
1163 for (IndexBufferBatches::iterator it
= itv
->second
.begin(); it
!= itv
->second
.end(); ++it
)
1167 BatchElements
& batch
= it
->second
;
1169 if (!g_Renderer
.m_SkipSubmit
)
1171 for (size_t i
= 0; i
< batch
.first
.size(); ++i
)
1172 glDrawElements(GL_TRIANGLES
, batch
.first
[i
], GL_UNSIGNED_SHORT
, batch
.second
[i
]);
1175 g_Renderer
.m_Stats
.m_DrawCalls
++;
1176 g_Renderer
.m_Stats
.m_TerrainTris
+= std::accumulate(batch
.first
.begin(), batch
.first
.end(), 0) / 3;
1180 CVertexBuffer::Unbind();
1183 void CPatchRData::RenderOutline()
1185 CTerrain
* terrain
= m_Patch
->m_Parent
;
1186 ssize_t gx
= m_Patch
->m_X
* PATCH_SIZE
;
1187 ssize_t gz
= m_Patch
->m_Z
* PATCH_SIZE
;
1190 std::vector
<CVector3D
> line
;
1194 for (i
= 0, j
= 0; i
<= PATCH_SIZE
; ++i
)
1196 terrain
->CalcPosition(gx
+ i
, gz
+ j
, pos
);
1197 line
.push_back(pos
);
1199 for (i
= PATCH_SIZE
, j
= 1; j
<= PATCH_SIZE
; ++j
)
1201 terrain
->CalcPosition(gx
+ i
, gz
+ j
, pos
);
1202 line
.push_back(pos
);
1204 for (i
= PATCH_SIZE
-1, j
= PATCH_SIZE
; i
>= 0; --i
)
1206 terrain
->CalcPosition(gx
+ i
, gz
+ j
, pos
);
1207 line
.push_back(pos
);
1209 for (i
= 0, j
= PATCH_SIZE
-1; j
>= 0; --j
)
1211 terrain
->CalcPosition(gx
+ i
, gz
+ j
, pos
);
1212 line
.push_back(pos
);
1216 #warning TODO: implement CPatchRData::RenderOutlines for GLES
1218 glVertexPointer(3, GL_FLOAT
, sizeof(CVector3D
), &line
[0]);
1219 glDrawArrays(GL_LINE_STRIP
, 0, line
.size());
1223 void CPatchRData::RenderSides(CShaderProgramPtr
& shader
)
1225 ENSURE(m_UpdateFlags
==0);
1230 SSideVertex
*base
= (SSideVertex
*)m_VBSides
->m_Owner
->Bind();
1232 // setup data pointers
1233 GLsizei stride
= sizeof(SSideVertex
);
1234 shader
->VertexPointer(3, GL_FLOAT
, stride
, &base
->m_Position
);
1236 shader
->AssertPointersBound();
1238 if (!g_Renderer
.m_SkipSubmit
)
1239 glDrawArrays(GL_TRIANGLE_STRIP
, m_VBSides
->m_Index
, (GLsizei
)m_VBSides
->m_Count
);
1242 g_Renderer
.m_Stats
.m_DrawCalls
++;
1243 g_Renderer
.m_Stats
.m_TerrainTris
+= m_VBSides
->m_Count
- 2;
1245 CVertexBuffer::Unbind();
1248 void CPatchRData::RenderPriorities(CTextRenderer
& textRenderer
)
1250 CTerrain
* terrain
= m_Patch
->m_Parent
;
1251 CCamera
* camera
= g_Game
->GetView()->GetCamera();
1253 for (ssize_t j
= 0; j
< PATCH_SIZE
; ++j
)
1255 for (ssize_t i
= 0; i
< PATCH_SIZE
; ++i
)
1257 ssize_t gx
= m_Patch
->m_X
* PATCH_SIZE
+ i
;
1258 ssize_t gz
= m_Patch
->m_Z
* PATCH_SIZE
+ j
;
1261 terrain
->CalcPosition(gx
, gz
, pos
);
1263 // Move a bit towards the center of the tile
1264 pos
.X
+= TERRAIN_TILE_SIZE
/4.f
;
1265 pos
.Z
+= TERRAIN_TILE_SIZE
/4.f
;
1268 camera
->GetScreenCoordinates(pos
, x
, y
);
1270 textRenderer
.PrintfAt(x
, y
, L
"%d", m_Patch
->m_MiniPatches
[j
][i
].Priority
);
1276 // Water build and rendering
1279 // Build vertex buffer for water vertices over our patch
1280 void CPatchRData::BuildWater()
1282 PROFILE3("build water");
1284 // number of vertices in each direction in each patch
1285 ENSURE((PATCH_SIZE
% water_cell_size
) == 0);
1289 g_VBMan
.Release(m_VBWater
);
1292 if (m_VBWaterIndices
)
1294 g_VBMan
.Release(m_VBWaterIndices
);
1295 m_VBWaterIndices
= 0;
1299 g_VBMan
.Release(m_VBWaterShore
);
1302 if (m_VBWaterIndicesShore
)
1304 g_VBMan
.Release(m_VBWaterIndicesShore
);
1305 m_VBWaterIndicesShore
= 0;
1307 m_WaterBounds
.SetEmpty();
1309 // We need to use this to access the water manager or we may not have the
1310 // actual values but some compiled-in defaults
1311 CmpPtr
<ICmpWaterManager
> cmpWaterManager(*m_Simulation
, SYSTEM_ENTITY
);
1312 if (!cmpWaterManager
)
1315 // Build data for water
1316 std::vector
<SWaterVertex
> water_vertex_data
;
1317 std::vector
<GLushort
> water_indices
;
1318 u16 water_index_map
[PATCH_SIZE
+1][PATCH_SIZE
+1];
1319 memset(water_index_map
, 0xFF, sizeof(water_index_map
));
1321 // Build data for shore
1322 std::vector
<SWaterVertex
> water_vertex_data_shore
;
1323 std::vector
<GLushort
> water_indices_shore
;
1324 u16 water_shore_index_map
[PATCH_SIZE
+1][PATCH_SIZE
+1];
1325 memset(water_shore_index_map
, 0xFF, sizeof(water_shore_index_map
));
1327 WaterManager
* WaterMgr
= g_Renderer
.GetWaterManager();
1329 CPatch
* patch
= m_Patch
;
1330 CTerrain
* terrain
= patch
->m_Parent
;
1332 ssize_t mapSize
= (size_t)terrain
->GetVerticesPerSide();
1334 //Top-left coordinates of our patch.
1335 ssize_t x1
= m_Patch
->m_X
*PATCH_SIZE
;
1336 ssize_t z1
= m_Patch
->m_Z
*PATCH_SIZE
;
1338 // to whoever implements different water heights, this is a TODO: water height)
1339 float waterHeight
= cmpWaterManager
->GetExactWaterLevel(0.0f
,0.0f
);
1341 // The 4 points making a water tile.
1342 int moves
[4][2] = { {0,0}, {water_cell_size
,0}, {0,water_cell_size
}, {water_cell_size
,water_cell_size
} };
1343 // Where to look for when checking for water for shore tiles.
1344 int check
[10][2] = { {0,0},{water_cell_size
,0},{water_cell_size
*2,0},{0,water_cell_size
},{0,water_cell_size
*2},{water_cell_size
,water_cell_size
},{water_cell_size
*2,water_cell_size
*2}, {-water_cell_size
,0}, {0,-water_cell_size
}, {-water_cell_size
,-water_cell_size
} };
1346 // build vertices, uv, and shader varying
1347 for (ssize_t z
= 0; z
< PATCH_SIZE
; z
+= water_cell_size
)
1349 for (ssize_t x
= 0; x
< PATCH_SIZE
; x
+= water_cell_size
)
1352 // Check that this tile is close to water
1353 bool nearWat
= false;
1354 for (size_t test
= 0; test
< 10; ++test
)
1355 if (terrain
->GetVertexGroundLevel(x
+x1
+check
[test
][0], z
+z1
+check
[test
][1]) < waterHeight
)
1360 // This is actually lying and I should call CcmpTerrain
1361 /*if (!terrain->IsOnMap(x+x1, z+z1)
1362 && !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
1363 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
1364 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
1367 for (int i
= 0; i
< 4; ++i
)
1369 if (water_index_map
[z
+moves
[i
][1]][x
+moves
[i
][0]] != 0xFFFF)
1372 ssize_t zz
= z
+z1
+moves
[i
][1];
1373 ssize_t xx
= x
+x1
+moves
[i
][0];
1375 SWaterVertex vertex
;
1376 terrain
->CalcPosition(xx
,zz
, vertex
.m_Position
);
1377 float depth
= waterHeight
- vertex
.m_Position
.Y
;
1379 vertex
.m_Position
.Y
= waterHeight
;
1381 m_WaterBounds
+= vertex
.m_Position
;
1383 vertex
.m_WaterData
= CVector2D(WaterMgr
->m_WindStrength
[xx
+ zz
*mapSize
], depth
);
1385 water_index_map
[z
+moves
[i
][1]][x
+moves
[i
][0]] = water_vertex_data
.size();
1386 water_vertex_data
.push_back(vertex
);
1388 water_indices
.push_back(water_index_map
[z
+ moves
[2][1]][x
+ moves
[2][0]]);
1389 water_indices
.push_back(water_index_map
[z
+ moves
[0][1]][x
+ moves
[0][0]]);
1390 water_indices
.push_back(water_index_map
[z
+ moves
[1][1]][x
+ moves
[1][0]]);
1391 water_indices
.push_back(water_index_map
[z
+ moves
[1][1]][x
+ moves
[1][0]]);
1392 water_indices
.push_back(water_index_map
[z
+ moves
[3][1]][x
+ moves
[3][0]]);
1393 water_indices
.push_back(water_index_map
[z
+ moves
[2][1]][x
+ moves
[2][0]]);
1395 // Check id this tile is partly over land.
1396 // If so add a square over the terrain. This is necessary to render waves that go on shore.
1397 if (terrain
->GetVertexGroundLevel(x
+x1
, z
+z1
) < waterHeight
1398 && terrain
->GetVertexGroundLevel(x
+x1
+ water_cell_size
, z
+z1
) < waterHeight
1399 && terrain
->GetVertexGroundLevel(x
+x1
, z
+z1
+water_cell_size
) < waterHeight
1400 && terrain
->GetVertexGroundLevel(x
+x1
+ water_cell_size
, z
+z1
+water_cell_size
) < waterHeight
)
1403 for (int i
= 0; i
< 4; ++i
)
1405 if (water_shore_index_map
[z
+moves
[i
][1]][x
+moves
[i
][0]] != 0xFFFF)
1407 ssize_t zz
= z
+z1
+moves
[i
][1];
1408 ssize_t xx
= x
+x1
+moves
[i
][0];
1410 SWaterVertex vertex
;
1411 terrain
->CalcPosition(xx
,zz
, vertex
.m_Position
);
1413 vertex
.m_Position
.Y
+= 0.02f
;
1414 m_WaterBounds
+= vertex
.m_Position
;
1416 vertex
.m_WaterData
= CVector2D(0.0f
, -5.0f
);
1418 water_shore_index_map
[z
+moves
[i
][1]][x
+moves
[i
][0]] = water_vertex_data_shore
.size();
1419 water_vertex_data_shore
.push_back(vertex
);
1421 if (terrain
->GetTriangulationDir(x
+x1
,z
+z1
))
1423 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[2][1]][x
+ moves
[2][0]]);
1424 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[0][1]][x
+ moves
[0][0]]);
1425 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[1][1]][x
+ moves
[1][0]]);
1426 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[1][1]][x
+ moves
[1][0]]);
1427 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[3][1]][x
+ moves
[3][0]]);
1428 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[2][1]][x
+ moves
[2][0]]);
1432 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[3][1]][x
+ moves
[3][0]]);
1433 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[2][1]][x
+ moves
[2][0]]);
1434 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[0][1]][x
+ moves
[0][0]]);
1435 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[3][1]][x
+ moves
[3][0]]);
1436 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[0][1]][x
+ moves
[0][0]]);
1437 water_indices_shore
.push_back(water_shore_index_map
[z
+ moves
[1][1]][x
+ moves
[1][0]]);
1442 // no vertex buffers if no data generated
1443 if (water_indices
.size() != 0)
1445 m_VBWater
= g_VBMan
.Allocate(sizeof(SWaterVertex
), water_vertex_data
.size(), GL_STATIC_DRAW
, GL_ARRAY_BUFFER
);
1446 m_VBWater
->m_Owner
->UpdateChunkVertices(m_VBWater
, &water_vertex_data
[0]);
1448 m_VBWaterIndices
= g_VBMan
.Allocate(sizeof(GLushort
), water_indices
.size(), GL_STATIC_DRAW
, GL_ELEMENT_ARRAY_BUFFER
);
1449 m_VBWaterIndices
->m_Owner
->UpdateChunkVertices(m_VBWaterIndices
, &water_indices
[0]);
1452 if (water_indices_shore
.size() != 0)
1454 m_VBWaterShore
= g_VBMan
.Allocate(sizeof(SWaterVertex
), water_vertex_data_shore
.size(), GL_STATIC_DRAW
, GL_ARRAY_BUFFER
);
1455 m_VBWaterShore
->m_Owner
->UpdateChunkVertices(m_VBWaterShore
, &water_vertex_data_shore
[0]);
1457 // Construct indices buffer
1458 m_VBWaterIndicesShore
= g_VBMan
.Allocate(sizeof(GLushort
), water_indices_shore
.size(), GL_STATIC_DRAW
, GL_ELEMENT_ARRAY_BUFFER
);
1459 m_VBWaterIndicesShore
->m_Owner
->UpdateChunkVertices(m_VBWaterIndicesShore
, &water_indices_shore
[0]);
1462 void CPatchRData::RenderWater(CShaderProgramPtr
& shader
, bool onlyShore
, bool fixedPipeline
)
1464 ASSERT(m_UpdateFlags
==0);
1466 if (g_Renderer
.m_SkipSubmit
|| (!m_VBWater
&& !m_VBWaterShore
))
1469 if (m_VBWater
!= 0x0 && !onlyShore
)
1471 SWaterVertex
*base
=(SWaterVertex
*)m_VBWater
->m_Owner
->Bind();
1473 // setup data pointers
1474 GLsizei stride
= sizeof(SWaterVertex
);
1475 shader
->VertexPointer(3, GL_FLOAT
, stride
, &base
[m_VBWater
->m_Index
].m_Position
);
1477 shader
->VertexAttribPointer(str_a_waterInfo
, 2, GL_FLOAT
, false, stride
, &base
[m_VBWater
->m_Index
].m_WaterData
);
1479 shader
->AssertPointersBound();
1481 u8
* indexBase
= m_VBWaterIndices
->m_Owner
->Bind();
1482 glDrawElements(GL_TRIANGLES
, (GLsizei
) m_VBWaterIndices
->m_Count
,
1483 GL_UNSIGNED_SHORT
, indexBase
+ sizeof(u16
)*(m_VBWaterIndices
->m_Index
));
1485 g_Renderer
.m_Stats
.m_DrawCalls
++;
1486 g_Renderer
.m_Stats
.m_WaterTris
+= m_VBWaterIndices
->m_Count
/ 3;
1488 if (m_VBWaterShore
!= 0x0 && g_Renderer
.GetWaterManager()->m_WaterFancyEffects
&& !g_Renderer
.GetWaterManager()->m_WaterUgly
)
1490 SWaterVertex
*base
=(SWaterVertex
*)m_VBWaterShore
->m_Owner
->Bind();
1492 GLsizei stride
= sizeof(SWaterVertex
);
1493 shader
->VertexPointer(3, GL_FLOAT
, stride
, &base
[m_VBWaterShore
->m_Index
].m_Position
);
1495 shader
->VertexAttribPointer(str_a_waterInfo
, 2, GL_FLOAT
, false, stride
, &base
[m_VBWaterShore
->m_Index
].m_WaterData
);
1497 shader
->AssertPointersBound();
1499 u8
* indexBase
= m_VBWaterIndicesShore
->m_Owner
->Bind();
1500 glDrawElements(GL_TRIANGLES
, (GLsizei
) m_VBWaterIndicesShore
->m_Count
,
1501 GL_UNSIGNED_SHORT
, indexBase
+ sizeof(u16
)*(m_VBWaterIndicesShore
->m_Index
));
1503 g_Renderer
.m_Stats
.m_DrawCalls
++;
1504 g_Renderer
.m_Stats
.m_WaterTris
+= m_VBWaterIndicesShore
->m_Count
/ 3;
1507 CVertexBuffer::Unbind();