pawel
pawel

Reputation: 231

Issue with drawing an Qml Item with raw OpenGL calls

I want to draw a single item in QtQuick scene using raw OpenGL calls. I have decided to take approach suggested in this question.

I have created a Qt Quick item deriving from QQuickFramebufferObject and exposed it to QML as Renderer: (code is based on Qt example: Scene Graph - Rendering FBOs)

class FboInSGRenderer : public QQuickFramebufferObject {
    Q_OBJECT
public:
    Renderer *createRenderer() const;
};

source file:

class LogoInFboRenderer : public QQuickFramebufferObject::Renderer {
    public:
        LogoInFboRenderer() { }

        void render() {
            int width = 1, height = 1;
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glColor4f(0.0, 1.0, 0.0, 0.8);
            glBegin(GL_QUADS);
            glVertex2f(0, 0);
            glVertex2f(width, 0);
            glVertex2f(width, height);
            glVertex2f(0, height);
            glEnd();

            glLineWidth(2.5);
            glColor4f(0.0, 0.0, 0.0, 1.0);
            glBegin(GL_LINES);
            glVertex2f(0, 0);
            glVertex2f(width, height);
            glVertex2f(width, 0);
            glVertex2f(0, height);
            glEnd();

            update();
        }

        QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) {
            QOpenGLFramebufferObjectFormat format;
            format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
            format.setSamples(4);
            return new QOpenGLFramebufferObject(size, format);
        }
};

QQuickFramebufferObject::Renderer *FboInSGRenderer::createRenderer() const {
    return new LogoInFboRenderer();
}

In Qml I use it as follows:

import QtQuick 2.4
import SceneGraphRendering 1.0

Rectangle {
    width: 400
    height: 400
    color: "purple"
    Renderer {
        id: renderer
        anchors.fill: parent
    }
}

I was expecting to see that rendered "X" will fill entire scene, but instead I get the result presented below:

enter image description here

Other experiments seem to confirm that drew shape has always it's size (width/height) divided by 2.

I also checked that size parameter in createFramebufferObject has correct value.

Looking into docs led me to property textureFollowsItemSize in QQuickFramebufferObject class but it is by default set to true.

Am I doing something wrong or should I consider this behavior as Qt bug?

Upvotes: 12

Views: 3112

Answers (2)

Michał W. Urbańczyk
Michał W. Urbańczyk

Reputation: 1493

The drawn rectangle is half the sizes you expect because the default coordinate range is [-1, 1], not [0, 1] as your code assumes. If you want to use [0, 1] scale, then you should appropriately set the projection matrix:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);

Upvotes: 3

folibis
folibis

Reputation: 12854

As Qt documentation says: "Warning: It is crucial that OpenGL operations and interaction with the scene graph happens exclusively on the rendering thread, primarily during the updatePaintNode() call. The best rule of thumb is to only use classes with the "QSG" prefix inside the QQuickItem::updatePaintNode() function." I do it in this way:

*.h

class MyQuickItem : public QQuickItem
{
    Q_OBJECT
public:
    MyQuickItem();
    ~MyQuickItem();

protected:
    QSGNode *updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData);
    QSGNode *addNode(QSGGeometry *geometry, const QColor &color);
};

*.cpp

MyQuickItem::MyQuickItem()
{
    setFlag(QQuickItem::ItemHasContents,true);
}

MyQuickItem::~MyQuickItem()
{

}

QSGNode *MyQuickItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
{
    Q_UNUSED(updatePaintNodeData)

    QSGTransformNode *root = static_cast<QSGTransformNode *>(oldNode);
    if(!root) root = new QSGTransformNode;
    QSGNode *node;
    QSGGeometry *geometry;

    QSGSimpleRectNode *rect = new QSGSimpleRectNode();
    rect->setColor(Qt::green);
    rect->setRect(boundingRect());
    root->appendChildNode(rect);

    geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
    geometry->setDrawingMode(GL_LINES);
    geometry->setLineWidth(5.0);
    geometry->vertexDataAsPoint2D()[0].set(x(), y());
    geometry->vertexDataAsPoint2D()[1].set(width(), height());
    node = addNode(geometry,Qt::blue);
    root->appendChildNode(node);

    geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
    geometry->setDrawingMode(GL_LINES);
    geometry->setLineWidth(5.0);
    geometry->vertexDataAsPoint2D()[0].set(width(), y());
    geometry->vertexDataAsPoint2D()[1].set(x(), height());
    node = addNode(geometry,Qt::blue);
    root->appendChildNode(node);

    return root;
}

QSGNode *MyQuickItem::addNode(QSGGeometry *geometry, const QColor &color)
{
    QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
    material->setColor(color);
    QSGGeometryNode *node = new QSGGeometryNode;
    node->setGeometry(geometry);
    node->setFlag(QSGNode::OwnsGeometry);
    node->setMaterial(material);
    node->setFlag(QSGNode::OwnsMaterial);
    return node;
}

In main.cpp

qmlRegisterType<MyQuickItem>("MyObjects", 1, 0, "MyObject");

And usage:

import QtQuick 2.3
import QtQuick.Window 2.2
import MyObjects 1.0    

Window {
    visible: true
    width: 360
    height: 360

    MyObject {
        anchors.fill: parent
    }
}

Upvotes: 0

Related Questions