shithub: renderfs

Download patch

ref: 139309e7c5cb445df58e3f3f252e3545d59558bd
parent: 7ca15d3d92fbe86ba688758f3ef07db3c787d504
author: rodri <rgl@antares-labs.eu>
date: Wed Apr 3 10:24:27 EDT 2024

use lib9p's Cmdtab for ctl messages and sanitize its input.

also provide better camera defaults.

--- a/fs.c
+++ b/fs.c
@@ -27,6 +27,23 @@
 };
 
 enum {
+	CMviewport,
+	CMmove,
+	CMprojection,
+	CMfov,
+	CMclip,
+	CMshoot,
+};
+Cmdtab cmds[] = {
+	CMviewport,	"viewport",	3,	/* viewport $width $height */
+	CMmove,		"move",		5,	/* move $subject $x $y $z */
+	CMprojection,	"projection",	2,	/* projection [persp|ortho] */
+	CMfov,		"fov",		2,	/* fov $angle */
+	CMclip,		"clip",		3,	/* clip [near|far] $dz */
+	CMshoot,	"shoot",	1,	/* shoot */
+};
+
+enum {
 	Qroot,
 	Qnew,
 	Qn,
@@ -43,6 +60,7 @@
 char Enotfound[] = "file does not exist";
 char Enotdir[] = "not a directory";
 char Eperm[] = "permission denied";
+char Enoscene[] = "no scene";
 
 Dirtab dirtab[] = {
  [Qroot]	"/",		DMDIR|0555,
@@ -53,6 +71,7 @@
  [Qscene]	"scene",	0666,
 };
 char *jefe = "Pablo R. Picasso";
+Memsubfont *memfont;
 Renderer *renderer;
 Client *clients;
 ulong nclients;
@@ -66,7 +85,7 @@
 	Point3 pos, lightdir;
 	double intens;
 
-	c = &clients[0];
+	c = &clients[0];	/* TODO figure out a way to address the correct client */
 
 	pos = model2world(sp->su->entity, sp->v->p);
 	lightdir = normvec3(subpt3(light.p, pos));
@@ -111,6 +130,25 @@
 	return perms[m&OMASK];
 }
 
+static long
+strwidth(char *s)
+{
+	long cw, nc, n;
+	Rune r;
+
+	/*
+	 * memfont = defont = vga. so we can safely assume all
+	 * the glyphs have the same width.
+	 */
+	nc = 0;
+	cw = memfont->info->width;
+	while(*s){
+		nc += n = chartorune(&r, s);
+		s += n;
+	}
+	return cw*nc;
+}
+
 static ulong
 newclient(void)
 {
@@ -128,12 +166,15 @@
 	c->slot = c-clients;
 	c->inuse = 1;
 	c->cam = emalloc9p(sizeof *c->cam);
+	c->cam->vp = mkviewport(Rect(0,0,320,200));
 	c->cam->fov = 40*DEG;
 	c->cam->clip.n = 0.01;
 	c->cam->clip.f = 1000;
-	placecamera(c->cam, Pt3(0,0,100,1), Pt3(0,0,0,1), Vec3(0,1,0));
+	c->cam->projtype = PERSPECTIVE;
 	c->cam->rctl = renderer;
 	c->cam->s = newscene(nil);
+	placecamera(c->cam, Pt3(0,0,100,1), Pt3(0,0,0,1), Vec3(0,1,0));
+	reloadcamera(c->cam);
 	return c->slot;
 }
 
@@ -280,13 +321,13 @@
 			*qid = (Qid){Qroot, 0, QTDIR};
 			return nil;
 		}
-		for(i = Qn+1; i < nelem(dirtab); i++){
+		for(i = Qn+1; i < nelem(dirtab); i++)
 			if(strcmp(name, dirtab[i].name) == 0){
 				*qid = (Qid){QPATH(i, SLOT(path)), 0, dirtab[i].perm>>24};
 				f->qid = *qid;
 				return nil;
 			}
-		}
+		return Enotfound;
 	default:
 		return Enotdir;
 	}
@@ -331,6 +372,7 @@
 fsread(Req *r)
 {
 	Client *c;
+	Framebuf *fb;
 	Memimage *i;
 	char buf[1024], cbuf[30], *t;
 	uvlong path;
@@ -360,7 +402,8 @@
 		respond(r, nil);
 		break;
 	case Qframe:
-		i = c->cam->vp->getfb(c->cam->vp)->cb;
+		fb = c->cam->vp->getfb(c->cam->vp);
+		i = fb->cb;
 		if(off < 5*12){
 			n = snprint(buf, sizeof buf, "%11s %11d %11d %11d %11d ",
 				chantostr(cbuf, i->chan),
@@ -401,8 +444,8 @@
 		free(t);
 		break;
 	case Qscene:
-//		readstr(r, "no scenes\n");
-		n = snprint(buf, sizeof buf, "viewport %R\n", c->cam->vp? c->cam->vp->getfb(c->cam->vp)->r: ZR);
+//		snprint(buf, sizeof buf, "%s\n", Enoscene);
+		n = snprint(buf, sizeof buf, "viewport %R\n", c->cam->vp->getfb(c->cam->vp)->r);
 		n += snprint(buf+n, sizeof(buf)-n, "pos %V\n", c->cam->p);
 		n += snprint(buf+n, sizeof(buf)-n, "fov %g°\n", c->cam->fov/DEG);
 		n += snprint(buf+n, sizeof(buf)-n, "clip [%g %g]\n", c->cam->clip.n, c->cam->clip.f);
@@ -419,10 +462,15 @@
 	Client *c;
 	Model *model;
 	Entity *ent;
-	char *msg, *f[10];
+	Framebuf *fb;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	Point pt;
+	char *msg, *f[4];
 	uvlong path;
-	ulong cnt, nf;
-	int w, h;
+	ulong cnt;
+	int nf, w, h;
+	double clipn, clipf;
 
 	path = r->fid->qid.path;
 	cnt = r->ifcall.count;
@@ -437,44 +485,70 @@
 		memmove(msg, r->ifcall.data, cnt);
 		msg[cnt] = 0;
 
-		nf = tokenize(msg, f, nelem(f));
-		if(nf == 3 && strcmp(f[0], "viewport") == 0){
-		/* viewport $width $height */
-			if(c->cam->vp != nil)
-				rmviewport(c->cam->vp);
+		cb = parsecmd(msg, strlen(msg));
+		ct = lookupcmd(cb, cmds, nelem(cmds));
+		if(ct == nil)
+			goto nocmd;
 
-			w = strtoul(f[1], nil, 10);
-			h = strtoul(f[2], nil, 10);
+		switch(ct->index){
+		case CMviewport:
+			w = strtoul(cb->f[1], nil, 10);
+			h = strtoul(cb->f[2], nil, 10);
+			rmviewport(c->cam->vp);
 			c->cam->vp = mkviewport(Rect(0,0,w,h));
-		}else if(nf == 5 && strcmp(f[0], "move") == 0 && strcmp(f[1], "camera") == 0){
-		/* move camera $x $y $z */
-			c->cam->p.x = strtod(f[2], nil);
-			c->cam->p.y = strtod(f[3], nil);
-			c->cam->p.z = strtod(f[4], nil);
-		}else if(nf == 2 && strcmp(f[0], "projection") == 0){
-		/* projection [persp|ortho] */
-			if(strcmp(f[1], "persp") == 0)
+			break;
+		case CMmove:
+			if(strcmp(cb->f[1], "camera") == 0){
+				c->cam->p.x = strtod(cb->f[2], nil);
+				c->cam->p.y = strtod(cb->f[3], nil);
+				c->cam->p.z = strtod(cb->f[4], nil);
+			}
+			break;
+		case CMprojection:
+			if(strcmp(cb->f[1], "persp") == 0)
 				c->cam->projtype = PERSPECTIVE;
-			else if(strcmp(f[1], "ortho") == 0)
+			else if(strcmp(cb->f[1], "ortho") == 0)
 				c->cam->projtype = ORTHOGRAPHIC;
 			reloadcamera(c->cam);
-		}else if(nf == 2 && strcmp(f[0], "fov") == 0){
-		/* fov $angle */
-			c->cam->fov = strtod(f[1], nil);
-			if(utfrune(f[1], L'°') != nil)
+			break;
+		case CMfov:
+			c->cam->fov = strtod(cb->f[1], nil);
+
+			if(utfrune(cb->f[1], L'°') != nil)
 				c->cam->fov *= DEG;
+
+			c->cam->fov = fclamp(c->cam->fov, 1*DEG, 180*DEG);
 			reloadcamera(c->cam);
-		}else if(nf == 3 && strcmp(f[0], "clip") == 0){
-		/* clip [near|far] $dz */
-			if(strcmp(f[1], "near") == 0)
-				c->cam->clip.n = strtod(f[2], nil);
-			else if(strcmp(f[1], "far") == 0)
-				c->cam->clip.f = strtod(f[2], nil);
+			break;
+		case CMclip:
+			clipn = c->cam->clip.n;
+			clipf = c->cam->clip.f;
+
+			if(strcmp(cb->f[1], "near") == 0)
+				clipn = strtod(cb->f[2], nil);
+			else if(strcmp(cb->f[1], "far") == 0)
+				clipf = strtod(cb->f[2], nil);
+
+			if(clipn >= clipf)
+				break;
+
+			c->cam->clip.n = clipn;
+			c->cam->clip.f = clipf;
 			reloadcamera(c->cam);
-		}else if(nf == 1 && strcmp(f[0], "shoot") == 0)
-		/* shoot */
+			break;
+		case CMshoot:
+			if(c->cam->s->nents < 1){
+				fb = c->cam->vp->getfb(c->cam->vp);
+				pt = Pt(Dx(fb->r)/2-strwidth(Enoscene)/2,Dy(fb->r)/2-memfont->height/2);
+				memimagedraw(fb->cb, fb->r, memwhite, ZP, nil, ZP, S);
+				memimagestring(fb->cb, pt, memblack, ZP, memfont, Enoscene);
+				break;
+			}
 			shootcamera(c->cam, &auxshaders);
-
+			break;
+		}
+nocmd:
+		free(cb);
 		free(msg);
 		r->ofcall.count = cnt;
 		respond(r, nil);
@@ -487,7 +561,9 @@
 		nf = tokenize(msg, f, nelem(f));
 		if(nf != 1)
 			goto noscene;
+
 		fprint(2, "loading obj from %s\n", msg);
+
 		/* TODO load an actual scene (format tbd) */
 		model = newmodel();
 		if((model->obj = objparse(f[0])) == nil){
@@ -575,6 +651,7 @@
 		sysfatal("memimageinit: %r");
 	if((renderer = initgraphics()) == nil)
 		sysfatal("initgraphics: %r");
+	memfont = getmemdefont();
 
 	threadpostmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
 	threadexits(nil);