extern crate gl; extern crate gl_helpers; extern crate glutin; extern crate main_loop; extern crate mat4; extern crate specs; extern crate specs_bundler; extern crate specs_camera; extern crate specs_guided_join; extern crate specs_sprite; extern crate specs_time; extern crate specs_transform; use gl::types::*; use gl_helpers::*; use glutin::GlContext; use specs::{ Builder, DispatcherBuilder, Join, Read, ReadExpect, ReadStorage, System, World, WriteStorage, }; use specs_bundler::Bundler; use specs_camera::{Camera2D, CameraBundle, CameraSystem}; use specs_guided_join::GuidedJoin; use specs_sprite::{Sprite, SpriteBundle, SpriteGuide}; use specs_time::{Time, TimeBundle}; use specs_transform::{GlobalTransform2D, Parent, Transform2D, TransformBundle}; static VERTEX_DATA: [GLfloat; 16] = [ // position uv 1f32, 1f32, 1f32, 1f32, -1f32, 1f32, 0f32, 1f32, 1f32, -1f32, 1f32, 0f32, -1f32, -1f32, 0f32, 0f32, ]; static VERTEX: &'static str = " #version 120 uniform float color; uniform float width; uniform float height; uniform mat4 projection; uniform mat4 model_view; attribute vec2 position; attribute vec2 uv; varying vec2 v_uv; varying float v_color; void main() { gl_Position = projection * model_view * vec4(position.x * width, position.y * height, 0.0, 1.0); v_uv = uv; v_color = color; } "; static FRAGMENT: &'static str = " #version 120 varying vec2 v_uv; varying float v_color; void main() { gl_FragColor = vec4(v_uv, v_color, 1.0); } "; struct SimpleSystem { ms: f32, } impl SimpleSystem { fn new() -> Self { SimpleSystem { ms: 0.0 } } } impl<'a> System<'a> for SimpleSystem { type SystemData = ( Read<'a, Time>, WriteStorage<'a, Camera2D>, WriteStorage<'a, Sprite>, WriteStorage<'a, Transform2D>, ); fn run(&mut self, (time, mut cameras, sprites, mut locals): Self::SystemData) { let dt = time.delta(); self.ms += dt; for camera in (&mut cameras).join() { camera.set_ortho_size(2.0 + self.ms.sin()); } for (_, local) in (&sprites, &mut locals).join() { local.position[0] = self.ms.sin() * 0.75; local.position[1] = self.ms.cos() * 0.75; } } } struct RenderSystem { view: [f32; 16], model: [f32; 16], model_view: [f32; 16], projection: [f32; 16], gl_program: GLProgram, _gl_buffer: GLBuffer, gl_vertex_array: GLVertexArray, } impl RenderSystem { pub fn new() -> Self { let gl_program = GLProgram::new(VERTEX, FRAGMENT); let gl_buffer = GLBuffer::new(BufferTarget::Array, 4, Usage::StaticDraw, &VERTEX_DATA); let mut gl_vertex_array = GLVertexArray::new(); gl_vertex_array.bind(); gl_buffer.bind(); gl_vertex_array.add_attribute(&gl_buffer, gl_program.get_attribute("position"), 0); gl_vertex_array.add_attribute(&gl_buffer, gl_program.get_attribute("uv"), 2); gl_set_clear_color(&[0.3, 0.3, 0.3, 1.0]); gl_clear(true, true, true); RenderSystem { view: mat4::new_identity(), model: mat4::new_identity(), model_view: mat4::new_identity(), projection: mat4::new_identity(), gl_program: gl_program, _gl_buffer: gl_buffer, gl_vertex_array: gl_vertex_array, } } } impl<'a> System<'a> for RenderSystem { type SystemData = ( ReadExpect<'a, SpriteGuide>, ReadStorage<'a, Camera2D>, ReadStorage<'a, Sprite>, ReadStorage<'a, GlobalTransform2D>, ); fn run(&mut self, (sorted_sprites, cameras, sprites, transforms): Self::SystemData) { gl_clear(true, true, true); self.gl_program.bind(); self.gl_vertex_array.bind(); self.gl_vertex_array.enable_attributes(); if let Some(camera) = (&cameras).join().nth(0) { mat4::set_mat32(&mut self.projection, camera.projection()); mat4::set_mat32(&mut self.view, camera.view()); for (sprite, transform) in (&sprites, &transforms).guided_join(&*sorted_sprites) { mat4::set_mat32(&mut self.model, transform.as_ref()); mat4::mul(&mut self.model_view, &self.model, &self.view); self.gl_program.get_uniform("color").set_1f(sprite.image); self.gl_program.get_uniform("width").set_1f(sprite.width); self.gl_program.get_uniform("height").set_1f(sprite.height); self.gl_program .get_uniform("projection") .set_mat4f(&self.projection); self.gl_program .get_uniform("model_view") .set_mat4f(&self.model_view); gl_draw_arrays(DrawMode::TriangleStrip, 0, 4); } } } } fn main() { let mut screen_width = 1024_usize; let mut screen_height = 768_usize; let mut events_loop = glutin::EventsLoop::new(); let window_builder = glutin::WindowBuilder::new() .with_title("Simple") .with_dimensions((screen_width as u32, screen_height as u32).into()); let gl_window = glutin::GlWindow::new( window_builder, glutin::ContextBuilder::new().with_vsync(true), &events_loop, ).unwrap(); unsafe { gl_window.make_current().unwrap(); } let hidpi_factor = gl_window.get_current_monitor().get_hidpi_factor(); gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _); let gl_info = GLInfo::new(); println!("{}", gl_info.version()); println!( "OpenGL version: {:?}.{:?}, GLSL version {:?}.{:?}0", gl_info.major(), gl_info.minor(), gl_info.glsl_major(), gl_info.glsl_minor() ); gl_set_defaults(); let mut world = World::new(); let mut dispatcher = Bundler::new(&mut world, DispatcherBuilder::new()) .bundle(TimeBundle::::default()) .unwrap() .bundle(SpriteBundle::::default()) .unwrap() .bundle(TransformBundle::::default()) .unwrap() .bundle(CameraBundle::::default()) .unwrap() .with( SimpleSystem::new(), "simple_system", &[CameraSystem::::name()], ) .with_thread_local(RenderSystem::new()) .build(); let entity0 = world .create_entity() .with(Transform2D::::default()) .with( Sprite::::new(1.0) .with_layer_and_z(0, 0) .with_width(1.25) .with_height(1.0), ) .build(); let entity1 = world .create_entity() .with(Transform2D::::default()) .with(Parent::new(entity0)) .with( Sprite::::new(0.5) .with_layer_and_z(1, 1) .with_width(0.5) .with_height(0.75), ) .build(); let _entity2 = world .create_entity() .with(Transform2D::::default()) .with(Parent::new(entity1)) .with( Sprite::::new(0.25) .with_layer_and_z(1, 0) .with_width(0.5) .with_height(0.25), ) .build(); let mut camera = Camera2D::::new(); camera.set_ortho_size(3.0); world .create_entity() .with(Transform2D::::default()) .with(camera) .build(); main_loop::glutin::run(&mut events_loop, move |events, _| { for event in events { match event { glutin::Event::WindowEvent { event, .. } => match event { glutin::WindowEvent::CloseRequested => return main_loop::ControlFlow::Break, glutin::WindowEvent::Resized(logical_size) => { let physical_size = logical_size.to_physical(hidpi_factor); screen_width = physical_size.width as usize; screen_height = physical_size.height as usize; gl_window.resize(physical_size); gl_set_viewport(0, 0, screen_width, screen_height); let mut camera_2ds = world.write_storage::>(); for camera_2d in (&mut camera_2ds).join() { camera_2d.set_size(screen_width, screen_height); } } _ => (), }, _ => (), } } dispatcher.dispatch(&mut world.res); world.maintain(); gl_window.swap_buffers().unwrap(); main_loop::ControlFlow::Continue }); }