
float PackUnorm2x8(vec2 xy)
{
    return dot(floor(255.0 * xy + 0.5), vec2(1.0 / 65535.0, 256.0 / 65535.0));
}
vec2 UnpackUnorm2x8(float pack)
{
    vec2 xy;
    xy.x = modf(pack * 255.996094, xy.y);

    return xy * vec2(1.003922, 0.00392156862);
}
float UnpackUnorm2x8X(float pack)
{
    return fract(pack * 65535.0 * 0.00390625) * 256.0 * recip;
}
float UnpackUnorm2x8Y(float pack)
{
    return floor(pack * 65535.0 * 0.00390625) * recip;
}

uint packSnorm4x8(vec4 x)
{
    x = clamp(x, -1.0, 1.0) * 127.0;
    uvec4 sig = uvec4(mix(vec4(0), vec4(1), greaterThanEqual(sign(x), vec4(0))));
    uvec4 mag = uvec4(abs(x));
    uvec4 r = sig << 7 | mag;
    return r.x << 24 | r.y << 16 | r.z << 8 | r.w;
}

vec4 unpackSnorm4x8(uint x)
{
    uvec4 r = (uvec4(x) >> uvec4(24, 16, 8, 0)) & uvec4(0xFF);
    uvec4 sig = r >> 7;
    uvec4 mag = r & uvec4(0x7F);
    vec4 fsig = mix(vec4(-1), vec4(1), greaterThanEqual(sig, uvec4(1)));
    vec4 fmag = vec4(mag) / 127.0;
    return fsig * fmag;
}
uint packSnorm3x10(vec3 x)
{
    x = clamp(x, -1., 1.) * 511.;
    uvec3 sig = uvec3(mix(vec3(0), vec3(1), greaterThanEqual(sign(x), vec3(0))));
    uvec3 mag = uvec3(abs(x));
    uvec3 r = sig.xyz << 9 | mag.xyz;
    return r.x << 22 | r.y << 12 | r.z << 2;
}

vec3 unpackSnorm3x10(uint x)
{
    uvec3 r = (uvec3(x) >> uvec3(22, 12, 2)) & uvec3(0x3FF);
    uvec3 sig = r >> 9;
    uvec3 mag = r & uvec3(0x1FF);
    vec3 fsig = mix(vec3(-1), vec3(1), greaterThanEqual(sig, uvec3(1)));
    vec3 fmag = vec3(mag) / 511.;
    return fsig * fmag;
}

#define unpack(x) unpackSnorm4x8(floatBitsToUint(x))
#define pack(x) uintBitsToFloat(packSnorm4x8(x))
#define pack2(x) uintBitsToFloat(packSnorm3x10(x))
#define unpack2(x) unpackSnorm3x10(floatBitsToUint(x))

//////////
/*
    Normals encoding and decoding based on Spectrum by Zombye
*/
vec2 encodeNormal(in vec3 normal)
{
    normal.xy /= abs(normal.x) + abs(normal.y) + abs(normal.z);
    return (normal.z <= 0.0 ? (1.0 - abs(normal.yx)) * vec2(normal.x >= 0.0 ? 1.0 : -1.0, normal.y >= 0.0 ? 1.0 : -1.0)
                            : normal.xy) *
               0.5 +
           0.5;
}
vec3 decodeNormal(in vec2 encodedNormal)
{
    encodedNormal = encodedNormal * 2.0 - 1.0;
    vec3 normal = vec3(encodedNormal, 1.0 - abs(encodedNormal.x) - abs(encodedNormal.y));
    float t = max(-normal.z, 0.0);
    normal.xy += vec2(normal.x >= 0.0 ? -t : t, normal.y >= 0.0 ? -t : t);
    return normalize(normal);
}
#ifdef PI_SUPPORT

float encodeVec2(vec2 a)
{
    // Ensure 'a' is within the range [0.0, 1.0]
    vec2 clamped = clamp(a, 0.0, 1.0);

    // Scale both components to an 8-bit range
    float high = ceil(clamped.x * 64); // Avoid rounding errors at the upper limit
    float low = ceil(clamped.y * 256); // Ensure full use of 8-bit range

    // Combine the two 8-bit values into a single float
    return (high * 256.0 + low) / 65536.0; // Normalize to full 16-bit range
}

vec2 decodeVec2_a(float a)
{
    // Unpack the encoded value back to two components
    float unpacked = a * 65536.0;

    // Extract the high and low bytes
    float high = floor(unpacked / 256) * 4.0; // High byte
    float low = unpacked - high * 64;         // Low byte

    // Convert back to the original range [0.0, 1.0]
    return vec2(high, low) / 255.0;
}
float encodeVec2_a(vec2 a)
{
    // Ensure 'a' is within the range [0.0, 1.0]
    vec2 clamped = clamp(a, 0.0, 1.0);

    // Scale both components to an 8-bit range
    float high = ceil(clamped.x * 64); // Avoid rounding errors at the upper limit
    float low = ceil(clamped.y * 256); // Ensure full use of 8-bit range

    // Combine the two 8-bit values into a single float
    return (high * 256.0 + low) / 65536.0; // Normalize to full 16-bit range
}

vec2 decodeVec2(float a)
{
    // Unpack the encoded value back to two components
    float unpacked = a * 65536.0;

    // Extract the high and low bytes
    float high = floor(unpacked / 256) * 4.0; // High byte
    float low = unpacked - high * 64;         // Low byte

    // Convert back to the original range [0.0, 1.0]
    return vec2(high, low) / 255.0;
}

#else
// Encode a vec2 in [0,1] into a single float.
float encodeVec2(vec2 a) {
    vec2 t = floor(a * 255.0);
    return (t.x + t.y * 256.0) / 65535.0;
}

float encodeVec2n(vec2 a) {
    // Convert X to an integer [0,255] – full 8‑bit precision.
    float x = floor(a.x * 255.0 + 0.5);
    // Quantize Y to 4 bits: integer in [0,15].
    float y = floor(a.y * 15.0 + 0.5);
    // Pack into a 12‑bit integer: X in the high 8 bits, Y in the low 4 bits.
    float combined = x * 16.0 + y;  // Range: 0 to 4095.
    // Map [0,4095] linearly to [-1,1].
    return (combined / 4095.0) * 2.0 - 1.0;
}

vec2 decodeVec2n(float f) {

    // Recover the 12‑bit integer from [-1,1].
    float combined = (f + 1.0) * 4095.0 / 2.0;
    // Extract X from the high 8 bits and Y from the low 4 bits.
    float x = floor(combined / 16.0);
    float y = mod(combined, 16.0);
    // Normalize back to [0,1]: X with 8‑bit precision, Y with 4‑bit.
    return vec2(x / 255.0, y / 15.0);

}


// Decode the float back into a vec2 in [0,1].
vec2 decodeVec2(float a) {
    float n = a * 65535.0;
    return vec2(mod(n, 256.0), floor(n / 256.0)) / 255.0;
}

const float remapval = 20.0;

// Encode a vec2 in [0,remapval] by first remapping it into [0,1].
float encodeVec2_a(vec2 a) {
    return encodeVec2(clamp(a / remapval, 0.0, 1.0));
}

// Decode the float and then scale back to [0,remapval].
vec2 decodeVec2_a(float a) {
    return decodeVec2(a) * remapval;
}


#endif

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

vec3 encodeVec3(vec3 x, vec3 y)
{
    float r = encodeVec2_a(vec2(x.r, y.r));
    float g = encodeVec2_a(vec2(x.g, y.g));
    float b = encodeVec2_a(vec2(x.b, y.b));
    return vec3(r, g, b);
}
vec3 decodeVec3_x(vec3 x)
{
    float r = decodeVec2_a(x.r).r;
    float g = decodeVec2_a(x.g).r;
    float b = decodeVec2_a(x.b).r;
    return vec3(r, g, b);
}
vec3 decodeVec3_y(vec3 x)
{
    float r = decodeVec2_a(x.r).g;
    float g = decodeVec2_a(x.g).g;
    float b = decodeVec2_a(x.b).g;
    return vec3(r, g, b);
}

vec4 encodeTwoVec3(vec3 vecA, vec3 vecB)
{
    // Clamp inputs to their respective ranges
    vecA = clamp(vecA, 0.0, 256.0);
    vecB = clamp(vecB, 0.0, 1.0);

    // Quantize vecA components to 16 bits (0 to 65535)
    vec3 vecA_q = floor(vecA * (65535.0 / 256.0) + 0.5);

    // Quantize vecB components to 5 bits (0 to 31)
    vec3 vecB_q = floor(vecB * 31.0 + 0.5);

    // Pack vecB_q components into the Alpha component
    // A = vecB_q.x * 1024 + vecB_q.y * 32 + vecB_q.z
    float A_value = (vecB_q.x * 1024.0) + (vecB_q.y * 32.0) + vecB_q.z;

    // Normalize A_value to [0, 1] for storage
    float A_encoded = A_value / 32767.0; // Since 2^15 - 1 = 32767

    // Normalize vecA_q to [0, 1] for storage
    vec3 encodedRGB = vecA_q / 65535.0;

    // Return the encoded vec4
    return vec4(encodedRGB, A_encoded);
}

void decodeTwoVec3(vec4 encodedColor, out vec3 vecA, out vec3 vecB)
{
    // Retrieve quantized vecA components
    vec3 vecA_q = floor(encodedColor.rgb * 65535.0 + 0.5);

    // Recover original vecA values
    vecA = vecA_q * (256.0 / 65535.0);

    // Retrieve the packed A_value
    float A_value = floor(encodedColor.a * 32767.0 + 0.5);

    // Extract vecB_q components from A_value
    float vecB_q_x = floor(A_value / 1024.0);
    A_value -= vecB_q_x * 1024.0;

    float vecB_q_y = floor(A_value / 32.0);
    A_value -= vecB_q_y * 32.0;

    float vecB_q_z = A_value;

    // Recover original vecB values
    vecB.x = vecB_q_x / 31.0;
    vecB.y = vecB_q_y / 31.0;
    vecB.z = vecB_q_z / 31.0;
}



vec2 packDepthRG(float depth) {
    depth = clamp(depth, 0.0, 1.0);
    float hi = floor(depth * 65536.0) / 65536.0;
    float lo = depth - hi;
    return vec2(hi, lo);
}
float unpackDepthRG(vec2 rg) {
    return rg.x + rg.y;
}

#ifdef DISTANT_HORIZONS

// Favor near range (closer to 0 = more precision)
float encodeDepthBias(float d) {
    return sqrt(d); // Or d^0.7, tweakable
}

float decodeDepthBias(float e) {
    return e * e;
}

#endif