moved over the polyfiller code and implemented blending
authorJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 3 Sep 2019 03:19:29 +0000 (06:19 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 3 Sep 2019 03:19:29 +0000 (06:19 +0300)
17 files changed:
.gdbinit
.gitignore
Makefile
src/census/3dgfx.c [new file with mode: 0644]
src/census/3dgfx.h [new file with mode: 0644]
src/census/census.c [new file with mode: 0644]
src/census/census.h [new file with mode: 0644]
src/census/gfxutil.c [new file with mode: 0644]
src/census/gfxutil.h [new file with mode: 0644]
src/census/polyclip.c [new file with mode: 0644]
src/census/polyclip.h [new file with mode: 0644]
src/census/polyfill.c [new file with mode: 0644]
src/census/polyfill.h [new file with mode: 0644]
src/census/polytmpl.h [new file with mode: 0644]
src/census/util.c [new file with mode: 0644]
src/census/util.h [new file with mode: 0644]
src/kmain.c

index 06d68b4..5a75505 100644 (file)
--- a/.gdbinit
+++ b/.gdbinit
@@ -1,2 +1,2 @@
 target remote localhost:1234
-symbol-file test.sym
+symbol-file bcensus.sym
index 5ad65e9..130c484 100644 (file)
@@ -2,7 +2,7 @@
 *.swp
 *.d
 *.bin
-*.elf
+bcensus
 *.map
 *.sym
 *.disasm
index 7f4b7e9..2a0ad5c 100644 (file)
--- 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 (file)
index 0000000..9058d28
--- /dev/null
@@ -0,0 +1,706 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__DJGPP__)
+#include <malloc.h>
+#else
+#include <alloca.h>
+#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; i<G3D_NUM_MATRICES; i++) {
+               g3d_matrix_mode(i);
+               g3d_load_identity();
+       }
+
+       for(i=0; i<MAX_LIGHTS; i++) {
+               g3d_light_color(i, 1, 1, 1);
+       }
+       g3d_light_ambient(0.1, 0.1, 0.1);
+
+       g3d_mtl_diffuse(1, 1, 1);
+       return 0;
+}
+
+void g3d_destroy(void)
+{
+       free(st);
+}
+
+void g3d_framebuffer(int width, int height, void *pixels)
+{
+       st->width = 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; j<nfaces; j++) {
+               vnum = prim;    /* reset vnum for each iteration */
+
+               for(i=0; i<vnum; i++) {
+                       v[i] = iarr ? varr[*iarr++] : *varr++;
+
+                       xform4_vec3(st->mat[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; i<vnum; i++) {
+                       if(v[i].w != 0.0f) {
+                               v[i].x /= v[i].w;
+                               v[i].y /= v[i].w;
+                               /*v[i].z /= v[i].w;*/
+                       }
+
+                       /* viewport transformation */
+                       v[i].x = (v[i].x * 0.5f + 0.5f) * (float)st->vport[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; i<MAX_LIGHTS; i++) {
+               float ldir[3];
+               float ndotl;
+
+               if(!(st->opt & (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 (file)
index 0000000..89b28d8
--- /dev/null
@@ -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 (file)
index 0000000..1755133
--- /dev/null
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <string.h>
+#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 (file)
index 0000000..97fa353
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef CENSUS_H_
+#define CENSUS_H_
+
+#include <inttypes.h>
+
+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 (file)
index 0000000..6479205
--- /dev/null
@@ -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<h; i++) { \
+               int sum = sptr[0] * (rad + 1); \
+               int count = (rad * 2 + 1) << 8; \
+               int midsize = w - rad * 2; \
+               int firstpix = sptr[0]; \
+               int lastpix = sptr[pstep * (w - 1)]; \
+               /* add up the contributions for the -1 pixel */ \
+               for(j=0; j<rad; j++) { \
+                       sum += sptr[pstep * j]; \
+               } \
+               /* first part adding sptr[rad] and subtracting sptr[0] */ \
+               for(j=0; j<=rad; j++) { \
+                       sum += (int)sptr[pstep * rad] - firstpix; \
+                       sptr += pstep; \
+                       *dptr = scale * sum / count; \
+                       dptr += pstep; \
+               } \
+               /* middle part adding sptr[rad] and subtracting sptr[-(rad+1)] */ \
+               for(j=1; j<midsize; j++) { \
+                       sum += (int)sptr[pstep * rad] - (int)sptr[-(rad + 1) * pstep]; \
+                       sptr += pstep; \
+                       *dptr = scale * sum / count; \
+                       dptr += pstep; \
+               } \
+               /* last part adding lastpix and subtracting sptr[-(rad+1)] */ \
+               for(j=0; j<rad; j++) { \
+                       sum += lastpix - (int)sptr[-(rad + 1) * pstep]; \
+                       sptr += pstep; \
+                       *dptr = scale * sum / count; \
+                       dptr += pstep; \
+               } \
+               sptr += sstep; \
+               dptr += sstep; \
+       }
+
+/* TODO bound blur rad to image size to avoid inner loop conditionals */
+/* TODO make version with pow2 (rad*2+1) to avoid div with count everywhere */
+void blur_grey_horiz(uint32_t *dest, uint32_t *src, int xsz, int ysz, int rad, int scale)
+{
+       int i, j;
+       unsigned char *dptr = (unsigned char*)dest;
+       unsigned char *sptr = (unsigned char*)src;
+
+       BLUR(xsz, ysz, 2, 0);
+}
+
+
+void blur_grey_vert(uint32_t *dest, uint32_t *src, int xsz, int ysz, int rad, int scale)
+{
+       int i, j;
+       unsigned char *dptr = (unsigned char*)dest;
+       unsigned char *sptr = (unsigned char*)src;
+       int pixel_step = xsz * 2;
+       int scanline_step = 2 - ysz * pixel_step;
+
+       BLUR(ysz, xsz, pixel_step, scanline_step);
+}
+
+void convimg_rgb24_rgb16(uint16_t *dest, unsigned char *src, int xsz, int ysz)
+{
+       int i;
+       int npixels = xsz * ysz;
+
+       for(i=0; i<npixels; i++) {
+               int r = *src++;
+               int g = *src++;
+               int b = *src++;
+               *dest++ = PACK_RGB16(r, g, b);
+       }
+}
diff --git a/src/census/gfxutil.h b/src/census/gfxutil.h
new file mode 100644 (file)
index 0000000..feb4d01
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef GFXUTIL_H_
+#define GFXUTIL_H_
+
+#include "inttypes.h"
+
+#define PACK_RGB16(r, g, b) \
+       ((((uint16_t)(r) << 8) & 0xf800) | \
+        (((uint16_t)(g) << 3) & 0x7e0) | \
+        (((uint16_t)(b) >> 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 (file)
index 0000000..35e6cd6
--- /dev/null
@@ -0,0 +1,331 @@
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+#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; i++) {
+               nextidx = i + 1;
+               if(nextidx >= 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; i++) {
+               nextidx = i + 1;
+               if(nextidx >= 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; i++) {
+               nextidx = i + 1;
+               if(nextidx >= 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 (file)
index 0000000..adee29d
--- /dev/null
@@ -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 (file)
index 0000000..3f94e25
--- /dev/null
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__DJGPP__)
+#include <malloc.h>
+#else
+#include <alloca.h>
+#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; i<nverts - 1; i++) {
+               x0 = v->x >> 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 (file)
index 0000000..0a88098
--- /dev/null
@@ -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 (file)
index 0000000..03724cf
--- /dev/null
@@ -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<end_idx; i++) {
+               edge[i].x = x;
+               x += slope;
+#ifdef GOURAUD
+               /* we'll store the color in the edge tables with COLOR_SHIFT extra bits of precision */
+               edge[i].r = r;
+               edge[i].g = g;
+               edge[i].b = b;
+               r += rslope;
+               g += gslope;
+               b += bslope;
+#ifdef BLEND
+               edge[i].a = a;
+               a += aslope;
+#endif
+#endif /* GOURAUD */
+#ifdef TEXMAP
+               edge[i].u = u;
+               edge[i].v = v;
+               u += uslope;
+               v += vslope;
+#endif
+       }
+
+       return (uint32_t)start_idx | ((uint32_t)(end_idx - 1) << 16);
+}
+
+void POLYFILL(struct pvertex *pv, int nverts)
+{
+       int i, winding;
+       int topidx = 0, botidx = 0, sltop = pfill_fb.height, slbot = 0;
+       struct pvertex *left, *right;
+       uint32_t color;
+       /* the following variables are used for interpolating horizontally accros scanlines */
+#if defined(GOURAUD) || defined(TEXMAP)
+       int mid;
+       int32_t dx, tmp;
+#else
+       /* flat version, just pack the color now */
+       color = PACK_RGB32(pv[0].r, pv[0].g, pv[0].b);
+#endif
+#ifdef GOURAUD
+       int32_t r, g, b, dr, dg, db, rslope, gslope, bslope;
+#ifdef BLEND
+       int32_t a, da, aslope;
+#endif
+#endif
+#ifdef TEXMAP
+       int32_t u, v, du, dv, uslope, vslope;
+#endif
+
+       for(i=1; i<nverts; i++) {
+               if(pv[i].y < pv[topidx].y) topidx = i;
+               if(pv[i].y > pv[botidx].y) botidx = i;
+       }
+
+       winding = 0;
+       for(i=0; i<nverts; i++) {
+               int next = NEXTIDX(i);
+               winding += ((pv[next].x - pv[i].x) >> 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<nverts; i++) {
+               int next = NEXTIDX(i);
+               int32_t y0 = pv[i].y;
+               int32_t y1 = pv[next].y;
+
+               if((y0 >> 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 (file)
index 0000000..a91bb95
--- /dev/null
@@ -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 (file)
index 0000000..76dd044
--- /dev/null
@@ -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_ */
index ad76287..99104a2 100644 (file)
@@ -29,12 +29,14 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #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) {