Mercurial > touhou
comparison examples/stdrenderer.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, TessSliceIndex}; | 8 use luminance::tess::{Mode, TessBuilder, TessSliceIndex}; |
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::{AnmRunner, Sprite, Vertex as FakeVertex}; | 14 use touhou::th06::anm0_vm::{AnmRunner, Sprite, Vertex as FakeVertex}; |
15 use touhou::th06::std::{Stage, Position, Box2D}; | 15 use touhou::th06::std::{Stage, Position, Box2D}; |
16 use touhou::th06::std_vm::StageRunner; | 16 use touhou::th06::std_vm::StageRunner; |
17 use touhou::util::prng::Prng; | 17 use touhou::util::prng::Prng; |
18 use touhou::util::math::perspective; | 18 use touhou::util::math::perspective; |
19 use std::cell::RefCell; | 19 use std::cell::RefCell; |
20 use std::fs::File; | |
21 use std::io::{BufReader, Read}; | |
22 use std::rc::Rc; | 20 use std::rc::Rc; |
23 use std::env; | 21 use std::env; |
24 use std::path::Path; | 22 use std::path::Path; |
23 | |
24 #[path = "common.rs"] | |
25 mod common; | |
26 use common::{load_file_into_vec, load_anm_image, LoadedTexture}; | |
25 | 27 |
26 const VS: &str = r#" | 28 const VS: &str = r#" |
27 in ivec3 in_position; | 29 in ivec3 in_position; |
28 in vec2 in_texcoord; | 30 in vec2 in_texcoord; |
29 in uvec4 in_color; | 31 in uvec4 in_color; |
105 | 107 |
106 #[uniform(name = "fog_color")] | 108 #[uniform(name = "fog_color")] |
107 fog_color: Uniform<[f32; 4]>, | 109 fog_color: Uniform<[f32; 4]>, |
108 } | 110 } |
109 | 111 |
110 fn load_file_into_vec(filename: &str) -> Vec<u8> { | |
111 let file = File::open(filename).unwrap(); | |
112 let mut file = BufReader::new(file); | |
113 let mut buf = vec![]; | |
114 file.read_to_end(&mut buf).unwrap(); | |
115 buf | |
116 } | |
117 | |
118 fn main() { | 112 fn main() { |
119 // Parse arguments. | 113 // Parse arguments. |
120 let args: Vec<_> = env::args().collect(); | 114 let args: Vec<_> = env::args().collect(); |
121 if args.len() != 4 { | 115 if args.len() != 3 { |
122 eprintln!("Usage: {} <STD file> <ANM file> <PNG file>", args[0]); | 116 eprintln!("Usage: {} <STD file> <ANM file>", args[0]); |
123 return; | 117 return; |
124 } | 118 } |
125 let std_filename = &args[1]; | 119 let std_filename = Path::new(&args[1]); |
126 let anm_filename = &args[2]; | 120 let anm_filename = Path::new(&args[2]); |
127 let png_filename = &args[3]; | |
128 | 121 |
129 // Open the STD file. | 122 // Open the STD file. |
130 let buf = load_file_into_vec(std_filename); | 123 let buf = load_file_into_vec(std_filename); |
131 let (_, stage) = Stage::from_slice(&buf).unwrap(); | 124 let (_, stage) = Stage::from_slice(&buf).unwrap(); |
132 | 125 |
166 let mut stage_runner = StageRunner::new(Rc::new(RefCell::new(stage))); | 159 let mut stage_runner = StageRunner::new(Rc::new(RefCell::new(stage))); |
167 | 160 |
168 let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap(); | 161 let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap(); |
169 | 162 |
170 // Open the image atlas matching this ANM. | 163 // Open the image atlas matching this ANM. |
171 let tex = load_from_disk(&mut surface, Path::new(png_filename)).expect("texture loading"); | 164 let tex = load_anm_image(&mut surface, &anm0, anm_filename); |
172 | 165 |
173 // set the uniform interface to our type so that we can read textures from the shader | 166 // set the uniform interface to our type so that we can read textures from the shader |
174 let (program, _) = | 167 let (program, _) = |
175 Program::<Semantics, (), ShaderInterface>::from_strings(None, VS, None, FS).expect("program creation"); | 168 Program::<Semantics, (), ShaderInterface>::from_strings(None, VS, None, FS).expect("program creation"); |
176 | 169 |
205 // and use it in the shader | 198 // and use it in the shader |
206 surface | 199 surface |
207 .pipeline_builder() | 200 .pipeline_builder() |
208 .pipeline(&back_buffer, [0., 0., 0., 0.], |pipeline, shd_gate| { | 201 .pipeline(&back_buffer, [0., 0., 0., 0.], |pipeline, shd_gate| { |
209 // bind our fancy texture to the GPU: it gives us a bound texture we can use with the shader | 202 // bind our fancy texture to the GPU: it gives us a bound texture we can use with the shader |
210 let bound_tex = pipeline.bind_texture(&tex); | 203 let bound_tex = match &tex { |
204 LoadedTexture::Rgb(tex) => pipeline.bind_texture(tex), | |
205 LoadedTexture::Rgba(tex) => pipeline.bind_texture(tex), | |
206 }; | |
211 | 207 |
212 shd_gate.shade(&program, |rdr_gate, iface| { | 208 shd_gate.shade(&program, |rdr_gate, iface| { |
213 // update the texture; strictly speaking, this update doesn’t do much: it just tells the GPU | 209 // update the texture; strictly speaking, this update doesn’t do much: it just tells the GPU |
214 // to use the texture passed as argument (no allocation or copy is performed) | 210 // to use the texture passed as argument (no allocation or copy is performed) |
215 iface.color_map.update(&bound_tex); | 211 iface.color_map.update(&bound_tex); |
224 let far = stage_runner.fog_far - 101010101. / 2010101.; | 220 let far = stage_runner.fog_far - 101010101. / 2010101.; |
225 iface.fog_color.update(stage_runner.fog_color); | 221 iface.fog_color.update(stage_runner.fog_color); |
226 iface.fog_scale.update(1. / (far - near)); | 222 iface.fog_scale.update(1. / (far - near)); |
227 iface.fog_end.update(far); | 223 iface.fog_end.update(far); |
228 | 224 |
225 let render_state = RenderState::default() | |
226 .set_blending((Equation::Additive, Factor::SrcAlpha, Factor::SrcAlphaComplement)); | |
227 | |
229 let stage = stage_runner.stage.borrow(); | 228 let stage = stage_runner.stage.borrow(); |
230 for instance in stage.instances.iter() { | 229 for instance in stage.instances.iter() { |
231 iface.instance_position.update([instance.pos.x, instance.pos.y, instance.pos.z]); | 230 iface.instance_position.update([instance.pos.x, instance.pos.y, instance.pos.z]); |
232 | 231 |
233 rdr_gate.render(RenderState::default(), |tess_gate| { | 232 rdr_gate.render(render_state, |tess_gate| { |
234 let (begin, end) = indices[instance.id as usize]; | 233 let (begin, end) = indices[instance.id as usize]; |
235 tess_gate.render(&mut surface, tess.slice(begin..end)); | 234 tess_gate.render(&mut surface, tess.slice(begin..end)); |
236 }); | 235 }); |
237 } | 236 } |
238 }); | 237 }); |
245 fn fill_vertices(sprite: Rc<RefCell<Sprite>>, vertices: &mut [Vertex; 6], x: f32, y: f32, z: f32) { | 244 fn fill_vertices(sprite: Rc<RefCell<Sprite>>, vertices: &mut [Vertex; 6], x: f32, y: f32, z: f32) { |
246 let mut fake_vertices = unsafe { std::mem::transmute::<&mut [Vertex; 6], &mut [FakeVertex; 4]>(vertices) }; | 245 let mut fake_vertices = unsafe { std::mem::transmute::<&mut [Vertex; 6], &mut [FakeVertex; 4]>(vertices) }; |
247 let sprite = sprite.borrow(); | 246 let sprite = sprite.borrow(); |
248 sprite.fill_vertices(&mut fake_vertices, x, y, z); | 247 sprite.fill_vertices(&mut fake_vertices, x, y, z); |
249 } | 248 } |
250 | |
251 fn load_from_disk(surface: &mut GlfwSurface, path: &Path) -> Option<Texture<Flat, Dim2, NormRGB8UI>> { | |
252 // load the texture into memory as a whole bloc (i.e. no streaming) | |
253 match image::open(&path) { | |
254 Ok(img) => { | |
255 let (width, height) = img.dimensions(); | |
256 let texels = img | |
257 .pixels() | |
258 .map(|(_x, _y, rgb)| (rgb[0], rgb[1], rgb[2])) | |
259 .collect::<Vec<_>>(); | |
260 | |
261 // create the luminance texture; the third argument is the number of mipmaps we want (leave it | |
262 // to 0 for now) and the latest is a the sampler to use when sampling the texels in the | |
263 // shader (we’ll just use the default one) | |
264 let tex = | |
265 Texture::new(surface, [width, height], 0, &Sampler::default()).expect("luminance texture creation"); | |
266 | |
267 // the first argument disables mipmap generation (we don’t care so far) | |
268 tex.upload(GenMipmaps::No, &texels); | |
269 | |
270 Some(tex) | |
271 } | |
272 | |
273 Err(e) => { | |
274 eprintln!("cannot open image {}: {}", path.display(), e); | |
275 None | |
276 } | |
277 } | |
278 } |