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] = [
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<ImageAsset, FileImporter<PathBuf>, FileExporter<PathBuf>>;
struct RenderSystem {
view: [f32; 16],
model: [f32; 16],
model_view: [f32; 16],
projection: [f32; 16],
gl_textures: HashMap<Handle<ImageAsset>, Arc<GLTexture>>,
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<ImageAsset>,
image_asset_manager: &ImageManager,
) -> Option<Arc<GLTexture>> {
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<f32>>,
ReadExpect<'a, ImageManager>,
ReadExpect<'a, SpriteGuide<f32, Handle<ImageAsset>>>,
ReadStorage<'a, Sprite<f32, Handle<ImageAsset>>>,
ReadStorage<'a, GlobalTransform2D<f32>>,
);
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::<f32>::default())
.unwrap()
.bundle(CameraBundle::<f32>::default())
.unwrap()
.bundle(TimeBundle::<f32>::default())
.unwrap()
.bundle(SpriteBundle::<f32, Handle<ImageAsset>>::default())
.unwrap()
.with_thread_local(RenderSystem::default())
.build();
world.add_resource(image_manager);
world
.create_entity()
.with(Transform2D::<f32>::default())
.with(Sprite::<f32, Handle<ImageAsset>>::new(crate_handle))
.build();
world
.create_entity()
.with(Transform2D::<f32>::default())
.with(
Camera2D::<f32>::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::<Time<f32>>();
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::<Camera2D<f32>>();
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::<ImageManager>();
for event in image_handler.poll_events() {
println!("{:?}", event);
}
gl_window.swap_buffers().unwrap();
main_loop::ControlFlow::Continue
});
}