From f492e92cb5c3214a096c35934b0055c35f4be56c Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 10 May 2017 02:43:00 +0200 Subject: [PATCH] - Handle degenerate triangles either sent as input or caused by clipping --- src/polyrenderer/drawers/poly_triangle.cpp | 32 +++++++++++++++++----- src/polyrenderer/drawers/poly_triangle.h | 1 + src/polyrenderer/drawers/screen_triangle.h | 32 ++++++++++++---------- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/polyrenderer/drawers/poly_triangle.cpp b/src/polyrenderer/drawers/poly_triangle.cpp index 19628cf47..1cf9609cc 100644 --- a/src/polyrenderer/drawers/poly_triangle.cpp +++ b/src/polyrenderer/drawers/poly_triangle.cpp @@ -152,8 +152,28 @@ ShadedTriVertex PolyTriangleDrawer::shade_vertex(const TriMatrix &objectToClip, return sv; } +bool PolyTriangleDrawer::is_degenerate(const ShadedTriVertex *vert) +{ + // A degenerate triangle has a zero cross product for two of its sides. + float ax = vert[1].x - vert[0].x; + float ay = vert[1].y - vert[0].y; + float az = vert[1].w - vert[0].w; + float bx = vert[2].x - vert[0].x; + float by = vert[2].y - vert[0].y; + float bz = vert[2].w - vert[0].w; + float crossx = ay * bz - az * by; + float crossy = az * bx - ax * bz; + float crossz = ax * by - ay * bx; + float crosslengthsqr = crossx * crossx + crossy * crossy + crossz * crossz; + return crosslengthsqr <= 1.e-6f; +} + void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread) { + // Reject triangle if degenerate + if (is_degenerate(vert)) + return; + // Cull, clip and generate additional vertices as needed TriVertex clippedvert[max_additional_vertices]; int numclipvert = clipedge(vert, clippedvert); @@ -224,14 +244,13 @@ void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool // Draw screen triangles if (ccw) { - for (int i = numclipvert; i > 1; i--) + for (int i = numclipvert - 1; i > 1; i--) { args->v1 = &clippedvert[numclipvert - 1]; args->v2 = &clippedvert[i - 1]; args->v3 = &clippedvert[i - 2]; - args->CalculateGradients(); - - ScreenTriangle::Draw(args, thread); + if (args->CalculateGradients()) + ScreenTriangle::Draw(args, thread); } } else @@ -241,9 +260,8 @@ void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool args->v1 = &clippedvert[0]; args->v2 = &clippedvert[i - 1]; args->v3 = &clippedvert[i]; - args->CalculateGradients(); - - ScreenTriangle::Draw(args, thread); + if (args->CalculateGradients()) + ScreenTriangle::Draw(args, thread); } } } diff --git a/src/polyrenderer/drawers/poly_triangle.h b/src/polyrenderer/drawers/poly_triangle.h index c939149d3..c663cd2d0 100644 --- a/src/polyrenderer/drawers/poly_triangle.h +++ b/src/polyrenderer/drawers/poly_triangle.h @@ -47,6 +47,7 @@ private: static ShadedTriVertex shade_vertex(const TriMatrix &objectToClip, const float *clipPlane, const TriVertex &v); static void draw_arrays(const PolyDrawArgs &args, WorkerThreadData *thread); static void draw_shaded_triangle(const ShadedTriVertex *vertices, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread); + static bool is_degenerate(const ShadedTriVertex *vertices); static int clipedge(const ShadedTriVertex *verts, TriVertex *clippedvert); diff --git a/src/polyrenderer/drawers/screen_triangle.h b/src/polyrenderer/drawers/screen_triangle.h index 3dd4c24eb..4339edaf4 100644 --- a/src/polyrenderer/drawers/screen_triangle.h +++ b/src/polyrenderer/drawers/screen_triangle.h @@ -66,29 +66,31 @@ struct TriDrawTriangleArgs ScreenTriangleStepVariables gradientX; ScreenTriangleStepVariables gradientY; - void CalculateGradients() + bool CalculateGradients() { - gradientX.W = FindGradientX(v1->x, v1->y, v2->x, v2->y, v3->x, v3->y, v1->w, v2->w, v3->w); - gradientY.W = FindGradientY(v1->x, v1->y, v2->x, v2->y, v3->x, v3->y, v1->w, v2->w, v3->w); - gradientX.U = FindGradientX(v1->x, v1->y, v2->x, v2->y, v3->x, v3->y, v1->u * v1->w, v2->u * v2->w, v3->u * v3->w); - gradientY.U = FindGradientY(v1->x, v1->y, v2->x, v2->y, v3->x, v3->y, v1->u * v1->w, v2->u * v2->w, v3->u * v3->w); - gradientX.V = FindGradientX(v1->x, v1->y, v2->x, v2->y, v3->x, v3->y, v1->v * v1->w, v2->v * v2->w, v3->v * v3->w); - gradientY.V = FindGradientY(v1->x, v1->y, v2->x, v2->y, v3->x, v3->y, v1->v * v1->w, v2->v * v2->w, v3->v * v3->w); + float bottomX = (v2->x - v3->x) * (v1->y - v3->y) - (v1->x - v3->x) * (v2->y - v3->y); + float bottomY = (v1->x - v3->x) * (v2->y - v3->y) - (v2->x - v3->x) * (v1->y - v3->y); + if ((bottomX >= -FLT_EPSILON && bottomX <= FLT_EPSILON) || (bottomY >= -FLT_EPSILON && bottomY <= FLT_EPSILON)) + return false; + + gradientX.W = FindGradientX(bottomX, v1->w, v2->w, v3->w); + gradientY.W = FindGradientY(bottomY, v1->w, v2->w, v3->w); + gradientX.U = FindGradientX(bottomX, v1->u * v1->w, v2->u * v2->w, v3->u * v3->w); + gradientY.U = FindGradientY(bottomY, v1->u * v1->w, v2->u * v2->w, v3->u * v3->w); + gradientX.V = FindGradientX(bottomX, v1->v * v1->w, v2->v * v2->w, v3->v * v3->w); + gradientY.V = FindGradientY(bottomY, v1->v * v1->w, v2->v * v2->w, v3->v * v3->w); + return true; } private: - static float FindGradientX(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) + float FindGradientX(float bottomX, float c0, float c1, float c2) { - float top = (c1 - c2) * (y0 - y2) - (c0 - c2) * (y1 - y2); - float bottom = (x1 - x2) * (y0 - y2) - (x0 - x2) * (y1 - y2); - return top / bottom; + return ((c1 - c2) * (v1->y - v3->y) - (c0 - c2) * (v2->y - v3->y)) / bottomX; } - static float FindGradientY(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) + float FindGradientY(float bottomY, float c0, float c1, float c2) { - float top = (c1 - c2) * (x0 - x2) - (c0 - c2) * (x1 - x2); - float bottom = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); - return top / bottom; + return ((c1 - c2) * (v1->x - v3->x) - (c0 - c2) * (v2->x - v3->x)) / bottomY; } };