0b53eee3aace592eb49e2a730b21f9c77c8c4faa
[opengl.git] / main.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <iostream>
4 #include <fstream>
5 #include <sstream>
6 #include <array>
7 #include <vector>
8 #ifdef __APPLE__
9 #include <GL/glew.h>
10 #else
11 #include <OpenGL/glew.h>
12 #endif
13 #include <GLUT/glut.h>
14 #include <glm/glm.hpp>
15 #include <glm/ext.hpp>
16 #include <glm/gtc/type_ptr.hpp>
17 #include "teapot.h"
18
19 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
20
21 using namespace std;
22
23 GLuint pyramidVao, lightVao, teapotVao;
24 GLuint gradientProgId, solidProgId, normalProgId;
25 glm::vec3 camPos   = glm::vec3(0.0f, 0.0f,  -10.0f);
26 glm::vec3 camFront = glm::vec3(0.0f, 0.0f, 1.0f);
27 glm::vec3 camUp    = glm::vec3(0.0f, 1.0f,  0.0f);
28 float yaw = 1.57, pitch = 0;
29 bool doScale, doRotate, doTranslate;
30
31 const int WIDTH = 800, HEIGHT = 600;
32 const float ASPECT = (float)WIDTH / (float)HEIGHT;
33
34 void setProjectionAndViewUniforms(GLuint progId) {
35         GLuint projId = glGetUniformLocation(progId, "projection");
36         glm::mat4 proj = glm::perspective(glm::radians(45.f), ASPECT, 0.01f, 10000.f);
37         glUniformMatrix4fv(projId, 1, GL_FALSE, glm::value_ptr(proj));
38
39         GLuint viewId = glGetUniformLocation(progId, "view");
40         glm::mat4 view = glm::lookAt(camPos, camPos + camFront, camUp);
41         glUniformMatrix4fv(viewId, 1, GL_FALSE, glm::value_ptr(view));
42 }
43
44 void setLightColorAndPos(GLuint progId, glm::vec3 lightPos, glm::vec4 lightColor) {
45         GLuint lightColorLoc = glGetUniformLocation(progId, "lightColor");
46         glUniform4fv(lightColorLoc, 1, glm::value_ptr(lightColor));
47
48         GLuint lightPosLoc = glGetUniformLocation(progId, "lightPos");
49         glUniform3fv(lightPosLoc, 1, glm::value_ptr(lightPos));
50 }
51
52 void drawLight(float d, glm::vec3 lightPos, glm::vec4 lightColor) {
53         glUseProgram(solidProgId);
54         glBindVertexArray(lightVao);
55         setProjectionAndViewUniforms(solidProgId);
56         glm::mat4 model = glm::translate(glm::mat4(1.f), lightPos);
57         model = glm::scale(model, glm::vec3(0.2));
58         GLuint modelId = glGetUniformLocation(solidProgId, "model");
59         glUniformMatrix4fv(modelId, 1, GL_FALSE, glm::value_ptr(model));
60
61         GLuint colorLoc = glGetUniformLocation(solidProgId, "color");
62         glm::vec4 color(lightColor);
63         glUniform4fv(colorLoc, 1, glm::value_ptr(color));
64                 
65         glDrawArrays(GL_TRIANGLES, 0, 36);
66 };
67
68 void drawPyramids(float d, glm::vec3 lightPos, glm::vec4 lightColor) {
69         glUseProgram(gradientProgId);
70         glBindVertexArray(pyramidVao);
71         setProjectionAndViewUniforms(gradientProgId);
72
73         setLightColorAndPos(gradientProgId, lightPos, lightColor);
74
75         GLuint viewPosLoc = glGetUniformLocation(gradientProgId, "viewPos");
76         glUniform3fv(viewPosLoc, 1, glm::value_ptr(camPos));
77
78         GLuint modelId = glGetUniformLocation(gradientProgId, "model");
79
80         for (int i = 0; i < 10; i++) {
81
82                 glm::mat4 model = glm::mat4(1.f);
83
84                 model = glm::translate(model, glm::vec3(sin(i * 30) * 10, 0, i * 2 - 10));
85                 
86                 if (doRotate) {
87                         model = glm::rotate(model, d * glm::radians(30.f), glm::vec3(0.f, 1.f, 0.f));
88                         model = glm::rotate(model, d * glm::radians(20.f), glm::vec3(1.f, 0.f, 0.f));
89                 }
90                 
91                 if (doScale)
92                         model = glm::scale(model, glm::vec3(1.f, 0.7f + 0.7f * (1 + sin(d + (i + 3))), 1.f));
93
94                 if (doTranslate)
95                         model = glm::translate(model, glm::vec3(sin(d + (i + 1)), cos(d + (i + -3)), sin(d + (i + 4))));
96         
97
98                 glUniformMatrix4fv(modelId, 1, GL_FALSE, glm::value_ptr(model));
99                 
100                 glDrawArrays(GL_TRIANGLES, 0, 18);
101         }
102
103 };
104
105 enum TeapotProjection { teapotOrtho, teapotCamera, teapotPerspStatic, teapotPerspAnimated };
106
107 void drawTeapot(TeapotProjection proj, bool rotate, float d, glm::vec3 lightPos, glm::vec4 lightColor) {
108         glUseProgram(normalProgId);
109         
110         GLuint projId = glGetUniformLocation(normalProgId, "projection");
111         GLuint viewId = glGetUniformLocation(normalProgId, "view");
112         switch (proj) {
113         case teapotCamera:
114                 setProjectionAndViewUniforms(normalProgId);
115                 break;
116         case teapotOrtho:
117                 {
118                         glm::mat4 proj = glm::ortho(-5.f * ASPECT, 5.f * ASPECT, -5.f, 5.f, 0.01f, 1000.f);
119                         glUniformMatrix4fv(projId, 1, GL_FALSE, glm::value_ptr(proj));
120
121                         glm::vec3 camPos(-5, 5, -5), camFront = glm::vec3(0) - camPos;
122                         glm::mat4 view = glm::lookAt(camPos, camPos + camFront, camUp);
123                         glUniformMatrix4fv(viewId, 1, GL_FALSE, glm::value_ptr(view));
124                 }
125                 break;
126         case teapotPerspStatic:
127         case teapotPerspAnimated:
128                 {
129                         float fov = glm::radians(45.f);
130                         if (proj == teapotPerspAnimated)
131                                 fov += glm::radians(sin(d) * 30.f);
132                         glm::mat4 proj = glm::perspective(fov, ASPECT, 0.01f, 10000.f);
133                         glUniformMatrix4fv(projId, 1, GL_FALSE, glm::value_ptr(proj));
134
135                         glm::mat4 view = glm::lookAt(glm::vec3(0, 0, -10), glm::vec3(0), camUp);
136                         glUniformMatrix4fv(viewId, 1, GL_FALSE, glm::value_ptr(view));
137                 }
138                 break;
139         }
140         setLightColorAndPos(normalProgId, lightPos, lightColor);
141         glBindVertexArray(teapotVao);
142         GLuint modelId = glGetUniformLocation(normalProgId, "model");
143         glm::mat4 model(1);
144         model = glm::scale(model, glm::vec3(0.3));
145         if (rotate) model = glm::rotate(model, d, glm::vec3(0, 1, 0));
146         glUniformMatrix4fv(modelId, 1, GL_FALSE, glm::value_ptr(model));
147
148         glDrawArrays(GL_TRIANGLES, 0, teapot_vertex_count);
149 }
150
151 void display() {
152         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
153         float d = (float)glutGet(GLUT_ELAPSED_TIME) * 0.001f;
154
155         glm::vec3 lightPos = glm::vec3(sin(d) * 10, 0, cos(d) * 10);
156         glm::vec4 lightColor(1, 1, 1, 1);
157
158         glViewport(0, 0, WIDTH, HEIGHT);
159         drawPyramids(d, lightPos, lightColor);
160         drawLight(d, lightPos, lightColor);
161         drawTeapot(teapotCamera, false, d, lightPos, lightColor);
162
163         glClear(GL_DEPTH_BUFFER_BIT);
164
165         glViewport(0, 0, WIDTH / 4, HEIGHT / 4);
166         drawTeapot(teapotOrtho, false, d, lightPos, lightColor);
167
168         glViewport(WIDTH / 4, 0, WIDTH / 4, HEIGHT / 4);
169         drawTeapot(teapotOrtho, true, d, lightPos, lightColor);
170
171         glViewport(WIDTH / 4 * 2, 0, WIDTH / 4, HEIGHT / 4);
172         drawTeapot(teapotPerspStatic, false, d, lightPos, lightColor);
173
174         glViewport(WIDTH / 4 * 3, 0, WIDTH / 4, HEIGHT / 4);
175         drawTeapot(teapotPerspAnimated, false, d, lightPos, lightColor);
176
177         glutSwapBuffers();
178 }
179
180 void attachShader(GLuint progId, const char* filePath, GLenum type) {
181         GLuint shader = glCreateShader(type);
182
183         if (!shader) {
184                 fprintf(stderr, "error creating shader\n");
185                 exit(1);
186         }
187
188         ifstream file(filePath);
189         stringstream buffer;
190         buffer << file.rdbuf();
191         string str = buffer.str();
192         const char* contents = str.c_str();
193
194         glShaderSource(shader, 1, (const GLchar**)&contents, NULL);
195         glCompileShader(shader);
196         GLint success;
197         glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
198         if (!success) {
199                 GLchar log[1024];
200                 glGetShaderInfoLog(shader, 1024, NULL, log);
201                 fprintf(stderr, "error: %s\n", log);
202                 exit(1);
203         }
204         glAttachShader(progId, shader);
205 }
206
207 GLuint compileShaders(char* vertexShader, char* fragmentShader) {
208         GLuint progId = glCreateProgram();
209
210         attachShader(progId, vertexShader, GL_VERTEX_SHADER);
211         attachShader(progId, fragmentShader, GL_FRAGMENT_SHADER);
212
213         glLinkProgram(progId);
214         GLint success = 0;
215         glGetProgramiv(progId, GL_LINK_STATUS, &success);
216         if (!success) {
217                 GLchar log[1024];
218                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
219                 fprintf(stderr, "error linking: %s\n", log);
220                 exit(1);
221         }
222
223         return progId;
224 }
225
226 #define BUFFER_OFFSET(i) ((char *)NULL + (i))
227
228 GLuint setupBuffers(glm::vec3* vertices, glm::vec3* normals, GLuint progId) {
229
230         GLfloat colors[] = {
231                 0, 1, 0, 1,
232                 1, 0, 0, 1,
233                 0, 0, 1, 1,
234
235                 0, 1, 0, 1,
236                 1, 0, 0, 1,
237                 0, 0, 1, 1,
238
239                 0, 1, 0, 1,
240                 1, 0, 0, 1,
241                 0, 0, 1, 1,
242
243                 0, 1, 0, 1,
244                 1, 0, 0, 1,
245                 0, 0, 1, 1,
246
247                 0, 1, 0, 1,
248                 1, 0, 0, 1,
249                 0, 0, 1, 1,
250                 
251                 0, 1, 0, 1,
252                 1, 0, 0, 1,
253                 0, 0, 1, 1
254         };
255
256         GLuint numVerts = 18;
257
258         GLuint vbo;
259         glGenBuffers(1, &vbo);
260
261         GLuint vao;
262         glGenVertexArrays(1, &vao);
263
264         GLuint posId = glGetAttribLocation(progId, "vPosition");
265         GLuint colorId = glGetAttribLocation(progId, "vColor");
266         GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
267
268         GLuint vertsLen = numVerts * 3 * sizeof(GLfloat);
269         GLuint colorsLen = numVerts * 4 * sizeof(GLfloat);
270         GLuint normalLen = numVerts * 3 * sizeof(GLfloat);
271
272         glBindBuffer(GL_ARRAY_BUFFER, vbo);
273         glBufferData(GL_ARRAY_BUFFER, vertsLen + colorsLen + normalLen, NULL, GL_STATIC_DRAW);
274
275         glBufferSubData(GL_ARRAY_BUFFER, 0, vertsLen, glm::value_ptr(vertices[0]));
276         glBufferSubData(GL_ARRAY_BUFFER, vertsLen, colorsLen, colors);
277         glBufferSubData(GL_ARRAY_BUFFER, vertsLen + colorsLen, normalLen, glm::value_ptr(normals[0]));
278         
279         glBindVertexArray(vao);
280
281         glEnableVertexAttribArray(posId);
282         glVertexAttribPointer(posId, 3, GL_FLOAT, GL_FALSE, 0, 0);
283
284         glEnableVertexAttribArray(colorId);
285         glVertexAttribPointer(colorId, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen));
286
287         glEnableVertexAttribArray(normalLoc);
288         glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen + colorsLen));
289
290         return vao;
291 }
292
293 vector<glm::vec3> quadToTriangles(glm::vec3 *quads) {
294         vector<glm::vec3> triangles(6);
295         triangles[0] = quads[0];
296         triangles[1] = quads[1];
297         triangles[2] = quads[2];
298         triangles[3] = quads[2];
299         triangles[4] = quads[3];
300         triangles[5] = quads[0];
301         return triangles;
302 }
303
304 template <typename T>
305 void append(vector<T> &a, vector<T> &b) {
306         a.insert(a.end(), b.begin(), b.end());
307 };
308
309 void setupLightBuffers(GLuint progId) {
310         vector<glm::vec3> vertices;
311         glm::vec3 front[] = {
312                 glm::vec3(1, -1, -1),
313                 glm::vec3(-1, -1, -1),
314                 glm::vec3(-1, 1, -1),
315                 glm::vec3(1, 1, -1)
316         };
317         vector<glm::vec3> frontTriangles = quadToTriangles(front);
318         append(vertices, frontTriangles);
319
320         glm::vec3 back[] = {
321                 glm::vec3(1, 1, 1),
322                 glm::vec3(-1, 1, 1),
323                 glm::vec3(-1, -1, 1),
324                 glm::vec3(1, -1, 1)
325         };
326         vector<glm::vec3> backQuads = quadToTriangles(back);
327         append(vertices, backQuads);
328
329         glm::vec3 top[] = {
330                 glm::vec3(1, 1, -1),
331                 glm::vec3(-1, 1, -1),
332                 glm::vec3(-1, 1, 1),
333                 glm::vec3(1, 1, 1)
334         };
335         vector<glm::vec3> topTriangles = quadToTriangles(top);
336         append(vertices, topTriangles);
337
338         glm::vec3 bottom[] = {
339                 glm::vec3(1, -1, 1),
340                 glm::vec3(-1, -1, 1),
341                 glm::vec3(-1, -1, -1),
342                 glm::vec3(1, -1, -1)
343         };
344         vector<glm::vec3> bottomTriangles = quadToTriangles(bottom);
345         append(vertices, bottomTriangles);
346
347         glm::vec3 left[] = {
348                 glm::vec3(-1, 1, 1),
349                 glm::vec3(-1, 1, -1),
350                 glm::vec3(-1, -1, -1),
351                 glm::vec3(-1, -1, 1)
352         };
353         vector<glm::vec3> leftTriangles = quadToTriangles(left);
354         append(vertices, leftTriangles);
355
356         glm::vec3 right[] = {
357                 glm::vec3(1, 1, -1),
358                 glm::vec3(1, 1, 1),
359                 glm::vec3(1, -1, 1),
360                 glm::vec3(1, -1, -1)
361         };
362         vector<glm::vec3> rightTriangles = quadToTriangles(right);
363         append(vertices, rightTriangles);
364         GLuint verticesSize = 36 * 3 * sizeof(GLfloat);
365
366         glGenVertexArrays(1, &lightVao);
367         GLuint vbo;
368         glBindVertexArray(lightVao);
369         glGenBuffers(1, &vbo);
370         glBindBuffer(GL_ARRAY_BUFFER, vbo);
371         glBufferData(GL_ARRAY_BUFFER, verticesSize, NULL, GL_STATIC_DRAW);
372         glBufferSubData(GL_ARRAY_BUFFER, 0, verticesSize, glm::value_ptr(vertices[0]));
373         GLuint posLoc = glGetAttribLocation(progId, "vPosition");
374         glEnableVertexAttribArray(posLoc);
375         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
376 }
377
378 void setupTeapotBuffers(GLuint progId) {
379         GLuint vbos[2];
380         glGenBuffers(2, vbos);
381         glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
382         glBufferData(GL_ARRAY_BUFFER, 3 * teapot_vertex_count * sizeof(float), teapot_vertex_points, GL_STATIC_DRAW);
383         glBindBuffer(GL_ARRAY_BUFFER, vbos[1]);
384         glBufferData(GL_ARRAY_BUFFER, 3 * teapot_vertex_count * sizeof(float), teapot_normals, GL_STATIC_DRAW);
385
386         glGenVertexArrays(1, &teapotVao);
387         glBindVertexArray(teapotVao);
388
389         GLuint posLoc = glGetAttribLocation(progId, "vPosition");
390         GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
391
392         glEnableVertexAttribArray(posLoc);
393         glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
394         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL);
395         
396         glEnableVertexAttribArray(normalLoc);
397         glBindBuffer(GL_ARRAY_BUFFER, vbos[1]);
398         glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL);
399 }
400
401 void validateProgram(GLuint progId) {
402         glValidateProgram(progId);
403         
404         GLint success;
405         glGetProgramiv(progId, GL_VALIDATE_STATUS, &success);
406         if (!success) {
407                 GLchar log[1024];
408                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
409                 fprintf(stderr, "error: %s\n", log);
410                 exit(1);
411         }
412 }
413
414 void init() {
415         glm::vec3 vertices[18] = {
416                 glm::vec3(0.0f, 1.0f, 0.0f),
417                 glm::vec3(1.0f, -1.0f, -1.0f),
418                 glm::vec3(-1.0f, -1.0f, -1.0f),
419
420                 glm::vec3(0.0f, 1.0f, 0.0f),
421                 glm::vec3(-1.0f, -1.0f, 1.0f),
422                 glm::vec3(1.0f, -1.0f, 1.0f),
423
424                 glm::vec3(0.0f, 1.0f, 0.0f),
425                 glm::vec3(-1.0f, -1.0f, -1.0f),
426                 glm::vec3(-1.0f, -1.0f, 1.0f),
427
428                 glm::vec3(0.0f, 1.0f, 0.0f),
429                 glm::vec3(1.0f, -1.0f, 1.0f),
430                 glm::vec3(1.0f, -1.0f, -1.0f),
431
432                 glm::vec3(1, -1, 1),
433                 glm::vec3(-1, -1, 1),
434                 glm::vec3(-1, -1, -1),
435                 glm::vec3(-1, -1, -1),
436                 glm::vec3(1, -1, -1),
437                 glm::vec3(1, -1, 1)
438         };
439
440         // work out the normals
441         glm::vec3 normals[18];
442         for (int i = 0; i < 6; i++) {
443                 glm::vec3 a = vertices[i * 3];
444                 glm::vec3 b = vertices[i * 3 + 1];
445                 glm::vec3 c = vertices[i * 3 + 2];
446                 glm::vec3 u = glm::normalize(a - c);
447                 glm::vec3 v = glm::normalize(b - c);
448                 glm::vec3 norm = glm::normalize(glm::cross(v, u));
449                 for(int j = 0; j < 3; j++) {
450                         normals[i * 3 + j] = -glm::vec3(norm);
451                 }
452         }
453
454         gradientProgId = compileShaders((char*)"vertex.glsl", (char*)"fragment.glsl");
455         glUseProgram(gradientProgId);
456         pyramidVao = setupBuffers(vertices, normals, gradientProgId);
457         validateProgram(gradientProgId);
458
459         solidProgId = compileShaders((char*)"solidvertex.glsl", (char*)"solidfrag.glsl");
460         glUseProgram(solidProgId);
461         setupLightBuffers(solidProgId);
462         validateProgram(solidProgId);
463
464         normalProgId = compileShaders((char*)"vertex.glsl", (char*)"normalfrag.glsl");
465         glUseProgram(normalProgId);
466         setupTeapotBuffers(normalProgId);
467         validateProgram(normalProgId);
468
469         glEnable(GL_DEPTH_TEST); 
470         glEnable(GL_CULL_FACE); 
471 }
472
473 bool* keyStates = new bool[256];
474
475 void keyboard(unsigned char key, int x, int y) {
476         keyStates[key] = true;
477         if (key == 'z')
478                 doScale = !doScale;
479         if (key == 'x')
480                 doRotate = !doRotate;
481         if (key == 'c')
482                 doTranslate = !doTranslate;
483 }
484
485 void keyboardUp(unsigned char key, int x, int y) {
486         keyStates[key] = false;
487 }
488
489 void timer(int _) {
490         float xSpeed = 0.f, ySpeed = 0.f, zSpeed = 0.f;
491         if (keyStates['w'])
492                 zSpeed = 0.1f;
493         if (keyStates['s'])
494                 zSpeed = -0.1f;
495         if (keyStates['a'])
496                 xSpeed = 0.1f;
497         if (keyStates['d'])
498                 xSpeed = -0.1f;
499         if (keyStates['q'])
500                 ySpeed = 0.1f;
501         if (keyStates['e'])
502                 ySpeed = -0.1f;
503
504         camPos.x += xSpeed * sin(yaw) + zSpeed * cos(yaw);
505         camPos.y += ySpeed;
506         camPos.z += zSpeed * sin(yaw) - xSpeed * cos(yaw);
507         glutPostRedisplay();
508         glutTimerFunc(16, timer, 0);
509 }
510
511 int prevMouseX, prevMouseY;
512 bool firstMouse = true;
513
514 void motion(int x, int y) {
515         if (firstMouse) {
516                 prevMouseX = x;
517                 prevMouseY = y;
518                 firstMouse = false;
519         }
520         int dx = x - prevMouseX, dy = y - prevMouseY;
521
522         prevMouseX = x;
523         prevMouseY = y;
524
525         const float sensitivity = 0.005f;
526         yaw += dx * sensitivity;
527         pitch -= dy * sensitivity;
528
529         glm::vec3 front;
530         front.x = cos(pitch) * cos(yaw);
531         front.y = sin(pitch);
532         front.z = cos(pitch) * sin(yaw);
533         camFront = glm::normalize(front);
534 }
535
536 void mouse(int button, int state, int x, int y) {
537         if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
538                 firstMouse = true;
539 }
540
541 int main(int argc, char** argv) {
542         glutInit(&argc, argv);
543         glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGB|GLUT_3_2_CORE_PROFILE);
544         glutInitWindowSize(WIDTH, HEIGHT);
545         int win = glutCreateWindow("Hello Triangle");
546         glutDisplayFunc(display);
547
548         glewInit();
549         
550         init();
551
552         glutKeyboardFunc(keyboard);
553         glutKeyboardUpFunc(keyboardUp);
554         glutTimerFunc(16, timer, 0);
555         glutMotionFunc(motion);
556         glutMouseFunc(mouse);
557
558         glutMainLoop();
559
560         return 0;
561 }
562