From 37da5f0a27740bb43bac67a2eb98f725beca975e Mon Sep 17 00:00:00 2001 From: rodri Date: Tue, 30 Jan 2024 12:01:01 +0000 Subject: 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. --- alloc.c | 51 ++++++++ camera.c | 57 ++++++--- fb.c | 81 ++++++++++++ graphics.h | 137 ++++++++++++++------ internal.h | 29 +++++ mkfile | 30 ++++- nanosec.c | 109 ++++++++++++++++ readme | 2 +- render.c | 419 ++++++++++++++++++++++++++++++++++++++++++------------------- shadeop.c | 26 ++++ triangle.c | 42 ------- util.c | 108 ++++++++++++++++ viewport.c | 29 +++++ 13 files changed, 895 insertions(+), 225 deletions(-) create mode 100644 alloc.c create mode 100644 fb.c create mode 100644 internal.h create mode 100644 nanosec.c create mode 100644 shadeop.c delete mode 100644 triangle.c create mode 100644 util.c create mode 100644 viewport.c diff --git a/alloc.c b/alloc.c new file mode 100644 index 0000000..4154c83 --- /dev/null +++ b/alloc.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/camera.c b/camera.c index 1d67868..7b33c91 100644 --- a/camera.c +++ b/camera.c @@ -1,32 +1,41 @@ #include #include +#include #include +#include #include -#include +#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); +} diff --git a/fb.c b/fb.c new file mode 100644 index 0000000..a7a5122 --- /dev/null +++ b/fb.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/graphics.h b/graphics.h index 46f230d..e044811 100644 --- 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 @@ 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); diff --git a/mkfile b/mkfile index 695a28b..9190fe8 100644 --- 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}\ +#include +#include + +/* + * 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); +} diff --git a/readme b/readme index d025dce..f33310d 100644 --- 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. diff --git a/render.c b/render.c index 8b77186..48a15e6 100644 --- a/render.c +++ b/render.c @@ -1,29 +1,83 @@ #include #include +#include #include +#include #include -#include +#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 +#include +#include +#include +#include +#include +#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 -#include -#include -#include -#include - -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); -} diff --git a/util.c b/util.c new file mode 100644 index 0000000..1ff251f --- /dev/null +++ b/util.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#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); +} -- cgit v1.2.3