Tutorial - Setup Horde with Qt5 & QtQuick 2.1

From Horde3D Wiki
Jump to: navigation, search
Before you start, if you don't know what QtQuick is, you should read the QtQuick Documentation:

http://qt-project.org/doc/qt-5.1/qtquick/qtquick-index.html (Substitute correct version of your Qt distribution).

Horde3D library should be already installed for build scripts to succeed.

QtHorde3D.h
#ifndef QTHORDE3D_H
#define QTHORDE3D_H

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

class QtHorde3D: public QQuickItem
{
    Q_OBJECT

private:
    int m_timerID;
    QOpenGLFramebufferObject* m_fbo;
    H3DNode m_cam;

public:
    QtHorde3D(QQuickItem* parent = 0);
    ~QtHorde3D();

protected:
    QSGNode* updatePaintNode(QSGNode* old, UpdatePaintNodeData* data);
    void timerEvent(QTimerEvent* evt);

private slots:
    void cleanup();
};

#endif // QTHORDE3D_H
QtHorde3D.cpp
#include "QtHorde3D.h"
#include <QQuickWindow>
#include <QOpenGLContext>
#include <QSGSimpleTextureNode>
#include <Horde3DUtils.h>

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

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

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

QSGNode* QtHorde3D::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("Content");

            // 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 QtHorde3D::cleanup()
{
    this->killTimer(m_timerID);
    m_timerID = 0;

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

        delete m_fbo;
        m_fbo = 0;
    }
}
main.qml
import QtQuick 2.1
import Horde3D 1.0

Item {
    id: main

    Horde3D {
        id: centralArea

        anchors.fill: 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 scene rendered with Horde3D. 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
    }
}
main.cpp
#include "QtHorde3D.h"
#include <QQuickView>
#include <QGuiApplication>

int main(int argc, char** argv)
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<QtHorde3D>("Horde3D", 1, 0, "Horde3D");

    QQuickView win;
    win.setSource(QUrl("main.qml"));
    win.setResizeMode(QQuickView::SizeRootObjectToView);
    win.show();

    return app.exec();
}

QMake project file:

h3d_qt5.pro
TEMPLATE = app
TARGET = h3d_qt5
DEPENDPATH += .
INCLUDEPATH += .
LIBS += -lHorde3D -lHorde3DUtils
QT += qml quick

# Input
HEADERS += QtHorde3D.h
SOURCES += QtHorde3D.cpp main.cpp

Note: Linkage order for your application

CMake project file:

CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9)

PROJECT(h3d_qt5)

FIND_PACKAGE(Qt5Quick)

SET(CMAKE_AUTOMOC ON)
SET(h3d_qt5_SOURCES QtHorde3D.cpp main.cpp)

ADD_EXECUTABLE(Qt5Horde3D ${h3d_qt5_SOURCES})

QT5_USE_MODULES(Qt5Horde3D Qml Quick)
TARGET_LINK_LIBRARIES(Qt5Horde3D Horde3D Horde3DUtils)

The result should look like this

Horde With Qt5 & QtQuick 2.1
H3DPlaceHolder.png
This tutorial introduces the use of Horde3D with Qt5 & QtQuick 2.1
Version: 1.0
Compatible with Horde3D: 1.0 beta 5
Release date: 2013-09-06
Author(s): Zuck