Prepare the stitching line feature

master
Jeremy HU 2022-10-01 16:26:59 +10:00
parent 29f7e06701
commit 8b64425341
10 changed files with 335 additions and 276 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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));
}
return combineMultipleMeshes(std::move(multipleMeshes));
}
multipleMeshes.push_back(std::make_tuple(subMesh, childCombineMode, childIdString));
}
return combineMultipleMeshes(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;

View File

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

View File

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

View File

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