mirror of
https://github.com/Team-Resurgent/GamepadTest.git
synced 2026-08-15 11:18:15 -07:00
487 lines
16 KiB
C++
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;
|
|
}
|