import { invertMat4, multiplyMat4 } from "../scene/index.js";
export function createStereoCameraRig(options) {
    validateViewport(options.viewport);
    const eyeSeparation = options.eyeSeparation ?? 0.064;
    const convergenceDistance = options.convergenceDistance ?? 10;
    const layout = options.layout ?? "side-by-side";
    if (!Number.isFinite(eyeSeparation) || eyeSeparation <= 0 || eyeSeparation > 1) {
        throw new RangeError("Stereo eyeSeparation must be finite and in (0, 1].");
    }
    if (!Number.isFinite(convergenceDistance) || convergenceDistance <= options.frame.near) {
        throw new RangeError("Stereo convergenceDistance must be finite and greater than the near plane.");
    }
    const half = eyeSeparation / 2;
    const left = createEyeView("left", -half, options, layout, convergenceDistance);
    const right = createEyeView("right", half, options, layout, convergenceDistance);
    return {
        layout,
        eyeSeparation,
        convergenceDistance,
        views: [left, right]
    };
}
function createEyeView(eye, eyeOffset, options, layout, convergenceDistance) {
    const viewMatrix = multiplyMat4(translationMat4(-eyeOffset, 0, 0), options.frame.viewMatrix);
    const projectionMatrix = offsetProjectionForConvergence(options.frame.projectionMatrix, eyeOffset, options.frame.near, convergenceDistance);
    return {
        eye,
        eyeOffset,
        cameraPosition: offsetCameraPosition(options.frame.viewMatrix, options.frame.cameraPosition, eyeOffset),
        viewport: eyeViewport(eye, options.viewport, layout),
        viewMatrix,
        projectionMatrix,
        viewProjectionMatrix: multiplyMat4(projectionMatrix, viewMatrix)
    };
}
function offsetProjectionForConvergence(projection, eyeOffset, near, convergenceDistance) {
    const result = [...projection];
    result[8] = (result[8] ?? 0) + eyeOffset * near / convergenceDistance;
    return result;
}
function offsetCameraPosition(viewMatrix, cameraPosition, eyeOffset) {
    const inverseView = invertMat4(viewMatrix);
    const right = [inverseView[0], inverseView[1], inverseView[2]];
    return [
        cameraPosition[0] + right[0] * eyeOffset,
        cameraPosition[1] + right[1] * eyeOffset,
        cameraPosition[2] + right[2] * eyeOffset
    ];
}
function eyeViewport(eye, viewport, layout) {
    if (layout === "side-by-side") {
        const halfWidth = Math.floor(viewport.width / 2);
        return eye === "left"
            ? { x: viewport.x, y: viewport.y, width: halfWidth, height: viewport.height }
            : { x: viewport.x + halfWidth, y: viewport.y, width: viewport.width - halfWidth, height: viewport.height };
    }
    const halfHeight = Math.floor(viewport.height / 2);
    return eye === "left"
        ? { x: viewport.x, y: viewport.y, width: viewport.width, height: halfHeight }
        : { x: viewport.x, y: viewport.y + halfHeight, width: viewport.width, height: viewport.height - halfHeight };
}
function validateViewport(viewport) {
    const values = [viewport.x, viewport.y, viewport.width, viewport.height];
    if (values.some((value) => !Number.isFinite(value))) {
        throw new RangeError("Stereo viewport values must be finite.");
    }
    if (viewport.width < 2 || viewport.height < 2) {
        throw new RangeError("Stereo viewport dimensions must be at least 2x2.");
    }
}
function translationMat4(x, y, z) {
    return [
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        x, y, z, 1
    ];
}
//# sourceMappingURL=StereoCameraRig.js.map