Another version!
This time I'm using
QSGSimpleTextureNode, doing things (almost) right.
In fact it solved almost all of the issues of the previous solution and it's now perfectly integrated in the QtQuick scene graph instead of simply copying the render content on the window background.
view3d.hCode:
#ifndef VIEW3D_H
#define VIEW3D_H
#include <QQuickItem>
#include <QOpenGLFramebufferObject>
#include <Horde3D.h>
class View3D : public QQuickItem
{
Q_OBJECT
private:
int m_timerID;
QOpenGLFramebufferObject* m_fbo;
H3DNode m_cam;
public:
View3D(
QQuickItem* parent = 0
);
virtual ~View3D();
protected:
QSGNode* updatePaintNode(QSGNode* old, UpdatePaintNodeData* data);
void timerEvent(QTimerEvent* evt);
private slots:
void cleanup();
};
#endif // VIEW3D_H
view3d.cppCode:
#include "view3d.h"
#include <QQuickWindow>
#include <QOpenGLContext>
#include <QSGSimpleTextureNode>
#include <Horde3DUtils.h>
View3D::View3D(QQuickItem* parent)
: QQuickItem(parent),
m_timerID(0),
m_fbo(0),
m_cam(0)
{
this->setFlag(QQuickItem::ItemHasContents);
}
View3D::~View3D()
{
this->cleanup();
}
void View3D::timerEvent(QTimerEvent* evt)
{
if (evt && evt->timerId() == m_timerID)
this->update();
}
QSGNode* View3D::updatePaintNode(QSGNode* node, UpdatePaintNodeData* data)
{
QSGSimpleTextureNode* textureNode = static_cast<QSGSimpleTextureNode*>(node);
if (!textureNode)
textureNode = new QSGSimpleTextureNode();
// Push Qt state.
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
glPushAttrib(GL_ALL_ATTRIB_BITS);
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glShadeModel(GL_FLAT);
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);
glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Render Horde3D.
int x = this->x();
int y = this->y();
int width = this->width();
int height = this->height();
delete m_fbo;
m_fbo = 0;
if (width && height)
{
m_fbo = new QOpenGLFramebufferObject(width, height);
textureNode->setTexture(this->window()->createTextureFromId(m_fbo->texture(), m_fbo->size()));
}
else
{
textureNode->setTexture(this->window()->createTextureFromId(0, QSize(0,0)));
}
textureNode->setRect(this->boundingRect());
// Flip Y-axis between Horde3D and GLSL.
QMatrix4x4 flipY;
flipY.translate(width*0.5, height*0.5);
flipY.scale(1.0, -1.0);
flipY.translate(-width*0.5, -height*0.5);
data->transformNode->setMatrix(flipY);
if (m_fbo)
{
m_fbo->bind();
if (!m_cam)
{
// Initialize engine.
h3dInit();
h3dSetOption(H3DOptions::LoadTextures, 1);
h3dSetOption(H3DOptions::TexCompression, 0);
h3dSetOption(H3DOptions::FastAnimation, 0);
h3dSetOption(H3DOptions::MaxAnisotropy, 4);
h3dSetOption(H3DOptions::ShadowMapSize, 2048);
// Add resources.
H3DRes pipelineRes = h3dAddResource(H3DResTypes::Pipeline, "pipelines/forward.pipeline.xml", 0);
H3DRes envRes = h3dAddResource(H3DResTypes::SceneGraph, "models/platform/platform.scene.xml", 0);
H3DRes modelRes = h3dAddResource(H3DResTypes::SceneGraph, "models/knight/knight.scene.xml", 0);
// Load added resources.
h3dutLoadResourcesFromDisk("data");
// Add an environment.
H3DNode env = h3dAddNodes(H3DRootNode, envRes);
h3dSetNodeTransform(env, 0, 0, 0, 0, 0, 0, 20, 20, 20 );
// Add a model.
H3DNode model = h3dAddNodes(H3DRootNode, modelRes);
h3dSetNodeTransform(model, 0, 0, 0, 0, 180, 0, 1, 1, 1 );
// Add a light source.
H3DNode light = h3dAddLightNode( H3DRootNode, "Light1", 0, "LIGHTING", "SHADOWMAP" );
h3dSetNodeTransform( light, 0, 15, 40, 0, 0, 0, 100, 100, 100 );
h3dSetNodeParamF( light, H3DLight::RadiusF, 0, 1000 );
h3dSetNodeParamF( light, H3DLight::FovF, 0, 90 );
h3dSetNodeParamI( light, H3DLight::ShadowMapCountI, 1 );
h3dSetNodeParamF( light, H3DLight::ShadowMapBiasF, 0, 0.01f );
h3dSetNodeParamF( light, H3DLight::ColorF3, 0, 1.0f );
h3dSetNodeParamF( light, H3DLight::ColorF3, 1, 0.8f );
h3dSetNodeParamF( light, H3DLight::ColorF3, 2, 0.7f );
h3dSetNodeParamF( light, H3DLight::ColorMultiplierF, 0, 1.0f );
// Add a camera.
m_cam = h3dAddCameraNode(H3DRootNode, "Camera", pipelineRes);
h3dSetNodeTransform(m_cam, 0, 30, 100, 0, 0, 0, 1, 1, 1 );
QObject::connect(QOpenGLContext::currentContext(), SIGNAL(aboutToBeDestroyed()), this, SLOT(cleanup()), Qt::DirectConnection);
}
// Setup viewport and render target sizes.
h3dSetNodeParamI(m_cam, H3DCamera::ViewportXI, 0);
h3dSetNodeParamI(m_cam, H3DCamera::ViewportYI, 0);
h3dSetNodeParamI(m_cam, H3DCamera::ViewportWidthI, width);
h3dSetNodeParamI(m_cam, H3DCamera::ViewportHeightI, height);
h3dSetupCameraView(m_cam, 45.0f, (float)width / height, 0.1f, 1000.0f);
h3dResizePipelineBuffers(h3dGetNodeParamI(m_cam, H3DCamera::PipeResI), width, height);
// Render scene.
h3dRender(m_cam);
// Finish rendering of frame.
h3dFinalizeFrame();
m_fbo->release();
// Restore (pop) Qt state.
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glPopAttrib();
glPopClientAttrib();
}
if (!m_timerID)
m_timerID = this->startTimer(16);
return textureNode;
}
void View3D::cleanup()
{
this->killTimer(m_timerID);
m_timerID = 0;
if (m_cam)
{
h3dRelease();
m_cam = 0;
delete m_fbo;
m_fbo = 0;
}
}
main.cppCode:
#include "view3d.h"
#include <QQuickView>
#include <QGuiApplication>
int main(int argc, char** argv)
{
QGuiApplication app(argc, argv);
qmlRegisterType<View3D>("View3DToolkit", 1, 0, "View3D");
QQuickView win;
win.setSource(QUrl("main.qml"));
win.setResizeMode(QQuickView::SizeRootObjectToView);
win.show();
return app.exec();
}
main.qmlCode:
import QtQuick 2.1
import View3DToolkit 1.0
Item {
width: 320
height: 480
View3D {
anchors.fill: parent
Rectangle {
color: Qt.rgba(0, 1, 0, 0.5);
width: 100
height: 100
anchors.centerIn: parent
}
}
Rectangle {
color: Qt.rgba(1, 1, 1, 0.7)
radius: 10
border.width: 1
border.color: "white"
anchors.fill: label
anchors.margins: -10
}
Text {
id: label
color: "black"
wrapMode: Text.WordWrap
text: "The background here is a squircle rendered with raw OpenGL using the 'beforeRender()' signal in QQuickWindow. This text label and its border is rendered using QML"
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: 20
}
}