/* OpenSceneGraph example, osganimate. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * 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. */ #include #include #include #if defined(WIN32) && !defined(__CYGWIN__) #include typedef HWND WindowHandle; typedef osgViewer::GraphicsWindowWin32::WindowData WindowData; #elif defined(__APPLE__) // Assume using Carbon on Mac. // #include #include typedef WindowRef WindowHandle; typedef osgViewer::GraphicsWindowCarbon::WindowData WindowData; #else // all other unix #include typedef Window WindowHandle; typedef osgViewer::GraphicsWindowX11::WindowData WindowData; // 10/17/08 LM -- osgViewer/api/X11/GraphicsWindowX11 includes X.h. // X.h defines KeyPress and KeyRelease. // By doing so, using QEvent::KeyPress resolves into QEvent::2, // causing a compile error. #undef KeyPress #undef KeyRelease #endif QOSGWidget::QOSGWidget( QWidget * parent, WindowFlags f) : QWidget(parent, f), _gw(0) { #if 0 // This was a win on Linux. Same as WA_PaintOnScreen? extern void qt_x11_set_global_double_buffer(bool); qt_x11_set_global_double_buffer(false); #endif createContext(parent); // Date: Tue, 16 Jun 2009 10:07:16 +0000 // From: "Eric Pouliquen" // Subject: Re: [osg-submissions] New QOSGWidget demo with a 4-way split // window and bonus outboard window. // Suggested replacing the two setAttribute calls with this... // setAttribute(Qt::WA_OpaquePaintEvent); // This solverd a flickering problem on Windows // 8600 GT (185.85) and a Quadro FX1400 (182.65) // but causes a visible black border to be visible in the rendering // windows on Linux. This problem gone when WA_PaintOnScreen // is used in combination. // Hmmm... // According to Qt doc, WA_PaintOnScreen is X11 only and disables // double-buffering. I think this just means it disables a // buffer swap under Qt control. We want OSG to have full control. // // Equivalent to qt_x11_set_global_double_buffer(false)? // // Tried turning it off and got severe flashing on Linux. // Looks like without this we get an extraneous clear and // buffer swap form Qt. setAttribute(Qt::WA_PaintOnScreen); // This flags that something other than Qt is responsible for // all rendering in the window under the widget's control. setAttribute(Qt::WA_OpaquePaintEvent); // This seems superfluous now. // setAttribute(Qt::WA_NoSystemBackground); // Here or in ViewQOSG? // Either way is OK; but since this class is also the one implementing // eventFilter(), I thought it might be wiser here. // Qt // If you want to see how TAB and SHIFT-TAB work wrt focus, // uncomment the following line. // NOTE: If focusPolicy was set in the .ui file, that setting will // override this. So don't set focusPolicy in the .ui file! if (parent) { // The desire is to allow tabbing among the views of the // composite viewer. On Linux, I could get that to work in many // different ways, including setting StrongFocus on the QOSGWidget and // re-implementing focusInEvent() in ViewQOSG. But the only thing // that worked on Windows was to set StrongFocus on the *parent*, NOT // on this QOSGWidget, AND to have ViewQOSG::keyReleaseEvent() do // something special on the release of a Tab or Shift-Tab key. (The // release is seen by the ViewQOSG that is getting the focus.) parent->setFocusPolicy( Qt::StrongFocus ); // This instance of the QOSGWidget becomes the filter object on its // 'parent'. Ie, the child is now filtering events for the parent // QWidget. parent->installEventFilter( this ); qDebug() << "parent->width() is " << parent->width(); qDebug() << "parent->height() is " << parent->height(); qDebug() << "parent->x() is " << parent->x(); qDebug() << "parent->y() is " << parent->y(); qDebug() << "width() is " << width(); qDebug() << "height() is " << height(); qDebug() << "x() is " << x(); qDebug() << "y() is " << y(); } else setFocusPolicy( Qt::StrongFocus ); } void QOSGWidget::createContext(QWidget * parent) { osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); osg::ref_ptr traits = new osg::GraphicsContext::Traits; traits->readDISPLAY(); if (traits->displayNum<0) traits->displayNum = 0; traits->windowName = "qosgwidget"; traits->screenNum = 0; // original location: // traits->x = x(); // traits->y = y(); // traits->width = width(); // traits->height = height(); traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->windowDecoration = false; traits->doubleBuffer = true; traits->sharedContext = 0; traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); #if defined(__APPLE__) // Extract a WindowPtr from the HIViewRef that QWidget::winId() returns. // Without this change, the peer tries to call GetWindowPort on the HIViewRef // which returns 0 and we only render white. traits->inheritedWindowData = new WindowData(HIViewGetWindow((HIViewRef)winId())); #else // all others traits->inheritedWindowData = new WindowData(winId()); #endif if (ds->getStereo()) { switch(ds->getStereoMode()) { case(osg::DisplaySettings::QUAD_BUFFER): traits->quadBufferStereo = true; break; case(osg::DisplaySettings::VERTICAL_INTERLACE): case(osg::DisplaySettings::CHECKERBOARD): case(osg::DisplaySettings::HORIZONTAL_INTERLACE): traits->stencil = 8; break; default: break; } } osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get()); _gw = dynamic_cast(gc.get()); // A person named Lukas on the OSG group posted that to support embedding // in another window, the following traits had to be set after the // graphics context was created. And he's right, if embedding, you // definitely have to do this AFTER creating the context.... Since it // also works when creating a top-level window, do it here for all cases // We may have just gotten some Bad Window errors from X11 calling // XGetWindowAttributes before the Window has been realized. // We'll also have garbage in the traits which get fixed up next. // _overrideTraits is superfluous now. if (parent) { traits->x = parent->x(); traits->y = parent->y(); traits->width = parent->width(); traits->height = parent->height(); } else { traits->x = x(); traits->y = y(); traits->width = width(); traits->height = height(); } } //------------------------------------------------------------------------------ // From the Qt Doc: // "eventFilter() can accept or reject the event, and allow or deny further // processing of the event. // // If all event filters allow further processing of an event (by each // returning false), the event is sent to the target object, in this case, the // parent of this QOSGWidget instance. // // If one of them stops processing (by returning true), the target (ie the // parent widget) and any later event filters do not get to see the event at // all. " // // Effectively, this filter takes all the events it's interested in, most // notably keyboard events, and passes them along to OSG and *prevents* them // from reaching their intended target, this widget's parent. // // Since QOSGWidget sets Qt focusPolicy to StrongFocus, eventFilter() isn't // needed for the keyboard events...at least this what I've observed on Linux; // however it is essential to support resizing in embedded Qt windows that // aren't a top-level window. // // Addendum: I think the above statement is true only if the parent has the // NoFocus policy? If the parent also has StrongFocus, then // eventFilter() has to return false for key events or tabbing // breaks. //------------------------------------------------------------------------------ // Return false to allow event to go to the intended target, the parent of // this QOSGWidget. The only events we really want to go to the parent are // TAB or shift-TAB presses and releases. bool QOSGWidget::eventFilter(QObject *obj, QEvent *event) { if (obj != parent()) { return false; } else if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) { qDebug() << "QOSGWidget::eventFilter: TAB Press on " << qPrintable(objectName()); // Empirically have found that it's not necessary to call // keyPressEvent on tab press ... my guess is that OSG ignores it. keyPressEvent( ke ); // Return false so that the parent QWidget will process the tab. return false; } else { qDebug() << "QOSGWidget::eventFilter: KeyPress on " << qPrintable(objectName()); keyPressEvent( ke ); // event handled, return true because parent does not have to see // this event return true; } } else if (event->type() == QEvent::KeyRelease) { QKeyEvent *ke = static_cast(event); if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) { qDebug() << "QOSGWidget::eventFilter: TAB Release on " << qPrintable(objectName()); keyReleaseEvent( ke ); // Return false so that the parent QWidget will process the tab.. return false; } else { qDebug() << "QOSGWidget::eventFilter: KeyRelease on " << qPrintable(objectName()); keyReleaseEvent( ke ); // event handled, return true because parent does not have to see // this event return true; } } else if (event->type() == QEvent::Resize) { QResizeEvent *re = static_cast(event); qDebug() << "QOSGWidget::eventFilter: width is " << re->size().width() << "; height is " << re->size().height() << " on " << qPrintable(objectName()) ; // Call setGeometry on 'this', which will trigger // QOSGWidget::resizeEvent setGeometry(0, 0, re->size().width(), re->size().height()); // event handled, return true because parent does not have to see // this event return true; } else if (event->type() == QEvent::Close) { QCloseEvent *ce = static_cast(event); closeEvent( ce ); } return false; } // Skip all of the event queue reimplementations on WIN32. // On second thought, all but the key events. #ifndef WIN32 void QOSGWidget::destroyEvent(bool /* destroyWindow */, bool /* destroySubWindows */ ) { qDebug() << "QOSGWidget::destroyEvent"; _gw->getEventQueue()->closeWindow(); } void QOSGWidget::closeEvent( QCloseEvent * /* event */ ) { qDebug() << "QOSGWidget::closeEvent"; _gw->getEventQueue()->closeWindow(); } void QOSGWidget::resizeEvent( QResizeEvent * event ) { const QSize & size = event->size(); qDebug() << "QOSGWidget::resizeEvent on " << qPrintable(objectName()) << " - width is " << size.width() << "; height is " << size.height(); _gw->getEventQueue()->windowResize(0, 0, size.width(), size.height() ); _gw->resized(0, 0, size.width(), size.height()); } void QOSGWidget::mousePressEvent( QMouseEvent* event ) { qDebug() << "QOSGWidget::mousePressEvent on " << qPrintable(objectName()) << " at X, Y = " << event->x() << ", " << event->y(); int button = 0; switch(event->button()) { case(Qt::LeftButton): button = 1; break; case(Qt::MidButton): button = 2; break; case(Qt::RightButton): button = 3; break; case(Qt::NoButton): button = 0; break; default: button = 0; break; } _gw->getEventQueue()->mouseButtonPress(event->x(), event->y(), button); } void QOSGWidget::mouseDoubleClickEvent ( QMouseEvent * event ) { qDebug() << "QOSGWidget::mouseDoubleClickEvent on " << qPrintable(objectName()); int button = 0; switch(event->button()) { case(Qt::LeftButton): button = 1; break; case(Qt::MidButton): button = 2; break; case(Qt::RightButton): button = 3; break; case(Qt::NoButton): button = 0; break; default: button = 0; break; } _gw->getEventQueue()->mouseDoubleButtonPress(event->x(), event->y(), button); } void QOSGWidget::mouseReleaseEvent( QMouseEvent* event ) { qDebug() << "QOSGWidget::mouseReleaseEvent on " << qPrintable(objectName()) << " at X, Y = " << event->x() << ", " << event->y(); int button = 0; switch(event->button()) { case(Qt::LeftButton): button = 1; break; case(Qt::MidButton): button = 2; break; case(Qt::RightButton): button = 3; break; case(Qt::NoButton): button = 0; break; default: button = 0; break; } _gw->getEventQueue()->mouseButtonRelease(event->x(), event->y(), button); } void QOSGWidget::mouseMoveEvent( QMouseEvent* event ) { qDebug() << "QOSGWidget::mouseMoveEvent"; _gw->getEventQueue()->mouseMotion(event->x(), event->y()); } #endif // ndef WIN32 void QOSGWidget::keyPressEvent( QKeyEvent* event ) { qDebug() << "QOSGWidget::keyPressEvent on " << qPrintable(objectName()); _gw->getEventQueue()->keyPress( (osgGA::GUIEventAdapter::KeySymbol) *(event->text().toAscii().data() ) ); } void QOSGWidget::keyReleaseEvent( QKeyEvent* event ) { qDebug() << "QOSGWidget::keyReleaseEvent on " << qPrintable(objectName()); if (event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab) { qDebug() << "---- It's a TAB Release" ; } else { int c = *event->text().toAscii().data(); _gw->getEventQueue()->keyRelease( (osgGA::GUIEventAdapter::KeySymbol) (c) ); } } //--------------------------------------------------------------------------- // ViewQOSG creates the traits... ViewQOSG::ViewQOSG( QWidget *parent ) : osgViewer::View(), QOSGWidget( parent ), _daw(parent) { // OSG setGeometry( 0, 0, parent->width(), parent->height() ); getCamera()->setGraphicsContext( getGraphicsWindow() ); getCamera()->setProjectionMatrixAsPerspective( 30.0f, static_cast(parent->width()) / static_cast(parent->height()), 1.0, 1000.0); getCamera()->setViewport( new osg::Viewport( 0, 0, parent->width(), parent->height())); setupManipulatorAndHandler( *this ); } // ViewQOSG::ViewQOSG //--------------------------------------------------------------------------- // note: since ViewQOSG has focusPolicy of Qt::NoFocus, this won't be called, // but leaving it here anyway. // See keyReleaseEvent() void ViewQOSG::focusInEvent( QFocusEvent * /* event */) { qDebug() << "ViewQOSG::focusInEvent on " << qPrintable(objectName()); CompositeViewerQOSG *cv = dynamic_cast(getViewerBase()); if (cv) // Tell the viewer that this view's camera should now have focus cv->setCameraWithFocus( getCamera() ); } #ifndef WIN32 //--------------------------------------------------------------------------- void ViewQOSG::resizeEvent( QResizeEvent * event ) { const QSize & size = event->size(); qDebug() << "ViewQOSG::resizeEvent: width is " << size.width() << "; height is " << size.height(); QOSGWidget::resizeEvent( event ); // This call seems to be essential to getting a picture after view's // parent QWidget has been collapsed due to QSplitter activity. getCamera()->setProjectionMatrixAsPerspective( 30.0f, static_cast(size.width()) / static_cast(size.height()), 1.0, 1000.0); //// // This is an attempt to prevent Qt from giving tab focus to a ViewQOSG that // has been collapsed; in our example, this can happen by using the QSplitters // in the QMainWindow. // // However, while testing this, discovered the bigger problem: // On *any* resize, osgViewer::CompositeViewer::eventTraversal() calls // setCameraWithFocus(0) (ie, to the first view). // Meanwhile, Qt's focus widget may or may not be the correpsonding ViewQOSG. // Possibly worse, the ViewQOSG corresponding to the first view in the // composite viewer may not even be visible. // Doesn't seem to matter in this sense: regardless of which VIEWQOSG gets a // keyboard event, the compositeViewer applies the key's corresponding action // to the current camera/view. // // All of which suggests that trying to support TAB focus on the ViewQOSG // widgets just isn't worth it. // if (size.width() == 0 || size.height() == 0) // setFocusPolicy( Qt::NoFocus ); // else // setFocusPolicy( Qt::StrongFocus ); //// } // ViewQOSG::resizeEvent #endif // WIN32 //--------------------------------------------------------------------------- // Reimplementing keyReleaseEvent from QOSGWidget. // When tabbing in Qt, the current widget sees the TAB Press and the widget // that gets the focus sees the TAB Release. // On that TAB Release, we need to tell the CompositeViewer the camera that // should now have the focus. // // SEE COMMENTS IN ViewQOSG::resizeEvent re how resizing screws this all up... void ViewQOSG::keyReleaseEvent( QKeyEvent* event ) { qDebug() << "ViewQOSG::keyReleaseEvent on " << qPrintable(objectName()); if (event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab) { if (event->key() == Qt::Key_Tab) qDebug() << "... and it's a TAB"; else qDebug() << "... and it's a SHIFT-TAB"; CompositeViewerQOSG *cv = dynamic_cast(getViewerBase()); if (cv) { // Tell the viewer that this view's camera should now have focus cv->setCameraWithFocus( getCamera() ); // Note that the Stats come up wherever; having little success in // getting them to come up in a specific osgView. // cv->setEventQueue( _gw->getEventQueue() ); } // and otherwise ignore the event } else { QOSGWidget::keyReleaseEvent( event ); } } // ViewQOSG::keyReleaseEvent //--------------------------------------------------------------------------- void ViewQOSG::setData( osg::ref_ptr loadedModel ) { setSceneData(loadedModel.get()); } //--------------------------------------------------------------------------- float ViewQOSG::aspectRatio( int width, int height ) { return static_cast(width) / static_cast(height); } //--------------------------------------------------------------------------- void setupManipulatorAndHandler(osgViewer::View & view /*, osg::ArgumentParser & arguments*/) { // set up the camera manipulators. { osg::ref_ptr keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator; keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() ); keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() ); keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() ); keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() ); view.setCameraManipulator( keyswitchManipulator.get() ); } // add the state manipulator view.addEventHandler( new osgGA::StateSetManipulator( view.getCamera()->getOrCreateStateSet()) ); // add the thread model handler view.addEventHandler(new osgViewer::ThreadingHandler); // add the window size toggle handler view.addEventHandler(new osgViewer::WindowSizeHandler); // add the stats handler view.addEventHandler(new osgViewer::StatsHandler); // add the help handler view.addEventHandler(new osgViewer::HelpHandler(/*arguments.getApplicationUsage()*/)); }