Transformations

  CU_MDX_Transformations.zip (64.3 KiB, 2,545 hits)


  CUnitFramework.zip (101.1 KiB, 20,443 hits)

Extract the C-Unit Framework
inside the project (not solution) directory.

Recommended reading for this tutorial:

In 3D graphics, there are three basic transformations: translation, rotation, and scale. To transform our geometry, we have to manipulate the world matrix before rendering each separate piece of geometry. The specific manipulations required are described in any beginning graphics book or article such as the ones listed above.

To make things easier, we’ll creat a class, WorldTransform, that will handle all the transformations with simple method calls. In the future, other classes can derive from this class if they need their own world matrix to transform, rotate, and scale.

namespace CUnit
{
/// World matrix wrapper
public class WorldTransform
{
private Matrix m_translate;
private Matrix m_rotate;
private Matrix m_scale;
float m_rotationX, m_rotationY, m_rotationZ;

/// Creates a new WorldTransform
public WorldTransform() Toggle
{
Reset();
}


/// Reset the matrices to default position.
public void Reset() Toggle
{
m_translate = Matrix.Identity;
m_rotate = Matrix.Identity;
m_scale = Matrix.Identity;
m_rotationX = m_rotationY = m_rotationZ = 0.0f;
}


/// Absolute translation
/// X
/// Y
/// Z
public void TranslateAbs( float x, float y, float z ) Toggle
{
m_translate.M41 = x;
m_translate.M42 = y;
m_translate.M43 = z;
}


/// Absolute translation
/// Translations vector
public void TranslateAbs( Vector3 translation ) Toggle
{
TranslateAbs( translation.X, translation.Y, translation.Z );
}


/// Relative translation
/// X
/// Y
/// Z
public void TranslateRel( float x, float y, float z ) Toggle
{
m_translate.M41 += x;
m_translate.M42 += y;
m_translate.M43 += z;
}


/// Relative translation
/// Translations vector
public void TranslateRel( Vector3 translation ) Toggle
{
TranslateRel( translation.X, translation.Y, translation.Z );
}


/// Absolute rotation
/// X radians
/// Y radians
/// Z radians
public void RotateAbs( float x, float y, float z ) Toggle
{
m_rotationX = x;
m_rotationY = y;
m_rotationZ = z;
m_rotate = Matrix.RotationYawPitchRoll( y, x, z );
}


/// Relative rotation
/// X radians
/// Y radians
/// Z radians
public void RotateRel( float x, float y, float z ) Toggle
{
m_rotationX += x;
m_rotationY += y;
m_rotationZ += z;
m_rotate = Matrix.RotationYawPitchRoll( m_rotationY, m_rotationX, m_rotationZ );
}


/// Absolute scale.
/// X
/// Y
/// Z
public void ScaleAbs( float x, float y, float z ) Toggle
{
m_scale.M11 = x;
m_scale.M22 = y;
m_scale.M33 = z;
}


/// Relative scale
/// X
/// Y
/// Z
public void ScaleRel( float x, float y, float z ) Toggle
{
m_scale.M11 += x;
m_scale.M22 += y;
m_scale.M33 += z;
}


/// The combined transformation matrix.
public Matrix Transform Toggle
{
get
{
return  m_scale * m_rotate * m_translate;
}
}


/// Gets and sets the position vector
public Vector3 Position Toggle
{
get { return new Vector3( m_translate.M41, m_translate.M42, m_translate.M43 ); }
set
{
m_translate.M41 = value.X;
m_translate.M42 = value.Y;
m_translate.M43 = value.Z;
}
}


/// Absolute x position.
public float XPosition Toggle
{
get { return m_translate.M41; }
set { m_translate.M41 = value; }
}


/// Absolute y position.
public float YPosition Toggle
{
get { return m_translate.M42; }
set { m_translate.M42 = value; }
}


/// Absolute z position.
public float ZPosition Toggle
{
get { return m_translate.M43; }
set { m_translate.M43 = value; }
}


/// Absolute x rotation.
public float XRotation Toggle
{
get { return m_rotationX; }
set { RotateAbs( value, m_rotationY, m_rotationZ ); }
}


/// Absolute y rotation.
public float YRotation Toggle
{
get { return m_rotationY; }
set { RotateAbs( m_rotationX, value, m_rotationZ ); }
}


/// Absolute z rotation.
public float ZRotation Toggle
{
get { return m_rotationZ; }
set { RotateAbs( m_rotationX, m_rotationY, value ); }
}


/// Absolute x scale.
public float XScale Toggle
{
get { return m_scale.M11; }
set { m_scale.M11 = value; }
}


/// Absolute y scale.
public float YScale Toggle
{
get { return m_scale.M22; }
set { m_scale.M22 = value; }
}


/// Absolute z scale.
public float ZScale Toggle
{
get { return m_scale.M33; }
set { m_scale.M33 = value; }
}

}
}

The WorldTransform class contains methods to translate, rotate, and scale to either absolute values or relative to the current transformation. The translation and scaling values are the easiest to set because they can be put straight into their corresponding matrices. The rotation values, on the other hand, cannot be put directly into the rotation matrix. We need to create_ a rotation matrix by calling Matrix.RotationYawPitchRoll.

The Transform property concatenates the 3 matrices together and returns the result. The order of the concatenation is important because matrix multiplication is noncommutative. The scaling matrix should be applied first, followed by the rotation matrix, and then the translation matrix.

private float Xdirection = 3f;
private float Ydirection = 3f;
private float scale = 1f;

/// Updates a frame prior to rendering.
/// The Direct3D device
/// Time elapsed since last frame
public override void OnUpdateFrame( Device device, float elapsedTime )
{
// Translation
m_transform.TranslateRel( Xdirection * elapsedTime, Ydirection * elapsedTime, 0f );
if ( m_transform.XPosition > 4f || m_transform.XPosition < –4f )
{
m_transform.XPosition = Math.Max( –4f, m_transform.XPosition );
m_transform.XPosition = Math.Min( 4f, m_transform.XPosition );
Xdirection *= –1f;
}
if ( m_transform.YPosition > 4f || m_transform.YPosition < –4f )
{
m_transform.YPosition = Math.Max( –4f, m_transform.YPosition );
m_transform.YPosition = Math.Min( 4f, m_transform.YPosition );
Ydirection *= –1f;
}

// Rotation
m_transform.RotateRel( 0f, 0f, 6.28f * elapsedTime );

// Scale
float _scale = scale * elapsedTime;
m_transform.ScaleRel( _scale, _scale, _scale );
if ( m_transform.XScale > 2f || m_transform.XScale < 0.5f )
{
m_transform.XScale = Math.Max( 0.5f, m_transform.XScale );
m_transform.XScale = Math.Min( 2f, m_transform.XScale );
scale *= –1.0f;
}
}

Transforming geometry is made easy with the WorldTransform class. Each frame, we can update_ the transform by applying a translation, rotation, and a scale. Notice that I am multiplying the amount to transform by the elapsedTime passed into the method. This is necessary in order to have a consistent animation speed over different machines. We’ll go over how to get this value in a later tutorial.

/// Renders the current frame.
/// The Direct3D device
/// Time elapsed since last frame
public override void OnRenderFrame( Device device, float elapsedTime )
{
device.Clear( ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0 );
device.BeginScene();

// Clip…

// Render buffer
device.VertexFormat = PositionColored.Format;
device.Transform.World = m_transform.Transform;
device.SetStreamSource( 0, m_vb4, 0, PositionColored.StrideSize );
device.Indices = m_ib4;
device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 4, 0, 2 );

// Clip…

device.EndScene();
device.Present();
}

When we render the geometry, all we do is set the world transform of the device to the matrix created by the WorldTransform class.

On to the next tutorial!