Skip to content

Commit

Permalink
Merge pull request #33 from rust3ds/feature/texenv
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-h-chamberlain authored Dec 5, 2023
2 parents ffb8d70 + 3127289 commit b02384f
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 60 deletions.
5 changes: 0 additions & 5 deletions citro3d-sys/bindgen.sh

This file was deleted.

61 changes: 22 additions & 39 deletions citro3d/examples/triangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use citro3d::macros::include_shader;
use citro3d::math::{AspectRatio, ClipPlanes, Matrix4, Projection, StereoDisplacement};
use citro3d::render::ClearFlags;
use citro3d::texenv;
use citro3d::{attrib, buffer, render, shader};
use ctru::prelude::*;
use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D};
Expand Down Expand Up @@ -33,20 +34,21 @@ 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),
},
];

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");
Expand Down Expand Up @@ -79,15 +81,22 @@ 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);

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);

scene_init(&mut program);
// 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(stage0)
.src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None)
.func(texenv::Mode::BOTH, texenv::CombineFunc::Replace);

let projection_uniform_idx = program.get_uniform("projection").unwrap();

Expand All @@ -100,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 {
Expand All @@ -127,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();

Expand Down Expand Up @@ -190,23 +193,3 @@ fn calculate_projections() -> Projections {
center,
}
}

fn scene_init(program: &mut shader::Program) {
// 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);
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);
}
}
69 changes: 61 additions & 8 deletions citro3d/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ pub mod error;
pub mod math;
pub mod render;
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 @@ -30,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 +63,17 @@ impl Instance {
#[doc(alias = "C3D_Init")]
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 All @@ -73,7 +95,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)) {
Expand Down Expand Up @@ -125,20 +148,29 @@ 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(),
);
}
}

/// 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
Expand Down Expand Up @@ -174,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 {
Expand Down
32 changes: 30 additions & 2 deletions citro3d/src/math/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,42 @@ impl<Kind> Projection<Kind> {

/// 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
}
Expand Down
8 changes: 2 additions & 6 deletions citro3d/src/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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());
}
}
}
Expand Down
Loading

0 comments on commit b02384f

Please sign in to comment.