Add glTF PBR model loading
[opengl.git] / main.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <iostream>
4 #include <array>
5 #include <vector>
6 #ifdef __APPLE__
7 #include <GL/glew.h>
8 #include "cocoa.h"
9 #else
10 #include <OpenGL/glew.h>
11 #endif
12 #include <GLUT/glut.h>
13 #include "shapes.hpp"
14 #include <glm/glm.hpp>
15 #include <glm/ext.hpp>
16 #include <glm/gtc/type_ptr.hpp>
17 #include "model.hpp"
18 #include "program.hpp"
19 #include "skybox.hpp"
20 #include "image.hpp"
21
22 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
23
24 using namespace std;
25
26 GLuint lightVao;
27
28 Program *textureProg, *plainProg, *reflectProg, *pbrProg;
29 Skybox *skybox;
30 Model *chest, *mirrorCube, *pbr;
31 GLuint albedoMap, metallicMap, roughnessMap, normalMap, aoMap;
32                           
33 glm::vec3 camPos   = glm::vec3(0.0f, 0.0f,  -5.f);
34 glm::vec3 camFront = glm::vec3(0.0f, 0.0f, 1.0f);
35 glm::vec3 camUp    = glm::vec3(0.0f, 1.0f,  0.0f);
36 float yaw = 1.57, pitch = 0;
37 bool doScale, doRotate, doTranslate;
38
39 struct Light {
40         glm::vec3 pos;
41         glm::vec3 color;
42 };
43
44 std::vector<Light> lights = {
45         { glm::vec3(0, 0, 3), glm::vec3(1) },
46         { glm::vec3(0, 3, 0), glm::vec3(1) },
47         { glm::vec3(3, 0, 0), glm::vec3(1) },
48         { glm::vec3(3, 0, 0), glm::vec3(1) }
49 };
50
51 int activeLight = 0;
52
53 const int WIDTH = 800, HEIGHT = 600;
54 const float ASPECT = (float)WIDTH / (float)HEIGHT;
55
56 glm::mat4 projMat() {
57         return glm::perspective(glm::radians(45.f), ASPECT, 0.01f, 10000.f);
58 }
59
60 glm::mat4 viewMat() {
61         return glm::lookAt(camPos, camPos + camFront, camUp);
62 }
63
64 void setProjectionAndViewUniforms(GLuint progId) {
65         GLuint projLoc = glGetUniformLocation(progId, "projection");
66         glm::mat4 proj = projMat();
67         glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
68
69         GLuint viewLoc = glGetUniformLocation(progId, "view");
70         glm::mat4 view = viewMat();
71         glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
72
73         GLuint camPosLoc = glGetUniformLocation(progId, "camPos");
74         glUniform3fv(camPosLoc, 1, glm::value_ptr(camPos));
75 }
76
77 void setLightColorAndPos(GLuint progId, glm::vec3 lightPos, glm::vec4 lightColor) {
78         GLuint lightColorLoc = glGetUniformLocation(progId, "lightColor");
79         glUniform4fv(lightColorLoc, 1, glm::value_ptr(lightColor));
80
81         GLuint lightPosLoc = glGetUniformLocation(progId, "vLightPos");
82         glUniform3fv(lightPosLoc, 1, glm::value_ptr(lightPos));
83
84         GLuint viewPosLoc = glGetUniformLocation(progId, "vViewPos");
85         glUniform3fv(viewPosLoc, 1, glm::value_ptr(camPos));
86 }
87
88 void drawLight(Light &light) {
89         glUseProgram(plainProg->progId);
90         glBindVertexArray(lightVao);
91         setProjectionAndViewUniforms(plainProg->progId);
92         glm::mat4 model = glm::translate(glm::mat4(1.f), light.pos);
93         model = glm::scale(model, glm::vec3(0.2));
94         GLuint modelLoc = glGetUniformLocation(plainProg->progId, "model");
95         glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
96
97         GLuint colorLoc = glGetUniformLocation(plainProg->progId, "color");
98         glUniform4fv(colorLoc, 1, glm::value_ptr(light.color));
99                 
100         glDrawArrays(GL_TRIANGLES, 0, 36);
101 }
102
103
104 void display() {
105         glClearColor(0.5, 0.5, 0.5, 1);
106         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
107         float d = (float)glutGet(GLUT_ELAPSED_TIME) * 0.001f;
108
109         /* glm::vec4 lightColor(1, 1, 1, 1); */
110
111         /* drawLight(d, lightPos, lightColor); */
112
113         /* glUseProgram(textureProg->progId); */
114         /* setProjectionAndViewUniforms(textureProg->progId); */
115         /* setLightColorAndPos(textureProg->progId, lightPos, lightColor); */
116
117         /* Model::Node *top = chest->find("top"); */
118         /* top->model = glm::translate(glm::mat4(1), glm::vec3(0, 1, -1)); */
119         /* top->model = glm::rotate(top->model, sin(d / 2.5f) * 0.5f, glm::vec3(1, 0, 0)); */
120         /* top->model = glm::translate(top->model, glm::vec3(0, -1, 1)); */
121         
122         /* Model::Node *jewels = chest->find("jewels"); */
123         /* jewels->model = glm::scale(glm::mat4(1), glm::vec3((sin(d) + 1.2f) / 2.f)); */
124
125         /* Model::Node *lock = chest->find("lock"); */
126         /* lock->model = glm::translate(glm::mat4(1), glm::vec3(0.22610, 3.36478, -0.75649)); */
127         /* lock->model = glm::rotate(lock->model, (d / 2.5f), glm::vec3(0, 1, 0.4)); */
128         /* lock->model = glm::translate(lock->model, -glm::vec3(0.22610, 3.36478, -0.75649)); */
129
130         /* Model::Node *key = chest->find("key"); */
131         /* key->model = glm::translate(glm::mat4(1), glm::vec3(0, 0, sin(d))); */
132         
133         /* chest->draw(); */
134
135         /* mirrorCube->draw(); */
136
137         glUseProgram(pbrProg->progId);
138         setProjectionAndViewUniforms(pbrProg->progId);
139
140         glm::vec3 lightPositions[4], lightColors[4];
141         for (int i = 0; i < 4; i++) {
142                 lightPositions[i] = lights[i].pos;
143                 lightColors[i] = lights[i].color;
144         }
145         
146         glUniform3fv(glGetUniformLocation(pbrProg->progId, "lightPositions"), 4, glm::value_ptr(lightPositions[0]));
147         glUniform3fv(glGetUniformLocation(pbrProg->progId, "lightColors"), 4, glm::value_ptr(lightColors[0]));
148
149         /* glUniform1i(glGetUniformLocation(pbrProg->progId, "albedoMap"), 0); */
150         /* glActiveTexture(GL_TEXTURE0); */
151         /* glBindTexture(GL_TEXTURE_2D, albedoMap); */
152
153         /* glUniform1i(glGetUniformLocation(pbrProg->progId, "normalMap"), 1); */
154         /* glActiveTexture(GL_TEXTURE1); */
155         /* glBindTexture(GL_TEXTURE_2D, normalMap); */
156
157         /* glUniform1i(glGetUniformLocation(pbrProg->progId, "metallicMap"), 2); */
158         /* glActiveTexture(GL_TEXTURE2); */
159         /* glBindTexture(GL_TEXTURE_2D, metallicMap); */
160
161         /* glUniform1i(glGetUniformLocation(pbrProg->progId, "roughnessMap"), 3); */
162         /* glActiveTexture(GL_TEXTURE3); */
163         /* glBindTexture(GL_TEXTURE_2D, roughnessMap); */
164         
165         /* glUniform1i(glGetUniformLocation(pbrProg->progId, "aoMap"), 4); */
166         /* glActiveTexture(GL_TEXTURE4); */
167         /* glBindTexture(GL_TEXTURE_2D, aoMap); */
168
169         glUniform1i(glGetUniformLocation(pbrProg->progId, "irradianceMap"), 5);
170         glActiveTexture(GL_TEXTURE5);
171         glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->getIrradianceMap());
172
173         glUniform1i(glGetUniformLocation(pbrProg->progId, "prefilterMap"), 6);
174         glActiveTexture(GL_TEXTURE6);
175         glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->getPrefilterMap());
176
177         glUniform1i(glGetUniformLocation(pbrProg->progId, "brdfMap"), 7);
178         glActiveTexture(GL_TEXTURE7);
179         glBindTexture(GL_TEXTURE_2D, skybox->getBRDFMap());
180
181         pbr->draw();
182
183         for (Light &light: lights) drawLight(light);
184
185         skybox->draw(projMat(), viewMat());
186
187         glutSwapBuffers();
188 }
189
190 void setupLightBuffers(GLuint progId) {
191         auto vertices = cube();
192         GLuint verticesSize = 36 * 3 * sizeof(GLfloat);
193
194         glGenVertexArrays(1, &lightVao);
195         GLuint vbo;
196         glBindVertexArray(lightVao);
197         glGenBuffers(1, &vbo);
198         glBindBuffer(GL_ARRAY_BUFFER, vbo);
199         glBufferData(GL_ARRAY_BUFFER, verticesSize, NULL, GL_STATIC_DRAW);
200         glBufferSubData(GL_ARRAY_BUFFER, 0, verticesSize, glm::value_ptr(vertices[0]));
201         GLuint posLoc = glGetAttribLocation(progId, "vPosition");
202         glEnableVertexAttribArray(posLoc);
203         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
204 }
205
206 GLuint loadTexture(const std::string &path) {
207         Image img(path);
208         GLuint texId;
209         glGenTextures(1, &texId);
210         glBindTexture(GL_TEXTURE_2D, texId);
211         glTexImage2D(GL_TEXTURE_2D, 0, img.format(), img.width(), img.height(), 0, img.format(), GL_UNSIGNED_BYTE, img.data());
212         glGenerateMipmap(GL_TEXTURE_2D);
213         return texId;
214 }
215
216 void init() {
217         plainProg = new Program("plainvertex.glsl", "plainfrag.glsl");
218         glUseProgram(plainProg->progId);
219         setupLightBuffers(plainProg->progId);
220         plainProg->validate();
221
222         skybox = new Skybox(Image("models/loftSkybox/Newport_Loft_Ref.hdr"));
223
224         /* textureProg = new Program("texturevertex.glsl", "texturefrag.glsl"); */
225         /* chest = new Model("models/chest.dae", *textureProg, *skybox); */
226         /* mirrorCube = new Model("models/mirrorCube.dae", *textureProg, *skybox); */
227
228         pbrProg = new Program("pbrvert.glsl", "pbrfrag.glsl");
229         glUseProgram(pbrProg->progId);
230         pbr = new Model("models/sphereMetal.gltf", *pbrProg, *skybox);
231
232         /* albedoMap = loadTexture("models/materials/streakedmetal/streakedmetal-albedo.png"); */
233         /* metallicMap = loadTexture("models/materials/streakedmetal/streakedmetal-metalness.png"); */
234         /* normalMap = loadTexture("models/materials/streakedmetal/streakedmetal-normal.png"); */
235         /* roughnessMap = loadTexture("models/materials/streakedmetal/streakedmetal-roughness.png"); */
236         /* aoMap = loadTexture("models/materials/streakedmetal/streakedmetal-ao.png"); */
237
238         glEnable(GL_DEPTH_TEST); 
239         glEnable(GL_CULL_FACE); 
240         // prevent edge artifacts in specular cubemaps
241         glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
242 }
243
244 bool* keyStates = new bool[256];
245
246 void keyboard(unsigned char key, int x, int y) {
247         keyStates[key] = true;
248         if (key == 'z')
249                 doScale = !doScale;
250         if (key == 'x')
251                 doRotate = !doRotate;
252         if (key == 'c')
253                 doTranslate = !doTranslate;
254 }
255
256 void keyboardUp(unsigned char key, int x, int y) {
257         keyStates[key] = false;
258 }
259
260 void timer(int _) {
261         float xSpeed = 0.f, ySpeed = 0.f, zSpeed = 0.f;
262         if (keyStates['w'])
263                 zSpeed = 0.1f;
264         if (keyStates['s'])
265                 zSpeed = -0.1f;
266         if (keyStates['a'])
267                 xSpeed = 0.1f;
268         if (keyStates['d'])
269                 xSpeed = -0.1f;
270         if (keyStates['q'])
271                 ySpeed = 0.1f;
272         if (keyStates['e'])
273                 ySpeed = -0.1f;
274
275         if (keyStates['j']) lights[activeLight].pos.z += 0.1f;
276         if (keyStates['k']) lights[activeLight].pos.z -= 0.1f;
277         if (keyStates['h']) lights[activeLight].pos.x -= 0.1f;
278         if (keyStates['l']) lights[activeLight].pos.x += 0.1f;
279         if (keyStates['m']) lights[activeLight].pos.y -= 0.1f;
280         if (keyStates['n']) lights[activeLight].pos.y += 0.1f;
281
282         camPos.x += xSpeed * sin(yaw) + zSpeed * cos(yaw);
283         camPos.y += ySpeed;
284         camPos.z += zSpeed * sin(yaw) - xSpeed * cos(yaw);
285         glutPostRedisplay();
286         glutTimerFunc(16, timer, 0);
287 }
288
289 int prevMouseX, prevMouseY;
290 bool firstMouse = true;
291
292 void motion(int x, int y) {
293         if (firstMouse) {
294                 prevMouseX = x;
295                 prevMouseY = y;
296                 firstMouse = false;
297         }
298         int dx = x - prevMouseX, dy = y - prevMouseY;
299
300         prevMouseX = x;
301         prevMouseY = y;
302
303         const float sensitivity = 0.005f;
304         yaw += dx * sensitivity;
305         pitch -= dy * sensitivity;
306
307         glm::vec3 front;
308         front.x = cos(pitch) * cos(yaw);
309         front.y = sin(pitch);
310         front.z = cos(pitch) * sin(yaw);
311         camFront = glm::normalize(front);
312
313         if (pitch < -1.57079632679 || pitch >= 1.57079632679) {
314                 camUp = glm::vec3(0, -1, 0);
315         } else {
316                 camUp = glm::vec3(0, 1, 0);
317         }
318 }
319
320 void mouse(int button, int state, int x, int y) {
321         if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
322                 firstMouse = true;
323 }
324
325 int main(int argc, char** argv) {
326         glutInit(&argc, argv);
327         glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGB|GLUT_3_2_CORE_PROFILE);
328         glutInitWindowSize(WIDTH, HEIGHT);
329         int win = glutCreateWindow("Physically Based Rendering");
330         glutDisplayFunc(display);
331
332         glewInit();
333         
334         init();
335         
336         swizzle();
337
338         glutKeyboardFunc(keyboard);
339         glutKeyboardUpFunc(keyboardUp);
340         glutTimerFunc(16, timer, 0);
341         glutMotionFunc(motion);
342         glutMouseFunc(mouse);
343
344         glutMainLoop();
345
346         return 0;
347 }
348