ref: 3000f7986489f01ecda0d20af68dc73c2f074fa9
parent: 5f823efa33ea598dabc3cb584363aa967bdf7b08
author: rodri <rgl@antares-labs.eu>
date: Tue Jan 30 10:12:36 EST 2024
adapted to the new libgraphics and its rendering requirements.
--- a/alloc.c
+++ b/alloc.c
@@ -1,6 +1,7 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
+#include <memdraw.h>
void*
emalloc(ulong n)
@@ -40,5 +41,17 @@
i = allocimage(d, r, chan, repl, col);
if(i == nil)
sysfatal("allocimage: %r");
+ return i;
+}
+
+Memimage *
+eallocmemimage(Rectangle r, ulong chan)
+{
+ Memimage *i;
+
+ i = allocmemimage(r, chan);
+ if(i == nil)
+ sysfatal("allocmemimage: %r");
+ memfillcolor(i, DTransparent);
return i;
}
--- a/dat.h
+++ b/dat.h
@@ -24,5 +24,6 @@
Sfov,
Scampos,
Scambx, Scamby, Scambz,
+ Sfps,
Se
};
--- a/fns.h
+++ b/fns.h
@@ -1,3 +1,4 @@
void *emalloc(ulong);
void *erealloc(void*, ulong);
Image *eallocimage(Display*, Rectangle, ulong, int, ulong);
+Memimage *eallocmemimage(Rectangle, ulong);
--- a/main.c
+++ b/main.c
@@ -2,11 +2,12 @@
#include <libc.h>
#include <thread.h>
#include <draw.h>
+#include <memdraw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>
-#include <graphics.h>
-#include <obj.h>
+#include "libobj/obj.h"
+#include "libgraphics/graphics.h"
#include "dat.h"
#include "fns.h"
@@ -37,14 +38,15 @@
[Kcam3] = KF|4,
[Kscrshot] = KF|12
};
-
char stats[Se][256];
-
+Memimage *screenfb;
Mousectl *mctl;
+Channel *drawc;
int kdown;
-vlong t0, t;
-double Δt;
-char *mdlpath = "../threedee/mdl/rocket.obj";
+OBJ *model;
+Memimage *modeltex;
+Shader *shader;
+double θ, ω;
Camera cams[4], *maincam;
Camcfg camcfgs[4] = {
@@ -51,53 +53,247 @@
2,0,-4,1,
0,0,0,1,
0,1,0,0,
- 90*DEG, 0.1, 100, Ppersp,
+ 90*DEG, 0.1, 100, PERSPECTIVE,
-2,0,-4,1,
0,0,0,1,
0,1,0,0,
- 120*DEG, 0.1, 100, Ppersp,
+ 120*DEG, 0.1, 100, PERSPECTIVE,
-2,0,4,1,
0,0,0,1,
0,1,0,0,
- 90*DEG, 0.1, 100, Ppersp,
+ 90*DEG, 0.1, 100, PERSPECTIVE,
+// -2,0,4,1,
+// 0,0,0,1,
+// 0,1,0,0,
+// 0, 0.1, 100, ORTHOGRAPHIC,
+
2,0,4,1,
0,0,0,1,
0,1,0,0,
- 120*DEG, 0.1, 100, Ppersp
+ 120*DEG, 0.1, 100, PERSPECTIVE
};
+Point3 center = {0,0,0,1};
+Point3 light = {0,1,1,1}; /* global directional light */
-int
-depthcmp(void *a, void *b)
+static int
+min(int a, int b)
{
- Triangle3 *ta, *tb;
- double za, zb;
+ return a < b? a: b;
+}
- ta = (Triangle3*)a;
- tb = (Triangle3*)b;
- za = (ta->p0.z + ta->p1.z + ta->p2.z)/3;
- zb = (tb->p0.z + tb->p1.z + tb->p2.z)/3;
- return zb-za;
+static int
+max(int a, int b)
+{
+ return a > b? a: b;
}
-void
-drawaxis(void)
+//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");
+//}
+
+Point3
+vertshader(VSparams *sp)
{
- 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);
+ *sp->n = qrotate(*sp->n, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI));
+ sp->su->var_intensity[sp->idx] = fmax(0, dotvec3(*sp->n, light));
+ *sp->n = world2vcs(maincam, *sp->n);
+ *sp->p = qrotate(*sp->p, Vec3(0,1,0), θ+fmod(ω*sp->su->uni_time/1e9, 2*PI));
+ *sp->p = ndc2viewport(maincam, world2ndc(maincam, *sp->p));
+ return *sp->p;
+}
- 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");
+Memimage *
+gouraudshader(FSparams *sp)
+{
+ double intens;
+
+ intens = dotvec3(Vec3(sp->su->var_intensity[0], sp->su->var_intensity[1], sp->su->var_intensity[2]), sp->bc);
+ sp->cbuf[1] *= intens;
+ sp->cbuf[2] *= intens;
+ sp->cbuf[3] *= intens;
+ memfillcolor(sp->frag, *(ulong*)sp->cbuf);
+
+ return sp->frag;
}
+Memimage *
+toonshader(FSparams *sp)
+{
+ double intens;
+
+ intens = dotvec3(Vec3(sp->su->var_intensity[0], sp->su->var_intensity[1], sp->su->var_intensity[2]), sp->bc);
+ intens = intens > 0.85? 1: intens > 0.60? 0.80: intens > 0.45? 0.60: intens > 0.30? 0.45: intens > 0.15? 0.30: 0;
+ sp->cbuf[1] = 0;
+ sp->cbuf[2] = 155*intens;
+ sp->cbuf[3] = 255*intens;
+ memfillcolor(sp->frag, *(ulong*)sp->cbuf);
+
+ return sp->frag;
+}
+
+Memimage *
+triangleshader(FSparams *sp)
+{
+ Triangle2 t;
+ Rectangle bbox;
+ Point3 bc;
+ uchar cbuf[4];
+
+ t.p0 = Pt2(240,200,1);
+ t.p1 = Pt2(400,40,1);
+ t.p2 = Pt2(240,40,1);
+
+ bbox = Rect(
+ min(min(t.p0.x, t.p1.x), t.p2.x), min(min(t.p0.y, t.p1.y), t.p2.y),
+ max(max(t.p0.x, t.p1.x), t.p2.x), max(max(t.p0.y, t.p1.y), t.p2.y)
+ );
+ if(!ptinrect(sp->p, bbox))
+ return nil;
+
+ bc = barycoords(t, Pt2(sp->p.x,sp->p.y,1));
+ if(bc.x < 0 || bc.y < 0 || bc.z < 0)
+ return nil;
+
+ cbuf[0] = 0xFF;
+ cbuf[1] = 0xFF*bc.z;
+ cbuf[2] = 0xFF*bc.y;
+ cbuf[3] = 0xFF*bc.x;
+ memfillcolor(sp->frag, *(ulong*)cbuf);
+ return sp->frag;
+}
+
+Memimage *
+circleshader(FSparams *sp)
+{
+ Point2 uv;
+ double r, d;
+ uchar cbuf[4];
+
+ uv = Pt2(sp->p.x,sp->p.y,1);
+ uv.x /= Dx(sp->su->fb->r);
+ uv.y /= Dy(sp->su->fb->r);
+// r = 0.3;
+ r = 0.3*fabs(sin(sp->su->uni_time/1e9));
+ d = vec2len(subpt2(uv, Vec2(0.5,0.5)));
+
+ if(d > r + r*0.05 || d < r - r*0.05)
+ return nil;
+
+ cbuf[0] = 0xFF;
+ cbuf[1] = 0;
+ cbuf[2] = 0xFF*uv.y;
+ cbuf[3] = 0xFF*uv.x;
+
+ memfillcolor(sp->frag, *(ulong*)cbuf);
+ return sp->frag;
+}
+
+/* some shaping functions from The Book of Shaders, Chapter 5 */
+Memimage *
+sfshader(FSparams *sp)
+{
+ Point2 uv;
+ double y, pct;
+ uchar cbuf[4];
+
+ uv = Pt2(sp->p.x,sp->p.y,1);
+ uv.x /= Dx(sp->su->fb->r);
+ uv.y /= Dy(sp->su->fb->r);
+ uv.y = 1 - uv.y; /* make [0 0] the bottom-left corner */
+
+// y = step(0.5, uv.x);
+// y = pow(uv.x, 5);
+// y = sin(uv.x);
+ y = sin(uv.x*sp->su->uni_time/1e8)/2.0 + 0.5;
+// y = smoothstep(0.1, 0.9, uv.x);
+ pct = smoothstep(y-0.02, y, uv.y) - smoothstep(y, y+0.02, uv.y);
+
+ cbuf[0] = 0xFF;
+ cbuf[1] = 0xFF*flerp(y, 0, pct);
+ cbuf[2] = 0xFF*flerp(y, 1, pct);
+ cbuf[3] = 0xFF*flerp(y, 0, pct);
+
+ memfillcolor(sp->frag, *(ulong*)cbuf);
+ return sp->frag;
+}
+
+Memimage *
+boxshader(FSparams *sp)
+{
+ Point2 uv, p;
+ Point2 r;
+ uchar cbuf[4];
+
+ uv = Pt2(sp->p.x,sp->p.y,1);
+ uv.x /= Dx(sp->su->fb->r);
+ uv.y /= Dy(sp->su->fb->r);
+ r = Vec2(0.2,0.4);
+
+ p = Pt2(fabs(uv.x - 0.5), fabs(uv.y - 0.5), 1);
+ p = subpt2(p, r);
+ p.x = fmax(p.x, 0);
+ p.y = fmax(p.y, 0);
+
+ if(vec2len(p) > 0)
+ return nil;
+
+ cbuf[0] = 0xFF;
+ cbuf[1] = 0xFF*smoothstep(0,1,uv.x+uv.y);
+ cbuf[2] = 0xFF*uv.y;
+ cbuf[3] = 0xFF*uv.x;
+
+ memfillcolor(sp->frag, *(ulong*)cbuf);
+ return sp->frag;
+}
+
+Point3
+ivshader(VSparams *sp)
+{
+ return ndc2viewport(maincam, world2ndc(maincam, *sp->p));
+}
+
+Memimage *
+identshader(FSparams *sp)
+{
+ memfillcolor(sp->frag, *(ulong*)sp->cbuf);
+ return sp->frag;
+}
+
+Shader shadertab[] = {
+ { "triangle", ivshader, triangleshader },
+ { "circle", ivshader, circleshader },
+ { "box", ivshader, boxshader },
+ { "sf", ivshader, sfshader },
+ { "gouraud", vertshader, gouraudshader },
+ { "toon", vertshader, toonshader },
+ { "ident", vertshader, identshader },
+};
+Shader *
+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
drawstats(void)
{
@@ -104,11 +300,12 @@
int i;
snprint(stats[Scamno], sizeof(stats[Scamno]), "CAM %lld", maincam-cams+1);
- snprint(stats[Sfov], sizeof(stats[Sfov]), "FOV %g°", maincam->fov);
+ snprint(stats[Sfov], sizeof(stats[Sfov]), "FOV %g°", maincam->fov/DEG);
snprint(stats[Scampos], sizeof(stats[Scampos]), "%V", maincam->p);
snprint(stats[Scambx], sizeof(stats[Scambx]), "bx %V", maincam->bx);
snprint(stats[Scamby], sizeof(stats[Scamby]), "by %V", maincam->by);
snprint(stats[Scambz], sizeof(stats[Scambz]), "bz %V", maincam->bz);
+ snprint(stats[Sfps], sizeof(stats[Sfps]), "FPS %.0f/%.0f/%.0f/%.0f", !maincam->stats.max? 0: 1e9/maincam->stats.max, !maincam->stats.avg? 0: 1e9/maincam->stats.avg, !maincam->stats.min? 0: 1e9/maincam->stats.min, !maincam->stats.v? 0: 1e9/maincam->stats.v);
for(i = 0; i < Se; i++)
string(screen, addpt(screen->r.min, Pt(10,10 + i*font->height)), display->black, ZP, font, stats[i]);
}
@@ -116,62 +313,12 @@
void
redraw(void)
{
-// Triangle3 tmp;
-// static TTriangle3 *vistris;
-// static int nallocvistri;
-// Triangle trit;
-// Point3 n;
-// u8int c;
-// int i, nvistri;
-//
-// nvistri = 0;
-// if(nallocvistri == 0 && vistris == nil){
-// nallocvistri = model.ntri/2;
-// vistris = emalloc(nallocvistri*sizeof(TTriangle3));
-// }
-// for(i = 0; i < model.ntri; i++){
-// /* world to camera */
-// tmp.p0 = world2vcs(maincam, model.tris[i].p0);
-// tmp.p1 = world2vcs(maincam, model.tris[i].p1);
-// tmp.p2 = world2vcs(maincam, model.tris[i].p2);
-// /* back-face culling */
-// n = normvec3(crossvec3(subpt3(tmp.p1, tmp.p0), subpt3(tmp.p2, tmp.p0)));
-// if(dotvec3(n, mulpt3(tmp.p0, -1)) <= 0)
-// continue;
-// /* camera to projected ndc */
-// tmp.p0 = vcs2ndc(maincam, tmp.p0);
-// tmp.p1 = vcs2ndc(maincam, tmp.p1);
-// tmp.p2 = vcs2ndc(maincam, tmp.p2);
-// /* clipping */
-// /*
-// * no clipping for now, the whole triangle is ignored
-// * if any of its vertices gets outside the frustum.
-// */
-// if(isclipping(tmp.p0) || isclipping(tmp.p1) || isclipping(tmp.p2))
-// continue;
-// if(nvistri >= nallocvistri){
-// nallocvistri += model.ntri/3;
-// vistris = erealloc(vistris, nallocvistri*sizeof(TTriangle3));
-// }
-// vistris[nvistri] = (TTriangle3)tmp;
-// c = 0xff*fabs(dotvec3(n, Vec3(0,0,1)));
-// vistris[nvistri].tx = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c<<24|c<<16|c<<8|0xff);
-// nvistri++;
-// }
-// qsort(vistris, nvistri, sizeof(TTriangle3), depthcmp);
+ memfillcolor(screenfb, DWhite);
+ maincam->vp->fbctl->draw(maincam->vp->fbctl, screenfb);
lockdisplay(display);
- draw(maincam->viewport, maincam->viewport->r, display->white, nil, ZP);
- drawaxis();
-// for(i = 0; i < nvistri; i++){
-// /* ndc to screen */
-// trit.p0 = toviewport(maincam, vistris[i].p0);
-// trit.p1 = toviewport(maincam, vistris[i].p1);
-// trit.p2 = toviewport(maincam, vistris[i].p2);
-// filltriangle(maincam->viewport, trit, vistris[i].tx, ZP);
-// triangle(maincam->viewport, trit, 0, display->black, ZP);
-// freeimage(vistris[i].tx);
-// }
+ loadimage(screen, rectaddpt(screenfb->r, screen->r.min), byteaddr(screenfb, screenfb->r.min), bytesperline(screenfb->r, screenfb->depth)*Dy(screenfb->r));
+// drawaxis();
drawstats();
flushimage(display, 1);
unlockdisplay(display);
@@ -178,16 +325,13 @@
}
void
-drawproc(void *drawc)
+drawproc(void *)
{
- Channel *c;
-
threadsetname("drawproc");
- c = drawc;
for(;;){
- send(c, nil);
- sleep(MS2FR);
+ shootcamera(maincam, model, modeltex, shader);
+ nbsend(drawc, nil);
}
}
@@ -211,18 +355,12 @@
void
mouse(void)
{
- if((mctl->buttons & 1) != 0)
- fprint(2, "%v\n", fromviewport(maincam, mctl->xy));
if((mctl->buttons & 8) != 0){
- maincam->fov -= 5;
- if(maincam->fov < 1)
- maincam->fov = 1;
+ maincam->fov = fclamp(maincam->fov - 5*DEG, 1*DEG, 359*DEG);
reloadcamera(maincam);
}
if((mctl->buttons & 16) != 0){
- maincam->fov += 5;
- if(maincam->fov > 359)
- maincam->fov = 359;
+ maincam->fov = fclamp(maincam->fov + 5*DEG, 1*DEG, 359*DEG);
reloadcamera(maincam);
}
}
@@ -235,9 +373,11 @@
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;
@@ -271,6 +411,17 @@
}
void
+keyproc(void *c)
+{
+ threadsetname("keyproc");
+
+ for(;;){
+ nbsend(c, nil);
+ sleep(HZ2MS(100)); /* key poll rate */
+ }
+}
+
+void
handlekeys(void)
{
if(kdown & 1<<K↑)
@@ -316,14 +467,13 @@
if(getwindow(display, Refnone) < 0)
fprint(2, "can't reattach to window\n");
unlockdisplay(display);
- maincam->viewport = screen;
- reloadcamera(maincam);
+ nbsend(drawc, nil);
}
void
usage(void)
{
- fprint(2, "usage: %s [-l objmdl]\n", argv0);
+ fprint(2, "usage: %s [-t texture] [-s shader] model\n", argv0);
exits("usage");
}
@@ -330,58 +480,82 @@
void
threadmain(int argc, char *argv[])
{
- OBJ *objmesh;
- Channel *drawc;
+ Viewport *v;
+ Channel *keyc;
+ char *mdlpath, *texpath, *sname, *p;
int i;
GEOMfmtinstall();
+ texpath = nil;
+ sname = "gouraud";
ARGBEGIN{
+ case 't': texpath = EARGF(usage()); break;
+ case 's': sname = EARGF(usage()); break;
default: usage();
- case 'l':
- mdlpath = EARGF(usage());
- break;
}ARGEND;
- if(argc != 0)
+ if(argc != 1)
usage();
+ mdlpath = argv[0];
+
+ if((shader = getshader(sname)) == nil)
+ sysfatal("couldn't find %s shader", sname);
+
+ if((model = objparse(mdlpath)) == nil)
+ sysfatal("objparse: %r");
+ if(texpath != nil){
+ if((p = strrchr(texpath, '/')) == nil)
+ p = texpath;
+ p = strchr(p, '.');
+ if(p == nil)
+ sysfatal("unknown image file");
+ if(strcmp(++p, "tga") == 0 && (modeltex = readtga(texpath)) == nil)
+ sysfatal("readtga: %r");
+ else if(strcmp(p, "png") == 0 && (modeltex = readpng(texpath)) == nil)
+ sysfatal("readpng: %r");
+ if(modeltex == nil)
+ sysfatal("unknown image file");
+ }
+
if(initdraw(nil, nil, "3d") < 0)
sysfatal("initdraw: %r");
+ if(memimageinit() != 0)
+ sysfatal("memimageinit: %r");
if((mctl = initmouse(nil, screen)) == nil)
sysfatal("initmouse: %r");
+ screenfb = eallocmemimage(rectsubpt(screen->r, screen->r.min), screen->chan);
for(i = 0; i < nelem(cams); i++){
+ v = mkviewport(screenfb->r);
placecamera(&cams[i], camcfgs[i].p, camcfgs[i].lookat, camcfgs[i].up);
- configcamera(&cams[i], screen, camcfgs[i].fov, camcfgs[i].clipn, camcfgs[i].clipf, camcfgs[i].ptype);
+ configcamera(&cams[i], v, camcfgs[i].fov, camcfgs[i].clipn, camcfgs[i].clipf, camcfgs[i].ptype);
}
maincam = &cams[0];
- if((objmesh = objparse(mdlpath)) == nil)
- sysfatal("objparse: %r");
+ light = normvec3(subpt3(light, center));
+ keyc = chancreate(sizeof(void*), 1);
+ drawc = chancreate(sizeof(void*), 1);
display->locking = 1;
unlockdisplay(display);
- drawc = chancreate(1, 0);
- proccreate(drawproc, drawc, 1024);
- proccreate(kbdproc, nil, 4096);
+ proccreate(kbdproc, nil, mainstacksize);
+ proccreate(keyproc, keyc, mainstacksize);
+ proccreate(drawproc, nil, mainstacksize);
- t0 = nsec();
for(;;){
- enum {MOUSE, RESIZE, DRAW};
+ enum {MOUSE, RESIZE, KEY, DRAW};
Alt a[] = {
{mctl->c, &mctl->Mouse, CHANRCV},
{mctl->resizec, nil, CHANRCV},
+ {keyc, nil, CHANRCV},
{drawc, nil, CHANRCV},
- {nil, nil, CHANNOBLK}
+ {nil, nil, CHANEND}
};
switch(alt(a)){
case MOUSE: mouse(); break;
case RESIZE: resize(); break;
+ case KEY: handlekeys(); break;
case DRAW: redraw(); break;
}
- t = nsec();
- Δt = (t-t0)/1e9;
- handlekeys();
- t0 = t;
- sleep(16);
}
}
--- a/mkfile
+++ b/mkfile
@@ -6,25 +6,14 @@
alloc.$O\
main.$O\
-HFILES=libgeometry/geometry.h\
- libgraphics/graphics.h\
- libobj/obj.h\
- dat.h\
- fns.h\
+HFILES=dat.h fns.h
LIB=\
libobj/libobj.a$O\
libgraphics/libgraphics.a$O\
- libgeometry/libgeometry.a$O\
-CFLAGS=$CFLAGS -I. -Ilibgeometry -Ilibgraphics -Ilibobj
-
</sys/src/cmd/mkone
-libgeometry/libgeometry.a$O:
- cd libgeometry
- mk install
-
libgraphics/libgraphics.a$O:
cd libgraphics
mk install
@@ -33,8 +22,14 @@
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
+
clean nuke:V:
rm -f *.[$OS] [$OS].out $TARG
- @{cd libgeometry; mk $target}
@{cd libgraphics; mk $target}
@{cd libobj; mk $target}
--- a/readme
+++ b/readme
@@ -2,9 +2,9 @@
You will need:
-• libgeometry
+• libgeometry (ships with 9front)
• libgraphics
and
• libobj
-Place/bind(1) them to the proper directory slot and mk it.
+`mk pulldeps` should take care of everything.