This mod to QOSGWidget enables multithreaded rendering using QOSGWidget using
CompositeViewer. It demonstrates as well the embedding of QOSGWidget into other
QTWidgets. Performance is very good (70 fps) on AMD 3Ghz, FedoraCore8 using
NVidia 8600GTS.
usage:
~~~~~~
osgviewerQT --QOSGWidget --MTCompositeViewer cow.osg
There are some issues with this mod:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Other multithreading modes fail.
2. Correct Widget inheritance is crucial to functionality.
3. Written for QT4 only
4. Using 'ClickToFocus'. Select the widget first before manipulating.
5. though working fine we get a BadWindow error message see thread
[osg-users] Multiple Contexts in QT Applications
6. Although the problem (point 5) is related to deranged traits values
which are set by failing a call to XGetWindowAttributes in
GraphicsWindowX11::setWindow the following fix (to set traits only if
XGetWindowAttributes returns with no error) does not solve the problem
properly:
@ line 750, GraphicsWindowX11.cpp
Status s = XGetWindowAttributes( _display, _window, &watt );
if (s = True)
{
_traits->x = watt.x;
_traits->y = watt.y;
_traits->width = watt.width;
_traits->height = watt.height;
}
The context can be embedded and displayed but somehow the
perspective is distored in this case. That's why we set the traits
in QOSGWidget::createContext after initializing the graphicsContext.
Regards,
Lukas
/* 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.
*/
#if USE_QT4
#include <QtCore/QString>
#include <QtCore/QTimer>
#include <QtGui/QKeyEvent>
#include <QtGui/QApplication>
#include <QtOpenGL/QGLWidget>
#include <QtGui/QtGui>
#include <QtGui/QWidget>
using Qt::WindowFlags;
#else
class QWidget;
#include <qtimer.h>
#include <qgl.h>
#include <qapplication.h>
#define WindowFlags WFlags
#endif
#include <osgViewer/Viewer>
#include <osgViewer/CompositeViewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/GraphicsWindow>
#include <osgViewer/ViewerEventHandlers>
#if defined(WIN32) && !defined(__CYGWIN__)
#include <osgViewer/api/Win32/GraphicsWindowWin32>
typedef HWND WindowHandle;
typedef osgViewer::GraphicsWindowWin32::WindowData WindowData;
#elif defined(__APPLE__) && defined(APPLE_PRE_10_3)
#include <osgViewer/api/Carbon/GraphicsWindowCarbon>
typedef WindowRef WindowHandle;
typedef osgViewer::GraphicsWindowCarbon::WindowData WindowData;
#else // all other unix
#include <osgViewer/api/X11/GraphicsWindowX11>
typedef Window WindowHandle;
typedef osgViewer::GraphicsWindowX11::WindowData WindowData;
#endif
#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/StateSetManipulator>
#include <osgGA/AnimationPathManipulator>
#include <osgGA/TerrainManipulator>
#include <osgDB/ReadFile>
#include <iostream>
#include <sstream>
class QOSGWidget : public QWidget
{
public:
QOSGWidget( QWidget * parent = 0, const char * name = 0, WindowFlags f
= 0, bool overrideTraits = false);
virtual ~QOSGWidget() {}
osgViewer::GraphicsWindow* getGraphicsWindow() { return _gw.get(); }
const osgViewer::GraphicsWindow* getGraphicsWindow() const { return
_gw.get(); }
protected:
void init();
void createContext();
virtual void mouseDoubleClickEvent ( QMouseEvent * event );
virtual void closeEvent( QCloseEvent * event );
virtual void destroyEvent( bool destroyWindow = true, bool
destroySubWindows = true);
virtual void resizeEvent( QResizeEvent * event );
virtual void keyPressEvent( QKeyEvent* event );
virtual void keyReleaseEvent( QKeyEvent* event );
virtual void mousePressEvent( QMouseEvent* event );
virtual void mouseReleaseEvent( QMouseEvent* event );
virtual void mouseMoveEvent( QMouseEvent* event );
osg::ref_ptr<osgViewer::GraphicsWindow> _gw;
bool _overrideTraits;
};
QOSGWidget::QOSGWidget( QWidget * parent, const char * name, WindowFlags f,
bool overrideTraits):
#if USE_QT4
QWidget(parent, f), _overrideTraits (overrideTraits)
#else
QWidget(parent, name, f), _overrideTraits (overrideTraits)
#endif
{
createContext();
#if USE_QT4
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground);
setFocusPolicy(Qt::ClickFocus);
#else
setBackgroundMode(Qt::NoBackground);
#endif
}
void QOSGWidget::createContext()
{
osg::DisplaySettings* ds = osg::DisplaySettings::instance();
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new
osg::GraphicsContext::Traits;
traits->readDISPLAY();
if (traits->displayNum<0) traits->displayNum = 0;
traits->windowName = "osgViewerQt";
traits->screenNum = 0;
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();
traits->inheritedWindowData = new WindowData(winId());
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<osg::GraphicsContext> gc =
osg::GraphicsContext::createGraphicsContext(traits.get());
_gw = dynamic_cast<osgViewer::GraphicsWindow*>(gc.get());
// get around dearanged traits on X11 (MTCompositeViewer only)
if (_overrideTraits)
{
traits->x = x();
traits->y = y();
traits->width = width();
traits->height = height();
}
}
void QOSGWidget::destroyEvent(bool destroyWindow, bool destroySubWindows)
{
_gw->getEventQueue()->closeWindow();
}
void QOSGWidget::closeEvent( QCloseEvent * event )
{
#ifndef USE_QT4
event->accept();
#endif
_gw->getEventQueue()->closeWindow();
}
void QOSGWidget::resizeEvent( QResizeEvent * event )
{
const QSize & size = event->size();
_gw->getEventQueue()->windowResize(0, 0, size.width(), size.height() );
_gw->resized(0, 0, size.width(), size.height());
}
void QOSGWidget::keyPressEvent( QKeyEvent* event )
{
#if USE_QT4
_gw->getEventQueue()->keyPress( (osgGA::GUIEventAdapter::KeySymbol)
*(event->text().toAscii().data() ) );
#else
_gw->getEventQueue()->keyPress( (osgGA::GUIEventAdapter::KeySymbol)
event->ascii() );
#endif
}
void QOSGWidget::keyReleaseEvent( QKeyEvent* event )
{
#if USE_QT4
int c = *event->text().toAscii().data();
#else
int c = event->ascii();
#endif
_gw->getEventQueue()->keyRelease( (osgGA::GUIEventAdapter::KeySymbol) (c) );
}
void QOSGWidget::mousePressEvent( QMouseEvent* event )
{
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 )
{
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 )
{
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 )
{
_gw->getEventQueue()->mouseMotion(event->x(), event->y());
}
class ViewerQOSG : public osgViewer::Viewer, public QOSGWidget
{
public:
ViewerQOSG(QWidget * parent = 0, const char * name = 0, WindowFlags f =
0):
QOSGWidget( parent, name, f )
{
getCamera()->setViewport(new osg::Viewport(0,0,width(),height()));
getCamera()->setProjectionMatrixAsPerspective(30.0f,
static_cast<double>(width())/static_cast<double>(height()), 1.0f, 10000.0f);
getCamera()->setGraphicsContext(getGraphicsWindow());
setThreadingModel(osgViewer::Viewer::SingleThreaded);
connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
_timer.start(10);
}
virtual void paintEvent( QPaintEvent * event ) { frame(); }
protected:
QTimer _timer;
};
class CompositeViewerQOSG : public osgViewer::CompositeViewer, public QOSGWidget
{
public:
CompositeViewerQOSG(QWidget * parent = 0, const char * name = 0,
WindowFlags f = 0):
QOSGWidget( parent, name, f )
{
setThreadingModel(osgViewer::CompositeViewer::SingleThreaded);
connect(&_timer, SIGNAL(timeout()), this, SLOT(repaint()));
_timer.start(1);
}
virtual void paintEvent( QPaintEvent * event ) { frame(); }
protected:
QTimer _timer;
};
#if USE_QT4
// we use this wrapper for CompositeViewer ONLY because of the timer
//
class MTCompositeViewer : public osgViewer::CompositeViewer, public QWidget
{
public:
MTCompositeViewer (QWidget * parent = 0, WindowFlags f = 0):
QWidget (parent, f)
{
setThreadingModel(osgViewer::CompositeViewer::DrawThreadPerContext);
connect(&_timer, SIGNAL(timeout()), this, SLOT(repaint()));
_timer.start(1);
}
virtual void paintEvent( QPaintEvent * event ) { frame(); }
protected:
QTimer _timer;
};
#endif
void setupManipulatorAndHandler(osgViewer::View & viewer, osg::ArgumentParser &
arguments)
{
// set up the camera manipulators.
{
osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> 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() );
viewer.setCameraManipulator( keyswitchManipulator.get() );
}
// add the state manipulator
viewer.addEventHandler( new
osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
// add the thread model handler
viewer.addEventHandler(new osgViewer::ThreadingHandler);
// add the window size toggle handler
viewer.addEventHandler(new osgViewer::WindowSizeHandler);
// add the stats handler
viewer.addEventHandler(new osgViewer::StatsHandler);
// add the help handler
viewer.addEventHandler(new
osgViewer::HelpHandler(arguments.getApplicationUsage()));
}
int mainQOSGWidget(QApplication& a, osg::ArgumentParser& arguments)
{
// load the scene.
osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
if (!loadedModel)
{
std::cout << arguments[0] <<": No data loaded." << std::endl;
return 1;
}
std::cout<<"Using QOSGWidget - QWidget + osgViewer creating the graphics
context."<<std::endl;
#if USE_QT4
if (arguments.read("--MTCompositeViewer"))
{
std::cout<<" + using standard CompositeViewer with seperate contexts
(Multithreaded mode)."<<std::endl;
int nbCowsX = 3;
int nbCowsY = 3;
int size = 200;
unsigned int width = nbCowsX * size;
unsigned int height = nbCowsY * size;
QGridLayout *uiLayout = new QGridLayout ();
QWidget *wy = new QWidget ();
// we pass wy as a parent because want wy->show () to work on cViewer
// too..
//
MTCompositeViewer *cViewer = new MTCompositeViewer (wy);
for (int x=0;x<nbCowsX; x++)
for (int y=0;y<nbCowsY; y++)
{
// embed the QOSGWidget into QGroupBox to demonstrate that we
// really use QT's Widgets
//
std::stringstream widgetname; widgetname << "View (" << x <<
"," << y << ")";
QGroupBox *w= new QGroupBox (QString (widgetname.str ().c_str
()), wy);
QGridLayout *tmpl = new QGridLayout ();
QOSGWidget *gw = new QOSGWidget (w, 0, 0, true);
tmpl->addWidget (gw);
w->setLayout(tmpl);
uiLayout->addWidget (w, y, x);
// setup views as usual
osgViewer::View* view = new osgViewer::View;
view->getCamera()->setGraphicsContext(gw->getGraphicsWindow ());
view->getCamera()->setProjectionMatrixAsPerspective
(30.0f,
static_cast<double>(size)/static_cast<double>(size/nbCowsY), 1.0, 1000.0);
view->getCamera()->setViewport(new
osg::Viewport(0,0,size,size));
view->addEventHandler(new osgViewer::StatsHandler);
view->setCameraManipulator(new osgGA::TrackballManipulator);
view->setSceneData(loadedModel.get ());
cViewer->addView(view);
}
wy->setLayout (uiLayout);
wy->resize (width, height);
wy->show ();
a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) );
return a.exec();
}
else
{
#endif
if (arguments.read("--CompositeViewer"))
{
osg::ref_ptr<CompositeViewerQOSG> viewerWindow(new
CompositeViewerQOSG);
viewerWindow->setGeometry(0,0,640,480);
unsigned int width = viewerWindow->width();
unsigned int height = viewerWindow->height();
{
osgViewer::View* view1 = new osgViewer::View;
view1->getCamera()->setGraphicsContext(viewerWindow->getGraphicsWindow());
view1->getCamera()->setProjectionMatrixAsPerspective(30.0f,
static_cast<double>(width)/static_cast<double>(height/2), 1.0, 1000.0);
view1->getCamera()->setViewport(new
osg::Viewport(0,0,width,height/2));
view1->setSceneData(loadedModel.get());
setupManipulatorAndHandler(*view1, arguments);
viewerWindow->addView(view1);
}
{
osgViewer::View* view2 = new osgViewer::View;
view2->getCamera()->setGraphicsContext(viewerWindow->getGraphicsWindow());
view2->getCamera()->setProjectionMatrixAsPerspective(30.0f,
static_cast<double>(width)/static_cast<double>(height/2), 1.0, 1000.0);
view2->getCamera()->setViewport(new
osg::Viewport(0,height/2,width,height/2));
view2->setSceneData(loadedModel.get());
setupManipulatorAndHandler(*view2, arguments);
viewerWindow->addView(view2);
}
viewerWindow->show();
a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) );
return a.exec();
}
else
{
osg::ref_ptr<ViewerQOSG> viewerWindow(new ViewerQOSG);
viewerWindow->setGeometry(0,0,640,480);
viewerWindow->setCameraManipulator(new osgGA::TrackballManipulator);
viewerWindow->setSceneData(loadedModel.get());
viewerWindow->show();
setupManipulatorAndHandler(*viewerWindow.get(), arguments);
a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) );
return a.exec();
}
#if USE_QT4
}
#endif
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org