Code:
#include "FPCamera.h"
CFPCamera::CFPCamera(D3DXVECTOR3 startPos) : m_position(startPos),m_yaw(0),m_pitch(0),m_roll(0)
{
// Start with an orthagonal camera axis
m_up=D3DXVECTOR3(0.0f,1.0f,0.0f);
m_look=D3DXVECTOR3(0.0f,0.0f,1.0f);
m_right=D3DXVECTOR3(1.0f,0.0f,0.0f);
}
CFPCamera::~CFPCamera(void)
{
}
void CFPCamera::CalculateViewMatrix(D3DXMATRIX *viewMatrix)
{
/* Start with our camera axis pointing down z
An alternative method is to just keep adjusting our axis but if we do that the
axis can start to lose its orthogonal shape (due to floating point innacuracies).
This could be solved by rebuilding the orthogonal shape each time with the following:
1. normalising the look vector
2. creating the up vector from the cross product of the look and the right
3. normalising up
4. creating the right vector from the cross product of the look and the up
5. normalising right
*/
m_up=D3DXVECTOR3(0.0f,1.0f,0.0f);
m_look=D3DXVECTOR3(0.0f,0.0f,1.0f);
m_right=D3DXVECTOR3(1.0f,0.0f,0.0f);
// Yaw is rotation around the y axis (m_up)
// Create a matrix that can carry out this rotation
D3DXMATRIX yawMatrix;
D3DXMatrixRotationAxis(&yawMatrix, &m_up, m_yaw);
// To apply yaw we rotate the m_look & m_right vectors about the m_up vector (using our yaw matrix)
D3DXVec3TransformCoord(&m_look, &m_look, &yawMatrix);
D3DXVec3TransformCoord(&m_right, &m_right, &yawMatrix);
// Pitch is rotation around the x axis (m_right)
// Create a matrix that can carry out this rotation
D3DXMATRIX pitchMatrix;
D3DXMatrixRotationAxis(&pitchMatrix, &m_right, m_pitch);
// To apply pitch we rotate the m_look and m_up vectors about the m_right vector (using our pitch matrix)
D3DXVec3TransformCoord(&m_look, &m_look, &pitchMatrix);
D3DXVec3TransformCoord(&m_up, &m_up, &pitchMatrix);
// Roll is rotation around the z axis (m_look)
// Create a matrix that can carry out this rotation
D3DXMATRIX rollMatrix;
D3DXMatrixRotationAxis(&rollMatrix, &m_look, m_roll);
// To apply roll we rotate up and right about the look vector (using our roll matrix)
// Note: roll only really applies for things like aircraft unless you are implementing lean
D3DXVec3TransformCoord(&m_right, &m_right, &rollMatrix);
D3DXVec3TransformCoord(&m_up, &m_up, &rollMatrix);
// Build the view matrix from the transformed camera axis
D3DXMatrixIdentity(viewMatrix);
viewMatrix->_11 = m_right.x; viewMatrix->_12 = m_up.x; viewMatrix->_13 = m_look.x;
viewMatrix->_21 = m_right.y; viewMatrix->_22 = m_up.y; viewMatrix->_23 = m_look.y;
viewMatrix->_31 = m_right.z; viewMatrix->_32 = m_up.z; viewMatrix->_33 = m_look.z;
viewMatrix->_41 = - D3DXVec3Dot( &m_position,&m_right);
viewMatrix->_42 = - D3DXVec3Dot( &m_position,&m_up);
viewMatrix->_43 = - D3DXVec3Dot( &m_position,&m_look);
}
// Yaw - rotation around y axis
void CFPCamera::Yaw(float amount)
{
m_yaw+=amount;
m_yaw=RestrictAngleTo360Range(m_yaw);
}
// Pitch - rotation around x axis
void CFPCamera::Pitch(float amount)
{
m_pitch+=amount;
m_pitch=RestrictAngleTo360Range(m_pitch);
}
// Roll - rotation around z axis
// Note: normally only used for aircraft type cameras rather than land based ones
void CFPCamera::Roll(float amount)
{
m_roll+=amount;
m_roll=RestrictAngleTo360Range(m_roll);
}
// Keep the angle in the range 0 to 360 (2*PI)
float CFPCamera::RestrictAngleTo360Range(float angle) const
{
while(angle>2*D3DX_PI)
angle-=2*D3DX_PI;
while(angle<0)
angle+=2*D3DX_PI;
return angle;
}
Main.cpp
Code:
/*
Demo of a first person Direct3D camera implementation
*/
#define VC_EXTRALEAN
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <string>
#include <sstream>
#include "FPCamera.h"
// Global Direct3D objects
IDirect3D9* gD3dObject=NULL;
IDirect3DDevice9* gD3dDevice=NULL;
ID3DXMesh * gTeapotMesh=NULL;
ID3DXFont * gFont=NULL;
// Direct3D materials
D3DMATERIAL9 gRedMaterial;
D3DMATERIAL9 gGreenMaterial;
D3DMATERIAL9 gBlueMaterial;
D3DMATERIAL9 gYellowMaterial;
// Our camera class
CFPCamera *gCamera=NULL;
// Some program constants
const float kCameraMovementSpeed=0.4f;
const float kCameraRotationSpeed=0.01f;
const int kFontSize=14;
// Forward declarations of functions in this source file
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
bool SetupDirect3D(HWND hWnd);
void UpdateGame();
// Helper funtion to convert from rads to degrees
int ToDegrees(float rads)
{
return (int)(180.0f/D3DX_PI*rads);
}
// Helper template function to convert values to a string format
// See the String Handling notes in the C++ section of the site
template <class T>
std::string ToString(const T & t)
{
std::ostringstream oss;
oss.precision(2);
oss.setf(std::ios_base::fixed);
oss << t;
return oss.str();
}
// Program entry point
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style= CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc= (WNDPROC)WndProc;
wcex.cbClsExtra= 0;
wcex.cbWndExtra= 0;
wcex.hInstance= hInstance;
wcex.hIcon= 0;
wcex.hCursor= LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName= 0;
wcex.lpszClassName= "MyWindowClass";
wcex.hIconSm= 0;
RegisterClassEx(&wcex);
HWND hWnd = CreateWindow("MyWindowClass", "Camera Demo", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 808, 634, NULL, NULL, hInstance, NULL);
if (!SetupDirect3D(hWnd))
return 0;
// Create our camera and set its initial position
gCamera=new CFPCamera(D3DXVECTOR3(0,0,-12.0f));
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
// When there are no messages for our window update or demo
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
UpdateGame();
// Dont be mean - let other apps have a wee bit of time:
Sleep(0);
}
}
// Clean up and exit
delete gCamera;
gFont->Release();
gTeapotMesh->Release();
gD3dDevice->Release();
gD3dObject->Release();
return (int)msg.wParam;
}
// Callback function for our window
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch(wParam)
{
// Translations
case 'W':
case VK_UP:
gCamera->MoveForward(kCameraMovementSpeed);
break;
case 'S':
case VK_DOWN:
gCamera->MoveForward(-kCameraMovementSpeed);
break;
case 'D':
case VK_RIGHT:
gCamera->MoveRight(kCameraMovementSpeed);
break;
case 'A':
case VK_LEFT:
gCamera->MoveRight(-kCameraMovementSpeed);
break;
case VK_HOME:
gCamera->MoveUp(kCameraMovementSpeed);
break;
case VK_END:
gCamera->MoveUp(-kCameraMovementSpeed);
break;
// Rotations
case VK_NUMPAD4:
gCamera->Yaw(-kCameraRotationSpeed);
break;
case VK_NUMPAD6:
gCamera->Yaw(kCameraRotationSpeed);
break;
case VK_NUMPAD8:
gCamera->Pitch(-kCameraRotationSpeed);
break;
case VK_NUMPAD2:
gCamera->Pitch(kCameraRotationSpeed);
break;
case VK_NUMPAD7:
gCamera->Roll(-kCameraRotationSpeed);
break;
case VK_NUMPAD9:
gCamera->Roll(kCameraRotationSpeed);
break;
default:
break;
}
break;
default:
// We do not want to handle this message so pass back to Windows to handle it in a default way
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Create our Direct3D objects etc.
bool SetupDirect3D(HWND hWnd)
{
gD3dObject=Direct3DCreate9(D3D_SDK_VERSION);
if (!gD3dObject)
return 0;
D3DPRESENT_PARAMETERS presParams;
ZeroMemory(&presParams,sizeof(presParams));
presParams.Windowed=TRUE;
presParams.SwapEffect=D3DSWAPEFFECT_DISCARD;
presParams.BackBufferFormat=D3DFMT_UNKNOWN;
presParams.PresentationInterval=D3DPRESENT_INTERVAL_ONE;
presParams.EnableAutoDepthStencil = TRUE;
presParams.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT hr=gD3dObject->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING, &presParams, &gD3dDevice);
if (FAILED(hr))
return false;
// Turn on the zbuffer
gD3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
// Create a teapot to view
hr=D3DXCreateTeapot(gD3dDevice,&gTeapotMesh,NULL);
if (FAILED(hr))
return false;
// The teapot has position and normal per vertex so we will need to turn on the lights
gD3dDevice->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_XRGB(20,20,20));
gD3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE);
// Fill in a light structure defining our light
D3DLIGHT9 light;
ZeroMemory( &light, sizeof(D3DLIGHT9) );
light.Type = D3DLIGHT_DIRECTIONAL;
light.Diffuse.r = 1.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;
light.Diffuse.a = 1.0f;
light.Range = 1000.0f;
// Create a direction for our light - it must be normalized
D3DXVECTOR3 vecDir;
vecDir = D3DXVECTOR3(0.0f,-0.3f,0.7f);
D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDir );
// Tell the device about the light and turn it on
gD3dDevice->SetLight( 0, &light );
gD3dDevice->LightEnable( 0, TRUE );
// Create three materials so we can draw the teapot three times
// in red, green and blue
ZeroMemory( &gRedMaterial, sizeof(D3DMATERIAL9) );
gRedMaterial.Diffuse.r = gRedMaterial.Ambient.r = 1.0f;
gRedMaterial.Diffuse.g = gRedMaterial.Ambient.g = 0;
gRedMaterial.Diffuse.b = gRedMaterial.Ambient.b = 0;
gRedMaterial.Diffuse.a = gRedMaterial.Ambient.a = 1.0f;
ZeroMemory( &gGreenMaterial, sizeof(D3DMATERIAL9) );
gGreenMaterial.Diffuse.r = gGreenMaterial.Ambient.r = 0;
gGreenMaterial.Diffuse.g = gGreenMaterial.Ambient.g = 1.0f;
gGreenMaterial.Diffuse.b = gGreenMaterial.Ambient.b = 0;
gGreenMaterial.Diffuse.a = gGreenMaterial.Ambient.a = 1.0f;
ZeroMemory( &gBlueMaterial, sizeof(D3DMATERIAL9) );
gBlueMaterial.Diffuse.r = gBlueMaterial.Ambient.r = 0;
gBlueMaterial.Diffuse.g = gBlueMaterial.Ambient.g = 0;
gBlueMaterial.Diffuse.b = gBlueMaterial.Ambient.b = 1.0f;
gBlueMaterial.Diffuse.a = gBlueMaterial.Ambient.a = 1.0f;
ZeroMemory( &gYellowMaterial, sizeof(D3DMATERIAL9) );
gYellowMaterial.Diffuse.r = gYellowMaterial.Ambient.r = 1.0f;
gYellowMaterial.Diffuse.g = gYellowMaterial.Ambient.g = 1.0f;
gYellowMaterial.Diffuse.b = gYellowMaterial.Ambient.b = 0.0f;
gYellowMaterial.Diffuse.a = gYellowMaterial.Ambient.a = 1.0f;
// Set up matrix
RECT rct;
GetClientRect(hWnd,&rct);
D3DXMATRIX matProj;
float aspect = (rct.right-rct.left) / (float)(rct.bottom-rct.top);
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, aspect, 1.0f, 100.0f );
gD3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
// Create a D3DX font object
hr=D3DXCreateFont( gD3dDevice, kFontSize, 0, FW_BOLD, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Arial"), &gFont );
if (FAILED(hr))
return false;
return true;
}
const float kGapX=3.0f;
const float kGapY=2.0f;
const int kNumTeapotsPerAxis=16;
// Called each update we clear the scene, draw all our teapots, put up some text and present it
void UpdateGame()
{
gD3dDevice->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0),1.0f,0);
gD3dDevice->BeginScene();
// Get and set the view matrix
D3DXMATRIX viewMatrix;
gCamera->CalculateViewMatrix(&viewMatrix);
gD3dDevice->SetTransform( D3DTS_VIEW, &viewMatrix );
// Represent the y axis with red teapots
gD3dDevice->SetMaterial( &gRedMaterial );
for (int i=0;i<kNumTeapotsPerAxis;i++)
{
D3DXMATRIX worldMatrix;
D3DXMatrixTranslation(&worldMatrix,0.0f,kGapY+kGapY*i,0.0f);
gD3dDevice->SetTransform( D3DTS_WORLD, &worldMatrix );
gTeapotMesh->DrawSubset(0);
}
// Represent the x axis with blue teapots
gD3dDevice->SetMaterial( &gBlueMaterial );
for (int i=0;i<kNumTeapotsPerAxis;i++)
{
D3DXMATRIX worldMatrix;
D3DXMatrixTranslation(&worldMatrix,kGapX+kGapX*i,0.0f,0.0f);
gD3dDevice->SetTransform( D3DTS_WORLD, &worldMatrix );
gTeapotMesh->DrawSubset(0);
}
// Represent the z axis with green teapots
gD3dDevice->SetMaterial( &gGreenMaterial );
for (int i=0;i<kNumTeapotsPerAxis;i++)
{
D3DXMATRIX worldMatrix;
D3DXMatrixTranslation(&worldMatrix,0.0f,0.0f,kGapX+kGapX*i);
gD3dDevice->SetTransform( D3DTS_WORLD, &worldMatrix );
gTeapotMesh->DrawSubset(0);
}
// Finally put a yellow teapot in the middle of the axis and rotate it
static float rotation=0;
rotation+=0.1f;
D3DXMATRIX worldMatrix;
D3DXMatrixRotationY(&worldMatrix,rotation);
gD3dDevice->SetTransform( D3DTS_WORLD, &worldMatrix );
gD3dDevice->SetMaterial( &gYellowMaterial );
gTeapotMesh->DrawSubset(0);
// Draw some text showing the controls and current angles and position
D3DCOLOR fontColor = D3DCOLOR_XRGB(255,255,255);
RECT rct;
rct.left=kFontSize;
rct.right=800;
rct.top=kFontSize;
rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, "Controls:", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, "Left and right arrows: translate camera left and right", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, "Up and down arrows: translate camera forward and back", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, "Home and End keys: translate camera up and down", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, "Numpad 4 and 6: yaw", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, "Numpad 8 and 2: pitch", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, "Numpad 7 and 9: roll", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
// Current values
gFont->DrawText(NULL, "Current Values", -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
std::string posStr="Position x:"+ToString(gCamera->GetPosition().x) +
" y:" +ToString(gCamera->GetPosition().y)+
" z:" +ToString(gCamera->GetPosition().z);
gFont->DrawText(NULL, posStr.c_str(), -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
std::string rotYawStr="Yaw - rotation around the y axis: "+ToString(gCamera->GetYaw())+
" Radians "+ToString(ToDegrees(gCamera->GetYaw()))+" Degrees";
std::string rotPitchStr="Pitch - rotation around the x axis: " +ToString(gCamera->GetPitch())+
" Radians "+ToString(ToDegrees(gCamera->GetPitch()))+" Degrees";
std::string rotRollStr="Roll - rotation around the z axis: " +ToString(gCamera->GetRoll())+
" Radians "+ToString(ToDegrees(gCamera->GetRoll()))+" Degrees";
gFont->DrawText(NULL, rotYawStr.c_str(), -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, rotPitchStr.c_str(), -1, &rct, 0, fontColor );
rct.top+=kFontSize;rct.bottom=rct.top+kFontSize;
gFont->DrawText(NULL, rotRollStr.c_str(), -1, &rct, 0, fontColor );
gD3dDevice->EndScene();
gD3dDevice->Present( NULL, NULL, NULL, NULL );
}
if u straight compile, u get this