Mercurial > touhou
comparison examples/anmrenderer.rs @ 704:84af5bedbde4
anmrenderer: also load the alpha PNG.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Fri, 23 Aug 2019 19:09:37 +0200 |
parents | b6c351ca0a35 |
children | ed65f9412bc0 |
comparison
equal
deleted
inserted
replaced
703:81232dac8136 | 704:84af5bedbde4 |
---|---|
1 use image::GenericImageView; | 1 use image::{GenericImageView, DynamicImage}; |
2 use luminance::blending::{Equation, Factor}; | 2 use luminance::blending::{Equation, Factor}; |
3 use luminance::context::GraphicsContext; | 3 use luminance::context::GraphicsContext; |
4 use luminance::framebuffer::Framebuffer; | 4 use luminance::framebuffer::Framebuffer; |
5 use luminance::pipeline::BoundTexture; | 5 use luminance::pipeline::BoundTexture; |
6 use luminance::pixel::{NormRGB8UI, Floating}; | 6 use luminance::pixel::{NormRGB8UI, NormRGBA8UI, Floating}; |
7 use luminance::render_state::RenderState; | 7 use luminance::render_state::RenderState; |
8 use luminance::shader::program::{Program, Uniform}; | 8 use luminance::shader::program::{Program, Uniform}; |
9 use luminance::tess::{Mode, TessBuilder}; | 9 use luminance::tess::{Mode, TessBuilder}; |
10 use luminance::texture::{Dim2, Flat, Sampler, Texture, GenMipmaps}; | 10 use luminance::texture::{Dim2, Flat, Sampler, Texture, GenMipmaps}; |
11 use luminance_derive::{Semantics, Vertex, UniformInterface}; | 11 use luminance_derive::{Semantics, Vertex, UniformInterface}; |
85 | 85 |
86 #[uniform(name = "mvp")] | 86 #[uniform(name = "mvp")] |
87 mvp: Uniform<[[f32; 4]; 4]>, | 87 mvp: Uniform<[[f32; 4]; 4]>, |
88 } | 88 } |
89 | 89 |
90 fn load_file_into_vec(filename: &str) -> Vec<u8> { | 90 fn load_file_into_vec(filename: &Path) -> Vec<u8> { |
91 let file = File::open(filename).unwrap(); | 91 let file = File::open(filename).unwrap(); |
92 let mut file = BufReader::new(file); | 92 let mut file = BufReader::new(file); |
93 let mut buf = vec![]; | 93 let mut buf = vec![]; |
94 file.read_to_end(&mut buf).unwrap(); | 94 file.read_to_end(&mut buf).unwrap(); |
95 buf | 95 buf |
96 } | 96 } |
97 | 97 |
98 enum LoadedTexture { | |
99 Rgba(Texture<Flat, Dim2, NormRGBA8UI>), | |
100 Rgb(Texture<Flat, Dim2, NormRGB8UI>), | |
101 } | |
102 | |
98 fn main() { | 103 fn main() { |
99 // Parse arguments. | 104 // Parse arguments. |
100 let args: Vec<_> = env::args().collect(); | 105 let args: Vec<_> = env::args().collect(); |
101 if args.len() != 4 { | 106 if args.len() != 3 { |
102 eprintln!("Usage: {} <ANM file> <PNG file> <script number>", args[0]); | 107 eprintln!("Usage: {} <ANM file> <script number>", args[0]); |
103 return; | 108 return; |
104 } | 109 } |
105 let anm_filename = &args[1]; | 110 let anm_filename = Path::new(&args[1]); |
106 let png_filename = &args[2]; | 111 let script: u8 = args[2].parse().expect("number"); |
107 let script: u8 = args[3].parse().expect("number"); | |
108 | 112 |
109 // Open the ANM file. | 113 // Open the ANM file. |
110 let buf = load_file_into_vec(anm_filename); | 114 let buf = load_file_into_vec(anm_filename); |
111 let (_, mut anms) = Anm0::from_slice(&buf).unwrap(); | 115 let (_, mut anms) = Anm0::from_slice(&buf).unwrap(); |
112 let anm0 = anms.pop().unwrap(); | 116 let anm0 = anms.pop().unwrap(); |
130 fill_vertices(sprite.clone(), &mut vertices); | 134 fill_vertices(sprite.clone(), &mut vertices); |
131 | 135 |
132 let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap(); | 136 let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap(); |
133 | 137 |
134 // Open the image atlas matching this ANM. | 138 // Open the image atlas matching this ANM. |
135 println!("{} {}", anm0.first_name, png_filename); | 139 let png_filename = anm_filename.with_file_name(Path::new(&anm0.png_filename).file_name().unwrap()); |
136 let tex = load_from_disk(&mut surface, Path::new(png_filename)).expect("texture loading"); | 140 let tex = match anm0.alpha_filename { |
141 Some(ref filename) => { | |
142 let alpha_filename = anm_filename.with_file_name(Path::new(filename).file_name().unwrap()); | |
143 LoadedTexture::Rgba(png_alpha(&mut surface, &png_filename, &alpha_filename).expect("texture loading")) | |
144 }, | |
145 None => { | |
146 LoadedTexture::Rgb(load_from_disk(&mut surface, &png_filename).expect("texture loading")) | |
147 } | |
148 }; | |
137 | 149 |
138 // set the uniform interface to our type so that we can read textures from the shader | 150 // set the uniform interface to our type so that we can read textures from the shader |
139 let (program, _) = | 151 let (program, _) = |
140 Program::<Semantics, (), ShaderInterface>::from_strings(None, VS, None, FS).expect("program creation"); | 152 Program::<Semantics, (), ShaderInterface>::from_strings(None, VS, None, FS).expect("program creation"); |
141 | 153 |
173 // and use it in the shader | 185 // and use it in the shader |
174 surface | 186 surface |
175 .pipeline_builder() | 187 .pipeline_builder() |
176 .pipeline(&back_buffer, [0., 0., 0., 0.], |pipeline, shd_gate| { | 188 .pipeline(&back_buffer, [0., 0., 0., 0.], |pipeline, shd_gate| { |
177 // bind our fancy texture to the GPU: it gives us a bound texture we can use with the shader | 189 // bind our fancy texture to the GPU: it gives us a bound texture we can use with the shader |
178 let bound_tex = pipeline.bind_texture(&tex); | 190 let bound_tex = match &tex { |
191 LoadedTexture::Rgb(tex) => pipeline.bind_texture(tex), | |
192 LoadedTexture::Rgba(tex) => pipeline.bind_texture(tex), | |
193 }; | |
179 | 194 |
180 shd_gate.shade(&program, |rdr_gate, iface| { | 195 shd_gate.shade(&program, |rdr_gate, iface| { |
181 // update the texture; strictly speaking, this update doesn’t do much: it just tells the GPU | 196 // update the texture; strictly speaking, this update doesn’t do much: it just tells the GPU |
182 // to use the texture passed as argument (no allocation or copy is performed) | 197 // to use the texture passed as argument (no allocation or copy is performed) |
183 iface.color_map.update(&bound_tex); | 198 iface.color_map.update(&bound_tex); |
219 match image::open(&path) { | 234 match image::open(&path) { |
220 Ok(img) => { | 235 Ok(img) => { |
221 let (width, height) = img.dimensions(); | 236 let (width, height) = img.dimensions(); |
222 let texels = img | 237 let texels = img |
223 .pixels() | 238 .pixels() |
224 .map(|(x, y, rgb)| (rgb[0], rgb[1], rgb[2])) | 239 .map(|(_x, _y, rgb)| (rgb[0], rgb[1], rgb[2])) |
225 .collect::<Vec<_>>(); | 240 .collect::<Vec<_>>(); |
226 | 241 |
227 // create the luminance texture; the third argument is the number of mipmaps we want (leave it | 242 // create the luminance texture; the third argument is the number of mipmaps we want (leave it |
228 // to 0 for now) and the latest is a the sampler to use when sampling the texels in the | 243 // to 0 for now) and the latest is a the sampler to use when sampling the texels in the |
229 // shader (we’ll just use the default one) | 244 // shader (we’ll just use the default one) |
240 eprintln!("cannot open image {}: {}", path.display(), e); | 255 eprintln!("cannot open image {}: {}", path.display(), e); |
241 None | 256 None |
242 } | 257 } |
243 } | 258 } |
244 } | 259 } |
260 | |
261 fn png_alpha(surface: &mut GlfwSurface, rgb: &Path, alpha: &Path) -> Option<Texture<Flat, Dim2, NormRGBA8UI>> { | |
262 // load the texture into memory as a whole bloc (i.e. no streaming) | |
263 match image::open(&alpha) { | |
264 Ok(img) => { | |
265 let (width, height) = img.dimensions(); | |
266 let alpha = match img.grayscale() { | |
267 DynamicImage::ImageLuma8(img) => img, | |
268 _ => { | |
269 eprintln!("cannot convert alpha image {} to grayscale", alpha.display()); | |
270 return None; | |
271 } | |
272 }; | |
273 let img = match image::open(&rgb) { | |
274 Ok(img) => img, | |
275 Err(e) => { | |
276 eprintln!("cannot open rgb image {}: {}", rgb.display(), e); | |
277 return None; | |
278 }, | |
279 }; | |
280 let texels = img | |
281 .pixels() | |
282 .zip(alpha.pixels()) | |
283 .map(|((_x, _y, rgb), luma)| (rgb[0], rgb[1], rgb[1], luma[0])) | |
284 .collect::<Vec<_>>(); | |
285 | |
286 // create the luminance texture; the third argument is the number of mipmaps we want (leave it | |
287 // to 0 for now) and the latest is a the sampler to use when sampling the texels in the | |
288 // shader (we’ll just use the default one) | |
289 let tex = | |
290 Texture::new(surface, [width, height], 0, &Sampler::default()).expect("luminance texture creation"); | |
291 | |
292 // the first argument disables mipmap generation (we don’t care so far) | |
293 tex.upload(GenMipmaps::No, &texels); | |
294 | |
295 Some(tex) | |
296 } | |
297 | |
298 Err(e) => { | |
299 eprintln!("cannot open alpha image {}: {}", alpha.display(), e); | |
300 None | |
301 } | |
302 } | |
303 } |