Err... Only rotation?
Yep. Right now it’s the most useful thing I’ve found about matrices, besides them being a fantastical mathematical object which you probably will have hours of fun multiplicating on paper.
But... I just have to give one more math concept : orthogonality.
Orthogonality is intuitively a graphical concept, but can be expressed theoretically as well. I prefer to start by the theoric approach.
If you take those two 2D vectors :
A = [ 1, 0 ] B = [ 0, 1 ]
No matter how hard you try, it is impossible to express B as a linear combination of A. By that I mean, you can’t add up a series of A’s and get B in return :
B = x * A <-- will never happen
Because of that, we can say that B and A are orthogonal. Geometrically, they’re also perpendicular.
But it goes worse. Take those three 3D vectors :
A = [ 5, 0, 0 ] B = [ 0, 0, 2 ] C = [ 0, 3, 0 ]
Here, it is impossible to express any vector in this set as a linear combination of the two others. In other words, you can’t add up a scaled version of A and a scaled version of B and get C in return, or any substitution :
A = x * B + y * C <-- won't happen B = x * A + y * C <-- nope C = x * A + y * B <-- sorry
And because of that, we can say that A, B and C are mutually orthogonal. They’re also perpendicular to each other, but that’s a bit harder to visualize.
X
axis
Y
axis
Z
axis
XZ
plane
XY
plane
ZY
plane
The axis actually are a visual representation of linear combinations.
X
axis is formed of any linear combination of the X unit vector (XAxis = a * [1, 0, 0]
)
Y
axis is formed of any linear combination of the Y unit vector (YAxis = a * [0, 1, 0]
)
Z
axis is formed of any linear combination of the Z unit vector (ZAxis = a * [0, 0, 1]
)
Same thing for the planes!
XZ
plane is formed of any combination of the X and Z unit vectors (XZPlane = a * [1, 0, 0) + b * [0, 0, 1]
)
XY
plane is formed of any combination of the X and Y unit vectors (XYPlane = a * [1, 0, 0) + b * [0, 1, 0]
)
ZY
plane is formed of any combination of the Z and Y unit vectors (ZYPlane = a * [0, 0, 1) + b * [0, 1, 0]
)
a
and b
in the formulas above are any scalar, so any single value at all.
Why did I do this? Take any plane. It is impossible to obtain a unit vector of the remaining axis within this plane. That is to say that the set formed of the three X, Y
and Z
unit vectors is mutually orthogonal.
Now take the three vectors that form the 3D rotation matrix, and its default untouched value :
{{ Right.x, Right.y, Right.z }, {{ 1, 0, 0 }, { Up.x, Up.y, Up.z }, = { 0, 1, 0 }, { Forward.x, Forward.y, Forward.z }} { 0, 0, 1 }}
That means that the set of unit vectors that is an untouched rotation matrix is mutually orthogonal!
Right = [ 1, 0, 0 ]
is mutually orthogonal to Up
and Forward
Up = [ 0, 1, 0 ]
is mutually orthogonal to Right
and Forward
Forward = [ 0, 0, 1 ]
is mutually orthogonal to Up
and Right
And even better, it stays true whatever the rotation matrix is. It’s logical after all... whereever you’re looking at, the orthogonality and perpendicularity stays between each other.
Even if you didn’t understand the process, as long as you understand the conclusion : a rotation matrix is formed of three mutually orthogonal vectors. I know I went fast on this, yet said alot of stuff you don’t necessarily need to know.
But here’s why I told you about orthogonal sets in the first place : the cross product operation.
The cross product (the symbol is simply X) is an operation whose definition you don’t really need to know. Too much information is like not enough.
Its effect is that if you feed it two vectors, it will return a vector that is orthogonal to those two vectors. And it is easy to guess the direct uses of this property.
TVVec3Cross
. As for its uses... A section break please.
Here’s something that is heard every once in a while in the forum : “How do I align a mesh to the landscape?”
There is a trigonometry approach. There always is a trig approach, with tangents and arc-tangents, sinus and cosinus, pythagoras here and there and so on. But there is also a simpler matrix-based approach!
What are those again? The Up
vector, the Right
vector and the Forward
vector.
Up
vector is the landscape normal where the mesh stands!
Right
and Forward
vectors should be as close as possible to the current vectors, since we don’t want to rotate the mesh around the Y
axis, or as little as possible.
Like in many situations, we have one fixed vector, and two tentative vectors. We have two choices :
Right
and obtain Forward
;
Forward
and obtain Right
.
Since the current Forward
and Right
vectors (from the current rotation matrix) are orthogonal, taking one or the other will give the same result.
I chose to work with Forward
since it’s more intuitive; we want to look into the same direction as before, as much as possible.
C# version :
// Get the landscape normal, store it in the top vector TV_3DVECTOR TopVector = Landscape.NormalAt(Position.x, Position.z); // Get the current rotation matrix's vector -- could also be done with GetBasisVectors TV_3DMATRIX CurrentRotationMatrix = Mesh.RotationMatrix(); // Get the Forward vector out of the matrix, store it in the tentative Forward vector TV_3DVECTOR TentativeForwardVector = new TV_3DVECTOR(CurrentRotationMatrix.m31, CurrentRotationMatrix.m32, CurrentRotationMatrix.m33); TV_3DVECTOR RightVector = new TV_3DVECTOR(), ForwardVector = new TV_3DVECTOR(); // Use the cross product to get the actual Right vector Maths.TVVec3Cross(ref RightVector, TopVector, TentativeForwardVector); // Re-use the cross product to get the actual Forward vector; one that is really orthogonal to the two others Maths.TVVec3Cross(ref ForwardVector, RightVector, TopVector); // Normalize the two calculated vectors to avoid a scaling rotation matrix! Maths.TVVec3Normalize(ref RightVector, RightVector); Maths.TVVec3Normalize(ref ForwardVector, ForwardVector); // Construct the rotation matrix from the vectors CurrentMatrix.m11 = RightVector.x; CurrentMatrix.m12 = RightVector.y; CurrentMatrix.m13 = RightVector.z; CurrentMatrix.m21 = TopVector.x; CurrentMatrix.m22 = TopVector.y; CurrentMatrix.m23 = TopVector.z; CurrentMatrix.m31 = ForwardVector.x; CurrentMatrix.m32 = ForwardVector.y; CurrentMatrix.m33 = ForwardVector.z; // And assign it Mesh.RotationMatrix = CurrentMatrix;
VB.Net version (thanks to Hawthorne!) :
Function BindMatrixToLand(ByVal InMatrix As TV_3DMATRIX, ByVal Scale As TV_3DVECTOR, ByVal Position As TV_3DVECTOR) As TV_3DMATRIX With InMatrix Dim TopVector As TV_3DVECTOR = TVLandscapeObject.GetNormal(.m41, .m43) Dim TentativeForwardVector As New TV_3DVECTOR(.m31, .m32, .m33) Dim RightVector As New TV_3DVECTOR() Dim ForwardVector As New TV_3DVECTOR() MathLibrary.TVVec3Cross(RightVector, TopVector, TentativeForwardVector) MathLibrary.TVVec3Cross(ForwardVector, RightVector, TopVector) MathLibrary.TVVec3Normalize(RightVector, RightVector) MathLibrary.TVVec3Normalize(ForwardVector, ForwardVector) .m11 = RightVector.x * Scale.x .m12 = RightVector.y * Scale.y .m13 = RightVector.z * Scale.z .m21 = TopVector.x * Scale.x .m22 = TopVector.y * Scale.y .m23 = TopVector.z * Scale.z .m31 = ForwardVector.x * Scale.x .m32 = ForwardVector.y * Scale.y .m33 = ForwardVector.z * Scale.z .m41 = Position.x .m42 = Position.y .m43 = Position.z End With Return InMatrix End Function
Note : The VB.Net version takes scale and position components in parameters so that you can keep them after the matrix transformation, and use SetMatrix instead of SetRotationMatrix (which is unavailable with TVMiniMesh).
And there you have it; your mesh is aligned to your landscape. That is to say how magic matrices are.
A couple of oddities worth noting :
Vector1 X Vector2 = -(Vector2 X Vector1)
Left
, Bottom
or Backward
vectors, here is the cross product order to follow :
Top X Forward = Right Right X Top = Forward Forward X Right = Top
TO BE CONTINUED...?