pub struct Ray { /* private fields */ }Expand description
A point in the world and the direction it looks.
Camera::ray_through builds one from a
pixel; hit it against whatever shapes a game keeps to see what that
pixel points at.
Implementations§
Source§impl Ray
impl Ray
Sourcepub fn new(origin: Vec3, direction: Vec3) -> Self
pub fn new(origin: Vec3, direction: Vec3) -> Self
A ray from origin along direction, which is kept one unit long so
that every hit is a distance in meters.
Sourcepub fn at(&self, t: f32) -> Vec3
pub fn at(&self, t: f32) -> Vec3
The point t meters along the ray.
Examples found in repository?
examples/sound-lab.rs (line 504)
474 fn handle_drag(&mut self, ctx: &mut TickContext<'_, SoundCheck>) {
475 // Read before the check below for the UI's own claim on the
476 // pointer, so a release over it still frees a source a drag moved
477 // there.
478 if ctx.released(Button::Select) {
479 self.dragging = None;
480 }
481 if ctx.ui_wants_pointer() {
482 return;
483 }
484 let ray = ctx
485 .last_camera()
486 .ray_through(ctx.pointer(), ctx.window_size());
487
488 if ctx.pressed(Button::Select) {
489 self.dragging = self.sources.iter().position(|source| {
490 ray.hit_sphere(source.position, SOURCE_PICK_RADIUS)
491 .is_some()
492 });
493 }
494
495 let Some(index) = self.dragging else {
496 return;
497 };
498 let Some(distance) = ray.hit_plane(ray::Plane {
499 point: Vec3::ZERO,
500 normal: Vec3::Y,
501 }) else {
502 return;
503 };
504 let hit = ray.at(distance);
505 let dropped =
506 Vec2::new(hit.x, hit.z).clamp(Vec2::splat(-PLAY_BOUND), Vec2::splat(PLAY_BOUND));
507 self.sources[index].position = Vec3::new(dropped.x, SOURCE_HEIGHT, dropped.y);
508 }More examples
examples/isometric-board.rs (line 440)
414 fn handle_click(&mut self, ctx: &mut TickContext<'_, Board>) {
415 if ctx.ui_wants_pointer() || !ctx.pressed(Button::Select) {
416 return;
417 }
418 let ray = ctx
419 .last_camera()
420 .ray_through(ctx.pointer(), ctx.window_size());
421 let (lift, half) = unit_geometry(self.turn);
422
423 if self.current().target.is_none() {
424 let center = self.current().position;
425 if ray.hit_aabb(center - half, center + half).is_some() {
426 self.selected = !self.selected;
427 return;
428 }
429 }
430 if !self.selected {
431 return;
432 }
433
434 let Some(distance) = ray.hit_plane(ray::Plane {
435 point: Vec3::ZERO,
436 normal: Vec3::Y,
437 }) else {
438 return;
439 };
440 let Some(tile) = tile_at(ray.at(distance)) else {
441 return;
442 };
443 if tile == self.current().tile || tile == self.other().tile {
444 return;
445 }
446
447 let destination = tile_center(tile) + Vec3::Y * lift;
448 let heading = destination.x - self.current().position.x;
449 let current = self.current_mut();
450 if heading.abs() > f32::EPSILON {
451 current.facing_right = heading > 0.0;
452 }
453 current.target = Some(destination);
454 self.selected = false;
455 ctx.play(Sound::Click);
456 }
457
458 /// Cursor target, `None` while the UI has the pointer.
459 fn hovered(&self, ctx: &FrameContext<'_, Board>) -> Hover {
460 if ctx.ui_wants_pointer() {
461 return Hover::None;
462 }
463 let ray = ctx
464 .last_camera()
465 .ray_through(ctx.pointer(), ctx.window_size());
466
467 if self.current().target.is_none() {
468 let (_, half) = unit_geometry(self.turn);
469 let center = self.current().position;
470 if ray.hit_aabb(center - half, center + half).is_some() {
471 return Hover::CurrentUnit;
472 }
473 }
474 let Some(distance) = ray.hit_plane(ray::Plane {
475 point: Vec3::ZERO,
476 normal: Vec3::Y,
477 }) else {
478 return Hover::None;
479 };
480 match tile_at(ray.at(distance)) {
481 Some(tile) => Hover::Tile(tile),
482 None => Hover::None,
483 }
484 }Sourcepub fn hit_plane(&self, plane: Plane) -> Option<f32>
pub fn hit_plane(&self, plane: Plane) -> Option<f32>
Distance in meters along the ray to plane; None where the ray is
parallel to it or points away from it.
Examples found in repository?
examples/sound-lab.rs (lines 498-501)
474 fn handle_drag(&mut self, ctx: &mut TickContext<'_, SoundCheck>) {
475 // Read before the check below for the UI's own claim on the
476 // pointer, so a release over it still frees a source a drag moved
477 // there.
478 if ctx.released(Button::Select) {
479 self.dragging = None;
480 }
481 if ctx.ui_wants_pointer() {
482 return;
483 }
484 let ray = ctx
485 .last_camera()
486 .ray_through(ctx.pointer(), ctx.window_size());
487
488 if ctx.pressed(Button::Select) {
489 self.dragging = self.sources.iter().position(|source| {
490 ray.hit_sphere(source.position, SOURCE_PICK_RADIUS)
491 .is_some()
492 });
493 }
494
495 let Some(index) = self.dragging else {
496 return;
497 };
498 let Some(distance) = ray.hit_plane(ray::Plane {
499 point: Vec3::ZERO,
500 normal: Vec3::Y,
501 }) else {
502 return;
503 };
504 let hit = ray.at(distance);
505 let dropped =
506 Vec2::new(hit.x, hit.z).clamp(Vec2::splat(-PLAY_BOUND), Vec2::splat(PLAY_BOUND));
507 self.sources[index].position = Vec3::new(dropped.x, SOURCE_HEIGHT, dropped.y);
508 }More examples
examples/isometric-board.rs (lines 434-437)
414 fn handle_click(&mut self, ctx: &mut TickContext<'_, Board>) {
415 if ctx.ui_wants_pointer() || !ctx.pressed(Button::Select) {
416 return;
417 }
418 let ray = ctx
419 .last_camera()
420 .ray_through(ctx.pointer(), ctx.window_size());
421 let (lift, half) = unit_geometry(self.turn);
422
423 if self.current().target.is_none() {
424 let center = self.current().position;
425 if ray.hit_aabb(center - half, center + half).is_some() {
426 self.selected = !self.selected;
427 return;
428 }
429 }
430 if !self.selected {
431 return;
432 }
433
434 let Some(distance) = ray.hit_plane(ray::Plane {
435 point: Vec3::ZERO,
436 normal: Vec3::Y,
437 }) else {
438 return;
439 };
440 let Some(tile) = tile_at(ray.at(distance)) else {
441 return;
442 };
443 if tile == self.current().tile || tile == self.other().tile {
444 return;
445 }
446
447 let destination = tile_center(tile) + Vec3::Y * lift;
448 let heading = destination.x - self.current().position.x;
449 let current = self.current_mut();
450 if heading.abs() > f32::EPSILON {
451 current.facing_right = heading > 0.0;
452 }
453 current.target = Some(destination);
454 self.selected = false;
455 ctx.play(Sound::Click);
456 }
457
458 /// Cursor target, `None` while the UI has the pointer.
459 fn hovered(&self, ctx: &FrameContext<'_, Board>) -> Hover {
460 if ctx.ui_wants_pointer() {
461 return Hover::None;
462 }
463 let ray = ctx
464 .last_camera()
465 .ray_through(ctx.pointer(), ctx.window_size());
466
467 if self.current().target.is_none() {
468 let (_, half) = unit_geometry(self.turn);
469 let center = self.current().position;
470 if ray.hit_aabb(center - half, center + half).is_some() {
471 return Hover::CurrentUnit;
472 }
473 }
474 let Some(distance) = ray.hit_plane(ray::Plane {
475 point: Vec3::ZERO,
476 normal: Vec3::Y,
477 }) else {
478 return Hover::None;
479 };
480 match tile_at(ray.at(distance)) {
481 Some(tile) => Hover::Tile(tile),
482 None => Hover::None,
483 }
484 }Sourcepub fn hit_sphere(&self, center: Vec3, radius: f32) -> Option<f32>
pub fn hit_sphere(&self, center: Vec3, radius: f32) -> Option<f32>
Distance in meters along the ray to the sphere of radius meters
around center; None where the ray never intersects it.
A ray that starts within one returns where it leaves.
Examples found in repository?
examples/sound-lab.rs (line 490)
474 fn handle_drag(&mut self, ctx: &mut TickContext<'_, SoundCheck>) {
475 // Read before the check below for the UI's own claim on the
476 // pointer, so a release over it still frees a source a drag moved
477 // there.
478 if ctx.released(Button::Select) {
479 self.dragging = None;
480 }
481 if ctx.ui_wants_pointer() {
482 return;
483 }
484 let ray = ctx
485 .last_camera()
486 .ray_through(ctx.pointer(), ctx.window_size());
487
488 if ctx.pressed(Button::Select) {
489 self.dragging = self.sources.iter().position(|source| {
490 ray.hit_sphere(source.position, SOURCE_PICK_RADIUS)
491 .is_some()
492 });
493 }
494
495 let Some(index) = self.dragging else {
496 return;
497 };
498 let Some(distance) = ray.hit_plane(ray::Plane {
499 point: Vec3::ZERO,
500 normal: Vec3::Y,
501 }) else {
502 return;
503 };
504 let hit = ray.at(distance);
505 let dropped =
506 Vec2::new(hit.x, hit.z).clamp(Vec2::splat(-PLAY_BOUND), Vec2::splat(PLAY_BOUND));
507 self.sources[index].position = Vec3::new(dropped.x, SOURCE_HEIGHT, dropped.y);
508 }Sourcepub fn hit_aabb(&self, min: Vec3, max: Vec3) -> Option<f32>
pub fn hit_aabb(&self, min: Vec3, max: Vec3) -> Option<f32>
Distance in meters along the ray to the bounds between min and
max; None where the ray never intersects them.
A ray that starts within them returns where it leaves.
Examples found in repository?
More examples
examples/isometric-board.rs (line 425)
414 fn handle_click(&mut self, ctx: &mut TickContext<'_, Board>) {
415 if ctx.ui_wants_pointer() || !ctx.pressed(Button::Select) {
416 return;
417 }
418 let ray = ctx
419 .last_camera()
420 .ray_through(ctx.pointer(), ctx.window_size());
421 let (lift, half) = unit_geometry(self.turn);
422
423 if self.current().target.is_none() {
424 let center = self.current().position;
425 if ray.hit_aabb(center - half, center + half).is_some() {
426 self.selected = !self.selected;
427 return;
428 }
429 }
430 if !self.selected {
431 return;
432 }
433
434 let Some(distance) = ray.hit_plane(ray::Plane {
435 point: Vec3::ZERO,
436 normal: Vec3::Y,
437 }) else {
438 return;
439 };
440 let Some(tile) = tile_at(ray.at(distance)) else {
441 return;
442 };
443 if tile == self.current().tile || tile == self.other().tile {
444 return;
445 }
446
447 let destination = tile_center(tile) + Vec3::Y * lift;
448 let heading = destination.x - self.current().position.x;
449 let current = self.current_mut();
450 if heading.abs() > f32::EPSILON {
451 current.facing_right = heading > 0.0;
452 }
453 current.target = Some(destination);
454 self.selected = false;
455 ctx.play(Sound::Click);
456 }
457
458 /// Cursor target, `None` while the UI has the pointer.
459 fn hovered(&self, ctx: &FrameContext<'_, Board>) -> Hover {
460 if ctx.ui_wants_pointer() {
461 return Hover::None;
462 }
463 let ray = ctx
464 .last_camera()
465 .ray_through(ctx.pointer(), ctx.window_size());
466
467 if self.current().target.is_none() {
468 let (_, half) = unit_geometry(self.turn);
469 let center = self.current().position;
470 if ray.hit_aabb(center - half, center + half).is_some() {
471 return Hover::CurrentUnit;
472 }
473 }
474 let Some(distance) = ray.hit_plane(ray::Plane {
475 point: Vec3::ZERO,
476 normal: Vec3::Y,
477 }) else {
478 return Hover::None;
479 };
480 match tile_at(ray.at(distance)) {
481 Some(tile) => Hover::Tile(tile),
482 None => Hover::None,
483 }
484 }Trait Implementations§
impl Copy for Ray
impl StructuralPartialEq for Ray
Auto Trait Implementations§
impl Freeze for Ray
impl RefUnwindSafe for Ray
impl Send for Ray
impl Sync for Ray
impl Unpin for Ray
impl UnsafeUnpin for Ray
impl UnwindSafe for Ray
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more