diff options
-rw-r--r-- | alloc.c | 51 | ||||
-rw-r--r-- | camera.c | 57 | ||||
-rw-r--r-- | fb.c | 81 | ||||
-rw-r--r-- | graphics.h | 137 | ||||
-rw-r--r-- | internal.h | 29 | ||||
-rw-r--r-- | mkfile | 30 | ||||
-rw-r--r-- | nanosec.c | 109 | ||||
-rw-r--r-- | readme | 2 | ||||
-rw-r--r-- | render.c | 419 | ||||
-rw-r--r-- | shadeop.c | 26 | ||||
-rw-r--r-- | triangle.c | 42 | ||||
-rw-r--r-- | util.c | 108 | ||||
-rw-r--r-- | viewport.c | 29 |
13 files changed, 895 insertions, 225 deletions
@@ -0,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; +} @@ -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 @@ reloadcamera(Camera *c) 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); +} @@ -0,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); +} @@ -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 @@ struct Vertex Point2 uv; /* texture coordinate */ }; -struct Framebuffer +typedef Vertex Triangle[3]; + +/* shader params */ +struct VSparams +{ + 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 { - Rectangle r; /* frame geometry */ - int bpp; /* bytes per pixel */ - uchar *color; /* pixel color buffer */ - float *depth; /* pixel depth buffer */ + 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*); + +/* viewport */ +Viewport *mkviewport(Rectangle); +void rmviewport(Viewport*); -/* rendering */ -#define FPS (60) /* frame rate */ -#define MS2FR (1e3/FPS) /* ms per frame */ +/* 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 */ diff --git a/internal.h b/internal.h new file mode 100644 index 0000000..a6b7a96 --- /dev/null +++ b/internal.h @@ -0,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); @@ -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} diff --git a/nanosec.c b/nanosec.c new file mode 100644 index 0000000..f82d47a --- /dev/null +++ b/nanosec.c @@ -0,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); +} @@ -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. @@ -1,29 +1,83 @@ #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, - }; - - 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; + if(dst == nil || src == nil) + return; + + 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 @@ -44,103 +98,20 @@ world2ndc(Camera *c, Point3 p) return vcs2ndc(c, world2vcs(c, p)); } -/* requires p to be in NDC */ -int -isclipping(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; -} - -/* 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, - }; - - /* 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; -} - -Point -toviewport(Camera *c, Point3 p) +Point3 +ndc2viewport(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); -} + Matrix3 view; -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 - }; + 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; - return rframexform(Pt2(p.x,p.y,1), rf); + return xform3(p, view); } void @@ -152,7 +123,7 @@ perspective(Matrix3 m, double fov, double a, double n, double f) 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 @@ orthographic(Matrix3 m, double l, double r, double b, double t, double n, double 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)); + } +} + +static void +shaderunit(void *arg) +{ + 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); } -Point -string3(Camera *c, Point3 p, Image *src, Font *f, char *s) +void +shade(Framebuf *fb, OBJ *model, Memimage *modeltex, Shader *s, ulong nprocs) { - p = world2ndc(c, p); - if(isclipping(p)) - return Pt(-1,-1); - return string(c->viewport, toviewport(c, p), src, ZP, f, s); + 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); } diff --git a/shadeop.c b/shadeop.c new file mode 100644 index 0000000..c1484c3 --- /dev/null +++ b/shadeop.c @@ -0,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); +} diff --git a/triangle.c b/triangle.c deleted file mode 100644 index 9c49651..0000000 --- a/triangle.c +++ /dev/null @@ -1,42 +0,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); -} @@ -0,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); +} diff --git a/viewport.c b/viewport.c new file mode 100644 index 0000000..81e1653 --- /dev/null +++ b/viewport.c @@ -0,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); +} |