Research on the underlying Scene Graph of Qt new rendering (3), scenegraph

Source: Internet
Author: User

Research on the underlying Scene Graph of Qt new rendering (3), scenegraph
Research on the underlying Scene Graph of Qt new rendering (III)

The previous article introduced some theoretical content of Qt Quick and SceneGraph. This is my latest research achievement. Next I will introduce how to use Scene Graph to make some interesting results. This is also my attempt to perform SceneGraph.

My goal is to implement and rotate a texture cube under the rendering framework of Scene Graph. It took a few days, although not so satisfied, but it has come to an end.

Jiang caiyang's original article, first published at: http://blog.csdn.net/gamesdev/article/details/43196787. Welcome to the discussion.

This article is too difficult and suitable for experienced peers to communicate.

First, compare the code in C ++ and QML to a little simpler, so let's start with the simplest.

Import QtQuick 2.4 import QtQuick. Window 2.2 import TexturedCube 1.0 Window {title: qsTr ("Scene Graph Textured Object") width: isMobileDevice ()? Screen. width: 480 height: isMobileDevice ()? Screen. height: 320 visible: true Rectangle {anchors. fill: parent color: "orange" Cube {id: theCube anchors. centerIn: parent length: 60 source :".. /image/avatar.jpg "property int rotateAngle: 0 transform: [Rotation {angle: theCube. rotateAngle axis {x: 1 y: 1}] NumberAnimation on rotateAngle {from: 0 to: 360 duration: 1000 loops: Animation. infinite} Timer {interval: 3000 repeat: true property int tCount: 0 running: true onTriggered: {var sourceList = [".. /image/soul.png ",".. /image/avatar.jpg "]; theCube. source = sourceList [tCount ++ % 2] ;}}} function isMobileDevice () // determine whether it is a mobile platform {return Qt. platform. OS = "android" | Qt. platform. OS = "blackberry" | Qt. platform. OS = "ios" | Qt. platform. OS === "winphone ";}}

A normal window with an orange background shows our Cube. I want my Cube to rotate along one axis, so I set NumberAnimationon rotateAngle. In addition, I want to change the texture of the Cube every three seconds, so I set a Timer to replace the texture. Each Item has a transform Member, which indicates the type of conversion that the Item undergoes. Currently, transform supports Translation, Rotation, and Scale. Some people want to make the MouseArea irregular, in fact, it is more convenient to provide the Shear class officially.

The QML code we see is just a representation. In fact, behind the scenes, it is a complicated C ++ class: TexturedCube. Next let's take a look at the content of TexturedCube. h:


#ifndef TEXTUREDCUBE#define TEXTUREDCUBE#include <QUrl>#include <QQuickItem>#define DECLRARE_QUICKITEM_PROPERTY( aType, aProperty ) protected:\    aType m_ ## aProperty; public: \    aType aProperty( void ) { return m_ ## aProperty; } \    void set ## aProperty( aType _ ## aProperty ) \    {\        if ( m_ ## aProperty == _ ## aProperty ) return; \        m_ ## aProperty = _ ## aProperty; \        emit aProperty ## Changed( _ ## aProperty ); \        update( ); \    }class TexturedCube: public QQuickItem{    Q_OBJECT    Q_PROPERTY( qreal length READ Length                WRITE setLength NOTIFY LengthChanged )    Q_PROPERTY( QUrl source READ Source                WRITE setSource NOTIFY SourceChanged )public:    explicit TexturedCube( void );    QSGNode* updatePaintNode( QSGNode* oldNode,                              UpdatePaintNodeData* );private slots:    void checkTextureReconstruct( QUrl source );signals:    void LengthChanged( qreal length );    void SourceChanged( QUrl source );private:    QString source2Path( QUrl source );    QUrl        m_LastSource;    bool        m_TextureNeedsReconstruct;protected:    DECLRARE_QUICKITEM_PROPERTY( qreal, Length )    DECLRARE_QUICKITEM_PROPERTY( QUrl, Source )};#endif // TEXTUREDCUBE

In the header file, I first defined a convenient macro, including the attributes Getter-Setter and Notifier. Like most attributes of QQuickItem, once a property changes, a change signal is sent, that is, the notifier we call, And QQuickItem must be notified for the next update. Here, update () does not mean immediate update, but tells QQuickItem to call the updatePaintNode () function before the next cycle. Next let's take a look at the TexturedCube. cpp file.

// TexturedCube. cpp # include <QQmlFile> # include <QSGGeometryNode> # include <QSGGeometry> # include <QSGOpaqueTextureMaterial> # include <QQuickWindow> # include "TexturedCube. h "# define VERTEX_COUNT 36 TexturedCube: TexturedCube (void) {m_Length = 10.0; m_Source =" "; setFlag (ItemHasContents, true); connect (this, SIGNAL (SourceChanged (QUrl), this, SLOT (checkTextureReconstruct (QUrl);} struct Tex Vertex {QVector3D position; QVector2D texCoord;}; QSGNode * vertex: updatePaintNode (QSGNode * oldNode, QQuickItem: vertex *) {QSGGeometryNode * node = vertex; QSGGeometry * geometry = vertex; if (oldNode = Q_NULLPTR) {// create a ry static QSGGeometry: Attribute attributes [] = {QSGGeometry: Attribute: create (0, // subscript 3 of the array element, // The number of Yuan GL_FLOAT, // The type of the yuan is true), // is it a vertex? Set the element QSGGeometry: Attribute: create (1, // subscript 2 of the array element, // The number of elements GL_FLOAT, // The type of the element is false) // whether it is a vertex position element}; static QSGGeometry: AttributeSet attributeSet = {2, // number of attributes sizeof (TexturedCubeVertex ), // size of the personalized vertex structure attributes // attribute array}; geometry = new QSGGeometry (attributeSet, // Attribute Set VERTEX_COUNT ); // Number of vertices geometry-> setDrawingMode (GL_TRIANGLES); geometry-> setVertexDataPattern (QSGGeometry: DynamicP Attern); geometry-> allocate (VERTEX_COUNT); // create a material QImage image (source2Path (m_Source); QSGTexture * texture = window ()-> createTextureFromImage (image. mirrored (); texture-> setParent (window (); QSGOpaqueTextureMaterial * material = new material; material-> setTexture (texture); // create node = new QSGGeometryNode; node-> setGeometry (geometry); node-> setMaterial (material ); Node-> setFlag (QSGNode: OwnsGeometry); node-> setFlag (QSGNode: OwnsOpaqueMaterial); node-> markDirty (QSGNode: DirtyGeometry | QSGNode :: dirtyMaterial);} else {node = static_cast <QSGGeometryNode *> (oldNode); geometry = node-> geometry (); QSGNode: DirtyState dirtyState = QSGNode: DirtyGeometry; if (m_TextureNeedsReconstruct) {QSGOpaqueTextureMaterial * material = static_cast <QSGOpaqueText UreMaterial *> (node-> material (); if (material! = Q_NULLPTR) {QSGTexture * texture = material-> texture (); texture-> setParent (Q_NULLPTR); delete texture; QImage image (source2Path (m_Source); texture = window () -> createTextureFromImage (image. mirrored (); texture-> setParent (window (); material-> setTexture (texture); dirtyState | = QSGNode: DirtyMaterial ;}} node-> markDirty (dirtyState);} TexturedCubeVertex * v = static_cast <Textured CubeVertex *> (geometry-> vertexData (); // you can specify the vertex coordinate qreal semi = m_Length/2.0; const QVector3D basicVertices [] = {QVector3D (semi,-semi, semi), QVector3D (semi,-semi,-semi), QVector3D (-semi,-semi,-semi), QVector3D (-semi,-semi, semi ), QVector3D (semi, semi, semi), QVector3D (semi, semi,-semi), QVector3D (-semi, semi,-semi), QVector3D (-semi, semi, semi)}; // v [0]. position = BasicVertices [6]; v [0]. texCoord = QVector2D (0.0, 0.0); v [1]. position = basicVertices [2]; v [1]. texCoord = QVector2D (0.0, 1.0); v [2]. position = basicVertices [5]; v [2]. texCoord = QVector2D (1.0, 0.0); v [3]. position = basicVertices [2]; v [3]. texCoord = QVector2D (0.0, 1.0); v [4]. position = basicVertices [1]; v [4]. texCoord = QVector2D (1.0, 1.0); v [5]. position = basicVertices [5]; v [5]. texCoord = Q Vector2D (1.0, 0.0); // v [6]. position = basicVertices [4]; v [6]. texCoord = QVector2D (0.0, 0.0); v [7]. position = basicVertices [0]; v [7]. texCoord = QVector2D (0.0, 1.0); v [8]. position = basicVertices [7]; v [8]. texCoord = QVector2D (1.0, 0.0); v [9]. position = basicVertices [0]; v [9]. texCoord = QVector2D (0.0, 1.0); v [10]. position = basicVertices [3]; v [10]. texCoord = QVector2D (1.0, 1.0); v [11]. Position = basicVertices [7]; v [11]. texCoord = QVector2D (1.0, 0.0); // v [12] above. position = basicVertices [2]; v [12]. texCoord = QVector2D (0.0, 0.0); v [13]. position = basicVertices [3]; v [13]. texCoord = QVector2D (0.0, 1.0); v [14]. position = basicVertices [1]; v [14]. texCoord = QVector2D (1.0, 0.0); v [15]. position = basicVertices [3]; v [15]. texCoord = QVector2D (0.0, 1.0); v [16]. position = basicVe Rtices [0]; v [16]. texCoord = QVector2D (1.0, 1.0); v [17]. position = basicVertices [1]; v [17]. texCoord = QVector2D (1.0, 0.0); // v [18] below. position = basicVertices [7]; v [18]. texCoord = QVector2D (0.0, 0.0); v [19]. position = basicVertices [6]; v [19]. texCoord = QVector2D (0.0, 1.0); v [20]. position = basicVertices [4]; v [20]. texCoord = QVector2D (1.0, 0.0); v [21]. position = basicVertices [6]; v [21]. te XCoord = QVector2D (0.0, 1.0); v [22]. position = basicVertices [5]; v [22]. texCoord = QVector2D (1.0, 1.0); v [23]. position = basicVertices [4]; v [23]. texCoord = QVector2D (1.0, 0.0); // left v [24]. position = basicVertices [7]; v [24]. texCoord = QVector2D (0.0, 0.0); v [25]. position = basicVertices [3]; v [25]. texCoord = QVector2D (0.0, 1.0); v [26]. position = basicVertices [6]; v [26]. texCoord = QVector2D (1.0, 0.0); v [27]. position = basicVertices [3]; v [27]. texCoord = QVector2D (0.0, 1.0); v [28]. position = basicVertices [2]; v [28]. texCoord = QVector2D (1.0, 1.0); v [29]. position = basicVertices [6]; v [29]. texCoord = QVector2D (1.0, 0.0); // v [30] on the right. position = basicVertices [5]; v [30]. texCoord = QVector2D (0.0, 0.0); v [31]. position = basicVertices [1]; v [31]. texCoord = QVector2D (0.0, 1.0); v [32]. Position = basicVertices [4]; v [32]. texCoord = QVector2D (1.0, 0.0); v [33]. position = basicVertices [1]; v [33]. texCoord = QVector2D (0.0, 1.0); v [34]. position = basicVertices [0]; v [34]. texCoord = QVector2D (1.0, 1.0); v [35]. position = basicVertices [4]; v [35]. texCoord = QVector2D (1.0, 0.0); const QRectF bounding = boundingRect (); const float factor = m_Length; for (int I = 0; I <VERTEX_COUNT; + + I) // adjust the position {// here, because the coordinate system x increases to the right and y increases downward, which conforms to the screen coordinate system, it increases toward the inside according to the OpenGL right coordinate system. Float x = v [I]. position. x () + bounding. width ()/2; float y = v [I]. position. y () + bounding. height ()/2; float z = (v [I]. position. z () + semi)/factor; v [I]. position. setX (x); v [I]. position. setY (y); v [I]. position. setZ (z);} return node;} void TexturedCube: checkTextureReconstruct (QUrl source) {m_TextureNeedsReconstruct = m_LastSource! = Source;} QString TexturedCube: source2Path (QUrl source) {QUrl url (source); if (url. isRelative () {QUrl baseURL ("file: //" + qApp-> applicationFilePath (); url = baseURL. resolved (url);} return QQmlFile: urlToLocalFileOrQrc (url );}

After practice, it takes 36 vertices to render a cube with six textures. So I set a macro VERTEX_COUNT with a value of 36. In addition, the default rendering of QtScene Graph is AoS (Array of Structure), which is the same as that of Direct3D9, so we need to define our own vertex format. StructTexturedCubeVertex contains vertex positions and texture coordinates (uv coordinates ). In the TexturedCube constructor, we can set setFlag (ItemHasContents, true); To tell Scene Graph that this project has content and needs to call the updatePaintNode () function. When the source changes, to call the corresponding processing function, you need to connect to the processor. The core here is the updatePaintNode () function. This function is similar to a transfer station where trains come from a distance and enter the station. If there is any change, such as a passenger or a passenger, the personnel in the car will also change. The train leaves the station and leaves for the next destination. This function is quite interesting. For the first time, we found that the oldNode pointer was empty, so we created the ry, material, and texture. Otherwise, the oldNode pointer is not empty. We don't think this is the first rendering. We take out its ry and texture. If the texture path changes, we also need to reload it and parse the path through source2Path. This may not be as complicated as the Qt source code, because if Qt comes from the network, that is, starting with http, QNetworkAccessManager in QQmlEngine needs to be called. In addition, the Image class of QtQuick also has the cache function, this example is not so complex. You can change the texture content synchronously based on the source changes.

Due to the flexibility of Qt SceneGraph, we need to use many classes to write together to create the ry as required. First, we need to create an array of QSGGeometry: Attribute to tell SceneGraph and OpenGL what vertex cache we need. Note that this array cannot be on the stack, because it is used outside the updatePaintNode () function, so I mark it as static. Then, we need the QSGGeometry: AttributeSet structure object to identify the attributes we need.

After the coordinates of the top, bottom, left, right, front, and back vertices and texture coordinates are set, the coordinates must be adapted once. Because QQuickItem is a rectangle control after all, after width and height are specified, boundingRect () has a corresponding value. Here we need to describe the z value. As described in the previous articles, the coordinate value must be limited to x, 0, Screen. width], y, [0, Screen. height], z, [0, stackLayer] (where stackLayer is the number of layers of the Item stack), so that you can see it in the viewport. Finally, the source2Path () method of Qt is used to process the path.

The main. cpp file is also relatively simple. Here I will only introduce the main parts:

...... Int main (int argc, char ** argv) {QApplication app (argc, argv );...... // Register the qmlRegisterType class <TexturedCube> ("TexturedCube", 1, 0, "Cube"); QQmlApplicationEngine; engine. load (QUrl ("qrc: // qml/main. qml "));...... Return app.exe c ();}

The final result is the effect. The results in Windows are as follows:


It may be strange that it was originally a cube? How does it look like a patch? In fact, if we set QSG_VISUALIZE = overdraw, we will understand.

In fact, there is a cube shown, just because the range of the cube is xε [0, Screen. width], y, [0, Screen. height], z, [0, stackLayer] (where stackLayer is the number of layers of the Item stack). In this case, the left and right sides and the upper and lower sides are squashed very small, so we almost don't feel their existence.

Note: For this reason, I realized that it is not appropriate to draw a 3D model under Scene Graph. Due to the limitations of z and the viewport, we can see that 3D objects are flat. Maybe the example I made six months ago is more practical.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.