gloss_renderer/
selector.rs

1use crate::{
2    components::{CamController, Name, VisOutline},
3    plugin_manager::{plugins::InternalPlugin, GpuSystem, RunnerState},
4    scene::Scene,
5    viewer::GpuResources,
6};
7
8pub struct Selector {
9    pub current_selected: String,
10}
11
12#[derive(Clone)]
13pub struct SelectorPlugin {
14    pub autorun: bool,
15}
16
17impl SelectorPlugin {
18    pub fn new(autorun: bool) -> Self {
19        Self { autorun }
20    }
21}
22
23impl InternalPlugin for SelectorPlugin {
24    fn autorun(&self) -> bool {
25        self.autorun
26    }
27
28    fn gpu_systems(&self) -> Vec<GpuSystem> {
29        vec![GpuSystem::new(handle_selection_click).with_name("selector_gpu_system")]
30    }
31}
32
33fn handle_selection_click(scene: &mut Scene, runner: &mut RunnerState, gpu_res: &GpuResources) {
34    // Early return if LMB is not clicked
35    if !scene.get_current_cam().unwrap().is_click(scene) {
36        return;
37    }
38
39    // Get the cursor position if it exists, otherwise return
40    #[allow(clippy::cast_possible_truncation)]
41    #[allow(clippy::cast_sign_loss)]
42    let cursor_pos = scene
43        .get_current_cam()
44        .and_then(|camera| scene.get_comp::<&CamController>(&camera.entity).ok())
45        .and_then(|cam_control| cam_control.cursor_position)
46        .map(|pos| (pos.x as u32, pos.y as u32));
47
48    let Some((x, y)) = cursor_pos else { return };
49
50    let gpu = &gpu_res.gpu;
51
52    // Get the entity id texture from the renderer
53    let entity_id_texture = gpu_res.renderer.entity_id_buffer();
54    // Scale as per the scale factor
55    let scaled_x = x / entity_id_texture.tex_params.scale_factor;
56    let scaled_y = y / entity_id_texture.tex_params.scale_factor;
57
58    // We only need the pixel at selection so we dont really need to download the whole thing
59    let single_pixel_img =
60        pollster::block_on(entity_id_texture.download_pixel_to_cpu(gpu.device(), gpu.queue(), wgpu::TextureAspect::All, scaled_x, scaled_y));
61    let entity_id = single_pixel_img.as_bytes()[0];
62
63    // Switch off selection for previous entity using the name in the selector
64    // Always do this, every click regardless of where should switch off the previous selection
65    if let Ok(selector) = scene.get_resource::<&mut Selector>() {
66        if let Some(prev_entity) = scene.get_entity_with_name(&selector.current_selected) {
67            if let Ok(mut vis_outline) = scene.world.get::<&mut VisOutline>(prev_entity) {
68                vis_outline.show_outline = false;
69            }
70        }
71    }
72    let _ = scene.remove_resource::<Selector>();
73
74    // For pixels with no entity, we get 0, dont do anything in that case.
75    // If entity_id is not 0, we can look up the entity in the scene to select
76    if entity_id != 0 {
77        // Look for an entity with given ID (internally iterates over all ents)
78        let entity_ref = scene.find_entity_with_id(entity_id);
79
80        // Modify selector and VisOutline state if entity is found
81        if let Some(e_ref) = entity_ref {
82            let name = e_ref.get::<&Name>().expect("The entity has no name").0.clone();
83            // Only ents with VisOutline are candidates for visual selection
84            if let Some(mut vis_outline) = e_ref.get::<&mut VisOutline>() {
85                vis_outline.show_outline = true;
86            }
87            // Add the selector resource to the scene
88            scene.add_resource(Selector {
89                current_selected: name.clone(),
90            });
91        }
92    }
93
94    runner.request_redraw(); //need to redraw again so the next frame we show the outline
95}