1 /*
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/
6 
7 Copyright (c) 2000-2014 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreRoot.h"
29 #include "OgreTerrainQuadTreeNode.h"
30 #include "OgreTerrainLodManager.h"
31 #include "OgreStreamSerialiser.h"
32 #include "OgreLogManager.h"
33 #include "OgreTerrain.h"
34 
35 namespace Ogre
36 {
37     const uint16 TerrainLodManager::WORKQUEUE_LOAD_LOD_DATA_REQUEST = 1;
38     const uint32 TerrainLodManager::TERRAINLODDATA_CHUNK_ID = StreamSerialiser::makeIdentifier("TLDA");
39     const uint16 TerrainLodManager::TERRAINLODDATA_CHUNK_VERSION = 1;
40 
TerrainLodManager(Terrain * t,DataStreamPtr & stream)41     TerrainLodManager::TerrainLodManager(Terrain* t, DataStreamPtr& stream)
42         : mTerrain(t)
43     {
44         init();
45         mDataStream = stream;
46         mStreamOffset = !mDataStream ? 0 : mDataStream->tell();
47     }
48 
TerrainLodManager(Terrain * t,const String & filename)49     TerrainLodManager::TerrainLodManager(Terrain* t, const String& filename)
50         : mTerrain(t), mStreamOffset(0)
51     {
52         init();
53         open(filename);
54     }
55 
open(const String & filename)56     void TerrainLodManager::open(const String& filename)
57     {
58         if(!filename.empty() && filename.length() > 0)
59             mDataStream = Root::getSingleton().openFileStream(filename, mTerrain->_getDerivedResourceGroup());
60     }
61 
close()62     void TerrainLodManager::close()
63     {
64         mDataStream.reset();
65     }
66 
isOpen() const67     bool TerrainLodManager::isOpen() const
68     {
69         return mDataStream.get() != 0;
70     }
71 
init()72     void TerrainLodManager::init()
73     {
74         mHighestLodPrepared = -1;
75         mHighestLodLoaded = -1;
76         mTargetLodLevel = -1;
77         mIncreaseLodLevelInProgress = false;
78         mLastRequestSynchronous = false;
79         mLodInfoTable = 0;
80 
81         WorkQueue* wq = Root::getSingleton().getWorkQueue();
82         mWorkQueueChannel = wq->getChannel("Ogre/TerrainLodManager");
83         wq->addRequestHandler(mWorkQueueChannel, this);
84         wq->addResponseHandler(mWorkQueueChannel, this);
85     }
86 
~TerrainLodManager()87     TerrainLodManager::~TerrainLodManager()
88     {
89         waitForDerivedProcesses();
90         WorkQueue* wq = Root::getSingleton().getWorkQueue();
91         wq->removeRequestHandler(mWorkQueueChannel, this);
92         wq->removeResponseHandler(mWorkQueueChannel, this);
93 
94         if(mLodInfoTable)
95             OGRE_FREE(mLodInfoTable,MEMCATEGORY_GENERAL);
96     }
97 
canHandleRequest(const WorkQueue::Request * req,const WorkQueue * srcQ)98     bool TerrainLodManager::canHandleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ)
99     {
100         LoadLodRequest lreq = any_cast<LoadLodRequest>(req->getData());
101         if (lreq.requestee != this)
102             return false;
103         return RequestHandler::canHandleRequest(req, srcQ);
104     }
105     //---------------------------------------------------------------------
canHandleResponse(const WorkQueue::Response * res,const WorkQueue * srcQ)106     bool TerrainLodManager::canHandleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
107     {
108         LoadLodRequest lreq = any_cast<LoadLodRequest>(res->getRequest()->getData());
109         return (lreq.requestee == this);
110     }
111 
handleRequest(const WorkQueue::Request * req,const WorkQueue * srcQ)112     WorkQueue::Response* TerrainLodManager::handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ)
113     {
114         LoadLodRequest lreq = any_cast<LoadLodRequest>(req->getData());
115         // read data from file into temporary height & delta buffer
116         try {
117             if(lreq.currentPreparedLod>lreq.requestedLod)
118                 readLodData(lreq.currentPreparedLod-1, lreq.requestedLod);
119         } catch (Exception& e) {
120             return OGRE_NEW WorkQueue::Response(req, false, Any(), e.getFullDescription());
121         }
122 
123         int lastTreeStart = -1;
124         for( int level=lreq.currentLoadedLod-1; level>=lreq.requestedLod; --level )
125         {
126             LodInfo& lodinfo = getLodInfo(level);
127             // skip re-assign
128             if(lastTreeStart != (int)lodinfo.treeStart)
129             {
130                 mTerrain->getQuadTree()->assignVertexData(lodinfo.treeStart, lodinfo.treeEnd,
131                         lodinfo.resolution, lodinfo.size);
132                 lastTreeStart = lodinfo.treeStart;
133             }
134         }
135         return OGRE_NEW WorkQueue::Response(req, true, Any());
136     }
137 
handleResponse(const WorkQueue::Response * res,const WorkQueue * srcQ)138     void TerrainLodManager::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
139     {
140         const WorkQueue::Request* req = res->getRequest();
141         // No response data, just request
142         LoadLodRequest lreq = any_cast<LoadLodRequest>(req->getData());
143 
144         mIncreaseLodLevelInProgress = false;
145 
146         if (res->succeeded())
147         {
148             // no others update LOD status
149             if(lreq.currentPreparedLod == mHighestLodPrepared && lreq.currentLoadedLod == mHighestLodLoaded )
150             {
151                 if( lreq.requestedLod < mHighestLodPrepared )
152                     mHighestLodPrepared = lreq.requestedLod;
153 
154                 int lastTreeStart = -1;
155                 for( int level = mHighestLodLoaded-1; level>=lreq.requestedLod && level>=mTargetLodLevel; --level )
156                 {
157                     LodInfo& lodinfo = getLodInfo(level);
158                     // skip re-load
159                     if(lastTreeStart != (int)lodinfo.treeStart)
160                     {
161                         mTerrain->getQuadTree()->load(lodinfo.treeStart, lodinfo.treeEnd);
162                         lastTreeStart = lodinfo.treeStart;
163                     }
164                     --mHighestLodLoaded;
165                 }
166             }
167 
168             // has streamed in new data, should update terrain
169             if(lreq.currentPreparedLod>lreq.requestedLod)
170             {
171                 mTerrain->dirty();
172                 mTerrain->updateGeometryWithoutNotifyNeighbours();
173             }
174 
175             // there are new requests
176             if(mHighestLodLoaded != mTargetLodLevel)
177                 updateToLodLevel(mTargetLodLevel,mLastRequestSynchronous);
178         }
179         else
180         {
181             LogManager::getSingleton().stream(LML_CRITICAL) << "Failed to prepare and load terrain LOD: " << res->getMessages();
182         }
183     }
buildLodInfoTable()184     void TerrainLodManager::buildLodInfoTable()
185     {
186         uint16 numLodLevels = mTerrain->getNumLodLevels();
187         mLodInfoTable = OGRE_ALLOC_T(LodInfo, numLodLevels, MEMCATEGORY_GENERAL);
188 
189         uint16 size = mTerrain->getSize();
190         uint16 depth = mTerrain->mTreeDepth;
191         uint16 prevdepth = depth;
192         uint16 last = 0;
193         uint16 currresolution = size;
194         uint16 bakedresolution = size;
195         uint16 targetSplits = (bakedresolution - 1) / (Terrain::TERRAIN_MAX_BATCH_SIZE - 1);
196 
197         int *lodDepth = OGRE_ALLOC_T(int, numLodLevels, MEMCATEGORY_GENERAL);
198         for(int level=0; level<numLodLevels; level++)
199             lodDepth[level] = (level < mTerrain->getNumLodLevelsPerLeaf()) ? depth-1 : numLodLevels-level-1;
200 
201         while(depth-- && targetSplits)
202         {
203             uint splits = 1 << depth;
204             if (splits == targetSplits)
205             {
206                 for(uint level=0; level<numLodLevels; level++)
207                 {
208                     if (lodDepth[level] >= depth && lodDepth[level] < prevdepth)
209                     {
210                         mLodInfoTable[level].treeStart = depth;
211                         mLodInfoTable[level].treeEnd = prevdepth;
212                         mLodInfoTable[level].isLast = level == last+prevdepth-depth-static_cast<uint>(1);
213                         mLodInfoTable[level].resolution = bakedresolution;
214                         mLodInfoTable[level].size = ((bakedresolution-1) / splits) + 1;
215                         // this lod info has been filled
216                         lodDepth[level] = -1;
217                     }
218                 }
219                 // next set to look for
220                 bakedresolution =  ((currresolution - 1) >> 1) + 1;
221                 targetSplits = (bakedresolution - 1) / (Terrain::TERRAIN_MAX_BATCH_SIZE - 1);
222                 prevdepth = depth;
223             }
224             currresolution = ((currresolution - 1) >> 1) + 1;
225             last++;
226         }
227         for(int level=0; level<numLodLevels; level++)
228         {
229             if (lodDepth[level]>=0 && lodDepth[level]<=prevdepth)
230             {
231                 mLodInfoTable[level].treeStart = 0;
232                 mLodInfoTable[level].treeEnd = 1;
233                 mLodInfoTable[level].isLast = level == last+prevdepth-depth-1;
234                 mLodInfoTable[level].resolution = bakedresolution;
235                 mLodInfoTable[level].size = bakedresolution;
236             }
237         }
238         OGRE_FREE(lodDepth,MEMCATEGORY_GENERAL);
239     }
240 
241     // data are reorganized from lowest lod level(mNumLodLevels-1) to highest(0)
separateData(float * data,uint16 size,uint16 numLodLevels,LodsData & lods)242     void TerrainLodManager::separateData(float* data, uint16 size, uint16 numLodLevels, LodsData& lods)
243     {
244         lods.resize(numLodLevels);
245         for (int level = numLodLevels - 1; level >= 0; level--)
246         {
247             unsigned int inc = 1 << level;
248             unsigned int prev = 1 << (level + 1);
249 
250             for (uint16 y = 0; y < size; y += inc)
251             {
252                 for (uint16 x = 0; x < size-1; x += inc)
253                     if ((level == numLodLevels - 1) || ((x % prev != 0) || (y % prev != 0)))
254                         lods[level].push_back( data[y*size + x] );
255                 if ((level == numLodLevels -1) || (y % prev) != 0)
256                     lods[level].push_back( data[y*size + size-1] );
257                 if (y+inc > size)
258                     break;
259             }
260         }
261     }
262     //---------------------------------------------------------------------
updateToLodLevel(int lodLevel,bool synchronous)263     void TerrainLodManager::updateToLodLevel(int lodLevel, bool synchronous /* = false */)
264     {
265         //init
266         if(mHighestLodPrepared==-1)
267             mHighestLodPrepared = mTerrain->getNumLodLevels();
268         if(mHighestLodLoaded==-1)
269             mHighestLodLoaded = mTerrain->getNumLodLevels();
270 
271         lodLevel = mTerrain->getPositiveLodLevel(lodLevel);
272 
273         mTargetLodLevel = lodLevel;
274         mLastRequestSynchronous = synchronous;
275 
276         // need loading
277         if(mTargetLodLevel<mHighestLodLoaded)
278         {
279             // no task is running
280             if (!mIncreaseLodLevelInProgress)
281             {
282                 mIncreaseLodLevelInProgress = true;
283                 LoadLodRequest req(this,mHighestLodPrepared,mHighestLodLoaded,mTargetLodLevel);
284                 Root::getSingleton().getWorkQueue()->addRequest(
285                     mWorkQueueChannel, WORKQUEUE_LOAD_LOD_DATA_REQUEST,
286                     Any(req), 0, synchronous);
287             }
288             else if(synchronous)
289                 waitForDerivedProcesses();
290         }
291         // need unloading
292         else if(mTargetLodLevel>mHighestLodLoaded)
293         {
294             for( int level=mHighestLodLoaded; level<mTargetLodLevel; level++ )
295             {
296                 LodInfo& lod = getLodInfo(level);
297                 if(lod.isLast)
298                 {
299                     mTerrain->getQuadTree()->unload(lod.treeStart, lod.treeEnd);
300                     mHighestLodLoaded = level+1;
301                 }
302             }
303         }
304     }
305 
306     // save each LOD level separately compressed so seek is possible
saveLodData(StreamSerialiser & stream,Terrain * terrain)307     void TerrainLodManager::saveLodData(StreamSerialiser& stream, Terrain* terrain)
308     {
309         uint16 numLodLevels = terrain->getNumLodLevels();
310 
311         LodsData lods;
312         separateData(terrain->mHeightData, terrain->getSize(), numLodLevels, lods);
313         separateData(terrain->mDeltaData, terrain->getSize(), numLodLevels, lods);
314 
315         for (int level = numLodLevels - 1; level >=0; level--)
316         {
317             stream.writeChunkBegin(TERRAINLODDATA_CHUNK_ID, TERRAINLODDATA_CHUNK_VERSION);
318             stream.startDeflate();
319             stream.write(&(lods[level][0]), lods[level].size());
320             stream.stopDeflate();
321             stream.writeChunkEnd(TERRAINLODDATA_CHUNK_ID);
322         }
323     }
324 
readLodData(uint16 lowerLodBound,uint16 higherLodBound)325     void TerrainLodManager::readLodData(uint16 lowerLodBound, uint16 higherLodBound)
326     {
327         if(!mDataStream) // No file to read from
328             return;
329 
330         uint16 numLodLevels = mTerrain->getNumLodLevels();
331         mDataStream->seek(mStreamOffset);
332         StreamSerialiser stream(mDataStream);
333 
334         const StreamSerialiser::Chunk *mainChunk = stream.readChunkBegin(Terrain::TERRAIN_CHUNK_ID, Terrain::TERRAIN_CHUNK_VERSION);
335 
336         if(mainChunk->version > 1)
337         {
338             // skip the general information
339             stream.readChunkBegin(Terrain::TERRAINGENERALINFO_CHUNK_ID, Terrain::TERRAINGENERALINFO_CHUNK_VERSION);
340             stream.readChunkEnd(Terrain::TERRAINGENERALINFO_CHUNK_ID);
341 
342             // skip the previous lod data
343             for(int skip=numLodLevels-1-lowerLodBound; skip>0; skip--)
344             {
345                 stream.readChunkBegin(TERRAINLODDATA_CHUNK_ID, TERRAINLODDATA_CHUNK_VERSION);
346                 stream.readChunkEnd(TERRAINLODDATA_CHUNK_ID);
347             }
348 
349             // uncompress
350             uint maxSize = 2 * mTerrain->getGeoDataSizeAtLod(higherLodBound);
351             float *lodData = OGRE_ALLOC_T(float, maxSize, MEMCATEGORY_GENERAL);
352 
353             for(int level=lowerLodBound; level>=higherLodBound; level-- )
354             {
355                 // both height data and delta data
356                 uint dataSize = 2 * mTerrain->getGeoDataSizeAtLod(level);
357 
358                 // reach and read the target lod data
359                 const StreamSerialiser::Chunk *c = stream.readChunkBegin(TERRAINLODDATA_CHUNK_ID,
360                         TERRAINLODDATA_CHUNK_VERSION);
361                 stream.startDeflate(c->length);
362                 stream.read(lodData, dataSize);
363                 stream.stopDeflate();
364                 stream.readChunkEnd(TERRAINLODDATA_CHUNK_ID);
365 
366                 fillBufferAtLod(level, lodData, dataSize);
367             }
368             stream.readChunkEnd(Terrain::TERRAIN_CHUNK_ID);
369 
370             OGRE_FREE(lodData, MEMCATEGORY_GENERAL);
371         }
372     }
fillBufferAtLod(uint lodLevel,const float * data,uint dataSize)373     void TerrainLodManager::fillBufferAtLod(uint lodLevel, const float* data, uint dataSize )
374     {
375         unsigned int inc = 1 << lodLevel;
376         unsigned int prev = 1 << (lodLevel + 1);
377         uint16 numLodLevels = mTerrain->getNumLodLevels();
378         uint16 size = mTerrain->getSize();
379 
380         const float* heightDataPtr = data;
381         const float* deltaDataPtr = data+dataSize/2;
382 
383         for (uint16 y = 0; y < size; y += inc)
384         {
385             for (uint16 x = 0; x < size-1; x += inc)
386                 if ((lodLevel == numLodLevels - static_cast<uint>(1)) || (x % prev) || (y % prev))
387                 {
388                     mTerrain->mHeightData[y*size + x] = *(heightDataPtr++);
389                     mTerrain->mDeltaData[y*size + x] = *(deltaDataPtr++);
390                 }
391             if ((lodLevel == numLodLevels - static_cast<uint>(1)) || (y % prev))
392             {
393                 mTerrain->mHeightData[y*size + size-1] = *(heightDataPtr++);
394                 mTerrain->mDeltaData[y*size + size-1] = *(deltaDataPtr++);
395             }
396             if (y+inc > size)
397                 break;
398         }
399     }
waitForDerivedProcesses()400     void TerrainLodManager::waitForDerivedProcesses()
401     {
402         while (mIncreaseLodLevelInProgress)
403         {
404             // we need to wait for this to finish
405             OGRE_THREAD_SLEEP(50);
406             Root::getSingleton().getWorkQueue()->processResponses();
407         }
408     }
409 }
410