Add Phong lighting
[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* vaos;
23 GLuint progId;
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 display() {
31         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
32         glUseProgram(progId);
33         glBindVertexArray(vaos[0]);
34
35         float d = (float)glutGet(GLUT_ELAPSED_TIME) * 0.001f;
36
37         GLuint projId = glGetUniformLocation(progId, "projection");
38         glm::mat4 proj = glm::perspective(glm::radians(45.f), 1.33f, 0.01f, 10000.f);
39         glUniformMatrix4fv(projId, 1, GL_FALSE, glm::value_ptr(proj));
40
41         GLuint viewId = glGetUniformLocation(progId, "view");
42         glm::mat4 view = glm::lookAt(camPos, camPos + camFront, camUp);
43         glUniformMatrix4fv(viewId, 1, GL_FALSE, glm::value_ptr(view));
44
45         GLuint lightPosLoc = glGetUniformLocation(progId, "lightPos");
46         glm::vec3 lightPos = glm::vec3(sin(d) * 10, 0, 0);//10 * cos(d));
47         glUniform3fv(lightPosLoc, 1, glm::value_ptr(lightPos));
48         
49         GLuint viewPosLoc = glGetUniformLocation(progId, "viewPos");
50         glUniform3fv(viewPosLoc, 1, glm::value_ptr(camPos));
51
52         GLuint modelId = glGetUniformLocation(progId, "model");
53
54         for (int i = 0; i < 10; i++) {
55
56                 glm::mat4 model = glm::mat4(1.f);
57
58                 model = glm::translate(model, glm::vec3(sin(i * 30) * 10, 0, i * 2 - 10));
59                 
60                 if (doRotate) {
61                         model = glm::rotate(model, d * glm::radians(30.f), glm::vec3(0.f, 1.f, 0.f));
62                         model = glm::rotate(model, d * glm::radians(20.f), glm::vec3(1.f, 0.f, 0.f));
63                 }
64                 
65                 if (doScale)
66                         model = glm::scale(model, glm::vec3(1.f, 0.7f + 0.7f * (1 + sin(d + (i + 3))), 1.f));
67
68                 if (doTranslate)
69                         model = glm::translate(model, glm::vec3(sin(d + (i + 1)), cos(d + (i + -3)), sin(d + (i + 4))));
70         
71
72                 glUniformMatrix4fv(modelId, 1, GL_FALSE, glm::value_ptr(model));
73                 
74                 glDrawArrays(GL_TRIANGLES, 0, 12);
75         }
76         glutSwapBuffers();
77 }
78
79 void attachShader(GLuint progId, const char* filePath, GLenum type) {
80         GLuint shader = glCreateShader(type);
81
82         if (!shader) {
83                 fprintf(stderr, "error creating shader\n");
84                 exit(1);
85         }
86
87         ifstream file(filePath);
88         stringstream buffer;
89         buffer << file.rdbuf();
90         string str = buffer.str();
91         const char* contents = str.c_str();
92
93         glShaderSource(shader, 1, (const GLchar**)&contents, NULL);
94         glCompileShader(shader);
95         GLint success;
96         glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
97         if (!success) {
98                 GLchar log[1024];
99                 glGetShaderInfoLog(shader, 1024, NULL, log);
100                 fprintf(stderr, "error: %s\n", log);
101                 exit(1);
102         }
103         glAttachShader(progId, shader);
104 }
105
106 GLuint compileShaders(char* vertexShader, char* fragmentShader) {
107         GLuint progId = glCreateProgram();
108
109         attachShader(progId, vertexShader, GL_VERTEX_SHADER);
110         attachShader(progId, fragmentShader, GL_FRAGMENT_SHADER);
111
112         glLinkProgram(progId);
113         GLint success = 0;
114         glGetProgramiv(progId, GL_LINK_STATUS, &success);
115         if (!success) {
116                 GLchar log[1024];
117                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
118                 fprintf(stderr, "error linking: %s\n", log);
119                 exit(1);
120         }
121
122         return progId;
123 }
124
125 #define BUFFER_OFFSET(i) ((char *)NULL + (i))
126
127 GLuint setupBuffers(glm::vec3* vertices, glm::vec3* normals, GLuint progId) {
128
129         GLfloat colors[] = {
130                 0, 1, 0, 1,
131                 1, 0, 0, 1,
132                 0, 0, 1, 1,
133
134                 0, 1, 0, 1,
135                 1, 0, 0, 1,
136                 0, 0, 1, 1,
137
138                 0, 1, 0, 1,
139                 1, 0, 0, 1,
140                 0, 0, 1, 1,
141
142                 0, 1, 0, 1,
143                 1, 0, 0, 1,
144                 0, 0, 1, 1
145         };
146
147         GLuint numVerts = 12;
148
149         GLuint vbo;
150         glGenBuffers(1, &vbo);
151
152         GLuint vao;
153         glGenVertexArrays(1, &vao);
154
155         GLuint posId = glGetAttribLocation(progId, "vPosition");
156         GLuint colorId = glGetAttribLocation(progId, "vColor");
157         GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
158
159         GLuint vertsLen = numVerts * 3 * sizeof(GLfloat);
160         GLuint colorsLen = numVerts * 4 * sizeof(GLfloat);
161         GLuint normalLen = numVerts * 3 * sizeof(GLfloat);
162
163         glBindBuffer(GL_ARRAY_BUFFER, vbo);
164         glBufferData(GL_ARRAY_BUFFER, vertsLen + colorsLen + normalLen, NULL, GL_STATIC_DRAW);
165
166         glBufferSubData(GL_ARRAY_BUFFER, 0, vertsLen, glm::value_ptr(vertices[0]));
167         glBufferSubData(GL_ARRAY_BUFFER, vertsLen, colorsLen, colors);
168         glBufferSubData(GL_ARRAY_BUFFER, vertsLen + colorsLen, normalLen, glm::value_ptr(normals[0]));
169         
170         glBindVertexArray(vao);
171
172         glEnableVertexAttribArray(posId);
173         glVertexAttribPointer(posId, 3, GL_FLOAT, GL_FALSE, 0, 0);
174
175         glEnableVertexAttribArray(colorId);
176         glVertexAttribPointer(colorId, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen));
177
178         glEnableVertexAttribArray(normalLoc);
179         glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertsLen + colorsLen));
180
181         return vao;
182 }
183
184 void validateProgram(GLuint progId) {
185         glValidateProgram(progId);
186         
187         GLint success;
188         glGetProgramiv(progId, GL_VALIDATE_STATUS, &success);
189         if (!success) {
190                 GLchar log[1024];
191                 glGetProgramInfoLog(progId, sizeof(log), NULL, log);
192                 fprintf(stderr, "error: %s\n", log);
193                 exit(1);
194         }
195 }
196
197 void init() {
198         glm::vec3 vertices[12] = {
199                 glm::vec3(0.0f, 1.0f, 0.0f),
200                 glm::vec3(1.0f, -1.0f, -1.0f),
201                 glm::vec3(-1.0f, -1.0f, -1.0f),
202
203                 glm::vec3(0.0f, 1.0f, 0.0f),
204                 glm::vec3(-1.0f, -1.0f, 1.0f),
205                 glm::vec3(1.0f, -1.0f, 1.0f),
206
207                 glm::vec3(0.0f, 1.0f, 0.0f),
208                 glm::vec3(-1.0f, -1.0f, -1.0f),
209                 glm::vec3(-1.0f, -1.0f, 1.0f),
210
211                 glm::vec3(0.0f, 1.0f, 0.0f),
212                 glm::vec3(1.0f, -1.0f, 1.0f),
213                 glm::vec3(1.0f, -1.0f, -1.0f)
214         };
215
216         // work out the normals
217         glm::vec3 normals[12];
218         for (int i = 0; i < 4; i++) {
219                 glm::vec3 a = vertices[i * 3];
220                 glm::vec3 b = vertices[i * 3 + 1];
221                 glm::vec3 c = vertices[i * 3 + 2];
222                 glm::vec3 u = glm::normalize(a - c);
223                 glm::vec3 v = glm::normalize(b - c);
224                 glm::vec3 norm = glm::normalize(glm::cross(v, u));
225                 for(int j = 0; j < 3; j++) {
226                         normals[i * 3 + j] = glm::vec3(norm);
227                 }
228         }
229
230         vaos = new GLuint[2];
231
232         progId = compileShaders((char*)"vertex.glsl", (char*)"fragment.glsl");
233         glUseProgram(progId);
234         vaos[0] = setupBuffers(vertices, normals, progId);
235         validateProgram(progId);
236
237         glEnable(GL_DEPTH_TEST); 
238         glEnable(GL_CULL_FACE); 
239 }
240
241 bool* keyStates = new bool[256];
242
243 void keyboard(unsigned char key, int x, int y) {
244         keyStates[key] = true;
245         if (key == 'z')
246                 doScale = !doScale;
247         if (key == 'x')
248                 doRotate = !doRotate;
249         if (key == 'c')
250                 doTranslate = !doTranslate;
251 }
252
253 void keyboardUp(unsigned char key, int x, int y) {
254         keyStates[key] = false;
255 }
256
257 void timer(int _) {
258         float xSpeed = 0.f, ySpeed = 0.f, zSpeed = 0.f;
259         if (keyStates['w'])
260                 zSpeed = 0.1f;
261         if (keyStates['s'])
262                 zSpeed = -0.1f;
263         if (keyStates['a'])
264                 xSpeed = 0.1f;
265         if (keyStates['d'])
266                 xSpeed = -0.1f;
267         if (keyStates['q'])
268                 ySpeed = 0.1f;
269         if (keyStates['e'])
270                 ySpeed = -0.1f;
271
272         camPos.x += xSpeed * sin(yaw) + zSpeed * cos(yaw);
273         camPos.y += ySpeed;
274         camPos.z += zSpeed * sin(yaw) - xSpeed * cos(yaw);
275         glutPostRedisplay();
276         glutTimerFunc(16, timer, 0);
277 }
278
279 int prevMouseX, prevMouseY;
280 bool firstMouse = true;
281
282 void motion(int x, int y) {
283         if (firstMouse) {
284                 prevMouseX = x;
285                 prevMouseY = y;
286                 firstMouse = false;
287         }
288         int dx = x - prevMouseX, dy = y - prevMouseY;
289
290         prevMouseX = x;
291         prevMouseY = y;
292
293         const float sensitivity = 0.005f;
294         yaw += dx * sensitivity;
295         pitch -= dy * sensitivity;
296
297         glm::vec3 front;
298         front.x = cos(pitch) * cos(yaw);
299         front.y = sin(pitch);
300         front.z = cos(pitch) * sin(yaw);
301         camFront = glm::normalize(front);
302 }
303
304 void mouse(int button, int state, int x, int y) {
305         if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
306                 firstMouse = true;
307 }
308
309 int main(int argc, char** argv) {
310         glutInit(&argc, argv);
311         glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGB|GLUT_3_2_CORE_PROFILE);
312         glutInitWindowSize(800, 600);
313         int win = glutCreateWindow("Hello Triangle");
314         glutDisplayFunc(display);
315
316         glewInit();
317         
318         init();
319
320         glutKeyboardFunc(keyboard);
321         glutKeyboardUpFunc(keyboardUp);
322         glutTimerFunc(16, timer, 0);
323         glutMotionFunc(motion);
324         glutMouseFunc(mouse);
325
326         glutMainLoop();
327
328         return 0;
329 }
330