<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!-- textureandlight.qdoc --> <title>Lit and Textured Cube Example | Qt Canvas 3D 5.9</title> <link rel="stylesheet" type="text/css" href="style/offline-simple.css" /> <script type="text/javascript"> document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css"); // loading style sheet breaks anchors that were jumped to before // so force jumping to anchor again setTimeout(function() { var anchor = location.hash; // need to jump to different anchor first (e.g. none) location.hash = "#"; setTimeout(function() { location.hash = anchor; }, 0); }, 0); </script> </head> <body> <div class="header" id="qtdocheader"> <div class="main"> <div class="main-rounded"> <div class="navigationbar"> <table><tr> <td >Qt 5.9</td><td ><a href="qtcanvas3d-index.html">Qt Canvas 3D</a></td><td ><a href="qtcanvas3d-examples.html">Qt Canvas 3D Examples</a></td><td >Lit and Textured Cube Example</td></tr></table><table class="buildversion"><tr> <td id="buildversion" width="100%" align="right">Qt 5.9.4 Reference Documentation</td> </tr></table> </div> </div> <div class="content"> <div class="line"> <div class="content mainContent"> <div class="sidebar"> <div class="toc"> <h3><a name="toc">Contents</a></h3> <ul> <li class="level1"><a href="#qt-quick-implementation">Qt Quick Implementation</a></li> <li class="level2"><a href="#creating-canvas3d">Creating Canvas3D</a></li> <li class="level2"><a href="#importing-the-javascript-file">Importing the JavaScript File</a></li> <li class="level1"><a href="#javascript-implementation">JavaScript Implementation</a></li> <li class="level2"><a href="#matrix-library">Matrix Library</a></li> <li class="level2"><a href="#initializegl-function">initializeGL Function</a></li> <li class="level2"><a href="#paintgl-function">paintGL Function</a></li> <li class="level1"><a href="#logging">Logging</a></li> </ul> </div> <div class="sidebar-content" id="sidebar-content"></div></div> <h1 class="title">Lit and Textured Cube Example</h1> <span class="subtitle"></span> <!-- $$$textureandlight-description --> <div class="descr"> <a name="details"></a> <p>The Lit and Textured Cube example goes through the basics of using Qt Canvas 3D.</p> <p class="centerAlign"><img src="images/textureandlight-example.png" alt="" /></p><a name="qt-quick-implementation"></a> <h2 id="qt-quick-implementation">Qt Quick Implementation</h2> <a name="creating-canvas3d"></a> <h3 >Creating Canvas3D</h3> <p>In <a href="qtcanvas3d-textureandlight-qml-textureandlight-main-qml.html">main.qml</a>, we add a <a href="qml-qtcanvas3d-canvas3d.html">Canvas3D</a> under the root <code>Item</code>:</p> <pre class="qml"> <span class="type"><a href="qml-qtcanvas3d-canvas3d.html">Canvas3D</a></span> { <span class="name">id</span>: <span class="name">canvas3d</span> <span class="name">anchors</span>.fill:<span class="name">parent</span> ... </pre> <p>Inside it, we catch the <code>initializeGL</code> and <code>paintGL</code> signals to forward the initialization and rendering calls to the js object:</p> <pre class="qml"> <span class="comment">// Emitted when one time initializations should happen</span> <span class="name">onInitializeGL</span>: { <span class="name">GLCode</span>.<span class="name">initializeGL</span>(<span class="name">canvas3d</span>); } <span class="comment">// Emitted each time Canvas3D is ready for a new frame</span> <span class="name">onPaintGL</span>: { <span class="name">GLCode</span>.<span class="name">paintGL</span>(<span class="name">canvas3d</span>); } </pre> <a name="importing-the-javascript-file"></a> <h3 >Importing the JavaScript File</h3> <p>We import the JavaScript file in the QML:</p> <pre class="qml"> import "textureandlight.js" as GLCode </pre> <p>In the <code>initializeGL</code> function of the JavaScript, we initialize the OpenGL state. We also create the <a href="qml-qtcanvas3d-textureimage.html">TextureImage</a> and register handlers for image load success and fail signals. If the load succeeds, the OpenGL texture is created and filled with pixel data from the loaded image.</p> <a name="javascript-implementation"></a> <h2 id="javascript-implementation">JavaScript Implementation</h2> <a name="matrix-library"></a> <h3 >Matrix Library</h3> <p>In <a href="qtcanvas3d-textureandlight-qml-textureandlight-textureandlight-js.html">textureandlight.js</a>, we first include a fast matrix library. Using this makes it a lot easier to handle 3D math operations such as matrix transformations:</p> <pre class="js"> <span class="name">Qt</span>.<span class="name">include</span>(<span class="string">"gl-matrix.js"</span>) </pre> <a name="initializegl-function"></a> <h3 >initializeGL Function</h3> <p>Let's take a closer look at the <code>initializeGL</code> function. It is called by <a href="qml-qtcanvas3d-canvas3d.html">Canvas3D</a> once the render node is ready.</p> <p>First of all, we need to get a <a href="qml-qtcanvas3d-context3d.html">Context3D</a> from our <a href="qml-qtcanvas3d-canvas3d.html">Canvas3D</a>. We want a context that supports depth buffer and antialising:</p> <pre class="js"> <span class="comment">// Get the OpenGL context object that represents the API we call</span> <span class="name">gl</span> <span class="operator">=</span> <span class="name">canvas</span>.<span class="name">getContext</span>(<span class="string">"canvas3d"</span>, {depth:<span class="number">true</span>, antialias:<span class="number">true</span>, alpha:<span class="number">false</span>}); </pre> <p>Then we initialize the OpenGL state for the context:</p> <pre class="js"> <span class="comment">// Setup the OpenGL state</span> <span class="name">gl</span>.<span class="name">enable</span>(<span class="name">gl</span>.<span class="name">DEPTH_TEST</span>); <span class="name">gl</span>.<span class="name">depthFunc</span>(<span class="name">gl</span>.<span class="name">LESS</span>); <span class="name">gl</span>.<span class="name">enable</span>(<span class="name">gl</span>.<span class="name">CULL_FACE</span>); <span class="name">gl</span>.<span class="name">cullFace</span>(<span class="name">gl</span>.<span class="name">BACK</span>); <span class="name">gl</span>.<span class="name">clearColor</span>(<span class="number">0.98</span>, <span class="number">0.98</span>, <span class="number">0.98</span>, <span class="number">1.0</span>); <span class="name">gl</span>.<span class="name">clearDepth</span>(<span class="number">1.0</span>); <span class="name">gl</span>.<span class="name">pixelStorei</span>(<span class="name">gl</span>.<span class="name">UNPACK_FLIP_Y_WEBGL</span>, <span class="number">false</span>); </pre> <p>Next, let's take a look into shader initialization in the <code>initShaders</code> function, which we call in <code>initializeGL</code>. First we define the vertex shader:</p> <pre class="js"> var <span class="name">vertexShader</span> = <span class="name">getShader</span>(<span class="name">gl</span>, <span class="string">"attribute highp vec3 aVertexNormal; \ attribute highp vec3 aVertexPosition; \ attribute highp vec2 aTextureCoord; \ \ uniform highp mat4 uNormalMatrix; \ uniform mat4 uMVMatrix; \ uniform mat4 uPMatrix; \ \ varying mediump vec4 vColor; \ varying highp vec2 vTextureCoord; \ varying highp vec3 vLighting; \ \ void main(void) { \ gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); \ vTextureCoord = aTextureCoord; \ highp vec3 ambientLight = vec3(0.5, 0.5, 0.5); \ highp vec3 directionalLightColor = vec3(0.75, 0.75, 0.75); \ highp vec3 directionalVector = vec3(0.85, 0.8, 0.75); \ highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); \ highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); \ vLighting = ambientLight + (directionalLightColor * directional); \ }"</span>, <span class="name">gl</span>.<span class="name">VERTEX_SHADER</span>); </pre> <p>We follow that up by defining a fragment shader:</p> <pre class="js"> var <span class="name">fragmentShader</span> = <span class="name">getShader</span>(<span class="name">gl</span>, <span class="string">"varying highp vec2 vTextureCoord; \ varying highp vec3 vLighting; \ \ uniform sampler2D uSampler; \ \ void main(void) { \ mediump vec3 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)).rgb; \ gl_FragColor = vec4(texelColor * vLighting, 1.0); \ }"</span>, <span class="name">gl</span>.<span class="name">FRAGMENT_SHADER</span>); </pre> <p>Then we need to create the shader program (<a href="qml-qtcanvas3d-canvas3dprogram.html">Canvas3DProgram</a>), attach the shaders to it, and then link and use the program:</p> <pre class="js"> <span class="comment">// Create the Canvas3DProgram for shader</span> var <span class="name">shaderProgram</span> = <span class="name">gl</span>.<span class="name">createProgram</span>(); <span class="comment">// Attach the shader sources to the shader program</span> <span class="name">gl</span>.<span class="name">attachShader</span>(<span class="name">shaderProgram</span>, <span class="name">vertexShader</span>); <span class="name">gl</span>.<span class="name">attachShader</span>(<span class="name">shaderProgram</span>, <span class="name">fragmentShader</span>); <span class="comment">// Link the program</span> <span class="name">gl</span>.<span class="name">linkProgram</span>(<span class="name">shaderProgram</span>); <span class="comment">// Check the linking status</span> <span class="keyword">if</span> (!<span class="name">gl</span>.<span class="name">getProgramParameter</span>(<span class="name">shaderProgram</span>, <span class="name">gl</span>.<span class="name">LINK_STATUS</span>)) { <span class="name">console</span>.<span class="name">log</span>(<span class="string">"Could not initialise shaders"</span>); <span class="name">console</span>.<span class="name">log</span>(<span class="name">gl</span>.<span class="name">getProgramInfoLog</span>(<span class="name">shaderProgram</span>)); } <span class="comment">// Take the shader program into use</span> <span class="name">gl</span>.<span class="name">useProgram</span>(<span class="name">shaderProgram</span>); </pre> <p>And finally, look up and store the vertex attributes and uniform locations:</p> <pre class="js"> <span class="comment">// Look up where the vertex data needs to go</span> <span class="name">vertexPositionAttribute</span> <span class="operator">=</span> <span class="name">gl</span>.<span class="name">getAttribLocation</span>(<span class="name">shaderProgram</span>, <span class="string">"aVertexPosition"</span>); <span class="name">gl</span>.<span class="name">enableVertexAttribArray</span>(<span class="name">vertexPositionAttribute</span>); <span class="name">textureCoordAttribute</span> <span class="operator">=</span> <span class="name">gl</span>.<span class="name">getAttribLocation</span>(<span class="name">shaderProgram</span>, <span class="string">"aTextureCoord"</span>); <span class="name">gl</span>.<span class="name">enableVertexAttribArray</span>(<span class="name">textureCoordAttribute</span>); <span class="name">vertexNormalAttribute</span> <span class="operator">=</span> <span class="name">gl</span>.<span class="name">getAttribLocation</span>(<span class="name">shaderProgram</span>, <span class="string">"aVertexNormal"</span>); <span class="name">gl</span>.<span class="name">enableVertexAttribArray</span>(<span class="name">vertexNormalAttribute</span>); <span class="comment">// Get the uniform locations</span> <span class="name">pMatrixUniform</span> <span class="operator">=</span> <span class="name">gl</span>.<span class="name">getUniformLocation</span>(<span class="name">shaderProgram</span>, <span class="string">"uPMatrix"</span>); <span class="name">mvMatrixUniform</span> <span class="operator">=</span> <span class="name">gl</span>.<span class="name">getUniformLocation</span>(<span class="name">shaderProgram</span>, <span class="string">"uMVMatrix"</span>); <span class="name">nUniform</span> <span class="operator">=</span> <span class="name">gl</span>.<span class="name">getUniformLocation</span>(<span class="name">shaderProgram</span>, <span class="string">"uNormalMatrix"</span>); <span class="comment">// Setup texture sampler uniform</span> var <span class="name">textureSamplerUniform</span> = <span class="name">gl</span>.<span class="name">getUniformLocation</span>(<span class="name">shaderProgram</span>, <span class="string">"uSampler"</span>) <span class="name">gl</span>.<span class="name">activeTexture</span>(<span class="name">gl</span>.<span class="name">TEXTURE0</span>); <span class="name">gl</span>.<span class="name">uniform1i</span>(<span class="name">textureSamplerUniform</span>, <span class="number">0</span>); <span class="name">gl</span>.<span class="name">bindTexture</span>(<span class="name">gl</span>.<span class="name">TEXTURE_2D</span>, <span class="number">0</span>); </pre> <p>After initializing the shader program, we set up the vertex buffer in <code>initBuffers</code> function. Let's look at the vertex index buffer creation as an example:</p> <pre class="js"> var <span class="name">cubeVertexIndexBuffer</span> = <span class="name">gl</span>.<span class="name">createBuffer</span>(); <span class="name">gl</span>.<span class="name">bindBuffer</span>(<span class="name">gl</span>.<span class="name">ELEMENT_ARRAY_BUFFER</span>, <span class="name">cubeVertexIndexBuffer</span>); <span class="name">gl</span>.<span class="name">bufferData</span>(<span class="name">gl</span>.<span class="name">ELEMENT_ARRAY_BUFFER</span>, new <span class="name">Uint16Array</span>([ <span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="comment">// front</span> <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="comment">// back</span> <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">8</span>, <span class="number">10</span>, <span class="number">11</span>, <span class="comment">// top</span> <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">12</span>, <span class="number">14</span>, <span class="number">15</span>, <span class="comment">// bottom</span> <span class="number">16</span>, <span class="number">17</span>, <span class="number">18</span>, <span class="number">16</span>, <span class="number">18</span>, <span class="number">19</span>, <span class="comment">// right</span> <span class="number">20</span>, <span class="number">21</span>, <span class="number">22</span>, <span class="number">20</span>, <span class="number">22</span>, <span class="number">23</span> <span class="comment">// left</span> ]), <span class="name">gl</span>.<span class="name">STATIC_DRAW</span>); </pre> <p>Above, first we create the buffer, then bind it and finally insert the data into it. Other buffers are all handled in a similar fashion.</p> <p>As the final step in <code>initializeGL</code>, we create a texture image from <a href="qml-qtcanvas3d-textureimagefactory.html">TextureImageFactory</a>, and register handlers for <code>imageLoaded</code> and <code>imageLoadingFailed</code> signals. Once the texture image is successfully loaded, we create the actual texture:</p> <pre class="js"> <span class="name">qtLogoImage</span>.<span class="name">imageLoaded</span>.<span class="name">connect</span>(<span class="keyword">function</span>() { <span class="name">console</span>.<span class="name">log</span>(<span class="string">"Texture loaded, "</span><span class="operator">+</span><span class="name">qtLogoImage</span>.<span class="name">src</span>); <span class="comment">// Create the Canvas3DTexture object</span> <span class="name">cubeTexture</span> <span class="operator">=</span> <span class="name">gl</span>.<span class="name">createTexture</span>(); <span class="comment">// Bind it</span> <span class="name">gl</span>.<span class="name">bindTexture</span>(<span class="name">gl</span>.<span class="name">TEXTURE_2D</span>, <span class="name">cubeTexture</span>); <span class="comment">// Set the properties</span> <span class="name">gl</span>.<span class="name">texImage2D</span>(<span class="name">gl</span>.<span class="name">TEXTURE_2D</span>, <span class="comment">// target</span> <span class="number">0</span>, <span class="comment">// level</span> <span class="name">gl</span>.<span class="name">RGBA</span>, <span class="comment">// internalformat</span> <span class="name">gl</span>.<span class="name">RGBA</span>, <span class="comment">// format</span> <span class="name">gl</span>.<span class="name">UNSIGNED_BYTE</span>, <span class="comment">// type</span> <span class="name">qtLogoImage</span>); <span class="comment">// pixels</span> <span class="comment">// Set texture filtering parameters</span> <span class="name">gl</span>.<span class="name">texParameteri</span>(<span class="name">gl</span>.<span class="name">TEXTURE_2D</span>, <span class="name">gl</span>.<span class="name">TEXTURE_MAG_FILTER</span>, <span class="name">gl</span>.<span class="name">LINEAR</span>); <span class="name">gl</span>.<span class="name">texParameteri</span>(<span class="name">gl</span>.<span class="name">TEXTURE_2D</span>, <span class="name">gl</span>.<span class="name">TEXTURE_MIN_FILTER</span>, <span class="name">gl</span>.<span class="name">LINEAR_MIPMAP_NEAREST</span>); <span class="comment">// Generate mipmap</span> <span class="name">gl</span>.<span class="name">generateMipmap</span>(<span class="name">gl</span>.<span class="name">TEXTURE_2D</span>); }); </pre> <a name="paintgl-function"></a> <h3 >paintGL Function</h3> <p><code>paintGL</code> is called by <a href="qml-qtcanvas3d-canvas3d.html">Canvas3D</a> whenever it is ready to receive a new frame. Let's go through the steps that are done in each render cycle.</p> <p>First we check if canvas has been resized or if pixel ratio has changed, and update the projection matrix if necessary:</p> <pre class="js"> var <span class="name">pixelRatio</span> = <span class="name">canvas</span>.<span class="name">devicePixelRatio</span>; var <span class="name">currentWidth</span> = <span class="name">canvas</span>.<span class="name">width</span> <span class="operator">*</span> <span class="name">pixelRatio</span>; var <span class="name">currentHeight</span> = <span class="name">canvas</span>.<span class="name">height</span> <span class="operator">*</span> <span class="name">pixelRatio</span>; <span class="keyword">if</span> (<span class="name">currentWidth</span> <span class="operator">!==</span> <span class="name">width</span> <span class="operator">||</span> <span class="name">currentHeight</span> <span class="operator">!==</span> <span class="name">height</span> ) { <span class="name">width</span> <span class="operator">=</span> <span class="name">currentWidth</span>; <span class="name">height</span> <span class="operator">=</span> <span class="name">currentHeight</span>; <span class="name">gl</span>.<span class="name">viewport</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="name">width</span>, <span class="name">height</span>); <span class="name">mat4</span>.<span class="name">perspective</span>(<span class="name">pMatrix</span>, <span class="name">degToRad</span>(<span class="number">45</span>), <span class="name">width</span> <span class="operator">/</span> <span class="name">height</span>, <span class="number">0.1</span>, <span class="number">500.0</span>); <span class="name">gl</span>.<span class="name">uniformMatrix4fv</span>(<span class="name">pMatrixUniform</span>, <span class="number">false</span>, <span class="name">pMatrix</span>); } </pre> <p>Then we clear the render area using the clear color set in <code>initializeGL</code>:</p> <pre class="js"> <span class="name">gl</span>.<span class="name">clear</span>(<span class="name">gl</span>.<span class="name">COLOR_BUFFER_BIT</span> <span class="operator">|</span> <span class="name">gl</span>.<span class="name">DEPTH_BUFFER_BIT</span>); </pre> <p>Next we reset the model view matrix and apply translation and rotations:</p> <pre class="js"> <span class="name">mat4</span>.<span class="name">identity</span>(<span class="name">mvMatrix</span>); <span class="name">mat4</span>.<span class="name">translate</span>(<span class="name">mvMatrix</span>, <span class="name">mvMatrix</span>, [(<span class="name">canvas</span>.<span class="name">yRotAnim</span> <span class="operator">-</span> <span class="number">120.0</span>) <span class="operator">/</span> <span class="number">120.0</span>, (<span class="name">canvas</span>.<span class="name">xRotAnim</span> <span class="operator">-</span> <span class="number">60.0</span>) <span class="operator">/</span> <span class="number">50.0</span>, -<span class="number">10.0</span>]); <span class="name">mat4</span>.<span class="name">rotate</span>(<span class="name">mvMatrix</span>, <span class="name">mvMatrix</span>, <span class="name">degToRad</span>(<span class="name">canvas</span>.<span class="name">xRotAnim</span>), [<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>]); <span class="name">mat4</span>.<span class="name">rotate</span>(<span class="name">mvMatrix</span>, <span class="name">mvMatrix</span>, <span class="name">degToRad</span>(<span class="name">canvas</span>.<span class="name">yRotAnim</span>), [<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>]); <span class="name">mat4</span>.<span class="name">rotate</span>(<span class="name">mvMatrix</span>, <span class="name">mvMatrix</span>, <span class="name">degToRad</span>(<span class="name">canvas</span>.<span class="name">zRotAnim</span>), [<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>]); <span class="name">gl</span>.<span class="name">uniformMatrix4fv</span>(<span class="name">mvMatrixUniform</span>, <span class="number">false</span>, <span class="name">mvMatrix</span>); </pre> <p>As we have a lit cube, we invert and transpose the model view matrix to be used for lighting calculations:</p> <pre class="js"> <span class="name">mat4</span>.<span class="name">invert</span>(<span class="name">nMatrix</span>, <span class="name">mvMatrix</span>); <span class="name">mat4</span>.<span class="name">transpose</span>(<span class="name">nMatrix</span>, <span class="name">nMatrix</span>); <span class="name">gl</span>.<span class="name">uniformMatrix4fv</span>(<span class="name">nUniform</span>, <span class="number">false</span>, <span class="name">nMatrix</span>); </pre> <p>And finally we draw the cube:</p> <pre class="js"> <span class="name">gl</span>.<span class="name">drawElements</span>(<span class="name">gl</span>.<span class="name">TRIANGLES</span>, <span class="number">36</span>, <span class="name">gl</span>.<span class="name">UNSIGNED_SHORT</span>, <span class="number">0</span>); </pre> <a name="logging"></a> <h2 id="logging">Logging</h2> <p>Qt Canvas 3D uses Qt's categorized logging feature. This example enables all Qt Canvas 3D log output with the code shown below. For more on <a href="qml-qtcanvas3d-canvas3d.html">Canvas3D</a>'s logging features refer to <a href="qtcanvas3d-logging.html">Qt Canvas 3D Logging</a>.</p> <pre class="cpp"> <span class="comment">// Uncomment to turn on all the logging categories of Canvas3D</span> <span class="comment">// QString loggingFilter = QString("qt.canvas3d.info.debug=true\n");</span> <span class="comment">// loggingFilter += QStringLiteral("qt.canvas3d.rendering.debug=true\n")</span> <span class="comment">// + QStringLiteral("qt.canvas3d.rendering.warning=true\n")</span> <span class="comment">// + QStringLiteral("qt.canvas3d.glerrors.debug=true");</span> <span class="comment">// QLoggingCategory::setFilterRules(loggingFilter);</span> </pre> <p>Files:</p> <ul> <li><a href="qtcanvas3d-textureandlight-qml-textureandlight-main-qml.html">textureandlight/qml/textureandlight/main.qml</a></li> <li><a href="qtcanvas3d-textureandlight-qml-textureandlight-textureandlight-js.html">textureandlight/qml/textureandlight/textureandlight.js</a></li> <li><a href="qtcanvas3d-textureandlight-main-cpp.html">textureandlight/main.cpp</a></li> <li><a href="qtcanvas3d-textureandlight-textureandlight-pro.html">textureandlight/textureandlight.pro</a></li> <li><a href="qtcanvas3d-textureandlight-textureandlight-qrc.html">textureandlight/textureandlight.qrc</a></li> </ul> </div> <!-- @@@textureandlight --> </div> </div> </div> </div> </div> <div class="footer"> <p> <acronym title="Copyright">©</acronym> 2017 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners.<br> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br> Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners. </p> </div> </body> </html>