source: ps/trunk/source/renderer/PatchRData.cpp@ 9635

Last change on this file since 9635 was 9635, checked in by philip, 11 years ago

Fix typo in comment

  • Property svn:eol-style set to native
File size: 32.5 KB
Line 
1/* Copyright (C) 2011 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/Patch.h"
27#include "graphics/Terrain.h"
28#include "lib/alignment.h"
29#include "lib/allocators/pool.h"
30#include "lib/res/graphics/unifont.h"
31#include "maths/MathUtil.h"
32#include "ps/CLogger.h"
33#include "ps/Game.h"
34#include "ps/Profile.h"
35#include "ps/Pyrogenesis.h"
36#include "ps/World.h"
37#include "ps/GameSetup/Config.h"
38#include "renderer/AlphaMapCalculator.h"
39#include "renderer/PatchRData.h"
40#include "renderer/Renderer.h"
41#include "renderer/WaterManager.h"
42#include "simulation2/Simulation2.h"
43#include "simulation2/components/ICmpWaterManager.h"
44
45const ssize_t BlendOffsets[9][2] = {
46 { 0, -1 },
47 { -1, -1 },
48 { -1, 0 },
49 { -1, 1 },
50 { 0, 1 },
51 { 1, 1 },
52 { 1, 0 },
53 { 1, -1 },
54 { 0, 0 }
55};
56
57///////////////////////////////////////////////////////////////////
58// CPatchRData constructor
59CPatchRData::CPatchRData(CPatch* patch) :
60 m_Patch(patch), m_VBSides(0), m_VBBase(0), m_VBBaseIndices(0), m_VBBlends(0), m_VBBlendIndices(0)
61{
62 ENSURE(patch);
63 Build();
64}
65
66///////////////////////////////////////////////////////////////////
67// CPatchRData destructor
68CPatchRData::~CPatchRData()
69{
70 // release vertex buffer chunks
71 if (m_VBSides) g_VBMan.Release(m_VBSides);
72 if (m_VBBase) g_VBMan.Release(m_VBBase);
73 if (m_VBBaseIndices) g_VBMan.Release(m_VBBaseIndices);
74 if (m_VBBlends) g_VBMan.Release(m_VBBlends);
75 if (m_VBBlendIndices) g_VBMan.Release(m_VBBlendIndices);
76}
77
78const float uvFactor = 0.125f / sqrt(2.f);
79static void CalculateUV(float uv[2], ssize_t x, ssize_t z)
80{
81 // The UV axes are offset 45 degrees from XZ
82 uv[0] = ( x-z)*uvFactor;
83 uv[1] = (-x-z)*uvFactor;
84}
85
86/**
87 * Represents a blend for a single tile, texture and shape.
88 */
89struct STileBlend
90{
91 CTerrainTextureEntry* m_Texture;
92 int m_Priority;
93 u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
94
95 struct DecreasingPriority
96 {
97 bool operator()(const STileBlend& a, const STileBlend& b) const
98 {
99 if (a.m_Priority > b.m_Priority)
100 return true;
101 if (a.m_Priority < b.m_Priority)
102 return false;
103 if (a.m_Texture && b.m_Texture)
104 return a.m_Texture->GetTag() > b.m_Texture->GetTag();
105 return false;
106 }
107 };
108
109 struct CurrentTile
110 {
111 bool operator()(const STileBlend& a) const
112 {
113 return (a.m_TileMask & (1 << 8)) != 0;
114 }
115 };
116};
117
118/**
119 * Represents the ordered collection of blends drawn on a particular tile.
120 */
121struct STileBlendStack
122{
123 u8 i, j;
124 std::vector<STileBlend> blends; // back of vector is lowest-priority texture
125};
126
127/**
128 * Represents a batched collection of blends using the same texture.
129 */
130struct SBlendLayer
131{
132 struct Tile
133 {
134 u8 i, j;
135 u8 shape;
136 };
137
138 CTerrainTextureEntry* m_Texture;
139 std::vector<Tile> m_Tiles;
140};
141
142void CPatchRData::BuildBlends()
143{
144 m_BlendSplats.clear();
145
146 std::vector<SBlendVertex> blendVertices;
147 std::vector<u16> blendIndices;
148
149 CTerrain* terrain = m_Patch->m_Parent;
150
151 std::vector<STileBlendStack> blendStacks;
152 blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
153
154 // For each tile in patch ..
155 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
156 {
157 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
158 {
159 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
160 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
161
162 std::vector<STileBlend> blends;
163 blends.reserve(9);
164
165 // Compute a blend for every tile in the 3x3 square around this tile
166 for (size_t n = 0; n < 9; ++n)
167 {
168 ssize_t ox = gx + BlendOffsets[n][1];
169 ssize_t oz = gz + BlendOffsets[n][0];
170
171 CMiniPatch* nmp = terrain->GetTile(ox, oz);
172 if (!nmp)
173 continue;
174
175 STileBlend blend;
176 blend.m_Texture = nmp->GetTextureEntry();
177 blend.m_Priority = nmp->GetPriority();
178 blend.m_TileMask = 1 << n;
179 blends.push_back(blend);
180 }
181
182 // Sort the blends, highest priority first
183 std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
184
185 STileBlendStack blendStack;
186 blendStack.i = i;
187 blendStack.j = j;
188
189 // Put the blends into the tile's stack, merging any adjacent blends with the same texture
190 for (size_t k = 0; k < blends.size(); ++k)
191 {
192 if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
193 blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
194 else
195 blendStack.blends.push_back(blends[k]);
196 }
197
198 // Remove blends that are after (i.e. lower priority than) the current tile
199 // (including the current tile), since we don't want to render them on top of
200 // the tile's base texture
201 blendStack.blends.erase(
202 std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
203 blendStack.blends.end());
204
205 blendStacks.push_back(blendStack);
206 }
207 }
208
209 // Given the blend stack per tile, we want to batch together as many blends as possible.
210 // Group them into a series of layers (each of which has a single texture):
211 // (This is effectively a topological sort / linearisation of the partial order induced
212 // by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
213
214 std::vector<SBlendLayer> blendLayers;
215
216 while (true)
217 {
218 if (!blendLayers.empty())
219 {
220 // Try to grab as many tiles as possible that match our current layer,
221 // from off the blend stacks of all the tiles
222
223 CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
224
225 for (size_t k = 0; k < blendStacks.size(); ++k)
226 {
227 if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
228 {
229 SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, blendStacks[k].blends.back().m_TileMask };
230 blendLayers.back().m_Tiles.push_back(t);
231 blendStacks[k].blends.pop_back();
232 }
233 // (We've already merged adjacent entries of the same texture in each stack,
234 // so we don't need to bother looping to check the next entry in this stack again)
235 }
236 }
237
238 // We've grabbed as many tiles as possible; now we need to start a new layer.
239 // The new layer's texture could come from the back of any non-empty stack;
240 // choose the longest stack as a heuristic to reduce the number of layers
241 CTerrainTextureEntry* bestTex = NULL;
242 size_t bestStackSize = 0;
243
244 for (size_t k = 0; k < blendStacks.size(); ++k)
245 {
246 if (blendStacks[k].blends.size() > bestStackSize)
247 {
248 bestStackSize = blendStacks[k].blends.size();
249 bestTex = blendStacks[k].blends.back().m_Texture;
250 }
251 }
252
253 // If all our stacks were empty, we're done
254 if (bestStackSize == 0)
255 break;
256
257 // Otherwise add the new layer, then loop back and start filling it in
258
259 SBlendLayer layer;
260 layer.m_Texture = bestTex;
261 blendLayers.push_back(layer);
262 }
263
264 // Now build outgoing splats
265 m_BlendSplats.resize(blendLayers.size());
266
267 for (size_t k = 0; k < blendLayers.size(); ++k)
268 {
269 SSplat& splat = m_BlendSplats[k];
270 splat.m_IndexStart = blendIndices.size();
271 splat.m_Texture = blendLayers[k].m_Texture;
272
273 for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
274 {
275 SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
276 AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape);
277 }
278
279 splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
280 }
281
282 // Release existing vertex buffer chunks
283 if (m_VBBlends)
284 {
285 g_VBMan.Release(m_VBBlends);
286 m_VBBlends = 0;
287 }
288
289 if (m_VBBlendIndices)
290 {
291 g_VBMan.Release(m_VBBlendIndices);
292 m_VBBlendIndices = 0;
293 }
294
295 if (blendVertices.size())
296 {
297 // Construct vertex buffer
298
299 m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), blendVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
300 m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends, &blendVertices[0]);
301
302 // Update the indices to include the base offset of the vertex data
303 for (size_t k = 0; k < blendIndices.size(); ++k)
304 blendIndices[k] += m_VBBlends->m_Index;
305
306 m_VBBlendIndices = g_VBMan.Allocate(sizeof(u16), blendIndices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
307 m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices, &blendIndices[0]);
308 }
309}
310
311void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices, u16 i, u16 j, u8 shape)
312{
313 CTerrain* terrain = m_Patch->m_Parent;
314
315 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
316 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
317
318 // uses the current neighbour texture
319 BlendShape8 shape8;
320 for (size_t m = 0; m < 8; ++m)
321 shape8[m] = (shape & (1 << m)) ? 0 : 1;
322
323 // calculate the required alphamap and the required rotation of the alphamap from blendshape
324 unsigned int alphamapflags;
325 int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
326
327 // now actually render the blend tile (if we need one)
328 if (alphamap == -1)
329 return;
330
331 float u0 = g_Renderer.m_AlphaMapCoords[alphamap].u0;
332 float u1 = g_Renderer.m_AlphaMapCoords[alphamap].u1;
333 float v0 = g_Renderer.m_AlphaMapCoords[alphamap].v0;
334 float v1 = g_Renderer.m_AlphaMapCoords[alphamap].v1;
335
336 if (alphamapflags & BLENDMAP_FLIPU)
337 std::swap(u0, u1);
338
339 if (alphamapflags & BLENDMAP_FLIPV)
340 std::swap(v0, v1);
341
342 int base = 0;
343 if (alphamapflags & BLENDMAP_ROTATE90)
344 base = 1;
345 else if (alphamapflags & BLENDMAP_ROTATE180)
346 base = 2;
347 else if (alphamapflags & BLENDMAP_ROTATE270)
348 base = 3;
349
350 SBlendVertex vtx[4];
351 vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
352 vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
353 vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
354 vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
355 vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
356 vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
357 vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
358 vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
359
360 SBlendVertex dst;
361
362 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
363 CVector3D normal;
364
365 bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
366
367 size_t index = blendVertices.size();
368
369 CalculateUV(dst.m_UVs, gx, gz);
370 terrain->CalcPosition(gx, gz, dst.m_Position);
371 terrain->CalcNormal(gx, gz, normal);
372 dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
373 dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
374 dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
375 blendVertices.push_back(dst);
376
377 CalculateUV(dst.m_UVs, gx + 1, gz);
378 terrain->CalcPosition(gx + 1, gz, dst.m_Position);
379 terrain->CalcNormal(gx + 1, gz, normal);
380 dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
381 dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
382 dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
383 blendVertices.push_back(dst);
384
385 CalculateUV(dst.m_UVs, gx + 1, gz + 1);
386 terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
387 terrain->CalcNormal(gx + 1, gz + 1, normal);
388 dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
389 dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
390 dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
391 blendVertices.push_back(dst);
392
393 CalculateUV(dst.m_UVs, gx, gz + 1);
394 terrain->CalcPosition(gx, gz + 1, dst.m_Position);
395 terrain->CalcNormal(gx, gz + 1, normal);
396 dst.m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
397 dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
398 dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
399 blendVertices.push_back(dst);
400
401 bool dir = terrain->GetTriangulationDir(gx, gz);
402 if (dir)
403 {
404 blendIndices.push_back(index+0);
405 blendIndices.push_back(index+1);
406 blendIndices.push_back(index+3);
407
408 blendIndices.push_back(index+1);
409 blendIndices.push_back(index+2);
410 blendIndices.push_back(index+3);
411 }
412 else
413 {
414 blendIndices.push_back(index+0);
415 blendIndices.push_back(index+1);
416 blendIndices.push_back(index+2);
417
418 blendIndices.push_back(index+2);
419 blendIndices.push_back(index+3);
420 blendIndices.push_back(index+0);
421 }
422}
423
424void CPatchRData::BuildIndices()
425{
426 CTerrain* terrain = m_Patch->m_Parent;
427
428 ssize_t px = m_Patch->m_X * PATCH_SIZE;
429 ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
430
431 // must have allocated some vertices before trying to build corresponding indices
432 ENSURE(m_VBBase);
433
434 // number of vertices in each direction in each patch
435 ssize_t vsize=PATCH_SIZE+1;
436
437 std::vector<unsigned short> indices;
438 indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
439
440 // release existing splats
441 m_Splats.clear();
442
443 // build grid of textures on this patch
444 std::vector<CTerrainTextureEntry*> textures;
445 CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
446 for (ssize_t j=0;j<PATCH_SIZE;j++) {
447 for (ssize_t i=0;i<PATCH_SIZE;i++) {
448 CTerrainTextureEntry* tex=m_Patch->m_MiniPatches[j][i].GetTextureEntry();
449 texgrid[j][i]=tex;
450 if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
451 textures.push_back(tex);
452 }
453 }
454 }
455
456 // now build base splats from interior textures
457 m_Splats.resize(textures.size());
458 // build indices for base splats
459 size_t base=m_VBBase->m_Index;
460 ENSURE(base + vsize*vsize < 65536); // mustn't overflow u16 indexes
461 for (size_t i=0;i<m_Splats.size();i++) {
462 CTerrainTextureEntry* tex=textures[i];
463
464 SSplat& splat=m_Splats[i];
465 splat.m_Texture=tex;
466 splat.m_IndexStart=indices.size();
467
468 for (ssize_t j = 0; j < PATCH_SIZE; j++)
469 {
470 for (ssize_t i = 0; i < PATCH_SIZE; i++)
471 {
472 if (texgrid[j][i] == tex)
473 {
474 bool dir = terrain->GetTriangulationDir(px+i, pz+j);
475 if (dir)
476 {
477 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
478 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
479 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
480
481 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
482 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
483 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
484 }
485 else
486 {
487 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
488 indices.push_back(u16(((j+0)*vsize+(i+1))+base));
489 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
490
491 indices.push_back(u16(((j+1)*vsize+(i+1))+base));
492 indices.push_back(u16(((j+1)*vsize+(i+0))+base));
493 indices.push_back(u16(((j+0)*vsize+(i+0))+base));
494 }
495 }
496 }
497 }
498 splat.m_IndexCount=indices.size()-splat.m_IndexStart;
499 }
500
501 // Release existing vertex buffer chunk
502 if (m_VBBaseIndices)
503 {
504 g_VBMan.Release(m_VBBaseIndices);
505 m_VBBaseIndices = 0;
506 }
507
508 ENSURE(indices.size());
509
510 // Construct vertex buffer
511 m_VBBaseIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
512 m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices, &indices[0]);
513}
514
515
516void CPatchRData::BuildVertices()
517{
518 // create both vertices and lighting colors
519
520 // number of vertices in each direction in each patch
521 ssize_t vsize=PATCH_SIZE+1;
522
523 std::vector<SBaseVertex> vertices;
524 vertices.resize(vsize*vsize);
525
526 // get index of this patch
527 ssize_t px=m_Patch->m_X;
528 ssize_t pz=m_Patch->m_Z;
529
530 CTerrain* terrain=m_Patch->m_Parent;
531 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
532
533 bool includeSunColor = (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER);
534
535 // build vertices
536 for (ssize_t j=0;j<vsize;j++) {
537 for (ssize_t i=0;i<vsize;i++) {
538 ssize_t ix=px*PATCH_SIZE+i;
539 ssize_t iz=pz*PATCH_SIZE+j;
540 ssize_t v=(j*vsize)+i;
541
542 // calculate vertex data
543 terrain->CalcPosition(ix,iz,vertices[v].m_Position);
544 CalculateUV(vertices[v].m_UVs, ix, iz);
545
546 // Calculate diffuse lighting for this vertex
547 // Ambient is added by the lighting pass (since ambient is the same
548 // for all vertices, it need not be stored in the vertex structure)
549 CVector3D normal;
550 terrain->CalcNormal(ix,iz,normal);
551
552 vertices[v].m_DiffuseColor = lightEnv.EvaluateDiffuse(normal, includeSunColor);
553 }
554 }
555
556 // upload to vertex buffer
557 if (!m_VBBase)
558 m_VBBase = g_VBMan.Allocate(sizeof(SBaseVertex), vsize * vsize, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
559
560 m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase, &vertices[0]);
561}
562
563void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
564{
565 ssize_t vsize = PATCH_SIZE + 1;
566 CTerrain* terrain = m_Patch->m_Parent;
567 CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
568
569 for (ssize_t k = 0; k < vsize; k++)
570 {
571 ssize_t gx = m_Patch->m_X * PATCH_SIZE;
572 ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
573 switch (side)
574 {
575 case CPATCH_SIDE_NEGX: gz += k; break;
576 case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
577 case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
578 case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
579 }
580
581 CVector3D pos;
582 terrain->CalcPosition(gx, gz, pos);
583
584 // Clamp the height to the water level
585 float waterHeight = 0.f;
586 if (!cmpWaterManager.null())
587 waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
588 pos.Y = std::max(pos.Y, waterHeight);
589
590 SSideVertex v0, v1;
591 v0.m_Position = pos;
592 v1.m_Position = pos;
593 v1.m_Position.Y = 0;
594
595 // If this is the start of this tristrip, but we've already got a partial
596 // tristrip, add a couple of degenerate triangles to join the strips properly
597 if (k == 0 && !vertices.empty())
598 {
599 vertices.push_back(vertices.back());
600 vertices.push_back(v1);
601 }
602
603 // Now add the new triangles
604 vertices.push_back(v1);
605 vertices.push_back(v0);
606 }
607}
608
609void CPatchRData::BuildSides()
610{
611 std::vector<SSideVertex> sideVertices;
612
613 int sideFlags = m_Patch->GetSideFlags();
614
615 // If no sides are enabled, we don't need to do anything
616 if (!sideFlags)
617 return;
618
619 // For each side, generate a tristrip by adding a vertex at ground/water
620 // level and a vertex underneath at height 0.
621
622 if (sideFlags & CPATCH_SIDE_NEGX)
623 BuildSide(sideVertices, CPATCH_SIDE_NEGX);
624
625 if (sideFlags & CPATCH_SIDE_POSX)
626 BuildSide(sideVertices, CPATCH_SIDE_POSX);
627
628 if (sideFlags & CPATCH_SIDE_NEGZ)
629 BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
630
631 if (sideFlags & CPATCH_SIDE_POSZ)
632 BuildSide(sideVertices, CPATCH_SIDE_POSZ);
633
634 if (sideVertices.empty())
635 return;
636
637 if (!m_VBSides)
638 m_VBSides = g_VBMan.Allocate(sizeof(SSideVertex), sideVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
639 m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides, &sideVertices[0]);
640}
641
642void CPatchRData::Build()
643{
644 BuildVertices();
645 BuildSides();
646 BuildIndices();
647 BuildBlends();
648}
649
650void CPatchRData::Update()
651{
652 if (m_UpdateFlags!=0) {
653 // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
654 // than everything; it's complicated slightly because the blends are dependent
655 // on both vertex and index data
656 BuildVertices();
657 BuildSides();
658 BuildIndices();
659 BuildBlends();
660
661 m_UpdateFlags=0;
662 }
663}
664
665// Types used for glMultiDrawElements batching:
666
667// To minimise the cost of memory allocations, everything used for computing
668// batches uses a pool allocator. (All allocations are short-lived so we can
669// just throw away the whole pool at the end of each frame.)
670
671// std::map types with appropriate pool allocators and default comparison operator
672#define POOLED_BATCH_MAP(Key, Value) \
673 std::map<Key, Value, std::less<Key>, pool_allocator<std::pair<Key const, Value> > >
674
675// Equivalent to "m[k]", when it returns a pool-allocated std::map (since we can't
676// use the default constructor in that case)
677template<typename M>
678typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, RawPoolAllocator& pool)
679{
680 return m.insert(std::make_pair(k,
681 typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(pool))
682 )).first->second;
683}
684
685// Equivalent to "m[k]", when it returns a std::pair of pool-allocated std::vectors
686template<typename M>
687typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, RawPoolAllocator& pool)
688{
689 return m.insert(std::make_pair(k, std::make_pair(
690 typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(pool)),
691 typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(pool))
692 ))).first->second;
693}
694
695static const size_t POOL_SIZE = 4*MiB; // this should be enough for fairly huge maps
696
697// Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
698typedef std::pair<std::vector<GLint, pool_allocator<GLint> >, std::vector<void*, pool_allocator<void*> > > BatchElements;
699
700// Group batches by index buffer
701typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
702
703// Group batches by vertex buffer
704typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches;
705
706// Group batches by texture
707typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches;
708
709void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches)
710{
711 RawPoolAllocator pool(POOL_SIZE);
712
713 TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(pool)));
714
715 PROFILE_START("compute batches");
716
717 // Collect all the patches' base splats into their appropriate batches
718 for (size_t i = 0; i < patches.size(); ++i)
719 {
720 CPatchRData* patch = patches[i];
721 for (size_t j = 0; j < patch->m_Splats.size(); ++j)
722 {
723 SSplat& splat = patch->m_Splats[j];
724
725 BatchElements& batch = PooledPairGet(
726 PooledMapGet(
727 PooledMapGet(batches, splat.m_Texture, pool),
728 patch->m_VBBase->m_Owner, pool
729 ),
730 patch->m_VBBaseIndices->m_Owner, pool
731 );
732
733 batch.first.push_back(splat.m_IndexCount);
734
735 u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
736 batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart));
737 }
738 }
739
740 PROFILE_END("compute batches");
741
742 // Render each batch
743 for (TextureBatches::iterator itt = batches.begin(); itt != batches.end(); ++itt)
744 {
745 if (itt->first)
746 itt->first->GetTexture()->Bind();
747 else
748 g_Renderer.GetTextureManager().GetErrorTexture()->Bind();
749
750 for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
751 {
752 GLsizei stride = sizeof(SBaseVertex);
753 SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
754 glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
755 glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
756 glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]);
757
758 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
759 {
760 it->first->Bind();
761
762 BatchElements& batch = it->second;
763
764 if (!g_Renderer.m_SkipSubmit)
765 {
766 // Don't use glMultiDrawElements here since it doesn't have a significant
767 // performance impact and it suffers from various driver bugs (e.g. it breaks
768 // in Mesa 7.10 swrast with index VBOs)
769 for (size_t i = 0; i < batch.first.size(); ++i)
770 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
771 }
772
773 g_Renderer.m_Stats.m_DrawCalls++;
774 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
775 }
776 }
777 }
778
779 CVertexBuffer::Unbind();
780}
781
782/**
783 * Helper structure for RenderBlends.
784 */
785struct SBlendBatch
786{
787 SBlendBatch(RawPoolAllocator& pool) :
788 m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(pool))
789 {
790 }
791
792 CTerrainTextureEntry* m_Texture;
793 VertexBufferBatches m_Batches;
794};
795
796/**
797 * Helper structure for RenderBlends.
798 */
799struct SBlendStackItem
800{
801 SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
802 const std::vector<CPatchRData::SSplat>& s, RawPoolAllocator& pool) :
803 vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(pool))
804 {
805 }
806
807 typedef std::vector<CPatchRData::SSplat, pool_allocator<CPatchRData::SSplat*> > SplatStack;
808 CVertexBuffer::VBChunk* vertices;
809 CVertexBuffer::VBChunk* indices;
810 SplatStack splats;
811};
812
813void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
814{
815 RawPoolAllocator pool(POOL_SIZE);
816
817 typedef std::vector<SBlendBatch, pool_allocator<SBlendBatch*> > BatchesStack;
818 BatchesStack batches((BatchesStack::allocator_type(pool)));
819
820 PROFILE_START("compute batches");
821
822 // Reserve an arbitrary size that's probably big enough in most cases,
823 // to avoid heavy reallocations
824 batches.reserve(256);
825
826 typedef std::vector<SBlendStackItem, pool_allocator<SBlendStackItem*> > BlendStacks;
827 BlendStacks blendStacks((BlendStacks::allocator_type(pool)));
828 blendStacks.reserve(patches.size());
829
830 // Extract all the blend splats from each patch
831 for (size_t i = 0; i < patches.size(); ++i)
832 {
833 CPatchRData* patch = patches[i];
834 if (!patch->m_BlendSplats.empty())
835 {
836
837 blendStacks.push_back(SBlendStackItem(patch->m_VBBlends, patch->m_VBBlendIndices, patch->m_BlendSplats, pool));
838 // Reverse the splats so the first to be rendered is at the back of the list
839 std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
840 }
841 }
842
843 // Rearrange the collection of splats to be grouped by texture, preserving
844 // order of splats within each patch:
845 // (This is exactly the same algorithm used in CPatchRData::BuildBlends,
846 // but applied to patch-sized splats rather than to tile-sized splats;
847 // see that function for comments on the algorithm.)
848 while (true)
849 {
850 if (!batches.empty())
851 {
852 CTerrainTextureEntry* tex = batches.back().m_Texture;
853
854 for (size_t k = 0; k < blendStacks.size(); ++k)
855 {
856 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
857 if (!splats.empty() && splats.back().m_Texture == tex)
858 {
859 CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
860 CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
861
862 BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, pool), indices->m_Owner, pool);
863 batch.first.push_back(splats.back().m_IndexCount);
864
865 u8* indexBase = indices->m_Owner->GetBindAddress();
866 batch.second.push_back(indexBase + sizeof(u16)*(indices->m_Index + splats.back().m_IndexStart));
867
868 splats.pop_back();
869 }
870 }
871 }
872
873 CTerrainTextureEntry* bestTex = NULL;
874 size_t bestStackSize = 0;
875
876 for (size_t k = 0; k < blendStacks.size(); ++k)
877 {
878 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
879 if (splats.size() > bestStackSize)
880 {
881 bestStackSize = splats.size();
882 bestTex = splats.back().m_Texture;
883 }
884 }
885
886 if (bestStackSize == 0)
887 break;
888
889 SBlendBatch layer(pool);
890 layer.m_Texture = bestTex;
891 batches.push_back(layer);
892 }
893
894 PROFILE_END("compute batches");
895
896 CVertexBuffer* lastVB = NULL;
897
898 for (BatchesStack::iterator itt = batches.begin(); itt != batches.end(); ++itt)
899 {
900 if (itt->m_Texture)
901 itt->m_Texture->GetTexture()->Bind();
902 else
903 g_Renderer.GetTextureManager().GetErrorTexture()->Bind();
904
905 for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
906 {
907 // Rebind the VB only if it changed since the last batch
908 if (itv->first != lastVB)
909 {
910 lastVB = itv->first;
911 GLsizei stride = sizeof(SBlendVertex);
912 SBlendVertex *base = (SBlendVertex *)itv->first->Bind();
913
914 glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
915
916 glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
917
918 pglClientActiveTextureARB(GL_TEXTURE0);
919 glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]);
920
921 pglClientActiveTextureARB(GL_TEXTURE1);
922 glTexCoordPointer(2, GL_FLOAT, stride, &base->m_AlphaUVs[0]);
923 }
924
925 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
926 {
927 it->first->Bind();
928
929 BatchElements& batch = it->second;
930
931 if (!g_Renderer.m_SkipSubmit)
932 {
933 for (size_t i = 0; i < batch.first.size(); ++i)
934 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
935 }
936
937 g_Renderer.m_Stats.m_DrawCalls++;
938 g_Renderer.m_Stats.m_BlendSplats++;
939 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
940 }
941 }
942 }
943
944 pglClientActiveTextureARB(GL_TEXTURE0);
945
946 CVertexBuffer::Unbind();
947}
948
949void CPatchRData::RenderStreams(const std::vector<CPatchRData*>& patches, int streamflags)
950{
951 // Each batch has a list of index counts, and a list of pointers-to-first-indexes
952 typedef std::pair<std::vector<GLint>, std::vector<void*> > BatchElements;
953
954 // Group batches by index buffer
955 typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
956
957 // Group batches by vertex buffer
958 typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
959
960 VertexBufferBatches batches;
961
962 PROFILE_START("compute batches");
963
964 // Collect all the patches into their appropriate batches
965 for (size_t i = 0; i < patches.size(); ++i)
966 {
967 CPatchRData* patch = patches[i];
968 BatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
969
970 batch.first.push_back(patch->m_VBBaseIndices->m_Count);
971
972 u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
973 batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index));
974 }
975
976 PROFILE_END("compute batches");
977
978 // Render each batch
979 for (VertexBufferBatches::iterator itv = batches.begin(); itv != batches.end(); ++itv)
980 {
981 GLsizei stride = sizeof(SBaseVertex);
982 SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
983
984 glVertexPointer(3, GL_FLOAT, stride, &base->m_Position);
985 if (streamflags & STREAM_UV0)
986 {
987 pglClientActiveTextureARB(GL_TEXTURE0);
988 glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs);
989 }
990 if (streamflags & STREAM_POSTOUV0)
991 {
992 pglClientActiveTextureARB(GL_TEXTURE0);
993 glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
994 }
995 if (streamflags & STREAM_POSTOUV1)
996 {
997 pglClientActiveTextureARB(GL_TEXTURE1);
998 glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
999 }
1000 if (streamflags & STREAM_POSTOUV2)
1001 {
1002 pglClientActiveTextureARB(GL_TEXTURE2);
1003 glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
1004 }
1005 if (streamflags & STREAM_POSTOUV3)
1006 {
1007 pglClientActiveTextureARB(GL_TEXTURE3);
1008 glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
1009 }
1010 if (streamflags & STREAM_COLOR)
1011 {
1012 glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
1013 }
1014
1015 for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
1016 {
1017 it->first->Bind();
1018
1019 BatchElements& batch = it->second;
1020
1021 if (!g_Renderer.m_SkipSubmit)
1022 {
1023 for (size_t i = 0; i < batch.first.size(); ++i)
1024 glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
1025 }
1026
1027 g_Renderer.m_Stats.m_DrawCalls++;
1028 g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
1029 }
1030 }
1031
1032 pglClientActiveTextureARB(GL_TEXTURE0);
1033
1034 CVertexBuffer::Unbind();
1035}
1036
1037void CPatchRData::RenderOutline()
1038{
1039 CTerrain* terrain = m_Patch->m_Parent;
1040 ssize_t gx = m_Patch->m_X * PATCH_SIZE;
1041 ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
1042
1043 CVector3D pos;
1044 std::vector<CVector3D> line;
1045
1046 ssize_t i, j;
1047
1048 for (i = 0, j = 0; i <= PATCH_SIZE; ++i)
1049 {
1050 terrain->CalcPosition(gx + i, gz + j, pos);
1051 line.push_back(pos);
1052 }
1053 for (i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
1054 {
1055 terrain->CalcPosition(gx + i, gz + j, pos);
1056 line.push_back(pos);
1057 }
1058 for (i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
1059 {
1060 terrain->CalcPosition(gx + i, gz + j, pos);
1061 line.push_back(pos);
1062 }
1063 for (i = 0, j = PATCH_SIZE-1; j >= 0; --j)
1064 {
1065 terrain->CalcPosition(gx + i, gz + j, pos);
1066 line.push_back(pos);
1067 }
1068
1069 glVertexPointer(3, GL_FLOAT, sizeof(CVector3D), &line[0]);
1070 glDrawArrays(GL_LINE_STRIP, 0, line.size());
1071}
1072
1073void CPatchRData::RenderSides()
1074{
1075 ENSURE(m_UpdateFlags==0);
1076
1077 if (!m_VBSides)
1078 return;
1079
1080 SSideVertex *base = (SSideVertex *)m_VBSides->m_Owner->Bind();
1081
1082 // setup data pointers
1083 GLsizei stride = sizeof(SSideVertex);
1084 glVertexPointer(3, GL_FLOAT, stride, &base->m_Position);
1085
1086 if (!g_Renderer.m_SkipSubmit)
1087 glDrawArrays(GL_TRIANGLE_STRIP, m_VBSides->m_Index, (GLsizei)m_VBSides->m_Count);
1088
1089 // bump stats
1090 g_Renderer.m_Stats.m_DrawCalls++;
1091 g_Renderer.m_Stats.m_TerrainTris += m_VBSides->m_Count - 2;
1092
1093 CVertexBuffer::Unbind();
1094}
1095
1096void CPatchRData::RenderPriorities()
1097{
1098 CTerrain* terrain = m_Patch->m_Parent;
1099 CCamera* camera = g_Game->GetView()->GetCamera();
1100
1101 for (ssize_t j = 0; j < PATCH_SIZE; ++j)
1102 {
1103 for (ssize_t i = 0; i < PATCH_SIZE; ++i)
1104 {
1105 ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
1106 ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
1107
1108 CVector3D pos;
1109 terrain->CalcPosition(gx, gz, pos);
1110
1111 // Move a bit towards the center of the tile
1112 pos.X += CELL_SIZE/4.f;
1113 pos.Z += CELL_SIZE/4.f;
1114
1115 float x, y;
1116 camera->GetScreenCoordinates(pos, x, y);
1117
1118 glPushMatrix();
1119 glTranslatef(x, g_yres - y, 0.f);
1120
1121 // Draw the text upside-down, because it's aligned with
1122 // the GUI (which uses the top-left as (0,0))
1123 glScalef(1.0f, -1.0f, 1.0f);
1124
1125 glwprintf(L"%d", m_Patch->m_MiniPatches[j][i].Priority);
1126 glPopMatrix();
1127 }
1128 }
1129}
Note: See TracBrowser for help on using the repository browser.