4c9171ab71a63c726e19996252e402b4e24949e1
[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         glClearColor(0.5, 0.5, 0.5, 1);
153         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
154         float d = (float)glutGet(GLUT_ELAPSED_TIME) * 0.001f;
155
156         glm::vec3 lightPos = glm::vec3(sin(d) * 10, 0, cos(d) * 10);
157         glm::vec4 lightColor(1, 1, 1, 1);
158
159         glViewport(0, 0, WIDTH, HEIGHT);
160         drawPyramids(d, lightPos, lightColor);
161         drawLight(d, lightPos, lightColor);
162         drawTeapot(teapotCamera, false, d, lightPos, lightColor);
163
164         glClear(GL_DEPTH_BUFFER_BIT);
165
166         glViewport(0, 0, WIDTH / 4, HEIGHT / 4);
167         drawTeapot(teapotOrtho, false, d, lightPos, lightColor);
168
169         glViewport(WIDTH / 4, 0, WIDTH / 4, HEIGHT / 4);
170         drawTeapot(teapotOrtho, true, d, lightPos, lightColor);
171
172         glViewport(WIDTH / 4 * 2, 0, WIDTH / 4, HEIGHT / 4);
173         drawTeapot(teapotPerspStatic, false, d, lightPos, lightColor);
174
175         glViewport(WIDTH / 4 * 3, 0, WIDTH / 4, HEIGHT / 4);
176         drawTeapot(teapotPerspAnimated, false, d, lightPos, lightColor);
177
178         glutSwapBuffers();
179 }
180
181 void attachShader(GLuint progId, const char* filePath, GLenum type) {
182         GLuint shader = glCreateShader(type);
183
184         if (!shader) {
185                 fprintf(stderr, "error creating shader\n");
186                 exit(1);
187         }
188
189         ifstream file(filePath);
190         stringstream buffer;
191         buffer << file.rdbuf();
192         string str = buffer.str();
193         const char* contents = str.c_str();
194
195         glShaderSource(shader, 1, (const GLchar**)&contents, NULL);
196         glCompileShader(shader);
197         GLint success;
198         glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
199         if (!success) {
200                 GLchar log[1024];
201                 glGetShaderInfoLog(shader, 1024, NULL, log);
202                 fprintf(stderr, "error: %s\n", log);
203                 exit(1);
204         }
205         glAttachShader(progId, shader);
206 }
207
208 GLuint compileShaders(char* vertexShader, char* fragmentShader) {
209         GLuint progId = glCreateProgram();
210
211         attachShader(progId, vertexShader, GL_VERTEX_SHADER);
212         attachShader(progId, fragmentShader, GL_FRAGMENT_SHADER);
213
214         glLinkProgram(progId);
215         GLint success = 0;
216         glGetProgramiv(progId, GL_LINK_STATUS, &success);
217         if (!success) {
218                 GLchar log[1024];
219                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
220                 fprintf(stderr, "error linking: %s\n", log);
221                 exit(1);
222         }
223
224         return progId;
225 }
226
227 #define BUFFER_OFFSET(i) ((char *)NULL + (i))
228
229 GLuint setupBuffers(glm::vec3* vertices, glm::vec3* normals, GLuint progId) {
230
231         GLfloat colors[] = {
232                 0, 1, 0, 1,
233                 1, 0, 0, 1,
234                 0, 0, 1, 1,
235
236                 0, 1, 0, 1,
237                 1, 0, 0, 1,
238                 0, 0, 1, 1,
239
240                 0, 1, 0, 1,
241                 1, 0, 0, 1,
242                 0, 0, 1, 1,
243
244                 0, 1, 0, 1,
245                 1, 0, 0, 1,
246                 0, 0, 1, 1,
247
248                 0, 1, 0, 1,
249                 1, 0, 0, 1,
250                 0, 0, 1, 1,
251                 
252                 0, 1, 0, 1,
253                 1, 0, 0, 1,
254                 0, 0, 1, 1
255         };
256
257         GLuint numVerts = 18;
258
259         GLuint vbo;
260         glGenBuffers(1, &vbo);
261
262         GLuint vao;
263         glGenVertexArrays(1, &vao);
264
265         GLuint posId = glGetAttribLocation(progId, "vPosition");
266         GLuint colorId = glGetAttribLocation(progId, "vColor");
267         GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
268
269         GLuint vertsLen = numVerts * 3 * sizeof(GLfloat);
270         GLuint colorsLen = numVerts * 4 * sizeof(GLfloat);
271         GLuint normalLen = numVerts * 3 * sizeof(GLfloat);
272
273         glBindBuffer(GL_ARRAY_BUFFER, vbo);
274         glBufferData(GL_ARRAY_BUFFER, vertsLen + colorsLen + normalLen, NULL, GL_STATIC_DRAW);
275
276         glBufferSubData(GL_ARRAY_BUFFER, 0, vertsLen, glm::value_ptr(vertices[0]));
277         glBufferSubData(GL_ARRAY_BUFFER, vertsLen, colorsLen, colors);
278         glBufferSubData(GL_ARRAY_BUFFER, vertsLen + colorsLen, normalLen, glm::value_ptr(normals[0]));
279         
280         glBindVertexArray(vao);
281
282         glEnableVertexAttribArray(posId);
283         glVertexAttribPointer(posId, 3, GL_FLOAT, GL_FALSE, 0, 0);
284
285         glEnableVertexAttribArray(colorId);
286         glVertexAttribPointer(colorId, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen));
287
288         glEnableVertexAttribArray(normalLoc);
289         glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen + colorsLen));
290
291         return vao;
292 }
293
294 vector<glm::vec3> quadToTriangles(glm::vec3 *quads) {
295         vector<glm::vec3> triangles(6);
296         triangles[0] = quads[0];
297         triangles[1] = quads[1];
298         triangles[2] = quads[2];
299         triangles[3] = quads[2];
300         triangles[4] = quads[3];
301         triangles[5] = quads[0];
302         return triangles;
303 }
304
305 template <typename T>
306 void append(vector<T> &a, vector<T> &b) {
307         a.insert(a.end(), b.begin(), b.end());
308 };
309
310 void setupLightBuffers(GLuint progId) {
311         vector<glm::vec3> vertices;
312         glm::vec3 front[] = {
313                 glm::vec3(1, -1, -1),
314                 glm::vec3(-1, -1, -1),
315                 glm::vec3(-1, 1, -1),
316                 glm::vec3(1, 1, -1)
317         };
318         vector<glm::vec3> frontTriangles = quadToTriangles(front);
319         append(vertices, frontTriangles);
320
321         glm::vec3 back[] = {
322                 glm::vec3(1, 1, 1),
323                 glm::vec3(-1, 1, 1),
324                 glm::vec3(-1, -1, 1),
325                 glm::vec3(1, -1, 1)
326         };
327         vector<glm::vec3> backQuads = quadToTriangles(back);
328         append(vertices, backQuads);
329
330         glm::vec3 top[] = {
331                 glm::vec3(1, 1, -1),
332                 glm::vec3(-1, 1, -1),
333                 glm::vec3(-1, 1, 1),
334                 glm::vec3(1, 1, 1)
335         };
336         vector<glm::vec3> topTriangles = quadToTriangles(top);
337         append(vertices, topTriangles);
338
339         glm::vec3 bottom[] = {
340                 glm::vec3(1, -1, 1),
341                 glm::vec3(-1, -1, 1),
342                 glm::vec3(-1, -1, -1),
343                 glm::vec3(1, -1, -1)
344         };
345         vector<glm::vec3> bottomTriangles = quadToTriangles(bottom);
346         append(vertices, bottomTriangles);
347
348         glm::vec3 left[] = {
349                 glm::vec3(-1, 1, 1),
350                 glm::vec3(-1, 1, -1),
351                 glm::vec3(-1, -1, -1),
352                 glm::vec3(-1, -1, 1)
353         };
354         vector<glm::vec3> leftTriangles = quadToTriangles(left);
355         append(vertices, leftTriangles);
356
357         glm::vec3 right[] = {
358                 glm::vec3(1, 1, -1),
359                 glm::vec3(1, 1, 1),
360                 glm::vec3(1, -1, 1),
361                 glm::vec3(1, -1, -1)
362         };
363         vector<glm::vec3> rightTriangles = quadToTriangles(right);
364         append(vertices, rightTriangles);
365         GLuint verticesSize = 36 * 3 * sizeof(GLfloat);
366
367         glGenVertexArrays(1, &lightVao);
368         GLuint vbo;
369         glBindVertexArray(lightVao);
370         glGenBuffers(1, &vbo);
371         glBindBuffer(GL_ARRAY_BUFFER, vbo);
372         glBufferData(GL_ARRAY_BUFFER, verticesSize, NULL, GL_STATIC_DRAW);
373         glBufferSubData(GL_ARRAY_BUFFER, 0, verticesSize, glm::value_ptr(vertices[0]));
374         GLuint posLoc = glGetAttribLocation(progId, "vPosition");
375         glEnableVertexAttribArray(posLoc);
376         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
377 }
378
379 void setupTeapotBuffers(GLuint progId) {
380         GLuint vbos[2];
381         glGenBuffers(2, vbos);
382         glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
383         glBufferData(GL_ARRAY_BUFFER, 3 * teapot_vertex_count * sizeof(float), teapot_vertex_points, GL_STATIC_DRAW);
384         glBindBuffer(GL_ARRAY_BUFFER, vbos[1]);
385         glBufferData(GL_ARRAY_BUFFER, 3 * teapot_vertex_count * sizeof(float), teapot_normals, GL_STATIC_DRAW);
386
387         glGenVertexArrays(1, &teapotVao);
388         glBindVertexArray(teapotVao);
389
390         GLuint posLoc = glGetAttribLocation(progId, "vPosition");
391         GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
392
393         glEnableVertexAttribArray(posLoc);
394         glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
395         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL);
396         
397         glEnableVertexAttribArray(normalLoc);
398         glBindBuffer(GL_ARRAY_BUFFER, vbos[1]);
399         glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL);
400 }
401
402 void validateProgram(GLuint progId) {
403         glValidateProgram(progId);
404         
405         GLint success;
406         glGetProgramiv(progId, GL_VALIDATE_STATUS, &success);
407         if (!success) {
408                 GLchar log[1024];
409                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
410                 fprintf(stderr, "error: %s\n", log);
411                 exit(1);
412         }
413 }
414
415 void init() {
416         glm::vec3 vertices[18] = {
417                 glm::vec3(0.0f, 1.0f, 0.0f),
418                 glm::vec3(1.0f, -1.0f, -1.0f),
419                 glm::vec3(-1.0f, -1.0f, -1.0f),
420
421                 glm::vec3(0.0f, 1.0f, 0.0f),
422                 glm::vec3(-1.0f, -1.0f, 1.0f),
423                 glm::vec3(1.0f, -1.0f, 1.0f),
424
425                 glm::vec3(0.0f, 1.0f, 0.0f),
426                 glm::vec3(-1.0f, -1.0f, -1.0f),
427                 glm::vec3(-1.0f, -1.0f, 1.0f),
428
429                 glm::vec3(0.0f, 1.0f, 0.0f),
430                 glm::vec3(1.0f, -1.0f, 1.0f),
431                 glm::vec3(1.0f, -1.0f, -1.0f),
432
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                 glm::vec3(1, -1, 1)
439         };
440
441         // work out the normals
442         glm::vec3 normals[18];
443         for (int i = 0; i < 6; i++) {
444                 glm::vec3 a = vertices[i * 3];
445                 glm::vec3 b = vertices[i * 3 + 1];
446                 glm::vec3 c = vertices[i * 3 + 2];
447                 glm::vec3 u = glm::normalize(a - c);
448                 glm::vec3 v = glm::normalize(b - c);
449                 glm::vec3 norm = glm::normalize(glm::cross(v, u));
450                 for(int j = 0; j < 3; j++) {
451                         normals[i * 3 + j] = -glm::vec3(norm);
452                 }
453         }
454
455         gradientProgId = compileShaders((char*)"vertex.glsl", (char*)"fragment.glsl");
456         glUseProgram(gradientProgId);
457         pyramidVao = setupBuffers(vertices, normals, gradientProgId);
458         validateProgram(gradientProgId);
459
460         solidProgId = compileShaders((char*)"solidvertex.glsl", (char*)"solidfrag.glsl");
461         glUseProgram(solidProgId);
462         setupLightBuffers(solidProgId);
463         validateProgram(solidProgId);
464
465         normalProgId = compileShaders((char*)"vertex.glsl", (char*)"normalfrag.glsl");
466         glUseProgram(normalProgId);
467         setupTeapotBuffers(normalProgId);
468         validateProgram(normalProgId);
469
470         glEnable(GL_DEPTH_TEST); 
471         glEnable(GL_CULL_FACE); 
472 }
473
474 bool* keyStates = new bool[256];
475
476 void keyboard(unsigned char key, int x, int y) {
477         keyStates[key] = true;
478         if (key == 'z')
479                 doScale = !doScale;
480         if (key == 'x')
481                 doRotate = !doRotate;
482         if (key == 'c')
483                 doTranslate = !doTranslate;
484 }
485
486 void keyboardUp(unsigned char key, int x, int y) {
487         keyStates[key] = false;
488 }
489
490 void timer(int _) {
491         float xSpeed = 0.f, ySpeed = 0.f, zSpeed = 0.f;
492         if (keyStates['w'])
493                 zSpeed = 0.1f;
494         if (keyStates['s'])
495                 zSpeed = -0.1f;
496         if (keyStates['a'])
497                 xSpeed = 0.1f;
498         if (keyStates['d'])
499                 xSpeed = -0.1f;
500         if (keyStates['q'])
501                 ySpeed = 0.1f;
502         if (keyStates['e'])
503                 ySpeed = -0.1f;
504
505         camPos.x += xSpeed * sin(yaw) + zSpeed * cos(yaw);
506         camPos.y += ySpeed;
507         camPos.z += zSpeed * sin(yaw) - xSpeed * cos(yaw);
508         glutPostRedisplay();
509         glutTimerFunc(16, timer, 0);
510 }
511
512 int prevMouseX, prevMouseY;
513 bool firstMouse = true;
514
515 void motion(int x, int y) {
516         if (firstMouse) {
517                 prevMouseX = x;
518                 prevMouseY = y;
519                 firstMouse = false;
520         }
521         int dx = x - prevMouseX, dy = y - prevMouseY;
522
523         prevMouseX = x;
524         prevMouseY = y;
525
526         const float sensitivity = 0.005f;
527         yaw += dx * sensitivity;
528         pitch -= dy * sensitivity;
529
530         glm::vec3 front;
531         front.x = cos(pitch) * cos(yaw);
532         front.y = sin(pitch);
533         front.z = cos(pitch) * sin(yaw);
534         camFront = glm::normalize(front);
535
536         if (pitch < -1.57079632679 || pitch >= 1.57079632679) {
537                 camUp = glm::vec3(0, -1, 0);
538         } else {
539                 camUp = glm::vec3(0, 1, 0);
540         }
541 }
542
543 void mouse(int button, int state, int x, int y) {
544         if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
545                 firstMouse = true;
546 }
547
548 int main(int argc, char** argv) {
549         glutInit(&argc, argv);
550         glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGB|GLUT_3_2_CORE_PROFILE);
551         glutInitWindowSize(WIDTH, HEIGHT);
552         int win = glutCreateWindow("Hello Triangle");
553         glutDisplayFunc(display);
554
555         glewInit();
556         
557         init();
558
559         glutKeyboardFunc(keyboard);
560         glutKeyboardUpFunc(keyboardUp);
561         glutTimerFunc(16, timer, 0);
562         glutMotionFunc(motion);
563         glutMouseFunc(mouse);
564
565         glutMainLoop();
566
567         return 0;
568 }
569