0

I want to use Qt scene graph to draw a grid. I haven't succeeded in research for a few days. Please help me, thank you!

The issue is:

  1. Why can't I show the results?
  2. Where do I call glViewport? or some other way?

I have followed the code and found that Qt called renderer->setViewportRect(rect) in QQuickWindowPrivate::renderSceneGraph();

But the scene graph uses the entire window as the drawing area instead of the custom QQuickItem object.

I recalculated the shader matrix, but it didn't work. I think it is ugly

source code

// grid_item.h
class GridItem : public QQuickItem
{
    Q_OBJECT

public:
    explicit GridItem(QQuickItem *parent = nullptr);

protected:
    QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) Q_DECL_OVERRIDE;
};

// grid_item.cpp
GridItem::GridItem(QQuickItem *parent) : QQuickItem (parent)
{
    setFlag(ItemHasContents, true);
}

QSGNode *GridItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
    QRectF rect = boundingRect();

    if (rect.isEmpty()) {
        delete oldNode;
        return nullptr;
    }

    QSGGeometryNode *node = nullptr;
    QSGGeometry *geometry = nullptr;
    GridItemMaterial *material = nullptr;

    if(!oldNode)
    {
        node = new QSGGeometryNode;
        node->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial, true);

        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 0);
        geometry->setDrawingMode(QSGGeometry::DrawLines);
        node->setGeometry(geometry);

        material = new GridItemMaterial;
        material->setFlag(QSGMaterial::RequiresDeterminant, true);
        node->setMaterial(material);
    }
    else
    {
        node = static_cast<QSGGeometryNode *>(oldNode);
        geometry = node->geometry();
        material = static_cast<GridItemMaterial *>(node->material());
    }

    int m_xAxisSegment {10};
    int m_yAxisSegment {10};
    const int totalVertices = (m_xAxisSegment+1)*2 + (m_yAxisSegment+1)*2;

    if(geometry->vertexCount() != totalVertices)
    {
        geometry->allocate(totalVertices);

        QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();

        for(int x=0; x<=m_xAxisSegment; x++)
        {
            float xPos = 1.0f*x/m_xAxisSegment;

            (*vertices++).set(xPos, 0.0f);
            (*vertices++).set(xPos, 1.0f);
        }

        for(int y=0; y<=m_yAxisSegment; y++)
        {
            float yPos = 1.0f*y/m_yAxisSegment;

            (*vertices++).set(0.0f, yPos);
            (*vertices++).set(1.0f, yPos);
        }

        node->markDirty(QSGNode::DirtyGeometry);
    }

    // calculate matrix for shader
    ConvertParameter param;
    param.windowWidth = 640;
    param.windowHeight = 480;

    param.contentX = 100;
    param.contentY = 100;
    param.contentWidth = 200;
    param.contentHeight = 200;

    param.glX = 0;
    param.glY = 0;
    param.glWidth = 1.0f;
    param.glHeight = 1.0f;

    material->m_convertParameter = param;

    return node;
}

// grid_item_material.h
class GridItemMaterial : public QSGMaterial
{
public:
    QSGMaterialType *type() const Q_DECL_OVERRIDE;
    QSGMaterialShader *createShader() const Q_DECL_OVERRIDE;

    ConvertParameter m_convertParameter;
};

// grid_item_material.cpp
QSGMaterialType *GridItemMaterial::type() const
{
    static QSGMaterialType type;
    return &type;
}

QSGMaterialShader *GridItemMaterial::createShader() const
{
    return new GridItemMaterialShader;
}

// grid_item_material_shader.h
class GridItemMaterialShader : public QSGMaterialShader
{
public:
    GridItemMaterialShader();

    const char *const *attributeNames() const Q_DECL_OVERRIDE;
    void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) Q_DECL_OVERRIDE;

protected:
    void initialize() Q_DECL_OVERRIDE;

    QMatrix4x4 getConvertMatrix(const ConvertParameter &param);

private:
    int m_id_mvpMatrix {-1};
    int m_id_gridlineColor {-1};
};

// grid_item_material_shader.cpp
GridItemMaterialShader::GridItemMaterialShader()
{
    setShaderSourceFile(QOpenGLShader::Vertex, ":/shaders/gridlines.vert");
    setShaderSourceFile(QOpenGLShader::Fragment, ":/shaders/gridlines.frag");
}

const char * const *GridItemMaterialShader::attributeNames() const
{
    static char const *const names[] = { "Vertex", 0 };
    return names;
}

void GridItemMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *)
{
    GridItemMaterial *material = static_cast<GridItemMaterial *>(newMaterial);

    QMatrix4x4 matrix = getConvertMatrix(material->m_convertParameter);
    program()->setUniformValue(m_id_mvpMatrix, matrix);

    program()->setUniformValue(m_id_gridlineColor, QColor::fromRgbF(1, 0, 0, 1));
}

void GridItemMaterialShader::initialize()
{
    m_id_mvpMatrix = program()->uniformLocation("mvpMatrix");
    m_id_gridlineColor = program()->uniformLocation("gridlineColor");
}

QMatrix4x4 GridItemMaterialShader::getConvertMatrix(const ConvertParameter &param)
{
    QMatrix4x4 model1;

    // convert window to (-1, -1)..(+1, +1)
    model1.setToIdentity();
    model1.translate(-1, -1, 0);
    model1.scale(2.0f/param.windowWidth, 2.0f/param.windowHeight, 1.0f);

    // left-bottom
    QVector4D v3(param.contentX, param.windowHeight-param.contentY-param.contentHeight, 0, 1);
    v3 = model1 * v3;

    // right-top
    QVector4D v4(param.contentX+param.contentWidth, param.windowHeight-param.contentY, 0, 1);
    v4 = model1 * v4;

    // content area should in (-1, -1)..(+1, +1)
    float width = v4.x() - v3.x();
    float height = v4.y() - v3.y();

    QMatrix4x4 model2;
    model2.setToIdentity();
    model2.translate(v3.x(), v3.y(), 0);
    model2.scale(width/param.glWidth, height/param.glHeight, 1);
    model2.translate(-param.glX, -param.glY, 0);

    return model2;
}

// grid_convert_parameter.h
struct ConvertParameter
{
    int windowWidth = 640;
    int windowHeight = 480;

    int contentX = 100;
    int contentY = 100;
    int contentWidth = 200;
    int contentHeight = 200;

    float glX = 3;
    float glY = 3;
    float glWidth = 4.0f;
    float glHeight = 4.0f;
};

// main.cpp
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<GridItem>("io.draw", 1, 0, "GridItem");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    QQuickWindow *window = static_cast<QQuickWindow *>(engine.rootObjects().first());

    QSurfaceFormat format = window->requestedFormat();
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setVersion(3, 3);
    window->setFormat(format);

    window->show();

    return app.exec();
}

// main.qml
import QtQuick 2.9
import QtQuick.Controls 2.4
import io.draw 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    GridItem {
        x: 100
        y: 100
        width: 200
        height: 200
    }
}

// gridlines.vert
#version 330 core

uniform mat4 mvpMatrix;

layout(location = 0) in vec2 Vertex;

void main(void)
{
    gl_Position = mvpMatrix * vec4(Vertex, 0.0, 1.0);
}

// gridlines.frag
#version 330 core

uniform vec4 gridlineColor;

layout(location = 0) out vec4 fragColor;

void main(void)
{
    fragColor = gridlineColor;
}

I have also made a simple change based on the Qt OpenGL demo.

class OpenGLWindow : public QWindow, protected QOpenGLFunctions_3_3_Core

Almost done the same thing, except that the results are output directly to the entire window (but this is not what I want)

Another difference is the transformation matrix changed:

QMatrix4x4 model, view, projection;
projection.ortho(0, 1, 0, 1, -10, 10);
m_program->setUniformValue(m_matrixUniform, projection*view*model);

It works properly...

Because it involves OpenGL and Qt Scene Graph, I don't know what went wrong.

fangxu
  • 13
  • 4
  • 1
    Thank you! I have simplified some code, but it is still very long :( – fangxu Sep 12 '18 at 07:51
  • 1
    Code involving OpenGL things tends to become long, usually. If you think that nothing can be removed without preventing to reproduce your issue then it's the Minimal CVE (independently of how long it is). If I remember right there is a size limit for text in questions (which you obviously have not exceeded). It seems the "fathers" were aware of that MCVE topic. ;-) – Scheff's Cat Sep 12 '18 at 07:57
  • I have solved finally! Here some bugs are recorded: 1. The scene graph setup the whole window rect as viewport rect. The quick item's matrix has to be calculated manually. 2. Material needs to use set as RequiresFullMatrix, otherwise invalid 3. The elements in a QMatrix4x4 are specified in row-major order! – fangxu Sep 13 '18 at 14:33
  • NIce to hear. So, you may write a self answer and even accept it (after 48 hours). – Scheff's Cat Sep 13 '18 at 14:37

0 Answers0