extern crate sdl2; use sdl2::event::Event; use sdl2::keyboard::Keycode; use sdl2::pixels::Color; use sdl2::rect::FPoint; use sdl2::render::{RenderGeometryTextureParams, Vertex, VertexIndices}; use std::mem::offset_of; use std::thread; use std::time::Duration; fn main() { let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); let window = video_subsystem .window("Rust SDL2 render_geometry custom struct example", 800, 600) .position_centered() .opengl() .build() .unwrap(); let mut canvas = window.into_canvas().build().unwrap(); let mut event_pump = sdl_context.event_pump().unwrap(); let mut running = true; while running { for event in event_pump.poll_iter() { match event { Event::Quit { .. } | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { running = false; } _ => {} } } // black background canvas.set_draw_color(Color::BLACK); canvas.clear(); // First, draw a triangle using `render_geometry`. The `tex_coord` fields are unused but // must be provided, `render_geometry` only supports `sdl2::render::Vertex`. let vertices = [ Vertex { position: FPoint::new(100.0, 200.0), color: Color::RED, tex_coord: FPoint::new(0.0, 0.0), }, Vertex { position: FPoint::new(200.0, 200.0), color: Color::GREEN, tex_coord: FPoint::new(0.0, 0.0), }, Vertex { position: FPoint::new(150.0, 100.0), color: Color::BLUE, tex_coord: FPoint::new(0.0, 0.0), }, ]; canvas .render_geometry(&vertices, None, VertexIndices::Sequential) .expect("render_geometry failed (probably unsupported, see error message)"); // `render_geometry_raw` supports any custom struct as long as it contains the needed data // (or other layout compatible of the needed data). // The struct does not need to be `repr(C)` or `Copy` for example. struct MyVertex { // For demonstration purposes color is `[u8; 4]` here. `[u8; 4]` is layout-compatible // with `sdl2::pixels::Color` color: [u8; 4], // The struct may contain data not needed by SDL. #[expect(dead_code)] foo: Vec, // When defining your own vertex struct, using `FPoint` for position and tex_coord // (and `Color` for color) is the easiest way. These are obviously layout-compatible // with `FPoint` and `Color`, respectively. pos: FPoint, } // Define the vertices of a square let vertices = [ MyVertex { color: [0, 0, 0, 0xff], foo: b"some".to_vec(), pos: FPoint::new(300.0, 100.0), }, MyVertex { color: [0, 0xff, 0, 0xff], foo: b"unrelated".to_vec(), pos: FPoint::new(400.0, 100.0), }, MyVertex { color: [0xff, 0, 0, 0xff], foo: b"data".to_vec(), pos: FPoint::new(300.0, 200.0), }, MyVertex { color: [0xff, 0xff, 0, 0xff], foo: b"!".to_vec(), pos: FPoint::new(400.0, 200.0), }, ]; // A square is rendered as two triangles (see indices) // SAFETY: core::mem::offset_of makes sure the offsets are right and alignment is respected. unsafe { canvas.render_geometry_raw( &vertices, offset_of!(MyVertex, pos), &vertices, offset_of!(MyVertex, color), None::>, &[[0, 1, 2], [1, 2, 3]], ) } .expect("render_geometry_raw failed (probably unsupported, see error message)"); // Parameters can be reused, here only the positions are swapped out for new ones. // SAFETY: core::mem::offset_of makes sure the offsets are right and alignment is respected. // The offset 0 is correct because the element type of positions is `[f32; 2]`. unsafe { canvas.render_geometry_raw( &[ [500.0f32, 100.0], [600.0, 100.0], [500.0, 200.0], [600.0, 200.0], ], 0, &vertices, offset_of!(MyVertex, color), None::>, &[[0, 1, 2], [1, 2, 3]], ) } .expect("render_geometry_raw failed (probably unsupported, see error message)"); canvas.present(); thread::sleep(Duration::from_millis(16)); } }