Created
November 24, 2022 05:57
-
-
Save hYdos/c260e0afe72200a4872e7f9000ddae5d 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
#![feature(slice_as_chunks)] | |
mod utils; | |
use std::ffi::{CString}; | |
use std::fs::read; | |
use std::mem; | |
use std::mem::{MaybeUninit, size_of, size_of_val}; | |
use std::ptr::{null_mut}; | |
use citro3d_sys::*; | |
use ctru::prelude::*; | |
use ctru::romfs::RomFS; | |
use ctru_sys::gfxInitDefault; | |
use libc::c_int; | |
use crate::utils::{C3D_AngleFromDegrees, C3D_LightColor, C3D_TexSetFilter, color, LightLut_Phong, Mtx_Identity, newMatrix, wait}; | |
const CLEAR_COLOR: u32 = color(0xFF, 0xFF, 0xFF); | |
#[repr(C)] | |
struct Vertex { | |
position: [f32; 3], | |
texcoord: [f32; 2], | |
normal: [f32; 3], | |
} // Vertex Format Data | |
const VERTEX_LIST: [Vertex; 36] = [ | |
// First face (PZ) | |
// First triangle | |
Vertex { position: [-0.5, -0.5, 0.5], texcoord: [0.0, 0.0], normal: [0.0, 0.0, 1.0] }, | |
Vertex { position: [0.5, -0.5, 0.5], texcoord: [1.0, 0.0], normal: [0.0, 0.0, 1.0] }, | |
Vertex { position: [0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [0.0, 0.0, 1.0] }, | |
// Second triangle | |
Vertex { position: [0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [0.0, 0.0, 1.0] }, | |
Vertex { position: [-0.5, 0.5, 0.5], texcoord: [0.0, 1.0], normal: [0.0, 0.0, 1.0] }, | |
Vertex { position: [-0.5, -0.5, 0.5], texcoord: [0.0, 0.0], normal: [0.0, 0.0, 1.0] }, | |
// Second face (MZ) | |
// First triangle | |
Vertex { position: [-0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [0.0, 0.0, -1.0] }, | |
Vertex { position: [-0.5, 0.5, -0.5], texcoord: [1.0, 0.0], normal: [0.0, 0.0, -1.0] }, | |
Vertex { position: [0.5, 0.5, -0.5], texcoord: [1.0, 1.0], normal: [0.0, 0.0, -1.0] }, | |
// Second triangle | |
Vertex { position: [0.5, 0.5, -0.5], texcoord: [1.0, 1.0], normal: [0.0, 0.0, -1.0] }, | |
Vertex { position: [0.5, -0.5, -0.5], texcoord: [0.0, 1.0], normal: [0.0, 0.0, -1.0] }, | |
Vertex { position: [-0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [0.0, 0.0, -1.0] }, | |
// Third face (PX) | |
// First triangle | |
Vertex { position: [0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [1.0, 0.0, 0.0] }, | |
Vertex { position: [0.5, 0.5, -0.5], texcoord: [1.0, 0.0], normal: [1.0, 0.0, 0.0] }, | |
Vertex { position: [0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [1.0, 0.0, 0.0] }, | |
// Second triangle | |
Vertex { position: [0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [1.0, 0.0, 0.0] }, | |
Vertex { position: [0.5, -0.5, 0.5], texcoord: [0.0, 1.0], normal: [1.0, 0.0, 0.0] }, | |
Vertex { position: [0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [1.0, 0.0, 0.0] }, | |
// Fourth face (MX) | |
// First triangle | |
Vertex { position: [-0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [-1.0, 0.0, 0.0] }, | |
Vertex { position: [-0.5, -0.5, 0.5], texcoord: [1.0, 0.0], normal: [-1.0, 0.0, 0.0] }, | |
Vertex { position: [-0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [-1.0, 0.0, 0.0] }, | |
// Second triangle | |
Vertex { position: [-0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [-1.0, 0.0, 0.0] }, | |
Vertex { position: [-0.5, 0.5, -0.5], texcoord: [0.0, 1.0], normal: [-1.0, 0.0, 0.0] }, | |
Vertex { position: [-0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [-1.0, 0.0, 0.0] }, | |
// Fifth face (PY) | |
// First triangle | |
Vertex { position: [-0.5, 0.5, -0.5], texcoord: [0.0, 0.0], normal: [0.0, 1.0, 0.0] }, | |
Vertex { position: [-0.5, 0.5, 0.5], texcoord: [1.0, 0.0], normal: [0.0, 1.0, 0.0] }, | |
Vertex { position: [0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [0.0, 1.0, 0.0] }, | |
// Second triangle | |
Vertex { position: [0.5, 0.5, 0.5], texcoord: [1.0, 1.0], normal: [0.0, 1.0, 0.0] }, | |
Vertex { position: [0.5, 0.5, -0.5], texcoord: [0.0, 1.0], normal: [0.0, 1.0, 0.0] }, | |
Vertex { position: [-0.5, 0.5, -0.5], texcoord: [0.0, 0.0], normal: [0.0, 1.0, 0.0] }, | |
// Sixth face (MY) | |
// First triangle | |
Vertex { position: [-0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [0.0, -1.0, 0.0] }, | |
Vertex { position: [0.5, -0.5, -0.5], texcoord: [1.0, 0.0], normal: [0.0, -1.0, 0.0] }, | |
Vertex { position: [0.5, -0.5, 0.5], texcoord: [1.0, 1.0], normal: [0.0, -1.0, 0.0] }, | |
// Second triangle | |
Vertex { position: [0.5, -0.5, 0.5], texcoord: [1.0, 1.0], normal: [0.0, -1.0, 0.0] }, | |
Vertex { position: [-0.5, -0.5, 0.5], texcoord: [0.0, 1.0], normal: [0.0, -1.0, 0.0] }, | |
Vertex { position: [-0.5, -0.5, -0.5], texcoord: [0.0, 0.0], normal: [0.0, -1.0, 0.0] }, | |
]; | |
const VERTEX_LIST_COUNT: usize = VERTEX_LIST.len(); | |
const MATERIAL: C3D_Material = C3D_Material { | |
ambient: [0.0, 0.0, 0.0], | |
diffuse: [1.0, 1.0, 1.0], | |
specular0: [0.4, 0.4, 0.4], | |
specular1: [0.0, 0.0, 0.0], | |
emission: [0.0, 0.0, 0.0], | |
}; | |
struct UniformLocations { | |
projection: s8, | |
model_view: s8 | |
} | |
unsafe fn scene_init(hid: &Hid) -> UniformLocations { | |
// Load the Vertex shader, create a shader program and bind it | |
let mut shader_bytes = to_u32(read("romfs:/vshader.v.sbin").unwrap()); | |
let vshader_dvlb = DVLB_ParseFile(shader_bytes.as_mut_ptr(), shader_bytes.len() as u32); | |
let mut _program = MaybeUninit::uninit(); | |
assert_eq!(shaderProgramInit(_program.as_mut_ptr()), 0); | |
let mut program = _program.assume_init(); | |
assert_eq!(shaderProgramSetVsh(&mut program, (*vshader_dvlb).DVLE), 0); | |
C3D_BindProgram(&mut program); | |
// Get the location of the uniforms | |
let projection_str = CString::new("projection").unwrap(); | |
let model_view_str = CString::new("modelView").unwrap(); | |
let u_loc_projection = shaderInstanceGetUniformLocation(program.vertexShader, projection_str.as_ptr()); | |
let u_loc_model_view = shaderInstanceGetUniformLocation(program.vertexShader, model_view_str.as_ptr()); | |
// Configure attributes for use with the Vertex shader | |
let attr_info = C3D_GetAttrInfo(); | |
println!("{attr_info:?}"); | |
wait(hid); | |
AttrInfo_Init(attr_info); | |
AttrInfo_AddLoader(attr_info, 0, GPU_FLOAT, 3); // v0=position | |
AttrInfo_AddLoader(attr_info, 1, GPU_FLOAT, 2); // v1=texcoord | |
AttrInfo_AddLoader(attr_info, 2, GPU_FLOAT, 3); // v2=normal | |
// Create the VBO (vertex buffer object) | |
let vbo_data = linearAlloc(size_of_val(&VERTEX_LIST)); | |
std::ptr::copy(VERTEX_LIST.as_ptr(), vbo_data.cast(), size_of_val(&VERTEX_LIST)); | |
// Configure buffers | |
let buf_info = C3D_GetBufInfo(); | |
BufInfo_Init(buf_info); | |
BufInfo_Add(buf_info, vbo_data, size_of::<Vertex>() as isize, 4, 0x3210); | |
// Load the textures and bind them to their respective texture units | |
let mut diffuse_tex = load_texture_from_mem("diffuse.rgb"); | |
let mut normal_tex = load_texture_from_mem("normal.rgb"); | |
C3D_TexSetFilter(&mut diffuse_tex, GPU_LINEAR, GPU_NEAREST); | |
C3D_TexSetFilter(&mut normal_tex, GPU_LINEAR, GPU_NEAREST); | |
C3D_TexBind(0, &mut diffuse_tex as *mut _); | |
C3D_TexBind(1, &mut normal_tex as *mut _); | |
// Configure the texenv stages: | |
// 1) Combine primary fragment lighting color with texture color. | |
// 2) Add secondary fragment lighting color (specular component). | |
let mut env = C3D_GetTexEnv(0); | |
C3D_TexEnvInit(env); | |
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_FRAGMENT_PRIMARY_COLOR, 0); | |
C3D_TexEnvFunc(env, C3D_RGB, GPU_MODULATE); | |
env = C3D_GetTexEnv(1); | |
C3D_TexEnvInit(env); | |
C3D_TexEnvSrc(env, C3D_Both, GPU_PREVIOUS, GPU_FRAGMENT_SECONDARY_COLOR, 0); | |
C3D_TexEnvFunc(env, C3D_RGB, GPU_ADD); | |
let mut light_env = { | |
let mut _light_env = MaybeUninit::uninit(); | |
C3D_LightEnvInit(_light_env.as_mut_ptr()); | |
_light_env.assume_init() | |
}; | |
C3D_LightEnvBind(&mut light_env); | |
C3D_LightEnvMaterial(&mut light_env, &MATERIAL); | |
C3D_LightEnvBumpMode(&mut light_env, GPU_BUMP_AS_BUMP); | |
C3D_LightEnvBumpSel(&mut light_env, 1); | |
let mut lut_phong = { | |
let mut _lut_phong = MaybeUninit::uninit(); | |
LightLut_Phong(_lut_phong.assume_init_mut(), 30.0); | |
_lut_phong.assume_init() | |
}; | |
C3D_LightEnvLut(&mut light_env, GPU_LUT_D0, GPU_LUTINPUT_LN, false, &mut lut_phong); | |
let mut light_vec = FVec4_New!(0.0, 0.0, -0.5, 1.0); | |
let mut light = { | |
let mut _light = MaybeUninit::uninit(); | |
C3D_LightInit(_light.as_mut_ptr(), &mut light_env); | |
_light.assume_init() | |
}; | |
C3D_LightColor(&mut light, 1.0, 1.0, 1.0); | |
C3D_LightPosition(&mut light, &mut light_vec); | |
UniformLocations { | |
projection: u_loc_projection, | |
model_view: u_loc_model_view | |
} | |
} | |
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(); | |
let _console = Console::init(gfx.bottom_screen.borrow_mut()); | |
unsafe { | |
// Setup matrices used in render loop | |
let mut projection = newMatrix(); | |
Mtx_Identity(&mut projection); | |
gfx.top_screen.borrow_mut().set_3d_enabled(true); | |
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE as usize); | |
// Initialize the render targets | |
let display_transfer_flags = GX_TRANSFER_FLIP_VERT(false) | GX_TRANSFER_OUT_TILED(false) | GX_TRANSFER_RAW_COPY(false) | | |
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | | |
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO); | |
let target_left = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, C3D_DEPTHTYPE { __e: GPU_RB_DEPTH24_STENCIL8 }); | |
C3D_RenderTargetSetOutput(target_left, GFX_TOP, GFX_LEFT, display_transfer_flags); | |
let uniform_locs = scene_init(&hid ); | |
let mut angle_x = 0.0; | |
let mut angle_y = 0.0; | |
// Main loop | |
println!("Render Loop Start"); | |
while apt.main_loop() { | |
println!("Frame 2"); | |
if hid.keys_held().contains(KeyPad::KEY_START | KeyPad::KEY_SELECT) { | |
break; // break in order to return to hbmenu | |
} | |
// Rotate the cube each frame | |
angle_x += C3D_AngleFromDegrees(1.0); | |
angle_y += C3D_AngleFromDegrees(0.5); | |
// Render the scene | |
C3D_FrameBegin(C3D_FRAME_SYNCDRAW as u8_); | |
{ | |
C3D_RenderTargetClear(target_left, C3D_CLEAR_ALL, CLEAR_COLOR, 0); | |
C3D_FrameDrawOn(target_left); | |
// Compute the projection matrix | |
Mtx_PerspStereoTilt(&mut projection, C3D_AngleFromDegrees(40.0) /* TODO: 90 */, C3D_AspectRatioTop as f32, 0.01, 1000.0, 0.0, 2.0, false); | |
// Calculate the modelView matrix | |
let mut model_view = newMatrix(); | |
Mtx_Identity(&mut model_view); | |
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); | |
// Update the uniforms | |
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uniform_locs.projection as c_int, &projection); | |
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uniform_locs.model_view as c_int, &model_view); | |
// Draw the VBO | |
C3D_DrawArrays(GPU_TRIANGLES, 0, VERTEX_LIST_COUNT as c_int); | |
} | |
C3D_FrameEnd(0); | |
println!("Frame End") | |
} | |
println!("Exit"); | |
C3D_Fini(); | |
} | |
} | |
unsafe fn load_texture_from_mem(path: &str) -> C3D_Tex { | |
let rom_path = format!("romfs:/{path}"); | |
let bytes = read(&rom_path).unwrap_or_else(|_| panic!("Failed to read {rom_path}")); | |
let mut _tex = MaybeUninit::uninit(); | |
let t3x = Tex3DS_TextureImport(bytes.as_ptr() as *const _, bytes.len(), _tex.as_mut_ptr(), null_mut(), false); | |
let tex = _tex.assume_init(); | |
// Delete the t3x object since we don't need it | |
Tex3DS_TextureFree(t3x); | |
tex | |
} | |
fn to_u32(mut items: Vec<u8>) -> Vec<u32> { | |
assert_eq!(items.len() % size_of::<u32>(), 0); | |
assert_eq!(items.capacity() % size_of::<u32>(), 0); | |
let length = items.len() / size_of::<u32>(); | |
let capacity = items.capacity() / size_of::<u32>(); | |
let ptr = items.as_mut_ptr() as *mut u32; | |
// Don't run the destructor for vec32 | |
mem::forget(items); | |
// SAFETY: Should be | |
unsafe { Vec::from_raw_parts(ptr, length, capacity) } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment