aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@digia.com>2014-06-06 16:53:33 +0200
committerLaszlo Agocs <laszlo.agocs@digia.com>2014-06-19 20:55:57 +0200
commit2bc44ba16bb86bd7ebc7eb59ae78eda80a737984 (patch)
tree5000e2f744b0f61a0f9f4eeb2cfed5306dde1772 /examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp
parenta560018d08d4212bc65c163eb608639f19b38df2 (diff)
Add a QQuickWidget - QQuickView comparison example
Besides serving as a side-by-side test tool for QQuickView and QQuickWidget (including multisampling), it also demonstrates some useful practices for rendering 3D content via QQuickFramebufferObject. Done-with: Paul Olav Tvete <paul.tvete@digia.com> Change-Id: Ie73e998ee91e32ef1535dd6f0f65c8a69addcc5e Reviewed-by: Paul Olav Tvete <paul.tvete@digia.com>
Diffstat (limited to 'examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp')
-rw-r--r--examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp273
1 files changed, 273 insertions, 0 deletions
diff --git a/examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp b/examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp
new file mode 100644
index 0000000000..3b9f2e97f3
--- /dev/null
+++ b/examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "fbitem.h"
+#include <QOpenGLFramebufferObject>
+#include <QOpenGLFunctions>
+#include <QMatrix4x4>
+
+FbItem::FbItem(QQuickItem *parent)
+ : QQuickFramebufferObject(parent),
+ m_target(0, 0, -1),
+ m_syncState(AllNeedsSync),
+ m_multisample(false)
+{
+}
+
+QQuickFramebufferObject::Renderer *FbItem::createRenderer() const
+{
+ return new FbItemRenderer(m_multisample);
+}
+
+void FbItem::setEye(const QVector3D &v)
+{
+ if (m_eye != v) {
+ m_eye = v;
+ m_syncState |= CameraNeedsSync;
+ update();
+ }
+}
+
+void FbItem::setTarget(const QVector3D &v)
+{
+ if (m_target != v) {
+ m_target = v;
+ m_syncState |= CameraNeedsSync;
+ update();
+ }
+}
+
+void FbItem::setRotation(const QVector3D &v)
+{
+ if (m_rotation != v) {
+ m_rotation = v;
+ m_syncState |= RotationNeedsSync;
+ update();
+ }
+}
+
+int FbItem::swapSyncState()
+{
+ int s = m_syncState;
+ m_syncState = 0;
+ return s;
+}
+
+FbItemRenderer::FbItemRenderer(bool multisample)
+ : m_inited(false),
+ m_multisample(multisample),
+ m_dirty(DirtyAll)
+{
+ m_camera.setToIdentity();
+ m_baseWorld.setToIdentity();
+ m_baseWorld.translate(0, 0, -1);
+ m_world = m_baseWorld;
+}
+
+void FbItemRenderer::synchronize(QQuickFramebufferObject *qfbitem)
+{
+ FbItem *item = static_cast<FbItem *>(qfbitem);
+ int syncState = item->swapSyncState();
+ if (syncState & FbItem::CameraNeedsSync) {
+ m_camera.setToIdentity();
+ m_camera.lookAt(item->eye(), item->eye() + item->target(), QVector3D(0, 1, 0));
+ m_dirty |= DirtyCamera;
+ }
+ if (syncState & FbItem::RotationNeedsSync) {
+ m_rotation = item->rotation();
+ m_dirty |= DirtyWorld;
+ }
+}
+
+struct StateBinder
+{
+ StateBinder(FbItemRenderer *r)
+ : m_r(r) {
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ f->glEnable(GL_DEPTH_TEST);
+ f->glEnable(GL_CULL_FACE);
+ f->glDepthMask(GL_TRUE);
+ f->glDepthFunc(GL_LESS);
+ f->glFrontFace(GL_CCW);
+ f->glCullFace(GL_BACK);
+ m_r->m_program->bind();
+ }
+ ~StateBinder() {
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ m_r->m_program->release();
+ f->glDisable(GL_CULL_FACE);
+ f->glDisable(GL_DEPTH_TEST);
+ }
+ FbItemRenderer *m_r;
+};
+
+void FbItemRenderer::updateDirtyUniforms()
+{
+ if (m_dirty & DirtyProjection)
+ m_program->setUniformValue(m_projMatrixLoc, m_proj);
+
+ if (m_dirty & DirtyCamera)
+ m_program->setUniformValue(m_camMatrixLoc, m_camera);
+
+ if (m_dirty & DirtyWorld) {
+ m_program->setUniformValue(m_worldMatrixLoc, m_world);
+ QMatrix3x3 normalMatrix = m_world.normalMatrix();
+ m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
+ }
+
+ if (m_dirty & DirtyLight)
+ m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
+
+ m_dirty = 0;
+}
+
+void FbItemRenderer::render()
+{
+ ensureInit();
+
+ QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
+ if (!m_vao.isCreated())
+ setupVertexAttribs();
+
+ StateBinder state(this);
+
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ f->glClearColor(0, 0, 0, 0);
+ f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (m_dirty & DirtyWorld) {
+ m_world = m_baseWorld;
+ m_world.rotate(m_rotation.x(), 1, 0, 0);
+ m_world.rotate(m_rotation.y(), 0, 1, 0);
+ m_world.rotate(m_rotation.z(), 0, 0, 1);
+ }
+
+ updateDirtyUniforms();
+
+ f->glDrawArrays(GL_TRIANGLES, 0, m_logo.vertexCount());
+}
+
+QOpenGLFramebufferObject *FbItemRenderer::createFramebufferObject(const QSize &size)
+{
+ m_dirty |= DirtyProjection;
+ m_proj.setToIdentity();
+ m_proj.perspective(45.0f, GLfloat(size.width()) / size.height(), 0.01f, 100.0f);
+
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(m_multisample ? 4 : 0);
+ return new QOpenGLFramebufferObject(size, format);
+}
+
+void FbItemRenderer::ensureInit()
+{
+ if (m_inited)
+ return;
+
+ m_inited = true;
+
+ initBuf();
+ initProgram();
+}
+
+void FbItemRenderer::initBuf()
+{
+ m_vao.create();
+ QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
+
+ m_logoVbo.create();
+ m_logoVbo.bind();
+ m_logoVbo.allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat));
+
+ setupVertexAttribs();
+}
+
+void FbItemRenderer::setupVertexAttribs()
+{
+ m_logoVbo.bind();
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ f->glEnableVertexAttribArray(0);
+ f->glEnableVertexAttribArray(1);
+ f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);
+ f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
+ m_logoVbo.release();
+}
+
+static const char *vertexShaderSource =
+ "attribute vec4 vertex;\n"
+ "attribute vec3 normal;\n"
+ "varying vec3 vert;\n"
+ "varying vec3 vertNormal;\n"
+ "uniform mat4 projMatrix;\n"
+ "uniform mat4 camMatrix;\n"
+ "uniform mat4 worldMatrix;\n"
+ "uniform mat3 normalMatrix;\n"
+ "void main() {\n"
+ " vert = vertex.xyz;\n"
+ " vertNormal = normalMatrix * normal;\n"
+ " gl_Position = projMatrix * camMatrix * worldMatrix * vertex;\n"
+ "}\n";
+
+static const char *fragmentShaderSource =
+ "varying highp vec3 vert;\n"
+ "varying highp vec3 vertNormal;\n"
+ "uniform highp vec3 lightPos;\n"
+ "void main() {\n"
+ " highp vec3 L = normalize(lightPos - vert);\n"
+ " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
+ " highp vec3 color = vec3(0.39, 1.0, 0.0);\n"
+ " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
+ " gl_FragColor = vec4(col, 1.0);\n"
+ "}\n";
+
+void FbItemRenderer::initProgram()
+{
+ m_program.reset(new QOpenGLShaderProgram);
+ m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
+ m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
+ m_program->bindAttributeLocation("vertex", 0);
+ m_program->bindAttributeLocation("normal", 1);
+ m_program->link();
+ m_projMatrixLoc = m_program->uniformLocation("projMatrix");
+ m_camMatrixLoc = m_program->uniformLocation("camMatrix");
+ m_worldMatrixLoc = m_program->uniformLocation("worldMatrix");
+ m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
+ m_lightPosLoc = m_program->uniformLocation("lightPos");
+}