9eead719b0
Full project snapshot migrated to new Gitea remote without history: engine, editor, physics, script, examples, tests, docs, and assets. Relicensed from GPLv3 to MIT and updated repo URLs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
134 lines
4.9 KiB
Rust
134 lines
4.9 KiB
Rust
//! The [`WindowApp`] trait and per-callback context.
|
|
|
|
use winit::event::WindowEvent;
|
|
use winit::window::Window;
|
|
|
|
use crate::input::InputState;
|
|
use crate::math::Color;
|
|
use crate::render::{Gpu, RenderContext};
|
|
|
|
/// An application driven by the engine's event loop.
|
|
///
|
|
/// Implement this and pass the value to [`run`](super::run). All methods have
|
|
/// empty defaults so minimal apps only override what they need. Per frame the
|
|
/// engine calls [`event`](Self::event) for each pending window event, then
|
|
/// [`update`](Self::update), then clears and presents the surface.
|
|
///
|
|
/// This trait is the **window-event handler** — the per-frame plumbing between
|
|
/// `winit` and a renderer. It is distinct from the engine's
|
|
/// [`App`](crate::app::App) **container**, which owns the scene, assets, and
|
|
/// scheduled systems. The editor's main loop typically implements this trait
|
|
/// on a struct that *also* owns an `oxide_engine::app::App`.
|
|
pub trait WindowApp {
|
|
/// Called once, after the window and GPU context exist but before the
|
|
/// first frame.
|
|
fn init(&mut self, ctx: &mut AppCtx<'_>) {
|
|
let _ = ctx;
|
|
}
|
|
|
|
/// Called for every raw window event (keyboard, mouse, resize, focus, …).
|
|
///
|
|
/// Events the engine itself reacts to (close request, resize) are still
|
|
/// forwarded here afterwards, so apps observe everything.
|
|
fn event(&mut self, ctx: &mut AppCtx<'_>, event: &WindowEvent) {
|
|
let _ = (ctx, event);
|
|
}
|
|
|
|
/// Called once per frame, before the frame is rendered.
|
|
fn update(&mut self, ctx: &mut AppCtx<'_>) {
|
|
let _ = ctx;
|
|
}
|
|
|
|
/// Called each frame after the surface has been cleared and before it is
|
|
/// presented, so the app can record its own draw commands into the frame.
|
|
///
|
|
/// This is the hook editor/overlay UI (egui) and, in later stages, the
|
|
/// scene renderer draw through. The surface is cleared with
|
|
/// [`LoadOp::Clear`](wgpu::LoadOp::Clear) *before* this runs; record passes
|
|
/// here with [`LoadOp::Load`](wgpu::LoadOp::Load) to draw on top of the
|
|
/// clear color rather than wiping it.
|
|
fn render(&mut self, ctx: &RenderCtx<'_>) {
|
|
let _ = ctx;
|
|
}
|
|
}
|
|
|
|
/// Per-frame rendering context passed to [`WindowApp::render`].
|
|
///
|
|
/// Unlike [`AppCtx`], this borrows the GPU and surface immutably: by the time
|
|
/// the draw hook runs the frame's surface texture is already acquired, so the
|
|
/// app receives the handles it needs to record additional passes into
|
|
/// [`view`](Self::view) without re-entering the render context.
|
|
pub struct RenderCtx<'a> {
|
|
/// The GPU device/queue to record and submit commands with.
|
|
pub gpu: &'a Gpu,
|
|
/// The current frame's surface texture view (the render target).
|
|
pub view: &'a wgpu::TextureView,
|
|
/// The window being rendered, e.g. for input/UI integration that needs it.
|
|
pub window: &'a Window,
|
|
/// The surface's texture format, needed to build matching pipelines.
|
|
pub surface_format: wgpu::TextureFormat,
|
|
/// Surface size in physical pixels (`width`, `height`).
|
|
pub size: (u32, u32),
|
|
}
|
|
|
|
/// Engine state handed to every [`WindowApp`] callback.
|
|
pub struct AppCtx<'a> {
|
|
pub(crate) render: &'a mut RenderContext,
|
|
pub(crate) window: &'a Window,
|
|
pub(crate) exit: &'a mut bool,
|
|
pub(crate) input: &'a InputState,
|
|
/// Seconds elapsed since the previous frame (`0.0` during
|
|
/// [`App::init`] and the first frame).
|
|
pub dt: f32,
|
|
}
|
|
|
|
impl AppCtx<'_> {
|
|
/// The render context driving the window surface.
|
|
pub fn render(&mut self) -> &mut RenderContext {
|
|
self.render
|
|
}
|
|
|
|
/// Sets the color the surface is cleared to, effective next frame.
|
|
pub fn set_clear_color(&mut self, color: Color) {
|
|
self.render.set_clear_color(color);
|
|
}
|
|
|
|
/// The current clear color.
|
|
pub fn clear_color(&self) -> Color {
|
|
self.render.clear_color()
|
|
}
|
|
|
|
/// Current surface size in physical pixels.
|
|
pub fn size(&self) -> (u32, u32) {
|
|
self.render.size()
|
|
}
|
|
|
|
/// Sets the window title.
|
|
pub fn set_title(&self, title: &str) {
|
|
self.window.set_title(title);
|
|
}
|
|
|
|
/// The window being driven, e.g. to construct UI/input integration that
|
|
/// needs a window handle.
|
|
pub fn window(&self) -> &Window {
|
|
self.window
|
|
}
|
|
|
|
/// Asks the event loop to exit after the current callback returns.
|
|
pub fn request_exit(&mut self) {
|
|
*self.exit = true;
|
|
}
|
|
|
|
/// The per-frame input snapshot.
|
|
///
|
|
/// Reflects every keyboard / mouse / scroll event delivered since the
|
|
/// previous frame's `update` returned. In [`WindowApp::event`] callbacks
|
|
/// it includes the event currently being delivered (the runner pumps it
|
|
/// before invoking the callback). In [`WindowApp::update`] it is the
|
|
/// accumulated state for the new frame; the runner clears edges (pressed/
|
|
/// released, mouse delta, scroll) automatically after `update` returns.
|
|
pub fn input(&self) -> &InputState {
|
|
self.input
|
|
}
|
|
}
|