Skip to content

Commit

Permalink
Clean up texenv API a bit and add some docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-h-chamberlain committed Nov 26, 2023
1 parent 78984de commit 47ebd1c
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 31 deletions.
7 changes: 4 additions & 3 deletions citro3d/examples/triangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use citro3d::macros::include_shader;
use citro3d::math::{AspectRatio, ClipPlanes, Matrix4, Projection, StereoDisplacement};
use citro3d::render::ClearFlags;
use citro3d::texenv::{self, CombineFunc, TexEnv};
use citro3d::texenv;
use citro3d::{attrib, buffer, render, shader};
use ctru::prelude::*;
use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D};
Expand Down Expand Up @@ -91,9 +91,10 @@ fn main() {

// 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
TexEnv::get(&mut instance, texenv::Id(0))
instance
.texenv(texenv::Stage::new(0).unwrap())
.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();

Expand Down
48 changes: 45 additions & 3 deletions citro3d/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ pub mod shader;
pub mod texenv;
pub mod uniform;

use std::cell::OnceCell;
use std::fmt;

pub use error::{Error, Result};

use self::texenv::TexEnv;
use self::uniform::Uniform;

pub mod macros {
Expand All @@ -31,8 +35,15 @@ pub mod macros {
/// should instantiate to use this library.
#[non_exhaustive]
#[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 {
/// Initialize the default `citro3d` instance.
Expand All @@ -51,7 +62,17 @@ impl Instance {
/// Fails if `citro3d` cannot be initialized.
pub fn with_cmdbuf_size(size: usize) -> Result<Self> {
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 {
Err(Error::FailedToInitialize)
}
Expand Down Expand Up @@ -178,6 +199,27 @@ impl Instance {
pub fn bind_geometry_uniform(&mut self, index: uniform::Index, uniform: impl Uniform) {
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 {
Expand Down
86 changes: 61 additions & 25 deletions citro3d/src/texenv.rs
Original file line number Diff line number Diff line change
@@ -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 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")]
pub struct TexEnv<'a> {
pub struct TexEnv {
raw: *mut citro3d_sys::C3D_TexEnv,
_instance: &'a mut Instance,
}

impl<'a> TexEnv<'a> {
#[doc(alias = "C3D_TexEnvInit")]
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);
}
}
// https://oreo639.github.io/citro3d/texenv_8h.html#a9eda91f8e7252c91f873b1d43e3728b6
pub(crate) const TEXENV_COUNT: usize = 6;

pub fn get(instance: &'a mut Instance, id: Id) -> Self {
unsafe {
impl TexEnv {
pub(crate) fn new(stage: Stage) -> Self {
let mut result = unsafe {
Self {
raw: citro3d_sys::C3D_GetTexEnv(id.0),
_instance: instance,
raw: citro3d_sys::C3D_GetTexEnv(stage.0 as _),
}
};
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(
&mut self,
mode: Mode,
s1: Source,
s2: Option<Source>,
s3: Option<Source>,
source0: Source,
source1: Option<Source>,
source2: Option<Source>,
) -> &mut Self {
unsafe {
citro3d_sys::C3D_TexEnvSrc(
self.raw,
mode.bits(),
s1 as _,
s2.unwrap_or(Source::PrimaryColor) as _,
s3.unwrap_or(Source::PrimaryColor) as _,
)
source0 as _,
source1.unwrap_or(Source::PrimaryColor) as _,
source2.unwrap_or(Source::PrimaryColor) as _,
);
}
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 {
unsafe {
citro3d_sys::C3D_TexEnvFunc(self.raw, mode.bits(), func as _);
Expand All @@ -57,17 +75,23 @@ impl<'a> TexEnv<'a> {
}

bitflags! {
/// Whether to operate on colors, alpha values, or both.
#[doc(alias = "C3D_TexEnvMode")]
pub struct Mode: citro3d_sys::C3D_TexEnvMode {
#[allow(missing_docs)]
const RGB = citro3d_sys::C3D_RGB;
#[allow(missing_docs)]
const ALPHA = citro3d_sys::C3D_Alpha;
#[allow(missing_docs)]
const BOTH = citro3d_sys::C3D_Both;
}
}

/// A source operand of a [`TexEnv`]'s texture combination.
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
#[doc(alias = "GPU_TEVSRC")]
#[allow(missing_docs)]
pub enum Source {
PrimaryColor = ctru_sys::GPU_PRIMARY_COLOR,
FragmentPrimaryColor = ctru_sys::GPU_FRAGMENT_PRIMARY_COLOR,
Expand All @@ -81,9 +105,11 @@ pub enum Source {
Previous = ctru_sys::GPU_PREVIOUS,
}

/// The combination function to apply to the [`TexEnv`] operands.
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
#[doc(alias = "GPU_COMBINEFUNC")]
#[allow(missing_docs)]
pub enum CombineFunc {
Replace = ctru_sys::GPU_REPLACE,
Modulate = ctru_sys::GPU_MODULATE,
Expand All @@ -95,5 +121,15 @@ pub enum CombineFunc {
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)]
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))
}
}

0 comments on commit 47ebd1c

Please sign in to comment.