Example #1
0
HRESULT CMesh3D::InitialMesh(LPCSTR Name, FILE *FileLog )
{
	m_pMesh         = 0;
	m_pMeshMaterial = 0;
	m_pMeshTextura  = 0;
	m_SizeFVF       = 0;
	m_Alpha         = 1.0f;	
	ID3DXBuffer *pMaterialBuffer  = 0;
	if ( FAILED( D3DXLoadMeshFromX( Name, D3DXMESH_SYSTEMMEM, g_pD3DDevice, 0, &pMaterialBuffer, 0, &m_TexturCount, &m_pMesh ) ) )
	{
		if ( m_pMesh == 0 )
		{		
			if ( FileLog ) 
				fprintf( FileLog, "error load x file '%s'\n", Name );
			return E_FAIL;
		}
	}

	if ( m_pMesh->GetFVF() & D3DFVF_XYZ ) 
		m_SizeFVF += sizeof(float)*3;
	if ( m_pMesh->GetFVF() & D3DFVF_NORMAL ) 
		m_SizeFVF += sizeof(float)*3;
	if ( m_pMesh->GetFVF() & D3DFVF_TEX1 )
		m_SizeFVF += sizeof(float)*2;

	m_pMesh->GetVertexBuffer( &m_VertexBuffer );
	m_pMesh->GetIndexBuffer(  &m_IndexBuffer  );
	// Извлекаем свойства материала и названия{имена} структуры
	D3DXMATERIAL *D3DXMeshMaterial = (D3DXMATERIAL *)pMaterialBuffer->GetBufferPointer();
	m_pMeshMaterial  = new D3DMATERIAL9[m_TexturCount];
	m_pMeshTextura   = new IDirect3DTexture9*[m_TexturCount];
	for ( DWORD i = 0; i < m_TexturCount; i++ )
	{
		// Копируем материал
		m_pMeshMaterial[i] = D3DXMeshMaterial[i].MatD3D;
		// Установить окружающего свет
		m_pMeshMaterial[i].Ambient = m_pMeshMaterial[i].Diffuse;
		// Загружаем текстуру
		string FileName = string( "model//" ) + string( D3DXMeshMaterial[i].pTextureFilename );
		if ( FAILED( D3DXCreateTextureFromFile( g_pD3DDevice, FileName.c_str(), &m_pMeshTextura[i] )))
		{
			fprintf( FileLog, "error load texture '%s'\n", D3DXMeshMaterial[i].pTextureFilename );
			m_pMeshTextura[i] = 0;
		}
	}
	// Уничтожаем буфер материала
	pMaterialBuffer->Release();

	return S_OK;
}
Example #2
0
HRESULT HelloShadowVolume::RestoreDeviceObjects()
{
    HRESULT hr;
    IDirect3DDevice8* device;
    hr = m_spD3D->CreateDevice(
             D3DADAPTER_DEFAULT,
             D3DDEVTYPE_HAL,
             m_hWnd,
             D3DCREATE_HARDWARE_VERTEXPROCESSING,
             &m_dpps,
             &device);
    if (FAILED(hr))
    {
        MessageBox(0, L"CreateDevice failed", 0, 0);
        return E_FAIL;
    }
    m_spDevice.reset(device, [](IDirect3DDevice8* device) {
        device->Release();
    });

    m_spDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
    m_spDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
    m_spDevice->SetRenderState(D3DRS_DITHERENABLE, TRUE);
    D3DVIEWPORT8 viewport = { 0, 0, m_iWidth, m_iHeight };
    m_spDevice->SetViewport(&viewport);

    D3DXVECTOR3 eye(0.0f, 0.0f, 30.0f);
    D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
    D3DXMatrixLookAtLH(&m_mtView, &eye, &target, &up);

    D3DXMatrixPerspectiveFovLH(&m_mtProj, 0.2*D3DX_PI, (float)m_iWidth / (float)m_iHeight, 1.0f, 100.f);

    m_cPlaneTint = { 0.7f, 0.6f, 0.4f, 1.0f };


    ID3DXMesh* plane;
    //D3DXCreatePolygon(m_spDevice.get(), 2.0f, 4, &plane, NULL);
    CreatePlane(m_spDevice.get(), 15.0f, 10, &plane);
    //D3DXCreateSphere(m_spDevice.get(), 1.0f,20,20, &plane, NULL);

    IDirect3DVertexBuffer8* vb;
    IDirect3DIndexBuffer8* ib;
    plane->GetVertexBuffer(&vb);
    plane->GetIndexBuffer(&ib);
    m_spPlaneVB.reset(vb, [](IDirect3DVertexBuffer8* vb) {
        vb->Release();
    });
    m_spPlaneIB.reset(ib, [](IDirect3DIndexBuffer8* ib) {
        ib->Release();
    });
    m_dwPlaneNumVertices = plane->GetNumVertices();
    m_dwPlaneNumFaces = plane->GetNumFaces();

    plane->Release();

    DWORD decl[] = {
        D3DVSD_STREAM(0),
        D3DVSD_REG(0, D3DVSDT_FLOAT3),
        D3DVSD_REG(3, D3DVSDT_FLOAT3),
        D3DVSD_END()
    };
    hr = CreateVSFromBinFile(m_spDevice.get(), decl, L"plane.vso", &m_dwPlaneVSH);
    if (FAILED(hr))
    {
        MessageBox(0, 0, L"CreateVSFromBinFile failed", 0);
        return E_FAIL;
    }
    hr = CreatePSFromBinFile(m_spDevice.get(), L"plane.pso", &m_dwPlanePSH);
    if (FAILED(hr))
    {
        MessageBox(0, 0, L"CreatePSFromBinFile failed", 0);
        return E_FAIL;
    }

    D3DXMATRIX Rx, Tz;
    D3DXMatrixRotationX(&Rx, D3DX_PI*0.5f);
    D3DXMatrixTranslation(&Tz, 0.0f, -3.0f, 0.0f);
    m_mtPlaneWorld = Rx * Tz;

    ID3DXMesh* occluder;
    CreateOccluder(m_spDevice.get(), &occluder);
    IDirect3DVertexBuffer8* vbOccluder;
    IDirect3DIndexBuffer8* ibOccluder;
    occluder->GetVertexBuffer(&vbOccluder);
    occluder->GetIndexBuffer(&ibOccluder);
    m_spOccluderVB.reset(vbOccluder, [](IDirect3DVertexBuffer8* vb) {
        vb->Release();
    });
    m_spOccluderIB.reset(ibOccluder, [](IDirect3DIndexBuffer8* ib) {
        ib->Release();
    });
    m_dwOccluderNumVertices = occluder->GetNumVertices();
    m_dwOccluderNumFaces = occluder->GetNumFaces();
    occluder->Release();

    hr = CreateVSFromBinFile(m_spDevice.get(), decl, L"occluder.vso", &m_dwOccluderVSH);
    if (FAILED(hr))
    {
        MessageBox(0, 0, L"CreateVSFromBinFile failed", 0);
        return E_FAIL;
    }
    hr = CreatePSFromBinFile(m_spDevice.get(), L"occluder.pso", &m_dwOccluderPSH);
    if (FAILED(hr))
    {
        MessageBox(0, 0, L"CreatePSFromBinFile failed", 0);
        return E_FAIL;
    }
    m_cOccluderTint = { 0.3f, 0.0f, 0.8f, 1.0f };
    D3DXMATRIX Rz, T;
    D3DXMatrixTranslation(&T, 5.1f * cosf(0.5), 0.0f, 5.1f * sinf(0.5));
    D3DXMatrixIdentity(&m_mtVolumeWorld);
    D3DXMatrixRotationZ(&Rz, 0.5f);
    m_mtOccluderWorld = T * Rz;

    ID3DXMesh* volume;
    CreateVolume(m_spDevice.get(), &volume);
    IDirect3DVertexBuffer8* vbVolume;
    IDirect3DIndexBuffer8* ibVolume;
    volume->GetVertexBuffer(&vbVolume);
    volume->GetIndexBuffer(&ibVolume);
    m_spVolumeVB.reset(vbVolume, [](IDirect3DVertexBuffer8* vb) {
        vb->Release();
    });
    m_spVolumeIB.reset(ibVolume, [](IDirect3DIndexBuffer8* ib) {
        ib->Release();
    });
    m_dwVolumeNumVertices = volume->GetNumVertices();
    m_dwVolumeNumFaces = volume->GetNumFaces();
    volume->Release();

    hr = CreateVSFromBinFile(m_spDevice.get(), decl, L"volume.vso", &m_dwVolumeVSH);
    if (FAILED(hr))
    {
        MessageBox(0, 0, L"CreateVSFromBinFile failed", 0);
        return E_FAIL;
    }
    hr = CreatePSFromBinFile(m_spDevice.get(), L"volume.pso", &m_dwVolumePSH);
    if (FAILED(hr))
    {
        MessageBox(0, 0, L"CreatePSFromBinFile failed", 0);
        return E_FAIL;
    }
    m_cVolumeTint = { 0.7f, 0.0f, 0.0f, 1.0f };

    D3DXMATRIX Sx;
    D3DXMatrixIdentity(&m_mtVolumeWorld);
    D3DXMatrixScaling(&Sx, 6.0f, 1.0f, 1.0f);
    D3DXMatrixRotationZ(&Rz, 0.5f);
    m_mtVolumeWorld = Sx * Rz;

    return S_OK;
}
void LODManager::Render(IDirect3DDevice9 *D3DDevice) {
  const char *meshpath =
    (lod == GRID_FARNEAR ? "landscape\\lod\\farnear\\" :
    (lod == GRID_FARFAR  ? "landscape\\lod\\farfar\\" :
			   "landscape\\lod\\farinf\\"));
  const char *textpath =
    (lod == GRID_FARNEAR ? "landscapelod\\generated\\farnear\\" :
    (lod == GRID_FARFAR  ? "landscapelod\\generated\\farfar\\" :
			   "landscapelod\\generated\\farinf\\"));

  int nativeminx = (GRID_SIZE * 32) + (Constants.Coordinates.x - GridDistantCount.Get());
  int nativeminy = (GRID_SIZE * 32) + (Constants.Coordinates.y - GridDistantCount.Get());
  int nativemaxx = (GRID_SIZE * 32) + (Constants.Coordinates.x + GridDistantCount.Get());
  int nativemaxy = (GRID_SIZE * 32) + (Constants.Coordinates.y + GridDistantCount.Get());

  /* y-axis has flipped rounding */
  nativeminx = (nativeminx / 32) - GRID_SIZE;
  nativeminy = (nativeminy / 32) - GRID_SIZE + 0;
  nativemaxx = (nativemaxx / 32) - GRID_SIZE;
  nativemaxy = (nativemaxy / 32) - GRID_SIZE + 0;

  int gridx = Constants.Coordinates.x / 32;
  int gridy = Constants.Coordinates.y / 32;
  for (int x = (gridx - extend); x <= (gridx + extend); x++)
  for (int y = (gridy - extend); y <= (gridy + extend); y++) {
    /* TODO: try radius, seems it's not a box */
    /* leave out Oblivion's native tiles */
    if ((x >= nativeminx) && (x <= nativemaxx) &&
	(y >= nativeminy) && (y <= nativemaxy))
      continue;
    /* leave out other LOD's inner tiles */
    if ((abs(gridx - x) <= inner) &&
	(abs(gridy - y) <= inner))
      continue;

    /* where are we? */
    const float TileOffset[4] = {x * TILE_DIM, y * TILE_DIM, 0, 0};

    /* filter outside-array coordinates */
    if (((GRID_OFFSET + y) >= 0) && ((GRID_OFFSET + y) < GRID_SIZE) &&
	((GRID_OFFSET + x) >= 0) && ((GRID_OFFSET + x) < GRID_SIZE)) {

      /* never seen, never attempted */
      if (MeshIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] < -1) {
	/* TODO: 32 means 32x32 cells, in theory that can be different as well */
	char buf[256]; sprintf(buf, "%02d.%02d.%02d.32", WorldSpace, x * 32, y * 32);
	char pth[256]; strcpy(pth, meshpath); strcat(pth, buf); strcat(pth, ".x");

	/* no textures without mesh, but we can render texture-free */
	if ((MeshIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] =
	    MeshManager::GetSingleton()->LoadPrivateMesh(pth, MR_REGULAR)) != -1) {

	  if (ColrIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] < -1) {
	    strcpy(pth, textpath); strcat(pth, buf); strcat(pth, ".dds");
	    ColrIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] =
	      TextureManager::GetSingleton()->LoadPrivateTexture(pth, TR_PLANAR);
	  }

	  if (NormIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] < -1) {
	    strcpy(pth, textpath); strcat(pth, buf); strcat(pth, "_fn.dds");
	    NormIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] =
	      TextureManager::GetSingleton()->LoadPrivateTexture(pth, TR_PLANAR);
	  }

	  /* put the addresses */
	  ManagedMeshRecord    *mesh = Meshes [lod][GRID_OFFSET + y][GRID_OFFSET + x] =    MeshManager::GetSingleton()->GetMesh   (MeshIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x]);
	  ManagedTextureRecord *colr = Colors [lod][GRID_OFFSET + y][GRID_OFFSET + x] = TextureManager::GetSingleton()->GetTexture(ColrIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x]);
	  ManagedTextureRecord *norm = Normals[lod][GRID_OFFSET + y][GRID_OFFSET + x] = TextureManager::GetSingleton()->GetTexture(NormIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x]);

	  /* failure to load all resources */
	  if (!mesh || !colr || !norm) {
	    if (mesh) mesh->Release();
	    if (colr) colr->Release();
	    if (norm) norm->Release();

	    MeshIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] = -1;
	    ColrIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] = -1;
	    NormIDs[lod][GRID_OFFSET + y][GRID_OFFSET + x] = -1;

	    continue;
	  }

#if	defined(OBGE_GAMMACORRECTION)
	  /* remember DeGamma for this kind of texture */
	  static const bool PotDeGamma = true;
	  colr->GetTexture()->SetPrivateData(GammaGUID, &PotDeGamma, sizeof(PotDeGamma), 0);
#endif
	}
      }

      /* get the addresses */
      ManagedMeshRecord    *mesh = Meshes [lod][GRID_OFFSET + y][GRID_OFFSET + x];
      ManagedTextureRecord *colr = Colors [lod][GRID_OFFSET + y][GRID_OFFSET + x];
      ManagedTextureRecord *norm = Normals[lod][GRID_OFFSET + y][GRID_OFFSET + x];

      ID3DXMesh *m;
      if (mesh && (m = (ID3DXMesh *)mesh->GetMesh())) {
#if 0
	DWORD FVF  = m->GetFVF();
	DWORD size = m->GetNumBytesPerVertex();
	DWORD numf = m->GetNumFaces();
	DWORD numv = m->GetNumVertices();

	IDirect3DIndexBuffer9 *pIB; m->GetIndexBuffer(&pIB);
	IDirect3DVertexBuffer9 *pVB; m->GetVertexBuffer(&pVB);

	D3DDevice->SetStreamSource(0, pVB, 0, size);
	D3DDevice->SetFVF(FVF);
	D3DDevice->SetTexture(0, colr->GetTexture());
	D3DDevice->SetTexture(1, norm->GetTexture());
	D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numv, 0, numf);
#endif

	D3DDevice->SetTexture(0, colr ? colr->GetTexture() : NULL);
	D3DDevice->SetTexture(1, norm ? norm->GetTexture() : NULL);

	D3DDevice->SetVertexShader(vShader[lod]);
	D3DDevice->SetPixelShader (pShader[lod]);
	D3DDevice->SetVertexShaderConstantF(32, TileOffset, 1);

	m->DrawSubset(0);
      }
    }

    /* water-planes */
    D3DDevice->SetVertexShader(vShaderW);
    D3DDevice->SetPixelShader (pShaderW);
    D3DDevice->SetVertexShaderConstantF(32, TileOffset, 1);

    D3DDevice->SetStreamSource(0, WaterVertex, 0, sizeof(WaterTile));
    D3DDevice->SetFVF(WATERTILEFORMAT);
    D3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
  }

  const float TileOffset[4] = {0, 0, 0, 1};

  /* infini-plane */
  D3DDevice->SetVertexShader(vShaderW);
  D3DDevice->SetPixelShader (pShaderW);
  D3DDevice->SetVertexShaderConstantF(32, TileOffset, 1);

  D3DDevice->SetStreamSource(0, InfiniteVertex, 0, sizeof(WaterTile));
  D3DDevice->SetFVF(WATERTILEFORMAT);
  D3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
}
Example #4
0
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    WNDCLASS wc;

    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = (WNDPROC) MainWindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
    wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "xtocmod";
    if (RegisterClass(&wc) == 0)
    {
	MessageBox(NULL,
                   "Failed to register the window class.", "Fatal Error",
                   MB_OK | MB_ICONERROR);
	return NULL;
    }

    DWORD windowStyle = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
                         WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
    g_mainWindow = CreateWindow("xtocmod",
                                "xtocmod",
                                windowStyle,
                                CW_USEDEFAULT,
                                CW_USEDEFAULT,
                                300, 300,
                                NULL,
                                NULL,
                                hInstance,
                                NULL);
    if (g_mainWindow == NULL)
    {
        MessageBox(NULL,
                   "Error creating application window.", "Fatal Error",
                   MB_OK | MB_ICONERROR);
    }

    //ShowWindow(g_mainWindow, SW_SHOW);
    SetForegroundWindow(g_mainWindow);
    SetFocus(g_mainWindow);

    // Initialize D3D
    g_d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (g_d3d == NULL)
    {
        ShowD3DErrorMessage("Initializing D3D", 0);
        return 1;
    }

    D3DPRESENT_PARAMETERS presentParams;
    ZeroMemory(&presentParams, sizeof(presentParams));
    presentParams.Windowed = TRUE;
    presentParams.SwapEffect = D3DSWAPEFFECT_COPY;
#if 0
    presentParams.BackBufferWidth = 300;
    presentParams.BackBufferHeight = 300;
    presentParams.BackBufferCount = 1;
    presentParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
    presentParams.Windowed = TRUE;
#endif
    
    HRESULT hr = g_d3d->CreateDevice(D3DADAPTER_DEFAULT,
                                     D3DDEVTYPE_HAL,
                                     g_mainWindow,
                                     D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                     &presentParams,
                                     &g_d3dDev);
    if (FAILED(hr))
    {
        ShowD3DErrorMessage("Creating D3D device", hr);
        //return 1;
    }

    string inputFilename(lpCmdLine);
    string outputFilename(inputFilename, 0, inputFilename.rfind('.'));
    outputFilename += ".cmod";

    ID3DXMesh* mesh = NULL;
    ID3DXBuffer* adjacency = NULL;
    ID3DXBuffer* materialBuf = NULL;
    ID3DXBuffer* effects = NULL;
    DWORD numMaterials;
    
    hr = D3DXLoadMeshFromX(inputFilename.c_str(),
                           0,
                           g_d3dDev,
                           &adjacency,
                           &materialBuf,
                           &effects,
                           &numMaterials,
                           &mesh);
    if (FAILED(hr))
    {
        ShowD3DErrorMessage("Loading mesh from X file", hr);
        return 1;
    }


    DWORD numVertices = mesh->GetNumVertices();
    DWORD numFaces = mesh->GetNumFaces();

    cout << "vertices: " << numVertices << '\n';
    cout << "faces: " << numFaces << '\n';

    cout << "adjacency buffer size: " << adjacency->GetBufferSize() << '\n';

    ofstream meshfile(outputFilename.c_str());

    // Output the header
    meshfile << "#celmodel__ascii\n\n";

    cout << "numMaterials=" << numMaterials << '\n';
    D3DXMATERIAL* materials = reinterpret_cast<D3DXMATERIAL*>(materialBuf->GetBufferPointer());
    for (DWORD mat = 0; mat < numMaterials; mat++)
    {
        meshfile << "material\n";
        meshfile << "diffuse " << materials[mat].MatD3D.Diffuse << '\n';
        //meshfile << "emissive " << materials[mat].MatD3D.Emissive << '\n';
        meshfile << "specular " << materials[mat].MatD3D.Specular << '\n';
        meshfile << "specpower " << materials[mat].MatD3D.Power << '\n';
        meshfile << "opacity " << materials[mat].MatD3D.Diffuse.a << '\n';
        meshfile << "end_material\n\n";
    }

    // Vertex format
    D3DVERTEXELEMENT9 declElements[MAX_FVF_DECL_SIZE];
    hr = mesh->GetDeclaration(declElements);
    if (FAILED(hr))
    {
        ShowD3DErrorMessage("Checking vertex declaration", hr);
        return 1;
    }

    DWORD stride = D3DXGetDeclVertexSize(declElements, 0);

    VertexAttribute vertexMap[VertexAttribute::MaxAttribute];
    CreateVertexAttributeMap(declElements, vertexMap);

    meshfile << "mesh\n\n";

    DumpVertexDescription(vertexMap, meshfile);

    ID3DXMesh* optMesh = NULL;
    ID3DXBuffer* vertexRemap = NULL;
    DWORD* faceRemap = new DWORD[numFaces];
    DWORD* optAdjacency = new DWORD[numFaces * 3];
    hr = mesh->Optimize(D3DXMESHOPT_COMPACT | D3DXMESHOPT_STRIPREORDER,
                        //D3DXMESHOPT_VERTEXCACHE |
                        reinterpret_cast<DWORD*>(adjacency->GetBufferPointer()),
                        optAdjacency,
                        faceRemap,
                        &vertexRemap,
                        &optMesh);
    if (FAILED(hr))
    {
        ShowD3DErrorMessage("Optimize failed: ", hr);
        return 1;
    }
    
    // Attribute table
    DWORD attribTableSize = 0;
    hr = optMesh->GetAttributeTable(NULL, &attribTableSize);
    if (FAILED(hr))
    {
        ShowD3DErrorMessage("Querying attribute table size", hr);
        return 1;
    }

    D3DXATTRIBUTERANGE* attribTable = NULL;
    if (attribTableSize > 0)
    {
        attribTable = new D3DXATTRIBUTERANGE[attribTableSize];
        hr = optMesh->GetAttributeTable(attribTable, &attribTableSize);
        if (FAILED(hr))
        {
            ShowD3DErrorMessage("Getting attribute table", hr);
            return 1;
        }
    }

    cout << "Attribute table size: " << attribTableSize << '\n';
    if (attribTableSize == 1)
    {
        cout << "Attribute id: " << attribTable[0].AttribId << '\n';
    }

    if (!DumpMeshVertices(optMesh, vertexMap, stride, meshfile))
        return 1;
    
    // output the indices
    for (DWORD attr = 0; attr < attribTableSize; attr++)
    {
        StripifyMeshSubset(optMesh, attr, meshfile);
    }
    meshfile << "\nend_mesh\n";

#if 0
    IDirect3DIndexBuffer9* indices = NULL;
    hr = mesh->GetIndexBuffer(&indices);
#endif

#if 0
    // No message loop required for this app
    MSG msg;
    GetMessage(&msg, NULL, 0u, 0u);
    while (msg.message != WM_QUIT)
    {
        GetMessage(&msg, NULL, 0u, 0u);
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
#endif

    return 0;
}