From 0f81c1bdc8f52a6fb4bf5790e86ca04e11868c4a Mon Sep 17 00:00:00 2001 From: fredrikr79 Date: Thu, 2 Oct 2025 16:01:03 +0200 Subject: [PATCH 1/4] implement chase camera From a8c4d897bb1ee45bce94e3225f8eaea4fda02a52 Mon Sep 17 00:00:00 2001 From: fredrikr79 Date: Thu, 2 Oct 2025 16:18:34 +0200 Subject: [PATCH 2/4] create ChaseCamera struct --- gloom-rs/src/draw.rs | 42 +++++++++++++++++++++++++++++++++++++----- gloom-rs/src/main.rs | 23 ++++++++++------------- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/gloom-rs/src/draw.rs b/gloom-rs/src/draw.rs index 54fe3c2..dc39198 100644 --- a/gloom-rs/src/draw.rs +++ b/gloom-rs/src/draw.rs @@ -57,17 +57,42 @@ pub enum Nodes { SceneRoot, } +pub struct ChaseCamera { + pub position: glm::Vec3, + pub target: glm::Vec3, + pub radius: f32, // set to big number to get normal camera + pub perspective: glm::Mat4, + pitch: f32, + yaw: f32 +} + +impl ChaseCamera { + pub fn new(radius: f32, perspective: glm::Mat4) -> Self { + ChaseCamera { + position: glm::vec3(100.0, 35.0, 0.0), + target: glm::vec3(0.0, 0.0, 0.0), + radius, + perspective, + pitch: 0.0, + yaw: 0.0 + } + } +} + pub struct World { pub nodes: HashMap>>>>, - pub anim_ctxs: Vec + pub anim_ctxs: Vec, + pub camera: ChaseCamera, + pub model_transformation: glm::Mat4 } impl World { - pub fn new() -> Self { + pub fn new(radius: f32, perspective: glm::Mat4) -> Self { let meshes = Self::load_models(); let nodes = Self::setup_scene_graph(&meshes); let anim_ctxs:Vec = Vec::new(); - World { nodes, anim_ctxs } + let camera = ChaseCamera::new(radius, perspective); + World { nodes, anim_ctxs, camera, model_transformation: glm::identity() } } fn load_models() -> HashMap { @@ -147,8 +172,15 @@ impl World { return nodes; } - pub fn update(&mut self, elapsed: f32, perspective: glm::Mat4, transformation: glm::Mat4, shader: &Shader) { - let transform_thus_far = perspective * transformation; + fn view_matrix(&self) -> glm::Mat4 { + glm::look_at( + &self.camera.position, + &self.camera.target, + &glm::vec3(0.0, 1.0, 0.0) + ) + } + + pub fn update(&mut self, elapsed: f32, shader: &Shader) { let rotor_speed = 60.0; for i in 0..self.nodes[&Nodes::HeliRoot].len() { diff --git a/gloom-rs/src/main.rs b/gloom-rs/src/main.rs index c385284..52a51c5 100644 --- a/gloom-rs/src/main.rs +++ b/gloom-rs/src/main.rs @@ -91,16 +91,13 @@ fn main() { } let mut transformation: glm::Mat4; - let perspective: glm::Mat4 = glm::perspective( + let mut world = draw::World::new(100.0, glm::perspective( window_aspect_ratio, 120.0f32, 1.0f32, 1000.0f32 - ); + )); - let mut world = draw::World::new(); - - let mut camera_position: glm::Vec3 = glm::vec3(0.0, 0.0, 0.0); let translation_speed = 30.0f32; let rotation_speed = 1.5f32; @@ -159,22 +156,22 @@ fn main() { for key in keys.iter() { match key { VirtualKeyCode::W => { - camera_position += forward * trans_speed; + world.camera.position += forward * trans_speed; } VirtualKeyCode::A => { - camera_position -= right * trans_speed; + world.camera.position -= right * trans_speed; } VirtualKeyCode::S => { - camera_position -= forward * trans_speed; + world.camera.position -= forward * trans_speed; } VirtualKeyCode::D => { - camera_position += right * trans_speed; + world.camera.position += right * trans_speed; } VirtualKeyCode::Space => { - camera_position.y -= trans_speed; + world.camera.position.y -= trans_speed; } VirtualKeyCode::LShift => { - camera_position.y += trans_speed; + world.camera.position.y += trans_speed; } VirtualKeyCode::E => { world.anim_ctxs.push(AnimCTX{ @@ -219,7 +216,7 @@ fn main() { let yaw_matrix = glm::rotation(yaw, &glm::vec3(0.0, 1.0, 0.0)); let pitch_matrix = glm::rotation(pitch, &glm::vec3(1.0, 0.0, 0.0)); - let translation_matrix = glm::translation(&(camera_position)); + let translation_matrix = glm::translation(&(world.camera.position)); // apply yaw dependent pitch first. let rotation_matrix = pitch_matrix * yaw_matrix; @@ -232,7 +229,7 @@ fn main() { gl::ClearColor(40.0f32 / 256.0f32, 42.0f32 / 256.0f32, 54.0f32 / 256.0f32, 1.0); // night sky gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); - world.update(elapsed, perspective, transformation, &simple_shader); + world.update(elapsed, &simple_shader); // Display the new color buffer on the display context.swap_buffers().unwrap(); // we use "double buffering" to avoid artifacts From fc0b67cd27826648b096c05b558e3d648fe961cc Mon Sep 17 00:00:00 2001 From: fredrikr79 Date: Thu, 2 Oct 2025 16:48:57 +0200 Subject: [PATCH 3/4] implement proper chase camera --- gloom-rs/src/draw.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gloom-rs/src/draw.rs b/gloom-rs/src/draw.rs index dc39198..88072be 100644 --- a/gloom-rs/src/draw.rs +++ b/gloom-rs/src/draw.rs @@ -206,6 +206,23 @@ impl World { self.nodes.get_mut(&Nodes::HeliDoor).unwrap()[0].rotation.z = door_transform.roll; } + // camera chase after target + let camera_target = &self.nodes[&Nodes::HeliRoot][0]; + self.camera.target = camera_target.position; + let distance = glm::distance(&self.camera.position, &self.camera.target); + let direction = glm::normalize(&(self.camera.target - self.camera.position)); + if distance > self.camera.radius { + self.camera.position += direction * (distance - self.camera.radius); + } + + // update camera angles + self.camera.yaw = direction.z.atan2(direction.x) + glm::half_pi::(); + let horizontal_distance = (direction.x * direction.x + direction.z * direction.z).sqrt(); + self.camera.pitch = direction.y.atan2(horizontal_distance); + + + let transform_thus_far = self.camera.perspective * self.view_matrix(); + // Build scene graph on the fly or store scene_root separately unsafe { draw_scene(&self.nodes[&Nodes::SceneRoot][0], &transform_thus_far, glm::identity(), &shader, elapsed); From fbe6077527999063b442d8403a9a838066d3ab4c Mon Sep 17 00:00:00 2001 From: fredrikr79 Date: Thu, 2 Oct 2025 18:24:36 +0200 Subject: [PATCH 4/4] task 7c --- gloom-rs/src/draw.rs | 17 ++++++----- gloom-rs/src/main.rs | 72 +------------------------------------------- 2 files changed, 10 insertions(+), 79 deletions(-) diff --git a/gloom-rs/src/draw.rs b/gloom-rs/src/draw.rs index 88072be..52c10a3 100644 --- a/gloom-rs/src/draw.rs +++ b/gloom-rs/src/draw.rs @@ -115,7 +115,6 @@ impl World { let lunar_mesh = &meshes[&Nodes::LunarSurface]; let mut lunar_node = SceneNode::from_vao(lunar_mesh.vao_id, lunar_mesh.index_count); lunar_node.reference_point = glm::vec3(0.0, 0.0, 0.0); - lunar_node.position.y = -10.0; nodes.insert(Nodes::LunarSurface, vec![lunar_node]); nodes.insert(Nodes::HeliRoot, Vec::new()); @@ -183,14 +182,16 @@ impl World { pub fn update(&mut self, elapsed: f32, shader: &Shader) { let rotor_speed = 60.0; + // update all helicopter positions for i in 0..self.nodes[&Nodes::HeliRoot].len() { - // let iter_heli_heading = toolbox::simple_heading_animation(elapsed + i as f32 * 1.6f32); - // let heli_root = &mut self.nodes.get_mut(&Nodes::HeliRoot).unwrap()[i]; - // heli_root.position.x = iter_heli_heading.x; - // heli_root.position.z = iter_heli_heading.z; - // heli_root.rotation.z = iter_heli_heading.roll; - // heli_root.rotation.y = iter_heli_heading.yaw; - // heli_root.rotation.x = iter_heli_heading.pitch; + let iter_heli_heading = toolbox::simple_heading_animation(elapsed + i as f32 * 1.6f32); + let heli_root = &mut self.nodes.get_mut(&Nodes::HeliRoot).unwrap()[i]; + heli_root.position.x = iter_heli_heading.x; + heli_root.position.y = 15.0; + heli_root.position.z = iter_heli_heading.z; + heli_root.rotation.z = iter_heli_heading.roll; + heli_root.rotation.y = iter_heli_heading.yaw; + heli_root.rotation.x = iter_heli_heading.pitch; self.nodes.get_mut(&Nodes::HeliMainRotor).unwrap()[i].rotation.y = elapsed * rotor_speed; self.nodes.get_mut(&Nodes::HeliTailRotor).unwrap()[i].rotation.x = elapsed * rotor_speed; diff --git a/gloom-rs/src/main.rs b/gloom-rs/src/main.rs index 52a51c5..f35d7b0 100644 --- a/gloom-rs/src/main.rs +++ b/gloom-rs/src/main.rs @@ -90,20 +90,13 @@ fn main() { println!("GLSL\t: {}", util::get_gl_string(gl::SHADING_LANGUAGE_VERSION)); } - let mut transformation: glm::Mat4; - let mut world = draw::World::new(100.0, glm::perspective( + let mut world = draw::World::new(30.0, glm::perspective( window_aspect_ratio, 120.0f32, 1.0f32, 1000.0f32 )); - let translation_speed = 30.0f32; - let rotation_speed = 1.5f32; - - let mut yaw = 0.0f32; - let mut pitch = 0.0f32; - // == // Set up your shaders here // Basic usage of shader helper: @@ -131,9 +124,6 @@ fn main() { let delta_time = now.duration_since(previous_frame_time).as_secs_f32(); previous_frame_time = now; - let angle_speed = rotation_speed * delta_time; - let trans_speed: f32 = translation_speed * delta_time; - // Handle resize events if let Ok(mut new_size) = window_size.lock() { if new_size.2 { @@ -145,62 +135,10 @@ fn main() { } } - // Calculate forward vector, and right vector in x-z plane for relative yaw based translation. - let f_xz = glm::vec3(-yaw.sin(), yaw.cos(), 0.0); - let r_xz = glm::rotation2d(glm::half_pi()) * f_xz; - - let forward = glm::vec3(f_xz.x, f_xz.z, f_xz.y); - let right = glm::vec3(r_xz.x, r_xz.z, r_xz.y); // Handle keyboard input if let Ok(keys) = pressed_keys.lock() { for key in keys.iter() { match key { - VirtualKeyCode::W => { - world.camera.position += forward * trans_speed; - } - VirtualKeyCode::A => { - world.camera.position -= right * trans_speed; - } - VirtualKeyCode::S => { - world.camera.position -= forward * trans_speed; - } - VirtualKeyCode::D => { - world.camera.position += right * trans_speed; - } - VirtualKeyCode::Space => { - world.camera.position.y -= trans_speed; - } - VirtualKeyCode::LShift => { - world.camera.position.y += trans_speed; - } - VirtualKeyCode::E => { - world.anim_ctxs.push(AnimCTX{ - stime : elapsed, - rand_seed: rand::random::() - }); - } - - VirtualKeyCode::Down => { - pitch += angle_speed; - pitch = pitch.clamp( - -std::f32::consts::FRAC_PI_2, - std::f32::consts::FRAC_PI_2 - ); - } - VirtualKeyCode::Up => { - pitch -= angle_speed; - pitch = pitch.clamp( - -std::f32::consts::FRAC_PI_2, - std::f32::consts::FRAC_PI_2 - ); - } - VirtualKeyCode::Right => { - yaw += angle_speed; - } - VirtualKeyCode::Left => { - yaw -= angle_speed; - } - _ => { } } } @@ -214,14 +152,6 @@ fn main() { *delta = (0.0, 0.0); // reset when done } - let yaw_matrix = glm::rotation(yaw, &glm::vec3(0.0, 1.0, 0.0)); - let pitch_matrix = glm::rotation(pitch, &glm::vec3(1.0, 0.0, 0.0)); - let translation_matrix = glm::translation(&(world.camera.position)); - - // apply yaw dependent pitch first. - let rotation_matrix = pitch_matrix * yaw_matrix; - transformation = rotation_matrix * translation_matrix; - unsafe { simple_shader.activate();