Browse Source

Start porting <c3d/maths.h>, vectors and matrices

pull/27/head
Ian Chamberlain 1 year ago
parent
commit
2301e5022c
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 114
      citro3d/examples/triangle.rs
  2. 36
      citro3d/src/lib.rs
  3. 87
      citro3d/src/math.rs
  4. 27
      citro3d/src/shader.rs

114
citro3d/examples/triangle.rs

@ -3,13 +3,10 @@
#![feature(allocator_api)] #![feature(allocator_api)]
use std::ffi::CStr;
use std::mem::MaybeUninit;
use citro3d::macros::include_shader; use citro3d::macros::include_shader;
use citro3d::math::{CoordinateSystem, Matrix};
use citro3d::render::{self, ClearFlags}; use citro3d::render::{self, ClearFlags};
use citro3d::{attrib, buffer, shader}; use citro3d::{attrib, buffer, shader, AspectRatio};
use citro3d_sys::C3D_Mtx;
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D}; use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D};
@ -92,7 +89,10 @@ fn main() {
let mut buf_info = buffer::Info::new(); let mut buf_info = buffer::Info::new();
let (attr_info, vbo_idx) = prepare_vbos(&mut buf_info, &vbo_data); let (attr_info, vbo_idx) = prepare_vbos(&mut buf_info, &vbo_data);
let projection_uniform_idx = scene_init(&mut program); scene_init(&mut program);
let projection_uniform_idx = program.get_uniform_location("projection").unwrap();
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();
@ -109,14 +109,7 @@ fn main() {
let clear_color: u32 = 0x7F_7F_7F_FF; let clear_color: u32 = 0x7F_7F_7F_FF;
target.clear(ClearFlags::ALL, clear_color, 0); target.clear(ClearFlags::ALL, clear_color, 0);
unsafe { instance.update_vertex_uniform_mat4x4(projection_uniform_idx, projection);
// Update the uniforms
citro3d_sys::C3D_FVUnifMtx4x4(
ctru_sys::GPU_VERTEX_SHADER,
projection_uniform_idx.into(),
projection,
);
}
instance.set_attr_info(&attr_info); instance.set_attr_info(&attr_info);
@ -165,16 +158,12 @@ where
} }
struct Projections { struct Projections {
left: C3D_Mtx, left: Matrix,
right: C3D_Mtx, right: Matrix,
center: C3D_Mtx, center: Matrix,
} }
fn calculate_projections() -> Projections { fn calculate_projections() -> Projections {
let mut left_eye = MaybeUninit::uninit();
let mut right_eye = MaybeUninit::uninit();
let mut center = MaybeUninit::uninit();
// TODO: it would be cool to allow playing around with these parameters on // TODO: it would be cool to allow playing around with these parameters on
// the fly with D-pad, etc. // the fly with D-pad, etc.
let slider_val = unsafe { ctru_sys::osGet3DSliderState() }; let slider_val = unsafe { ctru_sys::osGet3DSliderState() };
@ -182,53 +171,48 @@ fn calculate_projections() -> Projections {
let near = 0.01; let near = 0.01;
let far = 100.0; let far = 100.0;
let fovy = 40.0_f32.to_radians(); let fov_y = 40.0_f32.to_radians();
let screen = 2.0; let screen = 2.0;
unsafe { let left_eye = Matrix::perspective_stereo_tilt(
citro3d_sys::Mtx_PerspStereoTilt( fov_y,
left_eye.as_mut_ptr(), AspectRatio::TopScreen,
fovy, near,
citro3d_sys::C3D_AspectRatioTop as f32, far,
near, -iod,
far, screen,
-iod, CoordinateSystem::LeftHanded,
screen, );
true,
); let right_eye = Matrix::perspective_stereo_tilt(
fov_y,
citro3d_sys::Mtx_PerspStereoTilt( AspectRatio::TopScreen,
right_eye.as_mut_ptr(), near,
fovy, far,
citro3d_sys::C3D_AspectRatioTop as f32, iod,
near, screen,
far, CoordinateSystem::LeftHanded,
iod, );
screen,
true, let center = Matrix::perspective_tilt(
); fov_y,
AspectRatio::BottomScreen,
citro3d_sys::Mtx_PerspTilt( near,
center.as_mut_ptr(), far,
fovy, CoordinateSystem::LeftHanded,
citro3d_sys::C3D_AspectRatioBot as f32, );
near,
far, Projections {
true, left: left_eye,
); right: right_eye,
center,
Projections {
left: left_eye.assume_init(),
right: right_eye.assume_init(),
center: center.assume_init(),
}
} }
} }
fn scene_init(program: &mut shader::Program) -> i8 { fn scene_init(program: &mut shader::Program) {
// Load the vertex shader, create a shader program and bind it // Load the vertex shader, create a shader program and bind it
unsafe { unsafe {
citro3d_sys::C3D_BindProgram(program.as_raw()); citro3d_sys::C3D_BindProgram(program.as_raw_mut());
// 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
@ -242,13 +226,5 @@ fn scene_init(program: &mut shader::Program) -> i8 {
0, 0,
); );
citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, ctru_sys::GPU_REPLACE); citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, ctru_sys::GPU_REPLACE);
// Get the location of the uniforms
let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap();
ctru_sys::shaderInstanceGetUniformLocation(
(*program.as_raw()).vertexShader,
projection_name.as_ptr(),
)
} }
} }

36
citro3d/src/lib.rs

@ -5,11 +5,12 @@
pub mod attrib; pub mod attrib;
pub mod buffer; pub mod buffer;
pub mod error; pub mod error;
pub mod math;
pub mod render; pub mod render;
pub mod shader; pub mod shader;
use citro3d_sys::C3D_FrameDrawOn;
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use math::Matrix;
pub mod macros { pub mod macros {
//! Helper macros for working with shaders. //! Helper macros for working with shaders.
@ -53,7 +54,7 @@ impl Instance {
/// Fails if the given target cannot be used for drawing. /// Fails if the given target cannot be used for drawing.
pub fn select_render_target(&mut self, target: &render::Target<'_>) -> Result<()> { pub fn select_render_target(&mut self, target: &render::Target<'_>) -> Result<()> {
let _ = self; let _ = self;
if unsafe { C3D_FrameDrawOn(target.as_raw()) } { if unsafe { citro3d_sys::C3D_FrameDrawOn(target.as_raw()) } {
Ok(()) Ok(())
} else { } else {
Err(Error::InvalidRenderTarget) Err(Error::InvalidRenderTarget)
@ -121,6 +122,19 @@ impl Instance {
); );
} }
} }
// TODO: need separate versions for vertex/geometry and different dimensions?
// Maybe we could do something nicer with const generics, or something, although
// it will probably be tricker
pub fn update_vertex_uniform_mat4x4(&mut self, index: i8, matrix: &Matrix) {
unsafe {
citro3d_sys::C3D_FVUnifMtx4x4(
ctru_sys::GPU_VERTEX_SHADER,
index.into(),
matrix.as_raw(),
)
}
}
} }
impl Drop for Instance { impl Drop for Instance {
@ -130,3 +144,21 @@ impl Drop for Instance {
} }
} }
} }
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum AspectRatio {
TopScreen,
BottomScreen,
Other(f32),
}
impl From<AspectRatio> for f32 {
fn from(ratio: AspectRatio) -> Self {
match ratio {
AspectRatio::TopScreen => citro3d_sys::C3D_AspectRatioTop as f32,
AspectRatio::BottomScreen => citro3d_sys::C3D_AspectRatioBot as f32,
AspectRatio::Other(ratio) => ratio,
}
}
}

87
citro3d/src/math.rs

@ -0,0 +1,87 @@
//! Safe wrappers for working with matrix and vector types provided by `citro3d`.
use std::mem::MaybeUninit;
use crate::AspectRatio;
/// A 4-vector of [`u8`]s.
pub struct IntVec(citro3d_sys::C3D_IVec);
/// A 4-vector of [`f32`]s.
pub struct FloatVec(citro3d_sys::C3D_FVec);
/// A quaternion, internally represented the same way as [`FVec`].
pub struct Quaternion(citro3d_sys::C3D_FQuat);
/// A 4x4 row-major matrix of [`f32`]s.
pub struct Matrix(citro3d_sys::C3D_Mtx);
/// Whether to use left-handed or right-handed coordinates for calculations.
#[derive(Clone, Copy, Debug)]
#[non_exhaustive] // This probably is exhaustive, but just in case
pub enum CoordinateSystem {
LeftHanded,
RightHanded,
}
impl Matrix {
// TODO: this could probably be generalized with something like builder or options
// pattern. Should look and see what the different citro3d implementations look like
pub fn perspective_stereo_tilt(
fov_y: f32,
aspect_ratio: AspectRatio,
near: f32,
far: f32,
interocular_distance: f32,
/* better name ?? */ screen_depth: f32,
coordinates: CoordinateSystem,
) -> Self {
let mut result = MaybeUninit::uninit();
let inner = unsafe {
citro3d_sys::Mtx_PerspStereoTilt(
result.as_mut_ptr(),
fov_y,
aspect_ratio.into(),
near,
far,
interocular_distance,
screen_depth,
matches!(coordinates, CoordinateSystem::LeftHanded),
);
result.assume_init()
};
Self(inner)
}
pub fn perspective_tilt(
fov_y: f32,
aspect_ratio: AspectRatio,
near: f32,
far: f32,
coordinates: CoordinateSystem,
) -> Self {
let mut result = MaybeUninit::uninit();
let inner = unsafe {
citro3d_sys::Mtx_PerspTilt(
result.as_mut_ptr(),
fov_y,
aspect_ratio.into(),
near,
far,
matches!(coordinates, CoordinateSystem::LeftHanded),
);
result.assume_init()
};
Self(inner)
}
pub(crate) fn as_raw(&self) -> *const citro3d_sys::C3D_Mtx {
&self.0
}
}

27
citro3d/src/shader.rs

@ -5,6 +5,7 @@
//! documentation for <https://github.com/devkitPro/picasso>. //! documentation for <https://github.com/devkitPro/picasso>.
use std::error::Error; use std::error::Error;
use std::ffi::CString;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
/// A PICA200 shader program. It may have one or both of: /// A PICA200 shader program. It may have one or both of:
@ -66,8 +67,30 @@ impl Program {
} }
} }
// TODO: newtype for index?
pub fn get_uniform_location(&self, name: &str) -> crate::Result<i8> {
let vertex_instance = unsafe { (*self.as_raw()).vertexShader };
if vertex_instance.is_null() {
return Err(todo!());
}
let name = CString::new(name).map_err(|e| -> crate::Error { todo!() })?;
let idx =
unsafe { ctru_sys::shaderInstanceGetUniformLocation(vertex_instance, name.as_ptr()) };
if idx < 0 {
Err(todo!())
} else {
Ok(idx)
}
}
// TODO: pub(crate) // TODO: pub(crate)
pub fn as_raw(&mut self) -> *mut ctru_sys::shaderProgram_s { pub fn as_raw(&self) -> *const ctru_sys::shaderProgram_s {
&self.program
}
pub fn as_raw_mut(&mut self) -> *mut ctru_sys::shaderProgram_s {
&mut self.program &mut self.program
} }
} }
@ -75,7 +98,7 @@ impl Program {
impl Drop for Program { impl Drop for Program {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
let _ = ctru_sys::shaderProgramFree(self.as_raw()); let _ = ctru_sys::shaderProgramFree(self.as_raw_mut());
} }
} }
} }

Loading…
Cancel
Save