//----------------------------------------------------------------------------- // File: OptimizedMesh.cpp // // Desc: Sample of optimizing meshes in D3D // // Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #define STRICT #include #include #include #include #include #include "D3DApp.h" #include "D3DFont.h" #include "D3DUtil.h" #include "DXUtil.h" #include "resource.h" struct SStripData { LPDIRECT3DINDEXBUFFER8 m_pStrips; // strip indices (single strip) LPDIRECT3DINDEXBUFFER8 m_pStripsMany; // strip indices (many strips) DWORD m_cStripIndices; DWORD *m_rgcStripLengths; DWORD m_cStrips; SStripData() :m_pStrips(NULL), m_pStripsMany(NULL), m_cStripIndices(0), m_rgcStripLengths(NULL) {} }; struct SMeshData { LPD3DXMESH m_pMeshSysMem; // System memory copy of mesh LPD3DXMESH m_pMesh; // Local version of mesh, copied on resize LPDIRECT3DVERTEXBUFFER8 m_pVertexBuffer; // vertex buffer of mesh SStripData *m_rgStripData; // strip indices split by attribute DWORD m_cStripDatas; SMeshData() :m_pMeshSysMem(NULL), m_pMesh(NULL), m_pVertexBuffer(NULL), m_rgStripData(NULL), m_cStripDatas(0) {} void ReleaseLocalMeshes() { SAFE_RELEASE(m_pMesh); SAFE_RELEASE(m_pVertexBuffer); } void ReleaseAll() { SAFE_RELEASE(m_pMeshSysMem); SAFE_RELEASE(m_pMesh); SAFE_RELEASE(m_pVertexBuffer); for (DWORD iStripData = 0; iStripData < m_cStripDatas; iStripData++) { SAFE_RELEASE(m_rgStripData[iStripData].m_pStrips); SAFE_RELEASE(m_rgStripData[iStripData].m_pStripsMany); delete []m_rgStripData[iStripData].m_rgcStripLengths; } delete []m_rgStripData; m_rgStripData = NULL; m_cStripDatas = 0; } }; //----------------------------------------------------------------------------- // Name: class CMyD3DApplication // Desc: Main class to run this application. Most functionality is inherited // from the CD3DApplication base class. //----------------------------------------------------------------------------- class CMyD3DApplication : public CD3DApplication { TCHAR m_strMeshFilename[512]; TCHAR m_strInitialDir[512]; BOOL m_bShowVertexCacheOptimized; BOOL m_bShowStripReordered; BOOL m_bShowStrips; BOOL m_bShowSingleStrip; BOOL m_bForce32ByteFVF; CD3DFont* m_pFont; // Font for outputting frame stats CD3DArcBall m_ArcBall; // Mouse rotation utility D3DXVECTOR3 m_vObjectCenter; // Center of bounding sphere of object FLOAT m_fObjectRadius; // Radius of bounding sphere of object D3DXMATRIX m_matWorld; DWORD m_cObjectsPerSide; // sqrt of the number of objects to draw DWORD m_dwMemoryOptions; // various forms of mesh data SMeshData m_MeshAttrSorted; SMeshData m_MeshStripReordered; SMeshData m_MeshVertexCacheOptimized; DWORD m_dwNumMaterials; // Number of materials LPDIRECT3DTEXTURE8* m_pMeshTextures; D3DMATERIAL8* m_pMeshMaterials; public: HRESULT OneTimeSceneInit(); HRESULT InitDeviceObjects(); HRESULT RestoreDeviceObjects(); HRESULT InvalidateDeviceObjects(); HRESULT DeleteDeviceObjects(); HRESULT Render(); HRESULT FrameMove(); HRESULT FinalCleanup(); LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); CMyD3DApplication(); HRESULT LoadMeshData(LPD3DXMESH *pMeshSysMemLoaded, LPD3DXBUFFER *ppAdjacencyBuffer); HRESULT OptimizeMeshData(LPD3DXMESH pMeshSysMem, LPD3DXBUFFER pAdjacencyBuffer, DWORD dwOptFlags, SMeshData *pMeshData); HRESULT UpdateLocalMeshes(SMeshData *pMeshData); HRESULT DrawMeshData(SMeshData *pMeshData); }; //----------------------------------------------------------------------------- // Name: WinMain() // Desc: Entry point to the program. Initializes everything, and goes into a // message-processing loop. Idle time is used to render the scene. //----------------------------------------------------------------------------- INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT ) { CMyD3DApplication d3dApp; if( FAILED( d3dApp.Create( hInst ) ) ) return 0; return d3dApp.Run(); } //----------------------------------------------------------------------------- // Name: CMyD3DApplication() // Desc: Constructor //----------------------------------------------------------------------------- CMyD3DApplication::CMyD3DApplication() { // Override base class members m_strWindowTitle = _T("OptimizedMesh: Optimizing Meshes in D3D"); m_bUseDepthBuffer = TRUE; m_bShowCursorWhenFullscreen = TRUE; // Initialize member variables m_bShowVertexCacheOptimized = TRUE; m_bShowStripReordered = FALSE; m_bShowSingleStrip = TRUE; m_bShowStrips = FALSE; m_bShowSingleStrip = FALSE; m_bForce32ByteFVF = TRUE; m_dwMemoryOptions = D3DXMESH_MANAGED; m_pFont = new CD3DFont( _T("Arial"), 12, D3DFONT_BOLD ); _tcscpy( m_strInitialDir, DXUtil_GetDXSDKMediaPath() ); _tcscpy( m_strMeshFilename, _T("knot.x") ); m_cObjectsPerSide = 1; // initialize mesh data structures m_dwNumMaterials = 0; m_pMeshTextures = NULL; m_pMeshMaterials = NULL; } //----------------------------------------------------------------------------- // Name: OneTimeSceneInit() // Desc: Called during initial app startup, this function performs all the // permanent initialization. //----------------------------------------------------------------------------- HRESULT CMyD3DApplication::OneTimeSceneInit() { // Set cursor to indicate that user can move the object with the mouse #ifdef _WIN64 SetClassLongPtr( m_hWnd, GCLP_HCURSOR, (LONG_PTR)LoadCursor( NULL, IDC_SIZEALL ) ); #else SetClassLong( m_hWnd, GCL_HCURSOR, (LONG)LoadCursor( NULL, IDC_SIZEALL ) ); #endif return S_OK; } //----------------------------------------------------------------------------- // Name: FrameMove() // Desc: Called once per frame, the call is the entry point for animating // the scene. //----------------------------------------------------------------------------- HRESULT CMyD3DApplication::FrameMove() { // Setup viewing postion from ArcBall D3DXMATRIX matTemp; D3DXMatrixTranslation( &m_matWorld, -m_vObjectCenter.x, -m_vObjectCenter.y, -m_vObjectCenter.z ); D3DXMatrixMultiply( &m_matWorld, &m_matWorld, m_ArcBall.GetTranslationMatrix() ); D3DXMatrixMultiply( &m_matWorld, &m_matWorld, m_ArcBall.GetRotationMatrix() ); D3DXMatrixTranslation( &matTemp, -m_fObjectRadius * (m_cObjectsPerSide-1),//* 0.5f, -m_fObjectRadius * (m_cObjectsPerSide-1),//* 0.5f, 0 ); D3DXMatrixMultiply( &m_matWorld, &m_matWorld, &matTemp ); D3DXMATRIX matView; D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0, 0,-3.7f*m_fObjectRadius * m_cObjectsPerSide), &D3DXVECTOR3( 0, 0, 0 ), &D3DXVECTOR3( 0, 1, 0 ) ); m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); return S_OK; } HRESULT CMyD3DApplication::DrawMeshData(SMeshData *pMeshData) { HRESULT hr; DWORD iCurFace; // Set and draw each of the materials in the mesh for( DWORD iMaterial=0; iMaterial < m_dwNumMaterials; iMaterial++ ) { m_pd3dDevice->SetMaterial( &m_pMeshMaterials[iMaterial] ); m_pd3dDevice->SetTexture( 0, m_pMeshTextures[iMaterial] ); if( !m_bShowStrips && !m_bShowSingleStrip) { pMeshData->m_pMesh->DrawSubset( iMaterial ); } else // drawing strips { DWORD dwFVF; DWORD cBytesPerVertex; DWORD iStrip; dwFVF = pMeshData->m_pMesh->GetFVF(); cBytesPerVertex = D3DXGetFVFVertexSize(dwFVF); m_pd3dDevice->SetVertexShader(dwFVF); m_pd3dDevice->SetStreamSource(0, pMeshData->m_pVertexBuffer, cBytesPerVertex); if(m_bShowSingleStrip) { m_pd3dDevice->SetIndices(pMeshData->m_rgStripData[iMaterial].m_pStrips, 0); hr = m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, pMeshData->m_pMesh->GetNumVertices(), 0, pMeshData->m_rgStripData[iMaterial].m_cStripIndices - 2); if (FAILED(hr)) return hr; } else { m_pd3dDevice->SetIndices(pMeshData->m_rgStripData[iMaterial].m_pStripsMany, 0); iCurFace = 0; for (iStrip = 0; iStrip < pMeshData->m_rgStripData[iMaterial].m_cStrips; iStrip++) { hr = m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, pMeshData->m_pMesh->GetNumVertices(), iCurFace, pMeshData->m_rgStripData[iMaterial].m_rgcStripLengths[iStrip]); if (FAILED(hr)) return hr; iCurFace += 2 + pMeshData->m_rgStripData[iMaterial].m_rgcStripLengths[iStrip]; } } } } return S_OK; } //----------------------------------------------------------------------------- // Name: Render() // Desc: Called once per frame, the call is the entry point for 3d // rendering. This function sets up render states, clears the // viewport, and renders the scene. //----------------------------------------------------------------------------- HRESULT CMyD3DApplication::Render() { DWORD xOffset; DWORD yOffset; D3DXMATRIX matWorld; D3DXMATRIX matTemp; DWORD cTriangles = 0; FLOAT fTrisPerSec; TCHAR strInfo[120]; TCHAR *szOptString; // Clear the scene m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0x000000ff, 1.0f, 0x00000000 ); // Draw scene if( SUCCEEDED( m_pd3dDevice->BeginScene() ) ) { for (xOffset = 0; xOffset < m_cObjectsPerSide; xOffset++) { for (yOffset = 0; yOffset < m_cObjectsPerSide; yOffset++) { D3DXMatrixTranslation( &matTemp, m_fObjectRadius * xOffset * 2, m_fObjectRadius * yOffset * 2, 0 ); D3DXMatrixMultiply( &matWorld, &m_matWorld, &matTemp ); m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); if (m_bShowVertexCacheOptimized) DrawMeshData(&m_MeshVertexCacheOptimized); else if (m_bShowStripReordered) DrawMeshData(&m_MeshStripReordered); else DrawMeshData(&m_MeshAttrSorted); } } // Calculate and show triangles per sec, a reasonable throughput number if (m_MeshAttrSorted.m_pMesh != NULL) cTriangles = m_MeshAttrSorted.m_pMesh->GetNumFaces() * m_cObjectsPerSide * m_cObjectsPerSide; else cTriangles = 0; fTrisPerSec = m_fFPS * cTriangles; if (m_bShowVertexCacheOptimized) szOptString = _T("VCache Optimized"); else if (m_bShowStripReordered) szOptString = _T("Strip Reordered"); else szOptString = _T("Unoptimized"); // Output statistics wsprintf( strInfo, _T("%s, %ld tris per sec, %ld triangles"), szOptString, (DWORD)fTrisPerSec, cTriangles); m_pFont->DrawText( 2, 0, D3DCOLOR_ARGB(255,255,255,0), m_strFrameStats ); m_pFont->DrawText( 2, 20, D3DCOLOR_ARGB(255,255,255,0), m_strDeviceStats ); m_pFont->DrawText( 2, 40, D3DCOLOR_ARGB(255,255,255,0), strInfo); m_pd3dDevice->EndScene(); } return S_OK; } HRESULT CMyD3DApplication::LoadMeshData ( LPD3DXMESH *ppMesh, LPD3DXBUFFER *ppAdjacencyBuffer ) { LPDIRECT3DVERTEXBUFFER8 pMeshVB = NULL; LPD3DXBUFFER pD3DXMtrlBuffer = NULL; BYTE* pVertices; TCHAR strMesh[512]; HRESULT hr = S_OK; BOOL bNormalsInFile; LPD3DXMESH pMeshSysMem = NULL; LPD3DXMESH pMeshTemp; DWORD *rgdwAdjacencyTemp = NULL; DWORD i; D3DXMATERIAL* d3dxMaterials; DWORD dw32Bit; // Get a path to the media file DXUtil_FindMediaFile( strMesh, m_strMeshFilename ); // Load the mesh from the specified file hr = D3DXLoadMeshFromX( strMesh, D3DXMESH_SYSTEMMEM, m_pd3dDevice, ppAdjacencyBuffer, &pD3DXMtrlBuffer, &m_dwNumMaterials, &pMeshSysMem ); if( FAILED(hr) ) goto End; // remember if the mesh is 32 or 16 bit, to be added in on the clones dw32Bit = pMeshSysMem->GetOptions() & D3DXMESH_32BIT; // Get the array of materials out of the returned buffer, and allocate a texture array d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer(); m_pMeshMaterials = new D3DMATERIAL8[m_dwNumMaterials]; m_pMeshTextures = new LPDIRECT3DTEXTURE8[m_dwNumMaterials]; for( i=0; iGetVertexBuffer( &pMeshVB ); if( SUCCEEDED(hr) ) { hr = pMeshVB->Lock( 0, 0, &pVertices, D3DLOCK_NOSYSLOCK ); if( SUCCEEDED(hr) ) { hr = D3DXComputeBoundingSphere( pVertices, pMeshSysMem->GetNumVertices(), pMeshSysMem->GetFVF(), &m_vObjectCenter, &m_fObjectRadius ); pMeshVB->Unlock(); } pMeshVB->Release(); } if( FAILED(hr) ) goto End; // remember if there were normals in the file, before possible clone operation bNormalsInFile = pMeshSysMem->GetFVF() & D3DFVF_NORMAL; // if using 32byte vertices, check fvf if (m_bForce32ByteFVF) { // force 32 byte vertices if (pMeshSysMem->GetFVF() != (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)) { hr = pMeshSysMem->CloneMeshFVF( pMeshSysMem->GetOptions(), D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1, m_pd3dDevice, &pMeshTemp ); if( FAILED(hr) ) goto End; pMeshSysMem->Release(); pMeshSysMem = pMeshTemp; } } // otherwise, just make sure that there is a normal mesh else if ( !(pMeshSysMem->GetFVF() & D3DFVF_NORMAL) ) { hr = pMeshSysMem->CloneMeshFVF( pMeshSysMem->GetOptions(), pMeshSysMem->GetFVF() | D3DFVF_NORMAL, m_pd3dDevice, &pMeshTemp ); if (FAILED(hr)) return hr; pMeshSysMem->Release(); pMeshSysMem = pMeshTemp; } // Compute normals for the mesh, if not present if (!bNormalsInFile) { D3DXComputeNormals( pMeshSysMem, NULL ); } *ppMesh = pMeshSysMem; pMeshSysMem = NULL; End: SAFE_RELEASE( pMeshSysMem ); return hr; } HRESULT CMyD3DApplication::OptimizeMeshData ( LPD3DXMESH pMeshSysMem, LPD3DXBUFFER pAdjacencyBuffer, DWORD dwOptFlags, SMeshData *pMeshData ) { HRESULT hr = S_OK; LPD3DXBUFFER pbufTemp = NULL; DWORD iMaterial; // attribute sort - the un-optimized mesh option // remember the adjacency for the vertex cache optimization hr = pMeshSysMem->Optimize( dwOptFlags|D3DXMESH_SYSTEMMEM, (DWORD*)pAdjacencyBuffer->GetBufferPointer(), NULL, NULL, NULL, &pMeshData->m_pMeshSysMem); if( FAILED(hr) ) goto End; pMeshData->m_cStripDatas = m_dwNumMaterials; pMeshData->m_rgStripData = new SStripData[pMeshData->m_cStripDatas]; if (pMeshData->m_rgStripData == NULL) { hr = E_OUTOFMEMORY; goto End; } for (iMaterial = 0; iMaterial < m_dwNumMaterials; iMaterial++) { hr = D3DXConvertMeshSubsetToSingleStrip(pMeshData->m_pMeshSysMem, iMaterial, D3DXMESH_IB_MANAGED, &pMeshData->m_rgStripData[iMaterial].m_pStrips, &pMeshData->m_rgStripData[iMaterial].m_cStripIndices); if (FAILED(hr)) goto End; hr = D3DXConvertMeshSubsetToStrips(pMeshData->m_pMeshSysMem, iMaterial, D3DXMESH_IB_MANAGED, &pMeshData->m_rgStripData[iMaterial].m_pStripsMany, NULL, &pbufTemp, &pMeshData->m_rgStripData[iMaterial].m_cStrips); if (FAILED(hr)) goto End; pMeshData->m_rgStripData[iMaterial].m_rgcStripLengths = new DWORD[pMeshData->m_rgStripData[iMaterial].m_cStrips]; if (pMeshData->m_rgStripData[iMaterial].m_rgcStripLengths == NULL) { hr = E_OUTOFMEMORY; goto End; } memcpy(pMeshData->m_rgStripData[iMaterial].m_rgcStripLengths, pbufTemp->GetBufferPointer(), sizeof(DWORD)*pMeshData->m_rgStripData[iMaterial].m_cStrips); } End: SAFE_RELEASE(pbufTemp); return hr; } //----------------------------------------------------------------------------- // Name: InitDeviceObjects() // Desc: Initialize scene objects. //----------------------------------------------------------------------------- HRESULT CMyD3DApplication::InitDeviceObjects() { HRESULT hr = S_OK; LPD3DXMESH pMeshSysMem = NULL; LPD3DXBUFFER pAdjacencyBuffer = NULL; // Initialize the font m_pFont->InitDeviceObjects( m_pd3dDevice ); // check current display setting CheckMenuItem( GetMenu(m_hWnd), ID_OPTIONS_DISPLAY1 + (m_cObjectsPerSide-1), MF_CHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_SHOWNONOPTIMIZEDMESH, (!m_bShowStripReordered && !m_bShowVertexCacheOptimized) ? MF_CHECKED : MF_UNCHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_SHOWVCACHEOPTIMIZED, m_bShowVertexCacheOptimized ? MF_CHECKED : MF_UNCHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_SHOWSTRIPREORDERED, m_bShowStripReordered ? MF_CHECKED : MF_UNCHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_SHOWTRILIST, (!m_bShowStrips && !m_bShowSingleStrip) ? MF_CHECKED : MF_UNCHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_SHOWONESTRIP, m_bShowSingleStrip ? MF_CHECKED : MF_UNCHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_SHOWMANYSTRIPS, m_bShowStrips ? MF_CHECKED : MF_UNCHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_DYNAMICVB, (m_dwMemoryOptions == D3DXMESH_DYNAMIC) ? MF_CHECKED : MF_UNCHECKED ); CheckMenuItem( GetMenu(m_hWnd), IDM_FORCE32BYTEVERTEX, m_bForce32ByteFVF ? MF_CHECKED : MF_UNCHECKED ); hr = LoadMeshData(&pMeshSysMem, &pAdjacencyBuffer); if (FAILED(hr)) { // ignore load errors, just draw blank screen if mesh is invalid hr = S_OK; goto End; } hr = OptimizeMeshData(pMeshSysMem, pAdjacencyBuffer, D3DXMESHOPT_ATTRSORT, &m_MeshAttrSorted); if (FAILED(hr)) goto End; hr = OptimizeMeshData(pMeshSysMem, pAdjacencyBuffer, D3DXMESHOPT_STRIPREORDER, &m_MeshStripReordered); if (FAILED(hr)) goto End; hr = OptimizeMeshData(pMeshSysMem, pAdjacencyBuffer, D3DXMESHOPT_VERTEXCACHE, &m_MeshVertexCacheOptimized); if (FAILED(hr)) goto End; End: SAFE_RELEASE( pMeshSysMem ); SAFE_RELEASE( pAdjacencyBuffer ); return hr; } HRESULT CMyD3DApplication::UpdateLocalMeshes(SMeshData *pMeshData) { HRESULT hr = S_OK; // if a mesh was loaded, update the local meshes if (pMeshData->m_pMeshSysMem != NULL) { hr = pMeshData->m_pMeshSysMem->CloneMeshFVF( m_dwMemoryOptions|D3DXMESH_VB_WRITEONLY, pMeshData->m_pMeshSysMem->GetFVF(), m_pd3dDevice, &pMeshData->m_pMesh ); if (FAILED(hr)) goto End; hr = pMeshData->m_pMesh->GetVertexBuffer(&pMeshData->m_pVertexBuffer); if (FAILED(hr)) goto End; } End: return hr; } //----------------------------------------------------------------------------- // Name: RestoreDeviceObjects() // Desc: Initialize scene objects. //----------------------------------------------------------------------------- HRESULT CMyD3DApplication::RestoreDeviceObjects() { m_pFont->RestoreDeviceObjects(); // Setup render state m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_DITHERENABLE, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR ); // Setup the light D3DLIGHT8 light; light.Type = D3DLIGHT_DIRECTIONAL; light.Diffuse.r = light.Diffuse.g = light.Diffuse.b = 1.0f; light.Specular.r = light.Specular.g = light.Specular.b = 0.0f; light.Ambient.r = light.Ambient.g = light.Ambient.b = 0.3f; light.Position = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &D3DXVECTOR3( 0.3f, -1.0f, 1.0f ) ); light.Attenuation0 = light.Attenuation1 = light.Attenuation2 = 0.0f; light.Range = sqrtf(FLT_MAX); m_pd3dDevice->SetLight(0, &light ); m_pd3dDevice->LightEnable(0, TRUE ); m_ArcBall.SetWindow( m_d3dsdBackBuffer.Width, m_d3dsdBackBuffer.Height, 0.85f ); m_ArcBall.SetRadius( m_fObjectRadius ); FLOAT fAspect = m_d3dsdBackBuffer.Width / (FLOAT)m_d3dsdBackBuffer.Height; D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspect, m_fObjectRadius/64.0f, m_fObjectRadius*200.0f); m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); // update the local copies of the meshes UpdateLocalMeshes(&m_MeshAttrSorted); UpdateLocalMeshes(&m_MeshStripReordered); UpdateLocalMeshes(&m_MeshVertexCacheOptimized); return S_OK; } //----------------------------------------------------------------------------- // Name: InvalidateDeviceObjects() // Desc: //----------------------------------------------------------------------------- HRESULT CMyD3DApplication::InvalidateDeviceObjects() { m_pFont->InvalidateDeviceObjects(); m_MeshAttrSorted.ReleaseLocalMeshes(); m_MeshStripReordered.ReleaseLocalMeshes(); m_MeshVertexCacheOptimized.ReleaseLocalMeshes(); return S_OK; } //----------------------------------------------------------------------------- // Name: DeleteDeviceObjects() // Desc: Called when the app is exiting, or the device is being changed, // this function deletes any device dependent objects. //----------------------------------------------------------------------------- HRESULT CMyD3DApplication::DeleteDeviceObjects() { m_pFont->DeleteDeviceObjects(); for( UINT i=0; i= ID_OPTIONS_DISPLAY1) && (LOWORD(wParam) <= ID_OPTIONS_DISPLAY36)) { // uncheck old item CheckMenuItem( GetMenu(hWnd), ID_OPTIONS_DISPLAY1 + (m_cObjectsPerSide-1), MF_UNCHECKED ); // calc new item m_cObjectsPerSide = LOWORD(wParam) - ID_OPTIONS_DISPLAY1 + 1; // check new item CheckMenuItem( GetMenu(hWnd), ID_OPTIONS_DISPLAY1 + (m_cObjectsPerSide-1), MF_CHECKED ); } } return CD3DApplication::MsgProc( hWnd, uMsg, wParam, lParam ); }