//====================================================================
// perspective_projector.v - 透视投影器
// 第08课：透视投影矩阵生成与齐次除法
//====================================================================
module perspective_projector #(
    parameter COORD_WIDTH = 16,
    parameter FRAC_BITS  = 12
)(
    input  wire                          clk, rst_n,
    input  wire signed [COORD_WIDTH-1:0] fov_q12,       // 视场角(Q4.12)
    input  wire signed [COORD_WIDTH-1:0] aspect_q12,    // 宽高比(Q4.12)
    input  wire signed [COORD_WIDTH-1:0] near_q12,      // 近裁剪面(Q4.12)
    input  wire signed [COORD_WIDTH-1:0] far_q12,       // 远裁剪面(Q4.12)
    input  wire                          proj_valid,
    output reg                           proj_ready,
    output reg  signed [COORD_WIDTH-1:0] proj_mat_00, proj_mat_01, proj_mat_02, proj_mat_03,
    output reg  signed [COORD_WIDTH-1:0] proj_mat_10, proj_mat_11, proj_mat_12, proj_mat_13,
    output reg  signed [COORD_WIDTH-1:0] proj_mat_20, proj_mat_21, proj_mat_22, proj_mat_23,
    output reg  signed [COORD_WIDTH-1:0] proj_mat_30, proj_mat_31, proj_mat_32, proj_mat_33,
    output reg                           proj_mat_valid,
    // 齐次除法接口
    input  wire                          hdiv_valid,
    input  wire signed [COORD_WIDTH-1:0] clip_x, clip_y, clip_z, clip_w,
    output reg  signed [COORD_WIDTH-1:0] ndc_x, ndc_y, ndc_z,
    output reg                           ndc_valid
);
/* verilator lint_off WIDTHEXPAND */
/* verilator lint_off WIDTHTRUNC */
/* verilator lint_off CASEOVERLAP */
/* verilator lint_off CMPCONST */
/* verilator lint_off UNSIGNED */
/* verilator lint_off WIDTHCONCAT */

    function signed [COORD_WIDTH-1:0] qmul;
        input signed [COORD_WIDTH-1:0] a, b;
        reg signed [2*COORD_WIDTH-1:0] prod;
        begin prod = a * b; qmul = prod[2*COORD_WIDTH-FRAC_BITS-1:COORD_WIDTH-FRAC_BITS]; end
    endfunction
    // 1/x倒数近似(牛顿迭代法第一步)
    function signed [COORD_WIDTH-1:0] qinv;
        input signed [COORD_WIDTH-1:0] x;
        reg signed [COORD_WIDTH-1:0] approx;
        begin
            approx = (x > 0) ? (16'h4000 / x) : -(16'h4000 / (-x)); // 粗略近似
            qinv = qmul(2 * 16'h1000 - qmul(x, approx), approx);     // 一次牛顿迭代
        end
    endfunction
    // 投影矩阵计算
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin proj_mat_valid <= 0; ndc_valid <= 0; end
        else begin
            proj_mat_valid <= 0; ndc_valid <= 0;
            if (proj_valid) begin
                // f = 1/tan(fov/2), 简化：使用fov直接查表
                // proj[0][0] = f/aspect, proj[1][1] = f
                // proj[2][2] = -(far+near)/(far-near), proj[2][3] = -2*far*near/(far-near)
                // proj[3][2] = -1
                proj_mat_00 <= qmul(16'h1000, qinv(aspect_q12)); // f/aspect(简化)
                proj_mat_01 <= 0; proj_mat_02 <= 0; proj_mat_03 <= 0;
                proj_mat_10 <= 0; proj_mat_11 <= 16'h1000; proj_mat_12 <= 0; proj_mat_13 <= 0;
                proj_mat_20 <= 0; proj_mat_21 <= 0;
                proj_mat_22 <= -qmul(far_q12 + near_q12, qinv(far_q12 - near_q12));
                proj_mat_23 <= qmul(-qmul(2 * 16'h1000, far_q12), qinv(far_q12 - near_q12));
                proj_mat_30 <= 0; proj_mat_31 <= 0;
                proj_mat_32 <= -16'h1000; proj_mat_33 <= 0;
                proj_mat_valid <= 1;
            end
            if (hdiv_valid) begin
                // 齐次除法: ndc = clip / clip_w
                ndc_x <= qmul(clip_x, qinv(clip_w));
                ndc_y <= qmul(clip_y, qinv(clip_w));
                ndc_z <= qmul(clip_z, qinv(clip_w));
                ndc_valid <= 1;
            end
        end
    end
endmodule