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
This commit is contained in:
RuiwenTang 2024-02-04 13:51:51 +08:00 committed by Hermet Park
parent a3e2189afa
commit 3f9b0eff22
3 changed files with 30 additions and 12 deletions

View file

@ -42,7 +42,10 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
fillIndex.clear(); fillIndex.clear();
Tessellator tess{&fillVertex, &fillIndex}; Tessellator tess{&fillVertex, &fillIndex};
tess.tessellate(&rshape, true); if (!tess.tessellate(&rshape, true)) {
fillVertex.clear();
fillIndex.clear();
}
} }
if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {

View file

@ -662,8 +662,10 @@ static bool _validEdgePair(Edge* left, Edge* right) {
bool ActiveEdgeList::valid() bool ActiveEdgeList::valid()
{ {
auto left = head; auto left = head;
if (!left) { if (!left && !tail) {
return true; return true;
} else if (!left || !tail) {
return false;
} }
for(auto right = left->right; right; right = right->right) { 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 cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count; auto cmdCnt = rshape->path.cmds.count;
@ -913,9 +915,9 @@ void Tessellator::tessellate(const RenderShape *rshape, bool antialias)
this->mergeVertices(); this->mergeVertices();
this->simplifyMesh(); if (!this->simplifyMesh()) return false;
this->tessMesh(); if (!this->tessMesh()) return false;
// output triangles // output triangles
for (auto poly = this->pPolygon; poly; poly = poly->next) { for (auto poly = this->pPolygon; poly; poly = poly->next) {
@ -935,6 +937,8 @@ void Tessellator::tessellate(const RenderShape *rshape, bool antialias)
if (antialias) { if (antialias) {
// TODO extract outline from current polygon list and generate aa edges // TODO extract outline from current polygon list and generate aa edges
} }
return true;
} }
void Tessellator::tessellate(const Array<const RenderShape *> &shapes) void Tessellator::tessellate(const Array<const RenderShape *> &shapes)
@ -1085,7 +1089,7 @@ void Tessellator::mergeVertices()
for (auto v = pMesh->head->next; v;) { for (auto v = pMesh->head->next; v;) {
auto next = v->next; 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 // already sorted, this means these two points is same
v->point = v->prev->point; 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 /// this is a basic sweep line algorithm
/// https://www.youtube.com/watch?v=qkhUNzCGDt0&t=293s /// https://www.youtube.com/watch?v=qkhUNzCGDt0&t=293s
@ -1134,6 +1138,11 @@ void Tessellator::simplifyMesh()
v->left = left_enclosing; v->left = left_enclosing;
v->right = right_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) { if (v->edge_below.head) {
for (auto e = v->edge_below.head; e; e = e->below_next) { for (auto e = v->edge_below.head; e; e = e->below_next) {
// check current edge is intersected by left or right neighbor edges // check current edge is intersected by left or right neighbor edges
@ -1154,7 +1163,7 @@ void Tessellator::simplifyMesh()
if (!ael.valid()) { if (!ael.valid()) {
// If AEL is not valid, means we meet the problem caused by floating point precision // 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 // we are done for all edge end with current point
@ -1170,9 +1179,11 @@ void Tessellator::simplifyMesh()
left = e; left = e;
} }
} }
return true;
} }
void Tessellator::tessMesh() bool Tessellator::tessMesh()
{ {
/// this function also use sweep line algorithm /// this function also use sweep line algorithm
/// but during the process, we calculate the winding number of left and right /// but during the process, we calculate the winding number of left and right
@ -1185,6 +1196,8 @@ void Tessellator::tessMesh()
continue; continue;
} }
if (!ael.valid()) return false;
detail::Edge *left_enclosing = nullptr; detail::Edge *left_enclosing = nullptr;
detail::Edge *right_enclosing = nullptr; detail::Edge *right_enclosing = nullptr;
@ -1315,6 +1328,8 @@ void Tessellator::tessMesh()
v->edge_below.tail->right_poly = right_poly; v->edge_below.tail->right_poly = right_poly;
} }
} }
return true;
} }
bool Tessellator::matchFillRule(int32_t winding) bool Tessellator::matchFillRule(int32_t winding)

View file

@ -54,7 +54,7 @@ public:
Tessellator(Array<float>* points, Array<uint32_t>* indices); Tessellator(Array<float>* points, Array<uint32_t>* indices);
~Tessellator(); ~Tessellator();
void tessellate(const RenderShape *rshape, bool antialias = false); bool tessellate(const RenderShape *rshape, bool antialias = false);
void tessellate(const Array<const RenderShape*> &shapes); void tessellate(const Array<const RenderShape*> &shapes);
@ -65,9 +65,9 @@ private:
void mergeVertices(); void mergeVertices();
void simplifyMesh(); bool simplifyMesh();
void tessMesh(); bool tessMesh();
bool matchFillRule(int32_t winding); bool matchFillRule(int32_t winding);