Drawing Text

Again, drawing text is something that has already been done. However you just did the bare minimum before; we will delve a little deeper into the methods now. In the previous examples, there has been a Font object both in the Microsoft.DirectX.Direct3D namespace, as well as the System.Drawing namespace. You must distinguish between them, so in order to help facilitate this, you should use the following using clause:

 using Direct3D = Microsoft.DirectX.Direct3D; 

This will allow us to the use the Direct3D alias instead of having to type the entire namespace out. With that, let's go ahead and create a new project for our text drawing example. Make sure you add the references and the using clauses to the project (including the previous one). Also, ensure that you have a private variable for our device, and the window style set correctly for our rendering. With those items done, add the following variables:

 private Direct3D.Font font = null; private Mesh mesh = null; private Material meshMaterial; private float angle = 0.0f; 

Here we are declaring a font that will allow us to draw text on the screen, as well as a mesh and that mesh's material. The mesh will actually be simple text that has been extruded and is a 3D mesh rather than the simple 2D text we'll be drawing as well. The angle parameter we've used before will allow the 3D text to rotate around based on frame rate. We now need to initialize the graphics with the method found in Listing 10.3.

Listing 10.3 Initializing Your Graphics for Font Drawing
 public void InitializeGraphics() {     // Set our presentation parameters     PresentParameters presentParams = new PresentParameters();     presentParams.Windowed = true;     presentParams.SwapEffect = SwapEffect.Discard;     presentParams.AutoDepthStencilFormat = DepthFormat.D16;     presentParams.EnableAutoDepthStencil = true;     // Create our device     device = new Device(0, DeviceType.Hardware, this,        CreateFlags.SoftwareVertexProcessing, presentParams);     device.DeviceReset += new System.EventHandler(this.OnDeviceReset);     OnDeviceReset(device, null);     // What font do we want to use?     System.Drawing.Font localFont = new System.Drawing.Font         ("Arial", 14.0f, FontStyle.Italic);     // Create an extruded version of this font     mesh = Mesh.TextFromFont(device, localFont, "Managed DirectX",             0.001f, 0.4f);     // Create a material for our text mesh     meshMaterial = new Material();     meshMaterial.Diffuse = Color.Peru;     // Create a font we can draw with     font = new Direct3D.Font(device, localFont); } 

We create a hardware device with a depth buffer here, and hook the DeviceReset event. Since we will only need to set our lights and camera up once per device reset, we will put that code in the event handler (we'll get to that in a moment). Finally, we create a System.Drawing.Font that we will use as the basis for both our 2D and 3D text items. I picked a 14-point Arial font, but feel free to pick any valid font on your system. We first create our 3D extruded mesh from the font. We will use the string "Managed DirectX" to extrude. You will need an entirely new mesh for every different string you want to render in 3D. We then set our material to any color we want and create our 2D font.

RENDERING EXTRUDED TEXT

Make sure that 3D extruded text is really what you want when you are deciding what to render. Drawing some 2D text can be done with two simple triangles, while drawing 3D extruded text can take thousands of triangles.

You will want to set up the camera and lights in the event handler method that you've used here as well, so define that like this:

 private void OnDeviceReset(object sender, EventArgs e) {     Device dev = (Device)sender;     dev.Transform.Projection = Matrix.PerspectiveFovLH(         (float)Math.PI / 4, this.Width / this.Height, 1.0f, 100.0f);     dev.Transform.View = Matrix.LookAtLH(new Vector3(0,0, -9.0f),         new Vector3(), new Vector3(0,1,0));     dev.Lights[0].Type = LightType.Directional;     dev.Lights[0].Diffuse = Color.White;     dev.Lights[0].Direction = new Vector3(0, 0, 1);     dev.Lights[0].Commit();     dev.Lights[0].Enabled = true; } 

The camera and lights are really only for the 3D extruded text. The 2D text has all been pre-transformed and lit, so these options aren't necessary. However, the extruded text is actually a real 3D model, so in order to have it look correct we create our camera and light. Let's add a method to draw this 3D extruded text now:

 private void Draw3DText(Vector3 axis, Vector3 location) {     device.Transform.World = Matrix.RotationAxis(         axis, angle) *         Matrix.Translation(location);     device.Material = meshMaterial;     mesh.DrawSubset(0);     angle += 0.01f; } 

As you can see here, we pass in the location of the mesh in world space, and the axis we want it to rotate on. This method is similar to the DrawMesh method we've used in previous examples, as it simply sets the material and draws the first mesh subset. In this extruded mesh, we know there will only ever be one subset. We also increase our angle parameter. Controlling the "animation" this way makes the movement be controlled by frame rate, which we already know is a bad thing, but for readability we will use this method. Now, let's add our method to draw some 2D text:

 private void Draw2DText(string text, int x, int y, Color c) {     font.DrawText(null, text, new Rectangle(x, y,         this.Width , this.Height ),         DrawTextFormat.NoClip | DrawTextFormat.ExpandTabs |         DrawTextFormat.WordBreak , c); } 

Here we pass in the actual text we want to render, as well as the position (in screen coordinates) where we want the upper left corner of the text to reside. The last parameter is naturally the color. You'll notice now that in our call to DrawText, we include the width and height of the window in the rectangle we pass in. We do this because we've also passed in the WordBreak flag, which will cause automatic word wrapping to occur if the text starts to run past the end of the bounding rectangle. We also want any tabs in the text to be expanded as well, and the text not to be clipped.

With the two main methods for rendering our text in place, we now only need to include our OnPaint override (see Listing 10.4):

Listing 10.4 Rendering Your Text
 protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {     device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);     device.BeginScene();     Draw2DText("Here's some text", 10, 10, Color.WhiteSmoke);     Draw2DText("Here's some text\r\nwith\r\nhard\r\nline breaks",         100, 80, Color.Violet);     Draw2DText("This\tis\tsome\ttext\twith\ttabs.",         this.Width / 2, this.Height - 80 , Color.RoyalBlue);     Draw2DText("If you type enough words in a single sentence, " +         "you may notice that the text begins to wrap.  Try resizing " +         "the window to notice how the text changes as you size it.",         this.Width / 2 + this.Width / 4, this.Height / 4 , Color.Yellow);     // Draw our two spinning meshes     Draw3DText(new Vector3(1.0f, 1.0f, 0.0f),         new Vector3(-3.0f, 0.0f, 0.0f));     Draw3DText(new Vector3(0.0f, 1.0f, 1.0f),         new Vector3(0.0f, -1.0f, 1.0f));     device.EndScene();     device.Present();     this.Invalidate(); } 

You'll notice that at first, we draw simple text like we've done before; just a short sentence in the upper left. Next we draw some new text with actual hard line breaks. We expect this text to be rendered with these line breaks. After that we draw some words that are separated with tabs instead of spaces. Since we included the ExpandTabs flag, these will be expanded when rendered. Finally, we draw one very large sentence. There are no hard line breaks in this sentence, but we fully expect it to have the word wrap feature invoked on it. Finally, add your main method:

 static void Main() {     using (Form1 frm = new Form1())     {         // Show our form and initialize our graphics engine         frm.Show();         frm.InitializeGraphics();         Application.Run(frm);     } } 

Running the application now will render some text that looks like what you see in Figure 10.2.

Figure 10.2. Rendered text.

graphics/10fig02.gif

ENHANCING FONT PEFORMANCE

The Font class draws its text based on textures that it creates to hold the rendered letters. Rendering these letters to a texture happens via GDI, and can potentially be slow. It may be wise to call the two preload methods on the font class during startup to ensure you don't hit these load times during runtime. You can call PreloadCharacters to load a specific set of characters, or PreloadText to load a specific string.

Feel free to resize this window like the text asks you to. You'll notice that as the window size changes, the text wraps in different ways. You'll also notice even the text that doesn't wrap initially can start wrapping.



Managed DirectX 9 Graphics and Game Programming, Kick Start
Managed DirectX 9 Kick Start: Graphics and Game Programming
ISBN: B003D7JUW6
EAN: N/A
Year: 2002
Pages: 180
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net