1 // Viewer.cxx -- alternative flightgear viewer application
3 // Copyright (C) 2009 - 2012 Mathias Froehlich
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include <osg/ArgumentParser>
26 #include <osg/ProxyNode>
27 #include <osg/PagedLOD>
28 #include <osgDB/ReadFile>
32 #include <osgViewer/api/X11/GraphicsWindowX11>
35 #include "MEncoderCaptureOperation.hxx"
39 Viewer::Viewer(osg::ArgumentParser& arguments) :
40 osgViewer::Viewer(arguments),
41 _sceneDataGroup(new osg::Group),
42 _timeIncrement(SGTimeStamp::fromSec(0)),
43 _simTime(SGTimeStamp::fromSec(0))
45 /// Careful: this method really assigns the sceneDataGroup to all cameras!
46 /// FIXME the 'useMasterScene' flag at the slave is able to get around that!!!
47 osgViewer::Viewer::setSceneData(_sceneDataGroup.get());
48 /// The only changed default that is renderer independent ...
49 getCamera()->setClearColor(osg::Vec4(0, 0, 0, 0));
57 if (_viewerFederate.valid())
58 _viewerFederate->shutdown();
64 Viewer::readCameraConfig(const SGPropertyNode& viewerNode)
66 // Collect and realize all windows
67 for (int i = 0; i < viewerNode.nChildren(); ++i) {
68 // FIXME support window, fullscreen, offscreen
69 const SGPropertyNode* windowNode = viewerNode.getChild(i);
70 if (!windowNode || windowNode->getNameString() != "window")
73 std::string name = windowNode->getStringValue("name", "");
75 SG_LOG(SG_VIEW, SG_ALERT, "Ignoring unnamed window!");
79 Drawable* drawable = getOrCreateDrawable(name);
81 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
82 screenIdentifier = getScreenIdentifier(windowNode->getStringValue("display", ""));
83 drawable->setScreenIdentifier(screenIdentifier.displayName());
85 if (windowNode->getBoolValue("fullscreen", false)) {
86 osg::GraphicsContext::ScreenSettings screenSettings;
87 screenSettings = getScreenSettings(screenIdentifier);
88 drawable->setPosition(SGVec2i(0, 0));
89 drawable->setSize(SGVec2i(screenSettings.width, screenSettings.height));
90 drawable->setFullscreen(true);
91 drawable->setOffscreen(false);
93 } else if (windowNode->getBoolValue("video", false)) {
94 drawable->setPosition(SGVec2i(0, 0));
96 size[0] = windowNode->getIntValue("geometry/width", 1366);
97 size[1] = windowNode->getIntValue("geometry/height", 768);
98 drawable->setSize(size);
99 drawable->setFullscreen(true);
100 drawable->setOffscreen(true);
102 std::string outputFile = windowNode->getStringValue("output-file", "fgviewer.avi");
103 unsigned fps = windowNode->getIntValue("frames-per-second", 30);
105 /// This is the case for the video writers, have a fixed time increment
106 _timeIncrement = SGTimeStamp::fromSec(1.0/fps);
108 MEncoderCaptureOperation* captureOperation;
109 captureOperation = new MEncoderCaptureOperation(outputFile, fps);
110 osgViewer::ScreenCaptureHandler* captureHandler;
111 captureHandler = new osgViewer::ScreenCaptureHandler(captureOperation, -1);
112 addEventHandler(captureHandler);
113 captureHandler->startCapture();
118 position[0] = windowNode->getIntValue("geometry/x", 0);
119 position[1] = windowNode->getIntValue("geometry/y", 0);
120 drawable->setPosition(position);
122 size[0] = windowNode->getIntValue("geometry/width", 1366);
123 size[1] = windowNode->getIntValue("geometry/height", 768);
124 drawable->setSize(size);
125 drawable->setFullscreen(false);
126 drawable->setOffscreen(false);
130 for (int i = 0; i < viewerNode.nChildren(); ++i) {
131 const SGPropertyNode* cameraNode = viewerNode.getChild(i);
132 if (!cameraNode || cameraNode->getNameString() != "camera")
135 std::string name = cameraNode->getStringValue("name", "");
137 SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration needs a name!");
141 SlaveCamera* slaveCamera = getOrCreateSlaveCamera(name);
143 std::string drawableName = cameraNode->getStringValue("window", "");
144 if (drawableName.empty()) {
145 SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration needs an assigned window!");
148 Drawable* drawable = getDrawable(drawableName);
150 SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration \"" << name << "\" needs a drawable configured!");
153 slaveCamera->setDrawableName(drawableName);
154 drawable->attachSlaveCamera(slaveCamera);
156 SGVec2i size = drawable->getSize();
157 SGVec4i viewport(0, 0, size[0], size[1]);
158 viewport[0] = cameraNode->getIntValue("viewport/x", viewport[0]);
159 viewport[1] = cameraNode->getIntValue("viewport/y", viewport[1]);
160 viewport[2] = cameraNode->getIntValue("viewport/width", viewport[2]);
161 viewport[3] = cameraNode->getIntValue("viewport/height", viewport[3]);
162 slaveCamera->setViewport(viewport);
164 double headingDeg = cameraNode->getDoubleValue("view-offset/heading-deg", 0);
165 double pitchDeg = cameraNode->getDoubleValue("view-offset/pitch-deg", 0);
166 double rollDeg = cameraNode->getDoubleValue("view-offset/roll-deg", 0);
167 slaveCamera->setViewOffsetDeg(headingDeg, pitchDeg, rollDeg);
169 // Care for the reference points
170 if (const SGPropertyNode* referencePointsNode = cameraNode->getNode("reference-points")) {
171 for (int j = 0; j < referencePointsNode->nChildren(); ++j) {
172 const SGPropertyNode* referencePointNode = cameraNode->getNode("reference-point");
173 if (!referencePointNode)
175 std::string name = referencePointNode->getStringValue("name", "");
179 point[0] = referencePointNode->getDoubleValue("x", 0);
180 point[1] = referencePointNode->getDoubleValue("y", 0);
181 slaveCamera->setProjectionReferencePoint(name, point);
184 // Define 4 reference points by monitor dimensions
185 else if (const SGPropertyNode* physicalDimensionsNode = cameraNode->getNode("physical-dimensions")) {
186 double physicalWidth = physicalDimensionsNode->getDoubleValue("width", viewport[2]);
187 double physicalHeight = physicalDimensionsNode->getDoubleValue("height", viewport[3]);
188 if (const SGPropertyNode* bezelNode = physicalDimensionsNode->getNode("bezel")) {
189 double bezelHeightTop = bezelNode->getDoubleValue("top", 0);
190 double bezelHeightBottom = bezelNode->getDoubleValue("bottom", 0);
191 double bezelWidthLeft = bezelNode->getDoubleValue("left", 0);
192 double bezelWidthRight = bezelNode->getDoubleValue("right", 0);
193 slaveCamera->setMonitorProjectionReferences(physicalWidth, physicalHeight,
194 bezelHeightTop, bezelHeightBottom,
195 bezelWidthLeft, bezelWidthRight);
199 // The frustum node takes precedence, as this is the most explicit one.
200 if (const SGPropertyNode* frustumNode = cameraNode->getNode("frustum")) {
201 Frustum frustum(slaveCamera->getAspectRatio());
202 frustum._near = frustumNode->getDoubleValue("near", frustum._near);
203 frustum._left = frustumNode->getDoubleValue("left", frustum._left);
204 frustum._right = frustumNode->getDoubleValue("right", frustum._right);
205 frustum._bottom = frustumNode->getDoubleValue("bottom", frustum._bottom);
206 frustum._top = frustumNode->getDoubleValue("top", frustum._top);
207 slaveCamera->setFrustum(frustum);
209 } else if (const SGPropertyNode* perspectiveNode = cameraNode->getNode("perspective")) {
210 double fieldOfViewDeg = perspectiveNode->getDoubleValue("field-of-view-deg", 55);
211 slaveCamera->setFustumByFieldOfViewDeg(fieldOfViewDeg);
213 } else if (const SGPropertyNode* monitorNode = cameraNode->getNode("monitor")) {
215 std::string referenceCameraName;
216 std::string names[2];
217 std::string referenceNames[2];
220 if (const SGPropertyNode* leftOfNode = monitorNode->getNode("left-of")) {
221 referenceCameraName = leftOfNode->getStringValue("");
222 names[0] = "lowerRight";
223 referenceNames[0] = "lowerLeft";
224 names[1] = "upperRight";
225 referenceNames[1] = "upperLeft";
226 } else if (const SGPropertyNode* rightOfNode = monitorNode->getNode("right-of")) {
227 referenceCameraName = rightOfNode->getStringValue("");
228 names[0] = "lowerLeft";
229 referenceNames[0] = "lowerRight";
230 names[1] = "upperLeft";
231 referenceNames[1] = "upperRight";
232 } else if (const SGPropertyNode* aboveNode = monitorNode->getNode("above")) {
233 referenceCameraName = aboveNode->getStringValue("");
234 names[0] = "lowerLeft";
235 referenceNames[0] = "upperLeft";
236 names[1] = "lowerRight";
237 referenceNames[1] = "upperRight";
238 } else if (const SGPropertyNode* belowNode = monitorNode->getNode("below")) {
239 referenceCameraName = belowNode->getStringValue("");
240 names[0] = "upperLeft";
241 referenceNames[0] = "lowerLeft";
242 names[1] = "upperRight";
243 referenceNames[1] = "lowerRight";
246 // referenceNames[0] = ;
248 // referenceNames[1] = ;
251 // If we finally found a set of reference points that should match,
252 // then create a relative frustum matching these references
253 if (SlaveCamera* referenceSlaveCamera = getSlaveCamera(referenceCameraName)) {
254 slaveCamera->setRelativeFrustum(names, *referenceSlaveCamera, referenceNames);
256 SG_LOG(SG_VIEW, SG_ALERT, "Unable to find reference camera \"" << referenceCameraName
257 << "\" for camera \"" << name << "\"!");
260 // Set a proper default taking the current aspect ratio into account
261 slaveCamera->setFustumByFieldOfViewDeg(55);
269 Viewer::setupDefaultCameraConfigIfUnset()
271 if (getNumDrawables() || getNumSlaveCameras())
274 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
275 screenIdentifier = getDefaultScreenIdentifier();
277 Drawable* drawable = getOrCreateDrawable("fgviewer");
278 drawable->setScreenIdentifier(screenIdentifier.displayName());
279 drawable->setPosition(SGVec2i(0, 0));
280 SGVec2i size(800, 600);
281 drawable->setSize(size);
282 drawable->setFullscreen(false);
283 drawable->setOffscreen(false);
285 SlaveCamera* slaveCamera = getOrCreateSlaveCamera(drawable->getName());
286 slaveCamera->setDrawableName(drawable->getName());
287 drawable->attachSlaveCamera(slaveCamera);
288 slaveCamera->setViewport(SGVec4i(0, 0, size[0], size[1]));
289 slaveCamera->setViewOffset(osg::Matrix::identity());
290 slaveCamera->setFustumByFieldOfViewDeg(55);
294 Viewer::readConfiguration(const std::string&)
300 Viewer::setRenderer(Renderer* renderer)
303 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setRenderer(): Setting the renderer to zero is not supported!");
306 if (_renderer.valid()) {
307 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setRenderer(): Setting the renderer twice is not supported!");
310 _renderer = renderer;
314 Viewer::getRenderer()
316 return _renderer.get();
320 Viewer::getOrCreateDrawable(const std::string& name)
322 Drawable* drawable = getDrawable(name);
325 if (!_renderer.valid())
327 drawable = _renderer->createDrawable(*this, name);
330 _drawableVector.push_back(drawable);
335 Viewer::getDrawable(const std::string& name)
337 return getDrawable(getDrawableIndex(name));
341 Viewer::getDrawableIndex(const std::string& name)
343 for (DrawableVector::size_type i = 0; i < _drawableVector.size(); ++i) {
344 if (_drawableVector[i]->getName() == name)
351 Viewer::getDrawable(unsigned index)
353 if (_drawableVector.size() <= index)
355 return _drawableVector[index].get();
359 Viewer::getNumDrawables() const
361 return _drawableVector.size();
365 Viewer::getOrCreateSlaveCamera(const std::string& name)
367 SlaveCamera* slaveCamera = getSlaveCamera(name);
370 if (!_renderer.valid())
372 slaveCamera = _renderer->createSlaveCamera(*this, name);
375 _slaveCameraVector.push_back(slaveCamera);
380 Viewer::getSlaveCamera(const std::string& name)
382 return getSlaveCamera(getSlaveCameraIndex(name));
386 Viewer::getSlaveCameraIndex(const std::string& name)
388 for (SlaveCameraVector::size_type i = 0; i < _slaveCameraVector.size(); ++i) {
389 if (_slaveCameraVector[i]->getName() == name)
396 Viewer::getSlaveCamera(unsigned index)
398 if (_slaveCameraVector.size() <= index)
400 return _slaveCameraVector[index].get();
404 Viewer::getNumSlaveCameras() const
406 return _slaveCameraVector.size();
415 if (!_renderer.valid())
418 // Setup a default config if there is none
419 setupDefaultCameraConfigIfUnset();
422 if (!_renderer->realize(*this)) {
423 SG_LOG(SG_VIEW, SG_ALERT, "Renderer::realize() failed!");
427 osgViewer::Viewer::realize();
431 Viewer::realizeDrawables()
433 for (DrawableVector::iterator i = _drawableVector.begin(); i != _drawableVector.end(); ++i) {
434 if (!(*i)->realize(*this)) {
435 SG_LOG(SG_VIEW, SG_ALERT, "Unable to realize drawable \"" << (*i)->getName() << "\"!");
444 Viewer::realizeSlaveCameras()
446 for (SlaveCameraVector::iterator i = _slaveCameraVector.begin(); i != _slaveCameraVector.end(); ++i) {
447 if (!(*i)->realize(*this)) {
448 SG_LOG(SG_VIEW, SG_ALERT, "Unable to realize camera \"" << (*i)->getName() << "\"!");
457 Viewer::advance(double)
459 if (_timeIncrement == SGTimeStamp::fromSec(0)) {
460 // Flightgears current scheme - could be improoved
461 _simTime = SGTimeStamp::now();
463 // Giving an explicit time increment makes sense in presence
464 // of the video capture where we need deterministic
465 // frame times and object positions for each picture.
466 _simTime += _timeIncrement;
469 // This sets the frame stamps simulation time to simTime
470 // and schedules a frame event
471 osgViewer::Viewer::advance(_simTime.toSecs());
475 Viewer::updateTraversal()
478 if (_viewerFederate.valid()) {
479 if (_timeIncrement == SGTimeStamp::fromSec(0)) {
480 if (!_viewerFederate->timeAdvanceAvailable()) {
481 SG_LOG(SG_NETWORK, SG_ALERT, "Got error from federate update!");
482 _viewerFederate->shutdown();
486 osg::FrameStamp* frameStamp = getViewerFrameStamp();
487 SGTimeStamp timeStamp = SGTimeStamp::fromSec(frameStamp->getSimulationTime());
488 if (!_viewerFederate->timeAdvance(timeStamp)) {
489 SG_LOG(SG_NETWORK, SG_ALERT, "Got error from federate update!");
490 _viewerFederate->shutdown();
497 osgViewer::Viewer::updateTraversal();
499 if (!_renderer->update(*this)) {
500 SG_LOG(SG_VIEW, SG_ALERT, "Renderer::update() failed!");
505 Viewer::updateSlaveCameras()
507 for (SlaveCameraVector::iterator i = _slaveCameraVector.begin(); i != _slaveCameraVector.end(); ++i) {
508 if (!(*i)->update(*this)) {
509 SG_LOG(SG_VIEW, SG_ALERT, "SlaveCamera::update() failed!");
517 Viewer::setReaderWriterOptions(simgear::SGReaderWriterOptions* readerWriterOptions)
519 _readerWriterOptions = readerWriterOptions;
522 simgear::SGReaderWriterOptions*
523 Viewer::getReaderWriterOptions()
525 return _readerWriterOptions.get();
529 Viewer::setSceneData(osg::Node* node)
531 _sceneDataGroup->removeChildren(0, _sceneDataGroup->getNumChildren());
532 insertSceneData(node);
536 Viewer::insertSceneData(osg::Node* node)
538 _sceneDataGroup->addChild(node);
542 Viewer::insertSceneData(const std::string& fileName, const osgDB::Options* options)
545 osg::ProxyNode* proxyNode = new osg::ProxyNode;
547 proxyNode->setDatabaseOptions(options->clone(osg::CopyOp()));
549 proxyNode->setDatabaseOptions(_readerWriterOptions->clone(osg::CopyOp()));
550 proxyNode->setFileName(0, fileName);
551 insertSceneData(proxyNode);
554 osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile(fileName, options);
557 insertSceneData(node.get());
563 Viewer::getSceneDataGroup()
565 return _sceneDataGroup.get();
568 class Viewer::_PurgeLevelOfDetailNodesVisitor : public osg::NodeVisitor {
570 _PurgeLevelOfDetailNodesVisitor() :
571 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
573 virtual ~_PurgeLevelOfDetailNodesVisitor()
576 virtual void apply(osg::ProxyNode& node)
578 for (unsigned i = 0; i < node.getNumChildren(); ++i) {
579 if (node.getFileName(i).empty())
581 node.removeChildren(i, node.getNumChildren() - i);
585 osg::NodeVisitor::apply(static_cast<osg::Group&>(node));
587 virtual void apply(osg::PagedLOD& node)
589 for (unsigned i = 0; i < node.getNumChildren(); ++i) {
590 if (node.getFileName(i).empty())
592 node.removeChildren(i, node.getNumChildren() - i);
596 osg::NodeVisitor::apply(static_cast<osg::Group&>(node));
601 Viewer::purgeLevelOfDetailNodes()
603 _PurgeLevelOfDetailNodesVisitor purgeLevelOfDetailNodesVisitor;
604 _sceneDataGroup->accept(purgeLevelOfDetailNodesVisitor);
607 osg::GraphicsContext::ScreenIdentifier
608 Viewer::getDefaultScreenIdentifier()
610 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
611 screenIdentifier.readDISPLAY();
612 if (screenIdentifier.displayNum < 0)
613 screenIdentifier.displayNum = 0;
614 if (screenIdentifier.screenNum < 0)
615 screenIdentifier.screenNum = 0;
616 return screenIdentifier;
619 osg::GraphicsContext::ScreenIdentifier
620 Viewer::getScreenIdentifier(const std::string& display)
622 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
623 screenIdentifier.setScreenIdentifier(display);
625 osg::GraphicsContext::ScreenIdentifier defaultScreenIdentifier;
626 defaultScreenIdentifier = getDefaultScreenIdentifier();
627 if (screenIdentifier.hostName.empty())
628 screenIdentifier.hostName = defaultScreenIdentifier.hostName;
629 if (screenIdentifier.displayNum < 0)
630 screenIdentifier.displayNum = defaultScreenIdentifier.displayNum;
631 if (screenIdentifier.screenNum < 0)
632 screenIdentifier.screenNum = defaultScreenIdentifier.screenNum;
634 return screenIdentifier;
637 osg::GraphicsContext::ScreenSettings
638 Viewer::getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier)
640 osg::GraphicsContext::ScreenSettings screenSettings;
642 osg::GraphicsContext::WindowingSystemInterface* wsi;
643 wsi = osg::GraphicsContext::getWindowingSystemInterface();
645 SG_LOG(SG_VIEW, SG_ALERT, "No windowing system interface defined!");
646 return screenSettings;
649 wsi->getScreenSettings(screenIdentifier, screenSettings);
650 return screenSettings;
653 osg::GraphicsContext::Traits*
654 Viewer::getTraits(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier)
656 osg::DisplaySettings* ds = _displaySettings.get();
658 ds = osg::DisplaySettings::instance().get();
660 osg::GraphicsContext::Traits* traits = new osg::GraphicsContext::Traits(ds);
662 traits->hostName = screenIdentifier.hostName;
663 traits->displayNum = screenIdentifier.displayNum;
664 traits->screenNum = screenIdentifier.screenNum;
666 // not seriously consider something different
667 traits->doubleBuffer = true;
669 osg::GraphicsContext::ScreenSettings screenSettings;
670 screenSettings = getScreenSettings(screenIdentifier);
674 traits->width = screenSettings.width;
675 traits->height = screenSettings.height;
681 class Viewer::_ResetScreenSaverSwapCallback : public osg::GraphicsContext::SwapCallback {
683 _ResetScreenSaverSwapCallback() :
684 _timeStamp(SGTimeStamp::fromSec(0))
687 virtual ~_ResetScreenSaverSwapCallback()
690 virtual void swapBuffersImplementation(osg::GraphicsContext* graphicsContext)
692 graphicsContext->swapBuffersImplementation();
694 // This callback must be attached to this type of graphics context
695 assert(dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsContext));
697 // Reset the screen saver every 10 seconds
698 SGTimeStamp timeStamp = SGTimeStamp::now();
699 if (timeStamp < _timeStamp)
701 _timeStamp = timeStamp + SGTimeStamp::fromSec(10);
702 // Obviously runs in the draw thread. Thus, use the draw display.
703 XResetScreenSaver(static_cast<osgViewer::GraphicsWindowX11*>(graphicsContext)->getDisplay());
706 SGTimeStamp _timeStamp;
710 osg::GraphicsContext*
711 Viewer::createGraphicsContext(osg::GraphicsContext::Traits* traits)
713 osg::GraphicsContext::WindowingSystemInterface* wsi;
714 wsi = osg::GraphicsContext::getWindowingSystemInterface();
716 SG_LOG(SG_VIEW, SG_ALERT, "No windowing system interface defined!");
720 osg::GraphicsContext* graphicsContext = wsi->createGraphicsContext(traits);
721 if (!graphicsContext) {
722 SG_LOG(SG_VIEW, SG_ALERT, "Unable to create window \"" << traits->windowName << "\"!");
727 if (dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsContext))
728 graphicsContext->setSwapCallback(new _ResetScreenSaverSwapCallback);
731 return graphicsContext;
735 const HLAViewerFederate*
736 Viewer::getViewerFederate() const
738 return _viewerFederate.get();
742 Viewer::getViewerFederate()
744 return _viewerFederate.get();
748 Viewer::setViewerFederate(HLAViewerFederate* viewerFederate)
750 if (!viewerFederate) {
751 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setViewerFederate(): Setting the viewer federate to zero is not supported!");
754 if (_viewerFederate.valid()) {
755 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setViewerFederate(): Setting the viewer federate twice is not supported!");
758 _viewerFederate = viewerFederate;
759 _viewerFederate->attachToViewer(this);
763 } // namespace fgviewer