Horde3D
http://www.horde3d.org/forums/

Qt 5.1 (Qt Quick 2.1) & Horde3D
http://www.horde3d.org/forums/viewtopic.php?f=1&t=1895
Page 1 of 1

Author:  Zuck [ 08.08.2013, 04:14 ]
Post subject:  Qt 5.1 (Qt Quick 2.1) & Horde3D

Hi guys,

I'm trying to integrate Horde3D (from horde3d-x branch) inside the new Qt Quick 2(.1) system of Qt 5(.1).

This is the current code:

view3d.h
Code:
#ifndef VIEW3D_H
#define VIEW3D_H

#include <QQuickItem>
#include <QDateTime>
#include <Horde3D.h>

class View3D : public QQuickItem
{
    Q_OBJECT

protected:
    H3DRes  m_pipeline;
    H3DNode m_cam;

public:
    View3D(
        QQuickItem* parent = 0
    );
    virtual ~View3D();

private slots:
    void onPaint();
    void onRendered();
    void onWindowChanged(QQuickWindow* win);

signals:
    void rendered();
};

#endif // VIEW3D_H


view3d.cpp
Code:
#include "view3d.h"
#include <QQuickWindow>
#include <Horde3DUtils.h>

View3D::View3D(QQuickItem* parent)
   :    QQuickItem(parent),
        m_cam(0)
{
    this->setObjectName("View3D");

    QObject::connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(onWindowChanged(QQuickWindow*)));
    QObject::connect(this, SIGNAL(rendered()), this, SLOT(onRendered()), Qt::DirectConnection);
}

View3D::~View3D()
{
    h3dRelease();
}

void View3D::onPaint()
{
    // Initialize engine.
    h3dInit();

    if (!m_cam)
    {
        // Add a pipeline resource.
        m_pipeline = h3dAddResource(H3DResTypes::Pipeline, "pipelines/forward.pipeline.xml", 0);

        // Add a model.
        H3DRes modelRes = h3dAddResource(H3DResTypes::SceneGraph, "models/knight/knight.scene.xml", 0);
        H3DNode model = h3dAddNodes(H3DRootNode, modelRes);

        // Add a light source.
        H3DNode light = h3dAddLightNode(H3DRootNode, "Light1", 0, "LIGHTING", "SHADOWMAP");
        h3dSetNodeTransform(light, 0, 20, 0, 0, 0, 0, 1, 1, 1);
        h3dSetNodeParamF(light, H3DLight::RadiusF, 0, 50.0f);

        // Add a camera
        m_cam = h3dAddCameraNode(H3DRootNode, "Camera", m_pipeline);

        // Load added resources.
        h3dutLoadResourcesFromDisk("");
    }

    int x = this->x();
    int y = this->y();
    int width = this->width();
    int height = this->height();

    // Setup viewport and render target sizes
    h3dSetNodeParamI(m_cam, H3DCamera::ViewportXI, x);
    h3dSetNodeParamI(m_cam, H3DCamera::ViewportYI, y);
    h3dSetNodeParamI(m_cam, H3DCamera::ViewportWidthI, width);
    h3dSetNodeParamI(m_cam, H3DCamera::ViewportHeightI, height);
    h3dSetupCameraView(m_cam, 45.0f, (float)width / height, 0.5f, 2048.0f);
    h3dResizePipelineBuffers(m_pipeline, width, height);

    // Render scene
    h3dRender(m_cam);

    // Finish rendering of frame
    h3dFinalizeFrame();

    emit rendered();
}

void View3D::onRendered()
{
    this->window()->update();
}

void View3D::onWindowChanged(QQuickWindow* win)
{
    if (win)
    {
        QObject::connect(win, SIGNAL(beforeRendering()), this, SLOT(onPaint()), Qt::DirectConnection);

        win->setClearBeforeRendering(false);
    }
}



main.cpp
Code:
#include "view3d.h"
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickWindow>
#include <QApplication>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    app.setApplicationName(QObject::tr("Qt5 + Horde3D-x"));

    qmlRegisterType<View3D>("View3D", 1, 0, "View3D");

    QQmlApplicationEngine engine(QUrl("main.qml"));
    QObject* topLevel = engine.rootObjects().value(0);
    QQuickWindow* win = qobject_cast<QQuickWindow*>(topLevel);

    if (!win)
    {
        qWarning("Error: not a valid window.");
        return -1;
    }

    win->showMaximized();

    return app.exec();
}


main.qml
Code:
import QtQuick 2.1
import QtQuick.Controls 1.0
import View3D 1.0

ApplicationWindow {
    id: main

    menuBar: MenuBar {

        Menu {
            title: qsTr("&File")

            MenuItem {
                text: qsTr("&New Project")
                shortcut: "Ctrl+N"
            }
        }
    }

    View3D {
        id: centralArea

        anchors.fill: parent
    }

    Rectangle {
        width: 200
        height: 200
        color: "#ffffff"
    }
}


The problem is that after h3dInit call, something goes wrong with the OpenGL context and the ApplicationWindow is rendered all black (also the menubar becomes black) and Horde3D doesn't render anything.

The front Rectangle element, instead, is rendered correctly.

Any hints?

Thanks,
Emanuele

Author:  attila [ 09.08.2013, 12:50 ]
Post subject:  Re: Qt 5.1 (Qt Quick 2.1) & Horde3D

I haven't used Qt for years, so probably I am not the best person to help.

What i would try:
- You should call h3dInit only once / not per frame (before any other h3d call)
- In order to successfully initialize the engine the calling application must provide a valid OpenGL context.
this could be done using h3dut on windows or using qt for creating the context, I found this: http://qt-project.org/forums/viewthread/4109 maybe you should subclass QGLWidget.

Hope it helps

Author:  Zuck [ 15.08.2013, 07:13 ]
Post subject:  Re: Qt 5.1 (Qt Quick 2.1) & Horde3D

Hi attila,

thanks for your reply.

BTW the approach you suggested is valid for Qt4 (is still valid for Qt5, but only if you want to use widgets), but my purpose is to integrate Horde3D inside the new QtQuick (2.1) framework which is OpenGL-based (so the OGL context is already there).

Author:  Zuck [ 17.08.2013, 00:41 ]
Post subject:  Re: Qt 5.1 (Qt Quick 2.1) & Horde3D

Here it is a first working prototype :)

It's not perfect and has many limitations, but it's a starting point to reach something more cool!

Feel free to use it:

view3d.h
Code:
#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:
    void timerEvent(QTimerEvent* evt);

private slots:
    void paint();
    void cleanup();
    void handleWindowChanged(QQuickWindow* win);
};

#endif // VIEW3D_H


view3d.cpp
Code:
#include "view3d.h"
#include <QQuickWindow>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <Horde3DUtils.h>

View3D::View3D(QQuickItem* parent)
   :    QQuickItem(parent),
        m_timerID(0),
        m_fbo(0),
        m_cam(0)
{
    this->setFlag(QQuickItem::ItemHasContents);

    QObject::connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*)));
}

View3D::~View3D()
{
    this->cleanup();
}

void View3D::timerEvent(QTimerEvent* evt)
{
    if (evt && evt->timerId() == m_timerID)
        this->window()->update();
}

void View3D::paint()
{
    // Save (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);

    // Clear back surface.
    glClearColor(0,0,0,1);
    glClear(GL_COLOR_BUFFER_BIT);

    // 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);

    if (m_fbo)
    {
        QOpenGLFunctions glFunctions(QOpenGLContext::currentContext());
        glFunctions.glUseProgram(0);

        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 modelRes = h3dAddResource(H3DResTypes::SceneGraph, "models/platform/platform.scene.xml", 0);

            // Load added resources.
            h3dutLoadResourcesFromDisk("data/");

            // Add an environment.
            H3DNode model = h3dAddNodes(H3DRootNode, modelRes);
            h3dSetNodeTransform(model, 0, -20, 0, 0, 0, 0, 20, 20, 20 );

            // Add a light source.
            H3DNode light = h3dAddLightNode( H3DRootNode, "Light1", 0, "LIGHTING", "SHADOWMAP" );
            h3dSetNodeTransform( light, 0, 15, 10, -60, 0, 0, 1, 1, 1 );
            h3dSetNodeParamF( light, H3DLight::RadiusF, 0, 30 );
            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);

            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();
        m_fbo->blitFramebuffer(this->window()->renderTarget(), m_fbo);

        // Restore (pop) Qt state.
        glMatrixMode(GL_TEXTURE);
        glPopMatrix();
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
        glPopAttrib();
        glPopClientAttrib();
    }
}

void View3D::cleanup()
{
    this->handleWindowChanged(0);

    if (m_cam)
    {
        h3dRelease();
        m_cam = 0;

        delete m_fbo;
        m_fbo = 0;
    }
}

void View3D::handleWindowChanged(QQuickWindow *win)
{
    if (m_timerID)
    {
        this->killTimer(m_timerID);

        m_timerID = 0;
    }

    if (win)
    {
        QObject::connect(win, SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);

        win->setClearBeforeRendering(false);

        m_timerID = this->startTimer(16);
    }
}

Author:  Zuck [ 17.08.2013, 08:27 ]
Post subject:  Re: Qt 5.1 (Qt Quick 2.1) & Horde3D

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.h
Code:
#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.cpp
Code:
#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.cpp
Code:
#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.qml
Code:
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
    }
}

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/