feat: BlackHoleUniforms + camera/params mirror system
This commit is contained in:
parent
1a2b157ef3
commit
265ec9925d
@ -1,11 +1,36 @@
|
|||||||
#import bevy_sprite::mesh2d_vertex_output::VertexOutput
|
#import bevy_sprite::mesh2d_vertex_output::VertexOutput
|
||||||
|
|
||||||
@group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> time: f32;
|
struct BlackHoleUniforms {
|
||||||
|
eye: vec4<f32>,
|
||||||
|
forward: vec4<f32>,
|
||||||
|
right: vec4<f32>,
|
||||||
|
up: vec4<f32>,
|
||||||
|
resolution: vec2<f32>,
|
||||||
|
time: f32,
|
||||||
|
_pad3: f32,
|
||||||
|
rs: f32,
|
||||||
|
disk_inner: f32,
|
||||||
|
disk_outer: f32,
|
||||||
|
disk_tilt: f32,
|
||||||
|
disk_brightness: f32,
|
||||||
|
disk_rotation_speed: f32,
|
||||||
|
doppler_strength: f32,
|
||||||
|
star_intensity: f32,
|
||||||
|
skybox_intensity: f32,
|
||||||
|
grid_density: f32,
|
||||||
|
doppler_enabled: u32,
|
||||||
|
grid_enabled: u32,
|
||||||
|
planet_count: u32,
|
||||||
|
steps: u32,
|
||||||
|
_pad4: f32,
|
||||||
|
_pad5: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> uniforms: BlackHoleUniforms;
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
// Gradient from time, just to prove the uniform flows.
|
// Prove the camera basis + uniforms flow: tint by eye direction.
|
||||||
let t = (sin(time) + 1.0) * 0.5;
|
let n = normalize(uniforms.eye.xyz);
|
||||||
let col = mix(vec3<f32>(0.02, 0.02, 0.05), vec3<f32>(0.08, 0.04, 0.12), t);
|
return vec4<f32>(abs(n) * 0.5 + 0.3, 1.0);
|
||||||
return vec4<f32>(col, 1.0);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,105 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::reflect::TypePath;
|
use bevy::reflect::TypePath;
|
||||||
use bevy::render::render_resource::AsBindGroup;
|
use bevy::render::render_resource::{AsBindGroup, ShaderType};
|
||||||
|
use bevy::render::storage::ShaderBuffer;
|
||||||
use bevy::shader::ShaderRef;
|
use bevy::shader::ShaderRef;
|
||||||
use bevy::sprite_render::Material2d;
|
use bevy::sprite_render::Material2d;
|
||||||
|
|
||||||
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
/// GPU uniform: params + camera packed into one struct bound at binding 0.
|
||||||
|
#[derive(Clone, ShaderType)]
|
||||||
|
pub struct BlackHoleUniforms {
|
||||||
|
// Camera basis + eye (4 vec3s = must align; pad each to vec4 via the shader struct)
|
||||||
|
pub eye: Vec3,
|
||||||
|
pub _pad0: f32,
|
||||||
|
pub forward: Vec3,
|
||||||
|
pub _pad1: f32,
|
||||||
|
pub right: Vec3,
|
||||||
|
pub _pad2: f32,
|
||||||
|
pub up: Vec3,
|
||||||
|
pub fov: f32,
|
||||||
|
// Resolution
|
||||||
|
pub resolution: Vec2,
|
||||||
|
pub time: f32,
|
||||||
|
pub _pad3: f32,
|
||||||
|
// Physics + disk
|
||||||
|
pub rs: f32,
|
||||||
|
pub disk_inner: f32,
|
||||||
|
pub disk_outer: f32,
|
||||||
|
pub disk_tilt: f32,
|
||||||
|
pub disk_brightness: f32,
|
||||||
|
pub disk_rotation_speed: f32,
|
||||||
|
pub doppler_strength: f32,
|
||||||
|
pub star_intensity: f32,
|
||||||
|
pub skybox_intensity: f32,
|
||||||
|
pub grid_density: f32,
|
||||||
|
// Flags packed as u32 (bools aren't valid uniform scalar types in WGSL)
|
||||||
|
pub doppler_enabled: u32,
|
||||||
|
pub grid_enabled: u32,
|
||||||
|
pub planet_count: u32,
|
||||||
|
pub steps: u32,
|
||||||
|
pub _pad4: f32,
|
||||||
|
pub _pad5: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BlackHoleUniforms {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
eye: Vec3::new(0.0, 0.0, 30.0),
|
||||||
|
_pad0: 0.0,
|
||||||
|
forward: Vec3::new(0.0, 0.0, -1.0),
|
||||||
|
_pad1: 0.0,
|
||||||
|
right: Vec3::new(1.0, 0.0, 0.0),
|
||||||
|
_pad2: 0.0,
|
||||||
|
up: Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
fov: 1.0,
|
||||||
|
resolution: Vec2::new(1280.0, 720.0),
|
||||||
|
time: 0.0,
|
||||||
|
_pad3: 0.0,
|
||||||
|
rs: 1.0,
|
||||||
|
disk_inner: 3.0,
|
||||||
|
disk_outer: 15.0,
|
||||||
|
disk_tilt: 1.318,
|
||||||
|
disk_brightness: 1.0,
|
||||||
|
disk_rotation_speed: 0.5,
|
||||||
|
doppler_strength: 1.0,
|
||||||
|
star_intensity: 1.0,
|
||||||
|
skybox_intensity: 0.0,
|
||||||
|
grid_density: 1.0,
|
||||||
|
doppler_enabled: 1,
|
||||||
|
grid_enabled: 0,
|
||||||
|
planet_count: 0,
|
||||||
|
steps: 300,
|
||||||
|
_pad4: 0.0,
|
||||||
|
_pad5: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One planet's data, uploaded in a storage buffer (binding 3).
|
||||||
|
#[derive(Clone, Copy, ShaderType, Default)]
|
||||||
|
pub struct SphereData {
|
||||||
|
pub center: Vec3,
|
||||||
|
pub radius: f32,
|
||||||
|
pub color: Vec3,
|
||||||
|
pub emissive: u32,
|
||||||
|
pub _pad0: f32,
|
||||||
|
pub _pad1: f32,
|
||||||
|
pub _pad2: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAX_PLANETS: usize = 32;
|
||||||
|
|
||||||
|
#[derive(Asset, TypePath, AsBindGroup, Clone)]
|
||||||
pub struct BlackHoleMaterial {
|
pub struct BlackHoleMaterial {
|
||||||
#[uniform(0)]
|
#[uniform(0)]
|
||||||
pub time: f32,
|
pub uniforms: BlackHoleUniforms,
|
||||||
|
// Texture at binding 1 + its matching sampler at binding 2. The derive
|
||||||
|
// requires the texture and sampler attributes to live on the same field.
|
||||||
|
#[texture(1)]
|
||||||
|
#[sampler(2)]
|
||||||
|
pub skybox: Option<Handle<Image>>,
|
||||||
|
#[storage(3, read_only)]
|
||||||
|
pub planets: Handle<ShaderBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material2d for BlackHoleMaterial {
|
impl Material2d for BlackHoleMaterial {
|
||||||
@ -15,3 +107,13 @@ impl Material2d for BlackHoleMaterial {
|
|||||||
"shaders/black_hole.wgsl".into()
|
"shaders/black_hole.wgsl".into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for BlackHoleMaterial {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
uniforms: BlackHoleUniforms::default(),
|
||||||
|
skybox: None,
|
||||||
|
planets: Handle::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -7,12 +7,11 @@ pub struct BlackHolePlugin;
|
|||||||
|
|
||||||
impl Plugin for BlackHolePlugin {
|
impl Plugin for BlackHolePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugins(Material2dPlugin::<BlackHoleMaterial>::default())
|
app.init_resource::<crate::camera::OrbitCamera>()
|
||||||
.add_systems(Startup, spawn_fullscreen_quad)
|
|
||||||
.add_systems(Update, update_time)
|
|
||||||
.init_resource::<crate::camera::OrbitCamera>()
|
|
||||||
.init_resource::<crate::params::BlackHoleParams>()
|
.init_resource::<crate::params::BlackHoleParams>()
|
||||||
.add_systems(Update, crate::camera::orbit_controller);
|
.add_plugins(Material2dPlugin::<BlackHoleMaterial>::default())
|
||||||
|
.add_systems(Startup, spawn_fullscreen_quad)
|
||||||
|
.add_systems(Update, (crate::camera::orbit_controller, mirror_params));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,17 +19,56 @@ fn spawn_fullscreen_quad(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<BlackHoleMaterial>>,
|
mut materials: ResMut<Assets<BlackHoleMaterial>>,
|
||||||
|
window: Query<&Window>,
|
||||||
) {
|
) {
|
||||||
commands.spawn(Camera2d);
|
let win = match window.single() {
|
||||||
|
Ok(w) => w,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let aspect = win.width() / win.height();
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh2d(meshes.add(Rectangle::new(2.0, 2.0))),
|
Mesh2d(meshes.add(Rectangle::new(2.0 * aspect, 2.0))),
|
||||||
MeshMaterial2d(materials.add(BlackHoleMaterial { time: 0.0 })),
|
MeshMaterial2d(materials.add(BlackHoleMaterial::default())),
|
||||||
Transform::default(),
|
Transform::default(),
|
||||||
));
|
));
|
||||||
|
commands.spawn(Camera2d);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_time(time: Res<Time>, mut materials: ResMut<Assets<BlackHoleMaterial>>) {
|
fn mirror_params(
|
||||||
|
camera: Res<crate::camera::OrbitCamera>,
|
||||||
|
params: Res<crate::params::BlackHoleParams>,
|
||||||
|
time: Res<Time>,
|
||||||
|
window: Query<&Window>,
|
||||||
|
mut materials: ResMut<Assets<BlackHoleMaterial>>,
|
||||||
|
) {
|
||||||
|
let win = match window.single() {
|
||||||
|
Ok(w) => w,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
// Only update when something changed.
|
||||||
|
let (eye, forward, right, up) = camera.basis();
|
||||||
for (_, mat) in materials.iter_mut() {
|
for (_, mat) in materials.iter_mut() {
|
||||||
mat.time = time.elapsed_secs();
|
let u = &mut mat.uniforms;
|
||||||
|
u.eye = eye;
|
||||||
|
u.forward = forward;
|
||||||
|
u.right = right;
|
||||||
|
u.up = up;
|
||||||
|
u.fov = camera.fov;
|
||||||
|
u.resolution = Vec2::new(win.width(), win.height());
|
||||||
|
u.time = time.elapsed_secs();
|
||||||
|
u.rs = params.rs;
|
||||||
|
u.disk_inner = params.disk_inner;
|
||||||
|
u.disk_outer = params.disk_outer;
|
||||||
|
u.disk_tilt = params.disk_tilt;
|
||||||
|
u.disk_brightness = params.disk_brightness;
|
||||||
|
u.disk_rotation_speed = params.disk_rotation_speed;
|
||||||
|
u.doppler_strength = params.doppler_strength;
|
||||||
|
u.star_intensity = params.star_intensity;
|
||||||
|
u.skybox_intensity = params.skybox_intensity;
|
||||||
|
u.grid_density = params.grid_density;
|
||||||
|
u.doppler_enabled = params.doppler_enabled as u32;
|
||||||
|
u.grid_enabled = params.grid_enabled as u32;
|
||||||
|
u.planet_count = params.planet_count;
|
||||||
|
u.steps = params.steps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user