extern crate gl; extern crate gl_helpers; extern crate glutin; extern crate assets; extern crate futures; extern crate futures_cpupool; extern crate imagefmt; 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::{Api, GlContext, GlRequest}; use assets::{ FileExporter, FileImporter, Handle, ImageAsset, ImageAssetExportOptions, ImageAssetImportOptions, Manager, }; use futures_cpupool::CpuPool; use specs::{Builder, DispatcherBuilder, Join, ReadExpect, ReadStorage, System, World}; use specs_bundler::Bundler; use specs_camera::{Camera2D, CameraBundle}; use specs_guided_join::GuidedJoin; use specs_sprite::{Sprite, SpriteBundle, SpriteGuide}; use specs_time::{Time, TimeBundle}; use specs_transform::{GlobalTransform2D, Transform2D, TransformBundle}; use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::sync::Arc; static VERTEX_DATA: [GLfloat; 16] = [ // position uv 0.5f32, 0.5f32, 1f32, 1f32, -0.5f32, 0.5f32, 0f32, 1f32, 0.5f32, -0.5f32, 1f32, 0f32, -0.5f32, -0.5f32, 0f32, 0f32, ]; static VERTEX: &'static str = " uniform mat4 projection; uniform mat4 model_view; attribute vec2 position; attribute vec2 uv; varying vec2 v_uv; void main() { gl_Position = projection * model_view * vec4(position, 0, 1.0); v_uv = uv; } "; static FRAGMENT: &'static str = " precision mediump float; uniform sampler2D diffuse; varying vec2 v_uv; void main() { gl_FragColor = texture2D(diffuse, v_uv); } "; pub type ImageManager = Manager, FileExporter>; struct RenderSystem { view: [f32; 16], model: [f32; 16], model_view: [f32; 16], projection: [f32; 16], gl_textures: HashMap, Arc>, gl_program: GLProgram, _gl_buffer: GLBuffer, gl_vertex_array: GLVertexArray, } impl Default for RenderSystem { fn default() -> 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_textures: HashMap::new(), gl_program: gl_program, _gl_buffer: gl_buffer, gl_vertex_array: gl_vertex_array, } } } impl RenderSystem { pub fn get_gl_texture( &mut self, handle: &Handle, image_asset_manager: &ImageManager, ) -> Option> { if !self.gl_textures.contains_key(handle) { if let Some(image_asset) = image_asset_manager.get(handle) { let gl_texture = GLTexture::new_2d( image_asset.w, image_asset.h, InternalFormat::RGBA, DataFormat::RGBA, DataKind::UnsignedByte, FilterMode::Linear, Wrap::Repeat, true, &*image_asset.buf, ); self.gl_textures .insert(handle.clone(), Arc::new(gl_texture)); } } self.gl_textures.get(handle).map(|x| x.clone()) } } impl<'a> System<'a> for RenderSystem { type SystemData = ( ReadStorage<'a, Camera2D>, ReadExpect<'a, ImageManager>, ReadExpect<'a, SpriteGuide>>, ReadStorage<'a, Sprite>>, ReadStorage<'a, GlobalTransform2D>, ); fn run( &mut self, (cameras, image_asset_manager, sprite_guide, 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(sprite_guide.as_slice()) { mat4::set_mat32(&mut self.model, &transform.0); mat4::mul(&mut self.model_view, &self.model, &self.view); self.gl_program .get_uniform("projection") .set_mat4f(&self.projection); self.gl_program .get_uniform("model_view") .set_mat4f(&self.model_view); if let Some(gl_texture) = self.get_gl_texture(&sprite.image, &*image_asset_manager) { self.gl_program .get_uniform("diffuse") .set_sampler_2d(&*gl_texture, 0); } 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 = glutin::WindowBuilder::new() .with_title("OpenGL") .with_maximized(true); let context = { let builder = glutin::ContextBuilder::new().with_vsync(true); if cfg!(target_os = "windows") { builder } else { builder.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0))) } }; let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); unsafe { gl_window.make_current().unwrap(); } 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(); if let Some(logical_size) = gl_window.get_inner_size() { let physical_size = logical_size.to_physical(gl_window.get_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 pool = Arc::new(CpuPool::new_num_cpus()); let file_importer = Arc::new(FileImporter::new()); let file_exporter = Arc::new(FileExporter::new()); let mut image_manager = ImageManager::new(file_importer, file_exporter, pool); let assets = if cfg!(target_os = "android") { Path::new("") } else { Path::new("assets") }; let crate_handle = image_manager.add( assets.join("crate.jpg"), (), ImageAssetImportOptions { col_fmt: imagefmt::ColFmt::RGBA, }, assets.join("crate.jpg"), (), ImageAssetExportOptions::default(), true, ); let _not_found_handle = image_manager.add( assets.join("not_found.jpg"), (), ImageAssetImportOptions { col_fmt: imagefmt::ColFmt::RGBA, }, assets.join("not_found.jpg"), (), ImageAssetExportOptions::default(), true, ); let mut world = World::new(); let mut dispatcher = Bundler::new(&mut world, DispatcherBuilder::new()) .bundle(TransformBundle::::default()) .unwrap() .bundle(CameraBundle::::default()) .unwrap() .bundle(TimeBundle::::default()) .unwrap() .bundle(SpriteBundle::>::default()) .unwrap() .with_thread_local(RenderSystem::default()) .build(); world.add_resource(image_manager); world .create_entity() .with(Transform2D::::default()) .with(Sprite::>::new(crate_handle)) .build(); world .create_entity() .with(Transform2D::::default()) .with( Camera2D::::default() .with_size(screen_width, screen_height) .with_ortho_size(1.0), ) .build(); main_loop::glutin::run(&mut events_loop, move |events, _| { for event in events { match event { glutin::Event::WindowEvent { event: glutin::WindowEvent::CloseRequested, .. } => { let time = world.read_resource::>(); println!("Total: {}s {}fps", time.current(), time.fps()); return main_loop::ControlFlow::Break; } glutin::Event::WindowEvent { event: glutin::WindowEvent::Resized(logical_size), .. } => { let physical_size = logical_size.to_physical(gl_window.get_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 cameras = world.write_storage::>(); for camera in (&mut cameras).join() { camera.set_size(screen_width, screen_height); } } _ => (), } } dispatcher.dispatch(&mut world.res); world.maintain(); let image_handler = world.write_resource::(); for event in image_handler.poll_events() { println!("{:?}", event); } gl_window.swap_buffers().unwrap(); main_loop::ControlFlow::Continue }); }