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!