Skip to content

Commit a44c08b

Browse files
refactor(picking): refactor picking to fit directory structure
1 parent 18f6678 commit a44c08b

4 files changed

Lines changed: 252 additions & 246 deletions

File tree

Lines changed: 3 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -1,242 +1,5 @@
11
pub mod components;
2+
pub mod queries;
23

3-
use crate::ecs::{
4-
camera::components::Projection,
5-
world::{Entity, Vec2, Vec3, Vec4, World},
6-
};
7-
8-
#[derive(Debug, Clone, Copy)]
9-
pub struct PickingRay {
10-
pub origin: Vec3,
11-
pub direction: Vec3,
12-
}
13-
14-
impl PickingRay {
15-
pub fn from_screen_position(world: &World, screen_pos: Vec2) -> Option<Self> {
16-
let window = &world.resources.window;
17-
let window_handle = window.handle.as_ref()?;
18-
let scale_factor = window_handle.scale_factor() as f32;
19-
let window_size = window_handle.inner_size();
20-
21-
let camera_entity = world.resources.active_camera?;
22-
23-
let camera = world.get_camera(camera_entity)?;
24-
let global_transform = world.get_global_transform(camera_entity)?;
25-
26-
let physical_x = screen_pos.x * scale_factor;
27-
let physical_y = screen_pos.y * scale_factor;
28-
29-
let ndc_x = (2.0 * physical_x) / window_size.width as f32 - 1.0;
30-
let ndc_y = 1.0 - (2.0 * physical_y) / window_size.height as f32;
31-
32-
let view_matrix = global_transform.0.try_inverse()?;
33-
let projection_matrix = camera.projection.matrix();
34-
let inverse_vp = (projection_matrix * view_matrix).try_inverse()?;
35-
36-
let (origin, direction) = match camera.projection {
37-
Projection::Perspective { .. } => {
38-
let camera_pos = global_transform.0.column(3).xyz();
39-
let clip_pos = Vec4::new(ndc_x, ndc_y, -1.0, 1.0);
40-
let world_pos = inverse_vp * clip_pos;
41-
let world_pos = world_pos.xyz() / world_pos.w;
42-
let direction = (world_pos - camera_pos).normalize();
43-
(camera_pos, direction)
44-
}
45-
Projection::Orthographic { .. } => {
46-
let near_clip = Vec4::new(ndc_x, ndc_y, -1.0, 1.0);
47-
let far_clip = Vec4::new(ndc_x, ndc_y, 1.0, 1.0);
48-
49-
let near_world = inverse_vp * near_clip;
50-
let far_world = inverse_vp * far_clip;
51-
52-
let near_point = near_world.xyz() / near_world.w;
53-
let far_point = far_world.xyz() / far_world.w;
54-
55-
let direction = (far_point - near_point).normalize();
56-
(near_point, direction)
57-
}
58-
};
59-
60-
Some(Self { origin, direction })
61-
}
62-
63-
pub fn intersect_plane(&self, plane_normal: Vec3, plane_distance: f32) -> Option<Vec3> {
64-
let denom = nalgebra_glm::dot(&plane_normal, &self.direction);
65-
if denom.abs() < 1e-6 {
66-
return None;
67-
}
68-
69-
let t = -(nalgebra_glm::dot(&plane_normal, &self.origin) + plane_distance) / denom;
70-
if t < 0.0 {
71-
return None;
72-
}
73-
74-
Some(self.origin + self.direction * t)
75-
}
76-
77-
pub fn intersect_ground_plane(&self, y_level: f32) -> Option<Vec3> {
78-
self.intersect_plane(Vec3::new(0.0, 1.0, 0.0), -y_level)
79-
}
80-
}
81-
82-
#[derive(Debug, Clone)]
83-
pub struct PickingResult {
84-
pub entity: Entity,
85-
pub distance: f32,
86-
pub world_position: Vec3,
87-
}
88-
89-
#[derive(Debug, Clone, Copy)]
90-
pub struct PickingOptions {
91-
pub max_distance: f32,
92-
pub ignore_invisible: bool,
93-
}
94-
95-
impl Default for PickingOptions {
96-
fn default() -> Self {
97-
Self {
98-
max_distance: f32::INFINITY,
99-
ignore_invisible: true,
100-
}
101-
}
102-
}
103-
104-
pub fn pick_entities_with_options(
105-
world: &World,
106-
screen_pos: Vec2,
107-
options: PickingOptions,
108-
) -> Vec<PickingResult> {
109-
let ray = match PickingRay::from_screen_position(world, screen_pos) {
110-
Some(ray) => ray,
111-
None => return Vec::new(),
112-
};
113-
114-
let mut results = Vec::new();
115-
116-
for entity in world.query_entities(crate::ecs::world::BOUNDING_VOLUME) {
117-
let bounding_volume = match world.get_bounding_volume(entity) {
118-
Some(bv) => bv,
119-
None => continue,
120-
};
121-
122-
let global_transform = match world.get_global_transform(entity) {
123-
Some(gt) => gt,
124-
None => continue,
125-
};
126-
127-
if options.ignore_invisible
128-
&& let Some(visible) = world.get_visibility(entity)
129-
&& !visible.visible
130-
{
131-
continue;
132-
}
133-
134-
let transformed_bv = bounding_volume.transform(&global_transform.0);
135-
136-
let to_center = transformed_bv.obb.center - ray.origin;
137-
let projection = nalgebra_glm::dot(&to_center, &ray.direction);
138-
let closest_point = if projection < 0.0 {
139-
ray.origin
140-
} else {
141-
ray.origin + ray.direction * projection
142-
};
143-
144-
let distance_to_sphere = nalgebra_glm::distance(&closest_point, &transformed_bv.obb.center);
145-
if distance_to_sphere > transformed_bv.sphere_radius {
146-
continue;
147-
}
148-
149-
if let Some(distance) = transformed_bv.obb.intersect_ray(ray.origin, ray.direction)
150-
&& distance <= options.max_distance
151-
{
152-
let world_position = ray.origin + ray.direction * distance;
153-
results.push(PickingResult {
154-
entity,
155-
distance,
156-
world_position,
157-
});
158-
}
159-
}
160-
161-
results.sort_by(|a, b| a.distance.partial_cmp(&b.distance).unwrap());
162-
results
163-
}
164-
165-
pub fn pick_entities(world: &World, screen_pos: Vec2) -> Vec<PickingResult> {
166-
pick_entities_with_options(world, screen_pos, PickingOptions::default())
167-
}
168-
169-
pub fn pick_closest_entity(world: &World, screen_pos: Vec2) -> Option<PickingResult> {
170-
pick_entities(world, screen_pos).into_iter().next()
171-
}
172-
173-
pub fn get_ground_position_from_screen(
174-
world: &World,
175-
screen_pos: Vec2,
176-
y_level: f32,
177-
) -> Option<Vec3> {
178-
let ray = PickingRay::from_screen_position(world, screen_pos)?;
179-
ray.intersect_ground_plane(y_level)
180-
}
181-
182-
pub fn pick_entities_in_frustum(world: &World, entities: &[Entity]) -> Vec<Entity> {
183-
let camera_entity = match world.resources.active_camera {
184-
Some(entity) => entity,
185-
None => return Vec::new(),
186-
};
187-
188-
let camera = match world.get_camera(camera_entity) {
189-
Some(cam) => cam,
190-
None => return Vec::new(),
191-
};
192-
193-
let global_transform = match world.get_global_transform(camera_entity) {
194-
Some(gt) => gt,
195-
None => return Vec::new(),
196-
};
197-
198-
let view_matrix = match global_transform.0.try_inverse() {
199-
Some(vm) => vm,
200-
None => return Vec::new(),
201-
};
202-
203-
let projection_matrix = camera.projection.matrix();
204-
let view_projection = projection_matrix * view_matrix;
205-
206-
let mut visible_entities = Vec::new();
207-
208-
for &entity in entities {
209-
let bounding_volume = match world.get_bounding_volume(entity) {
210-
Some(bv) => bv,
211-
None => continue,
212-
};
213-
214-
let global_transform = match world.get_global_transform(entity) {
215-
Some(gt) => gt,
216-
None => continue,
217-
};
218-
219-
let transformed_bv = bounding_volume.transform(&global_transform.0);
220-
let center = transformed_bv.obb.center;
221-
222-
let clip_pos = view_projection * Vec4::new(center.x, center.y, center.z, 1.0);
223-
if clip_pos.w <= 0.0 {
224-
continue;
225-
}
226-
227-
let ndc = clip_pos.xyz() / clip_pos.w;
228-
let sphere_radius_ndc = transformed_bv.sphere_radius / clip_pos.w;
229-
230-
if ndc.x + sphere_radius_ndc >= -1.0
231-
&& ndc.x - sphere_radius_ndc <= 1.0
232-
&& ndc.y + sphere_radius_ndc >= -1.0
233-
&& ndc.y - sphere_radius_ndc <= 1.0
234-
&& ndc.z + sphere_radius_ndc >= -1.0
235-
&& ndc.z - sphere_radius_ndc <= 1.0
236-
{
237-
visible_entities.push(entity);
238-
}
239-
}
240-
241-
visible_entities
242-
}
4+
pub use components::*;
5+
pub use queries::*;
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
use crate::ecs::world::Vec3;
1+
mod hovered;
22

3-
#[derive(Debug, Clone, Copy, Default)]
4-
pub struct Hovered {
5-
pub cursor_position: Vec3,
6-
pub distance: f32,
7-
}
3+
pub use hovered::*;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use crate::ecs::world::Vec3;
2+
3+
#[derive(Debug, Clone, Copy, Default)]
4+
pub struct Hovered {
5+
pub cursor_position: Vec3,
6+
pub distance: f32,
7+
}

0 commit comments

Comments
 (0)