/********************************************************
    © 2020 Continuum Graphics LLC. All Rights Reserved
 ********************************************************/

#if !defined _ENCODING_
	#define _ENCODING_

	float Encode2x8(vec2 a) {
		const vec2 constant1 = vec2(1.0, 256.0) / 65535.0; //2^16-1

		return dot(floor(a * 255.0), constant1);
	}

	float Encode2x8(float x, float y) {
		return Encode2x8(vec2(x, y));
	}

	vec2 Decode2x8(float a) {
		const vec2 constant1 = 65535.0 / vec2(256.0, 65536.0);
		const float constant2 = 256.0 / 255.0;
		return fract(a * constant1) * constant2;
	}

	float Encode4x8F(in vec4 a) {
		uvec4 v = uvec4(round(clamp01(a) * 255.0)) << uvec4(0, 8, 16, 24);
		
		return uintBitsToFloat(sum4(v));
	}

	vec4 Decode4x8F(in float encodedbuffer) {
		uvec4 decode     = uvec4(floatBitsToUint(encodedbuffer));
			decode.yzw = decode.yzw >> uvec3(8, 16, 24);
			decode.xyz = decode.xyz & 255;
		
		return vec4(decode) / 255.0;
	}

	float Encode2x8F(in vec2 a) {
		uvec2 v = uvec2(round(clamp01(a) * 255.0)) << uvec2(0, 16);
	
		return uintBitsToFloat(sum2(v));
	}

	float Encode2x8F(float x, float y) {
		return Encode2x8F(vec2(x, y));
	}

	vec2 Decode2x8F(in float encodedbuffer) {
		uvec2 decode = uvec2(floatBitsToUint(encodedbuffer));
			decode.y = decode.y >> 16u;
			decode.x = decode.x & 255;
		
		return vec2(decode) / 255.0;
	}

#if 0
	#define INVROOT2 .70710678

	void ellipseToDisk(inout vec2 uv) {
		float x = uv.x * sqrt(1.0 - pow2(uv.y) * 0.5);
		float y = uv.y * sqrt(1.0 - pow2(uv.x) * 0.5);
		uv = vec2(x, y);
	}

	void ellipseFromDisk(inout vec2 uv) {
		/**Convert from the disk to the pow2*/
		float uSqMinusVSq = pow2(uv.x) - pow2(uv.y);
		// making this constant 2.0 causes a small set of vectors to be mapped off by 90 degrees 
		// because of floating point rounding errors. When set to 1.9999995f this error is no longer
		// an issue
		float t1 = 1.9999995 + uSqMinusVSq;
		//float t1 = 2.0 + uSqMinusVSq;
		float t2 = sqrt(-8.0 * pow2(uv.x) + t1 * t1);

		float newU = sqrt(2.0 + uSqMinusVSq - t2) * INVROOT2 * sign(uv.x);
		float newV = 2.0 * uv.y * inversesqrt(2.0 - uSqMinusVSq + t2);

		uv = vec2(newU, newV);
	}

	float squaredMagnitude(vec2 uv) {
		return dot(uv, uv);
	}

	vec3 DecodeNormal(float pack) {
		vec2 a = Decode2x8(pack) * 2.0 - 1.0;
		float zsign   = sign(a.x);
		float u       = (abs(a.x) - 0.5) * 2.0;
		vec2 uv = vec2(u, a.y);

		/**map back from the pow2 to the disk*/
		ellipseToDisk(uv);
		
		/**return the vector using the map*/
		float r2 = squaredMagnitude(uv);
		float temp = sqrt((1.0 - pow2(1.0 - r2)) / r2);
		return normalize(vec3(uv.x * temp, uv.y * temp, zsign * (1.0 - r2)));
	}

	float EncodeNormal(vec3 vec) {
		float denominator = inversesqrt(abs(vec.z) + 1.0);
		vec2 uv = vec.xy * denominator;

		ellipseFromDisk(uv);

		/**Pack sign of z into sign of u*/
		float zsign = fsign(vec.z);
		uv.x = (uv.x * 0.5 + 0.5) * zsign;

		return Encode2x8(uv * 0.5 + 0.5);
	}

	vec3 DecodeNormal(float encoded, mat4 gbufferModelView) {
		return mat3(gbufferModelView) * DecodeNormal(encoded);
	}
#else
	float EncodeNormal(vec3 a) {
		vec3 b = abs(a);
		vec2 p = a.xy / (b.x + b.y + b.z);

		vec2 encoded = a.z <= 0.0 ? (1.0 - abs(p.yx)) * fsign(p) : p;
			encoded = encoded * 0.5 + 0.5;

		return Encode2x8(encoded);
	}

	vec3 DecodeNormal(float encoded) {
		vec2 a = Decode2x8(encoded);
			 a = a * 2.0 - 1.0;

		vec2 b = abs(a);
		float z = 1.0 - b.x - b.y;

		return normalize(vec3(z < 0.0 ? (1.0 - b.yx) * fsign(a) : a, z));
	}

	vec3 DecodeNormal(float encoded, mat4 gbufferModelView) {
		return mat3(gbufferModelView) * DecodeNormal(encoded);
	}
#endif

/*
Look into these
vec2 sphericalEncode(in vec3 v) {
    // Assuming rho is always 1 (normalized v)
    float thetaNormalized = acos(v.y) / PI;
    float phiNormalized = (atan(v.x, v.z) / PI) * 0.5 + 0.5;
    return vec2(phiNormalized, thetaNormalized);
}

vec3 sphericalDecode(in vec2 p) {
    float theta = p.y * PI;
    float phi   = (p.x * (2.0 * PI) - PI);

    float sintheta = sin(theta);
    return vec3(sintheta * sin(phi), cos(theta), sintheta * cos(phi));
}

*/

	vec2 EncodeME8(float f) {
		const float Reverse8bit = 1.0 / 255.0;
		float exponentPart = floor(log2(f));
		float mantissaPart = clamp01((128.0 * Reverse8bit) * f / exp2(exponentPart));
			exponentPart = clamp01((exponentPart + 127.0) * Reverse8bit);

		return vec2(mantissaPart, exponentPart);
	}

	float DecodeME8(vec2 fe) {
		float exponentPart = exp2(fe.y * 255.0 - 127.0);
		float mantissaPart = (510.0 / 256.0) * fe.x;

		return exponentPart * mantissaPart;
	}
	
	float DecodeME8(float mantissa, float exponent) {
		float exponentPart = exp2(exponent * 255.0 - 127.0);
		float mantissaPart = (510.0 / 256.0) * mantissa;

		return exponentPart * mantissaPart;
	}

	vec3 EncodeColor(vec3 rgb){
		return rgb * 0.1;
	}

	vec3 DecodeColor(vec3 rgb){
		return rgb * 10.0;
	}

	vec4 EncodeRGBE8(vec3 rgb) {
		const float Reverse8bit = 1.0 / 255.0;
		float exponentPart = floor(log2(max(max(rgb.r, rgb.g), rgb.b)));
		vec3  mantissaPart = clamp01((128.0 * Reverse8bit) * rgb / exp2(exponentPart));
			  exponentPart = clamp01((exponentPart + 127.0) * Reverse8bit);

		return vec4(mantissaPart, exponentPart);
	}

	vec3 DecodeRGBE8(vec4 rgbe) {
		float exponentPart = exp2(rgbe.a * 255.0 - 127.0);
		vec3  mantissaPart = (510.0 / 256.0) * rgbe.rgb;

		return exponentPart * mantissaPart;
	}

#endif
