Circular cursor + dragging
[opengl.git] / main.cpp
index d1a35534b5ae67b3a25c78fba5d1025b3cb3cbe6..7054e7a04905a2a636da53ce115233687661c3c1 100644 (file)
--- a/main.cpp
+++ b/main.cpp
 
 using namespace std;
 
-GLuint lightVao;
+GLuint lightVao, cursorVao;
+GLuint cursorNumIndices;
 
-Program *textureProg, *plainProg, *reflectProg, *pbrProg;
+Program *textureProg, *plainProg, *reflectProg, *pbrProg, *cursorProg;
 
 ControlWindow controlWindow;
 
@@ -48,6 +49,8 @@ float yaw = 1.57, pitch = 0;
 
 Model *targetModel; // The model that the selection is happening on
 Model::VertexLookup closestVertex;
+// How close a vertex needs to be to the cursor before it is "over"
+const float closestVertexThreshold = 0.5;
 
 std::map<VertIdx, glm::vec3> manipulators;
 VertIdx curManipulator = {-1,-1};
@@ -108,22 +111,22 @@ void setLightColorAndPos(GLuint progId, glm::vec3 lightPos, glm::vec4 lightColor
        glUniform3fv(viewPosLoc, 1, glm::value_ptr(camPos));
 }
 
-void drawBox(glm::mat4 trans, glm::vec3 color) {
-       glUseProgram(plainProg->progId);
-       glBindVertexArray(lightVao);
-       setProjectionAndViewUniforms(plainProg->progId);
+void drawPlainProg(Program *p, GLuint vao, glm::mat4 trans, glm::vec3 color) {
+       glUseProgram(p->progId);
+       glBindVertexArray(vao);
+       setProjectionAndViewUniforms(p->progId);
        glm::mat4 model = glm::scale(trans, glm::vec3(0.3));
-       GLuint modelLoc = glGetUniformLocation(plainProg->progId, "model");
+       GLuint modelLoc = glGetUniformLocation(p->progId, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
 
-       GLuint colorLoc = glGetUniformLocation(plainProg->progId, "color");
+       GLuint colorLoc = glGetUniformLocation(p->progId, "color");
        glUniform4fv(colorLoc, 1, glm::value_ptr(color));
                
        glDrawArrays(GL_TRIANGLES, 0, 36);
 }
 
 void drawLight(Light &light) {
-       drawBox(light.trans, light.color);
+       drawPlainProg(plainProg, lightVao, light.trans, light.color);
 }
 
 int findNodeTrans(const struct aiNode *n, const struct aiString name, glm::mat4 *dest) {
@@ -154,8 +157,18 @@ glm::mat4 worldSpaceToModelSpace(aiNode *node, glm::mat4 m) {
        return res;
 }
 
-void highlightVertex() {
-       drawBox(glm::translate(glm::mat4(1), closestVertex.pos), {1, 1, 0.5});
+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() {
@@ -214,17 +227,18 @@ void display() {
                sceneModel->draw(skyboxes[activeSkybox], d * 1000);
 
        if (curMode == Blendshapes) {
-               highlightVertex();
+               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};
-                       drawBox(glm::translate(glm::mat4(1), v.second), color);
+                       drawCursor(v.second, color);
 
                        glm::vec3 origVertex = aiVector3DToVec3(bsModel.model->meshes[v.first.first].ai.mVertices[v.first.second]);
-                       drawBox(glm::translate(glm::mat4(1), origVertex), {0,0,1});
+                       drawCursor(origVertex, {0,0,1}, 0.7);
                }
 
                bsModel.model->draw(skyboxes[activeSkybox], d * 1000);
@@ -245,9 +259,8 @@ void display() {
        glutSwapBuffers();
 }
 
-void setupLightBuffers(GLuint progId) {
-       auto vertices = cube();
-       GLuint verticesSize = 36 * 3 * sizeof(GLfloat);
+void setupPlainBuffers(GLuint progId, std::vector<glm::vec3> vertices) {
+       GLuint verticesSize = vertices.size() * sizeof(glm::vec3);
 
        glGenVertexArrays(1, &lightVao);
        GLuint vbo;
@@ -261,6 +274,31 @@ void setupLightBuffers(GLuint progId) {
        glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
 }
 
+GLuint setupBuffersWithIndices(GLuint progId, GLuint vao,
+                                                        std::vector<glm::vec3> vertices,
+                                                        std::vector<GLuint> indices) {
+       GLuint vbos[2];
+       glBindVertexArray(vao);
+       glGenBuffers(2, vbos);
+
+       int verticesSize = vertices.size() * sizeof(glm::vec3);
+       // positions
+       glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
+       glBufferData(GL_ARRAY_BUFFER, verticesSize, NULL, GL_STATIC_DRAW);
+       glBufferSubData(GL_ARRAY_BUFFER, 0, verticesSize,
+                                       glm::value_ptr(vertices[0]));
+
+       GLuint posLoc = glGetAttribLocation(progId, "vPosition");
+       glEnableVertexAttribArray(posLoc);
+       glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
+       // indices
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
+       glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
+
+       return indices.size();
+}
+
 bool needToCalculateClosestVertex = false;
 bool needToInterpolateBlendshapes = false;
 
@@ -297,9 +335,15 @@ void init() {
 
        plainProg = new Program("plainvertex.glsl", "plainfrag.glsl");
        glUseProgram(plainProg->progId);
-       setupLightBuffers(plainProg->progId);
+       setupPlainBuffers(plainProg->progId, cube());
        plainProg->validate();
 
+       cursorProg = new Program("plainvertex.glsl", "plainfrag.glsl");
+       glUseProgram(cursorProg->progId);
+       glGenVertexArrays(1, &cursorVao);
+       cursorNumIndices = setupBuffersWithIndices(cursorProg->progId, cursorVao, sphere(), sphereIndices());
+       cursorProg->validate();
+
        skyboxes.push_back(Skybox(Image("skyboxes/loft/Newport_Loft_Ref.hdr")));
        skyboxes.push_back(Skybox(Image("skyboxes/wooden_lounge_8k.hdr")));
        skyboxes.push_back(Skybox(Image("skyboxes/machine_shop_02_8k.hdr")));
@@ -360,7 +404,7 @@ void init() {
                for (int i = 0; i < numBlends; i++) names[i] = bsModel.blendshapes[i].name;
                controlWindow = createControlWindow(names, &cwDelegate);
 
-               camPos = { 0, 22, 81 };
+               camPos = { 0, 18, 81 };
                camFront = { 0, 0, -1 };
                camUp = { 0, 1, 0 };
                zfar = 10000;
@@ -476,12 +520,40 @@ int prevMouseX, prevMouseY;
 bool firstMouse = true;
 
 void motion(int x, int y) {
-#ifdef ENABLE_MOVEMENT
        if (firstMouse) {
                prevMouseX = x;
                prevMouseY = y;
                firstMouse = false;
        }
+       float dx = x - prevMouseX, dy = y - prevMouseY;
+       prevMouseX = x; prevMouseY = y;
+       if (curMode == Blendshapes) {
+               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) {
+       if (firstMouse) {
+               prevMouseX = x;
+               prevMouseY = y;
+               firstMouse = false;
+       }
+       mouseX = x; mouseY = y;
+       prevMouseX = x;
+       prevMouseY = y;
+#ifdef ENABLE_MOVEMENT
+
+
        int dx = x - prevMouseX, dy = y - prevMouseY;
 
        prevMouseX = x;
@@ -503,21 +575,22 @@ void motion(int x, int y) {
                camUp = glm::vec3(0, 1, 0);
        }
 #endif
-
-       mouseX = x; mouseY = y;
+       if (curMode == Blendshapes)
                needToCalculateClosestVertex = true;
-
 }
 
 void mouse(int button, int state, int x, int y) {
        if (isPanelFocused(controlWindow))
                return;
+
        if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
+               if (closestVertex.distance < closestVertexThreshold) {
                        VertIdx idx = { closestVertex.meshIdx, closestVertex.vertIdx };
                        if (manipulators.count(idx) <= 0)
                                manipulators[idx] = closestVertex.pos;
                        curManipulator = idx;
                }
+       }
 
 #ifdef ENABLE_MOVEMENT
        if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
@@ -547,7 +620,7 @@ int main(int argc, char** argv) {
        glutKeyboardUpFunc(keyboardUp);
        glutTimerFunc(16, timer, 0);
        glutMotionFunc(motion);
-       glutPassiveMotionFunc(motion);
+       glutPassiveMotionFunc(passiveMotion);
        glutMouseFunc(mouse);
 
        glutMainLoop();