diff --git a/dust3d.pro b/dust3d.pro index 83a25525..70981631 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -497,6 +497,9 @@ HEADERS += src/clothforce.h SOURCES += src/projectfacestonodes.cpp HEADERS += src/projectfacestonodes.h +SOURCES += src/simulateclothmeshes.cpp +HEADERS += src/simulateclothmeshes.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 2ba52067..1ecf9655 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -22,6 +22,7 @@ #include "isotropicremesh.h" #include "projectfacestonodes.h" #include "document.h" +#include "simulateclothmeshes.h" MeshGenerator::MeshGenerator(Snapshot *snapshot) : m_snapshot(snapshot) @@ -1677,7 +1678,8 @@ void MeshGenerator::collectUncombinedComponent(const QString &componentIdString) } } -void MeshGenerator::collectClothComponent(const QString &componentIdString) +void MeshGenerator::collectClothComponentIdStrings(const QString &componentIdString, + std::vector *componentIdStrings) { const auto &component = findComponent(componentIdString); if (ComponentLayer::Cloth == componentLayer(component)) { @@ -1685,73 +1687,45 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString) if (nullptr == componentCache.mesh) { return; } - if (m_clothCollisionTriangles.empty() || m_clothTargetNodes.empty()) - return; - std::vector uncombinedVertices; - std::vector> uncombinedOriginalFaces; - std::vector> uncombinedFaces; - componentCache.mesh->fetch(uncombinedVertices, uncombinedOriginalFaces); - uncombinedFaces.reserve(uncombinedOriginalFaces.size()); - for (const auto &it: uncombinedOriginalFaces) { - if (4 == it.size()) { - uncombinedFaces.push_back(std::vector { - it[0], it[1], it[2] - }); - uncombinedFaces.push_back(std::vector { - it[2], it[3], it[0] - }); - } else if (3 == it.size()) { - uncombinedFaces.push_back(it); - } - } - - std::map> positionMap; - std::pair defaultSource; - for (const auto &it: componentCache.outcomeNodeVertices) { - if (!it.second.first.isNull()) - defaultSource.first = it.second.first; - positionMap.insert({PositionKey(it.first), it.second}); - } - std::vector> uncombinedVertexSources(uncombinedVertices.size(), defaultSource); - for (size_t i = 0; i < uncombinedVertices.size(); ++i) { - auto findSource = positionMap.find(PositionKey(uncombinedVertices[i])); - if (findSource == positionMap.end()) - continue; - uncombinedVertexSources[i] = findSource->second; - } - - std::vector &filteredClothVertices = uncombinedVertices; - std::vector> &filteredClothFaces = uncombinedFaces; - std::vector externalForces; - ClothForce clothForce = componentClothForce(component); - float clothOffset = 0.015f + (componentClothOffset(component) * 0.05f); - if (ClothForce::Centripetal == clothForce) { - externalForces.resize(filteredClothVertices.size()); - for (size_t i = 0; i < filteredClothFaces.size(); ++i) { - const auto &face = filteredClothFaces[i]; - auto faceForceDirection = -polygonNormal(uncombinedVertices, face); - for (const auto &vertex: face) - externalForces[vertex] += faceForceDirection; - } - for (auto &it: externalForces) - it = (it.normalized() + QVector3D(0.0f, -1.0f, 0.0f)).normalized(); - } else { - externalForces.resize(filteredClothVertices.size(), QVector3D(0.0f, -1.0f, 0.0f)); - } - ClothSimulator clothSimulator(filteredClothVertices, - filteredClothFaces, - m_clothCollisionVertices, - m_clothCollisionTriangles, - externalForces); - clothSimulator.setStiffness(componentClothStiffness(component)); - clothSimulator.create(); - for (size_t i = 0; i < 350; ++i) - clothSimulator.step(); - clothSimulator.getCurrentVertices(&filteredClothVertices); - for (size_t i = 0; i < filteredClothVertices.size(); ++i) { - filteredClothVertices[i] -= externalForces[i] * clothOffset; - } + 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() || m_clothTargetNodes.empty()) + return; + std::vector componentIdStrings; + collectClothComponentIdStrings(componentIdString, &componentIdStrings); + + std::vector 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.outcomeNodeVertices = &componentCache.outcomeNodeVertices; + m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end()); + } + simulateClothMeshes(&clothMeshes, + &m_clothCollisionVertices, + &m_clothCollisionTriangles, + &m_clothTargetNodes); + for (auto &clothMesh: clothMeshes) { auto vertexStartIndex = m_outcome->vertices.size(); auto updateVertexIndices = [=](std::vector> &faces) { for (auto &it: faces) { @@ -1759,10 +1733,9 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString) subIt += vertexStartIndex; } }; - updateVertexIndices(filteredClothFaces); - - m_outcome->vertices.insert(m_outcome->vertices.end(), filteredClothVertices.begin(), filteredClothVertices.end()); - for (const auto &it: filteredClothFaces) { + updateVertexIndices(clothMesh.faces); + m_outcome->vertices.insert(m_outcome->vertices.end(), clothMesh.vertices.begin(), clothMesh.vertices.end()); + for (const auto &it: clothMesh.faces) { if (4 == it.size()) { m_outcome->triangles.push_back(std::vector { it[0], it[1], it[2] @@ -1774,20 +1747,11 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString) m_outcome->triangles.push_back(it); } } - m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), filteredClothFaces.begin(), filteredClothFaces.end()); - - m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end()); - Q_ASSERT(uncombinedVertices.size() == filteredClothVertices.size()); - for (size_t i = 0; i < uncombinedVertices.size(); ++i) { - const auto &source = uncombinedVertexSources[i]; - m_outcome->nodeVertices.push_back(std::make_pair(filteredClothVertices[i], source)); + m_outcome->triangleAndQuads.insert(m_outcome->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_outcome->nodeVertices.push_back(std::make_pair(clothMesh.vertices[i], source)); } - return; - } - for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { - if (childIdString.isEmpty()) - continue; - collectClothComponent(childIdString); } } diff --git a/src/meshgenerator.h b/src/meshgenerator.h index 61cd1b4f..15a20e91 100644 --- a/src/meshgenerator.h +++ b/src/meshgenerator.h @@ -138,6 +138,8 @@ private: float componentClothOffset(const std::map *component); void collectUncombinedComponent(const QString &componentIdString); void collectClothComponent(const QString &componentIdString); + void collectClothComponentIdStrings(const QString &componentIdString, + std::vector *componentIdStrings); void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector &cutTemplate); void remesh(const std::vector &inputNodes, const std::vector &inputVertices, diff --git a/src/simulateclothmeshes.cpp b/src/simulateclothmeshes.cpp new file mode 100644 index 00000000..9b67346d --- /dev/null +++ b/src/simulateclothmeshes.cpp @@ -0,0 +1,105 @@ +#include +#include +#include "simulateclothmeshes.h" +#include "positionkey.h" +#include "util.h" +#include "clothsimulator.h" + +class ClothMeshesSimulator +{ +public: + ClothMeshesSimulator(std::vector *clothMeshes, + const std::vector *clothCollisionVertices, + const std::vector> *clothCollisionTriangles, + const std::vector> *clothTargetNodes) : + m_clothMeshes(clothMeshes), + m_clothCollisionVertices(clothCollisionVertices), + m_clothCollisionTriangles(clothCollisionTriangles), + m_clothTargetNodes(clothTargetNodes) + { + } + void simulate(ClothMesh *clothMesh) const + { + std::vector> filteredClothFaces; + for (const auto &it: clothMesh->faces) { + if (4 == it.size()) { + filteredClothFaces.push_back(std::vector { + it[0], it[1], it[2] + }); + filteredClothFaces.push_back(std::vector { + it[2], it[3], it[0] + }); + } else if (3 == it.size()) { + filteredClothFaces.push_back(it); + } + } + std::map> positionMap; + std::pair defaultSource; + for (const auto &it: *clothMesh->outcomeNodeVertices) { + 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 &filteredClothVertices = clothMesh->vertices; + std::vector externalForces; + const auto &clothForce = clothMesh->clothForce; + float clothOffset = 0.015f + (clothMesh->clothOffset * 0.05f); + if (ClothForce::Centripetal == clothForce) { + externalForces.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 (auto &it: externalForces) + it = (it.normalized() + QVector3D(0.0f, -1.0f, 0.0f)).normalized(); + } else { + 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 < 350; ++i) + clothSimulator.step(); + clothSimulator.getCurrentVertices(&filteredClothVertices); + for (size_t i = 0; i < filteredClothVertices.size(); ++i) { + filteredClothVertices[i] -= externalForces[i] * clothOffset; + } + } + void operator()(const tbb::blocked_range &range) const + { + for (size_t i = range.begin(); i != range.end(); ++i) { + simulate(&(*m_clothMeshes)[i]); + } + } +private: + std::vector *m_clothMeshes = nullptr; + const std::vector *m_clothCollisionVertices = nullptr; + const std::vector> *m_clothCollisionTriangles = nullptr; + const std::vector> *m_clothTargetNodes = nullptr; +}; + +void simulateClothMeshes(std::vector *clothMeshes, + const std::vector *clothCollisionVertices, + const std::vector> *clothCollisionTriangles, + const std::vector> *clothTargetNodes) +{ + tbb::parallel_for(tbb::blocked_range(0, clothMeshes->size()), + ClothMeshesSimulator(clothMeshes, + clothCollisionVertices, + clothCollisionTriangles, + clothTargetNodes)); +} diff --git a/src/simulateclothmeshes.h b/src/simulateclothmeshes.h new file mode 100644 index 00000000..46fc7217 --- /dev/null +++ b/src/simulateclothmeshes.h @@ -0,0 +1,24 @@ +#ifndef DUST3D_SIMULATE_CLOTH_MESHES_H +#define DUST3D_SIMULATE_CLOTH_MESHES_H +#include +#include +#include +#include "clothforce.h" + +struct ClothMesh +{ + std::vector vertices; + std::vector> faces; + std::vector> vertexSources; + const std::vector>> *outcomeNodeVertices; + ClothForce clothForce; + float clothOffset; + float clothStiffness; +}; + +void simulateClothMeshes(std::vector *clothMeshes, + const std::vector *clothCollisionVertices, + const std::vector> *clothCollisionTriangles, + const std::vector> *clothTargetNodes); + +#endif