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 @@ @@ -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; @@ -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::*;

13
citro3d-sys/src/os.rs

@ -0,0 +1,13 @@ @@ -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; @@ -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 { @@ -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() { @@ -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() { @@ -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, &center);
});
}
}
@ -159,7 +158,7 @@ where @@ -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 @@ -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) { @@ -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
}
}

2
citro3d/src/buffer.rs

@ -127,9 +127,9 @@ impl Info { @@ -127,9 +127,9 @@ impl Info {
// Error codes from <https://github.com/devkitPro/citro3d/blob/master/source/buffers.c#L11>
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()?,

Loading…
Cancel
Save