January 8, 2006

Managed DirectX: Rendering Doom3/Quake4 Fonts

As stated in an earlier posting, I'm working on a Doom3 modification. The catch is, I'm only using the Doom3 content, not the engine.

So, if you want to render Doom3 fonts using Managed DirectX, here's the code. (ResourceToStream is a helper function that returns a MemoryStream from the byte array passed to it.

Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D

Public Class idFont

Private _font As Texture
Private _device As Device
Private _sprite As Sprite
Private Structure fontchar
Dim height As Integer ' number of scan lines
Dim top As Integer ' top of glyph in buffer
Dim bottom As Integer ' bottom of glyph in buffer
Dim pitch As Integer ' width for copying
Dim xSkip As Integer ' x adjustment
Dim imageWidth As Integer ' width of actual image
Dim imageHeight As Integer ' height of actual image
Dim s As Single ' x offset in image where glyph starts
Dim t As Single ' y offset in image where glyph starts
Dim s2, t2 As Single
End Structure
Private xRes, yRes As Integer
Private height As Integer = 0

Private _chars(255) As fontchar

Public Sub New(ByVal fontdata() As Byte, _
ByVal fontlayoutdata() As Byte, _
ByVal device As Device)

_font = TextureLoader.FromStream(device, ResourceToStream(fontdata))
xRes = _font.GetSurfaceLevel(0).Description.Width
yRes = _font.GetSurfaceLevel(0).Description.Height
Dim s As New IO.BinaryReader(ResourceToStream(fontlayoutdata))
For x As Integer = 0 To 255
With _chars(x)
.height = s.ReadInt32
If .height > height Then height = .height
.top = s.ReadInt32
.bottom = s.ReadInt32
.pitch = s.ReadInt32
.xSkip = s.ReadInt32
.imageWidth = s.ReadInt32
.imageHeight = s.ReadInt32
.s = s.ReadSingle
.t = s.ReadSingle
.s2 = s.ReadSingle
.t2 = s.ReadSingle
s.ReadInt32() : s.ReadBytes(32) ' Dispose unneeded extra stuff
End With
Next
s.Close()

_sprite = New Sprite(device)
End Sub

Public Function DrawText(ByVal text As String, _
ByVal x As Integer, _
ByVal y As Integer, _
ByVal color As Color) As Integer

If text Is Nothing Then Return y
If text.Length = 0 Then Return y

Dim c As Integer, ch As fontchar
_sprite.Begin(SpriteFlags.AlphaBlend)

For z As Integer = 0 To text.Length - 1
c = Asc(text.Chars(z))

If c >= 0 And c <= 255 Then
ch = _chars(c)
' Valid char, let's go
_sprite.Draw2D(_font, _
New Rectangle(ch.s * xRes, ch.t * yRes, ch.imageWidth, ch.imageHeight), _
New Size(ch.imageWidth, ch.imageHeight), _
New PointF(x, y + height - ch.top), _
color)
x += ch.xSkip
End If
Next

_sprite.End()

Return x

End Function

End Class
The reason for returning the x variable is so you can mix fonts on a single line. It returns the horizontal position where it stopped rendering.

No comments: