citro3d/
texenv.rs

1//! Texture combiner support. See <https://www.khronos.org/opengl/wiki/Texture_Combiners>
2//! for more details.
3
4use bitflags::bitflags;
5
6use crate::texture;
7
8// https://oreo639.github.io/citro3d/texenv_8h.html#a9eda91f8e7252c91f873b1d43e3728b6
9pub(crate) const TEXENV_COUNT: usize = 6;
10
11/// A texture combiner, also called a "texture environment" (hence the struct name).
12/// See also [`texenv.h` documentation](https://oreo639.github.io/citro3d/texenv_8h.html).
13#[derive(Clone, Copy)]
14#[doc(alias = "C3D_TexEnv")]
15pub struct TexEnv {
16    inner: citro3d_sys::C3D_TexEnv,
17
18    // For checking that necessary textures are bound
19    pub(crate) sources: [Source; 6],
20}
21
22impl TexEnv {
23    pub fn as_raw(&self) -> *mut citro3d_sys::C3D_TexEnv {
24        &self.inner as *const _ as *mut _
25    }
26
27    /// Create a new texture combiner stage, or "texture environment"
28    #[doc(alias = "C3D_TexEnvInit")]
29    pub fn new() -> TexEnv {
30        let inner = unsafe {
31            let mut inner = core::mem::MaybeUninit::<citro3d_sys::C3D_TexEnv>::uninit();
32            Self::init_reset(inner.as_mut_ptr());
33            inner.assume_init()
34        };
35
36        TexEnv {
37            inner,
38            sources: [Source::default(); 6],
39        }
40    }
41
42    #[doc(alias = "C3D_TexEnvInit")]
43    pub fn reset(self) -> TexEnv {
44        unsafe {
45            citro3d_sys::C3D_TexEnvInit(self.as_raw());
46        }
47
48        TexEnv {
49            inner: self.inner,
50            sources: [Source::default(); 6],
51        }
52    }
53
54    /// Set the sources to use for the rgb and/or alpha components of this texenv stage.
55    /// If sourcing from a texture unit, ensure a texture is also bound to that unit
56    /// with [`Frame::with_texture`]
57    #[doc(alias = "C3D_TexEnvSrc")]
58    pub fn src(
59        mut self,
60        mode: Mode,
61        source0: Source,
62        source1: Option<Source>,
63        source2: Option<Source>,
64    ) -> TexEnv {
65        unsafe {
66            citro3d_sys::C3D_TexEnvSrc(
67                self.as_raw(),
68                mode.bits(),
69                source0 as _,
70                source1.unwrap_or_default() as _,
71                source2.unwrap_or_default() as _,
72            );
73        }
74
75        if mode.contains(Mode::RGB) {
76            self.sources[0] = source0;
77            self.sources[1] = source1.unwrap_or_default();
78            self.sources[2] = source2.unwrap_or_default();
79        }
80
81        if mode.contains(Mode::ALPHA) {
82            self.sources[3] = source0;
83            self.sources[4] = source1.unwrap_or_default();
84            self.sources[5] = source2.unwrap_or_default();
85        }
86
87        self
88    }
89
90    #[doc(alias = "C3D_TexEnvOpRgb")]
91    pub fn op_rgb(self, o1: RGBOp, o2: Option<RGBOp>, o3: Option<RGBOp>) -> TexEnv {
92        unsafe {
93            citro3d_sys::C3D_TexEnvOpRgb(
94                self.as_raw(),
95                o1 as _,
96                o2.unwrap_or_default() as _,
97                o3.unwrap_or_default() as _,
98            );
99        }
100        self
101    }
102
103    #[doc(alias = "C3D_TexEnvOpAlpha")]
104    pub fn op_alpha(self, o1: AlphaOp, o2: Option<AlphaOp>, o3: Option<AlphaOp>) -> TexEnv {
105        unsafe {
106            citro3d_sys::C3D_TexEnvOpAlpha(
107                self.as_raw(),
108                o1 as _,
109                o2.unwrap_or_default() as _,
110                o3.unwrap_or_default() as _,
111            );
112        }
113        self
114    }
115
116    #[doc(alias = "C3D_TexEnvFunc")]
117    pub fn func(self, mode: Mode, func: CombineFunc) -> TexEnv {
118        unsafe {
119            citro3d_sys::C3D_TexEnvFunc(self.as_raw(), mode.bits(), func as _);
120        }
121        self
122    }
123
124    #[doc(alias = "C3D_TexEnvColor")]
125    pub fn color(self, color: u32) -> TexEnv {
126        unsafe {
127            citro3d_sys::C3D_TexEnvColor(self.as_raw(), color);
128        }
129        self
130    }
131
132    #[doc(alias = "C3D_TexEnvScale")]
133    pub fn scale(self, mode: Mode, scale: Scale) -> TexEnv {
134        unsafe {
135            citro3d_sys::C3D_TexEnvScale(self.as_raw(), mode.bits() as _, scale as _);
136        }
137        self
138    }
139
140    /// Set this as the active texenv in the given stage.
141    #[doc(alias = "C3D_SetTexEnv")]
142    pub(crate) fn set_texenv(&self, stage: usize) -> crate::Result<()> {
143        if stage >= TEXENV_COUNT {
144            return Err(crate::Error::IndexOutOfBounds {
145                idx: stage as i32,
146                len: TEXENV_COUNT as i32,
147            });
148        }
149
150        unsafe {
151            citro3d_sys::C3D_SetTexEnv(stage as i32, self.as_raw());
152        }
153
154        Ok(())
155    }
156
157    #[doc(alias = "C3D_TexEnvInit")]
158    pub(crate) unsafe fn init_reset(texenv: *mut citro3d_sys::C3D_TexEnv) {
159        unsafe {
160            citro3d_sys::C3D_TexEnvInit(texenv);
161            citro3d_sys::C3D_DirtyTexEnv(texenv);
162        }
163    }
164
165    #[doc(alias = "C3D_TexEnvGet")]
166    pub(crate) unsafe fn get_texenv(stage: usize) -> *mut citro3d_sys::C3D_TexEnv {
167        unsafe { citro3d_sys::C3D_GetTexEnv(stage as i32) }
168    }
169}
170
171/// Configure the source values of the texture combiner.
172#[derive(Clone, Copy)]
173pub struct Sources {
174    /// The first [`Source`] operand to the texture combiner
175    pub source0: Source,
176    /// Optional additional [`Source`] operand to use
177    pub source1: Option<Source>,
178    /// Optional additional [`Source`] operand to use
179    pub source2: Option<Source>,
180}
181
182impl Default for Sources {
183    fn default() -> Self {
184        Sources {
185            source0: Source::PrimaryColor,
186            source1: None,
187            source2: None,
188        }
189    }
190}
191
192impl Default for TexEnv {
193    fn default() -> Self {
194        Self::new()
195    }
196}
197
198bitflags! {
199    /// Whether to operate on colors, alpha values, or both.
200    #[doc(alias = "C3D_TexEnvMode")]
201    pub struct Mode: citro3d_sys::C3D_TexEnvMode {
202        #[allow(missing_docs)]
203        const RGB = citro3d_sys::C3D_RGB;
204        #[allow(missing_docs)]
205        const ALPHA = citro3d_sys::C3D_Alpha;
206        #[allow(missing_docs)]
207        const BOTH = citro3d_sys::C3D_Both;
208    }
209}
210
211/// A source operand of a [`TexEnv`]'s texture combination.
212#[doc(alias = "GPU_TEVSRC")]
213#[allow(missing_docs)]
214#[derive(Debug, Clone, Copy, Default)]
215#[repr(u8)]
216#[non_exhaustive]
217pub enum Source {
218    #[default]
219    PrimaryColor = ctru_sys::GPU_PRIMARY_COLOR,
220    FragmentPrimaryColor = ctru_sys::GPU_FRAGMENT_PRIMARY_COLOR,
221    FragmentSecondaryColor = ctru_sys::GPU_FRAGMENT_SECONDARY_COLOR,
222    Texture0 = ctru_sys::GPU_TEXTURE0,
223    Texture1 = ctru_sys::GPU_TEXTURE1,
224    Texture2 = ctru_sys::GPU_TEXTURE2,
225    Texture3 = ctru_sys::GPU_TEXTURE3,
226    PreviousBuffer = ctru_sys::GPU_PREVIOUS_BUFFER,
227    Constant = ctru_sys::GPU_CONSTANT,
228    Previous = ctru_sys::GPU_PREVIOUS,
229}
230
231impl Source {
232    pub const fn corresponding_index(&self) -> Option<texture::Index> {
233        match self {
234            Source::Texture0 => Some(texture::Index::Texture0),
235            Source::Texture1 => Some(texture::Index::Texture1),
236            Source::Texture2 => Some(texture::Index::Texture2),
237            Source::Texture3 => Some(texture::Index::Texture3),
238            _ => None,
239        }
240    }
241}
242
243/// The combination function to apply to the [`TexEnv`] operands.
244#[doc(alias = "GPU_COMBINEFUNC")]
245#[allow(missing_docs)]
246#[derive(Debug, Clone, Copy)]
247#[repr(u8)]
248#[non_exhaustive]
249pub enum CombineFunc {
250    Replace = ctru_sys::GPU_REPLACE,
251    Modulate = ctru_sys::GPU_MODULATE,
252    Add = ctru_sys::GPU_ADD,
253    AddSigned = ctru_sys::GPU_ADD_SIGNED,
254    Interpolate = ctru_sys::GPU_INTERPOLATE,
255    Subtract = ctru_sys::GPU_SUBTRACT,
256    Dot3Rgb = ctru_sys::GPU_DOT3_RGB,
257    // Added in libcrtu 2.3.0:
258    // Dot3Rgba = ctru_sys::GPU_DOT3_RGBA,
259}
260
261/// The RGB combiner operands.
262#[doc(alias = "GPU_TEVOP_RGB")]
263#[allow(missing_docs)]
264#[derive(Debug, Clone, Copy, Default)]
265#[repr(u8)]
266#[non_exhaustive]
267pub enum RGBOp {
268    #[default]
269    SrcColor = ctru_sys::GPU_TEVOP_RGB_SRC_COLOR,
270    OneMinusSrcColor = ctru_sys::GPU_TEVOP_RGB_ONE_MINUS_SRC_COLOR,
271    SrcAlpha = ctru_sys::GPU_TEVOP_RGB_SRC_ALPHA,
272    OneMinusSrcAlpha = ctru_sys::GPU_TEVOP_RGB_ONE_MINUS_SRC_ALPHA,
273    SrcRed = ctru_sys::GPU_TEVOP_RGB_SRC_R,
274    OneMinusSrcRed = ctru_sys::GPU_TEVOP_RGB_ONE_MINUS_SRC_R,
275    SrcGreen = ctru_sys::GPU_TEVOP_RGB_SRC_G,
276    OneMinusSrcGreen = ctru_sys::GPU_TEVOP_RGB_ONE_MINUS_SRC_G,
277    SrcBlue = ctru_sys::GPU_TEVOP_RGB_SRC_B,
278    OneMinusSrcBlue = ctru_sys::GPU_TEVOP_RGB_ONE_MINUS_SRC_B,
279}
280
281/// The Alpha combiner operands.
282#[doc(alias = "GPU_TEVOP_RGB")]
283#[allow(missing_docs)]
284#[derive(Debug, Clone, Copy, Default)]
285#[repr(u8)]
286#[non_exhaustive]
287pub enum AlphaOp {
288    #[default]
289    SrcAlpha = ctru_sys::GPU_TEVOP_A_SRC_ALPHA,
290    OneMinusSrcAlpha = ctru_sys::GPU_TEVOP_A_ONE_MINUS_SRC_ALPHA,
291    SrcRed = ctru_sys::GPU_TEVOP_A_SRC_R,
292    OneMinusSrcRed = ctru_sys::GPU_TEVOP_A_ONE_MINUS_SRC_R,
293    SrcGreen = ctru_sys::GPU_TEVOP_A_SRC_G,
294    OneMinusSrcGreen = ctru_sys::GPU_TEVOP_A_ONE_MINUS_SRC_G,
295    SrcBlue = ctru_sys::GPU_TEVOP_A_SRC_B,
296    OneMinusSrcBlue = ctru_sys::GPU_TEVOP_A_ONE_MINUS_SRC_B,
297}
298
299#[doc(alias = "GPU_TEVSCALE")]
300#[allow(missing_docs)]
301#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
302#[repr(u8)]
303pub enum Scale {
304    #[default]
305    X1 = ctru_sys::GPU_TEVSCALE_1,
306    X2 = ctru_sys::GPU_TEVSCALE_2,
307    X4 = ctru_sys::GPU_TEVSCALE_4,
308}