الشبكة العربية لمطوري الألعاب

مبتدئ  Mahmoud Yassin مشاركة 1

الموضوع : Simple Camera Class
المستوى : beginner-intermediate
الادوات المستخدمة: C++\DirectX

السلام عليكم
سوف اقدم فى هذا الموضوع شرح مبسط عن كيفية عمل كاميرا تحركها بالضغط على لوحة المفاتيح
اود القول انه لا يوجد شىء اسمه الكاميرا فى عالم ال 3D ولكنه تعبير افتراضى وغالبا مايطلق عليها اسم Virtual

camera
ويمكن الحصول على هذا التأثير باستخدام ما يعرف بال Transformations
وهناك ثلاث انواع من ال Transformations
1- World Transformation وهى تستخدم لوضع الاجسام فى اماكن محدده فى الفضاء الثلاثى مثلا اذا كان

هناك طاولة فى لعبة فان هذه الطاولة يكون اها المكان الخاص بها بالنسبة الى نقطة الاصل(0,0,0) وليكن هذا المكان

(12,20و34).
2- View Transformastion وهى التى تستخدم لعمل تأثير الكاميرا والغرض منها هو تغيير المنظور للاجسام

ولنأخذ مثال الطاولة ,فإذا نظرنا الى الطاولة من المكان (0,0,0) بالتأكيد سوف نحصل على منظور او رؤية او View

غير التى نحصل عليها اذا نظرنا الى نفس الطاولة من مكان اخر ولنقل (50و12و75) .
3- Projection Transformation أو الاسقاط, وهى تعتبر المرحلة الاخيرة من ال Geometry

pipeline والتى يتم فيها تحويل الاجسام ال 3D لكى ترسم على الشاشة ال 2D بشكل توحى وكأنها 3D .

والسؤال الان كيف يتم تمثيل هذه التحويلات أو ال Transformations ؟
بكل بساطة يتم ذلك بإستخدام المصفوفات او ال Matrix
فكل واحدة من هذه ال Transformations لها Matrix خاصة بها نقوم بتكوينها وعند التطبيق نقوم بكل بساطة

بتقديمها لل D3DDevice وذلك باستخدام الدالة
SetTransform(Transform type,relevant matrix);//ok

ولكن كيف نكون هذه المصفوفات؟
هذه المضفوفات يكون حجمها size 4x4
والنوعين الثانى والثالث من هذه التحويلات يتطلب ان تكون المصفوفة فى شكل معين
فمثلا ال View Matrix يجب ان تكون فى صورة

 xaxis.x                yaxis.x                zaxis.x               0
 xaxis.y                yaxis.y                zaxis.y               0
 xaxis.z                yaxis.z                zaxis.z               0
-dot(xaxis, eye)       -dot(yaxis, eye)       -dot(zaxis, eye)       1
وحيث
   zaxis = normal(At - Eye)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)



وبالطبع هذه المصفوفة لها اثبات موجود فى كل كتب ال Computer Graphics

وكذلك ال Projection Matrix لها الشكل الخاص بها

اما ال World matrix فهى بكل بساطة نتاج لعمليات ال

Translation,Rotation,Scaling وكل منها لها ال functions الخاصة بها
يعنى لو أردنا أن نعمل Rotation لجسم ما حول محور ال X نستخدم الدالة
D3DXMatrixRotationX(OutputMatrix,Rotation Angle);

ولكن هذا لم يجيب على السؤال, كيف نكون هذه ال matrices ؟ وأقصد ال View و ال Projection
هناك طريقتين لعمل هذا :-
1- ان تقوم انت بتكوين عناصر المصفوفة بنفسك, وهذا ما سوف نستخدمه فى تكوين ال View
2- ان تستخدم الدوال المساعدة او ال Helper Functions وهو أسهل بكل تأكيد

وكل هذا تم استخدامه فى البرنامج المدمج بالاسفل
ويمكنك تنزيل ال Workspace + Mesh Model من خلال هذا الرابط

http://rapidshare.de/files/36905094/SimpleCamera.rar.html

علما بانى قمت بعمل ال Compilation على Visual C++ 2005 Express Edition

وان شاء الله أكمل باقى الموضوع فى المرة القادمة.


DirectX Developer

Software Developer

مبتدئ  Mahmoud Yassin مشاركة 2

وهذا هو الكود لمن لم يستطع التنزيل من الرابيدشير







//===========================
//------------------------------------------------------
//	A simple camera class by : Mahmoud Yassin	(14 Oct,2006)
//   Control Keys: Up         - move forward			
//                 Down       - move backward			
//                 Left       - camera strafes left		
//                 Right      - camera strafes Right	
//                 +		  - move to the right
//	       -		  - move to the left 
//                 *                   - rotate up
//	      /		  - rotate down
//                 Home       - camera moves up
//                 End        - camera moves down
//----------------------------------------------------------
#include 
#include 
#include 
#include 

HWND                    g_hWnd           = NULL;
LPDIRECT3D9             g_pD3D           = NULL;
LPDIRECT3DDEVICE9       g_pd3dDevice     = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer  = NULL;
LPD3DXMESH              g_pMesh          = NULL;
D3DMATERIAL9           *g_pMeshMaterials = NULL;
LPDIRECT3DTEXTURE9     *g_pMeshTextures  = NULL;
unsigned long           g_dwNumMaterials = 0L;

float  g_fElpasedTime;
double g_dCurTime;
double g_dLastTime;
float  g_fMoveSpeed = 10.0f;
////////////////////////////////////
// prototypes
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
				   LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void init(void);
void shutDown(void);
void render(void);
void getUserInput(void);
//////////////////////////////////
// class for simple camera manipulation
class SimpleCamera
{
private:
	LPDIRECT3DDEVICE9       m_pd3dDevice;

	D3DXMATRIX				matXRotation,	//rotate about X-Axis (Up-Down)
							matYRotation ;	//rotate about Y-Axis (Left-Right)

	D3DXVECTOR3				m_vEye;     // Eye Position
	D3DXVECTOR3				m_vLook;    // Look Vector
	D3DXVECTOR3				m_vUp;      // Up Vector
	D3DXVECTOR3				m_vRight;   // Right Vector

	D3DXMATRIX				matProj;	//projection matrix
	D3DXMATRIX				view;		// view matrix    

public:
	SimpleCamera(IDirect3DDevice9 * g_pd3dDevice)
	{
		m_pd3dDevice = g_pd3dDevice;

		// set the default camera position
		m_vEye=D3DXVECTOR3(0.0f, 2.0f, -10.0f);    // Eye Position
		m_vLook=D3DXVECTOR3(0.0f, 0.0f, 5.0f);  // Look Vector

		//up vector is parallel to Y axis
		m_vUp=D3DXVECTOR3(0.0f, 1.0f, 0.0f);      // Up Vector

		//right vector is parallel to X axis
		m_vRight=D3DXVECTOR3(1.0f, 0.0f, 0.0f);   // Right Vector

		//create the projection matrix
		D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 
                                640.0f / 480.0f, 0.1f, 10000.0f );
		m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
	}
	//position of the camera
	void SetPosition(float x,float y,float z)
	{
		m_vEye=D3DXVECTOR3(x,y,z);
	}
	//make the camera look at a specific point
	void LookAt(float x,float y,float z)
	{
		m_vLook=D3DXVECTOR3(x,y,z);
	}
	//rotate left and right
	void RotateLeftRight(float angle)
	{		 
  		D3DXMatrixRotationAxis( &matYRotation, &D3DXVECTOR3(0,1,0), D3DXToRadian(angle) );
		D3DXVec3TransformCoord( &m_vLook, &m_vLook, &matYRotation );
		D3DXVec3TransformCoord( &m_vUp, &m_vUp, &matYRotation );
	}
	//rotate up and down
	void RotateUpDown(float angle)
	{
		D3DXMatrixRotationAxis( &matXRotation, &D3DXVECTOR3(1,0,0), D3DXToRadian(angle) );
		D3DXVec3TransformCoord( &m_vLook, &m_vLook, &matXRotation );
		D3DXVec3TransformCoord( &m_vUp, &m_vUp, &matXRotation );
	}

	//----------------------------------------------------------------
	// here we have to build the view matrix, it should be in the form:
	//  |   rx     ux     lx    0 |
	//  |   ry     uy     ly    0 |
	//  |   rz     uz     lz    0 |
	//  | -(r.e) -(u.e) -(l.e)  1 |
	// Where r = Right vector
	//       u = Up vector
	//       l = Look vector
	//       e = Eye position in world space
	//       . = Dot-product operation
	//-----------------------------------------------------------------
	void UpdateCamera()
	{		
		D3DXMatrixIdentity( &view );

		D3DXVec3Normalize( &m_vLook, &m_vLook );
		D3DXVec3Cross( &m_vRight, &m_vUp, &m_vLook );
		D3DXVec3Normalize( &m_vRight, &m_vRight );
		D3DXVec3Cross( &m_vUp, &m_vLook, &m_vRight );
		D3DXVec3Normalize( &m_vUp, &m_vUp );

		view._11 = m_vRight.x;
		view._12 = m_vUp.x;
		view._13 = m_vLook.x;
		view._14 = 0.0f;

		view._21 = m_vRight.y;
		view._22 = m_vUp.y;
		view._23 = m_vLook.y;
		view._24 = 0.0f;

		view._31 = m_vRight.z;
		view._32 = m_vUp.z;
		view._33 = m_vLook.z;
		view._34 = 0.0f;

		view._41 = -D3DXVec3Dot( &m_vEye, &m_vRight );
		view._42 = -D3DXVec3Dot( &m_vEye, &m_vUp );
		view._43 = -D3DXVec3Dot( &m_vEye, &m_vLook );
		view._44 =  1.0f;

		m_pd3dDevice->SetTransform( D3DTS_VIEW, &view ); 
	}
	D3DXVECTOR3 GetPosition()
	{
		return m_vEye;
	}

	D3DXVECTOR3 GetUpVector()
	{
		return m_vUp;
	}

	D3DXVECTOR3 GetRightVector()
	{
		return m_vRight;
	}

	D3DXVECTOR3 GetLookVector()
	{
		return m_vLook;
	}
};
SimpleCamera * camera;

// The application's entry point
//--------------------------------------
int WINAPI WinMain(	HINSTANCE hInstance,
					HINSTANCE hPrevInstance,
					LPSTR     lpCmdLine,
					int       nCmdShow )
{
	WNDCLASSEX winClass; 
	MSG        uMsg;

    memset(&uMsg,0,sizeof(uMsg));

	winClass.lpszClassName = "MY_WINDOWS_CLASS";
	winClass.cbSize        = sizeof(WNDCLASSEX);
	winClass.style         = CS_HREDRAW | CS_VREDRAW;
	winClass.lpfnWndProc   = WindowProc;
	winClass.hInstance     = hInstance;
	winClass.hIcon	       = NULL;// 
    winClass.hIconSm	   = NULL;//
	winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	winClass.lpszMenuName  = NULL;
	winClass.cbClsExtra    = 0;
	winClass.cbWndExtra    = 0;

	if( !RegisterClassEx(&winClass) )
		return E_FAIL;

	g_hWnd = CreateWindowEx( NULL, "MY_WINDOWS_CLASS", 
		                     "Simple Camera - Mahmoud Yassin",
						     WS_OVERLAPPEDWINDOW | WS_VISIBLE,
					         0, 0, 640, 480, NULL, NULL, hInstance, NULL );

	if( g_hWnd == NULL )
		return E_FAIL;

    ShowWindow( g_hWnd, nCmdShow );
    UpdateWindow( g_hWnd );

	init();

	while( uMsg.message != WM_QUIT )
	{
		if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
		{
			TranslateMessage( &uMsg );
			DispatchMessage( &uMsg );
		}
		else
		{
			g_dCurTime     = timeGetTime();
			g_fElpasedTime = (float)((g_dCurTime - g_dLastTime) * 0.001);
			g_dLastTime    = g_dCurTime;

		    render();
		}
	}

	shutDown();

    UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance );

	return uMsg.wParam;
}

//----------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//----------------------------------
LRESULT CALLBACK WindowProc( HWND   hWnd, 
							 UINT   msg, 
							 WPARAM wParam, 
							 LPARAM lParam )
{
    switch( msg )
	{
        case WM_KEYDOWN:
		{
			switch( wParam )
			{
				case VK_ESCAPE:
					PostQuitMessage(0);
					break;
			}
		}
        break;

		case WM_CLOSE:
		{
			PostQuitMessage(0);	
		}

        case WM_DESTROY:
		{
            PostQuitMessage(0);
		}
        break;

		default:
		{
			return DefWindowProc( hWnd, msg, wParam, lParam );
		}
		break;
	}

	return 0;
}

//------------------------------------------------------------------------------
// Name: getUserInput()
// Desc: 
//------------------------------------------------------------------------------
void getUserInput( void )
{	
	float x_Rot=0.0f,y_Rot=0.0f;
	unsigned char keys[256];

	GetKeyboardState( keys );

    D3DXVECTOR3 tmpLook  = camera->GetLookVector();
	D3DXVECTOR3 tmpRight = camera->GetRightVector();
	D3DXVECTOR3 tmpEye   = camera->GetPosition();

	// Up 
	if( keys[VK_UP] & 0x80 )
		tmpEye -= tmpLook*-g_fMoveSpeed*g_fElpasedTime;

	// Down 
	if( keys[VK_DOWN] & 0x80 )
		tmpEye += (tmpLook*-g_fMoveSpeed)*g_fElpasedTime;

	// Left 
	if( keys[VK_LEFT] & 0x80 )
		tmpEye -= (tmpRight*g_fMoveSpeed)*g_fElpasedTime;

	// Right
	if( keys[VK_RIGHT] & 0x80 )
		tmpEye += (tmpRight*g_fMoveSpeed)*g_fElpasedTime;
	//------------------------------------
	//Rotation left - right
	if( keys[VK_SUBTRACT] & 0x80 )
		y_Rot-=0.1f;	

	if( keys[VK_ADD] & 0x80 )
		y_Rot+=0.1f;

	//Rotation up - down
	if( keys[VK_MULTIPLY] & 0x80 )
		x_Rot-=0.1f;	

	if( keys[VK_DIVIDE] & 0x80 )
		x_Rot+=0.1f;

	// Home Key - move up
	if( keys[VK_HOME] & 0x80 )
		tmpEye.y += g_fMoveSpeed*g_fElpasedTime; 

	// End Key - move down
	if( keys[VK_END] & 0x80 )
		tmpEye.y -= g_fMoveSpeed*g_fElpasedTime;

	//update camera position
	camera->SetPosition(tmpEye.x,tmpEye.y,tmpEye.z);

	//update camera rotations
	camera->RotateLeftRight(y_Rot);
	camera->RotateUpDown(x_Rot);
}

/////////////////////////////////////
//init 3D then the camera
///////////////////////////////////
void init( void )
{
    g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

    D3DDISPLAYMODE d3ddm;
    g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm );

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );

    d3dpp.Windowed               = TRUE;
    d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat       = d3ddm.Format;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;

    g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                          D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                          &d3dpp, &g_pd3dDevice );

    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
	g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
	g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );   

	//-------------------------------

	LPD3DXBUFFER pD3DXMtrlBuffer;

	HRESULT hr = D3DXLoadMeshFromX("mesh.x", D3DXMESH_SYSTEMMEM, 
                       g_pd3dDevice, NULL, 
                       &pD3DXMtrlBuffer, NULL, &g_dwNumMaterials, 
                       &g_pMesh );
	if (FAILED(hr))
	{
		MessageBox(0,"can not find the mesh file","error",0);
		exit(0);
	}


    // We need to extract the material properties and texture names 
    // from the pD3DXMtrlBuffer

    D3DXMATERIAL *d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();
    g_pMeshMaterials = new D3DMATERIAL9[g_dwNumMaterials];
    g_pMeshTextures  = new LPDIRECT3DTEXTURE9[g_dwNumMaterials];

    for( unsigned long i = 0; i < g_dwNumMaterials; ++i )
    {
        // Copy the material over...
        g_pMeshMaterials[i] = d3dxMaterials[i].MatD3D;

        // Set the ambient color for the material (D3DX does not do this)
        g_pMeshMaterials[i].Ambient = g_pMeshMaterials[i].Diffuse;

		// Create the texture...
		g_pMeshTextures[i] = NULL;
        D3DXCreateTextureFromFile( g_pd3dDevice, d3dxMaterials[i].pTextureFilename, 
                                   &g_pMeshTextures[i] );
    }

    pD3DXMtrlBuffer->Release();

	//now create the camera
	camera=new SimpleCamera(g_pd3dDevice);

}

// shutDown()
 void shutDown( void )
{
	if( g_pMeshMaterials != NULL )
        delete[] g_pMeshMaterials;

    if( g_pMeshTextures != NULL )
    {
        for( unsigned long i = 0; i < g_dwNumMaterials; i++ )
		{
			if( g_pMeshTextures[i] != NULL )
				g_pMeshTextures[i]->Release();
		}

        delete[] g_pMeshTextures;
    }

	if( g_pMesh != NULL )
        g_pMesh->Release(); 

    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

    if( g_pD3D != NULL )
        g_pD3D->Release();
}

 ///////////////////////////////
 //render everything here
 ///////////////////////////////
void render( void )
{
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                         D3DCOLOR_COLORVALUE(0.0f, 0.0f, 1.0, 0.5f), 1.0f, 0 );

	//read keyboard input
	getUserInput();
	//here I update the view matrix and the orientation
	camera->UpdateCamera();

	D3DXMATRIX matWorld;
	D3DXMatrixScaling( &matWorld, 5.0f, 5.0f, 5.0f );
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

    g_pd3dDevice->BeginScene();

    for( unsigned long i = 0; i < g_dwNumMaterials; i++ )
    {
        g_pd3dDevice->SetMaterial( &g_pMeshMaterials[i] );
        g_pd3dDevice->SetTexture( 0, g_pMeshTextures[i] );
        g_pMesh->DrawSubset( i );
    }
    g_pd3dDevice->EndScene();
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}


------------------------------------------
DirectX Developer

Software Developer