summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-01-24 20:15:00 -0500
committerpommicket <pommicket@gmail.com>2023-01-24 20:15:31 -0500
commitcbdb80698fd7128e7bcc63c8513e53731dda0963 (patch)
treefff92d1eb01a170d1d9ad4ff48be42bedb87cf96 /src
parentaa6d1c80d68e85220d6fd94af421dcee787a5c7b (diff)
pause menu
Diffstat (limited to 'src')
-rw-r--r--src/fshader_post.glsl12
-rw-r--r--src/main.rs193
-rw-r--r--src/menu.pngbin17072 -> 21714 bytes
-rw-r--r--src/menu.xcfbin92668 -> 106590 bytes
-rw-r--r--src/sdl.rs11
-rw-r--r--src/win.rs64
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
index 6a5a481..4bebd35 100644
--- a/src/menu.png
+++ b/src/menu.png
Binary files differ
diff --git a/src/menu.xcf b/src/menu.xcf
index b231ee8..3f6d306 100644
--- a/src/menu.xcf
+++ b/src/menu.xcf
Binary files differ
diff --git a/src/sdl.rs b/src/sdl.rs
index 0b06ed4..2695445 100644
--- a/src/sdl.rs
+++ b/src/sdl.rs
@@ -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)
+}
diff --git a/src/win.rs b/src/win.rs
index 831a182..08b0b05 100644
--- a/src/win.rs
+++ b/src/win.rs
@@ -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()) };