ref: 37da5f0a27740bb43bac67a2eb98f725beca975e
parent: df792cbb5cf94abf0d286ebfd73bad9714dce509
author: rodri <rgl@antares-labs.eu>
date: Tue Jan 30 07:01:01 EST 2024
import the new renderer and clean things up. i integrated the renderer i've been developing on the tinyrend repo and got rid of a bunch of stuff that's no longer necessary. also began structuring things to fit the new interface i have in mind. there are still some artifacts with the projection xforms that cause issues with clipping and division by zero.
--- /dev/null
+++ b/alloc.c
@@ -1,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
+
+void *
+emalloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("malloc: %r");
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+void *
+erealloc(void *p, ulong n)
+{
+ void *np;
+
+ np = realloc(p, n);
+ if(np == nil){
+ if(n == 0)
+ return nil;
+ sysfatal("realloc: %r");
+ }
+ if(p == nil)
+ setmalloctag(np, getcallerpc(&p));
+ else
+ setrealloctag(np, getcallerpc(&p));
+ return np;
+}
+
+Memimage *
+eallocmemimage(Rectangle r, ulong chan)
+{
+ Memimage *i;
+
+ i = allocmemimage(r, chan);
+ if(i == nil)
+ sysfatal("allocmemimage: %r");
+ memfillcolor(i, DTransparent);
+ return i;
+}
--- a/camera.c
+++ b/camera.c
@@ -1,32 +1,41 @@
#include <u.h>
#include <libc.h>
+#include <thread.h>
#include <draw.h>
+#include <memdraw.h>
#include <geometry.h>
-#include <graphics.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
-static int
-max(int a, int b)
+static void
+updatestats(Camera *c, uvlong v)
{
- return a > b ? a : b;
+ c->stats.v = v;
+ c->stats.n++;
+ c->stats.acc += v;
+ c->stats.avg = c->stats.acc/c->stats.n;
+ c->stats.min = v < c->stats.min || c->stats.n == 1? v: c->stats.min;
+ c->stats.max = v > c->stats.max || c->stats.n == 1? v: c->stats.max;
}
static void
verifycfg(Camera *c)
{
- assert(c->viewport != nil);
- if(c->ptype == Ppersp)
+ assert(c->vp != nil);
+ if(c->projtype == PERSPECTIVE)
assert(c->fov > 0 && c->fov < 360*DEG);
assert(c->clip.n > 0 && c->clip.n < c->clip.f);
}
void
-configcamera(Camera *c, Image *v, double fov, double n, double f, Projection p)
+configcamera(Camera *c, Viewport *v, double fov, double n, double f, Projection p)
{
- c->viewport = v;
+ c->vp = v;
c->fov = fov;
c->clip.n = n;
c->clip.f = f;
- c->ptype = p;
+ c->projtype = p;
reloadcamera(c);
}
@@ -55,23 +64,37 @@
double l, r, b, t;
verifycfg(c);
- switch(c->ptype){
- case Portho:
+ switch(c->projtype){
+ case ORTHOGRAPHIC:
/*
- r = Dx(c->viewport->r)/2;
- t = Dy(c->viewport->r)/2;
+ r = Dx(c->vp->fbctl->fb[0]->r)/2;
+ t = Dy(c->vp->fbctl->fb[0]->r)/2;
l = -r;
b = -t;
*/
l = t = 0;
- r = Dx(c->viewport->r);
- b = Dy(c->viewport->r);
+ r = Dx(c->vp->fbctl->fb[0]->r);
+ b = Dy(c->vp->fbctl->fb[0]->r);
orthographic(c->proj, l, r, b, t, c->clip.n, c->clip.f);
break;
- case Ppersp:
- a = (double)Dx(c->viewport->r)/Dy(c->viewport->r);
+ case PERSPECTIVE:
+ a = (double)Dx(c->vp->fbctl->fb[0]->r)/Dy(c->vp->fbctl->fb[0]->r);
perspective(c->proj, c->fov, a, c->clip.n, c->clip.f);
break;
default: sysfatal("unknown projection type");
}
+}
+
+void
+shootcamera(Camera *c, OBJ *m, Memimage *tex, Shader *s)
+{
+ uvlong t0, t1;
+
+ c->vp->fbctl->reset(c->vp->fbctl);
+ t0 = nanosec();
+ shade(c->vp->fbctl->fb[c->vp->fbctl->idx^1], m, tex, s, 1); /* address the back buffer */
+ t1 = nanosec();
+ c->vp->fbctl->swap(c->vp->fbctl);
+
+ updatestats(c, t1-t0);
}
--- /dev/null
+++ b/fb.c
@@ -1,0 +1,81 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
+
+static void
+framebufctl_draw(Framebufctl *ctl, Memimage *dst)
+{
+ lock(&ctl->swplk);
+ memimagedraw(dst, dst->r, ctl->fb[ctl->idx]->cb, ZP, nil, ZP, SoverD);
+ unlock(&ctl->swplk);
+}
+
+static void
+framebufctl_swap(Framebufctl *ctl)
+{
+ lock(&ctl->swplk);
+ ctl->idx ^= 1;
+ unlock(&ctl->swplk);
+}
+
+static void
+framebufctl_reset(Framebufctl *ctl)
+{
+ Framebuf *fb;
+
+ /* address the back buffer—resetting the front buffer is VERBOTEN */
+ fb = ctl->fb[ctl->idx^1];
+ memsetd(fb->zbuf, Inf(-1), Dx(fb->r)*Dy(fb->r));
+ memfillcolor(fb->cb, DTransparent);
+}
+
+Framebuf *
+mkfb(Rectangle r)
+{
+ Framebuf *fb;
+
+ fb = emalloc(sizeof *fb);
+ memset(fb, 0, sizeof *fb);
+ fb->cb = eallocmemimage(r, RGBA32);
+ fb->zbuf = emalloc(Dx(r)*Dy(r)*sizeof(*fb->zbuf));
+ memsetd(fb->zbuf, Inf(-1), Dx(r)*Dy(r));
+ fb->r = r;
+ return fb;
+}
+
+void
+rmfb(Framebuf *fb)
+{
+ free(fb->zbuf);
+ freememimage(fb->cb);
+ free(fb);
+}
+
+Framebufctl *
+mkfbctl(Rectangle r)
+{
+ Framebufctl *fc;
+
+ fc = emalloc(sizeof *fc);
+ memset(fc, 0, sizeof *fc);
+ fc->fb[0] = mkfb(r);
+ fc->fb[1] = mkfb(r);
+ fc->draw = framebufctl_draw;
+ fc->swap = framebufctl_swap;
+ fc->reset = framebufctl_reset;
+ return fc;
+}
+
+void
+rmfbctl(Framebufctl *fc)
+{
+ rmfb(fc->fb[1]);
+ rmfb(fc->fb[0]);
+ free(fc);
+}
--- a/graphics.h
+++ b/graphics.h
@@ -1,18 +1,24 @@
+#define HZ2MS(hz) (1000/(hz))
+
typedef enum {
- Portho, /* orthographic */
- Ppersp /* perspective */
+ ORTHOGRAPHIC,
+ PERSPECTIVE
} Projection;
typedef struct Color Color;
typedef struct Vertex Vertex;
-typedef struct Framebuffer Framebuffer;
+typedef struct VSparams VSparams;
+typedef struct FSparams FSparams;
+typedef struct SUparams SUparams;
+typedef struct Shader Shader;
+typedef struct Framebuf Framebuf;
+typedef struct Framebufctl Framebufctl;
typedef struct Viewport Viewport;
typedef struct Camera Camera;
-typedef struct Triangle Triangle;
struct Color
{
- double r, g, b;
+ double r, g, b, a;
};
struct Vertex
@@ -23,61 +29,122 @@
Point2 uv; /* texture coordinate */
};
-struct Framebuffer
+typedef Vertex Triangle[3];
+
+/* shader params */
+struct VSparams
{
- Rectangle r; /* frame geometry */
- int bpp; /* bytes per pixel */
- uchar *color; /* pixel color buffer */
- float *depth; /* pixel depth buffer */
+ SUparams *su;
+ Point3 *p;
+ Point3 *n;
+ uint idx;
};
+struct FSparams
+{
+ SUparams *su;
+ Memimage *frag;
+ Point p;
+ Point3 bc;
+ uchar *cbuf;
+};
+
+/* shader unit params */
+struct SUparams
+{
+ Framebuf *fb;
+ OBJElem **b, **e;
+ int id;
+ Channel *donec;
+
+ /* TODO replace with a Scene */
+ OBJ *model;
+ Memimage *modeltex;
+
+ double var_intensity[3];
+
+ uvlong uni_time;
+
+ Point3 (*vshader)(VSparams*);
+ Memimage *(*fshader)(FSparams*);
+};
+
+struct Shader
+{
+ char *name;
+ Point3 (*vshader)(VSparams*); /* vertex shader */
+ Memimage *(*fshader)(FSparams*); /* fragment shader */
+};
+
+struct Framebuf
+{
+ Memimage *cb; /* color buffer */
+ double *zbuf; /* z/depth buffer */
+ Lock zbuflk;
+ Rectangle r;
+};
+
+struct Framebufctl
+{
+ Framebuf *fb[2]; /* double buffering */
+ uint idx; /* front buffer index */
+ Lock swplk;
+
+ void (*draw)(Framebufctl*, Memimage*);
+ void (*swap)(Framebufctl*);
+ void (*reset)(Framebufctl*);
+};
+
struct Viewport
{
RFrame;
- Framebuffer;
+ Framebufctl *fbctl;
};
struct Camera
{
RFrame3; /* VCS */
- Image *viewport;
+ Viewport *vp;
double fov; /* vertical FOV */
struct {
double n, f; /* near and far clipping planes */
} clip;
Matrix3 proj; /* VCS to NDC xform */
- Projection ptype;
-};
+ Projection projtype;
-struct Triangle
-{
- Point p0, p1, p2;
+ struct {
+ uvlong min, avg, max, acc, n, v;
+ } stats;
};
-/* Camera */
-void configcamera(Camera*, Image*, double, double, double, Projection);
+/* camera */
+void configcamera(Camera*, Viewport*, double, double, double, Projection);
void placecamera(Camera*, Point3, Point3, Point3);
void aimcamera(Camera*, Point3);
void reloadcamera(Camera*);
+void shootcamera(Camera*, OBJ*, Memimage*, Shader*);
-/* rendering */
-#define FPS (60) /* frame rate */
-#define MS2FR (1e3/FPS) /* ms per frame */
+/* viewport */
+Viewport *mkviewport(Rectangle);
+void rmviewport(Viewport*);
+
+/* render */
Point3 world2vcs(Camera*, Point3);
Point3 vcs2ndc(Camera*, Point3);
Point3 world2ndc(Camera*, Point3);
-int isclipping(Point3);
-int clipline3(Point3*, Point3*);
-Point toviewport(Camera*, Point3);
-Point2 fromviewport(Camera*, Point);
+Point3 ndc2viewport(Camera*, Point3);
void perspective(Matrix3, double, double, double, double);
void orthographic(Matrix3, double, double, double, double, double, double);
-/* temporary debug helpers */
-void line3(Camera*, Point3, Point3, int, int, Image*);
-Point string3(Camera*, Point3, Image*, Font*, char*);
-/* triangle */
-Triangle Trian(int, int, int, int, int, int);
-Triangle Trianpt(Point, Point, Point);
-void triangle(Image*, Triangle, int, Image*, Point);
-void filltriangle(Image*, Triangle, Image*, Point);
+/* util */
+double fmin(double, double);
+double fmax(double, double);
+Memimage *rgb(ulong);
+Memimage *readtga(char*);
+Memimage *readpng(char*);
+
+/* shadeop */
+double step(double, double);
+double smoothstep(double, double, double);
+
+extern Rectangle UR; /* unit rectangle */
--- /dev/null
+++ b/internal.h
@@ -1,0 +1,29 @@
+typedef struct Deco Deco;
+struct Deco
+{
+ int pfd[2];
+ int infd;
+ char *prog;
+};
+
+/* alloc */
+void *emalloc(ulong);
+void *erealloc(void*, ulong);
+Memimage *eallocmemimage(Rectangle, ulong);
+
+/* fb */
+Framebuf *mkfb(Rectangle);
+void rmfb(Framebuf*);
+Framebufctl *mkfbctl(Rectangle);
+void rmfbctl(Framebufctl*);
+
+/* render */
+void shade(Framebuf *fb, OBJ *model, Memimage *modeltex, Shader *s, ulong nprocs);
+
+/* util */
+int min(int, int);
+int max(int, int);
+void memsetd(double*, double, usize);
+
+/* nanosec */
+uvlong nanosec(void);
--- a/mkfile
+++ b/mkfile
@@ -3,9 +3,35 @@
LIB=libgraphics.a$O
OFILES=\
camera.$O\
+ viewport.$O\
render.$O\
- triangle.$O\
+ alloc.$O\
+ fb.$O\
+ shadeop.$O\
+ util.$O\
+ nanosec.$O\
-HFILES=graphics.h
+HFILES=\
+ graphics.h\
+ internal.h\
+ libobj/obj.h
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+
</sys/src/cmd/mklib
+
+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
+
+clean nuke:V:
+ rm -f *.[$OS] $LIB
+ @{cd libobj; mk $target}
--- /dev/null
+++ b/nanosec.c
@@ -1,0 +1,109 @@
+#include <u.h>
+#include <libc.h>
+#include <tos.h>
+
+/*
+ * This code is a mixture of cpuid(1) and the nanosec() found in vmx,
+ * in order to force the use of nsec(2) in case we are running in a
+ * virtualized environment where the clock is mis-bhyve-ing.
+ */
+
+typedef struct Res {
+ ulong ax, bx, cx, dx;
+} Res;
+
+static uchar _cpuid[] = {
+ 0x5E, /* POP SI (PC) */
+ 0x5D, /* POP BP (Res&) */
+ 0x58, /* POP AX */
+ 0x59, /* POP CX */
+
+ 0x51, /* PUSH CX */
+ 0x50, /* PUSH AX */
+ 0x55, /* PUSH BP */
+ 0x56, /* PUSH SI */
+
+ 0x31, 0xDB, /* XOR BX, BX */
+ 0x31, 0xD2, /* XOR DX, DX */
+
+ 0x0F, 0xA2, /* CPUID */
+
+ 0x89, 0x45, 0x00, /* MOV AX, 0(BP) */
+ 0x89, 0x5d, 0x04, /* MOV BX, 4(BP) */
+ 0x89, 0x4d, 0x08, /* MOV CX, 8(BP) */
+ 0x89, 0x55, 0x0C, /* MOV DX, 12(BP) */
+ 0xC3, /* RET */
+};
+
+static Res (*cpuid)(ulong ax, ulong cx) = (Res(*)(ulong, ulong)) _cpuid;
+
+/*
+ * nsec() is wallclock and can be adjusted by timesync
+ * so need to use cycles() instead, but fall back to
+ * nsec() in case we can't
+ */
+uvlong
+nanosec(void)
+{
+ static uvlong fasthz, xstart;
+ char buf[13], path[128];
+ ulong w;
+ uvlong x, div;
+ int fd;
+ Res r;
+
+ if(fasthz == ~0ULL)
+ return nsec() - xstart;
+
+ if(fasthz == 0){
+ /* first long in a.out header */
+ snprint(path, sizeof path, "/proc/%d/text", getpid());
+ fd = open(path, OREAD);
+ if(fd < 0)
+ goto Wallclock;
+ if(read(fd, buf, 4) != 4){
+ close(fd);
+ goto Wallclock;
+ }
+ close(fd);
+
+ w = ((ulong *) buf)[0];
+
+ switch(w){
+ default:
+ goto Wallclock;
+ case 0x978a0000: /* amd64 */
+ /* patch out POP BP -> POP AX */
+ _cpuid[1] = 0x58;
+ case 0xeb010000: /* 386 */
+ break;
+ }
+ segflush(_cpuid, sizeof(_cpuid));
+
+ r = cpuid(0x40000000, 0);
+ ((ulong *) buf)[0] = r.bx;
+ ((ulong *) buf)[1] = r.cx;
+ ((ulong *) buf)[2] = r.dx;
+ buf[12] = 0;
+
+ if(strstr(buf, "bhyve") != nil)
+ goto Wallclock;
+
+ if(_tos->cyclefreq){
+ fasthz = _tos->cyclefreq;
+ cycles(&xstart);
+ } else {
+Wallclock:
+ fasthz = ~0ULL;
+ xstart = nsec();
+ }
+ return 0;
+ }
+ cycles(&x);
+ x -= xstart;
+
+ /* this is ugly */
+ for(div = 1000000000ULL; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
+
+ return x / (fasthz / div);
+}
--- a/readme
+++ b/readme
@@ -1,6 +1,6 @@
libgraphics
-Libgraphics provides 3D computer graphics through draw(3).
+Libgraphics provides 3D computer graphics through memdraw(2).
Still in early stages of research, subject to change, drastically, at
any given time.
--- a/render.c
+++ b/render.c
@@ -1,31 +1,85 @@
#include <u.h>
#include <libc.h>
+#include <thread.h>
#include <draw.h>
+#include <memdraw.h>
#include <geometry.h>
-#include <graphics.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
-static Point2
-flatten(Camera *c, Point3 p)
+Rectangle UR = {0,0,1,1};
+
+
+static void
+pixel(Memimage *dst, Point p, Memimage *src)
{
- Point2 p2;
- Matrix S = {
- Dx(c->viewport->r)/2, 0, 0,
- 0, Dy(c->viewport->r)/2, 0,
- 0, 0, 1,
- }, T = {
- 1, 0, 1,
- 0, 1, 1,
- 0, 0, 1,
- };
+ if(dst == nil || src == nil)
+ return;
- p2 = Pt2(p.x, p.y, p.w);
- if(p2.w != 0)
- p2 = divpt2(p2, p2.w);
- mulm(S, T);
- p2 = xform(p2, S);
- return p2;
+ memimagedraw(dst, rectaddpt(UR, p), src, ZP, nil, ZP, SoverD);
}
+/*
+ * it only processes quads for now.
+ */
+static int
+triangulate(OBJElem **newe, OBJElem *e)
+{
+ OBJIndexArray *newidxtab;
+ OBJIndexArray *idxtab;
+
+ idxtab = &e->indextab[OBJVGeometric];
+ newe[0] = emalloc(sizeof *newe[0]);
+ newe[0]->type = OBJEFace;
+ newidxtab = &newe[0]->indextab[OBJVGeometric];
+ newidxtab->nindex = 3;
+ newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
+ newidxtab->indices[0] = idxtab->indices[0];
+ newidxtab->indices[1] = idxtab->indices[1];
+ newidxtab->indices[2] = idxtab->indices[2];
+ idxtab = &e->indextab[OBJVTexture];
+ newidxtab = &newe[0]->indextab[OBJVTexture];
+ newidxtab->nindex = 3;
+ newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
+ newidxtab->indices[0] = idxtab->indices[0];
+ newidxtab->indices[1] = idxtab->indices[1];
+ newidxtab->indices[2] = idxtab->indices[2];
+ idxtab = &e->indextab[OBJVNormal];
+ newidxtab = &newe[0]->indextab[OBJVNormal];
+ newidxtab->nindex = 3;
+ newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
+ newidxtab->indices[0] = idxtab->indices[0];
+ newidxtab->indices[1] = idxtab->indices[1];
+ newidxtab->indices[2] = idxtab->indices[2];
+
+ idxtab = &e->indextab[OBJVGeometric];
+ newe[1] = emalloc(sizeof *newe[1]);
+ newe[1]->type = OBJEFace;
+ newidxtab = &newe[1]->indextab[OBJVGeometric];
+ newidxtab->nindex = 3;
+ newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
+ newidxtab->indices[0] = idxtab->indices[0];
+ newidxtab->indices[1] = idxtab->indices[2];
+ newidxtab->indices[2] = idxtab->indices[3];
+ idxtab = &e->indextab[OBJVTexture];
+ newidxtab = &newe[1]->indextab[OBJVTexture];
+ newidxtab->nindex = 3;
+ newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
+ newidxtab->indices[0] = idxtab->indices[0];
+ newidxtab->indices[1] = idxtab->indices[2];
+ newidxtab->indices[2] = idxtab->indices[3];
+ idxtab = &e->indextab[OBJVNormal];
+ newidxtab = &newe[1]->indextab[OBJVNormal];
+ newidxtab->nindex = 3;
+ newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
+ newidxtab->indices[0] = idxtab->indices[0];
+ newidxtab->indices[1] = idxtab->indices[2];
+ newidxtab->indices[2] = idxtab->indices[3];
+
+ return 2;
+}
+
Point3
world2vcs(Camera *c, Point3 p)
{
@@ -44,105 +98,22 @@
return vcs2ndc(c, world2vcs(c, p));
}
-/* requires p to be in NDC */
-int
-isclipping(Point3 p)
+Point3
+ndc2viewport(Camera *c, Point3 p)
{
- if(p.x > p.w || p.x < -p.w ||
- p.y > p.w || p.y < -p.w ||
- p.z > p.w || p.z < 0)
- return 1;
- return 0;
-}
+ Matrix3 view;
-/* Liang-Barsky algorithm, requires p0, p1 to be in NDC */
-int
-clipline3(Point3 *p0, Point3 *p1)
-{
- Point3 q0, q1, v;
- int m0, m1, i;
- double ti, to, th;
- double c0[3*2] = {
- p0->w + p0->x, p0->w - p0->x, p0->w + p0->y,
- p0->w - p0->y, p0->z, p0->w - p0->z,
- }, c1[3*2] = {
- p1->w + p1->x, p1->w - p1->x, p1->w + p1->y,
- p1->w - p1->y, p1->z, p1->w - p1->z,
- };
+ identity3(view);
+ view[0][3] = c->vp->fbctl->fb[0]->r.max.x/2.0;
+ view[1][3] = c->vp->fbctl->fb[0]->r.max.y/2.0;
+ view[2][3] = 1.0/2.0;
+ view[0][0] = Dx(c->vp->fbctl->fb[0]->r)/2.0;
+ view[1][1] = -Dy(c->vp->fbctl->fb[0]->r)/2.0;
+ view[2][2] = 1.0/2.0;
- /* bit-encoded regions */
- m0 = (c0[0] < 0) << 0 |
- (c0[1] < 0) << 1 |
- (c0[2] < 0) << 2 |
- (c0[3] < 0) << 3 |
- (c0[4] < 0) << 4 |
- (c0[5] < 0) << 5;
- m1 = (c1[0] < 0) << 0 |
- (c1[1] < 0) << 1 |
- (c1[2] < 0) << 2 |
- (c1[3] < 0) << 3 |
- (c1[4] < 0) << 4 |
- (c1[5] < 0) << 5;
-
- if((m0 & m1) != 0)
- return 1; /* trivially rejected */
- if((m0 | m1) == 0)
- return 0; /* trivially accepted */
-
- ti = 0;
- to = 1;
- for(i = 0; i < 3*2; i++){
- if(c1[i] < 0){
- th = c0[i] / (c0[i]-c1[i]);
- if(th < to)
- to = th;
- }else if(c0[i] < 0){
- th = c0[i] / (c0[i]-c1[i]);
- if(th < ti)
- ti = th;
- }
- if(ti > to)
- return 1;
- }
-
- /* chop line to fit inside NDC */
- q0 = *p0;
- q1 = *p1;
- v = subpt3(q1, q0);
- if(m0 != 0)
- *p0 = addpt3(q0, mulpt3(v, ti));
- if(m1 != 0)
- *p1 = addpt3(q0, mulpt3(v, to));
-
- return 0;
+ return xform3(p, view);
}
-Point
-toviewport(Camera *c, Point3 p)
-{
- Point2 p2;
- RFrame rf = {
- c->viewport->r.min.x, c->viewport->r.max.y, 1,
- 1, 0, 0,
- 0, -1, 0
- };
-
- p2 = invrframexform(flatten(c, p), rf);
- return Pt(p2.x, p2.y);
-}
-
-Point2
-fromviewport(Camera *c, Point p)
-{
- RFrame rf = {
- c->viewport->r.min.x, c->viewport->r.max.y, 1,
- 1, 0, 0,
- 0, -1, 0
- };
-
- return rframexform(Pt2(p.x,p.y,1), rf);
-}
-
void
perspective(Matrix3 m, double fov, double a, double n, double f)
{
@@ -152,7 +123,7 @@
identity3(m);
m[0][0] = cotan/a;
m[1][1] = cotan;
- m[2][2] = -(f+n)/(f-n);
+ m[2][2] = (f+n)/(f-n);
m[2][3] = -2*f*n/(f-n);
m[3][2] = -1;
}
@@ -169,21 +140,213 @@
m[2][3] = -(f + n)/(f - n);
}
-void
-line3(Camera *c, Point3 p0, Point3 p1, int end0, int end1, Image *src)
+static void
+rasterize(SUparams *params, Triangle t, Memimage *frag)
{
- p0 = world2ndc(c, p0);
- p1 = world2ndc(c, p1);
- if(clipline3(&p0, &p1))
- return;
- line(c->viewport, toviewport(c, p0), toviewport(c, p1), end0, end1, 0, src, ZP);
+ FSparams fsp;
+ Triangle2 t₂, tt₂;
+ Rectangle bbox;
+ Point p, tp;
+ Point3 bc;
+ double z, w, depth;
+ uchar cbuf[4];
+
+ t₂.p0 = Pt2(t[0].p.x/t[0].p.w, t[0].p.y/t[0].p.w, 1);
+ t₂.p1 = Pt2(t[1].p.x/t[1].p.w, t[1].p.y/t[1].p.w, 1);
+ t₂.p2 = Pt2(t[2].p.x/t[2].p.w, t[2].p.y/t[2].p.w, 1);
+ /* find the triangle's bbox and clip it against the fb */
+ 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)+1, max(max(t₂.p0.y, t₂.p1.y), t₂.p2.y)+1
+ );
+ bbox.min.x = max(bbox.min.x, params->fb->r.min.x);
+ bbox.min.y = max(bbox.min.y, params->fb->r.min.y);
+ bbox.max.x = min(bbox.max.x, params->fb->r.max.x);
+ bbox.max.y = min(bbox.max.y, params->fb->r.max.y);
+ cbuf[0] = 0xFF;
+ fsp.su = params;
+ fsp.frag = frag;
+ fsp.cbuf = cbuf;
+
+ for(p.y = bbox.min.y; p.y < bbox.max.y; p.y++)
+ for(p.x = bbox.min.x; p.x < bbox.max.x; p.x++){
+ bc = barycoords(t₂, Pt2(p.x,p.y,1));
+ if(bc.x < 0 || bc.y < 0 || bc.z < 0)
+ continue;
+
+ z = t[0].p.z*bc.x + t[1].p.z*bc.y + t[2].p.z*bc.z;
+ w = t[0].p.w*bc.x + t[1].p.w*bc.y + t[2].p.w*bc.z;
+ depth = fclamp(z/w, 0, 1);
+ lock(¶ms->fb->zbuflk);
+ if(depth <= params->fb->zbuf[p.x + p.y*Dx(params->fb->r)]){
+ unlock(¶ms->fb->zbuflk);
+ continue;
+ }
+ params->fb->zbuf[p.x + p.y*Dx(params->fb->r)] = depth;
+ unlock(¶ms->fb->zbuflk);
+
+ cbuf[0] = 0xFF;
+ if((t[0].uv.w + t[1].uv.w + t[2].uv.w) != 0){
+ tt₂.p0 = mulpt2(t[0].uv, bc.x);
+ tt₂.p1 = mulpt2(t[1].uv, bc.y);
+ tt₂.p2 = mulpt2(t[2].uv, bc.z);
+
+ tp.x = (tt₂.p0.x + tt₂.p1.x + tt₂.p2.x)*Dx(params->modeltex->r);
+ tp.y = (1 - (tt₂.p0.y + tt₂.p1.y + tt₂.p2.y))*Dy(params->modeltex->r);
+
+ switch(params->modeltex->chan){
+ case RGB24:
+ unloadmemimage(params->modeltex, rectaddpt(UR, tp), cbuf+1, sizeof cbuf - 1);
+ break;
+ case RGBA32:
+ unloadmemimage(params->modeltex, rectaddpt(UR, tp), cbuf, sizeof cbuf);
+ break;
+ }
+ }else
+ memset(cbuf+1, 0xFF, sizeof cbuf - 1);
+
+ fsp.p = p;
+ fsp.bc = bc;
+ pixel(params->fb->cb, p, params->fshader(&fsp));
+ }
}
-Point
-string3(Camera *c, Point3 p, Image *src, Font *f, char *s)
+static void
+shaderunit(void *arg)
{
- p = world2ndc(c, p);
- if(isclipping(p))
- return Pt(-1,-1);
- return string(c->viewport, toviewport(c, p), src, ZP, f, s);
+ SUparams *params;
+ VSparams vsp;
+ Memimage *frag;
+ OBJVertex *verts, *tverts, *nverts; /* geometric, texture and normals vertices */
+ OBJIndexArray *idxtab;
+ OBJElem **ep;
+ Triangle t;
+ Point3 n; /* surface normal */
+
+ params = arg;
+ vsp.su = params;
+ frag = rgb(DBlack);
+
+ threadsetname("shader unit #%d", params->id);
+
+ verts = params->model->vertdata[OBJVGeometric].verts;
+ tverts = params->model->vertdata[OBJVTexture].verts;
+ nverts = params->model->vertdata[OBJVNormal].verts;
+
+ for(ep = params->b; ep != params->e; ep++){
+ idxtab = &(*ep)->indextab[OBJVGeometric];
+
+ t[0].p = Pt3(verts[idxtab->indices[0]].x,verts[idxtab->indices[0]].y,verts[idxtab->indices[0]].z,verts[idxtab->indices[0]].w);
+ t[1].p = Pt3(verts[idxtab->indices[1]].x,verts[idxtab->indices[1]].y,verts[idxtab->indices[1]].z,verts[idxtab->indices[1]].w);
+ t[2].p = Pt3(verts[idxtab->indices[2]].x,verts[idxtab->indices[2]].y,verts[idxtab->indices[2]].z,verts[idxtab->indices[2]].w);
+
+ idxtab = &(*ep)->indextab[OBJVNormal];
+ if(idxtab->nindex == 3){
+ t[0].n = Vec3(nverts[idxtab->indices[0]].i, nverts[idxtab->indices[0]].j, nverts[idxtab->indices[0]].k);
+ t[0].n = normvec3(t[0].n);
+ t[1].n = Vec3(nverts[idxtab->indices[1]].i, nverts[idxtab->indices[1]].j, nverts[idxtab->indices[1]].k);
+ t[1].n = normvec3(t[1].n);
+ t[2].n = Vec3(nverts[idxtab->indices[2]].i, nverts[idxtab->indices[2]].j, nverts[idxtab->indices[2]].k);
+ t[2].n = normvec3(t[2].n);
+ }else{
+ n = normvec3(crossvec3(subpt3(t[2].p, t[0].p), subpt3(t[1].p, t[0].p)));
+ t[0].n = t[1].n = t[2].n = mulpt3(n, -1);
+ }
+
+ vsp.p = &t[0].p;
+ vsp.n = &t[0].n;
+ vsp.idx = 0;
+ t[0].p = params->vshader(&vsp);
+ vsp.p = &t[1].p;
+ vsp.n = &t[1].n;
+ vsp.idx = 1;
+ t[1].p = params->vshader(&vsp);
+ vsp.p = &t[2].p;
+ vsp.n = &t[2].n;
+ vsp.idx = 2;
+ t[2].p = params->vshader(&vsp);
+
+ idxtab = &(*ep)->indextab[OBJVTexture];
+ if(params->modeltex != nil && idxtab->nindex == 3){
+ t[0].uv = Pt2(tverts[idxtab->indices[0]].u, tverts[idxtab->indices[0]].v, 1);
+ t[1].uv = Pt2(tverts[idxtab->indices[1]].u, tverts[idxtab->indices[1]].v, 1);
+ t[2].uv = Pt2(tverts[idxtab->indices[2]].u, tverts[idxtab->indices[2]].v, 1);
+ }else{
+ t[0].uv = t[1].uv = t[2].uv = Vec2(0,0);
+ }
+
+ rasterize(params, t, frag);
+ }
+
+ freememimage(frag);
+ sendp(params->donec, nil);
+ free(params);
+ threadexits(nil);
+}
+
+void
+shade(Framebuf *fb, OBJ *model, Memimage *modeltex, Shader *s, ulong nprocs)
+{
+ static int nparts, nworkers;
+ static OBJElem **elems = nil;
+ OBJElem *trielems[2];
+ int i, nelems;
+ uvlong time;
+ OBJObject *o;
+ OBJElem *e;
+ OBJIndexArray *idxtab;
+ SUparams *params;
+ Channel *donec;
+
+ if(elems == nil){
+ nelems = 0;
+ for(i = 0; i < nelem(model->objtab); i++)
+ for(o = model->objtab[i]; o != nil; o = o->next)
+ for(e = o->child; e != nil; e = e->next){
+ idxtab = &e->indextab[OBJVGeometric];
+ /* discard non-triangles */
+ if(e->type != OBJEFace || (idxtab->nindex != 3 && idxtab->nindex != 4))
+ continue;
+ if(idxtab->nindex == 4){
+ triangulate(trielems, e);
+ nelems += 2;
+ elems = erealloc(elems, nelems*sizeof(*elems));
+ elems[nelems-2] = trielems[0];
+ elems[nelems-1] = trielems[1];
+ }else{
+ elems = erealloc(elems, ++nelems*sizeof(*elems));
+ elems[nelems-1] = e;
+ }
+ }
+ if(nelems < nprocs){
+ nworkers = nelems;
+ nparts = 1;
+ }else{
+ nworkers = nprocs;
+ nparts = nelems/nprocs;
+ }
+ }
+ time = nanosec();
+
+ donec = chancreate(sizeof(void*), 0);
+
+ for(i = 0; i < nworkers; i++){
+ params = emalloc(sizeof *params);
+ params->fb = fb;
+ params->b = &elems[i*nparts];
+ params->e = params->b + nparts;
+ params->id = i;
+ params->donec = donec;
+ params->model = model;
+ params->modeltex = modeltex;
+ params->uni_time = time;
+ params->vshader = s->vshader;
+ params->fshader = s->fshader;
+ proccreate(shaderunit, params, mainstacksize);
+// fprint(2, "spawned su %d for elems [%d, %d)\n", params->id, i*nparts, i*nparts+nparts);
+ }
+
+ while(i--)
+ recvp(donec);
+ chanfree(donec);
}
--- /dev/null
+++ b/shadeop.c
@@ -1,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
+
+double
+step(double edge, double n)
+{
+ if(n < edge)
+ return 0;
+ return 1;
+}
+
+double
+smoothstep(double edge0, double edge1, double n)
+{
+ double t;
+
+ t = fclamp((n-edge0)/(edge1-edge0), 0, 1);
+ return t*t * (3 - 2*t);
+}
--- a/triangle.c
+++ /dev/null
@@ -1,42 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <geometry.h>
-#include <graphics.h>
-
-Triangle
-Trian(int x0, int y0, int x1, int y1, int x2, int y2)
-{
- return (Triangle){Pt(x0, y0), Pt(x1, y1), Pt(x2, y2)};
-}
-
-Triangle
-Trianpt(Point p0, Point p1, Point p2)
-{
- return (Triangle){p0, p1, p2};
-};
-
-void
-triangle(Image *dst, Triangle t, int thick, Image *src, Point sp)
-{
- Point pl[4];
-
- pl[0] = t.p0;
- pl[1] = t.p1;
- pl[2] = t.p2;
- pl[3] = pl[0];
-
- poly(dst, pl, nelem(pl), 0, 0, thick, src, sp);
-}
-
-void
-filltriangle(Image *dst, Triangle t, Image *src, Point sp)
-{
- Point pl[3];
-
- pl[0] = t.p0;
- pl[1] = t.p1;
- pl[2] = t.p2;
-
- fillpoly(dst, pl, nelem(pl), 0, src, sp);
-}
--- /dev/null
+++ b/util.c
@@ -1,0 +1,108 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
+
+int
+min(int a, int b)
+{
+ return a < b? a: b;
+}
+
+int
+max(int a, int b)
+{
+ return a > b? a: b;
+}
+
+double
+fmin(double a, double b)
+{
+ return a < b? a: b;
+}
+
+double
+fmax(double a, double b)
+{
+ return a > b? a: b;
+}
+
+void
+memsetd(double *p, double v, usize len)
+{
+ double *dp;
+
+ for(dp = p; dp < p+len; dp++)
+ *dp = v;
+}
+
+Memimage *
+rgb(ulong c)
+{
+ Memimage *i;
+
+ i = eallocmemimage(UR, screen->chan);
+ i->flags |= Frepl;
+ i->clipr = Rect(-1e6, -1e6, 1e6, 1e6);
+ memfillcolor(i, c);
+ return i;
+}
+
+static void
+decproc(void *arg)
+{
+ char buf[32];
+ Deco *d;
+
+ d = arg;
+
+ close(d->pfd[0]);
+ dup(d->infd, 0);
+ close(d->infd);
+ dup(d->pfd[1], 1);
+ close(d->pfd[1]);
+
+ snprint(buf, sizeof buf, "/bin/%s", d->prog);
+
+ execl(buf, d->prog, "-9t", nil);
+ threadexitsall("execl: %r");
+}
+
+static Memimage *
+genreadimage(char *prog, char *path)
+{
+ Memimage *i;
+ Deco d;
+
+ d.prog = prog;
+
+ if(pipe(d.pfd) < 0)
+ sysfatal("pipe: %r");
+ d.infd = open(path, OREAD);
+ if(d.infd < 0)
+ sysfatal("open: %r");
+ procrfork(decproc, &d, mainstacksize, RFFDG|RFNAMEG|RFNOTEG);
+ close(d.pfd[1]);
+ i = readmemimage(d.pfd[0]);
+ close(d.pfd[0]);
+ close(d.infd);
+
+ return i;
+}
+
+Memimage *
+readtga(char *path)
+{
+ return genreadimage("tga", path);
+}
+
+Memimage *
+readpng(char *path)
+{
+ return genreadimage("png", path);
+}
--- /dev/null
+++ b/viewport.c
@@ -1,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
+
+Viewport *
+mkviewport(Rectangle r)
+{
+ Viewport *v;
+
+ v = emalloc(sizeof *v);
+ v->p = Pt2(0,0,1);
+ v->bx = Vec2(1,0);
+ v->by = Vec2(0,1);
+ v->fbctl = mkfbctl(r);
+ return v;
+}
+
+void
+rmviewport(Viewport *v)
+{
+ rmfbctl(v->fbctl);
+ free(v);
+}