Skip to content

Commit

Permalink
Fixed graphical artifacts in rotation gizmo
Browse files Browse the repository at this point in the history
  • Loading branch information
urholaukkarinen committed Jan 29, 2024
1 parent 3b01f3d commit 33ba0ea
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 34 deletions.
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<title>egui-gizmo demo</title><style>body,canvas,html{margin:0;padding:0;width:100%;height:100%;overflow:hidden;position:absolute;background:black;z-index:0}</style><link as=fetch crossorigin href=./egui-gizmo-demo_bg.wasm integrity=sha384-gv6VcHXKqcxVYZYIqXT7UvGlz4KIJW8XYb86PzGWDF_ReerXp3F4xWbLBnPfx9Qm rel=preload type=application/wasm><link crossorigin href=./egui-gizmo-demo.js integrity=sha384-GrLsch8RCrPYLRQ6BZugJpDQ5UTLwUKl2HmRbaV2V-Eu7UTw9tr7DVUPpyPgePTU rel=modulepreload></head><body oncontextmenu="return false;"><script type=module>import a,*as b from"./egui-gizmo-demo.js";a(`./egui-gizmo-demo_bg.wasm`);window.wasmBindings=b</script></body></html>
<title>egui-gizmo demo</title><style>body,canvas,html{margin:0;padding:0;width:100%;height:100%;overflow:hidden;position:absolute;background:black;z-index:0}</style><link as=fetch crossorigin href=./egui-gizmo-demo_bg.wasm integrity=sha384-CoZgxOfGNgMidVxJZBj4y6IuxI1sBygjdJhVTE1STBBbuI7MwBz-Db2lvBAIZn27 rel=preload type=application/wasm><link crossorigin href=./egui-gizmo-demo.js integrity=sha384-pWDv33DuDrrkUHe2XL4zSWeCkMKnGRdKnj4ZFZtrJ9rHUbhYIC37AQfABk_zoZ-C rel=modulepreload></head><body oncontextmenu="return false;"><script type=module>import a,*as b from"./egui-gizmo-demo.js";a(`./egui-gizmo-demo_bg.wasm`);window.wasmBindings=b</script></body></html>
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,11 @@ impl GizmoConfig {
self.view_matrix.row(2).xyz()
}

/// Up vector of the view camera
pub(crate) fn view_up(&self) -> DVec3 {
self.view_matrix.row(1).xyz()
}

/// Right vector of the view camera
pub(crate) fn view_right(&self) -> DVec3 {
self.view_matrix.row(0).xyz()
Expand Down
2 changes: 1 addition & 1 deletion src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub fn round_to_interval(val: f64, interval: f64) -> f64 {
pub fn world_to_screen(viewport: Rect, mvp: DMat4, pos: DVec3) -> Option<Pos2> {
let mut pos = mvp * DVec4::from((pos, 1.0));

if pos.w < 0.0 {
if pos.w < 1e-10 {
return None;
}

Expand Down
24 changes: 21 additions & 3 deletions src/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,26 @@ impl Painter3d {
end_angle: f64,
stroke: impl Into<Stroke>,
) -> ShapeIdx {
let angle = end_angle - start_angle;
let step_count = steps(angle);
let mut closed = false;
let mut angle = end_angle - start_angle;

if angle <= -TAU {
angle = -TAU;
closed = true;
} else if angle >= TAU {
angle = TAU;
closed = true;
}

let mut step_count = steps(angle);
let mut points = Vec::with_capacity(step_count);

let step_size = angle / (step_count - 1) as f64;

if closed {
step_count -= 1;
}

for step in (0..step_count).map(|i| step_size * i as f64) {
let x = f64::cos(start_angle + step) * radius;
let z = f64::sin(start_angle + step) * radius;
Expand All @@ -48,7 +62,11 @@ impl Painter3d {
.filter_map(|point| self.vec3_to_pos2(point))
.collect::<Vec<_>>();

self.painter.add(Shape::line(points, stroke))
self.painter.add(if closed {
Shape::closed_line(points, stroke)
} else {
Shape::line(points, stroke)
})
}

pub fn circle(&self, radius: f64, stroke: impl Into<Stroke>) -> ShapeIdx {
Expand Down
25 changes: 17 additions & 8 deletions src/subgizmo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ mod rotation;
mod scale;
mod translation;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum TransformKind {
Axis,
Plane,
}

pub(crate) trait SubGizmoState: Default + Copy + Clone + Send + Sync + 'static {}
impl<T> WidgetData for T where T: SubGizmoState {}

Expand All @@ -35,10 +41,15 @@ pub(crate) struct SubGizmoConfig<T> {
}

pub(crate) trait SubGizmoBase: 'static {
/// Identifier for this subgizmo. It should be unique across all subgizmos.
fn id(&self) -> Id;
/// Sets whether this subgizmo is currently focused
fn set_focused(&mut self, focused: bool);
/// Sets whether this subgizmo is currently active
fn set_active(&mut self, active: bool);
/// Returns true if this subgizmo is currently focused
fn is_focused(&self) -> bool;
/// Returns true if this subgizmo is currently active
fn is_active(&self) -> bool;
}

Expand All @@ -65,8 +76,12 @@ impl<T: 'static> SubGizmoBase for SubGizmoConfig<T> {
}

pub(crate) trait SubGizmo: SubGizmoBase {
/// Pick the subgizmo based on pointer ray. If it is close enough to
/// the mouse pointer, distance from camera to the subgizmo is returned.
fn pick(&mut self, ui: &Ui, ray: Ray) -> Option<f64>;
/// Update the subgizmo based on pointer ray and interaction.
fn update(&mut self, ui: &Ui, ray: Ray) -> Option<GizmoResult>;
/// Draw the subgizmo
fn draw(&self, ui: &Ui);
}

Expand All @@ -78,13 +93,13 @@ where
id_source: impl Hash,
config: GizmoConfig,
direction: GizmoDirection,
kind: TransformKind,
transform_kind: TransformKind,
) -> Self {
Self {
id: Id::new(id_source),
config,
direction,
transform_kind: kind,
transform_kind,
focused: false,
active: false,
opacity: 0.0,
Expand Down Expand Up @@ -144,9 +159,3 @@ where
state.save(ui.ctx(), self.id);
}
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum TransformKind {
Axis,
Plane,
}
1 change: 1 addition & 0 deletions src/subgizmo/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::painter::Painter3d;
use crate::subgizmo::{SubGizmoConfig, SubGizmoState};
use crate::{GizmoDirection, GizmoMode, Ray};
use glam::{DMat4, DVec3};

const ARROW_FADE: RangeInclusive<f64> = 0.95..=0.99;
const PLANE_FADE: RangeInclusive<f64> = 0.70..=0.86;

Expand Down
50 changes: 29 additions & 21 deletions src/subgizmo/rotation.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::f64::consts::{FRAC_PI_2, PI, TAU};

use egui::Ui;
use glam::{DMat4, DQuat, DVec2, DVec3};
use glam::{DMat3, DMat4, DQuat, DVec2, DVec3};

use crate::math::{ray_to_plane_origin, rotation_align, round_to_interval, world_to_screen};
use crate::painter::Painter3d;
Expand Down Expand Up @@ -154,38 +154,46 @@ fn arc_angle(subgizmo: &SubGizmoConfig<RotationState>) -> f64 {
let min_dot = 0.990;
let max_dot = 0.995;

f64::min(1.0, f64::max(0.0, dot - min_dot) / (max_dot - min_dot)) * FRAC_PI_2 + FRAC_PI_2
let mut angle =
f64::min(1.0, f64::max(0.0, dot - min_dot) / (max_dot - min_dot)) * FRAC_PI_2 + FRAC_PI_2;
if (angle - PI).abs() < 1e-2 {
angle = PI;
}
angle
}

/// Calculates a matrix used when rendering the rotation axis.
fn rotation_matrix(subgizmo: &SubGizmoConfig<RotationState>) -> DMat4 {
if subgizmo.direction == GizmoDirection::Screen {
let forward = subgizmo.config.view_forward();
let right = subgizmo.config.view_right();
let up = subgizmo.config.view_up();

let rotation = DQuat::from_mat3(&DMat3::from_cols(up, -forward, -right));

return DMat4::from_rotation_translation(rotation, subgizmo.config.translation);
}

// First rotate towards the gizmo normal
let local_normal = subgizmo.local_normal();
let rotation = rotation_align(DVec3::Y, local_normal);
let mut rotation = DQuat::from_mat3(&rotation);
let config = subgizmo.config;

// TODO optimize this. Use same code for all axes if possible.

if subgizmo.direction != GizmoDirection::Screen {
if config.local_space() {
rotation = config.rotation * rotation;
}

let tangent = tangent(subgizmo);
let normal = subgizmo.normal();
let mut forward = config.view_forward();
if config.left_handed {
forward *= -1.0;
}
let angle = f64::atan2(tangent.cross(forward).dot(normal), tangent.dot(forward));
if config.local_space() {
rotation = config.rotation * rotation;
}

// Rotate towards the camera, along the rotation axis.
rotation = DQuat::from_axis_angle(normal, angle) * rotation;
} else {
let angle = f64::atan2(local_normal.x, local_normal.z) + FRAC_PI_2;
rotation = DQuat::from_axis_angle(local_normal, angle) * rotation;
let tangent = tangent(subgizmo);
let normal = subgizmo.normal();
let mut forward = config.view_forward();
if config.left_handed {
forward *= -1.0;
}
let angle = f64::atan2(tangent.cross(forward).dot(normal), tangent.dot(forward));

// Rotate towards the camera, along the rotation axis.
rotation = DQuat::from_axis_angle(normal, angle) * rotation;

DMat4::from_rotation_translation(rotation, config.translation)
}
Expand Down

0 comments on commit 33ba0ea

Please sign in to comment.