Drawing
Text - Custom Rasterizers and normal 2D Text
Author
:
Jack Hoxley
Written : 20th December 2000
Contact : [Web]
[Email]
Download : Graph_06.Zip
[21 Kb]
Contents
of this lesson:
1. Introduction
2. Normal 2D Text
3. Custom 2D Font rasterizers
1.
Introduction
Welcome
back to another lesson on DirectX Graphics; This lesson isn't a very complicated
one, but you'll probably need to learn how to draw text onto the screen sooner
or later - so we're going to cover it before we get onto some of the more advanced
topics. Drawing text is drawing text and doesn't really need much explanation
or theory work - so we may as well just get straight on with it.
2.
Normal 2D Text
This
is the easiest to use, and the one you'll probably use the most. Almost from
square one all my projects have a little frame rate indicator in the corner
- I like to keep track of the speed as I add more and more features (usually
starts around about 1000fps and ends up at about 50fps). In DirectX7 (which
is where I do most of my work) I just draw the text to the top corner of the
backbuffer (or wherever I want it); we'll now have a look at how this has changed
in DirectX8.
The
good thing is that, In my opinion, it has changed for the better. You can now
allow DirectX to do some fairly simple text manipulations (like centering) that
in DirectX7 you would have to of done manually. It is slightly more complicated
than the text interfaces in DirectX7, but it's still pretty simple:
'//Some new variables that are needed Dim
MainFont
As
D3DXFont '//This will be *created* in a minute
Dim
MainFontDesc
As
IFont '//We use this temporarily to setup the font
Dim
TextRect
As
RECT '//This defines where it will be
Dim
fnt As
New
StdFont '//This is also used to describe and setup the font
Dim
TextToDraw
As
String '//This is the text that will be displayed... |
|
|
|
Not
too difficult really; a few new variables. Bare in mind that the "fnt"
object is late bound - if you intend to keep switching the font around an altering
it you should switch this to being early bound. also, the IFont variable may
well not appear in the Intellisense list - but if you just type it then you
should be able to get it working...
'## SETUP TEXT RENDERING ##
fnt.Name = "Verdana"
fnt.Size = 18
fnt.Bold = True
Set MainFontDesc = fnt
Set MainFont = D3DX.CreateFont(D3DDevice, MainFontDesc.hFont)
|
|
|
|
A
little complicated and a bit odd, but thats how to do it - first we fill out
our "fnt" object with the relevent details and the get VB to convert/typecast
it into our IFont variable; which in turn is then used to create the final font.
TextRect.Top
= 1
TextRect.Left
= 1
TextRect.bottom
= 32
TextRect.Right
= 640
D3DX.DrawText
MainFont,
&HFFCCCCFF,
"Current
Frame
Rate:
"
&
FPS_Current,
TextRect,
DT_TOP
Or
DT_CENTER |
|
|
|
This
final part goes between the "D3DDevice.BeginScene" and "D3DDevice.EndScene"
calls in the render procedure. It's not greatly complicated but there's a little
more to think about than usual....
Firstly
there's the rectangle that you must specify - whilst it doesn't matter if you
get the wrong size it will look a little silly if it's too small (you'll lose
parts of your text) and if it's too big you'll be inefficient.
Secondly
there are the flags to be used; these can be any combination of the following
(Although some cancel each other out):
DT_LEFT
- The
text
appears
along
the
left
of the
RECT
(Left
Justify) DT_TOP
- The
text
appears
across
the
top of
the
RECT DT_CENTER
- The
text
is
centered
horizontally
in the
RECT
(Center
Justify) DT_RIGHT
- The
text
is
along
the
right
edge
of the
RECT
(Right
Justify) DT_VCENTER
- The
text
is
centered
vertically DT_BOTTOM
- The
text
appears
along
the
bottom
edge DT_SINGLELINE
- The
string
passed
is a
single
line -
line
feeds
and
carriage
returns
are
-
ignored |
|
|
|
There
isn't really much more too it than that; there are a few other flags to be used,
but the above should be fine - if not, you can look up the others in the object
browser...
3.
Custom 2D Font Rasterizers
These
are slightly more complicated than the above method, but if done properly they
can look much much nicer. As you're probably aware, the above method uses a
font registered with the system - the same ones used in your graphics package
and office suite. But if you want something a little more interesting - a font
with a shadow/3D effect to it, or a font that has a texture...
This
is where custom fonts come in, you're font can be whatever you can represent
using a single texture - be that a weird alien language, or a normal font coloured
to suit the theme of your game. The only really difficult part is the drawing
of your text; but if you're a half decent artist or know a half-decent artist
this shouldn't be too much of a challenge..
The
basics of a custom font rasterizer is to take a string, work out which parts
of the texture you need to use, then generate the relevent geometry. Think of
the texture as a palette - where each n*n segment holds one letter - you then
need to work out which segment represents each letter in the string. Below is
an example of what the texture looks like for our custom font.
The
bright green background will be our transparent colour later on
Note
that each character is 16x16 pixels in size; from a 256x128 texture this allows
us 128 characters. A 256x256 texture would allow us 256 characters - the same
number there are in the ascii character set (0 to 255).
To
work out what letter we need we'll just use VB's built in functions "Mid$(
) " and "Asc( )" to read through a string that's passed to a
function. The code looks like this:
'//This must be called during the .BeginScene and .EndScene lines...
Private Sub RenderStringFromCustomFont_2D(strText As String, startX As Single, StartY As Single, Height As Integer, Width As Integer)
Dim I As Integer '//Loop variable
Dim CharX As Integer, CharY As Integer '//Grid coordinates for our character 0-15 and 0-7
Dim Char As String '//The current Character in the string
Dim LinearEntry As Integer 'Without going into 2D entries, just work it out if it were a line
If Len(strText) = 0 Then Exit Sub '//If there is no text dont try to render it....
For I = 1 To Len(strText) '//Loop through each character
'//1. Choose the Texture Coordinates
'To do this we just need to isolate which entry in the texture we
'need to use - the Vertex creation code sorts out the ACTUAL texture coordinates
Char = Mid$(strText, I, 1) '//Get the current character
If Asc(Char) >= 65 And Asc(Char) <= 90 Then
'A character number from 65 through to 90 are the upper case A-Z letters
'which if we wrap around our texture are entries 0 - 25.
LinearEntry = Asc(Char) - 65 '//Make it so character 65 references entry 0 in our texture
ElseIf Asc(Char) >= 97 And Asc(Char) <= 122 Then
'We have a lower case letter.
LinearEntry = Asc(Char) - 71 '//Make it so that the lower case letters reference values 26-51
ElseIf Asc(Char) >= 48 And Asc(Char) <= 57 Then
'We have a numerical character, which occupy entries 52-62 in our texture
LinearEntry = Asc(Char) + 4
'//Finally: Special Cases. I couldn't be bothered to work out a formula
' for full-stop/spaces/punctuation characters, so I'm going to hardcode them
ElseIf Char = " " Then
'Space
LinearEntry = 63
ElseIf Char = "." Then
'Full stop
LinearEntry = 62
ElseIf Char = ";" Then
'Semi colon
LinearEntry = 66
ElseIf Char = "/" Then
'Forward slash
LinearEntry = 64
ElseIf Char = "," Then
'Guess what; its a comma..
LinearEntry = 65
End If
'We now need to process the actual coordinates.
If LinearEntry <= 15 Then
CharY = 0
CharX = LinearEntry
End If
If LinearEntry >= 16 And LinearEntry <= 31 Then
CharY = 1
CharX = LinearEntry - 16
End If
If LinearEntry >= 32 And LinearEntry <= 47 Then
CharY = 2
CharX = LinearEntry - 32
End If
If LinearEntry >= 48 And LinearEntry <= 63 Then
CharY = 3
CharX = LinearEntry - 48
End If
If LinearEntry >= 64 And LinearEntry <= 79 Then
CharY = 4
CharX = LinearEntry - 64
End If
'Fill in the rest if you really need them...
'//2. Generate the Vertices
vertChar(0) = CreateTLVertex(startX + (Width * I), StartY, 0, 1, &HFFFFFF, 0, (1 / 16) * CharX, (1 / 8) * CharY)
vertChar(1) = CreateTLVertex(startX + (Width * I) + Width, StartY, 0, 1, &HFFFFFF, 0, ((1 / 16) * CharX) + (1 / 16), (1 / 8) * CharY)
vertChar(2) = CreateTLVertex(startX + (Width * I), StartY + Height, 0, 1, &HFFFFFF, 0, (1 / 16) * CharX, ((1 / 8) * CharY) + (1 / 8))
vertChar(3) = CreateTLVertex(startX + (Width * I) + Width, StartY + Height, 0, 1, &HFFFFFF, 0, ((1 / 16) * CharX) + (1 / 16), ((1 / 8) * CharY) + (1 / 8))
'//3. Render the vertices
D3DDevice.SetTexture 0, fntTex '//Set the device to use our custom font as a texture
D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLESTRIP, 2, vertChar(0), Len(vertChar(0))
Next I
End Sub
|
|
|
|
The
above is fairly self explanatory, but to give you a general idea how it works:
1. We recieve our string
2. We go through our string character by character rendering it
3. Whilst each character has a 2D coordinate in our texture, it makes it easier
if we calculate it in 1D first - the LinearEntry value
4. We then convert this linear entry into a 2D coordinate for our texture.
5. Now we generate the vertices - note that we reuse the same 4 vertices every
time. We also convert the characters X/Y coordinate into a valid texture coordinate
6. Finally we render it to the screen. Nothing greatly new here. NB: we must
only call this function during a Device.BeginScene and Device.EndScene block.
There
are two rather large limitations to this method:
1. Characters - you're limited to the number of characters the you draw. This
example only uses about 70 or so.
2. Any alignment must be done manually by you - you cant just jam "DT_CENTER"
into it and hope it appears correctly....
Other
than that there's not really much more to learn about drawing text. It's unlikely
you'll really want to use anything more than this - and if you do it'll probably
be based on, or similiar to the two methods already used.
You
can download the source code to this lesson from the top of this page;
Once
you've got this extremely long and complicated [hehe] lesson under your belt
you can move onto the next lesson: An introduction
to lighting. (Which is complicated)
|