www.pudn.com > IntroductionTo3DGameEngineDesign.rar > D3DFont.cs


//----------------------------------------------------------------------------- 
// File: D3DFont.cs 
// 
// Desc: Shortcut functions for using DX objects 
// 
// Copyright (c) 2001-2002 Microsoft Corporation. All rights reserved 
//----------------------------------------------------------------------------- 
using System; 
using System.Drawing; 
using Microsoft.DirectX; 
using Microsoft.DirectX.Direct3D; 
using Direct3D=Microsoft.DirectX.Direct3D; 
 
 
///  
/// A generic font class  
///  
public class GraphicsFont 
{ 
    public const int MaxNumfontVertices = 50*6; 
   
    // Font rendering flags 
    [System.Flags] 
    public enum RenderFlags 
    { 
        Centered = 0x0001, 
        TwoSided = 0x0002,  
        Filtered = 0x0004, 
    } 
    private System.Drawing.Font systemFont; 
 
    private bool isZEnable = false; 
    public bool ZBufferEnable { get { return isZEnable; } set { isZEnable = value; } } 
    string ourFontName;            // Font properties 
    int ourFontHeight; 
 
	public int LineHeight { get { return ourFontHeight*2; } } 
 
    private Direct3D.Device device; 
    private TextureState textureState0; 
    private TextureState textureState1; 
    private Sampler samplerState0; 
    private RenderStates renderState; 
    private Direct3D.Texture fontTexture; 
    private Direct3D.VertexBuffer vertexBuffer; 
    private CustomVertex.TransformedColoredTextured[] fontVertices = new CustomVertex.TransformedColoredTextured[MaxNumfontVertices]; 
 
    private int textureWidth;                 // Texture dimensions 
    private int textureHeight; 
    private float textureScale; 
    public int spacingPerChar; 
    private float[,] textureCoords = new float[128-32,4]; 
 
    // Stateblocks for setting and restoring render states 
    private StateBlock savedStateBlock; 
    private StateBlock drawTextStateBlock; 
 
 
 
 
    ///  
    /// Create a new font object 
    ///  
    /// The font to use 
    public GraphicsFont(System.Drawing.Font f) 
    { 
        ourFontName = f.Name; 
        ourFontHeight = (int)f.Size; 
        systemFont = f; 
    } 
 
    public GraphicsFont(string fontName) : this(fontName, FontStyle.Regular, 12){} 
    public GraphicsFont(string fontName, FontStyle style) : this(fontName, style, 12){} 
    ///  
    /// Create a new font object 
    ///  
    /// The name of the font 
    /// The style 
    /// Size of the font 
    public GraphicsFont(string fontName, FontStyle style, int size) 
    { 
        ourFontName = fontName; 
        ourFontHeight = size; 
        systemFont = new System.Drawing.Font( fontName, ourFontHeight, style); 
    } 
 
 
 
 
    ///  
    /// Initialize the device objects 
    ///  
    /// The grpahics device used to initialize 
    public void InitializeDeviceObjects(Device dev) 
    { 
        if (dev != null) 
        { 
            // Set up our events 
            dev.DeviceReset += new System.EventHandler(this.RestoreDeviceObjects); 
        } 
 
        // Keep a local copy of the device 
        device = dev; 
        textureState0 = device.TextureState[0]; 
        textureState1 = device.TextureState[1]; 
        samplerState0 = device.SamplerState[0]; 
        renderState = device.RenderState; 
 
        // Establish the font and texture size 
        textureScale  = 1.0f; // Draw fonts into texture without scaling 
 
        // Large fonts need larger textures 
        if( ourFontHeight > 60 ) 
            textureWidth = textureHeight = 2048; 
        else if( ourFontHeight > 30 ) 
            textureWidth = textureHeight = 1024; 
        else if( ourFontHeight > 15 ) 
            textureWidth = textureHeight = 512; 
        else 
            textureWidth  = textureHeight = 256; 
 
        // If requested texture is too big, use a smaller texture and smaller font, 
        // and scale up when rendering. 
        Direct3D.Caps d3dCaps = device.DeviceCaps; 
 
        if( textureWidth > d3dCaps.MaxTextureWidth ) 
        { 
            textureScale = (float)d3dCaps.MaxTextureWidth / (float)textureWidth; 
            textureWidth = textureHeight = d3dCaps.MaxTextureWidth; 
        } 
 
        Bitmap bmp = new Bitmap( textureWidth, textureHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb ); 
        Graphics g = Graphics.FromImage( bmp ); 
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; 
        g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; 
        g.TextContrast = 0; 
 
 
        string str; 
        float x = 0; 
        float y = 0; 
        Point p = new Point(0, 0); 
        Size size = new Size(0,0); 
             
        // Calculate the spacing between characters based on line height 
        size = g.MeasureString( " ", systemFont).ToSize(); 
        x = spacingPerChar = (int) Math.Ceiling( size.Height * 0.3 ); 
 
        for( char c = (char)32; c < (char)127; c++ ) 
        { 
            str = c.ToString(); 
            // We need to do some things here to get the right sizes.  The default implemententation of MeasureString 
            // will return a resolution independant size.  For our height, this is what we want.  However, for our width, we  
            // want a resolution dependant size. 
            Size resSize = g.MeasureString( str, systemFont).ToSize(); 
            size.Height = resSize.Height + 1; 
 
            // Now the Resolution independent width 
            if (c != ' ') // We need the special case here because a space has a 0 width in GenericTypoGraphic stringformats 
            { 
                resSize = g.MeasureString( str, systemFont, p, StringFormat.GenericTypographic).ToSize(); 
                size.Width = resSize.Width; 
            } 
            else 
                size.Width = resSize.Width; 
 
            if( ( x + size.Width + spacingPerChar ) > textureWidth ) 
            { 
                x = spacingPerChar; 
                y += size.Height; 
            } 
 
            if (c != ' ') // We need the special case here because a space has a 0 width in GenericTypoGraphic stringformats 
                g.DrawString( str, systemFont, Brushes.White, new Point((int)x, (int)y), StringFormat.GenericTypographic); 
            else 
                g.DrawString( str, systemFont, Brushes.White, new Point((int)x, (int)y)); 
            textureCoords[c-32,0] = ((float) ( x + 0           - spacingPerChar )) / textureWidth; 
            textureCoords[c-32,1] = ((float) ( y + 0           + 0              )) / textureHeight; 
            textureCoords[c-32,2] = ((float) ( x + size.Width  + spacingPerChar )) / textureWidth; 
            textureCoords[c-32,3] = ((float) ( y + size.Height + 0              )) / textureHeight; 
 
            x += size.Width + (2 * spacingPerChar); 
        } 
 
        // Create a new texture for the font from the bitmap we just created 
        fontTexture = Texture.FromBitmap(device, bmp, 0, Pool.Managed); 
        RestoreDeviceObjects(null, null); 
    } 
 
 
 
 
    ///  
    /// Restore the font after a device has been reset 
    ///  
    public void RestoreDeviceObjects(object sender, EventArgs e) 
    { 
        vertexBuffer = new VertexBuffer( typeof(CustomVertex.TransformedColoredTextured), MaxNumfontVertices, 
            device, Usage.WriteOnly | Usage.Dynamic, 0, Pool.Default ); 
 
        // Create the state blocks for rendering text 
        for( int which=0; which < 2; which++ ) 
        { 
            device.BeginStateBlock(); 
            device.SetTexture( 0, fontTexture ); 
 
            if ( isZEnable ) 
                renderState.ZBufferEnable = true; 
            else 
                renderState.ZBufferEnable = false; 
 
            renderState.AlphaBlendEnable = true; 
            renderState.SourceBlend = Blend.SourceAlpha; 
            renderState.DestinationBlend = Blend.InvSourceAlpha; 
            renderState.AlphaTestEnable = true; 
            renderState.ReferenceAlpha = 0x08; 
            renderState.AlphaFunction = Compare.GreaterEqual; 
            renderState.FillMode = FillMode.Solid; 
            renderState.CullMode = Cull.CounterClockwise; 
            renderState.StencilEnable = false; 
            renderState.Clipping = true; 
            device.ClipPlanes.DisableAll(); 
            renderState.VertexBlend = VertexBlend.Disable; 
            renderState.IndexedVertexBlendEnable = false; 
            renderState.FogEnable = false; 
            renderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha; 
            textureState0.ColorOperation = TextureOperation.Modulate; 
            textureState0.ColorArgument1 = TextureArgument.TextureColor; 
            textureState0.ColorArgument2 = TextureArgument.Diffuse; 
            textureState0.AlphaOperation = TextureOperation.Modulate; 
            textureState0.AlphaArgument1 = TextureArgument.TextureColor; 
            textureState0.AlphaArgument2 = TextureArgument.Diffuse; 
            textureState0.TextureCoordinateIndex = 0; 
            textureState0.TextureTransform = TextureTransform.Disable; // REVIEW 
            textureState1.ColorOperation = TextureOperation.Disable; 
            textureState1.AlphaOperation = TextureOperation.Disable; 
            samplerState0.MinFilter = TextureFilter.Point; 
            samplerState0.MagFilter = TextureFilter.Point; 
            samplerState0.MipFilter = TextureFilter.None; 
 
            if( which==0 ) 
                savedStateBlock = device.EndStateBlock(); 
            else 
                drawTextStateBlock = device.EndStateBlock(); 
        } 
 
     
    } 
 
 
 
    ///  
    /// Draw some text on the screen 
    ///  
    public void DrawText( float xpos, float ypos, Color color, string text) 
    { 
        DrawText( xpos, ypos, color, text, RenderFlags.Filtered ); 
    } 
 
 
    ///  
    /// Draw some text on the screen 
    ///  
    public void DrawText( float xpos, float ypos, Color color, string text, RenderFlags flags) 
    { 
        if( text == null ) 
            return; 
 
        // Setup renderstate 
        savedStateBlock.Capture(); 
		try 
		{ 
			drawTextStateBlock.Apply(); 
			device.SetTexture( 0, fontTexture ); 
			device.VertexFormat = CustomVertex.TransformedColoredTextured.Format; 
			device.PixelShader = null; 
			device.SetStreamSource( 0, vertexBuffer, 0); 
 
			// Set filter states 
			if( (flags & RenderFlags.Filtered) != 0 ) 
			{ 
				samplerState0.MinFilter = TextureFilter.Linear; 
				samplerState0.MagFilter = TextureFilter.Linear; 
			} 
 
			// Adjust for character spacing 
			xpos -= spacingPerChar; 
			float fStartX = xpos; 
 
			// Fill vertex buffer 
			int iv = 0; 
			int dwNumTriangles = 0; 
 
			foreach( char c in text ) 
			{ 
				if( c == '\n' ) 
				{ 
					xpos = fStartX; 
					ypos += (textureCoords[0,3]-textureCoords[0,1])*textureHeight; 
				} 
 
				if( (c-32) < 0 || (c-32) >= 128-32 ) 
					continue; 
 
				float tx1 = textureCoords[c-32,0]; 
				float ty1 = textureCoords[c-32,1]; 
				float tx2 = textureCoords[c-32,2]; 
				float ty2 = textureCoords[c-32,3]; 
 
				float w = (tx2-tx1) *  textureWidth / textureScale; 
				float h = (ty2-ty1) * textureHeight / textureScale; 
 
				int intColor = color.ToArgb(); 
				if( c != ' ' ) 
				{ 
					fontVertices[iv++] = new CustomVertex.TransformedColoredTextured( new Vector4(xpos+0-0.5f,ypos+h-0.5f,0.9f,1.0f), intColor, tx1, ty2 ); 
					fontVertices[iv++] = new CustomVertex.TransformedColoredTextured( new Vector4(xpos+0-0.5f,ypos+0-0.5f,0.9f,1.0f), intColor, tx1, ty1 ); 
					fontVertices[iv++] = new CustomVertex.TransformedColoredTextured( new Vector4(xpos+w-0.5f,ypos+h-0.5f,0.9f,1.0f), intColor, tx2, ty2 ); 
					fontVertices[iv++] = new CustomVertex.TransformedColoredTextured( new Vector4(xpos+w-0.5f,ypos+0-0.5f,0.9f,1.0f), intColor, tx2, ty1 ); 
					fontVertices[iv++] = new CustomVertex.TransformedColoredTextured( new Vector4(xpos+w-0.5f,ypos+h-0.5f,0.9f,1.0f), intColor, tx2, ty2 ); 
					fontVertices[iv++] = new CustomVertex.TransformedColoredTextured( new Vector4(xpos+0-0.5f,ypos+0-0.5f,0.9f,1.0f), intColor, tx1, ty1 ); 
					dwNumTriangles += 2; 
 
					if( dwNumTriangles*3 > (MaxNumfontVertices-6) ) 
					{ 
						// Set the data for the vertexbuffer 
						vertexBuffer.SetData(fontVertices, 0, LockFlags.Discard); 
						device.DrawPrimitives( PrimitiveType.TriangleList, 0, dwNumTriangles ); 
						dwNumTriangles = 0; 
						iv = 0; 
					} 
				} 
 
				xpos += w - (2 * spacingPerChar); 
			} 
 
			// Set the data for the vertex buffer 
			vertexBuffer.SetData(fontVertices, 0, LockFlags.Discard); 
			if( dwNumTriangles > 0 ) 
				device.DrawPrimitives( PrimitiveType.TriangleList, 0, dwNumTriangles ); 
		} 
		catch 
		{ 
		} 
		finally 
		{ 
 
			// Restore the modified renderstates 
			savedStateBlock.Apply(); 
		} 
    } 
 
 
 
 
    ///  
    /// Draws scaled 2D text.  Note that x and y are in viewport coordinates 
    /// (ranging from -1 to +1).  fXScale and fYScale are the size fraction  
    /// relative to the entire viewport.  For example, a fXScale of 0.25 is 
    /// 1/8th of the screen width.  This allows you to output text at a fixed 
    /// fraction of the viewport, even if the screen or window size changes. 
    ///  
    public void DrawTextScaled( float x, float y, float z, float fXScale, float fYScale, System.Drawing.Color color, 
                                    string text, RenderFlags flags) 
    { 
        if( device == null ) 
            throw new System.ArgumentNullException(); 
 
        // Set up renderstate 
        savedStateBlock.Capture(); 
        drawTextStateBlock.Apply(); 
        device.VertexFormat = CustomVertex.TransformedColoredTextured.Format; 
        device.PixelShader = null; 
        device.SetStreamSource(0, vertexBuffer, 0); 
 
        // Set filter states 
        if( (flags & RenderFlags.Filtered) != 0 ) 
        { 
            samplerState0.MinFilter = TextureFilter.Linear; 
            samplerState0.MagFilter = TextureFilter.Linear; 
        } 
 
        Viewport vp = device.Viewport; 
        float xpos = (x+1.0f)*vp.Width/2; 
        float ypos = (y+1.0f)*vp.Height/2; 
        float sz = z; 
        float rhw = 1.0f; 
        float fLineHeight = ( textureCoords[0,3] - textureCoords[0,1] ) * textureHeight; 
 
        // Adjust for character spacing 
        xpos -= spacingPerChar * (fXScale*vp.Height)/fLineHeight; 
        float fStartX = xpos; 
 
        // Fill vertex buffer 
        int numTriangles = 0; 
        int realColor = color.ToArgb(); 
        int iv = 0; 
 
        foreach (char c in text) 
        { 
            if( c == '\n' ) 
            { 
                xpos  = fStartX; 
                ypos += fYScale*vp.Height; 
            } 
 
            if( (c-32) < 0 || (c-32) >= 128-32 ) 
                continue; 
 
            float tx1 = textureCoords[c-32,0]; 
            float ty1 = textureCoords[c-32,1]; 
            float tx2 = textureCoords[c-32,2]; 
            float ty2 = textureCoords[c-32,3]; 
 
            float w = (tx2-tx1)*textureWidth; 
            float h = (ty2-ty1)*textureHeight; 
 
            w *= (fXScale*vp.Height)/fLineHeight; 
            h *= (fYScale*vp.Height)/fLineHeight; 
 
            if( c != ' ' ) 
            { 
                fontVertices[iv++] = new CustomVertex.TransformedColoredTextured(new Vector4(xpos+0-0.5f,ypos+h-0.5f,sz,rhw), realColor, tx1, ty2 ); 
                fontVertices[iv++] = new CustomVertex.TransformedColoredTextured(new Vector4(xpos+0-0.5f,ypos+0-0.5f,sz,rhw), realColor, tx1, ty1 ); 
                fontVertices[iv++] = new CustomVertex.TransformedColoredTextured(new Vector4(xpos+w-0.5f,ypos+h-0.5f,sz,rhw), realColor, tx2, ty2 ); 
                fontVertices[iv++] = new CustomVertex.TransformedColoredTextured(new Vector4(xpos+w-0.5f,ypos+0-0.5f,sz,rhw), realColor, tx2, ty1 ); 
                fontVertices[iv++] = new CustomVertex.TransformedColoredTextured(new Vector4(xpos+w-0.5f,ypos+h-0.5f,sz,rhw), realColor, tx2, ty2 ); 
                fontVertices[iv++] = new CustomVertex.TransformedColoredTextured(new Vector4(xpos+0-0.5f,ypos+0-0.5f,sz,rhw), realColor, tx1, ty1 ); 
                numTriangles += 2; 
 
                if( numTriangles*3 > (MaxNumfontVertices-6) ) 
                { 
                    // Unlock, render, and relock the vertex buffer 
                    vertexBuffer.SetData(fontVertices, 0, LockFlags.Discard); 
                    device.DrawPrimitives( PrimitiveType.TriangleList , 0, numTriangles ); 
                    numTriangles = 0; 
                    iv = 0; 
                } 
            } 
 
            xpos += w - (2 * spacingPerChar) * (fXScale*vp.Height)/fLineHeight; 
        } 
 
        // Unlock and render the vertex buffer 
        vertexBuffer.SetData(fontVertices, 0, LockFlags.Discard); 
        if( numTriangles > 0 ) 
            device.DrawPrimitives( PrimitiveType.TriangleList , 0, numTriangles ); 
 
        // Restore the modified renderstates 
        savedStateBlock.Apply(); 
    } 
    public void DrawTextScaled( float x, float y, float z, float fXScale, float fYScale, System.Drawing.Color color, 
        string text) 
    { 
        this.DrawTextScaled(x,y,z,fXScale, fYScale, color, text, 0); 
    } 
 
 
 
 
 
    ///  
    /// Renders 3D text 
    ///  
    public void Render3DText( string text, RenderFlags flags ) 
    { 
        if( device == null ) 
            throw new System.ArgumentNullException(); 
 
        // Set up renderstate 
        savedStateBlock.Capture(); 
        drawTextStateBlock.Apply(); 
        device.VertexFormat = CustomVertex.PositionNormalTextured.Format; 
        device.PixelShader = null; 
        device.SetStreamSource(0, vertexBuffer, 0, VertexInformation.GetFormatSize(CustomVertex.PositionNormalTextured.Format) ); 
 
        // Set filter states 
        if( (flags & RenderFlags.Filtered) != 0 ) 
        { 
            samplerState0.MinFilter = TextureFilter.Linear; 
            samplerState0.MagFilter = TextureFilter.Linear; 
        } 
 
        // Position for each text element 
        float x = 0.0f; 
        float y = 0.0f; 
 
        // Center the text block at the origin 
        if( (flags & RenderFlags.Centered) != 0 ) 
        { 
            System.Drawing.SizeF sz = GetTextExtent(text); 
            x = -(((float)sz.Width)/10.0f)/2.0f; 
            y = -(((float)sz.Height)/10.0f)/2.0f; 
        } 
 
        // Turn off culling for two-sided text 
        if( (flags & RenderFlags.TwoSided) != 0 ) 
            renderState.CullMode = Cull.None; 
 
        // Adjust for character spacing 
        x -= spacingPerChar / 10.0f; 
        float fStartX = x; 
 
        // Fill vertex buffer 
        GraphicsStream strm = vertexBuffer.Lock(0, 0, LockFlags.Discard); 
        int numTriangles = 0; 
 
        foreach (char c in text) 
        { 
            if( c == '\n' ) 
            { 
                x = fStartX; 
                y -= (textureCoords[0,3]-textureCoords[0,1])*textureHeight/10.0f; 
            } 
 
            if( (c-32) < 0 || (c-32) >= 128-32 ) 
                continue; 
 
            float tx1 = textureCoords[c-32,0]; 
            float ty1 = textureCoords[c-32,1]; 
            float tx2 = textureCoords[c-32,2]; 
            float ty2 = textureCoords[c-32,3]; 
 
            float w = (tx2-tx1) * textureWidth  / ( 10.0f * textureScale ); 
            float h = (ty2-ty1) * textureHeight / ( 10.0f * textureScale ); 
 
            if( c != ' ' ) 
            { 
                strm.Write( new CustomVertex.PositionNormalTextured( new Vector3(x+0,y+0,0), new Vector3(0,0,-1), tx1, ty2 )); 
                strm.Write( new CustomVertex.PositionNormalTextured( new Vector3(x+0,y+h,0), new Vector3(0,0,-1), tx1, ty1 )); 
                strm.Write( new CustomVertex.PositionNormalTextured( new Vector3(x+w,y+0,0), new Vector3(0,0,-1), tx2, ty2 )); 
                strm.Write( new CustomVertex.PositionNormalTextured( new Vector3(x+w,y+h,0), new Vector3(0,0,-1), tx2, ty1 )); 
                strm.Write( new CustomVertex.PositionNormalTextured( new Vector3(x+w,y+0,0), new Vector3(0,0,-1), tx2, ty2 )); 
                strm.Write( new CustomVertex.PositionNormalTextured( new Vector3(x+0,y+h,0), new Vector3(0,0,-1), tx1, ty1 )); 
                numTriangles += 2; 
 
                if( numTriangles*3 > (MaxNumfontVertices-6) ) 
                { 
                    // Unlock, render, and relock the vertex buffer 
                    vertexBuffer.Unlock(); 
                    device.DrawPrimitives( PrimitiveType.TriangleList , 0, numTriangles ); 
                    strm = vertexBuffer.Lock(0, 0, LockFlags.Discard); 
                    numTriangles = 0; 
                } 
            } 
 
            x += w - (2 * spacingPerChar) / 10.0f; 
        } 
 
        // Unlock and render the vertex buffer 
        vertexBuffer.Unlock(); 
        if( numTriangles > 0 ) 
            device.DrawPrimitives( PrimitiveType.TriangleList , 0, numTriangles ); 
 
        // Restore the modified renderstates 
        savedStateBlock.Apply(); 
    } 
 
 
 
 
    ///  
    /// Get the dimensions of a text string 
    ///  
    private System.Drawing.SizeF GetTextExtent( string text) 
    { 
        if( null == text || text == string.Empty ) 
            throw new System.ArgumentNullException(); 
 
        float fRowWidth  = 0.0f; 
        float fRowHeight = (textureCoords[0,3]-textureCoords[0,1])*textureHeight; 
        float fWidth     = 0.0f; 
        float fHeight    = fRowHeight; 
 
        foreach(char c in text) 
        { 
            if( c == '\n' ) 
            { 
                fRowWidth = 0.0f; 
                fHeight  += fRowHeight; 
            } 
 
            if( (c-32) < 0 || (c-32) >= 128-32 ) 
                continue; 
 
            float tx1 = textureCoords[c-32,0]; 
            float tx2 = textureCoords[c-32,2]; 
 
            fRowWidth += (tx2-tx1)*textureWidth - 2*spacingPerChar; 
 
            if( fRowWidth > fWidth ) 
                fWidth = fRowWidth; 
        } 
 
        return new System.Drawing.SizeF(fWidth, fHeight); 
    } 
 
 
    ///  
    /// Cleanup any resources being used 
    ///  
    public void Dispose(object sender, EventArgs e) 
    { 
        if (systemFont != null) 
            systemFont.Dispose(); 
        systemFont = null; 
 
    } 
}