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 29 #ifndef __Ogre_TerrainGroup_H__ 30 #define __Ogre_TerrainGroup_H__ 31 32 #include "OgreTerrainPrerequisites.h" 33 #include "OgreTerrain.h" 34 #include "OgreWorkQueue.h" 35 #include "OgreIteratorWrappers.h" 36 #include "OgreConfigFile.h" 37 38 namespace Ogre 39 { 40 class TerrainAutoUpdateLod; 41 42 /** \addtogroup Optional 43 * @{ 44 */ 45 /** \addtogroup Terrain 46 * Some details on the terrain component 47 * @{ 48 */ 49 50 /** Helper class to assist you in managing multiple terrain instances 51 that are connected to each other. 52 @remarks 53 This class makes it easy to specify the same options for a group of 54 terrain instances and have them positioned relative to each other and 55 associated via each other's neighbour connections. You can do all this 56 manually but this class just makes things easier, so that you only 57 have to specify most options once. 58 @par 59 Terrains are maintained in a grid of entries, and for simplicity 60 the grid cells are indexed from 0 as a 'centre' slot, supporting both 61 positive and negative values. so (0,0) is the centre slot, (1,0) is the 62 slot to the right of the centre, (1,0) is the slot above the centre, (-2,1) 63 is the slot two to the left of the centre and one up, etc. The maximum 64 extent of each axis is -32768 to +32767, so in other words enough for 65 over 4 billion entries. That should be enough for anyone! 66 @par 67 Note that this is not a 'paging' class as such. It's simply a way to make it easier to 68 perform common tasks with multiple terrain instances, which you choose when 69 to define, load and remove. Automatic paging is handled separately by the Paging 70 component. 71 */ 72 class _OgreTerrainExport TerrainGroup : public WorkQueue::RequestHandler, 73 public WorkQueue::ResponseHandler, public TerrainAlloc 74 { 75 public: 76 /** Constructor. 77 @param sm The SceneManager which will parent the terrain instances. 78 @param align The alignment that all terrain instances will use 79 @param terrainSize The size of each terrain down one edge in vertices (2^n+1) 80 @param terrainWorldSize The world size of each terrain instance 81 */ 82 TerrainGroup(SceneManager* sm, Terrain::Alignment align, uint16 terrainSize, 83 Real terrainWorldSize); 84 /** Alternate constructor. 85 @remarks 86 You can ONLY use this constructor if you subsequently call loadGroupDefinition 87 or loadLegacyTerrain to populate the rest. 88 */ 89 TerrainGroup(SceneManager* sm); 90 virtual ~TerrainGroup(); 91 92 /** Retrieve a shared structure which will provide the base settings for 93 all terrains created via this group. 94 @remarks 95 All neighbouring terrains should have the same basic settings (particularly 96 the size parameters) - to avoid having to set the terrain import information 97 more than once, you can retrieve the standard settings for this group 98 here and modify them to your needs. Once you've done that you can 99 use the shortcut methods in this class to create new terrain instances 100 using these base settings (plus any per-instance settings you might 101 want to use). 102 @note 103 The structure returned from this method is intended for in-place modification, 104 that's why it is not const and there is no equivalent 'set' method. 105 You should not, however, change the alignment or any of the size parameters 106 after you start constructing instances, since neighbouring terrains 107 should have the same size & alignment. 108 */ getDefaultImportSettings()109 virtual Terrain::ImportData& getDefaultImportSettings() { return mDefaultImportData; } 110 111 /** Define the centre position of the grid of terrain. 112 */ 113 virtual void setOrigin(const Vector3& pos); 114 115 /** Retrieve the centre position of the grid of terrain. 116 */ getOrigin()117 virtual const Vector3& getOrigin() const { return mOrigin; } 118 119 /** Retrieve the alignment of the grid of terrain (cannot be modified after construction). 120 */ getAlignment()121 virtual Terrain::Alignment getAlignment() const { return mAlignment; } 122 123 /** Retrieve the world size of each terrain instance 124 */ getTerrainWorldSize()125 virtual Real getTerrainWorldSize() const { return mTerrainWorldSize; } 126 /** Set the world size of terrain. 127 @note This will cause the terrains to change position due to their size change 128 @param newWorldSize the new world size of each terrain instance 129 */ 130 virtual void setTerrainWorldSize(Real newWorldSize); 131 /** Retrieve the size of each terrain instance in number of vertices down one side 132 */ getTerrainSize()133 virtual uint16 getTerrainSize() const { return mTerrainSize; } 134 /** Set the size of each terrain instance in number of vertices down one side. 135 @note This will cause the height data in each nested terrain to be bilinear 136 filtered to fit the new data size. 137 @param newTerrainSize the new map size of each terrain instance 138 */ 139 virtual void setTerrainSize(uint16 newTerrainSize); 140 /** Retrieve the SceneManager being used for this group. 141 */ getSceneManager()142 virtual SceneManager* getSceneManager() const { return mSceneManager; } 143 144 /** Set the naming convention for file names in this terrain group. 145 @remarks 146 You can more easily generate file names for saved / loaded terrain slots 147 if you define a naming prefix. When you call saveAllTerrains(), all the 148 terrain instances currently loaded will be saved to a file named 149 <prefix>_<index>.<extension>, where <index> is 150 given by packing the x and y coordinates of the entry into a 32-bit 151 index (@see packIndex). 152 */ 153 void setFilenameConvention(const String& prefix, const String& extension); 154 /// @see setFilenameConvention 155 void setFilenamePrefix(const String& prefix); 156 /// @see setFilenameConvention 157 void setFilenameExtension(const String& extension); 158 /// @see setFilenameConvention getFilenamePrefix()159 const String& getFilenamePrefix() const { return mFilenamePrefix; } 160 /// @see setFilenameConvention getFilenameExtension()161 const String& getFilenameExtension() const { return mFilenameExtension; } 162 163 /** Set the resource group in which files will be located. */ setResourceGroup(const String & grp)164 void setResourceGroup(const String& grp) { mResourceGroup = grp; } 165 /** Get the resource group in which files will be located. */ getResourceGroup()166 const String& getResourceGroup() const { return mResourceGroup; } 167 /** Define a 'slot' in the terrain grid - in this case to be loaded from 168 a generated file name. 169 @remarks 170 At this stage the terrain instance isn't actually present in the grid, 171 you're merely expressing an intention for it to take its place there 172 once it's loaded. The reason we do it like this is to support 173 background preparation of this terrain instance. 174 @note This method assumes that you want a file name to be generated from 175 the naming convention that you have supplied (@see setFilenameConvention). 176 If a file of that name isn't found during loading, then a flat terrain is 177 created instead at height 0. 178 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 179 */ 180 virtual void defineTerrain(long x, long y); 181 182 /** Define a 'slot' in the terrain grid - in this case a flat terrain. 183 @remarks 184 At this stage the terrain instance isn't actually present in the grid, 185 you're merely expressing an intention for it to take its place there 186 once it's loaded. The reason we do it like this is to support 187 background preparation of this terrain instance. 188 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 189 @param constantHeight The constant, uniform height that you want the terrain 190 to start at 191 */ 192 virtual void defineTerrain(long x, long y, float constantHeight); 193 194 /** Define the content of a 'slot' in the terrain grid. 195 @remarks 196 At this stage the terrain instance isn't actually present in the grid, 197 you're merely expressing an intention for it to take its place there 198 once it's loaded. The reason we do it like this is to support 199 background preparation of this terrain instance. 200 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 201 @param importData Import data - this data is copied during the 202 call so you may destroy your copy afterwards. 203 */ 204 virtual void defineTerrain(long x, long y, const Terrain::ImportData* importData); 205 206 /** Define the content of a 'slot' in the terrain grid. 207 @remarks 208 At this stage the terrain instance isn't actually present in the grid, 209 you're merely expressing an intention for it to take its place there 210 once it's loaded. The reason we do it like this is to support 211 background preparation of this terrain instance. 212 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 213 @param img Heightfield image - this data is copied during the call so you may destroy your copy afterwards. 214 @param layers Optional texture layers to use (if not supplied, default import 215 data layers will be used) - this data is copied during the 216 call so you may destroy your copy afterwards. 217 */ 218 virtual void defineTerrain(long x, long y, const Image* img, const Terrain::LayerInstanceList* layers = 0); 219 220 /** Define the content of a 'slot' in the terrain grid. 221 @remarks 222 At this stage the terrain instance isn't actually present in the grid, 223 you're merely expressing an intention for it to take its place there 224 once it's loaded. The reason we do it like this is to support 225 background preparation of this terrain instance. 226 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 227 @param pFloat Heights array 228 @param layers Optional texture layers to use (if not supplied, default import 229 data layers will be used) - this data is copied during the 230 call so you may destroy your copy afterwards. 231 */ 232 virtual void defineTerrain(long x, long y, const float* pFloat, const Terrain::LayerInstanceList* layers = 0); 233 234 /** Define the content of a 'slot' in the terrain grid. 235 @remarks 236 At this stage the terrain instance isn't actually present in the grid, 237 you're merely expressing an intention for it to take its place there 238 once it's loaded. The reason we do it like this is to support 239 background preparation of this terrain instance. 240 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 241 @param filename The name of a file which fully defines the terrain (as 242 written by Terrain::save). Size settings from all files must agree. 243 */ 244 virtual void defineTerrain(long x, long y, const String& filename); 245 246 247 /** Load any terrain instances that have been defined but not loaded yet. 248 @param synchronous Whether we should force this to happen entirely in the 249 primary thread (default false, operations are threaded if possible) 250 */ 251 virtual void loadAllTerrains(bool synchronous = false); 252 253 /** Load a specific terrain slot based on the definition that has already 254 been supplied. 255 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 256 @param synchronous Whether we should force this to happen entirely in the 257 primary thread (default false, operations are threaded if possible) 258 */ 259 virtual void loadTerrain(long x, long y, bool synchronous = false); 260 261 /** Load a terrain.cfg as used by the terrain scene manager into a single terrain slot 262 * 263 * automatically configures the SM2Profile if it is used. 264 * @attention not all of the legacy parameters/ parameter combinations are supported 265 * @param cfgFilename .cfg file that specifices what textures/scale/mipmaps/etc to use. 266 * @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 267 */ 268 void loadLegacyTerrain(const String& cfgFilename, long x = 0, long y = 0, bool synchronous = true); 269 270 /// @overload 271 void loadLegacyTerrain(const ConfigFile& cfg, long x = 0, long y = 0, bool synchronous = true); 272 273 /** Unload a specific terrain slot. 274 @remarks 275 This destroys the Terrain instance but retains the slot definition (so 276 it would be reloaded next time you call loadAllTerrains() if you did not 277 remove it beforehand). 278 @note 279 While the definition of the terrain is kept, if you used import data 280 to populate it, this will have been lost so repeat loading cannot occur. 281 The only way to support repeat loading is via the 'filename' option of 282 defineTerrain instead. 283 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 284 */ 285 virtual void unloadTerrain(long x, long y); 286 287 /** Remove a specific terrain slot. 288 @remarks 289 This destroys any Terrain instance at this position and also removes the 290 definition, so it essentially no longer exists. 291 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 292 */ 293 virtual void removeTerrain(long x, long y); 294 295 /** Remove all terrain instances. 296 */ 297 void removeAllTerrains(); 298 299 /** Save all terrain instances using the assigned file names, or 300 via the filename convention. 301 @see setFilenameConvention 302 @see setResourceGroup 303 @param onlyIfModified If true, only terrains that have been modified since load(), 304 or since the last save(), will be saved. You want to set this to true if 305 you loaded the terrain from these same files, but false if you 306 defined them using some other input data since the files wouldn't exist. 307 @param replaceManualFilenames If true, replaces any manually defined filenames 308 in the TerrainSlotDefinition with the generated names from the convention. 309 This is recommended since it makes everything more consistent, although 310 you might want to use manual filenames in the original definition to import 311 previously separate data. 312 */ 313 void saveAllTerrains(bool onlyIfModified, bool replaceManualFilenames = true); 314 315 /** Definition of how to populate a 'slot' in the terrain group. 316 */ 317 struct _OgreTerrainExport TerrainSlotDefinition 318 { 319 /// Filename, if this is to be loaded from a file 320 String filename; 321 /// Import data, if this is to be defined based on importing 322 Terrain::ImportData* importData; 323 TerrainSlotDefinitionTerrainSlotDefinition324 TerrainSlotDefinition() :importData(0) {} 325 ~TerrainSlotDefinition(); 326 327 /// Set to use import data 328 void useImportData(); 329 /// Set to use file name 330 void useFilename(); 331 /// Destroy temp import resources 332 void freeImportData(); 333 }; 334 335 /** Slot for a terrain instance, together with its definition. */ 336 struct _OgreTerrainExport TerrainSlot : public TerrainAlloc 337 { 338 /// The coordinates of the terrain slot relative to the centre slot (signed). 339 long x, y; 340 /// Definition used to load the terrain 341 TerrainSlotDefinition def; 342 /// Actual terrain instance 343 Terrain* instance; 344 TerrainSlotTerrainSlot345 TerrainSlot(long _x, long _y) : x(_x), y(_y), instance(0) {} 346 virtual ~TerrainSlot(); 347 void freeInstance(); 348 }; 349 350 /** Get the definition of a slot in the terrain. 351 @remarks 352 Definitions exist before the actual instances to allow background loading. 353 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 354 @return The definition, or null if nothing is in this slot. While this return value is 355 not const, you should be careful about modifying it; it will have no effect unless you load 356 the terrain afterwards, and can cause a race condition if you modify it while a background 357 load is in progress. 358 */ 359 virtual TerrainSlotDefinition* getTerrainDefinition(long x, long y) const; 360 361 /** Get the terrain instance at a given slot, if loaded. 362 @param x, y The coordinates of the terrain slot relative to the centre slot (signed). 363 @return The terrain, or null if no terrain is loaded in this slot (call getTerrainDefinition if 364 you want to access the definition before it is loaded). 365 */ 366 virtual Terrain* getTerrain(long x, long y) const; 367 368 /** Free as many resources as possible for optimal run-time memory use for all 369 terrain tiles. 370 @see Terrain::freeTemporaryResources 371 */ 372 void freeTemporaryResources(); 373 374 /** Trigger the update process for all terrain instances. 375 @see Terrain::update 376 */ 377 void update(bool synchronous = false); 378 379 /** Performs an update on all terrain geometry. 380 @see Terrain::updateGeometry 381 */ 382 void updateGeometry(); 383 384 /** Updates derived data for all terrains (LOD, lighting) to reflect changed height data. 385 @see Terrain::updateDerivedData 386 */ 387 void updateDerivedData(bool synchronous = false, uint8 typeMask = 0xFF); 388 389 /** Result from a terrain ray intersection with the terrain group. 390 */ 391 struct _OgreTerrainExport RayResult 392 { 393 /// Whether an intersection occurred 394 bool hit; 395 /// Which terrain instance was hit, if any 396 Terrain* terrain; 397 /// Position at which the intersection occurred 398 Vector3 position; 399 RayResultRayResult400 RayResult(bool _hit, Terrain* _terrain, const Vector3& _pos) 401 : hit(_hit), terrain(_terrain), position(_pos) {} 402 }; 403 404 /** Get the height data for a given world position (projecting the point 405 down on to the terrain underneath). 406 @param x, y,z Position in world space. Positions will be clamped to the edge 407 of the terrain 408 @param ppTerrain Pointer to a pointer to a terrain which will be completed 409 with the terrain that was found to resolve this query, or null if none were 410 */ 411 float getHeightAtWorldPosition(Real x, Real y, Real z, Terrain** ppTerrain = 0); 412 413 /** Get the height data for a given world position (projecting the point 414 down on to the terrain). 415 @param pos Position in world space. Positions will be clamped to the edge 416 of the terrain 417 @param ppTerrain Pointer to a pointer to a terrain which will be completed 418 with the terrain that was found to resolve this query, or null if none were 419 */ 420 float getHeightAtWorldPosition(const Vector3& pos, Terrain** ppTerrain = 0); 421 422 /** Test for intersection of a given ray with any terrain in the group. If the ray hits 423 a terrain, the point of intersection and terrain instance is returned. 424 @param ray The ray to test for intersection 425 @param distanceLimit The distance from the ray origin at which we will stop looking, 426 0 indicates no limit 427 @return A result structure which contains whether the ray hit a terrain and if so, where. 428 @remarks This can be called from any thread as long as no parallel write to 429 the terrain data occurs. 430 */ 431 RayResult rayIntersects(const Ray& ray, Real distanceLimit = 0) const; 432 433 typedef std::vector<Terrain*> TerrainList; 434 /** Test intersection of a box with the terrain. 435 @remarks 436 Tests an AABB for overlap with a terrain bounding box. Note that this does not mean that the box 437 touches the terrain itself, just the bounding box for the terrain. You can use this to get region 438 results for further testing or use (e.g. painting areas). 439 @param box The AABB you want to test in world units 440 @param resultList Pointer to a list of terrain pointers which will be updated to include just 441 the terrains that overlap 442 */ 443 void boxIntersects(const AxisAlignedBox& box, TerrainList* resultList) const; 444 /** Test intersection of a sphere with the terrain. 445 @remarks 446 Tests a sphere for overlap with a terrain bounding box. Note that this does not mean that the sphere 447 touches the terrain itself, just the bounding box for the terrain. You can use this to get region 448 results for further testing or use (e.g. painting areas). 449 @param sphere The sphere you want to test in world units 450 @param resultList Pointer to a list of terrain pointers which will be updated to include just 451 the terrains that overlap 452 */ 453 void sphereIntersects(const Sphere& sphere, TerrainList* resultList) const; 454 455 /** Convert a world position to terrain slot coordinates. 456 @param pos The world position 457 @param x,y Pointers to the coordinates to be completed. 458 */ 459 void convertWorldPositionToTerrainSlot(const Vector3& pos, long *x, long *y) const; 460 461 /** Convert a slot location to a world position at the centre 462 @param x,y The slot coordinates 463 @param pos Pointer to the world position to be completed 464 */ 465 void convertTerrainSlotToWorldPosition(long x, long y, Vector3* pos) const; 466 467 /** Calls Terrain::isDerivedDataUpdateInProgress on each loaded instance and returns true 468 if any of them are undergoing a derived update. 469 */ 470 bool isDerivedDataUpdateInProgress() const; 471 472 /// Packed map, signed 16 bits for each axis from -32767 to +32767 473 typedef std::map<uint32, TerrainSlot*> TerrainSlotMap; 474 typedef MapIterator<TerrainSlotMap> TerrainIterator; 475 typedef ConstMapIterator<TerrainSlotMap> ConstTerrainIterator; 476 477 /// Get an iterator over the defined terrains. 478 TerrainIterator getTerrainIterator(); 479 /// Get an iterator over the defined terrains (const) 480 ConstTerrainIterator getTerrainIterator() const; 481 482 /// WorkQueue::RequestHandler override 483 bool canHandleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ); 484 /// WorkQueue::RequestHandler override 485 WorkQueue::Response* handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ); 486 /// WorkQueue::ResponseHandler override 487 bool canHandleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ); 488 /// WorkQueue::ResponseHandler override 489 void handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ); 490 491 /// Convert coordinates to a packed integer index 492 uint32 packIndex(long x, long y) const; 493 494 /// Convert a packed integer index to coordinates 495 void unpackIndex(uint32 key, long *x, long *y); 496 497 /// Generate a file name based on the current naming convention 498 String generateFilename(long x, long y) const; 499 500 /** Save the group data only in native form to a file. 501 */ 502 void saveGroupDefinition(const String& filename); 503 /** Save the group data only in native form to a serializing stream. 504 */ 505 void saveGroupDefinition(StreamSerialiser& stream); 506 /** Load the group definition only in native form from a file. 507 */ 508 void loadGroupDefinition(const String& filename); 509 /** Load the group definition only in native form from a serializing stream. 510 */ 511 void loadGroupDefinition(StreamSerialiser& stream); 512 513 514 static const uint16 WORKQUEUE_LOAD_REQUEST; 515 static const uint32 CHUNK_ID; 516 static const uint16 CHUNK_VERSION; 517 518 /// Loads terrain's next LOD level. 519 void increaseLodLevel(long x, long y, bool synchronous = false); 520 /// Removes terrain's highest LOD level. 521 void decreaseLodLevel(long x, long y); 522 523 void setAutoUpdateLod(TerrainAutoUpdateLod* updater); 524 /// Automatically checks if terrain's LOD level needs to be updated. 525 void autoUpdateLod(long x, long y, bool synchronous, const Any &data); 526 void autoUpdateLodAll(bool synchronous, const Any &data); 527 528 /** Get the number of terrains that are still waiting for the Terrain::prepare() to be called. 529 * 530 * @note Terrain::prepare() happens in background thread so the actual call will be completed 531 * a bit before this returns the reduced number. 532 * 533 * @return Amount of terrain prepare requests pending. 534 */ 535 size_t getNumTerrainPrepareRequests() const; 536 537 protected: 538 typedef std::map<TerrainSlot*, WorkQueue::RequestID> TerrainPrepareRequestMap; 539 SceneManager *mSceneManager; 540 Terrain::Alignment mAlignment; 541 uint16 mTerrainSize; 542 Real mTerrainWorldSize; 543 Terrain::ImportData mDefaultImportData; 544 Vector3 mOrigin; 545 TerrainSlotMap mTerrainSlots; 546 TerrainPrepareRequestMap mTerrainPrepareRequests; 547 uint16 mWorkQueueChannel; 548 String mFilenamePrefix; 549 String mFilenameExtension; 550 String mResourceGroup; 551 TerrainAutoUpdateLod *mAutoUpdateLod; 552 Terrain::DefaultGpuBufferAllocator mBufferAllocator; 553 554 /// Get the position of a terrain instance 555 Vector3 getTerrainSlotPosition(long x, long y); 556 /// Retrieve a slot, potentially allocate one 557 TerrainSlot* getTerrainSlot(long x, long y, bool createIfMissing); 558 TerrainSlot* getTerrainSlot(long x, long y) const; 559 void freeTerrainSlotInstance(TerrainSlot* slot); 560 void connectNeighbour(TerrainSlot* slot, long offsetx, long offsety); 561 562 void loadTerrainImpl(TerrainSlot* slot, bool synchronous); 563 564 /// Structure for holding the load request 565 struct LoadRequest 566 { 567 TerrainSlot* slot; 568 TerrainGroup* origin; 569 _OgreTerrainExport friend std::ostream& operator<<(std::ostream& o, const LoadRequest& r) 570 { return o; } 571 }; 572 573 574 }; 575 576 577 /** @} */ 578 /** @} */ 579 580 } 581 582 #endif 583 584