diff options
-rw-r--r-- | assets/mdl/needle.vmdl | 13 | ||||
-rw-r--r-- | dat.h | 17 | ||||
-rw-r--r-- | fns.h | 8 | ||||
-rw-r--r-- | mkfile | 1 | ||||
-rw-r--r-- | musw.c | 144 | ||||
-rw-r--r-- | muswd.c | 39 | ||||
-rw-r--r-- | notes | 17 | ||||
-rw-r--r-- | party.c | 3 | ||||
-rw-r--r-- | physics.c | 19 | ||||
-rw-r--r-- | universe.c | 63 |
10 files changed, 275 insertions, 49 deletions
diff --git a/assets/mdl/needle.vmdl b/assets/mdl/needle.vmdl index ba2fd44..d029c17 100644 --- a/assets/mdl/needle.vmdl +++ b/assets/mdl/needle.vmdl @@ -1,14 +1,13 @@ # The Needle vector model - v 10 0 v 8 -4 +v 8 -4 +v -10 -6 v -10 -6 v -12 0 v -10 6 +v -10 6 v 8 4 - -l 1 2 -l 2 3 -c 3 4 5 -l 5 6 -l 6 1 +v 8 4 +v 10 0 +llcll @@ -34,6 +34,7 @@ typedef struct GameState GameState; typedef struct Derivative Derivative; typedef struct Conn Conn; typedef struct Player Player; +typedef struct PInput PInput; typedef struct Lobby Lobby; typedef struct Party Party; @@ -68,8 +69,8 @@ struct Sprite struct Particle { Point2 p, v; - double yaw; - double mass; + double θ, ω; + double mass; /* kg */ }; struct Bullet @@ -83,6 +84,7 @@ struct Ship { Particle; Kind kind; + int fuel; Bullet rounds[10]; VModel *mdl; Matrix mdlxform; @@ -98,8 +100,10 @@ struct Universe { Ship ships[2]; Star star; + double t, timeacc; - int (*step)(Universe*); + void (*step)(Universe*, double); + void (*reset)(Universe*); }; struct GameState @@ -119,12 +123,19 @@ struct Conn char dir[40]; int ctl; int data; + int status; +}; + +struct PInput +{ + ulong kdown; }; struct Player { char *name; Conn conn; + PInput oldinput, input; }; struct Lobby @@ -1,4 +1,4 @@ -#define FPS2MS(fps) (1000/(fps)) +#define HZ2MS(hz) (1000/(hz)) /* * alloc @@ -37,3 +37,9 @@ Party *newparty(Player[2]); void delparty(Party*); void addparty(Party*); +/* + * universe + */ +Universe *newuniverse(void); +void deluniverse(Universe*); +void inituniverse(Universe*); @@ -14,6 +14,7 @@ OFILES=\ lobby.$O\ party.$O\ sprite.$O\ + universe.$O\ HFILES=\ dat.h\ @@ -1,5 +1,6 @@ #include <u.h> #include <libc.h> +#include <bio.h> #include <thread.h> #include <draw.h> #include <mouse.h> @@ -27,7 +28,7 @@ Keymap kmap[] = { {.key = 'y', .op = Ksay}, {.key = 'q', .op = Kquit} }; -ulong kup, kdown; +ulong kdown; typedef struct Ball Ball; struct Ball @@ -35,11 +36,116 @@ struct Ball Point2 p, v; }; +RFrame screenrf; Ball bouncer; +Universe *universe; +VModel *needlemdl; char winspec[32]; int debug; +Point +toscreen(Point2 p) +{ + p = invrframexform(p, screenrf); + return Pt(p.x,p.y); +} + +Point2 +fromscreen(Point p) +{ + return rframexform(Pt2(p.x,p.y,1), screenrf); +} + +/* + * readvmodel and drawship are testing routines + * that will later be implemented as VModel methods. + */ +VModel * +readvmodel(char *file) +{ + ulong lineno; + char *s, *args[2]; + Biobuf *bin; + VModel *mdl; + + bin = Bopen(file, OREAD); + if(bin == nil) + sysfatal("Bopen: %r"); + + mdl = emalloc(sizeof *mdl); + mdl->pts = nil; + mdl->npts = 0; + mdl->strokefmt = nil; + + lineno = 0; + while(s = Brdline(bin, '\n')){ + s[Blinelen(bin)-1] = 0; + lineno++; + + switch(*s++){ + case '#': + continue; + case 'v': + if(tokenize(s, args, nelem(args)) != nelem(args)){ + werrstr("syntax error: %s:%lud 'v' expects %d args", + file, lineno, nelem(args)); + free(mdl); + Bterm(bin); + return nil; + } + mdl->pts = erealloc(mdl->pts, ++mdl->npts*sizeof(Point2)); + mdl->pts[mdl->npts-1].x = strtod(args[0], nil); + mdl->pts[mdl->npts-1].y = strtod(args[1], nil); + mdl->pts[mdl->npts-1].w = 1; + break; + case 'l': + case 'c': + mdl->strokefmt = strdup(s-1); + break; + } + } + Bterm(bin); + + return mdl; +} + +void +drawship(Ship *ship, Image *dst) +{ + int i; + char *s; + Point pts[3]; + VModel *mdl; + Point2 *p; + Matrix T = { + 1, 0, ship->p.x, + 0, 1, ship->p.y, + 0, 0, 1 + }, R = { + cos(ship->θ), -sin(ship->θ), 0, + sin(ship->θ), cos(ship->θ), 0, + 0, 0, 1 + }; + + mulm(T, R); + mdl = ship->mdl; + p = mdl->pts; + for(s = mdl->strokefmt; s != 0 && p-mdl->pts < mdl->npts; s++) + switch(*s){ + case 'l': + line(dst, toscreen(xform(p[0], T)), toscreen(xform(p[1], T)), 0, 0, 0, display->white, ZP); + p += 2; + break; + case 'c': + for(i = 0; i < nelem(pts); i++) + pts[i] = toscreen(xform(p[i], T)); + bezspline(dst, pts, nelem(pts), 0, 0, 0, display->white, ZP); + p += 3; + break; + } +} + void kbdproc(void *) { @@ -84,18 +190,17 @@ kbdproc(void *) break; } } - kup = ~kdown; if(debug) - fprint(2, "kup %.*lub\nkdown %.*lub\n", - sizeof(kup)*8, kup, sizeof(kdown)*8, kdown); + fprint(2, "kdown %.*lub\n", + sizeof(kdown)*8, kdown); } } void threadnetrecv(void *arg) { - uchar buf[256]; + uchar buf[1024]; int fd, n; Ioproc *io; @@ -103,10 +208,10 @@ threadnetrecv(void *arg) io = ioproc(); while((n = ioread(io, fd, buf, sizeof buf)) > 0){ - unpack(buf, n, "PP", &bouncer.p, &bouncer.v); - - if(debug) - fprint(2, "bouncer %v %v\n", bouncer.p, bouncer.v); + unpack(buf, n, "PPdPdP", &bouncer.p, + &universe->ships[0].p, &universe->ships[0].θ, + &universe->ships[1].p, &universe->ships[1].θ, + &universe->star.p); } closeioproc(io); } @@ -135,7 +240,11 @@ redraw(void) lockdisplay(display); draw(screen, screen->r, display->black, nil, ZP); - fillellipse(screen, addpt(screen->r.min,Pt(Dx(screen->r)/2,Dy(screen->r)/2+bouncer.p.y)), 2, 2, display->white, ZP); + fillellipse(screen, toscreen(bouncer.p), 2, 2, display->white, ZP); + + drawship(&universe->ships[0], screen); + drawship(&universe->ships[1], screen); + fillellipse(screen, toscreen(universe->star.p), 4, 4, display->white, ZP); flushimage(display, 1); unlockdisplay(display); @@ -154,6 +263,8 @@ resize(void) sysfatal("resize failed"); unlockdisplay(display); + screenrf.p = Pt2(screen->r.min.x+Dx(screen->r)/2,screen->r.max.y-Dy(screen->r)/2,1); + /* ignore move events */ if(Dx(screen->r) != SCRW || Dy(screen->r) != SCRH){ fd = open("/dev/wctl", OWRITE); @@ -203,18 +314,29 @@ threadmain(int argc, char *argv[]) display->locking = 1; unlockdisplay(display); + screenrf.p = Pt2(screen->r.min.x+Dx(screen->r)/2,screen->r.max.y-Dy(screen->r)/2,1); + screenrf.bx = Vec2(1, 0); + screenrf.by = Vec2(0,-1); + proccreate(kbdproc, nil, 4096); fd = dial(server, nil, nil, nil); if(fd < 0) sysfatal("dial: %r"); + universe = newuniverse(); + needlemdl = readvmodel("assets/mdl/needle.vmdl"); + if(needlemdl == nil) + sysfatal("readvmodel: %r"); + universe->ships[0].mdl = needlemdl; + universe->ships[1].mdl = needlemdl; + threadcreate(threadnetrecv, &fd, 4096); threadcreate(threadresize, mc, 4096); io = ioproc(); for(;;){ redraw(); - iosleep(io, FPS2MS(30)); + iosleep(io, HZ2MS(30)); } } @@ -66,20 +66,24 @@ void broadcaststate(void) { int i, n; - uchar buf[256]; + uchar buf[1024]; Player *player; - Party *p, *np; + Party *p; for(p = theparty.next; p != &theparty; p = p->next){ - n = pack(buf, sizeof buf, "PP", p->state.p, p->state.v); + n = pack(buf, sizeof buf, "PPdPdP", + p->state.p, + p->u->ships[0].p, p->u->ships[0].θ, + p->u->ships[1].p, p->u->ships[1].θ, + p->u->star.p); for(i = 0; i < nelem(p->players); i++){ if(write(p->players[i].conn.data, buf, n) != n){ player = &p->players[i^1]; lobby->takeseat(lobby, player->conn.dir, player->conn.ctl, player->conn.data); - np = p->prev; - delparty(p); - p = np; + /* step back and delete the spoiled party */ + p = p->prev; + delparty(p->next); break; } } @@ -112,7 +116,8 @@ threadsim(void *) if(lobby->getcouple(lobby, couple) != -1){ newparty(couple); - resetsim(theparty.prev); /* reset the new party */ + resetsim(theparty.prev); + theparty.prev->u->reset(theparty.prev->u); } now = nanosec(); @@ -120,8 +125,15 @@ threadsim(void *) then = now; for(p = theparty.next; p != &theparty; p = p->next){ + p->u->timeacc += frametime/1e9; p->state.timeacc += frametime/1e9; + while(p->u->timeacc >= Δt){ + p->u->step(p->u, Δt); + p->u->timeacc -= Δt; + p->u->t += Δt; + } + while(p->state.timeacc >= Δt){ integrate(&p->state, p->state.t, Δt); p->state.timeacc -= Δt; @@ -131,7 +143,7 @@ threadsim(void *) broadcaststate(); - iosleep(io, FPS2MS(70)); + iosleep(io, HZ2MS(70)); } } @@ -158,10 +170,17 @@ fprintstates(int fd) { ulong i = 0; Party *p; + Ship *s; - for(p = theparty.next; p != &theparty; p = p->next, i++) - fprint(fd, "%lud [p %v v %v]\n", + for(p = theparty.next; p != &theparty; p = p->next, i++){ + fprint(fd, "%lud p %v v %v\n", i, p->state.p, p->state.v); + for(s = &p->u->ships[0]; s-p->u->ships < nelem(p->u->ships); s++){ + fprint(fd, "%lud s%lld k%d p %v v %v θ %g ω %g m %g f %d\n", + i, s-p->u->ships, s->kind, s->p, s->v, s->θ, s->ω, s->mass, s->fuel); + } + fprint(fd, "%lud S p %v m %g\n", i, p->u->star.p, p->u->star.mass); + } } @@ -1,17 +0,0 @@ -• there's, at most, two players waiting in the lobby at a given time. -it makes no sense to allocate more than two seats since the threadsim -will consume them whenever they are ready to join the party. i'm -thinking of using channels to synchronize the two threads, so -threadsim doesn't loop doing nothing (but sleeping) until at least a -couple of players join. - -• the integrator has to operate with vectors and the different objects -in the universe, some of which may require their own governing laws. - -• think of a way to pack the bullets efficiently. will they be part -of the global state broadcast? what does the client need to know to -render and manage them? - -• it could be beneficial to do dynamics in the client as well, which -means sending more data, and probably require a tighter sync, but a -smoother user experience. @@ -23,6 +23,8 @@ newparty(Player *players) p->players[0] = players[0]; p->players[1] = players[1]; + p->u = newuniverse(); + addparty(p); return p; @@ -33,6 +35,7 @@ delparty(Party *p) { p->next->prev = p->prev; p->prev->next = p->next; + deluniverse(p->u); free(p); } @@ -18,6 +18,25 @@ accel(GameState *s, double) return Vec2(0, -k*s->p.y - b*s->v.y); } +/* + * XXX: remember to take thrust into account, based on user input. + */ +static Point2 +accelship(Universe *u, Ship *s, double) +{ + double g, d; + + d = vec2len(subpt2(u->star.p, s->p)); + g = G*u->star.mass/(d*d); + return mulpt2(normvec2(subpt2(u->star.p, s->p)), g); +} + +static Point2 +accelbullet(Universe *, Bullet *, double) +{ + return Vec2(0,0); +} + static Derivative eval(GameState *s0, double t, double Δt, Derivative *d) { diff --git a/universe.c b/universe.c new file mode 100644 index 0000000..32869b5 --- /dev/null +++ b/universe.c @@ -0,0 +1,63 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include "libgeometry/geometry.h" +#include "dat.h" +#include "fns.h" + +static void +universe_step(Universe *u, double Δt) +{ + //integrate(u, u->t, Δt); +} + +static void +universe_reset(Universe *u) +{ + int i, j; + + for(i = 0; i < nelem(u->ships); i++){ + for(j = 0; j < nelem(u->ships[i].rounds); j++) + memset(&u->ships[i].rounds[j], 0, sizeof(Bullet)); + memset(&u->ships[i].Particle, 0, sizeof(Particle)); + } + memset(&u->star.Particle, 0, sizeof(Particle)); + inituniverse(u); +} + +void +inituniverse(Universe *u) +{ + u->ships[0].p = Pt2(SCRW/2-50,SCRH/2-50,1); + u->ships[0].θ = (180+45)*DEG; + u->ships[0].mass = 10e3; /* 10 tons */ + u->ships[0].kind = NEEDLE; + u->ships[0].fuel = 100; + + u->ships[1].p = Pt2(-SCRW/2+50,-SCRH/2+50,1); + u->ships[1].θ = 45*DEG; + u->ships[1].mass = 40e3; /* 40 tons */ + u->ships[1].kind = WEDGE; + u->ships[1].fuel = 200; + + u->star.p = Pt2(0,0,1); + u->star.mass = 5.97e24; /* earth's mass */ +} + +Universe * +newuniverse(void) +{ + Universe *u; + + u = emalloc(sizeof(Universe)); + memset(u, 0, sizeof *u); + u->step = universe_step; + u->reset = universe_reset; + return u; +} + +void +deluniverse(Universe *u) +{ + free(u); +} |