summaryrefslogtreecommitdiff
path: root/solar.c
diff options
context:
space:
mode:
authorrodri <rgl@antares-labs.eu>2024-05-12 20:34:03 +0000
committerrodri <rgl@antares-labs.eu>2024-05-12 20:34:03 +0000
commitaeb153cdd6cb37dc1e9d61b22a92430a756ddeb5 (patch)
tree679f3970653138702f9206a8beb256d814de1cb5 /solar.c
parent55ceca8db396829cba474164545c6bcfcc45eea4 (diff)
download3dee-aeb153cdd6cb37dc1e9d61b22a92430a756ddeb5.tar.gz
3dee-aeb153cdd6cb37dc1e9d61b22a92430a756ddeb5.tar.bz2
3dee-aeb153cdd6cb37dc1e9d61b22a92430a756ddeb5.zip
began work on a solar system planetarium.
Diffstat (limited to 'solar.c')
-rw-r--r--solar.c685
1 files changed, 685 insertions, 0 deletions
diff --git a/solar.c b/solar.c
new file mode 100644
index 0000000..4682d7f
--- /dev/null
+++ b/solar.c
@@ -0,0 +1,685 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "libgraphics/graphics.h"
+#include "fns.h"
+
+enum {
+ K↑,
+ K↓,
+ K←,
+ K→,
+ Krise,
+ Kfall,
+ KR↑,
+ KR↓,
+ KR←,
+ KR→,
+ KR↺,
+ KR↻,
+ Kzoomin,
+ Kzoomout,
+ Khud,
+ Ke
+};
+
+enum {
+ Sfov,
+ Scampos,
+ Scambx, Scamby, Scambz,
+ Sfps,
+ Sframes,
+ Se
+};
+
+enum {
+ Cmdwinht = 50,
+ Cmdmargin = 10,
+ Cmdpadding = 3,
+
+ Cmdlookat = 0,
+ Cmdgoto,
+};
+
+typedef struct Planet Planet;
+typedef struct Camcfg Camcfg;
+typedef struct HReq HReq;
+typedef struct Cmdbut Cmdbut;
+typedef struct Cmdbox Cmdbox;
+
+struct Planet
+{
+ int id; /* Horizons API ID */
+ char *name;
+ double scale;
+ Entity *body;
+ Material *mtl;
+};
+
+struct Camcfg
+{
+ Point3 p, lookat, up;
+ double fov, clipn, clipf;
+ int ptype;
+};
+
+struct HReq
+{
+ int pfd[2];
+ int pid; /* planet id */
+ char *t0, *t1; /* start and end times */
+};
+
+struct Cmdbut
+{
+ char *label;
+ Rectangle r;
+ void (*handler)(Cmdbut*, Mousectl*);
+};
+
+struct Cmdbox
+{
+ Rectangle r;
+ Cmdbut *cmds;
+ ulong ncmds;
+};
+
+Rune keys[Ke] = {
+ [K↑] = Kup,
+ [K↓] = Kdown,
+ [K←] = Kleft,
+ [K→] = Kright,
+ [Krise] = Kpgup,
+ [Kfall] = Kpgdown,
+ [KR↑] = 'w',
+ [KR↓] = 's',
+ [KR←] = 'a',
+ [KR→] = 'd',
+ [KR↺] = 'q',
+ [KR↻] = 'e',
+ [Kzoomin] = 'z',
+ [Kzoomout] = 'x',
+ [Khud] = 'h',
+};
+Planet planets[] = {
+ { .id = 10, .name = "Sol", .scale = 100 },
+ { .id = 1, .name = "Mercury", .scale = 0.333333 },
+ { .id = 2, .name = "Venus", .scale = 0.8 },
+ { .id = 399, .name = "Earth", .scale = 1 },
+ { .id = 301, .name = "Luna", .scale = 0.25 },
+ { .id = 4, .name = "Mars", .scale = 0.5 },
+ { .id = 5, .name = "Jupiter", .scale = 11 },
+ { .id = 6, .name = "Saturn", .scale = 9 },
+ { .id = 7, .name = "Uranus", .scale = 4 },
+ { .id = 8, .name = "Neptune", .scale = 3.666666 },
+ { .id = 9, .name = "Pluto", .scale = 0.166666 },
+};
+char stats[Se][256];
+Rectangle viewr, cmdr;
+Cmdbox cmdbox;
+Image *screenb;
+Mousectl *mctl;
+Keyboardctl *kctl;
+Channel *drawc;
+int kdown;
+Model *model;
+Scene *scene;
+double θ, ω = 0;
+
+Camera camera;
+Camcfg cameracfg = {
+ 2,0,4,1,
+ 0,0,0,1,
+ 0,1,0,0,
+ 80*DEG, 0.01, 1e12, PERSPECTIVE
+};
+Point3 center = {0,0,0,1};
+
+static int doprof;
+static int showhud;
+
+static int
+min(int a, int b)
+{
+ return a < b? a: b;
+}
+
+static int
+max(int a, int b)
+{
+ return a > b? a: b;
+}
+
+static void
+sailor(void *arg)
+{
+ char buf[128], pidstr[8];
+ HReq *r;
+
+ r = arg;
+
+ close(r->pfd[0]);
+ dup(r->pfd[1], 1);
+ close(r->pfd[1]);
+
+ getwd(buf, sizeof(buf)-1);
+ snprint(buf+strlen(buf), sizeof(buf)-strlen(buf), "/tools/horizonget");
+ snprint(pidstr, sizeof pidstr, "%d", r->pid);
+
+ execl(buf, "horizonget", pidstr, r->t0, r->t1, nil);
+ sysfatal("execl: %r");
+}
+
+static char *
+getplanetstate(int id, char *t0, char *t1)
+{
+ Biobuf *bin;
+ char *line, *lastline;
+ HReq r;
+
+ r.pid = id;
+ r.t0 = t0;
+ r.t1 = t1;
+ lastline = nil;
+
+ if(pipe(r.pfd) < 0)
+ sysfatal("pipe: %r");
+ switch(fork()){
+ case -1:
+ sysfatal("fork: %r");
+ case 0:
+ sailor(&r);
+ default:
+ close(r.pfd[1]);
+ bin = Bfdopen(r.pfd[0], OREAD);
+ if(bin == nil)
+ sysfatal("Bfdopen: %r");
+ while((line = Brdline(bin, '\n')) != nil){
+ line[Blinelen(bin)-1] = 0;
+ lastline = line;
+ }
+ if(lastline != nil)
+ lastline = strdup(lastline);
+ Bterm(bin);
+ close(r.pfd[0]);
+ }
+
+ return lastline;
+}
+
+static Planet *
+getplanet(Entity *e)
+{
+ int i;
+
+ for(i = 0; i < nelem(planets); i++)
+ if(e == planets[i].body)
+ return &planets[i];
+ return nil;
+}
+
+Point3
+identvshader(VSparams *sp)
+{
+ Planet *p;
+ Point3 pos;
+
+ p = getplanet(sp->su->entity);
+ assert(p != nil);
+
+ Matrix3 S = {
+ p->scale, 0, 0, 0,
+ 0, p->scale, 0, 0,
+ 0, 0, p->scale, 0,
+ 0, 0, 0, 1,
+ };
+ pos = xform3(sp->v->p, S);
+
+ sp->v->mtl = p->mtl;
+ sp->v->c = p->mtl->diffuse;
+ return world2clip(&camera, model2world(sp->su->entity, pos));
+}
+
+Color
+identshader(FSparams *sp)
+{
+ Color tc, c;
+
+ if(sp->v.mtl != nil && sp->v.mtl->diffusemap != nil && sp->v.uv.w != 0)
+ tc = texture(sp->v.mtl->diffusemap, sp->v.uv, neartexsampler);
+ else if(sp->su->entity->mdl->tex != nil && sp->v.uv.w != 0)
+ tc = texture(sp->su->entity->mdl->tex, sp->v.uv, neartexsampler);
+ else
+ tc = Pt3(1,1,1,1);
+
+ c.a = 1;
+ c.b = fclamp(sp->v.c.b*tc.b, 0, 1);
+ c.g = fclamp(sp->v.c.g*tc.g, 0, 1);
+ c.r = fclamp(sp->v.c.r*tc.r, 0, 1);
+
+ return c;
+}
+
+Shadertab shader = { "ident", identvshader, identshader };
+
+void
+zoomin(void)
+{
+ camera.fov = fclamp(camera.fov - 1*DEG, 1*DEG, 180*DEG);
+ reloadcamera(&camera);
+}
+
+void
+zoomout(void)
+{
+ camera.fov = fclamp(camera.fov + 1*DEG, 1*DEG, 180*DEG);
+ reloadcamera(&camera);
+}
+
+void
+drawstats(void)
+{
+ int i;
+
+ snprint(stats[Sfov], sizeof(stats[Sfov]), "FOV %g°", camera.fov/DEG);
+ snprint(stats[Scampos], sizeof(stats[Scampos]), "%V", camera.p);
+ snprint(stats[Scambx], sizeof(stats[Scambx]), "bx %V", camera.bx);
+ snprint(stats[Scamby], sizeof(stats[Scamby]), "by %V", camera.by);
+ snprint(stats[Scambz], sizeof(stats[Scambz]), "bz %V", camera.bz);
+ snprint(stats[Sfps], sizeof(stats[Sfps]), "FPS %.0f/%.0f/%.0f/%.0f", !camera.stats.max? 0: 1e9/camera.stats.max, !camera.stats.avg? 0: 1e9/camera.stats.avg, !camera.stats.min? 0: 1e9/camera.stats.min, !camera.stats.v? 0: 1e9/camera.stats.v);
+ snprint(stats[Sframes], sizeof(stats[Sframes]), "frame %llud", camera.stats.nframes);
+ for(i = 0; i < Se; i++)
+ stringbg(screen, addpt(screen->r.min, Pt(10,10 + i*font->height)), display->black, ZP, font, stats[i], display->white, ZP);
+}
+
+void
+redraw(void)
+{
+ int i;
+
+ lockdisplay(display);
+ camera.vp->draw(camera.vp, screenb);
+ draw(screen, rectaddpt(viewr, screen->r.min), display->black, nil, ZP);
+ draw(screen, rectaddpt(viewr, screen->r.min), screenb, nil, ZP);
+ draw(screen, rectaddpt(cmdbox.r, screen->r.min), display->white, nil, ZP);
+ for(i = 0; i < cmdbox.ncmds; i++){
+ border(screen, rectaddpt(cmdbox.cmds[i].r, screen->r.min), 1, display->black, ZP);
+ string(screen, addpt(screen->r.min, addpt(cmdbox.cmds[i].r.min, Pt(Cmdpadding,Cmdpadding))), display->black, ZP, font, cmdbox.cmds[i].label);
+ }
+ if(showhud)
+ drawstats();
+ flushimage(display, 1);
+ unlockdisplay(display);
+}
+
+void
+drawproc(void *)
+{
+ uvlong t0, Δt;
+
+ threadsetname("drawproc");
+
+ t0 = nsec();
+ for(;;){
+ shootcamera(&camera, &shader);
+ if(doprof)
+ fprint(2, "R %llud %llud\nE %llud %llud\nT %llud %llud\nr %llud %llud\n\n",
+ camera.times.R[camera.times.cur-1].t0, camera.times.R[camera.times.cur-1].t1,
+ camera.times.E[camera.times.cur-1].t0, camera.times.E[camera.times.cur-1].t1,
+ camera.times.Tn[camera.times.cur-1].t0, camera.times.Tn[camera.times.cur-1].t1,
+ camera.times.Rn[camera.times.cur-1].t0, camera.times.Rn[camera.times.cur-1].t1);
+ Δt = nsec() - t0;
+ if(Δt > HZ2MS(60)*1000000ULL){
+ nbsend(drawc, nil);
+ t0 += Δt;
+ }
+ }
+}
+
+static char *
+genplanetmenu(int idx)
+{
+ if(idx < nelem(planets))
+ return planets[idx].name;
+ return nil;
+}
+
+void
+lookat_cmd(Cmdbut *, Mousectl *mc)
+{
+ static Menu menu = { .gen = genplanetmenu };
+ Planet *p;
+ int idx;
+
+ idx = menuhit(1, mc, &menu, _screen);
+ if(idx < 0)
+ return;
+ p = &planets[idx];
+ placecamera(&camera, camera.p, p->body->p, cameracfg.up);
+ nbsend(drawc, nil);
+}
+
+void
+goto_cmd(Cmdbut *, Mousectl *mc)
+{
+ static Menu menu = { .gen = genplanetmenu };
+ Planet *p;
+ int idx;
+
+ idx = menuhit(1, mc, &menu, _screen);
+ if(idx < 0)
+ return;
+ p = &planets[idx];
+ placecamera(&camera, addpt3(p->body->p, Vec3(0,0,10)), p->body->p, cameracfg.up);
+ nbsend(drawc, nil);
+}
+
+Cmdbut cmds[] = {
+ { .label = "look at", .handler = lookat_cmd },
+ { .label = "go to", .handler = goto_cmd },
+};
+
+void
+lmb(void)
+{
+ Cmdbut *cmd;
+ int i;
+
+ cmd = nil;
+ for(i = 0; i < cmdbox.ncmds; i++)
+ if(ptinrect(subpt(mctl->xy, screen->r.min), cmdbox.cmds[i].r))
+ cmd = &cmdbox.cmds[i];
+
+ if(cmd == nil)
+ return;
+ cmd->handler(cmd, mctl);
+}
+
+void
+mmb(void)
+{
+ enum {
+ QUIT,
+ };
+ static char *items[] = {
+ [QUIT] "quit",
+ nil,
+ };
+ static Menu menu = { .item = items };
+
+ switch(menuhit(2, mctl, &menu, _screen)){
+ case QUIT:
+ threadexitsall(nil);
+ }
+ nbsend(drawc, nil);
+}
+
+void
+mouse(void)
+{
+ static Mouse om;
+
+ if((om.buttons ^ mctl->buttons) == 0)
+ return;
+ if((mctl->buttons & 1) != 0)
+ lmb();
+ if((mctl->buttons & 2) != 0)
+ mmb();
+ if((mctl->buttons & 8) != 0)
+ zoomin();
+ if((mctl->buttons & 16) != 0)
+ zoomout();
+
+ om = mctl->Mouse;
+}
+
+void
+kbdproc(void *)
+{
+ Rune r, *a;
+ char buf[128], *s;
+ int fd, n;
+
+ threadsetname("kbdproc");
+
+ if((fd = open("/dev/kbd", OREAD)) < 0)
+ sysfatal("kbdproc: %r");
+ memset(buf, 0, sizeof buf);
+
+ for(;;){
+ if(buf[0] != 0){
+ n = strlen(buf)+1;
+ memmove(buf, buf+n, sizeof(buf)-n);
+ }
+ if(buf[0] == 0){
+ if((n = read(fd, buf, sizeof(buf)-1)) <= 0)
+ break;
+ buf[n-1] = 0;
+ buf[n] = 0;
+ }
+ if(buf[0] == 'c'){
+ chartorune(&r, buf+1);
+ if(r == Kdel){
+ close(fd);
+ threadexitsall(nil);
+ }else
+ nbsend(kctl->c, &r);
+ }
+ if(buf[0] != 'k' && buf[0] != 'K')
+ continue;
+ s = buf+1;
+ kdown = 0;
+ while(*s){
+ s += chartorune(&r, s);
+ for(a = keys; a < keys+Ke; a++)
+ if(r == *a){
+ kdown |= 1 << a-keys;
+ break;
+ }
+ }
+ }
+}
+
+void
+keyproc(void *c)
+{
+ threadsetname("keyproc");
+
+ for(;;){
+ nbsend(c, nil);
+ sleep(HZ2MS(100)); /* key poll rate */
+ }
+}
+
+void
+handlekeys(void)
+{
+ static int okdown;
+
+ if(kdown & 1<<K↑)
+ placecamera(&camera, subpt3(camera.p, mulpt3(camera.bz, 0.1)), camera.bz, camera.by);
+ if(kdown & 1<<K↓)
+ placecamera(&camera, addpt3(camera.p, mulpt3(camera.bz, 0.1)), camera.bz, camera.by);
+ if(kdown & 1<<K←)
+ placecamera(&camera, subpt3(camera.p, mulpt3(camera.bx, 0.1)), camera.bz, camera.by);
+ if(kdown & 1<<K→)
+ placecamera(&camera, addpt3(camera.p, mulpt3(camera.bx, 0.1)), camera.bz, camera.by);
+ if(kdown & 1<<Krise)
+ placecamera(&camera, addpt3(camera.p, mulpt3(camera.by, 0.1)), camera.bz, camera.by);
+ if(kdown & 1<<Kfall)
+ placecamera(&camera, subpt3(camera.p, mulpt3(camera.by, 0.1)), camera.bz, camera.by);
+ if(kdown & 1<<KR↑)
+ aimcamera(&camera, qrotate(camera.bz, camera.bx, 1*DEG));
+ if(kdown & 1<<KR↓)
+ aimcamera(&camera, qrotate(camera.bz, camera.bx, -1*DEG));
+ if(kdown & 1<<KR←)
+ aimcamera(&camera, qrotate(camera.bz, camera.by, 1*DEG));
+ if(kdown & 1<<KR→)
+ aimcamera(&camera, qrotate(camera.bz, camera.by, -1*DEG));
+ if(kdown & 1<<KR↺)
+ placecamera(&camera, camera.p, camera.bz, qrotate(camera.by, camera.bz, 1*DEG));
+ if(kdown & 1<<KR↻)
+ placecamera(&camera, camera.p, camera.bz, qrotate(camera.by, camera.bz, -1*DEG));
+ if(kdown & 1<<Kzoomin)
+ zoomin();
+ if(kdown & 1<<Kzoomout)
+ zoomout();
+
+ if((okdown & 1<<Khud) == 0 && (kdown & 1<<Khud) != 0)
+ showhud ^= 1;
+
+ okdown = kdown;
+}
+
+void
+resize(void)
+{
+ lockdisplay(display);
+ if(getwindow(display, Refnone) < 0)
+ fprint(2, "can't reattach to window\n");
+ unlockdisplay(display);
+ nbsend(drawc, nil);
+}
+
+static void
+confproc(void)
+{
+ char buf[64];
+ int fd;
+
+ snprint(buf, sizeof buf, "/proc/%d/ctl", getpid());
+ fd = open(buf, OWRITE);
+ if(fd < 0)
+ sysfatal("open: %r");
+
+ if(doprof)
+ fprint(fd, "profile\n");
+ close(fd);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s\n", argv0);
+ exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ Viewport *v;
+ Renderer *rctl;
+ Channel *keyc;
+ Entity *subject;
+ OBJ *obj;
+ Point lblsiz;
+ char *s, *p;
+ int i, j;
+
+ GEOMfmtinstall();
+ ARGBEGIN{
+ case 'p': doprof++; break;
+ default: usage();
+ }ARGEND;
+ if(argc != 0)
+ usage();
+
+ confproc();
+
+ if((obj = objparse("mdl/planet.obj")) == nil)
+ sysfatal("objparse: %r");
+ model = newmodel();
+ loadobjmodel(model, obj);
+ objfree(obj);
+ scene = newscene(nil);
+ fprint(2, "loading planet states...\n");
+ for(i = 0; i < nelem(planets); i++){
+ subject = newentity(model);
+// subject->p.x = i*2e3;
+ scene->addent(scene, subject);
+ planets[i].body = subject;
+ for(j = 0; j < model->nmaterials; j++)
+ if(strcmp(planets[i].name, model->materials[j].name) == 0)
+ planets[i].mtl = &model->materials[j];
+ if(i == 0){
+ subject->p = Pt3(0,0,0,1);
+ continue;
+ }
+ s = getplanetstate(planets[i].id, nil, nil);
+ if(s == nil)
+ sysfatal("couldn't load planet: %s", planets[i].name);
+ p = strchr(s, '=');
+ subject->p.x = strtod(++p, nil);
+ p = strchr(p, '=');
+ subject->p.y = strtod(++p, nil);
+ p = strchr(p, '=');
+ subject->p.z = strtod(++p, nil);
+ subject->p.w = 1;
+ subject->p = divpt3(subject->p, 1e5);
+ free(s);
+ fprint(2, "%s ready\n", planets[i].name);
+ }
+
+ if(memimageinit() != 0)
+ sysfatal("memimageinit: %r");
+ if((rctl = initgraphics()) == nil)
+ sysfatal("initgraphics: %r");
+ if(initdraw(nil, nil, "solar") < 0)
+ sysfatal("initdraw: %r");
+ if((mctl = initmouse(nil, screen)) == nil)
+ sysfatal("initmouse: %r");
+
+ viewr = rectsubpt(Rpt(screen->r.min, subpt(screen->r.max, Pt(0,Cmdwinht))), screen->r.min);
+ cmdbox.r = Rect(viewr.min.x, viewr.max.y, Dx(viewr), Dy(screen->r));
+ cmdbox.cmds = cmds;
+ cmdbox.ncmds = nelem(cmds);
+ for(i = 0; i < nelem(cmds); i++){
+ lblsiz = stringsize(font, cmds[i].label);
+ cmds[i].r = Rect(0,0,Cmdpadding+lblsiz.x+Cmdpadding,Cmdpadding+lblsiz.y+Cmdpadding);
+ if(i == 0)
+ cmds[i].r = rectaddpt(cmds[i].r, addpt(cmdbox.r.min, Pt(Cmdmargin,Cmdmargin)));
+ else
+ cmds[i].r = rectaddpt(cmds[i].r, Pt(cmds[i-1].r.max.x+Cmdmargin,cmds[i-1].r.min.y));
+ }
+ screenb = eallocimage(display, viewr, RGBA32, 0, DNofill);
+ v = mkviewport(screenb->r);
+ placecamera(&camera, cameracfg.p, cameracfg.lookat, cameracfg.up);
+ configcamera(&camera, v, cameracfg.fov, cameracfg.clipn, cameracfg.clipf, cameracfg.ptype);
+ camera.s = scene;
+ camera.rctl = rctl;
+
+ kctl = emalloc(sizeof *kctl);
+ kctl->c = chancreate(sizeof(Rune), 16);
+ keyc = chancreate(sizeof(void*), 1);
+ drawc = chancreate(sizeof(void*), 1);
+ display->locking = 1;
+ unlockdisplay(display);
+
+ proccreate(kbdproc, nil, mainstacksize);
+ proccreate(keyproc, keyc, mainstacksize);
+ proccreate(drawproc, nil, mainstacksize);
+
+ for(;;){
+ enum {MOUSE, RESIZE, KEY, DRAW};
+ Alt a[] = {
+ {mctl->c, &mctl->Mouse, CHANRCV},
+ {mctl->resizec, nil, CHANRCV},
+ {keyc, nil, CHANRCV},
+ {drawc, nil, CHANRCV},
+ {nil, nil, CHANEND}
+ };
+ switch(alt(a)){
+ case MOUSE: mouse(); break;
+ case RESIZE: resize(); break;
+ case KEY: handlekeys(); break;
+ case DRAW: redraw(); break;
+ }
+ }
+}