📜 ⬆️ ⬇️

How physics simulation works in games using the example of Bullet Physics



Many modern games use physical simulation. The first acquaintance with the physics engine is usually promising. The ball jumps away from the cube and rolls down the sloping, but in the process of work, when the project is almost ready, it turns out that something does not work as desired. Here you can ruin a lot of time.

And if nothing can be done about Nvidia Physix (hi Unity3D) except for a couple of parameters, Bullet Physics and Box2D are available in source codes, and from hopelessness you start to understand how things work and work out.
')
Dealing with how things work is very useful. All game physics engines are very similar. They are all impulse base, which means that in each simulation frame all (forces) are translated into impulses, impulses are added, divided into mass and added to the velocities (linear and rotational) of the object, the speed determines how much the object shifts in the current simulation frame.

And then the question arises, if everything is so simple, how is it possible that you can put cubes in a column and they are calm and do not fly away?

I will show that a small piece of code is responsible for all this magic!

I assume that the reader already knows that in the physics engine, there are two main types of simulated bodies: a rigid body (an elastic body, moves and has a masa) and a static body (a fixed body, is motionless and has no mass). Kinematic and soft body are not worth attention.

Collision detection


All magic begins when two bodies touch each other. Bullet Physics uses the term collision.

The system must identify all bodies that touch each other in a given simulation frame. In each touch (collision) may participate 2 or more bodies. Finding all the possible touches can be very resource intensive.

Broadphase Collision Detection

The first step is to find the intersection of the AABB boxes. AABB - these are two points that describe the box inside which the object lies. The idea is that if two objects intersect, then their AABB boxes intersect.

This problem in BulletPhysics solves one of three algorithms. They are implemented as classes that implement btBroadphaseInterface .

1. The most obvious and ineffective is btSimpleBroadphase, it is a simple enumeration of all O (n ^ 2) pairs.

2. btAxisSweep3 works in a limited space. You specify the AABB box, inside which all objects are supposed to be located. This may seem inconvenient, but due to the fact that floating-point numbers with increasing values ​​lose accuracy, you still can not simulate anything at a distance of a million units as if you did it at a distance of 10 units from the center of coordinates (the unit is usually considered a meter).

3. btDbvtBroadphase organizes all the objects in two tree structures, one for static objects, the other for dynamic (rigid, kinematic) bodies.

Narrowphase Collision Detection

After finding the groups of objects whose AABB boxes have crossed, it checks whether there is an intersection in reality.

Here it should be remembered that all objects are given a shape (shape), it can be a ball, a cylinder, a box, a convex polyhedron for a rigid body, or just a polyhedron for a static body. There is also a compound form from the forms listed in the previous sentence.

For each pair, an algorithm for finding points and perpendiculars of the intersection is defined.



Decision


Ok, all intersections found, what to do next? Once the engine is impulse based on us, it is necessary to calculate such an impulse with which to shove two touching objects so that everything looks realistic.

Here I promised that there will be a small piece of code. It seems to me two years ago he was generally in three lines, but now he looks like this.

// Project Gauss Seidel or the equivalent Sequential Impulse void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimit(btRigidBody& body1,btRigidBody& body2,const btSolverConstraint& c) { btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; const btScalar deltaVel1Dotn = c.m_contactNormal.dot(body1.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetDeltaAngularVelocity()); const btScalar deltaVel2Dotn = -c.m_contactNormal.dot(body2.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetDeltaAngularVelocity()); deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; if (sum < c.m_lowerLimit) { deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; c.m_appliedImpulse = c.m_lowerLimit; } else { c.m_appliedImpulse = sum; } body1.internalApplyImpulse(c.m_contactNormal*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); body2.internalApplyImpulse(-c.m_contactNormal*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); } 

At the entrance we have two contacting bodies, a touch point with a perpendicular touch and, as a result, we have two impulses that should resolve the collision, make the boxes calmly and motionlessly lie one on top of the other, and the cylinder and the ball roll smoothly and realistically down the slope.

Good advice instead of parting


Touch this code is not worth it. For example, if you develop a game with racing cars, and the car will beat on the curb or wall, and it will behave unpredictably and not playable, then you should pay attention to the perpendicular at the touch point. It is the inconstancy and unpredictability of the perpendicular at the touch point that often determines the unpredictability of the simulation.

You looked into the soul of the modern physics engine and are now obliged to marry him.

Source: https://habr.com/ru/post/238925/


All Articles