#include "Cloth.h" #include "conversions.h" #include "Obstacles.h" #include "Constraints.h" #include "Forces.h" #include "GeneralizedSolver.h" #include "UtilEOL.h" #include "external\ArcSim\mesh.hpp" #include "external\ArcSim\io.hpp" #include "external\ArcSim\geometry.hpp" #ifdef EOLC_ONLINE #define GLM_FORCE_RADIANS #include #include #include "online/MatrixStack.h" #include "online/Program.h" #include "online/GLSL.h" #endif // EOLC_ONLINE using namespace std; using namespace Eigen; Cloth::Cloth() { consts = make_shared(); myForces = make_shared(); } void Cloth::build(const Vector2i res, const Vector3d &x00, const Vector3d &x01, const Vector3d &x10, const Vector3d &x11) { for (int i = 0; i < res(0); ++i) { double u = i / (res(0) - 1.0); Vector3d x0 = (1 - u)*x00 + u*x10; Vector3d x1 = (1 - u)*x01 + u*x11; for (int j = 0; j < res(1); ++j) { double v = j / (res(1) - 1.0); Vector3d x = (1 - v)*x0 + v*x1; mesh.add(new Vert(Vec3(x(0),x(1),0.0), Vec3(0))); mesh.add(new Node(e2v(x), e2v(x), Vec3(0), 0, 0, false)); connect(mesh.verts.back(), mesh.nodes.back()); } } double dx = ((x01(0) - x00(0)) / (res(1) - 1)) / 2; double dy = ((x10(1) - x00(1)) / (res(0) - 1)) / 2; for (int i = 0; i < res(0) - 1; ++i) { for (int j = 0; j < res(1) - 1; ++j) { Vector3d x = x00; x(0) += (2 * j + 1) * dx; x(1) += (2 * i + 1) * dy; Vec3 ver(x[0], x[1], 0); Vec3 vel(0.0, 0.0, 0.0); mesh.add(new Vert(Vec3(x(0), x(1), 0.0), Vec3(0))); mesh.add(new Node(e2v(x), e2v(x), Vec3(0), 0, 0, false)); connect(mesh.verts.back(), mesh.nodes.back()); } } int center_index_cnt = 0; for (int i = 0; i < res(0) - 1; i++) { for (int j = 0; j < res(1) - 1; j++) { int k0 = (i * res(1)) + j; // upper right index int kc0 = ((res(0) * res(1)) + center_index_cnt); // center index center_index_cnt++; vector verts1; verts1.push_back(mesh.verts[k0]); verts1.push_back(mesh.verts[k0 + 1]); verts1.push_back(mesh.verts[kc0]); vector faces1 = triangulateARC(verts1); for (int f = 0; f < faces1.size(); f++) mesh.add(faces1[f]); vector verts2; verts2.push_back(mesh.verts[k0 + 1]); verts2.push_back(mesh.verts[k0 + res(1) + 1]); verts2.push_back(mesh.verts[kc0]); vector faces2 = triangulateARC(verts2); for (int f = 0; f < faces2.size(); f++) mesh.add(faces2[f]); vector verts3; verts3.push_back(mesh.verts[k0 + res(1) + 1]); verts3.push_back(mesh.verts[k0 + res(1)]); verts3.push_back(mesh.verts[kc0]); vector faces3 = triangulateARC(verts3); for (int f = 0; f < faces3.size(); f++) mesh.add(faces3[f]); vector verts4; verts4.push_back(mesh.verts[k0 + res(1)]); verts4.push_back(mesh.verts[k0]); verts4.push_back(mesh.verts[kc0]); vector faces4 = triangulateARC(verts4); for (int f = 0; f < faces4.size(); f++) mesh.add(faces4[f]); } } for (int i = 0; i < mesh.faces.size(); i++) { mesh.faces[i]->material = &material; } mark_nodes_to_preserve(mesh); compute_ms_data(mesh); v.resize(mesh.nodes.size() * 3); f.resize(mesh.nodes.size() * 3); posBuf.clear(); norBuf.clear(); texBuf.clear(); eleBuf.clear(); posBuf.resize(mesh.nodes.size() * 3); norBuf.resize(mesh.nodes.size() * 3); texBuf.resize(mesh.nodes.size() * 2); eleBuf.resize(mesh.faces.size() * 3); updatePosNor(); } void Cloth::updatePosNor() { for (int i = 0; i < mesh.nodes.size(); i++) { Vec3 xm = mesh.nodes[i]->x; Vec3 Xm = mesh.nodes[i]->verts[0]->u; Vec3 nm = mesh.nodes[i]->n; Vec3 tm = mesh.nodes[i]->verts[0]->u; posBuf[3 * i + 0] = xm[0]; posBuf[3 * i + 1] = xm[1]; posBuf[3 * i + 2] = xm[2]; norBuf[3 * i + 0] = nm[0]; norBuf[3 * i + 1] = nm[1]; norBuf[3 * i + 2] = nm[2]; texBuf[2 * i + 0] = tm[0]; texBuf[2 * i + 1] = tm[1]; } for (int i = 0; i < mesh.faces.size(); i++) { eleBuf[3 * i + 0] = mesh.faces[i]->v[0]->index; eleBuf[3 * i + 1] = mesh.faces[i]->v[1]->index; eleBuf[3 * i + 2] = mesh.faces[i]->v[2]->index; } } void Cloth::updateBuffers() { posBuf.clear(); norBuf.clear(); texBuf.clear(); eleBuf.clear(); posBuf.resize(mesh.nodes.size() * 3); norBuf.resize(mesh.nodes.size() * 3); texBuf.resize(mesh.nodes.size() * 2); eleBuf.resize(mesh.faces.size() * 3); // Update position and normal buffers updatePosNor(); } void Cloth::updatePreviousMesh() { delete_mesh(last_mesh); last_mesh = deep_copy(mesh); } void Cloth::velocityTransfer() { v.resize(mesh.nodes.size() * 3 + mesh.EoL_Count * 2); v.setZero(); // Loop through all of our nodes and update their velocities for (int n = 0; n < mesh.nodes.size(); n++) { Node* node = mesh.nodes[n]; Vert* vert = node->verts[0]; // Search through the unremeshed mesh to see if this node existed before, or was just introduced it this step // TODO:: Can more than one node fall within the close range? bool found = false; double how_close = 1e-6; int closest = -1; for (int j = 0; j < last_mesh.nodes.size(); j++) { double dist = unsigned_vv_distance(vert->u, last_mesh.nodes[j]->verts[0]->u); if (unsigned_vv_distance(vert->u, last_mesh.nodes[j]->verts[0]->u) < how_close) { how_close = dist; closest = j; found = true; break; } } // If the node existed before do one of two things if (found) { // If the node was EOL but is no longer, we have to transfer it's EOL components back into fully LAG if (node->EoL_state == Node::WasEOL) { node->EoL_state = Node::IsLAG; MatrixXd F = MatrixXd::Zero(3, 2); Vector3d nodev = v2e(node->v); Vector2d nodeV = v322e(vert->v); for (int j = 0; j < vert->adjf.size(); j++) { F += incedent_angle(vert, vert->adjf[j]) * deform_grad(vert->adjf[j]); } is_seam_or_boundary(node) ? F *= (1 / M_PI) : F *= (1 / (2 * M_PI)); Vector3d newvL = nodev - F*nodeV; v(3 * n) = newvL(0); v(3 * n + 1) = newvL(1); v(3 * n + 2) = newvL(2); } // If it already existed, just pull it's old information else { // TODO:: The node will have the data if it gets here correct? v(3 * n) = node->v[0]; v(3 * n + 1) = node->v[1]; v(3 * n + 2) = node->v[2]; if (node->EoL) { v(mesh.nodes.size() * 3 + node->EoL_index * 2) = vert->v[0]; v(mesh.nodes.size() * 3 + node->EoL_index * 2 + 1) = vert->v[1]; } } } // If the node did not exist We have to do one of four things else { // If its a LAG point we can just use barycentric averaging if (!node->EoL) { Face* old_face = get_enclosing_face(last_mesh, Vec2(vert->u[0], vert->u[1])); Vec3 bary = get_barycentric_coords(Vec2(vert->u[0], vert->u[1]), old_face); Vector3d vwA = v2e(old_face->v[0]->node->v); Vector3d vwB = v2e(old_face->v[1]->node->v); Vector3d vwC = v2e(old_face->v[2]->node->v); // If any of its surrounding points are EOL, the Eulerian component velocity component must be taken into account if (old_face->v[0]->node->EoL) vwA += -deform_grad(old_face) * v322e(old_face->v[0]->v); if (old_face->v[1]->node->EoL) vwB += -deform_grad(old_face) * v322e(old_face->v[1]->v); if (old_face->v[2]->node->EoL) vwC += -deform_grad(old_face) * v322e(old_face->v[2]->v); Vector3d v_new_world = bary[0] * vwA + bary[1] * vwB + bary[2] * vwC; node->v = e2v(v_new_world); v(3 * n) = v_new_world(0); v(3 * n + 1) = v_new_world(1); v(3 * n + 2) = v_new_world(2); } else { // If the new EOL was the result of a conserved edge split, we average the components for efficiency // For this to happen neither adjacent nodes can be NewEOLs, otherwise the averaging would be wrong if (node->EoL_state == Node::NewEOLFromSplit) { Vector3d newvL = Vector3d::Zero(); Vector3d newvE = Vector3d::Zero(); for (int e = 0; e < node->adje.size(); e++) { if (node->adje[e]->preserve) { Node* adjn = other_node(node->adje[e], node); newvL += v2e(adjn->v); newvE += v2e(adjn->verts[0]->v); } } newvL /= 2.0; newvE /= 2.0; // Should only ever be two here..? // We put in the new averaged velocities v(mesh.nodes.size() * 3 + node->EoL_index * 2) = newvE(0); v(mesh.nodes.size() * 3 + node->EoL_index * 2 + 1) = newvE(1); v(3 * n) = newvL(0); v(3 * n + 1) = newvL(1); v(3 * n + 2) = newvL(2); } // If this is brand new EOL with no previuosly known information, we calculate its world velocity and distribute it else { Face* old_face = get_enclosing_face(last_mesh, Vec2(vert->u[0], vert->u[1])); Matrix2d ftf = deform_grad(old_face).transpose() * deform_grad(old_face); Vector2d dtv = -deform_grad(old_face).transpose() * v2e(node->v); // We set up a simple KKT system with the purpose of putting as much world velocity as possible into the Eulerian component MatrixXd KKTl = MatrixXd::Zero(4, 4); KKTl.block(0, 0, 2, 2) = ftf; KKTl.block<2, 2>(0, 2) = Matrix2d::Identity(); KKTl.block<2, 2>(2, 0) = Matrix2d::Identity(); VectorXd KKTr(4); KKTr << dtv, VectorXd::Zero(2); ConjugateGradient cg; cg.compute(KKTl); VectorXd newvE = cg.solve(KKTr); // This gives us the Eulerian component vert->v = Vec3(newvE(0), newvE(1), 0.0); v(mesh.nodes.size() * 3 + node->EoL_index * 2) = newvE(0); v(mesh.nodes.size() * 3 + node->EoL_index * 2 + 1) = newvE(1); // We use the deformation gradient to extract the lagrangian component Vector3d newvL = v2e(node->v) + -deform_grad(old_face) * newvE.segment<2>(0); node->v = e2v(newvL); v(3 * n) = newvL(0); v(3 * n + 1) = newvL(1); v(3 * n + 2) = newvL(2); } } } } } void Cloth::solve(shared_ptr gs, double h) { VectorXd b = -(myForces->M * v + h * myForces->f); bool success = gs->velocitySolve(false, consts->hasCollisions, myForces->MDK, b, consts->Aeq, consts->beq, consts->Aineq, consts->bineq, v); } void Cloth::step(shared_ptr gs, shared_ptr obs, const Vector3d& grav, double h) { consts->fill(mesh, obs, h); velocityTransfer(); myForces->fill(mesh, material, grav, h); solve(gs, h); for (int n = 0; n < mesh.nodes.size(); n++) { Node* node = mesh.nodes[n]; Vert* vert = node->verts[0]; node->v[0] = v(n * 3); node->v[1] = v(n * 3 + 1); node->v[2] = v(n * 3 + 2); node->x = node->x + h * node->v; if (node->EoL) { vert->v[0] = v(mesh.nodes.size() * 3 + node->EoL_index * 2); vert->v[1] = v(mesh.nodes.size() * 3 + node->EoL_index * 2 + 1); //if (mesh.nodes[i]->verts[0]->u[0] != Xmin && mesh.nodes[i]->verts[0]->u[0] != Xmax) mesh.nodes[i]->verts[0]->u[0] = mesh.nodes[i]->verts[0]->u[0] + h * mesh.nodes[i]->verts[0]->v[0]; //if (mesh.nodes[i]->verts[0]->u[1] != Ymin && mesh.nodes[i]->verts[0]->u[1] != Ymax) mesh.nodes[i]->verts[0]->u[1] = mesh.nodes[i]->verts[0]->u[1] + h * mesh.nodes[i]->verts[0]->v[1]; vert->u[0] = vert->u[0] + h * vert->v[0]; vert->u[1] = vert->u[1] + h * vert->v[1]; } } updateBuffers(); } #ifdef EOLC_ONLINE void Cloth::init() { glGenBuffers(1, &posBufID); glBindBuffer(GL_ARRAY_BUFFER, posBufID); glBufferData(GL_ARRAY_BUFFER, posBuf.size() * sizeof(float), &posBuf[0], GL_DYNAMIC_DRAW); glGenBuffers(1, &norBufID); glBindBuffer(GL_ARRAY_BUFFER, norBufID); glBufferData(GL_ARRAY_BUFFER, norBuf.size() * sizeof(float), &norBuf[0], GL_DYNAMIC_DRAW); glGenBuffers(1, &texBufID); glBindBuffer(GL_ARRAY_BUFFER, texBufID); glBufferData(GL_ARRAY_BUFFER, texBuf.size() * sizeof(float), &texBuf[0], GL_DYNAMIC_DRAW); glGenBuffers(1, &eleBufID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eleBufID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, eleBuf.size() * sizeof(unsigned int), &eleBuf[0], GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); assert(glGetError() == GL_NO_ERROR); } void Cloth::draw(shared_ptr MV, const shared_ptr p) const { // Draw mesh glUniform3fv(p->getUniform("kdFront"), 1, Vector3f(1.0, 0.0, 0.0).data()); glUniform3fv(p->getUniform("kdBack"), 1, Vector3f(1.0, 1.0, 0.0).data()); MV->pushMatrix(); glUniformMatrix4fv(p->getUniform("MV"), 1, GL_FALSE, glm::value_ptr(MV->topMatrix())); int h_pos = p->getAttribute("aPos"); glEnableVertexAttribArray(h_pos); glBindBuffer(GL_ARRAY_BUFFER, posBufID); glBufferData(GL_ARRAY_BUFFER, posBuf.size() * sizeof(float), &posBuf[0], GL_DYNAMIC_DRAW); glVertexAttribPointer(h_pos, 3, GL_FLOAT, GL_FALSE, 0, (const void *)0); int h_nor = p->getAttribute("aNor"); glEnableVertexAttribArray(h_nor); glBindBuffer(GL_ARRAY_BUFFER, norBufID); glBufferData(GL_ARRAY_BUFFER, norBuf.size() * sizeof(float), &norBuf[0], GL_DYNAMIC_DRAW); glVertexAttribPointer(h_nor, 3, GL_FLOAT, GL_FALSE, 0, (const void *)0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eleBufID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, eleBuf.size() * sizeof(unsigned int), &eleBuf[0], GL_DYNAMIC_DRAW); glDrawElements(GL_TRIANGLES, eleBuf.size(), GL_UNSIGNED_INT, (void*)0); glDisableVertexAttribArray(h_nor); glDisableVertexAttribArray(h_pos); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); MV->popMatrix(); } void Cloth::drawSimple(shared_ptr MV, const shared_ptr p) const { for (int i = 0; i < mesh.nodes.size(); i++) { if (!mesh.nodes[i]->EoL) { glColor3f(1.0f, 1.0f, 0.0f); glPointSize(10.0f); glBegin(GL_POINTS); glVertex3f(mesh.nodes[i]->x[0], mesh.nodes[i]->x[1], mesh.nodes[i]->x[2]); glEnd(); } } for (int i = 0; i < mesh.edges.size(); i++) { Edge *e = mesh.edges[i]; if (e->preserve) { glColor3f(0.0f, 1.0f, 0.0f); glLineWidth(3.0f); glBegin(GL_LINES); glVertex3f(e->n[0]->x[0], e->n[0]->x[1], e->n[0]->x[2]); glVertex3f(e->n[1]->x[0], e->n[1]->x[1], e->n[1]->x[2]); glEnd(); glLineWidth(1.0f); } if (e->n[0]->EoL) { glColor3f(1.0f, 0.0f, 0.0f); glPointSize(10.0f); glBegin(GL_POINTS); glVertex3f(e->n[0]->x[0], e->n[0]->x[1], e->n[0]->x[2]); glEnd(); } if (e->n[1]->EoL) { glColor3f(1.0f, 0.0f, 0.0f); glPointSize(10.0f); glBegin(GL_POINTS); glVertex3f(e->n[1]->x[0], e->n[1]->x[1], e->n[1]->x[2]); glEnd(); } } } #endif // EOLC_ONLINE