Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions citro3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ all-features = true
default-target = "armv6k-nintendo-3ds"
targs = []
cargo-args = ["-Z", "build-std"]

[package.metadata.cargo-3ds]
romfs_dir = "examples/assets/romfs"
93 changes: 93 additions & 0 deletions citro3d/examples/assets/gshader_geo.pica
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
; Example PICA200 geometry shader
.gsh point c0

; Uniforms
.fvec projection[4]

; Constants
.constf myconst(0.0, 1.0, -1.0, 0.5)
.alias zeros myconst.xxxx ; Vector full of zeros
.alias ones myconst.yyyy ; Vector full of ones
.alias half myconst.wwww

; Outputs - this time the type *is* used
.out outpos position
.out outclr color

; Inputs: we will receive the following inputs:
; v0-v1: position/color of the first vertex
; v2-v3: position/color of the second vertex
; v4-v5: position/color of the third vertex

.entry gmain
.proc gmain
; Calculate the midpoints of the vertices
mov r4, v0
add r4, v2, r4
mul r4, half, r4
mov r5, v2
add r5, v4, r5
mul r5, half, r5
mov r6, v4
add r6, v0, r6
mul r6, half, r6

; Emit the first triangle
mov r0, v0
mov r1, r4
mov r2, r6
call emit_triangle

; Emit the second triangle
mov r0, r4
mov r1, v2
mov r2, r5
call emit_triangle

; Emit the third triangle
mov r0, r6
mov r1, r5
mov r2, v4
call emit_triangle

; We're finished
end
.end

.proc emit_triangle
; Emit the first vertex
setemit 0
mov r8, r0
mov r9, v1
call process_vertex
emit

; Emit the second vertex
setemit 1
mov r8, r1
mov r9, v3
call process_vertex
emit

; Emit the third vertex and finish the primitive
setemit 2, prim
mov r8, r2
mov r9, v5
call process_vertex
emit
.end

; Subroutine
; Inputs:
; r8: vertex position
; r9: vertex color
.proc process_vertex
; outpos = projectionMatrix * r8
dp4 outpos.x, projection[0], r8
dp4 outpos.y, projection[1], r8
dp4 outpos.z, projection[2], r8
dp4 outpos.w, projection[3], r8

; outclr = r9
mov outclr, r9
.end
26 changes: 26 additions & 0 deletions citro3d/examples/assets/vshader_geo.pica
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
; Example PICA200 vertex shader

; Constants
.constf myconst(0.0, 1.0, -1.0, -0.5)
.alias zeros myconst.xxxx ; Vector full of zeros
.alias ones myconst.yyyy ; Vector full of ones

; Outputs - since we are also using a geoshader the output type isn't really used
.out outpos position
.out outclr color

; Inputs (defined as aliases for convenience)
.alias inpos v0
.alias inclr v1

.entry vmain
.proc vmain
; Pass through both inputs to the geoshader
mov outpos.xyz, inpos
mov outpos.w, ones
mov outclr.xyz, inclr
mov outclr.w, ones

; We're finished
end
.end
2 changes: 1 addition & 1 deletion citro3d/examples/cube.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn main() {
let mut buf_info = buffer::Info::new();
buf_info.add(vbo_data, attr_info.permutation()).unwrap();

let projection_uniform_idx = program.get_uniform("projection").unwrap();
let projection_uniform_idx = program.get_vertex_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),
Expand Down
208 changes: 208 additions & 0 deletions citro3d/examples/dynamic-shader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// This examples demonstrates loading a shader into memory at runtime

#![feature(allocator_api)]

use citro3d::math::{AspectRatio, ClipPlanes, Matrix4, Projection, StereoDisplacement};
use citro3d::render::{ClearFlags, Frame, ScreenTarget, Target};
use citro3d::texenv;
use citro3d::{attrib, buffer, shader};
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,
}

static VERTICES: &[Vertex] = &[
Vertex {
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, -3.0),
color: Vec3::new(0.0, 1.0, 0.0),
},
Vertex {
pos: Vec3::new(0.5, -0.5, -3.0),
color: Vec3::new(0.0, 0.0, 1.0),
},
];

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 = instance
.render_target(width, height, top_left, None)
.expect("failed to create render target");

let RawFrameBuffer { width, height, .. } = top_right.raw_framebuffer();
let mut top_right_target = instance
.render_target(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 = instance
.render_target(width, height, bottom_screen, None)
.expect("failed to create bottom screen render target");

let _romfs = ctru::services::romfs::RomFS::new().unwrap();

let shader = {
let shader_bytes = std::fs::read("romfs:/vshader.shbin").unwrap();
shader::Library::from_bytes(shader_bytes).unwrap()
};

let vertex_shader = shader.get(0).unwrap();

let program = shader::Program::new(vertex_shader).unwrap();
let projection_uniform_idx = program.get_vertex_uniform("projection").unwrap();

let vbo_data = buffer::Buffer::new(VERTICES);

let mut buf_info = buffer::Info::new();
let attr_info = prepare_vbos(&mut buf_info, vbo_data);

let stage0 = texenv::TexEnv::new()
.src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None)
.func(texenv::Mode::BOTH, texenv::CombineFunc::Replace);

while apt.main_loop() {
hid.scan_input();

if hid.keys_down().contains(KeyPad::START) {
break;
}

instance.render_frame_with(|mut frame| {
// Sadly closures can't have lifetime specifiers,
// so we wrap `render_to` in this function to force the borrow checker rules.
fn cast_lifetime_to_closure<'frame, T>(x: T) -> T
where
T: Fn(&mut Frame<'frame>, &'frame mut ScreenTarget<'_>, &Matrix4),
{
x
}

let render_to = cast_lifetime_to_closure(|frame, target, projection| {
target.clear(ClearFlags::ALL, CLEAR_COLOR, 0);

frame
.select_render_target(target)
.expect("failed to set render target");
frame.bind_vertex_uniform(projection_uniform_idx, projection);

frame.set_texenvs(&[stage0]);

frame.set_attr_info(&attr_info);

frame
.draw_arrays(buffer::Primitive::Triangles, &buf_info, None)
.unwrap();
});

// We bind the vertex shader.
frame.bind_program(&program);

// 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 Projections {
left_eye,
right_eye,
center,
} = calculate_projections();

render_to(&mut frame, &mut top_left_target, &left_eye);
render_to(&mut frame, &mut top_right_target, &right_eye);
render_to(&mut frame, &mut bottom_target, &center);

frame
});
}
}

fn prepare_vbos(buf_info: &mut buffer::Info, vbo_data: buffer::Buffer) -> attrib::Info {
// Configure attributes for use with the vertex shader
let mut attr_info = attrib::Info::new();

attr_info
.add_loader(attrib::Register::V0, attrib::Format::Float, 3)
.unwrap();

attr_info
.add_loader(attrib::Register::V1, attrib::Format::Float, 3)
.unwrap();

buf_info.add(vbo_data, attr_info.permutation()).unwrap();

attr_info
}

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,
}
}
4 changes: 2 additions & 2 deletions citro3d/examples/fragment-light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,10 @@ fn main() {

// Setup the rotating view of the cube
let mut view = Matrix4::identity();
let model_idx = program.get_uniform("modelView").unwrap();
let model_idx = program.get_vertex_uniform("modelView").unwrap();
view.translate(0.0, 0.0, -2.0);

let projection_uniform_idx = program.get_uniform("projection").unwrap();
let projection_uniform_idx = program.get_vertex_uniform("projection").unwrap();

let stage0 = texenv::TexEnv::new()
.src(
Expand Down
Loading