Hello everyone,
I am new for Rust Community. Thanks for welcome-saying!
I have tested with ChatGPT but ChatGPT is really catastrophically.
I create simple Software Rasterization on SDL2 with Rust. I find funny because rust is really faster than C# or python.
I already build against static version of executable without required loaded dynamic libraries.
build.rs
at root directory of my sdl2_project:
fn main() {
println!("cargo:rustc-link-lib=static=SDL2main");
println!("cargo:rustc-link-lib=static=SDL2-static");
println!("cargo:rustc-link-search=native=C:/SDL2/lib");
println!("cargo:rustc-link-lib=dylib=AdvAPI32");
println!("cargo:rustc-link-lib=dylib=bcrypt");
println!("cargo:rustc-link-lib=dylib=User32");
println!("cargo:rustc-link-lib=dylib=Ole32");
println!("cargo:rustc-link-lib=dylib=Gdi32");
println!("cargo:rustc-link-lib=dylib=Imm32");
println!("cargo:rustc-link-lib=dylib=WinMM");
println!("cargo:rustc-link-lib=dylib=OleAut32");
println!("cargo:rustc-link-lib=dylib=SetupAPI");
println!("cargo:rustc-link-lib=dylib=cfgmgr32");
println!("cargo:rustc-link-lib=dylib=shell32");
println!("cargo:rustc-link-lib=dylib=Version");
println!("cargo:rustc-link-lib=dylib=libcmt");
println!("cargo:rustc-link-lib=static=libvcruntime");
println!("cargo:rustc-link-lib=static=libucrt");
println!("cargo:rustc-link-lib=static=ucrt");
}
And I need to create config.toml in %USERPROFILE%\.cargo\
# Windows 64 bit programs
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "link-arg=libvcruntime.lib", "-Ctarget-feature=+crt-static", "-Zunstable-options"]
[build]
rustflags = ["-Ctarget-feature=+crt-static"]
I want to show - how do I draw lined triangle on SDL2's SDL_Surface - It would like to make as Software Rasterization.
extern crate sdl2;
use sdl2::pixels::Color;
use sdl2::surface::Surface;
use sdl2::pixels::PixelFormatEnum;
// Struct to handle screen coordinate transformations
struct ScreenCoordinator {
window_width: i32,
window_height: i32,
}
impl ScreenCoordinator {
fn new(window_width: i32, window_height: i32) -> Self {
Self {
window_width,
window_height,
}
}
fn to_screen_coords(&self, x: f32, y: f32) -> (i32, i32) {
let screen_x = ((x + 1.0) * (self.window_width as f32 / 2.0)) as i32;
let screen_y = ((1.0 - y) * (self.window_height as f32 / 2.0)) as i32; // Flip Y-axis
(screen_x, screen_y)
}
}
// Lined triangle
fn draw_triangle(
surface: &mut Surface,
p1: (f32, f32),
p2: (f32, f32),
p3: (f32, f32),
color: Color,
coordinator: &ScreenCoordinator
) {
let width = surface.width() as i32;
let height = surface.height() as i32;
let pitch = surface.pitch() as i32;
let pixels = surface.without_lock_mut().unwrap();
let mut draw_line = |(x0, y0): (f32, f32), (x1, y1): (f32, f32)| {
let (mut x0, mut y0) = coordinator.to_screen_coords(x0, y0);
let (x1, y1) = coordinator.to_screen_coords(x1, y1);
let dx = (x1 - x0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let dy = -(y1 - y0).abs();
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx + dy;
loop {
if x0 >= 0 && x0 < width && y0 >= 0 && y0 < height {
let offset = (y0 * pitch + x0 * 3) as usize;
pixels[offset] = color.r;
pixels[offset + 1] = color.g;
pixels[offset + 2] = color.b;
}
if x0 == x1 && y0 == y1 {
break;
}
let e2 = 2 * err;
if e2 >= dy {
err += dy;
x0 += sx;
}
if e2 <= dx {
err += dx;
y0 += sy;
}
}
};
draw_line(p1, p2); // Ensure p1 and p2 are (f32, f32)
draw_line(p2, p3); // Ensure p2 and p3 are (f32, f32)
draw_line(p3, p1); // Ensure p3 and p1 are (f32, f32)
}
fn main() {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window_width = 800;
let window_height = 600;
let window = video_subsystem
.window("Rust SDL2: 02 Lined Triangle", window_width as u32, window_height as u32)
.position_centered()
.build()
.unwrap();
// Create a surface with the same dimensions as the window
let mut surface = Surface::new(window_width, window_height, PixelFormatEnum::RGB24).unwrap();
let background_color = Color::RGB(255 / 10, 255 / 10, 255 / 10); // Red background
// Clear the surface with the background color
surface.fill_rect(None, background_color).unwrap();
// Initialize the ScreenCoordinator using the `new` method
let coordinator = ScreenCoordinator::new(window_width as i32, window_height as i32);
// Draw a triangle using OpenGL-like coordinates
draw_triangle(&mut surface, (0.0, 0.5), (0.5, -0.5), (-0.5, -0.5), Color::RGB(255, 85, 0), &coordinator);
// Create a canvas to present the surface to the window
let mut canvas = window.into_canvas().software().build().unwrap();
// Convert the surface to a texture
let texture_creator = canvas.texture_creator();
let texture = surface.as_texture(&texture_creator).unwrap();
// Present the texture in the window
canvas.copy(&texture, None, None).unwrap();
canvas.present();
// Wait for a quit event
let mut event_pump = sdl_context.event_pump().unwrap();
loop {
for event in event_pump.poll_iter() {
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
match event {
Event::Quit {..} |
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
return;
},
_ => {}
}
}
}
}
But I tried to fill triangle -> It doesn't show me. How do I fix it? if I change draw_triangle
to draw_filled_triangle
and build with cargo and it doesn't see triangle.
I hope you have to help me if you have experience of Software Rasterization. Thank you for supporting me!
I explain about terms:
ScreenCoordinator it works like OpenGL's coordinator like website LearningOpenGL - You know that.
Lined triangle - it draws only 3 lines into naked triangle.
Filled triangle - it draws a filled shape triangle like OpenGL's shape.
I hope you understand my English. Sorry my bad English!
Greeting from Germany
Do you know how do I get filled triangle from pixels by SDL_Surface ( Surface ).
// EDIT I got successful "Filled Triangle"
// Function to fill a triangle
fn fill_triangle(
surface: &mut Surface,
p1: (f32, f32),
p2: (f32, f32),
p3: (f32, f32),
color: Color,
coordinator: &ScreenCoordinator
) {
let width = surface.width() as i32;
let height = surface.height() as i32;
let pitch = surface.pitch() as i32;
let pixels = surface.without_lock_mut().unwrap();
// Convert points to screen coordinates
let (x1, y1) = coordinator.to_screen_coords(p1.0, p1.1);
let (x2, y2) = coordinator.to_screen_coords(p2.0, p2.1);
let (x3, y3) = coordinator.to_screen_coords(p3.0, p3.1);
// Sort the vertices by y-coordinate ascending (y1 <= y2 <= y3)
let (x1, y1, x2, y2, x3, y3) = if y1 > y2 {
if y2 > y3 {
(x3, y3, x2, y2, x1, y1)
} else if y1 > y3 {
(x2, y2, x3, y3, x1, y1)
} else {
(x2, y2, x1, y1, x3, y3)
}
} else {
if y1 > y3 {
(x3, y3, x1, y1, x2, y2)
} else if y2 > y3 {
(x1, y1, x3, y3, x2, y2)
} else {
(x1, y1, x2, y2, x3, y3)
}
};
// Helper function to draw a horizontal line
let mut draw_horizontal_line = |y: i32, x_min: i32, x_max: i32| {
for x in x_min..=x_max {
if x >= 0 && x < width && y >= 0 && y < height {
let offset = (y * pitch + x * 3) as usize;
pixels[offset] = color.r;
pixels[offset + 1] = color.g;
pixels[offset + 2] = color.b;
}
}
};
// Fill bottom flat triangle
if y2 != y1 {
for y in y1..=y2 {
let alpha = (y - y1) as f32 / (y2 - y1) as f32;
let beta = (y - y1) as f32 / (y3 - y1) as f32;
let x_start = x1 + ((x2 - x1) as f32 * alpha) as i32;
let x_end = x1 + ((x3 - x1) as f32 * beta) as i32;
draw_horizontal_line(y, x_start.min(x_end), x_start.max(x_end));
}
}
// Fill top flat triangle
if y3 != y2 {
for y in y2..=y3 {
let alpha = (y - y2) as f32 / (y3 - y2) as f32;
let beta = (y - y1) as f32 / (y3 - y1) as f32;
let x_start = x2 + ((x3 - x2) as f32 * alpha) as i32;
let x_end = x1 + ((x3 - x1) as f32 * beta) as i32;
draw_horizontal_line(y, x_start.min(x_end), x_start.max(x_end));
}
}
}
And example:
fill_triangle(&mut surface, (0.0, 0.5), (0.5, -0.5), (-0.5, -0.5), Color::RGB(255, 85, 0), &coordinator);
And I continue more details for Software Rasterization