use std::{
sync::{
Arc,
RwLock,
},
thread::spawn,
};
use ncollide2d::{
world::{
CollisionWorld,
},
pipeline::object::{
CollisionGroups,
GeometricQueryType,
},
math::{
Isometry,
Point,
Vector,
},
query::{
Ray,
},
shape::{
ShapeHandle,
Polyline,
Segment,
},
};
pub use crate::*;
pub struct TileCollider {
world: CollisionWorld<f32, usize>,
}
impl TileCollider {
pub fn new() -> Self {
Self {
world: CollisionWorld::new(0.02),
}
}
pub fn add_object(&mut self, id: usize, object: &Object) {
let polygon = Polyline::new(
object.points()
.iter()
.map(|p| Point::new(p.x, p.y))
.collect::<Vec<Point<f32>>>(),
None
);
self.world.add(
Isometry::identity(),
ShapeHandle::new(polygon),
CollisionGroups::new(),
GeometricQueryType::Contacts(0.02, 0.02),
id
);
}
pub fn update(&mut self) {
self.world.update()
}
pub fn get_hovered_objects(&self, point: &Point<f32>) -> Vec<usize> {
let mut object_ids = vec![];
let mut interferences = vec![];
self.world.broad_phase
.interferences_with_point(
point,
&mut interferences
);
let ray = Ray::new(point.clone(), Vector::x());
for handle in interferences {
if let Some(co) = self.world.collision_object(*handle) {
if let Some(polyline) = co.shape().downcast_ref::<Polyline<f32>>() {
let mut winding_number = 0;
let points = polyline.points();
for edge in polyline.edges() {
let segment = Segment::new(points[edge.indices.x], points[edge.indices.y]);
use ncollide2d::query::RayCast;
if segment.intersects_ray(&Isometry::identity(), &ray) {
winding_number += 1;
}
}
if winding_number % 2 == 1 {
object_ids.push(*co.data());
}
}
}
}
object_ids
}
}
pub trait TileColliderLoader {
fn load(&mut self, tile: Arc<RwLock<Tile>>);
}
impl TileColliderLoader for Arc<RwLock<TileCollider>> {
fn load(&mut self, tile: Arc<RwLock<Tile>>) {
let collider_clone = self.clone();
spawn(move || {
if let Ok(tile) = tile.read() {
if let Ok(objects) = tile.objects().read() {
match collider_clone.write() {
Ok(mut collider) => {
for object_id in 0..objects.len() {
if objects[object_id].points().len() >= 2 {
collider.add_object(object_id, &objects[object_id]);
}
}
collider.update();
},
Err(_e) => log::error!("Could not aquire collider lock. Not loading the objects of this tile."),
}
}
}
});
}
}