Managed DirectX 9 (Part 3) - Matrices and Transformations

ThePentiumGuy

Senior Contributor
Joined
May 21, 2003
Location
Boston, Massachusetts
I will be making a set of 5 or 6 tutorials, each of which include source for Visual Basic.NET 2002 and Visual Basic.NET 2003. C# tutorials may or may not follow. These tutorials are to give you guys a jumpstart to some Direct3D. I hope you enjoy these.

This tutorial will teach you how to move stuff around using Matrices and Transformations.

Hey welcome to the next tutorial.. this is the tutorial that will start to confuse the hell out of you.. just know 1 thing: As long as you keep an open mind and a positive attitude youll understand this ;p.

Heh, lets start with a few Q and A to get the ball rolling

The obvious question:

"How do you move stuff??"

The n00b responses (which I expect from some of you at this stage):

"Well, the first thing to do is recreate the vertices - its kinda simple, if you want to move a square right, all you gotta do is this:
vertex(0) = new customvertex.positionTextured(<1 unit to the right>)
and do the same with the other vertices"

Sorry, wrong answer... http://www.dotnet.community/x_images/images/smilies/frown.gif Direct3D doesnt allow this... why? Because youre changing data in the vertex buffer. The vertex buffer is loaded and ready to go (there is a way around this but this is not for now), and you cant change it!

n00b response to that:

"Well all you gotta do is recreate the vertex buffer and load in a new set of vertices"

Well... yes that could work... but isnt that a TOTAL waste of memory? Youre creating a TOTALLY new vertex buffer and youre filling it with ANOTHER set of vertices = lag fest & flicker mania http://www.dotnet.community/images/smilies/biggrin.gif. So .... youre wrong ;p.

A g33k comes up with a response:

"Well, its rather simple actually, Direct3D has its own built-in functions for doing this, it involves a complex system of matrices. The world gets translated in the direction which you want your objects to move and the result is a mass-movement of all objects in that specified direction."

Uhh... yeah thats the correct answer..... In english please??, here it is:

-Every object is in the WORLD.
These objects cant move on their own (by changing the vertices or recreating the vertex buffer..etc) because it would be too slow.... imagine moving a 3D Character this way...do you realize how many vertices wed have to move in order to move him? Even if this was just 1 pixel to the right.. its a CRAZY way of doing this

So, the ingenious Microsoft came up with a way of doing this:

Move the world to move an object.

;P, scary huh? Again, keep an optimistic outlook and an open mind towards this concept, it might seem weird but everything has its reasons.

If an object wants to move left... what do you do? Make the world move left as well :p. The same goes for the other directions.. which ever direction the object wants to go in, the world goes in.

So to move that high polygon 3D character model, you move the world rather than move the individual vertices over... you sort of understand what I mean?

How do we move the world? Well... heres where it gets a bit confusing,
Microsoft stores the world in a Matrix which is stored in your Device. The process of moving the world is called Translating... there are other transformations as well, such as Rotation and Scaling, but Translating is just moving it...

Well heres how to translate the world 1 unit to the right.. assuming d3ddev is your device:

d3ddev.Transform.World = Matrix.Translation(1,0,0).
The 3 arguments are X, Y, and Z respecitively. That 1 indicates that the world is being moved 1 unit to the right.

Now that our character has moved 1 unit to the right, how do we move him back to his original position?

d3ddev.Transform.World = Matrix.Translation(0,0,0)

Lets try experimenting with this http://www.dotnet.community/x_images/images/smilies/smile.gif.


Open up the previous project (Rendering a Sprite)... Go to GameClass and then go to the RenderScene sub..

Right before Alex.Render, type in

d3ddev.transform.World = Matrix.Translation(30,0,0)
Run it...

edit: before you run it, do 1 thing, it is an error on my part in the previous tutorial, in the Initialize in GameClass, please change


D3Ddev.Transform.View = Matrix.OrthoOffCenterLH(0, DP.Width, DP.Height, 0, 0, 10)

to

D3Ddev.Transform.View = Matrix.OrthoOffCenterLH(0, DP.Width, DP.Height, 0, 1, 10)

Due to some mathematical reason, the zNearPlane argument must be 1 (dont worry if you dont understand this line anyways, i will explain later in the tutorial


The character just shifted 30 units to the right. Be sure to realize one thing, remember that the top left of the character is position (0,0,0), which is the top left of the screen?

Well now, the point (0,0,0) has now shifted 30 units to the right, it is now where 30,0,0 used to be.. the characters top left is still at point 0,0,0... the top left of the screen is now -30,0,0 http://www.dotnet.community/images/smilies/biggrin.gif... starting to understand a bit?

Now lets try something different

How do we translate the world 1 unit to the right each frame[each time the object is rendered]?

Well, first of all, we would have d3ddev.transform.world = matrix.translation(1,0,0) right? How would we make it move 1 unit to the right of its previous position, meaning how do we make it keep moving right 1 unit??

Wed simply do this [replace this line with the translation(30,0,0) line:

d3ddev.transform.world = Matrix.Multiply(d3ddev.transform.world, matrix.translation(1,0,0))

Dont worry, the line may seem tricky, but just replace that line with your other translation line and run it... So now the guy moves REALLY quickly across the screen.. for those of you with fast computers,
I suggest that you change the 1 into a 0.1 or a 0.01. Depending on the amount of FPS (Frames Per Second) you get, the faster hell go ....

so Multiply simply translates the world from the PREVIOUS position.. and without Multiply, it would translate the world from position (0,0,0)... got all that? If you dont - play around with it for a while and it might sink in http://www.dotnet.community/images/smilies/biggrin.gif.

-------------------------------------------------------------

Now that youve understood that (I dont expect you to understand it completely, but get the general gist of what it does), lets discuss a bit on how to animate.

The first response that people come up with is usually

"Simple, use the Multiply along with Translation to animate the character - except use variables.. for example:

d3ddev.transform.world = Matrix.Multiply(d3ddev.transform.world, matrix.Translation(x,y,0)) since Z doesnt matter in a 2d game set it to 0. When the character presses (for example) right, set x = 1 so he can start animating, when there is no key pressed, set x = 0 so he can STOP animating"

- Ill tell you right now that this is the wrong way, but.. I like to teach people Why we do things as opposed to simply How we do things... lets experiment:

In GameClass:
Public X as Single
Public Y as Single

replace that translation line [you should only have 1 translation line] with:

d3ddev.transform.world = Matrix.Multiply(d3ddev.transform.world, matrix.Translation(x,y,0))

Now go back to form1.. add the following code

Code:
[color=#0000ff]If[/color] e.KeyCode = Keys.Right [color=#0000ff]Then
 
[/color]Game.X = 0.1
 
[color=#0000ff]End[/color][color=#0000ff]If
 
[/color][color=#0000ff]If[/color] e.KeyCode = Keys.Left [color=#0000ff]Then
 
[/color]Game.X = -0.1
 
[color=#0000ff]End[/color][color=#0000ff]If
 
[/color][color=#0000ff]If[/color] e.KeyCode = Keys.Up [color=#0000ff]Then
 
[/color]Game.Y = -0.1
 
[color=#0000ff]End[/color][color=#0000ff]If
 
[/color][color=#0000ff]If[/color] e.KeyCode = Keys.Down [color=#0000ff]Then
 
[/color]Game.Y = 0.1
 
[color=#0000ff]End[/color][color=#0000ff]If[/color]
[color=#0000ff]
 
[/color][color=#0000ff]


[/color]And in form1_keyup:
game.x = 0
game.y = 0

Now test out the code ;D...

You see that it works right? Heres my reasoning behind why we shouldnt do this... the Matrix.Multiply thing is based on time (FPS) becuase it moves the world based on frame count..
you move it .1 in whatever direction, lets say the Frames Per Second is 1, that means that it moves the world by .1 (in whatever direction) every second. Based on the FPS only youll know how much youre actually moving the world by... this leads to confusion..

So theres another technique which does not use multiply and translation, it just uses translation < i bet you guys now know what it is ;) >

<Im Splitting post to overcome limit>
 
Last edited by a moderator:

ThePentiumGuy

Senior Contributor
Joined
May 21, 2003
Location
Boston, Massachusetts

---------


The other technique is really simple - We simply use Translation and use variables.

It works something like this [ replace the Multiply line with this ]
d3ddev.transform.world = matrix.Translation(x,y,0)

Now, the translation is not based on FRAMERATE, its simply based on the amount that X and Y is.

Go back to form 1 and revise the code in form1_keydown:

Code:
[/color]
 
[color=blue]If[/color][color=black] e.KeyCode = Keys.Right [/color][color=blue]Then[/color]
 
[color=black]Game.X += 1[/color]
 
[color=blue]End[/color][color=blue]If[/color]
 
[color=blue]If[/color][color=black] e.KeyCode = Keys.Left [/color][color=blue]Then[/color]
 
[color=black]Game.X -= 1[/color]
 
[color=blue]End[/color][color=blue]If[/color]
 
[color=blue]If[/color][color=black] e.KeyCode = Keys.Up [/color][color=blue]Then[/color]
 
[color=black]Game.Y -= 1[/color]
 
[color=blue]End[/color][color=blue]If[/color]
 
[color=blue]If[/color][color=black] e.KeyCode = Keys.Down [/color][color=blue]Then[/color]
 
[color=black]Game.Y += 1[/color]
 
[color=blue]End[/color][color=blue]If[/color]
 
[color=black]

Its pretty simple, since its not based on framerate, we can use "normal sized" amounts as opposed to using .01 or .1.

Oh yeah, delete the code in form1_keyup http://www.dotnet.community/images/smilies/wink.gif.

Now run the thing http://www.dotnet.community/images/smilies/biggrin.gif, it works. As a matter of fact, you might actually have to increase the 1 to maybe 5 or 10.

Well, here comes another question which people will ask/
Remember, you have to move the world to move an object...

"what if you had Two objects??? What if object1 wanted to go right and object2 wanted to go left? Oh no! is this a limitation of Direct3D????"

Of course its not ;P.

This is a confusing explanation, and I gotta explain this through experimentation lol.

In GameClass,
Private Alex2 As clsSprite

at the end of Initialize,
Alex2 = new clsSprite(d3ddev, "down1.bmp", new point(5,5), 32,32)

and in RenderScene after Alex.Render:
Alex2.Render

Run it,
Again - as expected, you should see both alex and alex2 moving beucase youre moving the WORLD...

Well,
how do you move 2 objects at once? You translate the world again before rendering the 2nd object!

In GameClass,
Dim X2 as Single
Dim Y2 As Single

in form1_keydown, add:

Code:
[color=#0000ff]If[/color] e.KeyCode = Keys.W [color=#0000ff]Then
[/color]Game.y2 -= 1
 
[color=#0000ff]End[/color] [color=#0000ff]If
 
[/color][color=#0000ff]If[/color] e.KeyCode = Keys.S [color=#0000ff]Then
 
[/color]Game.y2 += 1
 
[color=#0000ff]End[/color] [color=#0000ff]If
 
[/color][color=#0000ff]If[/color] e.KeyCode = Keys.A [color=#0000ff]Then
 
[/color]Game.x2 -= 1
 
[color=#0000ff]End[/color] [color=#0000ff]If
 
[/color][color=#0000ff]If[/color] e.KeyCode = Keys.D [color=#0000ff]Then
 
[/color]Game.x2 += 1
 
[color=#0000ff]End[/color] [color=#0000ff]If
 
[/color]

Ok, now before alex2.Render:
d3ddev.Transform.World = Matrix.Translation(x2,y2,0)

Run it (dont worry if you dont understand the code), push W S A D and UP DOWN LEFT RIGHT to move the guys... they move SEPERATELY :).

Now its time for an explanation...
Heres how the program "reads" at the code

Translates the world to Alexs position
Renders Alex

Translates the world to Alex2s position
Renders Alex2

Its a weird concept to sink in :s, its really hard to explain.. but heres 1 other thing you should realize,

After you render alex, any more translating after that wont really do anything to Alexs position... hes already been rendered :), and he "stays" there.

Experiment a bit with this, and try to understand it before moving on...

------------------------------------------------------------------------
The View Matrix

Here is a question which I said I would answer in the previous tutorial (Rendering a Sprite).

I asked you earlier to type in the line:
D3Ddev.Transform.View = Matrix.OrthoOffCenterLH(0, DP.Width, DP.Height, 0, 1, 10)

near the end of GameClass, and I told you I would explain it in the next tutorial.

The World matrix stores the objects, and the View matrix stores what you can see.

In this view matrix, we define the left of the screen to have an x of 0, we define the right of the screen to be DP.Width (ex: if it was 800x600, the right of the screen would be 800), we define the bottom of the screen to be DP.Height (600 in our case), we define the Top of the screen to have a y of 0.

The next 2 arguments tell us zNear plane and zFar plane. Heres what those things do (its not important in 2d but it certainly is important in 3d):

-It basically says "If any object has a Z which is less than 1, dont render it, If any object has a Z greater than 0, dont render it". Its sort of a memory manager so that you dont render your ENTIRE world (imagine a HUGE 3d world), at the same time - you render it as you approach there.

If we HADNT speficied the View matrix, it would be by default:
Left: -1
Right: 1
Top: -1
Bottom: 1
ZNearPlane: 1
ZFarPlane: <im not too sure, I think it may be 2>

This means that point (0,0,0) is the CENTER of the screen, we certainly dont want that to happen.

Now tto explain what OrthoOffCenterLH means...

Ortho: No matter what Z value an object has, its not gonna matter - ideal for 2d, bad for 3d... the Z value of an object only does 1 thing: determines what object renders on top of the other object. If and object had a z value of 1 and an other object had a z value of 5, then the object with a zvalue of 1 would be rendered on top.

OffCenter: This means that the center of the screen isnt 0,0,0

LH: Left handed coordinate system, this means that the more Z an object has, the farther away it is.

The reason why I said replace the 0 with a 1 < in my previous post > was for mathematical reasons. Never have a zNear of 0.

Thats all for this tutorial :). Mess around with some stuff, and learn more - It took me 2 weeks to understand this concept and I learned it by messing around.

-The Pentium Guy
 

Attachments

  • 4 - Matrices and Transformations.zip
    20.7 KB · Views: 66
Last edited by a moderator:
Top