From d2b65c8ff1b9a4e74b4940d7f7f32f66f50c4b13 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Fri, 24 Nov 2023 22:44:46 -0500 Subject: [PATCH 1/6] Add safe wrapper for C3D_BindProgram --- citro3d/examples/triangle.rs | 9 ++++----- citro3d/src/lib.rs | 12 +++++++++++- citro3d/src/shader.rs | 8 ++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index 9412e59..a6a5152 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -79,7 +79,8 @@ fn main() { let shader = shader::Library::from_bytes(SHADER_BYTES).unwrap(); let vertex_shader = shader.get(0).unwrap(); - let mut program = shader::Program::new(vertex_shader).unwrap(); + let program = shader::Program::new(vertex_shader).unwrap(); + instance.bind_program(&program); let mut vbo_data = Vec::with_capacity_in(VERTICES.len(), ctru::linear::LinearAllocator); vbo_data.extend_from_slice(VERTICES); @@ -87,7 +88,7 @@ fn main() { let mut buf_info = buffer::Info::new(); let (attr_info, vbo_idx) = prepare_vbos(&mut buf_info, &vbo_data); - scene_init(&mut program); + scene_init(); let projection_uniform_idx = program.get_uniform("projection").unwrap(); @@ -191,11 +192,9 @@ fn calculate_projections() -> Projections { } } -fn scene_init(program: &mut shader::Program) { +fn scene_init() { // Load the vertex shader, create a shader program and bind it unsafe { - citro3d_sys::C3D_BindProgram(program.as_raw_mut()); - // 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 let env = citro3d_sys::C3D_GetTexEnv(0); diff --git a/citro3d/src/lib.rs b/citro3d/src/lib.rs index a100e31..9368b5c 100644 --- a/citro3d/src/lib.rs +++ b/citro3d/src/lib.rs @@ -73,7 +73,8 @@ impl Instance { } /// Render a frame. The passed in function/closure can mutate the instance, - /// such as to [select a render target](Self::select_render_target). + /// such as to [select a render target](Self::select_render_target) + /// or [bind a new shader program](Self::bind_program). #[doc(alias = "C3D_FrameBegin")] #[doc(alias = "C3D_FrameEnd")] pub fn render_frame_with(&mut self, f: impl FnOnce(&mut Self)) { @@ -139,6 +140,15 @@ impl Instance { } } + /// Use the given [`shader::Program`] for subsequent draw calls. + pub fn bind_program(&mut self, program: &shader::Program) { + // SAFETY: AFAICT C3D_BindProgram just copies pointers from the given program, + // instead of mutating the pointee in any way that would cause UB + unsafe { + citro3d_sys::C3D_BindProgram(program.as_raw().cast_mut()); + } + } + /// Bind a uniform to the given `index` in the vertex shader for the next draw call. /// /// # Example diff --git a/citro3d/src/shader.rs b/citro3d/src/shader.rs index c2042bf..9f1de0b 100644 --- a/citro3d/src/shader.rs +++ b/citro3d/src/shader.rs @@ -17,6 +17,7 @@ use crate::uniform; /// /// The PICA200 does not support user-programmable fragment shaders. #[doc(alias = "shaderProgram_s")] +#[must_use] pub struct Program { program: ctru_sys::shaderProgram_s, } @@ -102,18 +103,13 @@ impl Program { pub(crate) fn as_raw(&self) -> *const ctru_sys::shaderProgram_s { &self.program } - - // TODO: pub(crate) - pub fn as_raw_mut(&mut self) -> *mut ctru_sys::shaderProgram_s { - &mut self.program - } } impl Drop for Program { #[doc(alias = "shaderProgramFree")] fn drop(&mut self) { unsafe { - let _ = ctru_sys::shaderProgramFree(self.as_raw_mut()); + let _ = ctru_sys::shaderProgramFree(self.as_raw().cast_mut()); } } } From 0e02f904424fbd7a8283083e57faf291e311790f Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sat, 25 Nov 2023 00:13:35 -0500 Subject: [PATCH 2/6] Initial texenv implementation --- citro3d-sys/bindgen.sh | 5 -- citro3d/examples/triangle.rs | 25 +++------ citro3d/src/lib.rs | 1 + citro3d/src/texenv.rs | 99 ++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 24 deletions(-) delete mode 100755 citro3d-sys/bindgen.sh create mode 100644 citro3d/src/texenv.rs diff --git a/citro3d-sys/bindgen.sh b/citro3d-sys/bindgen.sh deleted file mode 100755 index ebb4ff9..0000000 --- a/citro3d-sys/bindgen.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -cargo run --package bindgen-citro3d > src/bindings.rs diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index a6a5152..ca1358a 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -6,6 +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::{attrib, buffer, render, shader}; use ctru::prelude::*; use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D}; @@ -88,7 +89,11 @@ fn main() { let mut buf_info = buffer::Info::new(); let (attr_info, vbo_idx) = prepare_vbos(&mut buf_info, &vbo_data); - scene_init(); + // 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)) + .src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None) + .func(texenv::Mode::BOTH, CombineFunc::Replace); let projection_uniform_idx = program.get_uniform("projection").unwrap(); @@ -191,21 +196,3 @@ fn calculate_projections() -> Projections { center, } } - -fn scene_init() { - // Load the vertex shader, create a shader program and bind it - unsafe { - // 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 - let env = citro3d_sys::C3D_GetTexEnv(0); - citro3d_sys::C3D_TexEnvInit(env); - citro3d_sys::C3D_TexEnvSrc( - env, - citro3d_sys::C3D_Both, - ctru_sys::GPU_PRIMARY_COLOR, - 0, - 0, - ); - citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, ctru_sys::GPU_REPLACE); - } -} diff --git a/citro3d/src/lib.rs b/citro3d/src/lib.rs index 9368b5c..3cdd983 100644 --- a/citro3d/src/lib.rs +++ b/citro3d/src/lib.rs @@ -15,6 +15,7 @@ pub mod error; pub mod math; pub mod render; pub mod shader; +pub mod texenv; pub mod uniform; pub use error::{Error, Result}; diff --git a/citro3d/src/texenv.rs b/citro3d/src/texenv.rs new file mode 100644 index 0000000..fc5798a --- /dev/null +++ b/citro3d/src/texenv.rs @@ -0,0 +1,99 @@ +//! Texture environment support. See `` for more information. + +use bitflags::bitflags; + +use crate::Instance; + +#[doc(alias = "C3D_TexEnv")] +pub struct TexEnv<'a> { + 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); + } + } + + pub fn get(instance: &'a mut Instance, id: Id) -> Self { + unsafe { + Self { + raw: citro3d_sys::C3D_GetTexEnv(id.0), + _instance: instance, + } + } + } + + pub fn src( + &mut self, + mode: Mode, + s1: Source, + s2: Option, + s3: Option, + ) -> &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 _, + ) + } + self + } + + pub fn func(&mut self, mode: Mode, func: CombineFunc) -> &mut Self { + unsafe { + citro3d_sys::C3D_TexEnvFunc(self.raw, mode.bits(), func as _); + } + + self + } +} + +bitflags! { + #[doc(alias = "C3D_TexEnvMode")] + pub struct Mode: citro3d_sys::C3D_TexEnvMode { + const RGB = citro3d_sys::C3D_RGB; + const ALPHA = citro3d_sys::C3D_Alpha; + const BOTH = citro3d_sys::C3D_Both; + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +#[doc(alias = "GPU_TEVSRC")] +pub enum Source { + PrimaryColor = ctru_sys::GPU_PRIMARY_COLOR, + FragmentPrimaryColor = ctru_sys::GPU_FRAGMENT_PRIMARY_COLOR, + FragmentSecondaryColor = ctru_sys::GPU_FRAGMENT_SECONDARY_COLOR, + Texture0 = ctru_sys::GPU_TEXTURE0, + Texture1 = ctru_sys::GPU_TEXTURE1, + Texture2 = ctru_sys::GPU_TEXTURE2, + Texture3 = ctru_sys::GPU_TEXTURE3, + PreviousBuffer = ctru_sys::GPU_PREVIOUS_BUFFER, + Constant = ctru_sys::GPU_CONSTANT, + Previous = ctru_sys::GPU_PREVIOUS, +} + +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +#[doc(alias = "GPU_COMBINEFUNC")] +pub enum CombineFunc { + Replace = ctru_sys::GPU_REPLACE, + Modulate = ctru_sys::GPU_MODULATE, + Add = ctru_sys::GPU_ADD, + AddSigned = ctru_sys::GPU_ADD_SIGNED, + Interpolate = ctru_sys::GPU_INTERPOLATE, + Subtract = ctru_sys::GPU_SUBTRACT, + Dot3Rgb = ctru_sys::GPU_DOT3_RGB, + Dot3Rgba = ctru_sys::GPU_DOT3_RGBA, +} + +#[derive(Copy, Clone, Debug)] +pub struct Id(/* TODO maybe non pub? idk */ pub libc::c_int); From c47c5a93eb6e4bbd1ef00100dd5caa880322be97 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sat, 25 Nov 2023 22:50:36 -0500 Subject: [PATCH 3/6] Clean up texenv API a bit and add some docs --- citro3d/examples/triangle.rs | 7 +-- citro3d/src/lib.rs | 48 ++++++++++++++++++-- citro3d/src/texenv.rs | 86 +++++++++++++++++++++++++----------- 3 files changed, 110 insertions(+), 31 deletions(-) diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index ca1358a..4cc50b9 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -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}; @@ -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(); diff --git a/citro3d/src/lib.rs b/citro3d/src/lib.rs index 3cdd983..797d62f 100644 --- a/citro3d/src/lib.rs +++ b/citro3d/src/lib.rs @@ -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 { @@ -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_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. @@ -52,7 +63,17 @@ impl Instance { #[doc(alias = "C3D_Init")] pub fn with_cmdbuf_size(size: usize) -> Result { 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) } @@ -185,6 +206,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 { diff --git a/citro3d/src/texenv.rs b/citro3d/src/texenv.rs index fc5798a..222a645 100644 --- a/citro3d/src/texenv.rs +++ b/citro3d/src/texenv.rs @@ -1,52 +1,70 @@ -//! Texture environment support. See `` for more information. +//! Texture combiner support. See +//! 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, - s3: Option, + source0: Source, + source1: Option, + source2: Option, ) -> &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 _); @@ -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, @@ -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, @@ -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 { + (index < 6).then_some(Self(index)) + } +} From 30964ba57a09825ee4e0eee194f2f562cfab53bd Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sun, 26 Nov 2023 21:12:23 -0500 Subject: [PATCH 4/6] Build fix for libctru 2.2.x --- citro3d/src/texenv.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/citro3d/src/texenv.rs b/citro3d/src/texenv.rs index 222a645..d53e1bf 100644 --- a/citro3d/src/texenv.rs +++ b/citro3d/src/texenv.rs @@ -88,10 +88,11 @@ bitflags! { } /// A source operand of a [`TexEnv`]'s texture combination. -#[derive(Debug, Clone, Copy)] -#[repr(u32)] #[doc(alias = "GPU_TEVSRC")] #[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +#[non_exhaustive] pub enum Source { PrimaryColor = ctru_sys::GPU_PRIMARY_COLOR, FragmentPrimaryColor = ctru_sys::GPU_FRAGMENT_PRIMARY_COLOR, @@ -106,10 +107,11 @@ pub enum Source { } /// The combination function to apply to the [`TexEnv`] operands. -#[derive(Debug, Clone, Copy)] -#[repr(u32)] #[doc(alias = "GPU_COMBINEFUNC")] #[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +#[non_exhaustive] pub enum CombineFunc { Replace = ctru_sys::GPU_REPLACE, Modulate = ctru_sys::GPU_MODULATE, @@ -118,7 +120,8 @@ pub enum CombineFunc { Interpolate = ctru_sys::GPU_INTERPOLATE, Subtract = ctru_sys::GPU_SUBTRACT, Dot3Rgb = ctru_sys::GPU_DOT3_RGB, - Dot3Rgba = ctru_sys::GPU_DOT3_RGBA, + // Added in libcrtu 2.3.0: + // Dot3Rgba = ctru_sys::GPU_DOT3_RGBA, } /// A texture combination stage identifier. This index doubles as the order From 0fbd4418d8f39d495a6d0622a8fd67f15369a198 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Thu, 30 Nov 2023 08:31:51 -0500 Subject: [PATCH 5/6] Minor cleanups --- citro3d/examples/triangle.rs | 26 +++++++++++--------------- citro3d/src/lib.rs | 8 ++++---- citro3d/src/texenv.rs | 16 +++++----------- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index 4cc50b9..7673670 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -48,6 +48,7 @@ static VERTICES: &[Vertex] = &[ ]; static SHADER_BYTES: &[u8] = include_shader!("assets/vshader.pica"); +const CLEAR_COLOR: u32 = 0x68_B0_D8_FF; fn main() { let mut soc = Soc::new().expect("failed to get SOC"); @@ -87,12 +88,13 @@ fn main() { vbo_data.extend_from_slice(VERTICES); let mut buf_info = buffer::Info::new(); - let (attr_info, vbo_idx) = prepare_vbos(&mut buf_info, &vbo_data); + let (attr_info, vbo_data) = prepare_vbos(&mut buf_info, &vbo_data); // 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 + let stage0 = texenv::Stage::new(0).unwrap(); instance - .texenv(texenv::Stage::new(0).unwrap()) + .texenv(stage0) .src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None) .func(texenv::Mode::BOTH, texenv::CombineFunc::Replace); @@ -107,18 +109,17 @@ fn main() { instance.render_frame_with(|instance| { let mut render_to = |target: &mut render::Target, projection| { + target.clear(ClearFlags::ALL, CLEAR_COLOR, 0); + instance .select_render_target(target) .expect("failed to set render target"); - let clear_color: u32 = 0x7F_7F_7F_FF; - target.clear(ClearFlags::ALL, clear_color, 0); - instance.bind_vertex_uniform(projection_uniform_idx, projection); instance.set_attr_info(&attr_info); - instance.draw_arrays(buffer::Primitive::Triangles, vbo_idx); + instance.draw_arrays(buffer::Primitive::Triangles, vbo_data); }; let Projections { @@ -134,15 +135,10 @@ fn main() { } } -// sheeeesh, this sucks to type: -fn prepare_vbos<'buf, 'info, 'vbo>( - buf_info: &'info mut buffer::Info, - vbo_data: &'vbo [Vertex], -) -> (attrib::Info, buffer::Slice<'buf>) -where - 'info: 'buf, - 'vbo: 'buf, -{ +fn prepare_vbos<'a>( + buf_info: &'a mut buffer::Info, + vbo_data: &'a [Vertex], +) -> (attrib::Info, buffer::Slice<'a>) { // Configure attributes for use with the vertex shader let mut attr_info = attrib::Info::new(); diff --git a/citro3d/src/lib.rs b/citro3d/src/lib.rs index 797d62f..c18e6fc 100644 --- a/citro3d/src/lib.rs +++ b/citro3d/src/lib.rs @@ -148,16 +148,16 @@ impl Instance { /// Render primitives from the current vertex array buffer. #[doc(alias = "C3D_DrawArrays")] - pub fn draw_arrays(&mut self, primitive: buffer::Primitive, index: buffer::Slice) { - self.set_buffer_info(index.info()); + pub fn draw_arrays(&mut self, primitive: buffer::Primitive, vbo_data: buffer::Slice) { + self.set_buffer_info(vbo_data.info()); // TODO: should we also require the attrib info directly here? unsafe { citro3d_sys::C3D_DrawArrays( primitive as ctru_sys::GPU_Primitive_t, - index.index(), - index.len(), + vbo_data.index(), + vbo_data.len(), ); } } diff --git a/citro3d/src/texenv.rs b/citro3d/src/texenv.rs index d53e1bf..055366d 100644 --- a/citro3d/src/texenv.rs +++ b/citro3d/src/texenv.rs @@ -6,20 +6,14 @@ use bitflags::bitflags; /// 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 { - raw: *mut citro3d_sys::C3D_TexEnv, -} +pub struct TexEnv(*mut citro3d_sys::C3D_TexEnv); // https://oreo639.github.io/citro3d/texenv_8h.html#a9eda91f8e7252c91f873b1d43e3728b6 pub(crate) const TEXENV_COUNT: usize = 6; impl TexEnv { pub(crate) fn new(stage: Stage) -> Self { - let mut result = unsafe { - Self { - raw: citro3d_sys::C3D_GetTexEnv(stage.0 as _), - } - }; + let mut result = unsafe { Self(citro3d_sys::C3D_GetTexEnv(stage.0 as _)) }; result.reset(); result } @@ -27,7 +21,7 @@ impl TexEnv { /// Re-initialize the texture combiner to its default state. pub fn reset(&mut self) { unsafe { - citro3d_sys::C3D_TexEnvInit(self.raw); + citro3d_sys::C3D_TexEnvInit(self.0); } } @@ -48,7 +42,7 @@ impl TexEnv { ) -> &mut Self { unsafe { citro3d_sys::C3D_TexEnvSrc( - self.raw, + self.0, mode.bits(), source0 as _, source1.unwrap_or(Source::PrimaryColor) as _, @@ -67,7 +61,7 @@ impl TexEnv { #[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 _); + citro3d_sys::C3D_TexEnvFunc(self.0, mode.bits(), func as _); } self From 31272895d7bf45e0673af086536933ced157f76b Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Thu, 30 Nov 2023 09:15:53 -0500 Subject: [PATCH 6/6] Fixup right-handed coordinates and projection API Since it's a builder, we should really consume self and return Self instead of using &mut self. This makes it easier to convert into the final matrix at the end of building. --- citro3d/examples/triangle.rs | 6 +++--- citro3d/src/math/projection.rs | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index 7673670..305a9f8 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -34,15 +34,15 @@ struct Vertex { static VERTICES: &[Vertex] = &[ Vertex { - pos: Vec3::new(0.0, 0.5, 3.0), + pos: Vec3::new(0.0, 0.5, -3.0), color: Vec3::new(1.0, 0.0, 0.0), }, Vertex { - pos: Vec3::new(-0.5, -0.5, 3.0), + pos: Vec3::new(-0.5, -0.5, -3.0), color: Vec3::new(0.0, 1.0, 0.0), }, Vertex { - pos: Vec3::new(0.5, -0.5, 3.0), + pos: Vec3::new(0.5, -0.5, -3.0), color: Vec3::new(0.0, 0.0, 1.0), }, ]; diff --git a/citro3d/src/math/projection.rs b/citro3d/src/math/projection.rs index 0ec72e5..f694ad6 100644 --- a/citro3d/src/math/projection.rs +++ b/citro3d/src/math/projection.rs @@ -26,14 +26,42 @@ impl Projection { /// Set the coordinate system's orientation for the projection. /// See [`CoordinateOrientation`] for more details. - pub fn coordinates(&mut self, orientation: CoordinateOrientation) -> &mut Self { + /// + /// # Example + /// + /// ``` + /// # let _runner = test_runner::GdbRunner::default(); + /// # use citro3d::math::{Projection, AspectRatio, CoordinateOrientation, Matrix4, ClipPlanes}; + /// let clip_planes = ClipPlanes { + /// near: 0.1, + /// far: 100.0, + /// }; + /// let mtx: Matrix4 = Projection::perspective(40.0, AspectRatio::TopScreen, clip_planes) + /// .coordinates(CoordinateOrientation::LeftHanded) + /// .into(); + /// ``` + pub fn coordinates(mut self, orientation: CoordinateOrientation) -> Self { self.coordinates = orientation; self } /// Set the screen rotation for the projection. /// See [`ScreenOrientation`] for more details. - pub fn screen(&mut self, orientation: ScreenOrientation) -> &mut Self { + /// + /// # Example + /// + /// ``` + /// # let _runner = test_runner::GdbRunner::default(); + /// # use citro3d::math::{Projection, AspectRatio, ScreenOrientation, Matrix4, ClipPlanes}; + /// let clip_planes = ClipPlanes { + /// near: 0.1, + /// far: 100.0, + /// }; + /// let mtx: Matrix4 = Projection::perspective(40.0, AspectRatio::TopScreen, clip_planes) + /// .screen(ScreenOrientation::None) + /// .into(); + /// ``` + pub fn screen(mut self, orientation: ScreenOrientation) -> Self { self.rotation = orientation; self }