Skip to content

Instantly share code, notes, and snippets.

@zokier
Last active December 26, 2015 16:28
Show Gist options
  • Save zokier/7179787 to your computer and use it in GitHub Desktop.
Save zokier/7179787 to your computer and use it in GitHub Desktop.
#[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