/* -*-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 */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osgEarth { //! Method to use when determining when to switch levels of detail. enum class LODMethod { CAMERA_DISTANCE = osg::LOD::DISTANCE_FROM_EYE_POINT, SCREEN_SPACE = osg::LOD::PIXEL_SIZE_ON_SCREEN }; namespace Util { /** * A customized CCC that works correctly under an RTT camera and also with * an orthographic projection matrix. */ class SuperClusterCullingCallback : public osg::ClusterCullingCallback { public: bool cull(osg::NodeVisitor* nv, osg::Drawable*, osg::State*) const; }; /** * Utility functions for creating cluster cullers */ class OSGEARTH_EXPORT ClusterCullingFactory { public: /** * Creates a cluster culling callback based on the data in a node graph. * NOTE! Never put a CCC somewhere where it will be under a transform. They * only work in absolute world space. */ static osg::NodeCallback* create(osg::Node* node, const osg::Vec3d& ecefControlPoint); /** * Same as above, but uses another method to compute the parameters. There should only * be one, but we need to investigate to see which is the better algorithm. Keeping this * for now since it works with the feature setup.. */ static osg::NodeCallback* create2(osg::Node* node, const osg::Vec3d& ecefControlPoint); /** * Creates a cluster culling callback and installs it on the node. If the node is * a transform, it will create a group above the transform and install the callback * on that group instead. */ static osg::Node* createAndInstall(osg::Node* node, const osg::Vec3d& ecefControlPoint); /** * Creates a cluster culling callback with the standard parameters. */ static osg::NodeCallback* create(const osg::Vec3& controlPoint, const osg::Vec3& normal, float deviation, float radius); /** * Creates a cluster culling callback based on an extent (for geocentric tiles). */ static osg::NodeCallback* create(const class GeoExtent& extent); }; /** * Simple occlusion culling callback that does a ray interseciton between the eyepoint * and a A GeoTransform node. */ struct OSGEARTH_EXPORT OcclusionCullingCallback : public osg::NodeCallback { OcclusionCullingCallback(GeoTransform* xform); /** Maximum eye altitude at which to perform the occlusion culling test */ double getMaxAltitude() const; void setMaxAltitude(double value); /** * Gets the maximum number of ms that the OcclusionCullingCallback can run on each frame. */ static double getMaxFrameTime(); /** * Sets the maximum number of ms that the OcclusionCullingCallback can run on each frame. * @param ms * The maximum number of milliseconds to run the OcclusionCullingCallback on each frame. */ static void setMaxFrameTime(double ms); public: //NodeCallback void operator()(osg::Node* node, osg::NodeVisitor* nv); private: osg::observer_ptr _xform; osg::Vec3d _prevEye; bool _visible; double _maxAltitude; static double _maxFrameTime; }; /** * Culling utilities. */ struct OSGEARTH_EXPORT Culling { static osgUtil::CullVisitor* asCullVisitor(osg::NodeVisitor* nv); static osgUtil::CullVisitor* asCullVisitor(osg::NodeVisitor& nv) { return asCullVisitor(&nv); } }; /** * Node Visitor that proxies the CullVisitor but uses a separate * frustum for bounds-culling. */ class ProxyCullVisitor : public osg::NodeVisitor, public osg::CullStack { private: osgUtil::CullVisitor* _cv; osg::Polytope _proxyFrustum; osg::Polytope _proxyProjFrustum; osg::Matrix _proxyModelViewMatrix; osg::Matrix _proxyProjMatrix; public: ProxyCullVisitor(osgUtil::CullVisitor* cv, const osg::Matrix& proj, const osg::Matrix& view); // access to the underlying cull visitor. osgUtil::CullVisitor* getCullVisitor() { return _cv; } bool isCulledByProxyFrustum(osg::Node& node); bool isCulledByProxyFrustum(const osg::BoundingBox& bbox); public: // proxy functions: osg::Vec3 getEyePoint() const; osg::Vec3 getViewPoint() const; float getDistanceToEyePoint(const osg::Vec3& pos, bool useLODScale) const; float getDistanceFromEyePoint(const osg::Vec3& pos, bool useLODScale) const; float getDistanceToViewPoint(const osg::Vec3& pos, bool useLODScale) const; protected: osgUtil::CullVisitor::value_type distance(const osg::Vec3& coord, const osg::Matrix& matrix); void handle_cull_callbacks_and_traverse(osg::Node& node); void apply(osg::Node& node); void apply(osg::Transform& xform); void apply(osg::Drawable& drawable); void apply(osg::LOD& lod); }; /** * Horizon culling in a shader program. */ class OSGEARTH_EXPORT HorizonCullingProgram { public: static void install(osg::StateSet* stateset); static void remove(osg::StateSet* stateset); }; /** * Group that lets you adjust the LOD scale on its children. */ class OSGEARTH_EXPORT LODScaleGroup : public osg::Group { public: LODScaleGroup(); /** * Factor by which to multiply the camera's LOD scale. */ void setLODScaleFactor(float value) { _scaleFactor = value; } float getLODScaleFactor() const { return _scaleFactor; } public: // osg::Group virtual void traverse(osg::NodeVisitor& nv); protected: virtual ~LODScaleGroup() { } private: float _scaleFactor; }; struct OSGEARTH_EXPORT CullDebugger { Config dumpRenderBin(osgUtil::RenderBin* bin) const; }; /** * Cull callback for cameras that sets the oe_Camera uniform. */ class OSGEARTH_EXPORT InstallCameraUniform : public osg::NodeCallback { public: META_Object(osgEarth, InstallCameraUniform); void operator()(osg::Node* node, osg::NodeVisitor* nv); InstallCameraUniform() { } protected: InstallCameraUniform(const InstallCameraUniform& rhs, const osg::CopyOp& copy) : osg::NodeCallback(rhs, copy) { } }; /** * Cull callback that limits visibility to an altitude range (HAE) */ class OSGEARTH_EXPORT AltitudeCullCallback : public osg::NodeCallback { public: optional& maxAltitude() { return _maxAltitude; } osg::ref_ptr& srs() { return _srs; } void operator()(osg::Node* node, osg::NodeVisitor* nv); private: optional _maxAltitude; osg::ref_ptr _srs; }; /** * Cull callback that will permit culling but will supress rendering. * We use this to toggle node visibility to that paged scene graphs * will continue to page in/out even when the layer is not visible. */ class OSGEARTH_EXPORT ToggleVisibleCullCallback : public osg::NodeCallback { public: void setVisible(bool value) { _visible = value; } bool getVisible() const { return _visible; } private: struct EmptyRenderBin : public osgUtil::RenderBin { EmptyRenderBin(osgUtil::RenderStage* stage) : osgUtil::RenderBin() { setName("OE_EMPTY_RENDER_BIN"); _stage = stage; } }; bool _visible = true; public: void operator()(osg::Node* node, osg::NodeVisitor* nv) override { if (_visible) { traverse(node, nv); } else { osgUtil::CullVisitor* cv = dynamic_cast(nv); osg::ref_ptr savedBin; osg::ref_ptr emptyStage; if (cv) { savedBin = cv->getCurrentRenderBin(); emptyStage = new EmptyRenderBin(savedBin->getStage()); cv->setCurrentRenderBin(emptyStage.get()); } traverse(node, nv); if (cv) { cv->setCurrentRenderBin(savedBin.get()); } } } }; } }