diff --git a/render_dos.c b/render_dos.c index cddcb31..c1bc1e9 100644 --- a/render_dos.c +++ b/render_dos.c @@ -15,6 +15,8 @@ struct Context uint8_t *backbuffer; uint8_t *colorbuffer; float *depthbuffer; + uint8_t *texture; + float lightdir[3]; }; static uint8_t *vga = (uint8_t *)0xA0000; @@ -154,8 +156,8 @@ static void MapToRes(struct Context *ctx, float a[]) a[1] = a[1] / a[3]; a[2] = a[2] / a[3]; - a[0] = (a[0] + 1.0) * 0.5 * (ctx->width - 1); - a[1] = (1.0 - a[1]) * 0.5 * (ctx->height - 1); + a[0] = (a[0] + 1.0f) * 0.5f * (ctx->width - 1); + a[1] = (1.0f - a[1]) * 0.5f * (ctx->height - 1); } static void Cross(float result[], float a[], float b[]) @@ -279,137 +281,248 @@ static void Perspective(float mat[], float fov, float aspectratio, float nearp, mat[15] = 0.0f; } -static void DrawTriangle(struct Context *ctx, float wp0[], float wp1[], float wp2[], uint8_t texture[], float c0[], float c1[], float c2[], float mvp[], float lightdir[], float n0[], float n1[], float n2[]) +struct ScrVertex { - float p0[4], p1[4], p2[4]; + float x, y; + float t[2]; + float n[3]; + float depth; +}; - MatMultVec3(p0, mvp, wp0); - MatMultVec3(p1, mvp, wp1); - MatMultVec3(p2, mvp, wp2); +static void FillScanLine(struct Context *ctx, const struct ScrVertex *v0, const struct ScrVertex *v1) +{ + int x, y, start, end, t0x, t0y, t1x, t1y, tx, ty; + float c[3], n[3], depth, *depthbuf, xdiff, depthdiff, tdiff[2], ndiff[3], diffuse; + uint8_t *texel; + + + start = v0->x; + if (start < 0) + start = 0; + + end = v1->x; + if (end > ctx->width) + end = ctx->width; + + y = v0->y; - if (p0[3] != 0.0f && p1[3] != 0.0f && p2[3] != 0.0f) + xdiff = v1->x - v0->x; + depthdiff = v1->depth - v0->depth; + tdiff[0] = v1->t[0] - v0->t[0]; + tdiff[1] = v1->t[1] - v0->t[1]; + + ndiff[0] = v1->n[0] - v0->n[0]; + ndiff[1] = v1->n[1] - v0->n[1]; + ndiff[2] = v1->n[2] - v0->n[2]; + + + for (x = start; x < end; ++x) { - int x, y, idx, tx, ty, ti; - float p[2]; - float depth; - float w0, w1, w2, dot00, dot01, dot11, dot20, dot21, denom, d0, d1, d2; - float v0[2], v1[2], v2[2]; - float texcoord[2]; - float normal[3]; - float diff; - int aabbmin[2], aabbmax[2]; - float c[3]; + float t = ((float)x - v0->x) / xdiff; + depth = v0->depth + t * depthdiff; - MapToRes(ctx, p0); - MapToRes(ctx, p1); - MapToRes(ctx, p2); - - aabbmin[0] = Max(Min3(p0[0], p1[0], p2[0]), 0.0f); - aabbmin[1] = Max(Min3(p0[1], p1[1], p2[1]), 0.0f); - aabbmax[0] = Min(Max3(p0[0], p1[0], p2[0]), ctx->width - 1); - aabbmax[1] = Min(Max3(p0[1], p1[1], p2[1]), ctx->height - 1); + depthbuf = &ctx->depthbuffer[x + y * ctx->width]; + if (*depthbuf < depth) + continue; - for (y = aabbmin[1]; y <= aabbmax[1]; ++y) - { - for (x = aabbmin[0]; x <= aabbmax[0]; ++x) - { - p[0] = x; - p[1] = y; + *depthbuf = depth; + + tx = (v0->t[0] + t * tdiff[0]) * ctx->tex_w; + ty = (1.0f - (v0->t[1] + t * tdiff[1])) * ctx->tex_h; - if (!IsOnRight(p0, p1, p) || !IsOnRight(p1, p2, p) || !IsOnRight(p2, p0, p)) - continue; + if (tx < 0) + tx = 0; + else if (tx >= ctx->width) + tx = ctx->width - 1; - idx = x + ctx->width * y; - - v0[0] = p1[0] - p0[0]; - v0[1] = p1[1] - p0[1]; - v1[0] = p2[0] - p0[0]; - v1[1] = p2[1] - p0[1]; - v2[0] = p[0] - p0[0]; - v2[1] = p[1] - p0[1]; + if (ty < 0) + ty = 0; + else if (ty >= ctx->height) + ty = ctx->height - 1; - dot00 = Dot2(v0, v0); - dot01 = Dot2(v0, v1); - dot11 = Dot2(v1, v1); - dot20 = Dot2(v2, v0); - dot21 = Dot2(v2, v1); + n[0] = v0->n[0] + t * ndiff[0]; + n[1] = v0->n[1] + t * ndiff[1]; + n[2] = v0->n[2] + t * ndiff[2]; - denom = dot00 * dot11 - dot01 * dot01; - w1 = (dot11 * dot20 - dot01 * dot21) / denom; - w2 = (dot00 * dot21 - dot01 * dot20) / denom; - w0 = 1.0f - w1 - w2; + Normalize(n); + diffuse = Max(0.5, Dot(n, ctx->lightdir)); - depth = p0[2] * w0 + p1[2] * w1 + p2[2] * w2; + texel = &ctx->texture[(tx + ty * ctx->tex_w) * 3]; + c[0] = texel[0] / 255.0f * diffuse; + c[1] = texel[1] / 255.0f * diffuse; + c[2] = texel[2] / 255.0f * diffuse; - if (depth > 0.0 && ctx->depthbuffer[idx] > depth) - { - ctx->depthbuffer[idx] = depth; - idx = idx * 3; - - texcoord[0] = w0 * c0[0] + w1 * c1[0] + w2 * c2[0]; - texcoord[1] = w0 * c0[1] + w1 * c1[1] + w2 * c2[1]; - - normal[0] = w0 * n0[0] + w1 * n1[0] + w2 * n2[0]; - normal[1] = w0 * n0[1] + w1 * n1[1] + w2 * n2[1]; - normal[2] = w0 * n0[2] + w1 * n1[2] + w2 * n2[2]; - - Normalize(normal); - diff = Max(0.5, Dot(normal, lightdir)); - - tx = texcoord[0] * ctx->tex_w; - ty = (1 - texcoord[1]) * ctx->tex_h; - ti = (tx + ty * ctx->tex_w) * 3; - - c[0] = texture[ti] / 255.0f * diff; - c[1] = texture[ti + 1] / 255.0f * diff; - c[2] = texture[ti + 2] / 255.0f * diff; - - SetPixel(ctx, x, y, c); - } - } - } + SetPixel(ctx, x, y, c); } } -// static void OutputPPM(FILE *file, struct Context *ctx) -// { -// int i, numpixels; +static void SortByY(struct ScrVertex** s) +{ + struct ScrVertex* temp; + + if (s[0]->y > s[1]->y) + { + temp = s[0]; + s[0] = s[1]; + s[1] = temp; + } + + if (s[1]->y > s[2]->y) + { + temp = s[1]; + s[1] = s[2]; + s[2] = temp; + } + + if (s[0]->y > s[1]->y) + { + temp = s[0]; + s[0] = s[1]; + s[1] = temp; + } +} + +static void DebugDrawPoint(struct Context *ctx, int x, int y) +{ + float c[] = { 1.0f, 0.0f, 0.0f }; + + if (x < 0 || x >= ctx->width || y < 0 || y >= ctx->height) + return; + + SetPixel(ctx, x, y, c); +} + +static void LerpScrVertex(const struct ScrVertex *a, const struct ScrVertex *b, struct ScrVertex *res, float t) +{ + res->x = a->x + (b->x - a->x) * t; + res->y = a->y + (b->y - a->y) * t; + res->t[0] = a->t[0] + (b->t[0] - a->t[0]) * t; + res->t[1] = a->t[1] + (b->t[1] - a->t[1]) * t; + res->n[0] = a->n[0] + (b->n[0] - a->n[0]) * t; + res->n[1] = a->n[1] + (b->n[1] - a->n[1]) * t; + res->n[2] = a->n[2] + (b->n[2] - a->n[2]) * t; + res->depth = a->depth + (b->depth - a->depth) * t; +} + +static void DrawTriangle(struct Context *ctx, float wp0[], float wp1[], float wp2[], float c0[], float c1[], float c2[], float mvp[], float n0[], float n1[], float n2[]) +{ + float p[3][4]; + struct ScrVertex v[3], nv, *s[4], *tempv; + int i; + float nf; + + MatMultVec3(p[0], mvp, wp0); + MatMultVec3(p[1], mvp, wp1); + MatMultVec3(p[2], mvp, wp2); + + for (i = 0; i < 3; ++i) + { + if (p[i][3] == 0.0f) + return; + + MapToRes(ctx, p[i]); + + v[i].x = p[i][0]; + v[i].y = p[i][1]; + v[i].depth = p[i][2]; + } + + v[0].t[0] = c0[0]; + v[0].t[1] = c0[1]; -// fprintf(file, "P3\n%d\n%d\n255\n", ctx->width, ctx->height); - -// numpixels = ctx->width * ctx->height; + v[1].t[0] = c1[0]; + v[1].t[1] = c1[1]; -// for (i = 0; i < numpixels * 3; ++i) -// { -// fprintf(file, "%d ", (int)(ctx->colorbuffer[i] * 255.0f)); -// } + v[2].t[0] = c2[0]; + v[2].t[1] = c2[1]; -// fprintf(file, "\n"); -// } + v[0].n[0] = n0[0]; + v[0].n[1] = n0[1]; + v[0].n[2] = n0[2]; -// static void OutputVT100(FILE* out, const struct Context *ctx) -// { -// int r1, g1, b1, r2, b2, g2, x, y, idx, rowoffset; -// fprintf(out, "\x1b[1;1H\n"); + v[1].n[0] = n1[0]; + v[1].n[1] = n1[1]; + v[1].n[2] = n1[2]; + + v[2].n[0] = n2[0]; + v[2].n[1] = n2[1]; + v[2].n[2] = n2[2]; -// for (y = 0; y <= ctx->height - 2; y += 2) -// { -// fprintf(out, "\n"); -// for (x = 0; x < ctx->width; ++x) -// { -// idx = (x + y * ctx->width) * 3; -// rowoffset = ctx->width * 3; -// r1 = ctx->colorbuffer[idx] * 255; -// g1 = ctx->colorbuffer[idx + 1] * 255; -// b1 = ctx->colorbuffer[idx + 2] * 255; -// r2 = ctx->colorbuffer[idx + rowoffset] * 255; -// g2 = ctx->colorbuffer[idx + 1 + rowoffset] * 255; -// b2 = ctx->colorbuffer[idx + 2 + rowoffset] * 255; + if (IsOnRight(p[0], p[2], p[1])) + return; /* backface cull */ -// fprintf(out, "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dmâ–€", r1, g1, b1, r2, g2, b2); -// } -// } -// } + s[0] = &v[0]; + s[1] = &v[1]; + s[2] = &v[2]; + s[3] = &nv; + SortByY(s); + + nf = (s[1]->y - s[0]->y) / (s[2]->y - s[0]->y); + LerpScrVertex(s[0], s[2], &nv, nf); + nv.y = s[1]->y; + + if (s[3]->x < s[1]->x) + { + tempv = s[1]; + s[1] = s[3]; + s[3] = tempv; + } + + // for (i = 0; i < 4; ++i) + // { + // DebugDrawPoint(ctx, s[i]->x, s[i]->y); + // } + + if (s[0]->y != s[1]->y) + { + int start, end, sc_y; + + start = s[0]->y; + if (start < 0) + start = 0; + + end = s[1]->y + 1; + if (end > ctx->height) + end = ctx->height; + + for (sc_y = start; sc_y < end; sc_y++) + { + struct ScrVertex scv[2]; + float t = ((float)sc_y - s[0]->y) / (s[1]->y - s[0]->y); + + LerpScrVertex(s[0], s[1], &scv[0], t); + LerpScrVertex(s[0], s[3], &scv[1], t); + + scv[0].y = sc_y; + FillScanLine(ctx, &scv[0], &scv[1]); + } + } + + if (s[1]->y != s[2]->y) + { + int start, end, sc_y; + + start = s[3]->y + 1; + if (start < 0) + start = 0; + + end = s[2]->y + 1; + if (end > ctx->height) + end = ctx->height; + + for (sc_y = start; sc_y < end; sc_y++) + { + struct ScrVertex scv[2]; + float t = ((float)sc_y - s[3]->y) / (s[2]->y - s[3]->y); + + LerpScrVertex(s[1], s[2], &scv[0], t); + LerpScrVertex(s[3], s[2], &scv[1], t); + + scv[0].y = sc_y; + FillScanLine(ctx, &scv[0], &scv[1]); + } + } +} static void OutputVGA(const struct Context *ctx) { @@ -417,16 +530,6 @@ static void OutputVGA(const struct Context *ctx) memcpy(vga, ctx->colorbuffer, 320 * 200); } -// Function DumpMat(Real Array mat) -// Declare Integer i -// For i = 0 to 15 -// Output mat[i] & " " -// If i % 4 == 3 -// Output "" -// End -// End -// End - static void *MyMalloc(size_t n) { void *mem = malloc(n); @@ -443,9 +546,6 @@ int main(int argc, char **argv) { struct Context ctx; - // float bg[] = { 0.25f, 0.25f, 0.25f }; - float lightdir[] = { 0.3f, 1.0f, 0.4f }; - float view[16]; float proj[16]; float mvp[16]; @@ -454,7 +554,7 @@ int main(int argc, char **argv) float targetpos[] = { 0.0f, 0.0f, 0.0f }; float upvector[] = { 0.0f, 1.0f, 0.0f }; - float aspectratio = 1.46; + float aspectratio = 1.3f; float p0[3], p1[3], p2[3]; float c0[3], c1[3], c2[3]; @@ -462,7 +562,6 @@ int main(int argc, char **argv) int datasize, i, vertices, tp; float *data; - uint8_t *texture; int temp, i0, i1, i2; float camangle; @@ -470,11 +569,6 @@ int main(int argc, char **argv) FILE *in = stdin; FILE *out = stdout; - // getch(); - - // SetMode(0x3); - // return 0; - if (argc > 1) { in = fopen(argv[1], "r"); @@ -503,7 +597,10 @@ int main(int argc, char **argv) ctx.colorbuffer = ctx.backbuffer; - Normalize(lightdir); + ctx.lightdir[0] = 0.3f; + ctx.lightdir[1] = 1.0f; + ctx.lightdir[2] = 0.4f; + Normalize(ctx.lightdir); Perspective(proj, DegToRad(90.0f) / aspectratio, aspectratio, 0.1f, 50.0f); @@ -520,13 +617,13 @@ int main(int argc, char **argv) fscanf(in, "%d\n%d\n", &ctx.tex_w, &ctx.tex_h); tp = ctx.tex_w * ctx.tex_h * 3; - texture = MyMalloc(tp); + ctx.texture = MyMalloc(tp); for (i = 0; i < tp; ++i) { int v; fscanf(in, "%d\n", &v); - texture[i] = v; + ctx.texture[i] = v; } fprintf(stderr, "textura rdy\n"); @@ -592,16 +689,12 @@ int main(int argc, char **argv) n2[1] = data[i2 + 6]; n2[2] = data[i2 + 7]; - DrawTriangle(&ctx, p0, p1, p2, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2); + DrawTriangle(&ctx, p0, p1, p2, c0, c1, c2, mvp, n0, n1, n2); } - - // OutputPPM(out, &ctx); - // OutputVT100(out, &ctx); OutputVGA(&ctx); } - return 0; }