Page 1 of 1

Intro to Matrices in DirectX Camera, 3d location manipulation, rotation

#1 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3102
  • View blog
  • Posts: 19,142
  • Joined: 14-September 07

Posted 01 April 2008 - 06:27 AM

In the vertices tutorial we played around with 2D sets of points that when defined in a custom structure, create basic shapes or designs. In the 3D sense we will be doing the same thing, but in this case we will be playing around with matrixes. Each float number represents a spot in space that can be combined with other plots to form 3D shapes such as spheres, cubes, or pretty much anything you can imagine.

This tutorial assumes you know some about Windows API, directX, and C++. Let's dive in!

Like previous sections we import the needed files and libraries (directX files must be linked or pragma'ed in like below and define what a key up and key down is so the ESC key can exit our program:

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution and keyboard macros
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library files
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")



Now that is out of the way I will reiterate the basics of the Windows program here in code so that we can move on to the purpose of this tutorial:

We define pointers to the directX object so we can manipulate it as we see fit:
// global declarations
LPDIRECT3D9 d3d;	// the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;	// the pointer to the device class
LPDIRECT3DVERTEXBUFFER9 t_buffer = NULL;	// the pointer to the vertex buffer

// function prototypes
void initD3D(HWND hWnd);	// sets up and initializes Direct3D
void render_frame(void);	// renders a single frame
void cleanD3D(void);	// closes Direct3D and releases memory
void init_graphics(void);	// 3D declarations



Remember that we had a vertex buffer for the vertices as well? DirectX has to have a place to hold all of the coordinate data before drawing it to the screen so we create a buffer for that (Again, most everything in directX needs a buffer for something). Also included are the function prototypes we will be using in this segment of code. They are really self explanatory: one sets up all the directX stuff we need, the other is our render frame every tick method and the last one declares all of our graphics we need.

Here is the main loop from the entry of a Windows program. Note it calls the init function then goes into the render frame method every cycle:

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
				   HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine,
				   int nCmdShow)
{
	HWND hWnd;
	WNDCLASSEX wc;

	ZeroMemory(&wc, sizeof(WNDCLASSEX));

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = (WNDPROC)WindowProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	// wc.hbrBackground = (HBRUSH)COLOR_WINDOW;	// not needed any more
	wc.lpszClassName = "WClass";

	RegisterClassEx(&wc);

	hWnd = CreateWindowEx(NULL, "WClass", "Direct3D Program",
						  WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
						  NULL, NULL, hInstance, NULL);

	  ShowWindow(hWnd, nCmdShow);

	// set up and initialize Direct3D
	initD3D(hWnd);

	// enter the main loop:

	MSG msg;

	while(TRUE)
	{
		DWORD starting_point = GetTickCount();

		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		render_frame();

		// check the 'escape' key
		if(KEY_DOWN(VK_ESCAPE))
			PostMessage(hWnd, WM_DESTROY, 0, 0);

		while ((GetTickCount() - starting_point) < 25);
	}

	// clean up DirectX
	cleanD3D();

	return msg.wParam;
}

// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_DESTROY:
			{
				PostQuitMessage(0);
				return 0;
			} break;
	}

	return DefWindowProc (hWnd, message, wParam, lParam);
}



Basic WinAPI crap that I don't particularly care for since it detracts from our mission. Nevertheless we now have all the frame work we need to do much anything with directX. (People who have read the previous tutorials will notice the lack of all the extra files, i thought it would be best to cut down on the OOP of the file segregation and focus on the code for now).

In order to define our points and matrixes we must first define our own custom vertexs and color schemes:

struct CUSTOMVERTEX {FLOAT X, Y, Z; DWORD COLOR;};
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)



DirectX sends coordinates through its pipeline to transform 2D points into a 3D representation of the object.

We must now define our data and initlize directX for this program's use:

// this function initializes Direct3D
void initD3D(HWND hWnd)
{
	d3d = Direct3DCreate9(D3D_SDK_VERSION);

	D3DPRESENT_PARAMETERS d3dpp;

	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = FALSE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth = SCREEN_WIDTH;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;
	d3dpp.EnableAutoDepthStencil = TRUE;  
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;	

	// create a device class 
	d3d->CreateDevice(D3DADAPTER_DEFAULT,
					  D3DDEVTYPE_HAL,
					  hWnd,
					  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
					  &d3dpp,
					  &d3ddev);

	init_graphics();	// call the function to initialize the triangle

	d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);	// turn off the 3D lighting
	d3ddev->SetRenderState(D3DRS_ZENABLE, FALSE); // turn on the z-buffer

	return;
}



As you can see the pointer is utilized and all major things we need for this code is set up here. Initialization is one of my favorite words :)

The Z-buffer is the equivalent of a depth reader. Just like with sprites with a Z-order, the Z-buffer tells directX how objects relate to each other in 3D space. It allows objects top rotate around another, behind, in front, etc... (Since we are only drawing one triangle the Z-Buffer will be set to FALSE, but its there in case you want/need it).

Before looking at the render_frame part that does all the cool work let us look at how Direct3D cleans up after itself (with our help of course!) and the init_graphics which define our matrixes we are working with:

// this is the function that cleans up Direct3D 
void cleanD3D(void)
{
	  t_buffer->Release();	// close and release the vertex buffer
	d3ddev->Release();	// close and release the 3D device
	d3d->Release();	// close and release Direct3D

	return;
}


// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
	// create the vertices using the CUSTOMVERTEX struct
	CUSTOMVERTEX t_vert[] =
	{
		{ 2.5f, -3.0f, 0.0f, D3DCOLOR_XRGB(100, 0, 0), },
		{ 0.0f, 3.0f, 0.0f, D3DCOLOR_XRGB(0, 0, 100), },
		{ -2.5f, -3.0f, 0.0f, D3DCOLOR_XRGB(0, 100, 0), },
	};

	// create a vertex buffer interface called t_buffer
	d3ddev->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),
							   0,
							   CUSTOMFVF,
							   D3DPOOL_MANAGED,
							   &t_buffer,
							   NULL);

	VOID* pVoid;	// a void pointer

	// lock t_buffer and load the vertices into it
	t_buffer->Lock(0, 0, (void**)&pVoid, 0);
	memcpy(pVoid, t_vert, sizeof(t_vert));
	t_buffer->Unlock();

	return;
}



All allocated memory items must be freed to avoid a memory leak and the init graphics set up triangles for use to manipulate in 3D space :)

Notice that we multiply the size of the custom vertex by '3'--that's how we know we are going to get a triangle (triangles have 3 sides if you haven't gotten it by now). You can play around with the colors, I made them darker cause i'm tired of seeing the same three prong colored triangle all the time :)

Here we are at the function of the hour---render_frame()! We set up the matrix(es). Now what we haven't discussed is the various things matrixes can do. There is the camera (how you view the 3D world), the rotation, and various transformations. We'll only worry about the camera and rotation in this tutorial.

// single frame render function
void render_frame(void)
{
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

	d3ddev->BeginScene();

	// select which vertex format we are using
	d3ddev->SetFVF(CUSTOMFVF);

	// SET UP THE PIPELINE

	D3DXMATRIX matRotateY;	// a matrix to store the rotation information

	static float index = 0.0f; index+=0.05f;	// an ever-increasing float value (rotation)

	// build a matrix to rotate the model 
	D3DXMatrixRotationY(&matRotateY, index);

	// tell Direct3D about our matrix
	d3ddev->SetTransform(D3DTS_WORLD, &matRotateY);

	D3DXMATRIX matView;	// the view transform matrix

	D3DXMatrixLookAtLH(&matView,
					   &D3DXVECTOR3 (0.0f, 0.0f, 10.0f),	// the camera position
					   &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),	// the look-at position
					   &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));	// the up direction

	d3ddev->SetTransform(D3DTS_VIEW, &matView);	// set the view transform to matView

	D3DXMATRIX matProjection;	 // the projection transform matrix

	D3DXMatrixPerspectiveFovLH(&matProjection,
							   D3DXToRadian(45),	// the horizontal field of view
							   (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
							   1.0f,	// the near view-plane
							   100.0f);	// the far view-plane

	d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);	// set the projection

	// select the vertex buffer to display
	d3ddev->SetStreamSource(0, t_buffer, 0, sizeof(CUSTOMVERTEX));

	// copy the vertex buffer to the back buffer
	d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

	d3ddev->EndScene();

	d3ddev->Present(NULL, NULL, NULL, NULL);

	return;
}



There is a matrix to hold and store data about EVERYTHING! Camera position, look at position, how fast to rotate the triangle, etc... The 3D Math with this can get complicated, but this shows a neat example without too much linear algebra/calculus. Hope you learned a bit about matrixes from this tutorial. I'll do meshes next hopefully. I'm still working on getting them to load correctly (to compile this sample put all the code into one file, or more if you prefer).

Remember to have the DirectX SDK installed and have all the appropriate files linked or pragma'ed in. Happy Coding!

Is This A Good Question/Topic? 0
  • +

Page 1 of 1