Anatomy of a Bug
The model editor animation bug was the second-worst bug to hit Leadwerks Game Engine in all its history. Reported multiple times, this would cause animated models to discard triangles only in the model editor, only on Linux.
http://www.leadwerks.com/werkspace/topic/10856-model-editor-freaks-out/
http://www.leadwerks.com/werkspace/topic/12678-model-animation-vs-flashing-bodyparts/
Since our animation commands have worked solidly for years, I was at my wits' end trying to figure this out. I strongly suspected a driver bug having to do with sharing uniform buffers across multiple contexts, but the fact it happened on both AMD and Nvidia cards did not support that, or indicated the problem was more low-level within the Linux distro. An engineer from Nvidia wasn't able to find the cause. If correct, this would not be the first driver bug I have found and had confirmed, by the Nvidia, AMD, and Intel driver teams.
To make things even more difficult, the error only occurred in the release build. Debug builds could not be debugged because no error would occur!
It never even occurred to me that the actual bone matrix data could be inputted wrong until Leadwerks user Roland reported that the bug was occurring in his game. This was the first time anyone had reported the error was occurring anywhere but the model editor.
I finally determined that the actual bone matrices being sent to the animation shader contained many values of "-nan", meaning the negative form of "not a number". I was shocked. How could this possibly be when our animation commands have been completely reliable for years?
I started printing values out and finally traced the problem back to the Quaternion spherical linear interpolation, or Slerp function. Slerp is a function that smoothly interpolates between two quaternion rotations without the problem of gimbal lock. This is the code for the function:
void Quat::Slerp(const Quat& q, float a, Quat& result) { bool f = false; float b = 1.0f - a; float d = x*q.x + y*q.y + z*q.z + w*q.w; if (d<0.0f) { d = -d; f = true; } if (d<1.0f) { float om = Math::ACos(d); float si = Math::Sin(om); a = Math::Sin(a*om) / si; b = Math::Sin(b*om) / si; } if (f == true) a *= -1.0f; result.x = x*b + q.x*a; result.y = y*b + q.y*a; result.z = z*b + q.z*a; result.w = w*b + q.w*a; }
if (a==0.0f) { result.x = x; result.y = y; result.z = z; result.w = w; return; }
- 8
8 Comments
Recommended Comments