Natasha England-Elbro
1 year ago
2 changed files with 296 additions and 1 deletions
@ -0,0 +1,246 @@
@@ -0,0 +1,246 @@
|
||||
//! This example demonstrates the most basic usage of `citro3d`: rendering a simple
|
||||
//! RGB triangle (sometimes called a "Hello triangle") to the 3DS screen.
|
||||
|
||||
#![feature(allocator_api)] |
||||
|
||||
use citro3d::macros::include_shader; |
||||
use citro3d::math::{ |
||||
AspectRatio, ClipPlanes, CoordinateOrientation, FVec3, Matrix4, Projection, StereoDisplacement, |
||||
}; |
||||
use citro3d::render::ClearFlags; |
||||
use citro3d::{attrib, buffer, render, shader}; |
||||
use citro3d::{texenv, DrawingIndices}; |
||||
use ctru::prelude::*; |
||||
use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D}; |
||||
|
||||
#[repr(C)] |
||||
#[derive(Copy, Clone)] |
||||
struct Vec3 { |
||||
x: f32, |
||||
y: f32, |
||||
z: f32, |
||||
} |
||||
|
||||
impl Vec3 { |
||||
const fn new(x: f32, y: f32, z: f32) -> Self { |
||||
Self { x, y, z } |
||||
} |
||||
} |
||||
|
||||
#[repr(C)] |
||||
#[derive(Copy, Clone)] |
||||
struct Vertex { |
||||
pos: Vec3, |
||||
color: Vec3, |
||||
} |
||||
|
||||
// borrowed from https://bevyengine.org/examples/3D%20Rendering/generate-custom-mesh/
|
||||
const VERTS: &[[f32; 3]] = &[ |
||||
// top (facing towards +y)
|
||||
[-0.5, 0.5, -0.5], // vertex with index 0
|
||||
[0.5, 0.5, -0.5], // vertex with index 1
|
||||
[0.5, 0.5, 0.5], // etc. until 23
|
||||
[-0.5, 0.5, 0.5], |
||||
// bottom (-y)
|
||||
[-0.5, -0.5, -0.5], |
||||
[0.5, -0.5, -0.5], |
||||
[0.5, -0.5, 0.5], |
||||
[-0.5, -0.5, 0.5], |
||||
// right (+x)
|
||||
[0.5, -0.5, -0.5], |
||||
[0.5, -0.5, 0.5], |
||||
[0.5, 0.5, 0.5], // This vertex is at the same position as vertex with index 2, but they'll have different UV and normal
|
||||
[0.5, 0.5, -0.5], |
||||
// left (-x)
|
||||
[-0.5, -0.5, -0.5], |
||||
[-0.5, -0.5, 0.5], |
||||
[-0.5, 0.5, 0.5], |
||||
[-0.5, 0.5, -0.5], |
||||
// back (+z)
|
||||
[-0.5, -0.5, 0.5], |
||||
[-0.5, 0.5, 0.5], |
||||
[0.5, 0.5, 0.5], |
||||
[0.5, -0.5, 0.5], |
||||
// forward (-z)
|
||||
[-0.5, -0.5, -0.5], |
||||
[-0.5, 0.5, -0.5], |
||||
[0.5, 0.5, -0.5], |
||||
[0.5, -0.5, -0.5], |
||||
]; |
||||
|
||||
static SHADER_BYTES: &[u8] = include_shader!("assets/vshader.pica"); |
||||
const CLEAR_COLOR: u32 = 0x68_B0_D8_FF; |
||||
|
||||
fn main() { |
||||
let mut soc = Soc::new().expect("failed to get SOC"); |
||||
drop(soc.redirect_to_3dslink(true, true)); |
||||
|
||||
let gfx = Gfx::new().expect("Couldn't obtain GFX controller"); |
||||
let mut hid = Hid::new().expect("Couldn't obtain HID controller"); |
||||
let apt = Apt::new().expect("Couldn't obtain APT controller"); |
||||
|
||||
let mut instance = citro3d::Instance::new().expect("failed to initialize Citro3D"); |
||||
|
||||
let top_screen = TopScreen3D::from(&gfx.top_screen); |
||||
|
||||
let (mut top_left, mut top_right) = top_screen.split_mut(); |
||||
|
||||
let RawFrameBuffer { width, height, .. } = top_left.raw_framebuffer(); |
||||
let mut top_left_target = |
||||
render::Target::new(width, height, top_left, None).expect("failed to create render target"); |
||||
|
||||
let RawFrameBuffer { width, height, .. } = top_right.raw_framebuffer(); |
||||
let mut top_right_target = render::Target::new(width, height, top_right, None) |
||||
.expect("failed to create render target"); |
||||
|
||||
let mut bottom_screen = gfx.bottom_screen.borrow_mut(); |
||||
let RawFrameBuffer { width, height, .. } = bottom_screen.raw_framebuffer(); |
||||
|
||||
let mut bottom_target = render::Target::new(width, height, bottom_screen, None) |
||||
.expect("failed to create bottom screen render target"); |
||||
|
||||
let shader = shader::Library::from_bytes(SHADER_BYTES).unwrap(); |
||||
let vertex_shader = shader.get(0).unwrap(); |
||||
|
||||
let program = shader::Program::new(vertex_shader).unwrap(); |
||||
instance.bind_program(&program); |
||||
let mut vbo_data = Vec::with_capacity_in(VERTS.len(), ctru::linear::LinearAllocator); |
||||
for vert in VERTS.iter().enumerate().map(|(i, v)| Vertex { |
||||
pos: Vec3 { |
||||
x: v[0], |
||||
y: v[1], |
||||
z: v[2], |
||||
}, |
||||
color: Vec3::new(i as f32 % 1., i as f32 % 1., i as f32 % 1.), |
||||
}) { |
||||
vbo_data.push(vert); |
||||
} |
||||
|
||||
let mut buf_info = buffer::Info::new(); |
||||
let (attr_info, vbo_data) = prepare_vbos(&mut buf_info, &vbo_data); |
||||
|
||||
// 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
|
||||
let stage0 = texenv::Stage::new(0).unwrap(); |
||||
instance |
||||
.texenv(stage0) |
||||
.src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None) |
||||
.func(texenv::Mode::BOTH, texenv::CombineFunc::Replace); |
||||
|
||||
let projection_uniform_idx = program.get_uniform("projection").unwrap(); |
||||
let camera_transform = Matrix4::looking_at( |
||||
FVec3::new(1.8, 1.8, 1.8), |
||||
FVec3::new(0.0, 0.0, 0.0), |
||||
FVec3::new(0.0, 1.0, 0.0), |
||||
CoordinateOrientation::RightHanded, |
||||
); |
||||
|
||||
while apt.main_loop() { |
||||
hid.scan_input(); |
||||
|
||||
if hid.keys_down().contains(KeyPad::START) { |
||||
break; |
||||
} |
||||
|
||||
instance.render_frame_with(|instance| { |
||||
let mut render_to = |target: &mut render::Target, projection| { |
||||
target.clear(ClearFlags::ALL, CLEAR_COLOR, 0); |
||||
|
||||
instance |
||||
.select_render_target(target) |
||||
.expect("failed to set render target"); |
||||
|
||||
instance.bind_vertex_uniform( |
||||
projection_uniform_idx, |
||||
&(projection * camera_transform.clone()), |
||||
); |
||||
|
||||
instance.set_attr_info(&attr_info); |
||||
unsafe { |
||||
instance.draw_elements( |
||||
buffer::Primitive::Triangles, |
||||
DrawingIndices::U16(&[ |
||||
0, 3, 1, 1, 3, 2, // triangles making up the top (+y) facing side.
|
||||
4, 5, 7, 5, 6, 7, // bottom (-y)
|
||||
8, 11, 9, 9, 11, 10, // right (+x)
|
||||
12, 13, 15, 13, 14, 15, // left (-x)
|
||||
16, 19, 17, 17, 19, 18, // back (+z)
|
||||
20, 21, 23, 21, 22, 23, // forward (-z)
|
||||
]), |
||||
); |
||||
} |
||||
|
||||
instance.draw_arrays(buffer::Primitive::Triangles, vbo_data); |
||||
}; |
||||
|
||||
let Projections { |
||||
left_eye, |
||||
right_eye, |
||||
center, |
||||
} = calculate_projections(); |
||||
|
||||
render_to(&mut top_left_target, &left_eye); |
||||
render_to(&mut top_right_target, &right_eye); |
||||
render_to(&mut bottom_target, ¢er); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
fn prepare_vbos<'a>( |
||||
buf_info: &'a mut buffer::Info, |
||||
vbo_data: &'a [Vertex], |
||||
) -> (attrib::Info, buffer::Slice<'a>) { |
||||
// Configure attributes for use with the vertex shader
|
||||
let mut attr_info = attrib::Info::new(); |
||||
|
||||
let reg0 = attrib::Register::new(0).unwrap(); |
||||
let reg1 = attrib::Register::new(1).unwrap(); |
||||
|
||||
attr_info |
||||
.add_loader(reg0, attrib::Format::Float, 3) |
||||
.unwrap(); |
||||
|
||||
attr_info |
||||
.add_loader(reg1, attrib::Format::Float, 3) |
||||
.unwrap(); |
||||
|
||||
let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap(); |
||||
|
||||
(attr_info, buf_idx) |
||||
} |
||||
|
||||
struct Projections { |
||||
left_eye: Matrix4, |
||||
right_eye: Matrix4, |
||||
center: Matrix4, |
||||
} |
||||
|
||||
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 = ctru::os::current_3d_slider_state(); |
||||
let interocular_distance = slider_val / 2.0; |
||||
|
||||
let vertical_fov = 40.0_f32.to_radians(); |
||||
let screen_depth = 2.0; |
||||
|
||||
let clip_planes = ClipPlanes { |
||||
near: 0.01, |
||||
far: 100.0, |
||||
}; |
||||
|
||||
let (left, right) = StereoDisplacement::new(interocular_distance, screen_depth); |
||||
|
||||
let (left_eye, right_eye) = |
||||
Projection::perspective(vertical_fov, AspectRatio::TopScreen, clip_planes) |
||||
.stereo_matrices(left, right); |
||||
|
||||
let center = |
||||
Projection::perspective(vertical_fov, AspectRatio::BottomScreen, clip_planes).into(); |
||||
|
||||
Projections { |
||||
left_eye, |
||||
right_eye, |
||||
center, |
||||
} |
||||
} |
Loading…
Reference in new issue