new workspace

This commit is contained in:
galister
2025-06-18 01:14:04 +09:00
parent 95f2ae4296
commit f05d3a8251
252 changed files with 24618 additions and 184 deletions

View File

@@ -0,0 +1,12 @@
#[derive(rust_embed::Embed)]
#[folder = "src/gui/assets/"]
pub struct GuiAsset;
impl wgui::assets::AssetProvider for GuiAsset {
fn load_from_path(&mut self, path: &str) -> anyhow::Result<Vec<u8>> {
match GuiAsset::get(path) {
Some(data) => Ok(data.data.to_vec()),
None => anyhow::bail!("embedded file {} not found", path),
}
}
}

View File

@@ -0,0 +1,3 @@
mod asset;
pub mod panel;
mod timestep;

View File

@@ -0,0 +1,161 @@
use std::sync::Arc;
use glam::vec2;
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
use wgui::{
event::{Event as WguiEvent, MouseDownEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent},
layout::Layout,
renderer_vk::context::Context as WguiContext,
};
use crate::{
backend::{
input::{Haptics, InteractionHandler, PointerHit},
overlay::{FrameMeta, OverlayBackend, OverlayRenderer, ShouldRender},
},
graphics::{CommandBuffers, ExtentExt},
state::AppState,
};
use super::{asset::GuiAsset, timestep::Timestep};
pub struct GuiPanel {
pub layout: Layout,
context: WguiContext,
timestep: Timestep,
pub width: u32,
pub height: u32,
}
impl GuiPanel {
pub fn new_from_template(
app: &AppState,
width: u32,
height: u32,
path: &str,
) -> anyhow::Result<Self> {
let mut me = Self::new_blank(app, width, height)?;
let parent = me.layout.root_widget;
let _res = wgui::parser::parse_from_assets(&mut me.layout, parent, path)?;
Ok(me)
}
pub fn new_blank(app: &AppState, width: u32, height: u32) -> anyhow::Result<Self> {
let layout = Layout::new(Box::new(GuiAsset {}))?;
let context = WguiContext::new(app.gfx.clone(), app.gfx.surface_format, 1.0)?;
let mut timestep = Timestep::new();
timestep.set_tps(60.0);
Ok(Self {
layout,
context,
timestep,
width,
height,
})
}
}
impl OverlayBackend for GuiPanel {
fn set_renderer(&mut self, _: Box<dyn OverlayRenderer>) {
log::debug!("Attempted to replace renderer on GuiPanel!");
}
fn set_interaction(&mut self, _: Box<dyn InteractionHandler>) {
log::debug!("Attempted to replace interaction layer on GuiPanel!");
}
}
impl InteractionHandler for GuiPanel {
fn on_scroll(&mut self, _app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) {
self.layout
.push_event(&WguiEvent::MouseWheel(MouseWheelEvent {
shift: vec2(delta_x, delta_y),
pos: hit.uv,
}))
.unwrap()
}
fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
self.layout
.push_event(&WguiEvent::MouseMotion(MouseMotionEvent { pos: hit.uv }))
.unwrap();
None
}
fn on_left(&mut self, _app: &mut AppState, _pointer: usize) {
//TODO: is this needed?
}
fn on_pointer(&mut self, _app: &mut AppState, hit: &PointerHit, pressed: bool) {
if pressed {
self.layout
.push_event(&WguiEvent::MouseDown(MouseDownEvent { pos: hit.uv }))
.unwrap();
} else {
self.layout
.push_event(&WguiEvent::MouseUp(MouseUpEvent { pos: hit.uv }))
.unwrap();
}
}
}
impl OverlayRenderer for GuiPanel {
fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
Ok(())
}
fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
Ok(())
}
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
self.timestep.reset();
Ok(())
}
fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result<ShouldRender> {
while self.timestep.on_tick() {
self.layout.tick()?;
}
Ok(if self.layout.check_toggle_needs_redraw() {
ShouldRender::Should
} else {
ShouldRender::Can
})
}
fn render(
&mut self,
app: &mut AppState,
tgt: Arc<ImageView>,
buf: &mut CommandBuffers,
_alpha: f32,
) -> anyhow::Result<bool> {
self.context.update_viewport(tgt.extent_u32arr(), 1.0)?;
self.layout.update(tgt.extent_vec2(), self.timestep.alpha);
let mut cmd_buf = app
.gfx
.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)
.unwrap();
cmd_buf.begin_rendering(tgt)?;
let primitives = wgui::drawing::draw(&self.layout)?;
self.context.draw(&app.gfx, &mut cmd_buf, &primitives)?;
cmd_buf.end_rendering()?;
buf.push(cmd_buf.build()?);
Ok(true)
}
fn frame_meta(&mut self) -> Option<FrameMeta> {
Some(FrameMeta {
extent: [self.width, self.height, 1],
..Default::default()
})
}
}

View File

@@ -0,0 +1,70 @@
use std::{sync::LazyLock, time::Instant};
static TIME_START: LazyLock<Instant> = LazyLock::new(Instant::now);
pub fn get_micros() -> u64 {
TIME_START.elapsed().as_micros() as u64
}
#[derive(Default)]
pub struct Timestep {
current_time_us: u64,
accumulator: f32,
time_micros: u64,
ticks: u32,
speed: f32,
pub alpha: f32,
delta: f32,
loopnum: u8,
}
impl Timestep {
pub fn new() -> Timestep {
let mut timestep = Timestep {
speed: 1.0,
..Default::default()
};
timestep.reset();
timestep
}
fn calculate_alpha(&mut self) {
self.alpha = (self.accumulator / self.delta).clamp(0.0, 1.0);
}
pub fn set_tps(&mut self, tps: f32) {
self.delta = 1000.0 / tps;
}
pub fn reset(&mut self) {
self.current_time_us = get_micros();
self.accumulator = 0.0;
}
pub fn on_tick(&mut self) -> bool {
let newtime = get_micros();
let frametime = newtime - self.current_time_us;
self.time_micros += frametime;
self.current_time_us = newtime;
self.accumulator += frametime as f32 * self.speed / 1000.0;
self.calculate_alpha();
if self.accumulator >= self.delta {
self.accumulator -= self.delta;
self.loopnum += 1;
self.ticks += 1;
if self.loopnum > 5 {
// cannot keep up!
self.loopnum = 0;
self.accumulator = 0.0;
return false;
}
true
} else {
self.loopnum = 0;
false
}
}
}