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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::collections::HashMap;
use std::error::Error;
use std::io::{Cursor, Read};

use gfx;
use gfx::format::*;
use gfx::texture::Kind;
use gfx_core;

use image::{self, RgbaImage};
use zip::ZipArchive;

use cache::WebAsset;

const FACE_NAMES: [&'static str; 6] = ["east", "west", "up", "down", "north", "south"];

pub struct SkyboxAsset(String);
impl Default for SkyboxAsset {
    fn default() -> Self {
        SkyboxAsset("clouds1.zip".to_owned())
    }
}
impl WebAsset for SkyboxAsset {
    type Type = SkyboxRaw;

    fn url(&self) -> String {
        format!("https://opengameart.org/sites/default/files/{}", self.0)
    }
    fn filename(&self) -> String {
        format!("sky/{}", self.0)
    }
    fn parse(&self, data: Vec<u8>) -> Result<Self::Type, Box<Error>> {
        let mut zip = ZipArchive::new(Cursor::new(data))?;
        let mut faces = HashMap::new();

        for i in 0..zip.len() {
            let mut file = zip.by_index(i)?;
            for face in FACE_NAMES.iter() {
                if file.name().contains(face) {
                    let mut image_data = Vec::new();
                    file.read_to_end(&mut image_data)?;
                    faces.insert(
                        face.to_owned(),
                        image::load_from_memory(&image_data)?.to_rgba(),
                    );
                    break;
                }
            }
        }

        assert_eq!(faces.len(), 6);
        let width = faces.iter().next().unwrap().1.width();
        let height = faces.iter().next().unwrap().1.height();
        assert_eq!(width, height);
        for img in faces.values() {
            assert_eq!(img.width(), width);
            assert_eq!(img.height(), height);
        }

        Ok(SkyboxRaw {
            resolution: width as u16,
            faces,
        })
    }
}
pub struct SkyboxRaw {
    resolution: u16,
    faces: HashMap<&'static str, RgbaImage>,
}

pub struct Skybox<R: gfx::Resources> {
    pub(crate) texture_view: gfx_core::handle::ShaderResourceView<R, [f32; 4]>,
    pub(crate) _texture: gfx_core::handle::Texture<R, gfx_core::format::R8_G8_B8_A8>,
}
impl<R: gfx::Resources> Skybox<R> {
    pub fn new<F: gfx::Factory<R>, C: gfx_core::command::Buffer<R>>(
        factory: &mut F,
        encoder: &mut gfx::Encoder<R, C>,
    ) -> Self {
        let mut raw = SkyboxAsset::default().load().unwrap();
        let mut data = Vec::new();
        let mut data_slices = Vec::new();
        for face in FACE_NAMES.iter() {
            data.push(raw.faces.remove(face).unwrap().into_raw());
        }
        for face in data.iter() {
            data_slices.push(gfx::memory::cast_slice(&face[..]));
        }

        let mipmaps = 1 + (raw.resolution.next_power_of_two() as f32).log2() as u8;
        let texture = factory
            .create_texture::<R8_G8_B8_A8>(
                Kind::Cube(raw.resolution),
                mipmaps,
                gfx::SHADER_RESOURCE,
                gfx::memory::Usage::Dynamic,
                Some(ChannelType::Srgb),
            )
            .unwrap();

        for (i, face) in gfx::texture::CUBE_FACES.iter().cloned().enumerate() {
            encoder
                .update_texture::<R8_G8_B8_A8, gfx::format::Srgba8>(
                    &texture,
                    Some(face),
                    gfx_core::texture::NewImageInfo {
                        xoffset: 0,
                        yoffset: 0,
                        zoffset: 0,
                        width: raw.resolution,
                        height: raw.resolution,
                        depth: 1,
                        format: (),
                        mipmap: 0,
                    },
                    &data_slices[i][..],
                )
                .unwrap();
        }

        let texture_view = factory
            .view_texture_as_shader_resource::<gfx::format::Srgba8>(
                &texture,
                (0, mipmaps),
                Swizzle::new(),
            )
            .unwrap();

        encoder.generate_mipmap(&texture_view);

        Self {
            texture_view,
            _texture: texture,
        }
    }
}