Skip to content

Add params for sdfs #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion assets/fragment.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
struct FragmentInput {
@location(0) color: vec4<f32>,
@location(1) pos: vec2<f32>,
@location(2) params: vec4<f32>,
};

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
let d = sdf::sdf(in.pos);
let d = sdf::sdf(in.pos, in.params);
return fill::fill(d, in.color);
}
9 changes: 6 additions & 3 deletions assets/vertex.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ var<uniform> view: View;
struct Vertex {
@location(0) position: vec3<f32>,
@location(1) color: vec4<f32>,
@location(2) rotation: vec2<f32>,
@location(3) scale: f32,
@location(4) frame: f32,
@location(2) params: vec4<f32>,
@location(3) rotation: vec2<f32>,
@location(4) scale: f32,
@location(5) frame: f32,
};

struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) pos: vec2<f32>,
@location(2) params: vec4<f32>,
};

@vertex
Expand All @@ -37,6 +39,7 @@ fn vertex(
// Project the world position of the mesh into screen position
out.clip_position = view.view_proj * vec4<f32>(pos, 1.);
out.color = vertex.color;
out.params = vertex.params;
out.pos = vec2<f32>(x, y) * vertex.frame;
return out;
}
87 changes: 87 additions & 0 deletions examples/params.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::f32::consts::PI;

use bevy::color::palettes::css as Colors;
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
use bevy_smud::prelude::*;
use rand::{prelude::IteratorRandom, random};

// this example shows how to use per-instance parameters in shapes
// in this simple example, a width and height is passed to a box shape,
// but it could be used for almost anything.

fn main() {
App::new()
.add_plugins((DefaultPlugins, SmudPlugin, bevy_lospec::PalettePlugin))
.init_state::<GameState>()
.add_loading_state(
LoadingState::new(GameState::Loading)
.continue_to_state(GameState::Running)
.load_collection::<AssetHandles>(),
)
.insert_resource(Msaa::Off)
.add_systems(OnEnter(GameState::Running), setup)
.run();
}

#[derive(Clone, Eq, PartialEq, Debug, Hash, States, Default)]
enum GameState {
#[default]
Loading,
Running,
}

#[derive(Resource, AssetCollection)]
struct AssetHandles {
#[asset(path = "vinik24.json")]
palette: Handle<bevy_lospec::Palette>,
}

fn setup(
mut commands: Commands,
mut shaders: ResMut<Assets<Shader>>,
assets: Res<AssetHandles>,
palettes: Res<Assets<bevy_lospec::Palette>>,
) {
let box_sdf = shaders.add_sdf_expr("smud::sd_box(p, params.xy)");
let padding = 5.; // need some padding for the outline/falloff
let spacing = 70.;
let palette = palettes.get(&assets.palette).unwrap();

let clear_color = palette.lightest();
commands.insert_resource(ClearColor(clear_color));
let mut rng = rand::thread_rng();

for i in 0..100 {
let size = Vec2::new(random::<f32>() * 20. + 1., random::<f32>() * 20. + 1.);
let x = ((i % 10) as f32 - 4.5) * spacing;
let y = ((i / 10) as f32 - 4.5) * spacing;

let transform = Transform {
scale: Vec3::splat(1.),
translation: Vec3::new(x, y, 0.),
rotation: Quat::from_rotation_z(random::<f32>() * PI),
};

let color = palette
.iter()
.filter(|c| *c != &clear_color)
.choose(&mut rng)
.copied()
.unwrap_or(Colors::PINK.into());

commands.spawn(ShapeBundle {
transform,
shape: SmudShape {
color,
sdf: box_sdf.clone(),
frame: Frame::Quad(f32::max(size.x, size.y) + padding),
params: Vec4::new(size.x, size.y, 0., 0.),
..Default::default()
},
..Default::default()
});
}

commands.spawn(Camera2dBundle::default());
}
5 changes: 5 additions & 0 deletions src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub struct SmudShape {
pub fill: Handle<Shader>, // todo: wrap in newtypes?
/// The outer bounds for the shape, should be bigger than the sdf shape
pub frame: Frame,
/// Parameters to pass to shapes, for things such as width of a box
// perhaps it would be a better idea to have this as a separate component?
// keeping it here for now...
pub params: Vec4,
}

impl Default for SmudShape {
Expand All @@ -27,6 +31,7 @@ impl Default for SmudShape {
color: css::PINK.into(),
sdf: default(),
frame: default(),
params: default(),
fill: DEFAULT_FILL_HANDLE,
}
}
Expand Down
31 changes: 22 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ impl SpecializedRenderPipeline for SmudPipeline {
debug!("specializing for {shader:?}");

// Customize how to store the meshes' vertex attributes in the vertex buffer
// Our meshes only have position and color
// Our meshes only have position, color and params
let vertex_attributes = vec![
// (GOTCHA! attributes are sorted alphabetically, and offsets need to reflect this)
// Color
Expand All @@ -230,29 +230,36 @@ impl SpecializedRenderPipeline for SmudPipeline {
VertexAttribute {
format: VertexFormat::Float32,
offset: (4) * 4,
shader_location: 4,
shader_location: 5,
},
// perf: Maybe it's possible to pack this more efficiently?
// Params
VertexAttribute {
format: VertexFormat::Float32x4,
offset: (4 + 1) * 4,
shader_location: 2,
},
// Position
VertexAttribute {
format: VertexFormat::Float32x3,
offset: (4 + 1) * 4,
offset: (4 + 1 + 4) * 4,
shader_location: 0,
},
// Rotation
VertexAttribute {
format: VertexFormat::Float32x2,
offset: (4 + 1 + 3) * 4,
shader_location: 2,
offset: (4 + 1 + 4 + 3) * 4,
shader_location: 3,
},
// Scale
VertexAttribute {
format: VertexFormat::Float32,
offset: (4 + 1 + 3 + 2) * 4,
shader_location: 3,
offset: (4 + 1 + 4 + 3 + 2) * 4,
shader_location: 4,
},
];
// This is the sum of the size of the attributes above
let vertex_array_stride = (4 + 1 + 3 + 2 + 1) * 4;
let vertex_array_stride = (4 + 1 + 4 + 3 + 2 + 1) * 4;

RenderPipelineDescriptor {
vertex: VertexState {
Expand Down Expand Up @@ -365,11 +372,12 @@ var<uniform> globals: Globals;
struct FragmentInput {{
@location(0) color: vec4<f32>,
@location(1) pos: vec2<f32>,
@location(2) params: vec4<f32>,
}};

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {{
let d = sdf::sdf(in.pos);
let d = sdf::sdf(in.pos, in.params);
return fill::fill(d, in.color);
}}
"#
Expand All @@ -391,6 +399,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {{
#[derive(Component, Clone, Debug)]
struct ExtractedShape {
color: Color,
params: Vec4,
frame: f32,
sdf_shader: Handle<Shader>,
fill_shader: Handle<Shader>,
Expand Down Expand Up @@ -419,6 +428,7 @@ fn extract_shapes(
entity,
ExtractedShape {
color: shape.color,
params: shape.params,
transform: *transform,
sdf_shader: shape.sdf.clone_weak(),
fill_shader: shape.fill.clone_weak(),
Expand Down Expand Up @@ -617,6 +627,7 @@ fn prepare_shapes(

let lrgba: LinearRgba = extracted_shape.color.into();
let color = lrgba.to_f32_array();
let params = extracted_shape.params.to_array();

let position = extracted_shape.transform.translation();
let position = position.into();
Expand All @@ -633,6 +644,7 @@ fn prepare_shapes(
let vertex = ShapeVertex {
position,
color,
params,
rotation,
scale,
frame: extracted_shape.frame,
Expand Down Expand Up @@ -674,6 +686,7 @@ fn prepare_shapes(
struct ShapeVertex {
pub color: [f32; 4],
pub frame: f32,
pub params: [f32; 4], // for now all shapes have 4 f32 parameters
pub position: [f32; 3],
pub rotation: [f32; 2],
pub scale: f32,
Expand Down
2 changes: 1 addition & 1 deletion src/sdf_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl SdfAssets for Assets<Shader> {

#import smud

fn sdf(p: vec2<f32>) -> f32 {{
fn sdf(p: vec2<f32>, params: vec4<f32>) -> f32 {{
{body}
}}
"#
Expand Down
3 changes: 3 additions & 0 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ fn extract_ui_shapes(
sdf_shader: shape.sdf.clone_weak(),
fill_shader: shape.fill.clone_weak(),
frame,
params: shape.params,
});
}
}
Expand Down Expand Up @@ -196,6 +197,7 @@ fn prepare_ui_shapes(
}

let color = extracted_shape.color.as_linear_rgba_f32();
let params = extracted_shape.params.to_array();

let position = position.into();

Expand All @@ -211,6 +213,7 @@ fn prepare_ui_shapes(
let vertex = ShapeVertex {
position,
color,
params,
rotation,
scale,
frame: extracted_shape.frame,
Expand Down
Loading