ref: 702e1d92ff4e270f5be88d1b5b190c4de758a101
parent: 53c0208f4caf8701189484e6125821f69e249f94
author: rodri <[email protected]>
date: Tue May 14 12:40:49 EDT 2024
use real lengths in solar. bring a qball for vis.
--- a/fns.h
+++ b/fns.h
@@ -2,3 +2,4 @@
void *erealloc(void*, ulong);
Image *eallocimage(Display*, Rectangle, ulong, int, ulong);
Memimage *eallocmemimage(Rectangle, ulong);
+void qb(Rectangle, Point, Point, Quaternion*, Quaternion*);
--- a/mdl/planet.mtl
+++ b/mdl/planet.mtl
@@ -1,6 +1,5 @@
newmtl Sol
Kd 1.0 1.0 1.0
-map_Kd sol_diffuse.jpg
newmtl Mercury
Kd 1.0 1.0 1.0
binary files a/mdl/sol_diffuse.jpg /dev/null differ
--- a/mkfile
+++ b/mkfile
@@ -7,6 +7,7 @@
OFILES=\
alloc.$O\
+ qb.$O\
HFILES=dat.h fns.h
--- /dev/null
+++ b/qb.c
@@ -1,0 +1,86 @@
+/*
+ * Ken Shoemake's Quaternion rotation controller
+ * “Arcball Rotation Control”, Graphics Gems IV § III.1, pp. 175-192, August 1994.
+ */
+#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"
+
+static int
+min(int a, int b)
+{
+ return a < b? a: b;
+}
+
+/*
+ * Convert a mouse point into a unit quaternion, flattening if
+ * constrained to a particular plane.
+ */
+static Quaternion
+mouseq(Point2 p, Quaternion *axis)
+{
+ double l;
+ Quaternion q;
+ double rsq = p.x*p.x + p.y*p.y; /* quadrance */
+
+ if(rsq > 1){ /* outside the sphere */
+ rsq = sqrt(rsq);
+ q.r = 0;
+ q.i = p.x/rsq;
+ q.j = p.y/rsq;
+ q.k = 0;
+ }else{ /* within the sphere */
+ q.r = 0;
+ q.i = p.x;
+ q.j = p.y;
+ q.k = sqrt(1 - rsq);
+ }
+
+ if(axis != nil){
+ l = dotq(q, *axis);
+ q.i -= l*axis->i;
+ q.j -= l*axis->j;
+ q.k -= l*axis->k;
+ l = qlen(q);
+ if(l != 0){
+ q.i /= l;
+ q.j /= l;
+ q.k /= l;
+ }
+ }
+
+ return q;
+}
+
+void
+qb(Rectangle r, Point p0, Point p1, Quaternion *orient, Quaternion *axis)
+{
+ Quaternion q, down;
+ Point2 rmin, rmax;
+ Point2 s0, s1; /* screen coords */
+ Point2 v0, v1; /* unit sphere coords */
+ Point2 ctlcen; /* controller center */
+ double ctlrad; /* controller radius */
+
+ rmin = Vec2(r.min.x, r.min.y);
+ rmax = Vec2(r.max.x, r.max.y);
+ s0 = Vec2(p0.x, p0.y);
+ s1 = Vec2(p1.x, p1.y);
+ ctlcen = divpt2(addpt2(rmin, rmax), 2);
+ ctlrad = min(Dx(r), Dy(r));
+ v0 = divpt2(subpt2(s0, ctlcen), ctlrad);
+ down = invq(mouseq(v0, axis));
+
+ q = *orient;
+ v1 = divpt2(subpt2(s1, ctlcen), ctlrad);
+ *orient = mulq(q, mulq(down, mouseq(v1, axis)));
+}
--- a/solar.c
+++ b/solar.c
@@ -109,17 +109,17 @@
[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 },
+ { .id = 10, .name = "Sol", .scale = 695700 },
+ { .id = 1, .name = "Mercury", .scale = 2439.4 },
+ { .id = 2, .name = "Venus", .scale = 6051.8 },
+ { .id = 399, .name = "Earth", .scale = 6371.0084 },
+ { .id = 301, .name = "Luna", .scale = 1737.4 },
+ { .id = 4, .name = "Mars", .scale = 3389.50 },
+ { .id = 5, .name = "Jupiter", .scale = 69911 },
+ { .id = 6, .name = "Saturn", .scale = 58232 },
+ { .id = 7, .name = "Uranus", .scale = 25362 },
+ { .id = 8, .name = "Neptune", .scale = 24622 },
+ { .id = 9, .name = "Pluto", .scale = 1188.3 },
};
char stats[Se][256];
char datefmt[] = "YYYY-MM-DD";
@@ -129,12 +129,12 @@
Mousectl *mctl;
Keyboardctl *kctl;
Channel *drawc;
+Mouse om;
int kdown;
Tm date;
char datestr[16];
Model *model;
Scene *scene;
-double θ, ω = 0;
Camera camera;
Camcfg cameracfg = {
@@ -144,7 +144,9 @@
80*DEG, 0.01, 1e12, PERSPECTIVE
};
Point3 center = {0,0,0,1};
+double speed = 10;
+static int museummode;
static int doprof;
static int showhud;
@@ -242,7 +244,6 @@
p = strchr(p, '=');
planets[i].body->p.z = strtod(++p, nil);
planets[i].body->p.w = 1;
- planets[i].body->p = divpt3(planets[i].body->p, 1e5);
free(s);
fprint(2, "%s ready\n", planets[i].name);
}
@@ -288,15 +289,13 @@
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);
+ c.b = fclamp(tc.b, 0, 1);
+ c.g = fclamp(tc.g, 0, 1);
+ c.r = fclamp(tc.r, 0, 1);
return c;
}
@@ -411,7 +410,7 @@
if(idx < 0)
return;
p = &planets[idx];
- placecamera(&camera, addpt3(p->body->p, Vec3(0,0,10)), p->body->p, cameracfg.up);
+ placecamera(&camera, addpt3(p->body->p, Vec3(0,0,1.5*p->scale)), p->body->p, cameracfg.up);
nbsend(drawc, nil);
}
@@ -421,6 +420,9 @@
Tm t;
char buf[16];
+ if(museummode)
+ return;
+
memmove(buf, datestr, sizeof buf);
if(enter("new date", buf, sizeof buf, mctl, kctl, nil) <= 0)
return;
@@ -443,6 +445,13 @@
Cmdbut *cmd;
int i;
+ if(ptinrect(subpt(mctl->xy, screen->r.min), viewr) && (om.buttons^mctl->buttons) == 0){
+ return;
+ }
+
+ if((om.buttons ^ mctl->buttons) == 0)
+ return;
+
cmd = nil;
for(i = 0; i < cmdbox.ncmds; i++)
if(ptinrect(subpt(mctl->xy, screen->r.min), cmdbox.cmds[i].r))
@@ -457,15 +466,27 @@
mmb(void)
{
enum {
+ CHGSPEED,
QUIT,
};
static char *items[] = {
+ [CHGSPEED] "change speed",
[QUIT] "quit",
nil,
};
static Menu menu = { .item = items };
+ char buf[128];
+ if((om.buttons ^ mctl->buttons) == 0)
+ return;
+
switch(menuhit(2, mctl, &menu, _screen)){
+ case CHGSPEED:
+ snprint(buf, sizeof buf, "%g", speed);
+ if(enter("speed (km)", buf, sizeof buf, mctl, kctl, nil) <= 0)
+ return;
+ speed = fabs(strtod(buf, nil));
+ break;
case QUIT:
threadexitsall(nil);
}
@@ -475,10 +496,6 @@
void
mouse(void)
{
- static Mouse om;
-
- if((om.buttons ^ mctl->buttons) == 0)
- return;
if((mctl->buttons & 1) != 0)
lmb();
if((mctl->buttons & 2) != 0)
@@ -555,17 +572,17 @@
static int okdown;
if(kdown & 1<<K↑)
- placecamera(&camera, subpt3(camera.p, mulpt3(camera.bz, 0.1)), camera.bz, camera.by);
+ placecamera(&camera, subpt3(camera.p, mulpt3(camera.bz, speed)), camera.bz, camera.by);
if(kdown & 1<<K↓)
- placecamera(&camera, addpt3(camera.p, mulpt3(camera.bz, 0.1)), camera.bz, camera.by);
+ placecamera(&camera, addpt3(camera.p, mulpt3(camera.bz, speed)), camera.bz, camera.by);
if(kdown & 1<<K←)
- placecamera(&camera, subpt3(camera.p, mulpt3(camera.bx, 0.1)), camera.bz, camera.by);
+ placecamera(&camera, subpt3(camera.p, mulpt3(camera.bx, speed)), camera.bz, camera.by);
if(kdown & 1<<K→)
- placecamera(&camera, addpt3(camera.p, mulpt3(camera.bx, 0.1)), camera.bz, camera.by);
+ placecamera(&camera, addpt3(camera.p, mulpt3(camera.bx, speed)), camera.bz, camera.by);
if(kdown & 1<<Krise)
- placecamera(&camera, addpt3(camera.p, mulpt3(camera.by, 0.1)), camera.bz, camera.by);
+ placecamera(&camera, addpt3(camera.p, mulpt3(camera.by, speed)), camera.bz, camera.by);
if(kdown & 1<<Kfall)
- placecamera(&camera, subpt3(camera.p, mulpt3(camera.by, 0.1)), camera.bz, camera.by);
+ placecamera(&camera, subpt3(camera.p, mulpt3(camera.by, speed)), camera.bz, camera.by);
if(kdown & 1<<KR↑)
aimcamera(&camera, qrotate(camera.bz, camera.bx, 1*DEG));
if(kdown & 1<<KR↓)
@@ -618,7 +635,7 @@
void
usage(void)
{
- fprint(2, "usage: %s\n", argv0);
+ fprint(2, "usage: %s [-m]\n", argv0);
exits("usage");
}
@@ -636,6 +653,7 @@
tmfmtinstall();
GEOMfmtinstall();
ARGBEGIN{
+ case 'm': museummode++; break;
case 'p': doprof++; break;
default: usage();
}ARGEND;
@@ -649,6 +667,15 @@
model = newmodel();
loadobjmodel(model, obj);
objfree(obj);
+ /*
+ * normalize the vertices so that we can scale
+ * each planet based on its radius
+ */
+ for(i = 0; i < model->nprims; i++)
+ for(j = 0; j < model->prims[i].type+1; j++){
+ model->prims[i].v[j].p = normvec3(model->prims[i].v[j].p);
+ model->prims[i].v[j].p.w = 1;
+ }
scene = newscene(nil);
for(i = 0; i < nelem(planets); i++){
subject = newentity(model);
@@ -660,11 +687,13 @@
if(i == 0){
subject->p = Pt3(0,0,0,1);
continue;
- }
+ }else if(museummode)
+ subject->p.x = planets[i-1].body->p.x + 1.5*planets[i-1].scale + planets[i].scale;
}
tmnow(&date, nil);
snprint(datestr, sizeof datestr, "%τ", tmfmt(&date, datefmt));
- updateplanets();
+ if(!museummode)
+ updateplanets();
if(memimageinit() != 0)
sysfatal("memimageinit: %r");
--- a/vis.c
+++ b/vis.c
@@ -50,7 +50,7 @@
Model *model;
Entity *subject;
Scene *scene;
-double θ, ω = 0;
+Quaternion orient = {1,0,0,0};
Camera cams[4], *maincam;
Camcfg camcfgs[4] = {
@@ -94,22 +94,18 @@
return a > b? a: b;
}
-//void
-//drawaxis(void)
-//{
-// Point3 op = Pt3(0,0,0,1),
-// px = Pt3(1,0,0,1),
-// py = Pt3(0,1,0,1),
-// pz = Pt3(0,0,1,1);
-//
-// line3(maincam, op, px, 0, Endarrow, display->black);
-// string3(maincam, px, display->black, font, "x");
-// line3(maincam, op, py, 0, Endarrow, display->black);
-// string3(maincam, py, display->black, font, "y");
-// line3(maincam, op, pz, 0, Endarrow, display->black);
-// string3(maincam, pz, display->black, font, "z");
-//}
+static Point3
+Vecquat(Quaternion q)
+{
+ return Vec3(q.i, q.j, q.k);
+}
+static Point3
+Ptquat(Quaternion q, double w)
+{
+ return Pt3(q.i, q.j, q.k, w);
+}
+
Point3
gouraudvshader(VSparams *sp)
{
@@ -121,8 +117,8 @@
Material m;
Color ambient, diffuse, specular;
- sp->v->n = qrotate(sp->v->n, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI));
- sp->v->p = qrotate(sp->v->p, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI));
+ sp->v->n = Vecquat(mulq(mulq(orient, Quatvec(0, sp->v->n)), invq(orient)));
+ sp->v->p = Ptquat(mulq(mulq(orient, Quatvec(0, sp->v->p)), invq(orient)), sp->v->p.w);
pos = model2world(sp->su->entity, sp->v->p);
if(sp->v->mtl != nil){
m = *sp->v->mtl;
@@ -182,8 +178,8 @@
Color a, d, s;
double ss;
- sp->v->n = qrotate(sp->v->n, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI));
- sp->v->p = qrotate(sp->v->p, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI));
+ sp->v->n = Vecquat(mulq(mulq(orient, Quatvec(0, sp->v->n)), invq(orient)));
+ sp->v->p = Ptquat(mulq(mulq(orient, Quatvec(0, sp->v->p)), invq(orient)), sp->v->p.w);
pos = model2world(sp->su->entity, sp->v->p);
addvattr(sp->v, "pos", VAPoint, &pos);
if(sp->v->mtl != nil){
@@ -268,6 +264,8 @@
Point3 pos, lightdir;
double intens;
+ sp->v->n = Vecquat(mulq(mulq(orient, Quatvec(0, sp->v->n)), invq(orient)));
+ sp->v->p = Ptquat(mulq(mulq(orient, Quatvec(0, sp->v->p)), invq(orient)), sp->v->p.w);
pos = model2world(sp->su->entity, sp->v->p);
lightdir = normvec3(subpt3(light.p, pos));
intens = fmax(0, dotvec3(sp->v->n, lightdir));
@@ -313,6 +311,8 @@
Point3
ivshader(VSparams *sp)
{
+ sp->v->n = Vecquat(mulq(mulq(orient, Quatvec(0, sp->v->n)), invq(orient)));
+ sp->v->p = Ptquat(mulq(mulq(orient, Quatvec(0, sp->v->p)), invq(orient)), sp->v->p.w);
return world2clip(maincam, model2world(sp->su->entity, sp->v->p));
}
@@ -468,7 +468,6 @@
maincam->vp->draw(maincam->vp, screenb);
draw(screen, screen->r, bg, nil, ZP);
draw(screen, screen->r, screenb, nil, ZP);
-// drawaxis();
if(showhud)
drawstats();
flushimage(display, 1);
@@ -512,12 +511,22 @@
if((model->tex = readmemimage(fd)) == nil)
sysfatal("readmemimage: %r");
}
- light.p = qrotate(light.p, Vec3(0,1,0), θ+fmod(ω*Δt/1e9, 2*PI));
}
}
}
void
+lmb(void)
+{
+ static Mouse om;
+
+ if((om.buttons^mctl->buttons) == 0)
+ qb(screen->r, om.xy, mctl->xy, &orient, nil);
+
+ om = mctl->Mouse;
+}
+
+void
mmb(void)
{
enum {
@@ -583,6 +592,8 @@
void
mouse(void)
{
+ if((mctl->buttons & 1) != 0)
+ lmb();
if((mctl->buttons & 2) != 0)
mmb();
if((mctl->buttons & 4) != 0)
@@ -732,7 +743,7 @@
void
usage(void)
{
- fprint(2, "usage: %s [-t texture] [-n normals] [-s shader] [-ω yrot] model...\n", argv0);
+ fprint(2, "usage: %s [-t texture] [-n normals] [-s shader] model...\n", argv0);
exits("usage");
}
@@ -754,7 +765,6 @@
case 't': texpath = EARGF(usage()); break;
case 'n': norpath = EARGF(usage()); break;
case 's': sname = EARGF(usage()); break;
- case L'ω': ω = strtod(EARGF(usage()), nil)*DEG; break;
case L'ι': inception++; break;
case 'p': doprof++; break;
default: usage();