// #include "res://addons/rokojori_action_library/Runtime/Shading/Library/Qauternion.gdshaderinc"

vec4 quaternionFromMatrix( mat3 m )
{
  vec4 qa = vec4( 0.0, 0.0, 0.0, 1.0 );

  float trace = m[ 0 ][ 0 ] + m[ 1 ][ 1 ] + m[ 2 ][ 2 ];

  if ( trace > 0.0 ) 
  {

    float s = 0.5 / sqrt( trace + 1.0 );

    qa.w = 0.25 / s;
    qa.x = ( m[ 1 ][ 2 ] - m[ 2 ][ 1 ] ) * s;
    qa.y = ( m[ 2 ][ 0 ] - m[ 0 ][ 2 ] ) * s;
    qa.z = ( m[ 0 ][ 1 ] - m[ 1 ][ 0 ] ) * s;

  } 
  else if ( m[ 0 ][ 0 ] > m[ 1 ][ 1 ] && m[ 0 ][ 0 ] > m[ 2 ][ 2 ] ) 
  {

    float s = 2.0 * sqrt( 1.0 + m[ 0 ][ 0 ] - m[ 1 ][ 1 ] - m[ 2 ][ 2 ] );

    qa.w = ( m[ 1 ][ 2 ] - m[ 2 ][ 1 ] ) / s;
    qa.x = 0.25 * s;
    qa.y = ( m[ 1 ][ 0 ] + m[ 0 ][ 1 ] ) / s;
    qa.z = ( m[ 2 ][ 0 ] + m[ 0 ][ 2 ] ) / s;
  } 
  else if ( m[ 1 ][ 1 ] > m[ 2 ][ 2 ] ) 
  {

    float s = 2.0 * sqrt( 1.0 + m[ 1 ][ 1 ] - m[ 0 ][ 0 ] - m[ 2 ][ 2 ] );

    qa.w = ( m[ 2 ][ 0 ] - m[ 0 ][ 2 ] ) / s;
    qa.x = ( m[ 1 ][ 0 ] + m[ 0 ][ 1 ] ) / s;
    qa.y = 0.25 * s;
    qa.z = ( m[ 2 ][ 1 ] + m[ 1 ][ 2 ] ) / s;

  } 
  else 
  {

    float s = 2.0 * sqrt( 1.0 + m[ 2 ][ 2 ] - m[ 0 ][ 0 ] - m[ 1 ][ 1 ] );

    qa.w = ( m[ 0 ][ 1 ] - m[ 1 ][ 0 ] ) / s;
    qa.x = ( m[ 2 ][ 0 ] + m[ 0 ][ 2 ] ) / s;
    qa.y = ( m[ 2 ][ 1 ] + m[ 1 ][ 2 ] ) / s;
    qa.z = 0.25 * s;
  }

  return qa;
}

vec4 quaternionMultiply( vec4 a, vec4 b )
{

  // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm

  float qax = a.x;
  float qay = a.y;
  float qaz = a.z;
  float qaw = a.w;

  float qbx = b.x;
  float qby = b.y;
  float qbz = b.z;
  float qbw = b.w;

  vec4 q = vec4( 0.0, 0.0, 0.0, 0.0 );

  q.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
  q.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
  q.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
  q.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;

  return q;
}

vec4 quaternionSlerp( vec4 qa, vec4 qb, float t )
{
  if ( t == 0.0 ) 
  {
    return qa;
  }

  if ( t == 1.0 ) 
  { 
    return qb;
  }

  float x = qa.x; 
  float y = qa.y; 
  float z = qa.z; 
  float w = qa.w;


  float cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;

  if ( cosHalfTheta < 0.0 ) 
  {
    qa.w = - qb.w;
    qa.x = - qb.x;
    qa.y = - qb.y;
    qa.z = - qb.z;

    cosHalfTheta = - cosHalfTheta;
  } 
  else 
  {

    qa = qb;
  }

  if ( cosHalfTheta >= 1.0 ) 
  {

    qa.w = w;
    qa.x = x;
    qa.y = y;
    qa.z = z;

    return qa;
  }

  float sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;

  if ( sqrSinHalfTheta <= 0.000001 ) 
  {

    float s = 1.0 - t;

    qa.w = s * w + t * qa.w;
    qa.x = s * x + t * qa.x;
    qa.y = s * y + t * qa.y;
    qa.z = s * z + t * qa.z;

    qa = normalize( qa );

    return qa;

  }

  float sinHalfTheta = sqrt( sqrSinHalfTheta );
  float halfTheta = atan( sinHalfTheta, cosHalfTheta );
  float ratioA = sin( ( 1.0 - t ) * halfTheta ) / sinHalfTheta;
  float ratioB = sin( t * halfTheta ) / sinHalfTheta;

  qa.w = ( w * ratioA + qa.w * ratioB );
  qa.x = ( x * ratioA + qa.x * ratioB );
  qa.y = ( y * ratioA + qa.y * ratioB );
  qa.z = ( z * ratioA + qa.z * ratioB );

  return qa;
}