ref: ef7a0d43dea903cd8c2f52adcc263c437b5a0454
parent: 5dfb42dcc2a66f99de1f736ec12062417700a9c9
author: rodri <rgl@antares-labs.eu>
date: Wed May 22 15:09:52 EDT 2024
began work on a model editor. add two more models.
--- /dev/null
+++ b/mdl/quad.obj
@@ -1,0 +1,10 @@
+v -100 -100 0
+v 100 -100 0
+v -100 100 0
+v 100 100 0
+vt 0 0
+vt 1 0
+vt 0 1
+vt 1 1
+f 1/1 2/2 3/3
+f 4/4 3/3 2/2
--- /dev/null
+++ b/mdl/rocket.obj
@@ -1,0 +1,143 @@
+# Blender v2.79 (sub 0) OBJ File: ''
+# www.blender.org
+v -0.334186 0.372621 -0.333268
+v -0.334186 0.370788 0.335102
+v 0.334186 0.370788 0.335102
+v 0.334186 0.372621 -0.333268
+v -0.334186 -0.295749 -0.335102
+v -0.334186 -0.297582 0.333269
+v 0.334186 -0.297582 0.333268
+v 0.334186 -0.295749 -0.335102
+v -0.334186 -0.695815 -0.336199
+v -0.334186 -0.697648 0.332171
+v 0.334186 -0.695815 -0.336199
+v 0.334186 -0.697648 0.332171
+v -0.120197 -0.754088 -0.122369
+v -0.120197 -0.754748 0.118024
+v 0.120197 -0.754088 -0.122369
+v 0.120197 -0.754748 0.118024
+v -0.306281 -1.117213 -0.309450
+v -0.306281 -1.118893 0.303111
+v 0.306281 -1.117213 -0.309450
+v 0.306281 -1.118893 0.303110
+v -0.289248 0.712766 -0.287397
+v -0.289248 0.711179 0.291097
+v 0.289248 0.712766 -0.287397
+v 0.289248 0.711179 0.291097
+v -0.233218 0.885662 -0.230892
+v -0.233218 0.884382 0.235542
+v 0.233218 0.885662 -0.230893
+v 0.233218 0.884382 0.235542
+v -0.165619 1.041305 -0.162866
+v -0.165619 1.040397 0.168371
+v 0.165619 1.041305 -0.162866
+v 0.165619 1.040397 0.168370
+v -0.109259 1.126811 -0.106272
+v -0.109259 1.126212 0.112246
+v 0.109259 1.126811 -0.106272
+v 0.109259 1.126212 0.112246
+v -0.052090 1.177090 -0.048964
+v -0.052090 1.176804 0.055215
+v 0.052090 1.177090 -0.048964
+v 0.052090 1.176804 0.055215
+v -0.026259 1.194980 -0.023084
+v -0.026259 1.194836 0.029434
+v 0.026259 1.194980 -0.023084
+v 0.026259 1.194836 0.029434
+v -0.010547 1.200304 -0.007358
+v -0.010547 1.200246 0.013737
+v 0.010547 1.200304 -0.007358
+v 0.010547 1.200246 0.013737
+s off
+f 1 22 21
+f 7 10 12
+f 1 6 2
+f 2 7 3
+f 3 8 4
+f 5 4 8
+f 11 16 15
+f 6 9 10
+f 8 12 11
+f 5 11 9
+f 13 19 17
+f 9 15 13
+f 12 14 16
+f 10 13 14
+f 17 20 18
+f 16 18 20
+f 14 17 18
+f 15 20 19
+f 24 27 28
+f 3 23 24
+f 2 24 22
+f 4 21 23
+f 26 32 30
+f 23 25 27
+f 22 28 26
+f 21 26 25
+f 30 36 34
+f 25 30 29
+f 28 31 32
+f 27 29 31
+f 36 39 40
+f 29 34 33
+f 32 35 36
+f 31 33 35
+f 38 44 42
+f 35 37 39
+f 34 40 38
+f 33 38 37
+f 41 46 45
+f 37 42 41
+f 40 43 44
+f 39 41 43
+f 45 48 47
+f 44 47 48
+f 43 45 47
+f 42 48 46
+f 1 2 22
+f 7 6 10
+f 1 5 6
+f 2 6 7
+f 3 7 8
+f 5 1 4
+f 11 12 16
+f 6 5 9
+f 8 7 12
+f 5 8 11
+f 13 15 19
+f 9 11 15
+f 12 10 14
+f 10 9 13
+f 17 19 20
+f 16 14 18
+f 14 13 17
+f 15 16 20
+f 24 23 27
+f 3 4 23
+f 2 3 24
+f 4 1 21
+f 26 28 32
+f 23 21 25
+f 22 24 28
+f 21 22 26
+f 30 32 36
+f 25 26 30
+f 28 27 31
+f 27 25 29
+f 36 35 39
+f 29 30 34
+f 32 31 35
+f 31 29 33
+f 38 40 44
+f 35 33 37
+f 34 36 40
+f 33 34 38
+f 41 42 46
+f 37 38 42
+f 40 39 43
+f 39 37 41
+f 45 46 48
+f 44 43 47
+f 43 41 45
+f 42 44 48
--- /dev/null
+++ b/med.c
@@ -1,0 +1,776 @@
+#include <u.h>
+#include <libc.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,
+ Kfrustum,
+ Ke
+};
+
+enum {
+ Sfov,
+ Scampos,
+ Scambx, Scamby, Scambz,
+ Sfps,
+ Sframes,
+ Se
+};
+
+typedef struct Camcfg Camcfg;
+struct Camcfg
+{
+ Point3 p, lookat, up;
+ double fov, clipn, clipf;
+ int ptype;
+};
+
+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',
+ [Kfrustum] = ' ',
+};
+char stats[Se][256];
+Image *screenb;
+Mousectl *mctl;
+Keyboardctl *kctl;
+Channel *drawc;
+int kdown;
+Scene *scene;
+Entity *subject;
+Model *model;
+Shadertab *shader;
+QLock scenelk;
+Mouse om;
+Quaternion orient = {1,0,0,0};
+
+Camera cam;
+Camcfg camcfg = {
+ 0,2,4,1,
+ 0,0,0,1,
+ 0,1,0,0,
+ 40*DEG, 0.01, 10, PERSPECTIVE
+};
+Point3 center = {0,0,0,1};
+LightSource light; /* global point light */
+
+static int doprof;
+static int showhud;
+Color (*tsampler)(Memimage*,Point2);
+
+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 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);
+}
+
+void
+materializefrustum(void)
+{
+ Primitive l;
+ Framebuf *fb;
+ Point3 p[4];
+ int i;
+
+ fb = cam.vp->getfb(cam.vp);
+ p[0] = Pt3(0,0,1,1);
+ p[1] = Pt3(Dx(fb->r),0,1,1);
+ p[2] = Pt3(Dx(fb->r),Dy(fb->r),1,1);
+ p[3] = Pt3(0,Dy(fb->r),1,1);
+ memset(&l, 0, sizeof l);
+
+ for(i = 0; i < nelem(p); i++){
+ /* front frame */
+ l.type = PLine;
+ l.v[0].p = viewport2world(&cam, p[i]);
+ l.v[1].p = viewport2world(&cam, p[(i+1)%nelem(p)]);
+ qlock(&scenelk);
+ model->prims = erealloc(model->prims, ++model->nprims*sizeof(*model->prims));
+ model->prims[model->nprims-1] = l;
+ qunlock(&scenelk);
+
+ /* middle frame */
+ l.v[0].p = viewport2world(&cam, subpt3(p[i], Vec3(0,0,0.5)));
+ l.v[1].p = viewport2world(&cam, subpt3(p[(i+1)%nelem(p)], Vec3(0,0,0.5)));
+ qlock(&scenelk);
+ model->prims = erealloc(model->prims, ++model->nprims*sizeof(*model->prims));
+ model->prims[model->nprims-1] = l;
+ qunlock(&scenelk);
+
+ /* back frame */
+ l.v[0].p = viewport2world(&cam, subpt3(p[i], Vec3(0,0,1)));
+ l.v[1].p = viewport2world(&cam, subpt3(p[(i+1)%nelem(p)], Vec3(0,0,1)));
+ qlock(&scenelk);
+ model->prims = erealloc(model->prims, ++model->nprims*sizeof(*model->prims));
+ model->prims[model->nprims-1] = l;
+ qunlock(&scenelk);
+
+ /* struts */
+ l.v[1].p = viewport2world(&cam, p[i]);
+ qlock(&scenelk);
+ model->prims = erealloc(model->prims, ++model->nprims*sizeof(*model->prims));
+ model->prims[model->nprims-1] = l;
+ qunlock(&scenelk);
+ }
+}
+
+void
+addcube(void)
+{
+ static Point3 axis[3] = {{0,1,0,0}, {1,0,0,0}, {0,0,1,0}};
+ Primitive t[2];
+ Point3 p, v1, v2;
+ int i, j, k;
+
+ memset(t, 0, sizeof t);
+ t[0].type = t[1].type = PTriangle;
+
+ p = Vec3(-0.5,-0.5,0.5);
+ v1 = Vec3(1,0,0);
+ v2 = Vec3(0,1,0);
+ t[0].v[0].p = addpt3(center, p);
+ t[0].v[1].p = addpt3(center, addpt3(p, v1));
+ t[0].v[2].p = addpt3(center, addpt3(p, addpt3(v1, v2)));
+ t[1].v[0].p = t[0].v[0].p;
+ t[1].v[1].p = t[0].v[2].p;
+ t[1].v[2].p = addpt3(center, addpt3(p, v2));
+
+ for(i = 0; i < 6; i++){
+ for(j = 0; j < 2; j++)
+ for(k = 0; k < 3; k++){
+ if(i > 0)
+ t[j].v[k].p = qrotate(t[j].v[k].p, axis[i%3], PI/2);
+ t[j].v[k].n = normvec3(subpt3(t[j].v[k].p, center));
+ }
+
+ qlock(&scenelk);
+ model->prims = erealloc(model->prims, (model->nprims += 2)*sizeof(*model->prims));
+ model->prims[model->nprims-2] = t[0];
+ model->prims[model->nprims-1] = t[1];
+ qunlock(&scenelk);
+ }
+}
+
+Point3
+gouraudvshader(VSparams *sp)
+{
+ static double Ka = 0.1; /* ambient factor */
+ static double Ks = 0.5; /* specular factor */
+ double Kd; /* diffuse factor */
+ double spec;
+ Point3 pos, lightdir, lookdir;
+ Material m;
+ Color ambient, diffuse, specular;
+
+ 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;
+
+ ambient = mulpt3(light.c, Ka);
+ ambient.r *= m.ambient.r;
+ ambient.g *= m.ambient.g;
+ ambient.b *= m.ambient.b;
+ ambient.a *= m.ambient.a;
+
+ lightdir = normvec3(subpt3(light.p, pos));
+ Kd = fmax(0, dotvec3(sp->v->n, lightdir));
+ diffuse = mulpt3(light.c, Kd);
+ diffuse.r *= m.diffuse.r;
+ diffuse.g *= m.diffuse.g;
+ diffuse.b *= m.diffuse.b;
+ diffuse.a *= m.diffuse.a;
+
+ lookdir = normvec3(subpt3(cam.p, pos));
+ lightdir = qrotate(lightdir, sp->v->n, PI);
+ spec = pow(fmax(0, dotvec3(lookdir, lightdir)), m.shininess);
+ specular = mulpt3(light.c, spec*Ks);
+ specular.r *= m.specular.r;
+ specular.g *= m.specular.g;
+ specular.b *= m.specular.b;
+ specular.a *= m.specular.a;
+
+ sp->v->c = addpt3(ambient, addpt3(diffuse, specular));
+ }
+ return world2clip(&cam, pos);
+}
+
+Color
+gouraudshader(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, tsampler);
+ }else if(sp->su->entity->mdl->tex != nil && sp->v.uv.w != 0)
+ tc = texture(sp->su->entity->mdl->tex, sp->v.uv, tsampler);
+ 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;
+}
+
+Point3
+phongvshader(VSparams *sp)
+{
+ Point3 pos;
+ Color a, d, s;
+ double ss;
+
+ 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){
+ a = sp->v->mtl->ambient;
+ d = sp->v->mtl->diffuse;
+ s = sp->v->mtl->specular;
+ ss = sp->v->mtl->shininess;
+ addvattr(sp->v, "ambient", VAPoint, &a);
+ addvattr(sp->v, "diffuse", VAPoint, &d);
+ addvattr(sp->v, "specular", VAPoint, &s);
+ addvattr(sp->v, "shininess", VANumber, &ss);
+ }
+ return world2clip(&cam, pos);
+}
+
+Color
+phongshader(FSparams *sp)
+{
+ static double Ka = 0.1; /* ambient factor */
+ static double Ks = 0.5; /* specular factor */
+ double Kd; /* diffuse factor */
+ double spec;
+ Color ambient, diffuse, specular, tc, c;
+ Point3 pos, lookdir, lightdir;
+ Material m;
+ Vertexattr *va;
+
+ va = getvattr(&sp->v, "pos");
+ pos = va->p;
+
+ va = getvattr(&sp->v, "ambient");
+ m.ambient = va != nil? va->p: Pt3(1,1,1,1);
+ va = getvattr(&sp->v, "diffuse");
+ m.diffuse = va != nil? va->p: Pt3(1,1,1,1);
+ va = getvattr(&sp->v, "specular");
+ m.specular = va != nil? va->p: Pt3(1,1,1,1);
+ va = getvattr(&sp->v, "shininess");
+ m.shininess = va != nil? va->n: 1;
+
+ ambient = mulpt3(light.c, Ka);
+ ambient.r *= m.ambient.r;
+ ambient.g *= m.ambient.g;
+ ambient.b *= m.ambient.b;
+ ambient.a *= m.ambient.a;
+
+ lightdir = normvec3(subpt3(light.p, pos));
+ Kd = fmax(0, dotvec3(sp->v.n, lightdir));
+ diffuse = mulpt3(light.c, Kd);
+ diffuse.r *= m.diffuse.r;
+ diffuse.g *= m.diffuse.g;
+ diffuse.b *= m.diffuse.b;
+ diffuse.a *= m.diffuse.a;
+
+ lookdir = normvec3(subpt3(cam.p, pos));
+ lightdir = qrotate(lightdir, sp->v.n, PI);
+ spec = pow(fmax(0, dotvec3(lookdir, lightdir)), m.shininess);
+ specular = mulpt3(light.c, spec*Ks);
+ specular.r *= m.specular.r;
+ specular.g *= m.specular.g;
+ specular.b *= m.specular.b;
+ specular.a *= m.specular.a;
+
+ if(sp->v.mtl != nil && sp->v.mtl->diffusemap != nil && sp->v.uv.w != 0)
+ tc = texture(sp->v.mtl->diffusemap, sp->v.uv, tsampler);
+ else if(sp->su->entity->mdl->tex != nil && sp->v.uv.w != 0)
+ tc = texture(sp->su->entity->mdl->tex, sp->v.uv, tsampler);
+ else
+ tc = Pt3(1,1,1,1);
+
+ c = addpt3(ambient, addpt3(diffuse, specular));
+ c.a = 1;
+ c.b = fclamp(c.b*tc.b, 0, 1);
+ c.g = fclamp(c.g*tc.g, 0, 1);
+ c.r = fclamp(c.r*tc.r, 0, 1);
+
+ return c;
+}
+
+Point3
+identvshader(VSparams *sp)
+{
+ 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));
+ addvattr(sp->v, "intensity", VANumber, &intens);
+ if(sp->v->mtl != nil)
+ sp->v->c = sp->v->mtl->diffuse;
+ return world2clip(&cam, 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, tsampler);
+ else if(sp->su->entity->mdl->tex != nil && sp->v.uv.w != 0)
+ tc = texture(sp->su->entity->mdl->tex, sp->v.uv, tsampler);
+ 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 shadertab[] = {
+ { "ident", identvshader, identshader },
+ { "gouraud", gouraudvshader, gouraudshader },
+ { "phong", phongvshader, phongshader },
+};
+Shadertab *
+getshader(char *name)
+{
+ int i;
+
+ for(i = 0; i < nelem(shadertab); i++)
+ if(strcmp(shadertab[i].name, name) == 0)
+ return &shadertab[i];
+ return nil;
+}
+
+void
+zoomin(void)
+{
+ cam.fov = fclamp(cam.fov - 1*DEG, 1*DEG, 180*DEG);
+ reloadcamera(&cam);
+}
+
+void
+zoomout(void)
+{
+ cam.fov = fclamp(cam.fov + 1*DEG, 1*DEG, 180*DEG);
+ reloadcamera(&cam);
+}
+
+void
+drawstats(void)
+{
+ int i;
+
+ snprint(stats[Sfov], sizeof(stats[Sfov]), "FOV %g°", cam.fov/DEG);
+ snprint(stats[Scampos], sizeof(stats[Scampos]), "%V", cam.p);
+ snprint(stats[Scambx], sizeof(stats[Scambx]), "bx %V", cam.bx);
+ snprint(stats[Scamby], sizeof(stats[Scamby]), "by %V", cam.by);
+ snprint(stats[Scambz], sizeof(stats[Scambz]), "bz %V", cam.bz);
+ snprint(stats[Sfps], sizeof(stats[Sfps]), "FPS %.0f/%.0f/%.0f/%.0f", !cam.stats.max? 0: 1e9/cam.stats.max, !cam.stats.avg? 0: 1e9/cam.stats.avg, !cam.stats.min? 0: 1e9/cam.stats.min, !cam.stats.v? 0: 1e9/cam.stats.v);
+ snprint(stats[Sframes], sizeof(stats[Sframes]), "frame %llud", cam.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)
+{
+ static Image *bg;
+
+ if(bg == nil)
+ bg = eallocimage(display, UR, RGB24, 1, 0x888888FF);
+
+ lockdisplay(display);
+ cam.vp->draw(cam.vp, screenb);
+ draw(screen, screen->r, bg, nil, ZP);
+ draw(screen, screen->r, screenb, nil, ZP);
+ if(showhud)
+ drawstats();
+ flushimage(display, 1);
+ unlockdisplay(display);
+}
+
+void
+drawproc(void *)
+{
+ uvlong t0, Δt;
+
+ threadsetname("drawproc");
+
+ t0 = nsec();
+ for(;;){
+ qlock(&scenelk);
+ shootcamera(&cam, shader);
+ qunlock(&scenelk);
+ if(doprof)
+ fprint(2, "R %llud %llud\nE %llud %llud\nT %llud %llud\nr %llud %llud\n\n",
+ cam.times.R[cam.times.cur-1].t0, cam.times.R[cam.times.cur-1].t1,
+ cam.times.E[cam.times.cur-1].t0, cam.times.E[cam.times.cur-1].t1,
+ cam.times.Tn[cam.times.cur-1].t0, cam.times.Tn[cam.times.cur-1].t1,
+ cam.times.Rn[cam.times.cur-1].t0, cam.times.Rn[cam.times.cur-1].t1);
+ Δt = nsec() - t0;
+ if(Δt > HZ2MS(60)*1000000ULL){
+ nbsend(drawc, nil);
+ t0 += Δt;
+ }
+ }
+}
+
+void
+lmb(void)
+{
+ if((om.buttons^mctl->buttons) == 0)
+ qball(screen->r, om.xy, mctl->xy, &orient, nil);
+}
+
+void
+mmb(void)
+{
+ enum {
+ TSNEAREST,
+ TSBILINEAR,
+ SP,
+ QUIT,
+ };
+ static char *items[] = {
+ [TSNEAREST] "use nearest sampler",
+ [TSBILINEAR] "use bilinear sampler",
+ [SP] "",
+ [QUIT] "quit",
+ nil,
+ };
+ static Menu menu = { .item = items };
+
+ switch(menuhit(2, mctl, &menu, _screen)){
+ case TSNEAREST:
+ tsampler = neartexsampler;
+ break;
+ case TSBILINEAR:
+ tsampler = bilitexsampler;
+ break;
+ case QUIT:
+ threadexitsall(nil);
+ }
+ nbsend(drawc, nil);
+}
+
+static char *
+genrmbmenuitem(int idx)
+{
+ if(idx < nelem(shadertab))
+ return shadertab[idx].name;
+ else if(idx == nelem(shadertab))
+ return "";
+ else if(idx == nelem(shadertab)+1)
+ return "add cube";
+ return nil;
+}
+
+void
+rmb(void)
+{
+ static Menu menu = { .gen = genrmbmenuitem };
+ int idx;
+
+ idx = menuhit(3, mctl, &menu, _screen);
+ if(idx < 0)
+ return;
+ if(idx < nelem(shadertab)){
+ shader = &shadertab[idx];
+ memset(&cam.stats, 0, sizeof(cam.stats));
+ }else if(idx == nelem(shadertab)+1)
+ addcube();
+ nbsend(drawc, nil);
+}
+
+void
+mouse(void)
+{
+ if((mctl->buttons & 1) != 0)
+ lmb();
+ if((mctl->buttons & 2) != 0)
+ mmb();
+ if((mctl->buttons & 4) != 0)
+ rmb();
+ 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(&cam, subpt3(cam.p, mulpt3(cam.bz, 0.1)), cam.bz, cam.by);
+ if(kdown & 1<<K↓)
+ placecamera(&cam, addpt3(cam.p, mulpt3(cam.bz, 0.1)), cam.bz, cam.by);
+ if(kdown & 1<<K←)
+ placecamera(&cam, subpt3(cam.p, mulpt3(cam.bx, 0.1)), cam.bz, cam.by);
+ if(kdown & 1<<K→)
+ placecamera(&cam, addpt3(cam.p, mulpt3(cam.bx, 0.1)), cam.bz, cam.by);
+ if(kdown & 1<<Krise)
+ placecamera(&cam, addpt3(cam.p, mulpt3(cam.by, 0.1)), cam.bz, cam.by);
+ if(kdown & 1<<Kfall)
+ placecamera(&cam, subpt3(cam.p, mulpt3(cam.by, 0.1)), cam.bz, cam.by);
+ if(kdown & 1<<KR↑)
+ aimcamera(&cam, qrotate(cam.bz, cam.bx, 1*DEG));
+ if(kdown & 1<<KR↓)
+ aimcamera(&cam, qrotate(cam.bz, cam.bx, -1*DEG));
+ if(kdown & 1<<KR←)
+ aimcamera(&cam, qrotate(cam.bz, cam.by, 1*DEG));
+ if(kdown & 1<<KR→)
+ aimcamera(&cam, qrotate(cam.bz, cam.by, -1*DEG));
+ if(kdown & 1<<KR↺)
+ placecamera(&cam, cam.p, cam.bz, qrotate(cam.by, cam.bz, 1*DEG));
+ if(kdown & 1<<KR↻)
+ placecamera(&cam, cam.p, cam.bz, qrotate(cam.by, cam.bz, -1*DEG));
+ if(kdown & 1<<Kzoomin)
+ zoomin();
+ if(kdown & 1<<Kzoomout)
+ zoomout();
+
+ if((okdown & 1<<Khud) == 0 && (kdown & 1<<Khud) != 0)
+ showhud ^= 1;
+
+ if((okdown & 1<<Kfrustum) == 0 && (kdown & 1<<Kfrustum) != 0)
+ materializefrustum();
+
+ 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;
+
+ GEOMfmtinstall();
+ ARGBEGIN{
+ case 'p': doprof++; break;
+ default: usage();
+ }ARGEND;
+ if(argc != 0)
+ usage();
+
+ confproc();
+
+ if((shader = getshader("gouraud")) == nil)
+ sysfatal("couldn't find main shader");
+
+ scene = newscene(nil);
+ model = newmodel();
+ subject = newentity(model);
+ scene->addent(scene, subject);
+
+ if(memimageinit() != 0)
+ sysfatal("memimageinit: %r");
+ if((rctl = initgraphics()) == nil)
+ sysfatal("initgraphics: %r");
+ if(initdraw(nil, nil, "med") < 0)
+ sysfatal("initdraw: %r");
+ if((mctl = initmouse(nil, screen)) == nil)
+ sysfatal("initmouse: %r");
+
+ screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), RGBA32, 0, DNofill);
+ v = mkviewport(screenb->r);
+ placecamera(&cam, camcfg.p, camcfg.lookat, camcfg.up);
+ configcamera(&cam, v, camcfg.fov, camcfg.clipn, camcfg.clipf, camcfg.ptype);
+ cam.s = scene;
+ cam.rctl = rctl;
+ light.p = Pt3(0,100,100,1);
+ light.c = Pt3(1,1,1,1);
+ light.type = LIGHT_POINT;
+ tsampler = neartexsampler;
+
+ 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;
+ }
+ }
+}
--- a/mkfile
+++ b/mkfile
@@ -3,7 +3,9 @@
BIN=$home/bin/$objtype
TARG=\
vis\
+ med\
solar\
+ projtest\
OFILES=\
alloc.$O\
--- /dev/null
+++ b/projtest.c
@@ -1,0 +1,103 @@
+#include <u.h>
+#include <libc.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"
+
+Camera cam;
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s\n", argv0);
+ exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ Point3 np, fp;
+ Framebuf *fb;
+
+ GEOMfmtinstall();
+ ARGBEGIN{
+ default: usage();
+ }ARGEND
+ if(argc != 0)
+ usage();
+
+ if(memimageinit() != 0)
+ sysfatal("memimageinit: %r");
+
+ placecamera(&cam, Pt3(0,0,1,1), Vec3(0,0,0), Vec3(0,1,0));
+ configcamera(&cam, mkviewport(Rect(0,0,640,480)), 40*DEG, 0.01, 10, PERSPECTIVE);
+
+ fb = cam.vp->getfb(cam.vp);
+ np = Pt3(0,0,-0.01,1);
+ fp = Pt3(0,0,-10,1);
+ fprint(2, "near %V\nfar %V\n", np, fp);
+ np = vcs2clip(&cam, np);
+ fp = vcs2clip(&cam, fp);
+ fprint(2, "E → C\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = clip2ndc(np);
+ fp = clip2ndc(fp);
+ fprint(2, "C → N\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = ndc2viewport(fb, np);
+ fp = ndc2viewport(fb, fp);
+ fprint(2, "N → V\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = viewport2ndc(fb, np);
+ fp = viewport2ndc(fb, fp);
+ fprint(2, "V → N\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = ndc2vcs(&cam, np);
+ fp = ndc2vcs(&cam, fp);
+ fprint(2, "N → E\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+
+ fprint(2, "\n");
+
+ np = Pt3(Dx(fb->r)/2.0,Dy(fb->r)/2.0,1,1);
+ fp = Pt3(Dx(fb->r)/2.0,Dy(fb->r)/2.0,0,1);
+ fprint(2, "near %V\nfar %V\n", np, fp);
+ np = viewport2ndc(fb, np);
+ fp = viewport2ndc(fb, fp);
+ fprint(2, "V → N\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = ndc2vcs(&cam, np);
+ fp = ndc2vcs(&cam, fp);
+ fprint(2, "N → E\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = vcs2clip(&cam, np);
+ fp = vcs2clip(&cam, fp);
+ fprint(2, "E → C\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = clip2ndc(np);
+ fp = clip2ndc(fp);
+ fprint(2, "C → N\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+ np = ndc2viewport(fb, np);
+ fp = ndc2viewport(fb, fp);
+ fprint(2, "N → V\n");
+ fprint(2, "near %V\n", np);
+ fprint(2, "far %V\n", fp);
+
+ exits(nil);
+}