Circular cursor + dragging
authorLuke Lau <luke_lau@icloud.com>
Wed, 11 Mar 2020 23:13:41 +0000 (23:13 +0000)
committerLuke Lau <luke_lau@icloud.com>
Wed, 11 Mar 2020 23:13:41 +0000 (23:13 +0000)
main.cpp
shapes.hpp
skybox.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();
index 92cc8adb0882ecd6c3d3c54955bad6eb0a25d286..876ce3c550ebf97c3951850d4483f1ddaf6fe1ae 100644 (file)
@@ -1,4 +1,6 @@
 #include <array>
+#include <vector>
+#include <cmath>
 #define GLM_FORCE_PURE
 #include <glm/glm.hpp>
 
@@ -24,7 +26,70 @@ constexpr array<glm::vec3, 6> plane() {
        });
 }
 
-constexpr array<glm::vec3, 36> cube() {
+inline vector<glm::vec3> cube() {
+       int i = 0;
+       vector<glm::vec3> vertices(36);
+       
+       const array<glm::vec3, 4> front = {
+               glm::vec3(1, -1, -1),
+               glm::vec3(-1, -1, -1),
+               glm::vec3(-1, 1, -1),
+               glm::vec3(1, 1, -1)
+       };
+       
+       for (auto v: quadToTriangles(front))
+               vertices[i++] = v;
+
+       const array<glm::vec3, 4> back = {
+               glm::vec3(1, 1, 1),
+               glm::vec3(-1, 1, 1),
+               glm::vec3(-1, -1, 1),
+               glm::vec3(1, -1, 1)
+       };
+       for (auto v: quadToTriangles(back))
+               vertices[i++] = v;
+
+
+       const array<glm::vec3, 4> top = {
+               glm::vec3(1, 1, -1),
+               glm::vec3(-1, 1, -1),
+               glm::vec3(-1, 1, 1),
+               glm::vec3(1, 1, 1)
+       };
+       for (auto v: quadToTriangles(top))
+               vertices[i++] = v;
+
+       const array<glm::vec3, 4> bottom = {
+               glm::vec3(1, -1, 1),
+               glm::vec3(-1, -1, 1),
+               glm::vec3(-1, -1, -1),
+               glm::vec3(1, -1, -1)
+       };
+       for (auto v: quadToTriangles(bottom))
+               vertices[i++] = v;
+
+       const array<glm::vec3, 4> left = {
+               glm::vec3(-1, 1, 1),
+               glm::vec3(-1, 1, -1),
+               glm::vec3(-1, -1, -1),
+               glm::vec3(-1, -1, 1)
+       };
+       for (auto v: quadToTriangles(left))
+               vertices[i++] = v;
+
+       const array<glm::vec3, 4> right = {
+               glm::vec3(1, 1, -1),
+               glm::vec3(1, 1, 1),
+               glm::vec3(1, -1, 1),
+               glm::vec3(1, -1, -1)
+       };
+       for (auto v: quadToTriangles(right))
+               vertices[i++] = v;
+
+       return vertices;
+}
+
+constexpr array<glm::vec3, 36> cubeArray() {
        int i = 0;
        array<glm::vec3, 36> vertices;
        
@@ -112,7 +177,72 @@ constexpr glm::vec3 pyramid[18] = {
        glm::vec3(1, -1, 1)
 };
 
-// TODO: Add a sphere
-/* constexpr std::vector<glm::vec3> sphere() { */
+// taken from http://www.songho.ca/opengl/gl_sphere.html
+#define SECTOR_COUNT 16
+#define STACK_COUNT 16
+inline std::vector<glm::vec3> sphere(float radius = 1) {
+       float sectorStep = 2 * M_PI / SECTOR_COUNT;
+       float stackStep = M_PI / STACK_COUNT;
+
+       std::vector<glm::vec3> vertices;
+
+       for(int i = 0; i <= STACK_COUNT; i++) {
+               float stackAngle = M_PI / 2 - i * stackStep;        // starting from pi/2 to -pi/2
+               float xy = radius * cosf(stackAngle);             // r * cos(u)
+               float z = radius * sinf(stackAngle);              // r * sin(u)
+
+               // add (sectorCount+1) vertices per stack
+               // the first and last vertices have same position and normal, but different tex coords
+               for(int j = 0; j <= SECTOR_COUNT; j++)
+               {
+                       float sectorAngle = j * sectorStep;           // starting from 0 to 2pi
+
+                       // vertex position (x, y, z)
+                       float x = xy * cosf(sectorAngle);             // r * cos(u) * cos(v)
+                       float y = xy * sinf(sectorAngle);             // r * cos(u) * sin(v)
+                       vertices.push_back({x, y, z});
+
+                       // normalized vertex normal (nx, ny, nz)
+                       /* nx = x * lengthInv; */
+                       /* ny = y * lengthInv; */
+                       /* nz = z * lengthInv; */
+                       /* normals.push_back(nx); */
+                       /* normals.push_back(ny); */
+                       /* normals.push_back(nz); */
+
+               }
+       }
+       return vertices;
+}
+
+inline std::vector<unsigned int> sphereIndices() {
+       // generate CCW index list of sphere triangles
+       vector<unsigned int> indices;
+       int k1, k2;
+       for(int i = 0; i < STACK_COUNT; ++i)
+       {
+               k1 = i * (SECTOR_COUNT + 1);     // beginning of current stack
+               k2 = k1 + SECTOR_COUNT + 1;      // beginning of next stack
+
+               for(int j = 0; j < SECTOR_COUNT; ++j, ++k1, ++k2)
+               {
+                       // 2 triangles per sector excluding first and last stacks
+                       // k1 => k2 => k1+1
+                       if(i != 0)
+                       {
+                               indices.push_back(k1);
+                               indices.push_back(k2);
+                               indices.push_back(k1 + 1);
+                       }
 
-/* } */
+                       // k1+1 => k2 => k2+1
+                       if(i != (STACK_COUNT-1))
+                       {
+                               indices.push_back(k1 + 1);
+                               indices.push_back(k2);
+                               indices.push_back(k2 + 1);
+                       }
+               }
+       } 
+       return indices;
+}
index 83f130855fa668919ea9ca938230a117bb839e5f..1c99e9588e1c1abd182525ae1bf537607ab2325d 100644 (file)
@@ -35,7 +35,7 @@ void Skybox::generatePrefilterMap() const {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
        
-       setupVertices(prefilterProg.progId, cube());
+       setupVertices(prefilterProg.progId, cubeArray());
        
        glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
        constexpr GLuint MAX_MIP_LEVELS = 5;
@@ -124,7 +124,7 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
        // generate vertices
-       setupVertices(equiProg.progId, cube());
+       setupVertices(equiProg.progId, cubeArray());
 
        // render the cube
 
@@ -165,7 +165,7 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
        glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
 
        // generate vertices
-       setupVertices(irradianceProg.progId, cube());
+       setupVertices(irradianceProg.progId, cubeArray());
 
        // render irradiance map
        glViewport(0, 0, 32, 32);
@@ -186,7 +186,7 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
        glDepthFunc(GL_LEQUAL);
 
        // reverse so facing inside out
-       vao = setupVertices(program.progId, cube(), true);
+       vao = setupVertices(program.progId, cubeArray(), true);
 
        // restore default framebuffer
        glBindFramebuffer(GL_FRAMEBUFFER, 0);