-
Notifications
You must be signed in to change notification settings - Fork 14
feat: add indexed drawing #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ian-h-chamberlain
wants to merge
5
commits into
main
Choose a base branch
from
feature/indexed-drawing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
73b454f
feat: add indexed drawing
0x00002a bfff51d
fix: crashing citra
0x00002a 910f39d
chore: rename drawingindexecies -> indextype
0x00002a 8f9d70a
Use a checked index API and do a bit of cleanup
ian-h-chamberlain 8e9205d
Use a vertex buffer slice in draw_elements args
ian-h-chamberlain File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
//! This example demonstrates the most basic usage of `citro3d`: rendering a simple | ||
//! RGB triangle (sometimes called a "Hello triangle") to the 3DS screen. | ||
|
||
#![feature(allocator_api)] | ||
|
||
use citro3d::macros::include_shader; | ||
use citro3d::math::{ | ||
AspectRatio, ClipPlanes, CoordinateOrientation, FVec3, Matrix4, Projection, StereoDisplacement, | ||
}; | ||
use citro3d::render::ClearFlags; | ||
use citro3d::{attrib, buffer, render, shader, texenv}; | ||
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, | ||
} | ||
|
||
// borrowed from https://bevyengine.org/examples/3D%20Rendering/generate-custom-mesh/ | ||
const VERTS: &[[f32; 3]] = &[ | ||
// top (facing towards +y) | ||
[-0.5, 0.5, -0.5], // vertex with index 0 | ||
[0.5, 0.5, -0.5], // vertex with index 1 | ||
[0.5, 0.5, 0.5], // etc. until 23 | ||
[-0.5, 0.5, 0.5], | ||
// bottom (-y) | ||
[-0.5, -0.5, -0.5], | ||
[0.5, -0.5, -0.5], | ||
[0.5, -0.5, 0.5], | ||
[-0.5, -0.5, 0.5], | ||
// right (+x) | ||
[0.5, -0.5, -0.5], | ||
[0.5, -0.5, 0.5], | ||
[0.5, 0.5, 0.5], // This vertex is at the same position as vertex with index 2, but they'll have different UV and normal | ||
[0.5, 0.5, -0.5], | ||
// left (-x) | ||
[-0.5, -0.5, -0.5], | ||
[-0.5, -0.5, 0.5], | ||
[-0.5, 0.5, 0.5], | ||
[-0.5, 0.5, -0.5], | ||
// back (+z) | ||
[-0.5, -0.5, 0.5], | ||
[-0.5, 0.5, 0.5], | ||
[0.5, 0.5, 0.5], | ||
[0.5, -0.5, 0.5], | ||
// forward (-z) | ||
[-0.5, -0.5, -0.5], | ||
[-0.5, 0.5, -0.5], | ||
[0.5, 0.5, -0.5], | ||
[0.5, -0.5, -0.5], | ||
]; | ||
|
||
static SHADER_BYTES: &[u8] = include_shader!("assets/vshader.pica"); | ||
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 shader = shader::Library::from_bytes(SHADER_BYTES).unwrap(); | ||
let vertex_shader = shader.get(0).unwrap(); | ||
|
||
let program = shader::Program::new(vertex_shader).unwrap(); | ||
instance.bind_program(&program); | ||
let mut vbo_data = Vec::with_capacity_in(VERTS.len(), ctru::linear::LinearAllocator); | ||
for vert in VERTS.iter().enumerate().map(|(i, v)| Vertex { | ||
pos: Vec3 { | ||
x: v[0], | ||
y: v[1], | ||
z: v[2], | ||
}, | ||
color: { | ||
// Give each vertex a slightly different color just to highlight edges/corners | ||
let value = i as f32 / VERTS.len() as f32; | ||
Vec3::new(1.0, 0.7 * value, 0.5) | ||
}, | ||
}) { | ||
vbo_data.push(vert); | ||
} | ||
|
||
let mut buf_info = buffer::Info::new(); | ||
let (attr_info, vbo_slice) = prepare_vbos(&mut buf_info, &vbo_data); | ||
|
||
// 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 stage0 = texenv::Stage::new(0).unwrap(); | ||
instance | ||
.texenv(stage0) | ||
.src(texenv::Mode::BOTH, texenv::Source::PrimaryColor, None, None) | ||
.func(texenv::Mode::BOTH, texenv::CombineFunc::Replace); | ||
|
||
let projection_uniform_idx = program.get_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), | ||
FVec3::new(0.0, 1.0, 0.0), | ||
CoordinateOrientation::RightHanded, | ||
); | ||
let indices: &[u8] = &[ | ||
0, 3, 1, 1, 3, 2, // triangles making up the top (+y) facing side. | ||
4, 5, 7, 5, 6, 7, // bottom (-y) | ||
8, 11, 9, 9, 11, 10, // right (+x) | ||
12, 13, 15, 13, 14, 15, // left (-x) | ||
16, 19, 17, 17, 19, 18, // back (+z) | ||
20, 21, 23, 21, 22, 23, // forward (-z) | ||
]; | ||
let indices = vbo_slice.index_buffer(indices).unwrap(); | ||
|
||
while apt.main_loop() { | ||
hid.scan_input(); | ||
|
||
if hid.keys_down().contains(KeyPad::START) { | ||
break; | ||
} | ||
|
||
instance.render_frame_with(|instance| { | ||
let mut render_to = |target: &mut render::Target, projection| { | ||
target.clear(ClearFlags::ALL, CLEAR_COLOR, 0); | ||
|
||
instance | ||
.select_render_target(target) | ||
.expect("failed to set render target"); | ||
|
||
instance.bind_vertex_uniform(projection_uniform_idx, projection * camera_transform); | ||
|
||
instance.set_attr_info(&attr_info); | ||
unsafe { | ||
instance.draw_elements(buffer::Primitive::Triangles, &buf_info, &indices); | ||
} | ||
}; | ||
|
||
let Projections { | ||
left_eye, | ||
right_eye, | ||
center, | ||
} = calculate_projections(); | ||
|
||
render_to(&mut top_left_target, &left_eye); | ||
render_to(&mut top_right_target, &right_eye); | ||
render_to(&mut bottom_target, ¢er); | ||
}); | ||
} | ||
} | ||
|
||
fn prepare_vbos<'a>( | ||
buf_info: &'a mut buffer::Info, | ||
vbo_data: &'a [Vertex], | ||
) -> (attrib::Info, buffer::Slice<'a>) { | ||
// Configure attributes for use with the vertex shader | ||
let mut attr_info = attrib::Info::new(); | ||
|
||
let reg0 = attrib::Register::new(0).unwrap(); | ||
let reg1 = attrib::Register::new(1).unwrap(); | ||
|
||
attr_info | ||
.add_loader(reg0, attrib::Format::Float, 3) | ||
.unwrap(); | ||
|
||
attr_info | ||
.add_loader(reg1, attrib::Format::Float, 3) | ||
.unwrap(); | ||
|
||
let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap(); | ||
|
||
(attr_info, buf_idx) | ||
} | ||
|
||
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, | ||
} | ||
} |
This file contains hidden or 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
This file contains hidden or 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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.