From 10843571c724084c68d33d0438167d400cc8de2e Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Tue, 3 Sep 2019 06:19:29 +0300 Subject: [PATCH] moved over the polyfiller code and implemented blending --- .gdbinit | 2 +- .gitignore | 2 +- Makefile | 2 +- src/census/3dgfx.c | 706 +++++++++++++++++++++++++++++++++++++++++++++++++ src/census/3dgfx.h | 121 +++++++++ src/census/census.c | 43 +++ src/census/census.h | 12 + src/census/gfxutil.c | 217 +++++++++++++++ src/census/gfxutil.h | 33 +++ src/census/polyclip.c | 331 +++++++++++++++++++++++ src/census/polyclip.h | 38 +++ src/census/polyfill.c | 179 +++++++++++++ src/census/polyfill.h | 61 +++++ src/census/polytmpl.h | 345 ++++++++++++++++++++++++ src/census/util.c | 3 + src/census/util.h | 102 +++++++ src/kmain.c | 15 +- 17 files changed, 2206 insertions(+), 6 deletions(-) create mode 100644 src/census/3dgfx.c create mode 100644 src/census/3dgfx.h create mode 100644 src/census/census.c create mode 100644 src/census/census.h create mode 100644 src/census/gfxutil.c create mode 100644 src/census/gfxutil.h create mode 100644 src/census/polyclip.c create mode 100644 src/census/polyclip.h create mode 100644 src/census/polyfill.c create mode 100644 src/census/polyfill.h create mode 100644 src/census/polytmpl.h create mode 100644 src/census/util.c create mode 100644 src/census/util.h diff --git a/.gdbinit b/.gdbinit index 06d68b4..5a75505 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,2 +1,2 @@ target remote localhost:1234 -symbol-file test.sym +symbol-file bcensus.sym diff --git a/.gitignore b/.gitignore index 5ad65e9..130c484 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *.swp *.d *.bin -*.elf +bcensus *.map *.sym *.disasm diff --git a/Makefile b/Makefile index 7f4b7e9..2a0ad5c 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ ssrc = $(wildcard src/*.s) $(wildcard src/libc/*.s) $(wildcard src/boot/*.s) $(w Ssrc = $(wildcard src/*.S) obj = $(csrc:.c=.o) $(ssrc:.s=.o) $(Ssrc:.S=.o) dep = $(obj:.o=.d) -elf = bcensus.elf +elf = bcensus bin = bcensus.bin warn = -pedantic -Wall diff --git a/src/census/3dgfx.c b/src/census/3dgfx.c new file mode 100644 index 0000000..9058d28 --- /dev/null +++ b/src/census/3dgfx.c @@ -0,0 +1,706 @@ +#include +#include +#include +#include +#include +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__DJGPP__) +#include +#else +#include +#endif +#include "3dgfx.h" +#include "gfxutil.h" +#include "polyfill.h" +#include "polyclip.h" +#include "inttypes.h" +#include "census.h" +#include "util.h" + +#define STACK_SIZE 8 +typedef float g3d_matrix[16]; + +#define MAX_LIGHTS 4 + +#define IMM_VBUF_SIZE 256 + +struct light { + float x, y, z; + float r, g, b; +}; + +struct material { + float kd[3]; + float ks[3]; + float shin; +}; + +struct g3d_state { + unsigned int opt; + int frontface; + int polymode; + + g3d_matrix mat[G3D_NUM_MATRICES][STACK_SIZE]; + int mtop[G3D_NUM_MATRICES]; + int mmode; + + g3d_matrix norm_mat; + + float ambient[3]; + struct light lt[MAX_LIGHTS]; + struct material mtl; + + int width, height; + uint32_t *pixels; + + int vport[4]; + + /* immediate mode */ + int imm_prim; + int imm_numv, imm_pcount; + struct g3d_vertex imm_curv; + struct g3d_vertex imm_vbuf[IMM_VBUF_SIZE]; +}; + +static void imm_flush(void); +static void xform4_vec3(const float *mat, float *vec); +static void xform3_vec3(const float *mat, float *vec); +static void shade(struct g3d_vertex *v); + +static struct g3d_state *st; +static const float idmat[] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +}; + +int g3d_init(void) +{ + int i; + + if(!(st = calloc(1, sizeof *st))) { + fprintf(stderr, "failed to allocate G3D context\n"); + return -1; + } + st->opt = G3D_CLIP_FRUSTUM; + st->polymode = POLYFILL_FLAT; + + for(i=0; iwidth = width; + st->height = height; + st->pixels = pixels; + + pfill_fb.pixels = pixels; + pfill_fb.width = width; + pfill_fb.height = height; + + g3d_viewport(0, 0, width, height); +} + +/* set the framebuffer pointer, without resetting the size */ +void g3d_framebuffer_addr(void *pixels) +{ + st->pixels = pixels; + pfill_fb.pixels = pixels; +} + +void g3d_viewport(int x, int y, int w, int h) +{ + st->vport[0] = x; + st->vport[1] = y; + st->vport[2] = w; + st->vport[3] = h; +} + +void g3d_enable(unsigned int opt) +{ + st->opt |= opt; +} + +void g3d_disable(unsigned int opt) +{ + st->opt &= ~opt; +} + +void g3d_setopt(unsigned int opt, unsigned int mask) +{ + st->opt = (st->opt & ~mask) | (opt & mask); +} + +unsigned int g3d_getopt(unsigned int mask) +{ + return st->opt & mask; +} + +void g3d_front_face(unsigned int order) +{ + st->frontface = order; +} + +void g3d_polygon_mode(int pmode) +{ + st->polymode = pmode; +} + +void g3d_matrix_mode(int mmode) +{ + st->mmode = mmode; +} + +void g3d_load_identity(void) +{ + int top = st->mtop[st->mmode]; + memcpy(st->mat[st->mmode][top], idmat, 16 * sizeof(float)); +} + +void g3d_load_matrix(const float *m) +{ + int top = st->mtop[st->mmode]; + memcpy(st->mat[st->mmode][top], m, 16 * sizeof(float)); +} + +#define M(i,j) (((i) << 2) + (j)) +void g3d_mult_matrix(const float *m2) +{ + int i, j, top = st->mtop[st->mmode]; + float m1[16]; + float *dest = st->mat[st->mmode][top]; + + memcpy(m1, dest, sizeof m1); + + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + *dest++ = m1[M(0,j)] * m2[M(i,0)] + + m1[M(1,j)] * m2[M(i,1)] + + m1[M(2,j)] * m2[M(i,2)] + + m1[M(3,j)] * m2[M(i,3)]; + } + } +} + +void g3d_push_matrix(void) +{ + int top = st->mtop[st->mmode]; + if(top >= G3D_NUM_MATRICES) { + fprintf(stderr, "g3d_push_matrix overflow\n"); + return; + } + memcpy(st->mat[st->mmode][top + 1], st->mat[st->mmode][top], 16 * sizeof(float)); + st->mtop[st->mmode] = top + 1; +} + +void g3d_pop_matrix(void) +{ + if(st->mtop[st->mmode] <= 0) { + fprintf(stderr, "g3d_pop_matrix underflow\n"); + return; + } + --st->mtop[st->mmode]; +} + +void g3d_translate(float x, float y, float z) +{ + float m[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; + m[12] = x; + m[13] = y; + m[14] = z; + g3d_mult_matrix(m); +} + +void g3d_rotate(float deg, float x, float y, float z) +{ + float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + float angle = M_PI * deg / 180.0f; + float sina = sin(angle); + float cosa = cos(angle); + float one_minus_cosa = 1.0f - cosa; + float nxsq = x * x; + float nysq = y * y; + float nzsq = z * z; + + m[0] = nxsq + (1.0f - nxsq) * cosa; + m[4] = x * y * one_minus_cosa - z * sina; + m[8] = x * z * one_minus_cosa + y * sina; + m[1] = x * y * one_minus_cosa + z * sina; + m[5] = nysq + (1.0 - nysq) * cosa; + m[9] = y * z * one_minus_cosa - x * sina; + m[2] = x * z * one_minus_cosa - y * sina; + m[6] = y * z * one_minus_cosa + x * sina; + m[10] = nzsq + (1.0 - nzsq) * cosa; + m[15] = 1.0f; + + g3d_mult_matrix(m); +} + +void g3d_scale(float x, float y, float z) +{ + float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + m[0] = x; + m[5] = y; + m[10] = z; + m[15] = 1.0f; + g3d_mult_matrix(m); +} + +void g3d_ortho(float left, float right, float bottom, float top, float znear, float zfar) +{ + float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + float dx = right - left; + float dy = top - bottom; + float dz = zfar - znear; + + m[0] = 2.0 / dx; + m[5] = 2.0 / dy; + m[10] = -2.0 / dz; + m[12] = -(right + left) / dx; + m[13] = -(top + bottom) / dy; + m[14] = -(zfar + znear) / dz; + + g3d_mult_matrix(m); +} + +void g3d_frustum(float left, float right, float bottom, float top, float nr, float fr) +{ + float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + float dx = right - left; + float dy = top - bottom; + float dz = fr - nr; + + float a = (right + left) / dx; + float b = (top + bottom) / dy; + float c = -(fr + nr) / dz; + float d = -2.0 * fr * nr / dz; + + m[0] = 2.0 * nr / dx; + m[5] = 2.0 * nr / dy; + m[8] = a; + m[9] = b; + m[10] = c; + m[11] = -1.0f; + m[14] = d; + + g3d_mult_matrix(m); +} + +void g3d_perspective(float vfov_deg, float aspect, float znear, float zfar) +{ + float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + float vfov = M_PI * vfov_deg / 180.0f; + float s = 1.0f / tan(vfov * 0.5f); + float range = znear - zfar; + + m[0] = s / aspect; + m[5] = s; + m[10] = (znear + zfar) / range; + m[11] = -1.0f; + m[14] = 2.0f * znear * zfar / range; + + g3d_mult_matrix(m); +} + +const float *g3d_get_matrix(int which, float *m) +{ + int top = st->mtop[which]; + + if(m) { + memcpy(m, st->mat[which][top], 16 * sizeof(float)); + } + return st->mat[which][top]; +} + +void g3d_light_pos(int idx, float x, float y, float z) +{ + int mvtop = st->mtop[G3D_MODELVIEW]; + + st->lt[idx].x = x; + st->lt[idx].y = y; + st->lt[idx].z = z; + + xform4_vec3(st->mat[G3D_MODELVIEW][mvtop], &st->lt[idx].x); +} + +void g3d_light_color(int idx, float r, float g, float b) +{ + st->lt[idx].r = r; + st->lt[idx].g = g; + st->lt[idx].b = b; +} + +void g3d_light_ambient(float r, float g, float b) +{ + st->ambient[0] = r; + st->ambient[1] = g; + st->ambient[2] = b; +} + +void g3d_mtl_diffuse(float r, float g, float b) +{ + st->mtl.kd[0] = r; + st->mtl.kd[1] = g; + st->mtl.kd[2] = b; +} + +void g3d_mtl_specular(float r, float g, float b) +{ + st->mtl.ks[0] = r; + st->mtl.ks[1] = g; + st->mtl.ks[2] = b; +} + +void g3d_mtl_shininess(float shin) +{ + st->mtl.shin = shin; +} + +static INLINE int calc_shift(unsigned int x) +{ + int res = -1; + while(x) { + x >>= 1; + ++res; + } + return res; +} + +static INLINE int calc_mask(unsigned int x) +{ + return x - 1; +} + +void g3d_set_texture(int xsz, int ysz, void *pixels) +{ + pfill_tex.pixels = pixels; + pfill_tex.width = xsz; + pfill_tex.height = ysz; + + pfill_tex.xshift = calc_shift(xsz); + pfill_tex.yshift = calc_shift(ysz); + pfill_tex.xmask = calc_mask(xsz); + pfill_tex.ymask = calc_mask(ysz); +} + +void g3d_draw(int prim, const struct g3d_vertex *varr, int varr_size) +{ + g3d_draw_indexed(prim, varr, varr_size, 0, 0); +} + +void g3d_draw_indexed(int prim, const struct g3d_vertex *varr, int varr_size, + const uint16_t *iarr, int iarr_size) +{ + int i, j, vnum, nfaces, fill_mode; + struct pvertex pv[16]; + struct g3d_vertex v[16]; + int mvtop = st->mtop[G3D_MODELVIEW]; + int ptop = st->mtop[G3D_PROJECTION]; + struct g3d_vertex *tmpv; + + tmpv = alloca(prim * 6 * sizeof *tmpv); + + /* calc the normal matrix */ + memcpy(st->norm_mat, st->mat[G3D_MODELVIEW][mvtop], 16 * sizeof(float)); + st->norm_mat[12] = st->norm_mat[13] = st->norm_mat[14] = 0.0f; + + nfaces = (iarr ? iarr_size : varr_size) / prim; + + for(j=0; jmat[G3D_MODELVIEW][mvtop], &v[i].x); + xform3_vec3(st->norm_mat, &v[i].nx); + + if(st->opt & G3D_LIGHTING) { + shade(v + i); + } + if(st->opt & G3D_TEXTURE_GEN) { + v[i].u = v[i].nx * 0.5 + 0.5; + v[i].v = v[i].ny * 0.5 + 0.5; + } + if(st->opt & G3D_TEXTURE_MAT) { + float *mat = st->mat[G3D_TEXTURE][st->mtop[G3D_TEXTURE]]; + float x = mat[0] * v[i].u + mat[4] * v[i].v + mat[12]; + float y = mat[1] * v[i].u + mat[5] * v[i].v + mat[13]; + float w = mat[3] * v[i].u + mat[7] * v[i].v + mat[15]; + v[i].u = x / w; + v[i].v = y / w; + } + xform4_vec3(st->mat[G3D_PROJECTION][ptop], &v[i].x); + } + + /* clipping */ + if(st->opt & G3D_CLIP_FRUSTUM) { + for(i=0; i<6; i++) { + memcpy(tmpv, v, vnum * sizeof *v); + + if(clip_frustum(v, &vnum, tmpv, vnum, i) < 0) { + /* polygon completely outside of view volume. discard */ + vnum = 0; + break; + } + } + + if(!vnum) continue; + } + + for(i=0; ivport[2] + st->vport[0]; + v[i].y = (0.5f - v[i].y * 0.5f) * (float)st->vport[3] + st->vport[1]; + + /* convert pos to 24.8 fixed point */ + pv[i].x = cround64(v[i].x * 256.0f); + pv[i].y = cround64(v[i].y * 256.0f); + /* convert tex coords to 16.16 fixed point */ + pv[i].u = cround64(v[i].u * 65536.0f); + pv[i].v = cround64(v[i].v * 65536.0f); + /* pass the color through as is */ + pv[i].r = v[i].r; + pv[i].g = v[i].g; + pv[i].b = v[i].b; + pv[i].a = v[i].a; + } + + /* backface culling */ + if(vnum > 2 && st->opt & G3D_CULL_FACE) { + int32_t ax = pv[1].x - pv[0].x; + int32_t ay = pv[1].y - pv[0].y; + int32_t bx = pv[2].x - pv[0].x; + int32_t by = pv[2].y - pv[0].y; + int32_t cross_z = (ax >> 4) * (by >> 4) - (ay >> 4) * (bx >> 4); + int sign = (cross_z >> 31) & 1; + + if(!(sign ^ st->frontface)) { + continue; /* back-facing */ + } + } + + switch(vnum) { + case 1: + if(st->opt & G3D_BLEND) { + int r, g, b; + int inv_alpha = 255 - pv[0].a; + uint32_t *dest = st->pixels + (pv[0].y >> 8) * st->width + (pv[0].x >> 8); + r = ((int)pv[0].r * pv[0].a + UNPACK_R32(*dest) * inv_alpha) >> 8; + g = ((int)pv[0].g * pv[0].a + UNPACK_G32(*dest) * inv_alpha) >> 8; + b = ((int)pv[0].b * pv[0].a + UNPACK_B32(*dest) * inv_alpha) >> 8; + *dest++ = PACK_RGB16(r, g, b); + } else { + uint32_t *dest = st->pixels + (pv[0].y >> 8) * st->width + (pv[0].x >> 8); + *dest = PACK_RGB32(pv[0].r, pv[0].g, pv[0].b); + } + break; + + case 2: + { + uint32_t col = PACK_RGB32(pv[0].r, pv[0].g, pv[0].b); + draw_line(pv[0].x >> 8, pv[0].y >> 8, pv[1].x >> 8, pv[1].y >> 8, col); + } + break; + + default: + fill_mode = st->polymode; + if(st->opt & G3D_TEXTURE_2D) { + fill_mode |= POLYFILL_TEX_BIT; + } + if(st->opt & G3D_BLEND) { + fill_mode |= POLYFILL_BLEND_BIT; + } + polyfill(fill_mode, pv, vnum); + } + } +} + +void g3d_begin(int prim) +{ + st->imm_prim = prim; + st->imm_pcount = prim; + st->imm_numv = 0; +} + +void g3d_end(void) +{ + imm_flush(); +} + +static void imm_flush(void) +{ + int numv = st->imm_numv; + st->imm_numv = 0; + g3d_draw_indexed(st->imm_prim, st->imm_vbuf, numv, 0, 0); +} + +void g3d_vertex(float x, float y, float z) +{ + struct g3d_vertex *vptr = st->imm_vbuf + st->imm_numv++; + *vptr = st->imm_curv; + vptr->x = x; + vptr->y = y; + vptr->z = z; + vptr->w = 1.0f; + + if(!--st->imm_pcount) { + if(st->imm_numv >= IMM_VBUF_SIZE - st->imm_prim) { + imm_flush(); + } + st->imm_pcount = st->imm_prim; + } +} + +void g3d_normal(float x, float y, float z) +{ + st->imm_curv.nx = x; + st->imm_curv.ny = y; + st->imm_curv.nz = z; +} + +void g3d_color3b(unsigned char r, unsigned char g, unsigned char b) +{ + st->imm_curv.r = r; + st->imm_curv.g = g; + st->imm_curv.b = b; + st->imm_curv.a = 255; +} + +void g3d_color4b(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + st->imm_curv.r = r; + st->imm_curv.g = g; + st->imm_curv.b = b; + st->imm_curv.a = a; +} + +void g3d_color3f(float r, float g, float b) +{ + int ir = r * 255.0f; + int ig = g * 255.0f; + int ib = b * 255.0f; + st->imm_curv.r = ir > 255 ? 255 : ir; + st->imm_curv.g = ig > 255 ? 255 : ig; + st->imm_curv.b = ib > 255 ? 255 : ib; + st->imm_curv.a = 255; +} + +void g3d_color4f(float r, float g, float b, float a) +{ + int ir = r * 255.0f; + int ig = g * 255.0f; + int ib = b * 255.0f; + int ia = a * 255.0f; + st->imm_curv.r = ir > 255 ? 255 : ir; + st->imm_curv.g = ig > 255 ? 255 : ig; + st->imm_curv.b = ib > 255 ? 255 : ib; + st->imm_curv.a = ia > 255 ? 255 : ia; +} + +void g3d_texcoord(float u, float v) +{ + st->imm_curv.u = u; + st->imm_curv.v = v; +} + +static void xform4_vec3(const float *mat, float *vec) +{ + float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2] + mat[12]; + float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2] + mat[13]; + float z = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14]; + float w = mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15]; + + vec[0] = x; + vec[1] = y; + vec[2] = z; + vec[3] = w; +} + +static void xform3_vec3(const float *mat, float *vec) +{ + float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2]; + float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2]; + float z = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2]; + + vec[0] = x; + vec[1] = y; + vec[2] = z; +} + +#define NORMALIZE(v) \ + do { \ + float len = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); \ + if(len != 0.0) { \ + float s = 1.0 / len; \ + v[0] *= s; \ + v[1] *= s; \ + v[2] *= s; \ + } \ + } while(0) + +static void shade(struct g3d_vertex *v) +{ + int i, r, g, b; + float color[3]; + + color[0] = st->ambient[0] * st->mtl.kd[0]; + color[1] = st->ambient[1] * st->mtl.kd[1]; + color[2] = st->ambient[2] * st->mtl.kd[2]; + + for(i=0; iopt & (G3D_LIGHT0 << i))) { + continue; + } + + ldir[0] = st->lt[i].x - v->x; + ldir[1] = st->lt[i].y - v->y; + ldir[2] = st->lt[i].z - v->z; + NORMALIZE(ldir); + + if((ndotl = v->nx * ldir[0] + v->ny * ldir[1] + v->nz * ldir[2]) < 0.0f) { + ndotl = 0.0f; + } + + color[0] += st->mtl.kd[0] * st->lt[i].r * ndotl; + color[1] += st->mtl.kd[1] * st->lt[i].g * ndotl; + color[2] += st->mtl.kd[2] * st->lt[i].b * ndotl; + } + + r = cround64(color[0] * 255.0); + g = cround64(color[1] * 255.0); + b = cround64(color[2] * 255.0); + + v->r = r > 255 ? 255 : r; + v->g = g > 255 ? 255 : g; + v->b = b > 255 ? 255 : b; +} diff --git a/src/census/3dgfx.h b/src/census/3dgfx.h new file mode 100644 index 0000000..89b28d8 --- /dev/null +++ b/src/census/3dgfx.h @@ -0,0 +1,121 @@ +#ifndef THREEDGFX_H_ +#define THREEDGFX_H_ + +#include "inttypes.h" + +struct g3d_vertex { + float x, y, z, w; + float nx, ny, nz; + float u, v; + unsigned char r, g, b, a; +}; + +enum { + G3D_POINTS = 1, + G3D_LINES = 2, + G3D_TRIANGLES = 3, + G3D_QUADS = 4 +}; + +/* g3d_enable/g3d_disable bits */ +enum { + G3D_CULL_FACE = 0x000001, + G3D_DEPTH_TEST = 0x000002, /* XXX not implemented */ + G3D_LIGHTING = 0x000004, + G3D_LIGHT0 = 0x000008, + G3D_LIGHT1 = 0x000010, + G3D_LIGHT2 = 0x000020, + G3D_LIGHT3 = 0x000040, + G3D_TEXTURE_2D = 0x000080, /* XXX doesn't affect anything, use g3d_polygon_mode */ + G3D_BLEND = 0x000100, + G3D_TEXTURE_GEN = 0x000200, + G3D_CLIP_FRUSTUM = 0x000800,/* when disabled, don't clip against the frustum */ + G3D_CLIP_PLANE0 = 0x001000, /* user-defined 3D clipping planes XXX not impl. */ + G3D_CLIP_PLANE1 = 0x002000, + G3D_CLIP_PLANE2 = 0x004000, + G3D_CLIP_PLANE3 = 0x008000, + + G3D_TEXTURE_MAT = 0x010000, + + G3D_ALL = 0x7fffffff +}; + +/* arg to g3d_front_face */ +enum { G3D_CCW, G3D_CW }; + +/* arg to g3d_polygon_mode */ +enum { + G3D_WIRE, + G3D_FLAT, + G3D_GOURAUD +}; + +/* matrix stacks */ +enum { + G3D_MODELVIEW, + G3D_PROJECTION, + G3D_TEXTURE, + + G3D_NUM_MATRICES +}; + +int g3d_init(void); +void g3d_destroy(void); + +void g3d_framebuffer(int width, int height, void *pixels); +void g3d_framebuffer_addr(void *pixels); +void g3d_viewport(int x, int y, int w, int h); + +void g3d_enable(unsigned int opt); +void g3d_disable(unsigned int opt); +void g3d_setopt(unsigned int opt, unsigned int mask); +unsigned int g3d_getopt(unsigned int mask); + +void g3d_front_face(unsigned int order); +void g3d_polygon_mode(int pmode); + +void g3d_matrix_mode(int mmode); + +void g3d_load_identity(void); +void g3d_load_matrix(const float *m); +void g3d_mult_matrix(const float *m); +void g3d_push_matrix(void); +void g3d_pop_matrix(void); + +void g3d_translate(float x, float y, float z); +void g3d_rotate(float angle, float x, float y, float z); +void g3d_scale(float x, float y, float z); +void g3d_ortho(float left, float right, float bottom, float top, float znear, float zfar); +void g3d_frustum(float left, float right, float bottom, float top, float znear, float zfar); +void g3d_perspective(float vfov, float aspect, float znear, float zfar); + +/* returns pointer to the *internal* matrix, and if argument m is not null, + * also copies the internal matrix there. */ +const float *g3d_get_matrix(int which, float *m); + +void g3d_light_pos(int idx, float x, float y, float z); +void g3d_light_color(int idx, float r, float g, float b); + +void g3d_light_ambient(float r, float g, float b); + +void g3d_mtl_diffuse(float r, float g, float b); +void g3d_mtl_specular(float r, float g, float b); +void g3d_mtl_shininess(float shin); + +void g3d_set_texture(int xsz, int ysz, void *pixels); + +void g3d_draw(int prim, const struct g3d_vertex *varr, int varr_size); +void g3d_draw_indexed(int prim, const struct g3d_vertex *varr, int varr_size, + const uint16_t *iarr, int iarr_size); + +void g3d_begin(int prim); +void g3d_end(void); +void g3d_vertex(float x, float y, float z); +void g3d_normal(float x, float y, float z); +void g3d_color3b(unsigned char r, unsigned char g, unsigned char b); +void g3d_color4b(unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void g3d_color3f(float r, float g, float b); +void g3d_color4f(float r, float g, float b, float a); +void g3d_texcoord(float u, float v); + +#endif /* THREEDGFX_H_ */ diff --git a/src/census/census.c b/src/census/census.c new file mode 100644 index 0000000..1755133 --- /dev/null +++ b/src/census/census.c @@ -0,0 +1,43 @@ +#include +#include +#include "census.h" +#include "3dgfx.h" +#include "panic.h" + +void init_census(void *pixels, int xsz, int ysz) +{ + fb_pixels = pixels; + fb_width = xsz; + fb_height = ysz; + + g3d_init(); + g3d_framebuffer(xsz, ysz, fb_pixels); + g3d_polygon_mode(G3D_FLAT); + g3d_viewport(0, 0, xsz, ysz); +} + +void draw_census(void) +{ + int i; + memset(fb_pixels, 0, fb_width * fb_height * 4); + + g3d_matrix_mode(G3D_MODELVIEW); + g3d_load_identity(); + + g3d_enable(G3D_BLEND); + + g3d_color4b(64, 128, 255, 255); + for(i=0; i<2; i++) { + g3d_begin(G3D_QUADS); + g3d_vertex(-0.4, -0.5, 0); + g3d_vertex(0.6, -0.2, 0); + g3d_vertex(0, 0.8, 0); + g3d_vertex(-0.5, 0.6, 0); + g3d_end(); + + g3d_translate(0.2, -0.2, 0); + g3d_color4b(255, 32, 255, 128); + } + + g3d_disable(G3D_BLEND); +} diff --git a/src/census/census.h b/src/census/census.h new file mode 100644 index 0000000..97fa353 --- /dev/null +++ b/src/census/census.h @@ -0,0 +1,12 @@ +#ifndef CENSUS_H_ +#define CENSUS_H_ + +#include + +int fb_width, fb_height; +uint32_t *fb_pixels; + +void init_census(void *pixels, int xsz, int ysz); +void draw_census(void); + +#endif /* CENSUS_H_ */ diff --git a/src/census/gfxutil.c b/src/census/gfxutil.c new file mode 100644 index 0000000..6479205 --- /dev/null +++ b/src/census/gfxutil.c @@ -0,0 +1,217 @@ +#include "gfxutil.h" +#include "census.h" + +enum { + IN = 0, + LEFT = 1, + RIGHT = 2, + TOP = 4, + BOTTOM = 8 +}; + +static int outcode(int x, int y, int xmin, int ymin, int xmax, int ymax) +{ + int code = 0; + + if(x < xmin) { + code |= LEFT; + } else if(x > xmax) { + code |= RIGHT; + } + if(y < ymin) { + code |= TOP; + } else if(y > ymax) { + code |= BOTTOM; + } + return code; +} + +#define FIXMUL(a, b) (((a) * (b)) >> 8) +#define FIXDIV(a, b) (((a) << 8) / (b)) + +#define LERP(a, b, t) ((a) + FIXMUL((b) - (a), (t))) + +int clip_line(int *x0, int *y0, int *x1, int *y1, int xmin, int ymin, int xmax, int ymax) +{ + int oc_out; + + int oc0 = outcode(*x0, *y0, xmin, ymin, xmax, ymax); + int oc1 = outcode(*x1, *y1, xmin, ymin, xmax, ymax); + + long fx0, fy0, fx1, fy1, fxmin, fymin, fxmax, fymax; + + if(!(oc0 | oc1)) return 1; /* both points are inside */ + + fx0 = *x0 << 8; + fy0 = *y0 << 8; + fx1 = *x1 << 8; + fy1 = *y1 << 8; + fxmin = xmin << 8; + fymin = ymin << 8; + fxmax = xmax << 8; + fymax = ymax << 8; + + for(;;) { + long x, y, t; + + if(oc0 & oc1) return 0; /* both have points with the same outbit, not visible */ + if(!(oc0 | oc1)) break; /* both points are inside */ + + oc_out = oc0 ? oc0 : oc1; + + if(oc_out & TOP) { + t = FIXDIV(fymin - fy0, fy1 - fy0); + x = LERP(fx0, fx1, t); + y = fymin; + } else if(oc_out & BOTTOM) { + t = FIXDIV(fymax - fy0, fy1 - fy0); + x = LERP(fx0, fx1, t); + y = fymax; + } else if(oc_out & LEFT) { + t = FIXDIV(fxmin - fx0, fx1 - fx0); + x = fxmin; + y = LERP(fy0, fy1, t); + } else if(oc_out & RIGHT) { + t = FIXDIV(fxmax - fx0, fx1 - fx0); + x = fxmax; + y = LERP(fy0, fy1, t); + } + + if(oc_out == oc0) { + fx0 = x; + fy0 = y; + oc0 = outcode(fx0 >> 8, fy0 >> 8, xmin, ymin, xmax, ymax); + } else { + fx1 = x; + fy1 = y; + oc1 = outcode(fx1 >> 8, fy1 >> 8, xmin, ymin, xmax, ymax); + } + } + + *x0 = fx0 >> 8; + *y0 = fy0 >> 8; + *x1 = fx1 >> 8; + *y1 = fy1 >> 8; + return 1; +} + +void draw_line(int x0, int y0, int x1, int y1, uint32_t color) +{ + int i, dx, dy, x_inc, y_inc, error; + uint32_t *fb = fb_pixels; + + fb += y0 * fb_width + x0; + + dx = x1 - x0; + dy = y1 - y0; + + if(dx >= 0) { + x_inc = 1; + } else { + x_inc = -1; + dx = -dx; + } + if(dy >= 0) { + y_inc = fb_width; + } else { + y_inc = -fb_width; + dy = -dy; + } + + if(dx > dy) { + error = dy * 2 - dx; + for(i=0; i<=dx; i++) { + *fb = color; + if(error >= 0) { + error -= dx * 2; + fb += y_inc; + } + error += dy * 2; + fb += x_inc; + } + } else { + error = dx * 2 - dy; + for(i=0; i<=dy; i++) { + *fb = color; + if(error >= 0) { + error -= dy * 2; + fb += x_inc; + } + error += dx * 2; + fb += y_inc; + } + } +} + + +#define BLUR(w, h, pstep, sstep) \ + for(i=0; i> 3) & 0x1f)) + +#define UNPACK_R16(c) (((c) >> 8) & 0xf8) +#define UNPACK_G16(c) (((c) >> 3) & 0xfc) +#define UNPACK_B16(c) (((c) << 3) & 0xf8) + +#define PACK_RGB32(r, g, b) \ + ((((uint32_t)(r) & 0xff) << 16) | \ + (((uint32_t)(g) & 0xff) << 8) | \ + ((uint32_t)(b) & 0xff)) + +#define UNPACK_R32(c) (((c) >> 16) & 0xff) +#define UNPACK_G32(c) (((c) >> 8) & 0xff) +#define UNPACK_B32(c) ((c) & 0xff) + +int clip_line(int *x0, int *y0, int *x1, int *y1, int xmin, int ymin, int xmax, int ymax); +void draw_line(int x0, int y0, int x1, int y1, uint32_t color); + +/* scale in 24.8 fixed point */ +void blur_grey_horiz(uint32_t *dest, uint32_t *src, int xsz, int ysz, int radius, int scale); +void blur_grey_vert(uint32_t *dest, uint32_t *src, int xsz, int ysz, int radius, int scale); + +void convimg_rgb24_rgb16(uint16_t *dest, unsigned char *src, int xsz, int ysz); + +#endif /* GFXUTIL_H_ */ diff --git a/src/census/polyclip.c b/src/census/polyclip.c new file mode 100644 index 0000000..35e6cd6 --- /dev/null +++ b/src/census/polyclip.c @@ -0,0 +1,331 @@ +#include +#include +#include +#include "polyclip.h" + +struct ray { + float origin[3]; + float dir[3]; +}; + +static int clip_edge(struct g3d_vertex *poly, int *vnumptr, + const struct g3d_vertex *v0, const struct g3d_vertex *v1, + const struct cplane *plane); +static int check_clip_edge(const struct g3d_vertex *v0, + const struct g3d_vertex *v1, const struct cplane *plane); +static int clip_edge_frustum(struct g3d_vertex *poly, int *vnumptr, + const struct g3d_vertex *v0, const struct g3d_vertex *v1, int fplane); +static float distance_signed(float *pos, const struct cplane *plane); +static int intersect(const struct ray *ray, const struct cplane *plane, float *t); +static int inside_frustum_plane(const struct g3d_vertex *v, int fplane); + + +int clip_poly(struct g3d_vertex *vout, int *voutnum, + const struct g3d_vertex *vin, int vnum, struct cplane *plane) +{ + int i, nextidx, res; + int edges_clipped = 0; + + *voutnum = 0; + + for(i=0; i= vnum) nextidx = 0; + res = clip_edge(vout, voutnum, vin + i, vin + nextidx, plane); + if(res == 0) { + ++edges_clipped; + } + } + + if(*voutnum <= 0) { + assert(edges_clipped == 0); + return -1; + } + + return edges_clipped > 0 ? 0 : 1; +} + +int check_clip_poly(const struct g3d_vertex *v, int vnum, struct cplane *plane) +{ + int i, nextidx, res; + int edges_clipped = 0; + + for(i=0; i= vnum) nextidx = 0; + res = check_clip_edge(v + i, v + nextidx, plane); + if(res == 0) { + ++edges_clipped; + } + } + return edges_clipped ? 0 : res; +} + +int clip_frustum(struct g3d_vertex *vout, int *voutnum, + const struct g3d_vertex *vin, int vnum, int fplane) +{ + int i, nextidx, res; + int edges_clipped = 0; + + if(vnum == 1) { + /* special case: point clipping */ + return inside_frustum_plane(vin, fplane) ? 1 : -1; + } + + *voutnum = 0; + + for(i=0; i= vnum) nextidx = 0; + res = clip_edge_frustum(vout, voutnum, vin + i, vin + nextidx, fplane); + if(res == 0) { + ++edges_clipped; + } + } + + if(*voutnum <= 0) { + assert(edges_clipped == 0); + return -1; + } + + return edges_clipped > 0 ? 0 : 1; +} + +#define LERP_VATTR(res, v0, v1, t) \ + do { \ + (res)->nx = (v0)->nx + ((v1)->nx - (v0)->nx) * (t); \ + (res)->ny = (v0)->ny + ((v1)->ny - (v0)->ny) * (t); \ + (res)->nz = (v0)->nz + ((v1)->nz - (v0)->nz) * (t); \ + (res)->u = (v0)->u + ((v1)->u - (v0)->u) * (t); \ + (res)->v = (v0)->v + ((v1)->v - (v0)->v) * (t); \ + (res)->r = (v0)->r + ((v1)->r - (v0)->r) * (t); \ + (res)->g = (v0)->g + ((v1)->g - (v0)->g) * (t); \ + (res)->b = (v0)->b + ((v1)->b - (v0)->b) * (t); \ + } while(0) + + +/* returns: + * 1 -> both inside + * 0 -> straddling and clipped + * -1 -> both outside + * + * also returns the size of the polygon through vnumptr + */ +static int clip_edge(struct g3d_vertex *poly, int *vnumptr, + const struct g3d_vertex *v0, const struct g3d_vertex *v1, + const struct cplane *plane) +{ + float pos0[3], pos1[3]; + float d0, d1, t; + struct ray ray; + int i, vnum = *vnumptr; + + pos0[0] = v0->x; pos0[1] = v0->y; pos0[2] = v0->z; + pos1[0] = v1->x; pos1[1] = v1->y; pos1[2] = v1->z; + + d0 = distance_signed(pos0, plane); + d1 = distance_signed(pos1, plane); + + for(i=0; i<3; i++) { + ray.origin[i] = pos0[i]; + ray.dir[i] = pos1[i] - pos0[i]; + } + + if(d0 >= 0.0) { + /* start inside */ + if(d1 >= 0.0) { + /* all inside */ + poly[vnum++] = *v1; /* append v1 */ + *vnumptr = vnum; + return 1; + } else { + /* going out */ + struct g3d_vertex *vptr = poly + vnum; + + intersect(&ray, plane, &t); + + vptr->x = ray.origin[0] + ray.dir[0] * t; + vptr->y = ray.origin[1] + ray.dir[1] * t; + vptr->z = ray.origin[2] + ray.dir[2] * t; + vptr->w = 1.0f; + + LERP_VATTR(vptr, v0, v1, t); + vnum++; /* append new vertex on the intersection point */ + } + } else { + /* start outside */ + if(d1 >= 0) { + /* going in */ + struct g3d_vertex *vptr = poly + vnum; + + intersect(&ray, plane, &t); + + vptr->x = ray.origin[0] + ray.dir[0] * t; + vptr->y = ray.origin[1] + ray.dir[1] * t; + vptr->z = ray.origin[2] + ray.dir[2] * t; + vptr->w = 1.0f; + + LERP_VATTR(vptr, v0, v1, t); + vnum++; /* append new vertex on the intersection point */ + + /* then append v1 ... */ + poly[vnum++] = *v1; + } else { + /* all outside */ + return -1; + } + } + + *vnumptr = vnum; + return 0; +} + +/* same as above, but only checks for clipping and classifies the edge */ +static int check_clip_edge(const struct g3d_vertex *v0, + const struct g3d_vertex *v1, const struct cplane *plane) +{ + float pos0[3], pos1[3]; + float d0, d1; + + pos0[0] = v0->x; pos0[1] = v0->y; pos0[2] = v0->z; + pos1[0] = v1->x; pos1[1] = v1->y; pos1[2] = v1->z; + + d0 = distance_signed(pos0, plane); + d1 = distance_signed(pos1, plane); + + if(d0 > 0.0f && d1 > 0.0f) { + return 1; + } + if(d0 < 0.0f && d1 < 0.0f) { + return -1; + } + return 0; +} + +static float distance_signed(float *pos, const struct cplane *plane) +{ + float dx = pos[0] - plane->x; + float dy = pos[1] - plane->y; + float dz = pos[2] - plane->z; + return dx * plane->nx + dy * plane->ny + dz * plane->nz; +} + +static int intersect(const struct ray *ray, const struct cplane *plane, float *t) +{ + float orig_pt_dir[3]; + + float ndotdir = plane->nx * ray->dir[0] + plane->ny * ray->dir[1] + plane->nz * ray->dir[2]; + if(fabs(ndotdir) < 1e-6) { + *t = 0.0f; + return 0; + } + + orig_pt_dir[0] = plane->x - ray->origin[0]; + orig_pt_dir[1] = plane->y - ray->origin[1]; + orig_pt_dir[2] = plane->z - ray->origin[2]; + + *t = (plane->nx * orig_pt_dir[0] + plane->ny * orig_pt_dir[1] + plane->nz * orig_pt_dir[2]) / ndotdir; + return 1; +} + +/* homogeneous frustum clipper helpers */ + +static int inside_frustum_plane(const struct g3d_vertex *v, int fplane) +{ + switch(fplane) { + case CLIP_LEFT: + return v->x >= -v->w; + case CLIP_RIGHT: + return v->x <= v->w; + case CLIP_BOTTOM: + return v->y >= -v->w; + case CLIP_TOP: + return v->y <= v->w; + case CLIP_NEAR: + return v->z >= -v->w; + case CLIP_FAR: + return v->z <= v->w; + } + assert(0); + return 0; +} + +static float intersect_frustum(const struct g3d_vertex *a, const struct g3d_vertex *b, int fplane) +{ + switch(fplane) { + case CLIP_LEFT: + return (-a->w - a->x) / (b->x - a->x + b->w - a->w); + case CLIP_RIGHT: + return (a->w - a->x) / (b->x - a->x - b->w + a->w); + case CLIP_BOTTOM: + return (-a->w - a->y) / (b->y - a->y + b->w - a->w); + case CLIP_TOP: + return (a->w - a->y) / (b->y - a->y - b->w + a->w); + case CLIP_NEAR: + return (-a->w - a->z) / (b->z - a->z + b->w - a->w); + case CLIP_FAR: + return (a->w - a->z) / (b->z - a->z - b->w + a->w); + } + + assert(0); + return 0; +} + +static int clip_edge_frustum(struct g3d_vertex *poly, int *vnumptr, + const struct g3d_vertex *v0, const struct g3d_vertex *v1, int fplane) +{ + int vnum = *vnumptr; + int in0, in1; + float t; + + in0 = inside_frustum_plane(v0, fplane); + in1 = inside_frustum_plane(v1, fplane); + + if(in0) { + /* start inside */ + if(in1) { + /* all inside */ + poly[vnum++] = *v1; /* append v1 */ + *vnumptr = vnum; + return 1; + } else { + /* going out */ + struct g3d_vertex *vptr = poly + vnum; + + t = intersect_frustum(v0, v1, fplane); + + vptr->x = v0->x + (v1->x - v0->x) * t; + vptr->y = v0->y + (v1->y - v0->y) * t; + vptr->z = v0->z + (v1->z - v0->z) * t; + vptr->w = v0->w + (v1->w - v0->w) * t; + + LERP_VATTR(vptr, v0, v1, t); + ++vnum; /* append new vertex on the intersection point */ + } + } else { + /* start outside */ + if(in1) { + /* going in */ + struct g3d_vertex *vptr = poly + vnum; + + t = intersect_frustum(v0, v1, fplane); + + vptr->x = v0->x + (v1->x - v0->x) * t; + vptr->y = v0->y + (v1->y - v0->y) * t; + vptr->z = v0->z + (v1->z - v0->z) * t; + vptr->w = v0->w + (v1->w - v0->w) * t; + + LERP_VATTR(vptr, v0, v1, t); + ++vnum; /* append new vertex on the intersection point */ + + /* then append v1 ... */ + poly[vnum++] = *v1; + } else { + /* all outside */ + return -1; + } + } + + *vnumptr = vnum; + return 0; +} diff --git a/src/census/polyclip.h b/src/census/polyclip.h new file mode 100644 index 0000000..adee29d --- /dev/null +++ b/src/census/polyclip.h @@ -0,0 +1,38 @@ +#ifndef POLYCLIP_H_ +#define POLYCLIP_H_ + +#include "3dgfx.h" + +struct cplane { + float x, y, z; + float nx, ny, nz; +}; + +enum { + CLIP_LEFT, CLIP_RIGHT, + CLIP_BOTTOM, CLIP_TOP, + CLIP_NEAR, CLIP_FAR +}; + +/* Generic polygon clipper + * returns: + * 1 -> fully inside, not clipped + * 0 -> straddling the plane and clipped + * -1 -> fully outside, not clipped + * in all cases, vertices are copied to vout, and the vertex count is written + * to wherever voutnum is pointing + */ +int clip_poly(struct g3d_vertex *vout, int *voutnum, + const struct g3d_vertex *vin, int vnum, struct cplane *plane); + +/* only checks if the polygon would be clipped by the plane, and classifies it + * as inside/outside/straddling, without actually producing a clipped polygon. + * return values are the same as clip_poly. + */ +int check_clip_poly(const struct g3d_vertex *v, int vnum, struct cplane *plane); + +/* Special-case frustum clipper (might be slightly faster) */ +int clip_frustum(struct g3d_vertex *vout, int *voutnum, + const struct g3d_vertex *vin, int vnum, int fplane); + +#endif /* POLYCLIP_H_ */ diff --git a/src/census/polyfill.c b/src/census/polyfill.c new file mode 100644 index 0000000..3f94e25 --- /dev/null +++ b/src/census/polyfill.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__DJGPP__) +#include +#else +#include +#endif +#include "polyfill.h" +#include "gfxutil.h" + +#define FILL_POLY_BITS 0x03 + +/* mode bits: 00-wire 01-flat 10-gouraud 11-reserved + * bit 2: texture + * bit 3: blend + */ +void (*fillfunc[])(struct pvertex*, int) = { + polyfill_wire, + polyfill_flat, + polyfill_gouraud, + 0, + polyfill_tex_wire, + polyfill_tex_flat, + polyfill_tex_gouraud, + 0, + polyfill_blend_wire, + polyfill_blend_flat, + polyfill_blend_gouraud, + 0, + polyfill_blend_tex_wire, + polyfill_blend_tex_flat, + polyfill_blend_tex_gouraud, + 0 +}; + +struct pimage pfill_fb, pfill_tex; + +void polyfill(int mode, struct pvertex *verts, int nverts) +{ +#ifndef NDEBUG + if(!fillfunc[mode]) { + fprintf(stderr, "polyfill mode %d not implemented\n", mode); + abort(); + } +#endif + + fillfunc[mode](verts, nverts); +} + +void polyfill_wire(struct pvertex *verts, int nverts) +{ + int i, x0, y0, x1, y1; + struct pvertex *v = verts; + unsigned short color = ((v->r << 8) & 0xf800) | + ((v->g << 3) & 0x7e0) | ((v->b >> 3) & 0x1f); + + for(i=0; ix >> 8; + y0 = v->y >> 8; + ++v; + x1 = v->x >> 8; + y1 = v->y >> 8; + if(clip_line(&x0, &y0, &x1, &y1, 0, 0, pfill_fb.width, pfill_fb.height)) { + draw_line(x0, y0, x1, y1, color); + } + } + x0 = verts[0].x >> 8; + y0 = verts[0].y >> 8; + if(clip_line(&x1, &y1, &x0, &y0, 0, 0, pfill_fb.width, pfill_fb.height)) { + draw_line(x1, y1, x0, y0, color); + } +} + +void polyfill_tex_wire(struct pvertex *verts, int nverts) +{ + polyfill_wire(verts, nverts); /* TODO */ +} + +void polyfill_blend_wire(struct pvertex *verts, int nverts) +{ + polyfill_wire(verts, nverts); /* TODO */ +} + +void polyfill_blend_tex_wire(struct pvertex *verts, int nverts) +{ + polyfill_wire(verts, nverts); /* TODO */ +} + +#define NEXTIDX(x) (((x) - 1 + nverts) % nverts) +#define PREVIDX(x) (((x) + 1) % nverts) + +/* XXX + * When HIGH_QUALITY is defined, the rasterizer calculates slopes for attribute + * interpolation on each scanline separately; otherwise the slope for each + * attribute would be calculated once for the whole polygon, which is faster, + * but produces some slight quantization artifacts, due to the limited precision + * of fixed-point calculations. + */ +#define HIGH_QUALITY + +/* extra bits of precision to use when interpolating colors. + * try tweaking this if you notice strange quantization artifacts. + */ +#define COLOR_SHIFT 12 + + +#define POLYFILL polyfill_flat +#define SCANEDGE scanedge_flat +#undef GOURAUD +#undef TEXMAP +#undef BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE + +#define POLYFILL polyfill_gouraud +#define SCANEDGE scanedge_gouraud +#define GOURAUD +#undef TEXMAP +#undef BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE + +#define POLYFILL polyfill_tex_flat +#define SCANEDGE scanedge_tex_flat +#undef GOURAUD +#define TEXMAP +#undef BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE + +#define POLYFILL polyfill_tex_gouraud +#define SCANEDGE scanedge_tex_gouraud +#define GOURAUD +#define TEXMAP +#undef BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE + +#define POLYFILL polyfill_blend_flat +#define SCANEDGE scanedge_blend_flat +#undef GOURAUD +#undef TEXMAP +#define BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE + +#define POLYFILL polyfill_blend_gouraud +#define SCANEDGE scanedge_blend_gouraud +#define GOURAUD +#undef TEXMAP +#define BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE + +#define POLYFILL polyfill_blend_tex_flat +#define SCANEDGE scanedge_blend_tex_flat +#undef GOURAUD +#define TEXMAP +#define BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE + +#define POLYFILL polyfill_blend_tex_gouraud +#define SCANEDGE scanedge_blend_tex_gouraud +#define GOURAUD +#define TEXMAP +#define BLEND +#include "polytmpl.h" +#undef POLYFILL +#undef SCANEDGE diff --git a/src/census/polyfill.h b/src/census/polyfill.h new file mode 100644 index 0000000..0a88098 --- /dev/null +++ b/src/census/polyfill.h @@ -0,0 +1,61 @@ +#ifndef POLYFILL_H_ +#define POLYFILL_H_ + +#include "inttypes.h" + +#define POLYFILL_MODE_MASK 0x03 +#define POLYFILL_TEX_BIT 0x04 +#define POLYFILL_BLEND_BIT 0x08 + +enum { + POLYFILL_WIRE = 0, + POLYFILL_FLAT, + POLYFILL_GOURAUD, + + POLYFILL_TEX_WIRE = 4, + POLYFILL_TEX_FLAT, + POLYFILL_TEX_GOURAUD, + + POLYFILL_BLEND_WIRE = 8, + POLYFILL_BLEND_FLAT, + POLYFILL_BLEND_GOURAUD, + + POLYFILL_BLEND_TEX_WIRE = 12, + POLYFILL_BLEND_TEX_FLAT, + POLYFILL_BLEND_TEX_GOURAUD +}; + +/* projected vertices for the rasterizer */ +struct pvertex { + int32_t x, y; /* 24.8 fixed point */ + int32_t u, v; /* 16.16 fixed point */ + int32_t r, g, b, a; /* int 0-255 */ +}; + +struct pimage { + uint32_t *pixels; + int width, height; + + int xshift, yshift; + unsigned int xmask, ymask; +}; + +extern struct pimage pfill_fb; +extern struct pimage pfill_tex; + +void polyfill(int mode, struct pvertex *verts, int nverts); + +void polyfill_wire(struct pvertex *verts, int nverts); +void polyfill_flat(struct pvertex *verts, int nverts); +void polyfill_gouraud(struct pvertex *verts, int nverts); +void polyfill_tex_wire(struct pvertex *verts, int nverts); +void polyfill_tex_flat(struct pvertex *verts, int nverts); +void polyfill_tex_gouraud(struct pvertex *verts, int nverts); +void polyfill_blend_wire(struct pvertex *verts, int nverts); +void polyfill_blend_flat(struct pvertex *verts, int nverts); +void polyfill_blend_gouraud(struct pvertex *verts, int nverts); +void polyfill_blend_tex_wire(struct pvertex *verts, int nverts); +void polyfill_blend_tex_flat(struct pvertex *verts, int nverts); +void polyfill_blend_tex_gouraud(struct pvertex *verts, int nverts); + +#endif /* POLYFILL_H_ */ diff --git a/src/census/polytmpl.h b/src/census/polytmpl.h new file mode 100644 index 0000000..03724cf --- /dev/null +++ b/src/census/polytmpl.h @@ -0,0 +1,345 @@ +static uint32_t SCANEDGE(struct pvertex *v0, struct pvertex *v1, struct pvertex *edge) +{ + int i; + int32_t x, dx, dy, slope; +#ifdef GOURAUD + int r, g, b, dr, dg, db; + int32_t rslope, gslope, bslope; +#ifdef BLEND + int32_t a, da, aslope; +#endif +#endif /* GOURAUD */ +#ifdef TEXMAP + int32_t u, v, du, dv, uslope, vslope; +#endif + int32_t start_idx, end_idx; + + if(v0->y > v1->y) { + struct pvertex *tmp = v0; + v0 = v1; + v1 = tmp; + } + + x = v0->x; + dy = v1->y - v0->y; + dx = v1->x - v0->x; + slope = (dx << 8) / dy; +#ifdef GOURAUD + r = (v0->r << COLOR_SHIFT); + g = (v0->g << COLOR_SHIFT); + b = (v0->b << COLOR_SHIFT); + dr = (v1->r << COLOR_SHIFT) - r; + dg = (v1->g << COLOR_SHIFT) - g; + db = (v1->b << COLOR_SHIFT) - b; + rslope = (dr << 8) / dy; + gslope = (dg << 8) / dy; + bslope = (db << 8) / dy; +#ifdef BLEND + a = (v0->a << COLOR_SHIFT); + da = (v1->a << COLOR_SHIFT) - a; + aslope = (da << 8) / dy; +#endif /* BLEND */ +#endif /* GOURAUD */ +#ifdef TEXMAP + u = v0->u; + v = v0->v; + du = v1->u - v0->u; + dv = v1->v - v0->v; + uslope = (du << 8) / dy; + vslope = (dv << 8) / dy; +#endif + + start_idx = v0->y >> 8; + end_idx = v1->y >> 8; + + for(i=start_idx; i pv[botidx].y) botidx = i; + } + + winding = 0; + for(i=0; i> 4) * ((pv[next].y + pv[i].y) >> 4); + } + + /* +1 to avoid crashing due to off-by-one rounding errors in the rasterization */ + left = alloca((pfill_fb.height + 1) * sizeof *left); + right = alloca((pfill_fb.height + 1) * sizeof *right); + + for(i=0; i> 8) == (y1 >> 8)) { + /*if(y0 > y1) {*/ + int i0, i1; + int idx = y0 >> 8; + if(pv[i].x < pv[next].x) { + i0 = i; + i1 = next; + } else { + i0 = next; + i1 = i; + } + left[idx].x = pv[i0].x; + right[idx].x = pv[i1].x; +#ifdef GOURAUD + left[idx].r = pv[i0].r << COLOR_SHIFT; + left[idx].g = pv[i0].g << COLOR_SHIFT; + left[idx].b = pv[i0].b << COLOR_SHIFT; + right[idx].r = pv[i1].r << COLOR_SHIFT; + right[idx].g = pv[i1].g << COLOR_SHIFT; + right[idx].b = pv[i1].b << COLOR_SHIFT; +#ifdef BLEND + left[idx].a = pv[i0].a << COLOR_SHIFT; + right[idx].a = pv[i1].a << COLOR_SHIFT; +#endif /* BLEND */ +#endif +#ifdef TEXMAP + left[idx].u = pv[i0].u; + left[idx].v = pv[i0].v; + right[idx].u = pv[i1].u; + right[idx].v = pv[i1].v; +#endif + if(idx > slbot) slbot = idx; + if(idx < sltop) sltop = idx; + /*}*/ + } else { + struct pvertex *edge; + uint32_t res, tmp; + + if(winding < 0) { + // clockwise + edge = y0 > y1 ? left : right; + } else { + // counter-clockwise + edge = y0 > y1 ? right : left; + } + res = SCANEDGE(pv + i, pv + next, edge); + tmp = (res >> 16) & 0xffff; + if(tmp > slbot) slbot = tmp; + if((tmp = res & 0xffff) < sltop) { + sltop = tmp; + } + } + } + + /* calculate the slopes of all attributes across the largest span out + * of the three: middle, top, or bottom. + */ +#ifndef HIGH_QUALITY +#if defined(GOURAUD) || defined(TEXMAP) + mid = (sltop + slbot) >> 1; + dx = right[mid].x - left[mid].x; + if((tmp = right[sltop].x - left[sltop].x) > dx) { + dx = tmp; + mid = sltop; + } + if((tmp = right[slbot].x - left[slbot].x) > dx) { + dx = tmp; + mid = slbot; + } + if(!dx) dx = 256; /* avoid division by zero */ +#endif +#ifdef GOURAUD + dr = right[mid].r - left[mid].r; + dg = right[mid].g - left[mid].g; + db = right[mid].b - left[mid].b; + rslope = (dr << 8) / dx; + gslope = (dg << 8) / dx; + bslope = (db << 8) / dx; +#ifdef BLEND + da = right[mid].a - left[mid].a; + aslope = (da << 8) / dx; +#endif /* BLEND */ +#endif +#ifdef TEXMAP + du = right[mid].u - left[mid].u; + dv = right[mid].v - left[mid].v; + uslope = (du << 8) / dx; + vslope = (dv << 8) / dx; +#endif +#endif /* !defined(HIGH_QUALITY) */ + + /* for each scanline ... */ + for(i=sltop; i<=slbot; i++) { + uint32_t *pixptr; + int32_t x; + + x = left[i].x; + pixptr = pfill_fb.pixels + i * pfill_fb.width + (x >> 8); + +#ifdef GOURAUD + r = left[i].r; + g = left[i].g; + b = left[i].b; +#ifdef BLEND + a = left[i].a; +#endif /* BLEND */ +#endif +#ifdef TEXMAP + u = left[i].u; + v = left[i].v; +#endif + +#if defined(HIGH_QUALITY) && (defined(GOURAUD) || defined(TEXMAP)) + if(!(dx = right[i].x - left[i].x)) dx = 256; + +#ifdef GOURAUD + dr = right[i].r - left[i].r; + dg = right[i].g - left[i].g; + db = right[i].b - left[i].b; + rslope = (dr << 8) / dx; + gslope = (dg << 8) / dx; + bslope = (db << 8) / dx; +#ifdef BLEND + da = right[i].a - left[i].a; + aslope = (da << 8) / dx; +#endif /* BLEND */ +#endif /* GOURAUD */ +#ifdef TEXMAP + du = right[i].u - left[i].u; + dv = right[i].v - left[i].v; + uslope = (du << 8) / dx; + vslope = (dv << 8) / dx; +#endif +#endif /* HIGH_QUALITY */ + + /* go across the scanline interpolating if necessary */ + while(x <= right[i].x) { +#if defined(GOURAUD) || defined(TEXMAP) || defined(BLEND) + int cr, cg, cb; +#endif +#ifdef BLEND + uint32_t fbcol; + int alpha, inv_alpha; +#endif +#ifdef GOURAUD + /* we upped the color precision to while interpolating the + * edges, now drop the extra bits before packing + */ + cr = r < 0 ? 0 : (r >> COLOR_SHIFT); + cg = g < 0 ? 0 : (g >> COLOR_SHIFT); + cb = b < 0 ? 0 : (b >> COLOR_SHIFT); + r += rslope; + g += gslope; + b += bslope; +#ifdef BLEND + a += aslope; +#else + if(cr > 255) cr = 255; + if(cg > 255) cg = 255; + if(cb > 255) cb = 255; +#endif /* BLEND */ +#endif /* GOURAUD */ +#ifdef TEXMAP + { + int tx = (u >> (16 - pfill_tex.xshift)) & pfill_tex.xmask; + int ty = (v >> (16 - pfill_tex.yshift)) & pfill_tex.ymask; + uint32_t texel = pfill_tex.pixels[(ty << pfill_tex.xshift) + tx]; +#ifdef GOURAUD + /* This is not correct, should be /255, but it's much faster + * to shift by 8 (/256), and won't make a huge difference + */ + cr = (cr * UNPACK_R32(texel)) >> 8; + cg = (cg * UNPACK_G32(texel)) >> 8; + cb = (cb * UNPACK_B32(texel)) >> 8; +#else + cr = UNPACK_R32(texel); + cg = UNPACK_G32(texel); + cb = UNPACK_B32(texel); +#endif + } + u += uslope; + v += vslope; +#endif + +#ifdef BLEND +#if !defined(GOURAUD) && !defined(TEXMAP) + /* flat version: cr,cg,cb are uninitialized so far */ + cr = pv[0].r; + cg = pv[0].g; + cb = pv[0].b; +#endif +#ifdef GOURAUD + alpha = a >> COLOR_SHIFT; +#else + alpha = pv[0].a; +#endif + fbcol = *pixptr; + inv_alpha = 255 - alpha; + cr = (cr * alpha + UNPACK_R32(fbcol) * inv_alpha) >> 8; + cg = (cg * alpha + UNPACK_G32(fbcol) * inv_alpha) >> 8; + cb = (cb * alpha + UNPACK_B32(fbcol) * inv_alpha) >> 8; + if(cr > 255) cr = 255; + if(cg > 255) cg = 255; + if(cb > 255) cb = 255; +#endif /* BLEND */ + +#if defined(GOURAUD) || defined(TEXMAP) || defined(BLEND) + color = PACK_RGB32(cr, cg, cb); +#endif + +#ifdef DEBUG_OVERDRAW + *pixptr++ += DEBUG_OVERDRAW; +#else + *pixptr++ = color; +#endif + x += 256; + } + } +} + diff --git a/src/census/util.c b/src/census/util.c new file mode 100644 index 0000000..a91bb95 --- /dev/null +++ b/src/census/util.c @@ -0,0 +1,3 @@ +#include "util.h" + +uint32_t perf_start_count, perf_interval_count; diff --git a/src/census/util.h b/src/census/util.h new file mode 100644 index 0000000..76dd044 --- /dev/null +++ b/src/census/util.h @@ -0,0 +1,102 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include "inttypes.h" + +#ifdef __GNUC__ +#define INLINE __inline + +#elif defined(__WATCOMC__) +#define INLINE __inline + +#else +#define INLINE +#endif + +/* fast conversion of double -> 32bit int + * for details see: + * - http://chrishecker.com/images/f/fb/Gdmfp.pdf + * - http://stereopsis.com/FPU.html#convert + */ +static INLINE int32_t cround64(double val) +{ + val += 6755399441055744.0; + return *(int32_t*)&val; +} + +extern uint32_t perf_start_count, perf_interval_count; + +#ifdef __WATCOMC__ +void perf_start(void); +#pragma aux perf_start = \ + "xor eax, eax" \ + "cpuid" \ + "rdtsc" \ + "mov [perf_start_count], eax" \ + modify[eax ebx ecx edx]; + +void perf_end(void); +#pragma aux perf_end = \ + "xor eax, eax" \ + "cpuid" \ + "rdtsc" \ + "sub eax, [perf_start_count]" \ + "mov [perf_interval_count], eax" \ + modify [eax ebx ecx edx]; + +void debug_break(void); +#pragma aux debug_break = "int 3"; +#endif + +#ifdef __GNUC__ +#define perf_start() asm volatile ( \ + "xor %%eax, %%eax\n" \ + "cpuid\n" \ + "rdtsc\n" \ + "mov %%eax, %0\n" \ + : "=m"(perf_start_count) \ + :: "%eax", "%ebx", "%ecx", "%edx") + +#define perf_end() asm volatile ( \ + "xor %%eax, %%eax\n" \ + "cpuid\n" \ + "rdtsc\n" \ + "sub %1, %%eax\n" \ + "mov %%eax, %0\n" \ + : "=m"(perf_interval_count) \ + : "m"(perf_start_count) \ + : "%eax", "%ebx", "%ecx", "%edx") + +#define debug_break() \ + asm volatile ("int $3") +#endif + +#ifdef _MSC_VER +#define perf_start() \ + do { \ + __asm { \ + xor eax, eax \ + cpuid \ + rdtsc \ + mov [perf_start_count], eax \ + } \ + } while(0) + +#define perf_end() \ + do { \ + __asm { \ + xor eax, eax \ + cpuid \ + rdtsc \ + sub eax, [perf_start_count] \ + mov [perf_interval_count], eax \ + } \ + } while(0) + +#define debug_break() \ + do { \ + __asm { int 3 } \ + } while(0) +#endif + +#endif /* UTIL_H_ */ diff --git a/src/kmain.c b/src/kmain.c index ad76287..99104a2 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -29,12 +29,14 @@ along with this program. If not, see . #include "vbe.h" #include "audio.h" #include "panic.h" +#include "census/census.h" static int video_init(void); static int modecmp(const void *a, const void *b); static struct video_mode vmode; -static void *fbptr; +static void *fbptr, *backbuf; +static int fbsize; void pcboot_main(void) @@ -60,10 +62,17 @@ void pcboot_main(void) if(video_init() == -1) { panic("Failed to find suitable video mode"); } + fbsize = vmode.width * vmode.height * vmode.bpp / 8; + if(!(backbuf = malloc(fbsize))) { + panic("Failed to allocate back buffer"); + } + init_census(backbuf, vmode.width, vmode.height); for(;;) { + draw_census(); + wait_vsync(); - memset(fbptr, 0x80, vmode.width * vmode.height * vmode.bpp / 8); + memcpy(fbptr, backbuf, fbsize); } } @@ -75,7 +84,7 @@ static int video_init(void) const char *vendor; if(mode_idx == -1 && (vendor = get_video_vendor()) && strstr(vendor, "SeaBIOS")) { - mode_idx = find_video_mode_idx(800, 600, 0); + mode_idx = find_video_mode_idx(800, 600, 32); } if(mode_idx == -1 && vbe_get_edid(&edid) == 0 && edid_preferred_resolution(&edid, &xres, &yres) == 0) { -- 1.7.10.4