diff 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
line wrap: on
line diff
--- a/examples/anmrenderer.rs
+++ b/examples/anmrenderer.rs
@@ -1,9 +1,9 @@
-use image::GenericImageView;
+use image::{GenericImageView, DynamicImage};
 use luminance::blending::{Equation, Factor};
 use luminance::context::GraphicsContext;
 use luminance::framebuffer::Framebuffer;
 use luminance::pipeline::BoundTexture;
-use luminance::pixel::{NormRGB8UI, Floating};
+use luminance::pixel::{NormRGB8UI, NormRGBA8UI, Floating};
 use luminance::render_state::RenderState;
 use luminance::shader::program::{Program, Uniform};
 use luminance::tess::{Mode, TessBuilder};
@@ -87,7 +87,7 @@ struct ShaderInterface {
     mvp: Uniform<[[f32; 4]; 4]>,
 }
 
-fn load_file_into_vec(filename: &str) -> Vec<u8> {
+fn load_file_into_vec(filename: &Path) -> Vec<u8> {
     let file = File::open(filename).unwrap();
     let mut file = BufReader::new(file);
     let mut buf = vec![];
@@ -95,16 +95,20 @@ fn load_file_into_vec(filename: &str) ->
     buf
 }
 
+enum LoadedTexture {
+    Rgba(Texture<Flat, Dim2, NormRGBA8UI>),
+    Rgb(Texture<Flat, Dim2, NormRGB8UI>),
+}
+
 fn main() {
     // Parse arguments.
     let args: Vec<_> = env::args().collect();
-    if args.len() != 4 {
-        eprintln!("Usage: {} <ANM file> <PNG file> <script number>", args[0]);
+    if args.len() != 3 {
+        eprintln!("Usage: {} <ANM file> <script number>", args[0]);
         return;
     }
-    let anm_filename = &args[1];
-    let png_filename = &args[2];
-    let script: u8 = args[3].parse().expect("number");
+    let anm_filename = Path::new(&args[1]);
+    let script: u8 = args[2].parse().expect("number");
 
     // Open the ANM file.
     let buf = load_file_into_vec(anm_filename);
@@ -132,8 +136,16 @@ fn main() {
     let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap();
 
     // Open the image atlas matching this ANM.
-    println!("{} {}", anm0.first_name, png_filename);
-    let tex = load_from_disk(&mut surface, Path::new(png_filename)).expect("texture loading");
+    let png_filename = anm_filename.with_file_name(Path::new(&anm0.png_filename).file_name().unwrap());
+    let tex = match anm0.alpha_filename {
+        Some(ref filename) => {
+            let alpha_filename = anm_filename.with_file_name(Path::new(filename).file_name().unwrap());
+            LoadedTexture::Rgba(png_alpha(&mut surface, &png_filename, &alpha_filename).expect("texture loading"))
+        },
+        None => {
+            LoadedTexture::Rgb(load_from_disk(&mut surface, &png_filename).expect("texture loading"))
+        }
+    };
 
     // set the uniform interface to our type so that we can read textures from the shader
     let (program, _) =
@@ -175,7 +187,10 @@ fn main() {
             .pipeline_builder()
             .pipeline(&back_buffer, [0., 0., 0., 0.], |pipeline, shd_gate| {
                 // bind our fancy texture to the GPU: it gives us a bound texture we can use with the shader
-                let bound_tex = pipeline.bind_texture(&tex);
+                let bound_tex = match &tex {
+                    LoadedTexture::Rgb(tex) => pipeline.bind_texture(tex),
+                    LoadedTexture::Rgba(tex) => pipeline.bind_texture(tex),
+                };
 
                 shd_gate.shade(&program, |rdr_gate, iface| {
                     // update the texture; strictly speaking, this update doesn’t do much: it just tells the GPU
@@ -221,7 +236,7 @@ fn load_from_disk(surface: &mut GlfwSurf
             let (width, height) = img.dimensions();
             let texels = img
                 .pixels()
-                .map(|(x, y, rgb)| (rgb[0], rgb[1], rgb[2]))
+                .map(|(_x, _y, rgb)| (rgb[0], rgb[1], rgb[2]))
                 .collect::<Vec<_>>();
 
             // create the luminance texture; the third argument is the number of mipmaps we want (leave it
@@ -242,3 +257,47 @@ fn load_from_disk(surface: &mut GlfwSurf
         }
     }
 }
+
+fn png_alpha(surface: &mut GlfwSurface, rgb: &Path, alpha: &Path) -> Option<Texture<Flat, Dim2, NormRGBA8UI>> {
+    // load the texture into memory as a whole bloc (i.e. no streaming)
+    match image::open(&alpha) {
+        Ok(img) => {
+            let (width, height) = img.dimensions();
+            let alpha = match img.grayscale() {
+                DynamicImage::ImageLuma8(img) => img,
+                _ => {
+                    eprintln!("cannot convert alpha image {} to grayscale", alpha.display());
+                    return None;
+                }
+            };
+            let img = match image::open(&rgb) {
+                Ok(img) => img,
+                Err(e) => {
+                    eprintln!("cannot open rgb image {}: {}", rgb.display(), e);
+                    return None;
+                },
+            };
+            let texels = img
+                .pixels()
+                .zip(alpha.pixels())
+                .map(|((_x, _y, rgb), luma)| (rgb[0], rgb[1], rgb[1], luma[0]))
+                .collect::<Vec<_>>();
+
+            // create the luminance texture; the third argument is the number of mipmaps we want (leave it
+            // to 0 for now) and the latest is a the sampler to use when sampling the texels in the
+            // shader (we’ll just use the default one)
+            let tex =
+                Texture::new(surface, [width, height], 0, &Sampler::default()).expect("luminance texture creation");
+
+            // the first argument disables mipmap generation (we don’t care so far)
+            tex.upload(GenMipmaps::No, &texels);
+
+            Some(tex)
+        }
+
+        Err(e) => {
+            eprintln!("cannot open alpha image {}: {}", alpha.display(), e);
+            None
+        }
+    }
+}