#version 120
#define Uncube 0.2 //"Uncube" distortion level [0.0 0.2 0.4 0.6 0.8 1.0]
#define Camera 0.5 //Camera height Camera [0.0 0.5 1.0]
#define PAPER_TEXTURE_SPEED 2.0 // Paper texture update rate [0.1 0.5 1.0 2.0 5.0 10.0]
#define UNCUBE_DISTANCE_SCALING 1 // Enable distance-based uncube scaling [0 1]
#define UNCUBE_MIN_DISTANCE 5.0 // Distance where uncube starts scaling [5.0 10.0 20.0]
#define UNCUBE_MAX_DISTANCE 50.0 // Distance where uncube reaches full strength [50.0 100.0 150.0]
#define UNCUBE_MIN_STRENGTH 0.02 // Minimum uncube strength up close [0.0 0.1 0.2]
#define UNCUBE_TIME_SYNC 1	 // Enable time-synced uncube updates [0 1]


attribute vec2 mc_Entity;
uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform vec3 cameraPosition;
uniform float frameTimeCounter;
varying vec4 color;
varying vec2 lmcoord;
varying vec4 glcolor;

// Simple hash function for time-based randomization
float hash(vec2 p) {
    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}

vec3 hash3(vec3 p)
{
  return fract(cos(p*mat3(-31.14,15.92,65.35,-89.79,-32.38,46.26,43.38,32.79,-02.88))*41.97);
}
vec3 value3(vec3 p)
{
    vec3 f = floor(p);
    vec3 s = p-f; s*= s*s*(3.-s-s);
    const vec2 o = vec2(0,1);
    return mix(mix(mix(hash3(f+o.xxx),hash3(f+o.yxx),s.x),
                   mix(hash3(f+o.xyx),hash3(f+o.yyx),s.x),s.y),
               mix(mix(hash3(f+o.xxy),hash3(f+o.yxy),s.x),
                   mix(hash3(f+o.xyy),hash3(f+o.yyy),s.x),s.y),s.z);
}

//Time-synced block warping function with distance scaling
vec3 off(vec3 p, float distanceFromCamera)
{
    // Time offsets for noise sampling
    vec3 timeOffset1, timeOffset2, timeOffset3;
    
    #if UNCUBE_TIME_SYNC == 1
        // Create discrete time steps matching paper texture system
        float timeStep = floor(frameTimeCounter * PAPER_TEXTURE_SPEED);
        
        // Generate different random seeds for each time step
        float seed1 = hash(vec2(timeStep, 1.0)) * 1000.0;
        float seed2 = hash(vec2(timeStep, 2.0)) * 1000.0;
        float seed3 = hash(vec2(timeStep, 3.0)) * 1000.0;
        
        // Apply the time-offset to the noise sampling
        timeOffset1 = vec3(seed1, seed1 * 1.3, seed1 * 1.7);
        timeOffset2 = vec3(seed2, seed2 * 1.1, seed2 * 1.9);
        timeOffset3 = vec3(seed3, seed3 * 0.9, seed3 * 2.1);
    #else
        // Static uncube distortion (original behavior)
        timeOffset1 = vec3(0.0);
        timeOffset2 = vec3(0.0);
        timeOffset3 = vec3(0.0);
    #endif
    
    // Sample noise with time-based offsets (same structure as original)
    vec3 noise1 = value3(p + timeOffset1) * 0.4;
    vec3 noise2 = value3(p/2. + timeOffset2) * 0.6;
    vec3 noise3 = value3(p/8. + timeOffset3);
    
    // Calculate distance-based uncube strength
    float uncubeStrength = Uncube;
    
    #if UNCUBE_DISTANCE_SCALING == 1
        // Scale uncube strength based on distance
        float distanceFactor = smoothstep(UNCUBE_MIN_DISTANCE, UNCUBE_MAX_DISTANCE, distanceFromCamera);
        uncubeStrength = mix(UNCUBE_MIN_STRENGTH, Uncube, distanceFactor);
    #endif
    
    return (noise1 + noise2 + noise3 - 1.0) * uncubeStrength;
}

void main()
{

    vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
	// Calculate distance from camera for distance-based effects
    float distanceFromCamera = length(pos);
    pos = mat3(gbufferModelViewInverse) * pos  + gbufferModelViewInverse[3].xyz;
    pos += off(pos+cameraPosition, distanceFromCamera);
    vec3 h = pos+cameraPosition;
    pos.y -= off(cameraPosition-vec3(0,1,0), 0.0).y*Camera;
    gl_Position = gl_ProjectionMatrix * gbufferModelView * vec4(pos,1);
    gl_FogFragCoord = length(pos);
    
    color = gl_Color;
    lmcoord = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy;
    glcolor = gl_Color;
}