Implement stitching line back closing
parent
7668818ab4
commit
0be42e485b
|
@ -34,8 +34,8 @@ public:
|
||||||
}
|
}
|
||||||
void setRadius(float toRadius)
|
void setRadius(float toRadius)
|
||||||
{
|
{
|
||||||
if (toRadius < 0.005f)
|
if (toRadius < 0.001f)
|
||||||
toRadius = 0.005f;
|
toRadius = 0.001f;
|
||||||
else if (toRadius > 1)
|
else if (toRadius > 1)
|
||||||
toRadius = 1;
|
toRadius = 1;
|
||||||
radius = toRadius;
|
radius = toRadius;
|
||||||
|
|
|
@ -203,6 +203,14 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Vector3 &operator-=(const Vector3 &other)
|
||||||
|
{
|
||||||
|
m_data[0] -= other.x();
|
||||||
|
m_data[1] -= other.y();
|
||||||
|
m_data[2] -= other.z();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline Vector3 &operator*=(double number)
|
inline Vector3 &operator*=(double number)
|
||||||
{
|
{
|
||||||
m_data[0] *= number;
|
m_data[0] *= number;
|
||||||
|
|
|
@ -460,13 +460,22 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineStitchingMesh(const st
|
||||||
|
|
||||||
std::vector<size_t> orderedIndices;
|
std::vector<size_t> orderedIndices;
|
||||||
bool isCircle = false;
|
bool isCircle = false;
|
||||||
|
bool isClosing = false;
|
||||||
|
std::vector<StitchMeshBuilder::Node> orderedBuilderNodes;
|
||||||
|
if (!builderNodeLinks.empty()) {
|
||||||
flattenLinks(builderNodeLinks, &orderedIndices, &isCircle);
|
flattenLinks(builderNodeLinks, &orderedIndices, &isCircle);
|
||||||
std::vector<StitchMeshBuilder::Node> orderedBuilderNodes(orderedIndices.size());
|
orderedBuilderNodes.resize(orderedIndices.size());
|
||||||
for (size_t i = 0; i < orderedIndices.size(); ++i)
|
for (size_t i = 0; i < orderedIndices.size(); ++i)
|
||||||
orderedBuilderNodes[i] = builderNodes[orderedIndices[i]];
|
orderedBuilderNodes[i] = builderNodes[orderedIndices[i]];
|
||||||
|
} else {
|
||||||
|
orderedBuilderNodes.push_back(builderNodes[0]);
|
||||||
|
isClosing = true;
|
||||||
|
}
|
||||||
|
|
||||||
splines.emplace_back(StitchMeshBuilder::Spline {
|
splines.emplace_back(StitchMeshBuilder::Spline {
|
||||||
std::move(orderedBuilderNodes),
|
std::move(orderedBuilderNodes),
|
||||||
isCircle
|
isCircle,
|
||||||
|
isClosing
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
#include <dust3d/base/debug.h>
|
#include <dust3d/base/debug.h>
|
||||||
#include <dust3d/mesh/stitch_mesh_builder.h>
|
#include <dust3d/mesh/stitch_mesh_builder.h>
|
||||||
|
|
||||||
|
@ -41,12 +42,13 @@ const std::vector<std::vector<size_t>> &StitchMeshBuilder::generatedFaces()
|
||||||
return m_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());
|
std::vector<Spline> interpolatedSplines = m_splines;
|
||||||
|
for (size_t i = 0; i < m_splines.size(); ++i) {
|
||||||
for (size_t i = 0; i < splines.size(); ++i) {
|
const auto &spline = m_splines[i];
|
||||||
const auto &spline = splines[i];
|
if (spline.isClosing)
|
||||||
|
continue;
|
||||||
std::vector<Vector3> polyline(spline.nodes.size());
|
std::vector<Vector3> polyline(spline.nodes.size());
|
||||||
std::vector<double> radiuses(spline.nodes.size());
|
std::vector<double> radiuses(spline.nodes.size());
|
||||||
for (size_t j = 0; j < spline.nodes.size(); ++j) {
|
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<Vector3> segmentPoints;
|
||||||
std::vector<double> segmentRadiuses;
|
std::vector<double> segmentRadiuses;
|
||||||
splitPolylineToSegments(polyline, radiuses, m_targetSegments, &segmentPoints, &segmentRadiuses);
|
splitPolylineToSegments(polyline, radiuses, m_targetSegments, &segmentPoints, &segmentRadiuses);
|
||||||
stitchingPoints[i].resize(segmentPoints.size());
|
auto &interpolatedSpline = interpolatedSplines[i];
|
||||||
for (size_t k = 0; k < segmentPoints.size(); ++k) {
|
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].originVertex = m_generatedVertices.size();
|
||||||
stitchingPoints[i][k].radius = segmentRadiuses[k];
|
stitchingPoints[i][k].radius = spline.nodes[k].radius;
|
||||||
m_generatedVertices.push_back(segmentPoints[k]);
|
m_generatedVertices.push_back(spline.nodes[k].origin);
|
||||||
m_generatedStitchingPoints.push_back(stitchingPoints[i][k]);
|
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()
|
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);
|
std::vector<std::vector<StitchingPoint>> stitchingPoints = convertSplinesToStitchingPoints(m_splines);
|
||||||
generateMeshFromStitchingPoints(stitchingPoints);
|
generateMeshFromStitchingPoints(stitchingPoints);
|
||||||
|
|
||||||
|
@ -179,29 +269,34 @@ void StitchMeshBuilder::build()
|
||||||
for (auto &it: m_generatedNormals)
|
for (auto &it: m_generatedNormals)
|
||||||
it.normalize();
|
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
|
// Generate other side of faces
|
||||||
auto oneSideVertexCount = m_generatedVertices.size();
|
auto oneSideVertexCount = m_generatedVertices.size();
|
||||||
m_generatedVertices.reserve(oneSideVertexCount * 2);
|
m_generatedVertices.reserve(oneSideVertexCount * 2);
|
||||||
for (size_t i = 0; i < oneSideVertexCount; ++i) {
|
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);
|
m_generatedFaces.reserve(m_generatedFaces.size() * 2);
|
||||||
auto oneSideFaceCount = m_generatedFaces.size();
|
auto oneSideFaceCount = m_generatedFaces.size();
|
||||||
for (size_t i = 0; i < oneSideFaceCount; ++i) {
|
for (size_t i = 0; i < oneSideFaceCount; ++i) {
|
||||||
auto quad = m_generatedFaces[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> {
|
m_generatedFaces.emplace_back(std::vector<size_t> {
|
||||||
oneSideVertexCount + quad[3],
|
|
||||||
oneSideVertexCount + quad[2],
|
oneSideVertexCount + quad[2],
|
||||||
oneSideVertexCount + quad[1],
|
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
|
// Stitch layers for first stitching line
|
||||||
for (size_t j = 1; j <= m_targetSegments; ++j) {
|
for (size_t j = 1; j <= m_targetSegments; ++j) {
|
||||||
size_t i = j - 1;
|
size_t i = j - 1;
|
||||||
|
@ -215,6 +310,30 @@ void StitchMeshBuilder::build()
|
||||||
|
|
||||||
// Stitch layers for last stitching line
|
// Stitch layers for last stitching line
|
||||||
size_t offsetBetweenFirstAndLastBorder = oneSideVertexCount - (m_targetSegments + 1);
|
size_t offsetBetweenFirstAndLastBorder = oneSideVertexCount - (m_targetSegments + 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> {
|
||||||
|
(m_targetSegments - 0) + offsetBetweenFirstAndLastBorder,
|
||||||
|
0 + offsetBetweenFirstAndLastBorder,
|
||||||
|
oneSideVertexCount + 0 + offsetBetweenFirstAndLastBorder,
|
||||||
|
oneSideVertexCount + (m_targetSegments - 0) + offsetBetweenFirstAndLastBorder
|
||||||
|
});
|
||||||
|
} else {
|
||||||
for (size_t j = 1; j <= m_targetSegments; ++j) {
|
for (size_t j = 1; j <= m_targetSegments; ++j) {
|
||||||
size_t i = j - 1;
|
size_t i = j - 1;
|
||||||
m_generatedFaces.emplace_back(std::vector<size_t> {
|
m_generatedFaces.emplace_back(std::vector<size_t> {
|
||||||
|
@ -224,6 +343,7 @@ void StitchMeshBuilder::build()
|
||||||
oneSideVertexCount + i + offsetBetweenFirstAndLastBorder
|
oneSideVertexCount + i + offsetBetweenFirstAndLastBorder
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Stitch layers for all head nodes of stitching lines
|
// Stitch layers for all head nodes of stitching lines
|
||||||
for (size_t j = 1; j < m_splines.size(); ++j) {
|
for (size_t j = 1; j < m_splines.size(); ++j) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ public:
|
||||||
{
|
{
|
||||||
std::vector<Node> nodes;
|
std::vector<Node> nodes;
|
||||||
bool isCircle = false;
|
bool isCircle = false;
|
||||||
|
bool isClosing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
StitchMeshBuilder(std::vector<Spline> &&splines);
|
StitchMeshBuilder(std::vector<Spline> &&splines);
|
||||||
|
@ -60,8 +61,9 @@ private:
|
||||||
std::vector<Vector3> m_generatedVertices;
|
std::vector<Vector3> m_generatedVertices;
|
||||||
std::vector<std::vector<size_t>> m_generatedFaces;
|
std::vector<std::vector<size_t>> m_generatedFaces;
|
||||||
std::vector<Vector3> m_generatedNormals;
|
std::vector<Vector3> m_generatedNormals;
|
||||||
size_t m_targetSegments = 5;
|
size_t m_targetSegments = 15;
|
||||||
|
|
||||||
|
void interpolateSplinesToHaveEqualSizeOfNodesExceptClosingSplines();
|
||||||
std::vector<std::vector<StitchingPoint>> convertSplinesToStitchingPoints(const std::vector<Spline> &splines);
|
std::vector<std::vector<StitchingPoint>> convertSplinesToStitchingPoints(const std::vector<Spline> &splines);
|
||||||
void generateMeshFromStitchingPoints(const std::vector<std::vector<StitchingPoint>> &stitchingPoints);
|
void generateMeshFromStitchingPoints(const std::vector<std::vector<StitchingPoint>> &stitchingPoints);
|
||||||
void generateMeshFromStitchingPoints(const std::vector<StitchingPoint> &a,
|
void generateMeshFromStitchingPoints(const std::vector<StitchingPoint> &a,
|
||||||
|
@ -72,6 +74,8 @@ private:
|
||||||
std::vector<Vector3> *targetPoints,
|
std::vector<Vector3> *targetPoints,
|
||||||
std::vector<double> *targetRadiuses);
|
std::vector<double> *targetRadiuses);
|
||||||
double segmentsLength(const std::vector<Vector3> &segmentPoints);
|
double segmentsLength(const std::vector<Vector3> &segmentPoints);
|
||||||
|
void interpolateSplinesForBackClosing();
|
||||||
|
void addQuadButMaybeTriangle(const std::vector<size_t> &quadButMaybeTriangle);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue