Collision detection, movement, the works..

Started by
3 comments, last by Pirosan 19 years, 7 months ago
ok, i dont really know how easy this will follow, so i will be as specific as i can... I am working with the base classes from the book "The zen of direct3d game programming" to render my objects in 3d space... and have gotten to the point where i can easily create objects and walk around. Now i had recently made the feature to walk around and look with the mouse, so i thought that i should add collision detection so that i could set the person onto the ground and allow the user to walk on the levels i have made. I tried to create a function that uses D3DXINTERCEPT but needed to use the ZenFrame (in my case its OsFrame) class to get the required mLocal translation/rotation matrix so that i can move the ray to the corrent location in order to correctly check for a collision. here is the code below for both OsFrame and OsMesh classes:

// ---------------------------- class OsMesh
class OsMesh : public OsObject
{
public:
	OsMesh();
	~OsMesh();
	OsMesh( OsMesh& OtherMesh );
// Variables:
public:
	int m_NumMats;

protected:
	LPD3DXMESH m_pMesh;	// The mesh


	LPDIRECT3DTEXTURE8* m_pTextures;	// The mesh's textures
	OsMaterial* m_pMaterials;			// The mesh's materials

// Functions:
public:
	HRESULT LoadXFile( char* pstrPathName );	//Loads a mesh
	HRESULT Render();							// Renders the mesh

	void SetMaterial( OsMaterial* pMaterial );
	float checkCollide(D3DXVECTOR3 pRayPos, D3DXVECTOR3 pRayDir);
	int GetSize(){ return sizeof( *this ); }

protected:

};
float OsMesh::checkCollide(D3DXVECTOR3 pRayPos, D3DXVECTOR3 pRayDir){

BOOL bHit;
DWORD dwFace;
float fBary1, fBary2, fDist;
D3DXIntersect(m_pMesh, &pRayPos, &pRayDir, &bHit, &dwFace, &fBary1, &fBary2, &fDist, NULL, NULL);
if(bHit){
	return fDist;
g_mDist = fDist;
}else{
return -1;
}
}

OsMesh::OsMesh( OsMesh& OtherMesh )
{
	m_NumMats = OtherMesh.m_NumMats;
	m_pMesh = OtherMesh.m_pMesh;

	m_pMesh->AddRef();

	m_pTextures = new LPDIRECT3DTEXTURE8[ m_NumMats ];

	CopyMemory( m_pTextures, OtherMesh.m_pTextures, sizeof( m_pTextures ) );

	for( int i = 0 ; i < m_NumMats ; i++ )
		m_pTextures->AddRef();

	m_pMaterials = new OsMaterial[ m_NumMats ];

	CopyMemory( m_pMaterials, OtherMesh.m_pMaterials, sizeof( m_pMaterials ) );

}	


OsMesh::OsMesh()
{
	m_pMesh = 0;
	m_NumMats = 0;

	m_pTextures = 0;
	m_pMaterials = 0;
}

OsMesh::~OsMesh()
{
	if( m_pMesh )
		m_pMesh->Release();


	if( m_pTextures )
		delete[] m_pTextures;

	if( m_pMaterials )
		delete[] m_pMaterials;
}

// Sets all the materials for the object to the specified material
void OsMesh::SetMaterial( OsMaterial* pMaterial )
{
	for( int i = 0 ; i < m_NumMats ; i++ )
	{
		CopyMemory( &m_pMaterials.m_Material, &pMaterial->m_Material, sizeof( D3DMATERIAL8 ) );
	}
}



HRESULT OsMesh::LoadXFile( char* pstrPathName )
{
	HRESULT r = 0;

	// The buffer to hold the materials
	LPD3DXBUFFER pMaterialBuffer = 0;

	// Load the x file from disk
	r = D3DXLoadMeshFromX( pstrPathName, D3DXMESH_SYSTEMMEM, g_d3d_device, 0, 
							&pMaterialBuffer, (DWORD*)&m_NumMats, &m_pMesh );
	if( FAILED( r ) )
	{
		FatalError( "Failed to load XFile with file name:" );
		FatalError( pstrPathName );
		return E_FAIL;
	}

	// Create a new texture array
	m_pTextures = new LPDIRECT3DTEXTURE8[ m_NumMats ];
	// Create a new material array
	m_pMaterials = new OsMaterial[ m_NumMats ];

	// Get a pointer to the start of the material buffer
	D3DXMATERIAL* pMaterials = (D3DXMATERIAL*)pMaterialBuffer->GetBufferPointer();

	// Loop for each material in the buffer
	for( int i = 0 ; i < m_NumMats ; i++ )
	{
		// Extract the material from the buffer
		m_pMaterials.m_Material = pMaterials.MatD3D;
		// Brighten the material
		m_pMaterials.m_Material.Ambient = m_pMaterials.m_Material.Diffuse;
		
		// If a texture is not set for this material
		if( !pMaterials.pTextureFilename )
		{
			// Set the texture to the default texture 
			m_pTextures = g_pDefaultTexture;

			// Iterate to the next loop because there is no texture
			continue;
		}
		
		// Create a new texture from the filename supplied
		r = D3DXCreateTextureFromFile( g_d3d_device, pMaterials.pTextureFilename, &m_pTextures );
		if( FAILED( r ) )
		{
			FatalError( "Unable to load texture for mesh with filname:" );
			FatalError( pMaterials.pTextureFilename );
			
			// If the texture load failed then set
			// it to the default texture
			m_pTextures = g_pDefaultTexture;
		}

			
	}

	// Release the material buffer
	pMaterialBuffer->Release();

	return S_OK;

}

HRESULT OsMesh::Render()
{
	HRESULT r = E_FAIL;
	
	// Loop for each material
	for( int i = 0 ; i < m_NumMats ; i++ )
	{
		// Set this material as active
		m_pMaterials.Update();
		// Set this texture as active
		g_d3d_device->SetTexture( 0, m_pTextures );
		// Render this subset of the mesh
		r = m_pMesh->DrawSubset(i);

		// Reset the vertex shader
		g_d3d_device->SetVertexShader( OSIRUS_VERTEX_TYPE );
	}

	// Return the result of the render operation
	return r;
}




// --------------------------- class OsFrame
class OsFrame;
typedef int (*FRAME_MOVEMENT_CALLBACK)( OsFrame* pFrame, void* Parameter );

class OsFrame
{
public:
	OsFrame();
	~OsFrame();

// Variables
public:
	void* m_pParameter;

protected:
	// The local matrix for this frame
	D3DXMATRIX m_mLocal;

	// The position of this frame
	D3DXVECTOR3 m_vPosition;
	// The velocity of this frame
	D3DXVECTOR3 m_vVelocity;
	
	// The orientation of this frame
	float m_Yaw, m_Pitch, m_Roll;

	// The list of objects in this frame
	OsObject* m_pObjectList;

	// Pointer to the next frame in the list
	OsFrame* m_pNext;

	OsFrame* m_pChildFrameList;
	OsFrame* m_pParentFrame;

	FRAME_MOVEMENT_CALLBACK m_pfnCallback;
	BOOL m_bCallback;

	
// Functions
public:
	HRESULT SetCallback( FRAME_MOVEMENT_CALLBACK pfnCallback );
	
	void SetVelocity( float x, float y, float z );
	void GetVelocity( float& x, float& y, float& z );

	void SetPosition( float x, float y, float z );
	void GetPosition( float& x, float& y, float& z );

	// Returns the local matrix for this frame
	void GetLocal( D3DXMATRIX& pMatrix );

	void SetYaw( float Yaw ){ m_Yaw = Yaw;  }
	void GetYaw( float& Yaw){ Yaw = m_Yaw; }

	void SetPitch( float Pitch ){ m_Pitch = Pitch;  }
	void GetPitch( float& Pitch){ Pitch = m_Pitch; }

	void SetRoll( float Roll ){ m_Roll = Roll;  }
	void GetRoll( float& Roll){ Roll = m_Roll; }

	// Update the position of this objects
	void Update();	

	// Add an object to the frame
	HRESULT AddObject( OsObject* pNewObject );

	// Render the objects
	HRESULT Render(); 

	// Set/Get the next pointer for use in the list
	void SetNext( OsFrame* pNext ){ m_pNext = pNext; }
	OsFrame* GetNext(){ return m_pNext; }

	
	HRESULT AddFrame( OsFrame* pNewFrame );
	float checkCollide(D3DXVECTOR3 pRayPos, D3DXVECTOR3 pRayDir);

protected:

	void SetParent( OsFrame* pParent ){ m_pParentFrame = pParent; }
	OsFrame* GetParent(){ return m_pParentFrame; }
};

float OsFrame::checkCollide(D3DXVECTOR3 pRayPos, D3DXVECTOR3 pRayDir){
OsObject* pObject = m_pObjectList;

OsMesh* mesh = (OsMesh*) &pObject;


D3DXMATRIX m_mILocal;
D3DXMATRIX m_mILocalN;





D3DXMatrixInverse( &m_mILocal, NULL, &m_mLocal );

D3DXMatrixTranspose( &m_mILocalN, &m_mLocal );

// Transform Coords
D3DXVec3TransformCoord( &pRayPos, &pRayPos, &m_mILocal );
D3DXVec3TransformNormal( &pRayDir, &pRayDir, &m_mILocalN );



return mesh->checkCollide( pRayPos, pRayDir);

//return pObject->checkCollide(pRayPos, pRayDir);
}
OsFrame::OsFrame()
{
	// Set the orientation to 0
	m_Yaw = 0.0f;
	m_Pitch = 0.0f;
	m_Roll = 0.0f;

	// Set the position and velocity to 0
	m_vPosition = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	m_vVelocity = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );

	// Init the local matrix to an identity
	D3DXMatrixIdentity( &m_mLocal );

	// Zero out the object list
	m_pObjectList = 0;

	m_pNext = 0;
	m_pChildFrameList = 0;
	m_pParentFrame = 0;

	m_pfnCallback = 0;
	m_bCallback = FALSE;
}

OsFrame::~OsFrame()
{

	
}

// Sets the callback behavior function for this frame
HRESULT OsFrame::SetCallback( FRAME_MOVEMENT_CALLBACK pfnCallback )
{
	// If a valid pointer was not specified
	// (or if the user wants to disable all plugins)...
	if( !pfnCallback )
	{
		// Set the callback status flag to FALSE
		m_bCallback = FALSE;
		// Set the callback pointer to NULL
		m_pfnCallback = NULL;
		// Return failure. If the caller is disabling
		// calbacks on purpose then it can just
		// ignore the failure return.
		return E_FAIL;
	}

	// Set the callback pointer to the new function
	m_pfnCallback = pfnCallback;

	// Set the callback presence flag to TRUE
	m_bCallback = TRUE;

	return S_OK;
}

// Adds a new child frame to this frame
HRESULT OsFrame::AddFrame( OsFrame* pNewFrame )
{
	// Make sure the new frame is valid
	if( !pNewFrame )
	{
		FatalError( "Failed in attempt to add an invalid child frame" );
		return E_FAIL;
	}

	// Tell it whos da daddy
	pNewFrame->SetParent( this );
	
	// If the child frame has not been create yet...
	if( !m_pChildFrameList )
	{
		// ...Set this new frame as the first in the list
		m_pChildFrameList = pNewFrame;
	}
	else
	{
		// ...The list already exists

		// Get a pointer to the start of the list
		OsFrame* pTempFrame = m_pChildFrameList;

		// Find the last entry in the list
		while( pTempFrame->GetNext() )
			pTempFrame = pTempFrame->GetNext();

		// Add this frame to the end of this list
		pTempFrame->SetNext( pNewFrame );
	}
	
	return S_OK;
}


HRESULT OsFrame::AddObject( OsObject* pNewObject )
{
	// Return if the new object is bogus
	if( !pNewObject )
		return E_FAIL;

	// Tell the object it has a new parent frame
	pNewObject->SetParentFrame( this );
	
	// If the object list does not exist yet...
	if( !m_pObjectList )
	{
		// ...Set this object to the start of the list
		m_pObjectList = pNewObject;
	}
	else 
	{
		// ...The list has already been created
		// so add this object to the end of the list
		
		// Get a pointer to the start of the list
		OsObject* pObject = m_pObjectList;

		// Find the last object in the list
		while( pObject->GetNext() )
			pObject = (OsObject*)pObject->GetNext();

		// Add this to the last item in the list
		pObject->SetNext( pNewObject );
	}

	return S_OK;
}

HRESULT OsFrame::Render()
{
	
	// Update the position and orientation of the frame
	// and set the new world transform matrix
	Update();

	// Get a pointer to the child frame list
	OsFrame* pFrame = m_pChildFrameList;

	// Loop for every child frame in the list
	while( pFrame )
	{
		// Render this child frame
		pFrame->Render();
		// Iterate to the next frame
		pFrame = pFrame->GetNext();
	}
	
	// Return if this frame has no visuals to render
	if( !m_pObjectList )
		return S_OK;

	// Get a pointer to the start of the list
	OsObject* pObject = m_pObjectList;

	// Reset the transform in case those pesky children modified it
	g_d3d_device->SetTransform( D3DTS_WORLD, &m_mLocal );

	// Loop for each object in the list
	while( pObject )
	{
		// Render the object
		pObject->Render();
		
		// Increment to the next object in the list
		pObject = (OsObject*)pObject->GetNext();
	}	

	// Success!
	return S_OK;

}

// Updates the frames position and orientation
// taking into account the properties of the 
// parent frame.
void OsFrame::Update()
{
	// Call the behavior plugin if one has been assigned
	if( m_bCallback )
		m_pfnCallback( this, m_pParameter );
	
	// Create some temporary matrices for the
	// rotation and translation transformations
	D3DXMATRIX mRotX, mRotY, mRotZ, mTrans, mRotTemp;

	// Update the position by the velocity
	m_vPosition.x += m_vVelocity.x;
	m_vPosition.y += m_vVelocity.y;
	m_vPosition.z += m_vVelocity.z;

	// Set the translation matrix
	D3DXMatrixTranslation( &mTrans, m_vPosition.x, m_vPosition.y, m_vPosition.z );

	// Set the rotation around the x axis
	D3DXMatrixRotationX( &mRotX, m_Pitch );
	// Set the rotation around the y axis
	D3DXMatrixRotationY( &mRotY, m_Yaw );
	// Set the rotation around the z axis
	D3DXMatrixRotationZ( &mRotZ, m_Roll );

	// Concatenate the y axis and x axis rotation matrices
	D3DXMatrixMultiply( &mRotTemp, &mRotX, &mRotY );
	// Concatenate the xy axes and z axis rotation matrices
	D3DXMatrixMultiply( &mRotTemp, &mRotZ, &mRotTemp );
	// Concatenate the xyz axes and translation matrices
	D3DXMatrixMultiply( &mTrans, &mRotTemp, &mTrans );

	// Update the copy of the local matrix
	m_mLocal = mTrans;	

	// If this frame is a child of another frame...
	if( GetParent() )
	{
		// ...Then we need to get the parents transform matrix
		
		// Matrix to hold the parents matrix
		D3DXMATRIX mParent;

		// Get the parents local to world transform matrix
		GetParent()->GetLocal( mParent );

		// Concatenate the parents matrix with this frames matrix
		D3DXMatrixMultiply( &m_mLocal, &m_mLocal, &mParent);
	}
	

	// Set the world matrix
	g_d3d_device->SetTransform( D3DTS_WORLD, &m_mLocal );

}

// Returns the local transform matrix
void OsFrame::GetLocal( D3DXMATRIX& Matrix )
{
	Update();
	
	Matrix = m_mLocal;
}

// Returns the velocity of the frame
void OsFrame::GetVelocity( float& x, float& y, float& z )
{
	x = m_vVelocity.x;
	y = m_vVelocity.y;
	z = m_vVelocity.z;
}

// Sets the velocity of the frame
void OsFrame::SetVelocity( float x, float y, float z )
{
	m_vVelocity.x = x;
	m_vVelocity.y = y;
	m_vVelocity.z = z;
}

// Returns the position of the frame
void OsFrame::GetPosition( float& x, float& y, float& z )
{
	x = m_vPosition.x;
	y = m_vPosition.y;
	z = m_vPosition.z;
}

// Sets the position of the frame
void OsFrame::SetPosition( float x, float y, float z )
{
	m_vPosition.x = x;
	m_vPosition.y = y;
	m_vPosition.z = z;
}


notice the functions called CheckCollide in both... i call the checkCollide function with the frame, then the frame takes the rays i give it and translate it to the needed location so that basically the mesh is in the center of the "universe", then it checks on OsMesh to make sure it collides or not. PROBLEM: my problem is that with this code my program crashes all the time... when i took out the following line of code in the OsFrame::CheckCollide function... D3DXIntersect(m_pMesh, &pRayPos, &pRayDir, &bHit, &dwFace, &fBary1, &fBary2, &fDist, NULL, NULL); ...it worked fine, but of course never checked for a collision... in order to cast my OsObject in the OsFrame::Check Collison, i used this following bit of code that i feel may be at fault: OsObject* pObject = m_pObjectList; OsMesh* mesh = (OsMesh*) &pObject ...any ideas on what i could do? i dont think i am casting correctly... Chris EDIT: PS: sorry if my code is a bit unorganized, as long as you find the CheckCollide functions ok you should be fine in looking at my code
Bow before me... for i am l33t!
Advertisement
I'm afraid i'm not much help as far as casting goes. However, sincce you're looking into intersections, here's an awesome tutorial using ellipsoids (even explains stair-like movement).

As for your problem, if you leave out that line, bHit is not initialised... So that would be an error. Also, a bit further you have the following:

if(bHit){
return fDist;
g_mDist = fDist;
}else{

? g_mDist = fDist; will never get called, and i'm not even sure it'd compile... it shouldn't, or at the very least should give a warning...
oh im sorry, that was a leftover bit of code from another attempt to get information from the function, that variable is initialized in the beginning of the source file.

Also, the collision function used to work for me, until i moved it into my OsFrame class and tried casting the class... so i think that the main problem is in the casting...

*ponder*
Bow before me... for i am l33t!
Should mesh really be a pointer to a pointer, declared as only a pointer?
Try:
OsObject* pObject = m_pObjectList;
OsMesh* mesh = (OsMesh*)pObject;
oh my gosh, how did i miss that?!?

Thankyou so much for your helo, its working perfectly!


Chris
Bow before me... for i am l33t!

This topic is closed to new replies.

Advertisement