#include #include #include #include #include #include #include Mousectl *mc; Channel *tc; static void timerproc(void*) { for(;;){ sleep(10); send(tc, nil); } } typedef struct Kbdstate Kbdstate; struct Kbdstate { char f; char b; char sl; char sr; char rl; char rr; }; Channel *kc; static void kbdproc(void*) { char buf[128], *s; int kfd, n; Rune r; Kbdstate kbd; if((kfd = open("/dev/kbd", OREAD)) < 0) sysfatal("can't open /dev/kbd: %r"); buf[0] = 0; memset(&kbd, 0, sizeof(Kbdstate)); for(;;){ if(buf[0] != 0){ n = strlen(buf)+1; memmove(buf, buf+n, sizeof(buf)-n); } if(buf[0] == 0){ n = read(kfd, buf, sizeof(buf)-1); if(n <= 0) break; //buf[n-1] = 0; buf[n] = 0; } switch(*buf){ case 'c': s = buf+1; while(*s){ if(*s == 'x') threadexitsall(nil); s++; } break; case 'K': memset(&kbd, 0, sizeof(Kbdstate)); case 'k': s = buf+1; while(*s){ s += chartorune(&r, s); switch(r){ case 'w': kbd.f = 1; break; case 'a': kbd.sl = 1; break; case 'd': kbd.sr = 1; break; case 's': kbd.b = 1; break; case 'q': kbd.rl = 1; break; case 'e': kbd.rr = 1; break; } } send(kc, &kbd); break; } } } Space *worldmat; enum { Black, Grey, White, Red, Green, Blue, Numcolours }; Image *colours[Numcolours]; static void initcolours(void) { Rectangle r = Rect(0, 0, 1, 1); colours[Black] = allocimage(display, r, screen->chan, 1, 0x000000ff); colours[Grey] = allocimage(display, r, screen->chan, 1, 0x77777777); colours[White] = allocimage(display, r, screen->chan, 1, 0xffffffff); colours[Red] = allocimage(display, r, screen->chan, 1, 0xff0000ff); colours[Green] = allocimage(display, r, screen->chan, 1, 0x00ff00ff); colours[Blue] = allocimage(display, r, screen->chan, 1, 0x0000ffff); } static void init(void) { initcolours(); } static void drawvector(Point3 a, Point3 b, Image *colour) { a = xformpointd(a, nil, worldmat); b = xformpointd(b, nil, worldmat); //print("a=(%f,%f)\n", a.x, a.y); //print("b=(%f,%f)\n", b.x, b.y); line(screen, (Point){a.x, a.y}, (Point){b.x, b.y}, Enddisc, Enddisc, 0, colour, ZP); } static void drawquad(Point3 p[4], Image *colour) { Point3 q[4]; int i, j; for(i = 0; i < 4; i++) q[i] = xformpointd(p[i], nil, worldmat); for(i = 0, j = 1; i < 4; i++, j = (j+1)%4) line(screen, (Point){q[i].x, q[i].y}, (Point){q[j].x, q[j].y}, Enddisc, Enddisc, 0, colour, ZP); } static void drawcube(Image *c) { static Point3 v[8] = { { -0.5, -0.5, -0.5, 1 }, { +0.5, -0.5, -0.5, 1 }, { +0.5, -0.5, +0.5, 1 }, { -0.5, -0.5, +0.5, 1 }, { -0.5, +0.5, -0.5, 1 }, { +0.5, +0.5, -0.5, 1 }, { +0.5, +0.5, +0.5, 1 }, { -0.5, +0.5, +0.5, 1 }, }; Point3 u[8]; int i; for(i = 0; i < 8; i++) u[i] = xformpointd(v[i], nil, worldmat); drawquad(&v[0], c); drawquad(&v[4], c); for(i = 0; i < 4; i++) line(screen, (Point){u[i].x, u[i].y}, (Point){u[i+4].x, u[i+4].y}, Enddisc, Enddisc, 0, c, ZP); } static void drawaxes(void) { Point3 o = {0,0,0,1}; Point3 x = {1,0,0,1}; Point3 y = {0,1,0,1}; Point3 z = {0,0,1,1}; drawvector(o, x, colours[Red]); drawvector(o, y, colours[Green]); drawvector(o, z, colours[Blue]); } enum { GridMin = -100, GridMax = +100, GridStep = 10, }; static void drawxzgrid(Image *c) { int i; Point3 p1, p2; Point3 q1, q2; Point3 t = { 10000, 0, 10000, 1 }; t = xformpointd(t, nil, worldmat); //print("t=(%f,%f,%f,%f)\n", t.x, t.y, t.z, t.w); p1.y = p2.y = 0; p1.w = p2.w = 1; p1.z = GridMin; p2.z = GridMax; for(i = GridMin; i <= GridMax; i += GridStep){ p1.x = p2.x = i; //print("p1=(%f,%f,%f,%f)\n", p1.x, p1.y, p1.z, p1.w); //print("p2=(%f,%f,%f,%f)\n", p2.x, p2.y, p2.z, p2.w); q1 = xformpointd(p1, nil, worldmat); q2 = xformpointd(p2, nil, worldmat); //print("q1=(%f,%f,%f,%f)\n", q1.x, q1.y, q1.z, q1.w); //print("q2=(%f,%f,%f,%f)\n", q2.x, q2.y, q2.z, q2.w); line(screen, (Point){q1.x/q1.z,q1.y/q1.z}, (Point){q2.x/q2.z,q2.y/q2.z}, Enddisc, Enddisc, 0, c, ZP); } p1.x = GridMin; p2.x = GridMax; for(i = GridMin; i <= GridMax; i += GridStep){ p1.z = p2.z = i; //print("p1=(%f,%f,%f,%f)\n", p1.x, p1.y, p1.z, p1.w); //print("p2=(%f,%f,%f,%f)\n", p2.x, p2.y, p2.z, p2.w); q1 = xformpointd(p1, nil, worldmat); q2 = xformpointd(p2, nil, worldmat); //print("q1=(%f,%f,%f,%f)\n", q1.x, q1.y, q1.z, q1.w); //print("q2=(%f,%f,%f,%f)\n", q2.x, q2.y, q2.z, q2.w); line(screen, (Point){q1.x/q1.z,q1.y/q1.z}, (Point){q2.x/q2.z,q2.y/q2.z}, Enddisc, Enddisc, 0, c, ZP); } } typedef struct Camera Camera; struct Camera { Point3 pos; Point3 target; }; Camera camera = { .pos = { 100, 10, 100, 1 }, .target = { 0, 10, 0, 1 }, }; static void redraw(void) { int i; Point cubes[] = { { 2, 2 }, { 5, -2 }, { 10, 1 }, { -30, 55 }, { 25, -33 }, }; worldmat = pushmat(worldmat); look(worldmat, camera.pos, camera.target, add3(camera.pos, (Point3){0,1,0,1})); draw(screen, screen->r, colours[Black], nil, ZP); drawxzgrid(colours[Grey]); drawaxes(); for(i = 0; i < nelem(cubes); i++){ worldmat = pushmat(worldmat); move(worldmat, cubes[i].x, 0.5, cubes[i].y); drawcube(colours[White]); worldmat = popmat(worldmat); } worldmat = popmat(worldmat); flushimage(display, 1); } static void resized(void) { while(worldmat != nil) worldmat = popmat(worldmat); worldmat = pushmat(nil); viewport(worldmat, screen->r, 1); persp(worldmat, 60, 0.001, 5000.0); } void threadmain(int argc, char **argv) { double Δθ = 1.0; Point3 v, n, up = {0,1,0,1}; Kbdstate kbd; ARGBEGIN{ }ARGEND; if(initdraw(nil, nil, nil) < 0) sysfatal("initdraw: %r"); mc = initmouse(nil, nil); if(mc == nil) sysfatal("initmouse: %r"); kc = chancreate(sizeof(Kbdstate), 0); proccreate(kbdproc, nil, 1024); tc = chancreate(1, 0); proccreate(timerproc, nil, 1024); init(); memset(&kbd, 0, sizeof(Kbdstate)); resized(); redraw(); for(;;){ Alt a[] = { { mc->c, nil, CHANRCV }, { mc->resizec, nil, CHANRCV }, { kc, &kbd, CHANRCV }, { tc, nil, CHANRCV }, { nil, nil, CHANEND} }; switch(alt(a)){ case 0: break; case 1: if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); resized(); redraw(); break; case 2: break; case 3: v = sub3(camera.target, camera.pos); v = unit3(v); n = cross3(v, up); //v = mul3(v, 1); //n = mul3(n, 1); if(kbd.f) camera.pos = add3(camera.pos, v); if(kbd.b) camera.pos = sub3(camera.pos, v); if(kbd.sl) camera.pos = sub3(camera.pos, n); if(kbd.sr) camera.pos = add3(camera.pos, n); if(kbd.rr){ Quaternion q = {cos(radians(-Δθ)/2), 0, sin(radians(-Δθ)/2), 0}; Quaternion p = {0, v.x, v.y, v.z}; p = qmul(qmul(q, p), qinv(q)); v = (Point3){p.i, p.j, p.k, 1.}; } if(kbd.rl){ Quaternion q = {cos(radians(Δθ)/2), 0, sin(radians(Δθ)/2), 0}; Quaternion p = {0, v.x, v.y, v.z}; p = qmul(qmul(q, p), qinv(q)); v = (Point3){p.i, p.j, p.k, 1.}; } camera.target = add3(camera.pos, v); redraw(); break; } } }