start to a shader??
This commit is contained in:
parent
db19b9c416
commit
f509833262
61
assets/shaders/bezier.wgsl
Normal file
61
assets/shaders/bezier.wgsl
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
struct Material {
|
||||||
|
p0: vec2<f32>,
|
||||||
|
c0: vec2<f32>,
|
||||||
|
c1: vec2<f32>,
|
||||||
|
p1: vec2<f32>,
|
||||||
|
width: f32,
|
||||||
|
color: vec4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(2) @binding(0)
|
||||||
|
var<uniform> mat: Material;
|
||||||
|
|
||||||
|
fn bezier(p0: vec2<f32>, c0: vec2<f32>, c1: vec2<f32>, p1: vec2<f32>, t: f32) -> vec2<f32> {
|
||||||
|
let u = 1.0 - t;
|
||||||
|
return
|
||||||
|
u*u*u*p0 +
|
||||||
|
3.0*u*u*t*c0 +
|
||||||
|
3.0*u*t*t*c1 +
|
||||||
|
t*t*t*p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distance_to_bezier(p: vec2<f32>) -> f32 {
|
||||||
|
var min_d = 1e9;
|
||||||
|
let steps = 32;
|
||||||
|
|
||||||
|
var prev = mat.p0;
|
||||||
|
for (var i = 1; i <= steps; i++) {
|
||||||
|
let t = f32(i) / f32(steps);
|
||||||
|
let cur = bezier(mat.p0, mat.c0, mat.c1, mat.p1, t);
|
||||||
|
|
||||||
|
// distance to segment
|
||||||
|
let v = cur - prev;
|
||||||
|
let w = p - prev;
|
||||||
|
let t_seg = clamp(dot(w, v) / dot(v, v), 0.0, 1.0);
|
||||||
|
let proj = prev + t_seg * v;
|
||||||
|
|
||||||
|
min_d = min(min_d, distance(p, proj));
|
||||||
|
prev = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
return min_d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fragment(
|
||||||
|
@location(0) world_pos: vec3<f32>,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
|
let d = distance_to_bezier(world_pos.xy);
|
||||||
|
|
||||||
|
let half_w = mat.width * 0.5;
|
||||||
|
let aa = fwidth(d);
|
||||||
|
|
||||||
|
let alpha = smoothstep(half_w + aa, half_w - aa, d);
|
||||||
|
|
||||||
|
if (alpha <= 0.001) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec4(mat.color.rgb, mat.color.a * alpha);
|
||||||
|
}
|
||||||
|
|
||||||
114
src/main.rs
114
src/main.rs
@ -1,4 +1,7 @@
|
|||||||
use crate::avian::{CharacterControllerBundle, CharacterControllerPlugin};
|
use crate::{
|
||||||
|
avian::{CharacterControllerBundle, CharacterControllerPlugin},
|
||||||
|
shaders::BezierMaterial,
|
||||||
|
};
|
||||||
use avian2d::{
|
use avian2d::{
|
||||||
PhysicsPlugins,
|
PhysicsPlugins,
|
||||||
math::Vector,
|
math::Vector,
|
||||||
@ -6,12 +9,14 @@ use avian2d::{
|
|||||||
};
|
};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
camera::ScalingMode,
|
camera::ScalingMode,
|
||||||
color::palettes::css::{GREEN, RED},
|
color::palettes::css::{GREEN, RED, WHITE},
|
||||||
input_focus::InputFocus,
|
input_focus::InputFocus,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
sprite_render::Material2dPlugin,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod avian;
|
pub mod avian;
|
||||||
|
pub mod shaders;
|
||||||
|
|
||||||
const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
|
const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
|
||||||
const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
|
const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
|
||||||
@ -24,12 +29,12 @@ fn main() {
|
|||||||
// Add physics plugins and specify a units-per-meter scaling factor, 1 meter = 20 pixels.
|
// Add physics plugins and specify a units-per-meter scaling factor, 1 meter = 20 pixels.
|
||||||
// The unit allows the engine to tune its parameters for the scale of the world, improving stability.
|
// The unit allows the engine to tune its parameters for the scale of the world, improving stability.
|
||||||
PhysicsPlugins::default().with_length_unit(20.0),
|
PhysicsPlugins::default().with_length_unit(20.0),
|
||||||
|
Material2dPlugin::<BezierMaterial>::default(),
|
||||||
CharacterControllerPlugin,
|
CharacterControllerPlugin,
|
||||||
))
|
))
|
||||||
.init_resource::<InputFocus>()
|
.init_resource::<InputFocus>()
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, (setup, setup_ui))
|
||||||
.add_systems(Update, debug_border)
|
.add_systems(Update, (debug_border, do_menu, move_bezier))
|
||||||
.add_systems(Update, do_menu)
|
|
||||||
.insert_resource(Gravity(Vector::ZERO))
|
.insert_resource(Gravity(Vector::ZERO))
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
@ -81,8 +86,41 @@ fn do_menu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button(asset_server: &AssetServer) -> impl Bundle {
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
(
|
let mut projection = OrthographicProjection::default_2d();
|
||||||
|
projection.scaling_mode = ScalingMode::AutoMin {
|
||||||
|
min_width: 1920.,
|
||||||
|
min_height: 1080.,
|
||||||
|
};
|
||||||
|
commands.spawn((Camera2d, Projection::Orthographic(projection)));
|
||||||
|
|
||||||
|
// player
|
||||||
|
commands.spawn((
|
||||||
|
Sprite::from_image(asset_server.load("player.png")),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
CharacterControllerBundle::new(Collider::capsule(15., 27.5)),
|
||||||
|
));
|
||||||
|
|
||||||
|
// A cube to move around
|
||||||
|
commands.spawn((
|
||||||
|
Sprite {
|
||||||
|
color: Color::srgb(0.0, 0.4, 0.7),
|
||||||
|
custom_size: Some(Vec2::new(30.0, 30.0)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(50.0, -100.0, 0.0),
|
||||||
|
RigidBody::Dynamic,
|
||||||
|
Collider::rectangle(30.0, 30.0),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_ui(
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<BezierMaterial>>,
|
||||||
|
) {
|
||||||
|
commands.spawn((
|
||||||
Node {
|
Node {
|
||||||
width: percent(100),
|
width: percent(100),
|
||||||
height: percent(100),
|
height: percent(100),
|
||||||
@ -116,37 +154,47 @@ fn button(asset_server: &AssetServer) -> impl Bundle {
|
|||||||
TextShadow::default(),
|
TextShadow::default(),
|
||||||
)]
|
)]
|
||||||
)],
|
)],
|
||||||
)
|
));
|
||||||
|
|
||||||
|
// Curve points (world-space)
|
||||||
|
let p0 = Vec2::new(-200.0, -100.0);
|
||||||
|
let p1 = Vec2::new(200.0, 100.0);
|
||||||
|
let c0 = p0 + Vec2::new(150.0, 0.0);
|
||||||
|
let c1 = p1 + Vec2::new(-150.0, 0.0);
|
||||||
|
|
||||||
|
// Bounding quad (must contain entire curve + width)
|
||||||
|
let center = (p0 + p1) * 0.5;
|
||||||
|
let size = Vec2::new(500.0, 300.0);
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Mesh2d(meshes.add(Rectangle::new(size.x, size.y))),
|
||||||
|
MeshMaterial2d(materials.add(BezierMaterial {
|
||||||
|
p0,
|
||||||
|
c0,
|
||||||
|
c1,
|
||||||
|
p1,
|
||||||
|
width: 12.0,
|
||||||
|
color: WHITE.into(),
|
||||||
|
})),
|
||||||
|
Transform::from_translation(center.extend(0.0)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, assets: Res<AssetServer>) {
|
fn move_bezier(mut materials: ResMut<Assets<BezierMaterial>>, windows: Query<&Window>) {
|
||||||
let mut projection = OrthographicProjection::default_2d();
|
let Ok(window) = windows.single() else {
|
||||||
projection.scaling_mode = ScalingMode::AutoMin {
|
return;
|
||||||
min_width: 1920.,
|
};
|
||||||
min_height: 1080.,
|
let Some(mouse_pos) = window.cursor_position() else {
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
commands.spawn((Camera2d, Projection::Orthographic(projection)));
|
|
||||||
|
|
||||||
// player
|
for (_, mat) in materials.iter_mut() {
|
||||||
commands.spawn((
|
mat.p0 = mouse_pos;
|
||||||
Sprite::from_image(asset_server.load("player.png")),
|
}
|
||||||
Transform::from_xyz(0.0, 0.0, 0.0),
|
|
||||||
CharacterControllerBundle::new(Collider::capsule(15., 27.5)),
|
|
||||||
));
|
|
||||||
|
|
||||||
// A cube to move around
|
// pos: Vec2, in logical pixels
|
||||||
commands.spawn((
|
// (0, 0) is bottom-left of the window
|
||||||
Sprite {
|
println!("Mouse window position: {:?}", mouse_pos);
|
||||||
color: Color::srgb(0.0, 0.4, 0.7),
|
|
||||||
custom_size: Some(Vec2::new(30.0, 30.0)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
Transform::from_xyz(50.0, -100.0, 0.0),
|
|
||||||
RigidBody::Dynamic,
|
|
||||||
Collider::rectangle(30.0, 30.0),
|
|
||||||
));
|
|
||||||
|
|
||||||
commands.spawn(button(&assets));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_border(mut gizmos: Gizmos) {
|
fn debug_border(mut gizmos: Gizmos) {
|
||||||
|
|||||||
33
src/shaders/mod.rs
Normal file
33
src/shaders/mod.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::render::render_resource::*;
|
||||||
|
use bevy::shader::ShaderRef;
|
||||||
|
use bevy::sprite_render::{AlphaMode2d, Material2d};
|
||||||
|
|
||||||
|
// This is the struct that will be passed to your shader
|
||||||
|
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
||||||
|
pub struct BezierMaterial {
|
||||||
|
#[uniform(0)]
|
||||||
|
pub p0: Vec2,
|
||||||
|
#[uniform(0)]
|
||||||
|
pub c0: Vec2,
|
||||||
|
#[uniform(0)]
|
||||||
|
pub c1: Vec2,
|
||||||
|
#[uniform(0)]
|
||||||
|
pub p1: Vec2,
|
||||||
|
#[uniform(0)]
|
||||||
|
pub width: f32,
|
||||||
|
#[uniform(0)]
|
||||||
|
pub color: LinearRgba,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 BezierMaterial {
|
||||||
|
fn fragment_shader() -> ShaderRef {
|
||||||
|
"shaders/bezier.wgsl".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alpha_mode(&self) -> AlphaMode2d {
|
||||||
|
AlphaMode2d::Blend
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user