aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStapleButter <thetotalworm@gmail.com>2017-05-21 20:14:03 +0200
committerStapleButter <thetotalworm@gmail.com>2017-05-21 20:14:03 +0200
commitcb1b03b2537c2860344cd4533a4261fd2adad933 (patch)
treea65108770f03dce82c52f757220b6799a2ce2807
parentedc959dcf51eda1e5c844896f1c1efe67cff5776 (diff)
make the 3D renderer work per-scanline
-rw-r--r--src/GPU3D.h7
-rw-r--r--src/GPU3D_Soft.cpp419
2 files changed, 415 insertions, 11 deletions
diff --git a/src/GPU3D.h b/src/GPU3D.h
index ae24658..739b2d8 100644
--- a/src/GPU3D.h
+++ b/src/GPU3D.h
@@ -22,6 +22,11 @@
namespace GPU3D
{
+namespace SoftRenderer
+{
+class Slope;
+}
+
typedef struct
{
s32 Position[4];
@@ -59,6 +64,8 @@ typedef struct
bool IsShadow;
bool ClearStencil;
+ // data below rather specific to the software renderer
+
u32 VTop, VBottom; // vertex indices
s32 YTop, YBottom; // Y coords
s32 XTop, XBottom; // associated X coords
diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp
index 67d797d..d16138b 100644
--- a/src/GPU3D_Soft.cpp
+++ b/src/GPU3D_Soft.cpp
@@ -296,13 +296,26 @@ private:
s32 y;
};
+typedef struct
+{
+ Polygon* PolyData;
+
+ Slope SlopeL, SlopeR;
+ s32 XL, XR;
+ u32 CurVL, CurVR;
+ u32 NextVL, NextVR;
+
+} RendererPolygon;
+
+RendererPolygon PolygonList[2048];
+
void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha)
{
u32 vramaddr = (texparam & 0xFFFF) << 3;
- u32 width = 8 << ((texparam >> 20) & 0x7);
- u32 height = 8 << ((texparam >> 23) & 0x7);
+ s32 width = 8 << ((texparam >> 20) & 0x7);
+ s32 height = 8 << ((texparam >> 23) & 0x7);
s >>= 4;
t >>= 4;
@@ -626,8 +639,386 @@ u32 RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t)
return r | (g << 8) | (b << 16) | (a << 24);
}
-void RenderPolygon(Polygon* polygon)
+void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y)
+{
+ Polygon* polygon = rp->PolyData;
+
+ while (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
+ {
+ rp->CurVL = rp->NextVL;
+
+ if (polygon->FacingView)
+ {
+ rp->NextVL = rp->CurVL + 1;
+ if (rp->NextVL >= polygon->NumVertices)
+ rp->NextVL = 0;
+ }
+ else
+ {
+ rp->NextVL = rp->CurVL - 1;
+ if ((s32)rp->NextVL < 0)
+ rp->NextVL = polygon->NumVertices - 1;
+ }
+ }
+
+ rp->XL = rp->SlopeL.Setup(polygon->Vertices[rp->CurVL]->FinalPosition[0], polygon->Vertices[rp->NextVL]->FinalPosition[0],
+ polygon->Vertices[rp->CurVL]->FinalPosition[1], polygon->Vertices[rp->NextVL]->FinalPosition[1],
+ polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL], 0);
+}
+
+void SetupPolygonRightEdge(RendererPolygon* rp, s32 y)
+{
+ Polygon* polygon = rp->PolyData;
+
+ while (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
+ {
+ rp->CurVR = rp->NextVR;
+
+ if (polygon->FacingView)
+ {
+ rp->NextVR = rp->CurVR - 1;
+ if ((s32)rp->NextVR < 0)
+ rp->NextVR = polygon->NumVertices - 1;
+ }
+ else
+ {
+ rp->NextVR = rp->CurVR + 1;
+ if (rp->NextVR >= polygon->NumVertices)
+ rp->NextVR = 0;
+ }
+ }
+
+ rp->XR = rp->SlopeR.Setup(polygon->Vertices[rp->CurVR]->FinalPosition[0], polygon->Vertices[rp->NextVR]->FinalPosition[0],
+ polygon->Vertices[rp->CurVR]->FinalPosition[1], polygon->Vertices[rp->NextVR]->FinalPosition[1],
+ polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR], 1);
+}
+
+void SetupPolygon(RendererPolygon* rp, Polygon* polygon)
+{
+ u32 nverts = polygon->NumVertices;
+
+ u32 vtop = polygon->VTop, vbot = polygon->VBottom;
+ s32 ytop = polygon->YTop, ybot = polygon->YBottom;
+
+ rp->PolyData = polygon;
+
+ rp->CurVL = vtop;
+ rp->CurVR = vtop;
+
+ if (polygon->FacingView)
+ {
+ rp->NextVL = rp->CurVL + 1;
+ if (rp->NextVL >= nverts) rp->NextVL = 0;
+ rp->NextVR = rp->CurVR - 1;
+ if ((s32)rp->NextVR < 0) rp->NextVR = nverts - 1;
+ }
+ else
+ {
+ rp->NextVL = rp->CurVL - 1;
+ if ((s32)rp->NextVL < 0) rp->NextVL = nverts - 1;
+ rp->NextVR = rp->CurVR + 1;
+ if (rp->NextVR >= nverts) rp->NextVR = 0;
+ }
+
+ if (ybot == ytop)
+ {
+ //ybot++;
+
+ vtop = 0; vbot = 0;
+ //xtop = 256; xbot = 0;
+ int i;
+
+ i = 1;
+ if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
+ if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
+
+ i = nverts - 1;
+ if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
+ if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
+
+ rp->CurVL = vtop; rp->NextVL = vtop;
+ rp->CurVR = vbot; rp->NextVR = vbot;
+
+ rp->XL = rp->SlopeL.SetupDummy(polygon->Vertices[rp->CurVL]->FinalPosition[0], 0);
+ rp->XR = rp->SlopeR.SetupDummy(polygon->Vertices[rp->CurVR]->FinalPosition[0], 1);
+ }
+ else
+ {
+ SetupPolygonLeftEdge(rp, ytop);
+ SetupPolygonRightEdge(rp, ytop);
+ }
+}
+
+void RenderPolygonScanline(RendererPolygon* rp, s32 y)
+{
+ // TODO: shit
+
+ Polygon* polygon = rp->PolyData;
+
+ u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
+ bool wireframe = (polyalpha == 0);
+
+ bool (*fnDepthTest)(s32 oldz, s32 z);
+ if (polygon->Attr & (1<<14))
+ fnDepthTest = DepthTest<true>;
+ else
+ fnDepthTest = DepthTest<false>;
+
+ if (polygon->YTop != polygon->YBottom)
+ {
+ if (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
+ {
+ SetupPolygonLeftEdge(rp, y);
+ }
+
+ if (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
+ {
+ SetupPolygonRightEdge(rp, y);
+ }
+ }
+
+ Vertex *vlcur, *vlnext, *vrcur, *vrnext;
+ s32 xstart, xend;
+ Slope* slope_start;
+ Slope* slope_end;
+
+ xstart = rp->XL;
+ xend = rp->XR;
+
+ s32 wl = rp->SlopeL.Interp.Interpolate(polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL]);
+ s32 wr = rp->SlopeR.Interp.Interpolate(polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR]);
+
+ s32 zl = rp->SlopeL.Interp.InterpolateZ(polygon->FinalZ[rp->CurVL], polygon->FinalZ[rp->NextVL], polygon->WBuffer);
+ s32 zr = rp->SlopeR.Interp.InterpolateZ(polygon->FinalZ[rp->CurVR], polygon->FinalZ[rp->NextVR], polygon->WBuffer);
+
+ // if the left and right edges are swapped, render backwards.
+ // note: we 'forget' to swap the xmajor flags, on purpose
+ // the hardware has the same bug
+ if (xstart > xend)
+ {
+ vlcur = polygon->Vertices[rp->CurVR];
+ vlnext = polygon->Vertices[rp->NextVR];
+ vrcur = polygon->Vertices[rp->CurVL];
+ vrnext = polygon->Vertices[rp->NextVL];
+
+ slope_start = &rp->SlopeR;
+ slope_end = &rp->SlopeL;
+
+ s32 tmp;
+ tmp = xstart; xstart = xend; xend = tmp;
+ tmp = wl; wl = wr; wr = tmp;
+ tmp = zl; zl = zr; zr = tmp;
+ }
+ else
+ {
+ vlcur = polygon->Vertices[rp->CurVL];
+ vlnext = polygon->Vertices[rp->NextVL];
+ vrcur = polygon->Vertices[rp->CurVR];
+ vrnext = polygon->Vertices[rp->NextVR];
+
+ slope_start = &rp->SlopeL;
+ slope_end = &rp->SlopeR;
+ }
+
+ // interpolate attributes along Y
+
+ s32 rl = slope_start->Interp.Interpolate(vlcur->FinalColor[0], vlnext->FinalColor[0]);
+ s32 gl = slope_start->Interp.Interpolate(vlcur->FinalColor[1], vlnext->FinalColor[1]);
+ s32 bl = slope_start->Interp.Interpolate(vlcur->FinalColor[2], vlnext->FinalColor[2]);
+
+ s32 sl = slope_start->Interp.Interpolate(vlcur->TexCoords[0], vlnext->TexCoords[0]);
+ s32 tl = slope_start->Interp.Interpolate(vlcur->TexCoords[1], vlnext->TexCoords[1]);
+
+ s32 rr = slope_end->Interp.Interpolate(vrcur->FinalColor[0], vrnext->FinalColor[0]);
+ s32 gr = slope_end->Interp.Interpolate(vrcur->FinalColor[1], vrnext->FinalColor[1]);
+ s32 br = slope_end->Interp.Interpolate(vrcur->FinalColor[2], vrnext->FinalColor[2]);
+
+ s32 sr = slope_end->Interp.Interpolate(vrcur->TexCoords[0], vrnext->TexCoords[0]);
+ s32 tr = slope_end->Interp.Interpolate(vrcur->TexCoords[1], vrnext->TexCoords[1]);
+
+ // calculate edges
+ //
+ // edge fill rules for opaque pixels:
+ // * right edge is filled if slope > 1
+ // * left edge is filled if slope <= 1
+ // * edges with slope = 0 are always filled
+ // edges are always filled if the pixels are translucent
+ // in wireframe mode, there are special rules for equal Z (TODO)
+
+ s32 l_edgeend, r_edgestart;
+ bool l_filledge, r_filledge;
+
+ if (rp->SlopeL.XMajor)
+ {
+ l_edgeend = slope_start->EdgeLimit(0);
+ if (l_edgeend == xstart) l_edgeend++;
+
+ l_filledge = slope_start->Negative;
+ }
+ else
+ {
+ l_edgeend = xstart + 1;
+
+ l_filledge = true;
+ }
+
+ if (rp->SlopeR.XMajor)
+ {
+ r_edgestart = slope_end->EdgeLimit(1);
+ if (r_edgestart == xend) r_edgestart--;
+
+ r_filledge = !slope_end->Negative;
+ }
+ else
+ {
+ r_edgestart = xend - 1;
+
+ r_filledge = slope_end->Increment==0;
+ }
+
+ int yedge = 0;
+ if (y == polygon->YTop) yedge = 0x4;
+ else if (y == polygon->YBottom-1) yedge = 0x8;
+
+ Interpolator interpX(xstart, xend+1, wl, wr, 8);
+
+ for (s32 x = xstart; x <= xend; x++)
+ {
+ if (x < 0) continue;
+ if (x > 255) break;
+
+ int edge = yedge;
+ if (x < l_edgeend) edge |= 0x1;
+ else if (x > r_edgestart) edge |= 0x2;
+
+ // wireframe polygons. really ugly, but works
+ if (wireframe && edge==0)
+ {
+ x = r_edgestart + 1;
+ continue;
+ }
+
+ u32 pixeladdr = (y*256) + x;
+ u32 attr = polygon->Attr & 0x3F008000;
+
+ // check stencil buffer for shadows
+ if (polygon->IsShadow)
+ {
+ if (StencilBuffer[pixeladdr] == 0)
+ continue;
+ }
+
+ interpX.SetX(x);
+
+ s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
+
+ if (polygon->IsShadowMask)
+ {
+ // for shadow masks: set stencil bits where the depth test fails.
+ // draw nothing.
+
+ // checkme
+ if (polyalpha == 31)
+ {
+ if (!wireframe)
+ {
+ if ((edge & 0x1) && !l_filledge)
+ continue;
+ if ((edge & 0x2) && !r_filledge)
+ continue;
+ }
+ }
+
+ if (!fnDepthTest(DepthBuffer[pixeladdr], z))
+ StencilBuffer[pixeladdr] = 1;
+
+ continue;
+ }
+
+ if (!fnDepthTest(DepthBuffer[pixeladdr], z))
+ continue;
+
+ u32 vr = interpX.Interpolate(rl, rr);
+ u32 vg = interpX.Interpolate(gl, gr);
+ u32 vb = interpX.Interpolate(bl, br);
+
+ s16 s = interpX.Interpolate(sl, sr);
+ s16 t = interpX.Interpolate(tl, tr);
+
+ u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t);
+ u8 alpha = color >> 24;
+
+ // alpha test
+ // TODO: check alpha test when blending is disabled
+ if (alpha <= RenderAlphaRef) continue;
+
+ if (alpha == 31)
+ {
+ // edge fill rules for opaque pixels
+ // TODO, eventually: antialiasing
+ if (!wireframe)
+ {
+ if ((edge & 0x1) && !l_filledge)
+ continue;
+ if ((edge & 0x2) && !r_filledge)
+ continue;
+ }
+
+ DepthBuffer[pixeladdr] = z;
+ }
+ else
+ {
+ u32 dstattr = AttrBuffer[pixeladdr];
+ attr |= (1<<30);
+ if (polygon->IsShadow) dstattr |= (1<<30);
+
+ // skip if polygon IDs are equal
+ // note: this only happens if the destination pixel was translucent
+ // or always when drawing a shadow
+ // (the GPU keeps track of which pixels are translucent, regardless of
+ // the destination alpha)
+ if ((dstattr & 0x7F000000) == (attr & 0x7F000000))
+ continue;
+
+ u32 dstcolor = ColorBuffer[pixeladdr];
+ u32 dstalpha = dstcolor >> 24;
+
+ if ((dstalpha > 0) && (RenderDispCnt & (1<<3)))
+ {
+ u32 srcR = color & 0x3F;
+ u32 srcG = (color >> 8) & 0x3F;
+ u32 srcB = (color >> 16) & 0x3F;
+
+ u32 dstR = dstcolor & 0x3F;
+ u32 dstG = (dstcolor >> 8) & 0x3F;
+ u32 dstB = (dstcolor >> 16) & 0x3F;
+
+ alpha++;
+ dstR = ((srcR * alpha) + (dstR * (32-alpha))) >> 5;
+ dstG = ((srcG * alpha) + (dstG * (32-alpha))) >> 5;
+ dstB = ((srcB * alpha) + (dstB * (32-alpha))) >> 5;
+
+ alpha--;
+ if (alpha > dstalpha) dstalpha = alpha;
+
+ color = dstR | (dstG << 8) | (dstB << 16) | (dstalpha << 24);
+ }
+
+ if (polygon->Attr & (1<<11))
+ DepthBuffer[pixeladdr] = z;
+ }
+
+ ColorBuffer[pixeladdr] = color;
+ AttrBuffer[pixeladdr] = attr;
+ }
+
+ rp->XL = rp->SlopeL.Step();
+ rp->XR = rp->SlopeR.Step();
+}
+
+void RenderPolygon(RendererPolygon* rp)
{
+ Polygon* polygon = rp->PolyData;
int nverts = polygon->NumVertices;
bool isline = false;
@@ -639,17 +1030,17 @@ void RenderPolygon(Polygon* polygon)
// draw, line per line
- u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
+ /*u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
bool wireframe = (polyalpha == 0);
bool (*fnDepthTest)(s32 oldz, s32 z);
if (polygon->Attr & (1<<14))
fnDepthTest = DepthTest<true>;
else
- fnDepthTest = DepthTest<false>;
+ fnDepthTest = DepthTest<false>;*/
- int lcur = vtop, rcur = vtop;
+ /*int lcur = vtop, rcur = vtop;
int lnext, rnext;
if (polygon->FacingView)
@@ -735,7 +1126,7 @@ void RenderPolygon(Polygon* polygon)
xR = slopeR.Setup(polygon->Vertices[rcur]->FinalPosition[0], polygon->Vertices[rnext]->FinalPosition[0],
polygon->Vertices[rcur]->FinalPosition[1], polygon->Vertices[rnext]->FinalPosition[1],
polygon->FinalW[rcur], polygon->FinalW[rnext], 1);
- }
+ }*/
if (ybot > 192) ybot = 192;
@@ -746,7 +1137,8 @@ void RenderPolygon(Polygon* polygon)
for (s32 y = ytop; y < ybot; y++)
{
- if (!isline)
+ RenderPolygonScanline(rp, y);
+ /*if (!isline)
{
if (y >= polygon->Vertices[lnext]->FinalPosition[1] && lcur != vbot)
{
@@ -1031,7 +1423,7 @@ void RenderPolygon(Polygon* polygon)
}
xL = slopeL.Step();
- xR = slopeR.Step();
+ xR = slopeR.Step();*/
}
}
@@ -1091,18 +1483,23 @@ void RenderFrame(Vertex* vertices, Polygon* polygons, int npolys)
}
}
+ for (int i = 0; i < npolys; i++)
+ {
+ SetupPolygon(&PolygonList[i], &polygons[i]);
+ }
+
// TODO: Y-sorting of translucent polygons
for (int i = 0; i < npolys; i++)
{
if (polygons[i].Translucent) continue;
- RenderPolygon(&polygons[i]);
+ RenderPolygon(&PolygonList[i]);
}
for (int i = 0; i < npolys; i++)
{
if (!polygons[i].Translucent) continue;
- RenderPolygon(&polygons[i]);
+ RenderPolygon(&PolygonList[i]);
}
}