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::{
|
||||
PhysicsPlugins,
|
||||
math::Vector,
|
||||
@ -6,12 +9,14 @@ use avian2d::{
|
||||
};
|
||||
use bevy::{
|
||||
camera::ScalingMode,
|
||||
color::palettes::css::{GREEN, RED},
|
||||
color::palettes::css::{GREEN, RED, WHITE},
|
||||
input_focus::InputFocus,
|
||||
prelude::*,
|
||||
sprite_render::Material2dPlugin,
|
||||
};
|
||||
|
||||
pub mod avian;
|
||||
pub mod shaders;
|
||||
|
||||
const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
|
||||
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.
|
||||
// The unit allows the engine to tune its parameters for the scale of the world, improving stability.
|
||||
PhysicsPlugins::default().with_length_unit(20.0),
|
||||
Material2dPlugin::<BezierMaterial>::default(),
|
||||
CharacterControllerPlugin,
|
||||
))
|
||||
.init_resource::<InputFocus>()
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, debug_border)
|
||||
.add_systems(Update, do_menu)
|
||||
.add_systems(Startup, (setup, setup_ui))
|
||||
.add_systems(Update, (debug_border, do_menu, move_bezier))
|
||||
.insert_resource(Gravity(Vector::ZERO))
|
||||
.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 {
|
||||
width: percent(100),
|
||||
height: percent(100),
|
||||
@ -116,37 +154,47 @@ fn button(asset_server: &AssetServer) -> impl Bundle {
|
||||
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>) {
|
||||
let mut projection = OrthographicProjection::default_2d();
|
||||
projection.scaling_mode = ScalingMode::AutoMin {
|
||||
min_width: 1920.,
|
||||
min_height: 1080.,
|
||||
fn move_bezier(mut materials: ResMut<Assets<BezierMaterial>>, windows: Query<&Window>) {
|
||||
let Ok(window) = windows.single() else {
|
||||
return;
|
||||
};
|
||||
let Some(mouse_pos) = window.cursor_position() else {
|
||||
return;
|
||||
};
|
||||
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)),
|
||||
));
|
||||
for (_, mat) in materials.iter_mut() {
|
||||
mat.p0 = mouse_pos;
|
||||
}
|
||||
|
||||
// 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),
|
||||
));
|
||||
|
||||
commands.spawn(button(&assets));
|
||||
// pos: Vec2, in logical pixels
|
||||
// (0, 0) is bottom-left of the window
|
||||
println!("Mouse window position: {:?}", mouse_pos);
|
||||
}
|
||||
|
||||
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