fast3d/gbi/
utils.rs

1use crate::output::gfx::Face;
2use crate::rsp::RSPConstants;
3#[allow(unused_imports)]
4use bitflags::Flags;
5use fast3d_gbi::defines::render_mode::{
6    BlendAlpha2, BlendColor, RenderMode, RenderModeFlags, ZMode,
7};
8use fast3d_gbi::defines::{AlphaCompare, CycleType, GeometryModes, OtherModeH, TextureFilter};
9
10// TODO: Replace with the new getter on GfxCommand
11pub fn get_cmd(val: usize, start_bit: u32, num_bits: u32) -> usize {
12    (val >> start_bit) & ((1 << num_bits) - 1)
13}
14
15pub fn get_render_mode_from_other_mode_l(other_mode_l: u32) -> RenderMode {
16    RenderMode::try_from(other_mode_l).unwrap()
17}
18
19pub fn other_mode_l_uses_texture_edge(other_mode_l: u32) -> bool {
20    let render_mode = get_render_mode_from_other_mode_l(other_mode_l);
21    render_mode.flags.contains(RenderModeFlags::CVG_X_ALPHA)
22}
23
24pub fn other_mode_l_uses_alpha(other_mode_l: u32) -> bool {
25    let render_mode = get_render_mode_from_other_mode_l(other_mode_l);
26    render_mode.blend_cycle1.alpha2 == BlendAlpha2::OneMinusAlpha
27    // TODO: Do we need to check which cycle we're in?
28    // render_mode.blend_cycle2.color2 == BlendColor::Memory && render_mode.blend_cycle2.alpha2 == BlendAlpha2::OneMinusAlpha
29}
30
31pub fn other_mode_l_alpha_compare_threshold(other_mode_l: u32) -> bool {
32    other_mode_l & AlphaCompare::Threshold as u32 == AlphaCompare::Threshold as u32
33}
34
35pub fn other_mode_l_uses_fog(other_mode_l: u32) -> bool {
36    let render_mode = get_render_mode_from_other_mode_l(other_mode_l);
37    render_mode.blend_cycle1.color1 == BlendColor::Fog
38}
39
40pub fn get_zmode_from_other_mode_l(other_mode_l: u32) -> ZMode {
41    get_render_mode_from_other_mode_l(other_mode_l).z_mode
42}
43
44pub fn other_mode_l_alpha_compare_dither(other_mode_l: u32) -> bool {
45    other_mode_l & AlphaCompare::Dither as u32 == AlphaCompare::Dither as u32
46}
47
48pub fn get_cycle_type_from_other_mode_h(mode_h: u32) -> CycleType {
49    (((mode_h >> OtherModeH::Shift::CYCLE_TYPE.bits()) & 0x03) as u8)
50        .try_into()
51        .unwrap()
52}
53
54pub fn get_texture_filter_from_other_mode_h(mode_h: u32) -> TextureFilter {
55    (((mode_h >> OtherModeH::Shift::TEXT_FILT.bits()) & 0x3) as u8)
56        .try_into()
57        .unwrap()
58}
59
60pub fn translate_cull_mode(
61    geometry_mode: GeometryModes,
62    rsp_constants: &RSPConstants,
63) -> Option<Face> {
64    // We do unchecked comparisons because the values set in rsp_constants are per GBI
65    // and do not appear in the general GeometryModes enum
66    let cull_front = geometry_mode.contains(GeometryModes::from_bits_retain(
67        rsp_constants.geomode_cull_front_val,
68    ));
69    let cull_back = geometry_mode.contains(GeometryModes::from_bits_retain(
70        rsp_constants.geomode_cull_back_val,
71    ));
72
73    if cull_front && cull_back {
74        panic!("Culling both front and back faces is not supported");
75    } else if cull_front {
76        Some(Face::Front)
77    } else if cull_back {
78        Some(Face::Back)
79    } else {
80        None
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    pub trait I32MathExt {
89        fn ushr(self, n: u32) -> u32;
90    }
91
92    impl I32MathExt for i32 {
93        fn ushr(self, n: u32) -> u32 {
94            ((self >> n) & ((1 << (32 - n)) - 1)) as u32
95        }
96    }
97
98    #[test]
99    fn test_get_cmd() {
100        let word: usize = 84939284;
101        let a = get_cmd(word, 16, 8) / 2;
102        let b = get_cmd(word, 8, 8) / 2;
103        let c = get_cmd(word, 0, 8) / 2;
104
105        assert_eq!(a, 8);
106        assert_eq!(b, 9);
107        assert_eq!(c, 10);
108
109        assert_eq!(a, ((((word as i32).ushr(16)) & 0xFF) / 2) as usize);
110    }
111}