2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
7 Copyright (c) 2000-2009 Torus Knot Software Ltd
8 Also see acknowledgements in Readme.html
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License along with
20 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22 http://www.gnu.org/copyleft/lesser.txt.
24 You may alternatively use this source under the terms of a specific version of
25 the OGRE Unrestricted License provided you have obtained such a license from
26 Torus Knot Software Ltd.
27 -----------------------------------------------------------------------------
29 #include "OgreTerrain.h"
30 #include "OgreTerrainQuadTreeNode.h"
31 #include "OgreStreamSerialiser.h"
33 #include "OgreImage.h"
34 #include "OgrePixelFormat.h"
35 #include "OgreSceneManager.h"
36 #include "OgreSceneNode.h"
37 #include "OgreException.h"
38 #include "OgreBitwise.h"
39 #include "OgreStringConverter.h"
40 #include "OgreViewport.h"
44 //---------------------------------------------------------------------
45 const uint32
Terrain::TERRAIN_CHUNK_ID
= StreamSerialiser::makeIdentifier("TERR");
46 const uint16
Terrain::TERRAIN_CHUNK_VERSION
= 1;
47 // since 129^2 is the greatest power we can address in 16-bit index
48 const uint16
Terrain::TERRAIN_MAX_BATCH_SIZE
= 129;
49 //---------------------------------------------------------------------
50 //---------------------------------------------------------------------
51 bool TerrainGlobalOptions::msUseTriangleStrips
= true;
52 bool TerrainGlobalOptions::msUseLodMorph
= true;
53 Real
TerrainGlobalOptions::msSkirtSize
= 10;
54 bool TerrainGlobalOptions::msGenerateVertexNormals
= false;
55 bool TerrainGlobalOptions::msGenerateNormalMap
= true;
56 bool TerrainGlobalOptions::msGenerateShadowMap
= false;
57 Vector3
TerrainGlobalOptions::msShadowMapDir
= Vector3(1, -1, 0).normalisedCopy();
58 bool TerrainGlobalOptions::msGenerateHorizonMap
= false;
59 Radian
TerrainGlobalOptions::msHorizonMapAzimuth
= Radian(0);
60 Radian
TerrainGlobalOptions::msHorizonMapZenith
= Radian(0);
61 Real
TerrainGlobalOptions::msMaxPixelError
= 3.0;
62 //---------------------------------------------------------------------
63 Terrain::Terrain(SceneManager
* sm
)
70 , mNumLodLevelsPerLeafNode(0)
73 mRootNode
= sm
->getRootSceneNode()->createChildSceneNode();
74 sm
->addListener(this);
76 //---------------------------------------------------------------------
83 mSceneMgr
->destroySceneNode(mRootNode
);
84 mSceneMgr
->removeListener(this);
87 //---------------------------------------------------------------------
88 const AxisAlignedBox
& Terrain::getAABB() const
91 return AxisAlignedBox::BOX_NULL
;
93 return mQuadTree
->getAABB();
95 //---------------------------------------------------------------------
96 Real
Terrain::getBoundingRadius() const
101 return mQuadTree
->getBoundingRadius();
103 //---------------------------------------------------------------------
104 void Terrain::save(StreamSerialiser
& stream
)
106 stream
.writeChunkBegin(TERRAIN_CHUNK_ID
, TERRAIN_CHUNK_VERSION
);
108 uint8 align
= (uint8
)mAlign
;
109 stream
.write(&align
);
111 stream
.write(&mSize
);
112 stream
.write(&mWorldSize
);
113 uint16 splatDimCount
;
114 if (mSplatTextureWorldSize
.empty())
117 Real dim
= getSplatTextureWorldSize(0);
118 stream
.write(&splatDimCount
);
123 splatDimCount
= static_cast<uint16
>(mSplatTextureWorldSize
.size());
124 stream
.write(&splatDimCount
);
125 stream
.write(&mSplatTextureWorldSize
[0], mSplatTextureWorldSize
.size());
127 stream
.write(&mMaxBatchSize
);
128 stream
.write(&mMinBatchSize
);
130 stream
.write(mHeightData
, mSize
* mSize
);
132 stream
.writeChunkEnd(TERRAIN_CHUNK_ID
);
134 //---------------------------------------------------------------------
135 bool Terrain::prepare(StreamSerialiser
& stream
)
140 mUseTriangleStrips
= TerrainGlobalOptions::getUseTriangleStrips();
141 mUseLodMorph
= TerrainGlobalOptions::getUseLodMorph();
142 mSkirtSize
= TerrainGlobalOptions::getSkirtSize();
144 if (!stream
.readChunkBegin(TERRAIN_CHUNK_ID
, TERRAIN_CHUNK_VERSION
))
149 mAlign
= (Alignment
)align
;
150 stream
.read(&mWorldSize
);
151 uint16 splatDimCount
;
152 stream
.read(&splatDimCount
);
153 for (uint16 i
= 0; i
< splatDimCount
; ++i
)
157 setSplatTextureWorldSize(i
, dim
);
159 stream
.read(&mMaxBatchSize
);
160 stream
.read(&mMinBatchSize
);
163 determineLodLevels();
166 size_t numVertices
= mSize
* mSize
;
167 mHeightData
= OGRE_ALLOC_T(float, numVertices
, MEMCATEGORY_GEOMETRY
);
168 stream
.read(mHeightData
, numVertices
);
170 stream
.readChunkEnd(TERRAIN_CHUNK_ID
);
172 mQuadTree
= OGRE_NEW
TerrainQuadTreeNode(this, 0, 0, 0, mSize
, mNumLodLevels
- 1, 0, 0);
173 mQuadTree
->prepare();
175 mDeltaData
= OGRE_ALLOC_T(float, numVertices
, MEMCATEGORY_GEOMETRY
);
176 // calculate entire terrain
178 rect
.top
= 0; rect
.bottom
= mSize
;
179 rect
.left
= 0; rect
.right
= mSize
;
180 calculateHeightDeltas(rect
);
184 //---------------------------------------------------------------------
185 bool Terrain::prepare(const ImportData
& importData
)
190 if (!(Bitwise::isPO2(importData
.terrainSize
- 1) && Bitwise::isPO2(importData
.minBatchSize
- 1)
191 && Bitwise::isPO2(importData
.maxBatchSize
- 1)))
193 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS
,
194 "terrainSise, minBatchSize and maxBatchSize must all be n^2 + 1",
198 if (importData
.minBatchSize
> importData
.maxBatchSize
)
200 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS
,
201 "minBatchSize must be less than or equal to maxBatchSize",
205 if (importData
.maxBatchSize
> TERRAIN_MAX_BATCH_SIZE
)
207 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS
,
208 "maxBatchSize must be no larger than " +
209 StringConverter::toString(TERRAIN_MAX_BATCH_SIZE
),
214 mUseTriangleStrips
= TerrainGlobalOptions::getUseTriangleStrips();
215 mUseLodMorph
= TerrainGlobalOptions::getUseLodMorph();
216 mSkirtSize
= TerrainGlobalOptions::getSkirtSize();
217 mGenerateVertexNormals
= TerrainGlobalOptions::getGenerateVertexNormals();
218 mGenerateNormalMap
= TerrainGlobalOptions::getGenerateNormalMap();
219 mGenerateShadowMap
= TerrainGlobalOptions::getGenerateShadowMap();
220 mGenerateHorizonMap
= TerrainGlobalOptions::getGenerateHorizonMap();
222 mAlign
= importData
.terrainAlign
;
223 mSize
= importData
.terrainSize
;
224 mWorldSize
= importData
.worldSize
;
225 for (uint16 i
= 0; i
< importData
.splatTextureWorldSizeList
.size(); ++i
)
227 setSplatTextureWorldSize(i
, importData
.splatTextureWorldSizeList
[i
]);
229 mMaxBatchSize
= importData
.maxBatchSize
;
230 mMinBatchSize
= importData
.minBatchSize
;
231 mPos
= importData
.pos
;
233 determineLodLevels();
235 size_t numVertices
= mSize
* mSize
;
237 mHeightData
= OGRE_ALLOC_T(float, numVertices
, MEMCATEGORY_GEOMETRY
);
239 if (importData
.inputFloat
)
241 if (Math::RealEqual(importData
.inputBias
, 0.0) && Math::RealEqual(importData
.inputScale
, 1.0))
244 memcpy(mHeightData
, importData
.inputFloat
, sizeof(float) * numVertices
);
249 float* src
= importData
.inputFloat
;
250 float* dst
= mHeightData
;
251 for (size_t i
= 0; i
< numVertices
; ++i
)
252 *dst
++ = (*src
++ * importData
.inputScale
) + importData
.inputBias
;
255 else if (importData
.inputImage
)
257 Image
* img
= importData
.inputImage
;
259 if (img
->getWidth() != mSize
|| img
->getHeight() != mSize
)
260 img
->resize(mSize
, mSize
);
262 // convert image data to floats
263 PixelBox
destBox(mSize
, mSize
, 1, PF_FLOAT32_R
, mHeightData
);
264 PixelUtil::bulkPixelConversion(img
->getPixelBox(), destBox
);
266 if (!Math::RealEqual(importData
.inputBias
, 0.0) || !Math::RealEqual(importData
.inputScale
, 1.0))
268 float* pAdj
= mHeightData
;
269 for (size_t i
= 0; i
< numVertices
; ++i
)
271 *pAdj
= (*pAdj
* importData
.inputScale
) + importData
.inputBias
;
279 // start with flat terrain
280 memset(mHeightData
, 0, sizeof(float) * mSize
* mSize
);
283 mDeltaData
= OGRE_ALLOC_T(float, numVertices
, MEMCATEGORY_GEOMETRY
);
286 mQuadTree
= OGRE_NEW
TerrainQuadTreeNode(this, 0, 0, 0, mSize
, mNumLodLevels
- 1, 0, 0);
287 mQuadTree
->prepare();
289 // calculate entire terrain
291 rect
.top
= 0; rect
.bottom
= mSize
;
292 rect
.left
= 0; rect
.right
= mSize
;
293 calculateHeightDeltas(rect
);
299 //---------------------------------------------------------------------
300 void Terrain::determineLodLevels()
302 /* On a leaf-node basis, LOD can vary from maxBatch to minBatch in
303 number of vertices. After that, nodes will be gathered into parent
304 nodes with the same number of vertices, but they are combined with
305 3 of their siblings. In practice, the number of LOD levels overall
307 LODlevels = log2(size - 1) - log2(minBatch - 1) + 1
308 TreeDepth = log2((size - 1) / (maxBatch - 1)) + 1
310 .. it's just that at the max LOD, the terrain is divided into
311 (size - 1) / (maxBatch - 1) tiles each of maxBatch vertices, and
312 at the lowest LOD the terrain is made up of one single tile of
315 Example: size = 257, minBatch = 17, maxBatch = 33
317 LODlevels = log2(257 - 1) - log2(17 - 1) + 1 = 8 - 4 + 1 = 5
318 TreeDepth = log2((size - 1) / (maxBatch - 1)) + 1 = 4
320 LOD list - this assumes everything changes at once, which rarely happens of course
321 in fact except where groupings must occur, tiles can change independently
322 LOD 0: 257 vertices, 8 x 33 vertex tiles (tree depth 3)
323 LOD 1: 129 vertices, 8 x 17 vertex tiles (tree depth 3)
324 LOD 2: 65 vertices, 4 x 17 vertex tiles (tree depth 2)
325 LOD 3: 33 vertices, 2 x 17 vertex tiles (tree depth 1)
326 LOD 4: 17 vertices, 1 x 17 vertex tiles (tree depth 0)
328 Notice how we only have 2 sizes of index buffer to be concerned about,
329 17 vertices (per side) or 33. This makes buffer re-use much easier while
330 still giving the full range of LODs.
332 mNumLodLevelsPerLeafNode
= Math::Log2(mMaxBatchSize
- 1) - Math::Log2(mMinBatchSize
- 1) + 1;
333 mNumLodLevels
= Math::Log2(mSize
- 1) - Math::Log2(mMinBatchSize
- 1) + 1;
334 mTreeDepth
= Math::Log2(mMaxBatchSize
- 1) - Math::Log2(mMinBatchSize
- 1) + 2;
336 /* Now we need to figure out how to distribute vertex data. We want to
337 use 16-bit indexes for compatibility, which means that the maximum patch
338 size that we can address (even sparsely for lower LODs) is 129x129
339 (the next one up, 257x257 is too big).
341 So we need to split the vertex data into chunks of 129. The number of
342 primary tiles this creates also indicates the point above which in
343 the node tree that we can no longer merge tiles at lower LODs without
344 using different vertex data. For example, using the 257x257 input example
345 above, the vertex data would have to be split in 2 (in each dimension)
346 in order to fit within the 129x129 range. This data could be shared by
347 all tree depths from 1 onwards, it's just that LODs 3-1 would sample
348 the 129x129 data sparsely. LOD 0 would sample all of the vertices.
350 LOD 4 however, the lowest LOD, could not work with the same vertex data
351 because it needs to cover the entire terrain. There are 2 choices here:
352 create another set of vertex data at 17x17 which is only used by LOD 4,
353 or make LOD 4 occur at tree depth 1 instead (ie still split up, and
354 rendered as 2x9 along each edge instead.
356 Since rendering very small batches is not desirable, and the vertex counts
357 are inherently not going to be large, creating a separate vertex set is
358 preferable. This will also be more efficient on the vertex cache with
361 We probably need a larger example, because in this case only 1 level (LOD 0)
362 needs to use this separate vertex data. Higher detail terrains will need
363 it for multiple levels, here's a 2049x2049 example with 65 / 33 batch settings:
365 LODlevels = log2(2049 - 1) - log2(33 - 1) + 1 = 11 - 5 + 1 = 7
366 TreeDepth = log2((2049 - 1) / (65 - 1)) + 1 = 6
367 Number of vertex data splits at most detailed level:
368 (size - 1) / (TERRAIN_MAX_BATCH_SIZE - 1) = 2048 / 128 = 16
370 LOD 0: 2049 vertices, 32 x 65 vertex tiles (tree depth 5) vdata 0-15 [129x16]
371 LOD 1: 1025 vertices, 32 x 33 vertex tiles (tree depth 5) vdata 0-15 [129x16]
372 LOD 2: 513 vertices, 16 x 33 vertex tiles (tree depth 4) vdata 0-15 [129x16]
373 LOD 3: 257 vertices, 8 x 33 vertex tiles (tree depth 3) vdata 16-17 [129x2]
374 LOD 4: 129 vertices, 4 x 33 vertex tiles (tree depth 2) vdata 16-17 [129x2]
375 LOD 5: 65 vertices, 2 x 33 vertex tiles (tree depth 1) vdata 16-17 [129x2]
376 LOD 6: 33 vertices, 1 x 33 vertex tiles (tree depth 0) vdata 18 [33]
378 All the vertex counts are to be squared, they are just along one edge.
379 So as you can see, we need to have 3 levels of vertex data to satisy this
380 (admittedly quite extreme) case, and a total of 19 sets of vertex data.
381 The full detail geometry, which is 16(x16) sets of 129(x129), used by
382 LODs 0-2. LOD 3 can't use this set because it needs to group across them,
383 because it has only 8 tiles, so we make another set which satisfies this
384 at a maximum of 129 vertices per vertex data section. In this case LOD
385 3 needs 257(x257) total vertices so we still split into 2(x2) sets of 129.
386 This set is good up to and including LOD 5, but LOD 6 needs a single
387 contiguous set of vertices, so we make a 33x33 vertex set for it.
389 In terms of vertex data stored, this means that while our primary data is:
390 2049^2 = 4198401 vertices
391 our final stored vertex data is
392 (16 * 129)^2 + (2 * 129)^2 + 33^2 = 4327749 vertices
394 That equals a 3% premium, but it's both necessary and worth it for the
395 reduction in batch count resulting from the grouping. In addition, at
396 LODs 3 and 6 (or rather tree depth 3 and 0) there is the opportunity
397 to free up the vertex data used by more detailed LODs, which is
398 important when dealing with large terrains. For example, if we
399 freed the (GPU) vertex data for LOD 0-2 in the medium distance,
400 we would save 98% of the memory overhead for this terrain.
404 uint16 depth
= mTreeDepth
;
405 uint16 prevdepth
= depth
;
406 uint16 currresolution
= mSize
;
407 uint16 bakedresolution
= mSize
;
408 uint16 targetSplits
= (bakedresolution
- 1) / (TERRAIN_MAX_BATCH_SIZE
- 1);
409 while(depth
-- && targetSplits
)
411 uint splits
= 1 << depth
;
412 if (splits
== targetSplits
)
414 // vertex data goes at this level, at bakedresolution
415 // applies to all lower levels (except those with a closer vertex data)
416 mQuadTree
->assignVertexData(depth
, prevdepth
, bakedresolution
);
418 // next set to look for
419 bakedresolution
= ((currresolution
- 1) >> 1) + 1;
420 targetSplits
= (bakedresolution
- 1) / (TERRAIN_MAX_BATCH_SIZE
- 1);
425 currresolution
= ((currresolution
- 1) >> 1) + 1;
430 // Always assign vertex data to the top of the tree
431 mQuadTree
->assignVertexData(0, prevdepth
, bakedresolution
);
434 //---------------------------------------------------------------------
440 //---------------------------------------------------------------------
441 void Terrain::unload()
446 //---------------------------------------------------------------------
447 void Terrain::unprepare()
450 mQuadTree
->unprepare();
452 //---------------------------------------------------------------------
453 float* Terrain::getHeightData()
457 //---------------------------------------------------------------------
458 float* Terrain::getHeightData(long x
, long y
)
460 assert (x
>= 0 && x
< mSize
&& y
>= 0 && y
< mSize
);
461 return &mHeightData
[y
* mSize
+ x
];
463 //---------------------------------------------------------------------
464 float Terrain::getHeight(long x
, long y
)
466 return *getHeightData(x
, y
);
468 //---------------------------------------------------------------------
469 const float* Terrain::getDeltaData()
473 //---------------------------------------------------------------------
474 const float* Terrain::getDeltaData(long x
, long y
)
476 assert (x
>= 0 && x
< mSize
&& y
>= 0 && y
< mSize
);
477 return &mDeltaData
[y
* mSize
+ x
];
479 //---------------------------------------------------------------------
480 void Terrain::getPoint(long x
, long y
, Vector3
* outpos
)
482 getPointAlign(x
, y
, mAlign
, outpos
);
484 //---------------------------------------------------------------------
485 void Terrain::getPoint(long x
, long y
, float height
, Vector3
* outpos
)
487 getPointAlign(x
, y
, height
, mAlign
, outpos
);
489 //---------------------------------------------------------------------
490 void Terrain::getPointAlign(long x
, long y
, Alignment align
, Vector3
* outpos
)
492 getPointAlign(x
, y
, *getHeightData(x
, y
), align
, outpos
);
494 //---------------------------------------------------------------------
495 void Terrain::getPointAlign(long x
, long y
, float height
, Alignment align
, Vector3
* outpos
)
501 outpos
->x
= x
* mScale
+ mBase
;
502 outpos
->z
= y
* mScale
+ mBase
;
506 outpos
->y
= x
* mScale
+ mBase
;
507 outpos
->z
= y
* mScale
+ mBase
;
511 outpos
->x
= x
* mScale
+ mBase
;
512 outpos
->y
= y
* mScale
+ mBase
;
517 //---------------------------------------------------------------------
518 void Terrain::getVector(const Vector3
& inVec
, Vector3
* outVec
)
520 getVectorAlign(inVec
.x
, inVec
.y
, inVec
.z
, mAlign
, outVec
);
522 //---------------------------------------------------------------------
523 void Terrain::getVector(Real x
, Real y
, Real z
, Vector3
* outVec
)
525 getVectorAlign(x
, y
, z
, mAlign
, outVec
);
527 //---------------------------------------------------------------------
528 void Terrain::getVectorAlign(const Vector3
& inVec
, Alignment align
, Vector3
* outVec
)
530 getVectorAlign(inVec
.x
, inVec
.y
, inVec
.z
, align
, outVec
);
532 //---------------------------------------------------------------------
533 void Terrain::getVectorAlign(Real x
, Real y
, Real z
, Alignment align
, Vector3
* outVec
)
556 //---------------------------------------------------------------------
557 Terrain::Alignment
Terrain::getAlignment() const
561 //---------------------------------------------------------------------
562 uint16
Terrain::getSize() const
566 //---------------------------------------------------------------------
567 uint16
Terrain::getMaxBatchSize() const
569 return mMaxBatchSize
;
571 //---------------------------------------------------------------------
572 uint16
Terrain::getMinBatchSize() const
574 return mMinBatchSize
;
576 //---------------------------------------------------------------------
577 Real
Terrain::getWorldSize() const
581 //---------------------------------------------------------------------
582 Real
Terrain::getSplatTextureWorldSize(uint16 index
) const
584 if (index
< mSplatTextureWorldSize
.size())
586 return mSplatTextureWorldSize
[index
];
588 else if (!mSplatTextureWorldSize
.empty())
590 return mSplatTextureWorldSize
[0];
594 // default to tile 100 times
595 return mWorldSize
* 0.01;
598 //---------------------------------------------------------------------
599 void Terrain::setSplatTextureWorldSize(uint16 index
, Real size
)
601 if (index
>= mSplatTextureWorldSize
.size())
603 mSplatTextureWorldSize
.resize(index
+ 1);
604 mSplatTextureUVMultiplier
.resize(index
+ 1);
607 mSplatTextureWorldSize
[index
] = size
;
608 mSplatTextureUVMultiplier
[index
] = mWorldSize
/ size
;
610 //---------------------------------------------------------------------
611 Real
Terrain::getSplatTextureUVMultipler(uint16 index
) const
613 if (index
< mSplatTextureUVMultiplier
.size())
615 return mSplatTextureUVMultiplier
[index
];
617 else if (!mSplatTextureUVMultiplier
.empty())
619 return mSplatTextureUVMultiplier
[0];
623 // default to tile 100 times
627 //---------------------------------------------------------------------
628 void Terrain::setPosition(const Vector3
& pos
)
631 mRootNode
->setPosition(pos
);
634 //---------------------------------------------------------------------
635 void Terrain::updateBaseScale()
637 // centre the terrain on local origin
638 mBase
= -mWorldSize
* 0.5;
639 // scale determines what 1 unit on the grid becomes in world space
640 mScale
= mWorldSize
/ (Real
)mSize
;
642 //---------------------------------------------------------------------
643 void Terrain::dirty()
647 // calculate entire terrain
649 rect
.top
= 0; rect
.bottom
= mSize
;
650 rect
.left
= 0; rect
.right
= mSize
;
651 calculateHeightDeltas(rect
);
653 //---------------------------------------------------------------------
654 void Terrain::dirtyRect(const Rect
& rect
)
658 calculateHeightDeltas(rect
);
661 //---------------------------------------------------------------------
662 void Terrain::freeCPUResources()
664 OGRE_FREE(mHeightData
, MEMCATEGORY_GEOMETRY
);
667 OGRE_FREE(mDeltaData
, MEMCATEGORY_GEOMETRY
);
670 OGRE_DELETE mQuadTree
;
676 //---------------------------------------------------------------------
677 void Terrain::freeGPUResources()
679 // delete batched geometry
681 // SHARE geometry between Terrain instances!
684 //---------------------------------------------------------------------
685 void Terrain::calculateHeightDeltas(const Rect
& rect
)
687 Rect
clampedRect(rect
);
688 clampedRect
.left
= std::max(0L, clampedRect
.left
);
689 clampedRect
.top
= std::max(0L, clampedRect
.top
);
690 clampedRect
.right
= std::min((long)mSize
, clampedRect
.right
);
691 clampedRect
.bottom
= std::min((long)mSize
, clampedRect
.bottom
);
693 mQuadTree
->preDeltaCalculation(clampedRect
);
695 /// Iterate over target levels,
696 for (int targetLevel
= 1; targetLevel
< mNumLodLevels
; ++targetLevel
)
698 int sourceLevel
= targetLevel
- 1;
699 int step
= 1 << targetLevel
;
700 // The step of the next higher LOD
701 int higherstep
= step
>> 1;
703 // round the rectangle at this level so that it starts & ends on
704 // the step boundaries
705 Rect
lodRect(clampedRect
);
706 lodRect
.left
-= lodRect
.left
% step
;
707 lodRect
.top
-= lodRect
.top
% step
;
708 if (lodRect
.right
% step
)
709 lodRect
.right
+= step
- (lodRect
.right
% step
);
710 if (lodRect
.bottom
% step
)
711 lodRect
.bottom
+= step
- (lodRect
.bottom
% step
);
713 for (int j
= lodRect
.top
; j
< lodRect
.bottom
- step
; j
+= step
)
715 for (int i
= lodRect
.left
; i
< lodRect
.right
- step
; i
+= step
)
717 // Form planes relating to the lower detail tris to be produced
718 // For tri lists and even tri strip rows, they are this shape:
722 // For odd tri strip rows, they are this shape:
727 Vector3 v1
, v2
, v3
, v4
;
728 getPointAlign(i
, j
, ALIGN_X_Z
, &v1
);
729 getPointAlign(i
+ step
, j
, ALIGN_X_Z
, &v2
);
730 getPointAlign(i
, j
+ step
, ALIGN_X_Z
, &v3
);
731 getPointAlign(i
+ step
, j
+ step
, ALIGN_X_Z
, &v4
);
734 bool backwardTri
= false;
735 if (!mUseTriangleStrips
|| j
% 2 == 0)
737 t1
.redefine(v1
, v3
, v2
);
738 t2
.redefine(v2
, v3
, v4
);
742 t1
.redefine(v1
, v3
, v4
);
743 t2
.redefine(v1
, v4
, v2
);
747 // include the bottommost row of vertices if this is the last row
748 int zubound
= (j
== (mSize
- step
)? step
: step
- 1);
749 for ( int z
= 0; z
<= zubound
; z
++ )
751 // include the rightmost col of vertices if this is the last col
752 int xubound
= (i
== (mSize
- step
)? step
: step
- 1);
753 for ( int x
= 0; x
<= xubound
; x
++ )
755 int fulldetailx
= i
+ x
;
756 int fulldetailz
= j
+ z
;
757 if ( fulldetailx
% step
== 0 &&
758 fulldetailz
% step
== 0 )
760 // Skip, this one is a vertex at this level
764 Real zpct
= (Real
)z
/ (Real
)step
;
765 Real xpct
= (Real
)x
/ (Real
)step
;
767 //interpolated height
769 getPointAlign(fulldetailx
, fulldetailz
, ALIGN_X_Z
, &actualPos
);
771 // Determine which tri we're on
772 if ((xpct
+ zpct
<= 1.0f
&& !backwardTri
) ||
773 (xpct
+ (1-zpct
) <= 1.0f
&& backwardTri
))
777 (-(t1
.normal
.x
* actualPos
.x
)
778 - t1
.normal
.z
* actualPos
.z
779 - t1
.d
) / t1
.normal
.y
;
785 (-(t2
.normal
.x
* actualPos
.x
)
786 - t2
.normal
.z
* actualPos
.z
787 - t2
.d
) / t2
.normal
.y
;
790 Real actual_h
= actualPos
.y
;
791 Real delta
= interp_h
- actual_h
;
793 // max(delta) is the worst case scenario at this LOD
794 // compared to the original heightmap
796 // tell the quadtree about this
797 mQuadTree
->notifyDelta(fulldetailx
, fulldetailz
, sourceLevel
, delta
);
800 // If this vertex is being removed at this LOD,
801 // then save the height difference since that's the move
802 // it will need to make. Vertices to be removed at this LOD
803 // are halfway between the steps
804 if ((fulldetailx
% step
) == step
/ 2 || (fulldetailz
% step
) == step
/ 2)
806 // Save height difference
807 mDeltaData
[fulldetailx
+ (fulldetailz
* mSize
)] = delta
;
817 mQuadTree
->postDeltaCalculation(clampedRect
);
821 //---------------------------------------------------------------------
822 uint16
Terrain::getResolutionAtLod(uint16 lodLevel
)
824 return ((mSize
- 1) >> lodLevel
) + 1;
826 //---------------------------------------------------------------------
827 void Terrain::preFindVisibleObjects(SceneManager
* source
,
828 SceneManager::IlluminationRenderStage irs
, Viewport
* v
)
830 // only calculate LOD on main render passes
831 if (irs
== SceneManager::IRS_NONE
)
832 calculateCurrentLod(v
);
834 //---------------------------------------------------------------------
835 void Terrain::sceneManagerDestroyed(SceneManager
* source
)
839 if (source
== mSceneMgr
)
842 //---------------------------------------------------------------------
843 void Terrain::calculateCurrentLod(Viewport
* vp
)
847 // calculate error terms
848 const Camera
* cam
= vp
->getCamera()->getLodCamera();
850 // A = 1 / tan(fovy) (== 1 for fovy=45)
851 Real A
= 1.0 / Math::Tan(cam
->getFOVy());
852 // T = 2 * maxPixelError / vertRes
853 Real maxPixelError
= TerrainGlobalOptions::getMaxPixelError() * cam
->_getLodBiasInverse();
854 Real T
= 2.0 * maxPixelError
/ (Real
)vp
->getActualHeight();
857 Real cFactor
= A
/ T
;
860 mQuadTree
->calculateCurrentLod(cam
, cFactor
);
863 //---------------------------------------------------------------------
864 std::pair
<bool, Vector3
> Terrain::rayIntersects(const Ray
& ray
)
866 typedef std::pair
<bool, Vector3
> Result
;
867 // first step: convert the ray to a local vertex space
868 // we assume terrain to be in the x-z plane, with the [0,0] vertex
869 // at origin and a plane distance of 1 between vertices.
870 // This makes calculations easier.
871 Vector3 rayOrigin
= ray
.getOrigin() - getPosition();
872 Vector3 rayDirection
= ray
.getDirection();
873 // relabel axes (probably wrong? need correct coordinate transformation)
874 switch (getAlignment())
877 std::swap(rayOrigin
.y
, rayOrigin
.z
);
878 std::swap(rayDirection
.y
, rayDirection
.z
);
881 std::swap(rayOrigin
.x
, rayOrigin
.y
);
882 std::swap(rayDirection
.x
, rayDirection
.y
);
887 // readjust coordinate origin
888 rayOrigin
.x
+= mWorldSize
/2;
889 rayOrigin
.z
+= mWorldSize
/2;
890 // scale down to vertex level
891 rayOrigin
.x
/= mScale
;
892 rayOrigin
.z
/= mScale
;
893 rayDirection
.x
/= mScale
;
894 rayDirection
.z
/= mScale
;
895 rayDirection
.normalise();
896 Ray
localRay (rayOrigin
, rayDirection
);
898 // test if the ray actually hits the terrain's bounds
899 // TODO: Replace y values with actual heightmap height limits
900 AxisAlignedBox
aabb (Vector3(0, 0, 0), Vector3(mSize
, 1000000, mSize
));
901 std::pair
<bool, Real
> aabbTest
= localRay
.intersects(aabb
);
903 return Result(false, Vector3());
904 // get intersection point and move inside
905 Vector3 cur
= localRay
.getPoint(aabbTest
.second
);
907 // now check every quad the ray touches
908 int quadX
= std::min(std::max(static_cast<int>(cur
.x
), 0), (int)mSize
-2);
909 int quadZ
= std::min(std::max(static_cast<int>(cur
.z
), 0), (int)mSize
-2);
910 int flipX
= (rayDirection
.x
< 0 ? 0 : 1);
911 int flipZ
= (rayDirection
.z
< 0 ? 0 : 1);
912 int xDir
= (rayDirection
.x
< 0 ? -1 : 1);
913 int zDir
= (rayDirection
.z
< 0 ? -1 : 1);
915 while (cur
.y
>= -1 && cur
.y
<= 2)
917 if (quadX
< 0 || quadX
>= (int)mSize
-1 || quadZ
< 0 || quadZ
>= (int)mSize
-1)
920 result
= checkQuadIntersection(quadX
, quadZ
, localRay
);
924 // determine next quad to test
925 Real xDist
= (quadX
- cur
.x
+ flipX
) / rayDirection
.x
;
926 Real zDist
= (quadZ
- cur
.z
+ flipZ
) / rayDirection
.z
;
930 cur
+= rayDirection
* xDist
;
935 cur
+= rayDirection
* zDist
;
942 // transform the point of intersection back to world space
943 result
.second
.x
*= mScale
;
944 result
.second
.z
*= mScale
;
945 result
.second
.x
-= mWorldSize
/2;
946 result
.second
.z
-= mWorldSize
/2;
947 switch (getAlignment())
950 std::swap(result
.second
.y
, result
.second
.z
);
953 std::swap(result
.second
.x
, result
.second
.y
);
958 result
.second
+= getPosition();
962 //---------------------------------------------------------------------
963 std::pair
<bool, Vector3
> Terrain::checkQuadIntersection(int x
, int z
, const Ray
& ray
)
965 // build the two planes belonging to the quad's triangles
966 Vector3
v1 (x
, *getHeightData(x
,z
), z
);
967 Vector3
v2 (x
+1, *getHeightData(x
+1,z
), z
);
968 Vector3
v3 (x
, *getHeightData(x
,z
+1), z
+1);
969 Vector3
v4 (x
+1, *getHeightData(x
+1,z
+1), z
+1);
970 // TODO: Is this the correct triangle order?
971 Plane
p1 (v1
, v3
, v2
);
972 Plane
p2 (v3
, v4
, v2
);
974 // Test for intersection with the two planes.
975 // Then test that the intersection points are actually
976 // still inside the triangle (with a small error margin)
977 std::pair
<bool, Real
> planeInt
= ray
.intersects(p1
);
980 Vector3 where
= ray
.getPoint(planeInt
.second
);
981 Vector3 rel
= where
- v1
;
982 if (rel
.x
>= -0.01 && rel
.x
<= 1.01 && rel
.z
>= -0.01 && rel
.z
<= 1.01 && rel
.x
+rel
.z
<= 1.01)
983 return std::pair
<bool, Vector3
>(true, where
);
985 planeInt
= ray
.intersects(p2
);
988 Vector3 where
= ray
.getPoint(planeInt
.second
);
989 Vector3 rel
= where
- v1
;
990 if (rel
.x
>= -0.01 && rel
.x
<= 1.01 && rel
.z
>= -0.01 && rel
.z
<= 1.01 && rel
.x
+rel
.z
>= 0.99)
991 return std::pair
<bool, Vector3
>(true, where
);
994 return std::pair
<bool, Vector3
>(false, Vector3());