diff options
author | pommicket <pommicket@gmail.com> | 2023-01-24 20:15:00 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2023-01-24 20:15:31 -0500 |
commit | cbdb80698fd7128e7bcc63c8513e53731dda0963 (patch) | |
tree | fff92d1eb01a170d1d9ad4ff48be42bedb87cf96 /src | |
parent | aa6d1c80d68e85220d6fd94af421dcee787a5c7b (diff) |
pause menu
Diffstat (limited to 'src')
-rw-r--r-- | src/fshader_post.glsl | 12 | ||||
-rw-r--r-- | src/main.rs | 193 | ||||
-rw-r--r-- | src/menu.png | bin | 17072 -> 21714 bytes | |||
-rw-r--r-- | src/menu.xcf | bin | 92668 -> 106590 bytes | |||
-rw-r--r-- | src/sdl.rs | 11 | ||||
-rw-r--r-- | src/win.rs | 64 |
6 files changed, 216 insertions, 64 deletions
diff --git a/src/fshader_post.glsl b/src/fshader_post.glsl index c44371a..41b442f 100644 --- a/src/fshader_post.glsl +++ b/src/fshader_post.glsl @@ -3,14 +3,22 @@ uniform sampler2D u_menu_texture; uniform float u_paused; uniform float u_aspect_ratio; uniform float u_menu_scale; +uniform vec2 u_highlight_button; IN vec2 uv; void main() { - vec4 color = texture(u_main_texture, uv) * (1.0 - 0.5 * u_paused); + // amount to darken screen by when paused + float pause_darkening = 0.75; + vec4 color = texture(u_main_texture, uv) * (1.0 - pause_darkening * u_paused); vec2 menu_uv = (uv * 2.0 - 1.0) * vec2(1.0, -1.0); menu_uv *= 1.0 / u_menu_scale; menu_uv = menu_uv * vec2(u_aspect_ratio, 1.0); menu_uv = 0.5 * menu_uv + 0.5; - color = mix(color, vec4(1.0), u_paused * texture(u_menu_texture, menu_uv).x); + float menu_pixel = u_paused * texture(u_menu_texture, menu_uv).x; + vec4 menu_color = vec4(1.0); + if (uv.y >= u_highlight_button.x && uv.y <= u_highlight_button.y) { + menu_color = vec4(1.0, 0.5, 0.0, 1.0); // highlight button + } + color = mix(color, menu_color, menu_pixel); gl_FragColor = clamp(color, 0.0, 1.0); } diff --git a/src/main.rs b/src/main.rs index eaeaf9d..8f8859b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ /* @TODO: -- pause screen - reload settings.txt when changed - strip ' ' and '\n' from *inside* string - flash error on bad string (see @TODO(error handling)) @@ -42,7 +41,17 @@ type Mat3 = Matrix3<f32>; type Mat4 = Matrix4<f32>; type Rot3 = Rotation3<f32>; -const MENU_SCALE: f32 = 0.5; +const MENU_SCALE: f32 = 0.6; +#[derive(Clone, Copy)] +enum MenuButton { + Resume, + Quit +} +/// array of buttons in menu.png. (y, height, button) +const MENU_BUTTONS: &[(f32, f32, MenuButton)] = &[ + (375.0, 135.0, MenuButton::Resume), + (605.0, 165.0, MenuButton::Quit), +]; #[repr(i32)] #[derive(Clone, Copy)] @@ -233,6 +242,7 @@ struct State { show_debug_info: bool, fullscreen: bool, esc_menu: bool, + quit: bool, frame_time: Instant, programs: Programs, config: sdf::SceneConfig, @@ -360,6 +370,7 @@ impl State { main_array, test_array, esc_menu: false, + quit: false, post_array, scene_list, settings, @@ -436,7 +447,7 @@ impl State { self.flash_icon = icon; } - fn render_resolution(&self) -> (i32, i32) { + fn get_render_resolution(&self) -> (i32, i32) { let scale = self.settings.get_f32("scale").unwrap_or(1.0); if scale <= 0.0 || scale > 100.0 { win::display_error_message(&format!("bad scale: {scale}")); @@ -450,6 +461,79 @@ impl State { (w, h) } + /// render the SDF to the main framebuffer + fn render_main(&mut self) { + let render_resolution = self.get_render_resolution(); + let window = &mut self.window; + let view = &self.view; + window.viewport(0, 0, render_resolution.0, render_resolution.1); + + window.clear_screen(win::ColorF32::BLACK); + window.use_program(&self.programs.main); + window.bind_framebuffer(Some(&self.main_framebuffer)); + window.uniform1f( + "u_aspect_ratio", + render_resolution.0 as f32 / render_resolution.1 as f32, + ); + { + let (w, h) = window.size(); + window.uniform2f("u_screen_size", w as f32, h as f32); + } + window.uniform1f("u_time", view.time as f32); + window.uniform1f( + "u_fov", + self.settings.get_f32("fov").unwrap_or(45.0).to_radians(), + ); + window.uniform1f( + "u_focal_length", + self.settings.get_f32("focal-length").unwrap_or(1.0), + ); + window.uniform1f("u_level_set", view.level_set); + window.uniform1i("u_hsv", self.settings.get_i32("hsv").unwrap_or(0)); + let antialiasing = self.settings.get_i32("antialiasing").unwrap_or(1); + window.uniform2i("u_antialiasing", antialiasing, antialiasing); + window.uniform1i( + "u_iterations", + self.settings.get_i32("max-iterations").unwrap_or(30), + ); + window.uniform1f( + "u_distance_threshold", + self.settings.get_f32("distance-threshold").unwrap_or(0.02), + ); + window.uniform3x3f("u_rotation", view.rotation().as_slice()); + window.uniform3f_slice("u_translation", view.pos.as_slice()); + window.uniform4f_color("u_flash", self.flash); + window.uniform1i("u_flash_icon", self.flash_icon as i32); + + self.main_array.draw(); + } + + /// draw the main framebuffer to the screen, apply postprocessing + fn render_post(&mut self) { + let highlight_button = self.menu_button_at_pos(self.window.get_mouse_pos()); + let render_resolution = self.get_render_resolution(); + let window = &mut self.window; + window.bind_framebuffer(None); + window.viewport_full_screen(); + window.use_program(&self.programs.post); + window.active_texture(0, &self.main_framebuffer_texture); + window.active_texture(1, &self.menu_texture); + window.uniform1f("u_paused", if self.esc_menu { 1.0 } else { 0.0 }); + window.uniform_texture("u_main_texture", 0); + window.uniform_texture("u_menu_texture", 1); + window.uniform1f( + "u_aspect_ratio", + render_resolution.0 as f32 / render_resolution.1 as f32, + ); + window.uniform1f("u_menu_scale", MENU_SCALE); + if let Some((_, y1, y2)) = highlight_button { + window.uniform2f("u_highlight_button", y1, y2); + } else { + window.uniform2f("u_highlight_button", 0.0, 0.0); + } + self.post_array.draw(); + } + /// save a screenshot fn take_screenshot(&mut self) -> Result<(), String> { let texture = &self.main_framebuffer_texture; @@ -489,6 +573,32 @@ impl State { self.flash(Icon::Screenshot); Ok(()) } + + /// returns Some(button, v1, v2) which is a bit weird but oh well + fn menu_button_at_pos(&self, screen_pos: (i32, i32)) -> Option<(MenuButton, f32, f32)> { + let window_height = self.window.size().1 as f32; + let texture_height = self.menu_texture.height() as f32; + let y = screen_pos.1 as f32 / window_height as f32; + for &(y1, h, button) in MENU_BUTTONS { + let y1 = y1 / texture_height; + let h = h / texture_height; + let y2 = y1 + h; + let y1 = (y1 - 0.5) * MENU_SCALE + 0.5; + let y2 = (y2 - 0.5) * MENU_SCALE + 0.5; + if y >= y1 && y <= y2 { + return Some((button, 1.0 - y2, 1.0 - y1)); + } + } + None + } + + fn press_menu_button(&mut self, button: MenuButton) { + use MenuButton::*; + match button { + Resume => self.esc_menu = false, + Quit => self.quit = true, + } + } // returns false if we should quit fn frame(&mut self) -> bool { @@ -509,6 +619,7 @@ impl State { while let Some(event) = self.window.next_event() { use win::Event::*; use win::Key::*; + use win::MouseButton; match event { Quit => return false, KeyDown { @@ -595,6 +706,13 @@ impl State { .pitch_by(-yrel as f32 * mouse_sensitivity * frame_dt); } } + MouseButtonDown { button: MouseButton::Left, x, y, .. } => { + if self.esc_menu { + if let Some((menu_button, _, _)) = self.menu_button_at_pos((x, y)) { + self.press_menu_button(menu_button); + } + } + } _ => {} } } @@ -666,7 +784,7 @@ impl State { self.view.level_set += dl * level_set_amount; } - let render_resolution = self.render_resolution(); + let render_resolution = self.get_render_resolution(); if render_resolution != self.main_framebuffer_size { // window resized. create new framebuffer let result = self.main_framebuffer_texture.set_data::<ColorU8>( @@ -687,77 +805,28 @@ impl State { self.main_framebuffer_size = render_resolution; } - let window = &mut self.window; - let view = &self.view; - window.viewport(0, 0, render_resolution.0, render_resolution.1); - - window.clear_screen(win::ColorF32::BLACK); - window.use_program(&self.programs.main); - window.bind_framebuffer(Some(&self.main_framebuffer)); - window.uniform1f( - "u_aspect_ratio", - render_resolution.0 as f32 / render_resolution.1 as f32, - ); - { - let (w, h) = window.size(); - window.uniform2f("u_screen_size", w as f32, h as f32); + // if the escape menu is open, stop rendering the SDF. + // the framebuffer contents will stay the same. + // this lowers GPU usage (and increases framerate). + if !self.esc_menu { + self.render_main(); } - window.uniform1f("u_time", view.time as f32); - window.uniform1f( - "u_fov", - self.settings.get_f32("fov").unwrap_or(45.0).to_radians(), - ); - window.uniform1f( - "u_focal_length", - self.settings.get_f32("focal-length").unwrap_or(1.0), - ); - window.uniform1f("u_level_set", view.level_set); - window.uniform1i("u_hsv", self.settings.get_i32("hsv").unwrap_or(0)); - let antialiasing = self.settings.get_i32("antialiasing").unwrap_or(1); - window.uniform2i("u_antialiasing", antialiasing, antialiasing); - window.uniform1i( - "u_iterations", - self.settings.get_i32("max-iterations").unwrap_or(30), - ); - window.uniform1f( - "u_distance_threshold", - self.settings.get_f32("distance-threshold").unwrap_or(0.02), - ); - window.uniform3x3f("u_rotation", view.rotation().as_slice()); - window.uniform3f_slice("u_translation", view.pos.as_slice()); - window.uniform4f_color("u_flash", self.flash); - window.uniform1i("u_flash_icon", self.flash_icon as i32); - + self.flash.a = f32::max(self.flash.a - frame_dt * (2.0 - 1.0 * self.flash.a), 0.0); if self.flash.a <= 0.0 { // icon is no longer visible self.flash_icon = Icon::None; } - self.main_array.draw(); - - window.bind_framebuffer(None); - window.viewport_full_screen(); - window.use_program(&self.programs.post); - window.active_texture(0, &self.main_framebuffer_texture); - window.active_texture(1, &self.menu_texture); - window.uniform1f("u_paused", if self.esc_menu { 1.0 } else { 0.0 }); - window.uniform_texture("u_main_texture", 0); - window.uniform_texture("u_menu_texture", 1); - window.uniform1f( - "u_aspect_ratio", - render_resolution.0 as f32 / render_resolution.1 as f32, - ); - window.uniform1f("u_menu_scale", MENU_SCALE); - self.post_array.draw(); + self.render_post(); - window.swap(); + self.window.swap(); if self.show_debug_info { println!("frame time = {:?}ms", frame_dt * 1000.0); } - true + !self.quit } } diff --git a/src/menu.png b/src/menu.png Binary files differindex 6a5a481..4bebd35 100644 --- a/src/menu.png +++ b/src/menu.png diff --git a/src/menu.xcf b/src/menu.xcf Binary files differindex b231ee8..3f6d306 100644 --- a/src/menu.xcf +++ b/src/menu.xcf @@ -167,6 +167,9 @@ pub const SDL_BUTTON_RMASK: u32 = sdl_button_mask(SDL_BUTTON_RIGHT); pub const SDL_BUTTON_X1MASK: u32 = sdl_button_mask(SDL_BUTTON_X1); pub const SDL_BUTTON_X2MASK: u32 = sdl_button_mask(SDL_BUTTON_X2); +pub const SDL_RELEASED: u8 = 0; +pub const SDL_PRESSED: u8 = 1; + pub const SDL_GL_RED_SIZE: SDL_GLattr = 0; pub const SDL_GL_GREEN_SIZE: SDL_GLattr = 1; pub const SDL_GL_BLUE_SIZE: SDL_GLattr = 2; @@ -821,6 +824,7 @@ extern "C" { fn SDL_GL_SwapWindow(window: *mut SDL_Window); fn SDL_GL_GetProcAddress(proc: *const c_char) -> *mut c_void; fn SDL_PollEvent(event: *mut SDL_Event) -> c_int; + fn SDL_GetMouseState(x: *mut c_int, y: *mut c_int) -> u32; fn SDL_GetKeyboardState(numkeys: *mut c_int) -> *const u8; fn SDL_GetKeyFromScancode(scancode: SDL_Scancode) -> SDL_Keycode; // NOTE: do NOT add SDL_GetScancodeFromKey !!! see get_scancodes_from_key for explanation. @@ -1613,3 +1617,10 @@ pub unsafe fn get_scancodes_from_key(keycode: SDL_Keycode) -> impl Iterator<Item // should perform well. (0..scancode::NUM_SCANCODES).filter(move |&scn| SDL_GetKeyFromScancode(scn) == keycode) } + +pub unsafe fn get_mouse_state() -> (i32, i32, u32) { + let mut x = 0; + let mut y = 0; + let state = SDL_GetMouseState((&mut x) as *mut c_int, (&mut y) as *mut c_int); + (x as _, y as _, state) +} @@ -434,6 +434,28 @@ impl KeyModifier { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MouseButton { + Left, + Middle, + Right, + X1, + X2, +} + +impl MouseButton { + fn from_sdl(x: u8) -> Option<Self> { + Some(match x { + sdl::SDL_BUTTON_LEFT => MouseButton::Left, + sdl::SDL_BUTTON_MIDDLE => MouseButton::Middle, + sdl::SDL_BUTTON_RIGHT => MouseButton::Right, + sdl::SDL_BUTTON_X1 => MouseButton::X1, + sdl::SDL_BUTTON_X2 => MouseButton::X2, + _ => return None, + }) + } +} + #[derive(Debug, Clone)] pub enum Event { Quit, @@ -451,6 +473,20 @@ pub enum Event { xrel: i32, yrel: i32, }, + MouseButtonDown { + button: MouseButton, + x: i32, + y: i32, + /// 1 for single-click, 2 for double-click, etc. + clicks: u8 + }, + MouseButtonUp { + button: MouseButton, + x: i32, + y: i32, + /// 1 for single-click, 2 for double-click, etc. + clicks: u8 + } } pub fn display_error_message(message: &str) { @@ -1493,6 +1529,29 @@ impl Window { yrel: motion.yrel, }); } + sdl::SDL_MOUSEBUTTONDOWN | sdl::SDL_MOUSEBUTTONUP => { + let b = unsafe { sdl.button }; + if let Some(button) = MouseButton::from_sdl(b.button) { + let x = b.x; + let y = b.y; + let clicks = b.clicks; + if b.state == sdl::SDL_PRESSED { + return Some(Event::MouseButtonDown { + x, + y, + clicks, + button, + }); + } else if b.state == sdl::SDL_RELEASED { + return Some(Event::MouseButtonUp { + x, + y, + clicks, + button, + }); + } + } + } _ => {} } } @@ -1679,6 +1738,11 @@ impl Window { self.uniform1i(name, slot as i32); } + pub fn get_mouse_pos(&self) -> (i32, i32) { + let state = unsafe { sdl::get_mouse_state() }; + (state.0, state.1) + } + pub fn is_key_down(&self, key: Key) -> bool { let kbd_state = unsafe { sdl::get_keyboard_state() }; let mut scancodes = unsafe { sdl::get_scancodes_from_key(key.to_sdl()) }; |