Switch to Z-up coordinate system: WASD on XY ground plane, E/Q for vertical

This commit is contained in:
Greg Shuflin
2026-02-14 01:31:23 -08:00
parent 78b6c5f07b
commit 1614722095
7 changed files with 25 additions and 25 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ Single-crate Rust app (`wgpu-demo`) that runs both natively and in-browser via W
- `scene.rs``SceneObject` placement with animated rotation. `build_scene()` generates all meshes and objects
- `input.rs``HashSet<KeyCode>` key tracking
**Movement model:** Axis-aligned (not camera-relative). W/S = Y, A/D = X, Q/E = Z. Mouse look controls yaw/pitch.
**Movement model:** Z-up coordinate system. Axis-aligned (not camera-relative). W/S = Y (forward/back), A/D = X (left/right), E/Q = Z (up/down). Mouse look controls yaw/pitch.
**WASM-specific:** `web-time` replaces `std::time::Instant`. HUD overlays (help, coordinate bar) are HTML elements in `index.html`, manipulated via `web-sys` DOM calls from Rust. Pointer lock via winit's cursor grab API.
+3 -3
View File
@@ -130,7 +130,7 @@
<div class="help-box">
<h2>Controls</h2>
<div class="help-row">
<span class="help-action">Up / Down (Y)</span>
<span class="help-action">Forward / Back (Y)</span>
<span class="help-keys"><kbd>W</kbd><kbd>S</kbd></span>
</div>
<div class="help-row">
@@ -138,8 +138,8 @@
<span class="help-keys"><kbd>A</kbd><kbd>D</kbd></span>
</div>
<div class="help-row">
<span class="help-action">Forward / Back (Z)</span>
<span class="help-keys"><kbd>Q</kbd><kbd>E</kbd></span>
<span class="help-action">Up / Down (Z)</span>
<span class="help-keys"><kbd>E</kbd><kbd>Q</kbd></span>
</div>
<div class="help-row">
<span class="help-action">Look</span>
+6 -6
View File
@@ -25,29 +25,29 @@ impl Camera {
}
}
/// Forward direction on the XZ plane (for WASD movement)
/// Forward direction on the XY plane (for WASD movement)
pub fn forward(&self) -> Vec3 {
Vec3::new(self.yaw.sin(), 0.0, -self.yaw.cos()).normalize()
Vec3::new(self.yaw.cos(), self.yaw.sin(), 0.0).normalize()
}
/// Right direction on the XZ plane
/// Right direction on the XY plane
pub fn right(&self) -> Vec3 {
Vec3::new(self.yaw.cos(), 0.0, self.yaw.sin()).normalize()
Vec3::new(self.yaw.sin(), -self.yaw.cos(), 0.0).normalize()
}
/// The actual look direction (including pitch) for the view matrix
fn look_dir(&self) -> Vec3 {
Vec3::new(
self.pitch.cos() * self.yaw.cos(),
self.pitch.cos() * self.yaw.sin(),
self.pitch.sin(),
-self.pitch.cos() * self.yaw.cos(),
)
.normalize()
}
pub fn view_matrix(&self) -> Mat4 {
let target = self.position + self.look_dir();
Mat4::look_at_rh(self.position, target, Vec3::Y)
Mat4::look_at_rh(self.position, target, Vec3::Z)
}
pub fn projection_matrix(&self) -> Mat4 {
+5 -5
View File
@@ -42,8 +42,8 @@ impl App {
let pos = Vec3::new(10.0, -10.0, 10.0);
let to_origin = (Vec3::ZERO - pos).normalize();
let mut cam = Camera::new(pos, 1.0);
cam.yaw = to_origin.x.atan2(-to_origin.z);
cam.pitch = to_origin.y.asin();
cam.yaw = to_origin.y.atan2(to_origin.x);
cam.pitch = to_origin.z.asin();
cam
},
input: InputState::new(),
@@ -69,12 +69,12 @@ impl App {
if self.input.is_pressed(KeyCode::KeyD) {
self.camera.position.x += speed;
}
if self.input.is_pressed(KeyCode::KeyQ) {
self.camera.position.z -= speed;
}
if self.input.is_pressed(KeyCode::KeyE) {
self.camera.position.z += speed;
}
if self.input.is_pressed(KeyCode::KeyQ) {
self.camera.position.z -= speed;
}
}
fn try_lock_cursor(&self, window: &Window) {
+6 -6
View File
@@ -214,10 +214,10 @@ impl Mesh {
pub fn icosphere_with_spike(subdivisions: u32, color: [f32; 3], spike_color: [f32; 3]) -> Self {
let mut mesh = Self::icosphere(subdivisions, color);
// Add a cone spike pointing in +Y direction
// Add a cone spike pointing in +Z direction
let spike_base_radius = 0.3;
let spike_base_y = 0.9;
let spike_tip = Vec3::new(0.0, 1.8, 0.0);
let spike_base_z = 0.9;
let spike_tip = Vec3::new(0.0, 0.0, 1.8);
let segments = 12;
// Generate base ring points
@@ -226,8 +226,8 @@ impl Mesh {
let angle = (i as f32 / segments as f32) * std::f32::consts::TAU;
Vec3::new(
angle.cos() * spike_base_radius,
spike_base_y,
angle.sin() * spike_base_radius,
spike_base_z,
)
})
.collect();
@@ -255,8 +255,8 @@ impl Mesh {
}
// Base cap (so the spike isn't open at the bottom)
let base_center = Vec3::new(0.0, spike_base_y, 0.0);
let base_normal = Vec3::NEG_Y;
let base_center = Vec3::new(0.0, 0.0, spike_base_z);
let base_normal = Vec3::NEG_Z;
for i in 0..segments {
let a = base_points[(i + 1) % segments];
let b = base_points[i];
+3 -3
View File
@@ -80,7 +80,7 @@ pub fn build_scene() -> (Vec<Mesh>, Vec<SceneObject>) {
objects.push(SceneObject {
mesh_index: spiked_ball_index,
position: Vec3::ZERO,
rotation_axis: Vec3::new(0.3, 1.0, 0.1).normalize(),
rotation_axis: Vec3::new(0.3, 0.1, 1.0).normalize(),
rotation_speed: 0.5,
scale: 0.8,
base_rotation: 0.0,
@@ -93,8 +93,8 @@ pub fn build_scene() -> (Vec<Mesh>, Vec<SceneObject>) {
let position = Vec3::new(
rng.gen_range(-spread..spread),
rng.gen_range(-spread * 0.5..spread * 0.5),
rng.gen_range(-spread..spread),
rng.gen_range(-spread * 0.5..spread * 0.5),
);
let rotation_axis = Vec3::new(
@@ -102,7 +102,7 @@ pub fn build_scene() -> (Vec<Mesh>, Vec<SceneObject>) {
rng.gen_range(-1.0..1.0),
rng.gen_range(-1.0..1.0),
)
.normalize_or(Vec3::Y);
.normalize_or(Vec3::Z);
let rotation_speed = rng.gen_range(0.2..1.5) * if rng.gen_bool(0.5) { 1.0 } else { -1.0 };
let scale = rng.gen_range(0.5..2.5);
+1 -1
View File
@@ -42,7 +42,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let normal = normalize(in.world_normal);
// Simple directional light + ambient for a flat-shaded look
let light_dir = normalize(vec3<f32>(0.4, 1.0, 0.6));
let light_dir = normalize(vec3<f32>(0.4, 0.6, 1.0));
let ambient = 0.15;
let diffuse = max(dot(normal, light_dir), 0.0);