Browse Source

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.
pull/16/head
Ian Chamberlain 2 years ago
parent
commit
e47c414069
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 2
      citro3d-sys/src/lib.rs
  2. 13
      citro3d-sys/src/os.rs
  3. 123
      citro3d/examples/triangle.rs
  4. 2
      citro3d/src/buffer.rs

2
citro3d-sys/src/lib.rs

@ -7,6 +7,7 @@
pub mod base; pub mod base;
pub mod gx; pub mod gx;
pub mod os;
pub mod renderqueue; pub mod renderqueue;
pub mod texenv; pub mod texenv;
pub mod uniforms; pub mod uniforms;
@ -16,6 +17,7 @@ mod bindings;
pub use base::*; pub use base::*;
pub use bindings::*; pub use bindings::*;
pub use gx::*; pub use gx::*;
pub use os::*;
pub use renderqueue::*; pub use renderqueue::*;
pub use texenv::*; pub use texenv::*;
pub use uniforms::*; pub use uniforms::*;

13
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
}

123
citro3d/examples/triangle.rs

@ -11,7 +11,6 @@ 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};
use std::f32::consts::PI;
use std::ffi::CStr; use std::ffi::CStr;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
@ -38,15 +37,15 @@ struct Vertex {
static VERTICES: &[Vertex] = &[ static VERTICES: &[Vertex] = &[
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), color: Vec3::new(1.0, 0.0, 0.0),
}, },
Vertex { 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), color: Vec3::new(0.0, 1.0, 0.0),
}, },
Vertex { 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), color: Vec3::new(0.0, 0.0, 1.0),
}, },
]; ];
@ -95,13 +94,7 @@ 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, mut projection) = scene_init(&mut program); let projection_uniform_idx = 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) };
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();
@ -132,9 +125,15 @@ fn main() {
instance.draw_arrays(buffer::Primitive::Triangles, vbo_idx); instance.draw_arrays(buffer::Primitive::Triangles, vbo_idx);
}; };
render_to(&mut top_left_target, &projection); let Projections {
render_to(&mut top_right_target, &right_eye_projection); left,
render_to(&mut bottom_target, &projection); right,
center,
} = calculate_projections();
render_to(&mut top_left_target, &left);
render_to(&mut top_right_target, &right);
render_to(&mut bottom_target, &center);
}); });
} }
} }
@ -159,7 +158,7 @@ where
.unwrap(); .unwrap();
attr_info attr_info
.add_loader(reg1, attrib::Format::Float, 5) .add_loader(reg1, attrib::Format::Float, 3)
.unwrap(); .unwrap();
let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap(); let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap();
@ -167,34 +166,71 @@ where
(attr_info, buf_idx) (attr_info, buf_idx)
} }
fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx) { struct Projections {
// Load the vertex shader, create a shader program and bind it 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 { 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 citro3d_sys::Mtx_PerspStereoTilt(
let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap(); right_eye.as_mut_ptr(),
let projection_uniform_idx = ctru_sys::shaderInstanceGetUniformLocation( fovy,
(*program.as_raw()).vertexShader, citro3d_sys::C3D_AspectRatioTop as f32,
projection_name.as_ptr(), near,
far,
iod,
screen,
true,
); );
// Compute the projection matrix citro3d_sys::Mtx_PerspTilt(
let projection = { center.as_mut_ptr(),
let mut projection = MaybeUninit::uninit(); fovy,
citro3d_sys::Mtx_OrthoTilt( citro3d_sys::C3D_AspectRatioBot as f32,
projection.as_mut_ptr(), near,
// The 3ds top screen is a 5:3 ratio far,
-1.66, true,
1.66, );
-1.0,
1.0, Projections {
0.0, left: left_eye.assume_init(),
1.0, right: right_eye.assume_init(),
true, center: center.assume_init(),
); }
projection.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 // 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
@ -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); 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
} }
} }

2
citro3d/src/buffer.rs

@ -127,9 +127,9 @@ impl Info {
// Error codes from <https://github.com/devkitPro/citro3d/blob/master/source/buffers.c#L11> // Error codes from <https://github.com/devkitPro/citro3d/blob/master/source/buffers.c#L11>
match res { match res {
..=-3 => Err(crate::Error::System(res)),
-2 => Err(crate::Error::InvalidMemoryLocation), -2 => Err(crate::Error::InvalidMemoryLocation),
-1 => Err(crate::Error::TooManyBuffers), -1 => Err(crate::Error::TooManyBuffers),
..=0 => Err(crate::Error::System(res)),
_ => Ok(Slice { _ => Ok(Slice {
index: res, index: res,
size: vbo_data.len().try_into()?, size: vbo_data.len().try_into()?,

Loading…
Cancel
Save