This page contains some frequently asked questions about the Direct3D API contained within the DirectX SDK. This page is continuously being updated.
You should link with debug versions of the libraries when doing a debug build so you get warnings and error messages. Obviously for your final release build you should link with the release libraries.
d3d9.lib - stub library for Direct 3D, link is with d3d9.dll at runtime, debug level controlled by Control Panel
d3dx9.lib - the release library for D3DX, full code no dll
d3dx9d.lib - the stub library for D3DX, link is with the (SDK only) d3dx9d.dll at run time
d3dx9dt.lib - debug library for D3DX, full code no dll - useful if giving to someone without SDK installed for testing.
Note: from the February update of the SDK D3DX has been moved into a .dll. Also the statically linked debug library (D3DX9dt.lib) has been removed; use D3DX9d.lib instead. More info: here
Other useful ones
d3dxof.lib - x file reading templates
dxguid.lib - to use globally unique identifiers
dxerr.lib - used for getting DirectX error strings
See also: Link Errors
I have added some notes on picking. See the page here: Picking
There are a number of D3DX functions that can help with collisions:
D3DXBoxBoundProbe - determines if a ray intersects a bounding box
D3DXIntersect - determines if a ray intersects a mesh
D3DXIntersectTri - more accurate but slower test
D3DXSphereBoundProbe - determines if a ray intersects a bounding sphere
D3DXPlaneIntersectLine - finds the intersection between plane and a line
D3DXVec3Unproject - projects a vector from screen space into object space
The texture provides an interface called GetLevelDesc(...) this returns information on the loaded texture like its width, height, colour format, memory position etc. To use this function you must declare an instance of the D3DSURFACE_DESC structure. You pass the address of this instance to the GetLevelDesc function and it fills in the values. After the call you can look at the filled values. An example might be:
D3DSURFACE_DESC desc;
gTexture->GetLevelDesc(0,&desc);
w=desc.Width;
Look in the DX help file for more information.
You can easily switch to wireframe using the fill mode render state e.g.
void CVisualisation::SetWireframeMode(bool set)
{
if (set)
gD3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
else
gD3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
}
The viewing frustum is needed in order to do frustum culling of the scene i.e. do not render objects that are outside the view. Note that the correct term is frustum not frustrum.
In Direct3D to find the viewing frustum you need to combine the view matrix and projection matrix and extract the values. The result will be the six planes: left, right, top, bottom, near and far. In order to describe a plane we need a direction normal and offset (distance). So we can define a structure or class for our plane containing these values and a function to normalise them.
struct TFrustumPlane
{
D3DXVECTOR3 m_normal;
float m_distance;
inline void Normalise()
{
float denom = 1 / sqrt((m_normal.x*m_normal.x) + (m_normal.y*m_normal.y) + (m_normal.z*m_normal.z));
m_normal.x = m_normal.x * denom;
m_normal.y = m_normal.y * denom;
m_normal.z = m_normal.z * denom;
m_distance = m_distance * denom;
}
};
We then combine the view and projection matrices and work out the planes, the code below does this. It assumes you have an array for your planes: TFrustumPlane m_frustumPlanes[6]; a view matrix matView and a projection matrix matProj:
// Get combined matrix
D3DXMATRIXA16 matComb;
D3DXMatrixMultiply(&matComb, matView, matProj);
// Left clipping plane
m_frustumPlanes[0].m_normal.x = matComb._14 + matComb._11;
m_frustumPlanes[0].m_normal.y = matComb._24 + matComb._21;
m_frustumPlanes[0].m_normal.z = matComb._34 + matComb._31;
m_frustumPlanes[0].m_distance = matComb._44 + matComb._41;
// Right clipping plane
m_frustumPlanes[1].m_normal.x = matComb._14 - matComb._11;
m_frustumPlanes[1].m_normal.y = matComb._24 - matComb._21;
m_frustumPlanes[1].m_normal.z = matComb._34 - matComb._31;
m_frustumPlanes[1].m_distance = matComb._44 - matComb._41;
// Top clipping plane
m_frustumPlanes[2].m_normal.x = matComb._14 - matComb._12;
m_frustumPlanes[2].m_normal.y = matComb._24 - matComb._22;
m_frustumPlanes[2].m_normal.z = matComb._34 - matComb._32;
m_frustumPlanes[2].m_distance = matComb._44 - matComb._42;
// Bottom clipping plane
m_frustumPlanes[3].m_normal.x = matComb._14 + matComb._12;
m_frustumPlanes[3].m_normal.y = matComb._24 + matComb._22;
m_frustumPlanes[3].m_normal.z = matComb._34 + matComb._32;
m_frustumPlanes[3].m_distance = matComb._44 + matComb._42;
// Near clipping plane
m_frustumPlanes[4].m_normal.x = matComb._13;
m_frustumPlanes[4].m_normal.y = matComb._23;
m_frustumPlanes[4].m_normal.z = matComb._33;
m_frustumPlanes[4].m_distance = matComb._43;
// Far clipping plane
m_frustumPlanes[5].m_normal.x = matComb._14 - matComb._13;
m_frustumPlanes[5].m_normal.y = matComb._24 - matComb._23;
m_frustumPlanes[5].m_normal.z = matComb._34 - matComb._33;
m_frustumPlanes[5].m_distance = matComb._44 - matComb._43;
See the next question on how to use this data for intersection testing against bounding boxes.
There are three possibilities, either the BB is completely outside the frustum, it is completely inside or it intersects. The following code assumes you have created the plane data as described in the previous answer and you pass in a world space AABB (Axis Aligned Bounding Box).
/*****************************************************************************
Desc: Taking an AABB min and max in world space, work out its interaction with the view frustum
0 is outside
1 is partially in
2 is completely within
Note: the viewing frustum must be calculated first
******************************************************************************/
WORD cullAABB(const D3DXVECTOR3 &aabbMin, const D3DXVECTOR3 &aabbMax)
{
bool intersect = FALSE;
WORD result=0;
D3DXVECTOR3 minExtreme,maxExtreme;
for (WORD i=0;i<6;i++)
{
if (m_frustumPlanes[i].m_normal.x <= 0)
{
minExtreme.x = aabbMin.x;
maxExtreme.x = aabbMax.x;
}
else
{
minExtreme.x = aabbMax.x;
maxExtreme.x = aabbMin.x;
}
if (m_frustumPlanes[i].m_normal.y <= 0)
{
minExtreme.y = aabbMin.y;
maxExtreme.y = aabbMax.y;
}
else
{
minExtreme.y = aabbMax.y;
maxExtreme.y = aabbMin.y;
}
if (m_frustumPlanes[i].m_normal.z <= 0)
{
minExtreme.z = aabbMin.z;
maxExtreme.z = aabbMax.z;
}
else
{
minExtreme.z = aabbMax.z;
maxExtreme.z = aabbMin.z;
}
if (m_frustumPlanes[i].DistanceToPoint(minExtreme) > 0)
{
result = 0;
return result;
}
if (m_frustumPlanes[i].DistanceToPoint(maxExtreme) >= 0)
intersect = TRUE;
}
if (intersect)
result = 1;
else
result = 2;
return result;
}
The DistanceToPoint function is simply: D3DXVec3Dot(&m_normal, &pnt) + m_distance;
The vector and matrix types that come with Direct3D are highly optimized. There are many functions that can be performed using them, like dot product, cross product etc. which are optimized to run best on the processor. i.e. if your game runs on a Pentium machine these functions will use code optimized by Intel for top speed but if your game runs on an AMD it uses code optimized for that platform. So it would be hard for you to write a vector structure/class and functions that can outperform the D3DX ones. For true black box code you will require your own vectors for calculations outside of the visualization component. A conversion will then need to take place when passing these into the visualization component.
Look on the Internet, there are plenty of places where you can download models in various formats. The most common format seems to be .3ds which can be loaded into 3DS Max and saved out as a .x file or converted using the converter. A comprehensive list of good sites can be found in the Resources section of this site.
You may want to read colour data from a texture if for example you use a texture to encode level data. e.g. a blue colour means put a wall here, a red means an enemy etc.
Firstly you need to create your image in either bmp or tga format. You then need to load it into your program. You must force it to be 32 bit even if it was not originally. It is important to do this so you know the format of the data and hence how to read it. So for example you could create a 24 bit graphic in your art tool and then on load force it to be 32 bit (Direct3D converts it for you). This is how you do it:
// Load the map texture
HRESULT hr=D3DXCreateTextureFromFileEx(gD3dDevice, filename, 0, 0, 1 ,0 ,D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_NONE ,D3DX_FILTER_NONE ,0 ,NULL ,NULL ,&texture );
Its the extended file load version. The D3DFMT_A8R8G8B8 means whatever the original format it will be converted to 32 bit with 8 bits (one byte) for each colour component. It is put in the managed memory pool so Direct3D handles where to put it and you have no filters applied. If it was successful 'texture' will point to the loaded texture.
Now to get the colour information from the file you must lock the texture:
D3DLOCKED_RECT locked;
HRESULT hr=texture->LockRect(0,&locked,NULL,0);
If that is successful it returns a structure 'locked' that contains info about the texture. You can now obtain a pointer that points to the colour in the top left of the image:
BYTE *bytePointer=(BYTE*)locked.pBits;
Now you have a pointer to all the data, you need to interpret it as colours. Since you know it is 32 bit format you can extract the data. You go along the horizontal (x coordinate) reading the values 4 bytes at a time. One gotcha is that a line of data in memory may be longer than the width of the image - this is due to padding for optimisation purposes. So the memory width of a line may actually be different than the image width. You can find it though by looking at locked.Pitch, so:
for (DWORD y=0;y<surfaceDesc.Height;y++)
{
for (DWORD x=0;x<surfaceDesc.Width;x++)
{
DWORD index=(x*4+(y*(locked.Pitch)));
// Blue
BYTE b=bytePointer[index];
// Green
BYTE g=bytePointer[index+1];
// Red
BYTE r=bytePointer[index+2];
// Alpha
BYTE a=bytePointer[index+3];
}
}
And that's it. Once you have finished you must unlock the texture:
texture->UnlockRect(0);
Note: when you create and manipulate texture resources you can specify memory pool and usage etc. for full details on the options please see the D3D Resources page.
Billboards are 2D shapes rotated to always face the camera. You can see this in some games where trees will look the same whatever angle you view them from. This is because they are being rotated to face you all the time. The advantages of using billboards is that it is only two triangles and a texture rather than the alternative of using a full 3D model with many triangles. The disadvantage is of course that the image is the same wherever you view it from.
To create a billboard you need to create two triangles that make up a square. So there will be 4 vertices, one for each corner of the square. Each vertex should be given a position and a texture co-ordinate. The texture co-ordinates will be 0,0 for the top left vertex and 1,1 for the lower right. So this is all fairly simple, the slightly tricky bit is to render it so that it is always facing the camera. To do that we need to apply a matrix which is the inverse of the current view matrix. We also set the translation component to 0 as we do not want to translate the billboard by the camera position. So in Direct3D we do this:
// Create billboard matrix
D3DXMatrixInverse( &m_matBillboard, NULL, &view );
m_matBillboard._41 = 0.0f;
m_matBillboard._42 = 0.0f;
m_matBillboard._43 = 0.0f;
To render our billboard we simply multiply the billboard matrix by our objects scaling matrix (if there is one) and then assign the objects world position as above:
// Create a matrix to scale our billboard
D3DXMatrixScaling(&temp,scale->x,scale->y,scale->z);
// Multiply this by the billboard matrix
D3DXMATRIX result;
D3DXMatrixMultiply(&result,&matBillboardMatrix,&temp);
// Now factor in the objects world position
result._41 = pos->x;
result._42 = pos->y;
result._43 = pos->z;
// Set the final matrix as the world matrix
gDevice->SetTransform(D3DTS_WORLD, &result )
We can now render as normal e.g. set texture, call DrawPrimitive.
Each DrawIndexedPrimitive call uses just one texture so you could split your 3D object into a number of vertex and index buffers for each texture area. You then just render one after the other switching the texture each time. However a more efficient method is to use just one index buffer and one big vertex buffer. The DrawIndexedPrimitive function allows you to specify a range of triangles to render each time rather than always rendering the whole lot. So you can set a texture, render from a part of the vertex buffer, set another texture and render another part etc. An example will make this clearer:
E.g.. rendering a cube with a different texture on each side.
A cube is made up of 6 sides so we could create a 3D model with two triangles forming a square for each side of the cube. That would mean we would have 6*2=12 triangles in our cube. Since we need 3 indexes per triangle that would give us an index buffer containing 12*3=36 entries. We would need 4 vertices for each side of our cube so we would have 4*6=24 vertices altogether. We could share vertices amongst the sides but that would prevent us from having separate texture co-ordinates per triangle which we will require. If all this sounds confusing I suggest you draw it on paper to see how it works or better still get a box and label the corners (this is what I do).
To summarise: we will have 6 textures, one for each side. We have 12 triangles and 24 vertices. So to draw our cube with different textures on each side we would do a loop like this:
for (int i=0;i<6;i++)
{
gD3dDevice->SetTexture(0,m_texture[i]);
gD3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,i*4,4,i*6,2);
}
The DrawIndexedPrimitive takes the following parameters:
D3DPRIMITIVETYPE Type - the type of storage, normally triangle list
INT BaseVertexIndex - an offset, always set to 0 for our purposes
UINT MinIndex - the first vertex we will be using in this call, since there are 4 vertices per side we do i*4
UINT NumVertices - the number of vertices in this call, this is 4 as there are 4 used per side
UINT StartIndex - the start index of our triangles in the index buffer
UINT PrimitiveCount - the number of triangles to draw, this is 2 as we have 2 per side
Note: you could leave MinIndex at 0 and NumVertices as 24 if you want, which means you can use any vertex in the buffer during this call but it is normally better to give as much information to the driver as you can so it can carry out optimisations etc.
With Direct3D you can create a texture in memory using the CreateTexture call. Make sure you set it to D3DUSAGE_DYNAMIC because you want to write to it and set the memory pool to D3DPOOL_DEFAULT. You can specify the format you want e.g. for a 32 bit texture you would say D3DFMT_A8R8G8B8. There are many other formats available.
Once you have created it you can write to it by locking the texture. This returns you a pointer to the memory where you can write. When writing remember the width of the data in memory will not always equal the width of the image so you must take the stride into account. Once finished writing you can save to disk using: D3DXSaveTextureToFile
When your device is created the presentation interval is set to D3DPRESENT_INTERVAL_DEFAULT. This setting synchronises your frames to to the window rate so there is only one present allowed per frame. This prevents tearing effects (D3DPRESENT_INTERVAL_IMMEDIATE provides unlimited presents). However if you are seeing fluctuations in frame rate you might be better to try D3DPRESENT_INTERVAL_ONE, this uses a higher resolution timer than the default setting which improves the quality of the vsync but takes up a bit more processing time. To change this value you need to alter the D3DPRESENT_PARAMETERS PresentationInterval value.
You are not clearing the back buffer correctly. The Direct3D debug runtime clears the back buffer alternately green or purple so if you see these colours you must not be clearing it yourself.
Yes you can. You can obtain a HDC for normal GDI drawing IDirect3DSurface9::GetDC(), and you can use Windows dialogs to implement user interfaces IDirect3DDevice9::SetDialogBoxMode(). However bear in mind there are a few restrictions - check the DirectX help for details.
There is a really useful D3DX function: D3DXMatrixDecompose that, when passed a transformation matrix, will return scale, rotation and translation values.
Updated: from the April DirectX SDK release Visual Studio 6 is no longer supported.
Are you using Visual Studio 6? If so then you need to use the D3DX library found in the extras download. Ultimately I would move to .net. Also see: DirectX 9.0c and missing dll below.
The resources page lists some useful books and many links to help with Direct3D. There is also a list of generic DirectX questions and answers here: DirectX Q&A
From the February DirectX update the D3DX library has been changed from a .lib into a .dll. This means that if you compile your program using the February SDK (24) or the April SDK (25) and someone runs it who has not got those SDKs they will get this missing DLL error. The obvious solution is to ship the DLL with the .exe but Microsoft do not allow this. Apparently they are working on a fix for this problem. See also DirectX 9.0c.
This is nothing to worry about, if you look closely you will see it is marked as (info) and so is not an error. The reason it appears is that Direct3D tried to create an index buffer in video memory but the video card does not support them. I have yet to find one that does, so it is not a problem.
It is useful to use the Direct3D symbol files for debugging into the code. You can download them or use the Microsoft symbol server. The steps required to get this working are:
I got the above solution originally from a post by Simon O'Conner.
You are hogging all the system resources. It is a good idea to allow other applications time to do some processing when your game is minimised or out of focus. The best way to handle this is to maintain a paused variable. When paused you do not do any rendering and you use the Sleep function to give time over to other applications. In order to determine when your game is not active you need to trap some window API messages:
Pause your game when getting these messages:
You need to resume your game when you get these messages:
Note: there are few other messages that you may consider that I have not mentioned above, these are to do with power management.
Normally your message loop will be as described on this page: D3D Setup where you render whenever there is no message in the queue. When you are paused you should not render but instead do a Sleep. I recommend a Sleep(10) when paused. It is also an idea to do a Sleep(0) when not paused to give over some time to other applications even when your game is active.
Picture in picture is when you have two or more views rendered to the screen e.g. a main view and a box in the corner showing a view from elsewhere. There are two ways you can achieve this with Direct3D, the first is to render the second view to a texture and then render that texture onto a quad on the screen. The second way described here is to use Viewports.
Direct3D has a function SetViewport that allows you to specify just a part of the screen to render to. You call it before starting to render.
You can get the main viewport like this:
gD3dDevice->GetViewport( &m_mainViewport );
then make a copy of this viewport for your picture in picture and change the size and screen position:
// Create a PIP viewport
m_pipViewport=m_mainViewport;
// Make it a set size
m_pipViewport.Width=256;
m_pipViewport.Height=256;
// Position
m_pipViewport.X=64;
m_pipViewport.Y=64;
In your render loop set the main viewport using SetViewport and render as normal. Then set the viewport to the PIP viewport and change camera etc whatever you want to do - and render again. Note you only have to clear the z buffer before rendering this and not the target.
Note that there is a demo application with source code showing off a working Direct3D application here: Cube Demo
On this site:
Other: Programmers Heaven DirectX FAQ, Microsoft DirectX 9 Developer FAQ
See the resources page for Books, Links and Assets