ref: 901503e77b462db789885a3b9424ad00434b0699
author: qwx <[email protected]>
date: Mon May 27 19:44:46 EDT 2019
initial import
--- /dev/null
+++ b/bsp.c
@@ -1,0 +1,354 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+/* a grid 8 is used for maps; points are considered on a line if they are
+ * within 8px of it; this accounts for floating error */
+
+typedef struct Bsp Bsp;
+typedef struct Divline Divline;
+typedef struct Node Node;
+
+enum{
+ Maxv = 0x7fffffff,
+};
+
+struct Divline{
+ Point p;
+ float dx;
+ float dy;
+};
+struct Bsp{
+ Seg *s;
+ Divline;
+ Bsp *side[2];
+};
+Seg *segs;
+
+struct Node{
+ short x;
+ short y;
+ short dx;
+ short dy;
+ short bound[2][4];
+ ushort child[2];
+};
+Node *nodes;
+int nnodes;
+
+static float bbox[4];
+
+void *
+erealloc(void *p, ulong n)
+{
+ if((p = realloc(p, n)) == nil)
+ sysfatal("realloc: %r");
+ return p;
+}
+
+int
+subsect(Seg *s)
+{
+ USED(s);
+ return 0;
+}
+
+int
+mknodes(Bsp *b, short box[4])
+{
+ int i, f;
+ short sbox[2][4];
+
+ if(b->s != nil){
+ f = subsect(b->s);
+ for(i=0; i<4; i++)
+ box[i] = bbox[i];
+ return f | 1<<15;
+ }
+ /* ... */
+ return 0;
+}
+
+static int
+fsign(float f)
+{
+ return f < 0. ? -1 : f > 0.;
+}
+
+static float
+fround(float x)
+{
+ if(x > 0.){
+ if(x - (int)x < 0.1)
+ return (int)x;
+ else if(x - (int)x > 0.9)
+ return (int)x + 1;
+ else
+ return x;
+ }
+ if((int)x - x < 0.1)
+ return (int)x;
+ else if((int)x - x > 0.9)
+ return (int)x - 1;
+ return x;
+}
+
+static void
+divline(Divline *d, Seg *s)
+{
+ d->p = s->p1;
+ d->dx = s->p2.x - s->p1.x;
+ d->dy = s->p2.y - s->p1.y;
+}
+
+/* fractional intercept point along first vector */
+static float
+InterceptVector(Divline *v2, Divline *v1)
+{
+ float frac, num, den;
+
+ den = v1->dy * v2->dx - v1->dx * v2->dy;
+ if(den == 0.)
+ sysfatal("InterceptVector: parallel");
+ num = (v1->p.x - v2->p.x) * v1->dy + (v2->p.y - v1->p.y) * v1->dx;
+ frac = num / den;
+ if(frac <= 0. || frac >= 1.)
+ sysfatal("InterceptVector: intersection outside line");
+ return frac;
+}
+
+static int
+ptside(Point *p, Divline *l)
+{
+ float dx, dy, left, right, a, b, c, d;
+
+ if(l->dx == 0){
+ if (p->x > l->p.x - 2 && p->x < l->p.x + 2)
+ return -1;
+ if (p->x < l->p.x)
+ return l->dy > 0;
+ return l->dy < 0;
+ }
+ if(l->dy == 0){
+ if(p->y > l->p.y - 2 && p->y < l->p.y + 2)
+ return -1;
+ if(p->y < l->p.y)
+ return l->dx < 0;
+ return l->dx > 0;
+ }
+ dx = l->p.x - p->x;
+ dy = l->p.y - p->y;
+ a = l->dx * l->dx + l->dy * l->dy;
+ b = 2 * (l->dx*dx + l->dy * dy);
+ c = dx * dx + dy * dy - 2 * 2; /* 2 unit radius */
+ d = b * b - 4 * a * c;
+ if(d > 0)
+ return -1; /* colinear: within 4px of line */
+ dx = p->x - l->p.x;
+ dy = p->y - l->p.y;
+ left = l->dy * dx;
+ right = dy * l->dx;
+ if(abs(left - right) < 0.5) /* allow slope on line */
+ return -1;
+ if(right < left)
+ return 0; /* front */
+ return 1; /* back */
+}
+
+/* if the line is colinear, it will be placed on the front side if going
+ * the same direction as the dividing line */
+static int
+lineside(Seg *wl, Divline *bl)
+{
+ int s1, s2;
+ float dx, dy;
+
+ s1 = ptside(&wl->p1, bl);
+ s2 = ptside(&wl->p2, bl);
+ if(s1 == s2){ /* colinear: see if the directions are the same */
+ if(s1 == -1){
+ dx = wl->p2.x - wl->p1.x;
+ dy = wl->p2.y - wl->p1.y;
+ if(fsign(dx) == fsign(bl->dx)
+ && fsign(dy) == fsign(bl->dy))
+ return 0;
+ return 1;
+ }
+ return s1;
+ }
+ if(s1 == -1)
+ return s2;
+ if(s2 == -1)
+ return s1;
+ return -2; /* line must be split */
+}
+
+/* truncate given world line to the front side of the divline and return
+ * the cut off back side in a newly allocated world line */
+static Seg *
+cutline(Seg *s, Divline *bl)
+{
+ int side, ofs;
+ float f;
+ Point intr;
+ Seg *p;
+ Divline d;
+
+ divline(&d, s);
+ p = emalloc(sizeof *p);
+ *p = *s;
+ f = InterceptVector(&d, bl);
+ intr.x = d.p.x + fround(d.dx * f);
+ intr.y = d.p.y + fround(d.dy * f);
+ ofs = s->ofs + fround(f * sqrt(d.dx * d.dx + d.dy * d.dy));
+ side = ptside(&s->p1, bl);
+ if(side == 0){ /* line starts on front side */
+ s->p2 = intr;
+ p->p1 = intr;
+ p->ofs = ofs;
+ }else{ /* line starts on back side */
+ s->p1 = intr;
+ s->ofs = ofs;
+ p->p2 = intr;
+ }
+ return p;
+}
+
+static void
+addnode(Seg **ss, int *n, Seg *s, Seg *p)
+{
+ if(s - *ss >= *n){
+ *n += 64;
+ *ss = erealloc(*ss, *n * sizeof **ss);
+ }
+ *s = *p;
+}
+
+/* actually split line list as EvaluateLines predicted */
+static void
+split(Seg *ss, Seg *s, Seg **front, Seg **back)
+{
+ int side, be, fe;
+ Seg *p, *q, *b, *f;
+ Divline d;
+
+ fe = be = ss->e - s;
+ f = *front = emalloc(fe * sizeof **front);
+ b = *back = emalloc(be * sizeof **back);
+ for(p=ss; p<ss->e; p++){
+ side = p == s ? 0 : lineside(p, &d);
+ switch(side){
+ case 0: addnode(front, &fe, f++, p); break;
+ case 1: addnode(back, &be, b++, p); break;
+ case -2:
+ q = cutline(p, &d);
+ addnode(front, &fe, f++, p);
+ addnode(back, &be, b++, q);
+ break;
+ }
+ }
+ (*front)->e = f;
+ (*back)->e = b;
+}
+
+/* grade quality of a split along given line for the current list of lines.
+ * evaluation is halted as soon as it is determined that a better split
+ * already exists. a split is good if it divides the lines evenly without
+ * cutting many lines. a horizontal or vertical split is better than a sloped
+ * split. the lower the returned value, the better. if the split line does
+ * not divide any of the lines at all, Maxv is returned */
+static int
+evalsplit(Seg *ss, Seg *s, int bestv)
+{
+ int side, s1, s2, v;
+ Divline d;
+ Seg *p;
+
+ divline(&d, s);
+ for(p=ss, v=s1=s2=0; p<ss->e; p++){
+ side = p == s ? 0 : lineside(p, &d);
+ switch(side){
+ case -2: s2++; /* wet floor */
+ case 0: s1++; break;
+ case 1: s2++; break;
+ }
+ v = (s1 > s2 ? s1 : s2) + (s1 + s2 - (p - ss)) * 8;
+ if(v > bestv)
+ return v;
+ }
+ if(s1 == 0 || s2 == 0)
+ return Maxv;
+ return v;
+}
+
+/* recursively partition seg list */
+static Bsp *
+bsp(Seg *ss)
+{
+ int v, bestv;
+ Bsp *b;
+ Seg *s, *bests, *front, *back;
+
+ b = emalloc(sizeof *b);
+ /* find the best line to partition on */
+ for(s=ss, bestv=Maxv, bests=nil; s<ss->e; s++){
+ v = evalsplit(ss, s, bestv);
+ if(v < bestv){
+ bestv = v;
+ bests = s;
+ }
+ }
+ /* if none of the lines should be split, the remaining lines
+ * are convex, and form a terminal node */
+ if(bestv == Maxv){
+ b->s = ss;
+ return b;
+ }
+ /* divide the line list into two nodes along the best split line */
+ divline(b, bests);
+ split(ss, bests, &front, &back);
+ /* recursively divide the lists */
+ b->side[0] = bsp(front);
+ b->side[1] = bsp(back);
+ return b;
+}
+
+static void
+mkseg(void)
+{
+ Line *l;
+ Seg *s;
+
+ /* FIXME: free all nodes and segs first */
+ segs = emalloc(nsides * sizeof *segs);
+ for(s=segs, l=lines; l<lines+nlines; s++, l++){
+ s->p1 = *l->v;
+ s->p2 = *l->w;
+ s->l = l;
+ s->s = l->r;
+ if(l->f & LFtwosd){
+ s++;
+ s->p1 = *l->v;
+ s->p2 = *l->w;
+ s->l = l;
+ s->s = l->l;
+ }
+ }
+ segs->e = s;
+}
+
+void
+buildnodes(void)
+{
+ Bsp *b;
+ short bounds[4];
+
+ mkseg();
+ b = bsp(segs);
+ nnodes = nsides;
+ nodes = emalloc(nnodes * sizeof *nodes);
+ mknodes(b, bounds);
+ /* FIXME: free everything */
+}
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,104 @@
+typedef struct Thing Thing;
+typedef struct Line Line;
+typedef struct Side Side;
+typedef struct Vertex Vertex;
+typedef struct Sector Sector;
+typedef struct Seg Seg;
+
+enum{
+ CFline = 1<<23,
+ CFthing = 1<<22,
+ CFvert = 1<<21,
+};
+
+struct Vertex{
+ int i;
+ Point;
+ uchar sel[4];
+};
+extern Vertex *verts;
+extern int nverts;
+
+struct Sector{
+ int i;
+ Point; /* floor z, ceiling z */
+ char fflat[8+1];
+ char cflat[8+1];
+ short lum;
+ short type;
+ short tag;
+};
+extern Sector *sects;
+extern int nsects;
+
+struct Side{
+ int i;
+ Point; /* Δ */
+ char high[8+1];
+ char low[8+1];
+ char mid[8+1];
+ int sid;
+ Sector *s;
+};
+extern Side *sides;
+extern int nsides;
+
+enum{
+ LFimpass = 1<<0,
+ LFmblock = 1<<1,
+ LFtwosd = 1<<2,
+ LFuunpeg = 1<<3,
+ LFdunpeg = 1<<4,
+ LFsecret = 1<<5,
+ LFsblock = 1<<6,
+ LFnodraw = 1<<7,
+ LFmapped = 1<<8
+};
+struct Line{
+ int i;
+ int vid;
+ int wid;
+ Vertex *v;
+ Vertex *w;
+ int rid;
+ int lid;
+ Side *r; /* FIXME: front/back, not right/left? */
+ Side *l;
+ short type;
+ short f;
+ short tag;
+ uchar sel[4];
+};
+extern Line *lines;
+extern int nlines;
+
+enum{
+ TFbaby = 1<<0,
+ TFeasy = TFbaby,
+ TFmed = 1<<1,
+ TFhard = 1<<2,
+ TFnite = TFhard,
+ TFdeaf = 1<<3,
+ TFmponly = 1<<4
+};
+struct Thing{
+ int i;
+ Point;
+ short an;
+ short type;
+ short f;
+ uchar sel[4];
+};
+extern Thing *things;
+extern int nthings;
+
+struct Seg{
+ Point p1;
+ Point p2;
+ Line *l;
+ Side *s;
+ int ofs;
+ int grouped;
+ Seg *e;
+};
+extern Seg *segs;
--- /dev/null
+++ b/dmap.c
@@ -1,0 +1,405 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include "dat.h"
+#include "fns.h"
+
+/* FIXME: inserting/modifying vertices/things: snap to grid is essential */
+/* FIXME: need a node builder to test shit */
+
+enum{
+ Cbg,
+ Cwall,
+ Cline,
+ Cthing,
+ Cvert,
+ Cgrid,
+ Ctext,
+ Cend,
+
+ Nview = 1000,
+ Nzoom = 300,
+};
+
+static int viewdiv = Nzoom;
+static int grunit = 128;
+static int skipms, mode, join;
+static Image *col[Cend], *fb, *fbsel, *fbselc;
+static Point center, view;
+static Keyboardctl *kc;
+static Mousectl *mc;
+
+void *
+emalloc(ulong n)
+{
+ void *p;
+
+ if((p = mallocz(n, 1)) == nil)
+ sysfatal("mallocz: %r");
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+Image *
+eallocimage(Rectangle r, ulong chan, int repl, ulong c)
+{
+ Image *i;
+
+ if((i = allocimage(display, r, chan, repl, c)) == nil)
+ sysfatal("allocimage: %r");
+ return i;
+}
+
+Point
+viewpt(Point p)
+{
+ double f;
+
+ f = (double)viewdiv / Nview;
+ p.x = view.x + p.x * f;
+ p.y = view.y + p.y * f;
+ return p;
+}
+
+Point
+abspt(Point p)
+{
+ double f;
+
+ f = (double)viewdiv / Nview;
+ p.x = (p.x - view.x) / f;
+ p.y = (p.y - view.y) / f;
+ return p;
+}
+
+void
+drawgrid(void)
+{
+ Point p, v;
+
+ p = Pt(0, 0);
+ for(;;){
+ v = viewpt(p);
+ if(v.x >= fb->r.max.x && v.y >= fb->r.max.y)
+ break;
+ line(fb, Pt(0, v.y), Pt(Dx(fb->r), v.y), 0, 0, 0, col[Cgrid], ZP);
+ line(fb, Pt(v.x, 0), Pt(v.x, Dy(fb->r)), 0, 0, 0, col[Cgrid], ZP);
+ p.x += grunit;
+ p.y += grunit;
+ }
+ p = Pt(0 - grunit, 0 - grunit);
+ for(;;){
+ v = viewpt(p);
+ if(v.x <= fb->r.min.x && v.y <= fb->r.min.y)
+ break;
+ line(fb, Pt(0, v.y), Pt(Dx(fb->r), v.y), 0, 0, 0, col[Cgrid], ZP);
+ line(fb, Pt(v.x, 0), Pt(v.x, Dy(fb->r)), 0, 0, 0, col[Cgrid], ZP);
+ p.x -= grunit;
+ p.y -= grunit;
+ }
+}
+
+void
+doline(Point m1, Point m2, int type, uchar *sel)
+{
+ int et;
+ Image *c;
+
+ c = col[type];
+ et = type == Cthing && mode == 2 || type != Cthing && mode == 1 ? Endarrow : 0;
+ line(fb, m1, m2, 0, et, 0, c, ZP);
+ loadimage(fbselc, fbselc->r, sel, 4);
+ line(fbsel, m1, m2, 0, et, 0, fbselc, ZP);
+}
+
+void
+doellipse(Point p, int r, int type, uchar *sel)
+{
+ fillellipse(fb, p, r, r, col[type], ZP);
+ loadimage(fbselc, fbselc->r, sel, 4);
+ fillellipse(fbsel, p, r, r, fbselc, ZP);
+}
+
+void
+drawlines(void)
+{
+ Line *l;
+ Point m1, m2;
+
+ for(l=lines; l<lines+nlines; l++){
+ m1 = viewpt(*l->v);
+ m2 = viewpt(*l->w);
+ if(!ptinrect(m1, fb->r) && !ptinrect(m2, fb->r)
+ && !((m1.x >= 0 && m1.x < Dx(fb->r) && (m1.y < 0 || m1.y >= Dy(fb->r)))
+ || (m2.x >= 0 && m2.x < Dx(fb->r) && (m2.y < 0 || m2.y >= Dy(fb->r)))))
+ continue;
+ doline(m1, m2, l->f & LFimpass ? Cwall : Cline, l->sel);
+ }
+
+}
+
+void
+drawvertices(void)
+{
+ Vertex *v;
+ Point p;
+ int r;
+
+ r = mode==0 ? 2 : 1;
+ for(v=verts; v<verts+nverts; v++){
+ p = viewpt(*v);
+ if(!ptinrect(p, fb->r))
+ continue;
+ doellipse(p, r, Cvert, v->sel);
+ }
+}
+
+void
+drawthings(void)
+{
+ Thing *t;
+ Point m1, m2;
+
+ for(t=things; t<things+nthings; t++){
+ m1 = viewpt(*t);
+ if(!ptinrect(m1, fb->r))
+ continue;
+ if(mode != 2)
+ doellipse(m1, 1, Cthing, t->sel);
+ else{
+ m2 = t->an == 0 ? (Point){m1.x+1, m1.y}
+ : t->an == 45 ? (Point){m1.x+1, m1.y-1}
+ : t->an == 90 ? (Point){m1.x, m1.y-1}
+ : t->an == 135 ? (Point){m1.x-1, m1.y-1}
+ : t->an == 180 ? (Point){m1.x-1, m1.y}
+ : t->an == 225 ? (Point){m1.x-1, m1.y+1}
+ : t->an == 270 ? (Point){m1.x, m1.y+1}
+ : t->an == 315 ? (Point){m1.x+1, m1.y+1}
+ : m1;
+ doline(m1, m2, Cthing, t->sel);
+ }
+ }
+}
+
+void
+redraw(void)
+{
+ draw(fb, fb->r, col[Cbg], nil, ZP);
+ draw(fbsel, fbsel->r, col[Cbg], nil, ZP);
+ drawgrid();
+ drawlines();
+ drawvertices();
+ drawthings();
+ string(fb, Pt(0,0), col[Ctext], Pt(0,0), font, join ? "join" : "split");
+ draw(screen, screen->r, fb, nil, ZP);
+ flushimage(display, 1);
+}
+
+void
+drawstats(Point p)
+{
+ char buf[128];
+ uchar sel[4];
+ u32int x;
+ Line *l;
+ Thing *t;
+ Side *d;
+ Sector *s;
+ Vertex *v;
+
+ unloadimage(fbsel, rectaddpt(fbselc->r, p), sel, sizeof sel);
+ x = sel[3] << 24 | sel[2] << 16 | sel[1] << 8 | sel[0];
+ draw(screen, screen->r, fb, nil, ZP);
+ p = abspt(p);
+ sprint(buf, "%d,%d", p.x & ~(grunit-1), p.y & ~(grunit-1));
+ p = Pt(screen->r.max.x - strlen(buf) * font->width,
+ screen->r.max.y - font->height);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ if(x == 0)
+ goto end;
+ p = Pt(screen->r.min.x, screen->r.max.y - font->height * 5);
+ switch(x & 7<<21){
+ case CFline:
+ l = lines + (x & 0xffff);
+ sprint(buf, "(%d,%d %d,%d len N) type %d tag %d",
+ l->v->x, l->v->y, l->w->x, l->w->y, l->type, l->tag);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ p.y += font->height;
+ d = l->r;
+ sprint(buf, "front: %-8s %-8s %-8s ofs %d,%d",
+ d->high, d->mid, d->low, d->x, d->y);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ p.y += font->height;
+ s = d->s;
+ sprint(buf, "floor %d %-8s ceiling %d %-8s (height %d) light %d type %d tag %d",
+ s->x, s->fflat, s->y, s->cflat, s->y - s->x,
+ s->lum, s->type, s->tag);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ p.y += font->height;
+ if((l->f & LFtwosd) == 0)
+ goto end;
+ d = l->l;
+ sprint(buf, "back: %-8s %-8s %-8s ofs %d,%d",
+ d->high, d->mid, d->low, d->x, d->y);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ p.y += font->height;
+ s = d->s;
+ sprint(buf, "floor %d %-8s ceiling %d %-8s (height %d) light %d type %d tag %d",
+ s->x, s->fflat, s->y, s->cflat, s->y - s->x,
+ s->lum, s->type, s->tag);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ break;
+ case CFthing:
+ t = things + (x & 0xffff);
+ p.y += font->height * 4;
+ sprint(buf, "%d,%d ∠ %d type %d flags %#08ux",
+ t->x, t->y, t->an, t->type, t->f);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ break;
+ case CFvert:
+ v = verts + (x & 0xffff);
+ p.y += font->height * 4;
+ sprint(buf, "%d,%d", v->x, v->y);
+ string(screen, p, col[Ctext], Pt(0,0), font, buf);
+ break;
+ }
+end:
+ flushimage(display, 1);
+}
+
+void
+initfb(int v)
+{
+ Rectangle r;
+
+ center = divpt(subpt(screen->r.max, screen->r.min), 2);
+ if(v)
+ view = center;
+ r = rectsubpt(screen->r, screen->r.min);
+ freeimage(fb);
+ fb = eallocimage(r, screen->chan, 0, DNofill);
+ freeimage(fbsel);
+ fbsel = eallocimage(r, XRGB32, 0, DNofill);
+ redraw();
+ skipms++;
+}
+
+void
+usage(void)
+{
+ fprint(2, "%s [mapdir]\n", argv0);
+ threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ Mouse m;
+ Point mo;
+ double f, vx, vy;
+ Rune r;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+ if(initdraw(nil, nil, "dmap") < 0)
+ sysfatal("initdraw: %r");
+ load(*argv);
+ if((kc = initkeyboard(nil)) == nil)
+ sysfatal("initkeyboard: %r");
+ if((mc = initmouse(nil, screen)) == nil)
+ sysfatal("initmouse: %r");
+ col[Cbg] = display->black,
+ col[Cwall] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0xccccccff);
+ col[Cline] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x5f5f5fff);
+ col[Cthing] = eallocimage(Rect(0,0,1,1), screen->chan, 1, DGreen);
+ col[Cvert] = eallocimage(Rect(0,0,1,1), screen->chan, 1, DGreyblue);
+ col[Cgrid] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x101010ff);
+ col[Ctext] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x884400ff);
+ fbselc = eallocimage(Rect(0,0,1,1), XRGB32, 1, DNofill);
+ initfb(1);
+ vx = view.x - center.x;
+ vy = view.y - center.y;
+
+ Alt a[] = {
+ {mc->resizec, nil, CHANRCV},
+ {mc->c, &m, CHANRCV},
+ {kc->c, &r, CHANRCV},
+ {nil, nil, CHANEND}
+ };
+ for(;;){
+ switch(alt(a)){
+ case 0:
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("getwindow: %r");
+ initfb(0);
+ break;
+ case 1:
+ if(skipms){
+ skipms = 0;
+ goto skip;
+ }
+ if(m.buttons & 2){
+ f = (double)viewdiv / Nview;
+ viewdiv += (m.xy.y - mo.y) / 2;
+ if(viewdiv > 10 * Nview)
+ viewdiv = 10 * Nview;
+ else if(viewdiv < 1)
+ viewdiv = 1;
+ f = ((double)viewdiv / Nview) / f;
+ vx *= f;
+ vy *= f;
+ view = addpt(center, Pt(vx, vy));
+ redraw();
+ }
+ if(m.buttons & 4){
+ mo = subpt(m.xy, mo);
+ view = addpt(view, mo);
+ vx = view.x - center.x;
+ vy = view.y - center.y;
+ redraw();
+ }
+ if(ptinrect(m.xy, screen->r))
+ drawstats(subpt(m.xy, screen->r.min));
+ skip:
+ mo = m.xy;
+ break;
+ case 2:
+ switch(r){
+ paint: redraw(); break;
+ case Kdel:
+ case 'q': threadexitsall(nil);
+ case 'a': mode = (mode + 1) % 3; goto paint;
+ case 'j': join ^= 1; goto paint;
+ case 'r':
+ view = center;
+ vx = view.x - center.x;
+ vy = view.y - center.y;
+ goto paint;
+ case '+':
+ case '=':
+ if(grunit < 1024){
+ grunit <<= 1;
+ goto paint;
+ }
+ break;
+ case '-':
+ if(grunit > 1){
+ grunit >>= 1;
+ goto paint;
+ }
+ break;
+ case 'z':
+ viewdiv = Nzoom;
+ goto paint;
+ }
+ break;
+ default:
+ threadexitsall("alt: %r");
+ }
+ }
+}
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,3 @@
+void buildnodes(void);
+void load(char*);
+void* emalloc(ulong);
--- /dev/null
+++ b/fs.c
@@ -1,0 +1,238 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+enum{
+ Vertsz = 4,
+ Sectsz = 26,
+ Sidesz = 30,
+ Linesz = 14,
+ Thingsz = 10
+};
+
+Vertex *verts;
+int nverts;
+Sector *sects;
+int nsects;
+Side *sides;
+int nsides;
+Line *lines;
+int nlines;
+Thing *things;
+int nthings;
+
+#define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+
+static vlong
+filelen(int fd)
+{
+ vlong l;
+ Dir *d;
+
+ d = dirfstat(fd);
+ if(d == nil)
+ sysfatal("filelen: %r");
+ l = d->length;
+ free(d);
+ return l;
+}
+
+static void
+loadthings(char *f)
+{
+ int n;
+ vlong l;
+ char *s;
+ uchar u[Thingsz];
+ Biobuf *bf;
+ Thing *p;
+ static u32int sel = CFthing;
+
+ s = smprint("%s/things", f);
+ bf = Bopen(s, OREAD);
+ if(bf == nil)
+ sysfatal("loadthings: %r");
+ free(s);
+ l = filelen(Bfildes(bf));
+ if(l % sizeof u != 0)
+ sysfatal("invalid THINGS lump");
+ n = l / sizeof u;
+ things = emalloc(n * sizeof *things);
+ Blethal(bf, nil);
+ for(p=things; Bread(bf, u, sizeof u) == sizeof u; p++, sel++){
+ if(nthings++ == n)
+ sysfatal("loadthings: overflow\n");
+ p->i = nthings;
+ p->x = (s16int)(u[0] | u[1]<<8);
+ p->y = -(s16int)(u[2] | u[3]<<8);
+ p->an = (s16int)(u[4] | u[5]<<8);
+ p->type = (s16int)(u[6] | u[7]<<8);
+ p->f = (s16int)(u[7] | u[8]<<8);
+ PBIT32(p->sel, sel);
+ }
+ Bterm(bf);
+}
+
+static void
+loadlines(char *f)
+{
+ int n;
+ vlong l;
+ char *s;
+ uchar u[Linesz];
+ Biobuf *bf;
+ Line *p;
+ static u32int sel = CFline;
+
+ s = smprint("%s/linedefs", f);
+ bf = Bopen(s, OREAD);
+ if(bf == nil)
+ sysfatal("loadlines: %r");
+ free(s);
+ l = filelen(Bfildes(bf));
+ if(l % sizeof u != 0)
+ sysfatal("invalid LINEDEFS lump");
+ n = l / sizeof u;
+ lines = emalloc(n * sizeof *lines);
+ Blethal(bf, nil);
+ for(p=lines; Bread(bf, u, sizeof u) == sizeof u; p++, sel++){
+ if(nlines++ == n)
+ sysfatal("loadsides: overflow\n");
+ p->i = nlines;
+ /* FIXME: are these supposed to be sign extended? */
+ p->vid = (s16int)(u[0] | u[1]<<8);
+ p->v = verts + p->vid;
+ p->wid = (s16int)(u[2] | u[3]<<8);
+ p->w = verts + p->wid;
+ p->f = (s16int)(u[4] | u[5]<<8);
+ p->type = (s16int)(u[6] | u[7]<<8);
+ p->tag = (s16int)(u[8] | u[9]<<8);
+ /* FIXME: if == (s16int)-1, dont set pointer */
+ p->rid = (s16int)(u[10] | u[11]<<8);
+ p->r = sides + p->rid;
+ p->lid = (s16int)(u[12] | u[13]<<8);
+ p->l = sides + p->lid;
+ PBIT32(p->sel, sel);
+ }
+ Bterm(bf);
+}
+
+static void
+loadsides(char *f)
+{
+ int n;
+ vlong l;
+ char *s;
+ uchar u[Sidesz];
+ Biobuf *bf;
+ Side *p;
+
+ s = smprint("%s/sidedefs", f);
+ bf = Bopen(s, OREAD);
+ if(bf == nil)
+ sysfatal("loadsides: %r");
+ free(s);
+ l = filelen(Bfildes(bf));
+ if(l % sizeof u != 0)
+ sysfatal("invalid SIDEDEFS lump");
+ n = l / sizeof u;
+ sides = emalloc(n * sizeof *sides);
+ Blethal(bf, nil);
+ for(p=sides; Bread(bf, u, sizeof u) == sizeof u; p++){
+ if(nsides++ == n)
+ sysfatal("loadsides: overflow\n");
+ p->i = nsides;
+ p->x = (s16int)(u[0] | u[1]<<8);
+ p->y = -(s16int)(u[2] | u[3]<<8);
+ memcpy(p->high, u+4, 8);
+ memcpy(p->low, u+12, 8);
+ memcpy(p->mid, u+20, 8);
+ p->sid = (s16int)(u[28] | u[29]<<8);
+ p->s = sects + p->sid;
+ }
+ Bterm(bf);
+}
+
+static void
+loadsects(char *f)
+{
+ int n;
+ vlong l;
+ char *s;
+ uchar u[Sectsz];
+ Biobuf *bf;
+ Sector *p;
+
+ s = smprint("%s/sectors", f);
+ bf = Bopen(s, OREAD);
+ if(bf == nil)
+ sysfatal("loadsects: %r");
+ free(s);
+ l = filelen(Bfildes(bf));
+ if(l % sizeof u != 0)
+ sysfatal("invalid SECTORS lump");
+ n = l / sizeof u;
+ sects = emalloc(n * sizeof *sects);
+ Blethal(bf, nil);
+ for(p=sects; Bread(bf, u, sizeof u) == sizeof u; p++){
+ if(nsects++ == n)
+ sysfatal("loadsects: overflow\n");
+ p->i = nsects;
+ p->y = (s16int)(u[0] | u[1]<<8);
+ p->x = -(s16int)(u[2] | u[3]<<8);
+ memcpy(p->fflat, u+4, 8);
+ memcpy(p->cflat, u+12, 8);
+ p->lum = (s16int)(u[20] | u[21]<<8);
+ p->type = (s16int)(u[22] | u[23]<<8);
+ p->tag = (s16int)(u[24] | u[25]<<8);
+ }
+ Bterm(bf);
+}
+
+static void
+loadverts(char *f)
+{
+ int n;
+ vlong l;
+ char *s;
+ uchar u[Vertsz];
+ Biobuf *bf;
+ Vertex *p;
+ static u32int sel = CFvert;
+
+ s = smprint("%s/vertexes", f);
+ bf = Bopen(s, OREAD);
+ if(bf == nil)
+ sysfatal("loadverts: %r");
+ free(s);
+ l = filelen(Bfildes(bf));
+ if(l % sizeof u != 0)
+ sysfatal("invalid VERTEXES lump");
+ n = l / sizeof u;
+ verts = emalloc(n * sizeof *verts);
+ Blethal(bf, nil);
+ for(p=verts; Bread(bf, u, sizeof u) == sizeof u; p++, sel++){
+ if(nverts++ == n)
+ sysfatal("loadverts: overflow\n");
+ p->i = nverts;
+ p->x = (s16int)(u[0] | u[1]<<8);
+ p->y = -(s16int)(u[2] | u[3]<<8);
+ PBIT32(p->sel, sel);
+ }
+ Bterm(bf);
+}
+
+void
+load(char *f)
+{
+ if(f == nil)
+ f = ".";
+ loadverts(f);
+ loadsects(f);
+ loadsides(f);
+ loadlines(f);
+ loadthings(f);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,12 @@
+</$objtype/mkfile
+
+BIN=$home/bin/$objtype
+TARG=dmap
+OFILES=\
+ bsp.$O\
+ dmap.$O\
+ fs.$O\
+
+HFILES=dat.h fns.h
+
+</sys/src/cmd/mkone