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