Import Oxide engine (Stages 0–10) under MIT license

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>
This commit is contained in:
Homer Simpson
2026-07-05 20:41:02 +02:00
parent 2afb56b329
commit f56a1eea3b
128 changed files with 40493 additions and 2 deletions
+140
View File
@@ -0,0 +1,140 @@
//! egui ⇄ engine glue for the editor.
//!
//! The engine core stays UI-agnostic; all egui wiring lives here in the editor.
//! [`EguiLayer`] owns the [`egui_winit`] input state and the [`egui_wgpu`]
//! renderer, translates window events, and paints a built UI into the frame's
//! surface view (recorded with `LoadOp::Load`, so it composites on top of the
//! engine's clear).
use egui_wgpu::{Renderer, RendererOptions, ScreenDescriptor};
use egui_winit::State;
use oxide_engine::wgpu;
use oxide_engine::winit::event::WindowEvent;
use oxide_engine::winit::window::Window;
/// Holds the egui input state and GPU renderer for one window.
pub struct EguiLayer {
state: State,
renderer: Renderer,
}
impl EguiLayer {
/// Creates the layer for `window`, building a renderer that targets the
/// given surface format.
pub fn new(
window: &Window,
device: &wgpu::Device,
surface_format: wgpu::TextureFormat,
) -> Self {
let context = egui::Context::default();
let state = State::new(
context,
egui::ViewportId::ROOT,
window,
Some(window.scale_factor() as f32),
None,
None,
);
// Defaults: no MSAA, no depth/stencil, dithering on — matches the
// editor's flat clear-color surface.
let renderer = Renderer::new(device, surface_format, RendererOptions::default());
Self { state, renderer }
}
/// Feeds a window event to egui. Returns `true` if egui consumed it (e.g.
/// a click landed on a panel), so the caller can suppress its own handling.
pub fn on_window_event(&mut self, window: &Window, event: &WindowEvent) -> bool {
self.state.on_window_event(window, event).consumed
}
/// Whether the pointer is currently over a **floating** egui area — a
/// `Window` (Preferences, Layer Names, Groups, …) or other non-background
/// layer — rather than empty space or the background dock.
///
/// The viewport is painted under a transparent dock area (background
/// order), so a geometric "cursor inside the viewport rect" test can't tell
/// that a floating panel is sitting on top of it. The host uses this to
/// suppress viewport orbit/pan/zoom (and stray WASD while typing in a panel
/// that overlaps the viewport).
pub fn pointer_over_floating(&self) -> bool {
let ctx = self.state.egui_ctx();
let Some(pos) = ctx.pointer_latest_pos() else {
return false;
};
ctx.layer_id_at(pos)
.map(|layer| layer.order > egui::Order::Background)
.unwrap_or(false)
}
/// Builds the UI via `build_ui` and paints it into `view`.
///
/// `build_ui` receives the root [`egui::Ui`]; panels are shown inside it
/// (egui 0.34's `show_inside` model). It may be called more than once per
/// frame if egui needs an extra layout pass, so it must be idempotent.
#[allow(clippy::too_many_arguments)]
pub fn paint(
&mut self,
window: &Window,
device: &wgpu::Device,
queue: &wgpu::Queue,
view: &wgpu::TextureView,
size: (u32, u32),
build_ui: impl FnMut(&mut egui::Ui),
) {
let raw_input = self.state.take_egui_input(window);
let context = self.state.egui_ctx().clone();
let output = context.run_ui(raw_input, build_ui);
self.state
.handle_platform_output(window, output.platform_output);
let primitives = context.tessellate(output.shapes, output.pixels_per_point);
let screen = ScreenDescriptor {
size_in_pixels: [size.0.max(1), size.1.max(1)],
pixels_per_point: output.pixels_per_point,
};
for (id, delta) in &output.textures_delta.set {
self.renderer.update_texture(device, queue, *id, delta);
}
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("oxide.editor.egui.encoder"),
});
// egui may emit its own command buffers (for paint callbacks); submit
// those ahead of our pass.
let user_buffers =
self.renderer
.update_buffers(device, queue, &mut encoder, &primitives, &screen);
{
let pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("oxide.editor.egui.pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
// Load: keep the engine's clear; draw the UI over it.
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
// egui-wgpu wants a 'static pass; the encoder outlives it here.
let mut pass = pass.forget_lifetime();
self.renderer.render(&mut pass, &primitives, &screen);
}
for id in &output.textures_delta.free {
self.renderer.free_texture(id);
}
queue.submit(
user_buffers
.into_iter()
.chain(std::iter::once(encoder.finish())),
);
}
}