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. 147
      citro3d/src/math.rs

63
citro3d/examples/triangle.rs

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
#![feature(allocator_api)]
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::{attrib, buffer, shader, AspectRatio};
use ctru::prelude::*;
@ -117,8 +117,8 @@ fn main() { @@ -117,8 +117,8 @@ fn main() {
};
let Projections {
left,
right,
left_eye: left,
right_eye: right,
center,
} = calculate_projections();
@ -158,8 +158,8 @@ where @@ -158,8 +158,8 @@ where
}
struct Projections {
left: Matrix,
right: Matrix,
left_eye: Matrix,
right_eye: Matrix,
center: Matrix,
}
@ -167,44 +167,51 @@ fn calculate_projections() -> Projections { @@ -167,44 +167,51 @@ fn calculate_projections() -> Projections {
// TODO: it would be cool to allow playing around with these parameters on
// the fly with D-pad, etc.
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 far = 100.0;
let fov_y = 40.0_f32.to_radians();
let screen = 2.0;
let vertical_fov = 40.0_f32.to_radians();
let screen_depth = 2.0;
let left_eye = Matrix::perspective_stereo_tilt(
fov_y,
let clip_plane = ClipPlane {
near: 0.01,
far: 100.0,
};
let stereoscopic = Stereoscopic::Stereo {
interocular_distance,
screen_depth,
};
let left_eye = Matrix::perspective_projection(
vertical_fov,
AspectRatio::TopScreen,
near,
far,
-iod,
screen,
Orientation::Natural,
clip_plane,
stereoscopic,
CoordinateSystem::LeftHanded,
);
let right_eye = Matrix::perspective_stereo_tilt(
fov_y,
let right_eye = Matrix::perspective_projection(
vertical_fov,
AspectRatio::TopScreen,
near,
far,
iod,
screen,
Orientation::Natural,
clip_plane,
stereoscopic.invert(),
CoordinateSystem::LeftHanded,
);
let center = Matrix::perspective_tilt(
fov_y,
let center = Matrix::perspective_projection(
vertical_fov,
AspectRatio::BottomScreen,
near,
far,
Orientation::Natural,
clip_plane,
Stereoscopic::Mono,
CoordinateSystem::LeftHanded,
);
Projections {
left: left_eye,
right: right_eye,
left_eye,
right_eye,
center,
}
}

147
citro3d/src/math.rs

@ -16,67 +16,61 @@ pub struct Quaternion(citro3d_sys::C3D_FQuat); @@ -16,67 +16,61 @@ 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,
// TODO: does it make sense to have a helper that builds both left and right
// eyes for stereoscopic at the same time?
/// Construct a projection matrix suitable for projecting 3D world space onto
/// the 3DS screens.
pub fn perspective_projection(
vertical_fov: f32,
aspect_ratio: AspectRatio,
near: f32,
far: f32,
interocular_distance: f32,
/* better name ?? */ screen_depth: f32,
orientation: Orientation,
clip_plane: ClipPlane,
stereo: Stereoscopic,
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,
let (make_mtx_persp, make_mtx_stereo);
let initialize_mtx: &dyn Fn(_, _, _, _, _, _) -> _ = match stereo {
Stereoscopic::Mono => {
let make_mtx = match orientation {
Orientation::Natural => citro3d_sys::Mtx_PerspTilt,
Orientation::HardwareDefault => citro3d_sys::Mtx_Persp,
};
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,
screen_depth,
matches!(coordinates, CoordinateSystem::LeftHanded),
);
result.assume_init()
} => {
let make_mtx = match orientation {
Orientation::Natural => citro3d_sys::Mtx_PerspStereoTilt,
Orientation::HardwareDefault => citro3d_sys::Mtx_PerspStereo,
};
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
}
};
Self(inner)
}
let left_handed = matches!(coordinates, CoordinateSystem::LeftHanded);
pub fn perspective_tilt(
fov_y: f32,
aspect_ratio: AspectRatio,
near: f32,
far: f32,
coordinates: CoordinateSystem,
) -> Self {
let mut result = MaybeUninit::uninit();
initialize_mtx(
result.as_mut_ptr(),
vertical_fov,
aspect_ratio.into(),
clip_plane.near,
clip_plane.far,
left_handed,
);
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()
};
let inner = unsafe { result.assume_init() };
Self(inner)
}
@ -85,3 +79,56 @@ impl Matrix { @@ -85,3 +79,56 @@ impl Matrix {
&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