|
|
|
@ -20,6 +20,7 @@
|
|
|
|
|
* SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <unordered_set>
|
|
|
|
|
#include <dust3d/base/debug.h>
|
|
|
|
|
#include <dust3d/mesh/stitch_mesh_builder.h>
|
|
|
|
|
|
|
|
|
@ -41,12 +42,13 @@ const std::vector<std::vector<size_t>> &StitchMeshBuilder::generatedFaces()
|
|
|
|
|
return m_generatedFaces;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::vector<StitchMeshBuilder::StitchingPoint>> StitchMeshBuilder::convertSplinesToStitchingPoints(const std::vector<Spline> &splines)
|
|
|
|
|
void StitchMeshBuilder::interpolateSplinesToHaveEqualSizeOfNodesExceptClosingSplines()
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::vector<StitchingPoint>> stitchingPoints(splines.size());
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < splines.size(); ++i) {
|
|
|
|
|
const auto &spline = splines[i];
|
|
|
|
|
std::vector<Spline> interpolatedSplines = m_splines;
|
|
|
|
|
for (size_t i = 0; i < m_splines.size(); ++i) {
|
|
|
|
|
const auto &spline = m_splines[i];
|
|
|
|
|
if (spline.isClosing)
|
|
|
|
|
continue;
|
|
|
|
|
std::vector<Vector3> polyline(spline.nodes.size());
|
|
|
|
|
std::vector<double> radiuses(spline.nodes.size());
|
|
|
|
|
for (size_t j = 0; j < spline.nodes.size(); ++j) {
|
|
|
|
@ -56,11 +58,27 @@ std::vector<std::vector<StitchMeshBuilder::StitchingPoint>> StitchMeshBuilder::c
|
|
|
|
|
std::vector<Vector3> segmentPoints;
|
|
|
|
|
std::vector<double> segmentRadiuses;
|
|
|
|
|
splitPolylineToSegments(polyline, radiuses, m_targetSegments, &segmentPoints, &segmentRadiuses);
|
|
|
|
|
stitchingPoints[i].resize(segmentPoints.size());
|
|
|
|
|
for (size_t k = 0; k < segmentPoints.size(); ++k) {
|
|
|
|
|
auto &interpolatedSpline = interpolatedSplines[i];
|
|
|
|
|
interpolatedSpline.nodes.resize(segmentPoints.size());
|
|
|
|
|
for (size_t j = 0; j < spline.nodes.size(); ++j) {
|
|
|
|
|
interpolatedSpline.nodes[j].origin = segmentPoints[j];
|
|
|
|
|
interpolatedSpline.nodes[j].radius = segmentRadiuses[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_splines = std::move(interpolatedSplines);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::vector<StitchMeshBuilder::StitchingPoint>> StitchMeshBuilder::convertSplinesToStitchingPoints(const std::vector<Spline> &splines)
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::vector<StitchingPoint>> stitchingPoints(splines.size());
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < splines.size(); ++i) {
|
|
|
|
|
const auto &spline = splines[i];
|
|
|
|
|
stitchingPoints[i].resize(spline.nodes.size());
|
|
|
|
|
for (size_t k = 0; k < spline.nodes.size(); ++k) {
|
|
|
|
|
stitchingPoints[i][k].originVertex = m_generatedVertices.size();
|
|
|
|
|
stitchingPoints[i][k].radius = segmentRadiuses[k];
|
|
|
|
|
m_generatedVertices.push_back(segmentPoints[k]);
|
|
|
|
|
stitchingPoints[i][k].radius = spline.nodes[k].radius;
|
|
|
|
|
m_generatedVertices.push_back(spline.nodes[k].origin);
|
|
|
|
|
m_generatedStitchingPoints.push_back(stitchingPoints[i][k]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -159,8 +177,80 @@ void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector<Stitch
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StitchMeshBuilder::interpolateSplinesForBackClosing()
|
|
|
|
|
{
|
|
|
|
|
if (m_splines.size() < 2) {
|
|
|
|
|
dust3dDebug << "Expected at least two splines for back closing interpoloating, current:" << m_splines.size();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Vector3 sourceOrigin;
|
|
|
|
|
Vector3 targetPosition = m_splines.back().nodes.front().origin;
|
|
|
|
|
const auto &nodes = m_splines[m_splines.size() - 2].nodes;
|
|
|
|
|
std::vector<Vector3> sourcePositions(nodes.size());
|
|
|
|
|
for (size_t i = 0; i < nodes.size(); ++i) {
|
|
|
|
|
sourcePositions[i] = nodes[i].origin;
|
|
|
|
|
sourceOrigin += sourcePositions[i];
|
|
|
|
|
}
|
|
|
|
|
sourceOrigin /= nodes.size();
|
|
|
|
|
Vector3 targetDirection = (targetPosition - sourceOrigin).normalized();
|
|
|
|
|
std::vector<Vector3> sourceVectors(nodes.size());
|
|
|
|
|
for (size_t i = 0; i < nodes.size(); ++i) {
|
|
|
|
|
sourceVectors[i] = (nodes[i].origin - sourceOrigin).normalized();
|
|
|
|
|
}
|
|
|
|
|
std::vector<Vector3> interpolatedPositions(nodes.size());
|
|
|
|
|
std::vector<double> interpolatedRadiuses(nodes.size());
|
|
|
|
|
for (size_t i = 0; i < nodes.size(); ++i) {
|
|
|
|
|
interpolatedPositions[i] = sourceOrigin +
|
|
|
|
|
sourceVectors[i].rotated(Vector3::crossProduct(sourceVectors[i], targetDirection), Math::Pi * 0.25) *
|
|
|
|
|
((nodes[i].origin - sourceOrigin).length() + (nodes[i].origin - targetPosition).length() * 0.5) * 0.5;
|
|
|
|
|
interpolatedRadiuses[i] = nodes[i].radius * 0.5;
|
|
|
|
|
}
|
|
|
|
|
m_splines.back().nodes.resize(nodes.size());
|
|
|
|
|
for (size_t i = 0; i < nodes.size(); ++i) {
|
|
|
|
|
m_splines.back().nodes[i].origin = interpolatedPositions[i];
|
|
|
|
|
m_splines.back().nodes[i].radius = interpolatedRadiuses[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StitchMeshBuilder::addQuadButMaybeTriangle(const std::vector<size_t> &quadButMaybeTriangle)
|
|
|
|
|
{
|
|
|
|
|
std::vector<size_t> finalFace;
|
|
|
|
|
std::unordered_set<size_t> used;
|
|
|
|
|
finalFace.reserve(4);
|
|
|
|
|
for (const auto &it: quadButMaybeTriangle) {
|
|
|
|
|
auto insertResult = used.insert(it);
|
|
|
|
|
if (insertResult.second)
|
|
|
|
|
finalFace.push_back(it);
|
|
|
|
|
}
|
|
|
|
|
m_generatedFaces.emplace_back(finalFace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StitchMeshBuilder::build()
|
|
|
|
|
{
|
|
|
|
|
if (m_splines.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Calculate target segments
|
|
|
|
|
m_targetSegments = 1;
|
|
|
|
|
for (const auto &it: m_splines) {
|
|
|
|
|
m_targetSegments = std::max(m_targetSegments, it.nodes.size() - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Make sure there is no closing nodes in the middle
|
|
|
|
|
|
|
|
|
|
// Interpolate splines to make sure every spline have equal size of nodes, except the closing splines
|
|
|
|
|
interpolateSplinesToHaveEqualSizeOfNodesExceptClosingSplines();
|
|
|
|
|
|
|
|
|
|
// Preprocess back closing
|
|
|
|
|
if (m_splines.back().isClosing)
|
|
|
|
|
interpolateSplinesForBackClosing();
|
|
|
|
|
|
|
|
|
|
// TODO: Preprocess front closing
|
|
|
|
|
|
|
|
|
|
// Update to the real interploated segments
|
|
|
|
|
m_targetSegments = m_splines.back().nodes.size() - 1;
|
|
|
|
|
|
|
|
|
|
// Generate one side faces
|
|
|
|
|
std::vector<std::vector<StitchingPoint>> stitchingPoints = convertSplinesToStitchingPoints(m_splines);
|
|
|
|
|
generateMeshFromStitchingPoints(stitchingPoints);
|
|
|
|
|
|
|
|
|
@ -179,29 +269,34 @@ void StitchMeshBuilder::build()
|
|
|
|
|
for (auto &it: m_generatedNormals)
|
|
|
|
|
it.normalize();
|
|
|
|
|
|
|
|
|
|
// Move all vertices off distance at radius
|
|
|
|
|
for (size_t i = 0; i < m_generatedVertices.size(); ++i) {
|
|
|
|
|
m_generatedVertices[i] += m_generatedNormals[i] * m_generatedStitchingPoints[i].radius;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate other side of faces
|
|
|
|
|
auto oneSideVertexCount = m_generatedVertices.size();
|
|
|
|
|
m_generatedVertices.reserve(oneSideVertexCount * 2);
|
|
|
|
|
for (size_t i = 0; i < oneSideVertexCount; ++i) {
|
|
|
|
|
m_generatedVertices.emplace_back(m_generatedVertices[i] - m_generatedNormals[i] * m_generatedStitchingPoints[i].radius * 2.0);
|
|
|
|
|
m_generatedVertices.emplace_back(m_generatedVertices[i]);
|
|
|
|
|
}
|
|
|
|
|
m_generatedFaces.reserve(m_generatedFaces.size() * 2);
|
|
|
|
|
auto oneSideFaceCount = m_generatedFaces.size();
|
|
|
|
|
for (size_t i = 0; i < oneSideFaceCount; ++i) {
|
|
|
|
|
auto quad = m_generatedFaces[i];
|
|
|
|
|
// The following quad vertices should follow the order of 2,1,0,3 strictly,
|
|
|
|
|
// This is to allow 2,1,0 to be grouped as triangle in the later quad to triangles processing.
|
|
|
|
|
// If not follow this order, the front triangle and back triangle maybe cross over because of not be parallel.
|
|
|
|
|
m_generatedFaces.emplace_back(std::vector<size_t> {
|
|
|
|
|
oneSideVertexCount + quad[3],
|
|
|
|
|
oneSideVertexCount + quad[2],
|
|
|
|
|
oneSideVertexCount + quad[1],
|
|
|
|
|
oneSideVertexCount + quad[0]
|
|
|
|
|
oneSideVertexCount + quad[0],
|
|
|
|
|
oneSideVertexCount + quad[3]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move all vertices off distance at radius
|
|
|
|
|
for (size_t i = 0; i < oneSideVertexCount; ++i) {
|
|
|
|
|
auto moveDistance = m_generatedNormals[i] * m_generatedStitchingPoints[i].radius;
|
|
|
|
|
m_generatedVertices[i] += moveDistance;
|
|
|
|
|
m_generatedVertices[i + oneSideVertexCount] -= moveDistance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stitch layers for first stitching line
|
|
|
|
|
for (size_t j = 1; j <= m_targetSegments; ++j) {
|
|
|
|
|
size_t i = j - 1;
|
|
|
|
@ -215,14 +310,39 @@ void StitchMeshBuilder::build()
|
|
|
|
|
|
|
|
|
|
// Stitch layers for last stitching line
|
|
|
|
|
size_t offsetBetweenFirstAndLastBorder = oneSideVertexCount - (m_targetSegments + 1);
|
|
|
|
|
for (size_t j = 1; j <= m_targetSegments; ++j) {
|
|
|
|
|
size_t i = j - 1;
|
|
|
|
|
if (m_splines.back().isClosing) {
|
|
|
|
|
size_t half = m_targetSegments / 2;
|
|
|
|
|
for (size_t j = 1; j <= half; ++j) {
|
|
|
|
|
size_t i = j - 1;
|
|
|
|
|
addQuadButMaybeTriangle(std::vector<size_t> {
|
|
|
|
|
oneSideVertexCount + j + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
oneSideVertexCount + i + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
oneSideVertexCount + (m_targetSegments - i) + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
oneSideVertexCount + (m_targetSegments - j) + offsetBetweenFirstAndLastBorder
|
|
|
|
|
});
|
|
|
|
|
addQuadButMaybeTriangle(std::vector<size_t> {
|
|
|
|
|
(m_targetSegments - j) + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
(m_targetSegments - i) + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
i + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
j + offsetBetweenFirstAndLastBorder
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
m_generatedFaces.emplace_back(std::vector<size_t> {
|
|
|
|
|
oneSideVertexCount + j + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
j + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
i + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
oneSideVertexCount + i + offsetBetweenFirstAndLastBorder
|
|
|
|
|
(m_targetSegments - 0) + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
0 + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
oneSideVertexCount + 0 + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
oneSideVertexCount + (m_targetSegments - 0) + offsetBetweenFirstAndLastBorder
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
for (size_t j = 1; j <= m_targetSegments; ++j) {
|
|
|
|
|
size_t i = j - 1;
|
|
|
|
|
m_generatedFaces.emplace_back(std::vector<size_t> {
|
|
|
|
|
oneSideVertexCount + j + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
j + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
i + offsetBetweenFirstAndLastBorder,
|
|
|
|
|
oneSideVertexCount + i + offsetBetweenFirstAndLastBorder
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stitch layers for all head nodes of stitching lines
|
|
|
|
|