diff --git a/.gitignore b/.gitignore index a6e5cf1..4a87a85 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ Cargo.lock # Can be used for local development to set a custom toolchain rust-toolchain rust-toolchain.toml +.cargo/ diff --git a/citro3d-sys/src/texenv.rs b/citro3d-sys/src/texenv.rs index 1919a19..f7ef661 100644 --- a/citro3d-sys/src/texenv.rs +++ b/citro3d-sys/src/texenv.rs @@ -1,5 +1,8 @@ //! Definitions for ``. //! Most of these functions are `static inline` so they don't get generated by `bindgen`. +//! See . + +// TODO: probably should mark these all as #[inline] for parity with the C code use core::ops::{BitOr, Shl}; @@ -10,7 +13,7 @@ use super::*; // TODO: why are these two different macros in C? /// Creates a texture combiner source parameter from three sources. -fn gpu_tevsources(a: T, b: T, c: T) -> T +fn GPU_TEVSOURCES(a: T, b: T, c: T) -> T where T: BitOr + Shl, { @@ -18,7 +21,7 @@ where } /// Creates a texture combiner operand parameter from three operands. -fn gpu_tevoperands(a: T, b: T, c: T) -> T +fn GPU_TEVOPERANDS(a: T, b: T, c: T) -> T where T: BitOr + Shl, { @@ -26,7 +29,7 @@ where } pub unsafe fn C3D_TexEnvInit(env: *mut C3D_TexEnv) { - (*env).srcRgb = gpu_tevsources(GPU_PREVIOUS, 0, 0) as u16; + (*env).srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0) as u16; (*env).srcAlpha = (*env).srcRgb; (*env).__bindgen_anon_1.opAll = 0; (*env).funcRgb = GPU_REPLACE as u16; @@ -36,24 +39,35 @@ pub unsafe fn C3D_TexEnvInit(env: *mut C3D_TexEnv) { (*env).scaleAlpha = GPU_TEVSCALE_1 as u16; } -// TODO: maybe - -pub unsafe fn C3D_TexEnvSrc(env: *mut C3D_TexEnv, mode: c_int, s1: c_int, s2: c_int, s3: c_int) { - let param = gpu_tevsources(s1, s2, s3); +pub unsafe fn C3D_TexEnvSrc( + env: *mut C3D_TexEnv, + mode: C3D_TexEnvMode, + s1: GPU_TEVSRC, + s2: GPU_TEVSRC, // default GPU_PRIMARY_COLOR + s3: GPU_TEVSRC, // default GPU_PRIMARY_COLOR +) { + let param = GPU_TEVSOURCES(s1, s2, s3); - if mode & C3D_RGB as i32 != 0 { + if mode & C3D_RGB != 0 { (*env).srcRgb = param as u16; } - if mode & C3D_Alpha as i32 != 0 { + if mode & C3D_Alpha != 0 { (*env).srcAlpha = param as u16; } } -pub unsafe fn C3D_TexEnvOp(env: *mut C3D_TexEnv, mode: c_int, o1: c_int, o2: c_int, o3: c_int) { - let param = gpu_tevoperands(o1, o2, o3); - - if mode & C3D_RGB as i32 != 0 { +// TODO: match API of texenv.h +pub unsafe fn C3D_TexEnvOp( + env: *mut C3D_TexEnv, + mode: C3D_TexEnvMode, + o1: c_int, + o2: c_int, + o3: c_int, +) { + let param = GPU_TEVOPERANDS(o1, o2, o3); + + if mode & C3D_RGB != 0 { // (*env).opRgb = param as u16; (*env) .__bindgen_anon_1 @@ -61,7 +75,7 @@ pub unsafe fn C3D_TexEnvOp(env: *mut C3D_TexEnv, mode: c_int, o1: c_int, o2: c_i .set_opRgb(param as u32); } - if mode & C3D_Alpha as i32 != 0 { + if mode & C3D_Alpha != 0 { (*env) .__bindgen_anon_1 .__bindgen_anon_1 @@ -69,12 +83,12 @@ pub unsafe fn C3D_TexEnvOp(env: *mut C3D_TexEnv, mode: c_int, o1: c_int, o2: c_i } } -pub unsafe fn C3D_TexEnvFunc(env: *mut C3D_TexEnv, mode: c_int, param: c_int) { - if mode & C3D_RGB as i32 != 0 { +pub unsafe fn C3D_TexEnvFunc(env: *mut C3D_TexEnv, mode: C3D_TexEnvMode, param: GPU_COMBINEFUNC) { + if mode & C3D_RGB != 0 { (*env).funcRgb = param as u16; } - if mode & C3D_Alpha as i32 != 0 { + if mode & C3D_Alpha != 0 { (*env).funcAlpha = param as u16; } } diff --git a/citro3d/examples/triangle.rs b/citro3d/examples/triangle.rs index 266d165..24caa4d 100644 --- a/citro3d/examples/triangle.rs +++ b/citro3d/examples/triangle.rs @@ -3,14 +3,43 @@ use citro3d_sys::{shaderProgram_s, DVLB_s}; use ctru::gfx::{Gfx, Screen, Side}; use ctru::services::apt::Apt; use ctru::services::hid::{Hid, KeyPad}; +use ctru::services::soc::Soc; -use std::ffi::CString; +use std::ffi::CStr; use std::mem::MaybeUninit; +#[repr(C)] +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)] +struct Vertex { + pos: Vec3, + color: Vec3, +} + const VERTICES: [Vertex; 3] = [ - Vertex::new(200.0, 200.0, 0.5), - Vertex::new(100.0, 40.0, 0.5), - Vertex::new(300.0, 40.0, 0.5), + Vertex { + pos: Vec3::new(0.0, 0.5, 0.5), + color: Vec3::new(1.0, 0.0, 0.0), + }, + Vertex { + pos: Vec3::new(-0.5, -0.5, 0.5), + color: Vec3::new(0.0, 1.0, 0.0), + }, + Vertex { + pos: Vec3::new(0.5, -0.5, 0.5), + color: Vec3::new(0.0, 0.0, 1.0), + }, ]; fn main() { @@ -19,6 +48,8 @@ fn main() { let gfx = Gfx::init().expect("Couldn't obtain GFX controller"); let hid = Hid::init().expect("Couldn't obtain HID controller"); let apt = Apt::init().expect("Couldn't obtain APT controller"); + let mut soc = Soc::init().expect("failed to get SOC"); + drop(soc.redirect_to_3dslink(true, true)); let top_screen = gfx.top_screen.borrow_mut(); @@ -48,21 +79,23 @@ fn main() { let (program, uloc_projection, projection, vbo_data, vshader_dvlb) = scene_init(); - // Main loop while apt.main_loop() { - //Scan all the inputs. This should be done once for each frame hid.scan_input(); if hid.keys_down().contains(KeyPad::KEY_START) { break; } - const CLEAR_COLOR: u32 = 0x68_B0_D8_FF; + let clear_color: u32 = 0x7F_7F_7F_FF; unsafe { - citro3d_sys::C3D_FrameBegin(citro3d_sys::C3D_FRAME_SYNCDRAW as u8); + citro3d_sys::C3D_FrameBegin( + citro3d_sys::C3D_FRAME_SYNCDRAW + .try_into() + .expect("const is valid u8"), + ); - citro3d_sys::C3D_RenderTargetClear(target, citro3d_sys::C3D_CLEAR_ALL, CLEAR_COLOR, 0); + citro3d_sys::C3D_RenderTargetClear(target, citro3d_sys::C3D_CLEAR_ALL, clear_color, 0); citro3d_sys::C3D_FrameDrawOn(target); } scene_render(uloc_projection.into(), &projection); @@ -78,19 +111,6 @@ fn main() { } } -#[repr(C)] -struct Vertex { - x: f32, - y: f32, - z: f32, -} - -impl Vertex { - const fn new(x: f32, y: f32, z: f32) -> Self { - Self { x, y, z } - } -} - static SHBIN_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/examples/assets/vshader.shbin")); @@ -101,22 +121,22 @@ fn scene_init() -> (shaderProgram_s, i8, C3D_Mtx, *mut libc::c_void, *mut DVLB_s // Assume the data is aligned properly... let vshader_dvlb = citro3d_sys::DVLB_ParseFile( - shader_bytes.as_mut_ptr() as _, + shader_bytes.as_mut_ptr().cast(), (shader_bytes.len() / 4) .try_into() .expect("shader len fits in a u32"), ); - let mut program = MaybeUninit::::uninit(); - - citro3d_sys::shaderProgramInit(program.as_mut_ptr()); - citro3d_sys::shaderProgramSetVsh(program.as_mut_ptr(), (*vshader_dvlb).DVLE); - - let mut program = program.assume_init(); + let mut program = { + let mut program = MaybeUninit::uninit(); + citro3d_sys::shaderProgramInit(program.as_mut_ptr()); + program.assume_init() + }; + citro3d_sys::shaderProgramSetVsh(&mut program, (*vshader_dvlb).DVLE); citro3d_sys::C3D_BindProgram(&mut program); // Get the location of the uniforms - let projection_name = CString::new("projection").unwrap(); + let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap(); let uloc_projection = citro3d_sys::shaderInstanceGetUniformLocation( program.vertexShader, projection_name.as_ptr(), @@ -126,44 +146,46 @@ fn scene_init() -> (shaderProgram_s, i8, C3D_Mtx, *mut libc::c_void, *mut DVLB_s let attr_info = citro3d_sys::C3D_GetAttrInfo(); citro3d_sys::AttrInfo_Init(attr_info); citro3d_sys::AttrInfo_AddLoader(attr_info, 0, citro3d_sys::GPU_FLOAT, 3); // v0=position - citro3d_sys::AttrInfo_AddFixed(attr_info, 1); // v1=color - - // Set the fixed attribute (color) to solid white - citro3d_sys::C3D_FixedAttribSet(1, 1.0, 1.0, 1.0, 1.0); + citro3d_sys::AttrInfo_AddLoader(attr_info, 1, citro3d_sys::GPU_FLOAT, 3); // v1=color - let mut projection = MaybeUninit::::uninit(); // Compute the projection matrix - citro3d_sys::Mtx_OrthoTilt( - projection.as_mut_ptr(), - 0.0, - 400.0, - 0.0, - 240.0, - 0.0, - 1.0, - true, - ); - let projection = projection.assume_init(); - - let vertices_len = std::mem::size_of_val(&VERTICES); + 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() + }; - // Create the VBO (vertex buffer object) - let vbo_data = - citro3d_sys::linearAlloc(vertices_len.try_into().expect("size does not fit in u32")); + // Create the vertex buffer object + let vbo_data: *mut Vertex = citro3d_sys::linearAlloc( + std::mem::size_of_val(&VERTICES) + .try_into() + .expect("size fits in u32"), + ) + .cast(); - vbo_data.copy_from(VERTICES.as_ptr().cast(), vertices_len); + vbo_data.copy_from(VERTICES.as_ptr(), VERTICES.len()); // Configure buffers let buf_info = citro3d_sys::C3D_GetBufInfo(); citro3d_sys::BufInfo_Init(buf_info); citro3d_sys::BufInfo_Add( buf_info, - vbo_data, + vbo_data.cast(), std::mem::size_of::() .try_into() - .expect("size of vertex fits in u32"), - 1, - 0x0, + .expect("size of vec3 fits in u32"), + 2, // Each vertex has two attributes + 0x10, // v0 = position, v1 = color, in LSB->MSB nibble order ); // Configure the first fragment shading substage to just pass through the vertex color @@ -172,18 +194,20 @@ fn scene_init() -> (shaderProgram_s, i8, C3D_Mtx, *mut libc::c_void, *mut DVLB_s citro3d_sys::C3D_TexEnvInit(env); citro3d_sys::C3D_TexEnvSrc( env, - citro3d_sys::C3D_Both as i32, - citro3d_sys::GPU_PRIMARY_COLOR as i32, + citro3d_sys::C3D_Both, + citro3d_sys::GPU_PRIMARY_COLOR, 0, 0, ); - citro3d_sys::C3D_TexEnvFunc( - env, - citro3d_sys::C3D_Both as i32, - citro3d_sys::GPU_REPLACE as i32, - ); - - (program, uloc_projection, projection, vbo_data, vshader_dvlb) + citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, citro3d_sys::GPU_REPLACE); + + ( + program, + uloc_projection, + projection, + vbo_data.cast(), + vshader_dvlb, + ) } } @@ -193,7 +217,14 @@ fn scene_render(uloc_projection: i32, projection: &C3D_Mtx) { citro3d_sys::C3D_FVUnifMtx4x4(citro3d_sys::GPU_VERTEX_SHADER, uloc_projection, projection); // Draw the VBO - citro3d_sys::C3D_DrawArrays(citro3d_sys::GPU_TRIANGLES, 0, VERTICES.len() as i32); + citro3d_sys::C3D_DrawArrays( + citro3d_sys::GPU_TRIANGLES, + 0, + VERTICES + .len() + .try_into() + .expect("VERTICES.len() fits in i32"), + ); } }