Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions examples/joins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::f32::consts::TAU;

use bevy::{
color::palettes::css::{BLUE, RED},
prelude::*,
};
use bevy_polyline::prelude::*;

const NUM_STEPS: u16 = 8;
const RADIUS: f32 = 1.0;
const WIDTH: f32 = 60.0;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(PolylinePlugin)
.add_systems(Startup, setup)
.run();
}

fn setup(
mut commands: Commands,
mut polyline_materials: ResMut<Assets<PolylineMaterial>>,
mut polylines: ResMut<Assets<Polyline>>,
) {
for (joins, offset, color) in [(false, -1.0, RED), (true, 1.0, BLUE)] {
let center = Vec3 {
x: 1.5 * offset,
y: 0.0,
z: 0.0,
};

let max = f32::from(NUM_STEPS);
let vertices = (0u16..NUM_STEPS + 1)
.map(|t| {
let angle = f32::from(t) / max * TAU;
Vec3 {
x: angle.cos() * RADIUS,
y: angle.sin() * RADIUS,
z: 0.0,
} + center
})
.collect();

commands.spawn(PolylineBundle {
polyline: PolylineHandle(polylines.add(Polyline { vertices })),
material: PolylineMaterialHandle(polyline_materials.add(PolylineMaterial {
width: WIDTH,
color: color.into(),
perspective: false,
joins,
..default()
})),
..default()
});
}

// camera
commands.spawn((
Camera3d::default(),
Camera {
hdr: true,
..default()
},
Msaa::Sample4,
Transform::from_xyz(0.0, 0.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
));
}
18 changes: 18 additions & 0 deletions src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ pub struct PolylineMaterial {
///
/// Note that `depth_bias` **does not** interact with this in any way.
pub perspective: bool,

// Whether to render round joins and caps, with a radius of half the line width.
pub joins: bool,
}

impl Default for PolylineMaterial {
Expand All @@ -73,6 +76,7 @@ impl Default for PolylineMaterial {
color: Color::WHITE.to_linear(),
depth_bias: 0.0,
perspective: false,
joins: false,
}
}
}
Expand Down Expand Up @@ -112,6 +116,7 @@ pub struct GpuPolylineMaterial {
pub perspective: bool,
pub bind_group: BindGroup,
pub alpha_mode: AlphaMode,
pub joins: bool,
}

impl RenderAsset for GpuPolylineMaterial {
Expand Down Expand Up @@ -156,6 +161,7 @@ impl RenderAsset for GpuPolylineMaterial {
perspective: polyline_material.perspective,
alpha_mode,
bind_group,
joins: polyline_material.joins,
})
}
}
Expand Down Expand Up @@ -218,6 +224,15 @@ impl SpecializedRenderPipeline for PolylineMaterialPipeline {
.shader_defs
.push("POLYLINE_PERSPECTIVE".into());
}

if key.contains(PolylinePipelineKey::JOINS) {
descriptor.vertex.shader_defs.push("JOINS".into());

if let Some(ref mut fragment) = descriptor.fragment {
fragment.shader_defs.push("JOINS".into());
}
}

descriptor.layout = vec![
self.polyline_pipeline.view_layout.clone(),
self.polyline_pipeline.polyline_layout.clone(),
Expand Down Expand Up @@ -322,6 +337,9 @@ pub fn queue_material_polylines(
if material.perspective {
polyline_key |= PolylinePipelineKey::PERSPECTIVE
}
if material.joins {
polyline_key |= PolylinePipelineKey::JOINS
}
let pipeline_id =
pipelines.specialize(&pipeline_cache, &material_pipeline, polyline_key);

Expand Down
2 changes: 1 addition & 1 deletion src/polyline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use bevy::{
},
},
prelude::*,
reflect::TypePath,
render::{
extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
Expand Down Expand Up @@ -284,6 +283,7 @@ bitflags::bitflags! {
const PERSPECTIVE = (1 << 0);
const TRANSPARENT_MAIN_PASS = (1 << 1);
const HDR = (1 << 2);
const JOINS = (1 << 3);
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
}
}
Expand Down
45 changes: 41 additions & 4 deletions src/shaders/polyline.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ struct Vertex {
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) color: vec4<f32>,
#ifdef JOINS
@location(1) uv: vec2<f32>,
@location(2) max_u: f32,
#endif
};

@vertex
Expand All @@ -53,10 +57,13 @@ fn vertex(vertex: Vertex) -> VertexOutput {
let clip = mix(clip0, clip1, position.z);

let resolution = vec2(view.viewport.z, view.viewport.w);
let screen0 = resolution * (0.5 * clip0.xy / clip0.w + 0.5);
let screen1 = resolution * (0.5 * clip1.xy / clip1.w + 0.5);
var screen0 = resolution * (0.5 * clip0.xy / clip0.w + 0.5);
var screen1 = resolution * (0.5 * clip1.xy / clip1.w + 0.5);

let diff = screen1 - screen0;
let len = length(diff);

let x_basis = normalize(screen1 - screen0);
let x_basis = diff / len;
let y_basis = vec2(-x_basis.y, x_basis.x);

var line_width = material.width;
Expand All @@ -71,6 +78,16 @@ fn vertex(vertex: Vertex) -> VertexOutput {
}
#endif

// low-poly join technique similar to
// https://www.researchgate.net/publication/220200701_High-Quality_Cartographic_Roads_on_High-Resolution_DEMs
#ifdef JOINS
screen0 = screen0 - x_basis * line_width / 2.0;
screen1 = screen1 + x_basis * line_width / 2.0;

let max_u = 1.0 + line_width / len;
let uv = vec2((2.0 * position.z - 1.0) * max_u, position.y * 2.0);
#endif

let pt_offset = line_width * (position.x * x_basis + position.y * y_basis);
let pt0 = screen0 + pt_offset;
let pt1 = screen1 + pt_offset;
Expand All @@ -91,7 +108,11 @@ fn vertex(vertex: Vertex) -> VertexOutput {
depth = depth * exp2(-material.depth_bias * log2(clip.w / depth - epsilon));
}

return VertexOutput(vec4(clip.w * ((2.0 * pt) / resolution - 1.0), depth, clip.w), color);
#ifdef JOINS
return VertexOutput(vec4(clip.w * ((2.0 * pt) / resolution - 1.0), depth, clip.w), color, uv, max_u - 1.0);
#else
return VertexOutput(vec4(clip.w * ((2.0 * pt) / resolution - 1.0), depth, clip.w), color);
#endif
}

fn clip_near_plane(a: vec4<f32>, b: vec4<f32>) -> vec4<f32> {
Expand All @@ -108,9 +129,25 @@ fn clip_near_plane(a: vec4<f32>, b: vec4<f32>) -> vec4<f32> {

struct FragmentInput {
@location(0) color: vec4<f32>,
#ifdef JOINS
@location(1) uv: vec2<f32>,
@location(2) max_u: f32,
#endif
};

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef JOINS
if ( abs( in.uv.x ) > 1.0 ) {
let a = in.uv.y;
let b = select(in.uv.x + 1.0, in.uv.x - 1.0, in.uv.x > 0.0) / in.max_u;
let len2 = a * a + b * b;

if ( len2 > 1.0 ) {
discard;
}
}
#endif

return in.color;
}