//====================================================================
// triangle_rasterizer.v - 三角形光栅化器
// 第04课：扫描线光栅化，重心坐标判断，属性插值
//====================================================================
module triangle_rasterizer #(
    parameter COORD_WIDTH = 16,
    parameter COLOR_WIDTH = 24,
    parameter FB_WIDTH    = 640,
    parameter FB_HEIGHT   = 480,
    parameter FRAC_BITS  = 12
)(
    input  wire                          clk,
    input  wire                          rst_n,
    input  wire                          tri_valid,
    input  wire signed [COORD_WIDTH-1:0] tri_v0_x, tri_v0_y,
    input  wire signed [COORD_WIDTH-1:0] tri_v1_x, tri_v1_y,
    input  wire signed [COORD_WIDTH-1:0] tri_v2_x, tri_v2_y,
    input  wire [COLOR_WIDTH-1:0]        tri_v0_color, tri_v1_color, tri_v2_color,
    output reg                           tri_ready,
    output reg                           frag_valid,
    output reg  [COORD_WIDTH-1:0]        frag_x, frag_y,
    output reg  [COLOR_WIDTH-1:0]        frag_color,
    output reg                           frag_last,
    input  wire                          frag_ready
);
/* verilator lint_off WIDTHEXPAND */
/* verilator lint_off WIDTHTRUNC */
/* verilator lint_off CASEOVERLAP */
/* verilator lint_off CMPCONST */
/* verilator lint_off UNSIGNED */
/* verilator lint_off WIDTHCONCAT */

    reg signed [COORD_WIDTH-1:0] bb_min_x, bb_max_x, bb_min_y, bb_max_y;
    reg signed [COORD_WIDTH-1:0] scan_x, scan_y;
    reg signed [2*COORD_WIDTH-1:0] det;
    reg signed [COORD_WIDTH-1:0] dy12, dx12, dy20, dx20;
    reg signed [2*COORD_WIDTH-1:0] lambda0, lambda1;
    reg signed [COORD_WIDTH-1:0] v0x, v0y, v1x, v1y, v2x, v2y;
    reg [COLOR_WIDTH-1:0] c0, c1, c2;
    localparam S_IDLE=3'd0, S_SETUP=3'd1, S_SCAN=3'd2, S_CHECK=3'd3, S_INTERP=3'd4, S_OUTPUT=3'd5, S_DONE=3'd6;
    reg [2:0] state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= S_IDLE; tri_ready <= 1'b1; frag_valid <= 1'b0; frag_last <= 1'b0;
        end else begin
            frag_valid <= 1'b0; frag_last <= 1'b0;
            case (state)
                S_IDLE: begin
                    tri_ready <= 1'b1;
                    if (tri_valid) begin
                        tri_ready <= 1'b0;
                        v0x <= tri_v0_x; v0y <= tri_v0_y; v1x <= tri_v1_x; v1y <= tri_v1_y;
                        v2x <= tri_v2_x; v2y <= tri_v2_y; c0 <= tri_v0_color; c1 <= tri_v1_color; c2 <= tri_v2_color;
                        state <= S_SETUP;
                    end
                end
                S_SETUP: begin
                    dy12 <= v1y - v2y; dx12 <= v2x - v1x;
                    dy20 <= v2y - v0y; dx20 <= v0x - v2x;
                    det <= (v1y - v2y) * (v0x - v2x) + (v2x - v1x) * (v0y - v2y);
                    bb_min_x <= (v0x < v1x) ? ((v0x < v2x) ? v0x : v2x) : ((v1x < v2x) ? v1x : v2x);
                    bb_max_x <= (v0x > v1x) ? ((v0x > v2x) ? v0x : v2x) : ((v1x > v2x) ? v1x : v2x);
                    bb_min_y <= (v0y < v1y) ? ((v0y < v2y) ? v0y : v2y) : ((v1y < v2y) ? v1y : v2y);
                    bb_max_y <= (v0y > v1y) ? ((v0y > v2y) ? v0y : v2y) : ((v1y > v2y) ? v1y : v2y);
                    scan_x <= (v0x < v1x) ? ((v0x < v2x) ? v0x : v2x) : ((v1x < v2x) ? v1x : v2x);
                    scan_y <= (v0y < v1y) ? ((v0y < v2y) ? v0y : v2y) : ((v1y < v2y) ? v1y : v2y);
                    state <= S_SCAN;
                end
                S_SCAN: begin
                    if (scan_y <= bb_max_y) begin
                        if (scan_x <= bb_max_x) begin
                            lambda0 <= dy12 * (scan_x - v2x) + dx12 * (scan_y - v2y);
                            lambda1 <= dy20 * (scan_x - v2x) + dx20 * (scan_y - v2y);
                            state <= S_CHECK;
                        end else begin
                            scan_x <= bb_min_x; scan_y <= scan_y + 16'd1;
                        end
                    end else state <= S_DONE;
                end
                S_CHECK: begin
                    if ((det > 0 && lambda0 >= 0 && lambda1 >= 0 && (det-lambda0-lambda1) >= 0) ||
                        (det < 0 && lambda0 <= 0 && lambda1 <= 0 && (det-lambda0-lambda1) <= 0))
                        state <= S_INTERP;
                    else begin scan_x <= scan_x + 16'd1; state <= S_SCAN; end
                end
                S_INTERP: begin
                    frag_color <= (lambda0 >= lambda1) ? ((lambda0 >= (det-lambda0-lambda1)) ? c0 : c2) : ((lambda1 >= (det-lambda0-lambda1)) ? c1 : c2);
                    frag_x <= scan_x[COORD_WIDTH-1:0]; frag_y <= scan_y[COORD_WIDTH-1:0];
                    state <= S_OUTPUT;
                end
                S_OUTPUT: begin
                    frag_valid <= 1'b1;
                    if (scan_x >= bb_max_x && scan_y >= bb_max_y) begin frag_last <= 1'b1; state <= S_DONE; end
                    else begin scan_x <= scan_x + 16'd1; state <= S_SCAN; end
                end
                S_DONE: begin tri_ready <= 1'b1; state <= S_IDLE; end
                default: state <= S_IDLE;
            endcase
        end
    end
endmodule