Prepare the stitching line feature
parent
29f7e06701
commit
8b64425341
|
@ -283,6 +283,8 @@ HEADERS += ../dust3d/mesh/resolve_triangle_tangent.h
|
|||
SOURCES += ../dust3d/mesh/resolve_triangle_tangent.cc
|
||||
HEADERS += ../dust3d/mesh/smooth_normal.h
|
||||
SOURCES += ../dust3d/mesh/smooth_normal.cc
|
||||
HEADERS += ../dust3d/mesh/stitch_mesh_builder.h
|
||||
SOURCES += ../dust3d/mesh/stitch_mesh_builder.cc
|
||||
HEADERS += ../dust3d/mesh/stroke_mesh_builder.h
|
||||
SOURCES += ../dust3d/mesh/stroke_mesh_builder.cc
|
||||
HEADERS += ../dust3d/mesh/stroke_modifier.h
|
||||
|
|
Binary file not shown.
|
@ -48,7 +48,7 @@ void MaterialPreviewsGenerator::generate()
|
|||
|
||||
std::vector<dust3d::Uuid> partIds;
|
||||
dust3d::Ds3FileReader ds3Reader((const std::uint8_t *)fileData.data(), fileData.size());
|
||||
for (int i = 0; i < ds3Reader.items().size(); ++i) {
|
||||
for (int i = 0; i < (int)ds3Reader.items().size(); ++i) {
|
||||
dust3d::Ds3ReaderItem item = ds3Reader.items().at(i);
|
||||
if (item.type == "model") {
|
||||
std::vector<std::uint8_t> data;
|
||||
|
|
|
@ -32,6 +32,7 @@ enum class PartTarget
|
|||
{
|
||||
Model = 0,
|
||||
CutFace,
|
||||
StitchingLine,
|
||||
Count
|
||||
};
|
||||
PartTarget PartTargetFromString(const char *targetString);
|
||||
|
@ -43,6 +44,8 @@ PartTarget PartTargetFromString(const char *targetString)
|
|||
return PartTarget::Model; \
|
||||
if (target == "CutFace") \
|
||||
return PartTarget::CutFace; \
|
||||
if (target == "StitchingLine") \
|
||||
return PartTarget::StitchingLine; \
|
||||
return PartTarget::Model; \
|
||||
}
|
||||
const char *PartTargetToString(PartTarget target);
|
||||
|
@ -54,6 +57,8 @@ const char *PartTargetToString(PartTarget target)
|
|||
return "Model"; \
|
||||
case PartTarget::CutFace: \
|
||||
return "CutFace"; \
|
||||
case PartTarget::StitchingLine: \
|
||||
return "StitchingLine"; \
|
||||
default: \
|
||||
return "Model"; \
|
||||
} \
|
||||
|
@ -67,6 +72,8 @@ std::string PartTargetToDispName(PartTarget target)
|
|||
return std::string("Model"); \
|
||||
case PartTarget::CutFace: \
|
||||
return std::string("Cut Face"); \
|
||||
case PartTarget::StitchingLine: \
|
||||
return std::string("Stitching Line"); \
|
||||
default: \
|
||||
return std::string("Model"); \
|
||||
} \
|
||||
|
|
|
@ -31,31 +31,28 @@ namespace dust3d
|
|||
|
||||
MeshCombiner::Mesh::Mesh(const std::vector<Vector3> &vertices, const std::vector<std::vector<size_t>> &faces)
|
||||
{
|
||||
m_vertices = new std::vector<Vector3>(vertices);
|
||||
m_triangles = new std::vector<std::vector<size_t>>;
|
||||
triangulate(vertices, faces, m_triangles);
|
||||
m_solidMesh = new SolidMesh;
|
||||
m_solidMesh->setVertices(m_vertices);
|
||||
m_solidMesh->setTriangles(m_triangles);
|
||||
m_vertices = std::make_unique<std::vector<Vector3>>(vertices);
|
||||
m_triangles = std::make_unique<std::vector<std::vector<size_t>>>();
|
||||
triangulate(vertices, faces, m_triangles.get());
|
||||
m_solidMesh = std::make_unique<SolidMesh>();
|
||||
m_solidMesh->setVertices(m_vertices.get());
|
||||
m_solidMesh->setTriangles(m_triangles.get());
|
||||
m_solidMesh->prepare();
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh::Mesh(const Mesh &other)
|
||||
{
|
||||
m_vertices = new std::vector<Vector3>;
|
||||
m_triangles = new std::vector<std::vector<size_t>>;
|
||||
m_vertices = std::make_unique<std::vector<Vector3>>();
|
||||
m_triangles = std::make_unique<std::vector<std::vector<size_t>>>();
|
||||
other.fetch(*m_vertices, *m_triangles);
|
||||
m_solidMesh = new SolidMesh;
|
||||
m_solidMesh->setVertices(m_vertices);
|
||||
m_solidMesh->setTriangles(m_triangles);
|
||||
m_solidMesh = std::make_unique<SolidMesh>();
|
||||
m_solidMesh->setVertices(m_vertices.get());
|
||||
m_solidMesh->setTriangles(m_triangles.get());
|
||||
m_solidMesh->prepare();
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh::~Mesh()
|
||||
{
|
||||
delete m_solidMesh;
|
||||
delete m_vertices;
|
||||
delete m_triangles;
|
||||
}
|
||||
|
||||
void MeshCombiner::Mesh::fetch(std::vector<Vector3> &vertices, std::vector<std::vector<size_t>> &faces) const
|
||||
|
@ -72,19 +69,13 @@ bool MeshCombiner::Mesh::isNull() const
|
|||
return nullptr == m_vertices || m_vertices->empty();
|
||||
}
|
||||
|
||||
bool MeshCombiner::Mesh::isCombinable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh *MeshCombiner::combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,
|
||||
std::vector<std::pair<Source, size_t>> *combinedVerticesComeFrom)
|
||||
{
|
||||
if (firstMesh.isNull() || !firstMesh.isCombinable() ||
|
||||
secondMesh.isNull() || !secondMesh.isCombinable())
|
||||
if (firstMesh.isNull() || secondMesh.isNull())
|
||||
return nullptr;
|
||||
|
||||
SolidMeshBooleanOperation booleanOperation(firstMesh.m_solidMesh, secondMesh.m_solidMesh);
|
||||
SolidMeshBooleanOperation booleanOperation(firstMesh.m_solidMesh.get(), secondMesh.m_solidMesh.get());
|
||||
if (!booleanOperation.combine())
|
||||
return nullptr;
|
||||
|
||||
|
@ -96,14 +87,14 @@ MeshCombiner::Mesh *MeshCombiner::combine(const Mesh &firstMesh, const Mesh &sec
|
|||
if (nullptr == vertices)
|
||||
return;
|
||||
for (const auto &point: *vertices) {
|
||||
auto insertResult = verticesSourceMap.insert({{point.x(), point.y(), point.z()},
|
||||
verticesSourceMap.insert({{point.x(), point.y(), point.z()},
|
||||
{source, vertexIndex}});
|
||||
++vertexIndex;
|
||||
}
|
||||
};
|
||||
if (nullptr != combinedVerticesComeFrom) {
|
||||
addToSourceMap(firstMesh.m_solidMesh, Source::First);
|
||||
addToSourceMap(secondMesh.m_solidMesh, Source::Second);
|
||||
addToSourceMap(firstMesh.m_solidMesh.get(), Source::First);
|
||||
addToSourceMap(secondMesh.m_solidMesh.get(), Source::Second);
|
||||
}
|
||||
|
||||
std::vector<std::vector<size_t>> resultTriangles;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define DUST3D_MESH_MESH_COMBINER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <dust3d/base/vector3.h>
|
||||
#include <dust3d/mesh/solid_mesh.h>
|
||||
|
||||
|
@ -55,14 +56,13 @@ public:
|
|||
~Mesh();
|
||||
void fetch(std::vector<Vector3> &vertices, std::vector<std::vector<size_t>> &faces) const;
|
||||
bool isNull() const;
|
||||
bool isCombinable() const;
|
||||
|
||||
friend MeshCombiner;
|
||||
|
||||
private:
|
||||
SolidMesh *m_solidMesh = nullptr;
|
||||
std::vector<Vector3> *m_vertices = nullptr;
|
||||
std::vector<std::vector<size_t>> *m_triangles = nullptr;
|
||||
std::unique_ptr<SolidMesh> m_solidMesh;
|
||||
std::unique_ptr<std::vector<Vector3>> m_vertices;
|
||||
std::unique_ptr<std::vector<std::vector<size_t>>> m_triangles;
|
||||
};
|
||||
|
||||
static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,
|
||||
|
|
|
@ -375,7 +375,7 @@ void MeshGenerator::cutFaceStringToCutTemplate(const std::string &cutFaceString,
|
|||
}
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes)
|
||||
std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes)
|
||||
{
|
||||
auto findPart = m_snapshot->parts.find(partIdString);
|
||||
if (findPart == m_snapshot->parts.end()) {
|
||||
|
@ -473,7 +473,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
|||
partCache.previewVertices.clear();
|
||||
partCache.isSuccessful = false;
|
||||
partCache.joined = (target == PartTarget::Model && !isDisabled);
|
||||
partCache.releaseMeshes();
|
||||
|
||||
struct NodeInfo
|
||||
{
|
||||
|
@ -545,7 +544,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
|||
bool buildSucceed = false;
|
||||
std::map<std::string, int> nodeIdStringToIndexMap;
|
||||
std::map<int, std::string> nodeIndexToIdStringMap;
|
||||
StrokeModifier *strokeModifier = nullptr;
|
||||
|
||||
auto addNodeToPartCache = [&](const std::string &nodeIdString, const NodeInfo &nodeInfo) {
|
||||
ObjectNode objectNode;
|
||||
|
@ -575,7 +573,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
|||
});
|
||||
};
|
||||
|
||||
strokeModifier = new StrokeModifier;
|
||||
auto strokeModifier = std::make_unique<StrokeModifier>();
|
||||
|
||||
if (smooth)
|
||||
strokeModifier->enableSmooth();
|
||||
|
@ -626,7 +624,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
|||
|
||||
std::vector<size_t> sourceNodeIndices;
|
||||
|
||||
StrokeMeshBuilder *strokeMeshBuilder = new StrokeMeshBuilder;
|
||||
auto strokeMeshBuilder = std::make_unique<StrokeMeshBuilder>();
|
||||
|
||||
strokeMeshBuilder->setDeformThickness(deformThickness);
|
||||
strokeMeshBuilder->setDeformWidth(deformWidth);
|
||||
|
@ -680,14 +678,11 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
|||
partCache.objectNodeVertices.push_back({position, {partIdString, nodeIdString}});
|
||||
}
|
||||
|
||||
delete strokeMeshBuilder;
|
||||
strokeMeshBuilder = nullptr;
|
||||
|
||||
bool hasMeshError = false;
|
||||
MeshCombiner::Mesh *mesh = nullptr;
|
||||
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||
|
||||
if (buildSucceed) {
|
||||
mesh = new MeshCombiner::Mesh(partCache.vertices, partCache.faces);
|
||||
mesh = std::make_unique<MeshCombiner::Mesh>(partCache.vertices, partCache.faces);
|
||||
if (mesh->isNull()) {
|
||||
hasMeshError = true;
|
||||
}
|
||||
|
@ -698,7 +693,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
|||
std::vector<Vector3> partPreviewVertices;
|
||||
Color partPreviewColor = partColor;
|
||||
if (nullptr != mesh) {
|
||||
partCache.mesh = new MeshCombiner::Mesh(*mesh);
|
||||
mesh->fetch(partPreviewVertices, partCache.previewTriangles);
|
||||
partCache.previewVertices = partPreviewVertices;
|
||||
partCache.isSuccessful = true;
|
||||
|
@ -747,21 +741,16 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
|||
}
|
||||
}
|
||||
|
||||
delete strokeModifier;
|
||||
|
||||
if (mesh && mesh->isNull()) {
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
mesh.reset();
|
||||
}
|
||||
|
||||
if (isDisabled) {
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
mesh.reset();
|
||||
}
|
||||
|
||||
if (target != PartTarget::Model) {
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
mesh.reset();
|
||||
}
|
||||
|
||||
if (hasMeshError && target == PartTarget::Model) {
|
||||
|
@ -820,9 +809,9 @@ std::string MeshGenerator::componentColorName(const std::map<std::string, std::s
|
|||
return std::string();
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode)
|
||||
std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode)
|
||||
{
|
||||
MeshCombiner::Mesh *mesh = nullptr;
|
||||
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||
|
||||
Uuid componentId;
|
||||
const std::map<std::string, std::string> *component = &m_snapshot->rootComponent;
|
||||
|
@ -842,7 +831,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
|||
if (m_cacheEnabled) {
|
||||
if (m_dirtyComponentIds.find(componentIdString) == m_dirtyComponentIds.end()) {
|
||||
if (nullptr != componentCache.mesh)
|
||||
return new MeshCombiner::Mesh(*componentCache.mesh);
|
||||
return std::make_unique<MeshCombiner::Mesh>(*componentCache.mesh);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -851,7 +840,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
|||
componentCache.objectNodes.clear();
|
||||
componentCache.objectEdges.clear();
|
||||
componentCache.objectNodeVertices.clear();
|
||||
componentCache.releaseMeshes();
|
||||
componentCache.mesh.reset();
|
||||
|
||||
std::string linkDataType = String::valueOrEmpty(*component, "linkDataType");
|
||||
if ("partId" == linkDataType) {
|
||||
|
@ -860,8 +849,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
|||
bool retryable = true;
|
||||
mesh = combinePartMesh(partIdString, &hasError, &retryable, m_interpolationEnabled);
|
||||
if (hasError) {
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
mesh.reset();
|
||||
if (retryable && m_interpolationEnabled) {
|
||||
hasError = false;
|
||||
mesh = combinePartMesh(partIdString, &hasError, &retryable, false);
|
||||
|
@ -870,7 +858,6 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
|||
m_isSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
const auto &partCache = m_cacheContext->parts[partIdString];
|
||||
for (const auto &vertex: partCache.vertices)
|
||||
componentCache.noneSeamVertices.insert(vertex);
|
||||
|
@ -882,19 +869,13 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
|||
for (const auto &it: partCache.objectNodeVertices)
|
||||
componentCache.objectNodeVertices.push_back(it);
|
||||
} else {
|
||||
std::vector<std::pair<CombineMode, std::vector<std::pair<std::string, std::string>>>> combineGroups;
|
||||
// Firstly, group by combine mode
|
||||
std::vector<std::pair<CombineMode, std::vector<std::string>>> combineGroups;
|
||||
int currentGroupIndex = -1;
|
||||
auto lastCombineMode = CombineMode::Count;
|
||||
bool foundColorSolubilitySetting = false;
|
||||
for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) {
|
||||
if (childIdString.empty())
|
||||
continue;
|
||||
const auto &child = findComponent(childIdString);
|
||||
std::string colorName = componentColorName(child);
|
||||
if (colorName == "+") {
|
||||
foundColorSolubilitySetting = true;
|
||||
}
|
||||
auto combineMode = componentCombineMode(child);
|
||||
if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) {
|
||||
combineGroups.push_back({combineMode, {}});
|
||||
|
@ -904,113 +885,44 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
|||
if (-1 == currentGroupIndex) {
|
||||
continue;
|
||||
}
|
||||
combineGroups[currentGroupIndex].second.push_back({childIdString, colorName});
|
||||
combineGroups[currentGroupIndex].second.push_back(childIdString);
|
||||
}
|
||||
// Secondly, sub group by color
|
||||
std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, std::string>> groupMeshes;
|
||||
std::vector<std::tuple<std::unique_ptr<MeshCombiner::Mesh>, CombineMode, std::string>> groupMeshes;
|
||||
for (const auto &group: combineGroups) {
|
||||
std::set<size_t> used;
|
||||
std::vector<std::vector<std::string>> componentIdStrings;
|
||||
int currentSubGroupIndex = -1;
|
||||
auto lastColorName = std::string();
|
||||
for (size_t i = 0; i < group.second.size(); ++i) {
|
||||
if (used.find(i) != used.end())
|
||||
auto childMesh = combineComponentChildGroupMesh(group.second, componentCache);
|
||||
if (nullptr == childMesh || childMesh->isNull())
|
||||
continue;
|
||||
//const auto &colorName = group.second[i].second;
|
||||
const std::string colorName = "white"; // Force to use the same color = deactivate combine by color
|
||||
if (lastColorName != colorName || lastColorName.empty()) {
|
||||
componentIdStrings.push_back({});
|
||||
++currentSubGroupIndex;
|
||||
lastColorName = colorName;
|
||||
groupMeshes.emplace_back(std::make_tuple(std::move(childMesh), group.first, String::join(group.second, "|")));
|
||||
}
|
||||
if (-1 == currentSubGroupIndex) {
|
||||
continue;
|
||||
}
|
||||
used.insert(i);
|
||||
componentIdStrings[currentSubGroupIndex].push_back(group.second[i].first);
|
||||
if (colorName.empty())
|
||||
continue;
|
||||
for (size_t j = i + 1; j < group.second.size(); ++j) {
|
||||
if (used.find(j) != used.end())
|
||||
continue;
|
||||
const auto &otherColorName = group.second[j].second;
|
||||
if (otherColorName.empty())
|
||||
continue;
|
||||
if (otherColorName != colorName)
|
||||
continue;
|
||||
used.insert(j);
|
||||
componentIdStrings[currentSubGroupIndex].push_back(group.second[j].first);
|
||||
}
|
||||
}
|
||||
std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, std::string>> multipleMeshes;
|
||||
std::vector<std::string> subGroupMeshIdStringList;
|
||||
for (const auto &it: componentIdStrings) {
|
||||
std::vector<std::string> componentChildGroupIdStringList;
|
||||
for (const auto &componentChildGroupIdString: it)
|
||||
componentChildGroupIdStringList.push_back(componentChildGroupIdString);
|
||||
MeshCombiner::Mesh *childMesh = combineComponentChildGroupMesh(it, componentCache);
|
||||
if (nullptr == childMesh)
|
||||
continue;
|
||||
if (childMesh->isNull()) {
|
||||
delete childMesh;
|
||||
continue;
|
||||
}
|
||||
std::string componentChildGroupIdStringListString = String::join(componentChildGroupIdStringList, "|");
|
||||
subGroupMeshIdStringList.push_back(componentChildGroupIdStringListString);
|
||||
multipleMeshes.push_back(std::make_tuple(childMesh, CombineMode::Normal, componentChildGroupIdStringListString));
|
||||
}
|
||||
MeshCombiner::Mesh *subGroupMesh = combineMultipleMeshes(multipleMeshes, true/*foundColorSolubilitySetting*/);
|
||||
if (nullptr == subGroupMesh)
|
||||
continue;
|
||||
groupMeshes.push_back(std::make_tuple(subGroupMesh, group.first, String::join(subGroupMeshIdStringList, "&")));
|
||||
}
|
||||
mesh = combineMultipleMeshes(groupMeshes, true);
|
||||
mesh = combineMultipleMeshes(std::move(groupMeshes), true);
|
||||
}
|
||||
|
||||
if (nullptr != mesh)
|
||||
componentCache.mesh = new MeshCombiner::Mesh(*mesh);
|
||||
componentCache.mesh = std::make_unique<MeshCombiner::Mesh>(*mesh);
|
||||
|
||||
if (nullptr != mesh && mesh->isNull()) {
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
}
|
||||
|
||||
if (componentId.isNull()) {
|
||||
// Prepare cloth collision shap
|
||||
if (nullptr != mesh && !mesh->isNull()) {
|
||||
m_clothCollisionVertices.clear();
|
||||
m_clothCollisionTriangles.clear();
|
||||
mesh->fetch(m_clothCollisionVertices, m_clothCollisionTriangles);
|
||||
} else {
|
||||
// TODO: when no body is valid, may add ground plane as collision shape
|
||||
// ... ...
|
||||
}
|
||||
mesh.reset();
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, std::string>> &multipleMeshes, bool recombine)
|
||||
std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineMultipleMeshes(std::vector<std::tuple<std::unique_ptr<MeshCombiner::Mesh>, CombineMode, std::string>> &&multipleMeshes, bool recombine)
|
||||
{
|
||||
MeshCombiner::Mesh *mesh = nullptr;
|
||||
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||
std::string meshIdStrings;
|
||||
for (const auto &it: multipleMeshes) {
|
||||
for (auto &it: multipleMeshes) {
|
||||
auto subMesh = std::move(std::get<0>(it));
|
||||
const auto &childCombineMode = std::get<1>(it);
|
||||
MeshCombiner::Mesh *subMesh = std::get<0>(it);
|
||||
const std::string &subMeshIdString = std::get<2>(it);
|
||||
if (nullptr == subMesh || subMesh->isNull()) {
|
||||
delete subMesh;
|
||||
continue;
|
||||
}
|
||||
if (!subMesh->isCombinable()) {
|
||||
// TODO: Collect vertices
|
||||
delete subMesh;
|
||||
continue;
|
||||
}
|
||||
if (nullptr == mesh) {
|
||||
mesh = subMesh;
|
||||
mesh = std::move(subMesh);
|
||||
meshIdStrings = subMeshIdString;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
auto combinerMethod = childCombineMode == CombineMode::Inversion ?
|
||||
MeshCombiner::Method::Diff : MeshCombiner::Method::Union;
|
||||
auto combinerMethodString = combinerMethod == MeshCombiner::Method::Union ?
|
||||
|
@ -1018,48 +930,42 @@ MeshCombiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector<std::
|
|||
meshIdStrings += combinerMethodString + subMeshIdString;
|
||||
if (recombine)
|
||||
meshIdStrings += "!";
|
||||
MeshCombiner::Mesh *newMesh = nullptr;
|
||||
std::unique_ptr<MeshCombiner::Mesh> newMesh;
|
||||
auto findCached = m_cacheContext->cachedCombination.find(meshIdStrings);
|
||||
if (findCached != m_cacheContext->cachedCombination.end()) {
|
||||
if (nullptr != findCached->second) {
|
||||
newMesh = new MeshCombiner::Mesh(*findCached->second);
|
||||
newMesh = std::make_unique<MeshCombiner::Mesh>(*findCached->second);
|
||||
}
|
||||
} else {
|
||||
newMesh = combineTwoMeshes(*mesh,
|
||||
*subMesh,
|
||||
combinerMethod,
|
||||
recombine);
|
||||
delete subMesh;
|
||||
if (nullptr != newMesh)
|
||||
m_cacheContext->cachedCombination.insert({meshIdStrings, new MeshCombiner::Mesh(*newMesh)});
|
||||
m_cacheContext->cachedCombination.insert({meshIdStrings, std::make_unique<MeshCombiner::Mesh>(*newMesh)});
|
||||
else
|
||||
m_cacheContext->cachedCombination.insert({meshIdStrings, nullptr});
|
||||
}
|
||||
if (newMesh && !newMesh->isNull()) {
|
||||
delete mesh;
|
||||
mesh = newMesh;
|
||||
mesh = std::move(newMesh);
|
||||
} else {
|
||||
m_isSuccessful = false;
|
||||
delete newMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nullptr != mesh && mesh->isNull()) {
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
mesh.reset();
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const std::vector<std::string> &componentIdStrings, GeneratedComponent &componentCache)
|
||||
std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineComponentChildGroupMesh(const std::vector<std::string> &componentIdStrings, GeneratedComponent &componentCache)
|
||||
{
|
||||
std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, std::string>> multipleMeshes;
|
||||
std::vector<std::tuple<std::unique_ptr<MeshCombiner::Mesh>, CombineMode, std::string>> multipleMeshes;
|
||||
for (const auto &childIdString: componentIdStrings) {
|
||||
CombineMode childCombineMode = CombineMode::Normal;
|
||||
MeshCombiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode);
|
||||
std::unique_ptr<MeshCombiner::Mesh> subMesh = combineComponentMesh(childIdString, &childCombineMode);
|
||||
|
||||
if (CombineMode::Uncombined == childCombineMode) {
|
||||
delete subMesh;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1076,31 +982,25 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const std::vec
|
|||
componentCache.objectNodeVertices.push_back(it);
|
||||
|
||||
if (nullptr == subMesh || subMesh->isNull()) {
|
||||
delete subMesh;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!subMesh->isCombinable()) {
|
||||
componentCache.incombinableMeshes.push_back(subMesh);
|
||||
continue;
|
||||
multipleMeshes.emplace_back(std::make_tuple(std::move(subMesh), childCombineMode, childIdString));
|
||||
}
|
||||
|
||||
multipleMeshes.push_back(std::make_tuple(subMesh, childCombineMode, childIdString));
|
||||
}
|
||||
return combineMultipleMeshes(multipleMeshes);
|
||||
return combineMultipleMeshes(std::move(multipleMeshes));
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh *MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second,
|
||||
std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second,
|
||||
MeshCombiner::Method method,
|
||||
bool recombine)
|
||||
{
|
||||
if (first.isNull() || second.isNull())
|
||||
return nullptr;
|
||||
std::vector<std::pair<MeshCombiner::Source, size_t>> combinedVerticesSources;
|
||||
MeshCombiner::Mesh *newMesh = MeshCombiner::combine(first,
|
||||
auto newMesh = std::unique_ptr<MeshCombiner::Mesh>(MeshCombiner::combine(first,
|
||||
second,
|
||||
method,
|
||||
&combinedVerticesSources);
|
||||
&combinedVerticesSources));
|
||||
if (nullptr == newMesh)
|
||||
return nullptr;
|
||||
if (!newMesh->isNull() && recombine) {
|
||||
|
@ -1112,18 +1012,14 @@ MeshCombiner::Mesh *MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh &fi
|
|||
recombiner.setFaces(&combinedFaces);
|
||||
if (recombiner.recombine()) {
|
||||
if (isWatertight(recombiner.regeneratedFaces())) {
|
||||
MeshCombiner::Mesh *reMesh = new MeshCombiner::Mesh(recombiner.regeneratedVertices(), recombiner.regeneratedFaces());
|
||||
if (!reMesh->isNull() && reMesh->isCombinable()) {
|
||||
delete newMesh;
|
||||
newMesh = reMesh;
|
||||
} else {
|
||||
delete reMesh;
|
||||
auto reMesh = std::make_unique<MeshCombiner::Mesh>(recombiner.regeneratedVertices(), recombiner.regeneratedFaces());
|
||||
if (!reMesh->isNull()) {
|
||||
newMesh = std::move(reMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newMesh->isNull()) {
|
||||
delete newMesh;
|
||||
return nullptr;
|
||||
}
|
||||
return newMesh;
|
||||
|
@ -1245,23 +1141,6 @@ void MeshGenerator::postprocessObject(Object *object)
|
|||
object->setTriangleVertexNormals(triangleVertexNormals);
|
||||
}
|
||||
|
||||
void MeshGenerator::collectIncombinableComponentMeshes(const std::string &componentIdString)
|
||||
{
|
||||
const auto &component = findComponent(componentIdString);
|
||||
if (CombineMode::Uncombined == componentCombineMode(component))
|
||||
return;
|
||||
const auto &componentCache = m_cacheContext->components[componentIdString];
|
||||
for (const auto &mesh: componentCache.incombinableMeshes) {
|
||||
m_isSuccessful = false;
|
||||
collectIncombinableMesh(mesh, componentCache);
|
||||
}
|
||||
for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) {
|
||||
if (childIdString.empty())
|
||||
continue;
|
||||
collectIncombinableComponentMeshes(childIdString);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshGenerator::collectIncombinableMesh(const MeshCombiner::Mesh *mesh, const GeneratedComponent &componentCache)
|
||||
{
|
||||
if (nullptr == mesh)
|
||||
|
@ -1302,7 +1181,7 @@ void MeshGenerator::collectUncombinedComponent(const std::string &componentIdStr
|
|||
m_object->edges.insert(m_object->edges.end(), componentCache.objectEdges.begin(), componentCache.objectEdges.end());
|
||||
m_nodeVertices.insert(m_nodeVertices.end(), componentCache.objectNodeVertices.begin(), componentCache.objectNodeVertices.end());
|
||||
|
||||
collectIncombinableMesh(componentCache.mesh, componentCache);
|
||||
collectIncombinableMesh(componentCache.mesh.get(), componentCache);
|
||||
return;
|
||||
}
|
||||
for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) {
|
||||
|
@ -1459,7 +1338,6 @@ void MeshGenerator::generate()
|
|||
}
|
||||
m_cacheContext->partMirrorIdMap.erase(mirrorFrom);
|
||||
}
|
||||
it->second.releaseMeshes();
|
||||
it = m_cacheContext->parts.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
@ -1469,13 +1347,11 @@ void MeshGenerator::generate()
|
|||
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
||||
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
||||
if (std::string::npos != combinationIt->first.find(it->first)) {
|
||||
delete combinationIt->second;
|
||||
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
||||
continue;
|
||||
}
|
||||
combinationIt++;
|
||||
}
|
||||
it->second.releaseMeshes();
|
||||
it = m_cacheContext->components.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
@ -1489,7 +1365,6 @@ void MeshGenerator::generate()
|
|||
for (const auto &dirtyComponentId: m_dirtyComponentIds) {
|
||||
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
||||
if (std::string::npos != combinationIt->first.find(dirtyComponentId)) {
|
||||
delete combinationIt->second;
|
||||
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
||||
continue;
|
||||
}
|
||||
|
@ -1533,13 +1408,10 @@ void MeshGenerator::generate()
|
|||
|
||||
// Recursively check uncombined components
|
||||
collectUncombinedComponent(to_string(Uuid()));
|
||||
collectIncombinableComponentMeshes(to_string(Uuid()));
|
||||
|
||||
collectErroredParts();
|
||||
postprocessObject(m_object);
|
||||
|
||||
delete combinedMesh;
|
||||
|
||||
if (needDeleteCacheContext) {
|
||||
delete m_cacheContext;
|
||||
m_cacheContext = nullptr;
|
||||
|
|
|
@ -40,19 +40,8 @@ class MeshGenerator
|
|||
{
|
||||
public:
|
||||
|
||||
class GeneratedPart
|
||||
struct GeneratedPart
|
||||
{
|
||||
public:
|
||||
~GeneratedPart()
|
||||
{
|
||||
releaseMeshes();
|
||||
};
|
||||
void releaseMeshes()
|
||||
{
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
}
|
||||
MeshCombiner::Mesh *mesh = nullptr;
|
||||
std::vector<Vector3> vertices;
|
||||
std::vector<std::vector<size_t>> faces;
|
||||
std::vector<ObjectNode> objectNodes;
|
||||
|
@ -64,23 +53,9 @@ public:
|
|||
bool joined = true;
|
||||
};
|
||||
|
||||
class GeneratedComponent
|
||||
struct GeneratedComponent
|
||||
{
|
||||
public:
|
||||
~GeneratedComponent()
|
||||
{
|
||||
releaseMeshes();
|
||||
};
|
||||
void releaseMeshes()
|
||||
{
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
for (auto &it: incombinableMeshes)
|
||||
delete it;
|
||||
incombinableMeshes.clear();
|
||||
}
|
||||
MeshCombiner::Mesh *mesh = nullptr;
|
||||
std::vector<MeshCombiner::Mesh *> incombinableMeshes;
|
||||
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||
std::set<std::pair<PositionKey, PositionKey>> sharedQuadEdges;
|
||||
std::set<PositionKey> noneSeamVertices;
|
||||
std::vector<ObjectNode> objectNodes;
|
||||
|
@ -88,27 +63,16 @@ public:
|
|||
std::vector<std::pair<Vector3, std::pair<Uuid, Uuid>>> objectNodeVertices;
|
||||
};
|
||||
|
||||
class GeneratedCacheContext
|
||||
struct GeneratedCacheContext
|
||||
{
|
||||
public:
|
||||
~GeneratedCacheContext()
|
||||
{
|
||||
for (auto &it: cachedCombination)
|
||||
delete it.second;
|
||||
for (auto &it: parts)
|
||||
it.second.releaseMeshes();
|
||||
for (auto &it: components)
|
||||
it.second.releaseMeshes();
|
||||
}
|
||||
std::map<std::string, GeneratedComponent> components;
|
||||
std::map<std::string, GeneratedPart> parts;
|
||||
std::map<std::string, std::string> partMirrorIdMap;
|
||||
std::map<std::string, MeshCombiner::Mesh *> cachedCombination;
|
||||
std::map<std::string, std::unique_ptr<MeshCombiner::Mesh>> cachedCombination;
|
||||
};
|
||||
|
||||
class PartPreview
|
||||
struct PartPreview
|
||||
{
|
||||
public:
|
||||
std::vector<Vector2> cutTemplate;
|
||||
|
||||
std::vector<Vector3> vertices;
|
||||
|
@ -156,25 +120,22 @@ private:
|
|||
bool m_cacheEnabled = false;
|
||||
float m_smoothShadingThresholdAngleDegrees = 60;
|
||||
uint64_t m_id = 0;
|
||||
std::vector<Vector3> m_clothCollisionVertices;
|
||||
std::vector<std::vector<size_t>> m_clothCollisionTriangles;
|
||||
bool m_weldEnabled = true;
|
||||
bool m_interpolationEnabled = true;
|
||||
|
||||
void collectParts();
|
||||
void collectIncombinableComponentMeshes(const std::string &componentIdString);
|
||||
void collectIncombinableMesh(const MeshCombiner::Mesh *mesh, const GeneratedComponent &componentCache);
|
||||
bool checkIsComponentDirty(const std::string &componentIdString);
|
||||
bool checkIsPartDirty(const std::string &partIdString);
|
||||
bool checkIsPartDependencyDirty(const std::string &partIdString);
|
||||
void checkDirtyFlags();
|
||||
MeshCombiner::Mesh *combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes=true);
|
||||
MeshCombiner::Mesh *combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode);
|
||||
std::unique_ptr<MeshCombiner::Mesh> combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes=true);
|
||||
std::unique_ptr<MeshCombiner::Mesh> combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode);
|
||||
void makeXmirror(const std::vector<Vector3> &sourceVertices, const std::vector<std::vector<size_t>> &sourceFaces,
|
||||
std::vector<Vector3> *destVertices, std::vector<std::vector<size_t>> *destFaces);
|
||||
void collectSharedQuadEdges(const std::vector<Vector3> &vertices, const std::vector<std::vector<size_t>> &faces,
|
||||
std::set<std::pair<PositionKey, PositionKey>> *sharedQuadEdges);
|
||||
MeshCombiner::Mesh *combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second,
|
||||
std::unique_ptr<MeshCombiner::Mesh> combineTwoMeshes(const MeshCombiner::Mesh &first, const MeshCombiner::Mesh &second,
|
||||
MeshCombiner::Method method,
|
||||
bool recombine=true);
|
||||
void generateSmoothTriangleVertexNormals(const std::vector<Vector3> &vertices, const std::vector<std::vector<size_t>> &triangles,
|
||||
|
@ -182,9 +143,9 @@ private:
|
|||
std::vector<std::vector<Vector3>> *triangleVertexNormals);
|
||||
const std::map<std::string, std::string> *findComponent(const std::string &componentIdString);
|
||||
CombineMode componentCombineMode(const std::map<std::string, std::string> *component);
|
||||
MeshCombiner::Mesh *combineComponentChildGroupMesh(const std::vector<std::string> &componentIdStrings,
|
||||
std::unique_ptr<MeshCombiner::Mesh> combineComponentChildGroupMesh(const std::vector<std::string> &componentIdStrings,
|
||||
GeneratedComponent &componentCache);
|
||||
MeshCombiner::Mesh *combineMultipleMeshes(const std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, std::string>> &multipleMeshes, bool recombine=true);
|
||||
std::unique_ptr<MeshCombiner::Mesh> combineMultipleMeshes(std::vector<std::tuple<std::unique_ptr<MeshCombiner::Mesh>, CombineMode, std::string>> &&multipleMeshes, bool recombine=true);
|
||||
std::string componentColorName(const std::map<std::string, std::string> *component);
|
||||
void collectUncombinedComponent(const std::string &componentIdString);
|
||||
void cutFaceStringToCutTemplate(const std::string &cutFaceString, std::vector<Vector2> &cutTemplate);
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2022 Jeremy HU <jeremy-at-dust3d dot org>. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <dust3d/base/debug.h>
|
||||
#include <dust3d/mesh/stitch_mesh_builder.h>
|
||||
|
||||
namespace dust3d
|
||||
{
|
||||
|
||||
StitchMeshBuilder::StitchMeshBuilder(std::vector<Spline> &&splines)
|
||||
{
|
||||
m_splines = std::move(splines);
|
||||
}
|
||||
|
||||
const std::vector<Vector3> &StitchMeshBuilder::generatedVertices()
|
||||
{
|
||||
return m_generatedVertices;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
std::vector<std::vector<StitchingPoint>> stitchingPoints(splines.size());
|
||||
|
||||
size_t targetSegments = 5; // FIXME: decide target segments by ...
|
||||
for (size_t i = 0; i < splines.size(); ++i) {
|
||||
const auto &spline = splines[i];
|
||||
std::vector<Vector3> polyline(spline.nodes.size());
|
||||
for (size_t j = 0; j < spline.nodes.size(); ++j)
|
||||
polyline[j] = spline.nodes[j].origin;
|
||||
auto segmentPoints = splitPolylineToSegments(polyline, targetSegments);
|
||||
stitchingPoints[i].resize(segmentPoints.size());
|
||||
for (size_t k = 0; k < segmentPoints.size(); ++k) {
|
||||
stitchingPoints[i][k].originVertex = m_generatedVertices.size();
|
||||
// TODO: stitchingPoints[i][k].radius = ...
|
||||
m_generatedVertices.emplace_back(segmentPoints[k]);
|
||||
}
|
||||
}
|
||||
|
||||
return stitchingPoints;
|
||||
}
|
||||
|
||||
double StitchMeshBuilder::segmentsLength(const std::vector<Vector3> &segmentPoints)
|
||||
{
|
||||
double totalLength = 0.0;
|
||||
for (size_t j = 1; j < segmentPoints.size(); ++j) {
|
||||
size_t i = j - 1;
|
||||
totalLength += (segmentPoints[i] - segmentPoints[j]).length();
|
||||
}
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
std::vector<Vector3> StitchMeshBuilder::splitPolylineToSegments(const std::vector<Vector3> &polyline,
|
||||
size_t targetSegments)
|
||||
{
|
||||
std::vector<Vector3> segmentPoints;
|
||||
if (polyline.size() < 2)
|
||||
return segmentPoints;
|
||||
double totalLength = segmentsLength(polyline);
|
||||
if (Math::isZero(totalLength)) {
|
||||
Vector3 middle = (polyline.front() + polyline.back()) * 0.5;
|
||||
for (size_t i = 0; i < targetSegments + 1; ++i) {
|
||||
segmentPoints.push_back(middle);
|
||||
}
|
||||
return segmentPoints;
|
||||
}
|
||||
double segmentLength = totalLength / targetSegments;
|
||||
double wantLength = segmentLength;
|
||||
segmentPoints.push_back(polyline.front());
|
||||
for (size_t j = 1; j < polyline.size(); ++j) {
|
||||
size_t i = j - 1;
|
||||
double lineLength = (polyline[j] - polyline[i]).length();
|
||||
Vector3 polylineStart = polyline[i];
|
||||
while (true) {
|
||||
if (lineLength < wantLength) {
|
||||
wantLength -= lineLength;
|
||||
break;
|
||||
}
|
||||
Vector3 newPosition = (polylineStart * (lineLength - wantLength) + polyline[j] * wantLength) / lineLength;
|
||||
segmentPoints.push_back(newPosition);
|
||||
lineLength -= wantLength;
|
||||
polylineStart = newPosition;
|
||||
wantLength = segmentLength;
|
||||
}
|
||||
}
|
||||
if (segmentPoints.size() < targetSegments + 1) {
|
||||
segmentPoints.push_back(polyline.back());
|
||||
} else {
|
||||
segmentPoints.back() = polyline.back();
|
||||
}
|
||||
return segmentPoints;
|
||||
}
|
||||
|
||||
void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector<std::vector<StitchMeshBuilder::StitchingPoint>> &stitchingPoints)
|
||||
{
|
||||
for (size_t j = 1; j < stitchingPoints.size(); ++j) {
|
||||
size_t i = j - 1;
|
||||
generateMeshFromStitchingPoints(stitchingPoints[i], stitchingPoints[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector<StitchMeshBuilder::StitchingPoint> &a,
|
||||
const std::vector<StitchMeshBuilder::StitchingPoint> &b)
|
||||
{
|
||||
if (a.size() != b.size()) {
|
||||
dust3dDebug << "Unmatch sizes of stitching points, a:" << a.size() << "b:" << b.size();
|
||||
return;
|
||||
}
|
||||
if (a.size() < 2) {
|
||||
dust3dDebug << "Expected at least two stitching points, current:" << a.size();
|
||||
return;
|
||||
}
|
||||
for (size_t j = 1; j < a.size(); ++j) {
|
||||
size_t i = j - 1;
|
||||
m_generatedFaces.emplace_back(std::vector<size_t> {
|
||||
a[i].originVertex,
|
||||
b[i].originVertex,
|
||||
b[j].originVertex,
|
||||
a[j].originVertex
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void StitchMeshBuilder::build()
|
||||
{
|
||||
std::vector<std::vector<StitchingPoint>> stitchingPoints = convertSplinesToStitchingPoints(m_splines);
|
||||
generateMeshFromStitchingPoints(stitchingPoints);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2022 Jeremy HU <jeremy-at-dust3d dot org>. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DUST3D_MESH_STITCH_MESH_BUILDER_H_
|
||||
#define DUST3D_MESH_STITCH_MESH_BUILDER_H_
|
||||
|
||||
#include <dust3d/base/vector3.h>
|
||||
|
||||
namespace dust3d
|
||||
{
|
||||
|
||||
class StitchMeshBuilder
|
||||
{
|
||||
public:
|
||||
struct Node
|
||||
{
|
||||
Vector3 origin;
|
||||
double radius;
|
||||
};
|
||||
|
||||
struct Spline
|
||||
{
|
||||
std::vector<Node> nodes;
|
||||
bool isCircle = false;
|
||||
};
|
||||
|
||||
StitchMeshBuilder(std::vector<Spline> &&splines);
|
||||
void build();
|
||||
const std::vector<Vector3> &generatedVertices();
|
||||
const std::vector<std::vector<size_t>> &generatedFaces();
|
||||
|
||||
private:
|
||||
struct StitchingPoint
|
||||
{
|
||||
size_t originVertex;
|
||||
double radius;
|
||||
};
|
||||
|
||||
std::vector<Spline> m_splines;
|
||||
std::vector<Vector3> m_generatedVertices;
|
||||
std::vector<std::vector<size_t>> m_generatedFaces;
|
||||
|
||||
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<StitchingPoint> &a,
|
||||
const std::vector<StitchingPoint> &b);
|
||||
std::vector<Vector3> splitPolylineToSegments(const std::vector<Vector3> &polyline,
|
||||
size_t targetSegments);
|
||||
double segmentsLength(const std::vector<Vector3> &segmentPoints);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue