- Use static ifs (C++11 version of them, anyhow) for the triangle draw function

This commit is contained in:
Magnus Norddahl 2018-12-06 03:10:14 +01:00
parent e22e249287
commit 2aefeb6401
2 changed files with 192 additions and 65 deletions

View file

@ -54,6 +54,8 @@ static void SortVertices(const TriDrawTriangleArgs *args, ShadedTriVertex **sort
void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread) void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread)
{ {
using namespace TriScreenDrawerModes;
// Sort vertices by Y position // Sort vertices by Y position
ShadedTriVertex *sortedVertices[3]; ShadedTriVertex *sortedVertices[3];
SortVertices(args, sortedVertices); SortVertices(args, sortedVertices);
@ -75,8 +77,7 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
// Find start/end X positions for each line covered by the triangle: // Find start/end X positions for each line covered by the triangle:
int leftEdge[MAXHEIGHT]; int16_t edges[MAXHEIGHT * 2];
int rightEdge[MAXHEIGHT];
float longDX = sortedVertices[2]->x - sortedVertices[0]->x; float longDX = sortedVertices[2]->x - sortedVertices[0]->x;
float longDY = sortedVertices[2]->y - sortedVertices[0]->y; float longDY = sortedVertices[2]->y - sortedVertices[0]->y;
@ -98,8 +99,8 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
x0 = clamp(x0, 0, clipright); x0 = clamp(x0, 0, clipright);
x1 = clamp(x1, 0, clipright); x1 = clamp(x1, 0, clipright);
leftEdge[y] = x0; edges[y << 1] = x0;
rightEdge[y] = x1; edges[(y << 1) + 1] = x1;
shortPos += shortStep; shortPos += shortStep;
longPos += longStep; longPos += longStep;
@ -121,64 +122,96 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
x0 = clamp(x0, 0, clipright); x0 = clamp(x0, 0, clipright);
x1 = clamp(x1, 0, clipright); x1 = clamp(x1, 0, clipright);
leftEdge[y] = x0; edges[y << 1] = x0;
rightEdge[y] = x1; edges[(y << 1) + 1] = x1;
shortPos += shortStep; shortPos += shortStep;
longPos += longStep; longPos += longStep;
} }
} }
// Draw the triangle: int opt = 0;
if (args->uniforms->DepthTest()) opt |= SWTRI_DepthTest;
/*if (args->uniforms->StencilTest())*/ opt |= SWTRI_StencilTest;
if (args->uniforms->WriteColor()) opt |= SWTRI_WriteColor;
if (args->uniforms->WriteDepth()) opt |= SWTRI_WriteDepth;
if (args->uniforms->WriteStencil()) opt |= SWTRI_WriteStencil;
int bmode = (int)args->uniforms->BlendMode(); TriangleDrawers[opt](args, thread, edges, topY, bottomY);
auto drawfunc = args->destBgra ? ScreenTriangle::SpanDrawers32[bmode] : ScreenTriangle::SpanDrawers8[bmode]; }
float stepXW = args->gradientX.W; template<typename OptT>
float v1X = args->v1->x; void DrawTriangle(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread, int16_t *edges, int topY, int bottomY)
float v1Y = args->v1->y; {
float v1W = args->v1->w; using namespace TriScreenDrawerModes;
bool depthTest = args->uniforms->DepthTest(); void(*drawfunc)(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread);
bool stencilTest = true; float stepXW, v1X, v1Y, v1W, posXW;
bool writeColor = args->uniforms->WriteColor(); uint8_t stencilTestValue, stencilWriteValue;
bool writeStencil = args->uniforms->WriteStencil(); float *zbufferLine;
bool writeDepth = args->uniforms->WriteDepth(); uint8_t *stencilLine;
uint8_t stencilTestValue = args->uniforms->StencilTestValue();
uint8_t stencilWriteValue = args->uniforms->StencilWriteValue(); if (OptT::Flags & SWTRI_WriteColor)
{
int bmode = (int)args->uniforms->BlendMode();
drawfunc = args->destBgra ? ScreenTriangle::SpanDrawers32[bmode] : ScreenTriangle::SpanDrawers8[bmode];
}
if ((OptT::Flags & SWTRI_DepthTest) || (OptT::Flags & SWTRI_WriteDepth))
{
stepXW = args->gradientX.W;
v1X = args->v1->x;
v1Y = args->v1->y;
v1W = args->v1->w;
}
if (OptT::Flags & SWTRI_StencilTest)
stencilTestValue = args->uniforms->StencilTestValue();
if (OptT::Flags & SWTRI_WriteStencil)
stencilWriteValue = args->uniforms->StencilWriteValue();
int num_cores = thread->num_cores; int num_cores = thread->num_cores;
for (int y = topY + thread->skipped_by_thread(topY); y < bottomY; y += num_cores) for (int y = topY + thread->skipped_by_thread(topY); y < bottomY; y += num_cores)
{ {
int x = leftEdge[y]; int x = edges[y << 1];
int xend = rightEdge[y]; int xend = edges[(y << 1) + 1];
float *zbufferLine = args->zbuffer + args->stencilpitch * y; if ((OptT::Flags & SWTRI_StencilTest) || (OptT::Flags & SWTRI_WriteStencil))
uint8_t *stencilLine = args->stencilbuffer + args->stencilpitch * y; stencilLine = args->stencilbuffer + args->stencilpitch * y;
float startX = x + (0.5f - v1X); if ((OptT::Flags & SWTRI_DepthTest) || (OptT::Flags & SWTRI_WriteDepth))
float startY = y + (0.5f - v1Y); {
float posXW = v1W + stepXW * startX + args->gradientY.W * startY + args->depthOffset; zbufferLine = args->zbuffer + args->stencilpitch * y;
float startX = x + (0.5f - v1X);
float startY = y + (0.5f - v1Y);
posXW = v1W + stepXW * startX + args->gradientY.W * startY + args->depthOffset;
}
#ifndef NO_SSE #ifndef NO_SSE
__m128 mstepXW = _mm_set1_ps(stepXW * 4.0f); __m128 mstepXW, mfirstStepXW;
__m128 mfirstStepXW = _mm_setr_ps(0.0f, stepXW, stepXW + stepXW, stepXW + stepXW + stepXW); if ((OptT::Flags & SWTRI_DepthTest) || (OptT::Flags & SWTRI_WriteDepth))
{
mstepXW = _mm_set1_ps(stepXW * 4.0f);
mfirstStepXW = _mm_setr_ps(0.0f, stepXW, stepXW + stepXW, stepXW + stepXW + stepXW);
}
while (x < xend) while (x < xend)
{ {
int xstart = x; int xstart = x;
if (depthTest && stencilTest) if ((OptT::Flags & SWTRI_DepthTest) && (OptT::Flags & SWTRI_StencilTest))
{ {
int xendsse = x + ((xend - x) / 4); int xendsse = x + ((xend - x) / 4);
__m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW);
while (_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 15 && while (x < xendsse &&
_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 15 &&
stencilLine[x] == stencilTestValue && stencilLine[x] == stencilTestValue &&
stencilLine[x + 1] == stencilTestValue && stencilLine[x + 1] == stencilTestValue &&
stencilLine[x + 2] == stencilTestValue && stencilLine[x + 2] == stencilTestValue &&
stencilLine[x + 3] == stencilTestValue && stencilLine[x + 3] == stencilTestValue)
x < xendsse)
{ {
if (writeDepth) if (OptT::Flags & SWTRI_WriteDepth)
_mm_storeu_ps(zbufferLine + x, mposXW); _mm_storeu_ps(zbufferLine + x, mposXW);
mposXW = _mm_add_ps(mposXW, mstepXW); mposXW = _mm_add_ps(mposXW, mstepXW);
x += 4; x += 4;
@ -187,19 +220,19 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
while (zbufferLine[x] <= posXW && stencilLine[x] == stencilTestValue && x < xend) while (zbufferLine[x] <= posXW && stencilLine[x] == stencilTestValue && x < xend)
{ {
if (writeDepth) if (OptT::Flags & SWTRI_WriteDepth)
zbufferLine[x] = posXW; zbufferLine[x] = posXW;
posXW += stepXW; posXW += stepXW;
x++; x++;
} }
} }
else if (depthTest) else if (OptT::Flags & SWTRI_DepthTest)
{ {
int xendsse = x + ((xend - x) / 4); int xendsse = x + ((xend - x) / 4);
__m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW);
while (_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 15 && x < xendsse) while (x < xendsse && _mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 15)
{ {
if (writeDepth) if (OptT::Flags & SWTRI_WriteDepth)
_mm_storeu_ps(zbufferLine + x, mposXW); _mm_storeu_ps(zbufferLine + x, mposXW);
mposXW = _mm_add_ps(mposXW, mstepXW); mposXW = _mm_add_ps(mposXW, mstepXW);
x += 4; x += 4;
@ -208,14 +241,20 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
while (zbufferLine[x] <= posXW && x < xend) while (zbufferLine[x] <= posXW && x < xend)
{ {
if (writeDepth) if (OptT::Flags & SWTRI_WriteDepth)
zbufferLine[x] = posXW; zbufferLine[x] = posXW;
posXW += stepXW; posXW += stepXW;
x++; x++;
} }
} }
else if (stencilTest) else if (OptT::Flags & SWTRI_StencilTest)
{ {
int xendsse = x + ((xend - x) / 16);
while (x < xendsse && _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_loadu_si128((const __m128i*)&stencilLine[x]), _mm_set1_epi8(stencilTestValue))) == 0xffff)
{
x += 16;
}
while (stencilLine[x] == stencilTestValue && x < xend) while (stencilLine[x] == stencilTestValue && x < xend)
x++; x++;
} }
@ -226,16 +265,24 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
if (x > xstart) if (x > xstart)
{ {
if (writeColor) if (OptT::Flags & SWTRI_WriteColor)
drawfunc(y, xstart, x, args, thread); drawfunc(y, xstart, x, args, thread);
if (writeStencil) if (OptT::Flags & SWTRI_WriteStencil)
{ {
for (int i = xstart; i < x; i++) int i = xstart;
stencilLine[i] = stencilWriteValue; int xendsse = xstart + ((x - xstart) / 16);
while (i < xendsse)
{
_mm_storeu_si128((__m128i*)&stencilLine[i], _mm_set1_epi8(stencilWriteValue));
i += 16;
}
while (i < x)
stencilLine[i++] = stencilWriteValue;
} }
if (!depthTest && writeDepth) if (!(OptT::Flags & SWTRI_DepthTest) && (OptT::Flags & SWTRI_WriteDepth))
{ {
for (int i = xstart; i < x; i++) for (int i = xstart; i < x; i++)
{ {
@ -245,16 +292,16 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
} }
} }
if (depthTest && stencilTest) if ((OptT::Flags & SWTRI_DepthTest) && (OptT::Flags & SWTRI_StencilTest))
{ {
int xendsse = x + ((xend - x) / 4); int xendsse = x + ((xend - x) / 4);
__m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW);
while ((_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 0 || while (x < xendsse &&
(_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 0 ||
stencilLine[x] != stencilTestValue || stencilLine[x] != stencilTestValue ||
stencilLine[x + 1] != stencilTestValue || stencilLine[x + 1] != stencilTestValue ||
stencilLine[x + 2] != stencilTestValue || stencilLine[x + 2] != stencilTestValue ||
stencilLine[x + 3] != stencilTestValue) && stencilLine[x + 3] != stencilTestValue))
x < xendsse)
{ {
mposXW = _mm_add_ps(mposXW, mstepXW); mposXW = _mm_add_ps(mposXW, mstepXW);
x += 4; x += 4;
@ -267,11 +314,11 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
x++; x++;
} }
} }
else if (depthTest) else if (OptT::Flags & SWTRI_DepthTest)
{ {
int xendsse = x + ((xend - x) / 4); int xendsse = x + ((xend - x) / 4);
__m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW);
while (_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 0 && x < xendsse) while (x < xendsse && _mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 0)
{ {
mposXW = _mm_add_ps(mposXW, mstepXW); mposXW = _mm_add_ps(mposXW, mstepXW);
x += 4; x += 4;
@ -284,11 +331,16 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
x++; x++;
} }
} }
else if (stencilTest) else if (OptT::Flags & SWTRI_StencilTest)
{ {
int xendsse = x + ((xend - x) / 16);
while (x < xendsse && _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_loadu_si128((const __m128i*)&stencilLine[x]), _mm_set1_epi8(stencilTestValue))) == 0)
{
x += 16;
}
while (stencilLine[x] != stencilTestValue && x < xend) while (stencilLine[x] != stencilTestValue && x < xend)
{ {
posXW += stepXW;
x++; x++;
} }
} }
@ -298,27 +350,27 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
{ {
int xstart = x; int xstart = x;
if (depthTest && stencilTest) if ((OptT::Flags & SWTRI_DepthTest) && (OptT::Flags & SWTRI_StencilTest))
{ {
while (zbufferLine[x] <= posXW && stencilLine[x] == stencilTestValue && x < xend) while (zbufferLine[x] <= posXW && stencilLine[x] == stencilTestValue && x < xend)
{ {
if (writeDepth) if (OptT::Flags & SWTRI_WriteDepth)
zbufferLine[x] = posXW; zbufferLine[x] = posXW;
posXW += stepXW; posXW += stepXW;
x++; x++;
} }
} }
else if (depthTest) else if (OptT::Flags & SWTRI_DepthTest)
{ {
while (zbufferLine[x] <= posXW && x < xend) while (zbufferLine[x] <= posXW && x < xend)
{ {
if (writeDepth) if (OptT::Flags & SWTRI_WriteDepth)
zbufferLine[x] = posXW; zbufferLine[x] = posXW;
posXW += stepXW; posXW += stepXW;
x++; x++;
} }
} }
else if (stencilTest) else if (OptT::Flags & SWTRI_StencilTest)
{ {
while (stencilLine[x] == stencilTestValue && x < xend) while (stencilLine[x] == stencilTestValue && x < xend)
x++; x++;
@ -330,16 +382,16 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
if (x > xstart) if (x > xstart)
{ {
if (writeColor) if (OptT::Flags & SWTRI_WriteColor)
drawfunc(y, xstart, x, args, thread); drawfunc(y, xstart, x, args, thread);
if (writeStencil) if (OptT::Flags & SWTRI_WriteStencil)
{ {
for (int i = xstart; i < x; i++) for (int i = xstart; i < x; i++)
stencilLine[i] = stencilWriteValue; stencilLine[i] = stencilWriteValue;
} }
if (!depthTest && writeDepth) if (!(OptT::Flags & SWTRI_DepthTest) && (OptT::Flags & SWTRI_WriteDepth))
{ {
for (int i = xstart; i < x; i++) for (int i = xstart; i < x; i++)
{ {
@ -349,7 +401,7 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
} }
} }
if (depthTest && stencilTest) if ((OptT::Flags & SWTRI_DepthTest) && (OptT::Flags & SWTRI_StencilTest))
{ {
while ((zbufferLine[x] > posXW || stencilLine[x] != stencilTestValue) && x < xend) while ((zbufferLine[x] > posXW || stencilLine[x] != stencilTestValue) && x < xend)
{ {
@ -357,7 +409,7 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
x++; x++;
} }
} }
else if (depthTest) else if (OptT::Flags & SWTRI_DepthTest)
{ {
while (zbufferLine[x] > posXW && x < xend) while (zbufferLine[x] > posXW && x < xend)
{ {
@ -365,11 +417,10 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
x++; x++;
} }
} }
else if (stencilTest) else if (OptT::Flags & SWTRI_StencilTest)
{ {
while (stencilLine[x] != stencilTestValue && x < xend) while (stencilLine[x] != stencilTestValue && x < xend)
{ {
posXW += stepXW;
x++; x++;
} }
} }
@ -2164,4 +2215,40 @@ void(*ScreenTriangle::RectDrawers32[])(const void *, int, int, int, const RectDr
&DrawRect32<TriScreenDrawerModes::StyleAddShadedTranslated> &DrawRect32<TriScreenDrawerModes::StyleAddShadedTranslated>
}; };
void(*ScreenTriangle::TriangleDrawers[])(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread, int16_t *edges, int topY, int bottomY) =
{
nullptr,
nullptr,
nullptr,
nullptr,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt4>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt5>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt6>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt7>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt8>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt9>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt10>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt11>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt12>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt13>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt14>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt15>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt16>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt17>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt18>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt19>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt20>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt21>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt22>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt23>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt24>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt25>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt26>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt27>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt28>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt29>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt30>,
&DrawTriangle<TriScreenDrawerModes::TriangleOpt31>
};
int ScreenTriangle::FuzzStart = 0; int ScreenTriangle::FuzzStart = 0;

View file

@ -142,6 +142,8 @@ class ScreenTriangle
public: public:
static void Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread); static void Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread);
static void(*TriangleDrawers[])(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread, int16_t *edges, int topY, int bottomY);
static void(*SpanDrawers8[])(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread); static void(*SpanDrawers8[])(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread);
static void(*SpanDrawers32[])(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread); static void(*SpanDrawers32[])(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread);
static void(*RectDrawers8[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *); static void(*RectDrawers8[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *);
@ -216,4 +218,42 @@ namespace TriScreenDrawerModes
11, 6, 6, 11, 15, 6, 6, 11, 15, 18, 11, 6, 6, 11, 15, 6, 6, 11, 15, 18,
21, 6, 6, 6, 6, 11, 6, 6, 11, 6, 21, 6, 6, 6, 6, 11, 6, 6, 11, 6,
}; };
enum SWTriangleFlags
{
SWTRI_DepthTest = 1,
SWTRI_StencilTest = 2,
SWTRI_WriteColor = 4,
SWTRI_WriteDepth = 8,
SWTRI_WriteStencil = 16
};
struct TriangleOpt4 { static const int Flags = 4; };
struct TriangleOpt5 { static const int Flags = 5; };
struct TriangleOpt6 { static const int Flags = 6; };
struct TriangleOpt7 { static const int Flags = 7; };
struct TriangleOpt8 { static const int Flags = 8; };
struct TriangleOpt9 { static const int Flags = 9; };
struct TriangleOpt10 { static const int Flags = 10; };
struct TriangleOpt11 { static const int Flags = 11; };
struct TriangleOpt12 { static const int Flags = 12; };
struct TriangleOpt13 { static const int Flags = 13; };
struct TriangleOpt14 { static const int Flags = 14; };
struct TriangleOpt15 { static const int Flags = 15; };
struct TriangleOpt16 { static const int Flags = 16; };
struct TriangleOpt17 { static const int Flags = 17; };
struct TriangleOpt18 { static const int Flags = 18; };
struct TriangleOpt19 { static const int Flags = 19; };
struct TriangleOpt20 { static const int Flags = 20; };
struct TriangleOpt21 { static const int Flags = 21; };
struct TriangleOpt22 { static const int Flags = 22; };
struct TriangleOpt23 { static const int Flags = 23; };
struct TriangleOpt24 { static const int Flags = 24; };
struct TriangleOpt25 { static const int Flags = 25; };
struct TriangleOpt26 { static const int Flags = 26; };
struct TriangleOpt27 { static const int Flags = 27; };
struct TriangleOpt28 { static const int Flags = 28; };
struct TriangleOpt29 { static const int Flags = 29; };
struct TriangleOpt30 { static const int Flags = 30; };
struct TriangleOpt31 { static const int Flags = 31; };
} }