diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio.rs | 52 | ||||
| -rw-r--r-- | src/context.rs | 64 | ||||
| -rw-r--r-- | src/framebuffer.rs | 6 | ||||
| -rw-r--r-- | src/lib.rs | 327 | ||||
| -rw-r--r-- | src/state.rs | 47 |
5 files changed, 307 insertions, 189 deletions
diff --git a/src/audio.rs b/src/audio.rs index 5cbce90..0dca32e 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -103,28 +103,53 @@ impl Assets { #[cfg(not(target_arch = "wasm32"))] pub struct Context { + manager: kira::manager::AudioManager, } #[cfg(not(target_arch = "wasm32"))] impl Context { pub fn new() -> Self { Self { + manager: kira::manager::AudioManager::new(kira::manager::AudioManagerSettings::default()) + .expect("failed to create audio manager"), } } } #[cfg(not(target_arch = "wasm32"))] pub struct Audio { + data: kira::sound::static_sound::StaticSoundData, } #[cfg(not(target_arch = "wasm32"))] impl Audio { - pub fn new(ctx: &Context, _bytes: &[u8]) -> Self { + pub fn new(_ctx: &Context, bytes: &'static [u8]) -> Self { Self { + data: kira::sound::static_sound::StaticSoundData::from_cursor(std::io::Cursor::new(bytes)) + .expect("failed to decode audio"), } } - pub fn play(&self, ctx: &Context, looping: Option<(Option<f64>, Option<f64>)>) { + pub fn play( + &self, + ctx: &mut Context, + looping: Option<(Option<f64>, Option<f64>)> + ) -> Result<kira::sound::static_sound::StaticSoundHandle, String> + { + let sd = if let Some((ss, se)) = looping { + let start = if let Some(s) = ss { s } else { 0.0 }; + if let Some(e) = se { + self.data.loop_region(start..e) + } else { + self.data.loop_region(start..) + } + } else { + self.data.clone() + }; + match ctx.manager.play(sd) { + Ok(h) => Ok(h), + Err(e) => Err(e.to_string()), + } } } @@ -132,6 +157,7 @@ impl Audio { pub struct Assets { pub ctx: Context, pub audio: HashMap<String, Audio>, + pub music_handle: Option<kira::sound::static_sound::StaticSoundHandle>, } #[cfg(not(target_arch = "wasm32"))] @@ -144,19 +170,37 @@ impl Assets { Self { ctx, audio, + music_handle: None, } } pub fn play_sfx(&mut self, name: &str) { if let Some(a) = self.audio.get(name) { - a.play(&self.ctx, None); + if let Err(e) = a.play(&mut self.ctx, None) { + log::warn!("failed to play sound {}: {}", name, e); + } } } pub fn is_music_playing(&self) -> bool { - false + if let Some(mh) = &self.music_handle { + mh.state() == kira::sound::PlaybackState::Playing + } else { false } } pub fn play_music(&mut self, name: &str, start: Option<f64>, end: Option<f64>) { + if let Some(s) = &mut self.music_handle { + let _ = s.stop(kira::tween::Tween::default()); + } + if let Some(a) = self.audio.get(name) { + match a.play(&mut self.ctx, Some((start, end))) { + Ok(h) => { + self.music_handle = Some(h); + }, + Err(e) => { + log::warn!("failed to play music {}: {}", name, e); + } + } + } } } diff --git a/src/context.rs b/src/context.rs index c6c68bc..28d0a79 100644 --- a/src/context.rs +++ b/src/context.rs @@ -26,32 +26,41 @@ pub fn compute_upscale(windoww: u32, windowh: u32) -> u32 { (ratio - 1).max(1) } +#[cfg(not(target_arch = "wasm32"))] pub struct Context { - pub window: winit::window::Window, + pub window: sdl2::video::Window, pub gl: glow::Context, pub emptyvao: glow::VertexArray, + pub timer: sdl2::TimerSubsystem, +} - #[cfg(target_arch = "wasm32")] + +#[cfg(target_arch = "wasm32")] +pub struct Context { + pub window: winit::window::Window, + pub gl: glow::Context, + pub emptyvao: glow::VertexArray, pub performance: web_sys::Performance, } impl Context { - pub fn new(window: winit::window::Window, gl: glow::Context) -> Self { - unsafe { - gl.clear_color(0.1, 0.1, 0.1, 1.0); - gl.clear_depth_f32(1.0); - - gl.enable(glow::DEPTH_TEST); - gl.depth_func(glow::LEQUAL); - - gl.enable(glow::BLEND); - gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); - - gl.enable(glow::STENCIL_TEST); - - gl.cull_face(glow::FRONT); - } + #[cfg(not(target_arch = "wasm32"))] + pub fn new(sdl: sdl2::Sdl, window: sdl2::video::Window, gl: glow::Context) -> Self { + let emptyvao = unsafe { + gl.create_vertex_array().expect("failed to initialize vao") + }; + let ret = Self { + window, + gl, + emptyvao, + timer: sdl.timer().expect("failed to initialize timer subsystem"), + }; + ret.init(); + ret + } + #[cfg(target_arch = "wasm32")] + pub fn new(window: winit::window::Window, gl: glow::Context) -> Self { let emptyvao = unsafe { gl.create_vertex_array().expect("failed to initialize vao") }; @@ -59,7 +68,7 @@ impl Context { #[cfg(target_arch = "wasm32")] unsafe { js_track_resized_setup(); } - Self { + let ret = Self { window, gl, emptyvao, @@ -67,6 +76,25 @@ impl Context { #[cfg(target_arch = "wasm32")] performance: web_sys::window().expect("failed to find window") .performance().expect("failed to get performance"), + }; + ret.init(); + ret + } + + pub fn init(&self) { + unsafe { + self.gl.clear_color(0.1, 0.1, 0.1, 1.0); + self.gl.clear_depth_f32(1.0); + + self.gl.enable(glow::DEPTH_TEST); + self.gl.depth_func(glow::LEQUAL); + + self.gl.enable(glow::BLEND); + self.gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); + + self.gl.enable(glow::STENCIL_TEST); + + self.gl.cull_face(glow::FRONT); } } diff --git a/src/framebuffer.rs b/src/framebuffer.rs index c0ac72b..fb6ab02 100644 --- a/src/framebuffer.rs +++ b/src/framebuffer.rs @@ -11,7 +11,13 @@ pub struct Framebuffer { impl Framebuffer { pub fn screen(ctx: &context::Context) -> Self { + #[cfg(target_arch = "wasm32")] let (windoww, windowh): (f32, f32) = ctx.window.inner_size().into(); + #[cfg(not(target_arch = "wasm32"))] + let (windoww, windowh) = { + let (w, h) = ctx.window.size(); + (w as f32, h as f32) + }; let ratio = context::compute_upscale(windoww as _, windowh as _) as f32; let upscalew = context::RENDER_WIDTH * ratio; let upscaleh = context::RENDER_HEIGHT * ratio; @@ -35,113 +35,123 @@ where } } -pub fn event_loop_body<G>(event: winit::event::Event<()>, elwt: &winit::event_loop::EventLoopWindowTarget<()>) - where G: state::Game + 'static, +#[cfg(not(target_arch = "wasm32"))] +pub async fn run<'a, F, G, Fut>(title: &str, gnew: F) +where + Fut: std::future::Future<Output = G>, + G: state::Game + 'static, + F: (Fn(&'a context::Context) -> Fut), { - contextualize(|ctx, st, game: &mut G| { - match &event { - winit::event::Event::WindowEvent { - event: wev, - window_id, - .. - } => match wev { - winit::event::WindowEvent::CloseRequested - if *window_id == ctx.window.id() => elwt.exit(), - winit::event::WindowEvent::Resized{..} => { - #[cfg(target_arch = "wasm32")] - ctx.maximize_canvas(); + env_logger::Builder::new() + .filter(None, log::LevelFilter::Info) + .init(); + + log::info!("hello computer, starting up..."); + + let (sdl, window, gl, mut event_loop, _gl_context) = { + let sdl = sdl2::init().expect("failed to initialize SDL2"); + let video = sdl.video().expect("failed to initialize SDL2 video"); + // let gl_attr = video.gl_attr(); + // gl_attr.set_context_profile(sdl2::video::GLProfile::Core); + // gl_attr.set_context_version(3, 0); + let window = video + .window(title, 640, 360) + .opengl() + // .fullscreen_desktop() + .resizable() + .build() + .unwrap(); + let gl_context = window.gl_create_context().unwrap(); + let gl = unsafe { + glow::Context::from_loader_function(|s| video.gl_get_proc_address(s) as *const _) + }; + let event_loop = sdl.event_pump().unwrap(); + (sdl, window, gl, event_loop, gl_context) + }; + + let ctx = Box::leak(Box::new(context::Context::new(sdl, window, gl))); + let game = Box::leak(Box::new(gnew(ctx).await)); + let st = Box::leak(Box::new(state::State::new(&ctx))); + + unsafe { + CTX = Some(ctx as _); + ST = Some(st as _); + G = Some(game as *mut G as *mut std::ffi::c_void); + } + + 'running: loop { + for event in event_loop.poll_iter() { + match event { + sdl2::event::Event::Quit {..} => { + log::info!("bye!"); + break 'running; + }, + sdl2::event::Event::Window { win_event: sdl2::event::WindowEvent::Resized(_, _), .. } => { st.handle_resize(&ctx); }, - winit::event::WindowEvent::Focused(false) => { + sdl2::event::Event::Window { win_event: sdl2::event::WindowEvent::FocusLost, .. } => { st.keys = state::Keys::new(); }, - winit::event::WindowEvent::CursorMoved { position, ..} => { - st.mouse_moved(&ctx, position.x as f32, position.y as f32, game); + sdl2::event::Event::MouseMotion { x, y, .. } => { + st.mouse_moved(&ctx, x as f32, y as f32, game); + }, + sdl2::event::Event::MouseButtonDown {..} => { + st.mouse_pressed(&ctx, game) + }, + sdl2::event::Event::MouseButtonUp {..} => { + st.mouse_released(&ctx) + }, + sdl2::event::Event::KeyDown { keycode: Some(key), repeat: false, .. } => { + st.key_pressed(&ctx, key) + }, + sdl2::event::Event::KeyUp { keycode: Some(key), repeat: false, .. } => { + st.key_released(&ctx, key) }, - winit::event::WindowEvent::MouseInput { - button, - state, - .. - } => match state { - winit::event::ElementState::Pressed => { - st.mouse_pressed(&ctx, *button, game) - }, - winit::event::ElementState::Released => { - st.mouse_released(&ctx, *button) - }, - } - winit::event::WindowEvent::KeyboardInput { - event: winit::event::KeyEvent { - physical_key: winit::keyboard::PhysicalKey::Code(key), - state, - repeat: false, - .. - }, - .. - } => match state { - winit::event::ElementState::Pressed => { - st.key_pressed(&ctx, *key) - }, - winit::event::ElementState::Released => { - st.key_released(&ctx, *key) - }, - } _ => {}, - }, - - winit::event::Event::AboutToWait => { - if ctx.resize_necessary() { - #[cfg(target_arch = "wasm32")] - ctx.maximize_canvas(); - st.handle_resize(&ctx); - } - if let Some(f) = &mut st.request { - match std::future::Future::poll(f.as_mut(), &mut st.waker_ctx) { - std::task::Poll::Pending => {}, - std::task::Poll::Ready(res) => { - st.request = None; - match res { - Ok(r) => st.request_returned(&ctx, game, r), - Err(e) => log::warn!("error during HTTP request: {}", e), - } - }, + } + } + if ctx.resize_necessary() { + st.handle_resize(&ctx); + } + if let Some(f) = &mut st.request { + match std::future::Future::poll(f.as_mut(), &mut st.waker_ctx) { + std::task::Poll::Pending => {}, + std::task::Poll::Ready(res) => { + st.request = None; + match res { + Ok(r) => st.request_returned(&ctx, game, r), + Err(e) => log::warn!("error during HTTP request: {}", e), } - // f.poll(); - } - st.run_update(&ctx, game); - st.run_render(&ctx, game); - ctx.window.request_redraw(); - }, - - _ => {}, + }, + } } - }); + st.run_update(&ctx, game); + st.run_render(&ctx, game); + ctx.window.gl_swap_window(); + } + + // event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll); + // event_loop.run(|event, elwt| { + // event_loop_body::<G>(event, elwt); + // }).expect("window closed"); } +#[cfg(target_arch = "wasm32")] pub async fn run<'a, F, G, Fut>(gnew: F) where Fut: std::future::Future<Output = G>, G: state::Game + 'static, F: (Fn(&'a context::Context) -> Fut), { - #[cfg(target_arch = "wasm32")] - { - console_log::init_with_level(log::Level::Debug).unwrap(); - console_error_panic_hook::set_once(); - tracing_wasm::set_as_global_default(); - } - #[cfg(not(target_arch = "wasm32"))] - { - env_logger::Builder::new() - .filter(None, log::LevelFilter::Info) - .init(); - } + console_log::init_with_level(log::Level::Debug).unwrap(); + console_error_panic_hook::set_once(); + tracing_wasm::set_as_global_default(); + log::info!("hello computer, starting up..."); let event_loop = winit::event_loop::EventLoop::new() .expect("failed to initialize event loop"); - #[cfg(target_arch = "wasm32")] let (window, gl) = { let window = winit::window::WindowBuilder::new() .with_maximized(true) @@ -163,63 +173,10 @@ where (window, gl) }; - #[cfg(not(target_arch = "wasm32"))] - let (window, gl) = { - use glutin::config::GlConfig; - use glutin::context::NotCurrentGlContext; - use glutin::display::{GlDisplay, GetGlDisplay}; - use glutin::surface::GlSurface; - use raw_window_handle::HasRawWindowHandle; - use glutin_winit::GlWindow; - let window_builder = winit::window::WindowBuilder::new() - .with_title("teleia") - .with_maximized(true) - .with_decorations(false); - let template = glutin::config::ConfigTemplateBuilder::new(); - let display_builder = glutin_winit::DisplayBuilder::new().with_window_builder(Some(window_builder)); - let (window, gl_config) = display_builder - .build(&event_loop, template, |configs| { - configs.reduce(|a, c| { - if c.num_samples() > a.num_samples() { c } else { a } - }).expect("failed to obtain select configuration") - }).expect("failed to obtain opengl display"); - let window = window.expect("failed to create window"); - let raw_window_handle = window.raw_window_handle(); - let gl_display = gl_config.display(); - let context_attributes = glutin::context::ContextAttributesBuilder::new() - // .with_context_api(glutin::context::ContextApi::OpenGl(Some(glutin::context::Version { - // major: 3, - // minor: 3, - // }))) - .build(Some(raw_window_handle)); - unsafe { - let not_current_gl_context = gl_display.create_context(&gl_config, &context_attributes) - .expect("failed to obtain opengl context"); - let attrs = window.build_surface_attributes(Default::default()); - let gl_surface = gl_display.create_window_surface(&gl_config, &attrs) - .expect("failed to create opengl surface"); - let gl_context = not_current_gl_context.make_current(&gl_surface) - .expect("failed to set openglt context"); - let gl = glow::Context::from_loader_function_cstr(|s| gl_display.get_proc_address(s)); - gl_surface - .set_swap_interval(&gl_context, glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())) - .expect("failed to set swap interval"); - (window, gl) - } - }; - let ctx = Box::leak(Box::new(context::Context::new(window, gl))); - - #[cfg(target_arch = "wasm32")] - { - ctx.maximize_canvas(); - } - + ctx.maximize_canvas(); let game = Box::leak(Box::new(gnew(ctx).await)); let st = Box::leak(Box::new(state::State::new(&ctx))); - // request = Some(Box::new(async { - // "foo".to_owned() - // })); unsafe { CTX = Some(ctx as _); @@ -227,20 +184,84 @@ where G = Some(game as *mut G as *mut std::ffi::c_void); } + event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait); + event_loop.spawn(|event, elwt| { + contextualize(|ctx, st, game: &mut G| { + match &event { + winit::event::Event::WindowEvent { + event: wev, + window_id, + .. + } => match wev { + winit::event::WindowEvent::CloseRequested + if *window_id == ctx.window.id() => elwt.exit(), + winit::event::WindowEvent::Resized{..} => { + #[cfg(target_arch = "wasm32")] + ctx.maximize_canvas(); + st.handle_resize(&ctx); + }, + winit::event::WindowEvent::Focused(false) => { + st.keys = state::Keys::new(); + }, + winit::event::WindowEvent::CursorMoved { position, ..} => { + st.mouse_moved(&ctx, position.x as f32, position.y as f32, game); + }, + winit::event::WindowEvent::MouseInput { + state, + .. + } => match state { + winit::event::ElementState::Pressed => { + st.mouse_pressed(&ctx, game) + }, + winit::event::ElementState::Released => { + st.mouse_released(&ctx) + }, + } + winit::event::WindowEvent::KeyboardInput { + event: winit::event::KeyEvent { + physical_key: winit::keyboard::PhysicalKey::Code(key), + state, + repeat: false, + .. + }, + .. + } => match state { + winit::event::ElementState::Pressed => { + st.key_pressed(&ctx, *key) + }, + winit::event::ElementState::Released => { + st.key_released(&ctx, *key) + }, + } + _ => {}, + }, + + winit::event::Event::AboutToWait => { + if ctx.resize_necessary() { + #[cfg(target_arch = "wasm32")] + ctx.maximize_canvas(); + st.handle_resize(&ctx); + } + if let Some(f) = &mut st.request { + match std::future::Future::poll(f.as_mut(), &mut st.waker_ctx) { + std::task::Poll::Pending => {}, + std::task::Poll::Ready(res) => { + st.request = None; + match res { + Ok(r) => st.request_returned(&ctx, game, r), + Err(e) => log::warn!("error during HTTP request: {}", e), + } + }, + } + // f.poll(); + } + st.run_update(&ctx, game); + st.run_render(&ctx, game); + ctx.window.request_redraw(); + }, - #[cfg(target_arch = "wasm32")] - { - event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait); - event_loop.spawn(|event, elwt| { - event_loop_body::<G>(event, elwt); + _ => {}, + } }); - } - - #[cfg(not(target_arch = "wasm32"))] - { - event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll); - event_loop.run(|event, elwt| { - event_loop_body::<G>(event, elwt); - }).expect("window closed"); - } + }); } diff --git a/src/state.rs b/src/state.rs index c5478a9..0be163b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -93,11 +93,13 @@ pub struct PointLight { pub attenuation: glam::Vec2, } -#[cfg(target_arch = "wasm32")] type Timestamp = f64; +#[cfg(target_arch = "wasm32")] +type Keycode = winit::keyboard::KeyCode; + #[cfg(not(target_arch = "wasm32"))] -type Timestamp = std::time::Instant; +type Keycode = sdl2::keyboard::Keycode; pub struct State { pub acc: f64, @@ -105,7 +107,7 @@ pub struct State { pub tick: u64, pub rebinding: Option<Key>, - pub keybindings: BiHashMap<winit::keyboard::KeyCode, Key>, + pub keybindings: BiHashMap<Keycode, Key>, pub keys: Keys, pub screen: framebuffer::Framebuffer, @@ -131,11 +133,13 @@ pub fn now(ctx: &context::Context) -> Timestamp { } #[cfg(not(target_arch = "wasm32"))] -pub fn now(_ctx: &context::Context) -> Timestamp { - std::time::Instant::now() +pub fn now(ctx: &context::Context) -> Timestamp { + let ms = ctx.timer.ticks64(); + (ms as f64) / 1000.0 } -pub fn default_keybindings() -> BiHashMap<winit::keyboard::KeyCode, Key> { +#[cfg(target_arch = "wasm32")] +pub fn default_keybindings() -> BiHashMap<Keycode, Key> { BiHashMap::from_iter(vec![ (winit::keyboard::KeyCode::KeyW, Key::Up), (winit::keyboard::KeyCode::KeyS, Key::Down), @@ -150,6 +154,22 @@ pub fn default_keybindings() -> BiHashMap<winit::keyboard::KeyCode, Key> { ]) } +#[cfg(not(target_arch = "wasm32"))] +pub fn default_keybindings() -> BiHashMap<Keycode, Key> { + BiHashMap::from_iter(vec![ + (sdl2::keyboard::Keycode::W, Key::Up), + (sdl2::keyboard::Keycode::S, Key::Down), + (sdl2::keyboard::Keycode::A, Key::Left), + (sdl2::keyboard::Keycode::D, Key::Right), + (sdl2::keyboard::Keycode::NUM_1, Key::A), + (sdl2::keyboard::Keycode::NUM_2, Key::B), + (sdl2::keyboard::Keycode::Q, Key::L), + (sdl2::keyboard::Keycode::E, Key::R), + (sdl2::keyboard::Keycode::TAB, Key::Start), + (sdl2::keyboard::Keycode::SPACE, Key::Select), + ]) +} + impl State { pub fn new(ctx: &context::Context) -> Self { let screen = framebuffer::Framebuffer::screen(ctx); @@ -347,7 +367,6 @@ impl State { pub fn mouse_pressed<G>( &mut self, ctx: &context::Context, - _button: winit::event::MouseButton, game: &mut G ) where G: Game { log::info!("click"); @@ -363,16 +382,19 @@ impl State { pub fn mouse_released( &mut self, _ctx: &context::Context, - _button: winit::event::MouseButton, ) { } pub fn key_pressed( &mut self, _ctx: &context::Context, - key: winit::keyboard::KeyCode, + key: Keycode, ) { - if key == winit::keyboard::KeyCode::F12 { + #[cfg(target_arch = "wasm32")] + let rebind = key == winit::keyboard::KeyCode::F12; + #[cfg(not(target_arch = "wasm32"))] + let rebind = key == sdl2::keyboard::Keycode::F12; + if rebind { self.keybindings = default_keybindings(); self.rebinding = None; self.write_log("Reset keybindings!"); @@ -388,7 +410,7 @@ impl State { pub fn key_released( &mut self, _ctx: &context::Context, - key: winit::keyboard::KeyCode, + key: Keycode, ) { if let Some(k) = self.keybindings.get_by_left(&key) { self.keys.pressed[*k] = false; @@ -437,10 +459,7 @@ impl State { pub fn run_update<G>(&mut self, ctx: &context::Context, game: &mut G) where G: Game { let now = now(ctx); - #[cfg(target_arch = "wasm32")] let diff = now - self.last; - #[cfg(not(target_arch = "wasm32"))] - let diff = now.duration_since(self.last).as_secs_f64(); self.acc += diff; self.last = now; |
