import { Material } from "./Material.js";
import { RenderDeviceError } from "./RenderDevice.js";
import { BaseRenderPass } from "./RenderPass.js";
import { ShaderModule } from "./ShaderModule.js";
import { DEFAULT_DEPTH_SHADER_NAME, createDefaultShaderLibrary } from "./ShaderLibrary.js";
import {} from "./ForwardPass.js";
import { identityMat4, multiplyMat4 } from "../scene/index.js";
export class DepthMaterial extends Material {
    constructor() {
        super({
            name: "depth",
            shaderKey: DEFAULT_DEPTH_SHADER_NAME,
            parameters: {
                u_modelViewProjection: identityMatrix()
            },
            requiredAttributes: ["a_position"],
            uniformSchema: [{ name: "u_modelViewProjection", kind: "mat4" }]
        });
    }
}
export class DepthPass extends BaseRenderPass {
    options;
    shaderLibrary;
    material = new DepthMaterial();
    shaderModule = null;
    constructor(options) {
        super("depth", [], ["depth"]);
        this.options = options;
        this.shaderLibrary = options.shaderLibrary ?? createDefaultShaderLibrary();
    }
    execute(context) {
        for (const caster of this.options.casters) {
            this.drawCaster(context.device, caster);
        }
    }
    drawCaster(device, caster) {
        const shader = this.getShader(device);
        const vertexBuffer = caster.geometry.vertexBuffer.upload(device);
        const indexBuffer = caster.geometry.indexBuffer?.upload(device);
        const drawRange = resolveDepthDrawRange(caster.geometry, caster.drawRange);
        const modelMatrix = toMat4(caster.modelMatrix ?? identityMat4(), caster.label);
        const viewProjection = toMat4(this.options.viewProjectionMatrix ?? identityMat4(), caster.label);
        this.material.setParameter("u_modelViewProjection", multiplyMat4(viewProjection, modelMatrix));
        const command = {
            label: caster.label ?? "shadow-caster",
            topology: caster.geometry.topology,
            vertexBuffer,
            vertexFormat: caster.geometry.vertexBuffer.format,
            vertexCount: indexBuffer ? caster.geometry.vertexBuffer.vertexCount : drawRange.count,
            ...(indexBuffer === undefined && drawRange.start > 0 ? { firstVertex: drawRange.start } : {}),
            shader,
            uniforms: this.material.getParameters()
        };
        if (indexBuffer) {
            Object.assign(command, {
                indexBuffer,
                indexType: caster.geometry.indexBuffer?.type,
                indexCount: drawRange.count,
                ...(drawRange.start > 0 ? { firstIndex: drawRange.start } : {})
            });
        }
        device.draw(command);
    }
    getShader(device) {
        if (!this.shaderModule) {
            this.shaderModule = ShaderModule.fromLibrary(this.shaderLibrary, this.material.shaderKey);
        }
        return this.shaderModule.compile(device);
    }
}
function resolveDepthDrawRange(geometry, range) {
    const available = geometry.indexBuffer?.count ?? geometry.vertexBuffer.vertexCount;
    if (!range)
        return { start: 0, count: available };
    if (!Number.isInteger(range.start) || range.start < 0 || !Number.isInteger(range.count) || range.count <= 0 || range.start + range.count > available) {
        throw new RenderDeviceError("Depth caster drawRange must fit inside geometry draw count", "DEPTH_DRAW_RANGE_INVALID", {
            start: range.start,
            count: range.count,
            available
        });
    }
    return range;
}
function identityMatrix() {
    return new Float32Array([
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    ]);
}
function toMat4(value, label) {
    const values = Array.from(value);
    if (values.length !== 16 || values.some((entry) => !Number.isFinite(entry))) {
        throw new Error(`DepthPass ${label ?? "caster"} matrix must contain 16 finite numbers.`);
    }
    return values;
}
//# sourceMappingURL=DepthPass.js.map