.X file-ok betöltése

A .x egy 3D-s modell file formátum, amit a DirectX-hez találtak ki.

Ezek DirectX alatt kényelmesen betölthetők és megjeleníthetők. Rengeteg modell található neten és a DirectX SDK-ban (<DirectX SDK könyvtár>\Samples\Media). Sok 3D-s szerkesztő program tud exportálni ilyen formátumba. Ilyen pl az ingyenes Blender.

Példa projekt, az órán használt keretredszerrel, letölthető innen.

Modellek betöltése

A következő változókra van szükség a betöltéshez és a megjelenítéshez is:

ID3DXMesh *mesh;
D3DMATERIAL9 *meshMaterials;
IDirect3DTexture9 **meshTextures;
DWORD numMaterials;

Az első maga a modell, ami több textúrázott anyagot is tartalmazhat. Ezek számát tárolja majd a numMaterials, a többi kettő pedig a konrét adatok tömbjei lesznek.

Mindet érdemes nullára inicializálni.

A tényleges betöltő kód:

void LoadX::loadMesh()
{
    //** Takaritas, hatha nem eloszor hivjuk ezt
    SAFE_RELEASE(mesh);
    SAFE_DELETE_ARRAY(meshMaterials);
    for (DWORD i=0; i<numMaterials; ++i)
        SAFE_RELEASE(meshTextures[i]);
    SAFE_DELETE_ARRAY(meshTextures);


    LPD3DXBUFFER materialBuffer;
    HRESULT hr;
    
    V( D3DXLoadMeshFromX(L"X\\airplane 2.x", D3DXMESH_MANAGED,
               _device, NULL, &materialBuffer, NULL, &numMaterials,
               &mesh) );


    //** Nem ugyan az, mint a D3DMATERIAL9 !
    D3DXMATERIAL* materials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer();

    //** Tobb anyag es textura is lehet egy mesh-ben
    meshMaterials = new D3DMATERIAL9[numMaterials];
    meshTextures = new LPDIRECT3DTEXTURE9[numMaterials];

    for (DWORD i=0; i<numMaterials; ++i)
    {
        meshMaterials[i] = materials[i].MatD3D;

        //** Az ambiens szint kezzel kell atallitani
        meshMaterials[i].Ambient = meshMaterials[i].Diffuse;

        //** toltsuk be a texturakat, ha vannak
        meshTextures[i] = 0;
        if (materials[i].pTextureFilename)
        {
            LPCWSTR wfilename = ConvertLPCSTRToLPWSTR(materials[i].pTextureFilename);
            WCHAR withpath[60];
            StringCbPrintf(withpath, 60, L"X\\%s", wfilename);

            V( D3DXCreateTextureFromFile(_device, withpath, &meshTextures[i]) );
        }
    }
    
    materialBuffer->Release();
}

Ez használja a következő segédfüggvényt, amire a Unicode-os és nem Unicode-es stringeketet használó függvények keveredése miatt van szükség:

LPWSTR ConvertLPCSTRToLPWSTR(char* pCstring)
{
    LPWSTR pszOut = NULL;
    if (pCstring != NULL)
    {
        int nInputStrLen = (int)strlen(pCstring);
        int nOutputStrLen = MultiByteToWideChar(CP_ACP, 0, pCstring, nInputStrLen, NULL, 0) + 2;
        pszOut = new WCHAR [nOutputStrLen];
        if (pszOut)
        {
            memset (pszOut, 0x00, sizeof (WCHAR)*nOutputStrLen);
            MultiByteToWideChar(CP_ACP, 0, pCstring, nInputStrLen, pszOut, nInputStrLen);
        }
    }
    return pszOut;
}

Megjelenítés

A kirajzolás ennél már jóval egyszerűbb:

void LoadX::onFrameRender( double fTime, float fElapsedTime )
{
    _device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(65, 105, 225), 1, 0);

    if ( SUCCEEDED(_device->BeginScene()) )
    {
        for (DWORD i=0; i<numMaterials; ++i)
        {
            //** Anyagjellemzo es textura megadas
            _device->SetMaterial(&meshMaterials[i]);
            _device->SetTexture(0,meshTextures[i]);

            //** Al-mesh kirajzolas
            mesh->DrawSubset( i );
        }
        _device->EndScene();
    }
}

Takarítás

A betöltött adatokak fel kell szabadítanunk, ha a program véget ér, vagy már nincs rá szükségünk:

void LoadX::onDestroyDevice()
{
    SAFE_RELEASE(mesh);
    SAFE_DELETE_ARRAY(meshMaterials);
    for (DWORD i=0; i<numMaterials; ++i)
        SAFE_RELEASE(meshTextures[i]);
    SAFE_DELETE_ARRAY(meshTextures);
}