1 /* Copyright (C) 2017 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include <set>
21 #include <algorithm>
22 #include <numeric>
23 
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"
35 #include "ps/Game.h"
36 #include "ps/Profile.h"
37 #include "ps/Pyrogenesis.h"
38 #include "ps/World.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"
47 
48 const ssize_t BlendOffsets[9][2] = {
49 	{  0, -1 },
50 	{ -1, -1 },
51 	{ -1,  0 },
52 	{ -1,  1 },
53 	{  0,  1 },
54 	{  1,  1 },
55 	{  1,  0 },
56 	{  1, -1 },
57 	{  0,  0 }
58 };
59 
60 ///////////////////////////////////////////////////////////////////
61 // CPatchRData constructor
CPatchRData(CPatch * patch,CSimulation2 * simulation)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)
69 {
70 	ENSURE(patch);
71 	Build();
72 }
73 
74 ///////////////////////////////////////////////////////////////////
75 // CPatchRData destructor
~CPatchRData()76 CPatchRData::~CPatchRData()
77 {
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);
88 }
89 
90 /**
91  * Represents a blend for a single tile, texture and shape.
92  */
93 struct STileBlend
94 {
95 	CTerrainTextureEntry* m_Texture;
96 	int m_Priority;
97 	u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
98 
99 	struct DecreasingPriority
100 	{
operator ()STileBlend::DecreasingPriority101 		bool operator()(const STileBlend& a, const STileBlend& b) const
102 		{
103 			if (a.m_Priority > b.m_Priority)
104 				return true;
105 			if (a.m_Priority < b.m_Priority)
106 				return false;
107 			if (a.m_Texture && b.m_Texture)
108 				return a.m_Texture->GetTag() > b.m_Texture->GetTag();
109 			return false;
110 		}
111 	};
112 
113 	struct CurrentTile
114 	{
operator ()STileBlend::CurrentTile115 		bool operator()(const STileBlend& a) const
116 		{
117 			return (a.m_TileMask & (1 << 8)) != 0;
118 		}
119 	};
120 };
121 
122 /**
123  * Represents the ordered collection of blends drawn on a particular tile.
124  */
125 struct STileBlendStack
126 {
127 	u8 i, j;
128 	std::vector<STileBlend> blends; // back of vector is lowest-priority texture
129 };
130 
131 /**
132  * Represents a batched collection of blends using the same texture.
133  */
134 struct SBlendLayer
135 {
136 	struct Tile
137 	{
138 		u8 i, j;
139 		u8 shape;
140 	};
141 
142 	CTerrainTextureEntry* m_Texture;
143 	std::vector<Tile> m_Tiles;
144 };
145 
BuildBlends()146 void CPatchRData::BuildBlends()
147 {
148 	PROFILE3("build blends");
149 
150 	m_BlendSplats.clear();
151 
152 	std::vector<SBlendVertex> blendVertices;
153 	std::vector<u16> blendIndices;
154 
155 	CTerrain* terrain = m_Patch->m_Parent;
156 
157 	std::vector<STileBlendStack> blendStacks;
158 	blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
159 
160 	// For each tile in patch ..
161 	for (ssize_t j = 0; j < PATCH_SIZE; ++j)
162 	{
163 		for (ssize_t i = 0; i < PATCH_SIZE; ++i)
164 		{
165 			ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
166 			ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
167 
168 			std::vector<STileBlend> blends;
169 			blends.reserve(9);
170 
171 			// Compute a blend for every tile in the 3x3 square around this tile
172 			for (size_t n = 0; n < 9; ++n)
173 			{
174 				ssize_t ox = gx + BlendOffsets[n][1];
175 				ssize_t oz = gz + BlendOffsets[n][0];
176 
177 				CMiniPatch* nmp = terrain->GetTile(ox, oz);
178 				if (!nmp)
179 					continue;
180 
181 				STileBlend blend;
182 				blend.m_Texture = nmp->GetTextureEntry();
183 				blend.m_Priority = nmp->GetPriority();
184 				blend.m_TileMask = 1 << n;
185 				blends.push_back(blend);
186 			}
187 
188 			// Sort the blends, highest priority first
189 			std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
190 
191 			STileBlendStack blendStack;
192 			blendStack.i = i;
193 			blendStack.j = j;
194 
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)
197 			{
198 				if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
199 					blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
200 				else
201 					blendStack.blends.push_back(blends[k]);
202 			}
203 
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());
210 
211 			blendStacks.push_back(blendStack);
212 		}
213 	}
214 
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.)
219 
220 	std::vector<SBlendLayer> blendLayers;
221 
222 	while (true)
223 	{
224 		if (!blendLayers.empty())
225 		{
226 			// Try to grab as many tiles as possible that match our current layer,
227 			// from off the blend stacks of all the tiles
228 
229 			CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
230 
231 			for (size_t k = 0; k < blendStacks.size(); ++k)
232 			{
233 				if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
234 				{
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();
238 				}
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)
241 			}
242 		}
243 
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;
249 
250 		for (size_t k = 0; k < blendStacks.size(); ++k)
251 		{
252 			if (blendStacks[k].blends.size() > bestStackSize)
253 			{
254 				bestStackSize = blendStacks[k].blends.size();
255 				bestTex = blendStacks[k].blends.back().m_Texture;
256 			}
257 		}
258 
259 		// If all our stacks were empty, we're done
260 		if (bestStackSize == 0)
261 			break;
262 
263 		// Otherwise add the new layer, then loop back and start filling it in
264 
265 		SBlendLayer layer;
266 		layer.m_Texture = bestTex;
267 		blendLayers.push_back(layer);
268 	}
269 
270 	// Now build outgoing splats
271 	m_BlendSplats.resize(blendLayers.size());
272 
273 	for (size_t k = 0; k < blendLayers.size(); ++k)
274 	{
275 		SSplat& splat = m_BlendSplats[k];
276 		splat.m_IndexStart = blendIndices.size();
277 		splat.m_Texture = blendLayers[k].m_Texture;
278 
279 		for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
280 		{
281 			SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
282 			AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
283 		}
284 
285 		splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
286 	}
287 
288 	// Release existing vertex buffer chunks
289 	if (m_VBBlends)
290 	{
291 		g_VBMan.Release(m_VBBlends);
292 		m_VBBlends = 0;
293 	}
294 
295 	if (m_VBBlendIndices)
296 	{
297 		g_VBMan.Release(m_VBBlendIndices);
298 		m_VBBlendIndices = 0;
299 	}
300 
301 	if (blendVertices.size())
302 	{
303 		// Construct vertex buffer
304 
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]);
307 
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;
311 
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]);
314 	}
315 }
316 
AddBlend(std::vector<SBlendVertex> & blendVertices,std::vector<u16> & blendIndices,u16 i,u16 j,u8 shape,CTerrainTextureEntry * texture)317 void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
318 			   u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
319 {
320 	CTerrain* terrain = m_Patch->m_Parent;
321 
322 	ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
323 	ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
324 
325 	// uses the current neighbour texture
326 	BlendShape8 shape8;
327 	for (size_t m = 0; m < 8; ++m)
328 		shape8[m] = (shape & (1 << m)) ? 0 : 1;
329 
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);
333 
334 	// now actually render the blend tile (if we need one)
335 	if (alphamap == -1)
336 		return;
337 
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;
342 
343 	if (alphamapflags & BLENDMAP_FLIPU)
344 		std::swap(u0, u1);
345 
346 	if (alphamapflags & BLENDMAP_FLIPV)
347 		std::swap(v0, v1);
348 
349 	int base = 0;
350 	if (alphamapflags & BLENDMAP_ROTATE90)
351 		base = 1;
352 	else if (alphamapflags & BLENDMAP_ROTATE180)
353 		base = 2;
354 	else if (alphamapflags & BLENDMAP_ROTATE270)
355 		base = 3;
356 
357 	SBlendVertex vtx[4];
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;
366 
367 	SBlendVertex dst;
368 
369 	const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
370 	CVector3D normal;
371 
372 	bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
373 
374 	size_t index = blendVertices.size();
375 
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);
383 
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);
391 
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);
399 
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);
407 
408 	bool dir = terrain->GetTriangulationDir(gx, gz);
409 	if (dir)
410 	{
411 		blendIndices.push_back(index+0);
412 		blendIndices.push_back(index+1);
413 		blendIndices.push_back(index+3);
414 
415 		blendIndices.push_back(index+1);
416 		blendIndices.push_back(index+2);
417 		blendIndices.push_back(index+3);
418 	}
419 	else
420 	{
421 		blendIndices.push_back(index+0);
422 		blendIndices.push_back(index+1);
423 		blendIndices.push_back(index+2);
424 
425 		blendIndices.push_back(index+2);
426 		blendIndices.push_back(index+3);
427 		blendIndices.push_back(index+0);
428 	}
429 }
430 
BuildIndices()431 void CPatchRData::BuildIndices()
432 {
433 	PROFILE3("build indices");
434 
435 	CTerrain* terrain = m_Patch->m_Parent;
436 
437 	ssize_t px = m_Patch->m_X * PATCH_SIZE;
438 	ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
439 
440 	// must have allocated some vertices before trying to build corresponding indices
441 	ENSURE(m_VBBase);
442 
443 	// number of vertices in each direction in each patch
444 	ssize_t vsize=PATCH_SIZE+1;
445 
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);
448 
449 	std::vector<unsigned short> indices;
450 	indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
451 
452 	// release existing splats
453 	m_Splats.clear();
454 
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();
461 			texgrid[j][i]=tex;
462 			if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
463 				textures.push_back(tex);
464 			}
465 		}
466 	}
467 
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;
472 
473 	for (size_t i=0;i<m_Splats.size();i++) {
474 		CTerrainTextureEntry* tex=textures[i];
475 
476 		SSplat& splat=m_Splats[i];
477 		splat.m_Texture=tex;
478 		splat.m_IndexStart=indices.size();
479 
480 		for (ssize_t j = 0; j < PATCH_SIZE; j++)
481 		{
482 			for (ssize_t i = 0; i < PATCH_SIZE; i++)
483 			{
484 				if (texgrid[j][i] == tex)
485 				{
486 					bool dir = terrain->GetTriangulationDir(px+i, pz+j);
487 					if (dir)
488 					{
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));
492 
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));
496 					}
497 					else
498 					{
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));
502 
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));
506 					}
507 				}
508 			}
509 		}
510 		splat.m_IndexCount=indices.size()-splat.m_IndexStart;
511 	}
512 
513 	// Release existing vertex buffer chunk
514 	if (m_VBBaseIndices)
515 	{
516 		g_VBMan.Release(m_VBBaseIndices);
517 		m_VBBaseIndices = 0;
518 	}
519 
520 	ENSURE(indices.size());
521 
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]);
525 }
526 
527 
BuildVertices()528 void CPatchRData::BuildVertices()
529 {
530 	PROFILE3("build vertices");
531 
532 	// create both vertices and lighting colors
533 
534 	// number of vertices in each direction in each patch
535 	ssize_t vsize=PATCH_SIZE+1;
536 
537 	std::vector<SBaseVertex> vertices;
538 	vertices.resize(vsize*vsize);
539 
540 	// get index of this patch
541 	ssize_t px=m_Patch->m_X;
542 	ssize_t pz=m_Patch->m_Z;
543 
544 	CTerrain* terrain=m_Patch->m_Parent;
545 	const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
546 
547 	bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED);
548 
549 	// build vertices
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;
555 
556 			// calculate vertex data
557 			terrain->CalcPosition(ix,iz,vertices[v].m_Position);
558 
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)
562 			CVector3D normal;
563 			terrain->CalcNormal(ix,iz,normal);
564 
565 			vertices[v].m_Normal = normal;
566 
567 			vertices[v].m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
568 		}
569 	}
570 
571 	// upload to vertex buffer
572 	if (!m_VBBase)
573 		m_VBBase = g_VBMan.Allocate(sizeof(SBaseVertex), vsize * vsize, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
574 
575 	m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase, &vertices[0]);
576 }
577 
BuildSide(std::vector<SSideVertex> & vertices,CPatchSideFlags side)578 void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
579 {
580 	ssize_t vsize = PATCH_SIZE + 1;
581 	CTerrain* terrain = m_Patch->m_Parent;
582 	CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
583 
584 	for (ssize_t k = 0; k < vsize; k++)
585 	{
586 		ssize_t gx = m_Patch->m_X * PATCH_SIZE;
587 		ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
588 		switch (side)
589 		{
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;
594 		}
595 
596 		CVector3D pos;
597 		terrain->CalcPosition(gx, gz, pos);
598 
599 		// Clamp the height to the water level
600 		float waterHeight = 0.f;
601 		if (cmpWaterManager)
602 			waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
603 		pos.Y = std::max(pos.Y, waterHeight);
604 
605 		SSideVertex v0, v1;
606 		v0.m_Position = pos;
607 		v1.m_Position = pos;
608 		v1.m_Position.Y = 0;
609 
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())
613 		{
614 			vertices.push_back(vertices.back());
615 			vertices.push_back(v1);
616 		}
617 
618 		// Now add the new triangles
619 		vertices.push_back(v1);
620 		vertices.push_back(v0);
621 	}
622 }
623 
BuildSides()624 void CPatchRData::BuildSides()
625 {
626 	PROFILE3("build sides");
627 
628 	std::vector<SSideVertex> sideVertices;
629 
630 	int sideFlags = m_Patch->GetSideFlags();
631 
632 	// If no sides are enabled, we don't need to do anything
633 	if (!sideFlags)
634 		return;
635 
636 	// For each side, generate a tristrip by adding a vertex at ground/water
637 	// level and a vertex underneath at height 0.
638 
639 	if (sideFlags & CPATCH_SIDE_NEGX)
640 		BuildSide(sideVertices, CPATCH_SIDE_NEGX);
641 
642 	if (sideFlags & CPATCH_SIDE_POSX)
643 		BuildSide(sideVertices, CPATCH_SIDE_POSX);
644 
645 	if (sideFlags & CPATCH_SIDE_NEGZ)
646 		BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
647 
648 	if (sideFlags & CPATCH_SIDE_POSZ)
649 		BuildSide(sideVertices, CPATCH_SIDE_POSZ);
650 
651 	if (sideVertices.empty())
652 		return;
653 
654 	if (!m_VBSides)
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]);
657 }
658 
Build()659 void CPatchRData::Build()
660 {
661 	BuildVertices();
662 	BuildSides();
663 	BuildIndices();
664 	BuildBlends();
665 	BuildWater();
666 }
667 
Update(CSimulation2 * simulation)668 void CPatchRData::Update(CSimulation2* simulation)
669 {
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
675 		BuildVertices();
676 		BuildSides();
677 		BuildIndices();
678 		BuildBlends();
679 		BuildWater();
680 
681 		m_UpdateFlags=0;
682 	}
683 }
684 
685 // Types used for glMultiDrawElements batching:
686 
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.)
690 
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 > >
694 
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)
697 template<typename M>
PooledMapGet(M & m,const typename M::key_type & k,Allocators::DynamicArena & arena)698 typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
699 {
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))
702 	)).first->second;
703 }
704 
705 // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
706 template<typename M>
PooledPairGet(M & m,const typename M::key_type & k,Allocators::DynamicArena & arena)707 typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
708 {
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))
712 	))).first->second;
713 }
714 
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;
717 
718 // Group batches by index buffer
719 typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
720 
721 // Group batches by vertex buffer
722 typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches;
723 
724 // Group batches by texture
725 typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches;
726 
RenderBases(const std::vector<CPatchRData * > & patches,const CShaderDefines & context,ShadowMap * shadow,bool isDummyShader,const CShaderProgramPtr & dummy)727 void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
728 			      ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
729 {
730 	Allocators::DynamicArena arena(1 * MiB);
731 
732 	TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(arena)));
733 
734  	PROFILE_START("compute batches");
735 
736  	// Collect all the patches' base splats into their appropriate batches
737  	for (size_t i = 0; i < patches.size(); ++i)
738  	{
739  		CPatchRData* patch = patches[i];
740  		for (size_t j = 0; j < patch->m_Splats.size(); ++j)
741  		{
742  			SSplat& splat = patch->m_Splats[j];
743 
744  			BatchElements& batch = PooledPairGet(
745 				PooledMapGet(
746  					PooledMapGet(batches, splat.m_Texture, arena),
747  					patch->m_VBBase->m_Owner, arena
748 				),
749 				patch->m_VBBaseIndices->m_Owner, arena
750 			);
751 
752  			batch.first.push_back(splat.m_IndexCount);
753 
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));
756 		}
757  	}
758 
759  	PROFILE_END("compute batches");
760 
761  	// Render each batch
762  	for (TextureBatches::iterator itt = batches.begin(); itt != batches.end(); ++itt)
763 	{
764 		int numPasses = 1;
765 
766 		CShaderTechniquePtr techBase;
767 
768 		if (!isDummyShader)
769 		{
770 			if (itt->first->GetMaterial().GetShaderEffect().length() == 0)
771 			{
772 				LOGERROR("Terrain renderer failed to load shader effect.\n");
773 				continue;
774 			}
775 
776 			techBase = g_Renderer.GetShaderManager().LoadEffect(itt->first->GetMaterial().GetShaderEffect(),
777 						context, itt->first->GetMaterial().GetShaderDefines(0));
778 
779 			numPasses = techBase->GetNumPasses();
780 		}
781 
782 		for (int pass = 0; pass < numPasses; ++pass)
783 		{
784 			if (!isDummyShader)
785 			{
786 				techBase->BeginPass(pass);
787 				TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
788 			}
789 
790 			const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
791 
792 			if (itt->first->GetMaterial().GetSamplers().size() != 0)
793 			{
794 				const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers();
795 				size_t samplersNum = samplers.size();
796 
797 				for (size_t s = 0; s < samplersNum; ++s)
798 				{
799 					const CMaterial::TextureSampler& samp = samplers[s];
800 					shader->BindTexture(samp.Name, samp.Sampler);
801 				}
802 
803 				itt->first->GetMaterial().GetStaticUniforms().BindUniforms(shader);
804 
805 #if !CONFIG2_GLES
806 				if (isDummyShader)
807 				{
808 					glMatrixMode(GL_TEXTURE);
809 					glLoadMatrixf(itt->first->GetTextureMatrix());
810 					glMatrixMode(GL_MODELVIEW);
811 				}
812 				else
813 #endif
814 				{
815 					float c = itt->first->GetTextureMatrix()[0];
816 					float ms = itt->first->GetTextureMatrix()[8];
817 					shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
818 				}
819 			}
820 			else
821 			{
822 				shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
823 			}
824 
825 			for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
826 			{
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]);
833 
834 				shader->AssertPointersBound();
835 
836 				for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
837 				{
838 					it->first->Bind();
839 
840 					BatchElements& batch = it->second;
841 
842 					if (!g_Renderer.m_SkipSubmit)
843 					{
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]);
849 					}
850 
851 					g_Renderer.m_Stats.m_DrawCalls++;
852 					g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
853 				}
854 			}
855 
856 			if (!isDummyShader)
857 				techBase->EndPass();
858 		}
859 	}
860 
861 #if !CONFIG2_GLES
862 	if (isDummyShader)
863 	{
864 		glMatrixMode(GL_TEXTURE);
865 		glLoadIdentity();
866 		glMatrixMode(GL_MODELVIEW);
867 	}
868 #endif
869 
870 	CVertexBuffer::Unbind();
871 }
872 
873 /**
874  * Helper structure for RenderBlends.
875  */
876 struct SBlendBatch
877 {
SBlendBatchSBlendBatch878 	SBlendBatch(Allocators::DynamicArena& arena) :
879 		m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
880 	{
881 	}
882 
883 	CTerrainTextureEntry* m_Texture;
884  	VertexBufferBatches m_Batches;
885 };
886 
887 /**
888  * Helper structure for RenderBlends.
889  */
890 struct SBlendStackItem
891 {
SBlendStackItemSBlendStackItem892 	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))
895 	{
896 	}
897 
898 	typedef std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Allocators::DynamicArena > > SplatStack;
899 	CVertexBuffer::VBChunk* vertices;
900 	CVertexBuffer::VBChunk* indices;
901 	SplatStack splats;
902 };
903 
RenderBlends(const std::vector<CPatchRData * > & patches,const CShaderDefines & context,ShadowMap * shadow,bool isDummyShader,const CShaderProgramPtr & dummy)904 void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
905 			      ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
906 {
907 	Allocators::DynamicArena arena(1 * MiB);
908 
909 	typedef std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Allocators::DynamicArena > > BatchesStack;
910 	BatchesStack batches((BatchesStack::allocator_type(arena)));
911 
912 	CShaderDefines contextBlend = context;
913 	contextBlend.Add(str_BLEND, str_1);
914 
915  	PROFILE_START("compute batches");
916 
917  	// Reserve an arbitrary size that's probably big enough in most cases,
918  	// to avoid heavy reallocations
919  	batches.reserve(256);
920 
921 	typedef std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Allocators::DynamicArena > > BlendStacks;
922 	BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
923 	blendStacks.reserve(patches.size());
924 
925 	// Extract all the blend splats from each patch
926  	for (size_t i = 0; i < patches.size(); ++i)
927  	{
928  		CPatchRData* patch = patches[i];
929  		if (!patch->m_BlendSplats.empty())
930  		{
931 
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());
935  		}
936  	}
937 
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.)
943 	while (true)
944 	{
945 		if (!batches.empty())
946 		{
947 			CTerrainTextureEntry* tex = batches.back().m_Texture;
948 
949 			for (size_t k = 0; k < blendStacks.size(); ++k)
950 			{
951 				SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
952 				if (!splats.empty() && splats.back().m_Texture == tex)
953 				{
954 					CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
955 					CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
956 
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);
959 
960 		 			u8* indexBase = indices->m_Owner->GetBindAddress();
961 		 			batch.second.push_back(indexBase + sizeof(u16)*(indices->m_Index + splats.back().m_IndexStart));
962 
963 					splats.pop_back();
964 				}
965 			}
966 		}
967 
968 		CTerrainTextureEntry* bestTex = NULL;
969 		size_t bestStackSize = 0;
970 
971 		for (size_t k = 0; k < blendStacks.size(); ++k)
972 		{
973 			SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
974 			if (splats.size() > bestStackSize)
975 			{
976 				bestStackSize = splats.size();
977 				bestTex = splats.back().m_Texture;
978 			}
979 		}
980 
981 		if (bestStackSize == 0)
982 			break;
983 
984 		SBlendBatch layer(arena);
985 		layer.m_Texture = bestTex;
986 		batches.push_back(layer);
987 	}
988 
989  	PROFILE_END("compute batches");
990 
991  	CVertexBuffer* lastVB = NULL;
992 
993  	for (BatchesStack::iterator itt = batches.begin(); itt != batches.end(); ++itt)
994 	{
995 		if (itt->m_Texture->GetMaterial().GetSamplers().size() == 0)
996 			continue;
997 
998 		int numPasses = 1;
999 		CShaderTechniquePtr techBase;
1000 
1001 		if (!isDummyShader)
1002 		{
1003 			techBase = g_Renderer.GetShaderManager().LoadEffect(itt->m_Texture->GetMaterial().GetShaderEffect(), contextBlend, itt->m_Texture->GetMaterial().GetShaderDefines(0));
1004 
1005 			numPasses = techBase->GetNumPasses();
1006 		}
1007 
1008 		CShaderProgramPtr previousShader;
1009 		for (int pass = 0; pass < numPasses; ++pass)
1010 		{
1011 			if (!isDummyShader)
1012 			{
1013 				techBase->BeginPass(pass);
1014 				TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
1015 
1016 				glEnable(GL_BLEND);
1017 				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1018 			}
1019 
1020 			const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
1021 
1022 			if (itt->m_Texture)
1023 			{
1024 				const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
1025 				size_t samplersNum = samplers.size();
1026 
1027 				for (size_t s = 0; s < samplersNum; ++s)
1028 				{
1029 					const CMaterial::TextureSampler& samp = samplers[s];
1030 					shader->BindTexture(samp.Name, samp.Sampler);
1031 				}
1032 
1033 				shader->BindTexture(str_blendTex, itt->m_Texture->m_TerrainAlpha->second.m_hCompositeAlphaMap);
1034 
1035 				itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(shader);
1036 
1037 #if !CONFIG2_GLES
1038 				if (isDummyShader)
1039 				{
1040 					pglClientActiveTextureARB(GL_TEXTURE0);
1041 					glMatrixMode(GL_TEXTURE);
1042 					glLoadMatrixf(itt->m_Texture->GetTextureMatrix());
1043 					glMatrixMode(GL_MODELVIEW);
1044 				}
1045 				else
1046 #endif
1047 				{
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);
1051 				}
1052 			}
1053 			else
1054 			{
1055 				shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
1056 			}
1057 
1058 			for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
1059 			{
1060 				// Rebind the VB only if it changed since the last batch
1061 				if (itv->first != lastVB || shader != previousShader)
1062 				{
1063 					lastVB = itv->first;
1064 					previousShader = shader;
1065 					GLsizei stride = sizeof(SBlendVertex);
1066 					SBlendVertex *base = (SBlendVertex *)itv->first->Bind();
1067 
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]);
1073 				}
1074 
1075 				shader->AssertPointersBound();
1076 
1077 				for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1078 				{
1079 					it->first->Bind();
1080 
1081 					BatchElements& batch = it->second;
1082 
1083 					if (!g_Renderer.m_SkipSubmit)
1084 					{
1085 						for (size_t i = 0; i < batch.first.size(); ++i)
1086 							glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1087 					}
1088 
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;
1092 				}
1093 			}
1094 
1095 			if (!isDummyShader)
1096 			{
1097 				glDisable(GL_BLEND);
1098 				techBase->EndPass();
1099 			}
1100 		}
1101 	}
1102 
1103 #if !CONFIG2_GLES
1104 	if (isDummyShader)
1105 	{
1106 		pglClientActiveTextureARB(GL_TEXTURE0);
1107 		glMatrixMode(GL_TEXTURE);
1108 		glLoadIdentity();
1109 		glMatrixMode(GL_MODELVIEW);
1110 	}
1111 #endif
1112 
1113 	CVertexBuffer::Unbind();
1114 }
1115 
RenderStreams(const std::vector<CPatchRData * > & patches,const CShaderProgramPtr & shader,int streamflags)1116 void CPatchRData::RenderStreams(const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader, int streamflags)
1117 {
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;
1120 
1121 	// Group batches by index buffer
1122 	typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
1123 
1124 	// Group batches by vertex buffer
1125 	typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
1126 
1127  	VertexBufferBatches batches;
1128 
1129  	PROFILE_START("compute batches");
1130 
1131  	// Collect all the patches into their appropriate batches
1132  	for (size_t i = 0; i < patches.size(); ++i)
1133  	{
1134  		CPatchRData* patch = patches[i];
1135 		BatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
1136 
1137 		batch.first.push_back(patch->m_VBBaseIndices->m_Count);
1138 
1139 		u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
1140  		batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index));
1141  	}
1142 
1143  	PROFILE_END("compute batches");
1144 
1145 	ENSURE(!(streamflags & ~(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0|STREAM_POSTOUV1)));
1146 
1147  	// Render each batch
1148  	for (VertexBufferBatches::iterator itv = batches.begin(); itv != batches.end(); ++itv)
1149 	{
1150 		GLsizei stride = sizeof(SBaseVertex);
1151 		SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
1152 
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);
1160 
1161 		shader->AssertPointersBound();
1162 
1163 		for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1164 		{
1165 			it->first->Bind();
1166 
1167 			BatchElements& batch = it->second;
1168 
1169 			if (!g_Renderer.m_SkipSubmit)
1170 			{
1171 				for (size_t i = 0; i < batch.first.size(); ++i)
1172 					glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1173 			}
1174 
1175 			g_Renderer.m_Stats.m_DrawCalls++;
1176 			g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1177 		}
1178 	}
1179 
1180 	CVertexBuffer::Unbind();
1181 }
1182 
RenderOutline()1183 void CPatchRData::RenderOutline()
1184 {
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;
1188 
1189 	CVector3D pos;
1190 	std::vector<CVector3D> line;
1191 
1192 	ssize_t i, j;
1193 
1194 	for (i = 0, j = 0; i <= PATCH_SIZE; ++i)
1195 	{
1196 		terrain->CalcPosition(gx + i, gz + j, pos);
1197 		line.push_back(pos);
1198 	}
1199 	for (i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1200 	{
1201 		terrain->CalcPosition(gx + i, gz + j, pos);
1202 		line.push_back(pos);
1203 	}
1204 	for (i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1205 	{
1206 		terrain->CalcPosition(gx + i, gz + j, pos);
1207 		line.push_back(pos);
1208 	}
1209 	for (i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1210 	{
1211 		terrain->CalcPosition(gx + i, gz + j, pos);
1212 		line.push_back(pos);
1213 	}
1214 
1215 #if CONFIG2_GLES
1216 #warning TODO: implement CPatchRData::RenderOutlines for GLES
1217 #else
1218 	glVertexPointer(3, GL_FLOAT, sizeof(CVector3D), &line[0]);
1219 	glDrawArrays(GL_LINE_STRIP, 0, line.size());
1220 #endif
1221 }
1222 
RenderSides(CShaderProgramPtr & shader)1223 void CPatchRData::RenderSides(CShaderProgramPtr& shader)
1224 {
1225 	ENSURE(m_UpdateFlags==0);
1226 
1227 	if (!m_VBSides)
1228 		return;
1229 
1230 	SSideVertex *base = (SSideVertex *)m_VBSides->m_Owner->Bind();
1231 
1232 	// setup data pointers
1233 	GLsizei stride = sizeof(SSideVertex);
1234 	shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
1235 
1236 	shader->AssertPointersBound();
1237 
1238 	if (!g_Renderer.m_SkipSubmit)
1239 		glDrawArrays(GL_TRIANGLE_STRIP, m_VBSides->m_Index, (GLsizei)m_VBSides->m_Count);
1240 
1241 	// bump stats
1242 	g_Renderer.m_Stats.m_DrawCalls++;
1243 	g_Renderer.m_Stats.m_TerrainTris += m_VBSides->m_Count - 2;
1244 
1245 	CVertexBuffer::Unbind();
1246 }
1247 
RenderPriorities(CTextRenderer & textRenderer)1248 void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
1249 {
1250 	CTerrain* terrain = m_Patch->m_Parent;
1251 	CCamera* camera = g_Game->GetView()->GetCamera();
1252 
1253 	for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1254 	{
1255 		for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1256 		{
1257 			ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1258 			ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1259 
1260 			CVector3D pos;
1261 			terrain->CalcPosition(gx, gz, pos);
1262 
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;
1266 
1267 			float x, y;
1268 			camera->GetScreenCoordinates(pos, x, y);
1269 
1270 			textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1271 		}
1272 	}
1273 }
1274 
1275 //
1276 // Water build and rendering
1277 //
1278 
1279 // Build vertex buffer for water vertices over our patch
BuildWater()1280 void CPatchRData::BuildWater()
1281 {
1282 	PROFILE3("build water");
1283 
1284 	// Number of vertices in each direction in each patch
1285 	ENSURE(PATCH_SIZE % water_cell_size == 0);
1286 
1287 	if (m_VBWater)
1288 	{
1289 		g_VBMan.Release(m_VBWater);
1290 		m_VBWater = nullptr;
1291 	}
1292 	if (m_VBWaterIndices)
1293 	{
1294 		g_VBMan.Release(m_VBWaterIndices);
1295 		m_VBWaterIndices = nullptr;
1296 	}
1297 	if (m_VBWaterShore)
1298 	{
1299 		g_VBMan.Release(m_VBWaterShore);
1300 		m_VBWaterShore = nullptr;
1301 	}
1302 	if (m_VBWaterIndicesShore)
1303 	{
1304 		g_VBMan.Release(m_VBWaterIndicesShore);
1305 		m_VBWaterIndicesShore = nullptr;
1306 	}
1307 	m_WaterBounds.SetEmpty();
1308 
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)
1313 		return;
1314 
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));
1320 
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));
1326 
1327 	WaterManager* WaterMgr = g_Renderer.GetWaterManager();
1328 
1329 	CPatch* patch = m_Patch;
1330 	CTerrain* terrain = patch->m_Parent;
1331 
1332 	ssize_t mapSize = terrain->GetVerticesPerSide();
1333 
1334 	// Top-left coordinates of our patch.
1335 	ssize_t px = m_Patch->m_X * PATCH_SIZE;
1336 	ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
1337 
1338 	// To whoever implements different water heights, this is a TODO: water height)
1339 	float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
1340 
1341 	// The 4 points making a water tile.
1342 	int moves[4][2] = {
1343 		{0,0},
1344 		{water_cell_size,0},
1345 		{0,water_cell_size},
1346 		{water_cell_size,water_cell_size}
1347 	};
1348 	// Where to look for when checking for water for shore tiles.
1349 	int check[10][2] = {
1350 		{0, 0},
1351 		{water_cell_size, 0},
1352 		{water_cell_size*2, 0},
1353 		{0, water_cell_size},
1354 		{0, water_cell_size*2},
1355 		{water_cell_size, water_cell_size},
1356 		{water_cell_size*2, water_cell_size*2},
1357 		{-water_cell_size, 0},
1358 		{0, -water_cell_size},
1359 		{-water_cell_size, -water_cell_size}
1360 	};
1361 
1362 	// build vertices, uv, and shader varying
1363 	for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
1364 	{
1365 		for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
1366 		{
1367 			// Check that this tile is close to water
1368 			bool nearWater = false;
1369 			for (size_t test = 0; test < 10; ++test)
1370 				if (terrain->GetVertexGroundLevel(x + px + check[test][0], z + pz + check[test][1]) < waterHeight)
1371 					nearWater = true;
1372 			if (!nearWater)
1373 				continue;
1374 
1375 			// This is actually lying and I should call CcmpTerrain
1376 			/*if (!terrain->IsOnMap(x+x1, z+z1)
1377 			 && !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
1378 			 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
1379 			 && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
1380 			 continue;*/
1381 
1382 			for (int i = 0; i < 4; ++i)
1383 			{
1384 				if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1385 					continue;
1386 
1387 				ssize_t xx = x + px + moves[i][0];
1388 				ssize_t zz = z + pz + moves[i][1];
1389 
1390 				SWaterVertex vertex;
1391 				terrain->CalcPosition(xx,zz, vertex.m_Position);
1392 				float depth = waterHeight - vertex.m_Position.Y;
1393 
1394 				vertex.m_Position.Y = waterHeight;
1395 
1396 				m_WaterBounds += vertex.m_Position;
1397 
1398 				vertex.m_WaterData = CVector2D(WaterMgr->m_WindStrength[xx + zz*mapSize], depth);
1399 
1400 				water_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data.size();
1401 				water_vertex_data.push_back(vertex);
1402 			}
1403 			water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1404 			water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
1405 			water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1406 			water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
1407 			water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
1408 			water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
1409 
1410 			// Check id this tile is partly over land.
1411 			// If so add a square over the terrain. This is necessary to render waves that go on shore.
1412 			if (terrain->GetVertexGroundLevel(x+px, z+pz) < waterHeight
1413 				&& terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz) < waterHeight
1414 				&& terrain->GetVertexGroundLevel(x+px, z+pz+water_cell_size) < waterHeight
1415 				&& terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz+water_cell_size) < waterHeight)
1416 				continue;
1417 
1418 			for (int i = 0; i < 4; ++i)
1419 			{
1420 				if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
1421 					continue;
1422 				ssize_t xx = x + px + moves[i][0];
1423 				ssize_t zz = x + pz + moves[i][1];
1424 
1425 				SWaterVertex vertex;
1426 				terrain->CalcPosition(xx,zz, vertex.m_Position);
1427 
1428 				vertex.m_Position.Y += 0.02f;
1429 				m_WaterBounds += vertex.m_Position;
1430 
1431 				vertex.m_WaterData = CVector2D(0.0f, -5.0f);
1432 
1433 				water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data_shore.size();
1434 				water_vertex_data_shore.push_back(vertex);
1435 			}
1436 			if (terrain->GetTriangulationDir(x + px, z + pz))
1437 			{
1438 				water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1439 				water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1440 				water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1441 				water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1442 				water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1443 				water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1444 			}
1445 			else
1446 			{
1447 				water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1448 				water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
1449 				water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1450 				water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
1451 				water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
1452 				water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
1453 			}
1454 		}
1455 	}
1456 
1457 	// No vertex buffers if no data generated
1458 	if (!water_indices.empty())
1459 	{
1460 		m_VBWater = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
1461 		m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater, &water_vertex_data[0]);
1462 
1463 		m_VBWaterIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
1464 		m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices, &water_indices[0]);
1465 	}
1466 
1467 	if (!water_indices_shore.empty())
1468 	{
1469 		m_VBWaterShore = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data_shore.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
1470 		m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore, &water_vertex_data_shore[0]);
1471 
1472 		// Construct indices buffer
1473 		m_VBWaterIndicesShore = g_VBMan.Allocate(sizeof(GLushort), water_indices_shore.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
1474 		m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore, &water_indices_shore[0]);
1475 	}
1476 }
1477 
RenderWater(CShaderProgramPtr & shader,bool onlyShore,bool fixedPipeline)1478 void CPatchRData::RenderWater(CShaderProgramPtr& shader, bool onlyShore, bool fixedPipeline)
1479 {
1480 	ASSERT(m_UpdateFlags==0);
1481 
1482 	if (g_Renderer.m_SkipSubmit || (!m_VBWater && !m_VBWaterShore))
1483 		return;
1484 
1485 #if !CONFIG2_GLES
1486 	if (g_Renderer.m_WaterRenderMode == WIREFRAME)
1487 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1488 #endif
1489 
1490 	if (m_VBWater != 0x0 && !onlyShore)
1491 	{
1492 		SWaterVertex *base=(SWaterVertex *)m_VBWater->m_Owner->Bind();
1493 
1494 		// setup data pointers
1495 		GLsizei stride = sizeof(SWaterVertex);
1496 		shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWater->m_Index].m_Position);
1497 		if (!fixedPipeline)
1498 			shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWater->m_Index].m_WaterData);
1499 
1500 		shader->AssertPointersBound();
1501 
1502 		u8* indexBase = m_VBWaterIndices->m_Owner->Bind();
1503 		glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndices->m_Count,
1504 					   GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndices->m_Index));
1505 
1506 		g_Renderer.m_Stats.m_DrawCalls++;
1507 		g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
1508 	}
1509 
1510 	if (m_VBWaterShore != 0x0 &&
1511 	    g_Renderer.GetWaterManager()->m_WaterEffects &&
1512 	    g_Renderer.GetWaterManager()->m_WaterFancyEffects)
1513 	{
1514 		SWaterVertex *base=(SWaterVertex *)m_VBWaterShore->m_Owner->Bind();
1515 
1516 		GLsizei stride = sizeof(SWaterVertex);
1517 		shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWaterShore->m_Index].m_Position);
1518 		if (!fixedPipeline)
1519 			shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWaterShore->m_Index].m_WaterData);
1520 
1521 		shader->AssertPointersBound();
1522 
1523 		u8* indexBase = m_VBWaterIndicesShore->m_Owner->Bind();
1524 		glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndicesShore->m_Count,
1525 					   GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndicesShore->m_Index));
1526 
1527 		g_Renderer.m_Stats.m_DrawCalls++;
1528 		g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;
1529 	}
1530 
1531 	CVertexBuffer::Unbind();
1532 
1533 #if !CONFIG2_GLES
1534 	if (g_Renderer.m_WaterRenderMode == WIREFRAME)
1535 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1536 #endif
1537 }
1538