3D DirectX10 Free Look Camera (Timer based)
Introduction: Camera Basics
Okay so I promised I’d write a tutorial on writing a simple freelook camera, this tutorial doesnt only apply to DirectX10 but to any graphics API. Okay so the simplest possible camera we cam implement short of a first person style camera is a free look camera, without roll, so basically only two degrees of freedom: left/right and up/down, we are also going to implement some basic movement controls forwards/backwards and Strafe Left/Right.
First things first, as usual I’m not going to go into major detail about viewing and projections, if you want more info check out: http://en.wikipedia.org/wiki/Graphical_projection and http://en.wikipedia.org/wiki/3D_projection . Okay now to view any 3D scene we need a virtual camera with a field of view (FOV- theta) and near (a) and far (b) view planes, these together form a view frustum as show in figure 1. This frustum defines what is visible in the scene, all geometry clipping and hidden surface removal is based on this frustum.
Now the shape of this frustum is controlled by the projection that we we to employ, there are several types of projective but chances are you will end up using a perspective projection. Now the projection is attached to our virtual camera (think of the projection as a camera lens), to orient ourselves in a scene we need to know the position that we are viewing the scene from, the direction in which we’re looking and which way is up (shown in figure 2), now this up direction is very important for movement calculations and obviously to display the scene the right way up
These three parameters are called the eye position, the view vector and the up vector.
For a free look camera, we need the up vector to be in the direction of the camera top, if we were doing a first person camera where the viewer is stuck on the ground then the up direction will always be towards the sky. So in conclusion to view a scene, you need a virtual camera positioned somewhere in a scene (pos/up/view) with some sort of lens (projection).
Now I need to quickly explain what happens to an object’s vertices when it travels through the rendering pipeline. When a object’s vertex gets sent down the rendering pipeline it has a position in its own model space, this position is now moved to wherever the object is required to be in the world by multiplying the vertex by the world matrix for the object. Now if you run through the world you would think of it as the world standing still and the camera moving, but for now it is beneficial to think of it in the opposite way, that the camera is stationary and its the world that’s moving. Cause mathematically that is exactly whats happening.
The camera never moves, the worlds position is just adjusted to give that impression. So we’ve positioned our object in the world, the next step it move it according to our view point, if we want to looking to the left of the object, we need to move the entire world to the right, so we multiply the vertex position with the view matrix to move it as necessary. Remember each object will have its own world matrix but used the exact same view matrix, the last step is to convert from 3D to the 2D image that is displayed on the screen, the camera lens does that just like it does in a real camera, the vertices are flatten to the near viewing plane by multiplying the vertex by the projection matrix. The entire process is show below in figure 3.
The Camera Body: The Container Class
Figure 4 shows the class structure for our camera, source code is below, we have the two matrices: view and projection, we have function to position the camera, change the view and so on…
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.01745329251994329576923690768489
class camera
{
/*******************************************************************
* Members
********************************************************************/
private:
//view parameters
float heading, pitch; //in radians
//matrices
D3DXMATRIX viewMatrix;
D3DXMATRIX projectionMatrix;
D3DXMATRIX rotationMatrix;
//view vectors
const D3DXVECTOR3 dV, dU; //default view and up vectors
D3DXVECTOR3 eye, view, up;
//movement vectors and movement toggles
D3DXVECTOR3 forward, strafeRight;
int movementToggles[4]; //fwrd, bck, strfLft, strfRght
//camera timer for movement
HRTIMER::timer camTimer;
/*******************************************************************
* Methods
********************************************************************/
public:
//constructor and destructor
camera();
virtual ~camera();
//set projection methods
void setPerspectiveProjection( float FOV, float aspectRatio, float zNear, float zFar );
//camera positioning methods
void setPositionAndView( float x, float y, float z, float hDeg, float pDeg );
void adjustHeadingPitch( float hRad, float pRad );
void setMovementToggle( int i, int v );
//update camera view/position
void update();
//get methods
D3DXMATRIX& getViewMatrix() { return viewMatrix; }
D3DXMATRIX& getProjectionMatrix() { return projectionMatrix; }
private:
//create view, forward, strafe vectors from heading/pitch
void updateView();
};
|
The Camera Lens: Creating the Projection Matrix
Lets do the easiest thing first, creating the camera lens or projection matrix. Almost all graphical APIs have a helper functions to create this matrix for you. To create a left handed perspective projection in D3D10, the function you need is: D3DXMatrixPerspectiveFovLH( outputMatrix, FOV, aspectRatio, nearPlane, farPlane)… The code to create this matrix is:
void camera::setPerspectiveProjection(float FOV, float aspectRatio, float zNear, float zFar)
{
//convert FOV from degrees to radians
FOV = FOV * (float) DEG_TO_RAD;
D3DXMatrixPerspectiveFovLH( &projectionMatrix, FOV, aspectRatio, zNear, zFar );
}
I’m setting the projection matrix’s FOV using a degree parameter, since I only do it once and its easier to visualize a degree based FOV its fine, the helper function requires radians, so I simply convert to radians using the constant defined in the class header file. The aspect ratio is the ratio of width:height for your display window. If your window is 640×480 then the aspect ratio is 640:480 -> 4:3 or numerically 1.3333333333…
The Camera: Creating the View Matrix
Since we are using a camera with only 2 degree of freedom (axes of movement) ->heading (left/right) and pitch (up/down), we store the direction in which we’re facing as a angle on each axis. These angles are stored as radians. The final view vector is calculated from these two variables and original position of the view and the up vector is adjusted accordingly. This mean that to get an accurate up vector we need to initialize the camera with correct values when we create it, so that the up vector is valid once the view changes. so the constructor for the camera class is:
camera::camera(): dV( D3DXVECTOR3(0,0,1) ),
dU( D3DXVECTOR3(0,1,0) ),
eye( D3DXVECTOR3(0,0,0) ),
view( D3DXVECTOR3(0,0,1) ),
up( D3DXVECTOR3(0,1,0) ),
forward( D3DXVECTOR3(0,0,1) ),
strafeRight( D3DXVECTOR3(1,0,0) ),
heading(0),
pitch(0)
{
//set matrices to identity
D3DXMatrixIdentity( &viewMatrix );
D3DXMatrixIdentity( &projectionMatrix );
//initialize movement toggles
movementToggles[0] = 0;
movementToggles[1] = 0;
movementToggles[2] = 0;
movementToggles[3] = 0;
}
This always initializes our camera at the position (0,0,0), facing the positive z axis. *NOTE: we will discuss the movement toggles later on. We also set our default view and up vectors to (0,0,1) and (0,1,0) respectively. We will use these default vectors to calculate the new view vector from the heading and pitch parameters when the view changes. So how do we adjust the view? Well we simple modify the heading and pitch parameters and run the updateView() function which we’ll define later. Just to be safe we add hard limits to the values of the heading and pitch (0~2pi) just so over the course of the program we don’t overflow the variables.
void camera::adjustHeadingPitch( float hRad, float pRad )
{
heading += hRad;
pitch += pRad;
//value clamping - keep heading and pitch between 0 and 2 pi
if ( heading > TWO_PI ) heading -= (float) TWO_PI;
else if ( heading < 0 ) heading = (float) TWO_PI + heading;
if ( pitch > TWO_PI ) pitch -= (float) TWO_PI;
else if ( pitch < 0 ) pitch = (float) TWO_PI + pitch;
}
We also said we would be adding movement controls: forwards, backwards and strafing. To move we will need to know which direction is forward ( hint: the view vector
) and which way is directly right, backwards and left are obviously in the direct opposite direction. We now have the two unit vectors forward and strafeRight which also need to be updated as soon as the view changes. As we said forward is easy as it is the view vector and backward is the negative forward vector. How do we calculate the right vector? Well we have the forward direction and the up direction so right should be ortogonal (90 degrees) to both of them (ie the x axis, if forward is the z axis and u is the y axes), hm, we have two vectors and need to get another vector orthogonal to them, if we think back to basic calculas and we suddenly remember that the cross product of two vectors gives us an orthogonal vector, wiki the cross product for more details. We also need to unsure that the movement vectors are unit vectors to ensure consistant movement.
To create the new view and up vectors, we first create a rotation matrix which will rotate any point/vector by the degrees on each axis that we specify, we then rotate the default view and up vectors (du & dv) to get the new view and up vectors. Easy Peasy…
We now have all the info necessary to create our updateView() function.
void camera::updateView()
{
//create rotation matrix
D3DXMatrixRotationYawPitchRoll( &rotationMatrix, heading, pitch, 0 );
//create new view and up vectors
D3DXVec3TransformCoord( &view, &dV, &rotationMatrix );
D3DXVec3TransformCoord( &up, &dU, &rotationMatrix );
//create new forward and strafe vectors
D3DXVec3Normalize( &forward, &view );
D3DXVec3Cross( &strafeRight, &up, &view );
D3DXVec3Normalize( &strafeRight, &strafeRight );
//take into account eye position
view = eye + view;
//update view matrix
D3DXMatrixLookAtLH( &viewMatrix, &eye, &view, &up );
}
Statically Positioning our Camera
void camera::setPositionAndView(float x, float y, float z, float hDeg, float pDeg)
{
//set eye coordinates
eye.x = x;
eye.y = y;
eye.z = z;
//set heading and pitch
heading = hDeg * (float) DEG_TO_RAD;
pitch = pDeg * (float) DEG_TO_RAD;
//update view
updateView();
}
This simply positions the camera on a location and set the view according to the heading/pitch params specified.
Moving our Camera
There are two ways to move the camera, incrementally step by by, i.e. on each update (frame) we move the camera by a fixed amount but there is a massive problem with this, variable framerates! Imagine if we moved the camera by 1 unit every frame, on a midrange PC getting around 45 frames per second we will be moving at 45units a second, but now comes a guy with a monster machine and runs the game at 75 frames per second and our movement speed has increased as well, this is unacceptable and would ruin any game. I dont know if any of you remember how quake3’s physics was reliant on the FPS, and so guys getting higher FPS rates could run faster and jump further than guys with lower FPS rates. So how do we correct this? We need to ensure a fixed movement rate based on time and not frames, so we do just that, we make movement timer based! We firstly add movement toggles, which get activated when a player pushes a movement key, and deactivated when he releases it, this is necessary to ensure that if a player pushes both forward and back the camera doesnt move and then when he releases a single key it immediately starts moving in the still active direction.
The to work out the distance moved between updates, we create a simple timer and check the elapsed time in seconds, we then multiply this time by the movement rate per second. Both of these functions are shown below:
/*******************************************************************
* set camera movement toggles
********************************************************************/
void camera::setMovementToggle( int i, int v )
{
movementToggles[i] = v;
}
/*******************************************************************
* update camera view and position - timer based for fluid movement
********************************************************************/
void camera::update()
{
//get current elapsed time
float t = (float) camTimer.getElapsedTimeSeconds();
//update position - 1.5 unit per second
eye += t * ( movementToggles[0] + movementToggles[1] ) * 1.5f * forward +
t * ( movementToggles[2] + movementToggles[3] ) * 1.5f * strafeRight;
//update view
updateView();
//reset timer
camTimer.reset();
}
This update function is what gets called from the main game loop, also the reason why updateView() is declared private. On each frame update, you will process input, set the movement toggles appropriately, run the adjustHeadingPitch() function and then run the update() function to create the new view matrix.
NOTE: The timer used is based on my silly encapsulation of the tick timers in the windows API, look at my High Resolution Timers entry.
Conclusion
So there we have it, a super simple super basic free look camera implementation in less than a 100 lines of code. In the next blog entry I will be covering how to give the camera mouse control using Windows Raw Input. With those two tutorials you will have all the tools to create a basic input system for your game!!!
Hoep you enjoyed it!
My Thoughts on Dragon Age: Origins
I have been nervously awaiting Dragon Age: Origins, I’ve been following all related news and so on for months, so when the release date finally drew near I was super excited. The game launched on the 9th of November here, a monday, so I ended up taking 2 days leave just so me and the game could spend some quality time together. Now before I continue, I do need to state for the record I am a huge Baldur’s Gate / Original Fallout fan, those two series are my top two games of all times, basically that means that any future RPG will be compared to those games, and boy do they set the bar high!
We got the game a few days after the rest of the world so I read through several reviews, most of them giving the game great scores! (“Yay!” I thought, but then remembered that fallout 3 also got great scores and the moment died) Then I read the bit-tech.net review (I honestly value those guys opinions – they are definitely one of the better review sites on the net – well their hardware reviews need work) and in that review they said the game was good but nothing new or special, that it brought nothing to the table and yeh… I was honestly a little ticked off by that since in the last 5 years there have been a lot of games that tried doing the same old stuff and failed miserably, the witcher & mass effect are the only RPGs in the last 5 years that I felt did things right, I dont mind if its the same concepts and stuff, just do it right! I’m so tired of devs being forced to bring new elements to the table and in many cases have those exact elements sink the game.
So how did I find the game?! well I technically finished it twice in the space of 5 days. I really didn’t get much sleep, I think I sunk around 50+ hours into it, and how do i feel about it? Well, I love it!! This game is exactly what I expected and much much more! It is everything baldur’s gate 2 was and more. (Yes,unfortunately the world is smaller, but its understandable since created 3D environments is a lot harder than drawing a few sprites).
I’m not going to go into too much detail regarding the game since there are a million detailed reviews out there to read, simple this will be a brief overview of what I loved/disliked about the game.
What they did right
To start the new character system at first glance looks very simple and scaled down, with not a lot of customization options, so I thought this might be a bad thing then I remembered how much I disliked the D&D 3rd edition rules just for the opposite reason, where there was no real specialization, ie. i could get a lock picking, plate wearing, archer mage who would be terrible at all those thing but the combination was still possible. The new character system in DA is simple and specialized and I love that about it, from when you create the character you can already see the bigger picture, although on the first play through I did make a few mistakes in my character build that made me play through again.
The combat is tactical and involved, its not simple point at the enemy and chill, you need to be constantly on your toes watching what the enemy is doing and responding to it, the whole combat tactics section was awesome, in it you can set up condition that control your characters, for example if you’re magic resistant templar sees a mage on the battlefield that must be his priority target, or if your fighter encounters an enemy with high armor should use a armor piercing ability. There is also a nice feature that when a player attacks and enemy from behind he gets large bonuses to his hit chance and damage, this has been present in other RPGs but it was usually restricted to rogue/thieves and backstabbing, here its open to anyone so character positioning is very important. There is also a heavy reliance on traps and poisons, the poisons/traps available are quite varied and mixing them up in battle is pretty damn awesome, leading an enemy through a grease trap you laid then finishing him off with some crow poison… All characters should have at least one level of poison making so they can make use of the poisons.
The armor/damage system needs special mention, this is probably the first game I cnat think of where armor does not affect a character chance to be hit score, a character’s armor and defense are entirely seperate entities and honestly thats exactly how it needs to be, armor only reduces the amount of damage taken when hit, so it is there to help out low dex characters like fighters that do tend to be hit a lot, and it also allows a mage to wear light robes and and end up being more protected than my fighter wearing the heaviest armor possible. And to counter that each weapon has an armor penetration number, so certain weapon types are better against certain targets.
I could go on and on about how elegant and well implemented the combat system in the game is but i wont
There have been complaints regarding the difficulty but I personally loved it, I can understand that the average console player was probably tearing his hair out during some sections of the game, and that they’re probably not used to having to plan ahead and that the slow pace of combat would probably be frustrating, but that just made the game so much better for me. A lot of the battles that I kept losing over and over ended up being easily won once I changed tactics and I like the idea that the reason I was losing was because I wasn’t thinking.
The character design is excellent, not really much to say in that regard, the character interaction is very mass effect’ish (most recent example) and that’s a good thing, bioware have improved the inter party dialogue with each game they make just making it better and better. The world/mythology they created is also excellent, and very detailed, its really nice to discover a new world. Half the enjoyment of the game for me was reading all the info about the world lying around in books and scrolls. The overall writing quality in the game is excellent and I cant wait to revisit the world they created
Movement is achieved by means of a world map (BG style), I personally prefer this as this allows for more detailed locations than in a free roam game, I’d rather have less unique locations then tons of the same dungeons/subways (cough fallout3/oblivion cough).
The size of the world is a little small, but it still provides you with plenty of characters to meet and areas to explore, even so with the small world it still took me around 35hrs to finish the game the second time round, and that’s with me rushing through through the first half. Granted around 5hours of that was watching loading screens :/
What they did wrong
While I was praising the earlier character and combat systems, they aren’t perfect. The main concern for me was the lack of detailed info on the effects of abilities and spells. Getting told that this ability boosts damage at the cost of defence doesn’t particularly help especially when you don’t know by how much, you end up having to write down the character stats then enable the ability and check the differences. Also there was absolutely no display available for a characters missile defense score or for his attack speed. The lack of any sort of attack speed indicator on weapons is honestly unacceptable, and the fact that certain abilities seem to affect this attack speed with absolutely no mention of it is also unnacceptable (ie. I swear powerful swings slows down the attack rate, but it might just be me imagining things). Also info that’s missing is the level of skill required to lock pick certain items / disarm traps / etc. and most importantly the amount of damage spells do, players are left totally in the dark regarding that – descriptions state: spell does moderate damage over a short period (WHAT THE HELL DOES THAT MEAN – what does moderate mean? moderate at level 5 is different to moderate at level 15).
What I would have liked to see when a player mouse overs a score like attack, is the base attack score, the additional attack score from attributes, then each item and buffs contributions to the score. Its frustrating when your character has 3 or more effects on him and you cant tell what the hell each one does, apart from that it affects a certain score. Also add the damn attack speed on weapon descriptions, I want to clearly see exactly how much faster a long bow is compared to a short bow, I also want to see exactly how much faster enabling rapid fire on an archer is compared to his standard attack. The game combat system is very intricate and detailed and then hiding all the elegant maths from the player is simply not on. I cant imagine that any player would be scared off by the numbers if anything it would probably improve the experience. Currently playing the game feels like you’re driving a car with gauges telling you your battery voltage, oil pressure, tire pressure, outside temperature and a million other non essential things in detail, and then having your speedometer have three sections marked: slow/fast/very fast, not really very useful…
Another problem for me was the loading times, my machine at home is by no means low end or even mid range, its well up in the high end bracket and even so the load times were a bit ridiculous, ranging from 10s to 2minutes for even the smallest areas, and it may be my imagination but they seems to deteriorate over time, starting out reasonably quick and gettign progressively worse over time. Another thing is the the mini transitions between main areas and sub areas in thsoe main areas also take long but it seems like that’s due to some sort of race condition / thread blocking since during each transition I can see my CPU load meters lock at 100% for both cores for a few seconds, once they unlock the loading starts and the transition occurs quickly. So if they can fix that problem the load times should decrease by a fair bit.
Conclusion
I know i barely went into any detail about the game and what its about and so on but like I said there are a ton of other places to check up on that, while like people have said the game isn’t anything revolutionary or new, it’s an old concept done right! Change isn’t always a good thing, take an old concept and improve on it, do it right, take the effort to make a good solid game that people enjoy playing rather than some half assed proof of concept BS or some watered down consoletard game (cough COD:MW2 I’m looking at you cough). It’s also really good to know that there are companies out there that care about PC gamers. Bioware and EA, thank you so much for Dragon Age, this game is by far the game of the year for me!!! You are welcome to my money any time in the future.
Basically this game is Baldur’s Gate 2, just not using the D&D rules and not set in the forgotten realms! If you liked BG then you’ll love this!! Honestly this is a game worth buying so go out and buy it if you haven’t already!!!
October Update! Exciting News!!!
I’ve had a rough few months, and have finally started to recover! In the middle of last year I ended up burning from stress and trying to juggle a million things at once, and I’m finally starting to feel like I’ve recovered. I’ve spent the better part of the day cleaning up the blog layout and the content of several of my popular blog posts just to make it easier to read.
I’ll be writing up a few new tutorials soon! As a sneak peak the upcoming tutorials cover: windows RAW input, how to write a 3D free look camera, an introduction to pathfinding and a tutorial on an efficient implementation of A*.
In other news I’ve had a bit of a career change, I’ll be lecturing the final year and honors level graphics courses, 3D graphics is one of my key interests second only to AI, and this gives me the opportunity to focus on it full time! This also mean that in setting up the course content for next year, a few more DX10 tutorials will pop up
And I’ll finally get the time to do and show some more advanced techniques like bump-mapping, anti-aliasing and maybe even occlusion culling. I’m pretty damn excited!!!
Also in the pipeline is a complete upgrade for my slightly less monstrous machine, once the upgrade is complete it will be back in the realm of monstrous
I cant wait!! I’ll post photos of the work-log and everything once I get all the parts in, I cant believe that I’m forced to import components due to lack of local suppliers or if there is a supplier with stock then I have to deal with absolutely insane prices.
I don’t know how much free time I’ll have in the next few days since I have one last deadline to complete at my current work, but once that is done I’ll write that camera tutorial!
In other news my master’s thesis is going well, I’ve completed my literature survey and have developed a very basic pathfinding framework to test various algorithms with, but the actual writing of the thesis is whats killing me. Writing has never been a strong point of mine, I know what I want to say but I struggle to put it into words and ironically I have a pretty large vocabulary so even that isn’t an issue. I’m never really happy with anything I write, but i guess that’s the nature of being a little bit of a perfectionist.
Downtime
I havent really updated this blog in forever, I’ve been swamped with work. Its not helping matters that my band is also completing our full-length album release so thats also taking up a fair chunk of my time.
The last few months have been crazy, I’ve been reading so much about pathfinding in games and I’ve learnt so much, I’ve started writing my masters thesis but I’m finding it extremely difficult and time consuming, writing has never been a strong point, never mind academic writing. I’ve spent around 2 weeks writing and re-writing and only have 3 completed pages of my introduction.
Its and interesting field and I have a few ideas on extending a few common algorithms to provide faster and better quality results, but as with all things research related there is always the chance that my “optimizations” might end up making things worse, as Adam Savage says “Failure is always an option”.
Looking at the directx 11 specs, tech demos, etc I cant help but feel excited, I kinda just wanna lock myself in a room and play with it
My directx 10 tutorials kinda stopped coming as my workload increased and I’m sorry about that, I’ll try my best to find the time to perhaps write a new tutorial or two.
Anyways I had a bit of time before a meeting at work and realized I havent touched this blog in a while so decided to show it some love
Some C++ Debugging Advice – use #ifdef
If you are an experienced c++ programmer you can stop reading, nothing I cover here will be of any use to you. I’m just writing a little post on a simple little preprocessor trick that c++ programmers have been using for years. Using the #ifdef directive to tag blocks of debugging code.
So lets we have a function that does something like searching through a tree, while debugging we’d like to lets say have a counter to count all the nodes visited during the search but of course in the released version we don’t need this counter using up precious resources. lets have some example code:
double counter;
void treeSearch::visitNode(node* n)
{
... some code ...
counter++;
}
Some beginner programmers would probably do this:
bool debugMode = false;
double counter;
void treeSearch::visitNode(node* n)
{
... some code ...
if ( debugMode) counter++;
}
Yeh, that works but now every time you visit a node, the program evaluates an if statement, not to mention the memory for the counter has still been allocated. Yes in this case memory cost is pretty negligible but that is not always the case. So how can we improve upon this? The answer is by using basic pre-processor directives, more specifically conditional inclusions (#ifdef). Simply put these directives mark code for inclusion into the program during compile time if some condition is met.
so our code would now look as follows:
#ifdef _DEBUG
double counter;
#endif
void treeSearch::visitNode(node* n)
{
... some code ...
#ifdef _DEBUG
counter++;
#endif
}
So now those lines will only be included if _DEBUG is defined, what this means is that the counter variable and the code that increments it doesnt get compiled, and your program is as fast as possible and still has debugging functions. You can set the _DEBUG flag in your code manually by using the define directive: #define _DEBUG, or if your IDE supports it, multiple compile profiles, one of which defines _DEBUG before compiling. Visual studio by default defines _DEBUG when compiling using the Debug profile.
So that was a very simple and brief tutorial on something pretty much most C++ programmers know, but i posted it just in case some didnt
IE8 Brief Performance
so it got released yesterday and there’s a lot of speculation regarding performance, so I just briefly timed the load timed of a few of my favorite sites:
| ie | ff | |
|---|---|---|
| wordpress | 8 | 15 |
| 8.8 | 9.1 | |
| xs forums | 9.6 | 13 |
| cnn.com (uncached) | 18 | 28.2 |
| myspace | 5 | 8 |
| myspace login | 27 | 26.7 |
IE8 seems to do okay, my tests arent super scientific or even done properly, just a quick test between the latest stable FF3 and IE8 on my machine. The memory usage of IE8 seems crazy tho: 60mb for two tabs across 2 processes. Earlier it was sitting at around 210mb for 3 tabs open with 4 processes running.
I’m sure they’ll be some in depth performance reviews in the future, and I’ll be looking forward to it…
My attempt at a DX10 game engine… Name Ideas
So I’ve started with developing the AI test bed for my masters experiments, and since I kinda wanted something that looked nice, I basically started developing a game engine without knowing it
I’ve been working on it for around a week now, and have a very basic renderer and a basic camera system going… The next step will be developing the scene graph and spatial data structures needed for rendering. I’ve been doing so much reading on scene graphs and so one that it’s coing out my ears and yet I’m not any closer to having an idea on a good solution. I could probably do my entire masters on scene graphs and spatial sorting.
Anyways I’m going to discontinue my DX10 tutorials since all the future tutorials will anyways be based off of my engine, so I’m going to start a new series of tutorials on building a very basic dx0 game engine.
The amount of files in the projects are growing and I need to come up wiht a nice name so i can start encapsulating the classes in namespaces, and have a nice uniform naming across the components, since the engine is going to be super super simple i was thinking as using one of the following as the engine name:
- Cimplicity
- basikEngine
- CimplEngine
- SimplEngine
- engineBasix
Any other suggestions?
DirectX 10 Tutorial 5: Basic Meshes
Since my car has been broken for the last two days, I’ve taken off work and have been working on my Masters degree, since part of my Masters involves building a small “game engine” for AI testing, I’ve been doing some more DX10 work, so its convenient for me to quickly slap together a few more tutorials.
Mesh Basics
I covered the basics of indexed buffers and the depth testing in the last tutorial, in this short tut, I’m going to cover the basics of directX meshes. A mesh is a data structure that contains all the vertex and index buffers needed to draw an object. It’s a neater method of drawing objects as we’ll see.
There are four steps to using meshes:
- Create the mesh
- Fill the Mesh with the index and vertex data necessary
- Commit the mesh to the device
- Draw the mesh
So let’s create a new mesh, first things first, we’ll define an ID3DX10Mesh* pointer called pMesh.
//create mesh
if ( FAILED( D3DX10CreateMesh( pD3DDevice, vertexInputLayout, 2, "POSITION", 8, 12, D3DX10_MESH_32_BIT, &pMesh) ) ) return fatalError("Could not create mesh!");
//vertices for a cube
vertex v[8];
v[0] = vertex( D3DXVECTOR3(-1,1,-1), D3DXVECTOR4(1,0,0,1) ); //front top left
v[1] = vertex( D3DXVECTOR3(1,1,-1), D3DXVECTOR4(0,1,0,1) ); //front top right
v[2] = vertex( D3DXVECTOR3(-1,-1,-1), D3DXVECTOR4(0,0,1,1) ); //front bottom left
v[3] = vertex( D3DXVECTOR3(1,-1,-1), D3DXVECTOR4(1,1,0,1) ); //front bottom right
v[4] = vertex( D3DXVECTOR3(-1,1,1), D3DXVECTOR4(1,0,0,1) ); //back top left
v[5] = vertex( D3DXVECTOR3(1,1,1), D3DXVECTOR4(0,1,0,1) ); //back top right
v[6] = vertex( D3DXVECTOR3(-1,-1,1), D3DXVECTOR4(0,0,1,1) ); //back bottom left
v[7] = vertex( D3DXVECTOR3(1,-1,1), D3DXVECTOR4(1,1,0,1) ); //back bottom right
//create indexes for a cube
unsigned int i[36] = { 2,0,3,3,1,0,
3,1,7,7,5,1,
6,4,2,2,0,4,
7,5,6,6,4,5,
0,4,1,1,5,4,
6,2,7,7,3,2 };
//insert data into mesh and commit changes
pMesh->SetVertexData(0, v);
pMesh->SetIndexData(i, 36);
pMesh->CommitToDevice();
Now we use D3DX10CreateMesh to create the mesh, the parameters are the d3d device, the vertex input layout, number of elements in the vertex layout, the name of the element that stores the vertex position, number of vertices, number of faces, mesh flag and finally the output mesh pointer.
There is a little trick here, remember how before we had triangle strips? Here we don’t, we have to specify every single triangle (referred to as a face in the mesh) by hand. So our index list looks a bit different from before.
To add the vertex data and index data, we simply use the mesh SET methods, a mesh can have multiple vertex buffers and so when you set a vertex buffer, you need to specify in which slot you wish to store it. The index data SET method simply takes the index array and the number of indexes in it.
The final step is to commit the mesh, every time you make a change to a mesh you need to commit it to the device before the changes will be taken into effect.
Drawing the Mesh
So how do we draw a mesh?
for( UINT p = 0; p < techDesc.Passes; p++ )
{
//apply technique
pBasicTechnique->GetPassByIndex( p )->Apply( 0 );
pMesh->DrawSubset(0);
}
How simple is that? We use the draw subset method of the mesh interface to draw the mesh, the value given to the method specifies with attribute group to draw, attribute groups for meshes will be covered in a later tutorial, for now just specify 0. And that’s how to create and draw a basic mesh.
I’ve updated the draw code to render a bunch of spinning cubes just for fun:

Source Code
Source code: tutorial5.zip
DirectX 10 Tutorial 4: Indexed Buffers and Depth Testing
Okay so it’s been a while since my last tutorial, and I apologize for that. We dealt with textures in the last tutorial, and many of you might be wondering while I handled that so early? Well mainly because D3D 10 isn’t exactly an API designed for beginners, so a critical feature required for any scene rendering (depth testing or z-buffering) is done in D3D by use of a depth stencil texture, covering textures before depth testing makes sense in this case. Remember guys I’m not going to spoon feed you, these tutorials expect you to read the SDK docs for details on the variable types and the methods, these tutorials are just to give you a running start.
Indexed Buffers
Before I get to Depth Testing, let’s draw something a little more complicated that a quad, how about a cube. Using the same method as in tutorial 3 the code to draw a six sided cube is as follows:
//CUBE DRAW METHOD 1
//------------------------------------------------------------
//lock vertex buffer for CPU use
pVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**) &v );
//vertices for a cube
v[0] = vertex( D3DXVECTOR3(-1,-1,-1), D3DXVECTOR4(1,0,0,1) );
v[1] = vertex( D3DXVECTOR3(-1,1,-1), D3DXVECTOR4(0,1,0,1) );
v[2] = vertex( D3DXVECTOR3(1,-1,-1), D3DXVECTOR4(0,0,1,1) );
v[3] = vertex( D3DXVECTOR3(1,1,-1), D3DXVECTOR4(1,1,0,1) );
v[4] = vertex( D3DXVECTOR3(1,-1,-1), D3DXVECTOR4(1,0,0,1) );
v[5] = vertex( D3DXVECTOR3(1,1,-1), D3DXVECTOR4(0,1,0,1) );
v[6] = vertex( D3DXVECTOR3(1,-1,1), D3DXVECTOR4(0,0,1,1) );
v[7] = vertex( D3DXVECTOR3(1,1,1), D3DXVECTOR4(1,1,0,1) );
v[8] = vertex( D3DXVECTOR3(-1,-1,1), D3DXVECTOR4(1,0,0,1) );
v[9] = vertex( D3DXVECTOR3(-1,1,1), D3DXVECTOR4(0,1,0,1) );
v[10] = vertex( D3DXVECTOR3(-1,-1,-1), D3DXVECTOR4(0,0,1,1) );
v[11] = vertex( D3DXVECTOR3(-1,1,-1), D3DXVECTOR4(1,1,0,1) );
v[12] = vertex( D3DXVECTOR3(-1,-1,1), D3DXVECTOR4(1,0,0,1) );
v[13] = vertex( D3DXVECTOR3(-1,1,1), D3DXVECTOR4(0,1,0,1) );
v[14] = vertex( D3DXVECTOR3(1,-1,1), D3DXVECTOR4(0,0,1,1) );
v[15] = vertex( D3DXVECTOR3(1,1,1), D3DXVECTOR4(1,1,0,1) );
v[16] = vertex( D3DXVECTOR3(-1,-1,1), D3DXVECTOR4(1,0,0,1) );
v[17] = vertex( D3DXVECTOR3(-1,-1,-1), D3DXVECTOR4(0,1,0,1) );
v[18] = vertex( D3DXVECTOR3(1,-1,1), D3DXVECTOR4(0,0,1,1) );
v[19] = vertex( D3DXVECTOR3(1,-1,-1), D3DXVECTOR4(1,1,0,1) );
v[20] = vertex( D3DXVECTOR3(-1,1,-1), D3DXVECTOR4(1,0,0,1) );
v[21] = vertex( D3DXVECTOR3(-1,1,1), D3DXVECTOR4(0,1,0,1) );
v[22] = vertex( D3DXVECTOR3(1,1,-1), D3DXVECTOR4(0,0,1,1) );
v[23] = vertex( D3DXVECTOR3(1,1,1), D3DXVECTOR4(1,1,0,1) );
pVertexBuffer->Unmap();
//send vertices down pipeline
for( UINT p = 0; p < techDesc.Passes; p++ )
{
//apply technique
pBasicTechnique->GetPassByIndex( p )->Apply( 0 );
//draw 5 quads
pD3DDevice->Draw( 4, 0 );
pD3DDevice->Draw( 4, 4 );
pD3DDevice->Draw( 4, 8 );
pD3DDevice->Draw( 4, 12 );
pD3DDevice->Draw( 4, 16 );
pD3DDevice->Draw( 4, 20 );
}
The code above produces the following cube (I’ve added rotation and moved the camera – take a look at the code for more details), noticed the depth problem, whatever face got drawn last is on top irrespective of whether it is obscure by another face.
Image 1 – No Depth Testing
So as you can see to draw our cube we need to define each vertex and add it to the vertex buffer, then call the draw method 6 times. Each draw call draws a single triangle list with 4 vertices, drawing each face. So in this method we send 24 vertices down the pipeline and use 4 draw calls. This is a little crazy just to draw a single cube with only 8 vertices. There must be a simple more efficient method of doing this and there is: indexing.
What indexing does is let you pass a vertex buffer containing all the key vertices down the pipeline, and also a list of the order the vertices must be drawn in. So for our cube you’ll send the 8 vertices down the pipeline followed by a list of the order to draw them in. It’ll be a bit clearer once you see the code and of course read the index buffer sections in the SDK docs.
//lock vertex buffer for CPU use
pVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**) &v );
//vertices for a cube
v[0] = vertex( D3DXVECTOR3(-1,1,-1), D3DXVECTOR4(1,0,0,1) ); //front top left
v[1] = vertex( D3DXVECTOR3(1,1,-1), D3DXVECTOR4(0,1,0,1) ); //front top right
v[2] = vertex( D3DXVECTOR3(-1,-1,-1), D3DXVECTOR4(0,0,1,1) ); //front bottom left
v[3] = vertex( D3DXVECTOR3(1,-1,-1), D3DXVECTOR4(1,1,0,1) ); //front bottom right
v[4] = vertex( D3DXVECTOR3(-1,1,1), D3DXVECTOR4(1,0,0,1) ); //back top left
v[5] = vertex( D3DXVECTOR3(1,1,1), D3DXVECTOR4(0,1,0,1) ); //back top right
v[6] = vertex( D3DXVECTOR3(-1,-1,1), D3DXVECTOR4(0,0,1,1) ); //back bottom left
v[7] = vertex( D3DXVECTOR3(1,-1,1), D3DXVECTOR4(1,1,0,1) ); //back bottom right
pVertexBuffer->Unmap();
//create indexes for a cube
unsigned int* i = NULL;
pIndexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**) &i );
//front face
i[0] = 2;
i[1] = 0;
i[2] = 3;
i[3] = 1;
i[4] = 0xffffffff; //start new strip
//right face
i[5] = 3;
i[6] = 1;
i[7] = 7;
i[8] = 5;
i[9] = 0xffffffff;
//left face
i[10] = 6;
i[11] = 4;
i[12] = 2;
i[13] = 0;
i[14] = 0xffffffff;
//back face
i[15] = 7;
i[16] = 5;
i[17] = 6;
i[18] = 4;
i[19] = 0xffffffff;
//top face
i[20] = 0;
i[21] = 4;
i[22] = 1;
i[23] = 5;
i[24] = 0xffffffff;
//bottom face
i[25] = 6;
i[26] = 2;
i[27] = 7;
i[28] = 3;
pIndexBuffer->Unmap();
//send vertices down pipeline
for( UINT p = 0; p < techDesc.Passes; p++ )
{
//apply technique
pBasicTechnique->GetPassByIndex( p )->Apply( 0 );
//draw 5 quads - 29 indexes = 4 indexes x 6 faces + 5 breaks
pD3DDevice->DrawIndexed( 29, 0, 0 );
}
Wow! That’s a lot of code! Well not really once you look at it, we add the 8 key vertices to the vertex buffer exactly as before. Then we map the index buffer in exactly the same way as the vertex buffer and start filling it with the indexes of vertices in the vertex buffer. So for the front face we’re telling it, draw the front bottom left vertex, then the front top left vertex, then the front bottom right and finally the front top right. Now whats the 0xffffffff mean? Well that indicates that a new line list or triangle list must be started at that point, It does the same job as calling a draw call for each face without any of the overhead.
So lets do some basic maths on what we saved by using indexing rather than the standard vertex buffer method. We had a vertex buffer filled with 24 vertices (each vertex weighing in at 224bits) so the vertex buffer 672bytes large. In the second case the vertex buffer is 224bytes, and we have an index buffer with 29 32bit ints (116bytes) so a total of: 340bytes. That’s nearly 50% reduction in memory used, not to mention we are only using a single draw call compared to 4 draw calls when using just a vertex buffer.
So how do we create the index buffer? Well in DX10 all buffers are the same so we create an index buffer the in the same manner we created a vertex buffer with a few minor changes:
//create vertex and index buffers (space for 100 entries)
//---------------------------------------------------------------------------------
//create vertex buffer
UINT numVertices = 100;
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DYNAMIC;
bd.ByteWidth = sizeof( vertex ) * numVertices;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;
if ( FAILED( pD3DDevice->CreateBuffer( &bd, NULL, &pVertexBuffer ) ) ) return fatalError("Could not create vertex buffer!");
//create index buffer
//change buffer desc bytewidth to index type
bd.ByteWidth = sizeof( unsigned int ) * numVertices;
if ( FAILED( pD3DDevice->CreateBuffer( &bd, NULL, &pIndexBuffer ) ) ) return fatalError("Could not create index buffer!");
//set vertex and index buffers
UINT stride = sizeof( vertex );
UINT offset = 0;
pD3DDevice->IASetVertexBuffers( 0, 1, &pVertexBuffer, &stride, &offset );
pD3DDevice->IASetIndexBuffer( pIndexBuffer, DXGI_FORMAT_R32_UINT, offset );
We still use the createBuffer method to create the index buffer, we just change the bytewidth since the buffer stores 32bit unsigned ints. Once the buffer is created we bind it to the Input Assembly by calling the IASetIndexBuffer method ( all we need to specify is a pointer to the buffer, the format of the indexes, and the offset, in case we wish to use only a set portion of the buffer ).
Depth Testing (Z-buffering)
I’m not going to explain in depth what depth testing/depth buffering/z-buffering is (its covered in almost all beginner graphics tutorials), you guys can use google for that , but here’s a quick link to the basics: http://en.wikipedia.org/wiki/Z-buffer.
In DX10, depth testing is accomplished by making use of a depth stencil, there is a nicely detailed section in the SDK docs regarding the Output-Merger Stage, and here they cover how DX10 accomplishes the depth stencil test internally.
So lets just briefly go over what depth testing is, we have a depth buffer that stores the distance for each pixel in the screen to the camera, so for every pixel we draw from the pixel shader, we compare it’s distance to the camera to the distance stored in the depth buffer, if the new pixel is closer than the distance in the depth buffer then it is drawn and the depth buffer is updated with that pixels distance. That way we only draw the closest visible objects to the viewer, obstruction further objects.
So lets enable this in DX10:
//dx manager members
ID3D10Texture2D* pDepthStencil;
ID3D10DepthStencilView* pDepthStencilView;
bool dxManager::createRenderTargetsAndDepthBuffer( UINT width, UINT height )
{
//try to get the back buffer
ID3D10Texture2D* pBackBuffer;
if ( FAILED( pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*) &pBackBuffer) ) ) return fatalError("Could not get back buffer");
//try to create render target view
if ( FAILED( pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView) ) ) return fatalError("Could not create render target view");
pBackBuffer->Release();
//create depth stencil texture
D3D10_TEXTURE2D_DESC descDepth;
descDepth.Width = width;
descDepth.Height = height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D32_FLOAT;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D10_USAGE_DEFAULT;
descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
if( FAILED( pD3DDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil ) ) ) return fatalError("Could not create depth stencil texture");
// Create the depth stencil view
D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = descDepth.Format;
descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
if( FAILED( pD3DDevice->CreateDepthStencilView( pDepthStencil, &descDSV, &pDepthStencilView ) ) ) return fatalError("Could not create depth stencil view");
//set render targets
pD3DDevice->OMSetRenderTargets( 1, &pRenderTargetView, pDepthStencilView );
return true;
}
First we add two new members in the dxmanager class, a ID3D10Texture2D depth stencil pointer and a depth stencil view pointer. Then we create a new texture and assign it to the depth stencil pointer. After this we create a view to the texture by making use of a depth stencil view desc, sort of like the way we created texture views.
The final step is to modify the Output manager’s render targets to include the depth stencil, this automatically enables depth testing. Once we run the program, we get this result:
Image 2 – Depth Testing Enabled
So that’s basically it for this short tutorial, I’m sorry its so short and simple, I’m just flooded with other work right now. I’m going to be covering meshes and lighting in the next several tutorials.
Source Code
Source Code + VS2k8 project files: tutorial4.zip



