skullball/rust/src/util.rs
2026-08-06 20:01:01 +03:00

82 lines
2.2 KiB
Rust

use godot::{
classes::{PhysicsRayQueryParameters3D, World3D},
prelude::*,
};
pub fn cast_ray(
from: Vector3,
to: Vector3,
world: &Option<Gd<World3D>>,
exclude: &Array<Rid>,
) -> Option<RaycastResult> {
let ray = PhysicsRayQueryParameters3D::create(from, to);
if let Some(mut ray) = ray {
ray.set_collision_mask(0b1111);
ray.set_hit_back_faces(false);
ray.set_hit_from_inside(false);
ray.set_collide_with_bodies(true);
ray.set_exclude(&exclude);
if let Some(world) = world
&& let Some(mut space) = world.get_direct_space_state()
{
let results = space.intersect_ray(&ray);
if let Some(position) = results.get("position")
&& let Some(collider) = results.get("collider")
&& let Some(normal) = results.get("normal")
{
Some(RaycastResult {
position: Vector3::from_variant(&position),
normal: Vector3::from_variant(&normal),
collider: Gd::from_variant(&collider),
})
} else {
None
}
} else {
None
}
} else {
None
}
}
pub fn shotgun_ray(
from: Vector3,
to: Vector3,
up: Vector3,
world: &Option<Gd<World3D>>,
exclude: &Array<Rid>,
density: u8,
angle: f32,
distance: f32,
) -> Vec<RaycastResult> {
let mut results = Vec::new();
let original_dir = (to - from).normalized();
let step = angle / density as f32;
let up = up.normalized();
let right = original_dir.cross(up).normalized();
for y in 0..density {
let y_rot = -(angle / 2.) + step * y as f32;
for x in 0..density {
let x_rot = -(angle / 2.) + step * x as f32;
let dir = original_dir.rotated(up, x_rot).rotated(right, y_rot);
let to = from + dir * distance;
if let Some(res) = cast_ray(from, to, world, exclude) {
results.push(res);
}
}
}
results
}
#[derive(Debug, Clone)]
pub struct RaycastResult {
pub position: Vector3,
pub normal: Vector3,
pub collider: Gd<Object>,
}