从目前网上介绍OSG与Qml集成有两种方式:
第一种方法实现起来比较优雅方便,但是不支持多pass渲染,本文主要介绍第二种。在本文用EventAdapter继承QQuickItem作为连接QtQuick与Osg的一个基类,后面只要继承这个类并注册到qml就可以了。
最后看代码
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class EventAdapter : public QQuickItem
{
Q_OBJECT
public:
explicit EventAdapter(QQuickItem *parent = nullptr);
osgViewer::Viewer* getViewer();
public slots:
protected:
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
void keyPressEvent(QKeyEvent *event);
QSGNode* updatePaintNode(QSGNode *oldNode,
UpdatePaintNodeData *updatePaintNodeData);
public:
inline int mouseButton(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;
}
return button;
}
public:
osg::ref_ptr<osgViewer::Viewer> viewer;
QOpenGLFramebufferObject *fbo;
QSGTexture *texture;
QSGSimpleTextureNode *textureNode;
public slots:
void updateViewport();
private slots:
void onWindowChanged(QQuickWindow *window);
void frame();
private:
void initOSG();
void initFBO();
void updateFBO();
};
#include "EventAdapter.h"
#include
EventAdapter::EventAdapter(QQuickItem *parent) :
QQuickItem(parent),
fbo(nullptr),
texture(nullptr),
textureNode(nullptr)
{
initOSG();
connect(this, SIGNAL(windowChanged(QQuickWindow*)),
this, SLOT(onWindowChanged(QQuickWindow*)));
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::AllButtons);
}
void EventAdapter::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
if (window())
{
updateViewport();
}
QQuickItem::geometryChanged(newGeometry, oldGeometry);
}
void EventAdapter::mousePressEvent(QMouseEvent *event)
{
int button = mouseButton(event);
viewer->getEventQueue()->mouseButtonPress(event->x(), event->y(), button);
}
void EventAdapter::mouseMoveEvent(QMouseEvent *event)
{
viewer->getEventQueue()->mouseMotion(event->x(), event->y());
}
void EventAdapter::mouseReleaseEvent(QMouseEvent *event)
{
int button = mouseButton(event);
viewer->getEventQueue()->mouseButtonRelease(event->x(), event->y(), button);
}
void EventAdapter::keyPressEvent(QKeyEvent *event)
{
viewer->getEventQueue()->keyPress(static_cast<int>(*(event->text().toLatin1().data())));
}
void EventAdapter::wheelEvent(QWheelEvent *event)
{
if (event->delta() > 0)
viewer->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_UP);
else
viewer->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_DOWN);
}
QSGNode *EventAdapter::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
{
if (oldNode && oldNode != textureNode) {
delete oldNode;
}
Q_UNUSED(updatePaintNodeData)
return textureNode;
}
void EventAdapter::updateViewport()
{
if (!this->window())
return;
QSize size(this->boundingRect().size().toSize());
viewer->getCamera()->getGraphicsContext()->resizedImplementation(0, 0, size.width(), size.height());
osgGA::GUIEventAdapter *ea = viewer->getEventQueue()->getCurrentEventState();
ea->setXmin(0);
ea->setXmax(size.width());
ea->setYmin(0);
ea->setYmax(size.height());
viewer->getCamera()->setViewport(0, 0, size.width(), size.height());
viewer->getCamera()->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(size.width()) / static_cast<double>(size.height()), 0.1f, 10000.0f);
// view->getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true);
// view->getCamera()->getGraphicsContext()->getState()->setUseVertexAttributeAliasing(true);
if (texture && texture->textureSize() != size) {
updateFBO();
}
}
void EventAdapter::initOSG() {
viewer = new osgViewer::Viewer();
viewer->addEventHandler( new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()) );
viewer->addEventHandler(new osgViewer::StatsHandler);
//viewer->addEventHandler(new osgViewer::WindowSizeHandler);
//viewer->getEventQueue()->getCurrentEventState()
}
void EventAdapter::initFBO() {
QRectF rect = this->boundingRect();
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
QSize size(rect.size().toSize());
fbo = new QOpenGLFramebufferObject(size, format);
texture = this->window()->createTextureFromId(fbo->texture(), size);
textureNode = new QSGSimpleTextureNode();
textureNode->setRect(0, this->height(), this->width(), -height());
textureNode->setTexture(texture);
this->setFlag(QQuickItem::ItemHasContents, true);
updateViewport();
this->update();
}
void EventAdapter::updateFBO() {
if (fbo)
delete fbo;
QRectF rect = this->mapRectToItem(nullptr, boundingRect());
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
QSize size(rect.size().toSize());
fbo = new QOpenGLFramebufferObject(size, format);
if (texture)
delete texture;
texture = window()->createTextureFromId(fbo->texture(), size);
textureNode = new QSGSimpleTextureNode();
textureNode->setRect(0, height(), width(), -this->height());
textureNode->setTexture(texture);
this->update();
}
void EventAdapter::onWindowChanged(QQuickWindow *window)
{
if (!window)
{
return;
}
//std::cout << "onWindowChanged" << std::endl;
connect(window, SIGNAL(beforeRendering()),
this, SLOT(frame()), Qt::DirectConnection);
window->setClearBeforeRendering(false);
osg::ref_ptr<osgViewer::GraphicsWindowEmbedded> graphicsWindow
= new osgViewer::GraphicsWindowEmbedded(0, 0, window->width(), window->height());
graphicsWindow->setClearColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
viewer->getCamera()->setGraphicsContext(graphicsWindow);
updateViewport();
connect(window, SIGNAL(widthChanged(int)),
this, SLOT(updateViewport()));
connect(window, SIGNAL(heightChanged(int)),
this, SLOT(updateViewport()));
}
void EventAdapter::frame()
{
window()->update();
if(!fbo)
initFBO();
if(fbo)
{
fbo->bind();
}
QOpenGLContext::currentContext()->functions()->glUseProgram(0);
viewer->frame();
}
osgViewer::Viewer* EventAdapter::getViewer()
{
return viewer.get();
}
注:
由于Qml默认QSG_RENDER_LOOP是threaded,所以需要在环境变量设置QSG_RENDER_LOOP为basic或者window,要不然会有崩溃现象,关于QtQuick的渲染机制,具体可以参考QtQuick基础教程(四)—场景渲染(Scene Graph)
需要将EventAdapter或者其子类注册到Qml中
qmlRegisterType<SceneItem>("Hohai.Controls", 1, 0, "EventAdapter");