Browse Source

Consolidate matrix construction API

pull/27/head
Ian Chamberlain 1 year ago
parent
commit
974ee2b742
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 63
      citro3d/examples/triangle.rs
  2. 137
      citro3d/src/math.rs

63
citro3d/examples/triangle.rs

@ -4,7 +4,7 @@
#![feature(allocator_api)] #![feature(allocator_api)]
use citro3d::macros::include_shader; use citro3d::macros::include_shader;
use citro3d::math::{CoordinateSystem, Matrix}; use citro3d::math::{ClipPlane, CoordinateSystem, Matrix, Orientation, Stereoscopic};
use citro3d::render::{self, ClearFlags}; use citro3d::render::{self, ClearFlags};
use citro3d::{attrib, buffer, shader, AspectRatio}; use citro3d::{attrib, buffer, shader, AspectRatio};
use ctru::prelude::*; use ctru::prelude::*;
@ -117,8 +117,8 @@ fn main() {
}; };
let Projections { let Projections {
left, left_eye: left,
right, right_eye: right,
center, center,
} = calculate_projections(); } = calculate_projections();
@ -158,8 +158,8 @@ where
} }
struct Projections { struct Projections {
left: Matrix, left_eye: Matrix,
right: Matrix, right_eye: Matrix,
center: Matrix, center: Matrix,
} }
@ -167,44 +167,51 @@ fn calculate_projections() -> Projections {
// 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() };
let iod = slider_val / 4.0; let interocular_distance = slider_val / 4.0;
let near = 0.01; let vertical_fov = 40.0_f32.to_radians();
let far = 100.0; let screen_depth = 2.0;
let fov_y = 40.0_f32.to_radians();
let screen = 2.0;
let left_eye = Matrix::perspective_stereo_tilt( let clip_plane = ClipPlane {
fov_y, near: 0.01,
far: 100.0,
};
let stereoscopic = Stereoscopic::Stereo {
interocular_distance,
screen_depth,
};
let left_eye = Matrix::perspective_projection(
vertical_fov,
AspectRatio::TopScreen, AspectRatio::TopScreen,
near, Orientation::Natural,
far, clip_plane,
-iod, stereoscopic,
screen,
CoordinateSystem::LeftHanded, CoordinateSystem::LeftHanded,
); );
let right_eye = Matrix::perspective_stereo_tilt( let right_eye = Matrix::perspective_projection(
fov_y, vertical_fov,
AspectRatio::TopScreen, AspectRatio::TopScreen,
near, Orientation::Natural,
far, clip_plane,
iod, stereoscopic.invert(),
screen,
CoordinateSystem::LeftHanded, CoordinateSystem::LeftHanded,
); );
let center = Matrix::perspective_tilt( let center = Matrix::perspective_projection(
fov_y, vertical_fov,
AspectRatio::BottomScreen, AspectRatio::BottomScreen,
near, Orientation::Natural,
far, clip_plane,
Stereoscopic::Mono,
CoordinateSystem::LeftHanded, CoordinateSystem::LeftHanded,
); );
Projections { Projections {
left: left_eye, left_eye,
right: right_eye, right_eye,
center, center,
} }
} }

137
citro3d/src/math.rs

@ -16,67 +16,61 @@ pub struct Quaternion(citro3d_sys::C3D_FQuat);
/// A 4x4 row-major matrix of `f32`s. /// A 4x4 row-major matrix of `f32`s.
pub struct Matrix(citro3d_sys::C3D_Mtx); 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 { impl Matrix {
// TODO: this could probably be generalized with something like builder or options // TODO: does it make sense to have a helper that builds both left and right
// pattern. Should look and see what the different citro3d implementations look like // eyes for stereoscopic at the same time?
pub fn perspective_stereo_tilt(
fov_y: f32, /// Construct a projection matrix suitable for projecting 3D world space onto
/// the 3DS screens.
pub fn perspective_projection(
vertical_fov: f32,
aspect_ratio: AspectRatio, aspect_ratio: AspectRatio,
near: f32, orientation: Orientation,
far: f32, clip_plane: ClipPlane,
interocular_distance: f32, stereo: Stereoscopic,
/* better name ?? */ screen_depth: f32,
coordinates: CoordinateSystem, coordinates: CoordinateSystem,
) -> Self { ) -> Self {
let mut result = MaybeUninit::uninit(); let (make_mtx_persp, make_mtx_stereo);
let inner = unsafe { let initialize_mtx: &dyn Fn(_, _, _, _, _, _) -> _ = match stereo {
citro3d_sys::Mtx_PerspStereoTilt( Stereoscopic::Mono => {
result.as_mut_ptr(), let make_mtx = match orientation {
fov_y, Orientation::Natural => citro3d_sys::Mtx_PerspTilt,
aspect_ratio.into(), Orientation::HardwareDefault => citro3d_sys::Mtx_Persp,
near, };
far,
make_mtx_persp = move |a, b, c, d, e, f| unsafe { make_mtx(a, b, c, d, e, f) };
&make_mtx_persp
}
Stereoscopic::Stereo {
interocular_distance, interocular_distance,
screen_depth, screen_depth,
matches!(coordinates, CoordinateSystem::LeftHanded), } => {
); let make_mtx = match orientation {
Orientation::Natural => citro3d_sys::Mtx_PerspStereoTilt,
result.assume_init() Orientation::HardwareDefault => citro3d_sys::Mtx_PerspStereo,
}; };
Self(inner) make_mtx_stereo = move |a, b, c, d, e, f| unsafe {
make_mtx(a, b, c, d, interocular_distance, screen_depth, e, f)
};
&make_mtx_stereo
} }
};
pub fn perspective_tilt( let left_handed = matches!(coordinates, CoordinateSystem::LeftHanded);
fov_y: f32,
aspect_ratio: AspectRatio,
near: f32,
far: f32,
coordinates: CoordinateSystem,
) -> Self {
let mut result = MaybeUninit::uninit();
let inner = unsafe { let mut result = MaybeUninit::uninit();
citro3d_sys::Mtx_PerspTilt( initialize_mtx(
result.as_mut_ptr(), result.as_mut_ptr(),
fov_y, vertical_fov,
aspect_ratio.into(), aspect_ratio.into(),
near, clip_plane.near,
far, clip_plane.far,
matches!(coordinates, CoordinateSystem::LeftHanded), left_handed,
); );
result.assume_init() let inner = unsafe { result.assume_init() };
};
Self(inner) Self(inner)
} }
@ -85,3 +79,56 @@ impl Matrix {
&self.0 &self.0
} }
} }
/// Whether to use left-handed or right-handed coordinates for calculations.
#[derive(Clone, Copy, Debug)]
pub enum CoordinateSystem {
LeftHanded,
RightHanded,
}
/// Whether to rotate a projection to account for the 3DS screen configuration.
/// Both screens on the 3DS are oriented such that the "top" of the screen is
/// on the [left | right] ? side of the device when it's held normally, so
/// projections must account for this extra rotation to display in the correct
/// orientation.
#[derive(Clone, Copy, Debug)]
pub enum Orientation {
/// Rotate the projection 90° to account for the 3DS screen rotation.
Natural,
/// Don't rotate the projection at all.
HardwareDefault,
}
#[derive(Clone, Copy, Debug)]
// TODO: better name
pub enum Stereoscopic {
Mono,
Stereo {
interocular_distance: f32,
// TODO: better name? At least docstring
screen_depth: f32,
},
}
impl Stereoscopic {
/// Flip the stereoscopic projection for the opposite eye.
pub fn invert(self) -> Self {
match self {
Self::Stereo {
interocular_distance,
screen_depth,
} => Self::Stereo {
interocular_distance: -interocular_distance,
screen_depth,
},
mono => mono,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ClipPlane {
pub near: f32,
pub far: f32,
}

Loading…
Cancel
Save