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 09350c6 commit 3c2cfd9
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 25 deletions.
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
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 3c2cfd9

Please sign in to comment.