use scene; use tile; use view; use vr; use world as model; use world::HasMap; extern crate memmap; use std::collections::BTreeMap; use std::marker::PhantomData; use std::time::SystemTime; use gfx::{self, tex}; use gfx::traits::FactoryExt; use na::{self, ToHomogeneous}; use num_traits::identities::One; use piston::input::{Button, Input, Key}; const PI: f32 = ::std::f32::consts::PI; const TWO_PI_CIRC: f32 = 2.0 * PI / 256.0; const R1: f32 = 256.0; const R2: f32 = 64.0; const R3: f32 = 128.0; //const SKY_COLOR: [f32; 4] = [0.15, 0.15, 0.75, 1.0]; const SKY_COLOR: [f32; 4] = [0.005, 0.005, 0.01, 1.0]; gfx_defines! { vertex Vertex { pos: [f32; 3] = "a_pos", uv: [f32; 2] = "a_uv", tileidx: u32 = "a_tileidx", } constant Constants { anim: [u32; 4] = "anim", r1: f32 = "R1", r2: f32 = "R2", r3: f32 = "R3", haze: f32 = "haze", hazecolor: [f32; 4] = "hazecolor", } constant Locals { millis: u32 = "millis", treadmill_x: f32 = "treadmill_x", treadmill_y: f32 = "treadmill_y", } pipeline pipe { vbuf: gfx::VertexBuffer = (), trans: gfx::ConstantBuffer<::view::Trans> = "b_trans", constants: gfx::ConstantBuffer = "b_constants", locals: gfx::ConstantBuffer = "b_locals", atlas: gfx::TextureSampler<[f32; 4]> = "t_tiles", pixcolor: gfx::RenderTarget<::view::ColorFormat> = "pixcolor", depth: gfx::DepthTarget<::view::DepthFormat> = gfx::preset::depth::LESS_EQUAL_WRITE, } } fn get_model(world: &model::World) -> (Vec, Vec) { let mut verticies = Vec::new(); let mut indicies = Vec::new(); let mut v = 0; for (r, row) in world.map().rows().enumerate() { for (c, tile) in row.into_iter().enumerate() { let tileidx = tile.val as u32; let alt = match tileidx { 5 => 0.1, 6 => 0.8, 7 => 0.2, 8 => 1.5, 9 => 1.0, 10 | 11 | 12 => 1.0, _ => 0.0, }; let (rf, cf) = (r as f32, c as f32); if alt == 0.0 { verticies.extend_from_slice( &[Vertex { pos: [ cf + 0., rf + 1., 0. ], uv: [0., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 1., 0. ], uv: [1., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 0., 0. ], uv: [1., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 0., rf + 0., 0. ], uv: [0., 1.], tileidx: tileidx },]); indicies.extend_from_slice( &[ v + 0, v + 1, v + 2, v + 2, v + 3, v + 0 ]); v += 4; } else { verticies.extend_from_slice( &[Vertex { pos: [ cf + 0., rf + 1., 0. ], uv: [0., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 1., 0. ], uv: [1., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 0., 0. ], uv: [1., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 0., rf + 0., 0. ], uv: [0., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 0., rf, 0. ], uv: [0., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf, 0. ], uv: [1., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf, alt ], uv: [1., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 0., rf, alt ], uv: [0., 1.], tileidx: tileidx },]); indicies.extend_from_slice( &[ v + 0, v + 1, v + 2, v + 2, v + 3, v + 0, v + 4, v + 5, v + 6, v + 6, v + 7, v + 4 ]); v += 8; } } } (verticies, indicies) } #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum TrackMode { Touch, Press } pub struct WorldScene> { pso: gfx::PipelineState, camera: na::Matrix4, constants: Constants, constants_buffer: gfx::handle::Buffer, constants_dirty: bool, locals: gfx::handle::Buffer, atlas: gfx::handle::ShaderResourceView::View>, sampler: gfx::handle::Sampler, f: PhantomData, vbuf: gfx::handle::Buffer, slice: gfx::Slice, start_time: SystemTime, treadmills: (f32, f32), pads: BTreeMap)>, _worldmap: model::World, lat: u8, lng: u8, } impl> WorldScene { pub fn new(device: &mut D, factory: &mut F, aux_command: &mut ::CommandBuffer) -> WorldScene { let worldmap = get_data_model(); let (model, model_idx) = get_model(&worldmap); let (vertex_buffer, slice) = factory.create_vertex_buffer_with_slice(&model, &model_idx[..]); WorldScene { pso: factory.create_pipeline_simple(VERTEX_SHADER_SRC, FRAGMENT_SHADER_SRC, pipe::new()) .expect("create pipeline"), camera: na::Matrix4::one(), constants: Constants { anim: ANIMDATA, r1: R1, r2: R2, r3: R3, haze: 1.0/2.0f32.sqrt(), hazecolor: SKY_COLOR }, constants_buffer: factory.create_constant_buffer(1), constants_dirty: true, locals: factory.create_constant_buffer(1), atlas: tile::get_tiles::<_, _, view::ColorFormat>(device, factory, aux_command), sampler: factory.create_sampler(tex::SamplerInfo::new(tex::FilterMethod::Jrd, tex::WrapMode::Tile)), f: PhantomData, vbuf: vertex_buffer, slice: slice, start_time: SystemTime::now(), treadmills: (0.0, 0.0), pads: BTreeMap::new(), _worldmap: worldmap, lat: 144, lng: 90, } } fn toroid((x, y): (f32, f32), r1: f32, r2: f32, r3: f32) -> na::Vector3 { let x: f32 = TWO_PI_CIRC * x as f32; let y: f32 = TWO_PI_CIRC * y as f32; na::Vector3::::new(r3 * x.sin(), // use r3 instead of r2 for "deflated" torus (r1 + r2 * x.cos()) * y.cos(), (r1 + r2 * x.cos()) * y.sin()) } } const ANIMDATA: [u32; 4] = [1 << 0 | 1 << 1 | 1 << 2, 0, 1 << (68 % 32) | 1 << (69 % 32) | 1 << (70 % 32) | 1 << (71 % 32) | 1 << (76 % 32), 0]; impl> scene::Scene for WorldScene { fn event(&mut self, event: scene::Event) { use scene::Event::*; use vr::Event::*; match event { // treadmill / camera movement registration Vr(Touch { dev_idx, .. }) => { self.pads.insert(dev_idx, (TrackMode::Touch, None)); }, Vr(Press { dev_idx, .. }) => { self.pads.insert(dev_idx, (TrackMode::Press, None)); }, Vr(Unpress { dev_idx, .. }) => { self.pads.insert(dev_idx, (TrackMode::Touch, None)); }, Vr(Untouch { dev_idx, .. }) => { self.pads.remove(&dev_idx); }, // treadmill / camera reset Piston(Input::Press(Button::Keyboard(Key::Backspace))) => { self.treadmills = (0.0, 0.0); }, Piston(Input::Press(Button::Keyboard(Key::D0))) => { self.camera = na::Matrix4::one(); }, // player movement Piston(Input::Press(Button::Keyboard(Key::Up))) => { self.lat = self.lat.wrapping_sub(1); }, Piston(Input::Press(Button::Keyboard(Key::Down))) => { self.lat = self.lat.wrapping_add(1); }, Piston(Input::Press(Button::Keyboard(Key::Left))) => { self.lng = self.lng.wrapping_sub(1); }, Piston(Input::Press(Button::Keyboard(Key::Right))) => { self.lng = self.lng.wrapping_add(1); }, // scale adjustment Piston(Input::Press(Button::Keyboard(Key::Q))) => { self.constants = Constants { r1: R1 / 2.0, r2: R2 / 2.0, r3: R3 / 2.0, ..self.constants }; self.constants_dirty = true; }, Piston(Input::Press(Button::Keyboard(Key::D1))) => { self.constants = Constants { r1: R1, r2: R2, r3: R3, ..self.constants }; self.constants_dirty = true; }, Piston(Input::Press(Button::Keyboard(Key::D2))) => { self.constants = Constants { r1: R1 * 2.0, r2: R2 * 2.0, r3: R3 * 2.0, ..self.constants }; self.constants_dirty = true; }, Piston(Input::Press(Button::Keyboard(Key::D3))) => { self.constants = Constants { r1: R1 * 4.0, r2: R2 * 4.0, r3: R3 * 4.0, ..self.constants }; self.constants_dirty = true; }, Piston(Input::Press(Button::Keyboard(Key::D4))) => { self.constants = Constants { r1: R1 * 16.0, r2: R2 * 16.0, r3: R3 * 16.0, ..self.constants }; self.constants_dirty = true; }, Piston(Input::Press(Button::Keyboard(Key::H))) => { self.constants = Constants { haze: self.constants.haze * 2.0f32.sqrt().sqrt(), ..self.constants }; println!("haze: {}", self.constants.haze); self.constants_dirty = true; }, Piston(Input::Press(Button::Keyboard(Key::N))) => { self.constants = Constants { haze: self.constants.haze / 2.0f32.sqrt().sqrt(), ..self.constants }; println!("haze: {}", self.constants.haze); self.constants_dirty = true; }, _ => () } } fn update(&mut self, vr: &mut Option, encoder: &mut gfx::Encoder) { const NANOS_PER_MILLI: u32 = 1_000_000; const MILLIS_PER_SEC: u64 = 1_000; let elapsed = self.start_time.elapsed().expect("scene timer"); let millis = elapsed.subsec_nanos() / NANOS_PER_MILLI + (elapsed.as_secs() * MILLIS_PER_SEC) as u32; for (pad, track) in self.pads.iter_mut() { let mode = track.0; if let Some(state) = vr.as_ref().and_then(|vr| vr.get_controller_state(*pad)) { if let Some(old_state) = track.1 { match mode { TrackMode::Touch => { const THRESHOLD: f32 = 0.005; const SCALE: f32 = 32.0; let xdiff = state.rAxis[0].x - old_state.rAxis[0].x; let ydiff = state.rAxis[0].y - old_state.rAxis[0].y; if xdiff.abs() > THRESHOLD { self.treadmills.0 += SCALE * xdiff; } if ydiff.abs() > THRESHOLD { self.treadmills.1 += SCALE * ydiff; } }, TrackMode::Press => { let rot = na::Vector3::new(0.0, 0.0, 0.0); let speed = R2 * 0.005; if state.rAxis[0].x > 0.5 { self.camera = na::Similarity3::new(na::Vector3::new(-speed, 0.0, 0.0), rot, 1.0).to_homogeneous() * self.camera; } if state.rAxis[0].x < -0.5 { self.camera = na::Similarity3::new(na::Vector3::new( speed, 0.0, 0.0), rot, 1.0).to_homogeneous() * self.camera; } if state.rAxis[0].y > 0.5 { self.camera = na::Similarity3::new(na::Vector3::new( 0.0, -speed, 0.0), rot, 1.0).to_homogeneous() * self.camera; } if state.rAxis[0].y < -0.5 { self.camera = na::Similarity3::new(na::Vector3::new( 0.0, speed, 0.0), rot, 1.0).to_homogeneous() * self.camera; } }, } if state.unPacketNum == old_state.unPacketNum { continue; } } *track = (mode, Some(state)); } } if self.constants_dirty { self.constants_dirty = false; encoder.update_constant_buffer(&self.constants_buffer, &self.constants); } encoder.update_constant_buffer(&self.locals, &Locals { millis: millis, treadmill_x: self.treadmills.0, treadmill_y: self.treadmills.1 }); } fn render(&self, _factory: &mut F, encoder: &mut gfx::Encoder, trans: &gfx::handle::Buffer, target: &gfx::handle::RenderTargetView, depth: &gfx::handle::DepthStencilView) { encoder.clear(&target, SKY_COLOR); encoder.clear_depth(&depth, 1.0); let pipe = pipe::Data { vbuf: self.vbuf.clone(), trans: trans.clone(), constants: self.constants_buffer.clone(), locals: self.locals.clone(), atlas: (self.atlas.clone(), self.sampler.clone()), pixcolor: target.clone(), depth: depth.clone(), }; encoder.draw(&self.slice, &self.pso, &pipe); } fn origin(&self) -> na::Matrix4 { let (r1, r2, r3) = (self.constants.r1, self.constants.r2, self.constants.r3); let (y, x) = (self.lat as f32 + 0.5, self.lng as f32 + 0.5); // center of tile let eye = Self::toroid((x, y), r1, r2, r3); let looktgt = Self::toroid((x, y - 1.0), r1, r2, r3); // look ahead = north let normal = Self::toroid((x, y), 0.0, r2, r2) * na::Vector3::new(r2 / r3, 1.0, 1.0); self.camera * na::Isometry3::look_at_rh(eye.as_point(), looktgt.as_point(), &normal, ).to_homogeneous() } } fn get_data_model() -> model::World { use self::memmap::{Mmap, Protection}; use std::mem::transmute; fn mmap_to_rows<'a, M: model::HasMap>(mmap: &memmap::Mmap) -> &'a M where M: Copy + 'a { assert_eq!(::std::mem::size_of::(), mmap.len()); unsafe { transmute::<*const u8, &M>(mmap.ptr()) } } let filename = "data/WORLD.MAP"; let file_mmap = Mmap::open_path(filename, Protection::Read).unwrap(); mmap_to_rows::(&file_mmap).clone() } const VERTEX_SHADER_SRC: &'static [u8] = include_bytes!("shader/torus_vertex.glsl"); const FRAGMENT_SHADER_SRC: &'static [u8] = include_bytes!("shader/tile_frag.glsl");