Before talking about my progress in depth, I would summarize everything I did in this week below:
- Understood what are various approaches towards implementing a physics engine.
- Reviewed concepts of 3D math and calculus which will be required throughout the development of this engine.
- Reviewed concepts of physics such as laws of motion which would be required to implement physics for point masses.
- Implemented physics for point masses (particles), using Newton-Euler integration method and laws of motion.
- Tested the damping of the motion of particles and their behavior under uniform acceleration by logging their state on the console.
The first thing to consider while making a physics engine is how to approach it and what assumptions to operate under. The first choice is regarding how to treat objects. Whether it should treat objects as a whole rigid body or as a collection of point masses (a mass aggregate engine). Developing a mass aggregate engine has the advantage of not requiring to take care of rotations, for this reason, I will start with implementing a mass aggregate system which I would be extended into a rigid body system. The second thing to consider is how to handle contact resolution, either use an iterative approach to resolve contacts without accounting for their collective effect or use a Jacobian-based approach to resolve their collective influence. The second approach is computationally expensive and might not even succeed in all the cases, for the simplicity and speed of the first approach, I will be using it instead of this Jacobian-based approach. The third and last consideration is regarding whether to use forces or impulses to resolve contacts. Some engines use forces to resolve for contacts while using impulses for collisions. I will be approaching this engine by implementing both collisions and contacts as impulses since the math for impulses is easier than the math for handling forces. Also, this impulse-based approach is suitable in terms of flexibility and can be used as foundation for experimenting with other approaches (Source).
In a particle (point mass) system, each particle must have a position, velocity and acceleration which we will keep a track of. Refer to Particle.h inside the source code at the end of this blog post. Apart from the member variables mentioned above, each particle has a damping factor and a inverse of mass stored in it.
The damping factor is an alternate to drag in motion of any particle. This factor to the power of time passed since last update is multiplied by the velocity at the end of each velocity calculation in the integrator. Refer to the Integrate method in Particle.cpp. As in the equation, if the damping factor is 1, it means there will be no damping, and as per Newton’s first law, if there were no force acting on a particle which was in a state of motion, because of no damping, it will remain in the state of motion with the same velocity. Similarly, if the damping factor is 0, a particle will stop as soon as there is no force acting on it.
Each particle / point mass has a mass associated with it. However, we are storing the inverse of mass for several reasons. Firstly, all the equations we use, such as the second law of motion to integrate, employ inverse of mass, thus making our calculations faster. Secondly, practically there is implication of using infinite mass, i.e. something with so much mass that no force could possibly move it, if we use inverse of mass, then the expression evaluates to 0 and satisfies the second law of motion. Also, if m = 0, then this expression cannot be evaluated, this is sensible as well since there cannot be anything with practically no mass. If m = 0, then any force would make the acceleration infinite and this isn’t possible. Thus, to account for both calculation speed and theoretical logic, we store the inverse of mass inside Particle class, while making accessors and mutators for manipulating its value.
Lastly, lets discuss the integrator for which estimates the velocity and position for each particle given their uniform acceleration. As mentioned earlier, the method for integrator can be found in Particle.cpp.
Using integration on the expression for acceleration as per the second law, the position should be given by the formula in our integrator:
mPosition += mVelocity * deltaTime + mAcceleartion * deltaTime * deltaTime * 0.5;
However, since deltaTime, i.e. the time since last update is very small. Thus square of deltaTime is almost 0. For this reason, we can simply the equation as:
mPosition += mVelocity * deltaTime; (Used in the integrator code)
Also, by using the integration on second law for updating velocity, we have the formula,
ṗ’ = ṗ + at;
where, p is position, ṗ is velocity (dp / dt), a is acceleration and t is the amount of time passed.
We can update the equation above with damping factor as follows:
ṗ’ = ṗd + at;
However, the equation above is affected by the frame rate, thus we need to make the damping time dependent, we can further modift the equation as follows:
ṗ’ = ṗdt + at; (Used in the integrator code)
- Added Particle class with all the logic for particle physics explained above.
- Updated World class to add support for creation Particles. It also manages the memory for the particles added into the world.
- Added Body and Shape classes which will be used in the future when working on rigid bodies.