How to: Transform Direct3D Objects
You can move Direct3D objects in a scene by using world transformations.
Note |
---|
Managed Direct3D mobile applications require Windows Mobile version 5.0 software for Pocket PCs and Smartphones. See External Resources for the .NET Compact Framework for information about Windows Mobile software and SDKs. |
World space is similar to 3-dimensional Cartesian space that extends infinitely in all directions. World transformations define where Direct3D objects are placed in world space, before the view transformation translates the objects from world space to screen space. Using world transformations, you can translate (move), rotate, and scale Direct3D objects.
To combine multiple world transformations on an object, you must multiply their transformation matrices. In the following example, a rotation matrix and a translation matrix are multiplied using the Multiply method. You must transform matrices in the correct order to obtain the desired result. For example, to multiply a rotation matrix and a translation matrix, you must transform rotations before translations, as demonstrated in the following example.
Example
The following example animates a primitive box mesh that represents a ship. The example provides a complete form that includes the following objects:
A primitive Mesh object representing a ship.
A set of primitive Mesh objects representing buildings.
Several Light objects to provide light. One of these objects represents light from the ship engines.
A Device object.
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports Microsoft.WindowsMobile.DirectX
Imports Microsoft.WindowsMobile.DirectX.Direct3D
Class MatrixTransformsHowTo
Inherits Form
' Mesh representing the ship.
Private shipMesh As Mesh = Nothing
' Meshes representing buildings.
Private wallMeshes(2) As Mesh
Private meshColor As Color = Color.Goldenrod
' Description of the Direct3D light.
Private lightData As Light
Private device As Device
Private Enum ShipStatus
SS_LIFTOFF = 1
SS_TURNING = 2
SS_ENGINEON = 3
End Enum
Private myShipStatus As ShipStatus = ShipStatus.SS_LIFTOFF
Private firstTick As Integer = 0
Private newTick As Integer = 0
Private yVal As Single = - 2F
Private zVal As Single = 2F
Private Const midAltitude As Integer = 85
Private yCameraPosition As Single = - 1
Private xCameraPosition As Single = - 2
Private startAngle As Single = 0F
Private angle As Single = 0.1F
Private lastIncrement As Single = 0F
Private isEngineFired As Boolean = False
Private isShipDeparted As Boolean = False
Private isNewRotationOperation As Boolean = True
Public Sub New()
Dim present As PresentParameters
Me.Text = "Flying Ship"
' Enable the form to be closed.
' Required so that Hwnd of the form changes.
Me.MinimizeBox = False
present = New PresentParameters()
present.Windowed = True
present.AutoDepthStencilFormat = DepthFormat.D16
present.EnableAutoDepthStencil = True
present.SwapEffect = SwapEffect.Discard
device = New Device(0, DeviceType.Default, Me, CreateFlags.None, present)
AddHandler device.DeviceReset, AddressOf OnDeviceReset
Dim i As Integer
For i = 0 To wallMeshes.Length
wallMeshes(i) = Nothing
Next i
OnDeviceReset(Nothing, EventArgs.Empty)
End Sub
Private Sub OnDeviceReset(ByVal sender As Object, ByVal e As EventArgs)
' Meshes must be recreated whenever the device
' is reset, no matter which pool they are created in.
' Instead of loading a mesh from a file,
' this sample uses primitive box meshes
' to represent the ship and the buildings.
shipMesh = Mesh.Box(device, 0.8F, 0.18F, 2.2F)
wallMeshes(0) = Mesh.Box(device, 0.5F, 3.6F, 1F)
wallMeshes(1) = Mesh.Box(device, 0.5F, 1.8F, 2F)
wallMeshes(2) = Mesh.Box(device, 0.2F, 1F, 0.75F)
device.RenderState.Ambient = Color.White
' Provides main directional lighting.
device.Lights(0).Type = LightType.Directional
device.Lights(0).Direction = New Vector3(0.3F, - 0.5F, 0.2F)
device.Lights(0).Diffuse = Color.LightBlue
device.Lights(0).Update()
' Provides frontal lighting.
device.Lights(1).Type = LightType.Directional
device.Lights(1).Direction = New Vector3(0F, - 1F, - 3F)
device.Lights(1).Diffuse = Color.DarkSlateGray
device.Lights(1).Update()
' Turn on the lights.
device.Lights(0).Enabled = True
device.Lights(1).Enabled = True
' Turn off the light representing the engine.
device.Lights(2).Enabled = False
' For the projection matrix, set up a perspective transform (which
' transforms geometry from 3-D view space to 2-D viewport space, with
' a perspective divide that makes objects smaller in the distance). To build
' a perspective transform, you need the field of view (1/4 PI is common),
' the aspect ratio, and the near and far clipping planes (which define at
' the distances at which geometry should be no longer be rendered).
device.Transform.Projection = Matrix.PerspectiveFovLH(System.Convert.ToSingle(Math.PI) / 4F, System.Convert.ToSingle(Me.ClientSize.Width) / System.Convert.ToSingle(Me.ClientSize.Height), 1F, 80F)
End Sub
Protected Overrides Sub OnPaintBackground(ByVal e As PaintEventArgs)
' Do nothing.
End Sub
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
Dim material As New Material()
Dim engineMaterial As New Material()
' Begin the scene and clear the back buffer to black.
device.Clear(ClearFlags.Target Or ClearFlags.ZBuffer, Color.Black, 1F, 0)
device.BeginScene()
material.Diffuse = Color.WhiteSmoke
' Specifies the ambient color for the engines.
engineMaterial.Ambient = Color.White
SetupMatrices()
device.Material = material
' Draw ship on the screen.
shipMesh.DrawSubset(0)
SetupMovingLight()
If isEngineFired Then
device.Material = engineMaterial
device.Lights(2).Enabled = True
' Bind the vertex buffers of the primitive
' mesh to the Device object.
device.SetStreamSource(0, shipMesh.VertexBuffer, 0)
' Redraw the face of the ship representing the engine.
' A Box mesh has 4 vertices per face. The 20th vertex
' is the first vertex representing the engine. To use
' adjacent triangles, set the type to Primitive.TriangleFan.
device.DrawPrimitives(PrimitiveType.TriangleFan, 20, 2)
End If
material.Diffuse = Color.GhostWhite
device.Material = material
' Draw buildings, providing coordinates to locate each
' building on the x, y, and z planes. Because the camera is placed
' "behind" the scene initially (at a positive z-axis value in
' the call to Matrix.LookAtLH), positive z-axis values draw
' objects closer to the camera. In addition, positive x-axis
' values draw objects farther to the left instead of to the right.
' Draw the tall building.
device.Transform.World = Matrix.Translation(0.75F, - 0.2F, - 2F)
wallMeshes(0).DrawSubset(0)
' Draw the medium-sized buildings.
device.Transform.World = Matrix.Translation(- 1F, - 0.9F, 0F)
wallMeshes(1).DrawSubset(0)
device.Transform.World = Matrix.Translation(0F, - 0.9F, 0F)
wallMeshes(1).DrawSubset(0)
device.Transform.World = Matrix.Translation(1F, - 0.9F, 0F)
wallMeshes(1).DrawSubset(0)
device.Transform.World = Matrix.Translation(2F, - 0.9F, 0F)
wallMeshes(1).DrawSubset(0)
' Draw the small buildings.
device.Transform.World = Matrix.Translation(- 2F, - 1.5F, 5F)
wallMeshes(2).DrawSubset(0)
device.Transform.World = Matrix.Translation(- 1.25F, - 1.5F, 5F)
wallMeshes(2).DrawSubset(0)
device.Transform.World = Matrix.Translation(- 0.5F, - 1.5F, 5F)
wallMeshes(2).DrawSubset(0)
device.Transform.World = Matrix.Translation(0.75F, - 1.5F, 5F)
wallMeshes(2).DrawSubset(0)
' Finish the scene and present it on the screen.
device.EndScene()
device.Present()
' Repaint the scene.
Me.Invalidate()
End Sub
Private Sub SetupMatrices()
' Set the transformation matrices.
Dim fAngle As Single = angle
' To render the ship, combine a rotation on the y-axis with a
' translation (move) using the Matrix.Multiply method.
device.Transform.World = Matrix.Multiply(Matrix.RotationY(fAngle + startAngle), Matrix.Translation(- 0.5F, yVal, zVal))
' Set up the view matrix. You can define a view matrix with a camera position,
' a point to look at (camera target), and an "up" direction.
' First vector passed to LookAtLH is the camera position.
' Second vector passed to LookAtLH is the camera target.
' Third vector passed to LookAtLH defines the "up" direction.
' In this example, you set the camera seven units up along the z-axis ("behind"
' the scene), down one unit, and left two units. You then point the camera
' just above the origin and define "up" to be in the y-direction.
If Not isShipDeparted Then
device.Transform.View = Matrix.LookAtLH(New Vector3(- 2, - 1, 7), New Vector3(0, 1, 0), New Vector3(0, 1, 0))
Else
' Handles movement of camera after
' the ship "fires" the main engines.
device.Transform.View = Matrix.LookAtLH(New Vector3(xCameraPosition, yCameraPosition, 7), New Vector3(0, 1, 0), New Vector3(0, 1, 0))
xCameraPosition += 0.01F
yCameraPosition += 0.01F
End If
' Use the system time to control the animation.
' The high-resolution timer, if present for
' the hardware, could be used instead.
Dim tick As Integer = System.Environment.TickCount
If newTick = 0 Then
firstTick = tick / 100
End If
newTick = tick / 100 - firstTick + 1
' Use the tick count to change the current
' ship status. Animation is then
' dependent on the current status.
If newTick <= 10 Then
myShipStatus = ShipStatus.SS_LIFTOFF
ElseIf newTick <= midAltitude Then
myShipStatus = ShipStatus.SS_TURNING
Else
myShipStatus = ShipStatus.SS_ENGINEON
End If
Select Case myShipStatus
Case ShipStatus.SS_LIFTOFF
yVal += 0.015F
Case ShipStatus.SS_TURNING
yVal += 0.015F
angle = SetRotation(angle, 180F)
Case ShipStatus.SS_ENGINEON
isEngineFired = True
zVal = zVal - 0.04F
yVal = yVal + 0.005F
angle = SetRotation(angle, 180F)
If newTick > midAltitude + 30 Then
isShipDeparted = True
End If
End Select
End Sub
Private Function SetRotation(ByVal tempAngle As Single, ByVal rotationThreshold As Single) As Single
' SetRotation manipulates rotation values to simulate a vessel that
' gradually increases in turning speed, and then slows to
' a stop. rotationThreshold should be <= 180 degrees.
If isNewRotationOperation Then
' Reset values if this is a new rotation operation.
' Starting angle of ship must be added back in
' before the call to Matrix.RotationY.
tempAngle = 0.1F
isNewRotationOperation = False
End If
rotationThreshold = DegreesToRadians(rotationThreshold)
If tempAngle < rotationThreshold Then
Dim increment As Single = tempAngle
' Provide a gradual but increasing turning speed.
tempAngle *= 1.015F
lastIncrement = tempAngle - increment
Return tempAngle
Else
' Provide a gradual slowing to a stop.
tempAngle += lastIncrement * 0.75F
lastIncrement = lastIncrement * 0.75F
Return tempAngle
End If
End Function
Private Function DegreesToRadians(ByVal degrees As Single) As Single
Dim radians As Single = degrees *(3.141593F / 180F)
Return radians
End Function
Private Sub SetupMovingLight()
device.Lights(2).Type = LightType.Point
lightData = device.Lights(2)
device.Lights(2).Diffuse = Color.White
device.Lights(2).Range = 200F
If Not device.DeviceCaps.VertexProcessingCaps.SupportsPositionalLights Then
If device.LightsFixed(2).Type = LightType.Point Then
device.LightsFixed(2).Type = LightType.Directional
End If
End If
' Handle positioning for the light that emanates
' from the ship, representing the light from
' the main engines.
Select Case device.Lights(2).Type
Case LightType.Point
device.Lights(2).Position = New Vector3(0, yVal, zVal)
device.Lights(2).Attenuation1 = 0.2F
Case LightType.Directional
End Select ' Not implemented.
device.Lights(2).Update()
End Sub
Shared Sub Main()
Try
Dim d3dApp As New MatrixTransformsHowTo()
System.Windows.Forms.Application.Run(d3dApp)
Catch
MessageBox.Show("Your device does not have the needed 3-D " + "support to run this sample")
Catch
MessageBox.Show("Your device does not have the needed 3-D " + "support to run this sample")
Catch e As Exception
MessageBox.Show("The sample has run into an error and needs" + "to close: " + e.Message)
End Try
End Sub
End Class
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;
namespace MatrixTransforms
{
class MatrixTransformsHowTo : Form
{
// Mesh representing the ship.
Mesh shipMesh = null;
// Meshes representing buildings.
Mesh[] wallMeshes = new Mesh[3];
Color meshColor = Color.Goldenrod;
// Description of the Direct3D light.
private Light lightData;
Device device;
private enum ShipStatus
{
SS_LIFTOFF = 1,
SS_TURNING = 2,
SS_ENGINEON = 3
}
ShipStatus myShipStatus = ShipStatus.SS_LIFTOFF;
int firstTick = 0;
int newTick = 0;
float yVal = -2.0f;
float zVal = 2.0f;
const int midAltitude = 85;
float yCameraPosition = -1;
float xCameraPosition = -2;
float startAngle = 0.0f;
float angle = 0.1f;
float lastIncrement = 0.0f;
bool isEngineFired = false;
bool isShipDeparted = false;
bool isNewRotationOperation = true;
public MatrixTransformsHowTo()
{
PresentParameters present;
this.Text = "Flying Ship";
// Enable the form to be closed.
// Required so that Hwnd of the form changes.
this.MinimizeBox = false;
present = new PresentParameters();
present.Windowed = true;
present.AutoDepthStencilFormat = DepthFormat.D16;
present.EnableAutoDepthStencil = true;
present.SwapEffect = SwapEffect.Discard;
device = new Device(0, DeviceType.Default, this,
CreateFlags.None, present);
device.DeviceReset += new EventHandler(OnDeviceReset);
for (int i = 0; i < wallMeshes.Length; i++)
{
wallMeshes[i] = null;
}
OnDeviceReset(null, EventArgs.Empty);
}
private void OnDeviceReset(object sender, EventArgs e)
{
// Meshes must be recreated whenever the device
// is reset, no matter which pool they are created in.
// Instead of loading a mesh from a file,
// this sample uses primitive box meshes
// to represent the ship and the buildings.
shipMesh = Mesh.Box(device, .8f, 0.18f, 2.2f);
wallMeshes[0] = Mesh.Box(device, 0.5f, 3.6f, 1.0f);
wallMeshes[1] = Mesh.Box(device, 0.5f, 1.8f, 2.0f);
wallMeshes[2] = Mesh.Box(device, .2f, 1.0f, 0.75f);
device.RenderState.Ambient = Color.White;
// Provides main directional lighting.
device.Lights[0].Type = LightType.Directional;
device.Lights[0].Direction = new Vector3(0.3f, -0.5f, 0.2f);
device.Lights[0].Diffuse = Color.LightBlue;
device.Lights[0].Update();
// Provides frontal lighting.
device.Lights[1].Type = LightType.Directional;
device.Lights[1].Direction = new Vector3(0.0f, -1.0f, -3.0f);
device.Lights[1].Diffuse = Color.DarkSlateGray;
device.Lights[1].Update();
// Turn on the lights.
device.Lights[0].Enabled = true;
device.Lights[1].Enabled = true;
// Turn off the light representing the engine.
device.Lights[2].Enabled = false;
// For the projection matrix, set up a perspective transform (which
// transforms geometry from 3-D view space to 2-D viewport space, with
// a perspective divide that makes objects smaller in the distance). To build
// a perspective transform, you need the field of view (1/4 PI is common),
// the aspect ratio, and the near and far clipping planes (which define at
// the distances at which geometry should be no longer be rendered).
device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4.0F,
(float)this.ClientSize.Width / (float)this.ClientSize.Height,
1.0f, 80.0f);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do nothing.
}
protected override void OnPaint(PaintEventArgs e)
{
Material material = new Material();
Material engineMaterial = new Material();
// Begin the scene and clear the back buffer to black.
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black,
1.0f, 0);
device.BeginScene();
material.Diffuse = Color.WhiteSmoke;
// Specifies the ambient color for the engines.
engineMaterial.Ambient = Color.White;
SetupMatrices();
device.Material = material;
// Draw ship on the screen.
shipMesh.DrawSubset(0);
SetupMovingLight();
if (isEngineFired)
{
device.Material = engineMaterial;
device.Lights[2].Enabled = true;
// Bind the vertex buffers of the primitive
// mesh to the Device object.
device.SetStreamSource(0, shipMesh.VertexBuffer, 0);
// Redraw the face of the ship representing the engine.
// A Box mesh has 4 vertices per face. The 20th vertex
// is the first vertex representing the "engine." To use
// adjacent triangles, set the type to Primitive.TriangleFan.
device.DrawPrimitives(PrimitiveType.TriangleFan, 20, 2);
}
material.Diffuse = Color.GhostWhite;
device.Material = material;
// Draw buildings, providing coordinates to locate each
// building on the x, y, and z planes. Because the camera is placed
// "behind" the scene initially (at a positive z-axis value in
// the call to Matrix.LookAtLH), positive z-axis values draw
// objects closer to the camera. In addition, positive x-axis
// values draw objects farther to the left instead of to the right.
// Draw the tall building.
device.Transform.World = Matrix.Translation(.75f, -0.2f, -2.0f);
wallMeshes[0].DrawSubset(0);
// Draw the medium-sized buildings.
device.Transform.World = Matrix.Translation(-1.0f, -0.9f, 0.0f);
wallMeshes[1].DrawSubset(0);
device.Transform.World = Matrix.Translation(0.0f, -0.9f, 0.0f);
wallMeshes[1].DrawSubset(0);
device.Transform.World = Matrix.Translation(1.0f, -0.9f, 0.0f);
wallMeshes[1].DrawSubset(0);
device.Transform.World = Matrix.Translation(2.0f, -0.9f, 0.0f);
wallMeshes[1].DrawSubset(0);
// Draw the small buildings.
device.Transform.World = Matrix.Translation(-2.0f, -1.5f, 5.0f);
wallMeshes[2].DrawSubset(0);
device.Transform.World = Matrix.Translation(-1.25f, -1.5f, 5.0f);
wallMeshes[2].DrawSubset(0);
device.Transform.World = Matrix.Translation(-0.5f, -1.5f, 5.0f);
wallMeshes[2].DrawSubset(0);
device.Transform.World = Matrix.Translation(0.75f, -1.5f, 5.0f);
wallMeshes[2].DrawSubset(0);
// Finish the scene and present it on the screen.
device.EndScene();
device.Present();
// Repaint the scene.
this.Invalidate();
}
private void SetupMatrices()
{
// Set the transformation matrices.
float fAngle = angle;
// To render the ship, combine a rotation on the y-axis with a
// translation (move) using the Matrix.Multiply method.
device.Transform.World = Matrix.Multiply(Matrix.RotationY(fAngle + startAngle), Matrix.Translation(-0.5f, yVal, zVal));
// Set up the view matrix. You can define a view matrix with a camera position,
// a point to look at (camera target), and an "up" direction.
// First vector passed to LookAtLH is the camera position.
// Second vector passed to LookAtLH is the camera target.
// Third vector passed to LookAtLH defines the "up" direction.
// Here, you set the camera seven units up along the z-axis ("behind"
// the scene), down one unit, and left two units. You then point the camera
// just above the origin and define "up" to be in the y-direction.
if (!isShipDeparted)
{
device.Transform.View = Matrix.LookAtLH(new Vector3(-2, -1, 7),
new Vector3(0, 1, 0), new Vector3(0, 1, 0));
}
else
{
// Handles movement of camera after
// the ship "fires" the main engines.
device.Transform.View = Matrix.LookAtLH(new Vector3(xCameraPosition,
yCameraPosition, 7), new Vector3(0, 1, 0), new Vector3(0, 1, 0));
xCameraPosition += 0.01f;
yCameraPosition += 0.01f;
}
// Use the system time to control the animation.
// The high-resolution timer, if present for
// the hardware, could be used instead.
int tick = System.Environment.TickCount;
if (newTick == 0) { firstTick = tick / 100; }
newTick = (tick / 100) - firstTick + 1;
// Use the tick count to change the current
// ship status. Animation is then
// dependent on the current status.
if (newTick <= 10) { myShipStatus = ShipStatus.SS_LIFTOFF; }
else if (newTick <= midAltitude) { myShipStatus = ShipStatus.SS_TURNING; }
else { myShipStatus = ShipStatus.SS_ENGINEON; }
switch (myShipStatus)
{
case ShipStatus.SS_LIFTOFF:
yVal += 0.015f;
break;
case ShipStatus.SS_TURNING:
yVal += 0.015f;
angle = SetRotation(angle, 180.0f);
break;
case ShipStatus.SS_ENGINEON:
isEngineFired = true;
zVal = zVal - 0.04f;
yVal = yVal + 0.005f;
angle = SetRotation(angle, 180.0f);
if (newTick > midAltitude + 30) { isShipDeparted = true; }
break;
}
}
private float SetRotation(float tempAngle, float rotationThreshold)
{
// SetRotation manipulates rotation values to simulate a vessel that
// gradually increases in turning speed, and then slows to
// a stop. rotationThreshold should be <= 180 degrees.
if (isNewRotationOperation)
{
// Reset values if this is a new rotation operation.
// Starting angle of ship must be added back in
// before the call to Matrix.RotationY.
tempAngle = 0.1f;
isNewRotationOperation = false;
}
rotationThreshold = DegreesToRadians(rotationThreshold);
if (tempAngle < rotationThreshold)
{
float increment = tempAngle;
// Provide a gradual but increasing turning speed.
tempAngle *= 1.015f;
lastIncrement = tempAngle - increment;
return tempAngle;
}
else
{
// Provide a gradual slowing to a stop.
tempAngle += (lastIncrement * 0.75f);
lastIncrement = lastIncrement * 0.75f;
return tempAngle;
}
}
private float DegreesToRadians(float degrees)
{
float radians = degrees * (3.141592654f / 180.0f);
return radians;
}
private void SetupMovingLight()
{
device.Lights[2].Type = LightType.Point;
lightData = device.Lights[2];
device.Lights[2].Diffuse = Color.White;
device.Lights[2].Range = 200.0f;
if(!device.DeviceCaps.VertexProcessingCaps.SupportsPositionalLights)
{
if (device.LightsFixed[2].Type == LightType.Point)
device.LightsFixed[2].Type = LightType.Directional;
}
// Handle positioning for the light that emanates
// from the ship, representing the light from
// the main engines.
switch (device.Lights[2].Type)
{
case LightType.Point:
device.Lights[2].Position = new Vector3(0,
yVal, zVal);
device.Lights[2].Attenuation1 = 0.2f;
break;
case LightType.Directional:
// Not implemented.
break;
}
device.Lights[2].Update();
}
static void Main()
{
try
{
MatrixTransformsHowTo d3dApp = new MatrixTransformsHowTo();
System.Windows.Forms.Application.Run(d3dApp);
}
catch(NotSupportedException)
{
MessageBox.Show("Your device does not have the needed 3-D " +
"support to run this sample");
}
catch(DriverUnsupportedException)
{
MessageBox.Show("Your device does not have the needed 3-D " +
"support to run this sample");
}
catch(Exception e)
{
MessageBox.Show("The sample has run into an error and needs" +
"to close: " + e.Message);
}
}
}
}
Compiling the Code
This example requires references to the following namespaces:
See Also
Tasks
How to: Use the High-Resolution Timer
Concepts
.NET Compact Framework How-To Topics