render/render_dos.c
2026-03-19 12:09:03 +01:00

608 lines
15 KiB
C

#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <dos.h>
#include <i86.h>
#include <conio.h>
#include <string.h>
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, &regs, &regs);
}
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;
}