use crate::{
camera::{CameraScaling, CameraSettings},
error::objects::*,
utils::scale,
};
use super::{physics::Shape, physics::*, NObject, Node, Object, ObjectsMap};
use crossbeam::atomic::AtomicCell;
use glam::{vec2, Vec2};
use hashbrown::HashMap;
use indexmap::{indexset, IndexSet};
use parking_lot::Mutex;
use rapier2d::prelude::*;
use std::sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc,
};
#[derive(Clone)]
pub struct Scene {
layers: Arc<Mutex<IndexSet<Layer>>>,
physics_pipeline: Arc<Mutex<PhysicsPipeline>>,
}
impl Scene {
pub fn iterate_all_physics(&self) {
let mut pipeline = self.physics_pipeline.lock();
let layers = self.layers.lock();
for layer in layers.iter() {
layer.step_physics(&mut pipeline);
}
}
pub fn new_layer(&self) -> Layer {
let object = Object::default();
let node = Layer::new(Arc::new(Mutex::new(Node {
object,
parent: None,
rigid_body_parent: None,
children: vec![],
})));
self.layers.lock().insert(node.clone());
node
}
pub fn remove_layer(&self, layer: &mut Layer) -> Result<(), NoLayerError> {
let node: NObject;
let mut layers = self.layers.lock();
if layers.remove(layer) {
node = layer.root.clone();
} else {
return Err(NoLayerError);
}
let mut objectguard = node.lock();
objectguard.remove_children(
&mut layer.objects_map.lock(),
&mut layer.rigid_body_roots.lock(),
);
layers.remove(layer);
Ok(())
}
pub fn get_layers(&self) -> IndexSet<Layer> {
self.layers.lock().clone()
}
}
impl Default for Scene {
fn default() -> Self {
Self {
layers: Arc::new(Mutex::new(indexset![])),
physics_pipeline: Arc::new(Mutex::new(PhysicsPipeline::new())),
}
}
}
#[derive(Clone)]
pub struct Layer {
pub(crate) root: NObject,
pub(crate) camera: Arc<Mutex<NObject>>,
camera_settings: Arc<AtomicCell<CameraSettings>>,
pub(crate) objects_map: Arc<Mutex<ObjectsMap>>,
rigid_body_roots: Arc<Mutex<ObjectsMap>>,
latest_object: Arc<AtomicU64>,
physics: Arc<Mutex<Physics>>,
physics_enabled: Arc<AtomicBool>,
}
impl Layer {
pub fn new(root: NObject) -> Self {
let mut objects_map = HashMap::new();
objects_map.insert(0, root.clone());
Self {
root: root.clone(),
camera: Arc::new(Mutex::new(root)),
camera_settings: Arc::new(AtomicCell::new(CameraSettings::default())),
objects_map: Arc::new(Mutex::new(objects_map)),
rigid_body_roots: Arc::new(Mutex::new(HashMap::new())),
latest_object: Arc::new(AtomicU64::new(1)),
physics: Arc::new(Mutex::new(Physics::new())),
physics_enabled: Arc::new(AtomicBool::new(true)),
}
}
pub(crate) fn physics(&self) -> &Arc<Mutex<Physics>> {
&self.physics
}
pub(crate) fn rigid_body_roots(&self) -> &Arc<Mutex<ObjectsMap>> {
&self.rigid_body_roots
}
pub fn set_camera(&self, camera: &Object) -> Result<(), ObjectError> {
*self.camera.lock() = camera.as_node().ok_or(ObjectError::Uninitialized)?;
Ok(())
}
pub(crate) fn camera_position(&self) -> Vec2 {
self.camera.lock().lock().object.transform.position
}
pub fn camera_scaling(&self) -> CameraScaling {
self.camera_settings.load().mode
}
pub fn zoom(&self) -> f32 {
self.camera_settings.load().zoom
}
pub fn set_zoom(&self, zoom: f32) {
let settings = self.camera_settings();
self.camera_settings.store(settings.zoom(zoom))
}
pub fn set_camera_settings(&self, settings: CameraSettings) {
self.camera_settings.store(settings)
}
pub fn camera_settings(&self) -> CameraSettings {
self.camera_settings.load()
}
pub fn side_to_world(&self, direction: [f32; 2], dimensions: Vec2) -> Vec2 {
let camera = Self::camera_position(self);
let direction = [direction[0] * 2.0 - 1.0, direction[1] * 2.0 - 1.0];
let dimensions = scale(Self::camera_scaling(self), dimensions);
let zoom = 1.0 / Self::zoom(self);
vec2(
direction[0] * (dimensions.x * zoom) + camera.x * 2.0,
direction[1] * (dimensions.y * zoom) + camera.y * 2.0,
)
}
pub fn contains_object(&self, object_id: &usize) -> bool {
self.objects_map.lock().contains_key(object_id)
}
pub(crate) fn step_physics(&self, physics_pipeline: &mut PhysicsPipeline) {
if self.physics_enabled.load(Ordering::Acquire) {
let mut map = self.rigid_body_roots.lock();
let mut physics = self.physics.lock();
physics.step(physics_pipeline); for (_, object) in map.iter_mut() {
let mut node = object.lock();
let rigid_body = physics
.rigid_body_set
.get(node.object.rigidbody_handle().unwrap())
.unwrap();
node.object.set_isometry(
(*rigid_body.translation()).into(),
rigid_body.rotation().angle(),
);
}
}
}
pub fn gravity(&self) -> Vec2 {
self.physics.lock().gravity.into()
}
pub fn set_gravity(&self, gravity: Vec2) {
self.physics.lock().gravity = gravity.into();
}
pub fn physics_enabled(&self) -> bool {
self.physics_enabled.load(Ordering::Acquire)
}
pub fn set_physics_enabled(&self, enabled: bool) {
self.physics_enabled.store(enabled, Ordering::Release)
}
pub fn physics_parameters(&self) -> IntegrationParameters {
self.physics.lock().integration_parameters
}
pub fn set_physics_parameters(&self, parameters: IntegrationParameters) {
self.physics.lock().integration_parameters = parameters;
}
pub fn add_joint(
&self,
object1: &Object,
object2: &Object,
data: impl Into<joints::GenericJoint>,
wake_up: bool,
) -> Result<ImpulseJointHandle, NoRigidBodyError> {
if let (Some(handle1), Some(handle2)) =
(object1.rigidbody_handle(), object2.rigidbody_handle())
{
Ok(self.physics.lock().impulse_joint_set.insert(
handle1,
handle2,
data.into().data,
wake_up,
))
} else {
Err(NoRigidBodyError)
}
}
pub fn get_joint(&self, handle: ImpulseJointHandle) -> Option<joints::GenericJoint> {
self.physics
.lock()
.impulse_joint_set
.get(handle)
.map(|joint| joints::GenericJoint { data: joint.data })
}
pub fn set_joint(
&self,
data: impl Into<joints::GenericJoint>,
handle: ImpulseJointHandle,
) -> Result<(), NoJointError> {
if let Some(joint) = self.physics.lock().impulse_joint_set.get_mut(handle) {
joint.data = data.into().data;
Ok(())
} else {
Err(NoJointError)
}
}
pub fn remove_joint(&self, handle: ImpulseJointHandle, wake_up: bool) {
self.physics
.lock()
.impulse_joint_set
.remove(handle, wake_up);
}
pub(crate) fn increment_id(&self) -> usize {
self.latest_object.fetch_add(1, Ordering::AcqRel) as usize
}
pub(crate) fn add_object(&self, id: usize, object: &NObject) {
self.objects_map.lock().insert(id, object.clone());
}
pub fn query_nearest_collider_at(&self, position: Vec2) -> Option<usize> {
let mut physics = self.physics.lock();
physics.update_query_pipeline();
let result = physics.query_pipeline.project_point(
&physics.rigid_body_set,
&physics.collider_set,
&position.into(),
true,
QueryFilter::default(),
);
if let Some((handle, _)) = result {
Some(physics.collider_set.get(handle).unwrap().user_data as usize)
} else {
None
}
}
pub fn cast_ray(
&self,
position: Vec2,
direction: Vec2,
time_of_impact: Real,
solid: bool,
) -> Option<usize> {
let mut physics = self.physics.lock();
physics.update_query_pipeline();
let result = physics.query_pipeline.cast_ray(
&physics.rigid_body_set,
&physics.collider_set,
&Ray::new(position.into(), direction.into()),
time_of_impact,
solid,
QueryFilter::default(),
);
if let Some((handle, _)) = result {
Some(physics.collider_set.get(handle).unwrap().user_data as usize)
} else {
None
}
}
pub fn cast_ray_and_get_normal(
&self,
position: Vec2,
direction: Vec2,
time_of_impact: Real,
solid: bool,
) -> Option<(usize, Vec2)> {
let mut physics = self.physics.lock();
physics.update_query_pipeline();
let result = physics.query_pipeline.cast_ray_and_get_normal(
&physics.rigid_body_set,
&physics.collider_set,
&Ray::new(position.into(), direction.into()),
time_of_impact,
solid,
QueryFilter::default(),
);
if let Some((handle, intersection)) = result {
Some((
physics.collider_set.get(handle).unwrap().user_data as usize,
intersection.normal.into(),
))
} else {
None
}
}
pub fn intersections_with_ray(
&self,
position: Vec2,
direction: Vec2,
time_of_impact: Real,
solid: bool,
) -> Vec<usize> {
let mut physics = self.physics.lock();
physics.update_query_pipeline();
let mut intersections = vec![];
let bodies = &physics.rigid_body_set;
let colliders = &physics.collider_set;
let filter = QueryFilter::default();
let mut callback = |handle| {
intersections.push(physics.collider_set.get(handle).unwrap().user_data as usize);
true
};
if direction.eq(&vec2(0.0, 0.0)) {
physics.query_pipeline.intersections_with_point(
bodies,
colliders,
&position.into(),
filter,
callback,
);
} else {
physics.query_pipeline.intersections_with_ray(
bodies,
colliders,
&Ray::new(position.into(), direction.into()),
time_of_impact,
solid,
filter,
|handle, _| callback(handle),
);
};
intersections
}
pub fn intersection_with_shape(&self, shape: Shape, position: (Vec2, f32)) -> Option<usize> {
let mut physics = self.physics.lock();
physics.update_query_pipeline();
let result = physics.query_pipeline.intersection_with_shape(
&physics.rigid_body_set,
&physics.collider_set,
&position.into(),
shape.0.as_ref(),
QueryFilter::default(),
);
result.map(|handle| physics.collider_set.get(handle).unwrap().user_data as usize)
}
pub fn intersections_with_shape(&self, shape: Shape, position: (Vec2, f32)) -> Vec<usize> {
let mut physics = self.physics.lock();
physics.update_query_pipeline();
let mut intersections = vec![];
let callback = |handle| {
intersections.push(physics.collider_set.get(handle).unwrap().user_data as usize);
true
};
physics.query_pipeline.intersections_with_shape(
&physics.rigid_body_set,
&physics.collider_set,
&position.into(),
shape.0.as_ref(),
QueryFilter::default(),
callback,
);
intersections
}
pub fn move_to(&self, object: &Object, index: usize) -> Result<(), ObjectError> {
let node = object.as_node().ok_or(ObjectError::Uninitialized)?;
let count = Self::count_children(&node);
if count < index {
return Err(ObjectError::Move(format!(
"This object can not be moved to {index}.\nYou can not go above {count}"
)));
} else {
Self::move_object_to(node, index);
}
Ok(())
}
pub fn move_up(&self, object: &Object) -> Result<(), ObjectError> {
let node = object.as_node().ok_or(ObjectError::Uninitialized)?;
let parent = Self::get_parent(&node);
let index = Self::find_child_index(&parent, &node);
if index == 0 {
return Err(ObjectError::Move(
"Object already on the top of the current layer.".to_string(),
));
} else {
Self::move_object_to(node, index - 1);
}
Ok(())
}
pub fn move_down(&self, object: &Object) -> Result<(), ObjectError> {
let node = object.as_node().ok_or(ObjectError::Uninitialized)?;
let parent = Self::get_parent(&node);
let count = Self::count_children(&node);
let index = Self::find_child_index(&parent, &node);
if count == index {
return Err(ObjectError::Move(format!(
"Object already at the bottom of the layer: {index}"
)));
} else {
Self::move_object_to(node, count + 1);
}
Ok(())
}
pub fn move_to_top(&self, object: &Object) -> Result<(), ObjectError> {
let node = object.as_node().ok_or(ObjectError::Uninitialized)?;
Self::move_object_to(node, 0);
Ok(())
}
pub fn move_to_bottom(&self, object: &Object) -> Result<(), ObjectError> {
let node = object.as_node().ok_or(ObjectError::Uninitialized)?;
let count = Self::count_children(&node) - 1;
Self::move_object_to(node, count);
Ok(())
}
fn get_parent(object: &NObject) -> NObject {
object.lock().parent.clone().unwrap().upgrade().unwrap()
}
fn find_child_index(parent: &NObject, object: &NObject) -> usize {
let parent = parent.lock();
parent
.children
.clone()
.into_iter()
.position(|x| Arc::ptr_eq(&x, object))
.unwrap()
}
fn count_children(object: &NObject) -> usize {
let parent = Self::get_parent(object);
let parent = parent.lock();
parent.children.len()
}
fn move_object_to(src: NObject, dst: usize) {
let parent = src.lock().parent.clone().unwrap().upgrade().unwrap();
let mut parent = parent.lock();
let index = parent
.children
.clone()
.into_iter()
.position(|x| Arc::ptr_eq(&x, &src))
.unwrap();
parent.children.swap(index, dst);
}
pub fn children_count(&self, parent: &Object) -> Result<usize, ObjectError> {
let node = parent.as_node().ok_or(ObjectError::Uninitialized)?;
Ok(Self::count_children(&node))
}
}
impl PartialEq for Layer {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.root, &other.root)
&& Arc::ptr_eq(&self.camera, &other.camera)
&& Arc::ptr_eq(&self.objects_map, &other.objects_map)
}
}
impl Eq for Layer {}
impl std::hash::Hash for Layer {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.root).hash(state);
Arc::as_ptr(&self.camera).hash(state);
Arc::as_ptr(&self.objects_map).hash(state);
}
}