From 842817c91dd34d64457c3f21db488b0ebb4eadb3 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 11 Jun 2015 16:01:17 +0200 Subject: [PATCH] Add fog support to software rasterizer (as if it wasn't slow enough already) --- GPU/Software/Clipper.cpp | 1 + GPU/Software/Rasterizer.cpp | 75 ++++++++++++++++++++++------------ GPU/Software/TransformUnit.cpp | 9 +++- GPU/Software/TransformUnit.h | 4 +- native | 2 +- 5 files changed, 61 insertions(+), 30 deletions(-) diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index b4552c82f9..f0bbacdd71 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -196,6 +196,7 @@ void ProcessRect(const VertexData& v0, const VertexData& v1) 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]; diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 0a6f1a1928..a25372d570 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -103,6 +103,15 @@ static inline u32 LookupColor(unsigned int index, unsigned int level) } } +static inline u8 ClampFogDepth(float fogdepth) { + if (fogdepth <= 0.0f) + return 0; + else if (fogdepth >= 1.0f) + return 255; + else + return (u8)(u32)(fogdepth * 255.0f); +} + static inline void GetTexelCoordinates(int level, float s, float t, int& out_u, int& out_v) { int width = 1 << (gstate.texsize[level] & 0xf); @@ -936,7 +945,7 @@ static inline Vec3 AlphaBlendingResult(const Vec4 &source, const Vec4< } template -inline void DrawSinglePixel(const DrawingCoords &p, u16 z, const Vec4 &color_in) { +inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &color_in) { Vec4 prim_color = color_in; // Depth range test // TODO: Clear mode? @@ -955,7 +964,7 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, const Vec4 &colo // 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? + // TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled? Probably yes if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) { if (gstate.isStencilTestEnabled() && !StencilTestPassed(stencil)) { stencil = ApplyStencilOp(gstate.getStencilOpSFail(), p.x, p.y); @@ -989,6 +998,14 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, const Vec4 &colo prim_color.b() <<= 1; } + if (gstate.isFogEnabled() && !gstate.isModeThrough() && !clearMode) { + Vec3 fogColor = Vec3::FromRGB(gstate.fogcolor); + fogColor = (prim_color.rgb() * (int)fog + fogColor * (255 - (int)fog)) / 255; + prim_color.r() = fogColor.r(); + prim_color.g() = fogColor.g(); + prim_color.b() = fogColor.b(); + } + const u32 old_color = GetPixelColor(p.x, p.y); u32 new_color; @@ -1200,6 +1217,7 @@ void DrawTriangleSlice( int w1_base = orient2d(v2.screenpos, v0.screenpos, pprime); int w2_base = orient2d(v0.screenpos, v1.screenpos, pprime); + // Step forward to y1 (slice..) w0_base += orient2dIncY(d12.x) * 16 * y1; w1_base += orient2dIncY(-d02.x) * 16 * y1; w2_base += orient2dIncY(d01.x) * 16 * y1; @@ -1226,20 +1244,18 @@ void DrawTriangleSlice( p.x = (p.x + 1) & 0x3FF) { // If p is on or inside all edges, render pixel - // TODO: Should we render if the pixel is both on the left and the right side? (i.e. degenerated triangle) if (w0 + bias0 >= 0 && w1 + bias1 >= 0 && w2 + bias2 >= 0) { - // TODO: Check if this check is still necessary - if (w0 == 0 && w1 == 0 && w2 == 0) + int wsum = w0 + w1 + w2; + if (wsum == 0.0f) continue; - - float wsum = 1.0f / (w0 + w1 + w2); + float wsum_recip = 1.0f / (float)wsum; Vec4 prim_color; Vec3 sec_color; if (gstate.getShadeMode() == GE_SHADE_GOURAUD && !clearMode) { - // TODO: Is that the correct way to interpolate? - prim_color = Interpolate(v0.color0, v1.color0, v2.color0, w0, w1, w2, wsum); - sec_color = Interpolate(v0.color1, v1.color1, v2.color1, w0, w1, w2, wsum); + // Does the PSP do perspective-correct color interpolation? The GC doesn't. + prim_color = Interpolate(v0.color0, v1.color0, v2.color0, w0, w1, w2, wsum_recip); + sec_color = Interpolate(v0.color1, v1.color1, v2.color1, w0, w1, w2, wsum_recip); } else { prim_color = v2.color0; sec_color = v2.color1; @@ -1247,8 +1263,8 @@ void DrawTriangleSlice( if (gstate.isTextureMapEnabled() && !clearMode) { if (gstate.isModeThrough()) { - // TODO: Is it really this simple? - Vec2 texcoords = Interpolate(v0.texturecoords, v1.texturecoords, v2.texturecoords, w0, w1, w2, wsum); + // Texture coordinate interpolation must definitely be perspective-correct. + Vec2 texcoords = Interpolate(v0.texturecoords, v1.texturecoords, v2.texturecoords, w0, w1, w2, wsum_recip); ApplyTexturing(prim_color, texcoords.s(), texcoords.t(), maxTexLevel, magFilt, texptr, texbufwidthbits); } else { float s = 0, t = 0; @@ -1269,15 +1285,18 @@ void DrawTriangleSlice( #endif } - // TODO: Fogging + int fog = 255; + if (gstate.isFogEnabled() && !clearMode) { + fog = ClampFogDepth(((float)v0.fogdepth * w0 + (float)v1.fogdepth * w1 + (float)v2.fogdepth * w2) * wsum_recip); + } u16 z = v2.screenpos.z; // TODO: Is that the correct way to interpolate? // Without the (u32), this causes an ICE in some versions of gcc. if (!flatZ) - z = (u16)(u32)(((float)v0.screenpos.z * w0 + (float)v1.screenpos.z * w1 + (float)v2.screenpos.z * w2) * wsum); + z = (u16)(u32)(((float)v0.screenpos.z * w0 + (float)v1.screenpos.z * w1 + (float)v2.screenpos.z * w2) * wsum_recip); - DrawSinglePixel(p, z, prim_color); + DrawSinglePixel(p, z, fog, prim_color); } } } @@ -1296,11 +1315,10 @@ void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData& if (d01.x * d02.y - d01.y * d02.x < 0) return; - // Is the division and multiplication just &= ~0xF ? - int minX = std::min(std::min(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) / 16 * 16; - int minY = std::min(std::min(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) / 16 * 16; - int maxX = std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) / 16 * 16; - int maxY = std::max(std::max(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) / 16 * 16; + int minX = std::min(std::min(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) & ~0xF; + int minY = std::min(std::min(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) & ~0xF; + int maxX = std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) & ~0xF; + int maxY = std::max(std::max(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) & ~0xF; DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0); DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0); @@ -1400,14 +1418,15 @@ void DrawPoint(const VertexData &v0) ScreenCoords pprime = pos; - // TODO: Fogging DrawingCoords p = TransformUnit::ScreenToDrawing(pprime); u16 z = pos.z; + u8 fog = ClampFogDepth(v0.fogdepth); + if (clearMode) { - DrawSinglePixel(p, z, prim_color); + DrawSinglePixel(p, z, v0.fogdepth, prim_color); } else { - DrawSinglePixel(p, z, prim_color); + DrawSinglePixel(p, z, v0.fogdepth, prim_color); } } @@ -1476,11 +1495,15 @@ void DrawLine(const VertexData &v0, const VertexData &v1) if (x < scissorTL.x || y < scissorTL.y || x >= scissorBR.x || y >= scissorBR.y) continue; + // Interpolate between the two points. Vec4 c0 = (v0.color0 * (steps - i) + v1.color0 * i) / steps1; Vec3 sec_color = (v0.color1 * (steps - i) + v1.color1 * i) / steps1; // TODO: UVGenMode? Vec2 tc = (v0.texturecoords * (float)(steps - i) + v1.texturecoords * (float)i) / steps1; Vec4 prim_color = c0; + + // TODO: Interpolate fog as well + float s = tc.s(); float t = tc.t(); @@ -1500,13 +1523,11 @@ void DrawLine(const VertexData &v0, const VertexData &v1) ScreenCoords pprime = ScreenCoords(x, y, z); - // TODO: Fogging DrawingCoords p = TransformUnit::ScreenToDrawing(pprime); - if (clearMode) { - DrawSinglePixel(p, z, prim_color); + DrawSinglePixel(p, z, v0.fogdepth, prim_color); } else { - DrawSinglePixel(p, z, prim_color); + DrawSinglePixel(p, z, v0.fogdepth, prim_color); } x = x + xinc; diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index e544599ee2..d9a13d4be7 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -172,7 +172,13 @@ VertexData TransformUnit::ReadVertex(VertexReader& vreader) if (!gstate.isModeThrough()) { vertex.modelpos = ModelCoords(pos[0], pos[1], pos[2]); vertex.worldpos = WorldCoords(TransformUnit::ModelToWorld(vertex.modelpos)); - vertex.clippos = ClipCoords(TransformUnit::ViewToClip(TransformUnit::WorldToView(vertex.worldpos))); + ModelCoords viewpos = TransformUnit::WorldToView(vertex.worldpos); + vertex.clippos = ClipCoords(TransformUnit::ViewToClip(viewpos)); + if (gstate.isFogEnabled()) { + vertex.fogdepth = (viewpos.z + getFloat24(gstate.fog1)) * getFloat24(gstate.fog2); + } else { + vertex.fogdepth = 1.0f; + } vertex.screenpos = ClipToScreenInternal(vertex.clippos, &outside_range_flag); if (vreader.hasNormal()) { @@ -187,6 +193,7 @@ VertexData TransformUnit::ReadVertex(VertexReader& vreader) vertex.screenpos.y = (u32)pos[1] * 16 + gstate.getOffsetY16(); vertex.screenpos.z = pos[2]; vertex.clippos.w = 1.f; + vertex.fogdepth = 1.f; } return vertex; diff --git a/GPU/Software/TransformUnit.h b/GPU/Software/TransformUnit.h index 9dc91cca9c..84ca3a2cdb 100644 --- a/GPU/Software/TransformUnit.h +++ b/GPU/Software/TransformUnit.h @@ -93,8 +93,9 @@ struct VertexData screenpos = ::Lerp(a.screenpos, b.screenpos, t); // TODO: Should use a LerpInt (?) texturecoords = ::Lerp(a.texturecoords, b.texturecoords, t); normal = ::Lerp(a.normal, b.normal, t); + fogdepth = ::Lerp(a.fogdepth, b.fogdepth, t); - u16 t_int =(u16)(t*256); + u16 t_int = (u16)(t*256); color0 = LerpInt,256>(a.color0, b.color0, t_int); color1 = LerpInt,256>(a.color1, b.color1, t_int); } @@ -108,6 +109,7 @@ struct VertexData WorldCoords worldnormal; Vec4 color0; Vec3 color1; + float fogdepth; }; class VertexReader; diff --git a/native b/native index 93a7d0c67e..316d6ab84d 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 93a7d0c67e239c816c408c1d8631727f2790eb4c +Subproject commit 316d6ab84def4666fe13ff0c14c9bcb1659ab5c1