### Complete Rendering Pipeline Example Source: https://context7.com/rust3ds/citro3d-rs/llms.txt This Rust code demonstrates a full rendering pipeline using Citro3D, from initializing graphics and input services to drawing a textured triangle with stereoscopic 3D. It requires specific shader and texture assets. ```rust #![feature(allocator_api)] use citro3d::macros::include_shader; use citro3d::math::{AspectRatio, ClipPlanes, Matrix4, Projection, StereoDisplacement}; use citro3d::render::{ClearFlags, Target}; use citro3d::{attrib, buffer, shader, texenv, texture, Instance}; use ctru::prelude::*; use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D}; static SHADER_BYTES: &[u8] = include_shader!("assets/vshader.pica"); static TEXTURE_BYTES: &[u8] = include_bytes!("assets/texture.t3d"); const CLEAR_COLOR: u32 = 0x68B0D8FF; #[repr(C)] #[derive(Copy, Clone)] struct Vertex { pos: [f32; 3], tex: [f32; 2], } static VERTICES: &[Vertex] = &[ Vertex { pos: [-0.5, 0.5, -3.0], tex: [0.0, 1.0] }, Vertex { pos: [-0.5, -0.5, -3.0], tex: [0.0, 0.0] }, Vertex { pos: [0.5, -0.5, -3.0], tex: [1.0, 0.0] }, Vertex { pos: [0.5, 0.5, -3.0], tex: [1.0, 1.0] }, ]; fn main() { 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 = Instance::new().expect("failed to initialize Citro3D"); // Setup render targets 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 left_target = instance.render_target(width, height, top_left, None).unwrap(); let RawFrameBuffer { width, height, .. } = top_right.raw_framebuffer(); let mut right_target = instance.render_target(width, height, top_right, None).unwrap(); // Load shader and get uniform location let shader_lib = shader::Library::from_bytes(SHADER_BYTES).unwrap(); let program = shader::Program::new(shader_lib.get(0).unwrap()).unwrap(); let proj_uniform = program.get_uniform("projection").unwrap(); // Setup vertex attributes and buffers let mut attr_info = attrib::Info::new(); attr_info.add_loader(attrib::Register::V0, attrib::Format::Float, 3).unwrap(); attr_info.add_loader(attrib::Register::V1, attrib::Format::Float, 2).unwrap(); let vbo = buffer::Buffer::new(VERTICES); let mut buf_info = buffer::Info::new(); buf_info.add(vbo, attr_info.permutation()).unwrap(); // Load texture let tex = texture::Tex3DSTexture::new(TEXTURE_BYTES, false).unwrap().into_texture(); // Setup texenv for textured rendering let texenv_stage = texenv::TexEnv::new() .src(texenv::Mode::BOTH, texenv::Source::Texture0, None, None) .func(texenv::Mode::BOTH, texenv::CombineFunc::Replace); while apt.main_loop() { hid.scan_input(); if hid.keys_down().contains(KeyPad::START) { break; } // Calculate stereo projections let slider = ctru::os::current_3d_slider_state(); let (left_disp, right_disp) = StereoDisplacement::new(slider / 2.0, 2.0); let (left_proj, right_proj) = Projection::perspective( 40.0_f32.to_radians(), AspectRatio::TopScreen, ClipPlanes { near: 0.01, far: 100.0 }, ).stereo_matrices(left_disp, right_disp); instance.render_frame_with(|mut frame| { frame.bind_program(&program); frame.set_attr_info(&attr_info); frame.set_texenvs(&[texenv_stage]); frame.bind_texture(texture::Index::Texture0, &tex); // Render left eye left_target.clear(ClearFlags::ALL, CLEAR_COLOR, 0); frame.select_render_target(&left_target).unwrap(); frame.bind_vertex_uniform(proj_uniform, &left_proj); frame.draw_arrays(buffer::Primitive::TriangleFan, &buf_info, None).unwrap(); // Render right eye right_target.clear(ClearFlags::ALL, CLEAR_COLOR, 0); frame.select_render_target(&right_target).unwrap(); frame.bind_vertex_uniform(proj_uniform, &right_proj); frame.draw_arrays(buffer::Primitive::TriangleFan, &buf_info, None).unwrap(); frame }); } } ``` -------------------------------- ### Render to Texture Example Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Demonstrates rendering a scene to a texture in VRAM, which can then be used in subsequent rendering passes. Requires a `citro3d::Instance`. ```rust use citro3d::texture::{Texture, TextureParameters, ColorFormat, Face, Index as TexIndex}; use citro3d::render::{DepthFormat, TextureTarget, Target, ClearFlags}; use citro3d::Instance; fn render_to_texture_example(instance: &mut Instance) { // Create texture in VRAM for render target let params = TextureParameters::new_2d_in_vram(256, 256, ColorFormat::Rgba8); let texture = Texture::new(params).unwrap(); // Create render target from texture let mut texture_target = instance .render_target_texture(texture, Face::default(), Some(DepthFormat::Depth24)) .unwrap(); // Render scene to texture instance.render_frame_with(|mut frame| { texture_target.clear(ClearFlags::ALL, 0x000000FF, 0); frame.select_render_target(&texture_target).unwrap(); // ... render scene to texture ... frame }); // Access the rendered texture let rendered_texture = texture_target.texture(); // Use rendered texture in another pass instance.render_frame_with(|mut frame| { // Select screen target... frame.bind_texture(TexIndex::Texture0, rendered_texture); // ... draw quad with rendered texture ... frame }); } ``` -------------------------------- ### Configure Texture Environment (TexEnv) Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Examples for setting up TexEnv stages to control fragment color combination. Up to 6 stages can be applied to the frame. ```rust use citro3d::texenv::{TexEnv, Mode, Source, CombineFunc, RGBOp, AlphaOp, Scale}; // Simple pass-through of vertex color (no texture) fn vertex_color_texenv() -> TexEnv { TexEnv::new() .src(Mode::BOTH, Source::PrimaryColor, None, None) .func(Mode::BOTH, CombineFunc::Replace) } // Texture-only rendering fn texture_only_texenv() -> TexEnv { TexEnv::new() .src(Mode::BOTH, Source::Texture0, None, None) .func(Mode::BOTH, CombineFunc::Replace) } // Modulate texture with vertex color fn modulated_texenv() -> TexEnv { TexEnv::new() .src(Mode::BOTH, Source::Texture0, Some(Source::PrimaryColor), None) .func(Mode::BOTH, CombineFunc::Modulate) } // Complex multi-stage setup fn multi_stage_texenv() -> [TexEnv; 2] { let stage0 = TexEnv::new() .src(Mode::RGB, Source::Texture0, Some(Source::PrimaryColor), None) .src(Mode::ALPHA, Source::Texture0, None, None) .func(Mode::RGB, CombineFunc::Modulate) .func(Mode::ALPHA, CombineFunc::Replace); let stage1 = TexEnv::new() .src(Mode::BOTH, Source::Previous, Some(Source::Constant), None) .func(Mode::BOTH, CombineFunc::Add) .color(0xFF0000FF) // Red constant color .scale(Mode::RGB, Scale::X2); // Double RGB output [stage0, stage1] } // Apply texenvs in render loop: fn apply_texenvs(frame: &mut citro3d::render::Frame) { let stages = multi_stage_texenv(); frame.set_texenvs(&stages); } ``` -------------------------------- ### Configure Hardware Lighting Environment Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Sets up a lighting environment with material properties, a light source, and a specular lookup table. Ensure the `citro3d` crate is included in your project. ```rust use std::pin::Pin; use citro3d::light::{ LightEnv, Light, LightIndex, Material, Lut, LutId, LutInput, Spotlight, DistanceAttenuation, FresnelSelector, }; use citro3d::color::Color; use citro3d::math::FVec3; fn setup_lighting() -> Pin> { // Create pinned lighting environment let mut env = LightEnv::new_pinned(); // Set material properties env.as_mut().set_material(Material { ambient: Some(Color::new(0.1, 0.1, 0.1)), diffuse: Some(Color::new(0.8, 0.8, 0.8)), specular0: Some(Color::new(1.0, 1.0, 1.0)), specular1: None, emission: None, }); // Create a light source let light_idx = env.as_mut().create_light().unwrap(); // Configure the light if let Some(mut light) = env.as_mut().light_mut(light_idx) { light.as_mut().set_position(FVec3::new(0.0, 10.0, 10.0)); light.as_mut().set_color(Color::new(1.0, 1.0, 1.0)); light.as_mut().set_enabled(true); } // Add specular LUT for shiny materials (Phong-like) let phong_lut = Lut::from_fn(|x| x.powf(30.0), false); env.as_mut().connect_lut(LutId::D0, LutInput::NormalHalf, phong_lut); env } ``` -------------------------------- ### Initialize Instance and Render Targets Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Initializes the Citro3D instance and sets up render targets for the 3DS screens. Ensure the GFX and APT controllers are initialized before creating the instance. ```rust use citro3d::Instance; use citro3d::render::{DepthFormat, ScreenTarget, Target, ClearFlags}; use ctru::prelude::*; use ctru::services::gfx::{RawFrameBuffer, Screen, TopScreen3D}; fn main() { let gfx = Gfx::new().expect("Couldn't obtain GFX controller"); let apt = Apt::new().expect("Couldn't obtain APT controller"); // Initialize citro3d instance let mut instance = Instance::new().expect("failed to initialize Citro3D"); // Or with custom command buffer size // let mut instance = Instance::with_cmdbuf_size(0x80000).unwrap(); // Create render target for top screen (3D capable) 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) // None = no depth buffer .expect("failed to create render target"); // Create render target with depth buffer let RawFrameBuffer { width, height, .. } = top_right.raw_framebuffer(); let mut top_right_target = instance .render_target(width, height, top_right, Some(DepthFormat::Depth24Stencil8)) .expect("failed to create render target"); // Create bottom screen 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"); // Main render loop while apt.main_loop() { instance.render_frame_with(|mut frame| { // Clear the render target top_left_target.clear(ClearFlags::ALL, 0x68B0D8FF, 0); // Select render target for drawing frame.select_render_target(&top_left_target).unwrap(); // ... perform draw calls ... frame }); } } ``` -------------------------------- ### Create 3D Transformation Matrices and Projections Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Demonstrates creating perspective, orthographic, and look-at matrices, along with basic matrix transformations and utility operations. ```rust use citro3d::math::{ Matrix4, Projection, ClipPlanes, AspectRatio, StereoDisplacement, CoordinateOrientation, ScreenOrientation, FVec3, FVec4, }; use std::f32::consts::PI; // Create perspective projection for 3DS screens fn create_projections() -> (Matrix4, Matrix4, Matrix4) { let clip_planes = ClipPlanes { near: 0.01, far: 100.0, }; let vertical_fov = 40.0_f32.to_radians(); // Get current 3D slider position let slider_val = ctru::os::current_3d_slider_state(); let interocular_distance = slider_val / 2.0; let screen_depth = 2.0; // Create stereo displacement for left/right eyes let (left, right) = StereoDisplacement::new(interocular_distance, screen_depth); // Create stereo projection matrices for top screen let (left_eye, right_eye) = Projection::perspective( vertical_fov, AspectRatio::TopScreen, clip_planes, ).stereo_matrices(left, right); // Create mono projection for bottom screen let center: Matrix4 = Projection::perspective( vertical_fov, AspectRatio::BottomScreen, clip_planes, ).into(); (left_eye, right_eye, center) } // Orthographic projection (for 2D rendering) fn create_orthographic() -> Matrix4 { Projection::orthographic( 0.0..240.0, // X range 0.0..400.0, // Y range ClipPlanes { near: 0.0, far: 100.0 }, ) .screen(ScreenOrientation::None) .into() } // Custom coordinate orientation fn left_handed_projection() -> Matrix4 { Projection::perspective( PI / 4.0, AspectRatio::TopScreen, ClipPlanes { near: 0.1, far: 100.0 }, ) .coordinates(CoordinateOrientation::LeftHanded) .into() } // Matrix transformations fn transform_matrix() -> Matrix4 { let mut mtx = Matrix4::identity(); // Apply transformations mtx.translate(0.0, 0.0, -5.0); // Move back mtx.rotate_y(0.5); // Rotate around Y axis mtx.scale(2.0, 2.0, 2.0); // Scale uniformly mtx } // Look-at matrix for camera positioning fn camera_matrix() -> Matrix4 { Matrix4::looking_at( FVec3::new(0.0, 1.0, 5.0), // Camera position FVec3::new(0.0, 0.0, 0.0), // Look at origin FVec3::new(0.0, 1.0, 0.0), // Up vector CoordinateOrientation::RightHanded, ) } // Matrix math utilities fn matrix_utilities() { let a = Matrix4::identity(); let b = Matrix4::diagonal(1.0, 2.0, 3.0, 1.0); // Transpose let transposed = a.transpose(); // Inverse (returns Err if no inverse exists) let inverted = b.inverse().unwrap_or(b); // Access rows let rows = a.rows_xyzw(); // [[f32; 4]; 4] in XYZW order } ``` -------------------------------- ### Configure Spotlight Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Configures a specific light source to act as a spotlight with a defined cutoff angle. Requires an existing `LightEnv` and `LightIndex`. ```rust use citro3d::light::{LightEnv, LightIndex, Spotlight}; use citro3d::math::FVec3; // Spotlight configuration fn setup_spotlight(env: Pin<&mut LightEnv>, light_idx: LightIndex) { if let Some(mut light) = env.light_mut(light_idx) { // Create spotlight with cutoff angle let spotlight = Spotlight::with_cutoff(0.5); // radians light.as_mut().set_spotlight(Some(spotlight)); light.as_mut().set_spotlight_direction(FVec3::new(0.0, -1.0, 0.0)); } } ``` -------------------------------- ### Compile and Load Shader Program Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Use the include_shader! macro to compile .pica shader source files at build time. Load the compiled shader library and create a program, then look up uniform indices by name. ```rust use citro3d::macros::include_shader; use citro3d::shader::{Library, Program, Entrypoint}; use citro3d::uniform; // Compile shader at build time (path relative to source file) static SHADER_BYTES: &[u8] = include_shader!("assets/vshader.pica"); fn setup_shader() -> (Program, uniform::Index) { // Parse the shader library from compiled bytes let shader = Library::from_bytes(SHADER_BYTES).unwrap(); // Get the vertex shader entrypoint (index 0) let vertex_shader: Entrypoint = shader.get(0).unwrap(); // Create a shader program from the vertex shader let program = Program::new(vertex_shader).unwrap(); // Look up uniform index by name (must match .pica shader) let projection_uniform_idx = program.get_uniform("projection").unwrap(); // For geometry shaders: // let geometry_shader = shader.get(1).unwrap(); // program.set_geometry_shader(geometry_shader, stride).unwrap(); (program, projection_uniform_idx) } ``` ```pica ; Uniforms .fvec projection[4] ; Inputs .alias inpos v0 .alias incolor v1 ; Outputs .out outpos position .out outclr color .proc main dp4 outpos.x, projection[0], inpos dp4 outpos.y, projection[1], inpos dp4 outpos.z, projection[2], inpos dp4 outpos.w, projection[3], inpos mov outclr, incolor end .end ``` -------------------------------- ### Configure Vertex Attributes and Buffers Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Define vertex structures with #[repr(C)] for predictable layout and create attribute info describing how vertex data maps to shader input registers. Allocate buffers in linear memory for GPU access. ```rust use citro3d::attrib::{Info as AttrInfo, Register, Format, Permutation}; use citro3d::buffer::{Info as BufInfo, Buffer, Primitive}; // Define vertex structure (must be #[repr(C)] for predictable layout) #[repr(C)] #[derive(Copy, Clone)] struct Vertex { pos: [f32; 3], // Maps to v0 color: [f32; 3], // Maps to v1 } // Define vertices static VERTICES: &[Vertex] = &[ Vertex { pos: [0.0, 0.5, -3.0], color: [1.0, 0.0, 0.0] }, Vertex { pos: [-0.5, -0.5, -3.0], color: [0.0, 1.0, 0.0] }, Vertex { pos: [0.5, -0.5, -3.0], color: [0.0, 0.0, 1.0] }, ]; fn setup_vertex_buffers() -> (AttrInfo, BufInfo) { // Create attribute info describing vertex layout let mut attr_info = AttrInfo::new(); // Add loader for position (3 floats -> register v0) attr_info .add_loader(Register::V0, Format::Float, 3) .unwrap(); // Add loader for color (3 floats -> register v1) attr_info .add_loader(Register::V1, Format::Float, 3) .unwrap(); // Create buffer in linear memory (required for GPU access) let vbo_data = Buffer::new(VERTICES); // Create buffer info and register the VBO let mut buf_info = BufInfo::new(); buf_info.add(vbo_data, attr_info.permutation()).unwrap(); (attr_info, buf_info) } ``` ```rust // For non-contiguous vertex data or custom layouts: fn custom_permutation_example() { // When shader expects: v0=position, v1=normal, v2=texcoord // But buffer has: position, texcoord (no normals) let permutation = Permutation::from_layout(&[Register::V0, Register::V2]).unwrap(); } ``` -------------------------------- ### Bind Lighting Environment Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Binds the configured lighting environment to the current frame for rendering. This should be called within the render loop. ```rust use citro3d::render::Frame; use citro3d::light::LightEnv; // Bind lighting in render loop fn bind_lighting(frame: &mut Frame, env: Pin<&mut LightEnv>) { frame.bind_light_env(Some(env)); } ``` -------------------------------- ### Bind Shader Uniforms Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Shows how to bind various data types like matrices, vectors, and booleans to vertex or geometry shader uniform registers. ```rust use citro3d::uniform::{Index, Uniform}; use citro3d::math::{Matrix4, FVec4, IVec}; fn bind_uniforms( frame: &mut citro3d::render::Frame, projection_idx: Index, model_idx: Index, color_idx: Index, ) { // Bind 4x4 matrix uniform let projection = Matrix4::identity(); frame.bind_vertex_uniform(projection_idx, &projection); // Bind float vector uniform let color = FVec4::new(1.0, 0.5, 0.25, 1.0); frame.bind_vertex_uniform(color_idx, color); // Bind integer vector uniform let int_vec = IVec::new(1, 2, 3, 4); frame.bind_vertex_uniform(Index::from(0x60), int_vec); // Bind boolean uniform frame.bind_vertex_uniform(Index::from(0x68), true); // Bind to geometry shader instead of vertex shader frame.bind_geometry_uniform(projection_idx, &projection); } // Uniform index ranges: // Float uniforms (.fvec): 0x00 - 0x5F // Integer uniforms (.ivec): 0x60 - 0x63 // Boolean uniforms (.bool): 0x68 - 0x78 ``` -------------------------------- ### Configure Distance Attenuation Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Applies distance-based attenuation to a light source, making it dimmer with distance. Requires an existing `LightEnv` and `LightIndex`. ```rust use citro3d::light::{LightEnv, LightIndex, DistanceAttenuation}; // Distance attenuation fn setup_distance_attenuation(env: Pin<&mut LightEnv>, light_idx: LightIndex) { if let Some(mut light) = env.light_mut(light_idx) { // Light attenuates from distance 1.0 to 50.0 let attenuation = DistanceAttenuation::new(1.0..50.0, |dist| { 1.0 / (1.0 + 0.1 * dist + 0.01 * dist * dist) }); light.as_mut().set_distance_attenutation(Some(attenuation)); } } ``` -------------------------------- ### Texture Loading and Binding Operations Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Provides functions for loading Tex3DS files, creating manual textures, generating mipmaps, allocating VRAM textures, and binding textures to render units. ```rust use citro3d::texture::{ Texture, Tex3DSTexture, TextureParameters, ColorFormat, Filter, Wrap, Face, Index as TexIndex, Mode }; // Load texture from Tex3DS format (generated by tex3ds tool) static TEXTURE_BYTES: &[u8] = include_bytes!("assets/kitten.t3d"); fn load_tex3ds_texture() -> Texture { let tex3ds = Tex3DSTexture::new(TEXTURE_BYTES, false).unwrap(); let mut texture = tex3ds.into_texture(); // Configure filtering texture.set_filter(Filter::Linear, Filter::Linear); texture.set_wrap(Wrap::Repeat, Wrap::Repeat); texture } // Create texture manually fn create_manual_texture(width: u16, height: u16, pixels: &[u8]) -> Texture { let params = TextureParameters::new_2d(width, height, ColorFormat::Rgba8); let mut texture = Texture::new(params).unwrap(); // Upload pixel data (must be in GPU-swizzled format) texture.load_image(pixels, Face::default()).unwrap(); // Set filtering texture.set_filter(Filter::Linear, Filter::Nearest); texture.set_wrap(Wrap::ClampToEdge, Wrap::ClampToEdge); texture } // Create texture with mipmaps fn create_mipmapped_texture(width: u16, height: u16) -> Texture { let params = TextureParameters::new_2d_with_mipmap(width, height, ColorFormat::Rgba8); let mut texture = Texture::new(params).unwrap(); // Load base level and generate mipmaps // texture.load_image(base_pixels, Face::default()).unwrap(); texture.generate_mipmap(Face::default()); texture.set_filter_mipmap(Filter::Linear); texture } // Create VRAM texture (faster but limited memory) fn create_vram_texture(width: u16, height: u16) -> Texture { let params = TextureParameters::new_2d_in_vram(width, height, ColorFormat::Rgba8); Texture::new(params).unwrap() } // Bind texture in render loop fn bind_texture(frame: &mut citro3d::render::Frame, texture: &Texture) { // Bind to texture unit 0 (matches Source::Texture0 in texenv) frame.bind_texture(TexIndex::Texture0, texture); // Can bind up to 4 textures: // frame.bind_texture(TexIndex::Texture1, &other_texture); // frame.bind_texture(TexIndex::Texture2, &another_texture); // frame.bind_texture(TexIndex::Texture3, &final_texture); } ``` -------------------------------- ### Issue Draw Calls with citro3d Source: https://context7.com/rust3ds/citro3d-rs/llms.txt Demonstrates rendering a triangle using draw_arrays and indexed drawing with draw_elements. Indices for indexed drawing must be allocated in linear memory. ```rust use citro3d::buffer::Primitive; use citro3d::render::{Frame, Target, ClearFlags}; fn render_triangle( instance: &mut citro3d::Instance, target: &mut impl Target, program: &citro3d::shader::Program, attr_info: &citro3d::attrib::Info, buf_info: &citro3d::buffer::Info, projection_idx: citro3d::uniform::Index, projection: &citro3d::math::Matrix4, texenv_stage: &citro3d::texenv::TexEnv, ) { instance.render_frame_with(|mut frame| { // Clear render target target.clear(ClearFlags::ALL, 0x68B0D8FF, 0); // Select render target frame.select_render_target(target).unwrap(); // Bind shader program (required before any draw calls) frame.bind_program(program); // Set vertex attributes frame.set_attr_info(attr_info); // Bind uniform (projection matrix) frame.bind_vertex_uniform(projection_idx, projection); // Set texture environment stage frame.set_texenvs(&[*texenv_stage]); // Draw all vertices as triangles frame .draw_arrays(Primitive::Triangles, buf_info, None) .unwrap(); // Or draw a subset of vertices: // frame.draw_arrays(Primitive::Triangles, buf_info, Some(0..3)).unwrap(); frame }); } // Indexed drawing example: use ctru::linear::LinearAllocator; fn draw_indexed(frame: &mut Frame, buf_info: &citro3d::buffer::Info) { // Indices must be in linear memory let mut indices: Vec = Vec::with_capacity_in(6, LinearAllocator); indices.extend_from_slice(&[0, 1, 2, 2, 3, 0]); frame.draw_elements(Primitive::Triangles, buf_info, &indices); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.