Skip to content

Instantly share code, notes, and snippets.

@hYdos
Created November 24, 2022 05:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hYdos/c260e0afe72200a4872e7f9000ddae5d to your computer and use it in GitHub Desktop.
Save hYdos/c260e0afe72200a4872e7f9000ddae5d to your computer and use it in GitHub Desktop.
#![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