Ah, nevermind, I stumbled on
this old thread where jyk and alvaro provided the exact answer I was looking for.
The two code snippets I've now come up with are
Alvaro's method:
Matrix3x3 createQuickLookAtMatrix(const Vector3 &forward) { Vector3 right; if(abs(vector.X) < abs(vector.Y)) { if(abs(vector.Z) < abs(vector.X)) { right = Vector3(vector.Y, -vector.X, 0.0f); } else { right = Vector3(0.0f, vector.Z, -vector.Y); } } else { if(abs(vector.Z) < abs(vector.Y)) { right = Vector3(vector.Y, -vector.X, 0.0f); } else { right = Vector3(vector.Z, 0.0f, -vector.X); } } Vector3 up = Vector3.Cross(forward, right); return Matrix3x3(right, up, forward);}
Jyk's method:
Matrix3x3 createQuickLookAtMatrix(const Vector3 &forward) { Vector3 crossVector; if(abs(vector.X) < abs(vector.Y)) { if(abs(vector.Z) < abs(vector.X)) { crossVector = Vector3.UnitZ; } else { crossVector = Vector3.UnitX; } } else { if(abs(vector.Z) < abs(vector.Y)) { crossVector = Vector3.UnitZ; } else { crossVector = Vector3.UnitY; } } Vector3 right = Vector3.Cross(vector, crossVector); Vector3 up = Vector3.Cross(vector, right); return Matrix3x3(right, up, forward);}
I don't quite understand yet why alvaro's method seems to work, but if it's safe, maybe a permutation of it could also yield the 'up' vector without resorting to a cross product at all.
Some unit tests with unit vectors, diagonal vectors and more seem to indicate it works.
So, back to the original question, can anything think of a vector for which alvaro's method wouldn't work?