#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; }; 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.0) * 0.5 * (ctx->width - 1); a[1] = (1.0 - a[1]) * 0.5 * (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; } 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[]) { float p0[4], p1[4], p2[4]; MatMultVec3(p0, mvp, wp0); MatMultVec3(p1, mvp, wp1); MatMultVec3(p2, mvp, wp2); if (p0[3] != 0.0f && p1[3] != 0.0f && p2[3] != 0.0f) { 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]; 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); for (y = aabbmin[1]; y <= aabbmax[1]; ++y) { for (x = aabbmin[0]; x <= aabbmax[0]; ++x) { p[0] = x; p[1] = y; if (!IsOnRight(p0, p1, p) || !IsOnRight(p1, p2, p) || !IsOnRight(p2, p0, p)) continue; 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]; dot00 = Dot2(v0, v0); dot01 = Dot2(v0, v1); dot11 = Dot2(v1, v1); dot20 = Dot2(v2, v0); dot21 = Dot2(v2, v1); denom = dot00 * dot11 - dot01 * dot01; w1 = (dot11 * dot20 - dot01 * dot21) / denom; w2 = (dot00 * dot21 - dot01 * dot20) / denom; w0 = 1.0f - w1 - w2; depth = p0[2] * w0 + p1[2] * w1 + p2[2] * w2; 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); } } } } } // static void OutputPPM(FILE *file, struct Context *ctx) // { // int i, numpixels; // fprintf(file, "P3\n%d\n%d\n255\n", ctx->width, ctx->height); // numpixels = ctx->width * ctx->height; // for (i = 0; i < numpixels * 3; ++i) // { // fprintf(file, "%d ", (int)(ctx->colorbuffer[i] * 255.0f)); // } // fprintf(file, "\n"); // } // 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"); // 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; // fprintf(out, "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dmâ–€", r1, g1, b1, r2, g2, b2); // } // } // } static void OutputVGA(const struct Context *ctx) { if (ctx->colorbuffer != vga) 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); 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 bg[] = { 0.25f, 0.25f, 0.25f }; float lightdir[] = { 0.3f, 1.0f, 0.4f }; 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.46; 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; uint8_t *texture; int temp, i0, i1, i2; float camangle; FILE *in = stdin; FILE *out = stdout; // getch(); // SetMode(0x3); // return 0; 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; Normalize(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; texture = MyMalloc(tp); for (i = 0; i < tp; ++i) { int v; fscanf(in, "%d\n", &v); 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, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2); } // OutputPPM(out, &ctx); // OutputVT100(out, &ctx); OutputVGA(&ctx); } return 0; }