fix: keep full-screen quad filled on window resize

The quad mesh was built once at startup with the initial aspect ratio, so
widening the window afterward left empty edges. Add a FullscreenQuad marker
component and a fit_quad_to_window system that rescales the quad's Transform
to the live aspect each frame (only mutating on change).

Also remove the misleading 'Render scale' slider — it isn't wired to a real
sub-resolution render target in Phase 1 (the full-screen quad always renders
at window resolution). Lowering Steps is the real perf lever. Documented in
a code comment for future work.
This commit is contained in:
xfy 2026-07-13 10:00:42 +08:00
parent 7e0ac65d55
commit 1e8cd93bc3
2 changed files with 31 additions and 4 deletions

View File

@ -3,6 +3,11 @@ use bevy::sprite_render::Material2dPlugin;
use super::material::BlackHoleMaterial; use super::material::BlackHoleMaterial;
/// Marks the full-screen quad so the resize system can find and rescale it
/// when the window's aspect ratio changes (the mesh is built once at startup).
#[derive(Component)]
struct FullscreenQuad;
pub struct BlackHolePlugin; pub struct BlackHolePlugin;
impl Plugin for BlackHolePlugin { impl Plugin for BlackHolePlugin {
@ -16,7 +21,7 @@ impl Plugin for BlackHolePlugin {
.add_systems(Startup, crate::scene::planets::spawn_default_planet) .add_systems(Startup, crate::scene::planets::spawn_default_planet)
.add_systems( .add_systems(
Update, Update,
(crate::camera::orbit_controller, mirror_params), (crate::camera::orbit_controller, mirror_params, fit_quad_to_window),
) )
.add_systems(Update, crate::scene::planets::upload_planets) .add_systems(Update, crate::scene::planets::upload_planets)
// bevy_egui 0.41 requires UI systems to run inside the egui context // bevy_egui 0.41 requires UI systems to run inside the egui context
@ -36,14 +41,34 @@ fn spawn_fullscreen_quad(
Err(_) => return, Err(_) => return,
}; };
let aspect = win.width() / win.height(); let aspect = win.width() / win.height();
// The mesh is a unit square (2x2 covers [-1,1]); we rescale via Transform
// in fit_quad_to_window whenever the aspect changes.
commands.spawn(( commands.spawn((
Mesh2d(meshes.add(Rectangle::new(2.0 * aspect, 2.0))), Mesh2d(meshes.add(Rectangle::new(2.0, 2.0))),
MeshMaterial2d(materials.add(BlackHoleMaterial::default())), MeshMaterial2d(materials.add(BlackHoleMaterial::default())),
Transform::default(), Transform::default().with_scale(Vec3::new(aspect, 1.0, 1.0)),
FullscreenQuad,
)); ));
commands.spawn(Camera2d); commands.spawn(Camera2d);
} }
/// Rescale the full-screen quad so it always covers the camera's view, even
/// after the window is resized. Camera2d's default projection spans y∈[-1,1]
/// and x∈[-aspect, aspect]; scaling the unit quad by (aspect, 1, 1) fills it.
fn fit_quad_to_window(window: Query<&Window>, mut quad: Query<&mut Transform, With<FullscreenQuad>>) {
let win = match window.single() {
Ok(w) => w,
Err(_) => return,
};
let aspect = win.width() / win.height();
for mut transform in &mut quad {
// Only mutate on change to avoid spamming change detection.
if transform.scale.x != aspect {
transform.scale.x = aspect;
}
}
}
fn mirror_params( fn mirror_params(
camera: Res<crate::camera::OrbitCamera>, camera: Res<crate::camera::OrbitCamera>,
params: Res<crate::params::BlackHoleParams>, params: Res<crate::params::BlackHoleParams>,

View File

@ -35,7 +35,9 @@ pub fn ui_system(
}); });
egui::CollapsingHeader::new("Renderer").show(ui, |ui| { 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.steps, 50..=600).text("Steps"));
ui.add(egui::Slider::new(&mut params.render_scale, 0.5..=1.0).text("Render scale")); // NOTE: render_scale is intentionally not exposed — it isn't wired
// to a real sub-resolution render target in Phase 1 (the full-screen
// quad always renders at window resolution). Lower `Steps` to gain FPS.
}); });
egui::CollapsingHeader::new("Background").show(ui, |ui| { 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.star_intensity, 0.0..=3.0).text("Star intensity"));