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