summaryrefslogtreecommitdiff
path: root/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp')
-rw-r--r--thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp323
1 files changed, 323 insertions, 0 deletions
diff --git a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp b/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp
new file mode 100644
index 0000000000..683ee603cd
--- /dev/null
+++ b/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp
@@ -0,0 +1,323 @@
+// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com>
+
+#include "nvmesh.h" // pch
+
+#include "ParameterizationQuality.h"
+
+#include "nvmesh/halfedge/Mesh.h"
+#include "nvmesh/halfedge/Face.h"
+#include "nvmesh/halfedge/Vertex.h"
+#include "nvmesh/halfedge/Edge.h"
+
+#include "nvmath/Vector.inl"
+
+#include "nvcore/Debug.h"
+
+#include <float.h>
+
+
+using namespace nv;
+
+#if 0
+/*
+float triangleConformalEnergy(Vector3 q[3], Vector2 p[3])
+{
+const Vector3 v1 = q[0];
+const Vector3 v2 = q[1];
+const Vector3 v3 = q[2];
+
+const Vector2 w1 = p[0];
+const Vector2 w2 = p[1];
+const Vector2 w3 = p[2];
+
+float x1 = v2.x() - v1.x();
+float x2 = v3.x() - v1.x();
+float y1 = v2.y() - v1.y();
+float y2 = v3.y() - v1.y();
+float z1 = v2.z() - v1.z();
+float z2 = v3.z() - v1.z();
+
+float s1 = w2.x() - w1.x();
+float s2 = w3.x() - w1.x();
+float t1 = w2.y() - w1.y();
+float t2 = w3.y() - w1.y();
+
+float r = 1.0f / (s1 * t2 - s2 * t1);
+Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
+Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
+
+Vector3 N = cross(v3-v1, v2-v1);
+
+// Rotate 90 around N.
+}
+*/
+
+static float triangleConformalEnergy(Vector3 q[3], Vector2 p[3])
+{
+ // Using Denis formulas:
+ Vector3 c0 = q[1] - q[2];
+ Vector3 c1 = q[2] - q[0];
+ Vector3 c2 = q[0] - q[1];
+
+ Vector3 N = cross(-c0, c1);
+ float T = length(N); // 2T
+ N = normalize(N, 0);
+
+ float cot_alpha0 = dot(-c1, c2) / length(cross(-c1, c2));
+ float cot_alpha1 = dot(-c2, c0) / length(cross(-c2, c0));
+ float cot_alpha2 = dot(-c0, c1) / length(cross(-c0, c1));
+
+ Vector3 t0 = -cot_alpha1 * c1 + cot_alpha2 * c2;
+ Vector3 t1 = -cot_alpha2 * c2 + cot_alpha0 * c0;
+ Vector3 t2 = -cot_alpha0 * c0 + cot_alpha1 * c1;
+
+ nvCheck(equal(length(t0), length(c0)));
+ nvCheck(equal(length(t1), length(c1)));
+ nvCheck(equal(length(t2), length(c2)));
+ nvCheck(equal(dot(t0, c0), 0));
+ nvCheck(equal(dot(t1, c1), 0));
+ nvCheck(equal(dot(t2, c2), 0));
+
+ // Gradients
+ Vector3 grad_u = 1.0f / T * (p[0].x * t0 + p[1].x * t1 + p[2].x * t2);
+ Vector3 grad_v = 1.0f / T * (p[0].y * t0 + p[1].y * t1 + p[2].y * t2);
+
+ // Rotated gradients
+ Vector3 Jgrad_u = 1.0f / T * (p[0].x * c0 + p[1].x * c1 + p[2].x * c2);
+ Vector3 Jgrad_v = 1.0f / T * (p[0].y * c0 + p[1].y * c1 + p[2].y * c2);
+
+ // Using Lengyel's formulas:
+ {
+ const Vector3 v1 = q[0];
+ const Vector3 v2 = q[1];
+ const Vector3 v3 = q[2];
+
+ const Vector2 w1 = p[0];
+ const Vector2 w2 = p[1];
+ const Vector2 w3 = p[2];
+
+ float x1 = v2.x - v1.x;
+ float x2 = v3.x - v1.x;
+ float y1 = v2.y - v1.y;
+ float y2 = v3.y - v1.y;
+ float z1 = v2.z - v1.z;
+ float z2 = v3.z - v1.z;
+
+ float s1 = w2.x - w1.x;
+ float s2 = w3.x - w1.x;
+ float t1 = w2.y - w1.y;
+ float t2 = w3.y - w1.y;
+
+ float r = 1.0f / (s1 * t2 - s2 * t1);
+ Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
+ Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
+
+ Vector3 Jsdir = cross(N, sdir);
+ Vector3 Jtdir = cross(N, tdir);
+
+ float x = 3;
+ }
+
+ // check: sdir == grad_u
+ // check: tdir == grad_v
+
+ return length(grad_u - Jgrad_v);
+}
+#endif // 0
+
+
+ParameterizationQuality::ParameterizationQuality()
+{
+ m_totalTriangleCount = 0;
+ m_flippedTriangleCount = 0;
+ m_zeroAreaTriangleCount = 0;
+
+ m_parametricArea = 0.0f;
+ m_geometricArea = 0.0f;
+
+ m_stretchMetric = 0.0f;
+ m_maxStretchMetric = 0.0f;
+
+ m_conformalMetric = 0.0f;
+ m_authalicMetric = 0.0f;
+}
+
+ParameterizationQuality::ParameterizationQuality(const HalfEdge::Mesh * mesh)
+{
+ nvDebugCheck(mesh != NULL);
+
+ m_totalTriangleCount = 0;
+ m_flippedTriangleCount = 0;
+ m_zeroAreaTriangleCount = 0;
+
+ m_parametricArea = 0.0f;
+ m_geometricArea = 0.0f;
+
+ m_stretchMetric = 0.0f;
+ m_maxStretchMetric = 0.0f;
+
+ m_conformalMetric = 0.0f;
+ m_authalicMetric = 0.0f;
+
+ const uint faceCount = mesh->faceCount();
+ for (uint f = 0; f < faceCount; f++)
+ {
+ const HalfEdge::Face * face = mesh->faceAt(f);
+ const HalfEdge::Vertex * vertex0 = NULL;
+
+ Vector3 p[3];
+ Vector2 t[3];
+
+ for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
+ {
+ const HalfEdge::Edge * edge = it.current();
+
+ if (vertex0 == NULL)
+ {
+ vertex0 = edge->vertex;
+
+ p[0] = vertex0->pos;
+ t[0] = vertex0->tex;
+ }
+ else if (edge->to() != vertex0)
+ {
+ p[1] = edge->from()->pos;
+ p[2] = edge->to()->pos;
+ t[1] = edge->from()->tex;
+ t[2] = edge->to()->tex;
+
+ processTriangle(p, t);
+ }
+ }
+ }
+
+ if (m_flippedTriangleCount + m_zeroAreaTriangleCount == faceCount)
+ {
+ // If all triangles are flipped, then none is.
+ m_flippedTriangleCount = 0;
+ }
+
+ nvDebugCheck(isFinite(m_parametricArea) && m_parametricArea >= 0);
+ nvDebugCheck(isFinite(m_geometricArea) && m_geometricArea >= 0);
+ nvDebugCheck(isFinite(m_stretchMetric));
+ nvDebugCheck(isFinite(m_maxStretchMetric));
+ nvDebugCheck(isFinite(m_conformalMetric));
+ nvDebugCheck(isFinite(m_authalicMetric));
+}
+
+bool ParameterizationQuality::isValid() const
+{
+ return m_flippedTriangleCount == 0; // @@ Does not test for self-overlaps.
+}
+
+float ParameterizationQuality::rmsStretchMetric() const
+{
+ if (m_geometricArea == 0) return 0.0f;
+ float normFactor = sqrtf(m_parametricArea / m_geometricArea);
+ return sqrtf(m_stretchMetric / m_geometricArea) * normFactor;
+}
+
+float ParameterizationQuality::maxStretchMetric() const
+{
+ if (m_geometricArea == 0) return 0.0f;
+ float normFactor = sqrtf(m_parametricArea / m_geometricArea);
+ return m_maxStretchMetric * normFactor;
+}
+
+float ParameterizationQuality::rmsConformalMetric() const
+{
+ if (m_geometricArea == 0) return 0.0f;
+ return sqrtf(m_conformalMetric / m_geometricArea);
+}
+
+float ParameterizationQuality::maxAuthalicMetric() const
+{
+ if (m_geometricArea == 0) return 0.0f;
+ return sqrtf(m_authalicMetric / m_geometricArea);
+}
+
+void ParameterizationQuality::operator += (const ParameterizationQuality & pq)
+{
+ m_totalTriangleCount += pq.m_totalTriangleCount;
+ m_flippedTriangleCount += pq.m_flippedTriangleCount;
+ m_zeroAreaTriangleCount += pq.m_zeroAreaTriangleCount;
+
+ m_parametricArea += pq.m_parametricArea;
+ m_geometricArea += pq.m_geometricArea;
+
+ m_stretchMetric += pq.m_stretchMetric;
+ m_maxStretchMetric = max(m_maxStretchMetric, pq.m_maxStretchMetric);
+
+ m_conformalMetric += pq.m_conformalMetric;
+ m_authalicMetric += pq.m_authalicMetric;
+}
+
+
+void ParameterizationQuality::processTriangle(Vector3 q[3], Vector2 p[3])
+{
+ m_totalTriangleCount++;
+
+ // Evaluate texture stretch metric. See:
+ // - "Texture Mapping Progressive Meshes", Sander, Snyder, Gortler & Hoppe
+ // - "Mesh Parameterization: Theory and Practice", Siggraph'07 Course Notes, Hormann, Levy & Sheffer.
+
+ float t1 = p[0].x;
+ float s1 = p[0].y;
+ float t2 = p[1].x;
+ float s2 = p[1].y;
+ float t3 = p[2].x;
+ float s3 = p[2].y;
+
+ float geometricArea = length(cross(q[1] - q[0], q[2] - q[0])) / 2;
+ float parametricArea = ((s2 - s1)*(t3 - t1) - (s3 - s1)*(t2 - t1)) / 2;
+
+ if (isZero(parametricArea))
+ {
+ m_zeroAreaTriangleCount++;
+ return;
+ }
+
+ Vector3 Ss = (q[0] * (t2- t3) + q[1] * (t3 - t1) + q[2] * (t1 - t2)) / (2 * parametricArea);
+ Vector3 St = (q[0] * (s3- s2) + q[1] * (s1 - s3) + q[2] * (s2 - s1)) / (2 * parametricArea);
+
+ float a = dot(Ss, Ss); // E
+ float b = dot(Ss, St); // F
+ float c = dot(St, St); // G
+
+ // Compute eigen-values of the first fundamental form:
+ float sigma1 = sqrtf(0.5f * max(0.0f, a + c - sqrtf(square(a - c) + 4 * square(b)))); // gamma uppercase, min eigenvalue.
+ float sigma2 = sqrtf(0.5f * max(0.0f, a + c + sqrtf(square(a - c) + 4 * square(b)))); // gamma lowercase, max eigenvalue.
+ nvCheck(sigma2 >= sigma1);
+
+ // isometric: sigma1 = sigma2 = 1
+ // conformal: sigma1 / sigma2 = 1
+ // authalic: sigma1 * sigma2 = 1
+
+ float rmsStretch = sqrtf((a + c) * 0.5f);
+ float rmsStretch2 = sqrtf((square(sigma1) + square(sigma2)) * 0.5f);
+ nvDebugCheck(equal(rmsStretch, rmsStretch2, 0.01f));
+
+ if (parametricArea < 0.0f)
+ {
+ // Count flipped triangles.
+ m_flippedTriangleCount++;
+
+ parametricArea = fabsf(parametricArea);
+ }
+
+ m_stretchMetric += square(rmsStretch) * geometricArea;
+ m_maxStretchMetric = max(m_maxStretchMetric, sigma2);
+
+ if (!isZero(sigma1, 0.000001f)) {
+ // sigma1 is zero when geometricArea is zero.
+ m_conformalMetric += (sigma2 / sigma1) * geometricArea;
+ }
+ m_authalicMetric += (sigma1 * sigma2) * geometricArea;
+
+ // Accumulate total areas.
+ m_geometricArea += geometricArea;
+ m_parametricArea += parametricArea;
+
+
+ //triangleConformalEnergy(q, p);
+}