From dacff70eaa6529246c4dfdc19e2c8d1de9bda514 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Sat, 10 Aug 2019 23:12:08 +0930 Subject: [PATCH] Use Eigen to do vector math on UV --- dust3d.pro | 1 + src/uvunwrap.cpp | 4 +- thirdparty/simpleuv/simpleuv/chartpacker.cpp | 5 +- thirdparty/simpleuv/simpleuv/meshdatatype.cpp | 26 ++++++++ thirdparty/simpleuv/simpleuv/meshdatatype.h | 11 ++++ thirdparty/simpleuv/simpleuv/parametrize.cpp | 3 +- thirdparty/simpleuv/simpleuv/triangulate.cpp | 58 +++++++++--------- thirdparty/simpleuv/simpleuv/uvunwrapper.cpp | 61 ++++++++----------- thirdparty/simpleuv/simpleuv/uvunwrapper.h | 13 ++-- 9 files changed, 102 insertions(+), 80 deletions(-) create mode 100644 thirdparty/simpleuv/simpleuv/meshdatatype.cpp diff --git a/dust3d.pro b/dust3d.pro index dbf711b9..cd7d0118 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -506,6 +506,7 @@ SOURCES += thirdparty/simpleuv/simpleuv/triangulate.cpp HEADERS += thirdparty/simpleuv/simpleuv/triangulate.h HEADERS += thirdparty/simpleuv/simpleuv/meshdatatype.h +SOURCES += thirdparty/simpleuv/simpleuv/meshdatatype.cpp SOURCES += thirdparty/simpleuv/thirdparty/squeezer/maxrects.c HEADERS += thirdparty/simpleuv/thirdparty/squeezer/maxrects.h diff --git a/src/uvunwrap.cpp b/src/uvunwrap.cpp index 249edb3b..2df7630f 100644 --- a/src/uvunwrap.cpp +++ b/src/uvunwrap.cpp @@ -59,7 +59,7 @@ void uvUnwrap(const Outcome &outcome, uvUnwrapper.unwrap(); qDebug() << "Texture size:" << uvUnwrapper.textureSize(); const std::vector &resultFaceUvs = uvUnwrapper.getFaceUvs(); - const std::vector &resultChartRects = uvUnwrapper.getChartRects(); + const std::vector &resultChartRects = uvUnwrapper.getChartRects(); const std::vector &resultChartSourcePartitions = uvUnwrapper.getChartSourcePartitions(); std::map vertexUvMap; for (decltype(choosenTriangles.size()) i = 0; i < choosenTriangles.size(); ++i) { @@ -88,6 +88,6 @@ void uvUnwrap(const Outcome &outcome, qDebug() << "Invalid UV chart source partition:" << source; continue; } - uvRects[partitionPartUuids[source - 1]].push_back(rect); + uvRects[partitionPartUuids[source - 1]].push_back({rect.left, rect.top, rect.width, rect.height}); } } diff --git a/thirdparty/simpleuv/simpleuv/chartpacker.cpp b/thirdparty/simpleuv/simpleuv/chartpacker.cpp index c096a468..73b5b4a2 100644 --- a/thirdparty/simpleuv/simpleuv/chartpacker.cpp +++ b/thirdparty/simpleuv/simpleuv/chartpacker.cpp @@ -3,7 +3,6 @@ extern "C" { #include } -#include namespace simpleuv { @@ -33,7 +32,7 @@ bool ChartPacker::tryPack(float textureSize) int width = textureSize * m_floatToIntFactor; int height = width; if (m_tryNum > 50) { - qDebug() << "Try the " << m_tryNum << "nth times pack with factor:" << m_textureSizeFactor << " size:" << width << "x" << height; + //qDebug() << "Try the " << m_tryNum << "nth times pack with factor:" << m_textureSizeFactor << " size:" << width << "x" << height; } for (const auto &chartSize: m_chartSizes) { maxRectsSize r; @@ -93,7 +92,7 @@ float ChartPacker::pack() break; m_textureSizeFactor += m_textureSizeGrowFactor; if (m_tryNum >= m_maxTryNum) { - qDebug() << "Tried too many times:" << m_tryNum; + //qDebug() << "Tried too many times:" << m_tryNum; break; } } diff --git a/thirdparty/simpleuv/simpleuv/meshdatatype.cpp b/thirdparty/simpleuv/simpleuv/meshdatatype.cpp new file mode 100644 index 00000000..5b9815ed --- /dev/null +++ b/thirdparty/simpleuv/simpleuv/meshdatatype.cpp @@ -0,0 +1,26 @@ +#include +#include + +namespace simpleuv +{ + +float dotProduct(const Vector3 &first, const Vector3 &second) +{ + Eigen::Vector3d v(first.xyz[0], first.xyz[1], first.xyz[2]); + Eigen::Vector3d w(second.xyz[0], second.xyz[1], second.xyz[2]); + return v.dot(w); +} + +Vector3 crossProduct(const Vector3 &first, const Vector3 &second) +{ + Eigen::Vector3d v(first.xyz[0], first.xyz[1], first.xyz[2]); + Eigen::Vector3d w(second.xyz[0], second.xyz[1], second.xyz[2]); + auto u = v.cross(w); + Vector3 result; + result.xyz[0] = u.x(); + result.xyz[1] = u.y(); + result.xyz[2] = u.z(); + return result; +} + +} diff --git a/thirdparty/simpleuv/simpleuv/meshdatatype.h b/thirdparty/simpleuv/simpleuv/meshdatatype.h index e83bd17e..210f29e0 100644 --- a/thirdparty/simpleuv/simpleuv/meshdatatype.h +++ b/thirdparty/simpleuv/simpleuv/meshdatatype.h @@ -13,6 +13,14 @@ struct Vector3 typedef Vector3 Vertex; +struct Rect +{ + float left; + float top; + float width; + float height; +}; + struct Face { size_t indices[3]; @@ -36,6 +44,9 @@ struct Mesh std::vector facePartitions; }; +float dotProduct(const Vector3 &first, const Vector3 &second); +Vector3 crossProduct(const Vector3 &first, const Vector3 &second); + } #endif diff --git a/thirdparty/simpleuv/simpleuv/parametrize.cpp b/thirdparty/simpleuv/simpleuv/parametrize.cpp index 316fce48..e7745fc8 100644 --- a/thirdparty/simpleuv/simpleuv/parametrize.cpp +++ b/thirdparty/simpleuv/simpleuv/parametrize.cpp @@ -4,7 +4,6 @@ #include #include #include -#include namespace simpleuv { @@ -56,7 +55,7 @@ bool extractResult(const std::vector &verticies, const Eigen::MatrixXd & return true; }; if ((decltype(verticies.size()))V_uv.size() < verticies.size() * 2) { - qDebug() << "Invalid V_uv.size:" << V_uv.size() << "Expected:" << verticies.size() * 2; + //qDebug() << "Invalid V_uv.size:" << V_uv.size() << "Expected:" << verticies.size() * 2; return false; } for (decltype(verticies.size()) i = 0; i < verticies.size(); i++) { diff --git a/thirdparty/simpleuv/simpleuv/triangulate.cpp b/thirdparty/simpleuv/simpleuv/triangulate.cpp index 8869fc97..f325572d 100644 --- a/thirdparty/simpleuv/simpleuv/triangulate.cpp +++ b/thirdparty/simpleuv/simpleuv/triangulate.cpp @@ -1,63 +1,63 @@ #include -#include +#include #include namespace simpleuv { -static QVector3D norm(const QVector3D &p1, const QVector3D &p2, const QVector3D &p3) +static Eigen::Vector3d norm(const Eigen::Vector3d &p1, const Eigen::Vector3d &p2, const Eigen::Vector3d &p3) { auto side1 = p2 - p1; auto side2 = p3 - p1; - auto perp = QVector3D::crossProduct(side1, side2); + auto perp = side1.cross(side2); return perp.normalized(); } -static float angle360(const QVector3D &a, const QVector3D &b, const QVector3D &direct) +static float angle360(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &direct) { - auto angle = atan2(QVector3D::crossProduct(a, b).length(), QVector3D::dotProduct(a, b)) * 180.0 / 3.1415926; - auto c = QVector3D::crossProduct(a, b); - if (QVector3D::dotProduct(c, direct) < 0) { + auto angle = atan2((a.cross(b)).norm(), a.dot(b)) * 180.0 / 3.1415926; + auto c = a.cross(b); + if (c.dot(direct) < 0) { angle += 180; } return angle; } -static QVector3D vertexToQVector3D(const Vertex &vertex) +static Eigen::Vector3d vertexToEigenVector3d(const Vertex &vertex) { - return QVector3D(vertex.xyz[0], vertex.xyz[1], vertex.xyz[2]); + return Eigen::Vector3d(vertex.xyz[0], vertex.xyz[1], vertex.xyz[2]); } -static bool pointInTriangle(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &p) +static bool pointInTriangle(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c, const Eigen::Vector3d &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) { + auto vXw = v.cross(w); + auto vXu = v.cross(u); + if (vXw.dot(vXu) < 0.0) { return false; } - auto uXw = QVector3D::crossProduct(u, w); - auto uXv = QVector3D::crossProduct(u, v); - if (QVector3D::dotProduct(uXw, uXv) < 0.0) { + auto uXw = u.cross(w); + auto uXv = u.cross(v); + if (uXw.dot(uXv) < 0.0) { return false; } - auto denom = uXv.length(); - auto r = vXw.length() / denom; - auto t = uXw.length() / denom; + auto denom = uXv.norm(); + auto r = vXw.norm() / denom; + auto t = uXw.norm() / denom; return r + t <= 1.0; } -static QVector3D ringNorm(const std::vector &vertices, const std::vector &ring) +static Eigen::Vector3d ringNorm(const std::vector &vertices, const std::vector &ring) { - QVector3D normal; + Eigen::Vector3d normal; for (size_t i = 0; i < ring.size(); ++i) { auto j = (i + 1) % ring.size(); auto k = (i + 2) % ring.size(); - const auto &enter = vertexToQVector3D(vertices[ring[i]]); - const auto &cone = vertexToQVector3D(vertices[ring[j]]); - const auto &leave = vertexToQVector3D(vertices[ring[k]]); + const auto &enter = vertexToEigenVector3d(vertices[ring[i]]); + const auto &cone = vertexToEigenVector3d(vertices[ring[j]]); + const auto &leave = vertexToEigenVector3d(vertices[ring[k]]); normal += norm(enter, cone, leave); } return normal.normalized(); @@ -68,20 +68,20 @@ void triangulate(const std::vector &vertices, std::vector &faces, if (ring.size() < 3) return; std::vector fillRing = ring; - QVector3D direct = ringNorm(vertices, fillRing); + Eigen::Vector3d direct = ringNorm(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 = vertexToQVector3D(vertices[fillRing[i]]); - const auto &cone = vertexToQVector3D(vertices[fillRing[j]]); - const auto &leave = vertexToQVector3D(vertices[fillRing[k]]); + const auto &enter = vertexToEigenVector3d(vertices[fillRing[i]]); + const auto &cone = vertexToEigenVector3d(vertices[fillRing[j]]); + const auto &leave = vertexToEigenVector3d(vertices[fillRing[k]]); auto angle = angle360(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 = vertexToQVector3D(vertices[(i + 3 + k) % fillRing.size()]); + auto fourth = vertexToEigenVector3d(vertices[(i + 3 + k) % fillRing.size()]); if (pointInTriangle(enter, cone, leave, fourth)) { isEar = false; break; diff --git a/thirdparty/simpleuv/simpleuv/uvunwrapper.cpp b/thirdparty/simpleuv/simpleuv/uvunwrapper.cpp index b6283b49..77162d89 100644 --- a/thirdparty/simpleuv/simpleuv/uvunwrapper.cpp +++ b/thirdparty/simpleuv/simpleuv/uvunwrapper.cpp @@ -7,9 +7,8 @@ #include #include #include -#include -#include -#include +#include +#include namespace simpleuv { @@ -26,7 +25,7 @@ const std::vector &UvUnwrapper::getFaceUvs() const return m_faceUvs; } -const std::vector &UvUnwrapper::getChartRects() const +const std::vector &UvUnwrapper::getChartRects() const { return m_chartRects; } @@ -109,13 +108,6 @@ double UvUnwrapper::distanceBetweenVertices(const Vertex &first, const Vertex &s return std::sqrt(x*x + y*y + z*z); } -double UvUnwrapper::dotProduct(const Vertex &first, const Vertex &second) -{ - const QVector3D &firstVector = QVector3D(first.xyz[0], first.xyz[1], first.xyz[2]); - const QVector3D &secondVector = QVector3D(second.xyz[0], second.xyz[1], second.xyz[2]); - return QVector3D::dotProduct(firstVector, secondVector); -} - void UvUnwrapper::calculateFaceTextureBoundingBox(const std::vector &faceTextureCoords, float &left, float &top, float &right, float &bottom) { @@ -189,7 +181,7 @@ bool UvUnwrapper::fixHolesExceptTheLongestRing(const std::vector &vertic while (true) { auto findLinkResult = holeVertexLink.find(index); if (findLinkResult == holeVertexLink.end()) { - qDebug() << "Search ring failed"; + //qDebug() << "Search ring failed"; return false; } for (const auto &item: findLinkResult->second) { @@ -210,7 +202,7 @@ bool UvUnwrapper::fixHolesExceptTheLongestRing(const std::vector &vertic } } if (!foundNewPath) { - qDebug() << "No new path to try"; + //qDebug() << "No new path to try"; return false; } visitedPath.insert({prev, index}); @@ -230,7 +222,7 @@ bool UvUnwrapper::fixHolesExceptTheLongestRing(const std::vector &vertic } } if (ring.size() < 3) { - qDebug() << "Ring too short, size:" << ring.size(); + //qDebug() << "Ring too short, size:" << ring.size(); return false; } holeRings.push_back({ring, ringLength}); @@ -319,18 +311,18 @@ void UvUnwrapper::makeSeamAndCut(const std::vector &verticies, } } -float UvUnwrapper::areaOf3dTriangle(const QVector3D &a, const QVector3D &b, const QVector3D &c) +float UvUnwrapper::areaOf3dTriangle(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c) { auto ab = b - a; auto ac = c - a; - return 0.5 * QVector3D::crossProduct(ab, ac).length(); + return 0.5 * (ab.cross(ac)).norm(); } -float UvUnwrapper::areaOf2dTriangle(const QVector2D &a, const QVector2D &b, const QVector2D &c) +float UvUnwrapper::areaOf2dTriangle(const Eigen::Vector2d &a, const Eigen::Vector2d &b, const Eigen::Vector2d &c) { - return areaOf3dTriangle(QVector3D(a.x(), a.y(), 0), - QVector3D(b.x(), b.y(), 0), - QVector3D(c.x(), c.y(), 0)); + return areaOf3dTriangle(Eigen::Vector3d(a.x(), a.y(), 0), + Eigen::Vector3d(b.x(), b.y(), 0), + Eigen::Vector3d(c.x(), c.y(), 0)); } void UvUnwrapper::calculateSizeAndRemoveInvalidCharts() @@ -347,19 +339,19 @@ void UvUnwrapper::calculateSizeAndRemoveInvalidCharts() std::pair size = {right - left, bottom - top}; if (size.first <= 0 || std::isnan(size.first) || std::isinf(size.first) || size.second <= 0 || std::isnan(size.second) || std::isinf(size.second)) { - qDebug() << "Found invalid chart size:" << size.first << "x" << size.second; + //qDebug() << "Found invalid chart size:" << size.first << "x" << size.second; continue; } float surfaceArea = 0; for (const auto &item: chart.first) { const auto &face = m_mesh.faces[item]; - surfaceArea += areaOf3dTriangle(QVector3D(m_mesh.vertices[face.indices[0]].xyz[0], + surfaceArea += areaOf3dTriangle(Eigen::Vector3d(m_mesh.vertices[face.indices[0]].xyz[0], m_mesh.vertices[face.indices[0]].xyz[1], m_mesh.vertices[face.indices[0]].xyz[2]), - QVector3D(m_mesh.vertices[face.indices[1]].xyz[0], + Eigen::Vector3d(m_mesh.vertices[face.indices[1]].xyz[0], m_mesh.vertices[face.indices[1]].xyz[1], m_mesh.vertices[face.indices[1]].xyz[2]), - QVector3D(m_mesh.vertices[face.indices[2]].xyz[0], + Eigen::Vector3d(m_mesh.vertices[face.indices[2]].xyz[0], m_mesh.vertices[face.indices[2]].xyz[1], m_mesh.vertices[face.indices[2]].xyz[2])); } @@ -369,25 +361,24 @@ void UvUnwrapper::calculateSizeAndRemoveInvalidCharts() item.coords[i].uv[0] -= left; item.coords[i].uv[1] -= top; } - uvArea += areaOf2dTriangle(QVector2D(item.coords[0].uv[0], item.coords[0].uv[1]), - QVector2D(item.coords[1].uv[0], item.coords[1].uv[1]), - QVector2D(item.coords[2].uv[0], item.coords[2].uv[1])); + uvArea += areaOf2dTriangle(Eigen::Vector2d(item.coords[0].uv[0], item.coords[0].uv[1]), + Eigen::Vector2d(item.coords[1].uv[0], item.coords[1].uv[1]), + Eigen::Vector2d(item.coords[2].uv[0], item.coords[2].uv[1])); } if (m_enableRotation) { - QVector3D center(size.first * 0.5, size.second * 0.5, 0); + Eigen::Vector3d center(size.first * 0.5, size.second * 0.5, 0); float minRectArea = size.first * size.second; float minRectLeft = 0; float minRectTop = 0; bool rotated = false; - float choosenDegree = 0; for (const auto °ree: m_rotateDegrees) { - QMatrix4x4 matrix; - matrix.rotate(degree, 0, 0, 1); + Eigen::Matrix3d matrix; + matrix = Eigen::AngleAxisd(degree * 180.0 / 3.1415926, Eigen::Vector3d::UnitZ()); std::vector rotatedUvs; for (auto &item: chart.second) { FaceTextureCoords rotatedCoords; for (int i = 0; i < 3; ++i) { - QVector3D point(item.coords[i].uv[0], item.coords[i].uv[1], 0); + Eigen::Vector3d point(item.coords[i].uv[0], item.coords[i].uv[1], 0); point -= center; point = matrix * point; rotatedCoords.coords[i].uv[0] = point.x(); @@ -404,13 +395,11 @@ void UvUnwrapper::calculateSizeAndRemoveInvalidCharts() size = newSize; minRectLeft = left; minRectTop = top; - choosenDegree = degree; rotated = true; chart.second = rotatedUvs; } } if (rotated) { - //qDebug() << "Choosen degree:" << choosenDegree; for (auto &item: chart.second) { for (int i = 0; i < 3; ++i) { item.coords[i].uv[0] -= minRectLeft; @@ -537,7 +526,7 @@ void UvUnwrapper::unwrapSingleIsland(const std::vector &group, int sourc decltype(localFaces.size()) faceNumBeforeFix = localFaces.size(); size_t remainingHoleNumAfterFix = 0; if (!fixHolesExceptTheLongestRing(localVertices, localFaces, &remainingHoleNumAfterFix)) { - qDebug() << "fixHolesExceptTheLongestRing failed"; + //qDebug() << "fixHolesExceptTheLongestRing failed"; return; } if (1 == remainingHoleNumAfterFix) { @@ -550,7 +539,7 @@ void UvUnwrapper::unwrapSingleIsland(const std::vector &group, int sourc std::vector secondGroup; makeSeamAndCut(localVertices, localFaces, localToGlobalFacesMap, firstGroup, secondGroup); if (firstGroup.empty() || secondGroup.empty()) { - qDebug() << "Cut mesh failed"; + //qDebug() << "Cut mesh failed"; return; } unwrapSingleIsland(firstGroup, sourcePartition, true); diff --git a/thirdparty/simpleuv/simpleuv/uvunwrapper.h b/thirdparty/simpleuv/simpleuv/uvunwrapper.h index 5b648fb9..cccceec0 100644 --- a/thirdparty/simpleuv/simpleuv/uvunwrapper.h +++ b/thirdparty/simpleuv/simpleuv/uvunwrapper.h @@ -2,10 +2,8 @@ #define SIMPLEUV_UV_UNWRAPPER_H #include #include -#include #include -#include -#include +#include namespace simpleuv { @@ -16,7 +14,7 @@ public: void setMesh(const Mesh &mesh); void unwrap(); const std::vector &getFaceUvs() const; - const std::vector &getChartRects() const; + const std::vector &getChartRects() const; const std::vector &getChartSourcePartitions() const; float textureSize() const; @@ -40,9 +38,8 @@ private: void buildEdgeToFaceMap(const std::vector &group, std::map, size_t> &edgeToFaceMap); void buildEdgeToFaceMap(const std::vector &faces, std::map, size_t> &edgeToFaceMap); double distanceBetweenVertices(const Vertex &first, const Vertex &second); - double dotProduct(const Vertex &first, const Vertex &second); - float areaOf3dTriangle(const QVector3D &a, const QVector3D &b, const QVector3D &c); - float areaOf2dTriangle(const QVector2D &a, const QVector2D &b, const QVector2D &c); + float areaOf3dTriangle(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c); + float areaOf2dTriangle(const Eigen::Vector2d &a, const Eigen::Vector2d &b, const Eigen::Vector2d &c); void triangulateRing(const std::vector &verticies, std::vector &faces, const std::vector &ring); void calculateFaceTextureBoundingBox(const std::vector &faceTextureCoords, @@ -54,7 +51,7 @@ private: std::vector, std::vector>> m_charts; std::vector> m_chartSizes; std::vector> m_scaledChartSizes; - std::vector m_chartRects; + std::vector m_chartRects; std::vector m_chartSourcePartitions; bool m_segmentByNormal = true; float m_segmentDotProductThreshold = 0.0; //90 degrees