Use Eigen to do vector math on UV

master
Jeremy Hu 2019-08-10 23:12:08 +09:30
parent b840482050
commit dacff70eaa
9 changed files with 102 additions and 80 deletions

View File

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

View File

@ -59,7 +59,7 @@ void uvUnwrap(const Outcome &outcome,
uvUnwrapper.unwrap();
qDebug() << "Texture size:" << uvUnwrapper.textureSize();
const std::vector<simpleuv::FaceTextureCoords> &resultFaceUvs = uvUnwrapper.getFaceUvs();
const std::vector<QRectF> &resultChartRects = uvUnwrapper.getChartRects();
const std::vector<simpleuv::Rect> &resultChartRects = uvUnwrapper.getChartRects();
const std::vector<int> &resultChartSourcePartitions = uvUnwrapper.getChartSourcePartitions();
std::map<int, QVector2D> 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});
}
}

View File

@ -3,7 +3,6 @@
extern "C" {
#include <maxrects.h>
}
#include <QDebug>
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;
}
}

View File

@ -0,0 +1,26 @@
#include <simpleuv/meshdatatype.h>
#include <Eigen/Dense>
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;
}
}

View File

@ -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<int> facePartitions;
};
float dotProduct(const Vector3 &first, const Vector3 &second);
Vector3 crossProduct(const Vector3 &first, const Vector3 &second);
}
#endif

View File

@ -4,7 +4,6 @@
#include <igl/harmonic.h>
#include <igl/map_vertices_to_circle.h>
#include <simpleuv/parametrize.h>
#include <QDebug>
namespace simpleuv
{
@ -56,7 +55,7 @@ bool extractResult(const std::vector<Vertex> &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++) {

View File

@ -1,63 +1,63 @@
#include <simpleuv/triangulate.h>
#include <QVector3D>
#include <Eigen/Dense>
#include <cmath>
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<Vertex> &vertices, const std::vector<size_t> &ring)
static Eigen::Vector3d ringNorm(const std::vector<Vertex> &vertices, const std::vector<size_t> &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<Vertex> &vertices, std::vector<Face> &faces,
if (ring.size() < 3)
return;
std::vector<size_t> 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;

View File

@ -7,9 +7,8 @@
#include <simpleuv/parametrize.h>
#include <simpleuv/chartpacker.h>
#include <simpleuv/triangulate.h>
#include <QDebug>
#include <QVector3D>
#include <QMatrix4x4>
#include <Eigen/Dense>
#include <Eigen/Geometry>
namespace simpleuv
{
@ -26,7 +25,7 @@ const std::vector<FaceTextureCoords> &UvUnwrapper::getFaceUvs() const
return m_faceUvs;
}
const std::vector<QRectF> &UvUnwrapper::getChartRects() const
const std::vector<Rect> &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> &faceTextureCoords,
float &left, float &top, float &right, float &bottom)
{
@ -189,7 +181,7 @@ bool UvUnwrapper::fixHolesExceptTheLongestRing(const std::vector<Vertex> &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<Vertex> &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<Vertex> &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<Vertex> &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<float, float> 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 &degree: 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<FaceTextureCoords> 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<size_t> &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<size_t> &group, int sourc
std::vector<size_t> 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);

View File

@ -2,10 +2,8 @@
#define SIMPLEUV_UV_UNWRAPPER_H
#include <vector>
#include <map>
#include <QRectF>
#include <simpleuv/meshdatatype.h>
#include <QVector3D>
#include <QVector2D>
#include <Eigen/Dense>
namespace simpleuv
{
@ -16,7 +14,7 @@ public:
void setMesh(const Mesh &mesh);
void unwrap();
const std::vector<FaceTextureCoords> &getFaceUvs() const;
const std::vector<QRectF> &getChartRects() const;
const std::vector<Rect> &getChartRects() const;
const std::vector<int> &getChartSourcePartitions() const;
float textureSize() const;
@ -40,9 +38,8 @@ private:
void buildEdgeToFaceMap(const std::vector<size_t> &group, std::map<std::pair<size_t, size_t>, size_t> &edgeToFaceMap);
void buildEdgeToFaceMap(const std::vector<Face> &faces, std::map<std::pair<size_t, size_t>, 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<Vertex> &verticies,
std::vector<Face> &faces, const std::vector<size_t> &ring);
void calculateFaceTextureBoundingBox(const std::vector<FaceTextureCoords> &faceTextureCoords,
@ -54,7 +51,7 @@ private:
std::vector<std::pair<std::vector<size_t>, std::vector<FaceTextureCoords>>> m_charts;
std::vector<std::pair<float, float>> m_chartSizes;
std::vector<std::pair<float, float>> m_scaledChartSizes;
std::vector<QRectF> m_chartRects;
std::vector<Rect> m_chartRects;
std::vector<int> m_chartSourcePartitions;
bool m_segmentByNormal = true;
float m_segmentDotProductThreshold = 0.0; //90 degrees