Mercurial > touhou
comparison examples/eclrenderer.rs @ 706:bca515da9047
examples: use common module.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Fri, 23 Aug 2019 19:46:47 +0200 |
parents | b6c351ca0a35 |
children | 987409d48991 |
comparison
equal
deleted
inserted
replaced
705:ed65f9412bc0 | 706:bca515da9047 |
---|---|
1 use image::GenericImageView; | 1 use luminance::blending::{Equation, Factor}; |
2 use luminance::context::GraphicsContext; | 2 use luminance::context::GraphicsContext; |
3 use luminance::framebuffer::Framebuffer; | 3 use luminance::framebuffer::Framebuffer; |
4 use luminance::pipeline::BoundTexture; | 4 use luminance::pipeline::BoundTexture; |
5 use luminance::pixel::{NormRGB8UI, Floating}; | 5 use luminance::pixel::Floating; |
6 use luminance::render_state::RenderState; | 6 use luminance::render_state::RenderState; |
7 use luminance::shader::program::{Program, Uniform}; | 7 use luminance::shader::program::{Program, Uniform}; |
8 use luminance::tess::{Mode, TessBuilder}; | 8 use luminance::tess::{Mode, TessBuilder}; |
9 use luminance::texture::{Dim2, Flat, Sampler, Texture, GenMipmaps}; | 9 use luminance::texture::{Dim2, Flat}; |
10 use luminance_derive::{Semantics, Vertex, UniformInterface}; | 10 use luminance_derive::{Semantics, Vertex, UniformInterface}; |
11 use luminance_glfw::event::{Action, Key, WindowEvent}; | 11 use luminance_glfw::event::{Action, Key, WindowEvent}; |
12 use luminance_glfw::surface::{GlfwSurface, Surface, WindowDim, WindowOpt}; | 12 use luminance_glfw::surface::{GlfwSurface, Surface, WindowDim, WindowOpt}; |
13 use touhou::th06::anm0::Anm0; | 13 use touhou::th06::anm0::Anm0; |
14 use touhou::th06::anm0_vm::{Sprite, Vertex as FakeVertex}; | 14 use touhou::th06::anm0_vm::{Sprite, Vertex as FakeVertex}; |
16 use touhou::th06::ecl_vm::EclRunner; | 16 use touhou::th06::ecl_vm::EclRunner; |
17 use touhou::th06::enemy::{Enemy, Game, Position}; | 17 use touhou::th06::enemy::{Enemy, Game, Position}; |
18 use touhou::util::math::{perspective, setup_camera}; | 18 use touhou::util::math::{perspective, setup_camera}; |
19 use touhou::util::prng::Prng; | 19 use touhou::util::prng::Prng; |
20 use std::cell::RefCell; | 20 use std::cell::RefCell; |
21 use std::fs::File; | |
22 use std::io::{BufReader, Read}; | |
23 use std::rc::Rc; | 21 use std::rc::Rc; |
24 use std::env; | 22 use std::env; |
25 use std::path::Path; | 23 use std::path::Path; |
24 | |
25 #[path = "common.rs"] | |
26 mod common; | |
27 use common::{load_file_into_vec, load_anm_image, LoadedTexture}; | |
26 | 28 |
27 const VS: &str = r#" | 29 const VS: &str = r#" |
28 in ivec3 in_position; | 30 in ivec3 in_position; |
29 in vec2 in_texcoord; | 31 in vec2 in_texcoord; |
30 in uvec4 in_color; | 32 in uvec4 in_color; |
86 | 88 |
87 #[uniform(name = "mvp")] | 89 #[uniform(name = "mvp")] |
88 mvp: Uniform<[[f32; 4]; 4]>, | 90 mvp: Uniform<[[f32; 4]; 4]>, |
89 } | 91 } |
90 | 92 |
91 fn load_file_into_vec(filename: &str) -> Vec<u8> { | |
92 let file = File::open(filename).unwrap(); | |
93 let mut file = BufReader::new(file); | |
94 let mut buf = vec![]; | |
95 file.read_to_end(&mut buf).unwrap(); | |
96 buf | |
97 } | |
98 | |
99 fn main() { | 93 fn main() { |
100 // Parse arguments. | 94 // Parse arguments. |
101 let args: Vec<_> = env::args().collect(); | 95 let args: Vec<_> = env::args().collect(); |
102 if args.len() != 6 { | 96 if args.len() != 5 { |
103 eprintln!("Usage: {} <ECL file> <ANM file> <PNG file> <easy|normal|hard|lunatic> <sub number>", args[0]); | 97 eprintln!("Usage: {} <ECL file> <ANM file> <easy|normal|hard|lunatic> <sub number>", args[0]); |
104 return; | 98 return; |
105 } | 99 } |
106 let ecl_filename = &args[1]; | 100 let ecl_filename = Path::new(&args[1]); |
107 let anm_filename = &args[2]; | 101 let anm_filename = Path::new(&args[2]); |
108 let png_filename = &args[3]; | 102 let rank: Rank = args[3].parse().expect("rank"); |
109 let rank: Rank = args[4].parse().expect("rank"); | 103 let sub: u16 = args[4].parse().expect("number"); |
110 let sub: u16 = args[5].parse().expect("number"); | |
111 | 104 |
112 // Open the ECL file. | 105 // Open the ECL file. |
113 let buf = load_file_into_vec(ecl_filename); | 106 let buf = load_file_into_vec(ecl_filename); |
114 let (_, ecl) = Ecl::from_slice(&buf).unwrap(); | 107 let (_, ecl) = Ecl::from_slice(&buf).unwrap(); |
115 | 108 |
140 let vertices: [Vertex; 4] = unsafe { std::mem::uninitialized() }; | 133 let vertices: [Vertex; 4] = unsafe { std::mem::uninitialized() }; |
141 | 134 |
142 let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap(); | 135 let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap(); |
143 | 136 |
144 // Open the image atlas matching this ANM. | 137 // Open the image atlas matching this ANM. |
145 let tex = load_from_disk(&mut surface, Path::new(png_filename)).expect("texture loading"); | 138 let tex = load_anm_image(&mut surface, &anm0.borrow(), anm_filename); |
146 | 139 |
147 // set the uniform interface to our type so that we can read textures from the shader | 140 // set the uniform interface to our type so that we can read textures from the shader |
148 let (program, _) = | 141 let (program, _) = |
149 Program::<Semantics, (), ShaderInterface>::from_strings(None, VS, None, FS).expect("program creation"); | 142 Program::<Semantics, (), ShaderInterface>::from_strings(None, VS, None, FS).expect("program creation"); |
150 | 143 |
193 // and use it in the shader | 186 // and use it in the shader |
194 surface | 187 surface |
195 .pipeline_builder() | 188 .pipeline_builder() |
196 .pipeline(&back_buffer, [0., 0., 0., 0.], |pipeline, shd_gate| { | 189 .pipeline(&back_buffer, [0., 0., 0., 0.], |pipeline, shd_gate| { |
197 // bind our fancy texture to the GPU: it gives us a bound texture we can use with the shader | 190 // bind our fancy texture to the GPU: it gives us a bound texture we can use with the shader |
198 let bound_tex = pipeline.bind_texture(&tex); | 191 let bound_tex = match &tex { |
192 LoadedTexture::Rgb(tex) => pipeline.bind_texture(tex), | |
193 LoadedTexture::Rgba(tex) => pipeline.bind_texture(tex), | |
194 }; | |
199 | 195 |
200 shd_gate.shade(&program, |rdr_gate, iface| { | 196 shd_gate.shade(&program, |rdr_gate, iface| { |
201 // update the texture; strictly speaking, this update doesn’t do much: it just tells the GPU | 197 // update the texture; strictly speaking, this update doesn’t do much: it just tells the GPU |
202 // to use the texture passed as argument (no allocation or copy is performed) | 198 // to use the texture passed as argument (no allocation or copy is performed) |
203 iface.color_map.update(&bound_tex); | 199 iface.color_map.update(&bound_tex); |
207 let mvp = view * proj; | 203 let mvp = view * proj; |
208 //println!("{:#?}", mvp); | 204 //println!("{:#?}", mvp); |
209 // TODO: check how to pass by reference. | 205 // TODO: check how to pass by reference. |
210 iface.mvp.update(*mvp.borrow_inner()); | 206 iface.mvp.update(*mvp.borrow_inner()); |
211 | 207 |
212 rdr_gate.render(RenderState::default(), |tess_gate| { | 208 let render_state = RenderState::default() |
209 .set_blending((Equation::Additive, Factor::SrcAlpha, Factor::SrcAlphaComplement)); | |
210 | |
211 rdr_gate.render(render_state, |tess_gate| { | |
213 // render the tessellation to the surface the regular way and let the vertex shader’s | 212 // render the tessellation to the surface the regular way and let the vertex shader’s |
214 // magic do the rest! | 213 // magic do the rest! |
215 tess_gate.render(&mut surface, (&tess).into()); | 214 tess_gate.render(&mut surface, (&tess).into()); |
216 }); | 215 }); |
217 }); | 216 }); |
226 for (x, y, z, sprite) in sprites { | 225 for (x, y, z, sprite) in sprites { |
227 let sprite = sprite.borrow(); | 226 let sprite = sprite.borrow(); |
228 sprite.fill_vertices(&mut fake_vertices, x, y, z); | 227 sprite.fill_vertices(&mut fake_vertices, x, y, z); |
229 } | 228 } |
230 } | 229 } |
231 | |
232 fn load_from_disk(surface: &mut GlfwSurface, path: &Path) -> Option<Texture<Flat, Dim2, NormRGB8UI>> { | |
233 // load the texture into memory as a whole bloc (i.e. no streaming) | |
234 match image::open(&path) { | |
235 Ok(img) => { | |
236 let (width, height) = img.dimensions(); | |
237 let texels = img | |
238 .pixels() | |
239 .map(|(x, y, rgb)| (rgb[0], rgb[1], rgb[2])) | |
240 .collect::<Vec<_>>(); | |
241 | |
242 // create the luminance texture; the third argument is the number of mipmaps we want (leave it | |
243 // to 0 for now) and the latest is a the sampler to use when sampling the texels in the | |
244 // shader (we’ll just use the default one) | |
245 let tex = | |
246 Texture::new(surface, [width, height], 0, &Sampler::default()).expect("luminance texture creation"); | |
247 | |
248 // the first argument disables mipmap generation (we don’t care so far) | |
249 tex.upload(GenMipmaps::No, &texels); | |
250 | |
251 Some(tex) | |
252 } | |
253 | |
254 Err(e) => { | |
255 eprintln!("cannot open image {}: {}", path.display(), e); | |
256 None | |
257 } | |
258 } | |
259 } |