diff --git a/.travis.yml b/.travis.yml index 89c5a7f2..04dff73b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,28 +54,13 @@ install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install qt59base qt59tools --force-yes; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then source /opt/qt59/bin/qt59-env.sh; fi - # Install Rust - - wget -O installrust.sh https://sh.rustup.rs - - sh installrust.sh -y - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH="$HOME/.cargo/bin:/usr/local/opt/qt/bin:$(brew --prefix)/bin:$PATH"; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export PATH="~/.cargo/bin:$PATH"; fi - script: - # Build Meshlite - - git clone https://github.com/huxingyi/meshlite.git || travis_terminate 1 - - cd meshlite/ffi - - cargo build --release - - cd ../../ - - cp meshlite/ffi/include/meshlite.h thirdparty/meshlite/meshlite.h || travis_terminate 1 - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cp meshlite/ffi/target/release/libmeshlite_ffi.dylib thirdparty/meshlite/libmeshlite_ffi.dylib || travis_terminate 1; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp meshlite/ffi/target/release/libmeshlite_ffi.so thirdparty/meshlite/libmeshlite_ffi.so || travis_terminate 1; fi # Build Dust3D - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then qmake -config release || travis_terminate 1; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then qmake -config release || travis_terminate 1; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make || travis_terminate 1; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make CXX="g++ -fext-numeric-literals" || travis_terminate 1; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/thirdparty/meshlite || travis_terminate 1; fi # Prepare tag name - export TAG="$TRAVIS_TAG" @@ -109,7 +94,7 @@ script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp ./dust3d appdir/usr/bin/dust3d || travis_terminate 1; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp ./ci/dust3d.desktop appdir/usr/share/applications/dust3d.desktop || travis_terminate 1; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then chmod a+x ./ci/linuxdeployqt.AppImage || travis_terminate 1; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then unset QTDIR; unset QT_PLUGIN_PATH ; export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/thirdparty/meshlite; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then unset QTDIR; unset QT_PLUGIN_PATH ; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./ci/linuxdeployqt.AppImage appdir/usr/share/applications/dust3d.desktop -bundle-non-qt-libs -verbose=2 || travis_terminate 1; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then rm appdir/AppRun || travis_terminate 1; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp ./ci/AppRun-patched-x86_64 appdir/AppRun || travis_terminate 1; fi diff --git a/ACKNOWLEDGEMENTS.html b/ACKNOWLEDGEMENTS.html index 0ed40bfb..a68eb600 100644 --- a/ACKNOWLEDGEMENTS.html +++ b/ACKNOWLEDGEMENTS.html @@ -1138,3 +1138,8 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode
     https://en.wikipedia.org/wiki/Eadweard_Muybridge
 
+ +

nodemesh

+
+    https://github.com/huxingyi/nodemesh
+
diff --git a/appveyor.yml b/appveyor.yml index 8cc6ca63..4dff0565 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -49,7 +49,6 @@ after_test: - if "%TAG%" == "" set TAG=unstable - 7z a dust3d-%TAG%-%PLATFORM%.zip %APPVEYOR_BUILD_FOLDER%\release\dust3d.exe - - 7z a dust3d-%TAG%-%PLATFORM%.zip %APPVEYOR_BUILD_FOLDER%\thirdparty\meshlite\meshlite_unstable_vc14_%PLATFORM%\meshlite_ffi.dll - 7z a dust3d-%TAG%-%PLATFORM%.zip %APPVEYOR_BUILD_FOLDER%\CGAL-4.11.1\build\bin\Release\CGAL-vc140-mt-4.11.1.dll - 7z a dust3d-%TAG%-%PLATFORM%.zip %APPVEYOR_BUILD_FOLDER%\CGAL-4.11.1\auxiliary\gmp\lib\libgmp-10.dll - 7z a dust3d-%TAG%-%PLATFORM%.zip %APPVEYOR_BUILD_FOLDER%\CGAL-4.11.1\auxiliary\gmp\lib\libmpfr-4.dll diff --git a/docs/builds.rst b/docs/builds.rst index 28481b5d..ab87d3cf 100644 --- a/docs/builds.rst +++ b/docs/builds.rst @@ -3,11 +3,7 @@ Building Dust3D Overview ========== -The core mesh algorithms of Dust3D written in Rust language, located in meshlite repository, - - https://github.com/huxingyi/meshlite - -The UI of Dust3D built in Qt5, the only thirdparty dependency which should been compiled separately is the CGAL library, however, CGAL will introduce some new dependencies, such as boost and gmp library. +The UI of Dust3D built in Qt5, the only third party dependency which should be compiled separately is the CGAL library, however, CGAL will introduce some new dependencies, such as boost and gmp library. Prerequisites =============== @@ -15,10 +11,6 @@ Prerequisites https://www.cgal.org/ -* Rust - - https://www.rust-lang.org/en-US/install.html - Building ========== @@ -40,13 +32,6 @@ Here is the snapshot of the command line of one build, you may use different def .. code-block:: sh - $ cd /Users/jeremy/Desktop - $ git clone https://github.com/huxingyi/meshlite.git - $ cd meshlite/ffi - $ cargo build --release - $ cp include/meshlite.h /Users/jeremy/Repositories/dust3d/thirdparty/meshlite/meshlite.h - $ cp target/release/libmeshlite_ffi.dylib /Users/jeremy/Repositories/dust3d/thirdparty/meshlite/libmeshlite_ffi.dylib - $ cd /Users/jeremy/Repositories/dust3d $ qmake -spec macx-xcode Open dust3d.xcodeproj in Xcode and build @@ -55,9 +40,6 @@ Here is the snapshot of the command line of one build, you may use different def .. code-block:: sh - ;Install Rust - $ curl https://sh.rustup.rs -sSf | sh ;Add ~/.cargo/bin to PATH after finishing install - ;Install Qt5 $ sudo apt-get install --reinstall qtchooser $ sudo apt-get install qtbase5-dev @@ -80,17 +62,8 @@ Here is the snapshot of the command line of one build, you may use different def $ cd ~/Documents $ git clone https://github.com/huxingyi/dust3d.git - ;Compile the internal dependency - $ cd ~/Desktop - $ git clone https://github.com/huxingyi/meshlite.git - $ cd meshlite/ffi - $ cargo build --release - $ cp ~/Desktop/meshlite/ffi/include/meshlite.h ~/Documents/dust3d/thirdparty/meshlite/meshlite.h - $ cp ~/Desktop/meshlite/ffi/target/release/libmeshlite_ffi.so ~/Documents/dust3d/thirdparty/meshlite/libmeshlite_ffi.so - ;Compile Dust3D $ cd ~/Documents/dust3d $ qmake -qt=5 -makefile $ make - $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/Documents/dust3d/thirdparty/meshlite $ ./dust3d diff --git a/dust3d.pro b/dust3d.pro index 36190fca..061dc948 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -101,9 +101,6 @@ HEADERS += src/theme.h SOURCES += src/meshloader.cpp HEADERS += src/meshloader.h -SOURCES += src/meshutil.cpp -HEADERS += src/meshutil.h - SOURCES += src/texturegenerator.cpp HEADERS += src/texturegenerator.h @@ -113,9 +110,6 @@ HEADERS += src/outcome.h SOURCES += src/meshresultpostprocessor.cpp HEADERS += src/meshresultpostprocessor.h -SOURCES += src/positionmap.cpp -HEADERS += src/positionmap.h - SOURCES += src/logbrowser.cpp HEADERS += src/logbrowser.h @@ -167,9 +161,6 @@ HEADERS += src/rigtype.h SOURCES += src/riggenerator.cpp HEADERS += src/riggenerator.h -SOURCES += src/meshquadify.cpp -HEADERS += src/meshquadify.h - SOURCES += src/skinnedmeshcreator.cpp HEADERS += src/skinnedmeshcreator.h @@ -200,9 +191,6 @@ HEADERS += src/posepreviewsgenerator.h SOURCES += src/posewidget.cpp HEADERS += src/posewidget.h -SOURCES += src/meshweldseam.cpp -HEADERS += src/meshweldseam.h - SOURCES += src/advancesettingwidget.cpp HEADERS += src/advancesettingwidget.h @@ -251,9 +239,6 @@ HEADERS += src/material.h SOURCES += src/fbxfile.cpp HEADERS += src/fbxfile.h -SOURCES += src/anglesmooth.cpp -HEADERS += src/anglesmooth.h - SOURCES += src/motiontimelinewidget.cpp HEADERS += src/motiontimelinewidget.h @@ -302,13 +287,41 @@ HEADERS += src/posedocument.h SOURCES += src/combinemode.cpp HEADERS += src/combinemode.h -SOURCES += src/meshinflate.cpp -HEADERS += src/meshinflate.h - SOURCES += src/main.cpp HEADERS += src/version.h +INCLUDEPATH += thirdparty/nodemesh + +SOURCES += thirdparty/nodemesh/nodemesh/wrapper.cpp +HEADERS += thirdparty/nodemesh/nodemesh/wrapper.h + +SOURCES += thirdparty/nodemesh/nodemesh/stitcher.cpp +HEADERS += thirdparty/nodemesh/nodemesh/stitcher.h + +SOURCES += thirdparty/nodemesh/nodemesh/builder.cpp +HEADERS += thirdparty/nodemesh/nodemesh/builder.h + +SOURCES += thirdparty/nodemesh/nodemesh/combiner.cpp +HEADERS += thirdparty/nodemesh/nodemesh/combiner.h + +SOURCES += thirdparty/nodemesh/nodemesh/util.cpp +HEADERS += thirdparty/nodemesh/nodemesh/util.h + +SOURCES += thirdparty/nodemesh/nodemesh/positionkey.cpp +HEADERS += thirdparty/nodemesh/nodemesh/positionkey.h + +SOURCES += thirdparty/nodemesh/nodemesh/modifier.cpp +HEADERS += thirdparty/nodemesh/nodemesh/modifier.h + +SOURCES += thirdparty/nodemesh/nodemesh/box.cpp +HEADERS += thirdparty/nodemesh/nodemesh/box.h + +SOURCES += thirdparty/nodemesh/nodemesh/recombiner.cpp +HEADERS += thirdparty/nodemesh/nodemesh/recombiner.h + +HEADERS += thirdparty/nodemesh/nodemesh/cgalmesh.h + INCLUDEPATH += thirdparty/crc64 SOURCES += thirdparty/crc64/crc64.c @@ -381,12 +394,6 @@ win32 { error("No CGAL_DIR define found in enviroment variables") } - contains(QMAKE_TARGET.arch, x86_64) { - MESHLITE_DIR = thirdparty/meshlite/meshlite_unstable_vc14_x64 - } else { - MESHLITE_DIR = thirdparty/meshlite/meshlite_unstable_vc14_x86 - } - MESHLITE_LIBNAME = meshlite_ffi.dll GMP_LIBNAME = libgmp-10 MPFR_LIBNAME = libmpfr-4 CGAL_LIBNAME = CGAL-vc140-mt-4.11.1 @@ -400,8 +407,6 @@ win32 { } macx { - MESHLITE_DIR = thirdparty/meshlite - MESHLITE_LIBNAME = meshlite_ffi GMP_LIBNAME = gmp MPFR_LIBNAME = mpfr CGAL_LIBNAME = cgal @@ -416,8 +421,6 @@ macx { } unix:!macx { - MESHLITE_DIR = thirdparty/meshlite - MESHLITE_LIBNAME = meshlite_ffi GMP_LIBNAME = gmp MPFR_LIBNAME = mpfr CGAL_LIBNAME = CGAL @@ -431,9 +434,6 @@ unix:!macx { MPFR_LIBDIR = /usr/local/lib } -INCLUDEPATH += $$MESHLITE_DIR -LIBS += -L$$MESHLITE_DIR -l$$MESHLITE_LIBNAME - INCLUDEPATH += $$BOOST_INCLUDEDIR INCLUDEPATH += $$GMP_INCLUDEDIR diff --git a/src/anglesmooth.cpp b/src/anglesmooth.cpp deleted file mode 100644 index 7c8367c5..00000000 --- a/src/anglesmooth.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include "util.h" -#include "anglesmooth.h" - -void angleSmooth(const std::vector &vertices, - const std::vector> &triangles, - const std::vector &triangleNormals, - float thresholdAngleDegrees, std::vector &triangleVertexNormals) -{ - std::vector>> triangleVertexNormalsMapByIndices(vertices.size()); - std::vector angleAreaWeightedNormals; - for (size_t triangleIndex = 0; triangleIndex < triangles.size(); ++triangleIndex) { - const auto &sourceTriangle = triangles[triangleIndex]; - size_t indices[] = {std::get<0>(sourceTriangle), - std::get<1>(sourceTriangle), - std::get<2>(sourceTriangle)}; - const auto &v1 = vertices[indices[0]]; - const auto &v2 = vertices[indices[1]]; - const auto &v3 = vertices[indices[2]]; - float area = areaOfTriangle(v1, v2, v3); - float angles[] = {radianBetweenVectors(v2-v1, v3-v1), - radianBetweenVectors(v1-v2, v3-v2), - radianBetweenVectors(v1-v3, v2-v3)}; - for (int i = 0; i < 3; ++i) { - triangleVertexNormalsMapByIndices[indices[i]].push_back({triangleIndex, angleAreaWeightedNormals.size()}); - angleAreaWeightedNormals.push_back(triangleNormals[triangleIndex] * area * angles[i]); - } - } - triangleVertexNormals = angleAreaWeightedNormals; - std::map, float> degreesBetweenFacesMap; - for (size_t vertexIndex = 0; vertexIndex < vertices.size(); ++vertexIndex) { - const auto &triangleVertices = triangleVertexNormalsMapByIndices[vertexIndex]; - for (const auto &triangleVertex: triangleVertices) { - for (const auto &otherTriangleVertex: triangleVertices) { - if (triangleVertex.first == otherTriangleVertex.first) - continue; - float degrees = 0; - auto findDegreesResult = degreesBetweenFacesMap.find({triangleVertex.first, otherTriangleVertex.first}); - if (findDegreesResult == degreesBetweenFacesMap.end()) { - degrees = angleBetweenVectors(triangleNormals[triangleVertex.first], triangleNormals[otherTriangleVertex.first]); - degreesBetweenFacesMap.insert({{triangleVertex.first, otherTriangleVertex.first}, degrees}); - degreesBetweenFacesMap.insert({{otherTriangleVertex.first, triangleVertex.first}, degrees}); - } else { - degrees = findDegreesResult->second; - } - if (degrees > thresholdAngleDegrees) { - continue; - } - triangleVertexNormals[triangleVertex.second] += angleAreaWeightedNormals[otherTriangleVertex.second]; - } - } - } - for (auto &item: triangleVertexNormals) - item.normalize(); -} diff --git a/src/anglesmooth.h b/src/anglesmooth.h deleted file mode 100644 index 4f5e78be..00000000 --- a/src/anglesmooth.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef DUST3D_ANGLE_SMOOTH_H -#define DUST3D_ANGLE_SMOOTH_H -#include -#include - -void angleSmooth(const std::vector &vertices, - const std::vector> &triangles, - const std::vector &triangleNormals, - float thresholdAngleDegrees, std::vector &triangleVertexNormals); - -#endif diff --git a/src/animalrigger.cpp b/src/animalrigger.cpp index 86554629..55f5a15b 100644 --- a/src/animalrigger.cpp +++ b/src/animalrigger.cpp @@ -519,7 +519,6 @@ bool AnimalRigger::rig() jointBone.name = namingChain(chainBaseName, chainMark.boneSide, chainGenerateOrder, spineNode.chainMarkIndices.size(), jointGenerateOrder); jointBone.headPosition = jointPositions[jointGenerateOrder - 1]; jointBone.tailPosition = jointPositions[jointGenerateOrder]; - jointBone.baseNormal = jointMark.baseNormal; jointBone.color = boneColor(); if (jointGenerateOrder == (int)jointMarkIndices.size()) { addVerticesToWeights(remainingLimbVertices, jointBone.index); diff --git a/src/document.cpp b/src/document.cpp index c1b41109..9d6312a7 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1476,14 +1476,7 @@ void Document::generateMesh() toSnapshot(snapshot); resetDirtyFlags(); m_meshGenerator = new MeshGenerator(snapshot); - m_meshGenerator->setSmoothNormal(m_smoothNormal); - m_meshGenerator->setWeldEnabled(weldEnabled); m_meshGenerator->setGeneratedCacheContext(&m_generatedCacheContext); - if (nullptr != m_sharedContextWidget) - m_meshGenerator->setSharedContextWidget(m_sharedContextWidget); - for (auto &part: partMap) { - m_meshGenerator->addPartPreviewRequirement(part.first); - } m_meshGenerator->moveToThread(thread); connect(thread, &QThread::started, m_meshGenerator, &MeshGenerator::process); connect(m_meshGenerator, &MeshGenerator::finished, this, &Document::meshReady); diff --git a/src/materialpreviewsgenerator.cpp b/src/materialpreviewsgenerator.cpp index e69fea52..d592132a 100644 --- a/src/materialpreviewsgenerator.cpp +++ b/src/materialpreviewsgenerator.cpp @@ -57,8 +57,6 @@ void MaterialPreviewsGenerator::generate() GeneratedCacheContext *cacheContext = new GeneratedCacheContext(); MeshGenerator *meshGenerator = new MeshGenerator(snapshot); - meshGenerator->setSmoothNormal(true); - meshGenerator->setWeldEnabled(false); meshGenerator->setGeneratedCacheContext(cacheContext); meshGenerator->generate(); diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index ab237b9a..06b26348 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -1,87 +1,36 @@ -#include -#include +#include #include -#include +#include +#include +#include +#include +#include +#include #include "meshgenerator.h" #include "util.h" -#include "document.h" -#include "meshlite.h" -#include "meshutil.h" -#include "theme.h" -#include "positionmap.h" -#include "meshquadify.h" -#include "meshweldseam.h" -#include "imageforever.h" -#include "material.h" #include "trianglesourcenoderesolve.h" -#include "meshinflate.h" -bool MeshGenerator::m_enableDebug = false; -PositionMap *MeshGenerator::m_forMakePositionKey = new PositionMap; - -GeneratedCacheContext::~GeneratedCacheContext() -{ - for (auto &cache: componentCombinableMeshs) { - deleteCombinableMesh(cache.second); - } -} - -void GeneratedCacheContext::updateComponentCombinableMesh(QString componentId, void *mesh) -{ - auto &cache = componentCombinableMeshs[componentId]; - if (nullptr != cache) - deleteCombinableMesh(cache); - cache = cloneCombinableMesh(mesh); -} +const std::vector g_defaultCutTemplate = { + {-1.0, -1.0}, + { 1.0, -1.0}, + { 1.0, 1.0}, + {-1.0, 1.0}, +}; MeshGenerator::MeshGenerator(Snapshot *snapshot) : - m_snapshot(snapshot), - m_isSucceed(false), - m_mesh(nullptr), - m_outcome(nullptr), - m_sharedContextWidget(nullptr), - m_cacheContext(nullptr), - m_smoothNormal(true), - m_weldEnabled(true) + m_snapshot(snapshot) { } MeshGenerator::~MeshGenerator() { + for (auto &it: m_partPreviewMeshes) + delete it.second; + delete m_resultMesh; delete m_snapshot; - delete m_mesh; - for (const auto &partPreviewMeshIt: m_partPreviewMeshMap) { - delete partPreviewMeshIt.second; - } delete m_outcome; } -void MeshGenerator::setSmoothNormal(bool smoothNormal) -{ - m_smoothNormal = smoothNormal; -} - -void MeshGenerator::setWeldEnabled(bool weldEnabled) -{ - m_weldEnabled = weldEnabled; -} - -void MeshGenerator::setGeneratedCacheContext(GeneratedCacheContext *cacheContext) -{ - m_cacheContext = cacheContext; -} - -void MeshGenerator::addPartPreviewRequirement(const QUuid &partId) -{ - //qDebug() << "addPartPreviewRequirement:" << partId; - m_requirePreviewPartIds.insert(partId); -} - -void MeshGenerator::setSharedContextWidget(QOpenGLWidget *widget) -{ - m_sharedContextWidget = widget; -} - bool MeshGenerator::isSucceed() { return m_isSucceed; @@ -89,23 +38,18 @@ bool MeshGenerator::isSucceed() MeshLoader *MeshGenerator::takeResultMesh() { - MeshLoader *resultMesh = m_mesh; - m_mesh = nullptr; + MeshLoader *resultMesh = m_resultMesh; + m_resultMesh = nullptr; return resultMesh; } MeshLoader *MeshGenerator::takePartPreviewMesh(const QUuid &partId) { - MeshLoader *resultMesh = m_partPreviewMeshMap[partId]; - m_partPreviewMeshMap[partId] = nullptr; + MeshLoader *resultMesh = m_partPreviewMeshes[partId]; + m_partPreviewMeshes[partId] = nullptr; return resultMesh; } -const std::set &MeshGenerator::requirePreviewPartIds() -{ - return m_requirePreviewPartIds; -} - const std::set &MeshGenerator::generatedPreviewPartIds() { return m_generatedPreviewPartIds; @@ -118,94 +62,6 @@ Outcome *MeshGenerator::takeOutcome() return outcome; } -void MeshGenerator::loadVertexSources(void *meshliteContext, int meshId, QUuid partId, const std::map &bmeshToNodeIdMap, std::vector>> &bmeshVertices, - std::vector> &bmeshQuads) -{ - int vertexCount = meshlite_get_vertex_count(meshliteContext, meshId); - int positionBufferLen = vertexCount * 3; - float *positionBuffer = new float[positionBufferLen]; - int positionCount = meshlite_get_vertex_position_array(meshliteContext, meshId, positionBuffer, positionBufferLen) / 3; - int *sourceBuffer = new int[positionBufferLen]; - int sourceCount = meshlite_get_vertex_source_array(meshliteContext, meshId, sourceBuffer, positionBufferLen); - Q_ASSERT(positionCount == sourceCount); - std::vector verticesPositions; - for (int i = 0, positionIndex = 0; i < positionCount; i++, positionIndex+=3) { - std::pair> vertex; - vertex.second.first = partId; - auto findNodeId = bmeshToNodeIdMap.find(sourceBuffer[i]); - if (findNodeId != bmeshToNodeIdMap.end()) - vertex.second.second = findNodeId->second; - vertex.first = QVector3D(positionBuffer[positionIndex + 0], positionBuffer[positionIndex + 1], positionBuffer[positionIndex + 2]); - verticesPositions.push_back(vertex.first); - bmeshVertices.push_back(vertex); - } - int faceCount = meshlite_get_face_count(meshliteContext, meshId); - int *faceVertexNumAndIndices = new int[faceCount * MAX_VERTICES_PER_FACE]; - int filledLength = meshlite_get_face_index_array(meshliteContext, meshId, faceVertexNumAndIndices, faceCount * MAX_VERTICES_PER_FACE); - int i = 0; - while (i < filledLength) { - int num = faceVertexNumAndIndices[i++]; - Q_ASSERT(num > 0 && num <= MAX_VERTICES_PER_FACE); - if (4 != num) { - i += num; - continue; - } - int i0 = faceVertexNumAndIndices[i++]; - int i1 = faceVertexNumAndIndices[i++]; - int i2 = faceVertexNumAndIndices[i++]; - int i3 = faceVertexNumAndIndices[i++]; - const auto &v0 = verticesPositions[i0]; - const auto &v1 = verticesPositions[i1]; - const auto &v2 = verticesPositions[i2]; - const auto &v3 = verticesPositions[i3]; - bmeshQuads.push_back(std::make_tuple(m_forMakePositionKey->makeKey(v0.x(), v0.y(), v0.z()), - m_forMakePositionKey->makeKey(v1.x(), v1.y(), v1.z()), - m_forMakePositionKey->makeKey(v2.x(), v2.y(), v2.z()), - m_forMakePositionKey->makeKey(v3.x(), v3.y(), v3.z()))); - } - delete[] faceVertexNumAndIndices; - delete[] positionBuffer; - delete[] sourceBuffer; -} - -void MeshGenerator::loadGeneratedPositionsToOutcome(void *meshliteContext, int triangulatedMeshId) -{ - int vertexCount = meshlite_get_vertex_count(meshliteContext, triangulatedMeshId); - int positionBufferLen = vertexCount * 3; - float *positionBuffer = new float[positionBufferLen]; - int positionCount = meshlite_get_vertex_position_array(meshliteContext, triangulatedMeshId, positionBuffer, positionBufferLen) / 3; - std::map verticesMap; - for (int i = 0, positionIndex = 0; i < positionCount; i++, positionIndex+=3) { - QVector3D vertex; - vertex = QVector3D(positionBuffer[positionIndex + 0], positionBuffer[positionIndex + 1], positionBuffer[positionIndex + 2]); - verticesMap[i] = m_outcome->vertices.size(); - m_outcome->vertices.push_back(vertex); - } - int faceCount = meshlite_get_face_count(meshliteContext, triangulatedMeshId); - int triangleIndexBufferLen = faceCount * 3; - int *triangleIndexBuffer = new int[triangleIndexBufferLen]; - int triangleCount = meshlite_get_triangle_index_array(meshliteContext, triangulatedMeshId, triangleIndexBuffer, triangleIndexBufferLen) / 3; - int triangleNormalBufferLen = faceCount * 3; - float *normalBuffer = new float[triangleNormalBufferLen]; - int normalCount = meshlite_get_triangle_normal_array(meshliteContext, triangulatedMeshId, normalBuffer, triangleNormalBufferLen) / 3; - Q_ASSERT(triangleCount == normalCount); - for (int i = 0, triangleVertIndex = 0, normalIndex=0; - i < triangleCount; - i++, triangleVertIndex+=3, normalIndex += 3) { - std::vector triangleIndices(3); - QVector3D triangleNormal; - triangleIndices[0] = verticesMap[triangleIndexBuffer[triangleVertIndex + 0]]; - triangleIndices[1] = verticesMap[triangleIndexBuffer[triangleVertIndex + 1]]; - triangleIndices[2] = verticesMap[triangleIndexBuffer[triangleVertIndex + 2]]; - triangleNormal = QVector3D(normalBuffer[normalIndex + 0], normalBuffer[normalIndex + 1], normalBuffer[normalIndex + 2]); - m_outcome->triangles.push_back(triangleIndices); - m_outcome->triangleNormals.push_back(triangleNormal); - } - delete[] positionBuffer; - delete[] triangleIndexBuffer; - delete[] normalBuffer; -} - void MeshGenerator::collectParts() { for (const auto &node: m_snapshot->nodes) { @@ -222,265 +78,25 @@ void MeshGenerator::collectParts() } } -bool MeshGenerator::checkIsPartDirty(QString partId) +bool MeshGenerator::checkIsPartDirty(const QString &partIdString) { - auto findPart = m_snapshot->parts.find(partId); + auto findPart = m_snapshot->parts.find(partIdString); if (findPart == m_snapshot->parts.end()) { - qDebug() << "Find part failed:" << partId; + qDebug() << "Find part failed:" << partIdString; return false; } return isTrueValueString(valueOfKeyInMapOrEmpty(findPart->second, "dirty")); } -void *MeshGenerator::combinePartMesh(QString partId, std::vector> *balls) -{ - auto findPart = m_snapshot->parts.find(partId); - if (findPart == m_snapshot->parts.end()) { - qDebug() << "Find part failed:" << partId; - return nullptr; - } - QUuid partIdNotAsString = QUuid(partId); - - auto &part = findPart->second; - bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled")); - bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored")); - bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived")); - bool wrapped = isTrueValueString(valueOfKeyInMapOrEmpty(part, "wrapped")); - int bmeshId = meshlite_bmesh_create(m_meshliteContext); - if (subdived) - meshlite_bmesh_set_cut_subdiv_count(m_meshliteContext, bmeshId, 1); - if (isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded"))) - meshlite_bmesh_set_round_way(m_meshliteContext, bmeshId, 1); - - QString colorString = valueOfKeyInMapOrEmpty(part, "color"); - QColor partColor = colorString.isEmpty() ? Theme::white : QColor(colorString); - - QString thicknessString = valueOfKeyInMapOrEmpty(part, "deformThickness"); - if (!thicknessString.isEmpty()) - meshlite_bmesh_set_deform_thickness(m_meshliteContext, bmeshId, thicknessString.toFloat()); - QString widthString = valueOfKeyInMapOrEmpty(part, "deformWidth"); - if (!widthString.isEmpty()) - meshlite_bmesh_set_deform_width(m_meshliteContext, bmeshId, widthString.toFloat()); - if (MeshGenerator::m_enableDebug) - meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1); - - QUuid materialId; - QString materialIdString = valueOfKeyInMapOrEmpty(part, "materialId"); - if (!materialIdString.isEmpty()) - materialId = QUuid(materialIdString); - - QString mirroredPartId; - QUuid mirroredPartIdNotAsString; - if (xMirrored) { - mirroredPartIdNotAsString = QUuid().createUuid(); - mirroredPartId = mirroredPartIdNotAsString.toString(); - m_cacheContext->partMirrorIdMap[mirroredPartId] = partId; - } - - std::map nodeToBmeshIdMap; - std::map bmeshToNodeIdMap; - auto &cacheBmeshNodes = m_cacheContext->partBmeshNodes[partId]; - auto &cacheBmeshVertices = m_cacheContext->partBmeshVertices[partId]; - auto &cacheBmeshQuads = m_cacheContext->partBmeshQuads[partId]; - cacheBmeshNodes.clear(); - cacheBmeshVertices.clear(); - cacheBmeshQuads.clear(); - std::map> bmeshNodeIdToDataMap; - - struct NodeInfo - { - float radius = 0; - QVector3D position; - BoneMark boneMark = BoneMark::None; - }; - std::map nodeInfos; - for (const auto &nodeId: m_partNodeIds[partId]) { - auto findNode = m_snapshot->nodes.find(nodeId); - if (findNode == m_snapshot->nodes.end()) { - qDebug() << "Find node failed:" << nodeId; - continue; - } - auto &node = findNode->second; - - float radius = valueOfKeyInMapOrEmpty(node, "radius").toFloat(); - float x = (valueOfKeyInMapOrEmpty(node, "x").toFloat() - m_mainProfileMiddleX); - float y = (m_mainProfileMiddleY - valueOfKeyInMapOrEmpty(node, "y").toFloat()); - float z = (m_sideProfileMiddleX - valueOfKeyInMapOrEmpty(node, "z").toFloat()); - - BoneMark boneMark = BoneMarkFromString(valueOfKeyInMapOrEmpty(node, "boneMark").toUtf8().constData()); - - auto &nodeInfo = nodeInfos[nodeId]; - nodeInfo.position = QVector3D(x, y, z); - nodeInfo.radius = radius; - nodeInfo.boneMark = boneMark; - - if (nullptr != balls) { - balls->push_back({QVector3D(x, y, z), radius}); - if (xMirrored) { - balls->push_back({QVector3D(-x, y, z), radius}); - } - } - } - std::set> edges; - for (const auto &edgeId: m_partEdgeIds[partId]) { - auto findEdge = m_snapshot->edges.find(edgeId); - if (findEdge == m_snapshot->edges.end()) { - qDebug() << "Find edge failed:" << edgeId; - continue; - } - auto &edge = findEdge->second; - - QString fromNodeId = valueOfKeyInMapOrEmpty(edge, "from"); - QString toNodeId = valueOfKeyInMapOrEmpty(edge, "to"); - - std::function connectNodes; - connectNodes = [&connectNodes, &edges, &nodeInfos](const QString &fromNodeId, const QString &toNodeId) { - - const auto &findFromNodeInfo = nodeInfos.find(fromNodeId); - if (findFromNodeInfo == nodeInfos.end()) { - qDebug() << "Find from-node info failed:" << fromNodeId; - return; - } - - const auto &findToNodeInfo = nodeInfos.find(toNodeId); - if (findToNodeInfo == nodeInfos.end()) { - qDebug() << "Find to-node info failed:" << toNodeId; - return; - } - - auto distanceBetweenNodes = findFromNodeInfo->second.position.distanceToPoint(findToNodeInfo->second.position); - float centerEmptyLength = distanceBetweenNodes - (findFromNodeInfo->second.radius + findToNodeInfo->second.radius); - if (centerEmptyLength < distanceBetweenNodes * 0.5) { - edges.insert({fromNodeId, toNodeId}); - return; - } - - // Cut off by add intermediate nodes - QString newNodeId = QUuid::createUuid().toString(); - auto &nodeInfo = nodeInfos[newNodeId]; - nodeInfo.position = (findFromNodeInfo->second.position + findToNodeInfo->second.position) / 2; - nodeInfo.radius = (findFromNodeInfo->second.radius + findToNodeInfo->second.radius) / 2; - - connectNodes(fromNodeId, newNodeId); - connectNodes(newNodeId, toNodeId); - }; - - connectNodes(fromNodeId, toNodeId); - } - - - for (const auto &nodeIt: nodeInfos) { - const auto &nodeId = nodeIt.first; - const auto &nodeInfo = nodeIt.second; - - int bmeshNodeId = meshlite_bmesh_add_node(m_meshliteContext, bmeshId, - nodeInfo.position.x(), nodeInfo.position.y(), nodeInfo.position.z(), nodeInfo.radius); - - nodeToBmeshIdMap[nodeId] = bmeshNodeId; - bmeshToNodeIdMap[bmeshNodeId] = nodeId; - - OutcomeNode bmeshNode; - bmeshNode.partId = QUuid(partId); - bmeshNode.origin = nodeInfo.position; - bmeshNode.radius = nodeInfo.radius; - bmeshNode.nodeId = QUuid(nodeId); - bmeshNode.color = partColor; - bmeshNode.materialId = materialId; - bmeshNode.boneMark = nodeInfo.boneMark; - bmeshNode.mirroredByPartId = mirroredPartId; - bmeshNodeIdToDataMap[bmeshNodeId].push_back(cacheBmeshNodes.size()); - cacheBmeshNodes.push_back(bmeshNode); - if (xMirrored) { - bmeshNode.partId = mirroredPartId; - bmeshNode.mirrorFromPartId = QUuid(partId); - bmeshNode.mirroredByPartId = QUuid(); - bmeshNode.origin.setX(-nodeInfo.position.x()); - bmeshNodeIdToDataMap[bmeshNodeId].push_back(cacheBmeshNodes.size()); - cacheBmeshNodes.push_back(bmeshNode); - } - } - - for (const auto &edgeIt: edges) { - const QString &fromNodeId = edgeIt.first; - const QString &toNodeId = edgeIt.second; - - auto findFromBmeshId = nodeToBmeshIdMap.find(fromNodeId); - if (findFromBmeshId == nodeToBmeshIdMap.end()) { - qDebug() << "Find from-node bmesh failed:" << fromNodeId; - continue; - } - - auto findToBmeshId = nodeToBmeshIdMap.find(toNodeId); - if (findToBmeshId == nodeToBmeshIdMap.end()) { - qDebug() << "Find to-node bmesh failed:" << toNodeId; - continue; - } - - meshlite_bmesh_add_edge(m_meshliteContext, bmeshId, findFromBmeshId->second, findToBmeshId->second); - } - - int meshId = 0; - void *resultMesh = nullptr; - if (!bmeshToNodeIdMap.empty()) { - meshId = meshlite_bmesh_generate_mesh(m_meshliteContext, bmeshId); - for (const auto &item: bmeshNodeIdToDataMap) { - float baseNormal[3] = {0, 0, 0}; - meshlite_bmesh_get_node_base_norm(m_meshliteContext, bmeshId, item.first, baseNormal); - for (auto &subItem: item.second) - cacheBmeshNodes[subItem].baseNormal = QVector3D(baseNormal[0], baseNormal[1], baseNormal[2]); - } - loadVertexSources(m_meshliteContext, meshId, partIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices, cacheBmeshQuads); - if (wrapped) - resultMesh = convertToCombinableConvexHullMesh(m_meshliteContext, meshId); - else - resultMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, meshId)); - } - - if (nullptr != resultMesh) { - if (xMirrored) { - int xMirroredMeshId = meshlite_mirror_in_x(m_meshliteContext, meshId, 0); - loadVertexSources(m_meshliteContext, xMirroredMeshId, mirroredPartIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices, cacheBmeshQuads); - void *mirroredMesh = nullptr; - if (wrapped) - mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId); - else - mirroredMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, xMirroredMeshId)); - if (nullptr != mirroredMesh) { - void *newResultMesh = unionCombinableMeshs(resultMesh, mirroredMesh); - deleteCombinableMesh(mirroredMesh); - if (nullptr != newResultMesh) { - deleteCombinableMesh(resultMesh); - resultMesh = newResultMesh; - } - } - } - } - - if (m_requirePreviewPartIds.find(partIdNotAsString) != m_requirePreviewPartIds.end()) { - int trimedMeshId = meshlite_trim(m_meshliteContext, meshId, 1); - m_partPreviewMeshMap[partIdNotAsString] = new MeshLoader(m_meshliteContext, trimedMeshId, -1, partColor, nullptr, m_smoothNormal); - m_generatedPreviewPartIds.insert(partIdNotAsString); - } - - if (isDisabled) { - if (nullptr != resultMesh) { - deleteCombinableMesh(resultMesh); - resultMesh = nullptr; - } - } - - return resultMesh; -} - -bool MeshGenerator::checkIsComponentDirty(QString componentId) +bool MeshGenerator::checkIsComponentDirty(const QString &componentIdString) { bool isDirty = false; const std::map *component = &m_snapshot->rootComponent; - if (componentId != QUuid().toString()) { - auto findComponent = m_snapshot->components.find(componentId); + if (componentIdString != QUuid().toString()) { + auto findComponent = m_snapshot->components.find(componentIdString); if (findComponent == m_snapshot->components.end()) { - qDebug() << "Component not found:" << componentId; + qDebug() << "Component not found:" << componentIdString; return isDirty; } component = &findComponent->second; @@ -508,220 +124,495 @@ bool MeshGenerator::checkIsComponentDirty(QString componentId) } if (isDirty) - m_dirtyComponentIds.insert(componentId); + m_dirtyComponentIds.insert(componentIdString); return isDirty; } -void *MeshGenerator::combineComponentMesh(QString componentId, CombineMode *combineMode, std::vector> *inflateBalls) +void MeshGenerator::checkDirtyFlags() { - QUuid componentIdNotAsString; + checkIsComponentDirty(QUuid().toString()); +} + +nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString) +{ + auto findPart = m_snapshot->parts.find(partIdString); + if (findPart == m_snapshot->parts.end()) { + qDebug() << "Find part failed:" << partIdString; + return nullptr; + } + QUuid partId = QUuid(partIdString); + auto &part = findPart->second; + bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled")); + bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored")); + bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived")); + bool rounded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded")); + QString colorString = valueOfKeyInMapOrEmpty(part, "color"); + QColor partColor = colorString.isEmpty() ? m_defaultPartColor : QColor(colorString); + float deformThickness = 1.0; + float deformWidth = 1.0; + + QString thicknessString = valueOfKeyInMapOrEmpty(part, "deformThickness"); + if (!thicknessString.isEmpty()) { + deformThickness = thicknessString.toFloat(); + } + + QString widthString = valueOfKeyInMapOrEmpty(part, "deformWidth"); + if (!widthString.isEmpty()) { + deformWidth = widthString.toFloat(); + } + + QUuid materialId; + QString materialIdString = valueOfKeyInMapOrEmpty(part, "materialId"); + if (!materialIdString.isEmpty()) + materialId = QUuid(materialIdString); + + auto &partCache = m_cacheContext->parts[partIdString]; + partCache.outcomeNodes.clear(); + partCache.outcomeNodeVertices.clear(); + partCache.vertices.clear(); + partCache.faces.clear(); + delete partCache.mesh; + partCache.mesh = nullptr; + + struct NodeInfo + { + float radius = 0; + QVector3D position; + BoneMark boneMark = BoneMark::None; + }; + std::map nodeInfos; + for (const auto &nodeIdString: m_partNodeIds[partIdString]) { + auto findNode = m_snapshot->nodes.find(nodeIdString); + if (findNode == m_snapshot->nodes.end()) { + qDebug() << "Find node failed:" << nodeIdString; + continue; + } + auto &node = findNode->second; + + float radius = valueOfKeyInMapOrEmpty(node, "radius").toFloat(); + float x = (valueOfKeyInMapOrEmpty(node, "x").toFloat() - m_mainProfileMiddleX); + float y = (m_mainProfileMiddleY - valueOfKeyInMapOrEmpty(node, "y").toFloat()); + float z = (m_sideProfileMiddleX - valueOfKeyInMapOrEmpty(node, "z").toFloat()); + + BoneMark boneMark = BoneMarkFromString(valueOfKeyInMapOrEmpty(node, "boneMark").toUtf8().constData()); + + auto &nodeInfo = nodeInfos[nodeIdString]; + nodeInfo.position = QVector3D(x, y, z); + nodeInfo.radius = radius; + nodeInfo.boneMark = boneMark; + } + + std::set> edges; + for (const auto &edgeIdString: m_partEdgeIds[partIdString]) { + auto findEdge = m_snapshot->edges.find(edgeIdString); + if (findEdge == m_snapshot->edges.end()) { + qDebug() << "Find edge failed:" << edgeIdString; + continue; + } + auto &edge = findEdge->second; + + QString fromNodeIdString = valueOfKeyInMapOrEmpty(edge, "from"); + QString toNodeIdString = valueOfKeyInMapOrEmpty(edge, "to"); + + const auto &findFromNodeInfo = nodeInfos.find(fromNodeIdString); + if (findFromNodeInfo == nodeInfos.end()) { + qDebug() << "Find from-node info failed:" << fromNodeIdString; + continue; + } + + const auto &findToNodeInfo = nodeInfos.find(toNodeIdString); + if (findToNodeInfo == nodeInfos.end()) { + qDebug() << "Find to-node info failed:" << toNodeIdString; + continue; + } + + edges.insert({fromNodeIdString, toNodeIdString}); + } + + std::map nodeIdStringToIndexMap; + std::map nodeIndexToIdStringMap; + + nodemesh::Modifier *modifier = new nodemesh::Modifier; + + QString mirroredPartIdString; + QUuid mirroredPartId; + if (xMirrored) { + mirroredPartId = QUuid().createUuid(); + mirroredPartIdString = mirroredPartId.toString(); + m_cacheContext->partMirrorIdMap[mirroredPartIdString] = partIdString; + } + + for (const auto &nodeIt: nodeInfos) { + const auto &nodeIdString = nodeIt.first; + const auto &nodeInfo = nodeIt.second; + size_t nodeIndex = modifier->addNode(nodeInfo.position, nodeInfo.radius, g_defaultCutTemplate); + nodeIdStringToIndexMap[nodeIdString] = nodeIndex; + nodeIndexToIdStringMap[nodeIndex] = nodeIdString; + + OutcomeNode outcomeNode; + outcomeNode.partId = QUuid(partIdString); + outcomeNode.nodeId = QUuid(nodeIdString); + outcomeNode.origin = nodeInfo.position; + outcomeNode.radius = nodeInfo.radius; + outcomeNode.color = partColor; + outcomeNode.materialId = materialId; + outcomeNode.boneMark = nodeInfo.boneMark; + outcomeNode.mirroredByPartId = mirroredPartIdString; + partCache.outcomeNodes.push_back(outcomeNode); + if (xMirrored) { + outcomeNode.partId = mirroredPartId; + outcomeNode.mirrorFromPartId = QUuid(partId); + outcomeNode.mirroredByPartId = QUuid(); + outcomeNode.origin.setX(-nodeInfo.position.x()); + partCache.outcomeNodes.push_back(outcomeNode); + } + } + + for (const auto &edgeIt: edges) { + const QString &fromNodeIdString = edgeIt.first; + const QString &toNodeIdString = edgeIt.second; + + auto findFromNodeIndex = nodeIdStringToIndexMap.find(fromNodeIdString); + if (findFromNodeIndex == nodeIdStringToIndexMap.end()) { + qDebug() << "Find from-node failed:" << fromNodeIdString; + continue; + } + + auto findToNodeIndex = nodeIdStringToIndexMap.find(toNodeIdString); + if (findToNodeIndex == nodeIdStringToIndexMap.end()) { + qDebug() << "Find to-node failed:" << toNodeIdString; + continue; + } + + modifier->addEdge(findFromNodeIndex->second, findToNodeIndex->second); + } + + if (subdived) + modifier->subdivide(); + + if (rounded) + modifier->roundEnd(); + + modifier->finalize(); + + nodemesh::Builder *builder = new nodemesh::Builder; + builder->setDeformThickness(deformThickness); + builder->setDeformWidth(deformWidth); + + for (const auto &node: modifier->nodes()) + builder->addNode(node.position, node.radius, node.cutTemplate); + for (const auto &edge: modifier->edges()) + builder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex); + bool buildSucceed = builder->build(); + + partCache.vertices = builder->generatedVertices(); + partCache.faces = builder->generatedFaces(); + for (size_t i = 0; i < partCache.vertices.size(); ++i) { + const auto &position = partCache.vertices[i]; + const auto &source = builder->generatedVerticesSourceNodeIndices()[i]; + size_t nodeIndex = modifier->nodes()[source].originNodeIndex; + const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex]; + partCache.outcomeNodeVertices.push_back({position, {partIdString, nodeIdString}}); + } + + nodemesh::Combiner::Mesh *mesh = nullptr; + + if (buildSucceed) { + mesh = new nodemesh::Combiner::Mesh(partCache.vertices, partCache.faces); + if (!mesh->isNull()) { + if (xMirrored) { + std::vector xMirroredVertices; + std::vector> xMirroredFaces; + makeXmirror(partCache.vertices, partCache.faces, &xMirroredVertices, &xMirroredFaces); + for (size_t i = 0; i < xMirroredVertices.size(); ++i) { + const auto &position = xMirroredVertices[i]; + const auto &source = builder->generatedVerticesSourceNodeIndices()[i]; + size_t nodeIndex = modifier->nodes()[source].originNodeIndex; + const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex]; + partCache.outcomeNodeVertices.push_back({position, {mirroredPartIdString, nodeIdString}}); + } + size_t xMirrorStart = partCache.vertices.size(); + for (const auto &vertex: xMirroredVertices) + partCache.vertices.push_back(vertex); + for (const auto &face: xMirroredFaces) { + std::vector newFace = face; + for (auto &it: newFace) + it += xMirrorStart; + partCache.faces.push_back(newFace); + } + nodemesh::Combiner::Mesh *xMirroredMesh = new nodemesh::Combiner::Mesh(xMirroredVertices, xMirroredFaces); + nodemesh::Combiner::Mesh *newMesh = combineTwoMeshes(*mesh, + *xMirroredMesh, nodemesh::Combiner::Method::Union); + delete xMirroredMesh; + if (newMesh && !newMesh->isNull()) { + delete mesh; + mesh = newMesh; + } else { + m_isSucceed = false; + qDebug() << "Xmirrored mesh generate failed"; + delete newMesh; + } + } + } else { + m_isSucceed = false; + qDebug() << "Mesh built is uncombinable"; + } + } else { + m_isSucceed = false; + qDebug() << "Mesh build failed"; + } + + m_partPreviewMeshes[partId] = nullptr; + m_generatedPreviewPartIds.insert(partId); + + if (nullptr != mesh) { + partCache.mesh = new nodemesh::Combiner::Mesh(*mesh); + + std::vector partPreviewVertices; + std::vector> partPreviewTriangles; + mesh->fetch(partPreviewVertices, partPreviewTriangles); + nodemesh::trim(&partPreviewVertices, true); + std::vector partPreviewTriangleNormals; + for (const auto &face: partPreviewTriangles) { + partPreviewTriangleNormals.push_back(QVector3D::normal( + partPreviewVertices[face[0]], + partPreviewVertices[face[1]], + partPreviewVertices[face[2]] + )); + } + std::vector> partPreviewTriangleVertexNormals; + generateSmoothTriangleVertexNormals(partPreviewVertices, + partPreviewTriangles, + partPreviewTriangleNormals, + &partPreviewTriangleVertexNormals); + m_partPreviewMeshes[partId] = new MeshLoader(partPreviewVertices, + partPreviewTriangles, + partPreviewTriangleVertexNormals, + partColor); + } + + delete builder; + delete modifier; + + if (mesh && mesh->isNull()) { + delete mesh; + mesh = nullptr; + } + + if (isDisabled) { + delete mesh; + mesh = nullptr; + } + + return mesh; +} + +nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &componentIdString, CombineMode *combineMode) +{ + nodemesh::Combiner::Mesh *mesh = nullptr; + + QUuid componentId; const std::map *component = &m_snapshot->rootComponent; - if (componentId != QUuid().toString()) { - componentIdNotAsString = QUuid(componentId); - auto findComponent = m_snapshot->components.find(componentId); + if (componentIdString != QUuid().toString()) { + componentId = QUuid(componentIdString); + auto findComponent = m_snapshot->components.find(componentIdString); if (findComponent == m_snapshot->components.end()) { - qDebug() << "Component not found:" << componentId; + qDebug() << "Component not found:" << componentIdString; return nullptr; } component = &findComponent->second; } - + *combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData()); + if (*combineMode == CombineMode::Normal) { + if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "inverse"))) + *combineMode = CombineMode::Inversion; + } - if (*combineMode == CombineMode::Inflation) { - if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) { - auto findCacheInflateBalls = m_cacheContext->componentInflateBalls.find(componentId); - if (findCacheInflateBalls != m_cacheContext->componentInflateBalls.end()) { - *inflateBalls = findCacheInflateBalls->second; + auto &componentCache = m_cacheContext->components[componentIdString]; + + if (m_cacheEnabled) { + if (m_dirtyComponentIds.find(componentIdString) == m_dirtyComponentIds.end()) { + if (nullptr == componentCache.mesh) return nullptr; - } + return new nodemesh::Combiner::Mesh(*componentCache.mesh); } } - if (*combineMode != CombineMode::Inflation) { - if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) { - auto findCachedMesh = m_cacheContext->componentCombinableMeshs.find(componentId); - if (findCachedMesh != m_cacheContext->componentCombinableMeshs.end() && - nullptr != findCachedMesh->second) { - //qDebug() << "Component mesh cache used:" << componentId; - return cloneCombinableMesh(findCachedMesh->second); - } - } - } + componentCache.sharedQuadEdges.clear(); + componentCache.noneSeamVertices.clear(); + componentCache.outcomeNodes.clear(); + componentCache.outcomeNodeVertices.clear(); + delete componentCache.mesh; + componentCache.mesh = nullptr; - bool smoothSeam = false; - float smoothSeamFactor = 0.0; - QString smoothSeamString = valueOfKeyInMapOrEmpty(*component, "smoothSeam"); - if (!smoothSeamString.isEmpty()) { - smoothSeam = true; - smoothSeamFactor = smoothSeamString.toFloat(); - } - - bool smoothAll = false; - float smoothAllFactor = 0.0; - QString smoothAllString = valueOfKeyInMapOrEmpty(*component, "smoothAll"); - if (!smoothAllString.isEmpty()) { - smoothAll = true; - smoothAllFactor = smoothAllString.toFloat(); - } - - void *resultMesh = nullptr; - - PositionMap positionsBeforeCombination; - auto &verticesSources = m_cacheContext->componentVerticesSources[componentId]; - verticesSources.map().clear(); QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType"); if ("partId" == linkDataType) { - QString partId = valueOfKeyInMapOrEmpty(*component, "linkData"); - std::vector> partBalls; - resultMesh = combinePartMesh(partId, &partBalls); - if (*combineMode == CombineMode::Inflation) { - deleteCombinableMesh(resultMesh); - resultMesh = nullptr; - inflateBalls->insert(inflateBalls->end(), partBalls.begin(), partBalls.end()); - } else { - for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) { - verticesSources.addPosition(bmeshVertex.first.x(), bmeshVertex.first.y(), bmeshVertex.first.z(), - bmeshVertex); - } - } + QString partIdString = valueOfKeyInMapOrEmpty(*component, "linkData"); + mesh = combinePartMesh(partIdString); + + const auto &partCache = m_cacheContext->parts[partIdString]; + for (const auto &vertex: partCache.vertices) + componentCache.noneSeamVertices.insert(vertex); + collectSharedQuadEdges(partCache.vertices, partCache.faces, &componentCache.sharedQuadEdges); + for (const auto &it: partCache.outcomeNodes) + componentCache.outcomeNodes.push_back(it); + for (const auto &it: partCache.outcomeNodeVertices) + componentCache.outcomeNodeVertices.push_back(it); } else { - for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { - if (childId.isEmpty()) + for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { + if (childIdString.isEmpty()) continue; CombineMode childCombineMode = CombineMode::Normal; - std::vector> childInflateBalls; - void *childCombinedMesh = combineComponentMesh(childId, &childCombineMode, &childInflateBalls); - inflateBalls->insert(inflateBalls->end(), childInflateBalls.begin(), childInflateBalls.end()); - if (childCombineMode == CombineMode::Inflation) { - deleteCombinableMesh(childCombinedMesh); - childCombinedMesh = nullptr; - if (nullptr == resultMesh) - continue; - std::vector> inflatedVertices; - void *inflatedMesh = meshInflate(resultMesh, childInflateBalls, inflatedVertices); - deleteCombinableMesh(resultMesh); - resultMesh = inflatedMesh; - for (const auto &item: inflatedVertices) { - const auto &oldPosition = item.first; - const auto &newPosition = item.second; - std::pair> source; - if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) { - verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z()); - source.first = newPosition; - verticesSources.addPosition(newPosition.x(), newPosition.y(), newPosition.z(), source); - } - } + nodemesh::Combiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode); + + const auto &childComponentCache = m_cacheContext->components[childIdString]; + for (const auto &vertex: childComponentCache.noneSeamVertices) + componentCache.noneSeamVertices.insert(vertex); + for (const auto &it: childComponentCache.sharedQuadEdges) + componentCache.sharedQuadEdges.insert(it); + for (const auto &it: childComponentCache.outcomeNodes) + componentCache.outcomeNodes.push_back(it); + for (const auto &it: childComponentCache.outcomeNodeVertices) + componentCache.outcomeNodeVertices.push_back(it); + + qDebug() << "Combine mode:" << CombineModeToString(childCombineMode); + if (nullptr == subMesh) { + m_isSucceed = false; + qDebug() << "Child mesh is null"; continue; } - for (const auto &positionIt: m_cacheContext->componentPositions[childId]) { - positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true); - } - for (const auto &verticesSourceIt: m_cacheContext->componentVerticesSources[childId].map()) { - verticesSources.map()[verticesSourceIt.first] = verticesSourceIt.second; - } - if (nullptr == childCombinedMesh) + if (subMesh->isNull()) { + m_isSucceed = false; + qDebug() << "Child mesh is uncombinable"; + delete subMesh; continue; - if (nullptr == resultMesh) { + } + if (nullptr == mesh) { if (childCombineMode == CombineMode::Inversion) { - deleteCombinableMesh(childCombinedMesh); + delete subMesh; } else { - resultMesh = childCombinedMesh; + mesh = subMesh; } } else { - void *newResultMesh = childCombineMode == CombineMode::Inversion ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh); - deleteCombinableMesh(childCombinedMesh); - if (nullptr != newResultMesh) { - deleteCombinableMesh(resultMesh); - resultMesh = newResultMesh; + nodemesh::Combiner::Mesh *newMesh = combineTwoMeshes(*mesh, + *subMesh, + childCombineMode == CombineMode::Inversion ? + nodemesh::Combiner::Method::Diff : nodemesh::Combiner::Method::Union); + delete subMesh; + if (newMesh && !newMesh->isNull()) { + delete mesh; + mesh = newMesh; + } else { + m_isSucceed = false; + qDebug() << "Mesh combine failed"; + delete newMesh; } } } } - if (nullptr != resultMesh) { - int meshIdForSmooth = convertFromCombinableMesh(m_meshliteContext, resultMesh); - std::vector positionsBeforeSmooth; - loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsBeforeSmooth); - - if (!positionsBeforeSmooth.empty()) { - std::vector seamVerticesIds; - std::unordered_set seamVerticesIndices; - - if (!positionsBeforeCombination.map().empty()) { - for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) { - const auto &oldPosition = positionsBeforeSmooth[vertexIndex]; - if (!positionsBeforeCombination.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z())) { - seamVerticesIds.push_back(vertexIndex + 1); - seamVerticesIndices.insert(vertexIndex); - } + if (nullptr != mesh) + componentCache.mesh = new nodemesh::Combiner::Mesh(*mesh); + + if (nullptr != mesh && mesh->isNull()) { + delete mesh; + mesh = nullptr; + } + + return mesh; +} + +nodemesh::Combiner::Mesh *MeshGenerator::combineTwoMeshes(const nodemesh::Combiner::Mesh &first, const nodemesh::Combiner::Mesh &second, + nodemesh::Combiner::Method method) +{ + if (first.isNull() || second.isNull()) + return nullptr; + std::vector> combinedVerticesSources; + nodemesh::Combiner::Mesh *newMesh = nodemesh::Combiner::combine(first, + second, + method, + &combinedVerticesSources); + if (nullptr == newMesh) + return nullptr; + if (!newMesh->isNull()) { + nodemesh::Recombiner recombiner; + std::vector combinedVertices; + std::vector> combinedFaces; + newMesh->fetch(combinedVertices, combinedFaces); + recombiner.setVertices(&combinedVertices, &combinedVerticesSources); + recombiner.setFaces(&combinedFaces); + if (recombiner.recombine()) { + if (nodemesh::isManifold(recombiner.regeneratedFaces())) { + nodemesh::Combiner::Mesh *reMesh = new nodemesh::Combiner::Mesh(recombiner.regeneratedVertices(), recombiner.regeneratedFaces(), false); + if (!reMesh->isNull() && !reMesh->isSelfIntersected()) { + delete newMesh; + newMesh = reMesh; + } else { + delete reMesh; } } - - bool meshChanged = false; - - if (smoothSeam) { - if (!seamVerticesIds.empty()) { - //qDebug() << "smoothSeamFactor:" << smoothSeamFactor << "seamVerticesIndices.size():" << seamVerticesNum; - meshlite_smooth_vertices(m_meshliteContext, meshIdForSmooth, smoothSeamFactor, seamVerticesIds.data(), seamVerticesIds.size()); - meshChanged = true; - } - } - - if (smoothAll) { - meshlite_smooth(m_meshliteContext, meshIdForSmooth, smoothAllFactor); - meshChanged = true; - } - - if (meshChanged) { - std::vector positionsAfterSmooth; - loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsAfterSmooth); - Q_ASSERT(positionsBeforeSmooth.size() == positionsAfterSmooth.size()); - - for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) { - const auto &oldPosition = positionsBeforeSmooth[vertexIndex]; - const auto &smoothedPosition = positionsAfterSmooth[vertexIndex]; - std::pair> source; - if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) { - verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z()); - source.first = smoothedPosition; - verticesSources.addPosition(smoothedPosition.x(), smoothedPosition.y(), smoothedPosition.z(), source); - } - } - deleteCombinableMesh(resultMesh); - resultMesh = convertToCombinableMesh(m_meshliteContext, meshIdForSmooth); - } } } - - m_cacheContext->componentInflateBalls[componentId] = *inflateBalls; - - m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh); - auto &cachedComponentPositions = m_cacheContext->componentPositions[componentId]; - cachedComponentPositions.clear(); - loadCombinableMeshVerticesPositions(resultMesh, cachedComponentPositions); - - if (nullptr == resultMesh) { - m_isSucceed = false; + return newMesh; +} + +void MeshGenerator::makeXmirror(const std::vector &sourceVertices, const std::vector> &sourceFaces, + std::vector *destVertices, std::vector> *destFaces) +{ + for (const auto &mirrorFrom: sourceVertices) { + destVertices->push_back(QVector3D(-mirrorFrom.x(), mirrorFrom.y(), mirrorFrom.z())); } + std::vector> newFaces; + for (const auto &mirrorFrom: sourceFaces) { + auto newFace = mirrorFrom; + std::reverse(newFace.begin(), newFace.end()); + destFaces->push_back(newFace); + } +} + +void MeshGenerator::collectSharedQuadEdges(const std::vector &vertices, const std::vector> &faces, + std::set> *sharedQuadEdges) +{ + for (const auto &face: faces) { + if (face.size() != 4) + continue; + sharedQuadEdges->insert({ + nodemesh::PositionKey(vertices[face[0]]), + nodemesh::PositionKey(vertices[face[2]]) + }); + sharedQuadEdges->insert({ + nodemesh::PositionKey(vertices[face[1]]), + nodemesh::PositionKey(vertices[face[3]]) + }); + } +} + +void MeshGenerator::setGeneratedCacheContext(GeneratedCacheContext *cacheContext) +{ + m_cacheContext = cacheContext; +} + +void MeshGenerator::process() +{ + generate(); - return resultMesh; + this->moveToThread(QGuiApplication::instance()->thread()); + emit finished(); } void MeshGenerator::generate() { if (nullptr == m_snapshot) return; + + m_isSucceed = true; + QElapsedTimer countTimeConsumed; countTimeConsumed.start(); - m_isSucceed = true; - - m_meshliteContext = meshlite_create_context(); - - initMeshUtils(); m_outcome = new Outcome; bool needDeleteCacheContext = false; @@ -729,7 +620,8 @@ void MeshGenerator::generate() m_cacheContext = new GeneratedCacheContext; needDeleteCacheContext = true; } else { - for (auto it = m_cacheContext->partBmeshNodes.begin(); it != m_cacheContext->partBmeshNodes.end(); ) { + m_cacheEnabled = true; + for (auto it = m_cacheContext->parts.begin(); it != m_cacheContext->parts.end(); ) { if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) { auto mirrorFrom = m_cacheContext->partMirrorIdMap.find(it->first); if (mirrorFrom != m_cacheContext->partMirrorIdMap.end()) { @@ -739,50 +631,14 @@ void MeshGenerator::generate() } m_cacheContext->partMirrorIdMap.erase(mirrorFrom); } - it = m_cacheContext->partBmeshNodes.erase(it); + it = m_cacheContext->parts.erase(it); continue; } it++; } - for (auto it = m_cacheContext->partBmeshVertices.begin(); it != m_cacheContext->partBmeshVertices.end(); ) { - if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) { - it = m_cacheContext->partBmeshVertices.erase(it); - continue; - } - it++; - } - for (auto it = m_cacheContext->partBmeshQuads.begin(); it != m_cacheContext->partBmeshQuads.end(); ) { - if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) { - it = m_cacheContext->partBmeshQuads.erase(it); - continue; - } - it++; - } - for (auto it = m_cacheContext->componentCombinableMeshs.begin(); it != m_cacheContext->componentCombinableMeshs.end(); ) { + for (auto it = m_cacheContext->components.begin(); it != m_cacheContext->components.end(); ) { if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) { - deleteCombinableMesh(it->second); - it = m_cacheContext->componentCombinableMeshs.erase(it); - continue; - } - it++; - } - for (auto it = m_cacheContext->componentPositions.begin(); it != m_cacheContext->componentPositions.end(); ) { - if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) { - it = m_cacheContext->componentPositions.erase(it); - continue; - } - it++; - } - for (auto it = m_cacheContext->componentInflateBalls.begin(); it != m_cacheContext->componentInflateBalls.end(); ) { - if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) { - it = m_cacheContext->componentInflateBalls.erase(it); - continue; - } - it++; - } - for (auto it = m_cacheContext->componentVerticesSources.begin(); it != m_cacheContext->componentVerticesSources.end(); ) { - if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) { - it = m_cacheContext->componentVerticesSources.erase(it); + it = m_cacheContext->components.erase(it); continue; } it++; @@ -798,102 +654,90 @@ void MeshGenerator::generate() m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat(); m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat(); - int resultMeshId = 0; - CombineMode combineMode; - std::vector> inflateBalls; - void *combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode, &inflateBalls); + auto combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode); + + const auto &componentCache = m_cacheContext->components[QUuid().toString()]; + + std::vector combinedVertices; + std::vector> combinedFaces; if (nullptr != combinedMesh) { - resultMeshId = convertFromCombinableMesh(m_meshliteContext, combinedMesh); - deleteCombinableMesh(combinedMesh); - } - - for (const auto &verticesSourcesIt: m_cacheContext->componentVerticesSources[QUuid().toString()].map()) { - m_outcome->nodeVertices.push_back(verticesSourcesIt.second); - } - - for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) { - m_outcome->nodes.insert(m_outcome->nodes.end(), - bmeshNodes.second.begin(), bmeshNodes.second.end()); - } - - int triangulatedFinalMeshId = resultMeshId; - if (triangulatedFinalMeshId > 0) { - if (m_weldEnabled) { - PositionMap excludePositions; - for (auto it = m_cacheContext->partBmeshVertices.begin(); it != m_cacheContext->partBmeshVertices.end(); ++it) { - for (const auto &bmeshVertex: it->second) { - excludePositions.addPosition(bmeshVertex.first.x(), bmeshVertex.first.y(), bmeshVertex.first.z(), true); - } - } - int totalAffectedNum = 0; - int affectedNum = 0; - int weldedMeshId = triangulatedFinalMeshId; - do { - affectedNum = 0; - weldedMeshId = meshWeldSeam(m_meshliteContext, weldedMeshId, 0.025, excludePositions, &affectedNum); - if (weldedMeshId <= 0) - break; - triangulatedFinalMeshId = weldedMeshId; - totalAffectedNum += affectedNum; - } while (affectedNum > 0); - qDebug() << "Total weld affected triangles:" << totalAffectedNum; + combinedMesh->fetch(combinedVertices, combinedFaces); + + size_t totalAffectedNum = 0; + size_t affectedNum = 0; + do { + std::vector weldedVertices; + std::vector> weldedFaces; + affectedNum = nodemesh::weldSeam(combinedVertices, combinedFaces, + 0.025, componentCache.noneSeamVertices, + weldedVertices, weldedFaces); + combinedVertices = weldedVertices; + combinedFaces = weldedFaces; + totalAffectedNum += affectedNum; + } while (affectedNum > 0); + qDebug() << "Total weld affected triangles:" << totalAffectedNum; + + std::vector combinedFacesNormals; + for (const auto &face: combinedFaces) { + combinedFacesNormals.push_back(QVector3D::normal( + combinedVertices[face[0]], + combinedVertices[face[1]], + combinedVertices[face[2]] + )); } - std::set> sharedQuadEdges; - for (const auto &bmeshQuads: m_cacheContext->partBmeshQuads) { - for (const auto &quad: bmeshQuads.second) { - sharedQuadEdges.insert(std::make_pair(std::get<0>(quad), std::get<2>(quad))); - sharedQuadEdges.insert(std::make_pair(std::get<1>(quad), std::get<3>(quad))); - } - } - if (!sharedQuadEdges.empty()) { - resultMeshId = meshQuadify(m_meshliteContext, triangulatedFinalMeshId, sharedQuadEdges, m_forMakePositionKey); - } - } - - if (resultMeshId > 0) { - loadGeneratedPositionsToOutcome(m_meshliteContext, triangulatedFinalMeshId); + recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads); + + m_outcome->nodes = componentCache.outcomeNodes; + m_outcome->nodeVertices = componentCache.outcomeNodeVertices; + m_outcome->vertices = combinedVertices; + m_outcome->triangles = combinedFaces; + m_outcome->triangleNormals = combinedFacesNormals; + std::vector> sourceNodes; triangleSourceNodeResolve(*m_outcome, sourceNodes); m_outcome->setTriangleSourceNodes(sourceNodes); - std::map, QColor> sourceNodeToColorMap; - for (const auto &node: m_outcome->nodes) - sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color}); + std::vector> triangleVertexNormals; + generateSmoothTriangleVertexNormals(combinedVertices, + combinedFaces, + combinedFacesNormals, + &triangleVertexNormals); + m_outcome->setTriangleVertexNormals(triangleVertexNormals); - std::vector triangleColors; - triangleColors.resize(m_outcome->triangles.size(), Theme::white); - const std::vector> *triangleSourceNodes = m_outcome->triangleSourceNodes(); - if (nullptr != triangleSourceNodes) { - for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); triangleIndex++) { - const auto &source = (*triangleSourceNodes)[triangleIndex]; - triangleColors[triangleIndex] = sourceNodeToColorMap[source]; - } - } - - m_mesh = new MeshLoader(m_meshliteContext, resultMeshId, triangulatedFinalMeshId, Theme::white, &triangleColors, m_smoothNormal); + m_resultMesh = new MeshLoader(*m_outcome); } - + delete combinedMesh; + if (needDeleteCacheContext) { delete m_cacheContext; m_cacheContext = nullptr; } - meshlite_destroy_context(m_meshliteContext); - qDebug() << "The mesh generation took" << countTimeConsumed.elapsed() << "milliseconds"; } -void MeshGenerator::process() +void MeshGenerator::generateSmoothTriangleVertexNormals(const std::vector &vertices, const std::vector> &triangles, + const std::vector &triangleNormals, + std::vector> *triangleVertexNormals) { - generate(); - - this->moveToThread(QGuiApplication::instance()->thread()); - emit finished(); -} - -void MeshGenerator::checkDirtyFlags() -{ - checkIsComponentDirty(QUuid().toString()); + std::vector smoothNormals; + nodemesh::angleSmooth(vertices, + triangles, + triangleNormals, + 60, + smoothNormals); + triangleVertexNormals->resize(triangles.size(), { + QVector3D(), QVector3D(), QVector3D() + }); + size_t index = 0; + for (size_t i = 0; i < triangles.size(); ++i) { + auto &normals = (*triangleVertexNormals)[i]; + for (size_t j = 0; j < 3; ++j) { + if (index < smoothNormals.size()) + normals[j] = smoothNormals[index]; + ++index; + } + } } diff --git a/src/meshgenerator.h b/src/meshgenerator.h index 33ee159a..d5f13581 100644 --- a/src/meshgenerator.h +++ b/src/meshgenerator.h @@ -1,31 +1,49 @@ #ifndef DUST3D_MESH_GENERATOR_H #define DUST3D_MESH_GENERATOR_H #include -#include -#include -#include #include -#include -#include -#include "snapshot.h" -#include "meshloader.h" +#include +#include +#include #include "outcome.h" -#include "positionmap.h" +#include "snapshot.h" #include "combinemode.h" +#include "meshloader.h" + +class GeneratedPart +{ +public: + ~GeneratedPart() + { + delete mesh; + }; + nodemesh::Combiner::Mesh *mesh = nullptr; + std::vector vertices; + std::vector> faces; + std::vector outcomeNodes; + std::vector>> outcomeNodeVertices; +}; + +class GeneratedComponent +{ +public: + ~GeneratedComponent() + { + delete mesh; + }; + nodemesh::Combiner::Mesh *mesh = nullptr; + std::set> sharedQuadEdges; + std::set noneSeamVertices; + std::vector outcomeNodes; + std::vector>> outcomeNodeVertices; +}; class GeneratedCacheContext { public: - ~GeneratedCacheContext(); - std::map>>> partBmeshVertices; - std::map> partBmeshNodes; - std::map>> partBmeshQuads; - std::map componentCombinableMeshs; - std::map> componentPositions; - std::map>>> componentVerticesSources; + std::map components; + std::map parts; std::map partMirrorIdMap; - std::map>> componentInflateBalls; - void updateComponentCombinableMesh(QString componentId, void *mesh); }; class MeshGenerator : public QObject @@ -34,56 +52,51 @@ class MeshGenerator : public QObject public: MeshGenerator(Snapshot *snapshot); ~MeshGenerator(); - void setSharedContextWidget(QOpenGLWidget *widget); - void addPartPreviewRequirement(const QUuid &partId); - void setGeneratedCacheContext(GeneratedCacheContext *cacheContext); - void setSmoothNormal(bool smoothNormal); - void setWeldEnabled(bool weldEnabled); bool isSucceed(); MeshLoader *takeResultMesh(); MeshLoader *takePartPreviewMesh(const QUuid &partId); - const std::set &requirePreviewPartIds(); const std::set &generatedPreviewPartIds(); Outcome *takeOutcome(); void generate(); + void setGeneratedCacheContext(GeneratedCacheContext *cacheContext); signals: void finished(); public slots: void process(); + private: - Snapshot *m_snapshot; - bool m_isSucceed; - MeshLoader *m_mesh; - std::map m_partPreviewMeshMap; - std::set m_requirePreviewPartIds; - std::set m_generatedPreviewPartIds; - QThread *m_thread; - Outcome *m_outcome; - QOpenGLWidget *m_sharedContextWidget; - void *m_meshliteContext; - GeneratedCacheContext *m_cacheContext; - bool m_smoothNormal; - bool m_weldEnabled; - float m_mainProfileMiddleX; - float m_sideProfileMiddleX; - float m_mainProfileMiddleY; - std::map> m_partNodeIds; - std::map> m_partEdgeIds; + QColor m_defaultPartColor = Qt::white; + Snapshot *m_snapshot = nullptr; + GeneratedCacheContext *m_cacheContext = nullptr; std::set m_dirtyComponentIds; std::set m_dirtyPartIds; -private: - static bool m_enableDebug; - static PositionMap *m_forMakePositionKey; -private: - void loadVertexSources(void *meshliteContext, int meshId, QUuid partId, const std::map &bmeshToNodeIdMap, std::vector>> &bmeshVertices, - std::vector> &bmeshQuads); - void loadGeneratedPositionsToOutcome(void *meshliteContext, int triangulatedMeshId); + float m_mainProfileMiddleX = 0; + float m_sideProfileMiddleX = 0; + float m_mainProfileMiddleY = 0; + Outcome *m_outcome = nullptr; + std::map> m_partNodeIds; + std::map> m_partEdgeIds; + std::set m_generatedPreviewPartIds; + MeshLoader *m_resultMesh = nullptr; + std::map m_partPreviewMeshes; + bool m_isSucceed = false; + bool m_cacheEnabled = false; + void collectParts(); + bool checkIsComponentDirty(const QString &componentIdString); + bool checkIsPartDirty(const QString &partIdString); void checkDirtyFlags(); - bool checkIsComponentDirty(QString componentId); - bool checkIsPartDirty(QString partId); - void *combineComponentMesh(QString componentId, CombineMode *combineMode, std::vector> *inflateBalls); - void *combinePartMesh(QString partId, std::vector> *balls=nullptr); + nodemesh::Combiner::Mesh *combinePartMesh(const QString &partIdString); + nodemesh::Combiner::Mesh *combineComponentMesh(const QString &componentIdString, CombineMode *combineMode); + void makeXmirror(const std::vector &sourceVertices, const std::vector> &sourceFaces, + std::vector *destVertices, std::vector> *destFaces); + void collectSharedQuadEdges(const std::vector &vertices, const std::vector> &faces, + std::set> *sharedQuadEdges); + nodemesh::Combiner::Mesh *combineTwoMeshes(const nodemesh::Combiner::Mesh &first, const nodemesh::Combiner::Mesh &second, + nodemesh::Combiner::Method method); + void generateSmoothTriangleVertexNormals(const std::vector &vertices, const std::vector> &triangles, + const std::vector &triangleNormals, + std::vector> *triangleVertexNormals); }; #endif diff --git a/src/meshinflate.cpp b/src/meshinflate.cpp deleted file mode 100644 index 51edca53..00000000 --- a/src/meshinflate.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include "meshinflate.h" -#include "meshutil.h" - -void *meshInflate(void *combinableMesh, const std::vector> &inflateBalls, std::vector> &inflatedVertices) -{ - std::vector oldPositions; - std::vector newPositions; - std::vector> faces; - std::set changedVertices; - - loadCombinableMeshVerticesPositionsAndFacesIndices(combinableMesh, oldPositions, faces); - newPositions = oldPositions; - - std::vector inflateBallRadius2s(inflateBalls.size()); - for (size_t i = 0; i < inflateBalls.size(); ++i) { - const auto &radius = inflateBalls[i].second; - inflateBallRadius2s[i] = radius * radius; - } - - for (size_t i = 0; i < oldPositions.size(); ++i) { - const auto &oldPos = oldPositions[i]; - QVector3D sumOfNewPos; - size_t pushedByBallNum = 0; - for (size_t j = 0; j < inflateBalls.size(); ++j) { - const auto &ball = inflateBalls[j]; - const auto &ballPos = ball.first; - const auto &ballRadius = std::max(ball.second, (float)0.00001); - const auto ballToPosVec = oldPos - ballPos; - if (ballToPosVec.lengthSquared() > inflateBallRadius2s[j]) - continue; - const auto distance = 1.0 - ballToPosVec.length() / ballRadius; - const auto smoothedDist = 3.0 * std::pow(distance, 2) - 2.0 * std::pow(distance, 3); - const auto newPosPushByBall = oldPos + ballToPosVec * smoothedDist; - sumOfNewPos += newPosPushByBall; - ++pushedByBallNum; - } - if (0 == pushedByBallNum) - continue; - newPositions[i] = sumOfNewPos / pushedByBallNum; - changedVertices.insert(i); - } - - for (const auto &index: changedVertices) { - inflatedVertices.push_back({oldPositions[index], newPositions[index]}); - } - - return buildCombinableMeshFromVerticesPositionsAndFacesIndices(newPositions, faces); -} diff --git a/src/meshinflate.h b/src/meshinflate.h deleted file mode 100644 index 9e901ed5..00000000 --- a/src/meshinflate.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef DUST3D_MESH_INFLATE_H -#define DUST3D_MESH_INFLATE_H -#include -#include - -void *meshInflate(void *combinableMesh, const std::vector> &inflateBalls, std::vector> &inflatedVertices); - -#endif diff --git a/src/meshloader.cpp b/src/meshloader.cpp index 859a64b6..50f801ec 100644 --- a/src/meshloader.cpp +++ b/src/meshloader.cpp @@ -3,194 +3,13 @@ #include #include #include "meshloader.h" -#include "meshlite.h" -#include "theme.h" -#include "positionmap.h" #include "ds3file.h" -#include "anglesmooth.h" #define MAX_VERTICES_PER_FACE 100 float MeshLoader::m_defaultMetalness = 0.0; float MeshLoader::m_defaultRoughness = 1.0; -MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, QColor defaultColor, const std::vector *triangleColors, bool smoothNormal) : - m_triangleVertices(nullptr), - m_triangleVertexCount(0), - m_edgeVertices(nullptr), - m_edgeVertexCount(0), - m_textureImage(nullptr) -{ - int edgeVertexPositionCount = meshlite_get_vertex_count(meshlite, meshId); - GLfloat *edgeVertexPositions = new GLfloat[edgeVertexPositionCount * 3]; - int loadedEdgeVertexPositionItemCount = meshlite_get_vertex_position_array(meshlite, meshId, edgeVertexPositions, edgeVertexPositionCount * 3); - - int offset = 0; - while (offset < loadedEdgeVertexPositionItemCount) { - QVector3D position = QVector3D(edgeVertexPositions[offset], edgeVertexPositions[offset + 1], edgeVertexPositions[offset + 2]); - m_vertices.push_back(position); - offset += 3; - } - int faceCount = meshlite_get_face_count(meshlite, meshId); - int *faceVertexNumAndIndices = new int[faceCount * (1 + MAX_VERTICES_PER_FACE)]; - int loadedFaceVertexNumAndIndicesItemCount = meshlite_get_face_index_array(meshlite, meshId, faceVertexNumAndIndices, faceCount * (1 + MAX_VERTICES_PER_FACE)); - offset = 0; - while (offset < loadedFaceVertexNumAndIndicesItemCount) { - int indicesNum = faceVertexNumAndIndices[offset++]; - assert(indicesNum >= 0 && indicesNum <= MAX_VERTICES_PER_FACE); - std::vector face; - for (int i = 0; i < indicesNum && offset < loadedFaceVertexNumAndIndicesItemCount; i++) { - int index = faceVertexNumAndIndices[offset++]; - assert(index >= 0 && index < loadedEdgeVertexPositionItemCount); - face.push_back(index); - } - m_faces.push_back(face); - } - delete[] faceVertexNumAndIndices; - faceVertexNumAndIndices = NULL; - - int edgeCount = meshlite_get_halfedge_count(meshlite, meshId); - int *edgeIndices = new int[edgeCount * 2]; - int loadedEdgeVertexIndexItemCount = meshlite_get_halfedge_index_array(meshlite, meshId, edgeIndices, edgeCount * 2); - GLfloat *edgeNormals = new GLfloat[edgeCount * 3]; - int loadedEdgeNormalItemCount = meshlite_get_halfedge_normal_array(meshlite, meshId, edgeNormals, edgeCount * 3); - - m_edgeVertexCount = edgeCount * 2; - m_edgeVertices = new Vertex[m_edgeVertexCount * 3]; - for (int i = 0; i < edgeCount; i++) { - int firstIndex = i * 2; - for (int j = 0; j < 2; j++) { - assert(firstIndex + j < loadedEdgeVertexIndexItemCount); - int posIndex = edgeIndices[firstIndex + j] * 3; - assert(posIndex < loadedEdgeVertexPositionItemCount); - Vertex *v = &m_edgeVertices[firstIndex + j]; - v->posX = edgeVertexPositions[posIndex + 0]; - v->posY = edgeVertexPositions[posIndex + 1]; - v->posZ = edgeVertexPositions[posIndex + 2]; - assert(firstIndex + 2 < loadedEdgeNormalItemCount); - v->normX = edgeNormals[firstIndex + 0]; - v->normY = edgeNormals[firstIndex + 1]; - v->normZ = edgeNormals[firstIndex + 2]; - v->colorR = 0.0; - v->colorG = 0.0; - v->colorB = 0.0; - v->texU = 0.0; - v->texV = 0.0; - v->metalness = 0; - v->roughness = 1.0; - } - } - - int triangleMesh = -1 == triangulatedMeshId ? meshlite_triangulate(meshlite, meshId) : triangulatedMeshId; - - int triangleVertexPositionCount = meshlite_get_vertex_count(meshlite, triangleMesh); - GLfloat *triangleVertexPositions = new GLfloat[triangleVertexPositionCount * 3]; - int loadedTriangleVertexPositionItemCount = meshlite_get_vertex_position_array(meshlite, triangleMesh, triangleVertexPositions, triangleVertexPositionCount * 3); - - GLfloat *triangleVertexSmoothNormals = new GLfloat[triangleVertexPositionCount * 3]; - if (smoothNormal) - memset(triangleVertexSmoothNormals, 0, sizeof(GLfloat) * triangleVertexPositionCount * 3); - - offset = 0; - while (offset < loadedTriangleVertexPositionItemCount) { - QVector3D position = QVector3D(triangleVertexPositions[offset], triangleVertexPositions[offset + 1], triangleVertexPositions[offset + 2]); - m_triangulatedVertices.push_back(position); - offset += 3; - } - - int triangleCount = meshlite_get_face_count(meshlite, triangleMesh); - int *triangleIndices = new int[triangleCount * 3]; - int loadedTriangleVertexIndexItemCount = meshlite_get_triangle_index_array(meshlite, triangleMesh, triangleIndices, triangleCount * 3); - - GLfloat *triangleNormals = new GLfloat[triangleCount * 3]; - int loadedTriangleNormalItemCount = meshlite_get_triangle_normal_array(meshlite, triangleMesh, triangleNormals, triangleCount * 3); - - float modelR = defaultColor.redF(); - float modelG = defaultColor.greenF(); - float modelB = defaultColor.blueF(); - m_triangleVertexCount = triangleCount * 3; - m_triangleVertices = new Vertex[m_triangleVertexCount * 3]; - const std::vector &inputVerticesForSmoothAngle = m_triangulatedVertices; - std::vector> inputTrianglesForSmoothAngle; - std::vector inputNormalsForSmoothAngle; - float thresholdAngleDegrees = 60; - for (int i = 0; i < triangleCount; i++) { - int firstIndex = i * 3; - float useColorR = modelR; - float useColorG = modelG; - float useColorB = modelB; - float useMetalness = m_defaultMetalness; - float useRoughness = m_defaultRoughness; - if (nullptr != triangleColors && i < (int)triangleColors->size()) { - const auto &triangleColor = (*triangleColors)[i]; - useColorR = triangleColor.redF(); - useColorG = triangleColor.greenF(); - useColorB = triangleColor.blueF(); - } - TriangulatedFace triangulatedFace; - triangulatedFace.color.setRedF(useColorR); - triangulatedFace.color.setGreenF(useColorG); - triangulatedFace.color.setBlueF(useColorB); - QVector3D positions[3]; - if (smoothNormal) { - inputTrianglesForSmoothAngle.push_back(std::make_tuple((size_t)triangleIndices[firstIndex + 0], - (size_t)triangleIndices[firstIndex + 1], - (size_t)triangleIndices[firstIndex + 2] - )); - inputNormalsForSmoothAngle.push_back(QVector3D(triangleNormals[firstIndex + 0], - triangleNormals[firstIndex + 1], - triangleNormals[firstIndex + 2])); - } - for (int j = 0; j < 3; j++) { - assert(firstIndex + j < loadedTriangleVertexIndexItemCount); - int posIndex = triangleIndices[firstIndex + j] * 3; - assert(posIndex < loadedTriangleVertexPositionItemCount); - triangulatedFace.indices[j] = triangleIndices[firstIndex + j]; - Vertex *v = &m_triangleVertices[firstIndex + j]; - v->posX = triangleVertexPositions[posIndex + 0]; - v->posY = triangleVertexPositions[posIndex + 1]; - v->posZ = triangleVertexPositions[posIndex + 2]; - assert(firstIndex + 2 < loadedTriangleNormalItemCount); - v->normX = triangleNormals[firstIndex + 0]; - v->normY = triangleNormals[firstIndex + 1]; - v->normZ = triangleNormals[firstIndex + 2]; - v->colorR = useColorR; - v->colorG = useColorG; - v->colorB = useColorB; - v->metalness = useMetalness; - v->roughness = useRoughness; - } - m_triangulatedFaces.push_back(triangulatedFace); - } - - if (smoothNormal) { - std::vector resultNormals; - angleSmooth(inputVerticesForSmoothAngle, inputTrianglesForSmoothAngle, inputNormalsForSmoothAngle, - thresholdAngleDegrees, resultNormals); - size_t normalIndex = 0; - for (int i = 0; i < triangleCount; i++) { - int firstIndex = i * 3; - for (int j = 0; j < 3; j++) { - assert(firstIndex + j < loadedTriangleVertexIndexItemCount); - Vertex *v = &m_triangleVertices[firstIndex + j]; - const auto &normal = resultNormals[normalIndex++]; - v->normX = normal.x(); - v->normY = normal.y(); - v->normZ = normal.z(); - } - } - } - - delete[] triangleVertexPositions; - delete[] triangleVertexSmoothNormals; - delete[] triangleIndices; - delete[] triangleNormals; - - delete[] edgeVertexPositions; - delete[] edgeIndices; - delete[] edgeNormals; -} - MeshLoader::MeshLoader(const MeshLoader &mesh) : m_triangleVertices(nullptr), m_triangleVertexCount(0), @@ -239,6 +58,40 @@ MeshLoader::MeshLoader(Vertex *triangleVertices, int vertexNum) : { } +MeshLoader::MeshLoader(const std::vector &vertices, const std::vector> &triangles, + const std::vector> &triangleVertexNormals, + const QColor &color) +{ + m_triangleVertexCount = triangles.size() * 3; + m_triangleVertices = new Vertex[m_triangleVertexCount]; + int destIndex = 0; + for (size_t i = 0; i < triangles.size(); ++i) { + for (auto j = 0; j < 3; j++) { + int vertexIndex = triangles[i][j]; + const QVector3D *srcVert = &vertices[vertexIndex]; + const QVector3D *srcNormal = &(triangleVertexNormals)[i][j]; + Vertex *dest = &m_triangleVertices[destIndex]; + dest->colorR = color.redF(); + dest->colorG = color.greenF(); + dest->colorB = color.blueF(); + dest->posX = srcVert->x(); + dest->posY = srcVert->y(); + dest->posZ = srcVert->z(); + dest->texU = 0; + dest->texV = 0; + dest->normX = srcNormal->x(); + dest->normY = srcNormal->y(); + dest->normZ = srcNormal->z(); + dest->metalness = m_defaultMetalness; + dest->roughness = m_defaultRoughness; + dest->tangentX = 0; + dest->tangentY = 0; + dest->tangentZ = 0; + destIndex++; + } + } +} + MeshLoader::MeshLoader(Outcome &outcome) : m_triangleVertices(nullptr), m_triangleVertexCount(0), @@ -246,6 +99,9 @@ MeshLoader::MeshLoader(Outcome &outcome) : m_edgeVertexCount(0), m_textureImage(nullptr) { + m_vertices = outcome.vertices; + m_faces = outcome.triangleAndQuads; + m_triangleVertexCount = outcome.triangles.size() * 3; m_triangleVertices = new Vertex[m_triangleVertexCount]; int destIndex = 0; @@ -315,7 +171,7 @@ const std::vector &MeshLoader::vertices() return m_vertices; } -const std::vector> &MeshLoader::faces() +const std::vector> &MeshLoader::faces() { return m_faces; } @@ -419,9 +275,9 @@ void MeshLoader::exportAsObj(const QString &filename) for (std::vector::const_iterator it = vertices().begin() ; it != vertices().end(); ++it) { stream << "v " << (*it).x() << " " << (*it).y() << " " << (*it).z() << endl; } - for (std::vector>::const_iterator it = faces().begin() ; it != faces().end(); ++it) { + for (std::vector>::const_iterator it = faces().begin() ; it != faces().end(); ++it) { stream << "f"; - for (std::vector::const_iterator subIt = (*it).begin() ; subIt != (*it).end(); ++subIt) { + for (std::vector::const_iterator subIt = (*it).begin() ; subIt != (*it).end(); ++subIt) { stream << " " << (1 + *subIt); } stream << endl; diff --git a/src/meshloader.h b/src/meshloader.h index adf250fe..df029f80 100644 --- a/src/meshloader.h +++ b/src/meshloader.h @@ -6,8 +6,6 @@ #include #include #include -#include "positionmap.h" -#include "theme.h" #include "outcome.h" #pragma pack(push) @@ -42,7 +40,9 @@ struct TriangulatedFace class MeshLoader { public: - MeshLoader(void *meshlite, int meshId, int triangulatedMeshId=-1, QColor defaultColor=Theme::white, const std::vector *triangleColors=nullptr, bool smoothNormal=true); + MeshLoader(const std::vector &vertices, const std::vector> &triangles, + const std::vector> &triangleVertexNormals, + const QColor &color=Qt::white); MeshLoader(Outcome &outcome); MeshLoader(Vertex *triangleVertices, int vertexNum); MeshLoader(const MeshLoader &mesh); @@ -53,7 +53,7 @@ public: Vertex *edgeVertices(); int edgeVertexCount(); const std::vector &vertices(); - const std::vector> &faces(); + const std::vector> &faces(); const std::vector &triangulatedVertices(); const std::vector &triangulatedFaces(); void setTextureImage(QImage *textureImage); @@ -77,7 +77,7 @@ private: Vertex *m_edgeVertices = nullptr; int m_edgeVertexCount = 0; std::vector m_vertices; - std::vector> m_faces; + std::vector> m_faces; std::vector m_triangulatedVertices; std::vector m_triangulatedFaces; QImage *m_textureImage = nullptr; diff --git a/src/meshquadify.cpp b/src/meshquadify.cpp deleted file mode 100644 index 7c7cfa54..00000000 --- a/src/meshquadify.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include "meshquadify.h" -#include "meshlite.h" - -int meshQuadify(void *meshlite, int meshId, const std::set> &sharedQuadEdges, - PositionMap *positionMapForMakeKey) -{ - int vertexCount = meshlite_get_vertex_count(meshlite, meshId); - float *vertexPositions = new float[vertexCount * 3]; - int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3); - int offset = 0; - Q_ASSERT(vertexArrayLen == vertexCount * 3); - std::vector positionKeyMap; - for (int i = 0; i < vertexCount; i++) { - float x = vertexPositions[offset + 0]; - float y = vertexPositions[offset + 1]; - float z = vertexPositions[offset + 2]; - positionKeyMap.push_back(positionMapForMakeKey->makeKey(x, y, z)); - offset += 3; - } - int faceCount = meshlite_get_face_count(meshlite, meshId); - int *faceVertexNumAndIndices = new int[faceCount * MAX_VERTICES_PER_FACE]; - int filledLength = meshlite_get_face_index_array(meshlite, meshId, faceVertexNumAndIndices, faceCount * MAX_VERTICES_PER_FACE); - int i = 0; - std::vector> newFaceIndices; - while (i < filledLength) { - int num = faceVertexNumAndIndices[i++]; - Q_ASSERT(num > 0 && num <= MAX_VERTICES_PER_FACE); - if (num < 3) { - i += num; - continue; - } - std::vector indices; - for (int j = 0; j < num; j++) { - int index = faceVertexNumAndIndices[i++]; - Q_ASSERT(index >= 0 && index < vertexCount); - indices.push_back(index); - } - newFaceIndices.push_back(indices); - } - int quadMesh = 0; - std::map, std::pair> triangleEdgeMap; - for (int i = 0; i < (int)newFaceIndices.size(); i++) { - const auto &faceIndices = newFaceIndices[i]; - if (faceIndices.size() == 3) { - triangleEdgeMap[std::make_pair(faceIndices[0], faceIndices[1])] = std::make_pair(i, faceIndices[2]); - triangleEdgeMap[std::make_pair(faceIndices[1], faceIndices[2])] = std::make_pair(i, faceIndices[0]); - triangleEdgeMap[std::make_pair(faceIndices[2], faceIndices[0])] = std::make_pair(i, faceIndices[1]); - } - } - std::unordered_set unionedFaces; - std::vector> newUnionedFaceIndices; - for (const auto &edge: triangleEdgeMap) { - if (unionedFaces.find(edge.second.first) != unionedFaces.end()) - continue; - auto pair = std::make_pair(positionKeyMap[edge.first.first], positionKeyMap[edge.first.second]); - if (sharedQuadEdges.find(pair) != sharedQuadEdges.end()) { - auto oppositeEdge = triangleEdgeMap.find(std::make_pair(edge.first.second, edge.first.first)); - if (oppositeEdge == triangleEdgeMap.end()) { - qDebug() << "Find opposite edge failed"; - } else { - if (unionedFaces.find(oppositeEdge->second.first) == unionedFaces.end()) { - unionedFaces.insert(edge.second.first); - unionedFaces.insert(oppositeEdge->second.first); - std::vector indices; - indices.push_back(edge.second.second); - indices.push_back(edge.first.first); - indices.push_back(oppositeEdge->second.second); - indices.push_back(edge.first.second); - newUnionedFaceIndices.push_back(indices); - } - } - } - } - std::vector newFaceVertexNumAndIndices; - for (int i = 0; i < (int)newFaceIndices.size(); i++) { - if (unionedFaces.find(i) == unionedFaces.end()) { - const auto &faceIndices = newFaceIndices[i]; - newFaceVertexNumAndIndices.push_back(faceIndices.size()); - for (const auto &index: faceIndices) { - newFaceVertexNumAndIndices.push_back(index); - } - } - } - for (const auto &faceIndices: newUnionedFaceIndices) { - newFaceVertexNumAndIndices.push_back(faceIndices.size()); - for (const auto &index: faceIndices) { - newFaceVertexNumAndIndices.push_back(index); - } - } - quadMesh = meshlite_build(meshlite, vertexPositions, vertexCount, newFaceVertexNumAndIndices.data(), newFaceVertexNumAndIndices.size()); - delete[] faceVertexNumAndIndices; - delete[] vertexPositions; - return quadMesh; -} diff --git a/src/meshquadify.h b/src/meshquadify.h deleted file mode 100644 index 2d90c38d..00000000 --- a/src/meshquadify.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef DUST3D_MESH_QUADIFY_H -#define DUST3D_MESH_QUADIFY_H -#include -#include "meshutil.h" -#include "positionmap.h" - -int meshQuadify(void *meshlite, int meshId, const std::set> &sharedQuadEdges, - PositionMap *positionMapForMakeKey); - -#endif diff --git a/src/meshresultpostprocessor.cpp b/src/meshresultpostprocessor.cpp index 15e60981..90fb1a9f 100644 --- a/src/meshresultpostprocessor.cpp +++ b/src/meshresultpostprocessor.cpp @@ -2,7 +2,6 @@ #include "meshresultpostprocessor.h" #include "uvunwrap.h" #include "triangletangentresolve.h" -#include "anglesmooth.h" MeshResultPostProcessor::MeshResultPostProcessor(const Outcome &outcome) { @@ -37,37 +36,6 @@ void MeshResultPostProcessor::poseProcess() triangleTangentResolve(*m_outcome, triangleTangents); m_outcome->setTriangleTangents(triangleTangents); } - - { - std::vector inputVerticies; - std::vector> inputTriangles; - std::vector inputNormals; - float thresholdAngleDegrees = 60; - for (const auto &vertex: m_outcome->vertices) - inputVerticies.push_back(vertex); - for (size_t i = 0; i < m_outcome->triangles.size(); ++i) { - const auto &triangle = m_outcome->triangles[i]; - const auto &triangleNormal = m_outcome->triangleNormals[i]; - inputTriangles.push_back(std::make_tuple((size_t)triangle[0], (size_t)triangle[1], (size_t)triangle[2])); - inputNormals.push_back(triangleNormal); - } - std::vector resultNormals; - angleSmooth(inputVerticies, inputTriangles, inputNormals, thresholdAngleDegrees, resultNormals); - std::vector> triangleVertexNormals; - triangleVertexNormals.resize(m_outcome->triangles.size(), { - QVector3D(), QVector3D(), QVector3D() - }); - size_t index = 0; - for (size_t i = 0; i < m_outcome->triangles.size(); ++i) { - auto &normals = triangleVertexNormals[i]; - for (size_t j = 0; j < 3; ++j) { - if (index < resultNormals.size()) - normals[j] = resultNormals[index]; - ++index; - } - } - m_outcome->setTriangleVertexNormals(triangleVertexNormals); - } } } diff --git a/src/meshutil.cpp b/src/meshutil.cpp deleted file mode 100644 index 6599fe40..00000000 --- a/src/meshutil.cpp +++ /dev/null @@ -1,530 +0,0 @@ -#include -#include "meshutil.h" -#include "meshlite.h" - -#define USE_CGAL 1 - -#if USE_CGAL == 1 -// Polygon_mesh_processing/corefinement_mesh_union.cpp -// https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2corefinement_mesh_union_8cpp-example.html#a2 -// https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2triangulate_faces_example_8cpp-example.html -// https://github.com/CGAL/cgal/issues/2875 -// https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2hausdorff_distance_remeshing_example_8cpp-example.html#a3 -// https://github.com/CGAL/cgal/blob/master/Subdivision_method_3/examples/Subdivision_method_3/CatmullClark_subdivision.cpp -// https://doc.cgal.org/latest/Polygon_mesh_processing/index.html#title25 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef CGAL::Exact_predicates_exact_constructions_kernel ExactKernel; -typedef CGAL::Simple_cartesian SimpleKernel; -typedef CGAL::Surface_mesh ExactMesh; -typedef CGAL::Surface_mesh SimpleMesh; -namespace PMP = CGAL::Polygon_mesh_processing; - -void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector &positions) -{ - int vertexCount = meshlite_get_vertex_count(meshliteContext, meshId); - float *vertexPositions = new float[vertexCount * 3]; - int vertexArrayLen = meshlite_get_vertex_position_array(meshliteContext, meshId, vertexPositions, vertexCount * 3); - int offset = 0; - assert(vertexArrayLen == vertexCount * 3); - for (int i = 0; i < vertexCount; i++) { - float x = vertexPositions[offset + 0]; - float y = vertexPositions[offset + 1]; - float z = vertexPositions[offset + 2]; - if (std::isnan(x) || std::isinf(x)) - x = 0; - if (std::isnan(y) || std::isinf(y)) - y = 0; - if (std::isnan(z) || std::isinf(z)) - z = 0; - positions.push_back(QVector3D(x, y, z)); - offset += 3; - } - delete[] vertexPositions; -} - -template -typename CGAL::Surface_mesh *buildCgalMesh(const std::vector &positions, const std::vector> &indices) -{ - typename CGAL::Surface_mesh *mesh = new typename CGAL::Surface_mesh; - std::map::Vertex_index> oldToNewMap; - for (int i = 0; i < (int)positions.size(); ++i) { - const auto &pos = positions[i]; - oldToNewMap[i] = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z())); - } - for (const auto &face: indices) { - std::vector::Vertex_index> faceVertexIndices; - for (const auto &index: face) { - faceVertexIndices.push_back(oldToNewMap[index]); - } - mesh->add_face(faceVertexIndices); - } - return mesh; -} - -template -typename CGAL::Surface_mesh *makeCgalMeshFromMeshlite(void *meshlite, int meshId) -{ - typename CGAL::Surface_mesh *mesh = new typename CGAL::Surface_mesh; - int vertexCount = meshlite_get_vertex_count(meshlite, meshId); - float *vertexPositions = new float[vertexCount * 3]; - int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3); - int offset = 0; - assert(vertexArrayLen == vertexCount * 3); - std::vector oldPositions; - std::map::Vertex_index> oldToNewMap; - for (int i = 0; i < vertexCount; i++) { - float x = vertexPositions[offset + 0]; - float y = vertexPositions[offset + 1]; - float z = vertexPositions[offset + 2]; - if (std::isnan(x) || std::isinf(x)) - x = 0; - if (std::isnan(y) || std::isinf(y)) - y = 0; - if (std::isnan(z) || std::isinf(z)) - z = 0; - oldPositions.push_back(QVector3D(x, y, z)); - offset += 3; - } - int faceCount = meshlite_get_face_count(meshlite, meshId); - int *faceVertexNumAndIndices = new int[faceCount * MAX_VERTICES_PER_FACE]; - int filledLength = meshlite_get_face_index_array(meshlite, meshId, faceVertexNumAndIndices, faceCount * MAX_VERTICES_PER_FACE); - int i = 0; - int addedFaceCount = 0; - while (i < filledLength) { - int num = faceVertexNumAndIndices[i++]; - assert(num > 0 && num <= MAX_VERTICES_PER_FACE); - if (num < 3) { - i += num; - continue; - } - std::vector::Vertex_index> faceVertexIndices; - for (int j = 0; j < num; j++) { - int index = faceVertexNumAndIndices[i++]; - assert(index >= 0 && index < vertexCount); - if (oldToNewMap.find(index) == oldToNewMap.end()) { - const auto &pos = oldPositions[index]; - oldToNewMap[index] = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z())); - } - faceVertexIndices.push_back(oldToNewMap[index]); - } - mesh->add_face(faceVertexIndices); - addedFaceCount++; - } - delete[] faceVertexNumAndIndices; - delete[] vertexPositions; - if (addedFaceCount < 3) { - delete mesh; - mesh = nullptr; - } - return mesh; -} - -void loadCombinableMeshVerticesPositions(void *mesh, std::vector &positions) -{ - ExactMesh *exactMesh = (ExactMesh *)mesh; - if (nullptr == exactMesh) - return; - - for (auto vertexIt = exactMesh->vertices_begin(); vertexIt != exactMesh->vertices_end(); vertexIt++) { - auto point = exactMesh->point(*vertexIt); - float x = (float)CGAL::to_double(point.x()); - float y = (float)CGAL::to_double(point.y()); - float z = (float)CGAL::to_double(point.z()); - if (std::isnan(x) || std::isinf(x)) - x = 0; - if (std::isnan(y) || std::isinf(y)) - y = 0; - if (std::isnan(z) || std::isinf(z)) - z = 0; - positions.push_back(QVector3D(x, y, z)); - } -} - -// https://doc.cgal.org/latest/Surface_mesh/index.html#circulators_example -// https://www.cgal.org/FAQ.html -template -int makeMeshliteMeshFromCgal(void *meshlite, typename CGAL::Surface_mesh *mesh) -{ - typename CGAL::Surface_mesh::Vertex_range vertexRange = mesh->vertices(); - int vertexCount = vertexRange.size(); - float *vertexPositions = new float[vertexCount * 3]; - int offset = 0; - typename CGAL::Surface_mesh::Vertex_range::iterator vertexIt; - std::map::Vertex_index, int> vertexIndexMap; - int i = 0; - for (vertexIt = vertexRange.begin(); vertexIt != vertexRange.end(); vertexIt++) { - typename Kernel::Point_3 point = mesh->point(*vertexIt); - float x = (float)CGAL::to_double(point.x()); - float y = (float)CGAL::to_double(point.y()); - float z = (float)CGAL::to_double(point.z()); - if (std::isnan(x) || std::isinf(x)) - x = 0; - if (std::isnan(y) || std::isinf(y)) - y = 0; - if (std::isnan(z) || std::isinf(z)) - z = 0; - vertexPositions[offset++] = x; - vertexPositions[offset++] = y; - vertexPositions[offset++] = z; - //printf("vertexPositions[%d]: %f,%f,%f\n", i, vertexPositions[offset - 3], vertexPositions[offset - 2], vertexPositions[offset - 1]); - vertexIndexMap[*vertexIt] = i; - i++; - } - typename CGAL::Surface_mesh::Face_range faceRage = mesh->faces(); - int faceCount = faceRage.size(); - int *faceVertexNumAndIndices = new int[faceCount * MAX_VERTICES_PER_FACE]; - typename CGAL::Surface_mesh::Face_range::iterator faceIt; - offset = 0; - for (faceIt = faceRage.begin(); faceIt != faceRage.end(); faceIt++) { - CGAL::Vertex_around_face_iterator> vbegin, vend; - std::vector indices; - //printf("face begin\n"); - for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(mesh->halfedge(*faceIt), *mesh); - vbegin != vend; - ++vbegin){ - //printf(" %d", vertexIndexMap[*vbegin]); - indices.push_back(vertexIndexMap[*vbegin]); - } - //printf("\nface end\n"); - faceVertexNumAndIndices[offset++] = indices.size(); - for (int j = 0; j < (int)indices.size(); j++) { - faceVertexNumAndIndices[offset++] = indices[j]; - } - } - int meshId = meshlite_build(meshlite, vertexPositions, vertexCount, faceVertexNumAndIndices, offset); - delete[] vertexPositions; - delete[] faceVertexNumAndIndices; - return meshId; -} - -template -bool precheckForConvexHull(InputIterator first, InputIterator beyond) -{ - typedef typename CGAL::internal::Convex_hull_3::Default_traits_for_Chull_3>::type Traits; - typedef typename Traits::Point_3 Point_3; - typedef std::list Point_3_list; - typedef typename Point_3_list::iterator P3_iterator; - Traits traits; - - Point_3_list points(first, beyond); - if (!(points.size() > 3)) - return false; - - // at least 4 points - typename Traits::Collinear_3 collinear = traits.collinear_3_object(); - typename Traits::Equal_3 equal = traits.equal_3_object(); - - P3_iterator point1_it = points.begin(); - P3_iterator point2_it = points.begin(); - point2_it++; - - // find three that are not collinear - while (point2_it != points.end() && equal(*point1_it,*point2_it)) - ++point2_it; - - if (!(point2_it != points.end())) - return false; - - P3_iterator point3_it = point2_it; - ++point3_it; - - if (!(point3_it != points.end())) - return false; - - while (point3_it != points.end() && collinear(*point1_it,*point2_it,*point3_it)) - ++point3_it; - - if (!(point3_it != points.end())) - return false; - - return true; -} - -template -ExactMesh *makeCgalConvexHullMeshFromMeshlite(void *meshlite, int meshId) -{ - typename CGAL::Surface_mesh *mesh = new typename CGAL::Surface_mesh; - int vertexCount = meshlite_get_vertex_count(meshlite, meshId); - float *vertexPositions = new float[vertexCount * 3]; - int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3); - int offset = 0; - assert(vertexArrayLen == vertexCount * 3); - std::vector points; - for (int i = 0; i < vertexCount; i++) { - float x = vertexPositions[offset + 0]; - float y = vertexPositions[offset + 1]; - float z = vertexPositions[offset + 2]; - if (std::isnan(x) || std::isinf(x)) - x = 0; - if (std::isnan(y) || std::isinf(y)) - y = 0; - if (std::isnan(z) || std::isinf(z)) - z = 0; - points.push_back(typename Kernel::Point_3(x, y, z)); - offset += 3; - } - delete[] vertexPositions; - try { - if (precheckForConvexHull::iterator, Kernel>(points.begin(), points.end())) { - CGAL::convex_hull_3(points.begin(), points.end(), *mesh); - } else { - delete mesh; - return nullptr; - } - } catch (...) { - delete mesh; - return nullptr; - } - return mesh; -} - -ExactMesh *unionCgalMeshs(ExactMesh *first, ExactMesh *second) -{ - ExactMesh *mesh = new ExactMesh; - try { - if (!PMP::corefine_and_compute_union(*first, *second, *mesh)) { - delete mesh; - return nullptr; - } - } catch (...) { - delete mesh; - return nullptr; - } - return mesh; -} - -ExactMesh *diffCgalMeshs(ExactMesh *first, ExactMesh *second) -{ - ExactMesh *mesh = new ExactMesh; - try { - if (!PMP::corefine_and_compute_difference(*first, *second, *mesh)) { - delete mesh; - return nullptr; - } - } catch (...) { - delete mesh; - return nullptr; - } - return mesh; -} - -#endif - -// http://cgal-discuss.949826.n4.nabble.com/Polygon-mesh-processing-corefine-and-compute-difference-td4663104.html -int unionMeshs(void *meshliteContext, const std::vector &meshIds, const std::set &inverseIds, int *errorCount) -{ -#if USE_CGAL == 1 - std::vector externalMeshs; - for (size_t i = 0; i < meshIds.size(); i++) { - int triangledMeshId = meshlite_triangulate(meshliteContext, meshIds[i]); - ExactMesh *mesh = makeCgalMeshFromMeshlite(meshliteContext, triangledMeshId); - if (CGAL::Polygon_mesh_processing::does_self_intersect(*mesh)) { - qDebug() << "CGAL::Polygon_mesh_processing::does_self_intersect:" << i; - if (errorCount) - (*errorCount)++; - delete mesh; - continue; - } - externalMeshs.push_back(mesh); - } - if (externalMeshs.size() > 0) { - ExactMesh *mergedExternalMesh = externalMeshs[0]; - for (size_t i = 1; i < externalMeshs.size(); i++) { - if (!mergedExternalMesh) { - qDebug() << "Last union failed, break with remains unprocessed:" << (externalMeshs.size() - i); - break; - } - ExactMesh *unionedExternalMesh = NULL; - try { - if (inverseIds.find(meshIds[i]) == inverseIds.end()) - unionedExternalMesh = unionCgalMeshs(mergedExternalMesh, externalMeshs[i]); - else - unionedExternalMesh = diffCgalMeshs(mergedExternalMesh, externalMeshs[i]); - } catch (...) { - qDebug() << "unionCgalMeshs throw exception"; - if (errorCount) - (*errorCount)++; - } - delete externalMeshs[i]; - if (unionedExternalMesh) { - delete mergedExternalMesh; - mergedExternalMesh = unionedExternalMesh; - } else { - if (errorCount) - (*errorCount)++; - qDebug() << "unionCgalMeshs failed"; - } - } - if (mergedExternalMesh) { - int mergedMeshId = makeMeshliteMeshFromCgal(meshliteContext, mergedExternalMesh); - if (mergedMeshId) - mergedMeshId = fixMeshHoles(meshliteContext, mergedMeshId); - else { - if (errorCount) - (*errorCount)++; - qDebug() << "makeMeshliteMeshFromCgal failed"; - } - delete mergedExternalMesh; - return mergedMeshId; - } - } -#else - return mergeMeshs(meshliteContext, meshIds); -#endif - return 0; -} - -int mergeMeshs(void *meshliteContext, const std::vector &meshIds) -{ - int mergedMeshId = meshIds[0]; - for (size_t i = 1; i < meshIds.size(); i++) { - mergedMeshId = meshlite_merge(meshliteContext, mergedMeshId, meshIds[i]); - } - return mergedMeshId; -} - -int subdivMesh(void *meshliteContext, int meshId, int *errorCount) -{ - int triangulatedMeshId = meshlite_triangulate(meshliteContext, meshId); - if (0 == meshlite_is_triangulated_manifold(meshliteContext, triangulatedMeshId)) { -#if USE_CGAL == 1 - int subdiviedMeshId = 0; - SimpleMesh *simpleMesh = nullptr; - try { - simpleMesh = makeCgalMeshFromMeshlite(meshliteContext, triangulatedMeshId); - CGAL::Subdivision_method_3::CatmullClark_subdivision(*simpleMesh, CGAL::Polygon_mesh_processing::parameters::number_of_iterations(1)); - subdiviedMeshId = makeMeshliteMeshFromCgal(meshliteContext, simpleMesh); - } catch (...) { - if (errorCount) - (*errorCount)++; - } - if (simpleMesh) - delete simpleMesh; - if (subdiviedMeshId) - subdiviedMeshId = fixMeshHoles(meshliteContext, subdiviedMeshId); - return subdiviedMeshId; -#endif - } - return meshlite_subdivide(meshliteContext, meshId); -} - -int fixMeshHoles(void *meshliteContext, int meshId) -{ - return meshlite_fix_hole(meshliteContext, meshId); -} - -void initMeshUtils() -{ - CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); -} - -void *convertToCombinableMesh(void *meshliteContext, int meshId) -{ - ExactMesh *mesh = nullptr; - if (0 == meshId) - return nullptr; - mesh = makeCgalMeshFromMeshlite(meshliteContext, meshId); - if (nullptr == mesh) { - return mesh; - } - if (CGAL::Polygon_mesh_processing::does_self_intersect(*mesh)) { - qDebug() << "CGAL::Polygon_mesh_processing::does_self_intersect meshId:" << meshId; - delete mesh; - return nullptr; - } - return (void *)mesh; -} - -void *unionCombinableMeshs(void *first, void *second) -{ - return (void *)unionCgalMeshs((ExactMesh *)first, (ExactMesh *)second); -} - -void *diffCombinableMeshs(void *first, void *second) -{ - return (void *)diffCgalMeshs((ExactMesh *)first, (ExactMesh *)second); -} - -int convertFromCombinableMesh(void *meshliteContext, void *mesh) -{ - return makeMeshliteMeshFromCgal(meshliteContext, (ExactMesh *)mesh); -} - -void deleteCombinableMesh(void *mesh) -{ - delete (ExactMesh *)mesh; -} - -void *cloneCombinableMesh(void *mesh) -{ - if (nullptr == mesh) - return nullptr; - return (void *)new ExactMesh(*(ExactMesh *)mesh); -} - -void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId) -{ - ExactMesh *mesh = makeCgalConvexHullMeshFromMeshlite(meshliteContext, meshId); - return (void *)mesh; -} - -void loadCombinableMeshVerticesPositionsAndFacesIndices(void *mesh, std::vector &positions, std::vector> &indices) -{ - ExactMesh *exactMesh = (ExactMesh *)mesh; - if (nullptr == exactMesh) - return; - - for (auto vertexIt = exactMesh->vertices_begin(); vertexIt != exactMesh->vertices_end(); vertexIt++) { - auto point = exactMesh->point(*vertexIt); - float x = (float)CGAL::to_double(point.x()); - float y = (float)CGAL::to_double(point.y()); - float z = (float)CGAL::to_double(point.z()); - if (std::isnan(x) || std::isinf(x)) - x = 0; - if (std::isnan(y) || std::isinf(y)) - y = 0; - if (std::isnan(z) || std::isinf(z)) - z = 0; - positions.push_back(QVector3D(x, y, z)); - } - - typename CGAL::Surface_mesh::Face_range faceRage = exactMesh->faces(); - typename CGAL::Surface_mesh::Face_range::iterator faceIt; - for (faceIt = faceRage.begin(); faceIt != faceRage.end(); faceIt++) { - CGAL::Vertex_around_face_iterator> vbegin, vend; - std::vector faceIndices; - for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(exactMesh->halfedge(*faceIt), *exactMesh); - vbegin != vend; - ++vbegin){ - faceIndices.push_back(*vbegin); - } - indices.push_back(faceIndices); - } -} - -void *buildCombinableMeshFromVerticesPositionsAndFacesIndices(const std::vector &positions, const std::vector> &indices) -{ - ExactMesh *mesh = nullptr; - if (indices.empty()) - return nullptr; - mesh = buildCgalMesh(positions, indices); - if (nullptr == mesh) { - return mesh; - } - if (CGAL::Polygon_mesh_processing::does_self_intersect(*mesh)) { - qDebug() << "CGAL::Polygon_mesh_processing::does_self_intersect"; - delete mesh; - return nullptr; - } - return (void *)mesh; -} diff --git a/src/meshutil.h b/src/meshutil.h deleted file mode 100644 index 7a144a2d..00000000 --- a/src/meshutil.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef DUST3D_MESH_UTIL_H -#define DUST3D_MESH_UTIL_H -#include -#include -#include - -#define MAX_VERTICES_PER_FACE 100 - -int mergeMeshs(void *meshliteContext, const std::vector &meshIds); -int unionMeshs(void *meshliteContext, const std::vector &meshIds, const std::set &inverseIds, int *errorCount=0); -int subdivMesh(void *meshliteContext, int meshId, int *errorCount=0); -int fixMeshHoles(void *meshliteContext, int meshId); - -void initMeshUtils(); -void *convertToCombinableMesh(void *meshliteContext, int meshId); -void *unionCombinableMeshs(void *first, void *second); -void *diffCombinableMeshs(void *first, void *second); -int convertFromCombinableMesh(void *meshliteContext, void *mesh); -void deleteCombinableMesh(void *mesh); -void *cloneCombinableMesh(void *mesh); -void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId); -void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector &positions); -void loadCombinableMeshVerticesPositions(void *mesh, std::vector &positions); -void loadCombinableMeshVerticesPositionsAndFacesIndices(void *mesh, std::vector &positions, std::vector> &indices); -void *buildCombinableMeshFromVerticesPositionsAndFacesIndices(const std::vector &positions, const std::vector> &indices); - -#endif diff --git a/src/meshweldseam.cpp b/src/meshweldseam.cpp deleted file mode 100644 index d0bcb6cd..00000000 --- a/src/meshweldseam.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "meshweldseam.h" -#include "meshutil.h" - -int meshWeldSeam(void *meshlite, int meshId, float allowedSmallestDistance, const PositionMap &excludePositions, int *affectedNum) -{ - int vertexCount = meshlite_get_vertex_count(meshlite, meshId); - float *vertexPositions = new float[vertexCount * 3]; - int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3); - int offset = 0; - Q_ASSERT(vertexArrayLen == vertexCount * 3); - std::vector positions; - std::unordered_set excludeVertices; - for (int i = 0; i < vertexCount; i++) { - float x = vertexPositions[offset + 0]; - float y = vertexPositions[offset + 1]; - float z = vertexPositions[offset + 2]; - auto position = QVector3D(x, y, z); - if (excludePositions.findPosition(x, y, z)) - excludeVertices.insert(i); - positions.push_back(position); - offset += 3; - } - int faceCount = meshlite_get_face_count(meshlite, meshId); - int *faceVertexNumAndIndices = new int[faceCount * MAX_VERTICES_PER_FACE]; - int filledLength = meshlite_get_face_index_array(meshlite, meshId, faceVertexNumAndIndices, faceCount * MAX_VERTICES_PER_FACE); - int i = 0; - std::vector> newFaceIndices; - while (i < filledLength) { - int num = faceVertexNumAndIndices[i++]; - Q_ASSERT(num > 0 && num <= MAX_VERTICES_PER_FACE); - if (num < 3) { - i += num; - continue; - } - std::vector indices; - for (int j = 0; j < num; j++) { - int index = faceVertexNumAndIndices[i++]; - Q_ASSERT(index >= 0 && index < vertexCount); - indices.push_back(index); - } - newFaceIndices.push_back(indices); - } - float squareOfAllowedSmallestDistance = allowedSmallestDistance * allowedSmallestDistance; - int weldedMesh = 0; - std::map weldVertexToMap; - std::unordered_set weldTargetVertices; - std::unordered_set processedFaces; - std::map, std::pair> triangleEdgeMap; - std::unordered_map vertexAdjFaceCountMap; - for (int i = 0; i < (int)newFaceIndices.size(); i++) { - const auto &faceIndices = newFaceIndices[i]; - if (faceIndices.size() == 3) { - vertexAdjFaceCountMap[faceIndices[0]]++; - vertexAdjFaceCountMap[faceIndices[1]]++; - vertexAdjFaceCountMap[faceIndices[2]]++; - triangleEdgeMap[std::make_pair(faceIndices[0], faceIndices[1])] = std::make_pair(i, faceIndices[2]); - triangleEdgeMap[std::make_pair(faceIndices[1], faceIndices[2])] = std::make_pair(i, faceIndices[0]); - triangleEdgeMap[std::make_pair(faceIndices[2], faceIndices[0])] = std::make_pair(i, faceIndices[1]); - } - } - for (int i = 0; i < (int)newFaceIndices.size(); i++) { - if (processedFaces.find(i) != processedFaces.end()) - continue; - const auto &faceIndices = newFaceIndices[i]; - if (faceIndices.size() == 3) { - bool indicesSeamCheck[3] = { - excludeVertices.find(faceIndices[0]) == excludeVertices.end(), - excludeVertices.find(faceIndices[1]) == excludeVertices.end(), - excludeVertices.find(faceIndices[2]) == excludeVertices.end() - }; - for (int j = 0; j < 3; j++) { - int next = (j + 1) % 3; - int nextNext = (j + 2) % 3; - if (indicesSeamCheck[j] && indicesSeamCheck[next]) { - std::pair edge = std::make_pair(faceIndices[j], faceIndices[next]); - int thirdVertexIndex = faceIndices[nextNext]; - if ((positions[edge.first] - positions[edge.second]).lengthSquared() < squareOfAllowedSmallestDistance) { - auto oppositeEdge = std::make_pair(edge.second, edge.first); - auto findOppositeFace = triangleEdgeMap.find(oppositeEdge); - if (findOppositeFace == triangleEdgeMap.end()) { - qDebug() << "Find opposite edge failed"; - continue; - } - int oppositeFaceIndex = findOppositeFace->second.first; - if (((positions[edge.first] - positions[thirdVertexIndex]).lengthSquared() < - (positions[edge.second] - positions[thirdVertexIndex]).lengthSquared()) && - vertexAdjFaceCountMap[edge.second] <= 4 && - weldVertexToMap.find(edge.second) == weldVertexToMap.end()) { - weldVertexToMap[edge.second] = edge.first; - weldTargetVertices.insert(edge.first); - processedFaces.insert(i); - processedFaces.insert(oppositeFaceIndex); - break; - } else if (vertexAdjFaceCountMap[edge.first] <= 4 && - weldVertexToMap.find(edge.first) == weldVertexToMap.end()) { - weldVertexToMap[edge.first] = edge.second; - weldTargetVertices.insert(edge.second); - processedFaces.insert(i); - processedFaces.insert(oppositeFaceIndex); - break; - } - } - } - } - } - } - std::vector newFaceVertexNumAndIndices; - int weldedCount = 0; - int faceCountAfterWeld = 0; - for (int i = 0; i < (int)newFaceIndices.size(); i++) { - const auto &faceIndices = newFaceIndices[i]; - std::vector mappedFaceIndices; - bool errored = false; - for (const auto &index: faceIndices) { - int finalIndex = index; - int mapTimes = 0; - while (mapTimes < 500) { - auto findMapResult = weldVertexToMap.find(finalIndex); - if (findMapResult == weldVertexToMap.end()) - break; - finalIndex = findMapResult->second; - mapTimes++; - } - if (mapTimes >= 500) { - qDebug() << "Map too much times"; - errored = true; - break; - } - mappedFaceIndices.push_back(finalIndex); - } - if (errored || mappedFaceIndices.size() < 3) - continue; - bool welded = false; - for (decltype(mappedFaceIndices.size()) j = 0; j < mappedFaceIndices.size(); j++) { - int next = (j + 1) % 3; - if (mappedFaceIndices[j] == mappedFaceIndices[next]) { - welded = true; - break; - } - } - if (welded) { - weldedCount++; - continue; - } - faceCountAfterWeld++; - newFaceVertexNumAndIndices.push_back(mappedFaceIndices.size()); - for (const auto &index: mappedFaceIndices) { - newFaceVertexNumAndIndices.push_back(index); - } - } - if (affectedNum) - *affectedNum = weldedCount; - qDebug() << "Welded" << weldedCount << "triangles(" << newFaceIndices.size() << " - " << weldedCount << " = " << faceCountAfterWeld << ")"; - weldedMesh = meshlite_build(meshlite, vertexPositions, vertexCount, newFaceVertexNumAndIndices.data(), newFaceVertexNumAndIndices.size()); - delete[] faceVertexNumAndIndices; - delete[] vertexPositions; - return weldedMesh; -} diff --git a/src/meshweldseam.h b/src/meshweldseam.h deleted file mode 100644 index 4e832144..00000000 --- a/src/meshweldseam.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef DUST3D_MESH_WELD_SEAM_H -#define DUST3D_MESH_WELD_SEAM_H -#include "meshlite.h" -#include "positionmap.h" -#include - -int meshWeldSeam(void *meshlite, int meshId, float allowedSmallestDistance, - const PositionMap &excludePositions, int *affectedNum=nullptr); - -#endif diff --git a/src/outcome.cpp b/src/outcome.cpp index ee59456c..abab1717 100644 --- a/src/outcome.cpp +++ b/src/outcome.cpp @@ -1 +1 @@ -#include "outcome.h" +#include "outcome.h" \ No newline at end of file diff --git a/src/outcome.h b/src/outcome.h index 87e44483..54b57345 100644 --- a/src/outcome.h +++ b/src/outcome.h @@ -21,7 +21,6 @@ struct OutcomeNode QUuid mirrorFromPartId; QUuid mirroredByPartId; BoneMark boneMark; - QVector3D baseNormal; }; class Outcome @@ -30,6 +29,7 @@ public: std::vector nodes; std::vector>> nodeVertices; std::vector vertices; + std::vector> triangleAndQuads; std::vector> triangles; std::vector triangleNormals; diff --git a/src/positionmap.cpp b/src/positionmap.cpp deleted file mode 100644 index 1e3703cc..00000000 --- a/src/positionmap.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "positionmap.h" - diff --git a/src/positionmap.h b/src/positionmap.h deleted file mode 100644 index f1fbf718..00000000 --- a/src/positionmap.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef DUST3D_POSITION_MAP_H -#define DUST3D_POSITION_MAP_H -#include - -struct PositionMapKey -{ - int x; - int y; - int z; - bool operator<(const PositionMapKey &other) const { - if (x < other.x) { - return true; - } else if (x > other.x) { - return false; - } - if (y < other.y) { - return true; - } else if (y > other.y) { - return false; - } - return z < other.z; - } -}; - -template -class PositionMap -{ -public: - PositionMap(float gridSize=0.001) : - m_intGridSize(PositionMap::makeInt(gridSize)) - { - } - - static int makeInt(float value) - { - return value * 1000; - } - - PositionMapKey makeKey(float x, float y, float z) const - { - PositionMapKey key; - key.x = makeInt(x) / m_intGridSize; - key.y = makeInt(y) / m_intGridSize; - key.z = makeInt(z) / m_intGridSize; - return key; - } - - void addPosition(float x, float y, float z, T data) - { - m_map[makeKey(x, y, z)] = data; - } - - bool findPosition(float x, float y, float z, T *data = nullptr) const - { - const auto &result = m_map.find(makeKey(x, y, z)); - if (result == m_map.end()) - return false; - if (data) - *data = result->second; - return true; - } - - void removePosition(float x, float y, float z) - { - m_map.erase(makeKey(x, y, z)); - } - - std::map &map() - { - return m_map; - } - -private: - std::map m_map; - int m_intGridSize; -}; - -#endif diff --git a/src/riggenerator.cpp b/src/riggenerator.cpp index 89cc5631..abc5ec13 100644 --- a/src/riggenerator.cpp +++ b/src/riggenerator.cpp @@ -74,7 +74,7 @@ void RigGenerator::generate() inputVerticesPositions.push_back(vertex); } - std::map, std::tuple, float, QVector3D>> markedNodes; + std::map, std::tuple, float>> markedNodes; for (const auto &bmeshNode: m_outcome->nodes) { if (bmeshNode.boneMark == BoneMark::None) continue; @@ -83,7 +83,7 @@ void RigGenerator::generate() boneSide = bmeshNode.origin.x() > 0 ? SkeletonSide::Left : SkeletonSide::Right; } //qDebug() << "Add bone mark:" << BoneMarkToString(bmeshNode.boneMark) << "side:" << SkeletonSideToDispName(boneSide); - markedNodes[std::make_pair(bmeshNode.partId, bmeshNode.nodeId)] = std::make_tuple(bmeshNode.boneMark, boneSide, bmeshNode.origin, std::set(), bmeshNode.radius, bmeshNode.baseNormal); + markedNodes[std::make_pair(bmeshNode.partId, bmeshNode.nodeId)] = std::make_tuple(bmeshNode.boneMark, boneSide, bmeshNode.origin, std::set(), bmeshNode.radius); } for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); triangleIndex++) { @@ -99,19 +99,19 @@ void RigGenerator::generate() inputTriangles.insert(newTriangle); } - std::vector, float, QVector3D>> markedNodesList; + std::vector, float>> markedNodesList; for (const auto &markedNode: markedNodes) { markedNodesList.push_back(markedNode.second); } // Combine the overlapped marks - std::vector, float, QVector3D>> combinedMarkedNodesList; + std::vector, float>> combinedMarkedNodesList; std::set processedNodes; for (size_t i = 0; i < markedNodesList.size(); ++i) { if (processedNodes.find(i) != processedNodes.end()) continue; const auto &first = markedNodesList[i]; - std::tuple, float, QVector3D> newNodes; + std::tuple, float> newNodes; size_t combinedNum = 1; newNodes = first; for (size_t j = i + 1; j < markedNodesList.size(); ++j) { @@ -126,7 +126,6 @@ void RigGenerator::generate() for (const auto &triangle: std::get<3>(second)) std::get<3>(newNodes).insert(triangle); std::get<4>(newNodes) += std::get<4>(second); - std::get<5>(newNodes) += std::get<5>(second); ++combinedNum; } } @@ -134,7 +133,6 @@ void RigGenerator::generate() if (combinedNum > 1) { std::get<2>(newNodes) /= combinedNum; std::get<4>(newNodes) /= combinedNum; - std::get<5>(newNodes).normalize(); qDebug() << "Combined" << combinedNum << "on mark:" << BoneMarkToString(std::get<0>(newNodes)) << "side:" << SkeletonSideToDispName(std::get<1>(newNodes)); } @@ -152,7 +150,6 @@ void RigGenerator::generate() m_autoRigger->addMarkGroup(std::get<0>(markedNode), std::get<1>(markedNode), std::get<2>(markedNode), std::get<4>(markedNode), - std::get<5>(markedNode), std::get<3>(markedNode)); } m_isSucceed = m_autoRigger->rig(); diff --git a/src/rigger.cpp b/src/rigger.cpp index e5a66d0e..5e420aba 100644 --- a/src/rigger.cpp +++ b/src/rigger.cpp @@ -71,8 +71,7 @@ bool Rigger::calculateBodyTriangles(std::set &bodyTriangle return true; } -bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, QVector3D baseNormal, - const std::set &markTriangles) +bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, const std::set &markTriangles) { m_marks.push_back(RiggerMark()); @@ -81,7 +80,6 @@ bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bo mark.boneSide = boneSide; mark.bonePosition = bonePosition; mark.nodeRadius = nodeRadius; - mark.baseNormal = baseNormal; mark.markTriangles = markTriangles; if (isCutOffSplitter(mark.boneMark)) { diff --git a/src/rigger.h b/src/rigger.h index b20faa27..462dd2e6 100644 --- a/src/rigger.h +++ b/src/rigger.h @@ -18,7 +18,6 @@ public: SkeletonSide boneSide; QVector3D bonePosition; float nodeRadius = 0; - QVector3D baseNormal; std::set markTriangles; const std::set &bigGroup() const { @@ -124,8 +123,7 @@ class Rigger : public QObject public: Rigger(const std::vector &verticesPositions, const std::set &inputTriangles); - bool addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, QVector3D baseNormal, - const std::set &markTriangles); + bool addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, const std::set &markTriangles); const std::vector> &messages(); const std::vector &resultBones(); const std::map &resultWeights(); diff --git a/src/trianglesourcenoderesolve.cpp b/src/trianglesourcenoderesolve.cpp index 953f66ba..89f0db92 100644 --- a/src/trianglesourcenoderesolve.cpp +++ b/src/trianglesourcenoderesolve.cpp @@ -1,5 +1,6 @@ +#include +#include #include "trianglesourcenoderesolve.h" -#include "positionmap.h" struct HalfColorEdge { @@ -19,18 +20,18 @@ struct CandidateEdge void triangleSourceNodeResolve(const Outcome &outcome, std::vector> &triangleSourceNodes) { std::map> vertexSourceMap; - PositionMap> positionMap; + std::map> positionMap; std::map, HalfColorEdge> halfColorEdgeMap; std::set brokenTriangleSet; for (const auto &it: outcome.nodeVertices) { - positionMap.addPosition(it.first.x(), it.first.y(), it.first.z(), it.second); + positionMap.insert({nodemesh::PositionKey(it.first), it.second}); } for (auto x = 0u; x < outcome.vertices.size(); x++) { const QVector3D *resultVertex = &outcome.vertices[x]; std::pair source; - if (positionMap.findPosition(resultVertex->x(), resultVertex->y(), resultVertex->z(), &source)) { - vertexSourceMap[x] = source; - } + auto findPosition = positionMap.find(nodemesh::PositionKey(*resultVertex)); + if (findPosition != positionMap.end()) + vertexSourceMap[x] = findPosition->second; } for (auto x = 0u; x < outcome.triangles.size(); x++) { const auto triangle = outcome.triangles[x]; diff --git a/src/trianglesourcenoderesolve.h b/src/trianglesourcenoderesolve.h index b3c623f2..dd835a6d 100644 --- a/src/trianglesourcenoderesolve.h +++ b/src/trianglesourcenoderesolve.h @@ -4,4 +4,4 @@ void triangleSourceNodeResolve(const Outcome &outcome, std::vector> &triangleSourceNodes); -#endif +#endif \ No newline at end of file diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h deleted file mode 100644 index 2b35b8cd..00000000 --- a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MESHLITE_H -#define MESHLITE_H - -#ifdef __cplusplus -extern "C" { -#endif - -void *meshlite_create_context(void); -int meshlite_destroy_context(void *context); -int meshlite_merge(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_import(void *context, const char *filename); -int meshlite_export(void *context, int mesh_id, const char *filename); -int meshlite_clone(void *context, int from_mesh_id); -int meshlite_triangulate(void *context, int mesh_id); -int meshlite_is_triangulated_manifold(void *context, int mesh_id); -int meshlite_subdivide(void *context, int mesh_id); -int meshlite_union(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_diff(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_intersect(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_scale(void *context, int mesh_id, float value); -int meshlite_get_vertex_count(void *context, int mesh_id); -int meshlite_get_vertex_position_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_get_vertex_source_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_face_count(void *context, int mesh_id); -int meshlite_get_face_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_triangle_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_triangle_normal_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_get_edge_count(void *context, int mesh_id); -int meshlite_get_edge_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_edge_normal_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_get_halfedge_count(void *context, int mesh_id); -int meshlite_get_halfedge_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len); -int meshlite_bmesh_create(void *context); -int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count); -int meshlite_bmesh_set_round_way(void *context, int bmesh_id, int round_way); -int meshlite_bmesh_set_deform_thickness(void *context, int bmesh_id, float thickness); -int meshlite_bmesh_set_deform_width(void *context, int bmesh_id, float width); -int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable); -int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius); -int meshlite_bmesh_set_node_cut_subdiv_count(void *context, int bmesh_id, int node_id, int subdiv_count); -int meshlite_bmesh_set_node_round_way(void *context, int bmesh_id, int node_id, int round_way); -int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id); -int meshlite_bmesh_generate_mesh(void *context, int bmesh_id); -int meshlite_bmesh_get_node_base_norm(void *context, int bmesh_id, int node_id, float *norm_buffer); -int meshlite_bmesh_destroy(void *context, int bmesh_id); -int meshlite_bmesh_error_count(void *context, int bmesh_id); -int meshlite_bmesh_add_seam_requirement(void *context, int bmesh_id); -int meshlite_bmesh_get_seam_count(void *context, int bmesh_id); -int meshlite_bmesh_get_seam_index_array(void *context, int bmesh_id, int *buffer, int max_buffer_len); -int meshlite_combine_adj_faces(void *context, int mesh_id); -int meshlite_combine_coplanar_faces(void *context, int mesh_id); -int meshlite_trim(void *context, int mesh_id, int normalize); -int meshlite_mirror_in_x(void *context, int mesh_id, float center_x); -int meshlite_mirror_in_z(void *context, int mesh_id, float center_z); -int meshlite_fix_hole(void *context, int mesh_id); -int meshlite_skeletonmesh_create(void *context); -int meshlite_skeletonmesh_set_end_radius(void *context, float radius); -int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z); -int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id); -int meshlite_smooth_vertices(void *context, int mesh_id, float factor, int *buffer, int max_buffer_len); -int meshlite_smooth(void *context, int mesh_id, float factor); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll deleted file mode 100644 index 0159aa38..00000000 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll and /dev/null differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib deleted file mode 100644 index 96d506d2..00000000 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib and /dev/null differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h deleted file mode 100644 index 2b35b8cd..00000000 --- a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MESHLITE_H -#define MESHLITE_H - -#ifdef __cplusplus -extern "C" { -#endif - -void *meshlite_create_context(void); -int meshlite_destroy_context(void *context); -int meshlite_merge(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_import(void *context, const char *filename); -int meshlite_export(void *context, int mesh_id, const char *filename); -int meshlite_clone(void *context, int from_mesh_id); -int meshlite_triangulate(void *context, int mesh_id); -int meshlite_is_triangulated_manifold(void *context, int mesh_id); -int meshlite_subdivide(void *context, int mesh_id); -int meshlite_union(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_diff(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_intersect(void *context, int first_mesh_id, int second_mesh_id); -int meshlite_scale(void *context, int mesh_id, float value); -int meshlite_get_vertex_count(void *context, int mesh_id); -int meshlite_get_vertex_position_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_get_vertex_source_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_face_count(void *context, int mesh_id); -int meshlite_get_face_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_triangle_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_triangle_normal_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_get_edge_count(void *context, int mesh_id); -int meshlite_get_edge_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_edge_normal_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_get_halfedge_count(void *context, int mesh_id); -int meshlite_get_halfedge_index_array(void *context, int mesh_id, int *buffer, int max_buffer_len); -int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer, int max_buffer_len); -int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len); -int meshlite_bmesh_create(void *context); -int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count); -int meshlite_bmesh_set_round_way(void *context, int bmesh_id, int round_way); -int meshlite_bmesh_set_deform_thickness(void *context, int bmesh_id, float thickness); -int meshlite_bmesh_set_deform_width(void *context, int bmesh_id, float width); -int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable); -int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius); -int meshlite_bmesh_set_node_cut_subdiv_count(void *context, int bmesh_id, int node_id, int subdiv_count); -int meshlite_bmesh_set_node_round_way(void *context, int bmesh_id, int node_id, int round_way); -int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id); -int meshlite_bmesh_generate_mesh(void *context, int bmesh_id); -int meshlite_bmesh_get_node_base_norm(void *context, int bmesh_id, int node_id, float *norm_buffer); -int meshlite_bmesh_destroy(void *context, int bmesh_id); -int meshlite_bmesh_error_count(void *context, int bmesh_id); -int meshlite_bmesh_add_seam_requirement(void *context, int bmesh_id); -int meshlite_bmesh_get_seam_count(void *context, int bmesh_id); -int meshlite_bmesh_get_seam_index_array(void *context, int bmesh_id, int *buffer, int max_buffer_len); -int meshlite_combine_adj_faces(void *context, int mesh_id); -int meshlite_combine_coplanar_faces(void *context, int mesh_id); -int meshlite_trim(void *context, int mesh_id, int normalize); -int meshlite_mirror_in_x(void *context, int mesh_id, float center_x); -int meshlite_mirror_in_z(void *context, int mesh_id, float center_z); -int meshlite_fix_hole(void *context, int mesh_id); -int meshlite_skeletonmesh_create(void *context); -int meshlite_skeletonmesh_set_end_radius(void *context, float radius); -int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z); -int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id); -int meshlite_smooth_vertices(void *context, int mesh_id, float factor, int *buffer, int max_buffer_len); -int meshlite_smooth(void *context, int mesh_id, float factor); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll deleted file mode 100644 index b21be9e9..00000000 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll and /dev/null differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib deleted file mode 100644 index 7b6484f9..00000000 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib and /dev/null differ diff --git a/thirdparty/meshlite/update_latest_release.sh b/thirdparty/meshlite/update_latest_release.sh deleted file mode 100644 index 90fdd748..00000000 --- a/thirdparty/meshlite/update_latest_release.sh +++ /dev/null @@ -1,6 +0,0 @@ -curl -L https://github.com/huxingyi/meshlite/releases/download/meshlite-unstable/meshlite_unstable_vc14_x86.zip --output meshlite_unstable_vc14_x86.zip -rm -rf meshlite_unstable_vc14_x86/* && unzip meshlite_unstable_vc14_x86.zip -d meshlite_unstable_vc14_x86 -rm meshlite_unstable_vc14_x86.zip -curl -L https://github.com/huxingyi/meshlite/releases/download/meshlite-unstable/meshlite_unstable_vc14_x64.zip --output meshlite_unstable_vc14_x64.zip -rm -rf meshlite_unstable_vc14_x64/* && unzip meshlite_unstable_vc14_x64.zip -d meshlite_unstable_vc14_x64 -rm meshlite_unstable_vc14_x64.zip \ No newline at end of file diff --git a/thirdparty/nodemesh/.gitignore b/thirdparty/nodemesh/.gitignore new file mode 100644 index 00000000..259148fa --- /dev/null +++ b/thirdparty/nodemesh/.gitignore @@ -0,0 +1,32 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/thirdparty/nodemesh/LICENSE b/thirdparty/nodemesh/LICENSE new file mode 100644 index 00000000..0fcae81c --- /dev/null +++ b/thirdparty/nodemesh/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Jeremy HU + +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. diff --git a/thirdparty/nodemesh/README.md b/thirdparty/nodemesh/README.md new file mode 100644 index 00000000..de5a1c3d --- /dev/null +++ b/thirdparty/nodemesh/README.md @@ -0,0 +1,2 @@ +# nodemesh +Mesh generating experiment for Dust3D diff --git a/thirdparty/nodemesh/nodemesh/box.cpp b/thirdparty/nodemesh/nodemesh/box.cpp new file mode 100644 index 00000000..6ed4585c --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/box.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian SimpleKernel; +typedef CGAL::Surface_mesh PolygonMesh; + +namespace nodemesh +{ + +void box(const QVector3D position, float radius, size_t subdivideTimes, std::vector &vertices, std::vector> &faces) +{ + std::vector beginPlane = { + {-radius, -radius, radius}, + { radius, -radius, radius}, + { radius, radius, radius}, + {-radius, radius, radius}, + }; + std::vector endPlane = { + beginPlane[0], + beginPlane[3], + beginPlane[2], + beginPlane[1] + }; + for (auto &vertex: endPlane) { + vertex.setZ(vertex.z() - radius - radius); + } + for (const auto &vertex: beginPlane) { + vertices.push_back(vertex); + } + for (const auto &vertex: endPlane) { + vertices.push_back(vertex); + } + std::vector beginLoop = { + 0, 1, 2, 3 + }; + std::vector endLoop = { + 4, 5, 6, 7 + }; + std::vector alignedEndLoop = { + 4, 7, 6, 5 + }; + faces.push_back(beginLoop); + faces.push_back(endLoop); + for (size_t i = 0; i < beginLoop.size(); ++i) { + size_t j = (i + 1) % beginLoop.size(); + faces.push_back({ + beginLoop[j], + beginLoop[i], + alignedEndLoop[i], + alignedEndLoop[j] + }); + } + for (auto &vertex: vertices) { + vertex += position; + } + + if (subdivideTimes > 0) { + std::vector> triangles; + triangulate(vertices, faces, triangles); + PolygonMesh *cgalMesh = buildCgalMesh(vertices, triangles); + if (nullptr != cgalMesh) { + CGAL::Subdivision_method_3::CatmullClark_subdivision(*cgalMesh, subdivideTimes); + vertices.clear(); + faces.clear(); + fetchFromCgalMesh(cgalMesh, vertices, faces); + delete cgalMesh; + } + } +} + +} diff --git a/thirdparty/nodemesh/nodemesh/box.h b/thirdparty/nodemesh/nodemesh/box.h new file mode 100644 index 00000000..83c11574 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/box.h @@ -0,0 +1,13 @@ +#ifndef NODEMESH_BOX_H +#define NODEMESH_BOX_H +#include +#include + +namespace nodemesh +{ + +void box(const QVector3D position, float radius, size_t subdivideTimes, std::vector &vertices, std::vector> &faces); + +} + +#endif diff --git a/thirdparty/nodemesh/nodemesh/builder.cpp b/thirdparty/nodemesh/nodemesh/builder.cpp new file mode 100644 index 00000000..4353baaf --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/builder.cpp @@ -0,0 +1,633 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WRAP_STEP_BACK_FACTOR 0.1 // 0.1 ~ 0.9 +#define WRAP_WELD_FACTOR 0.01 // Allowed distance: WELD_FACTOR * radius + +namespace nodemesh +{ + +size_t Builder::addNode(const QVector3D &position, float radius, const std::vector &cutTemplate) +{ + size_t nodeIndex = m_nodes.size(); + Node node; + node.position = position; + node.radius = radius; + node.cutTemplate = cutTemplate; + m_nodes.push_back(node); + m_sortedNodeIndices.push_back(nodeIndex); + //qDebug() << "addNode" << position << radius; + return nodeIndex; +} + +size_t Builder::addEdge(size_t firstNodeIndex, size_t secondNodeIndex) +{ + size_t edgeIndex = m_edges.size(); + m_edges.push_back(Edge { + .nodes = {firstNodeIndex, secondNodeIndex} + }); + m_nodes[firstNodeIndex].edges.push_back(edgeIndex); + m_nodes[secondNodeIndex].edges.push_back(edgeIndex); + //qDebug() << "addEdge" << firstNodeIndex << secondNodeIndex; + return edgeIndex; +} + +const std::vector &Builder::generatedVertices() +{ + return m_generatedVertices; +} + +const std::vector> &Builder::generatedFaces() +{ + return m_generatedFaces; +} + +const std::vector &Builder::generatedVerticesSourceNodeIndices() +{ + return m_generatedVerticesSourceNodeIndices; +} + +void Builder::sortNodeIndices() +{ + std::sort(m_sortedNodeIndices.begin(), m_sortedNodeIndices.end(), [&](const size_t &firstIndex, + const size_t &secondIndex) { + const Node &firstNode = m_nodes[firstIndex]; + const Node &secondNode = m_nodes[secondIndex]; + if (firstNode.edges.size() > secondNode.edges.size()) + return true; + if (firstNode.edges.size() < secondNode.edges.size()) + return false; + if (firstNode.radius > secondNode.radius) + return true; + if (firstNode.radius < secondNode.radius) + return false; + if (firstNode.position.y() > secondNode.position.y()) + return true; + if (firstNode.position.y() < secondNode.position.y()) + return false; + if (firstNode.position.z() > secondNode.position.z()) + return true; + if (firstNode.position.z() < secondNode.position.z()) + return false; + if (firstNode.position.x() > secondNode.position.x()) + return true; + if (firstNode.position.x() < secondNode.position.x()) + return false; + return false; + }); +} + +void Builder::prepareNode(size_t nodeIndex) +{ + auto &node = m_nodes[nodeIndex]; + node.raysToNeibors.resize(node.edges.size()); + std::vector neighborPositions(node.edges.size()); + std::vector neighborRadius(node.edges.size()); + for (size_t i = 0; i < node.edges.size(); ++i) { + size_t neighborIndex = m_edges[node.edges[i]].neiborOf(nodeIndex); + const auto &neighbor = m_nodes[neighborIndex]; + node.raysToNeibors[i] = (neighbor.position - node.position).normalized(); + neighborPositions[i] = neighbor.position; + neighborRadius[i] = neighbor.radius; + } + auto baseNormalResult = calculateBaseNormal(node.raysToNeibors, + neighborPositions, + neighborRadius); + node.initialBaseNormal = baseNormalResult.first; + node.hasInitialBaseNormal = baseNormalResult.second; +} + +void Builder::resolveBaseNormalRecursively(size_t nodeIndex) +{ + auto &node = m_nodes[nodeIndex]; + if (node.baseNormalResolved) + return; + + if (node.hasInitialBaseNormal) { + resolveBaseNormalForLeavesRecursively(nodeIndex, node.initialBaseNormal); + } else { + node.baseNormalSearched = true; + auto searchResult = searchBaseNormalFromNeighborsRecursively(nodeIndex); + if (searchResult.second) { + resolveBaseNormalForLeavesRecursively(nodeIndex, searchResult.first); + } else { + resolveBaseNormalForLeavesRecursively(nodeIndex, QVector3D {0, 0, 1}); + } + } +} + +void Builder::resolveBaseNormalForLeavesRecursively(size_t nodeIndex, const QVector3D &baseNormal) +{ + auto &node = m_nodes[nodeIndex]; + if (node.baseNormalResolved) + return; + node.baseNormalResolved = true; + node.baseNormal = baseNormal; + for (size_t i = 0; i < node.edges.size(); ++i) { + size_t neighborIndex = m_edges[node.edges[i]].neiborOf(nodeIndex); + const auto &neighbor = m_nodes[neighborIndex]; + switch (neighbor.edges.size()) { + case 1: + resolveBaseNormalForLeavesRecursively(neighborIndex, baseNormal); + break; + case 2: + neighbor.hasInitialBaseNormal ? + resolveBaseNormalForLeavesRecursively(neighborIndex, neighbor.initialBaseNormal) : + resolveBaseNormalForLeavesRecursively(neighborIndex, baseNormal); + break; + default: + // void + break; + } + } +} + +std::pair Builder::searchBaseNormalFromNeighborsRecursively(size_t nodeIndex) +{ + auto &node = m_nodes[nodeIndex]; + node.baseNormalSearched = true; + for (size_t i = 0; i < node.edges.size(); ++i) { + size_t neighborIndex = m_edges[node.edges[i]].neiborOf(nodeIndex); + const auto &neighbor = m_nodes[neighborIndex]; + if (neighbor.baseNormalResolved) + return {neighbor.baseNormal, true}; + } + for (size_t i = 0; i < node.edges.size(); ++i) { + size_t neighborIndex = m_edges[node.edges[i]].neiborOf(nodeIndex); + const auto &neighbor = m_nodes[neighborIndex]; + if (neighbor.hasInitialBaseNormal) + return {neighbor.initialBaseNormal, true}; + } + for (size_t i = 0; i < node.edges.size(); ++i) { + size_t neighborIndex = m_edges[node.edges[i]].neiborOf(nodeIndex); + const auto &neighbor = m_nodes[neighborIndex]; + if (neighbor.baseNormalSearched) + continue; + auto searchResult = searchBaseNormalFromNeighborsRecursively(neighborIndex); + if (searchResult.second) + return searchResult; + } + + return {{}, false}; +} + +bool Builder::build() +{ + bool succeed = true; + + sortNodeIndices(); + + if (m_sortedNodeIndices.size() < 2) { + if (m_sortedNodeIndices.size() == 1) { + const Node &node = m_nodes[0]; + int subdivideTimes = (node.cutTemplate.size() / 4) - 1; + if (subdivideTimes < 0) + subdivideTimes = 0; + box(node.position, node.radius, subdivideTimes, m_generatedVertices, m_generatedFaces); + m_generatedVerticesSourceNodeIndices.resize(m_generatedVertices.size(), 0); + } + return true; + } + + for (const auto &nodeIndex: m_sortedNodeIndices) { + prepareNode(nodeIndex); + } + for (const auto &nodeIndex: m_sortedNodeIndices) { + resolveBaseNormalRecursively(nodeIndex); + } + for (const auto &nodeIndex: m_sortedNodeIndices) { + if (!generateCutsForNode(nodeIndex)) + succeed = false; + } + + stitchEdgeCuts(); + + applyWeld(); + applyDeform(); + + return succeed; +} + +bool Builder::validateNormal(const QVector3D &normal) +{ + if (normal.isNull()) { + return false; + } + if (isnan(normal.x()) || isnan(normal.y()) || isnan(normal.z())) { + return false; + } + return true; +} + +std::pair Builder::calculateBaseNormal(const std::vector &directs, + const std::vector &positions, + const std::vector &weights) +{ + auto calculateThreePointsNormal = [&](size_t i0, size_t i1, size_t i2) -> std::pair { + auto normal = QVector3D::normal(positions[i0], positions[i1], positions[i2]); + if (validateNormal(normal)) { + return {normal, true}; + } + // >=15 degrees && <= 165 degrees + if (abs(QVector3D::dotProduct(directs[i0], directs[i1])) < 0.966) { + return {QVector3D::crossProduct(directs[i0], directs[i1]).normalized(), true}; + } else if (abs(QVector3D::dotProduct(directs[i1], directs[i2])) < 0.966) { + return {QVector3D::crossProduct(directs[i1], directs[i2]).normalized(), true}; + } else if (abs(QVector3D::dotProduct(directs[i2], directs[i0])) < 0.966) { + return {QVector3D::crossProduct(directs[i2], directs[i0]).normalized(), true}; + } else { + return {{}, false}; + } + }; + + if (directs.size() <= 1) { + return {{}, false}; + } else if (directs.size() <= 2) { + // >=15 degrees && <= 165 degrees + if (abs(QVector3D::dotProduct(directs[0], directs[1])) < 0.966) { + return {QVector3D::crossProduct(directs[0], directs[1]).normalized(), true}; + } + return {{}, false}; + } else if (directs.size() <= 3) { + return calculateThreePointsNormal(0, 1, 2); + } else { + std::vector> weightedIndices; + for (size_t i = 0; i < weights.size(); ++i) { + weightedIndices.push_back({i, weights[i]}); + } + std::sort(weightedIndices.begin(), weightedIndices.end(), [](const std::pair &first, const std::pair &second) { + return first.second > second.second; + }); + return calculateThreePointsNormal(weightedIndices[0].first, + weightedIndices[1].first, + weightedIndices[2].first); + } +} + +void Builder::insertCutVertices(const std::vector &cut, std::vector &vertices, size_t nodeIndex, const QVector3D &cutDirect) +{ + for (const auto &position: cut) { + size_t vertexIndex = m_generatedVertices.size(); + m_generatedVertices.push_back(position); + m_generatedVerticesSourceNodeIndices.push_back(nodeIndex); + m_generatedVerticesCutDirects.push_back(cutDirect); + vertices.push_back(vertexIndex); + } +} + +bool Builder::generateCutsForNode(size_t nodeIndex) +{ + if (m_swallowedNodes.find(nodeIndex) != m_swallowedNodes.end()) { + qDebug() << "node" << nodeIndex << "ignore cuts generating because of been swallowed"; + return true; + } + + auto &node = m_nodes[nodeIndex]; + size_t neighborsCount = node.edges.size(); + //qDebug() << "Generate cuts for node" << nodeIndex << "with neighbor count" << neighborsCount; + if (1 == neighborsCount) { + QVector3D cutNormal = node.raysToNeibors[0]; + std::vector cut; + makeCut(node.position, node.radius, node.cutTemplate, node.baseNormal, cutNormal, cut); + std::vector vertices; + insertCutVertices(cut, vertices, nodeIndex, cutNormal); + m_generatedFaces.push_back(vertices); + m_edges[node.edges[0]].cuts.push_back({vertices, -cutNormal}); + } else if (2 == neighborsCount) { + QVector3D cutNormal = (node.raysToNeibors[0].normalized() - node.raysToNeibors[1].normalized()) / 2; + std::vector cut; + makeCut(node.position, node.radius, node.cutTemplate, node.baseNormal, cutNormal, cut); + std::vector vertices; + insertCutVertices(cut, vertices, nodeIndex, cutNormal); + std::vector verticesReversed; + verticesReversed = vertices; + std::reverse(verticesReversed.begin(), verticesReversed.end()); + m_edges[node.edges[0]].cuts.push_back({vertices, -cutNormal}); + m_edges[node.edges[1]].cuts.push_back({verticesReversed, cutNormal}); + } else if (neighborsCount >= 3) { + std::vector offsets(node.edges.size(), 0.0); + bool offsetChanged = false; + size_t tries = 0; + do { + offsetChanged = false; + qDebug() << "Try wrap #" << tries; + if (tryWrapMultipleBranchesForNode(nodeIndex, offsets, offsetChanged)) { + qDebug() << "Wrap succeed"; + return true; + } + ++tries; + } while (offsetChanged); + return false; + } + + return true; +} + +bool Builder::tryWrapMultipleBranchesForNode(size_t nodeIndex, std::vector &offsets, bool &offsetsChanged) +{ + auto backupVertices = m_generatedVertices; + auto backupFaces = m_generatedFaces; + auto backupSourceIndices = m_generatedVerticesSourceNodeIndices; + auto backupVerticesCutDirects = m_generatedVerticesCutDirects; + + auto &node = m_nodes[nodeIndex]; + std::vector, QVector3D>> cutsForWrapping; + std::vector, QVector3D>> cutsForEdges; + bool directSwallowed = false; + for (size_t i = 0; i < node.edges.size(); ++i) { + QVector3D cutNormal = node.raysToNeibors[i]; + size_t edgeIndex = node.edges[i]; + size_t neighborIndex = m_edges[edgeIndex].neiborOf(nodeIndex); + const auto &neighbor = m_nodes[neighborIndex]; + float distance = (neighbor.position - node.position).length(); + if (qFuzzyIsNull(distance)) + distance = 0.0001f; + float radiusFactor = 1.0 - (node.radius / distance); + float radius = node.radius * radiusFactor + neighbor.radius * (1.0 - radiusFactor); + std::vector cut; + float offsetDistance = 0; + offsetDistance = offsets[i] * (distance - node.radius - neighbor.radius); + if (offsetDistance < 0) + offsetDistance = 0; + float finalDistance = node.radius + offsetDistance; + if (finalDistance >= distance) { + if (swallowEdgeForNode(nodeIndex, i)) { + qDebug() << "Neighbor too near to wrap, swallow it"; + offsets[i] = 0; + offsetsChanged = true; + directSwallowed = true; + continue; + } + } + makeCut(node.position + cutNormal * finalDistance, radius, node.cutTemplate, node.baseNormal, cutNormal, cut); + std::vector vertices; + insertCutVertices(cut, vertices, nodeIndex, cutNormal); + cutsForEdges.push_back({vertices, -cutNormal}); + std::vector verticesReversed; + verticesReversed = vertices; + std::reverse(verticesReversed.begin(), verticesReversed.end()); + cutsForWrapping.push_back({verticesReversed, cutNormal}); + } + if (directSwallowed) { + m_generatedVertices = backupVertices; + m_generatedFaces = backupFaces; + m_generatedVerticesSourceNodeIndices = backupSourceIndices; + m_generatedVerticesCutDirects = backupVerticesCutDirects; + return false; + } + Stitcher stitcher; + stitcher.setVertices(&m_generatedVertices); + std::vector failedEdgeLoops; + bool stitchSucceed = stitcher.stitch(cutsForWrapping); + std::vector> testFaces = stitcher.newlyGeneratedFaces(); + for (const auto &cuts: cutsForWrapping) { + testFaces.push_back(cuts.first); + } + if (stitchSucceed) { + stitchSucceed = nodemesh::isManifold(testFaces); + if (!stitchSucceed) { + qDebug() << "Mesh stitch but not manifold"; + } + } + if (stitchSucceed) { + nodemesh::Combiner::Mesh mesh(m_generatedVertices, testFaces, false); + if (mesh.isNull()) { + qDebug() << "Mesh stitched but not not pass test"; + //nodemesh::exportMeshAsObj(m_generatedVertices, testFaces, "/Users/jeremy/Desktop/test.obj"); + stitchSucceed = false; + for (size_t i = 0; i < node.edges.size(); ++i) { + failedEdgeLoops.push_back(i); + } + } + } else { + //nodemesh::exportMeshAsObj(m_generatedVertices, testFaces, "/Users/jeremy/Desktop/test.obj"); + stitcher.getFailedEdgeLoops(failedEdgeLoops); + } + if (!stitchSucceed) { + for (const auto &edgeLoop: failedEdgeLoops) { + if (offsets[edgeLoop] + WRAP_STEP_BACK_FACTOR < 1.0) { + offsets[edgeLoop] += WRAP_STEP_BACK_FACTOR; + offsetsChanged = true; + } + } + if (!offsetsChanged) { + for (const auto &edgeLoop: failedEdgeLoops) { + if (offsets[edgeLoop] + WRAP_STEP_BACK_FACTOR >= 1.0) { + if (swallowEdgeForNode(nodeIndex, edgeLoop)) { + qDebug() << "No offset to step back, swallow neighbor instead"; + offsets[edgeLoop] = 0; + offsetsChanged = true; + break; + } + } + } + } + m_generatedVertices = backupVertices; + m_generatedFaces = backupFaces; + m_generatedVerticesSourceNodeIndices = backupSourceIndices; + m_generatedVerticesCutDirects = backupVerticesCutDirects; + return false; + } + + // Weld nearby vertices + float weldThreshold = node.radius * WRAP_WELD_FACTOR; + float allowedMinDist2 = weldThreshold * weldThreshold; + for (size_t i = 0; i < node.edges.size(); ++i) { + for (size_t j = i + 1; j < node.edges.size(); ++j) { + const auto &first = cutsForEdges[i]; + const auto &second = cutsForEdges[j]; + for (const auto &u: first.first) { + for (const auto &v: second.first) { + if ((m_generatedVertices[u] - m_generatedVertices[v]).lengthSquared() < allowedMinDist2) { + qDebug() << "Weld" << v << "to" << u; + m_weldMap.insert({v, u}); + } + } + } + } + } + + for (const auto &face: stitcher.newlyGeneratedFaces()) { + m_generatedFaces.push_back(face); + } + for (size_t i = 0; i < node.edges.size(); ++i) { + m_edges[node.edges[i]].cuts.push_back(cutsForEdges[i]); + } + + return true; +} + +bool Builder::swallowEdgeForNode(size_t nodeIndex, size_t edgeOrder) +{ + auto &node = m_nodes[nodeIndex]; + size_t edgeIndex = node.edges[edgeOrder]; + if (m_swallowedEdges.find(edgeIndex) != m_swallowedEdges.end()) { + qDebug() << "No more edge to swallow"; + return false; + } + size_t neighborIndex = m_edges[edgeIndex].neiborOf(nodeIndex); + const auto &neighbor = m_nodes[neighborIndex]; + if (neighbor.edges.size() != 2) { + qDebug() << "Neighbor is not a simple two edges node to swallow, edges:" << neighbor.edges.size() << "neighbor:" << neighborIndex << "node" << nodeIndex; + return false; + } + size_t anotherEdgeIndex = neighbor.anotherEdge(edgeIndex); + if (m_swallowedEdges.find(anotherEdgeIndex) != m_swallowedEdges.end()) { + qDebug() << "Desired edge already been swallowed"; + return false; + } + node.edges[edgeOrder] = anotherEdgeIndex; + //qDebug() << "Nodes of edge" << anotherEdgeIndex << "before update:"; + //for (const auto &it: m_edges[anotherEdgeIndex].nodes) + // qDebug() << it; + m_edges[anotherEdgeIndex].updateNodeIndex(neighborIndex, nodeIndex); + //qDebug() << "Nodes of edge" << anotherEdgeIndex << "after update:"; + //for (const auto &it: m_edges[anotherEdgeIndex].nodes) + // qDebug() << it; + m_swallowedEdges.insert(edgeIndex); + m_swallowedNodes.insert(neighborIndex); + qDebug() << "Swallow edge" << edgeIndex << "for node" << nodeIndex << "neighbor" << neighborIndex << "got eliminated, choosen edge" << anotherEdgeIndex; + return true; +} + +void Builder::makeCut(const QVector3D &position, + float radius, + const std::vector &cutTemplate, + QVector3D &baseNormal, + const QVector3D &cutNormal, + std::vector &resultCut) +{ + QVector3D orientedBaseNormal = QVector3D::dotProduct(cutNormal, baseNormal) > 0 ? + baseNormal : -baseNormal; + // 0.966: < 15 degress + if (QVector3D::dotProduct(cutNormal, orientedBaseNormal) > 0.966) { + if (QVector3D::dotProduct(cutNormal, QVector3D(1, 0, 0)) > 0.966) { + orientedBaseNormal = QVector3D(0, 1, 0); + } else { + orientedBaseNormal = QVector3D(1, 0, 0); + } + } + baseNormal = orientedBaseNormal.normalized(); + QVector3D u = QVector3D::crossProduct(cutNormal, orientedBaseNormal).normalized(); + QVector3D v = QVector3D::crossProduct(u, cutNormal).normalized(); + auto uFactor = u * radius; + auto vFactor = v * radius; + for (const auto &t: cutTemplate) { + resultCut.push_back(position + uFactor * t.x() + vFactor * t.y()); + } +} + +void Builder::stitchEdgeCuts() +{ + for (size_t edgeIndex = 0; edgeIndex < m_edges.size(); ++edgeIndex) { + auto &edge = m_edges[edgeIndex]; + if (2 == edge.cuts.size()) { + Stitcher stitcher; + stitcher.setVertices(&m_generatedVertices); + stitcher.stitch(edge.cuts); + for (const auto &face: stitcher.newlyGeneratedFaces()) { + m_generatedFaces.push_back(face); + } + } + } +} + +void Builder::applyWeld() +{ + if (m_weldMap.empty()) + return; + + std::vector newVertices; + std::vector newSourceIndices; + std::vector> newFaces; + std::vector newVerticesCutDirects; + std::map oldVertexToNewMap; + for (const auto &face: m_generatedFaces) { + std::vector newIndices; + std::set inserted; + for (const auto &oldVertex: face) { + size_t useOldVertex = oldVertex; + size_t newIndex = 0; + auto findWeld = m_weldMap.find(useOldVertex); + if (findWeld != m_weldMap.end()) { + useOldVertex = findWeld->second; + } + auto findResult = oldVertexToNewMap.find(useOldVertex); + if (findResult == oldVertexToNewMap.end()) { + newIndex = newVertices.size(); + oldVertexToNewMap.insert({useOldVertex, newIndex}); + newVertices.push_back(m_generatedVertices[useOldVertex]); + newSourceIndices.push_back(m_generatedVerticesSourceNodeIndices[useOldVertex]); + newVerticesCutDirects.push_back(m_generatedVerticesCutDirects[useOldVertex]); + } else { + newIndex = findResult->second; + } + if (inserted.find(newIndex) != inserted.end()) + continue; + inserted.insert(newIndex); + newIndices.push_back(newIndex); + } + if (newIndices.size() < 3) { + qDebug() << "Face been welded"; + continue; + } + newFaces.push_back(newIndices); + } + + m_generatedVertices = newVertices; + m_generatedFaces = newFaces; + m_generatedVerticesSourceNodeIndices = newSourceIndices; + m_generatedVerticesCutDirects = newVerticesCutDirects; +} + +void Builder::setDeformThickness(float thickness) +{ + m_deformThickness = thickness; +} + +void Builder::setDeformWidth(float width) +{ + m_deformWidth = width; +} + +QVector3D Builder::calculateDeformPosition(const QVector3D &vertexPosition, const QVector3D &ray, const QVector3D &deformNormal, float deformFactor) +{ + QVector3D revisedNormal = QVector3D::dotProduct(ray, deformNormal) < 0.0 ? -deformNormal : deformNormal; + QVector3D projectRayOnRevisedNormal = revisedNormal * (QVector3D::dotProduct(ray, revisedNormal) / revisedNormal.lengthSquared()); + auto scaledProjct = projectRayOnRevisedNormal * deformFactor; + return vertexPosition + (scaledProjct - projectRayOnRevisedNormal); +} + +void Builder::applyDeform() +{ + for (size_t i = 0; i < m_generatedVertices.size(); ++i) { + auto &position = m_generatedVertices[i]; + const auto &node = m_nodes[m_generatedVerticesSourceNodeIndices[i]]; + const auto &cutDirect = m_generatedVerticesCutDirects[i]; + auto ray = position - node.position; + QVector3D sum; + size_t count = 0; + if (!qFuzzyCompare(m_deformThickness, (float)1.0)) { + auto deformedPosition = calculateDeformPosition(position, ray, node.baseNormal, m_deformThickness); + sum += deformedPosition; + ++count; + } + if (!qFuzzyCompare(m_deformWidth, (float)1.0)) { + auto deformedPosition = calculateDeformPosition(position, ray, QVector3D::crossProduct(node.baseNormal, cutDirect), m_deformWidth); + sum += deformedPosition; + ++count; + } + if (count > 0) + position = sum / count; + } +} + +} diff --git a/thirdparty/nodemesh/nodemesh/builder.h b/thirdparty/nodemesh/nodemesh/builder.h new file mode 100644 index 00000000..8f43a2f5 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/builder.h @@ -0,0 +1,117 @@ +#ifndef NODEMESH_BUILDER_H +#define NODEMESH_BUILDER_H +#include +#include +#include +#include +#include + +namespace nodemesh +{ + +class Builder +{ +public: + size_t addNode(const QVector3D &position, float radius, const std::vector &cutTemplate); + size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex); + void setDeformThickness(float thickness); + void setDeformWidth(float width); + const std::vector &generatedVertices(); + const std::vector> &generatedFaces(); + const std::vector &generatedVerticesSourceNodeIndices(); + void exportAsObj(const QString &filename); + bool build(); + +private: + + struct Edge; + + struct Node + { + float radius; + QVector3D position; + std::vector edges; + std::vector cutTemplate; + std::vector raysToNeibors; + QVector3D growthDirection; + QVector3D initialBaseNormal; + QVector3D baseNormal; + bool hasInitialBaseNormal = false; + bool baseNormalResolved = false; + bool baseNormalSearched = false; + + size_t anotherEdge(size_t edgeIndex) const + { + if (edges.size() != 2) + return edgeIndex; + const auto &otherIndex = edges[0]; + if (otherIndex == edgeIndex) + return edges[1]; + return otherIndex; + } + }; + + struct Edge + { + std::vector nodes; + std::vector, QVector3D>> cuts; + + size_t neiborOf(size_t nodeIndex) + { + const auto &otherIndex = nodes[0]; + if (otherIndex == nodeIndex) + return nodes[1]; + return otherIndex; + } + + void updateNodeIndex(size_t fromNodeIndex, size_t toNodeIndex) + { + if (nodes[0] == fromNodeIndex) { + nodes[0] = toNodeIndex; + return; + } + nodes[1] = toNodeIndex; + } + }; + + std::vector m_nodes; + std::vector m_edges; + std::vector m_generatedVertices; + std::vector m_generatedVerticesCutDirects; + std::vector m_generatedVerticesSourceNodeIndices; + std::vector> m_generatedFaces; + std::vector m_sortedNodeIndices; + std::map m_weldMap; + std::set m_swallowedEdges; + std::set m_swallowedNodes; + float m_deformThickness = 1.0; + float m_deformWidth = 1.0; + + void sortNodeIndices(); + void prepareNode(size_t nodeIndex); + std::pair calculateBaseNormal(const std::vector &directs, + const std::vector &positions, + const std::vector &weights); + bool validateNormal(const QVector3D &normal); + void resolveBaseNormalRecursively(size_t nodeIndex); + void resolveBaseNormalForLeavesRecursively(size_t nodeIndex, const QVector3D &baseNormal); + std::pair searchBaseNormalFromNeighborsRecursively(size_t nodeIndex); + bool generateCutsForNode(size_t nodeIndex); + bool tryWrapMultipleBranchesForNode(size_t nodeIndex, std::vector &offsets, bool &offsetsChanged); + void makeCut(const QVector3D &position, + float radius, + const std::vector &cutTemplate, + QVector3D &baseNormal, + const QVector3D &cutNormal, + std::vector &resultCut); + void insertCutVertices(const std::vector &cut, std::vector &vertices, size_t nodeIndex, const QVector3D &cutDirect); + void stitchEdgeCuts(); + void applyWeld(); + void applyDeform(); + QVector3D calculateDeformPosition(const QVector3D &vertexPosition, const QVector3D &ray, const QVector3D &deformNormal, float deformFactor); + bool swallowEdgeForNode(size_t nodeIndex, size_t edgeOrder); +}; + +} + +#endif diff --git a/thirdparty/nodemesh/nodemesh/cgalmesh.cpp b/thirdparty/nodemesh/nodemesh/cgalmesh.cpp new file mode 100644 index 00000000..4e5f33c5 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/cgalmesh.cpp @@ -0,0 +1,2 @@ +#include + diff --git a/thirdparty/nodemesh/nodemesh/cgalmesh.h b/thirdparty/nodemesh/nodemesh/cgalmesh.h new file mode 100644 index 00000000..fad9b8f2 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/cgalmesh.h @@ -0,0 +1,62 @@ +#ifndef NODEMESH_CGAL_MESH_H +#define NODEMESH_CGAL_MESH_H +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_exact_constructions_kernel ExactKernel; +typedef CGAL::Surface_mesh ExactMesh; + +template +typename CGAL::Surface_mesh *buildCgalMesh(const std::vector &positions, const std::vector> &indices) +{ + typename CGAL::Surface_mesh *mesh = new typename CGAL::Surface_mesh; + std::map::Vertex_index> vertexIndices; + for (const auto &face: indices) { + std::vector::Vertex_index> faceVertexIndices; + for (const auto &index: face) { + auto findIndex = vertexIndices.find(index); + if (findIndex != vertexIndices.end()) { + faceVertexIndices.push_back(findIndex->second); + } else { + const auto &pos = positions[index]; + auto newIndex = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z())); + vertexIndices.insert({index, newIndex}); + faceVertexIndices.push_back(newIndex); + } + } + mesh->add_face(faceVertexIndices); + } + return mesh; +} + +template +void fetchFromCgalMesh(typename CGAL::Surface_mesh *mesh, std::vector &vertices, std::vector> &faces) +{ + std::map::Vertex_index, size_t> vertexIndicesMap; + for (auto vertexIt = mesh->vertices_begin(); vertexIt != mesh->vertices_end(); vertexIt++) { + auto point = mesh->point(*vertexIt); + float x = (float)CGAL::to_double(point.x()); + float y = (float)CGAL::to_double(point.y()); + float z = (float)CGAL::to_double(point.z()); + vertexIndicesMap[*vertexIt] = vertices.size(); + vertices.push_back(QVector3D(x, y, z)); + } + + typename CGAL::Surface_mesh::Face_range faceRage = mesh->faces(); + typename CGAL::Surface_mesh::Face_range::iterator faceIt; + for (faceIt = faceRage.begin(); faceIt != faceRage.end(); faceIt++) { + CGAL::Vertex_around_face_iterator> vbegin, vend; + std::vector faceIndices; + for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(mesh->halfedge(*faceIt), *mesh); + vbegin != vend; + ++vbegin){ + faceIndices.push_back(vertexIndicesMap[*vbegin]); + } + faces.push_back(faceIndices); + } +} + +#endif + diff --git a/thirdparty/nodemesh/nodemesh/combiner.cpp b/thirdparty/nodemesh/nodemesh/combiner.cpp new file mode 100644 index 00000000..17b22aa9 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/combiner.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_exact_constructions_kernel ExactKernel; +typedef CGAL::Surface_mesh ExactMesh; + +namespace nodemesh +{ + +Combiner::Mesh::Mesh(const std::vector &vertices, const std::vector> &faces, bool removeSelfIntersects) +{ + ExactMesh *cgalMesh = nullptr; + if (!faces.empty()) { + std::vector> triangles; + triangulate(vertices, faces, triangles); + cgalMesh = buildCgalMesh(vertices, triangles); + if (!CGAL::is_valid_polygon_mesh(*cgalMesh)) { + qDebug() << "Mesh is not valid polygon"; + delete cgalMesh; + cgalMesh = nullptr; + } else if (CGAL::Polygon_mesh_processing::does_self_intersect(*cgalMesh)) { + //nodemesh::exportMeshAsObj(vertices, triangles, "/Users/jeremy/Desktop/test.obj"); + m_isSelfIntersected = true; + if (removeSelfIntersects) { + if (!CGAL::Polygon_mesh_processing::remove_self_intersections(*cgalMesh)) { + qDebug() << "Mesh does self intersect and cann't remove intersections"; + delete cgalMesh; + cgalMesh = nullptr; + } else { + qDebug() << "Mesh does self intersect but intersections got removed"; + } + } else { + delete cgalMesh; + cgalMesh = nullptr; + qDebug() << "Mesh does self intersect"; + } + } + } + m_privateData = cgalMesh; +} + +Combiner::Mesh::Mesh(const Mesh &other) +{ + if (other.m_privateData) { + m_privateData = new ExactMesh(*(ExactMesh *)other.m_privateData); + } +} + +Combiner::Mesh::~Mesh() +{ + ExactMesh *cgalMesh = (ExactMesh *)m_privateData; + delete cgalMesh; +} + +void Combiner::Mesh::fetch(std::vector &vertices, std::vector> &faces) const +{ + ExactMesh *exactMesh = (ExactMesh *)m_privateData; + if (nullptr == exactMesh) + return; + + fetchFromCgalMesh(exactMesh, vertices, faces); +} + +bool Combiner::Mesh::isNull() const +{ + return nullptr == m_privateData; +} + +bool Combiner::Mesh::isSelfIntersected() const +{ + return m_isSelfIntersected; +} + +Combiner::Mesh *Combiner::combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method, + std::vector> *combinedVerticesComeFrom) +{ + ExactMesh *resultCgalMesh = nullptr; + ExactMesh *firstCgalMesh = (ExactMesh *)firstMesh.m_privateData; + ExactMesh *secondCgalMesh = (ExactMesh *)secondMesh.m_privateData; + std::map> verticesSourceMap; + + auto addToSourceMap = [&](ExactMesh *mesh, Source source) { + size_t vertexIndex = 0; + for (auto vertexIt = mesh->vertices_begin(); vertexIt != mesh->vertices_end(); vertexIt++) { + auto point = mesh->point(*vertexIt); + float x = (float)CGAL::to_double(point.x()); + float y = (float)CGAL::to_double(point.y()); + float z = (float)CGAL::to_double(point.z()); + auto insertResult = verticesSourceMap.insert({{x, y, z}, {source, vertexIndex}}); + if (!insertResult.second) { + qDebug() << "Position key conflict:" << QVector3D {x, y, z} << "with:" << insertResult.first->first.position(); + } + ++vertexIndex; + } + }; + if (nullptr != combinedVerticesComeFrom) { + addToSourceMap(firstCgalMesh, Source::First); + addToSourceMap(secondCgalMesh, Source::Second); + } + + if (Method::Union == method) { + resultCgalMesh = new ExactMesh; + try { + if (!CGAL::Polygon_mesh_processing::corefine_and_compute_union(*firstCgalMesh, *secondCgalMesh, *resultCgalMesh)) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + } catch (...) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + } else if (Method::Diff == method) { + resultCgalMesh = new ExactMesh; + try { + if (!CGAL::Polygon_mesh_processing::corefine_and_compute_difference(*firstCgalMesh, *secondCgalMesh, *resultCgalMesh)) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + } catch (...) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + } + + if (nullptr != combinedVerticesComeFrom) { + combinedVerticesComeFrom->clear(); + if (nullptr != resultCgalMesh) { + for (auto vertexIt = resultCgalMesh->vertices_begin(); vertexIt != resultCgalMesh->vertices_end(); vertexIt++) { + auto point = resultCgalMesh->point(*vertexIt); + float x = (float)CGAL::to_double(point.x()); + float y = (float)CGAL::to_double(point.y()); + float z = (float)CGAL::to_double(point.z()); + auto findSource = verticesSourceMap.find(PositionKey(x, y, z)); + if (findSource == verticesSourceMap.end()) { + combinedVerticesComeFrom->push_back({Source::None, 0}); + } else { + combinedVerticesComeFrom->push_back(findSource->second); + } + } + } + } + + if (nullptr == resultCgalMesh) + return nullptr; + + Mesh *mesh = new Mesh; + mesh->m_privateData = resultCgalMesh; + return mesh; +} + +} diff --git a/thirdparty/nodemesh/nodemesh/combiner.h b/thirdparty/nodemesh/nodemesh/combiner.h new file mode 100644 index 00000000..19be4d54 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/combiner.h @@ -0,0 +1,49 @@ +#ifndef NODEMESH_COMBINER_H +#define NODEMESH_COMBINER_H +#include +#include + +namespace nodemesh +{ + +class Combiner +{ +public: + enum class Method + { + Union, + Diff + }; + + enum class Source + { + None, + First, + Second + }; + + class Mesh + { + public: + Mesh() = default; + Mesh(const std::vector &vertices, const std::vector> &faces, bool removeSelfIntersects=true); + Mesh(const Mesh &other); + ~Mesh(); + void fetch(std::vector &vertices, std::vector> &faces) const; + bool isNull() const; + bool isSelfIntersected() const; + + friend Combiner; + + private: + void *m_privateData = nullptr; + bool m_isSelfIntersected = false; + }; + + static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method, + std::vector> *combinedVerticesComeFrom=nullptr); +}; + +} + +#endif diff --git a/thirdparty/nodemesh/nodemesh/modifier.cpp b/thirdparty/nodemesh/nodemesh/modifier.cpp new file mode 100644 index 00000000..68719393 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/modifier.cpp @@ -0,0 +1,146 @@ +#include +#include +#include + +namespace nodemesh +{ + +size_t Modifier::addNode(const QVector3D &position, float radius, const std::vector &cutTemplate) +{ + size_t nodeIndex = m_nodes.size(); + + Node node; + node.position = position; + node.radius = radius; + node.cutTemplate = cutTemplate; + node.originNodeIndex = nodeIndex; + m_nodes.push_back(node); + + return nodeIndex; +} + +size_t Modifier::addEdge(size_t firstNodeIndex, size_t secondNodeIndex) +{ + size_t edgeIndex = m_edges.size(); + + Edge edge; + edge.firstNodeIndex = firstNodeIndex; + edge.secondNodeIndex = secondNodeIndex; + m_edges.push_back(edge); + + return edgeIndex; +} + +void Modifier::createIntermediateNode(const Node &firstNode, const Node &secondNode, float factor, Node *resultNode) +{ + float firstFactor = 1.0 - factor; + resultNode->position = firstNode.position * firstFactor + secondNode.position * factor; + resultNode->radius = firstNode.radius * firstFactor + secondNode.radius * factor; + resultNode->cutTemplate = firstNode.cutTemplate; + for (size_t i = 0; i < secondNode.cutTemplate.size(); ++i) { + if (i >= resultNode->cutTemplate.size()) + break; + resultNode->cutTemplate[i] = resultNode->cutTemplate[i] * firstFactor + secondNode.cutTemplate[i] * factor; + } +} + +void Modifier::subdivide() +{ + for (auto &node: m_nodes) { + auto oldCutTemplate = node.cutTemplate; + node.cutTemplate.clear(); + for (size_t i = 0; i < oldCutTemplate.size(); ++i) { + size_t j = (i + 1) % oldCutTemplate.size(); + QVector2D direct = (oldCutTemplate[i] + oldCutTemplate[j]).normalized(); + float length = (oldCutTemplate[i].length() + oldCutTemplate[j].length()) * 0.4; // 0.4 = 0.5 * 0.8 + node.cutTemplate.push_back(oldCutTemplate[i] * 0.8); + node.cutTemplate.push_back(direct * length); + } + } +} + +float Modifier::averageCutTemplateEdgeLength(const std::vector &cutTemplate) +{ + if (cutTemplate.empty()) + return 0; + + float sum = 0; + for (size_t i = 0; i < cutTemplate.size(); ++i) { + size_t j = (i + 1) % cutTemplate.size(); + sum += (cutTemplate[i] - cutTemplate[j]).length(); + } + return sum / cutTemplate.size(); +} + +void Modifier::roundEnd() +{ + std::map> neighbors; + for (const auto &edge: m_edges) { + neighbors[edge.firstNodeIndex].push_back(edge.secondNodeIndex); + neighbors[edge.secondNodeIndex].push_back(edge.firstNodeIndex); + } + for (const auto &it: neighbors) { + if (1 == it.second.size()) { + const Node ¤tNode = m_nodes[it.first]; + const Node &neighborNode = m_nodes[it.second[0]]; + Node endNode; + endNode.radius = currentNode.radius * 0.5; + endNode.position = currentNode.position + (currentNode.position - neighborNode.position).normalized() * endNode.radius; + endNode.cutTemplate = currentNode.cutTemplate; + endNode.originNodeIndex = currentNode.originNodeIndex; + size_t endNodeIndex = m_nodes.size(); + m_nodes.push_back(endNode); + addEdge(endNode.originNodeIndex, endNodeIndex); + } + } +} + +void Modifier::finalize() +{ + auto oldEdges = m_edges; + m_edges.clear(); + for (const auto &edge: oldEdges) { + const Node &firstNode = m_nodes[edge.firstNodeIndex]; + const Node &secondNode = m_nodes[edge.secondNodeIndex]; + float targetEdgeLength = (averageCutTemplateEdgeLength(firstNode.cutTemplate) * firstNode.radius + + averageCutTemplateEdgeLength(secondNode.cutTemplate) * secondNode.radius) * 0.5; + float currentEdgeLength = (firstNode.position - secondNode.position).length(); + if (targetEdgeLength >= currentEdgeLength) { + addEdge(edge.firstNodeIndex, edge.secondNodeIndex); + continue; + } + size_t newInsertNum = currentEdgeLength / targetEdgeLength; + if (newInsertNum < 1) + newInsertNum = 1; + float stepFactor = 1.0 / (newInsertNum + 1); + std::vector nodeIndices; + nodeIndices.push_back(edge.firstNodeIndex); + float factor = stepFactor; + for (size_t i = 0; i < newInsertNum && factor < 1.0; factor += stepFactor, ++i) { + Node intermediateNode; + const Node &firstNode = m_nodes[edge.firstNodeIndex]; + const Node &secondNode = m_nodes[edge.secondNodeIndex]; + createIntermediateNode(firstNode, secondNode, factor, &intermediateNode); + intermediateNode.originNodeIndex = factor <= 0.5 ? firstNode.originNodeIndex : secondNode.originNodeIndex; + size_t intermedidateNodeIndex = m_nodes.size(); + nodeIndices.push_back(intermedidateNodeIndex); + m_nodes.push_back(intermediateNode); + } + nodeIndices.push_back(edge.secondNodeIndex); + for (size_t i = 1; i < nodeIndices.size(); ++i) { + addEdge(nodeIndices[i - 1], nodeIndices[i]); + } + } +} + +const std::vector &Modifier::nodes() +{ + return m_nodes; +} + +const std::vector &Modifier::edges() +{ + return m_edges; +} + +} diff --git a/thirdparty/nodemesh/nodemesh/modifier.h b/thirdparty/nodemesh/nodemesh/modifier.h new file mode 100644 index 00000000..31e124a4 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/modifier.h @@ -0,0 +1,45 @@ +#ifndef NODEMESH_MODIFIER_H +#define NODEMESH_MODIFIER_H +#include +#include + +namespace nodemesh +{ + +class Modifier +{ +public: + struct Node + { + QVector3D position; + float radius; + std::vector cutTemplate; + size_t originNodeIndex; + }; + + struct Edge + { + size_t firstNodeIndex; + size_t secondNodeIndex; + }; + + size_t addNode(const QVector3D &position, float radius, const std::vector &cutTemplate); + size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex); + void subdivide(); + void roundEnd(); + const std::vector &nodes(); + const std::vector &edges(); + void finalize(); + +private: + + std::vector m_nodes; + std::vector m_edges; + + void createIntermediateNode(const Node &firstNode, const Node &secondNode, float factor, Node *resultNode); + float averageCutTemplateEdgeLength(const std::vector &cutTemplate); +}; + +} + +#endif diff --git a/thirdparty/nodemesh/nodemesh/positionkey.cpp b/thirdparty/nodemesh/nodemesh/positionkey.cpp new file mode 100644 index 00000000..2c365605 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/positionkey.cpp @@ -0,0 +1,53 @@ +#include + +namespace nodemesh +{ + +long PositionKey::m_toIntFactor = 100000; + +PositionKey::PositionKey(const QVector3D &v) : + PositionKey(v.x(), v.y(), v.z()) +{ +} + +PositionKey::PositionKey(float x, float y, float z) +{ + m_position.setX(x); + m_position.setY(y); + m_position.setZ(z); + + m_intX = x * m_toIntFactor; + m_intY = y * m_toIntFactor; + m_intZ = z * m_toIntFactor; +} + +const QVector3D &PositionKey::position() const +{ + return m_position; +} + +bool PositionKey::operator <(const PositionKey &right) const +{ + if (m_intX < right.m_intX) + return true; + if (m_intX > right.m_intX) + return false; + if (m_intY < right.m_intY) + return true; + if (m_intY > right.m_intY) + return false; + if (m_intZ < right.m_intZ) + return true; + if (m_intZ > right.m_intZ) + return false; + return false; +} + +bool PositionKey::operator ==(const PositionKey &right) const +{ + return m_intX == right.m_intX && + m_intY == right.m_intY && + m_intZ == right.m_intZ; +} + +} diff --git a/thirdparty/nodemesh/nodemesh/positionkey.h b/thirdparty/nodemesh/nodemesh/positionkey.h new file mode 100644 index 00000000..c581e169 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/positionkey.h @@ -0,0 +1,28 @@ +#ifndef NODEMESH_POSITION_KEY_H +#define NODEMESH_POSITION_KEY_H +#include + +namespace nodemesh +{ + +class PositionKey +{ +public: + PositionKey(const QVector3D &v); + PositionKey(float x, float y, float z); + const QVector3D &position() const; + bool operator <(const PositionKey &right) const; + bool operator ==(const PositionKey &right) const; + +private: + long m_intX = 0; + long m_intY = 0; + long m_intZ = 0; + QVector3D m_position; + + static long m_toIntFactor; +}; + +}; + +#endif diff --git a/thirdparty/nodemesh/nodemesh/recombiner.cpp b/thirdparty/nodemesh/nodemesh/recombiner.cpp new file mode 100644 index 00000000..0c435fbc --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/recombiner.cpp @@ -0,0 +1,449 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_EDGE_LOOP_LENGTH 1000 + +namespace nodemesh +{ + +void Recombiner::setVertices(const std::vector *vertices, + const std::vector> *verticesSourceIndices) +{ + m_vertices = vertices; + m_verticesSourceIndices = verticesSourceIndices; +} + +void Recombiner::setFaces(const std::vector> *faces) +{ + m_faces = faces; +} + +bool Recombiner::convertHalfEdgesToEdgeLoops(const std::vector> &halfEdges, + std::vector> *edgeLoops) +{ + std::map vertexLinkMap; + for (const auto &halfEdge: halfEdges) { + auto inserResult = vertexLinkMap.insert(halfEdge); + if (!inserResult.second) { + qDebug() << "Create edge loop from half edge failed, found repeated vertex link" << halfEdge.first << "->" << halfEdge.second << "exist:" << inserResult.first->first << "->" << inserResult.first->second; + return false; + } + } + while (!vertexLinkMap.empty()) { + std::vector edgeLoop; + size_t vertex = vertexLinkMap.begin()->first; + size_t head = vertex; + bool loopBack = false; + size_t limitLoop = MAX_EDGE_LOOP_LENGTH; + while ((limitLoop--) > 0) { + edgeLoop.push_back(vertex); + auto findNext = vertexLinkMap.find(vertex); + if (findNext == vertexLinkMap.end()) + break; + vertex = findNext->second; + if (vertex == head) { + loopBack = true; + break; + } + } + if (!loopBack) { + qDebug() << "Create edge loop from half edge failed, edge doesn't loop back"; + return false; + } + if (edgeLoop.size() < 3) { + qDebug() << "Create edge loop from half edge failed, edge loop size invalid:" << edgeLoop.size(); + return false; + } + for (const auto &vertex: edgeLoop) { + vertexLinkMap.erase(vertex); + } + edgeLoops->push_back(edgeLoop); + } + return true; +} + +size_t Recombiner::splitSeamVerticesToIslands(const std::map> &seamEdges, + std::map *vertexToIslandMap) +{ + std::set visited; + size_t nextIslandId = 0; + for (const auto &it: seamEdges) { + std::queue vertices; + vertices.push(it.first); + bool hasVertexJoin = false; + while (!vertices.empty()) { + auto v = vertices.front(); + vertices.pop(); + if (visited.find(v) != visited.end()) + continue; + visited.insert(v); + vertexToIslandMap->insert({v, nextIslandId}); + hasVertexJoin = true; + const auto findNeighbors = seamEdges.find(v); + if (findNeighbors != seamEdges.end()) { + for (const auto &neighbor: findNeighbors->second) { + vertices.push(neighbor); + } + } + } + if (hasVertexJoin) + ++nextIslandId; + } + return nextIslandId; +} + +bool Recombiner::buildHalfEdgeToFaceMap(std::map, size_t> &halfEdgeToFaceMap) +{ + bool succeed = true; + for (size_t faceIndex = 0; faceIndex < m_faces->size(); ++faceIndex) { + const auto &face = (*m_faces)[faceIndex]; + for (size_t i = 0; i < face.size(); ++i) { + size_t j = (i + 1) % face.size(); + const auto insertResult = halfEdgeToFaceMap.insert({{face[i], face[j]}, faceIndex}); + if (!insertResult.second) { + qDebug() << "Non manifold edge found:" << face[i] << face[j]; + succeed = false; + } + } + } + return succeed; +} + +bool Recombiner::recombine() +{ + buildHalfEdgeToFaceMap(m_halfEdgeToFaceMap); + + std::map> seamLink; + for (const auto &face: *m_faces) { + for (size_t i = 0; i < face.size(); ++i) { + const auto &index = face[i]; + auto source = (*m_verticesSourceIndices)[index]; + if (Combiner::Source::None == source.first) { + auto next = face[(i + 1) % face.size()]; + auto nextSource = (*m_verticesSourceIndices)[next]; + if (Combiner::Source::None == nextSource.first) { + seamLink[index].push_back(next); + } + } + } + } + std::map seamVertexToIslandMap; + size_t islands = splitSeamVerticesToIslands(seamLink, &seamVertexToIslandMap); + qDebug() << "Seam islands:" << islands; + + std::map, std::pair> edgesInSeamArea; + for (size_t faceIndex = 0; faceIndex < (*m_faces).size(); ++faceIndex) { + const auto &face = (*m_faces)[faceIndex]; + bool containsSeamVertex = false; + bool inFirstGroup = false; + size_t island = 0; + for (size_t i = 0; i < face.size(); ++i) { + const auto &index = face[i]; + auto source = (*m_verticesSourceIndices)[index]; + if (Combiner::Source::None == source.first) { + const auto &findIsland = seamVertexToIslandMap.find(index); + if (findIsland != seamVertexToIslandMap.end()) { + containsSeamVertex = true; + island = findIsland->second; + } + } else if (Combiner::Source::First == source.first) { + inFirstGroup = true; + } + } + if (containsSeamVertex) { + m_facesInSeamArea.insert({faceIndex, island}); + for (size_t i = 0; i < face.size(); ++i) { + const auto &index = face[i]; + const auto &next = face[(i + 1) % face.size()]; + std::pair edge = {index, next}; + edgesInSeamArea.insert({edge, {island, inFirstGroup}}); + } + } + } + + struct IslandData + { + std::vector> halfedges[2]; + std::vector> edgeLoops[2]; + }; + std::map islandsMap; + + for (const auto &edge: edgesInSeamArea) { + if (edgesInSeamArea.find({edge.first.second, edge.first.first}) == edgesInSeamArea.end()) { + islandsMap[edge.second.first].halfedges[edge.second.second ? 0 : 1].push_back(edge.first); + } + } + for (auto &it: islandsMap) { + for (size_t side = 0; side < 2; ++side) { + if (!convertHalfEdgesToEdgeLoops(it.second.halfedges[side], &it.second.edgeLoops[side])) { + qDebug() << "Convert half edges to edge loops for island" << it.first << "side" << side << "failed"; + it.second.edgeLoops[side].clear(); + } + } + } + + for (auto &it: islandsMap) { + for (size_t side = 0; side < 2; ++side) { + for (size_t i = 0; i < it.second.edgeLoops[side].size(); ++i) { + auto &edgeLoop = it.second.edgeLoops[side][i]; + size_t totalAdjustedTriangles = 0; + size_t adjustedTriangles = 0; + while ((adjustedTriangles=adjustTrianglesFromSeam(edgeLoop, it.first)) > 0) { + totalAdjustedTriangles += adjustedTriangles; + } + qDebug() << "Island" << it.first << "side" << side << "edge loop" << i << "adjusted" << totalAdjustedTriangles << "triangles"; + } + } + } + + for (auto &it: islandsMap) { + if (1 == it.second.edgeLoops[0].size() && + it.second.edgeLoops[0].size() == it.second.edgeLoops[1].size()) { + if (bridge(it.second.edgeLoops[0][0], it.second.edgeLoops[1][0])) { + m_goodSeams.insert(it.first); + } + } + } + //for (auto &it: islandsMap) { + // m_goodSeams.insert(it.first); + //} + + copyNonSeamFacesAsRegenerated(); + removeReluctantVertices(); + + qDebug() << "Optimized" << m_goodSeams.size() << "seams"; + + return true; +} + +size_t Recombiner::adjustTrianglesFromSeam(std::vector &edgeLoop, size_t seamIndex) +{ + if (edgeLoop.size() <= 3) + return 0; + + std::vector halfEdgeToFaces; + for (size_t i = 0; i < edgeLoop.size(); ++i) { + size_t j = (i + 1) % edgeLoop.size(); + auto findFace = m_halfEdgeToFaceMap.find({edgeLoop[j], edgeLoop[i]}); + if (findFace == m_halfEdgeToFaceMap.end()) { + qDebug() << "Find face for half edge failed:" << edgeLoop[j] << edgeLoop[i]; + return 0; + } + halfEdgeToFaces.push_back(findFace->second); + } + + std::vector removedFaceIndices; + std::set ignored; + for (size_t i = 0; i < edgeLoop.size(); ++i) { + size_t j = (i + 1) % edgeLoop.size(); + if (halfEdgeToFaces[i] == halfEdgeToFaces[j]) { + removedFaceIndices.push_back(halfEdgeToFaces[i]); + ignored.insert(edgeLoop[j]); + ++i; + continue; + } + } + + if (!ignored.empty()) { + std::vector newEdgeLoop; + for (const auto &v: edgeLoop) { + if (ignored.find(v) != ignored.end()) + continue; + newEdgeLoop.push_back(v); + } + if (newEdgeLoop.size() < 3) + return 0; + edgeLoop = newEdgeLoop; + for (const auto &faceIndex: removedFaceIndices) + m_facesInSeamArea.insert({faceIndex, seamIndex}); + } + + return ignored.size(); +} + +size_t Recombiner::otherVertexOfTriangle(const std::vector &face, const std::vector &indices) +{ + for (const auto &v: face) { + for (const auto &u: indices) { + if (u != v) + return u; + } + } + return face[0]; +} + +void Recombiner::copyNonSeamFacesAsRegenerated() +{ + for (size_t faceIndex = 0; faceIndex < m_faces->size(); ++faceIndex) { + const auto &findFaceInSeam = m_facesInSeamArea.find(faceIndex); + if (findFaceInSeam != m_facesInSeamArea.end() && + m_goodSeams.find(findFaceInSeam->second) != m_goodSeams.end()) + continue; + m_regeneratedFaces.push_back((*m_faces)[faceIndex]); + } +} + +const std::vector &Recombiner::regeneratedVertices() +{ + return m_regeneratedVertices; +} + +const std::vector> &Recombiner::regeneratedVerticesSourceIndices() +{ + return m_regeneratedVerticesSourceIndices; +} + +const std::vector> &Recombiner::regeneratedFaces() +{ + return m_regeneratedFaces; +} + +size_t Recombiner::nearestIndex(const QVector3D &position, const std::vector &edgeLoop) +{ + float minDist2 = std::numeric_limits::max(); + size_t choosenIndex = 0; + for (size_t i = 0; i < edgeLoop.size(); ++i) { + float dist2 = ((*m_vertices)[edgeLoop[i]] - position).lengthSquared(); + if (dist2 < minDist2) { + minDist2 = dist2; + choosenIndex = i; + } + } + return choosenIndex; +} + +bool Recombiner::bridge(const std::vector &first, const std::vector &second) +{ + const std::vector *large = &first; + const std::vector *small = &second; + if (large->size() < small->size()) + std::swap(large, small); + std::vector> matchedPairs; + std::map nearestIndicesFromLargeToSmall; + for (size_t i = 0; i < small->size(); ++i) { + const auto &positionOnSmall = (*m_vertices)[(*small)[i]]; + size_t nearestIndexOnLarge = nearestIndex(positionOnSmall, *large); + auto matchResult = nearestIndicesFromLargeToSmall.find(nearestIndexOnLarge); + size_t nearestIndexOnSmall; + if (matchResult == nearestIndicesFromLargeToSmall.end()) { + const auto &positionOnLarge = (*m_vertices)[(*large)[nearestIndexOnLarge]]; + nearestIndexOnSmall = nearestIndex(positionOnLarge, *small); + nearestIndicesFromLargeToSmall.insert({nearestIndexOnLarge, nearestIndexOnSmall}); + } else { + nearestIndexOnSmall = matchResult->second; + } + if (nearestIndexOnSmall == i) { + matchedPairs.push_back({nearestIndexOnSmall, nearestIndexOnLarge}); + } + } + + qDebug() << "small:" << small->size() << "large:" << large->size() << "matches:" << matchedPairs.size(); + + if (matchedPairs.empty()) + return false; + + for (size_t i = 0; i < matchedPairs.size(); ++i) { + size_t j = (i + 1) % matchedPairs.size(); + std::vector smallSide; + std::vector largeSide; + for (size_t indexOnSmall = matchedPairs[i].first; + ; + indexOnSmall = (indexOnSmall + 1) % small->size()) { + smallSide.push_back((*small)[indexOnSmall]); + if (indexOnSmall == matchedPairs[j].first) + break; + } + for (size_t indexOnLarge = matchedPairs[j].second; + ; + indexOnLarge = (indexOnLarge + 1) % large->size()) { + largeSide.push_back((*large)[indexOnLarge]); + if (indexOnLarge == matchedPairs[i].second) + break; + } + std::reverse(largeSide.begin(), largeSide.end()); + fillPairs(smallSide, largeSide); + } + + return true; +} + +void Recombiner::fillPairs(const std::vector &small, const std::vector &large) +{ + size_t smallIndex = 0; + size_t largeIndex = 0; + while (smallIndex + 1 < small.size() || + largeIndex + 1 < large.size()) { + if (smallIndex + 1 < small.size() && largeIndex + 1 < large.size()) { + float angleOnSmallEdgeLoop = angleBetween((*m_vertices)[large[largeIndex]] - (*m_vertices)[small[smallIndex]], + (*m_vertices)[small[smallIndex + 1]] - (*m_vertices)[small[smallIndex]]); + float angleOnLargeEdgeLoop = angleBetween((*m_vertices)[small[smallIndex]] - (*m_vertices)[large[largeIndex]], + (*m_vertices)[large[largeIndex + 1]] - (*m_vertices)[large[largeIndex]]); + if (angleOnSmallEdgeLoop < angleOnLargeEdgeLoop) { + m_regeneratedFaces.push_back({ + small[smallIndex], + small[smallIndex + 1], + large[largeIndex] + }); + ++smallIndex; + continue; + } + m_regeneratedFaces.push_back({ + large[largeIndex + 1], + large[largeIndex], + small[smallIndex] + }); + ++largeIndex; + continue; + } + if (smallIndex + 1 >= small.size()) { + m_regeneratedFaces.push_back({ + large[largeIndex + 1], + large[largeIndex], + small[smallIndex] + }); + ++largeIndex; + continue; + } + if (largeIndex + 1 >= large.size()) { + m_regeneratedFaces.push_back({ + small[smallIndex], + small[smallIndex + 1], + large[largeIndex] + }); + ++smallIndex; + continue; + } + break; + } +} + +void Recombiner::removeReluctantVertices() +{ + std::vector> rearrangedFaces; + std::map oldToNewIndexMap; + for (const auto &face: m_regeneratedFaces) { + std::vector newFace; + for (const auto &index: face) { + const auto &findIndex = oldToNewIndexMap.find(index); + if (findIndex != oldToNewIndexMap.end()) { + newFace.push_back(findIndex->second); + } else { + size_t newIndex = m_regeneratedVertices.size(); + m_regeneratedVertices.push_back((*m_vertices)[index]); + m_regeneratedVerticesSourceIndices.push_back((*m_verticesSourceIndices)[index]); + oldToNewIndexMap.insert({index, newIndex}); + newFace.push_back(newIndex); + } + } + rearrangedFaces.push_back(newFace); + } + m_regeneratedFaces = rearrangedFaces; +} + +} diff --git a/thirdparty/nodemesh/nodemesh/recombiner.h b/thirdparty/nodemesh/nodemesh/recombiner.h new file mode 100644 index 00000000..5933ab8b --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/recombiner.h @@ -0,0 +1,52 @@ +#ifndef NODEMESH_RECOMBINER_H +#define NODEMESH_RECOMBINER_H +#include +#include +#include +#include +#include + +namespace nodemesh +{ + +class Recombiner +{ +public: + void setVertices(const std::vector *vertices, + const std::vector> *verticesSourceIndices); + void setFaces(const std::vector> *faces); + const std::vector ®eneratedVertices(); + const std::vector> ®eneratedVerticesSourceIndices(); + const std::vector> ®eneratedFaces(); + bool recombine(); + +private: + const std::vector *m_vertices = nullptr; + const std::vector> *m_verticesSourceIndices = nullptr; + const std::vector> *m_faces = nullptr; + std::vector m_regeneratedVertices; + std::vector> m_regeneratedVerticesSourceIndices; + std::vector> m_regeneratedFaces; + std::map, size_t> m_halfEdgeToFaceMap; + std::map m_facesInSeamArea; + std::set m_goodSeams; + + bool addFaceToHalfEdgeToFaceMap(size_t faceIndex, + std::map, size_t> &halfEdgeToFaceMap); + bool buildHalfEdgeToFaceMap(std::map, size_t> &halfEdgeToFaceMap); + bool convertHalfEdgesToEdgeLoops(const std::vector> &halfEdges, + std::vector> *edgeLoops); + size_t splitSeamVerticesToIslands(const std::map> &seamEdges, + std::map *vertexToIslandMap); + void copyNonSeamFacesAsRegenerated(); + size_t adjustTrianglesFromSeam(std::vector &edgeLoop, size_t seamIndex); + size_t otherVertexOfTriangle(const std::vector &face, const std::vector &indices); + bool bridge(const std::vector &first, const std::vector &second); + size_t nearestIndex(const QVector3D &position, const std::vector &edgeLoop); + void removeReluctantVertices(); + void fillPairs(const std::vector &small, const std::vector &large); +}; + +} + +#endif diff --git a/thirdparty/nodemesh/nodemesh/stitcher.cpp b/thirdparty/nodemesh/nodemesh/stitcher.cpp new file mode 100644 index 00000000..2c8180e2 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/stitcher.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +namespace nodemesh +{ + +Stitcher::~Stitcher() +{ + delete m_wrapper; +} + +void Stitcher::setVertices(const std::vector *vertices) +{ + m_positions = vertices; +} + +const std::vector> &Stitcher::newlyGeneratedFaces() +{ + if (m_wrapper) + return m_wrapper->newlyGeneratedFaces(); + + return m_newlyGeneratedFaces; +} + +void Stitcher::getFailedEdgeLoops(std::vector &failedEdgeLoops) +{ + if (m_wrapper) + m_wrapper->getFailedEdgeLoops(failedEdgeLoops); +} + +bool Stitcher::stitchByQuads(const std::vector, QVector3D>> &edgeLoops) +{ + if (2 != edgeLoops.size()) + return false; + const auto &firstEdgeLoop = edgeLoops[0].first; + const auto &secondEdgeLoop = edgeLoops[1].first; + if (firstEdgeLoop.size() < 3 || firstEdgeLoop.size() != secondEdgeLoop.size()) + return false; + float minDist2 = std::numeric_limits::max(); + size_t choosenStartIndex = 0; + for (size_t k = 0; k < secondEdgeLoop.size(); ++k) { + float sumOfDist2 = 0; + std::vector edges; + for (size_t i = 0, j = k; i < firstEdgeLoop.size(); ++i, --j) { + const auto &positionOnFirstEdgeLoop = (*m_positions)[firstEdgeLoop[i % firstEdgeLoop.size()]]; + const auto &positionOnSecondEdgeLoop = (*m_positions)[secondEdgeLoop[(j + secondEdgeLoop.size()) % secondEdgeLoop.size()]]; + auto edge = positionOnSecondEdgeLoop - positionOnFirstEdgeLoop; + sumOfDist2 += edge.lengthSquared(); + } + if (sumOfDist2 < minDist2) { + minDist2 = sumOfDist2; + choosenStartIndex = k; + } + } + for (size_t i = 0, j = choosenStartIndex; i < firstEdgeLoop.size(); ++i, --j) { + m_newlyGeneratedFaces.push_back({ + secondEdgeLoop[(j + secondEdgeLoop.size()) % secondEdgeLoop.size()], + secondEdgeLoop[(j + secondEdgeLoop.size() - 1) % secondEdgeLoop.size()], + firstEdgeLoop[(i + 1) % firstEdgeLoop.size()], + firstEdgeLoop[i % firstEdgeLoop.size()], + }); + } + return true; +} + +bool Stitcher::stitch(const std::vector, QVector3D>> &edgeLoops) +{ + if (edgeLoops.size() == 2 && + edgeLoops[0].first.size() == edgeLoops[1].first.size()) + return stitchByQuads(edgeLoops); + + m_wrapper = new Wrapper; + m_wrapper->setVertices(m_positions); + m_wrapper->wrap(edgeLoops); + if (!m_wrapper->finished()) { + qDebug() << "Wrapping failed"; + return false; + } + return true; +} + +} + + diff --git a/thirdparty/nodemesh/nodemesh/stitcher.h b/thirdparty/nodemesh/nodemesh/stitcher.h new file mode 100644 index 00000000..3666c688 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/stitcher.h @@ -0,0 +1,31 @@ +#ifndef NODEMESH_STITCHER_H +#define NODEMESH_STITCHER_H +#include +#include +#include + +namespace nodemesh +{ + +class Stitcher +{ +public: + ~Stitcher(); + void setVertices(const std::vector *vertices); + bool stitch(const std::vector, QVector3D>> &edgeLoops); + const std::vector> &newlyGeneratedFaces(); + void getFailedEdgeLoops(std::vector &failedEdgeLoops); + +private: + const std::vector *m_positions; + std::vector> m_newlyGeneratedFaces; + Wrapper *m_wrapper = nullptr; + + bool stitchByQuads(const std::vector, QVector3D>> &edgeLoops); +}; + +} + + +#endif + diff --git a/thirdparty/nodemesh/nodemesh/util.cpp b/thirdparty/nodemesh/nodemesh/util.cpp new file mode 100644 index 00000000..4f5c6e7c --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/util.cpp @@ -0,0 +1,492 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace nodemesh +{ + +float radianToDegree(float r) +{ + return r * 180.0 / M_PI; +} + +float angleBetween(const QVector3D &v1, const QVector3D &v2) +{ + return atan2(QVector3D::crossProduct(v1, v2).length(), QVector3D::dotProduct(v1, v2)); +} + +float degreeBetween(const QVector3D &v1, const QVector3D &v2) +{ + return radianToDegree(angleBetween(v1, v2)); +} + +float degreeBetweenIn360(const QVector3D &a, const QVector3D &b, const QVector3D &direct) +{ + auto angle = radianToDegree(angleBetween(a, b)); + auto c = QVector3D::crossProduct(a, b); + if (QVector3D::dotProduct(c, direct) < 0) { + angle += 180; + } + return angle; +} + +QVector3D polygonNormal(const std::vector &vertices, const std::vector &polygon) +{ + QVector3D normal; + for (size_t i = 0; i < polygon.size(); ++i) { + auto j = (i + 1) % polygon.size(); + auto k = (i + 2) % polygon.size(); + const auto &enter = vertices[polygon[i]]; + const auto &cone = vertices[polygon[j]]; + const auto &leave = vertices[polygon[k]]; + normal += QVector3D::normal(enter, cone, leave); + } + return normal.normalized(); +} + +bool pointInTriangle(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &p) +{ + auto u = b - a; + auto v = c - a; + auto w = p - a; + auto vXw = QVector3D::crossProduct(v, w); + auto vXu = QVector3D::crossProduct(v, u); + if (QVector3D::dotProduct(vXw, vXu) < 0.0) { + return false; + } + auto uXw = QVector3D::crossProduct(u, w); + auto uXv = QVector3D::crossProduct(u, v); + if (QVector3D::dotProduct(uXw, uXv) < 0.0) { + return false; + } + auto denom = uXv.length(); + auto r = vXw.length() / denom; + auto t = uXw.length() / denom; + return r + t <= 1.0; +} + +void triangulate(const std::vector &vertices, const std::vector> &faces, std::vector> &triangles) +{ + std::vector> rings; + for (const auto &face: faces) { + if (face.size() > 3) { + rings.push_back(face); + } else { + triangles.push_back(face); + } + } + for (const auto &ring: rings) { + std::vector fillRing = ring; + QVector3D direct = polygonNormal(vertices, fillRing); + while (fillRing.size() > 3) { + bool newFaceGenerated = false; + for (decltype(fillRing.size()) i = 0; i < fillRing.size(); ++i) { + auto j = (i + 1) % fillRing.size(); + auto k = (i + 2) % fillRing.size(); + const auto &enter = vertices[fillRing[i]]; + const auto &cone = vertices[fillRing[j]]; + const auto &leave = vertices[fillRing[k]]; + auto angle = degreeBetweenIn360(cone - enter, leave - cone, direct); + if (angle >= 1.0 && angle <= 179.0) { + bool isEar = true; + for (size_t x = 0; x < fillRing.size() - 3; ++x) { + auto fourth = vertices[(i + 3 + k) % fillRing.size()]; + if (pointInTriangle(enter, cone, leave, fourth)) { + isEar = false; + break; + } + } + if (isEar) { + std::vector newFace = { + fillRing[i], + fillRing[j], + fillRing[k], + }; + triangles.push_back(newFace); + fillRing.erase(fillRing.begin() + j); + newFaceGenerated = true; + break; + } + } + } + if (!newFaceGenerated) + break; + } + if (fillRing.size() == 3) { + std::vector newFace = { + fillRing[0], + fillRing[1], + fillRing[2], + }; + triangles.push_back(newFace); + } else { + qDebug() << "Triangulate failed, ring size:" << fillRing.size(); + } + } +} + +void exportMeshAsObj(const std::vector &vertices, const std::vector> &faces, const QString &filename, const std::set *excludeFacesOfVertices) +{ + QFile file(filename); + if (file.open(QIODevice::WriteOnly)) { + QTextStream stream(&file); + stream << "# Generated by nodemesh, a library from Dust3D https://dust3d.org" << endl; + for (std::vector::const_iterator it = vertices.begin() ; it != vertices.end(); ++it) { + stream << "v " << (*it).x() << " " << (*it).y() << " " << (*it).z() << endl; + } + for (std::vector>::const_iterator it = faces.begin() ; it != faces.end(); ++it) { + bool excluded = false; + for (std::vector::const_iterator subIt = (*it).begin() ; subIt != (*it).end(); ++subIt) { + if (excludeFacesOfVertices && excludeFacesOfVertices->find(*subIt) != excludeFacesOfVertices->end()) { + excluded = true; + break; + } + } + if (excluded) + continue; + stream << "f"; + for (std::vector::const_iterator subIt = (*it).begin() ; subIt != (*it).end(); ++subIt) { + stream << " " << (1 + *subIt); + } + stream << endl; + } + } +} + +void exportMeshAsObjWithNormals(const std::vector &vertices, const std::vector> &faces, const QString &filename, + const std::vector &triangleVertexNormals) +{ + QFile file(filename); + if (file.open(QIODevice::WriteOnly)) { + QTextStream stream(&file); + stream << "# Generated by nodemesh, a library from Dust3D https://dust3d.org" << endl; + for (std::vector::const_iterator it = vertices.begin() ; it != vertices.end(); ++it) { + stream << "v " << (*it).x() << " " << (*it).y() << " " << (*it).z() << endl; + } + for (std::vector::const_iterator it = triangleVertexNormals.begin() ; it != triangleVertexNormals.end(); ++it) { + stream << "vn " << (*it).x() << " " << (*it).y() << " " << (*it).z() << endl; + } + size_t normalIndex = 0; + for (std::vector>::const_iterator it = faces.begin() ; it != faces.end(); ++it) { + stream << "f"; + for (std::vector::const_iterator subIt = (*it).begin() ; subIt != (*it).end(); ++subIt) { + ++normalIndex; + stream << " " << QString::number(1 + *subIt) + "//" + QString::number(normalIndex); + } + stream << endl; + } + } +} + +float areaOfTriangle(const QVector3D &a, const QVector3D &b, const QVector3D &c) +{ + auto ab = b - a; + auto ac = c - a; + return 0.5 * QVector3D::crossProduct(ab, ac).length(); +} + +void angleSmooth(const std::vector &vertices, + const std::vector> &triangles, + const std::vector &triangleNormals, + float thresholdAngleDegrees, + std::vector &triangleVertexNormals) +{ + std::vector>> triangleVertexNormalsMapByIndices(vertices.size()); + std::vector angleAreaWeightedNormals; + for (size_t triangleIndex = 0; triangleIndex < triangles.size(); ++triangleIndex) { + const auto &sourceTriangle = triangles[triangleIndex]; + if (sourceTriangle.size() != 3) { + qDebug() << "Encounter non triangle"; + continue; + } + const auto &v1 = vertices[sourceTriangle[0]]; + const auto &v2 = vertices[sourceTriangle[1]]; + const auto &v3 = vertices[sourceTriangle[2]]; + float area = areaOfTriangle(v1, v2, v3); + float angles[] = {radianToDegree(angleBetween(v2-v1, v3-v1)), + radianToDegree(angleBetween(v1-v2, v3-v2)), + radianToDegree(angleBetween(v1-v3, v2-v3))}; + for (int i = 0; i < 3; ++i) { + if (sourceTriangle[i] >= vertices.size()) { + qDebug() << "Invalid vertex index" << sourceTriangle[i] << "vertices size" << vertices.size(); + continue; + } + triangleVertexNormalsMapByIndices[sourceTriangle[i]].push_back({triangleIndex, angleAreaWeightedNormals.size()}); + angleAreaWeightedNormals.push_back(triangleNormals[triangleIndex] * area * angles[i]); + } + } + triangleVertexNormals = angleAreaWeightedNormals; + std::map, float> degreesBetweenFacesMap; + for (size_t vertexIndex = 0; vertexIndex < vertices.size(); ++vertexIndex) { + const auto &triangleVertices = triangleVertexNormalsMapByIndices[vertexIndex]; + for (const auto &triangleVertex: triangleVertices) { + for (const auto &otherTriangleVertex: triangleVertices) { + if (triangleVertex.first == otherTriangleVertex.first) + continue; + float degrees = 0; + auto findDegreesResult = degreesBetweenFacesMap.find({triangleVertex.first, otherTriangleVertex.first}); + if (findDegreesResult == degreesBetweenFacesMap.end()) { + degrees = angleBetween(triangleNormals[triangleVertex.first], triangleNormals[otherTriangleVertex.first]); + degreesBetweenFacesMap.insert({{triangleVertex.first, otherTriangleVertex.first}, degrees}); + degreesBetweenFacesMap.insert({{otherTriangleVertex.first, triangleVertex.first}, degrees}); + } else { + degrees = findDegreesResult->second; + } + if (degrees > thresholdAngleDegrees) { + continue; + } + triangleVertexNormals[triangleVertex.second] += angleAreaWeightedNormals[otherTriangleVertex.second]; + } + } + } + for (auto &item: triangleVertexNormals) + item.normalize(); +} + +void recoverQuads(const std::vector &vertices, const std::vector> &triangles, const std::set> &sharedQuadEdges, std::vector> &triangleAndQuads) +{ + std::vector verticesPositionKeys; + for (const auto &position: vertices) { + verticesPositionKeys.push_back(PositionKey(position)); + } + std::map, std::pair> triangleEdgeMap; + for (size_t i = 0; i < triangles.size(); i++) { + const auto &faceIndices = triangles[i]; + if (faceIndices.size() == 3) { + triangleEdgeMap[std::make_pair(faceIndices[0], faceIndices[1])] = std::make_pair(i, faceIndices[2]); + triangleEdgeMap[std::make_pair(faceIndices[1], faceIndices[2])] = std::make_pair(i, faceIndices[0]); + triangleEdgeMap[std::make_pair(faceIndices[2], faceIndices[0])] = std::make_pair(i, faceIndices[1]); + } + } + std::unordered_set unionedFaces; + std::vector> newUnionedFaceIndices; + for (const auto &edge: triangleEdgeMap) { + if (unionedFaces.find(edge.second.first) != unionedFaces.end()) + continue; + auto pair = std::make_pair(verticesPositionKeys[edge.first.first], verticesPositionKeys[edge.first.second]); + if (sharedQuadEdges.find(pair) != sharedQuadEdges.end()) { + auto oppositeEdge = triangleEdgeMap.find(std::make_pair(edge.first.second, edge.first.first)); + if (oppositeEdge == triangleEdgeMap.end()) { + qDebug() << "Find opposite edge failed"; + } else { + if (unionedFaces.find(oppositeEdge->second.first) == unionedFaces.end()) { + unionedFaces.insert(edge.second.first); + unionedFaces.insert(oppositeEdge->second.first); + std::vector indices; + indices.push_back(edge.second.second); + indices.push_back(edge.first.first); + indices.push_back(oppositeEdge->second.second); + indices.push_back(edge.first.second); + triangleAndQuads.push_back(indices); + } + } + } + } + for (size_t i = 0; i < triangles.size(); i++) { + if (unionedFaces.find(i) == unionedFaces.end()) { + triangleAndQuads.push_back(triangles[i]); + } + } +} + +size_t weldSeam(const std::vector &sourceVertices, const std::vector> &sourceTriangles, + float allowedSmallestDistance, const std::set &excludePositions, + std::vector &destVertices, std::vector> &destTriangles) +{ + std::unordered_set excludeVertices; + for (size_t i = 0; i < sourceVertices.size(); ++i) { + if (excludePositions.find(sourceVertices[i]) != excludePositions.end()) + excludeVertices.insert(i); + } + float squareOfAllowedSmallestDistance = allowedSmallestDistance * allowedSmallestDistance; + std::map weldVertexToMap; + std::unordered_set weldTargetVertices; + std::unordered_set processedFaces; + std::map, std::pair> triangleEdgeMap; + std::unordered_map vertexAdjFaceCountMap; + for (int i = 0; i < (int)sourceTriangles.size(); i++) { + const auto &faceIndices = sourceTriangles[i]; + if (faceIndices.size() == 3) { + vertexAdjFaceCountMap[faceIndices[0]]++; + vertexAdjFaceCountMap[faceIndices[1]]++; + vertexAdjFaceCountMap[faceIndices[2]]++; + triangleEdgeMap[std::make_pair(faceIndices[0], faceIndices[1])] = std::make_pair(i, faceIndices[2]); + triangleEdgeMap[std::make_pair(faceIndices[1], faceIndices[2])] = std::make_pair(i, faceIndices[0]); + triangleEdgeMap[std::make_pair(faceIndices[2], faceIndices[0])] = std::make_pair(i, faceIndices[1]); + } + } + for (int i = 0; i < (int)sourceTriangles.size(); i++) { + if (processedFaces.find(i) != processedFaces.end()) + continue; + const auto &faceIndices = sourceTriangles[i]; + if (faceIndices.size() == 3) { + bool indicesSeamCheck[3] = { + excludeVertices.find(faceIndices[0]) == excludeVertices.end(), + excludeVertices.find(faceIndices[1]) == excludeVertices.end(), + excludeVertices.find(faceIndices[2]) == excludeVertices.end() + }; + for (int j = 0; j < 3; j++) { + int next = (j + 1) % 3; + int nextNext = (j + 2) % 3; + if (indicesSeamCheck[j] && indicesSeamCheck[next]) { + std::pair edge = std::make_pair(faceIndices[j], faceIndices[next]); + int thirdVertexIndex = faceIndices[nextNext]; + if ((sourceVertices[edge.first] - sourceVertices[edge.second]).lengthSquared() < squareOfAllowedSmallestDistance) { + auto oppositeEdge = std::make_pair(edge.second, edge.first); + auto findOppositeFace = triangleEdgeMap.find(oppositeEdge); + if (findOppositeFace == triangleEdgeMap.end()) { + qDebug() << "Find opposite edge failed"; + continue; + } + int oppositeFaceIndex = findOppositeFace->second.first; + if (((sourceVertices[edge.first] - sourceVertices[thirdVertexIndex]).lengthSquared() < + (sourceVertices[edge.second] - sourceVertices[thirdVertexIndex]).lengthSquared()) && + vertexAdjFaceCountMap[edge.second] <= 4 && + weldVertexToMap.find(edge.second) == weldVertexToMap.end()) { + weldVertexToMap[edge.second] = edge.first; + weldTargetVertices.insert(edge.first); + processedFaces.insert(i); + processedFaces.insert(oppositeFaceIndex); + break; + } else if (vertexAdjFaceCountMap[edge.first] <= 4 && + weldVertexToMap.find(edge.first) == weldVertexToMap.end()) { + weldVertexToMap[edge.first] = edge.second; + weldTargetVertices.insert(edge.second); + processedFaces.insert(i); + processedFaces.insert(oppositeFaceIndex); + break; + } + } + } + } + } + } + int weldedCount = 0; + int faceCountAfterWeld = 0; + std::map oldToNewVerticesMap; + for (int i = 0; i < (int)sourceTriangles.size(); i++) { + const auto &faceIndices = sourceTriangles[i]; + std::vector mappedFaceIndices; + bool errored = false; + for (const auto &index: faceIndices) { + int finalIndex = index; + int mapTimes = 0; + while (mapTimes < 500) { + auto findMapResult = weldVertexToMap.find(finalIndex); + if (findMapResult == weldVertexToMap.end()) + break; + finalIndex = findMapResult->second; + mapTimes++; + } + if (mapTimes >= 500) { + qDebug() << "Map too much times"; + errored = true; + break; + } + mappedFaceIndices.push_back(finalIndex); + } + if (errored || mappedFaceIndices.size() < 3) + continue; + bool welded = false; + for (decltype(mappedFaceIndices.size()) j = 0; j < mappedFaceIndices.size(); j++) { + int next = (j + 1) % 3; + if (mappedFaceIndices[j] == mappedFaceIndices[next]) { + welded = true; + break; + } + } + if (welded) { + weldedCount++; + continue; + } + faceCountAfterWeld++; + std::vector newFace; + for (const auto &index: mappedFaceIndices) { + auto findMap = oldToNewVerticesMap.find(index); + if (findMap == oldToNewVerticesMap.end()) { + size_t newIndex = destVertices.size(); + newFace.push_back(newIndex); + destVertices.push_back(sourceVertices[index]); + oldToNewVerticesMap.insert({index, newIndex}); + } else { + newFace.push_back(findMap->second); + } + } + destTriangles.push_back(newFace); + } + return weldedCount; +} + +bool isManifold(const std::vector> &faces) +{ + std::set> halfEdges; + for (const auto &face: faces) { + for (size_t i = 0; i < face.size(); ++i) { + size_t j = (i + 1) % face.size(); + auto insertResult = halfEdges.insert({face[i], face[j]}); + if (!insertResult.second) + return false; + } + } + for (const auto &it: halfEdges) { + if (halfEdges.find({it.second, it.first}) == halfEdges.end()) + return false; + } + return true; +} + +void trim(std::vector *vertices, bool normalize) +{ + float xLow = std::numeric_limits::max(); + float xHigh = std::numeric_limits::lowest(); + float yLow = std::numeric_limits::max(); + float yHigh = std::numeric_limits::lowest(); + float zLow = std::numeric_limits::max(); + float zHigh = std::numeric_limits::lowest(); + for (const auto &position: *vertices) { + if (position.x() < xLow) + xLow = position.x(); + else if (position.x() > xHigh) + xHigh = position.x(); + if (position.y() < yLow) + yLow = position.y(); + else if (position.y() > yHigh) + yHigh = position.y(); + if (position.z() < zLow) + zLow = position.z(); + else if (position.z() > zHigh) + zHigh = position.z(); + } + float xMiddle = (xHigh + xLow) * 0.5; + float yMiddle = (yHigh + yLow) * 0.5; + float zMiddle = (zHigh + zLow) * 0.5; + if (normalize) { + float xSize = xHigh - xLow; + float ySize = yHigh - yLow; + float zSize = zHigh - zLow; + float longSize = ySize; + if (xSize > longSize) + longSize = xSize; + if (zSize > longSize) + longSize = zSize; + if (qFuzzyIsNull(longSize)) + longSize = 0.000001; + for (auto &position: *vertices) { + position.setX((position.x() - xMiddle) / longSize); + position.setY((position.y() - yMiddle) / longSize); + position.setZ((position.z() - zMiddle) / longSize); + } + } else { + for (auto &position: *vertices) { + position.setX((position.x() - xMiddle)); + position.setY((position.y() - yMiddle)); + position.setZ((position.z() - zMiddle)); + } + } +} + +} diff --git a/thirdparty/nodemesh/nodemesh/util.h b/thirdparty/nodemesh/nodemesh/util.h new file mode 100644 index 00000000..6e261f36 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/util.h @@ -0,0 +1,38 @@ +#ifndef NODEMESH_UTIL_H +#define NODEMESH_UTIL_H +#include +#include +#include +#include +#include + +namespace nodemesh +{ + +float angleBetween(const QVector3D &v1, const QVector3D &v2); +float radianToDegree(float r); +float degreeBetween(const QVector3D &v1, const QVector3D &v2); +float degreeBetweenIn360(const QVector3D &a, const QVector3D &b, const QVector3D &direct); +bool pointInTriangle(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &p); +QVector3D polygonNormal(const std::vector &vertices, const std::vector &polygon); +void triangulate(const std::vector &vertices, const std::vector> &faces, std::vector> &triangles); +void exportMeshAsObj(const std::vector &vertices, const std::vector> &faces, const QString &filename, const std::set *excludeFacesOfVertices=nullptr); +void exportMeshAsObjWithNormals(const std::vector &vertices, const std::vector> &faces, const QString &filename, + const std::vector &triangleVertexNormals); +float areaOfTriangle(const QVector3D &a, const QVector3D &b, const QVector3D &c); +void angleSmooth(const std::vector &vertices, + const std::vector> &triangles, + const std::vector &triangleNormals, + float thresholdAngleDegrees, + std::vector &triangleVertexNormals); +void recoverQuads(const std::vector &vertices, const std::vector> &triangles, const std::set> &sharedQuadEdges, std::vector> &triangleAndQuads); +size_t weldSeam(const std::vector &sourceVertices, const std::vector> &sourceTriangles, + float allowedSmallestDistance, const std::set &excludePositions, + std::vector &destVertices, std::vector> &destTriangles); +bool isManifold(const std::vector> &faces); +void trim(std::vector *vertices, bool normalize=false); + +} + +#endif + diff --git a/thirdparty/nodemesh/nodemesh/wrapper.cpp b/thirdparty/nodemesh/nodemesh/wrapper.cpp new file mode 100644 index 00000000..968401a7 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/wrapper.cpp @@ -0,0 +1,315 @@ +#include +#include +#include +#include + +namespace nodemesh +{ + +void Wrapper::setVertices(const std::vector *vertices) +{ + m_positions = vertices; +} + +void Wrapper::wrap(const std::vector, QVector3D>> &edgeLoops) +{ + size_t nextPlaneId = 1; + for (const auto &it: edgeLoops) { + addCandidateVertices(it.first, it.second, nextPlaneId++); + } + generate(); + finalize(); +} + +const std::vector> &Wrapper::newlyGeneratedFaces() +{ + return m_newlyGeneratedfaces; +} + +bool Wrapper::finished() +{ + if (!m_finalizeFinished) + return false; + if (m_candidates.empty()) + return true; + for (const auto &it: m_candidates) { + if (!isVertexClosed(it)) + return false; + } + return true; +} + +void Wrapper::getFailedEdgeLoops(std::vector &failedEdgeLoops) +{ + std::set edgeLoopIndices; + for (const auto &it: m_candidates) { + if (isVertexClosed(it)) + continue; + edgeLoopIndices.insert(m_sourceVertices[it].sourcePlane - 1); + } + for (const auto &index: edgeLoopIndices) { + failedEdgeLoops.push_back(index); + } +} + +void Wrapper::addCandidateVertices(const std::vector &vertices, const QVector3D &planeNormal, size_t planeId) +{ + std::map verticesIndexSet; + for (const auto &oldVertId: vertices) { + if (verticesIndexSet.find(oldVertId) == verticesIndexSet.end()) { + size_t index = addSourceVertex((*m_positions)[oldVertId], planeId, oldVertId); + verticesIndexSet.insert({oldVertId, index}); + } + } + for (size_t i = 0; i < vertices.size(); ++i) { + const auto &oldVertId = vertices[i]; + const auto &oldNextVertId = vertices[(i + 1) % vertices.size()]; + auto vertexIndex = verticesIndexSet[oldVertId]; + auto nextVertexIndex = verticesIndexSet[oldNextVertId]; + addStartup(nextVertexIndex, vertexIndex, planeNormal); + } +} + +size_t Wrapper::addSourceVertex(const QVector3D &position, size_t sourcePlane, size_t tag) +{ + auto addedIndex = m_sourceVertices.size(); + + SourceVertex sourceVertex; + sourceVertex.position = position; + sourceVertex.sourcePlane = sourcePlane; + sourceVertex.tag = tag; + sourceVertex.index = addedIndex; + + m_sourceVertices.push_back(sourceVertex); + m_candidates.push_back(addedIndex); + + return addedIndex; +} + +void Wrapper::addStartup(size_t p1, size_t p2, const QVector3D &baseNormal) +{ + if (m_items.empty()) + addItem(p1, p2, baseNormal); + m_generatedFaceEdgesMap.insert({WrapItemKey {.p1 = p2, .p2 = p1}, {0, false}}); +} + +QVector3D Wrapper::calculateFaceVector(size_t p1, size_t p2, const QVector3D &baseNormal) +{ + const auto &v1 = m_sourceVertices[p1]; + const auto &v2 = m_sourceVertices[p2]; + auto seg = v2.position - v1.position; + return QVector3D::crossProduct(seg, baseNormal); +} + +void Wrapper::addItem(size_t p1, size_t p2, const QVector3D &baseNormal) +{ + const auto &v1 = m_sourceVertices[p1]; + const auto &v2 = m_sourceVertices[p2]; + if (!m_items.empty() && v1.sourcePlane == v2.sourcePlane) + return; + if (findItem(p1, p2).second || findItem(p2, p1).second) + return; + if (isEdgeGenerated(p1, p2) || isEdgeGenerated(p2, p1)) + return; + auto index = m_items.size(); + m_items.push_back(WrapItem {.p3 = 0, .p1 = p1, .p2 = p2, .baseNormal = baseNormal, .processed = false}); + m_itemsMap.insert({WrapItemKey {.p1 = p1, .p2 = p2}, index}); + m_itemsList.push_front(index); +} + +std::pair Wrapper::findItem(size_t p1, size_t p2) +{ + auto key = WrapItemKey {.p1 = p1, .p2 = p2}; + auto findResult = m_itemsMap.find(key); + if (findResult == m_itemsMap.end()) { + return {0, false}; + } + return {findResult->second, true}; +} + +bool Wrapper::isEdgeGenerated(size_t p1, size_t p2) +{ + auto key = WrapItemKey {.p1 = p1, .p2 = p2}; + if (m_generatedFaceEdgesMap.find(key) == m_generatedFaceEdgesMap.end()) + return false; + return true; +} + +float Wrapper::angleOfBaseFaceAndPoint(size_t itemIndex, size_t vertexIndex) +{ + const auto &item = m_items[itemIndex]; + if (item.p1 == vertexIndex || item.p2 == vertexIndex) + return 0.0; + const auto &v1 = m_sourceVertices[item.p1]; + const auto &v2 = m_sourceVertices[item.p2]; + const auto &vp = m_sourceVertices[vertexIndex]; + if (v1.sourcePlane == v2.sourcePlane && v1.sourcePlane == vp.sourcePlane) + return 0.0; + auto vd1 = calculateFaceVector(item.p1, item.p2, item.baseNormal); + auto normal = QVector3D::normal(v2.position, v1.position, vp.position); + auto vd2 = calculateFaceVector(item.p1, item.p2, normal); + return radianToDegree(angleBetween(vd2, vd1)); +} + +std::pair Wrapper::findBestVertexOnTheLeft(size_t itemIndex) +{ + auto p1 = m_items[itemIndex].p1; + auto p2 = m_items[itemIndex].p2; + auto maxAngle = 0.0; + std::pair result = {0, false}; + for (auto it = m_candidates.begin(); it != m_candidates.end(); /*void*/) { + auto cand = *it; + if (isVertexClosed(cand)) { + it = m_candidates.erase(it); + continue; + } + ++it; + if (isEdgeClosed(p1, cand) || isEdgeClosed(p2, cand)) + continue; + auto angle = angleOfBaseFaceAndPoint(itemIndex, cand); + if (angle > maxAngle) { + maxAngle = angle; + result = {cand, true}; + } + } + return result; +} + +std::pair Wrapper::peekItem() +{ + for (const auto &itemIndex : m_itemsList) { + if (!m_items[itemIndex].processed) { + return {itemIndex, true}; + } + } + return {0, false}; +} + +bool Wrapper::isEdgeClosed(size_t p1, size_t p2) +{ + return m_generatedFaceEdgesMap.find(WrapItemKey {.p1 = p1, .p2 = p2}) != m_generatedFaceEdgesMap.end() && + m_generatedFaceEdgesMap.find(WrapItemKey {.p1 = p2, .p2 = p1}) != m_generatedFaceEdgesMap.end(); +} + +bool Wrapper::isVertexClosed(size_t vertexIndex) +{ + auto findResult = m_generatedVertexEdgesMap.find(vertexIndex); + if (findResult == m_generatedVertexEdgesMap.end()) + return false; + for (const auto &otherIndex: findResult->second) { + if (!isEdgeClosed(vertexIndex, otherIndex)) + return false; + } + return true; +} + +void Wrapper::generate() +{ + for (;;) { + auto findItem = peekItem(); + if (!findItem.second) + break; + auto itemIndex = findItem.first; + m_items[itemIndex].processed = true; + auto p1 = m_items[itemIndex].p1; + auto p2 = m_items[itemIndex].p2; + if (isEdgeClosed(p1, p2)) + continue; + auto findP3 = findBestVertexOnTheLeft(itemIndex); + if (findP3.second) { + auto p3 = findP3.first; + m_items[itemIndex].p3 = p3; + auto baseNormal = QVector3D::normal(m_sourceVertices[p1].position, + m_sourceVertices[p2].position, + m_sourceVertices[p3].position); + auto faceIndex = m_generatedFaces.size(); + m_generatedFaces.push_back(Face3 {.p1 = p1, .p2 = p2, .p3 = p3, .normal = baseNormal, .index = faceIndex}); + addItem(p3, p2, baseNormal); + addItem(p1, p3, baseNormal); + m_generatedFaceEdgesMap.insert({WrapItemKey {.p1 = p1, .p2 = p2}, {faceIndex, true}}); + m_generatedFaceEdgesMap.insert({WrapItemKey {.p1 = p2, .p2 = p3}, {faceIndex, true}}); + m_generatedFaceEdgesMap.insert({WrapItemKey {.p1 = p3, .p2 = p1}, {faceIndex, true}}); + m_generatedVertexEdgesMap[p1].push_back(p2); + m_generatedVertexEdgesMap[p1].push_back(p3); + m_generatedVertexEdgesMap[p2].push_back(p3); + m_generatedVertexEdgesMap[p2].push_back(p1); + m_generatedVertexEdgesMap[p3].push_back(p1); + m_generatedVertexEdgesMap[p3].push_back(p2); + } + } +} + +size_t Wrapper::anotherVertexIndexOfFace3(const Face3 &f, size_t p1, size_t p2) +{ + std::vector indices = {f.p1, f.p2, f.p3}; + for (const auto &index : indices) { + if (index != p1 && index != p2) + return index; + } + return 0; +} + +std::pair Wrapper::findPairFace3(const Face3 &f, std::map &usedIds, std::vector &q) +{ + std::vector indices = {f.p1, f.p2, f.p3}; + for (size_t i = 0; i < indices.size(); ++i) { + auto j = (i + 1) % indices.size(); + auto k = (i + 2) % indices.size(); + auto findPairedFace3Id = m_generatedFaceEdgesMap.find(WrapItemKey {.p1 = indices[j], .p2 = indices[i]}); + if (findPairedFace3Id != m_generatedFaceEdgesMap.end() && findPairedFace3Id->second.second) { + auto pairedFace3Id = findPairedFace3Id->second.first; + if (usedIds.find(pairedFace3Id) != usedIds.end()) + continue; + const auto &pairedFace3 = m_generatedFaces[pairedFace3Id]; + if (!almostEqual(pairedFace3.normal, f.normal)) + continue; + auto anotherIndex = anotherVertexIndexOfFace3(pairedFace3, indices[j], indices[i]); + auto mergedF = Face4 {.p1 = indices[i], .p2 = anotherIndex, .p3 = indices[j], .p4 = indices[k]}; + q.push_back(mergedF); + return {pairedFace3Id, true}; + } + } + return {0, false}; +} + +bool Wrapper::almostEqual(const QVector3D &v1, const QVector3D &v2) +{ + return abs(v1.x() - v2.x()) <= 0.01 && + abs(v1.y() - v2.y()) <= 0.01 && + abs(v1.z() - v2.z()) <= 0.01; +} + +void Wrapper::finalize() +{ + std::vector quads; + std::map usedIds; + m_finalizeFinished = true; + for (const auto &f: m_generatedFaces) { + if (usedIds.find(f.index) != usedIds.end()) + continue; + usedIds.insert({f.index, true}); + auto paired = findPairFace3(f, usedIds, quads); + if (paired.second) { + usedIds.insert({paired.first, true}); + continue; + } + std::vector addedVertices = { + m_sourceVertices[f.p1].tag, + m_sourceVertices[f.p2].tag, + m_sourceVertices[f.p3].tag, + }; + m_newlyGeneratedfaces.push_back(addedVertices); + } + for (const auto &f: quads) { + std::vector addedVertices = { + m_sourceVertices[f.p1].tag, + m_sourceVertices[f.p2].tag, + m_sourceVertices[f.p3].tag, + m_sourceVertices[f.p4].tag, + }; + m_newlyGeneratedfaces.push_back(addedVertices); + } +} + + +} diff --git a/thirdparty/nodemesh/nodemesh/wrapper.h b/thirdparty/nodemesh/nodemesh/wrapper.h new file mode 100644 index 00000000..cc917ec2 --- /dev/null +++ b/thirdparty/nodemesh/nodemesh/wrapper.h @@ -0,0 +1,107 @@ +#ifndef NODEMESH_WRAPPER_H +#define NODEMESH_WRAPPER_H +#include +#include +#include +#include + +namespace nodemesh +{ + +class Wrapper +{ +public: + void setVertices(const std::vector *vertices); + void wrap(const std::vector, QVector3D>> &edgeLoops); + const std::vector> &newlyGeneratedFaces(); + bool finished(); + void getFailedEdgeLoops(std::vector &failedEdgeLoops); + +private: + struct WrapItemKey + { + size_t p1; + size_t p2; + + bool operator <(const WrapItemKey &right) const + { + if (p1 < right.p1) + return true; + if (p1 > right.p1) + return false; + if (p2 < right.p2) + return true; + if (p2 > right.p2) + return false; + return false; + } + }; + + struct WrapItem + { + QVector3D baseNormal; + size_t p1; + size_t p2; + size_t p3; + bool processed; + }; + + struct Face3 + { + size_t p1; + size_t p2; + size_t p3; + QVector3D normal; + size_t index; + }; + + struct Face4 + { + size_t p1; + size_t p2; + size_t p3; + size_t p4; + }; + + struct SourceVertex + { + QVector3D position; + size_t sourcePlane; + size_t index; + size_t tag; + }; + + std::vector m_items; + std::map m_itemsMap; + std::deque m_itemsList; + const std::vector *m_positions; + std::vector m_candidates; + std::vector m_sourceVertices; + std::vector m_generatedFaces; + std::map> m_generatedFaceEdgesMap; + std::map> m_generatedVertexEdgesMap; + bool m_finalizeFinished = false; + std::vector> m_newlyGeneratedfaces; + + void addCandidateVertices(const std::vector &vertices, const QVector3D &planeNormal, size_t planeId); + size_t addSourceVertex(const QVector3D &position, size_t sourcePlane, size_t tag); + void addStartup(size_t p1, size_t p2, const QVector3D &baseNormal); + QVector3D calculateFaceVector(size_t p1, size_t p2, const QVector3D &baseNormal); + void addItem(size_t p1, size_t p2, const QVector3D &baseNormal); + std::pair findItem(size_t p1, size_t p2); + bool isEdgeGenerated(size_t p1, size_t p2); + float angleOfBaseFaceAndPoint(size_t itemIndex, size_t vertexIndex); + std::pair findBestVertexOnTheLeft(size_t itemIndex); + std::pair peekItem(); + bool isEdgeClosed(size_t p1, size_t p2); + bool isVertexClosed(size_t vertexIndex); + void generate(); + size_t anotherVertexIndexOfFace3(const Face3 &f, size_t p1, size_t p2); + std::pair findPairFace3(const Face3 &f, std::map &usedIds, std::vector &q); + void finalize(); + bool almostEqual(const QVector3D &v1, const QVector3D &v2); +}; + +} + +#endif