1 //-----------------------------------------------------------------------------
2 // File: XBMesh.cpp
3 //
4 // Desc: Support code for loading geometry stored in .xbg files. See the
5 //       <XBMesh.h> header file for information on using this class.
6 //
7 // Hist: 11.01.00 - New for November XDK release
8 //       12.15.00 - Changes for December XDK release
9 //       03.15.01 - Mass changes (removed D3DX and .x support) for April XDK
10 //       04.15.01 - Using packed resources for May XDK
11 //
12 // Copyright (c) Microsoft Corporation. All rights reserved.
13 //-----------------------------------------------------------------------------
14 #include <xtl.h>
15 #include <xgmath.h>
16 #include <stdio.h>
17 #include "XBMesh.h"
18 #include "XBUtil.h"
19 
20 
21 
22 
23 //-----------------------------------------------------------------------------
24 // Name: CXBMesh()
25 // Desc:
26 //-----------------------------------------------------------------------------
CXBMesh()27 CXBMesh::CXBMesh()
28 {
29     m_pAllocatedSysMem = NULL;
30     m_pAllocatedVidMem = NULL;
31     m_pMeshFrames      = NULL;
32     m_dwNumFrames      = 0;
33     m_dwRefCount       = 1L;
34 }
35 
36 
37 
38 
39 //-----------------------------------------------------------------------------
40 // Name: ~CXBMesh()
41 // Desc:
42 //-----------------------------------------------------------------------------
~CXBMesh()43 CXBMesh::~CXBMesh()
44 {
45     // Free textures
46     for( DWORD i=0; i<m_dwNumFrames; i++ )
47     {
48         for( DWORD j = 0; j < m_pMeshFrames[i].m_MeshData.m_dwNumSubsets; j++ )
49         {
50             SAFE_RELEASE( m_pMeshFrames[i].m_MeshData.m_pSubsets[j].pTexture );
51         }
52     }
53 
54     // Free allocated memory
55     if( m_pAllocatedSysMem )
56         delete[] m_pAllocatedSysMem;
57 
58     if( m_pAllocatedVidMem )
59         D3D_FreeContiguousMemory( m_pAllocatedVidMem );
60 }
61 
62 
63 
64 
65 //-----------------------------------------------------------------------------
66 // Name: Create()
67 // Desc:
68 //-----------------------------------------------------------------------------
Create(LPDIRECT3DDEVICE8 pd3dDevice,CHAR * strFilename,CXBPackedResource * pResource)69 HRESULT CXBMesh::Create( LPDIRECT3DDEVICE8 pd3dDevice, CHAR* strFilename,
70                          CXBPackedResource* pResource )
71 {
72     // Find the media file
73     CHAR strMeshPath[512];
74     if( FAILED( XBUtil_FindMediaFile( strMeshPath, strFilename ) ) )
75         return E_FAIL;
76 
77     // Open the file
78     HANDLE hFile;
79     DWORD dwNumBytesRead;
80     hFile = CreateFile(strMeshPath, GENERIC_READ, FILE_SHARE_READ, NULL,
81                        OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
82     if(hFile == INVALID_HANDLE_VALUE)
83     {
84         OUTPUT_DEBUG_STRING( "CXBMesh::Create(): ERROR: File not found!\n" );
85         return E_FAIL;
86     }
87 
88     // Read the magic number
89     DWORD dwFileID;
90     ReadFile(hFile, &dwFileID, sizeof(DWORD), &dwNumBytesRead, NULL);
91     if( dwFileID != XBG_FILE_ID )
92     {
93         OUTPUT_DEBUG_STRING( "CXBMesh::Create(): ERROR: Invalid XBG file type!\n" );
94         return E_FAIL;
95     }
96 
97     // Read in header
98     DWORD dwNumFrames;  // Number of mesh frames in the file
99     DWORD dwSysMemSize; // Num bytes needed for system memory objects
100     DWORD dwVidMemSize; // Num bytes needed for video memory objects
101 
102     ReadFile(hFile,  &dwNumFrames, sizeof(DWORD), &dwNumBytesRead, NULL);
103     ReadFile(hFile,  &dwSysMemSize, sizeof(DWORD), &dwNumBytesRead, NULL);
104     ReadFile(hFile,  &dwVidMemSize, sizeof(DWORD), &dwNumBytesRead, NULL);
105 
106     // Read in system memory objects
107     m_pAllocatedSysMem = (VOID*)new BYTE[dwSysMemSize];
108     ReadFile(hFile, m_pAllocatedSysMem, dwSysMemSize, &dwNumBytesRead, NULL);
109 
110 
111     // Read in video memory objects
112     m_pAllocatedVidMem = D3D_AllocContiguousMemory( dwVidMemSize, D3DVERTEXBUFFER_ALIGNMENT );
113     ReadFile(hFile, m_pAllocatedVidMem, dwVidMemSize, &dwNumBytesRead, NULL);
114 
115     // Done with the file
116     CloseHandle(hFile);
117 
118     // Now we need to patch the mesh data. Any pointers read from the file were
119     // stored as file offsets. So, we simply need to add a base address to patch
120     // things up.
121     m_pMeshFrames = (XBMESH_FRAME*)m_pAllocatedSysMem;
122     m_dwNumFrames = dwNumFrames;
123 
124     for( DWORD i=0; i<m_dwNumFrames; i++ )
125     {
126         XBMESH_FRAME* pFrame = &m_pMeshFrames[i];
127         XBMESH_DATA*  pMesh  = &m_pMeshFrames[i].m_MeshData;
128 
129         if( pFrame->m_pChild )
130             pFrame->m_pChild  = (XBMESH_FRAME*)( (DWORD)pFrame->m_pChild - 16 + (DWORD)m_pMeshFrames );
131         if( pFrame->m_pNext )
132             pFrame->m_pNext   = (XBMESH_FRAME*)( (DWORD)pFrame->m_pNext  - 16 + (DWORD)m_pMeshFrames );
133         if( pMesh->m_pSubsets )
134             pMesh->m_pSubsets = (XBMESH_SUBSET*)( (DWORD)pMesh->m_pSubsets - 16 + (DWORD)m_pMeshFrames);
135 
136         if( pMesh->m_dwNumIndices )
137             pMesh->m_IB.Data  = pMesh->m_IB.Data - 16 + (DWORD)m_pMeshFrames;
138         if( pMesh->m_dwNumVertices )
139             pMesh->m_VB.Register( m_pAllocatedVidMem );
140     }
141 
142     // Finally, create any textures used by the meshes' subsets. In this
143     // implementation, we are pulling textures out of the passed in resource.
144     if( pResource )
145     {
146         for( DWORD i=0; i<m_dwNumFrames; i++ )
147         {
148             XBMESH_DATA* pMesh = &m_pMeshFrames[i].m_MeshData;
149 
150             for( DWORD j = 0; j < pMesh->m_dwNumSubsets; j++ )
151             {
152                 XBMESH_SUBSET* pSubset = &pMesh->m_pSubsets[j];
153 
154                 pSubset->pTexture = pResource->GetTexture( pSubset->strTexture );
155             }
156         }
157     }
158 
159     return S_OK;
160 }
161 
162 
163 
164 
165 //-----------------------------------------------------------------------------
166 // Name: Render()
167 // Desc: Renders the hierarchy of frames and meshes.
168 //-----------------------------------------------------------------------------
Render(LPDIRECT3DDEVICE8 pd3dDevice,DWORD dwFlags)169 HRESULT CXBMesh::Render( LPDIRECT3DDEVICE8 pd3dDevice, DWORD dwFlags )
170 {
171     if( m_pMeshFrames )
172         RenderFrame( pd3dDevice, m_pMeshFrames, dwFlags );
173 
174     return S_OK;
175 }
176 
177 
178 
179 
180 //-----------------------------------------------------------------------------
181 // Name: RenderFrame()
182 // Desc: Renders a frame (save state, apply matrix, render children, restore).
183 //-----------------------------------------------------------------------------
RenderFrame(LPDIRECT3DDEVICE8 pd3dDevice,XBMESH_FRAME * pFrame,DWORD dwFlags)184 HRESULT CXBMesh::RenderFrame( LPDIRECT3DDEVICE8 pd3dDevice, XBMESH_FRAME* pFrame,
185                               DWORD dwFlags )
186 {
187     // Apply the frame's local transform
188     D3DXMATRIX matSavedWorld, matWorld;
189     pd3dDevice->GetTransform( D3DTS_WORLD, &matSavedWorld );
190     D3DXMatrixMultiply( &matWorld, &pFrame->m_matTransform, &matSavedWorld );
191     pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
192 
193     // Render the mesh data
194     if( pFrame->m_MeshData.m_dwNumSubsets )
195         RenderMesh( pd3dDevice, &pFrame->m_MeshData, dwFlags );
196 
197     // Render any child frames
198     if( pFrame->m_pChild )
199         RenderFrame( pd3dDevice, pFrame->m_pChild, dwFlags );
200 
201     // Restore the transformation matrix
202     pd3dDevice->SetTransform( D3DTS_WORLD, &matSavedWorld );
203 
204     // Render any sibling frames
205     if( pFrame->m_pNext )
206         RenderFrame( pd3dDevice, pFrame->m_pNext, dwFlags );
207 
208     return S_OK;
209 }
210 
211 
212 
213 
214 //-----------------------------------------------------------------------------
215 // Name: RenderMesh()
216 // Desc: Renders the mesh geometry.
217 //-----------------------------------------------------------------------------
RenderMesh(LPDIRECT3DDEVICE8 pd3dDevice,XBMESH_DATA * pMesh,DWORD dwFlags)218 HRESULT CXBMesh::RenderMesh( LPDIRECT3DDEVICE8 pd3dDevice, XBMESH_DATA* pMesh,
219                              DWORD dwFlags )
220 {
221     D3DVertexBuffer* pVB           = &pMesh->m_VB;
222     DWORD            dwNumVertices =  pMesh->m_dwNumVertices;
223     D3DIndexBuffer*  pIB           = &pMesh->m_IB;
224     DWORD            dwNumIndices  =  pMesh->m_dwNumIndices;
225     DWORD            dwFVF         =  pMesh->m_dwFVF;
226     DWORD            dwVertexSize  =  pMesh->m_dwVertexSize;
227     D3DPRIMITIVETYPE dwPrimType    =  pMesh->m_dwPrimType;
228     DWORD            dwNumSubsets  =  pMesh->m_dwNumSubsets;
229     XBMESH_SUBSET*   pSubsets      = &pMesh->m_pSubsets[0];
230 
231     (VOID)dwNumIndices; // not used
232 
233     if( dwNumVertices == 0 )
234         return S_OK;
235 
236     // Set the vertex stream
237     pd3dDevice->SetStreamSource( 0, pVB, dwVertexSize );
238     pd3dDevice->SetIndices( pIB, 0 );
239 
240     // Set the FVF code, unless the user asked us not to
241     if( 0 == ( dwFlags & XBMESH_NOFVF ) )
242         pd3dDevice->SetVertexShader( dwFVF );
243 
244     // Render the subsets
245     for( DWORD i = 0; i < dwNumSubsets; i++ )
246     {
247         BOOL bRender = FALSE;
248 
249         // Render the opaque subsets, unless the user asked us not to
250         if( 0 == ( dwFlags & XBMESH_ALPHAONLY ) )
251         {
252             if( 0 == ( dwFlags & XBMESH_NOMATERIALS ) )
253             {
254                 if( pSubsets[i].mtrl.Diffuse.a >= 1.0f )
255                     bRender = TRUE;
256             }
257             else
258                 bRender = TRUE;
259         }
260 
261         // Render the transparent subsets, unless the user asked us not to
262         if( 0 == ( dwFlags & XBMESH_OPAQUEONLY ) )
263         {
264             if( 0 == ( dwFlags & XBMESH_NOMATERIALS ) )
265             {
266                 if( pSubsets[i].mtrl.Diffuse.a < 1.0f )
267                     bRender = TRUE;
268             }
269         }
270 
271         if( bRender )
272         {
273             // Set the material, unless the user asked us not to
274             if( 0 == ( dwFlags & XBMESH_NOMATERIALS ) )
275                 pd3dDevice->SetMaterial( &pSubsets[i].mtrl );
276 
277             // Set the texture, unless the user asked us not to
278             if( 0 == ( dwFlags & XBMESH_NOTEXTURES ) )
279                 pd3dDevice->SetTexture( 0, pSubsets[i].pTexture );
280 
281             // Call the callback, so the app can tweak state before rendering
282             // each subset
283             BOOL bRenderSubset = RenderCallback( pd3dDevice, i, &pSubsets[i], dwFlags );
284 
285             // Draw the mesh subset
286             if( bRenderSubset )
287             {
288                 DWORD dwNumPrimitives = ( D3DPT_TRIANGLESTRIP == dwPrimType ) ? pSubsets[i].dwIndexCount-2 : pSubsets[i].dwIndexCount/3;
289                 pd3dDevice->DrawIndexedPrimitive( dwPrimType, 0, pSubsets[i].dwIndexCount,
290                                                   pSubsets[i].dwIndexStart, dwNumPrimitives );
291             }
292         }
293     }
294 
295     return S_OK;
296 }
297 
298 
299 
300 
301 //-----------------------------------------------------------------------------
302 // Name: ComputeRadius()
303 // Desc: Finds the furthest point from zero on the mesh.
304 //-----------------------------------------------------------------------------
ComputeRadius()305 FLOAT CXBMesh::ComputeRadius()
306 {
307     D3DXMATRIX matIdentity;
308     D3DXMatrixIdentity( &matIdentity );
309 
310     return ComputeFrameRadius( m_pMeshFrames, &matIdentity );
311 }
312 
313 
314 
315 
316 //-----------------------------------------------------------------------------
317 // Name: ComputeFrameRadius()
318 // Desc: Calls ComputeMeshRadius for each frame with the correct transform.
319 //-----------------------------------------------------------------------------
ComputeFrameRadius(XBMESH_FRAME * pFrame,D3DXMATRIX * pmatParent)320 FLOAT CXBMesh::ComputeFrameRadius( XBMESH_FRAME* pFrame, D3DXMATRIX* pmatParent )
321 {
322     // Apply the frame's local transform
323     D3DXMATRIX matWorld;
324     D3DXMatrixMultiply( &matWorld, &pFrame->m_matTransform, pmatParent );
325 
326     FLOAT fRadius = 0.0f;
327 
328     // Compute bounds for the mesh data
329     if( pFrame->m_MeshData.m_dwNumSubsets )
330         fRadius = ComputeMeshRadius( &pFrame->m_MeshData, &matWorld );
331 
332     // Compute bounds for any child frames
333     if( pFrame->m_pChild )
334     {
335         FLOAT fChildRadius = ComputeFrameRadius( pFrame->m_pChild, &matWorld  );
336 
337         if( fChildRadius > fRadius )
338             fRadius = fChildRadius;
339     }
340 
341     // Compute bounds for any sibling frames
342     if( pFrame->m_pNext )
343     {
344         FLOAT fSiblingRadius = ComputeFrameRadius( pFrame->m_pNext, pmatParent );
345 
346         if( fSiblingRadius > fRadius )
347             fRadius = fSiblingRadius;
348     }
349 
350     return fRadius;
351 }
352 
353 
354 
355 
356 //-----------------------------------------------------------------------------
357 // Name: ComputeMeshRadius()
358 // Desc: Finds the furthest point from zero on the mesh.
359 //-----------------------------------------------------------------------------
ComputeMeshRadius(XBMESH_DATA * pMesh,D3DXMATRIX * pmat)360 FLOAT CXBMesh::ComputeMeshRadius( XBMESH_DATA* pMesh, D3DXMATRIX* pmat )
361 {
362     DWORD       dwNumVertices = pMesh->m_dwNumVertices;
363     DWORD       dwVertexSize  = pMesh->m_dwVertexSize;
364     BYTE*       pVertices;
365     D3DXVECTOR3 vPos;
366     FLOAT       fMaxDist2 = 0.0f;
367 
368     pMesh->m_VB.Lock( 0, 0, &pVertices, 0 );
369 
370     while( dwNumVertices-- )
371     {
372         D3DXVec3TransformCoord( &vPos, (D3DXVECTOR3*)pVertices, pmat );
373 
374         FLOAT fDist2 = vPos.x*vPos.x + vPos.y*vPos.y + vPos.z*vPos.z;
375 
376         if( fDist2 > fMaxDist2 )
377             fMaxDist2 = fDist2;
378 
379         pVertices += dwVertexSize;
380     }
381 
382     pMesh->m_VB.Unlock();
383 
384     return sqrtf( fMaxDist2 );
385 }
386 
387 //-----------------------------------------------------------------------------
388 //  Take the union of two boxes
389 //-----------------------------------------------------------------------------
MAX(float a,float b)390 inline float MAX(float a, float b) { return a > b ? a : b; }
MIN(float a,float b)391 inline float MIN(float a, float b) { return a < b ? a : b; }
UnionBox(D3DXVECTOR3 * pvMin,D3DXVECTOR3 * pvMax,const D3DXVECTOR3 & vMin,const D3DXVECTOR3 & vMax)392 static void UnionBox(D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax, const D3DXVECTOR3 &vMin, const D3DXVECTOR3 &vMax)
393 {
394     pvMin->x = MIN(pvMin->x, vMin.x);
395     pvMin->y = MIN(pvMin->y, vMin.y);
396     pvMin->z = MIN(pvMin->z, vMin.z);
397     pvMax->x = MAX(pvMax->x, vMax.x);
398     pvMax->y = MAX(pvMax->y, vMax.y);
399     pvMax->z = MAX(pvMax->z, vMax.z);
400 }
401 
402 //-----------------------------------------------------------------------------
403 // Name: ComputeBoundingBox()
404 // Desc: Calculates the bounding box of the entire hierarchy.
405 //-----------------------------------------------------------------------------
ComputeBoundingBox(D3DXVECTOR3 * pvMin,D3DXVECTOR3 * pvMax)406 HRESULT CXBMesh::ComputeBoundingBox(D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax)
407 {
408     D3DXMATRIX matIdentity;
409     D3DXMatrixIdentity( &matIdentity );
410     return ComputeFrameBoundingBox( m_pMeshFrames, &matIdentity, pvMin, pvMax );
411 }
412 
413 //-----------------------------------------------------------------------------
414 // Name: ComputeFrameBoundingBox()
415 // Desc: Calls ComputeMeshBoundingBox for each frame with the correct transform.
416 //-----------------------------------------------------------------------------
ComputeFrameBoundingBox(XBMESH_FRAME * pFrame,D3DXMATRIX * pmatParent,D3DXVECTOR3 * pvMin,D3DXVECTOR3 * pvMax)417 HRESULT CXBMesh::ComputeFrameBoundingBox( XBMESH_FRAME* pFrame, D3DXMATRIX* pmatParent, D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax)
418 {
419     HRESULT hr;
420 
421     // initialize bounds to be reset on the first UnionBox
422     pvMin->x = pvMin->y = pvMin->z = FLT_MAX;
423     pvMax->x = pvMax->y = pvMax->z = -FLT_MAX;
424 
425     // Apply the frame's local transform
426     D3DXMATRIX matWorld;
427     D3DXMatrixMultiply( &matWorld, &pFrame->m_matTransform, pmatParent );
428 
429     // Compute bounds for the mesh data
430     if( pFrame->m_MeshData.m_dwNumSubsets )
431     {
432         D3DXVECTOR3 vMin, vMax;
433         hr = ComputeMeshBoundingBox( &pFrame->m_MeshData, &matWorld, &vMin, &vMax );
434         if (FAILED(hr))
435             return hr;
436         UnionBox(pvMin, pvMax, vMin, vMax);
437     }
438 
439     // Compute bounds for any child frames
440     if( pFrame->m_pChild )
441     {
442         D3DXVECTOR3 vMin, vMax;
443         hr = ComputeFrameBoundingBox( pFrame->m_pChild, &matWorld, &vMin, &vMax );
444         if (FAILED(hr))
445             return hr;
446         UnionBox(pvMin, pvMax, vMin, vMax);
447     }
448 
449     // Compute bounds for any sibling frames
450     if( pFrame->m_pNext )
451     {
452         D3DXVECTOR3 vMin, vMax;
453         hr = ComputeFrameBoundingBox( pFrame->m_pNext, pmatParent, &vMin, &vMax );
454         if (FAILED(hr))
455             return hr;
456         UnionBox(pvMin, pvMax, vMin, vMax);
457     }
458     return S_OK;
459 }
460 
461 
462 
463 
464 //-----------------------------------------------------------------------------
465 // Name: ComputeMeshBoundingBox()
466 // Desc: Calculate the bounding box of the transformed mesh.
467 //-----------------------------------------------------------------------------
ComputeMeshBoundingBox(XBMESH_DATA * pMesh,D3DXMATRIX * pmat,D3DXVECTOR3 * pvMin,D3DXVECTOR3 * pvMax)468 HRESULT CXBMesh::ComputeMeshBoundingBox( XBMESH_DATA* pMesh, D3DXMATRIX* pmat, D3DXVECTOR3 *pvMin, D3DXVECTOR3 *pvMax)
469 {
470     // initialize bounds to be reset on the first point
471     pvMin->x = pvMin->y = pvMin->z = FLT_MAX;
472     pvMax->x = pvMax->y = pvMax->z = -FLT_MAX;
473     DWORD       dwNumVertices = pMesh->m_dwNumVertices;
474     DWORD       dwVertexSize  = pMesh->m_dwVertexSize;
475     BYTE*       pVertices;
476     D3DXVECTOR3 vPos;
477     pMesh->m_VB.Lock( 0, 0, &pVertices, 0 );
478     while( dwNumVertices-- )
479     {
480         D3DXVec3TransformCoord( &vPos, (D3DXVECTOR3*)pVertices, pmat );
481         UnionBox(pvMin, pvMax, vPos, vPos);    // expand the bounding box to include the point
482         pVertices += dwVertexSize;
483     }
484     pMesh->m_VB.Unlock();
485     return S_OK;
486 }
487