#include #include #include #include #include #include #include #include #include "Shader.h" #include "Mesh.h" #include "Renderer.h" #include "MassSpringSolver.h" #include "UserInteraction.h" // G L O B A L S /////////////////////////////////////////////////////////////////// // Window static int g_windowWidth = 640, g_windowHeight = 640; static bool g_mouseClickDown = false; static bool g_mouseLClickButton, g_mouseRClickButton, g_mouseMClickButton; static int g_mouseClickX; static int g_mouseClickY; // User Interaction static UserInteraction* UI; static Renderer* g_pickRenderer; // Constants static const float PI = glm::pi(); // Shader Handles static PhongShader* g_phongShader; // linked phong shader static PickShader* g_pickShader; // linked pick shader // Shader parameters static const glm::vec3 g_albedo(0.0f, 0.3f, 0.7f); static const glm::vec3 g_ambient(0.01f, 0.01f, 0.01f); static const glm::vec3 g_light(1.0f, 1.0f, -1.0f); // Mesh static Mesh* g_clothMesh; // halfedge data structure // Render Target static ProgramInput* g_render_target; // vertex, normal, texutre, index // Animation static const int g_fps = 60; // frames per second | 60 static const int g_iter = 5; // iterations per time step | 10 static const int g_frame_time = 15; // approximate time for frame calculations | 15 static const int g_animation_timer = (int) ((1.0f / g_fps) * 1000 - g_frame_time); // Mass Spring System static mass_spring_system* g_system; static MassSpringSolver* g_solver; // System parameters namespace SystemParam { static const int n = 61; // must be odd, n * n = n_vertices | 61 static const float w = 2.0f; // width | 2.0f static const float h = 0.008f; // time step, smaller for better results | 0.008f = 0.016f/2 static const float r = w / (n - 1) * 1.05f; // spring rest legnth static const float k = 1.0f; // spring stiffness | 1.0f; static const float m = 0.25f / (n * n); // point mass | 0.25f static const float a = 0.993f; // damping, close to 1.0 | 0.993f static const float g = 9.8f * m; // gravitational force | 9.8f } // Constraint Graph static CgRootNode* g_cgRootNode; // Scene parameters static const float g_camera_distance = 4.2f; // Scene matrices static glm::mat4 g_ModelViewMatrix; static glm::mat4 g_ProjectionMatrix; // F U N C T I O N S ////////////////////////////////////////////////////////////// // state initialization static void initGlutState(int, char**); static void initGLState(); static void initShaders(); // Read, compile and link shaders static void initCloth(); // Generate cloth mesh static void initScene(); // Generate scene matrices // demos static void demo_hang(); // curtain hanging from top corners static void demo_drop(); // curtain dropping on sphere static void(*g_demo)() = demo_drop; // glut callbacks static void display(); static void reshape(int, int); static void mouse(int, int, int, int); static void motion(int, int); // draw cloth function static void drawCloth(); static void animateCloth(int value); // scene update static void updateProjection(); static void updateRenderTarget(); // cleaning static void cleanUp(); // error checks void checkGlErrors(); // M A I N ////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { try { initGlutState(argc, argv); glewInit(); initGLState(); initShaders(); initCloth(); initScene(); glutTimerFunc(g_animation_timer, animateCloth, 0); glutMainLoop(); cleanUp(); return 0; } catch (const std::runtime_error& e) { std::cout << "Exception caught: " << e.what() << std::endl; return -1; } } // S T A T E I N I T I A L I Z A T O N ///////////////////////////////////////////// static void initGlutState(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(g_windowWidth, g_windowHeight); glutCreateWindow("Cloth App"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMotionFunc(motion); } static void initGLState() { glClearColor(0.25f, 0.25f, 0.25f, 0); glClearDepth(1.); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glReadBuffer(GL_BACK); glEnable(GL_FRAMEBUFFER_SRGB); checkGlErrors(); } static void initShaders() { GLShader basic_vert(GL_VERTEX_SHADER); GLShader phong_frag(GL_FRAGMENT_SHADER); GLShader pick_frag(GL_FRAGMENT_SHADER); basic_vert.compile(std::ifstream("./shaders/basic.vshader")); phong_frag.compile(std::ifstream("./shaders/phong.fshader")); pick_frag.compile(std::ifstream("./shaders/pick.fshader")); g_phongShader = new PhongShader; g_pickShader = new PickShader; g_phongShader->link(basic_vert, phong_frag); g_pickShader->link(basic_vert, pick_frag); checkGlErrors(); } static void initCloth() { // short hand const int n = SystemParam::n; const float w = SystemParam::w; // generate mesh MeshBuilder meshBuilder; meshBuilder.uniformGrid(w, n); g_clothMesh = meshBuilder.getResult(); // fill program input g_render_target = new ProgramInput; g_render_target->setPositionData(g_clothMesh->vbuff(), g_clothMesh->vbuffLen()); g_render_target->setNormalData(g_clothMesh->nbuff(), g_clothMesh->nbuffLen()); g_render_target->setTextureData(g_clothMesh->tbuff(), g_clothMesh->tbuffLen()); g_render_target->setIndexData(g_clothMesh->ibuff(), g_clothMesh->ibuffLen()); // check errors checkGlErrors(); // build demo system g_demo(); } static void initScene() { g_ModelViewMatrix = glm::lookAt( glm::vec3(0.618, -0.786, 0.3f) * g_camera_distance, glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, 0.0f, 1.0f) ) * glm::translate(glm::mat4(1), glm::vec3(0.0f, 0.0f, SystemParam::w / 4)); updateProjection(); } static void demo_hang() { // short hand const int n = SystemParam::n; // initialize mass spring system MassSpringBuilder massSpringBuilder; massSpringBuilder.uniformGrid( SystemParam::n, SystemParam::h, SystemParam::r, SystemParam::k, SystemParam::m, SystemParam::a, SystemParam::g ); g_system = massSpringBuilder.getResult(); // initialize mass spring solver g_solver = new MassSpringSolver(g_system, g_clothMesh->vbuff()); // deformation constraint parameters const float tauc = 0.4f; // critical spring deformation | 0.4f const unsigned int deformIter = 15; // number of iterations | 15 // initialize constraints // spring deformation constraint CgSpringDeformationNode* deformationNode = new CgSpringDeformationNode(g_system, g_clothMesh->vbuff(), tauc, deformIter); deformationNode->addSprings(massSpringBuilder.getShearIndex()); deformationNode->addSprings(massSpringBuilder.getStructIndex()); // fix top corners CgPointFixNode* cornerFixer = new CgPointFixNode(g_system, g_clothMesh->vbuff()); cornerFixer->fixPoint(0); cornerFixer->fixPoint(n - 1); // initialize user interaction g_pickRenderer = new Renderer(); g_pickRenderer->setProgram(g_pickShader); g_pickRenderer->setProgramInput(g_render_target); g_pickRenderer->setElementCount(g_clothMesh->ibuffLen()); g_pickShader->setTessFact(SystemParam::n); CgPointFixNode* mouseFixer = new CgPointFixNode(g_system, g_clothMesh->vbuff()); UI = new GridMeshUI(g_pickRenderer, mouseFixer, g_clothMesh->vbuff(), n); // build constraint graph g_cgRootNode = new CgRootNode(g_system, g_clothMesh->vbuff()); // first layer g_cgRootNode->addChild(deformationNode); // second layer deformationNode->addChild(cornerFixer); deformationNode->addChild(mouseFixer); } static void demo_drop() { // short hand const int n = SystemParam::n; // initialize mass spring system MassSpringBuilder massSpringBuilder; massSpringBuilder.uniformGrid( SystemParam::n, SystemParam::h, SystemParam::r, SystemParam::k, SystemParam::m, SystemParam::a, SystemParam::g ); g_system = massSpringBuilder.getResult(); // initialize mass spring solver g_solver = new MassSpringSolver(g_system, g_clothMesh->vbuff()); // sphere collision constraint parameters const float radius = 0.64f; // sphere radius | 0.64f const Eigen::Vector3f center(0, 0, -1);// sphere center | (0, 0, -1) // deformation constraint parameters const float tauc = 0.12f; // critical spring deformation | 0.12f const unsigned int deformIter = 15; // number of iterations | 15 // initialize constraints // sphere collision constraint CgSphereCollisionNode* sphereCollisionNode = new CgSphereCollisionNode(g_system, g_clothMesh->vbuff(), radius, center); // spring deformation constraint CgSpringDeformationNode* deformationNode = new CgSpringDeformationNode(g_system, g_clothMesh->vbuff(), tauc, deformIter); deformationNode->addSprings(massSpringBuilder.getShearIndex()); deformationNode->addSprings(massSpringBuilder.getStructIndex()); // initialize user interaction g_pickRenderer = new Renderer(); g_pickRenderer->setProgram(g_pickShader); g_pickRenderer->setProgramInput(g_render_target); g_pickRenderer->setElementCount(g_clothMesh->ibuffLen()); g_pickShader->setTessFact(SystemParam::n); CgPointFixNode* mouseFixer = new CgPointFixNode(g_system, g_clothMesh->vbuff()); UI = new GridMeshUI(g_pickRenderer, mouseFixer, g_clothMesh->vbuff(), n); // build constraint graph g_cgRootNode = new CgRootNode(g_system, g_clothMesh->vbuff()); // first layer g_cgRootNode->addChild(deformationNode); g_cgRootNode->addChild(sphereCollisionNode); // second layer deformationNode->addChild(mouseFixer); } // G L U T C A L L B A C K S ////////////////////////////////////////////////////// static void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawCloth(); glutSwapBuffers(); checkGlErrors(); } static void reshape(int w, int h) { g_windowWidth = w; g_windowHeight = h; glViewport(0, 0, w, h); updateProjection(); glutPostRedisplay(); } static void mouse(const int button, const int state, const int x, const int y) { g_mouseClickX = x; g_mouseClickY = g_windowHeight - y - 1; g_mouseLClickButton |= (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN); g_mouseRClickButton |= (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN); g_mouseMClickButton |= (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN); g_mouseLClickButton &= !(button == GLUT_LEFT_BUTTON && state == GLUT_UP); g_mouseRClickButton &= !(button == GLUT_RIGHT_BUTTON && state == GLUT_UP); g_mouseMClickButton &= !(button == GLUT_MIDDLE_BUTTON && state == GLUT_UP); g_mouseClickDown = g_mouseLClickButton || g_mouseRClickButton || g_mouseMClickButton; // TODO: move to UserInteraction class: add renderer member variable // pick point if (g_mouseLClickButton) { UI->setModelview(g_ModelViewMatrix); UI->setProjection(g_ProjectionMatrix); UI->grabPoint(g_mouseClickX, g_mouseClickY); } else UI->releasePoint(); } static void motion(const int x, const int y) { const float dx = float(x - g_mouseClickX); const float dy = float (-(g_windowHeight - y - 1 - g_mouseClickY)); if (g_mouseLClickButton) { //glm::vec3 ux(g_ModelViewMatrix * glm::vec4(1, 0, 0, 0)); //glm::vec3 uy(g_ModelViewMatrix * glm::vec4(0, 1, 0, 0)); glm::vec3 ux(0, 1, 0); glm::vec3 uy(0, 0, -1); UI->movePoint(0.01f * (dx * ux + dy * uy)); } g_mouseClickX = x; g_mouseClickY = g_windowHeight - y - 1; } // C L O T H /////////////////////////////////////////////////////////////////////// static void drawCloth() { Renderer renderer; renderer.setProgram(g_phongShader); renderer.setModelview(g_ModelViewMatrix); renderer.setProjection(g_ProjectionMatrix); g_phongShader->setAlbedo(g_albedo); g_phongShader->setAmbient(g_ambient); g_phongShader->setLight(g_light); renderer.setProgramInput(g_render_target); renderer.setElementCount(g_clothMesh->ibuffLen()); renderer.draw(); } static void animateCloth(int value) { // solve two time-steps g_solver->solve(g_iter); g_solver->solve(g_iter); // fix points CgSatisfyVisitor visitor; visitor.satisfy(*g_cgRootNode); // update normals g_clothMesh->request_face_normals(); g_clothMesh->update_normals(); g_clothMesh->release_face_normals(); // update target updateRenderTarget(); // redisplay glutPostRedisplay(); // reset timer glutTimerFunc(g_animation_timer, animateCloth, 0); } // S C E N E U P D A T E /////////////////////////////////////////////////////////// static void updateProjection() { g_ProjectionMatrix = glm::perspective(PI / 4.0f, g_windowWidth * 1.0f / g_windowHeight, 0.01f, 1000.0f); } static void updateRenderTarget() { // update vertex positions g_render_target->setPositionData(g_clothMesh->vbuff(), g_clothMesh->vbuffLen()); // update vertex normals g_render_target->setNormalData(g_clothMesh->nbuff(), g_clothMesh->vbuffLen()); } // C L E A N U P ////////////////////////////////////////////////////////////////// static void cleanUp() { // delete mesh delete g_clothMesh; // delete UI delete g_pickRenderer; delete UI; // delete render target delete g_render_target; // delete mass-spring system delete g_system; delete g_solver; // delete constraint graph // TODO } // E R R O R S ///////////////////////////////////////////////////////////////////// void checkGlErrors() { const GLenum errCode = glGetError(); if (errCode != GL_NO_ERROR) { std::string error("GL Error: "); error += reinterpret_cast(gluErrorString(errCode)); std::cerr << error << std::endl; throw std::runtime_error(error); } }