有osg 封装的qt默认使用QGLWidget 组件,现在想使用QT的QOpenGLWidget,之前尝试封装使用RTTCamera是一直有问题,最近看到解决办法,就记录一下。
环境:OSG 3.4 版本以上,QT5.9
就是需要设置一个默认fbo id,帧缓存。
参考bbs
if (mIsFirstRenderRun)
{
// set set the default FBO-id.
// this id will be used when the rendering-backend is finished with RTT FBOs
GLuint defaultFboId = this->defaultFramebufferObject();
//mpGraphicsContext->setDefaultFboId(defaultFboId);
graphicsWindow_->setDefaultFboId(defaultFboId);
mIsFirstRenderRun = false;
}
viewer_->frame();
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace osgWidget
{
//! The subclass of osgViewer::CompositeViewer we use
/*!
* This subclassing allows us to remove the annoying automatic
* setting of the CPU affinity to core 0 by osgViewer::ViewerBase,
* osgViewer::CompositeViewer's base class.
*/
class Viewer : public osgViewer::CompositeViewer
{
public:
virtual void setupThreading();
};
}
class OSGWidget;
class Renderer : public QObject, protected QOpenGLFunctions
{
Q_OBJECT
public:
Renderer(OSGWidget *w);
void lockRenderer() { m_renderMutex.lock(); }
void unlockRenderer() { m_renderMutex.unlock(); }
QMutex *grabMutex() { return &m_grabMutex; }
QWaitCondition *grabCond() { return &m_grabCond; }
void prepareExit() { m_exiting = true; m_grabCond.wakeAll(); }
signals:
void contextWanted();
public :
void render();
private:
bool m_inited;
qreal m_fAngle;
qreal m_fScale;
QVector<QVector3D> vertices;
QVector<QVector3D> normals;
int vertexAttr;
int normalAttr;
int matrixUniform;
OSGWidget *m_glwidget;
QMutex m_renderMutex;
QElapsedTimer m_elapsed;
QMutex m_grabMutex;
QWaitCondition m_grabCond;
bool m_exiting;
};
// openg 组件信息过程
class OSGWidget : public QOpenGLWidget
{
Q_OBJECT
public:
OSGWidget(osgViewer::GraphicsWindowEmbedded* graphic,osgViewer::View* view,QWidget* parent = 0,
Qt::WindowFlags f = 0);
virtual ~OSGWidget();
protected:
virtual void paintEvent(QPaintEvent* paintEvent);
virtual void paintGL();
virtual void resizeGL(int width, int height);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual void mouseDoubleClickEvent(QMouseEvent * event);
virtual void wheelEvent(QWheelEvent* event);
virtual bool event(QEvent* event);
public:
virtual void onHome();
virtual void onResize(int width, int height);
osgGA::EventQueue* getEventQueue() const;
osg::ref_ptr<osgViewer::GraphicsWindowEmbedded> graphicsWindow_;
osg::ref_ptr<osgWidget::Viewer> viewer_;
void processSelection();
//
//
signals:
void renderRequested();
public slots:
void grabContext();
private slots:
void onAboutToCompose();
void onFrameSwapped();
void onAboutToResize();
void onResized();
private:
QThread *m_thread;
Renderer *m_renderer;
bool mIsFirstRenderRun;
int64_t m_lastTime;
};
#include "OSGWidget.h"
//#include "PickHandler.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace
{
}
namespace osgWidget
{
void Viewer::setupThreading()
{
if (_threadingModel == SingleThreaded)
{
if (_threadsRunning)
stopThreading();
}
else
{
if (!_threadsRunning)
startThreading();
}
}
}
OSGWidget::OSGWidget(osgViewer::GraphicsWindowEmbedded* graphic, osgViewer::View* view, QWidget* parent,
Qt::WindowFlags f)
: QOpenGLWidget(parent,
f)
, graphicsWindow_(graphic)
, viewer_(new osgWidget::Viewer)
,mIsFirstRenderRun(true),
m_lastTime(0)
{
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
//format.setVersion(4, 1);
format.setSamples(16);
setFormat(format);
viewer_->addView(view);
viewer_->setThreadingModel(osgViewer::CompositeViewer::SingleThreaded);
viewer_->realize();
// This ensures that the widget will receive keyboard events. This focus
// policy is not set by default. The default, Qt::NoFocus, will result in
// keyboard events that are ignored.
this->setFocusPolicy(Qt::StrongFocus);
this->setMinimumSize(100, 100);
// Ensures that the widget receives mouse move events even though no
// mouse button has been pressed. We require this in order to let the
// graphics window switch viewports properly.
this->setMouseTracking(true);
//后台线程模式
//connect(this, &QOpenGLWidget::aboutToCompose, this, &OSGWidget::onAboutToCompose);
//connect(this, &QOpenGLWidget::frameSwapped, this, &OSGWidget::onFrameSwapped);
//connect(this, &QOpenGLWidget::aboutToResize, this, &OSGWidget::onAboutToResize);
//connect(this, &QOpenGLWidget::resized, this, &OSGWidget::onResized);
//m_thread = new QThread;
//m_renderer = new Renderer(this);
//m_renderer->moveToThread(m_thread);
//connect(m_thread, &QThread::finished, m_renderer, &QObject::deleteLater);
//connect(this, &OSGWidget::renderRequested, m_renderer, &Renderer::render);
//connect(m_renderer, &Renderer::contextWanted, this, &OSGWidget::grabContext);
//m_thread->start();
}
OSGWidget::~OSGWidget()
{
//m_renderer->prepareExit();
//m_thread->quit();
//m_thread->wait();
//delete m_thread;
}
void OSGWidget::paintEvent(QPaintEvent* /* paintEvent */)
{
this->makeCurrent();
QPainter painter(this);
this->paintGL();
#ifdef WITH_SELECTION_PROCESSING
if (selectionActive_ && !selectionFinished_)
{
painter.setPen(Qt::black);
painter.setBrush(Qt::transparent);
painter.drawRect(makeRectangle(selectionStart_, selectionEnd_));
}
#endif
painter.end();
this->doneCurrent();
}
void OSGWidget::paintGL()
{
int64_t curTime = QDateTime::currentMSecsSinceEpoch();
if(curTime-m_lastTime<5)
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
m_lastTime = curTime;
if (mIsFirstRenderRun)
{
// set set the default FBO-id.
// this id will be used when the rendering-backend is finished with RTT FBOs
GLuint defaultFboId = this->defaultFramebufferObject();
//mpGraphicsContext->setDefaultFboId(defaultFboId);
graphicsWindow_->setDefaultFboId(defaultFboId);
mIsFirstRenderRun = false;
}
viewer_->frame();
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
}
void OSGWidget::resizeGL(int width, int height)
{
this->getEventQueue()->windowResize(this->x(), this->y(), width, height);
graphicsWindow_->resized(this->x(), this->y(), width, height);
this->onResize(width, height);
}
void OSGWidget::keyPressEvent(QKeyEvent* event)
{
QString keyString = event->text();
const char* keyData = keyString.toLocal8Bit().data();
if (event->key() == Qt::Key_S)
{
#ifdef WITH_SELECTION_PROCESSING
selectionActive_ = !selectionActive_;
#endif
// Further processing is required for the statistics handler here, so we do
// not return right away.
}
else if (event->key() == Qt::Key_D)
{
return;
}
else if (event->key() == Qt::Key_H)
{
this->onHome();
return;
}
this->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KeySymbol(*keyData));
}
void OSGWidget::keyReleaseEvent(QKeyEvent* event)
{
QString keyString = event->text();
const char* keyData = keyString.toLocal8Bit().data();
this->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KeySymbol(*keyData));
}
void OSGWidget::mouseMoveEvent(QMouseEvent* event)
{
// Note that we have to check the buttons mask in order to see whether the
// left button has been pressed. A call to `button()` will only result in
// `Qt::NoButton` for mouse move events.
{
auto pixelRatio = this->devicePixelRatio();
this->getEventQueue()->mouseMotion(static_cast<float>(event->x() * pixelRatio),
static_cast<float>(event->y() * pixelRatio));
}
}
void OSGWidget::mousePressEvent(QMouseEvent* event)
{
{
// 1 = left mouse button
// 2 = middle mouse button
// 3 = right mouse button
unsigned int button = 0;
switch (event->button())
{
case Qt::LeftButton:
button = 1;
break;
case Qt::MiddleButton:
button = 2;
break;
case Qt::RightButton:
button = 3;
break;
default:
break;
}
auto pixelRatio = this->devicePixelRatio();
this->getEventQueue()->mouseButtonPress(static_cast<float>(event->x() * pixelRatio),
static_cast<float>(event->y() * pixelRatio),
button);
}
}
void OSGWidget::mouseReleaseEvent(QMouseEvent* event)
{
{
// 1 = left mouse button
// 2 = middle mouse button
// 3 = right mouse button
unsigned int button = 0;
switch (event->button())
{
case Qt::LeftButton:
button = 1;
break;
case Qt::MiddleButton:
button = 2;
break;
case Qt::RightButton:
button = 3;
break;
default:
break;
}
auto pixelRatio = this->devicePixelRatio();
this->getEventQueue()->mouseButtonRelease(static_cast<float>(pixelRatio * event->x()),
static_cast<float>(pixelRatio * event->y()),
button);
}
}
void OSGWidget::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;
}
int pixelRatio = this->devicePixelRatio();
this->getEventQueue()->mouseDoubleButtonPress(event->x()*pixelRatio, event->y()*pixelRatio, button);
}
void OSGWidget::wheelEvent(QWheelEvent* event)
{
// Ignore wheel events as long as the selection is active.
event->accept();
int delta = event->delta();
osgGA::GUIEventAdapter::ScrollingMotion motion = delta > 0 ? osgGA::GUIEventAdapter::SCROLL_UP
: osgGA::GUIEventAdapter::SCROLL_DOWN;
this->getEventQueue()->mouseScroll(motion);
}
bool OSGWidget::event(QEvent* event)
{
bool handled = QOpenGLWidget::event(event);
// This ensures that the OSG widget is always going to be repainted after the
// user performed some interaction. Doing this in the event handler ensures
// that we don't forget about some event and prevents duplicate code.
switch (event->type())
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
case QEvent::Wheel:
this->update();
break;
default:
break;
}
return handled;
}
void OSGWidget::onHome()
{
osgViewer::ViewerBase::Views views;
viewer_->getViews(views);
for (std::size_t i = 0; i < views.size(); i++)
{
osgViewer::View* view = views.at(i);
view->home();
}
}
void OSGWidget::onResize(int width, int height)
{
std::vector<osg::Camera*> cameras;
viewer_->getCameras(cameras);
assert(cameras.size() >= 1);
auto pixelRatio = this->devicePixelRatio();
cameras[0]->setViewport(0, 0, width * pixelRatio, height * pixelRatio);
//cameras[1]->setViewport(width / 2 * pixelRatio, 0, width / 2 * pixelRatio, height * pixelRatio);
}
osgGA::EventQueue* OSGWidget::getEventQueue() const
{
osgGA::EventQueue* eventQueue = graphicsWindow_->getEventQueue();
if (eventQueue)
return eventQueue;
else
throw std::runtime_error("Unable to obtain valid event queue");
}
void OSGWidget::processSelection()
{
}
void OSGWidget::grabContext()
{
m_renderer->lockRenderer();
QMutexLocker lock(m_renderer->grabMutex());
context()->moveToThread(m_thread);
m_renderer->grabCond()->wakeAll();
m_renderer->unlockRenderer();
}
void OSGWidget::onAboutToCompose()
{
m_renderer->lockRenderer();
}
void OSGWidget::onFrameSwapped()
{
m_renderer->unlockRenderer();
// Assuming a blocking swap, our animation is driven purely by the
// vsync in this example.
emit renderRequested();
}
void OSGWidget::onAboutToResize()
{
m_renderer->lockRenderer();
}
void OSGWidget::onResized()
{
m_renderer->unlockRenderer();
}
Q_GLOBAL_STATIC(QMutex, initMutex)
Renderer::Renderer(OSGWidget *w) : m_inited(false),
m_glwidget(w),
m_exiting(false) {
}
void Renderer::render()
{
if (m_exiting)
return;
QOpenGLContext *ctx = m_glwidget->context();
if (!ctx) // QOpenGLWidget not yet initialized
return;
QSurfaceFormat format;
format.setVersion(2, 1);
format.setOption(QSurfaceFormat::DeprecatedFunctions, true);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setSamples(8);
ctx->setFormat(format);
// Grab the context.
m_grabMutex.lock();
emit contextWanted();
m_grabCond.wait(&m_grabMutex);
QMutexLocker lock(&m_renderMutex);
m_grabMutex.unlock();
if (m_exiting)
return;
Q_ASSERT(ctx->thread() == QThread::currentThread());
// Make the context (and an offscreen surface) current for this thread. The
// QOpenGLWidget's fbo is bound in the context.
m_glwidget->makeCurrent();
if (!m_inited) {
m_inited = true;
initializeOpenGLFunctions();
QMutexLocker initLock(initMutex());
m_elapsed.start();
// set set the default FBO-id.
// this id will be used when the rendering-backend is finished with RTT FBOs
GLuint defaultFboId = m_glwidget->defaultFramebufferObject();
//mpGraphicsContext->setDefaultFboId(defaultFboId);
m_glwidget->graphicsWindow_->setDefaultFboId(defaultFboId);
}
m_glwidget->viewer_->frame();
// Make no context current on this thread and move the QOpenGLWidget's
// context back to the gui thread.
m_glwidget->doneCurrent();
ctx->moveToThread(qGuiApp->thread());
// Schedule composition. Note that this will use QueuedConnection, meaning
// that update() will be invoked on the gui thread.
QMetaObject::invokeMethod(m_glwidget, "update");
}
在使用过程中没有发现问题,并且可以改成线程模式,osg 的渲染在线程中渲染。