Created
November 27, 2022 21:54
-
-
Save hYdos/20943d62a19dfbb878f747ddc3a57704 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use citro3d::include_aligned_bytes; | |
use citro3d_sys::{ | |
shaderProgram_s, C3D_FVec, C3D_Mtx, C3D_Tex, Mtx_RotateX, Mtx_RotateY, Mtx_Scale, | |
Mtx_Translate, GPU_TEXTURE_FILTER_PARAM, | |
}; | |
use ctru::gfx::{Gfx, Side}; | |
use ctru::services::apt::Apt; | |
use ctru::services::hid::{Hid, KeyPad}; | |
use citro3d::render::{ClearFlags, ColorFormat, DepthFormat}; | |
use crate::new_utils::{load_texture_from_mem, Vec2, Vec3}; | |
use std::ffi::CString; | |
use std::mem::MaybeUninit; | |
use ctru::console::Console; | |
use ctru::romfs::RomFS; | |
mod new_utils; | |
#[repr(C)] | |
struct Vertex { | |
position: Vec3, | |
texcoord: Vec2, | |
normal: Vec3, | |
} | |
const VERTICES: &[Vertex] = &[ | |
// First face (PZ) | |
// First triangle | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, 0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, 0.0, 1.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, -0.5, 0.5), | |
texcoord: Vec2::new(1.0, 0.0), | |
normal: Vec3::new(0.0, 0.0, 1.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, 0.0, 1.0), | |
}, | |
// Second triangle | |
Vertex { | |
position: Vec3::new(0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, 0.0, 1.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, 0.5), | |
texcoord: Vec2::new(0.0, 1.0), | |
normal: Vec3::new(0.0, 0.0, 1.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, 0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, 0.0, 1.0), | |
}, | |
// Second face (MZ) | |
// First triangle | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, 0.0, -1.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, -0.5), | |
texcoord: Vec2::new(1.0, 0.0), | |
normal: Vec3::new(0.0, 0.0, -1.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, 0.5, -0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, 0.0, -1.0), | |
}, | |
// Second triangle | |
Vertex { | |
position: Vec3::new(0.5, 0.5, -0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, 0.0, -1.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 1.0), | |
normal: Vec3::new(0.0, 0.0, -1.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, 0.0, -1.0), | |
}, | |
// Third face (PX) | |
// First triangle | |
Vertex { | |
position: Vec3::new(0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, 0.5, -0.5), | |
texcoord: Vec2::new(1.0, 0.0), | |
normal: Vec3::new(1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(1.0, 0.0, 0.0), | |
}, | |
// Second triangle | |
Vertex { | |
position: Vec3::new(0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, -0.5, 0.5), | |
texcoord: Vec2::new(0.0, 1.0), | |
normal: Vec3::new(1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(1.0, 0.0, 0.0), | |
}, | |
// Fourth face (MX) | |
// First triangle | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(-1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, 0.5), | |
texcoord: Vec2::new(1.0, 0.0), | |
normal: Vec3::new(-1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(-1.0, 0.0, 0.0), | |
}, | |
// Second triangle | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(-1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, -0.5), | |
texcoord: Vec2::new(0.0, 1.0), | |
normal: Vec3::new(-1.0, 0.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(-1.0, 0.0, 0.0), | |
}, | |
// Fifth face (PY) | |
// First triangle | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, 1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 0.0), | |
normal: Vec3::new(0.0, 1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, 1.0, 0.0), | |
}, | |
// Second triangle | |
Vertex { | |
position: Vec3::new(0.5, 0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, 1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, 0.5, -0.5), | |
texcoord: Vec2::new(0.0, 1.0), | |
normal: Vec3::new(0.0, 1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, 0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, 1.0, 0.0), | |
}, | |
// Sixth face (MY) | |
// First triangle | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, -1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, -0.5, -0.5), | |
texcoord: Vec2::new(1.0, 0.0), | |
normal: Vec3::new(0.0, -1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(0.5, -0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, -1.0, 0.0), | |
}, | |
// Second triangle | |
Vertex { | |
position: Vec3::new(0.5, -0.5, 0.5), | |
texcoord: Vec2::new(1.0, 1.0), | |
normal: Vec3::new(0.0, -1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, 0.5), | |
texcoord: Vec2::new(0.0, 1.0), | |
normal: Vec3::new(0.0, -1.0, 0.0), | |
}, | |
Vertex { | |
position: Vec3::new(-0.5, -0.5, -0.5), | |
texcoord: Vec2::new(0.0, 0.0), | |
normal: Vec3::new(0.0, -1.0, 0.0), | |
}, | |
]; | |
const SHADER_BYTES: &[u8] = include_aligned_bytes!("../resources/shader.v.shbin"); | |
struct UniformLocations { | |
projection: i32, | |
model_view: i32, | |
} | |
fn main() { | |
ctru::init(); | |
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 _romfs = RomFS::init().unwrap(); | |
std::env::set_var("RUST_BACKTRACE", "1"); | |
let mut top_screen = gfx.top_screen.borrow_mut(); | |
let frame_buffer = top_screen.get_raw_framebuffer(Side::Left); | |
let _console = Console::init(gfx.bottom_screen.borrow_mut()); | |
let mut instance = citro3d::Instance::new().expect("failed to initialize Citro3D"); | |
let mut render_target = instance | |
.render_target_for_screen( | |
&frame_buffer, | |
ColorFormat::RGBA8, | |
DepthFormat::Depth24Stencil8, | |
) | |
.expect("failed to create render target"); | |
render_target.set_output(&*top_screen, Side::Left); | |
let shader = unsafe { | |
citro3d_sys::DVLB_ParseFile( | |
// SAFETY: we're trusting the parse implementation doesn't mutate | |
// the contents of the data. From a quick read it looks like that's | |
// correct and it should just take a const arg in the API. | |
SHADER_BYTES.as_ptr() as *mut _, | |
SHADER_BYTES.len() as u32, | |
) | |
}; | |
let mut program = { | |
let mut program = unsafe { | |
let mut program = MaybeUninit::uninit(); | |
let result = citro3d_sys::shaderProgramInit(program.as_mut_ptr()); | |
if result != 0 { | |
panic!("{:?}", ctru::Error::from(result)); | |
} | |
program.assume_init() | |
}; | |
let result = unsafe { citro3d_sys::shaderProgramSetVsh(&mut program, (*shader).DVLE) }; | |
if result != 0 { | |
panic!("{:?}", ctru::Error::from(result)); | |
} | |
program | |
}; | |
let (uloc_projection, vbo_data) = scene_init(&mut program); | |
let mut angle_x = 0.0; | |
let mut angle_y = 0.0; | |
while apt.main_loop() { | |
hid.scan_input(); | |
if hid.keys_down().contains(KeyPad::KEY_START) { | |
break; | |
} | |
#[inline(always)] | |
pub fn C3D_AngleFromDegrees(angle: f32) -> f32 { | |
angle * std::f32::consts::PI / 180.0 | |
} | |
angle_x += C3D_AngleFromDegrees(1.0); | |
angle_y += C3D_AngleFromDegrees(0.5); | |
instance.render_frame_with(|instance| { | |
let clear_color: u32 = 0x7F_7F_7F_FF; | |
render_target.clear(ClearFlags::ALL, clear_color, 0); | |
instance | |
.select_render_target(&render_target) | |
.expect("failed to set render target"); | |
let model_view = unsafe { | |
let mut model_view = C3D_Mtx { | |
r: [ | |
C3D_FVec { | |
c: [1.0, 0.0, 0.0, 0.0], | |
}, | |
C3D_FVec { | |
c: [0.0, 1.0, 0.0, 0.0], | |
}, | |
C3D_FVec { | |
c: [0.0, 0.0, 1.0, 0.0], | |
}, | |
C3D_FVec { | |
c: [0.0, 0.0, 0.0, 1.0], | |
}, | |
], | |
}; | |
Mtx_Translate( | |
&mut model_view, | |
0.0, | |
0.0, | |
-3.0, /*+ sinf(angleX)*/ | |
true, | |
); | |
Mtx_RotateX(&mut model_view, angle_x, true); | |
Mtx_RotateY(&mut model_view, angle_y, true); | |
Mtx_Scale(&mut model_view, 1.5, 1.5, 1.5); | |
model_view | |
}; | |
let projection = unsafe { | |
let mut projection = MaybeUninit::uninit(); | |
citro3d_sys::Mtx_PerspStereoTilt( | |
projection.as_mut_ptr(), | |
C3D_AngleFromDegrees(40.0), /* TODO: 90 */ | |
citro3d_sys::C3D_AspectRatioTop as f32, | |
0.01, | |
1000.0, | |
0.0, | |
2.0, | |
false, | |
); | |
projection.assume_init() | |
}; | |
scene_render(&uloc_projection, &projection, &model_view); | |
}); | |
} | |
scene_exit(vbo_data); | |
} | |
fn scene_init(program: &mut shaderProgram_s) -> (UniformLocations, *mut libc::c_void) { | |
// Load the vertex shader, create a shader program and bind it | |
unsafe { | |
citro3d_sys::C3D_BindProgram(program); | |
// Get the location of the uniforms | |
let projection_name = CString::new("projection").unwrap(); | |
let uloc_projection = citro3d_sys::shaderInstanceGetUniformLocation( | |
program.vertexShader, | |
projection_name.as_ptr(), | |
); | |
let model_view_name = CString::new("model_view").unwrap(); | |
let uloc_model_view = citro3d_sys::shaderInstanceGetUniformLocation( | |
program.vertexShader, | |
model_view_name.as_ptr(), | |
); | |
// Configure attributes for use with the vertex shader | |
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_AddLoader(attr_info, 1, citro3d_sys::GPU_FLOAT, 2); // v1=texcoord | |
citro3d_sys::AttrInfo_AddLoader(attr_info, 2, citro3d_sys::GPU_FLOAT, 3); // v2=normal | |
// Create the vertex buffer object | |
let vbo_data: *mut Vertex = citro3d_sys::linearAlloc(std::mem::size_of_val(&VERTICES)).cast(); | |
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.cast(), | |
std::mem::size_of::<Vertex>() | |
.try_into() | |
.expect("size of Vertex fits in u32"), // TODO: why not unwrap since we know it is always true? | |
3, // Each vertex has three attributes | |
0x210, // // 4 = 0x3210, 2 = 0x10 so assuming 3 = 0x210 | |
); | |
// 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 | |
if false { | |
let env = citro3d_sys::C3D_GetTexEnv(0); | |
citro3d_sys::C3D_TexEnvInit(env); | |
citro3d_sys::C3D_TexEnvSrc( | |
env, | |
citro3d_sys::C3D_Both, | |
citro3d_sys::GPU_PRIMARY_COLOR, | |
0, | |
0, | |
); | |
citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, citro3d_sys::GPU_REPLACE); | |
} else { | |
let mut diffuse_tex = | |
load_texture_from_mem("diffuse.rgb"); | |
let mut normal_tex = | |
load_texture_from_mem("normal.rgb"); | |
pub fn C3D_TexSetFilter( | |
tex: &mut C3D_Tex, | |
mag_filter: GPU_TEXTURE_FILTER_PARAM, | |
min_filter: GPU_TEXTURE_FILTER_PARAM, | |
) { | |
macro_rules! gpu_texture_mag_filter { | |
($v:expr) => { | |
((($v) & 0x1) << 1) | |
}; | |
} | |
macro_rules! gpu_texture_min_filter { | |
($v:expr) => { | |
((($v) & 0x1) << 2) | |
}; | |
} | |
tex.param &= !(gpu_texture_mag_filter!(citro3d_sys::GPU_LINEAR) | |
| gpu_texture_min_filter!(citro3d_sys::GPU_LINEAR)); | |
tex.param |= | |
gpu_texture_mag_filter!(mag_filter) | gpu_texture_min_filter!(min_filter); | |
} | |
C3D_TexSetFilter( | |
&mut diffuse_tex, | |
citro3d_sys::GPU_LINEAR, | |
citro3d_sys::GPU_NEAREST, | |
); | |
C3D_TexSetFilter( | |
&mut normal_tex, | |
citro3d_sys::GPU_LINEAR, | |
citro3d_sys::GPU_NEAREST, | |
); | |
citro3d_sys::C3D_TexBind(0, &mut diffuse_tex); | |
citro3d_sys::C3D_TexBind(1, &mut normal_tex); | |
} | |
( | |
UniformLocations { | |
projection: uloc_projection as i32, | |
model_view: uloc_model_view as i32, | |
}, | |
vbo_data.cast(), | |
) | |
} | |
} | |
fn scene_render(uniforms: &UniformLocations, projection: &C3D_Mtx, model_view: &C3D_Mtx) { | |
unsafe { | |
// Update the uniforms | |
citro3d_sys::C3D_FVUnifMtx4x4( | |
citro3d_sys::GPU_VERTEX_SHADER, | |
uniforms.projection, | |
projection, | |
); | |
citro3d_sys::C3D_FVUnifMtx4x4( | |
citro3d_sys::GPU_VERTEX_SHADER, | |
uniforms.model_view, | |
model_view, | |
); | |
// Draw the VBO | |
citro3d_sys::C3D_DrawArrays( | |
citro3d_sys::GPU_TRIANGLES, | |
0, | |
VERTICES | |
.len() | |
.try_into() | |
.expect("VERTICES.len() fits in i32"), | |
); | |
} | |
} | |
fn scene_exit(vbo_data: *mut libc::c_void) { | |
unsafe { | |
citro3d_sys::linearFree(vbo_data); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment