From e47c4140699ea29152d63de90cbdcff1a3582c58 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sat, 17 Jun 2023 13:10:09 -0400 Subject: [PATCH] Use proper stereo perspective projection Read + use the OS 3D slider value when projecting left+right eyes, and use a mono perspective projection for the bottom screen. --- citro3d-sys/src/lib.rs | 2 + citro3d-sys/src/os.rs | 13 ++++ citro3d/examples/triangle.rs | 123 +++++++++++++++++++++++------------ citro3d/src/buffer.rs | 2 +- 4 files changed, 99 insertions(+), 41 deletions(-) create mode 100644 citro3d-sys/src/os.rs diff --git a/citro3d-sys/src/lib.rs b/citro3d-sys/src/lib.rs index 3426e83..48a46ec 100644 --- a/citro3d-sys/src/lib.rs +++ b/citro3d-sys/src/lib.rs @@ -7,6 +7,7 @@ pub mod base; pub mod gx; +pub mod os; pub mod renderqueue; pub mod texenv; pub mod uniforms; @@ -16,6 +17,7 @@ mod bindings; pub use base::*; pub use bindings::*; pub use gx::*; +pub use os::*; pub use renderqueue::*; pub use texenv::*; pub use uniforms::*; diff --git a/citro3d-sys/src/os.rs b/citro3d-sys/src/os.rs new file mode 100644 index 0000000..ed04411 --- /dev/null +++ b/citro3d-sys/src/os.rs @@ -0,0 +1,13 @@ +// TODO: move this to ctru-sys, maybe? +// would probably be auto-generated via https://github.com/rust3ds/ctru-rs/issues/123 + +use ctru_sys::{osSharedConfig_s, OS_SHAREDCFG_VADDR}; + +fn OS_SharedConfig() -> *mut osSharedConfig_s { + OS_SHAREDCFG_VADDR as _ +} + +/// Gets the state of the 3D slider as a value from 0.0 to 1.0 +pub unsafe fn osGet3DSliderState() -> f32 { + (*OS_SharedConfig()).slider_3d +} diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index ac6ea8b..6bfc1fa 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -11,7 +11,6 @@ use citro3d_sys::C3D_Mtx; use ctru::prelude::*; use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D}; -use std::f32::consts::PI; use std::ffi::CStr; use std::mem::MaybeUninit; @@ -38,15 +37,15 @@ struct Vertex { static VERTICES: &[Vertex] = &[ Vertex { - pos: Vec3::new(0.0, 0.5, 0.5), + 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, 0.5), + 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, 0.5), + pos: Vec3::new(0.5, -0.5, 3.0), color: Vec3::new(0.0, 0.0, 1.0), }, ]; @@ -95,13 +94,7 @@ fn main() { let mut buf_info = buffer::Info::new(); let (attr_info, vbo_idx) = prepare_vbos(&mut buf_info, &vbo_data); - let (projection_uniform_idx, mut projection) = scene_init(&mut program); - - unsafe { citro3d_sys::Mtx_RotateY(&mut projection, -PI / 12.0, true) }; - - let mut right_eye_projection = projection; - unsafe { citro3d_sys::Mtx_RotateY(&mut right_eye_projection, 2.0 * PI / 12.0, true) }; - + let projection_uniform_idx = scene_init(&mut program); while apt.main_loop() { hid.scan_input(); @@ -132,9 +125,15 @@ fn main() { instance.draw_arrays(buffer::Primitive::Triangles, vbo_idx); }; - render_to(&mut top_left_target, &projection); - render_to(&mut top_right_target, &right_eye_projection); - render_to(&mut bottom_target, &projection); + let Projections { + left, + right, + center, + } = calculate_projections(); + + render_to(&mut top_left_target, &left); + render_to(&mut top_right_target, &right); + render_to(&mut bottom_target, ¢er); }); } } @@ -159,7 +158,7 @@ where .unwrap(); attr_info - .add_loader(reg1, attrib::Format::Float, 5) + .add_loader(reg1, attrib::Format::Float, 3) .unwrap(); let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap(); @@ -167,34 +166,71 @@ where (attr_info, buf_idx) } -fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx) { - // Load the vertex shader, create a shader program and bind it +struct Projections { + left: C3D_Mtx, + right: C3D_Mtx, + center: C3D_Mtx, +} + +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 + // the fly with D-pad, etc. + let slider_val = unsafe { citro3d_sys::osGet3DSliderState() }; + let iod = slider_val / 4.0; + + let near = 0.01; + let far = 100.0; + let fovy = 40.0_f32.to_radians(); + let screen = 2.0; + unsafe { - citro3d_sys::C3D_BindProgram(program.as_raw()); + citro3d_sys::Mtx_PerspStereoTilt( + left_eye.as_mut_ptr(), + fovy, + citro3d_sys::C3D_AspectRatioTop as f32, + near, + far, + -iod, + screen, + true, + ); - // Get the location of the uniforms - let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap(); - let projection_uniform_idx = ctru_sys::shaderInstanceGetUniformLocation( - (*program.as_raw()).vertexShader, - projection_name.as_ptr(), + citro3d_sys::Mtx_PerspStereoTilt( + right_eye.as_mut_ptr(), + fovy, + citro3d_sys::C3D_AspectRatioTop as f32, + near, + far, + iod, + screen, + true, ); - // Compute the projection matrix - let projection = { - let mut projection = MaybeUninit::uninit(); - citro3d_sys::Mtx_OrthoTilt( - projection.as_mut_ptr(), - // The 3ds top screen is a 5:3 ratio - -1.66, - 1.66, - -1.0, - 1.0, - 0.0, - 1.0, - true, - ); - projection.assume_init() - }; + citro3d_sys::Mtx_PerspTilt( + center.as_mut_ptr(), + fovy, + citro3d_sys::C3D_AspectRatioBot as f32, + near, + far, + true, + ); + + Projections { + left: left_eye.assume_init(), + right: right_eye.assume_init(), + center: center.assume_init(), + } + } +} + +fn scene_init(program: &mut shader::Program) -> i8 { + // Load the vertex shader, create a shader program and bind it + unsafe { + citro3d_sys::C3D_BindProgram(program.as_raw()); // 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 @@ -209,6 +245,13 @@ fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx) { ); citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, ctru_sys::GPU_REPLACE); - (projection_uniform_idx, projection) + // Get the location of the uniforms + let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap(); + let projection_uniform_idx = ctru_sys::shaderInstanceGetUniformLocation( + (*program.as_raw()).vertexShader, + projection_name.as_ptr(), + ); + + projection_uniform_idx } } diff --git a/citro3d/src/buffer.rs b/citro3d/src/buffer.rs index 8af071d..534a487 100644 --- a/citro3d/src/buffer.rs +++ b/citro3d/src/buffer.rs @@ -127,9 +127,9 @@ impl Info { // Error codes from match res { + ..=-3 => Err(crate::Error::System(res)), -2 => Err(crate::Error::InvalidMemoryLocation), -1 => Err(crate::Error::TooManyBuffers), - ..=0 => Err(crate::Error::System(res)), _ => Ok(Slice { index: res, size: vbo_data.len().try_into()?,