1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "fbitem.h"
52#include <QOpenGLFramebufferObject>
53#include <QOpenGLFunctions>
54#include <QMatrix4x4>
55
56FbItem::FbItem(QQuickItem *parent)
57 : QQuickFramebufferObject(parent),
58 m_target(0, 0, -1),
59 m_syncState(AllNeedsSync),
60 m_multisample(false)
61{
62}
63
64QQuickFramebufferObject::Renderer *FbItem::createRenderer() const
65{
66 return new FbItemRenderer(m_multisample);
67}
68
69void FbItem::setEye(const QVector3D &v)
70{
71 if (m_eye != v) {
72 m_eye = v;
73 m_syncState |= CameraNeedsSync;
74 update();
75 }
76}
77
78void FbItem::setTarget(const QVector3D &v)
79{
80 if (m_target != v) {
81 m_target = v;
82 m_syncState |= CameraNeedsSync;
83 update();
84 }
85}
86
87void FbItem::setRotation(const QVector3D &v)
88{
89 if (m_rotation != v) {
90 m_rotation = v;
91 m_syncState |= RotationNeedsSync;
92 update();
93 }
94}
95
96int FbItem::swapSyncState()
97{
98 int s = m_syncState;
99 m_syncState = 0;
100 return s;
101}
102
103FbItemRenderer::FbItemRenderer(bool multisample)
104 : m_inited(false),
105 m_multisample(multisample),
106 m_dirty(DirtyAll)
107{
108 m_camera.setToIdentity();
109 m_baseWorld.setToIdentity();
110 m_baseWorld.translate(x: 0, y: 0, z: -1);
111 m_world = m_baseWorld;
112}
113
114void FbItemRenderer::synchronize(QQuickFramebufferObject *qfbitem)
115{
116 FbItem *item = static_cast<FbItem *>(qfbitem);
117 int syncState = item->swapSyncState();
118 if (syncState & FbItem::CameraNeedsSync) {
119 m_camera.setToIdentity();
120 m_camera.lookAt(eye: item->eye(), center: item->eye() + item->target(), up: QVector3D(0, 1, 0));
121 m_dirty |= DirtyCamera;
122 }
123 if (syncState & FbItem::RotationNeedsSync) {
124 m_rotation = item->rotation();
125 m_dirty |= DirtyWorld;
126 }
127}
128
129struct StateBinder
130{
131 StateBinder(FbItemRenderer *r)
132 : m_r(r) {
133 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
134 f->glEnable(GL_DEPTH_TEST);
135 f->glEnable(GL_CULL_FACE);
136 f->glDepthMask(GL_TRUE);
137 f->glDepthFunc(GL_LESS);
138 f->glFrontFace(GL_CCW);
139 f->glCullFace(GL_BACK);
140 m_r->m_program->bind();
141 }
142 ~StateBinder() {
143 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
144 m_r->m_program->release();
145 f->glDisable(GL_CULL_FACE);
146 f->glDisable(GL_DEPTH_TEST);
147 }
148 FbItemRenderer *m_r;
149};
150
151void FbItemRenderer::updateDirtyUniforms()
152{
153 if (m_dirty & DirtyProjection)
154 m_program->setUniformValue(location: m_projMatrixLoc, value: m_proj);
155
156 if (m_dirty & DirtyCamera)
157 m_program->setUniformValue(location: m_camMatrixLoc, value: m_camera);
158
159 if (m_dirty & DirtyWorld) {
160 m_program->setUniformValue(location: m_worldMatrixLoc, value: m_world);
161 QMatrix3x3 normalMatrix = m_world.normalMatrix();
162 m_program->setUniformValue(location: m_normalMatrixLoc, value: normalMatrix);
163 }
164
165 if (m_dirty & DirtyLight)
166 m_program->setUniformValue(location: m_lightPosLoc, value: QVector3D(0, 0, 70));
167
168 m_dirty = 0;
169}
170
171void FbItemRenderer::render()
172{
173 ensureInit();
174
175 if (m_vao.isCreated())
176 m_vao.bind();
177 else
178 setupVertexAttribs();
179
180 StateBinder state(this);
181
182 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
183 f->glClearColor(red: 0, green: 0, blue: 0, alpha: 0);
184 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
185
186 if (m_dirty & DirtyWorld) {
187 m_world = m_baseWorld;
188 m_world.rotate(angle: m_rotation.x(), x: 1, y: 0, z: 0);
189 m_world.rotate(angle: m_rotation.y(), x: 0, y: 1, z: 0);
190 m_world.rotate(angle: m_rotation.z(), x: 0, y: 0, z: 1);
191 }
192
193 updateDirtyUniforms();
194
195 f->glDrawArrays(GL_TRIANGLES, first: 0, count: m_logo.vertexCount());
196
197 if (m_vao.isCreated())
198 m_vao.release();
199}
200
201QOpenGLFramebufferObject *FbItemRenderer::createFramebufferObject(const QSize &size)
202{
203 m_dirty |= DirtyProjection;
204 m_proj.setToIdentity();
205 m_proj.perspective(verticalAngle: 45.0f, aspectRatio: GLfloat(size.width()) / size.height(), nearPlane: 0.01f, farPlane: 100.0f);
206
207 QOpenGLFramebufferObjectFormat format;
208 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
209 format.setSamples(m_multisample ? 4 : 0);
210 return new QOpenGLFramebufferObject(size, format);
211}
212
213void FbItemRenderer::ensureInit()
214{
215 if (m_inited)
216 return;
217
218 m_inited = true;
219
220 initBuf();
221 initProgram();
222}
223
224void FbItemRenderer::initBuf()
225{
226 QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
227
228 m_logoVbo.create();
229 m_logoVbo.bind();
230 m_logoVbo.allocate(data: m_logo.constData(), count: m_logo.count() * sizeof(GLfloat));
231
232 setupVertexAttribs();
233}
234
235void FbItemRenderer::setupVertexAttribs()
236{
237 m_logoVbo.bind();
238 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
239 f->glEnableVertexAttribArray(index: 0);
240 f->glEnableVertexAttribArray(index: 1);
241 f->glVertexAttribPointer(indx: 0, size: 3, GL_FLOAT, GL_FALSE, stride: 6 * sizeof(GLfloat), ptr: nullptr);
242 f->glVertexAttribPointer(indx: 1, size: 3, GL_FLOAT, GL_FALSE, stride: 6 * sizeof(GLfloat), ptr: reinterpret_cast<void *>(3 * sizeof(GLfloat)));
243 m_logoVbo.release();
244}
245
246static const char *vertexShaderSource =
247 "attribute vec4 vertex;\n"
248 "attribute vec3 normal;\n"
249 "varying vec3 vert;\n"
250 "varying vec3 vertNormal;\n"
251 "uniform mat4 projMatrix;\n"
252 "uniform mat4 camMatrix;\n"
253 "uniform mat4 worldMatrix;\n"
254 "uniform mat3 normalMatrix;\n"
255 "void main() {\n"
256 " vert = vertex.xyz;\n"
257 " vertNormal = normalMatrix * normal;\n"
258 " gl_Position = projMatrix * camMatrix * worldMatrix * vertex;\n"
259 "}\n";
260
261static const char *fragmentShaderSource =
262 "varying highp vec3 vert;\n"
263 "varying highp vec3 vertNormal;\n"
264 "uniform highp vec3 lightPos;\n"
265 "void main() {\n"
266 " highp vec3 L = normalize(lightPos - vert);\n"
267 " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
268 " highp vec3 color = vec3(0.39, 1.0, 0.0);\n"
269 " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
270 " gl_FragColor = vec4(col, 1.0);\n"
271 "}\n";
272
273void FbItemRenderer::initProgram()
274{
275 m_program.reset(other: new QOpenGLShaderProgram);
276 m_program->addCacheableShaderFromSourceCode(type: QOpenGLShader::Vertex, source: vertexShaderSource);
277 m_program->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragmentShaderSource);
278 m_program->bindAttributeLocation(name: "vertex", location: 0);
279 m_program->bindAttributeLocation(name: "normal", location: 1);
280 m_program->link();
281 m_projMatrixLoc = m_program->uniformLocation(name: "projMatrix");
282 m_camMatrixLoc = m_program->uniformLocation(name: "camMatrix");
283 m_worldMatrixLoc = m_program->uniformLocation(name: "worldMatrix");
284 m_normalMatrixLoc = m_program->uniformLocation(name: "normalMatrix");
285 m_lightPosLoc = m_program->uniformLocation(name: "lightPos");
286}
287

source code of qtdeclarative/examples/quick/quickwidgets/qquickviewcomparison/fbitem.cpp