shithub: qk1

Download patch

ref: 5c840ffec2b14ebf8ccede0f837c34b7eb159274
parent: 3a83bca01f54db3fdbde78658787664968b04aab
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Thu Jan 4 11:17:00 EST 2024

more precise alpha/fog blending

--- a/d_alpha.c
+++ b/d_alpha.c
@@ -5,21 +5,21 @@
 {
 	int a, b, c;
 
-	if(ca == 0 || (ca >> 24) != 0)
-		alpha = (ca >> 24)*alpha >> 8;
+	if(ca == 0 || (ca >> 24) != 0){
+		alpha = alpha*(ca >> 24) + 0x80;
+		alpha = ((alpha>>8) + alpha) >> 8;
+	}
 
+	ca = mulalpha(ca, alpha);
+
 	if(currententity != nil && currententity->effects & EF_ADDITIVE){
-		a = (alpha*((ca>> 0)&0xff) + 255*((cb>> 0)&0xff))>> 8;
-		b = (alpha*((ca>> 8)&0xff) + 255*((cb>> 8)&0xff))>> 8;
-		c = (alpha*((ca>>16)&0xff) + 255*((cb>>16)&0xff))>> 8;
+		a = ((ca>> 0)&0xff) + ((cb>> 0)&0xff);
+		b = ((ca>> 8)&0xff) + ((cb>> 8)&0xff);
+		c = ((ca>>16)&0xff) + ((cb>>16)&0xff);
 		return (cb & 0xff000000) | min(a, 255) | min(b, 255)<<8 | min(c, 255)<<16;
 	}
 
-	return
-		(cb & 0xff000000) |
-		((alpha*((ca>> 0)&0xff) + (255-alpha)*((cb>> 0)&0xff))>> 8) << 0 |
-		((alpha*((ca>> 8)&0xff) + (255-alpha)*((cb>> 8)&0xff))>> 8) << 8 |
-		((alpha*((ca>>16)&0xff) + (255-alpha)*((cb>>16)&0xff))>> 8) << 16;
+	return ca + mulalpha(cb, 255-alpha);
 }
 
 float
--- a/d_local.h
+++ b/d_local.h
@@ -83,5 +83,21 @@
 extern int d_minmip;
 extern float d_scalemip[3];
 
+static inline pixel_t
+mulalpha(pixel_t ca, int alpha)
+{
+	pixel_t a, b;
+
+	a = ((ca >> 0) & 0xff00ff)*alpha + 0x800080;
+	a = (a + ((a >> 8) & 0xff00ff)) >> 8;
+	a &= 0x00ff00ff;
+
+	b = ((ca >> 8) & 0xff00ff)*alpha + 0x800080;
+	b = (b + ((b >> 8) & 0xff00ff)) >> 0;
+	b &= 0xff00ff00;
+
+	return a | b;
+}
+
 pixel_t blendalpha(pixel_t ca, pixel_t cb, int alpha);
 float alphafor(int flags);
--- a/d_scan.c
+++ b/d_scan.c
@@ -165,7 +165,8 @@
 			sturb = ((s + r_turb_turb[(t>>16)&(CYCLE-1)])>>16)&63;
 			tturb = ((t + r_turb_turb[(s>>16)&(CYCLE-1)])>>16)&63;
 			*pdest = blendalpha(*(pbase + (tturb<<6) + sturb), *pdest, alpha);
-			*pz = izi; // FIXME(sigrid): can always update this one?
+			if(noblend)
+				*pz = izi;
 		}
 		s += sstep;
 		t += tstep;
@@ -191,7 +192,8 @@
 			sturb = ((s + r_turb_turb[(t>>16)&(CYCLE-1)])>>16)&63;
 			tturb = ((t + r_turb_turb[(s>>16)&(CYCLE-1)])>>16)&63;
 			*pdest = blendalpha(blendfog(*(pbase + (tturb<<6) + sturb), *fog), *pdest, alpha);
-			*pz = izi; // FIXME(sigrid): can always update this one?
+			if(noblend)
+				*pz = izi;
 		}
 		s += sstep;
 		t += tstep;
@@ -213,7 +215,7 @@
 	float		sdivz, tdivz, zi, z, du, dv;
 	float		sdivzstepu, tdivzstepu, zistepu;
 	fog_t fog;
-	bool fogged;
+	bool fogged, fogenabled;
 
 	sstep = 0;	// keep compiler happy
 	tstep = 0;	// ditto
@@ -224,6 +226,8 @@
 	zistepu = dvars.zistepu * 16;
 	izistep = (int)(dvars.zistepu * 0x8000 * 0x10000);
 
+	fogenabled = isfogged();
+
 	do{
 		pdest = dvars.viewbuffer + pspan->v*dvars.width + pspan->u;
 		pz = dvars.zbuffer + pspan->v*dvars.width + pspan->u;
@@ -249,6 +253,7 @@
 			// calculate s and t at the far end of the span
 			spancount = min(count, 16);
 			count -= spancount;
+			fogged = fogenabled && fogcalc(izi, izi + izistep*spancount, spancount, &fog);
 
 			if(count){
 				// calculate s/z, t/z, zi->fixed s and t at far end of span,
@@ -296,7 +301,6 @@
 				}
 			}
 
-			fogged = isfogged() ? fogcalc(izi, izi + izistep*spancount, spancount, &fog) : false;
 			if(fogged){
 				switch(spanfunc){
 				case SPAN_SOLID:
--- a/r_fog.c
+++ b/r_fog.c
@@ -43,6 +43,7 @@
 				fogvars.enabled = x > 0 ? (Enfog | Enskyfog) : 0;
 				setcvar("r_skyfog", x > 0 ? "1" : "0");
 			}
+			fogvars.pix = 0xff<<24 | fogvars.c2<<16 | fogvars.c1<<8 | fogvars.c0;
 			return;
 		}
 		fogvars.density = clamp(x, 0.0, 1.0) * 0.016;
@@ -57,6 +58,7 @@
 		x = atof(Cmd_Argv(i));
 		fogvars.c0 = 0xff * clamp(x, 0.0, 1.0);
 		r_skyfog_cb(&r_skyfog); /* recalculate sky fog */
+		fogvars.pix = 0xff<<24 | fogvars.c2<<16 | fogvars.c1<<8 | fogvars.c0;
 		break;
 	}
 	if(fogvars.density > 0.0)
@@ -70,6 +72,7 @@
 {
 	memset(&fogvars, 0, sizeof(fogvars));
 	fogvars.c0 = fogvars.c1 = fogvars.c2 = 0x80;
+	fogvars.pix = 0xff808080;
 	fogvars.allowed = r_fog.value > 0.0;
 	setcvar("r_skyfog", "0");
 }
--- a/r_fog.h
+++ b/r_fog.h
@@ -1,13 +1,14 @@
-#define fogstep(f) \
-	do{ \
-		(f).v[0] += (f).d[0]; \
-		(f).v[1] += (f).d[1]; \
-		(f).v[2] += (f).d[2]; \
-		(f).v[3] += (f).d[3]; \
-	}while(0)
+typedef struct fog_t fog_t;
 
-#define fogshift 8
+struct fog_t {
+	pixel64_t v;
+	pixel64_t d;
+};
 
+#define fogstep(f) do{ \
+		(f).v += (f).d; \
+	}while(0)
+
 static inline byte
 z2foga(uzint z)
 {
@@ -20,39 +21,48 @@
 	return 255*d;
 }
 
+static inline pixel64_t
+pixto64(pixel_t p)
+{
+	return
+		(pixel64_t)((p>>24)&0xff)<<56 |
+		(pixel64_t)((p>>16)&0xff)<<40 |
+		(pixel64_t)((p>> 8)&0xff)<<24 |
+		(pixel64_t)((p>> 0)&0xff)<< 8;
+}
+
 static inline pixel_t
+pixfrom64(pixel64_t p)
+{
+	return ((p>>56)&0xff)<<24 | ((p>>40)&0xff)<<16 | ((p>>24)&0xff)<<8 | ((p>>8)&0xff)<<0;
+}
+
+static inline pixel_t
 blendfog(pixel_t pix, fog_t fog)
 {
-	byte inva = 0xff - (fog.v[3]>>fogshift);
-	return
-		((fog.v[0] + ((inva*((pix>> 0)&0xff))<<fogshift)) >> (8 + fogshift)) << 0 |
-		((fog.v[1] + ((inva*((pix>> 8)&0xff))<<fogshift)) >> (8 + fogshift)) << 8 |
-		((fog.v[2] + ((inva*((pix>>16)&0xff))<<fogshift)) >> (8 + fogshift)) << 16|
-		(pix & (0xff<<24));
+	pixel_t a = pixfrom64(fog.v);
+	pixel_t b = mulalpha(pix, 0xff - (fog.v>>56));
+	return a + b;
 }
 
 static inline bool
 fogcalc(uzint zi0, uzint zi1, int cnt, fog_t *f)
 {
-	int end[3], v, e;
+	int v, e;
+	pixel64_t x;
 
 	if((v = z2foga(zi0)) == 0 || (e = z2foga(zi1)) == 0)
 		return false;
 
-	v <<= fogshift;
-	e <<= fogshift;
-
-	end[0] = e * fogvars.c0;
-	end[1] = e * fogvars.c1;
-	end[2] = e * fogvars.c2;
-	f->v[0] = v * fogvars.c0;
-	f->v[1] = v * fogvars.c1;
-	f->v[2] = v * fogvars.c2;
-	f->v[3] = v;
-	f->d[0] = (end[0] - f->v[0])/cnt;
-	f->d[1] = (end[1] - f->v[1])/cnt;
-	f->d[2] = (end[2] - f->v[2])/cnt;
-	f->d[3] = (e - v)/cnt;
+	f->v = pixto64(mulalpha(fogvars.pix, v));
+	x = pixto64(mulalpha(fogvars.pix, (e<v ? v-e : e-v)));
+	f->d =
+		(((x & (0xffffULL<<48))/cnt) & (0xffffULL<<48)) |
+		(((x & (0xffffULL<<32))/cnt) & (0xffffULL<<32)) |
+		(((x & (0xffffULL<<16))/cnt) & (0xffffULL<<16)) |
+		(((x & (0xffffULL<< 0))/cnt) & (0xffffULL<< 0));
+	if(e < v)
+		f->d = -f->d;
 
 	return true;
 }
--- a/r_shared.h
+++ b/r_shared.h
@@ -8,20 +8,15 @@
 	Enskyfog = 1<<1,
 };
 
-typedef struct fog_t fog_t;
 typedef struct fogvars_t fogvars_t;
 
-struct fog_t {
-	int v[4];
-	int d[4];
-};
-
 struct fogvars_t {
+	pixel_t pix;
 	float density;
-	byte c0, c1, c2, sky;
-	bool allowed;
-	int enabled;
 	int skyc0, skyc1, skyc2;
+	int enabled;
+	byte sky, c0, c1, c2;
+	bool allowed;
 };
 
 extern fogvars_t fogvars;
--- a/vid.h
+++ b/vid.h
@@ -4,6 +4,7 @@
 #define VID_GRADES	(1 << VID_CBITS)
 
 typedef u32int pixel_t;
+typedef u64int pixel64_t;
 
 typedef struct vrect_s
 {