diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index 264cdc242c..e55b9aab9a 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -33,6 +33,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunklodglobe.h ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunknode.h ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunkindex.h + ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunk.h ${CMAKE_CURRENT_SOURCE_DIR}/meshes/trianglesoup.h ${CMAKE_CURRENT_SOURCE_DIR}/meshes/grid.h @@ -66,6 +67,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunklodglobe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunknode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunkindex.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/globes/chunk.cpp ${CMAKE_CURRENT_SOURCE_DIR}/meshes/trianglesoup.cpp diff --git a/modules/globebrowsing/globes/chunk.cpp b/modules/globebrowsing/globes/chunk.cpp new file mode 100644 index 0000000000..3c3721d1da --- /dev/null +++ b/modules/globebrowsing/globes/chunk.cpp @@ -0,0 +1,129 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + + +#include + +#include + +#include +#include + + + +namespace { + const std::string _loggerCat = "Chunk"; +} + +namespace openspace { + + Chunk::Chunk(ChunkLodGlobe* owner, const ChunkIndex& chunkIndex) + : _owner(owner) + , _surfacePatch(chunkIndex) + , _index(chunkIndex) + , _isVisible(true) + { + + } + + const GeodeticPatch& Chunk::surfacePatch() const { + return _surfacePatch; + } + + ChunkLodGlobe* const Chunk::owner() const { + return _owner; + } + + const ChunkIndex Chunk::index() const { + return _index; + } + + bool Chunk::isVisible() const { + return _isVisible; + } + + void Chunk::setIndex(const ChunkIndex& index) { + _index = index; + _surfacePatch = GeodeticPatch(index); + } + + void Chunk::setOwner(ChunkLodGlobe* newOwner) { + _owner = newOwner; + } + + Chunk::Status Chunk::update(const RenderData& data) { + //Uses horizon culling, frustum culling and distance to camera to determine a + //desired level. + //In the current implementation of the horizon culling and the distance to the + //camera, the closer the ellipsoid is to a + //sphere, the better this will make the splitting. Using the minimum radius to + //be safe. This means that if the ellipsoid has high difference between radii, + //splitting might accur even though it is not needed. + + _isVisible = true; + Vec3 globePosition = data.position.dvec3(); + + auto center = _surfacePatch.center(); + auto ellipsoid = _owner->ellipsoid(); + Vec3 patchPosition = globePosition + ellipsoid.geodetic2ToCartesian(center); + + Vec3 cameraPosition = data.camera.position().dvec3(); + Vec3 cameraToChunk = patchPosition - cameraPosition; + Scalar minimumGlobeRadius = ellipsoid.minimumRadius(); + + + // Do horizon culling + const int maxHeight = 8700; // should be read from gdal dataset or mod file + if (!HorizonCuller::isVisible(data, _surfacePatch, ellipsoid, maxHeight)) { + _isVisible = false; + return WANT_MERGE; + } + + // Do frustum culling + if (!FrustumCuller::isVisible(data, _surfacePatch, ellipsoid)) { + _isVisible = false; + return WANT_MERGE; + } + + + // Calculate desired level based on distance + Scalar distance = glm::length(cameraToChunk); + _owner->minDistToCamera = fmin(_owner->minDistToCamera, distance); + + Scalar scaleFactor = 10 * minimumGlobeRadius; + Scalar projectedScaleFactor = scaleFactor / distance; + int desiredLevel = floor(log2(projectedScaleFactor)); + + // clamp level + desiredLevel = glm::clamp(desiredLevel, _owner->minSplitDepth, _owner->maxSplitDepth); + + if (desiredLevel < _index.level) return WANT_MERGE; + else if (_index.level < desiredLevel) return WANT_SPLIT; + else return DO_NOTHING; + } + + + + +} // namespace openspace diff --git a/modules/globebrowsing/globes/chunk.h b/modules/globebrowsing/globes/chunk.h new file mode 100644 index 0000000000..dd3bbf8ba1 --- /dev/null +++ b/modules/globebrowsing/globes/chunk.h @@ -0,0 +1,79 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __CHUNK_H__ +#define __CHUNK_H__ + +#include +#include +#include +#include + +#include + +#include +#include + + +namespace openspace { + + class ChunkLodGlobe; + + + class Chunk { + public: + + enum Status{ + DO_NOTHING, + WANT_MERGE, + WANT_SPLIT, + }; + + Chunk(ChunkLodGlobe* owner, const ChunkIndex& chunkIndex); + + /// Updates chunk internally and returns a desired level + Status update(const RenderData& data); + + const GeodeticPatch& surfacePatch() const; + ChunkLodGlobe* const owner() const; + const ChunkIndex index() const; + bool isVisible() const; + + void setIndex(const ChunkIndex& index); + void setOwner(ChunkLodGlobe* newOwner); + + + private: + ChunkLodGlobe* _owner; + ChunkIndex _index; + bool _isVisible; + GeodeticPatch _surfacePatch; + + }; + +} + + + +#endif // __CHUNK_H__ diff --git a/modules/globebrowsing/globes/chunklodglobe.cpp b/modules/globebrowsing/globes/chunklodglobe.cpp index 2bb0a3e7d6..613089e2d1 100644 --- a/modules/globebrowsing/globes/chunklodglobe.cpp +++ b/modules/globebrowsing/globes/chunklodglobe.cpp @@ -55,8 +55,8 @@ namespace openspace { const Ellipsoid& ellipsoid, std::shared_ptr tileProviderManager) : _ellipsoid(ellipsoid) - , _leftRoot(new ChunkNode(*this, LEFT_HEMISPHERE_INDEX)) - , _rightRoot(new ChunkNode(*this, RIGHT_HEMISPHERE_INDEX)) + , _leftRoot(new ChunkNode(Chunk(this, LEFT_HEMISPHERE_INDEX))) + , _rightRoot(new ChunkNode(Chunk(this, RIGHT_HEMISPHERE_INDEX))) , minSplitDepth(2) , maxSplitDepth(22) { @@ -67,7 +67,7 @@ namespace openspace { TriangleSoup::TextureCoordinates::Yes, TriangleSoup::Normals::No)); - _patchRenderer.reset(new LatLonPatchRenderer(geometry, tileProviderManager)); + _patchRenderer.reset(new ChunkRenderer(geometry, tileProviderManager)); } ChunkLodGlobe::~ChunkLodGlobe() { @@ -87,7 +87,7 @@ namespace openspace { return ready; } - LatLonPatchRenderer& ChunkLodGlobe::getPatchRenderer() { + ChunkRenderer& ChunkLodGlobe::getPatchRenderer() const{ return *_patchRenderer; } diff --git a/modules/globebrowsing/globes/chunklodglobe.h b/modules/globebrowsing/globes/chunklodglobe.h index b63932850e..c5ccc97be5 100644 --- a/modules/globebrowsing/globes/chunklodglobe.h +++ b/modules/globebrowsing/globes/chunklodglobe.h @@ -59,7 +59,7 @@ namespace openspace { std::shared_ptr tileProviderManager); ~ChunkLodGlobe(); - LatLonPatchRenderer& getPatchRenderer(); + ChunkRenderer& getPatchRenderer() const; bool initialize() override; bool deinitialize() override; @@ -85,7 +85,7 @@ namespace openspace { std::unique_ptr _rightRoot; // the patch used for actual rendering - std::unique_ptr _patchRenderer; + std::unique_ptr _patchRenderer; static const GeodeticPatch LEFT_HEMISPHERE; static const GeodeticPatch RIGHT_HEMISPHERE; diff --git a/modules/globebrowsing/globes/chunknode.cpp b/modules/globebrowsing/globes/chunknode.cpp index bd3657caa5..82a0b832b4 100644 --- a/modules/globebrowsing/globes/chunknode.cpp +++ b/modules/globebrowsing/globes/chunknode.cpp @@ -42,12 +42,9 @@ namespace openspace { int ChunkNode::instanceCount = 0; int ChunkNode::renderedPatches = 0; -ChunkNode::ChunkNode(ChunkLodGlobe& owner, const ChunkIndex& index, ChunkNode* parent) -: _owner(owner) -, _index(index) -, _patch(index) +ChunkNode::ChunkNode(const Chunk& chunk, ChunkNode* parent) +: _chunk(chunk) , _parent(parent) -, _isVisible(true) { _children[0] = nullptr; _children[1] = nullptr; @@ -79,26 +76,19 @@ void ChunkNode::render(const RenderData& data) { // Returns true or false wether this node can be merge or not bool ChunkNode::internalUpdateChunkTree(const RenderData& data) { - using namespace glm; - Geodetic2 center = _patch.center(); - + //Geodetic2 center = _chunk.surfacePatch.center(); //LDEBUG("x: " << patch.x << " y: " << patch.y << " level: " << patch.level << " lat: " << center.lat << " lon: " << center.lon); if (isLeaf()) { - - int desiredLevel = calculateDesiredLevelAndUpdateIsVisible(data, _index); - desiredLevel = glm::clamp(desiredLevel, _owner.minSplitDepth, _owner.maxSplitDepth); - if (desiredLevel > _index.level) { + Chunk::Status status = _chunk.update(data); + if (status == Chunk::WANT_SPLIT) { split(); } - else if(desiredLevel < _index.level){ - return true; // request a merge from parent - } - return false; + return status == Chunk::WANT_MERGE; } else { - int requestedMergeMask = 0; + char requestedMergeMask = 0; for (int i = 0; i < 4; ++i) { if (_children[i]->internalUpdateChunkTree(data)) { requestedMergeMask |= (1 << i); @@ -119,10 +109,11 @@ bool ChunkNode::internalUpdateChunkTree(const RenderData& data) { void ChunkNode::internalRender(const RenderData& data) { if (isLeaf()) { - if (_isVisible) { - LatLonPatchRenderer& patchRenderer = _owner.getPatchRenderer(); - - patchRenderer.renderPatch(_patch, data, _owner.ellipsoid(), _index); + if (_chunk.isVisible()) { + + ChunkRenderer& patchRenderer = _chunk.owner()->getPatchRenderer(); + patchRenderer.renderChunk(_chunk, _chunk.owner()->ellipsoid(), data); + //patchRenderer.renderPatch(_chunk.surfacePatch, data, _chunk.owner->ellipsoid(), _chunk.index); ChunkNode::renderedPatches++; } } @@ -133,90 +124,16 @@ void ChunkNode::internalRender(const RenderData& data) { } } -int ChunkNode::calculateDesiredLevelAndUpdateIsVisible( - const RenderData& data, - const ChunkIndex& traverseData) { - _isVisible = true; - Vec3 globePosition = data.position.dvec3(); - - Vec3 patchPosition = - globePosition + - _owner.ellipsoid().geodetic2ToCartesian(_patch.center()); - Vec3 cameraPosition = data.camera.position().dvec3(); - //Vec3 cameraDirection = Vec3(data.camera.viewDirection()); - Vec3 cameraToChunk = patchPosition - cameraPosition; - - Scalar minimumGlobeRadius = _owner.ellipsoid().minimumRadius(); - - /* - // if camera points at same direction as latlon patch normal, - // we see the back side and dont have to split it - //Scalar cosNormalCameraDirection = glm::dot(patchNormal, cameraDirection); - - Vec3 globeToCamera = cameraPosition - globePosition; - - Geodetic2 cameraPositionOnGlobe = - _owner.ellipsoid().cartesianToGeodetic2(globeToCamera); - Geodetic2 closestPatchPoint = _patch.closestPoint(cameraPositionOnGlobe); - - Vec3 normalOfClosestPatchPoint = - _owner.ellipsoid().geodeticSurfaceNormal(closestPatchPoint); - Scalar cosPatchNormalNormalizedGlobeToCamera = - glm::dot(normalOfClosestPatchPoint, glm::normalize(globeToCamera)); - - //LDEBUG(cosPatchNormalCameraDirection); - - // Get the minimum radius from the ellipsoid. The closer the ellipsoid is to a - // sphere, the better this will make the splitting. Using the minimum radius to - // be safe. This means that if the ellipsoid has high difference between radii, - // splitting might accur even though it is not needed. - Scalar minimumGlobeRadius = _owner.ellipsoid().minimumRadius(); - double cosAngleToHorizon = minimumGlobeRadius / glm::length(globeToCamera); - if (cosPatchNormalNormalizedGlobeToCamera < cosAngleToHorizon) { - _isVisible = false; - return traverseData.level - 1; - } - */ - - if (!HorizonCuller::isVisible( - data, - _patch, - _owner.ellipsoid(), - 8700)) - { - _isVisible = false; - return traverseData.level - 1; - } - - - // Do frustrum culling - //FrustumCuller& culler = _owner.getFrustumCuller(); - - if (!FrustumCuller::isVisible(data, _patch, _owner.ellipsoid())) { - _isVisible = false; - return traverseData.level - 1; - } - - - // Calculate desired level based on distance - Scalar distance = glm::length(cameraToChunk); - _owner.minDistToCamera = fmin(_owner.minDistToCamera, distance); - - Scalar scaleFactor = 10 * minimumGlobeRadius; - Scalar projectedScaleFactor = scaleFactor / distance; - int desiredLevel = floor( log2(projectedScaleFactor) ); - return desiredLevel; -} void ChunkNode::split(int depth) { if (depth > 0 && isLeaf()) { - auto childIndices = _index.childIndices(); + auto childIndices = _chunk.index().childIndices(); for (size_t i = 0; i < childIndices.size(); i++) { _children[i] = std::unique_ptr( - new ChunkNode(_owner, childIndices[i], this)); + new ChunkNode(Chunk(_chunk.owner(), childIndices[i]), this)); } } diff --git a/modules/globebrowsing/globes/chunknode.h b/modules/globebrowsing/globes/chunknode.h index b400451f06..936c4617ed 100644 --- a/modules/globebrowsing/globes/chunknode.h +++ b/modules/globebrowsing/globes/chunknode.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -46,9 +47,11 @@ namespace openspace { + + class ChunkNode { public: - ChunkNode(ChunkLodGlobe&, const ChunkIndex&, ChunkNode* parent = nullptr); + ChunkNode(const Chunk& chunk, ChunkNode* parent = nullptr); ~ChunkNode(); @@ -72,26 +75,13 @@ private: void internalRender(const RenderData& data); bool internalUpdateChunkTree(const RenderData& data); - /** - Uses horizon culling, frustum culling and distance to camera to determine a - desired level. - In the current implementation of the horizon culling and the distance to the - camera, the closer the ellipsoid is to a - sphere, the better this will make the splitting. Using the minimum radius to - be safe. This means that if the ellipsoid has high difference between radii, - splitting might accur even though it is not needed. - */ - int calculateDesiredLevelAndUpdateIsVisible( - const RenderData& data, - const ChunkIndex& traverseData); + ChunkNode* _parent; std::unique_ptr _children[4]; - ChunkLodGlobe& _owner; - GeodeticPatch _patch; - ChunkIndex _index; - bool _isVisible; + + Chunk _chunk; }; } // namespace openspace diff --git a/modules/globebrowsing/rendering/patchrenderer.cpp b/modules/globebrowsing/rendering/patchrenderer.cpp index 6cb87b448f..69e8c896b4 100644 --- a/modules/globebrowsing/rendering/patchrenderer.cpp +++ b/modules/globebrowsing/rendering/patchrenderer.cpp @@ -22,8 +22,8 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include // open space includes @@ -85,7 +85,7 @@ namespace openspace { ////////////////////////////////////////////////////////////////////////////////////// // LATLON PATCH RENDERER // ////////////////////////////////////////////////////////////////////////////////////// - LatLonPatchRenderer::LatLonPatchRenderer( + ChunkRenderer::ChunkRenderer( shared_ptr grid, shared_ptr tileProviderManager) : PatchRenderer(tileProviderManager) @@ -101,14 +101,13 @@ namespace openspace { _programObjectGlobalRendering->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); } - void LatLonPatchRenderer::renderPatch( - const GeodeticPatch& patch, - const RenderData& data, - const Ellipsoid& ellipsoid, - const ChunkIndex& chunkIndex) + + + void ChunkRenderer::renderChunk(const Chunk& chunk, const Ellipsoid& ellipsoid, + const RenderData& data) + { using namespace glm; - // TODO : Model transform should be fetched as a matrix directly. mat4 modelTransform = translate(mat4(1), data.position.vec3()); @@ -118,16 +117,16 @@ namespace openspace { // activate shader _programObjectGlobalRendering->activate(); - + // For now just pick the first one from height maps auto heightMapProviders = _tileProviderManager->heightMapProviders(); auto tileProviderHeight = heightMapProviders.begin()->second; // Get the textures that should be used for rendering - Tile heightTile = tileProviderHeight->getMostHiResTile(chunkIndex); + Tile heightTile = tileProviderHeight->getMostHiResTile(chunk.index()); + - // Bind and use the texture ghoul::opengl::TextureUnit texUnitHeight; texUnitHeight.activate(); @@ -135,15 +134,13 @@ namespace openspace { _programObjectGlobalRendering->setUniform("textureSamplerHeight", texUnitHeight); _programObjectGlobalRendering->setUniform("heightSamplingScale", heightTile.uvScale); _programObjectGlobalRendering->setUniform("heightSamplingOffset", heightTile.uvOffset); - - // Pick the first color texture auto colorTextureProviders = _tileProviderManager->colorTextureProviders(); auto tileProviderColor = colorTextureProviders.begin()->second; - Tile colorTile = tileProviderColor->getMostHiResTile(chunkIndex); + Tile colorTile = tileProviderColor->getMostHiResTile(chunk.index()); // Bind and use the texture @@ -155,8 +152,8 @@ namespace openspace { _programObjectGlobalRendering->setUniform("colorSamplingOffset", colorTile.uvOffset); - Geodetic2 swCorner = patch.southWestCorner(); - auto patchSize = patch.size(); + Geodetic2 swCorner = chunk.surfacePatch().southWestCorner(); + auto patchSize = chunk.surfacePatch().size(); _programObjectGlobalRendering->setUniform("modelViewProjectionTransform", modelViewProjectionTransform); _programObjectGlobalRendering->setUniform("minLatLon", vec2(swCorner.toLonLatVec2())); _programObjectGlobalRendering->setUniform("lonLatScalingFactor", vec2(patchSize.toLonLatVec2())); @@ -172,6 +169,8 @@ namespace openspace { // disable shader _programObjectGlobalRendering->deactivate(); } + + ////////////////////////////////////////////////////////////////////////////////////// // CLIPMAP PATCH RENDERER // diff --git a/modules/globebrowsing/rendering/patchrenderer.h b/modules/globebrowsing/rendering/patchrenderer.h index 03890b757d..cea4d048c2 100644 --- a/modules/globebrowsing/rendering/patchrenderer.h +++ b/modules/globebrowsing/rendering/patchrenderer.h @@ -39,7 +39,8 @@ #include #include #include -#include +#include + namespace ghoul { @@ -77,23 +78,22 @@ namespace openspace { // PATCH RENDERER SUBCLASSES // ////////////////////////////////////////////////////////////////////////////////////// - class LatLonPatchRenderer : public PatchRenderer { + class ChunkRenderer : public PatchRenderer { public: - LatLonPatchRenderer( - shared_ptr grid, - shared_ptr tileProviderManager); + ChunkRenderer(shared_ptr grid, + shared_ptr tileProviderManager); + + + void ChunkRenderer::renderChunk(const Chunk& chunk, const Ellipsoid& ellipsoid, const RenderData& data); - void renderPatch( - const GeodeticPatch& patch, - const RenderData& data, - const Ellipsoid& ellipsoid, - const ChunkIndex& chunkIndex); private: shared_ptr _grid; }; + + class ClipMapPatchRenderer : public PatchRenderer { public: ClipMapPatchRenderer(