diff --git a/src/window/game.rs b/src/window/game.rs index d20b371..ba2a188 100644 --- a/src/window/game.rs +++ b/src/window/game.rs @@ -3,7 +3,7 @@ use crate::{ emulator::{EmulatorClient, EmulatorCommand, SimId}, }; use egui::{ - menu, Button, CentralPanel, Color32, Context, Frame, Response, TopBottomPanel, Ui, + menu, Button, CentralPanel, Color32, Context, Frame, Response, Sense, TopBottomPanel, Ui, Vec2, ViewportBuilder, ViewportCommand, ViewportId, WidgetText, }; use winit::event_loop::EventLoopProxy; @@ -13,11 +13,27 @@ use super::{ AppWindow, }; +const COLOR_PRESETS: [[Color32; 2]; 3] = [ + [ + Color32::from_rgb(0xff, 0x00, 0x00), + Color32::from_rgb(0x00, 0xc6, 0xf0), + ], + [ + Color32::from_rgb(0x00, 0xb4, 0x00), + Color32::from_rgb(0xc8, 0x00, 0xff), + ], + [ + Color32::from_rgb(0xb4, 0x9b, 0x00), + Color32::from_rgb(0x00, 0x00, 0xff), + ], +]; + pub struct GameWindow { client: EmulatorClient, proxy: EventLoopProxy, sim_id: SimId, display_mode: DisplayMode, + colors: [Color32; 2], screen: Option, } @@ -28,6 +44,7 @@ impl GameWindow { proxy, sim_id, display_mode: DisplayMode::Anaglyph, + colors: COLOR_PRESETS[0], screen: None, } } @@ -104,6 +121,13 @@ impl GameWindow { ui.close_menu(); } }); + ui.menu_button("Colors", |ui| { + for preset in COLOR_PRESETS { + if ui.color_pair_button(preset[0], preset[1]).clicked() { + self.colors = preset; + } + } + }); }); ui.menu_button("Audio", |ui| { let p1_enabled = self.client.is_audio_enabled(SimId::Player1); @@ -175,7 +199,7 @@ impl AppWindow for GameWindow { let frame = Frame::central_panel(&ctx.style()).fill(Color32::BLACK); CentralPanel::default().frame(frame).show(ctx, |ui| { if let Some(screen) = self.screen.as_mut() { - screen.display_mode = self.display_mode; + screen.update(self.display_mode, self.colors); ui.add(screen); } }); @@ -209,6 +233,8 @@ trait UiExt { } response } + + fn color_pair_button(&mut self, left: Color32, right: Color32) -> Response; } impl UiExt for Ui { @@ -219,4 +245,18 @@ impl UiExt for Ui { let mut selected = selected; self.checkbox(&mut selected, text) } + + fn color_pair_button(&mut self, left: Color32, right: Color32) -> Response { + let button_size = Vec2::new(60.0, 20.0); + let (rect, response) = self.allocate_at_least(button_size, Sense::click()); + let center_x = rect.center().x; + let left_rect = rect.with_max_x(center_x); + self.painter().rect_filled(left_rect, 0.0, left); + let right_rect = rect.with_min_x(center_x); + self.painter().rect_filled(right_rect, 0.0, right); + + let style = self.style().interact(&response); + self.painter().rect_stroke(rect, 0.0, style.fg_stroke); + response + } } diff --git a/src/window/game_screen.rs b/src/window/game_screen.rs index b18caa7..3e2e36c 100644 --- a/src/window/game_screen.rs +++ b/src/window/game_screen.rs @@ -1,13 +1,15 @@ use std::{collections::HashMap, sync::Arc}; -use egui::Widget; -use wgpu::{util::DeviceExt as _, BindGroup, BindGroupLayout, RenderPipeline}; +use egui::{Color32, Rgba, Widget}; +use wgpu::{util::DeviceExt as _, BindGroup, BindGroupLayout, Buffer, RenderPipeline}; use crate::graphics::TextureSink; pub struct GameScreen { bind_group: Arc, - pub display_mode: DisplayMode, + color_buffer: Arc, + display_mode: DisplayMode, + colors: Colors, } impl GameScreen { @@ -117,12 +119,12 @@ impl GameScreen { let (sink, texture_view) = TextureSink::new(device, queue.clone()); let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default()); - let colors = Colors { - left: [1.0, 0.0, 0.0, 1.0], - right: [0.0, 0.7734375, 0.9375, 1.0], - }; + let colors = Colors::new( + Color32::from_rgb(0xff, 0x00, 0x00), + Color32::from_rgb(0x00, 0xc6, 0xf0), + ); - let color_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + let color_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("colors"), contents: bytemuck::bytes_of(&colors), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, @@ -145,7 +147,7 @@ impl GameScreen { }, wgpu::BindGroupEntry { binding: 2, - resource: color_buf.as_entire_binding(), + resource: color_buffer.as_entire_binding(), }, ], }); @@ -153,11 +155,18 @@ impl GameScreen { ( Self { bind_group: Arc::new(bind_group), + color_buffer: Arc::new(color_buffer), display_mode: DisplayMode::Anaglyph, + colors, }, sink, ) } + + pub fn update(&mut self, display_mode: DisplayMode, colors: [Color32; 2]) { + self.display_mode = display_mode; + self.colors = Colors::new(colors[0], colors[1]); + } } impl Widget for &mut GameScreen { @@ -167,7 +176,9 @@ impl Widget for &mut GameScreen { response.rect, GameScreenCallback { bind_group: self.bind_group.clone(), + color_buffer: self.color_buffer.clone(), display_mode: self.display_mode, + colors: self.colors, }, ); ui.painter().add(callback); @@ -177,10 +188,23 @@ impl Widget for &mut GameScreen { struct GameScreenCallback { bind_group: Arc, + color_buffer: Arc, display_mode: DisplayMode, + colors: Colors, } impl egui_wgpu::CallbackTrait for GameScreenCallback { + fn prepare( + &self, + _device: &wgpu::Device, + queue: &wgpu::Queue, + _screen_descriptor: &egui_wgpu::ScreenDescriptor, + _egui_encoder: &mut wgpu::CommandEncoder, + _callback_resources: &mut egui_wgpu::CallbackResources, + ) -> Vec { + queue.write_buffer(&self.color_buffer, 0, bytemuck::bytes_of(&self.colors)); + vec![] + } fn paint( &self, info: egui::PaintCallbackInfo, @@ -217,8 +241,16 @@ struct SharedGameScreenResources { #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] struct Colors { - left: [f32; 4], - right: [f32; 4], + left: Rgba, + right: Rgba, +} +impl Colors { + fn new(left: Color32, right: Color32) -> Self { + Self { + left: left.into(), + right: right.into(), + } + } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]