/* Calculate bounding box from triangle homogeneous coordinates.
Source: Blinn - Calculating Screen Coverage).
XL = - 1, XR = 1
YB = - 1, YT = 1
ZN = - 1, ZF = 1
*/
void GPUMath::boundingBox(QuadFloat v1, QuadFloat v2, QuadFloat v3,
s32bit x0, s32bit y0, u32bit width, u32bit height, s32bit &xMin, s32bit &xMax,
s32bit &yMin, s32bit &yMax, s32bit &zMin, s32bit &zMax)
{
u32bit outcodes[3];
bool anyVis[3];
u32bit accept;
u32bit reject;
f32bit minX, maxX, minY, maxY, minZ, maxZ;
#define BOUNDCODES(c, w, a, b, co0, co1) \
(((((c) - (a) * (w)) < 0.0f)?(co0):0x00) | (((-(c) + (b) * (w)) < 0.0f)?(co1):0x00))
/* Calculate the horizontal outcodes for the three vertices. */
outcodes[0] = BOUNDCODES(v1[0], v1[3], -1.0f, 1.0f, 0x01, 0x02);
outcodes[1] = BOUNDCODES(v2[0], v2[3], -1.0f, 1.0f, 0x01, 0x02);
outcodes[2] = BOUNDCODES(v3[0], v3[3], -1.0f, 1.0f, 0x01, 0x02);
/* Calculate the vertical outcodes for the three vertices. */
outcodes[0] |= BOUNDCODES(v1[1], v1[3], -1.0f, 1.0f, 0x04, 0x08);
outcodes[1] |= BOUNDCODES(v2[1], v2[3], -1.0f, 1.0f, 0x04, 0x08);
outcodes[2] |= BOUNDCODES(v3[1], v3[3], -1.0f, 1.0f, 0x04, 0x08);
/* Calculate the depth outcodes for the three vertices. */
outcodes[0] |= BOUNDCODES(v1[2], v1[3], -1.0f, 1.0f, 0x10, 0x20);
outcodes[1] |= BOUNDCODES(v2[2], v2[3], -1.0f, 1.0f, 0x10, 0x20);
outcodes[2] |= BOUNDCODES(v3[2], v3[3], -1.0f, 1.0f, 0x10, 0x20);
#undef BOUNDCODES
/* Calculate trivial acception. */
accept = outcodes[0] | outcodes[1] | outcodes[2];
/* Calculate trivial rejection. */
reject = outcodes[0] & outcodes[1] & outcodes[2];
//printf("outcodes[0] = %04x outcodes[1] = %04x outcodes[2] = %04x\n",
//outcodes[0], outcodes[1], outcodes[2]);
/* Check trivial rejection. */
GPU_ASSERT(
if (reject != 0)
panic("GPUMath", "boundingBox", "Triangle is trivially rejected (shouldn't happen here!).");
)
/* Set initial bounding box limits. */
minX = minY = minZ = 1.0f;
maxX = maxY = maxZ = -1.0f;
/* Set visibility flags. */
anyVis[0] = anyVis[1] = anyVis[2] = FALSE;
#define BOUNDRANGE(outcode, mask, c, w, min, max, vis)\
if (((outcode) & (mask)) == 0) \
{ \
vis = TRUE; \
\
if (((c) - (min) * (w)) < 0.0f) \
(min) = (c) / (w); \
if (((c) - (max) * (w)) > 0.0f) \
(max) = (c) / (w); \
}
/* Calculate horizontal range. */
BOUNDRANGE(outcodes[0], 0x03, v1[0], v1[3], minX, maxX, anyVis[0])
BOUNDRANGE(outcodes[1], 0x03, v2[0], v2[3], minX, maxX, anyVis[0])
BOUNDRANGE(outcodes[2], 0x03, v3[0], v3[3], minX, maxX, anyVis[0])
/* Calculate vertical range. */
BOUNDRANGE(outcodes[0], 0x0c, v1[1], v1[3], minY, maxY, anyVis[1])
BOUNDRANGE(outcodes[1], 0x0c, v2[1], v2[3], minY, maxY, anyVis[1])
BOUNDRANGE(outcodes[2], 0x0c, v3[1], v3[3], minY, maxY, anyVis[1])
/* Calculate depth range. */
BOUNDRANGE(outcodes[0], 0x30, v1[2], v1[3], minZ, maxZ, anyVis[2])
BOUNDRANGE(outcodes[1], 0x30, v2[2], v2[3], minZ, maxZ, anyVis[2])
BOUNDRANGE(outcodes[2], 0x30, v3[2], v3[3], minZ, maxZ, anyVis[2])
#undef BOUNDRANGE
//printf("First pass >> %f %f %f %f\n", minX, maxX, minY, maxY);
#define BOUNDRANGE2(outcode, mask0, mask1, c, w, min, max, a, b) \
if ((((outcode) & (mask0)) != 0) && (((c) - (min) * (w)) < 0.0f)) \
(min) = (a); \
if ((((outcode) & (mask1)) != 0) && (((c) - (max) * (w)) > 0.0f)) \
(max) = (b);
/* Check if all the vertices are visible (trivial accept). */
if (accept != 0)
{
/* No trivial accept, second pass required. */
/* Reset boundaries if none of the triangle points are visible. */
if (!anyVis[0])
{
minX = -1.0f;
maxX = 1.0f;
}
else
{
/* Second pass. */
BOUNDRANGE2(outcodes[0], 0x01, 0x02, v1[0], v1[3], minX, maxX, -1.0f, 1.0f);
BOUNDRANGE2(outcodes[1], 0x01, 0x02, v2[0], v2[3], minX, maxX, -1.0f, 1.0f);
BOUNDRANGE2(outcodes[2], 0x01, 0x02, v3[0], v3[3], minX, maxX, -1.0f, 1.0f);
}
if (!anyVis[1])
{
minY = -1.0f;
maxY = 1.0f;
}
else
{
/* Second pass. */
BOUNDRANGE2(outcodes[0], 0x04, 0x08, v1[1], v1[3], minY, maxY, -1.0f, 1.0f);
BOUNDRANGE2(outcodes[1], 0x04, 0x08, v2[1], v2[3], minY, maxY, -1.0f, 1.0f);
BOUNDRANGE2(outcodes[2], 0x04, 0x08, v3[1], v3[3], minY, maxY, -1.0f, 1.0f);
}
if (!anyVis[2])
{
minZ = -1.0f;
maxZ = 1.0f;
}
else
{
/* Second pass. */
BOUNDRANGE2(outcodes[0], 0x10, 0x20, v1[2], v1[3], minZ, maxZ, -1.0f, 1.0f);
BOUNDRANGE2(outcodes[1], 0x10, 0x20, v2[2], v2[3], minZ, maxZ, -1.0f, 1.0f);
BOUNDRANGE2(outcodes[2], 0x10, 0x20, v3[2], v3[3], minZ, maxZ, -1.0f, 1.0f);
}
}
#undef BOUNDRANGE2
//printf("Second pass >> %f %f %f %f\n", minX, maxX, minY, maxY);
/* Convert to viewport coordinates. */
xMin = GPU_MAX(s32bit(GPU_FLOOR(minX * (f32bit(width) * 0.5f) + f32bit(x0) + (f32bit(width) * 0.5f))) - 1, s32bit(x0));
xMax = GPU_MIN(s32bit(GPU_FLOOR(maxX * (f32bit(width) * 0.5f) + f32bit(x0) + (f32bit(width) * 0.5f))) + 1, s32bit(x0 + width));
yMin = GPU_MAX(s32bit(GPU_FLOOR(minY * (f32bit(height) * 0.5f) + f32bit(y0) + (f32bit(height) * 0.5f))) - 1, s32bit(y0));
yMax = GPU_MIN(s32bit(GPU_FLOOR(maxY * (f32bit(height) * 0.5f) + f32bit(y0) + (f32bit(height) * 0.5f))) + 1, s32bit(y0 + height));
//printf("Viewport coords: %d %d %d %d\n", xMin, xMax, yMin, yMax);
}