Last active
November 12, 2023 14:33
-
-
Save gwaldron/bc7d57cbdd6b93c515aae026ab605384 to your computer and use it in GitHub Desktop.
Infinite Scroll - Multicamera Version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* -*-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