Continued from the previous entry.
In the previous entry, we identified a problem with attaching and detaching actors in a scene. In this post, we’ll look at the solution to the problem and then adapt the solution to the ResidualVM code.
In graphics, we usually think of posing an object as three rotations around the three axes, X, Y, and Z. These rotations are referred to as Euler Angles. Unfortunately, this isn’t always the best approach, because while this rotation is easy to conceptualize, the result is sometimes ambiguous depending on how those angles are interpreted. So, while the EMI engine uses Euler Angles when interacting with the user, internally, the engine uses Quaternions. While I won’t go into a discussion of Quaternions vs. Euler Angles, or the math behind them, understand that the output generated by the engine is going to involve the conversion of Euler Angles to Quaternions, some computation, then back into Euler Angles.
After examining the disassembled EMI source code and confirming that the engine did use Quaternions internally, I set up some test code using the Irrlicht Quaternion implementation because it was easy to use and easy to test different configurations with. The result of my testing can be found here. This implementation correctly computes the angles as reported by the EMI retail engine, as recorded in this spreadsheet (local copy can be found here).
Essentially, after some testing, I found that the EMI retail engine computes the new rotation angle by finding the Quaternion of the actor we’re attached to, finding the inverse, then multiplying that by the Quaternion of the actor that’s being attached. For detaching, the operation is reversed, where we take the attached actor’s Quaternion and multiply that by the inverse of the attached actor’s Quaternion.
So, what do we need to add to ResidualVM? In ResidualVM, the Quaternion implementation requires two new methods:
- getInverse() – Finds the Inverse of a Quaternion
- toEuler() – Returns the Euler Angles from a Quaternion
With these new methods, we can now implement the correct code for attaching and detaching actors in actor.cpp. However, I discovered that the ResidualVM Quaternion code wasn’t making much sense and the result was still incorrect. After discussing it with Botje, wjp, somaen and klusark on IRC, I decided to make the Quaternion implementation and usage consistent. This work will be described in the next entry.