|
Intro In
this tutorial we're going to get into some hardcore definitions as
we move along.
Well the first thing you need to know:
What's the simplest polygon? A triangle of course! This is why
Direct3D constructs all of its shapes from triangles. A square is 2
triangles of course (draw a diagonal line from corner to corner and
voila). This is how we're going to construct our square in this
tutorial.
2nd thing you need to know -
Direct3D can only render bitmaps that are powers of 2 (for example:
1x1 2x2 4x4 8x8 16x16 32x32..etc). Create a 32x32 bitmap for this
tutorial, or just use one included in the source code :D.
Getting Started
Create a new project. Copy the same GameClass as the previous
project ("Initializing The Device"), and use the same Form1 code as
before.
For this tutorial, we're going to make an adjustment here which
we'll discuss in the next tutorial(dealing with
Matrices/Transformations). Go to your GameClass (in your new
project), and at the very end of the Initialize sub, type this in:
D3Ddev.Transform.View = Matrix.OrthoOffCenterLH(0, DP.Width,
DP.Height, 0, 0, 10)
Again, don't worry about this line in this tutorial, I'll explain
more in the next tutorial.
Hardcore Coding
Create a new class called clsSprite and at the top, Import the 3
DLLs which you usually import:
Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D
Imports Microsoft.DirectX.Direct3D.D3DX
Dim the following global variables in your class.
'Holds a copy of the device to be used in the
class
Private dxDevice As Device
'Holds the image that will be drawn
onto the square
Public SpriteImage As Texture
'This is the buffer in which the
vertices of the square will be stored
Public vBuffer As VertexBuffer
Create a contstructor:
Public Sub New(ByVal lDevice As Device, ByVal
ImageName AsString, ByVal TopLeft As Point, ByVal Width AsInteger,
ByVal Height AsInteger)
'Store a copy of our device in
the class
dxDevice = lDevice
'Load the image, we specify the arguments
that we need to create a square
Load(ImageName, TopLeft, Width, Height)
EndSub
Don't worry about the error, we're going to create a Load sub in a
minute.
First, a quick note: We can create a square given the TopLeft
coordinate. the Width, and the Height. It works like this:
TopLeft = (TopLeft.X, TopLeft.Y)
TopRight = (TopLeft.X + Width, TopLeft.Y)
BottomLeft = (TopLeft.X, TopLeft.Y + Height)
BottomRight = (TopLeft.X + width, TopLeft.Y + Height)
With some simple sketches, you'll realize that this holds true.
Now its time to do some more coding.
Create a sub called Load:
Public Sub Load(ByVal ImageName As String, ByVal topleft As Point,
ByVal width As Integer, ByVal height As Integer)
The next thing we're going to do is talk about Direct3D Vertices.
The thing about these vertices is that they are different from Math
vertices.
A D3D Vertex can hold various proprties. Some of it includes
Position - Just like a math vertex!
Color - .. yes, color, vertices can hold colors
Texture - you can place an image on top of the vertices
Normals - Stuff to do with lighting
In our case, we want our vertices to hold the Position and the
Texture.
So in the Load sub,
'Declares an array of vertices that will make
up the square.
Dim Vertices() As
CustomVertex.PositionTextured
Now we have to set the texture:
'SpriteImage will hold our .. Image :D.
SpriteImage = TextureLoader.FromFile(dxDevice, ImageName)
Primative Types
Now for a quick note. We know that a square is composed of 2
Triangles. That means that we'd need 6 vertices (3 per triangle) to
make up a square! Nope :o, thank goodness Direct3D has a way around
it. Later in our code, we're going to set the PrimativeType (Primitiave
means Triangle). The primitave type specifies how our shape will be
created. The 2 most common PrimativeTypes are:
PrimativeType.TriangleList - Using this method, we'd have to create
6 vertices for a square
PrimativeType.TriangleStrip - Using this method, we only need 4
triangles per square.
Take a look at these links from MSDN, they have pictures with
explanations to help you understand what this is all about.
TriangleList - A triangle list is a list of isolated triangles.
They may or may not not be near each other. A triangle list must
have at least three vertices. The total number of vertices must be
divisible by three.
TriangleStrip - A triangle strip is a series of connected
triangles. Because the triangles are connected, the application does
not need to repeatedly specify all three vertices for each triangle.
(Click the links to look at some diagrams on how Direct3D connects
them together)
So when we create our square, it is better to use a TriangleStrip.
This way, There are 3 vertices (TopLeft, TopRight, BottomLeft) for
the triangle - and D3D senses that 4th vertex (BottomRight) is
"alone" and connects that vertex with the TopRight and BottomLeft.
Thus, I opted to use a TriangleStrip to save memory(by using 2 less
vertices of course!)
-----
More Coding
The next thing to do (we're still in the Load sub here just in case
you're lost) is instantiate our VertexBuffer:
vBuffer = New
VertexBuffer(GetType(CustomVertex.PositionTextured), 4, dxDevice,
Usage.WriteOnly, CustomVertex.PositionTextured.Format, Pool.Default)
'GetType(CustomVertex.PositionTextured)
- This VertesBuffer will hold CustomVertex.PositionTextured
vertices
'4 - It'll hold 4 vertices (4 vertices make up a square
right)
'dxdevice - It'll use this device
'Usage.WriteOnly - We're only going to write to the buffer,
not read from it
'CustomVertex.PositionTextured.Format - DirectX is retarded
and wants the argument again? Can't think of an explanation for
this.
'Pool.Default - Specifies how we want to handle the memory
pool. Woah we're not going to mess with memory...so we'll let
Direct3D choose how it wants to handle the memory! Thus, specifying
Default.
Before setting the vertex positions and such, we need to lock the
vertex buffer. If we don't lock the buffer - it can lead to memory
problems.
'Lock the buffer before use.
'The reason for the equal sign is basically, you're telling the
Vertices
'to store/accept its data into the buffer... weird syntax huh?
Vertices = vBuffer.Lock(0, LockFlags.None)
'0 - The offset. Lock the entire
buffer. Any number greater than 0, you're only going to lock that
offset in bytes
'LockFlags.None - Just lock it.. whatever, don't specify any more
options as this is a simple program.
Here's an explanation for the Offset:
Imagine a vertex buffer as a row of data going from left to right
<0123456789>
The offset specifies how much you want to lock it. For example,
since we specified 0, we'd to lock the entire buffer. If we
specified 6, we'd lock:
<689>.
So we specify 0 :p.
Creating the vertices
The next thing we're going to discuss is Tu and Tv. They essentially
specify what corner of the texture goes where. Tu and Tv are written
like coordinates.. for example (tu,tv).
Here's a quick example of how tu and tv work:
A tu,tv of (0,0) would be the topleft of the square.
A tu,tv of (1,1) would be the bottomright of the square.
You'll understand more as we go along.
Now that we've locked it, we're going to tell the vertices where
they are!
'Set the vertices. The arguments are written
like (X,Y,Z,tU,tV)
'We set its Z to 1 so that it appears "Forwards" a bit, which is
what we want. Setting a Z to 0 would make it too close to the
screen. Setting a Z of like.. 8 would make it too far
'View the tutorial for more information on how all this works
'TopLeft
Vertices(0) = New CustomVertex.PositionTextured(topleft.X, topleft.Y,
1, 0, 0)
'TopRight
Vertices(1) = New CustomVertex.PositionTextured(topleft.X + width,
topleft.Y, 1, 1, 0)
'BottomLeft
Vertices(2) = New CustomVertex.PositionTextured(topleft.X, topleft.Y
+ height, 1, 0, 1)
'BottomRight
Vertices(3) = New CustomVertex.PositionTextured(topleft.X + width,
topleft.Y + height, 1, 1, 1)
Wait! Don't panic yet! I'll explain, don't worry :D.
OK, you know the stuff I told you
near the beginning at the tutorial about how the TopLeft position,
the width and the height could form a square? Well look at the X and
the Y arguments (the first 2 arguments).
I told you that (TopLeftX + Width, TopLeft.Y + height) would give
you the BottomRight vertex right? Well that's exactly what its
doing!
'BottomRight
Vertices(3) = New CustomVertex.PositionTextured(topleft.X +
width, topleft.Y + height, 1, 1, 1)
Now the Z argument, a Z of 0 is not recommended. Here's why,
the more the Z is, the farther away from us it is (the
"Deeper it is into the monitor" in other words :) ). When Z is 0 its
right at the monitor. Actually, its too close to the monitor
which is why we can't see anything. A Z value of 1 is
typical. When Z = 1, it's neither too far, nor too close to the
monitor. This is what GDI+ and everything else used for a Z value
(except it hides it from us to avoid confusion). Imagine setting a Z
of 2, that means our object will move farther back "deeper into the
monitor". So we set a typical Z of 1.
Well now that you see that, do you see how the Tu and the Tv work?
Let's recap:
I told you that a tu,tv of (0,0) would be topleft of the square:
'TopLeft
Vertices(0) = New CustomVertex.PositionTextured(topleft.X, topleft.Y,
1, 0, 0)
Well what do you know! It is the top. The same works
for the other ones as well:
(0,0) = TopLeft
(0,1) = TopRight
(1,0) = BottomLeft
(1,1) = BottomRight
And now the last line in the load sub:
'Unlock the buffer - we're ready to render
this thing ;D
vBuffer.Unlock()
Now its time to create a Render sub:
Public Sub Render()
'Sigh, set the vertex format AGAIN :).
dxDevice.VertexFormat = CustomVertex.PositionTextured.Format
'We're going to draw whatever is in
the buffer.
dxDevice.SetStreamSource(0, vBuffer, 0)
'0 - StreamNumber. Saying 0 draws all
the [memory] streams (meaning vertices) in the buffer
'vBuffer - Draws the content of our VertexBuffer
'0 - Offset. Draws the entire content of the buffer. Offset is
explained above when Locking the buffer.
'Draw the texture over the square we've just drawn.
dxDevice.SetTexture(0, SpriteImage)
'0 - Refers to the
TextureStage(?) No idea what this does, When in doubt, put 0!
'SpriteImage - The image we draw on our quad
'Draw our shape
dxDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2)
'PrimitiveType.TriangleStrip
- We draw using a TriangleStrip as opposed to a TriangleList
'0 - Our startvertex is 0 (remember the Vertices() array, the first
vertex is Vertices(0))
'2 - The PrimitiveCount.. meaning 2 Triangles("Primitives") :).
End Sub
Heh, I hope you've understood that - it's all in the comments ;D.
Basically you:
-Tell the device what type of vertices you'll be drawing
-Set the vertices (they're "invisible")
-Set the texture over the vertices
-Draw the textured box.
It's a weird way of drawing, I must admit.
Now go back to GameClass, in the globals,
Dim Alex As clsSprite
At the end of the Initialize sub:
Alex = New clsSprite(D3Ddev, "down1.bmp", New
Point(0, 0), 32, 32)
In between BeginScene and EndScene:
Alex.Render()
Run it! :). You should see the sprite of your choice rendering.
Err.. not really. The guy is all black... you have to turn off
lighting (do this at the end of the Initialize sub):
'Our object is so close to the screen that the
lighting conflicts with its visibility, making it have a "Sillhouette"
effect :).
D3Ddev.RenderState.Lighting = False
Well that's it for this tutorial! Click the link below for
the source, I guarantee you you'll see more green than black or blue
in this source :).
The Source
Code for this tutorial is located
here:
You can also
locate this by logging in to vbProgramming Forums and going
to: Tutorials > Tutorial Source Code >
Source Code |