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

#if !defined _UTILITIES_
#define _UTILITIES_

// This file will contain general utilities and helper funtions used by various projects.

#define TIME_SPEED 1.0 // [0.0 0.025 0.05 0.075 0.1 0.125 0.15 0.175 0.2 0.225 0.25 0.275 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4.0 5.0 6.0 7.0 8.0 9.0 10.0 12.0 14.0 16.0 18.0 20.0 24.0 28.0 32.0 36.0 40.0]
#define TIME ( frameTimeCounter * TIME_SPEED )

#define diagonal2(m) vec2((m)[0].x, (m)[1].y)
#define diagonal3(m) vec3(diagonal2(m), m[2].z)
#define diagonal4(m) vec4(diagonal3(m), m[2].w)

#define transMAD(mat, v) (     mat3(mat) * (v) + (mat)[3].xyz)
#define  projMAD(mat, v) (diagonal3(mat) * (v) + (mat)[3].xyz)

#define sum2(v) (v.x + v.y)
#define sum3(v) ((v.x + v.y) + v.z)
#define sum4(v) (((v).x + (v).y) + ((v).z + (v).w))

#define fstep(a, b) clamp(((b) - (a)) * 1e35, 0.0, 1.0)
#define fsign(a) (clamp((a) * 1e35, 0.0, 1.0) * 2.0 - 1.0)

#define textureRaw(samplr, coord) texelFetch(samplr, ivec2((coord) * viewDimensions), 0)
#define ScreenTex(samplr) texelFetch(samplr, ivec2(gl_FragCoord.st), 0)

#define up gbufferModelView[1].xyz


#include "../ContinuumLib/Utilities/Clamping.glsl"
#include "../ContinuumLib/Utilities/Pow.glsl"
#include "../ContinuumLib/Utilities/ColorConversions.glsl"
#include "../ContinuumLib/Utilities/Encoding.glsl"
#include "../ContinuumLib/Utilities/Noise.glsl"

mat3 RotateMatrix(vec3 x, vec3 y) {
  float d = dot(x, y);
  float id = 1.0 - d;

  vec3 cr = cross(y, x);
  float s = length(cr);

  vec3 m = cr / s;
  vec3 m2 = m * m * id + d;

  vec3 sm = s * m;
  vec3 w = (m.xy * id).xxy * m.yzz;

  return mat3(
     m2.x      , w.x - sm.z, w.y + sm.y,
     w.x + sm.z, m2.y      , w.z - sm.x,
     w.y - sm.y, w.z + sm.x, m2.z
  );
}

float facos(const float sx) {
  float x = clamp01(abs(sx));
  float a = sqrt(1.0 - x) * (-0.16882 * x + 1.56734);

  return sx > 0.0 ? a : PI - a;

  //float c = clamp(-sx * 1e35, 0., 1.);
  //return c * pi + a * -(c * 2. - 1.); //no conditional version
}

vec2 facos(const vec2 sx) {
  vec2 x = clamp01(abs(sx));
  vec2 a = sqrt(1.0 - x) * (-0.16882 * x + 1.56734);

  a.x = sx.x > 0.0 ? a.x : PI - a.x;
  a.y = sx.y > 0.0 ? a.y : PI - a.y;

  return a;

  //float c = clamp(-sx * 1e35, 0., 1.);
  //return c * pi + a * -(c * 2. - 1.); //no conditional version
}

vec3 blackbody(const float t) {
    // http://en.wikipedia.org/wiki/Planckian_locus

    const vec4 vx = vec4( -0.2661239e9, -0.2343580e6, 0.8776956e3, 0.179910   );
    const vec4 vy = vec4( -1.1063814,   -1.34811020,  2.18555832, -0.20219683 );
    //vec4 vy = vec4(-0.9549476,-1.37418593,2.09137015,-0.16748867); //>2222K
    float it = 1. / t;
    float it2= it * it;
    float x = dot( vx, vec4( it*it2, it2, it, 1. ) );
    float x2 = x * x;
    float y = dot( vy, vec4( x*x2, x2, x, 1. ) );

    // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
    const mat3 xyzToSrgb = mat3(
         3.2404542,-1.5371385,-0.4985314,
        -0.9692660, 1.8760108, 0.0415560,
         0.0556434,-0.2040259, 1.0572252
    );

    vec3 srgb = vec3( x/y, 1., (1.-x-y)/y ) * xyzToSrgb;

    return max( srgb, 0. );
}

// No intersection if returned y component is < 0.0
vec2 rsi(vec3 position, vec3 direction, float radius) {
    float PoD = dot(position, direction);
    float radiusSquared = radius * radius;

    float delta = PoD * PoD + radiusSquared - dot(position, position);
    if (delta < 0.0) return vec2(-1.0);
        delta = sqrt(delta);

    return -PoD + vec2(-delta, delta);
}

float Remap(float value, const float originalMin, const float originalMax, const float newMin, const float newMax) {
    return clamp01((((value - originalMin) / (originalMax - originalMin)) * (newMax - newMin)) + newMin);
}

vec3 cubeSmooth(vec3 x){
    return x * x * (3.0 - 2.0 * x);
}

mat3 GenerateTBN(vec3 plane) {
	mat3 tbn;
	
	vec3 plane3 = abs(plane);
	
	tbn[0].z = -plane.x;
	tbn[0].y = 0.0;
	tbn[0].x = plane3.y + plane.z;
	
	tbn[1].x = 0.0;
	tbn[1].y = -plane3.x - plane3.z;
	tbn[1].z = plane3.y;
	
	tbn[2] = plane;
	
	return tbn;
}

#define log10(x) (log(x) * rLOG10)

#endif
