shithub: renderfs

Download patch

ref: 0469e025814868bdf409a28b92550aea18c27661
parent: 315776d60f1d50b7560c3d4e830c075a5d3e8828
author: rodri <rgl@antares-labs.eu>
date: Thu Mar 28 18:58:06 EDT 2024

import libgraphics and allow multiple clients to set up rendering environments.

--- a/fs.c
+++ b/fs.c
@@ -4,38 +4,105 @@
 #include <thread.h>
 #include <draw.h>
 #include <memdraw.h>
+#include <geometry.h>
 #include <fcall.h>
 #include <9p.h>
+#include "libobj/obj.h"
+#include "libgraphics/graphics.h"
 
 typedef struct Dirtab Dirtab;
+typedef struct Client Client;
 
 struct Dirtab
 {
 	char *name;
-	uchar type;
-	uint qidpath;
 	uint perm;
 };
 
+struct Client
+{
+	ulong slot;
+	int inuse;
+	Camera *cam;
+};
+
 enum {
 	Qroot,
+	Qnew,
+	Qn,
 	Qctl,
 	Qframe,
+	Qscene,
 };
 
+#define QPATH(type, slot)	(((slot)<<8)|(type))
+#define QTYPE(path)		((path)&0xFF)
+#define SLOT(path)		((path)>>8)
+
 char Ebotch[] = "9P protocol botch";
-char Enotfound[] = "file not found";
+char Enotfound[] = "file does not exist";
 char Enotdir[] = "not a directory";
 char Eperm[] = "permission denied";
 
 Dirtab dirtab[] = {
-	"/",		QTDIR,	Qroot,	0555|DMDIR,
-	"ctl",		QTFILE,	Qctl,	0600,
-	"frame",	QTFILE,	Qframe,	0444,
+ [Qroot]	"/",		DMDIR|0555,
+ [Qnew]		"new",		0666,
+ [Qn]		nil,		DMDIR|0555,
+ [Qctl]		"ctl",		0666,
+ [Qframe]	"frame",	0444,
+ [Qscene]	"scene",	0666,
 };
 char *jefe = "Pablo R. Picasso";
-Memimage *fb;
+Renderer *renderer;
+Client *clients;
+ulong nclients;
 
+static LightSource light = {{0,100,100,1}, {1,1,1,1}, LIGHT_POINT};
+
+static Point3
+vshader(VSparams *sp)
+{
+	Client *c;
+	Point3 pos, lightdir;
+	double intens;
+
+	c = &clients[0];
+
+	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.r = sp->v->mtl->Kd.r;
+		sp->v->c.g = sp->v->mtl->Kd.g;
+		sp->v->c.b = sp->v->mtl->Kd.b;
+		sp->v->c.a = 1;
+	}
+	return world2clip(c->cam, pos);
+}
+
+static Color
+fshader(FSparams *sp)
+{
+	Color tc, c;
+
+	if(sp->v.mtl != nil && sp->v.mtl->map_Kd != nil && sp->v.uv.w != 0)
+		tc = texture(sp->v.mtl->map_Kd, 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 = fclamp(sp->v.c.a*tc.a, 0, 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 auxshaders = {"ident", vshader, fshader};
+
 static int
 mode2perm(int m)
 {
@@ -44,30 +111,89 @@
 	return perms[m&OMASK];
 }
 
+static ulong
+newclient(void)
+{
+	Client *c;
+	int i;
+
+	for(i = 0; i < nclients; i++)
+		if(!clients[i].inuse)
+			return i;
+
+	if(nclients%16 == 0)
+		clients = erealloc9p(clients, (nclients+16)*sizeof(*clients));
+
+	c = &clients[nclients++];
+	c->slot = c-clients;
+	c->inuse = 1;
+	c->cam = emalloc9p(sizeof *c->cam);
+	c->cam->rctl = renderer;
+	placecamera(c->cam, Pt3(0,0,100,1), Pt3(0,0,0,1), Vec3(0,1,0));
+	c->cam->s = newscene(nil);
+	return c->slot;
+}
+
+static Client *
+getclient(ulong slot)
+{
+	if(slot >= nclients)
+		return nil;
+	return &clients[slot];
+}
+
 static void
-fillstat(Dir *dir, Dirtab *d)
+closeclient(Client *c)
 {
-	dir->name = estrdup9p(d->name);
-	dir->uid = estrdup9p(jefe);
-	dir->gid = estrdup9p(jefe);
-	dir->mode = d->perm;
-	dir->length = 0;
-	dir->qid = (Qid){d->qidpath, 0, d->type};
-	dir->atime = time(0);
-	dir->mtime = time(0);
-	dir->muid = estrdup9p("");
+	c->inuse = 0;
 }
 
+static void
+fillstat(Dir *d, uvlong path)
+{
+	Dirtab *t;
+
+	t = &dirtab[QTYPE(path)];
+	if(t->name != nil)
+		d->name = estrdup9p(t->name);
+	else{
+		d->name = smprint("%llud", SLOT(path));
+		if(d->name == nil)
+			sysfatal("smprint: %r");
+	}
+	d->uid = estrdup9p(jefe);
+	d->gid = estrdup9p(jefe);
+	d->muid = nil;
+	d->atime = d->mtime = time(0);
+	d->length = 0;
+	d->mode = t->perm;
+	d->qid = (Qid){path, 0, t->perm>>24};
+}
+
 static int
-dirgen(int n, Dir *dir, void*)
+rootgen(int i, Dir *d, void*)
 {
-	if(++n >= nelem(dirtab))
+	if(++i >= Qn+nclients)
 		return -1;
-	fillstat(dir, &dirtab[n]);
+	fillstat(d, i < Qn? i: QPATH(Qn, i-Qn));
 	return 0;
 }
 
 static int
+clientgen(int i, Dir *d, void *aux)
+{
+	Client *c;
+
+	c = aux;
+	i += Qn+1;
+	if(i < nelem(dirtab)){
+		fillstat(d, QPATH(i, c->slot));
+		return 0;
+	}
+	return -1;
+}
+
+static int
 readimg(Memimage *i, char *t, Rectangle r, int offset, int n)
 {
 	int ww, oo, y, m;
@@ -108,31 +234,79 @@
 		return;
 	}
 
-	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
-	r->fid->qid = r->ofcall.qid;
-	r->fid->aux = nil;
+	r->fid->qid = (Qid){Qroot, 0, QTDIR};
+	r->ofcall.qid = r->fid->qid;
 	respond(r, nil);
 }
 
+char *
+fswalk1(Fid *f, char *name, Qid *qid)
+{
+	char buf[32];
+	uvlong path;
+	ulong n;
+	int i;
+
+	path = f->qid.path;
+	switch(QTYPE(path)){
+	case Qroot:
+		if(strcmp(name, "..") == 0){
+			*qid = f->qid;
+			return nil;
+		}
+		for(i = 1; i <= Qn; i++){
+			if(i == Qn){
+				n = strtoul(name, nil, 10);
+				snprint(buf, sizeof buf, "%lud", n);
+				if(n < nclients && strcmp(buf, name) == 0){
+					*qid = (Qid){QPATH(Qn, n), 0, dirtab[Qn].perm>>24};
+					f->qid = *qid;
+					return nil;
+				}
+				break;
+			}
+			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;
+	case Qn:
+		if(strcmp(name, "..") == 0){
+			*qid = (Qid){Qroot, 0, QTDIR};
+			return nil;
+		}
+		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;
+			}
+		}
+	default:
+		return Enotdir;
+	}
+}
+
 void
 fsopen(Req *r)
 {
-	int i, perm, want;
+	Dirtab *t;
+	uvlong path;
+	int perm, want;
 
-	for(i = 0; i < nelem(dirtab); i++)
-		if(r->fid->qid.path == dirtab[i].qidpath)
-			break;
-
-	if(i < nelem(dirtab)){
-		if(strcmp(r->fid->uid, jefe) == 0)
-			perm = dirtab[i].perm>>6;
-		else
-			perm = dirtab[i].perm;
-	}else{
+	path = r->fid->qid.path;
+	if(QTYPE(path) >= nelem(dirtab)){
 		respond(r, Ebotch);
 		return;
-	};
+	}
+	t = &dirtab[QTYPE(path)];
 
+	perm = t->perm;
+	if(strcmp(r->fid->uid, jefe) == 0)
+		perm >>= 6;
+
 	if((r->ifcall.mode & (OTRUNC|OCEXEC|ORCLOSE)) != 0)
 		goto deny;
 	want = mode2perm(r->ifcall.mode);
@@ -141,6 +315,12 @@
 		respond(r, Eperm);
 		return;
 	}
+
+	if(QTYPE(path) == Qnew){
+		path = QPATH(Qctl, newclient());
+		r->fid->qid.path = path;
+		r->ofcall.qid.path = path;
+	}
 	respond(r, nil);
 }
 
@@ -147,27 +327,37 @@
 void
 fsread(Req *r)
 {
+	Client *c;
 	Memimage *i;
-	char buf[128], cbuf[30], *t;
+	char buf[1024], cbuf[30], *t;
+	uvlong path;
 	ulong off, cnt;
 	int n;
 
+	path = r->fid->qid.path;
 	off = r->ifcall.offset;
 	cnt = r->ifcall.count;
+	c = &clients[SLOT(path)];
 
-	switch(r->fid->qid.path){
+	switch(QTYPE(path)){
 	default:
 		respond(r, "bug in fsread");
 		break;
 	case Qroot:
-		dirread9p(r, dirgen, nil);
+		dirread9p(r, rootgen, nil);
 		respond(r, nil);
 		break;
+	case Qn:
+		dirread9p(r, clientgen, &clients[SLOT(path)]);
+		respond(r, nil);
+		break;
 	case Qctl:
+		snprint(buf, sizeof buf, "%llud", SLOT(path));
+		readstr(r, buf);
 		respond(r, nil);
 		break;
 	case Qframe:
-		i = fb;
+		i = c->cam->vp->getfb(c->cam->vp)->cb;
 		if(off < 5*12){
 			n = snprint(buf, sizeof buf, "%11s %11d %11d %11d %11d ",
 				chantostr(cbuf, i->chan),
@@ -207,6 +397,16 @@
 		}
 		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);
+		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);
+		snprint(buf+n, sizeof(buf)-n, "proj %s\n", c->cam->projtype == PERSPECTIVE? "persp": "ortho");
+		readstr(r, buf);
+		respond(r, nil);
+		break;
 	}
 }
 
@@ -213,13 +413,18 @@
 void
 fswrite(Req *r)
 {
+	Client *c;
+	Model *model;
+	Entity *ent;
 	char *msg, *f[10];
+	uvlong path;
 	ulong cnt, nf;
-	int i;
 
+	path = r->fid->qid.path;
 	cnt = r->ifcall.count;
+	c = &clients[SLOT(path)];
 
-	switch(r->fid->qid.path){
+	switch(QTYPE(path)){
 	default:
 		respond(r, "bug in fswrite");
 		break;
@@ -227,13 +432,67 @@
 		msg = emalloc9p(cnt+1);
 		memmove(msg, r->ifcall.data, cnt);
 		msg[cnt] = 0;
+
 		nf = tokenize(msg, f, nelem(f));
-		for(i = 0; i < nf; i++)
-			fprint(2, "%s[%d]%s%s", i == 0? "": " ", i, f[i], i == nf-1? "\n": "");
+		if(nf == 3 && strcmp(f[0], "viewport") == 0){
+		/* viewport $width $height */
+			if(c->cam->vp != nil)
+				rmviewport(c->cam->vp);
+			c->cam->vp = mkviewport(Rect(0,0,strtoul(f[1], nil, 10),strtoul(f[2], nil, 10)));
+		}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)
+				c->cam->projtype = PERSPECTIVE;
+			else if(strcmp(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)
+				c->cam->fov *= DEG;
+		}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);
+		}else if(nf == 1 && strcmp(f[0], "shoot"))
+		/* shoot */
+			shootcamera(c->cam, &auxshaders);
+
 		free(msg);
 		r->ofcall.count = cnt;
 		respond(r, nil);
 		break;
+	case Qscene:
+		msg = emalloc9p(cnt+1);
+		memmove(msg, r->ifcall.data, cnt);
+		msg[cnt] = 0;
+
+		nf = tokenize(msg, f, nelem(f));
+		if(nf != 1)
+			goto noscene;
+		fprint(2, "loading obj from %s", msg);
+		/* TODO load an actual scene (format tbd) */
+		model = newmodel();
+		if((model->obj = objparse(f[0])) == nil){
+			delmodel(model);
+			goto noscene;
+		}
+		refreshmodel(model);
+		ent = newentity(model);
+		c->cam->s->addent(c->cam->s, ent);
+noscene:
+		free(msg);
+		r->ofcall.count = cnt;
+		respond(r, nil);
+		break;
 	}
 }
 
@@ -240,55 +499,35 @@
 void
 fsstat(Req *r)
 {
-	int i;
-
-	for(i = 0; i < nelem(dirtab); i++)
-		if(r->fid->qid.path == dirtab[i].qidpath){
-			fillstat(&r->d, &dirtab[i]);
-			respond(r, nil);
-			return;
-		}
-	respond(r, Enotfound);	
+	fillstat(&r->d, r->fid->qid.path);
+	respond(r, nil);	
 }
 
-char *
-fswalk1(Fid *f, char *name, Qid *qid)
+void
+fsdestroyfid(Fid *f)
 {
-	int i;
+	uvlong path;
 
-	switch(f->qid.path){
-	case Qroot:
-		if(strcmp(name, "..") == 0){
-			*qid = f->qid;
-			return nil;
-		}
-		for(i = 1; i < nelem(dirtab); i++)
-			if(strcmp(name, dirtab[i].name) == 0){
-				*qid = (Qid){dirtab[i].qidpath, 0, 0};
-				f->qid = *qid;
-				return nil;
-			}
-		return Enotfound;
-	default:
-		return Enotdir;
-	}
+	path = f->qid.path;
+	if(f->omode != -1 && QTYPE(path) >= Qn)
+		closeclient(&clients[SLOT(path)]);
 }
 
-char *
-fsclone(Fid *old, Fid *new)
+void
+fsending(Srv*)
 {
-	USED(old, new);
-	return nil;
+	threadexitsall(nil);
 }
 
 Srv fs = {
-	.attach	= fsattach,
-	.open	= fsopen,
-	.read	= fsread,
-	.write	= fswrite,
-	.stat	= fsstat,
-	.walk1	= fswalk1,
-	.clone	= fsclone,
+	.attach		= fsattach,
+	.walk1		= fswalk1,
+	.open		= fsopen,
+	.read		= fsread,
+	.write		= fswrite,
+	.stat		= fsstat,
+	.destroyfid	= fsdestroyfid,
+	.end		= fsending,
 };
 
 void
@@ -302,10 +541,10 @@
 threadmain(int argc, char *argv[])
 {
 	char *srvname, *mtpt;
-	int fd;
 
 	srvname = "render";
 	mtpt = "/mnt/render";
+	GEOMfmtinstall();
 	ARGBEGIN{
 	case 'D':
 		chatty9p++;
@@ -323,14 +562,11 @@
 
 	jefe = getuser();
 
-	fd = open("/dev/window", OREAD);
-	if(fd < 0)
-		sysfatal("open: %r");
-	fb = readmemimage(fd);
-	if(fb == nil)
-		sysfatal("readmemimage: %r");
-	close(fd);
+	if(memimageinit() != 0)
+		sysfatal("memimageinit: %r");
+	if((renderer = initgraphics()) == nil)
+		sysfatal("initgraphics: %r");
 
 	threadpostmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
-	exits(nil);
+	threadexits(nil);
 }
--- a/mkfile
+++ b/mkfile
@@ -5,7 +5,32 @@
 OFILES=\
 	fs.$O\
 
+LIB=\
+	libobj/libobj.a$O\
+	libgraphics/libgraphics.a$O\
+
 </sys/src/cmd/mkone
+
+libgraphics/libgraphics.a$O:
+	cd libgraphics
+	mk install
+
+libobj/libobj.a$O:
+	cd libobj
+	mk install
+
+pulldeps:VQ:
+	git/clone git://antares-labs.eu/libobj || \
+	git/clone git://shithub.us/rodri/libobj || \
+	git/clone https://github.com/sametsisartenep/libobj
+	git/clone git://antares-labs.eu/libgraphics || \
+	git/clone https://github.com/sametsisartenep/libgraphics
+	@{cd libgraphics; mk $target}
+
+clean nuke:V:
+	rm -f *.[$OS] [$OS].out $TARG
+	@{cd libgraphics; mk $target}
+	@{cd libobj; mk $target}
 
 uninstall:V:
 	rm -f $BIN/$TARG