shithub: tinyrend

Download patch

ref: 1b76dd0177af7e6949f8c20c434844f8d559b6cd
parent: 66ce5ae042207689a912bd5b2362905c22d534be
author: rodri <rgl@antares-labs.eu>
date: Sat Nov 18 11:34:21 EST 2023

implemented perspective projection and a new rendering procedure (flag2).

--- a/main.c
+++ b/main.c
@@ -27,6 +27,7 @@
 {
 	Memimage *dst;
 	Rectangle r;
+	OBJElem **b, **e;
 	int id;
 	Channel *donec;
 	Memimage *(*shader)(Sparams*);
@@ -35,6 +36,7 @@
 
 Memimage *fb, *zfb, *curfb;
 double *zbuf;
+Lock zbuflk;
 Memimage *red, *green, *blue;
 OBJ *model;
 Memimage *modeltex;
@@ -41,8 +43,21 @@
 Channel *drawc;
 int nprocs;
 int rendering;
+int flag2;
 
 char winspec[32];
+Point3 camera = {0,0,3,1};
+Matrix3 proj = {
+	1,  0, 0, 0,
+	0, -1, 0, 0,
+	0,  0, 1, 0,
+	0,  0, 0, 1,
+}, view = {
+	800/2.0, 0, 0, 800/2.0,
+	0, 800/2.0, 0, 800/2.0,
+	0, 0, 1/2.0, 1/2.0,
+	0, 0, 0, 1,
+}, rota;
 
 void resized(void);
 uvlong nanosec(void);
@@ -70,6 +85,26 @@
 }
 
 void
+swappt2(Point2 *a, Point2 *b)
+{
+	Point2 t;
+
+	t = *a;
+	*a = *b;
+	*b = t;
+}
+
+void
+swappt3(Point3 *a, Point3 *b)
+{
+	Point3 t;
+
+	t = *a;
+	*a = *b;
+	*b = t;
+}
+
+void
 memsetd(double *p, double v, usize len)
 {
 	double *dp;
@@ -297,6 +332,68 @@
 }
 
 void
+filltriangle2(Memimage *dst, Triangle3 st, Triangle2 tt, double intensity, Memimage *frag)
+{
+	Rectangle bbox;
+	Point p, tp;
+	Triangle2 st₂, tt₂;
+	Point3 bc;
+	double z;
+	uchar cbuf[4];
+
+	bbox = Rect(
+		min(min(st.p0.x, st.p1.x), st.p2.x), min(min(st.p0.y, st.p1.y), st.p2.y),
+		max(max(st.p0.x, st.p1.x), st.p2.x)+1, max(max(st.p0.y, st.p1.y), st.p2.y)+1
+	);
+	st₂.p0 = Pt2(st.p0.x, st.p0.y, 1);
+	st₂.p1 = Pt2(st.p1.x, st.p1.y, 1);
+	st₂.p2 = Pt2(st.p2.x, st.p2.y, 1);
+	cbuf[0] = 0xFF;
+
+	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(st₂, Pt2(p.x,p.y,1));
+			if(bc.x < 0 || bc.y < 0 || bc.z < 0)
+				continue;
+
+			z = st.p0.z*bc.x + st.p1.z*bc.y + st.p2.z*bc.z;
+			lock(&zbuflk);
+			if(z <= zbuf[p.x + p.y*Dx(dst->r)]){
+				unlock(&zbuflk);
+				continue;
+			}
+			zbuf[p.x + p.y*Dx(dst->r)] = z;
+
+			cbuf[1] = 0xFF*z;
+			cbuf[2] = 0xFF*z;
+			cbuf[3] = 0xFF*z;
+			memfillcolor(frag, *(ulong*)cbuf);
+			pixel(zfb, p, frag);
+			unlock(&zbuflk);
+
+			cbuf[0] = 0xFF;
+			if(tt.p0.w != 0 && tt.p1.w != 0 && tt.p2.w != 0){
+				tt₂.p0 = mulpt2(tt.p0, bc.x);
+				tt₂.p1 = mulpt2(tt.p1, bc.y);
+				tt₂.p2 = mulpt2(tt.p2, bc.z);
+
+				tp.x = (tt₂.p0.x + tt₂.p1.x + tt₂.p2.x)*Dx(modeltex->r);
+				tp.y = (1 - (tt₂.p0.y + tt₂.p1.y + tt₂.p2.y))*Dy(modeltex->r);
+
+				unloadmemimage(modeltex, rectaddpt(Rect(0,0,1,1), tp), cbuf+1, sizeof cbuf - 1);
+			}else
+				memset(cbuf+1, 0xFF, sizeof cbuf - 1);
+
+			cbuf[1] *= intensity;
+			cbuf[2] *= intensity;
+			cbuf[3] *= intensity;
+
+			memfillcolor(frag, *(ulong*)cbuf);
+			pixel(dst, p, frag);
+		}
+}
+
+void
 shaderunit(void *arg)
 {
 	SUparams *params;
@@ -351,6 +448,111 @@
 	chanfree(donec);
 }
 
+void
+shaderunit2(void *arg)
+{
+	SUparams *params;
+	Sparams sp;
+	OBJVertex *verts, *tverts;		/* geometric and texture vertices */
+	OBJIndexArray *idxtab;
+	OBJElem **ep;
+	Triangle3 t, st;			/* world- and screen-space triangles */
+	Triangle2 tt;				/* texture triangle */
+	Point3 n;				/* surface normal */
+	static Point3 light = {0,0,-1,0};	/* global light field */
+	double intensity;
+
+	params = arg;
+	sp.frag = rgb(DBlack);
+
+	threadsetname("shader unit #%d", params->id);
+
+	verts = model->vertdata[OBJVGeometric].verts;
+	tverts = model->vertdata[OBJVTexture].verts;
+
+	for(ep = params->b; ep != params->e; ep++){
+		idxtab = &(*ep)->indextab[OBJVGeometric];
+
+		t.p0 = Pt3(verts[idxtab->indices[0]].x,verts[idxtab->indices[0]].y,verts[idxtab->indices[0]].z,verts[idxtab->indices[0]].w);
+		t.p1 = Pt3(verts[idxtab->indices[1]].x,verts[idxtab->indices[1]].y,verts[idxtab->indices[1]].z,verts[idxtab->indices[1]].w);
+		t.p2 = Pt3(verts[idxtab->indices[2]].x,verts[idxtab->indices[2]].y,verts[idxtab->indices[2]].z,verts[idxtab->indices[2]].w);
+
+		t.p0 = xform3(t.p0, rota);
+		t.p1 = xform3(t.p1, rota);
+		t.p2 = xform3(t.p2, rota);
+
+		st.p0 = xform3(t.p0, view);
+		st.p1 = xform3(t.p1, view);
+		st.p2 = xform3(t.p2, view);
+		st.p0 = divpt3(st.p0, st.p0.w);
+		st.p1 = divpt3(st.p1, st.p1.w);
+		st.p2 = divpt3(st.p2, st.p2.w);
+
+		n = normvec3(crossvec3(subpt3(t.p2, t.p0), subpt3(t.p1, t.p0)));
+		intensity = dotvec3(n, light);
+		/* back-face culling */
+		if(intensity <= 0)
+			continue;
+
+		idxtab = &(*ep)->indextab[OBJVTexture];
+		if(modeltex != nil && idxtab->nindex == 3){
+			tt.p0 = Pt2(tverts[idxtab->indices[0]].u, tverts[idxtab->indices[0]].v, 1);
+			tt.p1 = Pt2(tverts[idxtab->indices[1]].u, tverts[idxtab->indices[1]].v, 1);
+			tt.p2 = Pt2(tverts[idxtab->indices[2]].u, tverts[idxtab->indices[2]].v, 1);
+		}else
+			memset(&tt, 0, sizeof tt);
+
+		filltriangle2(params->dst, st, tt, intensity, sp.frag);
+	}
+
+	freememimage(sp.frag);
+	sendp(params->donec, nil);
+	free(params);
+	threadexits(nil);
+}
+
+void
+shade2(Memimage *dst)
+{
+	int i, nelems, nparts;
+	OBJObject *o;
+	OBJElem **elems, *e;
+	OBJIndexArray *idxtab;
+	SUparams *params;
+	Channel *donec;
+
+	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)
+					continue;
+				elems = erealloc(elems, ++nelems*sizeof(*elems));
+				elems[nelems-1] = e;
+			}
+	nparts = nelems/nprocs;
+
+	donec = chancreate(sizeof(void*), 0);
+
+	for(i = 0; i < nprocs; i++){
+		params = emalloc(sizeof *params);
+		params->dst = dst;
+		params->b = &elems[i*nparts];
+		params->e = params->b + nparts;
+		params->id = i;
+		params->donec = donec;
+		proccreate(shaderunit2, params, mainstacksize);
+		fprint(2, "spawned su %d for elems %d to %d\n", params->id, i*nparts, i*nparts+nparts);
+	}
+
+	while(i--)
+		recvp(donec);
+	chanfree(donec);
+}
+
 Memimage *
 triangleshader(Sparams *sp)
 {
@@ -441,7 +643,7 @@
 	OBJElem *e;
 	OBJVertex *verts, *tverts;		/* geometric and texture vertices */
 	OBJIndexArray *idxtab;
-	Triangle3 t;
+	Triangle3 t, t₂;
 	Triangle2 st, tt;			/* screen and texture triangles */
 	Rectangle bbox;
 	Point3 bc, n;				/* barycentric coords and surface normal */
@@ -453,6 +655,7 @@
 
 	verts = model->vertdata[OBJVGeometric].verts;
 	tverts = model->vertdata[OBJVTexture].verts;
+	cbuf[0] = 0xFF;
 
 	for(i = 0; i < nelem(model->objtab); i++)
 		for(o = model->objtab[i]; o != nil; o = o->next)
@@ -467,10 +670,21 @@
 				t.p1 = Pt3(verts[idxtab->indices[1]].x,verts[idxtab->indices[1]].y,verts[idxtab->indices[1]].z,verts[idxtab->indices[1]].w);
 				t.p2 = Pt3(verts[idxtab->indices[2]].x,verts[idxtab->indices[2]].y,verts[idxtab->indices[2]].z,verts[idxtab->indices[2]].w);
 
-				st.p0 = Pt2((t.p0.x+1)*Dx(fb->r)/2, (-t.p0.y+1)*Dy(fb->r)/2, 1);
-				st.p1 = Pt2((t.p1.x+1)*Dx(fb->r)/2, (-t.p1.y+1)*Dy(fb->r)/2, 1);
-				st.p2 = Pt2((t.p2.x+1)*Dx(fb->r)/2, (-t.p2.y+1)*Dy(fb->r)/2, 1);
+				t.p0 = xform3(t.p0, rota);
+				t.p1 = xform3(t.p1, rota);
+				t.p2 = xform3(t.p2, rota);
 
+				t₂.p0 = xform3(t.p0, view);
+				t₂.p1 = xform3(t.p1, view);
+				t₂.p2 = xform3(t.p2, view);
+				t₂.p0 = divpt3(t₂.p0, t₂.p0.w);
+				t₂.p1 = divpt3(t₂.p1, t₂.p1.w);
+				t₂.p2 = divpt3(t₂.p2, t₂.p2.w);
+
+				st.p0 = Pt2(t₂.p0.x, t₂.p0.y, 1);
+				st.p1 = Pt2(t₂.p1.x, t₂.p1.y, 1);
+				st.p2 = Pt2(t₂.p2.x, t₂.p2.y, 1);
+
 				bbox = Rect(
 					min(min(st.p0.x, st.p1.x), st.p2.x), min(min(st.p0.y, st.p1.y), st.p2.y),
 					max(max(st.p0.x, st.p1.x), st.p2.x)+1, max(max(st.p0.y, st.p1.y), st.p2.y)+1
@@ -482,15 +696,14 @@
 				if(bc.x < 0 || bc.y < 0 || bc.z < 0)
 					continue;
 
-				z = t.p0.z*bc.x + t.p1.z*bc.y + t.p2.z*bc.z;
+				z = t₂.p0.z*bc.x + t₂.p1.z*bc.y + t₂.p2.z*bc.z;
 				if(z <= zbuf[sp->p.x+sp->p.y*Dx(fb->r)])
 					continue;
 				zbuf[sp->p.x+sp->p.y*Dx(fb->r)] = z;
 
-				cbuf[0] = 0xFF;
-				cbuf[1] = 0xFF*fabs(z);
-				cbuf[2] = 0xFF*fabs(z);
-				cbuf[3] = 0xFF*fabs(z);
+				cbuf[1] = 0xFF*z;
+				cbuf[2] = 0xFF*z;
+				cbuf[3] = 0xFF*z;
 				memfillcolor(sp->frag, *(ulong*)cbuf);
 				pixel(zfb, sp->p, sp->frag);
 
@@ -497,7 +710,7 @@
 				n = normvec3(crossvec3(subpt3(t.p2, t.p0), subpt3(t.p1, t.p0)));
 				intensity = dotvec3(n, light);
 				/* back-face culling */
-				if(intensity < 0)
+				if(intensity <= 0)
 					continue;
 
 				idxtab = &e->indextab[OBJVTexture];
@@ -511,12 +724,10 @@
 					tt.p2 = mulpt2(tt.p2, bc.z);
 
 					tp.x = (tt.p0.x + tt.p1.x + tt.p2.x)*Dx(modeltex->r);
-					tp.y = Dy(modeltex->r)-(tt.p0.y + tt.p1.y + tt.p2.y)*Dy(modeltex->r);
+					tp.y = (1 - (tt.p0.y + tt.p1.y + tt.p2.y))*Dy(modeltex->r);
 
-					cbuf[0] = 0xFF;
 					unloadmemimage(modeltex, rectaddpt(Rect(0,0,1,1), tp), cbuf+1, sizeof cbuf - 1);
 				}else{
-					cbuf[0] = 0xFF;
 					cbuf[1] = 0xFF;
 					cbuf[2] = 0xFF;
 					cbuf[3] = 0xFF;
@@ -547,7 +758,10 @@
 	uvlong t0, t1;
 
 	t0 = nanosec();
-	shade(fb, model != nil? modelshader: sfshader);
+	if(flag2)
+		shade2(fb);
+	else
+		shade(fb, model != nil? modelshader: sfshader);
 	t1 = nanosec();
 	fprint(2, "shader took %lludns\n", t1-t0);
 }
@@ -608,8 +822,12 @@
 }
 
 void
-lmb(Mousectl *, Keyboardctl *)
+lmb(Mousectl *mc, Keyboardctl *)
 {
+	Point p;
+
+	p = subpt(mc->xy, screen->r.min);
+	fprint(2, "p %P z %g\n", p, zbuf[p.x + p.y*Dx(fb->r)]);
 }
 
 void
@@ -634,7 +852,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-n nprocs] [-m objfile] [-t texfile]\n", argv0);
+	fprint(2, "usage: %s [-2] [-n nprocs] [-m objfile] [-t texfile] [-a yrotangle] [-z camzpos]\n", argv0);
 	exits("usage");
 }
 
@@ -645,11 +863,16 @@
 	Keyboardctl *kc;
 	Rune r;
 	char *mdlpath, *texpath;
+	double θ;
 
 	GEOMfmtinstall();
 	mdlpath = nil;
 	texpath = nil;
+	θ = 0;
 	ARGBEGIN{
+	case '2':
+		flag2++;
+		break;
 	case 'n':
 		nprocs = strtoul(EARGF(usage()), nil, 10);
 		break;
@@ -659,6 +882,12 @@
 	case 't':
 		texpath = EARGF(usage());
 		break;
+	case 'a':
+		θ = strtoul(EARGF(usage()), nil, 10)*DEG;
+		break;
+	case 'z':
+		camera.z = strtod(EARGF(usage()), nil);
+		break;
 	default: usage();
 	}ARGEND;
 	if(argc != 0)
@@ -697,6 +926,16 @@
 	display->locking = 1;
 	unlockdisplay(display);
 
+	proj[3][2] = -1.0/camera.z;
+	Matrix3 yrot = {
+		cos(θ), 0, -sin(θ), 0,
+		0, 1, 0, 0,
+		sin(θ), 0, cos(θ), 0,
+		0, 0, 0, 1,
+	};
+	identity3(rota);
+	mulm3(rota, yrot);
+	mulm3(view, proj);
 	rendering = 1;
 	proccreate(renderer, nil, mainstacksize);
 	threadcreate(scrsync, nil, mainstacksize);