import { GLSL3, NoBlending, RawShaderMaterial } from "three";


// Shadow-pass depth material for voxel impostors. World-space-sized
// camera-facing billboard, same geometry as the colour pass — in the
// shadow pass `modelViewMatrix = light_view * model`, so the quad
// expansion (which happens in view space here) lands as a light-facing
// world-sized splat. The depth at every fragment of the splat equals
// the splat centre's depth in light space (the corner offset is in
// view-XY only), so gl_FragCoord.z directly gives the splat's distance
// from the light.
//
// Mirrors three.js's packDepthToRGBA so the PCF shadow sampler can
// unpack our output; identical encoding to ImpostorShaderDepthV0.
const shader_vx = `
    in vec3 position;
    in vec2 uv;
    in vec3 iPosition;
    in vec3 iNormal;

    out vec2 vUv;

    uniform mat4 modelMatrix;
    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;
    uniform float uPointDiameter;

    void main() {
        vUv = uv;

        float model_scale = length(modelMatrix[0].xyz);
        float world_diameter = uPointDiameter * model_scale;
        float radius_OS = uPointDiameter * 0.5;

        // Inward-shift must match the colour pass exactly so the
        // cast silhouette agrees with the visible one — see
        // VoxelImpostorShaderV0 for the rationale.
        vec3 centre_OS = iPosition - iNormal * radius_OS;
        vec4 centre_view = modelViewMatrix * vec4(centre_OS, 1.0);
        vec2 corner = position.xy * world_diameter;
        gl_Position = projectionMatrix * (centre_view + vec4(corner, 0.0, 0.0));
    }
`;

const shader_fg = `
    precision highp float;
    precision highp int;

    in vec2 vUv;

    out vec4 color_out;

    uniform bool uDiscMask;

    const float PackUpscale = 256.0 / 255.0;
    const vec3 PackFactors = vec3(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0);
    const float ShiftRight8 = 1.0 / 256.0;

    vec4 packDepthToRGBA(const in float v) {
        vec4 r = vec4(fract(v * PackFactors), v);
        r.yzw -= r.xyz * ShiftRight8;
        return r * PackUpscale;
    }

    void main() {
        if (uDiscMask) {
            vec2 c = vUv - 0.5;
            if (dot(c, c) > 0.25) {
                discard;
            }
        }
        color_out = packDepthToRGBA(gl_FragCoord.z);
    }
`;


/**
 * Depth-pass companion to {@link VoxelImpostorShaderLitV0}. Wire as
 * ShadedGeometry.depth_material so the impostor casts a shadow whose
 * silhouette matches the visible splat cloud (modulo the half-splat
 * normal bias the lit shader applies on the receiver side).
 *
 * Keep `uPointDiameter` and `uDiscMask` aligned with the colour pass.
 */
export class VoxelImpostorShaderDepthV0 extends RawShaderMaterial {
    constructor() {
        super({
            vertexShader: shader_vx,
            fragmentShader: shader_fg,
            glslVersion: GLSL3,
            uniforms: {
                uPointDiameter: { value: 0.05 },
                uDiscMask: { value: false }
            }
        });

        this.blending = NoBlending;
    }
}
