|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis project is written in C# and uses Managed Direct X 9. You will need to have the SDK installed to use this. The code was created with Visual Studio 7.1. This project is a clock. Each digit of the clock is represented by pages. Each page contains the top half of a number on top and the bottom half on bottom. Two pages are shown next to each other, one with the top of the number and one with the bottom of the number. Put them together and you have a whole number. When shown this way, you can flip the pages, going from one number to another. See the last digit in the picture for an example of this, it is hard to explain. My code is fairly well documented, so you can probably learn how it did this just by looking at it. Here, I will discuss some of the more important parts. To do anything, we need to get the device that we will be drawing to. We also need to set up that device. The following code does this: private static Device device = null;// PresentParameters are used for the display device
PresentParameters presentParams = new PresentParameters();
// this is a windowed application
presentParams.Windowed = true;
// set the swap effect
presentParams.SwapEffect = SwapEffect.Discard;
// tell the device to use depth information
// and format to use, this will insure that things that
// should be infront will be
presentParams.AutoDepthStencilFormat = DepthFormat.D16;
presentParams.EnableAutoDepthStencil = true;
// create our display device
// we are using the default display device, and doing
// the vertex processing with the cpu, this will insure
// compatability with older cards
device = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, presentParams);
// we subscribe to the device reset event. A reset occurs
// when something occurs with our window
//such as resizing.
// We need to capture this event because during a reset
// we lose any information in video memory.
//Therefore we need to setup some stuff again.
device.DeviceReset += new EventHandler(this.OnDeviceReset);
// Set up the device now
this.OnDeviceReset(device, null);
We need to respond to the // Set up the projection matrix, this defines the volume
// that is visible on the screen, and its perspective.
device.Transform.Projection =
Matrix.PerspectiveFovLH((float)Math.PI/4,
this.Width/this.Height,1.0f,100.0f);
// Set up the view matrix. This is our camera.
// The camera will be located along the z axis,
// looking at the origin, which is where we will display the clock.
device.Transform.View = Matrix.LookAtLH(new Vector3(0,0,10f),
new Vector3(),new Vector3(0,1,0));
The other things I choose to do in // turn on lighting
device.RenderState.Lighting = true;
// place a ambient white light in our scene. An ambient light
// touches all surfaces that we draw, and
// affects them all equally. We set it to white so that the surfaces
// will look the color they actually are.
device.RenderState.Ambient = Color.White;
Now that our device is set up, we can draw whatever we want in the // create the mesh for our dot
dotMesh = Mesh.Sphere(device, .2f, 18, 18);
// because the default sphere mesh contains
// no color information for it's vertices,
// we will use ambient lighting to provide this color.
// the dot should reflect ambient light as if it is a black surface
// the color the dot will actually appear is based
// on its color and the color of the light
dotMaterial = new Material();
dotMaterial.Ambient = Color.Black;
Now that we have our mesh, we can easily draw it. We first need to tell the device what material it should use, and what texture it should use. We do not have a texture for our mesh, so we set this to // set the material and texture. These are properties
// of the device. All things drawn
// will use this material and texture.
// set the material to the dot material
device.Material = dotMaterial;
// set the texture to null
device.SetTexture(0,null);
Our final say on how our mesh will be drawn is done with the world transform. By default, our mesh is located at the origin. We will use a translation (movement) to place our mesh where we would like the dot to be located. // translate (move along the x,y,z axis) all things draw
device.Transform.World = Matrix.Translation(2.5f,.6f,0);
Now, we draw our mesh. Each mesh is composed of subsets, which are little pieces of the mesh. When drawing a mesh, you need to draw each of its subsets. However, since we have a really simple mesh, it only has one subset. To actually draw our mesh, we draw this one subset. Notice that we don't need to specify the device to draw on because we have already told the mesh which device it was meant to be used with. // draw one dot
dotMesh.DrawSubset(0);
Now, we will talk about drawing our numbers. In my code, each number is a class and is drawn by calling its texTop = new Texture[10];
texTop[0] = new Texture(device, new Bitmap(this.GetType(),
"0top.bmp"),Usage.Dynamic, Pool.Default);
texTop[1] = new Texture(device, new Bitmap(this.GetType(),
"1top.bmp"),Usage.Dynamic, Pool.Default);
texTop[2] = new Texture(device, new Bitmap(this.GetType(),
"2top.bmp"),Usage.Dynamic, Pool.Default);
texTop[3] = new Texture(device, new Bitmap(this.GetType(),
"3top.bmp"),Usage.Dynamic, Pool.Default);
texTop[4] = new Texture(device, new Bitmap(this.GetType(),
"4top.bmp"),Usage.Dynamic, Pool.Default);
texTop[5] = new Texture(device, new Bitmap(this.GetType(),
"5top.bmp"),Usage.Dynamic, Pool.Default);
texTop[6] = new Texture(device, new Bitmap(this.GetType(),
"6top.bmp"),Usage.Dynamic, Pool.Default);
texTop[7] = new Texture(device, new Bitmap(this.GetType(),
"7top.bmp"),Usage.Dynamic, Pool.Default);
texTop[8] = new Texture(device, new Bitmap(this.GetType(),
"8top.bmp"),Usage.Dynamic, Pool.Default);
texTop[9] = new Texture(device, new Bitmap(this.GetType(),
"9top.bmp"),Usage.Dynamic, Pool.Default);
texBottom = new Texture[10];
texBottom[0] = new Texture(device, new Bitmap(this.GetType(),
"0bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[1] = new Texture(device, new Bitmap(this.GetType(),
"1bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[2] = new Texture(device, new Bitmap(this.GetType(),
"2bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[3] = new Texture(device, new Bitmap(this.GetType(),
"3bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[4] = new Texture(device, new Bitmap(this.GetType(),
"4bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[5] = new Texture(device, new Bitmap(this.GetType(),
"5bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[6] = new Texture(device, new Bitmap(this.GetType(),
"6bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[7] = new Texture(device, new Bitmap(this.GetType(),
"7bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[8] = new Texture(device, new Bitmap(this.GetType(),
"8bottom.bmp"),Usage.Dynamic, Pool.Default);
texBottom[9] = new Texture(device, new Bitmap(this.GetType(),
"9bottom.bmp"),Usage.Dynamic, Pool.Default);
To actually draw our numbers, we need to draw primitives (in our case triangles) that the textures will be drawn on. The drawing is done with triangles because there are no squares. We make a square by sticking 2 triangles together. Therefore, each of our textures will be drawn by specifying 6 points or vertices. These size points will then be connected to form 2 triangles. One important detail is that we will be listing the vertices in counter-clockwise order. DirectX looks at the order that the vertices are listed in to determine if we are facing the texture. If we are not, it won't draw it unless we specifically tell it to. Because of this little thing, our page is actually consisted of 12 vertices, 6 for the front and 6 for the back. We define our vertices with the following code: // the coordinates in the front face of the page,
// this is the top half of a number
verts[0] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(-1.0f,
1.0f,0.0f,1.0f,0.0f);
verts[1] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(-1.0f,
0.0f,0.0f,1.0f,1.0f);
verts[2] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(1.0f,
1.0f,0.0f,0.0f,0.0f);
verts[3] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(-1.0f,
0.0f,0.0f,1.0f,1.0f);
verts[4] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(1.0f,
0.0f,0.0f,0.0f,1.0f);
verts[5] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(1.0f,
1.0f,0.0f,0.0f,0.0f);
// the coordinate in the back face of the page,
// this is the bottom half of a number
verts[6] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(-1.0f,
1.0f,0.0f,1.0f,1.0f);
verts[7] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(1.0f,
1.0f,0.0f,0.0f,1.0f);
verts[8] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(1.0f,
0.0f,0.0f,0.0f,0.0f);
verts[9] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(1.0f,
0.0f,0.0f,0.0f,0.0f);
verts[10] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(-1.0f,
0.0f,0.0f,1.0f,0.0f);
verts[11] = new Microsoft.DirectX.Direct3D.CustomVertex.PositionTextured(-1.0f,
1.0f,0.0f,1.0f,1.0f);
Notice that each vertex is defined by 5 numbers. The x, y, and z coordinates of the vertex, and the x and y coordinate of the texture that should line up with the vertex. (Note: these coordinates are normalized to a 0-1 scale.) When drawing our triangles, the texture will be laid on them in such a way that the coordinates of the texture will match up with those that we assign to the vertex. We place the vertices in a vertex buffer that we created using the following code. We will provide this buffer to the device when drawing our vertices. // vertex buffer storing coordinates used when drawing digit
private static VertexBuffer vb = null;
// create vertex buffer
vb = new VertexBuffer(typeof(CustomVertex.PositionTextured),
12,device,Usage.Dynamic | Usage.WriteOnly,
CustomVertex.PositionTextured.Format, Pool.Default);
We place the vertices in the buffer using the following line of code: // take the date from verts and put in our vertex buffer
buffer.SetData(verts,0,LockFlags.None);
The last thing we need to do before drawing our page is setting up the material. Again, we are not supplying any color information with our vertices, and so we need to do this: private static Material material;
material = new Material();
material.Ambient = Color.White;
Now, we draw our page. First, we tell the device what material and texture to use. We also tell it what format our vertices are in, and where to find our vertices. To make our page appear at the location and at the rotation that we would like, we change to world transform. // set up the material, so light will get reflected
device.Material = material;
// tell the device what format the vertices we will be drawing are in
device.VertexFormat = CustomVertex.PositionTextured.Format;
// tell the device which buffer to get the vertices from
device.SetStreamSource(0,vb,0);
// set texture for front of page and draw it
device.SetTexture(0,top);
// set world transform to draw page at right location and angle
device.Transform.World = Matrix.RotationX(angle) * Matrix.Translation(x,y,z);
We use device.DrawPrimitives(PrimitiveType.TriangleList,0,2);
To draw the back face, we just change the texture and draw the triangles defining the back face. device.SetTexture(0,bottom);
device.DrawPrimitives(PrimitiveType.TriangleList,6,2);
We repeat these processes for each page, making up our number. I hope that you could understand and follow my explanation. Don't hesitate to ask for more information. Enjoy!!
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||