diff --git a/modules/globebrowsing/datastructures/chunknode.cpp b/modules/globebrowsing/datastructures/chunknode.cpp index 5f6a9f425d..91464fb788 100644 --- a/modules/globebrowsing/datastructures/chunknode.cpp +++ b/modules/globebrowsing/datastructures/chunknode.cpp @@ -76,7 +76,7 @@ void ChunkNode::render(const RenderData& data, ChunkIndex traverseData) { // Returns true or false wether this node can be merge or not bool ChunkNode::internalUpdateChunkTree(const RenderData& data, ChunkIndex& traverseData) { using namespace glm; - LatLon center = _patch.center; + LatLon center = _patch.center(); //LDEBUG("x: " << patch.x << " y: " << patch.y << " level: " << patch.level << " lat: " << center.lat << " lon: " << center.lon); @@ -117,6 +117,7 @@ bool ChunkNode::internalUpdateChunkTree(const RenderData& data, ChunkIndex& trav void ChunkNode::internalRender(const RenderData& data, ChunkIndex& traverseData) { if (isLeaf()) { PatchRenderer& patchRenderer = _owner.getPatchRenderer(); + patchRenderer.renderPatch(_patch, data, _owner.globeRadius); } else { @@ -130,7 +131,7 @@ void ChunkNode::internalRender(const RenderData& data, ChunkIndex& traverseData) int ChunkNode::calculateDesiredLevel(const RenderData& data, const ChunkIndex& traverseData) { - Vec3 patchNormal = _patch.center.asUnitCartesian(); + Vec3 patchNormal = _patch.center().asUnitCartesian(); Vec3 patchPosition = data.position.dvec3() + _owner.globeRadius * patchNormal; Vec3 cameraPosition = data.camera.position().dvec3(); @@ -146,6 +147,14 @@ int ChunkNode::calculateDesiredLevel(const RenderData& data, const ChunkIndex& t } + // Do frustrum culling + FrustrumCuller& culler = _owner.getFrustrumCuller(); + + if (!culler.isVisible(data, _patch, _owner.globeRadius)) { + return traverseData.level - 1; + } + + // Calculate desired level based on distance Scalar distance = glm::length(cameraToChunk); _owner.minDistToCamera = fmin(_owner.minDistToCamera, distance); @@ -161,8 +170,8 @@ void ChunkNode::split(int depth) { if (depth > 0 && isLeaf()) { // Defining short handles for center, halfSize and quarterSize - const LatLon& c = _patch.center; - const LatLon& hs = _patch.halfSize; + const LatLon& c = _patch.center(); + const LatLon& hs = _patch.halfSize(); LatLon qs = LatLon(0.5 * hs.lat, 0.5 * hs.lon); // Subdivide bounds diff --git a/modules/globebrowsing/datastructures/latlon.cpp b/modules/globebrowsing/datastructures/latlon.cpp index 4d9f37545d..1e66cc37c6 100644 --- a/modules/globebrowsing/datastructures/latlon.cpp +++ b/modules/globebrowsing/datastructures/latlon.cpp @@ -55,7 +55,7 @@ namespace openspace { } - Vec3 LatLon::asUnitCartesian() { + Vec3 LatLon::asUnitCartesian() const{ return Vec3( glm::cos(lat) * glm::cos(lon), glm::cos(lat) * glm::sin(lon), @@ -75,47 +75,70 @@ namespace openspace { ////////////////////////////////////////////////////////////////////////////////////////// LatLonPatch::LatLonPatch(Scalar centerLat, Scalar centerLon, Scalar halfSizeLat, Scalar halfSizeLon) - : center(LatLon(centerLat, centerLon)) - , halfSize(LatLon(halfSizeLat, halfSizeLon)) + : _center(LatLon(centerLat, centerLon)) + , _halfSize(LatLon(halfSizeLat, halfSizeLon)) { } LatLonPatch::LatLonPatch(const LatLon& center, const LatLon& halfSize) - : center(center) - , halfSize(halfSize) + : _center(center) + , _halfSize(halfSize) { } LatLonPatch::LatLonPatch(const LatLonPatch& patch) - : center(patch.center) - , halfSize(patch.halfSize) + : _center(patch._center) + , _halfSize(patch._halfSize) { } + + void LatLonPatch::setCenter(const LatLon& center) { + _center = center; + } + + void LatLonPatch::setHalfSize(const LatLon& halfSize) { + _halfSize = halfSize; + } + + Scalar LatLonPatch::minimalBoundingRadius() const { + const LatLon& cornerNearEquator = _center.lat > 0 ? southWestCorner() : northWestCorner(); + return glm::length(_center.asUnitCartesian() - cornerNearEquator.asUnitCartesian()); + } + Scalar LatLonPatch::unitArea() const { - Scalar deltaTheta = 2 * halfSize.lon; - Scalar phiMin = center.lat - halfSize.lat; - Scalar phiMax = center.lat + halfSize.lat; + Scalar deltaTheta = 2 * _halfSize.lon; + Scalar phiMin = _center.lat - _halfSize.lat; + Scalar phiMax = _center.lat + _halfSize.lat; return deltaTheta * (sin(phiMax) - sin(phiMin)); } + const LatLon& LatLonPatch::center() const { + return _center; + } + + const LatLon& LatLonPatch::halfSize() const { + return _halfSize; + } + + LatLon LatLonPatch::northWestCorner() const{ - return LatLon(center.lat + halfSize.lat, center.lon - halfSize.lon); + return LatLon(_center.lat + _halfSize.lat, _center.lon - _halfSize.lon); } LatLon LatLonPatch::northEastCorner() const{ - return LatLon(center.lat + halfSize.lat, center.lon + halfSize.lon); + return LatLon(_center.lat + _halfSize.lat, _center.lon + _halfSize.lon); } LatLon LatLonPatch::southWestCorner() const{ - return LatLon(center.lat - halfSize.lat, center.lon - halfSize.lon); + return LatLon(_center.lat - _halfSize.lat, _center.lon - _halfSize.lon); } LatLon LatLonPatch::southEastCorner() const{ - return LatLon(center.lat - halfSize.lat, center.lon + halfSize.lon); + return LatLon(_center.lat - _halfSize.lat, _center.lon + _halfSize.lon); } } // namespace openspace diff --git a/modules/globebrowsing/datastructures/latlon.h b/modules/globebrowsing/datastructures/latlon.h index 1999f8a0d0..2d4c034112 100644 --- a/modules/globebrowsing/datastructures/latlon.h +++ b/modules/globebrowsing/datastructures/latlon.h @@ -43,7 +43,7 @@ struct LatLon { LatLon(const LatLon& src); static LatLon fromCartesian(const Vec3& v); - Vec3 asUnitCartesian(); + Vec3 asUnitCartesian() const; Vec2 asVec2() const; inline bool operator==(const LatLon& other); @@ -53,21 +53,39 @@ struct LatLon { Scalar lon; }; -struct LatLonPatch { + + +class LatLonPatch { +public: LatLonPatch(Scalar, Scalar, Scalar, Scalar); LatLonPatch(const LatLon& center, const LatLon& halfSize); LatLonPatch(const LatLonPatch& patch); + + void setCenter(const LatLon&); + void setHalfSize(const LatLon&); + + + // Returns the minimal bounding radius that together with the LatLonPatch's + // center point represents a sphere in which the patch is completely contained + Scalar minimalBoundingRadius() const; + + // Returns the area of the patch with unit radius Scalar unitArea() const; + LatLon northWestCorner() const; LatLon northEastCorner() const; LatLon southWestCorner() const; LatLon southEastCorner() const; - LatLon center; - LatLon halfSize; + const LatLon& center() const; + const LatLon& halfSize() const; + +private: + LatLon _center; + LatLon _halfSize; }; diff --git a/modules/globebrowsing/rendering/chunklodglobe.cpp b/modules/globebrowsing/rendering/chunklodglobe.cpp index 3e88d3e076..7c2cbd17b4 100644 --- a/modules/globebrowsing/rendering/chunklodglobe.cpp +++ b/modules/globebrowsing/rendering/chunklodglobe.cpp @@ -87,8 +87,7 @@ namespace openspace { _patchRenderer.reset(new LatLonPatchRenderer(geometry)); - std::shared_ptr fc(new FrustrumCuller(1.3f)); - _patchRenderer->setFrustrumCuller(fc); + _frustrumCuller = std::shared_ptr(new FrustrumCuller()); } @@ -113,12 +112,18 @@ namespace openspace { return *_patchRenderer; } + FrustrumCuller& ChunkLodGlobe::getFrustrumCuller() { + return *_frustrumCuller; + } + + + void ChunkLodGlobe::render(const RenderData& data){ minDistToCamera = INFINITY; - PatchIndex leftRootTileIndex = { 0, 0, 1 }; + ChunkIndex leftRootTileIndex = { 0, 0, 1 }; _leftRoot->render(data, leftRootTileIndex); - PatchIndex rightRootTileIndex = { 1, 0, 1 }; + ChunkIndex rightRootTileIndex = { 1, 0, 1 }; _rightRoot->render(data, rightRootTileIndex); //LDEBUG("min distnace to camera: " << minDistToCamera); diff --git a/modules/globebrowsing/rendering/chunklodglobe.h b/modules/globebrowsing/rendering/chunklodglobe.h index 9b718513f7..bab0d747c0 100644 --- a/modules/globebrowsing/rendering/chunklodglobe.h +++ b/modules/globebrowsing/rendering/chunklodglobe.h @@ -57,6 +57,7 @@ namespace openspace { ~ChunkLodGlobe(); PatchRenderer& getPatchRenderer(); + FrustrumCuller& getFrustrumCuller(); bool initialize() override; bool deinitialize() override; @@ -81,6 +82,9 @@ namespace openspace { // Covers all positive longitudes std::unique_ptr _rightRoot; + // Frustrum culler + std::shared_ptr _frustrumCuller; + // the patch used for actual rendering std::unique_ptr _patchRenderer; diff --git a/modules/globebrowsing/rendering/clipmapglobe.cpp b/modules/globebrowsing/rendering/clipmapglobe.cpp index 952f6a4d7a..c97e0598b6 100644 --- a/modules/globebrowsing/rendering/clipmapglobe.cpp +++ b/modules/globebrowsing/rendering/clipmapglobe.cpp @@ -104,7 +104,7 @@ namespace openspace { // Set patches to follow camera for (size_t i = 0; i < _patches.size(); i++) { - _patches[i].center = LatLon::fromCartesian(data.camera.position().dvec3()); + _patches[i].setCenter(LatLon::fromCartesian(data.camera.position().dvec3())); } // render patches for (size_t i = 0; i < _patches.size(); i++) diff --git a/modules/globebrowsing/rendering/frustrumculler.cpp b/modules/globebrowsing/rendering/frustrumculler.cpp index 4d16e0ab6b..516cd89270 100644 --- a/modules/globebrowsing/rendering/frustrumculler.cpp +++ b/modules/globebrowsing/rendering/frustrumculler.cpp @@ -1,4 +1,4 @@ -/***************************************************************************************** + /***************************************************************************************** * * * OpenSpace * * * @@ -34,8 +34,7 @@ namespace openspace { ////////////////////////////////////////////////////////////////////////////////////// // PATCH RENDERER // ////////////////////////////////////////////////////////////////////////////////////// - FrustrumCuller::FrustrumCuller(float tolerance) - : _tolerance(tolerance) + FrustrumCuller::FrustrumCuller() { } @@ -45,23 +44,66 @@ namespace openspace { } - bool FrustrumCuller::isVisible(const vec3& point, const mat4x4& modelViewProjection) { - vec4 pointModelSpace(point, 1.0f); - vec4 pointProjectionSpace = modelViewProjection * pointModelSpace; - vec2 pointScreenSpace = (1.0f / pointProjectionSpace.w) * pointProjectionSpace.xy; - + bool FrustrumCuller::isVisible(const RenderData& data, const vec3& point, const glm::vec2& marginScreenSpace) { - // just for readability + mat4 modelTransform = translate(mat4(1), data.position.vec3()); + mat4 viewTransform = data.camera.combinedViewMatrix(); + mat4 modelViewProjectionTransform = data.camera.projectionMatrix() + * viewTransform * modelTransform; + + vec2 pointScreenSpace = transformToScreenSpace(point, modelViewProjectionTransform); + return testPoint(pointScreenSpace, marginScreenSpace); + } + + + bool FrustrumCuller::isVisible(const RenderData& data, const LatLonPatch& patch, double radius) { + // An axis aligned bounding box based on the patch's minimum boudning sphere is + // used for testnig + + // Calculate the MVP matrix + mat4 modelTransform = translate(mat4(1), data.position.vec3()); + mat4 viewTransform = data.camera.combinedViewMatrix(); + mat4 modelViewProjectionTransform = data.camera.projectionMatrix() + * viewTransform * modelTransform; + + // Calculate the patch's center point in screen space + vec4 patchCenterModelSpace = vec4(radius * patch.center().asUnitCartesian(), 1); + vec4 patchCenterProjectionSpace = modelViewProjectionTransform * patchCenterModelSpace; + vec2 pointScreenSpace = (1.0f / patchCenterProjectionSpace.w) * patchCenterProjectionSpace.xy; + + // Calculate the screen space margin that represents an axis aligned bounding + // box based on the patch's minimum boudning sphere + double boundingRadius = radius * patch.minimalBoundingRadius(); + vec4 marginProjectionSpace = vec4(vec3(boundingRadius), 0) * data.camera.projectionMatrix(); + vec2 marginScreenSpace = (1.0f / patchCenterProjectionSpace.w) * marginProjectionSpace.xy; + + // Test the bounding box by testing the center point and the corresponding margin + return testPoint(pointScreenSpace, marginScreenSpace); + } + + + + + + bool FrustrumCuller::testPoint(const glm::vec2& pointScreenSpace, + const glm::vec2& marginScreenSpace) const + { + const vec2& p = pointScreenSpace; - return ((-_tolerance < p.x && p.x < _tolerance) & - (-_tolerance < p.y && p.y < _tolerance)); - + + vec2 cullBounds = vec2(1) + marginScreenSpace; + return ((-cullBounds.x < p.x && p.x < cullBounds.x) && + (-cullBounds.y < p.y && p.y < cullBounds.y)); } - bool FrustrumCuller::isVisible(const LatLonPatch& patch, double radius, const mat4x4& modelViewProjection) { - return isVisible(radius * patch.northWestCorner().asUnitCartesian(), modelViewProjection) - || isVisible(radius * patch.northEastCorner().asUnitCartesian(), modelViewProjection) - || isVisible(radius * patch.southWestCorner().asUnitCartesian(), modelViewProjection) - || isVisible(radius * patch.southEastCorner().asUnitCartesian(), modelViewProjection); + + glm::vec2 FrustrumCuller::transformToScreenSpace(const vec3& point, + const mat4x4& modelViewProjection) const + { + vec4 pointProjectionSpace = modelViewProjection * vec4(point, 1.0f); + vec2 pointScreenSpace = (1.0f / pointProjectionSpace.w) * pointProjectionSpace.xy; + return pointScreenSpace; } + + } // namespace openspace diff --git a/modules/globebrowsing/rendering/frustrumculler.h b/modules/globebrowsing/rendering/frustrumculler.h index ca6e53ce14..74ac892202 100644 --- a/modules/globebrowsing/rendering/frustrumculler.h +++ b/modules/globebrowsing/rendering/frustrumculler.h @@ -45,15 +45,38 @@ namespace openspace { class FrustrumCuller { public: - FrustrumCuller(float tolerance = 1.0f); + FrustrumCuller(); ~FrustrumCuller(); - bool isVisible(const vec3& point, const mat4x4& modelViewProjection); - bool isVisible(const LatLonPatch& patch, double radius, const mat4x4& modelViewProjection); + /// Returns true if the point is inside the view frustrum defined in RenderData. + /// The third argument marginScreenSpace is added to the default screen space + /// boundaries. E.g. for marginScreenSpace = {0.2, 0.2}, all points with + /// 1.2 < x,y < 1.2 would cause isVisible to return true. + bool isVisible( + const RenderData& data, + const vec3& point, + const vec2& marginScreenSpace = vec2(0)); + + + /// Returns false if the patch element is guaranteed to be outside the view + /// frustrum, and true is the patch element MAY be inside the view frustrum. + bool isVisible( + const RenderData& data, + const LatLonPatch& patch, + double radius); - - protected: - float _tolerance; + private: + + + /// Returns true if the point in screen space is inside the view frustrum. + /// The optional screen space margin vector is used to resize area defining + /// what is considered to be inside the view frustrum. + bool testPoint( + const glm::vec2& pointScreenSpace, + const glm::vec2& marginScreenSpace) const; + + glm::vec2 transformToScreenSpace(const vec3& point, const mat4x4& modelViewProjection) const; + }; diff --git a/modules/globebrowsing/rendering/patchrenderer.cpp b/modules/globebrowsing/rendering/patchrenderer.cpp index 93fa3e8083..be5658be6d 100644 --- a/modules/globebrowsing/rendering/patchrenderer.cpp +++ b/modules/globebrowsing/rendering/patchrenderer.cpp @@ -67,9 +67,7 @@ namespace openspace { } } - void PatchRenderer::setFrustrumCuller(std::shared_ptr fc) { - _frustrumCuller = fc; - } + ////////////////////////////////////////////////////////////////////////////////////// // LATLON PATCH RENDERER // @@ -104,23 +102,10 @@ namespace openspace { * viewTransform * modelTransform; - // View frustrum culling - if (_frustrumCuller != nullptr) { - //LatLon center = patch.center; - //vec3 centerPoint = radius * center.asUnitCartesian(); - if (!_frustrumCuller->isVisible(patch, radius, modelViewProjectionTransform)) { - // dont render the patch - return; - } - } - - - - LatLon swCorner = patch.southWestCorner(); _programObject->setUniform("modelViewProjectionTransform", modelViewProjectionTransform); _programObject->setUniform("minLatLon", vec2(swCorner.lat, swCorner.lon)); - _programObject->setUniform("latLonScalingFactor", 2.0f * vec2(patch.halfSize.asVec2())); + _programObject->setUniform("latLonScalingFactor", 2.0f * vec2(patch.halfSize().asVec2())); _programObject->setUniform("globeRadius", float(radius)); glEnable(GL_DEPTH_TEST); @@ -157,25 +142,26 @@ namespace openspace { _programObject->activate(); using namespace glm; - const mat4& viewTransform = data.camera.combinedViewMatrix(); + mat4 viewTransform = data.camera.combinedViewMatrix(); // TODO : Model transform should be fetched as a matrix directly. mat4 modelTransform = translate(mat4(1), data.position.vec3()); // Snap patch position int segmentsPerPatch = 32; + LatLon halfSize = patch.halfSize(); LatLon stepSize = LatLon( - patch.halfSize.lat * 2 / segmentsPerPatch, - patch.halfSize.lon * 2 / segmentsPerPatch); + halfSize.lat * 2 / segmentsPerPatch, + halfSize.lon * 2 / segmentsPerPatch); ivec2 patchesToCoverGlobe = ivec2( - M_PI / (patch.halfSize.lat * 2) + 0.5, - M_PI * 2 / (patch.halfSize.lon * 2) + 0.5); + M_PI / (halfSize.lat * 2) + 0.5, + M_PI * 2 / (halfSize.lon * 2) + 0.5); ivec2 intSnapCoord = ivec2( - patch.center.lat / (M_PI * 2) * segmentsPerPatch * patchesToCoverGlobe.y, - patch.center.lon / (M_PI) * segmentsPerPatch * patchesToCoverGlobe.x); + patch.center().lat / (M_PI * 2) * segmentsPerPatch * patchesToCoverGlobe.y, + patch.center().lon / (M_PI) * segmentsPerPatch * patchesToCoverGlobe.x); LatLon swCorner = LatLon( - stepSize.lat * intSnapCoord.x - patch.halfSize.lat, - stepSize.lon * intSnapCoord.y - patch.halfSize.lon); + stepSize.lat * intSnapCoord.x - halfSize.lat, + stepSize.lon * intSnapCoord.y - halfSize.lon); ivec2 contraction = ivec2(intSnapCoord.y % 2, intSnapCoord.x % 2); @@ -185,7 +171,7 @@ namespace openspace { _programObject->setUniform("modelViewProjectionTransform", data.camera.projectionMatrix() * viewTransform * modelTransform); _programObject->setUniform("minLatLon", vec2(swCorner.lat, swCorner.lon)); - _programObject->setUniform("latLonScalingFactor", 2.0f * vec2(patch.halfSize.lat, patch.halfSize.lon)); + _programObject->setUniform("latLonScalingFactor", 2.0f * vec2(halfSize.lat, halfSize.lon)); _programObject->setUniform("globeRadius", float(radius)); _programObject->setUniform("contraction", contraction); diff --git a/modules/globebrowsing/rendering/patchrenderer.h b/modules/globebrowsing/rendering/patchrenderer.h index a1b04e1600..a00ea36ecf 100644 --- a/modules/globebrowsing/rendering/patchrenderer.h +++ b/modules/globebrowsing/rendering/patchrenderer.h @@ -65,7 +65,6 @@ namespace openspace { unique_ptr _programObject; shared_ptr _geometry; - shared_ptr _frustrumCuller; };