Files
2024-12-29 22:27:02 -07:00

487 lines
16 KiB
C++

//-----------------------------------------------------------------------------
// File: XBMesh.cpp
//
// Desc: Support code for loading geometry stored in .xbg files. See the
// <XBMesh.h> header file for information on using this class.
//
// Hist: 11.01.00 - New for November XDK release
// 12.15.00 - Changes for December XDK release
// 03.15.01 - Mass changes (removed D3DX and .x support) for April XDK
// 04.15.01 - Using packed resources for May XDK
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include <xtl.h>
#include <xgmath.h>
#include <stdio.h>
#include "XBMesh.h"
#include "XBUtil.h"
//-----------------------------------------------------------------------------
// Name: CXBMesh()
// Desc:
//-----------------------------------------------------------------------------
CXBMesh::CXBMesh()
{
m_pAllocatedSysMem = NULL;
m_pAllocatedVidMem = NULL;
m_pMeshFrames = NULL;
m_dwNumFrames = 0;
m_dwRefCount = 1L;
}
//-----------------------------------------------------------------------------
// Name: ~CXBMesh()
// Desc:
//-----------------------------------------------------------------------------
CXBMesh::~CXBMesh()
{
// Free textures
for( DWORD i=0; i<m_dwNumFrames; i++ )
{
for( DWORD j = 0; j < m_pMeshFrames[i].m_MeshData.m_dwNumSubsets; j++ )
{
SAFE_RELEASE( m_pMeshFrames[i].m_MeshData.m_pSubsets[j].pTexture );
}
}
// Free alloacted memory
if( m_pAllocatedSysMem )
delete[] m_pAllocatedSysMem;
if( m_pAllocatedVidMem )
D3D_FreeContiguousMemory( m_pAllocatedVidMem );
}
//-----------------------------------------------------------------------------
// Name: Create()
// Desc:
//-----------------------------------------------------------------------------
HRESULT CXBMesh::Create( LPDIRECT3DDEVICE8 pd3dDevice, CHAR* strFilename,
CXBPackedResource* pResource )
{
// Find the media file
CHAR strMeshPath[512];
if( FAILED( XBUtil_FindMediaFile( strMeshPath, strFilename ) ) )
return E_FAIL;
// Open the file
HANDLE hFile;
DWORD dwNumBytesRead;
hFile = CreateFile(strMeshPath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
OUTPUT_DEBUG_STRING( "CXBMesh::Create(): ERROR: File not found!\n" );
return E_FAIL;
}
// Read the magic number
DWORD dwFileID;
ReadFile(hFile, &dwFileID, sizeof(DWORD), &dwNumBytesRead, NULL);
if( dwFileID != XBG_FILE_ID )
{
OUTPUT_DEBUG_STRING( "CXBMesh::Create(): ERROR: Invalid XBG file type!\n" );
return E_FAIL;
}
// Read in header
DWORD dwNumFrames; // Number of mesh frames in the file
DWORD dwSysMemSize; // Num bytes needed for system memory objects
DWORD dwVidMemSize; // Num bytes needed for video memory objects
ReadFile(hFile, &dwNumFrames, sizeof(DWORD), &dwNumBytesRead, NULL);
ReadFile(hFile, &dwSysMemSize, sizeof(DWORD), &dwNumBytesRead, NULL);
ReadFile(hFile, &dwVidMemSize, sizeof(DWORD), &dwNumBytesRead, NULL);
// Read in system memory objects
m_pAllocatedSysMem = (VOID*)new BYTE[dwSysMemSize];
ReadFile(hFile, m_pAllocatedSysMem, dwSysMemSize, &dwNumBytesRead, NULL);
// Read in video memory objects
m_pAllocatedVidMem = D3D_AllocContiguousMemory( dwVidMemSize, D3DVERTEXBUFFER_ALIGNMENT );
ReadFile(hFile, m_pAllocatedVidMem, dwVidMemSize, &dwNumBytesRead, NULL);
// Done with the file
CloseHandle(hFile);
// Now we need to patch the mesh data. Any pointers read from the file were
// stored as file offsets. So, we simply need to add a base address to patch
// things up.
m_pMeshFrames = (XBMESH_FRAME*)m_pAllocatedSysMem;
m_dwNumFrames = dwNumFrames;
for( DWORD i=0; i<m_dwNumFrames; i++ )
{
XBMESH_FRAME* pFrame = &m_pMeshFrames[i];
XBMESH_DATA* pMesh = &m_pMeshFrames[i].m_MeshData;
if( pFrame->m_pChild )
pFrame->m_pChild = (XBMESH_FRAME*)( (DWORD)pFrame->m_pChild - 16 + (DWORD)m_pMeshFrames );
if( pFrame->m_pNext )
pFrame->m_pNext = (XBMESH_FRAME*)( (DWORD)pFrame->m_pNext - 16 + (DWORD)m_pMeshFrames );
if( pMesh->m_pSubsets )
pMesh->m_pSubsets = (XBMESH_SUBSET*)( (DWORD)pMesh->m_pSubsets - 16 + (DWORD)m_pMeshFrames);
if( pMesh->m_dwNumIndices )
pMesh->m_IB.Data = pMesh->m_IB.Data - 16 + (DWORD)m_pMeshFrames;
if( pMesh->m_dwNumVertices )
pMesh->m_VB.Register( m_pAllocatedVidMem );
}
// Finally, create any textures used by the meshes' subsets. In this
// implementation, we are pulling textures out of the passed in resource.
if( pResource )
{
for( DWORD i=0; i<m_dwNumFrames; i++ )
{
XBMESH_DATA* pMesh = &m_pMeshFrames[i].m_MeshData;
for( DWORD j = 0; j < pMesh->m_dwNumSubsets; j++ )
{
XBMESH_SUBSET* pSubset = &pMesh->m_pSubsets[j];
pSubset->pTexture = pResource->GetTexture( pSubset->strTexture );
}
}
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Renders the hierarchy of frames and meshes.
//-----------------------------------------------------------------------------
HRESULT CXBMesh::Render( LPDIRECT3DDEVICE8 pd3dDevice, DWORD dwFlags )
{
if( m_pMeshFrames )
RenderFrame( pd3dDevice, m_pMeshFrames, dwFlags );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: RenderFrame()
// Desc: Renders a frame (save state, apply matrix, render children, restore).
//-----------------------------------------------------------------------------
HRESULT CXBMesh::RenderFrame( LPDIRECT3DDEVICE8 pd3dDevice, XBMESH_FRAME* pFrame,
DWORD dwFlags )
{
// Apply the frame's local transform
D3DXMATRIX matSavedWorld, matWorld;
pd3dDevice->GetTransform( D3DTS_WORLD, &matSavedWorld );
D3DXMatrixMultiply( &matWorld, &pFrame->m_matTransform, &matSavedWorld );
pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
// Render the mesh data
if( pFrame->m_MeshData.m_dwNumSubsets )
RenderMesh( pd3dDevice, &pFrame->m_MeshData, dwFlags );
// Render any child frames
if( pFrame->m_pChild )
RenderFrame( pd3dDevice, pFrame->m_pChild, dwFlags );
// Restore the transformation matrix
pd3dDevice->SetTransform( D3DTS_WORLD, &matSavedWorld );
// Render any sibling frames
if( pFrame->m_pNext )
RenderFrame( pd3dDevice, pFrame->m_pNext, dwFlags );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: RenderMesh()
// Desc: Renders the mesh geometry.
//-----------------------------------------------------------------------------
HRESULT CXBMesh::RenderMesh( LPDIRECT3DDEVICE8 pd3dDevice, XBMESH_DATA* pMesh,
DWORD dwFlags )
{
D3DVertexBuffer* pVB = &pMesh->m_VB;
DWORD dwNumVertices = pMesh->m_dwNumVertices;
D3DIndexBuffer* pIB = &pMesh->m_IB;
DWORD dwNumIndices = pMesh->m_dwNumIndices;
DWORD dwFVF = pMesh->m_dwFVF;
DWORD dwVertexSize = pMesh->m_dwVertexSize;
D3DPRIMITIVETYPE dwPrimType = pMesh->m_dwPrimType;
DWORD dwNumSubsets = pMesh->m_dwNumSubsets;
XBMESH_SUBSET* pSubsets = &pMesh->m_pSubsets[0];
(VOID)dwNumIndices; // not used
if( dwNumVertices == 0 )
return S_OK;
// Set the vertex stream
pd3dDevice->SetStreamSource( 0, pVB, dwVertexSize );
pd3dDevice->SetIndices( pIB, 0 );
// Set the FVF code, unless the user asked us not to
if( 0 == ( dwFlags & XBMESH_NOFVF ) )
pd3dDevice->SetVertexShader( dwFVF );
// Render the subsets
for( DWORD i = 0; i < dwNumSubsets; i++ )
{
BOOL bRender = FALSE;
// Render the opaque subsets, unless the user asked us not to
if( 0 == ( dwFlags & XBMESH_ALPHAONLY ) )
{
if( 0 == ( dwFlags & XBMESH_NOMATERIALS ) )
{
if( pSubsets[i].mtrl.Diffuse.a >= 1.0f )
bRender = TRUE;
}
else
bRender = TRUE;
}
// Render the transparent subsets, unless the user asked us not to
if( 0 == ( dwFlags & XBMESH_OPAQUEONLY ) )
{
if( 0 == ( dwFlags & XBMESH_NOMATERIALS ) )
{
if( pSubsets[i].mtrl.Diffuse.a < 1.0f )
bRender = TRUE;
}
}
if( bRender )
{
// Set the material, unless the user asked us not to
if( 0 == ( dwFlags & XBMESH_NOMATERIALS ) )
pd3dDevice->SetMaterial( &pSubsets[i].mtrl );
// Set the texture, unless the user asked us not to
if( 0 == ( dwFlags & XBMESH_NOTEXTURES ) )
pd3dDevice->SetTexture( 0, pSubsets[i].pTexture );
// Call the callback, so the app can tweak state before rendering
// each subset
BOOL bRenderSubset = RenderCallback( pd3dDevice, i, &pSubsets[i], dwFlags );
// Draw the mesh subset
if( bRenderSubset )
{
DWORD dwNumPrimitives = ( D3DPT_TRIANGLESTRIP == dwPrimType ) ? pSubsets[i].dwIndexCount-2 : pSubsets[i].dwIndexCount/3;
pd3dDevice->DrawIndexedPrimitive( dwPrimType, 0, pSubsets[i].dwIndexCount,
pSubsets[i].dwIndexStart, dwNumPrimitives );
}
}
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: ComputeRadius()
// Desc: Finds the farthest point from zero on the mesh.
//-----------------------------------------------------------------------------
FLOAT CXBMesh::ComputeRadius()
{
D3DXMATRIX matIdentity;
D3DXMatrixIdentity( &matIdentity );
return ComputeFrameRadius( m_pMeshFrames, &matIdentity );
}
//-----------------------------------------------------------------------------
// Name: ComputeFrameRadius()
// Desc: Calls ComputeMeshRadius for each frame with the correct transform.
//-----------------------------------------------------------------------------
FLOAT CXBMesh::ComputeFrameRadius( XBMESH_FRAME* pFrame, D3DXMATRIX* pmatParent )
{
// Apply the frame's local transform
D3DXMATRIX matWorld;
D3DXMatrixMultiply( &matWorld, &pFrame->m_matTransform, pmatParent );
FLOAT fRadius = 0.0f;
// Compute bounds for the mesh data
if( pFrame->m_MeshData.m_dwNumSubsets )
fRadius = ComputeMeshRadius( &pFrame->m_MeshData, &matWorld );
// Compute bounds for any child frames
if( pFrame->m_pChild )
{
FLOAT fChildRadius = ComputeFrameRadius( pFrame->m_pChild, &matWorld );
if( fChildRadius > fRadius )
fRadius = fChildRadius;
}
// Compute bounds for any sibling frames
if( pFrame->m_pNext )
{
FLOAT fSiblingRadius = ComputeFrameRadius( pFrame->m_pNext, pmatParent );
if( fSiblingRadius > fRadius )
fRadius = fSiblingRadius;
}
return fRadius;
}
//-----------------------------------------------------------------------------
// Name: ComputeMeshRadius()
// Desc: Finds the farthest point from zero on the mesh.
//-----------------------------------------------------------------------------
FLOAT CXBMesh::ComputeMeshRadius( XBMESH_DATA* pMesh, D3DXMATRIX* pmat )
{
DWORD dwNumVertices = pMesh->m_dwNumVertices;
DWORD dwVertexSize = pMesh->m_dwVertexSize;
BYTE* pVertices;
D3DXVECTOR3 vPos;
FLOAT fMaxDist2 = 0.0f;
pMesh->m_VB.Lock( 0, 0, &pVertices, 0 );
while( dwNumVertices-- )
{
D3DXVec3TransformCoord( &vPos, (D3DXVECTOR3*)pVertices, pmat );
FLOAT fDist2 = vPos.x*vPos.x + vPos.y*vPos.y + vPos.z*vPos.z;
if( fDist2 > fMaxDist2 )
fMaxDist2 = fDist2;
pVertices += dwVertexSize;
}
pMesh->m_VB.Unlock();
return sqrtf( fMaxDist2 );
}
//-----------------------------------------------------------------------------
// Take the union of two boxes
//-----------------------------------------------------------------------------
inline float MAX(float a, float b) { return a > b ? a : b; }
inline float MIN(float a, float b) { return a < b ? a : b; }
static void UnionBox(D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax, const D3DXVECTOR3 &vMin, const D3DXVECTOR3 &vMax)
{
pvMin->x = MIN(pvMin->x, vMin.x);
pvMin->y = MIN(pvMin->y, vMin.y);
pvMin->z = MIN(pvMin->z, vMin.z);
pvMax->x = MAX(pvMax->x, vMax.x);
pvMax->y = MAX(pvMax->y, vMax.y);
pvMax->z = MAX(pvMax->z, vMax.z);
}
//-----------------------------------------------------------------------------
// Name: ComputeBoundingBox()
// Desc: Calculates the bounding box of the entire hierarchy.
//-----------------------------------------------------------------------------
HRESULT CXBMesh::ComputeBoundingBox(D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax)
{
D3DXMATRIX matIdentity;
D3DXMatrixIdentity( &matIdentity );
return ComputeFrameBoundingBox( m_pMeshFrames, &matIdentity, pvMin, pvMax );
}
//-----------------------------------------------------------------------------
// Name: ComputeFrameBoundingBox()
// Desc: Calls ComputeMeshBoundingBox for each frame with the correct transform.
//-----------------------------------------------------------------------------
HRESULT CXBMesh::ComputeFrameBoundingBox( XBMESH_FRAME* pFrame, D3DXMATRIX* pmatParent, D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax)
{
HRESULT hr;
// initialize bounds to be reset on the first UnionBox
pvMin->x = pvMin->y = pvMin->z = FLT_MAX;
pvMax->x = pvMax->y = pvMax->z = -FLT_MAX;
// Apply the frame's local transform
D3DXMATRIX matWorld;
D3DXMatrixMultiply( &matWorld, &pFrame->m_matTransform, pmatParent );
// Compute bounds for the mesh data
if( pFrame->m_MeshData.m_dwNumSubsets )
{
D3DXVECTOR3 vMin, vMax;
hr = ComputeMeshBoundingBox( &pFrame->m_MeshData, &matWorld, &vMin, &vMax );
if (FAILED(hr))
return hr;
UnionBox(pvMin, pvMax, vMin, vMax);
}
// Compute bounds for any child frames
if( pFrame->m_pChild )
{
D3DXVECTOR3 vMin, vMax;
hr = ComputeFrameBoundingBox( pFrame->m_pChild, &matWorld, &vMin, &vMax );
if (FAILED(hr))
return hr;
UnionBox(pvMin, pvMax, vMin, vMax);
}
// Compute bounds for any sibling frames
if( pFrame->m_pNext )
{
D3DXVECTOR3 vMin, vMax;
hr = ComputeFrameBoundingBox( pFrame->m_pNext, pmatParent, &vMin, &vMax );
if (FAILED(hr))
return hr;
UnionBox(pvMin, pvMax, vMin, vMax);
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: ComputeMeshBoundingBox()
// Desc: Calculate the bounding box of the transformed mesh.
//-----------------------------------------------------------------------------
HRESULT CXBMesh::ComputeMeshBoundingBox( XBMESH_DATA* pMesh, D3DXMATRIX* pmat, D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax)
{
// initialize bounds to be reset on the first point
pvMin->x = pvMin->y = pvMin->z = FLT_MAX;
pvMax->x = pvMax->y = pvMax->z = -FLT_MAX;
DWORD dwNumVertices = pMesh->m_dwNumVertices;
DWORD dwVertexSize = pMesh->m_dwVertexSize;
BYTE* pVertices;
D3DXVECTOR3 vPos;
pMesh->m_VB.Lock( 0, 0, &pVertices, 0 );
while( dwNumVertices-- )
{
D3DXVec3TransformCoord( &vPos, (D3DXVECTOR3*)pVertices, pmat );
UnionBox(pvMin, pvMax, vPos, vPos); // expand the bounding box to include the point
pVertices += dwVertexSize;
}
pMesh->m_VB.Unlock();
return S_OK;
}