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);
+}