Remove component layer

master
huxingyi 2020-12-17 22:39:46 +09:30
parent 7817cde381
commit 62418b9340
26 changed files with 0 additions and 1668 deletions

View File

@ -344,9 +344,6 @@ HEADERS += src/combinemode.h
SOURCES += src/polycount.cpp
HEADERS += src/polycount.h
SOURCES += src/cutdocument.cpp
HEADERS += src/cutdocument.h
SOURCES += src/cutface.cpp
HEADERS += src/cutface.h
@ -403,12 +400,6 @@ HEADERS += src/boundingboxmesh.h
SOURCES += src/regionfiller.cpp
HEADERS += src/regionfiller.h
SOURCES += src/cyclefinder.cpp
HEADERS += src/cyclefinder.h
SOURCES += src/shortestpath.cpp
HEADERS += src/shortestpath.h
SOURCES += src/meshwrapper.cpp
HEADERS += src/meshwrapper.h
@ -442,24 +433,12 @@ HEADERS += src/booleanmesh.h
SOURCES += src/remesher.cpp
HEADERS += src/remesher.h
SOURCES += src/clothsimulator.cpp
HEADERS += src/clothsimulator.h
SOURCES += src/componentlayer.cpp
HEADERS += src/componentlayer.h
SOURCES += src/isotropicremesh.cpp
HEADERS += src/isotropicremesh.h
SOURCES += src/clothforce.cpp
HEADERS += src/clothforce.h
SOURCES += src/projectfacestonodes.cpp
HEADERS += src/projectfacestonodes.h
SOURCES += src/simulateclothmeshes.cpp
HEADERS += src/simulateclothmeshes.h
SOURCES += src/ddsfile.cpp
HEADERS += src/ddsfile.h

View File

@ -1,6 +0,0 @@
#include <QObject>
#include "clothforce.h"
IMPL_ClothForceFromString
IMPL_ClothForceToString
IMPL_ClothForceToDispName

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -1,6 +0,0 @@
#include <QObject>
#include "componentlayer.h"
IMPL_ComponentLayerToString
IMPL_ComponentLayerFromString
IMPL_ComponentLayerToDispName

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -21,8 +21,6 @@
#include "meshgenerator.h"
unsigned long Document::m_maxSnapshot = 1000;
const float Component::defaultClothStiffness = 0.5f;
const size_t Component::defaultClothIteration = 350;
Document::Document() :
SkeletonDocument(),
@ -1230,16 +1228,6 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
component["smoothSeam"] = QString::number(componentIt.second.smoothSeam);
if (componentIt.second.polyCount != PolyCount::Original)
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;
for (const auto &childId: componentIt.second.childrenIds) {
childIdList.append(childId.toString());
@ -1682,19 +1670,6 @@ void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource sou
if (smoothSeamIt != componentKv.second.end())
component.setSmoothSeam(smoothSeamIt->second.toFloat());
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;
if ("partId" == linkDataType) {
QUuid partId = oldNewIdMap[QUuid(linkData)];
@ -2508,76 +2483,6 @@ void Document::setComponentPolyCount(QUuid componentId, PolyCount count)
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)
{
auto component = componentMap.find(componentId);

View File

@ -26,8 +26,6 @@
#include "preferences.h"
#include "paintmode.h"
#include "proceduralanimation.h"
#include "componentlayer.h"
#include "clothforce.h"
#include "texturepainter.h"
class MaterialPreviewsGenerator;
@ -45,8 +43,6 @@ public:
class Component
{
public:
static const float defaultClothStiffness;
static const size_t defaultClothIteration;
Component()
{
}
@ -69,11 +65,6 @@ public:
float smoothAll = 0.0;
float smoothSeam = 0.0;
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;
QString linkData() const
{
@ -193,22 +184,6 @@ public:
{
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:
std::set<QUuid> m_childrenIdSet;
};
@ -314,10 +289,6 @@ signals:
void componentSmoothSeamChanged(QUuid componentId);
void componentPolyCountChanged(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 edgeRemoved(QUuid edgeId);
void nodeRadiusChanged(QUuid nodeId);
@ -585,11 +556,6 @@ public slots:
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
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 lockOtherComponents(QUuid componentId);
void hideAllComponents();

View File

@ -987,7 +987,6 @@ DocumentWindow::DocumentWindow() :
connect(m_partTreeWidget, &PartTreeWidget::setComponentSmoothAll, m_document, &Document::setComponentSmoothAll);
connect(m_partTreeWidget, &PartTreeWidget::setComponentSmoothSeam, m_document, &Document::setComponentSmoothSeam);
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::removeComponent, m_document, &Document::removeComponent);
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::setPartColorState, m_document, &Document::setPartColorState);
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::setPartBase, m_document, &Document::setPartBase);
connect(m_partTreeWidget, &PartTreeWidget::hideDescendantComponents, m_document, &Document::hideDescendantComponents);

View File

@ -17,11 +17,9 @@
#include "triangulatefaces.h"
#include "remesher.h"
#include "polycount.h"
#include "clothsimulator.h"
#include "isotropicremesh.h"
#include "projectfacestonodes.h"
#include "document.h"
#include "simulateclothmeshes.h"
#include "meshstroketifier.h"
#include "fileforever.h"
#include "snapshotxml.h"
@ -915,11 +913,6 @@ CombineMode MeshGenerator::componentCombineMode(const std::map<QString, QString>
combineMode = CombineMode::Inversion;
if (componentRemeshed(component))
combineMode = CombineMode::Uncombined;
if (combineMode == CombineMode::Normal) {
if (ComponentLayer::Body != ComponentLayerFromString(valueOfKeyInMapOrEmpty(*component, "layer").toUtf8().constData())) {
combineMode = CombineMode::Uncombined;
}
}
}
return combineMode;
}
@ -928,17 +921,9 @@ bool MeshGenerator::componentRemeshed(const std::map<QString, QString> *componen
{
if (nullptr == component)
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());
if (nullptr != polyCountValue)
*polyCountValue = PolyCountToValue(polyCount);
if (isCloth)
return true;
return polyCount != PolyCount::Original;
}
@ -967,47 +952,6 @@ QString MeshGenerator::componentColorName(const std::map<QString, QString> *comp
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 *mesh = nullptr;
@ -1616,8 +1560,6 @@ void MeshGenerator::collectUncombinedComponent(const QString &componentIdString)
{
const auto &component = findComponent(componentIdString);
if (CombineMode::Uncombined == componentCombineMode(component)) {
if (ComponentLayer::Body != componentLayer(component))
return;
const auto &componentCache = m_cacheContext->components[componentIdString];
if (nullptr == componentCache.mesh || componentCache.mesh->isNull()) {
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,
const std::vector<QVector3D> &triangleNormals,
std::vector<std::vector<QVector3D>> *triangleVertexNormals)
@ -1968,25 +1826,6 @@ void MeshGenerator::generate()
collectUncombinedComponent(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();
postprocessObject(m_object);

View File

@ -12,8 +12,6 @@
#include "snapshot.h"
#include "combinemode.h"
#include "model.h"
#include "componentlayer.h"
#include "clothforce.h"
class GeneratedPart
{
@ -170,15 +168,7 @@ private:
GeneratedComponent &componentCache);
MeshCombiner::Mesh *combineMultipleMeshes(const std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, QString>> &multipleMeshes, bool recombine=true);
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 collectClothComponent(const QString &componentIdString);
void collectClothComponentIdStrings(const QString &componentIdString,
std::vector<QString> *componentIdStrings);
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
void remesh(const std::vector<ObjectNode> &inputNodes,
const std::vector<std::tuple<QVector3D, float, size_t>> &interpolatedNodes,

View File

@ -8,7 +8,6 @@
#include <QVector2D>
#include <QRectF>
#include "bonemark.h"
#include "componentlayer.h"
struct ObjectNode
{
@ -26,7 +25,6 @@ struct ObjectNode
QUuid mirroredByPartId;
BoneMark boneMark = BoneMark::None;
QVector3D direction;
ComponentLayer layer = ComponentLayer::Body;
bool joined = true;
};

View File

@ -43,8 +43,6 @@ void saveObjectToXmlStream(const Object *object, QXmlStreamWriter *writer)
writer->writeAttribute("mirroredByPartId", node.mirroredByPartId.toString());
if (node.boneMark != BoneMark::None)
writer->writeAttribute("boneMark", BoneMarkToString(node.boneMark));
if (ComponentLayer::Body != node.layer)
writer->writeAttribute("layer", ComponentLayerToString(node.layer));
if (!node.joined)
writer->writeAttribute("joined", "false");
writer->writeEndElement();
@ -250,7 +248,6 @@ void loadObjectFromXmlStream(Object *object, QXmlStreamReader &reader)
node.mirrorFromPartId = QUuid(reader.attributes().value("mirrorFromPartId").toString());
node.mirroredByPartId = QUuid(reader.attributes().value("mirroredByPartId").toString());
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();
if (!joinedString.isEmpty())
node.joined = isTrueValueString(joinedString);

View File

@ -257,119 +257,6 @@ void PartTreeWidget::mousePressEvent(QMouseEvent *event)
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::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 == 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()) {
combineModeSelectBox = new QComboBox;
@ -669,8 +530,6 @@ void PartTreeWidget::showContextMenu(const QPoint &pos, bool shorted)
componentSettingsLayout->addRow(tr("Target"), partTargetSelectBox);
if (nullptr != combineModeSelectBox)
componentSettingsLayout->addRow(tr("Mode"), combineModeSelectBox);
if (nullptr != componentLayerLayout)
componentSettingsLayout->addRow(tr("Layer"), componentLayerLayout);
QVBoxLayout *newLayout = new QVBoxLayout;
newLayout->addLayout(layout);

View File

@ -24,7 +24,6 @@ signals:
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
void setComponentPolyCount(QUuid componentId, PolyCount count);
void setComponentLayer(QUuid componentId, ComponentLayer layer);
void setPartTarget(QUuid partId, PartTarget target);
void setPartBase(QUuid partId, PartBase base);
void moveComponent(QUuid componentId, QUuid toParentId);
@ -42,10 +41,6 @@ signals:
void setPartVisibleState(QUuid partId, bool visible);
void setPartColorState(QUuid partId, bool hasColor, QColor color);
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 showDescendantComponents(QUuid componentId);
void lockDescendantComponents(QUuid componentId);
@ -91,7 +86,6 @@ public slots:
void groupCollapsed(QTreeWidgetItem *item);
void removeAllContent();
void showContextMenu(const QPoint &pos, bool shorted=false);
void showClothSettingMenu(const QPoint &pos, const QUuid &componentId);
protected:
QSize sizeHint() const override;
void mousePressEvent(QMouseEvent *event) override;

View File

@ -16,7 +16,6 @@
#include "materiallistwidget.h"
#include "infolabel.h"
#include "cutface.h"
#include "cutdocument.h"
#include "skeletongraphicswidget.h"
#include "shortcuts.h"
#include "graphicscontainerwidget.h"

View File

@ -149,8 +149,6 @@ void RigGenerator::buildNeighborMap()
std::map<std::pair<QUuid, QUuid>, size_t> nodeIdToIndexMap;
for (size_t i = 0; i < m_object->nodes.size(); ++i) {
const auto &node = m_object->nodes[i];
if (ComponentLayer::Body != node.layer)
continue;
nodeIdToIndexMap.insert({{node.partId, node.nodeId}, i});
m_neighborMap.insert({i, {}});
}
@ -220,8 +218,6 @@ void RigGenerator::buildBoneNodeChain()
size_t middleStartNodeIndex = m_object->nodes.size();
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
const auto &node = m_object->nodes[nodeIndex];
if (ComponentLayer::Body != node.layer)
continue;
if (!BoneMarkIsBranchNode(node.boneMark))
continue;
m_branchNodesMapByMark[(int)node.boneMark].push_back(nodeIndex);
@ -655,21 +651,15 @@ void RigGenerator::computeSkinWeights()
std::map<std::pair<QUuid, QUuid>, size_t> nodeIdToIndexMap;
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
const auto &node = m_object->nodes[nodeIndex];
if (ComponentLayer::Body != node.layer)
continue;
nodeIdToIndexMap[{node.partId, node.nodeId}] = nodeIndex;
}
if (!nodeIdToIndexMap.empty()) {
for (size_t nonBodyNodeIndex = 0; nonBodyNodeIndex < m_object->nodes.size(); ++nonBodyNodeIndex) {
const auto &nonBodyNode = m_object->nodes[nonBodyNodeIndex];
if (ComponentLayer::Body == nonBodyNode.layer)
continue;
std::vector<std::pair<size_t, float>> distance2s;
distance2s.reserve(m_object->nodes.size());
for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) {
const auto &node = m_object->nodes[nodeIndex];
if (ComponentLayer::Body != node.layer)
continue;
distance2s.push_back(std::make_pair(nodeIndex,
(nonBodyNode.origin - node.origin).lengthSquared()));
}

View File

@ -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;
}

View File

@ -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

View File

@ -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));
}

View File

@ -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