aboutsummaryrefslogtreecommitdiff
path: root/render.c
diff options
context:
space:
mode:
authorrodri <rgl@antares-labs.eu>2024-01-30 12:01:01 +0000
committerrodri <rgl@antares-labs.eu>2024-01-30 12:01:01 +0000
commit37da5f0a27740bb43bac67a2eb98f725beca975e (patch)
tree2e403bbd2f477e65c8b82f10e950721b4103c895 /render.c
parentdf792cbb5cf94abf0d286ebfd73bad9714dce509 (diff)
downloadlibgraphics-37da5f0a27740bb43bac67a2eb98f725beca975e.tar.gz
libgraphics-37da5f0a27740bb43bac67a2eb98f725beca975e.tar.bz2
libgraphics-37da5f0a27740bb43bac67a2eb98f725beca975e.zip
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.
Diffstat (limited to 'render.c')
-rw-r--r--render.c419
1 files changed, 291 insertions, 128 deletions
diff --git a/render.c b/render.c
index 8b77186..48a15e6 100644
--- a/render.c
+++ b/render.c
@@ -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(&params->fb->zbuflk);
+ if(depth <= params->fb->zbuf[p.x + p.y*Dx(params->fb->r)]){
+ unlock(&params->fb->zbuflk);
+ continue;
+ }
+ params->fb->zbuf[p.x + p.y*Dx(params->fb->r)] = depth;
+ unlock(&params->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);
}