1/* -*-c++-*- */
2/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
3* Copyright 2008-2013 Pelican Mapping
4* http://osgearth.org
5*
6* osgEarth is free software; you can redistribute it and/or modify
7* it under the terms of the GNU Lesser General Public License as published by
8* the Free Software Foundation; either version 2 of the License, or
9* (at your option) any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14* GNU Lesser General Public License for more details.
15*
16* You should have received a copy of the GNU Lesser General Public License
17* along with this program.  If not, see <http://www.gnu.org/licenses/>
18*/
19#ifndef OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_FACTORY
20#define OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_FACTORY 1
21
22#include "Common"
23#include "TileNode"
24#include "TileNodeRegistry"
25#include "QuadTreeTerrainEngineOptions"
26#include <osgEarth/Map>
27#include <osgEarth/ThreadingUtils>
28#include <osgEarth/Containers>
29#include <osgEarth/HeightFieldUtils>
30#include <osgEarth/MapFrame>
31#include <osgEarth/MapInfo>
32#include <osg/Group>
33
34namespace osgEarth_engine_quadtree
35{
36    using namespace osgEarth;
37
38    struct HFKey {
39        TileKey _key;
40        bool    _fallback;
41        bool    _convertToHAE;
42        ElevationSamplePolicy _samplePolicy;
43        bool operator < (const HFKey& rhs) const {
44            if ( _key < rhs._key ) return true;
45            if ( rhs._key < _key ) return false;
46            if ( _fallback != rhs._fallback ) return true;
47            if ( _convertToHAE != rhs._convertToHAE ) return true;
48            return _samplePolicy < rhs._samplePolicy;
49        }
50    };
51
52    struct HFValue {
53        osg::ref_ptr<osg::HeightField> _hf;
54        bool                           _isFallback;
55    };
56
57    class HeightFieldCache : public osg::Referenced, public Revisioned
58    {
59    public:
60        HeightFieldCache():
61          _cache    ( true, 128 )
62        {
63
64        }
65
66        bool getOrCreateHeightField(
67                const MapFrame&                 frame,
68                const TileKey&                  key,
69                bool                            fallback,
70                osg::ref_ptr<osg::HeightField>& out_hf,
71                bool*                           out_isFallback =0L,
72                bool                            convertToHAE   =true,
73                ElevationSamplePolicy           samplePolicy   =SAMPLE_FIRST_VALID,
74                ProgressCallback*               progress       =0L ) const
75        {
76            // check the quick cache.
77            HFKey cachekey;
78            cachekey._key          = key;
79            cachekey._fallback     = fallback;
80            cachekey._convertToHAE = convertToHAE;
81            cachekey._samplePolicy = samplePolicy;
82
83            bool hit = false;
84            LRUCache<HFKey,HFValue>::Record rec;
85            if ( _cache.get(cachekey, rec) )
86            {
87                out_hf = rec.value()._hf.get();
88                if ( out_isFallback )
89                    *out_isFallback = rec.value()._isFallback;
90                return true;
91            }
92
93            bool isFallback;
94
95            bool ok = frame.getHeightField( key, fallback, out_hf, &isFallback, convertToHAE, samplePolicy, progress );
96
97            if ( ok )
98            {
99                // Treat Plate Carre specially by scaling the height values. (There is no need
100                // to do this with an empty heightfield)
101                const MapInfo& mapInfo = frame.getMapInfo();
102                if ( mapInfo.isPlateCarre() )
103                {
104                    HeightFieldUtils::scaleHeightFieldToDegrees( out_hf.get() );
105                }
106
107                if ( out_isFallback )
108                    *out_isFallback = isFallback;
109
110                // cache me
111                HFValue cacheval;
112                cacheval._hf = out_hf.get();
113                cacheval._isFallback = isFallback;
114                _cache.insert( cachekey, cacheval );
115            }
116
117            return ok;
118        }
119
120        void clear()
121        {
122            _cache.clear();
123        }
124
125    private:
126        mutable LRUCache<HFKey,HFValue> _cache;
127    };
128
129    /**
130     * For a given TileKey, this class builds a a corresponding TileNode from
131     * the map's data model.
132     *
133     * TODO: This should probably change to TileModelFactory or something since
134     * the creation of the TileNode itself is trivial and can be done outside of
135     * this class.
136     */
137    class TileModelFactory : public osg::Referenced
138    {
139    public:
140        TileModelFactory(
141            const Map*                                   map,
142            TileNodeRegistry*                            liveTiles,
143            const Drivers::QuadTreeTerrainEngineOptions& terrainOptions );
144
145        HeightFieldCache* getHeightFieldCache() const;
146
147        /** dtor */
148        virtual ~TileModelFactory() { }
149
150        void createTileModel(
151            const TileKey&           key,
152            osg::ref_ptr<TileModel>& out_model,
153            bool&                    out_hasRealData,
154            bool&                    out_hasLodBlendedLayers );
155
156    private:
157
158        const Map*                                   _map;
159        osg::ref_ptr<TileNodeRegistry>               _liveTiles;
160        const Drivers::QuadTreeTerrainEngineOptions& _terrainOptions;
161        osg::ref_ptr< HeightFieldCache > _hfCache;
162    };
163
164} // namespace osgEarth_engine_quadtree
165
166#endif // OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_FACTORY
167