diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index d43ef1c685..522ced9755 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -136,7 +136,7 @@ static void RotateUVThrough(VertexData &tl, VertexData &tr, VertexData &bl, Vert } } -void ProcessQuad(const VertexData& v0, const VertexData& v1) +void ProcessRect(const VertexData& v0, const VertexData& v1) { if (!gstate.isModeThrough()) { VertexData buf[4]; diff --git a/GPU/Software/Clipper.h b/GPU/Software/Clipper.h index 7c1881a50b..e0926e6248 100644 --- a/GPU/Software/Clipper.h +++ b/GPU/Software/Clipper.h @@ -24,6 +24,6 @@ namespace Clipper { void ProcessPoint(VertexData& v0); void ProcessLine(VertexData& v0, VertexData& v1); void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2); -void ProcessQuad(const VertexData& v0, const VertexData& v1); +void ProcessRect(const VertexData& v0, const VertexData& v1); } diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index f1e6c0c6ba..339b200300 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -18,6 +18,7 @@ #include "base/basictypes.h" #include "Common/ThreadPools.h" +#include "Core/Config.h" #include "Core/MemMap.h" #include "Core/Reporting.h" #include "GPU/GPUState.h" @@ -835,6 +836,148 @@ static inline Vec3 AlphaBlendingResult(const Vec3& source_rgb, int sou } } +template +inline void DrawSinglePixel(const DrawingCoords &p, u16 z, Vec3 prim_color_rgb, int prim_color_a) { + // Depth range test + // TODO: Clear mode? + if (!gstate.isModeThrough()) + if (z < gstate.getDepthRangeMin() || z > gstate.getDepthRangeMax()) + return; + + if (gstate.isColorTestEnabled() && !clearMode) + if (!ColorTestPassed(prim_color_rgb)) + return; + + // TODO: Does a need to be clamped? + if (gstate.isAlphaTestEnabled() && !clearMode) + if (!AlphaTestPassed(prim_color_a)) + return; + + // In clear mode, it uses the alpha color as stencil. + u8 stencil = clearMode ? prim_color_a : GetPixelStencil(p.x, p.y); + // TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled? + if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) { + if (gstate.isStencilTestEnabled() && !StencilTestPassed(stencil)) { + stencil = ApplyStencilOp(gstate.getStencilOpSFail(), p.x, p.y); + SetPixelStencil(p.x, p.y, stencil); + return; + } + + // Also apply depth at the same time. If disabled, same as passing. + if (gstate.isDepthTestEnabled() && !DepthTestPassed(p.x, p.y, z)) { + if (gstate.isStencilTestEnabled()) { + stencil = ApplyStencilOp(gstate.getStencilOpZFail(), p.x, p.y); + SetPixelStencil(p.x, p.y, stencil); + } + return; + } else if (gstate.isStencilTestEnabled()) { + stencil = ApplyStencilOp(gstate.getStencilOpZPass(), p.x, p.y); + } + + if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) { + SetPixelDepth(p.x, p.y, z); + } + } else if (clearMode && gstate.isClearModeDepthMask()) { + SetPixelDepth(p.x, p.y, z); + } + + // Doubling happens only when texturing is enabled, and after tests. + if (gstate.isTextureMapEnabled() && gstate.isColorDoublingEnabled() && !clearMode) { + // TODO: Does this need to be clamped before blending? + prim_color_rgb *= 2; + } + + if (gstate.isAlphaBlendEnabled() && !clearMode) { + Vec4 dst = Vec4::FromRGBA(GetPixelColor(p.x, p.y)); + prim_color_rgb = AlphaBlendingResult(prim_color_rgb, prim_color_a, dst); + } + + if (!clearMode) + prim_color_rgb = prim_color_rgb.Clamp(0, 255); + + u32 new_color = Vec4(prim_color_rgb.r(), prim_color_rgb.g(), prim_color_rgb.b(), stencil).ToRGBA(); + u32 old_color = GetPixelColor(p.x, p.y); + + // TODO: Is alpha blending still performed if logic ops are enabled? + if (gstate.isLogicOpEnabled() && !clearMode) { + // Logic ops don't affect stencil. + new_color = (stencil << 24) | (ApplyLogicOp(gstate.getLogicOp(), old_color, new_color) & 0x00FFFFFF); + } + + if (clearMode) { + new_color = (new_color & ~gstate.getClearModeColorMask()) | (old_color & gstate.getClearModeColorMask()); + } else { + new_color = (new_color & ~gstate.getColorMask()) | (old_color & gstate.getColorMask()); + } + + // TODO: Dither before or inside SetPixelColor + SetPixelColor(p.x, p.y, new_color); +} + +inline void ApplyTexturing(Vec3 &prim_color_rgb, int &prim_color_a, float s, float t, int maxTexLevel, int magFilt, u8 *texptr[], int texbufwidthbits[]) { + int u[4] = {0}, v[4] = {0}; // 1.23.8 fixed point + int frac_u, frac_v; + + int texlevel = 0; + + bool bilinear = magFilt != 0; + // bilinear = false; + + if (gstate.isModeThrough()) { + int u_texel = s * 256; + int v_texel = t * 256; + frac_u = u_texel & 0xff; + frac_v = v_texel & 0xff; + u_texel >>= 8; + v_texel >>= 8; + + // we need to compute UV for a quad of pixels together in order to get the mipmap deltas :( + // texlevel = x + + if (texlevel > maxTexLevel) + texlevel = maxTexLevel; + + GetTexelCoordinatesThrough(texlevel, u_texel, v_texel, u[0], v[0]); + if (bilinear) { + GetTexelCoordinatesThrough(texlevel, u_texel + 1, v_texel, u[1], v[1]); + GetTexelCoordinatesThrough(texlevel, u_texel, v_texel + 1, u[2], v[2]); + GetTexelCoordinatesThrough(texlevel, u_texel + 1, v_texel + 1, u[3], v[3]); + } + } else { + // we need to compute UV for a quad of pixels together in order to get the mipmap deltas :( + // texlevel = x + + if (texlevel > maxTexLevel) + texlevel = maxTexLevel; + + if (bilinear) { + GetTexelCoordinatesQuad(texlevel, s, t, u, v, frac_u, frac_v); + } else { + GetTexelCoordinates(texlevel, s, t, u[0], v[0]); + } + } + + Vec4 texcolor; + int bufwbits = texbufwidthbits[texlevel]; + const u8 *tptr = texptr[texlevel]; + if (!bilinear) { + // Nearest filtering only. Round texcoords or just chop bits? + texcolor = Vec4::FromRGBA(SampleNearest(texlevel, u[0], v[0], tptr, bufwbits)); + } else { + Vec4 texcolor_tl = Vec4::FromRGBA(SampleNearest(texlevel, u[0], v[0], tptr, bufwbits)); + Vec4 texcolor_tr = Vec4::FromRGBA(SampleNearest(texlevel, u[1], v[1], tptr, bufwbits)); + Vec4 texcolor_bl = Vec4::FromRGBA(SampleNearest(texlevel, u[2], v[2], tptr, bufwbits)); + Vec4 texcolor_br = Vec4::FromRGBA(SampleNearest(texlevel, u[3], v[3], tptr, bufwbits)); + // 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255. + Vec4 t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u; + Vec4 b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u; + texcolor = (t * (0x100 - frac_v) + b * frac_v) / (256 * 256); + } + Vec4 out = GetTextureFunctionOutput(prim_color_rgb, prim_color_a, texcolor); + prim_color_rgb = out.rgb(); + prim_color_a = out.a(); +} + template void DrawTriangleSlice( const VertexData& v0, const VertexData& v1, const VertexData& v2, @@ -858,6 +1001,14 @@ void DrawTriangleSlice( int maxTexLevel = (gstate.texmode >> 16) & 7; u8 *texptr[8] = {NULL}; + int magFilt = (gstate.texfilter>>8) & 1; + if (g_Config.iTexFiltering > 1) { + if (g_Config.iTexFiltering == 2) { + magFilt = 0; + } else if (g_Config.iTexFiltering == 3) { + magFilt = 1; + } + } if ((gstate.texfilter & 4) == 0) { // No mipmapping enabled maxTexLevel = 0; @@ -933,78 +1084,18 @@ void DrawTriangleSlice( } if (gstate.isTextureMapEnabled() && !clearMode) { - int u[4] = {0}, v[4] = {0}; // 1.23.8 fixed point - int frac_u, frac_v; - - int texlevel = 0; - - int magFilt = (gstate.texfilter>>8) & 1; - - bool bilinear = magFilt != 0; - // bilinear = false; - if (gstate.isModeThrough()) { // TODO: Is it really this simple? float s = ((v0.texturecoords.s() * w0 + v1.texturecoords.s() * w1 + v2.texturecoords.s() * w2) * wsum); float t = ((v0.texturecoords.t() * w0 + v1.texturecoords.t() * w1 + v2.texturecoords.t() * w2) * wsum); - - int u_texel = s * 256; - int v_texel = t * 256; - frac_u = u_texel & 0xff; - frac_v = v_texel & 0xff; - u_texel >>= 8; - v_texel >>= 8; - - // we need to compute UV for a quad of pixels together in order to get the mipmap deltas :( - // texlevel = x - - if (texlevel > maxTexLevel) - texlevel = maxTexLevel; - - GetTexelCoordinatesThrough(texlevel, u_texel, v_texel, u[0], v[0]); - if (bilinear) { - GetTexelCoordinatesThrough(texlevel, u_texel + 1, v_texel, u[1], v[1]); - GetTexelCoordinatesThrough(texlevel, u_texel, v_texel + 1, u[2], v[2]); - GetTexelCoordinatesThrough(texlevel, u_texel + 1, v_texel + 1, u[3], v[3]); - } + ApplyTexturing(prim_color_rgb, prim_color_a, s, t, maxTexLevel, magFilt, texptr, texbufwidthbits); } else { float s = 0, t = 0; GetTextureCoordinates(v0, v1, v2, w0, w1, w2, s, t); s = s * texScaleU + texOffsetU; t = t * texScaleV + texOffsetV; - - // we need to compute UV for a quad of pixels together in order to get the mipmap deltas :( - // texlevel = x - - if (texlevel > maxTexLevel) - texlevel = maxTexLevel; - - if (bilinear) { - GetTexelCoordinatesQuad(texlevel, s, t, u, v, frac_u, frac_v); - } else { - GetTexelCoordinates(texlevel, s, t, u[0], v[0]); - } + ApplyTexturing(prim_color_rgb, prim_color_a, s, t, maxTexLevel, magFilt, texptr, texbufwidthbits); } - - Vec4 texcolor; - int bufwbits = texbufwidthbits[texlevel]; - const u8 *tptr = texptr[texlevel]; - if (!bilinear) { - // Nearest filtering only. Round texcoords or just chop bits? - texcolor = Vec4::FromRGBA(SampleNearest(texlevel, u[0], v[0], tptr, bufwbits)); - } else { - Vec4 texcolor_tl = Vec4::FromRGBA(SampleNearest(texlevel, u[0], v[0], tptr, bufwbits)); - Vec4 texcolor_tr = Vec4::FromRGBA(SampleNearest(texlevel, u[1], v[1], tptr, bufwbits)); - Vec4 texcolor_bl = Vec4::FromRGBA(SampleNearest(texlevel, u[2], v[2], tptr, bufwbits)); - Vec4 texcolor_br = Vec4::FromRGBA(SampleNearest(texlevel, u[3], v[3], tptr, bufwbits)); - // 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255. - Vec4 t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u; - Vec4 b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u; - texcolor = (t * (0x100 - frac_v) + b * frac_v) / (256 * 256); - } - Vec4 out = GetTextureFunctionOutput(prim_color_rgb, prim_color_a, texcolor); - prim_color_rgb = out.rgb(); - prim_color_a = out.a(); } if (!clearMode) @@ -1018,78 +1109,7 @@ void DrawTriangleSlice( if (!flatZ) z = (u16)(u32)(((float)v0.screenpos.z * w0 + (float)v1.screenpos.z * w1 + (float)v2.screenpos.z * w2) * wsum); - // Depth range test - // TODO: Clear mode? - if (!gstate.isModeThrough()) - if (z < gstate.getDepthRangeMin() || z > gstate.getDepthRangeMax()) - continue; - - if (gstate.isColorTestEnabled() && !clearMode) - if (!ColorTestPassed(prim_color_rgb)) - continue; - - // TODO: Does a need to be clamped? - if (gstate.isAlphaTestEnabled() && !clearMode) - if (!AlphaTestPassed(prim_color_a)) - continue; - - // In clear mode, it uses the alpha color as stencil. - u8 stencil = clearMode ? prim_color_a : GetPixelStencil(p.x, p.y); - // TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled? - if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) { - if (gstate.isStencilTestEnabled() && !StencilTestPassed(stencil)) { - stencil = ApplyStencilOp(gstate.getStencilOpSFail(), p.x, p.y); - SetPixelStencil(p.x, p.y, stencil); - continue; - } - - // Also apply depth at the same time. If disabled, same as passing. - if (gstate.isDepthTestEnabled() && !DepthTestPassed(p.x, p.y, z)) { - if (gstate.isStencilTestEnabled()) { - stencil = ApplyStencilOp(gstate.getStencilOpZFail(), p.x, p.y); - SetPixelStencil(p.x, p.y, stencil); - } - continue; - } else if (gstate.isStencilTestEnabled()) { - stencil = ApplyStencilOp(gstate.getStencilOpZPass(), p.x, p.y); - } - - if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) { - SetPixelDepth(p.x, p.y, z); - } - } else if (clearMode && gstate.isClearModeDepthMask()) { - SetPixelDepth(p.x, p.y, z); - } - - if (gstate.isAlphaBlendEnabled() && !clearMode) { - Vec4 dst = Vec4::FromRGBA(GetPixelColor(p.x, p.y)); - prim_color_rgb = AlphaBlendingResult(prim_color_rgb, prim_color_a, dst); - } - - if (gstate.isTextureMapEnabled() && gstate.isColorDoublingEnabled() && !clearMode) { - prim_color_rgb *= 2; - } - - if (!clearMode) - prim_color_rgb = prim_color_rgb.Clamp(0, 255); - - u32 new_color = Vec4(prim_color_rgb.r(), prim_color_rgb.g(), prim_color_rgb.b(), stencil).ToRGBA(); - u32 old_color = GetPixelColor(p.x, p.y); - - // TODO: Is alpha blending still performed if logic ops are enabled? - if (gstate.isLogicOpEnabled() && !clearMode) { - // Logic ops don't affect stencil. - new_color = (stencil << 24) | (ApplyLogicOp(gstate.getLogicOp(), old_color, new_color) & 0x00FFFFFF); - } - - if (clearMode) { - new_color = (new_color & ~gstate.getClearModeColorMask()) | (old_color & gstate.getClearModeColorMask()); - } else { - new_color = (new_color & ~gstate.getColorMask()) | (old_color & gstate.getColorMask()); - } - - // TODO: Dither before or inside SetPixelColor - SetPixelColor(p.x, p.y, new_color); + DrawSinglePixel(p, z, prim_color_rgb, prim_color_a); } } } @@ -1126,8 +1146,16 @@ void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData& GlobalThreadPool::Loop(std::bind(&DrawTriangleSlice, v0, v1, v2, minX, minY, maxX, maxY, placeholder::_1, placeholder::_2), 0, range); } -void DrawPixel(ScreenCoords pos, Vec3 prim_color_rgb, int prim_color_a, Vec3 sec_color) { - // TODO: Texturing, blending etc. +void DrawPoint(const VertexData &v0) +{ + ScreenCoords pos = v0.screenpos; + Vec3 prim_color_rgb = v0.color0.rgb(); + int prim_color_a = v0.color0.a(); + Vec3 sec_color = v0.color1; + // TODO: UVGenMode? + float s = v0.texturecoords.s(); + float t = v0.texturecoords.t(); + ScreenCoords scissorTL(TransformUnit::DrawingToScreen(DrawingCoords(gstate.getScissorX1(), gstate.getScissorY1(), 0))); ScreenCoords scissorBR(TransformUnit::DrawingToScreen(DrawingCoords(gstate.getScissorX2(), gstate.getScissorY2(), 0))); @@ -1136,11 +1164,49 @@ void DrawPixel(ScreenCoords pos, Vec3 prim_color_rgb, int prim_color_a, Vec bool clearMode = gstate.isModeClear(); - // TODO: Abstract out texture mapping so we can insert it here. Too big to duplicate. - if (gstate.isColorDoublingEnabled() && !clearMode) { - // TODO: Do we need to clamp here? - prim_color_rgb *= 2; - sec_color *= 2; + if (gstate.isTextureMapEnabled() && !clearMode) { + int texbufwidthbits[8] = {0}; + + int maxTexLevel = (gstate.texmode >> 16) & 7; + u8 *texptr[8] = {NULL}; + + int magFilt = (gstate.texfilter>>8) & 1; + if (g_Config.iTexFiltering > 1) { + if (g_Config.iTexFiltering == 2) { + magFilt = 0; + } else if (g_Config.iTexFiltering == 3) { + magFilt = 1; + } + } + if ((gstate.texfilter & 4) == 0) { + // No mipmapping enabled + maxTexLevel = 0; + } + + if (gstate.isTextureMapEnabled() && !clearMode) { + // TODO: Always using level 0. + maxTexLevel = 0; + GETextureFormat texfmt = gstate.getTextureFormat(); + for (int i = 0; i <= maxTexLevel; i++) { + u32 texaddr = gstate.getTextureAddress(i); + texbufwidthbits[i] = GetTextureBufw(i, texaddr, texfmt) * 8; + texptr[i] = Memory::GetPointer(texaddr); + } + } + + if (gstate.isModeThrough()) { + // TODO: Is it really this simple? + ApplyTexturing(prim_color_rgb, prim_color_a, s, t, maxTexLevel, magFilt, texptr, texbufwidthbits); + } else { + float texScaleU = getFloat24(gstate.texscaleu); + float texScaleV = getFloat24(gstate.texscalev); + float texOffsetU = getFloat24(gstate.texoffsetu); + float texOffsetV = getFloat24(gstate.texoffsetv); + + s = s * texScaleU + texOffsetU; + t = t * texScaleV + texOffsetV; + ApplyTexturing(prim_color_rgb, prim_color_a, s, t, maxTexLevel, magFilt, texptr, texbufwidthbits); + } } if (!clearMode) @@ -1152,77 +1218,11 @@ void DrawPixel(ScreenCoords pos, Vec3 prim_color_rgb, int prim_color_a, Vec DrawingCoords p = TransformUnit::ScreenToDrawing(pprime); u16 z = pos.z; - // Depth range test - // TODO: Clear mode? - if (!gstate.isModeThrough()) - if (z < gstate.getDepthRangeMin() || z > gstate.getDepthRangeMax()) - return; - - if (gstate.isColorTestEnabled() && !clearMode) - if (!ColorTestPassed(prim_color_rgb)) - return; - - // TODO: Does a need to be clamped? - if (gstate.isAlphaTestEnabled() && !clearMode) - if (!AlphaTestPassed(prim_color_a)) - return; - - // In clear mode, it uses the alpha color as stencil. - u8 stencil = clearMode ? prim_color_a : GetPixelStencil(p.x, p.y); - // TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled? - if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) { - if (gstate.isStencilTestEnabled() && !StencilTestPassed(stencil)) { - stencil = ApplyStencilOp(gstate.getStencilOpSFail(), p.x, p.y); - SetPixelStencil(p.x, p.y, stencil); - return; - } - - // Also apply depth at the same time. If disabled, same as passing. - if (gstate.isDepthTestEnabled() && !DepthTestPassed(p.x, p.y, z)) { - if (gstate.isStencilTestEnabled()) { - stencil = ApplyStencilOp(gstate.getStencilOpZFail(), p.x, p.y); - SetPixelStencil(p.x, p.y, stencil); - } - return; - } else if (gstate.isStencilTestEnabled()) { - stencil = ApplyStencilOp(gstate.getStencilOpZPass(), p.x, p.y); - } - - if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) { - SetPixelDepth(p.x, p.y, z); - } - } else if (clearMode && gstate.isClearModeDepthMask()) { - SetPixelDepth(p.x, p.y, z); - } - - if (gstate.isAlphaBlendEnabled() && !clearMode) { - Vec4 dst = Vec4::FromRGBA(GetPixelColor(p.x, p.y)); - prim_color_rgb = AlphaBlendingResult(prim_color_rgb, prim_color_a, dst); - } - if (!clearMode) - prim_color_rgb = prim_color_rgb.Clamp(0, 255); - - u32 new_color = Vec4(prim_color_rgb.r(), prim_color_rgb.g(), prim_color_rgb.b(), stencil).ToRGBA(); - u32 old_color = GetPixelColor(p.x, p.y); - - // TODO: Is alpha blending still performed if logic ops are enabled? - if (gstate.isLogicOpEnabled() && !clearMode) { - // Logic ops don't affect stencil. - new_color = (stencil << 24) | (ApplyLogicOp(gstate.getLogicOp(), old_color, new_color) & 0x00FFFFFF); - } - if (clearMode) { - new_color = (new_color & ~gstate.getClearModeColorMask()) | (old_color & gstate.getClearModeColorMask()); + DrawSinglePixel(p, z, prim_color_rgb, prim_color_a); } else { - new_color = (new_color & ~gstate.getColorMask()) | (old_color & gstate.getColorMask()); + DrawSinglePixel(p, z, prim_color_rgb, prim_color_a); } - - SetPixelColor(p.x, p.y, new_color); -} - -void DrawPoint(const VertexData &v0) -{ - DrawPixel(v0.screenpos, v0.color0.rgb(), v0.color0.a(), v0.color1); } void DrawLine(const VertexData &v0, const VertexData &v1) @@ -1245,12 +1245,84 @@ void DrawLine(const VertexData &v0, const VertexData &v1) float yinc = (float)dy / steps; float zinc = (float)dz / steps; + ScreenCoords scissorTL(TransformUnit::DrawingToScreen(DrawingCoords(gstate.getScissorX1(), gstate.getScissorY1(), 0))); + ScreenCoords scissorBR(TransformUnit::DrawingToScreen(DrawingCoords(gstate.getScissorX2(), gstate.getScissorY2(), 0))); + bool clearMode = gstate.isModeClear(); + + int texbufwidthbits[8] = {0}; + + int maxTexLevel = (gstate.texmode >> 16) & 7; + u8 *texptr[8] = {NULL}; + + int magFilt = (gstate.texfilter>>8) & 1; + if (g_Config.iTexFiltering > 1) { + if (g_Config.iTexFiltering == 2) { + magFilt = 0; + } else if (g_Config.iTexFiltering == 3) { + magFilt = 1; + } + } + if ((gstate.texfilter & 4) == 0) { + // No mipmapping enabled + maxTexLevel = 0; + } + + if (gstate.isTextureMapEnabled() && !clearMode) { + // TODO: Always using level 0. + GETextureFormat texfmt = gstate.getTextureFormat(); + for (int i = 0; i <= maxTexLevel; i++) { + u32 texaddr = gstate.getTextureAddress(i); + texbufwidthbits[i] = GetTextureBufw(i, texaddr, texfmt) * 8; + texptr[i] = Memory::GetPointer(texaddr); + } + } + + float texScaleU = getFloat24(gstate.texscaleu); + float texScaleV = getFloat24(gstate.texscalev); + float texOffsetU = getFloat24(gstate.texoffsetu); + float texOffsetV = getFloat24(gstate.texoffsetv); + float x = a.x; float y = a.y; float z = a.z; - for (; steps >= 0; steps--) { - // TODO: interpolate color and UV over line - DrawPixel(ScreenCoords(x, y, z), v0.color0.rgb(), v0.color0.a(), v0.color1); + for (int i = 0; i <= steps; i++) { + if (x < scissorTL.x || y < scissorTL.y || x >= scissorBR.x || y >= scissorBR.y) + continue; + + Vec4 c0 = (v0.color0 * (steps - i) + v1.color0 * i) / steps; + Vec3 sec_color = (v0.color1 * (steps - i) + v1.color1 * i) / steps; + // TODO: UVGenMode? + Vec2 tc = (v0.texturecoords * (float)(steps - i) + v1.texturecoords * (float)i) / steps; + Vec3 prim_color_rgb = c0.rgb(); + int prim_color_a = c0.a(); + float s = tc.s(); + float t = tc.t(); + + if (gstate.isTextureMapEnabled() && !clearMode) { + if (gstate.isModeThrough()) { + // TODO: Is it really this simple? + ApplyTexturing(prim_color_rgb, prim_color_a, s, t, maxTexLevel, magFilt, texptr, texbufwidthbits); + } else { + s = s * texScaleU + texOffsetU; + t = t * texScaleV + texOffsetV; + ApplyTexturing(prim_color_rgb, prim_color_a, s, t, maxTexLevel, magFilt, texptr, texbufwidthbits); + } + } + + if (!clearMode) + prim_color_rgb += sec_color; + + ScreenCoords pprime = ScreenCoords(x, y, z); + + // TODO: Fogging + DrawingCoords p = TransformUnit::ScreenToDrawing(pprime); + + if (clearMode) { + DrawSinglePixel(p, z, prim_color_rgb, prim_color_a); + } else { + DrawSinglePixel(p, z, prim_color_rgb, prim_color_a); + } + x = x + xinc; y = y + yinc; z = z + zinc; diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index 1a537a078a..68646bc6b9 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -352,7 +352,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type } case GE_PRIM_RECTANGLES: - Clipper::ProcessQuad(data[0], data[1]); + Clipper::ProcessRect(data[0], data[1]); break; case GE_PRIM_LINES: