summaryrefslogtreecommitdiff
path: root/tools/playground/ras_test.cpp
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
committerclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
commit847962910f0bff071f3bf07c9abb87764fb6cac3 (patch)
treeddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /tools/playground/ras_test.cpp
downloadlibrw-847962910f0bff071f3bf07c9abb87764fb6cac3.tar.gz
librw-847962910f0bff071f3bf07c9abb87764fb6cac3.zip
Import aap/librw @ masterHEADmaster
Snapshot for re3/reVC vendoring, per @lzcnt. Source: https://github.com/aap/librw (master).
Diffstat (limited to 'tools/playground/ras_test.cpp')
-rw-r--r--tools/playground/ras_test.cpp902
1 files changed, 902 insertions, 0 deletions
diff --git a/tools/playground/ras_test.cpp b/tools/playground/ras_test.cpp
new file mode 100644
index 0000000..185fdec
--- /dev/null
+++ b/tools/playground/ras_test.cpp
@@ -0,0 +1,902 @@
+#include <rw.h>
+#include <skeleton.h>
+
+namespace rs {
+
+typedef int8_t i8;
+typedef uint8_t u8;
+typedef int16_t i16;
+typedef uint16_t u16;
+typedef int32_t i32;
+typedef uint32_t u32;
+typedef int64_t i64;
+typedef uint64_t u64;
+
+typedef struct Canvas Canvas;
+struct Canvas
+{
+ u8 *fb;
+ u32 *zbuf;
+ int w, h;
+};
+extern Canvas *canvas;
+
+typedef struct Texture Texture;
+struct Texture
+{
+ u8 *pixels;
+ int w, h;
+ int wrap;
+};
+
+typedef struct Point3 Point3;
+struct Point3
+{
+ int x, y, z;
+};
+
+typedef struct Color Color;
+struct Color
+{
+ u8 r, g, b, a;
+};
+
+typedef struct Vertex Vertex;
+struct Vertex
+{
+ i32 x, y, z;
+ float q; // 1/z
+ u8 r, g, b, a;
+ u8 f; // fog
+ float s, t;
+};
+
+Canvas *makecanvas(int w, int h);
+Texture *maketexture(int w, int h);
+void putpixel(Canvas *canvas, Point3 p, Color c);
+void clearcanvas(Canvas *canvas);
+void drawTriangle(Canvas *canvas, Vertex p1, Vertex p2, Vertex p3);
+
+// not good
+void drawRect(Canvas *canvas, Point3 p1, Point3 p2, Color c);
+void drawLine(Canvas *canvas, Point3 p1, Point3 p2, Color c);
+
+//#define trace(...) printf(__VA_ARGS__)
+#define trace(...)
+
+
+int clamp(int x);
+
+
+/*
+ * Render States
+ */
+enum TextureWrap {
+ WRAP_REPEAT,
+ WRAP_CLAMP,
+ WRAP_BORDER,
+};
+
+enum TextureFunction {
+ TFUNC_MODULATE,
+ TFUNC_DECAL,
+ TFUNC_HIGHLIGHT,
+ TFUNC_HIGHLIGHT2,
+};
+
+enum AlphaTestFunc {
+ ALPHATEST_NEVER,
+ ALPHATEST_ALWAYS,
+ ALPHATEST_LESS,
+ ALPHATEST_LEQUAL,
+ ALPHATEST_EQUAL,
+ ALPHATEST_GEQUAL,
+ ALPHATEST_GREATER,
+ ALPHATEST_NOTEQUAL,
+};
+
+enum AlphaTestFail {
+ ALPHAFAIL_KEEP,
+ ALPHAFAIL_FB_ONLY,
+ ALPHAFAIL_ZB_ONLY,
+};
+
+enum DepthTestFunc {
+ DEPTHTEST_NEVER,
+ DEPTHTEST_ALWAYS,
+ DEPTHTEST_GEQUAL,
+ DEPTHTEST_GREATER,
+};
+
+// The blend equation is
+// out = ((A - B) * C >> 7) + D
+// A, B and D select the color, C the alpha value
+enum AlphaBlendOp {
+ ALPHABLEND_SRC,
+ ALPHABLEND_DST,
+ ALPHABLEND_ZERO,
+ ALPHABLEND_FIX = ALPHABLEND_ZERO,
+};
+
+extern int srScissorX0, srScissorX1;
+extern int srScissorY0, srScissorY1;
+extern int srDepthTestEnable;
+extern int srDepthTestFunction;
+extern int srWriteZ;
+extern int srAlphaTestEnable;
+extern int srAlphaTestFunction;
+extern int srAlphaTestReference;
+extern int srAlphaTestFail;
+extern int srAlphaBlendEnable;
+extern int srAlphaBlendA;
+extern int srAlphaBlendB;
+extern int srAlphaBlendC;
+extern int srAlphaBlendD;
+extern int srAlphaBlendFix;
+extern int srTexEnable;
+extern Texture *srTexture;
+extern int srWrapU;
+extern int srWrapV;
+extern Color srBorder;
+extern int srTexUseAlpha;
+extern int srTexFunc;
+extern int srFogEnable;
+extern Color srFogCol;
+
+
+
+// end header
+
+
+
+
+#define CEIL(p) (((p)+15) >> 4)
+
+// render states
+int srScissorX0, srScissorX1;
+int srScissorY0, srScissorY1;
+int srDepthTestEnable = 1;
+int srDepthTestFunction = DEPTHTEST_GEQUAL;
+int srWriteZ = 1;
+int srAlphaTestEnable = 1;
+int srAlphaTestFunction = ALPHATEST_ALWAYS;
+int srAlphaTestReference;
+int srAlphaTestFail = ALPHAFAIL_FB_ONLY;
+int srAlphaBlendEnable = 1;
+int srAlphaBlendA = ALPHABLEND_SRC;
+int srAlphaBlendB = ALPHABLEND_DST;
+int srAlphaBlendC = ALPHABLEND_SRC;
+int srAlphaBlendD = ALPHABLEND_DST;
+int srAlphaBlendFix = 0x80;
+int srTexEnable = 0;
+Texture *srTexture;
+int srWrapU = WRAP_REPEAT;
+int srWrapV = WRAP_REPEAT;
+Color srBorder = { 255, 0, 0, 255 };
+int srTexUseAlpha = 1;
+int srTexFunc = TFUNC_MODULATE;
+int srFogEnable = 0;
+Color srFogCol = { 0, 0, 0, 0 };
+
+int clamp(int x) { if(x < 0) return 0; if(x > 255) return 255; return x; }
+
+Canvas*
+makecanvas(int w, int h)
+{
+ Canvas *canv;
+ canv = (Canvas*)malloc(sizeof(*canv) + w*h*(4+4));
+ canv->w = w;
+ canv->h = h;
+ canv->fb = ((u8*)canv + sizeof(*canv));
+ canv->zbuf = (u32*)(canv->fb + w*h*4);
+ return canv;
+}
+
+Texture*
+maketexture(int w, int h)
+{
+ Texture *t;
+ t = (Texture*)malloc(sizeof(*t) + w*h*4);
+ t->w = w;
+ t->h = h;
+ t->pixels = (u8*)t + sizeof(*t);
+ t->wrap = 0x11; // wrap u and v
+ return t;
+}
+
+void
+clearcanvas(Canvas *canvas)
+{
+ memset(canvas->fb, 0, canvas->w*canvas->h*4);
+ memset(canvas->zbuf, 0, canvas->w*canvas->h*4);
+}
+
+void
+writefb(Canvas *canvas, int x, int y, Color c)
+{
+ u8 *px = &canvas->fb[(y*canvas->w + x)*4];
+ u32 *z = &canvas->zbuf[y*canvas->w + x];
+
+ px[3] = c.r;
+ px[2] = c.g;
+ px[1] = c.b;
+ px[0] = c.a;
+}
+
+void
+putpixel(Canvas *canvas, Point3 p, Color c)
+{
+ // scissor test
+ if(p.x < srScissorX0 || p.x > srScissorX1 ||
+ p.y < srScissorY0 || p.y > srScissorY1)
+ return;
+
+ u8 *px = &canvas->fb[(p.y*canvas->w + p.x)*4];
+ u32 *z = &canvas->zbuf[p.y*canvas->w + p.x];
+
+ int fbwrite = 1;
+ int zbwrite = srWriteZ;
+
+ // alpha test
+ if(srAlphaTestEnable){
+ int fail;
+ switch(srAlphaTestFunction){
+ case ALPHATEST_NEVER:
+ fail = 1;
+ break;
+ case ALPHATEST_ALWAYS:
+ fail = 0;
+ break;
+ case ALPHATEST_LESS:
+ fail = c.a >= srAlphaTestReference;
+ break;
+ case ALPHATEST_LEQUAL:
+ fail = c.a > srAlphaTestReference;
+ break;
+ case ALPHATEST_EQUAL:
+ fail = c.a != srAlphaTestReference;
+ break;
+ case ALPHATEST_GEQUAL:
+ fail = c.a < srAlphaTestReference;
+ break;
+ case ALPHATEST_GREATER:
+ fail = c.a <= srAlphaTestReference;
+ break;
+ case ALPHATEST_NOTEQUAL:
+ fail = c.a == srAlphaTestReference;
+ break;
+ }
+ if(fail){
+ switch(srAlphaTestFail){
+ case ALPHAFAIL_KEEP:
+ return;
+ case ALPHAFAIL_FB_ONLY:
+ zbwrite = 0;
+ break;
+ case ALPHAFAIL_ZB_ONLY:
+ fbwrite = 0;
+ }
+ }
+ }
+
+ // ztest
+ if(srDepthTestEnable){
+ switch(srDepthTestFunction){
+ case DEPTHTEST_NEVER:
+ return;
+ case DEPTHTEST_ALWAYS:
+ break;
+ case DEPTHTEST_GEQUAL:
+ if((u32)p.z < *z)
+ return;
+ break;
+ case DEPTHTEST_GREATER:
+ if((u32)p.z <= *z)
+ return;
+ break;
+ }
+ }
+
+ Color d = { px[3], px[2], px[1], px[0] };
+
+ // blend
+ if(srAlphaBlendEnable){
+ int ar, ag, ab;
+ int br, bg, bb;
+ int dr, dg, db;
+ int ca;
+ switch(srAlphaBlendA){
+ case ALPHABLEND_SRC:
+ ar = c.r;
+ ag = c.g;
+ ab = c.b;
+ break;
+ case ALPHABLEND_DST:
+ ar = d.r;
+ ag = d.g;
+ ab = d.b;
+ break;
+ case ALPHABLEND_ZERO:
+ ar = 0;
+ ag = 0;
+ ab = 0;
+ break;
+ default: assert(0);
+ }
+ switch(srAlphaBlendB){
+ case ALPHABLEND_SRC:
+ br = c.r;
+ bg = c.g;
+ bb = c.b;
+ break;
+ case ALPHABLEND_DST:
+ br = d.r;
+ bg = d.g;
+ bb = d.b;
+ break;
+ case ALPHABLEND_ZERO:
+ br = 0;
+ bg = 0;
+ bb = 0;
+ break;
+ default: assert(0);
+ }
+ switch(srAlphaBlendC){
+ case ALPHABLEND_SRC:
+ ca = c.a;
+ break;
+ case ALPHABLEND_DST:
+ ca = d.a;
+ break;
+ case ALPHABLEND_FIX:
+ ca = srAlphaBlendFix;
+ break;
+ default: assert(0);
+ }
+ switch(srAlphaBlendD){
+ case ALPHABLEND_SRC:
+ dr = c.r;
+ dg = c.g;
+ db = c.b;
+ break;
+ case ALPHABLEND_DST:
+ dr = d.r;
+ dg = d.g;
+ db = d.b;
+ break;
+ case ALPHABLEND_ZERO:
+ dr = 0;
+ dg = 0;
+ db = 0;
+ break;
+ default: assert(0);
+ }
+
+ int r, g, b;
+ r = ((ar - br) * ca >> 7) + dr;
+ g = ((ag - bg) * ca >> 7) + dg;
+ b = ((ab - bb) * ca >> 7) + db;
+
+ c.r = clamp(r);
+ c.g = clamp(g);
+ c.b = clamp(b);
+ }
+
+ if(fbwrite)
+ writefb(canvas, p.x, p.y, c);
+ if(zbwrite)
+ *z = p.z;
+}
+
+Color
+sampletex_nearest(int u, int v)
+{
+ Texture *tex = srTexture;
+
+ const int usize = tex->w;
+ const int vsize = tex->h;
+
+ int iu = u >> 4;
+ int iv = v >> 4;
+
+ switch(srWrapU){
+ case WRAP_REPEAT:
+ iu %= usize;
+ break;
+ case WRAP_CLAMP:
+ if(iu < 0) iu = 0;
+ if(iu >= usize) iu = usize-1;
+ break;
+ case WRAP_BORDER:
+ if(iu < 0 || iu >= usize)
+ return srBorder;
+ }
+
+ switch(srWrapV){
+ case WRAP_REPEAT:
+ iv %= vsize;
+ break;
+ case WRAP_CLAMP:
+ if(iv < 0) iv = 0;
+ if(iv >= vsize) iv = vsize-1;
+ break;
+ case WRAP_BORDER:
+ if(iv < 0 || iv >= vsize)
+ return srBorder;
+ }
+
+ u8 *cp = &tex->pixels[(iv*tex->w + iu)*4];
+ Color c = { cp[0], cp[1], cp[2], cp[3] };
+ return c;
+}
+
+// t is texture, f is fragment
+Color
+texfunc(Color t, Color f)
+{
+ int r, g, b, a;
+ switch(srTexFunc){
+ case TFUNC_MODULATE:
+ r = t.r * f.r >> 7;
+ g = t.g * f.g >> 7;
+ b = t.b * f.b >> 7;
+ a = srTexUseAlpha ?
+ t.a * f.a >> 7 :
+ f.a;
+ break;
+ case TFUNC_DECAL:
+ r = t.r;
+ g = t.g;
+ b = t.b;
+ a = srTexUseAlpha ? t.a : f.a;
+ break;
+ case TFUNC_HIGHLIGHT:
+ r = (t.r * f.r >> 7) + f.a;
+ g = (t.g * f.g >> 7) + f.a;
+ b = (t.b * f.b >> 7) + f.a;
+ a = srTexUseAlpha ?
+ t.a + f.a :
+ f.a;
+ break;
+ case TFUNC_HIGHLIGHT2:
+ r = (t.r * f.r >> 7) + f.a;
+ g = (t.g * f.g >> 7) + f.a;
+ b = (t.b * f.b >> 7) + f.a;
+ a = srTexUseAlpha ? t.a : f.a;
+ break;
+ }
+ Color v;
+ v.r = clamp(r);
+ v.g = clamp(g);
+ v.b = clamp(b);
+ v.a = clamp(a);
+ return v;
+}
+
+Point3 mkpnt(int x, int y, int z) { Point3 p = { x, y, z}; return p; }
+
+void
+drawRect(Canvas *canvas, Point3 p1, Point3 p2, Color c)
+{
+ int x, y;
+ for(y = p1.y; y <= p2.y; y++)
+ for(x = p1.x; x <= p2.x; x++)
+ putpixel(canvas, mkpnt(x, y, 0), c);
+}
+
+void
+drawLine(Canvas *canvas, Point3 p1, Point3 p2, Color c)
+{
+ int dx, dy;
+ int incx, incy;
+ int e;
+ int x, y;
+
+ dx = abs(p2.x-p1.x);
+ incx = p2.x > p1.x ? 1 : -1;
+ dy = abs(p2.y-p1.y);
+ incy = p2.y > p1.y ? 1 : -1;
+ e = 0;
+ if(dx == 0){
+ for(y = p1.y; y != p2.y; y += incy)
+ putpixel(canvas, mkpnt(p1.x, y, 0), c);
+ }else if(dx > dy){
+ y = p1.y;
+ for(x = p1.x; x != p2.x; x += incx){
+ putpixel(canvas, mkpnt(x, y, 0), c);
+ e += dy;
+ if(2*e >= dx){
+ e -= dx;
+ y += incy;
+ }
+ }
+ }else{
+ x = p1.x;
+ for(y = p1.y; y != p2.y; y += incy){
+ putpixel(canvas, mkpnt(x, y, 0), c);
+ e += dx;
+ if(2*e >= dy){
+ e -= dy;
+ x += incx;
+ }
+ }
+ }
+}
+
+/*
+ attibutes we want to interpolate:
+ R G B A
+ U V / S T Q
+ X Y Z F
+*/
+
+struct TriAttribs
+{
+ i64 z;
+ i32 r, g, b, a;
+ i32 f;
+ float s, t;
+ float q;
+};
+
+static void
+add1(struct TriAttribs *a, struct TriAttribs *b)
+{
+ a->z += b->z;
+ a->r += b->r;
+ a->g += b->g;
+ a->b += b->b;
+ a->a += b->a;
+ a->f += b->f;
+ a->s += b->s;
+ a->t += b->t;
+ a->q += b->q;
+}
+
+static void
+sub1(struct TriAttribs *a, struct TriAttribs *b)
+{
+ a->z -= b->z;
+ a->r -= b->r;
+ a->g -= b->g;
+ a->b -= b->b;
+ a->a -= b->a;
+ a->f -= b->f;
+ a->s -= b->s;
+ a->t -= b->t;
+ a->q -= b->q;
+}
+
+static void
+guard(struct TriAttribs *a)
+{
+ if(a->z < 0) a->z = 0;
+ else if(a->z > 0x3FFFFFFFC000LL) a->z = 0x3FFFFFFFC000LL;
+ if(a->r < 0) a->r = 0;
+ else if(a->r > 0xFF000) a->r = 0xFF000;
+ if(a->g < 0) a->g = 0;
+ else if(a->g > 0xFF000) a->g = 0xFF000;
+ if(a->b < 0) a->b = 0;
+ else if(a->b > 0xFF000) a->b = 0xFF000;
+ if(a->a < 0) a->a = 0;
+ else if(a->a > 0xFF000) a->a = 0xFF000;
+ if(a->f < 0) a->f = 0;
+ else if(a->f > 0xFF000) a->f = 0xFF000;
+}
+
+struct RasTri
+{
+ int x, y;
+ int ymid, yend;
+ int right;
+ int e[2], dx[3], dy[3];
+ struct TriAttribs gx, gy, v, s;
+};
+
+static int
+triangleSetup(struct RasTri *tri, Vertex v1, Vertex v2, Vertex v3)
+{
+ int dx1, dx2, dx3;
+ int dy1, dy2, dy3;
+
+ dy1 = v3.y - v1.y; // long edge
+ if(dy1 == 0) return 1;
+ dx1 = v3.x - v1.x;
+ dx2 = v2.x - v1.x; // first small edge
+ dy2 = v2.y - v1.y;
+ dx3 = v3.x - v2.x; // second small edge
+ dy3 = v3.y - v2.y;
+
+ // this is twice the triangle area
+ const int area = dx2*dy1 - dx1*dy2;
+ if(area == 0) return 1;
+ // figure out if 0 or 1 is the right edge
+ tri->right = area < 0;
+
+ /* The gradients are to step whole pixels,
+ * so they are pre-multiplied by 16. */
+
+ float denom = 16.0f/area;
+ // gradients x
+#define GX(p) ((v2.p - v1.p)*dy1 - (v3.p - v1.p)*dy2)
+ tri->gx.z = GX(z)*denom * 16384;
+ tri->gx.r = GX(r)*denom * 4096;
+ tri->gx.g = GX(g)*denom * 4096;
+ tri->gx.b = GX(b)*denom * 4096;
+ tri->gx.a = GX(a)*denom * 4096;
+ tri->gx.f = GX(f)*denom * 4096;
+ tri->gx.s = GX(s)*denom;
+ tri->gx.t = GX(t)*denom;
+ tri->gx.q = GX(q)*denom;
+
+ // gradients y
+ denom = -denom;
+#define GY(p) ((v2.p - v1.p)*dx1 - (v3.p - v1.p)*dx2)
+ tri->gy.z = GY(z)*denom * 16384;
+ tri->gy.r = GY(r)*denom * 4096;
+ tri->gy.g = GY(g)*denom * 4096;
+ tri->gy.b = GY(b)*denom * 4096;
+ tri->gy.a = GY(a)*denom * 4096;
+ tri->gy.f = GY(f)*denom * 4096;
+ tri->gy.s = GY(s)*denom;
+ tri->gy.t = GY(t)*denom;
+ tri->gy.q = GY(q)*denom;
+
+ tri->ymid = CEIL(v2.y);
+ tri->yend = CEIL(v3.y);
+
+ tri->y = CEIL(v1.y);
+ tri->x = CEIL(v1.x);
+
+ tri->dy[0] = dy2<<4; // upper edge
+ tri->dy[1] = dy1<<4; // lower edge
+ tri->dy[2] = dy3<<4; // long edge
+ tri->dx[0] = dx2<<4;
+ tri->dx[1] = dx1<<4;
+ tri->dx[2] = dx3<<4;
+
+ // prestep to land on pixel center
+
+ int stepx = v1.x - (tri->x<<4);
+ int stepy = v1.y - (tri->y<<4);
+ tri->e[0] = (-stepy*tri->dx[0] + stepx*tri->dy[0]) >> 4;
+ tri->e[1] = (-stepy*tri->dx[1] + stepx*tri->dy[1]) >> 4;
+
+ // attributes along interpolated edge
+ // why is this cast needed? (mingw)
+ tri->v.z = (i64)v1.z*16384 - (stepy*tri->gy.z + stepx*tri->gx.z)/16;
+ tri->v.r = v1.r*4096 - (stepy*tri->gy.r + stepx*tri->gx.r)/16;
+ tri->v.g = v1.g*4096 - (stepy*tri->gy.g + stepx*tri->gx.g)/16;
+ tri->v.b = v1.b*4096 - (stepy*tri->gy.b + stepx*tri->gx.b)/16;
+ tri->v.a = v1.a*4096 - (stepy*tri->gy.a + stepx*tri->gx.a)/16;
+ tri->v.f = v1.f*4096 - (stepy*tri->gy.f + stepx*tri->gx.f)/16;
+ tri->v.s = v1.s - (stepy*tri->gy.s + stepx*tri->gx.s)/16.0f;
+ tri->v.t = v1.t - (stepy*tri->gy.t + stepx*tri->gx.t)/16.0f;
+ tri->v.q = v1.q - (stepy*tri->gy.q + stepx*tri->gx.q)/16.0f;
+
+ return 0;
+}
+
+void
+drawTriangle(Canvas *canvas, Vertex v1, Vertex v2, Vertex v3)
+{
+ Color c;
+ struct RasTri tri;
+ int stepx, stepy;
+
+ // Sort such that we have from top to bottom v1,v2,v3
+ if(v2.y < v1.y){ Vertex tmp = v1; v1 = v2; v2 = tmp; }
+ if(v3.y < v1.y){ Vertex tmp = v1; v1 = v3; v3 = tmp; }
+ if(v3.y < v2.y){ Vertex tmp = v2; v2 = v3; v3 = tmp; }
+
+ if(triangleSetup(&tri, v1, v2, v3))
+ return;
+
+ // Current scanline start and end
+ int xn[2] = { tri.x, tri.x };
+ int a = !tri.right; // left edge
+ int b = tri.right; // right edge
+
+ // If upper triangle has no height, only do the lower part
+ if(tri.dy[0] == 0)
+ goto secondtri;
+ while(tri.y < tri.yend){
+ /* TODO: is this the righ way to step the edges? */
+
+ /* Step x and interpolated value down left edge */
+ while(tri.e[a] <= -tri.dy[a]){
+ xn[a]--;
+ tri.e[a] += tri.dy[a];
+ sub1(&tri.v, &tri.gx);
+ }
+ while(tri.e[a] > 0){
+ xn[a]++;
+ tri.e[a] -= tri.dy[a];
+ add1(&tri.v, &tri.gx);
+ }
+
+ /* Step x down right edge */
+ while(tri.e[b] <= -tri.dy[b]){
+ xn[b]--;
+ tri.e[b] += tri.dy[b];
+ }
+ while(tri.e[b] > 0){
+ xn[b]++;
+ tri.e[b] -= tri.dy[b];
+ }
+
+ // When we reach the mid vertex, change state and jump to start of loop again
+ // TODO: this is a bit ugly in here...can we fix it?
+ if(tri.y == tri.ymid){
+ secondtri:
+ tri.dx[0] = tri.dx[2];
+ tri.dy[0] = tri.dy[2];
+ // Either the while prevents this or we returned early because dy1 == 0
+ assert(tri.dy[0] != 0);
+ stepx = v2.x - (xn[0]<<4);
+ stepy = v2.y - (tri.y<<4);
+ tri.e[0] = (-stepy*tri.dx[0] + stepx*tri.dy[0]) >> 4;
+
+ tri.ymid = -1; // so we don't do this again
+ continue;
+ }
+
+ /* Rasterize one line */
+ tri.s = tri.v;
+ for(tri.x = xn[a]; tri.x < xn[b]; tri.x++){
+ guard(&tri.s);
+ c.r = tri.s.r >> 12;
+ c.g = tri.s.g >> 12;
+ c.b = tri.s.b >> 12;
+ c.a = tri.s.a >> 12;
+ if(srTexEnable && srTexture){
+ float w = 1.0f/tri.s.q;
+ float s = tri.s.s * w;
+ float t = tri.s.t * w;
+ int u = s * srTexture->w * 16;
+ int v = t * srTexture->h * 16;
+ Color texc = sampletex_nearest(u, v);
+ c = texfunc(texc, c);
+ }
+ if(srFogEnable){
+ const int f = tri.s.f >> 12;
+ c.r = (f*c.r >> 8) + ((255 - f)*srFogCol.r >> 8);
+ c.g = (f*c.g >> 8) + ((255 - f)*srFogCol.g >> 8);
+ c.b = (f*c.b >> 8) + ((255 - f)*srFogCol.b >> 8);
+ }
+ putpixel(canvas, mkpnt(tri.x, tri.y, tri.s.z>>14), c);
+ add1(&tri.s, &tri.gx);
+ }
+
+ /* Step in y */
+ tri.y++;
+ tri.e[a] += tri.dx[a];
+ tri.e[b] += tri.dx[b];
+ add1(&tri.v, &tri.gy);
+ }
+}
+
+Canvas *canvas;
+
+}
+
+using namespace rw;
+
+void
+rastest_renderTriangles(RWDEVICE::Im2DVertex *scrverts, int32 numVerts, uint16 *indices, int32 numTris)
+{
+ int i;
+ RGBA col;
+ rs::Vertex v[3];
+ RWDEVICE::Im2DVertex *iv;
+
+ rs::srDepthTestEnable = 1;
+ rs::srAlphaTestEnable = 0;
+ rs::srTexEnable = 0;
+ rs::srAlphaBlendEnable = 0;
+
+ while(numTris--){
+ for(i = 0; i < 3; i++){
+ iv = &scrverts[indices[i]];
+ v[i].x = iv->getScreenX() * 16.0f;
+ v[i].y = iv->getScreenY() * 16.0f;
+ v[i].z = 16777216*(1.0f-iv->getScreenZ());
+ v[i].q = iv->getRecipCameraZ();
+ col = iv->getColor();
+ v[i].r = col.red;
+ v[i].g = col.green;
+ v[i].b = col.blue;
+ v[i].a = col.alpha;
+ v[i].f = 0;
+ v[i].s = iv->u*iv->getRecipCameraZ();
+ v[i].t = iv->v*iv->getRecipCameraZ();
+ }
+ drawTriangle(rs::canvas, v[0], v[1], v[2]);
+
+ indices += 3;
+ }
+}
+
+extern rw::Raster *testras;
+
+void
+beginSoftras(void)
+{
+ Camera *cam = (Camera*)engine->currentCamera;
+
+ if(rs::canvas == nil ||
+ cam->frameBuffer->width != rs::canvas->w ||
+ cam->frameBuffer->height != rs::canvas->h){
+ rs::canvas = rs::makecanvas(cam->frameBuffer->width, cam->frameBuffer->height);
+ testras = rw::Raster::create(rs::canvas->w, rs::canvas->h, 32, rw::Raster::C8888);
+ }
+
+ clearcanvas(rs::canvas);
+ rs::srScissorX0 = 0;
+ rs::srScissorX1 = rs::canvas->w-1;
+ rs::srScissorY0 = 0;
+ rs::srScissorY1 = rs::canvas->h-1;
+}
+
+void
+endSoftras(void)
+{
+ int i;
+ uint8 *dst = testras->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ if(dst == nil)
+ return;
+ uint8 *src = rs::canvas->fb;
+ for(i = 0; i < rs::canvas->w*rs::canvas->h; i++){
+ dst[0] = src[1];
+ dst[1] = src[2];
+ dst[2] = src[3];
+ dst[3] = src[0];
+ dst += 4;
+ src += 4;
+ }
+ // abgr in canvas
+ // bgra in raster
+ testras->unlock(0);
+}
+
+
+/*
+typedef struct PixVert PixVert;
+struct PixVert
+{
+ float x, y, z, q;
+ int r, g, b, a;
+ float u, v;
+};
+#include "test.inc"
+
+void
+drawtest(void)
+{
+ int i, j;
+ rs::Vertex v[3];
+
+ rs::srDepthTestEnable = 1;
+ rs::srAlphaTestEnable = 0;
+ rs::srTexEnable = 0;
+ rs::srAlphaBlendEnable = 0;
+
+ for(i = 0; i < nelem(verts); i += 3){
+ for(j = 0; j < 3; j++){
+ v[j].x = verts[i+j].x * 16.0f;
+ v[j].y = verts[i+j].y * 16.0f;
+ v[j].z = 16777216*(1.0f - verts[i+j].z);
+ v[j].q = verts[i+j].q;
+ v[j].r = verts[i+j].r;
+ v[j].g = verts[i+j].g;
+ v[j].b = verts[i+j].b;
+ v[j].a = verts[i+j].a;
+ v[j].f = 0;
+ v[j].s = verts[i+j].u*v[j].q;
+ v[j].t = verts[i+j].v*v[j].q;
+ }
+ drawTriangle(rs::canvas, v[0], v[1], v[2]);
+ }
+//exit(0);
+}
+*/