BSP tree test
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 25 Aug 2018 10:21:37 +0000 (13:21 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 25 Aug 2018 10:21:37 +0000 (13:21 +0300)
GNUmakefile
src/bsptree.c [new file with mode: 0644]
src/bsptree.h [new file with mode: 0644]
src/dos/main.c
src/dos/mouse.asm [new file with mode: 0644]
src/dos/sball.c [deleted file]
src/parts/rtxonoff.c
src/polyclip.c
src/polyclip.h
src/sdl/main.c

index 6251a07..1961fe3 100644 (file)
@@ -6,8 +6,8 @@ bin = demo
 inc = -I/usr/local/include -Isrc -Isrc/sdl -Ilibs/imago/src -Ilibs/mikmod/include
 warn = -pedantic -Wall -Wno-unused-variable -Wno-unused-function
 
-CFLAGS = $(warn) -g $(inc) `sdl-config --cflags`
-LDFLAGS = -Llibs/imago -Llibs/mikmod -limago -lmikmod `sdl-config --libs` -lm
+CFLAGS = $(warn) -g -O3 $(inc) `sdl-config --cflags` -fopenmp
+LDFLAGS = -Llibs/imago -Llibs/mikmod -limago -lmikmod `sdl-config --libs` -lm -lgomp
 
 $(bin): $(obj) imago mikmod
        $(CC) -o $@ $(obj) $(LDFLAGS)
diff --git a/src/bsptree.c b/src/bsptree.c
new file mode 100644 (file)
index 0000000..7783e25
--- /dev/null
@@ -0,0 +1,482 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__DJGPP__)
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+#include "bsptree.h"
+#include "dynarr.h"
+#include "inttypes.h"
+#include "polyclip.h"
+#include "vmath.h"
+#include "util.h"
+
+struct bspfile_header {
+       char magic[4];
+       uint32_t num_nodes;
+};
+
+static int count_nodes(struct bspnode *n);
+static void free_tree(struct bspnode *n);
+
+static int init_poly(struct bsppoly *poly, struct g3d_vertex *v, int vnum);
+static int init_poly_noplane(struct bsppoly *poly, struct g3d_vertex *v, int vnum);
+
+static struct bspnode *build_tree(struct bsppoly *polyarr, int num_polys);
+
+static void draw_bsp_tree(struct bspnode *n, const vec3_t *vdir);
+
+static void save_bsp_tree(struct bspnode *n, FILE *fp);
+static struct bspnode *load_bsp_tree(FILE *fp);
+
+int init_bsp(struct bsptree *bsp)
+{
+       bsp->root = 0;
+       bsp->soup = 0;
+       return 0;
+}
+
+void destroy_bsp(struct bsptree *bsp)
+{
+       int i;
+
+       free_tree(bsp->root);
+
+       if(bsp->soup) {
+               for(i=0; i<dynarr_size(bsp->soup); i++) {
+                       free(bsp->soup[i].verts);
+               }
+               dynarr_free(bsp->soup);
+       }
+}
+
+int save_bsp(struct bsptree *bsp, const char *fname)
+{
+       FILE *fp;
+       struct bspfile_header hdr;
+
+       if(!(fp = fopen(fname, "wb"))) {
+               fprintf(stderr, "save_bsp: failed to open %s for writing\n", fname);
+               return -1;
+       }
+
+       memcpy(hdr.magic, "MBSP", 4);
+       hdr.num_nodes = count_nodes(bsp->root);
+       fwrite(&hdr, sizeof hdr, 1, fp);
+
+       save_bsp_tree(bsp->root, fp);
+
+       fclose(fp);
+       return 0;
+}
+
+int load_bsp(struct bsptree *bsp, const char *fname)
+{
+       FILE *fp;
+       struct bspfile_header hdr;
+
+       if(!(fp = fopen(fname, "rb"))) {
+               fprintf(stderr, "load_bsp: failed to open %s\n", fname);
+               return -1;
+       }
+
+       if(fread(&hdr, sizeof hdr, 1, fp) < 1) {
+               fprintf(stderr, "load_bsp: %s: failed to read header\n", fname);
+               fclose(fp);
+               return -1;
+       }
+       if(memcmp(hdr.magic, "MBSP", 4) != 0) {
+               fprintf(stderr, "load_bsp: %s: invalid magic\n", fname);
+               fclose(fp);
+               return -1;
+       }
+       bsp->root = load_bsp_tree(fp);
+
+       fclose(fp);
+       return 0;
+}
+
+int bsp_add_poly(struct bsptree *bsp, struct g3d_vertex *v, int vnum)
+{
+       struct bsppoly poly, *tmp;
+
+       if(!bsp->soup && !(bsp->soup = dynarr_alloc(0, sizeof *bsp->soup))) {
+               fprintf(stderr, "bsp_add_poly: failed to create polygon soup dynamic array\n");
+               return -1;
+       }
+
+       if(init_poly(&poly, v, vnum) == -1) {
+               return -1;
+       }
+
+       if(!(tmp = dynarr_push(bsp->soup, &poly))) {
+               fprintf(stderr, "bsp_add_poly: failed to reallocate polygon soup\n");
+               free(poly.verts);
+               return -1;
+       }
+       bsp->soup = tmp;
+
+       return 0;
+}
+
+int bsp_add_mesh(struct bsptree *bsp, struct g3d_mesh *m)
+{
+       int i, j, nfaces;
+       struct g3d_vertex v[4];
+       struct g3d_vertex *vptr = m->varr;
+       uint16_t *iptr = m->iarr;
+
+       nfaces = m->iarr ? m->icount / m->prim : m->vcount / m->prim;
+
+       for(i=0; i<nfaces; i++) {
+               for(j=0; j<m->prim; j++) {
+                       if(m->iarr) {
+                               v[j] = m->varr[*iptr++];
+                       } else {
+                               v[j] = *vptr++;
+                       }
+               }
+               if(bsp_add_poly(bsp, v, m->prim) == -1) {
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int bsp_build(struct bsptree *bsp)
+{
+       assert(bsp->soup);
+       assert(!dynarr_empty(bsp->soup));
+
+       free_tree(bsp->root);
+
+       printf("building BSP tree with %d source polygons ...\n", dynarr_size(bsp->soup));
+       if(!(bsp->root = build_tree(bsp->soup, dynarr_size(bsp->soup)))) {
+               fprintf(stderr, "bsp_build failed\n");
+               return -1;
+       }
+       printf("done. created a tree with %d nodes\n", count_nodes(bsp->root));
+
+       /* build_tree has called dynarr_free on bsp->soup */
+       bsp->soup = 0;
+
+       return 0;
+}
+
+void draw_bsp(struct bsptree *bsp, float view_x, float view_y, float view_z)
+{
+       vec3_t vdir;
+
+       assert(bsp->root);
+
+       vdir.x = view_x;
+       vdir.y = view_y;
+       vdir.z = view_z;
+       draw_bsp_tree(bsp->root, &vdir);
+}
+
+static int count_nodes(struct bspnode *n)
+{
+       if(!n) return 0;
+       return count_nodes(n->front) + count_nodes(n->back) + 1;
+}
+
+static void free_tree(struct bspnode *n)
+{
+       if(n) {
+               free_tree(n->front);
+               free_tree(n->back);
+               free(n->poly.verts);
+               free(n);
+       }
+}
+
+
+static int init_poly(struct bsppoly *poly, struct g3d_vertex *v, int vnum)
+{
+       vec3_t va, vb, norm;
+
+       if(init_poly_noplane(poly, v, vnum) == -1) {
+               return -1;
+       }
+
+       va.x = v[1].x - v[0].x;
+       va.y = v[1].y - v[0].y;
+       va.z = v[1].z - v[0].z;
+       vb.x = v[2].x - v[0].x;
+       vb.y = v[2].y - v[0].y;
+       vb.z = v[2].z - v[0].z;
+       norm = v3_cross(va, vb);
+       v3_normalize(&norm);
+
+       poly->plane.x = v[0].x;
+       poly->plane.y = v[0].y;
+       poly->plane.z = v[0].z;
+       poly->plane.nx = norm.x;
+       poly->plane.ny = norm.y;
+       poly->plane.nz = norm.z;
+       return 0;
+}
+
+static int init_poly_noplane(struct bsppoly *poly, struct g3d_vertex *v, int vnum)
+{
+       if(!(poly->verts = malloc(vnum * sizeof *poly->verts))) {
+               fprintf(stderr, "failed to allocate BSP polygon\n");
+               return -1;
+       }
+       poly->vcount = vnum;
+       memcpy(poly->verts, v, vnum * sizeof *poly->verts);
+       return 0;
+}
+
+static int choose_poly(struct bsppoly *polyarr, int num_polys)
+{
+       int i, j, best, best_splits;
+
+       if(num_polys <= 1) {
+               return 0;
+       }
+
+       best = -1;
+       best_splits = INT_MAX;
+
+       for(i=0; i<num_polys; i++) {
+               struct cplane *plane = &polyarr[i].plane;
+               int num_splits = 0;
+
+#pragma omp parallel for reduction(+:num_splits)
+               for(j=0; j<num_polys; j++) {
+                       if(i == j) continue;
+
+                       if(check_clip_poly(polyarr[j].verts, polyarr[j].vcount, plane) == 0) {
+                               num_splits++;
+                       }
+               }
+
+               if(num_splits < best_splits) {
+                       best_splits = num_splits;
+                       best = i;
+               }
+       }
+
+       //printf("choose_poly(..., %d) -> %d splits\n", num_polys, best_splits);
+
+       return best;
+}
+
+static struct bspnode *build_tree(struct bsppoly *polyarr, int num_polys)
+{
+       int i, pidx, vnum;
+       struct bsppoly *sp, *tmp;
+       struct bsppoly *front_polys, *back_polys;
+       struct bspnode *node;
+
+       struct bspnode *nres;
+       int clipres, clipres_neg, clipped_vnum, clipped_neg_vnum, max_clipped_vnum = 0;
+       struct g3d_vertex *v, *clipped = 0, *clipped_neg = 0;
+       struct cplane negplane;
+
+       if((pidx = choose_poly(polyarr, num_polys)) == -1) {
+               return 0;
+       }
+       sp = polyarr + pidx;
+
+       negplane.x = sp->plane.x;
+       negplane.y = sp->plane.y;
+       negplane.z = sp->plane.z;
+       negplane.nx = -sp->plane.nx;
+       negplane.ny = -sp->plane.ny;
+       negplane.nz = -sp->plane.nz;
+
+       if(!(front_polys = dynarr_alloc(0, sizeof *front_polys)) ||
+                       !(back_polys = dynarr_alloc(0, sizeof *back_polys))) {
+               fprintf(stderr, "build_tree: failed to allocate front/back polygon arrays\n");
+               dynarr_free(front_polys);
+               return 0;
+       }
+
+       for(i=0; i<num_polys; i++) {
+               if(i == pidx) continue;
+
+               vnum = polyarr[i].vcount;
+
+               if(vnum * 2 > max_clipped_vnum) {
+                       /* resize clipped polygon buffers if necessary */
+                       max_clipped_vnum = vnum * 2;
+                       if(!(v = realloc(clipped, max_clipped_vnum * sizeof *clipped))) {
+                               fprintf(stderr, "build_tree: failed to reallocate clipped polygon buffer\n");
+                               goto fail;
+                       }
+                       clipped = v;
+                       if(!(v = realloc(clipped_neg, max_clipped_vnum * sizeof *clipped))) {
+                               fprintf(stderr, "build_tree: failed to reallocate clipped polygon buffer\n");
+                               goto fail;
+                       }
+                       clipped_neg = v;
+               }
+
+               v = polyarr[i].verts;
+
+               clipres = clip_poly(clipped, &clipped_vnum, v, vnum, &sp->plane);
+               clipres_neg = clip_poly(clipped_neg, &clipped_neg_vnum, v, vnum, &negplane);
+
+               /* detect edge cases where due to floating point imprecision, clipping
+                * by the positive plane clips the polygon, but clipping by the negative
+                * plane doesn't. If that happens, consider the polygon completely on
+                * the side indicated by -clipres_neg
+                */
+               if(clipres == 0 && clipres_neg != 0) {
+                       clipres = -clipres_neg;
+               }
+
+               if(clipres > 0) {
+                       /* polygon completely in the positive subspace */
+                       if(!(tmp = dynarr_push(front_polys, polyarr + i))) {
+                               fprintf(stderr, "build_tree: failed to reallocate polygon array\n");
+                               goto fail;
+                       }
+                       front_polys = tmp;
+
+               } else if(clipres < 0) {
+                       /* polygon completely in the negative subspace */
+                       if(!(tmp = dynarr_push(back_polys, polyarr + i))) {
+                               fprintf(stderr, "build_tree: failed to reallocate polygon array\n");
+                               goto fail;
+                       }
+                       back_polys = tmp;
+
+               } else {
+                       /* polygon is straddling the plane */
+                       struct bsppoly poly;
+                       poly.plane = polyarr[i].plane;
+
+                       if(init_poly_noplane(&poly, clipped, clipped_vnum) == -1) {
+                               goto fail;
+                       }
+                       if(!(tmp = dynarr_push(front_polys, &poly))) {
+                               fprintf(stderr, "build_tree: failed to reallocate polygon array\n");
+                               free(poly.verts);
+                               goto fail;
+                       }
+                       front_polys = tmp;
+
+                       if(init_poly_noplane(&poly, clipped_neg, clipped_neg_vnum) == -1) {
+                               goto fail;
+                       }
+                       if(!(tmp = dynarr_push(back_polys, &poly))) {
+                               fprintf(stderr, "build_tree: failed to reallocate polygon array\n");
+                               free(poly.verts);
+                               goto fail;
+                       }
+                       back_polys = tmp;
+
+                       /* we allocated new sub-polygons, so we need to free the original vertex array */
+                       free(polyarr[i].verts);
+               }
+       }
+
+       if(!(node = malloc(sizeof *node))) {
+               fprintf(stderr, "build_tree: failed to allocate new BSP node\n");
+               goto fail;
+       }
+       node->poly = *sp;
+       node->front = node->back = 0;
+
+       if(dynarr_size(front_polys)) {
+               if(!(node->front = build_tree(front_polys, dynarr_size(front_polys)))) {
+                       goto fail;
+               }
+       }
+       if(dynarr_size(back_polys)) {
+               if(!(node->back = build_tree(back_polys, dynarr_size(back_polys)))) {
+                       goto fail;
+               }
+       }
+
+       free(clipped);
+       free(clipped_neg);
+
+       /* we no longer need the original polygon array */
+       dynarr_free(polyarr);
+
+       return node;
+
+fail:
+       free(clipped);
+       free(clipped_neg);
+
+       for(i=0; i<dynarr_size(front_polys); i++) {
+               free(front_polys[i].verts);
+       }
+       dynarr_free(front_polys);
+
+       for(i=0; i<dynarr_size(back_polys); i++) {
+               free(back_polys[i].verts);
+       }
+       dynarr_free(back_polys);
+
+       return 0;
+}
+
+#undef DRAW_NGONS
+
+#ifndef DRAW_NGONS
+static void debug_draw_poly(struct g3d_vertex *varr, int vcount)
+{
+       int i, nfaces = vcount - 2;
+       int vbuf_size = nfaces * 3;
+       struct g3d_vertex *vbuf = alloca(vbuf_size * sizeof *vbuf);
+       struct g3d_vertex *vptr = varr + 1;
+
+       for(i=0; i<nfaces; i++) {
+               vbuf[i * 3] = varr[0];
+               vbuf[i * 3 + 1] = *vptr++;
+               vbuf[i * 3 + 2] = *vptr;
+       }
+
+       g3d_draw_indexed(G3D_TRIANGLES, vbuf, vbuf_size, 0, 0);
+}
+#endif
+
+static void draw_bsp_tree(struct bspnode *n, const vec3_t *vdir)
+{
+       float dot;
+       struct bsppoly *p;
+
+       if(!n) return;
+
+       p = &n->poly;
+
+       dot = vdir->x * p->plane.nx + vdir->y * p->plane.ny + vdir->z * p->plane.nz;
+       if(dot >= 0.0f) {
+               draw_bsp_tree(n->front, vdir);
+#ifdef DRAW_NGONS
+               g3d_draw_indexed(p->vcount, p->verts, p->vcount, 0, 0);
+#else
+               debug_draw_poly(p->verts, p->vcount);
+#endif
+               draw_bsp_tree(n->back, vdir);
+       } else {
+               draw_bsp_tree(n->back, vdir);
+#ifdef DRAW_NGONS
+               g3d_draw_indexed(p->vcount, p->verts, p->vcount, 0, 0);
+#else
+               debug_draw_poly(p->verts, p->vcount);
+#endif
+               draw_bsp_tree(n->front, vdir);
+       }
+}
+
+static void save_bsp_tree(struct bspnode *n, FILE *fp)
+{
+       /* TODO */
+}
+
+static struct bspnode *load_bsp_tree(FILE *fp)
+{
+       return 0;       /* TODO */
+}
diff --git a/src/bsptree.h b/src/bsptree.h
new file mode 100644 (file)
index 0000000..2839ab0
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef BSPMESH_H_
+#define BSPMESH_H_
+
+#include "mesh.h"
+#include "vmath.h"
+#include "polyclip.h"
+
+struct bsppoly {
+       struct cplane plane;
+       int vcount;
+       struct g3d_vertex *verts;
+};
+
+struct bspnode {
+       struct bsppoly poly;
+       struct bspnode *front, *back;
+};
+
+struct bsptree {
+       struct bspnode *root;
+       struct bsppoly *soup;   /* dynarr: see dynarr.h */
+};
+
+int init_bsp(struct bsptree *bsp);
+void destroy_bsp(struct bsptree *bsp);
+
+int save_bsp(struct bsptree *bsp, const char *fname);
+int load_bsp(struct bsptree *bsp, const char *fname);
+
+int bsp_add_poly(struct bsptree *bsp, struct g3d_vertex *v, int vnum);
+int bsp_add_mesh(struct bsptree *bsp, struct g3d_mesh *m);
+
+int bsp_build(struct bsptree *bsp);
+
+void draw_bsp(struct bsptree *bsp, float view_x, float view_y, float view_z);
+
+#endif /* BSPMESH_H_ */
index 018ed51..0b3747e 100644 (file)
 #include "timer.h"
 #include "gfx.h"
 #include "vmath.h"
-#include "sball.h"
 #include "cfgopt.h"
 #include "logger.h"
 #include "tinyfps.h"
 
 #undef NOKEYB
 
-static int handle_sball_event(sball_event *ev);
-static void recalc_sball_matrix(float *xform);
-
 static int quit;
 static int use_mouse;
 static long fbsize;
 
-static int use_sball;
 static vec3_t pos = {0, 0, 0};
 static quat_t rot = {0, 0, 0, 1};
 
@@ -61,10 +56,6 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       if(opt.sball && sball_init() == 0) {
-               use_sball = 1;
-       }
-
        reset_timer();
 
        while(!quit) {
@@ -83,14 +74,6 @@ int main(int argc, char **argv)
                if(use_mouse) {
                        mouse_bmask = read_mouse(&mouse_x, &mouse_y);
                }
-               if(use_sball && sball_pending()) {
-                       sball_event ev;
-                       printf("got sball event\n");
-                       while(sball_getevent(&ev)) {
-                               handle_sball_event(&ev);
-                       }
-                       recalc_sball_matrix(sball_matrix);
-               }
 
                time_msec = get_msec();
                demo_draw();
@@ -102,9 +85,6 @@ break_evloop:
 #ifndef NOKEYB
        kb_shutdown();
 #endif
-       if(use_sball) {
-               sball_shutdown();
-       }
        return 0;
 }
 
@@ -130,50 +110,3 @@ void swap_buffers(void *pixels)
                }
        }
 }
-
-
-#define TX(ev) ((ev)->motion.motion[0])
-#define TY(ev) ((ev)->motion.motion[1])
-#define TZ(ev) ((ev)->motion.motion[2])
-#define RX(ev) ((ev)->motion.motion[3])
-#define RY(ev) ((ev)->motion.motion[4])
-#define RZ(ev) ((ev)->motion.motion[5])
-
-static int handle_sball_event(sball_event *ev)
-{
-       switch(ev->type) {
-       case SBALL_EV_MOTION:
-               if(RX(ev) | RY(ev) | RZ(ev)) {
-                       float rx = (float)RX(ev);
-                       float ry = (float)RY(ev);
-                       float rz = (float)RZ(ev);
-                       float axis_len = sqrt(rx * rx + ry * ry + rz * rz);
-                       if(axis_len > 0.0) {
-                               rot = quat_rotate(rot, axis_len * 0.001, -rx / axis_len,
-                                               -ry / axis_len, -rz / axis_len);
-                       }
-               }
-
-               pos.x += TX(ev) * 0.001;
-               pos.y += TY(ev) * 0.001;
-               pos.z += TZ(ev) * 0.001;
-               break;
-
-       case SBALL_EV_BUTTON:
-               if(ev->button.pressed) {
-                       pos = v3_cons(0, 0, 0);
-                       rot = quat_cons(1, 0, 0, 0);
-               }
-               break;
-       }
-
-       return 0;
-}
-
-void recalc_sball_matrix(float *xform)
-{
-       quat_to_mat(xform, rot);
-       xform[12] = pos.x;
-       xform[13] = pos.y;
-       xform[14] = pos.z;
-}
diff --git a/src/dos/mouse.asm b/src/dos/mouse.asm
new file mode 100644 (file)
index 0000000..c82e7fd
--- /dev/null
@@ -0,0 +1,193 @@
+; vi:set filetype=nasm:
+; foo_ are watcom functions, _foo are djgpp functions
+
+QUERY equ 0
+SHOW equ 1
+HIDE equ 2
+READ equ 3
+WRITE equ 4
+PIXRATE equ 15
+XLIM equ 7
+YLIM equ 8
+
+PUSHA_EAX_OFFS equ 28
+PUSHA_ECX_OFFS equ 20
+PUSHA_EDX_OFFS equ 16
+
+       section .text
+       bits 32
+
+; int have_mouse(void)
+       global have_mouse_
+       global _have_mouse
+have_mouse_:
+_have_mouse:
+       pusha
+       mov ax, QUERY
+       int 0x33
+       and eax, 0xffff
+       mov [esp + PUSHA_EAX_OFFS], eax
+       popa
+       ret
+
+; void show_mouse(int show)
+       global show_mouse_
+show_mouse_:
+       pusha
+       test ax, ax
+       mov ax, HIDE
+       jz .skip
+       mov ax, SHOW
+.skip: int 0x33
+       popa
+       ret
+
+       global _show_mouse
+_show_mouse:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, [ebp + 8]
+       test ax, ax
+       mov ax, HIDE
+       jz .skip
+       mov ax, SHOW
+.skip: int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; int read_mouse(int *xp, int *yp)
+       global read_mouse_
+read_mouse_:
+       pusha
+       mov esi, eax    ; xp
+       mov edi, edx    ; yp
+       mov ax, READ
+       int 0x33
+       xor eax, eax
+       and ecx, 0xffff
+       and edx, 0xffff
+       mov ax, bx
+       mov [esp + PUSHA_EAX_OFFS], eax
+       mov [esi], ecx
+       mov [edi], edx
+       popa
+       ret
+
+       global _read_mouse
+_read_mouse:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, READ
+       int 0x33
+       xor eax, eax
+       mov ax, bx
+       and ecx, 0xffff
+       mov ebx, [ebp + 8]
+       mov [ebx], ecx
+       and edx, 0xffff
+       mov ebx, [ebp + 12]
+       mov [ebx], edx
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; void set_mouse(int x, int y)
+       global set_mouse_
+set_mouse_:
+       pusha
+       mov cx, ax
+       mov ax, WRITE
+       int 0x33
+       popa
+       ret
+       
+       global _set_mouse
+_set_mouse:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, WRITE
+       mov cx, [ebp + 8]
+       mov dx, [ebp + 12]
+       int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; void set_mouse_limits(int xmin, int ymin, int xmax, int ymax)
+       global set_mouse_limits_
+set_mouse_limits_:
+       pusha
+       mov cx, ax
+       mov dx, bx
+       mov ax, XLIM
+       int 0x33
+       mov ax, YLIM
+       mov cx, [esp + PUSHA_EDX_OFFS]
+       mov dx, [esp + PUSHA_ECX_OFFS]
+       int 0x33
+       popa
+       ret
+
+       global _set_mouse_limits
+_set_mouse_limits:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, XLIM
+       mov cx, [ebp + 8]
+       mov dx, [ebp + 16]
+       int 0x33
+       mov ax, YLIM
+       mov cx, [ebp + 12]
+       mov dx, [ebp + 20]
+       int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; void set_mouse_rate(int xrate, int yrate)
+       global set_mouse_rate_
+set_mouse_rate_:
+       pusha
+       mov cx, ax
+       mov ax, PIXRATE
+       int 0x33
+       popa
+       ret
+
+       global _set_mouse_rate
+_set_mouse_rate:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, PIXRATE
+       mov cx, [esp + 4]
+       mov dx, [esp + 8]
+       int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
diff --git a/src/dos/sball.c b/src/dos/sball.c
deleted file mode 100644 (file)
index 9b9238a..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dos.h>
-#include <conio.h>
-
-#ifdef __WATCOMC__
-#include <i86.h>
-#endif
-
-#ifdef __DJGPP__
-#include <stdint.h>
-#include <dpmi.h>
-#include <go32.h>
-#include <pc.h>
-#endif
-
-#include "sball.h"
-
-struct motion {
-       int x, y, z;
-       int rx, ry, rz;
-};
-
-#define UART1_BASE     0x3f8
-#define UART2_BASE     0x2f8
-#define UART1_IRQ      4
-#define UART2_IRQ      3
-
-#define UART_DATA      0
-#define UART_INTR      1
-#define UART_DIVLO     0
-#define UART_DIVHI     1
-#define UART_FIFO      2
-#define UART_IID       2
-#define UART_LCTL      3
-#define UART_MCTL      4
-#define UART_LSTAT     5
-#define UART_MSTAT     6
-
-/* interrupt enable register bits */
-#define INTR_RECV      1
-#define INTR_SEND      2
-#define INTR_LSTAT     4
-#define INTR_DELTA     8
-
-/* fifo control register bits */
-#define FIFO_ENABLE            0x01
-#define FIFO_RECV_CLEAR        0x02
-#define FIFO_SEND_CLEAR        0x04
-#define FIFO_DMA               0x08
-#define FIFO_TRIG_4            0x40
-#define FIFO_TRIG_8            0x80
-#define FIFO_TRIG_14   0xc0
-
-/* interrupt id register bits */
-#define IID_PENDING            0x01
-#define IID_ID0                        0x02
-#define IID_ID1                        0x04
-#define IID_ID2                        0x08
-#define IID_FIFO_EN            0xc0
-
-#define IID_SOURCE             0xe
-
-#define IID_DELTA              0
-#define IID_SEND               0x2
-#define IID_RECV               0x4
-#define IID_FIFO               0xc
-#define IID_STATUS             0x6
-
-/* line control register bits */
-#define LCTL_BITS_8    0x03
-#define LCTL_STOP_2    0x04
-#define LCTL_DLAB      0x80
-#define LCTL_8N1       LCTL_BITS_8
-#define LCTL_8N2       (LCTL_BITS_8 | LCTL_STOP_2)
-
-/* modem control register bits */
-#define MCTL_DTR       0x01
-#define MCTL_RTS       0x02
-#define MCTL_OUT1      0x04
-#define MCTL_OUT2      0x08
-#define MCTL_LOOP      0x10
-
-/* line status register bits */
-#define LST_DRDY               0x01
-#define LST_ERR_OVER   0x02
-#define LST_ERR_PARITY 0x04
-#define LST_ERR_FRAME  0x08
-#define LST_ERR_BRK            0x10
-#define LST_TREG_EMPTY 0x20
-#define LST_TIDLE              0x40
-#define LST_ERROR              0x80
-
-/* modem status register bits */
-#define MST_DELTA_CTS  0x01
-#define MST_DELTA_DSR  0x02
-#define MST_TERI               0x04
-#define MST_DELTA_DCD  0x08
-#define MST_CTS                        0x10
-#define MST_DSR                        0x20
-#define MST_RING               0x40
-#define MST_DCD                        0x80
-
-/* interrupt controller stuff */
-#define PIC1_CMD_PORT  0x20
-#define PIC1_DATA_PORT 0x21
-#define PIC2_CMD_PORT  0xa0
-#define PIC2_DATA_PORT 0xa1
-#define OCW2_EOI               0x20
-
-struct packet {
-       int id;
-       char data[80];
-};
-
-static int init_smouse(void);
-static void read_motion(int *m, const char *s);
-static void read_keystate(unsigned int *stptr, const char *s);
-static void procpkt(struct packet *p);
-static void enqueue_event(sball_event *ev);
-
-#define COM_FMT_8N1            LCTL_8N1
-#define COM_FMT_8N2            LCTL_8N2
-static void com_setup(int port, int baud, unsigned int fmt);
-static void com_close(void);
-
-static void com_putc(char c);
-static void com_puts(const char *s);
-static int com_getc(void);
-static char *com_gets(char *buf, int sz);
-
-static int com_have_recv(void);
-static int com_can_send(void);
-
-#ifdef __WATCOMC__
-#define INTERRUPT      __interrupt __far
-static void (INTERRUPT *prev_recv_intr)(void);
-#endif
-
-#ifdef __DJGPP__
-#define INTERRUPT
-
-static _go32_dpmi_seginfo intr, prev_intr;
-
-#define outp(port, val)        outportb(port, val)
-#define inp(port) inportb(port)
-#endif
-
-static void INTERRUPT recv_intr(void);
-
-static int uart_base, uart_intr_num;
-
-static struct packet pktbuf[16];
-static int pktbuf_ridx, pktbuf_widx;
-#define BNEXT(x)       (((x) + 1) & 0xf)
-#define BEMPTY(b)      (b##_ridx == b##_widx)
-
-static sball_event evbuf[16];
-static int evbuf_ridx, evbuf_widx;
-
-
-int sball_init(void)
-{
-       com_setup(0, 9600, COM_FMT_8N2);
-       init_smouse();
-       return 0;
-}
-
-void sball_shutdown(void)
-{
-       com_close();
-}
-
-int sball_getdev(void)
-{
-       return 0;
-}
-
-int sball_pending(void)
-{
-       _disable();
-       while(!BEMPTY(pktbuf)) {
-               procpkt(pktbuf + pktbuf_ridx);
-               pktbuf_ridx = BNEXT(pktbuf_ridx);
-       }
-       _enable();
-       return !BEMPTY(evbuf);
-}
-
-int sball_getevent(sball_event *ev)
-{
-       _disable();
-       while(!BEMPTY(pktbuf)) {
-               procpkt(pktbuf + pktbuf_ridx);
-               pktbuf_ridx = BNEXT(pktbuf_ridx);
-       }
-       _enable();
-
-       if(BEMPTY(evbuf)) {
-               return 0;
-       }
-       *ev = evbuf[evbuf_ridx];
-       evbuf_ridx = BNEXT(evbuf_ridx);
-       return 1;
-}
-
-static int init_smouse(void)
-{
-       /* try repeatedly zeroing the device until we get a response */
-       do {
-               delay(500);
-               com_puts("z\r");
-       } while(BEMPTY(pktbuf));
-
-       /* then ask for id string and request motion updates */
-       com_puts("vQ\r");
-       com_puts("m3\r");
-       return 0;
-}
-
-static void procpkt(struct packet *p)
-{
-       static unsigned int bnstate;
-       int i;
-       unsigned int st, delta, prev;
-       sball_event *ev;
-
-       switch(p->id) {
-       case 'd':
-               ev = evbuf + evbuf_widx;
-               read_motion(ev->motion.motion, p->data);
-               ev->type = SBALL_EV_MOTION;
-               enqueue_event(ev);
-               break;
-
-       case 'k':
-               read_keystate(&st, p->data);
-
-               delta = st ^ bnstate;
-               prev = bnstate;
-               bnstate = st;
-
-               for(i=0; i<32; i++) {
-                       if(delta & 1) {
-                               ev = evbuf + evbuf_widx;
-                               ev->type = SBALL_EV_BUTTON;
-                               ev->button.id = i;
-                               ev->button.pressed = st & 1;
-                               ev->button.state = prev ^ (1 << i);
-                               enqueue_event(ev);
-                       }
-                       st >>= 1;
-                       delta >>= 1;
-               }
-               break;
-
-       case 'v':
-               printf("Device: %s\n", p->data);
-               break;
-       /*
-       default:
-               printf("DBG %c -> %s\n", (char)p->id, p->data);
-       */
-       }
-}
-
-static void enqueue_event(sball_event *ev)
-{
-       if(ev != evbuf + evbuf_widx) {
-               evbuf[evbuf_widx] = *ev;
-       }
-
-       evbuf_widx = BNEXT(evbuf_widx);
-       if(evbuf_widx == evbuf_ridx) {
-               fprintf(stderr, "enqueue_event: overflow, dropping oldest\n");
-               evbuf_ridx = BNEXT(evbuf_ridx);
-       }
-}
-
-static void com_setup(int port, int baud, unsigned int fmt)
-{
-       unsigned char ctl;
-       unsigned short div = 115200 / baud;
-       static int base[] = {UART1_BASE, UART2_BASE};
-       static int irq[] = {UART1_IRQ, UART2_IRQ};
-
-       uart_base = base[port];
-       uart_intr_num = irq[port] | 8;
-
-       _disable();
-#ifdef __WATCOMC__
-       prev_recv_intr = _dos_getvect(uart_intr_num);
-       _dos_setvect(uart_intr_num, recv_intr);
-#endif
-#ifdef __DJGPP__
-       _go32_dpmi_get_protected_mode_interrupt_vector(uart_intr_num, &prev_intr);
-       intr.pm_offset = (intptr_t)recv_intr;
-       intr.pm_selector = _go32_my_cs();
-       _go32_dpmi_allocate_iret_wrapper(&intr);
-       _go32_dpmi_set_protected_mode_interrupt_vector(uart_intr_num, &intr);
-#endif
-       /* unmask the appropriate interrupt */
-       outp(PIC1_DATA_PORT, inp(PIC1_DATA_PORT) & ~(1 << irq[port]));
-
-       outp(uart_base + UART_LCTL, LCTL_DLAB);
-       outp(uart_base + UART_DIVLO, div & 0xff);
-       outp(uart_base + UART_DIVHI, (div >> 8) & 0xff);
-       outp(uart_base + UART_LCTL, fmt);       /* fmt should be LCTL_8N1, LCTL_8N2 etc */
-       outp(uart_base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR);
-       outp(uart_base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2);
-       outp(uart_base + UART_INTR, INTR_RECV);
-
-       _enable();
-}
-
-static void com_close(void)
-{
-       _disable();
-       outp(uart_base + UART_INTR, 0);
-       outp(uart_base + UART_MCTL, 0);
-#ifdef __WATCOMC__
-       _dos_setvect(uart_intr_num, prev_recv_intr);
-#endif
-#ifdef __DJGPP__
-       _go32_dpmi_set_protected_mode_interrupt_vector(uart_intr_num, &prev_intr);
-       _go32_dpmi_free_iret_wrapper(&intr);
-#endif
-       _enable();
-}
-
-static void com_putc(char c)
-{
-       while(!com_can_send());
-       while((inp(uart_base + UART_MSTAT) & MST_CTS) == 0);
-       outp(uart_base + UART_DATA, c);
-}
-
-static void com_puts(const char *s)
-{
-       while(*s) {
-               com_putc(*s++);
-       }
-}
-
-static int com_getc(void)
-{
-       int have;
-       while(!(have = com_have_recv()));
-       return inp(uart_base + UART_DATA);
-}
-
-static char *com_gets(char *buf, int sz)
-{
-       int c;
-       char *ptr = buf;
-
-       while(sz-- > 1 && (c = com_getc()) != -1) {
-               if(c == '\r') {
-                       *ptr++ = '\n';
-                       break;
-               }
-               *ptr++ = c;
-       }
-       if(c == -1) {
-               return 0;
-       }
-       *ptr = 0;
-       return buf;
-}
-
-static int com_have_recv(void)
-{
-       unsigned short stat = inp(uart_base + UART_LSTAT);
-       if(stat & LST_ERROR) {
-               fprintf(stderr, "receive error\n");
-               abort();
-       }
-       return stat & LST_DRDY;
-}
-
-static int com_can_send(void)
-{
-       return inp(uart_base + UART_LSTAT) & LST_TREG_EMPTY;
-}
-
-static void INTERRUPT recv_intr()
-{
-       static char buf[128];
-       static char *bptr = buf;
-       struct packet *pkt;
-       int idreg, c, datasz;
-
-       while(((idreg = inp(uart_base + UART_IID)) & IID_PENDING) == 0) {
-               while(com_have_recv()) {
-                       if((c = inp(uart_base + UART_DATA)) == '\r') {
-                               *bptr = 0;
-                               datasz = bptr - buf;
-                               bptr = buf;
-
-                               pkt = pktbuf + pktbuf_widx;
-                               pktbuf_widx = BNEXT(pktbuf_widx);
-
-                               if(pktbuf_widx == pktbuf_ridx) {
-                                       /* we overflowed, drop the oldest packet */
-                                       pktbuf_ridx = BNEXT(pktbuf_ridx);
-                               }
-
-                               if(datasz > sizeof pkt->data) {
-                                       datasz = sizeof pkt->data;      /* truncate */
-                               }
-                               pkt->id = buf[0];
-                               memcpy(pkt->data, buf + 1, datasz);
-
-                       } else if(bptr - buf < sizeof buf - 1) {
-                               *bptr++ = c;
-                       }
-               }
-       }
-
-       outp(PIC1_CMD_PORT, OCW2_EOI);
-}
-
-static void read_motion(int *m, const char *s)
-{
-       int i;
-
-       for(i=0; i<6; i++) {
-               long val = ((((long)s[0] & 0xf) << 12) |
-                       (((long)s[1] & 0xf) << 8) |
-                       (((long)s[2] & 0xf) << 4) |
-                       ((long)s[3] & 0xf)) - 32768;
-               s += 4;
-               *m++ = (int)val;
-       }
-}
-
-static void read_keystate(unsigned int *stptr, const char *s)
-{
-       int i, bit = 0;
-       unsigned int st = 0;
-
-       for(i=0; i<3; i++) {
-               st |= ((unsigned int)*s++ & 0xf) << bit;
-               bit += 4;
-       }
-       *stptr = st;
-}
index 4c5f35e..0f44854 100644 (file)
@@ -9,27 +9,33 @@
 #include "imago2.h"
 #include "gfxutil.h"
 #include "mesh.h"
+#include "bsptree.h"
 
 static int init(void);
 static void destroy(void);
 static void start(long trans_time);
 static void draw(void);
+static void keypress(int key);
 
 static struct screen scr = {
        "rtxonoff",
        init,
        destroy,
        start, 0,
-       draw
+       draw,
+       keypress
 };
 
 static float cam_theta = -29, cam_phi = 35;
-static float cam_dist = 6;
+static float cam_dist = 10;
+
+static int use_bsp = 1;
 
 static const char *car_fname[2] = {"data/ldiablo.obj", 0};
 static const char *cartex_fname[2] = {"data/ldiablo.png", 0};
 static struct g3d_mesh mesh_car[2];
 static struct pimage tex_car[2];
+static struct bsptree bsp_car[2];
 
 struct screen *rtxonoff_screen(void)
 {
@@ -45,6 +51,7 @@ static int init(void)
                        if(!(tex_car[i].pixels = img_load_pixels(cartex_fname[i],
                                                        &tex_car[i].width, &tex_car[i].height, IMG_FMT_RGB24))) {
                                fprintf(stderr, "failed to load car texture: %s\n", cartex_fname[i]);
+                               return -1;
                        }
                        convimg_rgb24_rgb16(tex_car[i].pixels, (unsigned char*)tex_car[i].pixels,
                                        tex_car[i].width, tex_car[i].height);
@@ -53,6 +60,13 @@ static int init(void)
                        if(load_mesh(&mesh_car[i], car_fname[i]) == -1) {
                                return -1;
                        }
+
+                       init_bsp(&bsp_car[i]);
+                       if(bsp_add_mesh(&bsp_car[i], &mesh_car[i]) == -1) {
+                               fprintf(stderr, "failed to construct BSP tree %d\n", i);
+                               return -1;
+                       }
+                       bsp_build(&bsp_car[i]);
                }
        }
        return 0;
@@ -60,6 +74,13 @@ static int init(void)
 
 static void destroy(void)
 {
+       int i;
+
+       for(i=0; i<2; i++) {
+               free(mesh_car[i].varr);
+               free(mesh_car[i].iarr);
+               destroy_bsp(&bsp_car[i]);
+       }
 }
 
 static void start(long trans_time)
@@ -81,6 +102,7 @@ static void update(void)
 static void draw(void)
 {
        int i;
+       static float vdir[3];
        float t = (float)time_msec / 16.0f;
 
        update();
@@ -93,16 +115,38 @@ static void draw(void)
        g3d_rotate(cam_phi, 1, 0, 0);
        g3d_rotate(cam_theta, 0, 1, 0);
 
+       if(use_bsp) {
+               const float *mat = g3d_get_matrix(G3D_MODELVIEW, 0);
+               /* transform (0, 0, -1) with transpose(mat3x3) */
+               vdir[0] = -mat[2];
+               vdir[1] = -mat[6];
+               vdir[2] = -mat[10];
+       }
+
        g3d_polygon_mode(G3D_TEX_GOURAUD);
 
        for(i=0; i<sizeof mesh_car / sizeof mesh_car[0]; i++) {
                if(mesh_car[i].varr) {
                        g3d_set_texture(tex_car[i].width, tex_car[i].height, tex_car[i].pixels);
 
-                       zsort_mesh(&mesh_car[i]);
-                       draw_mesh(&mesh_car[i]);
+                       if(use_bsp) {
+                               draw_bsp(&bsp_car[i], vdir[0], vdir[1], vdir[2]);
+                       } else {
+                               zsort_mesh(&mesh_car[i]);
+                               draw_mesh(&mesh_car[i]);
+                       }
                }
        }
 
        swap_buffers(fb_pixels);
 }
+
+static void keypress(int key)
+{
+       switch(key) {
+       case 'b':
+               use_bsp = !use_bsp;
+               printf("drawing with %s\n", use_bsp ? "BSP tree" : "z-sorting");
+               break;
+       }
+}
index 28e8505..35e6cd6 100644 (file)
@@ -11,6 +11,8 @@ struct ray {
 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);
@@ -43,6 +45,21 @@ int clip_poly(struct g3d_vertex *vout, int *voutnum,
        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)
@@ -163,6 +180,27 @@ static int clip_edge(struct g3d_vertex *poly, int *vnumptr,
        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)
 {
index 0b460cf..adee29d 100644 (file)
@@ -25,6 +25,12 @@ enum {
 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);
index 3165e31..2bb2d47 100644 (file)
@@ -38,7 +38,7 @@ int main(int argc, char **argv)
        }
        vmem_front = vmem_back = fb_pixels;
 
-       SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
+       SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE);
        if(!(fbsurf = SDL_SetVideoMode(xsz, ysz, fb_bpp, sdl_flags))) {
                fprintf(stderr, "failed to set video mode %dx%d %dbpp\n", fb_width, fb_height, fb_bpp);
                free(fb_pixels);