dust3d/src/clothsimulator.cpp

291 lines
10 KiB
C++
Raw Normal View History

#include <MassSpringSolver.h>
#include <set>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <CGAL/algorithm.h>
#include <CGAL/Side_of_triangle_mesh.h>
#include "clothsimulator.h"
#include "booleanmesh.h"
typedef CGAL::Simple_cartesian<double> K;
typedef K::Point_3 Point;
typedef K::Triangle_3 Triangle;
typedef CGAL::Polyhedron_3<K> Polyhedron;
typedef Polyhedron::HalfedgeDS HalfedgeDS;
typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive;
typedef CGAL::AABB_traits<K, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;
typedef CGAL::Side_of_triangle_mesh<Polyhedron, K> Point_inside;
template <class HDS>
class Build_mesh : public CGAL::Modifier_base<HDS> {
public:
Build_mesh(const std::vector<QVector3D> *vertices,
const std::vector<std::vector<size_t>> *faces) :
m_vertices(vertices),
m_faces(faces)
{
};
void operator()(HDS& hds)
{
// Postcondition: hds is a valid polyhedral surface.
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, false);
B.begin_surface(m_vertices->size(), m_faces->size());
typedef typename HDS::Vertex Vertex;
typedef typename Vertex::Point Point;
for (const auto &it: *m_vertices)
B.add_vertex(Point(it.x(), it.y(), it.z()));
for (const auto &it: *m_faces) {
B.begin_facet();
B.add_vertex_to_facet(it[0]);
B.add_vertex_to_facet(it[1]);
B.add_vertex_to_facet(it[2]);
B.end_facet();
}
B.end_surface();
};
private:
const std::vector<QVector3D> *m_vertices = nullptr;
const std::vector<std::vector<size_t>> *m_faces = nullptr;
};
// System parameters
//namespace SystemParam {
// static const int n = 61; // must be odd, n * n = n_vertices | 61
// static const float h = 0.001f; // time step, smaller for better results | 0.008f = 0.016f/2
// 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
//}
// Point - mesh collision node
class CgMeshCollisionNode : public CgPointNode {
private:
Tree *m_aabbTree = nullptr;
Point_inside *m_insideTester = nullptr;
Polyhedron m_polyhedron;
public:
CgMeshCollisionNode(mass_spring_system *system, float *vbuff,
const std::vector<QVector3D> &collisionVertices,
const std::vector<std::vector<size_t>> &collisionTriangles) :
CgPointNode(system, vbuff)
{
if (!collisionTriangles.empty()) {
Build_mesh<HalfedgeDS> mesh(&collisionVertices, &collisionTriangles);
m_polyhedron.delegate(mesh);
m_aabbTree = new Tree(faces(m_polyhedron).first, faces(m_polyhedron).second, m_polyhedron);
m_aabbTree->accelerate_distance_queries();
m_insideTester = new Point_inside(*m_aabbTree);
}
}
~CgMeshCollisionNode()
{
delete m_insideTester;
delete m_aabbTree;
};
bool query(unsigned int i) const
{
return false;
};
void satisfy()
{
for (unsigned int i = 0; i < system->n_points; i++) {
auto offset = 3 * i;
Point point(vbuff[offset + 0],
vbuff[offset + 1],
vbuff[offset + 2]);
if (nullptr != m_insideTester &&
(*m_insideTester)(point) != CGAL::ON_UNBOUNDED_SIDE) {
Point closestPoint = m_aabbTree->closest_point(point);
vbuff[offset + 0] = closestPoint.x();
vbuff[offset + 1] = closestPoint.y();
vbuff[offset + 2] = closestPoint.z();
}
}
}
2020-01-09 14:22:33 +00:00
void fixPoints(CgPointFixNode *fixNode)
{
2020-01-09 14:22:33 +00:00
for (unsigned int i = 0; i < system->n_points; i++) {
auto offset = 3 * i;
Point point(vbuff[offset + 0],
vbuff[offset + 1],
vbuff[offset + 2]);
if (nullptr != m_insideTester &&
(*m_insideTester)(point) != CGAL::ON_UNBOUNDED_SIDE) {
fixNode->fixPoint(i);
}
}
}
void collectErrorPoints(std::vector<size_t> *points)
{
for (unsigned int i = 0; i < system->n_points; i++) {
auto offset = 3 * i;
Point point(vbuff[offset + 0],
vbuff[offset + 1],
vbuff[offset + 2]);
if (nullptr != m_insideTester &&
(*m_insideTester)(point) != CGAL::ON_UNBOUNDED_SIDE) {
points->push_back(i);
}
}
}
};
ClothSimulator::ClothSimulator(const std::vector<QVector3D> &vertices,
const std::vector<std::vector<size_t>> &faces,
const std::vector<QVector3D> &collisionVertices,
const std::vector<std::vector<size_t>> &collisionTriangles,
const std::vector<QVector3D> &externalForces) :
m_vertices(vertices),
m_faces(faces),
m_collisionVertices(collisionVertices),
m_collisionTriangles(collisionTriangles),
m_externalForces(externalForces)
{
}
ClothSimulator::~ClothSimulator()
{
delete m_massSpringSystem;
delete m_massSpringSolver;
delete m_rootNode;
delete m_deformationNode;
delete m_meshCollisionNode;
2020-01-09 14:22:33 +00:00
delete m_fixNode;
}
void ClothSimulator::setStiffness(float stiffness)
{
m_stiffness = 1.0f + 5.0f * stiffness;
}
void ClothSimulator::convertMeshToCloth()
{
m_clothPointSources.reserve(m_vertices.size());
m_clothPointBuffer.reserve(m_vertices.size() * 3);
std::map<size_t, size_t> oldVertexToNewMap;
auto addPoint = [&](size_t index) {
auto findNew = oldVertexToNewMap.find(index);
if (findNew != oldVertexToNewMap.end())
return findNew->second;
const auto &position = m_vertices[index];
m_clothPointBuffer.push_back(position.x());
m_clothPointBuffer.push_back(position.y());
m_clothPointBuffer.push_back(position.z());
size_t newIndex = m_clothPointSources.size();
m_clothPointSources.push_back(index);
oldVertexToNewMap.insert({index, newIndex});
return newIndex;
};
std::set<std::pair<size_t, size_t>> oldEdges;
for (const auto &it: m_faces) {
for (size_t i = 0; i < it.size(); ++i) {
size_t j = (i + 1) % it.size();
if (oldEdges.find(std::make_pair(it[i], it[j])) != oldEdges.end())
continue;
m_clothSprings.push_back({addPoint(it[i]), addPoint(it[j])});
oldEdges.insert({it[i], it[j]});
oldEdges.insert({it[j], it[i]});
}
}
}
void ClothSimulator::getCurrentVertices(std::vector<QVector3D> *currentVertices)
{
*currentVertices = m_vertices;
for (size_t newIndex = 0; newIndex < m_clothPointSources.size(); ++newIndex) {
size_t oldIndex = m_clothPointSources[newIndex];
auto offset = newIndex * 3;
(*currentVertices)[oldIndex] = QVector3D(m_clothPointBuffer[offset + 0],
m_clothPointBuffer[offset + 1],
m_clothPointBuffer[offset + 2]);
}
}
void ClothSimulator::step()
{
if (nullptr == m_massSpringSolver)
return;
m_massSpringSolver->solve(5);
m_massSpringSolver->solve(5);
CgSatisfyVisitor visitor;
visitor.satisfy(*m_rootNode);
}
void ClothSimulator::create()
{
convertMeshToCloth();
if (m_clothPointSources.empty())
return;
float mass = 0.25f / m_clothPointSources.size();
float gravitationalForce = 9.8f * mass;
float damping = 0.993f; // damping, close to 1.0 | 0.993f;
float timeStep = 0.001f; //smaller for better results | 0.008f = 0.016f/2;
mass_spring_system::VectorXf masses(mass * mass_spring_system::VectorXf::Ones((unsigned int)m_clothSprings.size()));
mass_spring_system::EdgeList springList(m_clothSprings.size());
mass_spring_system::VectorXf restLengths(m_clothSprings.size());
mass_spring_system::VectorXf stiffnesses(m_clothSprings.size());
for (size_t i = 0; i < m_clothSprings.size(); ++i) {
const auto &source = m_clothSprings[i];
springList[i] = mass_spring_system::Edge(source.first, source.second);
restLengths[i] = (m_vertices[m_clothPointSources[source.first]] - m_vertices[m_clothPointSources[source.second]]).length() * 0.8;
stiffnesses[i] = m_stiffness;
}
mass_spring_system::VectorXf fext(m_clothPointSources.size() * 3);
for (size_t i = 0; i < m_clothPointSources.size(); ++i) {
const auto &externalForce = m_externalForces[i] * gravitationalForce;
auto offset = i * 3;
fext[offset + 0] = externalForce.x();
fext[offset + 1] = externalForce.y();
fext[offset + 2] = externalForce.z();
}
m_massSpringSystem = new mass_spring_system(m_clothPointSources.size(), m_clothSprings.size(), timeStep, springList, restLengths,
stiffnesses, masses, fext, damping);
m_massSpringSolver = new MassSpringSolver(m_massSpringSystem, m_clothPointBuffer.data());
// deformation constraint parameters
const float tauc = 0.12f; // critical spring deformation | 0.12f
const unsigned int deformIter = 15; // number of iterations | 15
std::vector<unsigned int> structSprintIndexList;
structSprintIndexList.reserve(springList.size());
m_deformationNode =
new CgSpringDeformationNode(m_massSpringSystem, m_clothPointBuffer.data(), tauc, deformIter);
m_deformationNode->addSprings(structSprintIndexList);
m_rootNode = new CgRootNode(m_massSpringSystem, m_clothPointBuffer.data());
m_rootNode->addChild(m_deformationNode);
2020-01-09 14:22:33 +00:00
m_meshCollisionNode = new CgMeshCollisionNode(m_massSpringSystem, m_clothPointBuffer.data(),
m_collisionVertices,
m_collisionTriangles);
2020-01-09 14:22:33 +00:00
m_fixNode = new CgPointFixNode(m_massSpringSystem, m_clothPointBuffer.data());
m_meshCollisionNode->fixPoints(m_fixNode);
m_deformationNode->addChild(m_fixNode);
m_rootNode->addChild(m_meshCollisionNode);
}