wip
This commit is contained in:
parent
3611612d47
commit
b8aee4788d
3 changed files with 135 additions and 128 deletions
138
src/main.rs
138
src/main.rs
|
|
@ -1,16 +1,10 @@
|
||||||
//! A shader that uses the WGSL shading language.
|
//! A shader that uses the WGSL shading language.
|
||||||
|
mod material;
|
||||||
|
mod systems;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{input::common_conditions::input_just_pressed, prelude::*, sprite::Material2dPlugin};
|
||||||
asset::AssetEvent,
|
|
||||||
input::common_conditions::*,
|
|
||||||
prelude::*,
|
|
||||||
reflect::TypePath,
|
|
||||||
render::render_resource::{AsBindGroup, Shader, ShaderRef},
|
|
||||||
sprite::{AlphaMode2d, Material2d, Material2dPlugin},
|
|
||||||
window::{PrimaryWindow, WindowMode},
|
|
||||||
};
|
|
||||||
|
|
||||||
const SHADER_ASSET_PATH: &str = "shaders/default.wgsl";
|
use material::CustomMaterial;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
|
|
@ -18,128 +12,16 @@ fn main() {
|
||||||
DefaultPlugins,
|
DefaultPlugins,
|
||||||
Material2dPlugin::<CustomMaterial>::default(),
|
Material2dPlugin::<CustomMaterial>::default(),
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, systems::setup)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
update_material_time,
|
material::update_material_time,
|
||||||
resize_fullscreen_quad,
|
material::shader_hot_reload,
|
||||||
shader_hot_reload,
|
systems::resize_fullscreen_quad,
|
||||||
exit_app.run_if(input_just_pressed(KeyCode::Escape)),
|
systems::exit_app.run_if(input_just_pressed(KeyCode::Escape)),
|
||||||
toggle_fullscreen.run_if(input_just_pressed(KeyCode::F11)),
|
systems::toggle_fullscreen.run_if(input_just_pressed(KeyCode::F11)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set up a simple 2D-screen-like surface
|
|
||||||
fn setup(
|
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
|
||||||
mut materials: ResMut<Assets<CustomMaterial>>,
|
|
||||||
mut primary_window: Single<&mut Window, With<PrimaryWindow>>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
primary_window.decorations = false;
|
|
||||||
|
|
||||||
commands.spawn(Camera2d);
|
|
||||||
|
|
||||||
// quad that fills the whole window
|
|
||||||
let screen_size = Vec2::new(
|
|
||||||
primary_window.resolution.width(),
|
|
||||||
primary_window.resolution.height(),
|
|
||||||
);
|
|
||||||
// create material with initial time = 0.0
|
|
||||||
let material_handle = materials.add(CustomMaterial {
|
|
||||||
time: 0.0,
|
|
||||||
resolution: screen_size,
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.spawn((
|
|
||||||
Mesh2d(meshes.add(Rectangle::default())),
|
|
||||||
MeshMaterial2d(material_handle),
|
|
||||||
Transform::from_scale(Vec3::new(screen_size.x, screen_size.y, 1.0)),
|
|
||||||
FullscreenQuad,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marker component for the fullscreen quad so we can update it on window resize
|
|
||||||
#[derive(Component)]
|
|
||||||
struct FullscreenQuad;
|
|
||||||
|
|
||||||
/// Update fullscreen quad transforms when the primary window is resized.
|
|
||||||
fn resize_fullscreen_quad(
|
|
||||||
primary_window: Single<&Window, With<PrimaryWindow>>,
|
|
||||||
mut transform: Single<&mut Transform, With<FullscreenQuad>>,
|
|
||||||
) {
|
|
||||||
let size = Vec2::new(
|
|
||||||
primary_window.resolution.width(),
|
|
||||||
primary_window.resolution.height(),
|
|
||||||
);
|
|
||||||
transform.scale = Vec3::new(size.x, size.y, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_app(mut exit: EventWriter<AppExit>) {
|
|
||||||
exit.write(AppExit::Success);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toggle_fullscreen(mut primary_window: Single<&mut Window, With<PrimaryWindow>>) {
|
|
||||||
primary_window.mode = match primary_window.mode {
|
|
||||||
WindowMode::BorderlessFullscreen(_) => WindowMode::Windowed,
|
|
||||||
WindowMode::Windowed => WindowMode::BorderlessFullscreen(MonitorSelection::Current),
|
|
||||||
WindowMode::Fullscreen(_, _) => WindowMode::BorderlessFullscreen(MonitorSelection::Current),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Listen for shader asset changes and force materials to update so the new shader is used.
|
|
||||||
fn shader_hot_reload(
|
|
||||||
mut shader_events: EventReader<AssetEvent<Shader>>,
|
|
||||||
mut materials: ResMut<Assets<CustomMaterial>>,
|
|
||||||
) {
|
|
||||||
let mut reload_needed = false;
|
|
||||||
for event in shader_events.read() {
|
|
||||||
match event {
|
|
||||||
AssetEvent::Modified { .. } => {
|
|
||||||
info!("shader changed: {event:?}");
|
|
||||||
reload_needed = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if reload_needed {
|
|
||||||
// iterate all materials and 'touch' them so Bevy updates bindings using the recompiled shader
|
|
||||||
for (_handle, material) in materials.iter_mut() {
|
|
||||||
// assign to itself to mark changed
|
|
||||||
let cloned = material.clone();
|
|
||||||
*material = cloned;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the struct that will be passed to your shader
|
|
||||||
#[derive(Asset, TypePath, AsBindGroup, Clone)]
|
|
||||||
struct CustomMaterial {
|
|
||||||
#[uniform(0)]
|
|
||||||
time: f32,
|
|
||||||
#[uniform(1)]
|
|
||||||
resolution: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_material_time(mut materials: ResMut<Assets<CustomMaterial>>, time: Res<Time>) {
|
|
||||||
let t = time.elapsed_secs();
|
|
||||||
for (_handle, material) in materials.iter_mut() {
|
|
||||||
material.time = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Material2d trait is very configurable, but comes with sensible defaults for all methods.
|
|
||||||
/// You only need to implement functions for features that need non-default behavior. See the Material2d api docs for details!
|
|
||||||
impl Material2d for CustomMaterial {
|
|
||||||
fn fragment_shader() -> ShaderRef {
|
|
||||||
SHADER_ASSET_PATH.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alpha_mode(&self) -> AlphaMode2d {
|
|
||||||
AlphaMode2d::Opaque
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
61
src/material.rs
Normal file
61
src/material.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
use bevy::{
|
||||||
|
asset::AssetEvent,
|
||||||
|
prelude::*,
|
||||||
|
reflect::TypePath,
|
||||||
|
render::render_resource::{AsBindGroup, Shader, ShaderRef},
|
||||||
|
sprite::{AlphaMode2d, Material2d},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SHADER_ASSET_PATH: &str = "shaders/default.wgsl";
|
||||||
|
|
||||||
|
// This is the struct that will be passed to your shader
|
||||||
|
#[derive(Asset, TypePath, AsBindGroup, Clone)]
|
||||||
|
pub struct CustomMaterial {
|
||||||
|
#[uniform(0)]
|
||||||
|
pub time: f32,
|
||||||
|
#[uniform(1)]
|
||||||
|
pub resolution: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_material_time(mut materials: ResMut<Assets<CustomMaterial>>, time: Res<Time>) {
|
||||||
|
let t = time.elapsed_secs();
|
||||||
|
for (_handle, material) in materials.iter_mut() {
|
||||||
|
material.time = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Listen for shader asset changes and force materials to update so the new shader is used.
|
||||||
|
pub fn shader_hot_reload(
|
||||||
|
mut shader_events: EventReader<AssetEvent<Shader>>,
|
||||||
|
mut materials: ResMut<Assets<CustomMaterial>>,
|
||||||
|
) {
|
||||||
|
let mut reload_needed = false;
|
||||||
|
for event in shader_events.read() {
|
||||||
|
match event {
|
||||||
|
AssetEvent::Modified { .. } => {
|
||||||
|
info!("shader changed: {event:?}");
|
||||||
|
reload_needed = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reload_needed {
|
||||||
|
// iterate all materials and 'touch' them so Bevy updates bindings using the recompiled shader
|
||||||
|
for (_handle, material) in materials.iter_mut() {
|
||||||
|
// assign to itself to mark changed
|
||||||
|
let cloned = material.clone();
|
||||||
|
*material = cloned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material2d for CustomMaterial {
|
||||||
|
fn fragment_shader() -> ShaderRef {
|
||||||
|
SHADER_ASSET_PATH.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alpha_mode(&self) -> AlphaMode2d {
|
||||||
|
AlphaMode2d::Opaque
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/systems.rs
Normal file
64
src/systems.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
window::{PrimaryWindow, WindowMode},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::material::CustomMaterial;
|
||||||
|
|
||||||
|
/// Marker component for the fullscreen quad so we can update it on window resize
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct FullscreenQuad;
|
||||||
|
|
||||||
|
/// set up a simple 2D-screen-like surface
|
||||||
|
pub fn setup(
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<CustomMaterial>>,
|
||||||
|
mut primary_window: Single<&mut Window, With<PrimaryWindow>>,
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
primary_window.decorations = false;
|
||||||
|
|
||||||
|
commands.spawn(Camera2d);
|
||||||
|
|
||||||
|
// quad that fills the whole window
|
||||||
|
let screen_size = Vec2::new(
|
||||||
|
primary_window.resolution.width(),
|
||||||
|
primary_window.resolution.height(),
|
||||||
|
);
|
||||||
|
// create material with initial time = 0.0
|
||||||
|
let material_handle = materials.add(CustomMaterial {
|
||||||
|
time: 0.0,
|
||||||
|
resolution: screen_size,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Mesh2d(meshes.add(Rectangle::default())),
|
||||||
|
MeshMaterial2d(material_handle),
|
||||||
|
Transform::from_scale(Vec3::new(screen_size.x, screen_size.y, 1.0)),
|
||||||
|
FullscreenQuad,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update fullscreen quad transforms when the primary window is resized.
|
||||||
|
pub fn resize_fullscreen_quad(
|
||||||
|
primary_window: Single<&Window, With<PrimaryWindow>>,
|
||||||
|
mut transform: Single<&mut Transform, With<FullscreenQuad>>,
|
||||||
|
) {
|
||||||
|
let size = Vec2::new(
|
||||||
|
primary_window.resolution.width(),
|
||||||
|
primary_window.resolution.height(),
|
||||||
|
);
|
||||||
|
transform.scale = Vec3::new(size.x, size.y, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_app(mut exit: EventWriter<AppExit>) {
|
||||||
|
exit.write(AppExit::Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_fullscreen(mut primary_window: Single<&mut Window, With<PrimaryWindow>>) {
|
||||||
|
primary_window.mode = match primary_window.mode {
|
||||||
|
WindowMode::BorderlessFullscreen(_) => WindowMode::Windowed,
|
||||||
|
WindowMode::Windowed => WindowMode::BorderlessFullscreen(MonitorSelection::Current),
|
||||||
|
WindowMode::Fullscreen(_, _) => WindowMode::BorderlessFullscreen(MonitorSelection::Current),
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue