Add cloth simulation
Integrate FastMassSpring, implemented by Samer Itani(@sam007961), Original paper: "Fast Simulation of Mass-Spring Systems" by Liu, T., Bargteil, A. W., Obrien, J. F., & Kavan, L.master
parent
56f0743845
commit
579fa24e1c
|
@ -1271,4 +1271,29 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode
|
||||||
license to install, use, modify, prepare derivative works, incorporate into
|
license to install, use, modify, prepare derivative works, incorporate into
|
||||||
other computer software, distribute, and sublicense such enhancements or
|
other computer software, distribute, and sublicense such enhancements or
|
||||||
derivative works thereof, in binary and source code form.
|
derivative works thereof, in binary and source code form.
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h1>FastMassSpring</h1>
|
||||||
|
<pre>
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Samer Itani
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
</pre>
|
</pre>
|
10
dust3d.pro
10
dust3d.pro
|
@ -482,10 +482,20 @@ HEADERS += src/contourtopartconverter.h
|
||||||
SOURCES += src/remesher.cpp
|
SOURCES += src/remesher.cpp
|
||||||
HEADERS += src/remesher.h
|
HEADERS += src/remesher.h
|
||||||
|
|
||||||
|
SOURCES += src/clothsimulator.cpp
|
||||||
|
HEADERS += src/clothsimulator.h
|
||||||
|
|
||||||
|
SOURCES += src/componentlayer.cpp
|
||||||
|
HEADERS += src/componentlayer.h
|
||||||
|
|
||||||
SOURCES += src/main.cpp
|
SOURCES += src/main.cpp
|
||||||
|
|
||||||
HEADERS += src/version.h
|
HEADERS += src/version.h
|
||||||
|
|
||||||
|
INCLUDEPATH += thirdparty/FastMassSpring/ClothApp
|
||||||
|
SOURCES += thirdparty/FastMassSpring/ClothApp/MassSpringSolver.cpp
|
||||||
|
HEADERS += thirdparty/FastMassSpring/ClothApp/MassSpringSolver.h
|
||||||
|
|
||||||
INCLUDEPATH += thirdparty/instant-meshes
|
INCLUDEPATH += thirdparty/instant-meshes
|
||||||
INCLUDEPATH += thirdparty/instant-meshes/instant-meshes-dust3d/src
|
INCLUDEPATH += thirdparty/instant-meshes/instant-meshes-dust3d/src
|
||||||
INCLUDEPATH += thirdparty/instant-meshes/instant-meshes-dust3d/ext/tbb/include
|
INCLUDEPATH += thirdparty/instant-meshes/instant-meshes-dust3d/ext/tbb/include
|
||||||
|
|
|
@ -748,6 +748,10 @@ Tips:
|
||||||
<source>Poly</source>
|
<source>Poly</source>
|
||||||
<translation>面数</translation>
|
<translation>面数</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Layer</source>
|
||||||
|
<translation>层</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PartWidget</name>
|
<name>PartWidget</name>
|
||||||
|
@ -1070,6 +1074,14 @@ Tips:
|
||||||
<source>Extreme High Poly</source>
|
<source>Extreme High Poly</source>
|
||||||
<translation>极高面</translation>
|
<translation>极高面</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Body</source>
|
||||||
|
<translation>身体</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cloth</source>
|
||||||
|
<translation>衣服</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>RigWidget</name>
|
<name>RigWidget</name>
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
#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 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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) :
|
||||||
|
m_vertices(vertices),
|
||||||
|
m_faces(faces),
|
||||||
|
m_collisionVertices(collisionVertices),
|
||||||
|
m_collisionTriangles(collisionTriangles)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ClothSimulator::~ClothSimulator()
|
||||||
|
{
|
||||||
|
delete m_massSpringSystem;
|
||||||
|
delete m_massSpringSolver;
|
||||||
|
delete m_rootNode;
|
||||||
|
delete m_deformationNode;
|
||||||
|
delete m_meshCollisionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
mass_spring_system::VectorXf masses(SystemParam::m * 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();
|
||||||
|
stiffnesses[i] = SystemParam::k;
|
||||||
|
}
|
||||||
|
|
||||||
|
mass_spring_system::VectorXf fext = Eigen::Vector3f(0, -SystemParam::g, 0).replicate(m_clothPointSources.size(), 1);
|
||||||
|
|
||||||
|
m_massSpringSystem = new mass_spring_system(m_clothPointSources.size(), m_clothSprings.size(), SystemParam::h, springList, restLengths,
|
||||||
|
stiffnesses, masses, fext, SystemParam::a);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
m_meshCollisionNode = new CgMeshCollisionNode(m_massSpringSystem, m_clothPointBuffer.data(),
|
||||||
|
m_collisionVertices,
|
||||||
|
m_collisionTriangles);
|
||||||
|
m_rootNode->addChild(m_meshCollisionNode);
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef DUST3D_CLOTH_SIMULATOR_H
|
||||||
|
#define DUST3D_CLOTH_SIMULATOR_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVector3D>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct mass_spring_system;
|
||||||
|
class MassSpringSolver;
|
||||||
|
class CgRootNode;
|
||||||
|
class CgSpringDeformationNode;
|
||||||
|
class CgMeshCollisionNode;
|
||||||
|
|
||||||
|
class ClothSimulator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
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);
|
||||||
|
~ClothSimulator();
|
||||||
|
void create();
|
||||||
|
void step();
|
||||||
|
void getCurrentVertices(std::vector<QVector3D> *currentVertices);
|
||||||
|
private:
|
||||||
|
std::vector<QVector3D> m_vertices;
|
||||||
|
std::vector<std::vector<size_t>> m_faces;
|
||||||
|
std::vector<QVector3D> m_collisionVertices;
|
||||||
|
std::vector<std::vector<size_t>> m_collisionTriangles;
|
||||||
|
std::vector<float> m_clothPointBuffer;
|
||||||
|
std::vector<size_t> m_clothPointSources;
|
||||||
|
std::vector<std::pair<size_t, size_t>> m_clothSprings;
|
||||||
|
mass_spring_system *m_massSpringSystem = nullptr;
|
||||||
|
MassSpringSolver *m_massSpringSolver = nullptr;
|
||||||
|
CgRootNode *m_rootNode = nullptr;
|
||||||
|
CgSpringDeformationNode *m_deformationNode = nullptr;
|
||||||
|
CgMeshCollisionNode *m_meshCollisionNode = nullptr;
|
||||||
|
void convertMeshToCloth();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <QObject>
|
||||||
|
#include "componentlayer.h"
|
||||||
|
|
||||||
|
IMPL_ComponentLayerToString
|
||||||
|
IMPL_ComponentLayerFromString
|
||||||
|
IMPL_ComponentLayerToDispName
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef DUST3D_COMPONENT_LAYER_H
|
||||||
|
#define DUST3D_COMPONENT_LAYER_H
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
enum class ComponentLayer
|
||||||
|
{
|
||||||
|
Body = 0,
|
||||||
|
Cloth,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
ComponentLayer ComponentLayerFromString(const char *layerString);
|
||||||
|
#define IMPL_ComponentLayerFromString \
|
||||||
|
ComponentLayer ComponentLayerFromString(const char *layerString) \
|
||||||
|
{ \
|
||||||
|
QString layer = layerString; \
|
||||||
|
if (layer == "Body") \
|
||||||
|
return ComponentLayer::Body; \
|
||||||
|
if (layer == "Cloth") \
|
||||||
|
return ComponentLayer::Cloth; \
|
||||||
|
return ComponentLayer::Body; \
|
||||||
|
}
|
||||||
|
const char *ComponentLayerToString(ComponentLayer layer);
|
||||||
|
#define IMPL_ComponentLayerToString \
|
||||||
|
const char *ComponentLayerToString(ComponentLayer layer) \
|
||||||
|
{ \
|
||||||
|
switch (layer) { \
|
||||||
|
case ComponentLayer::Body: \
|
||||||
|
return "Body"; \
|
||||||
|
case ComponentLayer::Cloth: \
|
||||||
|
return "Cloth"; \
|
||||||
|
default: \
|
||||||
|
return "Body"; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
QString ComponentLayerToDispName(ComponentLayer layer);
|
||||||
|
#define IMPL_ComponentLayerToDispName \
|
||||||
|
QString ComponentLayerToDispName(ComponentLayer layer) \
|
||||||
|
{ \
|
||||||
|
switch (layer) { \
|
||||||
|
case ComponentLayer::Body: \
|
||||||
|
return QObject::tr("Body"); \
|
||||||
|
case ComponentLayer::Cloth: \
|
||||||
|
return QObject::tr("Cloth"); \
|
||||||
|
default: \
|
||||||
|
return QObject::tr("Body"); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1201,6 +1201,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
|
||||||
component["smoothSeam"] = QString::number(componentIt.second.smoothSeam);
|
component["smoothSeam"] = QString::number(componentIt.second.smoothSeam);
|
||||||
if (componentIt.second.polyCount != PolyCount::Original)
|
if (componentIt.second.polyCount != PolyCount::Original)
|
||||||
component["polyCount"] = PolyCountToString(componentIt.second.polyCount);
|
component["polyCount"] = PolyCountToString(componentIt.second.polyCount);
|
||||||
|
if (componentIt.second.layer != ComponentLayer::Body)
|
||||||
|
component["layer"] = ComponentLayerToString(componentIt.second.layer);
|
||||||
QStringList childIdList;
|
QStringList childIdList;
|
||||||
for (const auto &childId: componentIt.second.childrenIds) {
|
for (const auto &childId: componentIt.second.childrenIds) {
|
||||||
childIdList.append(childId.toString());
|
childIdList.append(childId.toString());
|
||||||
|
@ -1708,6 +1710,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
|
||||||
if (smoothSeamIt != componentKv.second.end())
|
if (smoothSeamIt != componentKv.second.end())
|
||||||
component.setSmoothSeam(smoothSeamIt->second.toFloat());
|
component.setSmoothSeam(smoothSeamIt->second.toFloat());
|
||||||
component.polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(componentKv.second, "polyCount").toUtf8().constData());
|
component.polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(componentKv.second, "polyCount").toUtf8().constData());
|
||||||
|
component.layer = ComponentLayerFromString(valueOfKeyInMapOrEmpty(componentKv.second, "layer").toUtf8().constData());
|
||||||
//qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name;
|
//qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name;
|
||||||
if ("partId" == linkDataType) {
|
if ("partId" == linkDataType) {
|
||||||
QUuid partId = oldNewIdMap[QUuid(linkData)];
|
QUuid partId = oldNewIdMap[QUuid(linkData)];
|
||||||
|
@ -2497,6 +2500,20 @@ void Document::setComponentPolyCount(QUuid componentId, PolyCount count)
|
||||||
emit skeletonChanged();
|
emit skeletonChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::setComponentLayer(QUuid componentId, ComponentLayer layer)
|
||||||
|
{
|
||||||
|
Component *component = (Component *)findComponent(componentId);
|
||||||
|
if (nullptr == component)
|
||||||
|
return;
|
||||||
|
if (component->layer == layer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component->layer = layer;
|
||||||
|
component->dirty = true;
|
||||||
|
emit componentLayerChanged(componentId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void Document::createNewComponentAndMoveThisIn(QUuid componentId)
|
void Document::createNewComponentAndMoveThisIn(QUuid componentId)
|
||||||
{
|
{
|
||||||
auto component = componentMap.find(componentId);
|
auto component = componentMap.find(componentId);
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "paintmode.h"
|
#include "paintmode.h"
|
||||||
#include "proceduralanimation.h"
|
#include "proceduralanimation.h"
|
||||||
|
#include "componentlayer.h"
|
||||||
|
|
||||||
class MaterialPreviewsGenerator;
|
class MaterialPreviewsGenerator;
|
||||||
class MotionsGenerator;
|
class MotionsGenerator;
|
||||||
|
@ -67,6 +68,7 @@ public:
|
||||||
float smoothAll = 0.0;
|
float smoothAll = 0.0;
|
||||||
float smoothSeam = 0.0;
|
float smoothSeam = 0.0;
|
||||||
PolyCount polyCount = PolyCount::Original;
|
PolyCount polyCount = PolyCount::Original;
|
||||||
|
ComponentLayer layer = ComponentLayer::Body;
|
||||||
std::vector<QUuid> childrenIds;
|
std::vector<QUuid> childrenIds;
|
||||||
QString linkData() const
|
QString linkData() const
|
||||||
{
|
{
|
||||||
|
@ -396,6 +398,7 @@ signals:
|
||||||
void componentSmoothAllChanged(QUuid componentId);
|
void componentSmoothAllChanged(QUuid componentId);
|
||||||
void componentSmoothSeamChanged(QUuid componentId);
|
void componentSmoothSeamChanged(QUuid componentId);
|
||||||
void componentPolyCountChanged(QUuid componentId);
|
void componentPolyCountChanged(QUuid componentId);
|
||||||
|
void componentLayerChanged(QUuid componentId);
|
||||||
void nodeRemoved(QUuid nodeId);
|
void nodeRemoved(QUuid nodeId);
|
||||||
void edgeRemoved(QUuid edgeId);
|
void edgeRemoved(QUuid edgeId);
|
||||||
void nodeRadiusChanged(QUuid nodeId);
|
void nodeRadiusChanged(QUuid nodeId);
|
||||||
|
@ -642,6 +645,7 @@ public slots:
|
||||||
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
|
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
|
||||||
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
||||||
void setComponentPolyCount(QUuid componentId, PolyCount count);
|
void setComponentPolyCount(QUuid componentId, PolyCount count);
|
||||||
|
void setComponentLayer(QUuid componentId, ComponentLayer layer);
|
||||||
void hideOtherComponents(QUuid componentId);
|
void hideOtherComponents(QUuid componentId);
|
||||||
void lockOtherComponents(QUuid componentId);
|
void lockOtherComponents(QUuid componentId);
|
||||||
void hideAllComponents();
|
void hideAllComponents();
|
||||||
|
|
|
@ -1014,6 +1014,7 @@ DocumentWindow::DocumentWindow() :
|
||||||
connect(partTreeWidget, &PartTreeWidget::setComponentSmoothAll, m_document, &Document::setComponentSmoothAll);
|
connect(partTreeWidget, &PartTreeWidget::setComponentSmoothAll, m_document, &Document::setComponentSmoothAll);
|
||||||
connect(partTreeWidget, &PartTreeWidget::setComponentSmoothSeam, m_document, &Document::setComponentSmoothSeam);
|
connect(partTreeWidget, &PartTreeWidget::setComponentSmoothSeam, m_document, &Document::setComponentSmoothSeam);
|
||||||
connect(partTreeWidget, &PartTreeWidget::setComponentPolyCount, m_document, &Document::setComponentPolyCount);
|
connect(partTreeWidget, &PartTreeWidget::setComponentPolyCount, m_document, &Document::setComponentPolyCount);
|
||||||
|
connect(partTreeWidget, &PartTreeWidget::setComponentLayer, m_document, &Document::setComponentLayer);
|
||||||
connect(partTreeWidget, &PartTreeWidget::moveComponent, m_document, &Document::moveComponent);
|
connect(partTreeWidget, &PartTreeWidget::moveComponent, m_document, &Document::moveComponent);
|
||||||
connect(partTreeWidget, &PartTreeWidget::removeComponent, m_document, &Document::removeComponent);
|
connect(partTreeWidget, &PartTreeWidget::removeComponent, m_document, &Document::removeComponent);
|
||||||
connect(partTreeWidget, &PartTreeWidget::hideOtherComponents, m_document, &Document::hideOtherComponents);
|
connect(partTreeWidget, &PartTreeWidget::hideOtherComponents, m_document, &Document::hideOtherComponents);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "triangulatefaces.h"
|
#include "triangulatefaces.h"
|
||||||
#include "remesher.h"
|
#include "remesher.h"
|
||||||
#include "polycount.h"
|
#include "polycount.h"
|
||||||
|
#include "clothsimulator.h"
|
||||||
|
|
||||||
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
||||||
m_snapshot(snapshot)
|
m_snapshot(snapshot)
|
||||||
|
@ -840,6 +841,11 @@ CombineMode MeshGenerator::componentCombineMode(const std::map<QString, QString>
|
||||||
combineMode = CombineMode::Inversion;
|
combineMode = CombineMode::Inversion;
|
||||||
if (componentRemeshed(component))
|
if (componentRemeshed(component))
|
||||||
combineMode = CombineMode::Uncombined;
|
combineMode = CombineMode::Uncombined;
|
||||||
|
if (combineMode == CombineMode::Normal) {
|
||||||
|
if (ComponentLayer::Body != ComponentLayerFromString(valueOfKeyInMapOrEmpty(*component, "layer").toUtf8().constData())) {
|
||||||
|
combineMode = CombineMode::Uncombined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return combineMode;
|
return combineMode;
|
||||||
}
|
}
|
||||||
|
@ -879,6 +885,13 @@ QString MeshGenerator::componentColorName(const std::map<QString, QString> *comp
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ComponentLayer MeshGenerator::componentLayer(const std::map<QString, QString> *component)
|
||||||
|
{
|
||||||
|
if (nullptr == component)
|
||||||
|
return ComponentLayer::Body;
|
||||||
|
return ComponentLayerFromString(valueOfKeyInMapOrEmpty(*component, "layer").toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &componentIdString, CombineMode *combineMode)
|
MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &componentIdString, CombineMode *combineMode)
|
||||||
{
|
{
|
||||||
MeshCombiner::Mesh *mesh = nullptr;
|
MeshCombiner::Mesh *mesh = nullptr;
|
||||||
|
@ -1035,6 +1048,16 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component
|
||||||
mesh = nullptr;
|
mesh = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (componentId.isNull()) {
|
||||||
|
// Prepare cloth collision shap
|
||||||
|
if (nullptr != mesh && !mesh->isNull()) {
|
||||||
|
mesh->fetch(m_clothCollisionVertices, m_clothCollisionTriangles);
|
||||||
|
} else {
|
||||||
|
// TODO: when no body is valid, may add ground plane as collision shape
|
||||||
|
// ... ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nullptr != mesh) {
|
if (nullptr != mesh) {
|
||||||
float polyCountValue = 1.0f;
|
float polyCountValue = 1.0f;
|
||||||
bool remeshed = componentId.isNull() ? componentRemeshed(&m_snapshot->canvas, &polyCountValue) : componentRemeshed(component, &polyCountValue);
|
bool remeshed = componentId.isNull() ? componentRemeshed(&m_snapshot->canvas, &polyCountValue) : componentRemeshed(component, &polyCountValue);
|
||||||
|
@ -1386,6 +1409,7 @@ void MeshGenerator::generate()
|
||||||
|
|
||||||
// Recursively check uncombined components
|
// Recursively check uncombined components
|
||||||
collectUncombinedComponent(QUuid().toString());
|
collectUncombinedComponent(QUuid().toString());
|
||||||
|
collectClothComponent(QUuid().toString());
|
||||||
|
|
||||||
// Collect errored parts
|
// Collect errored parts
|
||||||
for (const auto &it: m_cacheContext->parts) {
|
for (const auto &it: m_cacheContext->parts) {
|
||||||
|
@ -1514,6 +1538,8 @@ void MeshGenerator::collectUncombinedComponent(const QString &componentIdString)
|
||||||
{
|
{
|
||||||
const auto &component = findComponent(componentIdString);
|
const auto &component = findComponent(componentIdString);
|
||||||
if (CombineMode::Uncombined == componentCombineMode(component)) {
|
if (CombineMode::Uncombined == componentCombineMode(component)) {
|
||||||
|
if (ComponentLayer::Body != componentLayer(component))
|
||||||
|
return;
|
||||||
const auto &componentCache = m_cacheContext->components[componentIdString];
|
const auto &componentCache = m_cacheContext->components[componentIdString];
|
||||||
if (nullptr == componentCache.mesh || componentCache.mesh->isNull()) {
|
if (nullptr == componentCache.mesh || componentCache.mesh->isNull()) {
|
||||||
qDebug() << "Uncombined mesh is null";
|
qDebug() << "Uncombined mesh is null";
|
||||||
|
@ -1553,6 +1579,71 @@ void MeshGenerator::collectUncombinedComponent(const QString &componentIdString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
||||||
|
{
|
||||||
|
const auto &component = findComponent(componentIdString);
|
||||||
|
if (ComponentLayer::Cloth == componentLayer(component)) {
|
||||||
|
const auto &componentCache = m_cacheContext->components[componentIdString];
|
||||||
|
if (nullptr == componentCache.mesh || componentCache.mesh->isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_clothCollisionTriangles.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::vector<QVector3D> uncombinedVertices;
|
||||||
|
std::vector<std::vector<size_t>> uncombinedFaces;
|
||||||
|
componentCache.mesh->fetch(uncombinedVertices, uncombinedFaces);
|
||||||
|
|
||||||
|
std::map<PositionKey, std::pair<QUuid, QUuid>> positionMap;
|
||||||
|
for (const auto &it: componentCache.outcomeNodeVertices) {
|
||||||
|
positionMap.insert({PositionKey(it.first), it.second});
|
||||||
|
}
|
||||||
|
std::vector<std::pair<QUuid, QUuid>> uncombinedVertexSources(uncombinedVertices.size());
|
||||||
|
for (size_t i = 0; i < uncombinedVertices.size(); ++i) {
|
||||||
|
auto findSource = positionMap.find(PositionKey(uncombinedVertices[i]));
|
||||||
|
if (findSource == positionMap.end())
|
||||||
|
continue;
|
||||||
|
uncombinedVertexSources[i] = findSource->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClothSimulator clothSimulator(uncombinedVertices,
|
||||||
|
uncombinedFaces,
|
||||||
|
m_clothCollisionVertices,
|
||||||
|
m_clothCollisionTriangles);
|
||||||
|
clothSimulator.create();
|
||||||
|
for (size_t i = 0; i < 30; ++i)
|
||||||
|
clothSimulator.step();
|
||||||
|
clothSimulator.getCurrentVertices(&uncombinedVertices);
|
||||||
|
|
||||||
|
auto vertexStartIndex = m_outcome->vertices.size();
|
||||||
|
auto updateVertexIndices = [=](std::vector<std::vector<size_t>> &faces) {
|
||||||
|
for (auto &it: faces) {
|
||||||
|
for (auto &subIt: it)
|
||||||
|
subIt += vertexStartIndex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updateVertexIndices(uncombinedFaces);
|
||||||
|
|
||||||
|
m_outcome->vertices.insert(m_outcome->vertices.end(), uncombinedVertices.begin(), uncombinedVertices.end());
|
||||||
|
m_outcome->triangles.insert(m_outcome->triangles.end(), uncombinedFaces.begin(), uncombinedFaces.end());
|
||||||
|
m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), uncombinedFaces.begin(), uncombinedFaces.end());
|
||||||
|
|
||||||
|
m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end());
|
||||||
|
for (size_t i = 0; i < uncombinedVertices.size(); ++i) {
|
||||||
|
const auto &source = uncombinedVertexSources[i];
|
||||||
|
if (source.first.isNull())
|
||||||
|
continue;
|
||||||
|
m_outcome->nodeVertices.push_back(std::make_pair(uncombinedVertices[i], source));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
||||||
|
if (childIdString.isEmpty())
|
||||||
|
continue;
|
||||||
|
collectClothComponent(childIdString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MeshGenerator::generateSmoothTriangleVertexNormals(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles,
|
void MeshGenerator::generateSmoothTriangleVertexNormals(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles,
|
||||||
const std::vector<QVector3D> &triangleNormals,
|
const std::vector<QVector3D> &triangleNormals,
|
||||||
std::vector<std::vector<QVector3D>> *triangleVertexNormals)
|
std::vector<std::vector<QVector3D>> *triangleVertexNormals)
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "snapshot.h"
|
#include "snapshot.h"
|
||||||
#include "combinemode.h"
|
#include "combinemode.h"
|
||||||
#include "meshloader.h"
|
#include "meshloader.h"
|
||||||
|
#include "componentlayer.h"
|
||||||
|
|
||||||
class GeneratedPart
|
class GeneratedPart
|
||||||
{
|
{
|
||||||
|
@ -100,6 +101,8 @@ private:
|
||||||
std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *m_cutFaceTransforms = nullptr;
|
std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *m_cutFaceTransforms = nullptr;
|
||||||
std::map<QUuid, std::map<QString, QVector2D>> *m_nodesCutFaces = nullptr;
|
std::map<QUuid, std::map<QString, QVector2D>> *m_nodesCutFaces = nullptr;
|
||||||
quint64 m_id = 0;
|
quint64 m_id = 0;
|
||||||
|
std::vector<QVector3D> m_clothCollisionVertices;
|
||||||
|
std::vector<std::vector<size_t>> m_clothCollisionTriangles;
|
||||||
|
|
||||||
void collectParts();
|
void collectParts();
|
||||||
bool checkIsComponentDirty(const QString &componentIdString);
|
bool checkIsComponentDirty(const QString &componentIdString);
|
||||||
|
@ -125,7 +128,9 @@ private:
|
||||||
GeneratedComponent &componentCache);
|
GeneratedComponent &componentCache);
|
||||||
MeshCombiner::Mesh *combineMultipleMeshes(const std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, QString>> &multipleMeshes, bool recombine=true);
|
MeshCombiner::Mesh *combineMultipleMeshes(const std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, QString>> &multipleMeshes, bool recombine=true);
|
||||||
QString componentColorName(const std::map<QString, QString> *component);
|
QString componentColorName(const std::map<QString, QString> *component);
|
||||||
|
ComponentLayer componentLayer(const std::map<QString, QString> *component);
|
||||||
void collectUncombinedComponent(const QString &componentIdString);
|
void collectUncombinedComponent(const QString &componentIdString);
|
||||||
|
void collectClothComponent(const QString &componentIdString);
|
||||||
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
||||||
void remesh(const std::vector<OutcomeNode> &inputNodes,
|
void remesh(const std::vector<OutcomeNode> &inputNodes,
|
||||||
const std::vector<QVector3D> &inputVertices,
|
const std::vector<QVector3D> &inputVertices,
|
||||||
|
|
|
@ -315,6 +315,17 @@ void PartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
}
|
}
|
||||||
QWidget *widget = new QWidget;
|
QWidget *widget = new QWidget;
|
||||||
if (nullptr != component) {
|
if (nullptr != component) {
|
||||||
|
QComboBox *componentLayerSelectBox = new QComboBox;
|
||||||
|
for (size_t i = 0; i < (size_t)ComponentLayer::Count; ++i) {
|
||||||
|
ComponentLayer layer = (ComponentLayer)i;
|
||||||
|
componentLayerSelectBox->addItem(ComponentLayerToDispName(layer));
|
||||||
|
}
|
||||||
|
componentLayerSelectBox->setCurrentIndex((int)component->layer);
|
||||||
|
connect(componentLayerSelectBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=](int index) {
|
||||||
|
emit setComponentLayer(component->id, (ComponentLayer)index);
|
||||||
|
emit groupOperationAdded();
|
||||||
|
});
|
||||||
|
|
||||||
QComboBox *combineModeSelectBox = new QComboBox;
|
QComboBox *combineModeSelectBox = new QComboBox;
|
||||||
for (size_t i = 0; i < (size_t)CombineMode::Count; ++i) {
|
for (size_t i = 0; i < (size_t)CombineMode::Count; ++i) {
|
||||||
CombineMode mode = (CombineMode)i;
|
CombineMode mode = (CombineMode)i;
|
||||||
|
@ -368,6 +379,7 @@ void PartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
if (nullptr != partTargetSelectBox)
|
if (nullptr != partTargetSelectBox)
|
||||||
componentSettingsLayout->addRow(tr("Target"), partTargetSelectBox);
|
componentSettingsLayout->addRow(tr("Target"), partTargetSelectBox);
|
||||||
componentSettingsLayout->addRow(tr("Mode"), combineModeSelectBox);
|
componentSettingsLayout->addRow(tr("Mode"), combineModeSelectBox);
|
||||||
|
componentSettingsLayout->addRow(tr("Layer"), componentLayerSelectBox);
|
||||||
|
|
||||||
QVBoxLayout *newLayout = new QVBoxLayout;
|
QVBoxLayout *newLayout = new QVBoxLayout;
|
||||||
newLayout->addLayout(layout);
|
newLayout->addLayout(layout);
|
||||||
|
|
|
@ -23,6 +23,7 @@ signals:
|
||||||
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
|
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
|
||||||
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
||||||
void setComponentPolyCount(QUuid componentId, PolyCount count);
|
void setComponentPolyCount(QUuid componentId, PolyCount count);
|
||||||
|
void setComponentLayer(QUuid componentId, ComponentLayer layer);
|
||||||
void setPartTarget(QUuid partId, PartTarget target);
|
void setPartTarget(QUuid partId, PartTarget target);
|
||||||
void setPartBase(QUuid partId, PartBase base);
|
void setPartBase(QUuid partId, PartBase base);
|
||||||
void moveComponent(QUuid componentId, QUuid toParentId);
|
void moveComponent(QUuid componentId, QUuid toParentId);
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
|
@ -0,0 +1,242 @@
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
[Xx]64/
|
||||||
|
[Xx]86/
|
||||||
|
[Bb]uild/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
|
||||||
|
# TODO: Un-comment the next line if you do not want to checkin
|
||||||
|
# your web deploy settings because they may include unencrypted
|
||||||
|
# passwords
|
||||||
|
#*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignoreable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# LightSwitch generated files
|
||||||
|
GeneratedArtifacts/
|
||||||
|
ModelManifest.xml
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
|
@ -0,0 +1,204 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{bbca6691-6c35-4bfe-9cb9-13f16d29990f}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>ClothApp</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<IncludePath>$(SolutionDir)packages\nupengl.core.0.1.0.1\build\native\include;$(IncludePath)</IncludePath>
|
||||||
|
<Linkage-freeglut>static</Linkage-freeglut>
|
||||||
|
<Linkage-glew>static</Linkage-glew>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<IncludePath>C:\Program Files\Eigen\include;C:\Program Files\OpenMesh 7.1\include;$(IncludePath)</IncludePath>
|
||||||
|
<LibraryPath>C:\Program Files\OpenMesh 7.1\lib;$(LibraryPath)</LibraryPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<IncludePath>$(SolutionDir)packages\nupengl.core.0.1.0.1\build\native\include;$(IncludePath)</IncludePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<IncludePath>C:\Program Files\Eigen\include;C:\Program Files\OpenMesh 7.1\include;$(IncludePath)</IncludePath>
|
||||||
|
<LibraryPath>C:\Program Files\OpenMesh 7.1\lib;$(LibraryPath)</LibraryPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>OpenMeshCored.lib;OpenMeshToolsd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>_USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS;NDEBUG</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>OpenMeshCore.lib;OpenMeshTools.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="shaders\phong.fshader">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
|
</None>
|
||||||
|
<None Include="shaders\pick.fshader">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="App.cpp" />
|
||||||
|
<ClCompile Include="MassSpringSolver.cpp" />
|
||||||
|
<ClCompile Include="Mesh.cpp" />
|
||||||
|
<ClCompile Include="Renderer.cpp" />
|
||||||
|
<ClCompile Include="Shader.cpp" />
|
||||||
|
<ClCompile Include="UserInteraction.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<FxCompile Include="shaders\basic.vshader">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="MassSpringSolver.h" />
|
||||||
|
<ClInclude Include="Mesh.h" />
|
||||||
|
<ClInclude Include="Renderer.h" />
|
||||||
|
<ClInclude Include="Shader.h" />
|
||||||
|
<ClInclude Include="UserInteraction.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets" Condition="Exists('..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets')" />
|
||||||
|
<Import Project="..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets" Condition="Exists('..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Shader Files">
|
||||||
|
<UniqueIdentifier>{55e50294-8ec8-4842-bc08-005ce41662d4}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="shaders\phong.fshader">
|
||||||
|
<Filter>Shader Files</Filter>
|
||||||
|
</None>
|
||||||
|
<None Include="shaders\pick.fshader">
|
||||||
|
<Filter>Shader Files</Filter>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="App.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Renderer.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Mesh.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="MassSpringSolver.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInteraction.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Shader.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<FxCompile Include="shaders\basic.vshader">
|
||||||
|
<Filter>Shader Files</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="Mesh.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Renderer.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="MassSpringSolver.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInteraction.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Shader.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,384 @@
|
||||||
|
#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); }
|
|
@ -0,0 +1,230 @@
|
||||||
|
#pragma once
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
#include <Eigen/Sparse>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
// Mass-Spring System struct
|
||||||
|
struct mass_spring_system {
|
||||||
|
typedef Eigen::SparseMatrix<float> SparseMatrix;
|
||||||
|
typedef Eigen::VectorXf VectorXf;
|
||||||
|
typedef std::pair<unsigned int, unsigned int> Edge;
|
||||||
|
typedef std::vector<Edge> EdgeList;
|
||||||
|
|
||||||
|
// parameters
|
||||||
|
unsigned int n_points; // number of points
|
||||||
|
unsigned int n_springs; // number of springs
|
||||||
|
float time_step; // time step
|
||||||
|
EdgeList spring_list; // spring edge list
|
||||||
|
VectorXf rest_lengths; // spring rest lengths
|
||||||
|
VectorXf stiffnesses; // spring stiffnesses
|
||||||
|
VectorXf masses; // point masses
|
||||||
|
VectorXf fext; // external forces
|
||||||
|
float damping_factor; // damping factor
|
||||||
|
|
||||||
|
mass_spring_system(
|
||||||
|
unsigned int n_points, // number of points
|
||||||
|
unsigned int n_springs, // number of springs
|
||||||
|
float time_step, // time step
|
||||||
|
EdgeList spring_list, // spring edge list
|
||||||
|
VectorXf rest_lengths, // spring rest lengths
|
||||||
|
VectorXf stiffnesses, // spring stiffnesses
|
||||||
|
VectorXf masses, // point masses
|
||||||
|
VectorXf fext, // external forces
|
||||||
|
float damping_factor // damping factor
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mass-Spring System Solver class
|
||||||
|
class MassSpringSolver {
|
||||||
|
private:
|
||||||
|
typedef Eigen::Vector3f Vector3f;
|
||||||
|
typedef Eigen::VectorXf VectorXf;
|
||||||
|
typedef Eigen::SparseMatrix<float> SparseMatrix;
|
||||||
|
typedef Eigen::SimplicialLLT<Eigen::SparseMatrix<float> > Cholesky;
|
||||||
|
typedef Eigen::Map<Eigen::VectorXf> Map;
|
||||||
|
typedef std::pair<unsigned int, unsigned int> Edge;
|
||||||
|
typedef Eigen::Triplet<float> Triplet;
|
||||||
|
typedef std::vector<Triplet> TripletList;
|
||||||
|
|
||||||
|
// system
|
||||||
|
mass_spring_system* system;
|
||||||
|
Cholesky system_matrix;
|
||||||
|
|
||||||
|
// M, L, J matrices
|
||||||
|
SparseMatrix M;
|
||||||
|
SparseMatrix L;
|
||||||
|
SparseMatrix J;
|
||||||
|
|
||||||
|
// state
|
||||||
|
Map current_state; // q(n), current state
|
||||||
|
VectorXf prev_state; // q(n - 1), previous state
|
||||||
|
VectorXf spring_directions; // d, spring directions
|
||||||
|
VectorXf inertial_term; // M * y, y = (a + 1) * q(n) - a * q(n - 1)
|
||||||
|
|
||||||
|
// steps
|
||||||
|
void globalStep();
|
||||||
|
void localStep();
|
||||||
|
|
||||||
|
public:
|
||||||
|
MassSpringSolver(mass_spring_system* system, float* vbuff);
|
||||||
|
|
||||||
|
// solve iterations
|
||||||
|
void solve(unsigned int n);
|
||||||
|
void timedSolve(unsigned int ms);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mass-Spring System Builder Class
|
||||||
|
class MassSpringBuilder {
|
||||||
|
private:
|
||||||
|
typedef Eigen::Vector3f Vector3f;
|
||||||
|
typedef Eigen::VectorXf VectorXf;
|
||||||
|
typedef std::pair<unsigned int, unsigned int> Edge;
|
||||||
|
typedef std::vector<Edge> EdgeList;
|
||||||
|
typedef Eigen::Triplet<float> Triplet;
|
||||||
|
typedef std::vector<Triplet> TripletList;
|
||||||
|
typedef std::vector<unsigned int> IndexList;
|
||||||
|
|
||||||
|
IndexList structI, shearI, bendI;
|
||||||
|
mass_spring_system* result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void uniformGrid(
|
||||||
|
unsigned int n, // grid width
|
||||||
|
float time_step, // time step
|
||||||
|
float rest_length, // spring rest length (non-diagonal)
|
||||||
|
float stiffness, // spring stiffness
|
||||||
|
float mass, // node mass
|
||||||
|
float damping_factor, // damping factor
|
||||||
|
float gravity // gravitationl force (-z axis)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// indices
|
||||||
|
IndexList getStructIndex(); // structural springs
|
||||||
|
IndexList getShearIndex(); // shearing springs
|
||||||
|
IndexList getBendIndex(); // bending springs
|
||||||
|
|
||||||
|
mass_spring_system* getResult();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constraint Graph
|
||||||
|
class CgNodeVisitor; // Constraint graph node visitor
|
||||||
|
|
||||||
|
// Constraint graph node
|
||||||
|
class CgNode {
|
||||||
|
protected:
|
||||||
|
mass_spring_system* system;
|
||||||
|
float* vbuff;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CgNode(mass_spring_system* system, float* vbuff);
|
||||||
|
|
||||||
|
virtual void satisfy() = 0; // satisfy constraint
|
||||||
|
virtual bool accept(CgNodeVisitor& visitor) = 0; // accept visitor
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// point constraint node
|
||||||
|
class CgPointNode : public CgNode {
|
||||||
|
public:
|
||||||
|
CgPointNode(mass_spring_system* system, float* vbuff);
|
||||||
|
virtual bool query(unsigned int i) const = 0; // check if point with index i is constrained
|
||||||
|
virtual bool accept(CgNodeVisitor& visitor);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// spring constraint node
|
||||||
|
class CgSpringNode : public CgNode {
|
||||||
|
protected:
|
||||||
|
typedef std::vector<CgNode*> NodeList;
|
||||||
|
NodeList children;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CgSpringNode(mass_spring_system* system, float* vbuff);
|
||||||
|
|
||||||
|
virtual bool accept(CgNodeVisitor& visitor);
|
||||||
|
void addChild(CgNode* node);
|
||||||
|
void removeChild(CgNode* node);
|
||||||
|
};
|
||||||
|
|
||||||
|
// root node
|
||||||
|
class CgRootNode : public CgSpringNode {
|
||||||
|
public:
|
||||||
|
CgRootNode(mass_spring_system* system, float* vbuff);
|
||||||
|
|
||||||
|
virtual void satisfy();
|
||||||
|
virtual bool accept(CgNodeVisitor& visitor);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CgPointFixNode : public CgPointNode {
|
||||||
|
protected:
|
||||||
|
typedef Eigen::Vector3f Vector3f;
|
||||||
|
std::unordered_map<unsigned int, Vector3f> fix_map;
|
||||||
|
public:
|
||||||
|
CgPointFixNode(mass_spring_system* system, float* vbuff);
|
||||||
|
virtual void satisfy();
|
||||||
|
|
||||||
|
virtual bool query(unsigned int i) const;
|
||||||
|
virtual void fixPoint(unsigned int i); // add point at index i to list
|
||||||
|
virtual void releasePoint(unsigned int i); // remove point at index i from list
|
||||||
|
};
|
||||||
|
|
||||||
|
// spring node
|
||||||
|
class CgSpringDeformationNode : public CgSpringNode {
|
||||||
|
private:
|
||||||
|
typedef std::pair<unsigned int, unsigned int> Edge;
|
||||||
|
typedef Eigen::Vector3f Vector3f;
|
||||||
|
std::unordered_set<unsigned int> items;
|
||||||
|
float tauc; // critical deformation rate
|
||||||
|
unsigned int n_iter; // number of iterations
|
||||||
|
|
||||||
|
public:
|
||||||
|
CgSpringDeformationNode(mass_spring_system* system, float* vbuff, float tauc, unsigned int n_iter);
|
||||||
|
virtual void satisfy();
|
||||||
|
|
||||||
|
void addSprings(std::vector<unsigned int> springs);
|
||||||
|
};
|
||||||
|
|
||||||
|
// sphere collision node
|
||||||
|
class CgSphereCollisionNode : public CgPointNode {
|
||||||
|
private:
|
||||||
|
typedef Eigen::Vector3f Vector3f;
|
||||||
|
|
||||||
|
float radius;
|
||||||
|
Vector3f center;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CgSphereCollisionNode(mass_spring_system* system, float* vbuff, float radius, Vector3f center);
|
||||||
|
virtual bool query(unsigned int i) const;
|
||||||
|
virtual void satisfy();
|
||||||
|
};
|
||||||
|
|
||||||
|
// node visitor
|
||||||
|
class CgNodeVisitor {
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual bool visit(CgPointNode& node);
|
||||||
|
virtual bool visit(CgSpringNode& node);
|
||||||
|
};
|
||||||
|
|
||||||
|
// fixed point query visitor
|
||||||
|
class CgQueryFixedPointVisitor : public CgNodeVisitor {
|
||||||
|
private:
|
||||||
|
unsigned int i;
|
||||||
|
bool queryResult;
|
||||||
|
public:
|
||||||
|
virtual bool visit(CgPointNode& node);
|
||||||
|
|
||||||
|
bool queryPoint(CgNode& root, unsigned int i);
|
||||||
|
};
|
||||||
|
|
||||||
|
// satisfy visitor
|
||||||
|
class CgSatisfyVisitor : public CgNodeVisitor {
|
||||||
|
public:
|
||||||
|
virtual bool visit(CgPointNode& node);
|
||||||
|
virtual bool visit(CgSpringNode& node);
|
||||||
|
|
||||||
|
void satisfy(CgNode& root);
|
||||||
|
};
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "Mesh.h"
|
||||||
|
|
||||||
|
// M E S H /////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
float* Mesh::vbuff() { return VERTEX_DATA(this); }
|
||||||
|
float* Mesh::nbuff() { return NORMAL_DATA(this); }
|
||||||
|
float* Mesh::tbuff() { return TEXTURE_DATA(this); }
|
||||||
|
unsigned int* Mesh::ibuff() { return &_ibuff[0]; }
|
||||||
|
void Mesh::useIBuff(std::vector<unsigned int>& _ibuff) { this->_ibuff = _ibuff; }
|
||||||
|
|
||||||
|
unsigned int Mesh::vbuffLen() { return (unsigned int)n_vertices() * 3; }
|
||||||
|
unsigned int Mesh::nbuffLen() { return (unsigned int)n_vertices() * 3; }
|
||||||
|
unsigned int Mesh::tbuffLen() { return (unsigned int)n_vertices() * 2; }
|
||||||
|
unsigned int Mesh::ibuffLen() { return (unsigned int)_ibuff.size(); }
|
||||||
|
|
||||||
|
|
||||||
|
// M E S H B U I L D E R /////////////////////////////////////////////////////////////////////
|
||||||
|
void MeshBuilder::uniformGrid(float w, int n) {
|
||||||
|
result = new Mesh;
|
||||||
|
unsigned int ibuffLen = 6 * (n - 1) * (n - 1);
|
||||||
|
std::vector<unsigned int> ibuff(ibuffLen);
|
||||||
|
|
||||||
|
// request mesh properties
|
||||||
|
result->request_vertex_normals();
|
||||||
|
result->request_vertex_normals();
|
||||||
|
result->request_vertex_texcoords2D();
|
||||||
|
|
||||||
|
// generate mesh
|
||||||
|
unsigned int idx = 0; // vertex index
|
||||||
|
const float d = w / (n - 1); // step distance
|
||||||
|
const float ud = 1.0f / (n - 1); // unit step distance
|
||||||
|
const OpenMesh::Vec3f o = OpenMesh::Vec3f(-w/2.0f, w/2.0f, 0.0f); // origin
|
||||||
|
const OpenMesh::Vec3f ux = OpenMesh::Vec3f(1.0f, 0.0f, 0.0f); // unit x direction
|
||||||
|
const OpenMesh::Vec3f uy = OpenMesh::Vec3f(0.0f, -1.0f, 0.0f); // unit y direction
|
||||||
|
std::vector<OpenMesh::VertexHandle> handle_table(n * n); // table storing vertex handles for easy grid connectivity establishment
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
for (int j = 0; j < n; j++) {
|
||||||
|
handle_table[j + i * n] = result->add_vertex(o + d*j*ux + d*i*uy); // add vertex
|
||||||
|
result->set_texcoord2D(handle_table[j + i * n], OpenMesh::Vec2f(ud*j, ud*i)); // add texture coordinates
|
||||||
|
|
||||||
|
//add connectivity
|
||||||
|
if (i > 0 && j < n - 1) {
|
||||||
|
result->add_face(
|
||||||
|
handle_table[j + i * n],
|
||||||
|
handle_table[j + 1 + (i - 1) * n],
|
||||||
|
handle_table[j + (i - 1) * n]
|
||||||
|
);
|
||||||
|
|
||||||
|
ibuff[idx++] = j + i * n;
|
||||||
|
ibuff[idx++] = j + 1 + (i - 1) * n;
|
||||||
|
ibuff[idx++] = j + (i - 1) * n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j > 0 && i > 0) {
|
||||||
|
result->add_face(
|
||||||
|
handle_table[j + i * n],
|
||||||
|
handle_table[j + (i - 1) * n],
|
||||||
|
handle_table[j - 1 + i * n]
|
||||||
|
);
|
||||||
|
|
||||||
|
ibuff[idx++] = j + i * n;
|
||||||
|
ibuff[idx++] = j + (i - 1) * n;
|
||||||
|
ibuff[idx++] = j - 1 + i * n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate normals
|
||||||
|
result->request_face_normals();
|
||||||
|
result->update_normals();
|
||||||
|
result->release_face_normals();
|
||||||
|
|
||||||
|
// set index buffer
|
||||||
|
result->useIBuff(ibuff);
|
||||||
|
}
|
||||||
|
Mesh* MeshBuilder::getResult() { return result; }
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Mesh type
|
||||||
|
typedef OpenMesh::TriMesh_ArrayKernelT<> _Mesh;
|
||||||
|
|
||||||
|
// Macros for extracting buffers from OpenMesh
|
||||||
|
#define VERTEX_DATA(mesh) (float*) &(mesh->point(*mesh->vertices_begin()))
|
||||||
|
#define NORMAL_DATA(mesh) (float*) &(mesh->normal(*mesh->vertices_begin()))
|
||||||
|
#define TEXTURE_DATA(mesh) (float*) &(mesh->texcoord2D(*mesh->vertices_begin()))
|
||||||
|
|
||||||
|
// Mesh class
|
||||||
|
class Mesh : public _Mesh {
|
||||||
|
private:
|
||||||
|
std::vector<unsigned int> _ibuff;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// pointers to buffers
|
||||||
|
float* vbuff();
|
||||||
|
float* nbuff();
|
||||||
|
float* tbuff();
|
||||||
|
unsigned int* ibuff();
|
||||||
|
|
||||||
|
// buffer sizes
|
||||||
|
unsigned int vbuffLen();
|
||||||
|
unsigned int nbuffLen();
|
||||||
|
unsigned int tbuffLen();
|
||||||
|
unsigned int ibuffLen();
|
||||||
|
|
||||||
|
// set index buffer
|
||||||
|
void useIBuff(std::vector<unsigned int>& _ibuff);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MeshBuilder {
|
||||||
|
private:
|
||||||
|
Mesh* result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void uniformGrid(float w, int n);
|
||||||
|
Mesh* getResult();
|
||||||
|
};
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "Renderer.h"
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
Renderer::Renderer() {}
|
||||||
|
|
||||||
|
void Renderer::setProgram(GLProgram* program) {
|
||||||
|
assert(program != nullptr);
|
||||||
|
assert((*program) > 0);
|
||||||
|
this->program = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::setProgramInput(ProgramInput* input) {
|
||||||
|
this->input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::setModelview(const glm::mat4& mv) {
|
||||||
|
assert(program != nullptr);
|
||||||
|
assert((*program) > 0);
|
||||||
|
glUseProgram(*program);
|
||||||
|
program->setModelView(mv);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::setProjection(const glm::mat4& p) {
|
||||||
|
assert(program != nullptr);
|
||||||
|
assert((*program) > 0);
|
||||||
|
glUseProgram(*program);
|
||||||
|
program->setProjection(p);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::setElementCount(unsigned int n_elements) { this->n_elements = n_elements; }
|
||||||
|
|
||||||
|
void Renderer::draw() {
|
||||||
|
assert(program != nullptr);
|
||||||
|
assert((*program) > 0);
|
||||||
|
glUseProgram(*program);
|
||||||
|
glBindVertexArray(*input);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
glDrawElements(GL_TRIANGLES, n_elements, GL_UNSIGNED_INT, 0);
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
glDisableVertexAttribArray(1);
|
||||||
|
glDisableVertexAttribArray(2);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include "Shader.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
protected:
|
||||||
|
GLProgram* program;
|
||||||
|
ProgramInput* input;
|
||||||
|
unsigned int n_elements;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Renderer();
|
||||||
|
|
||||||
|
void setProgram(GLProgram* program);
|
||||||
|
void setProgramInput(ProgramInput* input);
|
||||||
|
void setModelview(const glm::mat4& mv);
|
||||||
|
void setProjection(const glm::mat4& p);
|
||||||
|
void setElementCount(unsigned int n_elements);
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
};
|
|
@ -0,0 +1,190 @@
|
||||||
|
#include "Shader.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// GLSHADER ///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
GLShader::GLShader(GLenum shaderType) : handle(glCreateShader(shaderType)) {};
|
||||||
|
|
||||||
|
GLShader::operator GLuint() const {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLShader::~GLShader() {
|
||||||
|
glDeleteShader(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLShader::compile(const char* source) {
|
||||||
|
GLint compiled = 0; // Compiled flag
|
||||||
|
const char *ptrs[] = { source };
|
||||||
|
const GLint lens[] = { std::strlen(source) };
|
||||||
|
glShaderSource(handle, 1, ptrs, lens);
|
||||||
|
glCompileShader(handle);
|
||||||
|
glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
|
||||||
|
if (!compiled) {
|
||||||
|
GLint logSize = 0;
|
||||||
|
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &logSize);
|
||||||
|
std::vector<GLchar> errorLog(logSize);
|
||||||
|
glGetShaderInfoLog(handle, logSize, &logSize, &errorLog[0]);
|
||||||
|
std::cerr << &errorLog[0] << std::endl;
|
||||||
|
throw std::runtime_error("Failed to compile shader.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLShader::compile(std::ifstream& source) {
|
||||||
|
std::vector<char> text;
|
||||||
|
source.seekg(0, std::ios_base::end);
|
||||||
|
std::streampos fileSize = source.tellg();
|
||||||
|
text.resize(fileSize);
|
||||||
|
|
||||||
|
source.seekg(0, std::ios_base::beg);
|
||||||
|
source.read(&text[0], fileSize);
|
||||||
|
compile(&text[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GLPROGRAM //////////////////////////////////////////////////////////////////////////////////
|
||||||
|
GLProgram::GLProgram() : handle(glCreateProgram()) {}
|
||||||
|
|
||||||
|
void GLProgram::link(const GLShader& vshader, const GLShader& fshader) {
|
||||||
|
GLint linked = 0; // Linked flag
|
||||||
|
glAttachShader(handle, vshader);
|
||||||
|
glAttachShader(handle, fshader);
|
||||||
|
glLinkProgram(handle);
|
||||||
|
glDetachShader(handle, vshader);
|
||||||
|
glDetachShader(handle, fshader);
|
||||||
|
glGetProgramiv(handle, GL_LINK_STATUS, &linked);
|
||||||
|
if (!linked)
|
||||||
|
throw std::runtime_error("Failed to link shaders.");
|
||||||
|
|
||||||
|
// get camera uniforms
|
||||||
|
uModelViewMatrix = glGetUniformLocation(handle, "uModelViewMatrix");
|
||||||
|
uProjectionMatrix = glGetUniformLocation(handle, "uProjectionMatrix");
|
||||||
|
|
||||||
|
// post link
|
||||||
|
postLink();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLProgram::postLink() {}
|
||||||
|
|
||||||
|
void GLProgram::setUniformMat4(GLuint unif, glm::mat4 m) {
|
||||||
|
glUseProgram(handle);
|
||||||
|
glUniformMatrix4fv(unif,
|
||||||
|
1, GL_FALSE, glm::value_ptr(m[0]));
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GLProgram::setModelView(glm::mat4 m) {
|
||||||
|
setUniformMat4(uModelViewMatrix, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLProgram::setProjection(glm::mat4 m) {
|
||||||
|
setUniformMat4(uProjectionMatrix, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLProgram::operator GLuint() const { return handle; }
|
||||||
|
|
||||||
|
GLProgram::~GLProgram() { glDeleteProgram(handle); }
|
||||||
|
|
||||||
|
// PROGRAM INPUT //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
ProgramInput::ProgramInput() {
|
||||||
|
// generate buffers
|
||||||
|
glGenBuffers(4, &vbo[0]);
|
||||||
|
|
||||||
|
// generate vertex array object
|
||||||
|
glGenVertexArrays(1, &handle);
|
||||||
|
glBindVertexArray(handle);
|
||||||
|
|
||||||
|
// positions
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||||
|
|
||||||
|
// normals
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
|
||||||
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||||
|
|
||||||
|
// texture coordinates
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
|
||||||
|
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||||
|
|
||||||
|
// indices
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramInput::bufferData(unsigned int index, void* buff, size_t size) {
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo[index]);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, size,
|
||||||
|
buff, GL_STATIC_DRAW);
|
||||||
|
}
|
||||||
|
void ProgramInput::setPositionData(float* buff, unsigned int len) {
|
||||||
|
bufferData(0, buff, sizeof(float) * len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramInput::setNormalData(float* buff, unsigned int len) {
|
||||||
|
bufferData(1, buff, sizeof(float) * len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramInput::setTextureData(float* buff, unsigned int len) {
|
||||||
|
bufferData(2, buff, sizeof(float) * len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramInput::setIndexData(unsigned int* buff, unsigned int len) {
|
||||||
|
bufferData(3, buff, sizeof(unsigned int) * len);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramInput::operator GLuint() const {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ProgramInput::~ProgramInput() {
|
||||||
|
glDeleteBuffers(4, vbo);
|
||||||
|
glDeleteVertexArrays(1, &handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHADER PROGRAMS ////////////////////////////////////////////////////////////////////////////
|
||||||
|
PhongShader::PhongShader() : GLProgram() {}
|
||||||
|
void PhongShader::postLink() {
|
||||||
|
// get uniforms
|
||||||
|
uAlbedo = glGetUniformLocation(handle, "uAlbedo");
|
||||||
|
uAmbient = glGetUniformLocation(handle, "uAmbient");
|
||||||
|
uLight = glGetUniformLocation(handle, "uLight");
|
||||||
|
}
|
||||||
|
void PhongShader::setAlbedo(const glm::vec3& albedo) {
|
||||||
|
assert(uAlbedo >= 0);
|
||||||
|
glUseProgram(*this);
|
||||||
|
glUniform3f(uAlbedo, albedo[0], albedo[1], albedo[2]);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
void PhongShader::setAmbient(const glm::vec3& ambient) {
|
||||||
|
assert(uAmbient >= 0);
|
||||||
|
glUseProgram(*this);
|
||||||
|
glUniform3f(uAmbient, ambient[0], ambient[1], ambient[2]);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
void PhongShader::setLight(const glm::vec3& light) {
|
||||||
|
assert(uLight >= 0);
|
||||||
|
glUseProgram(*this);
|
||||||
|
glUniform3f(uLight, light[0], light[1], light[2]);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PickShader::PickShader() : GLProgram() {}
|
||||||
|
|
||||||
|
void PickShader::postLink() {
|
||||||
|
// get uniforms
|
||||||
|
uTessFact = glGetUniformLocation(handle, "uTessFact");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PickShader::setTessFact(unsigned int n) {
|
||||||
|
assert(uTessFact >= 0);
|
||||||
|
glUseProgram(*this);
|
||||||
|
glUniform1i(uTessFact, n);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
#pragma once
|
||||||
|
#include <fstream>
|
||||||
|
#include <GL\glew.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class NonCopyable {
|
||||||
|
private:
|
||||||
|
NonCopyable(const NonCopyable& other) = delete;
|
||||||
|
NonCopyable& operator=(const NonCopyable& other) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NonCopyable() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GLShader : public NonCopyable {
|
||||||
|
private:
|
||||||
|
GLuint handle; // Shader handle
|
||||||
|
|
||||||
|
public:
|
||||||
|
GLShader(GLenum shaderType);
|
||||||
|
/*GLShader(GLenum shaderType, const char* source);
|
||||||
|
GLShader(GLenum shaderType, std::ifstream& source);*/
|
||||||
|
void compile(const char* source);
|
||||||
|
void compile(std::ifstream& source);
|
||||||
|
operator GLuint() const; // cast to GLuint
|
||||||
|
~GLShader();
|
||||||
|
};
|
||||||
|
|
||||||
|
class GLProgram : public NonCopyable {
|
||||||
|
protected:
|
||||||
|
GLuint handle;
|
||||||
|
GLuint uModelViewMatrix, uProjectionMatrix;
|
||||||
|
void setUniformMat4(GLuint unif, glm::mat4 m);
|
||||||
|
|
||||||
|
public:
|
||||||
|
GLProgram();
|
||||||
|
virtual void link(const GLShader& vshader, const GLShader& fshader);
|
||||||
|
virtual void postLink();
|
||||||
|
operator GLuint() const; // cast to GLuint
|
||||||
|
void setModelView(glm::mat4 m);
|
||||||
|
void setProjection(glm::mat4 m);
|
||||||
|
|
||||||
|
~GLProgram();
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProgramInput : public NonCopyable {
|
||||||
|
private:
|
||||||
|
GLuint handle; // vertex array object handle
|
||||||
|
GLuint vbo[4]; // vertex buffer object handles | position, normal, texture, index
|
||||||
|
void bufferData(unsigned int index, void* buff, size_t size);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProgramInput();
|
||||||
|
|
||||||
|
void setPositionData(float* buff, unsigned int len);
|
||||||
|
void setNormalData(float* buff, unsigned int len);
|
||||||
|
void setTextureData(float* buff, unsigned int len);
|
||||||
|
void setIndexData(unsigned int* buff, unsigned int len);
|
||||||
|
|
||||||
|
operator GLuint() const; // cast to GLuint
|
||||||
|
|
||||||
|
~ProgramInput();
|
||||||
|
};
|
||||||
|
|
||||||
|
class PhongShader : public GLProgram {
|
||||||
|
private:
|
||||||
|
// Albedo | Ambient Light | Light Direction
|
||||||
|
GLuint uAlbedo, uAmbient, uLight;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PhongShader();
|
||||||
|
virtual void postLink();
|
||||||
|
void setAlbedo(const glm::vec3& albedo);
|
||||||
|
void setAmbient(const glm::vec3& ambient);
|
||||||
|
void setLight(const glm::vec3& light);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PickShader : public GLProgram {
|
||||||
|
private:
|
||||||
|
GLuint uTessFact;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PickShader();
|
||||||
|
virtual void postLink();
|
||||||
|
void setTessFact(unsigned int n);
|
||||||
|
};
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "UserInteraction.h"
|
||||||
|
#include <GL/glew.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
UserInteraction::UserInteraction(Renderer* renderer, CgPointFixNode* fixer, float* vbuff)
|
||||||
|
: renderer(renderer), vbuff(vbuff), fixer(fixer), i(-1) {}
|
||||||
|
|
||||||
|
void UserInteraction::setModelview(const glm::mat4& mv) { renderer->setModelview(mv); }
|
||||||
|
void UserInteraction::setProjection(const glm::mat4& p) { renderer->setProjection(p); }
|
||||||
|
|
||||||
|
void UserInteraction::grabPoint(int mouse_x, int mouse_y){
|
||||||
|
// render scene
|
||||||
|
glClearColor(0, 0, 0, 0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
renderer->draw();
|
||||||
|
glFlush();
|
||||||
|
|
||||||
|
// read color
|
||||||
|
color c(3);
|
||||||
|
glReadPixels(mouse_x, mouse_y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &c[0]);
|
||||||
|
i = colorToIndex(c);
|
||||||
|
if (i != -1) fixer->fixPoint(i);
|
||||||
|
|
||||||
|
// return to normal state
|
||||||
|
glClearColor(0.25f, 0.25f, 0.25f, 0);
|
||||||
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInteraction::releasePoint() { if (i == -1) return; fixer->releasePoint(i); i = -1; }
|
||||||
|
void UserInteraction::movePoint(vec3 v) {
|
||||||
|
if (i == -1) return;
|
||||||
|
fixer->releasePoint(i);
|
||||||
|
for(int j = 0; j < 3; j++)
|
||||||
|
vbuff[3 * i + j] += v[j];
|
||||||
|
fixer->fixPoint(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
GridMeshUI::GridMeshUI(Renderer* renderer, CgPointFixNode* fixer, float* vbuff, unsigned int n)
|
||||||
|
: UserInteraction(renderer, fixer, vbuff), n(n) {}
|
||||||
|
|
||||||
|
int GridMeshUI::colorToIndex(color c) const {
|
||||||
|
if (c[2] != 51) return -1;
|
||||||
|
int vx = std::round((n - 1) * c[0] / 255.0);
|
||||||
|
int vy = std::round((n - 1) * c[1] / 255.0);
|
||||||
|
return n * vy + vx;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
#include <glm/common.hpp>
|
||||||
|
|
||||||
|
#include "MassSpringSolver.h"
|
||||||
|
#include "Renderer.h"
|
||||||
|
|
||||||
|
class UserInteraction {
|
||||||
|
protected:
|
||||||
|
typedef glm::vec3 vec3;
|
||||||
|
typedef std::vector<unsigned char> color;
|
||||||
|
|
||||||
|
int i; // index of fixed point
|
||||||
|
float* vbuff; // vertex buffer
|
||||||
|
CgPointFixNode* fixer; // point fixer
|
||||||
|
Renderer* renderer; // pick shader renderer
|
||||||
|
virtual int colorToIndex(color c) const = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UserInteraction(Renderer* renderer, CgPointFixNode* fixer, float* vbuff);
|
||||||
|
|
||||||
|
void setModelview(const glm::mat4& mv);
|
||||||
|
void setProjection(const glm::mat4& p);
|
||||||
|
|
||||||
|
void grabPoint(int mouse_x, int mouse_y); // grab point with color c
|
||||||
|
void movePoint(vec3 v); // move grabbed point along mouse
|
||||||
|
void releasePoint(); // release grabbed point;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GridMeshUI : public UserInteraction {
|
||||||
|
protected:
|
||||||
|
const unsigned int n; // grid width
|
||||||
|
virtual int colorToIndex(color c) const;
|
||||||
|
public:
|
||||||
|
GridMeshUI(Renderer* renderer, CgPointFixNode* fixer, float* vbuff, unsigned int n);
|
||||||
|
};
|
|
@ -0,0 +1,471 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="nupengl.core" version="0.1.0.1" targetFramework="native" />
|
||||||
|
<package id="nupengl.core.redist" version="0.1.0.1" targetFramework="native" />
|
||||||
|
</packages>
|
|
@ -0,0 +1,19 @@
|
||||||
|
#version 430
|
||||||
|
|
||||||
|
uniform mat4 uModelViewMatrix;
|
||||||
|
// uniform mat4 uNormalMatrix;
|
||||||
|
uniform mat4 uProjectionMatrix;
|
||||||
|
|
||||||
|
layout(location=0) in vec3 aPosition;
|
||||||
|
layout(location=1) in vec3 aNormal;
|
||||||
|
layout(location=2) in vec2 aTexCoord;
|
||||||
|
|
||||||
|
out vec3 vNormal;
|
||||||
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
vNormal = aNormal;
|
||||||
|
vTexCoord = aTexCoord;
|
||||||
|
vec4 position = vec4(aPosition, 1.0);
|
||||||
|
gl_Position = uProjectionMatrix * uModelViewMatrix * position;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
#version 430
|
||||||
|
|
||||||
|
in vec3 vNormal;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform vec3 uAlbedo;
|
||||||
|
uniform vec3 uAmbient;
|
||||||
|
uniform vec3 uLight;
|
||||||
|
void main(){
|
||||||
|
vec3 normal = normalize(vNormal);
|
||||||
|
vec3 albedo = uAlbedo;
|
||||||
|
if(!gl_FrontFacing) {
|
||||||
|
normal = -normal;
|
||||||
|
albedo = 1.0 - albedo;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 toLight = normalize(-uLight);
|
||||||
|
float diffuse = max(0, dot(toLight, normal));
|
||||||
|
|
||||||
|
vec3 color = diffuse * albedo + uAmbient * albedo;
|
||||||
|
fragColor = vec4(color, 1.0);
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#version 430
|
||||||
|
|
||||||
|
in vec2 vTexCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform int uTessFact;
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
float vx = round(vTexCoord.x * (uTessFact - 1));
|
||||||
|
float vy = round(vTexCoord.y * (uTessFact - 1));
|
||||||
|
fragColor = vec4(vx / (uTessFact - 1), vy / (uTessFact - 1), 0.2, 1.0);
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 14
|
||||||
|
VisualStudioVersion = 14.0.25420.1
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ClothApp", "ClothApp\ClothApp.vcxproj", "{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D32F8712-F80F-46F9-85E1-FEB2BC718330}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
readme.md = readme.md
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Release|x64.Build.0 = Release|x64
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{BBCA6691-6C35-4BFE-9CB9-13F16D29990F}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Samer Itani
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,16 @@
|
||||||
|
A C++ implementation of *Fast Simulation of Mass-Spring Systems* [1], rendered with OpenGL.
|
||||||
|
The dynamic inverse procedure described in [2] was implemented to constrain spring deformations and prevent the "super-elastic" effect when using large time-steps.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
* **OpenGL, freeGLUT, GLEW, GLM** for rendering.
|
||||||
|
* **OpenMesh** for computing normals.
|
||||||
|
* **Eigen** for sparse matrix algebra.
|
||||||
|
|
||||||
|
### Demonstration
|
||||||
|
![curtain hang](https://media.giphy.com/media/5EC1drLIyBuHxRC4I8/giphy.gif)
|
||||||
|
![curtain drop](https://media.giphy.com/media/1zRbfGDmHTcn9RoUjy/giphy.gif)
|
||||||
|
|
||||||
|
### References
|
||||||
|
[1] Liu, T., Bargteil, A. W., Obrien, J. F., & Kavan, L. (2013). Fast simulation of mass-spring systems. *ACM Transactions on Graphics,32*(6), 1-7. doi:10.1145/2508363.2508406
|
||||||
|
|
||||||
|
[2] Provot, X. (1995). Deformation constraints in a mass-spring modelto describe rigid cloth behavior. *InGraphics Interface* 1995,147–154.
|
Loading…
Reference in New Issue