![]()
geXForm3d
Description: 3D transform interface
Source file: \genesis3d\OpenSource\Source\Math\XFORM3D.h
Functions: Copy, GetEulerAngles, GetIn, GetLeft, GetTranspose, GetUp, IsOrthogonal, IsOrthonormal, IsValid, Mirror, Multiply, Orthonormalize, Rotate, RotateX, RotateY, RotateZ, SetEulerAngles, SetFromLeftUpIn, SetIdentity, SetScaling, SetTranslation, SetXRotation, SetYRotation, SetZRotation, Scale, Transform, TransformArray, Translate, TransposeTransform
Types: geXForm3d
Macros: GEXFORM3D_MINIMUM_SCALE
Tutorial, Notes: view [Contents: What is a Transformation matrix?, Code snipet, Notes, Mathematical Properties, RotateAboutVector, RotateAboutIn, RotateAboutLeft, RotateAboutUp, RotateTowardsPoint ]
Proposed Functions: RotateAboutVector, RotateAboutIn, RotateAboutLeft, RotateAboutUp, RotateTowardsPoint
Addition for Genesis3D v1.6: types float converted to geFloat, IsIdentity
![]()
Types:
typedef struct {
geFloat AX, AY, AZ; // e[0][0],e[0][1],e[0][2]
geFloat BX, BY, BZ; // e[1][0],e[1][1],e[1][2]
geFloat CX, CY, CZ; // e[2][0],e[2][1],e[2][2]
geVec3d Translation; // e[0][3],e[1][3],e[2][3]
// 0,0,0,1 // e[3][0],e[3][1],e[3][2]
} geXForm3d;
This is a struct composed of 9 floats and a geVec3d. This structure represents a 4x4 transformation matrix.
Notes: This "represents" a 4x4 matrix (with the bottom row always [0, 0, 0, 1]) because that is how all the calculations in the code act. It is not possible to change the bottom row, and it not a true 4x4 matrix.
geFloat AX |
geFloat AY |
geFloat AZ |
(geVec3d Translation).X |
geFloat BX |
geFloat BY |
geFloat BZ |
(geVec3d Translation).Y |
geFloat CX |
geFloat CY |
geFloat CZ |
(geVec3d Translation).Z |
0.0f |
0.0f |
0.0f |
1.0f |
This represents a right-handed transform.
Notes:
from GEXFORM3D.H: The Euler angles are subsequent rotations by Angles-Z around the Z axis, then by Angles-Y around the Y axis, in the newly rotate coordinates then by Angles-X around the X axis. "Left,Up,In" are just the basis vectors in the new coordinate space. You can get them by multiplying the unit bases into the transforms.
![]()
Macros:
#define GEXFORM3D_MINIMUM_SCALE (0.00001f)
This apparently represents the minimum logical scale value.
![]()
Functions:
![]()
GENESISAPI void GENESISCC geXForm3d_Copy(const geXForm3d* Src, geXForm3d* Dst);
This function copies Src to Dst.
Returns: nothing.
Notes:
from GEXFORM3D.H: copies Src to Dst.
![]()
GENESISAPI void GENESISCC geXForm3d_GetEulerAngles(const geXForm3d* M, geVec3d* Angles);
This function computes the Euler Angles of M returning the result in Angles. The Euler Angles of a XForm is defined as the Angles that when applied in Z, then Y, then X order would produce the same rotational transform as the XForm. (Returned angles are in radians.)
Returns: nothing.
Notes:
from GEXFORM3D.H: Finds Euler angles from M and puts them into Angles
![]()
GENESISAPI void GENESISCC geXForm3d_GetIn(const geXForm3d* M, geVec3d* In);
This function returns the vector which is composed of (-AZ, -BZ, -CZ) from the matrix M.
Returns: nothing.
Notes:
from GEXFORM3D.H: Gets a vector that is 'in' in the frame of reference of M (facing -Z)
An "in" vector points "into" the scene or "forward" relative to the current XForm configuration's frame reference.
More detailed Notes:
Every matrix has an orientation, and a corresponding set of three vectors that define the principle axes of this orientation. These three axes may be found by (using the variables as defined above) the following three column vectors <AX, BX, CX>, <AY, BY, ZY>, <AZ, BZ, CZ>. (Note AX would be x componant, BX as y componant, CX as z componant--and so on for each of these three vectors.) This function returns -<AZ, BZ, CZ>.
![]()
GENESISAPI void GENESISCC geXForm3d_GetLeft(const geXForm3d* M, geVec3d* Left);
This function returns the vector which is composed of (-AX, -BX, -CX) from the matrix M.
Returns: nothing.
Notes:
from GEXFORM3D.H: Gets a vector that is 'left' in the frame of reference of M (facing -Z) (sic! that should be facing -X, I think)
A "left" vector is one that points to the left, relative to the current XForm configuration's frame of reference.
More detailed Notes:
Every matrix has an orientation, and a corresponding set of three vectors that define the principle axes of this orientation. These three axes may be found by (using the variables as defined above) the following three column vectors <AX, BX, CX>, <AY, BY, ZY>, <AZ, BZ, CZ>. (Note AX would be x componant, BX as y componant, CX as z componant--and so on for each of these three vectors.) This function returns -<AX, BX, CX>.
![]()
GENESISAPI void GENESISCC geXForm3d_GetTranspose(const geXForm3d* M, geXForm3d*
MTranspose);
This function calculates the Transpose of M returning the result in MTranspose.
Returns: nothing.
Notes:
from XFORM3D.H: Gets the Transpose transform of M (M^T). Transpose of a matrix is the switch of the rows and columns. The transpose is useful because it is rapidly computed and is equal to the inverse transform for orthonormal transforms. [inverse is (M') where M*M' = Identity ]More detailed Notes:
If the matrix is orthogonal, then the inverse is equal to the transpose.Example of use of a matrix inverse (M') would be as follows: Imagine you had a table-actor which is positioned in the world by matrix M1. Now suppose that a weapon-actor (positioned by by matrix M2) is dropped onto this table, and you now want to relate the weapon's transform to the tables's. That way, the weapon will "stick" to the table as the table is moved around. The calculation of M2 * M1' = M3 will give matrix for the weapon positioned on the table when the table is at the origin (i.e. when its matrix is at identity). So to calculate where the weapon should be when the table is moved around via M1, use M2 = M3 * M1. This first transform the weapon from identity to its relative position on the table (via M3). Then it is positioned the the site of the table via M1.
![]()
GENESISAPI void GENESISCC geXForm3d_GetUp(const geXForm3d* M, geVec3d* Up);
This function returns the vector which is composed of (AY, BY, CY) from the matrix M.
Returns: nothing.
Notes:
from XFORM3D.H: Gets a vector that is 'up' in the frame of reference of M (facing -Z) (sic! that should be facing +Y, I think)
A "up" vector is one that points to the up, relative to the current XForm configuration's frame of reference.
More detailed Notes:
Every matrix has an orientation, and a corresponding set of three vectors that define the principle axes of this orientation. These three axes may be found by (using the variables as defined above) the following three column vectors <AX, BX, CX>, <AY, BY, ZY>, <AZ, BZ, CZ>. (Note AX would be x componant, BX as y componant, CX as z componant--and so on for each of these three vectors.) This function returns <AY, BY, ZY>.
![]()
GENESISAPI geBoolean GENESISCC geXForm3d_IsIdentity(const geXForm3d* M);
Added for Genesis3D v1.6
Returns GE_TRUE if M is an identity matrix
![]()
GENESISAPI geBoolean GENESISCC geXForm3d_IsOrthogonal(const geXForm3d* M);
This function tests whether the Matrix M's column vectors are orthogonal. This tells us whether the transform would impart shearing.
Returns: GE_TRUE of M's column vectors are orthogonal, GE_FALSE otherwise.
More detailed Notes:
Every matrix has an orientation, and a corresponding set of three vectors that define the principle axes of this orientation. These three axes may be found by (using the variables as defined above) the following three column vectors <AX, BX, CX>, <AY, BY, ZY>, <AZ, BZ, CZ>. (Note AX would be x componant, BX as y componant, CX as z componant--and so on for each of these three vectors.) If each of these three vectors form right angles with each other (i.e. are "linearly independent"), then the vectors are said to be orthogonal. If the vectors are also of unit length (i.e. are normalized), then the vectors are said to be orthonormal. For a matrix to be a orthogonal, the column vectors must be orthonormal (not just orthogonal).Application: Why would one want to know if a matrix is orthogonal? An orthogonal matrix will preserve lengths and angles when used to transform vectors. Also, it is one such that its inverse is the same as its transpose, so use of GetTranspose and TransposeTransform give meaningful results (see notes there). Note: in this library, use IsOrthonormal to test for an orthogonal matrix.
Terminology Note: This function performs a different task than the name suggests. It determines whether its column vectors are orthogonal, not whether the matrix itself is orthogonal. Furthermore, the code comments in Genesis3D are inconsistent with the actual code:
// returns GE_TRUE if M is orthogonal
// (row1 cross row2 = row3 & col1 cross col2 = col3)
// * does not check for right-handed convention *The Genesis3D code itself only checks for (Col1 cross Col2) to be parallel to Col3. The comment should read:
// returns GE_TRUE if M's column vectors are orthogonal
// i.e. Normalized (col1 cross col2) = Normalized (col3)
// * does not check for right-handed convention *
Pratical note: Unless the matrix is directly constructed using the structoral componants of a geXForm3d, or by using three vectors that are not truly linearly independant with the function SetFromLeftUpIn, then matrices will generally be orthogonal (and, in fact, orthonormal). Using SetScaling or Scale will cause a matrix to not be mathematically orthogonal.
![]()
GENESISAPI geBoolean GENESISCC geXForm3d_IsOrthonormal(const geXForm3d* M);
This function tests whether the Matrix M's column vectors are orthonormal (and whether the matrix is mathematically orthogonal). This tells us whether the transform would impart scaling or shearing.
Returns: GE_TRUE of M's column vectors are orthonormal, GE_FALSE otherwise.
More detailed Notes:
Every matrix has an orientation, and a corresponding set of three vectors that define the principle axes of this orientation. These three axes may be found by (using the variables as defined above) the following three column vectors <AX, BX, CX>, <AY, BY, ZY>, <AZ, BZ, CZ>. (Note AX would be x componant, BX as y componant, CX as z componant--and so on for each of these three vectors.) If each of these three vectors form right angles with each other (i.e. are "linearly independent"), then the vectors are said to be orthogonal. If the vectors are also of unit length (i.e. are normalized), then the vectors are said to be orthonormal. For a matrix to be a orthogonal, the column vectors must be orthonormal (not just orthogonal).Application: Why would one want to know if a matrix is orthogonal? An orthogonal matrix will preserve lengths and angles when used to transform vectors. Also, it is one such that its inverse is the same as its transpose, so use of GetTranspose and TransposeTransform give meaningful results (see notes there). Note: in this library, use this function (IsOrthonormal) to test for an orthogonal matrix (not IsOrthogonal).
Terminology Note: This function performs a different task than the name suggests. It determines whether its column vectors are orthonormal, and as such really determines whether the matrix is mathematically orthogonal. The comments in the Genesis3D code are inconsistent with the code.
// returns GE_TRUE if M is orthonormal
// (if the rows and columns are all normalized (transform has no scaling or shearing)
// and is orthogonal (row1 cross row2 = row3 & col1 cross col2 = col3)To keep the comments consistant with the code, it should read:
// returns GE_TRUE if M is orthogonal
// (if the transform has no scaling or shearing)
// if (col1 cross col2) = col3The Genesis3D code only requires that (Col1 cross Col2) = Col3. In the rare condition that Col1, Col2, and Col3 were scaled by a scalars a, b, and a*b, then the matrix would still be incorrectly classified as orthonormal by this function.
Pratical note: Unless the matrix is directly constructed using the structoral componants of a geXForm3d, or by using three vectors that are not truly linearly independant (or are not normalized) with the function SetFromLeftUpIn, then matrices will generally be orthonormal. Using SetScaling or Scale will cause a matrix to not be mathematically orthogonal.
![]()
GENESISAPI geBoolean GENESISCC geXForm3d_IsValid(const geXForm3d* M);
This function tests that M is non-NULL and all of its components are real values.
Notes:
from XFORM3D.H: returns GE_TRUE if M is 'valid'. 'valid' means that M is non NULL, and there are no NAN's in the matrix.
![]()
GENESISAPI void GENESISCC geXForm3d_Mirror(const geXForm3d* Source, const geVec3d* PlaneNormal, float PlaneDist, geXForm3d* Dest);
This function takes a transform matrix Source and "mirrors" is about the plane with the normal PlaneNormal that is PlaneDist from the origin. The result is returned in Dest.
Returns: nothing.
Notes:
from XFORM3D.H: Mirrors a XForm3d about a plane.
![]()
GENESISAPI void GENESISCC geXForm3d_Multiply(const geXForm3d* M1, const geXForm3d* M2, geXForm3d* MProduct);
This function multiplies the two matrices M1 and M2 returning the result in MProduct. This can be used to apply the transform of M2 onto the M1 transformation.
Returns: nothing.
Notes:
from XFORM3D.H: MProduct = matrix multiply of M1*M2. Concatenates the transformation in the M2 matrix onto the transformation in M1.
![]()
GENESISAPI void GENESISCC geXForm3d_Orthonormalize(geXForm3d* M);
This function orthonormalizes the matrix M and returns the result in M replacing the original value. Orthonormalizing a matrix removes and scaling or other distortions from the transformation.
Returns: nothing.
Notes:
from XFORM3D.H: Essentially removes scaling (or other distortions) from an orthogonal (or nearly orthogonal) matrix. Returns a right-handed matrix.
![]()
GENESISAPI void GENESISCC geXForm3d_Rotate(const geXForm3d* M, const geVec3d* V, geVec3d* Result);
This function takes the vector V and rotates it by the transform M returning the result in Result. No translation is performed.
Note: This rotates about the origin (0, 0, 0). If the XForm is not currently at the origin, then rotation will result in the XForm "orbiting" the origin. Also, the geXForm3d M is NOT set to identity first.
Returns: nothing.
Notes:
from XFORM3D.H: Result is Matrix M* Vector V: V Rotated by M (no translation).
![]()
GENESISAPI void GENESISCC geXForm3d_RotateX(geXForm3d* M, geFloat RadianAngle);
This function rotates the transform M by RadianAngle radians about the X axis and returns the result in M replacing the original value.
Note: This rotates about the origin (0, 0, 0). If the XForm is not currently at the origin, then rotation will result in the XForm "orbiting" the origin. Also, the geXForm3d M is NOT set to identity first. To first set transform to identity, and then rotatate, use geXForm3d_SetRotationX
Returns: nothing.
Notes:
from XFORM3D.H: Rotates M by RadianAngle about X axis and applies the rotation to the existing contents of M.
![]()
GENESISAPI void GENESISCC geXForm3d_RotateY(geXForm3d* M, geFloat RadianAngle);
This function rotates the transform M by RadianAngle radians about the Y axis and returns the result in M replacing the original value.
Note: This rotates about the origin (0, 0, 0). If the XForm is not currently at the origin, then rotation will result in the XForm "orbiting" the origin. Also, the geXForm3d M is NOT set to identity first. To first set transform to identity, and then rotatate, use geXForm3d_SetRotationY
Returns: nothing.
Notes:
from XFORM3D.H: Rotates M by RadianAngle about Y axis and applies the rotation to the existing contents of M.
![]()
GENESISAPI void GENESISCC geXForm3d_RotateZ(geXForm3d* M, geFloat RadianAngle);
This function rotates the transform M by RadianAngle radians about the Z axis and returns the result in M replacing the original value.
Note: This rotates about the origin (0, 0, 0). If the XForm is not currently at the origin, then rotation will result in the XForm "orbiting" the origin. Also, the geXForm3d M is NOT set to identity first. To first set transform to identity, and then rotatate, use geXForm3d_SetRotationZ
Returns: nothing.
Notes:
from XFORM3D.H: Rotates M by RadianAngle about Z axis and applies the rotation to the existing contents of M.
![]()
GENESISAPI void GENESISCC geXForm3d_Scale(geXForm3d* M, geFloat x, geFloat y, geFloat z);
This function scales the transform M by the vector defined by (x,y,z) returning the result in M replacing the original contents.
Returns: nothing.
Notes:
from XFORM3D.H: Scales M by x,y,z. Applies the scale to the existing contents of M.See also SetScaling function.
![]()
GENESISAPI void GENESISCC geXForm3d_SetEulerAngles(geXForm3d* M, const geVec3d* Angles);
This function can be used to set the rotational part of the transform M by the Euler angles in Angles. Any existing transformation in M is lost.
Returns: nothing.
Notes:
from XFORM3D.H: Applies Euler angles to build M.
![]()
GENESISAPI void GENESISCC geXForm3d_SetFromLeftUpIn(geXForm3d* M, const geVec3d* Left, const geVec3d* Up, const geVec3d* In);
This function creates a transform utilizing the orthonormal vectors in Left, Up, and In and returns the result in M replacing the existing transformation. Translation is set to <0, 0, 0>
Returns: nothing.
Notes:
from XFORM3D.H: Builds an geXForm3d from orthonormal Left, Up and In vectors.
![]()
GENESISAPI void GENESISCC geXForm3d_SetIdentity(geXForm3d* M);
This function returns the Identity matrix in M, replacing its original contents.
Returns: nothing.
Notes:
from XFORM3D.H: Sets M to an identity matrix (clears it).An identity matrix is in the form:
|1, 0, 0, 0|
|0, 1, 0, 0|
|0, 0, 1, 0|
|0, 0, 0, 1|And has the properties that any vector (or point) multiplied by this matrix will remain unchanged. Note, the matrix translation is set to <0, 0, 0>
Setting the matrix to Identity essentially "clears" it, and any actor or camera using this will be positioned at the origin (with no rotation).
![]()
GENESISAPI void GENESISCC geXForm3d_SetScaling(geXForm3d* M, geFloat x, geFloat y, geFloat z);
This function creates a transform matrix M that produces the scaling (x,y,z). The contents of M are replaced.
Returns: nothing.
Notes:
from XFORM3D.H: Sets up a transform that scales by x,y,z. All existing contents of M are replaced.The scaling matrix result is in the form:
|x, 0, 0, 0|
|0, y, 0, 0|
|0, 0, z, 0|
|0, 0, 0, 1|See also Scale function.
![]()
GENESISAPI void GENESISCC geXForm3d_SetTranslation(geXForm3d* M, geFloat x, geFloat y, geFloat z);
This function creates a transform matrix M that produces the translation (x,y,z). The contents of M are replaced.
Returns: nothing.
Notes:
from XFORM3D.H: Sets up a transform that translates x,y,z. All existing contents of M are replaced.
This clears the matrix first. If you want to keep the current XForm, but simply translate it further, use geXForm3d_Translate
![]()
GENESISAPI void GENESISCC geXForm3d_SetXRotation(geXForm3d* M, geFloat RadianAngle);
This function creates a transform matrix M that produces a rotation of RadianAngle radians about the X axis. The contents of M are replaced.
Returns: nothing.
Notes:
from XFORM3D.H: Sets up a transform that rotates RadianAngle about X axis. All existing contents of M are replaced.
Note: This clears the matrix first. If you want to keep the current XForm, but simply rotate it further, use geXForm3d_RotateX
![]()
GENESISAPI void GENESISCC geXForm3d_SetYRotation(geXForm3d* M, geFloat RadianAngle);
This function creates a transform matrix M that produces a rotation of RadianAngle radians about the Y axis. The contents of M are replaced.
Returns: nothing.
Notes:
from XFORM3D.H: Sets up a transform that rotates RadianAngle about Y axis. All existing contents of M are replaced.
Note: This clears the matrix first. If you want to keep the current XForm, but simply rotate it further, use geXForm3d_RotateY
![]()
GENESISAPI void GENESISCC geXForm3d_SetZRotation(geXForm3d* M, geFloat RadianAngle);
This function creates a transform matrix M that produces a rotation of RadianAngle radians about the Z axis. The contents of M are replaced.
Returns: nothing.
Notes:
from XFORM3D.H: Sets up a transform that rotates RadianAngle about Z axis. All existing contents of M are replaced.
Note: This clears the matrix first. If you want to keep the current XForm, but simply rotate it further, use geXForm3d_RotateZ
![]()
GENESISAPI void GENESISCC geXForm3d_Transform(const geXForm3d* M, const geVec3d* V, geVec3d* Result);
This function transforms a vector V by the transform M returning the result in Result.
Returns: nothing.
Notes:
from XFORM3D.H: Result is Matrix M* Vector V: V Tranformed by M.Note: Transforming a vector means applying the matrix to the vector to achieve a result.
![]()
GENESISAPI void GENESISCC geXForm3d_TransformArray(const geXForm3d* XForm, const geVec3d* Source, geVec3d* Dest, int32 Count);
This function transforms the array of Count vectors in Source by the transform M returning the result in the array Dest.
Returns: nothing.
![]()
GENESISAPI void GENESISCC geXForm3d_Translate(geXForm3d* M, geFloat x, geFloat y, geFloat z);
This function translates the transform M by (x,y,z) returning the result in M.
Returns: nothing.
Notes:
from XFORM3D.H: Translates M by x,y,z. Applies the translation to the existing contents of M.
This will "move the transform" to a position specified by (x, y, z)
![]()
GENESISAPI void GENESISCC geXForm3d_TransposeTransform(const geXForm3d* M, const geVec3d* V, geVec3d* Result);
This function transforms a vector V by the transpose of the transform M returning the result in Result.
Returns: nothing.
Notes:
from XFORM3D.H: Applies the transpose transform of M to V. Result = (M^T)* V.See GetTranspose notes on the use of a matrix transpose.
![]()
Contents: What is a Transformation matrix?, Code snipet, Notes, Mathematical Properties, RotateAboutVector, RotateAboutIn, RotateAboutLeft, RotateAboutUp, RotateTowardsPoint
What IS a transformation matrix?
This was a difficult concept for me to grasp at first, so let me explain my understanding (and as always, thanks goes to Seven and his ProjectZ for getting me started.)
I primarily use a matrix for positioning my actors in my game. An actor is composed of many polygons (the actor Dema, for example, has approximately 800 ploygons.) And each of these is typically defined by 3 vertices (or points). As an actor runs around in a game, the engine must quickly get all those points recalculated to their corrrect location and rotation. So, how is this done? The answer is, through a transformation matrix.
I think of a transform matrix as a "black box" forumla. The initial points that define the actor's polygons are located near the origin (0, 0, 0). After applying the matrix, the points are in their correct location. Raw data goes in, correctly calculated data comes out.
So each time I want my actor to be in an a different location or orientation, I need to set up a new matrix. So the matrix really defines and determines my actor's location. To move an actor, I am really moving the matrix.
Return to Tutorial table of contents
Return to Main table of contents
Here is an example code snipet:
//define some needed variables.
geXForm3d
XForm;
geVec3d Pos;
geVec3d Angles;
geActor* MyActor;
SetActorPosition (Pos); //This would be some function to get the Pos
variable set to our actor's position
SetActorAngles (Angles); //This
would be some function to get the Angles variable set to our
actor's orientation
GetMyActorPointer (MyActor); //Some
actor that sets MyActor appropriately
//Initiate (clear) the xform matrix
geXForm3d_SetIdentity(&XForm);
//Rotate the actor in 3 planes to set
correct orientation
geXForm3d_RotateX(&XForm,
Angles.X);
geXForm3d_RotateY(&XForm, Angles.Y);
geXForm3d_RotateZ(&XForm, Angles.Z);
//Now translate the actor into position
geXForm3d_Translate(&XForm,
Pos.X, Pos.Y, Pos.Z);
//Now put move the actor to our desired
location and orientation
geActor_ClearPose (MyActor,
&XForm);
//Note: in a real program, I would use
geActor_SetPose to also change the animation
Return to Tutorial table of contents or Return to Main table of contents
//-----------------------------------------------------------------------------
I. All rotations are made about the origin (point 0,0,0). If your transform is not at the origin, then the rotation will appear to "orbit" the origin. To effect a rotation when not at the origin, first translate the transform back to the origin, apply the rotations, then translate it back to the desired location. The method applied in the code above (and in many tutorials) is to first clear the transform (set it to the origin), then apply needed rotations, then translate it to appropriate position--in that order.
II. The order of rotation order is important: Rotating X, Y, then Z will produce a different result than rotating Z, Y, X or Y, Z, X etc. Thus while (x, y, z) coordinates, in a Cartesian system, specifies a specific point regardless of which axis is changed first, the same is NOT true for applying x, y, and z rotations to a matrix.
III. The X, Y, Z axises used for transform rotations are GLOBAL, not relative to the current matrix orientation. For example: Suppose you have an actor standing at the origin with an Identity (cleared) matrix. An X rotation from this position would lay the actor forward onto the floor on its abdomen, while a Y rotation from this initial position would spin the actor around the axis of its spine (i.e. turn left or right), and a Z rotation from the initial position would cause the actor to lean over to one side. Suppose we choose to start with the X rotation, leaving the actor on its abdomen. Now let's consider a subsequent Y rotation. This will spin the actor around the global Y axis, in a plane parallel to the floor and around its umbilicus--not around its spine (which was on the Y axis initially). Thus the Y axis for a Y rotation is not relative to the actor's (meaning the matrix's, really) current orientation, but rather, it is always the global X, Y, or Z coordinates defined by the surrounding world.
IV. In many applications, the camera's and actor's orientation is simply set by changing Angles.Y to determine left/right gaze, and Angles.X to determine up/down gaze. Important points for this to work include: 1) Applying the X rotation first, while the matrix is still at Identity (meaning that it has been cleared, so that the X axis extends to the left and right) 2) applying the Y rotation after the X rotation, and 3) having an Angles.Z that is 0.0f, or ensuring that the Z rotation is done last. Note the effect of the Z rotation will vary, based on the Y rotation.
V. Note that all angles required for these functions are in radians. Whereas degrees range 0 to 360, radians range from 0 to 2*pi. The circumference of a unit circle (i.e. with a radius of 1.0) is 2*pi. So mathmeticians have chosen this scale for their angle units--calling them radians. The 360 degree range originated with the Sumerian's zodiac, I believe.
Return to Tutorial table of contents or Return to Main table of contents
Transform Mathematical Properties:
The following notes are from Mathematics for 3D Game Programming & Computer Graphics by Eric Lengyel (ISBN 1-58450-037-9)
Given any two scalars a and b and any three n x m matrices F, G, and H, the following properties hold.
Return to Tutorial table of contents or Return to Main table of contents
Proposed Functions
Note: These have been tested, and they work. For usual movement, the techniques described above (in section IV) may be faster.
//===============================================================================
Rotation Method #1. (Method #2 is here)
void geXForm3d_RotateAboutVector (geXForm3d* M, geVec3d* Vector, geFloat RadianAngle)
//Proposed function (not now part of
Genesis 3D)
//Purpose: Rotate the XForm about Vector by RadianAngles
angle--even if it is not at the origin)
// Input: M -- The transformation matrix that is to be
rotated. It need not be at origin
// Vector
-- The axis of rotation about which to rotate M
// RadianAngle
-- the amount of rotation.
// Output: M is changed to reflect new rotation
{
geVec3d Pos, Axis;
geVec3d Origin = {0 , 0, 0};
geQuaternion
Q;
geXForm3d QMatrix;
Pos = M->Translation;
M->Translation = Origin;
Axis = *Vector;
geVec3d_Normalize (&Axis);
geQuaternion_SetFromAxisAngle (&Q, &Axis, RadianAngle);
geQuaternion_ToMatrix (&Q, &QMatrix);
geXForm3d_Multiply (M, &QMatrix, M);
//now translate back to initial
position
M->Translation = Pos;
}
Comment: The mixing of quaternions and matrices appears at times to introduce small errors, which grow if called cyclically . For example, I wrote an application that repeatedly did the following:
I found that at some rotation axis angles, the left vector would wobble and wander. A fix for this problem is to save the desired axis of rotation for reuse, and only recalculating it when it is likely to have changed and not obtaining it each cycle. I don't want to make too big a deal out of this, but it gave me confusing results at first. Thus a better algorithm for the above would be:
Return to Tutorial table of contents or Return to Main table of contents
//===============================================================================
Rotation Method #2 (Method #1 is here)
After the above was written, I found the following
forumula in a reference that will also rotate a matrix about an
arbitrary axis. C = cos (angle), S = sin (angle), A = the vector
specifying the axis of rotation. The following table represents
the matrix that will effect the rotation.
| C + A.x*A.x*(1-C) | A.x*A.y*(1-C) - A.z*S | A.x*A.z*(1-C) + A.y*S | |
| Matrix for Rotation about A= | A.x*A.y*(1-C) + A.z*S | C + A.y*A.y*(1-C) | A.y*A.z*(1-C) - A.x*S |
| A.x*A.z*(1-C) - A.y*S | A.y*A.z*(1-C) + A.x*S | C + A.z*A.z*(1-C) |
Return to Tutorial table of contents or Return to Main table of contents
//===============================================================================
void geXForm3d_RotateAboutIn
(geXForm3d*
M, geFloat RadianAngle)
//Proposed function (not now part of
Genesis 3D)
//Purpose: This would rotate the XForm about the XForm's In
vector--even if it is not at the origin)
//Note: This function is older and not
as versitile as geXForm3d_RotateAboutVector
{
geVec3d Angles, Pos;
Pos = M->Translation;
geXForm3d_Translate (M, -Pos.X,
-Pos.Y, -Pos.Z); //Translate the
Xform back to the origin
geXForm3d_GetEulerAngles (M,
&Angles); //Remember how to get
back to the Xform's initial orientation.
geXForm3d_SetIdentity (M); //clear the XForm
geXForm3d_RotateZ (M,
-RadianAngle); //the In vector is
the same as the -Z axis when the XForm is at Identity
//Now
add back the initial rotations
geXForm3d_RotateZ
(M, Angles.Z); //must apply
rotations in the order for Euler angles restoration
geXForm3d_RotateY (M, Angles.Y);
geXForm3d_RotateX (M, Angles.X);
//now
translate back to initial position
geXForm3d_Translate
(M, Pos.X, Pos.Y, Pos.Z);
}
Return to Tutorial table of contents or Return to Main table of contents
//===============================================================================
void geXForm3d_RotateAboutUp
(geXForm3d*
M, geFloat RadianAngle)
//Proposed function (not now part of
Genesis 3D)
//Purpose: This would rotate the XForm about the XForm's Up
vector--even if it is not at the origin)
//Note: This function is older and not
as versitile as geXForm3d_RotateAboutVector
{
geVec3d Angles, Pos;
Pos = M->Translation;
geXForm3d_Translate (M, -Pos.X,
-Pos.Y, -Pos.Z); //Translate the
Xform back to the origin
geXForm3d_GetEulerAngles (M,
&Angles); //Remember how to get
back to the Xform's initial orientation.
geXForm3d_SetIdentity (M); //clear the XForm
geXForm3d_RotateY (M, RadianAngle); //the Up vector is the same as the Y axis when
the XForm is at Identity
//Now
add back the initial rotations
geXForm3d_RotateZ
(M, Angles.Z); //must apply
rotations in the order for Euler angles restoration
geXForm3d_RotateY (M, Angles.Y);
geXForm3d_RotateX (M, Angles.X);
//now
translate back to initial position
geXForm3d_Translate (M, Pos.X,
Pos.Y, Pos.Z);
}
Return to Tutorial table of contents or Return to Main table of contents
//===============================================================================
void geXForm3d_RotateAboutLeft
(geXForm3d* M, geFloat RadianAngle)
//Proposed function (not now part of
Genesis 3D)
//Purpose: This would rotate the XForm about the XForm's Left
vector--even if it is not at the origin)
//Note: This function is older and not
as versitile as geXForm3d_RotateAboutVector
{
geVec3d Angles, Pos;
Pos = M->Translation;
geXForm3d_Translate (M, -Pos.X,
-Pos.Y, -Pos.Z); //Translate the
Xform back to the origin
geXForm3d_GetEulerAngles (M,
&Angles); //Remember how to get
back to the Xform's initial orientation.
geXForm3d_SetIdentity (M); //clear
the XForm
geXForm3d_RotateX (M,
-RadianAngle); //the Left vector is
the same as the -X axis when the XForm is at Identity
//Now
add back the initial rotations
geXForm3d_RotateZ
(M, Angles.Z); //must apply
rotations in the order for Euler angles restoration
geXForm3d_RotateY (M, Angles.Y);
geXForm3d_RotateX (M, Angles.X);
//now
translate back to initial position
geXForm3d_Translate
(M, Pos.X, Pos.Y, Pos.Z);
}
Return to Tutorial table of contents or Return to Main table of contents
//===============================================================================
void geXForm3d_RotateTowardsPoint
(geXForm3d*
M, geVec3d* Point, geVec3d* RefUp)
//Note: This function has NOT yet
been tested.
//Proposed function (not now part of
Genesis 3D)
//Purpose: Rotate the geXForm M such that it is orientated
towards the point given by the Point's {x, y, z}
//Inputs:
// M: XForm to change. This need
not be at the origin. If XForm is away from
//
origin,
then the XForm will rotate at its current location.
// Point: The point (in world
space) to orient towards.
// RefUp: This is a vector that
points in the general upward direction
//
This
is to keep the transform upright while facing the desired point
//
Example
RefUp vector: {0, 1, 0}
//Output:
// Result is put back into M.
Original contents are replaced with result.
{
geVec3d Pos, In, Left, Up;
geBoolean AtOrigin;
//If
invalid inputs, then return without changing M
if
((!M) || (!Point) || (!RefUp)) return;
//Store
the XForm M's current position
Pos =
M->Translation;
//Is
the XForm M already at the origin?
AtOrigin
= ((Pos.X ==0) && (Pos.Y == 0) && (Pos.Z == 0));
//If
not at origin, then translate to origin.
if
(!AtOrigin) geXForm3d_Translate (M, 0, 0, 0);
//Create
In vector by subtracting Pos from Point, then normalize
geVec3d_Subtract
(Point, &Pos, &In);
geVec3d_Normalize (&In);
//Find
Left vector as normal from plane containing In and RefUp
//NOTE:
I will need to check whether I need to negate this or not (i.e.
is it really a "right" vector?)
geVec3d_CrossProduct
(&In, RefUp, &Left);
geVec3d_Normalize (&Left);
//Find
Up vector as normal from plane containing In and left
//Because In and Left are
perpendicular, normalized vectors -> no need to normalize Up
//NOTE:
I will need to check whether I need to negate this or not (i.e.
is it really a "down" vector?)
geVec3d_CrossProduct
(&In, &Left, &Up);
//Now
set orientation from In, Up, and Left;
geXForm3d_SetFromLeftUpIn
(M, &Left, &Up, &In);
//If
it wasn't originally at origin, then translate back to it
original location.
if
(!AtOrigin) geXForm3d_Translate (M, Pos.X, Pos.Y, Pos.Z);
}
Return to Tutorial table of contents or Return to Main table of contents
//===============================================================================
These comments (for better or for worse) are from Kdtop. I also added a few comments in the function explainations above, all of which are marked in blue.