Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs
6600 views
1
//! Ray casting for meshes.
2
//!
3
//! See the [`MeshRayCast`] system parameter for more information.
4
5
mod intersections;
6
7
use bevy_derive::{Deref, DerefMut};
8
9
use bevy_camera::{
10
primitives::Aabb,
11
visibility::{InheritedVisibility, ViewVisibility},
12
};
13
use bevy_math::{bounding::Aabb3d, Ray3d};
14
use bevy_mesh::{Mesh, Mesh2d, Mesh3d};
15
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
16
17
use intersections::*;
18
pub use intersections::{ray_aabb_intersection_3d, ray_mesh_intersection, RayMeshHit};
19
20
use bevy_asset::{Assets, Handle};
21
use bevy_ecs::{prelude::*, system::lifetimeless::Read, system::SystemParam};
22
use bevy_math::FloatOrd;
23
use bevy_transform::components::GlobalTransform;
24
use tracing::*;
25
26
/// How a ray cast should handle [`Visibility`](bevy_camera::visibility::Visibility).
27
#[derive(Clone, Copy, Reflect)]
28
#[reflect(Clone)]
29
pub enum RayCastVisibility {
30
/// Completely ignore visibility checks. Hidden items can still be ray cast against.
31
Any,
32
/// Only cast rays against entities that are visible in the hierarchy. See [`Visibility`](bevy_camera::visibility::Visibility).
33
Visible,
34
/// Only cast rays against entities that are visible in the hierarchy and visible to a camera or
35
/// light. See [`Visibility`](bevy_camera::visibility::Visibility).
36
VisibleInView,
37
}
38
39
/// Settings for a ray cast.
40
#[derive(Clone)]
41
pub struct MeshRayCastSettings<'a> {
42
/// Determines how ray casting should consider [`Visibility`](bevy_camera::visibility::Visibility).
43
pub visibility: RayCastVisibility,
44
/// A predicate that is applied for every entity that ray casts are performed against.
45
/// Only entities that return `true` will be considered.
46
pub filter: &'a dyn Fn(Entity) -> bool,
47
/// A function that is run every time a hit is found. Ray casting will continue to check for hits
48
/// along the ray as long as this returns `false`.
49
pub early_exit_test: &'a dyn Fn(Entity) -> bool,
50
}
51
52
impl<'a> MeshRayCastSettings<'a> {
53
/// Set the filter to apply to the ray cast.
54
pub fn with_filter(mut self, filter: &'a impl Fn(Entity) -> bool) -> Self {
55
self.filter = filter;
56
self
57
}
58
59
/// Set the early exit test to apply to the ray cast.
60
pub fn with_early_exit_test(mut self, early_exit_test: &'a impl Fn(Entity) -> bool) -> Self {
61
self.early_exit_test = early_exit_test;
62
self
63
}
64
65
/// Set the [`RayCastVisibility`] setting to apply to the ray cast.
66
pub fn with_visibility(mut self, visibility: RayCastVisibility) -> Self {
67
self.visibility = visibility;
68
self
69
}
70
71
/// This ray cast should exit as soon as the nearest hit is found.
72
pub fn always_early_exit(self) -> Self {
73
self.with_early_exit_test(&|_| true)
74
}
75
76
/// This ray cast should check all entities whose AABB intersects the ray and return all hits.
77
pub fn never_early_exit(self) -> Self {
78
self.with_early_exit_test(&|_| false)
79
}
80
}
81
82
impl<'a> Default for MeshRayCastSettings<'a> {
83
fn default() -> Self {
84
Self {
85
visibility: RayCastVisibility::VisibleInView,
86
filter: &|_| true,
87
early_exit_test: &|_| true,
88
}
89
}
90
}
91
92
/// Determines whether backfaces should be culled or included in ray intersection tests.
93
///
94
/// By default, backfaces are culled.
95
#[derive(Copy, Clone, Default, Reflect)]
96
#[reflect(Default, Clone)]
97
pub enum Backfaces {
98
/// Cull backfaces.
99
#[default]
100
Cull,
101
/// Include backfaces.
102
Include,
103
}
104
105
/// Disables backface culling for [ray casts](MeshRayCast) on this entity.
106
#[derive(Component, Copy, Clone, Default, Reflect)]
107
#[reflect(Component, Default, Clone)]
108
pub struct RayCastBackfaces;
109
110
/// A simplified mesh component that can be used for [ray casting](super::MeshRayCast).
111
///
112
/// Consider using this component for complex meshes that don't need perfectly accurate ray casting.
113
#[derive(Component, Clone, Debug, Deref, DerefMut, Reflect)]
114
#[reflect(Component, Debug, Clone)]
115
pub struct SimplifiedMesh(pub Handle<Mesh>);
116
117
type MeshFilter = Or<(With<Mesh3d>, With<Mesh2d>, With<SimplifiedMesh>)>;
118
119
/// Add this ray casting [`SystemParam`] to your system to cast rays into the world with an
120
/// immediate-mode API. Call `cast_ray` to immediately perform a ray cast and get a result.
121
///
122
/// Under the hood, this is a collection of regular bevy queries, resources, and local parameters
123
/// that are added to your system.
124
///
125
/// ## Usage
126
///
127
/// The following system casts a ray into the world with the ray positioned at the origin, pointing in
128
/// the X-direction, and returns a list of intersections:
129
///
130
/// ```
131
/// # use bevy_math::prelude::*;
132
/// # use bevy_picking::prelude::*;
133
/// fn ray_cast_system(mut ray_cast: MeshRayCast) {
134
/// let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
135
/// let hits = ray_cast.cast_ray(ray, &MeshRayCastSettings::default());
136
/// }
137
/// ```
138
///
139
/// ## Configuration
140
///
141
/// You can specify the behavior of the ray cast using [`MeshRayCastSettings`]. This allows you to filter out
142
/// entities, configure early-out behavior, and set whether the [`Visibility`](bevy_camera::visibility::Visibility)
143
/// of an entity should be considered.
144
///
145
/// ```
146
/// # use bevy_ecs::prelude::*;
147
/// # use bevy_math::prelude::*;
148
/// # use bevy_picking::prelude::*;
149
/// # #[derive(Component)]
150
/// # struct Foo;
151
/// fn ray_cast_system(mut ray_cast: MeshRayCast, foo_query: Query<(), With<Foo>>) {
152
/// let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
153
///
154
/// // Only ray cast against entities with the `Foo` component.
155
/// let filter = |entity| foo_query.contains(entity);
156
///
157
/// // Never early-exit. Note that you can change behavior per-entity.
158
/// let early_exit_test = |_entity| false;
159
///
160
/// // Ignore the visibility of entities. This allows ray casting hidden entities.
161
/// let visibility = RayCastVisibility::Any;
162
///
163
/// let settings = MeshRayCastSettings::default()
164
/// .with_filter(&filter)
165
/// .with_early_exit_test(&early_exit_test)
166
/// .with_visibility(visibility);
167
///
168
/// // Cast the ray with the settings, returning a list of intersections.
169
/// let hits = ray_cast.cast_ray(ray, &settings);
170
/// }
171
/// ```
172
#[derive(SystemParam)]
173
pub struct MeshRayCast<'w, 's> {
174
#[doc(hidden)]
175
pub meshes: Res<'w, Assets<Mesh>>,
176
#[doc(hidden)]
177
pub hits: Local<'s, Vec<(FloatOrd, (Entity, RayMeshHit))>>,
178
#[doc(hidden)]
179
pub output: Local<'s, Vec<(Entity, RayMeshHit)>>,
180
#[doc(hidden)]
181
pub culled_list: Local<'s, Vec<(FloatOrd, Entity)>>,
182
#[doc(hidden)]
183
pub culling_query: Query<
184
'w,
185
's,
186
(
187
Read<InheritedVisibility>,
188
Read<ViewVisibility>,
189
Read<Aabb>,
190
Read<GlobalTransform>,
191
Entity,
192
),
193
MeshFilter,
194
>,
195
#[doc(hidden)]
196
pub mesh_query: Query<
197
'w,
198
's,
199
(
200
Option<Read<Mesh2d>>,
201
Option<Read<Mesh3d>>,
202
Option<Read<SimplifiedMesh>>,
203
Has<RayCastBackfaces>,
204
Read<GlobalTransform>,
205
),
206
MeshFilter,
207
>,
208
}
209
210
impl<'w, 's> MeshRayCast<'w, 's> {
211
/// Casts the `ray` into the world and returns a sorted list of intersections, nearest first.
212
pub fn cast_ray(
213
&mut self,
214
ray: Ray3d,
215
settings: &MeshRayCastSettings,
216
) -> &[(Entity, RayMeshHit)] {
217
let ray_cull = info_span!("ray culling");
218
let ray_cull_guard = ray_cull.enter();
219
220
self.hits.clear();
221
self.culled_list.clear();
222
self.output.clear();
223
224
// Check all entities to see if the ray intersects the AABB. Use this to build a short list
225
// of entities that are in the path of the ray.
226
let (aabb_hits_tx, aabb_hits_rx) = crossbeam_channel::unbounded::<(FloatOrd, Entity)>();
227
let visibility_setting = settings.visibility;
228
self.culling_query.par_iter().for_each(
229
|(inherited_visibility, view_visibility, aabb, transform, entity)| {
230
let should_ray_cast = match visibility_setting {
231
RayCastVisibility::Any => true,
232
RayCastVisibility::Visible => inherited_visibility.get(),
233
RayCastVisibility::VisibleInView => view_visibility.get(),
234
};
235
if should_ray_cast
236
&& let Some(distance) = ray_aabb_intersection_3d(
237
ray,
238
&Aabb3d::new(aabb.center, aabb.half_extents),
239
&transform.affine(),
240
)
241
{
242
aabb_hits_tx.send((FloatOrd(distance), entity)).ok();
243
}
244
},
245
);
246
*self.culled_list = aabb_hits_rx.try_iter().collect();
247
248
// Sort by the distance along the ray.
249
self.culled_list.sort_by_key(|(aabb_near, _)| *aabb_near);
250
251
drop(ray_cull_guard);
252
253
// Perform ray casts against the culled entities.
254
let mut nearest_blocking_hit = FloatOrd(f32::INFINITY);
255
let ray_cast_guard = debug_span!("ray_cast");
256
self.culled_list
257
.iter()
258
.filter(|(_, entity)| (settings.filter)(*entity))
259
.for_each(|(aabb_near, entity)| {
260
// Get the mesh components and transform.
261
let Ok((mesh2d, mesh3d, simplified_mesh, has_backfaces, transform)) =
262
self.mesh_query.get(*entity)
263
else {
264
return;
265
};
266
267
// Get the underlying mesh handle. One of these will always be `Some` because of the query filters.
268
let Some(mesh_handle) = simplified_mesh
269
.map(|m| &m.0)
270
.or(mesh3d.map(|m| &m.0).or(mesh2d.map(|m| &m.0)))
271
else {
272
return;
273
};
274
275
// Is it even possible the mesh could be closer than the current best?
276
if *aabb_near > nearest_blocking_hit {
277
return;
278
}
279
280
// Does the mesh handle resolve?
281
let Some(mesh) = self.meshes.get(mesh_handle) else {
282
return;
283
};
284
285
// Backfaces of 2d meshes are never culled, unlike 3d meshes.
286
let backfaces = match (has_backfaces, mesh2d.is_some()) {
287
(false, false) => Backfaces::Cull,
288
_ => Backfaces::Include,
289
};
290
291
// Perform the actual ray cast.
292
let _ray_cast_guard = ray_cast_guard.enter();
293
let transform = transform.affine();
294
let intersection = ray_intersection_over_mesh(mesh, &transform, ray, backfaces);
295
296
if let Some(intersection) = intersection {
297
let distance = FloatOrd(intersection.distance);
298
if (settings.early_exit_test)(*entity) && distance < nearest_blocking_hit {
299
// The reason we don't just return here is because right now we are
300
// going through the AABBs in order, but that doesn't mean that an
301
// AABB that starts further away can't end up with a closer hit than
302
// an AABB that starts closer. We need to keep checking AABBs that
303
// could possibly contain a nearer hit.
304
nearest_blocking_hit = distance.min(nearest_blocking_hit);
305
}
306
self.hits.push((distance, (*entity, intersection)));
307
};
308
});
309
310
self.hits.retain(|(dist, _)| *dist <= nearest_blocking_hit);
311
self.hits.sort_by_key(|(k, _)| *k);
312
let hits = self.hits.iter().map(|(_, (e, i))| (*e, i.to_owned()));
313
self.output.extend(hits);
314
self.output.as_ref()
315
}
316
}
317
318