1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::thread;
use std::sync::mpsc;

use cgmath::{Matrix4, Vector2};
use gaia_assetgen::Properties;
use gaia_quadtree::Tile;
use gfx;
use lru_cache::LruCache;

pub mod terrain;
pub mod polygon;

use errors::*;
use self::polygon::{LabelStyle, PolygonRenderer};
use self::terrain::TerrainRenderer;
use tile_asset_getter::{TileAssetData, TileAssets};
use tile_chooser;
use tile_fetcher;

pub struct Renderer<R: gfx::Resources, F: gfx::Factory<R>> {
    asset_cache: LruCache<Tile, TileAssets<R>>,
    factory: F,
    polygon_renderer: PolygonRenderer<R, F>,
    terrain_renderer: TerrainRenderer<R, F>,
    texture_receiver: mpsc::Receiver<(Tile, Result<TileAssetData>)>,
    tile_sender: mpsc::Sender<Tile>,
}

impl<R: gfx::Resources, F: gfx::Factory<R> + Clone> Renderer<R, F> {
    pub fn new(factory: F) -> Result<Renderer<R, F>> {
        let (tile_sender, tile_receiver) = mpsc::channel();
        let (texture_sender, texture_receiver) = mpsc::channel();

        let polygon_renderer = PolygonRenderer::new(factory.clone())?;
        let terrain_renderer = TerrainRenderer::new(factory.clone())?;

        thread::Builder::new()
            .name("tile_fetcher".to_string())
            .spawn(move || tile_fetcher::fetch_tiles(tile_receiver, texture_sender))
            .chain_err(|| "Error creating texture loader thread")?;

        Ok(Renderer {
            asset_cache: LruCache::new(512),
            factory,
            polygon_renderer,
            terrain_renderer,
            texture_receiver,
            tile_sender,
        })
    }

    pub fn render<
        C: gfx::CommandBuffer<R>,
        Matrix: Into<Matrix4<f32>>,
        Vector: Into<Vector2<f32>>,
    >(
        &mut self,
        encoder: &mut gfx::Encoder<R, C>,
        target: gfx::handle::RenderTargetView<R, gfx::format::Srgba8>,
        stencil: gfx::handle::DepthStencilView<R, gfx::format::DepthStencil>,
        mvp: Matrix,
        look_at: Vector,
        camera_height: f32,
        polygon_color_chooser: &Fn(&Properties) -> Option<[u8; 4]>,
        label_style_chooser: &Fn(&Properties) -> Option<LabelStyle>,
        level_chooser: &Fn(f32) -> u8,
    ) -> Result<()> {
        // Get tiles loaded in background thread, and put them in the cache
        for (tile, tile_texture_data) in self.texture_receiver.try_iter() {
            let assets = tile_texture_data?.create_assets(&mut self.factory)?;
            self.asset_cache.insert(tile.to_origin(), assets);
        }

        let mvp = mvp.into();

        let (level_of_detail, tiles_to_render, tiles_to_fetch) = tile_chooser::choose_tiles(
            level_chooser,
            &mut self.asset_cache,
            mvp.clone(),
            look_at.into(),
            camera_height,
        );

        for tile_to_fetch in tiles_to_fetch {
            self.tile_sender
                .send(tile_to_fetch)
                .chain_err(|| "Error sending tile to background thread")?;
        }

        let mut polygon_metadatas = Vec::new();

        for (tile, indices) in tiles_to_render {
            let tile_assets = self.asset_cache.get_mut(&tile.to_origin()).unwrap();
            polygon_metadatas.push((tile_assets.metadata.clone(), tile.offset));

            self.terrain_renderer.render(
                encoder,
                target.clone(),
                stencil.clone(),
                &mvp,
                tile,
                indices,
                tile_assets,
            );
        }

        self.polygon_renderer.render(
            encoder,
            target,
            stencil,
            mvp,
            level_of_detail,
            &polygon_metadatas,
            polygon_color_chooser,
            label_style_chooser,
        );

        Ok(())
    }
}