/* -*-c++-*- */ /* osgEarth - Geospatial SDK for OpenSceneGraph * Copyright 2020 Pelican Mapping * http://osgearth.org * * osgEarth is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #ifndef OSGEARTH_TDTILES_H #define OSGEARTH_TDTILES_H #include #include #include #include #include #include #include #include #include #include #include #include /** * 3D Tiles * https://github.com/AnalyticalGraphicsInc/3d-tiles * EXPERIMENTAL */ namespace osgEarth { namespace Contrib { namespace ThreeDTiles { using namespace osgEarth; struct LoadContext { URIContext _uc; }; class OSGEARTH_EXPORT Asset { OE_OPTION(std::string, version); OE_OPTION(std::string, tilesetVersion); OE_OPTION(std::string, gltfUpAxis); Asset() { } Asset(const Json::Value& value) { fromJSON(value); } void fromJSON(const Json::Value&); Json::Value getJSON() const; }; class OSGEARTH_EXPORT BoundingVolume { OE_OPTION(osg::BoundingBoxd, box); OE_OPTION(osg::BoundingBoxd, region); OE_OPTION(osg::BoundingSphere, sphere); BoundingVolume() { } BoundingVolume(const Json::Value& value) { fromJSON(value); } void fromJSON(const Json::Value&); Json::Value getJSON() const; osg::BoundingSphere asBoundingSphere() const; }; class OSGEARTH_EXPORT TileContent { OE_OPTION(BoundingVolume, boundingVolume); OE_OPTION(URI, uri); TileContent() { } TileContent(const Json::Value& value, LoadContext& uc) { fromJSON(value, uc); } void fromJSON(const Json::Value&, LoadContext&); Json::Value getJSON() const; }; class OSGEARTH_EXPORT Tile : public osg::Referenced { public: OE_OPTION(BoundingVolume, boundingVolume); OE_OPTION(BoundingVolume, viewerRequestVolume); OE_OPTION(double, geometricError); OE_OPTION(RefinePolicy, refine); OE_OPTION(osg::Matrix, transform); OE_OPTION(TileContent, content); OE_OPTION_VECTOR(osg::ref_ptr, children); Tile() : _refine(REFINE_ADD) { } Tile(const Json::Value& value, LoadContext& uc) { fromJSON(value, uc); } void fromJSON(const Json::Value&, LoadContext& uc); Json::Value getJSON() const; osg::BoundingSphere getBoundingSphere(); }; class OSGEARTH_EXPORT Tileset : public osg::Referenced { public: OE_OPTION(Asset, asset); OE_OPTION(BoundingVolume, boundingVolume); OE_OPTION(double, geometricError); OE_OPTION_REFPTR(Tile, root); Tileset() { } Tileset(const Json::Value& value, LoadContext& uc) { fromJSON(value, uc); } void fromJSON(const Json::Value&, LoadContext& uc); Json::Value getJSON() const; static Tileset* create(const std::string& tilesetJSON, const URIContext& uc); }; class ThreeDTilesetNode; class ThreeDTileNode; /** * Node that renders a 3D-Tiles content record */ class OSGEARTH_EXPORT ThreeDTilesetContentNode : public osg::Group { public: ThreeDTilesetContentNode(ThreeDTilesetNode* tilesetNode, Tileset* tileset, osgDB::Options* options); ThreeDTileNode* getTileNode(); private: ThreeDTileNode* _tileNode; ThreeDTilesetNode* _tilesetNode; osg::ref_ptr< Tileset > _tileset; osg::ref_ptr< osgDB::Options > _options; }; /** * Node that renders a 3D-Tiles Tile */ class OSGEARTH_EXPORT ThreeDTileNode : public osg::MatrixTransform, public LoadableNode { public: ThreeDTileNode(ThreeDTilesetNode* tileset, Tile* tile, bool immediateLoad, osgDB::Options* options); osg::BoundingSphere computeBound() const; bool hasContent(); osg::Node* getContent(); bool isContentReady(); void resolveContent(); void updateTracking(osgUtil::CullVisitor* cv); void requestContent(osgUtil::IncrementalCompileOperation* ico); double getDistanceToTile(osgUtil::CullVisitor* cv); double computeScreenSpaceError(osgUtil::CullVisitor* cv); void traverse(osg::NodeVisitor& nv); bool unloadContent(); const Tile* getTile() const { return _tile.get(); } RefinePolicy getRefinePolicy() const { return _refine; } unsigned int getLastCulledFrameNumber() const; float getLastCulledFrameTime() const; virtual void resizeGLObjectBuffers(unsigned int maxSize); virtual void releaseGLObjects(osg::State* state) const; typedef std::list< osg::ref_ptr < ThreeDTileNode > > TileTracker; TileTracker::iterator _trackerItr; bool _trackerItrValid; void setParentTile(ThreeDTileNode* parentTile); public: // LoadableNode void load() override { // Load the content for this tile and attempt to resolve it. requestContent(nullptr); resolveContent(); // If this tile has children we also need to load their content so this node is ready to subdivide if (_children.valid()) { for (unsigned int i = 0; i < _children->getNumChildren(); i++) { osg::ref_ptr< ThreeDTileNode > childTile = dynamic_cast(_children->getChild(i)); if (childTile.valid()) { if (childTile->hasContent() && !childTile->isContentReady()) { childTile->requestContent(nullptr); childTile->resolveContent(); } } } } } void unload() override { unloadContent(); } bool isHighestResolution() const override { return getNumChildren() == 0; } bool isLoadComplete() const override { auto t = const_cast(this); // Check to see if the content of this tile is loaded. bool isContentReady = false; if (t->hasContent()) { isContentReady = t->isContentReady(); } // If this tile has children, check to make sure it's content is loaded as well. This will allow this tile to subdivide property. bool areChildrenReady = true; if (_children.valid()) { for (unsigned int i = 0; i < _children->getNumChildren(); i++) { osg::ref_ptr< ThreeDTileNode > childTile = dynamic_cast(_children->getChild(i)); if (childTile.valid()) { if (childTile->hasContent() && !childTile->isContentReady()) { areChildrenReady = false; } } } } return areChildrenReady && isContentReady; } bool getAutoUnload() const override { return _autoUnload; } void setAutoUnload(bool value) override { _autoUnload = value; } private: void createDebugBounds(); void computeBoundingVolume(); osg::ref_ptr< Tile > _tile; osg::ref_ptr< osg::Node > _content; osg::ref_ptr< osg::Group > _children; osg::ref_ptr< osg::Node > _boundsDebug; ThreeDTilesetNode* _tileset; Threading::Future< osg::ref_ptr > _contentFuture; bool _requestedContent; bool _immediateLoad; bool _firstVisit; osg::BoundingBoxd _boundingBox; osg::Matrixd _boundingBoxLocalToWorld; osg::BoundingSphere _localBoundingSphere; osg::ref_ptr< osgDB::Options > _options; osg::Vec4 _debugColor; unsigned int _lastCulledFrameNumber; float _lastCulledFrameTime; bool _autoUnload = true; RefinePolicy _refine; osg::observer_ptr< ThreeDTileNode > _parentTile; }; /** * Node representing a 3D-Tiles Tileset. */ class OSGEARTH_EXPORT ThreeDTilesetNode : public osg::Group { public: ThreeDTilesetNode(Tileset* tileset, const std::string& authorizationHeader, SceneGraphCallbacks* sceneGraphCallbacks, osgDB::Options* options); float getMaximumScreenSpaceError() const; void setMaximumScreenSpaceError(float maximumScreenSpaceError); double getSSEDenominator() const; void touchTile(ThreeDTileNode* node); void traverse(osg::NodeVisitor& nv); const Tileset* getTileset() const { return _tileset.get(); } const osgDB::Options* getOptions() const { return _options.get(); } const std::string& getAuthorizationHeader() const { return _authorizationHeader; } void setAuthorizationHeader(const std::string& authorizationHeader) { _authorizationHeader = authorizationHeader; } //! Get the scene graph callback host SceneGraphCallbacks* getSceneGraphCallbacks(SceneGraphCallbacks* callbacks); //! Set the scene graph callback host void setSceneGraphCallbacks(SceneGraphCallbacks* callbacks); void runPreMergeOperations(osg::Node* node); void runPostMergeOperations(osg::Node* node); /** * Gets/sets the maximum number of tiles to keep in memory before expiring them. */ unsigned int getMaxTiles() const; void setMaxTiles(unsigned int maxTiles); /** * Gets/sets the max age of tiles before they are considered for expiration. */ float getMaxAge() const; void setMaxAge(float maxAge); /** * Turns on/off bounding volume visualization. */ bool getShowBoundingVolumes() const; void setShowBoundingVolumes(bool showBoundingVolumes); /** * Turns on/off rendering a debug color per tile. */ bool getColorPerTile() const; void setColorPerTile(bool colorPerTile); const std::string& getOwnerName() const; void setOwnerName(const std::string& name); private: void expireTiles(const osg::NodeVisitor& nv); osg::ref_ptr _tileset; osg::ref_ptr _options; float _maximumScreenSpaceError; mutable std::mutex _mutex; ThreeDTileNode::TileTracker _tracker; ThreeDTileNode::TileTracker::iterator _sentryItr; unsigned int _maxTiles; float _maxAge; bool _showBoundingVolumes; bool _showColorPerTile; osg::ref_ptr< VirtualProgram> _debugVP; unsigned int _lastExpiredFrame; double _sseDenominator; std::string _authorizationHeader; osg::ref_ptr _sgCallbacks; std::string _ownerName; }; } } } #endif // OSGEARTH_TDTILES_H