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"
41 Viewer::Viewer(osg::ArgumentParser& arguments) :
42 osgViewer::Viewer(arguments),
43 _sceneDataGroup(new osg::Group),
44 _timeIncrement(SGTimeStamp::fromSec(0)),
45 _simTime(SGTimeStamp::fromSec(0))
47 /// Careful: this method really assigns the sceneDataGroup to all cameras!
48 /// FIXME the 'useMasterScene' flag at the slave is able to get around that!!!
49 osgViewer::Viewer::setSceneData(_sceneDataGroup.get());
50 /// The only changed default that is renderer independent ...
51 getCamera()->setClearColor(osg::Vec4(0, 0, 0, 0));
59 if (_viewerFederate.valid())
60 _viewerFederate->shutdown();
66 Viewer::readCameraConfig(const SGPropertyNode& viewerNode)
68 // Collect and realize all windows
69 for (int i = 0; i < viewerNode.nChildren(); ++i) {
70 // FIXME support window, fullscreen, offscreen
71 const SGPropertyNode* windowNode = viewerNode.getChild(i);
72 if (!windowNode || windowNode->getNameString() != "window")
75 std::string name = windowNode->getStringValue("name", "");
77 SG_LOG(SG_VIEW, SG_ALERT, "Ignoring unnamed window!");
81 Drawable* drawable = getOrCreateDrawable(name);
83 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
84 screenIdentifier = getScreenIdentifier(windowNode->getStringValue("display", ""));
85 drawable->setScreenIdentifier(screenIdentifier.displayName());
87 if (windowNode->getBoolValue("fullscreen", false)) {
88 osg::GraphicsContext::ScreenSettings screenSettings;
89 screenSettings = getScreenSettings(screenIdentifier);
90 drawable->setPosition(SGVec2i(0, 0));
91 drawable->setSize(SGVec2i(screenSettings.width, screenSettings.height));
92 drawable->setFullscreen(true);
93 drawable->setOffscreen(false);
95 } else if (windowNode->getBoolValue("video", false)) {
96 drawable->setPosition(SGVec2i(0, 0));
98 size[0] = windowNode->getIntValue("geometry/width", 1366);
99 size[1] = windowNode->getIntValue("geometry/height", 768);
100 drawable->setSize(size);
101 drawable->setFullscreen(true);
102 drawable->setOffscreen(true);
104 std::string outputFile = windowNode->getStringValue("output-file", "fgviewer.avi");
105 unsigned fps = windowNode->getIntValue("frames-per-second", 30);
107 /// This is the case for the video writers, have a fixed time increment
108 _timeIncrement = SGTimeStamp::fromSec(1.0/fps);
110 MEncoderCaptureOperation* captureOperation;
111 captureOperation = new MEncoderCaptureOperation(outputFile, fps);
112 osgViewer::ScreenCaptureHandler* captureHandler;
113 captureHandler = new osgViewer::ScreenCaptureHandler(captureOperation, -1);
114 addEventHandler(captureHandler);
115 captureHandler->startCapture();
120 position[0] = windowNode->getIntValue("geometry/x", 0);
121 position[1] = windowNode->getIntValue("geometry/y", 0);
122 drawable->setPosition(position);
124 size[0] = windowNode->getIntValue("geometry/width", 1366);
125 size[1] = windowNode->getIntValue("geometry/height", 768);
126 drawable->setSize(size);
127 drawable->setFullscreen(false);
128 drawable->setOffscreen(false);
132 for (int i = 0; i < viewerNode.nChildren(); ++i) {
133 const SGPropertyNode* cameraNode = viewerNode.getChild(i);
134 if (!cameraNode || cameraNode->getNameString() != "camera")
137 std::string name = cameraNode->getStringValue("name", "");
139 SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration needs a name!");
143 SlaveCamera* slaveCamera = getOrCreateSlaveCamera(name);
145 std::string drawableName = cameraNode->getStringValue("window", "");
146 if (drawableName.empty()) {
147 SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration needs an assigned window!");
150 Drawable* drawable = getDrawable(drawableName);
152 SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration \"" << name << "\" needs a drawable configured!");
155 slaveCamera->setDrawableName(drawableName);
156 drawable->attachSlaveCamera(slaveCamera);
158 SGVec2i size = drawable->getSize();
159 SGVec4i viewport(0, 0, size[0], size[1]);
160 viewport[0] = cameraNode->getIntValue("viewport/x", viewport[0]);
161 viewport[1] = cameraNode->getIntValue("viewport/y", viewport[1]);
162 viewport[2] = cameraNode->getIntValue("viewport/width", viewport[2]);
163 viewport[3] = cameraNode->getIntValue("viewport/height", viewport[3]);
164 slaveCamera->setViewport(viewport);
166 double headingDeg = cameraNode->getDoubleValue("view-offset/heading-deg", 0);
167 double pitchDeg = cameraNode->getDoubleValue("view-offset/pitch-deg", 0);
168 double rollDeg = cameraNode->getDoubleValue("view-offset/roll-deg", 0);
169 slaveCamera->setViewOffsetDeg(headingDeg, pitchDeg, rollDeg);
171 // Care for the reference points
172 if (const SGPropertyNode* referencePointsNode = cameraNode->getNode("reference-points")) {
173 for (int j = 0; j < referencePointsNode->nChildren(); ++j) {
174 const SGPropertyNode* referencePointNode = cameraNode->getNode("reference-point");
175 if (!referencePointNode)
177 std::string name = referencePointNode->getStringValue("name", "");
181 point[0] = referencePointNode->getDoubleValue("x", 0);
182 point[1] = referencePointNode->getDoubleValue("y", 0);
183 slaveCamera->setProjectionReferencePoint(name, point);
186 // Define 4 reference points by monitor dimensions
187 else if (const SGPropertyNode* physicalDimensionsNode = cameraNode->getNode("physical-dimensions")) {
188 double physicalWidth = physicalDimensionsNode->getDoubleValue("width", viewport[2]);
189 double physicalHeight = physicalDimensionsNode->getDoubleValue("height", viewport[3]);
190 if (const SGPropertyNode* bezelNode = physicalDimensionsNode->getNode("bezel")) {
191 double bezelHeightTop = bezelNode->getDoubleValue("top", 0);
192 double bezelHeightBottom = bezelNode->getDoubleValue("bottom", 0);
193 double bezelWidthLeft = bezelNode->getDoubleValue("left", 0);
194 double bezelWidthRight = bezelNode->getDoubleValue("right", 0);
195 slaveCamera->setMonitorProjectionReferences(physicalWidth, physicalHeight,
196 bezelHeightTop, bezelHeightBottom,
197 bezelWidthLeft, bezelWidthRight);
201 // The frustum node takes precedence, as this is the most explicit one.
202 if (const SGPropertyNode* frustumNode = cameraNode->getNode("frustum")) {
203 Frustum frustum(slaveCamera->getAspectRatio());
204 frustum._near = frustumNode->getDoubleValue("near", frustum._near);
205 frustum._left = frustumNode->getDoubleValue("left", frustum._left);
206 frustum._right = frustumNode->getDoubleValue("right", frustum._right);
207 frustum._bottom = frustumNode->getDoubleValue("bottom", frustum._bottom);
208 frustum._top = frustumNode->getDoubleValue("top", frustum._top);
209 slaveCamera->setFrustum(frustum);
211 } else if (const SGPropertyNode* perspectiveNode = cameraNode->getNode("perspective")) {
212 double fieldOfViewDeg = perspectiveNode->getDoubleValue("field-of-view-deg", 55);
213 slaveCamera->setFustumByFieldOfViewDeg(fieldOfViewDeg);
215 } else if (const SGPropertyNode* monitorNode = cameraNode->getNode("monitor")) {
217 std::string referenceCameraName;
218 std::string names[2];
219 std::string referenceNames[2];
222 if (const SGPropertyNode* leftOfNode = monitorNode->getNode("left-of")) {
223 referenceCameraName = leftOfNode->getStringValue("");
224 names[0] = "lowerRight";
225 referenceNames[0] = "lowerLeft";
226 names[1] = "upperRight";
227 referenceNames[1] = "upperLeft";
228 } else if (const SGPropertyNode* rightOfNode = monitorNode->getNode("right-of")) {
229 referenceCameraName = rightOfNode->getStringValue("");
230 names[0] = "lowerLeft";
231 referenceNames[0] = "lowerRight";
232 names[1] = "upperLeft";
233 referenceNames[1] = "upperRight";
234 } else if (const SGPropertyNode* aboveNode = monitorNode->getNode("above")) {
235 referenceCameraName = aboveNode->getStringValue("");
236 names[0] = "lowerLeft";
237 referenceNames[0] = "upperLeft";
238 names[1] = "lowerRight";
239 referenceNames[1] = "upperRight";
240 } else if (const SGPropertyNode* belowNode = monitorNode->getNode("below")) {
241 referenceCameraName = belowNode->getStringValue("");
242 names[0] = "upperLeft";
243 referenceNames[0] = "lowerLeft";
244 names[1] = "upperRight";
245 referenceNames[1] = "lowerRight";
248 // referenceNames[0] = ;
250 // referenceNames[1] = ;
253 // If we finally found a set of reference points that should match,
254 // then create a relative frustum matching these references
255 if (SlaveCamera* referenceSlaveCamera = getSlaveCamera(referenceCameraName)) {
256 slaveCamera->setRelativeFrustum(names, *referenceSlaveCamera, referenceNames);
258 SG_LOG(SG_VIEW, SG_ALERT, "Unable to find reference camera \"" << referenceCameraName
259 << "\" for camera \"" << name << "\"!");
262 // Set a proper default taking the current aspect ratio into account
263 slaveCamera->setFustumByFieldOfViewDeg(55);
271 Viewer::setupDefaultCameraConfigIfUnset()
273 if (getNumDrawables() || getNumSlaveCameras())
276 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
277 screenIdentifier = getDefaultScreenIdentifier();
279 Drawable* drawable = getOrCreateDrawable("fgviewer");
280 drawable->setScreenIdentifier(screenIdentifier.displayName());
281 drawable->setPosition(SGVec2i(0, 0));
282 SGVec2i size(800, 600);
283 drawable->setSize(size);
284 drawable->setFullscreen(false);
285 drawable->setOffscreen(false);
287 SlaveCamera* slaveCamera = getOrCreateSlaveCamera(drawable->getName());
288 slaveCamera->setDrawableName(drawable->getName());
289 drawable->attachSlaveCamera(slaveCamera);
290 slaveCamera->setViewport(SGVec4i(0, 0, size[0], size[1]));
291 slaveCamera->setViewOffset(osg::Matrix::identity());
292 slaveCamera->setFustumByFieldOfViewDeg(55);
296 Viewer::readConfiguration(const std::string&)
302 Viewer::setRenderer(Renderer* renderer)
305 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setRenderer(): Setting the renderer to zero is not supported!");
308 if (_renderer.valid()) {
309 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setRenderer(): Setting the renderer twice is not supported!");
312 _renderer = renderer;
316 Viewer::getRenderer()
318 return _renderer.get();
322 Viewer::getOrCreateDrawable(const std::string& name)
324 Drawable* drawable = getDrawable(name);
327 if (!_renderer.valid())
329 drawable = _renderer->createDrawable(*this, name);
332 _drawableVector.push_back(drawable);
337 Viewer::getDrawable(const std::string& name)
339 return getDrawable(getDrawableIndex(name));
343 Viewer::getDrawableIndex(const std::string& name)
345 for (DrawableVector::size_type i = 0; i < _drawableVector.size(); ++i) {
346 if (_drawableVector[i]->getName() == name)
353 Viewer::getDrawable(unsigned index)
355 if (_drawableVector.size() <= index)
357 return _drawableVector[index].get();
361 Viewer::getNumDrawables() const
363 return _drawableVector.size();
367 Viewer::getOrCreateSlaveCamera(const std::string& name)
369 SlaveCamera* slaveCamera = getSlaveCamera(name);
372 if (!_renderer.valid())
374 slaveCamera = _renderer->createSlaveCamera(*this, name);
377 _slaveCameraVector.push_back(slaveCamera);
382 Viewer::getSlaveCamera(const std::string& name)
384 return getSlaveCamera(getSlaveCameraIndex(name));
388 Viewer::getSlaveCameraIndex(const std::string& name)
390 for (SlaveCameraVector::size_type i = 0; i < _slaveCameraVector.size(); ++i) {
391 if (_slaveCameraVector[i]->getName() == name)
398 Viewer::getSlaveCamera(unsigned index)
400 if (_slaveCameraVector.size() <= index)
402 return _slaveCameraVector[index].get();
406 Viewer::getNumSlaveCameras() const
408 return _slaveCameraVector.size();
417 if (!_renderer.valid())
420 // Setup a default config if there is none
421 setupDefaultCameraConfigIfUnset();
424 if (!_renderer->realize(*this)) {
425 SG_LOG(SG_VIEW, SG_ALERT, "Renderer::realize() failed!");
429 osgViewer::Viewer::realize();
433 Viewer::realizeDrawables()
435 for (DrawableVector::iterator i = _drawableVector.begin(); i != _drawableVector.end(); ++i) {
436 if (!(*i)->realize(*this)) {
437 SG_LOG(SG_VIEW, SG_ALERT, "Unable to realize drawable \"" << (*i)->getName() << "\"!");
446 Viewer::realizeSlaveCameras()
448 for (SlaveCameraVector::iterator i = _slaveCameraVector.begin(); i != _slaveCameraVector.end(); ++i) {
449 if (!(*i)->realize(*this)) {
450 SG_LOG(SG_VIEW, SG_ALERT, "Unable to realize camera \"" << (*i)->getName() << "\"!");
459 Viewer::advance(double)
461 if (_timeIncrement == SGTimeStamp::fromSec(0)) {
462 // Flightgears current scheme - could be improoved
463 _simTime = SGTimeStamp::now();
465 // Giving an explicit time increment makes sense in presence
466 // of the video capture where we need deterministic
467 // frame times and object positions for each picture.
468 _simTime += _timeIncrement;
471 // This sets the frame stamps simulation time to simTime
472 // and schedules a frame event
473 osgViewer::Viewer::advance(_simTime.toSecs());
477 Viewer::updateTraversal()
480 if (_viewerFederate.valid()) {
481 if (_timeIncrement == SGTimeStamp::fromSec(0)) {
482 if (!_viewerFederate->timeAdvanceAvailable()) {
483 SG_LOG(SG_NETWORK, SG_ALERT, "Got error from federate update!");
484 _viewerFederate->shutdown();
488 osg::FrameStamp* frameStamp = getViewerFrameStamp();
489 SGTimeStamp timeStamp = SGTimeStamp::fromSec(frameStamp->getSimulationTime());
490 if (!_viewerFederate->timeAdvance(timeStamp)) {
491 SG_LOG(SG_NETWORK, SG_ALERT, "Got error from federate update!");
492 _viewerFederate->shutdown();
499 osgViewer::Viewer::updateTraversal();
501 if (!_renderer->update(*this)) {
502 SG_LOG(SG_VIEW, SG_ALERT, "Renderer::update() failed!");
507 Viewer::updateSlaveCameras()
509 for (SlaveCameraVector::iterator i = _slaveCameraVector.begin(); i != _slaveCameraVector.end(); ++i) {
510 if (!(*i)->update(*this)) {
511 SG_LOG(SG_VIEW, SG_ALERT, "SlaveCamera::update() failed!");
519 Viewer::setReaderWriterOptions(simgear::SGReaderWriterOptions* readerWriterOptions)
521 _readerWriterOptions = readerWriterOptions;
524 simgear::SGReaderWriterOptions*
525 Viewer::getReaderWriterOptions()
527 return _readerWriterOptions.get();
531 Viewer::setSceneData(osg::Node* node)
533 _sceneDataGroup->removeChildren(0, _sceneDataGroup->getNumChildren());
534 insertSceneData(node);
538 Viewer::insertSceneData(osg::Node* node)
540 _sceneDataGroup->addChild(node);
544 Viewer::insertSceneData(const std::string& fileName, const osgDB::Options* options)
547 osg::ProxyNode* proxyNode = new osg::ProxyNode;
549 proxyNode->setDatabaseOptions(options->clone(osg::CopyOp()));
551 proxyNode->setDatabaseOptions(_readerWriterOptions->clone(osg::CopyOp()));
552 proxyNode->setFileName(0, fileName);
553 insertSceneData(proxyNode);
556 osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile(fileName, options);
559 insertSceneData(node.get());
565 Viewer::getSceneDataGroup()
567 return _sceneDataGroup.get();
570 class Viewer::_PurgeLevelOfDetailNodesVisitor : public osg::NodeVisitor {
572 _PurgeLevelOfDetailNodesVisitor() :
573 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
575 virtual ~_PurgeLevelOfDetailNodesVisitor()
578 virtual void apply(osg::ProxyNode& node)
580 for (unsigned i = 0; i < node.getNumChildren(); ++i) {
581 if (node.getFileName(i).empty())
583 node.removeChildren(i, node.getNumChildren() - i);
587 osg::NodeVisitor::apply(static_cast<osg::Group&>(node));
589 virtual void apply(osg::PagedLOD& node)
591 for (unsigned i = 0; i < node.getNumChildren(); ++i) {
592 if (node.getFileName(i).empty())
594 node.removeChildren(i, node.getNumChildren() - i);
598 osg::NodeVisitor::apply(static_cast<osg::Group&>(node));
603 Viewer::purgeLevelOfDetailNodes()
605 _PurgeLevelOfDetailNodesVisitor purgeLevelOfDetailNodesVisitor;
606 _sceneDataGroup->accept(purgeLevelOfDetailNodesVisitor);
609 osg::GraphicsContext::ScreenIdentifier
610 Viewer::getDefaultScreenIdentifier()
612 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
613 screenIdentifier.readDISPLAY();
614 if (screenIdentifier.displayNum < 0)
615 screenIdentifier.displayNum = 0;
616 if (screenIdentifier.screenNum < 0)
617 screenIdentifier.screenNum = 0;
618 return screenIdentifier;
621 osg::GraphicsContext::ScreenIdentifier
622 Viewer::getScreenIdentifier(const std::string& display)
624 osg::GraphicsContext::ScreenIdentifier screenIdentifier;
625 screenIdentifier.setScreenIdentifier(display);
627 osg::GraphicsContext::ScreenIdentifier defaultScreenIdentifier;
628 defaultScreenIdentifier = getDefaultScreenIdentifier();
629 if (screenIdentifier.hostName.empty())
630 screenIdentifier.hostName = defaultScreenIdentifier.hostName;
631 if (screenIdentifier.displayNum < 0)
632 screenIdentifier.displayNum = defaultScreenIdentifier.displayNum;
633 if (screenIdentifier.screenNum < 0)
634 screenIdentifier.screenNum = defaultScreenIdentifier.screenNum;
636 return screenIdentifier;
639 osg::GraphicsContext::ScreenSettings
640 Viewer::getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier)
642 osg::GraphicsContext::ScreenSettings screenSettings;
644 osg::GraphicsContext::WindowingSystemInterface* wsi;
645 wsi = osg::GraphicsContext::getWindowingSystemInterface();
647 SG_LOG(SG_VIEW, SG_ALERT, "No windowing system interface defined!");
648 return screenSettings;
651 wsi->getScreenSettings(screenIdentifier, screenSettings);
652 return screenSettings;
655 osg::GraphicsContext::Traits*
656 Viewer::getTraits(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier)
658 osg::DisplaySettings* ds = _displaySettings.get();
660 ds = osg::DisplaySettings::instance().get();
662 osg::GraphicsContext::Traits* traits = new osg::GraphicsContext::Traits(ds);
664 traits->hostName = screenIdentifier.hostName;
665 traits->displayNum = screenIdentifier.displayNum;
666 traits->screenNum = screenIdentifier.screenNum;
668 // not seriously consider something different
669 traits->doubleBuffer = true;
671 osg::GraphicsContext::ScreenSettings screenSettings;
672 screenSettings = getScreenSettings(screenIdentifier);
676 traits->width = screenSettings.width;
677 traits->height = screenSettings.height;
683 class Viewer::_ResetScreenSaverSwapCallback : public osg::GraphicsContext::SwapCallback {
685 _ResetScreenSaverSwapCallback() :
686 _timeStamp(SGTimeStamp::fromSec(0))
689 virtual ~_ResetScreenSaverSwapCallback()
692 virtual void swapBuffersImplementation(osg::GraphicsContext* graphicsContext)
694 graphicsContext->swapBuffersImplementation();
696 // This callback must be attached to this type of graphics context
697 assert(dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsContext));
699 // Reset the screen saver every 10 seconds
700 SGTimeStamp timeStamp = SGTimeStamp::now();
701 if (timeStamp < _timeStamp)
703 _timeStamp = timeStamp + SGTimeStamp::fromSec(10);
704 // Obviously runs in the draw thread. Thus, use the draw display.
705 XResetScreenSaver(static_cast<osgViewer::GraphicsWindowX11*>(graphicsContext)->getDisplay());
708 SGTimeStamp _timeStamp;
712 osg::GraphicsContext*
713 Viewer::createGraphicsContext(osg::GraphicsContext::Traits* traits)
715 osg::GraphicsContext::WindowingSystemInterface* wsi;
716 wsi = osg::GraphicsContext::getWindowingSystemInterface();
718 SG_LOG(SG_VIEW, SG_ALERT, "No windowing system interface defined!");
722 osg::GraphicsContext* graphicsContext = wsi->createGraphicsContext(traits);
723 if (!graphicsContext) {
724 SG_LOG(SG_VIEW, SG_ALERT, "Unable to create window \"" << traits->windowName << "\"!");
729 if (dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsContext))
730 graphicsContext->setSwapCallback(new _ResetScreenSaverSwapCallback);
733 return graphicsContext;
737 const HLAViewerFederate*
738 Viewer::getViewerFederate() const
740 return _viewerFederate.get();
744 Viewer::getViewerFederate()
746 return _viewerFederate.get();
750 Viewer::setViewerFederate(HLAViewerFederate* viewerFederate)
752 if (!viewerFederate) {
753 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setViewerFederate(): Setting the viewer federate to zero is not supported!");
756 if (_viewerFederate.valid()) {
757 SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setViewerFederate(): Setting the viewer federate twice is not supported!");
760 _viewerFederate = viewerFederate;
761 _viewerFederate->attachToViewer(this);
765 } // namespace fgviewer