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