Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
PagedGeometry.cpp
Go to the documentation of this file.
1/*-------------------------------------------------------------------------------------
2Copyright (c) 2006 John Judnich
3
4This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
5Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
6 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
7 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
8 3. This notice may not be removed or altered from any source distribution.
9-------------------------------------------------------------------------------------*/
10
11//PagedGeometry.h
12//Main source file for the PagedGeometry engine.
13//-------------------------------------------------------------------------------------
14
15#include <OgreRoot.h>
16#include <OgreTimer.h>
17#include <OgreCamera.h>
18#include <OgreVector3.h>
19
20#include "PagedGeometry.h"
21#include "StaticBillboardSet.h"
22
23using namespace Ogre;
24using namespace std;
25
26namespace Forests {
27
28//-------------------------------------------------------------------------------------
29PagedGeometry::PagedGeometry(Camera* cam, const Real pageSize, Ogre::SceneNode* groupingSceneNode, Ogre::RenderQueueGroupID queue) :
30m_nRenderQueue(queue)
31{
32 //Setup camera, scene manager, and scene node
33 if (cam)
34 {
35 sceneCam = cam;
36 sceneMgr = sceneCam->getSceneManager();
37 oldCamPos = sceneCam->getDerivedPosition();
38 }
39 else
40 {
41 sceneCam = NULL;
42 sceneMgr = NULL;
43 oldCamPos = Vector3::ZERO;
44 }
45 m_pagedGroupingNode = groupingSceneNode;
46 lastSceneCam = NULL;
47 lastOldCamPos = Vector3::ZERO;
48
49 //Init. timer
50 timer.reset();
51 lastTime = 0;
52
53 //Setup page size / bounds
55 m_bounds = TBounds(0, 0, 0, 0);
56
57 //Misc.
58 pageLoader = NULL;
60 tempdir=""; // empty for current working directory
61 shadersEnabled = true; // enable shaders by default
62}
63
65{
66 //Remove all page managers and the geometry associated with them
68}
69
70void PagedGeometry::setTempDir(Ogre::String dir)
71{
72 tempdir = dir;
73}
74
76{
77 pageLoader = loader;
78}
79
80void PagedGeometry::setCamera(Camera *cam)
81{
82 if (cam == NULL){
83 //Simply set camera to null
84 sceneCam = NULL;
85 } else {
86 if (sceneMgr && cam->getSceneManager() != sceneMgr)
87 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "The specified camera is from the wrong SceneManager", "PagedGeometry::setCamera()");
88
89 if (cam == lastSceneCam){
90 //If the cache values for this camera are preserved, use them
91 std::swap(oldCamPos, lastOldCamPos);
92 std::swap(sceneCam, lastSceneCam);
93 } else {
96 sceneCam = cam;
97 }
98
99 //If sceneMgre is NULL (this only occurs the first time a camera is set),
100 //then set the scene manager (it won't change after this point).
101 if (sceneMgr == NULL)
102 sceneMgr = sceneCam->getSceneManager();
103 }
104}
105
107{
108 if (!managerList.empty())
109 OGRE_EXCEPT(Ogre::Exception::ERR_RENDERINGAPI_ERROR, "PagedGeometry::setPageSize() cannot be called after detail levels have been added. Call removeDetailLevels() first.", "PagedGeometry::setPageSize()");
110
111 pageSize = size;
112}
113
115{
116 if (!managerList.empty())
117 OGRE_EXCEPT(Ogre::Exception::ERR_RENDERINGAPI_ERROR, "PagedGeometry::setInfinite() cannot be called after detail levels have been added. Call removeDetailLevels() first.", "PagedGeometry::setInfinite()");
118
119 m_bounds = TBounds(0, 0, 0, 0);
120}
121
123{
124 if (!managerList.empty())
125 OGRE_EXCEPT(Ogre::Exception::ERR_RENDERINGAPI_ERROR, "PagedGeometry::setBounds() cannot be called after detail levels have been added. Call removeDetailLevels() first.", "PagedGeometry::setBounds()");
126 if (!Math::RealEqual(bounds.width(), bounds.height(), 0.01f))
127 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Bounds must be square", "PagedGeometry::setBounds()");
128 if (bounds.width() <= 0 || bounds.height() <=0)
129 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Bounds must have positive width and height", "PagedGeometry::setBounds()");
130
131 m_bounds = bounds;
132}
133
134TBounds PagedGeometry::convertAABToTBounds( const Ogre::AxisAlignedBox & aab ) const
135{
136 const Vector3 minimum(aab.getMinimum());
137 const Vector3 maximum(aab.getMaximum());
138 return TBounds (minimum.x, minimum.z, maximum.x, maximum.z);
139}
140
142{
143 std::list<GeometryPageManager *>::iterator it;
144
145 //Delete all the page managers
146 for (it = managerList.begin(); it != managerList.end(); ++it){
147 GeometryPageManager *mgr = *it;
148 delete mgr;
149 }
150
151 //Clear the page manager list
152 managerList.clear();
153}
154
156{
157 //If no camera has been set, then return without doing anything
158 if (sceneCam == NULL)
159 return;
160
161 //Calculate time since last update
162 unsigned long deltaTime, tmp;
163 tmp = timer.getMilliseconds();
164 deltaTime = tmp - lastTime;
165 lastTime = tmp;
166
167 //Get camera position and speed
168 Vector3 camPos = sceneCam->getDerivedPosition();
169 Vector3 camSpeed; //Speed in units-per-millisecond
170 if (deltaTime == 0){
171 camSpeed.x = 0;
172 camSpeed.y = 0;
173 camSpeed.z = 0;
174 } else {
175 camSpeed = (camPos - oldCamPos) / deltaTime;
176 }
177 oldCamPos = camPos;
178
179 if (pageLoader != 0){
180 //Update the PageLoader
182
183 //Update all the page managers
184 bool enableCache = true;
185 std::list<GeometryPageManager *>::iterator it;
186 GeometryPageManager *prevMgr = NULL;
187 for (it = managerList.begin(); it != managerList.end(); ++it)
188 {
189 GeometryPageManager *mgr = *it;
190 mgr->update(deltaTime, camPos, camSpeed, enableCache, prevMgr);
191 prevMgr = mgr;
192 }
193 }
194
195 //Update misc. subsystems
196 StaticBillboardSet::updateAll(sceneCam->getDerivedDirection());
197}
198
200{
201 assert(pageLoader);
202
203 std::list<GeometryPageManager *>::iterator it;
204 for (it = managerList.begin(); it != managerList.end(); ++it){
205 GeometryPageManager *mgr = *it;
206 mgr->reloadGeometry();
207 }
208}
209
210void PagedGeometry::reloadGeometryPage(const Vector3 &point)
211{
212 if (!pageLoader)
213 return;
214
215 std::list<GeometryPageManager *>::iterator it;
216 for (it = managerList.begin(); it != managerList.end(); ++it)
217 {
218 GeometryPageManager *mgr = *it;
219 mgr->reloadGeometryPage(point);
220 }
221}
222
223void PagedGeometry::reloadGeometryPages(const Ogre::Vector3 &center, Real radius)
224{
225 if (!pageLoader)
226 return;
227
228 std::list<GeometryPageManager *>::iterator it;
229 for (it = managerList.begin(); it != managerList.end(); ++it)
230 {
231 GeometryPageManager *mgr = *it;
232 mgr->reloadGeometryPages(center, radius);
233 }
234}
235
237{
238 if (!pageLoader)
239 return;
240
241 TBounds localArea = area;
242
243 if (localArea.left > localArea.right) std::swap(localArea.left, localArea.right);
244 if (localArea.top > localArea.bottom) std::swap(localArea.top, localArea.bottom);
245
246 std::list<GeometryPageManager *>::iterator it;
247 for (it = managerList.begin(); it != managerList.end(); ++it){
248 GeometryPageManager *mgr = *it;
249 mgr->reloadGeometryPages(localArea);
250 }
251}
252
254{
255 if (!pageLoader)
256 return;
257
258 TBounds localArea = area;
259
260 if (localArea.left > localArea.right) std::swap(localArea.left, localArea.right);
261 if (localArea.top > localArea.bottom) std::swap(localArea.top, localArea.bottom);
262
263 std::list<GeometryPageManager *>::iterator it;
264 for (it = managerList.begin(); it != managerList.end(); ++it){
265 GeometryPageManager *mgr = *it;
266 mgr->preloadGeometry(localArea);
267 }
268}
269
271{
272 if (!pageLoader)
273 return;
274
275 std::list<GeometryPageManager *>::iterator it;
276 for (it = managerList.begin(); it != managerList.end(); ++it){
277 GeometryPageManager *mgr = *it;
279 }
280}
281
282void PagedGeometry::_addDetailLevel(GeometryPageManager *mgr, Real maxRange, Real transitionLength)
283{
284 //Calculate the near range
285 Real minRange = 0;
286 if (!managerList.empty()){
287 GeometryPageManager *lastMgr = managerList.back();
288 minRange = lastMgr->getFarRange();
289 }
290
291 //Error check
292 if (maxRange <= minRange){
293 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Closer detail levels must be added before farther ones");
294 }
295
296 //Setup the new manager
297 mgr->setNearRange(minRange);
298 mgr->setFarRange(maxRange);
299 mgr->setTransition(transitionLength);
300
301 managerList.push_back(mgr);
302}
303
304//-------------------------------------------------------------------------------------
305
307: mainGeom(mainGeom)
308 , cacheTimer(0) // Reset the cache timer
309 , scrollBuffer(NULL)
310 , geomGrid(NULL)
311 , geomGridX(0)
312 , geomGridZ(0)
313{
314 //Use default cache speeds
316
317 //No transition default
318 setTransition(0);
319}
320
322{
323 //Delete GeometryPage's
324 for (int x = 0; x < geomGridX; ++x)
325 for (int z = 0; z < geomGridZ; ++z)
326 delete _getGridPage(x, z);
327
328 //Deallocate arrays
329 if(geomGrid)
330 delete[] geomGrid;
331 if(scrollBuffer)
332 delete[] scrollBuffer;
333}
334
335void GeometryPageManager::update(unsigned long deltaTime, Vector3 &camPos, Vector3 &camSpeed, bool &enableCache, GeometryPageManager *prevManager)
336{
337 //-- Cache new geometry pages --
338
339 //Cache 1 page ahead of the view ranges
340 const Real cacheDist = farTransDist + mainGeom->getPageSize();
341 const Real cacheDistSq = cacheDist * cacheDist;
342
343 //First calculate the general area where the pages will be processed
344 // 0,0 is the left top corner of the bounding box
345 int x1 = (int)Math::Floor(((camPos.x - cacheDist) - gridBounds.left) / mainGeom->getPageSize());
346 int x2 = (int)Math::Floor(((camPos.x + cacheDist) - gridBounds.left) / mainGeom->getPageSize());
347 int z1 = (int)Math::Floor(((camPos.z - cacheDist) - gridBounds.top) / mainGeom->getPageSize());
348 int z2 = (int)Math::Floor(((camPos.z + cacheDist) - gridBounds.top) / mainGeom->getPageSize());
349
350 if (scrollBuffer)
351 {
352 //Check if the page grid needs to be scrolled
353 int shiftX = 0, shiftZ = 0;
354 if (x1 < 0) shiftX = x1; else if (x2 >= geomGridX-1) shiftX = x2 - (geomGridX-1);
355 if (z1 < 0) shiftZ = z1; else if (z2 >= geomGridZ-1) shiftZ = z2 - (geomGridZ-1);
356 if (shiftX != 0 || shiftZ != 0)
357 {
358 //Scroll grid
359 _scrollGridPages(shiftX, shiftZ);
360
361 //Update grid bounds and processing area
362 gridBounds.left += shiftX * mainGeom->getPageSize();
363 gridBounds.right += shiftX * mainGeom->getPageSize();
364 gridBounds.top += shiftZ * mainGeom->getPageSize();
365 gridBounds.bottom += shiftZ * mainGeom->getPageSize();
366 x1 -= shiftX; x2 -= shiftX;
367 z1 -= shiftZ; z2 -= shiftZ;
368 }
369 }
370 else
371 {
372 // make sure that values are inbounds
373 if(x2 >= geomGridX)
374 x2 = geomGridX - 1;
375 if(z2 >= geomGridZ)
376 z2 = geomGridZ - 1;
377
378 if (x1 < 0)
379 x1 = 0;
380 if (z1 < 0)
381 z1 = 0;
382 }
383 //Now, in that general area, find what pages are within the cacheDist radius
384 //Pages within the cacheDist radius will be added to the pending block list
385 //to be loaded later, and pages within farDist will be loaded immediately.
386 for (int x = x1; x <= x2; ++x){
387 for (int z = z1; z <= z2; ++z){
388 GeometryPage *blk = _getGridPage(x, z);
389
390 Real dx = camPos.x - blk->_centerPoint.x;
391 Real dz = camPos.z - blk->_centerPoint.z;
392 Real distSq = dx * dx + dz * dz;
393
394 //If the page is in the cache radius...
395 if (distSq <= cacheDistSq){
396 //If the block hasn't been loaded yet, it should be
397 if (blk->_loaded == false){
398 //Test if the block's distance is between nearDist and farDist
399 if (distSq >= nearDistSq && distSq < farTransDistSq){
400 //If so, load the geometry immediately
401 _loadPage(blk);
402 loadedList.push_back(blk);
403 blk->_iter = (--loadedList.end());
404
405 //And remove it from the pending list if necessary
406 if (blk->_pending){
407 pendingList.remove(blk);
408 blk->_pending = false;
409 }
410 } else {
411 //Otherwise, add it to the pending geometry list (if not already)
412 //Pages in then pending list will be loaded later (see below)
413 if (!blk->_pending){
414 pendingList.push_back(blk);
415 blk->_pending = true;
416 }
417 }
418 } else {
419 //Set the inactive time to 0 (since the page is active). This
420 //must be done in order to keep it from expiring (and deleted).
421 //This way, blocks not in the cache radius won't have their
422 //inactivity clock reset, and they will expire in a few seconds.
423 blk->_inactiveTime = 0;
424 }
425 }
426 }
427 }
428
429
430 //Calculate cache speeds based on camera speed. This is important to keep the cache
431 //process running smooth, because if the cache can't keep up with the camera, the
432 //immediately visible pages will be forced to load instantly, which can cause
433 //noticeable and sudden stuttering. The cache system results in smoother performance
434 //because it smooths the loading tasks out across multiple frames. For example,
435 //instead of loading 10+ blocks every 2 seconds, the cache would load 1 block every
436 //200 milliseconds.
437 Real speed = Math::Sqrt(camSpeed.x * camSpeed.x + camSpeed.z * camSpeed.z);
438
439 unsigned long cacheInterval = 0;
440 if (speed == 0)
441 cacheInterval = maxCacheInterval;
442 else {
443 cacheInterval = (unsigned long)((mainGeom->getPageSize() * 0.8f) / (speed * pendingList.size()));
444 if (cacheInterval > maxCacheInterval)
445 cacheInterval = maxCacheInterval;
446 }
447
448
449 TPGeometryPages::iterator i1, i2;
450
451 //Now load a single geometry page periodically, based on the cacheInterval
452 cacheTimer += deltaTime;
453 if (cacheTimer >= cacheInterval && enableCache){
454 //Find a block to be loaded from the pending list
455 i1 = pendingList.begin();
456 i2 = pendingList.end();
457 while (i1 != i2)
458 {
459 GeometryPage *blk = *i1;
460
461 //Remove it from the pending list
462 i1 = pendingList.erase(i1);
463 blk->_pending = false;
464
465 //If it's within the geometry cache radius, load it and break out of the loop
466 Real dx = camPos.x - blk->_centerPoint.x;
467 Real dz = camPos.z - blk->_centerPoint.z;
468 Real distSq = dx * dx + dz * dz;
469 if (distSq <= cacheDistSq){
470 _loadPage(blk);
471 loadedList.push_back(blk);
472 blk->_iter = (--loadedList.end());
473
474 enableCache = false;
475 break;
476 }
477
478 //Otherwise this will keep looping until an unloaded page is found
479 }
480
481 //Reset the cache timer
482 cacheTimer = 0;
483 }
484
485
486 //-- Update existing geometry and impostors --
487
488 //Loop through each loaded geometry block
489 i1 = loadedList.begin();
490 i2 = loadedList.end();
491
492 //Real fadeBeginDistSq = farDistSq - Math::Sqr(mainGeom->getPageSize() * 1.4142f);
493 Real halfPageSize = mainGeom->getPageSize() * 0.5f;
494 while (i1 != i2)
495 {
496 GeometryPage *blk = *i1;
497
498 //If the geometry has expired...
499 if (blk->_inactiveTime >= inactivePageLife){
500 if (!blk->_keepLoaded) {
501 //Unload it
502 _unloadPage(blk);
503 i1 = loadedList.erase(i1);
504 } else {
505 //This page needs to be kept loaded indefinitely, so don't unload it
506 blk->_inactiveTime = 0;
507 ++i1;
508 }
509 } else {
510 //Update it's visibility/fade status based on it's distance from the camera
511 bool visible = false;
512 Real dx = camPos.x - blk->_centerPoint.x;
513 Real dz = camPos.z - blk->_centerPoint.z;
514 Real distSq = dx * dx + dz * dz;
515
516 Real overlap = 0, tmp;
517
518 tmp = blk->_trueBounds.getMaximum().x - halfPageSize;
519 if (tmp > overlap) overlap = tmp;
520 tmp = blk->_trueBounds.getMaximum().z - halfPageSize;
521 if (tmp > overlap) overlap = tmp;
522 tmp = blk->_trueBounds.getMinimum().x + halfPageSize;
523 if (tmp > overlap) overlap = tmp;
524 tmp = blk->_trueBounds.getMinimum().z + halfPageSize;
525 if (tmp > overlap) overlap = tmp;
526
527 Real pageLengthSq = Math::Sqr((mainGeom->getPageSize() + overlap) * 1.41421356f);
528
529 if (distSq + pageLengthSq >= nearDistSq && distSq - pageLengthSq < farTransDistSq){
530 //Fade the page when transitioning
531 bool enable = false;
532 Real fadeNear = 0;
533 Real fadeFar = 0;
534
535 if (fadeEnabled && distSq + pageLengthSq >= farDistSq){
536 //Fade in
537 visible = true;
538 enable = true;
539 fadeNear = farDist;
540 fadeFar = farTransDist;
541 }
542 else if (prevManager && prevManager->fadeEnabled && (distSq - pageLengthSq < prevManager->farTransDistSq)){
543 //Fade out
544 visible = true;
545 enable = true;
546 fadeNear = prevManager->farDist + (prevManager->farTransDist - prevManager->farDist) * 0.5f; //This causes geometry to fade out faster than it fades in, avoiding a state where a transition appears semitransparent
547 fadeFar = prevManager->farDist;
548 }
549
550 //Apply fade settings
551 if (enable != blk->_fadeEnable){
552 blk->setFade(enable, fadeNear, fadeFar);
553 blk->_fadeEnable = enable;
554 }
555 }
556 //Non-fade visibility
557 if (distSq >= nearDistSq && distSq < farDistSq)
558 visible = true;
559 //Hide all?
560 if (!mainGeom->getVisible())
561 visible = false;
562
563 //Update visibility
564 if (visible){
565 //Show the page if it isn't visible
566 if (blk->_visible != true){
567 blk->setVisible(true);
568 blk->_visible = true;
569 }
570 } else {
571 //Hide the page if it's not already
572 if (blk->_visible != false){
573 blk->setVisible(false);
574 blk->_visible = false;
575 }
576 }
577
578 //And update it
579 blk->update();
580
581 ++i1;
582 }
583
584 //Increment the inactivity timer for the geometry
585 blk->_inactiveTime += deltaTime;
586 }
587}
588
589//Clears all GeometryPage's
591{
592 TPGeometryPages::iterator it;
593 for (it = loadedList.begin(); it != loadedList.end(); ++it)
594 {
595 GeometryPage *page = *it;
596 _unloadPage(page);
597 }
598 loadedList.clear();
599}
600
601//Clears a single page (which contains the given point)
603{
604 //Determine which grid block contains the given points
605 int x = (int)Math::Floor(geomGridX * (point.x - gridBounds.left) / gridBounds.width());
606 int z = (int)Math::Floor(geomGridZ * (point.z - gridBounds.top) / gridBounds.height());
607
608 //Unload the grid block if it's in the grid area, and is loaded
609 if (x >= 0 && z >= 0 && x < geomGridX && z < geomGridZ){
610 GeometryPage *page = _getGridPage(x, z);
611 if (page->_loaded){
612 _unloadPage(page);
613 loadedList.erase(page->_iter);
614 }
615 }
616}
617
618//Clears pages within the radius
619void GeometryPageManager::reloadGeometryPages(const Vector3 &center, Real radius)
620{
621 //First calculate a square boundary to eliminate the search space
622 TBounds area(center.x - radius, center.z - radius, center.x + radius, center.z + radius);
623
624 //Determine which grid block contains the top-left corner
625 int x1 = (int)Math::Floor(geomGridX * (area.left - gridBounds.left) / gridBounds.width());
626 int z1 = (int)Math::Floor(geomGridZ * (area.top - gridBounds.top) / gridBounds.height());
627 if (x1 < 0) x1 = 0; else if (x1 > geomGridX-1) x1 = geomGridX-1;
628 if (z1 < 0) z1 = 0; else if (z1 > geomGridZ-1) z1 = geomGridZ-1;
629 //...and the bottom right
630 int x2 = (int)Math::Floor(geomGridX * (area.right - gridBounds.left) / gridBounds.width());
631 int z2 = (int)Math::Floor(geomGridZ * (area.bottom - gridBounds.top) / gridBounds.height());
632 if (x2 < 0) x2 = 0; else if (x2 > geomGridX-1) x2 = geomGridX-1;
633 if (z2 < 0) z2 = 0; else if (z2 > geomGridZ-1) z2 = geomGridZ-1;
634
635 //Scan all the grid blocks in the region
636 Real radiusSq = radius * radius;
637 for (int x = x1; x <= x2; ++x) {
638 for (int z = z1; z <= z2; ++z) {
639 GeometryPage *page = _getGridPage(x, z);
640 if (page->_loaded){
641 Vector3 pos = page->getCenterPoint();
642 Real distX = (pos.x - center.x), distZ = (pos.z - center.z);
643 Real distSq = distX * distX + distZ * distZ;
644
645 if (distSq <= radius) {
646 _unloadPage(page);
647 loadedList.erase(page->_iter);
648 }
649 }
650 }
651 }
652}
653
654//Clears pages within the bounds
656{
657 //Determine which grid block contains the top-left corner
658 int x1 = (int)Math::Floor(geomGridX * (area.left - gridBounds.left) / gridBounds.width());
659 int z1 = (int)Math::Floor(geomGridZ * (area.top - gridBounds.top) / gridBounds.height());
660 if (x1 < 0) x1 = 0; else if (x1 > geomGridX-1) x1 = geomGridX-1;
661 if (z1 < 0) z1 = 0; else if (z1 > geomGridZ-1) z1 = geomGridZ-1;
662 //...and the bottom right
663 int x2 = (int)Math::Floor(geomGridX * (area.right - gridBounds.left) / gridBounds.width());
664 int z2 = (int)Math::Floor(geomGridZ * (area.bottom - gridBounds.top) / gridBounds.height());
665 if (x2 < 0) x2 = 0; else if (x2 > geomGridX-1) x2 = geomGridX-1;
666 if (z2 < 0) z2 = 0; else if (z2 > geomGridZ-1) z2 = geomGridZ-1;
667
668 //Unload the grid blocks
669 for (int x = x1; x <= x2; ++x)
670 {
671 for (int z = z1; z <= z2; ++z)
672 {
673 GeometryPage *page = _getGridPage(x, z);
674 if (page->_loaded)
675 {
676 _unloadPage(page);
677 loadedList.erase(page->_iter);
678 }
679 }
680 }
681}
682
683//Loads pages within viewing range of the bounds and keeps them loaded
685{
686 TBounds loadarea;
687 loadarea.left = area.left - farDist;
688 loadarea.right = area.right + farDist;
689 loadarea.top = area.top - farDist;
690 loadarea.bottom = area.bottom + farDist;
691
692 //Determine which grid block contains the top-left corner
693 int x1 = (int)Math::Floor(geomGridX * (loadarea.left - gridBounds.left) / gridBounds.width());
694 int z1 = (int)Math::Floor(geomGridZ * (loadarea.top - gridBounds.top) / gridBounds.height());
695 if (x1 < 0) x1 = 0; else if (x1 > geomGridX-1) x1 = geomGridX-1;
696 if (z1 < 0) z1 = 0; else if (z1 > geomGridZ-1) z1 = geomGridZ-1;
697 //...and the bottom right
698 int x2 = (int)Math::Floor(geomGridX * (loadarea.right - gridBounds.left) / gridBounds.width());
699 int z2 = (int)Math::Floor(geomGridZ * (loadarea.bottom - gridBounds.top) / gridBounds.height());
700 if (x2 < 0) x2 = 0; else if (x2 > geomGridX-1) x2 = geomGridX-1;
701 if (z2 < 0) z2 = 0; else if (z2 > geomGridZ-1) z2 = geomGridZ-1;
702
703 //Preload the grid blocks
704 for (int x = x1; x <= x2; ++x) {
705 for (int z = z1; z <= z2; ++z) {
706 GeometryPage *page = _getGridPage(x, z);
707
708 //If the page isn't loaded
709 if (!page->_loaded){
710 //Load the geometry immediately
711 _loadPage(page);
712 loadedList.push_back(page);
713 page->_iter = (--loadedList.end());
714
715 //And remove it from the pending list if necessary
716 if (page->_pending){
717 pendingList.remove(page);
718 page->_pending = false;
719 }
720 }
721
722 //Flag the page so it won't expire and be deleted in a few seconds if
723 //it's currently not in the viewing range.
724 page->_keepLoaded = true;
725 }
726 }
727}
728
730{
731 //Set all grid blocks' _keepLoaded flag to false
732 for (int x = 0; x < geomGridX; ++x) {
733 for (int z = 0; z < geomGridZ; ++z) {
734 GeometryPage *page = _getGridPage(x, z);
735 page->_keepLoaded = true;
736 }
737 }
738}
739
740
741//Loads the given page of geometry immediately
742//Note: _loadPage() does add the page to loadedList, so that will have to be done manually
744{
745 //Calculate page info
746 PageInfo info;
747 Real halfPageSize = mainGeom->getPageSize() * 0.5f;
748
749 info.bounds.left = page->_centerPoint.x - halfPageSize;
750 info.bounds.right = page->_centerPoint.x + halfPageSize;
751 info.bounds.top = page->_centerPoint.z - halfPageSize;
752 info.bounds.bottom = page->_centerPoint.z + halfPageSize;
753 info.centerPoint = page->_centerPoint;
754 info.xIndex = page->_xIndex;
755 info.zIndex = page->_zIndex;
756
757 //Check if page needs unloading (if a delayed unload has been issued)
758 if (page->_needsUnload){
759 page->removeEntities();
761 page->_needsUnload = false;
762
763 page->clearBoundingBox();
764 }
765
766 //Load the page
767 page->setRegion(info.bounds.left, info.bounds.top, info.bounds.right, info.bounds.bottom);
768
771
772 page->build();
773 page->setVisible(page->_visible);
774
775 page->_inactiveTime = 0;
776 page->_loaded = true;
777 page->_fadeEnable = false;
778}
779
780//Unloads the given page of geometry immediately
781//Note: _unloadPage() does not remove the page from loadedList, so that will have to be done manually
783{
784 //Calculate boundaries to unload
785 PageInfo info;
786 Real halfPageSize = mainGeom->getPageSize() * 0.5f;
787
788 info.bounds.left = page->_centerPoint.x - halfPageSize;
789 info.bounds.right = page->_centerPoint.x + halfPageSize;
790 info.bounds.top = page->_centerPoint.z - halfPageSize;
791 info.bounds.bottom = page->_centerPoint.z + halfPageSize;
792 info.centerPoint = page->_centerPoint;
793 info.xIndex = page->_xIndex;
794 info.zIndex = page->_zIndex;
795
796 //Unload the page
797 page->removeEntities();
799 page->_needsUnload = false;
800
801 page->clearBoundingBox();
802
803 page->_inactiveTime = 0;
804 page->_loaded = false;
805 page->_fadeEnable = false;
806}
807
808//"Virtually" unloads the given page of geometry. In reality it is unloaded during the next load.
809//Note: _unloadPageDelayed() does not remove the page from loadedList, so that will have to be done manually
811{
812 page->_needsUnload = true;
813 page->_loaded = false;
814}
815
816
817//Scrolls pages in the grid by the given amount
818void GeometryPageManager::_scrollGridPages(int shiftX, int shiftZ)
819{
820 //Check if the camera moved completely out of the grid
821 if (shiftX > geomGridX || shiftX < -geomGridX || shiftZ > geomGridZ || shiftZ < -geomGridZ){
822 //If so, just reload all the tiles (reloading is accomplished by unloading - loading is automatic)
823 for (int x = 0; x < geomGridX; ++x){
824 for (int z = 0; z < geomGridZ; ++z){
825 GeometryPage *page = _getGridPage(x, z);
826 if (page->_loaded){
827 page->_keepLoaded = false;
828 _unloadPage(page);
829 loadedList.erase(page->_iter);
830 }
831 page->_centerPoint.x += shiftX * mainGeom->getPageSize();
832 page->_centerPoint.z += shiftZ * mainGeom->getPageSize();
833 page->_xIndex += shiftX;
834 page->_zIndex += shiftZ;
835 }
836 }
837 } else { //If not, scroll the grid by the X and Y values
838 //Scroll horizontally (X)
839 if (shiftX > 0){
840 for (int z = 0; z < geomGridZ; ++z){
841 //Temporarily store off-shifted pages first
842 for (int x = 0; x < shiftX; ++x){
843 GeometryPage *page = _getGridPage(x, z);
844 if (page->_loaded){
845 page->_keepLoaded = false;
846 _unloadPageDelayed(page);
847 loadedList.erase(page->_iter);
848 }
849 scrollBuffer[x] = page;
850 }
851 //Shift left
852 int shiftedMidpoint = geomGridX - shiftX;
853 for (int x = 0; x < shiftedMidpoint; ++x)
854 _setGridPage(x, z, _getGridPage(x + shiftX, z));
855 //Rotate temporary pages around to other side of grid
856 for (int x = 0; x < shiftX; ++x){
859 _setGridPage(x + shiftedMidpoint, z, scrollBuffer[x]);
860 }
861 }
862 }
863 else if (shiftX < 0) {
864 for (int z = 0; z < geomGridZ; ++z){
865 //Temporarily store off-shifted pages first
866 int initialMidpoint = geomGridX + shiftX;
867 for (int x = initialMidpoint; x < geomGridX; ++x){
868 GeometryPage *page = _getGridPage(x, z);
869 if (page->_loaded){
870 page->_keepLoaded = false;
871 _unloadPageDelayed(page);
872 loadedList.erase(page->_iter);
873 }
874 scrollBuffer[x - initialMidpoint] = page;
875 }
876 //Shift right
877 for (int x = geomGridX-1; x >= -shiftX; x--)
878 _setGridPage(x, z, _getGridPage(x + shiftX, z));
879 //Rotate temporary pages around to other side of grid
880 for (int x = 0; x < -shiftX; ++x){
884 }
885 }
886 }
887 //Scroll vertically (Z)
888 if (shiftZ > 0){
889 for (int x = 0; x < geomGridX; ++x){
890 //Temporarily store off-shifted pages first
891 for (int z = 0; z < shiftZ; ++z){
892 GeometryPage *page = _getGridPage(x, z);
893 if (page->_loaded){
894 page->_keepLoaded = false;
895 _unloadPageDelayed(page);
896 loadedList.erase(page->_iter);
897 }
898 scrollBuffer[z] = page;
899 }
900 //Shift left
901 int shiftedMidpoint = geomGridZ - shiftZ;
902 for (int z = 0; z < shiftedMidpoint; ++z)
903 _setGridPage(x, z, _getGridPage(x, z + shiftZ));
904 //Rotate temporary pages around to other side of grid
905 for (int z = 0; z < shiftZ; ++z){
908 _setGridPage(x, z + shiftedMidpoint, scrollBuffer[z]);
909 }
910 }
911 }
912 else if (shiftZ < 0) {
913 for (int x = 0; x < geomGridX; ++x){
914 //Temporarily store off-shifted pages first
915 int initialMidpoint = geomGridZ + shiftZ;
916 for (int z = initialMidpoint; z < geomGridZ; ++z){
917 GeometryPage *page = _getGridPage(x, z);
918 if (page->_loaded){
919 page->_keepLoaded = false;
920 _unloadPageDelayed(page);
921 loadedList.erase(page->_iter);
922 }
923 scrollBuffer[z - initialMidpoint] = page;
924 }
925 //Shift right
926 for (int z = geomGridZ-1; z >= -shiftZ; z--)
927 _setGridPage(x, z, _getGridPage(x, z + shiftZ));
928 //Rotate temporary pages around to other side of grid
929 for (int z = 0; z < -shiftZ; ++z){
933 }
934 }
935 }
936 }
937}
938
939
941{
944 _inactiveTime = 0; _xIndex = _zIndex = 0;
945 _centerPoint = Ogre::Vector3::ZERO;
946}
947
948void GeometryPage::addEntityToBoundingBox(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale)
949{
950 //Update bounding box
951 Ogre::Matrix4 mat(rotation);
952 mat.setScale(scale);
953 Ogre::AxisAlignedBox entBounds = ent->getBoundingBox();
954 entBounds.transform(mat);
955
956 Ogre::Vector3 relPosition = position - _centerPoint;
958 _trueBounds.setMinimum(entBounds.getMinimum() + relPosition);
959 _trueBounds.setMaximum(entBounds.getMaximum() + relPosition);
960 _trueBoundsUndefined = false;
961 } else {
962 Ogre::Vector3 min = _trueBounds.getMinimum();
963 Ogre::Vector3 max = _trueBounds.getMaximum();
964 min.makeFloor(entBounds.getMinimum() + relPosition);
965 max.makeCeil(entBounds.getMaximum() + relPosition);
966 _trueBounds.setMinimum(min);
967 _trueBounds.setMaximum(max);
968 }
969}
970
971const AxisAlignedBox &GeometryPage::getBoundingBox()
972{
973 return _trueBounds;
974}
975
977{
978 _trueBounds = AxisAlignedBox(0, 0, 0, 0, 0, 0);
980}
981}
This base-class can be extended to provide different ways of representing entities.
virtual void setFade(bool enabled, Ogre::Real visibleDist=0, Ogre::Real invisibleDist=0)=0
Sets fade behavior for this page.
Ogre::Vector3 _centerPoint
Ogre::AxisAlignedBox _trueBounds
virtual void removeEntities()=0
Remove all geometry/entities from the page completely.
virtual void clearBoundingBox()
Advanced: Reset the bounding box used by addEntityToBoundingBox()
virtual void build()
Perform any final steps to make added entities appear in the scene.
virtual void setVisible(bool visible)=0
Toggle the entire page's visibility.
virtual void update()
Do whatever needs to be done to keep the page geometry up-to-date.
std::list< GeometryPage * >::iterator _iter
virtual const Ogre::AxisAlignedBox & getBoundingBox()
Advanced: Return the bounding box computed with addEntityToBoundingBox()
Ogre::Vector3 & getCenterPoint()
Gets the center point of the page.
GeometryPage()
Constructor Initialise everything to zero, false or NULL except for _trueBoundsUndefined that is set ...
unsigned long _inactiveTime
virtual void addEntityToBoundingBox(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale)
Advanced: Expand the current bounding box to include the given entity.
virtual void setRegion(Ogre::Real left, Ogre::Real top, Ogre::Real right, Ogre::Real bottom)
Prepare a geometry page for entities.
Manages the rendering of geometry for a detail level type.
void setTransition(Ogre::Real transitionLength)
void update(unsigned long deltaTime, Ogre::Vector3 &camPos, Ogre::Vector3 &camSpeed, bool &enableCache, GeometryPageManager *prevManager)
Internal function - DO NOT USE.
void reloadGeometryPage(const Ogre::Vector3 &point)
Internal function - DO NOT USE.
GeometryPageManager(PagedGeometry *mainGeom)
Internal function - DO NOT USE.
void _scrollGridPages(int shiftX, int shiftZ)
GeometryPage * _getGridPage(const int x, const int z)
void _unloadPage(GeometryPage *page)
void setFarRange(Ogre::Real farRange)
Sets the far viewing range of this page manager.
void setCacheSpeed(unsigned long maxCacheInterval=200, unsigned long inactivePageLife=2000)
Customizes the cache behaviour (advanced).
~GeometryPageManager()
Internal function - DO NOT USE.
void setNearRange(Ogre::Real nearRange)
Sets the near viewing range of this page manager.
void preloadGeometry(const TBounds &area)
Internal function - DO NOT USE.
void _setGridPage(const int x, const int z, GeometryPage *page)
void resetPreloadedGeometry()
Internal function - DO NOT USE.
void _loadPage(GeometryPage *page)
Ogre::Real getFarRange() const
Gets the far viewing range of this page manager.
void reloadGeometryPages(const Ogre::Vector3 &center, Ogre::Real radius)
Internal function - DO NOT USE.
void _unloadPageDelayed(GeometryPage *page)
void reloadGeometry()
Internal function - DO NOT USE.
A class which you extend to provide a callback function for loading entities.
virtual void unloadPage(PageInfo &page)
This may be overridden (optional) to unload custom data associated with a page.
GeometryPage * geomPage
virtual void frameUpdate()
Provides a method for you to perform per-frame tasks for your PageLoader if overridden (optional)
virtual void loadPage(PageInfo &page)=0
This should be overridden to load a specified region of entities.
A class providing highly optimized methods for rendering massive amounts of geometry.
Ogre::Real getPageSize()
Gets the current page size.
std::list< GeometryPageManager * > managerList
void reloadGeometryPages(const Ogre::Vector3 &center, Ogre::Real radius)
Reloads geometry in the given radius area.
void removeDetailLevels()
Removes all detail levels from the PagedGeometry object.
void setCamera(Ogre::Camera *cam)
Sets the camera to use when calculating levels of detail.
void setTempDir(Ogre::String dir)
Sets the output directory for the imposter pages.
void preloadGeometry(const TBounds &area)
Preloads a region of geometry (loads once and never loads again)
Ogre::Camera * sceneCam
void reloadGeometryPage(const Ogre::Vector3 &point)
Reloads geometry at the given location.
PagedGeometry(Ogre::Camera *cam=NULL, Ogre::Real pageSize=100, Ogre::SceneNode *groupingSceneNode=NULL, Ogre::RenderQueueGroupID queue=Ogre::RENDER_QUEUE_6)
Initializes a PagedGeometry object.
void setPageLoader(PageLoader *loader)
Assigns a PageLoader object for the PagedGeometry.
Ogre::SceneManager * sceneMgr
bool getVisible()
Returns whether or not geometry managed by this PagedGeometry instance is visible.
void setInfinite()
Switches to infinite mode.
Ogre::Vector3 lastOldCamPos
PageLoader * getPageLoader()
Gets the PageLoader currently being used to load geometry.
Ogre::Camera * lastSceneCam
void resetPreloadedGeometry()
Releases geometry preloaded with preloadGeometry() to be unloaded if necessary.
void setPageSize(Ogre::Real size)
Sets the page size.
Ogre::SceneNode * m_pagedGroupingNode
void _addDetailLevel(GeometryPageManager *mgr, Ogre::Real maxRange, Ogre::Real transitionLength)
void update()
Updates this PagedGeometry object.
void setBounds(const TBounds bounds)
Switches to bounded mode and uses the given boundaries.
TBounds convertAABToTBounds(const Ogre::AxisAlignedBox &aab) const
Convert an Ogre::AxisAlignedBox to a TBounds coplanar to the plane defined by the UP axis.
void reloadGeometry()
Reloads all visible geometry.
static void updateAll(const Ogre::Vector3 &cameraDirection)
Manually updates all StaticBillboardSet objects for a frame.
Ogre::TRect< Ogre::Real > TBounds
Define TBounds as a TRect using Real numeric units.
Useful page information supplied to a pageloader.
int zIndex
The Z index of the page tile.
Ogre::Vector3 centerPoint
The center of the page (simply the middle of the bounds).
TBounds bounds
The page boundaries in which all entities should be placed.
int xIndex
The X index of the page tile.