shithub: libgraphics

Download patch

ref: aa8e41b4038d137edf5827ef6bb4330cab9f4768
parent: 61d9f77e3c7999e88cd616ba0c09912fe4838792
author: rodri <rgl@antares-labs.eu>
date: Fri Feb 20 14:04:49 EST 2026

render: finish incremental triangle rasterizer and other fixes

--- a/internal.h
+++ b/internal.h
@@ -15,6 +15,7 @@
 typedef struct fGradient fGradient;
 typedef struct pGradient pGradient;
 typedef struct vGradient vGradient;
+typedef struct Gradients Gradients;
 
 struct BPrimitive
 {
@@ -100,6 +101,14 @@
 	BVertex dy;
 };
 
+struct Gradients
+{
+	vGradient v;
+	pGradient bc;
+	fGradient z;
+	fGradient pcz;
+};
+
 /* alloc */
 void *_emalloc(ulong);
 void *_erealloc(void*, ulong);
@@ -127,7 +136,6 @@
 void _rmfbctl(Framebufctl*);
 
 /* vertex */
-void _loadvertex(BVertex*, BVertex*);
 void _lerpvertex(BVertex*, BVertex*, BVertex*, double);
 void _berpvertex(BVertex*, BVertex*, BVertex*, BVertex*, Point3);
 void _addvertex(BVertex*, BVertex*);
--- a/render.c
+++ b/render.c
@@ -229,7 +229,7 @@
 		pixel(cr, p, c, ropts & ROBlend);
 
 	task->clipr->min = minpt(task->clipr->min, p);
-	task->clipr->max = maxpt(task->clipr->max, addpt(p, (Point){1,1}));
+	task->clipr->max = maxpt(task->clipr->max, p);
 }
 
 static void
@@ -276,6 +276,8 @@
 	}
 
 	dp = subpt(p1, p0);
+	dplen = hypot(dp.x, dp.y);
+	dplen = dplen == 0? 0: 1.0/dplen;
 	Δe = 2*abs(dp.y);
 	e = 0;
 	Δy = p1.y > p0.y? 1: -1;
@@ -282,8 +284,7 @@
 
 	for(p = p0; p.x <= p1.x; p.x++){
 		Δp = subpt(p, p0);
-		dplen = hypot(dp.x, dp.y);
-		perc = dplen == 0? 0: hypot(Δp.x, Δp.y)/dplen;
+		perc = dplen*hypot(Δp.x, Δp.y);
 
 		if(steep) SWAP(int, &p.x, &p.y);
 
@@ -313,7 +314,7 @@
 			pixel(cr, p, c, ropts & ROBlend);
 
 		task->clipr->min = minpt(task->clipr->min, p);
-		task->clipr->max = maxpt(task->clipr->max, addpt(p, (Point){1,1}));
+		task->clipr->max = maxpt(task->clipr->max, p);
 discard:
 		if(steep) SWAP(int, &p.x, &p.y);
 
@@ -342,17 +343,46 @@
 }
 
 static void
+initgradients(Gradients *∇, BPrimitive *prim, Point2 p0)
+{
+	Point2 t[3];
+
+	t[0] = (Point2){prim->v[0].p.x, prim->v[0].p.y, 1};
+	t[1] = (Point2){prim->v[1].p.x, prim->v[1].p.y, 1};
+	t[2] = (Point2){prim->v[2].p.x, prim->v[2].p.y, 1};
+
+	∇->bc.p0 = _barycoords(t[0], t[1], t[2], p0);
+	∇->bc.dx = mulpt3((Point3){
+		t[2].y - t[1].y,
+		t[0].y - t[2].y,
+		t[1].y - t[0].y, 0}, ∇->bc.p0.w);
+	∇->bc.dy = mulpt3((Point3){
+		t[1].x - t[2].x,
+		t[2].x - t[0].x,
+		t[0].x - t[1].x, 0}, ∇->bc.p0.w);
+
+	_berpvertex(&∇->v.v0, prim->v+0, prim->v+1, prim->v+2, ∇->bc.p0);
+	_berpvertex(&∇->v.dx, prim->v+0, prim->v+1, prim->v+2, ∇->bc.dx);
+	_berpvertex(&∇->v.dy, prim->v+0, prim->v+1, prim->v+2, ∇->bc.dy);
+
+	∇->z.f0 = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, ∇->bc.p0);
+	∇->z.dx = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, ∇->bc.dx);
+	∇->z.dy = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, ∇->bc.dy);
+
+	∇->pcz.f0 = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, ∇->bc.p0);
+	∇->pcz.dx = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, ∇->bc.dx);
+	∇->pcz.dy = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, ∇->bc.dy);
+}
+
+static void
 rasterizetri(Rastertask *task)
 {
 	Shaderparams *sp;
 	Raster *cr, *zr;
 	BPrimitive *prim;
-	pGradient ∇bc;
-//	vGradient ∇v;
-//	fGradient ∇z, ∇pcz;
-//	BVertex v, *vp;
+	Gradients ∇;
+	BVertex v;
 	Point p;
-	Point2 t[3];
 	Point3 bc;
 	Color c;
 	float z, pcz;
@@ -366,79 +396,49 @@
 
 	ropts = sp->camera->rendopts;
 
-//	memset(&v, 0, sizeof v);
-//	vp = &v;
+	/* perspective divide vertex attributes */
+	_mulvertex(prim->v+0, prim->v[0].p.w);
+	_mulvertex(prim->v+1, prim->v[1].p.w);
+	_mulvertex(prim->v+2, prim->v[2].p.w);
 
-	t[0] = (Point2){prim->v[0].p.x, prim->v[0].p.y, 1};
-	t[1] = (Point2){prim->v[1].p.x, prim->v[1].p.y, 1};
-	t[2] = (Point2){prim->v[2].p.x, prim->v[2].p.y, 1};
+	initgradients(&∇, prim, (Point2){task->wr.min.x+0.5, task->wr.min.y+0.5, 1});
 
-	∇bc.p0 = _barycoords(t[0], t[1], t[2], (Point2){task->wr.min.x+0.5, task->wr.min.y+0.5, 1});
-	∇bc.dx = mulpt3((Point3){t[2].y - t[1].y, t[0].y - t[2].y, t[1].y - t[0].y, 0}, ∇bc.p0.w);
-	∇bc.dy = mulpt3((Point3){t[1].x - t[2].x, t[2].x - t[0].x, t[0].x - t[1].x, 0}, ∇bc.p0.w);
-
 //	/* TODO find a good method to apply the fill rule */
 //	if(istoporleft(&t[1], &t[2])){
-//		∇bc.p0.x -= ∇bc.p0.w;
-//		∇bc.dx.x -= ∇bc.p0.w;
-//		∇bc.dy.x -= ∇bc.p0.w;
+//		∇.bc.p0.x -= ∇.bc.p0.w;
+//		∇.bc.dx.x -= ∇.bc.p0.w;
+//		∇.bc.dy.x -= ∇.bc.p0.w;
 //	}
 //	if(istoporleft(&t[2], &t[0])){
-//		∇bc.p0.y -= ∇bc.p0.w;
-//		∇bc.dx.y -= ∇bc.p0.w;
-//		∇bc.dy.y -= ∇bc.p0.w;
+//		∇.bc.p0.y -= ∇.bc.p0.w;
+//		∇.bc.dx.y -= ∇.bc.p0.w;
+//		∇.bc.dy.y -= ∇.bc.p0.w;
 //	}
 //	if(istoporleft(&t[0], &t[1])){
-//		∇bc.p0.z -= ∇bc.p0.w;
-//		∇bc.dx.z -= ∇bc.p0.w;
-//		∇bc.dy.z -= ∇bc.p0.w;
+//		∇.bc.p0.z -= ∇.bc.p0.w;
+//		∇.bc.dx.z -= ∇.bc.p0.w;
+//		∇.bc.dy.z -= ∇.bc.p0.w;
 //	}
 
-	/* perspective divide vertex attributes */
-	_mulvertex(prim->v+0, prim->v[0].p.w);
-	_mulvertex(prim->v+1, prim->v[1].p.w);
-	_mulvertex(prim->v+2, prim->v[2].p.w);
-
-//	memset(&∇v, 0, sizeof ∇v);
-//	_berpvertex(&∇v.v0, prim->v+0, prim->v+1, prim->v+2, ∇bc.p0);
-//	_berpvertex(&∇v.dx, prim->v+0, prim->v+1, prim->v+2, ∇bc.dx);
-//	_berpvertex(&∇v.dy, prim->v+0, prim->v+1, prim->v+2, ∇bc.dy);
-//
-//	∇z.f0 = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, ∇bc.p0);
-//	∇z.dx = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, ∇bc.dx);
-//	∇z.dy = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, ∇bc.dy);
-//
-//	∇pcz.f0 = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, ∇bc.p0);
-//	∇pcz.dx = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, ∇bc.dx);
-//	∇pcz.dy = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, ∇bc.dy);
-
 	for(p.y = task->wr.min.y; p.y < task->wr.max.y; p.y++){
-		bc = ∇bc.p0;
-//		*sp->v = ∇v.v0;
-//		z = ∇z.f0;
-//		pcz = ∇pcz.f0;
+		bc = ∇.bc.p0;
+		*sp->v = ∇.v.v0;
+		z = ∇.z.f0;
+		pcz = ∇.pcz.f0;
 	for(p.x = task->wr.min.x; p.x < task->wr.max.x; p.x++){
 		if(bc.x < 0 || bc.y < 0 || bc.z < 0)
 			goto discard;
 
-		z = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, bc);
 		if((ropts & RODepth) && z <= getdepth(zr, p))
 			goto discard;
 
-		/* interpolate z⁻¹ and get actual z */
-		pcz = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, bc);
-		pcz = 1.0/(pcz < ε1? ε1: pcz);
-
 		/* perspective-correct attribute interpolation  */
-		_berpvertex(sp->v, prim->v+0, prim->v+1, prim->v+2, mulpt3(bc, pcz));
+		v = *sp->v;
+		_mulvertex(sp->v, 1.0/(pcz < ε1? ε1: pcz));
 
-//		_loadvertex(vp, sp->v);
-//		_mulvertex(vp, 1/(pcz < ε1? ε1: pcz));
-
-//		SWAP(BVertex*, &vp, &sp->v);
 		sp->p = p;
 		c = sp->stab->fs(sp);
-//		SWAP(BVertex*, &vp, &sp->v);
+		*sp->v = v;
 		if(c.a == 0)			/* discard non-colors */
 			goto discard;
 		if(ropts & RODepth)
@@ -449,17 +449,17 @@
 			pixel(cr, p, c, ropts & ROBlend);
 
 		task->clipr->min = minpt(task->clipr->min, p);
-		task->clipr->max = maxpt(task->clipr->max, addpt(p, (Point){1,1}));
+		task->clipr->max = maxpt(task->clipr->max, p);
 discard:
-		bc = addpt3(bc, ∇bc.dx);
-//		_addvertex(sp->v, &∇v.dx);
-//		z += ∇z.dx;
-//		pcz += ∇pcz.dx;
+		bc = addpt3(bc, ∇.bc.dx);
+		_addvertex(sp->v, &∇.v.dx);
+		z += ∇.z.dx;
+		pcz += ∇.pcz.dx;
 	}
-		∇bc.p0 = addpt3(∇bc.p0, ∇bc.dy);
-//		_addvertex(&∇v.v0, &∇v.dy);
-//		∇z.f0 += ∇z.dy;
-//		∇pcz.f0 += ∇pcz.dy;
+		∇.bc.p0 = addpt3(∇.bc.p0, ∇.bc.dy);
+		_addvertex(&∇.v.v0, &∇.v.dy);
+		∇.z.f0 += ∇.z.dy;
+		∇.pcz.f0 += ∇.pcz.dy;
 	}
 }
 
@@ -482,7 +482,6 @@
 	threadsetname("rasterizer %d", rp->id);
 
 	memset(&fsp, 0, sizeof fsp);
-	memset(&v, 0, sizeof v);
 	fsp.v = &v;
 	fsp.getuniform = sparams_getuniform;
 	fsp.getattr = sparams_getattr;
@@ -505,6 +504,8 @@
 					job->cliprects[0].max = maxpt(job->cliprects[0].max, job->cliprects[i].max);
 				}
 				job->fb->clipr = job->cliprects[0];
+				job->fb->clipr.max.x++;
+				job->fb->clipr.max.y++;
 
 				if(job->rctl->doprof)
 					job->times.Rn[rp->id].t1 = nanosec();
@@ -546,6 +547,8 @@
 	int i;
 
 	d->type = s->type;
+	d->tangent = s->tangent == NaI? ZP3: *(Point3*)itemarrayget(m->tangents, s->tangent);
+	d->mtl = s->mtl;
 	for(i = 0; i < s->type+1; i++){
 		v = itemarrayget(m->verts, s->v[i]);
 		d->v[i].p = *(Point3*)itemarrayget(m->positions, v->p);
@@ -552,9 +555,10 @@
 		d->v[i].n = v->n == NaI? ZP3: *(Point3*)itemarrayget(m->normals, v->n);
 		d->v[i].uv = v->uv == NaI? ZP2: *(Point2*)itemarrayget(m->texcoords, v->uv);
 		d->v[i].c = v->c == NaI? ZP3: *(Color*)itemarrayget(m->colors, v->c);
+		d->v[i].tangent = d->tangent;
+		d->v[i].mtl = d->mtl;
+		d->v[i].nattrs = 0;
 	}
-	d->tangent = s->tangent == NaI? ZP3: *(Point3*)itemarrayget(m->tangents, s->tangent);
-	d->mtl = s->mtl;
 	return d;
 }
 
@@ -624,9 +628,6 @@
 
 			switch(p->type){
 			case PPoint:
-				p->v[0].mtl = p->mtl;
-				p->v[0].nattrs = 0;
-
 				vsp.v = &p->v[0];
 				vsp.idx = 0;
 				p->v[0].p = vsp.stab->vs(&vsp);
@@ -650,9 +651,6 @@
 				break;
 			case PLine:
 				for(i = 0; i < 2; i++){
-					p->v[i].mtl = p->mtl;
-					p->v[i].nattrs = 0;
-
 					vsp.v = &p->v[i];
 					vsp.idx = i;
 					p->v[i].p = vsp.stab->vs(&vsp);
@@ -660,12 +658,11 @@
 
 				if(!isvisible(p->v[0].p) || !isvisible(p->v[1].p)){
 					np = _clipprimitive(p, cp);
+					if(np < 1)
+						break;
 					p = cp;
 				}
 
-				if(np < 1)
-					break;
-
 				p->v[0].p = clip2ndc(p->v[0].p);
 				p->v[1].p = clip2ndc(p->v[1].p);
 				p->v[0].p = ndc2viewport(vsp.fb, p->v[0].p);
@@ -686,10 +683,6 @@
 				break;
 			case PTriangle:
 				for(i = 0; i < 3; i++){
-					p->v[i].mtl = p->mtl;
-					p->v[i].nattrs = 0;
-					p->v[i].tangent = p->tangent;
-
 					vsp.v = &p->v[i];
 					vsp.idx = i;
 					p->v[i].p = vsp.stab->vs(&vsp);
--