shader_type particles;
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc"
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc"
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Textures.gdshaderinc"

uniform vec3 cameraPosition;
uniform float yaw;
uniform float cellSize = 1.0;
uniform int width = 10;
uniform int height = 10;

uniform sampler2D positionVariance; 
uniform vec3 maxPositionOffset = vec3(0,0,0);
uniform vec3 positionOffset = vec3(0,0,0);
uniform vec2 positionUVScale = vec2( 1.0, 1.0 );
uniform vec2 positionUVOffset = vec2( 1.0, 1.0 );
uniform float heightOffset = 0;
uniform sampler2D heightMap;
uniform float minHeight = 0;
uniform float maxHeight = 100;


uniform sampler2D rotationVariance; 
uniform vec3 minRotation = vec3( 0, 0, 0 );
uniform vec3 maxRotation = vec3( 0, 1, 0 );
uniform vec2 rotationUVScale = vec2( 1.0, 1.0 );
uniform vec2 rotationUVOffset = vec2( 1.0, 1.0 );

uniform sampler2D scaleVariance; 
uniform float scaleVarianceContrastScale = 1.0;
uniform float scaleVarianceContrastCenter = 0.5;

uniform vec3 minScale = vec3( 1, 1, 1 );
uniform vec3 maxScale = vec3( 1, 1, 1 );
uniform float uniScaleMin = 1.0;
uniform float uniScaleMax = 1.0;
uniform vec2 uniScaleUVOffset = vec2( 0.5, 0.5 );

uniform vec2 scaleUVScale = vec2( 1.0, 1.0 );
uniform vec2 scaleUVOffset = vec2( 1.0, 1.0 );

uniform float occupancyAmount = 0;
uniform float occupancyPower = 0;
uniform float occupancyTreshold = 0.5;
uniform float occupancyHideOffset = -3;
uniform float occupancyHideScale = 0.1;

uniform sampler2D occupancyVariance; 
uniform vec2 occupancyUVScale = vec2( 1.0, 1.0 );
uniform vec2 occupancyUVOffset = vec2( 1.0, 1.0 );


uniform float hideStart = 40;
uniform float hideMax = 60;
uniform float hideOffset = -0.6;

uniform float discardTreshold =  0.01;
uniform float discardOffset = 1000000;

uniform sampler2D coverageMap;
uniform sampler2D colorMap;
uniform vec2 mapSize = vec2(1024,1024);
uniform vec2 mapCenter = vec2(0,0);

vec2 indexToPosition( int index )
{
  int x = index % width;
  int y = index / height;
  
  return vec2( float(x), float(y) );  
}

void process() 
{
  vec2 position = indexToPosition( int(INDEX) );
  float rotation = round( 4.0 * yaw / 360.0 ) * PI / 2.0;
  
  vec3 snappedCameraPosition = vec3( 
    floor( cameraPosition.x / cellSize ) * cellSize, 
    0,
    floor( cameraPosition.z / cellSize ) * cellSize
  );
  
  vec2 origin = vec2( float(width)/2.0, float(height)/2.0 );
  position -= origin;
  
  position = rotate_v2( position, rotation ) * cellSize;
 
  
  vec3 position3D = vec3( position.x, heightOffset, position.y ) + snappedCameraPosition;
  
  float d = length( position3D - cameraPosition );
  float yOffset = mapClamped( d, hideStart, hideMax, 0, hideOffset );
  position3D.y += yOffset;
  
  
  vec2 mapOffset = mapCenter - mapSize/2.0;
  vec2 uv = ( vec2( position3D.x, position3D.z ) - mapOffset ) / mapSize;

  

  vec3 offset = (textureLod( positionVariance, tilingOffset( uv, positionUVScale, positionUVOffset ), 0 ).rgb - vec3(0.5,0.5,0.5) )  * maxPositionOffset;
  position3D += offset + positionOffset;
  vec3 rotSampled = textureLod( rotationVariance, tilingOffset( uv, rotationUVScale, rotationUVOffset ), 0 ).rgb;
  vec3 rot = ( rotSampled * ( maxRotation - minRotation ) + minRotation ) * PI * 2.0;
  // rot.y = round( 4.0 * rot.y / PI * 2.0 ) / 4.0 * PI * 2.0;
  vec3 sca = textureLod( scaleVariance, tilingOffset( uv, scaleUVScale, scaleUVOffset ), 0 ).rgb;
  sca = ( sca - scaleVarianceContrastCenter ) * scaleVarianceContrastScale + scaleVarianceContrastCenter;
  sca = clamp( sca, vec3( 0.0 ), vec3( 1.0 ) );
  sca = sca * ( maxScale - minScale ) + minScale;

  float uniScale = textureLod( scaleVariance, tilingOffset( uv + uniScaleUVOffset, scaleUVScale, scaleUVOffset ), 0 ).r;
  uniScale = ( uniScale - scaleVarianceContrastCenter ) * scaleVarianceContrastScale + scaleVarianceContrastCenter;
  uniScale = clamp( uniScale, 0.0, 1.0 );
  uniScale = uniScale * ( uniScaleMax - uniScaleMin ) + uniScaleMin;

  sca *= uniScale;
  vec2 uv2 = ( vec2( position3D.x, position3D.z ) - mapOffset ) / mapSize;
  float sampledHeight = textureLod( heightMap, uv2, 0 ).r;
  float occ = textureLod( occupancyVariance, tilingOffset( uv2, occupancyUVScale, occupancyUVOffset ), 0 ).r;
  float sampledCoverage = textureLod( coverageMap, uv2, 0 ).r;

  occ = clamp( ( occ - occupancyTreshold ) * occupancyPower + occupancyTreshold, 0.0, 1.0 );
  occ = 1.0 - occ;
  
  occ = min( occ, 1.0 - sampledCoverage );

  sca *= mix( 1.0, occupancyHideScale , occ * occupancyAmount );  
  
  float combinedOcc = occ * occupancyAmount; ;
  position3D.y += combinedOcc * occupancyHideOffset;
  position3D.y += mix( minHeight, maxHeight, sampledHeight );
  
  if ( combinedOcc >= ( 1.0 - clamp01( discardTreshold ) ) )
  {
    position3D.y += discardOffset;
  }
  
  TRANSFORM = TRS( position3D, rot, vec3( sca.x , sca.y, sca.z ) );
}
