use bevy::{ prelude::*, reflect::TypeUuid, render::{ camera::{Camera as BevyCamera, RenderTarget}, render_resource::{ AsBindGroup, Extent3d, ShaderRef, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, texture::BevyDefault, view::RenderLayers, }, sprite::{Material2d, MaterialMesh2dBundle}, }; use bevy_mod_raycast::{RayCastMethod, RayCastSource}; use crate::{pillar::UnlitPillar, player::*, PALETTE}; pub struct MyRaycastSet; #[derive(Component)] pub struct Camera { distance_to_player: f32, } pub fn spawn_camera( mut commands: Commands, asset_server: Res, mut windows: ResMut, mut meshes: ResMut>, mut post_processing_materials: ResMut>, mut images: ResMut>, ) { let window = windows.get_primary_mut().unwrap(); let size = Extent3d { width: window.physical_width(), height: window.physical_height(), ..default() }; // This is the texture that will be rendered to. let mut image = Image { texture_descriptor: TextureDescriptor { label: None, size, dimension: TextureDimension::D2, format: TextureFormat::bevy_default(), mip_level_count: 1, sample_count: 1, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT, }, ..default() }; // fill image.data with zeroes image.resize(size); let image_handle = images.add(image); // Main camera, first to render commands .spawn_bundle(Camera3dBundle { camera: BevyCamera { target: RenderTarget::Image(image_handle.clone()), ..default() }, ..default() }) .insert(Camera { distance_to_player: 100.0, }) .insert(RayCastSource::::new()); // This specifies the layer used for the post processing camera, which will be attached to the post processing camera and 2d quad. let post_processing_pass_layer = RenderLayers::layer((RenderLayers::TOTAL_LAYERS - 1) as u8); let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new( size.width as f32, size.height as f32, )))); let noise = asset_server.load("textures/noise.png"); // This material has the texture that has been rendered. let material_handle = post_processing_materials.add(PostProcessingMaterial { source_image: image_handle, noise, palette: PALETTE, }); // Post processing 2d quad, with material using the render texture done by the main camera, with a custom shader. commands .spawn_bundle(MaterialMesh2dBundle { mesh: quad_handle.into(), material: material_handle, transform: Transform { translation: Vec3::new(0.0, 0.0, 1.5), ..default() }, ..default() }) .insert(post_processing_pass_layer); // The post-processing pass camera. commands .spawn_bundle(Camera2dBundle { camera: BevyCamera { // renders after the first main camera which has default value: 0. priority: 1, ..default() }, ..Camera2dBundle::default() }) .insert(post_processing_pass_layer); } #[derive(AsBindGroup, TypeUuid, Clone)] #[uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5"] pub struct PostProcessingMaterial { /// In this example, this image will be the result of the main camera. #[texture(0)] #[sampler(1)] source_image: Handle, #[texture(2)] #[sampler(3)] noise: Handle, #[uniform(4)] palette: [Vec3; 16], } impl Material2d for PostProcessingMaterial { fn fragment_shader() -> ShaderRef { "shaders/post-processing.wgsl".into() } } pub fn camera_follow_player( player: Query<(&Transform, &Player), Without>, mut camera: Query<(&mut Transform, &Camera), Without>, ) { let player_pos = if let Some(player) = player.iter().next() { player.0.translation } else { return; }; for (mut trans, camera) in camera.iter_mut() { trans.translation = player_pos + Vec3::new(-1.0, 2.0, 1.0) * camera.distance_to_player; trans.look_at(player_pos, Vec3::Y); } } // update our `RayCastSource` with the current cursor position every frame. pub fn update_raycast_with_cursor( mut cursor: EventReader, mut query: Query<&mut RayCastSource>, ) { for mut pick_source in query.iter_mut() { // Grab the most recent cursor event if it exists: if let Some(cursor_latest) = cursor.iter().last() { // the 2.0 is bc of post-processing, which messes up the coordinates pick_source.cast_method = RayCastMethod::Screenspace(cursor_latest.position * 2.0); } } } #[derive(Default)] pub struct MouseCoords { /// The mouse coordinates on the floor, as they are pub raw: Vec3, /// The mouse coordinates, after being snapped to pillars and stuff pub processed: Vec3, } // Update our `RayCastSource` with the current cursor position every frame. pub fn update_raw_mouse_coords( query: Query<&RayCastSource>, mut coords: ResMut, ) { for pick_source in query.iter() { if let Some((_, intersection)) = pick_source.intersect_top() { coords.raw = intersection.position(); } } } /// checks for pillars and stuff pub fn update_processed_mouse_coords( query: Query<(&Transform, &UnlitPillar)>, mut coords: ResMut, ) { let mut dis = 20.0; coords.processed = coords.raw; for (trans, _) in query.iter() { let d = trans.translation.distance(coords.raw); if d < dis { coords.processed = trans.translation; dis = d; } } } pub fn update_distance_to_player(input: Res>, mut camera: Query<&mut Camera>) { let pressed = input.pressed(KeyCode::Space); for mut camera in &mut camera { let dis = if pressed { 20.0 } else { 100.0 }; camera.distance_to_player += (dis - camera.distance_to_player) / 80.0; } }