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-2013 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 "OgrePage.h" 29 #include "OgreRoot.h" 30 #include "OgrePagedWorldSection.h" 31 #include "OgrePagedWorld.h" 32 #include "OgrePageStrategy.h" 33 #include "OgrePageManager.h" 34 #include "OgreSceneNode.h" 35 #include "OgreSceneManager.h" 36 #include "OgreStreamSerialiser.h" 37 #include "OgrePageContentCollectionFactory.h" 38 #include "OgrePageContentCollection.h" 39 40 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS 41 #include "macUtils.h" 42 #endif 43 44 namespace Ogre 45 { 46 //--------------------------------------------------------------------- 47 const uint32 Page::CHUNK_ID = StreamSerialiser::makeIdentifier("PAGE"); 48 const uint16 Page::CHUNK_VERSION = 1; 49 const uint32 Page::CHUNK_CONTENTCOLLECTION_DECLARATION_ID = StreamSerialiser::makeIdentifier("PCNT"); 50 const uint16 Page::WORKQUEUE_PREPARE_REQUEST = 1; 51 const uint16 Page::WORKQUEUE_CHANGECOLLECTION_REQUEST = 3; 52 53 //--------------------------------------------------------------------- Page(PageID pageID,PagedWorldSection * parent)54 Page::Page(PageID pageID, PagedWorldSection* parent) 55 : mID(pageID) 56 , mParent(parent) 57 , mDeferredProcessInProgress(false) 58 , mModified(false) 59 , mDebugNode(0) 60 { 61 WorkQueue* wq = Root::getSingleton().getWorkQueue(); 62 mWorkQueueChannel = wq->getChannel("Ogre/Page"); 63 wq->addRequestHandler(mWorkQueueChannel, this); 64 wq->addResponseHandler(mWorkQueueChannel, this); 65 touch(); 66 } 67 //--------------------------------------------------------------------- ~Page()68 Page::~Page() 69 { 70 WorkQueue* wq = Root::getSingleton().getWorkQueue(); 71 wq->removeRequestHandler(mWorkQueueChannel, this); 72 wq->removeResponseHandler(mWorkQueueChannel, this); 73 74 destroyAllContentCollections(); 75 if (mDebugNode) 76 { 77 // destroy while we have the chance 78 SceneNode::ObjectIterator it = mDebugNode->getAttachedObjectIterator(); 79 while(it.hasMoreElements()) 80 mParent->getSceneManager()->destroyMovableObject(it.getNext()); 81 mDebugNode->removeAndDestroyAllChildren(); 82 mParent->getSceneManager()->destroySceneNode(mDebugNode); 83 84 mDebugNode = 0; 85 } 86 } 87 //--------------------------------------------------------------------- destroyAllContentCollections()88 void Page::destroyAllContentCollections() 89 { 90 for (ContentCollectionList::iterator i = mContentCollections.begin(); 91 i != mContentCollections.end(); ++i) 92 { 93 delete *i; 94 } 95 mContentCollections.clear(); 96 } 97 //--------------------------------------------------------------------- getManager() const98 PageManager* Page::getManager() const 99 { 100 return mParent->getManager(); 101 } 102 //--------------------------------------------------------------------- touch()103 void Page::touch() 104 { 105 mFrameLastHeld = Root::getSingleton().getNextFrameNumber(); 106 } 107 //--------------------------------------------------------------------- isHeld() const108 bool Page::isHeld() const 109 { 110 unsigned long nextFrame = Root::getSingleton().getNextFrameNumber(); 111 unsigned long dist; 112 if (nextFrame < mFrameLastHeld) 113 { 114 // we must have wrapped around 115 dist = mFrameLastHeld + (std::numeric_limits<unsigned long>::max() - mFrameLastHeld); 116 } 117 else 118 dist = nextFrame - mFrameLastHeld; 119 120 // 5-frame tolerance 121 return dist <= 5; 122 } 123 //--------------------------------------------------------------------- prepareImpl(StreamSerialiser & stream,PageData * dataToPopulate)124 bool Page::prepareImpl(StreamSerialiser& stream, PageData* dataToPopulate) 125 { 126 127 // Now do the real loading 128 if (!stream.readChunkBegin(CHUNK_ID, CHUNK_VERSION, "Page")) 129 return false; 130 131 // pageID check (we should know the ID we're expecting) 132 uint32 storedID; 133 stream.read(&storedID); 134 if (mID != storedID) 135 { 136 LogManager::getSingleton().stream() << "Error: Tried to populate Page ID " << mID 137 << " with data corresponding to page ID " << storedID; 138 stream.undoReadChunk(CHUNK_ID); 139 return false; 140 } 141 142 PageManager* mgr = getManager(); 143 144 while(stream.peekNextChunkID() == CHUNK_CONTENTCOLLECTION_DECLARATION_ID) 145 { 146 const StreamSerialiser::Chunk* collChunk = stream.readChunkBegin(); 147 String factoryName; 148 stream.read(&factoryName); 149 stream.readChunkEnd(CHUNK_CONTENTCOLLECTION_DECLARATION_ID); 150 // Supported type? 151 PageContentCollectionFactory* collFact = mgr->getContentCollectionFactory(factoryName); 152 if (collFact) 153 { 154 PageContentCollection* collInst = collFact->createInstance(); 155 if (collInst->prepare(stream)) // read type-specific data 156 { 157 dataToPopulate->collectionsToAdd.push_back(collInst); 158 } 159 else 160 { 161 LogManager::getSingleton().stream() << "Error preparing PageContentCollection type: " 162 << factoryName << " in " << *this; 163 collFact->destroyInstance(collInst); 164 } 165 } 166 else 167 { 168 LogManager::getSingleton().stream() << "Unsupported PageContentCollection type: " 169 << factoryName << " in " << *this; 170 // skip 171 stream.readChunkEnd(collChunk->id); 172 } 173 174 } 175 176 177 mModified = false; 178 179 return true; 180 } 181 //--------------------------------------------------------------------- load(bool synchronous)182 void Page::load(bool synchronous) 183 { 184 if (!mDeferredProcessInProgress) 185 { 186 destroyAllContentCollections(); 187 PageRequest req(this); 188 mDeferredProcessInProgress = true; 189 Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, WORKQUEUE_PREPARE_REQUEST, 190 Any(req), 0, synchronous); 191 } 192 193 } 194 //--------------------------------------------------------------------- unload()195 void Page::unload() 196 { 197 destroyAllContentCollections(); 198 } 199 //--------------------------------------------------------------------- canHandleRequest(const WorkQueue::Request * req,const WorkQueue * srcQ)200 bool Page::canHandleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ) 201 { 202 PageRequest preq = any_cast<PageRequest>(req->getData()); 203 // only deal with own requests 204 // we do this because if we delete a page we want any pending tasks to be discarded 205 if (preq.srcPage != this) 206 return false; 207 else 208 return RequestHandler::canHandleRequest(req, srcQ); 209 210 } 211 //--------------------------------------------------------------------- canHandleResponse(const WorkQueue::Response * res,const WorkQueue * srcQ)212 bool Page::canHandleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ) 213 { 214 PageRequest preq = any_cast<PageRequest>(res->getRequest()->getData()); 215 // only deal with own requests 216 // we do this because if we delete a page we want any pending tasks to be discarded 217 if (preq.srcPage != this) 218 return false; 219 else 220 return true; 221 222 } 223 //--------------------------------------------------------------------- handleRequest(const WorkQueue::Request * req,const WorkQueue * srcQ)224 WorkQueue::Response* Page::handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ) 225 { 226 // Background thread (maybe) 227 228 PageRequest preq = any_cast<PageRequest>(req->getData()); 229 // only deal with own requests; we shouldn't ever get here though 230 if (preq.srcPage != this) 231 return 0; 232 233 PageResponse res; 234 res.pageData = OGRE_NEW PageData(); 235 WorkQueue::Response* response = 0; 236 try 237 { 238 prepareImpl(res.pageData); 239 response = OGRE_NEW WorkQueue::Response(req, true, Any(res)); 240 } 241 catch (Exception& e) 242 { 243 // oops 244 response = OGRE_NEW WorkQueue::Response(req, false, Any(res), 245 e.getFullDescription()); 246 } 247 248 return response; 249 } 250 //--------------------------------------------------------------------- handleResponse(const WorkQueue::Response * res,const WorkQueue * srcQ)251 void Page::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ) 252 { 253 // Main thread 254 PageResponse pres = any_cast<PageResponse>(res->getData()); 255 PageRequest preq = any_cast<PageRequest>(res->getRequest()->getData()); 256 257 // only deal with own requests 258 if (preq.srcPage!= this) 259 return; 260 261 // final loading behaviour 262 if (res->succeeded()) 263 { 264 std::swap(mContentCollections, pres.pageData->collectionsToAdd); 265 loadImpl(); 266 } 267 268 OGRE_DELETE pres.pageData; 269 270 mDeferredProcessInProgress = false; 271 272 } 273 //--------------------------------------------------------------------- prepareImpl(PageData * dataToPopulate)274 bool Page::prepareImpl(PageData* dataToPopulate) 275 { 276 // Procedural preparation 277 if (mParent->_prepareProceduralPage(this)) 278 return true; 279 else 280 { 281 // Background loading 282 String filename = generateFilename(); 283 284 DataStreamPtr stream = Root::getSingleton().openFileStream(filename, 285 getManager()->getPageResourceGroup()); 286 StreamSerialiser ser(stream); 287 return prepareImpl(ser, dataToPopulate); 288 } 289 290 291 } 292 //--------------------------------------------------------------------- loadImpl()293 void Page::loadImpl() 294 { 295 mParent->_loadProceduralPage(this); 296 297 for (ContentCollectionList::iterator i = mContentCollections.begin(); 298 i != mContentCollections.end(); ++i) 299 { 300 (*i)->load(); 301 } 302 } 303 //--------------------------------------------------------------------- save()304 void Page::save() 305 { 306 String filename = generateFilename(); 307 save(filename); 308 } 309 //--------------------------------------------------------------------- save(const String & filename)310 void Page::save(const String& filename) 311 { 312 DataStreamPtr stream = Root::getSingleton().createFileStream(filename, 313 getManager()->getPageResourceGroup(), true); 314 StreamSerialiser ser(stream); 315 save(ser); 316 } 317 //--------------------------------------------------------------------- save(StreamSerialiser & stream)318 void Page::save(StreamSerialiser& stream) 319 { 320 stream.writeChunkBegin(CHUNK_ID, CHUNK_VERSION); 321 322 // page id 323 stream.write(&mID); 324 325 // content collections 326 for (ContentCollectionList::iterator i = mContentCollections.begin(); 327 i != mContentCollections.end(); ++i) 328 { 329 // declaration 330 stream.writeChunkBegin(CHUNK_CONTENTCOLLECTION_DECLARATION_ID); 331 stream.write(&(*i)->getType()); 332 stream.writeChunkEnd(CHUNK_CONTENTCOLLECTION_DECLARATION_ID); 333 // data 334 (*i)->save(stream); 335 } 336 337 stream.writeChunkEnd(CHUNK_ID); 338 339 mModified = false; 340 } 341 //--------------------------------------------------------------------- frameStart(Real timeSinceLastFrame)342 void Page::frameStart(Real timeSinceLastFrame) 343 { 344 updateDebugDisplay(); 345 346 // content collections 347 for (ContentCollectionList::iterator i = mContentCollections.begin(); 348 i != mContentCollections.end(); ++i) 349 { 350 (*i)->frameStart(timeSinceLastFrame); 351 } 352 353 354 } 355 //--------------------------------------------------------------------- frameEnd(Real timeElapsed)356 void Page::frameEnd(Real timeElapsed) 357 { 358 // content collections 359 for (ContentCollectionList::iterator i = mContentCollections.begin(); 360 i != mContentCollections.end(); ++i) 361 { 362 (*i)->frameEnd(timeElapsed); 363 } 364 365 } 366 //--------------------------------------------------------------------- notifyCamera(Camera * cam)367 void Page::notifyCamera(Camera* cam) 368 { 369 // content collections 370 for (ContentCollectionList::iterator i = mContentCollections.begin(); 371 i != mContentCollections.end(); ++i) 372 { 373 (*i)->notifyCamera(cam); 374 } 375 376 } 377 //--------------------------------------------------------------------- updateDebugDisplay()378 void Page::updateDebugDisplay() 379 { 380 uint8 dbglvl = getManager()->getDebugDisplayLevel(); 381 if (dbglvl > 0) 382 { 383 // update debug display 384 if (!mDebugNode) 385 { 386 mDebugNode = mParent->getSceneManager()->getRootSceneNode()->createChildSceneNode(); 387 } 388 mParent->getStrategy()->updateDebugDisplay(this, mDebugNode); 389 390 mDebugNode->setVisible(true); 391 } 392 else if (mDebugNode) 393 { 394 mDebugNode->setVisible(false); 395 } 396 397 } 398 //--------------------------------------------------------------------- createContentCollection(const String & typeName)399 PageContentCollection* Page::createContentCollection(const String& typeName) 400 { 401 PageContentCollection* coll = getManager()->createContentCollection(typeName); 402 coll->_notifyAttached(this); 403 mContentCollections.push_back(coll); 404 return coll; 405 } 406 //--------------------------------------------------------------------- destroyContentCollection(PageContentCollection * coll)407 void Page::destroyContentCollection(PageContentCollection* coll) 408 { 409 ContentCollectionList::iterator i = std::find( 410 mContentCollections.begin(), mContentCollections.end(), coll); 411 if (i != mContentCollections.end()) 412 { 413 mContentCollections.erase(i); 414 } 415 getManager()->destroyContentCollection(coll); 416 } 417 //--------------------------------------------------------------------- getContentCollectionCount() const418 size_t Page::getContentCollectionCount() const 419 { 420 return mContentCollections.size(); 421 } 422 //--------------------------------------------------------------------- getContentCollection(size_t index)423 PageContentCollection* Page::getContentCollection(size_t index) 424 { 425 assert(index < mContentCollections.size()); 426 427 return mContentCollections[index]; 428 } 429 //--------------------------------------------------------------------- getContentCollectionList() const430 const Page::ContentCollectionList& Page::getContentCollectionList() const 431 { 432 return mContentCollections; 433 } 434 //--------------------------------------------------------------------- getSceneManager() const435 SceneManager* Page::getSceneManager() const 436 { 437 return mParent->getSceneManager(); 438 } 439 //--------------------------------------------------------------------- operator <<(std::ostream & o,const Page & p)440 std::ostream& operator <<( std::ostream& o, const Page& p ) 441 { 442 o << "Page(ID:" << p.getID() << ", section:" << p.getParentSection()->getName() 443 << ", world:" << p.getParentSection()->getWorld()->getName() << ")"; 444 return o; 445 } 446 //--------------------------------------------------------------------- generateFilename() const447 String Page::generateFilename() const 448 { 449 StringUtil::StrStreamType str; 450 if (mParent) 451 str << mParent->getWorld()->getName() << "_" << mParent->getName(); 452 453 str << std::setw(8) << std::setfill('0') << std::hex << mID << ".page"; 454 455 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS 456 // For the iOS we need to prefix the file name with the path to the Caches folder 457 String cacheStr(Ogre::macCachePath() + str.str()); 458 return cacheStr; 459 #else 460 return str.str(); 461 #endif 462 } 463 464 465 466 } 467 468