115 lines
3.8 KiB
C++
115 lines
3.8 KiB
C++
#include <simpleuv/triangulate.h>
|
|
#include <QVector3D>
|
|
#include <cmath>
|
|
|
|
namespace simpleuv
|
|
{
|
|
|
|
static QVector3D norm(const QVector3D &p1, const QVector3D &p2, const QVector3D &p3)
|
|
{
|
|
auto side1 = p2 - p1;
|
|
auto side2 = p3 - p1;
|
|
auto perp = QVector3D::crossProduct(side1, side2);
|
|
return perp.normalized();
|
|
}
|
|
|
|
static float angle360(const QVector3D &a, const QVector3D &b, const QVector3D &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) {
|
|
angle += 180;
|
|
}
|
|
return angle;
|
|
}
|
|
|
|
static QVector3D vertexToQVector3D(const Vertex &vertex)
|
|
{
|
|
return QVector3D(vertex.xyz[0], vertex.xyz[1], vertex.xyz[2]);
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static QVector3D ringNorm(const std::vector<Vertex> &vertices, const std::vector<size_t> &ring)
|
|
{
|
|
QVector3D 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]]);
|
|
normal += norm(enter, cone, leave);
|
|
}
|
|
return normal.normalized();
|
|
}
|
|
|
|
void triangulate(const std::vector<Vertex> &vertices, std::vector<Face> &faces, const std::vector<size_t> &ring)
|
|
{
|
|
if (ring.size() < 3)
|
|
return;
|
|
std::vector<size_t> fillRing = ring;
|
|
QVector3D 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]]);
|
|
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()]);
|
|
if (pointInTriangle(enter, cone, leave, fourth)) {
|
|
isEar = false;
|
|
break;
|
|
}
|
|
}
|
|
if (isEar) {
|
|
Face newFace;
|
|
newFace.indices[0] = fillRing[i];
|
|
newFace.indices[1] = fillRing[j];
|
|
newFace.indices[2] = fillRing[k];
|
|
faces.push_back(newFace);
|
|
fillRing.erase(fillRing.begin() + j);
|
|
newFaceGenerated = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!newFaceGenerated)
|
|
break;
|
|
}
|
|
if (fillRing.size() == 3) {
|
|
Face newFace;
|
|
newFace.indices[0] = fillRing[0];
|
|
newFace.indices[1] = fillRing[1];
|
|
newFace.indices[2] = fillRing[2];
|
|
faces.push_back(newFace);
|
|
}
|
|
}
|
|
|
|
}
|