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
|
SOURCES += ../dust3d/mesh/resolve_triangle_tangent.cc
|
||||||
HEADERS += ../dust3d/mesh/smooth_normal.h
|
HEADERS += ../dust3d/mesh/smooth_normal.h
|
||||||
SOURCES += ../dust3d/mesh/smooth_normal.cc
|
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
|
HEADERS += ../dust3d/mesh/stroke_mesh_builder.h
|
||||||
SOURCES += ../dust3d/mesh/stroke_mesh_builder.cc
|
SOURCES += ../dust3d/mesh/stroke_mesh_builder.cc
|
||||||
HEADERS += ../dust3d/mesh/stroke_modifier.h
|
HEADERS += ../dust3d/mesh/stroke_modifier.h
|
||||||
|
|
Binary file not shown.
|
@ -48,7 +48,7 @@ void MaterialPreviewsGenerator::generate()
|
||||||
|
|
||||||
std::vector<dust3d::Uuid> partIds;
|
std::vector<dust3d::Uuid> partIds;
|
||||||
dust3d::Ds3FileReader ds3Reader((const std::uint8_t *)fileData.data(), fileData.size());
|
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);
|
dust3d::Ds3ReaderItem item = ds3Reader.items().at(i);
|
||||||
if (item.type == "model") {
|
if (item.type == "model") {
|
||||||
std::vector<std::uint8_t> data;
|
std::vector<std::uint8_t> data;
|
||||||
|
|
|
@ -32,6 +32,7 @@ enum class PartTarget
|
||||||
{
|
{
|
||||||
Model = 0,
|
Model = 0,
|
||||||
CutFace,
|
CutFace,
|
||||||
|
StitchingLine,
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
PartTarget PartTargetFromString(const char *targetString);
|
PartTarget PartTargetFromString(const char *targetString);
|
||||||
|
@ -43,6 +44,8 @@ PartTarget PartTargetFromString(const char *targetString)
|
||||||
return PartTarget::Model; \
|
return PartTarget::Model; \
|
||||||
if (target == "CutFace") \
|
if (target == "CutFace") \
|
||||||
return PartTarget::CutFace; \
|
return PartTarget::CutFace; \
|
||||||
|
if (target == "StitchingLine") \
|
||||||
|
return PartTarget::StitchingLine; \
|
||||||
return PartTarget::Model; \
|
return PartTarget::Model; \
|
||||||
}
|
}
|
||||||
const char *PartTargetToString(PartTarget target);
|
const char *PartTargetToString(PartTarget target);
|
||||||
|
@ -54,6 +57,8 @@ const char *PartTargetToString(PartTarget target)
|
||||||
return "Model"; \
|
return "Model"; \
|
||||||
case PartTarget::CutFace: \
|
case PartTarget::CutFace: \
|
||||||
return "CutFace"; \
|
return "CutFace"; \
|
||||||
|
case PartTarget::StitchingLine: \
|
||||||
|
return "StitchingLine"; \
|
||||||
default: \
|
default: \
|
||||||
return "Model"; \
|
return "Model"; \
|
||||||
} \
|
} \
|
||||||
|
@ -67,6 +72,8 @@ std::string PartTargetToDispName(PartTarget target)
|
||||||
return std::string("Model"); \
|
return std::string("Model"); \
|
||||||
case PartTarget::CutFace: \
|
case PartTarget::CutFace: \
|
||||||
return std::string("Cut Face"); \
|
return std::string("Cut Face"); \
|
||||||
|
case PartTarget::StitchingLine: \
|
||||||
|
return std::string("Stitching Line"); \
|
||||||
default: \
|
default: \
|
||||||
return std::string("Model"); \
|
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)
|
MeshCombiner::Mesh::Mesh(const std::vector<Vector3> &vertices, const std::vector<std::vector<size_t>> &faces)
|
||||||
{
|
{
|
||||||
m_vertices = new std::vector<Vector3>(vertices);
|
m_vertices = std::make_unique<std::vector<Vector3>>(vertices);
|
||||||
m_triangles = new std::vector<std::vector<size_t>>;
|
m_triangles = std::make_unique<std::vector<std::vector<size_t>>>();
|
||||||
triangulate(vertices, faces, m_triangles);
|
triangulate(vertices, faces, m_triangles.get());
|
||||||
m_solidMesh = new SolidMesh;
|
m_solidMesh = std::make_unique<SolidMesh>();
|
||||||
m_solidMesh->setVertices(m_vertices);
|
m_solidMesh->setVertices(m_vertices.get());
|
||||||
m_solidMesh->setTriangles(m_triangles);
|
m_solidMesh->setTriangles(m_triangles.get());
|
||||||
m_solidMesh->prepare();
|
m_solidMesh->prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshCombiner::Mesh::Mesh(const Mesh &other)
|
MeshCombiner::Mesh::Mesh(const Mesh &other)
|
||||||
{
|
{
|
||||||
m_vertices = new std::vector<Vector3>;
|
m_vertices = std::make_unique<std::vector<Vector3>>();
|
||||||
m_triangles = new std::vector<std::vector<size_t>>;
|
m_triangles = std::make_unique<std::vector<std::vector<size_t>>>();
|
||||||
other.fetch(*m_vertices, *m_triangles);
|
other.fetch(*m_vertices, *m_triangles);
|
||||||
m_solidMesh = new SolidMesh;
|
m_solidMesh = std::make_unique<SolidMesh>();
|
||||||
m_solidMesh->setVertices(m_vertices);
|
m_solidMesh->setVertices(m_vertices.get());
|
||||||
m_solidMesh->setTriangles(m_triangles);
|
m_solidMesh->setTriangles(m_triangles.get());
|
||||||
m_solidMesh->prepare();
|
m_solidMesh->prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshCombiner::Mesh::~Mesh()
|
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
|
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();
|
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,
|
MeshCombiner::Mesh *MeshCombiner::combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,
|
||||||
std::vector<std::pair<Source, size_t>> *combinedVerticesComeFrom)
|
std::vector<std::pair<Source, size_t>> *combinedVerticesComeFrom)
|
||||||
{
|
{
|
||||||
if (firstMesh.isNull() || !firstMesh.isCombinable() ||
|
if (firstMesh.isNull() || secondMesh.isNull())
|
||||||
secondMesh.isNull() || !secondMesh.isCombinable())
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
SolidMeshBooleanOperation booleanOperation(firstMesh.m_solidMesh, secondMesh.m_solidMesh);
|
SolidMeshBooleanOperation booleanOperation(firstMesh.m_solidMesh.get(), secondMesh.m_solidMesh.get());
|
||||||
if (!booleanOperation.combine())
|
if (!booleanOperation.combine())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -96,14 +87,14 @@ MeshCombiner::Mesh *MeshCombiner::combine(const Mesh &firstMesh, const Mesh &sec
|
||||||
if (nullptr == vertices)
|
if (nullptr == vertices)
|
||||||
return;
|
return;
|
||||||
for (const auto &point: *vertices) {
|
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}});
|
{source, vertexIndex}});
|
||||||
++vertexIndex;
|
++vertexIndex;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (nullptr != combinedVerticesComeFrom) {
|
if (nullptr != combinedVerticesComeFrom) {
|
||||||
addToSourceMap(firstMesh.m_solidMesh, Source::First);
|
addToSourceMap(firstMesh.m_solidMesh.get(), Source::First);
|
||||||
addToSourceMap(secondMesh.m_solidMesh, Source::Second);
|
addToSourceMap(secondMesh.m_solidMesh.get(), Source::Second);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<size_t>> resultTriangles;
|
std::vector<std::vector<size_t>> resultTriangles;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#define DUST3D_MESH_MESH_COMBINER_H_
|
#define DUST3D_MESH_MESH_COMBINER_H_
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
#include <dust3d/base/vector3.h>
|
#include <dust3d/base/vector3.h>
|
||||||
#include <dust3d/mesh/solid_mesh.h>
|
#include <dust3d/mesh/solid_mesh.h>
|
||||||
|
|
||||||
|
@ -55,14 +56,13 @@ public:
|
||||||
~Mesh();
|
~Mesh();
|
||||||
void fetch(std::vector<Vector3> &vertices, std::vector<std::vector<size_t>> &faces) const;
|
void fetch(std::vector<Vector3> &vertices, std::vector<std::vector<size_t>> &faces) const;
|
||||||
bool isNull() const;
|
bool isNull() const;
|
||||||
bool isCombinable() const;
|
|
||||||
|
|
||||||
friend MeshCombiner;
|
friend MeshCombiner;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SolidMesh *m_solidMesh = nullptr;
|
std::unique_ptr<SolidMesh> m_solidMesh;
|
||||||
std::vector<Vector3> *m_vertices = nullptr;
|
std::unique_ptr<std::vector<Vector3>> m_vertices;
|
||||||
std::vector<std::vector<size_t>> *m_triangles = nullptr;
|
std::unique_ptr<std::vector<std::vector<size_t>>> m_triangles;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,
|
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);
|
auto findPart = m_snapshot->parts.find(partIdString);
|
||||||
if (findPart == m_snapshot->parts.end()) {
|
if (findPart == m_snapshot->parts.end()) {
|
||||||
|
@ -473,7 +473,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
||||||
partCache.previewVertices.clear();
|
partCache.previewVertices.clear();
|
||||||
partCache.isSuccessful = false;
|
partCache.isSuccessful = false;
|
||||||
partCache.joined = (target == PartTarget::Model && !isDisabled);
|
partCache.joined = (target == PartTarget::Model && !isDisabled);
|
||||||
partCache.releaseMeshes();
|
|
||||||
|
|
||||||
struct NodeInfo
|
struct NodeInfo
|
||||||
{
|
{
|
||||||
|
@ -545,7 +544,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
||||||
bool buildSucceed = false;
|
bool buildSucceed = false;
|
||||||
std::map<std::string, int> nodeIdStringToIndexMap;
|
std::map<std::string, int> nodeIdStringToIndexMap;
|
||||||
std::map<int, std::string> nodeIndexToIdStringMap;
|
std::map<int, std::string> nodeIndexToIdStringMap;
|
||||||
StrokeModifier *strokeModifier = nullptr;
|
|
||||||
|
|
||||||
auto addNodeToPartCache = [&](const std::string &nodeIdString, const NodeInfo &nodeInfo) {
|
auto addNodeToPartCache = [&](const std::string &nodeIdString, const NodeInfo &nodeInfo) {
|
||||||
ObjectNode objectNode;
|
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)
|
if (smooth)
|
||||||
strokeModifier->enableSmooth();
|
strokeModifier->enableSmooth();
|
||||||
|
@ -626,7 +624,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
||||||
|
|
||||||
std::vector<size_t> sourceNodeIndices;
|
std::vector<size_t> sourceNodeIndices;
|
||||||
|
|
||||||
StrokeMeshBuilder *strokeMeshBuilder = new StrokeMeshBuilder;
|
auto strokeMeshBuilder = std::make_unique<StrokeMeshBuilder>();
|
||||||
|
|
||||||
strokeMeshBuilder->setDeformThickness(deformThickness);
|
strokeMeshBuilder->setDeformThickness(deformThickness);
|
||||||
strokeMeshBuilder->setDeformWidth(deformWidth);
|
strokeMeshBuilder->setDeformWidth(deformWidth);
|
||||||
|
@ -680,14 +678,11 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
||||||
partCache.objectNodeVertices.push_back({position, {partIdString, nodeIdString}});
|
partCache.objectNodeVertices.push_back({position, {partIdString, nodeIdString}});
|
||||||
}
|
}
|
||||||
|
|
||||||
delete strokeMeshBuilder;
|
|
||||||
strokeMeshBuilder = nullptr;
|
|
||||||
|
|
||||||
bool hasMeshError = false;
|
bool hasMeshError = false;
|
||||||
MeshCombiner::Mesh *mesh = nullptr;
|
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||||
|
|
||||||
if (buildSucceed) {
|
if (buildSucceed) {
|
||||||
mesh = new MeshCombiner::Mesh(partCache.vertices, partCache.faces);
|
mesh = std::make_unique<MeshCombiner::Mesh>(partCache.vertices, partCache.faces);
|
||||||
if (mesh->isNull()) {
|
if (mesh->isNull()) {
|
||||||
hasMeshError = true;
|
hasMeshError = true;
|
||||||
}
|
}
|
||||||
|
@ -698,7 +693,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
||||||
std::vector<Vector3> partPreviewVertices;
|
std::vector<Vector3> partPreviewVertices;
|
||||||
Color partPreviewColor = partColor;
|
Color partPreviewColor = partColor;
|
||||||
if (nullptr != mesh) {
|
if (nullptr != mesh) {
|
||||||
partCache.mesh = new MeshCombiner::Mesh(*mesh);
|
|
||||||
mesh->fetch(partPreviewVertices, partCache.previewTriangles);
|
mesh->fetch(partPreviewVertices, partCache.previewTriangles);
|
||||||
partCache.previewVertices = partPreviewVertices;
|
partCache.previewVertices = partPreviewVertices;
|
||||||
partCache.isSuccessful = true;
|
partCache.isSuccessful = true;
|
||||||
|
@ -747,21 +741,16 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const std::string &partIdStri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete strokeModifier;
|
|
||||||
|
|
||||||
if (mesh && mesh->isNull()) {
|
if (mesh && mesh->isNull()) {
|
||||||
delete mesh;
|
mesh.reset();
|
||||||
mesh = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
delete mesh;
|
mesh.reset();
|
||||||
mesh = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target != PartTarget::Model) {
|
if (target != PartTarget::Model) {
|
||||||
delete mesh;
|
mesh.reset();
|
||||||
mesh = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMeshError && target == PartTarget::Model) {
|
if (hasMeshError && target == PartTarget::Model) {
|
||||||
|
@ -820,9 +809,9 @@ std::string MeshGenerator::componentColorName(const std::map<std::string, std::s
|
||||||
return std::string();
|
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;
|
Uuid componentId;
|
||||||
const std::map<std::string, std::string> *component = &m_snapshot->rootComponent;
|
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_cacheEnabled) {
|
||||||
if (m_dirtyComponentIds.find(componentIdString) == m_dirtyComponentIds.end()) {
|
if (m_dirtyComponentIds.find(componentIdString) == m_dirtyComponentIds.end()) {
|
||||||
if (nullptr != componentCache.mesh)
|
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.objectNodes.clear();
|
||||||
componentCache.objectEdges.clear();
|
componentCache.objectEdges.clear();
|
||||||
componentCache.objectNodeVertices.clear();
|
componentCache.objectNodeVertices.clear();
|
||||||
componentCache.releaseMeshes();
|
componentCache.mesh.reset();
|
||||||
|
|
||||||
std::string linkDataType = String::valueOrEmpty(*component, "linkDataType");
|
std::string linkDataType = String::valueOrEmpty(*component, "linkDataType");
|
||||||
if ("partId" == linkDataType) {
|
if ("partId" == linkDataType) {
|
||||||
|
@ -860,8 +849,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
||||||
bool retryable = true;
|
bool retryable = true;
|
||||||
mesh = combinePartMesh(partIdString, &hasError, &retryable, m_interpolationEnabled);
|
mesh = combinePartMesh(partIdString, &hasError, &retryable, m_interpolationEnabled);
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
delete mesh;
|
mesh.reset();
|
||||||
mesh = nullptr;
|
|
||||||
if (retryable && m_interpolationEnabled) {
|
if (retryable && m_interpolationEnabled) {
|
||||||
hasError = false;
|
hasError = false;
|
||||||
mesh = combinePartMesh(partIdString, &hasError, &retryable, false);
|
mesh = combinePartMesh(partIdString, &hasError, &retryable, false);
|
||||||
|
@ -870,7 +858,6 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
||||||
m_isSuccessful = false;
|
m_isSuccessful = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &partCache = m_cacheContext->parts[partIdString];
|
const auto &partCache = m_cacheContext->parts[partIdString];
|
||||||
for (const auto &vertex: partCache.vertices)
|
for (const auto &vertex: partCache.vertices)
|
||||||
componentCache.noneSeamVertices.insert(vertex);
|
componentCache.noneSeamVertices.insert(vertex);
|
||||||
|
@ -882,19 +869,13 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
||||||
for (const auto &it: partCache.objectNodeVertices)
|
for (const auto &it: partCache.objectNodeVertices)
|
||||||
componentCache.objectNodeVertices.push_back(it);
|
componentCache.objectNodeVertices.push_back(it);
|
||||||
} else {
|
} else {
|
||||||
std::vector<std::pair<CombineMode, std::vector<std::pair<std::string, std::string>>>> combineGroups;
|
std::vector<std::pair<CombineMode, std::vector<std::string>>> combineGroups;
|
||||||
// Firstly, group by combine mode
|
|
||||||
int currentGroupIndex = -1;
|
int currentGroupIndex = -1;
|
||||||
auto lastCombineMode = CombineMode::Count;
|
auto lastCombineMode = CombineMode::Count;
|
||||||
bool foundColorSolubilitySetting = false;
|
|
||||||
for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) {
|
for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) {
|
||||||
if (childIdString.empty())
|
if (childIdString.empty())
|
||||||
continue;
|
continue;
|
||||||
const auto &child = findComponent(childIdString);
|
const auto &child = findComponent(childIdString);
|
||||||
std::string colorName = componentColorName(child);
|
|
||||||
if (colorName == "+") {
|
|
||||||
foundColorSolubilitySetting = true;
|
|
||||||
}
|
|
||||||
auto combineMode = componentCombineMode(child);
|
auto combineMode = componentCombineMode(child);
|
||||||
if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) {
|
if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) {
|
||||||
combineGroups.push_back({combineMode, {}});
|
combineGroups.push_back({combineMode, {}});
|
||||||
|
@ -904,113 +885,44 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const std::string &compo
|
||||||
if (-1 == currentGroupIndex) {
|
if (-1 == currentGroupIndex) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
combineGroups[currentGroupIndex].second.push_back({childIdString, colorName});
|
combineGroups[currentGroupIndex].second.push_back(childIdString);
|
||||||
}
|
}
|
||||||
// Secondly, sub group by color
|
std::vector<std::tuple<std::unique_ptr<MeshCombiner::Mesh>, CombineMode, std::string>> groupMeshes;
|
||||||
std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, std::string>> groupMeshes;
|
|
||||||
for (const auto &group: combineGroups) {
|
for (const auto &group: combineGroups) {
|
||||||
std::set<size_t> used;
|
auto childMesh = combineComponentChildGroupMesh(group.second, componentCache);
|
||||||
std::vector<std::vector<std::string>> componentIdStrings;
|
if (nullptr == childMesh || childMesh->isNull())
|
||||||
int currentSubGroupIndex = -1;
|
|
||||||
auto lastColorName = std::string();
|
|
||||||
for (size_t i = 0; i < group.second.size(); ++i) {
|
|
||||||
if (used.find(i) != used.end())
|
|
||||||
continue;
|
continue;
|
||||||
//const auto &colorName = group.second[i].second;
|
groupMeshes.emplace_back(std::make_tuple(std::move(childMesh), group.first, String::join(group.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;
|
|
||||||
}
|
}
|
||||||
if (-1 == currentSubGroupIndex) {
|
mesh = combineMultipleMeshes(std::move(groupMeshes), true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nullptr != mesh)
|
if (nullptr != mesh)
|
||||||
componentCache.mesh = new MeshCombiner::Mesh(*mesh);
|
componentCache.mesh = std::make_unique<MeshCombiner::Mesh>(*mesh);
|
||||||
|
|
||||||
if (nullptr != mesh && mesh->isNull()) {
|
if (nullptr != mesh && mesh->isNull()) {
|
||||||
delete mesh;
|
mesh.reset();
|
||||||
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
|
|
||||||
// ... ...
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mesh;
|
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;
|
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);
|
const auto &childCombineMode = std::get<1>(it);
|
||||||
MeshCombiner::Mesh *subMesh = std::get<0>(it);
|
|
||||||
const std::string &subMeshIdString = std::get<2>(it);
|
const std::string &subMeshIdString = std::get<2>(it);
|
||||||
if (nullptr == subMesh || subMesh->isNull()) {
|
if (nullptr == subMesh || subMesh->isNull()) {
|
||||||
delete subMesh;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!subMesh->isCombinable()) {
|
|
||||||
// TODO: Collect vertices
|
|
||||||
delete subMesh;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (nullptr == mesh) {
|
if (nullptr == mesh) {
|
||||||
mesh = subMesh;
|
mesh = std::move(subMesh);
|
||||||
meshIdStrings = subMeshIdString;
|
meshIdStrings = subMeshIdString;
|
||||||
} else {
|
continue;
|
||||||
|
}
|
||||||
auto combinerMethod = childCombineMode == CombineMode::Inversion ?
|
auto combinerMethod = childCombineMode == CombineMode::Inversion ?
|
||||||
MeshCombiner::Method::Diff : MeshCombiner::Method::Union;
|
MeshCombiner::Method::Diff : MeshCombiner::Method::Union;
|
||||||
auto combinerMethodString = combinerMethod == MeshCombiner::Method::Union ?
|
auto combinerMethodString = combinerMethod == MeshCombiner::Method::Union ?
|
||||||
|
@ -1018,48 +930,42 @@ MeshCombiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector<std::
|
||||||
meshIdStrings += combinerMethodString + subMeshIdString;
|
meshIdStrings += combinerMethodString + subMeshIdString;
|
||||||
if (recombine)
|
if (recombine)
|
||||||
meshIdStrings += "!";
|
meshIdStrings += "!";
|
||||||
MeshCombiner::Mesh *newMesh = nullptr;
|
std::unique_ptr<MeshCombiner::Mesh> newMesh;
|
||||||
auto findCached = m_cacheContext->cachedCombination.find(meshIdStrings);
|
auto findCached = m_cacheContext->cachedCombination.find(meshIdStrings);
|
||||||
if (findCached != m_cacheContext->cachedCombination.end()) {
|
if (findCached != m_cacheContext->cachedCombination.end()) {
|
||||||
if (nullptr != findCached->second) {
|
if (nullptr != findCached->second) {
|
||||||
newMesh = new MeshCombiner::Mesh(*findCached->second);
|
newMesh = std::make_unique<MeshCombiner::Mesh>(*findCached->second);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newMesh = combineTwoMeshes(*mesh,
|
newMesh = combineTwoMeshes(*mesh,
|
||||||
*subMesh,
|
*subMesh,
|
||||||
combinerMethod,
|
combinerMethod,
|
||||||
recombine);
|
recombine);
|
||||||
delete subMesh;
|
|
||||||
if (nullptr != newMesh)
|
if (nullptr != newMesh)
|
||||||
m_cacheContext->cachedCombination.insert({meshIdStrings, new MeshCombiner::Mesh(*newMesh)});
|
m_cacheContext->cachedCombination.insert({meshIdStrings, std::make_unique<MeshCombiner::Mesh>(*newMesh)});
|
||||||
else
|
else
|
||||||
m_cacheContext->cachedCombination.insert({meshIdStrings, nullptr});
|
m_cacheContext->cachedCombination.insert({meshIdStrings, nullptr});
|
||||||
}
|
}
|
||||||
if (newMesh && !newMesh->isNull()) {
|
if (newMesh && !newMesh->isNull()) {
|
||||||
delete mesh;
|
mesh = std::move(newMesh);
|
||||||
mesh = newMesh;
|
|
||||||
} else {
|
} else {
|
||||||
m_isSuccessful = false;
|
m_isSuccessful = false;
|
||||||
delete newMesh;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nullptr != mesh && mesh->isNull()) {
|
if (nullptr != mesh && mesh->isNull()) {
|
||||||
delete mesh;
|
mesh.reset();
|
||||||
mesh = nullptr;
|
|
||||||
}
|
}
|
||||||
return mesh;
|
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) {
|
for (const auto &childIdString: componentIdStrings) {
|
||||||
CombineMode childCombineMode = CombineMode::Normal;
|
CombineMode childCombineMode = CombineMode::Normal;
|
||||||
MeshCombiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode);
|
std::unique_ptr<MeshCombiner::Mesh> subMesh = combineComponentMesh(childIdString, &childCombineMode);
|
||||||
|
|
||||||
if (CombineMode::Uncombined == childCombineMode) {
|
if (CombineMode::Uncombined == childCombineMode) {
|
||||||
delete subMesh;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1076,31 +982,25 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const std::vec
|
||||||
componentCache.objectNodeVertices.push_back(it);
|
componentCache.objectNodeVertices.push_back(it);
|
||||||
|
|
||||||
if (nullptr == subMesh || subMesh->isNull()) {
|
if (nullptr == subMesh || subMesh->isNull()) {
|
||||||
delete subMesh;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subMesh->isCombinable()) {
|
multipleMeshes.emplace_back(std::make_tuple(std::move(subMesh), childCombineMode, childIdString));
|
||||||
componentCache.incombinableMeshes.push_back(subMesh);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
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,
|
MeshCombiner::Method method,
|
||||||
bool recombine)
|
bool recombine)
|
||||||
{
|
{
|
||||||
if (first.isNull() || second.isNull())
|
if (first.isNull() || second.isNull())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
std::vector<std::pair<MeshCombiner::Source, size_t>> combinedVerticesSources;
|
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,
|
second,
|
||||||
method,
|
method,
|
||||||
&combinedVerticesSources);
|
&combinedVerticesSources));
|
||||||
if (nullptr == newMesh)
|
if (nullptr == newMesh)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (!newMesh->isNull() && recombine) {
|
if (!newMesh->isNull() && recombine) {
|
||||||
|
@ -1112,18 +1012,14 @@ MeshCombiner::Mesh *MeshGenerator::combineTwoMeshes(const MeshCombiner::Mesh &fi
|
||||||
recombiner.setFaces(&combinedFaces);
|
recombiner.setFaces(&combinedFaces);
|
||||||
if (recombiner.recombine()) {
|
if (recombiner.recombine()) {
|
||||||
if (isWatertight(recombiner.regeneratedFaces())) {
|
if (isWatertight(recombiner.regeneratedFaces())) {
|
||||||
MeshCombiner::Mesh *reMesh = new MeshCombiner::Mesh(recombiner.regeneratedVertices(), recombiner.regeneratedFaces());
|
auto reMesh = std::make_unique<MeshCombiner::Mesh>(recombiner.regeneratedVertices(), recombiner.regeneratedFaces());
|
||||||
if (!reMesh->isNull() && reMesh->isCombinable()) {
|
if (!reMesh->isNull()) {
|
||||||
delete newMesh;
|
newMesh = std::move(reMesh);
|
||||||
newMesh = reMesh;
|
|
||||||
} else {
|
|
||||||
delete reMesh;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newMesh->isNull()) {
|
if (newMesh->isNull()) {
|
||||||
delete newMesh;
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return newMesh;
|
return newMesh;
|
||||||
|
@ -1245,23 +1141,6 @@ void MeshGenerator::postprocessObject(Object *object)
|
||||||
object->setTriangleVertexNormals(triangleVertexNormals);
|
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)
|
void MeshGenerator::collectIncombinableMesh(const MeshCombiner::Mesh *mesh, const GeneratedComponent &componentCache)
|
||||||
{
|
{
|
||||||
if (nullptr == mesh)
|
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_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());
|
m_nodeVertices.insert(m_nodeVertices.end(), componentCache.objectNodeVertices.begin(), componentCache.objectNodeVertices.end());
|
||||||
|
|
||||||
collectIncombinableMesh(componentCache.mesh, componentCache);
|
collectIncombinableMesh(componentCache.mesh.get(), componentCache);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) {
|
for (const auto &childIdString: String::split(String::valueOrEmpty(*component, "children"), ',')) {
|
||||||
|
@ -1459,7 +1338,6 @@ void MeshGenerator::generate()
|
||||||
}
|
}
|
||||||
m_cacheContext->partMirrorIdMap.erase(mirrorFrom);
|
m_cacheContext->partMirrorIdMap.erase(mirrorFrom);
|
||||||
}
|
}
|
||||||
it->second.releaseMeshes();
|
|
||||||
it = m_cacheContext->parts.erase(it);
|
it = m_cacheContext->parts.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1469,13 +1347,11 @@ void MeshGenerator::generate()
|
||||||
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
||||||
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
||||||
if (std::string::npos != combinationIt->first.find(it->first)) {
|
if (std::string::npos != combinationIt->first.find(it->first)) {
|
||||||
delete combinationIt->second;
|
|
||||||
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
combinationIt++;
|
combinationIt++;
|
||||||
}
|
}
|
||||||
it->second.releaseMeshes();
|
|
||||||
it = m_cacheContext->components.erase(it);
|
it = m_cacheContext->components.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1489,7 +1365,6 @@ void MeshGenerator::generate()
|
||||||
for (const auto &dirtyComponentId: m_dirtyComponentIds) {
|
for (const auto &dirtyComponentId: m_dirtyComponentIds) {
|
||||||
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
||||||
if (std::string::npos != combinationIt->first.find(dirtyComponentId)) {
|
if (std::string::npos != combinationIt->first.find(dirtyComponentId)) {
|
||||||
delete combinationIt->second;
|
|
||||||
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1533,13 +1408,10 @@ void MeshGenerator::generate()
|
||||||
|
|
||||||
// Recursively check uncombined components
|
// Recursively check uncombined components
|
||||||
collectUncombinedComponent(to_string(Uuid()));
|
collectUncombinedComponent(to_string(Uuid()));
|
||||||
collectIncombinableComponentMeshes(to_string(Uuid()));
|
|
||||||
|
|
||||||
collectErroredParts();
|
collectErroredParts();
|
||||||
postprocessObject(m_object);
|
postprocessObject(m_object);
|
||||||
|
|
||||||
delete combinedMesh;
|
|
||||||
|
|
||||||
if (needDeleteCacheContext) {
|
if (needDeleteCacheContext) {
|
||||||
delete m_cacheContext;
|
delete m_cacheContext;
|
||||||
m_cacheContext = nullptr;
|
m_cacheContext = nullptr;
|
||||||
|
|
|
@ -40,19 +40,8 @@ class MeshGenerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
class GeneratedPart
|
struct GeneratedPart
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
~GeneratedPart()
|
|
||||||
{
|
|
||||||
releaseMeshes();
|
|
||||||
};
|
|
||||||
void releaseMeshes()
|
|
||||||
{
|
|
||||||
delete mesh;
|
|
||||||
mesh = nullptr;
|
|
||||||
}
|
|
||||||
MeshCombiner::Mesh *mesh = nullptr;
|
|
||||||
std::vector<Vector3> vertices;
|
std::vector<Vector3> vertices;
|
||||||
std::vector<std::vector<size_t>> faces;
|
std::vector<std::vector<size_t>> faces;
|
||||||
std::vector<ObjectNode> objectNodes;
|
std::vector<ObjectNode> objectNodes;
|
||||||
|
@ -64,23 +53,9 @@ public:
|
||||||
bool joined = true;
|
bool joined = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GeneratedComponent
|
struct GeneratedComponent
|
||||||
{
|
{
|
||||||
public:
|
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||||
~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::set<std::pair<PositionKey, PositionKey>> sharedQuadEdges;
|
std::set<std::pair<PositionKey, PositionKey>> sharedQuadEdges;
|
||||||
std::set<PositionKey> noneSeamVertices;
|
std::set<PositionKey> noneSeamVertices;
|
||||||
std::vector<ObjectNode> objectNodes;
|
std::vector<ObjectNode> objectNodes;
|
||||||
|
@ -88,27 +63,16 @@ public:
|
||||||
std::vector<std::pair<Vector3, std::pair<Uuid, Uuid>>> objectNodeVertices;
|
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, GeneratedComponent> components;
|
||||||
std::map<std::string, GeneratedPart> parts;
|
std::map<std::string, GeneratedPart> parts;
|
||||||
std::map<std::string, std::string> partMirrorIdMap;
|
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<Vector2> cutTemplate;
|
||||||
|
|
||||||
std::vector<Vector3> vertices;
|
std::vector<Vector3> vertices;
|
||||||
|
@ -156,25 +120,22 @@ private:
|
||||||
bool m_cacheEnabled = false;
|
bool m_cacheEnabled = false;
|
||||||
float m_smoothShadingThresholdAngleDegrees = 60;
|
float m_smoothShadingThresholdAngleDegrees = 60;
|
||||||
uint64_t m_id = 0;
|
uint64_t m_id = 0;
|
||||||
std::vector<Vector3> m_clothCollisionVertices;
|
|
||||||
std::vector<std::vector<size_t>> m_clothCollisionTriangles;
|
|
||||||
bool m_weldEnabled = true;
|
bool m_weldEnabled = true;
|
||||||
bool m_interpolationEnabled = true;
|
bool m_interpolationEnabled = true;
|
||||||
|
|
||||||
void collectParts();
|
void collectParts();
|
||||||
void collectIncombinableComponentMeshes(const std::string &componentIdString);
|
|
||||||
void collectIncombinableMesh(const MeshCombiner::Mesh *mesh, const GeneratedComponent &componentCache);
|
void collectIncombinableMesh(const MeshCombiner::Mesh *mesh, const GeneratedComponent &componentCache);
|
||||||
bool checkIsComponentDirty(const std::string &componentIdString);
|
bool checkIsComponentDirty(const std::string &componentIdString);
|
||||||
bool checkIsPartDirty(const std::string &partIdString);
|
bool checkIsPartDirty(const std::string &partIdString);
|
||||||
bool checkIsPartDependencyDirty(const std::string &partIdString);
|
bool checkIsPartDependencyDirty(const std::string &partIdString);
|
||||||
void checkDirtyFlags();
|
void checkDirtyFlags();
|
||||||
MeshCombiner::Mesh *combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes=true);
|
std::unique_ptr<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> combineComponentMesh(const std::string &componentIdString, CombineMode *combineMode);
|
||||||
void makeXmirror(const std::vector<Vector3> &sourceVertices, const std::vector<std::vector<size_t>> &sourceFaces,
|
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);
|
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,
|
void collectSharedQuadEdges(const std::vector<Vector3> &vertices, const std::vector<std::vector<size_t>> &faces,
|
||||||
std::set<std::pair<PositionKey, PositionKey>> *sharedQuadEdges);
|
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,
|
MeshCombiner::Method method,
|
||||||
bool recombine=true);
|
bool recombine=true);
|
||||||
void generateSmoothTriangleVertexNormals(const std::vector<Vector3> &vertices, const std::vector<std::vector<size_t>> &triangles,
|
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);
|
std::vector<std::vector<Vector3>> *triangleVertexNormals);
|
||||||
const std::map<std::string, std::string> *findComponent(const std::string &componentIdString);
|
const std::map<std::string, std::string> *findComponent(const std::string &componentIdString);
|
||||||
CombineMode componentCombineMode(const std::map<std::string, std::string> *component);
|
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);
|
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);
|
std::string componentColorName(const std::map<std::string, std::string> *component);
|
||||||
void collectUncombinedComponent(const std::string &componentIdString);
|
void collectUncombinedComponent(const std::string &componentIdString);
|
||||||
void cutFaceStringToCutTemplate(const std::string &cutFaceString, std::vector<Vector2> &cutTemplate);
|
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