From 769e90a45fde0f4b4c34dd1e2fbf628da41362d8 Mon Sep 17 00:00:00 2001
From: Sky <skylersaleh@gmail.com>
Date: Sat, 1 Jul 2023 22:14:57 -0700
Subject: [PATCH] [GL] Implement most fragment lighting

---
 src/core/renderer_gl/renderer_gl.cpp | 173 +++++++++++++++++++++++++--
 1 file changed, 165 insertions(+), 8 deletions(-)

diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp
index ed14c603..5a0ca3ad 100644
--- a/src/core/renderer_gl/renderer_gl.cpp
+++ b/src/core/renderer_gl/renderer_gl.cpp
@@ -210,21 +210,178 @@ const char* fragmentShader = R"(
 
 		return result;
 	}
+	#define D0_LUT 0u
+	#define D1_LUT 1u
+	#define SP_LUT 2u
+	#define FR_LUT 3u
+	#define RB_LUT 4u
+	#define RG_LUT 5u
+	#define RR_LUT 6u
+
+	float lutLookup(uint lut, float value){
+		//TODO: Implement this.
+		return value; 
+	}
+	vec3 regToColor(uint reg){
+		return vec3(
+			float(bitfieldExtract(reg,20,8))/255.,
+			float(bitfieldExtract(reg,10,8))/255.,
+			float(bitfieldExtract(reg,00,8))/255.
+		);
+	}
+	vec3 rotateVec3ByQuaternion(vec3 v, vec4 q){
+		vec3 u=q.xyz;
+		float s = q.w;
+		return 2.0*dot(u, v)*u + (s*s - dot(u, u))*v + 2.0*s*cross(u, v);
+	}
+	float decodeFP(uint hex, uint E, uint M){
+		uint width = M + E + 1u;
+		uint bias = 128u - (1u << (E - 1u));
+		uint exponent = (hex >> M) & ((1u << E) - 1u);
+		uint mantissa = hex & ((1u << M) - 1u);
+		uint sign = (hex >> (E + M)) << 31u;
+
+		if ((hex & ((1u << (width - 1u)) - 1u)) != 0) {
+			if (exponent == (1u << E) - 1u) exponent = 255u;
+			else exponent += bias;
+			hex = sign | (mantissa << (23u - M)) | (exponent << 23u);
+		}else hex = sign;
+        return uintBitsToFloat(hex);
+	}
+	//Implements the following algorthm: https://mathb.in/26766
 
 	void calcLighting(out vec4 primary_color, out vec4 secondary_color){
+		// Quaternions describe a transformation from surface-local space to eye space.
+		// In surface-local space, by definition (and up to permutation) the normal vector is (0,0,1),
+		// the tangent vector is (1,0,0), and the bitangent vector is (0,1,0).
+		vec4 quat = v_quaternion;
+		vec3 normal    = normalize(rotateVec3ByQuaternion(vec3(0.0,0.0,1.0), quat));
+		vec3 tangent   = normalize(rotateVec3ByQuaternion(vec3(1.0,0.0,0.0), quat));
+		vec3 bitangent = normalize(rotateVec3ByQuaternion(vec3(0.0,1.0,0.0), quat));
+		vec3 view = normalize(v_view);
+
 		uint GPUREG_LIGHTING_ENABLE  = readPicaReg(0x008F);
 		if(bitfieldExtract(GPUREG_LIGHTING_ENABLE,0,1)==0){
-			primary_color = secondary_color = vec4(0.0);
+			primary_color = secondary_color = vec4(1.0);
 			return;
 		}
 		uint GPUREG_LIGHTING_AMBIENT = readPicaReg(0x01C0);
-		vec3 ambient = vec3(
-			float(bitfieldExtract(GPUREG_LIGHTING_AMBIENT,20,8))/255.,
-			float(bitfieldExtract(GPUREG_LIGHTING_AMBIENT,10,8))/255.,
-			float(bitfieldExtract(GPUREG_LIGHTING_AMBIENT,00,8))/255.
-		);
-		primary_color = vec4(ambient,1.0);
-		secondary_color = vec4(vec3(0.5) ,1.0);
+		
+		uint GPUREG_LIGHTING_NUM_LIGHTS = (readPicaReg(0x01C2)&0x7u)+1;
+		uint GPUREG_LIGHTING_LIGHT_PERMUTATION = readPicaReg(0x01D9);
+
+		primary_color   = vec4(vec3(0.0),1.0);
+		secondary_color = vec4(vec3(0.0),1.0);
+
+		primary_color.rgb+=	regToColor(GPUREG_LIGHTING_AMBIENT);
+
+		uint GPUREG_LIGHTING_LUTINPUT_ABS = readPicaReg(0x01D0);
+		uint GPUREG_LIGHTING_LUTINPUT_SELECT = readPicaReg(0x01D1);
+		uint GPUREG_LIGHTING_CONFIG0 = readPicaReg(0x01C3);
+		uint GPUREG_LIGHTING_CONFIG1 = readPicaReg(0x01C4);
+		uint GPUREG_LIGHTING_LUTINPUT_SCALE =  readPicaReg(0x01D2);
+		float d[7];
+
+		bool error_unimpl = false;
+
+		for(uint i = 0; i<GPUREG_LIGHTING_NUM_LIGHTS;++i){
+			uint light_id = bitfieldExtract(GPUREG_LIGHTING_LIGHT_PERMUTATION,int(i*3),3);
+		
+			uint GPUREG_LIGHTi_SPECULAR0 = readPicaReg(0x0140+0x10*light_id);
+			uint GPUREG_LIGHTi_SPECULAR1 = readPicaReg(0x0141+0x10*light_id);
+			uint GPUREG_LIGHTi_DIFFUSE = readPicaReg(0x0142+0x10*light_id);
+			uint GPUREG_LIGHTi_AMBIENT = readPicaReg(0x0143+0x10*light_id);
+			uint GPUREG_LIGHTi_VECTOR_LOW = readPicaReg(0x0144+0x10*light_id);
+			uint GPUREG_LIGHTi_VECTOR_HIGH= readPicaReg(0x0145+0x10*light_id);
+			uint GPUREG_LIGHTi_CONFIG = readPicaReg(0x0149+0x10*light_id);
+
+			vec3 light_vector = vec3(
+				decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_LOW,0,16),5,10),
+				decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_LOW,16,16),5,10),
+				decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_HIGH,0,16),5,10)
+			);
+			//Positional Light
+			if(bitfieldExtract(GPUREG_LIGHTi_CONFIG,0,1)==0)error_unimpl = true;
+
+
+			vec3 half_vector = normalize(normalize(light_vector)+view);
+
+			for(int i=0;i<7u;++i){
+				if(bitfieldExtract(GPUREG_LIGHTING_CONFIG1,16+i,1)==0){
+					uint scale_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SCALE,i*4,3);
+					float scale = float(1u<<scale_id);
+					if(scale_id>=6u) scale/=256.0;
+
+					uint input_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SELECT,i*4,3);
+					if(input_id==0u)d[i] = dot(normal,half_vector);
+					else if(input_id==1u)d[i] = dot(view,half_vector);
+					else if(input_id==2u)d[i] = dot(normal,view);
+					else if(input_id==3u)d[i] = dot(light_vector,normal);
+					else if(input_id==4u){
+						uint GPUREG_LIGHTi_SPOTDIR_LOW = readPicaReg(0x0146+0x10*light_id);
+						uint GPUREG_LIGHTi_SPOTDIR_HIGH= readPicaReg(0x0147+0x10*light_id);
+						vec3 spot_light_vector = vec3(
+							decodeFP(bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW,0,16),1,11),
+							decodeFP(bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW,16,16),1,11),
+							decodeFP(bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_HIGH,0,16),1,11)
+						);
+						d[i] = dot(-light_vector,spot_light_vector);// -L . P (aka Spotlight aka SP);
+					}else if(input_id==5u){
+						d[i] = 1.0;//TODO: cos <greek symbol> (aka CP);
+						error_unimpl = true;
+					}else d[i] = 1.0;
+
+					d[i] = lutLookup(i,d[i])*scale;
+					if(bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_ABS,2*i,1)!=0u)d[i]=abs(d[i]);
+				}else d[i]=1.0;
+			}
+			uint lookup_config = bitfieldExtract(GPUREG_LIGHTi_CONFIG,4,4);
+			if(lookup_config==0){
+				d[D1_LUT] = 1.0;
+				d[FR_LUT] = 1.0;
+				d[RG_LUT]=d[RB_LUT]=d[RR_LUT];
+			}else if(lookup_config==1){
+				d[D0_LUT] = 1.0;
+				d[D1_LUT] = 1.0;
+				d[RG_LUT]=d[RB_LUT]=d[RR_LUT];
+			}else if(lookup_config==2){
+				d[FR_LUT] = 1.0;
+				d[SP_LUT] = 1.0;
+				d[RG_LUT]=d[RB_LUT]=d[RR_LUT];
+			}else if(lookup_config==3){
+				d[SP_LUT] = 1.0;
+				d[RG_LUT]=d[RB_LUT]=d[RR_LUT]=1.0;
+			}else if(lookup_config==4)d[FR_LUT] = 1.0;
+			else if(lookup_config==5)d[D1_LUT] = 1.0;
+			else if(lookup_config==6)d[RG_LUT]=d[RB_LUT]=d[RR_LUT];
+
+			float distance_factor = 1.0; //a
+			float indirect_factor = 1.0;//fi
+			float shadow_factor = 1.0;  //o
+
+			float NdotL = dot(normal,light_vector); //Li*N
+
+			//Two sided diffuse
+			if(bitfieldExtract(GPUREG_LIGHTi_CONFIG,1,1)==0)NdotL=max(0.0,NdotL);
+			else NdotL=abs(NdotL);
+
+			float light_factor =  distance_factor*d[SP_LUT]*indirect_factor*shadow_factor;
+
+			primary_color.rgb   += light_factor*(regToColor(GPUREG_LIGHTi_AMBIENT) + regToColor(GPUREG_LIGHTi_DIFFUSE)*NdotL);
+			secondary_color.rgb += light_factor*(
+									 regToColor(GPUREG_LIGHTi_SPECULAR0)*d[D0_LUT]+
+									 regToColor(GPUREG_LIGHTi_SPECULAR1)*d[D1_LUT]*vec3(d[RR_LUT],d[RG_LUT],d[RB_LUT])
+									);
+		}	
+		uint fresnel_output1 = bitfieldExtract(GPUREG_LIGHTING_CONFIG0,2,1);
+		uint fresnel_output2 = bitfieldExtract(GPUREG_LIGHTING_CONFIG0,3,1);
+
+		if(fresnel_output1==1u) primary_color.a = d[FR_LUT];
+		if(fresnel_output2==1u) secondary_color.a = d[FR_LUT];
+
+		if(error_unimpl){
+			secondary_color = primary_color = vec4(1.0,0.,1.0,1.0);
+		}
 	}
 
 	void main() {