1use bitflags::bitflags;
5
6use crate::texture;
7
8pub(crate) const TEXENV_COUNT: usize = 6;
10
11#[derive(Clone, Copy)]
14#[doc(alias = "C3D_TexEnv")]
15pub struct TexEnv {
16 inner: citro3d_sys::C3D_TexEnv,
17
18 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 #[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 #[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 #[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#[derive(Clone, Copy)]
173pub struct Sources {
174 pub source0: Source,
176 pub source1: Option<Source>,
178 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 #[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#[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#[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 }
260
261#[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#[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}