384 lines
12 KiB
C++
Executable File
384 lines
12 KiB
C++
Executable File
#include "MassSpringSolver.h"
|
|
#include <iostream>
|
|
|
|
// S Y S T E M //////////////////////////////////////////////////////////////////////////////////////
|
|
mass_spring_system::mass_spring_system(
|
|
unsigned int n_points,
|
|
unsigned int n_springs,
|
|
float time_step,
|
|
EdgeList spring_list,
|
|
VectorXf rest_lengths,
|
|
VectorXf stiffnesses,
|
|
VectorXf masses,
|
|
VectorXf fext,
|
|
float damping_factor
|
|
)
|
|
: n_points(n_points), n_springs(n_springs),
|
|
time_step(time_step), spring_list(spring_list),
|
|
rest_lengths(rest_lengths), stiffnesses(stiffnesses), masses(masses),
|
|
fext(fext), damping_factor(damping_factor) {}
|
|
|
|
// S O L V E R //////////////////////////////////////////////////////////////////////////////////////
|
|
MassSpringSolver::MassSpringSolver(mass_spring_system* system, float* vbuff)
|
|
: system(system), current_state(vbuff, system->n_points * 3),
|
|
prev_state(current_state), spring_directions(system->n_springs * 3) {
|
|
|
|
float h2 = system->time_step * system->time_step; // shorthand
|
|
|
|
// compute M, L, J
|
|
TripletList LTriplets, JTriplets;
|
|
// L
|
|
L.resize(3 * system->n_points, 3 * system->n_points);
|
|
unsigned int k = 0; // spring counter
|
|
for (Edge& i : system->spring_list) {
|
|
for (int j = 0; j < 3; j++) {
|
|
LTriplets.push_back(
|
|
Triplet(3 * i.first + j, 3 * i.first + j, 1 * system->stiffnesses[k]));
|
|
LTriplets.push_back(
|
|
Triplet(3 * i.first + j, 3 * i.second + j, -1 * system->stiffnesses[k]));
|
|
LTriplets.push_back(
|
|
Triplet(3 * i.second + j, 3 * i.first + j, -1 * system->stiffnesses[k]));
|
|
LTriplets.push_back(
|
|
Triplet(3 * i.second + j, 3 * i.second + j, 1 * system->stiffnesses[k]));
|
|
}
|
|
k++;
|
|
}
|
|
L.setFromTriplets(LTriplets.begin(), LTriplets.end());
|
|
|
|
// J
|
|
J.resize(3 * system->n_points, 3 * system->n_springs);
|
|
k = 0; // spring counter
|
|
for (Edge& i : system->spring_list) {
|
|
for (unsigned int j = 0; j < 3; j++) {
|
|
JTriplets.push_back(
|
|
Triplet(3 * i.first + j, 3 * k + j, 1 * system->stiffnesses[k]));
|
|
JTriplets.push_back(
|
|
Triplet(3 * i.second + j, 3 * k + j, -1 * system->stiffnesses[k]));
|
|
}
|
|
k++;
|
|
}
|
|
J.setFromTriplets(JTriplets.begin(), JTriplets.end());
|
|
|
|
// M
|
|
TripletList MTriplets;
|
|
M.resize(3 * system->n_points, 3 * system->n_points);
|
|
for (unsigned int i = 0; i < system->n_points; i++) {
|
|
for (int j = 0; j < 3; j++) {
|
|
MTriplets.push_back(Triplet(3 * i + j, 3 * i + j, system->masses[i]));
|
|
}
|
|
}
|
|
M.setFromTriplets(MTriplets.begin(), MTriplets.end());
|
|
|
|
// pre-factor system matrix
|
|
SparseMatrix A = M + h2 * L;
|
|
system_matrix.compute(A);
|
|
}
|
|
|
|
void MassSpringSolver::globalStep() {
|
|
float h2 = system->time_step * system->time_step; // shorthand
|
|
|
|
// compute right hand side
|
|
VectorXf b = inertial_term
|
|
+ h2 * J * spring_directions
|
|
+ h2 * system->fext;
|
|
|
|
// solve system and update state
|
|
current_state = system_matrix.solve(b);
|
|
}
|
|
|
|
void MassSpringSolver::localStep() {
|
|
unsigned int j = 0;
|
|
for (Edge& i : system->spring_list) {
|
|
Vector3f p12(
|
|
current_state[3 * i.first + 0] - current_state[3 * i.second + 0],
|
|
current_state[3 * i.first + 1] - current_state[3 * i.second + 1],
|
|
current_state[3 * i.first + 2] - current_state[3 * i.second + 2]
|
|
);
|
|
|
|
p12.normalize();
|
|
spring_directions[3 * j + 0] = system->rest_lengths[j] * p12[0];
|
|
spring_directions[3 * j + 1] = system->rest_lengths[j] * p12[1];
|
|
spring_directions[3 * j + 2] = system->rest_lengths[j] * p12[2];
|
|
j++;
|
|
}
|
|
}
|
|
|
|
void MassSpringSolver::solve(unsigned int n) {
|
|
float a = system->damping_factor; // shorthand
|
|
|
|
// update inertial term
|
|
inertial_term = M * ((a + 1) * (current_state) - a * prev_state);
|
|
|
|
// save current state in previous state
|
|
prev_state = current_state;
|
|
|
|
// perform steps
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
localStep();
|
|
globalStep();
|
|
}
|
|
}
|
|
|
|
void MassSpringSolver::timedSolve(unsigned int ms) {
|
|
// TODO
|
|
}
|
|
|
|
|
|
// B U I L D E R ////////////////////////////////////////////////////////////////////////////////////
|
|
void MassSpringBuilder::uniformGrid(
|
|
unsigned int n,
|
|
float time_step,
|
|
float rest_length,
|
|
float stiffness,
|
|
float mass,
|
|
float damping_factor,
|
|
float gravity
|
|
) {
|
|
// n must be odd
|
|
assert(n % 2 == 1);
|
|
|
|
// shorthand
|
|
const double root2 = 1.41421356237;
|
|
|
|
// compute n_points and n_springs
|
|
unsigned int n_points = n * n;
|
|
unsigned int n_springs = (n - 1) * (5 * n - 2);
|
|
|
|
// build mass list
|
|
VectorXf masses(mass * VectorXf::Ones(n_springs));
|
|
|
|
// build spring list and spring parameters
|
|
EdgeList spring_list(n_springs);
|
|
structI.reserve(2 * (n - 1) * n);
|
|
shearI.reserve(2 * (n - 1) * (n - 1));
|
|
bendI.reserve(n * (n - 1));
|
|
|
|
VectorXf rest_lengths(n_springs);
|
|
VectorXf stiffnesses(n_springs);
|
|
unsigned int k = 0; // spring counter
|
|
for(unsigned int i = 0; i < n; i++) {
|
|
for(unsigned int j = 0; j < n; j++) {
|
|
// bottom right corner
|
|
if(i == n - 1 && j == n - 1) {
|
|
continue;
|
|
}
|
|
|
|
if (i == n - 1) {
|
|
// structural spring
|
|
spring_list[k] = Edge(n * i + j, n * i + j + 1);
|
|
rest_lengths[k] = rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
structI.push_back(k++);
|
|
|
|
// bending spring
|
|
if (j % 2 == 0) {
|
|
spring_list[k] = Edge(n * i + j, n * i + j + 2);
|
|
rest_lengths[k] = 2 * rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
bendI.push_back(k++);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// right edge
|
|
if (j == n - 1) {
|
|
// structural spring
|
|
spring_list[k] = Edge(n * i + j, n * (i + 1) + j);
|
|
rest_lengths[k] = rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
structI.push_back(k++);
|
|
|
|
// bending spring
|
|
if (i % 2 == 0){
|
|
spring_list[k] = Edge(n * i + j, n * (i + 2) + j);
|
|
rest_lengths[k] = 2 * rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
bendI.push_back(k++);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// structural springs
|
|
spring_list[k] = Edge(n * i + j, n * i + j + 1);
|
|
rest_lengths[k] = rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
structI.push_back(k++);
|
|
|
|
spring_list[k] = Edge(n * i + j, n * (i + 1) + j);
|
|
rest_lengths[k] = rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
structI.push_back(k++);
|
|
|
|
// shearing springs
|
|
spring_list[k] = Edge(n * i + j, n * (i + 1) + j + 1);
|
|
rest_lengths[k] = root2 * rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
shearI.push_back(k++);
|
|
|
|
spring_list[k] = Edge(n * (i + 1) + j, n * i + j + 1);
|
|
rest_lengths[k] = root2 * rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
shearI.push_back(k++);
|
|
|
|
// bending springs
|
|
if (j % 2 == 0) {
|
|
spring_list[k] = Edge(n * i + j, n * i + j + 2);
|
|
rest_lengths[k] = 2 * rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
bendI.push_back(k++);
|
|
}
|
|
if (i % 2 == 0) {
|
|
spring_list[k] = Edge(n * i + j, n * (i + 2) + j);
|
|
rest_lengths[k] = 2 * rest_length;
|
|
stiffnesses[k] = stiffness;
|
|
bendI.push_back(k++);
|
|
}
|
|
}
|
|
}
|
|
|
|
// compute external forces
|
|
VectorXf fext = Vector3f(0, 0, -gravity).replicate(n_points, 1);
|
|
|
|
result = new mass_spring_system(n_points, n_springs, time_step, spring_list, rest_lengths,
|
|
stiffnesses, masses, fext, damping_factor);
|
|
}
|
|
MassSpringBuilder::IndexList MassSpringBuilder::getStructIndex() { return structI; }
|
|
MassSpringBuilder::IndexList MassSpringBuilder::getShearIndex() { return shearI; }
|
|
MassSpringBuilder::IndexList MassSpringBuilder::getBendIndex() { return bendI; }
|
|
mass_spring_system* MassSpringBuilder::getResult() { return result; }
|
|
|
|
// C O N S T R A I N T //////////////////////////////////////////////////////////////////////////////
|
|
CgNode::CgNode(mass_spring_system* system, float* vbuff) : system(system), vbuff(vbuff) {}
|
|
|
|
// point node
|
|
CgPointNode::CgPointNode(mass_spring_system* system, float* vbuff) : CgNode(system, vbuff) {}
|
|
bool CgPointNode::accept(CgNodeVisitor& visitor) { return visitor.visit(*this); }
|
|
|
|
// spring node
|
|
CgSpringNode::CgSpringNode(mass_spring_system* system, float* vbuff) : CgNode(system, vbuff) {}
|
|
bool CgSpringNode::accept(CgNodeVisitor& visitor) {
|
|
for (CgNode* child : children) {
|
|
if (!child->accept(visitor)) return false;
|
|
}
|
|
return visitor.visit(*this);
|
|
}
|
|
void CgSpringNode::addChild(CgNode* node) { children.push_back(node); }
|
|
void CgSpringNode::removeChild(CgNode* node) {
|
|
children.erase(find(children.begin(), children.end(), node));
|
|
}
|
|
|
|
// root node
|
|
CgRootNode::CgRootNode(mass_spring_system* system, float* vbuff) : CgSpringNode(system, vbuff) {}
|
|
void CgRootNode::satisfy() { return; }
|
|
bool CgRootNode::accept(CgNodeVisitor& visitor) {
|
|
for (CgNode* child : children) {
|
|
if (!child->accept(visitor)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// point fix node
|
|
CgPointFixNode::CgPointFixNode(mass_spring_system* system, float* vbuff)
|
|
: CgPointNode(system, vbuff) {}
|
|
bool CgPointFixNode::query(unsigned int i) const { return fix_map.find(3 * i) != fix_map.end(); }
|
|
void CgPointFixNode::satisfy() {
|
|
for (auto fix : fix_map)
|
|
for (int i = 0; i < 3; i++)
|
|
vbuff[fix.first + i] = fix.second[i];
|
|
}
|
|
void CgPointFixNode::fixPoint(unsigned int i) {
|
|
assert(i >= 0 && i < system->n_points);
|
|
fix_map[3 * i] = Vector3f(vbuff[3 * i], vbuff[3 * i + 1], vbuff[3 * i + 2]);
|
|
}
|
|
void CgPointFixNode::releasePoint(unsigned int i) { fix_map.erase(3 * i); }
|
|
|
|
// spring deformation node
|
|
CgSpringDeformationNode::CgSpringDeformationNode(mass_spring_system* system, float* vbuff,
|
|
float tauc, unsigned int n_iter) : CgSpringNode(system, vbuff), tauc(tauc), n_iter(n_iter) {}
|
|
void CgSpringDeformationNode::satisfy() {
|
|
for (int k = 0; k < n_iter; k++) {
|
|
for (unsigned int i : items) {
|
|
Edge spring = system->spring_list[i];
|
|
CgQueryFixedPointVisitor visitor;
|
|
|
|
Vector3f p12(
|
|
vbuff[3 * spring.first + 0] - vbuff[3 * spring.second + 0],
|
|
vbuff[3 * spring.first + 1] - vbuff[3 * spring.second + 1],
|
|
vbuff[3 * spring.first + 2] - vbuff[3 * spring.second + 2]
|
|
);
|
|
|
|
float len = p12.norm();
|
|
float rlen = system->rest_lengths[i];
|
|
float diff = (len - (1 + tauc) * rlen) / len;
|
|
float rate = (len - rlen) / rlen;
|
|
|
|
// check deformation
|
|
if (rate <= tauc) continue;
|
|
|
|
// check if points are fixed
|
|
float f1, f2;
|
|
f1 = f2 = 0.5f;
|
|
|
|
// if first point is fixed
|
|
if (visitor.queryPoint(*this, spring.first)) { f1 = 0.0f; f2 = 1.0f; }
|
|
|
|
// if second point is fixed
|
|
if (visitor.queryPoint(*this, spring.second)) {
|
|
f1 = (f1 != 0.0f ? 1.0f : 0.0f);
|
|
f2 = 0.0f;
|
|
}
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
vbuff[3 * spring.first + j] -= p12[j] * f1 * diff;
|
|
vbuff[3 * spring.second + j] += p12[j] * f2 * diff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void CgSpringDeformationNode::addSprings(std::vector<unsigned int> springs) {
|
|
items.insert(springs.begin(), springs.end());
|
|
}
|
|
|
|
// sphere collision node
|
|
CgSphereCollisionNode::CgSphereCollisionNode(mass_spring_system* system, float* vbuff,
|
|
float radius, Vector3f center) : CgPointNode(system, vbuff), radius(radius), center(center) {}
|
|
bool CgSphereCollisionNode::query(unsigned int i) const { return false; }
|
|
void CgSphereCollisionNode::satisfy() {
|
|
for (int i = 0; i < system->n_points; i++) {
|
|
Vector3f p(
|
|
vbuff[3 * i + 0] - center[0],
|
|
vbuff[3 * i + 1] - center[1],
|
|
vbuff[3 * i + 2] - center[2]
|
|
);
|
|
|
|
if (p.norm() < radius) {
|
|
p.normalize();
|
|
p = radius * p;
|
|
}
|
|
else continue;
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
vbuff[3 * i + j] = p[j] + center[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
// node visitor
|
|
bool CgNodeVisitor::visit(CgPointNode& node) { return true; }
|
|
bool CgNodeVisitor::visit(CgSpringNode& node) { return true; }
|
|
|
|
// query fixed point visitor
|
|
bool CgQueryFixedPointVisitor::visit(CgPointNode& node) {
|
|
queryResult = node.query(i);
|
|
return !queryResult;
|
|
}
|
|
bool CgQueryFixedPointVisitor::queryPoint(CgNode& root, unsigned int i) {
|
|
this->i = i;
|
|
root.accept(*this);
|
|
return queryResult;
|
|
}
|
|
|
|
// satisfy visitor
|
|
bool CgSatisfyVisitor::visit(CgPointNode& node) { node.satisfy(); return true; }
|
|
bool CgSatisfyVisitor::visit(CgSpringNode& node) { node.satisfy(); return true; }
|
|
void CgSatisfyVisitor::satisfy(CgNode& root) { root.accept(*this); } |