source: ps/trunk/source/graphics/Terrain.cpp@ 9929

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

# New territory border rendering.
Add textured line overlay rendering.
Change terrain height calculations to be triangulation-dependent for improved accuracy.
Add triangulation-dependent terrain normal function.
Support separate S/T wrap modes for textures.
Rename CVector2D_Maths since it no longer conflicts with simulation CVector2D.
Coalesce freed chunks in vertex buffers, to avoid excessive fragmentation.
Add some things to help debug vertex buffer allocation a little.

  • Property svn:eol-style set to native
File size: 19.7 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/*
19 * Describes ground via heightmap and array of CPatch.
20 */
21
22#include "precompiled.h"
23
24#include "lib/res/graphics/ogl_tex.h"
25#include "lib/sysdep/cpu.h"
26
27#include "renderer/Renderer.h"
28
29#include "TerrainProperties.h"
30#include "TerrainTextureEntry.h"
31#include "TerrainTextureManager.h"
32
33#include <string.h>
34#include "Terrain.h"
35#include "Patch.h"
36#include "maths/FixedVector3D.h"
37#include "maths/MathUtil.h"
38#include "ps/CLogger.h"
39
40///////////////////////////////////////////////////////////////////////////////
41// CTerrain constructor
42CTerrain::CTerrain()
43: m_Heightmap(0), m_Patches(0), m_MapSize(0), m_MapSizePatches(0),
44m_BaseColour(255, 255, 255, 255)
45{
46}
47
48///////////////////////////////////////////////////////////////////////////////
49// CTerrain constructor
50CTerrain::~CTerrain()
51{
52 ReleaseData();
53}
54
55
56///////////////////////////////////////////////////////////////////////////////
57// ReleaseData: delete any data allocated by this terrain
58void CTerrain::ReleaseData()
59{
60 delete[] m_Heightmap;
61 delete[] m_Patches;
62}
63
64
65///////////////////////////////////////////////////////////////////////////////
66// Initialise: initialise this terrain to the given size
67// using given heightmap to setup elevation data
68bool CTerrain::Initialize(ssize_t patchesPerSide,const u16* data)
69{
70 // clean up any previous terrain
71 ReleaseData();
72
73 // store terrain size
74 m_MapSize=patchesPerSide*PATCH_SIZE+1;
75 m_MapSizePatches=patchesPerSide;
76 // allocate data for new terrain
77 m_Heightmap=new u16[m_MapSize*m_MapSize];
78 m_Patches=new CPatch[m_MapSizePatches*m_MapSizePatches];
79
80 // given a heightmap?
81 if (data) {
82 // yes; keep a copy of it
83 memcpy(m_Heightmap,data,m_MapSize*m_MapSize*sizeof(u16));
84 } else {
85 // build a flat terrain
86 memset(m_Heightmap,0,m_MapSize*m_MapSize*sizeof(u16));
87 }
88
89 // setup patch parents, indices etc
90 InitialisePatches();
91
92 return true;
93}
94
95///////////////////////////////////////////////////////////////////////////////
96
97CStr8 CTerrain::GetMovementClass(ssize_t i, ssize_t j) const
98{
99 CMiniPatch* tile = GetTile(i, j);
100 if (tile && tile->GetTextureEntry())
101 return tile->GetTextureEntry()->GetProperties().GetMovementClass();
102
103 return "default";
104}
105
106///////////////////////////////////////////////////////////////////////////////
107// CalcPosition: calculate the world space position of the vertex at (i,j)
108// If i,j is off the map, it acts as if the edges of the terrain are extended
109// outwards to infinity
110void CTerrain::CalcPosition(ssize_t i, ssize_t j, CVector3D& pos) const
111{
112 ssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);
113 ssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);
114 u16 height = m_Heightmap[hj*m_MapSize + hi];
115 pos.X = float(i*CELL_SIZE);
116 pos.Y = float(height*HEIGHT_SCALE);
117 pos.Z = float(j*CELL_SIZE);
118}
119
120///////////////////////////////////////////////////////////////////////////////
121// CalcPositionFixed: calculate the world space position of the vertex at (i,j)
122void CTerrain::CalcPositionFixed(ssize_t i, ssize_t j, CFixedVector3D& pos) const
123{
124 ssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);
125 ssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);
126 u16 height = m_Heightmap[hj*m_MapSize + hi];
127 pos.X = fixed::FromInt(i) * (int)CELL_SIZE;
128 pos.Y = fixed::FromInt(height) / (int)HEIGHT_UNITS_PER_METRE;
129 pos.Z = fixed::FromInt(j) * (int)CELL_SIZE;
130}
131
132
133///////////////////////////////////////////////////////////////////////////////
134// CalcNormal: calculate the world space normal of the vertex at (i,j)
135void CTerrain::CalcNormal(ssize_t i, ssize_t j, CVector3D& normal) const
136{
137 CVector3D left, right, up, down;
138
139 // Calculate normals of the four half-tile triangles surrounding this vertex:
140
141 // get position of vertex where normal is being evaluated
142 CVector3D basepos;
143 CalcPosition(i, j, basepos);
144
145 if (i > 0) {
146 CalcPosition(i-1, j, left);
147 left -= basepos;
148 left.Normalize();
149 }
150
151 if (i < m_MapSize-1) {
152 CalcPosition(i+1, j, right);
153 right -= basepos;
154 right.Normalize();
155 }
156
157 if (j > 0) {
158 CalcPosition(i, j-1, up);
159 up -= basepos;
160 up.Normalize();
161 }
162
163 if (j < m_MapSize-1) {
164 CalcPosition(i, j+1, down);
165 down -= basepos;
166 down.Normalize();
167 }
168
169 CVector3D n0 = up.Cross(left);
170 CVector3D n1 = left.Cross(down);
171 CVector3D n2 = down.Cross(right);
172 CVector3D n3 = right.Cross(up);
173
174 // Compute the mean of the normals
175 normal = n0 + n1 + n2 + n3;
176 float nlen=normal.Length();
177 if (nlen>0.00001f) normal*=1.0f/nlen;
178}
179
180///////////////////////////////////////////////////////////////////////////////
181// CalcNormalFixed: calculate the world space normal of the vertex at (i,j)
182void CTerrain::CalcNormalFixed(ssize_t i, ssize_t j, CFixedVector3D& normal) const
183{
184 CFixedVector3D left, right, up, down;
185
186 // Calculate normals of the four half-tile triangles surrounding this vertex:
187
188 // get position of vertex where normal is being evaluated
189 CFixedVector3D basepos;
190 CalcPositionFixed(i, j, basepos);
191
192 if (i > 0) {
193 CalcPositionFixed(i-1, j, left);
194 left -= basepos;
195 left.Normalize();
196 }
197
198 if (i < m_MapSize-1) {
199 CalcPositionFixed(i+1, j, right);
200 right -= basepos;
201 right.Normalize();
202 }
203
204 if (j > 0) {
205 CalcPositionFixed(i, j-1, up);
206 up -= basepos;
207 up.Normalize();
208 }
209
210 if (j < m_MapSize-1) {
211 CalcPositionFixed(i, j+1, down);
212 down -= basepos;
213 down.Normalize();
214 }
215
216 CFixedVector3D n0 = up.Cross(left);
217 CFixedVector3D n1 = left.Cross(down);
218 CFixedVector3D n2 = down.Cross(right);
219 CFixedVector3D n3 = right.Cross(up);
220
221 // Compute the mean of the normals
222 normal = n0 + n1 + n2 + n3;
223 normal.Normalize();
224}
225
226CVector3D CTerrain::CalcExactNormal(float x, float z) const
227{
228 // Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
229 const ssize_t xi = clamp((ssize_t)floor(x/CELL_SIZE), (ssize_t)0, m_MapSize-2);
230 const ssize_t zi = clamp((ssize_t)floor(z/CELL_SIZE), (ssize_t)0, m_MapSize-2);
231
232 const float xf = clamp(x/CELL_SIZE-xi, 0.0f, 1.0f);
233 const float zf = clamp(z/CELL_SIZE-zi, 0.0f, 1.0f);
234
235 float h00 = m_Heightmap[zi*m_MapSize + xi];
236 float h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
237 float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
238 float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
239
240 // Determine which terrain triangle this point is on,
241 // then compute the normal of that triangle's plane
242
243 if (GetTriangulationDir(xi, zi))
244 {
245 if (xf + zf <= 1.f)
246 {
247 // Lower-left triangle (don't use h11)
248 return -CVector3D(CELL_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
249 }
250 else
251 {
252 // Upper-right triangle (don't use h00)
253 return -CVector3D(CELL_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
254 }
255 }
256 else
257 {
258 if (xf <= zf)
259 {
260 // Upper-left triangle (don't use h10)
261 return -CVector3D(CELL_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
262 }
263 else
264 {
265 // Lower-right triangle (don't use h01)
266 return -CVector3D(CELL_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
267 }
268 }
269}
270
271///////////////////////////////////////////////////////////////////////////////
272// GetPatch: return the patch at (i,j) in patch space, or null if the patch is
273// out of bounds
274CPatch* CTerrain::GetPatch(ssize_t i, ssize_t j) const
275{
276 // range check (invalid indices are passed in by the culling and
277 // patch blend code because they iterate from 0..#patches and examine
278 // neighbors without checking if they're already on the edge)
279 if( (size_t)i >= (size_t)m_MapSizePatches || (size_t)j >= (size_t)m_MapSizePatches )
280 return 0;
281
282 return &m_Patches[(j*m_MapSizePatches)+i];
283}
284
285
286///////////////////////////////////////////////////////////////////////////////
287// GetTile: return the tile at (i,j) in tile space, or null if the tile is out
288// of bounds
289CMiniPatch* CTerrain::GetTile(ssize_t i, ssize_t j) const
290{
291 // see comment above
292 if( (size_t)i >= (size_t)(m_MapSize-1) || (size_t)j >= (size_t)(m_MapSize-1) )
293 return 0;
294
295 CPatch* patch=GetPatch(i/PATCH_SIZE, j/PATCH_SIZE); // can't fail (due to above check)
296 return &patch->m_MiniPatches[j%PATCH_SIZE][i%PATCH_SIZE];
297}
298
299float CTerrain::GetVertexGroundLevel(ssize_t i, ssize_t j) const
300{
301 i = clamp(i, (ssize_t)0, m_MapSize-1);
302 j = clamp(j, (ssize_t)0, m_MapSize-1);
303 return HEIGHT_SCALE * m_Heightmap[j*m_MapSize + i];
304}
305
306fixed CTerrain::GetVertexGroundLevelFixed(ssize_t i, ssize_t j) const
307{
308 i = clamp(i, (ssize_t)0, m_MapSize-1);
309 j = clamp(j, (ssize_t)0, m_MapSize-1);
310 // Convert to fixed metres (being careful to avoid intermediate overflows)
311 return fixed::FromInt(m_Heightmap[j*m_MapSize + i] / 2) / (int)(HEIGHT_UNITS_PER_METRE / 2);
312}
313
314fixed CTerrain::GetSlopeFixed(ssize_t i, ssize_t j) const
315{
316 // Clamp to size-2 so we can use the tiles (i,j)-(i+1,j+1)
317 i = clamp(i, (ssize_t)0, m_MapSize-2);
318 j = clamp(j, (ssize_t)0, m_MapSize-2);
319
320 u16 h00 = m_Heightmap[j*m_MapSize + i];
321 u16 h01 = m_Heightmap[(j+1)*m_MapSize + i];
322 u16 h10 = m_Heightmap[j*m_MapSize + (i+1)];
323 u16 h11 = m_Heightmap[(j+1)*m_MapSize + (i+1)];
324
325 // Difference of highest point from lowest point
326 u16 delta = std::max(std::max(h00, h01), std::max(h10, h11)) -
327 std::min(std::min(h00, h01), std::min(h10, h11));
328
329 // Compute fractional slope (being careful to avoid intermediate overflows)
330 return fixed::FromInt(delta / CELL_SIZE) / (int)HEIGHT_UNITS_PER_METRE;
331}
332
333float CTerrain::GetExactGroundLevel(float x, float z) const
334{
335 // Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
336 const ssize_t xi = clamp((ssize_t)floor(x/CELL_SIZE), (ssize_t)0, m_MapSize-2);
337 const ssize_t zi = clamp((ssize_t)floor(z/CELL_SIZE), (ssize_t)0, m_MapSize-2);
338
339 const float xf = clamp(x/CELL_SIZE-xi, 0.0f, 1.0f);
340 const float zf = clamp(z/CELL_SIZE-zi, 0.0f, 1.0f);
341
342 float h00 = m_Heightmap[zi*m_MapSize + xi];
343 float h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
344 float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
345 float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
346
347 // Determine which terrain triangle this point is on,
348 // then compute the linearly-interpolated height on that triangle's plane
349
350 if (GetTriangulationDir(xi, zi))
351 {
352 if (xf + zf <= 1.f)
353 {
354 // Lower-left triangle (don't use h11)
355 return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h01-h00)*zf);
356 }
357 else
358 {
359 // Upper-right triangle (don't use h00)
360 return HEIGHT_SCALE * (h11 + (h01-h11)*(1-xf) + (h10-h11)*(1-zf));
361 }
362 }
363 else
364 {
365 if (xf <= zf)
366 {
367 // Upper-left triangle (don't use h10)
368 return HEIGHT_SCALE * (h00 + (h11-h01)*xf + (h01-h00)*zf);
369 }
370 else
371 {
372 // Lower-right triangle (don't use h01)
373 return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h11-h10)*zf);
374 }
375 }
376}
377
378fixed CTerrain::GetExactGroundLevelFixed(fixed x, fixed z) const
379{
380 // Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
381 const ssize_t xi = clamp((ssize_t)(x / (int)CELL_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
382 const ssize_t zi = clamp((ssize_t)(z / (int)CELL_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
383
384 const fixed one = fixed::FromInt(1);
385
386 const fixed xf = clamp((x / (int)CELL_SIZE) - fixed::FromInt(xi), fixed::Zero(), one);
387 const fixed zf = clamp((z / (int)CELL_SIZE) - fixed::FromInt(zi), fixed::Zero(), one);
388
389 u16 h00 = m_Heightmap[zi*m_MapSize + xi];
390 u16 h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
391 u16 h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
392 u16 h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
393
394 // Intermediate scaling of xf, so we don't overflow in the multiplications below
395 // (h00 <= 65535, xf <= 1, max fixed is < 32768; divide by 2 here so xf1*h00 <= 32767.5)
396 const fixed xf0 = xf / 2;
397 const fixed xf1 = (one - xf) / 2;
398
399 // Linearly interpolate
400 return ((one - zf).Multiply(xf1 * h00 + xf0 * h10)
401 + zf.Multiply(xf1 * h01 + xf0 * h11)) / (int)(HEIGHT_UNITS_PER_METRE / 2);
402
403 // TODO: This should probably be more like GetExactGroundLevel()
404 // in handling triangulation properly
405}
406
407bool CTerrain::GetTriangulationDir(ssize_t i, ssize_t j) const
408{
409 // Clamp to size-2 so we can use the tiles (i,j)-(i+1,j+1)
410 i = clamp(i, (ssize_t)0, m_MapSize-2);
411 j = clamp(j, (ssize_t)0, m_MapSize-2);
412
413 int h00 = m_Heightmap[j*m_MapSize + i];
414 int h01 = m_Heightmap[(j+1)*m_MapSize + i];
415 int h10 = m_Heightmap[j*m_MapSize + (i+1)];
416 int h11 = m_Heightmap[(j+1)*m_MapSize + (i+1)];
417
418 // Prefer triangulating in whichever direction means the midpoint of the diagonal
419 // will be the highest. (In particular this means a diagonal edge will be straight
420 // along the top, and jagged along the bottom, which makes sense for terrain.)
421 int mid1 = h00+h11;
422 int mid2 = h01+h10;
423 return (mid1 < mid2);
424}
425
426///////////////////////////////////////////////////////////////////////////////
427// Resize: resize this terrain to the given size (in patches per side)
428void CTerrain::Resize(ssize_t size)
429{
430 if (size==m_MapSizePatches) {
431 // inexplicable request to resize terrain to the same size .. ignore it
432 return;
433 }
434
435 if (!m_Heightmap) {
436 // not yet created a terrain; build a default terrain of the given size now
437 Initialize(size,0);
438 return;
439 }
440
441 // allocate data for new terrain
442 ssize_t newMapSize=size*PATCH_SIZE+1;
443 u16* newHeightmap=new u16[newMapSize*newMapSize];
444 CPatch* newPatches=new CPatch[size*size];
445
446 if (size>m_MapSizePatches) {
447 // new map is bigger than old one - zero the heightmap so we don't get uninitialised
448 // height data along the expanded edges
449 memset(newHeightmap,0,newMapSize*newMapSize*sizeof(u16));
450 }
451
452 // now copy over rows of data
453 u16* src=m_Heightmap;
454 u16* dst=newHeightmap;
455 ssize_t copysize=std::min(newMapSize, m_MapSize);
456 for (ssize_t j=0;j<copysize;j++) {
457 memcpy(dst,src,copysize*sizeof(u16));
458 dst+=copysize;
459 src+=m_MapSize;
460 if (newMapSize>m_MapSize) {
461 // extend the last height to the end of the row
462 for (size_t i=0;i<newMapSize-(size_t)m_MapSize;i++) {
463 *dst++=*(src-1);
464 }
465 }
466 }
467
468
469 if (newMapSize>m_MapSize) {
470 // copy over heights of the last row to any remaining rows
471 src=newHeightmap+((m_MapSize-1)*newMapSize);
472 dst=src+newMapSize;
473 for (ssize_t i=0;i<newMapSize-m_MapSize;i++) {
474 memcpy(dst,src,newMapSize*sizeof(u16));
475 dst+=newMapSize;
476 }
477 }
478
479 // now build new patches
480 for (ssize_t j=0;j<size;j++) {
481 for (ssize_t i=0;i<size;i++) {
482 // copy over texture data from existing tiles, if possible
483 if (i<m_MapSizePatches && j<m_MapSizePatches) {
484 memcpy(newPatches[j*size+i].m_MiniPatches,m_Patches[j*m_MapSizePatches+i].m_MiniPatches,sizeof(CMiniPatch)*PATCH_SIZE*PATCH_SIZE);
485 }
486 }
487
488 if (j<m_MapSizePatches && size>m_MapSizePatches) {
489 // copy over the last tile from each column
490 for (ssize_t n=0;n<size-m_MapSizePatches;n++) {
491 for (ssize_t m=0;m<PATCH_SIZE;m++) {
492 CMiniPatch& src=m_Patches[j*m_MapSizePatches+m_MapSizePatches-1].m_MiniPatches[m][15];
493 for (ssize_t k=0;k<PATCH_SIZE;k++) {
494 CMiniPatch& dst=newPatches[j*size+m_MapSizePatches+n].m_MiniPatches[m][k];
495 dst = src;
496 }
497 }
498 }
499 }
500 }
501
502 if (size>m_MapSizePatches) {
503 // copy over the last tile from each column
504 CPatch* srcpatch=&newPatches[(m_MapSizePatches-1)*size];
505 CPatch* dstpatch=srcpatch+size;
506 for (ssize_t p=0;p<(ssize_t)size-m_MapSizePatches;p++) {
507 for (ssize_t n=0;n<(ssize_t)size;n++) {
508 for (ssize_t m=0;m<PATCH_SIZE;m++) {
509 for (ssize_t k=0;k<PATCH_SIZE;k++) {
510 CMiniPatch& src=srcpatch->m_MiniPatches[15][k];
511 CMiniPatch& dst=dstpatch->m_MiniPatches[m][k];
512 dst = src;
513 }
514 }
515 srcpatch++;
516 dstpatch++;
517 }
518 }
519 }
520
521
522 // release all the original data
523 ReleaseData();
524
525 // store new data
526 m_Heightmap=newHeightmap;
527 m_Patches=newPatches;
528 m_MapSize=(ssize_t)newMapSize;
529 m_MapSizePatches=(ssize_t)size;
530
531 // initialise all the new patches
532 InitialisePatches();
533}
534
535///////////////////////////////////////////////////////////////////////////////
536// InitialisePatches: initialise patch data
537void CTerrain::InitialisePatches()
538{
539 for (ssize_t j=0;j<m_MapSizePatches;j++) {
540 for (ssize_t i=0;i<m_MapSizePatches;i++) {
541 CPatch* patch=GetPatch(i,j); // can't fail
542 patch->Initialize(this,i,j);
543 }
544 }
545}
546
547///////////////////////////////////////////////////////////////////////////////
548// SetHeightMap: set up a new heightmap from 16-bit source data;
549// assumes heightmap matches current terrain size
550void CTerrain::SetHeightMap(u16* heightmap)
551{
552 // keep a copy of the given heightmap
553 memcpy(m_Heightmap,heightmap,m_MapSize*m_MapSize*sizeof(u16));
554
555 // recalculate patch bounds, invalidate vertices
556 for (ssize_t j=0;j<m_MapSizePatches;j++) {
557 for (ssize_t i=0;i<m_MapSizePatches;i++) {
558 CPatch* patch=GetPatch(i,j); // can't fail
559 patch->InvalidateBounds();
560 patch->SetDirty(RENDERDATA_UPDATE_VERTICES);
561 }
562 }
563}
564
565
566///////////////////////////////////////////////////////////////////////////////
567// FlattenArea: flatten out an area of terrain (specified in world space
568// coords); return the average height of the flattened area
569float CTerrain::FlattenArea(float x0, float x1, float z0, float z1)
570{
571 const ssize_t tx0 = clamp(ssize_t(x0/CELL_SIZE), (ssize_t)0, m_MapSize-1);
572 const ssize_t tx1 = clamp(ssize_t(x1/CELL_SIZE)+1, (ssize_t)0, m_MapSize-1);
573 const ssize_t tz0 = clamp(ssize_t(z0/CELL_SIZE), (ssize_t)0, m_MapSize-1);
574 const ssize_t tz1 = clamp(ssize_t(z1/CELL_SIZE)+1, (ssize_t)0, m_MapSize-1);
575
576 size_t count=0;
577 double sum=0.0f;
578 for (ssize_t z=tz0;z<=tz1;z++) {
579 for (ssize_t x=tx0;x<=tx1;x++) {
580 sum+=m_Heightmap[z*m_MapSize + x];
581 count++;
582 }
583 }
584 const u16 avgY = u16(sum/count);
585
586 for (ssize_t z=tz0;z<=tz1;z++) {
587 for (ssize_t x=tx0;x<=tx1;x++) {
588 m_Heightmap[z*m_MapSize + x]=avgY;
589 }
590 }
591
592 MakeDirty(tx0, tz0, tx1, tz1, RENDERDATA_UPDATE_VERTICES);
593
594 return avgY*HEIGHT_SCALE;
595}
596
597///////////////////////////////////////////////////////////////////////////////
598
599void CTerrain::MakeDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1, int dirtyFlags)
600{
601 // flag vertex data as dirty for affected patches, and rebuild bounds of these patches
602 ssize_t pi0 = clamp((i0/PATCH_SIZE)-1, (ssize_t)0, m_MapSizePatches);
603 ssize_t pi1 = clamp((i1/PATCH_SIZE)+1, (ssize_t)0, m_MapSizePatches);
604 ssize_t pj0 = clamp((j0/PATCH_SIZE)-1, (ssize_t)0, m_MapSizePatches);
605 ssize_t pj1 = clamp((j1/PATCH_SIZE)+1, (ssize_t)0, m_MapSizePatches);
606 for (ssize_t j = pj0; j < pj1; j++) {
607 for (ssize_t i = pi0; i < pi1; i++) {
608 CPatch* patch = GetPatch(i,j); // can't fail (i,j were clamped)
609 if (dirtyFlags & RENDERDATA_UPDATE_VERTICES)
610 patch->CalcBounds();
611 patch->SetDirty(dirtyFlags);
612 }
613 }
614}
615
616void CTerrain::MakeDirty(int dirtyFlags)
617{
618 for (ssize_t j = 0; j < m_MapSizePatches; j++) {
619 for (ssize_t i = 0; i < m_MapSizePatches; i++) {
620 CPatch* patch = GetPatch(i,j); // can't fail
621 if (dirtyFlags & RENDERDATA_UPDATE_VERTICES)
622 patch->CalcBounds();
623 patch->SetDirty(dirtyFlags);
624 }
625 }
626}
627
628CBound CTerrain::GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)
629{
630 i0 = clamp(i0, (ssize_t)0, m_MapSize-1);
631 j0 = clamp(j0, (ssize_t)0, m_MapSize-1);
632 i1 = clamp(i1, (ssize_t)0, m_MapSize-1);
633 j1 = clamp(j1, (ssize_t)0, m_MapSize-1);
634
635 u16 minH = 65535;
636 u16 maxH = 0;
637
638 for (ssize_t j = j0; j <= j1; ++j)
639 {
640 for (ssize_t i = i0; i <= i1; ++i)
641 {
642 minH = std::min(minH, m_Heightmap[j*m_MapSize + i]);
643 maxH = std::max(maxH, m_Heightmap[j*m_MapSize + i]);
644 }
645 }
646
647 CBound bound;
648 bound[0].X = (float)(i0*CELL_SIZE);
649 bound[0].Y = (float)(minH*HEIGHT_SCALE);
650 bound[0].Z = (float)(j0*CELL_SIZE);
651 bound[1].X = (float)(i1*CELL_SIZE);
652 bound[1].Y = (float)(maxH*HEIGHT_SCALE);
653 bound[1].Z = (float)(j1*CELL_SIZE);
654 return bound;
655}
Note: See TracBrowser for help on using the repository browser.