Fix wipeout rotation and block frame width

This commit is contained in:
Hakan Seven committed 2026-09-11 20:38:00 +03:00
1 parent 4a7f08f80e
commit d07e8cf7d4
4 files changed
+61 -8

No files matched your search

+44 -6
View File
@@ -722,14 +722,22 @@ impl PropertyEditable for Wipeout {
let u = glam::DVec3::new(self.u_vector.x, self.u_vector.y, self.u_vector.z);
let v = glam::DVec3::new(self.v_vector.x, self.v_vector.y, self.v_vector.z);
let normal = u.cross(v).normalize_or_zero();
if normal.length_squared() <= 1e-18 {
if normal.length_squared() <= 1e-18 || normal.z.abs() <= 1e-12 {
return;
}
let current_radians = u.y.atan2(u.x);
let rotation = glam::DQuat::from_axis_angle(
normal,
target_degrees.to_radians() - current_radians,
);
let target_radians = target_degrees.to_radians();
let horizontal = glam::DVec3::new(target_radians.cos(), target_radians.sin(), 0.0);
let target = (normal.z * horizontal - normal.dot(horizontal) * glam::DVec3::Z)
* normal.z.signum();
let direction = u.normalize_or_zero();
let target = target.normalize_or_zero();
if direction == glam::DVec3::ZERO || target == glam::DVec3::ZERO {
return;
}
let angle = normal
.dot(direction.cross(target))
.atan2(direction.dot(target));
let rotation = glam::DQuat::from_axis_angle(normal, angle);
let u = rotation * u;
let v = rotation * v;
self.u_vector = acadrust::types::Vector3::new(u.x, u.y, u.z);
@@ -786,3 +794,33 @@ impl Transformable for Wipeout {
});
}
}
#[cfg(test)]
mod wipeout_property_tests {
use super::*;
#[test]
fn rotation_reaches_the_requested_angle_for_mirrored_and_tilted_frames() {
for (u, v, target) in [
([1.0, 0.0, 0.0], [0.0, -1.0, 0.0], 90.0),
([1.0, 0.0, 1.0], [0.0, 1.0, 0.0], 45.0),
] {
let mut wipeout = Wipeout::new();
wipeout.u_vector = acadrust::types::Vector3::new(u[0], u[1], u[2]);
wipeout.v_vector = acadrust::types::Vector3::new(v[0], v[1], v[2]);
let lengths = (wipeout.u_vector.length(), wipeout.v_vector.length());
wipeout.apply_geom_prop("wo_rotation", &target.to_string());
let angle = wipeout
.u_vector
.y
.atan2(wipeout.u_vector.x)
.to_degrees()
.rem_euclid(360.0);
assert!((angle - target).abs() < 1e-9, "target={target} actual={angle}");
assert!((wipeout.u_vector.length() - lengths.0).abs() < 1e-12);
assert!((wipeout.v_vector.length() - lengths.1).abs() < 1e-12);
}
}
}
+13
View File
@@ -1495,6 +1495,19 @@ mod block_wire_storage_tests {
assert_eq!(names(&packed), names(&storage));
}
#[test]
fn both_block_wire_shaders_honor_fixed_screen_width() {
for source in [
include_str!("../../shaders/block_wire.wgsl"),
include_str!("../../shaders/block_wire_storage.wgsl"),
] {
assert!(source.contains("if world_hw < 0.0"));
assert!(source.contains(
"resolve_hw(0.0, wire_const.world_half_width, wire_const.half_width)"
));
}
}
#[test]
fn wire_shader_validates_with_naga() {
let source = include_str!("../../shaders/wire.wgsl");
+2 -1
View File
@@ -69,6 +69,7 @@ fn resolve_hw(taper_ratio: f32, world_hw: f32, px_hw: f32) -> f32 {
if world_hw > 0.0 {
return max(world_hw / u.world_per_pixel, 0.5);
}
if world_hw < 0.0 { return max(-world_hw, 0.5); }
var display_hw = max(px_hw * u.lineweight_scale, 0.5);
if u.lineweight_scale < 0.0 {
let scale = -u.lineweight_scale;
@@ -185,7 +186,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
let perpendicular = vec2<f32>(-direction.y, direction.x);
let clip_position = mix(clip_a, clip_b, which_end);
let half_width = resolve_hw(0.0, 0.0, wire_const.half_width);
let half_width = resolve_hw(0.0, wire_const.world_half_width, wire_const.half_width);
let extension = which_end * 2.0 - 1.0;
let offset_px = perpendicular * half_width * side
+ direction * half_width * extension;
+2 -1
View File
@@ -93,6 +93,7 @@ fn resolve_hw(taper_ratio: f32, world_hw: f32, px_hw: f32) -> f32 {
if world_hw > 0.0 {
return max(world_hw / u.world_per_pixel, 0.5);
}
if world_hw < 0.0 { return max(-world_hw, 0.5); }
var display_hw = max(px_hw * u.lineweight_scale, 0.5);
if u.lineweight_scale < 0.0 {
let scale = -u.lineweight_scale;
@@ -216,7 +217,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
let perpendicular = vec2<f32>(-direction.y, direction.x);
let clip_position = mix(clip_a, clip_b, which_end);
let half_width = resolve_hw(0.0, 0.0, wire_const.half_width);
let half_width = resolve_hw(0.0, wire_const.world_half_width, wire_const.half_width);
let extension = which_end * 2.0 - 1.0;
let offset_px = perpendicular * half_width * side
+ direction * half_width * extension;