diff options
Diffstat (limited to 'render.c')
-rw-r--r-- | render.c | 419 |
1 files changed, 291 insertions, 128 deletions
@@ -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); } |