#include #include #include #include #include #include #include #include #include struct Context { int width, height; int tex_w, tex_h; uint8_t *backbuffer; uint8_t *colorbuffer; float *depthbuffer; uint8_t *texture; float lightdir[3]; }; static uint8_t *vga = (uint8_t *)0xA0000; static const uint8_t bayer8x8[] = { 0, 32, 8, 40, 2, 34, 10, 42, 48, 16, 56, 24, 50, 18, 58, 26, 12, 44, 4, 36, 14, 46, 6, 38, 60, 28, 52, 20, 62, 30, 54, 22, 3, 35, 11, 43, 1, 33, 9, 41, 51, 19, 59, 27, 49, 17, 57, 25, 15, 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21 }; static void SetMode(unsigned char mode) { union REGS regs; regs.w.ax = mode; int386(0x10, ®s, ®s); } static void SetPalette(int i, unsigned char r, unsigned char g, unsigned char b) { outp(0x3C8, i); outp(0x3C9, r); outp(0x3C9, g); outp(0x3C9, b); } static void InitPalette(void) { int i, r, g, b; for (i = 0; i < 256; ++i) { r = (i >> 6) & 3; r = (r << 4) | (r << 2) | r; g = (i >> 3) & 7; g = (g << 3) | g; b = i & 7; b = (b << 3) | b; /* RRGGGBBB */ // SetPalette(i, (((i >> 6) & 3) << 4) | 15, (((i >> 3) & 7) << 3) | 7, ((i & 7) << 3) | 7); SetPalette(i, r, g, b); } } static void SetPixel(struct Context *ctx, int x, int y, float c[]) { int r, g, b; uint8_t color; uint8_t bayerval; r = c[0] * (4 * 64); g = c[1] * (8 * 64); b = c[2] * (8 * 64); bayerval = bayer8x8[(x & 7) + (y & 7) * 8]; r += bayerval; g += bayerval; b += bayerval; r >>= 6; g >>= 6; b >>= 6; if (r > 3) r = 3; if (g > 7) g = 7; if (b > 7) b = 7; color = (r << 6) | (g << 3) | b; ctx->colorbuffer[x + y * 320] = color; } static void ClearDepth(struct Context *ctx, float depth) { int i; int pixels = ctx->width * ctx->height; for (i = 0; i < pixels; ++i) { ctx->depthbuffer[i] = depth; } } static float DegToRad(float deg) { return deg * 0.0174532925f; } static float Min(float a, float b) { return a < b ? a : b; } static float Min3(float a, float b, float c) { return Min(Min(a, b), c); } static float Max(float a, float b) { return a > b ? a : b; } static float Max3(float a, float b, float c) { return Max(Max(a, b), c); } static int IsOnRight(float a[], float b[], float c[]) { return ((b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]) <= 0.0f); } static float Distance2(float a[], float b[]) { float x, y, d; x = b[0] - a[0]; y = b[1] - a[1]; return sqrt(x * x + y * y); } static void Normalize(float a[]) { float l = sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); a[0] = a[0] / l; a[1] = a[1] / l; a[2] = a[2] / l; } static void MapToRes(struct Context *ctx, float a[]) { a[0] = a[0] / a[3]; a[1] = a[1] / a[3]; a[2] = a[2] / a[3]; 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[]) { result[0] = a[1] * b[2] - a[2] * b[1]; result[1] = a[2] * b[0] - a[0] * b[2]; result[2] = a[0] * b[1] - a[1] * b[0]; } static float Dot(float a[], float b[]) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } static float Dot2(float a[], float b[]) { return a[0] * b[0] + a[1] * b[1]; } static void VecCopy3(float result[], float a[]) { result[0] = a[0]; result[1] = a[1]; result[2] = a[2]; } static void VecSubVec3(float result[], float a[], float b[]) { result[0] = a[0] - b[0]; result[1] = a[1] - b[1]; result[2] = a[2] - b[2]; }; static void MatMultMat(float result[], float a[], float b[]) { result[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12]; result[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13]; result[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14]; result[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15]; result[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12]; result[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13]; result[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14]; result[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15]; result[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12]; result[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13]; result[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14]; result[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15]; result[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12]; result[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13]; result[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14]; result[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15]; } static void MatMultVec(float result[], float a[], float v[]) { result[0] = a[0] * v[0] + a[1] * v[1] + a[2] * v[2] + a[3] * v[3]; result[1] = a[4] * v[0] + a[5] * v[1] + a[6] * v[2] + a[7] * v[3]; result[2] = a[8] * v[0] + a[9] * v[1] + a[10] * v[2] + a[11] * v[3]; result[3] = a[12] * v[0] + a[13] * v[1] + a[14] * v[2] + a[15] * v[3]; } static void MatMultVec3(float result[], float a[], float v[]) { result[0] = a[0] * v[0] + a[1] * v[1] + a[2] * v[2] + a[3]; result[1] = a[4] * v[0] + a[5] * v[1] + a[6] * v[2] + a[7]; result[2] = a[8] * v[0] + a[9] * v[1] + a[10] * v[2] + a[11]; result[3] = a[12] * v[0] + a[13] * v[1] + a[14] * v[2] + a[15]; } static void LookAt(float mat[], float campos[], float targetpos[], float upvec[]) { float forward[3]; float right[3]; float up[3]; VecSubVec3(forward, targetpos, campos); Normalize(forward); Cross(right, forward, upvec); Normalize(right); Cross(up, right, forward); mat[0] = right[0]; mat[1] = right[1]; mat[2] = right[2]; mat[3] = -Dot(right, campos); mat[4] = up[0]; mat[5] = up[1]; mat[6] = up[2]; mat[7] = -Dot(up, campos); mat[8] = -forward[0]; mat[9] = -forward[1]; mat[10] = -forward[2]; mat[11] = Dot(forward, campos); mat[12] = 0.0f; mat[13] = 0.0f; mat[14] = 0.0f; mat[15] = 1.0f; } static void Perspective(float mat[], float fov, float aspectratio, float nearp, float farp) { float tanhalffovy = tan(fov * 0.5f); mat[0] = 1.0f / (aspectratio * tanhalffovy); mat[1] = 0.0f; mat[2] = 0.0f; mat[3] = 0.0f; mat[4] = 0.0f; mat[5] = 1.0f / tanhalffovy; mat[6] = 0.0f; mat[7] = 0.0f; mat[8] = 0.0f; mat[9] = 0.0f; mat[10] = -(farp + nearp) / (farp - nearp); mat[11] = -(2 * farp * nearp) / (farp - nearp) ; mat[12] = 0.0f; mat[13] = 0.0f; mat[14] = -1.0f; mat[15] = 0.0f; } struct ScrVertex { float x, y; float t[2]; float n[3]; float depth; }; 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; 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) { float t = ((float)x - v0->x) / xdiff; depth = v0->depth + t * depthdiff; depthbuf = &ctx->depthbuffer[x + y * ctx->width]; if (*depthbuf < depth) continue; *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 (tx < 0) tx = 0; else if (tx >= ctx->width) tx = ctx->width - 1; if (ty < 0) ty = 0; else if (ty >= ctx->height) ty = ctx->height - 1; n[0] = v0->n[0] + t * ndiff[0]; n[1] = v0->n[1] + t * ndiff[1]; n[2] = v0->n[2] + t * ndiff[2]; Normalize(n); diffuse = Max(0.5, Dot(n, ctx->lightdir)); 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; SetPixel(ctx, x, y, c); } } 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]; v[1].t[0] = c1[0]; v[1].t[1] = c1[1]; v[2].t[0] = c2[0]; v[2].t[1] = c2[1]; v[0].n[0] = n0[0]; v[0].n[1] = n0[1]; v[0].n[2] = n0[2]; 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]; if (IsOnRight(p[0], p[2], p[1])) return; /* backface cull */ 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) { if (ctx->colorbuffer != vga) memcpy(vga, ctx->colorbuffer, 320 * 200); } static void *MyMalloc(size_t n) { void *mem = malloc(n); if (!mem) { fprintf(stderr, "cannot alloc %d bytes!\n", (int)n); exit(1); } return mem; } int main(int argc, char **argv) { struct Context ctx; float view[16]; float proj[16]; float mvp[16]; float campos[3]; float targetpos[] = { 0.0f, 0.0f, 0.0f }; float upvector[] = { 0.0f, 1.0f, 0.0f }; float aspectratio = 1.3f; float p0[3], p1[3], p2[3]; float c0[3], c1[3], c2[3]; float n0[3], n1[3], n2[3]; int datasize, i, vertices, tp; float *data; int temp, i0, i1, i2; float camangle; FILE *in = stdin; FILE *out = stdout; if (argc > 1) { in = fopen(argv[1], "r"); if (!in) { fprintf(stderr, "cannot open for reading: %s\n", argv[1]); exit(2); } } if (argc > 2) { out = fopen(argv[2], "w"); if (!out) { fprintf(stderr, "cannot open for writing: %s\n", argv[2]); exit(2); } } ctx.width = 320; ctx.height = 200; ctx.backbuffer = MyMalloc(320 * 240); ctx.depthbuffer = MyMalloc(sizeof(float) * ctx.width * ctx.height); ctx.colorbuffer = ctx.backbuffer; 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); fscanf(in, "%d\n%d\n", &datasize, &vertices); data = MyMalloc(sizeof(float) * datasize); for (i = 0; i < datasize; ++i) { fscanf(in, "%f\n", &data[i]); } fprintf(stderr, "model rdy\n"); fscanf(in, "%d\n%d\n", &ctx.tex_w, &ctx.tex_h); tp = ctx.tex_w * ctx.tex_h * 3; ctx.texture = MyMalloc(tp); for (i = 0; i < tp; ++i) { int v; fscanf(in, "%d\n", &v); ctx.texture[i] = v; } fprintf(stderr, "textura rdy\n"); camangle = 0.0f; SetMode(0x13); InitPalette(); for (;;) { const float camdis = 7.0f; ClearDepth(&ctx, 100.0); memset(ctx.colorbuffer, 82, 320*240); campos[0] = sin(camangle) * camdis; campos[1] = 0.8f * camdis; campos[2] = cos(camangle) * camdis; camangle = camangle + 3.14f * 0.005f; LookAt(view, campos, targetpos, upvector); MatMultMat(mvp, proj, view); for (i = 0; i < vertices; i += 3) { i0 = i * 8; i1 = (i + 1) * 8; i2 = (i + 2) * 8; p0[0] = data[i0]; p0[1] = data[i0 + 1]; p0[2] = data[i0 + 2]; p1[0] = data[i1]; p1[1] = data[i1 + 1]; p1[2] = data[i1 + 2]; p2[0] = data[i2]; p2[1] = data[i2 + 1]; p2[2] = data[i2 + 2]; c0[0] = data[i0 + 3]; c0[1] = data[i0 + 4]; c1[0] = data[i1 + 3]; c1[1] = data[i1 + 4]; c2[0] = data[i2 + 3]; c2[1] = data[i2 + 4]; n0[0] = data[i0 + 5]; n0[1] = data[i0 + 6]; n0[2] = data[i0 + 7]; n1[0] = data[i1 + 5]; n1[1] = data[i1 + 6]; n1[2] = data[i1 + 7]; n2[0] = data[i2 + 5]; n2[1] = data[i2 + 6]; n2[2] = data[i2 + 7]; DrawTriangle(&ctx, p0, p1, p2, c0, c1, c2, mvp, n0, n1, n2); } OutputVGA(&ctx); } return 0; }