assets 0.3.0

asynchronous asset management
Documentation
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<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
    });
}