shithub: libgraphics

Download patch

ref: 96eb8b3c74e8d95579dbd8a3727b7c25f4d49ba2
parent: 6b71bf694a9fa2e694f3e70a2067e0021b2c2f07
author: rodri <rgl@antares-labs.eu>
date: Wed Mar 6 07:41:00 EST 2024

add a texture sampler with nearest and bilinear routines.

pass the material reference along with the vertices.
also implemented back-face culling, but it's disabled
for now.

--- a/graphics.h
+++ b/graphics.h
@@ -38,6 +38,28 @@
 	double r, g, b, a;
 };
 
+/*
+ * a more general approach worth investigating.
+ * it could be made to handle types other than double.
+ *
+ * examples:
+ * 	double intens;
+ * 	addvattr(v, "intensity", 1, &intens);
+ *
+ * 	Point3 p;
+ * 	addvattr(v, "normal", 3, &p);
+ *
+ * 	Matrix3 m;
+ * 	addvattr(v, "proj", 4*4, m);
+ */
+//struct Vertexattr
+//{
+//	char *id;
+//	int type;
+//	ulong len;
+//	double val[];
+//};
+
 struct Vertexattr
 {
 	char *id;
@@ -54,6 +76,7 @@
 	Point3 n;		/* surface normal */
 	Color c;		/* shading color */
 	Point2 uv;		/* texture coordinate */
+	OBJMaterial *mtl;
 	/* TODO it'd be neat to use a dynamic hash table instead */
 	Vertexattr *attrs;	/* attributes (aka varyings) */
 	ulong nattrs;
@@ -222,6 +245,11 @@
 /* vertex */
 void addvattr(Vertex*, char*, int, void*);
 Vertexattr *getvattr(Vertex*, char*);
+
+/* texture */
+Color neartexsampler(Memimage*, Point2);
+Color bilitexsampler(Memimage*, Point2);
+Color texture(Memimage*, Point2, Color(*)(Memimage*, Point2));
 
 /* util */
 double fmin(double, double);
--- a/mkfile
+++ b/mkfile
@@ -7,6 +7,7 @@
 	render.$O\
 	scene.$O\
 	vertex.$O\
+	texture.$O\
 	alloc.$O\
 	fb.$O\
 	shadeop.$O\
--- a/render.c
+++ b/render.c
@@ -30,6 +30,17 @@
 	return 1;
 }
 
+static int
+isfacingback(Triangle t)
+{
+	double sa;	/* signed area */
+
+	sa = t[0].p.x * t[1].p.y - t[0].p.y * t[1].p.x +
+	     t[1].p.x * t[2].p.y - t[1].p.y * t[2].p.x +
+	     t[2].p.x * t[0].p.y - t[2].p.y * t[0].p.x;
+	return sa <= 0;
+}
+
 static void
 mulsdm(double r[6], double m[6][4], Point3 p)
 {
@@ -213,7 +224,7 @@
 static Point3
 clip2ndc(Point3 p)
 {
-	p.w = 1.0/p.w;
+	p.w = p.w == 0? 1: 1.0/p.w;
 	p.x *= p.w;
 	p.y *= p.w;
 	p.z *= p.w;
@@ -275,7 +286,7 @@
 	FSparams fsp;
 	Triangle2 t₂;
 	Rectangle bbox;
-	Point p, tp;
+	Point p;
 	Point3 bc;
 	double z, depth;
 	uchar cbuf[4];
@@ -325,31 +336,11 @@
 			bc = mulpt3(bc, z);
 			berpvertex(&fsp.v, &t[0], &t[1], &t[2], bc);
 
-			if(fsp.v.uv.w != 0){
-				tp.x = fsp.v.uv.x*Dx(params->entity->mdl->tex->r);
-				tp.y = (1 - fsp.v.uv.y)*Dy(params->entity->mdl->tex->r);
+			cbuf[0] = fsp.v.c.a*0xFF;
+			cbuf[1] = fsp.v.c.b*0xFF;
+			cbuf[2] = fsp.v.c.g*0xFF;
+			cbuf[3] = fsp.v.c.r*0xFF;
 
-				switch(params->entity->mdl->tex->chan){
-				case RGB24:
-					unloadmemimage(params->entity->mdl->tex, rectaddpt(UR, tp), cbuf+1, sizeof cbuf - 1);
-					cbuf[0] = 0xFF;
-					break;
-				case RGBA32:
-					unloadmemimage(params->entity->mdl->tex, rectaddpt(UR, tp), cbuf, sizeof cbuf);
-					break;
-				case XRGB32:
-					unloadmemimage(params->entity->mdl->tex, rectaddpt(UR, tp), cbuf, sizeof cbuf);
-					memmove(cbuf+1, cbuf, 3);
-					cbuf[0] = 0xFF;
-					break;
-				}
-			}else{
-				cbuf[0] = fsp.v.c.a*0xFF;
-				cbuf[1] = fsp.v.c.b*0xFF;
-				cbuf[2] = fsp.v.c.g*0xFF;
-				cbuf[3] = fsp.v.c.r*0xFF;
-			}
-
 			fsp.p = p;
 			pixel(params->fb->cb, p, params->fshader(&fsp));
 			delvattrs(&fsp.v);
@@ -386,17 +377,32 @@
 		nt = 1;	/* start with one. after clipping it might change */
 
 		idxtab = &(*ep)->indextab[OBJVGeometric];
-		t[0][0].p = Pt3(verts[idxtab->indices[0]].x,verts[idxtab->indices[0]].y,verts[idxtab->indices[0]].z,verts[idxtab->indices[0]].w);
-		t[0][1].p = Pt3(verts[idxtab->indices[1]].x,verts[idxtab->indices[1]].y,verts[idxtab->indices[1]].z,verts[idxtab->indices[1]].w);
-		t[0][2].p = Pt3(verts[idxtab->indices[2]].x,verts[idxtab->indices[2]].y,verts[idxtab->indices[2]].z,verts[idxtab->indices[2]].w);
+		t[0][0].p = Pt3(verts[idxtab->indices[0]].x,
+				verts[idxtab->indices[0]].y,
+				verts[idxtab->indices[0]].z,
+				verts[idxtab->indices[0]].w);
+		t[0][1].p = Pt3(verts[idxtab->indices[1]].x,
+				verts[idxtab->indices[1]].y,
+				verts[idxtab->indices[1]].z,
+				verts[idxtab->indices[1]].w);
+		t[0][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][0].n = Vec3(nverts[idxtab->indices[0]].i, nverts[idxtab->indices[0]].j, nverts[idxtab->indices[0]].k);
+			t[0][0].n = Vec3(nverts[idxtab->indices[0]].i,
+					 nverts[idxtab->indices[0]].j,
+					 nverts[idxtab->indices[0]].k);
 			t[0][0].n = normvec3(t[0][0].n);
-			t[0][1].n = Vec3(nverts[idxtab->indices[1]].i, nverts[idxtab->indices[1]].j, nverts[idxtab->indices[1]].k);
+			t[0][1].n = Vec3(nverts[idxtab->indices[1]].i,
+					 nverts[idxtab->indices[1]].j,
+					 nverts[idxtab->indices[1]].k);
 			t[0][1].n = normvec3(t[0][1].n);
-			t[0][2].n = Vec3(nverts[idxtab->indices[2]].i, nverts[idxtab->indices[2]].j, nverts[idxtab->indices[2]].k);
+			t[0][2].n = Vec3(nverts[idxtab->indices[2]].i,
+					 nverts[idxtab->indices[2]].j,
+					 nverts[idxtab->indices[2]].k);
 			t[0][2].n = normvec3(t[0][2].n);
 		}else{
 			/* TODO build a list of per-vertex normals earlier */
@@ -406,18 +412,19 @@
 
 		idxtab = &(*ep)->indextab[OBJVTexture];
 		if(params->entity->mdl->tex != nil && idxtab->nindex == 3){
-			t[0][0].uv = Pt2(tverts[idxtab->indices[0]].u, tverts[idxtab->indices[0]].v, 1);
-			t[0][1].uv = Pt2(tverts[idxtab->indices[1]].u, tverts[idxtab->indices[1]].v, 1);
-			t[0][2].uv = Pt2(tverts[idxtab->indices[2]].u, tverts[idxtab->indices[2]].v, 1);
+			t[0][0].uv = Pt2(tverts[idxtab->indices[0]].u,
+					 tverts[idxtab->indices[0]].v, 1);
+			t[0][1].uv = Pt2(tverts[idxtab->indices[1]].u,
+					 tverts[idxtab->indices[1]].v, 1);
+			t[0][2].uv = Pt2(tverts[idxtab->indices[2]].u,
+					 tverts[idxtab->indices[2]].v, 1);
 		}else{
 			t[0][0].uv = t[0][1].uv = t[0][2].uv = Vec2(0,0);
 		}
 
 		for(i = 0; i < 3; i++){
-			t[0][i].c.r = (*ep)->mtl != nil? (*ep)->mtl->Kd.r: 1;
-			t[0][i].c.g = (*ep)->mtl != nil? (*ep)->mtl->Kd.g: 1;
-			t[0][i].c.b = (*ep)->mtl != nil? (*ep)->mtl->Kd.b: 1;
-			t[0][i].c.a = 1;
+			t[0][i].c = Pt3(1,1,1,1);
+			t[0][i].mtl = (*ep)->mtl;
 			t[0][i].attrs = nil;
 			t[0][i].nattrs = 0;
 		}
@@ -436,12 +443,21 @@
 			nt = cliptriangle(t);
 
 		while(nt--){
-			t[nt][0].p = ndc2viewport(params->fb, clip2ndc(t[nt][0].p));
-			t[nt][1].p = ndc2viewport(params->fb, clip2ndc(t[nt][1].p));
-			t[nt][2].p = ndc2viewport(params->fb, clip2ndc(t[nt][2].p));
+			t[nt][0].p = clip2ndc(t[nt][0].p);
+			t[nt][1].p = clip2ndc(t[nt][1].p);
+			t[nt][2].p = clip2ndc(t[nt][2].p);
 
+			/* culling */
+//			if(isfacingback(t[nt]))
+//				goto skiptri;
+
+			t[nt][0].p = ndc2viewport(params->fb, t[nt][0].p);
+			t[nt][1].p = ndc2viewport(params->fb, t[nt][1].p);
+			t[nt][2].p = ndc2viewport(params->fb, t[nt][2].p);
+
 			rasterize(params, t[nt], frag);
 
+//skiptri:
 			delvattrs(&t[nt][0]);
 			delvattrs(&t[nt][1]);
 			delvattrs(&t[nt][2]);
--- /dev/null
+++ b/texture.c
@@ -1,0 +1,104 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
+
+/*
+ * uv-coords belong to the 4th quadrant (v grows bottom-up),
+ * hence the need to reverse the v coord.
+ */
+static Point
+uv2tp(Point2 uv, Memimage *i)
+{
+	assert(uv.x >= 0 && uv.x <= 1 && uv.y >= 0 && uv.y <= 1);
+	return Pt(uv.x*Dx(i->r), (1 - uv.y)*Dy(i->r));
+}
+
+static Color
+ul2col(ulong l)
+{
+	Color c;
+
+	c.a = (l     & 0xff)/255.0;
+	c.b = (l>>8  & 0xff)/255.0;
+	c.g = (l>>16 & 0xff)/255.0;
+	c.r = (l>>24 & 0xff)/255.0;
+	return c;
+}
+
+static Color
+cbuf2col(uchar b[4])
+{
+	Color c;
+
+	c.a = b[0] / 255.0;
+	c.b = b[1] / 255.0;
+	c.g = b[2] / 255.0;
+	c.r = b[3] / 255.0;
+	return c;
+}
+
+static Color
+_memreadpixel(Memimage *i, Point sp)
+{
+	uchar cbuf[4];
+
+	switch(i->chan){
+	case RGB24:
+		unloadmemimage(i, rectaddpt(UR, sp), cbuf+1, sizeof cbuf - 1);
+		cbuf[0] = 0xFF;
+		break;
+	case RGBA32:
+		unloadmemimage(i, rectaddpt(UR, sp), cbuf, sizeof cbuf);
+		break;
+	case XRGB32:
+		unloadmemimage(i, rectaddpt(UR, sp), cbuf, sizeof cbuf);
+		memmove(cbuf+1, cbuf, 3);
+		cbuf[0] = 0xFF;
+		break;
+	}
+
+	return cbuf2col(cbuf);
+}
+
+Color
+neartexsampler(Memimage *i, Point2 uv)
+{
+	return _memreadpixel(i, uv2tp(uv, i));
+}
+
+Color
+bilitexsampler(Memimage *i, Point2 uv)
+{
+	Rectangle r;
+	Color c1, c2;
+
+	r = rectaddpt(UR, uv2tp(uv, i));
+	if(r.min.x < i->r.min.x){
+		r.min.x++;
+		r.max.x++;
+	}if(r.min.y < i->r.min.y){
+		r.min.y++;
+		r.max.y++;
+	}if(r.max.x >= i->r.max.x){
+		r.min.x--;
+		r.max.x--;
+	}if(r.max.y >= i->r.max.y){
+		r.min.y--;
+		r.max.y--;
+	}
+	c1 = lerp3(_memreadpixel(i, r.min), _memreadpixel(i, Pt(r.max.x, r.min.y)), 0.5);
+	c2 = lerp3(_memreadpixel(i, Pt(r.min.x, r.max.y)), _memreadpixel(i, r.max), 0.5);
+	return lerp3(c1, c2, 0.5);
+}
+
+Color
+texture(Memimage *i, Point2 uv, Color(*sampler)(Memimage*,Point2))
+{
+	return sampler(i, uv);
+}