rustraight 0.6.4

A simple 2D game library for Rust, inspired by DXLib
use crate::geometry2d::Vec2;
use crate::graphics::DrawScreenParams;

// ── Vertex types ──────────────────────────────────────────────────────────────

/// 画像描画用の頂点(NDC座標・UV・マスク情報・アルファを含む、stride: 48 bytes)。
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct ScreenVertex {
    pub(crate) pos:       [f32; 2], // NDC 座標
    pub(crate) uv:        [f32; 2],
    pub(crate) screen_xy: [f32; 2], // 画面ピクセル位置(マスクサンプリング用に補間される)
    pub(crate) mask_ox:   f32,
    pub(crate) mask_oy:   f32,
    pub(crate) mask_w:    f32,
    pub(crate) mask_h:    f32,
    pub(crate) mask_on:   f32,      // 1.0 = マスク適用, 0.0 = マスクなし
    pub(crate) alpha:     f32,
}

// ── Helpers ───────────────────────────────────────────────────────────────────

/// 通常描画(メインウィンドウ/オフスクリーンスクリーン)用の画像矩形頂点を構築する。
/// 拡大縮小・回転(中心基準)・反転・マスク座標・アルファを適用した6頂点(2三角形)を返す。
pub(crate) fn build_screen_quad_ex(
    x: i32, y: i32,
    src_w: u32, src_h: u32,
    screen_w: u32, screen_h: u32,
    mask_ox: f32, mask_oy: f32, mask_w: f32, mask_h: f32, mask_on: f32,
    params: &DrawScreenParams,
) -> [ScreenVertex; 6] {
    let draw_w = src_w as f32 * params.scale_x;
    let draw_h = src_h as f32 * params.scale_y;
    let center = Vec2::new(x as f32 + draw_w * 0.5, y as f32 + draw_h * 0.5);
    let half   = Vec2::new(draw_w * 0.5, draw_h * 0.5);

    let rotation = params.rotation.to_radians();
    let corners = [
        center + Vec2::new(-half.x, -half.y).rotate(rotation),
        center + Vec2::new( half.x, -half.y).rotate(rotation),
        center + Vec2::new(-half.x,  half.y).rotate(rotation),
        center + Vec2::new( half.x,  half.y).rotate(rotation),
    ];

    let (u0, u1) = if params.flip_x { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };
    let (v0, v1) = if params.flip_y { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };

    let v = |idx: usize, u: f32, tv: f32| ScreenVertex {
        pos:       crate::draw::to_ndc(corners[idx].x, corners[idx].y, screen_w, screen_h),
        uv:        [u, tv],
        screen_xy: corners[idx].into(),
        mask_ox, mask_oy, mask_w, mask_h, mask_on,
        alpha:     params.alpha,
    };

    let tl = v(0, u0, v0);
    let tr = v(1, u1, v0);
    let bl = v(2, u0, v1);
    let br = v(3, u1, v1);
    [tl, tr, bl, tr, br, bl]
}

/// オーバーレイ描画用の画像矩形頂点を構築する。
/// x/y は仮想スクリーンのピクセル座標。win_scale_* でディスプレイピクセル座標に変換し、
/// main_x/main_y でメインウィンドウの画面上位置だけオフセットする。
#[allow(clippy::too_many_arguments)]
pub(crate) fn build_screen_quad_overlay(
    x: i32, y: i32,
    src_w: u32, src_h: u32,
    display_w: u32, display_h: u32,
    main_x: i32, main_y: i32,
    win_scale_x: f32, win_scale_y: f32,
    mask_ox: f32, mask_oy: f32, mask_w: f32, mask_h: f32, mask_on: f32,
    params: &DrawScreenParams,
) -> [ScreenVertex; 6] {
    let win_scale = Vec2::new(win_scale_x, win_scale_y);
    let main_pos  = Vec2::new(main_x as f32, main_y as f32);

    // 画像サイズ・位置を仮想スクリーンピクセルからディスプレイピクセルへスケールする
    let draw_w = src_w as f32 * params.scale_x * win_scale_x;
    let draw_h = src_h as f32 * params.scale_y * win_scale_y;
    let half   = Vec2::new(draw_w * 0.5, draw_h * 0.5);
    let center = Vec2::new(x as f32, y as f32) * win_scale + half + main_pos;

    // マスク座標を仮想スクリーン空間からディスプレイ空間へスケールする
    let (mask_offset, mask_size) = if mask_on > 0.5 {
        (Vec2::new(mask_ox, mask_oy) * win_scale + main_pos, Vec2::new(mask_w, mask_h) * win_scale)
    } else {
        (Vec2::new(mask_ox, mask_oy), Vec2::new(mask_w, mask_h))
    };

    let rotation = params.rotation.to_radians();
    let corners = [
        center + Vec2::new(-half.x, -half.y).rotate(rotation),
        center + Vec2::new( half.x, -half.y).rotate(rotation),
        center + Vec2::new(-half.x,  half.y).rotate(rotation),
        center + Vec2::new( half.x,  half.y).rotate(rotation),
    ];

    let (u0, u1) = if params.flip_x { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };
    let (v0, v1) = if params.flip_y { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };
    let sv = |idx: usize, u: f32, tv: f32| ScreenVertex {
        pos: crate::draw::to_ndc(corners[idx].x, corners[idx].y, display_w, display_h),
        uv: [u, tv],
        screen_xy: corners[idx].into(),
        mask_ox: mask_offset.x, mask_oy: mask_offset.y, mask_w: mask_size.x, mask_h: mask_size.y, mask_on,
        alpha: params.alpha,
    };
    let tl = sv(0, u0, v0); let tr = sv(1, u1, v0);
    let bl = sv(2, u0, v1); let br = sv(3, u1, v1);
    [tl, tr, bl, tr, br, bl]
}

/// メインサーフェスNDC座標の頂点を、オーバーレイのディスプレイ座標系NDCへ再投影する
/// (NDC→ピクセル→`main_pos`だけ平行移動→ディスプレイNDC、の順で変換する)。
pub(crate) fn reproject_ndc_to_overlay(p: Vec2, surface_size: Vec2, main_pos: Vec2, display_size: Vec2) -> Vec2 {
    let screen = Vec2::new(
        (p.x * 0.5 + 0.5) * surface_size.x,
        (1.0 - (p.y * 0.5 + 0.5)) * surface_size.y,
    ) + main_pos;
    Vec2::new(
        screen.x / display_size.x * 2.0 - 1.0,
        1.0 - screen.y / display_size.y * 2.0,
    )
}