Skip to main content

amethyst_physics/
objects.rs

1//! This module contains all object types (like the physics tags) that are exposed trough `amethyst_physics`.
2
3use std::sync::{Arc, RwLock};
4
5use amethyst_core::{
6    ecs::{Component, DenseVecStorage, FlaggedStorage},
7    math::Isometry3,
8};
9
10use crate::PtReal;
11
12macro_rules! define_opaque_object {
13    ($what:ident, $gc_name:ident) => {
14        /// This is an opaque ID that is created by a physics server.
15        /// Create this Opaque ID manually is not safe, for this reason is marked as so.
16        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17        pub enum $what {
18            #[allow(missing_docs)]
19            U32(u32),
20            #[allow(missing_docs)]
21            U64(u64),
22
23            #[allow(missing_docs)]
24            U32U32(u32, u32),
25            #[allow(missing_docs)]
26            U64U64(u64, u64),
27
28            #[allow(missing_docs)]
29            UsizeU32(usize, u32),
30            #[allow(missing_docs)]
31            UsizeU64(usize, u64),
32        }
33
34        impl $what {
35            #[allow(missing_docs)]
36            pub unsafe fn new_u32(a: u32) -> Self {
37                $what::U32(a)
38            }
39
40            #[allow(missing_docs)]
41            pub unsafe fn new_u64(a: u64) -> Self {
42                $what::U64(a)
43            }
44
45            #[allow(missing_docs)]
46            pub unsafe fn new_u32u32(a: u32, b: u32) -> Self {
47                $what::U32U32(a, b)
48            }
49
50            #[allow(missing_docs)]
51            pub unsafe fn new_u64u64(a: u64, b: u64) -> Self {
52                $what::U64U64(a, b)
53            }
54
55            #[allow(missing_docs)]
56            pub unsafe fn new_usizeu32(a: usize, b: u32) -> Self {
57                $what::UsizeU32(a, b)
58            }
59
60            #[allow(missing_docs)]
61            pub unsafe fn new_usizeu64(a: usize, b: u64) -> Self {
62                $what::UsizeU64(a, b)
63            }
64        }
65
66        impl PhysicsTag for $what {
67            fn request_resource_removal(&mut self, gc: &mut PhysicsGarbageCollector) {
68                gc.$gc_name.push(*self);
69            }
70        }
71    };
72}
73
74define_opaque_object!(PhysicsRigidBodyTag, bodies);
75define_opaque_object!(PhysicsAreaTag, areas);
76define_opaque_object!(PhysicsShapeTag, shapes);
77define_opaque_object!(PhysicsJointTag, joints);
78
79/// This is used only to perform the setup of these storages.
80///
81/// The setup happens in the `PhysicsBundle`.
82pub(crate) type PhysicsSetupStorages<'a> = (
83    amethyst_core::ecs::ReadStorage<'a, PhysicsHandle<PhysicsRigidBodyTag>>,
84    amethyst_core::ecs::ReadStorage<'a, PhysicsHandle<PhysicsAreaTag>>,
85    amethyst_core::ecs::ReadStorage<'a, PhysicsHandle<PhysicsShapeTag>>,
86    amethyst_core::ecs::ReadStorage<'a, PhysicsHandle<PhysicsJointTag>>,
87);
88
89/// This trait must be implemented for each structure that want to use the PhysicsHandle.
90pub trait PhysicsTag: Copy + std::fmt::Debug + Sync + Send + Sized + 'static {
91    /// This function is called when the *tag* is no more owned by any `PhysicsHandle`.
92    fn request_resource_removal(&mut self, gc: &mut PhysicsGarbageCollector);
93}
94
95/// The physics handle is used to track the physics resource lifetime.
96/// Indeed you don't have to care about dropping resources (life a RigidBody or a Shape) because
97/// they are automatically cleaned out once all PhysicsHandle to that object are dropped.
98///
99/// Worth to mention that you can store these handle anywhere, and the GC will always take care of
100/// its dropping.
101///
102/// If you need a copy of this resource you can simply use the function `clone()`.
103///
104/// All Physics Servers APIs want to deal directly with the PhysicsTag.
105/// Use the method `get()` to retrieve it.
106/// Keep in mind that it's lifetime is not tracked by the GC, thus is not a replacement of the PhysicsHandle.
107pub struct PhysicsHandle<T: PhysicsTag> {
108    tag_container: Arc<PhysicsTagContainer<T>>,
109}
110
111impl<T: PhysicsTag> PhysicsHandle<T> {
112    /// Creates new `PhysicsHandle`.
113    ///
114    /// This function must be called only by a physics backend.
115    pub fn new(tag: T, garbage_collector: Arc<RwLock<PhysicsGarbageCollector>>) -> Self {
116        PhysicsHandle {
117            tag_container: Arc::new(PhysicsTagContainer {
118                tag,
119                garbage_collector,
120            }),
121        }
122    }
123
124    /// Returns the PhysicsTag
125    /// Keep in mind that this doesn't alter the resource lifetime in anyway.
126    pub fn get(&self) -> T {
127        self.tag_container.tag
128    }
129}
130
131impl<T: PhysicsTag> std::fmt::Debug for PhysicsHandle<T> {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        write!(
134            f,
135            "PhysicsHandle{{\n   tag = {:?}\n   owner = {}\n   weak = {}\n}};",
136            self.get(),
137            Arc::strong_count(&self.tag_container),
138            Arc::weak_count(&self.tag_container)
139        )
140    }
141}
142
143impl<T: PhysicsTag> Clone for PhysicsHandle<T> {
144    fn clone(&self) -> Self {
145        PhysicsHandle {
146            tag_container: self.tag_container.clone(),
147        }
148    }
149}
150
151impl<T: PhysicsTag> Component for PhysicsHandle<T> {
152    type Storage = FlaggedStorage<PhysicsHandle<T>, DenseVecStorage<PhysicsHandle<T>>>;
153}
154
155/// This container holds both the Tag and the garbage collector.
156/// When this container is dropped it requests the dropping of the resource to the GC.
157///
158/// Thanks to the container, we can have many `PhysicsHandle` which all points to the same data.
159/// This mechanism, make sure to avoid too much useless copy and give a simple way to notify the GC
160/// only once, even if there are more handles of a resource.
161///
162/// The function `request_resource_removal`, that notify the GC is implemented per `PhysicsTag` to
163/// allow custom signaling operations depending on the tag type.
164struct PhysicsTagContainer<T: PhysicsTag> {
165    tag: T,
166    garbage_collector: Arc<RwLock<PhysicsGarbageCollector>>,
167}
168
169impl<T: PhysicsTag> std::ops::Drop for PhysicsTagContainer<T> {
170    fn drop(&mut self) {
171        let mut gc = self.garbage_collector.write().unwrap();
172        self.tag.request_resource_removal(&mut gc);
173    }
174}
175
176/// This garbage collector is used to store all the PhysicsTags to whom its associated handle get dropped.
177///
178/// The main benefit to use the Garbage Collector is that each PhysicsServer can implement its own destructor
179/// pipeline.
180/// Another benefit is that the user can store the PhysicsHandles even as resource or as prefer.
181///
182/// The alternative implementation was use a flagged storage.
183/// Using a FlaggedStorage would have been not only less powerful (since the objects are not tracked
184/// if stored elsewhere), but even more complicate.
185/// Indeed the FlaggedStorage has an handy Event system, which returns only the storage Index of the
186/// associated event.
187/// What this mean in practice is that you don't have access to PhysicsTag ID because the Index get
188/// removed and the only way would have been re implement a new storage with the capability to return
189/// PhysicsTag on component drop.
190/// Also the destruction pipeline is dictated by `amethyst_physics` to each physics backend.
191///
192/// Considering the above the GC seems a better way.
193#[derive(Debug)]
194pub struct PhysicsGarbageCollector {
195    /// List of body no more used.
196    pub bodies: Vec<PhysicsRigidBodyTag>,
197    /// List of areas no more used.
198    pub areas: Vec<PhysicsAreaTag>,
199    /// List of shapes no more used.
200    pub shapes: Vec<PhysicsShapeTag>,
201    /// List of joints no mor used.
202    pub joints: Vec<PhysicsJointTag>,
203}
204
205impl Default for PhysicsGarbageCollector {
206    fn default() -> Self {
207        PhysicsGarbageCollector {
208            bodies: Vec::new(),
209            areas: Vec::new(),
210            shapes: Vec::new(),
211            joints: Vec::new(),
212        }
213    }
214}
215
216/// This component allows to resolve an `Entity` transformation during the physics sub stepping.
217///
218/// Each physics sub step, the `RigidBody` moves, and if you have an `Area` attached to it, or a
219/// kinematic body, is it necessary to move it also each sub step, in order to have a  correct physics behaviour.
220///
221/// This component is useful only for game play implementation purposes, so it's not necessary in any way
222/// for rendering purposes.
223///
224/// Is necessary to also use the component `Parent`.
225#[allow(missing_debug_implementations)]
226pub struct PhysicsAttachment<N: PtReal> {
227    /// This is a cache parameter used to remember the actual global transform of the `Entity`.
228    pub(crate) cache_world_transform: Isometry3<N>,
229}
230
231impl<N: PtReal> Component for PhysicsAttachment<N> {
232    type Storage = DenseVecStorage<Self>;
233}
234
235impl<N: PtReal> Default for PhysicsAttachment<N> {
236    fn default() -> Self {
237        PhysicsAttachment {
238            cache_world_transform: Isometry3::identity(),
239        }
240    }
241}
242
243/// Collision Group which ID can go from 0 to 29 (inclusive)
244#[derive(Debug, Copy, Clone)]
245pub struct CollisionGroup(u8);
246
247/// The default collision group is 1.
248impl Default for CollisionGroup {
249    fn default() -> Self {
250        CollisionGroup(1u8)
251    }
252}
253
254impl CollisionGroup {
255    /// Create a new collisionGroup, panics if the submitted number is more than 29
256    pub fn new(group: u8) -> Self {
257        assert!(group < 29);
258        CollisionGroup(group)
259    }
260
261    /// Returns the group id
262    pub fn get(self) -> u8 {
263        self.0
264    }
265}