feat: full egui control panel + pointer routing

This commit is contained in:
xfy 2026-07-13 09:57:54 +08:00
parent 90e8c9b3a4
commit 7e0ac65d55
3 changed files with 56 additions and 7 deletions

View File

@ -24,6 +24,10 @@ impl Default for OrbitCamera {
}
}
/// Set by the UI system each frame; the orbit controller ignores input when true.
#[derive(Resource, Default)]
pub struct WantsPointer(pub bool);
impl OrbitCamera {
/// Compute the camera eye position and an orthonormal basis (forward/right/up)
/// in Bevy's right-handed Y-up coordinate system. The black hole sits at origin.
@ -50,11 +54,18 @@ impl OrbitCamera {
}
pub fn orbit_controller(
wants: Res<WantsPointer>,
mut camera: ResMut<OrbitCamera>,
mouse: Res<ButtonInput<MouseButton>>,
mut motion: MessageReader<MouseMotion>,
mut wheel: MessageReader<MouseWheel>,
) {
if wants.0 {
// egui is using the pointer: drain events so they don't pile up, and ignore.
motion.clear();
wheel.clear();
return;
}
if mouse.pressed(MouseButton::Left) {
for ev in motion.read() {
camera.yaw -= ev.delta.x * 0.005;

View File

@ -8,6 +8,7 @@ pub struct BlackHolePlugin;
impl Plugin for BlackHolePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<crate::camera::OrbitCamera>()
.init_resource::<crate::camera::WantsPointer>()
.init_resource::<crate::params::BlackHoleParams>()
.add_plugins(Material2dPlugin::<BlackHoleMaterial>::default())
.add_plugins(bevy_egui::EguiPlugin::default())

View File

@ -4,14 +4,51 @@ use bevy_egui::egui;
pub fn ui_system(
mut contexts: bevy_egui::EguiContexts,
mut params: ResMut<crate::params::BlackHoleParams>,
mut camera: ResMut<crate::camera::OrbitCamera>,
mut wants: ResMut<crate::camera::WantsPointer>,
) {
if let Ok(ctx) = contexts.ctx_mut() {
egui::Window::new("Controls").show(ctx, |ui| {
ui.add(
egui::Slider::new(&mut params.disk_brightness, 0.0..=3.0)
.text("Disk brightness"),
);
ui.add(egui::Slider::new(&mut params.steps, 50..=600).text("Steps"));
egui::Window::new("Controls")
.collapsible(true)
.default_pos([16.0, 16.0])
.show(ctx, |ui| {
egui::CollapsingHeader::new("Camera")
.default_open(true)
.show(ui, |ui| {
ui.add(egui::Slider::new(&mut camera.distance, 3.0..=200.0).text("Distance"));
ui.add(egui::Slider::new(&mut camera.yaw, -3.14..=3.14).text("Yaw"));
ui.add(egui::Slider::new(&mut camera.pitch, 0.05..=3.09).text("Pitch"));
ui.add(egui::Slider::new(&mut camera.fov, 0.3..=2.0).text("FOV"));
});
egui::CollapsingHeader::new("Accretion Disk")
.default_open(true)
.show(ui, |ui| {
ui.add(egui::Slider::new(&mut params.disk_inner, 1.5..=6.0).text("Inner radius"));
ui.add(egui::Slider::new(&mut params.disk_outer, 6.0..=40.0).text("Outer radius"));
ui.add(egui::Slider::new(&mut params.disk_tilt, 0.0..=3.14).text("Tilt"));
ui.add(egui::Slider::new(&mut params.disk_brightness, 0.0..=3.0).text("Brightness"));
ui.add(egui::Slider::new(&mut params.disk_rotation_speed, 0.0..=3.0).text("Rotation speed"));
});
egui::CollapsingHeader::new("Doppler").show(ui, |ui| {
ui.checkbox(&mut params.doppler_enabled, "Enabled");
ui.add_enabled(params.doppler_enabled, egui::Slider::new(&mut params.doppler_strength, 0.0..=3.0).text("Strength"));
});
egui::CollapsingHeader::new("Renderer").show(ui, |ui| {
ui.add(egui::Slider::new(&mut params.steps, 50..=600).text("Steps"));
ui.add(egui::Slider::new(&mut params.render_scale, 0.5..=1.0).text("Render scale"));
});
egui::CollapsingHeader::new("Background").show(ui, |ui| {
ui.add(egui::Slider::new(&mut params.star_intensity, 0.0..=3.0).text("Star intensity"));
ui.add(egui::Slider::new(&mut params.skybox_intensity, 0.0..=3.0).text("Skybox intensity"));
});
egui::CollapsingHeader::new("Grid").show(ui, |ui| {
ui.checkbox(&mut params.grid_enabled, "Enabled");
ui.add_enabled(params.grid_enabled, egui::Slider::new(&mut params.grid_density, 0.1..=4.0).text("Density"));
});
});
// egui captures pointer when the cursor is over a window or being interacted with.
wants.0 = ctx.egui_wants_pointer_input();
} else {
wants.0 = false;
}
}