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

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


float getDepth( sampler2D _depthTexture, vec2 screenUV )
{
  return textureLod( _depthTexture, screenUV, 0.0 ).r;
}

vec3 getDepthViewPositiontAtScreen( sampler2D _depthTexture, vec2 screenUV, mat4 _INV_PROJECTION_MATRIX )
{
  float depthValue = getDepth( _depthTexture, screenUV );
  return screenToView( screenUV, depthValue, _INV_PROJECTION_MATRIX ).xyz;
}

float getDepthProximityAlpha( sampler2D _depthTexture, vec2 screenUV, mat4 _INV_PROJECTION_MATRIX, float vertexZ, float fadeDistance )
{
  vec3 depthViewPosition = getDepthViewPositiontAtScreen( _depthTexture, screenUV, _INV_PROJECTION_MATRIX );
  return clamp( 1.0 - smoothstep( depthViewPosition.z + fadeDistance, depthViewPosition.z, vertexZ ), 0.0, 1.0);
}

vec3 getDepthViewPositionAtViewPosition( sampler2D _depthTexture, vec3 viewPosition, mat4 _PROJECTION_MATRIX, mat4 _INV_PROJECTION_MATRIX )
{
  vec2 screenPosition = viewToScreen( viewPosition, _PROJECTION_MATRIX );
  
  vec3 depthViewPosition = getDepthViewPositiontAtScreen( _depthTexture, screenPosition, _INV_PROJECTION_MATRIX );

  return depthViewPosition;
}

bool isVisibleAt( sampler2D _depthTexture, vec3 viewPosition, mat4 _PROJECTION_MATRIX, mat4 _INV_PROJECTION_MATRIX )
{  
  vec3 depthViewPosition = getDepthViewPositionAtViewPosition( _depthTexture, viewPosition, _PROJECTION_MATRIX, _INV_PROJECTION_MATRIX );

  float visible = viewPosition.z - depthViewPosition.z;
  
	return visible > 0.0;
}


bool isInFrontOf( sampler2D _depthTexture, vec3 viewPosition, vec2 screenPosition, mat4 _INV_PROJECTION_MATRIX )
{  
  vec3 depthViewPosition = getDepthViewPositiontAtScreen( _depthTexture, screenPosition, _INV_PROJECTION_MATRIX );

  float visible = viewPosition.z - depthViewPosition.z;
  
	return visible > 0.0;
}

bool isInFrontOfDepth( sampler2D _depthTexture, float testDepth, vec2 screenPosition )
{  
  float screenDepth = getDepth( _depthTexture, screenPosition );
  float visible = testDepth - screenDepth;  
	return visible > 0.0;
}

float getQuickOcclusionAt( sampler2D _depthTexture, vec3 viewPosition, vec2 uvPixelSize, int maxSteps,  float stride, 
                           mat4 _PROJECTION_MATRIX, mat4 _INV_PROJECTION_MATRIX )
{
  vec4 clipPosition = viewToClip( viewPosition, _PROJECTION_MATRIX );
  vec2 screenPosition = clipToScreen( clipPosition );
  float depth = clipPosition.z;

  bool isOccludedAtCenter = ! isInFrontOfDepth( _depthTexture, depth, screenPosition );

  if ( isOccludedAtCenter )
  {
    return 0.0;
  }


  float offsetPerLevel = 1.0 / ( float( maxSteps ) + 1.0 );
  float h2 = pow( 2.0, 0.5 ) / 2.0; 

  vec2[] offsets = 
  {
    // vec2( -h2,   -h2  ),
    vec2(  0.0,  -1.0 ),
    // vec2(  h2,   -h2 ),

    vec2( -1.0,   0.0 ),

    vec2(  1.0,   0.0 ),

    // vec2( -h2,   h2 ),
    vec2(  0.0,   1.0 )
    // vec2(  h2,   h2 )
  };

  int angles = 4;

  for ( int i = 0; i < maxSteps; i++ )
  {
    float occluded = 0.0; 
    vec2 rayScale = ( 1.0 + float( i ) ) * uvPixelSize * stride;

    for ( int j = 0; j < angles; j++ )
    {
      vec2 rayOffset = offsets[ j ] * rayScale;
      vec2 uvTestPosition = screenPosition + rayOffset; 

      bool stepVisible = isInFrontOfDepth( _depthTexture, depth, uvTestPosition );
      occluded += stepVisible ? 0.0 : 1.0;
    }

    if ( occluded > 0.0 )
    {
      float occludedSteps = occluded / float( angles );
      float levelOffset = ( float( i ) + 2.0 ) * offsetPerLevel;

      return levelOffset - occludedSteps * offsetPerLevel;
    }
  }

  return 1.0;
}


float getOcclusionAt( sampler2D _depthTexture, vec3 viewPosition, vec2 uvPixelSize, int maxSteps,  float stride, 
                           mat4 _PROJECTION_MATRIX, mat4 _INV_PROJECTION_MATRIX )
{
  vec4 clipPosition = viewToClip( viewPosition, _PROJECTION_MATRIX );
  vec2 screenPosition = clipToScreen( clipPosition );
  float depth = clipPosition.z;


  float maxOcclusion = 2.0 + float( maxSteps ) * 8.0;

  float occlusion = 0.0;

  bool centerVisible = isInFrontOfDepth( _depthTexture, depth, screenPosition );

  if ( centerVisible )
  {
    occlusion += 2.0;
  }


  float h2 = pow( 2.0, 0.5 ) / 2.0; 

  vec2[] offsets = 
  {
    vec2( -h2,   -h2  ),
    vec2(  0.0,  -1.0 ),
    vec2(  h2,   -h2 ),

    vec2( -1.0,   0.0 ),

    vec2(  1.0,   0.0 ),

    vec2( -h2,   h2 ),
    vec2(  0.0,   1.0 ),
    vec2(  h2,   h2 )
  };



  for ( int i = 0; i < maxSteps; i++ )
  {
    vec2 rayScale = ( 1.0 + float( i ) ) * uvPixelSize * stride;

    for ( int j = 0; j < 8; j++ )
    {
      vec2 rayOffset = offsets[ j ] * rayScale;
      vec2 uvTestPosition = screenPosition + rayOffset; 

      bool stepVisible = isInFrontOfDepth( _depthTexture, depth, uvTestPosition );
      
      if ( stepVisible )
      {
        occlusion += 1.0;
      } 
    }

  }

  return occlusion / maxOcclusion;
}