amethyst_physics 0.2.0

The Amethyst Physics engine interface.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
use amethyst_core::{
    deferred_dispatcher_operation::*,
    ecs::{DispatcherBuilder, System, SystemData, World},
    SystemBundle, SystemDesc,
};
use amethyst_error::Error;
use log::info;

use crate::{
    objects::PhysicsSetupStorages,
    systems::{
        PhysicsAttachmentSystem, PhysicsBatchSystem, PhysicsStepperSystem, PhysicsSyncEntitySystem,
        PhysicsSyncJointSystem, PhysicsSyncShapeSystem, PhysicsSyncTransformFromSystem,
        PhysicsSyncTransformToSystem,
    },
    PhysicsTime,
};

/// To use the `amethyst_physics` crate is necessary to register the `PhysicsBundle` as show below.
///
/// ```rust,ignore
/// use amethyst_physics::PhysicsBundle;
/// use amethyst::amethyst_nphysics::NPhysicsBackend;
///
/// let game_data = GameDataBuilder::default()
///     .with_bundle(PhysicsBundle::<f32, NPhysicsBackend>::new()).unwrap()
///
/// ```
/// Is it possible to define the Physics Engine floating point precision and the [PhysicsBackend](./trait.PhysicsBackend.html);
/// additionally, the physics frame rate can be specified using the function `with_frames_per_seconds`.
///
/// # Dispatcher pipeline
///
/// ##### Behind the scenes
/// To have a stable execution, the physics stepping is executed with a constant frame rate;
/// and to be frame rate agnostic it keep tracks of the elapsed time.
/// **But don't worry**, the above statement means that a physics step can occur multiple times per
/// each frame.
/// So, when you have a `System` that interact with the physics, you have to register it using
/// the API provided by the `PhysicsBundle`; `amethyst_physics` will take care to execute your `System`s
/// at the right time.
///
/// ##### Pipeline
/// The physics pipeline is composed by three sections:
/// - **Pre physics** `with_pre_physics`
///
///     Executed just before any physics step. In this section of the pipeline you want to register
///     any `System` that will alter the simulation (like add a force or change a transform).
///
/// - **In physics** `with_in_physics`
///
///     The `System`s in this stage are executed in parallel with the physics stepping, and this section
///     is meant for all the `System`s that have to be executed each physics frame but doesn't depend
///     on its state.
///
/// - **Post physics** `with_post_physics`
///
///     The last section of the physics pipeline, is simply executed just after the physics stepping.
///     In this section, you want to register the `System`s that collects the physics states,
///     (like checking for volumes overlaps, or collision events).
///
/// # Parallel physics dispatching
/// `amethyst_physics` is designed to dispatch the physics in parallel with everything else, by default.
/// When you start to interact with it, you have to approach it correctly to maintain this property.
///
/// Some internal parts are being explained, and if the physics of your game is not so heavy, or you
/// are not yet confortable with `amethyst_physics`, you can just skip this section.
///
/// The physics pipeline, just explained above, groups all the `System`s that interact with the physics.
/// We can consider all these `System`s, a single group; let's call it `PhysicsBatch`.
///
/// Like any other `System` in `Amethyst`, the `PhysicsBatch` is dispatched by `shred`, this mean that
/// if we make sure that its resources are not used by any other `System`, registered after it, them will
/// run in parallel.
///
/// ##### Synchronization
/// The main concept is easy, but let's see what it mean in practice.
///
/// When nothing is registered in the `PhysicsBatch`, the only resource that can potentially cause problems
/// is the [Transform Component].
/// To avoid using the [Transform Component] inside the `PhysicsBatch`; `amethyst_physics` defines the
/// `PhysicsSyncSystem`, that executed at the begining of each frame, it will take care to copy the
/// transforms from the physics to `Amethyst`. Leaving the physics and the rendering untied and free
/// to be executed in parallel.
///
/// The dispatcher looks like this:
/// ```ignore
/// |--Sync--||-------------PhysicsBatch------------|
///           |--Any other System--||-- Rendering --|
/// ```
///
/// Taking as example a race game, you may want to display a scratch on the car when it hits something.
/// To ensure that the physics runs in parallel, you want to register the `System` that checks for the
/// collision, before the `PhysicsBatch` (similarly as was explained above).
///
/// The dispatcher looks like this:
/// ```ignore
/// |--Sync--|         |-------------PhysicsBatch------------|
/// |--CollisionSync--||--Any other System--||-- Rendering --|
/// ```
///
/// That's it.
///
/// Following this small trick, the physics will run in parallel with anything else!.
///
/// ## Small TODO to highlight
/// I'm confident that this section will be removed ASAP, but for the sake of completeness I've to
/// mention a problem.
///
/// The above section, which explains how to make the physics runs in parallel, due to a small
/// Amethyst's design problem, is lying.
/// Indeed, is not possible to run the physics and the rendering in parallel, because they
/// are in two different pipelines.
///
/// So the dispatcher looks like:
/// ```ignore
/// |--Sync--|         |-------------PhysicsBatch------------|
/// |--CollisionSync--||--Any other System--|
///                                                            |-- Rendering --|
/// ```
///
/// To know more about it, check this: [https://github.com/AndreaCatania/amethyst/issues/2](https://github.com/AndreaCatania/amethyst/issues/2)
///
/// However, I'm confident that this will be solved soon, and for this reason the above section is
/// written as if this problem doesn't exist.
///
/// [Transform component]: ../amethyst_core/transform/components/struct.Transform.html
#[allow(missing_debug_implementations)]
pub struct PhysicsBundle<'a, 'b, N: crate::PtReal, B: crate::PhysicsBackend<N>> {
    phantom_data_float: std::marker::PhantomData<N>,
    phantom_data_backend: std::marker::PhantomData<B>,
    physics_time: PhysicsTime,
    physics_builder: DispatcherBuilder<'a, 'b>,
    pre_physics_dispatcher_operations: Vec<Box<dyn DispatcherOperation<'a, 'b>>>,
    in_physics_dispatcher_operations: Vec<Box<dyn DispatcherOperation<'a, 'b>>>,
    post_physics_dispatcher_operations: Vec<Box<dyn DispatcherOperation<'a, 'b>>>,
}

macro_rules! define_setters{
    ($(#[$doc_sy:meta])* $with_system:ident, $add_system:ident, $(#[$doc_sd:meta])* $with_system_desc:ident, $add_system_desc:ident, $(#[$doc_bund:meta])* $with_bundle:ident, $add_bundle:ident, $(#[$doc_bar:meta])* $with_barrier:ident, $add_barrier:ident, $vec:ident) => {
        $(#[$doc_sy])*
        pub fn $with_system<S>(
            mut self,
            system: S,
            name: String,
            dependencies: Vec<String>,
        ) -> Self
        where
            S: for<'c> System<'c> + 'static + Send,
        {
            self.$add_system(system, name, dependencies);
            self
        }

        $(#[$doc_sy])*
        pub fn $add_system<S>(
            &mut self,
            system: S,
            name: String,
            dependencies: Vec<String>,
        ) where
            S: for<'c> System<'c> + 'static + Send,
        {
            self.$vec
                .push(Box::new(AddSystem {
                    system,
                    name,
                    dependencies,
                }) as Box<dyn DispatcherOperation<'a, 'b>>);
        }

        $(#[$doc_sd])*
        pub fn $with_system_desc<SD, S>(
            mut self,
            system_desc: SD,
            name: String,
            dependencies: Vec<String>,
        ) -> Self
        where
            SD: SystemDesc<'a, 'b, S> + 'static,
            S: for<'s> System<'s> + 'static + Send,
        {
            self.$add_system_desc(system_desc, name, dependencies);
            self
        }

        $(#[$doc_sd])*
        pub fn $add_system_desc<SD, S>(
            &mut self,
            system_desc: SD,
            name: String,
            dependencies: Vec<String>,
        ) where
            SD: SystemDesc<'a, 'b, S> + 'static,
            S: for<'s> System<'s> + 'static + Send,
        {
            self.$vec
                .push(Box::new(AddSystemDesc::<SD, S>{
                    system_desc,
                    name,
                    dependencies,
                    marker: std::marker::PhantomData::<S>,
                }) as Box<dyn DispatcherOperation<'a, 'b>>);
        }

        $(#[$doc_bund])*
        pub fn $with_bundle<BUND>(
            mut self,
            bundle: BUND,
        ) -> Self
        where
            BUND: SystemBundle<'a, 'b> + 'static + Send,
        {
            self.$add_bundle(bundle);
            self
        }

        $(#[$doc_bund])*
        pub fn $add_bundle<BUND>(
            &mut self,
            bundle: BUND,
        ) where
            BUND: SystemBundle<'a, 'b> + 'static + Send,
        {
            self.$vec
                .push(Box::new(AddBundle {
                    bundle,
                }) as Box<dyn DispatcherOperation<'a, 'b>>);
        }

        $(#[$doc_bar])*
        pub fn $with_barrier(
            mut self,
        ) -> Self {
            self.$add_barrier();
            self
        }

        $(#[$doc_bar])*
        pub fn $add_barrier(
            &mut self,
        ){
            self.$vec
                .push(Box::new(AddBarrier {}) as Box<dyn DispatcherOperation<'a, 'b>>);
        }
    }
}

impl<'a, 'b, N: crate::PtReal, B: crate::PhysicsBackend<N>> Default
    for PhysicsBundle<'a, 'b, N, B>
{
    fn default() -> Self {
        Self::new()
    }
}

impl<'a, 'b, N: crate::PtReal, B: crate::PhysicsBackend<N>> PhysicsBundle<'a, 'b, N, B> {
    /// Creates new `PhysicsBundle`
    pub fn new() -> Self {
        Self {
            phantom_data_float: std::marker::PhantomData,
            phantom_data_backend: std::marker::PhantomData,
            physics_time: PhysicsTime::default(),
            physics_builder: DispatcherBuilder::new(),
            pre_physics_dispatcher_operations: Vec::new(),
            in_physics_dispatcher_operations: Vec::new(),
            post_physics_dispatcher_operations: Vec::new(),
        }
    }

    /// Set the physics frames per seconds.
    ///
    /// This is just an helper function, and you can modify it later in the game.
    ///
    /// Check the [PhysicsTime](./struct.PhysicsTime.html)
    pub fn with_frames_per_seconds(mut self, frames_per_seconds: u32) -> Self {
        self.physics_time.set_frames_per_seconds(frames_per_seconds);
        self
    }

    /// Set the physics frames per seconds.
    ///
    /// This is just an helper function, and you can modify it later in the game.
    ///
    /// Check the [PhysicsTime](./struct.PhysicsTime.html)
    pub fn set_frames_per_seconds(mut self, frames_per_seconds: u32) {
        self.physics_time.set_frames_per_seconds(frames_per_seconds);
    }

    /// Set the physics max sub steps.
    ///
    /// This controls how much physics step can be executed in a single frame. It's used to avoid
    /// spiral performance degradation.
    /// Set it to an too high value, will make this mechanism ineffective, and a too low value will make the physics unstable.
    /// Is advised to keep the default
    ///
    /// This is just an helper function, and you can modify it later in the game.
    /// Check the [PhysicsTime](./struct.PhysicsTime.html)
    pub fn with_max_sub_steps(mut self, max_sub_steps: u32) -> Self {
        self.physics_time.set_max_sub_steps(max_sub_steps);
        self
    }

    /// Set the physics max sub steps.
    ///
    /// This controls how much physics step can be executed in a single frame. It's used to avoid
    /// spiral performance degradation.
    /// Set it to an too high value, will make this mechanism ineffective, and a too low value will make the physics unstable.
    /// Is advised to keep the default
    ///
    /// This is just an helper function, and you can modify it later in the game.
    /// Check the [PhysicsTime](./struct.PhysicsTime.html)
    pub fn set_max_sub_steps(mut self, max_sub_steps: u32) {
        self.physics_time.set_max_sub_steps(max_sub_steps);
    }

    define_setters!(
        /// Add a `System` to the **Pre physics** pipeline.
        ///
        /// This pipeline is executed before the physics step. Register here all the `System`s that
        /// want to control the physics (like add force, set transform).
        with_pre_physics,
        add_pre_physics,
        /// Add a `SystemDesc` to the **Pre physics** pipeline.
        ///
        /// This pipeline is executed before the physics step. Register here all the `System`s that
        /// want to control the physics (like add force, set transform).
        with_system_desc_pre_physics,
        add_system_desc_pre_physics,
        /// Add a `Bundle` to the **Pre physics** pipeline.
        ///
        /// This pipeline is executed before the physics step. Register here all the `System`s that
        /// want to control the physics (like add force, set transform).
        with_bundle_pre_physics,
        add_bundle_pre_physics,
        /// Add a `Barrier` to the **Pre physics** pipeline.
        ///
        /// This pipeline is executed before the physics step. Register here all the `System`s that
        /// want to control the physics (like add force, set transform).
        with_barrier_pre_physics,
        add_barrier_pre_physics,
        pre_physics_dispatcher_operations
    );

    define_setters!(
        /// Add a `System` to the **In physics** pipeline.
        ///
        /// This pipeline is executed along the physics step.
        /// Register here all the `System`s that doesn't interact with the physics, but need to be
        /// executed each physics frame.
        with_in_physics,
        add_in_physics,
        /// Add a `SystemDesc` to the **In physics** pipeline.
        ///
        /// This pipeline is executed along the physics step.
        /// Register here all the `System`s that doesn't interact with the physics, but need to be
        /// executed each physics frame.
        with_system_desc_in_physics,
        add_system_desc_in_physics,
        /// Add a `Bundle` to the **In physics** pipeline.
        ///
        /// This pipeline is executed along the physics step.
        /// Register here all the `System`s that doesn't interact with the physics, but need to be
        /// executed each physics frame.
        with_bundle_in_physics,
        add_bundle_in_physics,
        /// Add a `Barrier` to the **In physics** pipeline.
        ///
        /// This pipeline is executed along the physics step.
        /// Register here all the `System`s that doesn't interact with the physics, but need to be
        /// executed each physics frame.
        with_barrier_in_physics,
        add_barrier_in_physics,
        in_physics_dispatcher_operations
    );

    define_setters!(
        /// Add a `System` to the **Post physics** pipeline.
        ///
        /// This pipeline is executed after the physics step. Register here all the `System`s that
        /// fetch the physics events.
        with_post_physics,
        add_post_physics,
        /// Add a `SystemDesc` to the **Post physics** pipeline.
        ///
        /// This pipeline is executed after the physics step. Register here all the `System`s that
        /// fetch the physics events.
        with_system_desc_post_physics,
        add_system_desc_post_physics,
        /// Add a `Bundle` to the **Post physics** pipeline.
        ///
        /// This pipeline is executed after the physics step. Register here all the `System`s that
        /// fetch the physics events.
        with_bundle_post_physics,
        add_bundle_post_physics,
        /// Add a `Barrier` to the **Post physics** pipeline.
        ///
        /// This pipeline is executed after the physics step. Register here all the `System`s that
        /// fetch the physics events.
        with_barrier_post_physics,
        add_barrier_post_physics,
        post_physics_dispatcher_operations
    );
}

impl<N, B> SystemBundle<'static, 'static> for PhysicsBundle<'static, 'static, N, B>
where
    N: crate::PtReal,
    B: crate::PhysicsBackend<N> + Send + 'static,
{
    fn build(
        self,
        world: &mut World,
        builder: &mut DispatcherBuilder<'static, 'static>,
    ) -> Result<(), Error> {
        PhysicsSetupStorages::setup(world);

        world.insert(B::create_world());
        world.insert(self.physics_time);

        let physics_builder = {
            let mut physics_builder = self.physics_builder;

            // Register PRE physics operations
            physics_builder.add(
                PhysicsAttachmentSystem::default(),
                "physics_attachment",
                &[],
            );
            self.pre_physics_dispatcher_operations
                .into_iter()
                .try_for_each(|operation| operation.exec(world, &mut physics_builder))
                .unwrap_or_else(|e| {
                    panic!("Failed to setup the pre physics systems. Error: {}", e)
                });

            // Register IN physics operations
            physics_builder.add_barrier();
            physics_builder.add(PhysicsStepperSystem::<N>::new(), "", &[]);
            self.in_physics_dispatcher_operations
                .into_iter()
                .try_for_each(|operation| operation.exec(world, &mut physics_builder))
                .unwrap_or_else(|e| panic!("Failed to setup the in physics systems. Error: {}", e));

            // Register POST physics operations
            physics_builder.add_barrier();
            self.post_physics_dispatcher_operations
                .into_iter()
                .try_for_each(|operation| operation.exec(world, &mut physics_builder))
                .unwrap_or_else(|e| {
                    panic!("Failed to setup the post physics systems. Error: {}", e)
                });

            physics_builder
        };

        // TODO the transform bundle should be split.
        // The hierarchy computation should run here.
        // Then, should run the parenting resolution here.
        // And, most important, after the `PhysicsSyncTransformFrom` should run the Matrix computation.
        //
        // At that point the physics batch and the rendering `System`s could run in parallel, correctly
        // `Synchronized`.

        builder.add(
            PhysicsSyncEntitySystem::<N>::default(),
            "physics_sync_entity",
            &[],
        );
        builder.add(
            PhysicsSyncShapeSystem::<N>::default(),
            "physics_sync_shape",
            &[],
        );
        builder.add(
            PhysicsSyncTransformToSystem::<N>::new(),
            "physics_sync_transform_to",
            &[],
        );
        builder.add(
            PhysicsAttachmentSystem::default(), // Note, this is executed **also** here because it computes the parent calculation useful to `PhysicsSyncTransformFromSystem`.
            "physics_attachment",
            &["physics_sync_transform_to"],
        );
        builder.add(
            PhysicsSyncTransformFromSystem::<N>::new(),
            "physics_sync_transform_from",
            &["physics_sync_transform_to"],
        );
        builder.add(
            PhysicsSyncJointSystem::<N>::default(),
            "physics_sync_joint",
            &["physics_attachment"],
        );

        builder.add_batch::<PhysicsBatchSystem<'static, 'static, N>>(
            physics_builder,
            "physics_batch",
            &[
                "physics_sync_shape",
                "physics_sync_joint",
                "physics_sync_entity",
                "physics_sync_transform_to",
                "physics_sync_transform_from",
                "physics_attachment",
            ],
        );

        info!("Physics bundle registered.");

        Ok(())
    }
}