d1a35534b5ae67b3a25c78fba5d1025b3cb3cbe6
[opengl.git] / main.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <iostream>
4 #include <array>
5 #include <vector>
6 #include <dirent.h>
7 #ifdef __APPLE__
8 #include <GL/glew.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 <assimp/Importer.hpp>
18 #include <assimp/scene.h>
19 #include <assimp/postprocess.h>
20 #include "model.hpp"
21 #include "program.hpp"
22 #include "skybox.hpp"
23 #include "image.hpp"
24 #include "util.hpp"
25 #include "ik.hpp"
26 #include "blendshapes.hpp"
27 #include "ui.hpp"
28
29 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
30
31 using namespace std;
32
33 GLuint lightVao;
34
35 Program *textureProg, *plainProg, *reflectProg, *pbrProg;
36
37 ControlWindow controlWindow;
38
39 std::vector<Skybox> skyboxes;
40 int activeSkybox = 0;
41
42 Assimp::Importer importer; // Need to keep this around, otherwise stuff disappears!
43 Model *sceneModel;
44
45 glm::vec3 camPos = {0, 0, -5}, camFront = {0, 0, 1}, camUp = {0, 1, 0};
46 float fov = glm::radians(30.f), znear = 0.01f, zfar = 10000.f;
47 float yaw = 1.57, pitch = 0;
48
49 Model *targetModel; // The model that the selection is happening on
50 Model::VertexLookup closestVertex;
51
52 std::map<VertIdx, glm::vec3> manipulators;
53 VertIdx curManipulator = {-1,-1};
54
55 BlendshapeModel bsModel;
56 bool playBlendshapeAnim = false;
57
58 struct Light {
59         glm::mat4 trans;
60         glm::vec3 color;
61 };
62
63 std::vector<Light> lights;
64
65 bool discoLights = false;
66
67 int windowWidth = 800, windowHeight = 600;
68
69 enum Mode {
70         Default,
71         Blendshapes
72 };
73 Mode curMode;
74
75 float aspect() {
76         return (float)windowWidth / (float)windowHeight;
77 }       
78
79 inline glm::mat4 projMat() {
80         return glm::perspective(fov, aspect(), znear, zfar);
81 }
82
83 inline glm::mat4 viewMat() {
84         return glm::lookAt(camPos, camPos + camFront, camUp);
85 }
86
87 void setProjectionAndViewUniforms(GLuint progId) {
88         GLuint projLoc = glGetUniformLocation(progId, "projection");
89         glm::mat4 proj = projMat();
90         glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
91
92         GLuint viewLoc = glGetUniformLocation(progId, "view");
93         glm::mat4 view = viewMat();
94         glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
95
96         GLuint camPosLoc = glGetUniformLocation(progId, "camPos");
97         glUniform3fv(camPosLoc, 1, glm::value_ptr(camPos));
98 }
99
100 void setLightColorAndPos(GLuint progId, glm::vec3 lightPos, glm::vec4 lightColor) {
101         GLuint lightColorLoc = glGetUniformLocation(progId, "lightColor");
102         glUniform4fv(lightColorLoc, 1, glm::value_ptr(lightColor));
103
104         GLuint lightPosLoc = glGetUniformLocation(progId, "vLightPos");
105         glUniform3fv(lightPosLoc, 1, glm::value_ptr(lightPos));
106
107         GLuint viewPosLoc = glGetUniformLocation(progId, "vViewPos");
108         glUniform3fv(viewPosLoc, 1, glm::value_ptr(camPos));
109 }
110
111 void drawBox(glm::mat4 trans, glm::vec3 color) {
112         glUseProgram(plainProg->progId);
113         glBindVertexArray(lightVao);
114         setProjectionAndViewUniforms(plainProg->progId);
115         glm::mat4 model = glm::scale(trans, glm::vec3(0.3));
116         GLuint modelLoc = glGetUniformLocation(plainProg->progId, "model");
117         glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
118
119         GLuint colorLoc = glGetUniformLocation(plainProg->progId, "color");
120         glUniform4fv(colorLoc, 1, glm::value_ptr(color));
121                 
122         glDrawArrays(GL_TRIANGLES, 0, 36);
123 }
124
125 void drawLight(Light &light) {
126         drawBox(light.trans, light.color);
127 }
128
129 int findNodeTrans(const struct aiNode *n, const struct aiString name, glm::mat4 *dest) {
130         if (strcmp(n->mName.data, name.data) == 0) {
131                 *dest = aiMatrixToMat4(n->mTransformation);
132                 return 0;
133         }
134         for (int i = 0; i < n->mNumChildren; i++) {
135                 if (findNodeTrans(n->mChildren[i], name, dest) == 0) {
136                         glm::mat4 t = aiMatrixToMat4(n->mTransformation);
137                         *dest = t * *dest;
138                         return 0;
139                 }
140         }
141         return 1;
142 }
143
144 glm::mat4 worldSpaceToModelSpace(aiNode *node, glm::mat4 m) {
145         aiNode *parent = node;
146         glm::mat4 res = m;
147         std::vector<glm::mat4> trans;
148         while (parent != nullptr) {
149                 /* res = res * glm::inverse(aiMatrixToMat4(parent->mTransformation)); */
150                 trans.push_back(glm::inverse(aiMatrixToMat4(parent->mTransformation)));
151                 parent = parent->mParent;
152         }
153         while (!trans.empty()) { res = trans.back() * res; trans.pop_back(); }
154         return res;
155 }
156
157 void highlightVertex() {
158         drawBox(glm::translate(glm::mat4(1), closestVertex.pos), {1, 1, 0.5});
159 }
160
161 void display() {
162         glClearColor(0.5, 0.5, 0.5, 1);
163         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
164         glViewport(0, 0, windowWidth * 2, windowHeight * 2);
165
166         float d = (float)glutGet(GLUT_ELAPSED_TIME) * 0.001f;
167
168         glUseProgram(getUtilProg()->progId);
169         setProjectionAndViewUniforms(getUtilProg()->progId);
170
171         glUseProgram(pbrProg->progId);
172         setProjectionAndViewUniforms(pbrProg->progId);
173
174         size_t numLights = lights.size() + (discoLights ? 3 : 0);
175         glm::vec3 lightPositions[numLights], lightColors[numLights];
176         for (int i = 0; i < lights.size(); i++) {
177                 lightPositions[i] = glm::vec3(lights[i].trans[3]);
178                 lightColors[i] = lights[i].color;
179         }
180
181         if (discoLights) {
182                 for (int i = numLights - 3; i < numLights; i++) {
183                         auto m = glm::translate(glm::mat4(1.f), glm::vec3(-2.5, 0, 0));
184                         m = glm::rotate(m, glm::radians(d * 100 + i * 30), glm::vec3(1, 0, 0));
185                         m = glm::rotate(m, glm::radians(d * 100 + i * 30), glm::vec3(0, 1, 0));
186                         m = glm::rotate(m, glm::radians(d * 100 + i * 30), glm::vec3(0, 0, 1));
187                         lightPositions[i] = glm::vec3(m * glm::vec4(5, 0, 0, 1));
188                         lightColors[i] = glm::vec3(0.2);
189                         if (i % 3 == 0) lightColors[i].x = sin(d);
190                         if (i % 3 == 1) lightColors[i].y = cos(d * 3);
191                         if (i % 3 == 2) lightColors[i].z = cos(d);
192                 }
193         }
194
195         glUniform1ui(glGetUniformLocation(pbrProg->progId, "numLights"), numLights);
196         glUniform3fv(glGetUniformLocation(pbrProg->progId, "lightPositions"), numLights, glm::value_ptr(lightPositions[0]));
197         glUniform3fv(glGetUniformLocation(pbrProg->progId, "lightColors"), numLights, glm::value_ptr(lightColors[0]));
198
199 #ifdef COWEDBOY_IK
200         { 
201                 glm::vec3 targetPos(sin(d) * 2 + 3, -2, 1);
202                 Light targetLight = { glm::translate(glm::mat4(1), targetPos), {0.5, 1, 1}  };
203                 drawLight(targetLight);
204                 inverseKinematics(*sceneModel->find("Shoulder.L"), *sceneModel->find("Finger.L"), targetPos);
205
206                 targetPos = { sin(d * 2) * 2 - 5, 2.5, 0 };
207                 targetLight = { glm::translate(glm::mat4(1), targetPos), {1, 1, 0.5}  };
208                 drawLight(targetLight);
209                 inverseKinematics(*sceneModel->find("Shoulder.R"), *sceneModel->find("Finger.R"), targetPos);
210         }
211 #endif
212
213         if (curMode == Default)
214                 sceneModel->draw(skyboxes[activeSkybox], d * 1000);
215
216         if (curMode == Blendshapes) {
217                 highlightVertex();
218
219                 for (auto v: manipulators) {
220                         glm::vec3 color = { 0.4, 1, 0 };
221                         if (closestVertex.meshIdx == v.first.first &&
222                                 closestVertex.vertIdx == v.first.second)
223                                 color = {1, 0, 0};
224                         drawBox(glm::translate(glm::mat4(1), v.second), color);
225
226                         glm::vec3 origVertex = aiVector3DToVec3(bsModel.model->meshes[v.first.first].ai.mVertices[v.first.second]);
227                         drawBox(glm::translate(glm::mat4(1), origVertex), {0,0,1});
228                 }
229
230                 bsModel.model->draw(skyboxes[activeSkybox], d * 1000);
231         }
232
233         for (Light &light: lights) drawLight(light);
234
235         // TODO: restore
236         /* if (discoLights) { */
237         /*      for (int i = numLights - 3; i < numLights; i++) { */
238         /*              Light l = { lightPositions[i], lightColors[i] }; */
239         /*              drawLight(l); */
240         /*      } */
241         /* } */
242
243         skyboxes[activeSkybox].draw(projMat(), viewMat());
244
245         glutSwapBuffers();
246 }
247
248 void setupLightBuffers(GLuint progId) {
249         auto vertices = cube();
250         GLuint verticesSize = 36 * 3 * sizeof(GLfloat);
251
252         glGenVertexArrays(1, &lightVao);
253         GLuint vbo;
254         glBindVertexArray(lightVao);
255         glGenBuffers(1, &vbo);
256         glBindBuffer(GL_ARRAY_BUFFER, vbo);
257         glBufferData(GL_ARRAY_BUFFER, verticesSize, NULL, GL_STATIC_DRAW);
258         glBufferSubData(GL_ARRAY_BUFFER, 0, verticesSize, glm::value_ptr(vertices[0]));
259         GLuint posLoc = glGetAttribLocation(progId, "vPosition");
260         glEnableVertexAttribArray(posLoc);
261         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
262 }
263
264 bool needToCalculateClosestVertex = false;
265 bool needToInterpolateBlendshapes = false;
266
267 class Delegate : public ControlWindowDelegate {
268         public:
269
270         virtual void weightChanged(int blendshape, float weight) {
271                 bsModel.blendshapes[blendshape].weight = weight;
272                 needToInterpolateBlendshapes = true;
273         }
274
275         virtual void solveWeights(std::vector<float> &newWeights) {
276                 ::solveWeights(&bsModel, manipulators);
277                 for (int i = 0; i < newWeights.size(); i++)
278                         newWeights[i] = bsModel.blendshapes[i].weight;
279                 needToInterpolateBlendshapes = true;
280         }
281
282         virtual void resetManipulators() {
283                 manipulators.clear();
284                 curManipulator = { -1, -1 };
285         }
286
287         virtual void playbackChanged(bool playing) {
288                 playBlendshapeAnim = playing;
289         }
290 };
291
292 Delegate cwDelegate;
293
294
295 void init() {
296         initUtilProg();
297
298         plainProg = new Program("plainvertex.glsl", "plainfrag.glsl");
299         glUseProgram(plainProg->progId);
300         setupLightBuffers(plainProg->progId);
301         plainProg->validate();
302
303         skyboxes.push_back(Skybox(Image("skyboxes/loft/Newport_Loft_Ref.hdr")));
304         skyboxes.push_back(Skybox(Image("skyboxes/wooden_lounge_8k.hdr")));
305         skyboxes.push_back(Skybox(Image("skyboxes/machine_shop_02_8k.hdr")));
306         skyboxes.push_back(Skybox(Image("skyboxes/pink_sunrise_8k.hdr")));
307
308         pbrProg = new Program("pbrvert.glsl", "pbrfrag.glsl");
309         glUseProgram(pbrProg->progId);
310
311         if (curMode == Default) {
312                 const std::string scenePath = "models/cowedboy.glb";
313                 const aiScene *scene = importer.ReadFile(
314                                 scenePath, aiProcess_Triangulate | aiProcess_CalcTangentSpace |
315                                 aiProcess_GenNormals | aiProcess_FlipUVs);
316                 if (!scene) {
317                         std::cerr << importer.GetErrorString() << std::endl;
318                         exit(1);
319                 }
320
321                 if (scene->mNumCameras > 0) {
322                         aiCamera *cam = scene->mCameras[0];
323                         glm::mat4 camTrans;
324                         if (findNodeTrans(scene->mRootNode, cam->mName, &camTrans) != 0)
325                                 abort(); // there must be a node with the same name as camera
326
327                         camPos = {camTrans[3][0], camTrans[3][1], camTrans[3][2]};
328
329                         glm::vec3 camLookAt =
330                                 glm::vec3(cam->mLookAt.x, cam->mLookAt.y, cam->mLookAt.z);
331                         camFront = camLookAt - camPos;
332
333                         camUp = glm::vec3(cam->mUp.x, cam->mUp.y, cam->mUp.z);
334
335                         fov = cam->mHorizontalFOV;
336                         // TODO: aspectRatio = cam->mAspect;
337                         znear = cam->mClipPlaneNear;
338                         zfar = cam->mClipPlaneFar;
339                 }
340
341                 for (int i = 0; i < scene->mNumLights; i++) {
342                         aiLight *light = scene->mLights[i];
343                         glm::mat4 trans;
344                         findNodeTrans(scene->mRootNode, light->mName, &trans);
345                         glm::vec3 col = {light->mColorAmbient.r, light->mColorAmbient.g,
346                                 light->mColorAmbient.b};
347                         Light l = {trans, col};
348                         lights.push_back(l);
349                 }
350
351                 sceneModel = new Model(scene, *pbrProg);
352         }
353
354         if (curMode == Blendshapes) {
355                 loadBlendshapes("models/high-res-blendshapes/", *pbrProg, &bsModel);
356                 targetModel = bsModel.model;
357
358                 size_t numBlends = bsModel.blendshapes.size();
359                 std::vector<std::string> names(numBlends);
360                 for (int i = 0; i < numBlends; i++) names[i] = bsModel.blendshapes[i].name;
361                 controlWindow = createControlWindow(names, &cwDelegate);
362
363                 camPos = { 0, 22, 81 };
364                 camFront = { 0, 0, -1 };
365                 camUp = { 0, 1, 0 };
366                 zfar = 10000;
367                 znear = 0.1f;
368         }
369
370         glEnable(GL_DEPTH_TEST); 
371         glEnable(GL_CULL_FACE); 
372         // prevent edge artifacts in specular cubemaps
373         glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
374
375         glViewport(0, 0, windowWidth * 2, windowHeight * 2);
376 }
377
378 bool keyStates[256] = {false};
379
380 void keyboard(unsigned char key, int x, int y) {
381         keyStates[key] = true;
382         if (key == 'z')
383                 activeSkybox = (activeSkybox + 1) % skyboxes.size();
384         if (key == 'c')
385                 discoLights = !discoLights;
386 }
387
388 void keyboardUp(unsigned char key, int x, int y) {
389         keyStates[key] = false;
390 }
391
392 int mouseX, mouseY;
393
394 /* #define ENABLE_MOVEMENT */
395 void timer(int _) {
396 #ifdef ENABLE_MOVEMENT
397         float xSpeed = 0.f, ySpeed = 0.f, zSpeed = 0.f;
398
399 #pragma clang diagnostic push
400 #pragma clang diagnostic ignored "-Wchar-subscripts"
401         if (keyStates['w'])
402                 zSpeed = 0.1f;
403         if (keyStates['s'])
404                 zSpeed = -0.1f;
405         if (keyStates['a'])
406                 xSpeed = 0.1f;
407         if (keyStates['d'])
408                 xSpeed = -0.1f;
409         if (keyStates['q'])
410                 ySpeed = 0.1f;
411         if (keyStates['e'])
412                 ySpeed = -0.1f;
413 #pragma clang diagnostic pop
414
415         camPos.x += xSpeed * sin(yaw) + zSpeed * cos(yaw);
416         camPos.y += ySpeed;
417         camPos.z += zSpeed * sin(yaw) - xSpeed * cos(yaw);
418 #endif
419
420         if (curMode == Blendshapes) {
421                 float xSpeed = 0, ySpeed = 0, zSpeed = 0;
422 #pragma clang diagnostic push
423 #pragma clang diagnostic ignored "-Wchar-subscripts"
424                 if (keyStates['w'])
425                         zSpeed = 0.1f;
426                 if (keyStates['s'])
427                         zSpeed = -0.1f;
428                 if (keyStates['a'])
429                         xSpeed = 0.1f;
430                 if (keyStates['d'])
431                         xSpeed = -0.1f;
432                 if (keyStates['q'])
433                         ySpeed = 0.1f;
434                 if (keyStates['e'])
435                         ySpeed = -0.1f;
436 #pragma clang diagnostic pop
437
438                 if (playBlendshapeAnim) {
439                         stepBlendshapeAnim(&bsModel);
440                         needToInterpolateBlendshapes = true;
441                         std::vector<float> newWeights(bsModel.blendshapes.size());
442                         for (int i = 0; i < bsModel.blendshapes.size(); i++)
443                                 newWeights[i] = bsModel.blendshapes[i].weight;
444                         updateWeights(&controlWindow, newWeights);
445                 }
446
447                 if (curManipulator.first != -1 && curManipulator.second != -1) {
448                         manipulators[curManipulator].x += xSpeed;
449                         manipulators[curManipulator].y += ySpeed;
450                         manipulators[curManipulator].z += zSpeed;
451                 }
452
453                 if (needToInterpolateBlendshapes) {
454                         interpolateBlendshapes(&bsModel);
455                         needToInterpolateBlendshapes = false;
456                 }
457
458                 if (needToCalculateClosestVertex) {
459                         GLint vpArr[4]; glGetIntegerv(GL_VIEWPORT, vpArr);
460                         glm::vec4 viewport(vpArr[0], vpArr[1], vpArr[2], vpArr[3]);
461                         glm::vec3 selectedPos = glm::unProject(glm::vec3(mouseX * 2, viewport[3] - mouseY * 2, 1), // hidpi
462                                         viewMat(),
463                                         projMat(),
464                                         viewport);
465
466                         closestVertex = targetModel->closestVertex(targetModel->getRoot(), camPos, selectedPos);
467                         needToCalculateClosestVertex = false;
468                 }
469         }
470
471         glutPostRedisplay();
472         glutTimerFunc(16, timer, 0);
473 }
474
475 int prevMouseX, prevMouseY;
476 bool firstMouse = true;
477
478 void motion(int x, int y) {
479 #ifdef ENABLE_MOVEMENT
480         if (firstMouse) {
481                 prevMouseX = x;
482                 prevMouseY = y;
483                 firstMouse = false;
484         }
485         int dx = x - prevMouseX, dy = y - prevMouseY;
486
487         prevMouseX = x;
488         prevMouseY = y;
489
490         const float sensitivity = 0.005f;
491         yaw += dx * sensitivity;
492         pitch -= dy * sensitivity;
493
494         glm::vec3 front;
495         front.x = cos(pitch) * cos(yaw);
496         front.y = sin(pitch);
497         front.z = cos(pitch) * sin(yaw);
498         camFront = glm::normalize(front);
499
500         if (pitch < -1.57079632679 || pitch >= 1.57079632679) {
501                 camUp = glm::vec3(0, -1, 0);
502         } else {
503                 camUp = glm::vec3(0, 1, 0);
504         }
505 #endif
506
507         mouseX = x; mouseY = y;
508         needToCalculateClosestVertex = true;
509
510 }
511
512 void mouse(int button, int state, int x, int y) {
513         if (isPanelFocused(controlWindow))
514                 return;
515         if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
516                 VertIdx idx = { closestVertex.meshIdx, closestVertex.vertIdx };
517                 if (manipulators.count(idx) <= 0)
518                         manipulators[idx] = closestVertex.pos;
519                 curManipulator = idx;
520         }
521
522 #ifdef ENABLE_MOVEMENT
523         if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
524                 firstMouse = true;
525 #endif
526 }
527
528 void reshape(int newWidth, int newHeight) {
529         windowWidth = newWidth, windowHeight = newHeight;
530 }
531
532 int main(int argc, char** argv) {
533         glutInit(&argc, argv);
534         glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGB|GLUT_3_2_CORE_PROFILE);
535         glutInitWindowSize(windowWidth, windowHeight);
536         glutCreateWindow("Physically Based Rendering");
537         glutDisplayFunc(display);
538         glutReshapeFunc(reshape);
539
540         glewInit();
541
542         // TODO: parse argv
543         curMode = Blendshapes;
544         init();
545
546         glutKeyboardFunc(keyboard);
547         glutKeyboardUpFunc(keyboardUp);
548         glutTimerFunc(16, timer, 0);
549         glutMotionFunc(motion);
550         glutPassiveMotionFunc(motion);
551         glutMouseFunc(mouse);
552
553         glutMainLoop();
554
555         return 0;
556 }
557