Mercurial > touhou
changeset 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 | 81232dac8136 |
children | ed65f9412bc0 |
files | examples/anmrenderer.rs src/th06/anm0.rs |
diffstat | 2 files changed, 79 insertions(+), 20 deletions(-) [+] |
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 + } + } +}
--- a/src/th06/anm0.rs +++ b/src/th06/anm0.rs @@ -58,10 +58,10 @@ pub struct Anm0 { pub format: u32, /// File name of the main image. - pub first_name: String, + pub png_filename: String, /// File name of an alpha channel image. - pub second_name: String, + pub alpha_filename: Option<String>, /// A list of sprites, coordinates into the attached image. pub sprites: Vec<Sprite>, @@ -187,7 +187,7 @@ fn parse_anm0(input: &[u8]) -> IResult<& let (i, sprite_offsets) = many_m_n(num_sprites, num_sprites, le_u32)(i)?; let (_, script_offsets) = many_m_n(num_scripts, num_scripts, tuple((le_u32, le_u32)))(i)?; - let first_name = if first_name_offset > 0 { + let png_filename = if first_name_offset > 0 { if input.len() < start_offset + first_name_offset as usize { return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); } @@ -198,15 +198,15 @@ fn parse_anm0(input: &[u8]) -> IResult<& String::new() }; - let second_name = if second_name_offset > 0 { + let alpha_filename = if second_name_offset > 0 { if input.len() < start_offset + second_name_offset as usize { return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); } let i = &input[start_offset + second_name_offset as usize..]; let (_, name) = parse_name(i)?; - name + Some(name) } else { - String::new() + None }; let mut sprites = vec![]; @@ -272,8 +272,8 @@ fn parse_anm0(input: &[u8]) -> IResult<& let anm0 = Anm0 { size: (width, height), format, - first_name, - second_name, + png_filename, + alpha_filename, sprites, scripts, };