// spot lights (depth-anchored) – con raycast X integrado
// ---- Created with 3Dmigoto v0.6.164 on Thu Aug  7 20:18:42 2025

#define cmp -
Texture1D<float4> IniParams      : register(t120);
Texture2D<float4> StereoParams   : register(t125);
Texture2D<float>  DepthBuffer    : register(t110);

// ===== Config =====
#define USE_RAYCAST   1        // 1 = usar adjust_from_depth_buffer (raycast X), 0 = método por depth
#define DEPTH_REVERSED 1
static const int   KERNEL_RAD  = 2;     // usado solo en el método por depth
static const float MAX_SHIFT   = 0.08;  // tope NDC por ojo
// ==================

// --- Método por depth (si USE_RAYCAST==0) ---
float depth_linear01_at_ndc(float2 ndc)
{
    uint W,H; DepthBuffer.GetDimensions(W,H);
    float sx = saturate(ndc.x * 0.5 + 0.5) * (W - 1);
    float sy = saturate(-ndc.y * 0.5 + 0.5) * (H - 1);

    float dMin = 1.0;
    [unroll]
    for (int dy = -KERNEL_RAD; dy <= KERNEL_RAD; ++dy)
    {
        [unroll]
        for (int dx = -KERNEL_RAD; dx <= KERNEL_RAD; ++dx)
        {
            float d = DepthBuffer.Load(int3(sx + dx, sy + dy, 0)).x;
            dMin = min(dMin, d);
        }
    }
#if DEPTH_REVERSED
    return 1.0 - dMin;   // 0=cerca, 1=lejos
#else
    return dMin;         // 0=cerca, 1=lejos
#endif
}

// --- Tu método: world_z + raycast en X ---
static const float near = 0.0001;
static const float far  = 1;

float world_z_from_depth_buffer(float x, float y)
{
    uint width, height;
    float z;

    DepthBuffer.GetDimensions(width, height);

    // NDC [-1,1] -> texel coords [0..w-1],[0..h-1] (Y invertido)
    x = min(max((x * 0.5 + 0.5) * width,  0), width  - 1);
    y = min(max((-y * 0.5 + 0.5) * height, 0), height - 1);

    z = DepthBuffer.Load(int3(x, y, 0)).x;
    if (z == 1) return 0;

    // Aproximación linealizada para reversed-Z
    // return (far*near/((z)*near) + (far*z));
    return (far*near/((1 - z) * near) + (far * z));
}

float adjust_from_depth_buffer(float x, float y)
{
    float4 stereo = StereoParams.Load(0);
    float4 testeo = IniParams.Load(int2(90,0)); // opcional/depuración
    if (stereo.x == 0) return 0;

    float separation  = stereo.x;
    float convergence = stereo.y;
    float old_offset, offset, w, sampled_w, distance;
    uint  i;

    offset   = (near - convergence) * separation; // Z = X offset from center
    distance = separation - offset;               // recorrido total

    old_offset = offset;
    [loop]
    for (i = 0; i < 200; i++)
    {
        offset += distance / 200.0;

        // Profundidad esperada en ese punto de la línea:
        w = (separation * convergence * 10) / (separation - offset);

        // Profundidad real de escena en pantalla:
        sampled_w = world_z_from_depth_buffer(x + offset, y);
        if (sampled_w == 0) return separation;

        // Si la escena es más cercana que lo esperado, paramos
        if (w > sampled_w) break;

        old_offset = offset;
    }

    return old_offset;
}

void main(
  float3 v0 : POSITION0,
  float4 v1 : TEXCOORD0,
  float4 v2 : TEXCOORD1,
  float3 v3 : TEXCOORD2,
  out float4 o0 : SV_Position0,
  out float2 o1 : TEXCOORD0,
  out float2 p1 : TEXCOORD2,
  out float3 o2 : TEXCOORD1)
{
  // Posición en pantalla (como tu VS original)
  o0.xy = v0.xy;
  o0.zw = float2(1,1);

#if USE_RAYCAST
  // --- Método raycast (X) ---
  float off = adjust_from_depth_buffer(o0.x, o0.y);   // offset ya en NDC
  off = clamp(off, -MAX_SHIFT, MAX_SHIFT);            // cinturón de seguridad
  o0.x += off;
#else
  // --- Método por depth normalizado ---
  float d = depth_linear01_at_ndc(o0.xy);             // 0=cerca, 1=lejos
  if (d < 0.999)
  {
      float shift = (1.0 - d);                        // parallax base (magnitud)
      shift = clamp(shift, -MAX_SHIFT, MAX_SHIFT);
      float4 s = StereoParams.Load(0);                // s.x = signo del ojo
      o0.x += s.x * shift ;                     // tu escala “lejos ok”
  }
#endif

  // Passthrough a la PS
  o1.xy = v1.xy;
  p1.xy = v2.xy;
  o2.xyz = v3.xyz;
}
