Add solid shader and light cube thing
[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
18 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
19
20 using namespace std;
21
22 GLuint pyramidVao, lightVao;
23 GLuint gradientProgId, solidProgId;
24 glm::vec3 camPos   = glm::vec3(0.0f, 0.0f,  -5.0f);
25 glm::vec3 camFront = glm::vec3(0.0f, 0.0f, 1.0f);
26 glm::vec3 camUp    = glm::vec3(0.0f, 1.0f,  0.0f);
27 float yaw = 1.57, pitch = 0;
28 bool doScale, doRotate, doTranslate;
29
30 void setProjectionAndViewUniforms(GLuint progId) {
31         GLuint projId = glGetUniformLocation(progId, "projection");
32         glm::mat4 proj = glm::perspective(glm::radians(45.f), 1.33f, 0.01f, 10000.f);
33         glUniformMatrix4fv(projId, 1, GL_FALSE, glm::value_ptr(proj));
34
35         GLuint viewId = glGetUniformLocation(progId, "view");
36         glm::mat4 view = glm::lookAt(camPos, camPos + camFront, camUp);
37         glUniformMatrix4fv(viewId, 1, GL_FALSE, glm::value_ptr(view));
38 }
39
40 void drawLight(float d, glm::vec3 lightPos) {
41         glUseProgram(solidProgId);
42         glBindVertexArray(lightVao);
43         setProjectionAndViewUniforms(solidProgId);
44         glm::mat4 model = glm::translate(glm::mat4(1.f), lightPos);
45         model = glm::scale(model, glm::vec3(0.2));
46         GLuint modelId = glGetUniformLocation(solidProgId, "model");
47         glUniformMatrix4fv(modelId, 1, GL_FALSE, glm::value_ptr(model));
48
49         GLuint colorLoc = glGetUniformLocation(solidProgId, "color");
50         glm::vec3 color = glm::vec3(1, 1, 1);
51         glUniform3fv(colorLoc, 1, glm::value_ptr(color));
52                 
53         glDrawArrays(GL_TRIANGLES, 0, 36);
54 };
55
56 void drawPyramids(float d, glm::vec3 lightPos) {
57         glUseProgram(gradientProgId);
58         glBindVertexArray(pyramidVao);
59         setProjectionAndViewUniforms(gradientProgId);
60
61         GLuint lightPosLoc = glGetUniformLocation(gradientProgId, "lightPos");
62         glUniform3fv(lightPosLoc, 1, glm::value_ptr(lightPos));
63         
64         GLuint viewPosLoc = glGetUniformLocation(gradientProgId, "viewPos");
65         glUniform3fv(viewPosLoc, 1, glm::value_ptr(camPos));
66
67         GLuint modelId = glGetUniformLocation(gradientProgId, "model");
68
69         for (int i = 0; i < 10; i++) {
70
71                 glm::mat4 model = glm::mat4(1.f);
72
73                 model = glm::translate(model, glm::vec3(sin(i * 30) * 10, 0, i * 2 - 10));
74                 
75                 if (doRotate) {
76                         model = glm::rotate(model, d * glm::radians(30.f), glm::vec3(0.f, 1.f, 0.f));
77                         model = glm::rotate(model, d * glm::radians(20.f), glm::vec3(1.f, 0.f, 0.f));
78                 }
79                 
80                 if (doScale)
81                         model = glm::scale(model, glm::vec3(1.f, 0.7f + 0.7f * (1 + sin(d + (i + 3))), 1.f));
82
83                 if (doTranslate)
84                         model = glm::translate(model, glm::vec3(sin(d + (i + 1)), cos(d + (i + -3)), sin(d + (i + 4))));
85         
86
87                 glUniformMatrix4fv(modelId, 1, GL_FALSE, glm::value_ptr(model));
88                 
89                 glDrawArrays(GL_TRIANGLES, 0, 12);
90         }
91
92 };
93
94 void display() {
95         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
96         float d = (float)glutGet(GLUT_ELAPSED_TIME) * 0.001f;
97
98         glm::vec3 lightPos = glm::vec3(sin(d) * 10, 0, cos(d) * 10);
99
100         drawPyramids(d, lightPos);
101         drawLight(d, lightPos);
102
103         glutSwapBuffers();
104 }
105
106 void attachShader(GLuint progId, const char* filePath, GLenum type) {
107         GLuint shader = glCreateShader(type);
108
109         if (!shader) {
110                 fprintf(stderr, "error creating shader\n");
111                 exit(1);
112         }
113
114         ifstream file(filePath);
115         stringstream buffer;
116         buffer << file.rdbuf();
117         string str = buffer.str();
118         const char* contents = str.c_str();
119
120         glShaderSource(shader, 1, (const GLchar**)&contents, NULL);
121         glCompileShader(shader);
122         GLint success;
123         glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
124         if (!success) {
125                 GLchar log[1024];
126                 glGetShaderInfoLog(shader, 1024, NULL, log);
127                 fprintf(stderr, "error: %s\n", log);
128                 exit(1);
129         }
130         glAttachShader(progId, shader);
131 }
132
133 GLuint compileShaders(char* vertexShader, char* fragmentShader) {
134         GLuint progId = glCreateProgram();
135
136         attachShader(progId, vertexShader, GL_VERTEX_SHADER);
137         attachShader(progId, fragmentShader, GL_FRAGMENT_SHADER);
138
139         glLinkProgram(progId);
140         GLint success = 0;
141         glGetProgramiv(progId, GL_LINK_STATUS, &success);
142         if (!success) {
143                 GLchar log[1024];
144                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
145                 fprintf(stderr, "error linking: %s\n", log);
146                 exit(1);
147         }
148
149         return progId;
150 }
151
152 #define BUFFER_OFFSET(i) ((char *)NULL + (i))
153
154 GLuint setupBuffers(glm::vec3* vertices, glm::vec3* normals, GLuint progId) {
155
156         GLfloat colors[] = {
157                 0, 1, 0, 1,
158                 1, 0, 0, 1,
159                 0, 0, 1, 1,
160
161                 0, 1, 0, 1,
162                 1, 0, 0, 1,
163                 0, 0, 1, 1,
164
165                 0, 1, 0, 1,
166                 1, 0, 0, 1,
167                 0, 0, 1, 1,
168
169                 0, 1, 0, 1,
170                 1, 0, 0, 1,
171                 0, 0, 1, 1
172         };
173
174         GLuint numVerts = 12;
175
176         GLuint vbo;
177         glGenBuffers(1, &vbo);
178
179         GLuint vao;
180         glGenVertexArrays(1, &vao);
181
182         GLuint posId = glGetAttribLocation(progId, "vPosition");
183         GLuint colorId = glGetAttribLocation(progId, "vColor");
184         GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
185
186         GLuint vertsLen = numVerts * 3 * sizeof(GLfloat);
187         GLuint colorsLen = numVerts * 4 * sizeof(GLfloat);
188         GLuint normalLen = numVerts * 3 * sizeof(GLfloat);
189
190         glBindBuffer(GL_ARRAY_BUFFER, vbo);
191         glBufferData(GL_ARRAY_BUFFER, vertsLen + colorsLen + normalLen, NULL, GL_STATIC_DRAW);
192
193         glBufferSubData(GL_ARRAY_BUFFER, 0, vertsLen, glm::value_ptr(vertices[0]));
194         glBufferSubData(GL_ARRAY_BUFFER, vertsLen, colorsLen, colors);
195         glBufferSubData(GL_ARRAY_BUFFER, vertsLen + colorsLen, normalLen, glm::value_ptr(normals[0]));
196         
197         glBindVertexArray(vao);
198
199         glEnableVertexAttribArray(posId);
200         glVertexAttribPointer(posId, 3, GL_FLOAT, GL_FALSE, 0, 0);
201
202         glEnableVertexAttribArray(colorId);
203         glVertexAttribPointer(colorId, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen));
204
205         glEnableVertexAttribArray(normalLoc);
206         glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen + colorsLen));
207
208         return vao;
209 }
210
211 vector<glm::vec3> quadToTriangles(glm::vec3 *quads) {
212         vector<glm::vec3> triangles(6);
213         triangles[0] = quads[0];
214         triangles[1] = quads[1];
215         triangles[2] = quads[2];
216         triangles[3] = quads[2];
217         triangles[4] = quads[3];
218         triangles[5] = quads[0];
219         return triangles;
220 }
221
222 template <typename T>
223 void append(vector<T> &a, vector<T> &b) {
224         a.insert(a.end(), b.begin(), b.end());
225 };
226
227 void setupLightBuffers(GLuint progId) {
228         vector<glm::vec3> vertices;
229         glm::vec3 front[] = {
230                 glm::vec3(1, -1, -1),
231                 glm::vec3(-1, -1, -1),
232                 glm::vec3(-1, 1, -1),
233                 glm::vec3(1, 1, -1)
234         };
235         vector<glm::vec3> frontTriangles = quadToTriangles(front);
236         append(vertices, frontTriangles);
237
238         glm::vec3 back[] = {
239                 glm::vec3(1, 1, 1),
240                 glm::vec3(-1, 1, 1),
241                 glm::vec3(-1, -1, 1),
242                 glm::vec3(1, -1, 1)
243         };
244         vector<glm::vec3> backQuads = quadToTriangles(back);
245         append(vertices, backQuads);
246
247         glm::vec3 top[] = {
248                 glm::vec3(1, 1, -1),
249                 glm::vec3(-1, 1, -1),
250                 glm::vec3(-1, 1, 1),
251                 glm::vec3(1, 1, 1)
252         };
253         vector<glm::vec3> topTriangles = quadToTriangles(top);
254         append(vertices, topTriangles);
255
256         glm::vec3 bottom[] = {
257                 glm::vec3(1, -1, 1),
258                 glm::vec3(-1, -1, 1),
259                 glm::vec3(-1, -1, -1),
260                 glm::vec3(1, -1, -1)
261         };
262         vector<glm::vec3> bottomTriangles = quadToTriangles(bottom);
263         append(vertices, bottomTriangles);
264
265         glm::vec3 left[] = {
266                 glm::vec3(-1, 1, 1),
267                 glm::vec3(-1, 1, -1),
268                 glm::vec3(-1, -1, -1),
269                 glm::vec3(-1, -1, 1)
270         };
271         vector<glm::vec3> leftTriangles = quadToTriangles(left);
272         append(vertices, leftTriangles);
273
274         glm::vec3 right[] = {
275                 glm::vec3(1, 1, -1),
276                 glm::vec3(1, 1, 1),
277                 glm::vec3(1, -1, 1),
278                 glm::vec3(1, -1, -1)
279         };
280         vector<glm::vec3> rightTriangles = quadToTriangles(right);
281         append(vertices, rightTriangles);
282         GLuint verticesSize = 36 * 3 * sizeof(GLfloat);
283
284         glGenVertexArrays(1, &lightVao);
285         GLuint vbo;
286         glBindVertexArray(lightVao);
287         glGenBuffers(1, &vbo);
288         glBindBuffer(GL_ARRAY_BUFFER, vbo);
289         glBufferData(GL_ARRAY_BUFFER, verticesSize, NULL, GL_STATIC_DRAW);
290         glBufferSubData(GL_ARRAY_BUFFER, 0, verticesSize, glm::value_ptr(vertices[0]));
291         GLuint posLoc = glGetAttribLocation(progId, "vPosition");
292         glEnableVertexAttribArray(posLoc);
293         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
294 }
295
296 void validateProgram(GLuint progId) {
297         glValidateProgram(progId);
298         
299         GLint success;
300         glGetProgramiv(progId, GL_VALIDATE_STATUS, &success);
301         if (!success) {
302                 GLchar log[1024];
303                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
304                 fprintf(stderr, "error: %s\n", log);
305                 exit(1);
306         }
307 }
308
309 void init() {
310         glm::vec3 vertices[12] = {
311                 glm::vec3(0.0f, 1.0f, 0.0f),
312                 glm::vec3(1.0f, -1.0f, -1.0f),
313                 glm::vec3(-1.0f, -1.0f, -1.0f),
314
315                 glm::vec3(0.0f, 1.0f, 0.0f),
316                 glm::vec3(-1.0f, -1.0f, 1.0f),
317                 glm::vec3(1.0f, -1.0f, 1.0f),
318
319                 glm::vec3(0.0f, 1.0f, 0.0f),
320                 glm::vec3(-1.0f, -1.0f, -1.0f),
321                 glm::vec3(-1.0f, -1.0f, 1.0f),
322
323                 glm::vec3(0.0f, 1.0f, 0.0f),
324                 glm::vec3(1.0f, -1.0f, 1.0f),
325                 glm::vec3(1.0f, -1.0f, -1.0f)
326         };
327
328         // work out the normals
329         glm::vec3 normals[12];
330         for (int i = 0; i < 4; i++) {
331                 glm::vec3 a = vertices[i * 3];
332                 glm::vec3 b = vertices[i * 3 + 1];
333                 glm::vec3 c = vertices[i * 3 + 2];
334                 glm::vec3 u = glm::normalize(a - c);
335                 glm::vec3 v = glm::normalize(b - c);
336                 glm::vec3 norm = glm::normalize(glm::cross(v, u));
337                 for(int j = 0; j < 3; j++) {
338                         normals[i * 3 + j] = glm::vec3(norm);
339                 }
340         }
341
342         gradientProgId = compileShaders((char*)"vertex.glsl", (char*)"fragment.glsl");
343         glUseProgram(gradientProgId);
344         pyramidVao = setupBuffers(vertices, normals, gradientProgId);
345         validateProgram(gradientProgId);
346
347         solidProgId = compileShaders((char*)"solidvertex.glsl", (char*)"solidfrag.glsl");
348         glUseProgram(solidProgId);
349         setupLightBuffers(solidProgId);
350         validateProgram(solidProgId);
351
352         glEnable(GL_DEPTH_TEST); 
353         glEnable(GL_CULL_FACE); 
354 }
355
356 bool* keyStates = new bool[256];
357
358 void keyboard(unsigned char key, int x, int y) {
359         keyStates[key] = true;
360         if (key == 'z')
361                 doScale = !doScale;
362         if (key == 'x')
363                 doRotate = !doRotate;
364         if (key == 'c')
365                 doTranslate = !doTranslate;
366 }
367
368 void keyboardUp(unsigned char key, int x, int y) {
369         keyStates[key] = false;
370 }
371
372 void timer(int _) {
373         float xSpeed = 0.f, ySpeed = 0.f, zSpeed = 0.f;
374         if (keyStates['w'])
375                 zSpeed = 0.1f;
376         if (keyStates['s'])
377                 zSpeed = -0.1f;
378         if (keyStates['a'])
379                 xSpeed = 0.1f;
380         if (keyStates['d'])
381                 xSpeed = -0.1f;
382         if (keyStates['q'])
383                 ySpeed = 0.1f;
384         if (keyStates['e'])
385                 ySpeed = -0.1f;
386
387         camPos.x += xSpeed * sin(yaw) + zSpeed * cos(yaw);
388         camPos.y += ySpeed;
389         camPos.z += zSpeed * sin(yaw) - xSpeed * cos(yaw);
390         glutPostRedisplay();
391         glutTimerFunc(16, timer, 0);
392 }
393
394 int prevMouseX, prevMouseY;
395 bool firstMouse = true;
396
397 void motion(int x, int y) {
398         if (firstMouse) {
399                 prevMouseX = x;
400                 prevMouseY = y;
401                 firstMouse = false;
402         }
403         int dx = x - prevMouseX, dy = y - prevMouseY;
404
405         prevMouseX = x;
406         prevMouseY = y;
407
408         const float sensitivity = 0.005f;
409         yaw += dx * sensitivity;
410         pitch -= dy * sensitivity;
411
412         glm::vec3 front;
413         front.x = cos(pitch) * cos(yaw);
414         front.y = sin(pitch);
415         front.z = cos(pitch) * sin(yaw);
416         camFront = glm::normalize(front);
417 }
418
419 void mouse(int button, int state, int x, int y) {
420         if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
421                 firstMouse = true;
422 }
423
424 int main(int argc, char** argv) {
425         glutInit(&argc, argv);
426         glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGB|GLUT_3_2_CORE_PROFILE);
427         glutInitWindowSize(800, 600);
428         int win = glutCreateWindow("Hello Triangle");
429         glutDisplayFunc(display);
430
431         glewInit();
432         
433         init();
434
435         glutKeyboardFunc(keyboard);
436         glutKeyboardUpFunc(keyboardUp);
437         glutTimerFunc(16, timer, 0);
438         glutMotionFunc(motion);
439         glutMouseFunc(mouse);
440
441         glutMainLoop();
442
443         return 0;
444 }
445