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