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