From 3f9b0eff22c291b8652a400d2c2c0cfc31d3584e Mon Sep 17 00:00:00 2001 From: RuiwenTang Date: Sun, 4 Feb 2024 13:51:51 +0800 Subject: [PATCH] gl_engine: enhance the tessellation algorithm * merge vertices that are close enough before tessellation * append return branch in tessellation to prevent dead loop caused by floating point precision --- src/renderer/gl_engine/tvgGlGeometry.cpp | 5 +++- src/renderer/gl_engine/tvgGlTessellator.cpp | 31 +++++++++++++++------ src/renderer/gl_engine/tvgGlTessellator.h | 6 ++-- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlGeometry.cpp b/src/renderer/gl_engine/tvgGlGeometry.cpp index 88fe0468..3338d1b5 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.cpp +++ b/src/renderer/gl_engine/tvgGlGeometry.cpp @@ -42,7 +42,10 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag) fillIndex.clear(); Tessellator tess{&fillVertex, &fillIndex}; - tess.tessellate(&rshape, true); + if (!tess.tessellate(&rshape, true)) { + fillVertex.clear(); + fillIndex.clear(); + } } if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { diff --git a/src/renderer/gl_engine/tvgGlTessellator.cpp b/src/renderer/gl_engine/tvgGlTessellator.cpp index 20010c16..4831f3bf 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.cpp +++ b/src/renderer/gl_engine/tvgGlTessellator.cpp @@ -662,8 +662,10 @@ static bool _validEdgePair(Edge* left, Edge* right) { bool ActiveEdgeList::valid() { auto left = head; - if (!left) { + if (!left && !tail) { return true; + } else if (!left || !tail) { + return false; } for(auto right = left->right; right; right = right->right) { @@ -898,7 +900,7 @@ Tessellator::~Tessellator() } -void Tessellator::tessellate(const RenderShape *rshape, bool antialias) +bool Tessellator::tessellate(const RenderShape *rshape, bool antialias) { auto cmds = rshape->path.cmds.data; auto cmdCnt = rshape->path.cmds.count; @@ -913,9 +915,9 @@ void Tessellator::tessellate(const RenderShape *rshape, bool antialias) this->mergeVertices(); - this->simplifyMesh(); + if (!this->simplifyMesh()) return false; - this->tessMesh(); + if (!this->tessMesh()) return false; // output triangles for (auto poly = this->pPolygon; poly; poly = poly->next) { @@ -935,6 +937,8 @@ void Tessellator::tessellate(const RenderShape *rshape, bool antialias) if (antialias) { // TODO extract outline from current polygon list and generate aa edges } + + return true; } void Tessellator::tessellate(const Array &shapes) @@ -1085,7 +1089,7 @@ void Tessellator::mergeVertices() for (auto v = pMesh->head->next; v;) { auto next = v->next; - if (detail::VertexCompare::compare(v->point, v->prev->point)) { + if (detail::VertexCompare::compare(v->point, v->prev->point) || detail::_pointLength(v->point - v->prev->point) <= 0.025f) { // already sorted, this means these two points is same v->point = v->prev->point; } @@ -1107,7 +1111,7 @@ void Tessellator::mergeVertices() } } -void Tessellator::simplifyMesh() +bool Tessellator::simplifyMesh() { /// this is a basic sweep line algorithm /// https://www.youtube.com/watch?v=qkhUNzCGDt0&t=293s @@ -1134,6 +1138,11 @@ void Tessellator::simplifyMesh() v->left = left_enclosing; v->right = right_enclosing; + if (!ael.valid()) { + // If AEL is not valid, means we meet the problem caused by floating point precision + return false; + } + if (v->edge_below.head) { for (auto e = v->edge_below.head; e; e = e->below_next) { // check current edge is intersected by left or right neighbor edges @@ -1154,7 +1163,7 @@ void Tessellator::simplifyMesh() if (!ael.valid()) { // If AEL is not valid, means we meet the problem caused by floating point precision - return; + return false; } // we are done for all edge end with current point @@ -1170,9 +1179,11 @@ void Tessellator::simplifyMesh() left = e; } } + + return true; } -void Tessellator::tessMesh() +bool Tessellator::tessMesh() { /// this function also use sweep line algorithm /// but during the process, we calculate the winding number of left and right @@ -1185,6 +1196,8 @@ void Tessellator::tessMesh() continue; } + if (!ael.valid()) return false; + detail::Edge *left_enclosing = nullptr; detail::Edge *right_enclosing = nullptr; @@ -1315,6 +1328,8 @@ void Tessellator::tessMesh() v->edge_below.tail->right_poly = right_poly; } } + + return true; } bool Tessellator::matchFillRule(int32_t winding) diff --git a/src/renderer/gl_engine/tvgGlTessellator.h b/src/renderer/gl_engine/tvgGlTessellator.h index 19818371..622a2c4d 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.h +++ b/src/renderer/gl_engine/tvgGlTessellator.h @@ -54,7 +54,7 @@ public: Tessellator(Array* points, Array* indices); ~Tessellator(); - void tessellate(const RenderShape *rshape, bool antialias = false); + bool tessellate(const RenderShape *rshape, bool antialias = false); void tessellate(const Array &shapes); @@ -65,9 +65,9 @@ private: void mergeVertices(); - void simplifyMesh(); + bool simplifyMesh(); - void tessMesh(); + bool tessMesh(); bool matchFillRule(int32_t winding);