From c2031117b9ec24f311a804a15f6d6db1416c6e9b Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Tue, 19 Apr 2022 17:55:35 +0200 Subject: [PATCH] Add params for sdfs Issue #3 --- assets/fragment.wgsl | 3 +- assets/vertex.wgsl | 9 +++-- examples/params.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++ src/components.rs | 5 +++ src/lib.rs | 28 ++++++++++---- src/sdf_assets.rs | 2 +- src/ui.rs | 3 ++ 7 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 examples/params.rs diff --git a/assets/fragment.wgsl b/assets/fragment.wgsl index 3b33a41..56e9448 100644 --- a/assets/fragment.wgsl +++ b/assets/fragment.wgsl @@ -1,11 +1,12 @@ struct FragmentInput { [[location(0)]] color: vec4; [[location(1)]] pos: vec2; + [[location(2)]] params: vec4; }; [[stage(fragment)]] fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { - let d = sdf(in.pos); + let d = sdf(in.pos, in.params); return fill(d, in.color); // return vec4(1.0, 1.0, 0.0, 1.0); } \ No newline at end of file diff --git a/assets/vertex.wgsl b/assets/vertex.wgsl index 3d10433..3b9ce34 100644 --- a/assets/vertex.wgsl +++ b/assets/vertex.wgsl @@ -11,15 +11,17 @@ var view: View; struct Vertex { [[location(0)]] position: vec3; [[location(1)]] color: vec4; - [[location(2)]] rotation: vec2; - [[location(3)]] scale: f32; - [[location(4)]] frame: f32; + [[location(2)]] params: vec4; + [[location(3)]] rotation: vec2; + [[location(4)]] scale: f32; + [[location(5)]] frame: f32; }; struct VertexOutput { [[builtin(position)]] clip_position: vec4; [[location(0)]] color: vec4; [[location(1)]] pos: vec2; + [[location(2)]] params: vec4; }; [[stage(vertex)]] @@ -40,6 +42,7 @@ fn vertex( // Project the world position of the mesh into screen position out.clip_position = view.view_proj * vec4(pos, 1.); out.color = vertex.color; + out.params = vertex.params; out.pos = vec2(x, y) * vertex.frame; return out; } \ No newline at end of file diff --git a/examples/params.rs b/examples/params.rs new file mode 100644 index 0000000..d3b41b2 --- /dev/null +++ b/examples/params.rs @@ -0,0 +1,88 @@ +use std::f32::consts::PI; + +use bevy::prelude::*; +use bevy_asset_loader::{AssetCollection, AssetLoader}; +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() { + let mut app = App::new(); + + AssetLoader::new(GameState::Loading) + .continue_to_state(GameState::Running) + .with_collection::() + .build(&mut app); + + app.add_state(GameState::Loading) + .insert_resource(Msaa { samples: 4 }) + .add_plugins(DefaultPlugins) + .add_plugin(SmudPlugin) + .add_plugin(bevy_lospec::PalettePlugin) + .add_system_set(SystemSet::on_enter(GameState::Running).with_system(setup)) + .run(); +} + +#[derive(Clone, Eq, PartialEq, Debug, Hash)] +enum GameState { + Loading, + Running, +} + +#[derive(AssetCollection)] +struct AssetHandles { + #[asset(path = "vinik24.json")] + palette: Handle, +} + +fn setup( + mut commands: Commands, + mut shaders: ResMut>, + assets: Res, + palettes: Res>, +) { + let box_sdf = shaders.add_sdf_expr("sd_box(p, params.xy)"); + let padding = 5.; // need some padding for the outline/falloff + let spacing = 70.; + let palette = palettes.get(assets.palette.clone()).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::() * 20. + 1., random::() * 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::() * PI), + }; + + let color = palette + .iter() + .filter(|c| *c != &clear_color) + .choose(&mut rng) + .copied() + .unwrap_or(Color::PINK); + + commands.spawn_bundle(ShapeBundle { + transform, + shape: SmudShape { + color, + sdf: box_sdf.clone(), + frame: Frame::Quad(f32::max(size.x, size.y) + padding), + params: Some(Vec4::new(size.x, size.y, 0., 0.)), + ..Default::default() + }, + ..Default::default() + }); + } + + commands.spawn_bundle(OrthographicCameraBundle::new_2d()); +} diff --git a/src/components.rs b/src/components.rs index bf6e9a3..ca39f88 100644 --- a/src/components.rs +++ b/src/components.rs @@ -17,6 +17,10 @@ pub struct SmudShape { pub fill: Handle, // 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: Option, } impl Default for SmudShape { @@ -25,6 +29,7 @@ impl Default for SmudShape { color: Color::PINK, sdf: Default::default(), frame: Default::default(), + params: Default::default(), fill: DEFAULT_FILL_HANDLE.typed(), } } diff --git a/src/lib.rs b/src/lib.rs index b5a4690..3b1ff45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,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 @@ -241,29 +241,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 { @@ -382,6 +389,7 @@ fn extract_sdf_shaders( #[derive(Component, Clone, Debug)] struct ExtractedShape { color: Color, + params: Option, frame: f32, sdf_shader: Handle, // todo could be HandleId? fill_shader: Handle, // todo could be HandleId? @@ -407,6 +415,7 @@ fn extract_shapes( extracted_shapes.0.alloc().init(ExtractedShape { color: shape.color, + params: shape.params, transform: *transform, sdf_shader: shape.sdf.clone_weak(), fill_shader: shape.fill.clone_weak(), @@ -537,6 +546,7 @@ fn queue_shapes( // | ((color[3] * 255.0) as u32) << 24; let color = extracted_shape.color.as_linear_rgba_f32(); + let params = extracted_shape.params.unwrap_or_default().to_array(); let position = extracted_shape.transform.translation; let z = position.z; @@ -548,6 +558,7 @@ fn queue_shapes( let vertex = ShapeVertex { position, color, + params, rotation, scale: extracted_shape.transform.scale.x, frame: extracted_shape.frame, @@ -578,6 +589,7 @@ fn queue_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, diff --git a/src/sdf_assets.rs b/src/sdf_assets.rs index 539a11c..622acc2 100644 --- a/src/sdf_assets.rs +++ b/src/sdf_assets.rs @@ -13,7 +13,7 @@ impl SdfAssets for Assets { let shader = Shader::from_wgsl(format!( r#" #import bevy_smud::shapes -fn sdf(p: vec2) -> f32 {{ +fn sdf(p: vec2, params: vec4) -> f32 {{ {body} }} "# diff --git a/src/ui.rs b/src/ui.rs index 5f68379..9e37fea 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -103,6 +103,7 @@ fn extract_ui_shapes( sdf_shader: shape.sdf.clone_weak(), fill_shader: shape.fill.clone_weak(), frame, + params: shape.params, }); } } @@ -191,6 +192,7 @@ fn prepare_ui_shapes( } let color = extracted_shape.color.as_linear_rgba_f32(); + let params = extracted_shape.params.unwrap_or_default().to_array(); let position = position.into(); // let position = Vec3::ZERO.into(); @@ -201,6 +203,7 @@ fn prepare_ui_shapes( let vertex = ShapeVertex { position, color, + params, rotation, scale: extracted_shape.transform.scale.x, frame: extracted_shape.frame,