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