+ Delegate cwDelegate;
+ ControlWindow controlWindow;
+
+ BlendshapeMode(std::string directory) {
+ loadBlendshapes(directory, *pbrProg, &bsModel);
+ targetModel = bsModel.model;
+
+ size_t numBlends = bsModel.blendshapes.size();
+ std::vector<std::string> names(numBlends);
+ for (int i = 0; i < numBlends; i++) names[i] = bsModel.blendshapes[i].name;
+
+ controlWindow = createControlWindow(names, &cwDelegate);
+
+ camPos = { 0, 18, 81 };
+ camFront = { 0, 0, -1 };
+ camUp = { 0, 1, 0 };
+ zfar = 10000;
+ znear = 0.1f;
+ }
+
+ void drawCursor(glm::vec3 pos, glm::vec3 color, float scale = 1) {
+ glUseProgram(cursorProg->progId);
+ glBindVertexArray(cursorVao);
+ setProjectionAndViewUniforms(cursorProg->progId);
+ glm::mat4 model = glm::scale(glm::translate(glm::mat4(1), pos), glm::vec3(scale * 0.4));
+ GLuint modelLoc = glGetUniformLocation(cursorProg->progId, "model");
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
+
+ GLuint colorLoc = glGetUniformLocation(cursorProg->progId, "color");
+ glUniform4fv(colorLoc, 1, glm::value_ptr(color));
+
+ glDrawElements(GL_TRIANGLES, cursorNumIndices, GL_UNSIGNED_INT, 0);
+ }
+
+ void display(float d) override {
+ if (closestVertex.distance < closestVertexThreshold)
+ drawCursor(closestVertex.pos, {0.5,0,1}, 0.9);
+
+ for (auto v: manipulators) {
+ glm::vec3 color = { 0.4, 1, 0 };
+ if (closestVertex.meshIdx == v.first.first &&
+ closestVertex.vertIdx == v.first.second)
+ color = {1, 0, 0};
+ drawCursor(v.second, color);
+
+ glm::vec3 origVertex = aiVector3DToVec3(bsModel.model->meshes[v.first.first].ai.mVertices[v.first.second]);
+ drawCursor(origVertex, {0,0,1}, 0.7);
+ }
+
+ bsModel.model->draw(skyboxes[activeSkybox], d * 1000);
+ }
+
+ void timer() override {
+ float xSpeed = 0, ySpeed = 0, zSpeed = 0;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wchar-subscripts"
+ if (keyStates['w'])
+ zSpeed = 0.1f;
+ if (keyStates['s'])
+ zSpeed = -0.1f;
+ if (keyStates['a'])
+ xSpeed = 0.1f;
+ if (keyStates['d'])
+ xSpeed = -0.1f;
+ if (keyStates['q'])
+ ySpeed = 0.1f;
+ if (keyStates['e'])
+ ySpeed = -0.1f;
+#pragma clang diagnostic pop
+
+ if (playBlendshapeAnim) {
+ stepBlendshapeAnim(&bsModel);
+ needToInterpolateBlendshapes = true;
+ std::vector<float> newWeights(bsModel.blendshapes.size());
+ for (int i = 0; i < bsModel.blendshapes.size(); i++)
+ newWeights[i] = bsModel.blendshapes[i].weight;
+ updateWeights(&controlWindow, newWeights);
+ }
+
+ if (curManipulator.first != -1 && curManipulator.second != -1) {
+ manipulators[curManipulator].x += xSpeed;
+ manipulators[curManipulator].y += ySpeed;
+ manipulators[curManipulator].z += zSpeed;
+ }
+
+ if (needToInterpolateBlendshapes) {
+ interpolateBlendshapes(&bsModel);
+ needToInterpolateBlendshapes = false;
+ }
+
+ if (needToCalculateClosestVertex) {
+ GLint vpArr[4]; glGetIntegerv(GL_VIEWPORT, vpArr);
+ glm::vec4 viewport(vpArr[0], vpArr[1], vpArr[2], vpArr[3]);
+ glm::vec3 selectedPos = glm::unProject(glm::vec3(mouseX * 2, viewport[3] - mouseY * 2, 1), // hidpi
+ viewMat(),
+ projMat(),
+ viewport);
+
+ closestVertex = targetModel->closestVertex(targetModel->getRoot(), camPos, selectedPos);
+ needToCalculateClosestVertex = false;
+ }
+ }
+
+
+ void motion(int x, int y, int dx, int dy) override {
+ if (closestVertex.distance > closestVertexThreshold) {
+ const glm::vec3 origin(0,18,0);
+ const float sensitivity = 0.003f;
+ auto camMat = glm::translate(glm::mat4(1), origin + camPos);
+ auto rotation = glm::rotate(glm::rotate(glm::mat4(1), -dx * sensitivity, {0, 1, 0}),
+ -dy * sensitivity, {1, 0, 0});
+ auto rotAroundOrig = camMat * rotation * glm::translate(glm::mat4(1), origin - camPos);
+ camPos = rotAroundOrig * glm::vec4(camPos, 0);
+ camFront = origin - camPos; // face center
+ }
+ needToCalculateClosestVertex = true;
+ }
+
+ void passiveMotion(int x, int y, int dx, int dy) override {
+ needToCalculateClosestVertex = true;