shithub: qk1

Download patch

ref: 48d333cfbbf7349058787093c08d9c52b8df1aeb
parent: 7240eb5e3f0f766939ab5f14add6384b286d253a
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Mon Oct 30 12:16:07 EDT 2023

.alpha: part 3 - build full alphamaps on startup once

--- a/d_alpha.c
+++ b/d_alpha.c
@@ -4,50 +4,119 @@
 #include "quakedef.h"
 #include "fns.h"
 
-/* FIXME(sigrid): this is super dumb.
- *
- * how it SHOULD be done:
- * 1) no point in 256 alphamaps, a'=255-a → (x->a->y) ≡ (y->a'->x)
- * 1.1) these are 8-bit colors, no way there are *different* variations for each alpha
- *      eg: x=3, y=9 → a≠a' → there is a very high chance (x->a->y) = (x->a'->y) if a is close to a'
- * 2) use k-d (3-d in this case) tree to look up colors faster
- * 3) if it's fast enough, can build all alpha maps just once on startup
- * 4) try different color models, this alpha blending looks like crap with liquids
- */
-byte *alphamap[256];
+typedef struct Col Col;
 
+struct Col {
+	Col *s[2];
+	u8int c[3];
+	u8int x;
+};
+
+static Col *lookup;
+
+static void
+ins(u8int c[3], u8int x)
+{
+	Col **p, *e, *l;
+	int i;
+
+	for(l = lookup, p = &lookup, e = lookup, i = 0; e != nil; i = (i+1)%3){
+		l = *p;
+		p = &e->s[c[i] >= e->c[i]];
+		e = *p;
+	}
+	if(l != nil && memcmp(l->c, c, 3) == 0)
+		return;
+	e = calloc(1, sizeof(*e));
+	memmove(e->c, c, 3);
+	e->x = x;
+	*p = e;
+}
+
+static void
+closest_(u8int c[3], Col *t, int i, Col **best, int *min)
+{
+	int d, d2, nd;
+
+	if(t == nil)
+		return;
+
+	i %= 3;
+	nd = (int)t->c[i] - (int)c[i];
+	i = (i+1)%3;
+	d = (int)t->c[i] - (int)c[i];
+	i = (i+1)%3;
+	d2 = (int)t->c[i] - (int)c[i];
+	d = nd*nd + d*d + d2*d2;
+
+	if(*min > d){
+		*min = d;
+		*best = t;
+	}
+
+	i += 2;
+	closest_(c, t->s[nd <= 0], i, best, min);
+	if(nd*nd < *min)
+		closest_(c, t->s[nd > 0], i, best, min);
+}
+
+static Col *
+closest(u8int c[3])
+{
+	Col *best;
+	int min;
+
+	best = nil;
+	min = 99999;
+	closest_(c, lookup, 0, &best, &min);
+	return best;
+}
+
+byte *alphamap[256>>AlphaStep];
+
 void
-buildalpha(int alpha)
+initalpha(void)
 {
 	extern s32int fbpal[256];
-	int a, b;
+	int a, b, ai, alpha, i;
 	byte *ca, *cb, *p;
-	int rr, gg, bb;
-	int i, dst, x, best;
+	u8int c[3];
+	Col *best;
 
-	if(alphamap[alpha] != nil)
+	if(lookup != nil)
 		return;
-	alphamap[alpha] = malloc(256*256);
+
 	p = (byte*)fbpal;
-	ca = p;
-	for(a = 0; a < 256; a++, ca += 4){
-		cb = p;
-		for(b = 0; b < 256; b++, cb++){
-			rr = (alpha*ca[0] + (255 - alpha)*(*cb++))>>8;
-			gg = (alpha*ca[1] + (255 - alpha)*(*cb++))>>8;
-			bb = (alpha*ca[2] + (255 - alpha)*(*cb++))>>8;
-			dst = 9999999;
-			best = 255;
-			for(i = 0; i < 768; i += 4){
-				x = (rr-p[i+0])*(rr-p[i+0]) +
-					(gg-p[i+1])*(gg-p[i+1]) +
-					(bb-p[i+2])*(bb-p[i+2]);
-				if(x < dst){
-					dst = x;
-					best = i;
-				}
+	for(i = 0; i < 64; i++){
+		ins(p+(64+i)*4, 64+i);
+		ins(p+(63-i)*4, 63-i);
+		ins(p+(128+i)*4, 128+i);
+		ins(p+(255-i)*4, 255-i);
+	}
+
+	for(alpha = 1; alpha <= 128; alpha += 1<<AlphaStep){
+		ai = alpha >> AlphaStep;
+		alphamap[ai] = malloc(256*256);
+
+		ca = p;
+		for(a = 0; a < 256; a++, ca += 4){
+			cb = p;
+			for(b = 0; b < 256; b++, cb++){
+				c[0] = (alpha*ca[0] + (255 - alpha)*(*cb++))>>8;
+				c[1] = (alpha*ca[1] + (255 - alpha)*(*cb++))>>8;
+				c[2] = (alpha*ca[2] + (255 - alpha)*(*cb++))>>8;
+				best = closest(c);
+				alphamap[ai][a<<8 | b] = best->x;
 			}
-			alphamap[alpha][a<<8 | b] = best/4;
+		}
+	}
+	for(alpha = 128+AlphaStep; alpha < 256; alpha += 1<<AlphaStep){
+		ai = alpha >> AlphaStep;
+		alphamap[ai] = malloc(256*256);
+		i = (256-alpha)>>AlphaStep;
+		for(a = 0; a < 256; a++){
+			for(b = 0; b < 256; b++)
+				alphamap[ai][b<<8 | a] = alphamap[i][a<<8 | b];
 		}
 	}
 }
--- a/d_edge.c
+++ b/d_edge.c
@@ -194,7 +194,6 @@
 				alpha *= alphafor(s->flags);
 			if(alpha < 1)
 				alpha = 255;
-			buildalpha(alpha);
 
 			r_drawnpolycount++;
 
--- a/d_local.h
+++ b/d_local.h
@@ -84,10 +84,15 @@
 
 extern void (*d_drawspans) (espan_t *pspan, byte alpha);
 
-extern byte *alphamap[256];
+enum {
+	// perhaps a bit too much, but looks ok
+	AlphaStep = 2,
+};
 
+extern byte *alphamap[256>>AlphaStep];
+
 #define blendalpha(a, b, alpha) \
-	alphamap[alpha][(u16int)((a)<<8 | (b))]
+	alphamap[alpha>>AlphaStep][(u16int)((a)<<8 | (b))]
 
-void buildalpha(int alpha);
+void initalpha(void);
 float alphafor(int flags);
--- a/r_main.c
+++ b/r_main.c
@@ -470,8 +470,6 @@
 
 		if (currententity == &cl_entities[cl.viewentity])
 			continue;	// don't draw the player
-		if(r_drawflags & DRAW_BLEND)
-			buildalpha(currententity->alpha);
 
 		// FIXME(sigrid): no trans-specific surfaces, hopefully?
 		if(entdrawflags(currententity) ^ r_drawflags)
--- a/vid.c
+++ b/vid.c
@@ -139,6 +139,8 @@
 	for(fp=fbpal; fp<fbpal+nelem(fbpal); p+=3, fp++)
 		*fp = p[0] << 16 | p[1] << 8 | p[2];
 
+	initalpha();
+
 	scr_fullupdate = 0;
 }