12,405,303 members (67,188 online)
Add your own
alternative version

65K views
319 downloads
39 bookmarked
Posted

# Managed DirectX Tutorials: Part 4 - The Transformation Pipeline

, 24 May 2006 CPOL
 Rate this:
Please Sign up or sign in to vote.
Set up a robust framework for creating manipulatable 3D objects

## The Transformation Pipeline

In our last tutorial, we created a triangle by specifying in screen coordinates where we wanted to put it.
This method, whilst simple is obviously not very useful for creating rich, moving 3D environments.
Instead, most DirectX applications use Transformed coordinates. This means that the coordinates of a model are manipulated from their original position before being rendered, so take this basic example for a man running:

`Man.Position Y += Speeds.Running * Time_Elapsed;`

This basic assignment multiplies the running speed by the amount of time elapsed since the last update.

## Theory

Whilst useful, the transformation pipeline can often be hard to understand.
When you create a model (either a triangle like in the last tutorial, or a complex 3D model) in transformed coordinates, everything is defined relative to the model itself. Take this example:

This shows a triangle with its coordinates defined relatively. The center point is defined as coordinate (0,0), and the three vertices being based off that.
The huge advantage of this system is that these coordinates can easily be scaled, translated and rotated across a screen.
The transformation pipeline transforms these relative coordinates into world space coordinates, i.e., how the primitive will be positioned in the full world, and finally view space coordinates, i.e., how the primitive will look on a screen.
Take the example of a crate in a game. This crate’s coordinates are relative to itself, like our triangle.

Now, someone places this box in a game world. The box is scaled by 3 to make it bigger, and to be moved into the game world it has to be translated into a new position.
To accomplish this, DirectX uses a mathematical structure called Matrices to perform manipulations on an object. It is beyond the scope of this article to fully explain Matrices, but if you are feeling lucky, http://en.wikipedia.org/wiki/Matrix_(mathematics), and when you’ve read it you can come back here!
A lower-level understanding of Matrices will help you program them, but we will go into this when we really start to use them in the next tutorial.

Basically, a matrix is a grid of numbers we can perform mathematical operations on to dynamically change the position of a primitive in DirectX.
Naturally, to do this we will need to re-write and add parts to our engine. The first thing to do is add two new objects to our cObject.cs file. These are `cPosObject `and `cPosTriangle`. As you can guess, these are the positioned versions of our original classes.

NOTE: In DirectX, we use the word “Transformed’ to describe vertices which have already been declared in screen-space coordinates (i.e. our last tutorial) and positioned to describe those which have been declared in object space (relative) coordinates.

The new `cObject `class:

```class cPosObject
{
public CustomVertex.PositionColored[] itsVerts;
public Matrix itsTransform;
public bool isActive;
}```

As you can see, this is very similar to our previous class, with the exception that our `CustomVertex `has been changed to a `PositionColored `as opposed to `TransformedColored`. This is explained in the note above.

The second difference is that we have added a `Matrix `object to our class. This is used when we render the triangle. It is how we translate, scale and rotate the object before rendering it (the transformation from `Object `space to World space coordinates).

We will assign this `Matrix `object in the constructor of our new `cPosTriangle `class. The class will have two methods and no members. The methods will be its constructor and the render method. The ability to render itself will be very useful when dealing with large objects. You will be able to see an example of this in the tutorial.

Its members will be inherited from our new `cPosObject `class.

```public cPosTriangle(bool ac, Vector3 Translation,
Vector3 Scale, Vector3 Rotation,Color pColor)
{
itsVerts = new CustomVertex.PositionColored[3];
Matrix T = Matrix.Translation(Translation);
Matrix S = Matrix.Scaling(Scale);
Matrix Rx = Matrix.RotationX(Rotation.X);
Matrix Ry = Matrix.RotationY(Rotation.Y);
Matrix Rz = Matrix.RotationZ(Rotation.Z);
itsTransform  = Rx * Ry * Rz * T * S;
isActive =  ac;
itsVerts[0]  = new CustomVertex.PositionColored
(new Vector3(0,  0, 0), pColor.ToArgb());
itsVerts[1]  = new CustomVertex.PositionColored
(new Vector3(2, 0, 0), pColor.ToArgb());
itsVerts[2]  = new CustomVertex.PositionColored
(new Vector3(1, 2, 0), pColor.ToArgb());
}```

Ok, calm down and move the mouse away from the large X at the top right of your screen.
It isn't that bad – it's basically the same method we have used previously, but we are assigning matrices for Rotation, Translation and Scale based on a set of XYZ coordinates. We make one matrix for each manipulation point (Translation, Scaling and then each rotation side) and then call a method to construct the matrix into a certain manipulation based on one of our parameters.

We then multiply all of these matrices together to come up with a final matrix which we can use to tell the rendering pipeline how to manipulate our primitive.

Because we now have a better system, you no longer need to input a set of vertices into the method, instead we can define the points as `Vector3`s and our scaling matrix can make any triangle from them.

This method can be easily overloaded to suit your needs – maybe you want to input vertices, maybe you don’t need rotation and maybe you want a global scale modifier for each side, etc.

And finally, our `Render() `method:

```public void Render(Device device)
{
device.Transform.World = itsTransform;
device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, itsVerts);
}```

The only change here is the assignment of `itsTransform `to `device.Transform.World`.

This sets how the device will render all primitives until it is re-assigned.
When we get into dealing with larger numbers of primitives, we can call this once for multiple primitives, but for now it will suffice.
We then draw the user primitives and by setting the world transform to `itsTransform`, `itsVerts `is automatically manipulated into the world space coordinates we want.

The next step is to change our `OnPaint `event. We need to call some methods in the rendering loop to set how we look at the scene.
We need to set the camera and view. We will explain in detail how this works here:

```device.Transform.View =
Matrix.LookAtLH(new Vector3(0, -40, 300),
new Vector3(0, -5, 0), new Vector3(0, 1, 0));```

This method defines the properties of our view point, or camera.
In a first-person game, this would be the eye of the character we are playing, for instance.

The `Matrix.LookAtLH `is defined as follows:

`Matrix.LookAtLH(Camera Position, Camera Target, Up )`

The first parameter is fairly self explanatory, a coordinate for the position of the camera.

The second coordinate is also pretty obvious, the target (i.e. where the camera is looking at). This is used with the cameras position to create a vector-line from the camera to its target so DX can calculate where the camera is looking.

The final parameter is not so obvious, but luckily it's almost always the same, this defines which way is upwards. As usual, this is the Y axis.
To put it into application, in a First Person Shooter game, the camera target will be wherever the crosshair (usually an aiming device / scope for the gun in the center of the screen).

As the player moves the mouse, the camera moves along with the crosshair. Although these are not directly linked, it’s a good way to see how the parameters would be used in a real-life application (well, not real life as such, but..)

The camera position would be the player moving around the screen.

Now feast your eyes upon my latest diagram. Although not usually of a high standard, this one should be even more interesting as my laptop (which I am currently using) does not have any image editing software for which the trial has not run out except for the world-(in?)famous Microsoft Paint.

Ok, here you see a bad guy, representing the camera target (what the player is looking at) and three possible camera positions (where the player is standing).

As you can see as the player moves, he/she has to turn accordingly to keep the bad-guy within his/her view range. As such, the vector between them changes.
This displays the relationship between the camera position / target in a visual application of how it may be used in a game (which is what most people reading this article will be interested in doing at some point).

The last stage in setting up a position-coordinate based system is to change the Projection. This is performed in a similar way to setting up the camera stage.
Projection defines things such as the camera’s viewing “cone” (anyone who has played Metal Gear Solid will understand what I mean). This is technically referred to as a viewing "Frustrum".
We make a call to this method:

`Matrix.PerspectiveFovLH(Field of View,Aspect Ratio, Near   Plane, Far Plane);`

The first parameter is our “viewing cone” as discussed earlier. This will usually be PI / 4 (quarter-circle) to mimic the human eye’s vision.
The second parameter is our Aspect Ratio. Usually this will be our height divided by width as with televisions, etc.
The last two parameters define the far and near planes. This means the closest plane it can see at, and the furthest.

Sounds like we need another diagram…

The red area denotes the area which will be rendered. Any objects before our near plane, after our far plane or outside of our viewing cone will be clipped (ignored).

The actual calls to set our camera and projection in the `OnPaint `method are as follows:

```device.Transform.View = Matrix.LookAtLH(new Vector3(0, -40,  300),
new Vector3(0,  -5, 0), new Vector3(0,  1, 0));
device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4,
Width / Height, 1f, 500f);```

Feel free to play around with these variables after you have read the tutorial to see the different reactions you get by changing them.
Now that we finally have our framework set up, we can call it in our `Main() `method.

To show the ease and robustness of our new engine, we will render four differently coloured triangles of the same size, in a line from each other.
To do this, switch back to our `GameWindow `class, and change the `public cTriangle Triangle `to an array of new `cPosTriangles`. Next add an `int `called `NumTriangles `and initialise it to `0`. This will keep track of how many triangles in our array we have initialised.

Then in the `Main() `method, add the following (replacing all code in the `using `statement):

As you can see, besides the color parameter, the only thing that changes is the Translation vector. This increases globally by one each time.
Only one more thing to do... and then you will be that next step closer to Doom IV…
Switch back to your `GameWindow `class, and in between the calls to `BeginScene()` and `EndScene()`, replace the code with the following:

```foreach (cTriangle Triangle in Triangles)
if (Triangle != null)
Triangle.Render(device);```

As you can see, this loops through every triangle in the array, tests whether or not they are `null`, and if not it calls its `Render() `method.
You can clearly see the results when you compile and run the program.

I encourage you to play with the camera and triangle variables – playing with scaling, rotating, translating, camera targets, etc.
In the next tutorial, we will really bring matrixes to use when we create a control system for moving and looking around our world.

## Feedback

I am always open for answering questions, whether through MSN (jamespraveen@aol.com), email (james@magclan.cwhnetworks.com) or through the message board attached to this article.
If you have a problem with any of my samples / my topics in general, please feel free to ask me.

## History

• 21/02/06: First submitted article 4 to CodeProject
• 22/02/06: Re-formatted code syntax

## License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

## About the Author

 Web Developer United States
I live in England, UK in a small town which only just got broadband...

In my spare time, I run a small website design, hosting and maintenance business at www.jamesgupta.com

## Comments and Discussions

 First Prev Next
 You don't have to be old to be wise :-). Judas priest Epsilone33-May-07 10:00 Epsilone3 3-May-07 10:00
 Kudos James... Vorlin26-Dec-06 22:06 Vorlin 26-Dec-06 22:06
 James, Unbelievable work... your ability to write about very technical subjects from a point of view that your readers can follow is excellent. I've done the same for other subjects, so I know what it is to write in a way that, as I say, "puts it in terms that your girlfriend's grandmother could understand". Yea, you have that touch... the touch that allows you to explain complicated things in simple terms that people can actually follow without burning out a few brain cells or having their eyes bulge out of their heads. I especially liked it when you said to back the mouse away from the big X at the top corner of the window... it takes lecturers and public speakers years to learn to use that kind of humor. Don't change ANYTHING about the way your phrase your writing, or your humor... and if anyone says differently then tell them to go study public speaking for four or five years and then come talk to you. I've lectured live on and off for the past eleven years and, if you can handle thinking of 100 people and yourself as being a 1:1 conversation, then your style will server you very, very well should you speak live to an audience. 100 others and yourself as 1:1? Sure... there is one of you and only one audience. That's a 1:1 conversation... and they don't get to talk unless you let them so that makes it even easier If you're interested in more 3d stuff, there is a group of devs who are doing great things with Blender3D (an open source sort of 3DSMax or Maya) and they're taking the Blender Game Engine to the next level in their project "Crystal Space". I've looked at their docs and nearly had an aneurism... they themselves admit that one of their biggest problems right now is that none of their devs can write an understandable set of docs or tutes. Follow the link below for the project site and navigate to this forum post: (Under "Community" in the left margin) Project Forums : General Crystal Space Discussion : Documentation on Usability http://www.crystalspace3d.org/tikiwiki/tiki-index.php?page=CrystalBlend Take a peek around, it seems like the sort of thing that you'd have a great time with. These guys are good coders and modelers, they're just weak in the writing department (and they say that themselves too). Have them read your tutes and I think they'll be more than happy to have you on board! Plus it's the sort of thing that would look great on a resume: Coder and lead doc writer... at your age! LOL Good luck, thanks for the great tutes over here! Scott / Vorlin
 Parts 1-4 ? W. Kleinschmit21-Feb-06 20:55 W. Kleinschmit 21-Feb-06 20:55
 Re: Parts 1-4 ? James Gupta22-Feb-06 6:39 James Gupta 22-Feb-06 6:39
 Formatting guidelines Nishant Sivakumar21-Feb-06 7:20 Nishant Sivakumar 21-Feb-06 7:20
 Re: Formatting guidelines James Gupta21-Feb-06 8:58 James Gupta 21-Feb-06 8:58
 Re: Formatting guidelines Nishant Sivakumar21-Feb-06 9:38 Nishant Sivakumar 21-Feb-06 9:38
 Re: Formatting guidelines Daniel Turini21-Feb-06 12:36 Daniel Turini 21-Feb-06 12:36
 Re: Formatting guidelines Nishant Sivakumar21-Feb-06 13:13 Nishant Sivakumar 21-Feb-06 13:13
 Re: Formatting guidelines James Gupta22-Feb-06 6:43 James Gupta 22-Feb-06 6:43
 Touche! fwsouthern22-Feb-06 7:18 fwsouthern 22-Feb-06 7:18
 Re: Formatting guidelines Nishant Sivakumar23-Feb-06 1:01 Nishant Sivakumar 23-Feb-06 1:01
 Last Visit: 31-Dec-99 18:00     Last Update: 29-Jul-16 3:39 Refresh 1

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160721.1 | Last Updated 24 May 2006
Article Copyright 2006 by James Gupta
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid