Внезапно решил попробовать использовать SDL2 и OpenGL в Rust. В сети есть соответствующие библиотеки и примеры к ним, но оказалось что все примеры устарели и больше не компилируются. Пришлось решить эту проблему.
Я взял за основу примеры от SDL2, OpenGL биндинга для Rust и адаптировал их для Rust 1.18.0. Так как я только начал изучать Rust, мог где-нибудь напортачить. Тем не менее, этот пример компилируются и работает.
Файл main.rs:
extern crate gl;
extern crate sdl2;
use sdl2::EventPump;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;
use gl::types::*;
use std::mem;
use std::ptr;
use std::str;
use std::ffi::CStr;
use std::ffi::CString;
// Vertex data
static VERTEX_DATA: [GLfloat; 6] = [0.0, 0.5, 0.5, -0.5, -0.5, -0.5];
// Shader sources
static VS_SRC: &'static str =
"#version 150\n\
in vec2 position;\n\
void main() {\n\
gl_Position = vec4(position, 0.0, 1.0);\n\
}";
static FS_SRC: &'static str =
"#version 150\n\
out vec4 out_color;\n\
void main() {\n\
out_color = vec4(1.0, 1.0, 1.0, 1.0);\n\
}";
// compile shader
fn compile_shader(src: &str, ty: GLenum) -> GLuint {
let shader;
unsafe {
shader = gl::CreateShader(ty);
// Attempt to compile the shader
let c_str = CString::new(src.as_bytes()).unwrap();
gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null());
gl::CompileShader(shader);
// Get the compile status
let mut status = gl::FALSE as GLint;
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
// Fail on error
if status != (gl::TRUE as GLint) {
let mut len = 0;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
gl::GetShaderInfoLog(shader,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar);
panic!("{}",
str::from_utf8(&buf)
.ok()
.expect("ShaderInfoLog not valid utf8"));
}
}
shader
}
// link fragment and vertex shader
fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
unsafe {
let program = gl::CreateProgram();
gl::AttachShader(program, vs);
gl::AttachShader(program, fs);
gl::LinkProgram(program);
// Get the link status
let mut status = gl::FALSE as GLint;
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
// Fail on error
if status != (gl::TRUE as GLint) {
let mut len: GLint = 0;
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
gl::GetProgramInfoLog(program,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar);
panic!("{}",
str::from_utf8(&buf)
.ok()
.expect("ProgramInfoLog not valid utf8"));
}
program
}
}
// convert OpenGL native string to string
unsafe fn gl_to_str(c_str: *const u8) -> &'static str {
mem::transmute(str::from_utf8(CStr::from_ptr(c_str as *const i8).to_bytes()).unwrap())
}
// simulate SDL_WaitEvent(NULL) function because it is not implemented in rust-sdl2
fn wait_for_event(events: &sdl2::EventSubsystem, pump: &mut EventPump) {
let event = pump.wait_event();
events.push_event(event).ok();
}
fn main() {
// initialize SDL and video subsystem
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let gl_attr = video_subsystem.gl_attr();
// Don't use deprecated OpenGL functions
gl_attr.set_context_profile(GLProfile::Core);
// Set the context into debug mode
gl_attr.set_context_flags().debug().forward_compatible().set();
// Set the OpenGL context version (OpenGL 3.2)
gl_attr.set_context_version(3, 2);
// Enable anti-aliasing
gl_attr.set_multisample_buffers(1);
gl_attr.set_multisample_samples(4);
let window = video_subsystem.window("Triangle", 640, 480)
.position_centered()
.opengl()
.build()
.unwrap();
// Yes, we're still using the Core profile
assert_eq!(gl_attr.context_profile(), GLProfile::Core);
// ... and we're still using OpenGL 3.2
assert_eq!(gl_attr.context_version(), (3, 2));
// init OpenGL function pointers
gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
// create OpenGL context and make it current
let gl_context = window.gl_create_context().unwrap();
window.gl_make_current(&gl_context).ok();
// print drivers information
unsafe {
println!("Vendor: {}", gl_to_str(gl::GetString(gl::VENDOR)));
println!("Renderer: {}", gl_to_str(gl::GetString(gl::RENDERER)));
println!("GLSL version: {}", gl_to_str(gl::GetString(gl::SHADING_LANGUAGE_VERSION)));
println!("GL version: {}", gl_to_str(gl::GetString(gl::VERSION)));
}
// load shaders
let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER);
let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER);
let program = link_program(vs, fs);
// prepare vertex array object and vertex buffer object
let mut vao = 0;
let mut vbo = 0;
unsafe {
// Create Vertex Array Object
gl::GenVertexArrays(1, &mut vao);
gl::BindVertexArray(vao);
// Create a Vertex Buffer Object and copy the vertex data to it
gl::GenBuffers(1, &mut vbo);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(gl::ARRAY_BUFFER,
(VERTEX_DATA.len() * mem::size_of::()) as GLsizeiptr,
mem::transmute(&VERTEX_DATA[0]),
gl::STATIC_DRAW);
// Use shader program
gl::UseProgram(program);
gl::BindFragDataLocation(program, 0, CString::new("out_color").unwrap().as_ptr());
// Specify the layout of the vertex data
let pos_attr = gl::GetAttribLocation(program, CString::new("position").unwrap().as_ptr());
gl::EnableVertexAttribArray(pos_attr as GLuint);
gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT,
gl::FALSE as GLboolean, 0, ptr::null());
}
// main event loop
let mut event_pump = sdl_context.event_pump().unwrap();
'running: loop {
// do rendering
unsafe {
// Clear the screen to black
gl::ClearColor(0.3, 0.3, 0.3, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
// Draw a triangle from the 3 vertices
gl::DrawArrays(gl::TRIANGLES, 0, 3);
}
// copy buffer to screen
window.gl_swap_window();
// wait for event
wait_for_event(&sdl_context.event().unwrap(), &mut event_pump);
// handle all arrived events
for event in event_pump.poll_iter() {
match event {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
break 'running
},
_ => {}
}
}
}
// Cleanup
unsafe {
gl::DeleteProgram(program);
gl::DeleteShader(fs);
gl::DeleteShader(vs);
gl::DeleteBuffers(1, &vbo);
gl::DeleteVertexArrays(1, &vao);
}
}
Теперь содержимое файла Cargo.toml:
[package] name = "triangle" version = "0.1.0" authors = [ "Alexander" ] [dependencies] gl = "0.6.3" [dependencies.sdl2] features = [ "use_mac_framework" ] version = "0.30.0"
Этот пример рассчитан на компиляцию в MacOS X. Так как SDL2 у меня установлен как фреймворк, потребовалось указать дополнительную опцию “use_mac_framework”. На других платформах она не нужна.
Как ни странно, в оболочке для Rust не оказалось аналога функции SDL_WaitEvent(NULL), которая ждет прихода следующего события, но не удаляет его из очереди. Пришлось реализовать её подручными средствами. Надеюсь, в будущих версиях rust-sdl2 это исправят.
Оставить комментарий
Для отправки комментария вам необходимо авторизоваться.