Last active
December 26, 2015 16:28
-
-
Save zokier/7179787 to your computer and use it in GitHub Desktop.
This file contains 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
#[feature(globs)]; | |
#[feature(macro_rules)]; | |
#[feature(managed_boxes)]; //TODO do without managed boxes | |
extern mod glfw; | |
extern mod gl; | |
use std::libc; | |
use std::cast; | |
use std::ptr; | |
use std::str; | |
use std::vec; | |
use std::rt::io::Reader; | |
use gl::types::*; | |
struct Position { | |
x: f64, | |
y: f64 | |
} | |
struct HorizVelocity { | |
x: f64 | |
} | |
struct VertVelocity { | |
y: f64 | |
} | |
struct SpriteTexture { | |
texture: GLuint, | |
texcoords: (uint, uint), | |
texsize: (uint, uint) | |
} | |
struct Sprite { | |
x_size: f64, | |
y_size: f64, | |
color: [f64, ..4], | |
texture: Option<SpriteTexture> | |
} | |
struct Score { | |
score: uint | |
} | |
struct Components { | |
position: Option<@mut Position>, | |
horiz_velocity: Option<@mut HorizVelocity>, | |
vert_velocity: Option<@mut VertVelocity>, | |
sprite: Option<@mut Sprite>, | |
score: Option<@mut Score>, | |
} | |
trait GlobalSystem { | |
fn process(&self, window: &glfw::Window) -> (); | |
} | |
struct BotInputSystem { | |
paddle: @Components, | |
ball: @Components | |
} | |
impl GlobalSystem for BotInputSystem { | |
fn process(&self, _: &glfw::Window) -> () { | |
let d = self.ball.position.unwrap().y - self.paddle.position.unwrap().y; | |
if std::num::abs(d) > 0.2 { | |
if d > 0.0 { | |
self.paddle.vert_velocity.unwrap().y = 1.5/60.0; | |
} else { | |
self.paddle.vert_velocity.unwrap().y = -1.5/60.0; | |
} | |
} else { | |
self.paddle.vert_velocity.unwrap().y = 0.0; | |
} | |
} | |
} | |
struct KeyboardInputSystem { | |
paddle: @Components | |
} | |
impl GlobalSystem for KeyboardInputSystem { | |
fn process(&self, window: &glfw::Window) -> () { | |
let mut dir = 0.0; | |
if window.get_key(glfw::KeyA) == glfw::Press { | |
} | |
if window.get_key(glfw::KeyZ) == glfw::Press { | |
dir -= 1.0; | |
} | |
self.paddle.vert_velocity.unwrap().y = 1.5*dir/60.0; | |
} | |
} | |
trait System { | |
fn process(&self, entity: @Components) -> (); | |
} | |
struct MovementSystem; | |
impl System for MovementSystem { | |
fn process(&self, entity: @Components) -> () { | |
match entity.position { | |
Some(pos) => { | |
match entity.vert_velocity { | |
Some(v) => pos.y += v.y, | |
None => () | |
} | |
match entity.horiz_velocity { | |
Some(v) => pos.x += v.x, | |
None => () | |
} | |
}, | |
None => () | |
} | |
} | |
} | |
struct EdgeCollisionSystem; | |
impl System for EdgeCollisionSystem { | |
fn process(&self, entity: @Components) -> () { | |
match (entity.position, entity.vert_velocity, entity.sprite) { | |
(Some(pos), Some(vel), Some(spr)) => { | |
if (pos.y + (spr.y_size/2.0)) >= 3.0 { | |
vel.y *= -1.0; | |
pos.y = 3.0 - (spr.y_size/2.0); | |
} | |
if (pos.y - (spr.y_size/2.0)) <= 0.0 { | |
vel.y *= -1.0; | |
pos.y = spr.y_size/2.0; | |
} | |
}, | |
(_, _, _) => () | |
} | |
} | |
} | |
struct PaddleCollisionSystem { | |
left_paddle: @Components, | |
right_paddle: @Components, | |
} | |
impl System for PaddleCollisionSystem { | |
fn process(&self, entity: @Components) -> () { | |
match (entity.position, entity.horiz_velocity, entity.vert_velocity, entity.sprite) { | |
(Some(pos), Some(hvel), Some(vvel), Some(spr)) => { | |
let mut paddles: Option<(@Components, @Components)> = None; | |
if (pos.x+(spr.x_size)/2.0) >= (self.right_paddle.position.unwrap().x-(self.right_paddle.sprite.unwrap().x_size)/2.0) { | |
paddles = Some((self.right_paddle, self.left_paddle)); | |
} | |
else if (pos.x-(spr.x_size)/2.0) <= (self.left_paddle.position.unwrap().x+(self.left_paddle.sprite.unwrap().x_size)/2.0) { | |
} | |
match paddles { | |
Some((paddle_a, paddle_b)) => { | |
let paddle_distance = pos.y - paddle_a.position.unwrap().y; | |
let paddle_height = paddle_a.sprite.unwrap().y_size/2.0; | |
if std::num::abs(paddle_distance) < paddle_height { | |
hvel.x *= -1.0; | |
vvel.y = 0.5*hvel.x*std::num::sinh(3.14*paddle_distance/paddle_height); | |
} | |
else { | |
paddle_b.score.unwrap().score += 1; | |
hvel.x *= -1.0; | |
} | |
}, | |
None => {} | |
} | |
}, | |
(_, _, _, _) => () | |
} | |
} | |
} | |
struct RenderSystem { | |
program: GLuint, | |
fs: GLuint, | |
vs: GLuint, | |
vbo: GLuint, | |
vao: GLuint, | |
position_uniform: GLint, | |
scale_uniform: GLint, | |
color_uniform: GLint, | |
window_uniform: GLint, | |
texcoords_uniform: GLint, | |
char_atlas_tex: GLuint | |
} | |
impl System for RenderSystem { | |
fn process(&self, entity: @Components) -> () { | |
match (entity.position, entity.sprite) { | |
(Some(pos), Some(sprite)) => { | |
gl::ProgramUniform2f(self.program, self.position_uniform, pos.x as f32, pos.y as f32); | |
gl::ProgramUniform2f(self.program, self.scale_uniform, sprite.x_size as f32, sprite.y_size as f32); | |
gl::ProgramUniform4f(self.program, self.color_uniform, sprite.color[0] as f32, sprite.color[1] as f32, sprite.color[2] as f32, sprite.color[3] as f32); | |
match sprite.texture { | |
Some(tex) => { | |
gl::BindTexture(gl::TEXTURE_2D, tex.texture); | |
let (tex_x, tex_y) = tex.texcoords; | |
let (tex_w, tex_h) = tex.texsize; | |
gl::ProgramUniform4f(self.program, self.texcoords_uniform, tex_x as f32, tex_y as f32, tex_w as f32, tex_h as f32); | |
}, | |
None => {} | |
} | |
gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4); | |
}, | |
(_, _) => () | |
} | |
} | |
} | |
struct World { | |
entities: ~[@Components], | |
systems: ~[@System], | |
global_systems: ~[@GlobalSystem] | |
} | |
impl World { | |
fn new() -> World { | |
return World {entities: ~[], systems: ~[], global_systems: ~[]}; | |
} | |
fn process(&self, _: &glfw::Window) { | |
for _ in self.global_systems.iter() { | |
} | |
for system in self.systems.iter() { | |
for entity in self.entities.iter() { | |
system.process(*entity); | |
} | |
} | |
} | |
} | |
enum PaddleSide { | |
RIGHT, | |
LEFT | |
} | |
fn new_ball() -> @Components { | |
@Components { | |
position: Some(@mut Position { x: 2.0, y: 1.5 }), | |
horiz_velocity: Some(@mut HorizVelocity { x: 1.0/60.0 }), | |
vert_velocity: Some(@mut VertVelocity { y: 0.0 }), | |
sprite: Some(@mut Sprite { | |
x_size: 0.10, | |
y_size: 0.20, | |
color: [0.8, 0.7, 0.3, 0.0], | |
texture: Some(SpriteTexture { | |
texture: 0, | |
texcoords: (0, 28), | |
texsize: (7, 14) | |
}) | |
}), | |
score: None | |
} | |
} | |
fn new_paddle(side: PaddleSide) -> @Components { | |
let xpos = match side { | |
RIGHT => 3.9, | |
LEFT => 0.1 | |
}; | |
@Components { | |
position: Some(@mut Position { x: xpos, y: 1.5 }), | |
horiz_velocity: None, | |
vert_velocity: Some(@mut VertVelocity { y: 0.0 }), | |
sprite: Some(@mut Sprite { | |
x_size: 0.1, | |
y_size: 0.4, | |
color: [xpos/4.0, 1.0-(xpos/4.0), 0.3, 1.0], | |
texture: None | |
}), | |
score: Some(@mut Score { score: 0 } ) | |
} | |
} | |
fn new_background_2() -> @Components { | |
@Components { | |
position: Some(@mut Position { x: 2.0, y: 1.5 }), | |
horiz_velocity: None, | |
vert_velocity: None, | |
sprite: Some(@mut Sprite { | |
x_size: 3.0, | |
y_size: 2.0, | |
color: [0.0, 0.0, 0.0, 0.3], | |
texture: None | |
}), | |
score: None | |
} | |
} | |
fn new_background() -> @Components { | |
@Components { | |
position: Some(@mut Position { x: 2.0, y: 1.5 }), | |
horiz_velocity: None, | |
vert_velocity: None, | |
sprite: Some(@mut Sprite { | |
x_size: 4.0, | |
y_size: 3.0, | |
color: [0.45, 0.4, 1.0, 1.0], | |
texture: None | |
}), | |
score: None | |
} | |
} | |
static VERTEX_DATA: [GLfloat, ..8] = [ | |
-0.5, 0.5, | |
-0.5, -0.5, | |
0.5, 0.5, | |
0.5, -0.5 | |
]; | |
#[start] | |
fn start(argc: int, argv: **u8) -> int { | |
std::rt::start_on_main_thread(argc, argv, main) | |
} | |
fn compile_shader(_: &[u8], ty: GLenum) -> GLuint { | |
let shader = gl::CreateShader(ty); | |
unsafe { | |
gl::CompileShader(shader); | |
let mut status = gl::FALSE as GLint; | |
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); | |
if status != (gl::TRUE as GLint) { | |
let mut len = 0; | |
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); | |
let mut buf = vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character | |
gl::GetShaderInfoLog(shader, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar); | |
fail!(str::raw::from_utf8(buf)); | |
} | |
} | |
shader | |
} | |
fn link_program(_: GLuint, _: GLuint, _: &str) -> GLuint { | |
let program = gl::CreateProgram(); | |
unsafe { | |
} | |
gl::LinkProgram(program); | |
unsafe { | |
let mut status = gl::FALSE as GLint; | |
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); | |
let mut len: GLint = 0; | |
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); | |
let mut buf = vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character | |
gl::GetProgramInfoLog(program, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar); | |
fail!(str::raw::from_utf8(buf)); | |
} | |
program | |
} | |
impl RenderSystem { | |
fn new() -> RenderSystem { | |
let vs_src = std::rt::io::fs::File::open_mode(&std::path::Path::new("main.vs.glsl"), std::rt::io::Open, std::rt::io::Read).unwrap().read_to_end(); | |
let vs = compile_shader(vs_src, gl::VERTEX_SHADER); | |
let fs_src = std::rt::io::fs::File::open_mode(&std::path::Path::new("main.fs.glsl"), std::rt::io::Open, std::rt::io::Read).unwrap().read_to_end(); | |
let fs = compile_shader(fs_src, gl::FRAGMENT_SHADER); | |
let program = link_program(vs, fs, "out_color"); | |
let mut vao = 0; | |
let mut vbo = 0; | |
let position_uniform: GLint = 0; | |
let scale_uniform: GLint = 0; | |
let color_uniform: GLint = 0; | |
let window_uniform: GLint = 0; | |
let texcoords_uniform: GLint = 0; | |
unsafe { | |
gl::GenVertexArrays(1, &mut vao); | |
gl::BindVertexArray(vao); | |
gl::GenBuffers(1, &mut vbo); | |
gl::UseProgram(program); | |
let vert_attr = "vertex".with_c_str(|ptr| gl::GetAttribLocation(program, ptr)); | |
gl::EnableVertexAttribArray(vert_attr as GLuint); | |
gl::VertexAttribPointer(vert_attr as GLuint, 2, gl::FLOAT, | |
gl::FALSE as GLboolean, 0, ptr::null()); | |
} | |
gl::Enable(gl::BLEND); | |
let char_atlas_src = std::rt::io::fs::File::open_mode(&std::path::Path::new("dina_128x128.gray"), std::rt::io::Open, std::rt::io::Read).unwrap().read_to_end(); | |
let mut char_atlas_tex: GLuint = 0; | |
unsafe { | |
gl::GenTextures(1, &mut char_atlas_tex); | |
gl::ActiveTexture(gl::TEXTURE0); | |
gl::TexImage2D(gl::TEXTURE_RECTANGLE, 0, gl::RED as GLint, 128, 128, 0, gl::RED, gl::UNSIGNED_BYTE, cast::transmute(&char_atlas_src[0])); | |
} | |
RenderSystem { | |
program: program, | |
fs: fs, | |
vs: vs, | |
vbo: vbo, | |
vao: vao, | |
position_uniform: position_uniform, | |
scale_uniform: scale_uniform, | |
color_uniform: color_uniform, | |
window_uniform: window_uniform, | |
texcoords_uniform: texcoords_uniform, | |
char_atlas_tex: char_atlas_tex | |
} | |
} | |
} | |
impl Drop for RenderSystem { | |
fn drop(&mut self) { | |
unsafe { | |
gl::DeleteBuffers(1, &self.vbo); | |
gl::DeleteVertexArrays(1, &self.vao); | |
gl::DeleteTextures(1, &self.char_atlas_tex); | |
} | |
} | |
} | |
fn main() { | |
do glfw::set_error_callback |_, description| { | |
println!("GLFW Error: {}", description); | |
} | |
do glfw::start { | |
let left_paddle: @Components = new_paddle(LEFT); | |
let right_paddle: @Components = new_paddle(RIGHT); | |
let ball: @Components = new_ball(); | |
let _: @Components = new_background(); | |
let _: @Components = new_background_2(); | |
let _ = @MovementSystem; | |
let _ = @EdgeCollisionSystem; | |
let _ = @PaddleCollisionSystem{ right_paddle: right_paddle, left_paddle: left_paddle }; | |
let world: World = World::new(); | |
glfw::window_hint::context_version(3, 2); | |
glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile); | |
glfw::window_hint::opengl_forward_compat(true); | |
let mut window_width = 800; | |
let mut window_height = 480; | |
let window = glfw::Window::create(window_width, window_height, "Pong", glfw::Windowed).expect("Failed to create GLFW window.");; | |
window.set_key_callback( | |
|_: &glfw::Window, _: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers| { | |
if action == glfw::Press { | |
} | |
} | |
); | |
window.make_context_current(); | |
gl::load_with(glfw::get_proc_address); | |
let _ = @RenderSystem::new(); | |
let (fb_size_port, _): (Port<(uint,uint)>, Chan<(uint,uint)>) = std::comm::stream(); | |
let _ = @KeyboardInputSystem { paddle: left_paddle }; | |
let _ = @BotInputSystem { paddle: right_paddle, ball: ball }; | |
let mut prev_scores = (0,0); | |
while !window.should_close() { | |
glfw::poll_events(); | |
while fb_size_port.peek() { | |
let (w,h) = fb_size_port.recv(); | |
window_width = w; | |
window_height = h; | |
} | |
gl::Viewport(0,0, window_width as GLint, window_height as GLint); | |
gl::ClearColor(0.8, 0.8, 0.8, 1.0); | |
gl::Clear(gl::COLOR_BUFFER_BIT); | |
world.process(&window); | |
let new_scores = (left_paddle.score.unwrap().score, right_paddle.score.unwrap().score); | |
if new_scores != prev_scores { | |
println!("{:?}", new_scores); | |
prev_scores = new_scores; | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment