// TODO: This is ugly static inline ScreenCoords ClipToScreenInternal(const ClipCoords& coords, bool set_flag = true) { ScreenCoords ret; // TODO: Check for invalid parameters (x2 < x1, etc) float vpx1 = getFloat24(gstate.viewportx1); float vpx2 = getFloat24(gstate.viewportx2); float vpy1 = getFloat24(gstate.viewporty1); float vpy2 = getFloat24(gstate.viewporty2); float vpz1 = getFloat24(gstate.viewportz1); float vpz2 = getFloat24(gstate.viewportz2); float retx = coords.x * vpx1 / coords.w + vpx2; float rety = coords.y * vpy1 / coords.w + vpy2; float retz = coords.z * vpz1 / coords.w + vpz2; if (gstate.clipEnable & 0x1) { if (retz < 0.f) retz = 0.f; if (retz > 65535.f) retz = 65535.f; } if (set_flag && (retx > 4095.9375f || rety > 4096.9375f || retx < 0 || rety < 0 || retz < 0 || retz > 65535.f)) outside_range_flag = true; // 16 = 0xFFFF / 4095.9375 return ScreenCoords(retx * 16, rety * 16, retz); }
static inline ScreenCoords ClipToScreenInternal(const ClipCoords& coords, bool *outside_range_flag) { ScreenCoords ret; // Parameters here can seem invalid, but the PSP is fine with negative viewport widths etc. // The checking that OpenGL and D3D do is actually quite superflous as the calculations still "work" // with some pretty crazy inputs, which PSP games are happy to do at times. float xScale = gstate.getViewportXScale(); float xCenter = gstate.getViewportXCenter(); float yScale = gstate.getViewportYScale(); float yCenter = gstate.getViewportYCenter(); float zScale = gstate.getViewportZScale(); float zCenter = gstate.getViewportZCenter(); float x = coords.x * xScale / coords.w + xCenter; float y = coords.y * yScale / coords.w + yCenter; float z = coords.z * zScale / coords.w + zCenter; // Is this really right? if (gstate.clipEnable & 0x1) { if (z < 0.f) z = 0.f; if (z > 65535.f) z = 65535.f; } if (outside_range_flag && (x > 4095.9375f || y > 4095.9375f || x < 0 || y < 0 || z < 0 || z > 65535.f)) *outside_range_flag = true; // 16 = 0xFFFF / 4095.9375 return ScreenCoords(x * 16, y * 16, z); }
static inline ScreenCoords ClipToScreenInternal(const ClipCoords& coords, bool *outside_range_flag) { ScreenCoords ret; // Parameters here can seem invalid, but the PSP is fine with negative viewport widths etc. // The checking that OpenGL and D3D do is actually quite superflous as the calculations still "work" // with some pretty crazy inputs, which PSP games are happy to do at times. float xScale = gstate.getViewportXScale(); float xCenter = gstate.getViewportXCenter(); float yScale = gstate.getViewportYScale(); float yCenter = gstate.getViewportYCenter(); float zScale = gstate.getViewportZScale(); float zCenter = gstate.getViewportZCenter(); float x = coords.x * xScale / coords.w + xCenter; float y = coords.y * yScale / coords.w + yCenter; float z = coords.z * zScale / coords.w + zCenter; // This matches hardware tests - depth is clamped when this flag is on. if (gstate.isClippingEnabled()) { if (z < 0.f) z = 0.f; if (z > 65535.f) z = 65535.f; } if (outside_range_flag && (x > 4095.9375f || y > 4095.9375f || x < 0 || y < 0 || z < 0 || z > 65535.f)) *outside_range_flag = true; // 16 = 0xFFFF / 4095.9375 // Round up at 0.625 to the nearest subpixel. return ScreenCoords(x * 16.0f + 0.375f, y * 16.0f + 0.375f, z); }
static inline ScreenCoords ClipToScreenInternal(const ClipCoords& coords, bool *outside_range_flag) { ScreenCoords ret; // Parameters here can seem invalid, but the PSP is fine with negative viewport widths etc. // The checking that OpenGL and D3D do is actually quite superflous as the calculations still "work" // with some pretty crazy inputs, which PSP games are happy to do at times. float xScale = gstate.getViewportXScale(); float xCenter = gstate.getViewportXCenter(); float yScale = gstate.getViewportYScale(); float yCenter = gstate.getViewportYCenter(); float zScale = gstate.getViewportZScale(); float zCenter = gstate.getViewportZCenter(); float x = coords.x * xScale / coords.w + xCenter; float y = coords.y * yScale / coords.w + yCenter; float z = coords.z * zScale / coords.w + zCenter; // Account for rounding for X and Y. // TODO: Validate actual rounding range. const float SCREEN_BOUND = 4095.0f + (15.5f / 16.0f); const float DEPTH_BOUND = 65535.5f; // This matches hardware tests - depth is clamped when this flag is on. if (gstate.isDepthClampEnabled()) { // Note: if the depth is clamped, the outside_range_flag should NOT be set, even for x and y. if (z < 0.f) z = 0.f; else if (z > 65535.0f) z = 65535.0f; else if (outside_range_flag && (x >= SCREEN_BOUND || y >= SCREEN_BOUND || x < 0 || y < 0)) *outside_range_flag = true; } else if (outside_range_flag && (x > SCREEN_BOUND || y >= SCREEN_BOUND || x < 0 || y < 0 || z < 0 || z >= DEPTH_BOUND)) { *outside_range_flag = true; } // 16 = 0xFFFF / 4095.9375 // Round up at 0.625 to the nearest subpixel. return ScreenCoords(x * 16.0f + 0.375f, y * 16.0f + 0.375f, z); }
void ProcessRect(const VertexData& v0, const VertexData& v1) { if (!gstate.isModeThrough()) { VertexData buf[4]; buf[0].clippos = ClipCoords(v0.clippos.x, v0.clippos.y, v1.clippos.z, v1.clippos.w); buf[0].texturecoords = v0.texturecoords; buf[1].clippos = ClipCoords(v0.clippos.x, v1.clippos.y, v1.clippos.z, v1.clippos.w); buf[1].texturecoords = Vec2<float>(v0.texturecoords.x, v1.texturecoords.y); buf[2].clippos = ClipCoords(v1.clippos.x, v0.clippos.y, v1.clippos.z, v1.clippos.w); buf[2].texturecoords = Vec2<float>(v1.texturecoords.x, v0.texturecoords.y); buf[3] = v1; // Color and depth values of second vertex are used for the whole rectangle buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0; buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1; buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth; VertexData* topleft = &buf[0]; VertexData* topright = &buf[1]; VertexData* bottomleft = &buf[2]; VertexData* bottomright = &buf[3]; for (int i = 0; i < 4; ++i) { if (buf[i].clippos.x < topleft->clippos.x && buf[i].clippos.y < topleft->clippos.y) topleft = &buf[i]; if (buf[i].clippos.x > topright->clippos.x && buf[i].clippos.y < topright->clippos.y) topright = &buf[i]; if (buf[i].clippos.x < bottomleft->clippos.x && buf[i].clippos.y > bottomleft->clippos.y) bottomleft = &buf[i]; if (buf[i].clippos.x > bottomright->clippos.x && buf[i].clippos.y > bottomright->clippos.y) bottomright = &buf[i]; } // Four triangles to do backfaces as well. Two of them will get backface culled. ProcessTriangle(*topleft, *topright, *bottomright, buf[3]); ProcessTriangle(*bottomright, *topright, *topleft, buf[3]); ProcessTriangle(*bottomright, *bottomleft, *topleft, buf[3]); ProcessTriangle(*topleft, *bottomleft, *bottomright, buf[3]); } else { // through mode handling VertexData buf[4]; buf[0].screenpos = ScreenCoords(v0.screenpos.x, v0.screenpos.y, v1.screenpos.z); buf[0].texturecoords = v0.texturecoords; buf[1].screenpos = ScreenCoords(v0.screenpos.x, v1.screenpos.y, v1.screenpos.z); buf[1].texturecoords = Vec2<float>(v0.texturecoords.x, v1.texturecoords.y); buf[2].screenpos = ScreenCoords(v1.screenpos.x, v0.screenpos.y, v1.screenpos.z); buf[2].texturecoords = Vec2<float>(v1.texturecoords.x, v0.texturecoords.y); buf[3] = v1; // Color and depth values of second vertex are used for the whole rectangle buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0; buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1; buf[0].clippos.w = buf[1].clippos.w = buf[2].clippos.w = buf[3].clippos.w = 1.0f; buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth = 1.0f; VertexData* topleft = &buf[0]; VertexData* topright = &buf[1]; VertexData* bottomleft = &buf[2]; VertexData* bottomright = &buf[3]; // Um. Why is this stuff needed? for (int i = 0; i < 4; ++i) { if (buf[i].screenpos.x < topleft->screenpos.x && buf[i].screenpos.y < topleft->screenpos.y) topleft = &buf[i]; if (buf[i].screenpos.x > topright->screenpos.x && buf[i].screenpos.y < topright->screenpos.y) topright = &buf[i]; if (buf[i].screenpos.x < bottomleft->screenpos.x && buf[i].screenpos.y > bottomleft->screenpos.y) bottomleft = &buf[i]; if (buf[i].screenpos.x > bottomright->screenpos.x && buf[i].screenpos.y > bottomright->screenpos.y) bottomright = &buf[i]; } RotateUVThrough(v0, v1, *topright, *bottomleft); if (gstate.isModeClear()) { Rasterizer::ClearRectangle(v0, v1); } else { // Four triangles to do backfaces as well. Two of them will get backface culled. Rasterizer::DrawTriangle(*topleft, *topright, *bottomright); Rasterizer::DrawTriangle(*bottomright, *topright, *topleft); Rasterizer::DrawTriangle(*bottomright, *bottomleft, *topleft); Rasterizer::DrawTriangle(*topleft, *bottomleft, *bottomright); } } }
void ProcessQuad(const VertexData& v0, const VertexData& v1) { if (!gstate.isModeThrough()) { VertexData buf[4]; buf[0].clippos = ClipCoords(v0.clippos.x, v0.clippos.y, v1.clippos.z, v1.clippos.w); buf[0].texturecoords = v0.texturecoords; buf[1].clippos = ClipCoords(v0.clippos.x, v1.clippos.y, v1.clippos.z, v1.clippos.w); buf[1].texturecoords = Vec2<float>(v0.texturecoords.x, v1.texturecoords.y); buf[2].clippos = ClipCoords(v1.clippos.x, v0.clippos.y, v1.clippos.z, v1.clippos.w); buf[2].texturecoords = Vec2<float>(v1.texturecoords.x, v0.texturecoords.y); buf[3] = v1; // Color and depth values of second vertex are used for the whole rectangle buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0; buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1; VertexData* topleft = &buf[0]; VertexData* topright = &buf[1]; VertexData* bottomleft = &buf[2]; VertexData* bottomright = &buf[3]; for (int i = 0; i < 4; ++i) { if (buf[i].clippos.x < topleft->clippos.x && buf[i].clippos.y < topleft->clippos.y) topleft = &buf[i]; if (buf[i].clippos.x > topright->clippos.x && buf[i].clippos.y < topright->clippos.y) topright = &buf[i]; if (buf[i].clippos.x < bottomleft->clippos.x && buf[i].clippos.y > bottomleft->clippos.y) bottomleft = &buf[i]; if (buf[i].clippos.x > bottomright->clippos.x && buf[i].clippos.y > bottomright->clippos.y) bottomright = &buf[i]; } ProcessTriangle(*topleft, *topright, *bottomright); ProcessTriangle(*bottomright, *topright, *topleft); ProcessTriangle(*bottomright, *bottomleft, *topleft); ProcessTriangle(*topleft, *bottomleft, *bottomright); } else { // through mode handling VertexData buf[4]; buf[0].screenpos = ScreenCoords(v0.screenpos.x, v0.screenpos.y, v1.screenpos.z); buf[0].texturecoords = v0.texturecoords; buf[1].screenpos = ScreenCoords(v0.screenpos.x, v1.screenpos.y, v1.screenpos.z); buf[1].texturecoords = Vec2<float>(v0.texturecoords.x, v1.texturecoords.y); buf[2].screenpos = ScreenCoords(v1.screenpos.x, v0.screenpos.y, v1.screenpos.z); buf[2].texturecoords = Vec2<float>(v1.texturecoords.x, v0.texturecoords.y); buf[3] = v1; // Color and depth values of second vertex are used for the whole rectangle buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0; buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1; buf[0].clippos.w = buf[1].clippos.w = buf[2].clippos.w = buf[3].clippos.w = 1.0f; VertexData* topleft = &buf[0]; VertexData* topright = &buf[1]; VertexData* bottomleft = &buf[2]; VertexData* bottomright = &buf[3]; for (int i = 0; i < 4; ++i) { if (buf[i].screenpos.x < topleft->screenpos.x && buf[i].screenpos.y < topleft->screenpos.y) topleft = &buf[i]; if (buf[i].screenpos.x > topright->screenpos.x && buf[i].screenpos.y < topright->screenpos.y) topright = &buf[i]; if (buf[i].screenpos.x < bottomleft->screenpos.x && buf[i].screenpos.y > bottomleft->screenpos.y) bottomleft = &buf[i]; if (buf[i].screenpos.x > bottomright->screenpos.x && buf[i].screenpos.y > bottomright->screenpos.y) bottomright = &buf[i]; } Rasterizer::DrawTriangle(*topleft, *topright, *bottomright); Rasterizer::DrawTriangle(*bottomright, *topright, *topleft); Rasterizer::DrawTriangle(*bottomright, *bottomleft, *topleft); Rasterizer::DrawTriangle(*topleft, *bottomleft, *bottomright); } }