|
In this tutorial I will demonstrate how to do Vertex Processing in Direct3d this will include displaying and rotating the vertexes as well as translation (not coded in this tutorial but I'll explain how in the appropriate section).
First I'll show you the code from the first tutorial written by Vahid Kazemi with the required changes to allow for the creation, rendering and rotating of a simple one-sided (you'll find out why I call it one sided later) triangle.
#include <windows.h>
#include <d3d8.h>
#include <d3dx8.h>
HWND hWnd;
LPDIRECT3D8 pD3D;
LPDIRECT3DDEVICE8 pDevice;
LPDIRECT3DVERTEXBUFFER8 pVB;
struct CUSTOMVERTEX
{
FLOAT x, y, z; // The untransformed position for the vertex
DWORD color; // The vertex color
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
LRESULT CALLBACK WndProc( HWND hWnd , UINT message , WPARAM wParam , LPARAM lParam){
switch(message){
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,message,wParam,lParam);
}
bool CreateSimpWindow(int width, int height){
// Create A Window Class Structure
WNDCLASSEX wc;
wc.cbClsExtra = 0;
wc.cbSize = sizeof(wc);
wc.cbWndExtra = 0;
wc.hbrBackground = NULL;
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hIconSm = NULL;
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "VK3D";
wc.lpszMenuName = NULL;
wc.style = CS_VREDRAW|CS_HREDRAW|CS_OWNDC;
// Register Window Class
RegisterClassEx(&wc);
// Create Window
hWnd = CreateWindowEx(0,
nbsp; "VK3D", "VK3D Direct3D Tutorial 2",
nbsp; WS_OVERLAPPEDWINDOW|WS_VISIBLE, 100,100,width,height,
nbsp; NULL,NULL,wc.hInstance,0);
return true;
}
bool InitD3D(){
if((pD3D = Direct3DCreate8(D3D_SDK_VERSION))==NULL){
return false;
}
D3DDISPLAYMODE d3ddm;
pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
if(FAILED(pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice))){
return false;
}
pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
CUSTOMVERTEX g_Vertices[] =
{
{ 0.0f, 1.0f, 0.0f, 0xffff0000, }, // x, y, z, color
{ 1.0f, -1.0f, 0.0f, 0xff00ff00, },
{ -1.0f, -1.0f, 0.0f, 0xff00ffff, },
};
if( FAILED(pDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &pVB ) ) )
{
return false;
}
VOID* pVertices;
if(FAILED(pVB->Lock(0, sizeof(g_Vertices), (BYTE**)&pVertices, 0)))
return false;
memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );
pVB->Unlock();
return true;
}
void RenderD3DScene(){
pDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1,0);
pDevice->BeginScene();
D3DXMATRIX matView;
D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 3.0f,-10.0f ),
&D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
&D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
pDevice->SetTransform( D3DTS_VIEW, &matView );
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
pDevice->SetTransform( D3DTS_PROJECTION, &matProj );
D3DXMATRIX matWorld;
D3DXMatrixRotationY( &matWorld, timeGetTime()/1000.0f );
pDevice->SetTransform( D3DTS_WORLD, &matWorld );
pDevice->SetStreamSource( 0, pVB, sizeof(CUSTOMVERTEX) );
pDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );
pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
pDevice->EndScene();
pDevice->Present(NULL,NULL,NULL,NULL);
}
void MessageLoop(){
MSG msg;
while(true){
RenderD3DScene();
if(PeekMessage(&msg,hWnd,0,0,PM_REMOVE)){
if(msg.message==WM_QUIT)return;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
INT WINAPI WinMain( HINSTANCE , HINSTANCE , LPSTR , INT ){
if(!CreateSimpWindow(400, 400))return 1;
if(!InitD3D())return 2;
MessageLoop();
return 0;
} |
The first difference you should not is the following additional header :
This header file is used for the D3DXMATRIX and anything else prefixed by D3DX and is the C++ version of the D3DMATRIX structure it allows for operator overloading and other C++ specific functionality as well as providing helper functions to simplify rotation, translation, etc.
| LPDIRECT3DVERTEXBUFFER8 pVB; |
The second difference is the pointer to the vertex buffer this is a buffer that will be filled with vertex information and then written out to the scene after doing any transformation (rotation, scaling, translation, etc) that you choose to perform on it.
struct CUSTOMVERTEX {
FLOAT x, y, z; // The untransformed position for the vertex
DWORD color; // The vertex color
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE) |
The third change is the new structure called CUSTOMVERTEX it is a custom vertex type which must be compatible with the vertex buffers custom vertex type parameter which is defined immediately after it as an untransformed x, y, z and a diffuse colour.
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; |
First we make it empty by calling the ZeroMemory macro (this macro simply fill all of allocated memory with zero values), then we set buffer depth color to the same as the desktop depth color, and we specify that program will run in a window, and finally we specify D3D_SWAPEFFECT_DISCARD which makes Direct3D to fill buffer with random values after beginning to draw (This option usually used for debug purposes to ensure were are properly clearing the screen). We did all of this to prepare d3dpp for passing to the IDirect3D8:CreateDevice function as the fifth argument:
pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
pDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); |
The next set on changes is to the render state to define the following: The culling mode in this case we define it as NONE so we can see both sides of the triangle otherwise we would only see either the front or the back depending on whether we are culling clockwise or anti-clock wise these define front and back. If you notice your 3d figures have holes in them or textures are incorrectly displaying you need to change the order of those vertexes.
The lighting here we define whether or not lighting and shadowing should be done in this case we say we don't want Direct3d lighting just use the base colors.
Then we enable the Z buffer.
There are many other possible render state changes some more useful then others.
CUSTOMVERTEX g_Vertices[] =
{
{ 0.0f, 1.0f, 0.0f, 0xffff0000, }, // x, y, z, color
{ 1.0f, -1.0f, 0.0f, 0xff00ff00, },
{ -1.0f, -1.0f, 0.0f, 0xff00ffff, },
}; |
In the above code we create a triangle centered on the origin (this is important if the vertexes are centered elsewhere it will not rotate correctly if you need it somewhere else translate it after rotating it) using our custom vertex format.
if( FAILED( pDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),0, D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT, &pVB ) ) ) {
return false;
} |
In the above code we define the size in this case 3 of our custom vertexes, format our custom vertex format, the usage restrictions which we don't need yet and the memory pool to put it in we use the default value for that as that puts it in video memory however if your using a lot of vertexes you might try system or managed modes otherwise you'll overload the video card.
VOID* pVertices;
if(FAILED(pVB->Lock(0, sizeof(g_Vertices), (BYTE**)&pVertices, 0)))
return false;
memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );
pVB->Unlock(); |
In this code we lock the vertex buffer and copy the vertexes into the buffer and then unlock it.
This code sets that we are doing operation on the view matrixes and on the 3d scene all examples from now on will include it.
D3DXMATRIX matView; D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 3.0f,-10.0f ),
&D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
&D3DXVECTOR3( 0.0f, 1.0f, 0.0f ));
pDevice->SetTransform( D3DTS_VIEW, &matView ); |
Here we set the view matrix of the 3d program to be ten units back and 3 up, looking at the origin and up to be the y axis. If you want a mobile camera you would modify this section to use variables that would be changed when you pushed the button for example if the second one used the variables or you could do transformation like the ones below on them.
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
pDevice->SetTransform( D3DTS_PROJECTION, &matProj ); |
Here we set the projection matrix this is to be used in this case a Left-Handed field of view based one where the fov is pi/4 and the aspect ratio is 1.0 the near view plane z is 1 and the far view plane z is 100.
D3DXMATRIX matWorld;
D3DXMatrixRotationY( &matWorld, timeGetTime()/1000.0f );
pDevice->SetTransform( D3DTS_WORLD, &matWorld ); |
Then we create the world matrix that defines what transformations will be performed on all subsequently drawn vertexes. In this case we are rotating the object around the Y axis.
pDevice->SetStreamSource( 0, pVB, sizeof(CUSTOMVERTEX) );
pDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );
pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 ); |
This code sets up the source of the vertexes, the format they were stored in and draws them to the screen assuming that they are stored as a list of triangles starting from 0 and there being only 1 triangle. There are other options here that can be useful in certain circumstances for efficiency or for reasons of simplicity. They will be covered later.
This code sets that we are finished doing operation on the view matrixes and on the 3d scene all examples from now on will include it. Okay if you've made the changes to the first tutorial correctly you should have a spinning triangle. I would recommend changing values like the colour, the positions of the vertexes, the number of vertexes (you could make a square or a cube), the cull mode, etc. This will help you understand what everything does. If you have any comment or need any information email me by clicking my name below or contact me at the yahoo group real-game-programming.
|