1/* -*-c++-*- */
2/* osgEarth - Geospatial SDK for OpenSceneGraph
3* Copyright 2008-2014 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_DRIVERS_REX_TERRAIN_ENGINE_GEOMETRY_POOL
20#define OSGEARTH_DRIVERS_REX_TERRAIN_ENGINE_GEOMETRY_POOL 1
21
22#include "Common"
23#include "MaskGenerator"
24#include "RexTerrainEngineOptions"
25#include <osgEarth/MapInfo>
26#include <osgEarth/TileKey>
27#include <osgEarth/ThreadingUtils>
28#include <osgEarth/ResourceReleaser>
29#include <osg/Geometry>
30
31#if OSG_MIN_VERSION_REQUIRED(3,5,9)
32#define SUPPORTS_VAO 1
33#endif
34
35namespace osgEarth { namespace Drivers { namespace RexTerrainEngine
36{
37    using namespace osgEarth;
38
39    // Adapted from osgTerrain shared geometry class.
40    class /*internal*/ SharedGeometry : public osg::Drawable
41    {
42    public:
43        SharedGeometry();
44
45        SharedGeometry(const SharedGeometry&, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
46
47        META_Node(osgEarthRex, SharedGeometry);
48
49        void setVertexArray(osg::Array* array) { _vertexArray = array; }
50        osg::Array* getVertexArray() { return _vertexArray.get(); }
51        const osg::Array* getVertexArray() const  { return _vertexArray.get(); }
52
53        void setNormalArray(osg::Array* array) { _normalArray = array; }
54        osg::Array* getNormalArray() { return _normalArray.get(); }
55        const osg::Array* getNormalArray() const { return _normalArray.get(); }
56
57        void setTexCoordArray(osg::Array* array) { _texcoordArray = array; }
58        osg::Array* getTexCoordArray() { return _texcoordArray.get(); }
59        const osg::Array* getTexCoordArray() const { return _texcoordArray.get(); }
60
61        void setNeighborArray(osg::Array* array) { _neighborArray = array; }
62        osg::Array* getNeighborArray() { return _neighborArray.get(); }
63        const osg::Array* getNeighborArray() const { return _neighborArray.get(); }
64
65        void setNeighborNormalArray(osg::Array* array) { _neighborNormalArray = array; }
66        osg::Array* getNeighborNormalArray() { return _neighborNormalArray.get(); }
67        const osg::Array* getNeighborNormalArray() const { return _neighborNormalArray.get(); }
68
69        void setDrawElements(osg::DrawElements* array) { _drawElements = array; }
70        osg::DrawElements* getDrawElements() { return _drawElements.get(); }
71        const osg::DrawElements* getDrawElements() const { return _drawElements.get(); }
72
73        void setMaskElements(osg::DrawElements* array) { _maskElements = array; }
74        osg::DrawElements* getMaskElements() { return _maskElements.get(); }
75        const osg::DrawElements* getMaskElements() const { return _maskElements.get(); }
76
77        // convert to a "real" geometry object
78        osg::Geometry* makeOsgGeometry();
79
80        // whether this geometry contains anything
81        bool empty() const;
82
83    public: // osg::Drawable
84
85#ifdef SUPPORTS_VAO
86        osg::VertexArrayState* createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const;
87#endif
88
89        void drawImplementation(osg::RenderInfo& renderInfo) const;
90
91        void resizeGLObjectBuffers(unsigned int maxSize);
92        void releaseGLObjects(osg::State* state) const;
93
94        bool supports(const osg::Drawable::AttributeFunctor&) const { return true; }
95        void accept(osg::Drawable::AttributeFunctor&);
96
97        bool supports(const osg::Drawable::ConstAttributeFunctor&) const { return true; }
98        void accept(osg::Drawable::ConstAttributeFunctor&) const;
99
100        bool supports(const osg::PrimitiveFunctor&) const { return true; }
101        void accept(osg::PrimitiveFunctor&) const;
102
103        bool supports(const osg::PrimitiveIndexFunctor&) const { return true; }
104        void accept(osg::PrimitiveIndexFunctor&) const;
105
106    protected:
107
108        virtual ~SharedGeometry();
109
110        osg::ref_ptr<osg::Array>        _vertexArray;
111        osg::ref_ptr<osg::Array>        _normalArray;
112        osg::ref_ptr<osg::Array>        _colorArray;
113        osg::ref_ptr<osg::Array>        _texcoordArray;
114        osg::ref_ptr<osg::Array>        _neighborArray;
115        osg::ref_ptr<osg::Array>        _neighborNormalArray;
116        osg::ref_ptr<osg::DrawElements> _drawElements;
117        osg::ref_ptr<osg::DrawElements> _maskElements;
118
119    private:
120
121        friend struct DrawTileCommand;
122        mutable osg::buffered_object<GLenum> _ptype;
123    };
124
125    /**
126     * Pool of terrain tile geometries.
127     *
128     * In a geocentric map, every tile at a particular LOD and a particular latitudinal
129     * (north-south) extent shares exactly the same geometry; each tile is just shifted
130     * and rotated differently. Therefore we can use the same Geometry for all tiles that
131     * share the same LOD and same min/max latitude in a geocentric map. In a projected
132     * map, all tiles at a given LOD share the same geometry regardless of extent, so eve
133     * more sharing is possible.
134     *
135     * This object creates and returns geometries based on TileKeys, sharing instances
136     * whenever possible. Concept adapted from OSG's osgTerrain::GeometryPool.
137     */
138    class GeometryPool : public osg::Group
139    {
140    public:
141        /** Construct the geometry pool */
142        GeometryPool(const RexTerrainEngineOptions& options);
143
144        /** Sets an object to release unused GL resources */
145        void setReleaser(ResourceReleaser* releaser);
146
147    public:
148        /**
149         * Hashtable key for unique (and therefore shareable) geometries.
150         */
151        struct GeometryKey
152        {
153            GeometryKey() :
154                lod(-1),
155                tileY(0),
156                patch(false),
157                size(0u)
158                {
159                }
160
161            bool operator < (const GeometryKey& rhs) const
162            {
163                if (lod < rhs.lod) return true;
164                if (lod > rhs.lod) return false;
165                if (tileY < rhs.tileY) return true;
166                if (tileY > rhs.tileY) return false;
167                if (size < rhs.size) return true;
168                if (size > rhs.size) return false;
169                if (patch == false && rhs.patch == true) return true;
170                return false;
171            }
172
173            int      lod;
174            int      tileY;
175            bool     patch;
176            unsigned size;
177        };
178
179        typedef std::map<GeometryKey, osg::ref_ptr<SharedGeometry> > GeometryMap;
180
181        /**
182         * Gets the Geometry associated with a tile key, creating a new one if
183         * necessary and storing it in the pool.
184         */
185        void getPooledGeometry(
186            const TileKey&               tileKey,
187            const MapInfo&               mapInfo,
188            unsigned                     tileSize,
189            MaskGenerator*               maskSet,
190            osg::ref_ptr<SharedGeometry>& out);
191
192        /**
193         * The number of elements (incides) in the terrain skirt, if applicable
194         */
195        int getNumSkirtElements(unsigned tileSize) const;
196
197        /**
198         * Are we doing pooling?
199         */
200        bool isEnabled() const { return _enabled; }
201
202        /**
203         * Clear and reset the pool.
204         */
205        void clear();
206
207
208    public: // osg::Node
209
210        /** Perform an update traversal to check for unused resources. */
211        void traverse(osg::NodeVisitor& nv);
212
213    protected:
214        virtual ~GeometryPool() { }
215
216        mutable Threading::Mutex       _geometryMapMutex;
217        GeometryMap                    _geometryMap;
218        //unsigned                       _tileSize;
219        const RexTerrainEngineOptions& _options;
220        osg::ref_ptr<ResourceReleaser> _releaser;
221
222        mutable osg::ref_ptr<osg::Vec3Array> _sharedTexCoords;
223
224        void createKeyForTileKey(
225            const TileKey& tileKey,
226            unsigned       size,
227            const MapInfo& mapInfo,
228            GeometryKey&   out) const;
229
230        SharedGeometry* createGeometry(
231            const TileKey& tileKey,
232            const MapInfo& mapInfo,
233            unsigned       tileSize,
234            MaskGenerator* maskSet ) const;
235
236        bool _enabled;
237        bool _debug;
238    };
239
240} } } // namespace osgEarth::Drivers::RexTerrainEngine
241
242#endif // OSGEARTH_DRIVERS_REX_TERRAIN_ENGINE_GEOMETRY_POOL
243