summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/VertexLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoCommon/VertexLoader.cpp')
-rw-r--r--Source/Core/VideoCommon/VertexLoader.cpp346
1 files changed, 321 insertions, 25 deletions
diff --git a/Source/Core/VideoCommon/VertexLoader.cpp b/Source/Core/VideoCommon/VertexLoader.cpp
index 4a389bc636..cb7e29e488 100644
--- a/Source/Core/VideoCommon/VertexLoader.cpp
+++ b/Source/Core/VideoCommon/VertexLoader.cpp
@@ -52,6 +52,7 @@ static int s_texmtxread = 0;
static int loop_counter;
+
// Vertex loaders read these. Although the scale ones should be baked into the shader.
int tcIndex;
int colIndex;
@@ -60,9 +61,20 @@ int colElements[2];
float posScale;
float tcScale[8];
+// bbox variables
// bbox must read vertex position, so convert it to this buffer
static float s_bbox_vertex_buffer[3];
static u8 *s_bbox_pCurBufferPointer_orig;
+static int s_bbox_primitive;
+static struct Point
+{
+ s32 x;
+ s32 y;
+ float z;
+} s_bbox_points[3];
+static u8 s_bbox_currPoint;
+static u8 s_bbox_loadedPoints;
+static const u8 s_bbox_primitivePoints[8] = { 3, 0, 3, 3, 3, 2, 2, 1 };
static const float fractionTable[32] = {
1.0f / (1U << 0), 1.0f / (1U << 1), 1.0f / (1U << 2), 1.0f / (1U << 3),
@@ -102,47 +114,326 @@ void LOADERDECL UpdateBoundingBoxPrepare()
VertexManager::s_pCurBufferPointer = (u8*)s_bbox_vertex_buffer;
}
+inline bool UpdateBoundingBoxVars()
+{
+ switch (s_bbox_primitive)
+ {
+ // Quads: fill 0,1,2 (check),1 (check, clear, repeat)
+ case 0:
+ ++s_bbox_loadedPoints;
+ if (s_bbox_loadedPoints == 3)
+ {
+ s_bbox_currPoint = 1;
+ return true;
+ }
+ if (s_bbox_loadedPoints == 4)
+ {
+ s_bbox_loadedPoints = 0;
+ s_bbox_currPoint = 0;
+ return true;
+ }
+ ++s_bbox_currPoint;
+ return false;
+
+ // Triangles: 0,1,2 (check, clear, repeat)
+ case 2:
+ ++s_bbox_loadedPoints;
+ if (s_bbox_loadedPoints == 3)
+ {
+ s_bbox_loadedPoints = 0;
+ s_bbox_currPoint = 0;
+ return true;
+ }
+ ++s_bbox_currPoint;
+ return false;
+
+ // Triangle strip: 0, 1, 2 (check), 0 (check), 1, (check), 2 (check, repeat checking 0, 1, 2)
+ case 3:
+ if (++s_bbox_currPoint == 3)
+ s_bbox_currPoint = 0;
+
+ if (s_bbox_loadedPoints == 2)
+ return true;
+
+ ++s_bbox_loadedPoints;
+ return false;
+
+ // Triangle fan: 0,1,2 (check), 1 (check), 2 (check, repeat checking 1,2)
+ case 4:
+ s_bbox_currPoint ^= s_bbox_currPoint ? 3 : 1;
+
+ if (s_bbox_loadedPoints == 2)
+ return true;
+
+ ++s_bbox_loadedPoints;
+ return false;
+
+ // Lines: 0,1 (check, clear, repeat)
+ case 5:
+ ++s_bbox_loadedPoints;
+ if (s_bbox_loadedPoints == 2)
+ {
+ s_bbox_loadedPoints = 0;
+ s_bbox_currPoint = 0;
+ return true;
+ }
+ ++s_bbox_currPoint;
+ return false;
+
+ // Line strip: 0,1 (check), 0 (check), 1 (check, repeat checking 0,1)
+ case 6:
+ s_bbox_currPoint ^= 1;
+
+ if (s_bbox_loadedPoints == 1)
+ return true;
+
+ ++s_bbox_loadedPoints;
+ return false;
+
+ // Points: 0 (check, clear, repeat)
+ case 7:
+ return true;
+
+ // This should not happen!
+ default:
+ return false;
+ }
+}
+
void LOADERDECL UpdateBoundingBox()
{
if (!PixelEngine::bbox_active)
return;
- // reset videodata pointer
+ // Reset videodata pointer
VertexManager::s_pCurBufferPointer = s_bbox_pCurBufferPointer_orig;
- // copy vertex pointers
+ // Copy vertex pointers
memcpy(VertexManager::s_pCurBufferPointer, s_bbox_vertex_buffer, 12);
VertexManager::s_pCurBufferPointer += 12;
- // We must transform the just loaded point by the current world and projection matrix - in software.
- // Then convert to screen space and update the bounding box.
- float p[3] = {s_bbox_vertex_buffer[0], s_bbox_vertex_buffer[1], s_bbox_vertex_buffer[2]};
+ // We must transform the just loaded point by the current world and projection matrix - in software
+ float transformed[3];
+ float screenPoint[3];
+
+ // We need to get the raw projection values for the bounding box calculation
+ // to work properly. That means, no projection hacks!
+ const float * const orig_point = s_bbox_vertex_buffer;
+ const float * const world_matrix = (float*)xfmem + MatrixIndexA.PosNormalMtxIdx * 4;
+ const float * const proj_matrix = xfregs.projection.rawProjection;
+
+ // Transform by world matrix
+ // Only calculate what we need, discard the rest
+ transformed[0] = orig_point[0] * world_matrix[0] + orig_point[1] * world_matrix[1] + orig_point[2] * world_matrix[2] + world_matrix[3];
+ transformed[1] = orig_point[0] * world_matrix[4] + orig_point[1] * world_matrix[5] + orig_point[2] * world_matrix[6] + world_matrix[7];
+
+ // Transform by projection matrix
+ switch (xfregs.projection.type)
+ {
+ // Perspective projection, we must divide by w
+ case GX_PERSPECTIVE:
+ transformed[2] = orig_point[0] * world_matrix[8] + orig_point[1] * world_matrix[9] + orig_point[2] * world_matrix[10] + world_matrix[11];
+ screenPoint[0] = (transformed[0] * proj_matrix[0] + transformed[2] * proj_matrix[1]) / (-transformed[2]);
+ screenPoint[1] = (transformed[1] * proj_matrix[2] + transformed[2] * proj_matrix[3]) / (-transformed[2]);
+ screenPoint[2] = ((transformed[2] * proj_matrix[4] + proj_matrix[5]) * (1.0f - (float) 1e-7)) / (-transformed[2]);
+ break;
+
+ // Orthographic projection
+ case GX_ORTHOGRAPHIC:
+ screenPoint[0] = transformed[0] * proj_matrix[0] + proj_matrix[1];
+ screenPoint[1] = transformed[1] * proj_matrix[2] + proj_matrix[3];
+
+ // We don't really have to care about z here
+ screenPoint[2] = -0.2f;
+ break;
+
+ default:
+ ERROR_LOG(VIDEO, "Unknown projection type: %d", xfregs.projection.type);
+ }
+
+ // Convert to screen space and add the point to the list - round like the real hardware
+ s_bbox_points[s_bbox_currPoint].x = (((s32)(0.5 + (16.0f * (screenPoint[0] * xfregs.viewport.wd + (xfregs.viewport.xOrig - 342.0f))))) + 6) >> 4;
+ s_bbox_points[s_bbox_currPoint].y = (((s32)(0.5 + (16.0f * (screenPoint[1] * xfregs.viewport.ht + (xfregs.viewport.yOrig - 342.0f))))) + 6) >> 4;
+ s_bbox_points[s_bbox_currPoint].z = screenPoint[2];
+
+ // Update point list for primitive
+ bool check_bbox = UpdateBoundingBoxVars();
+
+ // If we do not have enough points to check the bounding box yet, we are done for now
+ if (!check_bbox)
+ return;
+
+ // How many points does our primitive have?
+ const u8 numPoints = s_bbox_primitivePoints[s_bbox_primitive];
+
+ // If the primitive is a point, update the bounding box now
+ if (numPoints == 1)
+ {
+ Point & p = s_bbox_points[0];
+
+ // Point is out of bounds
+ if (p.x < 0 || p.x > 607 || p.y < 0 || p.y > 479 || p.z >= 0.0f)
+ return;
+
+ // Point is in bounds. Update bounding box if necessary and return
+ PixelEngine::bbox[0] = (p.x < PixelEngine::bbox[0]) ? p.x : PixelEngine::bbox[0];
+ PixelEngine::bbox[1] = (p.x > PixelEngine::bbox[1]) ? p.x : PixelEngine::bbox[1];
+ PixelEngine::bbox[2] = (p.y < PixelEngine::bbox[2]) ? p.y : PixelEngine::bbox[2];
+ PixelEngine::bbox[3] = (p.y > PixelEngine::bbox[3]) ? p.y : PixelEngine::bbox[3];
+
+ return;
+ }
+
+ // Now comes the fun part. We must clip the triangles/lines to the viewport - also in software
+ Point & p0 = s_bbox_points[0], &p1 = s_bbox_points[1], &p2 = s_bbox_points[2];
+
+ // Check for z-clip. This crude method is required for Mickey's Magical Mirror, at least
+ if ((p0.z > 0.0f) || (p1.z > 0.0f) || ((numPoints == 3) && (p2.z > 0.0f)))
+ return;
+
+ // Check points for bounds
+ u8 b0 = ((p0.x > 0) ? 1 : 0) | (((p0.y > 0) ? 1 : 0) << 1) | (((p0.x > 607) ? 1 : 0) << 2) | (((p0.y > 479) ? 1 : 0) << 3);
+ u8 b1 = ((p1.x > 0) ? 1 : 0) | (((p1.y > 0) ? 1 : 0) << 1) | (((p1.x > 607) ? 1 : 0) << 2) | (((p1.y > 479) ? 1 : 0) << 3);
+
+ // Let's be practical... If we only have a line, setting b2 to 3 saves an "if"-clause later on
+ u8 b2 = 3;
+
+ // Otherwise if we have a triangle, we need to check the third point
+ if (numPoints == 3)
+ b2 = ((p2.x > 0) ? 1 : 0) | (((p2.y > 0) ? 1 : 0) << 1) | (((p2.x > 607) ? 1 : 0) << 2) | (((p2.y > 479) ? 1 : 0) << 3);
+
+ // These are the internal bbox vars
+ s32 left = 608, right = -1, top = 480, bottom = -1;
+
+ // If the polygon is inside viewport, let's update the bounding box and be done with it
+ if ((b0 == 3) && (b0 == b1) && (b0 == b2))
+ {
+ // Line
+ if (numPoints == 2)
+ {
+ left = (p0.x < p1.x) ? p0.x : p1.x;
+ top = (p0.y < p1.y) ? p0.y : p1.y;
+ right = (p0.x > p1.x) ? p0.x : p1.x;
+ bottom = (p0.y > p1.y) ? p0.y : p1.y;
+ }
+
+ // Triangle
+ else
+ {
+ left = (p0.x < p1.x) ? (p0.x < p2.x) ? p0.x : p2.x : (p1.x < p2.x) ? p1.x : p2.x;
+ top = (p0.y < p1.y) ? (p0.y < p2.y) ? p0.y : p2.y : (p1.y < p2.y) ? p1.y : p2.y;
+ right = (p0.x > p1.x) ? (p0.x > p2.x) ? p0.x : p2.x : (p1.x > p2.x) ? p1.x : p2.x;
+ bottom = (p0.y > p1.y) ? (p0.y > p2.y) ? p0.y : p2.y : (p1.y > p2.y) ? p1.y : p2.y;
+ }
+
+ // Update bounding box
+ PixelEngine::bbox[0] = (left < PixelEngine::bbox[0]) ? left : PixelEngine::bbox[0];
+ PixelEngine::bbox[1] = (right > PixelEngine::bbox[1]) ? right : PixelEngine::bbox[1];
+ PixelEngine::bbox[2] = (top < PixelEngine::bbox[2]) ? top : PixelEngine::bbox[2];
+ PixelEngine::bbox[3] = (bottom > PixelEngine::bbox[3]) ? bottom : PixelEngine::bbox[3];
+
+ return;
+ }
+
+ // If it is not inside, then either it is completely outside, or it needs clipping.
+ // Check the primitive's lines
+ u8 i0 = b0 ^ b1;
+ u8 i1 = (numPoints == 3) ? (b1 ^ b2) : i0;
+ u8 i2 = (numPoints == 3) ? (b0 ^ b2) : i0;
+
+ // Primitive out of bounds - return
+ if (!(i0 | i1 | i2))
+ return;
+
+ // First point inside viewport - update internal bbox
+ if (b0 == 3)
+ {
+ left = p0.x;
+ top = p0.y;
+ right = p0.x;
+ bottom = p0.y;
+ }
+
+ // Second point inside
+ if (b1 == 3)
+ {
+ left = (p1.x < left) ? p1.x : left;
+ top = (p1.y < top) ? p1.y : top;
+ right = (p1.x > right) ? p1.x : right;
+ bottom = (p1.y > bottom) ? p1.y : bottom;
+ }
+
+ // Third point inside
+ if ((b2 == 3) && (numPoints == 3))
+ {
+ left = (p2.x < left) ? p2.x : left;
+ top = (p2.y < top) ? p2.y : top;
+ right = (p2.x > right) ? p2.x : right;
+ bottom = (p2.y > bottom) ? p2.y : bottom;
+ }
+
+ // Triangle equation vars
+ float m, c;
- const float *world_matrix = (float*)xfmem + MatrixIndexA.PosNormalMtxIdx * 4;
- const float *proj_matrix = &g_fProjectionMatrix[0];
+ // Some definitions to help with rounding later on
+ const float highNum = 89374289734.0f;
+ const float roundUp = 0.001f;
- float t[3];
- t[0] = p[0] * world_matrix[0] + p[1] * world_matrix[1] + p[2] * world_matrix[2] + world_matrix[3];
- t[1] = p[0] * world_matrix[4] + p[1] * world_matrix[5] + p[2] * world_matrix[6] + world_matrix[7];
- t[2] = p[0] * world_matrix[8] + p[1] * world_matrix[9] + p[2] * world_matrix[10] + world_matrix[11];
+ // Intersection result
+ s32 s;
- float o[3];
- o[0] = t[0] * proj_matrix[0] + t[1] * proj_matrix[1] + t[2] * proj_matrix[2] + proj_matrix[3];
- o[1] = t[0] * proj_matrix[4] + t[1] * proj_matrix[5] + t[2] * proj_matrix[6] + proj_matrix[7];
- o[2] = t[0] * proj_matrix[12] + t[1] * proj_matrix[13] + t[2] * proj_matrix[14] + proj_matrix[15];
+ // First line intersects
+ if (i0)
+ {
+ m = (p1.x - p0.x) ? ((p1.y - p0.y) / (p1.x - p0.x)) : highNum;
+ c = p0.y - (m * p0.x);
+ if (i0 & 1) { s = (s32)(c + roundUp); if (s >= 0 && s <= 479) left = 0; top = (s < top) ? s : top; bottom = (s > bottom) ? s : bottom; }
+ if (i0 & 2) { s = (s32)((-c / m) + roundUp); if (s >= 0 && s <= 607) top = 0; left = (s < left) ? s : left; right = (s > right) ? s : right; }
+ if (i0 & 4) { s = (s32)((m * 607) + c + roundUp); if (s >= 0 && s <= 479) right = 607; top = (s < top) ? s : top; bottom = (s > bottom) ? s : bottom; }
+ if (i0 & 8) { s = (s32)(((479 - c) / m) + roundUp); if (s >= 0 && s <= 607) bottom = 479; left = (s < left) ? s : left; right = (s > right) ? s : right; }
+ }
- o[0] /= o[2];
- o[1] /= o[2];
+ // Only check other lines if we are dealing with a triangle
+ if (numPoints == 3)
+ {
+ // Second line intersects
+ if (i1)
+ {
+ m = (p2.x - p1.x) ? ((p2.y - p1.y) / (p2.x - p1.x)) : highNum;
+ c = p1.y - (m * p1.x);
+ if (i1 & 1) { s = (s32)(c + roundUp); if (s >= 0 && s <= 479) left = 0; top = (s < top) ? s : top; bottom = (s > bottom) ? s : bottom; }
+ if (i1 & 2) { s = (s32)((-c / m) + roundUp); if (s >= 0 && s <= 607) top = 0; left = (s < left) ? s : left; right = (s > right) ? s : right; }
+ if (i1 & 4) { s = (s32)((m * 607) + c + roundUp); if (s >= 0 && s <= 479) right = 607; top = (s < top) ? s : top; bottom = (s > bottom) ? s : bottom; }
+ if (i1 & 8) { s = (s32)(((479 - c) / m) + roundUp); if (s >= 0 && s <= 607) bottom = 479; left = (s < left) ? s : left; right = (s > right) ? s : right; }
+ }
- // Max width seems to be 608, while max height is 480
- // Here height is set to 484 as BBox bottom always seems to be off by a few pixels
- o[0] = (o[0] + 1.0f) * 304.0f;
- o[1] = (1.0f - o[1]) * 242.0f;
+ // Third line intersects
+ if (i2)
+ {
+ m = (p2.x - p0.x) ? ((p2.y - p0.y) / (p2.x - p0.x)) : highNum;
+ c = p0.y - (m * p0.x);
+ if (i2 & 1) { s = (s32)(c + roundUp); if (s >= 0 && s <= 479) left = 0; top = (s < top) ? s : top; bottom = (s > bottom) ? s : bottom; }
+ if (i2 & 2) { s = (s32)((-c / m) + roundUp); if (s >= 0 && s <= 607) top = 0; left = (s < left) ? s : left; right = (s > right) ? s : right; }
+ if (i2 & 4) { s = (s32)((m * 607) + c + roundUp); if (s >= 0 && s <= 479) right = 607; top = (s < top) ? s : top; bottom = (s > bottom) ? s : bottom; }
+ if (i2 & 8) { s = (s32)(((479 - c) / m) + roundUp); if (s >= 0 && s <= 607) bottom = 479; left = (s < left) ? s : left; right = (s > right) ? s : right; }
+ }
+ }
- if (o[0] < PixelEngine::bbox[0]) PixelEngine::bbox[0] = (u16) std::max(0.0f, o[0]);
- if (o[0] > PixelEngine::bbox[1]) PixelEngine::bbox[1] = (u16) o[0];
- if (o[1] < PixelEngine::bbox[2]) PixelEngine::bbox[2] = (u16) std::max(0.0f, o[1]);
- if (o[1] > PixelEngine::bbox[3]) PixelEngine::bbox[3] = (u16) o[1];
+ // Wrong bounding box values, discard this polygon (it is outside)
+ if (left > 607 || top > 479 || right < 0 || bottom < 0)
+ return;
+
+ // Trim bounding box to viewport
+ left = (left < 0) ? 0 : left;
+ top = (top < 0) ? 0 : top;
+ right = (right > 607) ? 607 : right;
+ bottom = (bottom > 479) ? 479 : bottom;
+
+ // Update bounding box
+ PixelEngine::bbox[0] = (left < PixelEngine::bbox[0]) ? left : PixelEngine::bbox[0];
+ PixelEngine::bbox[1] = (right > PixelEngine::bbox[1]) ? right : PixelEngine::bbox[1];
+ PixelEngine::bbox[2] = (top < PixelEngine::bbox[2]) ? top : PixelEngine::bbox[2];
+ PixelEngine::bbox[3] = (bottom > PixelEngine::bbox[3]) ? bottom : PixelEngine::bbox[3];
}
void LOADERDECL TexMtx_ReadDirect_UByte()
@@ -584,6 +875,11 @@ int VertexLoader::SetupRunVertices(int vtx_attr_group, int primitive, int const
for (int i = 0; i < 2; i++)
colElements[i] = m_VtxAttr.color[i].Elements;
+ // Prepare bounding box
+ s_bbox_primitive = primitive;
+ s_bbox_currPoint = 0;
+ s_bbox_loadedPoints = 0;
+
VertexManager::PrepareForAdditionalData(primitive, count, native_stride);
return count;