Browse Source

Clean up texenv API a bit and add some docs

pull/33/head
Ian Chamberlain 1 year ago
parent
commit
c47c5a93eb
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 7
      citro3d/examples/triangle.rs
  2. 48
      citro3d/src/lib.rs
  3. 86
      citro3d/src/texenv.rs

7
citro3d/examples/triangle.rs

@ -6,7 +6,7 @@
use citro3d::macros::include_shader; use citro3d::macros::include_shader;
use citro3d::math::{AspectRatio, ClipPlanes, Matrix4, Projection, StereoDisplacement}; use citro3d::math::{AspectRatio, ClipPlanes, Matrix4, Projection, StereoDisplacement};
use citro3d::render::ClearFlags; use citro3d::render::ClearFlags;
use citro3d::texenv::{self, CombineFunc, TexEnv}; use citro3d::texenv;
use citro3d::{attrib, buffer, render, shader}; use citro3d::{attrib, buffer, render, shader};
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D}; use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D};
@ -91,9 +91,10 @@ fn main() {
// Configure the first fragment shading substage to just pass through the vertex color // Configure the first fragment shading substage to just pass through the vertex color
// See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight
TexEnv::get(&mut instance, texenv::Id(0)) instance
.texenv(texenv::Stage::new(0).unwrap())
.src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None) .src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None)
.func(texenv::Mode::BOTH, CombineFunc::Replace); .func(texenv::Mode::BOTH, texenv::CombineFunc::Replace);
let projection_uniform_idx = program.get_uniform("projection").unwrap(); let projection_uniform_idx = program.get_uniform("projection").unwrap();

48
citro3d/src/lib.rs

@ -18,8 +18,12 @@ pub mod shader;
pub mod texenv; pub mod texenv;
pub mod uniform; pub mod uniform;
use std::cell::OnceCell;
use std::fmt;
pub use error::{Error, Result}; pub use error::{Error, Result};
use self::texenv::TexEnv;
use self::uniform::Uniform; use self::uniform::Uniform;
pub mod macros { pub mod macros {
@ -31,8 +35,15 @@ pub mod macros {
/// should instantiate to use this library. /// should instantiate to use this library.
#[non_exhaustive] #[non_exhaustive]
#[must_use] #[must_use]
#[derive(Debug)] pub struct Instance {
pub struct Instance; texenvs: [OnceCell<TexEnv>; texenv::TEXENV_COUNT],
}
impl fmt::Debug for Instance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Instance").finish_non_exhaustive()
}
}
impl Instance { impl Instance {
/// Initialize the default `citro3d` instance. /// Initialize the default `citro3d` instance.
@ -52,7 +63,17 @@ impl Instance {
#[doc(alias = "C3D_Init")] #[doc(alias = "C3D_Init")]
pub fn with_cmdbuf_size(size: usize) -> Result<Self> { pub fn with_cmdbuf_size(size: usize) -> Result<Self> {
if unsafe { citro3d_sys::C3D_Init(size) } { if unsafe { citro3d_sys::C3D_Init(size) } {
Ok(Self) Ok(Self {
texenvs: [
// thank goodness there's only six of them!
OnceCell::new(),
OnceCell::new(),
OnceCell::new(),
OnceCell::new(),
OnceCell::new(),
OnceCell::new(),
],
})
} else { } else {
Err(Error::FailedToInitialize) Err(Error::FailedToInitialize)
} }
@ -185,6 +206,27 @@ impl Instance {
pub fn bind_geometry_uniform(&mut self, index: uniform::Index, uniform: impl Uniform) { pub fn bind_geometry_uniform(&mut self, index: uniform::Index, uniform: impl Uniform) {
uniform.bind(self, shader::Type::Geometry, index); uniform.bind(self, shader::Type::Geometry, index);
} }
/// Retrieve the [`TexEnv`] for the given stage, initializing it first if necessary.
///
/// # Example
///
/// ```
/// # use citro3d::texenv;
/// # let _runner = test_runner::GdbRunner::default();
/// # let mut instance = citro3d::Instance::new().unwrap();
/// let stage0 = texenv::Stage::new(0).unwrap();
/// let texenv0 = instance.texenv(stage0);
/// ```
#[doc(alias = "C3D_GetTexEnv")]
#[doc(alias = "C3D_TexEnvInit")]
pub fn texenv(&mut self, stage: texenv::Stage) -> &mut texenv::TexEnv {
let texenv = &mut self.texenvs[stage.0];
texenv.get_or_init(|| TexEnv::new(stage));
// We have to do this weird unwrap to get a mutable reference,
// since there is no `get_mut_or_init` or equivalent
texenv.get_mut().unwrap()
}
} }
impl Drop for Instance { impl Drop for Instance {

86
citro3d/src/texenv.rs

@ -1,52 +1,70 @@
//! Texture environment support. See `<c3d/texenv.h>` for more information. //! Texture combiner support. See <https://www.khronos.org/opengl/wiki/Texture_Combiners>
//! for more details.
use bitflags::bitflags; use bitflags::bitflags;
use crate::Instance; /// A texture combiner, also called a "texture environment" (hence the struct name).
/// See also [`texenv.h` documentation](https://oreo639.github.io/citro3d/texenv_8h.html).
#[doc(alias = "C3D_TexEnv")] #[doc(alias = "C3D_TexEnv")]
pub struct TexEnv<'a> { pub struct TexEnv {
raw: *mut citro3d_sys::C3D_TexEnv, raw: *mut citro3d_sys::C3D_TexEnv,
_instance: &'a mut Instance,
} }
impl<'a> TexEnv<'a> { // https://oreo639.github.io/citro3d/texenv_8h.html#a9eda91f8e7252c91f873b1d43e3728b6
#[doc(alias = "C3D_TexEnvInit")] pub(crate) const TEXENV_COUNT: usize = 6;
pub fn set(&self, _instance: &mut Instance, id: Id) {
unsafe {
// SAFETY: pointee is only copied from, not modified
citro3d_sys::C3D_SetTexEnv(id.0, self.raw);
}
}
pub fn get(instance: &'a mut Instance, id: Id) -> Self { impl TexEnv {
unsafe { pub(crate) fn new(stage: Stage) -> Self {
let mut result = unsafe {
Self { Self {
raw: citro3d_sys::C3D_GetTexEnv(id.0), raw: citro3d_sys::C3D_GetTexEnv(stage.0 as _),
_instance: instance,
} }
};
result.reset();
result
}
/// Re-initialize the texture combiner to its default state.
pub fn reset(&mut self) {
unsafe {
citro3d_sys::C3D_TexEnvInit(self.raw);
} }
} }
/// Configure the source values of the texture combiner.
///
/// # Parameters
///
/// - `mode`: which [`Mode`]\(s) to set the sourc operand(s) for.
/// - `source0`: the first [`Source`] operand to the texture combiner
/// - `source1` and `source2`: optional additional [`Source`] operands to use
#[doc(alias = "C3D_TexEnvSrc")]
pub fn src( pub fn src(
&mut self, &mut self,
mode: Mode, mode: Mode,
s1: Source, source0: Source,
s2: Option<Source>, source1: Option<Source>,
s3: Option<Source>, source2: Option<Source>,
) -> &mut Self { ) -> &mut Self {
unsafe { unsafe {
citro3d_sys::C3D_TexEnvSrc( citro3d_sys::C3D_TexEnvSrc(
self.raw, self.raw,
mode.bits(), mode.bits(),
s1 as _, source0 as _,
s2.unwrap_or(Source::PrimaryColor) as _, source1.unwrap_or(Source::PrimaryColor) as _,
s3.unwrap_or(Source::PrimaryColor) as _, source2.unwrap_or(Source::PrimaryColor) as _,
) );
} }
self self
} }
/// Configure the texture combination function.
///
/// # Parameters
///
/// - `mode`: the [`Mode`]\(s) the combination function will apply to.
/// - `func`: the [`CombineFunc`] used to combine textures.
#[doc(alias = "C3D_TexEnvFunc")]
pub fn func(&mut self, mode: Mode, func: CombineFunc) -> &mut Self { pub fn func(&mut self, mode: Mode, func: CombineFunc) -> &mut Self {
unsafe { unsafe {
citro3d_sys::C3D_TexEnvFunc(self.raw, mode.bits(), func as _); citro3d_sys::C3D_TexEnvFunc(self.raw, mode.bits(), func as _);
@ -57,17 +75,23 @@ impl<'a> TexEnv<'a> {
} }
bitflags! { bitflags! {
/// Whether to operate on colors, alpha values, or both.
#[doc(alias = "C3D_TexEnvMode")] #[doc(alias = "C3D_TexEnvMode")]
pub struct Mode: citro3d_sys::C3D_TexEnvMode { pub struct Mode: citro3d_sys::C3D_TexEnvMode {
#[allow(missing_docs)]
const RGB = citro3d_sys::C3D_RGB; const RGB = citro3d_sys::C3D_RGB;
#[allow(missing_docs)]
const ALPHA = citro3d_sys::C3D_Alpha; const ALPHA = citro3d_sys::C3D_Alpha;
#[allow(missing_docs)]
const BOTH = citro3d_sys::C3D_Both; const BOTH = citro3d_sys::C3D_Both;
} }
} }
/// A source operand of a [`TexEnv`]'s texture combination.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[repr(u32)] #[repr(u32)]
#[doc(alias = "GPU_TEVSRC")] #[doc(alias = "GPU_TEVSRC")]
#[allow(missing_docs)]
pub enum Source { pub enum Source {
PrimaryColor = ctru_sys::GPU_PRIMARY_COLOR, PrimaryColor = ctru_sys::GPU_PRIMARY_COLOR,
FragmentPrimaryColor = ctru_sys::GPU_FRAGMENT_PRIMARY_COLOR, FragmentPrimaryColor = ctru_sys::GPU_FRAGMENT_PRIMARY_COLOR,
@ -81,9 +105,11 @@ pub enum Source {
Previous = ctru_sys::GPU_PREVIOUS, Previous = ctru_sys::GPU_PREVIOUS,
} }
/// The combination function to apply to the [`TexEnv`] operands.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[repr(u32)] #[repr(u32)]
#[doc(alias = "GPU_COMBINEFUNC")] #[doc(alias = "GPU_COMBINEFUNC")]
#[allow(missing_docs)]
pub enum CombineFunc { pub enum CombineFunc {
Replace = ctru_sys::GPU_REPLACE, Replace = ctru_sys::GPU_REPLACE,
Modulate = ctru_sys::GPU_MODULATE, Modulate = ctru_sys::GPU_MODULATE,
@ -95,5 +121,15 @@ pub enum CombineFunc {
Dot3Rgba = ctru_sys::GPU_DOT3_RGBA, Dot3Rgba = ctru_sys::GPU_DOT3_RGBA,
} }
/// A texture combination stage identifier. This index doubles as the order
/// in which texture combinations will be applied.
// (I think?)
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Id(/* TODO maybe non pub? idk */ pub libc::c_int); pub struct Stage(pub(crate) usize);
impl Stage {
/// Get a stage index. Valid indices range from 0 to 5.
pub fn new(index: usize) -> Option<Self> {
(index < 6).then_some(Self(index))
}
}

Loading…
Cancel
Save