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}