1/****************************************************************************
2**
3** Copyright (C) 2016 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 "background_renderer.h"
52
53#include <qmath.h>
54#include <QFileInfo>
55#include <QTime>
56
57#include <QOpenGLShaderProgram>
58#include <QOpenGLContext>
59#include <QOpenGLFunctions>
60
61#include <math.h>
62
63static const char vertex_shader[] =
64 "attribute highp vec3 vertexCoord;"
65 "void main() {"
66 " gl_Position = vec4(vertexCoord,1.0);"
67 "}";
68
69static const char fragment_shader[] =
70 "void main() {"
71 " gl_FragColor = vec4(0.0,1.0,0.0,1.0);"
72 "}";
73
74static const float vertices[] = { -1, -1, 0,
75 -1, 1, 0,
76 1, -1, 0,
77 1, 1, 0 };
78
79FragmentToy::FragmentToy(const QString &fragmentSource, QObject *parent)
80 : QObject(parent)
81 , m_recompile_shaders(true)
82{
83 if (QFile::exists(fragmentSource)) {
84 QFileInfo info(fragmentSource);
85 m_fragment_file_last_modified = info.lastModified();
86 m_fragment_file = fragmentSource;
87#if QT_CONFIG(filesystemwatcher)
88 m_watcher.addPath(info.canonicalPath());
89 QObject::connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FragmentToy::fileChanged);
90#endif
91 }
92}
93
94FragmentToy::~FragmentToy()
95 = default;
96
97void FragmentToy::draw(const QSize &windowSize)
98{
99 if (!m_program)
100 initializeOpenGLFunctions();
101
102 glDisable(GL_STENCIL_TEST);
103 glDisable(GL_DEPTH_TEST);
104
105 glClearColor(0, 0, 0, 1);
106 glClear(GL_COLOR_BUFFER_BIT);
107 if (!m_vao.isCreated())
108 m_vao.create();
109
110 QOpenGLVertexArrayObject::Binder binder(&m_vao);
111
112 if (!m_vertex_buffer.isCreated()) {
113 m_vertex_buffer.create();
114 m_vertex_buffer.bind();
115 m_vertex_buffer.allocate(vertices, sizeof(vertices));
116 m_vertex_buffer.release();
117 }
118
119 if (!m_program) {
120 m_program.reset(new QOpenGLShaderProgram);
121 m_program->create();
122 m_vertex_shader.reset(new QOpenGLShader(QOpenGLShader::Vertex));
123 if (!m_vertex_shader->compileSourceCode(vertex_shader)) {
124 qWarning() << "Failed to compile the vertex shader:" << m_vertex_shader->log();
125 }
126 if (!m_program->addShader(m_vertex_shader.get())) {
127 qWarning() << "Failed to add vertex shader to program:" << m_program->log();
128 }
129 }
130
131 if (!m_fragment_shader && m_recompile_shaders) {
132 QByteArray data;
133 if (m_fragment_file.size()) {
134 QFile file(m_fragment_file);
135 if (file.open(QIODevice::ReadOnly)) {
136 data = file.readAll();
137 } else {
138 qWarning() << "Failed to load input file, falling back to default";
139 data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
140 }
141 } else {
142 QFile qrcFile(":/background.frag");
143 if (qrcFile.open(QIODevice::ReadOnly))
144 data = qrcFile.readAll();
145 else
146 data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
147 }
148 if (data.size()) {
149 m_fragment_shader.reset(new QOpenGLShader(QOpenGLShader::Fragment));
150 if (!m_fragment_shader->compileSourceCode(data)) {
151 qWarning() << "Failed to compile fragment shader:" << m_fragment_shader->log();
152 m_fragment_shader.reset(nullptr);
153 }
154 } else {
155 qWarning() << "Unknown error, no fragment shader";
156 }
157
158 if (m_fragment_shader) {
159 if (!m_program->addShader(m_fragment_shader.get())) {
160 qWarning() << "Failed to add fragment shader to program:" << m_program->log();
161 }
162 }
163 }
164
165 if (m_recompile_shaders) {
166 m_recompile_shaders = false;
167
168 if (m_program->link()) {
169 m_vertex_coord_pos = m_program->attributeLocation("vertexCoord");
170 } else {
171 qWarning() << "Failed to link shader program" << m_program->log();
172 }
173
174 }
175
176 if (!m_program->isLinked())
177 return;
178
179 m_program->bind();
180
181 m_vertex_buffer.bind();
182 m_program->setAttributeBuffer("vertexCoord", GL_FLOAT, 0, 3, 0);
183 m_program->enableAttributeArray("vertexCoord");
184 m_vertex_buffer.release();
185
186 m_program->setUniformValue("currentTime", (uint) QDateTime::currentMSecsSinceEpoch());
187 m_program->setUniformValue("windowSize", windowSize);
188
189 QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
190
191 m_program->release();
192}
193
194void FragmentToy::fileChanged(const QString &path)
195{
196 Q_UNUSED(path);
197 if (QFile::exists(m_fragment_file)) {
198 QFileInfo fragment_source(m_fragment_file);
199 if (fragment_source.lastModified() > m_fragment_file_last_modified) {
200 m_fragment_file_last_modified = fragment_source.lastModified();
201 m_recompile_shaders = true;
202 if (m_program) {
203 m_program->removeShader(m_fragment_shader.get());
204 m_fragment_shader.reset(nullptr);
205 }
206 }
207 } else {
208 m_recompile_shaders = true;
209 if (m_program) {
210 m_program->removeShader(m_fragment_shader.get());
211 m_fragment_shader.reset(nullptr);
212 }
213 }
214}
215