shown in other instead.
Simulation of Physics
When simulated, physics calculations are done in a number of stages. If physics is single-threaded, all stages go one after another, while multi-threaded physics can perform some of the operations in parallel.
Rate of Physics Simulation
Unigine physics module performs all its calculations during the render stage of the execution sequence. There are few things to point out about physics.
- It is simulated with its own fixed FPS, which does not depend on the rendering framerate. (The rendering FPS can also be capped by the physics one).
Do not set physics framerate too high; otherwise, it can cause rendering lags. If physics take more than 40 ms, further calculations are skipped.
- During each tick, a number of calculation iterations can be performed. This includes the full cycle of physics simulation:
There is no point in setting the number of iterations too high. Unigine checks whether the next iteration can be performed within 40 ms limit, and if not, simply skips it.
- flush() from the world script is called
- Collision detection is performed
- Joints are solved
Stages of Physics Simulation
Simulation of physics goes through a number of stages when it is updated each iteration. They are as follows.
- Physics flush
- Broad phase of collision detection
- Narrow phase of collision detection
- Synchronization of physics
In the performance profiler, the total time of physics simulation is displayed by the Physics counter.
1. Physics Flush
- Before anything else, a spatial tree is updated. After we have the up-to-date data regarding how all objects with physical bodies are positioned, it would be safe to transform them or calculate collisions.
- C++ API Plugin flush() is called, if there is such a function.
- Physics module calls the flush() of the world script. Here you can call all functions that handle physics simulation and interactions (and not only that, see the details on flush() usage and limitations).
2. Collision Detection: Broad Phase
After the script-based changes have been made (bodies with their shapes and joints were transformed according to game logic, physics-based callbacks were set, etc.), physics can be simulated.
- Within the Physical distance, all objects are found that have physical bodies. They will be simulated during the current physics tick. Make sure that the physical distance in your Unigine-based application is not too small, because physical interactions outside of it are not calculated, so objects freeze up. (However, even if one body from the island is found within the physical distance, the whole island would be simulated).
You can force to update nodes that are outside the Physical distance using addUpdateNode() function.
- Checking all pairs of objects for collision is too time consuming, especially if the scene is large. That is why potentially colliding objects are found based on a fast and rough test. If bodies intersect with their bounding volumes (which are shape-sized, and do not depend on nodes bounding volumes), or there are joints that connect them, such bodies are combined in a island. To put it short, all bodies are somehow connected (spatially or with joints), go into one island. That means, bodies inside one island can possibly interact with each other in this frame, while bodies in different islands would not collide for certain.
Basically, broad phase increases the efficiency of collision detection and decreases computational load. Before doing more precise and costly calculations, we can filter out pairs of objects that are positioned too far to collide.Non-colliding body dummy is skipped and not simulated unless it interacts with other bodies through joints.
- Unigine physics is deterministic. Bodies, shapes and joints are sorted inside islands. By that, we ensure that contacts will always be solved in the predefined order and visualization of physics in the world is fully repetitive (one one computer).
In the performance profiler you can find:
- The total time of broad phase is displayed by the PBroad counter.
- The number of islands is shown by the PIslands counter.
- The number of bodies is shown by the PBodies counter.
- The total number of joints is shown by the PJoints counter.
3. Collision Detection: Narrow Phase
During the narrow phase, exact collision tests are performed. In a single-threaded mode (when there are none or one separate physics thread) the simulation continues in the following way.
Shape-based collisions are found for bodies inside each island:
- Collisions between the physical objects: contact points between pairs of shapes are found.
- Collisions between the physical objects and static surfaces. If the surface is assigned a surface_base property (by default), it can also passively participate in physical interaction and prevent the physical object from going through it. In this case, contact points between the shape and surface polygons are computed.
Continuous Collision Detection
If a sphere or a capsule participates in the contact with any other shape or surface, continuous collision detection (CCD) is performed. Unigine takes velocities of the body, radius of its shape and calculates what contacts this body will have (during the current physics tick), assuming it continues its current trajectory. So, unlike the simple collision detection, contacts are analyzed not discretely, once per physics tick, but rather found for the whole frame.
In the performance profiler you can find:
- The total time of this narrow phase stage is displayed by the PNarrow counter.
- The number of contacts is displayed by the PContacts counter.
When collision has been detected, collision response is calculated, so that the bodies would gain new velocities.
- Right now bodies are prepared to participate in collisions: their found contacts are cached together with contacts from the previous frame — to ensure that they interact with each other properly.
In the performance profiler, the time of this stage is displayed by the PUpdate counter.
- Collision response for each body is calculated. Based on the gathered contact points data, Unigine computes the impulse a shape gets by collision: how the body changes its the motion after it has collided with another body or a surface. Contact points are solved in a pseudo-random order to achieve simulation stability and reproducibility.
- When contact responses are calculated, joints are solved. The impulses that joints give the bodies attached to them are computed: how according to the current state of the joint, the bodies should respond to keep the joint unbroken (i.e. based on their masses, linear and angular velocities, change their movement direction and orientation), and how that response affects the joint. For example, if the impulse exerted on a joint is too large, it is broken.
Joints are also solved in the pseudo-random order.Within one physics iteration, joints can be solved several times. The high number of joint iterations increase the precision of calculations, as well as computational load.
In the performance profiler, the total time of both collision response and joint solving stages is displayed by the PResponse counter.
- The results of contact and joint solving are accumulated and, finally, are applied to bodies. The coordinates of the bodies change according to their new linear and angular velocities.
In the performance profiler, the time of this stage is displayed by the PIntegrate counter.
In the performance profiler, the total time of simulation stage is displayed by the PSimulation counter.
5. Synchronization of Physics
Synchronization is the final stage of physics simulation. During the swap() in the Unigine main loop, physics module calls its internal flush() function. Bodies set their calculated transformations to objects. In the next frame, objects will be rendered in their new physics-based positions.
If visualizer options is enabled, shapes, joints or contacts of non-frozen bodies will be rendered.
Multi-Threaded Physics Simulation
Multi-threaded simulation of physics is run only when there are two or more physics threads (controlled through physics_threaded console variable).
- flush() from the world script is always performed in the main physics thread.
- After that, Unigine takes advantage of multiple CPUs during the narrow phase and simulation stage.
- As islands has been created, they can be safely handled in separate threads, as there are no contacts between them.
- Exact shape-based collisions are found in available threads.
- Then threaded island are synchronized in the main physics thread to exchange data about the current contacts and ones from the previous frames. It will ensure proper physical behavior of bodies.
- From there on, collision response and joints solving are again calculated per island in separate threads.
- Before physics is synchronized with the world, the engine waits for all threads to finish their calculations. When thread synchronization happens (during the swap stage of the Unigine main loop), physics is applied to nodes.