use crate::{
screen::{Camera, RenderError, RenderInput, RenderOptions, Screen},
text::{TextSystem, TextSystemData},
};
use winit::{
event::{ElementState, KeyEvent, WindowEvent},
keyboard::{Key, NamedKey},
window::Window,
};
pub struct Document {
cell_width: u32,
cell_height: u32,
}
pub struct Project {
screen: Screen,
egui: egui::Context,
egui_winit: egui_winit::State,
internal_state: ProjectState,
text: TextSystem,
show_debug: bool,
}
pub struct ProjectState {
pub camera: Camera,
pub render_options: RenderOptions,
pub document: Document,
}
impl Project {
pub async fn new(window: Window) -> color_eyre::Result<Self> {
let camera = Camera::default();
let render_options = RenderOptions::default();
let egui = egui::Context::default();
let egui_winit =
egui_winit::State::new(egui.clone(), egui::ViewportId::ROOT, &window, None, None);
let screen = Screen::new(window).await?;
let document = Document {
cell_width: 100,
cell_height: 100,
};
Ok(Self {
egui,
egui_winit,
screen,
text: TextSystem::new()?,
show_debug: false,
internal_state: ProjectState {
camera,
render_options,
document,
},
})
}
pub async fn create_surface(&mut self) -> color_eyre::Result<()> {
self.screen.create_surface().await
}
pub fn screen(&self) -> &Screen {
&self.screen
}
pub fn screen_mut(&mut self) -> &mut Screen {
&mut self.screen
}
pub fn gui_egui(&mut self) -> (egui::FullOutput, Vec<egui::ClippedPrimitive>) {
egui_winit::update_viewport_info(
self.egui_winit
.egui_input_mut()
.viewports
.get_mut(&egui::ViewportId::ROOT)
.unwrap(),
&self.egui,
self.screen.window(),
);
let input = self.egui_winit.take_egui_input(self.screen.window());
let full_output = self.egui.run(input, |ctx| {
egui::Window::new("Debug Tools")
.open(&mut self.show_debug)
.show(ctx, |ui| {
ui.horizontal(|ui| {
let mut vsync = self.screen.is_vsync();
let text = if vsync { "yes" } else { "no" };
ui.label("VSync");
ui.toggle_value(&mut vsync, text);
self.screen.vsync(vsync);
});
ui.add(
egui::Slider::new(&mut self.internal_state.render_options.gamma, 0.0..=2.0)
.text("Gamma"),
);
});
});
self.egui_winit
.handle_platform_output(self.screen.window(), full_output.platform_output.clone());
let prims = self
.egui
.tessellate(full_output.shapes.clone(), full_output.pixels_per_point);
(full_output, prims)
}
pub fn input(&mut self, event: &WindowEvent) -> bool {
#[cfg(debug_assertions)]
if matches!(
event,
WindowEvent::KeyboardInput {
event: KeyEvent {
logical_key: Key::Named(NamedKey::F1),
state: ElementState::Pressed,
repeat: false,
..
},
..
}
) {
self.show_debug = !self.show_debug;
return true;
}
false
}
pub fn update(&mut self, _dt: std::time::Duration) {
puffin::profile_function!();
}
pub fn render(&mut self) -> Result<(), RenderError> {
puffin::profile_function!();
let (egui_output, paint_jobs) = self.gui_egui();
let text_data = TextSystemData::extract();
self.text.prepare(&text_data);
let camera_text_data_info: Vec<_> =
self.text.produce_camera(&text_data).into_iter().collect();
let camera_text_data = camera_text_data_info
.iter()
.map(|(buf, mapper)| mapper.produce(buf))
.collect();
let world_text_data_info: Vec<_> =
self.text.produce_world(&text_data).into_iter().collect();
let world_text_data = world_text_data_info
.iter()
.map(|(buf, mapper)| mapper.produce(buf))
.collect();
let input = RenderInput {
paint_jobs: &paint_jobs,
textures_delta: &egui_output.textures_delta,
font_system: &mut self.text.fonts,
swash_cache: &mut self.text.swash,
camera: &mut self.internal_state.camera,
render_options: &self.internal_state.render_options,
camera_text_data,
world_text_data,
document: &self.internal_state.document,
};
self.screen.render(input)
}
}