use crate::{game::camera::GameCamera, terrain2d::*, util::Vector2I}; use bevy::{input::mouse::MouseWheel, prelude::*, render::camera::RenderTarget}; use bevy_prototype_debug_lines::DebugLines; pub struct TerrainDebugPlugin; impl Plugin for TerrainDebugPlugin { fn build(&self, app: &mut App) { app.insert_resource(TerrainBrush2D::default()) .add_system(debug_painter) .add_system_to_stage(TerrainStages::EventHandler, dirty_rect_visualizer); } } #[derive(Resource)] struct TerrainBrush2D { pub radius: i32, pub tile: TexelID, } impl Default for TerrainBrush2D { fn default() -> Self { TerrainBrush2D { radius: 7, tile: 3 } } } // REM: Dirty and hopefully temporary fn debug_painter( mut terrain: ResMut, mut debug_draw: ResMut, mut brush: ResMut, windows: Res, mouse_input: Res>, key_input: Res>, mut mouse_wheel: EventReader, camera_query: Query<(&Camera, &GlobalTransform), With>, ) { let allow_painting = key_input.pressed(KeyCode::LControl); // Change brush for event in mouse_wheel.iter() { if allow_painting { brush.radius = (brush.radius + event.y.round() as i32).clamp(1, 128); } } if !allow_painting { return; } // https://bevy-cheatbook.github.io/cookbook/cursor2world.html#2d-games // get the camera info and transform // assuming there is exactly one main camera entity, so query::single() is OK let (camera, camera_transform) = camera_query.single(); // get the window that the camera is displaying to (or the primary window) let window = if let RenderTarget::Window(id) = camera.target { windows.get(id).unwrap() } else { windows.get_primary().unwrap() }; // check if the cursor is inside the window and get its position let world_pos = if let Some(screen_pos) = window.cursor_position() { // get the size of the window let window_size = Vec2::new(window.width() as f32, window.height() as f32); // convert screen position [0..resolution] to ndc [-1..1] (gpu coordinates) let ndc = (screen_pos / window_size) * 2.0 - Vec2::ONE; // matrix for undoing the projection and camera transform let ndc_to_world = camera_transform.compute_matrix() * camera.projection_matrix().inverse(); // use it to convert ndc to world-space coordinates let world_pos = ndc_to_world.project_point3(ndc.extend(-1.0)); // reduce it to a 2D value world_pos.truncate() } else { return; }; if key_input.just_pressed(KeyCode::Key1) { brush.tile = 1; } if key_input.just_pressed(KeyCode::Key2) { brush.tile = 2; } if key_input.just_pressed(KeyCode::Key3) { brush.tile = 3; } if key_input.just_pressed(KeyCode::Key4) { brush.tile = 4; } if key_input.just_pressed(KeyCode::Key5) { brush.tile = 5; } if key_input.just_pressed(KeyCode::Key6) { brush.tile = 6; } let origin = Vector2I::from(world_pos); let radius = brush.radius; let id = match ( mouse_input.pressed(MouseButton::Left), mouse_input.pressed(MouseButton::Right), ) { (true, false) => brush.tile, (_, _) => 0, }; let color = TexelBehaviour2D::from_id(&brush.tile) .map_or(Color::rgba(0.0, 0.0, 0.0, 0.0), |tb| tb.color); for y in origin.y - (radius - 1)..origin.y + radius { for x in origin.x - (radius - 1)..origin.x + radius { let dx = (x - origin.x).abs(); let dy = (y - origin.y).abs(); if dx * dx + dy * dy <= (radius - 1) * (radius - 1) { let pos: Vector2I = Vector2I { x, y }; debug_draw.line_colored( Vec3::from(pos) + Vec3::new(0.45, 0.45, 1.0), Vec3::from(pos) + Vec3::new(0.55, 0.55, 1.0), 0.0, color, ); if mouse_input.pressed(MouseButton::Left) || mouse_input.pressed(MouseButton::Right) { // 6 is special if id == 6 { terrain.mark_dirty(&pos) } else { terrain.set_texel(&pos, id, None) } } } } } } /** Visualize dirty rects */ fn dirty_rect_visualizer(terrain: Res, mut debug_draw: ResMut) { for (chunk_index, chunk) in terrain.chunk_iter() { let rect = if let Some(rect) = chunk.dirty_rect { rect } else { continue; }; let color = Color::RED; let points = vec![ Vec3::new(rect.min.x as f32, rect.min.y as f32, 0.0), Vec3::new((rect.max.x + 1) as f32, rect.min.y as f32, 0.0), Vec3::new((rect.max.x + 1) as f32, (rect.max.y + 1) as f32, 0.0), Vec3::new(rect.min.x as f32, (rect.max.y + 1) as f32, 0.0), ]; for i in 0..points.len() { let offset = Vec3::from(chunk_index_to_global(chunk_index)); debug_draw.line_colored( offset + points[i], offset + points[(i + 1) % points.len()], 0.0, color, ); } } }