Stop using static initialisation so that dcpomatic::write() can be called more than...
[dcpomatic.git] / hacks / gl / modern.cc
1 #include <QtGui/QGuiApplication>
2 #include <QtGui/QKeyEvent>
3 #include <QtGui/QOpenGLWindow>
4 #include <QtGui/QOpenGLBuffer>
5 #include <QtGui/QOpenGLFunctions>
6 #include <QtGui/QOpenGLShaderProgram>
7 #include <QtGui/QOpenGLVertexArrayObject>
8 static QString vertexShader =
9 "#version 100\n"
10 "\n"
11 "attribute vec3 vertexPosition;\n"
12 "attribute vec3 vertexNormal;\n"
13 "attribute vec3 vertexColor;\n"
14 "attribute vec2 texCoord2d;\n"
15 "\n"
16 "uniform mat4 modelViewMatrix;\n"
17 "uniform mat3 normalMatrix;\n"
18 "uniform mat4 projectionMatrix;\n"
19 "\n"
20 "struct LightSource\n"
21 "{\n"
22 "    vec3 ambient;\n"
23 "    vec3 diffuse;\n"
24 "    vec3 specular;\n"
25 "    vec3 position;\n"
26 "};\n"
27 "uniform LightSource lightSource;\n"
28 "\n"
29 "struct LightModel\n"
30 "{\n"
31 "    vec3 ambient;\n"
32 "};\n"
33 "uniform LightModel lightModel;\n"
34 "\n"
35 "struct Material {\n"
36 "    vec3  emission;\n"
37 "    vec3  specular;\n"
38 "    float shininess;\n"
39 "};\n"
40 "uniform Material material;\n"
41 "\n"
42 "varying vec3 v_color;\n"
43 "varying vec2 v_texCoord2d;\n"
44 "\n"
45 "void main()\n"
46 "{\n"
47 "    vec3 normal     = normalize(normalMatrix * vertexNormal);                       // normal vector              \n"
48 "    vec3 position   = vec3(modelViewMatrix * vec4(vertexPosition, 1));              // vertex pos in eye coords   \n"
49 "    vec3 halfVector = normalize(lightSource.position + vec3(0,0,1));                // light half vector          \n"
50 "    float nDotVP    = dot(normal, normalize(lightSource.position));                 // normal . light direction   \n"
51 "    float nDotHV    = max(0.f, dot(normal,  halfVector));                           // normal . light half vector \n"
52 "    float pf        = mix(0.f, pow(nDotHV, material.shininess), step(0.f, nDotVP)); // power factor               \n"
53 "\n"
54 "    vec3 ambient    = lightSource.ambient;\n"
55 "    vec3 diffuse    = lightSource.diffuse * nDotVP;\n"
56 "    vec3 specular   = lightSource.specular * pf;\n"
57 "    vec3 sceneColor = material.emission + vertexColor * lightModel.ambient;\n"
58 "\n"
59 "    v_color = clamp(sceneColor +                             \n"
60 "                    ambient  * vertexColor +                 \n"
61 "                    diffuse  * vertexColor +                 \n"
62 "                    specular * material.specular, 0.f, 1.f );\n"
63 "\n"
64 "    v_texCoord2d = texCoord2d;\n"
65 "\n"
66 "    gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1);\n"
67 "}\n"
68 ;
69 static QString fragmentShader =
70 "#version 100\n"
71 "precision lowp vec3;\n"
72 "precision lowp vec2;\n"
73 "uniform sampler2D texUnit;\n"
74 "\n"
75 "varying vec3 v_color;\n"
76 "varying vec2 v_texCoord2d;\n"
77 "\n"
78 "void main()\n"
79 "{\n"
80 "    gl_FragColor = vec4(v_color, 1) * texture2D(texUnit, v_texCoord2d);\n"
81 "}\n"
82 ;
83 /*
84 * Texture copied and modifided modified from:
85 * <a href="https://www.opengl.org/archives/resources/code/samples/mjktips/TexShadowReflectLight.html
86 */
87 static char *circles[] = {
88 "................",
89 "................",
90 "......xxxx......",
91 "....xxxxxxxx....",
92 "...xxxxxxxxxx...",
93 "...xxx....xxx...",
94 "..xxx......xxx..",
95 "..xxx......xxx..",
96 "..xxx......xxx..",
97 "..xxx......xxx..",
98 "...xxx....xxx...",
99 "...xxxxxxxxxx...",
100 "....xxxxxxxx....",
101 "......xxxx......",
102 "................",
103 "................",
104 };
105 struct Window : QOpenGLWindow, QOpenGLFunctions
106 {
107 Window() :
108 m_vbo(QOpenGLBuffer::VertexBuffer),
109 m_ibo(QOpenGLBuffer::IndexBuffer)
110 {
111 }
112 void createShaderProgram()
113 {
114 if ( !m_pgm.addShaderFromSourceCode( QOpenGLShader::Vertex, vertexShader)) {
115 qDebug() << "Error in vertex shader:" << m_pgm.log();
116 exit(1);
117 }
118 if ( !m_pgm.addShaderFromSourceCode( QOpenGLShader::Fragment, fragmentShader)) {
119 qDebug() << "Error in fragment shader:" << m_pgm.log();
120 exit(1);
121 }
122 if ( !m_pgm.link() ) {
123 qDebug() << "Error linking shader program:" << m_pgm.log();
124 exit(1);
125 }
126 }
127 void createGeometry()
128 {
129 // Initialize and bind the VAO that's going to capture all this vertex state
130 m_vao.create();
131 m_vao.bind();
132 // we need 24 vertices, 24 normals, and 24 colors (6 faces, 4 vertices per face)
133 // since we can't share normal data at the corners (each corner gets 3 normals)
134 // and since we're not using glVertexAttribDivisor (not available in ES 2.0)
135 struct Vertex {
136 GLfloat position[3],
137 normal  [3],
138 color   [3],
139 texcoord[2];
140 } attribs[24]= {
141 // Top face (y = 1.0f)
142 { { 1.0f, 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f} },     // Green
143 { {-1.0f, 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 0.0f, 1.0f} },     // Green
144 { {-1.0f, 1.0f,  1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 1.0f, 1.0f} },     // Green
145 { { 1.0f, 1.0f,  1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f} },     // Green
146 // Bottom face (y = -1.0f)
147 { { 1.0f, -1.0f,  1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 0.0f, 0.0f} },    // Orange
148 { {-1.0f, -1.0f,  1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 0.0f, 1.0f} },    // Orange
149 { {-1.0f, -1.0f, -1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 1.0f, 1.0f} },    // Orange
150 { { 1.0f, -1.0f, -1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 1.0f, 0.0f} },    // Orange
151 // Front face  (z = 1.0f)
152 { { 1.0f,  1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f} },     // Red
153 { {-1.0f,  1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f} },     // Red
154 { {-1.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 1.0f, 1.0f} },     // Red
155 { { 1.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 1.0f, 0.0f} },     // Red
156 // Back face (z = -1.0f)
157 { { 1.0f, -1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 0.0f, 0.0f} },    // Yellow
158 { {-1.0f, -1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 0.0f, 1.0f} },    // Yellow
159 { {-1.0f,  1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 1.0f, 1.0f} },    // Yellow
160 { { 1.0f,  1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 1.0f, 0.0f} },    // Yellow
161 // Left face (x = -1.0f)
162 { {-1.0f,  1.0f,  1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f} },    // Blue
163 { {-1.0f,  1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 0.0f, 1.0f} },    // Blue
164 { {-1.0f, -1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 1.0f, 1.0f} },    // Blue
165 { {-1.0f, -1.0f,  1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f} },    // Blue
166 // Right face (x = 1.0f)
167 { {1.0f,  1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 0.0f, 0.0f} },     // Magenta
168 { {1.0f,  1.0f,  1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 0.0f, 1.0f} },     // Magenta
169 { {1.0f, -1.0f,  1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 1.0f, 1.0f} },     // Magenta
170 { {1.0f, -1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 1.0f, 0.0f} },     // Magenta
171 };
172 // Put all the attribute data in a FBO
173 m_vbo.create();
174 m_vbo.setUsagePattern( QOpenGLBuffer::StaticDraw );
175 m_vbo.bind();
176 m_vbo.allocate(&attribs, sizeof(attribs));
177 // Configure the vertex streams for this attribute data layout
178 m_pgm.enableAttributeArray("vertexPosition");
179 m_pgm.setAttributeBuffer("vertexPosition", GL_FLOAT, offsetof(Vertex, position), 3, sizeof(Vertex) );
180 m_pgm.enableAttributeArray("vertexNormal");
181 m_pgm.setAttributeBuffer("vertexNormal", GL_FLOAT, offsetof(Vertex, normal), 3, sizeof(Vertex) );
182 m_pgm.enableAttributeArray("vertexColor");
183 m_pgm.setAttributeBuffer("vertexColor", GL_FLOAT, offsetof(Vertex, color), 3, sizeof(Vertex) );
184 m_pgm.enableAttributeArray("texCoord2d");
185 m_pgm.setAttributeBuffer("texCoord2d", GL_FLOAT, offsetof(Vertex, texcoord), 3, sizeof(Vertex) );
186 // we need 36 indices (6 faces, 2 triangles per face, 3 vertices per triangle)
187 struct {
188 GLubyte cube[36];
189 } indices;
190 m_cnt=0; for (GLsizei i=0, v=0; v<6*4; v+=4)
191 {
192 // first triangle (ccw winding)
193 indices.cube[i++] = v + 0;
194 indices.cube[i++] = v + 1;
195 indices.cube[i++] = v + 2;
196 // second triangle (ccw winding)
197 indices.cube[i++] = v + 0;
198 indices.cube[i++] = v + 2;
199 indices.cube[i++] = v + 3;
200 m_cnt = i;
201 }
202 // Put all the index data in a IBO
203 m_ibo.create();
204 m_ibo.setUsagePattern( QOpenGLBuffer::StaticDraw );
205 m_ibo.bind();
206 m_ibo.allocate(&indices, sizeof(indices));
207 // Okay, we've finished setting up the vao
208 m_vao.release();
209 }
210 void createTexture(void)
211 {
212 GLubyte image[16][16][3];
213 GLubyte *loc;
214 int s, t;
215 /* Setup RGB image for the texture. */
216 loc = (GLubyte*) image;
217 for (t = 0; t < 16; t++) {
218 for (s = 0; s < 16; s++) {
219 if (circles[t][s] == 'x') {
220 /* Nice green. */
221 loc[0] = 0x1f;
222 loc[1] = 0x8f;
223 loc[2] = 0x1f;
224 } else {
225 /* Light gray. */
226 loc[0] = 0xaa;
227 loc[1] = 0xaa;
228 loc[2] = 0xaa;
229 }
230 loc += 3;
231 }
232 }
233 glGenTextures  (1, &m_tex);
234 glBindTexture  (GL_TEXTURE_2D, m_tex);
235 glPixelStorei  (GL_UNPACK_ALIGNMENT, 1);
236 glTexImage2D   (GL_TEXTURE_2D, 0, 3, 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
238 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
239 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
240 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
241 }
242 void initializeGL()
243 {
244 QOpenGLFunctions::initializeOpenGLFunctions();
245 createShaderProgram(); m_pgm.bind();
246 // Set lighting information
247 m_pgm.setUniformValue("lightSource.ambient",  QVector3D( 0.0f, 0.0f, 0.0f )); // opengl fixed-function default
248 m_pgm.setUniformValue("lightSource.diffuse",  QVector3D( 1.0f, 1.0f, 1.0f )); // opengl fixed-function default
249 m_pgm.setUniformValue("lightSource.specular", QVector3D( 1.0f, 1.0f, 1.0f )); // opengl fixed-function default
250 m_pgm.setUniformValue("lightSource.position", QVector3D( 1.0f, 1.0f, 1.0f )); // NOT DEFAULT VALUE
251 m_pgm.setUniformValue("lightModel.ambient",   QVector3D( 0.2f, 0.2f, 0.2f )); // opengl fixed-function default
252 m_pgm.setUniformValue("material.emission",    QVector3D( 0.0f, 0.0f, 0.0f )); // opengl fixed-function default
253 m_pgm.setUniformValue("material.specular",    QVector3D( 1.0f, 1.0f, 1.0f )); // NOT DEFAULT VALUE
254 m_pgm.setUniformValue("material.shininess",   10.0f);                         // NOT DEFAULT VALUE
255 createGeometry();
256 m_view.setToIdentity();
257 glEnable(GL_DEPTH_TEST);
258 glEnable(GL_TEXTURE_2D);
259 glActiveTexture(GL_TEXTURE0);
260 m_pgm.setUniformValue("texUnit", 0);
261 createTexture();
262 glClearColor(.5f,.5f,.5f,1.f);
263 }
264 void resizeGL(int w, int h)
265 {
266 glViewport(0, 0, w, h);
267 m_projection.setToIdentity();
268 if (w <= h)
269 m_projection.ortho(-2.f, 2.f, -2.f*h/w, 2.f*h/w, -2.f, 2.f);
270 else
271 m_projection.ortho(-2.f*w/h, 2.f*w/h, -2.f, 2.f, -2.f, 2.f);
272 update();
273 }
274 void paintGL()
275 {
276 static unsigned cnt;
277 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
278 glActiveTexture(GL_TEXTURE0);
279 glBindTexture(GL_TEXTURE_2D, m_tex);
280 QMatrix4x4 model;
281 model.rotate(cnt%360, 1,0,0);
282 model.rotate(45, 0,0,1);
283 QMatrix4x4 mv = m_view * model;
284 m_pgm.bind();
285 m_pgm.setUniformValue("modelViewMatrix", mv);
286 m_pgm.setUniformValue("normalMatrix", mv.normalMatrix());
287 m_pgm.setUniformValue("projectionMatrix", m_projection);
288 m_vao.bind();
289 glDrawElements(GL_TRIANGLES, m_cnt, GL_UNSIGNED_BYTE, 0);
290 update();
291 ++cnt;
292 }
293 void keyPressEvent(QKeyEvent * ev)
294 {
295 switch (ev->key()) {
296 case Qt::Key_Escape:
297 exit(0);
298 break;
299 default:
300 QOpenGLWindow::keyPressEvent(ev);
301 break;
302 }
303 }
304 QMatrix4x4 m_projection, m_view;
305 QOpenGLShaderProgram     m_pgm;
306 QOpenGLVertexArrayObject m_vao;
307 QOpenGLBuffer            m_vbo;
308 QOpenGLBuffer            m_ibo;
309 GLuint                   m_tex;
310 GLsizei                  m_cnt;
311 };
312 int main(int argc, char *argv[])
313 {
314 QGuiApplication a(argc,argv);
315 Window w;
316 w.setWidth(640); w.setHeight(480);
317 w.show();
318 return a.exec();
319 }