diff --git a/shaders/simple.vert b/shaders/simple.vert index 2a10ade..7eb80e9 100644 --- a/shaders/simple.vert +++ b/shaders/simple.vert @@ -3,17 +3,9 @@ in layout(location=0) vec3 position; in layout(location=1) vec4 aColor; out vec4 vColor; - -mat4 transform; +layout(location = 0) uniform mat4 transform; void main() { - transform = mat4( - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 - ); - gl_Position = transform * vec4(position, 1.0f); vColor = aColor; } diff --git a/src/main.rs b/src/main.rs index 7df1d18..d739398 100644 --- a/src/main.rs +++ b/src/main.rs @@ -163,6 +163,7 @@ fn main() { gl::Disable(gl::MULTISAMPLE); gl::Enable(gl::BLEND); gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); + gl::Enable(gl::PROGRAM_POINT_SIZE); gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS); gl::DebugMessageCallback(Some(util::debug_callback), ptr::null()); @@ -220,6 +221,40 @@ fn main() { let my_vao = unsafe { create_vao(&vertices, &colors, &indices) }; + let billboard_vertices = vec![ + -0.5, -0.5, 0.0, + -0.5, 0.5, 0.0, + 0.5, 0.5, 0.0, + 0.5, -0.5, 0.0, + ]; + let billboard_indices = vec![ + 0, 1, 2, + 0, 2, 3, + ]; + let billboard_colors = vec![ + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + ]; + let billboard_vao = unsafe { create_vao(&billboard_vertices, &billboard_colors, &billboard_indices) }; + + // Generate a grid of point‐particles + let grid_size = 200; + let mut particle_vertices = Vec::with_capacity(grid_size * grid_size * 3); + let mut particle_colors = Vec::with_capacity(grid_size * grid_size * 4); + for i in 0..grid_size { + for j in 0..grid_size { + let x = (i as f32 / grid_size as f32 - 0.5) * 20.0; + let y = (j as f32 / grid_size as f32 - 0.5) * 20.0; + let z = -5.0; + particle_vertices.extend_from_slice(&[x, y, z]); + particle_colors.extend_from_slice(&[1.0, 1.0, 1.0, 1.0]); + } + } + let particle_vao = unsafe { create_vao(&particle_vertices, &particle_colors, &Vec::new()) }; + let particle_count = (grid_size * grid_size) as i32; + // == // Set up your shaders here // Basic usage of shader helper: @@ -236,10 +271,17 @@ fn main() { .attach_file("./shaders/simple.frag") .link() }; + // get uniform location for the matrix (named `transform` in your vertex shader) + let transform_loc = unsafe { simple_shader.get_uniform_location("transform") }; // Used to demonstrate keyboard handling for exercise 2. let mut _arbitrary_number = 0.0; // feel free to remove + // camera position (x, y, z) and orientation (yaw, pitch) + let mut cam_pos: glm::Vec3 = glm::vec3(0.0, 0.0, 0.0); + let mut cam_yaw: f32 = 0.0; + let mut cam_pitch: f32 = 0.0; + // The main rendering loop let first_frame_time = std::time::Instant::now(); let mut previous_frame_time = first_frame_time; @@ -263,23 +305,38 @@ fn main() { } } - // Handle keyboard input + // Handle keyboard input for camera movement and rotation if let Ok(keys) = pressed_keys.lock() { + // movement speed + let speed = 2.5 * delta_time; + // camera direction vectors based on yaw & pitch + let front = glm::vec3( + cam_yaw.sin() * cam_pitch.cos(), + cam_pitch.sin(), + -cam_yaw.cos() * cam_pitch.cos(), + ); + let right = glm::normalize(&glm::cross(&front, &glm::vec3(0.0, 1.0, 0.0))); + let up_vec = glm::normalize(&glm::cross(&right, &front)); for key in keys.iter() { match key { - // The `VirtualKeyCode` enum is defined here: - // https://docs.rs/winit/0.25.0/winit/event/enum.VirtualKeyCode.html - VirtualKeyCode::A => { - _arbitrary_number += delta_time; - } - VirtualKeyCode::D => { - _arbitrary_number -= delta_time; - } - - // default handler: + // Move relative to camera orientation + VirtualKeyCode::W => { cam_pos += front * speed; } + VirtualKeyCode::S => { cam_pos -= front * speed; } + VirtualKeyCode::A => { cam_pos -= right * speed; } + VirtualKeyCode::D => { cam_pos += right * speed; } + VirtualKeyCode::Space => { cam_pos += up_vec * speed; } + VirtualKeyCode::LShift => { cam_pos -= up_vec * speed; } + // Rotation: adjust yaw and pitch + VirtualKeyCode::Left => { cam_yaw -= delta_time; } + VirtualKeyCode::Right => { cam_yaw += delta_time; } + VirtualKeyCode::Up => { cam_pitch += delta_time; } + VirtualKeyCode::Down => { cam_pitch -= delta_time; } _ => {} } } + // clamp pitch to avoid flipping + let pitch_limit = std::f32::consts::FRAC_PI_2 - 0.01; + cam_pitch = cam_pitch.clamp(-pitch_limit, pitch_limit); } // Handle mouse movement. delta contains the x and y movement of the mouse since last frame in pixels if let Ok(mut delta) = mouse_delta.lock() { @@ -298,6 +355,35 @@ fn main() { // == // Issue the necessary gl:: commands to draw your scene here simple_shader.activate(); + // build Model matrix (start from identity) + let mut model: glm::Mat4 = glm::identity(); + // cycle through each transform parameter (a,b,c,d,e,f) over time + let cycle_duration = 2.0; + let idx = ((elapsed / cycle_duration).floor() as usize) % 6; + let v = elapsed.sin(); + match idx { + 0 => model[(0,0)] = v, // a + 1 => model[(0,1)] = v, // b + 2 => model[(0,3)] = v, // c + 3 => model[(1,0)] = v, // d + 4 => model[(1,1)] = v, // e + 5 => model[(1,3)] = v, // f + _ => {} + } + // model translation to initial view distance + let translation_model: glm::Mat4 = glm::translation(&glm::vec3(0.0, 0.0, -5.0)); + let model = translation_model * model; + // build View matrix: translate and rotate world relative to camera + let view_translation: glm::Mat4 = glm::translation(&(-cam_pos)); + let view_rot_y: glm::Mat4 = glm::rotation(-cam_yaw, &glm::vec3(0.0, 1.0, 0.0)); + let view_rot_x: glm::Mat4 = glm::rotation(-cam_pitch, &glm::vec3(1.0, 0.0, 0.0)); + let view: glm::Mat4 = view_rot_x * view_rot_y * view_translation; + // build Projection matrix + let projection: glm::Mat4 = + glm::perspective(window_aspect_ratio, std::f32::consts::PI / 4.0, 1.0, 100.0); + // final transform: Projection * View * Model + let transform: glm::Mat4 = projection * view * model; + gl::UniformMatrix4fv(transform_loc, 1, gl::FALSE, transform.as_ptr()); gl::BindVertexArray(my_vao); gl::DrawElements( gl::TRIANGLES, @@ -305,6 +391,27 @@ fn main() { gl::UNSIGNED_INT, std::ptr::null(), ); + + // Draw point‐particles + let transform_particles = projection * view * glm::identity(); + gl::UniformMatrix4fv(transform_loc, 1, gl::FALSE, transform_particles.as_ptr()); + gl::BindVertexArray(particle_vao); + gl::DrawArrays(gl::POINTS, 0, particle_count); + + // Draw billboard (camera-facing quad) + let translation_bill = glm::translation(&glm::vec3(0.0, 0.0, -2.0)); + let inv_rot_y = glm::rotation(cam_yaw, &glm::vec3(0.0, 1.0, 0.0)); + let inv_rot_x = glm::rotation(cam_pitch, &glm::vec3(1.0, 0.0, 0.0)); + let model_billboard = translation_bill * inv_rot_y * inv_rot_x; + let transform_bill = projection * view * model_billboard; + gl::UniformMatrix4fv(transform_loc, 1, gl::FALSE, transform_bill.as_ptr()); + gl::BindVertexArray(billboard_vao); + gl::DrawElements( + gl::TRIANGLES, + billboard_indices.len() as i32, + gl::UNSIGNED_INT, + std::ptr::null(), + ); } // Display the new color buffer on the display