Skip to content
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

Fix arrows requiring a radius to be visible #1720

Merged
merged 6 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
67 changes: 53 additions & 14 deletions crates/re_renderer/shader/lines.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ const POSITION_DATA_TEXTURE_SIZE: i32 = 256;
// See lines.rs#LineStripFlags
const CAP_END_TRIANGLE: u32 = 1u;
const CAP_END_ROUND: u32 = 2u;
const CAP_START_TRIANGLE: u32 = 4u;
const CAP_START_ROUND: u32 = 8u;
const NO_COLOR_GRADIENT: u32 = 16u;
const CAP_END_EXTEND_OUTWARDS: u32 = 4u;
const CAP_START_TRIANGLE: u32 = 8u;
const CAP_START_ROUND: u32 = 16u;
const CAP_START_EXTEND_OUTWARDS: u32 = 32u;
const NO_COLOR_GRADIENT: u32 = 64u;

// A lot of the attributes don't need to be interpolated across triangles.
// To document that and safe some time we mark them up with @interpolate(flat)
Expand All @@ -63,7 +65,7 @@ struct VertexOut {
active_radius: f32,

@location(4) @interpolate(perspective)
closest_strip_position: Vec3,
round_cap_circle_center: Vec3,

@location(5) @interpolate(flat)
currently_active_flags: u32,
Expand Down Expand Up @@ -110,6 +112,16 @@ fn read_position_data(idx: u32) -> PositionData {
return data;
}

fn cap_length(flags: u32, radius: f32) -> f32 {
if has_any_flag(flags, CAP_END_TRIANGLE | CAP_START_TRIANGLE) {
return radius * 4.0;
} else if has_any_flag(flags, CAP_END_ROUND | CAP_START_ROUND) {
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
return radius;
} else {
return 0.0;
}
}

@vertex
fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
//
Expand Down Expand Up @@ -141,9 +153,11 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
let top_bottom = select(-1.0, 1.0, (local_idx <= 1u || local_idx == 3u)); // 1 for a top vertex, -1 for a bottom vertex.

// Let's assume for starters this vertex is part of a regular quad in a line strip.
// Fetch position data at the beginning and the end of that quad.
// Fetch position data at the beginning and the end of that quad, as well as the position data before and after this quad.
let pos_data_quad_before = read_position_data(pos_data_idx - 1u);
let pos_data_quad_begin = read_position_data(pos_data_idx);
let pos_data_quad_end = read_position_data(pos_data_idx + 1u);
let pos_data_quad_after = read_position_data(pos_data_idx + 2u);

// If the strip indices don't match up for start/end, then we're in a cap triangle!
let is_cap_triangle = pos_data_quad_begin.strip_index != pos_data_quad_end.strip_index;
Expand All @@ -162,17 +176,17 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
pos_data_current = pos_data_quad_end;
}

// Note that for caps triangles, the pos_data_current.pos stays constant over the entire triangle!
// However, to handle things in the fragment shader we need to add a seceond position which is different
// for start/end of the cap triangle.
// The closest "line strip skeleton" position to the current vertex.
// Various things like end cap or radius boosting can cause adjustments to it.
var center_position = pos_data_current.pos;

// Data valid for the entire strip that this vertex belongs to.
let strip_data = read_strip_data(pos_data_current.strip_index);

// Active flags are all flags that we react to at the current vertex.
// I.e. cap flags are only active in the respective cap triangle.
var currently_active_flags = strip_data.flags & (~(CAP_START_TRIANGLE | CAP_END_TRIANGLE | CAP_START_ROUND | CAP_END_ROUND));
var currently_active_flags = strip_data.flags &
(~(CAP_START_TRIANGLE | CAP_END_TRIANGLE | CAP_START_ROUND | CAP_END_ROUND | CAP_START_EXTEND_OUTWARDS | CAP_END_EXTEND_OUTWARDS));
Wumpf marked this conversation as resolved.
Show resolved Hide resolved

// Compute quad_dir and correct the currently_active_flags & correct center_position triangle caps.
var quad_dir: Vec3;
Expand All @@ -181,11 +195,11 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
if is_end_cap && has_any_flag(strip_data.flags, CAP_END_TRIANGLE | CAP_END_ROUND) {
currently_active_flags |= strip_data.flags & (CAP_END_TRIANGLE | CAP_END_ROUND);
is_at_pointy_end = is_at_quad_end;
quad_dir = pos_data_quad_begin.pos - read_position_data(pos_data_idx - 1u).pos; // Go one pos data back
quad_dir = pos_data_quad_begin.pos - pos_data_quad_before.pos; // Go one pos data back
} else if !is_end_cap && has_any_flag(strip_data.flags, CAP_START_TRIANGLE | CAP_START_ROUND) {
currently_active_flags |= strip_data.flags & (CAP_START_TRIANGLE | CAP_START_ROUND);
is_at_pointy_end = !is_at_quad_end;
quad_dir = read_position_data(pos_data_idx + 2u).pos - pos_data_quad_end.pos; // Go one pos data forward
quad_dir = pos_data_quad_after.pos - pos_data_quad_end.pos; // Go one pos data forward
} else {
// Discard vertex.
center_position = Vec3(0.0/0.0, 0.0/0.0, 0.0/0.0);
Expand All @@ -200,6 +214,29 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
let camera_ray = camera_ray_to_world_pos(center_position);
let camera_distance = distance(camera_ray.origin, center_position);
var strip_radius = unresolved_size_to_world(strip_data.unresolved_radius, camera_distance, frame.auto_size_lines);

// Make space for the end cap if this is either the cap itself or the cap follows right after/before this quad.
if !has_any_flag(strip_data.flags, CAP_END_EXTEND_OUTWARDS) {
var neighbor_cap_flags = currently_active_flags;
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
if is_at_quad_end && pos_data_current.strip_index != pos_data_quad_after.strip_index {
neighbor_cap_flags |= strip_data.flags & (CAP_END_TRIANGLE | CAP_END_ROUND);
}
if has_any_flag(neighbor_cap_flags, CAP_END_TRIANGLE | CAP_END_ROUND) {
center_position -= quad_dir * cap_length(neighbor_cap_flags, strip_radius);
}
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
}
if !has_any_flag(strip_data.flags, CAP_START_EXTEND_OUTWARDS) {
var neighbor_cap_flags = currently_active_flags;
if !is_at_quad_end && pos_data_current.strip_index != pos_data_quad_before.strip_index {
neighbor_cap_flags |= strip_data.flags & (CAP_START_TRIANGLE | CAP_START_ROUND);
}
if has_any_flag(neighbor_cap_flags, CAP_START_TRIANGLE | CAP_START_ROUND) {
center_position += quad_dir * cap_length(neighbor_cap_flags, strip_radius);
}
}
Wumpf marked this conversation as resolved.
Show resolved Hide resolved

// Boost radius only now that we subtracted/added the cap length.
// This way we don't get a gap for the outline at the cap.
if draw_data.radius_boost_in_ui_points > 0.0 {
let size_boost = world_size_from_point_size(draw_data.radius_boost_in_ui_points, camera_distance);
strip_radius += size_boost;
Expand All @@ -218,13 +255,15 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
// Span up the vertex away from the line's axis, orthogonal to the direction to the camera
let dir_up = normalize(cross(camera_ray.direction, quad_dir));

let round_cap_circle_center = center_position;

var pos: Vec3;
if is_cap_triangle && is_at_pointy_end {
// We extend the cap triangle far enough to handle triangle caps,
// and far enough to do rounded caps without any visible clipping.
// There is _some_ clipping, but we can't see it ;)
// If we want to do it properly, we would extend the radius for rounded caps too.
center_position = pos_data_current.pos + quad_dir * (strip_radius * 4.0 * select(-1.0, 1.0, is_end_cap));
center_position += quad_dir * (strip_radius * 4.0 * select(-1.0, 1.0, is_end_cap));
pos = center_position;
} else {
pos = center_position + (active_radius * top_bottom) * dir_up;
Expand All @@ -235,7 +274,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
out.position = frame.projection_from_world * Vec4(pos, 1.0);
out.position_world = pos;
out.center_position = center_position;
out.closest_strip_position = pos_data_current.pos;
out.round_cap_circle_center = round_cap_circle_center;
out.color = strip_data.color;
out.active_radius = active_radius;
out.currently_active_flags = currently_active_flags;
Expand All @@ -246,7 +285,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
fn compute_coverage(in: VertexOut) -> f32 {
var coverage = 1.0;
if has_any_flag(in.currently_active_flags, CAP_START_ROUND | CAP_END_ROUND) {
let distance_to_skeleton = length(in.position_world - in.closest_strip_position);
let distance_to_skeleton = length(in.position_world - in.round_cap_circle_center);
let pixel_world_size = approx_pixel_world_size_at(length(in.position_world - frame.camera_position));

// It's important that we do antialias both inwards and outwards of the exact border.
Expand Down
8 changes: 6 additions & 2 deletions crates/re_renderer/src/line_strip_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ where
.flags(
LineStripFlags::CAP_END_ROUND
| LineStripFlags::CAP_START_ROUND
| LineStripFlags::NO_COLOR_GRADIENT,
| LineStripFlags::NO_COLOR_GRADIENT
| LineStripFlags::CAP_END_EXTEND_OUTWARDS
| LineStripFlags::CAP_START_EXTEND_OUTWARDS,
)
}

Expand Down Expand Up @@ -325,7 +327,9 @@ where
.flags(
LineStripFlags::CAP_END_ROUND
| LineStripFlags::CAP_START_ROUND
| LineStripFlags::NO_COLOR_GRADIENT,
| LineStripFlags::NO_COLOR_GRADIENT
| LineStripFlags::CAP_END_EXTEND_OUTWARDS
| LineStripFlags::CAP_START_EXTEND_OUTWARDS,
)
}

Expand Down
22 changes: 11 additions & 11 deletions crates/re_renderer/src/renderer/lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,22 +213,22 @@ bitflags! {
/// Adds a round cap at the end of a line strip (excludes other end caps).
const CAP_END_ROUND = 0b0000_0010;

/// By default, line caps end at the last/first position of the the line strip.
/// This flag makes end caps extend outwards.
const CAP_END_EXTEND_OUTWARDS = 0b0000_0100;

/// Puts a equilateral triangle at the start of the line strip (excludes other start caps).
const CAP_START_TRIANGLE = 0b0000_0100;
const CAP_START_TRIANGLE = 0b0000_1000;

/// Adds a round cap at the start of a line strip (excludes other start caps).
const CAP_START_ROUND = 0b0000_1000;
const CAP_START_ROUND = 0b0001_0000;

/// Disable color gradient which is on by default
const NO_COLOR_GRADIENT = 0b0001_0000;
}
}
/// By default, line caps end at the last/first position of the the line strip.
/// This flag makes end caps extend outwards.
const CAP_START_EXTEND_OUTWARDS = 0b0010_0000;

impl LineStripFlags {
pub fn get_triangle_cap_tip_length(line_radius: f32) -> f32 {
// hardcoded in lines.wgsl
// Alternatively we could declare the entire last segment to be a tip, making the line length configurable!
line_radius * 4.0
/// Disable color gradient which is on by default
const NO_COLOR_GRADIENT = 0b0100_0000;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use re_log_types::{
Arrow3D, Component,
};
use re_query::{query_primary_with_history, EntityView, QueryError};
use re_renderer::{renderer::LineStripFlags, Size};
use re_renderer::Size;

use crate::{
misc::{SpaceViewHighlights, TransformCache, ViewerContext},
Expand Down Expand Up @@ -67,15 +67,17 @@ impl Arrows3DPart {
let origin = glam::Vec3::from(origin);

let radius = radius.map_or(Size::AUTO, |r| Size(r.0));
let tip_length = LineStripFlags::get_triangle_cap_tip_length(radius.0);
let vector_len = vector.length();
let end = origin + vector * ((vector_len - tip_length) / vector_len);
let end = origin + vector;
Wumpf marked this conversation as resolved.
Show resolved Hide resolved

let segment = line_batch
.add_segment(origin, end)
.radius(radius)
.color(color)
.flags(re_renderer::renderer::LineStripFlags::CAP_END_TRIANGLE)
.flags(
re_renderer::renderer::LineStripFlags::CAP_END_TRIANGLE
| re_renderer::renderer::LineStripFlags::CAP_START_ROUND
| re_renderer::renderer::LineStripFlags::CAP_START_EXTEND_OUTWARDS,
)
.user_data(picking_instance_hash);

if let Some(outline_mask_ids) = entity_highlight.instances.get(&instance_key) {
Expand Down