Remove component layer
parent
7817cde381
commit
62418b9340
21
dust3d.pro
21
dust3d.pro
|
@ -344,9 +344,6 @@ HEADERS += src/combinemode.h
|
||||||
SOURCES += src/polycount.cpp
|
SOURCES += src/polycount.cpp
|
||||||
HEADERS += src/polycount.h
|
HEADERS += src/polycount.h
|
||||||
|
|
||||||
SOURCES += src/cutdocument.cpp
|
|
||||||
HEADERS += src/cutdocument.h
|
|
||||||
|
|
||||||
SOURCES += src/cutface.cpp
|
SOURCES += src/cutface.cpp
|
||||||
HEADERS += src/cutface.h
|
HEADERS += src/cutface.h
|
||||||
|
|
||||||
|
@ -403,12 +400,6 @@ HEADERS += src/boundingboxmesh.h
|
||||||
SOURCES += src/regionfiller.cpp
|
SOURCES += src/regionfiller.cpp
|
||||||
HEADERS += src/regionfiller.h
|
HEADERS += src/regionfiller.h
|
||||||
|
|
||||||
SOURCES += src/cyclefinder.cpp
|
|
||||||
HEADERS += src/cyclefinder.h
|
|
||||||
|
|
||||||
SOURCES += src/shortestpath.cpp
|
|
||||||
HEADERS += src/shortestpath.h
|
|
||||||
|
|
||||||
SOURCES += src/meshwrapper.cpp
|
SOURCES += src/meshwrapper.cpp
|
||||||
HEADERS += src/meshwrapper.h
|
HEADERS += src/meshwrapper.h
|
||||||
|
|
||||||
|
@ -442,24 +433,12 @@ HEADERS += src/booleanmesh.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/isotropicremesh.cpp
|
SOURCES += src/isotropicremesh.cpp
|
||||||
HEADERS += src/isotropicremesh.h
|
HEADERS += src/isotropicremesh.h
|
||||||
|
|
||||||
SOURCES += src/clothforce.cpp
|
|
||||||
HEADERS += src/clothforce.h
|
|
||||||
|
|
||||||
SOURCES += src/projectfacestonodes.cpp
|
SOURCES += src/projectfacestonodes.cpp
|
||||||
HEADERS += src/projectfacestonodes.h
|
HEADERS += src/projectfacestonodes.h
|
||||||
|
|
||||||
SOURCES += src/simulateclothmeshes.cpp
|
|
||||||
HEADERS += src/simulateclothmeshes.h
|
|
||||||
|
|
||||||
SOURCES += src/ddsfile.cpp
|
SOURCES += src/ddsfile.cpp
|
||||||
HEADERS += src/ddsfile.h
|
HEADERS += src/ddsfile.h
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
#include <QObject>
|
|
||||||
#include "clothforce.h"
|
|
||||||
|
|
||||||
IMPL_ClothForceFromString
|
|
||||||
IMPL_ClothForceToString
|
|
||||||
IMPL_ClothForceToDispName
|
|
|
@ -1,49 +0,0 @@
|
||||||
#ifndef DUST3D_CLOTH_FORCE_H
|
|
||||||
#define DUST3D_CLOTH_FORCE_H
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
enum class ClothForce
|
|
||||||
{
|
|
||||||
Gravitational = 0,
|
|
||||||
Centripetal,
|
|
||||||
Count
|
|
||||||
};
|
|
||||||
ClothForce ClothForceFromString(const char *forceString);
|
|
||||||
#define IMPL_ClothForceFromString \
|
|
||||||
ClothForce ClothForceFromString(const char *forceString) \
|
|
||||||
{ \
|
|
||||||
QString force = forceString; \
|
|
||||||
if (force == "Gravitational") \
|
|
||||||
return ClothForce::Gravitational; \
|
|
||||||
if (force == "Centripetal") \
|
|
||||||
return ClothForce::Centripetal; \
|
|
||||||
return ClothForce::Gravitational; \
|
|
||||||
}
|
|
||||||
const char *ClothForceToString(ClothForce force);
|
|
||||||
#define IMPL_ClothForceToString \
|
|
||||||
const char *ClothForceToString(ClothForce force) \
|
|
||||||
{ \
|
|
||||||
switch (force) { \
|
|
||||||
case ClothForce::Gravitational: \
|
|
||||||
return "Gravitational"; \
|
|
||||||
case ClothForce::Centripetal: \
|
|
||||||
return "Centripetal"; \
|
|
||||||
default: \
|
|
||||||
return "Gravitational"; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
QString ClothForceToDispName(ClothForce force);
|
|
||||||
#define IMPL_ClothForceToDispName \
|
|
||||||
QString ClothForceToDispName(ClothForce force) \
|
|
||||||
{ \
|
|
||||||
switch (force) { \
|
|
||||||
case ClothForce::Gravitational: \
|
|
||||||
return QObject::tr("Gravitational"); \
|
|
||||||
case ClothForce::Centripetal: \
|
|
||||||
return QObject::tr("Centripetal"); \
|
|
||||||
default: \
|
|
||||||
return QObject::tr("Gravitational"); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,290 +0,0 @@
|
||||||
#include <MassSpringSolver.h>
|
|
||||||
#include <set>
|
|
||||||
#include <CGAL/Simple_cartesian.h>
|
|
||||||
#include <CGAL/AABB_tree.h>
|
|
||||||
#include <CGAL/AABB_traits.h>
|
|
||||||
#include <CGAL/Polyhedron_3.h>
|
|
||||||
#include <CGAL/Polyhedron_incremental_builder_3.h>
|
|
||||||
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
|
|
||||||
#include <CGAL/AABB_face_graph_triangle_primitive.h>
|
|
||||||
#include <CGAL/algorithm.h>
|
|
||||||
#include <CGAL/Side_of_triangle_mesh.h>
|
|
||||||
#include "clothsimulator.h"
|
|
||||||
#include "booleanmesh.h"
|
|
||||||
|
|
||||||
typedef CGAL::Simple_cartesian<double> K;
|
|
||||||
typedef K::Point_3 Point;
|
|
||||||
typedef K::Triangle_3 Triangle;
|
|
||||||
typedef CGAL::Polyhedron_3<K> Polyhedron;
|
|
||||||
typedef Polyhedron::HalfedgeDS HalfedgeDS;
|
|
||||||
typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive;
|
|
||||||
typedef CGAL::AABB_traits<K, Primitive> Traits;
|
|
||||||
typedef CGAL::AABB_tree<Traits> Tree;
|
|
||||||
typedef CGAL::Side_of_triangle_mesh<Polyhedron, K> Point_inside;
|
|
||||||
|
|
||||||
template <class HDS>
|
|
||||||
class Build_mesh : public CGAL::Modifier_base<HDS> {
|
|
||||||
public:
|
|
||||||
Build_mesh(const std::vector<QVector3D> *vertices,
|
|
||||||
const std::vector<std::vector<size_t>> *faces) :
|
|
||||||
m_vertices(vertices),
|
|
||||||
m_faces(faces)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
void operator()(HDS& hds)
|
|
||||||
{
|
|
||||||
// Postcondition: hds is a valid polyhedral surface.
|
|
||||||
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, false);
|
|
||||||
B.begin_surface(m_vertices->size(), m_faces->size());
|
|
||||||
typedef typename HDS::Vertex Vertex;
|
|
||||||
typedef typename Vertex::Point Point;
|
|
||||||
for (const auto &it: *m_vertices)
|
|
||||||
B.add_vertex(Point(it.x(), it.y(), it.z()));
|
|
||||||
for (const auto &it: *m_faces) {
|
|
||||||
B.begin_facet();
|
|
||||||
B.add_vertex_to_facet(it[0]);
|
|
||||||
B.add_vertex_to_facet(it[1]);
|
|
||||||
B.add_vertex_to_facet(it[2]);
|
|
||||||
B.end_facet();
|
|
||||||
}
|
|
||||||
B.end_surface();
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
const std::vector<QVector3D> *m_vertices = nullptr;
|
|
||||||
const std::vector<std::vector<size_t>> *m_faces = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// System parameters
|
|
||||||
//namespace SystemParam {
|
|
||||||
// static const int n = 61; // must be odd, n * n = n_vertices | 61
|
|
||||||
// static const float h = 0.001f; // time step, smaller for better results | 0.008f = 0.016f/2
|
|
||||||
// static const float m = 0.25f / (n * n); // point mass | 0.25f
|
|
||||||
// static const float a = 0.993f; // damping, close to 1.0 | 0.993f
|
|
||||||
// static const float g = 9.8f * m; // gravitational force | 9.8f
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Point - mesh collision node
|
|
||||||
class CgMeshCollisionNode : public CgPointNode {
|
|
||||||
private:
|
|
||||||
Tree *m_aabbTree = nullptr;
|
|
||||||
Point_inside *m_insideTester = nullptr;
|
|
||||||
Polyhedron m_polyhedron;
|
|
||||||
public:
|
|
||||||
CgMeshCollisionNode(mass_spring_system *system, float *vbuff,
|
|
||||||
const std::vector<QVector3D> &collisionVertices,
|
|
||||||
const std::vector<std::vector<size_t>> &collisionTriangles) :
|
|
||||||
CgPointNode(system, vbuff)
|
|
||||||
{
|
|
||||||
if (!collisionTriangles.empty()) {
|
|
||||||
Build_mesh<HalfedgeDS> mesh(&collisionVertices, &collisionTriangles);
|
|
||||||
m_polyhedron.delegate(mesh);
|
|
||||||
m_aabbTree = new Tree(faces(m_polyhedron).first, faces(m_polyhedron).second, m_polyhedron);
|
|
||||||
m_aabbTree->accelerate_distance_queries();
|
|
||||||
m_insideTester = new Point_inside(*m_aabbTree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~CgMeshCollisionNode()
|
|
||||||
{
|
|
||||||
delete m_insideTester;
|
|
||||||
delete m_aabbTree;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool query(unsigned int i) const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
void satisfy()
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < system->n_points; i++) {
|
|
||||||
auto offset = 3 * i;
|
|
||||||
Point point(vbuff[offset + 0],
|
|
||||||
vbuff[offset + 1],
|
|
||||||
vbuff[offset + 2]);
|
|
||||||
if (nullptr != m_insideTester &&
|
|
||||||
(*m_insideTester)(point) != CGAL::ON_UNBOUNDED_SIDE) {
|
|
||||||
Point closestPoint = m_aabbTree->closest_point(point);
|
|
||||||
vbuff[offset + 0] = closestPoint.x();
|
|
||||||
vbuff[offset + 1] = closestPoint.y();
|
|
||||||
vbuff[offset + 2] = closestPoint.z();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fixPoints(CgPointFixNode *fixNode)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < system->n_points; i++) {
|
|
||||||
auto offset = 3 * i;
|
|
||||||
Point point(vbuff[offset + 0],
|
|
||||||
vbuff[offset + 1],
|
|
||||||
vbuff[offset + 2]);
|
|
||||||
if (nullptr != m_insideTester &&
|
|
||||||
(*m_insideTester)(point) != CGAL::ON_UNBOUNDED_SIDE) {
|
|
||||||
fixNode->fixPoint(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void collectErrorPoints(std::vector<size_t> *points)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < system->n_points; i++) {
|
|
||||||
auto offset = 3 * i;
|
|
||||||
Point point(vbuff[offset + 0],
|
|
||||||
vbuff[offset + 1],
|
|
||||||
vbuff[offset + 2]);
|
|
||||||
if (nullptr != m_insideTester &&
|
|
||||||
(*m_insideTester)(point) != CGAL::ON_UNBOUNDED_SIDE) {
|
|
||||||
points->push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ClothSimulator::ClothSimulator(const std::vector<QVector3D> &vertices,
|
|
||||||
const std::vector<std::vector<size_t>> &faces,
|
|
||||||
const std::vector<QVector3D> &collisionVertices,
|
|
||||||
const std::vector<std::vector<size_t>> &collisionTriangles,
|
|
||||||
const std::vector<QVector3D> &externalForces) :
|
|
||||||
m_vertices(vertices),
|
|
||||||
m_faces(faces),
|
|
||||||
m_collisionVertices(collisionVertices),
|
|
||||||
m_collisionTriangles(collisionTriangles),
|
|
||||||
m_externalForces(externalForces)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ClothSimulator::~ClothSimulator()
|
|
||||||
{
|
|
||||||
delete m_massSpringSystem;
|
|
||||||
delete m_massSpringSolver;
|
|
||||||
delete m_rootNode;
|
|
||||||
delete m_deformationNode;
|
|
||||||
delete m_meshCollisionNode;
|
|
||||||
delete m_fixNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClothSimulator::setStiffness(float stiffness)
|
|
||||||
{
|
|
||||||
m_stiffness = 1.0f + 5.0f * stiffness;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClothSimulator::convertMeshToCloth()
|
|
||||||
{
|
|
||||||
m_clothPointSources.reserve(m_vertices.size());
|
|
||||||
m_clothPointBuffer.reserve(m_vertices.size() * 3);
|
|
||||||
|
|
||||||
std::map<size_t, size_t> oldVertexToNewMap;
|
|
||||||
auto addPoint = [&](size_t index) {
|
|
||||||
auto findNew = oldVertexToNewMap.find(index);
|
|
||||||
if (findNew != oldVertexToNewMap.end())
|
|
||||||
return findNew->second;
|
|
||||||
const auto &position = m_vertices[index];
|
|
||||||
m_clothPointBuffer.push_back(position.x());
|
|
||||||
m_clothPointBuffer.push_back(position.y());
|
|
||||||
m_clothPointBuffer.push_back(position.z());
|
|
||||||
size_t newIndex = m_clothPointSources.size();
|
|
||||||
m_clothPointSources.push_back(index);
|
|
||||||
oldVertexToNewMap.insert({index, newIndex});
|
|
||||||
return newIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::set<std::pair<size_t, size_t>> oldEdges;
|
|
||||||
for (const auto &it: m_faces) {
|
|
||||||
for (size_t i = 0; i < it.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % it.size();
|
|
||||||
if (oldEdges.find(std::make_pair(it[i], it[j])) != oldEdges.end())
|
|
||||||
continue;
|
|
||||||
m_clothSprings.push_back({addPoint(it[i]), addPoint(it[j])});
|
|
||||||
oldEdges.insert({it[i], it[j]});
|
|
||||||
oldEdges.insert({it[j], it[i]});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClothSimulator::getCurrentVertices(std::vector<QVector3D> *currentVertices)
|
|
||||||
{
|
|
||||||
*currentVertices = m_vertices;
|
|
||||||
for (size_t newIndex = 0; newIndex < m_clothPointSources.size(); ++newIndex) {
|
|
||||||
size_t oldIndex = m_clothPointSources[newIndex];
|
|
||||||
auto offset = newIndex * 3;
|
|
||||||
(*currentVertices)[oldIndex] = QVector3D(m_clothPointBuffer[offset + 0],
|
|
||||||
m_clothPointBuffer[offset + 1],
|
|
||||||
m_clothPointBuffer[offset + 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClothSimulator::step()
|
|
||||||
{
|
|
||||||
if (nullptr == m_massSpringSolver)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_massSpringSolver->solve(5);
|
|
||||||
m_massSpringSolver->solve(5);
|
|
||||||
|
|
||||||
CgSatisfyVisitor visitor;
|
|
||||||
visitor.satisfy(*m_rootNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClothSimulator::create()
|
|
||||||
{
|
|
||||||
convertMeshToCloth();
|
|
||||||
|
|
||||||
if (m_clothPointSources.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
float mass = 0.25f / m_clothPointSources.size();
|
|
||||||
float gravitationalForce = 9.8f * mass;
|
|
||||||
float damping = 0.993f; // damping, close to 1.0 | 0.993f;
|
|
||||||
float timeStep = 0.001f; //smaller for better results | 0.008f = 0.016f/2;
|
|
||||||
|
|
||||||
mass_spring_system::VectorXf masses(mass * mass_spring_system::VectorXf::Ones((unsigned int)m_clothSprings.size()));
|
|
||||||
|
|
||||||
mass_spring_system::EdgeList springList(m_clothSprings.size());
|
|
||||||
mass_spring_system::VectorXf restLengths(m_clothSprings.size());
|
|
||||||
mass_spring_system::VectorXf stiffnesses(m_clothSprings.size());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_clothSprings.size(); ++i) {
|
|
||||||
const auto &source = m_clothSprings[i];
|
|
||||||
springList[i] = mass_spring_system::Edge(source.first, source.second);
|
|
||||||
restLengths[i] = (m_vertices[m_clothPointSources[source.first]] - m_vertices[m_clothPointSources[source.second]]).length() * 0.8;
|
|
||||||
stiffnesses[i] = m_stiffness;
|
|
||||||
}
|
|
||||||
|
|
||||||
mass_spring_system::VectorXf fext(m_clothPointSources.size() * 3);
|
|
||||||
for (size_t i = 0; i < m_clothPointSources.size(); ++i) {
|
|
||||||
const auto &externalForce = m_externalForces[i] * gravitationalForce;
|
|
||||||
auto offset = i * 3;
|
|
||||||
fext[offset + 0] = externalForce.x();
|
|
||||||
fext[offset + 1] = externalForce.y();
|
|
||||||
fext[offset + 2] = externalForce.z();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_massSpringSystem = new mass_spring_system(m_clothPointSources.size(), m_clothSprings.size(), timeStep, springList, restLengths,
|
|
||||||
stiffnesses, masses, fext, damping);
|
|
||||||
|
|
||||||
m_massSpringSolver = new MassSpringSolver(m_massSpringSystem, m_clothPointBuffer.data());
|
|
||||||
|
|
||||||
// deformation constraint parameters
|
|
||||||
const float tauc = 0.12f; // critical spring deformation | 0.12f
|
|
||||||
const unsigned int deformIter = 15; // number of iterations | 15
|
|
||||||
|
|
||||||
std::vector<unsigned int> structSprintIndexList;
|
|
||||||
structSprintIndexList.reserve(springList.size());
|
|
||||||
m_deformationNode =
|
|
||||||
new CgSpringDeformationNode(m_massSpringSystem, m_clothPointBuffer.data(), tauc, deformIter);
|
|
||||||
m_deformationNode->addSprings(structSprintIndexList);
|
|
||||||
|
|
||||||
m_rootNode = new CgRootNode(m_massSpringSystem, m_clothPointBuffer.data());
|
|
||||||
m_rootNode->addChild(m_deformationNode);
|
|
||||||
|
|
||||||
m_meshCollisionNode = new CgMeshCollisionNode(m_massSpringSystem, m_clothPointBuffer.data(),
|
|
||||||
m_collisionVertices,
|
|
||||||
m_collisionTriangles);
|
|
||||||
|
|
||||||
m_fixNode = new CgPointFixNode(m_massSpringSystem, m_clothPointBuffer.data());
|
|
||||||
m_meshCollisionNode->fixPoints(m_fixNode);
|
|
||||||
m_deformationNode->addChild(m_fixNode);
|
|
||||||
|
|
||||||
m_rootNode->addChild(m_meshCollisionNode);
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
#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 CgPointFixNode;
|
|
||||||
|
|
||||||
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,
|
|
||||||
const std::vector<QVector3D> &externalForces);
|
|
||||||
~ClothSimulator();
|
|
||||||
void setStiffness(float stiffness);
|
|
||||||
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<QVector3D> m_externalForces;
|
|
||||||
std::vector<float> m_clothPointBuffer;
|
|
||||||
std::vector<size_t> m_clothPointSources;
|
|
||||||
std::vector<std::pair<size_t, size_t>> m_clothSprings;
|
|
||||||
float m_stiffness = 1.0f;
|
|
||||||
QVector3D m_offset;
|
|
||||||
mass_spring_system *m_massSpringSystem = nullptr;
|
|
||||||
MassSpringSolver *m_massSpringSolver = nullptr;
|
|
||||||
CgRootNode *m_rootNode = nullptr;
|
|
||||||
CgSpringDeformationNode *m_deformationNode = nullptr;
|
|
||||||
CgMeshCollisionNode *m_meshCollisionNode = nullptr;
|
|
||||||
CgPointFixNode *m_fixNode = nullptr;
|
|
||||||
void convertMeshToCloth();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,6 +0,0 @@
|
||||||
#include <QObject>
|
|
||||||
#include "componentlayer.h"
|
|
||||||
|
|
||||||
IMPL_ComponentLayerToString
|
|
||||||
IMPL_ComponentLayerFromString
|
|
||||||
IMPL_ComponentLayerToDispName
|
|
|
@ -1,49 +0,0 @@
|
||||||
#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
|
|
|
@ -1,201 +0,0 @@
|
||||||
#include <QDebug>
|
|
||||||
#include <QApplication>
|
|
||||||
#include "cutdocument.h"
|
|
||||||
|
|
||||||
const float CutDocument::m_nodeRadius = 0.05;
|
|
||||||
const float CutDocument::m_nodeScaleFactor = 0.5;
|
|
||||||
|
|
||||||
bool CutDocument::hasPastableNodesInClipboard() const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CutDocument::originSettled() const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CutDocument::isNodeEditable(QUuid nodeId) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(nodeId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CutDocument::isEdgeEditable(QUuid edgeId) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(edgeId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::copyNodes(std::set<QUuid> nodeIdSet) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(nodeIdSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::saveHistoryItem()
|
|
||||||
{
|
|
||||||
CutHistoryItem item;
|
|
||||||
toCutTemplate(item.cutTemplate);
|
|
||||||
m_undoItems.push_back(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CutDocument::undoable() const
|
|
||||||
{
|
|
||||||
return m_undoItems.size() >= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CutDocument::redoable() const
|
|
||||||
{
|
|
||||||
return !m_redoItems.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::undo()
|
|
||||||
{
|
|
||||||
if (!undoable())
|
|
||||||
return;
|
|
||||||
m_redoItems.push_back(m_undoItems.back());
|
|
||||||
m_undoItems.pop_back();
|
|
||||||
const auto &item = m_undoItems.back();
|
|
||||||
fromCutTemplate(item.cutTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::redo()
|
|
||||||
{
|
|
||||||
if (m_redoItems.empty())
|
|
||||||
return;
|
|
||||||
m_undoItems.push_back(m_redoItems.back());
|
|
||||||
const auto &item = m_redoItems.back();
|
|
||||||
fromCutTemplate(item.cutTemplate);
|
|
||||||
m_redoItems.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::paste()
|
|
||||||
{
|
|
||||||
// void
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::reset()
|
|
||||||
{
|
|
||||||
nodeMap.clear();
|
|
||||||
edgeMap.clear();
|
|
||||||
partMap.clear();
|
|
||||||
m_cutNodeIds.clear();
|
|
||||||
emit cleanup();
|
|
||||||
emit cutTemplateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::clearHistories()
|
|
||||||
{
|
|
||||||
m_undoItems.clear();
|
|
||||||
m_redoItems.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::toCutTemplate(std::vector<QVector2D> &cutTemplate)
|
|
||||||
{
|
|
||||||
for (const auto &nodeId: m_cutNodeIds) {
|
|
||||||
auto findNode = nodeMap.find(nodeId);
|
|
||||||
if (findNode == nodeMap.end())
|
|
||||||
continue;
|
|
||||||
QVector2D position = nodeToCutPosition({findNode->second.getX(),
|
|
||||||
findNode->second.getY(),
|
|
||||||
findNode->second.getZ()
|
|
||||||
});
|
|
||||||
cutTemplate.push_back(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector2D CutDocument::nodeToCutPosition(const QVector3D &nodePosition)
|
|
||||||
{
|
|
||||||
return {(nodePosition.x() * 2 - (float)1) / m_nodeScaleFactor,
|
|
||||||
(nodePosition.y() * 2 - (float)1) / m_nodeScaleFactor};
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector3D CutDocument::cutToNodePosition(const QVector2D &cutPosition)
|
|
||||||
{
|
|
||||||
return {(cutPosition.x() * m_nodeScaleFactor + 1) * (float)0.5,
|
|
||||||
(cutPosition.y() * m_nodeScaleFactor + 1) * (float)0.5,
|
|
||||||
(float)0};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::fromCutTemplate(const std::vector<QVector2D> &cutTemplate)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
|
|
||||||
std::set<QUuid> newAddedNodeIds;
|
|
||||||
std::set<QUuid> newAddedEdgeIds;
|
|
||||||
|
|
||||||
m_partId = QUuid::createUuid();
|
|
||||||
auto &part = partMap[m_partId];
|
|
||||||
part.id = m_partId;
|
|
||||||
|
|
||||||
for (const auto &position: cutTemplate) {
|
|
||||||
SkeletonNode node;
|
|
||||||
node.partId = m_partId;
|
|
||||||
node.id = QUuid::createUuid();
|
|
||||||
node.setRadius(m_nodeRadius);
|
|
||||||
auto nodePosition = cutToNodePosition(position);
|
|
||||||
node.setX(nodePosition.x());
|
|
||||||
node.setY(nodePosition.y());
|
|
||||||
node.setZ(nodePosition.z());
|
|
||||||
nodeMap[node.id] = node;
|
|
||||||
newAddedNodeIds.insert(node.id);
|
|
||||||
m_cutNodeIds.push_back(node.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_cutNodeIds.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % m_cutNodeIds.size();
|
|
||||||
const QUuid &firstNodeId = m_cutNodeIds[i];
|
|
||||||
const QUuid &secondNodeId = m_cutNodeIds[j];
|
|
||||||
|
|
||||||
SkeletonEdge edge;
|
|
||||||
edge.partId = m_partId;
|
|
||||||
edge.id = QUuid::createUuid();
|
|
||||||
edge.nodeIds.push_back(firstNodeId);
|
|
||||||
edge.nodeIds.push_back(secondNodeId);
|
|
||||||
edgeMap[edge.id] = edge;
|
|
||||||
newAddedEdgeIds.insert(edge.id);
|
|
||||||
nodeMap[firstNodeId].edgeIds.push_back(edge.id);
|
|
||||||
nodeMap[secondNodeId].edgeIds.push_back(edge.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &nodeIt: newAddedNodeIds) {
|
|
||||||
emit nodeAdded(nodeIt);
|
|
||||||
}
|
|
||||||
for (const auto &edgeIt: newAddedEdgeIds) {
|
|
||||||
emit edgeAdded(edgeIt);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit cutTemplateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
|
|
||||||
{
|
|
||||||
auto it = nodeMap.find(nodeId);
|
|
||||||
if (it == nodeMap.end()) {
|
|
||||||
qDebug() << "Find node failed:" << nodeId;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
it->second.addX(x);
|
|
||||||
it->second.addY(y);
|
|
||||||
it->second.addZ(z);
|
|
||||||
emit nodeOriginChanged(it->first);
|
|
||||||
emit cutTemplateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
|
|
||||||
{
|
|
||||||
auto it = nodeMap.find(nodeId);
|
|
||||||
if (it == nodeMap.end()) {
|
|
||||||
qDebug() << "Find node failed:" << nodeId;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
it->second.setX(x);
|
|
||||||
it->second.setY(y);
|
|
||||||
it->second.setZ(z);
|
|
||||||
auto part = partMap.find(it->second.partId);
|
|
||||||
if (part != partMap.end())
|
|
||||||
part->second.dirty = true;
|
|
||||||
emit nodeOriginChanged(nodeId);
|
|
||||||
emit cutTemplateChanged();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
#ifndef DUST3D_CUT_DOCUMENT_H
|
|
||||||
#define DUST3D_CUT_DOCUMENT_H
|
|
||||||
#include <deque>
|
|
||||||
#include <QVector2D>
|
|
||||||
#include "skeletondocument.h"
|
|
||||||
|
|
||||||
struct CutHistoryItem
|
|
||||||
{
|
|
||||||
std::vector<QVector2D> cutTemplate;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CutDocument : public SkeletonDocument
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
signals:
|
|
||||||
void cleanup();
|
|
||||||
void nodeAdded(QUuid nodeId);
|
|
||||||
void edgeAdded(QUuid edgeId);
|
|
||||||
void nodeOriginChanged(QUuid nodeId);
|
|
||||||
void cutTemplateChanged();
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool undoable() const override;
|
|
||||||
bool redoable() const override;
|
|
||||||
bool hasPastableNodesInClipboard() const override;
|
|
||||||
bool originSettled() const override;
|
|
||||||
bool isNodeEditable(QUuid nodeId) const override;
|
|
||||||
bool isEdgeEditable(QUuid edgeId) const override;
|
|
||||||
void copyNodes(std::set<QUuid> nodeIdSet) const override;
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
void toCutTemplate(std::vector<QVector2D> &cutTemplate);
|
|
||||||
void fromCutTemplate(const std::vector<QVector2D> &cutTemplate);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void saveHistoryItem();
|
|
||||||
void clearHistories();
|
|
||||||
void undo() override;
|
|
||||||
void redo() override;
|
|
||||||
void paste() override;
|
|
||||||
|
|
||||||
void moveNodeBy(QUuid nodeId, float x, float y, float z);
|
|
||||||
void setNodeOrigin(QUuid nodeId, float x, float y, float z);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const float m_nodeRadius;
|
|
||||||
static const float m_nodeScaleFactor;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::deque<CutHistoryItem> m_undoItems;
|
|
||||||
std::deque<CutHistoryItem> m_redoItems;
|
|
||||||
std::vector<QUuid> m_cutNodeIds;
|
|
||||||
QUuid m_partId;
|
|
||||||
|
|
||||||
QVector2D nodeToCutPosition(const QVector3D &nodePosition);
|
|
||||||
QVector3D cutToNodePosition(const QVector2D &cutPosition);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,206 +0,0 @@
|
||||||
#include <QString>
|
|
||||||
#include <vector>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <queue>
|
|
||||||
#include "cyclefinder.h"
|
|
||||||
#include "shortestpath.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// The cycle finder implement the following method:
|
|
||||||
// 1. Remove edge
|
|
||||||
// 2. Find shortest alternative path between the removed edge
|
|
||||||
// https://www.geeksforgeeks.org/find-minimum-weight-cycle-undirected-graph/
|
|
||||||
//
|
|
||||||
|
|
||||||
CycleFinder::CycleFinder(const std::vector<QVector3D> &nodePositions,
|
|
||||||
const std::vector<std::pair<size_t, size_t>> &edges) :
|
|
||||||
m_nodeNum(nodePositions.size()),
|
|
||||||
m_nodePositions(nodePositions),
|
|
||||||
m_edges(edges)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void CycleFinder::prepareWeights()
|
|
||||||
{
|
|
||||||
m_edgeLengths.resize(m_edges.size(), 1);
|
|
||||||
for (size_t i = 0; i < m_edges.size(); ++i) {
|
|
||||||
const auto &edge = m_edges[i];
|
|
||||||
auto length = m_nodePositions[edge.first].distanceToPoint(
|
|
||||||
m_nodePositions[edge.second]) * 10000;
|
|
||||||
m_edgeLengths[i] = length;
|
|
||||||
m_edgeLengthMap.insert({std::make_pair(edge.first, edge.second), length});
|
|
||||||
m_edgeLengthMap.insert({std::make_pair(edge.second, edge.first), length});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CycleFinder::validateCycleByFlatness(const std::vector<size_t> &cycle)
|
|
||||||
{
|
|
||||||
// Validate cycle by mesaure the flatness of the face
|
|
||||||
// Flatness = Average variation of corner normals
|
|
||||||
if (cycle.empty())
|
|
||||||
return false;
|
|
||||||
std::vector<QVector3D> normals;
|
|
||||||
for (size_t i = 0; i < cycle.size(); ++i) {
|
|
||||||
size_t h = (i + cycle.size() - 1) % cycle.size();
|
|
||||||
size_t j = (i + 1) % cycle.size();
|
|
||||||
QVector3D vh = m_nodePositions[cycle[h]];
|
|
||||||
QVector3D vi = m_nodePositions[cycle[i]];
|
|
||||||
QVector3D vj = m_nodePositions[cycle[j]];
|
|
||||||
if (QVector3D::dotProduct((vj - vi).normalized(),
|
|
||||||
(vi - vh).normalized()) <= 0.966) { // 15 degrees
|
|
||||||
auto vertexNormal = QVector3D::normal(vj - vi, vh - vi);
|
|
||||||
normals.push_back(vertexNormal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (normals.empty())
|
|
||||||
return false;
|
|
||||||
float sumOfDistance = 0;
|
|
||||||
for (size_t i = 0; i < normals.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % normals.size();
|
|
||||||
sumOfDistance += (normals[i] - normals[j]).length();
|
|
||||||
}
|
|
||||||
float flatness = sumOfDistance / normals.size();
|
|
||||||
if (flatness >= m_invalidFlatness) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CycleFinder::calculateCycleLength(const std::vector<size_t> &cycle)
|
|
||||||
{
|
|
||||||
float cycleLength = 0;
|
|
||||||
for (size_t i = 0; i < cycle.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % cycle.size();
|
|
||||||
auto edge = std::make_pair(cycle[i], cycle[j]);
|
|
||||||
auto findEdgeLength = m_edgeLengthMap.find(edge);
|
|
||||||
if (findEdgeLength == m_edgeLengthMap.end())
|
|
||||||
continue;
|
|
||||||
cycleLength += findEdgeLength->second;
|
|
||||||
}
|
|
||||||
return cycleLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CycleFinder::find()
|
|
||||||
{
|
|
||||||
prepareWeights();
|
|
||||||
|
|
||||||
if (m_edges.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::queue<std::pair<size_t, size_t>> waitEdges;
|
|
||||||
std::set<std::pair<size_t, size_t>> visited;
|
|
||||||
std::map<std::pair<size_t, size_t>, size_t> halfEdgeToCycleMap;
|
|
||||||
|
|
||||||
auto isPathValid = [&](const std::vector<size_t> &path) {
|
|
||||||
for (size_t i = 0; i < path.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % path.size();
|
|
||||||
auto edge = std::make_pair(path[i], path[j]);
|
|
||||||
if (m_halfEdges.find(edge) != m_halfEdges.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::map<size_t, int> oppositeCycleEdgeLengths;
|
|
||||||
for (size_t i = 0; i < path.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % path.size();
|
|
||||||
auto oppositeEdge = std::make_pair(path[j], path[i]);
|
|
||||||
auto findOpposite = halfEdgeToCycleMap.find(oppositeEdge);
|
|
||||||
if (findOpposite == halfEdgeToCycleMap.end())
|
|
||||||
continue;
|
|
||||||
oppositeCycleEdgeLengths[findOpposite->second] += m_edgeLengthMap[oppositeEdge];
|
|
||||||
}
|
|
||||||
for (const auto &it: oppositeCycleEdgeLengths) {
|
|
||||||
if (it.first >= m_cycleLengths.size()) {
|
|
||||||
qDebug() << "Find cycle length failed, should not happen";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto &fullLength = m_cycleLengths[it.first];
|
|
||||||
if (fullLength <= 0) {
|
|
||||||
qDebug() << "Cycle length invalid, should not happen";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((float)it.second / fullLength >= 0.5) {
|
|
||||||
// Half of the edges (measured by sum of length) have been used by opposite face
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!validateCycleByFlatness(path))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<std::pair<size_t, size_t>, size_t> edgeIndexMap;
|
|
||||||
for (size_t i = 0; i < m_edges.size(); ++i) {
|
|
||||||
const auto &edge = m_edges[i];
|
|
||||||
edgeIndexMap.insert({edge, i});
|
|
||||||
edgeIndexMap.insert({std::make_pair(edge.second, edge.first), i});
|
|
||||||
}
|
|
||||||
|
|
||||||
waitEdges.push(m_edges[0]);
|
|
||||||
while (!waitEdges.empty()) {
|
|
||||||
auto currentEdge = waitEdges.front();
|
|
||||||
waitEdges.pop();
|
|
||||||
if (visited.find(currentEdge) != visited.end())
|
|
||||||
continue;
|
|
||||||
visited.insert(currentEdge);
|
|
||||||
|
|
||||||
auto edges = m_edges;
|
|
||||||
auto weights = m_edgeLengths;
|
|
||||||
std::vector<size_t> path;
|
|
||||||
removeEdgeFrom(currentEdge, &edges, &weights);
|
|
||||||
if (!shortestPath(m_nodeNum, edges, weights, currentEdge.first, currentEdge.second, &path))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool isValid = isPathValid(path);
|
|
||||||
|
|
||||||
if (!isValid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < path.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % path.size();
|
|
||||||
auto edge = std::make_pair(path[i], path[j]);
|
|
||||||
m_halfEdges.insert(edge);
|
|
||||||
halfEdgeToCycleMap.insert({edge, m_cycles.size()});
|
|
||||||
}
|
|
||||||
|
|
||||||
m_cycles.push_back(path);
|
|
||||||
m_cycleLengths.push_back(calculateCycleLength(path));
|
|
||||||
|
|
||||||
// Update weights
|
|
||||||
for (size_t i = 0; i < path.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % path.size();
|
|
||||||
auto edge = std::make_pair(path[i], path[j]);
|
|
||||||
auto index = edgeIndexMap[edge];
|
|
||||||
m_edgeLengths[index] += 100000;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add opposite edges to wait queue
|
|
||||||
for (size_t i = 0; i < path.size(); ++i) {
|
|
||||||
size_t j = (i + 1) % path.size();
|
|
||||||
auto oppositeEdge = std::make_pair(path[j], path[i]);
|
|
||||||
if (visited.find(oppositeEdge) != visited.end())
|
|
||||||
continue;
|
|
||||||
waitEdges.push(oppositeEdge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::vector<size_t>> &CycleFinder::getCycles()
|
|
||||||
{
|
|
||||||
return m_cycles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CycleFinder::removeEdgeFrom(const std::pair<size_t, size_t> &edge,
|
|
||||||
std::vector<std::pair<size_t, size_t>> *edges,
|
|
||||||
std::vector<int> *edgeLengths)
|
|
||||||
{
|
|
||||||
auto oppositeEdge = std::make_pair(edge.second, edge.first);
|
|
||||||
for (auto it = edges->begin(); it != edges->end(); ++it) {
|
|
||||||
if (*it == edge || *it == oppositeEdge) {
|
|
||||||
auto index = it - edges->begin();
|
|
||||||
edgeLengths->erase(edgeLengths->begin() + index);
|
|
||||||
edges->erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
#ifndef DUST3D_CYCLE_FINDER
|
|
||||||
#define DUST3D_CYCLE_FINDER
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
#include <QVector3D>
|
|
||||||
|
|
||||||
class CycleFinder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CycleFinder(const std::vector<QVector3D> &nodePositions,
|
|
||||||
const std::vector<std::pair<size_t, size_t>> &edges);
|
|
||||||
void find();
|
|
||||||
const std::vector<std::vector<size_t>> &getCycles();
|
|
||||||
private:
|
|
||||||
size_t m_nodeNum = 0;
|
|
||||||
std::vector<QVector3D> m_nodePositions;
|
|
||||||
std::vector<std::pair<size_t, size_t>> m_edges;
|
|
||||||
std::vector<int> m_edgeLengths;
|
|
||||||
std::map<std::pair<size_t, size_t>, int> m_edgeLengthMap;
|
|
||||||
std::vector<std::vector<size_t>> m_cycles;
|
|
||||||
std::vector<int> m_cycleLengths;
|
|
||||||
std::set<std::pair<size_t, size_t>> m_cycleEdges;
|
|
||||||
std::set<std::pair<size_t, size_t>> m_halfEdges;
|
|
||||||
float m_invalidFlatness = 1.0;
|
|
||||||
void removeEdgeFrom(const std::pair<size_t, size_t> &edge,
|
|
||||||
std::vector<std::pair<size_t, size_t>> *edges,
|
|
||||||
std::vector<int> *edgeLengths);
|
|
||||||
void prepareWeights();
|
|
||||||
bool validateCycleByFlatness(const std::vector<size_t> &cycle);
|
|
||||||
int calculateCycleLength(const std::vector<size_t> &cycle);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
#include "meshgenerator.h"
|
#include "meshgenerator.h"
|
||||||
|
|
||||||
unsigned long Document::m_maxSnapshot = 1000;
|
unsigned long Document::m_maxSnapshot = 1000;
|
||||||
const float Component::defaultClothStiffness = 0.5f;
|
|
||||||
const size_t Component::defaultClothIteration = 350;
|
|
||||||
|
|
||||||
Document::Document() :
|
Document::Document() :
|
||||||
SkeletonDocument(),
|
SkeletonDocument(),
|
||||||
|
@ -1230,16 +1228,6 @@ 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);
|
|
||||||
if (componentIt.second.clothStiffnessAdjusted())
|
|
||||||
component["clothStiffness"] = QString::number(componentIt.second.clothStiffness);
|
|
||||||
if (componentIt.second.clothIterationAdjusted())
|
|
||||||
component["clothIteration"] = QString::number(componentIt.second.clothIteration);
|
|
||||||
if (componentIt.second.clothForceAdjusted())
|
|
||||||
component["clothForce"] = ClothForceToString(componentIt.second.clothForce);
|
|
||||||
if (componentIt.second.clothOffsetAdjusted())
|
|
||||||
component["clothOffset"] = QString::number(componentIt.second.clothOffset);
|
|
||||||
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());
|
||||||
|
@ -1682,19 +1670,6 @@ void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource sou
|
||||||
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());
|
|
||||||
auto findClothStiffness = componentKv.second.find("clothStiffness");
|
|
||||||
if (findClothStiffness != componentKv.second.end())
|
|
||||||
component.clothStiffness = findClothStiffness->second.toFloat();
|
|
||||||
auto findClothIteration = componentKv.second.find("clothIteration");
|
|
||||||
if (findClothIteration != componentKv.second.end())
|
|
||||||
component.clothIteration = findClothIteration->second.toUInt();
|
|
||||||
auto findClothForce = componentKv.second.find("clothForce");
|
|
||||||
if (findClothForce != componentKv.second.end())
|
|
||||||
component.clothForce = ClothForceFromString(valueOfKeyInMapOrEmpty(componentKv.second, "clothForce").toUtf8().constData());
|
|
||||||
auto findClothOffset = componentKv.second.find("clothOffset");
|
|
||||||
if (findClothOffset != componentKv.second.end())
|
|
||||||
component.clothOffset = valueOfKeyInMapOrEmpty(componentKv.second, "clothOffset").toFloat();
|
|
||||||
//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)];
|
||||||
|
@ -2508,76 +2483,6 @@ 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::setComponentClothStiffness(QUuid componentId, float stiffness)
|
|
||||||
{
|
|
||||||
Component *component = (Component *)findComponent(componentId);
|
|
||||||
if (nullptr == component)
|
|
||||||
return;
|
|
||||||
if (qFuzzyCompare(component->clothStiffness, stiffness))
|
|
||||||
return;
|
|
||||||
|
|
||||||
component->clothStiffness = stiffness;
|
|
||||||
component->dirty = true;
|
|
||||||
emit componentClothStiffnessChanged(componentId);
|
|
||||||
emit skeletonChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Document::setComponentClothIteration(QUuid componentId, size_t iteration)
|
|
||||||
{
|
|
||||||
Component *component = (Component *)findComponent(componentId);
|
|
||||||
if (nullptr == component)
|
|
||||||
return;
|
|
||||||
if (component->clothIteration == iteration)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component->clothIteration = iteration;
|
|
||||||
component->dirty = true;
|
|
||||||
emit componentClothIterationChanged(componentId);
|
|
||||||
emit skeletonChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Document::setComponentClothForce(QUuid componentId, ClothForce force)
|
|
||||||
{
|
|
||||||
Component *component = (Component *)findComponent(componentId);
|
|
||||||
if (nullptr == component)
|
|
||||||
return;
|
|
||||||
if (component->clothForce == force)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component->clothForce = force;
|
|
||||||
component->dirty = true;
|
|
||||||
emit componentClothForceChanged(componentId);
|
|
||||||
emit skeletonChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Document::setComponentClothOffset(QUuid componentId, float offset)
|
|
||||||
{
|
|
||||||
Component *component = (Component *)findComponent(componentId);
|
|
||||||
if (nullptr == component)
|
|
||||||
return;
|
|
||||||
if (qFuzzyCompare(component->clothOffset, offset))
|
|
||||||
return;
|
|
||||||
|
|
||||||
component->clothOffset = offset;
|
|
||||||
component->dirty = true;
|
|
||||||
emit componentClothOffsetChanged(componentId);
|
|
||||||
emit skeletonChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Document::createNewComponentAndMoveThisIn(QUuid componentId)
|
void Document::createNewComponentAndMoveThisIn(QUuid componentId)
|
||||||
{
|
{
|
||||||
auto component = componentMap.find(componentId);
|
auto component = componentMap.find(componentId);
|
||||||
|
|
|
@ -26,8 +26,6 @@
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "paintmode.h"
|
#include "paintmode.h"
|
||||||
#include "proceduralanimation.h"
|
#include "proceduralanimation.h"
|
||||||
#include "componentlayer.h"
|
|
||||||
#include "clothforce.h"
|
|
||||||
#include "texturepainter.h"
|
#include "texturepainter.h"
|
||||||
|
|
||||||
class MaterialPreviewsGenerator;
|
class MaterialPreviewsGenerator;
|
||||||
|
@ -45,8 +43,6 @@ public:
|
||||||
class Component
|
class Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const float defaultClothStiffness;
|
|
||||||
static const size_t defaultClothIteration;
|
|
||||||
Component()
|
Component()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -69,11 +65,6 @@ 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;
|
|
||||||
float clothStiffness = defaultClothStiffness;
|
|
||||||
ClothForce clothForce = ClothForce::Gravitational;
|
|
||||||
float clothOffset = 0.0f;
|
|
||||||
size_t clothIteration = defaultClothIteration;
|
|
||||||
std::vector<QUuid> childrenIds;
|
std::vector<QUuid> childrenIds;
|
||||||
QString linkData() const
|
QString linkData() const
|
||||||
{
|
{
|
||||||
|
@ -193,22 +184,6 @@ public:
|
||||||
{
|
{
|
||||||
return smoothAllAdjusted() || smoothSeamAdjusted();
|
return smoothAllAdjusted() || smoothSeamAdjusted();
|
||||||
}
|
}
|
||||||
bool clothStiffnessAdjusted() const
|
|
||||||
{
|
|
||||||
return fabs(clothStiffness - Component::defaultClothStiffness) >= 0.01;
|
|
||||||
}
|
|
||||||
bool clothIterationAdjusted() const
|
|
||||||
{
|
|
||||||
return clothIteration != defaultClothIteration;
|
|
||||||
}
|
|
||||||
bool clothForceAdjusted() const
|
|
||||||
{
|
|
||||||
return ClothForce::Gravitational != clothForce;
|
|
||||||
}
|
|
||||||
bool clothOffsetAdjusted() const
|
|
||||||
{
|
|
||||||
return fabs(clothOffset - 0.0) >= 0.01;
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
std::set<QUuid> m_childrenIdSet;
|
std::set<QUuid> m_childrenIdSet;
|
||||||
};
|
};
|
||||||
|
@ -314,10 +289,6 @@ signals:
|
||||||
void componentSmoothSeamChanged(QUuid componentId);
|
void componentSmoothSeamChanged(QUuid componentId);
|
||||||
void componentPolyCountChanged(QUuid componentId);
|
void componentPolyCountChanged(QUuid componentId);
|
||||||
void componentLayerChanged(QUuid componentId);
|
void componentLayerChanged(QUuid componentId);
|
||||||
void componentClothStiffnessChanged(QUuid componentId);
|
|
||||||
void componentClothIterationChanged(QUuid componentId);
|
|
||||||
void componentClothForceChanged(QUuid componentId);
|
|
||||||
void componentClothOffsetChanged(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);
|
||||||
|
@ -585,11 +556,6 @@ 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 setComponentClothStiffness(QUuid componentId, float stiffness);
|
|
||||||
void setComponentClothIteration(QUuid componentId, size_t iteration);
|
|
||||||
void setComponentClothForce(QUuid componentId, ClothForce force);
|
|
||||||
void setComponentClothOffset(QUuid componentId, float offset);
|
|
||||||
void hideOtherComponents(QUuid componentId);
|
void hideOtherComponents(QUuid componentId);
|
||||||
void lockOtherComponents(QUuid componentId);
|
void lockOtherComponents(QUuid componentId);
|
||||||
void hideAllComponents();
|
void hideAllComponents();
|
||||||
|
|
|
@ -987,7 +987,6 @@ DocumentWindow::DocumentWindow() :
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentSmoothAll, m_document, &Document::setComponentSmoothAll);
|
connect(m_partTreeWidget, &PartTreeWidget::setComponentSmoothAll, m_document, &Document::setComponentSmoothAll);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentSmoothSeam, m_document, &Document::setComponentSmoothSeam);
|
connect(m_partTreeWidget, &PartTreeWidget::setComponentSmoothSeam, m_document, &Document::setComponentSmoothSeam);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentPolyCount, m_document, &Document::setComponentPolyCount);
|
connect(m_partTreeWidget, &PartTreeWidget::setComponentPolyCount, m_document, &Document::setComponentPolyCount);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentLayer, m_document, &Document::setComponentLayer);
|
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::moveComponent, m_document, &Document::moveComponent);
|
connect(m_partTreeWidget, &PartTreeWidget::moveComponent, m_document, &Document::moveComponent);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::removeComponent, m_document, &Document::removeComponent);
|
connect(m_partTreeWidget, &PartTreeWidget::removeComponent, m_document, &Document::removeComponent);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::hideOtherComponents, m_document, &Document::hideOtherComponents);
|
connect(m_partTreeWidget, &PartTreeWidget::hideOtherComponents, m_document, &Document::hideOtherComponents);
|
||||||
|
@ -1002,10 +1001,6 @@ DocumentWindow::DocumentWindow() :
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setPartVisibleState, m_document, &Document::setPartVisibleState);
|
connect(m_partTreeWidget, &PartTreeWidget::setPartVisibleState, m_document, &Document::setPartVisibleState);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setPartColorState, m_document, &Document::setPartColorState);
|
connect(m_partTreeWidget, &PartTreeWidget::setPartColorState, m_document, &Document::setPartColorState);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentCombineMode, m_document, &Document::setComponentCombineMode);
|
connect(m_partTreeWidget, &PartTreeWidget::setComponentCombineMode, m_document, &Document::setComponentCombineMode);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentClothStiffness, m_document, &Document::setComponentClothStiffness);
|
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentClothIteration, m_document, &Document::setComponentClothIteration);
|
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentClothForce, m_document, &Document::setComponentClothForce);
|
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setComponentClothOffset, m_document, &Document::setComponentClothOffset);
|
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setPartTarget, m_document, &Document::setPartTarget);
|
connect(m_partTreeWidget, &PartTreeWidget::setPartTarget, m_document, &Document::setPartTarget);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::setPartBase, m_document, &Document::setPartBase);
|
connect(m_partTreeWidget, &PartTreeWidget::setPartBase, m_document, &Document::setPartBase);
|
||||||
connect(m_partTreeWidget, &PartTreeWidget::hideDescendantComponents, m_document, &Document::hideDescendantComponents);
|
connect(m_partTreeWidget, &PartTreeWidget::hideDescendantComponents, m_document, &Document::hideDescendantComponents);
|
||||||
|
|
|
@ -17,11 +17,9 @@
|
||||||
#include "triangulatefaces.h"
|
#include "triangulatefaces.h"
|
||||||
#include "remesher.h"
|
#include "remesher.h"
|
||||||
#include "polycount.h"
|
#include "polycount.h"
|
||||||
#include "clothsimulator.h"
|
|
||||||
#include "isotropicremesh.h"
|
#include "isotropicremesh.h"
|
||||||
#include "projectfacestonodes.h"
|
#include "projectfacestonodes.h"
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
#include "simulateclothmeshes.h"
|
|
||||||
#include "meshstroketifier.h"
|
#include "meshstroketifier.h"
|
||||||
#include "fileforever.h"
|
#include "fileforever.h"
|
||||||
#include "snapshotxml.h"
|
#include "snapshotxml.h"
|
||||||
|
@ -915,11 +913,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -928,17 +921,9 @@ bool MeshGenerator::componentRemeshed(const std::map<QString, QString> *componen
|
||||||
{
|
{
|
||||||
if (nullptr == component)
|
if (nullptr == component)
|
||||||
return false;
|
return false;
|
||||||
bool isCloth = false;
|
|
||||||
if (ComponentLayer::Cloth == ComponentLayerFromString(valueOfKeyInMapOrEmpty(*component, "layer").toUtf8().constData())) {
|
|
||||||
if (nullptr != polyCountValue)
|
|
||||||
*polyCountValue = PolyCountToValue(PolyCount::VeryHighPoly);
|
|
||||||
isCloth = true;
|
|
||||||
}
|
|
||||||
auto polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(*component, "polyCount").toUtf8().constData());
|
auto polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(*component, "polyCount").toUtf8().constData());
|
||||||
if (nullptr != polyCountValue)
|
if (nullptr != polyCountValue)
|
||||||
*polyCountValue = PolyCountToValue(polyCount);
|
*polyCountValue = PolyCountToValue(polyCount);
|
||||||
if (isCloth)
|
|
||||||
return true;
|
|
||||||
return polyCount != PolyCount::Original;
|
return polyCount != PolyCount::Original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -967,47 +952,6 @@ 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
float MeshGenerator::componentClothStiffness(const std::map<QString, QString> *component)
|
|
||||||
{
|
|
||||||
if (nullptr == component)
|
|
||||||
return Component::defaultClothStiffness;
|
|
||||||
auto findClothStiffness = component->find("clothStiffness");
|
|
||||||
if (findClothStiffness == component->end())
|
|
||||||
return Component::defaultClothStiffness;
|
|
||||||
return findClothStiffness->second.toFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t MeshGenerator::componentClothIteration(const std::map<QString, QString> *component)
|
|
||||||
{
|
|
||||||
if (nullptr == component)
|
|
||||||
return Component::defaultClothIteration;
|
|
||||||
auto findClothIteration = component->find("clothIteration");
|
|
||||||
if (findClothIteration == component->end())
|
|
||||||
return Component::defaultClothIteration;
|
|
||||||
return findClothIteration->second.toUInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
ClothForce MeshGenerator::componentClothForce(const std::map<QString, QString> *component)
|
|
||||||
{
|
|
||||||
if (nullptr == component)
|
|
||||||
return ClothForce::Gravitational;
|
|
||||||
return ClothForceFromString(valueOfKeyInMapOrEmpty(*component, "clothForce").toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
float MeshGenerator::componentClothOffset(const std::map<QString, QString> *component)
|
|
||||||
{
|
|
||||||
if (nullptr == component)
|
|
||||||
return 0.0f;
|
|
||||||
return valueOfKeyInMapOrEmpty(*component, "clothOffset").toFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -1616,8 +1560,6 @@ 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";
|
||||||
|
@ -1638,90 +1580,6 @@ void MeshGenerator::collectUncombinedComponent(const QString &componentIdString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshGenerator::collectClothComponentIdStrings(const QString &componentIdString,
|
|
||||||
std::vector<QString> *componentIdStrings)
|
|
||||||
{
|
|
||||||
const auto &component = findComponent(componentIdString);
|
|
||||||
if (ComponentLayer::Cloth == componentLayer(component)) {
|
|
||||||
const auto &componentCache = m_cacheContext->components[componentIdString];
|
|
||||||
if (nullptr == componentCache.mesh) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
componentIdStrings->push_back(componentIdString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
|
||||||
if (childIdString.isEmpty())
|
|
||||||
continue;
|
|
||||||
collectClothComponentIdStrings(childIdString, componentIdStrings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
|
||||||
{
|
|
||||||
if (m_clothCollisionTriangles.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<QString> componentIdStrings;
|
|
||||||
collectClothComponentIdStrings(componentIdString, &componentIdStrings);
|
|
||||||
|
|
||||||
std::vector<ClothMesh> clothMeshes(componentIdStrings.size());
|
|
||||||
for (size_t i = 0; i < componentIdStrings.size(); ++i) {
|
|
||||||
const auto &componentIdString = componentIdStrings[i];
|
|
||||||
const auto &componentCache = m_cacheContext->components[componentIdString];
|
|
||||||
if (nullptr == componentCache.mesh) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto &component = findComponent(componentIdString);
|
|
||||||
auto &clothMesh = clothMeshes[i];
|
|
||||||
componentCache.mesh->fetch(clothMesh.vertices, clothMesh.faces);
|
|
||||||
clothMesh.clothForce = componentClothForce(component);
|
|
||||||
clothMesh.clothOffset = componentClothOffset(component);
|
|
||||||
clothMesh.clothStiffness = componentClothStiffness(component);
|
|
||||||
clothMesh.clothIteration = componentClothIteration(component);
|
|
||||||
clothMesh.objectNodeVertices = &componentCache.objectNodeVertices;
|
|
||||||
//m_object->clothNodes.insert(m_object->clothNodes.end(), componentCache.objectNodes.begin(), componentCache.objectNodes.end());
|
|
||||||
//m_object->nodes.insert(m_object->nodes.end(), componentCache.objectNodes.begin(), componentCache.objectNodes.end());
|
|
||||||
for (const auto &objectNode: componentCache.objectNodes) {
|
|
||||||
auto newNode = objectNode;
|
|
||||||
newNode.layer = ComponentLayer::Cloth;
|
|
||||||
m_object->nodes.push_back(newNode);
|
|
||||||
}
|
|
||||||
m_object->edges.insert(m_object->edges.end(), componentCache.objectEdges.begin(), componentCache.objectEdges.end());
|
|
||||||
}
|
|
||||||
simulateClothMeshes(&clothMeshes,
|
|
||||||
&m_clothCollisionVertices,
|
|
||||||
&m_clothCollisionTriangles);
|
|
||||||
for (auto &clothMesh: clothMeshes) {
|
|
||||||
auto vertexStartIndex = m_object->vertices.size();
|
|
||||||
auto updateVertexIndices = [=](std::vector<std::vector<size_t>> &faces) {
|
|
||||||
for (auto &it: faces) {
|
|
||||||
for (auto &subIt: it)
|
|
||||||
subIt += vertexStartIndex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
updateVertexIndices(clothMesh.faces);
|
|
||||||
m_object->vertices.insert(m_object->vertices.end(), clothMesh.vertices.begin(), clothMesh.vertices.end());
|
|
||||||
for (const auto &it: clothMesh.faces) {
|
|
||||||
if (4 == it.size()) {
|
|
||||||
m_object->triangles.push_back(std::vector<size_t> {
|
|
||||||
it[0], it[1], it[2]
|
|
||||||
});
|
|
||||||
m_object->triangles.push_back(std::vector<size_t> {
|
|
||||||
it[2], it[3], it[0]
|
|
||||||
});
|
|
||||||
} else if (3 == it.size()) {
|
|
||||||
m_object->triangles.push_back(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_object->triangleAndQuads.insert(m_object->triangleAndQuads.end(), clothMesh.faces.begin(), clothMesh.faces.end());
|
|
||||||
for (size_t i = 0; i < clothMesh.vertices.size(); ++i) {
|
|
||||||
const auto &source = clothMesh.vertexSources[i];
|
|
||||||
m_nodeVertices.push_back(std::make_pair(clothMesh.vertices[i], source));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -1968,25 +1826,6 @@ void MeshGenerator::generate()
|
||||||
collectUncombinedComponent(QUuid().toString());
|
collectUncombinedComponent(QUuid().toString());
|
||||||
collectIncombinableComponentMeshes(QUuid().toString());
|
collectIncombinableComponentMeshes(QUuid().toString());
|
||||||
|
|
||||||
// Fetch nodes as body nodes before cloth nodes collecting
|
|
||||||
//std::set<std::pair<QUuid, QUuid>> bodyNodeMap;
|
|
||||||
//m_object->bodyNodes.reserve(m_object->nodes.size());
|
|
||||||
//for (const auto &it: m_object->nodes) {
|
|
||||||
// if (it.joined) {
|
|
||||||
// bodyNodeMap.insert({it.partId, it.nodeId});
|
|
||||||
// m_object->bodyNodes.push_back(it);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//m_object->bodyEdges.reserve(m_object->edges.size());
|
|
||||||
//for (const auto &it: m_object->edges) {
|
|
||||||
// if (bodyNodeMap.find(it.first) == bodyNodeMap.end())
|
|
||||||
// continue;
|
|
||||||
// if (bodyNodeMap.find(it.second) == bodyNodeMap.end())
|
|
||||||
// continue;
|
|
||||||
// m_object->bodyEdges.push_back(it);
|
|
||||||
//}
|
|
||||||
|
|
||||||
collectClothComponent(QUuid().toString());
|
|
||||||
collectErroredParts();
|
collectErroredParts();
|
||||||
postprocessObject(m_object);
|
postprocessObject(m_object);
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
#include "snapshot.h"
|
#include "snapshot.h"
|
||||||
#include "combinemode.h"
|
#include "combinemode.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include "componentlayer.h"
|
|
||||||
#include "clothforce.h"
|
|
||||||
|
|
||||||
class GeneratedPart
|
class GeneratedPart
|
||||||
{
|
{
|
||||||
|
@ -170,15 +168,7 @@ 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);
|
|
||||||
float componentClothStiffness(const std::map<QString, QString> *component);
|
|
||||||
size_t componentClothIteration(const std::map<QString, QString> *component);
|
|
||||||
ClothForce componentClothForce(const std::map<QString, QString> *component);
|
|
||||||
float componentClothOffset(const std::map<QString, QString> *component);
|
|
||||||
void collectUncombinedComponent(const QString &componentIdString);
|
void collectUncombinedComponent(const QString &componentIdString);
|
||||||
void collectClothComponent(const QString &componentIdString);
|
|
||||||
void collectClothComponentIdStrings(const QString &componentIdString,
|
|
||||||
std::vector<QString> *componentIdStrings);
|
|
||||||
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
||||||
void remesh(const std::vector<ObjectNode> &inputNodes,
|
void remesh(const std::vector<ObjectNode> &inputNodes,
|
||||||
const std::vector<std::tuple<QVector3D, float, size_t>> &interpolatedNodes,
|
const std::vector<std::tuple<QVector3D, float, size_t>> &interpolatedNodes,
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
#include <QRectF>
|
#include <QRectF>
|
||||||
#include "bonemark.h"
|
#include "bonemark.h"
|
||||||
#include "componentlayer.h"
|
|
||||||
|
|
||||||
struct ObjectNode
|
struct ObjectNode
|
||||||
{
|
{
|
||||||
|
@ -26,7 +25,6 @@ struct ObjectNode
|
||||||
QUuid mirroredByPartId;
|
QUuid mirroredByPartId;
|
||||||
BoneMark boneMark = BoneMark::None;
|
BoneMark boneMark = BoneMark::None;
|
||||||
QVector3D direction;
|
QVector3D direction;
|
||||||
ComponentLayer layer = ComponentLayer::Body;
|
|
||||||
bool joined = true;
|
bool joined = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,6 @@ void saveObjectToXmlStream(const Object *object, QXmlStreamWriter *writer)
|
||||||
writer->writeAttribute("mirroredByPartId", node.mirroredByPartId.toString());
|
writer->writeAttribute("mirroredByPartId", node.mirroredByPartId.toString());
|
||||||
if (node.boneMark != BoneMark::None)
|
if (node.boneMark != BoneMark::None)
|
||||||
writer->writeAttribute("boneMark", BoneMarkToString(node.boneMark));
|
writer->writeAttribute("boneMark", BoneMarkToString(node.boneMark));
|
||||||
if (ComponentLayer::Body != node.layer)
|
|
||||||
writer->writeAttribute("layer", ComponentLayerToString(node.layer));
|
|
||||||
if (!node.joined)
|
if (!node.joined)
|
||||||
writer->writeAttribute("joined", "false");
|
writer->writeAttribute("joined", "false");
|
||||||
writer->writeEndElement();
|
writer->writeEndElement();
|
||||||
|
@ -250,7 +248,6 @@ void loadObjectFromXmlStream(Object *object, QXmlStreamReader &reader)
|
||||||
node.mirrorFromPartId = QUuid(reader.attributes().value("mirrorFromPartId").toString());
|
node.mirrorFromPartId = QUuid(reader.attributes().value("mirrorFromPartId").toString());
|
||||||
node.mirroredByPartId = QUuid(reader.attributes().value("mirroredByPartId").toString());
|
node.mirroredByPartId = QUuid(reader.attributes().value("mirroredByPartId").toString());
|
||||||
node.boneMark = BoneMarkFromString(reader.attributes().value("boneMark").toString().toUtf8().constData());
|
node.boneMark = BoneMarkFromString(reader.attributes().value("boneMark").toString().toUtf8().constData());
|
||||||
node.layer = ComponentLayerFromString(reader.attributes().value("layer").toString().toUtf8().constData());
|
|
||||||
QString joinedString = reader.attributes().value("joined").toString();
|
QString joinedString = reader.attributes().value("joined").toString();
|
||||||
if (!joinedString.isEmpty())
|
if (!joinedString.isEmpty())
|
||||||
node.joined = isTrueValueString(joinedString);
|
node.joined = isTrueValueString(joinedString);
|
||||||
|
|
|
@ -257,119 +257,6 @@ void PartTreeWidget::mousePressEvent(QMouseEvent *event)
|
||||||
handleSingleClick(event->pos());
|
handleSingleClick(event->pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartTreeWidget::showClothSettingMenu(const QPoint &pos, const QUuid &componentId)
|
|
||||||
{
|
|
||||||
const Component *component = nullptr;
|
|
||||||
|
|
||||||
if (componentId.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
component = m_document->findComponent(componentId);
|
|
||||||
if (nullptr == component)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QMenu popupMenu;
|
|
||||||
|
|
||||||
QWidget *popup = new QWidget;
|
|
||||||
|
|
||||||
FloatNumberWidget *clothStiffnessWidget = new FloatNumberWidget;
|
|
||||||
clothStiffnessWidget->setItemName(tr("Stiffness"));
|
|
||||||
clothStiffnessWidget->setRange(0.0f, 1.0f);
|
|
||||||
clothStiffnessWidget->setValue(component->clothStiffness);
|
|
||||||
|
|
||||||
connect(clothStiffnessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
|
||||||
emit setComponentClothStiffness(componentId, value);
|
|
||||||
emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
|
|
||||||
QPushButton *clothStiffnessEraser = new QPushButton(QChar(fa::eraser));
|
|
||||||
Theme::initAwesomeToolButton(clothStiffnessEraser);
|
|
||||||
|
|
||||||
connect(clothStiffnessEraser, &QPushButton::clicked, [=]() {
|
|
||||||
clothStiffnessWidget->setValue(Component::defaultClothStiffness);
|
|
||||||
emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
|
|
||||||
QHBoxLayout *clothStiffnessLayout = new QHBoxLayout;
|
|
||||||
clothStiffnessLayout->addWidget(clothStiffnessEraser);
|
|
||||||
clothStiffnessLayout->addWidget(clothStiffnessWidget);
|
|
||||||
|
|
||||||
IntNumberWidget *clothIterationWidget = new IntNumberWidget;
|
|
||||||
clothIterationWidget->setItemName(tr("Iteration"));
|
|
||||||
clothIterationWidget->setRange(0, 1000);
|
|
||||||
clothIterationWidget->setValue(component->clothIteration);
|
|
||||||
|
|
||||||
connect(clothIterationWidget, &IntNumberWidget::valueChanged, [=](int value) {
|
|
||||||
//emit setComponentClothIteration(componentId, value);
|
|
||||||
//emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
|
|
||||||
QPushButton *clothIterationEraser = new QPushButton(QChar(fa::eraser));
|
|
||||||
Theme::initAwesomeToolButton(clothIterationEraser);
|
|
||||||
|
|
||||||
connect(clothIterationEraser, &QPushButton::clicked, [=]() {
|
|
||||||
clothIterationWidget->setValue(Component::defaultClothIteration);
|
|
||||||
emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
|
|
||||||
QHBoxLayout *clothIterationLayout = new QHBoxLayout;
|
|
||||||
clothIterationLayout->addWidget(clothIterationEraser);
|
|
||||||
clothIterationLayout->addWidget(clothIterationWidget);
|
|
||||||
|
|
||||||
FloatNumberWidget *clothOffsetWidget = new FloatNumberWidget;
|
|
||||||
clothOffsetWidget->setItemName(tr("Offset"));
|
|
||||||
clothOffsetWidget->setRange(0.0f, 1.0f);
|
|
||||||
clothOffsetWidget->setValue(component->clothOffset);
|
|
||||||
|
|
||||||
connect(clothOffsetWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
|
||||||
emit setComponentClothOffset(componentId, value);
|
|
||||||
emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
|
|
||||||
QPushButton *clothOffsetEraser = new QPushButton(QChar(fa::eraser));
|
|
||||||
Theme::initAwesomeToolButton(clothOffsetEraser);
|
|
||||||
|
|
||||||
connect(clothOffsetEraser, &QPushButton::clicked, [=]() {
|
|
||||||
clothOffsetWidget->setValue(0.0);
|
|
||||||
emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
|
|
||||||
QHBoxLayout *clothOffsetLayout = new QHBoxLayout;
|
|
||||||
clothOffsetLayout->addWidget(clothOffsetEraser);
|
|
||||||
clothOffsetLayout->addWidget(clothOffsetWidget);
|
|
||||||
|
|
||||||
QComboBox *clothForceSelectBox = new QComboBox;
|
|
||||||
for (size_t i = 0; i < (size_t)ClothForce::Count; ++i) {
|
|
||||||
ClothForce force = (ClothForce)i;
|
|
||||||
clothForceSelectBox->addItem(ClothForceToDispName(force));
|
|
||||||
}
|
|
||||||
clothForceSelectBox->setCurrentIndex((int)component->clothForce);
|
|
||||||
connect(clothForceSelectBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=](int index) {
|
|
||||||
emit setComponentClothForce(component->id, (ClothForce)index);
|
|
||||||
emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
QFormLayout *clothForceFormLayout = new QFormLayout;
|
|
||||||
clothForceFormLayout->addRow(tr("Force"), clothForceSelectBox);
|
|
||||||
QHBoxLayout *clothForceLayout = new QHBoxLayout;
|
|
||||||
clothForceLayout->addLayout(clothForceFormLayout);
|
|
||||||
clothForceLayout->addStretch();
|
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
|
||||||
mainLayout->addLayout(clothStiffnessLayout);
|
|
||||||
//mainLayout->addLayout(clothIterationLayout);
|
|
||||||
mainLayout->addLayout(clothOffsetLayout);
|
|
||||||
mainLayout->addLayout(clothForceLayout);
|
|
||||||
|
|
||||||
popup->setLayout(mainLayout);
|
|
||||||
|
|
||||||
QWidgetAction action(this);
|
|
||||||
action.setDefaultWidget(popup);
|
|
||||||
|
|
||||||
popupMenu.addAction(&action);
|
|
||||||
|
|
||||||
popupMenu.exec(mapToGlobal(pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<QUuid> PartTreeWidget::collectSelectedComponentIds(const QPoint &pos)
|
std::vector<QUuid> PartTreeWidget::collectSelectedComponentIds(const QPoint &pos)
|
||||||
{
|
{
|
||||||
std::set<QUuid> unorderedComponentIds = m_selectedComponentIds;
|
std::set<QUuid> unorderedComponentIds = m_selectedComponentIds;
|
||||||
|
@ -486,33 +373,7 @@ void PartTreeWidget::showContextMenu(const QPoint &pos, bool shorted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QHBoxLayout *componentLayerLayout = nullptr;
|
|
||||||
|
|
||||||
if (nullptr != component) {
|
if (nullptr != component) {
|
||||||
if (nullptr == part || part->hasLayerFunction()) {
|
|
||||||
QPushButton *clothSettingButton = new QPushButton();
|
|
||||||
connect(clothSettingButton, &QPushButton::clicked, this, [=]() {
|
|
||||||
showClothSettingMenu(mapFromGlobal(QCursor::pos()), component->id);
|
|
||||||
});
|
|
||||||
clothSettingButton->setIcon(Theme::awesome()->icon(fa::gear));
|
|
||||||
if (ComponentLayer::Cloth != component->layer)
|
|
||||||
clothSettingButton->hide();
|
|
||||||
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) {
|
|
||||||
clothSettingButton->setVisible(ComponentLayer::Cloth == (ComponentLayer)index);
|
|
||||||
emit setComponentLayer(component->id, (ComponentLayer)index);
|
|
||||||
emit groupOperationAdded();
|
|
||||||
});
|
|
||||||
componentLayerLayout = new QHBoxLayout;
|
|
||||||
componentLayerLayout->addWidget(componentLayerSelectBox);
|
|
||||||
componentLayerLayout->addWidget(clothSettingButton);
|
|
||||||
componentLayerLayout->setStretch(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nullptr == part || part->hasCombineModeFunction()) {
|
if (nullptr == part || part->hasCombineModeFunction()) {
|
||||||
combineModeSelectBox = new QComboBox;
|
combineModeSelectBox = new QComboBox;
|
||||||
|
@ -669,8 +530,6 @@ void PartTreeWidget::showContextMenu(const QPoint &pos, bool shorted)
|
||||||
componentSettingsLayout->addRow(tr("Target"), partTargetSelectBox);
|
componentSettingsLayout->addRow(tr("Target"), partTargetSelectBox);
|
||||||
if (nullptr != combineModeSelectBox)
|
if (nullptr != combineModeSelectBox)
|
||||||
componentSettingsLayout->addRow(tr("Mode"), combineModeSelectBox);
|
componentSettingsLayout->addRow(tr("Mode"), combineModeSelectBox);
|
||||||
if (nullptr != componentLayerLayout)
|
|
||||||
componentSettingsLayout->addRow(tr("Layer"), componentLayerLayout);
|
|
||||||
|
|
||||||
QVBoxLayout *newLayout = new QVBoxLayout;
|
QVBoxLayout *newLayout = new QVBoxLayout;
|
||||||
newLayout->addLayout(layout);
|
newLayout->addLayout(layout);
|
||||||
|
|
|
@ -24,7 +24,6 @@ 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);
|
||||||
|
@ -42,10 +41,6 @@ signals:
|
||||||
void setPartVisibleState(QUuid partId, bool visible);
|
void setPartVisibleState(QUuid partId, bool visible);
|
||||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||||
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
||||||
void setComponentClothStiffness(QUuid componentId, float clothStiffness);
|
|
||||||
void setComponentClothIteration(QUuid componentId, size_t iteration);
|
|
||||||
void setComponentClothForce(QUuid componentId, ClothForce force);
|
|
||||||
void setComponentClothOffset(QUuid componentId, float offset);
|
|
||||||
void hideDescendantComponents(QUuid componentId);
|
void hideDescendantComponents(QUuid componentId);
|
||||||
void showDescendantComponents(QUuid componentId);
|
void showDescendantComponents(QUuid componentId);
|
||||||
void lockDescendantComponents(QUuid componentId);
|
void lockDescendantComponents(QUuid componentId);
|
||||||
|
@ -91,7 +86,6 @@ public slots:
|
||||||
void groupCollapsed(QTreeWidgetItem *item);
|
void groupCollapsed(QTreeWidgetItem *item);
|
||||||
void removeAllContent();
|
void removeAllContent();
|
||||||
void showContextMenu(const QPoint &pos, bool shorted=false);
|
void showContextMenu(const QPoint &pos, bool shorted=false);
|
||||||
void showClothSettingMenu(const QPoint &pos, const QUuid &componentId);
|
|
||||||
protected:
|
protected:
|
||||||
QSize sizeHint() const override;
|
QSize sizeHint() const override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include "materiallistwidget.h"
|
#include "materiallistwidget.h"
|
||||||
#include "infolabel.h"
|
#include "infolabel.h"
|
||||||
#include "cutface.h"
|
#include "cutface.h"
|
||||||
#include "cutdocument.h"
|
|
||||||
#include "skeletongraphicswidget.h"
|
#include "skeletongraphicswidget.h"
|
||||||
#include "shortcuts.h"
|
#include "shortcuts.h"
|
||||||
#include "graphicscontainerwidget.h"
|
#include "graphicscontainerwidget.h"
|
||||||
|
|
|
@ -149,8 +149,6 @@ void RigGenerator::buildNeighborMap()
|
||||||
std::map<std::pair<QUuid, QUuid>, size_t> nodeIdToIndexMap;
|
std::map<std::pair<QUuid, QUuid>, size_t> nodeIdToIndexMap;
|
||||||
for (size_t i = 0; i < m_object->nodes.size(); ++i) {
|
for (size_t i = 0; i < m_object->nodes.size(); ++i) {
|
||||||
const auto &node = m_object->nodes[i];
|
const auto &node = m_object->nodes[i];
|
||||||
if (ComponentLayer::Body != node.layer)
|
|
||||||
continue;
|
|
||||||
nodeIdToIndexMap.insert({{node.partId, node.nodeId}, i});
|
nodeIdToIndexMap.insert({{node.partId, node.nodeId}, i});
|
||||||
m_neighborMap.insert({i, {}});
|
m_neighborMap.insert({i, {}});
|
||||||
}
|
}
|
||||||
|
@ -220,8 +218,6 @@ void RigGenerator::buildBoneNodeChain()
|
||||||
size_t middleStartNodeIndex = m_object->nodes.size();
|
size_t middleStartNodeIndex = m_object->nodes.size();
|
||||||
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
|
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
|
||||||
const auto &node = m_object->nodes[nodeIndex];
|
const auto &node = m_object->nodes[nodeIndex];
|
||||||
if (ComponentLayer::Body != node.layer)
|
|
||||||
continue;
|
|
||||||
if (!BoneMarkIsBranchNode(node.boneMark))
|
if (!BoneMarkIsBranchNode(node.boneMark))
|
||||||
continue;
|
continue;
|
||||||
m_branchNodesMapByMark[(int)node.boneMark].push_back(nodeIndex);
|
m_branchNodesMapByMark[(int)node.boneMark].push_back(nodeIndex);
|
||||||
|
@ -655,21 +651,15 @@ void RigGenerator::computeSkinWeights()
|
||||||
std::map<std::pair<QUuid, QUuid>, size_t> nodeIdToIndexMap;
|
std::map<std::pair<QUuid, QUuid>, size_t> nodeIdToIndexMap;
|
||||||
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
|
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
|
||||||
const auto &node = m_object->nodes[nodeIndex];
|
const auto &node = m_object->nodes[nodeIndex];
|
||||||
if (ComponentLayer::Body != node.layer)
|
|
||||||
continue;
|
|
||||||
nodeIdToIndexMap[{node.partId, node.nodeId}] = nodeIndex;
|
nodeIdToIndexMap[{node.partId, node.nodeId}] = nodeIndex;
|
||||||
}
|
}
|
||||||
if (!nodeIdToIndexMap.empty()) {
|
if (!nodeIdToIndexMap.empty()) {
|
||||||
for (size_t nonBodyNodeIndex = 0; nonBodyNodeIndex < m_object->nodes.size(); ++nonBodyNodeIndex) {
|
for (size_t nonBodyNodeIndex = 0; nonBodyNodeIndex < m_object->nodes.size(); ++nonBodyNodeIndex) {
|
||||||
const auto &nonBodyNode = m_object->nodes[nonBodyNodeIndex];
|
const auto &nonBodyNode = m_object->nodes[nonBodyNodeIndex];
|
||||||
if (ComponentLayer::Body == nonBodyNode.layer)
|
|
||||||
continue;
|
|
||||||
std::vector<std::pair<size_t, float>> distance2s;
|
std::vector<std::pair<size_t, float>> distance2s;
|
||||||
distance2s.reserve(m_object->nodes.size());
|
distance2s.reserve(m_object->nodes.size());
|
||||||
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
|
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
|
||||||
const auto &node = m_object->nodes[nodeIndex];
|
const auto &node = m_object->nodes[nodeIndex];
|
||||||
if (ComponentLayer::Body != node.layer)
|
|
||||||
continue;
|
|
||||||
distance2s.push_back(std::make_pair(nodeIndex,
|
distance2s.push_back(std::make_pair(nodeIndex,
|
||||||
(nonBodyNode.origin - node.origin).lengthSquared()));
|
(nonBodyNode.origin - node.origin).lengthSquared()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
#include <boost/config.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <boost/graph/graph_traits.hpp>
|
|
||||||
#include <boost/graph/adjacency_list.hpp>
|
|
||||||
#include <boost/graph/dijkstra_shortest_paths.hpp>
|
|
||||||
#include <QDebug>
|
|
||||||
#include "shortestpath.h"
|
|
||||||
|
|
||||||
using namespace boost;
|
|
||||||
|
|
||||||
bool shortestPath(size_t nodeNum,
|
|
||||||
const std::vector<std::pair<size_t, size_t>> &edges,
|
|
||||||
const std::vector<int> &weights,
|
|
||||||
size_t start,
|
|
||||||
size_t stop,
|
|
||||||
std::vector<size_t> *path)
|
|
||||||
{
|
|
||||||
typedef adjacency_list < listS, vecS, undirectedS,
|
|
||||||
no_property, property < edge_weight_t, int > > graph_t;
|
|
||||||
typedef graph_traits < graph_t >::vertex_descriptor vertex_descriptor;
|
|
||||||
typedef graph_traits < graph_t >::edge_descriptor edge_descriptor;
|
|
||||||
typedef std::pair<size_t, size_t> Edge;
|
|
||||||
|
|
||||||
size_t edgeNum = edges.size();
|
|
||||||
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
|
|
||||||
graph_t g(nodeNum);
|
|
||||||
property_map<graph_t, edge_weight_t>::type weightmap = get(edge_weight, g);
|
|
||||||
for (std::size_t j = 0; j < edgeNum; ++j) {
|
|
||||||
edge_descriptor e; bool inserted;
|
|
||||||
tie(e, inserted) = add_edge(edges[j].first, edges[j].second, g);
|
|
||||||
weightmap[e] = weights[j];
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
graph_t g(edges.data(), edges.data() + edgeNum, weights.data(), nodeNum);
|
|
||||||
property_map<graph_t, edge_weight_t>::type weightmap = get(edge_weight, g);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector<vertex_descriptor> p(num_vertices(g));
|
|
||||||
std::vector<size_t> d(num_vertices(g));
|
|
||||||
vertex_descriptor s = vertex(start, g);
|
|
||||||
|
|
||||||
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
|
|
||||||
// VC++ has trouble with the named parameters mechanism
|
|
||||||
property_map<graph_t, vertex_index_t>::type indexmap = get(vertex_index, g);
|
|
||||||
dijkstra_shortest_paths(g, s, &p[0], &d[0], weightmap, indexmap,
|
|
||||||
std::less<size_t>(), closed_plus<size_t>(),
|
|
||||||
(std::numeric_limits<size_t>::max)(), 0,
|
|
||||||
default_dijkstra_visitor());
|
|
||||||
#else
|
|
||||||
dijkstra_shortest_paths(g, s, predecessor_map(&p[0]).distance_map(&d[0]));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto current = stop;
|
|
||||||
while (current != start) {
|
|
||||||
path->push_back(current);
|
|
||||||
size_t next = p[current];
|
|
||||||
if (next == current)
|
|
||||||
return false;
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
path->push_back(current);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
#ifndef DUST3D_SHORTEST_PATH
|
|
||||||
#define DUST3D_SHORTEST_PATH
|
|
||||||
#include <vector>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Example code:
|
|
||||||
|
|
||||||
std::vector<QString> nodeNames = {
|
|
||||||
"A", //0
|
|
||||||
"B", //1
|
|
||||||
"C", //2
|
|
||||||
"D", //3
|
|
||||||
"E", //4
|
|
||||||
"F" //5
|
|
||||||
};
|
|
||||||
std::vector<std::pair<int, int>> edges = {
|
|
||||||
{1,0},
|
|
||||||
{1,3},
|
|
||||||
{1,4},
|
|
||||||
{3,4},
|
|
||||||
{3,5},
|
|
||||||
{4,5},
|
|
||||||
{5,2},
|
|
||||||
{0,2}
|
|
||||||
};
|
|
||||||
std::vector<int> weights(edges.size(), 1);
|
|
||||||
weights[7] = 10;
|
|
||||||
weights[4] = 10;
|
|
||||||
std::vector<int> path;
|
|
||||||
shortestPath(nodeNames.size(), edges, weights, 0, 5, &path);
|
|
||||||
for (const auto &it: path) {
|
|
||||||
qDebug() << nodeNames[it];
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool shortestPath(size_t nodeNum,
|
|
||||||
const std::vector<std::pair<size_t, size_t>> &edges,
|
|
||||||
const std::vector<int> &weights,
|
|
||||||
size_t start,
|
|
||||||
size_t stop,
|
|
||||||
std::vector<size_t> *path);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,95 +0,0 @@
|
||||||
#include <tbb/parallel_for.h>
|
|
||||||
#include <tbb/blocked_range.h>
|
|
||||||
#include "simulateclothmeshes.h"
|
|
||||||
#include "positionkey.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "clothsimulator.h"
|
|
||||||
|
|
||||||
class ClothMeshesSimulator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ClothMeshesSimulator(std::vector<ClothMesh> *clothMeshes,
|
|
||||||
const std::vector<QVector3D> *clothCollisionVertices,
|
|
||||||
const std::vector<std::vector<size_t>> *clothCollisionTriangles) :
|
|
||||||
m_clothMeshes(clothMeshes),
|
|
||||||
m_clothCollisionVertices(clothCollisionVertices),
|
|
||||||
m_clothCollisionTriangles(clothCollisionTriangles)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void simulate(ClothMesh *clothMesh) const
|
|
||||||
{
|
|
||||||
const auto &filteredClothFaces = clothMesh->faces;
|
|
||||||
std::map<PositionKey, std::pair<QUuid, QUuid>> positionMap;
|
|
||||||
std::pair<QUuid, QUuid> defaultSource;
|
|
||||||
for (const auto &it: *clothMesh->objectNodeVertices) {
|
|
||||||
if (!it.second.first.isNull())
|
|
||||||
defaultSource.first = it.second.first;
|
|
||||||
positionMap.insert({PositionKey(it.first), it.second});
|
|
||||||
}
|
|
||||||
clothMesh->vertexSources.resize(clothMesh->vertices.size(), defaultSource);
|
|
||||||
for (size_t i = 0; i < clothMesh->vertices.size(); ++i) {
|
|
||||||
auto findSource = positionMap.find(PositionKey(clothMesh->vertices[i]));
|
|
||||||
if (findSource == positionMap.end())
|
|
||||||
continue;
|
|
||||||
clothMesh->vertexSources[i] = findSource->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<QVector3D> &filteredClothVertices = clothMesh->vertices;
|
|
||||||
std::vector<QVector3D> externalForces;
|
|
||||||
std::vector<QVector3D> postProcessDirections;
|
|
||||||
const auto &clothForce = clothMesh->clothForce;
|
|
||||||
float clothOffset = 0.01f + (clothMesh->clothOffset * 0.05f);
|
|
||||||
if (ClothForce::Centripetal == clothForce) {
|
|
||||||
externalForces.resize(filteredClothVertices.size());
|
|
||||||
postProcessDirections.resize(filteredClothVertices.size());
|
|
||||||
for (size_t i = 0; i < filteredClothFaces.size(); ++i) {
|
|
||||||
const auto &face = filteredClothFaces[i];
|
|
||||||
auto faceForceDirection = -polygonNormal(filteredClothVertices, face);
|
|
||||||
for (const auto &vertex: face)
|
|
||||||
externalForces[vertex] += faceForceDirection;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < externalForces.size(); ++i) {
|
|
||||||
auto &it = externalForces[i];
|
|
||||||
it.normalize();
|
|
||||||
postProcessDirections[i] = it * clothOffset;
|
|
||||||
it = (it + QVector3D(0.0f, -1.0f, 0.0f)).normalized();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
postProcessDirections.resize(filteredClothVertices.size(), QVector3D(0.0f, -1.0f, 0.0f) * clothOffset);
|
|
||||||
externalForces.resize(filteredClothVertices.size(), QVector3D(0.0f, -1.0f, 0.0f));
|
|
||||||
}
|
|
||||||
ClothSimulator clothSimulator(filteredClothVertices,
|
|
||||||
filteredClothFaces,
|
|
||||||
*m_clothCollisionVertices,
|
|
||||||
*m_clothCollisionTriangles,
|
|
||||||
externalForces);
|
|
||||||
clothSimulator.setStiffness(clothMesh->clothStiffness);
|
|
||||||
clothSimulator.create();
|
|
||||||
for (size_t i = 0; i < clothMesh->clothIteration; ++i)
|
|
||||||
clothSimulator.step();
|
|
||||||
clothSimulator.getCurrentVertices(&filteredClothVertices);
|
|
||||||
for (size_t i = 0; i < filteredClothVertices.size(); ++i) {
|
|
||||||
filteredClothVertices[i] -= postProcessDirections[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void operator()(const tbb::blocked_range<size_t> &range) const
|
|
||||||
{
|
|
||||||
for (size_t i = range.begin(); i != range.end(); ++i) {
|
|
||||||
simulate(&(*m_clothMeshes)[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::vector<ClothMesh> *m_clothMeshes = nullptr;
|
|
||||||
const std::vector<QVector3D> *m_clothCollisionVertices = nullptr;
|
|
||||||
const std::vector<std::vector<size_t>> *m_clothCollisionTriangles = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
void simulateClothMeshes(std::vector<ClothMesh> *clothMeshes,
|
|
||||||
const std::vector<QVector3D> *clothCollisionVertices,
|
|
||||||
const std::vector<std::vector<size_t>> *clothCollisionTriangles)
|
|
||||||
{
|
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, clothMeshes->size()),
|
|
||||||
ClothMeshesSimulator(clothMeshes,
|
|
||||||
clothCollisionVertices,
|
|
||||||
clothCollisionTriangles));
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
#ifndef DUST3D_SIMULATE_CLOTH_MESHES_H
|
|
||||||
#define DUST3D_SIMULATE_CLOTH_MESHES_H
|
|
||||||
#include <QVector3D>
|
|
||||||
#include <vector>
|
|
||||||
#include <QUuid>
|
|
||||||
#include "clothforce.h"
|
|
||||||
|
|
||||||
struct ClothMesh
|
|
||||||
{
|
|
||||||
std::vector<QVector3D> vertices;
|
|
||||||
std::vector<std::vector<size_t>> faces;
|
|
||||||
std::vector<std::pair<QUuid, QUuid>> vertexSources;
|
|
||||||
const std::vector<std::pair<QVector3D, std::pair<QUuid, QUuid>>> *objectNodeVertices;
|
|
||||||
ClothForce clothForce;
|
|
||||||
float clothOffset;
|
|
||||||
float clothStiffness;
|
|
||||||
size_t clothIteration;
|
|
||||||
};
|
|
||||||
|
|
||||||
void simulateClothMeshes(std::vector<ClothMesh> *clothMeshes,
|
|
||||||
const std::vector<QVector3D> *clothCollisionVertices,
|
|
||||||
const std::vector<std::vector<size_t>> *clothCollisionTriangles);
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue