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);
}
}
}
}