Skip to content

Instantly share code, notes, and snippets.

@gwaldron
Last active November 12, 2023 14:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gwaldron/bc7d57cbdd6b93c515aae026ab605384 to your computer and use it in GitHub Desktop.
Save gwaldron/bc7d57cbdd6b93c515aae026ab605384 to your computer and use it in GitHub Desktop.
Infinite Scroll - Multicamera Version
/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2018 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.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <osgViewer/CompositeViewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgGA/StateSetManipulator>
#include <osgEarth/Notify>
#include <osgEarth/MapNode>
#include <osgEarth/GLUtils>
#define LC "[infinitystone] "
using namespace osgEarth;
int
usage(const char* name)
{
return 0;
}
#define NUM_CAMERAS 4
#define OE_TEST OE_DEBUG
class MyManipulator : public osgGA::CameraManipulator
{
public:
MyManipulator()
{
_zoom = 1.0;
}
void setRegion(double x, double y, double width, double height)
{
_windowX = x, _windowY = y;
_windowWidth = width, _windowHeight = height;
}
void setExtent(const GeoExtent& extent)
{
_extent = extent;
_leftMapX = extent.xMin();
_bottomMapY = extent.yMin();
}
void install(osgViewer::CompositeViewer* viewer, osgViewer::View* parentView, osg::Node* sceneData)
{
osg::GraphicsContext* gc = 0L;
int width = _windowWidth, height = _windowHeight;
if (parentView)
{
width = parentView->getCamera()->getViewport()->width();
height = parentView->getCamera()->getViewport()->width();
}
osgGA::EventHandler* stats = new osgViewer::StatsHandler();
osgGA::EventHandler* ssmanip = new osgGA::StateSetManipulator(sceneData->getOrCreateStateSet());
for (int i = 0; i < NUM_CAMERAS; ++i)
{
_view[i] = new osgViewer::View();
_view[i]->setCameraManipulator(this);
_view[i]->setUpViewInWindow(10, 10, width, height); // so lazy
if (gc)
_view[i]->getCamera()->setGraphicsContext(gc);
else if (parentView)
_view[i]->getCamera()->setGraphicsContext(parentView->getCamera()->getGraphicsContext());
else
_view[i]->getCamera()->setGraphicsContext(_view[0]->getCamera()->getGraphicsContext());
_view[i]->getCamera()->setViewMatrixAsLookAt(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0));
_view[i]->setSceneData(sceneData);
_view[i]->addEventHandler(stats);
_view[i]->addEventHandler(ssmanip);
viewer->addView(_view[i]);
}
}
double wrapMap(double x)
{
if (x < _extent.xMin())
return _extent.xMax() - (_extent.xMin() - x);
else if (x >= _extent.xMax())
return _extent.xMin() + (x - _extent.xMax());
else
return x;
}
double metersToPixels()
{
return _zoom * _windowHeight / _extent.height();
}
double pixelsToMeters()
{
return 1.0 / metersToPixels();
}
double round(double x)
{
return ::ceil(x);
}
void update()
{
double arWindow = _windowWidth / _windowHeight;
double visibleExtentY = _windowHeight * pixelsToMeters();
double visibleExtentX = visibleExtentY * arWindow;
double ptrMapX = _leftMapX;
double ptrVPX = 0.0;
double southMap = _bottomMapY;
double northMap = southMap + visibleExtentY;
double coveredExtent = 0.0;
int vpx = 0;
for (int i = 0; i < NUM_CAMERAS; ++i)
{
osg::Camera* cam = _view[i]->getCamera();
if (coveredExtent >= visibleExtentX || ptrVPX >= _windowWidth)
{
cam->setNodeMask(0);
}
else
{
cam->setNodeMask(~0);
double westMap = wrapMap(ptrMapX);
double eastMap = westMap + _extent.width();
double nextWestMap = eastMap;
// clamp to date line:
if (eastMap > _extent.xMax())
{
eastMap = _extent.xMax();
nextWestMap = _extent.xMin();
}
double VPW = round(eastMap - westMap) * metersToPixels();
// clamp to region:
if (VPW > _windowWidth - ptrVPX)
{
VPW = round(_windowWidth - ptrVPX);
double widthMap = VPW * pixelsToMeters();
eastMap = westMap + widthMap;
nextWestMap = eastMap;
if (nextWestMap >= _extent.xMax() || osg::equivalent(nextWestMap, _extent.xMax(), metersToPixels()))
nextWestMap = _extent.xMin();
}
if (VPW >= 1)
{
cam->setViewport(_windowX + ptrVPX, _windowY, (int)VPW, _windowHeight);
cam->setProjectionMatrixAsOrtho2D(westMap, eastMap, southMap, northMap);
OE_TEST << "Cam " << i << " : " << (int)(_windowX + ptrVPX) << "=>" << (int)(VPW) << std::endl;
}
else
{
cam->setNodeMask(0);
}
ptrMapX = nextWestMap;
ptrVPX += (int)VPW;
coveredExtent += eastMap - westMap;
}
}
}
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
switch (ea.getEventType())
{
case ea.FRAME:
OE_TEST << "Frame " << aa.asView()->getFrameStamp()->getFrameNumber() << std::endl;
update();
OE_TEST << "--------------------------------------------" << std::endl;
break;
case ea.DRAG:
pushEvent(ea);
if (_evPrev.valid())
{
_leftMapX -= (_evCurr->getX() - _evPrev->getX()) * pixelsToMeters();
_leftMapX = wrapMap(_leftMapX);
_bottomMapY -= (_evCurr->getY() - _evPrev->getY()) * pixelsToMeters();
}
break;
case ea.MOVE:
pushEvent(ea);
break;
case ea.SCROLL:
{
double x0 = pixelsToMeters() * _windowWidth;
double y0 = pixelsToMeters() * _windowHeight;
if (ea.getScrollingMotion() == ea.SCROLL_DOWN)
_zoom *= 1.1;
else
_zoom = osg::maximum(_zoom * 0.9, 1.0);
double x1 = pixelsToMeters() * _windowWidth;
double y1 = pixelsToMeters() * _windowHeight;
_leftMapX += 0.5*(x0 - x1);
_bottomMapY += 0.5*(y0 - y1);
}
break;
case ea.RESIZE:
setRegion(_windowX, _windowY, ea.getWindowWidth(), ea.getWindowHeight());
break;
}
return false;
}
void setByMatrix(const osg::Matrix& m)
{
_m = m;
}
void setByInverseMatrix(const osg::Matrix& m)
{
setByMatrix(osg::Matrixd::inverse(m));
}
/** get the position of the manipulator as 4x4 Matrix.*/
osg::Matrixd getMatrix() const
{
return _m;
}
/** get the position of the manipulator as a inverse matrix of the manipulator, typically used as a model view matrix.*/
osg::Matrixd getInverseMatrix() const
{
return osg::Matrixd::inverse(_m);
}
private:
osg::ref_ptr<osgViewer::View> _view[NUM_CAMERAS];
GeoExtent _extent;
double _windowX, _windowY;
double _windowWidth, _windowHeight;
double _leftMapX;
double _bottomMapY;
osg::Matrix _m;
double _zoom;
osg::ref_ptr<const osgGA::GUIEventAdapter> _evCurr;
osg::ref_ptr<const osgGA::GUIEventAdapter> _evPrev;
void pushEvent(const osgGA::GUIEventAdapter& ea)
{
_evPrev = _evCurr;
_evCurr = &ea;
}
};
int
main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc, argv);
osgViewer::CompositeViewer viewer;
osg::ref_ptr<osg::Node> node = osgDB::readNodeFiles(arguments);
MapNode* mapNode = MapNode::get(node.get());
// configure terrain for 2D map view:
mapNode->getTerrainOptions().setRangeMode(osg::LOD::PIXEL_SIZE_ON_SCREEN);
mapNode->getTerrainOptions().setTileSize(3);
// open the map before getting an SRS
mapNode->open();
if (mapNode->getMapSRS()->isProjected() == false)
{
OE_WARN << "Must use projected map!" << std::endl;
return 0;
}
GLUtils::setGlobalDefaults(node->getOrCreateStateSet());
GeoExtent extent = mapNode->getMap()->getProfile()->getExtent();
MyManipulator* manip = new MyManipulator();
manip->setRegion(0, 0, 1920, 1080);
manip->setExtent(extent);
manip->install(&viewer, 0L, node.get());
return viewer.run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment