dust3d/thirdparty/FastMassSpring/ClothApp/app.cpp

471 lines
13 KiB
C++
Executable File

#include <GL/glew.h>
#include <GL/glut.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <stdexcept>
#include <iostream>
#include <string>
#include <vector>
#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<float>();
// 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<const char*>(gluErrorString(errCode));
std::cerr << error << std::endl;
throw std::runtime_error(error);
}
}